PHP WebShell
Текущая директория: /usr/lib/node_modules/bitgo/node_modules/react-native/ReactCommon/jsinspector-modern/tracing
Просмотр файла: RuntimeSamplingProfileTraceEventSerializer.cpp
/*
* 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.
*/
#include <string_view>
#include "ProfileTreeNode.h"
#include "RuntimeSamplingProfileTraceEventSerializer.h"
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;
/// Fallback script ID for artificial call frames, such as (root), (idle) or
/// (program). Required for emulating the payload in a format that is expected
/// by Chrome DevTools.
constexpr uint32_t FALLBACK_SCRIPT_ID = 0;
constexpr std::string_view GARBAGE_COLLECTOR_FRAME_NAME = "(garbage collector)";
constexpr std::string_view ROOT_FRAME_NAME = "(root)";
constexpr std::string_view IDLE_FRAME_NAME = "(idle)";
constexpr std::string_view PROGRAM_FRAME_NAME = "(program)";
TraceEventProfileChunk::CPUProfile::Node convertToTraceEventProfileNode(
const ProfileTreeNode& node) {
const RuntimeSamplingProfile::SampleCallStackFrame& callFrame =
node.getCallFrame();
auto traceEventCallFrame =
TraceEventProfileChunk::CPUProfile::Node::CallFrame{
node.getCodeType() == ProfileTreeNode::CodeType::JavaScript ? "JS"
: "other",
callFrame.getScriptId(),
std::string(callFrame.getFunctionName()),
callFrame.hasUrl()
? std::optional<std::string>(std::string(callFrame.getUrl()))
: std::nullopt,
callFrame.hasLineNumber()
? std::optional<uint32_t>(callFrame.getLineNumber())
: std::nullopt,
callFrame.hasColumnNumber()
? std::optional<uint32_t>(callFrame.getColumnNumber())
: std::nullopt};
return TraceEventProfileChunk::CPUProfile::Node{
node.getId(),
traceEventCallFrame,
node.hasParent() ? std::optional<uint32_t>(node.getParentId())
: std::nullopt};
}
RuntimeSamplingProfile::SampleCallStackFrame createArtificialCallFrame(
std::string_view callFrameName) {
return RuntimeSamplingProfile::SampleCallStackFrame{
RuntimeSamplingProfile::SampleCallStackFrame::Kind::JSFunction,
FALLBACK_SCRIPT_ID,
callFrameName};
};
RuntimeSamplingProfile::SampleCallStackFrame createGarbageCollectorCallFrame() {
return RuntimeSamplingProfile::SampleCallStackFrame{
RuntimeSamplingProfile::SampleCallStackFrame::Kind::GarbageCollector,
FALLBACK_SCRIPT_ID,
GARBAGE_COLLECTOR_FRAME_NAME};
};
class ProfileTreeRootNode : public ProfileTreeNode {
public:
explicit ProfileTreeRootNode(uint32_t id)
: ProfileTreeNode(
id,
CodeType::Other,
createArtificialCallFrame(ROOT_FRAME_NAME)) {}
};
} // namespace
void RuntimeSamplingProfileTraceEventSerializer::sendProfileTraceEvent(
uint64_t threadId,
uint16_t profileId,
HighResTimeStamp profileStartTimestamp) const {
folly::dynamic serializedTraceEvent =
performanceTracer_.getSerializedRuntimeProfileTraceEvent(
threadId, profileId, profileStartTimestamp);
notificationCallback_(folly::dynamic::array(serializedTraceEvent));
}
void RuntimeSamplingProfileTraceEventSerializer::chunkEmptySample(
ProfileChunk& chunk,
uint32_t idleNodeId,
HighResDuration samplesTimeDelta) {
chunk.samples.push_back(idleNodeId);
chunk.timeDeltas.push_back(samplesTimeDelta);
}
void RuntimeSamplingProfileTraceEventSerializer::bufferProfileChunkTraceEvent(
ProfileChunk& chunk,
uint16_t profileId) {
if (chunk.isEmpty()) {
return;
}
std::vector<TraceEventProfileChunk::CPUProfile::Node> traceEventNodes;
traceEventNodes.reserve(chunk.nodes.size());
for (const auto& node : chunk.nodes) {
traceEventNodes.push_back(convertToTraceEventProfileNode(node));
}
traceEventBuffer_.push_back(
performanceTracer_.getSerializedRuntimeProfileChunkTraceEvent(
profileId,
chunk.threadId,
chunk.timestamp,
TraceEventProfileChunk{
.cpuProfile =
TraceEventProfileChunk::CPUProfile{
traceEventNodes, chunk.samples},
.timeDeltas =
TraceEventProfileChunk::TimeDeltas{chunk.timeDeltas},
}));
}
void RuntimeSamplingProfileTraceEventSerializer::processCallStack(
const std::vector<RuntimeSamplingProfile::SampleCallStackFrame>& callStack,
ProfileChunk& chunk,
ProfileTreeNode& rootNode,
uint32_t idleNodeId,
HighResDuration samplesTimeDelta,
NodeIdGenerator& nodeIdGenerator) {
if (callStack.empty()) {
chunkEmptySample(chunk, idleNodeId, samplesTimeDelta);
return;
}
ProfileTreeNode* previousNode = &rootNode;
for (auto it = callStack.rbegin(); it != callStack.rend(); ++it) {
const RuntimeSamplingProfile::SampleCallStackFrame& callFrame = *it;
bool isGarbageCollectorFrame = callFrame.getKind() ==
RuntimeSamplingProfile::SampleCallStackFrame::Kind::GarbageCollector;
ProfileTreeNode::CodeType childCodeType = isGarbageCollectorFrame
? ProfileTreeNode::CodeType::Other
: ProfileTreeNode::CodeType::JavaScript;
// We don't need real garbage collector call frame, we change it to
// what Chrome DevTools expects.
RuntimeSamplingProfile::SampleCallStackFrame childCallFrame =
isGarbageCollectorFrame ? createGarbageCollectorCallFrame() : callFrame;
ProfileTreeNode* maybeExistingChild =
previousNode->getIfAlreadyExists(childCodeType, childCallFrame);
if (maybeExistingChild != nullptr) {
previousNode = maybeExistingChild;
} else {
previousNode = previousNode->addChild(
nodeIdGenerator.getNext(), childCodeType, childCallFrame);
chunk.nodes.push_back(*previousNode);
}
}
chunk.samples.push_back(previousNode->getId());
chunk.timeDeltas.push_back(samplesTimeDelta);
}
void RuntimeSamplingProfileTraceEventSerializer::
sendBufferedTraceEventsAndClear() {
notificationCallback_(traceEventBuffer_);
traceEventBuffer_ = folly::dynamic::array();
}
void RuntimeSamplingProfileTraceEventSerializer::serializeAndNotify(
const RuntimeSamplingProfile& profile,
HighResTimeStamp tracingStartTime) {
const std::vector<RuntimeSamplingProfile::Sample>& samples =
profile.getSamples();
if (samples.empty()) {
return;
}
uint64_t firstChunkThreadId = samples.front().getThreadId();
HighResTimeStamp previousSampleTimestamp = tracingStartTime;
HighResTimeStamp currentChunkTimestamp = tracingStartTime;
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, currentChunkTimestamp};
NodeIdGenerator nodeIdGenerator{};
ProfileTreeRootNode rootNode(nodeIdGenerator.getNext());
chunk.nodes.push_back(rootNode);
ProfileTreeNode* programNode = rootNode.addChild(
nodeIdGenerator.getNext(),
ProfileTreeNode::CodeType::Other,
createArtificialCallFrame(PROGRAM_FRAME_NAME));
chunk.nodes.push_back(*programNode);
ProfileTreeNode* idleNode = rootNode.addChild(
nodeIdGenerator.getNext(),
ProfileTreeNode::CodeType::Other,
createArtificialCallFrame(IDLE_FRAME_NAME));
chunk.nodes.push_back(*idleNode);
uint32_t idleNodeId = idleNode->getId();
for (const auto& sample : samples) {
uint64_t currentSampleThreadId = sample.getThreadId();
auto currentSampleTimestamp = getHighResTimeStampForSample(sample);
// We should not attempt to merge samples from different threads.
// From past observations, this only happens for GC nodes.
// We should group samples by thread id once we support executing JavaScript
// on different threads.
if (currentSampleThreadId != chunk.threadId || chunk.isFull()) {
bufferProfileChunkTraceEvent(chunk, PROFILE_ID);
chunk = ProfileChunk{
profileChunkSize_, currentSampleThreadId, currentChunkTimestamp};
}
if (traceEventBuffer_.size() == traceEventChunkSize_) {
sendBufferedTraceEventsAndClear();
}
processCallStack(
sample.getCallStack(),
chunk,
rootNode,
idleNodeId,
currentSampleTimestamp - previousSampleTimestamp,
nodeIdGenerator);
previousSampleTimestamp = currentSampleTimestamp;
}
if (!chunk.isEmpty()) {
bufferProfileChunkTraceEvent(chunk, PROFILE_ID);
}
if (!traceEventBuffer_.empty()) {
sendBufferedTraceEventsAndClear();
}
}
} // namespace facebook::react::jsinspector_modern::tracing
Выполнить команду
Для локальной разработки. Не используйте в интернете!