Version: current

      1 <?php
      2 /********************************************************************************\
      3  * Copyright (C) Carl Taylor (cjtaylor@adepteo.com)                             *
      4  * Copyright (C) Torben Nehmer (torben@nehmer.net) for Code Cleanup             *
      5  * Licensed under the BSD license upon request                                  *
      6 \********************************************************************************/
      7
      8 /// Enable multiple timers to aid profiling of performance over sections of code
      9
     10 /**
     11  * Execution time profiler.
     12  *
     13  * @package sapphire
     14  * @subpackage misc
     15  */
     16 class Profiler {
     17     var $description;
     18     var $startTime;
     19     var $endTime;
     20     var $initTime;
     21     var $cur_timer;
     22     var $stack;
     23     var $trail;
     24     var $trace;
     25     var $count;
     26     var $running;
     27
     28     protected static $inst;
     29
     30     /**
     31     * Initialise the timer. with the current micro time
     32     */
     33     function Profiler( $output_enabled=false, $trace_enabled=false)
     34     {
     35         $this->description = array();
     36         $this->startTime = array();
     37         $this->endTime = array();
     38         $this->initTime = 0;
     39         $this->cur_timer = "";
     40         $this->stack = array();
     41         $this->trail = "";
     42         $this->trace = "";
     43         $this->count = array();
     44         $this->running = array();
     45         $this->initTime = $this->getMicroTime();
     46         $this->output_enabled = $output_enabled;
     47         $this->trace_enabled = $trace_enabled;
     48         $this->startTimer('unprofiled');
     49     }
     50
     51     // Public Methods
     52
     53     static function init() {
     54         if(!self::$inst) self::$inst = new Profiler(true,true);
     55     }
     56
     57     static function mark($name, $level2 = "", $desc = "") {
     58         if($level2 && $_GET['debug_profile'] > 1) $name .= " $level2";
     59
     60         if(!self::$inst) self::$inst = new Profiler(true,true);
     61
     62         self::$inst->startTimer($name, $desc);
     63     }
     64     static function unmark($name, $level2 = "", $desc = "") {
     65         if($level2 && $_GET['debug_profile'] > 1) $name .= " $level2";
     66
     67         if(!self::$inst) self::$inst = new Profiler(true,true);
     68
     69         self::$inst->stopTimer($name, $desc);
     70     }
     71     static function show($showTrace = false) {
     72         if(!self::$inst) self::$inst = new Profiler(true,true);
     73
     74         echo "<div style=\"position: absolute; z-index: 100000; top: 20px; left: 20px; background-color: white; padding: 20px; border: 1px #AAA solid; height: 80%; overflow: auto;\">";
     75         echo "<p><a href=\"#\" onclick=\"this.parentNode.parentNode.style.display = 'none'; return false;\">(Click to close)</a></p>";
     76         self::$inst->printTimers();
     77         if($showTrace) self::$inst->printTrace();
     78         echo "</div>";
     79     }
     80
     81     /**
     82     *   Start an individual timer
     83     *   This will pause the running timer and place it on a stack.
     84     *   @param string $name name of the timer
     85     *   @param string optional $desc description of the timer
     86     */
     87     function startTimer($name, $desc="" ){
     88         $this->trace.="start   $name\n";
     89         $n=array_push( $this->stack, $this->cur_timer );
     90         $this->__suspendTimer( $this->stack[$n-1] );
     91         $this->startTime[$name] = $this->getMicroTime();
     92         $this->cur_timer=$name;
     93         $this->description[$name] = $desc;
     94         if (!array_key_exists($name,$this->count))
     95             $this->count[$name] = 1;
     96         else
     97             $this->count[$name]++;
     98     }
     99
    100     /**
    101     *   Stop an individual timer
    102     *   Restart the timer that was running before this one
    103     *   @param string $name name of the timer
    104     */
    105     function stopTimer($name){
    106         $this->trace.="stop    $name\n";
    107         $this->endTime[$name] = $this->getMicroTime();
    108         if (!array_key_exists($name, $this->running))
    109             $this->running[$name] = $this->elapsedTime($name);
    110         else
    111             $this->running[$name] += $this->elapsedTime($name);
    112         $this->cur_timer=array_pop($this->stack);
    113         $this->__resumeTimer($this->cur_timer);
    114     }
    115
    116     /**
    117     *   measure the elapsed time of a timer without stoping the timer if
    118     *   it is still running
    119     */
    120     function elapsedTime($name){
    121         // This shouldn't happen, but it does once.
    122         if (!array_key_exists($name,$this->startTime))
    123             return 0;
    124
    125         if(array_key_exists($name,$this->endTime)){
    126             return ($this->endTime[$name] - $this->startTime[$name]);
    127         } else {
    128             $now=$this->getMicroTime();
    129             return ($now - $this->startTime[$name]);
    130         }
    131     }//end start_time
    132
    133     /**
    134     *   Measure the elapsed time since the profile class was initialised
    135     *
    136     */
    137     function elapsedOverall(){
    138         $oaTime = $this->getMicroTime() - $this->initTime;
    139         return($oaTime);
    140     }//end start_time
    141
    142     /**
    143     *   print out a log of all the timers that were registered
    144     *
    145     */
    146     function printTimers($enabled=false)
    147     {
    148         if($this->output_enabled||$enabled){
    149             $TimedTotal = 0;
    150             $tot_perc = 0;
    151             ksort($this->description);
    152             print("<pre>\n");
    153             $oaTime = $this->getMicroTime() - $this->initTime;
    154             echo"============================================================================\n";
    155             echo "                              PROFILER OUTPUT\n";
    156             echo"============================================================================\n";
    157             print( "Calls                    Time  Routine\n");
    158             echo"-----------------------------------------------------------------------------\n";
    159             while (list ($key, $val) = each ($this->description)) {
    160                 $t = $this->elapsedTime($key);
    161                 $total = $this->running[$key];
    162                 $count = $this->count[$key];
    163                 $TimedTotal += $total;
    164                 $perc = ($total/$oaTime)*100;
    165                 $tot_perc+=$perc;
    166                 // $perc=sprintf("%3.2f", $perc );
    167                 $lines[ sprintf( "%3d    %3.4f ms (%3.2f %%)  %s\n", $count, $total*1000, $perc, $key) ] = $total;
    168             }
    169             arsort($lines);
    170             foreach($lines as $line => $total) {
    171                 echo $line;
    172             }
    173
    174             echo "\n";
    175
    176             $missed=$oaTime-$TimedTotal;
    177             $perc = ($missed/$oaTime)*100;
    178             $tot_perc+=$perc;
    179             // $perc=sprintf("%3.2f", $perc );
    180             printf( "       %3.4f ms (%3.2f %%)  %s\n", $missed*1000,$perc, "Missed");
    181
    182             echo"============================================================================\n";
    183
    184             printf( "       %3.4f ms (%3.2f %%)  %s\n", $oaTime*1000,$tot_perc, "OVERALL TIME");
    185
    186             echo"============================================================================\n";
    187
    188             print("</pre>");
    189         }
    190     }
    191
    192     function printTrace( $enabled=false )
    193     {
    194         if($this->trace_enabled||$enabled){
    195             print("<pre>");
    196             print("Trace\n$this->trace\n\n");
    197             print("</pre>");
    198         }
    199     }
    200
    201     /// Internal Use Only Functions
    202
    203     /**
    204     * Get the current time as accuratly as possible
    205     *
    206     */
    207     function getMicroTime(){
    208         $tmp=explode(' ', microtime());
    209         $rt=$tmp[0]+$tmp[1];
    210         return $rt;
    211     }
    212
    213     /**
    214     * resume  an individual timer
    215     *
    216     */
    217     function __resumeTimer($name){
    218         $this->trace.="resume  $name\n";
    219         $this->startTime[$name] = $this->getMicroTime();
    220     }
    221
    222     /**
    223     *   suspend  an individual timer
    224     *
    225     */
    226     function __suspendTimer($name){
    227         $this->trace.="suspend $name\n";
    228         $this->endTime[$name] = $this->getMicroTime();
    229         if (!array_key_exists($name, $this->running))
    230             $this->running[$name] = $this->elapsedTime($name);
    231         else
    232             $this->running[$name] += $this->elapsedTime($name);
    233     }
    234 }
    235 ?>

[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the Silverstripe Forums to ask development related questions. -