From 044e72bc9631cc64f2dade1f914a680591b6cfbd Mon Sep 17 00:00:00 2001 From: Liron Yahdav Date: Thu, 23 May 2024 06:14:58 -0700 Subject: [PATCH] Make SystraceSection feed into Instruments signposts API (#44496) Summary: When doing performance profiling on a React Native iOS app and trying to identify bottlenecks on the native side it can be helpful to correlate actions to what is happening inside React Native. We already have the SystraceSection class for this, but it does nothing in open source. This diff allows SystraceSection to feed into the Instruments signpost API on iOS/macOS. Changelog: [iOS][Added] - Add Instruments signposts API for SystraceSection Reviewed By: sammy-SC Differential Revision: D56280451 --- .../ReactCommon/cxxreact/SystraceSection.h | 103 +++++++++++++++++- 1 file changed, 100 insertions(+), 3 deletions(-) diff --git a/packages/react-native/ReactCommon/cxxreact/SystraceSection.h b/packages/react-native/ReactCommon/cxxreact/SystraceSection.h index 34a31792e42eae..59fa9e35e0e2d4 100644 --- a/packages/react-native/ReactCommon/cxxreact/SystraceSection.h +++ b/packages/react-native/ReactCommon/cxxreact/SystraceSection.h @@ -11,6 +11,18 @@ #include #endif +#if defined(__APPLE__) +// This is required so that OS_LOG_TARGET_HAS_10_15_FEATURES will be set. +#include + +#if OS_LOG_TARGET_HAS_10_15_FEATURES && !defined(WITH_LOOM_TRACE) +#include +#include +#include +#endif + +#endif + namespace facebook::react { /** @@ -20,7 +32,7 @@ namespace facebook::react { * use a macro. */ #if defined(WITH_LOOM_TRACE) -#define SystraceSection \ +#define SystraceSectionUnwrapped \ static constexpr const char systraceSectionFile[] = __FILE__; \ fbsystrace::FbSystraceSection /** @@ -44,7 +56,7 @@ struct ConcreteSystraceSection { private: fbsystrace::FbSystraceSection m_section; }; -using SystraceSection = ConcreteSystraceSection; +using SystraceSectionUnwrapped = ConcreteSystraceSection; #else struct DummySystraceSection { public: @@ -53,7 +65,92 @@ struct DummySystraceSection { const __unused char* name, __unused ConvertsToStringPiece&&... args) {} }; -using SystraceSection = DummySystraceSection; +using SystraceSectionUnwrapped = DummySystraceSection; +#endif + +/** + * On recent Apple platforms we want to leverage the Instruments signposts APIs. + * To not break the other SystraceSection implementations above we wrap them. + * In the case of WITH_LOOM_TRACE we don't use the signposts APIs because of the + * templated type for SystraceSection. + */ +#if defined(__APPLE__) && OS_LOG_TARGET_HAS_10_15_FEATURES && \ + !defined(WITH_LOOM_TRACE) +namespace systrace { + +template +struct renderer { + static std::string render(const T& t) { + std::ostringstream oss; + oss << t; + return oss.str(); + } +}; + +template +static auto render(const T& t) + -> decltype(renderer::render(std::declval())) { + return renderer::render(t); +} + +inline os_log_t instrumentsLogHandle = nullptr; + +} // namespace systrace + +struct SystraceSection { + public: + template + explicit SystraceSection(const char* name, ConvertsToStringPiece&&... args) + : systraceSectionUnwrapped_(name, args...) { + if (!systrace::instrumentsLogHandle) { + systrace::instrumentsLogHandle = os_log_create( + "dev.reactnative.instruments", OS_LOG_CATEGORY_DYNAMIC_TRACING); + } + + // If the log isn't enabled, we don't want the performance overhead of the + // rest of the code below. + if (!os_signpost_enabled(systrace::instrumentsLogHandle)) { + return; + } + + name_ = name; + + const auto argsVector = std::vector{systrace::render(args)...}; + std::string argsString = ""; + for (size_t i = 0; i < argsVector.size(); i += 2) { + argsString += argsVector[i] + "=" + argsVector[i + 1] + ";"; + } + + signpostID_ = + os_signpost_id_make_with_pointer(systrace::instrumentsLogHandle, this); + + os_signpost_interval_begin( + systrace::instrumentsLogHandle, + signpostID_, + "SystraceSection", + "%s begin: %s", + name, + argsString.c_str()); + } + + ~SystraceSection() { + // We don't need to gate on os_signpost_enabled here because it's already + // checked in os_signpost_interval_end. + os_signpost_interval_end( + systrace::instrumentsLogHandle, + signpostID_, + "SystraceSection", + "%s end", + name_.data()); + } + + private: + os_signpost_id_t signpostID_ = OS_SIGNPOST_ID_INVALID; + std::string_view name_; + SystraceSectionUnwrapped systraceSectionUnwrapped_; +}; +#else +#define SystraceSection SystraceSectionUnwrapped #endif } // namespace facebook::react