xhprof_lib.php 26 KB

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