2
Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3
Code licensed under the BSD License:
4
http://developer.yahoo.com/yui/license.html
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.2.0' ,{requires:['oop']});