Skip to content

Commit dba8dbd

Browse files
huntiefacebook-github-bot
authored andcommitted
Implement support for Network trace events (#53761)
Summary: Pull Request resolved: #53761 Updates `NetworkReporter` and `PerformanceEntryReporter` to populate (minimal) `"ResourceSendRequest"` and `"ResourceFinished"` events when a CDP performance trace is active. This allows the Chrome DevTools Performance panel to display the "Network" track. **Notes** - The trace events that Chrome requires need extra fields which aren't present on `PerformanceResourceTiming`, hence the new + optional `devtoolsRequestId`, `requestMethod`, `resourceType` params. We only populate these in debug builds. **Limitations** - We emit a *complete trace event set* within `reportResourceTiming`, implementing basic initial support in the Performance panel Network track. This means 1/ either all/no events are sent for a given request (rather than incrementally), 2/ we aren't yet handling failed/cancelled requests in this pipeline. Changelog: [Internal] Reviewed By: hoxyq Differential Revision: D82212362 fbshipit-source-id: 4c6d5d2510cc98ddc819a2778222b835411295c8
1 parent 0caf8e7 commit dba8dbd

File tree

8 files changed

+231
-5
lines changed

8 files changed

+231
-5
lines changed

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

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,50 @@ void PerformanceTracer::reportEventLoopMicrotasks(
232232
});
233233
}
234234

235+
void PerformanceTracer::reportResourceTiming(
236+
const std::string& requestId,
237+
const std::string& url,
238+
HighResTimeStamp fetchStart,
239+
HighResTimeStamp responseStart,
240+
HighResTimeStamp responseEnd,
241+
int statusCode,
242+
const std::string& requestMethod,
243+
const std::string& resourceType) {
244+
if (!tracingAtomic_) {
245+
return;
246+
}
247+
248+
std::lock_guard<std::mutex> lock(mutex_);
249+
if (!tracingAtomic_) {
250+
return;
251+
}
252+
253+
enqueueEvent(PerformanceTracerResourceWillSendRequest{
254+
.requestId = requestId,
255+
.start = fetchStart,
256+
.threadId = getCurrentThreadId(),
257+
});
258+
enqueueEvent(PerformanceTracerResourceSendRequest{
259+
.requestId = requestId,
260+
.url = url,
261+
.start = fetchStart,
262+
.requestMethod = requestMethod,
263+
.resourceType = resourceType,
264+
.threadId = getCurrentThreadId(),
265+
});
266+
enqueueEvent(PerformanceTracerResourceReceiveResponse{
267+
.requestId = requestId,
268+
.start = responseStart,
269+
.statusCode = statusCode,
270+
.threadId = getCurrentThreadId(),
271+
});
272+
enqueueEvent(PerformanceTracerResourceFinish{
273+
.requestId = requestId,
274+
.start = responseEnd,
275+
.threadId = getCurrentThreadId(),
276+
});
277+
}
278+
235279
/* static */ TraceEvent PerformanceTracer::constructRuntimeProfileTraceEvent(
236280
RuntimeProfileId profileId,
237281
ProcessId processId,
@@ -505,6 +549,73 @@ void PerformanceTracer::enqueueTraceEventsFromPerformanceTracerEvent(
505549
.args = folly::dynamic::object("data", std::move(data)),
506550
});
507551
},
552+
[&](PerformanceTracerResourceWillSendRequest&& event) {
553+
folly::dynamic data =
554+
folly::dynamic::object("requestId", std::move(event.requestId));
555+
556+
events.emplace_back(TraceEvent{
557+
.name = "ResourceWillSendRequest",
558+
.cat = "devtools.timeline",
559+
.ph = 'I',
560+
.ts = event.start,
561+
.pid = processId_,
562+
.s = 't',
563+
.tid = event.threadId,
564+
.args = folly::dynamic::object("data", std::move(data)),
565+
});
566+
},
567+
[&](PerformanceTracerResourceSendRequest&& event) {
568+
folly::dynamic data =
569+
folly::dynamic::object("initiator", folly::dynamic::object())(
570+
"renderBlocking", "non_blocking")(
571+
"requestId", std::move(event.requestId))(
572+
"requestMethod", std::move(event.requestMethod))(
573+
"resourceType", std::move(event.resourceType))(
574+
"url", std::move(event.url));
575+
576+
events.emplace_back(TraceEvent{
577+
.name = "ResourceSendRequest",
578+
.cat = "devtools.timeline",
579+
.ph = 'I',
580+
.ts = event.start,
581+
.pid = processId_,
582+
.s = 't',
583+
.tid = event.threadId,
584+
.args = folly::dynamic::object("data", std::move(data)),
585+
});
586+
},
587+
[&](PerformanceTracerResourceReceiveResponse&& event) {
588+
folly::dynamic data = folly::dynamic::object("protocol", "h2")(
589+
"requestId", std::move(event.requestId))(
590+
"statusCode", event.statusCode)(
591+
"timing", folly::dynamic::object());
592+
593+
events.emplace_back(TraceEvent{
594+
.name = "ResourceReceiveResponse",
595+
.cat = "devtools.timeline",
596+
.ph = 'I',
597+
.ts = event.start,
598+
.pid = processId_,
599+
.s = 't',
600+
.tid = event.threadId,
601+
.args = folly::dynamic::object("data", std::move(data)),
602+
});
603+
},
604+
[&](PerformanceTracerResourceFinish&& event) {
605+
folly::dynamic data = folly::dynamic::object("didFail", false)(
606+
"requestId", std::move(event.requestId));
607+
608+
events.emplace_back(TraceEvent{
609+
.name = "ResourceFinish",
610+
.cat = "devtools.timeline",
611+
.ph = 'I',
612+
.ts = event.start,
613+
.pid = processId_,
614+
.s = 't',
615+
.tid = event.threadId,
616+
.args = folly::dynamic::object("data", std::move(data)),
617+
});
618+
},
508619
},
509620
std::move(event));
510621
}

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

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,21 @@ class PerformanceTracer {
108108
*/
109109
void reportEventLoopMicrotasks(HighResTimeStamp start, HighResTimeStamp end);
110110

111+
/**
112+
* Record a "ResourceSendRequest"/"ResourceFinish" event pair - a labelled
113+
* duration in the Performance timeline Network track. If not currently
114+
* tracing, this is a no-op.
115+
*/
116+
void reportResourceTiming(
117+
const std::string& requestId,
118+
const std::string& url,
119+
HighResTimeStamp fetchStart,
120+
HighResTimeStamp responseStart,
121+
HighResTimeStamp responseEnd,
122+
int statusCode,
123+
const std::string& requestMethod,
124+
const std::string& resourceType);
125+
111126
/**
112127
* Creates "Profile" Trace Event.
113128
*
@@ -181,12 +196,48 @@ class PerformanceTracer {
181196
HighResTimeStamp createdAt = HighResTimeStamp::now();
182197
};
183198

199+
struct PerformanceTracerResourceWillSendRequest {
200+
std::string requestId;
201+
HighResTimeStamp start;
202+
ThreadId threadId;
203+
HighResTimeStamp createdAt = HighResTimeStamp::now();
204+
};
205+
206+
struct PerformanceTracerResourceSendRequest {
207+
std::string requestId;
208+
std::string url;
209+
HighResTimeStamp start;
210+
std::string requestMethod;
211+
std::string resourceType;
212+
ThreadId threadId;
213+
HighResTimeStamp createdAt = HighResTimeStamp::now();
214+
};
215+
216+
struct PerformanceTracerResourceFinish {
217+
std::string requestId;
218+
HighResTimeStamp start;
219+
ThreadId threadId;
220+
HighResTimeStamp createdAt = HighResTimeStamp::now();
221+
};
222+
223+
struct PerformanceTracerResourceReceiveResponse {
224+
std::string requestId;
225+
HighResTimeStamp start;
226+
int statusCode;
227+
ThreadId threadId;
228+
HighResTimeStamp createdAt = HighResTimeStamp::now();
229+
};
230+
184231
using PerformanceTracerEvent = std::variant<
185232
PerformanceTracerEventTimeStamp,
186233
PerformanceTracerEventEventLoopTask,
187234
PerformanceTracerEventEventLoopMicrotask,
188235
PerformanceTracerEventMark,
189-
PerformanceTracerEventMeasure>;
236+
PerformanceTracerEventMeasure,
237+
PerformanceTracerResourceWillSendRequest,
238+
PerformanceTracerResourceSendRequest,
239+
PerformanceTracerResourceReceiveResponse,
240+
PerformanceTracerResourceFinish>;
190241

191242
#pragma mark - Private fields and methods
192243

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ struct TraceEvent {
5555
/** The ID for the process that output this event. */
5656
ProcessId pid;
5757

58+
/**
59+
* The scope of the event, either global (g), process (p), or thread (t).
60+
* Only applicable to instant events ("ph": "i").
61+
*/
62+
std::optional<char> s;
63+
5864
/** The ID for the thread that output this event. */
5965
ThreadId tid;
6066

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ namespace facebook::react::jsinspector_modern::tracing {
2626
result["ph"] = std::string(1, event.ph);
2727
result["ts"] = highResTimeStampToTracingClockTimeStamp(event.ts);
2828
result["pid"] = event.pid;
29+
if (event.s.has_value()) {
30+
result["s"] = std::string(1, event.s.value());
31+
}
2932
result["tid"] = event.tid;
3033
result["args"] = std::move(event.args);
3134
if (event.dur.has_value()) {

packages/react-native/ReactCommon/react/networking/NetworkReporter.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
#include "NetworkReporter.h"
99

1010
#ifdef REACT_NATIVE_DEBUGGER_ENABLED
11-
#include "jsinspector-modern/network/NetworkHandler.h"
11+
#include <jsinspector-modern/network/CdpNetwork.h>
12+
#include <jsinspector-modern/network/HttpUtils.h>
13+
#include <jsinspector-modern/network/NetworkHandler.h>
1214
#endif
1315
#include <react/featureflags/ReactNativeFeatureFlags.h>
1416
#include <react/performance/timeline/PerformanceEntryReporter.h>
@@ -43,6 +45,7 @@ void NetworkReporter::reportRequestStart(
4345
requestId,
4446
ResourceTimingData{
4547
.url = requestInfo.url,
48+
.requestMethod = requestInfo.httpMethod,
4649
.fetchStart = now,
4750
.requestStart = now,
4851
});
@@ -108,6 +111,13 @@ void NetworkReporter::reportResponseStart(
108111
it->second.connectEnd = now;
109112
it->second.responseStart = now;
110113
it->second.responseStatus = responseInfo.statusCode;
114+
#ifdef REACT_NATIVE_DEBUGGER_ENABLED
115+
// Debug build: Compute additional fields to send in CDP trace events
116+
it->second.resourceType =
117+
jsinspector_modern::cdp::network::resourceTypeFromMimeType(
118+
jsinspector_modern::mimeTypeFromHeaders(
119+
responseInfo.headers.value_or(Headers{})));
120+
#endif
111121
}
112122
}
113123
}
@@ -155,7 +165,10 @@ void NetworkReporter::reportResponseEnd(
155165
eventData.connectEnd.value_or(now),
156166
eventData.responseStart.value_or(now),
157167
now,
158-
eventData.responseStatus);
168+
eventData.responseStatus,
169+
requestId,
170+
eventData.requestMethod,
171+
eventData.resourceType);
159172
perfTimingsBuffer_.erase(requestId);
160173
}
161174
}

packages/react-native/ReactCommon/react/networking/NetworkReporter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ namespace facebook::react {
2828
*/
2929
struct ResourceTimingData {
3030
std::string url;
31+
std::string requestMethod;
32+
std::optional<std::string> resourceType;
3133
HighResTimeStamp fetchStart;
3234
HighResTimeStamp requestStart;
3335
std::optional<HighResTimeStamp> connectStart;

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

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,10 @@ void PerformanceEntryReporter::reportResourceTiming(
307307
std::optional<HighResTimeStamp> connectEnd,
308308
HighResTimeStamp responseStart,
309309
HighResTimeStamp responseEnd,
310-
const std::optional<int>& responseStatus) {
310+
const std::optional<int>& responseStatus,
311+
const std::optional<std::string>& devtoolsRequestId,
312+
const std::optional<std::string>& requestMethod,
313+
const std::optional<std::string>& resourceType) {
311314
const auto entry = PerformanceResourceTiming{
312315
{.name = url, .startTime = fetchStart},
313316
fetchStart,
@@ -319,6 +322,8 @@ void PerformanceEntryReporter::reportResourceTiming(
319322
responseStatus,
320323
};
321324

325+
traceResourceTiming(entry, devtoolsRequestId, requestMethod, resourceType);
326+
322327
// Add to buffers & notify observers
323328
{
324329
std::unique_lock lock(buffersMutex_);
@@ -370,4 +375,31 @@ void PerformanceEntryReporter::traceMeasure(
370375
}
371376
}
372377

378+
void PerformanceEntryReporter::traceResourceTiming(
379+
const PerformanceResourceTiming& entry,
380+
const std::optional<std::string>& devtoolsRequestId,
381+
const std::optional<std::string>& requestMethod,
382+
const std::optional<std::string>& resourceType) const {
383+
if (!entry.responseStart.has_value() || !entry.responseEnd.has_value() ||
384+
!entry.responseStatus.has_value() || !devtoolsRequestId.has_value() ||
385+
!requestMethod.has_value() || !resourceType.has_value()) {
386+
return;
387+
}
388+
389+
auto& performanceTracer =
390+
jsinspector_modern::tracing::PerformanceTracer::getInstance();
391+
392+
if (performanceTracer.isTracing()) {
393+
performanceTracer.reportResourceTiming(
394+
*devtoolsRequestId,
395+
entry.name,
396+
entry.fetchStart,
397+
*entry.responseStart,
398+
*entry.responseEnd,
399+
*entry.responseStatus,
400+
*requestMethod,
401+
*resourceType);
402+
}
403+
}
404+
373405
} // namespace facebook::react

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,10 @@ class PerformanceEntryReporter {
118118
std::optional<HighResTimeStamp> connectEnd,
119119
HighResTimeStamp responseStart,
120120
HighResTimeStamp responseEnd,
121-
const std::optional<int>& responseStatus);
121+
const std::optional<int>& responseStatus,
122+
const std::optional<std::string>& devtoolsRequestId,
123+
const std::optional<std::string>& requestMethod,
124+
const std::optional<std::string>& resourceType);
122125

123126
private:
124127
std::unique_ptr<PerformanceObserverRegistry> observerRegistry_;
@@ -182,6 +185,11 @@ class PerformanceEntryReporter {
182185
void traceMeasure(
183186
const PerformanceMeasure& entry,
184187
UserTimingDetailProvider&& detailProvider) const;
188+
void traceResourceTiming(
189+
const PerformanceResourceTiming& entry,
190+
const std::optional<std::string>& devtoolsRequestId,
191+
const std::optional<std::string>& requestMethod,
192+
const std::optional<std::string>& resourceType) const;
185193
};
186194

187195
} // namespace facebook::react

0 commit comments

Comments
 (0)