Skip to content

Commit f469aa3

Browse files
hoxyqmeta-codesync[bot]
authored andcommitted
Define an endpoint on HostTarget to capture frame timings (facebook#54672)
Summary: Pull Request resolved: facebook#54672 # Changelog: [Internal] Introduces an endpoint on `HostTarget` for capturing frame timings. This exposes a public API to Host, which could be called during active tracing session. `Host` could be notified through tracing delegate in `HostTargetDelegate` about tracing state, this has been implemented below as part of this diff stack. Reviewed By: sbuggay Differential Revision: D87373807 fbshipit-source-id: 3ffc5f2b803164e4316ff6c3824672c842187ddd
1 parent 5b7d92f commit f469aa3

File tree

3 files changed

+61
-1
lines changed

3 files changed

+61
-1
lines changed

packages/react-native/ReactCommon/jsinspector-modern/HostTarget.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <set>
2121
#include <string>
2222

23+
#include <jsinspector-modern/tracing/FrameTimingSequence.h>
2324
#include <jsinspector-modern/tracing/HostTracingProfile.h>
2425
#include <jsinspector-modern/tracing/TraceRecordingState.h>
2526
#include <jsinspector-modern/tracing/TracingCategory.h>
@@ -325,6 +326,7 @@ class JSINSPECTOR_EXPORT HostTarget : public EnableExecutorFromThis<HostTarget>
325326
*/
326327
void sendCommand(HostCommand command);
327328

329+
#pragma region Tracing
328330
/**
329331
* Creates a new HostTracingAgent.
330332
* This Agent is not owned by the HostTarget. The Agent will be destroyed at
@@ -363,6 +365,13 @@ class JSINSPECTOR_EXPORT HostTarget : public EnableExecutorFromThis<HostTarget>
363365
*/
364366
void emitTracingProfileForFirstFuseboxClient(tracing::HostTracingProfile tracingProfile) const;
365367

368+
/**
369+
* An endpoint for the Host to report frame timings that will be recorded if and only if there is currently an active
370+
* tracing session.
371+
*/
372+
void recordFrameTimings(tracing::FrameTimingSequence frameTimingSequence);
373+
#pragma endregion
374+
366375
private:
367376
/**
368377
* Constructs a new HostTarget.
@@ -386,13 +395,22 @@ class JSINSPECTOR_EXPORT HostTarget : public EnableExecutorFromThis<HostTarget>
386395
std::unique_ptr<PerfMonitorUpdateHandler> perfMonitorUpdateHandler_;
387396
std::unique_ptr<HostRuntimeBinding> perfMetricsBinding_;
388397

398+
#pragma region Tracing
389399
/**
390400
* Current pending trace recording, which encapsulates the configuration of
391401
* the tracing session and the state.
392402
*
393403
* Should only be allocated when there is an active tracing session.
394404
*/
395405
std::unique_ptr<HostTargetTraceRecording> traceRecording_{nullptr};
406+
/**
407+
* Protects the state inside traceRecording_.
408+
*
409+
* Calls to tracing subsystem could happen from different threads, depending on the mode (Background or CDP) and
410+
* the method: the Host could report frame timings from any arbitrary thread.
411+
*/
412+
std::mutex tracingMutex_;
413+
#pragma endregion
396414

397415
inline HostTargetDelegate &getDelegate()
398416
{

packages/react-native/ReactCommon/jsinspector-modern/HostTargetTracing.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,17 @@ std::shared_ptr<HostTracingAgent> HostTarget::createTracingAgent(
3939
bool HostTarget::startTracing(
4040
tracing::Mode tracingMode,
4141
std::set<tracing::Category> enabledCategories) {
42+
std::lock_guard lock(tracingMutex_);
43+
4244
if (traceRecording_ != nullptr) {
4345
if (traceRecording_->isBackgroundInitiated() &&
4446
tracingMode == tracing::Mode::CDP) {
45-
stopTracing();
47+
if (auto tracingDelegate = delegate_.getTracingDelegate()) {
48+
tracingDelegate->onTracingStopped();
49+
}
50+
51+
traceRecording_->stop();
52+
traceRecording_.reset();
4653
} else {
4754
return false;
4855
}
@@ -67,6 +74,8 @@ bool HostTarget::startTracing(
6774
}
6875

6976
tracing::HostTracingProfile HostTarget::stopTracing() {
77+
std::lock_guard lock(tracingMutex_);
78+
7079
assert(traceRecording_ != nullptr && "No tracing in progress");
7180

7281
if (auto tracingDelegate = delegate_.getTracingDelegate()) {
@@ -79,4 +88,17 @@ tracing::HostTracingProfile HostTarget::stopTracing() {
7988
return profile;
8089
}
8190

91+
void HostTarget::recordFrameTimings(
92+
tracing::FrameTimingSequence frameTimingSequence) {
93+
std::lock_guard lock(tracingMutex_);
94+
95+
if (traceRecording_) {
96+
traceRecording_->recordFrameTimings(frameTimingSequence);
97+
} else {
98+
assert(
99+
false &&
100+
"The HostTarget is not being profiled. Did you call recordFrameTimings() from the native Host side when there is no tracing in progress?");
101+
}
102+
}
103+
82104
} // namespace facebook::react::jsinspector_modern

packages/react-native/ReactCommon/jsinspector-modern/tests/TracingTest.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,24 @@ TEST_F(TracingTest, EnablesSamplingProfilerOnlyCategoryIsSpecified) {
5656
AtJsonPtr("/cat", "disabled-by-default-v8.cpu_profiler"))));
5757
}
5858

59+
TEST_F(TracingTest, RecordsFrameTimings) {
60+
InSequence s;
61+
62+
page_->startTracing(tracing::Mode::Background, {tracing::Category::Timeline});
63+
64+
auto now = HighResTimeStamp::now();
65+
auto frameTimingSequence = tracing::FrameTimingSequence(
66+
1, // id
67+
11, // threadId
68+
now,
69+
now + HighResDuration::fromNanoseconds(10),
70+
now + HighResDuration::fromNanoseconds(50));
71+
72+
page_->recordFrameTimings(frameTimingSequence);
73+
74+
auto tracingProfile = page_->stopTracing();
75+
EXPECT_EQ(tracingProfile.frameTimings.size(), 1u);
76+
EXPECT_EQ(tracingProfile.frameTimings[0].id, frameTimingSequence.id);
77+
}
78+
5979
} // namespace facebook::react::jsinspector_modern

0 commit comments

Comments
 (0)