Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403]

   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  // XHProf: A Hierarchical Profiler for PHP
  19  //
  20  // XHProf has two components:
  21  //
  22  //  * This module is the UI/reporting component, used
  23  //    for viewing results of XHProf runs from a browser.
  24  //
  25  //  * Data collection component: This is implemented
  26  //    as a PHP extension (XHProf).
  27  //
  28  // @author Kannan Muthukkaruppan
  29  //
  30  
  31  if (!isset($GLOBALS['XHPROF_LIB_ROOT'])) {
  32    // by default, the parent directory is XHPROF lib root
  33    $GLOBALS['XHPROF_LIB_ROOT'] = realpath(dirname(__FILE__) . '/..');
  34  }
  35  
  36  require_once $GLOBALS['XHPROF_LIB_ROOT'].'/utils/xhprof_lib.php';
  37  require_once $GLOBALS['XHPROF_LIB_ROOT'].'/utils/callgraph_utils.php';
  38  require_once $GLOBALS['XHPROF_LIB_ROOT'].'/utils/xhprof_runs.php';
  39  
  40  
  41  /**
  42   * Our coding convention disallows relative paths in hrefs.
  43   * Get the base URL path from the SCRIPT_NAME.
  44   */
  45  $base_path = rtrim(dirname($_SERVER['SCRIPT_NAME']), '/\\');
  46  
  47  
  48  /**
  49   * Generate references to required stylesheets & javascript.
  50   *
  51   * If the calling script (such as index.php) resides in
  52   * a different location that than 'xhprof_html' directory the
  53   * caller must provide the URL path to 'xhprof_html' directory
  54   * so that the correct location of the style sheets/javascript
  55   * can be specified in the generated HTML.
  56   *
  57   */
  58  function xhprof_include_js_css($ui_dir_url_path = null) {
  59  
  60    if (empty($ui_dir_url_path)) {
  61      $ui_dir_url_path = rtrim(dirname($_SERVER['SCRIPT_NAME']), '/\\');
  62    }
  63  
  64    // style sheets
  65    echo "<link href='$ui_dir_url_path/css/xhprof.css' rel='stylesheet' ".
  66      " type='text/css' />";
  67    echo "<link href='$ui_dir_url_path/jquery/jquery.tooltip.css' ".
  68      " rel='stylesheet' type='text/css' />";
  69    echo "<link href='$ui_dir_url_path/jquery/jquery.autocomplete.css' ".
  70      " rel='stylesheet' type='text/css' />";
  71  
  72    // javascript
  73    echo "<script src='$ui_dir_url_path/jquery/jquery-1.2.6.js'>".
  74         "</script>";
  75    echo "<script src='$ui_dir_url_path/jquery/jquery.tooltip.js'>".
  76         "</script>";
  77    echo "<script src='$ui_dir_url_path/jquery/jquery.autocomplete.js'>"
  78         ."</script>";
  79    echo "<script src='$ui_dir_url_path/js/xhprof_report.js'></script>";
  80  }
  81  
  82  
  83  /*
  84   * Formats call counts for XHProf reports.
  85   *
  86   * Description:
  87   * Call counts in single-run reports are integer values.
  88   * However, call counts for aggregated reports can be
  89   * fractional. This function will print integer values
  90   * without decimal point, but with commas etc.
  91   *
  92   *   4000 ==> 4,000
  93   *
  94   * It'll round fractional values to decimal precision of 3
  95   *   4000.1212 ==> 4,000.121
  96   *   4000.0001 ==> 4,000
  97   *
  98   */
  99  function xhprof_count_format($num) {
 100    $num = round($num, 3);
 101    if (round($num) == $num) {
 102      return number_format($num);
 103    } else {
 104      return number_format($num, 3);
 105    }
 106  }
 107  
 108  function xhprof_percent_format($s, $precision = 1) {
 109    return sprintf('%.'.$precision.'f%%', 100 * $s);
 110  }
 111  
 112  /**
 113   * Implodes the text for a bunch of actions (such as links, forms,
 114   * into a HTML list and returns the text.
 115   */
 116  function xhprof_render_actions($actions) {
 117    $out = array();
 118  
 119    if (count($actions)) {
 120      $out[] = '<ul class="xhprof_actions">';
 121      foreach ($actions as $action) {
 122        $out[] = '<li>'.$action.'</li>';
 123      }
 124      $out[] = '</ul>';
 125    }
 126  
 127    return implode('', $out);
 128  }
 129  
 130  
 131  /**
 132   * @param html-str $content  the text/image/innerhtml/whatever for the link
 133   * @param raw-str  $href
 134   * @param raw-str  $class
 135   * @param raw-str  $id
 136   * @param raw-str  $title
 137   * @param raw-str  $target
 138   * @param raw-str  $onclick
 139   * @param raw-str  $style
 140   * @param raw-str  $access
 141   * @param raw-str  $onmouseover
 142   * @param raw-str  $onmouseout
 143   * @param raw-str  $onmousedown
 144   * @param raw-str  $dir
 145   * @param raw-str  $rel
 146   */
 147  function xhprof_render_link($content, $href, $class='', $id='', $title='',
 148                              $target='',
 149                              $onclick='', $style='', $access='', $onmouseover='',
 150                              $onmouseout='', $onmousedown='') {
 151  
 152    if (!$content) {
 153      return '';
 154    }
 155  
 156    if ($href) {
 157      $link = '<a href="' . ($href) . '"';
 158    } else {
 159      $link = '<span';
 160    }
 161  
 162    if ($class) {
 163      $link .= ' class="' . ($class) . '"';
 164    }
 165    if ($id) {
 166      $link .= ' id="' . ($id) . '"';
 167    }
 168    if ($title) {
 169      $link .= ' title="' . ($title) . '"';
 170    }
 171    if ($target) {
 172      $link .= ' target="' . ($target) . '"';
 173    }
 174    if ($onclick && $href) {
 175      $link .= ' onclick="' . ($onclick) . '"';
 176    }
 177    if ($style && $href) {
 178      $link .= ' style="' . ($style) . '"';
 179    }
 180    if ($access && $href) {
 181      $link .= ' accesskey="' . ($access) . '"';
 182    }
 183    if ($onmouseover) {
 184      $link .= ' onmouseover="' . ($onmouseover) . '"';
 185    }
 186    if ($onmouseout) {
 187      $link .= ' onmouseout="' . ($onmouseout) . '"';
 188    }
 189    if ($onmousedown) {
 190      $link .= ' onmousedown="' . ($onmousedown) . '"';
 191    }
 192  
 193    $link .= '>';
 194    $link .= $content;
 195    if ($href) {
 196      $link .= '</a>';
 197    } else {
 198      $link .= '</span>';
 199    }
 200  
 201    return $link;
 202  }
 203  
 204  
 205  // default column to sort on -- wall time
 206  $sort_col = "wt";
 207  
 208  // default is "single run" report
 209  $diff_mode = false;
 210  
 211  // call count data present?
 212  $display_calls = true;
 213  
 214  // The following column headers are sortable
 215  $sortable_columns = array("fn" => 1,
 216                            "ct" => 1,
 217                            "wt" => 1,
 218                            "excl_wt" => 1,
 219                            "ut" => 1,
 220                            "excl_ut" => 1,
 221                            "st" => 1,
 222                            "excl_st" => 1,
 223                            "mu" => 1,
 224                            "excl_mu" => 1,
 225                            "pmu" => 1,
 226                            "excl_pmu" => 1,
 227                            "cpu" => 1,
 228                            "excl_cpu" => 1,
 229                            "samples" => 1,
 230                            "excl_samples" => 1
 231                            );
 232  
 233  // Textual descriptions for column headers in "single run" mode
 234  $descriptions = array(
 235                        "fn" => "Function Name",
 236                        "ct" =>  "Calls",
 237                        "Calls%" => "Calls%",
 238  
 239                        "wt" => "Incl. Wall Time<br>(microsec)",
 240                        "IWall%" => "IWall%",
 241                        "excl_wt" => "Excl. Wall Time<br>(microsec)",
 242                        "EWall%" => "EWall%",
 243  
 244                        "ut" => "Incl. User<br>(microsecs)",
 245                        "IUser%" => "IUser%",
 246                        "excl_ut" => "Excl. User<br>(microsec)",
 247                        "EUser%" => "EUser%",
 248  
 249                        "st" => "Incl. Sys <br>(microsec)",
 250                        "ISys%" => "ISys%",
 251                        "excl_st" => "Excl. Sys <br>(microsec)",
 252                        "ESys%" => "ESys%",
 253  
 254                        "cpu" => "Incl. CPU<br>(microsecs)",
 255                        "ICpu%" => "ICpu%",
 256                        "excl_cpu" => "Excl. CPU<br>(microsec)",
 257                        "ECpu%" => "ECPU%",
 258  
 259                        "mu" => "Incl.<br>MemUse<br>(bytes)",
 260                        "IMUse%" => "IMemUse%",
 261                        "excl_mu" => "Excl.<br>MemUse<br>(bytes)",
 262                        "EMUse%" => "EMemUse%",
 263  
 264                        "pmu" => "Incl.<br> PeakMemUse<br>(bytes)",
 265                        "IPMUse%" => "IPeakMemUse%",
 266                        "excl_pmu" => "Excl.<br>PeakMemUse<br>(bytes)",
 267                        "EPMUse%" => "EPeakMemUse%",
 268  
 269                        "samples" => "Incl. Samples",
 270                        "ISamples%" => "ISamples%",
 271                        "excl_samples" => "Excl. Samples",
 272                        "ESamples%" => "ESamples%",
 273                        );
 274  
 275  // Formatting Callback Functions...
 276  $format_cbk = array(
 277                        "fn" => "",
 278                        "ct" => "xhprof_count_format",
 279                        "Calls%" => "xhprof_percent_format",
 280  
 281                        "wt" => "number_format",
 282                        "IWall%" => "xhprof_percent_format",
 283                        "excl_wt" => "number_format",
 284                        "EWall%" => "xhprof_percent_format",
 285  
 286                        "ut" => "number_format",
 287                        "IUser%" => "xhprof_percent_format",
 288                        "excl_ut" => "number_format",
 289                        "EUser%" => "xhprof_percent_format",
 290  
 291                        "st" => "number_format",
 292                        "ISys%" => "xhprof_percent_format",
 293                        "excl_st" => "number_format",
 294                        "ESys%" => "xhprof_percent_format",
 295  
 296                        "cpu" => "number_format",
 297                        "ICpu%" => "xhprof_percent_format",
 298                        "excl_cpu" => "number_format",
 299                        "ECpu%" => "xhprof_percent_format",
 300  
 301                        "mu" => "number_format",
 302                        "IMUse%" => "xhprof_percent_format",
 303                        "excl_mu" => "number_format",
 304                        "EMUse%" => "xhprof_percent_format",
 305  
 306                        "pmu" => "number_format",
 307                        "IPMUse%" => "xhprof_percent_format",
 308                        "excl_pmu" => "number_format",
 309                        "EPMUse%" => "xhprof_percent_format",
 310  
 311                        "samples" => "number_format",
 312                        "ISamples%" => "xhprof_percent_format",
 313                        "excl_samples" => "number_format",
 314                        "ESamples%" => "xhprof_percent_format",
 315                        );
 316  
 317  
 318  // Textual descriptions for column headers in "diff" mode
 319  $diff_descriptions = array(
 320                        "fn" => "Function Name",
 321                        "ct" =>  "Calls Diff",
 322                        "Calls%" => "Calls<br>Diff%",
 323  
 324                        "wt" => "Incl. Wall<br>Diff<br>(microsec)",
 325                        "IWall%" => "IWall<br> Diff%",
 326                        "excl_wt" => "Excl. Wall<br>Diff<br>(microsec)",
 327                        "EWall%" => "EWall<br>Diff%",
 328  
 329                        "ut" => "Incl. User Diff<br>(microsec)",
 330                        "IUser%" => "IUser<br>Diff%",
 331                        "excl_ut" => "Excl. User<br>Diff<br>(microsec)",
 332                        "EUser%" => "EUser<br>Diff%",
 333  
 334                        "cpu" => "Incl. CPU Diff<br>(microsec)",
 335                        "ICpu%" => "ICpu<br>Diff%",
 336                        "excl_cpu" => "Excl. CPU<br>Diff<br>(microsec)",
 337                        "ECpu%" => "ECpu<br>Diff%",
 338  
 339                        "st" => "Incl. Sys Diff<br>(microsec)",
 340                        "ISys%" => "ISys<br>Diff%",
 341                        "excl_st" => "Excl. Sys Diff<br>(microsec)",
 342                        "ESys%" => "ESys<br>Diff%",
 343  
 344                        "mu" => "Incl.<br>MemUse<br>Diff<br>(bytes)",
 345                        "IMUse%" => "IMemUse<br>Diff%",
 346                        "excl_mu" => "Excl.<br>MemUse<br>Diff<br>(bytes)",
 347                        "EMUse%" => "EMemUse<br>Diff%",
 348  
 349                        "pmu" => "Incl.<br> PeakMemUse<br>Diff<br>(bytes)",
 350                        "IPMUse%" => "IPeakMemUse<br>Diff%",
 351                        "excl_pmu" => "Excl.<br>PeakMemUse<br>Diff<br>(bytes)",
 352                        "EPMUse%" => "EPeakMemUse<br>Diff%",
 353  
 354                        "samples" => "Incl. Samples Diff",
 355                        "ISamples%" => "ISamples Diff%",
 356                        "excl_samples" => "Excl. Samples Diff",
 357                        "ESamples%" => "ESamples Diff%",
 358                        );
 359  
 360  // columns that'll be displayed in a top-level report
 361  $stats = array();
 362  
 363  // columns that'll be displayed in a function's parent/child report
 364  $pc_stats = array();
 365  
 366  // Various total counts
 367  $totals = 0;
 368  $totals_1 = 0;
 369  $totals_2 = 0;
 370  
 371  /*
 372   * The subset of $possible_metrics that is present in the raw profile data.
 373   */
 374  $metrics = null;
 375  
 376  /**
 377   * Callback comparison operator (passed to usort() for sorting array of
 378   * tuples) that compares array elements based on the sort column
 379   * specified in $sort_col (global parameter).
 380   *
 381   * @author Kannan
 382   */
 383  function sort_cbk($a, $b) {
 384    global $sort_col;
 385    global $diff_mode;
 386  
 387    if ($sort_col == "fn") {
 388  
 389      // case insensitive ascending sort for function names
 390      $left = strtoupper($a["fn"]);
 391      $right = strtoupper($b["fn"]);
 392  
 393      if ($left == $right)
 394        return 0;
 395      return ($left < $right) ? -1 : 1;
 396  
 397    } else {
 398  
 399      // descending sort for all others
 400      $left = $a[$sort_col];
 401      $right = $b[$sort_col];
 402  
 403      // if diff mode, sort by absolute value of regression/improvement
 404      if ($diff_mode) {
 405        $left = abs($left);
 406        $right = abs($right);
 407      }
 408  
 409      if ($left == $right)
 410        return 0;
 411      return ($left > $right) ? -1 : 1;
 412    }
 413  }
 414  
 415  /**
 416   * Get the appropriate description for a statistic
 417   * (depending upon whether we are in diff report mode
 418   * or single run report mode).
 419   *
 420   * @author Kannan
 421   */
 422  function stat_description($stat) {
 423    global $descriptions;
 424    global $diff_descriptions;
 425    global $diff_mode;
 426  
 427    if ($diff_mode) {
 428      return $diff_descriptions[$stat];
 429    } else {
 430      return $descriptions[$stat];
 431    }
 432  }
 433  
 434  
 435  /**
 436   * Analyze raw data & generate the profiler report
 437   * (common for both single run mode and diff mode).
 438   *
 439   * @author: Kannan
 440   */
 441  function profiler_report ($url_params,
 442                            $rep_symbol,
 443                            $sort,
 444                            $run1,
 445                            $run1_desc,
 446                            $run1_data,
 447                            $run2 = 0,
 448                            $run2_desc = "",
 449                            $run2_data = array()) {
 450    global $totals;
 451    global $totals_1;
 452    global $totals_2;
 453    global $stats;
 454    global $pc_stats;
 455    global $diff_mode;
 456    global $base_path;
 457  
 458    // if we are reporting on a specific function, we can trim down
 459    // the report(s) to just stuff that is relevant to this function.
 460    // That way compute_flat_info()/compute_diff() etc. do not have
 461    // to needlessly work hard on churning irrelevant data.
 462    if (!empty($rep_symbol)) {
 463      $run1_data = xhprof_trim_run($run1_data, array($rep_symbol));
 464      if ($diff_mode) {
 465        $run2_data = xhprof_trim_run($run2_data, array($rep_symbol));
 466      }
 467    }
 468  
 469    if ($diff_mode) {
 470      $run_delta = xhprof_compute_diff($run1_data, $run2_data);
 471      $symbol_tab  = xhprof_compute_flat_info($run_delta, $totals);
 472      $symbol_tab1 = xhprof_compute_flat_info($run1_data, $totals_1);
 473      $symbol_tab2 = xhprof_compute_flat_info($run2_data, $totals_2);
 474    } else {
 475      $symbol_tab = xhprof_compute_flat_info($run1_data, $totals);
 476    }
 477  
 478    $run1_txt = sprintf("<b>Run #%s:</b> %s",
 479                        $run1, $run1_desc);
 480  
 481    $base_url_params = xhprof_array_unset(xhprof_array_unset($url_params,
 482                                                             'symbol'),
 483                                          'all');
 484  
 485    $top_link_query_string = "$base_path/?" . http_build_query($base_url_params);
 486  
 487    if ($diff_mode) {
 488      $diff_text = "Diff";
 489      $base_url_params = xhprof_array_unset($base_url_params, 'run1');
 490      $base_url_params = xhprof_array_unset($base_url_params, 'run2');
 491      $run1_link = xhprof_render_link('View Run #' . $run1,
 492                             "$base_path/?" .
 493                             http_build_query(xhprof_array_set($base_url_params,
 494                                                        'run',
 495                                                        $run1)));
 496      $run2_txt = sprintf("<b>Run #%s:</b> %s",
 497                          $run2, $run2_desc);
 498  
 499      $run2_link = xhprof_render_link('View Run #' . $run2,
 500                                      "$base_path/?" .
 501                          http_build_query(xhprof_array_set($base_url_params,
 502                                                            'run',
 503                                                            $run2)));
 504    } else {
 505      $diff_text = "Run";
 506    }
 507  
 508    // set up the action links for operations that can be done on this report
 509    $links = array();
 510    $links [] =  xhprof_render_link("View Top Level $diff_text Report",
 511                                   $top_link_query_string);
 512  
 513    if ($diff_mode) {
 514      $inverted_params = $url_params;
 515      $inverted_params['run1'] = $url_params['run2'];
 516      $inverted_params['run2'] = $url_params['run1'];
 517  
 518      // view the different runs or invert the current diff
 519      $links [] = $run1_link;
 520      $links [] = $run2_link;
 521      $links [] = xhprof_render_link('Invert ' . $diff_text . ' Report',
 522                             "$base_path/?".
 523                             http_build_query($inverted_params));
 524    }
 525  
 526    // lookup function typeahead form
 527    $links [] = '<input class="function_typeahead" ' .
 528                ' type="input" size="40" maxlength="100" />';
 529  
 530    echo xhprof_render_actions($links);
 531  
 532  
 533    echo
 534      '<dl class=phprof_report_info>' .
 535      '  <dt>' . $diff_text . ' Report</dt>' .
 536      '  <dd>' . ($diff_mode ?
 537                  $run1_txt . '<br><b>vs.</b><br>' . $run2_txt :
 538                  $run1_txt) .
 539      '  </dd>' .
 540      '  <dt>Tip</dt>' .
 541      '  <dd>Click a function name below to drill down.</dd>' .
 542      '</dl>' .
 543      '<div style="clear: both; margin: 3em 0em;"></div>';
 544  
 545    // data tables
 546    if (!empty($rep_symbol)) {
 547      if (!isset($symbol_tab[$rep_symbol])) {
 548        echo "<hr>Symbol <b>$rep_symbol</b> not found in XHProf run</b><hr>";
 549        return;
 550      }
 551  
 552      /* single function report with parent/child information */
 553      if ($diff_mode) {
 554        $info1 = isset($symbol_tab1[$rep_symbol]) ?
 555                         $symbol_tab1[$rep_symbol] : null;
 556        $info2 = isset($symbol_tab2[$rep_symbol]) ?
 557                         $symbol_tab2[$rep_symbol] : null;
 558        symbol_report($url_params, $run_delta, $symbol_tab[$rep_symbol],
 559                      $sort, $rep_symbol,
 560                      $run1, $info1,
 561                      $run2, $info2);
 562      } else {
 563        symbol_report($url_params, $run1_data, $symbol_tab[$rep_symbol],
 564                      $sort, $rep_symbol, $run1);
 565      }
 566    } else {
 567      /* flat top-level report of all functions */
 568      full_report($url_params, $symbol_tab, $sort, $run1, $run2);
 569    }
 570  }
 571  
 572  /**
 573   * Computes percentage for a pair of values, and returns it
 574   * in string format.
 575   */
 576  function pct($a, $b) {
 577    if ($b == 0) {
 578      return "N/A";
 579    } else {
 580      $res = (round(($a * 1000 / $b)) / 10);
 581      return $res;
 582    }
 583  }
 584  
 585  /**
 586   * Given a number, returns the td class to use for display.
 587   *
 588   * For instance, negative numbers in diff reports comparing two runs (run1 & run2)
 589   * represent improvement from run1 to run2. We use green to display those deltas,
 590   * and red for regression deltas.
 591   */
 592  function get_print_class($num, $bold) {
 593    global $vbar;
 594    global $vbbar;
 595    global $vrbar;
 596    global $vgbar;
 597    global $diff_mode;
 598  
 599    if ($bold) {
 600      if ($diff_mode) {
 601        if ($num <= 0) {
 602          $class = $vgbar; // green (improvement)
 603        } else {
 604          $class = $vrbar; // red (regression)
 605        }
 606      } else {
 607        $class = $vbbar; // blue
 608      }
 609    }
 610    else {
 611      $class = $vbar;  // default (black)
 612    }
 613  
 614    return $class;
 615  }
 616  
 617  /**
 618   * Prints a <td> element with a numeric value.
 619   */
 620  function print_td_num($num, $fmt_func, $bold=false, $attributes=null) {
 621  
 622    $class = get_print_class($num, $bold);
 623  
 624    if (!empty($fmt_func) && is_numeric($num) ) {
 625      $num = call_user_func($fmt_func, $num);
 626    }
 627  
 628    print("<td $attributes $class>$num</td>\n");
 629  }
 630  
 631  /**
 632   * Prints a <td> element with a pecentage.
 633   */
 634  function print_td_pct($numer, $denom, $bold=false, $attributes=null) {
 635    global $vbar;
 636    global $vbbar;
 637    global $diff_mode;
 638  
 639    $class = get_print_class($numer, $bold);
 640  
 641    if ($denom == 0) {
 642      $pct = "N/A%";
 643    } else {
 644      $pct = xhprof_percent_format($numer / abs($denom));
 645    }
 646  
 647    print("<td $attributes $class>$pct</td>\n");
 648  }
 649  
 650  /**
 651   * Print "flat" data corresponding to one function.
 652   *
 653   * @author Kannan
 654   */
 655  function print_function_info($url_params, $info, $sort, $run1, $run2) {
 656    static $odd_even = 0;
 657  
 658    global $totals;
 659    global $sort_col;
 660    global $metrics;
 661    global $format_cbk;
 662    global $display_calls;
 663    global $base_path;
 664  
 665    // Toggle $odd_or_even
 666    $odd_even = 1 - $odd_even;
 667  
 668    if ($odd_even) {
 669      print("<tr>");
 670    }
 671    else {
 672      print('<tr bgcolor="#e5e5e5">');
 673    }
 674  
 675    $href = "$base_path/?" .
 676             http_build_query(xhprof_array_set($url_params,
 677                                               'symbol', $info["fn"]));
 678  
 679    print('<td>');
 680    print(xhprof_render_link($info["fn"], $href));
 681    print_source_link($info);
 682    print("</td>\n");
 683  
 684    if ($display_calls) {
 685      // Call Count..
 686      print_td_num($info["ct"], $format_cbk["ct"], ($sort_col == "ct"));
 687      print_td_pct($info["ct"], $totals["ct"], ($sort_col == "ct"));
 688    }
 689  
 690    // Other metrics..
 691    foreach ($metrics as $metric) {
 692      // Inclusive metric
 693      print_td_num($info[$metric], $format_cbk[$metric],
 694                   ($sort_col == $metric));
 695      print_td_pct($info[$metric], $totals[$metric],
 696                   ($sort_col == $metric));
 697  
 698      // Exclusive Metric
 699      print_td_num($info["excl_" . $metric],
 700                   $format_cbk["excl_" . $metric],
 701                   ($sort_col == "excl_" . $metric));
 702      print_td_pct($info["excl_" . $metric],
 703                   $totals[$metric],
 704                   ($sort_col == "excl_" . $metric));
 705    }
 706  
 707    print("</tr>\n");
 708  }
 709  
 710  /**
 711   * Print non-hierarchical (flat-view) of profiler data.
 712   *
 713   * @author Kannan
 714   */
 715  function print_flat_data($url_params, $title, $flat_data, $sort, $run1, $run2, $limit) {
 716  
 717    global $stats;
 718    global $sortable_columns;
 719    global $vwbar;
 720    global $base_path;
 721  
 722    $size  = count($flat_data);
 723    if (!$limit) {              // no limit
 724      $limit = $size;
 725      $display_link = "";
 726    } else {
 727      $display_link = xhprof_render_link(" [ <b class=bubble>display all </b>]",
 728                                         "$base_path/?" .
 729                                         http_build_query(xhprof_array_set($url_params,
 730                                                                           'all', 1)));
 731    }
 732  
 733    print("<h3 align=center>$title $display_link</h3><br>");
 734  
 735    print('<table border=1 cellpadding=2 cellspacing=1 width="90%" '
 736          .'rules=rows bordercolor="#bdc7d8" align=center>');
 737    print('<tr bgcolor="#bdc7d8" align=right>');
 738  
 739    foreach ($stats as $stat) {
 740      $desc = stat_description($stat);
 741      if (array_key_exists($stat, $sortable_columns)) {
 742        $href = "$base_path/?"
 743                . http_build_query(xhprof_array_set($url_params, 'sort', $stat));
 744        $header = xhprof_render_link($desc, $href);
 745      } else {
 746        $header = $desc;
 747      }
 748  
 749      if ($stat == "fn")
 750        print("<th align=left><nobr>$header</th>");
 751      else print("<th " . $vwbar . "><nobr>$header</th>");
 752    }
 753    print("</tr>\n");
 754  
 755    if ($limit >= 0) {
 756      $limit = min($size, $limit);
 757      for ($i = 0; $i < $limit; $i++) {
 758        print_function_info($url_params, $flat_data[$i], $sort, $run1, $run2);
 759      }
 760    } else {
 761      // if $limit is negative, print abs($limit) items starting from the end
 762      $limit = min($size, abs($limit));
 763      for ($i = 0; $i < $limit; $i++) {
 764        print_function_info($url_params, $flat_data[$size - $i - 1], $sort, $run1, $run2);
 765      }
 766    }
 767    print("</table>");
 768  
 769    // let's print the display all link at the bottom as well...
 770    if ($display_link) {
 771      echo '<div style="text-align: left; padding: 2em">' . $display_link . '</div>';
 772    }
 773  
 774  }
 775  
 776  /**
 777   * Generates a tabular report for all functions. This is the top-level report.
 778   *
 779   * @author Kannan
 780   */
 781  function full_report($url_params, $symbol_tab, $sort, $run1, $run2) {
 782    global $vwbar;
 783    global $vbar;
 784    global $totals;
 785    global $totals_1;
 786    global $totals_2;
 787    global $metrics;
 788    global $diff_mode;
 789    global $descriptions;
 790    global $sort_col;
 791    global $format_cbk;
 792    global $display_calls;
 793    global $base_path;
 794  
 795    $possible_metrics = xhprof_get_possible_metrics();
 796  
 797    if ($diff_mode) {
 798  
 799      $base_url_params = xhprof_array_unset(xhprof_array_unset($url_params,
 800                                                               'run1'),
 801                                            'run2');
 802      $href1 = "$base_path/?" .
 803        http_build_query(xhprof_array_set($base_url_params,
 804                                          'run', $run1));
 805      $href2 = "$base_path/?" .
 806        http_build_query(xhprof_array_set($base_url_params,
 807                                          'run', $run2));
 808  
 809      print("<h3><center>Overall Diff Summary</center></h3>");
 810      print('<table border=1 cellpadding=2 cellspacing=1 width="30%" '
 811            .'rules=rows bordercolor="#bdc7d8" align=center>' . "\n");
 812      print('<tr bgcolor="#bdc7d8" align=right>');
 813      print("<th></th>");
 814      print("<th $vwbar>" . xhprof_render_link("Run #$run1", $href1) . "</th>");
 815      print("<th $vwbar>" . xhprof_render_link("Run #$run2", $href2) . "</th>");
 816      print("<th $vwbar>Diff</th>");
 817      print("<th $vwbar>Diff%</th>");
 818      print('</tr>');
 819  
 820      if ($display_calls) {
 821        print('<tr>');
 822        print("<td>Number of Function Calls</td>");
 823        print_td_num($totals_1["ct"], $format_cbk["ct"]);
 824        print_td_num($totals_2["ct"], $format_cbk["ct"]);
 825        print_td_num($totals_2["ct"] - $totals_1["ct"], $format_cbk["ct"], true);
 826        print_td_pct($totals_2["ct"] - $totals_1["ct"], $totals_1["ct"], true);
 827        print('</tr>');
 828      }
 829  
 830      foreach ($metrics as $metric) {
 831        $m = $metric;
 832        print('<tr>');
 833        print("<td>" . str_replace("<br>", " ", $descriptions[$m]) . "</td>");
 834        print_td_num($totals_1[$m], $format_cbk[$m]);
 835        print_td_num($totals_2[$m], $format_cbk[$m]);
 836        print_td_num($totals_2[$m] - $totals_1[$m], $format_cbk[$m], true);
 837        print_td_pct($totals_2[$m] - $totals_1[$m], $totals_1[$m], true);
 838        print('<tr>');
 839      }
 840      print('</table>');
 841  
 842      $callgraph_report_title = '[View Regressions/Improvements using Callgraph Diff]';
 843  
 844    } else {
 845      print("<p><center>\n");
 846  
 847      print('<table cellpadding=2 cellspacing=1 width="30%" '
 848            .'bgcolor="#bdc7d8" align=center>' . "\n");
 849      echo "<tr>";
 850      echo "<th style='text-align:right'>Overall Summary</th>";
 851      echo "<th></th>";
 852      echo "</tr>";
 853  
 854      foreach ($metrics as $metric) {
 855        echo "<tr>";
 856        echo "<td style='text-align:right; font-weight:bold'>Total "
 857              . str_replace("<br>", " ", stat_description($metric)) . ":</td>";
 858        echo "<td>" . number_format($totals[$metric]) .  " "
 859             . $possible_metrics[$metric][1] . "</td>";
 860        echo "</tr>";
 861      }
 862  
 863      if ($display_calls) {
 864        echo "<tr>";
 865        echo "<td style='text-align:right; font-weight:bold'>Number of Function Calls:</td>";
 866        echo "<td>" . number_format($totals['ct']) . "</td>";
 867        echo "</tr>";
 868      }
 869  
 870      echo "</table>";
 871      print("</center></p>\n");
 872  
 873      $callgraph_report_title = '[View Full Callgraph]';
 874    }
 875  
 876    print("<center><br><h3>" .
 877          xhprof_render_link($callgraph_report_title,
 878                      "$base_path/callgraph.php" . "?" . http_build_query($url_params))
 879          . "</h3></center>");
 880  
 881  
 882    $flat_data = array();
 883    foreach ($symbol_tab as $symbol => $info) {
 884      $tmp = $info;
 885      $tmp["fn"] = $symbol;
 886      $flat_data[] = $tmp;
 887    }
 888    usort($flat_data, 'sort_cbk');
 889  
 890    print("<br>");
 891  
 892    if (!empty($url_params['all'])) {
 893      $all = true;
 894      $limit = 0;    // display all rows
 895    } else {
 896      $all = false;
 897      $limit = 100;  // display only limited number of rows
 898    }
 899  
 900    $desc = str_replace("<br>", " ", $descriptions[$sort_col]);
 901  
 902    if ($diff_mode) {
 903      if ($all) {
 904        $title = "Total Diff Report: '
 905                 .'Sorted by absolute value of regression/improvement in $desc";
 906      } else {
 907        $title = "Top 100 <i style='color:red'>Regressions</i>/"
 908                 . "<i style='color:green'>Improvements</i>: "
 909                 . "Sorted by $desc Diff";
 910      }
 911    } else {
 912      if ($all) {
 913        $title = "Sorted by $desc";
 914      } else {
 915        $title = "Displaying top $limit functions: Sorted by $desc";
 916      }
 917    }
 918    print_flat_data($url_params, $title, $flat_data, $sort, $run1, $run2, $limit);
 919  }
 920  
 921  
 922  /**
 923   * Return attribute names and values to be used by javascript tooltip.
 924   */
 925  function get_tooltip_attributes($type, $metric) {
 926    return "type='$type' metric='$metric'";
 927  }
 928  
 929  /**
 930   * Print info for a parent or child function in the
 931   * parent & children report.
 932   *
 933   * @author Kannan
 934   */
 935  function pc_info($info, $base_ct, $base_info, $parent) {
 936    global $sort_col;
 937    global $metrics;
 938    global $format_cbk;
 939    global $display_calls;
 940  
 941    if ($parent)
 942      $type = "Parent";
 943    else $type = "Child";
 944  
 945    if ($display_calls) {
 946      $mouseoverct = get_tooltip_attributes($type, "ct");
 947      /* call count */
 948      print_td_num($info["ct"], $format_cbk["ct"], ($sort_col == "ct"), $mouseoverct);
 949      print_td_pct($info["ct"], $base_ct, ($sort_col == "ct"), $mouseoverct);
 950    }
 951  
 952    /* Inclusive metric values  */
 953    foreach ($metrics as $metric) {
 954      print_td_num($info[$metric], $format_cbk[$metric],
 955                   ($sort_col == $metric),
 956                   get_tooltip_attributes($type, $metric));
 957      print_td_pct($info[$metric], $base_info[$metric], ($sort_col == $metric),
 958                   get_tooltip_attributes($type, $metric));
 959    }
 960  }
 961  
 962  function print_pc_array($url_params, $results, $base_ct, $base_info, $parent,
 963                          $run1, $run2) {
 964    global $base_path;
 965  
 966    // Construct section title
 967    if ($parent) {
 968      $title = 'Parent function';
 969    }
 970    else {
 971      $title = 'Child function';
 972    }
 973    if (count($results) > 1) {
 974      $title .= 's';
 975    }
 976  
 977    print("<tr bgcolor='#e0e0ff'><td>");
 978    print("<b><i><center>" . $title . "</center></i></b>");
 979    print("</td></tr>");
 980  
 981    $odd_even = 0;
 982    foreach ($results as $info) {
 983      $href = "$base_path/?" .
 984        http_build_query(xhprof_array_set($url_params,
 985                                          'symbol', $info["fn"]));
 986  
 987      $odd_even = 1 - $odd_even;
 988  
 989      if ($odd_even) {
 990        print('<tr>');
 991      }
 992      else {
 993        print('<tr bgcolor="#e5e5e5">');
 994      }
 995  
 996      print("<td>" . xhprof_render_link($info["fn"], $href));
 997      print_source_link($info);
 998      print("</td>");
 999      pc_info($info, $base_ct, $base_info, $parent);
1000      print("</tr>");
1001    }
1002  }
1003  
1004  function print_source_link($info) {
1005    if (strncmp($info['fn'], 'run_init', 8) && $info['fn'] !== 'main()') {
1006      if (defined('XHPROF_SYMBOL_LOOKUP_URL')) {
1007        $link = xhprof_render_link(
1008          'source',
1009          XHPROF_SYMBOL_LOOKUP_URL . '?symbol='.rawurlencode($info["fn"]));
1010        print(' ('.$link.')');
1011      }
1012    }
1013  }
1014  
1015  
1016  function print_symbol_summary($symbol_info, $stat, $base) {
1017  
1018    $val = $symbol_info[$stat];
1019    $desc = str_replace("<br>", " ", stat_description($stat));
1020  
1021    print("$desc: </td>");
1022    print(number_format($val));
1023    print(" (" . pct($val, $base) . "% of overall)");
1024    if (substr($stat, 0, 4) == "excl") {
1025      $func_base = $symbol_info[str_replace("excl_", "", $stat)];
1026      print(" (" . pct($val, $func_base) . "% of this function)");
1027    }
1028    print("<br>");
1029  }
1030  
1031  /**
1032   * Generates a report for a single function/symbol.
1033   *
1034   * @author Kannan
1035   */
1036  function symbol_report($url_params,
1037                         $run_data, $symbol_info, $sort, $rep_symbol,
1038                         $run1,
1039                         $symbol_info1 = null,
1040                         $run2 = 0,
1041                         $symbol_info2 = null) {
1042    global $vwbar;
1043    global $vbar;
1044    global $totals;
1045    global $pc_stats;
1046    global $sortable_columns;
1047    global $metrics;
1048    global $diff_mode;
1049    global $descriptions;
1050    global $format_cbk;
1051    global $sort_col;
1052    global $display_calls;
1053    global $base_path;
1054  
1055    $possible_metrics = xhprof_get_possible_metrics();
1056  
1057    if ($diff_mode) {
1058      $diff_text = "<b>Diff</b>";
1059      $regr_impr = "<i style='color:red'>Regression</i>/<i style='color:green'>Improvement</i>";
1060    } else {
1061      $diff_text = "";
1062      $regr_impr = "";
1063    }
1064  
1065    if ($diff_mode) {
1066  
1067      $base_url_params = xhprof_array_unset(xhprof_array_unset($url_params,
1068                                                               'run1'),
1069                                            'run2');
1070      $href1 = "$base_path?"
1071        . http_build_query(xhprof_array_set($base_url_params, 'run', $run1));
1072      $href2 = "$base_path?"
1073        . http_build_query(xhprof_array_set($base_url_params, 'run', $run2));
1074  
1075      print("<h3 align=center>$regr_impr summary for $rep_symbol<br><br></h3>");
1076      print('<table border=1 cellpadding=2 cellspacing=1 width="30%" '
1077            .'rules=rows bordercolor="#bdc7d8" align=center>' . "\n");
1078      print('<tr bgcolor="#bdc7d8" align=right>');
1079      print("<th align=left>$rep_symbol</th>");
1080      print("<th $vwbar><a href=" . $href1 . ">Run #$run1</a></th>");
1081      print("<th $vwbar><a href=" . $href2 . ">Run #$run2</a></th>");
1082      print("<th $vwbar>Diff</th>");
1083      print("<th $vwbar>Diff%</th>");
1084      print('</tr>');
1085      print('<tr>');
1086  
1087      if ($display_calls) {
1088        print("<td>Number of Function Calls</td>");
1089        print_td_num($symbol_info1["ct"], $format_cbk["ct"]);
1090        print_td_num($symbol_info2["ct"], $format_cbk["ct"]);
1091        print_td_num($symbol_info2["ct"] - $symbol_info1["ct"],
1092                     $format_cbk["ct"], true);
1093        print_td_pct($symbol_info2["ct"] - $symbol_info1["ct"],
1094                     $symbol_info1["ct"], true);
1095        print('</tr>');
1096      }
1097  
1098  
1099      foreach ($metrics as $metric) {
1100        $m = $metric;
1101  
1102        // Inclusive stat for metric
1103        print('<tr>');
1104        print("<td>" . str_replace("<br>", " ", $descriptions[$m]) . "</td>");
1105        print_td_num($symbol_info1[$m], $format_cbk[$m]);
1106        print_td_num($symbol_info2[$m], $format_cbk[$m]);
1107        print_td_num($symbol_info2[$m] - $symbol_info1[$m], $format_cbk[$m], true);
1108        print_td_pct($symbol_info2[$m] - $symbol_info1[$m], $symbol_info1[$m], true);
1109        print('</tr>');
1110  
1111        // AVG (per call) Inclusive stat for metric
1112        print('<tr>');
1113        print("<td>" . str_replace("<br>", " ", $descriptions[$m]) . " per call </td>");
1114        $avg_info1 = 'N/A';
1115        $avg_info2 = 'N/A';
1116        if ($symbol_info1['ct'] > 0) {
1117          $avg_info1 = ($symbol_info1[$m] / $symbol_info1['ct']);
1118        }
1119        if ($symbol_info2['ct'] > 0) {
1120          $avg_info2 = ($symbol_info2[$m] / $symbol_info2['ct']);
1121        }
1122        print_td_num($avg_info1, $format_cbk[$m]);
1123        print_td_num($avg_info2, $format_cbk[$m]);
1124        print_td_num($avg_info2 - $avg_info1, $format_cbk[$m], true);
1125        print_td_pct($avg_info2 - $avg_info1, $avg_info1, true);
1126        print('</tr>');
1127  
1128        // Exclusive stat for metric
1129        $m = "excl_" . $metric;
1130        print('<tr style="border-bottom: 1px solid black;">');
1131        print("<td>" . str_replace("<br>", " ", $descriptions[$m]) . "</td>");
1132        print_td_num($symbol_info1[$m], $format_cbk[$m]);
1133        print_td_num($symbol_info2[$m], $format_cbk[$m]);
1134        print_td_num($symbol_info2[$m] - $symbol_info1[$m], $format_cbk[$m], true);
1135        print_td_pct($symbol_info2[$m] - $symbol_info1[$m], $symbol_info1[$m], true);
1136        print('</tr>');
1137      }
1138  
1139      print('</table>');
1140    }
1141  
1142    print("<br><h4><center>");
1143    print("Parent/Child $regr_impr report for <b>$rep_symbol</b>");
1144  
1145    $callgraph_href = "$base_path/callgraph.php?"
1146      . http_build_query(xhprof_array_set($url_params, 'func', $rep_symbol));
1147  
1148    print(" <a href='$callgraph_href'>[View Callgraph $diff_text]</a><br>");
1149  
1150    print("</center></h4><br>");
1151  
1152    print('<table border=1 cellpadding=2 cellspacing=1 width="90%" '
1153          .'rules=rows bordercolor="#bdc7d8" align=center>' . "\n");
1154    print('<tr bgcolor="#bdc7d8" align=right>');
1155  
1156    foreach ($pc_stats as $stat) {
1157      $desc = stat_description($stat);
1158      if (array_key_exists($stat, $sortable_columns)) {
1159  
1160        $href = "$base_path/?" .
1161          http_build_query(xhprof_array_set($url_params,
1162                                            'sort', $stat));
1163        $header = xhprof_render_link($desc, $href);
1164      } else {
1165        $header = $desc;
1166      }
1167  
1168      if ($stat == "fn")
1169        print("<th align=left><nobr>$header</th>");
1170      else print("<th " . $vwbar . "><nobr>$header</th>");
1171    }
1172    print("</tr>");
1173  
1174    print("<tr bgcolor='#e0e0ff'><td>");
1175    print("<b><i><center>Current Function</center></i></b>");
1176    print("</td></tr>");
1177  
1178    print("<tr>");
1179    // make this a self-reference to facilitate copy-pasting snippets to e-mails
1180    print("<td><a href=''>$rep_symbol</a>");
1181    print_source_link(array('fn' => $rep_symbol));
1182    print("</td>");
1183  
1184    if ($display_calls) {
1185      // Call Count
1186      print_td_num($symbol_info["ct"], $format_cbk["ct"]);
1187      print_td_pct($symbol_info["ct"], $totals["ct"]);
1188    }
1189  
1190    // Inclusive Metrics for current function
1191    foreach ($metrics as $metric) {
1192      print_td_num($symbol_info[$metric], $format_cbk[$metric], ($sort_col == $metric));
1193      print_td_pct($symbol_info[$metric], $totals[$metric], ($sort_col == $metric));
1194    }
1195    print("</tr>");
1196  
1197    print("<tr bgcolor='#ffffff'>");
1198    print("<td style='text-align:right;color:blue'>"
1199          ."Exclusive Metrics $diff_text for Current Function</td>");
1200  
1201    if ($display_calls) {
1202      // Call Count
1203      print("<td $vbar></td>");
1204      print("<td $vbar></td>");
1205    }
1206  
1207    // Exclusive Metrics for current function
1208    foreach ($metrics as $metric) {
1209      print_td_num($symbol_info["excl_" . $metric], $format_cbk["excl_" . $metric],
1210                   ($sort_col == $metric),
1211                   get_tooltip_attributes("Child", $metric));
1212      print_td_pct($symbol_info["excl_" . $metric], $symbol_info[$metric],
1213                   ($sort_col == $metric),
1214                   get_tooltip_attributes("Child", $metric));
1215    }
1216    print("</tr>");
1217  
1218    // list of callers/parent functions
1219    $results = array();
1220    if ($display_calls) {
1221      $base_ct = $symbol_info["ct"];
1222    } else {
1223      $base_ct = 0;
1224    }
1225    foreach ($metrics as $metric) {
1226      $base_info[$metric] = $symbol_info[$metric];
1227    }
1228    foreach ($run_data as $parent_child => $info) {
1229      list($parent, $child) = xhprof_parse_parent_child($parent_child);
1230      if (($child == $rep_symbol) && ($parent)) {
1231        $info_tmp = $info;
1232        $info_tmp["fn"] = $parent;
1233        $results[] = $info_tmp;
1234      }
1235    }
1236    usort($results, 'sort_cbk');
1237  
1238    if (count($results) > 0) {
1239      print_pc_array($url_params, $results, $base_ct, $base_info, true,
1240                     $run1, $run2);
1241    }
1242  
1243    // list of callees/child functions
1244    $results = array();
1245    $base_ct = 0;
1246    foreach ($run_data as $parent_child => $info) {
1247      list($parent, $child) = xhprof_parse_parent_child($parent_child);
1248      if ($parent == $rep_symbol) {
1249        $info_tmp = $info;
1250        $info_tmp["fn"] = $child;
1251        $results[] = $info_tmp;
1252        if ($display_calls) {
1253          $base_ct += $info["ct"];
1254        }
1255      }
1256    }
1257    usort($results, 'sort_cbk');
1258  
1259    if (count($results)) {
1260      print_pc_array($url_params, $results, $base_ct, $base_info, false,
1261                     $run1, $run2);
1262    }
1263  
1264    print("</table>");
1265  
1266    // These will be used for pop-up tips/help.
1267    // Related javascript code is in: xhprof_report.js
1268    print("\n");
1269    print('<script language="javascript">' . "\n");
1270    print("var func_name = '\"" . $rep_symbol . "\"';\n");
1271    print("var total_child_ct  = " . $base_ct . ";\n");
1272    if ($display_calls) {
1273      print("var func_ct   = " . $symbol_info["ct"] . ";\n");
1274    }
1275    print("var func_metrics = new Array();\n");
1276    print("var metrics_col  = new Array();\n");
1277    print("var metrics_desc  = new Array();\n");
1278    if ($diff_mode) {
1279      print("var diff_mode = true;\n");
1280    } else {
1281      print("var diff_mode = false;\n");
1282    }
1283    $column_index = 3; // First three columns are Func Name, Calls, Calls%
1284    foreach ($metrics as $metric) {
1285      print("func_metrics[\"" . $metric . "\"] = " . round($symbol_info[$metric]) . ";\n");
1286      print("metrics_col[\"". $metric . "\"] = " . $column_index . ";\n");
1287      print("metrics_desc[\"". $metric . "\"] = \"" . $possible_metrics[$metric][2] . "\";\n");
1288  
1289      // each metric has two columns..
1290      $column_index += 2;
1291    }
1292    print('</script>');
1293    print("\n");
1294  
1295  }
1296  
1297  /**
1298   * Generate the profiler report for a single run.
1299   *
1300   * @author Kannan
1301   */
1302  function profiler_single_run_report ($url_params,
1303                                       $xhprof_data,
1304                                       $run_desc,
1305                                       $rep_symbol,
1306                                       $sort,
1307                                       $run) {
1308  
1309    init_metrics($xhprof_data, $rep_symbol, $sort, false);
1310  
1311    profiler_report($url_params, $rep_symbol, $sort, $run, $run_desc,
1312                    $xhprof_data);
1313  }
1314  
1315  
1316  
1317  /**
1318   * Generate the profiler report for diff mode (delta between two runs).
1319   *
1320   * @author Kannan
1321   */
1322  function profiler_diff_report($url_params,
1323                                $xhprof_data1,
1324                                $run1_desc,
1325                                $xhprof_data2,
1326                                $run2_desc,
1327                                $rep_symbol,
1328                                $sort,
1329                                $run1,
1330                                $run2) {
1331  
1332  
1333    // Initialize what metrics we'll display based on data in Run2
1334    init_metrics($xhprof_data2, $rep_symbol, $sort, true);
1335  
1336    profiler_report($url_params,
1337                    $rep_symbol,
1338                    $sort,
1339                    $run1,
1340                    $run1_desc,
1341                    $xhprof_data1,
1342                    $run2,
1343                    $run2_desc,
1344                    $xhprof_data2);
1345  }
1346  
1347  
1348  /**
1349   * Generate a XHProf Display View given the various URL parameters
1350   * as arguments. The first argument is an object that implements
1351   * the iXHProfRuns interface.
1352   *
1353   * @param object  $xhprof_runs_impl  An object that implements
1354   *                                   the iXHProfRuns interface
1355   *.
1356   * @param array   $url_params   Array of non-default URL params.
1357   *
1358   * @param string  $source       Category/type of the run. The source in
1359   *                              combination with the run id uniquely
1360   *                              determines a profiler run.
1361   *
1362   * @param string  $run          run id, or comma separated sequence of
1363   *                              run ids. The latter is used if an aggregate
1364   *                              report of the runs is desired.
1365   *
1366   * @param string  $wts          Comma separate list of integers.
1367   *                              Represents the weighted ratio in
1368   *                              which which a set of runs will be
1369   *                              aggregated. [Used only for aggregate
1370   *                              reports.]
1371   *
1372   * @param string  $symbol       Function symbol. If non-empty then the
1373   *                              parent/child view of this function is
1374   *                              displayed. If empty, a flat-profile view
1375   *                              of the functions is displayed.
1376   *
1377   * @param string  $run1         Base run id (for diff reports)
1378   *
1379   * @param string  $run2         New run id (for diff reports)
1380   *
1381   */
1382  function displayXHProfReport($xhprof_runs_impl, $url_params, $source,
1383                               $run, $wts, $symbol, $sort, $run1, $run2) {
1384  
1385    if ($run) {                              // specific run to display?
1386  
1387      // run may be a single run or a comma separate list of runs
1388      // that'll be aggregated. If "wts" (a comma separated list
1389      // of integral weights is specified), the runs will be
1390      // aggregated in that ratio.
1391      //
1392      $runs_array = explode(",", $run);
1393  
1394      if (count($runs_array) == 1) {
1395        $xhprof_data = $xhprof_runs_impl->get_run($runs_array[0],
1396                                                  $source,
1397                                                  $description);
1398      } else {
1399        if (!empty($wts)) {
1400          $wts_array  = explode(",", $wts);
1401        } else {
1402          $wts_array = null;
1403        }
1404        $data = xhprof_aggregate_runs($xhprof_runs_impl,
1405                                      $runs_array, $wts_array, $source, false);
1406        $xhprof_data = $data['raw'];
1407        $description = $data['description'];
1408      }
1409  
1410  
1411      profiler_single_run_report($url_params,
1412                                 $xhprof_data,
1413                                 $description,
1414                                 $symbol,
1415                                 $sort,
1416                                 $run);
1417  
1418    } else if ($run1 && $run2) {                  // diff report for two runs
1419  
1420      $xhprof_data1 = $xhprof_runs_impl->get_run($run1, $source, $description1);
1421      $xhprof_data2 = $xhprof_runs_impl->get_run($run2, $source, $description2);
1422  
1423      profiler_diff_report($url_params,
1424                           $xhprof_data1,
1425                           $description1,
1426                           $xhprof_data2,
1427                           $description2,
1428                           $symbol,
1429                           $sort,
1430                           $run1,
1431                           $run2);
1432  
1433    } else {
1434      echo "No XHProf runs specified in the URL.";
1435      if (method_exists($xhprof_runs_impl, 'list_runs')) {
1436        $xhprof_runs_impl->list_runs();
1437      }
1438    }
1439  }