Skip to content

Commit

Permalink
Expose API for registering profiles and profile chunks (facebook#49084)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: facebook#49084

# Changelog: [Internal]

> NOTE: Some CI jobs are expected to fail, because changes in Hermes D67353585 should be landed first, and then grafted to Static Hermes.

Added public methods to `PerformanceTracer` instance for registering `Profile` and `ProfileChunk` Trace Events.

Also created data structs in `TraceEvent.h` to simplify serialization process for objects like call frames / samples / etc.

Reviewed By: huntie

Differential Revision: D68558805
  • Loading branch information
hoxyq authored and facebook-github-bot committed Feb 27, 2025
1 parent 8f01477 commit becdaf5
Show file tree
Hide file tree
Showing 3 changed files with 225 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ bool PerformanceTracer::stopTracing() {
}

performanceMeasureCount_ = 0;
profileCount_ = 0;
tracing_ = false;
return true;
}
Expand Down Expand Up @@ -213,6 +214,56 @@ void PerformanceTracer::reportThread(uint64_t id, const std::string& name) {
});
}

uint16_t PerformanceTracer::reportRuntimeProfile(
uint64_t threadId,
uint64_t eventUnixTimestamp) {
std::lock_guard lock(mutex_);
if (!tracing_) {
throw std::runtime_error(
"Runtime Profile should only be reported when Tracing is enabled");
}

++profileCount_;
// CDT prioritizes event timestamp over startTime metadata field.
// https://fburl.com/lo764pf4
buffer_.push_back(TraceEvent{
.id = profileCount_,
.name = "Profile",
.cat = "disabled-by-default-v8.cpu_profiler",
.ph = 'P',
.ts = eventUnixTimestamp,
.pid = processId_,
.tid = threadId,
.args = folly::dynamic::object(
"data", folly ::dynamic::object("startTime", eventUnixTimestamp)),
});

return profileCount_;
}

void PerformanceTracer::reportRuntimeProfileChunk(
uint16_t profileId,
uint64_t threadId,
uint64_t eventUnixTimestamp,
const tracing::TraceEventProfileChunk& traceEventProfileChunk) {
std::lock_guard lock(mutex_);
if (!tracing_) {
return;
}

buffer_.push_back(TraceEvent{
.id = profileId,
.name = "ProfileChunk",
.cat = "disabled-by-default-v8.cpu_profiler",
.ph = 'P',
.ts = eventUnixTimestamp,
.pid = processId_,
.tid = threadId,
.args =
folly::dynamic::object("data", traceEventProfileChunk.asDynamic()),
});
}

void PerformanceTracer::reportEventLoopTask(uint64_t start, uint64_t end) {
if (!tracing_) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include "CdpTracing.h"
#include "TraceEvent.h"
#include "TraceEventProfile.h"

#include <folly/dynamic.h>

Expand Down Expand Up @@ -94,6 +95,21 @@ class PerformanceTracer {
*/
void reportJavaScriptThread();

/**
* Record a corresponding Profile Trace Event.
* \return the id of the profile, should be used to linking profile chunks.
*/
uint16_t reportRuntimeProfile(uint64_t threadId, uint64_t eventUnixTimestamp);

/**
* Record a corresponding ProfileChunk Trace Event.
*/
void reportRuntimeProfileChunk(
uint16_t profileId,
uint64_t threadId,
uint64_t eventUnixTimestamp,
const tracing::TraceEventProfileChunk& traceEventProfileChunk);

/**
* Record an Event Loop tick, which will be represented as an Event Loop task
* on a timeline view and grouped with JavaScript samples.
Expand All @@ -111,6 +127,7 @@ class PerformanceTracer {
bool tracing_{false};
uint64_t processId_;
uint32_t performanceMeasureCount_{0};
uint16_t profileCount_{0};
std::vector<TraceEvent> buffer_;
std::mutex mutex_;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/*
* 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 <folly/dynamic.h>

#include <utility>

namespace facebook::react::jsinspector_modern::tracing {

/// Arbitrary data structure, which represents payload of the "ProfileChunk"
/// Trace Event.
struct TraceEventProfileChunk {
/// Deltas between timestamps of chronolocigally sorted samples.
/// Will be sent as part of the "ProfileChunk" trace event.
struct TimeDeltas {
public:
explicit TimeDeltas(std::vector<long long> deltas)
: deltas_(std::move(deltas)) {}

folly::dynamic asDynamic() const {
folly::dynamic value = folly::dynamic::array();
for (auto delta : deltas_) {
value.push_back(delta);
}

return value;
}

private:
std::vector<long long> deltas_;
};

/// Contains Profile information that will be emitted in this chunk: nodes and
/// sample root node ids.
struct CPUProfile {
/// Unique node in the profile tree, has unique id, call frame and
/// optionally
/// id of its parent node. Only root node has no parent.
struct Node {
/// Unique call frame in the call stack.
struct CallFrame {
public:
CallFrame(
std::string codeType,
uint32_t scriptId,
std::string functionName,
std::optional<std::string> url = std::nullopt,
std::optional<uint32_t> lineNumber = std::nullopt,
std::optional<uint32_t> columnNumber = std::nullopt)
: codeType_(std::move(codeType)),
scriptId_(scriptId),
functionName_(std::move(functionName)),
url_(std::move(url)),
lineNumber_(lineNumber),
columnNumber_(columnNumber) {}

folly::dynamic asDynamic() const {
folly::dynamic dynamicCallFrame = folly::dynamic::object();
dynamicCallFrame["codeType"] = codeType_;
dynamicCallFrame["scriptId"] = scriptId_;
dynamicCallFrame["functionName"] = functionName_;
if (url_.has_value()) {
dynamicCallFrame["url"] = url_.value();
}
if (lineNumber_.has_value()) {
dynamicCallFrame["lineNumber"] = lineNumber_.value();
}
if (columnNumber_.has_value()) {
dynamicCallFrame["columnNumber"] = columnNumber_.value();
}

return dynamicCallFrame;
}

private:
std::string codeType_;
uint32_t scriptId_;
std::string functionName_;
std::optional<std::string> url_;
std::optional<uint32_t> lineNumber_;
std::optional<uint32_t> columnNumber_;
};

public:
Node(
uint32_t id,
CallFrame callFrame,
std::optional<uint32_t> parentId = std::nullopt)
: id_(id), callFrame_(std::move(callFrame)), parentId_(parentId) {}

folly::dynamic asDynamic() const {
folly::dynamic dynamicNode = folly::dynamic::object();

dynamicNode["callFrame"] = callFrame_.asDynamic();
dynamicNode["id"] = id_;
if (parentId_.has_value()) {
dynamicNode["parent"] = parentId_.value();
}

return dynamicNode;
}

private:
uint32_t id_;
CallFrame callFrame_;
std::optional<uint32_t> parentId_;
};

public:
CPUProfile(std::vector<Node> nodes, std::vector<uint32_t> samples)
: nodes_(std::move(nodes)), samples_(std::move(samples)) {}

folly::dynamic asDynamic() const {
folly::dynamic dynamicNodes = folly::dynamic::array();
for (const auto& node : nodes_) {
dynamicNodes.push_back(node.asDynamic());
}

folly::dynamic dynamicSamples = folly::dynamic::array();
for (auto sample : samples_) {
dynamicSamples.push_back(sample);
}

return folly::dynamic::object("nodes", dynamicNodes)(
"samples", dynamicSamples);
}

private:
std::vector<Node> nodes_;
std::vector<uint32_t> samples_;
};

public:
TraceEventProfileChunk(CPUProfile cpuProfile, TimeDeltas timeDeltas)
: cpuProfile_(std::move(cpuProfile)),
timeDeltas_(std::move(timeDeltas)) {}

folly::dynamic asDynamic() const {
folly::dynamic value = folly::dynamic::object;
value["cpuProfile"] = cpuProfile_.asDynamic();
value["timeDeltas"] = timeDeltas_.asDynamic();

return value;
}

private:
CPUProfile cpuProfile_;
TimeDeltas timeDeltas_;
};

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

0 comments on commit becdaf5

Please sign in to comment.