Skip to content

Commit

Permalink
Merge pull request #6046 from gaearon/new-perf
Browse files Browse the repository at this point in the history
Add new ReactPerf
  • Loading branch information
gaearon committed Apr 29, 2016
2 parents 9d201ab + 67b1dbf commit 98a8f49
Show file tree
Hide file tree
Showing 12 changed files with 792 additions and 39 deletions.
2 changes: 1 addition & 1 deletion grunt/tasks/npm-react-addons.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ var addons = {
docs: 'two-way-binding-helpers',
},
Perf: {
module: 'ReactDefaultPerf',
module: 'ReactPerfAnalysis',
name: 'perf',
docs: 'perf',
},
Expand Down
2 changes: 1 addition & 1 deletion src/addons/ReactWithAddons.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ React.addons = {
};

if (__DEV__) {
React.addons.Perf = require('ReactDefaultPerf');
React.addons.Perf = require('ReactPerfAnalysis');
React.addons.TestUtils = require('ReactTestUtils');
}

Expand Down
156 changes: 154 additions & 2 deletions src/isomorphic/ReactDebugTool.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@

'use strict';

var ReactInvalidSetStateWarningDevTool = require('ReactInvalidSetStateWarningDevTool');
var ExecutionEnvironment = require('ExecutionEnvironment');

var performanceNow = require('performanceNow');
var warning = require('warning');

var eventHandlers = [];
Expand All @@ -37,6 +39,56 @@ function emitEvent(handlerFunctionName, arg1, arg2, arg3, arg4, arg5) {
}
}

var isProfiling = false;
var flushHistory = [];
var currentFlushNesting = 0;
var currentFlushMeasurements = null;
var currentFlushStartTime = null;
var currentTimerDebugID = null;
var currentTimerStartTime = null;
var currentTimerType = null;

function resetMeasurements() {
if (__DEV__) {
if (!isProfiling || currentFlushNesting === 0) {
currentFlushStartTime = null;
currentFlushMeasurements = null;
return;
}

var previousStartTime = currentFlushStartTime;
var previousMeasurements = currentFlushMeasurements || [];
var previousOperations = ReactNativeOperationHistoryDevtool.getHistory();

if (previousMeasurements.length || previousOperations.length) {
var registeredIDs = ReactComponentTreeDevtool.getRegisteredIDs();
flushHistory.push({
duration: performanceNow() - previousStartTime,
measurements: previousMeasurements || [],
operations: previousOperations || [],
treeSnapshot: registeredIDs.reduce((tree, id) => {
var ownerID = ReactComponentTreeDevtool.getOwnerID(id);
var parentID = ReactComponentTreeDevtool.getParentID(id);
tree[id] = {
displayName: ReactComponentTreeDevtool.getDisplayName(id),
text: ReactComponentTreeDevtool.getText(id),
childIDs: ReactComponentTreeDevtool.getChildIDs(id),
// Text nodes don't have owners but this is close enough.
ownerID: ownerID || ReactComponentTreeDevtool.getOwnerID(parentID),
parentID,
};
return tree;
}, {}),
});
}

currentFlushStartTime = performanceNow();
currentFlushMeasurements = [];
ReactComponentTreeDevtool.purgeUnmountedComponents();
ReactNativeOperationHistoryDevtool.clearHistory();
}
}

var ReactDebugTool = {
addDevtool(devtool) {
eventHandlers.push(devtool);
Expand All @@ -49,6 +101,95 @@ var ReactDebugTool = {
}
}
},
beginProfiling() {
if (__DEV__) {
if (isProfiling) {
return;
}

isProfiling = true;
flushHistory.length = 0;
resetMeasurements();
}
},
endProfiling() {
if (__DEV__) {
if (!isProfiling) {
return;
}

isProfiling = false;
resetMeasurements();
}
},
getFlushHistory() {
if (__DEV__) {
return flushHistory;
}
},
onBeginFlush() {
if (__DEV__) {
currentFlushNesting++;
resetMeasurements();
}
emitEvent('onBeginFlush');
},
onEndFlush() {
if (__DEV__) {
resetMeasurements();
currentFlushNesting--;
}
emitEvent('onEndFlush');
},
onBeginLifeCycleTimer(debugID, timerType) {
emitEvent('onBeginLifeCycleTimer', debugID, timerType);
if (__DEV__) {
if (isProfiling) {
warning(
!currentTimerType,
'There is an internal error in the React performance measurement code. ' +
'Did not expect %s timer to start while %s timer is still in ' +
'progress for %s instance.',
timerType,
currentTimerType || 'no',
(debugID === currentTimerDebugID) ? 'the same' : 'another'
);
currentTimerStartTime = performanceNow();
currentTimerDebugID = debugID;
currentTimerType = timerType;
}
}
},
onEndLifeCycleTimer(debugID, timerType) {
if (__DEV__) {
if (isProfiling) {
warning(
currentTimerType === timerType,
'There is an internal error in the React performance measurement code. ' +
'We did not expect %s timer to stop while %s timer is still in ' +
'progress for %s instance. Please report this as a bug in React.',
timerType,
currentTimerType || 'no',
(debugID === currentTimerDebugID) ? 'the same' : 'another'
);
currentFlushMeasurements.push({
timerType,
instanceID: debugID,
duration: performance.now() - currentTimerStartTime,
});
currentTimerStartTime = null;
currentTimerDebugID = null;
currentTimerType = null;
}
}
emitEvent('onEndLifeCycleTimer', debugID, timerType);
},
onBeginReconcilerTimer(debugID, timerType) {
emitEvent('onBeginReconcilerTimer', debugID, timerType);
},
onEndReconcilerTimer(debugID, timerType) {
emitEvent('onEndReconcilerTimer', debugID, timerType);
},
onBeginProcessingChildContext() {
emitEvent('onBeginProcessingChildContext');
},
Expand Down Expand Up @@ -90,6 +231,17 @@ var ReactDebugTool = {
},
};

ReactDebugTool.addDevtool(ReactInvalidSetStateWarningDevTool);
if (__DEV__) {
var ReactInvalidSetStateWarningDevTool = require('ReactInvalidSetStateWarningDevTool');
var ReactNativeOperationHistoryDevtool = require('ReactNativeOperationHistoryDevtool');
var ReactComponentTreeDevtool = require('ReactComponentTreeDevtool');
ReactDebugTool.addDevtool(ReactInvalidSetStateWarningDevTool);
ReactDebugTool.addDevtool(ReactComponentTreeDevtool);
ReactDebugTool.addDevtool(ReactNativeOperationHistoryDevtool);
var url = (ExecutionEnvironment.canUseDOM && window.location.href) || '';
if ((/[?&]react_perf\b/).test(url)) {
ReactDebugTool.beginProfiling();
}
}

module.exports = ReactDebugTool;
Loading

0 comments on commit 98a8f49

Please sign in to comment.