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

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ void PerformanceTracer::collectEvents(

void PerformanceTracer::reportMark(
const std::string_view& name,
HighResTimeStamp start) {
HighResTimeStamp start,
folly::dynamic&& detail) {
if (!tracingAtomic_) {
return;
}
Expand All @@ -123,32 +124,37 @@ void PerformanceTracer::reportMark(
return;
}

folly::dynamic eventArgs = folly::dynamic::object();
if (detail != nullptr) {
eventArgs = folly::dynamic::object(
"data",
folly::dynamic::object("detail", folly::toJson(std::move(detail))));
}

buffer_.emplace_back(TraceEvent{
.name = std::string(name),
.cat = "blink.user_timing",
.ph = 'I',
.ts = start,
.pid = processId_,
.tid = oscompat::getCurrentThreadId(),
.args = eventArgs,
});
}

void PerformanceTracer::reportMeasure(
const std::string_view& name,
HighResTimeStamp start,
HighResDuration duration,
const std::optional<DevToolsTrackEntryPayload>& trackMetadata) {
folly::dynamic&& detail) {
if (!tracingAtomic_) {
return;
}

folly::dynamic beginEventArgs = folly::dynamic::object();
if (trackMetadata.has_value()) {
folly::dynamic devtoolsObject = folly::dynamic::object(
"devtools",
folly::dynamic::object("track", trackMetadata.value().track));
if (detail != nullptr) {
beginEventArgs =
folly::dynamic::object("detail", folly::toJson(devtoolsObject));
folly::dynamic::object("detail", folly::toJson(std::move(detail)));
}

auto currentThreadId = oscompat::getCurrentThreadId();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

#pragma once

#include "CdpTracing.h"
#include "ConsoleTimeStamp.h"
#include "TraceEvent.h"
#include "TraceEventProfile.h"
Expand Down Expand Up @@ -60,13 +59,17 @@ class PerformanceTracer {
const std::function<void(const folly::dynamic& eventsChunk)>&
resultCallback,
uint16_t chunkSize);

/**
* Record a `Performance.mark()` event - a labelled timestamp. If not
* currently tracing, this is a no-op.
*
* See https://w3c.github.io/user-timing/#mark-method.
*/
void reportMark(const std::string_view& name, HighResTimeStamp start);
void reportMark(
const std::string_view& name,
HighResTimeStamp start,
folly::dynamic&& detail = nullptr);

/**
* Record a `Performance.measure()` event - a labelled duration. If not
Expand All @@ -78,7 +81,7 @@ class PerformanceTracer {
const std::string_view& name,
HighResTimeStamp start,
HighResDuration duration,
const std::optional<DevToolsTrackEntryPayload>& trackMetadata);
folly::dynamic&& detail = nullptr);

/**
* Record a "TimeStamp" Trace Event - a labelled entry on Performance
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#include <cxxreact/JSExecutor.h>
#include <cxxreact/ReactMarker.h>
#include <jsi/JSIDynamic.h>
#include <jsi/instrumentation.h>
#include <react/performance/timeline/PerformanceEntryReporter.h>
#include <react/performance/timeline/PerformanceObserver.h>
Expand Down Expand Up @@ -111,6 +112,19 @@ std::shared_ptr<PerformanceObserver> tryGetObserver(
return observerWrapper ? observerWrapper->observer : nullptr;
}

PerformanceEntryReporter::UserTimingDetailProvider getDetailProviderFromEntry(
jsi::Runtime& rt,
jsi::Value& entry) {
return [&rt, &entry]() -> folly::dynamic {
try {
auto detail = entry.asObject(rt).getProperty(rt, "detail");
return jsi::dynamicFromValue(rt, detail);
} catch (jsi::JSIException& ex) {
return nullptr;
}
};
}

} // namespace

NativePerformance::NativePerformance(std::shared_ptr<CallInvoker> jsInvoker)
Expand All @@ -124,18 +138,19 @@ void NativePerformance::reportMark(
jsi::Runtime& rt,
std::string name,
HighResTimeStamp startTime,
jsi::Value /*entry*/) {
PerformanceEntryReporter::getInstance()->reportMark(name, startTime);
jsi::Value entry) {
PerformanceEntryReporter::getInstance()->reportMark(
name, startTime, getDetailProviderFromEntry(rt, entry));
}

void NativePerformance::reportMeasure(
jsi::Runtime& rt,
std::string name,
HighResTimeStamp startTime,
HighResDuration duration,
jsi::Value /*entry*/) {
jsi::Value entry) {
PerformanceEntryReporter::getInstance()->reportMeasure(
name, startTime, duration);
name, startTime, duration, getDetailProviderFromEntry(rt, entry));
}

std::optional<double> NativePerformance::getMarkTime(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ target_link_libraries(react_performance_timeline
jsinspector_tracing
reactperflogger
react_featureflags
react_timing)
react_timing
folly_runtime)
Original file line number Diff line number Diff line change
Expand Up @@ -37,31 +37,40 @@ std::vector<PerformanceEntryType> getSupportedEntryTypesInternal() {
return supportedEntryTypes;
}

#if defined(__clang__)
#define NO_DESTROY [[clang::no_destroy]]
#else
#define NO_DESTROY
#endif
std::optional<std::string> getTrackFromDetail(folly::dynamic& detail) {
if (!detail.isObject()) {
return std::nullopt;
}

NO_DESTROY const std::string TRACK_PREFIX = "Track:";

std::tuple<std::optional<std::string>, std::string_view> parseTrackName(
const std::string& name) {
// Until there's a standard way to pass through track information, parse it
// manually, e.g., "Track:Foo:Event name"
// https://github.com/w3c/user-timing/issues/109
std::optional<std::string> trackName;
std::string_view eventName(name);
if (name.starts_with(TRACK_PREFIX)) {
const auto trackNameDelimiter = name.find(':', TRACK_PREFIX.length());
if (trackNameDelimiter != std::string::npos) {
trackName = name.substr(
TRACK_PREFIX.length(), trackNameDelimiter - TRACK_PREFIX.length());
eventName = std::string_view(name).substr(trackNameDelimiter + 1);
}
auto maybeDevtools = detail["devtools"];
if (!maybeDevtools.isObject()) {
return std::nullopt;
}

auto maybeTrack = maybeDevtools["track"];
if (!maybeTrack.isString()) {
return std::nullopt;
}

return maybeTrack.asString();
}

std::optional<std::string> getTrackGroupFromDetail(folly::dynamic& detail) {
if (!detail.isObject()) {
return std::nullopt;
}

auto maybeDevtools = detail["devtools"];
if (!maybeDevtools.isObject()) {
return std::nullopt;
}

return std::make_tuple(trackName, eventName);
auto maybeTrackGroup = maybeDevtools["trackGroup"];
if (!maybeTrackGroup.isString()) {
return std::nullopt;
}

return maybeTrackGroup.asString();
}

} // namespace
Expand Down Expand Up @@ -169,10 +178,11 @@ void PerformanceEntryReporter::clearEntries(

void PerformanceEntryReporter::reportMark(
const std::string& name,
const HighResTimeStamp startTime) {
const HighResTimeStamp startTime,
UserTimingDetailProvider&& detailProvider) {
const auto entry = PerformanceMark{{.name = name, .startTime = startTime}};

traceMark(entry);
traceMark(entry, std::move(detailProvider));

// Add to buffers & notify observers
{
Expand All @@ -187,14 +197,13 @@ void PerformanceEntryReporter::reportMeasure(
const std::string& name,
HighResTimeStamp startTime,
HighResDuration duration,
const std::optional<jsinspector_modern::DevToolsTrackEntryPayload>&
trackMetadata) {
UserTimingDetailProvider&& detailProvider) {
const auto entry = PerformanceMeasure{
{.name = std::string(name),
.startTime = startTime,
.duration = duration}};

traceMeasure(entry);
traceMeasure(entry, std::move(detailProvider));

// Add to buffers & notify observers
{
Expand Down Expand Up @@ -296,46 +305,45 @@ void PerformanceEntryReporter::reportResourceTiming(
observerRegistry_->queuePerformanceEntry(entry);
}

void PerformanceEntryReporter::traceMark(const PerformanceMark& entry) const {
void PerformanceEntryReporter::traceMark(
const PerformanceMark& entry,
UserTimingDetailProvider&& detailProvider) const {
auto& performanceTracer =
jsinspector_modern::tracing::PerformanceTracer::getInstance();
if (ReactPerfettoLogger::isTracing() || performanceTracer.isTracing()) {
auto [trackName, eventName] = parseTrackName(entry.name);

if (performanceTracer.isTracing()) {
performanceTracer.reportMark(entry.name, entry.startTime);
if (ReactPerfettoLogger::isTracing()) {
ReactPerfettoLogger::mark(entry.name, entry.startTime);
}

if (ReactPerfettoLogger::isTracing()) {
ReactPerfettoLogger::mark(eventName, entry.startTime, trackName);
if (performanceTracer.isTracing()) {
performanceTracer.reportMark(
entry.name,
entry.startTime,
detailProvider != nullptr ? detailProvider() : nullptr);
}
}
}

void PerformanceEntryReporter::traceMeasure(
const PerformanceMeasure& entry) const {
const PerformanceMeasure& entry,
UserTimingDetailProvider&& detailProvider) const {
auto& performanceTracer =
jsinspector_modern::tracing::PerformanceTracer::getInstance();
if (performanceTracer.isTracing() || ReactPerfettoLogger::isTracing()) {
auto [trackName, eventName] = parseTrackName(entry.name);

if (performanceTracer.isTracing()) {
std::optional<jsinspector_modern::DevToolsTrackEntryPayload>
trackMetadata;

if (trackName.has_value()) {
trackMetadata = {.track = trackName.value()};
}
performanceTracer.reportMeasure(
eventName, entry.startTime, entry.duration, trackMetadata);
}
auto detail = detailProvider != nullptr ? detailProvider() : nullptr;

if (ReactPerfettoLogger::isTracing()) {
ReactPerfettoLogger::measure(
eventName,
entry.name,
entry.startTime,
entry.startTime + entry.duration,
trackName);
detail != nullptr ? getTrackFromDetail(detail) : std::nullopt,
detail != nullptr ? getTrackGroupFromDetail(detail) : std::nullopt);
}

if (performanceTracer.isTracing()) {
performanceTracer.reportMeasure(
entry.name, entry.startTime, entry.duration, std::move(detail));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#include "PerformanceEntryKeyedBuffer.h"
#include "PerformanceObserverRegistry.h"

#include <jsinspector-modern/tracing/CdpTracing.h>
#include <folly/dynamic.h>
#include <react/timing/primitives.h>

#include <memory>
Expand Down Expand Up @@ -86,14 +86,18 @@ class PerformanceEntryReporter {
std::optional<HighResTimeStamp> getMarkTime(
const std::string& markName) const;

void reportMark(const std::string& name, HighResTimeStamp startTime);
using UserTimingDetailProvider = std::function<folly::dynamic()>;

void reportMark(
const std::string& name,
HighResTimeStamp startTime,
UserTimingDetailProvider&& detailProvider = nullptr);

void reportMeasure(
const std::string& name,
HighResTimeStamp startTime,
HighResDuration duration,
const std::optional<jsinspector_modern::DevToolsTrackEntryPayload>&
trackMetadata = std::nullopt);
UserTimingDetailProvider&& detailProvider = nullptr);

void reportEvent(
std::string name,
Expand Down Expand Up @@ -167,8 +171,12 @@ class PerformanceEntryReporter {
throw std::logic_error("Unhandled PerformanceEntryType");
}

void traceMark(const PerformanceMark& entry) const;
void traceMeasure(const PerformanceMeasure& entry) const;
void traceMark(
const PerformanceMark& entry,
UserTimingDetailProvider&& detailProvider) const;
void traceMeasure(
const PerformanceMeasure& entry,
UserTimingDetailProvider&& detailProvider) const;
};

} // namespace facebook::react
Loading