Skip to content

Commit 18e271a

Browse files
huntiemeta-codesync[bot]
authored andcommitted
Add Performance Issues to Perf Monitor (2/2)
Summary: Introduces the concept of **Performance Issues**, an experimental performance signals concept for React Native. **Design** Performance Issues are an **experimental** user space API via the User Timings [`devtools` object](https://developer.chrome.com/docs/devtools/performance/extension#devtools_object). ``` performance.measure({ start, end, detail: { devtools: { ... rnIssueLevel: 'info' | 'warning' | 'error' } } }); ``` When `rnIssueLevel` is present, we eagerly report an the event over CDP, regardless of an active performance trace, via the `"__react_native_perf_issues_reporter"` runtime binding. **This diff** - Updates the V2 Perf Monitor UI (Android) to display Performance Issues as a count. - (UI parts of this diff, including `HostTarget::installPerfIssuesBinding` are reinstated from the previous INP design, removed in D82208400). Changelog: [Internal] Differential Revision: D85448199
1 parent 7d7984b commit 18e271a

File tree

12 files changed

+179
-7
lines changed

12 files changed

+179
-7
lines changed

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/perfmonitor/PerfMonitorOverlayManager.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ internal class PerfMonitorOverlayManager(
2222

2323
private var view: PerfMonitorOverlayView? = null
2424
private var tracingState: TracingState = TracingState.ENABLEDINCDPMODE
25+
private var perfIssueCount: Int = 0
2526

2627
/** Enable the Perf Monitor overlay. */
2728
fun enable() {
@@ -72,8 +73,19 @@ internal class PerfMonitorOverlayManager(
7273

7374
override fun onRecordingStateChanged(state: TracingState) {
7475
tracingState = state
76+
if (state != TracingState.DISABLED) {
77+
perfIssueCount = 0
78+
}
7579
UiThreadUtil.runOnUiThread {
7680
view?.updateRecordingState(state)
81+
view?.updatePerfIssueCount(perfIssueCount)
82+
view?.show()
83+
}
84+
}
85+
86+
override fun onPerfIssueAdded(message: String) {
87+
UiThreadUtil.runOnUiThread {
88+
view?.updatePerfIssueCount(perfIssueCount++)
7789
view?.show()
7890
}
7991
}

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/perfmonitor/PerfMonitorOverlayView.kt

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,11 @@ internal class PerfMonitorOverlayView(
3030
private val onButtonPress: () -> Unit,
3131
) {
3232
private val dialog: Dialog
33+
private lateinit var statusIndicator: TextView
3334
private lateinit var statusLabel: TextView
3435
private lateinit var tooltipLabel: TextView
35-
private lateinit var statusIndicator: TextView
36+
private lateinit var issuesContainer: LinearLayout
37+
private lateinit var issueCountLabel: TextView
3638

3739
init {
3840
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(context)
@@ -70,6 +72,11 @@ internal class PerfMonitorOverlayView(
7072
dialog.show()
7173
}
7274

75+
fun updatePerfIssueCount(count: Int) {
76+
issueCountLabel.text = count.toString()
77+
issuesContainer.visibility = if (count == 0) LinearLayout.GONE else LinearLayout.VISIBLE
78+
}
79+
7380
private fun createToolbarDialog(): Dialog {
7481
statusIndicator =
7582
TextView(context).apply {
@@ -85,11 +92,7 @@ internal class PerfMonitorOverlayView(
8592
val textContainer =
8693
LinearLayout(context).apply {
8794
orientation = LinearLayout.VERTICAL
88-
layoutParams =
89-
LinearLayout.LayoutParams(
90-
LinearLayout.LayoutParams.WRAP_CONTENT,
91-
LinearLayout.LayoutParams.WRAP_CONTENT,
92-
)
95+
setPadding(dpToPx(2f).toInt(), 0, 0, 0)
9396
}
9497
statusLabel =
9598
TextView(context).apply {
@@ -106,10 +109,35 @@ internal class PerfMonitorOverlayView(
106109
textContainer.addView(statusLabel)
107110
textContainer.addView(tooltipLabel)
108111

112+
issuesContainer =
113+
LinearLayout(context).apply {
114+
setPadding(dpToPx(8f).toInt(), 0, 0, 0)
115+
visibility = LinearLayout.GONE
116+
}
117+
issueCountLabel =
118+
TextView(context).apply {
119+
textSize = TEXT_SIZE_PRIMARY
120+
setTextColor(Color.WHITE)
121+
typeface = TYPEFACE_BOLD
122+
val alertDrawable =
123+
context.getDrawable(android.R.drawable.ic_dialog_alert)?.apply {
124+
setBounds(
125+
0,
126+
1,
127+
dpToPx(TEXT_SIZE_PRIMARY).toInt(),
128+
dpToPx(TEXT_SIZE_PRIMARY).toInt() + 1,
129+
)
130+
}
131+
setCompoundDrawables(alertDrawable, null, null, null)
132+
compoundDrawablePadding = dpToPx(6f).toInt()
133+
}
134+
issuesContainer.addView(issueCountLabel)
135+
109136
val containerLayout = createInnerLayout()
110137
containerLayout.setOnClickListener { onButtonPress() }
111138
containerLayout.addView(statusIndicator)
112139
containerLayout.addView(textContainer)
140+
containerLayout.addView(issuesContainer)
113141

114142
val dialog =
115143
createAnchoredDialog(dpToPx(12f), dpToPx(12f)).apply { setContentView(containerLayout) }
@@ -175,7 +203,7 @@ internal class PerfMonitorOverlayView(
175203
showDividers = LinearLayout.SHOW_DIVIDER_MIDDLE
176204
dividerDrawable =
177205
object : ColorDrawable(Color.TRANSPARENT) {
178-
override fun getIntrinsicWidth(): Int = dpToPx(8f).toInt()
206+
override fun getIntrinsicWidth(): Int = dpToPx(10f).toInt()
179207
}
180208
}
181209
}

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/perfmonitor/PerfMonitorUpdateListener.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,7 @@ import com.facebook.react.devsupport.interfaces.TracingState
1212
internal interface PerfMonitorUpdateListener {
1313
/** Called when the recording state of the background performance trace has changed. */
1414
fun onRecordingStateChanged(state: TracingState)
15+
16+
/** Called when a new Performance Issue is added. */
17+
fun onPerfIssueAdded(message: String)
1518
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ internal class ReactHostInspectorTarget(reactHostImpl: ReactHostImpl) :
7474
}
7575
}
7676

77+
fun handleNativePerfIssueAdded(
78+
message: String,
79+
) {
80+
perfMonitorListeners.forEach { listener -> listener.onPerfIssueAdded(message) }
81+
}
82+
7783
override fun close() {
7884
mHybridData.resetNative()
7985
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,13 @@ void JReactHostInspectorTarget::onSetPausedInDebuggerMessage(
120120
}
121121
}
122122

123+
void JReactHostInspectorTarget::unstable_onPerfIssueAdded(
124+
const PerfIssuePayload& issue) {
125+
static auto method = javaClassStatic()->getMethod<void(local_ref<jstring>)>(
126+
"handleNativePerfIssueAdded");
127+
method(jobj_, make_jstring(issue.message));
128+
}
129+
123130
void JReactHostInspectorTarget::loadNetworkResource(
124131
const jsinspector_modern::LoadNetworkResourceRequest& params,
125132
jsinspector_modern::ScopedExecutor<

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ class JReactHostInspectorTarget
116116
void onReload(const PageReloadRequest& request) override;
117117
void onSetPausedInDebuggerMessage(
118118
const OverlaySetPausedInDebuggerMessageRequest& request) override;
119+
void unstable_onPerfIssueAdded(
120+
const jsinspector_modern::PerfIssuePayload& issue) override;
119121
void loadNetworkResource(
120122
const jsinspector_modern::LoadNetworkResourceRequest& params,
121123
jsinspector_modern::ScopedExecutor<

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,9 @@ std::shared_ptr<HostTarget> HostTarget::create(
203203
VoidExecutor executor) {
204204
std::shared_ptr<HostTarget> hostTarget{new HostTarget(delegate)};
205205
hostTarget->setExecutor(std::move(executor));
206+
if (InspectorFlags::getInstance().getPerfIssuesEnabled()) {
207+
hostTarget->installPerfIssuesBinding();
208+
}
206209
return hostTarget;
207210
}
208211

@@ -286,6 +289,17 @@ void HostTarget::sendCommand(HostCommand command) {
286289
});
287290
}
288291

292+
void HostTarget::installPerfIssuesBinding() {
293+
perfMonitorUpdateHandler_ =
294+
std::make_unique<PerfMonitorUpdateHandler>(delegate_);
295+
perfMetricsBinding_ = std::make_unique<HostRuntimeBinding>(
296+
*this, // Used immediately
297+
"__react_native_perf_issues_reporter",
298+
[this](const std::string& message) {
299+
perfMonitorUpdateHandler_->handlePerfIssueAdded(message);
300+
});
301+
}
302+
289303
HostTargetController::HostTargetController(HostTarget& target)
290304
: target_(target) {}
291305

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "InspectorInterfaces.h"
1313
#include "InstanceTarget.h"
1414
#include "NetworkIOAgent.h"
15+
#include "PerfMonitorV2.h"
1516
#include "ScopedExecutor.h"
1617
#include "WeakList.h"
1718

@@ -127,6 +128,12 @@ class HostTargetDelegate : public LoadNetworkResourceDelegate {
127128
virtual void onSetPausedInDebuggerMessage(
128129
const OverlaySetPausedInDebuggerMessageRequest& request) = 0;
129130

131+
/**
132+
* [Experimental] Called when the runtime has new data for the V2 Perf
133+
* Monitor overlay. This is called on the inspector thread.
134+
*/
135+
virtual void unstable_onPerfIssueAdded(const PerfIssuePayload& /*issue*/) {}
136+
130137
/**
131138
* Called by NetworkIOAgent on handling a `Network.loadNetworkResource` CDP
132139
* request. Platform implementations should override this to perform a
@@ -166,6 +173,13 @@ class HostTargetController final {
166173

167174
bool hasInstance() const;
168175

176+
/**
177+
* [Experimental] Install a runtime binding subscribing to new Performance
178+
* Issues, which we broadcast to the V2 Perf Monitor overlay via
179+
* \ref HostTargetDelegate::unstable_onPerfIssueAdded.
180+
*/
181+
void installPerfIssuesBinding();
182+
169183
/**
170184
* Increments the target's pause overlay counter. The counter represents the
171185
* exact number of Agents that have (concurrently) requested the pause
@@ -331,6 +345,7 @@ class JSINSPECTOR_EXPORT HostTarget
331345
std::shared_ptr<ExecutionContextManager> executionContextManager_;
332346
std::shared_ptr<InstanceTarget> currentInstance_{nullptr};
333347
std::unique_ptr<HostCommandSender> commandSender_;
348+
std::unique_ptr<PerfMonitorUpdateHandler> perfMonitorUpdateHandler_;
334349
std::unique_ptr<HostRuntimeBinding> perfMetricsBinding_;
335350

336351
/**
@@ -349,6 +364,13 @@ class JSINSPECTOR_EXPORT HostTarget
349364
return currentInstance_ != nullptr;
350365
}
351366

367+
/**
368+
* [Experimental] Install a runtime binding subscribing to new Peformance
369+
* Issues, which we broadcast to the V2 Perf Monitor overlay via
370+
* \ref HostTargetDelegate::unstable_onPerfMonitorUpdate.
371+
*/
372+
void installPerfIssuesBinding();
373+
352374
// Necessary to allow HostAgent to access HostTarget's internals in a
353375
// controlled way (i.e. only HostTargetController gets friend access, while
354376
// HostAgent itself doesn't).

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ bool InspectorFlags::getNetworkInspectionEnabled() const {
3333
return loadFlagsAndAssertUnchanged().networkInspectionEnabled;
3434
}
3535

36+
bool InspectorFlags::getPerfIssuesEnabled() const {
37+
return loadFlagsAndAssertUnchanged().perfIssuesEnabled;
38+
}
39+
3640
void InspectorFlags::dangerouslyResetFlags() {
3741
*this = InspectorFlags{};
3842
}
@@ -59,6 +63,7 @@ const InspectorFlags::Values& InspectorFlags::loadFlagsAndAssertUnchanged()
5963
.networkInspectionEnabled =
6064
ReactNativeFeatureFlags::enableBridgelessArchitecture() &&
6165
ReactNativeFeatureFlags::fuseboxNetworkInspectionEnabled(),
66+
.perfIssuesEnabled = ReactNativeFeatureFlags::perfIssuesEnabled(),
6267
};
6368

6469
if (cachedValues_.has_value() && !inconsistentFlagsStateLogged_) {

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ class InspectorFlags {
3535
*/
3636
bool getNetworkInspectionEnabled() const;
3737

38+
/**
39+
* Flag determining if the V2 in-app Performance Monitor is enabled.
40+
*/
41+
bool getPerfIssuesEnabled() const;
42+
3843
/**
3944
* Forcibly disable the main `getFuseboxEnabled()` flag. This should ONLY be
4045
* used by `ReactInstanceIntegrationTest`.
@@ -52,6 +57,7 @@ class InspectorFlags {
5257
bool fuseboxEnabled;
5358
bool isProfilingBuild;
5459
bool networkInspectionEnabled;
60+
bool perfIssuesEnabled;
5561
bool operator==(const Values&) const = default;
5662
};
5763

0 commit comments

Comments
 (0)