diff --git a/packages/react-native/ReactCommon/cxxreact/SystraceSection.h b/packages/react-native/ReactCommon/cxxreact/SystraceSection.h index 34a31792e42eae..69fd7202b4fccb 100644 --- a/packages/react-native/ReactCommon/cxxreact/SystraceSection.h +++ b/packages/react-native/ReactCommon/cxxreact/SystraceSection.h @@ -11,6 +11,17 @@ #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 +#endif + +#endif + namespace facebook::react { /** @@ -20,7 +31,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 +55,7 @@ struct ConcreteSystraceSection { private: fbsystrace::FbSystraceSection m_section; }; -using SystraceSection = ConcreteSystraceSection; +using SystraceSectionUnwrapped = ConcreteSystraceSection; #else struct DummySystraceSection { public: @@ -53,7 +64,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_); + } + + private: + os_signpost_id_t signpostID_ = OS_SIGNPOST_ID_INVALID; + const char* name_; + SystraceSectionUnwrapped systraceSectionUnwrapped_; +}; +#else +#define SystraceSection SystraceSectionUnwrapped #endif } // namespace facebook::react