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 @@ -9,6 +9,7 @@

#include <jsinspector-modern/tracing/PerformanceTracer.h>
#include <jsinspector-modern/tracing/RuntimeSamplingProfileTraceEventSerializer.h>
#include <jsinspector-modern/tracing/TraceEventSerializer.h>

namespace facebook::react::jsinspector_modern {

Expand All @@ -29,6 +30,26 @@ const uint16_t TRACE_EVENT_CHUNK_SIZE = 1000;
*/
const uint16_t PROFILE_TRACE_EVENT_CHUNK_SIZE = 1;

void serializeTraceEventsInChunks(
std::vector<tracing::TraceEvent>&& traceEvents,
uint16_t chunkSize,
const std::function<void(folly::dynamic&& eventsChunk)>& resultCallback) {
auto serializedTraceEvents = folly::dynamic::array();
for (auto&& traceEvent : traceEvents) {
// Emit trace events
serializedTraceEvents.push_back(
tracing::TraceEventSerializer::serialize(std::move(traceEvent)));

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

} // namespace

TracingAgent::TracingAgent(
Expand Down Expand Up @@ -89,8 +110,8 @@ bool TracingAgent::handleRequest(const cdp::PreparsedRequest& req) {

tracing::PerformanceTracer& performanceTracer =
tracing::PerformanceTracer::getInstance();
bool correctlyStopped = performanceTracer.stopTracing();
if (!correctlyStopped) {
auto collectedEvents = performanceTracer.stopTracing();
if (!collectedEvents) {
frontendChannel_(cdp::jsonError(
req.id,
cdp::ErrorCode::InternalError,
Expand All @@ -107,8 +128,11 @@ bool TracingAgent::handleRequest(const cdp::PreparsedRequest& req) {
"Tracing.dataCollected",
folly::dynamic::object("value", std::move(eventsChunk))));
};
performanceTracer.collectEvents(
dataCollectedCallback, TRACE_EVENT_CHUNK_SIZE);

serializeTraceEventsInChunks(
std::move(*collectedEvents),
TRACE_EVENT_CHUNK_SIZE,
dataCollectedCallback);

auto tracingProfile = instanceAgent_->collectTracingProfile();
tracing::IdGenerator profileIdGenerator;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,38 +28,39 @@ PerformanceTracer::PerformanceTracer()

bool PerformanceTracer::startTracing() {
std::lock_guard lock(mutex_);

if (tracingAtomic_) {
return false;
}
tracingAtomic_ = true;

buffer_.emplace_back(TraceEvent{
.name = "TracingStartedInPage",
.cat = "disabled-by-default-devtools.timeline",
.ph = 'I',
.ts = HighResTimeStamp::now(),
.pid = processId_,
.tid = oscompat::getCurrentThreadId(),
.args = folly::dynamic::object("data", folly::dynamic::object()),
});
tracingAtomic_ = true;

return true;
}

bool PerformanceTracer::stopTracing() {
std::lock_guard lock(mutex_);
if (!tracingAtomic_) {
return false;
std::optional<std::vector<TraceEvent>> PerformanceTracer::stopTracing() {
std::vector<TraceEvent> events;

{
std::lock_guard lock(mutex_);

if (!tracingAtomic_) {
return std::nullopt;
}

tracingAtomic_ = false;
performanceMeasureCount_ = 0;

buffer_.swap(events);
}
tracingAtomic_ = 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.
// samples will be displayed as empty. We use these events to avoid that.
// This could happen for non-bridgeless apps, where Performance interface is
// not supported and no spec-compliant Event Loop implementation.
buffer_.emplace_back(TraceEvent{
events.emplace_back(TraceEvent{
.name = "ReactNative-TracingStopped",
.cat = "disabled-by-default-devtools.timeline",
.ph = 'I',
Expand All @@ -68,75 +69,6 @@ bool PerformanceTracer::stopTracing() {
.tid = oscompat::getCurrentThreadId(),
});

performanceMeasureCount_ = 0;
return true;
}

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

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

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

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

folly::dynamic PerformanceTracer::collectEvents(uint16_t chunkSize) {
std::vector<TraceEvent> localBuffer;
{
std::lock_guard lock(mutex_);
buffer_.swap(localBuffer);
}

auto chunks = folly::dynamic::array();
if (localBuffer.empty()) {
return chunks;
}

auto chunk = folly::dynamic::array();
chunk.reserve(chunkSize);
for (auto&& event : localBuffer) {
chunk.push_back(TraceEventSerializer::serialize(std::move(event)));

if (chunk.size() == chunkSize) {
chunks.push_back(std::move(chunk));
chunk = folly::dynamic::array();
chunk.reserve(chunkSize);
}
}

if (!chunk.empty()) {
chunks.push_back(std::move(chunk));
}
return chunks;
}

std::vector<TraceEvent> PerformanceTracer::collectTraceEvents() {
std::vector<TraceEvent> events;
{
std::lock_guard lock(mutex_);
buffer_.swap(events);
}
return events;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

#include <folly/dynamic.h>
#include <atomic>
#include <functional>
#include <mutex>
#include <optional>
#include <vector>
Expand All @@ -34,14 +33,15 @@ class PerformanceTracer {
static PerformanceTracer& getInstance();

/**
* Mark trace session as started. Returns `false` if already tracing.
* Starts a tracing session. Returns `false` if already tracing.
*/
bool startTracing();

/**
* Mark trace session as stopped. Returns `false` if wasn't tracing.
* If there is a current tracing session, it stops tracing and returns all
* collected events. Otherwise, it returns empty.
*/
bool stopTracing();
std::optional<std::vector<TraceEvent>> stopTracing();

/**
* Returns whether the tracer is currently tracing. This can be useful to
Expand All @@ -52,25 +52,6 @@ class PerformanceTracer {
return tracingAtomic_;
}

/**
* Flush out buffered CDP Trace Events using the given callback.
*/
void collectEvents(
const std::function<void(folly::dynamic&& eventsChunk)>& resultCallback,
uint16_t chunkSize);

/**
* Flush out buffered CDP Trace Events into a folly::dynamic collection of
* chunks, which can be sent over CDP later.
*/
folly::dynamic collectEvents(uint16_t chunkSize);

/**
* Transfers an ownership of all buffered TraceEvents, the local buffer state
* is invalidated after this call.
*/
std::vector<TraceEvent> collectTraceEvents();

/**
* Record a `Performance.mark()` event - a labelled timestamp. If not
* currently tracing, this is a no-op.
Expand Down
Loading