You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

193 lines
5.5 KiB

14 years ago
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008-2011 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
/**
* CProfileLogRoute displays the profiling results in Web page.
*
* The profiling is done by calling {@link YiiBase::beginProfile()} and {@link YiiBase::endProfile()},
* which marks the begin and end of a code block.
*
* CProfileLogRoute supports two types of report by setting the {@link setReport report} property:
* <ul>
* <li>summary: list the execution time of every marked code block</li>
* <li>callstack: list the mark code blocks in a hierarchical view reflecting their calling sequence.</li>
* </ul>
*
* @author Qiang Xue <qiang.xue@gmail.com>
13 years ago
* @since 2.0
14 years ago
*/
class CProfileLogRoute extends CWebLogRoute
{
/**
* @var boolean whether to aggregate results according to profiling tokens.
* If false, the results will be aggregated by categories.
* Defaults to true. Note that this property only affects the summary report
* that is enabled when {@link report} is 'summary'.
*/
public $groupByToken = true;
/**
* @var string type of profiling report to display
*/
private $_report = 'summary';
/**
* Initializes the route.
* This method is invoked after the route is created by the route manager.
*/
public function init()
{
$this->levels = CLogger::LEVEL_PROFILE;
}
/**
* @return string the type of the profiling report to display. Defaults to 'summary'.
*/
public function getReport()
{
return $this->_report;
}
/**
* @param string $value the type of the profiling report to display. Valid values include 'summary' and 'callstack'.
*/
public function setReport($value)
{
if ($value === 'summary' || $value === 'callstack')
$this->_report = $value;
else
throw new CException(Yii::t('yii|CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".',
14 years ago
array('{report}' => $value)));
}
/**
* Displays the log messages.
* @param array $logs list of log messages
*/
public function processLogs($logs)
{
$app = \Yii::$app;
14 years ago
if (!($app instanceof CWebApplication) || $app->getRequest()->getIsAjaxRequest())
return;
if ($this->getReport() === 'summary')
$this->displaySummary($logs);
else
$this->displayCallstack($logs);
}
/**
* Displays the callstack of the profiling procedures for display.
* @param array $logs list of logs
*/
protected function displayCallstack($logs)
{
$stack = array();
$results = array();
$n = 0;
foreach ($logs as $log)
{
13 years ago
if ($log[1] !== CLogger::LEVEL_PROFILE) {
14 years ago
continue;
13 years ago
}
14 years ago
$message = $log[0];
13 years ago
if (!strncasecmp($message, 'begin:', 6)) {
14 years ago
$log[0] = substr($message, 6);
$log[4] = $n;
$stack[] = $log;
$n++;
13 years ago
} elseif (!strncasecmp($message, 'end:', 4)) {
14 years ago
$token = substr($message, 4);
13 years ago
if (($last = array_pop($stack)) !== null && $last[0] === $token) {
14 years ago
$delta = $log[3] - $last[3];
$results[$last[4]] = array($token, $delta, count($stack));
13 years ago
} else
13 years ago
{
throw new CException(Yii::t('yii|CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.',
14 years ago
array('{token}' => $token)));
13 years ago
}
14 years ago
}
}
// remaining entries should be closed here
$now = microtime(true);
13 years ago
while (($last = array_pop($stack)) !== null) {
14 years ago
$results[$last[4]] = array($last[0], $now - $last[3], count($stack));
13 years ago
}
14 years ago
ksort($results);
$this->render('profile-callstack', $results);
}
/**
* Displays the summary report of the profiling result.
* @param array $logs list of logs
*/
protected function displaySummary($logs)
{
$stack = array();
foreach ($logs as $log)
{
if ($log[1] !== CLogger::LEVEL_PROFILE)
continue;
$message = $log[0];
if (!strncasecmp($message, 'begin:', 6))
{
$log[0] = substr($message, 6);
$stack[] = $log;
13 years ago
} elseif (!strncasecmp($message, 'end:', 4))
14 years ago
{
$token = substr($message, 4);
if (($last = array_pop($stack)) !== null && $last[0] === $token)
{
$delta = $log[3] - $last[3];
if (!$this->groupByToken)
$token = $log[2];
if (isset($results[$token]))
$results[$token] = $this->aggregateResult($results[$token], $delta);
else
$results[$token] = array($token, 1, $delta, $delta, $delta);
13 years ago
} else
throw new CException(Yii::t('yii|CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.',
14 years ago
array('{token}' => $token)));
}
}
$now = microtime(true);
while (($last = array_pop($stack)) !== null)
{
$delta = $now - $last[3];
$token = $this->groupByToken ? $last[0] : $last[2];
if (isset($results[$token]))
$results[$token] = $this->aggregateResult($results[$token], $delta);
else
$results[$token] = array($token, 1, $delta, $delta, $delta);
}
$entries = array_values($results);
12 years ago
$func = create_function('$a,$b', 'return $a[4] < $b[4] ? 1 : 0;');
14 years ago
usort($entries, $func);
$this->render('profile-summary', $entries);
}
/**
* Aggregates the report result.
* @param array $result log result for this code block
* @param float $delta time spent for this code block
* @return array
*/
protected function aggregateResult($result, $delta)
{
list($token, $calls, $min, $max, $total) = $result;
if ($delta < $min)
$min = $delta;
elseif ($delta > $max)
$max = $delta;
$calls++;
$total += $delta;
return array($token, $calls, $min, $max, $total);
}
}