11
11
*/
12
12
'use strict' ;
13
13
14
+ const invariant = require ( 'fbjs/lib/invariant' ) ;
15
+
14
16
type RelayProfiler = {
15
17
attachProfileHandler (
16
18
name : string ,
@@ -29,52 +31,89 @@ const TRACE_TAG_JSC_CALLS = 1 << 27;
29
31
30
32
let _enabled = false ;
31
33
let _asyncCookie = 0 ;
34
+ const _markStack = [ ] ;
35
+ let _markStackIndex = - 1 ;
32
36
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
+ }
54
60
} ,
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
+ }
57
84
} ,
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
+ }
62
98
} ,
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.
65
102
} ,
66
- } : null ;
103
+ } ;
67
104
68
105
const Systrace = {
106
+ getUserTimingPolyfill ( ) {
107
+ return userTimingPolyfill ;
108
+ } ,
109
+
69
110
setEnabled ( enabled : boolean ) {
70
111
if ( _enabled !== enabled ) {
71
112
if ( __DEV__ ) {
72
113
if ( enabled ) {
73
114
global . nativeTraceBeginLegacy && global . nativeTraceBeginLegacy ( TRACE_TAG_JSC_CALLS ) ;
74
- require ( 'ReactDebugTool' ) . addHook ( ReactSystraceDevtool ) ;
75
115
} else {
76
116
global . nativeTraceEndLegacy && global . nativeTraceEndLegacy ( TRACE_TAG_JSC_CALLS ) ;
77
- require ( 'ReactDebugTool' ) . removeHook ( ReactSystraceDevtool ) ;
78
117
}
79
118
}
80
119
_enabled = enabled ;
0 commit comments