Skip to content

Commit 1aab2f9

Browse files
motiz88facebook-github-bot
authored andcommitted
Stash background trace directly in HostTarget, simplify API (facebook#55347)
Summary: Changelog: [Internal] Moves state and logic related to stashing a background trace from `JReactHostInspectorTarget` (JNI wrapper boilerplate, Android-specific) to `HostTarget` (cross-platform C++). Managing the full lifecycle of a background trace inside `HostTarget` lets us remove a fair amount of API surface: * `HostTargetDelegate::unstable_getHostTracingProfileThatWillBeEmittedOnInitialization()` * `HostTarget::hasActiveSessionWithFuseboxClient()` * `TracingAgent::emitExternalHostTracingProfile()` We also refactor some of the surrounding code and add tests (previously missing) for the behaviour of stashed background traces when there is more than one session. Reviewed By: huntie, hoxyq Differential Revision: D91589884
1 parent 40d939a commit 1aab2f9

7 files changed

Lines changed: 214 additions & 135 deletions

File tree

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostInspectorTarget.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ internal class ReactHostInspectorTarget(reactHostImpl: ReactHostImpl) :
4141

4242
external fun stopAndMaybeEmitBackgroundTrace(): Boolean
4343

44-
external fun stopAndDiscardBackgroundTrace()
44+
external fun stopTracing()
4545

4646
external override fun getTracingState(): TracingState
4747

@@ -65,7 +65,7 @@ internal class ReactHostInspectorTarget(reactHostImpl: ReactHostImpl) :
6565
}
6666

6767
override fun stopBackgroundTrace() {
68-
stopAndDiscardBackgroundTrace()
68+
stopTracing()
6969
}
7070

7171
fun handleNativePerfIssueAdded(

packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactHostInspectorTarget.cpp

Lines changed: 21 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,16 @@ JReactHostInspectorTarget::initHybrid(
7373
}
7474

7575
void JReactHostInspectorTarget::sendDebuggerResumeCommand() {
76+
inspectorTarget().sendCommand(HostCommand::DebuggerResume);
77+
}
78+
79+
jsinspector_modern::HostTarget& JReactHostInspectorTarget::inspectorTarget() {
7680
if (inspectorTarget_) {
77-
inspectorTarget_->sendCommand(HostCommand::DebuggerResume);
78-
} else {
79-
jni::throwNewJavaException(
80-
"java/lang/IllegalStateException",
81-
"Cannot send command while the Fusebox backend is not enabled");
81+
return *inspectorTarget_;
8282
}
83+
jni::throwNewJavaException(
84+
"java/lang/IllegalStateException",
85+
"Inspector method called while the Fusebox backend is not enabled.");
8386
}
8487

8588
jsinspector_modern::HostTargetMetadata
@@ -147,58 +150,22 @@ HostTarget* JReactHostInspectorTarget::getInspectorTarget() {
147150
}
148151

149152
bool JReactHostInspectorTarget::startBackgroundTrace() {
150-
if (inspectorTarget_) {
151-
return inspectorTarget_->startTracing(
152-
tracing::Mode::Background,
153-
{
154-
tracing::Category::HiddenTimeline,
155-
tracing::Category::RuntimeExecution,
156-
tracing::Category::Timeline,
157-
tracing::Category::UserTiming,
158-
});
159-
} else {
160-
jni::throwNewJavaException(
161-
"java/lang/IllegalStateException",
162-
"Cannot start Tracing session while the Fusebox backend is not enabled.");
163-
}
153+
return inspectorTarget().startTracing(
154+
tracing::Mode::Background,
155+
{
156+
tracing::Category::HiddenTimeline,
157+
tracing::Category::RuntimeExecution,
158+
tracing::Category::Timeline,
159+
tracing::Category::UserTiming,
160+
});
164161
}
165162

166-
tracing::HostTracingProfile JReactHostInspectorTarget::stopTracing() {
167-
if (inspectorTarget_) {
168-
return inspectorTarget_->stopTracing();
169-
} else {
170-
jni::throwNewJavaException(
171-
"java/lang/IllegalStateException",
172-
"Cannot stop Tracing session while the Fusebox backend is not enabled.");
173-
}
163+
void JReactHostInspectorTarget::stopTracing() {
164+
inspectorTarget().stopTracing();
174165
}
175166

176167
jboolean JReactHostInspectorTarget::stopAndMaybeEmitBackgroundTrace() {
177-
auto capturedTrace = inspectorTarget_->stopTracing();
178-
if (inspectorTarget_->hasActiveSessionWithFuseboxClient()) {
179-
inspectorTarget_->emitTracingProfileForFirstFuseboxClient(
180-
std::move(capturedTrace));
181-
return jboolean(true);
182-
}
183-
184-
stashTracingProfile(std::move(capturedTrace));
185-
return jboolean(false);
186-
}
187-
188-
void JReactHostInspectorTarget::stopAndDiscardBackgroundTrace() {
189-
inspectorTarget_->stopTracing();
190-
}
191-
192-
void JReactHostInspectorTarget::stashTracingProfile(
193-
tracing::HostTracingProfile&& hostTracingProfile) {
194-
stashedTracingProfile_ = std::move(hostTracingProfile);
195-
}
196-
197-
std::optional<tracing::HostTracingProfile> JReactHostInspectorTarget::
198-
unstable_getHostTracingProfileThatWillBeEmittedOnInitialization() {
199-
auto tracingProfile = std::move(stashedTracingProfile_);
200-
stashedTracingProfile_.reset();
201-
return tracingProfile;
168+
return jboolean(inspectorTarget().stopAndMaybeEmitBackgroundTrace());
202169
}
203170

204171
void JReactHostInspectorTarget::registerNatives() {
@@ -213,9 +180,7 @@ void JReactHostInspectorTarget::registerNatives() {
213180
makeNativeMethod(
214181
"stopAndMaybeEmitBackgroundTrace",
215182
JReactHostInspectorTarget::stopAndMaybeEmitBackgroundTrace),
216-
makeNativeMethod(
217-
"stopAndDiscardBackgroundTrace",
218-
JReactHostInspectorTarget::stopAndDiscardBackgroundTrace),
183+
makeNativeMethod("stopTracing", JReactHostInspectorTarget::stopTracing),
219184
makeNativeMethod(
220185
"getTracingState", JReactHostInspectorTarget::getTracingState),
221186
makeNativeMethod(
@@ -256,7 +221,7 @@ HostTargetTracingDelegate* JReactHostInspectorTarget::getTracingDelegate() {
256221

257222
void JReactHostInspectorTarget::recordFrameTimings(
258223
jni::alias_ref<JFrameTimingSequence::javaobject> frameTimingSequence) {
259-
inspectorTarget_->recordFrameTimings({
224+
inspectorTarget().recordFrameTimings({
260225
frameTimingSequence->getId(),
261226
frameTimingSequence->getThreadId(),
262227
frameTimingSequence->getBeginDrawingTimestamp(),

packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactHostInspectorTarget.h

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ class JReactHostInspectorTarget : public jni::HybridClass<JReactHostInspectorTar
240240
/**
241241
* Stops previously started trace recording and discards the captured trace.
242242
*/
243-
void stopAndDiscardBackgroundTrace();
243+
void stopTracing();
244244

245245
jsinspector_modern::HostTarget *getInspectorTarget();
246246

@@ -277,15 +277,20 @@ class JReactHostInspectorTarget : public jni::HybridClass<JReactHostInspectorTar
277277
void loadNetworkResource(
278278
const jsinspector_modern::LoadNetworkResourceRequest &params,
279279
jsinspector_modern::ScopedExecutor<jsinspector_modern::NetworkRequestListener> executor) override;
280-
std::optional<jsinspector_modern::tracing::HostTracingProfile>
281-
unstable_getHostTracingProfileThatWillBeEmittedOnInitialization() override;
282280
jsinspector_modern::HostTargetTracingDelegate *getTracingDelegate() override;
283281

284282
private:
285283
JReactHostInspectorTarget(
286284
jni::alias_ref<JReactHostInspectorTarget::javaobject> jobj,
287285
jni::alias_ref<JReactHostImpl> reactHostImpl,
288286
jni::alias_ref<JExecutor::javaobject> javaExecutor);
287+
288+
/**
289+
* Returns a reference to the HostTarget, throwing a Java IllegalStateException
290+
* if the Fusebox backend is not enabled (i.e., inspectorTarget_ is null).
291+
*/
292+
jsinspector_modern::HostTarget &inspectorTarget();
293+
289294
jni::global_ref<JReactHostInspectorTarget::javaobject> jobj_;
290295
// This weak reference breaks the cycle between the C++ HostTarget and the
291296
// Java ReactHostImpl, preventing memory leaks in apps that create multiple
@@ -296,20 +301,6 @@ class JReactHostInspectorTarget : public jni::HybridClass<JReactHostInspectorTar
296301
std::shared_ptr<jsinspector_modern::HostTarget> inspectorTarget_;
297302
std::optional<int> inspectorPageId_;
298303

299-
/**
300-
* Stops previously started trace recording and returns the captured HostTracingProfile.
301-
*/
302-
jsinspector_modern::tracing::HostTracingProfile stopTracing();
303-
/**
304-
* Stashes previously recorded HostTracingProfile that will be emitted when
305-
* CDP session is created. Once emitted, the value will be cleared from this
306-
* instance.
307-
*/
308-
void stashTracingProfile(jsinspector_modern::tracing::HostTracingProfile &&hostTracingProfile);
309-
/**
310-
* Previously recorded HostTracingProfile that will be emitted when CDP session is created.
311-
*/
312-
std::optional<jsinspector_modern::tracing::HostTracingProfile> stashedTracingProfile_;
313304
/**
314305
* Encapsulates the logic around tracing for this HostInspectorTarget.
315306
*/

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

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -236,13 +236,11 @@ class HostAgent::Impl final {
236236
emitSystemStateChanged(isSingleHost);
237237
}
238238

239-
auto stashedTraceRecording =
240-
targetController_.getDelegate()
241-
.unstable_getHostTracingProfileThatWillBeEmittedOnInitialization();
242-
if (stashedTraceRecording.has_value()) {
243-
tracingAgent_.emitExternalHostTracingProfile(
244-
std::move(stashedTraceRecording.value()));
245-
}
239+
auto emitted = targetController_.maybeEmitStashedBackgroundTrace();
240+
assert(
241+
emitted &&
242+
"Expected to find at least one session eligible to receive a background trace after ReactNativeApplication.enable");
243+
(void)emitted;
246244

247245
return {
248246
.isFinishedHandlingRequest = true,

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

Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -102,17 +102,8 @@ class HostTargetSession {
102102
}
103103
}
104104

105-
/**
106-
* Returns whether the ReactNativeApplication CDP domain is enabled.
107-
*
108-
* Chrome DevTools Frontend enables this domain as a client.
109-
*/
110-
bool hasFuseboxClient() const {
111-
return hostAgent_.hasFuseboxClientConnected();
112-
}
113-
114-
void emitHostTracingProfile(tracing::HostTracingProfile tracingProfile) {
115-
hostAgent_.emitExternalTracingProfile(std::move(tracingProfile));
105+
HostAgent& agent() {
106+
return hostAgent_;
116107
}
117108

118109
private:
@@ -323,6 +314,10 @@ bool HostTargetController::decrementPauseOverlayCounter() {
323314
return --pauseOverlayCounter_ != 0;
324315
}
325316

317+
bool HostTargetController::maybeEmitStashedBackgroundTrace() {
318+
return target_.maybeEmitStashedBackgroundTrace();
319+
}
320+
326321
namespace {
327322

328323
struct StaticHostTargetMetadata {
@@ -374,32 +369,26 @@ folly::dynamic createHostMetadataPayload(const HostTargetMetadata& metadata) {
374369
return result;
375370
}
376371

377-
bool HostTarget::hasActiveSessionWithFuseboxClient() const {
378-
bool hasActiveFuseboxSession = false;
379-
sessions_.forEach([&](auto& session) {
380-
hasActiveFuseboxSession |= session.hasFuseboxClient();
381-
});
382-
return hasActiveFuseboxSession;
383-
}
384-
385-
void HostTarget::emitTracingProfileForFirstFuseboxClient(
386-
tracing::HostTracingProfile tracingProfile) {
372+
bool HostTarget::maybeEmitStashedBackgroundTrace() {
387373
bool emitted = false;
388374
sessions_.forEach([&](auto& session) {
389375
if (emitted) {
390-
/**
391-
* HostTracingProfile object is not copiable for performance reasons,
392-
* because it could contain large Runtime sampling profile object.
393-
*
394-
* This approach would not work with multi-client debugger setup.
395-
*/
396376
return;
397377
}
398-
if (session.hasFuseboxClient()) {
399-
session.emitHostTracingProfile(std::move(tracingProfile));
378+
if (session.agent().hasFuseboxClientConnected()) {
379+
auto stashedTrace = std::exchange(stashedTracingProfile_, std::nullopt);
380+
if (stashedTrace) {
381+
session.agent().emitExternalTracingProfile(std::move(*stashedTrace));
382+
}
400383
emitted = true;
401384
}
402385
});
386+
return emitted;
387+
}
388+
389+
bool HostTarget::stopAndMaybeEmitBackgroundTrace() {
390+
stashedTracingProfile_ = stopTracing();
391+
return maybeEmitStashedBackgroundTrace();
403392
}
404393

405394
} // namespace facebook::react::jsinspector_modern

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

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -181,19 +181,6 @@ class HostTargetDelegate : public LoadNetworkResourceDelegate {
181181
"LoadNetworkResourceDelegate.loadNetworkResource is not implemented by this host target delegate.");
182182
}
183183

184-
/**
185-
* [Experimental] Will be called at the CDP session initialization to get the
186-
* trace recording that may have been stashed by the Host from the previous
187-
* background session.
188-
*
189-
* \return the HostTracingProfile if there is one that needs to be
190-
* displayed, otherwise std::nullopt.
191-
*/
192-
virtual std::optional<tracing::HostTracingProfile> unstable_getHostTracingProfileThatWillBeEmittedOnInitialization()
193-
{
194-
return std::nullopt;
195-
}
196-
197184
/**
198185
* An optional delegate that will be used by HostTarget to notify about tracing-related events.
199186
*/
@@ -256,6 +243,12 @@ class HostTargetController final {
256243
*/
257244
tracing::HostTracingProfile stopTracing();
258245

246+
/**
247+
* If there is a stashed background trace, emit it to the first eligible session.
248+
* \return true if an eligible session is found (even if there was no stashed background trace).
249+
*/
250+
bool maybeEmitStashedBackgroundTrace();
251+
259252
private:
260253
HostTarget &target_;
261254
size_t pauseOverlayCounter_{0};
@@ -352,18 +345,13 @@ class JSINSPECTOR_EXPORT HostTarget : public EnableExecutorFromThis<HostTarget>
352345
tracing::HostTracingProfile stopTracing();
353346

354347
/**
355-
* Returns whether there is an active session with the Fusebox client, i.e.
356-
* with Chrome DevTools Frontend fork for React Native.
348+
* Stops previously started trace recording and:
349+
* - If there is an active CDP session with ReactNativeApplication domain
350+
* enabled, emits the trace and returns true.
351+
* - Otherwise, stashes the captured trace, that will be emitted when a CDP
352+
* session enables ReactNativeApplication. Returns false.
357353
*/
358-
bool hasActiveSessionWithFuseboxClient() const;
359-
360-
/**
361-
* Emits the HostTracingProfile for the first active session with the Fusebox
362-
* client.
363-
*
364-
* @see \c hasActiveFrontendSession
365-
*/
366-
void emitTracingProfileForFirstFuseboxClient(tracing::HostTracingProfile tracingProfile);
354+
bool stopAndMaybeEmitBackgroundTrace();
367355

368356
/**
369357
* An endpoint for the Host to report frame timings that will be recorded if and only if there is currently an active
@@ -403,6 +391,11 @@ class JSINSPECTOR_EXPORT HostTarget : public EnableExecutorFromThis<HostTarget>
403391
* Should only be allocated when there is an active tracing session.
404392
*/
405393
std::unique_ptr<HostTargetTraceRecording> traceRecording_{nullptr};
394+
/**
395+
* Previously recorded HostTracingProfile that will be emitted when CDP session is created
396+
* and enables ReactNativeApplication. Once emitted, the value will be cleared.
397+
*/
398+
std::optional<tracing::HostTracingProfile> stashedTracingProfile_;
406399
/**
407400
* Protects the state inside traceRecording_.
408401
*
@@ -429,6 +422,12 @@ class JSINSPECTOR_EXPORT HostTarget : public EnableExecutorFromThis<HostTarget>
429422
*/
430423
void installPerfIssuesBinding();
431424

425+
/**
426+
* If there is a stashed background trace, emit it to the first eligible session.
427+
* \return true if an eligible session is found (even if there was no stashed background trace).
428+
*/
429+
bool maybeEmitStashedBackgroundTrace();
430+
432431
// Necessary to allow HostAgent to access HostTarget's internals in a
433432
// controlled way (i.e. only HostTargetController gets friend access, while
434433
// HostAgent itself doesn't).

0 commit comments

Comments
 (0)