2
Copyright (c) 2008, Yahoo! Inc. All rights reserved.
3
Code licensed under the BSD License:
4
http://developer.yahoo.net/yui/license.txt
7
YUI.add("profiler", function(Y){
12
* The YUI JavaScript profiler.
19
* Profiles functions in JavaScript.
26
//-------------------------------------------------------------------------
28
//-------------------------------------------------------------------------
31
* Container object on which to put the original unprofiled methods.
35
* @property _container
40
* Call information for functions.
48
//-------------------------------------------------------------------------
50
//-------------------------------------------------------------------------
53
* Called when a method ends execution. Marks the start and end time of the
54
* method so it can calculate how long the function took to execute. Also
55
* updates min/max/avg calculations for the function.
56
* @param {String} name The name of the function to mark as stopped.
57
* @param {int} duration The number of milliseconds it took the function to
63
_saveData : function (name /*:String*/, duration /*:int*/){
65
//get the function data
66
var functionData /*:Object*/ = this._report[name];
70
functionData.points.push(duration);
72
//if it's already been called at least once, do more complex calculations
73
if (functionData.calls > 1) {
74
functionData.avg = ((functionData.avg*(functionData.calls-1))+duration)/functionData.calls;
75
functionData.min = Math.min(functionData.min, duration);
76
functionData.max = Math.max(functionData.max, duration);
78
functionData.avg = duration;
79
functionData.min = duration;
80
functionData.max = duration;
85
//-------------------------------------------------------------------------
87
//-------------------------------------------------------------------------
90
* Returns the average amount of time (in milliseconds) that the function
91
* with the given name takes to execute.
92
* @param {String} name The name of the function whose data should be returned.
93
* If an object type method, it should be 'constructor.prototype.methodName';
94
* a normal object method would just be 'object.methodName'.
95
* @return {float} The average time it takes the function to execute.
98
getAverage : function (name /*:String*/) /*:float*/ {
99
return this._report[name].avg;
103
* Returns the number of times that the given function has been called.
104
* @param {String} name The name of the function whose data should be returned.
105
* @return {int} The number of times the function was called.
108
getCallCount : function (name /*:String*/) /*:int*/ {
109
return this._report[name].calls;
113
* Returns the maximum amount of time (in milliseconds) that the function
114
* with the given name takes to execute.
115
* @param {String} name The name of the function whose data should be returned.
116
* If an object type method, it should be 'constructor.prototype.methodName';
117
* a normal object method would just be 'object.methodName'.
118
* @return {float} The maximum time it takes the function to execute.
120
getMax : function (name /*:String*/) /*:int*/ {
121
return this._report[name].max;
125
* Returns the minimum amount of time (in milliseconds) that the function
126
* with the given name takes to execute.
127
* @param {String} name The name of the function whose data should be returned.
128
* If an object type method, it should be 'constructor.prototype.methodName';
129
* a normal object method would just be 'object.methodName'.
130
* @return {float} The minimum time it takes the function to execute.
132
getMin : function (name /*:String*/) /*:int*/ {
133
return this._report[name].min;
137
* Returns an object containing profiling data for a single function.
138
* The object has an entry for min, max, avg, calls, and points).
139
* @return {Object} An object containing profile data for a given function.
142
getFunctionReport : function (name /*:String*/) /*:Object*/ {
143
return this._report[name];
147
* Returns an object containing profiling data for all of the functions
148
* that were profiled. The object has an entry for each function and
149
* returns all information (min, max, average, calls, etc.) for each
151
* @return {Object} An object containing all profile data.
154
getFullReport : function (filter /*:Function*/) /*:Object*/ {
155
filter = filter || function(){return true;};
157
if (typeof filter == "function") {
160
for (var name in this._report){
161
if (filter(this._report[name])){
162
report[name] = this._report[name];
170
//-------------------------------------------------------------------------
172
//-------------------------------------------------------------------------
175
* Sets up a constructor for profiling, including all properties and methods on the prototype.
176
* @param {string} name The fully-qualified name of the function including namespace information.
177
* @param {Object} owner (Optional) The object that owns the function (namespace or containing object).
181
registerConstructor : function (name /*:String*/, owner /*:Object*/) /*:Void*/ {
182
this.registerFunction(name, owner, true);
186
* Sets up a function for profiling. It essentially overwrites the function with one
187
* that has instrumentation data. This method also creates an entry for the function
188
* in the profile report. The original function is stored on the _container object.
189
* @param {String} name The full name of the function including namespacing. This
190
* is the name of the function that is stored in the report.
191
* @param {Object} owner (Optional) The object that owns the function. If the function
192
* isn't global then this argument is required. This could be the namespace that
193
* the function belongs to, such as YAHOO.util.Dom, or the object on which it's
196
* @method registerFunction
198
registerFunction : function(name /*:String*/, owner /*:Object*/, registerPrototype /*:Boolean*/) /*:Void*/{
200
//figure out the function name without namespacing
201
var funcName /*:String*/ = (name.indexOf(".") > -1 ? name.substring(name.lastIndexOf(".")+1) : name);
202
if (!L.isObject(owner)){
203
owner = eval(name.substring(0, name.lastIndexOf(".")));
206
//get the method and prototype
207
var method /*:Function*/ = owner[funcName];
208
var prototype /*:Object*/ = method.prototype;
210
//see if the method has already been registered
211
if (L.isFunction(method) && !method.__yuiProfiled){
213
//create a new slot for the original method
214
this._container[name] = method;
216
//replace the function with the profiling one
217
owner[funcName] = function () {
219
var start = new Date();
220
var retval = method.apply(this, arguments);
221
var stop = new Date();
223
Y.Profiler._saveData(name, stop-start);
229
//copy the function properties over
230
Y.mix(owner[funcName], method);
231
owner[funcName].__yuiProfiled = true;
232
owner[funcName].prototype = prototype;
233
this._container[name].__yuiOwner = owner;
234
this._container[name].__yuiFuncName = funcName;
236
//register prototype if necessary
237
if (registerPrototype) {
238
this.registerObject(name + ".prototype", prototype);
241
//store function information
242
this._report[name] = {
257
* Sets up an object for profiling. It takes the object and looks for functions.
258
* When a function is found, registerMethod() is called on it. If set to recrusive
259
* mode, it will also setup objects found inside of this object for profiling,
260
* using the same methodology.
261
* @param {String} name The name of the object to profile (shows up in report).
262
* @param {Object} owner (Optional) The object represented by the name.
263
* @param {Boolean} recurse (Optional) Determines if subobject methods are also profiled.
267
registerObject : function (name /*:String*/, object /*:Object*/, recurse /*:Boolean*/) /*:Void*/{
270
object = (L.isObject(object) ? object : eval(name));
273
this._container[name] = object;
275
for (var prop in object) {
276
if (typeof object[prop] == "function"){
277
if (prop != "constructor" && prop != "superclass"){ //don't do constructor or superclass, it's recursive
278
this.registerFunction(name + "." + prop, object);
280
} else if (typeof object[prop] == "object" && recurse){
281
this.registerObject(name + "." + prop, object[prop], recurse);
288
* Removes a constructor function from profiling. Reverses the registerConstructor() method.
289
* @param {String} name The full name of the function including namespacing. This
290
* is the name of the function that is stored in the report.
292
* @method unregisterFunction
294
unregisterConstructor : function(name /*:String*/) /*:Void*/{
296
//see if the method has been registered
297
if (L.isFunction(this._container[name])){
300
//var owner /*:Object*/ = this._container[name].__yuiOwner;
301
//var funcName /*:String*/ = this._container[name].__yuiFuncName;
302
//delete this._container[name].__yuiOwner;
303
//delete this._container[name].__yuiFuncName;
305
//replace instrumented function
306
//owner[funcName] = this._container[name];
307
//delete this._container[name];
308
this.unregisterFunction(name, true);
316
* Removes function from profiling. Reverses the registerFunction() method.
317
* @param {String} name The full name of the function including namespacing. This
318
* is the name of the function that is stored in the report.
320
* @method unregisterFunction
322
unregisterFunction : function(name /*:String*/, unregisterPrototype /*:Boolean*/) /*:Void*/{
324
//see if the method has been registered
325
if (L.isFunction(this._container[name])){
327
//check to see if you should unregister the prototype
328
if (unregisterPrototype){
329
this.unregisterObject(name + ".prototype", this._container[name].prototype);
333
var owner /*:Object*/ = this._container[name].__yuiOwner;
334
var funcName /*:String*/ = this._container[name].__yuiFuncName;
335
delete this._container[name].__yuiOwner;
336
delete this._container[name].__yuiFuncName;
338
//replace instrumented function
339
owner[funcName] = this._container[name];
341
//delete supporting information
342
delete this._container[name];
343
delete this._report[name];
351
* Unregisters an object for profiling. It takes the object and looks for functions.
352
* When a function is found, unregisterMethod() is called on it. If set to recrusive
353
* mode, it will also unregister objects found inside of this object,
354
* using the same methodology.
355
* @param {String} name The name of the object to unregister.
356
* @param {Boolean} recurse (Optional) Determines if subobject methods should also be
361
unregisterObject : function (name /*:String*/, recurse /*:Boolean*/) /*:Void*/{
364
if (L.isObject(this._container[name])){
365
var object = this._container[name];
367
for (var prop in object) {
368
if (typeof object[prop] == "function"){
369
this.unregisterFunction(name + "." + prop);
370
} else if (typeof object[prop] == "object" && recurse){
371
this.unregisterObject(name + "." + prop, recurse);
375
delete this._container[name];