From e37cd0090a0d59e603f43fad297d8ee20758b35a Mon Sep 17 00:00:00 2001 From: Tomek Zawadzki Date: Wed, 14 Oct 2026 11:45:49 +0200 Subject: [PATCH 1/2] 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: From 5c5202e3ab5cca306e32d4379d6483e3836bde04 Mon Sep 17 00:00:00 2001 From: Tomek Zawadzki Date: Sat, 17 Oct 2026 00:23:50 +0200 Subject: [PATCH 2/2] Call ATrace directly through Include the NDK header and call ATrace_isEnabled/beginSection/endSection directly instead of resolving them with dlsym. libreactnative already links libandroid, and hermestooling links it through libreactnative. Co-Authored-By: Claude Opus 5.5 --- .../ReactCommon/cxxreact/TraceSection.h | 37 +++---------------- 1 file changed, 6 insertions(+), 31 deletions(-) diff --git a/packages/react-native/ReactCommon/cxxreact/TraceSection.h b/packages/react-native/ReactCommon/cxxreact/TraceSection.h index 8e838905aec1..2c9ff9ddb5f4 100644 --- a/packages/react-native/ReactCommon/cxxreact/TraceSection.h +++ b/packages/react-native/ReactCommon/cxxreact/TraceSection.h @@ -34,8 +34,8 @@ #include #endif -#if defined(__ANDROID__) && !defined(WITH_PERFETTO) && !defined(WITH_FBSYSTRACE) -#include +#if defined(__ANDROID__) && __ANDROID_API__ >= 23 && !defined(WITH_PERFETTO) && !defined(WITH_FBSYSTRACE) +#include #endif #if defined(__APPLE__) @@ -99,41 +99,16 @@ struct ConcreteTraceSection { fbsystrace::FbSystraceSection m_section; }; using TraceSectionUnwrapped = ConcreteTraceSection; -#elif defined(__ANDROID__) +#elif defined(__ANDROID__) && __ANDROID_API__ >= 23 // 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); + if (ATrace_isEnabled()) { + ATrace_beginSection(name); active_ = true; } } @@ -141,7 +116,7 @@ struct ATraceSection { ~ATraceSection() { if (active_) { - detail::getATraceApi().endSection(); + ATrace_endSection(); } }