From 9cb716ff76e45583999b040be8a99b00f0e022e1 Mon Sep 17 00:00:00 2001 From: Christoph Purrer Date: Wed, 14 Sep 2022 16:29:20 -0700 Subject: [PATCH] Add additional Systrace support Summary: Adding support for application to hook into further tracing methods ## Changelog [General][Added] - Add additional Systrace support Reviewed By: NickGerleman Differential Revision: D38673212 fbshipit-source-id: 55a90a0cd57809bca5f01da7acddcf253e5852ba --- React/CxxModule/RCTNativeModule.mm | 15 ++++++ React/Profiler/RCTProfile.h | 20 ++++++++ ReactCommon/cxxreact/CxxNativeModule.cpp | 58 ++++++++++++++---------- ReactCommon/cxxreact/SystraceSection.h | 12 ++++- 4 files changed, 79 insertions(+), 26 deletions(-) diff --git a/React/CxxModule/RCTNativeModule.mm b/React/CxxModule/RCTNativeModule.mm index ebc3eda4eb597f..3e400a0c88706e 100644 --- a/React/CxxModule/RCTNativeModule.mm +++ b/React/CxxModule/RCTNativeModule.mm @@ -73,6 +73,15 @@ static MethodCallResult invokeInner( void RCTNativeModule::invoke(unsigned int methodId, folly::dynamic &¶ms, int callId) { + id method = m_moduleData.methods[methodId]; + if (method) { + RCT_PROFILE_BEGIN_EVENT( + RCTProfileTagAlways, + @"[RCTNativeModule invoke]", + @{@"method" : [NSString stringWithUTF8String:method.JSMethodName]}); + RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @""); + } + const char *moduleName = [m_moduleData.name UTF8String]; const char *methodName = m_moduleData.methods[methodId].JSMethodName; @@ -173,6 +182,10 @@ static MethodCallResult invokeInner( BridgeNativeModulePerfLogger::syncMethodCallArgConversionEnd(moduleName, methodName); } + RCT_PROFILE_BEGIN_EVENT( + RCTProfileTagAlways, + @"[RCTNativeModule invokeInner]", + @{@"method" : [NSString stringWithUTF8String:method.JSMethodName]}); @try { if (context == Sync) { BridgeNativeModulePerfLogger::syncMethodCallExecutionStart(moduleName, methodName); @@ -214,6 +227,8 @@ static MethodCallResult invokeInner( #else RCTFatalException(exception); #endif + } @finally { + RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @""); } return folly::none; diff --git a/React/Profiler/RCTProfile.h b/React/Profiler/RCTProfile.h index 7341566ce86be8..066e570f73ef51 100644 --- a/React/Profiler/RCTProfile.h +++ b/React/Profiler/RCTProfile.h @@ -57,6 +57,20 @@ RCT_EXTERN void RCTProfileInit(RCTBridge *); */ RCT_EXTERN void RCTProfileEnd(RCTBridge *, void (^)(NSString *)); +/** + * Route the RCT_PROFILE_BEGIN_EVENT hooks to our loom tracing. + */ +#ifdef WITH_LOOM_TRACE +RCT_EXTERN BOOL _RCTLoomIsProfiling(void); +RCT_EXTERN void +_RCTLoomBeginEvent(NSString *name, const char *file, size_t line, NSDictionary *args); +RCT_EXTERN void _RCTLoomEndEvent(); +#else +#define _RCTLoomIsProfiling(...) NO +#define _RCTLoomBeginEvent(...) +#define _RCTLoomEndEvent(...) +#endif + /** * Collects the initial event information for the event and returns a reference ID */ @@ -68,6 +82,9 @@ RCT_EXTERN void _RCTProfileBeginEvent( NSDictionary *args); #define RCT_PROFILE_BEGIN_EVENT(tag, name, args) \ do { \ + if (_RCTLoomIsProfiling()) { \ + _RCTLoomBeginEvent(name, __FILE__, __LINE__, args); \ + } \ if (RCTProfileIsProfiling()) { \ NSThread *__calleeThread = [NSThread currentThread]; \ NSTimeInterval __time = CACurrentMediaTime(); \ @@ -89,6 +106,9 @@ RCT_EXTERN void _RCTProfileEndEvent( #define RCT_PROFILE_END_EVENT(tag, category) \ do { \ + if (_RCTLoomIsProfiling()) { \ + _RCTLoomEndEvent(); \ + } \ if (RCTProfileIsProfiling()) { \ NSThread *__calleeThread = [NSThread currentThread]; \ NSString *__threadName = RCTCurrentThreadName(); \ diff --git a/ReactCommon/cxxreact/CxxNativeModule.cpp b/ReactCommon/cxxreact/CxxNativeModule.cpp index fbfdaa0aeb41cb..ef5c5ea532b670 100644 --- a/ReactCommon/cxxreact/CxxNativeModule.cpp +++ b/ReactCommon/cxxreact/CxxNativeModule.cpp @@ -185,34 +185,42 @@ void CxxNativeModule::invoke( // stack. I'm told that will be possible in the future. TODO // mhorowitz #7128529: convert C++ exceptions to Java - messageQueueThread_->runOnQueue( - [method, params = std::move(params), first, second, callId]() { + const auto &moduleName = name_; + SystraceSection s( + "CxxMethodCallQueue", "module", moduleName, "method", method.name); + messageQueueThread_->runOnQueue([method, + moduleName, + params = std::move(params), + first, + second, + callId]() { #ifdef WITH_FBSYSTRACE - if (callId != -1) { - fbsystrace_end_async_flow(TRACE_TAG_REACT_APPS, "native", callId); - } + if (callId != -1) { + fbsystrace_end_async_flow(TRACE_TAG_REACT_APPS, "native", callId); + } #else - (void)(callId); + (void)(callId); #endif - SystraceSection s(method.name.c_str()); - try { - method.func(std::move(params), first, second); - } catch (const facebook::xplat::JsArgumentException &ex) { - throw; - } catch (std::exception &e) { - LOG(ERROR) << "std::exception. Method call " << method.name.c_str() - << " failed: " << e.what(); - std::terminate(); - } catch (std::string &error) { - LOG(ERROR) << "std::string. Method call " << method.name.c_str() - << " failed: " << error.c_str(); - std::terminate(); - } catch (...) { - LOG(ERROR) << "Method call " << method.name.c_str() - << " failed. unknown error"; - std::terminate(); - } - }); + SystraceSection s( + "CxxMethodCallDispatch", "module", moduleName, "method", method.name); + try { + method.func(std::move(params), first, second); + } catch (const facebook::xplat::JsArgumentException &ex) { + throw; + } catch (std::exception &e) { + LOG(ERROR) << "std::exception. Method call " << method.name.c_str() + << " failed: " << e.what(); + std::terminate(); + } catch (std::string &error) { + LOG(ERROR) << "std::string. Method call " << method.name.c_str() + << " failed: " << error.c_str(); + std::terminate(); + } catch (...) { + LOG(ERROR) << "Method call " << method.name.c_str() + << " failed. unknown error"; + std::terminate(); + } + }); } MethodCallResult CxxNativeModule::callSerializableNativeHook( diff --git a/ReactCommon/cxxreact/SystraceSection.h b/ReactCommon/cxxreact/SystraceSection.h index 3527f09fe7eb42..46996ff2bb74d9 100644 --- a/ReactCommon/cxxreact/SystraceSection.h +++ b/ReactCommon/cxxreact/SystraceSection.h @@ -14,6 +14,16 @@ namespace facebook { namespace react { +/** + * Allow providing an fbsystrace implementation that can short-circuit out + * quickly and can throttle too frequent events so we can get useful traces even + * if rendering etc. is spinning. For throttling we'll need file/line info so we + * use a macro. + */ +#if defined(WITH_LOOM_TRACE) +#define SystraceSection \ + static constexpr const char systraceSectionFile[] = __FILE__; \ + fbsystrace::FbSystraceSection /** * This is a convenience class to avoid lots of verbose profiling * #ifdefs. If WITH_FBSYSTRACE is not defined, the optimizer will @@ -23,7 +33,7 @@ namespace react { * different values in different files, there is no inconsistency in the sizes * of defined symbols. */ -#ifdef WITH_FBSYSTRACE +#elif defined(WITH_FBSYSTRACE) struct ConcreteSystraceSection { public: template