API Docs for: 3.8.0
Show:

File: profiler/js/Profiler.js

  1.     /**
  2.      * The YUI JavaScript profiler.
  3.      * @module profiler
  4.      */
  5.      
  6.     //-------------------------------------------------------------------------
  7.     // Private Variables and Functions
  8.     //-------------------------------------------------------------------------
  9.    
  10.     var container   = {},   //Container object on which to put the original unprofiled methods.
  11.         report      = {},   //Profiling information for functions
  12.         stopwatches = {},   //Additional stopwatch information
  13.        
  14.         WATCH_STARTED   = 0,
  15.         WATCH_STOPPED   = 1,
  16.         WATCH_PAUSED    = 2,    
  17.        
  18.         //shortcuts
  19.         L   = Y.Lang;

  20.     /* (intentionally not documented)
  21.      * Creates a report object with the given name.
  22.      * @param {String} name The name to store for the report object.
  23.      * @return {Void}
  24.      * @method createReport
  25.      * @private
  26.      */
  27.     function createReport(name){
  28.         report[name] = {
  29.             calls: 0,
  30.             max: 0,
  31.             min: 0,
  32.             avg: 0,
  33.             points: []
  34.         };
  35.         return report[name];
  36.     }
  37.    
  38.     /* (intentionally not documented)
  39.      * Called when a method ends execution. Marks the start and end time of the
  40.      * method so it can calculate how long the function took to execute. Also
  41.      * updates min/max/avg calculations for the function.
  42.      * @param {String} name The name of the function to mark as stopped.
  43.      * @param {int} duration The number of milliseconds it took the function to
  44.      *      execute.
  45.      * @return {Void}
  46.      * @method saveDataPoint
  47.      * @private
  48.      * @static
  49.      */
  50.     function saveDataPoint(name, duration){

  51.         //get the function data
  52.         var functionData /*:Object*/ = report[name];
  53.        
  54.         //just in case clear() was called
  55.         if (!functionData){
  56.             functionData = createReport(name);
  57.         }
  58.    
  59.         //increment the calls
  60.         functionData.calls++;
  61.         functionData.points.push(duration);

  62.         //if it's already been called at least once, do more complex calculations
  63.         if (functionData.calls > 1) {
  64.             functionData.avg = ((functionData.avg*(functionData.calls-1))+duration)/functionData.calls;
  65.             functionData.min = Math.min(functionData.min, duration);
  66.             functionData.max = Math.max(functionData.max, duration);
  67.         } else {
  68.             functionData.avg = duration;
  69.             functionData.min = duration;
  70.             functionData.max = duration;
  71.         }                            
  72.    
  73.     }
  74.    
  75.     //-------------------------------------------------------------------------
  76.     // Public Interface
  77.     //-------------------------------------------------------------------------
  78.    
  79.     /**
  80.      * Profiles functions in JavaScript.
  81.      * @class Profiler
  82.      * @static
  83.      */
  84.     Y.Profiler = {
  85.    
  86.         //-------------------------------------------------------------------------
  87.         // Utility Methods
  88.         //-------------------------------------------------------------------------        
  89.        
  90.         /**
  91.          * Removes all report data from the profiler.
  92.          * @param {String} name (Optional) The name of the report to clear. If
  93.          *      omitted, then all report data is cleared.
  94.          * @return {Void}
  95.          * @method clear
  96.          * @static
  97.          */
  98.         clear: function(name){
  99.             if (L.isString(name)){
  100.                 delete report[name];
  101.                 delete stopwatches[name];
  102.             } else {
  103.                 report = {};
  104.                 stopwatches = {};
  105.             }
  106.         },

  107.         /**
  108.          * Returns the uninstrumented version of a function/object.
  109.          * @param {String} name The name of the function/object to retrieve.
  110.          * @return {Function|Object} The uninstrumented version of a function/object.
  111.          * @method getOriginal
  112.          * @static
  113.          */    
  114.         getOriginal: function(name){
  115.             return container[name];
  116.         },
  117.    
  118.         /**
  119.          * Instruments a method to have profiling calls.
  120.          * @param {String} name The name of the report for the function.
  121.          * @param {Function} method The function to instrument.
  122.          * @return {Function} An instrumented version of the function.
  123.          * @method instrument
  124.          * @static
  125.          */
  126.         instrument: function(name, method){
  127.        
  128.             //create instrumented version of function
  129.             var newMethod = function () {
  130.    
  131.                 var start = new Date(),
  132.                     retval = method.apply(this, arguments),
  133.                     stop = new Date();
  134.                
  135.                 saveDataPoint(name, stop-start);
  136.                
  137.                 return retval;                
  138.            
  139.             };    

  140.             //copy the function properties over
  141.             Y.mix(newMethod, method);
  142.            
  143.             //assign prototype and flag as being profiled
  144.             newMethod.__yuiProfiled = true;
  145.             newMethod.prototype = method.prototype;
  146.            
  147.             //store original method
  148.             container[name] = method;
  149.             container[name].__yuiFuncName = name;
  150.            
  151.             //create the report
  152.             createReport(name);

  153.             //return the new method
  154.             return newMethod;
  155.         },    
  156.        
  157.         //-------------------------------------------------------------------------
  158.         // Stopwatch Methods
  159.         //-------------------------------------------------------------------------        
  160.        
  161.         /**
  162.          * Pauses profiling information for a given name.
  163.          * @param {String} name The name of the data point.
  164.          * @return {Void}
  165.          * @method pause
  166.          * @static
  167.          */        
  168.         pause: function(name){
  169.             var now = new Date(),
  170.                 stopwatch = stopwatches[name];
  171.                
  172.             if (stopwatch && stopwatch.state == WATCH_STARTED){
  173.                 stopwatch.total += (now - stopwatch.start);
  174.                 stopwatch.start = 0;
  175.                 stopwatch.state = WATCH_PAUSED;
  176.             }
  177.        
  178.         },
  179.        
  180.         /**
  181.          * Start profiling information for a given name. The name cannot be the name
  182.          * of a registered function or object. This is used to start timing for a
  183.          * particular block of code rather than instrumenting the entire function.
  184.          * @param {String} name The name of the data point.
  185.          * @return {Void}
  186.          * @method start
  187.          * @static
  188.          */
  189.         start: function(name){
  190.             if(container[name]){
  191.                 throw new Error("Cannot use '" + name + "' for profiling through start(), name is already in use.");
  192.             } else {
  193.            
  194.                 //create report if necessary
  195.                 if (!report[name]){
  196.                     createReport(name);
  197.                 }
  198.                
  199.                 //create stopwatch object if necessary
  200.                 if (!stopwatches[name]){            
  201.                     stopwatches[name] = {
  202.                         state: WATCH_STOPPED,
  203.                         start: 0,
  204.                         total: 0
  205.                     };
  206.                 }
  207.                
  208.                 if (stopwatches[name].state == WATCH_STOPPED){
  209.                     stopwatches[name].state = WATCH_STARTED;
  210.                     stopwatches[name].start = new Date();                    
  211.                 }

  212.             }
  213.         },
  214.        
  215.         /**
  216.          * Stops profiling information for a given name.
  217.          * @param {String} name The name of the data point.
  218.          * @return {Void}
  219.          * @method stop
  220.          * @static
  221.          */
  222.         stop: function(name){
  223.             var now = new Date(),
  224.                 stopwatch = stopwatches[name];
  225.                
  226.             if (stopwatch){
  227.                 if (stopwatch.state == WATCH_STARTED){
  228.                     saveDataPoint(name, stopwatch.total + (now - stopwatch.start));                    
  229.                 } else if (stopwatch.state == WATCH_PAUSED){
  230.                     saveDataPoint(name, stopwatch.total);
  231.                 }
  232.                
  233.                 //reset stopwatch information
  234.                 stopwatch.start = 0;
  235.                 stopwatch.total = 0;
  236.                 stopwatch.state = WATCH_STOPPED;                
  237.             }
  238.         },
  239.    
  240.         //-------------------------------------------------------------------------
  241.         // Reporting Methods
  242.         //-------------------------------------------------------------------------    
  243.        
  244.         /**
  245.          * Returns the average amount of time (in milliseconds) that the function
  246.          * with the given name takes to execute.
  247.          * @param {String} name The name of the function whose data should be returned.
  248.          *      If an object type method, it should be 'constructor.prototype.methodName';
  249.          *      a normal object method would just be 'object.methodName'.
  250.          * @return {float} The average time it takes the function to execute.
  251.          * @method getAverage
  252.          * @static
  253.          */
  254.         getAverage : function (name /*:String*/) /*:float*/ {
  255.             return report[name].avg;
  256.         },
  257.    
  258.         /**
  259.          * Returns the number of times that the given function has been called.
  260.          * @param {String} name The name of the function whose data should be returned.
  261.          * @return {int} The number of times the function was called.
  262.          * @method getCallCount
  263.          * @static
  264.          */
  265.         getCallCount : function (name /*:String*/) /*:int*/ {
  266.             return report[name].calls;    
  267.         },
  268.        
  269.         /**
  270.          * Returns the maximum amount of time (in milliseconds) that the function
  271.          * with the given name takes to execute.
  272.          * @param {String} name The name of the function whose data should be returned.
  273.          *      If an object type method, it should be 'constructor.prototype.methodName';
  274.          *      a normal object method would just be 'object.methodName'.
  275.          * @return {float} The maximum time it takes the function to execute.
  276.          * @method getMax
  277.          * @static
  278.          */
  279.         getMax : function (name /*:String*/) /*:int*/ {
  280.             return report[name].max;
  281.         },
  282.        
  283.         /**
  284.          * Returns the minimum amount of time (in milliseconds) that the function
  285.          * with the given name takes to execute.
  286.          * @param {String} name The name of the function whose data should be returned.
  287.          *      If an object type method, it should be 'constructor.prototype.methodName';
  288.          *      a normal object method would just be 'object.methodName'.
  289.          * @return {float} The minimum time it takes the function to execute.
  290.          * @method getMin
  291.          * @static
  292.          */
  293.         getMin : function (name /*:String*/) /*:int*/ {
  294.             return report[name].min;
  295.         },
  296.    
  297.         /**
  298.          * Returns an object containing profiling data for a single function.
  299.          * The object has an entry for min, max, avg, calls, and points).
  300.          * @return {Object} An object containing profile data for a given function.
  301.          * @method getFunctionReport
  302.          * @static
  303.          * @deprecated Use getReport() instead.
  304.          */
  305.         getFunctionReport : function (name /*:String*/) /*:Object*/ {
  306.             return report[name];
  307.         },
  308.    
  309.         /**
  310.          * Returns an object containing profiling data for a single function.
  311.          * The object has an entry for min, max, avg, calls, and points).
  312.          * @return {Object} An object containing profile data for a given function.
  313.          * @method getReport
  314.          * @static
  315.          */
  316.         getReport : function (name /*:String*/) /*:Object*/ {
  317.             return report[name];
  318.         },
  319.    
  320.         /**
  321.          * Returns an object containing profiling data for all of the functions
  322.          * that were profiled. The object has an entry for each function and
  323.          * returns all information (min, max, average, calls, etc.) for each
  324.          * function.
  325.          * @return {Object} An object containing all profile data.
  326.          * @method getFullReport
  327.          * @static
  328.          */
  329.         getFullReport : function (filter /*:Function*/) /*:Object*/ {
  330.             filter = filter || function(){return true;};
  331.        
  332.             if (L.isFunction(filter)) {
  333.                 var fullReport = {};
  334.                
  335.                 for (var name in report){
  336.                     if (filter(report[name])){
  337.                         fullReport[name] = report[name];    
  338.                     }
  339.                 }
  340.                
  341.                 return fullReport;
  342.             }
  343.         },
  344.    
  345.         //-------------------------------------------------------------------------
  346.         // Profiling Methods
  347.         //-------------------------------------------------------------------------  
  348.        
  349.         /**
  350.          * Sets up a constructor for profiling, including all properties and methods on the prototype.
  351.          * @param {string} name The fully-qualified name of the function including namespace information.
  352.          * @param {Object} owner (Optional) The object that owns the function (namespace or containing object).
  353.          * @return {Void}
  354.          * @method registerConstructor
  355.          * @static
  356.          */
  357.         registerConstructor : function (name /*:String*/, owner /*:Object*/) /*:Void*/ {    
  358.             this.registerFunction(name, owner, true);
  359.         },
  360.    
  361.         /**
  362.          * Sets up a function for profiling. It essentially overwrites the function with one
  363.          * that has instrumentation data. This method also creates an entry for the function
  364.          * in the profile report. The original function is stored on the container object.
  365.          * @param {String} name The full name of the function including namespacing. This
  366.          *      is the name of the function that is stored in the report.
  367.          * @param {Object} owner (Optional) The object that owns the function. If the function
  368.          *      isn't global then this argument is required. This could be the namespace that
  369.          *      the function belongs to or the object on which it's
  370.          *      a method.
  371.          * @param {Boolean} registerPrototype (Optional) Indicates that the prototype should
  372.          *      also be instrumented. Setting to true has the same effect as calling
  373.          *      registerConstructor().
  374.          * @return {Void}
  375.          * @method registerFunction
  376.          * @static
  377.          */    
  378.         registerFunction : function(name /*:String*/, owner /*:Object*/, registerPrototype /*:Boolean*/) /*:Void*/{
  379.        
  380.             //figure out the function name without namespacing
  381.             var funcName = (name.indexOf(".") > -1 ?
  382.                     name.substring(name.lastIndexOf(".")+1) : name),
  383.                 method,
  384.                 prototype;
  385.                
  386.             //if owner isn't an object, try to find it from the name
  387.             if (!L.isObject(owner)){
  388.                 owner = eval(name.substring(0, name.lastIndexOf(".")));
  389.             }
  390.            
  391.             //get the method and prototype
  392.             method = owner[funcName];
  393.             prototype = method.prototype;
  394.            
  395.             //see if the method has already been registered
  396.             if (L.isFunction(method) && !method.__yuiProfiled){
  397.                
  398.                 //replace the function with the profiling one
  399.                 owner[funcName] = this.instrument(name, method);
  400.                        
  401.                 /*
  402.                  * Store original function information. We store the actual
  403.                  * function as well as the owner and the name used to identify
  404.                  * the function so it can be restored later.
  405.                  */
  406.                 container[name].__yuiOwner = owner;
  407.                 container[name].__yuiFuncName = funcName;  //overwrite with less-specific name
  408.                  
  409.                 //register prototype if necessary
  410.                 if (registerPrototype) {            
  411.                     this.registerObject(name + ".prototype", prototype);          
  412.                 }
  413.    
  414.             }
  415.        
  416.         },
  417.            
  418.        
  419.         /**
  420.          * Sets up an object for profiling. It takes the object and looks for functions.
  421.          * When a function is found, registerMethod() is called on it. If set to recrusive
  422.          * mode, it will also setup objects found inside of this object for profiling,
  423.          * using the same methodology.
  424.          * @param {String} name The name of the object to profile (shows up in report).
  425.          * @param {Object} owner (Optional) The object represented by the name.
  426.          * @param {Boolean} recurse (Optional) Determines if subobject methods are also profiled.
  427.          * @return {Void}
  428.          * @method registerObject
  429.          * @static
  430.          */
  431.         registerObject : function (name /*:String*/, object /*:Object*/, recurse /*:Boolean*/) /*:Void*/{
  432.        
  433.             //get the object
  434.             object = (L.isObject(object) ? object : eval(name));
  435.        
  436.             //save the object
  437.             container[name] = object;
  438.        
  439.             for (var prop in object) {
  440.                 if (typeof object[prop] == "function"){
  441.                     if (prop != "constructor" && prop != "superclass"){ //don't do constructor or superclass, it's recursive
  442.                         this.registerFunction(name + "." + prop, object);
  443.                     }
  444.                 } else if (typeof object[prop] == "object" && recurse){
  445.                     this.registerObject(name + "." + prop, object[prop], recurse);
  446.                 }
  447.             }
  448.        
  449.         },    
  450.        
  451.         /**
  452.          * Removes a constructor function from profiling. Reverses the registerConstructor() method.
  453.          * @param {String} name The full name of the function including namespacing. This
  454.          *      is the name of the function that is stored in the report.
  455.          * @return {Void}
  456.          * @method unregisterFunction
  457.          * @static
  458.          */    
  459.         unregisterConstructor : function(name /*:String*/) /*:Void*/{
  460.                
  461.             //see if the method has been registered
  462.             if (L.isFunction(container[name])){
  463.                 this.unregisterFunction(name, true);
  464.             }    
  465.         },
  466.        
  467.         /**
  468.          * Removes function from profiling. Reverses the registerFunction() 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.
  471.          * @return {Void}
  472.          * @method unregisterFunction
  473.          * @static
  474.          */    
  475.         unregisterFunction : function(name /*:String*/, unregisterPrototype /*:Boolean*/) /*:Void*/{
  476.                
  477.             //see if the method has been registered
  478.             if (L.isFunction(container[name])){
  479.            
  480.                 //check to see if you should unregister the prototype
  481.                 if (unregisterPrototype){
  482.                     this.unregisterObject(name + ".prototype", container[name].prototype);
  483.                 }
  484.                    
  485.                 //get original data
  486.                 var owner /*:Object*/ = container[name].__yuiOwner,
  487.                     funcName /*:String*/ = container[name].__yuiFuncName;
  488.                    
  489.                 //delete extra information
  490.                 delete container[name].__yuiOwner;
  491.                 delete container[name].__yuiFuncName;
  492.                
  493.                 //replace instrumented function
  494.                 owner[funcName] = container[name];
  495.                
  496.                 //delete supporting information
  497.                 delete container[name];          
  498.             }
  499.                
  500.        
  501.         },
  502.        
  503.         /**
  504.          * Unregisters an object for profiling. It takes the object and looks for functions.
  505.          * When a function is found, unregisterMethod() is called on it. If set to recrusive
  506.          * mode, it will also unregister objects found inside of this object,
  507.          * using the same methodology.
  508.          * @param {String} name The name of the object to unregister.
  509.          * @param {Boolean} recurse (Optional) Determines if subobject methods should also be
  510.          *      unregistered.
  511.          * @return {Void}
  512.          * @method unregisterObject
  513.          * @static
  514.          */
  515.         unregisterObject : function (name /*:String*/, recurse /*:Boolean*/) /*:Void*/{
  516.        
  517.             //get the object
  518.             if (L.isObject(container[name])){            
  519.                 var object = container[name];    
  520.            
  521.                 for (var prop in object) {
  522.                     if (typeof object[prop] == "function"){
  523.                         this.unregisterFunction(name + "." + prop);
  524.                     } else if (typeof object[prop] == "object" && recurse){
  525.                         this.unregisterObject(name + "." + prop, recurse);
  526.                     }
  527.                 }
  528.                
  529.                 delete container[name];
  530.             }
  531.        
  532.         }
  533.              
  534.    
  535.     };
  536.