Use HighResTimeStamp (#51511)

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

# Changelog: [Internal]

There are multiple changes:
1. `PerformanceTracer` class, `TraceEvent` struct are moved to `tracing` namespace. These are parts of the Tracing subsystems of the jsinspector, this should bring more clarity and make things more explicit.
2. Added `Timing.h` class which defines conversion logic from `HighResTimeStamp` to absolute units that are expected by CDP.
3. `PerformanceTracer` will receive timestamps for Performance Web API entries in `HighResTimeStamp`.

Also, we will explicilty define a Tracking Clock time origin that will be epoch of the `steady_clock`. This aligns with the approach in Chromium and saves us from aligning custom DOMHighResTimeStamps that can be specified in performance.mark / performance.measure calls: these should not extend the timeline window. I've confirmed that this is the current behavior in Chromium.

Reviewed By: rubennorte, huntie

Differential Revision: D75185467

fbshipit-source-id: 37444392f12e8c9c4479c47c42b2c4badca7ecfd
This commit is contained in:
Ruslan Lesiutin
2025-05-22 10:16:45 -07:00
committed by Facebook GitHub Bot
parent cd01ecbb6a
commit aa7202861f
16 changed files with 163 additions and 123 deletions
@@ -179,7 +179,7 @@ RuntimeTargetController::collectSamplingProfile() {
void RuntimeTarget::registerForTracing() {
jsExecutor_([](auto& /*runtime*/) {
PerformanceTracer::getInstance().reportJavaScriptThread();
tracing::PerformanceTracer::getInstance().reportJavaScriptThread();
});
}
@@ -44,7 +44,7 @@ bool TracingAgent::handleRequest(const cdp::PreparsedRequest& req) {
}
bool correctlyStartedPerformanceTracer =
PerformanceTracer::getInstance().startTracing();
tracing::PerformanceTracer::getInstance().startTracing();
if (!correctlyStartedPerformanceTracer) {
frontendChannel_(cdp::jsonError(
@@ -56,7 +56,7 @@ bool TracingAgent::handleRequest(const cdp::PreparsedRequest& req) {
}
instanceAgent_->startTracing();
instanceTracingStartTimestamp_ = std::chrono::steady_clock::now();
instanceTracingStartTimestamp_ = HighResTimeStamp::now();
frontendChannel_(cdp::jsonResult(req.id));
return true;
@@ -73,7 +73,8 @@ bool TracingAgent::handleRequest(const cdp::PreparsedRequest& req) {
instanceAgent_->stopTracing();
PerformanceTracer& performanceTracer = PerformanceTracer::getInstance();
tracing::PerformanceTracer& performanceTracer =
tracing::PerformanceTracer::getInstance();
bool correctlyStopped = performanceTracer.stopTracing();
if (!correctlyStopped) {
frontendChannel_(cdp::jsonError(
@@ -11,6 +11,8 @@
#include "InstanceAgent.h"
#include <jsinspector-modern/cdp/CdpJson.h>
#include <jsinspector-modern/tracing/Timing.h>
#include <react/timing/primitives.h>
namespace facebook::react::jsinspector_modern {
@@ -54,9 +56,10 @@ class TracingAgent {
/**
* Timestamp of when we started tracing of an Instance, will be used as a
* a start of JavaScript samples recording.
* a start of JavaScript samples recording and as a time origin for the events
* in this trace.
*/
std::chrono::steady_clock::time_point instanceTracingStartTimestamp_;
HighResTimeStamp instanceTracingStartTimestamp_;
};
} // namespace facebook::react::jsinspector_modern
@@ -19,6 +19,7 @@ target_include_directories(jsinspector_tracing PUBLIC ${REACT_COMMON_DIR})
target_link_libraries(jsinspector_tracing
folly_runtime
oscompat
react_timing
)
target_compile_reactnative_options(jsinspector_tracing PRIVATE)
target_compile_options(jsinspector_tracing PRIVATE -Wpedantic)
@@ -14,35 +14,21 @@
namespace facebook::react::jsinspector_modern::tracing {
#if defined(REACT_NATIVE_DEBUGGER_ENABLED)
namespace {
inline uint64_t formatTimePointToUnixTimestamp(
std::chrono::steady_clock::time_point timestamp) {
return std::chrono::duration_cast<std::chrono::microseconds>(
timestamp.time_since_epoch())
.count();
}
} // namespace
EventLoopReporter::EventLoopReporter(EventLoopPhase phase)
: startTimestamp_(std::chrono::steady_clock::now()), phase_(phase) {}
: startTimestamp_(HighResTimeStamp::now()), phase_(phase) {}
EventLoopReporter::~EventLoopReporter() {
PerformanceTracer& performanceTracer = PerformanceTracer::getInstance();
if (performanceTracer.isTracing()) {
auto end = std::chrono::steady_clock::now();
auto end = HighResTimeStamp::now();
switch (phase_) {
case EventLoopPhase::Task:
performanceTracer.reportEventLoopTask(
formatTimePointToUnixTimestamp(startTimestamp_),
formatTimePointToUnixTimestamp(end));
performanceTracer.reportEventLoopTask(startTimestamp_, end);
break;
case EventLoopPhase::Microtasks:
performanceTracer.reportEventLoopMicrotasks(
formatTimePointToUnixTimestamp(startTimestamp_),
formatTimePointToUnixTimestamp(end));
performanceTracer.reportEventLoopMicrotasks(startTimestamp_, end);
break;
default:
@@ -7,7 +7,9 @@
#pragma once
#include <chrono>
#if defined(REACT_NATIVE_DEBUGGER_ENABLED)
#include <react/timing/primitives.h>
#endif
namespace facebook::react::jsinspector_modern::tracing {
@@ -29,7 +31,7 @@ struct EventLoopReporter {
private:
#if defined(REACT_NATIVE_DEBUGGER_ENABLED)
std::chrono::steady_clock::time_point startTimestamp_;
HighResTimeStamp startTimestamp_;
EventLoopPhase phase_;
#endif
};
@@ -6,6 +6,7 @@
*/
#include "PerformanceTracer.h"
#include "Timing.h"
#include <oscompat/OSCompat.h>
@@ -14,17 +15,7 @@
#include <array>
#include <mutex>
namespace facebook::react::jsinspector_modern {
namespace {
uint64_t getUnixTimestampOfNow() {
return std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::steady_clock::now().time_since_epoch())
.count();
}
} // namespace
namespace facebook::react::jsinspector_modern::tracing {
PerformanceTracer& PerformanceTracer::getInstance() {
static PerformanceTracer tracer;
@@ -52,7 +43,7 @@ bool PerformanceTracer::startTracing() {
.name = "TracingStartedInPage",
.cat = "disabled-by-default-devtools.timeline",
.ph = 'I',
.ts = getUnixTimestampOfNow(),
.ts = HighResTimeStamp::now(),
.pid = processId_,
.tid = oscompat::getCurrentThreadId(),
.args = folly::dynamic::object("data", folly::dynamic::object()),
@@ -78,7 +69,7 @@ bool PerformanceTracer::stopTracing() {
.name = "ReactNative-TracingStopped",
.cat = "disabled-by-default-devtools.timeline",
.ph = 'I',
.ts = getUnixTimestampOfNow(),
.ts = HighResTimeStamp::now(),
.pid = processId_,
.tid = oscompat::getCurrentThreadId(),
});
@@ -117,7 +108,7 @@ void PerformanceTracer::collectEvents(
void PerformanceTracer::reportMark(
const std::string_view& name,
uint64_t start) {
HighResTimeStamp start) {
if (!tracing_) {
return;
}
@@ -139,8 +130,8 @@ void PerformanceTracer::reportMark(
void PerformanceTracer::reportMeasure(
const std::string_view& name,
uint64_t start,
uint64_t duration,
HighResTimeStamp start,
HighResDuration duration,
const std::optional<DevToolsTrackEntryPayload>& trackMetadata) {
if (!tracing_) {
return;
@@ -197,7 +188,7 @@ void PerformanceTracer::reportProcess(uint64_t id, const std::string& name) {
.name = "process_name",
.cat = "__metadata",
.ph = 'M',
.ts = 0,
.ts = TRACING_TIME_ORIGIN,
.pid = id,
.tid = 0,
.args = folly::dynamic::object("name", name),
@@ -222,7 +213,7 @@ void PerformanceTracer::reportThread(uint64_t id, const std::string& name) {
.name = "thread_name",
.cat = "__metadata",
.ph = 'M',
.ts = 0,
.ts = TRACING_TIME_ORIGIN,
.pid = processId_,
.tid = id,
.args = folly::dynamic::object("name", name),
@@ -237,13 +228,15 @@ void PerformanceTracer::reportThread(uint64_t id, const std::string& name) {
.name = "ReactNative-ThreadRegistered",
.cat = "disabled-by-default-devtools.timeline",
.ph = 'I',
.ts = 0,
.ts = TRACING_TIME_ORIGIN,
.pid = processId_,
.tid = id,
});
}
void PerformanceTracer::reportEventLoopTask(uint64_t start, uint64_t end) {
void PerformanceTracer::reportEventLoopTask(
HighResTimeStamp start,
HighResTimeStamp end) {
if (!tracing_) {
return;
}
@@ -265,8 +258,8 @@ void PerformanceTracer::reportEventLoopTask(uint64_t start, uint64_t end) {
}
void PerformanceTracer::reportEventLoopMicrotasks(
uint64_t start,
uint64_t end) {
HighResTimeStamp start,
HighResTimeStamp end) {
if (!tracing_) {
return;
}
@@ -290,7 +283,7 @@ void PerformanceTracer::reportEventLoopMicrotasks(
folly::dynamic PerformanceTracer::getSerializedRuntimeProfileTraceEvent(
uint64_t threadId,
uint16_t profileId,
uint64_t eventUnixTimestamp) {
HighResTimeStamp profileTimestamp) {
// CDT prioritizes event timestamp over startTime metadata field.
// https://fburl.com/lo764pf4
return serializeTraceEvent(TraceEvent{
@@ -298,25 +291,28 @@ folly::dynamic PerformanceTracer::getSerializedRuntimeProfileTraceEvent(
.name = "Profile",
.cat = "disabled-by-default-v8.cpu_profiler",
.ph = 'P',
.ts = eventUnixTimestamp,
.ts = profileTimestamp,
.pid = processId_,
.tid = threadId,
.args = folly::dynamic::object(
"data", folly ::dynamic::object("startTime", eventUnixTimestamp)),
"data",
folly ::dynamic::object(
"startTime",
highResTimeStampToTracingClockTimeStamp(profileTimestamp))),
});
}
folly::dynamic PerformanceTracer::getSerializedRuntimeProfileChunkTraceEvent(
uint16_t profileId,
uint64_t threadId,
uint64_t eventUnixTimestamp,
HighResTimeStamp chunkTimestamp,
const tracing::TraceEventProfileChunk& traceEventProfileChunk) {
return serializeTraceEvent(TraceEvent{
.id = profileId,
.name = "ProfileChunk",
.cat = "disabled-by-default-v8.cpu_profiler",
.ph = 'P',
.ts = eventUnixTimestamp,
.ts = chunkTimestamp,
.pid = processId_,
.tid = threadId,
.args =
@@ -336,15 +332,15 @@ folly::dynamic PerformanceTracer::serializeTraceEvent(
result["name"] = event.name;
result["cat"] = event.cat;
result["ph"] = std::string(1, event.ph);
result["ts"] = event.ts;
result["ts"] = highResTimeStampToTracingClockTimeStamp(event.ts);
result["pid"] = event.pid;
result["tid"] = event.tid;
result["args"] = event.args;
if (event.dur.has_value()) {
result["dur"] = event.dur.value();
result["dur"] = highResDurationToTracingClockDuration(event.dur.value());
}
return result;
}
} // namespace facebook::react::jsinspector_modern
} // namespace facebook::react::jsinspector_modern::tracing
@@ -11,14 +11,15 @@
#include "TraceEvent.h"
#include "TraceEventProfile.h"
#include <folly/dynamic.h>
#include <react/timing/primitives.h>
#include <folly/dynamic.h>
#include <functional>
#include <mutex>
#include <optional>
#include <vector>
namespace facebook::react::jsinspector_modern {
namespace facebook::react::jsinspector_modern::tracing {
// TODO: Review how this API is integrated into jsinspector_modern (singleton
// design is copied from earlier FuseboxTracer prototype).
@@ -65,7 +66,7 @@ class PerformanceTracer {
*
* See https://w3c.github.io/user-timing/#mark-method.
*/
void reportMark(const std::string_view& name, uint64_t start);
void reportMark(const std::string_view& name, HighResTimeStamp start);
/**
* Record a `Performance.measure()` event - a labelled duration. If not
@@ -75,8 +76,8 @@ class PerformanceTracer {
*/
void reportMeasure(
const std::string_view& name,
uint64_t start,
uint64_t duration,
HighResTimeStamp start,
HighResDuration duration,
const std::optional<DevToolsTrackEntryPayload>& trackMetadata);
/**
@@ -99,13 +100,13 @@ class PerformanceTracer {
* Record an Event Loop tick, which will be represented as an Event Loop task
* on a timeline view and grouped with JavaScript samples.
*/
void reportEventLoopTask(uint64_t start, uint64_t end);
void reportEventLoopTask(HighResTimeStamp start, HighResTimeStamp end);
/**
* Record Microtasks phase of the Event Loop tick. Will be represented as a
* "Run Microtasks" block under a task.
*/
void reportEventLoopMicrotasks(uint64_t start, uint64_t end);
void reportEventLoopMicrotasks(HighResTimeStamp start, HighResTimeStamp end);
/**
* Create and serialize Profile Trace Event.
@@ -114,7 +115,7 @@ class PerformanceTracer {
folly::dynamic getSerializedRuntimeProfileTraceEvent(
uint64_t threadId,
uint16_t profileId,
uint64_t eventUnixTimestamp);
HighResTimeStamp profileTimestamp);
/**
* Create and serialize ProfileChunk Trace Event.
@@ -123,8 +124,8 @@ class PerformanceTracer {
folly::dynamic getSerializedRuntimeProfileChunkTraceEvent(
uint16_t profileId,
uint64_t threadId,
uint64_t eventUnixTimestamp,
const tracing::TraceEventProfileChunk& traceEventProfileChunk);
HighResTimeStamp chunkTimestamp,
const TraceEventProfileChunk& traceEventProfileChunk);
private:
PerformanceTracer();
@@ -135,10 +136,11 @@ class PerformanceTracer {
folly::dynamic serializeTraceEvent(const TraceEvent& event) const;
bool tracing_{false};
uint64_t processId_;
uint32_t performanceMeasureCount_{0};
std::vector<TraceEvent> buffer_;
std::mutex mutex_;
};
} // namespace facebook::react::jsinspector_modern
} // namespace facebook::react::jsinspector_modern::tracing
@@ -47,6 +47,7 @@ Pod::Spec.new do |s|
end
s.dependency "React-oscompat"
s.dependency "React-timing"
add_rn_third_party_dependencies(s)
end
@@ -14,6 +14,17 @@ namespace facebook::react::jsinspector_modern::tracing {
namespace {
// To capture samples timestamps Hermes is using steady_clock and returns
// them in microseconds granularity since epoch. In the future we might want to
// update Hermes to return timestamps in chrono type.
HighResTimeStamp getHighResTimeStampForSample(
const RuntimeSamplingProfile::Sample& sample) {
auto microsecondsSinceSteadyClockEpoch = sample.getTimestamp();
auto chronoTimePoint = std::chrono::steady_clock::time_point(
std::chrono::microseconds(microsecondsSinceSteadyClockEpoch));
return HighResTimeStamp::fromChronoSteadyClockTimePoint(chronoTimePoint);
}
// Right now we only emit single Profile. We might revisit this decision in the
// future, once we support multiple VMs being sampled at the same time.
constexpr uint16_t PROFILE_ID = 1;
@@ -28,13 +39,6 @@ constexpr std::string_view ROOT_FRAME_NAME = "(root)";
constexpr std::string_view IDLE_FRAME_NAME = "(idle)";
constexpr std::string_view PROGRAM_FRAME_NAME = "(program)";
uint64_t formatTimePointToUnixTimestamp(
std::chrono::steady_clock::time_point timestamp) {
return std::chrono::duration_cast<std::chrono::microseconds>(
timestamp.time_since_epoch())
.count();
}
TraceEventProfileChunk::CPUProfile::Node convertToTraceEventProfileNode(
const ProfileTreeNode& node) {
const RuntimeSamplingProfile::SampleCallStackFrame& callFrame =
@@ -91,10 +95,10 @@ class ProfileTreeRootNode : public ProfileTreeNode {
void RuntimeSamplingProfileTraceEventSerializer::sendProfileTraceEvent(
uint64_t threadId,
uint16_t profileId,
uint64_t profileStartUnixTimestamp) const {
HighResTimeStamp profileStartTimestamp) const {
folly::dynamic serializedTraceEvent =
performanceTracer_.getSerializedRuntimeProfileTraceEvent(
threadId, profileId, profileStartUnixTimestamp);
threadId, profileId, profileStartTimestamp);
notificationCallback_(folly::dynamic::array(serializedTraceEvent));
}
@@ -102,7 +106,7 @@ void RuntimeSamplingProfileTraceEventSerializer::sendProfileTraceEvent(
void RuntimeSamplingProfileTraceEventSerializer::chunkEmptySample(
ProfileChunk& chunk,
uint32_t idleNodeId,
long long samplesTimeDelta) {
HighResDuration samplesTimeDelta) {
chunk.samples.push_back(idleNodeId);
chunk.timeDeltas.push_back(samplesTimeDelta);
}
@@ -139,7 +143,7 @@ void RuntimeSamplingProfileTraceEventSerializer::processCallStack(
ProfileChunk& chunk,
ProfileTreeNode& rootNode,
uint32_t idleNodeId,
long long samplesTimeDelta,
HighResDuration samplesTimeDelta,
NodeIdGenerator& nodeIdGenerator) {
if (callStack.empty()) {
chunkEmptySample(chunk, idleNodeId, samplesTimeDelta);
@@ -183,7 +187,7 @@ void RuntimeSamplingProfileTraceEventSerializer::
void RuntimeSamplingProfileTraceEventSerializer::serializeAndNotify(
const RuntimeSamplingProfile& profile,
std::chrono::steady_clock::time_point tracingStartTime) {
HighResTimeStamp tracingStartTime) {
const std::vector<RuntimeSamplingProfile::Sample>& samples =
profile.getSamples();
if (samples.empty()) {
@@ -191,18 +195,15 @@ void RuntimeSamplingProfileTraceEventSerializer::serializeAndNotify(
}
uint64_t firstChunkThreadId = samples.front().getThreadId();
uint64_t tracingStartUnixTimestamp =
formatTimePointToUnixTimestamp(tracingStartTime);
uint64_t previousSampleUnixTimestamp = tracingStartUnixTimestamp;
uint64_t currentChunkUnixTimestamp = tracingStartUnixTimestamp;
HighResTimeStamp previousSampleTimestamp = tracingStartTime;
HighResTimeStamp currentChunkTimestamp = tracingStartTime;
sendProfileTraceEvent(
firstChunkThreadId, PROFILE_ID, tracingStartUnixTimestamp);
sendProfileTraceEvent(firstChunkThreadId, PROFILE_ID, tracingStartTime);
// There could be any number of new nodes in this chunk. Empty if all nodes
// are already emitted in previous chunks.
ProfileChunk chunk{
profileChunkSize_, firstChunkThreadId, currentChunkUnixTimestamp};
profileChunkSize_, firstChunkThreadId, currentChunkTimestamp};
NodeIdGenerator nodeIdGenerator{};
@@ -224,7 +225,7 @@ void RuntimeSamplingProfileTraceEventSerializer::serializeAndNotify(
for (const auto& sample : samples) {
uint64_t currentSampleThreadId = sample.getThreadId();
long long currentSampleUnixTimestamp = sample.getTimestamp();
auto currentSampleTimestamp = getHighResTimeStampForSample(sample);
// We should not attempt to merge samples from different threads.
// From past observations, this only happens for GC nodes.
@@ -233,7 +234,7 @@ void RuntimeSamplingProfileTraceEventSerializer::serializeAndNotify(
if (currentSampleThreadId != chunk.threadId || chunk.isFull()) {
bufferProfileChunkTraceEvent(chunk, PROFILE_ID);
chunk = ProfileChunk{
profileChunkSize_, currentSampleThreadId, currentChunkUnixTimestamp};
profileChunkSize_, currentSampleThreadId, currentChunkTimestamp};
}
if (traceEventBuffer_.size() == traceEventChunkSize_) {
@@ -245,10 +246,10 @@ void RuntimeSamplingProfileTraceEventSerializer::serializeAndNotify(
chunk,
rootNode,
idleNodeId,
currentSampleUnixTimestamp - previousSampleUnixTimestamp,
currentSampleTimestamp - previousSampleTimestamp,
nodeIdGenerator);
previousSampleUnixTimestamp = currentSampleUnixTimestamp;
previousSampleTimestamp = currentSampleTimestamp;
}
if (!chunk.isEmpty()) {
@@ -11,6 +11,8 @@
#include "ProfileTreeNode.h"
#include "RuntimeSamplingProfile.h"
#include <react/timing/primitives.h>
namespace facebook::react::jsinspector_modern::tracing {
namespace {
@@ -36,7 +38,7 @@ class RuntimeSamplingProfileTraceEventSerializer {
ProfileChunk(
uint16_t chunkSize,
uint64_t chunkThreadId,
uint64_t chunkTimestamp)
HighResTimeStamp chunkTimestamp)
: size(chunkSize), threadId(chunkThreadId), timestamp(chunkTimestamp) {
samples.reserve(size);
timeDeltas.reserve(size);
@@ -52,10 +54,10 @@ class RuntimeSamplingProfileTraceEventSerializer {
std::vector<ProfileTreeNode> nodes;
std::vector<uint32_t> samples;
std::vector<long long> timeDeltas;
std::vector<HighResDuration> timeDeltas;
uint16_t size;
uint64_t threadId;
uint64_t timestamp;
HighResTimeStamp timestamp;
};
public:
@@ -89,7 +91,7 @@ class RuntimeSamplingProfileTraceEventSerializer {
*/
void serializeAndNotify(
const RuntimeSamplingProfile& profile,
std::chrono::steady_clock::time_point tracingStartTime);
HighResTimeStamp tracingStartTime);
private:
/**
@@ -102,7 +104,7 @@ class RuntimeSamplingProfileTraceEventSerializer {
void sendProfileTraceEvent(
uint64_t threadId,
uint16_t profileId,
uint64_t profileStartUnixTimestamp) const;
HighResTimeStamp profileStartTimestamp) const;
/**
* Encapsulates logic for processing the empty sample, when the VM was idling.
@@ -114,7 +116,7 @@ class RuntimeSamplingProfileTraceEventSerializer {
void chunkEmptySample(
ProfileChunk& chunk,
uint32_t idleNodeId,
long long samplesTimeDelta);
HighResDuration samplesTimeDelta);
/**
* Records ProfileChunk as a "ProfileChunk" Trace Event in traceEventBuffer_.
@@ -143,7 +145,7 @@ class RuntimeSamplingProfileTraceEventSerializer {
ProfileChunk& chunk,
ProfileTreeNode& rootNode,
uint32_t idleNodeId,
long long samplesTimeDelta,
HighResDuration samplesTimeDelta,
NodeIdGenerator& nodeIdGenerator);
/**
@@ -0,0 +1,42 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <cassert>
#include <react/timing/primitives.h>
namespace facebook::react::jsinspector_modern::tracing {
// The Tracing Clock time origin is the steady_clock epoch. This is mostly done
// to replicate Chromium's behavior, but also saves us from aligning custom
// DOMHighResTimeStamps that can be specified in performance.mark /
// performance.measure calls: these should not extend the timeline window, this
// is the current approach in Chromium.
constexpr HighResTimeStamp TRACING_TIME_ORIGIN =
HighResTimeStamp::fromChronoSteadyClockTimePoint(
std::chrono::steady_clock::time_point());
// Tracing timestamps are represented a time value in microseconds since
// arbitrary time origin (epoch) with no fractional part.
inline uint64_t highResTimeStampToTracingClockTimeStamp(
HighResTimeStamp timestamp) {
assert(
timestamp >= TRACING_TIME_ORIGIN &&
"Provided timestamp is before time origin");
auto duration = timestamp - TRACING_TIME_ORIGIN;
return static_cast<uint64_t>(
static_cast<double>(duration.toNanoseconds()) / 1e3);
}
inline int64_t highResDurationToTracingClockDuration(HighResDuration duration) {
return static_cast<int64_t>(
static_cast<double>(duration.toNanoseconds()) / 1e3);
}
} // namespace facebook::react::jsinspector_modern::tracing
@@ -7,11 +7,11 @@
#pragma once
#include <react/timing/primitives.h>
#include <folly/dynamic.h>
namespace facebook::react::jsinspector_modern {
namespace {
namespace facebook::react::jsinspector_modern::tracing {
/**
* A trace event to send to the debugger frontend, as defined by the Trace Event
@@ -42,7 +42,7 @@ struct TraceEvent {
char ph;
/** The tracing clock timestamp of the event, in microseconds (µs). */
uint64_t ts;
HighResTimeStamp ts;
/** The process ID for the process that output this event. */
uint64_t pid;
@@ -57,9 +57,7 @@ struct TraceEvent {
* The duration of the event, in microseconds (µs). Only applicable to
* complete events ("ph": "X").
*/
std::optional<uint64_t> dur;
std::optional<HighResDuration> dur;
};
} // namespace
} // namespace facebook::react::jsinspector_modern
} // namespace facebook::react::jsinspector_modern::tracing
@@ -7,6 +7,9 @@
#pragma once
#include <jsinspector-modern/tracing/Timing.h>
#include <react/timing/primitives.h>
#include <folly/dynamic.h>
namespace facebook::react::jsinspector_modern::tracing {
@@ -18,10 +21,15 @@ struct TraceEventProfileChunk {
/// Will be sent as part of the "ProfileChunk" trace event.
struct TimeDeltas {
folly::dynamic toDynamic() const {
return folly::dynamic::array(deltas.begin(), deltas.end());
auto value = folly::dynamic::array();
value.reserve(deltas.size());
for (const auto& delta : deltas) {
value.push_back(highResDurationToTracingClockDuration(delta));
}
return value;
}
std::vector<long long> deltas;
std::vector<HighResDuration> deltas;
};
/// Contains Profile information that will be emitted in this chunk: nodes and
@@ -6,10 +6,10 @@
*/
#include <jsinspector-modern/tracing/RuntimeSamplingProfileTraceEventSerializer.h>
#include <jsinspector-modern/tracing/Timing.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <chrono>
#include <utility>
namespace facebook::react::jsinspector_modern::tracing {
@@ -71,7 +71,7 @@ TEST_F(RuntimeSamplingProfileTraceEventSerializerTest, EmptyProfile) {
PerformanceTracer::getInstance(), notificationCallback, 10);
auto profile = createEmptyProfile();
auto tracingStartTime = std::chrono::steady_clock::now();
auto tracingStartTime = HighResTimeStamp::now();
// Execute
serializer.serializeAndNotify(profile, tracingStartTime);
@@ -119,7 +119,7 @@ TEST_F(
samples.emplace_back(createSample(timestamp3, threadId, callStack3));
auto profile = createProfileWithSamples(std::move(samples));
auto tracingStartTime = std::chrono::steady_clock::now();
auto tracingStartTime = HighResTimeStamp::now();
// Execute
serializer.serializeAndNotify(profile, tracingStartTime);
@@ -148,7 +148,7 @@ TEST_F(RuntimeSamplingProfileTraceEventSerializerTest, EmptySample) {
samples.emplace_back(createSample(timestamp, threadId, emptyCallStack));
auto profile = createProfileWithSamples(std::move(samples));
auto tracingStartTime = std::chrono::steady_clock::now();
auto tracingStartTime = HighResTimeStamp::now();
// Mock the performance tracer methods
folly::dynamic profileEvent = folly::dynamic::object;
@@ -189,7 +189,7 @@ TEST_F(
auto profile = createProfileWithSamples(std::move(samples));
auto tracingStartTime = std::chrono::steady_clock::now();
auto tracingStartTime = HighResTimeStamp::now();
// Execute
serializer.serializeAndNotify(profile, tracingStartTime);
@@ -228,7 +228,7 @@ TEST_F(
}
auto profile = createProfileWithSamples(std::move(samples));
auto tracingStartTime = std::chrono::steady_clock::now();
auto tracingStartTime = HighResTimeStamp::now();
// Execute
serializer.serializeAndNotify(profile, tracingStartTime);
@@ -269,7 +269,7 @@ TEST_F(RuntimeSamplingProfileTraceEventSerializerTest, ProfileChunkSizeLimit) {
}
auto profile = createProfileWithSamples(std::move(samples));
auto tracingStartTime = std::chrono::steady_clock::now();
auto tracingStartTime = HighResTimeStamp::now();
// Execute
serializer.serializeAndNotify(profile, tracingStartTime);
@@ -37,10 +37,6 @@ std::vector<PerformanceEntryType> getSupportedEntryTypesInternal() {
return supportedEntryTypes;
}
uint64_t timestampToMicroseconds(DOMHighResTimeStamp timestamp) {
return static_cast<uint64_t>(timestamp * 1000);
}
double performanceNow() {
return chronoToDOMHighResTimeStamp(std::chrono::steady_clock::now());
}
@@ -312,13 +308,14 @@ PerformanceResourceTiming PerformanceEntryReporter::reportResourceTiming(
void PerformanceEntryReporter::traceMark(const PerformanceMark& entry) const {
auto& performanceTracer =
jsinspector_modern::PerformanceTracer::getInstance();
jsinspector_modern::tracing::PerformanceTracer::getInstance();
if (ReactPerfettoLogger::isTracing() || performanceTracer.isTracing()) {
auto [trackName, eventName] = parseTrackName(entry.name);
if (performanceTracer.isTracing()) {
performanceTracer.reportMark(
entry.name, timestampToMicroseconds(entry.startTime));
entry.name,
HighResTimeStamp::fromDOMHighResTimeStamp(entry.startTime));
}
if (ReactPerfettoLogger::isTracing()) {
@@ -330,7 +327,7 @@ void PerformanceEntryReporter::traceMark(const PerformanceMark& entry) const {
void PerformanceEntryReporter::traceMeasure(
const PerformanceMeasure& entry) const {
auto& performanceTracer =
jsinspector_modern::PerformanceTracer::getInstance();
jsinspector_modern::tracing::PerformanceTracer::getInstance();
if (performanceTracer.isTracing() || ReactPerfettoLogger::isTracing()) {
auto [trackName, eventName] = parseTrackName(entry.name);
@@ -343,8 +340,8 @@ void PerformanceEntryReporter::traceMeasure(
}
performanceTracer.reportMeasure(
eventName,
timestampToMicroseconds(entry.startTime),
timestampToMicroseconds(entry.duration),
HighResTimeStamp::fromDOMHighResTimeStamp(entry.startTime),
HighResDuration::fromDOMHighResTimeStamp(entry.duration),
trackMetadata);
}