2 Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
8 YUI.add('profiler', function(Y) {
11 * The YUI JavaScript profiler.
16 //-------------------------------------------------------------------------
17 // Private Variables and Functions
18 //-------------------------------------------------------------------------
20 var container = {}, //Container object on which to put the original unprofiled methods.
21 report = {}, //Profiling information for functions
22 stopwatches = {}, //Additional stopwatch information
31 /* (intentionally not documented)
32 * Creates a report object with the given name.
33 * @param {String} name The name to store for the report object.
35 * @method createReport
38 function createReport(name){
49 /* (intentionally not documented)
50 * Called when a method ends execution. Marks the start and end time of the
51 * method so it can calculate how long the function took to execute. Also
52 * updates min/max/avg calculations for the function.
53 * @param {String} name The name of the function to mark as stopped.
54 * @param {int} duration The number of milliseconds it took the function to
57 * @method saveDataPoint
61 function saveDataPoint(name, duration){
63 //get the function data
64 var functionData /*:Object*/ = report[name];
66 //just in case clear() was called
68 functionData = createReport(name);
73 functionData.points.push(duration);
75 //if it's already been called at least once, do more complex calculations
76 if (functionData.calls > 1) {
77 functionData.avg = ((functionData.avg*(functionData.calls-1))+duration)/functionData.calls;
78 functionData.min = Math.min(functionData.min, duration);
79 functionData.max = Math.max(functionData.max, duration);
81 functionData.avg = duration;
82 functionData.min = duration;
83 functionData.max = duration;
88 //-------------------------------------------------------------------------
90 //-------------------------------------------------------------------------
93 * Profiles functions in JavaScript.
99 //-------------------------------------------------------------------------
101 //-------------------------------------------------------------------------
104 * Removes all report data from the profiler.
105 * @param {String} name (Optional) The name of the report to clear. If
106 * omitted, then all report data is cleared.
111 clear: function(name){
112 if (L.isString(name)){
114 delete stopwatches[name];
122 * Returns the uninstrumented version of a function/object.
123 * @param {String} name The name of the function/object to retrieve.
124 * @return {Function|Object} The uninstrumented version of a function/object.
125 * @method getOriginal
128 getOriginal: function(name){
129 return container[name];
133 * Instruments a method to have profiling calls.
134 * @param {String} name The name of the report for the function.
135 * @param {Function} method The function to instrument.
136 * @return {Function} An instrumented version of the function.
140 instrument: function(name, method){
142 //create instrumented version of function
143 var newMethod = function () {
145 var start = new Date(),
146 retval = method.apply(this, arguments),
149 saveDataPoint(name, stop-start);
155 //copy the function properties over
156 Y.mix(newMethod, method);
158 //assign prototype and flag as being profiled
159 newMethod.__yuiProfiled = true;
160 newMethod.prototype = method.prototype;
162 //store original method
163 container[name] = method;
164 container[name].__yuiFuncName = name;
169 //return the new method
173 //-------------------------------------------------------------------------
175 //-------------------------------------------------------------------------
178 * Pauses profiling information for a given name.
179 * @param {String} name The name of the data point.
184 pause: function(name){
185 var now = new Date(),
186 stopwatch = stopwatches[name];
188 if (stopwatch && stopwatch.state == WATCH_STARTED){
189 stopwatch.total += (now - stopwatch.start);
191 stopwatch.state = WATCH_PAUSED;
197 * Start profiling information for a given name. The name cannot be the name
198 * of a registered function or object. This is used to start timing for a
199 * particular block of code rather than instrumenting the entire function.
200 * @param {String} name The name of the data point.
205 start: function(name){
207 throw new Error("Cannot use '" + name + "' for profiling through start(), name is already in use.");
210 //create report if necessary
215 //create stopwatch object if necessary
216 if (!stopwatches[name]){
217 stopwatches[name] = {
218 state: WATCH_STOPPED,
224 if (stopwatches[name].state == WATCH_STOPPED){
225 stopwatches[name].state = WATCH_STARTED;
226 stopwatches[name].start = new Date();
233 * Stops profiling information for a given name.
234 * @param {String} name The name of the data point.
239 stop: function(name){
240 var now = new Date(),
241 stopwatch = stopwatches[name];
244 if (stopwatch.state == WATCH_STARTED){
245 saveDataPoint(name, stopwatch.total + (now - stopwatch.start));
246 } else if (stopwatch.state == WATCH_PAUSED){
247 saveDataPoint(name, stopwatch.total);
250 //reset stopwatch information
253 stopwatch.state = WATCH_STOPPED;
257 //-------------------------------------------------------------------------
259 //-------------------------------------------------------------------------
262 * Returns the average amount of time (in milliseconds) that the function
263 * with the given name takes to execute.
264 * @param {String} name The name of the function whose data should be returned.
265 * If an object type method, it should be 'constructor.prototype.methodName';
266 * a normal object method would just be 'object.methodName'.
267 * @return {float} The average time it takes the function to execute.
271 getAverage : function (name /*:String*/) /*:float*/ {
272 return report[name].avg;
276 * Returns the number of times that the given function has been called.
277 * @param {String} name The name of the function whose data should be returned.
278 * @return {int} The number of times the function was called.
279 * @method getCallCount
282 getCallCount : function (name /*:String*/) /*:int*/ {
283 return report[name].calls;
287 * Returns the maximum amount of time (in milliseconds) that the function
288 * with the given name takes to execute.
289 * @param {String} name The name of the function whose data should be returned.
290 * If an object type method, it should be 'constructor.prototype.methodName';
291 * a normal object method would just be 'object.methodName'.
292 * @return {float} The maximum time it takes the function to execute.
296 getMax : function (name /*:String*/) /*:int*/ {
297 return report[name].max;
301 * Returns the minimum amount of time (in milliseconds) that the function
302 * with the given name takes to execute.
303 * @param {String} name The name of the function whose data should be returned.
304 * If an object type method, it should be 'constructor.prototype.methodName';
305 * a normal object method would just be 'object.methodName'.
306 * @return {float} The minimum time it takes the function to execute.
310 getMin : function (name /*:String*/) /*:int*/ {
311 return report[name].min;
315 * Returns an object containing profiling data for a single function.
316 * The object has an entry for min, max, avg, calls, and points).
317 * @return {Object} An object containing profile data for a given function.
318 * @method getFunctionReport
320 * @deprecated Use getReport() instead.
322 getFunctionReport : function (name /*:String*/) /*:Object*/ {
327 * Returns an object containing profiling data for a single function.
328 * The object has an entry for min, max, avg, calls, and points).
329 * @return {Object} An object containing profile data for a given function.
333 getReport : function (name /*:String*/) /*:Object*/ {
338 * Returns an object containing profiling data for all of the functions
339 * that were profiled. The object has an entry for each function and
340 * returns all information (min, max, average, calls, etc.) for each
342 * @return {Object} An object containing all profile data.
345 getFullReport : function (filter /*:Function*/) /*:Object*/ {
346 filter = filter || function(){return true;};
348 if (L.isFunction(filter)) {
351 for (var name in report){
352 if (filter(report[name])){
353 fullReport[name] = report[name];
361 //-------------------------------------------------------------------------
363 //-------------------------------------------------------------------------
366 * Sets up a constructor for profiling, including all properties and methods on the prototype.
367 * @param {string} name The fully-qualified name of the function including namespace information.
368 * @param {Object} owner (Optional) The object that owns the function (namespace or containing object).
370 * @method registerConstructor
373 registerConstructor : function (name /*:String*/, owner /*:Object*/) /*:Void*/ {
374 this.registerFunction(name, owner, true);
378 * Sets up a function for profiling. It essentially overwrites the function with one
379 * that has instrumentation data. This method also creates an entry for the function
380 * in the profile report. The original function is stored on the container object.
381 * @param {String} name The full name of the function including namespacing. This
382 * is the name of the function that is stored in the report.
383 * @param {Object} owner (Optional) The object that owns the function. If the function
384 * isn't global then this argument is required. This could be the namespace that
385 * the function belongs to or the object on which it's
387 * @param {Boolean} registerPrototype (Optional) Indicates that the prototype should
388 * also be instrumented. Setting to true has the same effect as calling
389 * registerConstructor().
391 * @method registerFunction
394 registerFunction : function(name /*:String*/, owner /*:Object*/, registerPrototype /*:Boolean*/) /*:Void*/{
396 //figure out the function name without namespacing
397 var funcName = (name.indexOf(".") > -1 ?
398 name.substring(name.lastIndexOf(".")+1) : name),
402 //if owner isn't an object, try to find it from the name
403 if (!L.isObject(owner)){
404 owner = eval(name.substring(0, name.lastIndexOf(".")));
407 //get the method and prototype
408 method = owner[funcName];
409 prototype = method.prototype;
411 //see if the method has already been registered
412 if (L.isFunction(method) && !method.__yuiProfiled){
414 //replace the function with the profiling one
415 owner[funcName] = this.instrument(name, method);
418 * Store original function information. We store the actual
419 * function as well as the owner and the name used to identify
420 * the function so it can be restored later.
422 container[name].__yuiOwner = owner;
423 container[name].__yuiFuncName = funcName; //overwrite with less-specific name
425 //register prototype if necessary
426 if (registerPrototype) {
427 this.registerObject(name + ".prototype", prototype);
436 * Sets up an object for profiling. It takes the object and looks for functions.
437 * When a function is found, registerMethod() is called on it. If set to recrusive
438 * mode, it will also setup objects found inside of this object for profiling,
439 * using the same methodology.
440 * @param {String} name The name of the object to profile (shows up in report).
441 * @param {Object} owner (Optional) The object represented by the name.
442 * @param {Boolean} recurse (Optional) Determines if subobject methods are also profiled.
444 * @method registerObject
447 registerObject : function (name /*:String*/, object /*:Object*/, recurse /*:Boolean*/) /*:Void*/{
450 object = (L.isObject(object) ? object : eval(name));
453 container[name] = object;
455 for (var prop in object) {
456 if (typeof object[prop] == "function"){
457 if (prop != "constructor" && prop != "superclass"){ //don't do constructor or superclass, it's recursive
458 this.registerFunction(name + "." + prop, object);
460 } else if (typeof object[prop] == "object" && recurse){
461 this.registerObject(name + "." + prop, object[prop], recurse);
468 * Removes a constructor function from profiling. Reverses the registerConstructor() method.
469 * @param {String} name The full name of the function including namespacing. This
470 * is the name of the function that is stored in the report.
472 * @method unregisterFunction
475 unregisterConstructor : function(name /*:String*/) /*:Void*/{
477 //see if the method has been registered
478 if (L.isFunction(container[name])){
479 this.unregisterFunction(name, true);
484 * Removes function from profiling. Reverses the registerFunction() method.
485 * @param {String} name The full name of the function including namespacing. This
486 * is the name of the function that is stored in the report.
488 * @method unregisterFunction
491 unregisterFunction : function(name /*:String*/, unregisterPrototype /*:Boolean*/) /*:Void*/{
493 //see if the method has been registered
494 if (L.isFunction(container[name])){
496 //check to see if you should unregister the prototype
497 if (unregisterPrototype){
498 this.unregisterObject(name + ".prototype", container[name].prototype);
502 var owner /*:Object*/ = container[name].__yuiOwner,
503 funcName /*:String*/ = container[name].__yuiFuncName;
505 //delete extra information
506 delete container[name].__yuiOwner;
507 delete container[name].__yuiFuncName;
509 //replace instrumented function
510 owner[funcName] = container[name];
512 //delete supporting information
513 delete container[name];
520 * Unregisters an object for profiling. It takes the object and looks for functions.
521 * When a function is found, unregisterMethod() is called on it. If set to recrusive
522 * mode, it will also unregister objects found inside of this object,
523 * using the same methodology.
524 * @param {String} name The name of the object to unregister.
525 * @param {Boolean} recurse (Optional) Determines if subobject methods should also be
528 * @method unregisterObject
531 unregisterObject : function (name /*:String*/, recurse /*:Boolean*/) /*:Void*/{
534 if (L.isObject(container[name])){
535 var object = container[name];
537 for (var prop in object) {
538 if (typeof object[prop] == "function"){
539 this.unregisterFunction(name + "." + prop);
540 } else if (typeof object[prop] == "object" && recurse){
541 this.unregisterObject(name + "." + prop, recurse);
545 delete container[name];
555 }, '3.0.0' ,{requires:['oop']});