2 Copyright (c) 2011, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
7 YAHOO.namespace("tool");
10 * The YUI JavaScript profiler.
12 * @namespace YAHOO.tool
17 * Profiles functions in JavaScript.
18 * @namespace YAHOO.tool
22 YAHOO.tool.Profiler = function(){
25 //-------------------------------------------------------------------------
26 // Private Variables and Functions
27 //-------------------------------------------------------------------------
29 var container = {}, //Container object on which to put the original unprofiled methods.
30 report = {}, //Profiling information for functions
31 stopwatches = {}, //Additional stopwatch information
40 * Creates a report object with the given name.
41 * @param {String} name The name to store for the report object.
43 * @method createReport
46 function createReport(name){
57 * Called when a method ends execution. Marks the start and end time of the
58 * method so it can calculate how long the function took to execute. Also
59 * updates min/max/avg calculations for the function.
60 * @param {String} name The name of the function to mark as stopped.
61 * @param {int} duration The number of milliseconds it took the function to
64 * @method saveDataPoint
68 function saveDataPoint(name, duration){
70 //get the function data
71 var functionData /*:Object*/ = report[name];
73 //just in case clear() was called
75 functionData = createReport(name);
80 functionData.points.push(duration);
82 //if it's already been called at least once, do more complex calculations
83 if (functionData.calls > 1) {
84 functionData.avg = ((functionData.avg*(functionData.calls-1))+duration)/functionData.calls;
85 functionData.min = Math.min(functionData.min, duration);
86 functionData.max = Math.max(functionData.max, duration);
88 functionData.avg = duration;
89 functionData.min = duration;
90 functionData.max = duration;
95 //-------------------------------------------------------------------------
97 //-------------------------------------------------------------------------
101 //-------------------------------------------------------------------------
103 //-------------------------------------------------------------------------
106 * Removes all report data from the profiler.
107 * @param {String} name (Optional) The name of the report to clear. If
108 * omitted, then all report data is cleared.
113 clear: function(name){
114 if (lang.isString(name)){
116 delete stopwatches[name];
124 * Returns the uninstrumented version of a function/object.
125 * @param {String} name The name of the function/object to retrieve.
126 * @return {Function|Object} The uninstrumented version of a function/object.
127 * @method getOriginal
130 getOriginal: function(name){
131 return container[name];
135 * Instruments a method to have profiling calls.
136 * @param {String} name The name of the report for the function.
137 * @param {Function} method The function to instrument.
138 * @return {Function} An instrumented version of the function.
142 instrument: function(name, method){
144 //create instrumented version of function
145 var newMethod = function () {
147 var start = new Date(),
148 retval = method.apply(this, arguments),
151 saveDataPoint(name, stop-start);
157 //copy the function properties over
158 lang.augmentObject(newMethod, method);
160 //assign prototype and flag as being profiled
161 newMethod.__yuiProfiled = true;
162 newMethod.prototype = method.prototype;
164 //store original method
165 container[name] = method;
166 container[name].__yuiFuncName = name;
171 //return the new method
175 //-------------------------------------------------------------------------
177 //-------------------------------------------------------------------------
180 * Pauses profiling information for a given name.
181 * @param {String} name The name of the data point.
186 pause: function(name){
187 var now = new Date(),
188 stopwatch = stopwatches[name];
190 if (stopwatch && stopwatch.state == WATCH_STARTED){
191 stopwatch.total += (now - stopwatch.start);
193 stopwatch.state = WATCH_PAUSED;
199 * Start profiling information for a given name. The name cannot be the name
200 * of a registered function or object. This is used to start timing for a
201 * particular block of code rather than instrumenting the entire function.
202 * @param {String} name The name of the data point.
207 start: function(name){
209 throw new Error("Cannot use '" + name + "' for profiling through start(), name is already in use.");
212 //create report if necessary
217 //create stopwatch object if necessary
218 if (!stopwatches[name]){
219 stopwatches[name] = {
220 state: WATCH_STOPPED,
226 if (stopwatches[name].state == WATCH_STOPPED){
227 stopwatches[name].state = WATCH_STARTED;
228 stopwatches[name].start = new Date();
235 * Stops profiling information for a given name.
236 * @param {String} name The name of the data point.
241 stop: function(name){
242 var now = new Date(),
243 stopwatch = stopwatches[name];
246 if (stopwatch.state == WATCH_STARTED){
247 saveDataPoint(name, stopwatch.total + (now - stopwatch.start));
248 } else if (stopwatch.state == WATCH_PAUSED){
249 saveDataPoint(name, stopwatch.total);
252 //reset stopwatch information
255 stopwatch.state = WATCH_STOPPED;
259 //-------------------------------------------------------------------------
261 //-------------------------------------------------------------------------
264 * Returns the average amount of time (in milliseconds) that the function
265 * with the given name takes to execute.
266 * @param {String} name The name of the function whose data should be returned.
267 * If an object type method, it should be 'constructor.prototype.methodName';
268 * a normal object method would just be 'object.methodName'.
269 * @return {float} The average time it takes the function to execute.
273 getAverage : function (name /*:String*/) /*:float*/ {
274 return report[name].avg;
278 * Returns the number of times that the given function has been called.
279 * @param {String} name The name of the function whose data should be returned.
280 * @return {int} The number of times the function was called.
281 * @method getCallCount
284 getCallCount : function (name /*:String*/) /*:int*/ {
285 return report[name].calls;
289 * Returns the maximum amount of time (in milliseconds) that the function
290 * with the given name takes to execute.
291 * @param {String} name The name of the function whose data should be returned.
292 * If an object type method, it should be 'constructor.prototype.methodName';
293 * a normal object method would just be 'object.methodName'.
294 * @return {float} The maximum time it takes the function to execute.
298 getMax : function (name /*:String*/) /*:int*/ {
299 return report[name].max;
303 * Returns the minimum amount of time (in milliseconds) that the function
304 * with the given name takes to execute.
305 * @param {String} name The name of the function whose data should be returned.
306 * If an object type method, it should be 'constructor.prototype.methodName';
307 * a normal object method would just be 'object.methodName'.
308 * @return {float} The minimum time it takes the function to execute.
312 getMin : function (name /*:String*/) /*:int*/ {
313 return report[name].min;
317 * Returns an object containing profiling data for a single function.
318 * The object has an entry for min, max, avg, calls, and points).
319 * @return {Object} An object containing profile data for a given function.
320 * @method getFunctionReport
322 * @deprecated Use getReport() instead.
324 getFunctionReport : function (name /*:String*/) /*:Object*/ {
329 * Returns an object containing profiling data for a single function.
330 * The object has an entry for min, max, avg, calls, and points).
331 * @return {Object} An object containing profile data for a given function.
335 getReport : function (name /*:String*/) /*:Object*/ {
340 * Returns an object containing profiling data for all of the functions
341 * that were profiled. The object has an entry for each function and
342 * returns all information (min, max, average, calls, etc.) for each
344 * @return {Object} An object containing all profile data.
347 getFullReport : function (filter /*:Function*/) /*:Object*/ {
348 filter = filter || function(){return true;};
350 if (lang.isFunction(filter)) {
353 for (var name in report){
354 if (filter(report[name])){
355 fullReport[name] = report[name];
363 //-------------------------------------------------------------------------
365 //-------------------------------------------------------------------------
368 * Sets up a constructor for profiling, including all properties and methods on the prototype.
369 * @param {string} name The fully-qualified name of the function including namespace information.
370 * @param {Object} owner (Optional) The object that owns the function (namespace or containing object).
372 * @method registerConstructor
375 registerConstructor : function (name /*:String*/, owner /*:Object*/) /*:Void*/ {
376 this.registerFunction(name, owner, true);
380 * Sets up a function for profiling. It essentially overwrites the function with one
381 * that has instrumentation data. This method also creates an entry for the function
382 * in the profile report. The original function is stored on the container object.
383 * @param {String} name The full name of the function including namespacing. This
384 * is the name of the function that is stored in the report.
385 * @param {Object} owner (Optional) The object that owns the function. If the function
386 * isn't global then this argument is required. This could be the namespace that
387 * the function belongs to, such as YAHOO.util.Dom, or the object on which it's
389 * @param {Boolean} registerPrototype (Optional) Indicates that the prototype should
390 * also be instrumented. Setting to true has the same effect as calling
391 * registerConstructor().
393 * @method registerFunction
396 registerFunction : function(name /*:String*/, owner /*:Object*/, registerPrototype /*:Boolean*/) /*:Void*/{
398 //figure out the function name without namespacing
399 var funcName = (name.indexOf(".") > -1 ?
400 name.substring(name.lastIndexOf(".")+1) : name),
404 //if owner isn't an object, try to find it from the name
405 if (!lang.isObject(owner)){
406 owner = eval(name.substring(0, name.lastIndexOf(".")));
409 //get the method and prototype
410 method = owner[funcName];
411 prototype = method.prototype;
413 //see if the method has already been registered
414 if (lang.isFunction(method) && !method.__yuiProfiled){
416 //replace the function with the profiling one
417 owner[funcName] = this.instrument(name, method);
420 * Store original function information. We store the actual
421 * function as well as the owner and the name used to identify
422 * the function so it can be restored later.
424 container[name].__yuiOwner = owner;
425 container[name].__yuiFuncName = funcName; //overwrite with less-specific name
427 //register prototype if necessary
428 if (registerPrototype) {
429 this.registerObject(name + ".prototype", prototype);
438 * Sets up an object for profiling. It takes the object and looks for functions.
439 * When a function is found, registerMethod() is called on it. If set to recrusive
440 * mode, it will also setup objects found inside of this object for profiling,
441 * using the same methodology.
442 * @param {String} name The name of the object to profile (shows up in report).
443 * @param {Object} owner (Optional) The object represented by the name.
444 * @param {Boolean} recurse (Optional) Determines if subobject methods are also profiled.
446 * @method registerObject
449 registerObject : function (name /*:String*/, object /*:Object*/, recurse /*:Boolean*/) /*:Void*/{
452 object = (lang.isObject(object) ? object : eval(name));
455 container[name] = object;
457 for (var prop in object) {
458 if (typeof object[prop] == "function"){
459 if (prop != "constructor" && prop != "superclass"){ //don't do constructor or superclass, it's recursive
460 this.registerFunction(name + "." + prop, object);
462 } else if (typeof object[prop] == "object" && recurse){
463 this.registerObject(name + "." + prop, object[prop], recurse);
470 * Removes a constructor function from profiling. Reverses the registerConstructor() method.
471 * @param {String} name The full name of the function including namespacing. This
472 * is the name of the function that is stored in the report.
474 * @method unregisterFunction
477 unregisterConstructor : function(name /*:String*/) /*:Void*/{
479 //see if the method has been registered
480 if (lang.isFunction(container[name])){
481 this.unregisterFunction(name, true);
486 * Removes function from profiling. Reverses the registerFunction() method.
487 * @param {String} name The full name of the function including namespacing. This
488 * is the name of the function that is stored in the report.
490 * @method unregisterFunction
493 unregisterFunction : function(name /*:String*/, unregisterPrototype /*:Boolean*/) /*:Void*/{
495 //see if the method has been registered
496 if (lang.isFunction(container[name])){
498 //check to see if you should unregister the prototype
499 if (unregisterPrototype){
500 this.unregisterObject(name + ".prototype", container[name].prototype);
504 var owner /*:Object*/ = container[name].__yuiOwner,
505 funcName /*:String*/ = container[name].__yuiFuncName;
507 //delete extra information
508 delete container[name].__yuiOwner;
509 delete container[name].__yuiFuncName;
511 //replace instrumented function
512 owner[funcName] = container[name];
514 //delete supporting information
515 delete container[name];
522 * Unregisters an object for profiling. It takes the object and looks for functions.
523 * When a function is found, unregisterMethod() is called on it. If set to recrusive
524 * mode, it will also unregister objects found inside of this object,
525 * using the same methodology.
526 * @param {String} name The name of the object to unregister.
527 * @param {Boolean} recurse (Optional) Determines if subobject methods should also be
530 * @method unregisterObject
533 unregisterObject : function (name /*:String*/, recurse /*:Boolean*/) /*:Void*/{
536 if (lang.isObject(container[name])){
537 var object = container[name];
539 for (var prop in object) {
540 if (typeof object[prop] == "function"){
541 this.unregisterFunction(name + "." + prop);
542 } else if (typeof object[prop] == "object" && recurse){
543 this.unregisterObject(name + "." + prop, recurse);
547 delete container[name];
557 YAHOO.register("profiler", YAHOO.tool.Profiler, {version: "2.9.0", build: "2800"});