Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,21 @@ PerformanceTracer::PerformanceTracer()

bool PerformanceTracer::startTracing() {
{
std::lock_guard lock(mutex_);
if (tracing_) {
std::lock_guard lock(tracingMutex_);
if (isTracing()) {
return false;
}

tracing_ = true;
}

reportProcess(processId_, "React Native");

{
std::lock_guard lock(mutex_);
buffer_.push_back(TraceEvent{
std::lock_guard lock(tracingMutex_);
if (!isTracing()) {
return false;
}
buffer_.emplace_back(TraceEvent{
.name = "TracingStartedInPage",
.cat = "disabled-by-default-devtools.timeline",
.ph = 'I',
Expand All @@ -49,24 +51,25 @@ bool PerformanceTracer::startTracing() {
.tid = oscompat::getCurrentThreadId(),
.args = folly::dynamic::object("data", folly::dynamic::object()),
});

return true;
}

return true;
}

bool PerformanceTracer::stopTracing() {
std::lock_guard lock(mutex_);
if (!tracing_) {
std::lock_guard lock(tracingMutex_);
if (!isTracing()) {
return false;
}
tracing_ = false;

// This is synthetic Trace Event, which should not be represented on a
// timeline. CDT is not using Profile or ProfileChunk events for determining
// trace timeline window, this is why trace that only contains JavaScript
// samples will be displayed as empty. We use this event to avoid that.
// This could happen for non-bridgeless apps, where Performance interface is
// not supported and no spec-compliant Event Loop implementation.
buffer_.push_back(TraceEvent{
buffer_.emplace_back(TraceEvent{
.name = "ReactNative-TracingStopped",
.cat = "disabled-by-default-devtools.timeline",
.ph = 'I',
Expand All @@ -75,51 +78,53 @@ bool PerformanceTracer::stopTracing() {
.tid = oscompat::getCurrentThreadId(),
});

// Potential increments of this counter are covered by tracing_ atomic flag.
performanceMeasureCount_ = 0;
tracing_ = false;
return true;
}

void PerformanceTracer::collectEvents(
const std::function<void(const folly::dynamic& eventsChunk)>&
resultCallback,
uint16_t chunkSize) {
std::lock_guard lock(mutex_);
std::vector<TraceEvent> localBuffer;
{
std::lock_guard lock(tracingMutex_);
buffer_.swap(localBuffer);
}

if (buffer_.empty()) {
if (localBuffer.empty()) {
return;
}

auto traceEvents = folly::dynamic::array();
for (auto event : buffer_) {
auto serializedTraceEvents = folly::dynamic::array();
for (auto&& event : localBuffer) {
// Emit trace events
traceEvents.push_back(serializeTraceEvent(event));
serializedTraceEvents.push_back(serializeTraceEvent(std::move(event)));

if (traceEvents.size() == chunkSize) {
resultCallback(traceEvents);
traceEvents = folly::dynamic::array();
if (serializedTraceEvents.size() == chunkSize) {
resultCallback(serializedTraceEvents);
serializedTraceEvents = folly::dynamic::array();
}
}
if (!traceEvents.empty()) {
resultCallback(traceEvents);
if (!serializedTraceEvents.empty()) {
resultCallback(serializedTraceEvents);
}

buffer_.clear();
}

void PerformanceTracer::reportMark(
const std::string_view& name,
HighResTimeStamp start) {
if (!tracing_) {
if (!isTracing()) {
return;
}

std::lock_guard<std::mutex> lock(mutex_);
if (!tracing_) {
std::lock_guard<std::mutex> lock(tracingMutex_);
if (!isTracing()) {
return;
}

buffer_.push_back(TraceEvent{
buffer_.emplace_back(TraceEvent{
.name = std::string(name),
.cat = "blink.user_timing",
.ph = 'I',
Expand All @@ -134,12 +139,7 @@ void PerformanceTracer::reportMeasure(
HighResTimeStamp start,
HighResDuration duration,
const std::optional<DevToolsTrackEntryPayload>& trackMetadata) {
if (!tracing_) {
return;
}

std::lock_guard<std::mutex> lock(mutex_);
if (!tracing_) {
if (!isTracing()) {
return;
}

Expand All @@ -152,10 +152,16 @@ void PerformanceTracer::reportMeasure(
folly::dynamic::object("detail", folly::toJson(devtoolsObject));
}

++performanceMeasureCount_;
auto currentThreadId = oscompat::getCurrentThreadId();
buffer_.push_back(TraceEvent{
.id = performanceMeasureCount_,

std::lock_guard<std::mutex> lock(tracingMutex_);
if (!isTracing()) {
return;
}
auto eventId = ++performanceMeasureCount_;

buffer_.emplace_back(TraceEvent{
.id = eventId,
.name = std::string(name),
.cat = "blink.user_timing",
.ph = 'b',
Expand All @@ -164,8 +170,8 @@ void PerformanceTracer::reportMeasure(
.tid = currentThreadId,
.args = beginEventArgs,
});
buffer_.push_back(TraceEvent{
.id = performanceMeasureCount_,
buffer_.emplace_back(TraceEvent{
.id = eventId,
.name = std::string(name),
.cat = "blink.user_timing",
.ph = 'e',
Expand All @@ -176,16 +182,16 @@ void PerformanceTracer::reportMeasure(
}

void PerformanceTracer::reportProcess(uint64_t id, const std::string& name) {
if (!tracing_) {
if (!isTracing()) {
return;
}

std::lock_guard<std::mutex> lock(mutex_);
if (!tracing_) {
std::lock_guard<std::mutex> lock(tracingMutex_);
if (!isTracing()) {
return;
}

buffer_.push_back(TraceEvent{
buffer_.emplace_back(TraceEvent{
.name = "process_name",
.cat = "__metadata",
.ph = 'M',
Expand All @@ -201,16 +207,16 @@ void PerformanceTracer::reportJavaScriptThread() {
}

void PerformanceTracer::reportThread(uint64_t id, const std::string& name) {
if (!tracing_) {
if (!isTracing()) {
return;
}

std::lock_guard<std::mutex> lock(mutex_);
if (!tracing_) {
std::lock_guard<std::mutex> lock(tracingMutex_);
if (!isTracing()) {
return;
}

buffer_.push_back(TraceEvent{
buffer_.emplace_back(TraceEvent{
.name = "thread_name",
.cat = "__metadata",
.ph = 'M',
Expand All @@ -225,7 +231,7 @@ void PerformanceTracer::reportThread(uint64_t id, const std::string& name) {
// no timeline events or user timings. We use this event to avoid that.
// This could happen for non-bridgeless apps, where Performance interface is
// not supported and no spec-compliant Event Loop implementation.
buffer_.push_back(TraceEvent{
buffer_.emplace_back(TraceEvent{
.name = "ReactNative-ThreadRegistered",
.cat = "disabled-by-default-devtools.timeline",
.ph = 'I',
Expand All @@ -238,16 +244,16 @@ void PerformanceTracer::reportThread(uint64_t id, const std::string& name) {
void PerformanceTracer::reportEventLoopTask(
HighResTimeStamp start,
HighResTimeStamp end) {
if (!tracing_) {
if (!isTracing()) {
return;
}

std::lock_guard lock(mutex_);
if (!tracing_) {
std::lock_guard<std::mutex> lock(tracingMutex_);
if (!isTracing()) {
return;
}

buffer_.push_back(TraceEvent{
buffer_.emplace_back(TraceEvent{
.name = "RunTask",
.cat = "disabled-by-default-devtools.timeline",
.ph = 'X',
Expand All @@ -261,16 +267,16 @@ void PerformanceTracer::reportEventLoopTask(
void PerformanceTracer::reportEventLoopMicrotasks(
HighResTimeStamp start,
HighResTimeStamp end) {
if (!tracing_) {
if (!isTracing()) {
return;
}

std::lock_guard lock(mutex_);
if (!tracing_) {
std::lock_guard<std::mutex> lock(tracingMutex_);
if (!isTracing()) {
return;
}

buffer_.push_back(TraceEvent{
buffer_.emplace_back(TraceEvent{
.name = "RunMicrotasks",
.cat = "v8.execute",
.ph = 'X',
Expand Down Expand Up @@ -322,21 +328,21 @@ folly::dynamic PerformanceTracer::getSerializedRuntimeProfileChunkTraceEvent(
}

folly::dynamic PerformanceTracer::serializeTraceEvent(
const TraceEvent& event) const {
TraceEvent&& event) const {
folly::dynamic result = folly::dynamic::object;

if (event.id.has_value()) {
std::array<char, 16> buffer{};
snprintf(buffer.data(), buffer.size(), "0x%x", event.id.value());
result["id"] = buffer.data();
}
result["name"] = event.name;
result["cat"] = event.cat;
result["name"] = std::move(event.name);
result["cat"] = std::move(event.cat);
result["ph"] = std::string(1, event.ph);
result["ts"] = highResTimeStampToTracingClockTimeStamp(event.ts);
result["pid"] = event.pid;
result["tid"] = event.tid;
result["args"] = event.args;
result["args"] = std::move(event.args);
if (event.dur.has_value()) {
result["dur"] = highResDurationToTracingClockDuration(event.dur.value());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <react/timing/primitives.h>

#include <folly/dynamic.h>
#include <atomic>
#include <functional>
#include <mutex>
#include <optional>
Expand Down Expand Up @@ -47,9 +48,7 @@ class PerformanceTracer {
* avoid doing expensive work (like formatting strings) if tracing is not
* enabled.
*/
bool isTracing() const {
// This is not thread safe but it's only a performance optimization. The
// call to report marks and measures is already thread safe.
inline bool isTracing() const {
return tracing_;
}

Expand Down Expand Up @@ -133,14 +132,22 @@ class PerformanceTracer {
PerformanceTracer& operator=(const PerformanceTracer&) = delete;
~PerformanceTracer() = default;

folly::dynamic serializeTraceEvent(const TraceEvent& event) const;

bool tracing_{false};
/**
* Serialize a TraceEvent into a folly::dynamic object.
* \param event rvalue reference to the TraceEvent object.
* \return folly::dynamic object that represents a serialized into JSON Trace
* Event for CDP.
*/
folly::dynamic serializeTraceEvent(TraceEvent&& event) const;

uint64_t processId_;
uint32_t performanceMeasureCount_{0};

std::atomic<bool> tracing_{false};
std::atomic<uint32_t> performanceMeasureCount_{0};

std::vector<TraceEvent> buffer_;
std::mutex mutex_;
// Protects buffer_ operations and tracing_ modifications.
std::mutex tracingMutex_;
};

} // namespace facebook::react::jsinspector_modern::tracing
Loading