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