ProfileTarget.php 5.48 KB
Newer Older
w  
Qiang Xue committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
<?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>
Alexander Makarov committed
21
 * @since 2.0
w  
Qiang Xue committed
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
 */
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
62
			throw new CException(Yii::t('yii', 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".',
w  
Qiang Xue committed
63 64 65 66 67 68 69 70 71
				array('{report}' => $value)));
	}

	/**
	 * Displays the log messages.
	 * @param array $logs list of log messages
	 */
	public function processLogs($logs)
	{
Qiang Xue committed
72
		$app = \Yii::$app;
w  
Qiang Xue committed
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
		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)
		{
Qiang Xue committed
93
			if ($log[1] !== CLogger::LEVEL_PROFILE) {
w  
Qiang Xue committed
94
				continue;
Qiang Xue committed
95
			}
w  
Qiang Xue committed
96
			$message = $log[0];
Qiang Xue committed
97
			if (!strncasecmp($message, 'begin:', 6)) {
w  
Qiang Xue committed
98 99 100 101
				$log[0] = substr($message, 6);
				$log[4] = $n;
				$stack[] = $log;
				$n++;
Qiang Xue committed
102
			} elseif (!strncasecmp($message, 'end:', 4)) {
w  
Qiang Xue committed
103
				$token = substr($message, 4);
Qiang Xue committed
104
				if (($last = array_pop($stack)) !== null && $last[0] === $token) {
w  
Qiang Xue committed
105 106
					$delta = $log[3] - $last[3];
					$results[$last[4]] = array($token, $delta, count($stack));
Qiang Xue committed
107
				} else
Qiang Xue committed
108
				{
109
					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.',
w  
Qiang Xue committed
110
						array('{token}' => $token)));
Qiang Xue committed
111
				}
w  
Qiang Xue committed
112 113 114 115
			}
		}
		// remaining entries should be closed here
		$now = microtime(true);
Qiang Xue committed
116
		while (($last = array_pop($stack)) !== null) {
w  
Qiang Xue committed
117
			$results[$last[4]] = array($last[0], $now - $last[3], count($stack));
Qiang Xue committed
118
		}
w  
Qiang Xue committed
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
		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;
Qiang Xue committed
139
			} elseif (!strncasecmp($message, 'end:', 4))
w  
Qiang Xue committed
140 141 142 143 144 145 146 147 148 149 150
			{
				$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);
Qiang Xue committed
151
				} else
152
					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.',
w  
Qiang Xue committed
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
						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);
resurtm committed
169
		$func = create_function('$a,$b', 'return $a[4] < $b[4] ? 1 : 0;');
w  
Qiang Xue committed
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
		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);
	}
Zander Baldwin committed
192
}