Refactor PerformanceTracer buffer type to use output event format (#48310)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48310

Refactors the internal storage format of trace events buffered by `PerformanceTracer`.

Aligning with the emitted [Trace Event Format](https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview?pli=1&tab=t.0#heading=h.yr4qxyxotyw) enables us to simplify away the issue of defining and converting from any intermediate formats. This becomes desirable as we generalise to more event types and forthcoming browser-emulating `__metadata` events.

Changelog: [Internal]

Reviewed By: hoxyq

Differential Revision: D67337442

fbshipit-source-id: 580928dfb4fcf4ac5efc82f93ab85a0d1d6dfb5c
This commit is contained in:
Alex Hunt
2024-12-17 10:57:39 -08:00
committed by Facebook GitHub Bot
parent 8696b79f73
commit dfdacb84ce
2 changed files with 102 additions and 155 deletions
@@ -53,28 +53,45 @@ bool PerformanceTracer::stopTracingAndCollectEvents(
return true;
}
auto traceEvents = folly::dynamic::array();
// Register "Main" process
traceEvents.push_back(folly::dynamic::object(
"args", folly::dynamic::object("name", "Main"))("cat", "__metadata")(
"name", "process_name")("ph", "M")("pid", PID)("tid", 0)("ts", 0));
buffer_.push_back(TraceEvent{
.name = "process_name",
.cat = "__metadata",
.ph = 'M',
.ts = 0,
.pid = PID,
.tid = 0,
.args = folly::dynamic::object("name", "Main"),
});
// Register "Timings" track
// NOTE: This is a hack to make the trace viewer show a "Timings" track
// NOTE: This is a hack to make the Trace Viewer show a "Timings" track
// adjacent to custom tracks in our current build of Chrome DevTools.
// In future, we should align events exactly.
traceEvents.push_back(
folly::dynamic::object("args", folly::dynamic::object("name", "Timings"))(
"cat", "__metadata")("name", "thread_name")("ph", "M")("pid", PID)(
"tid", USER_TIMINGS_DEFAULT_TRACK)("ts", 0));
buffer_.push_back(TraceEvent{
.name = "thread_name",
.cat = "__metadata",
.ph = 'M',
.ts = 0,
.pid = PID,
.tid = USER_TIMINGS_DEFAULT_TRACK,
.args = folly::dynamic::object("name", "Timings"),
});
for (const auto& [trackName, trackId] : customTrackIdMap_) {
// Register custom tracks
traceEvents.push_back(folly::dynamic::object(
"args", folly::dynamic::object("name", trackName))("cat", "__metadata")(
"name", "thread_name")("ph", "M")("pid", PID)("tid", trackId)("ts", 0));
buffer_.push_back(TraceEvent{
.name = "thread_name",
.cat = "__metadata",
.ph = 'M',
.ts = 0,
.pid = PID,
.tid = trackId,
.args = folly::dynamic::object("name", trackName),
});
}
auto traceEvents = folly::dynamic::array();
for (auto event : buffer_) {
// Emit trace events
traceEvents.push_back(serializeTraceEvent(event));
@@ -102,14 +119,15 @@ void PerformanceTracer::reportMark(
return;
}
TraceEventBase* event = new InstantTraceEvent{
std::string(name),
std::vector{TraceEventCategory::UserTiming},
start,
PID, // FIXME: This should be real process ID.
USER_TIMINGS_DEFAULT_TRACK, // FIXME: This should be real thread ID.
};
buffer_.push_back(event);
buffer_.push_back(TraceEvent{
.name = std::string(name),
.cat = "blink.user_timing",
.ph = 'I',
.ts = start,
.pid = PID, // FIXME: This should be the real process ID.
.tid = USER_TIMINGS_DEFAULT_TRACK, // FIXME: This should be the real
// thread ID.
});
}
void PerformanceTracer::reportMeasure(
@@ -122,8 +140,10 @@ void PerformanceTracer::reportMeasure(
return;
}
uint64_t threadId =
USER_TIMINGS_DEFAULT_TRACK; // FIXME: This should be real thread ID.
// NOTE: We synthetically create custom tracks as a hack to render them in
// our current build of Chrome DevTools frontend.
// TODO: Remove and align with web.
uint64_t threadId = USER_TIMINGS_DEFAULT_TRACK;
if (trackMetadata.has_value()) {
std::string trackName = trackMetadata.value().track;
@@ -136,71 +156,29 @@ void PerformanceTracer::reportMeasure(
}
}
TraceEventBase* event = new CompleteTraceEvent{
std::string(name),
std::vector{TraceEventCategory::UserTiming},
start,
PID, // FIXME: This should be real process ID.
threadId, // FIXME: This should be real thread ID.
duration};
buffer_.push_back(event);
buffer_.push_back(TraceEvent{
.name = std::string(name),
.cat = "blink.user_timing",
.ph = 'X',
.ts = start,
.pid = PID, // FIXME: This should be the real process ID.
.tid = threadId, // FIXME: This should be the real thread ID.
.dur = duration,
});
}
std::string PerformanceTracer::serializeTraceEventCategories(
TraceEventBase* event) const {
std::string result;
for (const auto& category : event->categories) {
switch (category) {
case TraceEventCategory::UserTiming:
result += "blink.user_timing";
break;
case TraceEventCategory::TimelineEvent:
result += "disabled-by-default-devtools.timeline";
break;
default:
throw std::runtime_error("Unknown trace event category");
}
result += ",";
}
if (result.length() > 0) {
result.pop_back();
}
return result;
}
folly::dynamic PerformanceTracer::serializeTraceEvent(
TraceEventBase* event) const {
folly::dynamic PerformanceTracer::serializeTraceEvent(TraceEvent event) const {
folly::dynamic result = folly::dynamic::object;
result["name"] = event->name;
result["cat"] = serializeTraceEventCategories(event);
result["args"] = event->args;
result["ts"] = event->timestamp;
result["pid"] = event->processId;
result["tid"] = event->threadId;
switch (event->type) {
case TraceEventType::Instant:
result["ph"] = "I";
break;
case TraceEventType::Complete: {
result["ph"] = "X";
auto completeEvent = static_cast<CompleteTraceEvent*>(event);
result["dur"] = completeEvent->duration;
break;
}
default:
throw std::runtime_error("Unknown trace event type");
result["name"] = event.name;
result["cat"] = event.cat;
result["ph"] = std::string(1, event.ph);
result["ts"] = event.ts;
result["pid"] = event.pid;
result["tid"] = event.tid;
result["args"] = event.args;
if (event.dur.has_value()) {
result["dur"] = event.dur.value();
}
return result;
@@ -21,80 +21,50 @@ namespace facebook::react::jsinspector_modern {
// TODO: Review how this API is integrated into jsinspector_modern (singleton
// design is copied from earlier FuseboxTracer prototype).
enum class TraceEventType {
Instant,
Complete,
};
enum class TraceEventCategory {
UserTiming,
TimelineEvent,
};
/*
* Based on the out-of-date "Trace Event Format" document from Google and our
* findings while reverse engineering the contract between Chrome and Chrome
* DevTools.
namespace {
/**
* A trace event to send to the debugger frontend, as defined by the Trace Event
* Format.
* https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview?pli=1&tab=t.0#heading=h.yr4qxyxotyw
*/
struct TraceEventBase {
*/
struct TraceEvent {
/** The name of the event, as displayed in the Trace Viewer. */
std::string name;
std::vector<TraceEventCategory> categories;
TraceEventType type;
uint64_t timestamp;
uint64_t processId;
uint64_t threadId;
/**
* A comma separated list of categories for the event, configuring how
* events are shown in the Trace Viewer UI.
*/
std::string cat;
/**
* The event type. This is a single character which changes depending on the
* type of event being output. See
* https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview?pli=1&tab=t.0#heading=h.puwqg050lyuy
*/
char ph;
/** The tracing clock timestamp of the event, in microseconds (µs). */
uint64_t ts;
/** The process ID for the process that output this event. */
uint64_t pid;
/** The thread ID for the process that output this event. */
uint64_t tid;
/** Any arguments provided for the event. */
folly::dynamic args = folly::dynamic::object();
/**
* The duration of the event, in microseconds (µs). Only applicable to
* complete events ("ph": "X").
*/
std::optional<uint64_t> dur;
};
template <TraceEventType T>
struct TraceEvent : public TraceEventBase {
TraceEvent(
std::string name,
std::vector<TraceEventCategory> categories,
uint64_t timestamp,
uint64_t processId,
uint64_t threadId)
: TraceEventBase{
std::move(name),
std::move(categories),
T,
timestamp,
processId,
threadId} {}
};
struct CompleteTraceEvent : public TraceEvent<TraceEventType::Complete> {
uint64_t duration;
CompleteTraceEvent(
std::string name,
std::vector<TraceEventCategory> categories,
uint64_t timestamp,
uint64_t processId,
uint64_t threadId,
uint64_t duration)
: TraceEvent<
TraceEventType::
Complete>{std::move(name), std::move(categories), timestamp, processId, threadId},
duration(duration) {}
};
struct InstantTraceEvent : public TraceEvent<TraceEventType::Instant> {
InstantTraceEvent(
std::string name,
std::vector<TraceEventCategory> categories,
uint64_t timestamp,
uint64_t processId,
uint64_t threadId)
: TraceEvent<TraceEventType::Instant>{
std::move(name),
std::move(categories),
timestamp,
processId,
threadId} {}
};
} // namespace
/**
* [Experimental] An interface for logging performance trace events to the
@@ -145,12 +115,11 @@ class PerformanceTracer {
PerformanceTracer& operator=(const PerformanceTracer&) = delete;
~PerformanceTracer() = default;
std::string serializeTraceEventCategories(TraceEventBase* event) const;
folly::dynamic serializeTraceEvent(TraceEventBase* event) const;
folly::dynamic serializeTraceEvent(TraceEvent event) const;
bool tracing_{false};
std::unordered_map<std::string, uint64_t> customTrackIdMap_;
std::vector<TraceEventBase*> buffer_;
std::vector<TraceEvent> buffer_;
std::mutex mutex_;
};