Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
75.00% |
6 / 8 |
CRAP | |
55.79% |
53 / 95 |
| iaTimer | |
0.00% |
0 / 1 |
|
75.00% |
6 / 8 |
155.30 | |
55.79% |
53 / 95 |
| start | |
100.00% |
1 / 1 |
3 | |
100.00% |
16 / 16 |
|||
| clear | |
100.00% |
1 / 1 |
1 | |
100.00% |
3 / 3 |
|||
| end | |
100.00% |
1 / 1 |
3 | |
100.00% |
13 / 13 |
|||
| report | n/a |
0 / 0 |
1 | n/a |
0 / 0 |
|||||
| table | n/a |
0 / 0 |
8 | n/a |
0 / 0 |
|||||
| getTimers | |
100.00% |
1 / 1 |
3 | |
100.00% |
8 / 8 |
|||
| cssClases | n/a |
0 / 0 |
1 | n/a |
0 / 0 |
|||||
| requestStart | |
100.00% |
1 / 1 |
3 | |
100.00% |
1 / 1 |
|||
| microtimeFormat | |
100.00% |
1 / 1 |
6 | |
100.00% |
12 / 12 |
|||
| report_rusage | |
0.00% |
0 / 1 |
30.00 | |
0.00% |
0 / 37 |
|||
| ramUsage | |
0.00% |
0 / 1 |
12.00 | |
0.00% |
0 / 5 |
|||
| <?php | |
| /** | |
| * iaTimer. Calculate & report timing for pieces of code, by label | |
| * Each label started more than once, simple stats are reported- | |
| * | |
| * iaTimer::start('s1'); ... iaTimer::end('s1'); ... echo iaTimer::report(); | |
| * Tip init with iaTimer::start("initTimers"); iaTimer::end("initTimers"); | |
| * | |
| * @package dev_tools | |
| * @author Informática Asocaida SA de CV | |
| * @author Raul Jose Santos | |
| * @version 1.1.2 | |
| * @copyright 2015 | |
| * @license MIT | |
| */ | |
| namespace ia\DevTools; | |
| use ia\Util\Str; | |
| use ia\Util\FormatIt; | |
| use ia\Lib\iaTableIt; | |
| /** | |
| * iaTimer. Calculate & report timing for pieces of code, by label | |
| * Each label started more than once, simple stats are reported- | |
| * | |
| * iaTimer::start('s1'); ... iaTimer::end('s1'); ... echo iaTimer::report(); | |
| * Tip init with iaTimer::start("initTimers"); iaTimer::end("initTimers"); | |
| * | |
| */ | |
| class iaTimer { | |
| /** | |
| * @var array saved times keyed by label | |
| * ie [ | |
| * 'label1'=>[ | |
| * 'n'=>number of observations | |
| * 'sum'=>'sum of observed lapsed times, total time | |
| * 'sqSum'=>'sum of squared observred lapsed times' | |
| * 'note'=>'Advice missing start/end calls' | |
| * 't0'=>start time, null on end called | |
| * 'max'=>maximum lapsed time observed | |
| * 'avg'=>average | |
| * 'min'=>minimum lapsed time observed | |
| * 'sdev'=>standard deviation | |
| * ] | |
| * , ... | |
| * ] | |
| */ | |
| static protected $timer = array(); | |
| /** @var bool true at least one label has multiple runs, show basic stats */ | |
| static protected $haveStats = false; | |
| /** | |
| * Indicate start timer named $label. | |
| * | |
| * iaTimer::start('s1'); ....; iaTimer::end('section 1'); | |
| * | |
| * @param string $label identifier for start/stop and report | |
| * @return void | |
| */ | |
| public static function start($label) { | |
| if(!isset(self::$timer[$label])) { | |
| // First time label is seen, set vars and return | |
| self::$timer[$label] = array( | |
| 'n'=>0, | |
| 'sum'=>0, | |
| 'sqSum'=>0, | |
| 'note'=>'', | |
| 't0'=>microtime(true), | |
| 'max'=>-1, | |
| 'min'=>1e9, | |
| ); | |
| return; | |
| } | |
| $t = &self::$timer[$label]; | |
| if($t['t0'] !== null) { | |
| // check if end was called | |
| $t['note'] = "Missing ".__CLASS__."::end($label), called."; | |
| self::end($label); | |
| } | |
| // Repeated label set new start time | |
| $t['t0']=microtime(true); | |
| self::$haveStats = true; | |
| } | |
| /** | |
| * Clears all timers | |
| * | |
| * @return void | |
| */ | |
| public static function clear() { | |
| self::$timer = array(); | |
| self::$haveStats = false; | |
| } | |
| /** | |
| * Indicate end timer named $label. | |
| * | |
| * iaTimer::start('s1'); .... iaTimer::end('section 1'); | |
| * | |
| * @param string $label identifier for start/stop and report | |
| * @return number lapsed time for $label | |
| */ | |
| public static function end($label) { | |
| if(!isset(self::$timer[$label]) || self::$timer[$label]['t0'] === null) { | |
| // $label not started, start it | |
| self::start($label); | |
| self::$timer[$label]['note'] = "Missing ".__CLASS__."::start($label), using requestStart."; | |
| self::$timer[$label]['t0'] = self::requestStart(); | |
| } | |
| // set variables for stats | |
| $t = &self::$timer[$label]; | |
| $t0 = abs(microtime(true) - $t['t0']); | |
| // set auxliary vars for stats | |
| $t['sum'] += $t0; | |
| $t['sqSum'] += $t0 * $t0; | |
| $t['min'] = min($t['min'], $t0); | |
| $t['max'] = max($t['max'], $t0); | |
| $t['n']++; | |
| // flag as end called | |
| $t['t0'] = null; | |
| return $t0; | |
| } | |
| /** | |
| * Returns css and html table with timing information. | |
| * echo iaTimer::report(); | |
| * | |
| * @return string: css and html table with timing information | |
| * | |
| * @codeCoverageIgnore | |
| */ | |
| public static function report($reportMemory = false, $reportRusage = false, $rusageWho = null) { | |
| $requestTime = microtime(true) - self::requestStart(); | |
| return "<style>".self::cssClases()."</style>".self::table($requestTime, $reportMemory, $reportRusage, $rusageWho); | |
| } | |
| /** | |
| * Returns an html table with timing information. | |
| * echo iaTimer::table(); | |
| * | |
| * @return string an html table (class=iaTimerTable) with timing information | |
| * | |
| * @codeCoverageIgnore | |
| */ | |
| public static function table($requestTime=null, $reportMemory = false, $reportRusage = false, $rusageWho = null) { | |
| $memoryUsage = $reportMemory ? self::ramUsage() : ''; | |
| $rUsageTable = $reportRusage ? self::report_rusage($rusageWho) : ''; | |
| if($requestTime === null) | |
| $requestTime = microtime(true) - self::requestStart(); | |
| $colspan = self::$haveStats ? 6 : 1; | |
| // report total time upto now | |
| $table = "<tr><td>Request<td>".self::microtimeFormat(abs($requestTime))."<td colspan=$colspan>"; | |
| self::getTimers(); | |
| foreach(self::$timer as $label => $t) { | |
| if($t['n'] <= 1) { | |
| // one measurment no stats | |
| $table .= "<tr><td>$label<td>".self::microtimeFormat($t['sum'])."<td colspan=$colspan>$t[note]"; | |
| continue; | |
| } | |
| $table .= "<tr><td>$label". | |
| "<td>".self::microtimeFormat($t['max']). | |
| "<td>".self::microtimeFormat($t['avg']). | |
| "<td>".self::microtimeFormat($t['min']). | |
| "<td>".self::microtimeFormat($t['sdev']). | |
| "<td>".number_format($t['n'],0,'.',','). | |
| "<td>".self::microtimeFormat($t['sum']). | |
| "<td>$t[note]"; | |
| } | |
| $header = ""; | |
| if(self::$haveStats) { | |
| $header .= "/Max<th>μ<th>Min<th>σ<th>n<th>∑"; | |
| } | |
| return "<table class='iaTimerTable'> | |
| <caption>iaTimers</caption> | |
| <thead><tr><th>Timer<th>t$header<th>Notes</thead> | |
| <tbody>$table</tbody></table>$memoryUsage$rUsageTable"; | |
| } | |
| /** | |
| * returns timers keyed by label, with basic stats calculated | |
| * | |
| * @return array timers keyed by label basic stats calculated | |
| * ['label1'=>[ | |
| * 'n'=>number of observations | |
| * 'sum'=>'sum of observed lapsed times, total time | |
| * 'sqSum'=>'sum of squared observred lapsed times' | |
| * 'note'=>'Advice missing start/end calls' | |
| * 't0'=>start time, null on end called | |
| * 'max'=>maximum lapsed time observed | |
| * 'avg'=>average | |
| * 'min'=>minimum lapsed time observed | |
| * 'sdev'=>standard deviation | |
| * ],...] | |
| */ | |
| public static function getTimers() { | |
| foreach(self::$timer as $label => &$t) { | |
| if($t['t0'] !== null) { | |
| // end not called, callit | |
| self::end($label); | |
| //$t = self::$timer[$label]; | |
| $t['note'] = "Missing ".__CLASS__."::end($label), issuing it!"; | |
| } | |
| $avg = $t['sum']/$t['n']; | |
| $t['avg'] = $avg; | |
| $t['sdev'] = sqrt(($t['sqSum'] / $t['n']) - ($avg * $avg)); | |
| } | |
| return self::$timer; | |
| } | |
| /** | |
| * Return class used by report. | |
| * echo "< style>".iaTimer::cssCLases()."< /style>"; | |
| * | |
| * @return string css clases used by report | |
| * | |
| * @codeCoverageIgnore | |
| */ | |
| public static function cssClases() { | |
| return " | |
| .iaTimerTable {border:1px silver solid;border-collapse:collapse;margin:1em;} | |
| .iaTimerTable caption {border:1px silver solid; font-weight:bold;} | |
| .iaTimerTable th {padding:0.25em;border:1px silver solid;} | |
| .iaTimerTable td {padding:0.25em;border:1px silver solid;vertical-align:top;text-align:right} | |
| .iaTimerTable td:first-child {text-align:left;font-weight:500;} | |
| .iaTimerTable td:last-child {text-align:left;} | |
| "; | |
| } | |
| /** | |
| * Returns start request time from $_SERVER | |
| * | |
| * @return number start request time from $_SERVER | |
| */ | |
| protected static function requestStart() { | |
| return isset($_SERVER['REQUEST_TIME_FLOAT']) ? $_SERVER['REQUEST_TIME_FLOAT'] : isset($_SERVER['REQUEST_TIME']) ? $_SERVER['REQUEST_TIME'] : microtime(true); | |
| } | |
| /** | |
| * Formats microtime result to strings with time units. | |
| * Limite 23 h 59 min 59 sec 999 ms | |
| * | |
| * @param number $t0 the number from microtime to format (sec.fraction) | |
| * @return string formatted lapsed time in milliseconds | |
| */ | |
| protected static function microtimeFormat($t0) { | |
| if(empty($t0) || !is_numeric($t0)) { | |
| $t0 = 0; | |
| } | |
| if($t0 < 0) { | |
| $t0 = -1 * $t0; | |
| } | |
| if($t0 >= 60*60*24) { | |
| return ">= 1 day not supported"; | |
| } | |
| if($t0 < 1) { | |
| return round($t0,4)." ms"; | |
| } | |
| return Str::strim( str_replace( | |
| array('0 h','00 min','00 sec',' 01',' 02',' 03',' 04',' 05',' 06',' 07',' 08',' 09') | |
| ,array('','','' ,' 1',' 2',' 3',' 4',' 5',' 6',' 7',' 8',' 9') | |
| ,gmDate(' G \h i \m\i\n s \s\e\c '.(round($t0-floor($t0),3)*1000).' \m\s',floor($t0) ) | |
| )); | |
| } | |
| public static function report_rusage($who = null) { | |
| if(!function_exists('getrusage')) { | |
| return ''; | |
| } | |
| $rusage = getrusage($who); | |
| $tit['ru_utime.tv_sec']='CPU user time secs.'; | |
| $tit['ru_utime.tv_usec']='CPU user time microseconds.'; | |
| $tit['ru_stime.tv_sec']='CPU system time secs.'; | |
| $tit['ru_stime.tv_usec']='CPU system time microseconds.'; | |
| $tit['ru_maxrss']='Maximum resident size in Kb.'; | |
| $tit['ru_ixrss']='Integer, amount of memory used by the text segment that was also shared among other processes in kb * ticks-of-execution'; | |
| $tit['ru_idrss']='Integer, amount of unshared memory residing in the data segment of a process in kb * ticks-of-execution'; | |
| $tit['ru_isrss']='Integer, amount of unshared memory residing in the stack segment of a process in kb * ticks-of-execution'; | |
| $tit['ru_minflt']='Number of page faults serviced without any I/O activity'; | |
| $tit['ru_majflt']='Number of page faults serviced that required I/O activity'; | |
| $tit['ru_nswap']='Number of times a process was swapped out of main memory'; | |
| $tit['ru_inblock']='Number of times the file system had to perform input'; | |
| $tit['ru_oublock']='Number of times the file system had to perform output'; | |
| $tit['ru_msgsnd']='Number of IPC messages sent'; | |
| $tit['ru_msgrcv']='Number of IPC messages received'; | |
| $tit['ru_nsignals']='Number of signals delivered'; | |
| $tit['ru_nvcsw']='Number of voluntary context switches'; | |
| $tit['ru_nivcsw']='Number of context switches due to higher priority or time slice exceeded'; | |
| $uni['ru_stime.tv_sec']=$uni['ru_utime.tv_sec']='secs.'; | |
| $uni['ru_stime.tv_usec']=$uni['ru_utime.tv_usec']='microseconds'; | |
| $uni['ru_maxrss']='Kb.'; | |
| $uni['ru_isrss']=$uni['ru_idrss']=$uni['ru_ixrss']='kb * ticks-of-execution'; | |
| $uni['ru_majflt']=$uni['ru_minflt']='pages'; | |
| $uni['ru_inblock']=$uni['ru_oublock']=$uni['ru_nswap']='# veces'; | |
| $uni['ru_msgrcv']=$uni['ru_msgsnd']='# IPC messages'; | |
| $uni['ru_nsignals']='# señales'; | |
| $uni['ru_nivcsw']=$uni['ru_nvcsw']='# context switches'; | |
| $rows = []; | |
| foreach($rusage as $k => $v) { | |
| $rows[] = "<th><span".(array_key_exists($k,$tit) ? " title='$tit[$k]'" : ''). | |
| ">$k</span><td NOWRAP class='der nowrap'>".number_format($v,0,'',',') . | |
| "<td>".(array_key_exists($k,$uni) ? $uni[$k] : ''); | |
| } | |
| return "<table class='iaTimerTable'><caption>rusage</caption><thead><tr><th>Item<th>Value<th>Units</thead><tbody><tr>" . | |
| implode("\r\n<tr>", $rows) . | |
| "</tbody></table>"; | |
| } | |
| /** | |
| * Reports memory usage | |
| * | |
| * @return string a div with memory usage information | |
| */ | |
| private static function ramUsage() { | |
| if(!function_exists('memory_get_usage') || !function_exists('memory_get_peak_usage')) { | |
| return ''; | |
| } | |
| // style='font-family:courier;margin-left:16px;' | |
| return "<table class='iaTimerTable'><caption>Used Memory</caption><thead><tr><th>Memory<th>Used<th>Total Memory<br/>Allocated</thead><tbody>". | |
| "<tr><td>Current<td>".FormatIt::bytes2units( memory_get_usage() )."<td>".FormatIt::bytes2units( memory_get_usage(true) ) . | |
| "<tr><td>Peak<td>".FormatIt::bytes2units( memory_get_peak_usage() )."<td>".FormatIt::bytes2units( memory_get_peak_usage(true) ) . | |
| "</tbody></table>"; | |
| } | |
| } |