Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401]

   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  
  22  if (!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   */
  34  function 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   */
  52  function 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   */
 127  function 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   */
 150  function 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   */
 167  function 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   */
 188  function 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   */
 242  function 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   */
 270  function 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   */
 322  function 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   */
 456  function 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   */
 518  function 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   */
 569  function 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   */
 641  function 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   */
 711  function 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   */
 721  function xhprof_array_unset($arr, $k) {
 722    unset($arr[$k]);
 723    return $arr;
 724  }
 725  
 726  /**
 727   * Type definitions for URL params
 728   */
 729  define('XHPROF_STRING_PARAM', 1);
 730  define('XHPROF_UINT_PARAM',   2);
 731  define('XHPROF_FLOAT_PARAM',  3);
 732  define('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   */
 744  function 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   */
 761  function 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   */
 780  function 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   */
 809  function 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   */
 836  function 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   */
 888  function 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   */
 931  function 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  }