1111 */
1212'use strict' ;
1313
14+ const invariant = require ( 'fbjs/lib/invariant' ) ;
15+
1416type RelayProfiler = {
1517 attachProfileHandler (
1618 name : string ,
@@ -29,52 +31,89 @@ const TRACE_TAG_JSC_CALLS = 1 << 27;
2931
3032let _enabled = false ;
3133let _asyncCookie = 0 ;
34+ const _markStack = [ ] ;
35+ let _markStackIndex = - 1 ;
3236
33- const ReactSystraceDevtool = __DEV__ ? {
34- onBeforeMountComponent ( debugID ) {
35- const ReactComponentTreeHook = require ( 'ReactGlobalSharedState' ) . ReactComponentTreeHook ;
36- const displayName = ReactComponentTreeHook . getDisplayName ( debugID ) ;
37- Systrace . beginEvent ( `ReactReconciler.mountComponent(${ displayName } )` ) ;
38- } ,
39- onMountComponent ( debugID ) {
40- Systrace . endEvent ( ) ;
41- } ,
42- onBeforeUpdateComponent ( debugID ) {
43- const ReactComponentTreeHook = require ( 'ReactGlobalSharedState' ) . ReactComponentTreeHook ;
44- const displayName = ReactComponentTreeHook . getDisplayName ( debugID ) ;
45- Systrace . beginEvent ( `ReactReconciler.updateComponent(${ displayName } )` ) ;
46- } ,
47- onUpdateComponent ( debugID ) {
48- Systrace . endEvent ( ) ;
49- } ,
50- onBeforeUnmountComponent ( debugID ) {
51- const ReactComponentTreeHook = require ( 'ReactGlobalSharedState' ) . ReactComponentTreeHook ;
52- const displayName = ReactComponentTreeHook . getDisplayName ( debugID ) ;
53- Systrace . beginEvent ( `ReactReconciler.unmountComponent(${ displayName } )` ) ;
37+ // Implements a subset of User Timing API necessary for React measurements.
38+ // https://developer.mozilla.org/en-US/docs/Web/API/User_Timing_API
39+ const REACT_MARKER = '\u269B' ;
40+ const userTimingPolyfill = {
41+ mark ( markName : string ) {
42+ if ( __DEV__ ) {
43+ if ( _enabled ) {
44+ _markStackIndex ++ ;
45+ _markStack [ _markStackIndex ] = markName ;
46+ let systraceLabel = markName ;
47+ // Since perf measurements are a shared namespace in User Timing API,
48+ // we prefix all React results with a React emoji.
49+ if ( markName [ 0 ] === REACT_MARKER ) {
50+ // This is coming from React.
51+ // Removing component IDs keeps trace colors stable.
52+ const indexOfId = markName . lastIndexOf ( ' (#' ) ;
53+ const cutoffIndex = indexOfId !== - 1 ? indexOfId : markName . length ;
54+ // Also cut off the emoji because it breaks Systrace
55+ systraceLabel = markName . slice ( 2 , cutoffIndex ) ;
56+ }
57+ Systrace . beginEvent ( systraceLabel ) ;
58+ }
59+ }
5460 } ,
55- onUnmountComponent ( debugID ) {
56- Systrace . endEvent ( ) ;
61+ measure ( measureName : string , startMark : ?string , endMark : ?string ) {
62+ if ( __DEV__ ) {
63+ if ( _enabled ) {
64+ invariant (
65+ typeof measureName === 'string' &&
66+ typeof startMark === 'string' &&
67+ typeof endMark === 'undefined' ,
68+ 'Only performance.measure(string, string) overload is supported.'
69+ ) ;
70+ const topMark = _markStack [ _markStackIndex ] ;
71+ invariant (
72+ startMark === topMark ,
73+ 'There was a mismatching performance.measure() call. ' +
74+ 'Expected "%s" but got "%s."' ,
75+ topMark ,
76+ startMark ,
77+ ) ;
78+ _markStackIndex -- ;
79+ // We can't use more descriptive measureName because Systrace doesn't
80+ // let us edit labels post factum.
81+ Systrace . endEvent ( ) ;
82+ }
83+ }
5784 } ,
58- onBeginLifeCycleTimer ( debugID , timerType ) {
59- const ReactComponentTreeHook = require ( 'ReactGlobalSharedState' ) . ReactComponentTreeHook ;
60- const displayName = ReactComponentTreeHook . getDisplayName ( debugID ) ;
61- Systrace . beginEvent ( `${ displayName } .${ timerType } ()` ) ;
85+ clearMarks ( markName : string ) {
86+ if ( __DEV__ ) {
87+ if ( _enabled ) {
88+ if ( _markStackIndex === - 1 ) {
89+ return ;
90+ }
91+ if ( markName === _markStack [ _markStackIndex ] ) {
92+ // React uses this for "cancelling" started measurements.
93+ // Systrace doesn't support deleting measurements, so we just stop them.
94+ userTimingPolyfill . measure ( markName , markName ) ;
95+ }
96+ }
97+ }
6298 } ,
63- onEndLifeCycleTimer ( debugID , timerType ) {
64- Systrace . endEvent ( ) ;
99+ clearMeasures ( ) {
100+ // React calls this to avoid memory leaks in browsers, but we don't keep
101+ // measurements anyway.
65102 } ,
66- } : null ;
103+ } ;
67104
68105const Systrace = {
106+ getUserTimingPolyfill ( ) {
107+ return userTimingPolyfill ;
108+ } ,
109+
69110 setEnabled ( enabled : boolean ) {
70111 if ( _enabled !== enabled ) {
71112 if ( __DEV__ ) {
72113 if ( enabled ) {
73114 global . nativeTraceBeginLegacy && global . nativeTraceBeginLegacy ( TRACE_TAG_JSC_CALLS ) ;
74- require ( 'ReactDebugTool' ) . addHook ( ReactSystraceDevtool ) ;
75115 } else {
76116 global . nativeTraceEndLegacy && global . nativeTraceEndLegacy ( TRACE_TAG_JSC_CALLS ) ;
77- require ( 'ReactDebugTool' ) . removeHook ( ReactSystraceDevtool ) ;
78117 }
79118 }
80119 _enabled = enabled ;
0 commit comments