diff --git a/packages/react-native/ReactCommon/hermes/inspector-modern/chrome/HermesRuntimeSamplingProfileSerializer.cpp b/packages/react-native/ReactCommon/hermes/inspector-modern/chrome/HermesRuntimeSamplingProfileSerializer.cpp index a7375112f0c..0c654a303a5 100644 --- a/packages/react-native/ReactCommon/hermes/inspector-modern/chrome/HermesRuntimeSamplingProfileSerializer.cpp +++ b/packages/react-native/ReactCommon/hermes/inspector-modern/chrome/HermesRuntimeSamplingProfileSerializer.cpp @@ -5,12 +5,16 @@ * LICENSE file in the root directory of this source tree. */ +#include + #include "HermesRuntimeSamplingProfileSerializer.h" namespace facebook::react::jsinspector_modern::tracing { namespace { +namespace fhsp = facebook::hermes::sampling_profiler; + /// Fallback script ID for call frames, when Hermes didn't provide one or when /// this frame is part of the VM, like native functions, used for parity with /// Chromium + V8. @@ -21,123 +25,114 @@ const std::string GARBAGE_COLLECTOR_FRAME_NAME = "(garbage collector)"; /// Filters out Hermes Suspend frames related to Debugger. /// Even though Debugger domain is expected to be disabled, Hermes might run /// Debugger loop while recording sampling profile. We only allow GC frames. -bool shouldIgnoreHermesFrame( - hermes::sampling_profiler::ProfileSampleCallStackFrame* hermesFrame) { - if (hermesFrame->getKind() != - hermes::sampling_profiler::ProfileSampleCallStackFrame::Kind::Suspend) { - return false; - } - - auto* suspendFrame = static_cast< - hermes::sampling_profiler::ProfileSampleCallStackSuspendFrame*>( - hermesFrame); - auto suspendFrameKind = suspendFrame->getSuspendFrameKind(); - return suspendFrameKind != - hermes::sampling_profiler::ProfileSampleCallStackSuspendFrame:: - SuspendFrameKind::GC; +inline bool shouldIgnoreHermesFrame( + const fhsp::ProfileSampleCallStackSuspendFrame& suspendFrame) { + return suspendFrame.getSuspendFrameKind() != + fhsp::ProfileSampleCallStackSuspendFrame::SuspendFrameKind::GC; } -RuntimeSamplingProfile::SampleCallStackFrame convertHermesFrameToTracingFrame( - hermes::sampling_profiler::ProfileSampleCallStackFrame* hermesFrame) { - switch (hermesFrame->getKind()) { - case hermes::sampling_profiler::ProfileSampleCallStackFrame::Kind:: - JSFunction: { - auto* jsFunctionFrame = static_cast< - hermes::sampling_profiler::ProfileSampleCallStackJSFunctionFrame*>( - hermesFrame); - return RuntimeSamplingProfile::SampleCallStackFrame{ - RuntimeSamplingProfile::SampleCallStackFrame::Kind::JSFunction, - jsFunctionFrame->hasScriptId() ? jsFunctionFrame->getScriptId() - : FALLBACK_SCRIPT_ID, - jsFunctionFrame->getFunctionName(), - jsFunctionFrame->hasUrl() - ? std::optional{jsFunctionFrame->getUrl()} - : std::nullopt, - jsFunctionFrame->hasLineNumber() - ? std::optional{jsFunctionFrame->getLineNumber() - 1} - // Hermes VM keeps line numbers as 1-based. Convert to - // 0-based. - : std::nullopt, - jsFunctionFrame->hasColumnNumber() - ? std::optional{jsFunctionFrame->getColumnNumber() - 1} - // Hermes VM keeps column numbers as 1-based. Convert to - // 0-based. - : std::nullopt, - }; - } - case hermes::sampling_profiler::ProfileSampleCallStackFrame::Kind:: - NativeFunction: { - auto* nativeFunctionFrame = - static_cast( - hermesFrame); +RuntimeSamplingProfile::SampleCallStackFrame convertNativeHermesFrame( + const fhsp::ProfileSampleCallStackNativeFunctionFrame& frame) { + return RuntimeSamplingProfile::SampleCallStackFrame{ + RuntimeSamplingProfile::SampleCallStackFrame::Kind::NativeFunction, + FALLBACK_SCRIPT_ID, // JavaScript Runtime defines the implementation + // for native function, no script ID to reference. + frame.getFunctionName(), + }; +} - return RuntimeSamplingProfile::SampleCallStackFrame{ - RuntimeSamplingProfile::SampleCallStackFrame::Kind::NativeFunction, - FALLBACK_SCRIPT_ID, // JavaScript Runtime defines the implementation - // for native function, no script ID to reference. - nativeFunctionFrame->getFunctionName(), - }; - } - case hermes::sampling_profiler::ProfileSampleCallStackFrame::Kind:: - HostFunction: { - auto* hostFunctionFrame = static_cast< - hermes::sampling_profiler::ProfileSampleCallStackHostFunctionFrame*>( - hermesFrame); +RuntimeSamplingProfile::SampleCallStackFrame convertHostFunctionHermesFrame( + const fhsp::ProfileSampleCallStackHostFunctionFrame& frame) { + return RuntimeSamplingProfile::SampleCallStackFrame{ + RuntimeSamplingProfile::SampleCallStackFrame::Kind::HostFunction, + FALLBACK_SCRIPT_ID, // JavaScript Runtime defines the implementation + // for host function, no script ID to reference. + frame.getFunctionName(), + }; +} - return RuntimeSamplingProfile::SampleCallStackFrame{ - RuntimeSamplingProfile::SampleCallStackFrame::Kind::HostFunction, - FALLBACK_SCRIPT_ID, // JavaScript Runtime defines the implementation - // for host function, no script ID to reference. - hostFunctionFrame->getFunctionName(), - }; - } - case hermes::sampling_profiler::ProfileSampleCallStackFrame::Kind:: - Suspend: { - auto* suspendFrame = static_cast< - hermes::sampling_profiler::ProfileSampleCallStackSuspendFrame*>( - hermesFrame); - auto suspendFrameKind = suspendFrame->getSuspendFrameKind(); - if (suspendFrameKind == - hermes::sampling_profiler::ProfileSampleCallStackSuspendFrame:: - SuspendFrameKind::GC) { - return RuntimeSamplingProfile::SampleCallStackFrame{ - RuntimeSamplingProfile::SampleCallStackFrame::Kind:: - GarbageCollector, - FALLBACK_SCRIPT_ID, // GC frames are part of the VM, no script ID to - // reference. - GARBAGE_COLLECTOR_FRAME_NAME, - }; - } - - // We should have filtered out Debugger Suspend frames before in - // shouldFilterOutHermesFrame(). - throw std::logic_error{ - "Unexpected Suspend frame found in Hermes call stack"}; - } - - default: - throw std::logic_error{"Unknown Hermes stack frame kind"}; +RuntimeSamplingProfile::SampleCallStackFrame convertSuspendHermesFrame( + const fhsp::ProfileSampleCallStackSuspendFrame& frame) { + if (frame.getSuspendFrameKind() == + fhsp::ProfileSampleCallStackSuspendFrame::SuspendFrameKind::GC) { + return RuntimeSamplingProfile::SampleCallStackFrame{ + RuntimeSamplingProfile::SampleCallStackFrame::Kind::GarbageCollector, + FALLBACK_SCRIPT_ID, // GC frames are part of the VM, no script ID to + // reference. + GARBAGE_COLLECTOR_FRAME_NAME, + }; } + + // We should have filtered out Debugger Suspend frames before in + // shouldFilterOutHermesFrame(). + throw std::logic_error{"Unexpected Suspend frame found in Hermes call stack"}; +} + +RuntimeSamplingProfile::SampleCallStackFrame convertJSFunctionHermesFrame( + const fhsp::ProfileSampleCallStackJSFunctionFrame& frame) { + return RuntimeSamplingProfile::SampleCallStackFrame{ + RuntimeSamplingProfile::SampleCallStackFrame::Kind::JSFunction, + frame.hasScriptId() ? frame.getScriptId() : FALLBACK_SCRIPT_ID, + frame.getFunctionName(), + frame.hasUrl() ? std::optional{frame.getUrl()} + : std::nullopt, + frame.hasLineNumber() ? std::optional{frame.getLineNumber() - 1} + // Hermes VM keeps line numbers as 1-based. Convert + // to 0-based. + : std::nullopt, + frame.hasColumnNumber() + ? std::optional{frame.getColumnNumber() - 1} + // Hermes VM keeps column numbers as 1-based. Convert to + // 0-based. + : std::nullopt, + }; } RuntimeSamplingProfile::Sample convertHermesSampleToTracingSample( - const hermes::sampling_profiler::ProfileSample& hermesSample) { + const fhsp::ProfileSample& hermesSample) { uint64_t reconciledTimestamp = hermesSample.getTimestamp(); - std::vector - hermesSampleCallStack = hermesSample.getCallStack(); + const std::vector& hermesSampleCallStack = + hermesSample.getCallStack(); std::vector reconciledSampleCallStack; reconciledSampleCallStack.reserve(hermesSampleCallStack.size()); - for (auto* hermesFrame : hermesSampleCallStack) { - if (shouldIgnoreHermesFrame(hermesFrame)) { - continue; + for (const auto& hermesFrame : hermesSampleCallStack) { + if (std::holds_alternative( + hermesFrame)) { + const auto& suspendFrame = + std::get(hermesFrame); + if (shouldIgnoreHermesFrame(suspendFrame)) { + continue; + } + + reconciledSampleCallStack.emplace_back( + convertSuspendHermesFrame(suspendFrame)); + } else if (std::holds_alternative< + fhsp::ProfileSampleCallStackNativeFunctionFrame>( + hermesFrame)) { + const auto& nativeFunctionFrame = + std::get( + hermesFrame); + reconciledSampleCallStack.emplace_back( + convertNativeHermesFrame(nativeFunctionFrame)); + } else if (std::holds_alternative< + fhsp::ProfileSampleCallStackHostFunctionFrame>( + hermesFrame)) { + const auto& hostFunctionFrame = + std::get(hermesFrame); + reconciledSampleCallStack.emplace_back( + convertHostFunctionHermesFrame(hostFunctionFrame)); + } else if (std::holds_alternative< + fhsp::ProfileSampleCallStackJSFunctionFrame>(hermesFrame)) { + const auto& jsFunctionFrame = + std::get(hermesFrame); + reconciledSampleCallStack.emplace_back( + convertJSFunctionHermesFrame(jsFunctionFrame)); + } else { + throw std::logic_error{"Unknown Hermes stack frame kind"}; } - RuntimeSamplingProfile::SampleCallStackFrame reconciledFrame = - convertHermesFrameToTracingFrame(hermesFrame); - reconciledSampleCallStack.push_back(std::move(reconciledFrame)); } return RuntimeSamplingProfile::Sample{ @@ -156,7 +151,7 @@ HermesRuntimeSamplingProfileSerializer::serializeToTracingSamplingProfile( std::vector reconciledSamples; reconciledSamples.reserve(hermesSamples.size()); - for (auto& hermesSample : hermesSamples) { + for (const auto& hermesSample : hermesSamples) { RuntimeSamplingProfile::Sample reconciledSample = convertHermesSampleToTracingSample(hermesSample); reconciledSamples.push_back(std::move(reconciledSample));