mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
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:
committed by
Facebook GitHub Bot
parent
cd01ecbb6a
commit
aa7202861f
@@ -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
|
||||
};
|
||||
|
||||
+26
-30
@@ -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
|
||||
|
||||
+1
@@ -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
|
||||
|
||||
+24
-23
@@ -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()) {
|
||||
|
||||
+9
-7
@@ -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
|
||||
|
||||
+7
-7
@@ -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);
|
||||
|
||||
+6
-9
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user