mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Implement support for Network trace events (#53761)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/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
This commit is contained in:
committed by
Facebook GitHub Bot
parent
0caf8e70d5
commit
dba8dbdeeb
@@ -232,6 +232,50 @@ void PerformanceTracer::reportEventLoopMicrotasks(
|
||||
});
|
||||
}
|
||||
|
||||
void PerformanceTracer::reportResourceTiming(
|
||||
const std::string& requestId,
|
||||
const std::string& url,
|
||||
HighResTimeStamp fetchStart,
|
||||
HighResTimeStamp responseStart,
|
||||
HighResTimeStamp responseEnd,
|
||||
int statusCode,
|
||||
const std::string& requestMethod,
|
||||
const std::string& resourceType) {
|
||||
if (!tracingAtomic_) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (!tracingAtomic_) {
|
||||
return;
|
||||
}
|
||||
|
||||
enqueueEvent(PerformanceTracerResourceWillSendRequest{
|
||||
.requestId = requestId,
|
||||
.start = fetchStart,
|
||||
.threadId = getCurrentThreadId(),
|
||||
});
|
||||
enqueueEvent(PerformanceTracerResourceSendRequest{
|
||||
.requestId = requestId,
|
||||
.url = url,
|
||||
.start = fetchStart,
|
||||
.requestMethod = requestMethod,
|
||||
.resourceType = resourceType,
|
||||
.threadId = getCurrentThreadId(),
|
||||
});
|
||||
enqueueEvent(PerformanceTracerResourceReceiveResponse{
|
||||
.requestId = requestId,
|
||||
.start = responseStart,
|
||||
.statusCode = statusCode,
|
||||
.threadId = getCurrentThreadId(),
|
||||
});
|
||||
enqueueEvent(PerformanceTracerResourceFinish{
|
||||
.requestId = requestId,
|
||||
.start = responseEnd,
|
||||
.threadId = getCurrentThreadId(),
|
||||
});
|
||||
}
|
||||
|
||||
/* static */ TraceEvent PerformanceTracer::constructRuntimeProfileTraceEvent(
|
||||
RuntimeProfileId profileId,
|
||||
ProcessId processId,
|
||||
@@ -505,6 +549,73 @@ void PerformanceTracer::enqueueTraceEventsFromPerformanceTracerEvent(
|
||||
.args = folly::dynamic::object("data", std::move(data)),
|
||||
});
|
||||
},
|
||||
[&](PerformanceTracerResourceWillSendRequest&& event) {
|
||||
folly::dynamic data =
|
||||
folly::dynamic::object("requestId", std::move(event.requestId));
|
||||
|
||||
events.emplace_back(TraceEvent{
|
||||
.name = "ResourceWillSendRequest",
|
||||
.cat = "devtools.timeline",
|
||||
.ph = 'I',
|
||||
.ts = event.start,
|
||||
.pid = processId_,
|
||||
.s = 't',
|
||||
.tid = event.threadId,
|
||||
.args = folly::dynamic::object("data", std::move(data)),
|
||||
});
|
||||
},
|
||||
[&](PerformanceTracerResourceSendRequest&& event) {
|
||||
folly::dynamic data =
|
||||
folly::dynamic::object("initiator", folly::dynamic::object())(
|
||||
"renderBlocking", "non_blocking")(
|
||||
"requestId", std::move(event.requestId))(
|
||||
"requestMethod", std::move(event.requestMethod))(
|
||||
"resourceType", std::move(event.resourceType))(
|
||||
"url", std::move(event.url));
|
||||
|
||||
events.emplace_back(TraceEvent{
|
||||
.name = "ResourceSendRequest",
|
||||
.cat = "devtools.timeline",
|
||||
.ph = 'I',
|
||||
.ts = event.start,
|
||||
.pid = processId_,
|
||||
.s = 't',
|
||||
.tid = event.threadId,
|
||||
.args = folly::dynamic::object("data", std::move(data)),
|
||||
});
|
||||
},
|
||||
[&](PerformanceTracerResourceReceiveResponse&& event) {
|
||||
folly::dynamic data = folly::dynamic::object("protocol", "h2")(
|
||||
"requestId", std::move(event.requestId))(
|
||||
"statusCode", event.statusCode)(
|
||||
"timing", folly::dynamic::object());
|
||||
|
||||
events.emplace_back(TraceEvent{
|
||||
.name = "ResourceReceiveResponse",
|
||||
.cat = "devtools.timeline",
|
||||
.ph = 'I',
|
||||
.ts = event.start,
|
||||
.pid = processId_,
|
||||
.s = 't',
|
||||
.tid = event.threadId,
|
||||
.args = folly::dynamic::object("data", std::move(data)),
|
||||
});
|
||||
},
|
||||
[&](PerformanceTracerResourceFinish&& event) {
|
||||
folly::dynamic data = folly::dynamic::object("didFail", false)(
|
||||
"requestId", std::move(event.requestId));
|
||||
|
||||
events.emplace_back(TraceEvent{
|
||||
.name = "ResourceFinish",
|
||||
.cat = "devtools.timeline",
|
||||
.ph = 'I',
|
||||
.ts = event.start,
|
||||
.pid = processId_,
|
||||
.s = 't',
|
||||
.tid = event.threadId,
|
||||
.args = folly::dynamic::object("data", std::move(data)),
|
||||
});
|
||||
},
|
||||
},
|
||||
std::move(event));
|
||||
}
|
||||
|
||||
@@ -108,6 +108,21 @@ class PerformanceTracer {
|
||||
*/
|
||||
void reportEventLoopMicrotasks(HighResTimeStamp start, HighResTimeStamp end);
|
||||
|
||||
/**
|
||||
* Record a "ResourceSendRequest"/"ResourceFinish" event pair - a labelled
|
||||
* duration in the Performance timeline Network track. If not currently
|
||||
* tracing, this is a no-op.
|
||||
*/
|
||||
void reportResourceTiming(
|
||||
const std::string& requestId,
|
||||
const std::string& url,
|
||||
HighResTimeStamp fetchStart,
|
||||
HighResTimeStamp responseStart,
|
||||
HighResTimeStamp responseEnd,
|
||||
int statusCode,
|
||||
const std::string& requestMethod,
|
||||
const std::string& resourceType);
|
||||
|
||||
/**
|
||||
* Creates "Profile" Trace Event.
|
||||
*
|
||||
@@ -181,12 +196,48 @@ class PerformanceTracer {
|
||||
HighResTimeStamp createdAt = HighResTimeStamp::now();
|
||||
};
|
||||
|
||||
struct PerformanceTracerResourceWillSendRequest {
|
||||
std::string requestId;
|
||||
HighResTimeStamp start;
|
||||
ThreadId threadId;
|
||||
HighResTimeStamp createdAt = HighResTimeStamp::now();
|
||||
};
|
||||
|
||||
struct PerformanceTracerResourceSendRequest {
|
||||
std::string requestId;
|
||||
std::string url;
|
||||
HighResTimeStamp start;
|
||||
std::string requestMethod;
|
||||
std::string resourceType;
|
||||
ThreadId threadId;
|
||||
HighResTimeStamp createdAt = HighResTimeStamp::now();
|
||||
};
|
||||
|
||||
struct PerformanceTracerResourceFinish {
|
||||
std::string requestId;
|
||||
HighResTimeStamp start;
|
||||
ThreadId threadId;
|
||||
HighResTimeStamp createdAt = HighResTimeStamp::now();
|
||||
};
|
||||
|
||||
struct PerformanceTracerResourceReceiveResponse {
|
||||
std::string requestId;
|
||||
HighResTimeStamp start;
|
||||
int statusCode;
|
||||
ThreadId threadId;
|
||||
HighResTimeStamp createdAt = HighResTimeStamp::now();
|
||||
};
|
||||
|
||||
using PerformanceTracerEvent = std::variant<
|
||||
PerformanceTracerEventTimeStamp,
|
||||
PerformanceTracerEventEventLoopTask,
|
||||
PerformanceTracerEventEventLoopMicrotask,
|
||||
PerformanceTracerEventMark,
|
||||
PerformanceTracerEventMeasure>;
|
||||
PerformanceTracerEventMeasure,
|
||||
PerformanceTracerResourceWillSendRequest,
|
||||
PerformanceTracerResourceSendRequest,
|
||||
PerformanceTracerResourceReceiveResponse,
|
||||
PerformanceTracerResourceFinish>;
|
||||
|
||||
#pragma mark - Private fields and methods
|
||||
|
||||
|
||||
@@ -55,6 +55,12 @@ struct TraceEvent {
|
||||
/** The ID for the process that output this event. */
|
||||
ProcessId pid;
|
||||
|
||||
/**
|
||||
* The scope of the event, either global (g), process (p), or thread (t).
|
||||
* Only applicable to instant events ("ph": "i").
|
||||
*/
|
||||
std::optional<char> s;
|
||||
|
||||
/** The ID for the thread that output this event. */
|
||||
ThreadId tid;
|
||||
|
||||
|
||||
@@ -26,6 +26,9 @@ namespace facebook::react::jsinspector_modern::tracing {
|
||||
result["ph"] = std::string(1, event.ph);
|
||||
result["ts"] = highResTimeStampToTracingClockTimeStamp(event.ts);
|
||||
result["pid"] = event.pid;
|
||||
if (event.s.has_value()) {
|
||||
result["s"] = std::string(1, event.s.value());
|
||||
}
|
||||
result["tid"] = event.tid;
|
||||
result["args"] = std::move(event.args);
|
||||
if (event.dur.has_value()) {
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
#include "NetworkReporter.h"
|
||||
|
||||
#ifdef REACT_NATIVE_DEBUGGER_ENABLED
|
||||
#include "jsinspector-modern/network/NetworkHandler.h"
|
||||
#include <jsinspector-modern/network/CdpNetwork.h>
|
||||
#include <jsinspector-modern/network/HttpUtils.h>
|
||||
#include <jsinspector-modern/network/NetworkHandler.h>
|
||||
#endif
|
||||
#include <react/featureflags/ReactNativeFeatureFlags.h>
|
||||
#include <react/performance/timeline/PerformanceEntryReporter.h>
|
||||
@@ -43,6 +45,7 @@ void NetworkReporter::reportRequestStart(
|
||||
requestId,
|
||||
ResourceTimingData{
|
||||
.url = requestInfo.url,
|
||||
.requestMethod = requestInfo.httpMethod,
|
||||
.fetchStart = now,
|
||||
.requestStart = now,
|
||||
});
|
||||
@@ -108,6 +111,13 @@ void NetworkReporter::reportResponseStart(
|
||||
it->second.connectEnd = now;
|
||||
it->second.responseStart = now;
|
||||
it->second.responseStatus = responseInfo.statusCode;
|
||||
#ifdef REACT_NATIVE_DEBUGGER_ENABLED
|
||||
// Debug build: Compute additional fields to send in CDP trace events
|
||||
it->second.resourceType =
|
||||
jsinspector_modern::cdp::network::resourceTypeFromMimeType(
|
||||
jsinspector_modern::mimeTypeFromHeaders(
|
||||
responseInfo.headers.value_or(Headers{})));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -155,7 +165,10 @@ void NetworkReporter::reportResponseEnd(
|
||||
eventData.connectEnd.value_or(now),
|
||||
eventData.responseStart.value_or(now),
|
||||
now,
|
||||
eventData.responseStatus);
|
||||
eventData.responseStatus,
|
||||
requestId,
|
||||
eventData.requestMethod,
|
||||
eventData.resourceType);
|
||||
perfTimingsBuffer_.erase(requestId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ namespace facebook::react {
|
||||
*/
|
||||
struct ResourceTimingData {
|
||||
std::string url;
|
||||
std::string requestMethod;
|
||||
std::optional<std::string> resourceType;
|
||||
HighResTimeStamp fetchStart;
|
||||
HighResTimeStamp requestStart;
|
||||
std::optional<HighResTimeStamp> connectStart;
|
||||
|
||||
+33
-1
@@ -307,7 +307,10 @@ void PerformanceEntryReporter::reportResourceTiming(
|
||||
std::optional<HighResTimeStamp> connectEnd,
|
||||
HighResTimeStamp responseStart,
|
||||
HighResTimeStamp responseEnd,
|
||||
const std::optional<int>& responseStatus) {
|
||||
const std::optional<int>& responseStatus,
|
||||
const std::optional<std::string>& devtoolsRequestId,
|
||||
const std::optional<std::string>& requestMethod,
|
||||
const std::optional<std::string>& resourceType) {
|
||||
const auto entry = PerformanceResourceTiming{
|
||||
{.name = url, .startTime = fetchStart},
|
||||
fetchStart,
|
||||
@@ -319,6 +322,8 @@ void PerformanceEntryReporter::reportResourceTiming(
|
||||
responseStatus,
|
||||
};
|
||||
|
||||
traceResourceTiming(entry, devtoolsRequestId, requestMethod, resourceType);
|
||||
|
||||
// Add to buffers & notify observers
|
||||
{
|
||||
std::unique_lock lock(buffersMutex_);
|
||||
@@ -370,4 +375,31 @@ void PerformanceEntryReporter::traceMeasure(
|
||||
}
|
||||
}
|
||||
|
||||
void PerformanceEntryReporter::traceResourceTiming(
|
||||
const PerformanceResourceTiming& entry,
|
||||
const std::optional<std::string>& devtoolsRequestId,
|
||||
const std::optional<std::string>& requestMethod,
|
||||
const std::optional<std::string>& resourceType) const {
|
||||
if (!entry.responseStart.has_value() || !entry.responseEnd.has_value() ||
|
||||
!entry.responseStatus.has_value() || !devtoolsRequestId.has_value() ||
|
||||
!requestMethod.has_value() || !resourceType.has_value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& performanceTracer =
|
||||
jsinspector_modern::tracing::PerformanceTracer::getInstance();
|
||||
|
||||
if (performanceTracer.isTracing()) {
|
||||
performanceTracer.reportResourceTiming(
|
||||
*devtoolsRequestId,
|
||||
entry.name,
|
||||
entry.fetchStart,
|
||||
*entry.responseStart,
|
||||
*entry.responseEnd,
|
||||
*entry.responseStatus,
|
||||
*requestMethod,
|
||||
*resourceType);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace facebook::react
|
||||
|
||||
+9
-1
@@ -118,7 +118,10 @@ class PerformanceEntryReporter {
|
||||
std::optional<HighResTimeStamp> connectEnd,
|
||||
HighResTimeStamp responseStart,
|
||||
HighResTimeStamp responseEnd,
|
||||
const std::optional<int>& responseStatus);
|
||||
const std::optional<int>& responseStatus,
|
||||
const std::optional<std::string>& devtoolsRequestId,
|
||||
const std::optional<std::string>& requestMethod,
|
||||
const std::optional<std::string>& resourceType);
|
||||
|
||||
private:
|
||||
std::unique_ptr<PerformanceObserverRegistry> observerRegistry_;
|
||||
@@ -182,6 +185,11 @@ class PerformanceEntryReporter {
|
||||
void traceMeasure(
|
||||
const PerformanceMeasure& entry,
|
||||
UserTimingDetailProvider&& detailProvider) const;
|
||||
void traceResourceTiming(
|
||||
const PerformanceResourceTiming& entry,
|
||||
const std::optional<std::string>& devtoolsRequestId,
|
||||
const std::optional<std::string>& requestMethod,
|
||||
const std::optional<std::string>& resourceType) const;
|
||||
};
|
||||
|
||||
} // namespace facebook::react
|
||||
|
||||
Reference in New Issue
Block a user