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,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