Hermes Inspector (#7322)
* Hermes Inspector Integration redesign * Improving the change file text * Resolving review comments * Reverting the unintentional change to enable hermes by default and some formatting fixes * Fixing x86 builds by ensuring the right calling convention prefix with inspector APIs * Fixing issues in instance reload * Fixing last incomplete commit by uploading JSEngine.props * yarn format * Some cleanups * yarn format * Adding markdown documentation * Reporting bundle download status to package and resolving review feedbacks * Fixing build
This commit is contained in:
Родитель
5ebc3efbcf
Коммит
945ccd4ae2
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"type": "prerelease",
|
||||
"comment": "Enabling Hermes Inspector",
|
||||
"packageName": "react-native-windows",
|
||||
"email": "anandrag@microsoft.com",
|
||||
"dependentChangeType": "patch"
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
# Hermes Inspector
|
||||
|
||||
*React Native for Windows* with *Hermes engine* now supports direct JavaScript runtime inspection using tools such as Chrome or Edge devtools, VSCode debugger, Flipper etc. by implementing an in-process Chrome Debug Protocol server.
|
||||
Please note that it is fundamentally different from "Remote JS Debugging", which loads the JavaScript bundle into a remote Chrome browser session with duplex communication over IPC channels.
|
||||
|
||||
We share the implementation (code and design) with other platforms wherever possible. All the external endpoints, APIs and protocols should be identical to *React Native* environments on other platforms.
|
||||
Hence, we expect most tooling available on other platforms to just work on Windows. But, as of now, we have tested only with Chrome and Edge devtools.
|
||||
|
||||
## Steps to enable direct debugging
|
||||
|
||||
1. Initialize React Native Host,
|
||||
- Turn on `DeveloperSupport`
|
||||
- Turn on `FastRefresh`
|
||||
- Turn off `WebDebugger`
|
||||
- Turn on `Direct Debugging`
|
||||
2. Ensure Dev-Server is running
|
||||
3. Start the application
|
||||
|
||||
After the app has booted,
|
||||
|
||||
4. Navigate to `edge://inspect` in Edge browser or `chrome://inspect` in Chrome browser
|
||||
5. Enable **Discover network targets** and **configure** the target discovery settings to include `localhost:8081` (or wherever the metro server is running)
|
||||
6. Within a few seconds "Hermes React Native" should appear on the page as a remote target
|
||||
7. Click on the **inspect** link to launch the devtools page
|
||||
8. Click `Ctrl+P` to open source files and set break points
|
||||
9. Alternatively, you can insert `debugger` statements in source code to break on specific locations
|
||||
|
||||
In order to break on locations during boot, you can either
|
||||
|
||||
- `debugger` statements during boot sequence will pause the runtime waiting for debugger to connect
|
||||
- Set break point and refresh the bundle through the Dev Server. The runtime will wait for debugger to attach.
|
||||
|
||||
## Steps to enable heap profiling
|
||||
|
||||
Follow steps 1-7 from above, and then
|
||||
1. Click on the "Memory" tab in the inspector
|
||||
2. Heap snapshots and instrumented allocations with callstacks should be working.
|
||||
|
||||
## Enable debugging/profiling on release builds
|
||||
|
||||
We keep the inspector turned off on release builds by default. If you want to debug or profile release builds, set the MSBuild property `EnableHermesInspectorInReleaseFlavor` to `'true'` when building the platform.
|
||||
|
||||
## Known Issues
|
||||
|
||||
1. CPU Sampling profiler current don't work
|
|
@ -2,5 +2,5 @@
|
|||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.210312.4" targetFramework="native" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.5.0" targetFramework="native" />
|
||||
<package id="ReactNative.Hermes.Windows" version="0.7.2-microsoft.5" targetFramework="native" />
|
||||
<package id="ReactNative.Hermes.Windows" version="0.7.2-microsoft.11" targetFramework="native" />
|
||||
</packages>
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
<packages>
|
||||
<package id="Microsoft.VCRTForwarders.140" version="1.0.2-rc" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.210312.4" targetFramework="native" />
|
||||
<package id="ReactNative.Hermes.Windows" version="0.7.2-microsoft.5" targetFramework="native" />
|
||||
<package id="ReactNative.Hermes.Windows" version="0.7.2-microsoft.11" targetFramework="native" />
|
||||
</packages>
|
|
@ -186,7 +186,7 @@
|
|||
</Target>
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\packages\Microsoft.Windows.CppWinRT.2.0.210312.4\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.210312.4\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="..\packages\ReactNative.Hermes.Windows.0.7.2-microsoft.5\build\native\ReactNative.Hermes.Windows.targets" Condition="Exists('..\packages\ReactNative.Hermes.Windows.0.7.2-microsoft.5\build\native\ReactNative.Hermes.Windows.targets')" />
|
||||
<Import Project="$(HermesPackage)\build\native\ReactNative.Hermes.Windows.targets" Condition="Exists('$(HermesPackage)\build\native\ReactNative.Hermes.Windows.targets')" />
|
||||
<Import Project="..\packages\$(WinUIPackageName).$(WinUIPackageVersion)\build\native\$(WinUIPackageName).targets" Condition="Exists('..\packages\$(WinUIPackageName).$(WinUIPackageVersion)\build\native\$(WinUIPackageName).targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
|
@ -195,7 +195,7 @@
|
|||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.210312.4\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Windows.CppWinRT.2.0.210312.4\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.210312.4\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Windows.CppWinRT.2.0.210312.4\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\ReactNative.Hermes.Windows.0.7.2-microsoft.5\build\native\ReactNative.Hermes.Windows.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(HermesPackage)\build\native\ReactNative.Hermes.Windows.targets'))" />
|
||||
<Error Condition="!Exists('$(HermesPackage)\build\native\ReactNative.Hermes.Windows.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(HermesPackage)\build\native\ReactNative.Hermes.Windows.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\$(WinUIPackageName).$(WinUIPackageVersion)\build\native\$(WinUIPackageName).targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\$(WinUIPackageName).$(WinUIPackageVersion)\build\native\$(WinUIPackageName).targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.210312.4" targetFramework="native"/>
|
||||
<package id="Microsoft.UI.Xaml" version="2.5.0" targetFramework="native"/>
|
||||
<package id="ReactNative.Hermes.Windows" version="0.7.2-microsoft.5" targetFramework="native"/>
|
||||
<package id="ReactNative.Hermes.Windows" version="0.7.2-microsoft.11" targetFramework="native"/>
|
||||
</packages>
|
|
@ -401,7 +401,8 @@ void ReactInstanceWin::Initialize() noexcept {
|
|||
switch (m_options.JsiEngine) {
|
||||
case JSIEngine::Hermes:
|
||||
#if defined(USE_HERMES)
|
||||
devSettings->jsiRuntimeHolder = std::make_shared<facebook::react::HermesRuntimeHolder>();
|
||||
devSettings->jsiRuntimeHolder =
|
||||
std::make_shared<facebook::react::HermesRuntimeHolder>(devSettings, m_jsMessageThread.Load());
|
||||
devSettings->inlineSourceMap = false;
|
||||
break;
|
||||
#endif
|
||||
|
|
|
@ -7,6 +7,6 @@
|
|||
<package id="Microsoft.UI.Xaml" version="2.5.0" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.210312.4" targetFramework="native" />
|
||||
<package id="Microsoft.WinUI" version="3.0.0-preview4.210210.4" targetFramework="native" />
|
||||
<package id="ReactNative.Hermes.Windows" version="0.7.2-microsoft.5" targetFramework="native" />
|
||||
<package id="ReactNative.Hermes.Windows" version="0.7.2-microsoft.11" targetFramework="native" />
|
||||
<!-- package id="ReactNative.V8Jsi.Windows.UWP" version="0.64.8" targetFramework="native" / -->
|
||||
</packages>
|
|
@ -3,10 +3,11 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<UseHermes Condition="'$(UseHermes)' == ''">false</UseHermes>
|
||||
<HermesVersion Condition="'$(HermesVersion)' == ''">0.7.2-microsoft.5</HermesVersion>
|
||||
<HermesVersion Condition="'$(HermesVersion)' == ''">0.7.2-microsoft.11</HermesVersion>
|
||||
<HermesPackage Condition="'$(HermesPackage)' == ''">$(SolutionDir)packages\ReactNative.Hermes.Windows.$(HermesVersion)</HermesPackage>
|
||||
<!-- TODO: Can we automatically distinguish between uwp and win32 here? -->
|
||||
<HermesArch Condition="'$(HermesArch)' == ''">uwp</HermesArch>
|
||||
<EnableHermesInspectorInReleaseFlavor Condition="'$(EnableHermesInspectorInReleaseFlavor)' == ''">false</EnableHermesInspectorInReleaseFlavor>
|
||||
|
||||
<UseV8 Condition="'$(UseV8)' == ''">false</UseV8>
|
||||
<V8Version Condition="'$(V8Version)' == ''">0.64.8</V8Version>
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="$(SolutionDir)\ExperimentalFeatures.props" Condition="Exists('$(SolutionDir)\ExperimentalFeatures.props')" />
|
||||
<Import Project="$(ReactNativeWindowsDir)PropertySheets\React.Cpp.props" />
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|ARM64">
|
||||
|
@ -85,6 +86,7 @@
|
|||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="$(SolutionDir)packages\boost.1.72.0.0\build\boost.targets" Condition="Exists('$(SolutionDir)packages\boost.1.72.0.0\build\boost.targets')" />
|
||||
<Import Project="$(HermesPackage)\build\native\ReactNative.Hermes.Windows.targets" Condition="Exists('$(HermesPackage)\build\native\ReactNative.Hermes.Windows.targets') AND '$(UseHermes)' == 'true'" />
|
||||
</ImportGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="$(ReactNativeDir)\ReactCommon\jsi\jsi\instrumentation.h" />
|
||||
|
|
|
@ -59,6 +59,18 @@ class DevServerHelper {
|
|||
PackagerOpenStackFrameUrlFormat, GetDeviceLocalHost(sourceBundleHost, sourceBundlePort).c_str());
|
||||
}
|
||||
|
||||
static std::string get_InspectorDeviceUrl(
|
||||
const std::string &packagerHost,
|
||||
const uint16_t packagerPort,
|
||||
const std::string &deviceName,
|
||||
const std::string &packageName) {
|
||||
return string_format(
|
||||
InspectorDeviceUrlFormat,
|
||||
GetDeviceLocalHost(packagerHost, packagerPort).c_str(),
|
||||
deviceName.c_str(),
|
||||
packageName.c_str());
|
||||
}
|
||||
|
||||
static constexpr const char DefaultPackagerHost[] = "localhost";
|
||||
static const uint16_t DefaultPackagerPort = 8081;
|
||||
|
||||
|
@ -79,6 +91,7 @@ class DevServerHelper {
|
|||
static constexpr const char PackagerConnectionUrlFormat[] = "ws://%s/message";
|
||||
static constexpr const char PackagerStatusUrlFormat[] = "http://%s/status";
|
||||
static constexpr const char PackagerOpenStackFrameUrlFormat[] = "https://%s/open-stack-frame";
|
||||
static constexpr const char InspectorDeviceUrlFormat[] = "ws://%s/inspector/device?name=%s&app=%s";
|
||||
|
||||
static constexpr const char PackagerOkStatus[] = "packager-status:running";
|
||||
const int LongPollFailureDelayMs = 5000;
|
||||
|
|
|
@ -21,6 +21,11 @@
|
|||
#include <winrt/Windows.Web.Http.Headers.h>
|
||||
#include <winrt/Windows.Web.Http.h>
|
||||
|
||||
#ifdef HERMES_ENABLE_DEBUGGER
|
||||
#include <winrt/Windows.ApplicationModel.Activation.h>
|
||||
#include <winrt/Windows.Networking.Connectivity.h>
|
||||
#endif
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4068 4251 4101 4804 4309)
|
||||
#include <cxxreact/JSExecutor.h>
|
||||
|
@ -232,6 +237,43 @@ void DevSupportManager::StopPollingLiveReload() {
|
|||
m_cancellation_token = true;
|
||||
}
|
||||
|
||||
void DevSupportManager::StartInspector(
|
||||
[[maybe_unused]] const std::string &packagerHost,
|
||||
[[maybe_unused]] const uint16_t packagerPort) noexcept {
|
||||
#ifdef HERMES_ENABLE_DEBUGGER
|
||||
std::string packageName("RNW");
|
||||
if (auto currentPackage = winrt::Windows::ApplicationModel::Package::Current()) {
|
||||
packageName = winrt::to_string(currentPackage.DisplayName());
|
||||
}
|
||||
|
||||
std::string deviceName("RNWHost");
|
||||
auto hostNames = winrt::Windows::Networking::Connectivity::NetworkInformation::GetHostNames();
|
||||
if (hostNames && hostNames.First() && hostNames.First().Current()) {
|
||||
deviceName = winrt::to_string(hostNames.First().Current().DisplayName());
|
||||
}
|
||||
|
||||
m_inspectorPackagerConnection = std::make_shared<InspectorPackagerConnection>(
|
||||
facebook::react::DevServerHelper::get_InspectorDeviceUrl(packagerHost, packagerPort, deviceName, packageName),
|
||||
m_BundleStatusProvider);
|
||||
m_inspectorPackagerConnection->connectAsync();
|
||||
#endif
|
||||
}
|
||||
|
||||
void DevSupportManager::StopInspector() noexcept {
|
||||
#ifdef HERMES_ENABLE_DEBUGGER
|
||||
if (m_inspectorPackagerConnection) {
|
||||
m_inspectorPackagerConnection->disconnectAsync();
|
||||
m_inspectorPackagerConnection = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void DevSupportManager::UpdateBundleStatus(bool isLastDownloadSucess, int64_t updateTimestamp) noexcept {
|
||||
#ifdef HERMES_ENABLE_DEBUGGER
|
||||
m_BundleStatusProvider->updateBundleStatus(isLastDownloadSucess, updateTimestamp);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::pair<std::string, bool> GetJavaScriptFromServer(
|
||||
const std::string &sourceBundleHost,
|
||||
const uint16_t sourceBundlePort,
|
||||
|
|
|
@ -14,6 +14,10 @@
|
|||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#if defined(HERMES_ENABLE_DEBUGGER)
|
||||
#include <InspectorPackagerConnection.h>
|
||||
#endif
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
struct DevSettings;
|
||||
|
@ -43,8 +47,31 @@ class DevSupportManager final : public facebook::react::IDevSupportManager {
|
|||
std::function<void()> onChangeCallback) override;
|
||||
virtual void StopPollingLiveReload() override;
|
||||
|
||||
virtual void StartInspector(const std::string &packagerHost, const uint16_t packagerPort) noexcept override;
|
||||
virtual void StopInspector() noexcept override;
|
||||
virtual void UpdateBundleStatus(bool isLastDownloadSucess, int64_t updateTimestamp) noexcept override;
|
||||
|
||||
private:
|
||||
std::atomic_bool m_cancellation_token;
|
||||
|
||||
#if defined(HERMES_ENABLE_DEBUGGER)
|
||||
std::shared_ptr<InspectorPackagerConnection> m_inspectorPackagerConnection;
|
||||
|
||||
struct BundleStatusProvider : public InspectorPackagerConnection::IBundleStatusProvider {
|
||||
virtual InspectorPackagerConnection::BundleStatus getBundleStatus() {
|
||||
return m_bundleStatus;
|
||||
}
|
||||
|
||||
void updateBundleStatus(bool isLastDownloadSucess, int64_t updateTimestamp) {
|
||||
m_bundleStatus.m_isLastDownloadSucess = isLastDownloadSucess;
|
||||
m_bundleStatus.m_updateTimestamp = updateTimestamp;
|
||||
}
|
||||
|
||||
private:
|
||||
InspectorPackagerConnection::BundleStatus m_bundleStatus;
|
||||
};
|
||||
std::shared_ptr<BundleStatusProvider> m_BundleStatusProvider = std::make_shared<BundleStatusProvider>();
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace Microsoft::ReactNative
|
||||
|
|
|
@ -3,31 +3,108 @@
|
|||
|
||||
#include "pch.h"
|
||||
|
||||
#include <hermes/hermes.h>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include <JSI/decorator.h>
|
||||
#include <cxxreact/MessageQueueThread.h>
|
||||
#include <cxxreact/SystraceSection.h>
|
||||
#include <hermes/hermes.h>
|
||||
#include "HermesRuntimeHolder.h"
|
||||
|
||||
#if defined(HERMES_ENABLE_DEBUGGER)
|
||||
#include <hermes/inspector/chrome/Registration.h>
|
||||
#endif
|
||||
|
||||
using namespace facebook;
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
std::shared_ptr<jsi::Runtime> HermesRuntimeHolder::getRuntime() noexcept {
|
||||
std::call_once(once_flag_, [this]() { initRuntime(); });
|
||||
namespace {
|
||||
|
||||
if (!runtime_)
|
||||
std::terminate();
|
||||
|
||||
// ChakraJsiRuntime is not thread safe as of now.
|
||||
if (own_thread_id_ != std::this_thread::get_id())
|
||||
std::terminate();
|
||||
|
||||
return runtime_;
|
||||
std::unique_ptr<facebook::hermes::HermesRuntime> makeHermesRuntimeSystraced(
|
||||
const ::hermes::vm::RuntimeConfig &runtimeConfig) {
|
||||
SystraceSection s("HermesExecutorFactory::makeHermesRuntimeSystraced");
|
||||
return hermes::makeHermesRuntime(runtimeConfig);
|
||||
}
|
||||
|
||||
#ifdef HERMES_ENABLE_DEBUGGER
|
||||
class HermesExecutorRuntimeAdapter final : public facebook::hermes::inspector::RuntimeAdapter {
|
||||
public:
|
||||
HermesExecutorRuntimeAdapter(
|
||||
std::shared_ptr<jsi::Runtime> runtime,
|
||||
facebook::hermes::HermesRuntime &hermesRuntime,
|
||||
std::shared_ptr<MessageQueueThread> thread)
|
||||
: m_runtime(runtime), m_hermesRuntime(hermesRuntime), m_thread(std::move(thread)) {}
|
||||
|
||||
virtual ~HermesExecutorRuntimeAdapter() = default;
|
||||
|
||||
jsi::Runtime &getRuntime() override {
|
||||
return *m_runtime;
|
||||
}
|
||||
|
||||
facebook::hermes::debugger::Debugger &getDebugger() override {
|
||||
return m_hermesRuntime.getDebugger();
|
||||
}
|
||||
|
||||
void tickleJs() override {
|
||||
// The queue will ensure that runtime_ is still valid when this
|
||||
// gets invoked.
|
||||
m_thread->runOnQueue([&runtime = m_runtime]() {
|
||||
auto func = runtime->global().getPropertyAsFunction(*runtime, "__tickleJs");
|
||||
func.call(*runtime);
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<jsi::Runtime> m_runtime;
|
||||
facebook::hermes::HermesRuntime &m_hermesRuntime;
|
||||
|
||||
std::shared_ptr<MessageQueueThread> m_thread;
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
std::shared_ptr<jsi::Runtime> HermesRuntimeHolder::getRuntime() noexcept {
|
||||
std::call_once(m_once_flag, [this]() { initRuntime(); });
|
||||
|
||||
if (!m_runtime)
|
||||
std::terminate();
|
||||
|
||||
// Make sure that the runtime instance is not consumed from multiple threads.
|
||||
if (m_own_thread_id != std::this_thread::get_id())
|
||||
std::terminate();
|
||||
|
||||
return m_runtime;
|
||||
}
|
||||
|
||||
HermesRuntimeHolder::HermesRuntimeHolder(
|
||||
std::shared_ptr<facebook::react::DevSettings> devSettings,
|
||||
std::shared_ptr<facebook::react::MessageQueueThread> jsQueue) noexcept
|
||||
: m_devSettings(std::move(devSettings)), m_jsQueue(std::move(jsQueue)) {}
|
||||
|
||||
void HermesRuntimeHolder::initRuntime() noexcept {
|
||||
runtime_ = facebook::hermes::makeHermesRuntime();
|
||||
own_thread_id_ = std::this_thread::get_id();
|
||||
auto runtimeConfig = ::hermes::vm::RuntimeConfig();
|
||||
auto hermesRuntime = makeHermesRuntimeSystraced(runtimeConfig);
|
||||
facebook::hermes::HermesRuntime &hermesRuntimeRef = *hermesRuntime;
|
||||
|
||||
m_runtime = std::move(hermesRuntime);
|
||||
m_own_thread_id = std::this_thread::get_id();
|
||||
|
||||
#ifdef HERMES_ENABLE_DEBUGGER
|
||||
if (m_devSettings->useDirectDebugger) {
|
||||
auto adapter = std::make_unique<HermesExecutorRuntimeAdapter>(m_runtime, hermesRuntimeRef, m_jsQueue);
|
||||
facebook::hermes::inspector::chrome::enableDebugging(std::move(adapter), "Hermes React Native");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Add js engine information to Error.prototype so in error reporting we
|
||||
// can send this information.
|
||||
auto errorPrototype =
|
||||
m_runtime->global().getPropertyAsObject(*m_runtime, "Error").getPropertyAsObject(*m_runtime, "prototype");
|
||||
errorPrototype.setProperty(*m_runtime, "jsEngine", "hermes");
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include <jsi/jsi.h>
|
||||
#include <thread>
|
||||
|
||||
#include <DevSettings.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
|
@ -14,12 +16,19 @@ class HermesRuntimeHolder : public facebook::jsi::RuntimeHolderLazyInit {
|
|||
public:
|
||||
std::shared_ptr<facebook::jsi::Runtime> getRuntime() noexcept override;
|
||||
|
||||
HermesRuntimeHolder(
|
||||
std::shared_ptr<facebook::react::DevSettings> devSettings,
|
||||
std::shared_ptr<facebook::react::MessageQueueThread> jsQueue) noexcept;
|
||||
|
||||
private:
|
||||
void initRuntime() noexcept;
|
||||
std::shared_ptr<facebook::jsi::Runtime> runtime_;
|
||||
std::shared_ptr<facebook::jsi::Runtime> m_runtime;
|
||||
|
||||
std::once_flag once_flag_;
|
||||
std::thread::id own_thread_id_;
|
||||
std::once_flag m_once_flag;
|
||||
std::thread::id m_own_thread_id;
|
||||
|
||||
std::shared_ptr<facebook::react::DevSettings> m_devSettings;
|
||||
std::shared_ptr<facebook::react::MessageQueueThread> m_jsQueue;
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
|
|
|
@ -21,6 +21,10 @@ struct IDevSupportManager {
|
|||
const uint16_t sourceBundlePort,
|
||||
std::function<void()> onChangeCallback) = 0;
|
||||
virtual void StopPollingLiveReload() = 0;
|
||||
|
||||
virtual void StartInspector(const std::string &packagerHost, const uint16_t packagerPort) noexcept = 0;
|
||||
virtual void StopInspector() noexcept = 0;
|
||||
virtual void UpdateBundleStatus(bool isLastDownloadSucess, int64_t updateTimestamp) noexcept = 0;
|
||||
};
|
||||
|
||||
std::shared_ptr<IDevSupportManager> CreateDevSupportManager();
|
||||
|
|
|
@ -0,0 +1,240 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#ifdef HERMES_ENABLE_DEBUGGER
|
||||
|
||||
#include <folly/json.h>
|
||||
#include <tracing/tracing.h>
|
||||
#include "InspectorPackagerConnection.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
IDestructible::~IDestructible() {}
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
|
||||
namespace Microsoft::ReactNative {
|
||||
|
||||
namespace {
|
||||
|
||||
struct InspectorProtocol {
|
||||
static constexpr std::string_view Message_PAGEID = "pageId";
|
||||
static constexpr std::string_view Message_PAYLOAD = "payload";
|
||||
|
||||
static constexpr std::string_view Message_eventName_wrappedEvent = "wrappedEvent";
|
||||
static constexpr std::string_view Message_eventName_getPages = "getPages";
|
||||
static constexpr std::string_view Message_eventName_connect = "connect";
|
||||
static constexpr std::string_view Message_eventName_disconnect = "disconnect";
|
||||
|
||||
static constexpr std::string_view Message_EVENT = "event";
|
||||
|
||||
enum class EventType { GetPages, WrappedEvent, Connect, Disconnect };
|
||||
|
||||
static EventType getEventType(const folly::dynamic &messageFromPackager) {
|
||||
std::string event = messageFromPackager.at(InspectorProtocol::Message_EVENT).getString();
|
||||
if (event == Message_eventName_getPages) {
|
||||
return EventType::GetPages;
|
||||
}
|
||||
|
||||
if (event == Message_eventName_wrappedEvent) {
|
||||
return EventType::WrappedEvent;
|
||||
}
|
||||
|
||||
if (event == Message_eventName_connect) {
|
||||
return EventType::Connect;
|
||||
}
|
||||
|
||||
if (event == Message_eventName_disconnect) {
|
||||
return EventType::Disconnect;
|
||||
}
|
||||
|
||||
assert(false && "Unknown event!");
|
||||
std::abort();
|
||||
}
|
||||
|
||||
static folly::dynamic constructResponseForPackager(EventType eventType, folly::dynamic &&payload) {
|
||||
folly::dynamic response = folly::dynamic::object;
|
||||
|
||||
switch (eventType) {
|
||||
case EventType::GetPages:
|
||||
response[InspectorProtocol::Message_EVENT] = InspectorProtocol::Message_eventName_getPages;
|
||||
break;
|
||||
case EventType::WrappedEvent:
|
||||
response[InspectorProtocol::Message_EVENT] = InspectorProtocol::Message_eventName_wrappedEvent;
|
||||
break;
|
||||
case EventType::Connect:
|
||||
response[InspectorProtocol::Message_EVENT] = InspectorProtocol::Message_eventName_connect;
|
||||
break;
|
||||
case EventType::Disconnect:
|
||||
response[InspectorProtocol::Message_EVENT] = InspectorProtocol::Message_eventName_disconnect;
|
||||
break;
|
||||
default:
|
||||
assert(false && "Unknown event Type.");
|
||||
std::abort();
|
||||
}
|
||||
|
||||
response[InspectorProtocol::Message_PAYLOAD] = std::move(payload);
|
||||
return response;
|
||||
}
|
||||
|
||||
static folly::dynamic constructGetPagesResponsePayloadForPackager(
|
||||
const std::vector<facebook::react::InspectorPage> &pages,
|
||||
InspectorPackagerConnection::BundleStatus bundleStatus) {
|
||||
folly::dynamic payload = folly::dynamic::array;
|
||||
for (const facebook::react::InspectorPage &page : pages) {
|
||||
folly::dynamic pageDyn = folly::dynamic::object;
|
||||
pageDyn["id"] = page.id;
|
||||
pageDyn["title"] = page.title;
|
||||
pageDyn["vm"] = page.vm;
|
||||
|
||||
pageDyn["isLastBundleDownloadSuccess"] = bundleStatus.m_isLastDownloadSucess;
|
||||
pageDyn["bundleUpdateTimestamp"] = bundleStatus.m_updateTimestamp;
|
||||
|
||||
payload.push_back(pageDyn);
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
static folly::dynamic constructVMResponsePayloadForPackager(int64_t pageId, std::string &&messageFromVM) {
|
||||
folly::dynamic payload = folly::dynamic::object;
|
||||
payload[InspectorProtocol::Message_eventName_wrappedEvent] = messageFromVM;
|
||||
payload[InspectorProtocol::Message_PAGEID] = pageId;
|
||||
return payload;
|
||||
}
|
||||
|
||||
static folly::dynamic constructVMResponsePayloadOnDisconnectForPackager(int64_t pageId) {
|
||||
folly::dynamic payload = folly::dynamic::object;
|
||||
payload[InspectorProtocol::Message_PAGEID] = pageId;
|
||||
return payload;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
RemoteConnection::RemoteConnection(int64_t pageId, const InspectorPackagerConnection &packagerConnection)
|
||||
: m_packagerConnection(packagerConnection), m_pageId(pageId) {}
|
||||
|
||||
void RemoteConnection::onMessage(std::string message) {
|
||||
folly::dynamic response = InspectorProtocol::constructResponseForPackager(
|
||||
InspectorProtocol::EventType::WrappedEvent,
|
||||
InspectorProtocol::constructVMResponsePayloadForPackager(m_pageId, std::move(message)));
|
||||
std::string responsestr = folly::toJson(response);
|
||||
m_packagerConnection.sendMessageToPackager(std::move(responsestr));
|
||||
}
|
||||
|
||||
void RemoteConnection::onDisconnect() {
|
||||
folly::dynamic response = InspectorProtocol::constructResponseForPackager(
|
||||
InspectorProtocol::EventType::Disconnect,
|
||||
InspectorProtocol::constructVMResponsePayloadOnDisconnectForPackager(m_pageId));
|
||||
|
||||
std::string responsestr = folly::toJson(response);
|
||||
m_packagerConnection.sendMessageToPackager(std::move(responsestr));
|
||||
}
|
||||
|
||||
winrt::fire_and_forget InspectorPackagerConnection::sendMessageToPackagerAsync(std::string &&message) const {
|
||||
std::string message_(std::move(message));
|
||||
co_await winrt::resume_background();
|
||||
m_packagerWebSocketConnection->Send(std::move(message_));
|
||||
co_return;
|
||||
}
|
||||
|
||||
void InspectorPackagerConnection::sendMessageToPackager(std::string &&message) const {
|
||||
sendMessageToPackagerAsync(std::move(message));
|
||||
}
|
||||
|
||||
void InspectorPackagerConnection::sendMessageToVM(int64_t pageId, std::string &&message) {
|
||||
m_localConnections[pageId]->sendMessage(std::move(message));
|
||||
}
|
||||
|
||||
InspectorPackagerConnection::InspectorPackagerConnection(
|
||||
std::string &&url,
|
||||
std::shared_ptr<IBundleStatusProvider> bundleStatusProvider)
|
||||
: m_url(std::move(url)), m_bundleStatusProvider(std::move(bundleStatusProvider)) {}
|
||||
|
||||
winrt::fire_and_forget InspectorPackagerConnection::disconnectAsync() {
|
||||
co_await winrt::resume_background();
|
||||
std::string reason("Explicit close");
|
||||
m_packagerWebSocketConnection->Close(Microsoft::React::WinRTWebSocketResource::CloseCode::GoingAway, reason);
|
||||
co_return;
|
||||
}
|
||||
|
||||
winrt::fire_and_forget InspectorPackagerConnection::connectAsync() {
|
||||
co_await winrt::resume_background();
|
||||
|
||||
std::vector<winrt::Windows::Security::Cryptography::Certificates::ChainValidationResult> certExceptions;
|
||||
m_packagerWebSocketConnection =
|
||||
std::make_shared<Microsoft::React::WinRTWebSocketResource>(m_url, std::move(certExceptions));
|
||||
|
||||
m_packagerWebSocketConnection->SetOnError([](const Microsoft::React::IWebSocketResource::Error &err) {
|
||||
facebook::react::tracing::error(err.Message.c_str());
|
||||
});
|
||||
|
||||
m_packagerWebSocketConnection->SetOnConnect(
|
||||
[]() { facebook::react::tracing::log("Inspector: Websocket connection succeeded."); });
|
||||
|
||||
m_packagerWebSocketConnection->SetOnMessage([self = shared_from_this()](
|
||||
size_t /*length*/, const std::string &message, bool isBinary) {
|
||||
assert(!isBinary && "We don't expect any binary messages !");
|
||||
folly::dynamic messageDyn = folly::parseJson(message);
|
||||
|
||||
InspectorProtocol::EventType eventType = InspectorProtocol::getEventType(messageDyn);
|
||||
switch (eventType) {
|
||||
case InspectorProtocol::EventType::GetPages: {
|
||||
std::vector<facebook::react::InspectorPage> inspetorPages = facebook::react::getInspectorInstance().getPages();
|
||||
folly::dynamic response = InspectorProtocol::constructResponseForPackager(
|
||||
InspectorProtocol::EventType::GetPages,
|
||||
InspectorProtocol::constructGetPagesResponsePayloadForPackager(
|
||||
inspetorPages, self->m_bundleStatusProvider->getBundleStatus()));
|
||||
|
||||
std::string responsestr = folly::toJson(response);
|
||||
self->sendMessageToPackager(std::move(responsestr));
|
||||
} break;
|
||||
|
||||
case InspectorProtocol::EventType::WrappedEvent: {
|
||||
folly::dynamic payload = messageDyn[InspectorProtocol::Message_PAYLOAD];
|
||||
int64_t pageId = payload[InspectorProtocol::Message_PAGEID].asInt();
|
||||
|
||||
if (self->m_localConnections.find(pageId) == self->m_localConnections.end()) {
|
||||
break;
|
||||
}
|
||||
|
||||
std::string wrappedEvent = payload[InspectorProtocol::Message_eventName_wrappedEvent].getString();
|
||||
self->sendMessageToVM(pageId, std::move(wrappedEvent));
|
||||
} break;
|
||||
|
||||
case InspectorProtocol::EventType::Connect: {
|
||||
folly::dynamic payload = messageDyn[InspectorProtocol::Message_PAYLOAD];
|
||||
int64_t pageId = payload[InspectorProtocol::Message_PAGEID].asInt();
|
||||
|
||||
if (self->m_localConnections.find(pageId) != self->m_localConnections.end()) {
|
||||
break;
|
||||
}
|
||||
|
||||
self->m_localConnections[pageId] = facebook::react::getInspectorInstance().connect(
|
||||
static_cast<int>(pageId), std::make_unique<RemoteConnection>(pageId, *self));
|
||||
} break;
|
||||
|
||||
case InspectorProtocol::EventType::Disconnect: {
|
||||
folly::dynamic payload = messageDyn[InspectorProtocol::Message_PAYLOAD];
|
||||
int64_t pageId = payload[InspectorProtocol::Message_PAGEID].asInt();
|
||||
if (self->m_localConnections.find(pageId) != self->m_localConnections.end()) {
|
||||
self->m_localConnections[pageId]->disconnect();
|
||||
self->m_localConnections.erase(pageId);
|
||||
}
|
||||
|
||||
} break;
|
||||
}
|
||||
});
|
||||
|
||||
Microsoft::React::IWebSocketResource::Protocols protocols;
|
||||
Microsoft::React::IWebSocketResource::Options options;
|
||||
m_packagerWebSocketConnection->Connect(protocols, options);
|
||||
|
||||
co_return;
|
||||
}
|
||||
|
||||
} // namespace Microsoft::ReactNative
|
||||
|
||||
#endif
|
|
@ -0,0 +1,58 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <WinRTWebSocketResource.h>
|
||||
#include <jsinspector/InspectorInterfaces.h>
|
||||
|
||||
namespace Microsoft::ReactNative {
|
||||
|
||||
class InspectorPackagerConnection final : public std::enable_shared_from_this<InspectorPackagerConnection> {
|
||||
public:
|
||||
winrt::fire_and_forget connectAsync();
|
||||
winrt::fire_and_forget disconnectAsync();
|
||||
|
||||
class BundleStatus {
|
||||
public:
|
||||
bool m_isLastDownloadSucess;
|
||||
int64_t m_updateTimestamp = -1;
|
||||
|
||||
BundleStatus(bool isLastDownloadSucess, long updateTimestamp)
|
||||
: m_isLastDownloadSucess(isLastDownloadSucess), m_updateTimestamp(updateTimestamp) {}
|
||||
BundleStatus() : m_isLastDownloadSucess(false), m_updateTimestamp(-1) {}
|
||||
};
|
||||
|
||||
struct IBundleStatusProvider {
|
||||
virtual BundleStatus getBundleStatus() = 0;
|
||||
};
|
||||
|
||||
InspectorPackagerConnection(std::string &&url, std::shared_ptr<IBundleStatusProvider> bundleStatusProvider);
|
||||
|
||||
private:
|
||||
friend class RemoteConnection;
|
||||
|
||||
winrt::fire_and_forget sendMessageToPackagerAsync(std::string &&message) const;
|
||||
void sendMessageToPackager(std::string &&message) const;
|
||||
|
||||
// Note:: VM side Inspector processes the messages asynchronousely in a sequential executor with dedicated thread.
|
||||
// Hence, we don't bother invoking the inspector asynchronously.
|
||||
void sendMessageToVM(int64_t pageId, std::string &&message);
|
||||
|
||||
std::unordered_map<int64_t, std::unique_ptr<facebook::react::ILocalConnection>> m_localConnections;
|
||||
std::shared_ptr<Microsoft::React::WinRTWebSocketResource> m_packagerWebSocketConnection;
|
||||
std::shared_ptr<IBundleStatusProvider> m_bundleStatusProvider;
|
||||
std::string m_url;
|
||||
};
|
||||
|
||||
class RemoteConnection final : public facebook::react::IRemoteConnection {
|
||||
public:
|
||||
RemoteConnection(int64_t pageId, const InspectorPackagerConnection &packagerConnection);
|
||||
void onMessage(std::string message) override;
|
||||
void onDisconnect() override;
|
||||
|
||||
private:
|
||||
int64_t m_pageId;
|
||||
const InspectorPackagerConnection &m_packagerConnection;
|
||||
};
|
||||
} // namespace Microsoft::ReactNative
|
|
@ -53,14 +53,9 @@
|
|||
#include <ReactCommon/TurboModuleBinding.h>
|
||||
#include "ChakraRuntimeHolder.h"
|
||||
|
||||
#include <tracing/tracing.h>
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// forward declaration.
|
||||
namespace facebook::react::tracing {
|
||||
void initializeETW();
|
||||
void initializeJSHooks(facebook::jsi::Runtime &runtime);
|
||||
} // namespace facebook::react::tracing
|
||||
|
||||
namespace {
|
||||
|
||||
#if (defined(_MSC_VER) && !defined(WINRT))
|
||||
|
@ -297,6 +292,10 @@ InstanceImpl::InstanceImpl(
|
|||
facebook::react::tracing::initializeETW();
|
||||
#endif
|
||||
|
||||
if (m_devSettings->useDirectDebugger && !m_devSettings->useWebDebugger) {
|
||||
m_devManager->StartInspector(m_devSettings->sourceBundleHost, m_devSettings->sourceBundlePort);
|
||||
}
|
||||
|
||||
// Default (common) NativeModules
|
||||
auto modules = GetDefaultNativeModules(nativeQueue);
|
||||
|
||||
|
@ -345,7 +344,7 @@ InstanceImpl::InstanceImpl(
|
|||
switch (m_devSettings->jsiEngineOverride) {
|
||||
case JSIEngineOverride::Hermes:
|
||||
#if defined(USE_HERMES)
|
||||
m_devSettings->jsiRuntimeHolder = std::make_shared<HermesRuntimeHolder>();
|
||||
m_devSettings->jsiRuntimeHolder = std::make_shared<HermesRuntimeHolder>(m_devSettings, m_jsThread);
|
||||
m_devSettings->inlineSourceMap = false;
|
||||
break;
|
||||
#else
|
||||
|
@ -433,10 +432,16 @@ void InstanceImpl::loadBundleInternal(std::string &&jsBundleRelativePath, bool s
|
|||
m_devSettings->inlineSourceMap);
|
||||
|
||||
if (!success) {
|
||||
m_devManager->UpdateBundleStatus(false, -1);
|
||||
m_devSettings->errorCallback(jsBundleString);
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t currentTimeInMilliSeconds =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono ::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
m_devManager->UpdateBundleStatus(true, currentTimeInMilliSeconds);
|
||||
|
||||
auto bundleUrl = DevServerHelper::get_BundleUrl(
|
||||
m_devSettings->sourceBundleHost,
|
||||
m_devSettings->sourceBundlePort,
|
||||
|
@ -484,6 +489,7 @@ void InstanceImpl::loadBundleInternal(std::string &&jsBundleRelativePath, bool s
|
|||
}
|
||||
|
||||
InstanceImpl::~InstanceImpl() {
|
||||
m_devManager->StopInspector();
|
||||
m_nativeQueue->quitSynchronous();
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,9 @@
|
|||
<ClCompile Include="$(MSBuildThisFileDirectory)HermesRuntimeHolder.cpp">
|
||||
<ExcludedFromBuild Condition="'$(UseHermes)' != 'true'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)InspectorPackagerConnection.cpp">
|
||||
<ExcludedFromBuild Condition="'$(UseHermes)' != 'true'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)InstanceManager.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)JSBigAbiString.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)JSI\ChakraApi.cpp" />
|
||||
|
@ -91,6 +94,7 @@
|
|||
<ClInclude Include="$(MSBuildThisFileDirectory)DevSettings.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)Executors\WebSocketJSExecutor.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)HermesRuntimeHolder.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)InspectorPackagerConnection.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)IDevSupportManager.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)IHttpResource.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)InstanceManager.h" />
|
||||
|
@ -120,6 +124,7 @@
|
|||
<ClInclude Include="$(MSBuildThisFileDirectory)Threading\MessageQueueThreadFactory.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)Tracing.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)tracing\fbsystrace.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)tracing\tracing.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)TurboModuleManager.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)TurboModuleRegistry.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)Utils.h" />
|
||||
|
|
|
@ -124,6 +124,9 @@
|
|||
<ClCompile Include="$(MSBuildThisFileDirectory)JSI\ChakraJsiRuntime_edgemode.cpp">
|
||||
<Filter>Source Files\JSI</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)InspectorPackagerConnection.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
|
@ -363,6 +366,12 @@
|
|||
<ClInclude Include="$(MSBuildThisFileDirectory)JSI\ChakraCoreRuntime.h">
|
||||
<Filter>Header Files\JSI</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)InspectorPackagerConnection.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)tracing\tracing.h">
|
||||
<Filter>Header Files\tracing</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="$(MSBuildThisFileDirectory)tracing\rnw.wprp">
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include <jsi/jsi.h>
|
||||
#include <winmeta.h>
|
||||
#include "tracing/fbsystrace.h"
|
||||
|
||||
#include <array>
|
||||
|
@ -383,6 +384,16 @@ void initializeETW() {
|
|||
}
|
||||
}
|
||||
|
||||
void log(const char *msg) {
|
||||
TraceLoggingWrite(
|
||||
g_hTraceLoggingProvider, "Trace", TraceLoggingLevel(WINEVENT_LEVEL_INFO), TraceLoggingString(msg, "message"));
|
||||
}
|
||||
|
||||
void error(const char *msg) {
|
||||
TraceLoggingWrite(
|
||||
g_hTraceLoggingProvider, "Trace", TraceLoggingLevel(WINEVENT_LEVEL_ERROR), TraceLoggingString(msg, "message"));
|
||||
}
|
||||
|
||||
} // namespace tracing
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
// forward declaration.
|
||||
namespace facebook {
|
||||
namespace jsi {
|
||||
class Runtime;
|
||||
}
|
||||
} // namespace facebook
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
namespace tracing {
|
||||
void initializeETW();
|
||||
void initializeJSHooks(facebook::jsi::Runtime &runtime);
|
||||
|
||||
void log(const char *msg);
|
||||
void error(const char *msg);
|
||||
} // namespace tracing
|
||||
} // namespace react
|
||||
} // namespace facebook
|
Загрузка…
Ссылка в новой задаче