/* * 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 #include #include #include #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& 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 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& track) { std::lock_guard 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