From e37cd0090a0d59e603f43fad297d8ee20758b35a Mon Sep 17 00:00:00 2001 From: Tomek Zawadzki Date: Wed, 14 Oct 2026 11:45:49 +0200 Subject: [PATCH] Emit C++ TraceSection markers through ATrace on Android In OSS Android builds TraceSection compiles to a no-op unless WITH_PERFETTO or WITH_FBSYSTRACE is defined, which nothing in the build sets. Route it to the NDK ATrace API instead (resolved with dlsym, gated on ATrace_isEnabled) so that the existing C++ sections show up in Perfetto and systrace captures. Co-Authored-By: Claude Opus 5.5 --- .../ReactCommon/cxxreact/TraceSection.h | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/packages/react-native/ReactCommon/cxxreact/TraceSection.h b/packages/react-native/ReactCommon/cxxreact/TraceSection.h index c0d7fba280b6..8e838905aec1 100644 --- a/packages/react-native/ReactCommon/cxxreact/TraceSection.h +++ b/packages/react-native/ReactCommon/cxxreact/TraceSection.h @@ -34,6 +34,10 @@ #include #endif +#if defined(__ANDROID__) && !defined(WITH_PERFETTO) && !defined(WITH_FBSYSTRACE) +#include +#endif + #if defined(__APPLE__) // This is required so that OS_LOG_TARGET_HAS_10_15_FEATURES will be set. #include @@ -95,6 +99,59 @@ struct ConcreteTraceSection { fbsystrace::FbSystraceSection m_section; }; using TraceSectionUnwrapped = ConcreteTraceSection; +#elif defined(__ANDROID__) +// Route TraceSection to the platform ATrace API so +// that C++ sections show up in Perfetto/systrace captures of OSS builds. +// Resolved with dlsym to avoid adding a link dependency on libandroid to +// every target that includes this header. +namespace detail { +struct ATraceApi { + bool (*isEnabled)() = nullptr; + void (*beginSection)(const char *) = nullptr; + void (*endSection)() = nullptr; +}; + +inline const ATraceApi &getATraceApi() +{ + static const ATraceApi api = [] { + ATraceApi result; + if (void *lib = dlopen("libandroid.so", RTLD_NOW | RTLD_LOCAL)) { + result.isEnabled = reinterpret_cast(dlsym(lib, "ATrace_isEnabled")); + result.beginSection = reinterpret_cast(dlsym(lib, "ATrace_beginSection")); + result.endSection = reinterpret_cast(dlsym(lib, "ATrace_endSection")); + } + return result; + }(); + return api; +} +} // namespace detail + +struct ATraceSection { + public: + template + explicit ATraceSection(const char *name, [[maybe_unused]] ConvertsToStringPiece &&...args) + { + const auto &api = detail::getATraceApi(); + if (api.isEnabled != nullptr && api.beginSection != nullptr && api.endSection != nullptr && api.isEnabled()) { + api.beginSection(name); + active_ = true; + } + } + + ~ATraceSection() + { + if (active_) { + detail::getATraceApi().endSection(); + } + } + + ATraceSection(const ATraceSection &) = delete; + ATraceSection &operator=(const ATraceSection &) = delete; + + private: + bool active_{false}; +}; +using TraceSectionUnwrapped = ATraceSection; #else struct DummyTraceSection { public: