/
githubmr
/
facebook-react-native
Обзор
Документация
Войти
/
githubmr
/
facebook-react-native
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
main
packages/react-native/ReactCommon/hermes/inspector-modern/chrome/HermesRuntimeSamplingProfileSerializer.cpp
176 строк
7 KB
Ruslan Lesiutin
Static generators for Profile Trace Events (#52915)
31 июл 2025, 17:43
31 июл 2025, 17:43
a8f6c96
Код
Авторство
О чём код?
/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #include "HermesRuntimeSamplingProfileSerializer.h" #include <oscompat/OSCompat.h> #include <variant> 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. constexpr uint32_t FALLBACK_SCRIPT_ID = 0; /// Garbage collector frame name, used for parity with Chromium + V8. constexpr std::string_view 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. inline bool shouldIgnoreHermesFrame( const fhsp::ProfileSampleCallStackSuspendFrame& suspendFrame) { return suspendFrame.getSuspendFrameKind() != fhsp::ProfileSampleCallStackSuspendFrame::SuspendFrameKind::GC; } RuntimeSamplingProfile::SampleCallStackFrame convertNativeHermesFrame( const fhsp::ProfileSampleCallStackNativeFunctionFrame& frame) { return RuntimeSamplingProfile::SampleCallStackFrame{ .kind = RuntimeSamplingProfile::SampleCallStackFrame::Kind::NativeFunction, .scriptId = FALLBACK_SCRIPT_ID, // JavaScript Runtime defines the implementation // for native function, no script ID to reference. .functionName = frame.getFunctionName(), }; } RuntimeSamplingProfile::SampleCallStackFrame convertHostFunctionHermesFrame( const fhsp::ProfileSampleCallStackHostFunctionFrame& frame) { return RuntimeSamplingProfile::SampleCallStackFrame{ .kind = RuntimeSamplingProfile::SampleCallStackFrame::Kind::HostFunction, .scriptId = FALLBACK_SCRIPT_ID, // JavaScript Runtime defines the implementation // for host function, no script ID to reference. .functionName = frame.getFunctionName(), }; } RuntimeSamplingProfile::SampleCallStackFrame convertSuspendHermesFrame( const fhsp::ProfileSampleCallStackSuspendFrame& frame) { if (frame.getSuspendFrameKind() == fhsp::ProfileSampleCallStackSuspendFrame::SuspendFrameKind::GC) { return RuntimeSamplingProfile::SampleCallStackFrame{ .kind = RuntimeSamplingProfile::SampleCallStackFrame::Kind:: GarbageCollector, .scriptId = FALLBACK_SCRIPT_ID, // GC frames are part of the VM, no // script ID to reference. .functionName = 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{ .kind = RuntimeSamplingProfile::SampleCallStackFrame::Kind::JSFunction, .scriptId = frame.getScriptId(), .functionName = frame.getFunctionName(), .scriptURL = frame.hasScriptUrl() ? std::optional<std::string_view>{frame.getScriptUrl()} : std::nullopt, .lineNumber = frame.hasFunctionLineNumber() ? std::optional<uint32_t>{frame.getFunctionLineNumber() - 1} // Hermes VM keeps line numbers as 1-based. Convert // to 0-based. : std::nullopt, .columnNumber = frame.hasFunctionColumnNumber() ? std::optional<uint32_t>{frame.getFunctionColumnNumber() - 1} // Hermes VM keeps column numbers as 1-based. Convert to // 0-based. : std::nullopt, }; } RuntimeSamplingProfile::Sample convertHermesSampleToTracingSample( const fhsp::ProfileSample& hermesSample) { uint64_t reconciledTimestamp = hermesSample.getTimestamp(); const auto callStackRange = hermesSample.getCallStackFramesRange(); std::vector<RuntimeSamplingProfile::SampleCallStackFrame> reconciledSampleCallStack; reconciledSampleCallStack.reserve(hermesSample.getCallStackFramesCount()); for (const auto& hermesFrame : callStackRange) { if (std::holds_alternative<fhsp::ProfileSampleCallStackSuspendFrame>( hermesFrame)) { const auto& suspendFrame = std::get<fhsp::ProfileSampleCallStackSuspendFrame>(hermesFrame); if (shouldIgnoreHermesFrame(suspendFrame)) { continue; } reconciledSampleCallStack.emplace_back( convertSuspendHermesFrame(suspendFrame)); } else if (std::holds_alternative< fhsp::ProfileSampleCallStackNativeFunctionFrame>( hermesFrame)) { const auto& nativeFunctionFrame = std::get<fhsp::ProfileSampleCallStackNativeFunctionFrame>( hermesFrame); reconciledSampleCallStack.emplace_back( convertNativeHermesFrame(nativeFunctionFrame)); } else if (std::holds_alternative< fhsp::ProfileSampleCallStackHostFunctionFrame>( hermesFrame)) { const auto& hostFunctionFrame = std::get<fhsp::ProfileSampleCallStackHostFunctionFrame>(hermesFrame); reconciledSampleCallStack.emplace_back( convertHostFunctionHermesFrame(hostFunctionFrame)); } else if (std::holds_alternative< fhsp::ProfileSampleCallStackJSFunctionFrame>(hermesFrame)) { const auto& jsFunctionFrame = std::get<fhsp::ProfileSampleCallStackJSFunctionFrame>(hermesFrame); reconciledSampleCallStack.emplace_back( convertJSFunctionHermesFrame(jsFunctionFrame)); } else { throw std::logic_error{"Unknown Hermes stack frame kind"}; } } return RuntimeSamplingProfile::Sample{ reconciledTimestamp, hermesSample.getThreadId(), std::move(reconciledSampleCallStack)}; } } // namespace /* static */ RuntimeSamplingProfile HermesRuntimeSamplingProfileSerializer::serializeToTracingSamplingProfile( hermes::sampling_profiler::Profile hermesProfile) { const auto samplesRange = hermesProfile.getSamplesRange(); std::vector<RuntimeSamplingProfile::Sample> reconciledSamples; reconciledSamples.reserve(hermesProfile.getSamplesCount()); for (const auto& hermesSample : samplesRange) { RuntimeSamplingProfile::Sample reconciledSample = convertHermesSampleToTracingSample(hermesSample); reconciledSamples.push_back(std::move(reconciledSample)); } return RuntimeSamplingProfile{ "Hermes", // Hermes' Profile should be the source of truth for this, // but it is safe to reuse the process ID here, since everything runs in // the same process. oscompat::getCurrentProcessId(), std::move(reconciledSamples), std::make_unique<RawHermesRuntimeProfile>(std::move(hermesProfile))}; } } // namespace facebook::react::jsinspector_modern::tracing