Commit | Line | Data |
---|---|---|
6af80cae EL |
1 | <?php |
2 | // This file is part of Moodle - http://moodle.org/ | |
3 | // | |
4 | // Moodle is free software: you can redistribute it and/or modify | |
5 | // it under the terms of the GNU General Public License as published by | |
6 | // the Free Software Foundation, either version 3 of the License, or | |
7 | // (at your option) any later version. | |
8 | // | |
9 | // Moodle is distributed in the hope that it will be useful, | |
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | // GNU General Public License for more details. | |
13 | // | |
14 | // You should have received a copy of the GNU General Public License | |
15 | // along with Moodle. If not, see <http://www.gnu.org/licenses/>. | |
16 | ||
17 | /** | |
18 | * @package core | |
19 | * @subpackage profiling | |
20 | * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com} | |
21 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
22 | */ | |
23 | ||
24 | defined('MOODLE_INTERNAL') || die(); | |
25 | ||
26 | // need some stuff from xhprof | |
27 | require_once($CFG->libdir . '/xhprof/xhprof_lib/utils/xhprof_lib.php'); | |
28 | require_once($CFG->libdir . '/xhprof/xhprof_lib/utils/xhprof_runs.php'); | |
29 | // need some stuff from moodle | |
30 | require_once($CFG->libdir.'/tablelib.php'); | |
31 | ||
32 | // TODO: Change the implementation below to proper profiling class | |
33 | ||
34 | /** | |
35 | * Returns if profiling is running, optionally setting it | |
36 | */ | |
37 | function profiling_is_running($value = null) { | |
38 | static $running = null; | |
39 | ||
40 | if (!is_null($value)) { | |
41 | $running = (bool)$value; | |
42 | } | |
43 | ||
44 | return $running; | |
45 | } | |
46 | ||
47 | /** | |
48 | * Returns if profiling has been saved, optionally setting it | |
49 | */ | |
50 | function profiling_is_saved($value = null) { | |
51 | static $saved = null; | |
52 | ||
53 | if (!is_null($value)) { | |
54 | $saved = (bool)$value; | |
55 | } | |
56 | ||
57 | return $saved; | |
58 | } | |
59 | ||
60 | /** | |
61 | * Start profiling observing all the configuration | |
62 | */ | |
63 | function profiling_start() { | |
64 | global $CFG, $SESSION, $SCRIPT; | |
65 | ||
66 | // If profiling isn't available, nothing to start | |
67 | if (!extension_loaded('xhprof') || !function_exists('xhprof_enable')) { | |
68 | return false; | |
69 | } | |
70 | ||
71 | // If profiling isn't enabled, nothing to start | |
c646dd26 | 72 | if (empty($CFG->profilingenabled) && empty($CFG->earlyprofilingenabled)) { |
6af80cae EL |
73 | return false; |
74 | } | |
75 | ||
76 | // If profiling is already running or saved, nothing to start | |
77 | if (profiling_is_running() || profiling_is_saved()) { | |
78 | return false; | |
79 | } | |
80 | ||
c646dd26 EL |
81 | // Set script (from global if available, else our own) |
82 | $script = !empty($SCRIPT) ? $SCRIPT : profiling_get_script(); | |
83 | ||
6af80cae EL |
84 | // Get PGC variables |
85 | $check = 'PROFILEME'; | |
86 | $profileme = isset($_POST[$check]) || isset($_GET[$check]) || isset($_COOKIE[$check]) ? true : false; | |
c646dd26 | 87 | $profileme = $profileme && !empty($CFG->profilingallowme); |
6af80cae EL |
88 | $check = 'DONTPROFILEME'; |
89 | $dontprofileme = isset($_POST[$check]) || isset($_GET[$check]) || isset($_COOKIE[$check]) ? true : false; | |
c646dd26 | 90 | $dontprofileme = $dontprofileme && !empty($CFG->profilingallowme); |
6af80cae EL |
91 | $check = 'PROFILEALL'; |
92 | $profileall = isset($_POST[$check]) || isset($_GET[$check]) || isset($_COOKIE[$check]) ? true : false; | |
c646dd26 | 93 | $profileall = $profileall && !empty($CFG->profilingallowall); |
6af80cae EL |
94 | $check = 'PROFILEALLSTOP'; |
95 | $profileallstop = isset($_POST[$check]) || isset($_GET[$check]) || isset($_COOKIE[$check]) ? true : false; | |
c646dd26 | 96 | $profileallstop = $profileallstop && !empty($CFG->profilingallowall); |
6af80cae EL |
97 | |
98 | // DONTPROFILEME detected, nothing to start | |
99 | if ($dontprofileme) { | |
100 | return false; | |
101 | } | |
102 | ||
103 | // PROFILEALLSTOP detected, clean the mark in seesion and continue | |
c646dd26 | 104 | if ($profileallstop && !empty($SESSION)) { |
6af80cae EL |
105 | unset($SESSION->profileall); |
106 | } | |
107 | ||
108 | // PROFILEALL detected, set the mark in session and continue | |
c646dd26 | 109 | if ($profileall && !empty($SESSION)) { |
6af80cae EL |
110 | $SESSION->profileall = true; |
111 | ||
112 | // SESSION->profileall detected, set $profileall | |
113 | } else if (!empty($SESSION->profileall)) { | |
114 | $profileall = true; | |
115 | } | |
116 | ||
117 | // Evaluate automatic (random) profiling if necessary | |
118 | $profileauto = false; | |
c646dd26 | 119 | if (!empty($CFG->profilingautofrec)) { |
6af80cae EL |
120 | $profileauto = (mt_rand(1, $CFG->profilingautofrec) === 1); |
121 | } | |
122 | ||
c646dd26 EL |
123 | // See if the $script matches any of the included patterns |
124 | $included = empty($CFG->profilingincluded) ? '' : $CFG->profilingincluded; | |
125 | $profileincluded = profiling_string_matches($script, $included); | |
6af80cae | 126 | |
c646dd26 EL |
127 | // See if the $script matches any of the excluded patterns |
128 | $excluded = empty($CFG->profilingexcluded) ? '' : $CFG->profilingexcluded; | |
129 | $profileexcluded = profiling_string_matches($script, $excluded); | |
6af80cae EL |
130 | |
131 | // Decide if profile auto must happen (observe matchings) | |
132 | $profileauto = $profileauto && $profileincluded && !$profileexcluded; | |
133 | ||
134 | // Decide if profile by match must happen (only if profileauto is disabled) | |
c646dd26 | 135 | $profilematch = $profileincluded && !$profileexcluded && empty($CFG->profilingautofrec); |
6af80cae EL |
136 | |
137 | // If not auto, me, all, match have been detected, nothing to do | |
138 | if (!$profileauto && !$profileme && !$profileall && !$profilematch) { | |
139 | return false; | |
140 | } | |
141 | ||
142 | // Arrived here, the script is going to be profiled, let's do it | |
143 | $ignore = array('call_user_func', 'call_user_func_array'); | |
144 | xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY, array('ignored_functions' => $ignore)); | |
145 | profiling_is_running(true); | |
c646dd26 EL |
146 | |
147 | // Started, return true | |
148 | return true; | |
6af80cae EL |
149 | } |
150 | ||
151 | /** | |
152 | * Stop profiling, gathering results and storing them | |
153 | */ | |
154 | function profiling_stop() { | |
68fabb5c | 155 | global $CFG, $DB, $SCRIPT; |
6af80cae EL |
156 | |
157 | // If profiling isn't available, nothing to stop | |
158 | if (!extension_loaded('xhprof') || !function_exists('xhprof_enable')) { | |
159 | return false; | |
160 | } | |
161 | ||
162 | // If profiling isn't enabled, nothing to stop | |
c646dd26 | 163 | if (empty($CFG->profilingenabled) && empty($CFG->earlyprofilingenabled)) { |
6af80cae EL |
164 | return false; |
165 | } | |
166 | ||
167 | // If profiling is not running or is already saved, nothing to stop | |
168 | if (!profiling_is_running() || profiling_is_saved()) { | |
169 | return false; | |
170 | } | |
171 | ||
c646dd26 EL |
172 | // Set script (from global if available, else our own) |
173 | $script = !empty($SCRIPT) ? $SCRIPT : profiling_get_script(); | |
174 | ||
6af80cae EL |
175 | // Arrived here, profiling is running, stop and save everything |
176 | profiling_is_running(false); | |
177 | $data = xhprof_disable(); | |
178 | ||
68fabb5c EL |
179 | // We only save the run after ensuring the DB table exists |
180 | // (this prevents problems with profiling runs enabled in | |
181 | // config.php before Moodle is installed. Rare but... | |
182 | $tables = $DB->get_tables(); | |
183 | if (!in_array('profiling', $tables)) { | |
184 | return false; | |
185 | } | |
186 | ||
6af80cae | 187 | $run = new moodle_xhprofrun(); |
c646dd26 | 188 | $run->prepare_run($script); |
6af80cae EL |
189 | $runid = $run->save_run($data, null); |
190 | profiling_is_saved(true); | |
191 | ||
192 | // Prune old runs | |
193 | profiling_prune_old_runs($runid); | |
c646dd26 EL |
194 | |
195 | // Finished, return true | |
196 | return true; | |
6af80cae EL |
197 | } |
198 | ||
199 | function profiling_prune_old_runs($exception = 0) { | |
200 | global $CFG, $DB; | |
201 | ||
202 | // Setting to 0 = no prune | |
203 | if (empty($CFG->profilinglifetime)) { | |
204 | return; | |
205 | } | |
206 | ||
207 | $cuttime = time() - ($CFG->profilinglifetime * 60); | |
208 | $params = array('cuttime' => $cuttime, 'exception' => $exception); | |
209 | ||
210 | $DB->delete_records_select('profiling', 'runreference = 0 AND | |
211 | timecreated < :cuttime AND | |
212 | runid != :exception', $params); | |
213 | } | |
214 | ||
c646dd26 EL |
215 | /** |
216 | * Returns the path to the php script being requested | |
217 | * | |
218 | * Note this function is a partial copy of initialise_fullme() and | |
219 | * setup_get_remote_url(), in charge of setting $FULLME, $SCRIPT and | |
220 | * friends. To be used by early profiling runs in situations where | |
221 | * $SCRIPT isn't defined yet | |
222 | * | |
223 | * @return string absolute path (wwwroot based) of the script being executed | |
224 | */ | |
225 | function profiling_get_script() { | |
226 | global $CFG; | |
227 | ||
228 | $wwwroot = parse_url($CFG->wwwroot); | |
229 | ||
230 | if (!isset($wwwroot['path'])) { | |
231 | $wwwroot['path'] = ''; | |
232 | } | |
233 | $wwwroot['path'] .= '/'; | |
234 | ||
235 | $path = $_SERVER['SCRIPT_NAME']; | |
236 | ||
237 | if (strpos($path, $wwwroot['path']) === 0) { | |
238 | return substr($path, strlen($wwwroot['path']) - 1); | |
239 | } | |
240 | return ''; | |
241 | } | |
242 | ||
6af80cae EL |
243 | function profiling_urls($report, $runid, $runid2 = null) { |
244 | global $CFG; | |
245 | ||
246 | $url = ''; | |
247 | switch ($report) { | |
248 | case 'run': | |
249 | $url = $CFG->wwwroot . '/lib/xhprof/xhprof_html/index.php?run=' . $runid; | |
250 | break; | |
251 | case 'diff': | |
252 | $url = $CFG->wwwroot . '/lib/xhprof/xhprof_html/index.php?run1=' . $runid . '&run2=' . $runid2; | |
253 | break; | |
254 | case 'graph': | |
255 | $url = $CFG->wwwroot . '/lib/xhprof/xhprof_html/callgraph.php?run=' . $runid; | |
256 | break; | |
257 | } | |
258 | return $url; | |
259 | } | |
260 | ||
261 | function profiling_print_run($run, $prevrunid = null) { | |
262 | global $CFG, $OUTPUT; | |
263 | ||
264 | $output = ''; | |
265 | ||
266 | // Prepare the runreference/runcomment form | |
267 | $checked = $run->runreference ? ' checked=checked' : ''; | |
268 | $referenceform = "<form id=\"profiling_runreference\" action=\"index.php\" method=\"GET\">" . | |
269 | "<input type=\"hidden\" name=\"sesskey\" value=\"" . sesskey() . "\"/>". | |
270 | "<input type=\"hidden\" name=\"runid\" value=\"$run->runid\"/>". | |
271 | "<input type=\"hidden\" name=\"listurl\" value=\"$run->url\"/>". | |
272 | "<input type=\"checkbox\" name=\"runreference\" value=\"1\"$checked/> ". | |
273 | "<input type=\"text\" name=\"runcomment\" value=\"$run->runcomment\"/> ". | |
274 | "<input type=\"submit\" value=\"" . get_string('savechanges') ."\"/>". | |
275 | "</form>"; | |
276 | ||
277 | $table = new html_table(); | |
278 | $table->align = array('right', 'left'); | |
279 | $table->tablealign = 'center'; | |
280 | $table->attributes['class'] = 'profilingruntable'; | |
281 | $table->colclasses = array('label', 'value'); | |
282 | $table->data = array( | |
187536f6 | 283 | array(get_string('runid', 'tool_profiling'), $run->runid), |
6af80cae EL |
284 | array(get_string('url'), $run->url), |
285 | array(get_string('date'), userdate($run->timecreated, '%d %B %Y, %H:%M')), | |
187536f6 PS |
286 | array(get_string('executiontime', 'tool_profiling'), format_float($run->totalexecutiontime / 1000, 3) . ' ms'), |
287 | array(get_string('cputime', 'tool_profiling'), format_float($run->totalcputime / 1000, 3) . ' ms'), | |
288 | array(get_string('calls', 'tool_profiling'), $run->totalcalls), | |
289 | array(get_string('memory', 'tool_profiling'), format_float($run->totalmemory / 1024, 0) . ' KB'), | |
290 | array(get_string('markreferencerun', 'tool_profiling'), $referenceform)); | |
6af80cae EL |
291 | $output = $OUTPUT->box(html_writer::table($table), 'generalbox boxwidthwide boxaligncenter profilingrunbox', 'profiling_summary', true); |
292 | // Add link to details | |
187536f6 | 293 | $strviewdetails = get_string('viewdetails', 'tool_profiling'); |
6af80cae EL |
294 | $url = profiling_urls('run', $run->runid); |
295 | $output.=$OUTPUT->heading('<a href="' . $url . '" onclick="javascript:window.open(' . "'" . $url . "'" . ');' . | |
296 | 'return false;"' . ' title="">' . $strviewdetails . '</a>', 3, 'main profilinglink'); | |
297 | // If there is one previous run marked as reference, add link to diff | |
298 | if ($prevrunid) { | |
187536f6 | 299 | $strviewdiff = get_string('viewdiff', 'tool_profiling'); |
6af80cae EL |
300 | $url = 'index.php?runid=' . $run->runid . '&runid2=' . $prevrunid . '&listurl=' . urlencode($run->url); |
301 | $output.=$OUTPUT->heading('<a href="' . $url . '" title="">' . $strviewdiff . '</a>', 3, 'main profilinglink'); | |
302 | } | |
303 | ||
304 | return $output; | |
305 | } | |
306 | ||
307 | function profiling_print_rundiff($run1, $run2) { | |
308 | global $CFG, $OUTPUT; | |
309 | ||
310 | $output = ''; | |
311 | ||
312 | // Prepare the reference/comment information | |
313 | $referencetext1 = ($run1->runreference ? get_string('yes') : get_string('no')) . | |
314 | ($run1->runcomment ? ' - ' . s($run1->runcomment) : ''); | |
315 | $referencetext2 = ($run2->runreference ? get_string('yes') : get_string('no')) . | |
316 | ($run2->runcomment ? ' - ' . s($run2->runcomment) : ''); | |
317 | ||
318 | // Calculate global differences | |
319 | $diffexecutiontime = profiling_get_difference($run1->totalexecutiontime, $run2->totalexecutiontime, 'ms', 1000); | |
320 | $diffcputime = profiling_get_difference($run1->totalcputime, $run2->totalcputime, 'ms', 1000); | |
321 | $diffcalls = profiling_get_difference($run1->totalcalls, $run2->totalcalls); | |
322 | $diffmemory = profiling_get_difference($run1->totalmemory, $run2->totalmemory, 'KB', 1024); | |
323 | ||
324 | $table = new html_table(); | |
325 | $table->align = array('right', 'left', 'left', 'left'); | |
326 | $table->tablealign = 'center'; | |
327 | $table->attributes['class'] = 'profilingruntable'; | |
328 | $table->colclasses = array('label', 'value1', 'value2'); | |
329 | $table->data = array( | |
187536f6 | 330 | array(get_string('runid', 'tool_profiling'), |
6af80cae EL |
331 | '<a href="index.php?runid=' . $run1->runid . '&listurl=' . urlencode($run1->url) . '" title="">' . $run1->runid . '</a>', |
332 | '<a href="index.php?runid=' . $run2->runid . '&listurl=' . urlencode($run2->url) . '" title="">' . $run2->runid . '</a>'), | |
333 | array(get_string('url'), $run1->url, $run2->url), | |
334 | array(get_string('date'), userdate($run1->timecreated, '%d %B %Y, %H:%M'), | |
335 | userdate($run2->timecreated, '%d %B %Y, %H:%M')), | |
187536f6 | 336 | array(get_string('executiontime', 'tool_profiling'), |
6af80cae EL |
337 | format_float($run1->totalexecutiontime / 1000, 3) . ' ms', |
338 | format_float($run2->totalexecutiontime / 1000, 3) . ' ms ' . $diffexecutiontime), | |
187536f6 | 339 | array(get_string('cputime', 'tool_profiling'), |
6af80cae EL |
340 | format_float($run1->totalcputime / 1000, 3) . ' ms', |
341 | format_float($run2->totalcputime / 1000, 3) . ' ms ' . $diffcputime), | |
187536f6 PS |
342 | array(get_string('calls', 'tool_profiling'), $run1->totalcalls, $run2->totalcalls . ' ' . $diffcalls), |
343 | array(get_string('memory', 'tool_profiling'), | |
6af80cae EL |
344 | format_float($run1->totalmemory / 1024, 0) . ' KB', |
345 | format_float($run2->totalmemory / 1024, 0) . ' KB ' . $diffmemory), | |
187536f6 | 346 | array(get_string('referencerun', 'tool_profiling'), $referencetext1, $referencetext2)); |
6af80cae EL |
347 | $output = $OUTPUT->box(html_writer::table($table), 'generalbox boxwidthwide boxaligncenter profilingrunbox', 'profiling_summary', true); |
348 | // Add link to details | |
187536f6 | 349 | $strviewdetails = get_string('viewdiffdetails', 'tool_profiling'); |
6af80cae | 350 | $url = profiling_urls('diff', $run1->runid, $run2->runid); |
187536f6 | 351 | //$url = $CFG->wwwroot . '/admin/tool/profiling/index.php?run=' . $run->runid; |
6af80cae EL |
352 | $output.=$OUTPUT->heading('<a href="' . $url . '" onclick="javascript:window.open(' . "'" . $url . "'" . ');' . |
353 | 'return false;"' . ' title="">' . $strviewdetails . '</a>', 3, 'main profilinglink'); | |
354 | return $output; | |
355 | } | |
356 | ||
357 | /** | |
358 | * Helper function that returns the HTML fragment to | |
359 | * be displayed on listing mode, it includes actions | |
360 | * like deletion/export/import... | |
361 | */ | |
362 | function profiling_list_controls($listurl) { | |
363 | global $CFG, $OUTPUT; | |
364 | ||
365 | $output = ''; | |
366 | ||
367 | return $output; | |
368 | } | |
369 | ||
370 | /** | |
371 | * Helper function that looks for matchings of one string | |
372 | * against an array of * wildchar patterns | |
373 | */ | |
374 | function profiling_string_matches($string, $patterns) { | |
375 | $patterns = explode(',', $patterns); | |
376 | foreach ($patterns as $pattern) { | |
377 | // Trim and prepare pattern | |
378 | $pattern = str_replace('\*', '.*', preg_quote(trim($pattern), '~')); | |
379 | // Don't process empty patterns | |
380 | if (empty($pattern)) { | |
381 | continue; | |
382 | } | |
383 | if (preg_match('~' . $pattern . '~', $string)) { | |
384 | return true; | |
385 | } | |
386 | } | |
387 | return false; | |
388 | } | |
389 | ||
390 | /** | |
391 | * Helper function that, given to floats, returns their numerical | |
392 | * and percentual differences, propertly formated and cssstyled | |
393 | */ | |
394 | function profiling_get_difference($number1, $number2, $units = '', $factor = 1, $numdec = 2) { | |
395 | $numdiff = $number2 - $number1; | |
396 | $perdiff = 0; | |
397 | if ($number1 != $number2) { | |
398 | $perdiff = $number1 != 0 ? ($number2 * 100 / $number1) - 100 : 0; | |
399 | } | |
400 | $sign = $number2 > $number1 ? '+' : ''; | |
401 | $delta = abs($perdiff) > 0.25 ? 'Δ' : '≈'; | |
402 | $spanclass = $number2 > $number1 ? 'worse' : ($number1 > $number2 ? 'better' : 'same'); | |
403 | $importantclass= abs($perdiff) > 1 ? ' profiling_important' : ''; | |
404 | $startspan = '<span class="profiling_' . $spanclass . $importantclass . '">'; | |
405 | $endspan = '</span>'; | |
406 | $fnumdiff = $sign . format_float($numdiff / $factor, $numdec); | |
407 | $fperdiff = $sign . format_float($perdiff, $numdec); | |
408 | return $startspan . $delta . ' ' . $fnumdiff . ' ' . $units . ' (' . $fperdiff . '%)' . $endspan; | |
409 | } | |
410 | ||
411 | /** | |
412 | * Custom implementation of iXHProfRuns | |
413 | * | |
414 | * This class is one implementation of the iXHProfRuns interface, in charge | |
415 | * of storing and retrieve profiling run data to/from DB (profiling table) | |
416 | * | |
417 | * The interface only defines two methods to be defined: get_run() and | |
418 | * save_run() we'll be implementing some more in order to keep all the | |
419 | * rest of information in our runs properly handled. | |
420 | */ | |
421 | class moodle_xhprofrun implements iXHProfRuns { | |
422 | ||
423 | protected $runid = null; | |
424 | protected $url = null; | |
425 | protected $totalexecutiontime = 0; | |
426 | protected $totalcputime = 0; | |
427 | protected $totalcalls = 0; | |
428 | protected $totalmemory = 0; | |
429 | protected $timecreated = 0; | |
430 | ||
431 | public function __construct() { | |
432 | $this->timecreated = time(); | |
433 | } | |
434 | ||
435 | /** | |
436 | * Given one runid and one type, return the run data | |
437 | * and some extra info in run_desc from DB | |
438 | * | |
439 | * Note that $type is completely ignored | |
440 | */ | |
441 | public function get_run($run_id, $type, &$run_desc) { | |
442 | global $DB; | |
443 | ||
444 | $rec = $DB->get_record('profiling', array('runid' => $run_id), '*', MUST_EXIST); | |
445 | ||
446 | $this->runid = $rec->runid; | |
447 | $this->url = $rec->url; | |
448 | $this->totalexecutiontime = $rec->totalexecutiontime; | |
449 | $this->totalcputime = $rec->totalcputime; | |
450 | $this->totalcalls = $rec->totalcalls; | |
451 | $this->totalmemory = $rec->totalmemory; | |
452 | $this->timecreated = $rec->timecreated; | |
453 | ||
454 | $run_desc = $this->url . ($rec->runreference ? ' (R) ' : ' ') . ' - ' . s($rec->runcomment); | |
455 | ||
456 | return unserialize(base64_decode($rec->data)); | |
457 | } | |
458 | ||
459 | /** | |
460 | * Given some run data, one type and, optionally, one runid | |
461 | * store the information in DB | |
462 | * | |
463 | * Note that $type is completely ignored | |
464 | */ | |
465 | public function save_run($xhprof_data, $type, $run_id = null) { | |
466 | global $DB; | |
467 | ||
468 | if (is_null($this->url)) { | |
469 | xhprof_error("Warning: You must use the prepare_run() method before saving it"); | |
470 | } | |
471 | ||
472 | // Calculate runid if needed | |
473 | $this->runid = is_null($run_id) ? md5($this->url . '-' . uniqid()) : $run_id; | |
474 | ||
475 | // Calculate totals | |
476 | $this->totalexecutiontime = $xhprof_data['main()']['wt']; | |
477 | $this->totalcputime = $xhprof_data['main()']['cpu']; | |
478 | $this->totalcalls = array_reduce($xhprof_data, array($this, 'sum_calls')); | |
479 | $this->totalmemory = $xhprof_data['main()']['mu']; | |
480 | ||
481 | // Prepare data | |
482 | $rec = new stdClass(); | |
483 | $rec->runid = $this->runid; | |
484 | $rec->url = $this->url; | |
485 | $rec->data = base64_encode(serialize($xhprof_data)); | |
486 | $rec->totalexecutiontime = $this->totalexecutiontime; | |
487 | $rec->totalcputime = $this->totalcputime; | |
488 | $rec->totalcalls = $this->totalcalls; | |
489 | $rec->totalmemory = $this->totalmemory; | |
490 | $rec->timecreated = $this->timecreated; | |
491 | ||
492 | $DB->insert_record('profiling', $rec); | |
493 | return $this->runid; | |
494 | } | |
495 | ||
496 | public function prepare_run($url) { | |
497 | $this->url = $url; | |
498 | } | |
499 | ||
500 | // Private API starts here | |
501 | ||
502 | protected function sum_calls($sum, $data) { | |
503 | return $sum + $data['ct']; | |
504 | } | |
505 | } | |
506 | ||
507 | /** | |
508 | * Simple subclass of {@link table_sql} that provides | |
509 | * some custom formatters for various columns, in order | |
510 | * to make the main profiles list nicer | |
511 | */ | |
512 | class xhprof_table_sql extends table_sql { | |
513 | ||
514 | protected $listurlmode = false; | |
515 | ||
516 | /** | |
54fb7e80 | 517 | * Get row classes to be applied based on row contents |
6af80cae | 518 | */ |
54fb7e80 EL |
519 | function get_row_class($row) { |
520 | return $row->runreference ? 'referencerun' : ''; // apply class to reference runs | |
6af80cae EL |
521 | } |
522 | ||
523 | /** | |
524 | * Define it the table is in listurlmode or not, output will | |
525 | * be different based on that | |
526 | */ | |
527 | function set_listurlmode($listurlmode) { | |
528 | $this->listurlmode = $listurlmode; | |
529 | } | |
530 | ||
531 | /** | |
532 | * Format URL, so it points to last run for that url | |
533 | */ | |
534 | protected function col_url($row) { | |
535 | global $OUTPUT; | |
536 | ||
537 | // Build the link to latest run for the script | |
187536f6 | 538 | $scripturl = new moodle_url('/admin/tool/profiling/index.php', array('script' => $row->url, 'listurl' => $row->url)); |
6af80cae EL |
539 | $scriptaction = $OUTPUT->action_link($scripturl, $row->url); |
540 | ||
541 | // Decide, based on $this->listurlmode which actions to show | |
542 | if ($this->listurlmode) { | |
543 | $detailsaction = ''; | |
544 | } else { | |
545 | // Build link icon to script details (pix + url + actionlink) | |
187536f6 PS |
546 | $detailsimg = $OUTPUT->pix_icon('t/right', get_string('profilingfocusscript', 'tool_profiling', $row->url)); |
547 | $detailsurl = new moodle_url('/admin/tool/profiling/index.php', array('listurl' => $row->url)); | |
6af80cae EL |
548 | $detailsaction = $OUTPUT->action_link($detailsurl, $detailsimg); |
549 | } | |
550 | ||
551 | return $scriptaction . ' ' . $detailsaction; | |
552 | } | |
553 | ||
554 | /** | |
555 | * Format profiling date, human and pointing to run | |
556 | */ | |
557 | protected function col_timecreated($row) { | |
558 | global $OUTPUT; | |
559 | $fdate = userdate($row->timecreated, '%d %b %Y, %H:%M'); | |
187536f6 | 560 | $url = new moodle_url('/admin/tool/profiling/index.php', array('runid' => $row->runid, 'listurl' => $row->url)); |
6af80cae EL |
561 | return $OUTPUT->action_link($url, $fdate); |
562 | } | |
563 | ||
564 | /** | |
565 | * Format execution time | |
566 | */ | |
567 | protected function col_totalexecutiontime($row) { | |
568 | return format_float($row->totalexecutiontime / 1000, 3) . ' ms'; | |
569 | } | |
570 | ||
571 | /** | |
572 | * Format cpu time | |
573 | */ | |
574 | protected function col_totalcputime($row) { | |
575 | return format_float($row->totalcputime / 1000, 3) . ' ms'; | |
576 | } | |
577 | ||
578 | /** | |
579 | * Format memory | |
580 | */ | |
581 | protected function col_totalmemory($row) { | |
582 | return format_float($row->totalmemory / 1024, 3) . ' KB'; | |
583 | } | |
584 | } |