1: <?php
2: 3: 4: 5: 6: 7: 8: 9:
10:
11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28:
29: class CProfileLogRoute extends CWebLogRoute
30: {
31: 32: 33: 34: 35: 36:
37: public $groupByToken=true;
38: 39: 40:
41: private $_report='summary';
42:
43: 44: 45: 46:
47: public function init()
48: {
49: $this->levels=CLogger::LEVEL_PROFILE;
50: }
51:
52: 53: 54:
55: public function getReport()
56: {
57: return $this->_report;
58: }
59:
60: 61: 62: 63:
64: public function setReport($value)
65: {
66: if($value==='summary' || $value==='callstack')
67: $this->_report=$value;
68: else
69: throw new CException(Yii::t('yii','CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".',
70: array('{report}'=>$value)));
71: }
72:
73: 74: 75: 76:
77: public function processLogs($logs)
78: {
79: $app=Yii::app();
80: if(!($app instanceof CWebApplication) || $app->getRequest()->getIsAjaxRequest())
81: return;
82:
83: if($this->getReport()==='summary')
84: $this->displaySummary($logs);
85: else
86: $this->displayCallstack($logs);
87: }
88:
89: 90: 91: 92: 93:
94: protected function displayCallstack($logs)
95: {
96: $stack=array();
97: $results=array();
98: $n=0;
99: foreach($logs as $log)
100: {
101: if($log[1]!==CLogger::LEVEL_PROFILE)
102: continue;
103: $message=$log[0];
104: if(!strncasecmp($message,'begin:',6))
105: {
106: $log[0]=substr($message,6);
107: $log[4]=$n;
108: $stack[]=$log;
109: $n++;
110: }
111: elseif(!strncasecmp($message,'end:',4))
112: {
113: $token=substr($message,4);
114: if(($last=array_pop($stack))!==null && $last[0]===$token)
115: {
116: $delta=$log[3]-$last[3];
117: $results[$last[4]]=array($token,$delta,count($stack));
118: }
119: else
120: 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.',
121: array('{token}'=>$token)));
122: }
123: }
124:
125: $now=microtime(true);
126: while(($last=array_pop($stack))!==null)
127: $results[$last[4]]=array($last[0],$now-$last[3],count($stack));
128: ksort($results);
129: $this->render('profile-callstack',$results);
130: }
131:
132: 133: 134: 135: 136:
137: protected function displaySummary($logs)
138: {
139: $stack=array();
140: $results=array();
141: foreach($logs as $log)
142: {
143: if($log[1]!==CLogger::LEVEL_PROFILE)
144: continue;
145: $message=$log[0];
146: if(!strncasecmp($message,'begin:',6))
147: {
148: $log[0]=substr($message,6);
149: $stack[]=$log;
150: }
151: elseif(!strncasecmp($message,'end:',4))
152: {
153: $token=substr($message,4);
154: if(($last=array_pop($stack))!==null && $last[0]===$token)
155: {
156: $delta=$log[3]-$last[3];
157: if(!$this->groupByToken)
158: $token=$log[2];
159: if(isset($results[$token]))
160: $results[$token]=$this->aggregateResult($results[$token],$delta);
161: else
162: $results[$token]=array($token,1,$delta,$delta,$delta);
163: }
164: else
165: 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.',
166: array('{token}'=>$token)));
167: }
168: }
169:
170: $now=microtime(true);
171: while(($last=array_pop($stack))!==null)
172: {
173: $delta=$now-$last[3];
174: $token=$this->groupByToken ? $last[0] : $last[2];
175: if(isset($results[$token]))
176: $results[$token]=$this->aggregateResult($results[$token],$delta);
177: else
178: $results[$token]=array($token,1,$delta,$delta,$delta);
179: }
180:
181: $entries=array_values($results);
182: $func=create_function('$a,$b','return $a[4]<$b[4]?1:0;');
183: usort($entries,$func);
184:
185: $this->render('profile-summary',$entries);
186: }
187:
188: 189: 190: 191: 192: 193:
194: protected function aggregateResult($result,$delta)
195: {
196: list($token,$calls,$min,$max,$total)=$result;
197: if($delta<$min)
198: $min=$delta;
199: elseif($delta>$max)
200: $max=$delta;
201: $calls++;
202: $total+=$delta;
203: return array($token,$calls,$min,$max,$total);
204: }
205: }