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