profiler.js
557 lines
| 21.1 KiB
| application/javascript
|
JavascriptLexer
r547 | /* | |||
r1073 | Copyright (c) 2010, Yahoo! Inc. All rights reserved. | |||
r547 | Code licensed under the BSD License: | |||
r1073 | http://developer.yahoo.com/yui/license.html | |||
version: 2.8.2r1 | ||||
r547 | */ | |||
YAHOO.namespace("tool"); | ||||
/** | ||||
* The YUI JavaScript profiler. | ||||
* @module profiler | ||||
* @namespace YAHOO.tool | ||||
* @requires yahoo | ||||
*/ | ||||
/** | ||||
* Profiles functions in JavaScript. | ||||
* @namespace YAHOO.tool | ||||
* @class Profiler | ||||
* @static | ||||
*/ | ||||
YAHOO.tool.Profiler = function(){ | ||||
//------------------------------------------------------------------------- | ||||
// Private Variables and Functions | ||||
//------------------------------------------------------------------------- | ||||
var container = {}, //Container object on which to put the original unprofiled methods. | ||||
report = {}, //Profiling information for functions | ||||
stopwatches = {}, //Additional stopwatch information | ||||
WATCH_STARTED = 0, | ||||
WATCH_STOPPED = 1, | ||||
WATCH_PAUSED = 2, | ||||
lang = YAHOO.lang; | ||||
/** | ||||
* Creates a report object with the given name. | ||||
* @param {String} name The name to store for the report object. | ||||
* @return {Void} | ||||
* @method createReport | ||||
* @private | ||||
*/ | ||||
function createReport(name){ | ||||
report[name] = { | ||||
calls: 0, | ||||
max: 0, | ||||
min: 0, | ||||
avg: 0, | ||||
points: [] | ||||
}; | ||||
} | ||||
/** | ||||
* Called when a method ends execution. Marks the start and end time of the | ||||
* method so it can calculate how long the function took to execute. Also | ||||
* updates min/max/avg calculations for the function. | ||||
* @param {String} name The name of the function to mark as stopped. | ||||
* @param {int} duration The number of milliseconds it took the function to | ||||
* execute. | ||||
* @return {Void} | ||||
* @method saveDataPoint | ||||
* @private | ||||
* @static | ||||
*/ | ||||
function saveDataPoint(name, duration){ | ||||
//get the function data | ||||
var functionData /*:Object*/ = report[name]; | ||||
//just in case clear() was called | ||||
if (!functionData){ | ||||
functionData = createReport(name); | ||||
} | ||||
//increment the calls | ||||
functionData.calls++; | ||||
functionData.points.push(duration); | ||||
//if it's already been called at least once, do more complex calculations | ||||
if (functionData.calls > 1) { | ||||
functionData.avg = ((functionData.avg*(functionData.calls-1))+duration)/functionData.calls; | ||||
functionData.min = Math.min(functionData.min, duration); | ||||
functionData.max = Math.max(functionData.max, duration); | ||||
} else { | ||||
functionData.avg = duration; | ||||
functionData.min = duration; | ||||
functionData.max = duration; | ||||
} | ||||
} | ||||
//------------------------------------------------------------------------- | ||||
// Singleton Object | ||||
//------------------------------------------------------------------------- | ||||
return { | ||||
//------------------------------------------------------------------------- | ||||
// Utility Methods | ||||
//------------------------------------------------------------------------- | ||||
/** | ||||
* Removes all report data from the profiler. | ||||
* @param {String} name (Optional) The name of the report to clear. If | ||||
* omitted, then all report data is cleared. | ||||
* @return {Void} | ||||
* @method clear | ||||
* @static | ||||
*/ | ||||
clear: function(name){ | ||||
if (lang.isString(name)){ | ||||
delete report[name]; | ||||
delete stopwatches[name]; | ||||
} else { | ||||
report = {}; | ||||
stopwatches = {}; | ||||
} | ||||
}, | ||||
/** | ||||
* Returns the uninstrumented version of a function/object. | ||||
* @param {String} name The name of the function/object to retrieve. | ||||
* @return {Function|Object} The uninstrumented version of a function/object. | ||||
* @method getOriginal | ||||
* @static | ||||
*/ | ||||
getOriginal: function(name){ | ||||
return container[name]; | ||||
}, | ||||
/** | ||||
* Instruments a method to have profiling calls. | ||||
* @param {String} name The name of the report for the function. | ||||
* @param {Function} method The function to instrument. | ||||
* @return {Function} An instrumented version of the function. | ||||
* @method instrument | ||||
* @static | ||||
*/ | ||||
instrument: function(name, method){ | ||||
//create instrumented version of function | ||||
var newMethod = function () { | ||||
var start = new Date(), | ||||
retval = method.apply(this, arguments), | ||||
stop = new Date(); | ||||
saveDataPoint(name, stop-start); | ||||
return retval; | ||||
}; | ||||
//copy the function properties over | ||||
lang.augmentObject(newMethod, method); | ||||
//assign prototype and flag as being profiled | ||||
newMethod.__yuiProfiled = true; | ||||
newMethod.prototype = method.prototype; | ||||
//store original method | ||||
container[name] = method; | ||||
container[name].__yuiFuncName = name; | ||||
//create the report | ||||
createReport(name); | ||||
//return the new method | ||||
return newMethod; | ||||
}, | ||||
//------------------------------------------------------------------------- | ||||
// Stopwatch Methods | ||||
//------------------------------------------------------------------------- | ||||
/** | ||||
* Pauses profiling information for a given name. | ||||
* @param {String} name The name of the data point. | ||||
* @return {Void} | ||||
* @method pause | ||||
* @static | ||||
*/ | ||||
pause: function(name){ | ||||
var now = new Date(), | ||||
stopwatch = stopwatches[name]; | ||||
if (stopwatch && stopwatch.state == WATCH_STARTED){ | ||||
stopwatch.total += (now - stopwatch.start); | ||||
stopwatch.start = 0; | ||||
stopwatch.state = WATCH_PAUSED; | ||||
} | ||||
}, | ||||
/** | ||||
* Start profiling information for a given name. The name cannot be the name | ||||
* of a registered function or object. This is used to start timing for a | ||||
* particular block of code rather than instrumenting the entire function. | ||||
* @param {String} name The name of the data point. | ||||
* @return {Void} | ||||
* @method start | ||||
* @static | ||||
*/ | ||||
start: function(name){ | ||||
if(container[name]){ | ||||
throw new Error("Cannot use '" + name + "' for profiling through start(), name is already in use."); | ||||
} else { | ||||
//create report if necessary | ||||
if (!report[name]){ | ||||
createReport(name); | ||||
} | ||||
//create stopwatch object if necessary | ||||
if (!stopwatches[name]){ | ||||
stopwatches[name] = { | ||||
state: WATCH_STOPPED, | ||||
start: 0, | ||||
total: 0 | ||||
}; | ||||
} | ||||
if (stopwatches[name].state == WATCH_STOPPED){ | ||||
stopwatches[name].state = WATCH_STARTED; | ||||
stopwatches[name].start = new Date(); | ||||
} | ||||
} | ||||
}, | ||||
/** | ||||
* Stops profiling information for a given name. | ||||
* @param {String} name The name of the data point. | ||||
* @return {Void} | ||||
* @method stop | ||||
* @static | ||||
*/ | ||||
stop: function(name){ | ||||
var now = new Date(), | ||||
stopwatch = stopwatches[name]; | ||||
if (stopwatch){ | ||||
if (stopwatch.state == WATCH_STARTED){ | ||||
saveDataPoint(name, stopwatch.total + (now - stopwatch.start)); | ||||
} else if (stopwatch.state == WATCH_PAUSED){ | ||||
saveDataPoint(name, stopwatch.total); | ||||
} | ||||
//reset stopwatch information | ||||
stopwatch.start = 0; | ||||
stopwatch.total = 0; | ||||
stopwatch.state = WATCH_STOPPED; | ||||
} | ||||
}, | ||||
//------------------------------------------------------------------------- | ||||
// Reporting Methods | ||||
//------------------------------------------------------------------------- | ||||
/** | ||||
* Returns the average amount of time (in milliseconds) that the function | ||||
* with the given name takes to execute. | ||||
* @param {String} name The name of the function whose data should be returned. | ||||
* If an object type method, it should be 'constructor.prototype.methodName'; | ||||
* a normal object method would just be 'object.methodName'. | ||||
* @return {float} The average time it takes the function to execute. | ||||
* @method getAverage | ||||
* @static | ||||
*/ | ||||
getAverage : function (name /*:String*/) /*:float*/ { | ||||
return report[name].avg; | ||||
}, | ||||
/** | ||||
* Returns the number of times that the given function has been called. | ||||
* @param {String} name The name of the function whose data should be returned. | ||||
* @return {int} The number of times the function was called. | ||||
* @method getCallCount | ||||
* @static | ||||
*/ | ||||
getCallCount : function (name /*:String*/) /*:int*/ { | ||||
return report[name].calls; | ||||
}, | ||||
/** | ||||
* Returns the maximum amount of time (in milliseconds) that the function | ||||
* with the given name takes to execute. | ||||
* @param {String} name The name of the function whose data should be returned. | ||||
* If an object type method, it should be 'constructor.prototype.methodName'; | ||||
* a normal object method would just be 'object.methodName'. | ||||
* @return {float} The maximum time it takes the function to execute. | ||||
* @method getMax | ||||
* @static | ||||
*/ | ||||
getMax : function (name /*:String*/) /*:int*/ { | ||||
return report[name].max; | ||||
}, | ||||
/** | ||||
* Returns the minimum amount of time (in milliseconds) that the function | ||||
* with the given name takes to execute. | ||||
* @param {String} name The name of the function whose data should be returned. | ||||
* If an object type method, it should be 'constructor.prototype.methodName'; | ||||
* a normal object method would just be 'object.methodName'. | ||||
* @return {float} The minimum time it takes the function to execute. | ||||
* @method getMin | ||||
* @static | ||||
*/ | ||||
getMin : function (name /*:String*/) /*:int*/ { | ||||
return report[name].min; | ||||
}, | ||||
/** | ||||
* Returns an object containing profiling data for a single function. | ||||
* The object has an entry for min, max, avg, calls, and points). | ||||
* @return {Object} An object containing profile data for a given function. | ||||
* @method getFunctionReport | ||||
* @static | ||||
* @deprecated Use getReport() instead. | ||||
*/ | ||||
getFunctionReport : function (name /*:String*/) /*:Object*/ { | ||||
return report[name]; | ||||
}, | ||||
/** | ||||
* Returns an object containing profiling data for a single function. | ||||
* The object has an entry for min, max, avg, calls, and points). | ||||
* @return {Object} An object containing profile data for a given function. | ||||
* @method getReport | ||||
* @static | ||||
*/ | ||||
getReport : function (name /*:String*/) /*:Object*/ { | ||||
return report[name]; | ||||
}, | ||||
/** | ||||
* Returns an object containing profiling data for all of the functions | ||||
* that were profiled. The object has an entry for each function and | ||||
* returns all information (min, max, average, calls, etc.) for each | ||||
* function. | ||||
* @return {Object} An object containing all profile data. | ||||
* @static | ||||
*/ | ||||
getFullReport : function (filter /*:Function*/) /*:Object*/ { | ||||
filter = filter || function(){return true;}; | ||||
if (lang.isFunction(filter)) { | ||||
var fullReport = {}; | ||||
for (var name in report){ | ||||
if (filter(report[name])){ | ||||
fullReport[name] = report[name]; | ||||
} | ||||
} | ||||
return fullReport; | ||||
} | ||||
}, | ||||
//------------------------------------------------------------------------- | ||||
// Profiling Methods | ||||
//------------------------------------------------------------------------- | ||||
/** | ||||
* Sets up a constructor for profiling, including all properties and methods on the prototype. | ||||
* @param {string} name The fully-qualified name of the function including namespace information. | ||||
* @param {Object} owner (Optional) The object that owns the function (namespace or containing object). | ||||
* @return {Void} | ||||
* @method registerConstructor | ||||
* @static | ||||
*/ | ||||
registerConstructor : function (name /*:String*/, owner /*:Object*/) /*:Void*/ { | ||||
this.registerFunction(name, owner, true); | ||||
}, | ||||
/** | ||||
* Sets up a function for profiling. It essentially overwrites the function with one | ||||
* that has instrumentation data. This method also creates an entry for the function | ||||
* in the profile report. The original function is stored on the container object. | ||||
* @param {String} name The full name of the function including namespacing. This | ||||
* is the name of the function that is stored in the report. | ||||
* @param {Object} owner (Optional) The object that owns the function. If the function | ||||
* isn't global then this argument is required. This could be the namespace that | ||||
* the function belongs to, such as YAHOO.util.Dom, or the object on which it's | ||||
* a method. | ||||
* @param {Boolean} registerPrototype (Optional) Indicates that the prototype should | ||||
* also be instrumented. Setting to true has the same effect as calling | ||||
* registerConstructor(). | ||||
* @return {Void} | ||||
* @method registerFunction | ||||
* @static | ||||
*/ | ||||
registerFunction : function(name /*:String*/, owner /*:Object*/, registerPrototype /*:Boolean*/) /*:Void*/{ | ||||
//figure out the function name without namespacing | ||||
var funcName = (name.indexOf(".") > -1 ? | ||||
name.substring(name.lastIndexOf(".")+1) : name), | ||||
method, | ||||
prototype; | ||||
//if owner isn't an object, try to find it from the name | ||||
if (!lang.isObject(owner)){ | ||||
owner = eval(name.substring(0, name.lastIndexOf("."))); | ||||
} | ||||
//get the method and prototype | ||||
method = owner[funcName]; | ||||
prototype = method.prototype; | ||||
//see if the method has already been registered | ||||
if (lang.isFunction(method) && !method.__yuiProfiled){ | ||||
//replace the function with the profiling one | ||||
owner[funcName] = this.instrument(name, method); | ||||
/* | ||||
* Store original function information. We store the actual | ||||
* function as well as the owner and the name used to identify | ||||
* the function so it can be restored later. | ||||
*/ | ||||
container[name].__yuiOwner = owner; | ||||
container[name].__yuiFuncName = funcName; //overwrite with less-specific name | ||||
//register prototype if necessary | ||||
if (registerPrototype) { | ||||
this.registerObject(name + ".prototype", prototype); | ||||
} | ||||
} | ||||
}, | ||||
/** | ||||
* Sets up an object for profiling. It takes the object and looks for functions. | ||||
* When a function is found, registerMethod() is called on it. If set to recrusive | ||||
* mode, it will also setup objects found inside of this object for profiling, | ||||
* using the same methodology. | ||||
* @param {String} name The name of the object to profile (shows up in report). | ||||
* @param {Object} owner (Optional) The object represented by the name. | ||||
* @param {Boolean} recurse (Optional) Determines if subobject methods are also profiled. | ||||
* @return {Void} | ||||
* @method registerObject | ||||
* @static | ||||
*/ | ||||
registerObject : function (name /*:String*/, object /*:Object*/, recurse /*:Boolean*/) /*:Void*/{ | ||||
//get the object | ||||
object = (lang.isObject(object) ? object : eval(name)); | ||||
//save the object | ||||
container[name] = object; | ||||
for (var prop in object) { | ||||
if (typeof object[prop] == "function"){ | ||||
if (prop != "constructor" && prop != "superclass"){ //don't do constructor or superclass, it's recursive | ||||
this.registerFunction(name + "." + prop, object); | ||||
} | ||||
} else if (typeof object[prop] == "object" && recurse){ | ||||
this.registerObject(name + "." + prop, object[prop], recurse); | ||||
} | ||||
} | ||||
}, | ||||
/** | ||||
* Removes a constructor function from profiling. Reverses the registerConstructor() method. | ||||
* @param {String} name The full name of the function including namespacing. This | ||||
* is the name of the function that is stored in the report. | ||||
* @return {Void} | ||||
* @method unregisterFunction | ||||
* @static | ||||
*/ | ||||
unregisterConstructor : function(name /*:String*/) /*:Void*/{ | ||||
//see if the method has been registered | ||||
if (lang.isFunction(container[name])){ | ||||
this.unregisterFunction(name, true); | ||||
} | ||||
}, | ||||
/** | ||||
* Removes function from profiling. Reverses the registerFunction() method. | ||||
* @param {String} name The full name of the function including namespacing. This | ||||
* is the name of the function that is stored in the report. | ||||
* @return {Void} | ||||
* @method unregisterFunction | ||||
* @static | ||||
*/ | ||||
unregisterFunction : function(name /*:String*/, unregisterPrototype /*:Boolean*/) /*:Void*/{ | ||||
//see if the method has been registered | ||||
if (lang.isFunction(container[name])){ | ||||
//check to see if you should unregister the prototype | ||||
if (unregisterPrototype){ | ||||
this.unregisterObject(name + ".prototype", container[name].prototype); | ||||
} | ||||
//get original data | ||||
var owner /*:Object*/ = container[name].__yuiOwner, | ||||
funcName /*:String*/ = container[name].__yuiFuncName; | ||||
//delete extra information | ||||
delete container[name].__yuiOwner; | ||||
delete container[name].__yuiFuncName; | ||||
//replace instrumented function | ||||
owner[funcName] = container[name]; | ||||
//delete supporting information | ||||
delete container[name]; | ||||
} | ||||
}, | ||||
/** | ||||
* Unregisters an object for profiling. It takes the object and looks for functions. | ||||
* When a function is found, unregisterMethod() is called on it. If set to recrusive | ||||
* mode, it will also unregister objects found inside of this object, | ||||
* using the same methodology. | ||||
* @param {String} name The name of the object to unregister. | ||||
* @param {Boolean} recurse (Optional) Determines if subobject methods should also be | ||||
* unregistered. | ||||
* @return {Void} | ||||
* @method unregisterObject | ||||
* @static | ||||
*/ | ||||
unregisterObject : function (name /*:String*/, recurse /*:Boolean*/) /*:Void*/{ | ||||
//get the object | ||||
if (lang.isObject(container[name])){ | ||||
var object = container[name]; | ||||
for (var prop in object) { | ||||
if (typeof object[prop] == "function"){ | ||||
this.unregisterFunction(name + "." + prop); | ||||
} else if (typeof object[prop] == "object" && recurse){ | ||||
this.unregisterObject(name + "." + prop, recurse); | ||||
} | ||||
} | ||||
delete container[name]; | ||||
} | ||||
} | ||||
}; | ||||
}(); | ||||
r1073 | YAHOO.register("profiler", YAHOO.tool.Profiler, {version: "2.8.2r1", build: "7"}); | |||