From f0527d86623112eee1e80da7884081f7ecf59bea Mon Sep 17 00:00:00 2001 From: Benoit Girard Date: Fri, 4 Oct 2024 14:27:25 -0700 Subject: [PATCH] Instrument XMLHttpRequest (#46844) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/46844 Currently we can't see when a network query is made. It makes it hard to debug performance in apps like Store because it's unclear which queries are preloaded, which are fetched as early as they can and which ones are fetched late. Changelog: [Internal] Reviewed By: rubennorte Differential Revision: D63858486 fbshipit-source-id: 29d74a68f96a7d4c7b212453d7c0ceba525f3e39 --- .../Libraries/Network/XMLHttpRequest.js | 27 ++++++++++++++++++- .../__snapshots__/public-api-test.js.snap | 4 +++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/packages/react-native/Libraries/Network/XMLHttpRequest.js b/packages/react-native/Libraries/Network/XMLHttpRequest.js index 458f0780a0f..7697233ae0a 100644 --- a/packages/react-native/Libraries/Network/XMLHttpRequest.js +++ b/packages/react-native/Libraries/Network/XMLHttpRequest.js @@ -22,6 +22,7 @@ const base64 = require('base64-js'); const invariant = require('invariant'); const DEBUG_NETWORK_SEND_DELAY: false = false; // Set to a number of milliseconds when debugging +const LABEL_FOR_MISSING_URL_FOR_PROFILING = 'Unknown URL'; export type NativeResponseType = 'base64' | 'blob' | 'text'; export type ResponseType = @@ -101,6 +102,7 @@ class XMLHttpRequest extends (EventTarget(...XHR_EVENTS): typeof EventTarget) { static DONE: number = DONE; static _interceptor: ?XHRInterceptor = null; + static _profiling: boolean = false; UNSENT: number = UNSENT; OPENED: number = OPENED; @@ -144,12 +146,17 @@ class XMLHttpRequest extends (EventTarget(...XHR_EVENTS): typeof EventTarget) { _timedOut: boolean = false; _trackingName: string = 'unknown'; _incrementalEvents: boolean = false; + _startTime: ?number = null; _performanceLogger: IPerformanceLogger = GlobalPerformanceLogger; static setInterceptor(interceptor: ?XHRInterceptor) { XMLHttpRequest._interceptor = interceptor; } + static enableProfiling(enableProfiling: boolean): void { + XMLHttpRequest._profiling = enableProfiling; + } + constructor() { super(); this._reset(); @@ -356,6 +363,11 @@ class XMLHttpRequest extends (EventTarget(...XHR_EVENTS): typeof EventTarget) { this._response += responseText; } + if (XMLHttpRequest._profiling) { + performance.mark( + 'Track:XMLHttpRequest:Incremental Data: ' + this._getMeasureURL(), + ); + } XMLHttpRequest._interceptor && XMLHttpRequest._interceptor.dataReceived(requestId, responseText); @@ -398,7 +410,13 @@ class XMLHttpRequest extends (EventTarget(...XHR_EVENTS): typeof EventTarget) { this._clearSubscriptions(); this._requestId = null; this.setReadyState(this.DONE); - + if (XMLHttpRequest._profiling && this._startTime != null) { + const start = this._startTime; + performance.measure('Track:XMLHttpRequest:' + this._getMeasureURL(), { + start, + end: performance.now(), + }); + } if (error) { XMLHttpRequest._interceptor && XMLHttpRequest._interceptor.loadingFailed(requestId, error); @@ -572,6 +590,7 @@ class XMLHttpRequest extends (EventTarget(...XHR_EVENTS): typeof EventTarget) { this._trackingName !== 'unknown' ? this._trackingName : this._url; this._perfKey = 'network_XMLHttpRequest_' + String(friendlyName); this._performanceLogger.startTimespan(this._perfKey); + this._startTime = performance.now(); invariant( this._method, 'XMLHttpRequest method needs to be defined (%s).', @@ -668,6 +687,12 @@ class XMLHttpRequest extends (EventTarget(...XHR_EVENTS): typeof EventTarget) { } super.addEventListener(type, listener); } + + _getMeasureURL(): string { + return ( + this._trackingName ?? this._url ?? LABEL_FOR_MISSING_URL_FOR_PROFILING + ); + } } module.exports = XMLHttpRequest; diff --git a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap index 15cd6c00cce..ae2abed6d49 100644 --- a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap +++ b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap @@ -6750,6 +6750,7 @@ declare class XMLHttpRequest extends EventTarget { static LOADING: number; static DONE: number; static _interceptor: ?XHRInterceptor; + static _profiling: boolean; UNSENT: number; OPENED: number; HEADERS_RECEIVED: number; @@ -6786,8 +6787,10 @@ declare class XMLHttpRequest extends EventTarget { _timedOut: boolean; _trackingName: string; _incrementalEvents: boolean; + _startTime: ?number; _performanceLogger: IPerformanceLogger; static setInterceptor(interceptor: ?XHRInterceptor): void; + static enableProfiling(enableProfiling: boolean): void; constructor(): void; _reset(): void; get responseType(): ResponseType; @@ -6831,6 +6834,7 @@ declare class XMLHttpRequest extends EventTarget { setResponseHeaders(responseHeaders: ?Object): void; setReadyState(newState: number): void; addEventListener(type: string, listener: EventListener): void; + _getMeasureURL(): string; } declare module.exports: XMLHttpRequest; "