Merge branch 'wip-MDL-60313-master' of git://github.com/marinaglancy/moodle
[moodle.git] / lib / xhprof / xhprof_lib / utils / xhprof_lib.php
1 <?php
2 //  Copyright (c) 2009 Facebook
3 //
4 //  Licensed under the Apache License, Version 2.0 (the "License");
5 //  you may not use this file except in compliance with the License.
6 //  You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 //  Unless required by applicable law or agreed to in writing, software
11 //  distributed under the License is distributed on an "AS IS" BASIS,
12 //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 //  See the License for the specific language governing permissions and
14 //  limitations under the License.
15 //
17 //
18 // This file contains various XHProf library (utility) functions.
19 // Do not add any display specific code here.
20 //
22 function xhprof_error($message) {
23   error_log($message);
24 }
26 /*
27  * The list of possible metrics collected as part of XHProf that
28  * require inclusive/exclusive handling while reporting.
29  *
30  * @author Kannan
31  */
32 function xhprof_get_possible_metrics() {
33  static $possible_metrics =
34    array("wt" => array("Wall", "microsecs", "walltime"),
35          "ut" => array("User", "microsecs", "user cpu time"),
36          "st" => array("Sys", "microsecs", "system cpu time"),
37          "cpu" => array("Cpu", "microsecs", "cpu time"),
38          "mu" => array("MUse", "bytes", "memory usage"),
39          "pmu" => array("PMUse", "bytes", "peak memory usage"),
40          "samples" => array("Samples", "samples", "cpu time"));
41  return $possible_metrics;
42 }
44 /**
45  * Initialize the metrics we'll display based on the information
46  * in the raw data.
47  *
48  * @author Kannan
49  */
50 function init_metrics($xhprof_data, $rep_symbol, $sort, $diff_report = false) {
51   global $stats;
52   global $pc_stats;
53   global $metrics;
54   global $diff_mode;
55   global $sortable_columns;
56   global $sort_col;
57   global $display_calls;
59   $diff_mode = $diff_report;
61   if (!empty($sort)) {
62     if (array_key_exists($sort, $sortable_columns)) {
63       $sort_col = $sort;
64     } else {
65       print("Invalid Sort Key $sort specified in URL");
66     }
67   }
69   // For C++ profiler runs, walltime attribute isn't present.
70   // In that case, use "samples" as the default sort column.
71   if (!isset($xhprof_data["main()"]["wt"])) {
73     if ($sort_col == "wt") {
74       $sort_col = "samples";
75     }
77     // C++ profiler data doesn't have call counts.
78     // ideally we should check to see if "ct" metric
79     // is present for "main()". But currently "ct"
80     // metric is artificially set to 1. So, relying
81     // on absence of "wt" metric instead.
82     $display_calls = false;
83   } else {
84     $display_calls = true;
85   }
87   // parent/child report doesn't support exclusive times yet.
88   // So, change sort hyperlinks to closest fit.
89   if (!empty($rep_symbol)) {
90     $sort_col = str_replace("excl_", "", $sort_col);
91   }
93   if ($display_calls) {
94     $stats = array("fn", "ct", "Calls%");
95   } else {
96     $stats = array("fn");
97   }
99   $pc_stats = $stats;
101   $possible_metrics = xhprof_get_possible_metrics();
102   foreach ($possible_metrics as $metric => $desc) {
103     if (isset($xhprof_data["main()"][$metric])) {
104       $metrics[] = $metric;
105       // flat (top-level reports): we can compute
106       // exclusive metrics reports as well.
107       $stats[] = $metric;
108       $stats[] = "I" . $desc[0] . "%";
109       $stats[] = "excl_" . $metric;
110       $stats[] = "E" . $desc[0] . "%";
112       // parent/child report for a function: we can
113       // only breakdown inclusive times correctly.
114       $pc_stats[] = $metric;
115       $pc_stats[] = "I" . $desc[0] . "%";
116     }
117   }
120 /*
121  * Get the list of metrics present in $xhprof_data as an array.
122  *
123  * @author Kannan
124  */
125 function xhprof_get_metrics($xhprof_data) {
127   // get list of valid metrics
128   $possible_metrics = xhprof_get_possible_metrics();
130   // return those that are present in the raw data.
131   // We'll just look at the root of the subtree for this.
132   $metrics = array();
133   foreach ($possible_metrics as $metric => $desc) {
134     if (isset($xhprof_data["main()"][$metric])) {
135       $metrics[] = $metric;
136     }
137   }
139   return $metrics;
142 /**
143  * Takes a parent/child function name encoded as
144  * "a==>b" and returns array("a", "b").
145  *
146  * @author Kannan
147  */
148 function xhprof_parse_parent_child($parent_child) {
149   $ret = explode("==>", $parent_child);
151   // Return if both parent and child are set
152   if (isset($ret[1])) {
153     return $ret;
154   }
156   return array(null, $ret[0]);
159 /**
160  * Given parent & child function name, composes the key
161  * in the format present in the raw data.
162  *
163  * @author Kannan
164  */
165 function xhprof_build_parent_child_key($parent, $child) {
166   if ($parent) {
167     return $parent . "==>" . $child;
168   } else {
169     return $child;
170   }
174 /**
175  * Checks if XHProf raw data appears to be valid and not corrupted.
176  *
177  *  @param   int    $run_id        Run id of run to be pruned.
178  *                                 [Used only for reporting errors.]
179  *  @param   array  $raw_data      XHProf raw data to be pruned
180  *                                 & validated.
181  *
182  *  @return  bool   true on success, false on failure
183  *
184  *  @author Kannan
185  */
186 function xhprof_valid_run($run_id, $raw_data) {
188   $main_info = $raw_data["main()"];
189   if (empty($main_info)) {
190     xhprof_error("XHProf: main() missing in raw data for Run ID: $run_id");
191     return false;
192   }
194   // raw data should contain either wall time or samples information...
195   if (isset($main_info["wt"])) {
196     $metric = "wt";
197   } else if (isset($main_info["samples"])) {
198     $metric = "samples";
199   } else {
200     xhprof_error("XHProf: Wall Time information missing from Run ID: $run_id");
201     return false;
202   }
204   foreach ($raw_data as $info) {
205     $val = $info[$metric];
207     // basic sanity checks...
208     if ($val < 0) {
209       xhprof_error("XHProf: $metric should not be negative: Run ID $run_id"
210                    . serialize($info));
211       return false;
212     }
213     if ($val > (86400000000)) {
214       xhprof_error("XHProf: $metric > 1 day found in Run ID: $run_id "
215                    . serialize($info));
216       return false;
217     }
218   }
219   return true;
223 /**
224  * Return a trimmed version of the XHProf raw data. Note that the raw
225  * data contains one entry for each unique parent/child function
226  * combination.The trimmed version of raw data will only contain
227  * entries where either the parent or child function is in the list
228  * of $functions_to_keep.
229  *
230  * Note: Function main() is also always kept so that overall totals
231  * can still be obtained from the trimmed version.
232  *
233  * @param  array  XHProf raw data
234  * @param  array  array of function names
235  *
236  * @return array  Trimmed XHProf Report
237  *
238  * @author Kannan
239  */
240 function xhprof_trim_run($raw_data, $functions_to_keep) {
242   // convert list of functions to a hash with function as the key
243   $function_map = array_fill_keys($functions_to_keep, 1);
245   // always keep main() as well so that overall totals can still
246   // be computed if need be.
247   $function_map['main()'] = 1;
249   $new_raw_data = array();
250   foreach ($raw_data as $parent_child => $info) {
251     list($parent, $child) = xhprof_parse_parent_child($parent_child);
253     if (isset($function_map[$parent]) || isset($function_map[$child])) {
254       $new_raw_data[$parent_child] = $info;
255     }
256   }
258   return $new_raw_data;
261 /**
262  * Takes raw XHProf data that was aggregated over "$num_runs" number
263  * of runs averages/nomalizes the data. Essentially the various metrics
264  * collected are divided by $num_runs.
265  *
266  * @author Kannan
267  */
268 function xhprof_normalize_metrics($raw_data, $num_runs) {
270   if (empty($raw_data) || ($num_runs == 0)) {
271     return $raw_data;
272   }
274   $raw_data_total = array();
276   if (isset($raw_data["==>main()"]) && isset($raw_data["main()"])) {
277     xhprof_error("XHProf Error: both ==>main() and main() set in raw data...");
278   }
280   foreach ($raw_data as $parent_child => $info) {
281     foreach ($info as $metric => $value) {
282       $raw_data_total[$parent_child][$metric] = ($value / $num_runs);
283     }
284   }
286   return $raw_data_total;
290 /**
291  * Get raw data corresponding to specified array of runs
292  * aggregated by certain weightage.
293  *
294  * Suppose you have run:5 corresponding to page1.php,
295  *                  run:6 corresponding to page2.php,
296  *             and  run:7 corresponding to page3.php
297  *
298  * and you want to accumulate these runs in a 2:4:1 ratio. You
299  * can do so by calling:
300  *
301  *     xhprof_aggregate_runs(array(5, 6, 7), array(2, 4, 1));
302  *
303  * The above will return raw data for the runs aggregated
304  * in 2:4:1 ratio.
305  *
306  *  @param object  $xhprof_runs_impl  An object that implements
307  *                                    the iXHProfRuns interface
308  *  @param  array  $runs            run ids of the XHProf runs..
309  *  @param  array  $wts             integral (ideally) weights for $runs
310  *  @param  string $source          source to fetch raw data for run from
311  *  @param  bool   $use_script_name If true, a fake edge from main() to
312  *                                  to __script::<scriptname> is introduced
313  *                                  in the raw data so that after aggregations
314  *                                  the script name is still preserved.
315  *
316  *  @return array  Return aggregated raw data
317  *
318  *  @author Kannan
319  */
320 function xhprof_aggregate_runs($xhprof_runs_impl, $runs,
321                                $wts, $source="phprof",
322                                $use_script_name=false) {
324   $raw_data_total = null;
325   $raw_data       = null;
326   $metrics        = array();
328   $run_count = count($runs);
329   $wts_count = count($wts);
331   if (($run_count == 0) ||
332       (($wts_count > 0) && ($run_count != $wts_count))) {
333     return array('description' => 'Invalid input..',
334                  'raw'  => null);
335   }
337   $bad_runs = array();
338   foreach ($runs as $idx => $run_id) {
340     $raw_data = $xhprof_runs_impl->get_run($run_id, $source, $description);
342     // use the first run to derive what metrics to aggregate on.
343     if ($idx == 0) {
344       foreach ($raw_data["main()"] as $metric => $val) {
345         if ($metric != "pmu") {
346           // for now, just to keep data size small, skip "peak" memory usage
347           // data while aggregating.
348           // The "regular" memory usage data will still be tracked.
349           if (isset($val)) {
350             $metrics[] = $metric;
351           }
352         }
353       }
354     }
356     if (!xhprof_valid_run($run_id, $raw_data)) {
357       $bad_runs[] = $run_id;
358       continue;
359     }
361     if ($use_script_name) {
362       $page = $description;
364       // create a fake function '__script::$page', and have and edge from
365       // main() to '__script::$page'. We will also need edges to transfer
366       // all edges originating from main() to now originate from
367       // '__script::$page' to all function called from main().
368       //
369       // We also weight main() ever so slightly higher so that
370       // it shows up above the new entry in reports sorted by
371       // inclusive metrics or call counts.
372       if ($page) {
373         foreach ($raw_data["main()"] as $metric => $val) {
374           $fake_edge[$metric] = $val;
375           $new_main[$metric]  = $val + 0.00001;
376         }
377         $raw_data["main()"] = $new_main;
378         $raw_data[xhprof_build_parent_child_key("main()",
379                                                 "__script::$page")]
380           = $fake_edge;
381       } else {
382         $use_script_name = false;
383       }
384     }
386     // if no weights specified, use 1 as the default weightage..
387     $wt = ($wts_count == 0) ? 1 : $wts[$idx];
389     // aggregate $raw_data into $raw_data_total with appropriate weight ($wt)
390     foreach ($raw_data as $parent_child => $info) {
391       if ($use_script_name) {
392         // if this is an old edge originating from main(), it now
393         // needs to be from '__script::$page'
394         if (substr($parent_child, 0, 9) == "main()==>") {
395           $child = substr($parent_child, 9);
396           // ignore the newly added edge from main()
397           if (substr($child, 0, 10) != "__script::") {
398             $parent_child = xhprof_build_parent_child_key("__script::$page",
399                                                           $child);
400           }
401         }
402       }
404       if (!isset($raw_data_total[$parent_child])) {
405         foreach ($metrics as $metric) {
406           $raw_data_total[$parent_child][$metric] = ($wt * $info[$metric]);
407         }
408       } else {
409         foreach ($metrics as $metric) {
410           $raw_data_total[$parent_child][$metric] += ($wt * $info[$metric]);
411         }
412       }
413     }
414   }
416   $runs_string = implode(",", $runs);
418   if (isset($wts)) {
419     $wts_string  = "in the ratio (" . implode(":", $wts) . ")";
420     $normalization_count = array_sum($wts);
421   } else {
422     $wts_string = "";
423     $normalization_count = $run_count;
424   }
426   $run_count = $run_count - count($bad_runs);
428   $data['description'] = "Aggregated Report for $run_count runs: ".
429                          "$runs_string $wts_string\n";
430   $data['raw'] = xhprof_normalize_metrics($raw_data_total,
431                                           $normalization_count);
432   $data['bad_runs'] = $bad_runs;
434   return $data;
438 /**
439  * Analyze hierarchical raw data, and compute per-function (flat)
440  * inclusive and exclusive metrics.
441  *
442  * Also, store overall totals in the 2nd argument.
443  *
444  * @param  array $raw_data          XHProf format raw profiler data.
445  * @param  array &$overall_totals   OUT argument for returning
446  *                                  overall totals for various
447  *                                  metrics.
448  * @return array Returns a map from function name to its
449  *               call count and inclusive & exclusive metrics
450  *               (such as wall time, etc.).
451  *
452  * @author Kannan Muthukkaruppan
453  */
454 function xhprof_compute_flat_info($raw_data, &$overall_totals) {
456   global $display_calls;
458   $metrics = xhprof_get_metrics($raw_data);
460   $overall_totals = array("ct" => 0,
461                            "wt" => 0,
462                            "ut" => 0,
463                            "st" => 0,
464                            "cpu" => 0,
465                            "mu" => 0,
466                            "pmu" => 0,
467                            "samples" => 0
468                            );
470   // compute inclusive times for each function
471   $symbol_tab = xhprof_compute_inclusive_times($raw_data);
473   /* total metric value is the metric value for "main()" */
474   foreach ($metrics as $metric) {
475     $overall_totals[$metric] = $symbol_tab["main()"][$metric];
476   }
478   /*
479    * initialize exclusive (self) metric value to inclusive metric value
480    * to start with.
481    * In the same pass, also add up the total number of function calls.
482    */
483   foreach ($symbol_tab as $symbol => $info) {
484     foreach ($metrics as $metric) {
485       $symbol_tab[$symbol]["excl_" . $metric] = $symbol_tab[$symbol][$metric];
486     }
487     if ($display_calls) {
488       /* keep track of total number of calls */
489       $overall_totals["ct"] += $info["ct"];
490     }
491   }
493   /* adjust exclusive times by deducting inclusive time of children */
494   foreach ($raw_data as $parent_child => $info) {
495     list($parent, $child) = xhprof_parse_parent_child($parent_child);
497     if ($parent) {
498       foreach ($metrics as $metric) {
499         // make sure the parent exists hasn't been pruned.
500         if (isset($symbol_tab[$parent])) {
501           $symbol_tab[$parent]["excl_" . $metric] -= $info[$metric];
502         }
503       }
504     }
505   }
507   return $symbol_tab;
510 /**
511  * Hierarchical diff:
512  * Compute and return difference of two call graphs: Run2 - Run1.
513  *
514  * @author Kannan
515  */
516 function xhprof_compute_diff($xhprof_data1, $xhprof_data2) {
517   global $display_calls;
519   // use the second run to decide what metrics we will do the diff on
520   $metrics = xhprof_get_metrics($xhprof_data2);
522   $xhprof_delta = $xhprof_data2;
524   foreach ($xhprof_data1 as $parent_child => $info) {
526     if (!isset($xhprof_delta[$parent_child])) {
528       // this pc combination was not present in run1;
529       // initialize all values to zero.
530       if ($display_calls) {
531         $xhprof_delta[$parent_child] = array("ct" => 0);
532       } else {
533         $xhprof_delta[$parent_child] = array();
534       }
535       foreach ($metrics as $metric) {
536         $xhprof_delta[$parent_child][$metric] = 0;
537       }
538     }
540     if ($display_calls) {
541       $xhprof_delta[$parent_child]["ct"] -= $info["ct"];
542     }
544     foreach ($metrics as $metric) {
545       $xhprof_delta[$parent_child][$metric] -= $info[$metric];
546     }
547   }
549   return $xhprof_delta;
553 /**
554  * Compute inclusive metrics for function. This code was factored out
555  * of xhprof_compute_flat_info().
556  *
557  * The raw data contains inclusive metrics of a function for each
558  * unique parent function it is called from. The total inclusive metrics
559  * for a function is therefore the sum of inclusive metrics for the
560  * function across all parents.
561  *
562  * @return array  Returns a map of function name to total (across all parents)
563  *                inclusive metrics for the function.
564  *
565  * @author Kannan
566  */
567 function xhprof_compute_inclusive_times($raw_data) {
568   global $display_calls;
570   $metrics = xhprof_get_metrics($raw_data);
572   $symbol_tab = array();
574   /*
575    * First compute inclusive time for each function and total
576    * call count for each function across all parents the
577    * function is called from.
578    */
579   foreach ($raw_data as $parent_child => $info) {
581     list($parent, $child) = xhprof_parse_parent_child($parent_child);
583     if ($parent == $child) {
584       /*
585        * XHProf PHP extension should never trigger this situation any more.
586        * Recursion is handled in the XHProf PHP extension by giving nested
587        * calls a unique recursion-depth appended name (for example, foo@1).
588        */
589       xhprof_error("Error in Raw Data: parent & child are both: $parent");
590       return;
591     }
593     if (!isset($symbol_tab[$child])) {
595       if ($display_calls) {
596         $symbol_tab[$child] = array("ct" => $info["ct"]);
597       } else {
598         $symbol_tab[$child] = array();
599       }
600       foreach ($metrics as $metric) {
601         $symbol_tab[$child][$metric] = $info[$metric];
602       }
603     } else {
604       if ($display_calls) {
605         /* increment call count for this child */
606         $symbol_tab[$child]["ct"] += $info["ct"];
607       }
609       /* update inclusive times/metric for this child  */
610       foreach ($metrics as $metric) {
611         $symbol_tab[$child][$metric] += $info[$metric];
612       }
613     }
614   }
616   return $symbol_tab;
620 /*
621  * Prunes XHProf raw data:
622  *
623  * Any node whose inclusive walltime accounts for less than $prune_percent
624  * of total walltime is pruned. [It is possible that a child function isn't
625  * pruned, but one or more of its parents get pruned. In such cases, when
626  * viewing the child function's hierarchical information, the cost due to
627  * the pruned parent(s) will be attributed to a special function/symbol
628  * "__pruned__()".]
629  *
630  *  @param   array  $raw_data      XHProf raw data to be pruned & validated.
631  *  @param   double $prune_percent Any edges that account for less than
632  *                                 $prune_percent of time will be pruned
633  *                                 from the raw data.
634  *
635  *  @return  array  Returns the pruned raw data.
636  *
637  *  @author Kannan
638  */
639 function xhprof_prune_run($raw_data, $prune_percent) {
641   $main_info = $raw_data["main()"];
642   if (empty($main_info)) {
643     xhprof_error("XHProf: main() missing in raw data");
644     return false;
645   }
647   // raw data should contain either wall time or samples information...
648   if (isset($main_info["wt"])) {
649     $prune_metric = "wt";
650   } else if (isset($main_info["samples"])) {
651     $prune_metric = "samples";
652   } else {
653     xhprof_error("XHProf: for main() we must have either wt "
654                  ."or samples attribute set");
655     return false;
656   }
658   // determine the metrics present in the raw data..
659   $metrics = array();
660   foreach ($main_info as $metric => $val) {
661     if (isset($val)) {
662       $metrics[] = $metric;
663     }
664   }
666   $prune_threshold = (($main_info[$prune_metric] * $prune_percent) / 100.0);
668   init_metrics($raw_data, null, null, false);
669   $flat_info = xhprof_compute_inclusive_times($raw_data);
671   foreach ($raw_data as $parent_child => $info) {
673     list($parent, $child) = xhprof_parse_parent_child($parent_child);
675     // is this child's overall total from all parents less than threshold?
676     if ($flat_info[$child][$prune_metric] < $prune_threshold) {
677       unset($raw_data[$parent_child]); // prune the edge
678     } else if ($parent &&
679                ($parent != "__pruned__()") &&
680                ($flat_info[$parent][$prune_metric] < $prune_threshold)) {
682       // Parent's overall inclusive metric is less than a threshold.
683       // All edges to the parent node will get nuked, and this child will
684       // be a dangling child.
685       // So instead change its parent to be a special function __pruned__().
686       $pruned_edge = xhprof_build_parent_child_key("__pruned__()", $child);
688       if (isset($raw_data[$pruned_edge])) {
689         foreach ($metrics as $metric) {
690           $raw_data[$pruned_edge][$metric]+=$raw_data[$parent_child][$metric];
691         }
692       } else {
693         $raw_data[$pruned_edge] = $raw_data[$parent_child];
694       }
696       unset($raw_data[$parent_child]); // prune the edge
697     }
698   }
700   return $raw_data;
704 /**
705  * Set one key in an array and return the array
706  *
707  * @author Kannan
708  */
709 function xhprof_array_set($arr, $k, $v) {
710   $arr[$k] = $v;
711   return $arr;
714 /**
715  * Removes/unsets one key in an array and return the array
716  *
717  * @author Kannan
718  */
719 function xhprof_array_unset($arr, $k) {
720   unset($arr[$k]);
721   return $arr;
724 /**
725  * Type definitions for URL params
726  */
727 define('XHPROF_STRING_PARAM', 1);
728 define('XHPROF_UINT_PARAM',   2);
729 define('XHPROF_FLOAT_PARAM',  3);
730 define('XHPROF_BOOL_PARAM',   4);
733 /**
734  * Internal helper function used by various
735  * xhprof_get_param* flavors for various
736  * types of parameters.
737  *
738  * @param string   name of the URL query string param
739  *
740  * @author Kannan
741  */
742 function xhprof_get_param_helper($param) {
743   $val = null;
744   if (isset($_GET[$param]))
745     $val = $_GET[$param];
746   else if (isset($_POST[$param])) {
747     $val = $_POST[$param];
748   }
749   return $val;
752 /**
753  * Extracts value for string param $param from query
754  * string. If param is not specified, return the
755  * $default value.
756  *
757  * @author Kannan
758  */
759 function xhprof_get_string_param($param, $default = '') {
760   $val = xhprof_get_param_helper($param);
762   if ($val === null)
763     return $default;
765   return $val;
768 /**
769  * Extracts value for unsigned integer param $param from
770  * query string. If param is not specified, return the
771  * $default value.
772  *
773  * If value is not a valid unsigned integer, logs error
774  * and returns null.
775  *
776  * @author Kannan
777  */
778 function xhprof_get_uint_param($param, $default = 0) {
779   $val = xhprof_get_param_helper($param);
781   if ($val === null)
782     $val = $default;
784   // trim leading/trailing whitespace
785   $val = trim($val);
787   // if it only contains digits, then ok..
788   if (ctype_digit($val)) {
789     return $val;
790   }
792   xhprof_error("$param is $val. It must be an unsigned integer.");
793   return null;
797 /**
798  * Extracts value for a float param $param from
799  * query string. If param is not specified, return
800  * the $default value.
801  *
802  * If value is not a valid unsigned integer, logs error
803  * and returns null.
804  *
805  * @author Kannan
806  */
807 function xhprof_get_float_param($param, $default = 0) {
808   $val = xhprof_get_param_helper($param);
810   if ($val === null)
811     $val = $default;
813   // trim leading/trailing whitespace
814   $val = trim($val);
816   // TBD: confirm the value is indeed a float.
817   if (true) // for now..
818     return (float)$val;
820   xhprof_error("$param is $val. It must be a float.");
821   return null;
824 /**
825  * Extracts value for a boolean param $param from
826  * query string. If param is not specified, return
827  * the $default value.
828  *
829  * If value is not a valid unsigned integer, logs error
830  * and returns null.
831  *
832  * @author Kannan
833  */
834 function xhprof_get_bool_param($param, $default = false) {
835   $val = xhprof_get_param_helper($param);
837   if ($val === null)
838     $val = $default;
840   // trim leading/trailing whitespace
841   $val = trim($val);
843   switch (strtolower($val)) {
844   case '0':
845   case '1':
846     $val = (bool)$val;
847     break;
848   case 'true':
849   case 'on':
850   case 'yes':
851     $val = true;
852     break;
853   case 'false':
854   case 'off':
855   case 'no':
856     $val = false;
857     break;
858   default:
859     xhprof_error("$param is $val. It must be a valid boolean string.");
860     return null;
861   }
863   return $val;
867 /**
868  * Initialize params from URL query string. The function
869  * creates globals variables for each of the params
870  * and if the URL query string doesn't specify a particular
871  * param initializes them with the corresponding default
872  * value specified in the input.
873  *
874  * @params array $params An array whose keys are the names
875  *                       of URL params who value needs to
876  *                       be retrieved from the URL query
877  *                       string. PHP globals are created
878  *                       with these names. The value is
879  *                       itself an array with 2-elems (the
880  *                       param type, and its default value).
881  *                       If a param is not specified in the
882  *                       query string the default value is
883  *                       used.
884  * @author Kannan
885  */
886 function xhprof_param_init($params) {
887   /* Create variables specified in $params keys, init defaults */
888   foreach ($params as $k => $v) {
889     switch ($v[0]) {
890     case XHPROF_STRING_PARAM:
891       $p = xhprof_get_string_param($k, $v[1]);
892       break;
893     case XHPROF_UINT_PARAM:
894       $p = xhprof_get_uint_param($k, $v[1]);
895       break;
896     case XHPROF_FLOAT_PARAM:
897       $p = xhprof_get_float_param($k, $v[1]);
898       break;
899     case XHPROF_BOOL_PARAM:
900       $p = xhprof_get_bool_param($k, $v[1]);
901       break;
902     default:
903       xhprof_error("Invalid param type passed to xhprof_param_init: "
904                    . $v[0]);
905       exit();
906     }
908     if ($k === 'run') {
909       $p = implode(',', array_filter(explode(',', $p), 'ctype_xdigit'));
910     }
912     // create a global variable using the parameter name.
913     $GLOBALS[$k] = $p;
914   }
918 /**
919  * Given a partial query string $q return matching function names in
920  * specified XHProf run. This is used for the type ahead function
921  * selector.
922  *
923  * @author Kannan
924  */
925 function xhprof_get_matching_functions($q, $xhprof_data) {
927   $matches = array();
929   foreach ($xhprof_data as $parent_child => $info) {
930     list($parent, $child) = xhprof_parse_parent_child($parent_child);
931     if (stripos($parent, $q) !== false) {
932       $matches[$parent] = 1;
933     }
934     if (stripos($child, $q) !== false) {
935       $matches[$child] = 1;
936     }
937   }
939   $res = array_keys($matches);
941   // sort it so the answers are in some reliable order...
942   asort($res);
944   return ($res);