Skip to content

Commit 8c22d87

Browse files
rubennortehannojg
authored andcommitted
Add support for details field and custom tracks in performance.mark and performance.measure for DevTools (facebook#52613)
Summary: Pull Request resolved: facebook#52613 Changelog: [internal] This adds first-class support for the `detail` field in `performance.mark` and `performance.measure`. Now that we have access the JS entry in native, we can access the `detail` field to propagate it to DevTools (and use it to extract track names for Perfetto). In order to avoid the performance overhead of always having to extract the `detail` field from the entry, this is done lazily only if we're actively profiling with DevTools or Perfetto. Reviewed By: sbuggay Differential Revision: D78340911 fbshipit-source-id: 383dd1cb6fcc8a04be9e65038503986f196e23c9
1 parent 1d48c11 commit 8c22d87

7 files changed

Lines changed: 133 additions & 63 deletions

File tree

packages/react-native/ReactCommon/jsinspector-modern/tracing/CdpTracing.h

Lines changed: 0 additions & 21 deletions
This file was deleted.

packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,9 @@ bool PerformanceTracer::stopTracingAndCollectEvents(
114114

115115
void PerformanceTracer::reportMark(
116116
const std::string_view& name,
117-
HighResTimeStamp start) {
118-
if (!tracing_) {
117+
HighResTimeStamp start,
118+
folly::dynamic&& detail) {
119+
if (!tracingAtomic_) {
119120
return;
120121
}
121122

@@ -124,26 +125,41 @@ void PerformanceTracer::reportMark(
124125
return;
125126
}
126127

127-
buffer_.push_back(TraceEvent{
128+
folly::dynamic eventArgs = folly::dynamic::object();
129+
if (detail != nullptr) {
130+
eventArgs = folly::dynamic::object(
131+
"data",
132+
folly::dynamic::object("detail", folly::toJson(std::move(detail))));
133+
}
134+
135+
buffer_.emplace_back(TraceEvent{
128136
.name = std::string(name),
129137
.cat = "blink.user_timing",
130138
.ph = 'I',
131139
.ts = start,
132-
.pid = PID, // FIXME: This should be the real process ID.
133-
.tid = USER_TIMINGS_DEFAULT_TRACK, // FIXME: This should be the real
134-
// thread ID.
140+
.pid = processId_,
141+
.tid = oscompat::getCurrentThreadId(),
142+
.args = eventArgs,
135143
});
136144
}
137145

138146
void PerformanceTracer::reportMeasure(
139147
const std::string_view& name,
140148
HighResTimeStamp start,
141149
HighResDuration duration,
142-
const std::optional<DevToolsTrackEntryPayload>& trackMetadata) {
143-
if (!tracing_) {
150+
folly::dynamic&& detail) {
151+
if (!tracingAtomic_) {
144152
return;
145153
}
146154

155+
folly::dynamic beginEventArgs = folly::dynamic::object();
156+
if (detail != nullptr) {
157+
beginEventArgs =
158+
folly::dynamic::object("detail", folly::toJson(std::move(detail)));
159+
}
160+
161+
auto currentThreadId = oscompat::getCurrentThreadId();
162+
147163
std::lock_guard<std::mutex> lock(mutex_);
148164
if (!tracing_) {
149165
return;

packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77

88
#pragma once
99

10-
#include "CdpTracing.h"
11-
12-
#include <react/timing/primitives.h>
10+
#include "ConsoleTimeStamp.h"
11+
#include "TraceEvent.h"
12+
#include "TraceEventProfile.h"
1313

1414
#include <react/timing/primitives.h>
1515

@@ -106,15 +106,19 @@ class PerformanceTracer {
106106
*/
107107
void collectEvents(
108108
const std::function<void(const folly::dynamic& eventsChunk)>&
109-
resultCallback);
109+
resultCallback,
110+
uint16_t chunkSize);
110111

111112
/**
112113
* Record a `Performance.mark()` event - a labelled timestamp. If not
113114
* currently tracing, this is a no-op.
114115
*
115116
* See https://w3c.github.io/user-timing/#mark-method.
116117
*/
117-
void reportMark(const std::string_view& name, HighResTimeStamp start);
118+
void reportMark(
119+
const std::string_view& name,
120+
HighResTimeStamp start,
121+
folly::dynamic&& detail = nullptr);
118122

119123
/**
120124
* Record a `Performance.measure()` event - a labelled duration. If not
@@ -126,8 +130,7 @@ class PerformanceTracer {
126130
const std::string_view& name,
127131
HighResTimeStamp start,
128132
HighResDuration duration,
129-
const std::optional<DevToolsTrackEntryPayload>& trackMetadata =
130-
std::nullopt);
133+
folly::dynamic&& detail = nullptr);
131134

132135
/**
133136
* Record a corresponding Trace Event for OS-level process.

packages/react-native/ReactCommon/react/nativemodule/webperformance/NativePerformance.cpp

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include <cxxreact/JSExecutor.h>
1515
#include <cxxreact/ReactMarker.h>
16+
#include <jsi/JSIDynamic.h>
1617
#include <jsi/instrumentation.h>
1718
#include <react/performance/timeline/PerformanceEntryReporter.h>
1819
#include <react/performance/timeline/PerformanceObserver.h>
@@ -111,6 +112,19 @@ std::shared_ptr<PerformanceObserver> tryGetObserver(
111112
return observerWrapper ? observerWrapper->observer : nullptr;
112113
}
113114

115+
PerformanceEntryReporter::UserTimingDetailProvider getDetailProviderFromEntry(
116+
jsi::Runtime& rt,
117+
jsi::Value& entry) {
118+
return [&rt, &entry]() -> folly::dynamic {
119+
try {
120+
auto detail = entry.asObject(rt).getProperty(rt, "detail");
121+
return jsi::dynamicFromValue(rt, detail);
122+
} catch (jsi::JSIException& ex) {
123+
return nullptr;
124+
}
125+
};
126+
}
127+
114128
} // namespace
115129

116130
NativePerformance::NativePerformance(std::shared_ptr<CallInvoker> jsInvoker)
@@ -124,18 +138,19 @@ void NativePerformance::reportMark(
124138
jsi::Runtime& rt,
125139
std::string name,
126140
HighResTimeStamp startTime,
127-
jsi::Value /*entry*/) {
128-
PerformanceEntryReporter::getInstance()->reportMark(name, startTime);
141+
jsi::Value entry) {
142+
PerformanceEntryReporter::getInstance()->reportMark(
143+
name, startTime, getDetailProviderFromEntry(rt, entry));
129144
}
130145

131146
void NativePerformance::reportMeasure(
132147
jsi::Runtime& rt,
133148
std::string name,
134149
HighResTimeStamp startTime,
135150
HighResDuration duration,
136-
jsi::Value /*entry*/) {
151+
jsi::Value entry) {
137152
PerformanceEntryReporter::getInstance()->reportMeasure(
138-
name, startTime, duration);
153+
name, startTime, duration, getDetailProviderFromEntry(rt, entry));
139154
}
140155

141156
std::optional<double> NativePerformance::getMarkTime(

packages/react-native/ReactCommon/react/performance/timeline/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ target_link_libraries(react_performance_timeline
1919
jsinspector_tracing
2020
reactperflogger
2121
react_featureflags
22-
react_timing)
22+
react_timing
23+
folly_runtime)

packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.cpp

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,42 @@ std::vector<PerformanceEntryType> getSupportedEntryTypesInternal() {
3636
return supportedEntryTypes;
3737
}
3838

39+
std::optional<std::string> getTrackFromDetail(folly::dynamic& detail) {
40+
if (!detail.isObject()) {
41+
return std::nullopt;
42+
}
43+
44+
auto maybeDevtools = detail["devtools"];
45+
if (!maybeDevtools.isObject()) {
46+
return std::nullopt;
47+
}
48+
49+
auto maybeTrack = maybeDevtools["track"];
50+
if (!maybeTrack.isString()) {
51+
return std::nullopt;
52+
}
53+
54+
return maybeTrack.asString();
55+
}
56+
57+
std::optional<std::string> getTrackGroupFromDetail(folly::dynamic& detail) {
58+
if (!detail.isObject()) {
59+
return std::nullopt;
60+
}
61+
62+
auto maybeDevtools = detail["devtools"];
63+
if (!maybeDevtools.isObject()) {
64+
return std::nullopt;
65+
}
66+
67+
auto maybeTrackGroup = maybeDevtools["trackGroup"];
68+
if (!maybeTrackGroup.isString()) {
69+
return std::nullopt;
70+
}
71+
72+
return maybeTrackGroup.asString();
73+
}
74+
3975
} // namespace
4076

4177
std::shared_ptr<PerformanceEntryReporter>&
@@ -141,10 +177,11 @@ void PerformanceEntryReporter::clearEntries(
141177

142178
void PerformanceEntryReporter::reportMark(
143179
const std::string& name,
144-
const HighResTimeStamp startTime) {
180+
const HighResTimeStamp startTime,
181+
UserTimingDetailProvider&& detailProvider) {
145182
const auto entry = PerformanceMark{{.name = name, .startTime = startTime}};
146183

147-
traceMark(entry);
184+
traceMark(entry, std::move(detailProvider));
148185

149186
// Add to buffers & notify observers
150187
{
@@ -159,14 +196,13 @@ void PerformanceEntryReporter::reportMeasure(
159196
const std::string& name,
160197
HighResTimeStamp startTime,
161198
HighResDuration duration,
162-
const std::optional<jsinspector_modern::DevToolsTrackEntryPayload>&
163-
trackMetadata) {
199+
UserTimingDetailProvider&& detailProvider) {
164200
const auto entry = PerformanceMeasure{
165201
{.name = std::string(name),
166202
.startTime = startTime,
167203
.duration = duration}};
168204

169-
traceMeasure(entry);
205+
traceMeasure(entry, std::move(detailProvider));
170206

171207
// Add to buffers & notify observers
172208
{
@@ -268,33 +304,45 @@ void PerformanceEntryReporter::reportResourceTiming(
268304
observerRegistry_->queuePerformanceEntry(entry);
269305
}
270306

271-
void PerformanceEntryReporter::traceMark(const PerformanceMark& entry) const {
307+
void PerformanceEntryReporter::traceMark(
308+
const PerformanceMark& entry,
309+
UserTimingDetailProvider&& detailProvider) const {
272310
auto& performanceTracer =
273311
jsinspector_modern::tracing::PerformanceTracer::getInstance();
274312
if (ReactPerfettoLogger::isTracing() || performanceTracer.isTracing()) {
275-
if (performanceTracer.isTracing()) {
276-
performanceTracer.reportMark(entry.name, entry.startTime);
277-
}
278-
279313
if (ReactPerfettoLogger::isTracing()) {
280314
ReactPerfettoLogger::mark(entry.name, entry.startTime);
281315
}
316+
317+
if (performanceTracer.isTracing()) {
318+
performanceTracer.reportMark(
319+
entry.name,
320+
entry.startTime,
321+
detailProvider != nullptr ? detailProvider() : nullptr);
322+
}
282323
}
283324
}
284325

285326
void PerformanceEntryReporter::traceMeasure(
286-
const PerformanceMeasure& entry) const {
327+
const PerformanceMeasure& entry,
328+
UserTimingDetailProvider&& detailProvider) const {
287329
auto& performanceTracer =
288330
jsinspector_modern::tracing::PerformanceTracer::getInstance();
289331
if (performanceTracer.isTracing() || ReactPerfettoLogger::isTracing()) {
290-
if (performanceTracer.isTracing()) {
291-
performanceTracer.reportMeasure(
292-
entry.name, entry.startTime, entry.duration);
293-
}
332+
auto detail = detailProvider != nullptr ? detailProvider() : nullptr;
294333

295334
if (ReactPerfettoLogger::isTracing()) {
296335
ReactPerfettoLogger::measure(
297-
entry.name, entry.startTime, entry.startTime + entry.duration);
336+
entry.name,
337+
entry.startTime,
338+
entry.startTime + entry.duration,
339+
detail != nullptr ? getTrackFromDetail(detail) : std::nullopt,
340+
detail != nullptr ? getTrackGroupFromDetail(detail) : std::nullopt);
341+
}
342+
343+
if (performanceTracer.isTracing()) {
344+
performanceTracer.reportMeasure(
345+
entry.name, entry.startTime, entry.duration, std::move(detail));
298346
}
299347
}
300348
}

packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
#include "PerformanceEntryKeyedBuffer.h"
1212
#include "PerformanceObserverRegistry.h"
1313

14-
#include <jsinspector-modern/tracing/CdpTracing.h>
14+
#include <folly/dynamic.h>
1515
#include <react/timing/primitives.h>
1616

1717
#include <memory>
@@ -86,14 +86,18 @@ class PerformanceEntryReporter {
8686
std::optional<HighResTimeStamp> getMarkTime(
8787
const std::string& markName) const;
8888

89-
void reportMark(const std::string& name, HighResTimeStamp startTime);
89+
using UserTimingDetailProvider = std::function<folly::dynamic()>;
90+
91+
void reportMark(
92+
const std::string& name,
93+
HighResTimeStamp startTime,
94+
UserTimingDetailProvider&& detailProvider = nullptr);
9095

9196
void reportMeasure(
9297
const std::string& name,
9398
HighResTimeStamp startTime,
9499
HighResDuration duration,
95-
const std::optional<jsinspector_modern::DevToolsTrackEntryPayload>&
96-
trackMetadata = std::nullopt);
100+
UserTimingDetailProvider&& detailProvider = nullptr);
97101

98102
void reportEvent(
99103
std::string name,
@@ -167,8 +171,12 @@ class PerformanceEntryReporter {
167171
throw std::logic_error("Unhandled PerformanceEntryType");
168172
}
169173

170-
void traceMark(const PerformanceMark& entry) const;
171-
void traceMeasure(const PerformanceMeasure& entry) const;
174+
void traceMark(
175+
const PerformanceMark& entry,
176+
UserTimingDetailProvider&& detailProvider) const;
177+
void traceMeasure(
178+
const PerformanceMeasure& entry,
179+
UserTimingDetailProvider&& detailProvider) const;
172180
};
173181

174182
} // namespace facebook::react

0 commit comments

Comments
 (0)