|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 // Copyright (c) 2009 Facebook 00003 // 00004 // Licensed under the Apache License, Version 2.0 (the "License"); 00005 // you may not use this file except in compliance with the License. 00006 // You may obtain a copy of the License at 00007 // 00008 // http://www.apache.org/licenses/LICENSE-2.0 00009 // 00010 // Unless required by applicable law or agreed to in writing, software 00011 // distributed under the License is distributed on an "AS IS" BASIS, 00012 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00013 // See the License for the specific language governing permissions and 00014 // limitations under the License. 00015 // 00016 00017 // 00018 // This file contains various XHProf library (utility) functions. 00019 // Do not add any display specific code here. 00020 // 00021 00022 function xhprof_error($message) { 00023 error_log($message); 00024 } 00025 00026 /* 00027 * The list of possible metrics collected as part of XHProf that 00028 * require inclusive/exclusive handling while reporting. 00029 * 00030 * @author Kannan 00031 */ 00032 function xhprof_get_possible_metrics() { 00033 static $possible_metrics = 00034 array("wt" => array("Wall", "microsecs", "walltime"), 00035 "ut" => array("User", "microsecs", "user cpu time"), 00036 "st" => array("Sys", "microsecs", "system cpu time"), 00037 "cpu" => array("Cpu", "microsecs", "cpu time"), 00038 "mu" => array("MUse", "bytes", "memory usage"), 00039 "pmu" => array("PMUse", "bytes", "peak memory usage"), 00040 "samples" => array("Samples", "samples", "cpu time")); 00041 return $possible_metrics; 00042 } 00043 00050 function init_metrics($xhprof_data, $rep_symbol, $sort, $diff_report = false) { 00051 global $stats; 00052 global $pc_stats; 00053 global $metrics; 00054 global $diff_mode; 00055 global $sortable_columns; 00056 global $sort_col; 00057 global $display_calls; 00058 00059 $diff_mode = $diff_report; 00060 00061 if (!empty($sort)) { 00062 if (array_key_exists($sort, $sortable_columns)) { 00063 $sort_col = $sort; 00064 } else { 00065 print("Invalid Sort Key $sort specified in URL"); 00066 } 00067 } 00068 00069 // For C++ profiler runs, walltime attribute isn't present. 00070 // In that case, use "samples" as the default sort column. 00071 if (!isset($xhprof_data["main()"]["wt"])) { 00072 00073 if ($sort_col == "wt") { 00074 $sort_col = "samples"; 00075 } 00076 00077 // C++ profiler data doesn't have call counts. 00078 // ideally we should check to see if "ct" metric 00079 // is present for "main()". But currently "ct" 00080 // metric is artificially set to 1. So, relying 00081 // on absence of "wt" metric instead. 00082 $display_calls = false; 00083 } else { 00084 $display_calls = true; 00085 } 00086 00087 // parent/child report doesn't support exclusive times yet. 00088 // So, change sort hyperlinks to closest fit. 00089 if (!empty($rep_symbol)) { 00090 $sort_col = str_replace("excl_", "", $sort_col); 00091 } 00092 00093 if ($display_calls) { 00094 $stats = array("fn", "ct", "Calls%"); 00095 } else { 00096 $stats = array("fn"); 00097 } 00098 00099 $pc_stats = $stats; 00100 00101 $possible_metrics = xhprof_get_possible_metrics($xhprof_data); 00102 foreach ($possible_metrics as $metric => $desc) { 00103 if (isset($xhprof_data["main()"][$metric])) { 00104 $metrics[] = $metric; 00105 // flat (top-level reports): we can compute 00106 // exclusive metrics reports as well. 00107 $stats[] = $metric; 00108 $stats[] = "I" . $desc[0] . "%"; 00109 $stats[] = "excl_" . $metric; 00110 $stats[] = "E" . $desc[0] . "%"; 00111 00112 // parent/child report for a function: we can 00113 // only breakdown inclusive times correctly. 00114 $pc_stats[] = $metric; 00115 $pc_stats[] = "I" . $desc[0] . "%"; 00116 } 00117 } 00118 } 00119 00120 /* 00121 * Get the list of metrics present in $xhprof_data as an array. 00122 * 00123 * @author Kannan 00124 */ 00125 function xhprof_get_metrics($xhprof_data) { 00126 00127 // get list of valid metrics 00128 $possible_metrics = xhprof_get_possible_metrics(); 00129 00130 // return those that are present in the raw data. 00131 // We'll just look at the root of the subtree for this. 00132 $metrics = array(); 00133 foreach ($possible_metrics as $metric => $desc) { 00134 if (isset($xhprof_data["main()"][$metric])) { 00135 $metrics[] = $metric; 00136 } 00137 } 00138 00139 return $metrics; 00140 } 00141 00148 function xhprof_parse_parent_child($parent_child) { 00149 $ret = explode("==>", $parent_child); 00150 00151 // Return if both parent and child are set 00152 if (isset($ret[1])) { 00153 return $ret; 00154 } 00155 00156 return array(null, $ret[0]); 00157 } 00158 00165 function xhprof_build_parent_child_key($parent, $child) { 00166 if ($parent) { 00167 return $parent . "==>" . $child; 00168 } else { 00169 return $child; 00170 } 00171 } 00172 00173 00186 function xhprof_valid_run($run_id, $raw_data) { 00187 00188 $main_info = $raw_data["main()"]; 00189 if (empty($main_info)) { 00190 xhprof_error("XHProf: main() missing in raw data for Run ID: $run_id"); 00191 return false; 00192 } 00193 00194 // raw data should contain either wall time or samples information... 00195 if (isset($main_info["wt"])) { 00196 $metric = "wt"; 00197 } else if (isset($main_info["samples"])) { 00198 $metric = "samples"; 00199 } else { 00200 xhprof_error("XHProf: Wall Time information missing from Run ID: $run_id"); 00201 return false; 00202 } 00203 00204 foreach ($raw_data as $info) { 00205 $val = $info[$metric]; 00206 00207 // basic sanity checks... 00208 if ($val < 0) { 00209 xhprof_error("XHProf: $metric should not be negative: Run ID $run_id" 00210 . serialize($info)); 00211 return false; 00212 } 00213 if ($val > (86400000000)) { 00214 xhprof_error("XHProf: $metric > 1 day found in Run ID: $run_id " 00215 . serialize($info)); 00216 return false; 00217 } 00218 } 00219 return true; 00220 } 00221 00222 00240 function xhprof_trim_run($raw_data, $functions_to_keep) { 00241 00242 // convert list of functions to a hash with function as the key 00243 $function_map = array_fill_keys($functions_to_keep, 1); 00244 00245 // always keep main() as well so that overall totals can still 00246 // be computed if need be. 00247 $function_map['main()'] = 1; 00248 00249 $new_raw_data = array(); 00250 foreach ($raw_data as $parent_child => $info) { 00251 list($parent, $child) = xhprof_parse_parent_child($parent_child); 00252 00253 if (isset($function_map[$parent]) || isset($function_map[$child])) { 00254 $new_raw_data[$parent_child] = $info; 00255 } 00256 } 00257 00258 return $new_raw_data; 00259 } 00260 00268 function xhprof_normalize_metrics($raw_data, $num_runs) { 00269 00270 if (empty($raw_data) || ($num_runs == 0)) { 00271 return $raw_data; 00272 } 00273 00274 $raw_data_total = array(); 00275 00276 if (isset($raw_data["==>main()"]) && isset($raw_data["main()"])) { 00277 xhprof_error("XHProf Error: both ==>main() and main() set in raw data..."); 00278 } 00279 00280 foreach ($raw_data as $parent_child => $info) { 00281 foreach ($info as $metric => $value) { 00282 $raw_data_total[$parent_child][$metric] = ($value / $num_runs); 00283 } 00284 } 00285 00286 return $raw_data_total; 00287 } 00288 00289 00320 function xhprof_aggregate_runs($xhprof_runs_impl, $runs, 00321 $wts, $source="phprof", 00322 $use_script_name=false) { 00323 00324 $raw_data_total = null; 00325 $raw_data = null; 00326 $metrics = array(); 00327 00328 $run_count = count($runs); 00329 $wts_count = count($wts); 00330 00331 if (($run_count == 0) || 00332 (($wts_count > 0) && ($run_count != $wts_count))) { 00333 return array('description' => 'Invalid input..', 00334 'raw' => null); 00335 } 00336 00337 $bad_runs = array(); 00338 foreach ($runs as $idx => $run_id) { 00339 00340 $raw_data = $xhprof_runs_impl->get_run($run_id, $source, $description); 00341 00342 // use the first run to derive what metrics to aggregate on. 00343 if ($idx == 0) { 00344 foreach ($raw_data["main()"] as $metric => $val) { 00345 if ($metric != "pmu") { 00346 // for now, just to keep data size small, skip "peak" memory usage 00347 // data while aggregating. 00348 // The "regular" memory usage data will still be tracked. 00349 if (isset($val)) { 00350 $metrics[] = $metric; 00351 } 00352 } 00353 } 00354 } 00355 00356 if (!xhprof_valid_run($run_id, $raw_data)) { 00357 $bad_runs[] = $run_id; 00358 continue; 00359 } 00360 00361 if ($use_script_name) { 00362 $page = $description; 00363 00364 // create a fake function '__script::$page', and have and edge from 00365 // main() to '__script::$page'. We will also need edges to transfer 00366 // all edges originating from main() to now originate from 00367 // '__script::$page' to all function called from main(). 00368 // 00369 // We also weight main() ever so slightly higher so that 00370 // it shows up above the new entry in reports sorted by 00371 // inclusive metrics or call counts. 00372 if ($page) { 00373 foreach ($raw_data["main()"] as $metric => $val) { 00374 $fake_edge[$metric] = $val; 00375 $new_main[$metric] = $val + 0.00001; 00376 } 00377 $raw_data["main()"] = $new_main; 00378 $raw_data[xhprof_build_parent_child_key("main()", 00379 "__script::$page")] 00380 = $fake_edge; 00381 } else { 00382 $use_script_name = false; 00383 } 00384 } 00385 00386 // if no weights specified, use 1 as the default weightage.. 00387 $wt = ($wts_count == 0) ? 1 : $wts[$idx]; 00388 00389 // aggregate $raw_data into $raw_data_total with appropriate weight ($wt) 00390 foreach ($raw_data as $parent_child => $info) { 00391 if ($use_script_name) { 00392 // if this is an old edge originating from main(), it now 00393 // needs to be from '__script::$page' 00394 if (substr($parent_child, 0, 9) == "main()==>") { 00395 $child = substr($parent_child, 9); 00396 // ignore the newly added edge from main() 00397 if (substr($child, 0, 10) != "__script::") { 00398 $parent_child = xhprof_build_parent_child_key("__script::$page", 00399 $child); 00400 } 00401 } 00402 } 00403 00404 if (!isset($raw_data_total[$parent_child])) { 00405 foreach ($metrics as $metric) { 00406 $raw_data_total[$parent_child][$metric] = ($wt * $info[$metric]); 00407 } 00408 } else { 00409 foreach ($metrics as $metric) { 00410 $raw_data_total[$parent_child][$metric] += ($wt * $info[$metric]); 00411 } 00412 } 00413 } 00414 } 00415 00416 $runs_string = implode(",", $runs); 00417 00418 if (isset($wts)) { 00419 $wts_string = "in the ratio (" . implode(":", $wts) . ")"; 00420 $normalization_count = array_sum($wts); 00421 } else { 00422 $wts_string = ""; 00423 $normalization_count = $run_count; 00424 } 00425 00426 $run_count = $run_count - count($bad_runs); 00427 00428 $data['description'] = "Aggregated Report for $run_count runs: ". 00429 "$runs_string $wts_string\n"; 00430 $data['raw'] = xhprof_normalize_metrics($raw_data_total, 00431 $normalization_count); 00432 $data['bad_runs'] = $bad_runs; 00433 00434 return $data; 00435 } 00436 00437 00454 function xhprof_compute_flat_info($raw_data, &$overall_totals) { 00455 00456 global $display_calls; 00457 00458 $metrics = xhprof_get_metrics($raw_data); 00459 00460 $overall_totals = array("ct" => 0, 00461 "wt" => 0, 00462 "ut" => 0, 00463 "st" => 0, 00464 "cpu" => 0, 00465 "mu" => 0, 00466 "pmu" => 0, 00467 "samples" => 0 00468 ); 00469 00470 // compute inclusive times for each function 00471 $symbol_tab = xhprof_compute_inclusive_times($raw_data); 00472 00473 /* total metric value is the metric value for "main()" */ 00474 foreach ($metrics as $metric) { 00475 $overall_totals[$metric] = $symbol_tab["main()"][$metric]; 00476 } 00477 00478 /* 00479 * initialize exclusive (self) metric value to inclusive metric value 00480 * to start with. 00481 * In the same pass, also add up the total number of function calls. 00482 */ 00483 foreach ($symbol_tab as $symbol => $info) { 00484 foreach ($metrics as $metric) { 00485 $symbol_tab[$symbol]["excl_" . $metric] = $symbol_tab[$symbol][$metric]; 00486 } 00487 if ($display_calls) { 00488 /* keep track of total number of calls */ 00489 $overall_totals["ct"] += $info["ct"]; 00490 } 00491 } 00492 00493 /* adjust exclusive times by deducting inclusive time of children */ 00494 foreach ($raw_data as $parent_child => $info) { 00495 list($parent, $child) = xhprof_parse_parent_child($parent_child); 00496 00497 if ($parent) { 00498 foreach ($metrics as $metric) { 00499 // make sure the parent exists hasn't been pruned. 00500 if (isset($symbol_tab[$parent])) { 00501 $symbol_tab[$parent]["excl_" . $metric] -= $info[$metric]; 00502 } 00503 } 00504 } 00505 } 00506 00507 return $symbol_tab; 00508 } 00509 00516 function xhprof_compute_diff($xhprof_data1, $xhprof_data2) { 00517 global $display_calls; 00518 00519 // use the second run to decide what metrics we will do the diff on 00520 $metrics = xhprof_get_metrics($xhprof_data2); 00521 00522 $xhprof_delta = $xhprof_data2; 00523 00524 foreach ($xhprof_data1 as $parent_child => $info) { 00525 00526 if (!isset($xhprof_delta[$parent_child])) { 00527 00528 // this pc combination was not present in run1; 00529 // initialize all values to zero. 00530 if ($display_calls) { 00531 $xhprof_delta[$parent_child] = array("ct" => 0); 00532 } else { 00533 $xhprof_delta[$parent_child] = array(); 00534 } 00535 foreach ($metrics as $metric) { 00536 $xhprof_delta[$parent_child][$metric] = 0; 00537 } 00538 } 00539 00540 if ($display_calls) { 00541 $xhprof_delta[$parent_child]["ct"] -= $info["ct"]; 00542 } 00543 00544 foreach ($metrics as $metric) { 00545 $xhprof_delta[$parent_child][$metric] -= $info[$metric]; 00546 } 00547 } 00548 00549 return $xhprof_delta; 00550 } 00551 00552 00567 function xhprof_compute_inclusive_times($raw_data) { 00568 global $display_calls; 00569 00570 $metrics = xhprof_get_metrics($raw_data); 00571 00572 $symbol_tab = array(); 00573 00574 /* 00575 * First compute inclusive time for each function and total 00576 * call count for each function across all parents the 00577 * function is called from. 00578 */ 00579 foreach ($raw_data as $parent_child => $info) { 00580 00581 list($parent, $child) = xhprof_parse_parent_child($parent_child); 00582 00583 if ($parent == $child) { 00584 /* 00585 * XHProf PHP extension should never trigger this situation any more. 00586 * Recursion is handled in the XHProf PHP extension by giving nested 00587 * calls a unique recursion-depth appended name (for example, foo@1). 00588 */ 00589 xhprof_error("Error in Raw Data: parent & child are both: $parent"); 00590 return; 00591 } 00592 00593 if (!isset($symbol_tab[$child])) { 00594 00595 if ($display_calls) { 00596 $symbol_tab[$child] = array("ct" => $info["ct"]); 00597 } else { 00598 $symbol_tab[$child] = array(); 00599 } 00600 foreach ($metrics as $metric) { 00601 $symbol_tab[$child][$metric] = $info[$metric]; 00602 } 00603 } else { 00604 if ($display_calls) { 00605 /* increment call count for this child */ 00606 $symbol_tab[$child]["ct"] += $info["ct"]; 00607 } 00608 00609 /* update inclusive times/metric for this child */ 00610 foreach ($metrics as $metric) { 00611 $symbol_tab[$child][$metric] += $info[$metric]; 00612 } 00613 } 00614 } 00615 00616 return $symbol_tab; 00617 } 00618 00619 00620 /* 00621 * Prunes XHProf raw data: 00622 * 00623 * Any node whose inclusive walltime accounts for less than $prune_percent 00624 * of total walltime is pruned. [It is possible that a child function isn't 00625 * pruned, but one or more of its parents get pruned. In such cases, when 00626 * viewing the child function's hierarchical information, the cost due to 00627 * the pruned parent(s) will be attributed to a special function/symbol 00628 * "__pruned__()".] 00629 * 00630 * @param array $raw_data XHProf raw data to be pruned & validated. 00631 * @param double $prune_percent Any edges that account for less than 00632 * $prune_percent of time will be pruned 00633 * from the raw data. 00634 * 00635 * @return array Returns the pruned raw data. 00636 * 00637 * @author Kannan 00638 */ 00639 function xhprof_prune_run($raw_data, $prune_percent) { 00640 00641 $main_info = $raw_data["main()"]; 00642 if (empty($main_info)) { 00643 xhprof_error("XHProf: main() missing in raw data"); 00644 return false; 00645 } 00646 00647 // raw data should contain either wall time or samples information... 00648 if (isset($main_info["wt"])) { 00649 $prune_metric = "wt"; 00650 } else if (isset($main_info["samples"])) { 00651 $prune_metric = "samples"; 00652 } else { 00653 xhprof_error("XHProf: for main() we must have either wt " 00654 ."or samples attribute set"); 00655 return false; 00656 } 00657 00658 // determine the metrics present in the raw data.. 00659 $metrics = array(); 00660 foreach ($main_info as $metric => $val) { 00661 if (isset($val)) { 00662 $metrics[] = $metric; 00663 } 00664 } 00665 00666 $prune_threshold = (($main_info[$prune_metric] * $prune_percent) / 100.0); 00667 00668 init_metrics($raw_data, null, null, false); 00669 $flat_info = xhprof_compute_inclusive_times($raw_data); 00670 00671 foreach ($raw_data as $parent_child => $info) { 00672 00673 list($parent, $child) = xhprof_parse_parent_child($parent_child); 00674 00675 // is this child's overall total from all parents less than threshold? 00676 if ($flat_info[$child][$prune_metric] < $prune_threshold) { 00677 unset($raw_data[$parent_child]); // prune the edge 00678 } else if ($parent && 00679 ($parent != "__pruned__()") && 00680 ($flat_info[$parent][$prune_metric] < $prune_threshold)) { 00681 00682 // Parent's overall inclusive metric is less than a threshold. 00683 // All edges to the parent node will get nuked, and this child will 00684 // be a dangling child. 00685 // So instead change its parent to be a special function __pruned__(). 00686 $pruned_edge = xhprof_build_parent_child_key("__pruned__()", $child); 00687 00688 if (isset($raw_data[$pruned_edge])) { 00689 foreach ($metrics as $metric) { 00690 $raw_data[$pruned_edge][$metric]+=$raw_data[$parent_child][$metric]; 00691 } 00692 } else { 00693 $raw_data[$pruned_edge] = $raw_data[$parent_child]; 00694 } 00695 00696 unset($raw_data[$parent_child]); // prune the edge 00697 } 00698 } 00699 00700 return $raw_data; 00701 } 00702 00703 00709 function xhprof_array_set($arr, $k, $v) { 00710 $arr[$k] = $v; 00711 return $arr; 00712 } 00713 00719 function xhprof_array_unset($arr, $k) { 00720 unset($arr[$k]); 00721 return $arr; 00722 } 00723 00727 define('XHPROF_STRING_PARAM', 1); 00728 define('XHPROF_UINT_PARAM', 2); 00729 define('XHPROF_FLOAT_PARAM', 3); 00730 define('XHPROF_BOOL_PARAM', 4); 00731 00732 00742 function xhprof_get_param_helper($param) { 00743 $val = null; 00744 if (isset($_GET[$param])) 00745 $val = $_GET[$param]; 00746 else if (isset($_POST[$param])) { 00747 $val = $_POST[$param]; 00748 } 00749 return $val; 00750 } 00751 00759 function xhprof_get_string_param($param, $default = '') { 00760 $val = xhprof_get_param_helper($param); 00761 00762 if ($val === null) 00763 return $default; 00764 00765 return $val; 00766 } 00767 00778 function xhprof_get_uint_param($param, $default = 0) { 00779 $val = xhprof_get_param_helper($param); 00780 00781 if ($val === null) 00782 $val = $default; 00783 00784 // trim leading/trailing whitespace 00785 $val = trim($val); 00786 00787 // if it only contains digits, then ok.. 00788 if (ctype_digit($val)) { 00789 return $val; 00790 } 00791 00792 xhprof_error("$param is $val. It must be an unsigned integer."); 00793 return null; 00794 } 00795 00796 00807 function xhprof_get_float_param($param, $default = 0) { 00808 $val = xhprof_get_param_helper($param); 00809 00810 if ($val === null) 00811 $val = $default; 00812 00813 // trim leading/trailing whitespace 00814 $val = trim($val); 00815 00816 // TBD: confirm the value is indeed a float. 00817 if (true) // for now.. 00818 return (float)$val; 00819 00820 xhprof_error("$param is $val. It must be a float."); 00821 return null; 00822 } 00823 00834 function xhprof_get_bool_param($param, $default = false) { 00835 $val = xhprof_get_param_helper($param); 00836 00837 if ($val === null) 00838 $val = $default; 00839 00840 // trim leading/trailing whitespace 00841 $val = trim($val); 00842 00843 switch (strtolower($val)) { 00844 case '0': 00845 case '1': 00846 $val = (bool)$val; 00847 break; 00848 case 'true': 00849 case 'on': 00850 case 'yes': 00851 $val = true; 00852 break; 00853 case 'false': 00854 case 'off': 00855 case 'no': 00856 $val = false; 00857 break; 00858 default: 00859 xhprof_error("$param is $val. It must be a valid boolean string."); 00860 return null; 00861 } 00862 00863 return $val; 00864 00865 } 00866 00886 function xhprof_param_init($params) { 00887 /* Create variables specified in $params keys, init defaults */ 00888 foreach ($params as $k => $v) { 00889 switch ($v[0]) { 00890 case XHPROF_STRING_PARAM: 00891 $p = xhprof_get_string_param($k, $v[1]); 00892 break; 00893 case XHPROF_UINT_PARAM: 00894 $p = xhprof_get_uint_param($k, $v[1]); 00895 break; 00896 case XHPROF_FLOAT_PARAM: 00897 $p = xhprof_get_float_param($k, $v[1]); 00898 break; 00899 case XHPROF_BOOL_PARAM: 00900 $p = xhprof_get_bool_param($k, $v[1]); 00901 break; 00902 default: 00903 xhprof_error("Invalid param type passed to xhprof_param_init: " 00904 . $v[0]); 00905 exit(); 00906 } 00907 00908 // create a global variable using the parameter name. 00909 $GLOBALS[$k] = $p; 00910 } 00911 } 00912 00913 00921 function xhprof_get_matching_functions($q, $xhprof_data) { 00922 00923 $matches = array(); 00924 00925 foreach ($xhprof_data as $parent_child => $info) { 00926 list($parent, $child) = xhprof_parse_parent_child($parent_child); 00927 if (stripos($parent, $q) !== false) { 00928 $matches[$parent] = 1; 00929 } 00930 if (stripos($child, $q) !== false) { 00931 $matches[$child] = 1; 00932 } 00933 } 00934 00935 $res = array_keys($matches); 00936 00937 // sort it so the answers are in some reliable order... 00938 asort($res); 00939 00940 return ($res); 00941 } 00942