3
Copyright 2012 Yahoo! Inc. All rights reserved.
4
Licensed under the BSD License.
5
http://yuilibrary.com/license/
7
YUI.add('profiler', function(Y) {
10
* The YUI JavaScript profiler.
14
//-------------------------------------------------------------------------
15
// Private Variables and Functions
16
//-------------------------------------------------------------------------
18
var container = {}, //Container object on which to put the original unprofiled methods.
19
report = {}, //Profiling information for functions
20
stopwatches = {}, //Additional stopwatch information
29
/* (intentionally not documented)
30
* Creates a report object with the given name.
31
* @param {String} name The name to store for the report object.
33
* @method createReport
36
function createReport(name){
47
/* (intentionally not documented)
48
* Called when a method ends execution. Marks the start and end time of the
49
* method so it can calculate how long the function took to execute. Also
50
* updates min/max/avg calculations for the function.
51
* @param {String} name The name of the function to mark as stopped.
52
* @param {int} duration The number of milliseconds it took the function to
55
* @method saveDataPoint
59
function saveDataPoint(name, duration){
61
//get the function data
62
var functionData /*:Object*/ = report[name];
64
//just in case clear() was called
66
functionData = createReport(name);
71
functionData.points.push(duration);
73
//if it's already been called at least once, do more complex calculations
74
if (functionData.calls > 1) {
75
functionData.avg = ((functionData.avg*(functionData.calls-1))+duration)/functionData.calls;
76
functionData.min = Math.min(functionData.min, duration);
77
functionData.max = Math.max(functionData.max, duration);
79
functionData.avg = duration;
80
functionData.min = duration;
81
functionData.max = duration;
86
//-------------------------------------------------------------------------
88
//-------------------------------------------------------------------------
91
* Profiles functions in JavaScript.
97
//-------------------------------------------------------------------------
99
//-------------------------------------------------------------------------
102
* Removes all report data from the profiler.
103
* @param {String} name (Optional) The name of the report to clear. If
104
* omitted, then all report data is cleared.
109
clear: function(name){
110
if (L.isString(name)){
112
delete stopwatches[name];
120
* Returns the uninstrumented version of a function/object.
121
* @param {String} name The name of the function/object to retrieve.
122
* @return {Function|Object} The uninstrumented version of a function/object.
123
* @method getOriginal
126
getOriginal: function(name){
127
return container[name];
131
* Instruments a method to have profiling calls.
132
* @param {String} name The name of the report for the function.
133
* @param {Function} method The function to instrument.
134
* @return {Function} An instrumented version of the function.
138
instrument: function(name, method){
140
//create instrumented version of function
141
var newMethod = function () {
143
var start = new Date(),
144
retval = method.apply(this, arguments),
147
saveDataPoint(name, stop-start);
153
//copy the function properties over
154
Y.mix(newMethod, method);
156
//assign prototype and flag as being profiled
157
newMethod.__yuiProfiled = true;
158
newMethod.prototype = method.prototype;
160
//store original method
161
container[name] = method;
162
container[name].__yuiFuncName = name;
167
//return the new method
171
//-------------------------------------------------------------------------
173
//-------------------------------------------------------------------------
176
* Pauses profiling information for a given name.
177
* @param {String} name The name of the data point.
182
pause: function(name){
183
var now = new Date(),
184
stopwatch = stopwatches[name];
186
if (stopwatch && stopwatch.state == WATCH_STARTED){
187
stopwatch.total += (now - stopwatch.start);
189
stopwatch.state = WATCH_PAUSED;
195
* Start profiling information for a given name. The name cannot be the name
196
* of a registered function or object. This is used to start timing for a
197
* particular block of code rather than instrumenting the entire function.
198
* @param {String} name The name of the data point.
203
start: function(name){
205
throw new Error("Cannot use '" + name + "' for profiling through start(), name is already in use.");
208
//create report if necessary
213
//create stopwatch object if necessary
214
if (!stopwatches[name]){
215
stopwatches[name] = {
216
state: WATCH_STOPPED,
222
if (stopwatches[name].state == WATCH_STOPPED){
223
stopwatches[name].state = WATCH_STARTED;
224
stopwatches[name].start = new Date();
231
* Stops profiling information for a given name.
232
* @param {String} name The name of the data point.
237
stop: function(name){
238
var now = new Date(),
239
stopwatch = stopwatches[name];
242
if (stopwatch.state == WATCH_STARTED){
243
saveDataPoint(name, stopwatch.total + (now - stopwatch.start));
244
} else if (stopwatch.state == WATCH_PAUSED){
245
saveDataPoint(name, stopwatch.total);
248
//reset stopwatch information
251
stopwatch.state = WATCH_STOPPED;
255
//-------------------------------------------------------------------------
257
//-------------------------------------------------------------------------
260
* Returns the average amount of time (in milliseconds) that the function
261
* with the given name takes to execute.
262
* @param {String} name The name of the function whose data should be returned.
263
* If an object type method, it should be 'constructor.prototype.methodName';
264
* a normal object method would just be 'object.methodName'.
265
* @return {float} The average time it takes the function to execute.
269
getAverage : function (name /*:String*/) /*:float*/ {
270
return report[name].avg;
274
* Returns the number of times that the given function has been called.
275
* @param {String} name The name of the function whose data should be returned.
276
* @return {int} The number of times the function was called.
277
* @method getCallCount
280
getCallCount : function (name /*:String*/) /*:int*/ {
281
return report[name].calls;
285
* Returns the maximum amount of time (in milliseconds) that the function
286
* with the given name takes to execute.
287
* @param {String} name The name of the function whose data should be returned.
288
* If an object type method, it should be 'constructor.prototype.methodName';
289
* a normal object method would just be 'object.methodName'.
290
* @return {float} The maximum time it takes the function to execute.
294
getMax : function (name /*:String*/) /*:int*/ {
295
return report[name].max;
299
* Returns the minimum amount of time (in milliseconds) that the function
300
* with the given name takes to execute.
301
* @param {String} name The name of the function whose data should be returned.
302
* If an object type method, it should be 'constructor.prototype.methodName';
303
* a normal object method would just be 'object.methodName'.
304
* @return {float} The minimum time it takes the function to execute.
308
getMin : function (name /*:String*/) /*:int*/ {
309
return report[name].min;
313
* Returns an object containing profiling data for a single function.
314
* The object has an entry for min, max, avg, calls, and points).
315
* @return {Object} An object containing profile data for a given function.
316
* @method getFunctionReport
318
* @deprecated Use getReport() instead.
320
getFunctionReport : function (name /*:String*/) /*:Object*/ {
325
* Returns an object containing profiling data for a single function.
326
* The object has an entry for min, max, avg, calls, and points).
327
* @return {Object} An object containing profile data for a given function.
331
getReport : function (name /*:String*/) /*:Object*/ {
336
* Returns an object containing profiling data for all of the functions
337
* that were profiled. The object has an entry for each function and
338
* returns all information (min, max, average, calls, etc.) for each
340
* @return {Object} An object containing all profile data.
341
* @method getFullReport
344
getFullReport : function (filter /*:Function*/) /*:Object*/ {
345
filter = filter || function(){return true;};
347
if (L.isFunction(filter)) {
350
for (var name in report){
351
if (filter(report[name])){
352
fullReport[name] = report[name];
360
//-------------------------------------------------------------------------
362
//-------------------------------------------------------------------------
365
* Sets up a constructor for profiling, including all properties and methods on the prototype.
366
* @param {string} name The fully-qualified name of the function including namespace information.
367
* @param {Object} owner (Optional) The object that owns the function (namespace or containing object).
369
* @method registerConstructor
372
registerConstructor : function (name /*:String*/, owner /*:Object*/) /*:Void*/ {
373
this.registerFunction(name, owner, true);
377
* Sets up a function for profiling. It essentially overwrites the function with one
378
* that has instrumentation data. This method also creates an entry for the function
379
* in the profile report. The original function is stored on the container object.
380
* @param {String} name The full name of the function including namespacing. This
381
* is the name of the function that is stored in the report.
382
* @param {Object} owner (Optional) The object that owns the function. If the function
383
* isn't global then this argument is required. This could be the namespace that
384
* the function belongs to or the object on which it's
386
* @param {Boolean} registerPrototype (Optional) Indicates that the prototype should
387
* also be instrumented. Setting to true has the same effect as calling
388
* registerConstructor().
390
* @method registerFunction
393
registerFunction : function(name /*:String*/, owner /*:Object*/, registerPrototype /*:Boolean*/) /*:Void*/{
395
//figure out the function name without namespacing
396
var funcName = (name.indexOf(".") > -1 ?
397
name.substring(name.lastIndexOf(".")+1) : name),
401
//if owner isn't an object, try to find it from the name
402
if (!L.isObject(owner)){
403
owner = eval(name.substring(0, name.lastIndexOf(".")));
406
//get the method and prototype
407
method = owner[funcName];
408
prototype = method.prototype;
410
//see if the method has already been registered
411
if (L.isFunction(method) && !method.__yuiProfiled){
413
//replace the function with the profiling one
414
owner[funcName] = this.instrument(name, method);
417
* Store original function information. We store the actual
418
* function as well as the owner and the name used to identify
419
* the function so it can be restored later.
421
container[name].__yuiOwner = owner;
422
container[name].__yuiFuncName = funcName; //overwrite with less-specific name
424
//register prototype if necessary
425
if (registerPrototype) {
426
this.registerObject(name + ".prototype", prototype);
435
* Sets up an object for profiling. It takes the object and looks for functions.
436
* When a function is found, registerMethod() is called on it. If set to recrusive
437
* mode, it will also setup objects found inside of this object for profiling,
438
* using the same methodology.
439
* @param {String} name The name of the object to profile (shows up in report).
440
* @param {Object} owner (Optional) The object represented by the name.
441
* @param {Boolean} recurse (Optional) Determines if subobject methods are also profiled.
443
* @method registerObject
446
registerObject : function (name /*:String*/, object /*:Object*/, recurse /*:Boolean*/) /*:Void*/{
449
object = (L.isObject(object) ? object : eval(name));
452
container[name] = object;
454
for (var prop in object) {
455
if (typeof object[prop] == "function"){
456
if (prop != "constructor" && prop != "superclass"){ //don't do constructor or superclass, it's recursive
457
this.registerFunction(name + "." + prop, object);
459
} else if (typeof object[prop] == "object" && recurse){
460
this.registerObject(name + "." + prop, object[prop], recurse);
467
* Removes a constructor function from profiling. Reverses the registerConstructor() method.
468
* @param {String} name The full name of the function including namespacing. This
469
* is the name of the function that is stored in the report.
471
* @method unregisterFunction
474
unregisterConstructor : function(name /*:String*/) /*:Void*/{
476
//see if the method has been registered
477
if (L.isFunction(container[name])){
478
this.unregisterFunction(name, true);
483
* Removes function from profiling. Reverses the registerFunction() method.
484
* @param {String} name The full name of the function including namespacing. This
485
* is the name of the function that is stored in the report.
487
* @method unregisterFunction
490
unregisterFunction : function(name /*:String*/, unregisterPrototype /*:Boolean*/) /*:Void*/{
492
//see if the method has been registered
493
if (L.isFunction(container[name])){
495
//check to see if you should unregister the prototype
496
if (unregisterPrototype){
497
this.unregisterObject(name + ".prototype", container[name].prototype);
501
var owner /*:Object*/ = container[name].__yuiOwner,
502
funcName /*:String*/ = container[name].__yuiFuncName;
504
//delete extra information
505
delete container[name].__yuiOwner;
506
delete container[name].__yuiFuncName;
508
//replace instrumented function
509
owner[funcName] = container[name];
511
//delete supporting information
512
delete container[name];
519
* Unregisters an object for profiling. It takes the object and looks for functions.
520
* When a function is found, unregisterMethod() is called on it. If set to recrusive
521
* mode, it will also unregister objects found inside of this object,
522
* using the same methodology.
523
* @param {String} name The name of the object to unregister.
524
* @param {Boolean} recurse (Optional) Determines if subobject methods should also be
527
* @method unregisterObject
530
unregisterObject : function (name /*:String*/, recurse /*:Boolean*/) /*:Void*/{
533
if (L.isObject(container[name])){
534
var object = container[name];
536
for (var prop in object) {
537
if (typeof object[prop] == "function"){
538
this.unregisterFunction(name + "." + prop);
539
} else if (typeof object[prop] == "object" && recurse){
540
this.unregisterObject(name + "." + prop, recurse);
544
delete container[name];
553
}, '3.5.1' ,{requires:['yui-base']});