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