|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 // This file is part of Moodle - http://moodle.org/ 00003 // 00004 // Moodle is free software: you can redistribute it and/or modify 00005 // it under the terms of the GNU General Public License as published by 00006 // the Free Software Foundation, either version 3 of the License, or 00007 // (at your option) any later version. 00008 // 00009 // Moodle is distributed in the hope that it will be useful, 00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 // GNU General Public License for more details. 00013 // 00014 // You should have received a copy of the GNU General Public License 00015 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 00016 00024 defined('MOODLE_INTERNAL') || die(); 00025 00026 // need some stuff from xhprof 00027 require_once($CFG->libdir . '/xhprof/xhprof_lib/utils/xhprof_lib.php'); 00028 require_once($CFG->libdir . '/xhprof/xhprof_lib/utils/xhprof_runs.php'); 00029 // need some stuff from moodle 00030 require_once($CFG->libdir.'/tablelib.php'); 00031 00032 // TODO: Change the implementation below to proper profiling class 00033 00037 function profiling_is_running($value = null) { 00038 static $running = null; 00039 00040 if (!is_null($value)) { 00041 $running = (bool)$value; 00042 } 00043 00044 return $running; 00045 } 00046 00050 function profiling_is_saved($value = null) { 00051 static $saved = null; 00052 00053 if (!is_null($value)) { 00054 $saved = (bool)$value; 00055 } 00056 00057 return $saved; 00058 } 00059 00063 function profiling_start() { 00064 global $CFG, $SESSION, $SCRIPT; 00065 00066 // If profiling isn't available, nothing to start 00067 if (!extension_loaded('xhprof') || !function_exists('xhprof_enable')) { 00068 return false; 00069 } 00070 00071 // If profiling isn't enabled, nothing to start 00072 if (empty($CFG->profilingenabled) && empty($CFG->earlyprofilingenabled)) { 00073 return false; 00074 } 00075 00076 // If profiling is already running or saved, nothing to start 00077 if (profiling_is_running() || profiling_is_saved()) { 00078 return false; 00079 } 00080 00081 // Set script (from global if available, else our own) 00082 $script = !empty($SCRIPT) ? $SCRIPT : profiling_get_script(); 00083 00084 // Get PGC variables 00085 $check = 'PROFILEME'; 00086 $profileme = isset($_POST[$check]) || isset($_GET[$check]) || isset($_COOKIE[$check]) ? true : false; 00087 $profileme = $profileme && !empty($CFG->profilingallowme); 00088 $check = 'DONTPROFILEME'; 00089 $dontprofileme = isset($_POST[$check]) || isset($_GET[$check]) || isset($_COOKIE[$check]) ? true : false; 00090 $dontprofileme = $dontprofileme && !empty($CFG->profilingallowme); 00091 $check = 'PROFILEALL'; 00092 $profileall = isset($_POST[$check]) || isset($_GET[$check]) || isset($_COOKIE[$check]) ? true : false; 00093 $profileall = $profileall && !empty($CFG->profilingallowall); 00094 $check = 'PROFILEALLSTOP'; 00095 $profileallstop = isset($_POST[$check]) || isset($_GET[$check]) || isset($_COOKIE[$check]) ? true : false; 00096 $profileallstop = $profileallstop && !empty($CFG->profilingallowall); 00097 00098 // DONTPROFILEME detected, nothing to start 00099 if ($dontprofileme) { 00100 return false; 00101 } 00102 00103 // PROFILEALLSTOP detected, clean the mark in seesion and continue 00104 if ($profileallstop && !empty($SESSION)) { 00105 unset($SESSION->profileall); 00106 } 00107 00108 // PROFILEALL detected, set the mark in session and continue 00109 if ($profileall && !empty($SESSION)) { 00110 $SESSION->profileall = true; 00111 00112 // SESSION->profileall detected, set $profileall 00113 } else if (!empty($SESSION->profileall)) { 00114 $profileall = true; 00115 } 00116 00117 // Evaluate automatic (random) profiling if necessary 00118 $profileauto = false; 00119 if (!empty($CFG->profilingautofrec)) { 00120 $profileauto = (mt_rand(1, $CFG->profilingautofrec) === 1); 00121 } 00122 00123 // See if the $script matches any of the included patterns 00124 $included = empty($CFG->profilingincluded) ? '' : $CFG->profilingincluded; 00125 $profileincluded = profiling_string_matches($script, $included); 00126 00127 // See if the $script matches any of the excluded patterns 00128 $excluded = empty($CFG->profilingexcluded) ? '' : $CFG->profilingexcluded; 00129 $profileexcluded = profiling_string_matches($script, $excluded); 00130 00131 // Decide if profile auto must happen (observe matchings) 00132 $profileauto = $profileauto && $profileincluded && !$profileexcluded; 00133 00134 // Decide if profile by match must happen (only if profileauto is disabled) 00135 $profilematch = $profileincluded && !$profileexcluded && empty($CFG->profilingautofrec); 00136 00137 // If not auto, me, all, match have been detected, nothing to do 00138 if (!$profileauto && !$profileme && !$profileall && !$profilematch) { 00139 return false; 00140 } 00141 00142 // Arrived here, the script is going to be profiled, let's do it 00143 $ignore = array('call_user_func', 'call_user_func_array'); 00144 xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY, array('ignored_functions' => $ignore)); 00145 profiling_is_running(true); 00146 00147 // Started, return true 00148 return true; 00149 } 00150 00154 function profiling_stop() { 00155 global $CFG, $DB, $SCRIPT; 00156 00157 // If profiling isn't available, nothing to stop 00158 if (!extension_loaded('xhprof') || !function_exists('xhprof_enable')) { 00159 return false; 00160 } 00161 00162 // If profiling isn't enabled, nothing to stop 00163 if (empty($CFG->profilingenabled) && empty($CFG->earlyprofilingenabled)) { 00164 return false; 00165 } 00166 00167 // If profiling is not running or is already saved, nothing to stop 00168 if (!profiling_is_running() || profiling_is_saved()) { 00169 return false; 00170 } 00171 00172 // Set script (from global if available, else our own) 00173 $script = !empty($SCRIPT) ? $SCRIPT : profiling_get_script(); 00174 00175 // Arrived here, profiling is running, stop and save everything 00176 profiling_is_running(false); 00177 $data = xhprof_disable(); 00178 00179 // We only save the run after ensuring the DB table exists 00180 // (this prevents problems with profiling runs enabled in 00181 // config.php before Moodle is installed. Rare but... 00182 $tables = $DB->get_tables(); 00183 if (!in_array('profiling', $tables)) { 00184 return false; 00185 } 00186 00187 $run = new moodle_xhprofrun(); 00188 $run->prepare_run($script); 00189 $runid = $run->save_run($data, null); 00190 profiling_is_saved(true); 00191 00192 // Prune old runs 00193 profiling_prune_old_runs($runid); 00194 00195 // Finished, return true 00196 return true; 00197 } 00198 00199 function profiling_prune_old_runs($exception = 0) { 00200 global $CFG, $DB; 00201 00202 // Setting to 0 = no prune 00203 if (empty($CFG->profilinglifetime)) { 00204 return; 00205 } 00206 00207 $cuttime = time() - ($CFG->profilinglifetime * 60); 00208 $params = array('cuttime' => $cuttime, 'exception' => $exception); 00209 00210 $DB->delete_records_select('profiling', 'runreference = 0 AND 00211 timecreated < :cuttime AND 00212 runid != :exception', $params); 00213 } 00214 00225 function profiling_get_script() { 00226 global $CFG; 00227 00228 $wwwroot = parse_url($CFG->wwwroot); 00229 00230 if (!isset($wwwroot['path'])) { 00231 $wwwroot['path'] = ''; 00232 } 00233 $wwwroot['path'] .= '/'; 00234 00235 $path = $_SERVER['SCRIPT_NAME']; 00236 00237 if (strpos($path, $wwwroot['path']) === 0) { 00238 return substr($path, strlen($wwwroot['path']) - 1); 00239 } 00240 return ''; 00241 } 00242 00243 function profiling_urls($report, $runid, $runid2 = null) { 00244 global $CFG; 00245 00246 $url = ''; 00247 switch ($report) { 00248 case 'run': 00249 $url = $CFG->wwwroot . '/lib/xhprof/xhprof_html/index.php?run=' . $runid; 00250 break; 00251 case 'diff': 00252 $url = $CFG->wwwroot . '/lib/xhprof/xhprof_html/index.php?run1=' . $runid . '&run2=' . $runid2; 00253 break; 00254 case 'graph': 00255 $url = $CFG->wwwroot . '/lib/xhprof/xhprof_html/callgraph.php?run=' . $runid; 00256 break; 00257 } 00258 return $url; 00259 } 00260 00261 function profiling_print_run($run, $prevrunid = null) { 00262 global $CFG, $OUTPUT; 00263 00264 $output = ''; 00265 00266 // Prepare the runreference/runcomment form 00267 $checked = $run->runreference ? ' checked=checked' : ''; 00268 $referenceform = "<form id=\"profiling_runreference\" action=\"index.php\" method=\"GET\">" . 00269 "<input type=\"hidden\" name=\"sesskey\" value=\"" . sesskey() . "\"/>". 00270 "<input type=\"hidden\" name=\"runid\" value=\"$run->runid\"/>". 00271 "<input type=\"hidden\" name=\"listurl\" value=\"$run->url\"/>". 00272 "<input type=\"checkbox\" name=\"runreference\" value=\"1\"$checked/> ". 00273 "<input type=\"text\" name=\"runcomment\" value=\"$run->runcomment\"/> ". 00274 "<input type=\"submit\" value=\"" . get_string('savechanges') ."\"/>". 00275 "</form>"; 00276 00277 $table = new html_table(); 00278 $table->align = array('right', 'left'); 00279 $table->tablealign = 'center'; 00280 $table->attributes['class'] = 'profilingruntable'; 00281 $table->colclasses = array('label', 'value'); 00282 $table->data = array( 00283 array(get_string('runid', 'tool_profiling'), $run->runid), 00284 array(get_string('url'), $run->url), 00285 array(get_string('date'), userdate($run->timecreated, '%d %B %Y, %H:%M')), 00286 array(get_string('executiontime', 'tool_profiling'), format_float($run->totalexecutiontime / 1000, 3) . ' ms'), 00287 array(get_string('cputime', 'tool_profiling'), format_float($run->totalcputime / 1000, 3) . ' ms'), 00288 array(get_string('calls', 'tool_profiling'), $run->totalcalls), 00289 array(get_string('memory', 'tool_profiling'), format_float($run->totalmemory / 1024, 0) . ' KB'), 00290 array(get_string('markreferencerun', 'tool_profiling'), $referenceform)); 00291 $output = $OUTPUT->box(html_writer::table($table), 'generalbox boxwidthwide boxaligncenter profilingrunbox', 'profiling_summary', true); 00292 // Add link to details 00293 $strviewdetails = get_string('viewdetails', 'tool_profiling'); 00294 $url = profiling_urls('run', $run->runid); 00295 $output.=$OUTPUT->heading('<a href="' . $url . '" onclick="javascript:window.open(' . "'" . $url . "'" . ');' . 00296 'return false;"' . ' title="">' . $strviewdetails . '</a>', 3, 'main profilinglink'); 00297 // If there is one previous run marked as reference, add link to diff 00298 if ($prevrunid) { 00299 $strviewdiff = get_string('viewdiff', 'tool_profiling'); 00300 $url = 'index.php?runid=' . $run->runid . '&runid2=' . $prevrunid . '&listurl=' . urlencode($run->url); 00301 $output.=$OUTPUT->heading('<a href="' . $url . '" title="">' . $strviewdiff . '</a>', 3, 'main profilinglink'); 00302 } 00303 00304 return $output; 00305 } 00306 00307 function profiling_print_rundiff($run1, $run2) { 00308 global $CFG, $OUTPUT; 00309 00310 $output = ''; 00311 00312 // Prepare the reference/comment information 00313 $referencetext1 = ($run1->runreference ? get_string('yes') : get_string('no')) . 00314 ($run1->runcomment ? ' - ' . s($run1->runcomment) : ''); 00315 $referencetext2 = ($run2->runreference ? get_string('yes') : get_string('no')) . 00316 ($run2->runcomment ? ' - ' . s($run2->runcomment) : ''); 00317 00318 // Calculate global differences 00319 $diffexecutiontime = profiling_get_difference($run1->totalexecutiontime, $run2->totalexecutiontime, 'ms', 1000); 00320 $diffcputime = profiling_get_difference($run1->totalcputime, $run2->totalcputime, 'ms', 1000); 00321 $diffcalls = profiling_get_difference($run1->totalcalls, $run2->totalcalls); 00322 $diffmemory = profiling_get_difference($run1->totalmemory, $run2->totalmemory, 'KB', 1024); 00323 00324 $table = new html_table(); 00325 $table->align = array('right', 'left', 'left', 'left'); 00326 $table->tablealign = 'center'; 00327 $table->attributes['class'] = 'profilingruntable'; 00328 $table->colclasses = array('label', 'value1', 'value2'); 00329 $table->data = array( 00330 array(get_string('runid', 'tool_profiling'), 00331 '<a href="index.php?runid=' . $run1->runid . '&listurl=' . urlencode($run1->url) . '" title="">' . $run1->runid . '</a>', 00332 '<a href="index.php?runid=' . $run2->runid . '&listurl=' . urlencode($run2->url) . '" title="">' . $run2->runid . '</a>'), 00333 array(get_string('url'), $run1->url, $run2->url), 00334 array(get_string('date'), userdate($run1->timecreated, '%d %B %Y, %H:%M'), 00335 userdate($run2->timecreated, '%d %B %Y, %H:%M')), 00336 array(get_string('executiontime', 'tool_profiling'), 00337 format_float($run1->totalexecutiontime / 1000, 3) . ' ms', 00338 format_float($run2->totalexecutiontime / 1000, 3) . ' ms ' . $diffexecutiontime), 00339 array(get_string('cputime', 'tool_profiling'), 00340 format_float($run1->totalcputime / 1000, 3) . ' ms', 00341 format_float($run2->totalcputime / 1000, 3) . ' ms ' . $diffcputime), 00342 array(get_string('calls', 'tool_profiling'), $run1->totalcalls, $run2->totalcalls . ' ' . $diffcalls), 00343 array(get_string('memory', 'tool_profiling'), 00344 format_float($run1->totalmemory / 1024, 0) . ' KB', 00345 format_float($run2->totalmemory / 1024, 0) . ' KB ' . $diffmemory), 00346 array(get_string('referencerun', 'tool_profiling'), $referencetext1, $referencetext2)); 00347 $output = $OUTPUT->box(html_writer::table($table), 'generalbox boxwidthwide boxaligncenter profilingrunbox', 'profiling_summary', true); 00348 // Add link to details 00349 $strviewdetails = get_string('viewdiffdetails', 'tool_profiling'); 00350 $url = profiling_urls('diff', $run1->runid, $run2->runid); 00351 //$url = $CFG->wwwroot . '/admin/tool/profiling/index.php?run=' . $run->runid; 00352 $output.=$OUTPUT->heading('<a href="' . $url . '" onclick="javascript:window.open(' . "'" . $url . "'" . ');' . 00353 'return false;"' . ' title="">' . $strviewdetails . '</a>', 3, 'main profilinglink'); 00354 return $output; 00355 } 00356 00362 function profiling_list_controls($listurl) { 00363 global $CFG, $OUTPUT; 00364 00365 $output = ''; 00366 00367 return $output; 00368 } 00369 00374 function profiling_string_matches($string, $patterns) { 00375 $patterns = explode(',', $patterns); 00376 foreach ($patterns as $pattern) { 00377 // Trim and prepare pattern 00378 $pattern = str_replace('\*', '.*', preg_quote(trim($pattern), '~')); 00379 // Don't process empty patterns 00380 if (empty($pattern)) { 00381 continue; 00382 } 00383 if (preg_match('~' . $pattern . '~', $string)) { 00384 return true; 00385 } 00386 } 00387 return false; 00388 } 00389 00394 function profiling_get_difference($number1, $number2, $units = '', $factor = 1, $numdec = 2) { 00395 $numdiff = $number2 - $number1; 00396 $perdiff = 0; 00397 if ($number1 != $number2) { 00398 $perdiff = $number1 != 0 ? ($number2 * 100 / $number1) - 100 : 0; 00399 } 00400 $sign = $number2 > $number1 ? '+' : ''; 00401 $delta = abs($perdiff) > 0.25 ? 'Δ' : '≈'; 00402 $spanclass = $number2 > $number1 ? 'worse' : ($number1 > $number2 ? 'better' : 'same'); 00403 $importantclass= abs($perdiff) > 1 ? ' profiling_important' : ''; 00404 $startspan = '<span class="profiling_' . $spanclass . $importantclass . '">'; 00405 $endspan = '</span>'; 00406 $fnumdiff = $sign . format_float($numdiff / $factor, $numdec); 00407 $fperdiff = $sign . format_float($perdiff, $numdec); 00408 return $startspan . $delta . ' ' . $fnumdiff . ' ' . $units . ' (' . $fperdiff . '%)' . $endspan; 00409 } 00410 00421 class moodle_xhprofrun implements iXHProfRuns { 00422 00423 protected $runid = null; 00424 protected $url = null; 00425 protected $totalexecutiontime = 0; 00426 protected $totalcputime = 0; 00427 protected $totalcalls = 0; 00428 protected $totalmemory = 0; 00429 protected $timecreated = 0; 00430 00431 public function __construct() { 00432 $this->timecreated = time(); 00433 } 00434 00441 public function get_run($run_id, $type, &$run_desc) { 00442 global $DB; 00443 00444 $rec = $DB->get_record('profiling', array('runid' => $run_id), '*', MUST_EXIST); 00445 00446 $this->runid = $rec->runid; 00447 $this->url = $rec->url; 00448 $this->totalexecutiontime = $rec->totalexecutiontime; 00449 $this->totalcputime = $rec->totalcputime; 00450 $this->totalcalls = $rec->totalcalls; 00451 $this->totalmemory = $rec->totalmemory; 00452 $this->timecreated = $rec->timecreated; 00453 00454 $run_desc = $this->url . ($rec->runreference ? ' (R) ' : ' ') . ' - ' . s($rec->runcomment); 00455 00456 return unserialize(base64_decode($rec->data)); 00457 } 00458 00465 public function save_run($xhprof_data, $type, $run_id = null) { 00466 global $DB; 00467 00468 if (is_null($this->url)) { 00469 xhprof_error("Warning: You must use the prepare_run() method before saving it"); 00470 } 00471 00472 // Calculate runid if needed 00473 $this->runid = is_null($run_id) ? md5($this->url . '-' . uniqid()) : $run_id; 00474 00475 // Calculate totals 00476 $this->totalexecutiontime = $xhprof_data['main()']['wt']; 00477 $this->totalcputime = $xhprof_data['main()']['cpu']; 00478 $this->totalcalls = array_reduce($xhprof_data, array($this, 'sum_calls')); 00479 $this->totalmemory = $xhprof_data['main()']['mu']; 00480 00481 // Prepare data 00482 $rec = new stdClass(); 00483 $rec->runid = $this->runid; 00484 $rec->url = $this->url; 00485 $rec->data = base64_encode(serialize($xhprof_data)); 00486 $rec->totalexecutiontime = $this->totalexecutiontime; 00487 $rec->totalcputime = $this->totalcputime; 00488 $rec->totalcalls = $this->totalcalls; 00489 $rec->totalmemory = $this->totalmemory; 00490 $rec->timecreated = $this->timecreated; 00491 00492 $DB->insert_record('profiling', $rec); 00493 return $this->runid; 00494 } 00495 00496 public function prepare_run($url) { 00497 $this->url = $url; 00498 } 00499 00500 // Private API starts here 00501 00502 protected function sum_calls($sum, $data) { 00503 return $sum + $data['ct']; 00504 } 00505 } 00506 00512 class xhprof_table_sql extends table_sql { 00513 00514 protected $listurlmode = false; 00515 00519 function get_row_class($row) { 00520 return $row->runreference ? 'referencerun' : ''; // apply class to reference runs 00521 } 00522 00527 function set_listurlmode($listurlmode) { 00528 $this->listurlmode = $listurlmode; 00529 } 00530 00534 protected function col_url($row) { 00535 global $OUTPUT; 00536 00537 // Build the link to latest run for the script 00538 $scripturl = new moodle_url('/admin/tool/profiling/index.php', array('script' => $row->url, 'listurl' => $row->url)); 00539 $scriptaction = $OUTPUT->action_link($scripturl, $row->url); 00540 00541 // Decide, based on $this->listurlmode which actions to show 00542 if ($this->listurlmode) { 00543 $detailsaction = ''; 00544 } else { 00545 // Build link icon to script details (pix + url + actionlink) 00546 $detailsimg = $OUTPUT->pix_icon('t/right', get_string('profilingfocusscript', 'tool_profiling', $row->url)); 00547 $detailsurl = new moodle_url('/admin/tool/profiling/index.php', array('listurl' => $row->url)); 00548 $detailsaction = $OUTPUT->action_link($detailsurl, $detailsimg); 00549 } 00550 00551 return $scriptaction . ' ' . $detailsaction; 00552 } 00553 00557 protected function col_timecreated($row) { 00558 global $OUTPUT; 00559 $fdate = userdate($row->timecreated, '%d %b %Y, %H:%M'); 00560 $url = new moodle_url('/admin/tool/profiling/index.php', array('runid' => $row->runid, 'listurl' => $row->url)); 00561 return $OUTPUT->action_link($url, $fdate); 00562 } 00563 00567 protected function col_totalexecutiontime($row) { 00568 return format_float($row->totalexecutiontime / 1000, 3) . ' ms'; 00569 } 00570 00574 protected function col_totalcputime($row) { 00575 return format_float($row->totalcputime / 1000, 3) . ' ms'; 00576 } 00577 00581 protected function col_totalmemory($row) { 00582 return format_float($row->totalmemory / 1024, 3) . ' KB'; 00583 } 00584 }