first commit

This commit is contained in:
2026-03-10 16:18:05 +00:00
commit 11f9c069b5
31635 changed files with 3187747 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
# 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.
cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on)
include(${REACT_COMMON_DIR}/cmake-utils/react-native-flags.cmake)
file(GLOB reactperflogger_SRC CONFIGURE_DEPENDS
reactperflogger/*.cpp
fusebox/*.cpp)
add_library(reactperflogger OBJECT ${reactperflogger_SRC})
target_include_directories(reactperflogger PUBLIC .)
target_link_libraries(reactperflogger
react_timing
folly_runtime
)
target_compile_reactnative_options(reactperflogger PRIVATE)
target_compile_options(reactperflogger PRIVATE -Wpedantic)

View File

@@ -0,0 +1,42 @@
# 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.
require "json"
package = JSON.parse(File.read(File.join(__dir__, "..", "..", "package.json")))
version = package['version']
source = { :git => 'https://github.com/facebook/react-native.git' }
if version == '1000.0.0'
# This is an unpublished version, use the latest commit hash of the react-native repo, which were presumably in.
source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1")
else
source[:tag] = "v#{version}"
end
header_search_paths = [
"\"$(PODS_TARGET_SRCROOT)/..\""
]
Pod::Spec.new do |s|
s.name = "React-perflogger"
s.version = version
s.summary = "-" # TODO
s.homepage = "https://reactnative.dev/"
s.license = package["license"]
s.author = "Meta Platforms, Inc. and its affiliates"
s.platforms = min_supported_versions
s.source = source
s.source_files = podspec_sources(["reactperflogger/*.{cpp,h}", "fusebox/*.{cpp,h}"], ["reactperflogger/*.h", "fusebox/*.h"])
s.header_dir = "reactperflogger"
s.pod_target_xcconfig = { "CLANG_CXX_LANGUAGE_STANDARD" => rct_cxx_language_standard() }
s.pod_target_xcconfig = {
"CLANG_CXX_LANGUAGE_STANDARD" => rct_cxx_language_standard(),
"HEADER_SEARCH_PATHS" => header_search_paths.join(' '),
}
add_rn_third_party_dependencies(s)
add_rncore_dependency(s)
end

View File

@@ -0,0 +1,141 @@
/*
* 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 <folly/json.h>
#include <fstream>
#include <mutex>
#include <unordered_map>
#include "FuseboxTracer.h"
namespace facebook::react {
namespace {
/** Process ID for all emitted events. */
const uint64_t PID = 1000;
} // namespace
bool FuseboxTracer::isTracing() {
std::lock_guard lock(mutex_);
return tracing_;
}
bool FuseboxTracer::startTracing() {
std::lock_guard lock(mutex_);
if (tracing_) {
return false;
}
tracing_ = true;
return true;
}
bool FuseboxTracer::stopTracing(
const std::function<void(const folly::dynamic& eventsChunk)>&
resultCallback) {
std::lock_guard lock(mutex_);
if (!tracing_) {
return false;
}
tracing_ = false;
if (buffer_.empty()) {
return true;
}
auto traceEvents = folly::dynamic::array();
// Register "Main" process
traceEvents.push_back(
folly::dynamic::object("args", folly::dynamic::object("name", "Main"))(
"cat", "__metadata")("name", "process_name")("ph", "M")("pid", PID)(
"tid", 0)("ts", 0));
// Register "Timings" track
// NOTE: This is a hack to make the trace viewer show a "Timings" track
// adjacent to custom tracks in our current build of Chrome DevTools.
// In future, we should align events exactly.
traceEvents.push_back(
folly::dynamic::object("args", folly::dynamic::object("name", "Timings"))(
"cat", "__metadata")("name", "thread_name")("ph", "M")("pid", PID)(
"tid", 1000)("ts", 0));
auto savedBuffer = std::move(buffer_);
buffer_.clear();
std::unordered_map<std::string, uint64_t> trackIdMap;
uint64_t nextTrack = 1001;
for (auto& event : savedBuffer) {
// For events with a custom track name, register track
if (!event.track.empty() && !trackIdMap.contains(event.track)) {
auto trackId = nextTrack++;
trackIdMap[event.track] = trackId;
traceEvents.push_back(
folly::dynamic::object(
"args", folly::dynamic::object("name", event.track))(
"cat", "__metadata")("name", "thread_name")("ph", "M")(
"pid", PID)("tid", trackId)("ts", 0));
}
auto trackId =
trackIdMap.contains(event.track) ? trackIdMap[event.track] : 1000;
// Emit "blink.user_timing" trace event
traceEvents.push_back(
folly::dynamic::object("args", folly::dynamic::object())(
"cat", "blink.user_timing")(
"dur", (event.end - event.start) * 1000)("name", event.name)(
"ph", "X")("ts", event.start * 1000)("pid", PID)("tid", trackId));
if (traceEvents.size() >= 1000) {
resultCallback(traceEvents);
traceEvents = folly::dynamic::array();
}
}
if (!traceEvents.empty()) {
resultCallback(traceEvents);
}
return true;
}
void FuseboxTracer::addEvent(
const std::string_view& name,
uint64_t start,
uint64_t end,
const std::optional<std::string_view>& track) {
std::lock_guard<std::mutex> lock(mutex_);
if (!tracing_) {
return;
}
buffer_.push_back(
BufferEvent{
.start = start,
.end = end,
.name = std::string(name),
.track = std::string(track.value_or(""))});
}
bool FuseboxTracer::stopTracingAndWriteToFile(const std::string& path) {
auto file = std::ofstream(path);
folly::dynamic traceEvents = folly::dynamic::array();
bool result = stopTracing([&traceEvents](const folly::dynamic& eventsChunk) {
traceEvents.insert(
traceEvents.end(), eventsChunk.begin(), eventsChunk.end());
});
file << folly::toJson(traceEvents) << std::endl;
file.close();
return result;
}
/* static */ FuseboxTracer& FuseboxTracer::getFuseboxTracer() {
static FuseboxTracer tracer;
return tracer;
}
} // namespace facebook::react

View File

@@ -0,0 +1,55 @@
/*
* 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/json/dynamic.h"
#include <functional>
#include <optional>
#include <vector>
namespace facebook::react {
struct BufferEvent {
uint64_t start;
uint64_t end;
std::string name;
std::string track;
};
/**
* @deprecated Replaced by jsinspector_modern::PerformanceTracer and will be
* removed when we delete FuseboxPerfettoDataSource.
*/
class FuseboxTracer {
public:
FuseboxTracer(const FuseboxTracer &) = delete;
bool isTracing();
// Verifies that tracing isn't started and starts tracing all in one step.
// Returns true if we were able to successful start tracing.
bool startTracing();
// Verifies that we're tracing and dumps the trace all in one step to avoid
// TOCTOU bugs. Returns false if we're not tracing. No result callbacks
// are expected in that scenario.
bool stopTracing(const std::function<void(const folly::dynamic &eventsChunk)> &resultCallback);
bool stopTracingAndWriteToFile(const std::string &path);
void
addEvent(const std::string_view &name, uint64_t start, uint64_t end, const std::optional<std::string_view> &track);
static FuseboxTracer &getFuseboxTracer();
private:
FuseboxTracer() {}
bool tracing_{false};
std::vector<BufferEvent> buffer_;
std::mutex mutex_;
};
} // namespace facebook::react

View File

@@ -0,0 +1,90 @@
/*
* 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 <gtest/gtest.h>
#include <reactperflogger/fusebox/FuseboxTracer.h>
using namespace ::testing;
namespace facebook::react {
namespace {
class FuseboxTracerTest : public ::testing::Test {
protected:
FuseboxTracerTest() = default;
~FuseboxTracerTest() override = default;
void SetUp() override {
stopTracingAndCollect();
}
void TearDown() override {
stopTracingAndCollect();
}
folly::dynamic stopTracingAndCollect() {
folly::dynamic trace = folly::dynamic::array;
FuseboxTracer::getFuseboxTracer().stopTracing(
[&trace](const folly::dynamic& eventsChunk) {
for (const auto& event : eventsChunk) {
trace.push_back(event);
}
});
return trace;
}
};
} // namespace
TEST_F(FuseboxTracerTest, TracingOffByDefault) {
EXPECT_FALSE(FuseboxTracer::getFuseboxTracer().isTracing());
}
TEST_F(FuseboxTracerTest, TracingOn) {
FuseboxTracer::getFuseboxTracer().startTracing();
EXPECT_TRUE(FuseboxTracer::getFuseboxTracer().isTracing());
stopTracingAndCollect();
}
TEST_F(FuseboxTracerTest, DiscardEventWhenNotOn) {
EXPECT_FALSE(FuseboxTracer::getFuseboxTracer().isTracing());
EXPECT_EQ(stopTracingAndCollect().size(), 0);
FuseboxTracer::getFuseboxTracer().addEvent("test", 0, 0, "default track");
FuseboxTracer::getFuseboxTracer().addEvent("test", 0, 0, "default track");
EXPECT_EQ(stopTracingAndCollect().size(), 0);
}
TEST_F(FuseboxTracerTest, NoDefaultEvents) {
FuseboxTracer::getFuseboxTracer().startTracing();
EXPECT_EQ(stopTracingAndCollect().size(), 0);
}
TEST_F(FuseboxTracerTest, SimpleEvent) {
FuseboxTracer::getFuseboxTracer().startTracing();
FuseboxTracer::getFuseboxTracer().addEvent("test", 0, 0, "default track");
EXPECT_GE(stopTracingAndCollect().size(), 1);
}
TEST_F(FuseboxTracerTest, MultiEvents) {
FuseboxTracer::getFuseboxTracer().startTracing();
for (int i = 0; i < 10; i++) {
FuseboxTracer::getFuseboxTracer().addEvent("test", 0, 0, "default track");
}
EXPECT_GE(stopTracingAndCollect().size(), 10);
EXPECT_EQ(stopTracingAndCollect().size(), 0);
}
TEST_F(FuseboxTracerTest, ShouldEndTracingEvenIfThereIsNoEvents) {
FuseboxTracer::getFuseboxTracer().startTracing();
EXPECT_EQ(stopTracingAndCollect().size(), 0);
EXPECT_FALSE(FuseboxTracer::getFuseboxTracer().isTracing());
}
} // namespace facebook::react

View File

@@ -0,0 +1,327 @@
/*
* 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 "BridgeNativeModulePerfLogger.h"
namespace facebook::react::BridgeNativeModulePerfLogger {
std::unique_ptr<NativeModulePerfLogger> g_perfLogger = nullptr;
void enableLogging(std::unique_ptr<NativeModulePerfLogger>&& newPerfLogger) {
g_perfLogger = std::move(newPerfLogger);
}
void disableLogging() {
g_perfLogger = nullptr;
}
void moduleDataCreateStart(const char* moduleName, int32_t id) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->moduleDataCreateStart(moduleName, id);
}
}
void moduleDataCreateEnd(const char* moduleName, int32_t id) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->moduleDataCreateEnd(moduleName, id);
}
}
void moduleCreateStart(const char* moduleName, int32_t id) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->moduleCreateStart(moduleName, id);
}
}
void moduleCreateCacheHit(const char* moduleName, int32_t id) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->moduleCreateCacheHit(moduleName, id);
}
}
void moduleCreateConstructStart(const char* moduleName, int32_t id) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->moduleCreateConstructStart(moduleName, id);
}
}
void moduleCreateConstructEnd(const char* moduleName, int32_t id) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->moduleCreateConstructEnd(moduleName, id);
}
}
void moduleCreateSetUpStart(const char* moduleName, int32_t id) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->moduleCreateSetUpStart(moduleName, id);
}
}
void moduleCreateSetUpEnd(const char* moduleName, int32_t id) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->moduleCreateSetUpEnd(moduleName, id);
}
}
void moduleCreateEnd(const char* moduleName, int32_t id) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->moduleCreateEnd(moduleName, id);
}
}
void moduleCreateFail(const char* moduleName, int32_t id) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->moduleCreateFail(moduleName, id);
}
}
void moduleJSRequireBeginningStart(const char* moduleName) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->moduleJSRequireBeginningStart(moduleName);
}
}
void moduleJSRequireBeginningCacheHit(const char* moduleName) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->moduleJSRequireBeginningCacheHit(moduleName);
}
}
void moduleJSRequireBeginningEnd(const char* moduleName) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->moduleJSRequireBeginningEnd(moduleName);
}
}
void moduleJSRequireBeginningFail(const char* moduleName) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->moduleJSRequireBeginningFail(moduleName);
}
}
void moduleJSRequireEndingStart(const char* moduleName) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->moduleJSRequireEndingStart(moduleName);
}
}
void moduleJSRequireEndingEnd(const char* moduleName) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->moduleJSRequireEndingEnd(moduleName);
}
}
void moduleJSRequireEndingFail(const char* moduleName) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->moduleJSRequireEndingFail(moduleName);
}
}
void syncMethodCallStart(const char* moduleName, const char* methodName) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->syncMethodCallStart(moduleName, methodName);
}
}
void syncMethodCallArgConversionStart(
const char* moduleName,
const char* methodName) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->syncMethodCallArgConversionStart(moduleName, methodName);
}
}
void syncMethodCallArgConversionEnd(
const char* moduleName,
const char* methodName) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->syncMethodCallArgConversionEnd(moduleName, methodName);
}
}
void syncMethodCallExecutionStart(
const char* moduleName,
const char* methodName) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->syncMethodCallExecutionStart(moduleName, methodName);
}
}
void syncMethodCallExecutionEnd(
const char* moduleName,
const char* methodName) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->syncMethodCallExecutionEnd(moduleName, methodName);
}
}
void syncMethodCallReturnConversionStart(
const char* moduleName,
const char* methodName) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->syncMethodCallReturnConversionStart(moduleName, methodName);
}
}
void syncMethodCallReturnConversionEnd(
const char* moduleName,
const char* methodName) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->syncMethodCallReturnConversionEnd(moduleName, methodName);
}
}
void syncMethodCallEnd(const char* moduleName, const char* methodName) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->syncMethodCallEnd(moduleName, methodName);
}
}
void syncMethodCallFail(const char* moduleName, const char* methodName) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->syncMethodCallFail(moduleName, methodName);
}
}
void asyncMethodCallStart(const char* moduleName, const char* methodName) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->asyncMethodCallStart(moduleName, methodName);
}
}
void asyncMethodCallArgConversionStart(
const char* moduleName,
const char* methodName) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->asyncMethodCallArgConversionStart(moduleName, methodName);
}
}
void asyncMethodCallArgConversionEnd(
const char* moduleName,
const char* methodName) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->asyncMethodCallArgConversionEnd(moduleName, methodName);
}
}
void asyncMethodCallDispatch(const char* moduleName, const char* methodName) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->asyncMethodCallDispatch(moduleName, methodName);
}
}
void asyncMethodCallEnd(const char* moduleName, const char* methodName) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->asyncMethodCallEnd(moduleName, methodName);
}
}
void asyncMethodCallFail(const char* moduleName, const char* methodName) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->asyncMethodCallFail(moduleName, methodName);
}
}
void asyncMethodCallBatchPreprocessStart() {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->asyncMethodCallBatchPreprocessStart();
}
}
void asyncMethodCallBatchPreprocessEnd(int batchSize) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->asyncMethodCallBatchPreprocessEnd(batchSize);
}
}
void asyncMethodCallExecutionStart(
const char* moduleName,
const char* methodName,
int32_t id) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->asyncMethodCallExecutionStart(moduleName, methodName, id);
}
}
void asyncMethodCallExecutionArgConversionStart(
const char* moduleName,
const char* methodName,
int32_t id) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->asyncMethodCallExecutionArgConversionStart(
moduleName, methodName, id);
}
}
void asyncMethodCallExecutionArgConversionEnd(
const char* moduleName,
const char* methodName,
int32_t id) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->asyncMethodCallExecutionArgConversionEnd(
moduleName, methodName, id);
}
}
void asyncMethodCallExecutionEnd(
const char* moduleName,
const char* methodName,
int32_t id) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->asyncMethodCallExecutionEnd(moduleName, methodName, id);
}
}
void asyncMethodCallExecutionFail(
const char* moduleName,
const char* methodName,
int32_t id) {
NativeModulePerfLogger* logger = g_perfLogger.get();
if (logger != nullptr) {
logger->asyncMethodCallExecutionFail(moduleName, methodName, id);
}
}
} // namespace facebook::react::BridgeNativeModulePerfLogger

View File

@@ -0,0 +1,78 @@
/*
* 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 <memory>
#include "NativeModulePerfLogger.h"
namespace facebook::react::BridgeNativeModulePerfLogger {
void enableLogging(std::unique_ptr<NativeModulePerfLogger> &&logger);
void disableLogging();
void moduleDataCreateStart(const char *moduleName, int32_t id);
void moduleDataCreateEnd(const char *moduleName, int32_t id);
/**
* Create NativeModule platform object
*/
void moduleCreateStart(const char *moduleName, int32_t id);
void moduleCreateCacheHit(const char *moduleName, int32_t id);
void moduleCreateConstructStart(const char *moduleName, int32_t id);
void moduleCreateConstructEnd(const char *moduleName, int32_t id);
void moduleCreateSetUpStart(const char *moduleName, int32_t id);
void moduleCreateSetUpEnd(const char *moduleName, int32_t id);
void moduleCreateEnd(const char *moduleName, int32_t id);
void moduleCreateFail(const char *moduleName, int32_t id);
/**
* JS require beginning
*/
void moduleJSRequireBeginningStart(const char *moduleName);
void moduleJSRequireBeginningCacheHit(const char *moduleName);
void moduleJSRequireBeginningEnd(const char *moduleName);
void moduleJSRequireBeginningFail(const char *moduleName);
/**
* JS require ending
*/
void moduleJSRequireEndingStart(const char *moduleName);
void moduleJSRequireEndingEnd(const char *moduleName);
void moduleJSRequireEndingFail(const char *moduleName);
// Sync method calls
void syncMethodCallStart(const char *moduleName, const char *methodName);
void syncMethodCallArgConversionStart(const char *moduleName, const char *methodName);
void syncMethodCallArgConversionEnd(const char *moduleName, const char *methodName);
void syncMethodCallExecutionStart(const char *moduleName, const char *methodName);
void syncMethodCallExecutionEnd(const char *moduleName, const char *methodName);
void syncMethodCallReturnConversionStart(const char *moduleName, const char *methodName);
void syncMethodCallReturnConversionEnd(const char *moduleName, const char *methodName);
void syncMethodCallEnd(const char *moduleName, const char *methodName);
void syncMethodCallFail(const char *moduleName, const char *methodName);
// Async method calls
void asyncMethodCallStart(const char *moduleName, const char *methodName);
void asyncMethodCallArgConversionStart(const char *moduleName, const char *methodName);
void asyncMethodCallArgConversionEnd(const char *moduleName, const char *methodName);
void asyncMethodCallDispatch(const char *moduleName, const char *methodName);
void asyncMethodCallEnd(const char *moduleName, const char *methodName);
void asyncMethodCallFail(const char *moduleName, const char *methodName);
/**
* Pre-processing async method call batch
*/
void asyncMethodCallBatchPreprocessStart();
void asyncMethodCallBatchPreprocessEnd(int batchSize);
// Async method call execution
void asyncMethodCallExecutionStart(const char *moduleName, const char *methodName, int32_t id);
void asyncMethodCallExecutionArgConversionStart(const char *moduleName, const char *methodName, int32_t id);
void asyncMethodCallExecutionArgConversionEnd(const char *moduleName, const char *methodName, int32_t id);
void asyncMethodCallExecutionEnd(const char *moduleName, const char *methodName, int32_t id);
void asyncMethodCallExecutionFail(const char *moduleName, const char *methodName, int32_t id);
} // namespace facebook::react::BridgeNativeModulePerfLogger

View File

@@ -0,0 +1,151 @@
/*
* 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
#if defined(__ANDROID__)
#include <android/log.h>
#endif // __ANDROID__
#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;
#if defined(__ANDROID__)
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 "";
}
#endif // __ANDROID__
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();
auto* hermesAPI =
castInterface<hermes::IHermesRootAPI>(hermes::makeHermesRootAPI());
hermesAPI->enableSamplingProfiler(SAMPLING_HZ);
}
void FuseboxPerfettoDataSource::OnFlush(const FlushArgs&) {}
void FuseboxPerfettoDataSource::OnStop(const StopArgs& a) {
std::stringstream stream;
auto* hermesAPI =
castInterface<hermes::IHermesRootAPI>(hermes::makeHermesRootAPI());
hermesAPI->dumpSampledTraceToStream(stream);
std::string trace = stream.str();
logHermesProfileToFusebox(trace);
#if defined(__ANDROID__)
// 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");
#endif // __ANDROID__
}
} // namespace facebook::react
PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(
facebook::react::FuseboxPerfettoDataSource);
#endif // WITH_PERFETTO

View File

@@ -0,0 +1,46 @@
/*
* 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

View File

@@ -0,0 +1,139 @@
/*
* 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 <folly/json.h>
#include <hermes/hermes.h>
#include <perfetto.h>
#include <iostream>
#include "HermesPerfettoDataSource.h"
#include "ReactPerfetto.h"
namespace facebook::react {
namespace {
const int SAMPLING_HZ = 100;
int64_t hermesDeltaTime = 0;
uint64_t hermesToPerfettoTime(int64_t hermesTs) {
if (hermesDeltaTime == 0) {
hermesDeltaTime = TrackEvent::GetTraceTimeNs() -
std::chrono::steady_clock::now().time_since_epoch().count();
}
return (hermesTs * 1000 + hermesDeltaTime);
}
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) {
auto track = getPerfettoWebPerfTrackSync("JS Sampling");
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) {
TRACE_EVENT_BEGIN(
"react-native", perfetto::DynamicString{"..."}, track, start);
TRACE_EVENT_END("react-native", track, end);
}
continue;
}
std::string name = frame["name"].asString();
TRACE_EVENT_BEGIN(
"react-native", perfetto::DynamicString{name}, track, start);
TRACE_EVENT_END("react-native", track, end);
}
}
void logHermesProfileToPerfetto(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 HermesPerfettoDataSource::OnStart(const StartArgs&) {
auto* hermesAPI =
castInterface<hermes::IHermesRootAPI>(hermes::makeHermesRootAPI());
hermesAPI->enableSamplingProfiler(SAMPLING_HZ);
TRACE_EVENT_INSTANT(
"react-native",
perfetto::DynamicString{"Profiling Started"},
getPerfettoWebPerfTrackSync("JS Sampling"),
TrackEvent::GetTraceTimeNs());
}
void HermesPerfettoDataSource::OnFlush(const FlushArgs&) {
// NOTE: We write data during OnFlush and not OnStop because we can't
// use the TRACE_EVENT macros in OnStop.
auto* hermesAPI =
castInterface<hermes::IHermesRootAPI>(hermes::makeHermesRootAPI());
std::stringstream stream;
hermesAPI->dumpSampledTraceToStream(stream);
std::string trace = stream.str();
logHermesProfileToPerfetto(trace);
}
void HermesPerfettoDataSource::OnStop(const StopArgs& a) {
auto* hermesAPI =
castInterface<hermes::IHermesRootAPI>(hermes::makeHermesRootAPI());
hermesAPI->disableSamplingProfiler();
}
} // namespace facebook::react
PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(
facebook::react::HermesPerfettoDataSource);
#endif // WITH_PERFETTO

View File

@@ -0,0 +1,40 @@
/*
* 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 {
class HermesPerfettoDataSource : public perfetto::DataSource<HermesPerfettoDataSource> {
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.hermes.profiler");
HermesPerfettoDataSource::Register(dsd);
}
constexpr static perfetto::BufferExhaustedPolicy kBufferExhaustedPolicy = perfetto::BufferExhaustedPolicy::kStall;
};
} // namespace facebook::react
PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS(facebook::react::HermesPerfettoDataSource);
#endif // WITH_PERFETTO

View File

@@ -0,0 +1,116 @@
/*
* 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 <cstdint>
namespace facebook::react {
/**
* A platform-agnostic interface to do performance logging on NativeModules and
* TuboModules.
*/
class NativeModulePerfLogger {
public:
virtual ~NativeModulePerfLogger() = default;
/**
* NativeModule Initialization.
*
* The initialization of two NativeModules can interleave. Therefore,
* performance markers should use the moduleName as a unique key.
*/
/**
* On iOS:
* - NativeModule initialization is split into two phases, which sometimes
* have a pause in the middle.
* - TurboModule initialization happens all at once.
*
* On Android:
* - NativeModule and TurboModule initialization happens all at once.
*
* These markers are meant for iOS NativeModules:
* - moduleDataCreateStart: very beginning of first phase.
* - moduleDataCreateEnd: after RCTModuleData has been created.
*/
virtual void moduleDataCreateStart(const char *moduleName, int32_t id) = 0;
virtual void moduleDataCreateEnd(const char *moduleName, int32_t id) = 0;
/**
* How long does it take to create the platform NativeModule object?
* - moduleCreateStart: start creating platform NativeModule
* - moduleCreateEnd: stop creating platform NativeModule
*/
virtual void moduleCreateStart(const char *moduleName, int32_t id) = 0;
virtual void moduleCreateCacheHit(const char *moduleName, int32_t id) = 0;
virtual void moduleCreateConstructStart(const char *moduleName, int32_t id) = 0;
virtual void moduleCreateConstructEnd(const char *moduleName, int32_t id) = 0;
virtual void moduleCreateSetUpStart(const char *moduleName, int32_t id) = 0;
virtual void moduleCreateSetUpEnd(const char *moduleName, int32_t id) = 0;
virtual void moduleCreateEnd(const char *moduleName, int32_t id) = 0;
virtual void moduleCreateFail(const char *moduleName, int32_t id) = 0;
/**
* How long, after starting JS require, does it take to start creating the
* platform NativeModule?
* - moduleJSRequireBeginningStart: start of JS require
* - moduleJSRequireBeginningEnd: start creating platform NativeModule
*/
virtual void moduleJSRequireBeginningStart(const char *moduleName) = 0;
virtual void moduleJSRequireBeginningCacheHit(const char *moduleName) = 0;
virtual void moduleJSRequireBeginningEnd(const char *moduleName) = 0;
virtual void moduleJSRequireBeginningFail(const char *moduleName) = 0;
/**
* How long does it take to return from the JS require after the platform
* NativeModule is created?
* - moduleJSRequireEndingStart: end creating platform NativeModule
* - moduleJSRequireEndingEnd: end of JS require
*/
virtual void moduleJSRequireEndingStart(const char *moduleName) = 0;
virtual void moduleJSRequireEndingEnd(const char *moduleName) = 0;
virtual void moduleJSRequireEndingFail(const char *moduleName) = 0;
// Sync method calls
virtual void syncMethodCallStart(const char *moduleName, const char *methodName) = 0;
virtual void syncMethodCallArgConversionStart(const char *moduleName, const char *methodName) = 0;
virtual void syncMethodCallArgConversionEnd(const char *moduleName, const char *methodName) = 0;
virtual void syncMethodCallExecutionStart(const char *moduleName, const char *methodName) = 0;
virtual void syncMethodCallExecutionEnd(const char *moduleName, const char *methodName) = 0;
virtual void syncMethodCallReturnConversionStart(const char *moduleName, const char *methodName) = 0;
virtual void syncMethodCallReturnConversionEnd(const char *moduleName, const char *methodName) = 0;
virtual void syncMethodCallEnd(const char *moduleName, const char *methodName) = 0;
virtual void syncMethodCallFail(const char *moduleName, const char *methodName) = 0;
// Async method calls
virtual void asyncMethodCallStart(const char *moduleName, const char *methodName) = 0;
virtual void asyncMethodCallArgConversionStart(const char *moduleName, const char *methodName) = 0;
virtual void asyncMethodCallArgConversionEnd(const char *moduleName, const char *methodName) = 0;
virtual void asyncMethodCallDispatch(const char *moduleName, const char *methodName) = 0;
virtual void asyncMethodCallEnd(const char *moduleName, const char *methodName) = 0;
virtual void asyncMethodCallFail(const char *moduleName, const char *methodName) = 0;
/**
* In the NativeModule system, we batch async NativeModule method calls.
* When we execute a batch of NativeModule method calls, we convert the batch
* from a jsi::Value to folly::dynamic to std::vector<MethodCall>. This marker
* documents that work.
*/
virtual void asyncMethodCallBatchPreprocessStart() = 0;
virtual void asyncMethodCallBatchPreprocessEnd(int batchSize) = 0;
// Async method call execution
virtual void asyncMethodCallExecutionStart(const char *moduleName, const char *methodName, int32_t id) = 0;
virtual void
asyncMethodCallExecutionArgConversionStart(const char *moduleName, const char *methodName, int32_t id) = 0;
virtual void asyncMethodCallExecutionArgConversionEnd(const char *moduleName, const char *methodName, int32_t id) = 0;
virtual void asyncMethodCallExecutionEnd(const char *moduleName, const char *methodName, int32_t id) = 0;
virtual void asyncMethodCallExecutionFail(const char *moduleName, const char *methodName, int32_t id) = 0;
};
} // namespace facebook::react

View File

@@ -0,0 +1,96 @@
/*
* 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 "ReactPerfetto.h"
#include <perfetto.h>
#include <unordered_map>
#include "FuseboxPerfettoDataSource.h"
#include "HermesPerfettoDataSource.h"
#include "ReactPerfettoCategories.h"
namespace facebook::react {
std::once_flag perfettoInit;
void initializePerfetto() {
std::call_once(perfettoInit, []() {
perfetto::TracingInitArgs args;
// Raise the size of the shared memory buffer. Since this
// is only used in tracing build, large buffers are okay
// for now.
args.shmem_size_hint_kb = 20 * 1024;
args.backends |= perfetto::kSystemBackend;
args.use_monotonic_clock = true;
perfetto::Tracing::Initialize(args);
TrackEvent::Register();
});
HermesPerfettoDataSource::RegisterDataSource();
FuseboxPerfettoDataSource::RegisterDataSource();
}
static perfetto::Track createTrack(const std::string& trackName) {
// Offset for custom perfetto tracks
static uint64_t trackId = 0x5F3759DF;
auto track = perfetto::Track(trackId++);
auto desc = track.Serialize();
desc.set_name(trackName);
TrackEvent::SetTrackDescriptor(track, desc);
return track;
}
perfetto::Track getPerfettoWebPerfTrackSync(const std::string& trackName) {
// In the case of marks we can reuse the same track saving some resources,
// because there's no risk of partial overlap that would break the timings.
static std::unordered_map<std::string, perfetto::Track> tracks;
auto it = tracks.find(trackName);
if (it == tracks.end()) {
auto track = createTrack(trackName);
tracks.emplace(trackName, track);
return track;
} else {
return it->second;
}
}
perfetto::Track getPerfettoWebPerfTrackAsync(const std::string& trackName) {
// Note that, in the case of measures, we don't cache and reuse a track for a
// given name because Perfetto does not support partially overlapping measures
// in the same track.
//
// E.g.:
// [.....]
// [......]
// In that case, Perfetto would just cut subsequent measures as:
// [.....]
// [..] <-- Part of this section is gone, so the timing is incorrect.
//
// There's a solution though. Perfetto does group different tracks with the
// same name together, so having a separate track for each async event allows
// overlap.
return createTrack(trackName);
}
// Perfetto's monotonic clock seems to match the std::chrono::steady_clock we
// use in HighResTimeStamp on Android platforms, but if that
// assumption is incorrect we may need to manually offset perfetto timestamps.
uint64_t highResTimeStampToPerfettoTraceTime(HighResTimeStamp timestamp) {
auto chronoDurationSinceSteadyClockEpoch =
timestamp.toChronoSteadyClockTimePoint().time_since_epoch();
auto nanoseconds = std::chrono::duration_cast<std::chrono::nanoseconds>(
chronoDurationSinceSteadyClockEpoch);
return static_cast<uint64_t>(nanoseconds.count());
}
} // namespace facebook::react
#endif // WITH_PERFETTO

View File

@@ -0,0 +1,28 @@
/*
* 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>
#include <react/timing/primitives.h>
#include <reactperflogger/ReactPerfettoCategories.h>
#include <string>
namespace facebook::react {
void initializePerfetto();
perfetto::Track getPerfettoWebPerfTrackSync(const std::string &trackName);
perfetto::Track getPerfettoWebPerfTrackAsync(const std::string &trackName);
uint64_t highResTimeStampToPerfettoTraceTime(HighResTimeStamp timestamp);
} // namespace facebook::react
#endif // WITH_PERFETTO

View File

@@ -0,0 +1,15 @@
/*
* 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 "ReactPerfettoCategories.h"
PERFETTO_TRACK_EVENT_STATIC_STORAGE_IN_NAMESPACE(facebook::react);
PERFETTO_USE_CATEGORIES_FROM_NAMESPACE(facebook::react);
#endif

View File

@@ -0,0 +1,18 @@
/*
* 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>
PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE(
facebook::react,
perfetto::Category("react-native").SetDescription("User timing events from React Native"));
#endif

View File

@@ -0,0 +1,118 @@
/*
* 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 "ReactPerfettoLogger.h"
#if defined(WITH_PERFETTO)
#include "ReactPerfetto.h"
#elif defined(WITH_FBSYSTRACE)
#include <fbsystrace.h>
#endif
namespace facebook::react {
namespace {
#if defined(WITH_PERFETTO)
const HighResTimeStamp PROGRAM_INIT_TIME = HighResTimeStamp::now();
const std::string PERFETTO_DEFAULT_TRACK_NAME = "# Web Performance";
const std::string PERFETTO_TRACK_NAME_PREFIX = "# Web Performance: ";
std::string toPerfettoTrackName(
const std::optional<std::string_view>& trackName,
const std::optional<std::string_view>& trackGroup) {
std::string perfettoTrackName = PERFETTO_DEFAULT_TRACK_NAME;
if (trackName || trackGroup) {
perfettoTrackName += ": ";
if (trackGroup) {
perfettoTrackName += *trackGroup;
if (trackName) {
perfettoTrackName += " | ";
}
}
if (trackName) {
perfettoTrackName += *trackName;
}
}
return perfettoTrackName;
}
#elif defined(WITH_FBSYSTRACE)
int64_t getDeltaNanos(HighResTimeStamp jsTime) {
auto now = HighResTimeStamp::now();
return (jsTime - now).toNanoseconds();
}
#endif
} // namespace
/* static */ bool ReactPerfettoLogger::isTracing() {
#if defined(WITH_PERFETTO)
return TRACE_EVENT_CATEGORY_ENABLED("react-native");
#elif defined(WITH_FBSYSTRACE)
return fbsystrace_is_tracing(TRACE_TAG_REACT);
#else
return false;
#endif
}
/* static */ void ReactPerfettoLogger::measure(
const std::string_view& eventName,
HighResTimeStamp startTime,
HighResTimeStamp endTime,
const std::optional<std::string_view>& trackName,
const std::optional<std::string_view>& trackGroup) {
#if defined(WITH_PERFETTO)
if (TRACE_EVENT_CATEGORY_ENABLED("react-native")) {
if (startTime < PROGRAM_INIT_TIME || endTime < PROGRAM_INIT_TIME) {
return;
}
auto track = getPerfettoWebPerfTrackAsync(
toPerfettoTrackName(trackName, trackGroup));
TRACE_EVENT_BEGIN(
"react-native",
perfetto::DynamicString(eventName.data(), eventName.size()),
track,
highResTimeStampToPerfettoTraceTime(startTime));
TRACE_EVENT_END(
"react-native", track, highResTimeStampToPerfettoTraceTime(endTime));
}
#elif defined(WITH_FBSYSTRACE)
static int cookie = 0;
fbsystrace_begin_async_section_with_timedelta(
TRACE_TAG_REACT, eventName.data(), cookie, getDeltaNanos(startTime));
fbsystrace_end_async_section_with_timedelta(
TRACE_TAG_REACT, eventName.data(), cookie, getDeltaNanos(endTime));
cookie++;
#endif
}
/* static */ void ReactPerfettoLogger::mark(
const std::string_view& eventName,
HighResTimeStamp startTime,
const std::optional<std::string_view>& trackName,
const std::optional<std::string_view>& trackGroup) {
#if defined(WITH_PERFETTO)
if (TRACE_EVENT_CATEGORY_ENABLED("react-native")) {
TRACE_EVENT_INSTANT(
"react-native",
perfetto::DynamicString(eventName.data(), eventName.size()),
getPerfettoWebPerfTrackSync(toPerfettoTrackName(trackName, trackGroup)),
highResTimeStampToPerfettoTraceTime(startTime));
}
#elif defined(WITH_FBSYSTRACE)
static const char* kTrackName = "# Web Performance: Markers";
fbsystrace_instant_for_track_with_timedelta(
TRACE_TAG_REACT, kTrackName, eventName.data(), getDeltaNanos(startTime));
#endif
}
} // namespace facebook::react

View File

@@ -0,0 +1,40 @@
/*
* 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 <react/timing/primitives.h>
#include <reactperflogger/ReactPerfettoCategories.h>
#include <optional>
#include <string_view>
namespace facebook::react {
/**
* An internal interface for logging performance events to Perfetto, when
* configured.
*/
class ReactPerfettoLogger {
public:
static bool isTracing();
static void mark(
const std::string_view &eventName,
HighResTimeStamp startTime,
const std::optional<std::string_view> &trackName = std::nullopt,
const std::optional<std::string_view> &trackGroup = std::nullopt);
static void measure(
const std::string_view &eventName,
HighResTimeStamp startTime,
HighResTimeStamp endTime,
const std::optional<std::string_view> &trackName = std::nullopt,
const std::optional<std::string_view> &trackGroup = std::nullopt);
};
} // namespace facebook::react