瀏覽代碼

Update xhprof profiler rules with fork of xhprof

Yannick Warnier 8 年之前
父節點
當前提交
d943b6522b

+ 25 - 0
tests/xhprof/README.md

@@ -0,0 +1,25 @@
+# XHProf / Tideways
+
+Previously the XHProf library was developed by Facebook. Since they moved to
+HHVM, they have dropped support for the library and several projects have
+forked it, between other reasons to provide support for PHP 7.
+
+## Install procedure
+
+To enable the profiler into Chamilo, you will need to do the following:
+- install the tideways-php library from https://tideways.io/profiler/downloads
+- add the following two lines to your Apache VirtualHost or (in a slightly different form) to your php.ini config (don't forget to update the path to your Chamilo root directory):
+```
+    php_value auto_prepend_file /var/www/chamilo/tests/xhprof/header.php
+    php_value auto_append_file /var/www/chamilo/tests/xhprof/footer.php
+```
+- restart your PHP interpreter (Apache or PHP-FPM, in most cases)
+- modify Chamilo's .htaccess file to comment (temporarily) the tests/ directory line, like so
+```
+# Deny access
+#RewriteRule ^(tests|.git) - [F,L,NC]
+```
+
+## Using XHProf
+
+Once you've done all the above, reload any Chamilo page. You should now see a little link at the bottom left of the page (under the footer). Click the link to see the details of the page load as seen by the profiler.

+ 7 - 5
tests/xhprof/footer.php

@@ -5,13 +5,15 @@
 /**
  * Init
  */
-if (extension_loaded('xhprof')) {
+if (extension_loaded('tideways')) {
     $profiler_namespace = 'chamilolms';  // namespace for your application
-    $xhprof_data = xhprof_disable();
-    $xhprof_runs = new XHProfRuns_Default();
-    $run_id = $xhprof_runs->save_run($xhprof_data, $profiler_namespace);
+    $xhprof_data = tideways_disable();
+    //$xhprof_runs = new XHProfRuns_Default();
+    //$run_id = $xhprof_runs->save_run($xhprof_data, $profiler_namespace);
+    $run_id = uniqid();
+    file_put_contents(sys_get_temp_dir() . '/' . $run_id . '.' . $profiler_namespace . '.xhprof', serialize($xhprof_data));
     // url to the XHProf UI libraries (change the host name and path)
     $subDir = substr(__DIR__, strlen(trim($_SERVER['DOCUMENT_ROOT'])));
     $profiler_url = sprintf($subDir.'/xhprof_html/index.php?run=%s&source=%s', $run_id, $profiler_namespace);
-    echo '<a href="'. $profiler_url .'" target="_blank">Profiler output</a>';
+    echo '<a style="z-index:-999; position: absolute;" href="'. $profiler_url .'" target="_blank">Profiler output</a>';
 }

+ 10 - 5
tests/xhprof/header.php

@@ -3,10 +3,15 @@
  * @package chamilo.profiling
  */
 /**
- * Init
+ * Init. Xhprof has been replaced by The Tideways profiler as Xhprof is not
+ * maintained for PHP anymore (Facebook moved to HHVM).
+ * See https://tideways.io/profiler/
+ * To use, you should load header.php and footer.php through an append_file
+ * in your php config also disable the .htaccess line about the tests/
+ * directory.
  */
-if (extension_loaded('xhprof')) {
-    include_once dirname(__FILE__).'/xhprof_lib/utils/xhprof_lib.php';
-    include_once dirname(__FILE__).'/xhprof_lib/utils/xhprof_runs.php';
-    xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY);
+if (extension_loaded('tideways')) {
+    //include_once dirname(__FILE__).'/xhprof_lib/utils/xhprof_lib.php';
+    //include_once dirname(__FILE__).'/xhprof_lib/utils/xhprof_runs.php';
+    tideways_enable(TIDEWAYS_FLAGS_NO_SPANS);
 }

+ 1 - 1
tests/xhprof/xhprof_html/callgraph.php

@@ -33,7 +33,7 @@
 // are at the same level.
 $GLOBALS['XHPROF_LIB_ROOT'] = dirname(__FILE__) . '/../xhprof_lib';
 
-include_once $GLOBALS['XHPROF_LIB_ROOT'].'/display/xhprof.php';
+require_once $GLOBALS['XHPROF_LIB_ROOT'].'/display/xhprof.php';
 
 ini_set('max_execution_time', 100);
 

+ 0 - 1
tests/xhprof/xhprof_html/css/xhprof.css

@@ -79,4 +79,3 @@ ul.xhprof_actions li a:hover {
   background:#3B5998 none repeat scroll 0 0;
   color:#FFFFFF;
 }
-

+ 2 - 2
tests/xhprof/xhprof_html/docs/index.html

@@ -74,7 +74,7 @@ system-wide performance monitoring system in use at Facebook that is
 built on top of XHProf.  XHProfLive continually gathers function-level
 profiler data from production tier by running a sample of page
 requests under XHProf. XHProfLive then aggregates the profile data
-corresponding to individual requests by various dimensions such a
+corresponding to individual requests by various dimensions such as
 time, page type, and can help answer a variety of questions such as:
 What is the function-level profile for a specific page? How expensive
 is function "foo" across all pages, or on a specific page? What
@@ -98,7 +98,7 @@ historical trend for execution time of a page/function? and so on.
 <ul>
 <li><b>Flat profile</b> (<a href="sample-flat-view.jpg" >screenshot</a>)
 
-<p>Provides function-level summary information such number of calls,
+<p>Function-level summary information such as number of calls,
 inclusive/exclusive wall time, memory usage, and CPU time.
 
 <p><li><b>Hierarchical profile (Parent/Child View)</b>

二進制
tests/xhprof/xhprof_html/docs/sample-callgraph-image.jpg


二進制
tests/xhprof/xhprof_html/docs/sample-diff-report-flat-view.jpg


二進制
tests/xhprof/xhprof_html/docs/sample-diff-report-parent-child-view.jpg


二進制
tests/xhprof/xhprof_html/docs/sample-flat-view.jpg


二進制
tests/xhprof/xhprof_html/docs/sample-parent-child-view.jpg


+ 1 - 1
tests/xhprof/xhprof_html/index.php

@@ -35,7 +35,7 @@
 // are at the same level.
 $GLOBALS['XHPROF_LIB_ROOT'] = dirname(__FILE__) . '/../xhprof_lib';
 
-include_once $GLOBALS['XHPROF_LIB_ROOT'].'/display/xhprof.php';
+require_once $GLOBALS['XHPROF_LIB_ROOT'].'/display/xhprof.php';
 
 // param name, its type, and default value
 $params = array('run'        => array(XHPROF_STRING_PARAM, ''),

二進制
tests/xhprof/xhprof_html/jquery/indicator.gif


+ 1 - 1
tests/xhprof/xhprof_html/jquery/jquery-1.2.6.js

@@ -6,7 +6,7 @@
  * Dual licensed under the MIT (MIT-LICENSE.txt)
  * and GPL (GPL-LICENSE.txt) licenses.
  *
- * $Date: 2009/03/17 18:35:18 $
+ * $Date: 2009-03-17 18:35:18 $
  * $Rev: 5685 $
  */
 

+ 1 - 1
tests/xhprof/xhprof_html/jquery/jquery.autocomplete.css

@@ -7,7 +7,7 @@
  *   http://www.opensource.org/licenses/mit-license.php
  *   http://www.gnu.org/licenses/gpl.html
  *
- * Revision: $Id: jquery.autocomplete.css,v 1.1.1.1 2009/03/17 18:35:18 kannan Exp $
+ * Revision: $Id$
  *
  */
 

+ 1 - 1
tests/xhprof/xhprof_html/jquery/jquery.autocomplete.js

@@ -7,7 +7,7 @@
  *   http://www.opensource.org/licenses/mit-license.php
  *   http://www.gnu.org/licenses/gpl.html
  *
- * Revision: $Id: jquery.autocomplete.js,v 1.1.1.1 2009/03/17 18:35:18 kannan Exp $
+ * Revision: $Id: jquery.autocomplete.js,v 1.1.1.1 2009-03-17 18:35:18 kannan Exp $
  *
  */
 

+ 1 - 1
tests/xhprof/xhprof_html/jquery/jquery.tooltip.css

@@ -6,7 +6,7 @@
  *
  * Copyright (c) 2006 - 2008 Jörn Zaefferer
  *
- * $Id: jquery.tooltip.css,v 1.1.1.1 2009/03/17 18:35:18 kannan Exp $
+ * $Id$
  *
  * Dual licensed under the MIT and GPL licenses:
  *   http://www.opensource.org/licenses/mit-license.php

+ 1 - 1
tests/xhprof/xhprof_html/jquery/jquery.tooltip.js

@@ -6,7 +6,7 @@
  *
  * Copyright (c) 2006 - 2008 Jörn Zaefferer
  *
- * $Id: jquery.tooltip.js,v 1.1.1.1 2009/03/17 18:35:18 kannan Exp $
+ * $Id: jquery.tooltip.js,v 1.1.1.1 2009-03-17 18:35:18 kannan Exp $
  * 
  * Dual licensed under the MIT and GPL licenses:
  *   http://www.opensource.org/licenses/mit-license.php

+ 2 - 2
tests/xhprof/xhprof_html/typeahead.php

@@ -25,8 +25,8 @@
 // are at the same level.
 $GLOBALS['XHPROF_LIB_ROOT'] = dirname(__FILE__) . '/../xhprof_lib';
 
-include_once $GLOBALS['XHPROF_LIB_ROOT'].'/display/xhprof.php';
+require_once $GLOBALS['XHPROF_LIB_ROOT'].'/display/xhprof.php';
 
 $xhprof_runs_impl = new XHProfRuns_Default();
 
-include_once $GLOBALS['XHPROF_LIB_ROOT'].'/display/typeahead_common.php';
+require_once $GLOBALS['XHPROF_LIB_ROOT'].'/display/typeahead_common.php';

+ 2 - 3
tests/xhprof/xhprof_lib/display/typeahead_common.php

@@ -25,7 +25,7 @@
  */
 
 
-include_once $GLOBALS['XHPROF_LIB_ROOT'].'/utils/xhprof_lib.php';
+require_once $GLOBALS['XHPROF_LIB_ROOT'].'/utils/xhprof_lib.php';
 
 // param name, its type, and default value
 $params = array('q'          => array(XHPROF_STRING_PARAM, ''),
@@ -69,7 +69,7 @@ if (in_array($q, $functions)) {
   foreach ($old_functions as $f) {
     // exact match case has already been added to the front
     if ($f != $q) {
-      $functions[] =$f;
+      $functions[] = $f;
     }
   }
 }
@@ -77,4 +77,3 @@ if (in_array($q, $functions)) {
 foreach ($functions as $f) {
   echo $f."\n";
 }
-

+ 48 - 108
tests/xhprof/xhprof_lib/display/xhprof.php

@@ -33,16 +33,16 @@ if (!isset($GLOBALS['XHPROF_LIB_ROOT'])) {
   $GLOBALS['XHPROF_LIB_ROOT'] = realpath(dirname(__FILE__) . '/..');
 }
 
-include_once $GLOBALS['XHPROF_LIB_ROOT'].'/utils/xhprof_lib.php';
-include_once $GLOBALS['XHPROF_LIB_ROOT'].'/utils/callgraph_utils.php';
-include_once $GLOBALS['XHPROF_LIB_ROOT'].'/utils/xhprof_runs.php';
+require_once $GLOBALS['XHPROF_LIB_ROOT'].'/utils/xhprof_lib.php';
+require_once $GLOBALS['XHPROF_LIB_ROOT'].'/utils/callgraph_utils.php';
+require_once $GLOBALS['XHPROF_LIB_ROOT'].'/utils/xhprof_runs.php';
 
 
 /**
  * Our coding convention disallows relative paths in hrefs.
  * Get the base URL path from the SCRIPT_NAME.
  */
-$base_path = rtrim(dirname($_SERVER['SCRIPT_NAME']), "/");
+$base_path = rtrim(dirname($_SERVER['SCRIPT_NAME']), '/\\');
 
 
 /**
@@ -58,16 +58,16 @@ $base_path = rtrim(dirname($_SERVER['SCRIPT_NAME']), "/");
 function xhprof_include_js_css($ui_dir_url_path = null) {
 
   if (empty($ui_dir_url_path)) {
-    $ui_dir_url_path = rtrim(dirname($_SERVER['SCRIPT_NAME']), "/");
+    $ui_dir_url_path = rtrim(dirname($_SERVER['SCRIPT_NAME']), '/\\');
   }
 
   // style sheets
   echo "<link href='$ui_dir_url_path/css/xhprof.css' rel='stylesheet' ".
-    " type='text/css'></link>";
+    " type='text/css' />";
   echo "<link href='$ui_dir_url_path/jquery/jquery.tooltip.css' ".
-    " rel='stylesheet' type='text/css'></link>";
+    " rel='stylesheet' type='text/css' />";
   echo "<link href='$ui_dir_url_path/jquery/jquery.autocomplete.css' ".
-    " rel='stylesheet' type='text/css'></link>";
+    " rel='stylesheet' type='text/css' />";
 
   // javascript
   echo "<script src='$ui_dir_url_path/jquery/jquery-1.2.6.js'>".
@@ -106,7 +106,7 @@ function xhprof_count_format($num) {
 }
 
 function xhprof_percent_format($s, $precision = 1) {
-  return sprintf('%.'.$precision.'f%%', 100*$s);
+  return sprintf('%.'.$precision.'f%%', 100 * $s);
 }
 
 /**
@@ -114,7 +114,7 @@ function xhprof_percent_format($s, $precision = 1) {
  * into a HTML list and returns the text.
  */
 function xhprof_render_actions($actions) {
-  $out = array( );
+  $out = array();
 
   if (count($actions)) {
     $out[] = '<ul class="xhprof_actions">';
@@ -380,8 +380,7 @@ $metrics = null;
  *
  * @author Kannan
  */
-function sort_cbk($a, $b)
-{
+function sort_cbk($a, $b) {
   global $sort_col;
   global $diff_mode;
 
@@ -413,82 +412,6 @@ function sort_cbk($a, $b)
   }
 }
 
-/**
- * Initialize the metrics we'll display based on the information
- * in the raw data.
- *
- * @author Kannan
- */
-function init_metrics($xhprof_data, $rep_symbol, $sort, $diff_report = false) {
-  global $stats;
-  global $pc_stats;
-  global $metrics;
-  global $diff_mode;
-  global $sortable_columns;
-  global $sort_col;
-  global $display_calls;
-
-  $diff_mode = $diff_report;
-
-  if (!empty($sort)) {
-    if (array_key_exists($sort, $sortable_columns)) {
-      $sort_col = $sort;
-    } else {
-      print("Invalid Sort Key $sort specified in URL");
-    }
-  }
-
-  // For C++ profiler runs, walltime attribute isn't present.
-  // In that case, use "samples" as the default sort column.
-  if (!isset($xhprof_data["main()"]["wt"])) {
-
-    if ($sort_col == "wt") {
-      $sort_col = "samples";
-    }
-
-    // C++ profiler data doesn't have call counts.
-    // ideally we should check to see if "ct" metric
-    // is present for "main()". But currently "ct"
-    // metric is artificially set to 1. So, relying
-    // on absence of "wt" metric instead.
-    $display_calls = false;
-  } else {
-    $display_calls = true;
-  }
-
-  // parent/child report doesn't support exclusive times yet.
-  // So, change sort hyperlinks to closest fit.
-  if (!empty($rep_symbol)) {
-    $sort_col = str_replace("excl_", "", $sort_col);
-  }
-
-  if ($display_calls) {
-    $stats = array("fn", "ct", "Calls%");
-  } else {
-    $stats = array("fn");
-  }
-
-  $pc_stats = $stats;
-
-  $possible_metrics = xhprof_get_possible_metrics($xhprof_data);
-  foreach ($possible_metrics as $metric => $desc) {
-    if (isset($xhprof_data["main()"][$metric])) {
-      $metrics[] = $metric;
-      // flat (top-level reports): we can compute
-      // exclusive metrics reports as well.
-      $stats[] = $metric;
-      $stats[] = "I" . $desc[0] . "%";
-      $stats[] = "excl_" . $metric;
-      $stats[] = "E" . $desc[0] . "%";
-
-      // parent/child report for a function: we can
-      // only breakdown inclusive times correctly.
-      $pc_stats[] = $metric;
-      $pc_stats[] = "I" . $desc[0] . "%";
-    }
-  }
-}
-
 /**
  * Get the appropriate description for a statistic
  * (depending upon whether we are in diff report mode
@@ -584,7 +507,7 @@ function profiler_report ($url_params,
 
   // set up the action links for operations that can be done on this report
   $links = array();
-  $links []=  xhprof_render_link("View Top Level $diff_text Report",
+  $links [] =  xhprof_render_link("View Top Level $diff_text Report",
                                  $top_link_query_string);
 
   if ($diff_mode) {
@@ -593,9 +516,9 @@ function profiler_report ($url_params,
     $inverted_params['run2'] = $url_params['run1'];
 
     // view the different runs or invert the current diff
-    $links []= $run1_link;
-    $links []= $run2_link;
-    $links []= xhprof_render_link('Invert ' . $diff_text . ' Report',
+    $links [] = $run1_link;
+    $links [] = $run2_link;
+    $links [] = xhprof_render_link('Invert ' . $diff_text . ' Report',
                            "$base_path/?".
                            http_build_query($inverted_params));
   }
@@ -698,7 +621,7 @@ function print_td_num($num, $fmt_func, $bold=false, $attributes=null) {
 
   $class = get_print_class($num, $bold);
 
-  if (!empty($fmt_func)) {
+  if (!empty($fmt_func) && is_numeric($num) ) {
     $num = call_user_func($fmt_func, $num);
   }
 
@@ -755,6 +678,7 @@ function print_function_info($url_params, $info, $sort, $run1, $run2) {
 
   print('<td>');
   print(xhprof_render_link($info["fn"], $href));
+  print_source_link($info);
   print("</td>\n");
 
   if ($display_calls) {
@@ -824,20 +748,19 @@ function print_flat_data($url_params, $title, $flat_data, $sort, $run1, $run2, $
 
     if ($stat == "fn")
       print("<th align=left><nobr>$header</th>");
-    else
-      print("<th " . $vwbar . "><nobr>$header</th>");
+    else print("<th " . $vwbar . "><nobr>$header</th>");
   }
   print("</tr>\n");
 
   if ($limit >= 0) {
     $limit = min($size, $limit);
-    for($i=0; $i < $limit; $i++) {
+    for ($i = 0; $i < $limit; $i++) {
       print_function_info($url_params, $flat_data[$i], $sort, $run1, $run2);
     }
   } else {
     // if $limit is negative, print abs($limit) items starting from the end
     $limit = min($size, abs($limit));
-    for($i=0; $i < $limit; $i++) {
+    for ($i = 0; $i < $limit; $i++) {
       print_function_info($url_params, $flat_data[$size - $i - 1], $sort, $run1, $run2);
     }
   }
@@ -925,7 +848,7 @@ function full_report($url_params, $symbol_tab, $sort, $run1, $run2) {
           .'bgcolor="#bdc7d8" align=center>' . "\n");
     echo "<tr>";
     echo "<th style='text-align:right'>Overall Summary</th>";
-    echo "<th'></th>";
+    echo "<th></th>";
     echo "</tr>";
 
     foreach ($metrics as $metric) {
@@ -1017,8 +940,7 @@ function pc_info($info, $base_ct, $base_info, $parent) {
 
   if ($parent)
     $type = "Parent";
-  else
-    $type = "Child";
+  else $type = "Child";
 
   if ($display_calls) {
     $mouseoverct = get_tooltip_attributes($type, "ct");
@@ -1061,6 +983,7 @@ function print_pc_array($url_params, $results, $base_ct, $base_info, $parent,
     $href = "$base_path/?" .
       http_build_query(xhprof_array_set($url_params,
                                         'symbol', $info["fn"]));
+
     $odd_even = 1 - $odd_even;
 
     if ($odd_even) {
@@ -1070,12 +993,25 @@ function print_pc_array($url_params, $results, $base_ct, $base_info, $parent,
       print('<tr bgcolor="#e5e5e5">');
     }
 
-    print("<td>" . xhprof_render_link($info["fn"], $href) . "</td>");
+    print("<td>" . xhprof_render_link($info["fn"], $href));
+    print_source_link($info);
+    print("</td>");
     pc_info($info, $base_ct, $base_info, $parent);
     print("</tr>");
   }
 }
 
+function print_source_link($info) {
+  if (strncmp($info['fn'], 'run_init', 8) && $info['fn'] !== 'main()') {
+    if (defined('XHPROF_SYMBOL_LOOKUP_URL')) {
+      $link = xhprof_render_link(
+        'source',
+        XHPROF_SYMBOL_LOOKUP_URL . '?symbol='.rawurlencode($info["fn"]));
+      print(' ('.$link.')');
+    }
+  }
+}
+
 
 function print_symbol_summary($symbol_info, $stat, $base) {
 
@@ -1178,10 +1114,10 @@ function symbol_report($url_params,
       $avg_info1 = 'N/A';
       $avg_info2 = 'N/A';
       if ($symbol_info1['ct'] > 0) {
-        $avg_info1 = ($symbol_info1[$m]/$symbol_info1['ct']);
+        $avg_info1 = ($symbol_info1[$m] / $symbol_info1['ct']);
       }
       if ($symbol_info2['ct'] > 0) {
-        $avg_info2 = ($symbol_info2[$m]/$symbol_info2['ct']);
+        $avg_info2 = ($symbol_info2[$m] / $symbol_info2['ct']);
       }
       print_td_num($avg_info1, $format_cbk[$m]);
       print_td_num($avg_info2, $format_cbk[$m]);
@@ -1231,8 +1167,7 @@ function symbol_report($url_params,
 
     if ($stat == "fn")
       print("<th align=left><nobr>$header</th>");
-    else
-      print("<th " . $vwbar . "><nobr>$header</th>");
+    else print("<th " . $vwbar . "><nobr>$header</th>");
   }
   print("</tr>");
 
@@ -1242,7 +1177,9 @@ function symbol_report($url_params,
 
   print("<tr>");
   // make this a self-reference to facilitate copy-pasting snippets to e-mails
-  print("<td><a href=''>$rep_symbol</a></td>");
+  print("<td><a href=''>$rep_symbol</a>");
+  print_source_link(array('fn' => $rep_symbol));
+  print("</td>");
 
   if ($display_calls) {
     // Call Count
@@ -1333,7 +1270,7 @@ function symbol_report($url_params,
   print("var func_name = '\"" . $rep_symbol . "\"';\n");
   print("var total_child_ct  = " . $base_ct . ";\n");
   if ($display_calls) {
-    print("var func_ct   = " . $symbol_info["ct"] . ";\n" );
+    print("var func_ct   = " . $symbol_info["ct"] . ";\n");
   }
   print("var func_metrics = new Array();\n");
   print("var metrics_col  = new Array();\n");
@@ -1345,7 +1282,7 @@ function symbol_report($url_params,
   }
   $column_index = 3; // First three columns are Func Name, Calls, Calls%
   foreach ($metrics as $metric) {
-    print("func_metrics[\"" . $metric . "\"] = " . round($symbol_info[$metric]) . ";\n" );
+    print("func_metrics[\"" . $metric . "\"] = " . round($symbol_info[$metric]) . ";\n");
     print("metrics_col[\"". $metric . "\"] = " . $column_index . ";\n");
     print("metrics_desc[\"". $metric . "\"] = \"" . $possible_metrics[$metric][2] . "\";\n");
 
@@ -1495,5 +1432,8 @@ function displayXHProfReport($xhprof_runs_impl, $url_params, $source,
 
   } else {
     echo "No XHProf runs specified in the URL.";
+    if (method_exists($xhprof_runs_impl, 'list_runs')) {
+      $xhprof_runs_impl->list_runs();
+    }
   }
 }

+ 44 - 34
tests/xhprof/xhprof_lib/utils/callgraph_utils.php

@@ -25,6 +25,7 @@ $xhprof_legal_image_types = array(
     "jpg" => 1,
     "gif" => 1,
     "png" => 1,
+    "svg" => 1, // support scalable vector graphic
     "ps"  => 1,
     );
 
@@ -68,6 +69,9 @@ function xhprof_generate_mime_header($type, $length) {
     case 'png':
       $mime = 'image/png';
       break;
+    case 'svg':
+      $mime = 'image/svg+xml'; // content type for scalable vector graphic
+      break;
     case 'ps':
       $mime = 'application/postscript';
     default:
@@ -88,7 +92,7 @@ function xhprof_generate_mime_header($type, $length) {
  * @param dot_script, string, the script for DOT to generate the image.
  * @param type, one of the supported image types, see
  * $xhprof_legal_image_types.
- * @return binary content of the generated image on success. empty string on
+ * @returns, binary content of the generated image on success. empty string on
  *           failure.
  *
  * @author cjiang
@@ -99,25 +103,31 @@ function xhprof_generate_image_by_dot($dot_script, $type) {
        0 => array("pipe", "r"),
        // stdout is a pipe that the child will write to
        1 => array("pipe", "w"),
-       // stderr is a file to write to
-       2 => array("file", "/dev/null", "a")
+       // stderr is a pipe that the child will write to
+       2 => array("pipe", "w")
        );
 
   $cmd = " dot -T".$type;
 
-  $process = proc_open($cmd, $descriptorspec, $pipes, "/tmp", array());
-
+  $process = proc_open( $cmd, $descriptorspec, $pipes, sys_get_temp_dir(), array( 'PATH' => getenv( 'PATH' ) ) );
   if (is_resource($process)) {
     fwrite($pipes[0], $dot_script);
     fclose($pipes[0]);
 
-   $output = stream_get_contents($pipes[1]);
-    fclose($pipes[1]);
+    $output = stream_get_contents($pipes[1]);
 
+    $err = stream_get_contents($pipes[2]);
+    if (!empty($err)) {
+      print "failed to execute cmd: \"$cmd\". stderr: `$err'\n";
+      exit;
+    }
+
+    fclose($pipes[2]);
+    fclose($pipes[1]);
     proc_close($process);
     return $output;
   }
-  print "failed to shell execute cmd=\"$cmd\"\n";
+  print "failed to execute cmd \"$cmd\"";
   exit();
 }
 
@@ -150,7 +160,7 @@ function xhprof_get_children_table($raw_data) {
  * @param func, string, the focus function.
  * @param critical_path, bool, whether or not to display critical path with
  *                             bold lines.
- * @return string, the DOT script to generate image.
+ * @returns, string, the DOT script to generate image.
  *
  * @author cjiang
  */
@@ -276,7 +286,7 @@ function xhprof_generate_dot_script($raw_data, $threshold, $source, $page,
       }
     }
 
-    $fontsize =", fontsize="
+    $fontsize = ", fontsize="
                .(int)($max_fontsize / (($sizing_factor - 1) / 10 + 1));
 
     $width = ", width=".sprintf("%.1f", $max_width / $sizing_factor);
@@ -284,50 +294,50 @@ function xhprof_generate_dot_script($raw_data, $threshold, $source, $page,
 
     if ($symbol == "main()") {
       $shape = "octagon";
-      $name ="Total: ".($totals["wt"]/1000.0)." ms\\n";
+      $name = "Total: ".($totals["wt"] / 1000.0)." ms\\n";
       $name .= addslashes(isset($page) ? $page : $symbol);
     } else {
       $shape = "box";
-      $name = addslashes($symbol)."\\nInc: ". sprintf("%.3f",$info["wt"]/1000) .
-              " ms (" . sprintf("%.1f%%", 100 * $info["wt"]/$totals["wt"]).")";
+      $name = addslashes($symbol)."\\nInc: ". sprintf("%.3f",$info["wt"] / 1000) .
+              " ms (" . sprintf("%.1f%%", 100 * $info["wt"] / $totals["wt"]).")";
     }
     if ($left === null) {
       $label = ", label=\"".$name."\\nExcl: "
-               .(sprintf("%.3f",$info["excl_wt"]/1000.0))." ms ("
-               .sprintf("%.1f%%", 100 * $info["excl_wt"]/$totals["wt"])
+               .(sprintf("%.3f",$info["excl_wt"] / 1000.0))." ms ("
+               .sprintf("%.1f%%", 100 * $info["excl_wt"] / $totals["wt"])
                . ")\\n".$info["ct"]." total calls\"";
     } else {
       if (isset($left[$symbol]) && isset($right[$symbol])) {
          $label = ", label=\"".addslashes($symbol).
-                  "\\nInc: ".(sprintf("%.3f",$left[$symbol]["wt"]/1000.0))
+                  "\\nInc: ".(sprintf("%.3f",$left[$symbol]["wt"] / 1000.0))
                   ." ms - "
-                  .(sprintf("%.3f",$right[$symbol]["wt"]/1000.0))." ms = "
-                  .(sprintf("%.3f",$info["wt"]/1000.0))." ms".
+                  .(sprintf("%.3f",$right[$symbol]["wt"] / 1000.0))." ms = "
+                  .(sprintf("%.3f",$info["wt"] / 1000.0))." ms".
                   "\\nExcl: "
-                  .(sprintf("%.3f",$left[$symbol]["excl_wt"]/1000.0))
-                  ." ms - ".(sprintf("%.3f",$right[$symbol]["excl_wt"]/1000.0))
-                   ." ms = ".(sprintf("%.3f",$info["excl_wt"]/1000.0))." ms".
+                  .(sprintf("%.3f",$left[$symbol]["excl_wt"] / 1000.0))
+                  ." ms - ".(sprintf("%.3f",$right[$symbol]["excl_wt"] / 1000.0))
+                   ." ms = ".(sprintf("%.3f",$info["excl_wt"] / 1000.0))." ms".
                   "\\nCalls: ".(sprintf("%.3f",$left[$symbol]["ct"]))." - "
                    .(sprintf("%.3f",$right[$symbol]["ct"]))." = "
                    .(sprintf("%.3f",$info["ct"]))."\"";
       } else if (isset($left[$symbol])) {
         $label = ", label=\"".addslashes($symbol).
-                  "\\nInc: ".(sprintf("%.3f",$left[$symbol]["wt"]/1000.0))
-                   ." ms - 0 ms = ".(sprintf("%.3f",$info["wt"]/1000.0))
+                  "\\nInc: ".(sprintf("%.3f",$left[$symbol]["wt"] / 1000.0))
+                   ." ms - 0 ms = ".(sprintf("%.3f",$info["wt"] / 1000.0))
                    ." ms"."\\nExcl: "
-                   .(sprintf("%.3f",$left[$symbol]["excl_wt"]/1000.0))
+                   .(sprintf("%.3f",$left[$symbol]["excl_wt"] / 1000.0))
                    ." ms - 0 ms = "
-                   .(sprintf("%.3f",$info["excl_wt"]/1000.0))." ms".
+                   .(sprintf("%.3f",$info["excl_wt"] / 1000.0))." ms".
                   "\\nCalls: ".(sprintf("%.3f",$left[$symbol]["ct"]))." - 0 = "
                   .(sprintf("%.3f",$info["ct"]))."\"";
       } else {
         $label = ", label=\"".addslashes($symbol).
                   "\\nInc: 0 ms - "
-                  .(sprintf("%.3f",$right[$symbol]["wt"]/1000.0))
-                  ." ms = ".(sprintf("%.3f",$info["wt"]/1000.0))." ms".
+                  .(sprintf("%.3f",$right[$symbol]["wt"] / 1000.0))
+                  ." ms = ".(sprintf("%.3f",$info["wt"] / 1000.0))." ms".
                   "\\nExcl: 0 ms - "
-                  .(sprintf("%.3f",$right[$symbol]["excl_wt"]/1000.0))
-                  ." ms = ".(sprintf("%.3f",$info["excl_wt"]/1000.0))." ms".
+                  .(sprintf("%.3f",$right[$symbol]["excl_wt"] / 1000.0))
+                  ." ms = ".(sprintf("%.3f",$info["excl_wt"] / 1000.0))." ms".
                   "\\nCalls: 0 - ".(sprintf("%.3f",$right[$symbol]["ct"]))
                   ." = ".(sprintf("%.3f",$info["ct"]))."\"";
       }
@@ -343,7 +353,7 @@ function xhprof_generate_dot_script($raw_data, $threshold, $source, $page,
 
     if (isset($sym_table[$parent]) && isset($sym_table[$child]) &&
         (empty($func) ||
-         (!empty($func) && ($parent == $func || $child == $func)) )) {
+         (!empty($func) && ($parent == $func || $child == $func)))) {
 
       $label = $info["ct"] == 1 ? $info["ct"]." call" : $info["ct"]." calls";
 
@@ -358,17 +368,17 @@ function xhprof_generate_dot_script($raw_data, $threshold, $source, $page,
                 ($sym_table[$parent]["wt"] - $sym_table["$parent"]["excl_wt"]))
         : "0.0%";
 
-      $linewidth= 1;
+      $linewidth = 1;
       $arrow_size = 1;
 
       if ($critical_path &&
           isset($path_edges[xhprof_build_parent_child_key($parent, $child)])) {
-        $linewidth = 10; $arrow_size=2;
+        $linewidth = 10; $arrow_size = 2;
       }
 
       $result .= "N" . $sym_table[$parent]["id"] . " -> N"
                  . $sym_table[$child]["id"];
-      $result .= "[arrowsize=$arrow_size, style=\"setlinewidth($linewidth)\","
+      $result .= "[arrowsize=$arrow_size, color=grey, style=\"setlinewidth($linewidth)\","
                  ." label=\""
                  .$label."\", headlabel=\"".$headlabel
                  ."\", taillabel=\"".$taillabel."\" ]";
@@ -418,7 +428,7 @@ function  xhprof_render_diff_image($xhprof_runs_impl, $run1, $run2,
  *                   threshold will be filtered out and won't apprear in the
  *                   generated image.
  * @param func, string, the focus function.
- * @return string, the DOT script to generate image.
+ * @returns, string, the DOT script to generate image.
  *
  * @author cjiang
  */

+ 86 - 7
tests/xhprof/xhprof_lib/utils/xhprof_lib.php

@@ -31,8 +31,8 @@ function xhprof_error($message) {
  */
 function xhprof_get_possible_metrics() {
  static $possible_metrics =
-   array("wt" => array("Wall", "microsecs", "walltime" ),
-         "ut" => array("User", "microsecs", "user cpu time" ),
+   array("wt" => array("Wall", "microsecs", "walltime"),
+         "ut" => array("User", "microsecs", "user cpu time"),
          "st" => array("Sys", "microsecs", "system cpu time"),
          "cpu" => array("Cpu", "microsecs", "cpu time"),
          "mu" => array("MUse", "bytes", "memory usage"),
@@ -41,6 +41,82 @@ function xhprof_get_possible_metrics() {
  return $possible_metrics;
 }
 
+/**
+ * Initialize the metrics we'll display based on the information
+ * in the raw data.
+ *
+ * @author Kannan
+ */
+function init_metrics($xhprof_data, $rep_symbol, $sort, $diff_report = false) {
+  global $stats;
+  global $pc_stats;
+  global $metrics;
+  global $diff_mode;
+  global $sortable_columns;
+  global $sort_col;
+  global $display_calls;
+
+  $diff_mode = $diff_report;
+
+  if (!empty($sort)) {
+    if (array_key_exists($sort, $sortable_columns)) {
+      $sort_col = $sort;
+    } else {
+      print("Invalid Sort Key $sort specified in URL");
+    }
+  }
+
+  // For C++ profiler runs, walltime attribute isn't present.
+  // In that case, use "samples" as the default sort column.
+  if (!isset($xhprof_data["main()"]["wt"])) {
+
+    if ($sort_col == "wt") {
+      $sort_col = "samples";
+    }
+
+    // C++ profiler data doesn't have call counts.
+    // ideally we should check to see if "ct" metric
+    // is present for "main()". But currently "ct"
+    // metric is artificially set to 1. So, relying
+    // on absence of "wt" metric instead.
+    $display_calls = false;
+  } else {
+    $display_calls = true;
+  }
+
+  // parent/child report doesn't support exclusive times yet.
+  // So, change sort hyperlinks to closest fit.
+  if (!empty($rep_symbol)) {
+    $sort_col = str_replace("excl_", "", $sort_col);
+  }
+
+  if ($display_calls) {
+    $stats = array("fn", "ct", "Calls%");
+  } else {
+    $stats = array("fn");
+  }
+
+  $pc_stats = $stats;
+
+  $possible_metrics = xhprof_get_possible_metrics();
+  foreach ($possible_metrics as $metric => $desc) {
+    if (isset($xhprof_data["main()"][$metric])) {
+      $metrics[] = $metric;
+      // flat (top-level reports): we can compute
+      // exclusive metrics reports as well.
+      $stats[] = $metric;
+      $stats[] = "I" . $desc[0] . "%";
+      $stats[] = "excl_" . $metric;
+      $stats[] = "E" . $desc[0] . "%";
+
+      // parent/child report for a function: we can
+      // only breakdown inclusive times correctly.
+      $pc_stats[] = $metric;
+      $pc_stats[] = "I" . $desc[0] . "%";
+    }
+  }
+}
+
 /*
  * Get the list of metrics present in $xhprof_data as an array.
  *
@@ -259,7 +335,7 @@ function xhprof_aggregate_runs($xhprof_runs_impl, $runs,
   }
 
   $bad_runs = array();
-  foreach($runs as $idx => $run_id) {
+  foreach ($runs as $idx => $run_id) {
 
     $raw_data = $xhprof_runs_impl->get_run($run_id, $source, $description);
 
@@ -294,7 +370,7 @@ function xhprof_aggregate_runs($xhprof_runs_impl, $runs,
       // it shows up above the new entry in reports sorted by
       // inclusive metrics or call counts.
       if ($page) {
-        foreach($raw_data["main()"] as $metric => $val) {
+        foreach ($raw_data["main()"] as $metric => $val) {
           $fake_edge[$metric] = $val;
           $new_main[$metric]  = $val + 0.00001;
         }
@@ -316,7 +392,7 @@ function xhprof_aggregate_runs($xhprof_runs_impl, $runs,
         // if this is an old edge originating from main(), it now
         // needs to be from '__script::$page'
         if (substr($parent_child, 0, 9) == "main()==>") {
-          $child =substr($parent_child, 9);
+          $child = substr($parent_child, 9);
           // ignore the newly added edge from main()
           if (substr($child, 0, 10) != "__script::") {
             $parent_child = xhprof_build_parent_child_key("__script::$page",
@@ -381,7 +457,7 @@ function xhprof_compute_flat_info($raw_data, &$overall_totals) {
 
   $metrics = xhprof_get_metrics($raw_data);
 
-  $overall_totals = array( "ct" => 0,
+  $overall_totals = array("ct" => 0,
                            "wt" => 0,
                            "ut" => 0,
                            "st" => 0,
@@ -829,6 +905,10 @@ function xhprof_param_init($params) {
       exit();
     }
 
+    if ($k === 'run') {
+      $p = implode(',', array_filter(explode(',', $p), 'ctype_xdigit'));
+    }
+
     // create a global variable using the parameter name.
     $GLOBALS[$k] = $p;
   }
@@ -863,4 +943,3 @@ function xhprof_get_matching_functions($q, $xhprof_data) {
 
   return ($res);
 }
-

+ 20 - 3
tests/xhprof/xhprof_lib/utils/xhprof_runs.php

@@ -68,6 +68,7 @@ interface iXHProfRuns {
 class XHProfRuns_Default implements iXHProfRuns {
 
   private $dir = '';
+  private $suffix = 'xhprof';
 
   private function gen_run_id($type) {
     return uniqid();
@@ -75,7 +76,7 @@ class XHProfRuns_Default implements iXHProfRuns {
 
   private function file_name($run_id, $type) {
 
-    $file = "$run_id.$type";
+    $file = "$run_id.$type." . $this->suffix;
 
     if (!empty($this->dir)) {
       $file = $this->dir . "/" . $file;
@@ -94,8 +95,7 @@ class XHProfRuns_Default implements iXHProfRuns {
       $dir = ini_get("xhprof.output_dir");
       if (empty($dir)) {
 
-        // some default that at least works on unix...
-        $dir = "/tmp";
+        $dir = sys_get_temp_dir();
 
         xhprof_error("Warning: Must specify directory location for XHProf runs. ".
                      "Trying {$dir} as default. You can either pass the " .
@@ -144,4 +144,21 @@ class XHProfRuns_Default implements iXHProfRuns {
     // echo "Saved run in {$file_name}.\nRun id = {$run_id}.\n";
     return $run_id;
   }
+
+  function list_runs() {
+    if (is_dir($this->dir)) {
+        echo "<hr/>Existing runs:\n<ul>\n";
+        $files = glob("{$this->dir}/*.{$this->suffix}");
+        usort($files, create_function('$a,$b', 'return filemtime($b) - filemtime($a);'));
+        foreach ($files as $file) {
+            list($run,$source) = explode('.', basename($file));
+            echo '<li><a href="' . htmlentities($_SERVER['SCRIPT_NAME'])
+                . '?run=' . htmlentities($run) . '&source='
+                . htmlentities($source) . '">'
+                . htmlentities(basename($file)) . "</a><small> "
+                . date("Y-m-d H:i:s", filemtime($file)) . "</small></li>\n";
+        }
+        echo "</ul>\n";
+    }
+  }
 }