mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Add FuseboxPerfettoDataSource to emit Fusebox traces using Perfetto (#46376)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/46376 Ideally we would use Fusebox directly but it's not supported in release build. This is a clever work around that: 1) Allows us to use Perfetto to start/stop a trace, otherwise we'd need to build another way to do that which would be either hacky and/or complex. 2) Implements tacking Hermes JS samples and putting them inside of Fusebox traces. 3) Manually taking the trace and opening them in an instance of devtools. Reviewed By: rubennorte Differential Revision: D62263012 fbshipit-source-id: 49468914d2b7e5da4761329b176f140b917a404d
This commit is contained in:
committed by
Facebook GitHub Bot
parent
199d194c46
commit
ef0ea4d834
+143
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef WITH_PERFETTO
|
||||
|
||||
#include <android/log.h>
|
||||
#include <folly/json.h>
|
||||
#include <hermes/hermes.h>
|
||||
#include <perfetto.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <reactperflogger/fusebox/FuseboxTracer.h>
|
||||
#include "FuseboxPerfettoDataSource.h"
|
||||
#include "ReactPerfetto.h"
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
namespace {
|
||||
|
||||
const std::string JS_SAMPLING_TRACK = "JS Sampling";
|
||||
|
||||
const int SAMPLING_HZ = 1000;
|
||||
|
||||
using perfetto::TrackEvent;
|
||||
|
||||
std::string getApplicationId() {
|
||||
pid_t pid = getpid();
|
||||
char path[64] = {0};
|
||||
snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
|
||||
FILE* cmdline = fopen(path, "r");
|
||||
if (cmdline) {
|
||||
char application_id[64] = {0};
|
||||
fread(application_id, sizeof(application_id), 1, cmdline);
|
||||
fclose(cmdline);
|
||||
return application_id;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
uint64_t hermesToPerfettoTime(int64_t hermesTs) {
|
||||
return (hermesTs / 1000);
|
||||
}
|
||||
|
||||
std::vector<folly::dynamic> getStack(
|
||||
const folly::dynamic& trace,
|
||||
const folly::dynamic& sample) {
|
||||
std::vector<folly::dynamic> stack;
|
||||
|
||||
auto stackFrameId = sample["sf"];
|
||||
auto stackFrame = trace["stackFrames"][stackFrameId.asString()];
|
||||
|
||||
while (!stackFrame.isNull()) {
|
||||
stack.push_back(stackFrame);
|
||||
auto parentStackFrameId = stackFrame["parent"];
|
||||
if (parentStackFrameId.isNull()) {
|
||||
break; // No more parents, we're done with this stack frame
|
||||
}
|
||||
stackFrame = trace["stackFrames"][parentStackFrameId.asString()];
|
||||
}
|
||||
std::reverse(stack.begin(), stack.end());
|
||||
return stack;
|
||||
}
|
||||
|
||||
void flushSample(
|
||||
const std::vector<folly::dynamic>& stack,
|
||||
uint64_t start,
|
||||
uint64_t end) {
|
||||
for (size_t i = 0; i < stack.size(); i++) {
|
||||
const auto& frame = stack[i];
|
||||
// Omit elements that are not the first 25 or the last 25
|
||||
if (i > 25 && i < stack.size() - 25) {
|
||||
if (i == 26) {
|
||||
FuseboxTracer::getFuseboxTracer().addEvent(
|
||||
"...", start, end, JS_SAMPLING_TRACK);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
std::string name = frame["name"].asString();
|
||||
FuseboxTracer::getFuseboxTracer().addEvent(
|
||||
name, start, end, JS_SAMPLING_TRACK);
|
||||
}
|
||||
}
|
||||
|
||||
void logHermesProfileToFusebox(const std::string& traceStr) {
|
||||
auto trace = folly::parseJson(traceStr);
|
||||
auto samples = trace["samples"];
|
||||
|
||||
std::vector previousStack = std::vector<folly::dynamic>();
|
||||
uint64_t previousStartTS = 0;
|
||||
uint64_t previousEndTS = 0;
|
||||
for (const auto& sample : samples) {
|
||||
auto perfettoTS = hermesToPerfettoTime(sample["ts"].asInt());
|
||||
|
||||
// Flush previous sample
|
||||
if (previousStack.size() > 0) {
|
||||
flushSample(
|
||||
previousStack,
|
||||
previousStartTS,
|
||||
std::min(previousEndTS, perfettoTS - 1));
|
||||
}
|
||||
|
||||
previousStack = getStack(trace, sample);
|
||||
previousStartTS = perfettoTS;
|
||||
previousEndTS = previousStartTS + 1000000000 / SAMPLING_HZ;
|
||||
}
|
||||
if (previousStack.size() > 0) {
|
||||
flushSample(previousStack, previousStartTS, previousEndTS);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void FuseboxPerfettoDataSource::OnStart(const StartArgs&) {
|
||||
FuseboxTracer::getFuseboxTracer().startTracing();
|
||||
facebook::hermes::HermesRuntime::enableSamplingProfiler(SAMPLING_HZ);
|
||||
}
|
||||
|
||||
void FuseboxPerfettoDataSource::OnFlush(const FlushArgs&) {}
|
||||
|
||||
void FuseboxPerfettoDataSource::OnStop(const StopArgs& a) {
|
||||
std::stringstream stream;
|
||||
facebook::hermes::HermesRuntime::dumpSampledTraceToStream(stream);
|
||||
std::string trace = stream.str();
|
||||
logHermesProfileToFusebox(trace);
|
||||
|
||||
// XXX: Adjust the package ID. Writing to other global tmp directories
|
||||
// seems to fail.
|
||||
std::string appId = getApplicationId();
|
||||
__android_log_print(
|
||||
ANDROID_LOG_INFO, "FuseboxTracer", "Application ID: %s", appId.c_str());
|
||||
FuseboxTracer::getFuseboxTracer().stopTracingAndWriteToFile(
|
||||
"/data/data/" + appId + "/cache/hermes_trace.json");
|
||||
}
|
||||
|
||||
} // namespace facebook::react
|
||||
|
||||
PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(
|
||||
facebook::react::FuseboxPerfettoDataSource);
|
||||
|
||||
#endif // WITH_PERFETTO
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#ifdef WITH_PERFETTO
|
||||
|
||||
#include <perfetto.h>
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
/**
|
||||
* This is a special datasource that will use Perfetto to start/stop
|
||||
* a FuseboxTracer trace (which will be saved to disk, not to the Perfetto
|
||||
* trace) while we wait for Fusebox to be supported in release builds.
|
||||
* Once it's supported we'll probably want to deprecate this data source.
|
||||
*/
|
||||
class FuseboxPerfettoDataSource
|
||||
: public perfetto::DataSource<FuseboxPerfettoDataSource> {
|
||||
public:
|
||||
void OnSetup(const SetupArgs&) override {}
|
||||
|
||||
void OnStart(const StartArgs&) override;
|
||||
|
||||
void OnFlush(const FlushArgs&) override;
|
||||
|
||||
void OnStop(const StopArgs& a) override;
|
||||
|
||||
static void RegisterDataSource() {
|
||||
perfetto::DataSourceDescriptor dsd;
|
||||
dsd.set_name("com.facebook.react.fusebox.profiler");
|
||||
FuseboxPerfettoDataSource::Register(dsd);
|
||||
}
|
||||
|
||||
constexpr static perfetto::BufferExhaustedPolicy kBufferExhaustedPolicy =
|
||||
perfetto::BufferExhaustedPolicy::kStall;
|
||||
};
|
||||
|
||||
} // namespace facebook::react
|
||||
|
||||
PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS(
|
||||
facebook::react::FuseboxPerfettoDataSource);
|
||||
|
||||
#endif // WITH_PERFETTO
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <perfetto.h>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "FuseboxPerfettoDataSource.h"
|
||||
#include "HermesPerfettoDataSource.h"
|
||||
#include "ReactPerfettoCategories.h"
|
||||
|
||||
@@ -32,6 +33,7 @@ void initializePerfetto() {
|
||||
});
|
||||
|
||||
HermesPerfettoDataSource::RegisterDataSource();
|
||||
FuseboxPerfettoDataSource::RegisterDataSource();
|
||||
}
|
||||
|
||||
static perfetto::Track createTrack(const std::string& trackName) {
|
||||
|
||||
Reference in New Issue
Block a user