Skip to content

Commit 3458ae7

Browse files
hoxyqfacebook-github-bot
authored andcommitted
Scaffold support for Tracing by jsinspector (facebook#48836)
Summary: # Changelog: [Internal] When `Tracing.start` CDP method is received, here is what happens next: - `TracingAgent`, which intercepts the event will propagate this status to `Instance` by calling `startTracing()` on `InstanceAgent`. - `InstanceAgent` will propagate it to `Runtime` entities. The only difference, there is no concept of tracing for Runtime, it will only have an API for starting sampling profiler. When `Tracing.end` CDP method is received, it is propagated in the same order. There is also `collect*()` methods for collecting profiles. This has multiple benefits: - We can control when `Runtime` or `Instance` are recreated and can ask them to start recording trace profiles right away. This may be required when we would add support for Reload and Profile from Performance panel. - We might leverage this setup in the future, once we add instrumentation for Network panel. `InstanceAgent` will get notified when tracing started and will correspondingly notify other part of the infrastructure that will be responsible for recording network calls. - We remain being fully agnostic to the actual `Runtime`, see corresponding `RuntimeTargetDelegate` for `V8`. We don't have the capacity for implementing and maintaining sampling profiler capabilities for it, but this approach unblocks it from technical perspective, if someone would want to invest into this. `InstanceProfile` is a superset of `RuntimeSamplingProfile`. In the future, `InstanceProfile` may also include things like `NetworkProfile`, or similar stuff. The definition for `RuntimeSamplingProfile` will be added in D68414421 and relies on the availability of new API for sampling profiler in Hermes. Differential Revision: D68327630
1 parent 6acd541 commit 3458ae7

17 files changed

+350
-59
lines changed

packages/react-native/ReactCommon/hermes/inspector-modern/chrome/HermesRuntimeTargetDelegate.cpp

+31
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ using namespace facebook::hermes;
2828
namespace facebook::react::jsinspector_modern {
2929

3030
#ifdef HERMES_ENABLE_DEBUGGER
31+
namespace {
32+
33+
const uint16_t HERMES_SAMPLING_FREQUENCY_HZ = 1000;
34+
35+
}
36+
3137
class HermesRuntimeTargetDelegate::Impl final : public RuntimeTargetDelegate {
3238
using HermesStackTrace = debugger::StackTrace;
3339

@@ -167,6 +173,18 @@ class HermesRuntimeTargetDelegate::Impl final : public RuntimeTargetDelegate {
167173
runtime_->getDebugger().captureStackTrace());
168174
}
169175

176+
void enableSamplingProfiler() override {
177+
runtime_->enableSamplingProfiler(HERMES_SAMPLING_FREQUENCY_HZ);
178+
}
179+
180+
void disableSamplingProfiler() override {
181+
runtime_->disableSamplingProfiler();
182+
}
183+
184+
tracing::RuntimeSamplingProfile collectSamplingProfile() override {
185+
return tracing::RuntimeSamplingProfile{};
186+
}
187+
170188
private:
171189
HermesRuntimeTargetDelegate& delegate_;
172190
std::shared_ptr<HermesRuntime> runtime_;
@@ -228,6 +246,19 @@ std::unique_ptr<StackTrace> HermesRuntimeTargetDelegate::captureStackTrace(
228246
return impl_->captureStackTrace(runtime, framesToSkip);
229247
}
230248

249+
void HermesRuntimeTargetDelegate::enableSamplingProfiler() {
250+
impl_->enableSamplingProfiler();
251+
}
252+
253+
void HermesRuntimeTargetDelegate::disableSamplingProfiler() {
254+
impl_->disableSamplingProfiler();
255+
}
256+
257+
tracing::RuntimeSamplingProfile
258+
HermesRuntimeTargetDelegate::collectSamplingProfile() {
259+
return impl_->collectSamplingProfile();
260+
}
261+
231262
#ifdef HERMES_ENABLE_DEBUGGER
232263
CDPDebugAPI& HermesRuntimeTargetDelegate::getCDPDebugAPI() {
233264
return impl_->getCDPDebugAPI();

packages/react-native/ReactCommon/hermes/inspector-modern/chrome/HermesRuntimeTargetDelegate.h

+6
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ class HermesRuntimeTargetDelegate : public RuntimeTargetDelegate {
5454
jsi::Runtime& runtime,
5555
size_t framesToSkip) override;
5656

57+
void enableSamplingProfiler() override;
58+
59+
void disableSamplingProfiler() override;
60+
61+
tracing::RuntimeSamplingProfile collectSamplingProfile() override;
62+
5763
private:
5864
// We use the private implementation idiom to ensure this class has the same
5965
// layout regardless of whether HERMES_ENABLE_DEBUGGER is defined. The net

packages/react-native/ReactCommon/jsinspector-modern/FallbackRuntimeTargetDelegate.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,18 @@ std::unique_ptr<StackTrace> FallbackRuntimeTargetDelegate::captureStackTrace(
4444
return std::make_unique<StackTrace>();
4545
}
4646

47+
void FallbackRuntimeTargetDelegate::enableSamplingProfiler() {
48+
// no-op
49+
};
50+
51+
void FallbackRuntimeTargetDelegate::disableSamplingProfiler() {
52+
// no-op
53+
};
54+
55+
tracing::RuntimeSamplingProfile
56+
FallbackRuntimeTargetDelegate::collectSamplingProfile() {
57+
throw std::logic_error(
58+
"Sampling Profiler capabilities are not supported for Runtime fallback");
59+
}
60+
4761
} // namespace facebook::react::jsinspector_modern

packages/react-native/ReactCommon/jsinspector-modern/FallbackRuntimeTargetDelegate.h

+6
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ class FallbackRuntimeTargetDelegate : public RuntimeTargetDelegate {
4040
jsi::Runtime& runtime,
4141
size_t framesToSkip) override;
4242

43+
void enableSamplingProfiler() override;
44+
45+
void disableSamplingProfiler() override;
46+
47+
tracing::RuntimeSamplingProfile collectSamplingProfile() override;
48+
4349
private:
4450
std::string engineDescription_;
4551
};

packages/react-native/ReactCommon/jsinspector-modern/HostAgent.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,11 @@ void HostAgent::sendInfoLogEntry(
223223

224224
void HostAgent::setCurrentInstanceAgent(
225225
std::shared_ptr<InstanceAgent> instanceAgent) {
226+
tracingAgent_.setCurrentInstanceAgent(instanceAgent);
227+
226228
auto previousInstanceAgent = std::move(instanceAgent_);
227229
instanceAgent_ = std::move(instanceAgent);
230+
228231
if (!sessionState_.isRuntimeDomainEnabled) {
229232
return;
230233
}

packages/react-native/ReactCommon/jsinspector-modern/InstanceAgent.cpp

+19
Original file line numberDiff line numberDiff line change
@@ -152,4 +152,23 @@ void InstanceAgent::maybeSendPendingConsoleMessages() {
152152
}
153153
}
154154

155+
void InstanceAgent::startTracing() {
156+
if (runtimeAgent_) {
157+
runtimeAgent_->enableSamplingProfiler();
158+
}
159+
}
160+
161+
void InstanceAgent::stopTracing() {
162+
if (runtimeAgent_) {
163+
runtimeAgent_->disableSamplingProfiler();
164+
}
165+
}
166+
167+
tracing::InstanceTracingProfile InstanceAgent::collectTracingProfile() {
168+
tracing::RuntimeSamplingProfile runtimeSamplingProfile =
169+
runtimeAgent_->collectSamplingProfile();
170+
171+
return tracing::InstanceTracingProfile{runtimeSamplingProfile};
172+
}
173+
155174
} // namespace facebook::react::jsinspector_modern

packages/react-native/ReactCommon/jsinspector-modern/InstanceAgent.h

+18
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include <jsinspector-modern/InspectorInterfaces.h>
1515
#include <jsinspector-modern/RuntimeAgent.h>
16+
#include <jsinspector-modern/tracing/InstanceTracingProfile.h>
1617

1718
#include <functional>
1819

@@ -59,6 +60,23 @@ class InstanceAgent final {
5960
*/
6061
void sendConsoleMessage(SimpleConsoleMessage message);
6162

63+
/**
64+
* Notify Instance about started Tracing session. Should be initiated by
65+
* TracingAgent on Tracing.start CDP method.
66+
*/
67+
void startTracing();
68+
69+
/**
70+
* Notify Instance about stopped Tracing session. Should be initiated by
71+
* TracingAgent on Tracing.end CDP method.
72+
*/
73+
void stopTracing();
74+
75+
/**
76+
* Return recorded profile for the previous tracing session.
77+
*/
78+
tracing::InstanceTracingProfile collectTracingProfile();
79+
6280
private:
6381
void maybeSendExecutionContextCreatedNotification();
6482
void sendConsoleMessageImmediately(SimpleConsoleMessage message);

packages/react-native/ReactCommon/jsinspector-modern/RuntimeAgent.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -149,4 +149,16 @@ RuntimeAgent::~RuntimeAgent() {
149149
sessionState_.lastRuntimeAgentExportedState = getExportedState();
150150
}
151151

152+
void RuntimeAgent::enableSamplingProfiler() {
153+
targetController_.enableSamplingProfiler();
154+
}
155+
156+
void RuntimeAgent::disableSamplingProfiler() {
157+
targetController_.disableSamplingProfiler();
158+
}
159+
160+
tracing::RuntimeSamplingProfile RuntimeAgent::collectSamplingProfile() {
161+
return targetController_.collectSamplingProfile();
162+
}
163+
152164
} // namespace facebook::react::jsinspector_modern

packages/react-native/ReactCommon/jsinspector-modern/RuntimeAgent.h

+17
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#include "RuntimeAgentDelegate.h"
1313
#include "RuntimeTarget.h"
1414

15+
#include <jsinspector-modern/tracing/RuntimeSamplingProfile.h>
16+
1517
namespace facebook::react::jsinspector_modern {
1618

1719
class RuntimeTargetController;
@@ -81,6 +83,21 @@ class RuntimeAgent final {
8183
*/
8284
ExportedState getExportedState();
8385

86+
/**
87+
* Start sampling profiler for the corresponding RuntimeTarget.
88+
*/
89+
void enableSamplingProfiler();
90+
91+
/**
92+
* Stop sampling profiler for the corresponding RuntimeTarget.
93+
*/
94+
void disableSamplingProfiler();
95+
96+
/**
97+
* Return recorded sampling profile for the previous sampling session.
98+
*/
99+
tracing::RuntimeSamplingProfile collectSamplingProfile();
100+
84101
private:
85102
FrontendChannel frontendChannel_;
86103
RuntimeTargetController& targetController_;

packages/react-native/ReactCommon/jsinspector-modern/RuntimeTarget.cpp

+25
Original file line numberDiff line numberDiff line change
@@ -159,4 +159,29 @@ void RuntimeTargetController::notifyDebuggerSessionDestroyed() {
159159
target_.emitDebuggerSessionDestroyed();
160160
}
161161

162+
void RuntimeTargetController::enableSamplingProfiler() {
163+
target_.enableSamplingProfiler();
164+
}
165+
166+
void RuntimeTargetController::disableSamplingProfiler() {
167+
target_.disableSamplingProfiler();
168+
}
169+
170+
tracing::RuntimeSamplingProfile
171+
RuntimeTargetController::collectSamplingProfile() {
172+
return target_.collectSamplingProfile();
173+
}
174+
175+
void RuntimeTarget::enableSamplingProfiler() {
176+
delegate_.enableSamplingProfiler();
177+
}
178+
179+
void RuntimeTarget::disableSamplingProfiler() {
180+
delegate_.disableSamplingProfiler();
181+
}
182+
183+
tracing::RuntimeSamplingProfile RuntimeTarget::collectSamplingProfile() {
184+
return delegate_.collectSamplingProfile();
185+
}
186+
162187
} // namespace facebook::react::jsinspector_modern

packages/react-native/ReactCommon/jsinspector-modern/RuntimeTarget.h

+48-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77

88
#pragma once
99

10-
#include <ReactCommon/RuntimeExecutor.h>
11-
1210
#include "ConsoleMessage.h"
1311
#include "ExecutionContext.h"
1412
#include "InspectorInterfaces.h"
@@ -17,6 +15,9 @@
1715
#include "StackTrace.h"
1816
#include "WeakList.h"
1917

18+
#include <ReactCommon/RuntimeExecutor.h>
19+
#include <jsinspector-modern/tracing/RuntimeSamplingProfile.h>
20+
2021
#include <memory>
2122

2223
#ifndef JSINSPECTOR_EXPORT
@@ -90,6 +91,21 @@ class RuntimeTargetDelegate {
9091
virtual std::unique_ptr<StackTrace> captureStackTrace(
9192
jsi::Runtime& runtime,
9293
size_t framesToSkip = 0) = 0;
94+
95+
/**
96+
* Start sampling profiler.
97+
*/
98+
virtual void enableSamplingProfiler() = 0;
99+
100+
/**
101+
* Stop sampling profiler.
102+
*/
103+
virtual void disableSamplingProfiler() = 0;
104+
105+
/**
106+
* Return recorded sampling profile for the previous sampling session.
107+
*/
108+
virtual tracing::RuntimeSamplingProfile collectSamplingProfile() = 0;
93109
};
94110

95111
/**
@@ -118,6 +134,21 @@ class RuntimeTargetController {
118134
*/
119135
void notifyDebuggerSessionDestroyed();
120136

137+
/**
138+
* Start sampling profiler for the corresponding RuntimeTarget.
139+
*/
140+
void enableSamplingProfiler();
141+
142+
/**
143+
* Stop sampling profiler for the corresponding RuntimeTarget.
144+
*/
145+
void disableSamplingProfiler();
146+
147+
/**
148+
* Return recorded sampling profile for the previous sampling session.
149+
*/
150+
tracing::RuntimeSamplingProfile collectSamplingProfile();
151+
121152
private:
122153
RuntimeTarget& target_;
123154
};
@@ -171,6 +202,21 @@ class JSINSPECTOR_EXPORT RuntimeTarget
171202
FrontendChannel channel,
172203
SessionState& sessionState);
173204

205+
/**
206+
* Start sampling profiler for a particular JavaScript runtime.
207+
*/
208+
void enableSamplingProfiler();
209+
210+
/**
211+
* Stop sampling profiler for a particular JavaScript runtime.
212+
*/
213+
void disableSamplingProfiler();
214+
215+
/**
216+
* Return recorded sampling profile for the previous sampling session.
217+
*/
218+
tracing::RuntimeSamplingProfile collectSamplingProfile();
219+
174220
private:
175221
/**
176222
* Constructs a new RuntimeTarget. The caller must call setExecutor

packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp

+33-4
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,44 @@ const uint16_t TRACE_EVENT_CHUNK_SIZE = 1000;
2424
bool TracingAgent::handleRequest(const cdp::PreparsedRequest& req) {
2525
if (req.method == "Tracing.start") {
2626
// @cdp Tracing.start support is experimental.
27-
if (PerformanceTracer::getInstance().startTracing()) {
28-
frontendChannel_(cdp::jsonResult(req.id));
29-
} else {
27+
if (!instanceAgent_) {
28+
frontendChannel_(cdp::jsonError(
29+
req.id,
30+
cdp::ErrorCode::InternalError,
31+
"Couldn't find instance available for Tracing"));
32+
33+
return true;
34+
}
35+
36+
bool correctlyStartedPerformanceTracer =
37+
PerformanceTracer::getInstance().startTracing();
38+
39+
if (!correctlyStartedPerformanceTracer) {
3040
frontendChannel_(cdp::jsonError(
3141
req.id,
3242
cdp::ErrorCode::InternalError,
3343
"Tracing session already started"));
44+
45+
return true;
3446
}
3547

48+
instanceAgent_->startTracing();
49+
frontendChannel_(cdp::jsonResult(req.id));
50+
3651
return true;
3752
} else if (req.method == "Tracing.end") {
3853
// @cdp Tracing.end support is experimental.
39-
bool correctlyStopped = PerformanceTracer::getInstance().stopTracing();
54+
if (!instanceAgent_) {
55+
frontendChannel_(cdp::jsonError(
56+
req.id,
57+
cdp::ErrorCode::InternalError,
58+
"Couldn't find instance available for Tracing"));
4059

60+
return true;
61+
}
62+
instanceAgent_->stopTracing();
63+
64+
bool correctlyStopped = PerformanceTracer::getInstance().stopTracing();
4165
if (!correctlyStopped) {
4266
frontendChannel_(cdp::jsonError(
4367
req.id,
@@ -68,4 +92,9 @@ bool TracingAgent::handleRequest(const cdp::PreparsedRequest& req) {
6892
return false;
6993
}
7094

95+
void TracingAgent::setCurrentInstanceAgent(
96+
std::shared_ptr<InstanceAgent> instanceAgent) {
97+
instanceAgent_ = std::move(instanceAgent);
98+
}
99+
71100
} // namespace facebook::react::jsinspector_modern

0 commit comments

Comments
 (0)