���ѧۧݧ�ӧ�� �ާ֧ߧ֧էا֧� - ���֧էѧܧ�ڧ��ӧѧ�� - /home3/cpr76684/public_html/xhprof_lib.tar
���ѧ٧ѧ�
display/typeahead_common.php 0000644 00000004726 15152024132 0012242 0 ustar 00 <?php // Copyright (c) 2009 Facebook // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // /** * AJAX endpoint for XHProf function name typeahead is implemented * as a thin wrapper around this file. The wrapper must set up * the global $xhprof_runs_impl to correspond to an object that * implements the iXHProfRuns interface. * * @author(s) Kannan Muthukkaruppan * Changhao Jiang */ require_once $GLOBALS['XHPROF_LIB_ROOT'].'/utils/xhprof_lib.php'; // param name, its type, and default value $params = array('q' => array(XHPROF_STRING_PARAM, ''), 'run' => array(XHPROF_STRING_PARAM, ''), 'run1' => array(XHPROF_STRING_PARAM, ''), 'run2' => array(XHPROF_STRING_PARAM, ''), 'source' => array(XHPROF_STRING_PARAM, 'xhprof'), ); // pull values of these params, and create named globals for each param xhprof_param_init($params); if (!empty($run)) { // single run mode $raw_data = $xhprof_runs_impl->get_run($run, $source, $desc_unused); $functions = xhprof_get_matching_functions($q, $raw_data); } else if (!empty($run1) && !empty($run2)) { // diff mode $raw_data = $xhprof_runs_impl->get_run($run1, $source, $desc_unused); $functions1 = xhprof_get_matching_functions($q, $raw_data); $raw_data = $xhprof_runs_impl->get_run($run2, $source, $desc_unused); $functions2 = xhprof_get_matching_functions($q, $raw_data); $functions = array_unique(array_merge($functions1, $functions2)); asort($functions); } else { xhprof_error("no valid runs specified to typeahead endpoint"); $functions = array(); } // If exact match is present move it to the front if (in_array($q, $functions)) { $old_functions = $functions; $functions = array($q); foreach ($old_functions as $f) { // exact match case has already been added to the front if ($f != $q) { $functions[] = $f; } } } foreach ($functions as $f) { echo $f."\n"; } display/xhprof.php 0000644 00000127432 15152024132 0010234 0 ustar 00 <?php // Copyright (c) 2009 Facebook // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // // XHProf: A Hierarchical Profiler for PHP // // XHProf has two components: // // * This module is the UI/reporting component, used // for viewing results of XHProf runs from a browser. // // * Data collection component: This is implemented // as a PHP extension (XHProf). // // @author Kannan Muthukkaruppan // if (!isset($GLOBALS['XHPROF_LIB_ROOT'])) { // by default, the parent directory is XHPROF lib root $GLOBALS['XHPROF_LIB_ROOT'] = realpath(dirname(__FILE__) . '/..'); } 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_url = htmlentities($_SERVER['SCRIPT_NAME']); /** * Generate references to required stylesheets & javascript. * * If the calling script (such as index.php) resides in * a different location that than 'xhprof_html' directory the * caller must provide the URL path to 'xhprof_html' directory * so that the correct location of the style sheets/javascript * can be specified in the generated HTML. * */ function xhprof_include_js_css($ui_dir_url_path = null) { global $base_path; if (empty($ui_dir_url_path)) { $ui_dir_url_path = $base_path; } // style sheets echo "<link href='$ui_dir_url_path/css/xhprof.css' rel='stylesheet' ". " type='text/css' />"; echo "<link href='$ui_dir_url_path/jquery/jquery.tooltip.css' ". " rel='stylesheet' type='text/css' />"; echo "<link href='$ui_dir_url_path/jquery/jquery.autocomplete.css' ". " rel='stylesheet' type='text/css' />"; // javascript echo "<script src='$ui_dir_url_path/jquery/jquery-1.2.6.js'>". "</script>"; echo "<script src='$ui_dir_url_path/jquery/jquery.tooltip.js'>". "</script>"; echo "<script src='$ui_dir_url_path/jquery/jquery.autocomplete.js'>" ."</script>"; echo "<script src='$ui_dir_url_path/js/xhprof_report.js'></script>"; } /* * Formats call counts for XHProf reports. * * Description: * Call counts in single-run reports are integer values. * However, call counts for aggregated reports can be * fractional. This function will print integer values * without decimal point, but with commas etc. * * 4000 ==> 4,000 * * It'll round fractional values to decimal precision of 3 * 4000.1212 ==> 4,000.121 * 4000.0001 ==> 4,000 * */ function xhprof_count_format($num) { $num = round($num, 3); if (round($num) == $num) { return number_format($num); } else { return number_format($num, 3); } } function xhprof_percent_format($s, $precision = 1) { return sprintf('%.'.$precision.'f%%', 100 * $s); } /** * Implodes the text for a bunch of actions (such as links, forms, * into a HTML list and returns the text. */ function xhprof_render_actions($actions) { $out = array(); if (count($actions)) { $out[] = '<ul class="xhprof_actions">'; foreach ($actions as $action) { $out[] = '<li>'.$action.'</li>'; } $out[] = '</ul>'; } return implode('', $out); } /** * @param html-str $content the text/image/innerhtml/whatever for the link * @param raw-str $href * @param raw-str $class * @param raw-str $id * @param raw-str $title * @param raw-str $target * @param raw-str $onclick * @param raw-str $style * @param raw-str $access * @param raw-str $onmouseover * @param raw-str $onmouseout * @param raw-str $onmousedown * @param raw-str $dir * @param raw-str $rel */ function xhprof_render_link($content, $href, $class='', $id='', $title='', $target='', $onclick='', $style='', $access='', $onmouseover='', $onmouseout='', $onmousedown='') { if (!$content) { return ''; } if ($href) { $link = '<a href="' . ($href) . '"'; } else { $link = '<span'; } if ($class) { $link .= ' class="' . ($class) . '"'; } if ($id) { $link .= ' id="' . ($id) . '"'; } if ($title) { $link .= ' title="' . ($title) . '"'; } if ($target) { $link .= ' target="' . ($target) . '"'; } if ($onclick && $href) { $link .= ' onclick="' . ($onclick) . '"'; } if ($style && $href) { $link .= ' style="' . ($style) . '"'; } if ($access && $href) { $link .= ' accesskey="' . ($access) . '"'; } if ($onmouseover) { $link .= ' onmouseover="' . ($onmouseover) . '"'; } if ($onmouseout) { $link .= ' onmouseout="' . ($onmouseout) . '"'; } if ($onmousedown) { $link .= ' onmousedown="' . ($onmousedown) . '"'; } $link .= '>'; $link .= $content; if ($href) { $link .= '</a>'; } else { $link .= '</span>'; } return $link; } // default column to sort on -- wall time $sort_col = "wt"; // default is "single run" report $diff_mode = false; // call count data present? $display_calls = true; // The following column headers are sortable $sortable_columns = array("fn" => 1, "ct" => 1, "wt" => 1, "excl_wt" => 1, "ut" => 1, "excl_ut" => 1, "st" => 1, "excl_st" => 1, "mu" => 1, "excl_mu" => 1, "pmu" => 1, "excl_pmu" => 1, "cpu" => 1, "excl_cpu" => 1, "samples" => 1, "excl_samples" => 1 ); // Textual descriptions for column headers in "single run" mode $descriptions = array( "fn" => "Function Name", "ct" => "Calls", "Calls%" => "Calls%", "wt" => "Incl. Wall Time<br>(microsec)", "IWall%" => "IWall%", "excl_wt" => "Excl. Wall Time<br>(microsec)", "EWall%" => "EWall%", "ut" => "Incl. User<br>(microsecs)", "IUser%" => "IUser%", "excl_ut" => "Excl. User<br>(microsec)", "EUser%" => "EUser%", "st" => "Incl. Sys <br>(microsec)", "ISys%" => "ISys%", "excl_st" => "Excl. Sys <br>(microsec)", "ESys%" => "ESys%", "cpu" => "Incl. CPU<br>(microsecs)", "ICpu%" => "ICpu%", "excl_cpu" => "Excl. CPU<br>(microsec)", "ECpu%" => "ECPU%", "mu" => "Incl.<br>MemUse<br>(bytes)", "IMUse%" => "IMemUse%", "excl_mu" => "Excl.<br>MemUse<br>(bytes)", "EMUse%" => "EMemUse%", "pmu" => "Incl.<br> PeakMemUse<br>(bytes)", "IPMUse%" => "IPeakMemUse%", "excl_pmu" => "Excl.<br>PeakMemUse<br>(bytes)", "EPMUse%" => "EPeakMemUse%", "samples" => "Incl. Samples", "ISamples%" => "ISamples%", "excl_samples" => "Excl. Samples", "ESamples%" => "ESamples%", ); // Formatting Callback Functions... $format_cbk = array( "fn" => "", "ct" => "xhprof_count_format", "Calls%" => "xhprof_percent_format", "wt" => "number_format", "IWall%" => "xhprof_percent_format", "excl_wt" => "number_format", "EWall%" => "xhprof_percent_format", "ut" => "number_format", "IUser%" => "xhprof_percent_format", "excl_ut" => "number_format", "EUser%" => "xhprof_percent_format", "st" => "number_format", "ISys%" => "xhprof_percent_format", "excl_st" => "number_format", "ESys%" => "xhprof_percent_format", "cpu" => "number_format", "ICpu%" => "xhprof_percent_format", "excl_cpu" => "number_format", "ECpu%" => "xhprof_percent_format", "mu" => "number_format", "IMUse%" => "xhprof_percent_format", "excl_mu" => "number_format", "EMUse%" => "xhprof_percent_format", "pmu" => "number_format", "IPMUse%" => "xhprof_percent_format", "excl_pmu" => "number_format", "EPMUse%" => "xhprof_percent_format", "samples" => "number_format", "ISamples%" => "xhprof_percent_format", "excl_samples" => "number_format", "ESamples%" => "xhprof_percent_format", ); // Textual descriptions for column headers in "diff" mode $diff_descriptions = array( "fn" => "Function Name", "ct" => "Calls Diff", "Calls%" => "Calls<br>Diff%", "wt" => "Incl. Wall<br>Diff<br>(microsec)", "IWall%" => "IWall<br> Diff%", "excl_wt" => "Excl. Wall<br>Diff<br>(microsec)", "EWall%" => "EWall<br>Diff%", "ut" => "Incl. User Diff<br>(microsec)", "IUser%" => "IUser<br>Diff%", "excl_ut" => "Excl. User<br>Diff<br>(microsec)", "EUser%" => "EUser<br>Diff%", "cpu" => "Incl. CPU Diff<br>(microsec)", "ICpu%" => "ICpu<br>Diff%", "excl_cpu" => "Excl. CPU<br>Diff<br>(microsec)", "ECpu%" => "ECpu<br>Diff%", "st" => "Incl. Sys Diff<br>(microsec)", "ISys%" => "ISys<br>Diff%", "excl_st" => "Excl. Sys Diff<br>(microsec)", "ESys%" => "ESys<br>Diff%", "mu" => "Incl.<br>MemUse<br>Diff<br>(bytes)", "IMUse%" => "IMemUse<br>Diff%", "excl_mu" => "Excl.<br>MemUse<br>Diff<br>(bytes)", "EMUse%" => "EMemUse<br>Diff%", "pmu" => "Incl.<br> PeakMemUse<br>Diff<br>(bytes)", "IPMUse%" => "IPeakMemUse<br>Diff%", "excl_pmu" => "Excl.<br>PeakMemUse<br>Diff<br>(bytes)", "EPMUse%" => "EPeakMemUse<br>Diff%", "samples" => "Incl. Samples Diff", "ISamples%" => "ISamples Diff%", "excl_samples" => "Excl. Samples Diff", "ESamples%" => "ESamples Diff%", ); // columns that'll be displayed in a top-level report $stats = array(); // columns that'll be displayed in a function's parent/child report $pc_stats = array(); // Various total counts $totals = 0; $totals_1 = 0; $totals_2 = 0; /* * The subset of $possible_metrics that is present in the raw profile data. */ $metrics = null; /** * Callback comparison operator (passed to usort() for sorting array of * tuples) that compares array elements based on the sort column * specified in $sort_col (global parameter). * * @author Kannan */ function sort_cbk($a, $b) { global $sort_col; global $diff_mode; if ($sort_col == "fn") { // case insensitive ascending sort for function names $left = strtoupper($a["fn"]); $right = strtoupper($b["fn"]); if ($left == $right) return 0; return ($left < $right) ? -1 : 1; } else { // descending sort for all others $left = $a[$sort_col]; $right = $b[$sort_col]; // if diff mode, sort by absolute value of regression/improvement if ($diff_mode) { $left = abs($left); $right = abs($right); } if ($left == $right) return 0; return ($left > $right) ? -1 : 1; } } /** * Get the appropriate description for a statistic * (depending upon whether we are in diff report mode * or single run report mode). * * @author Kannan */ function stat_description($stat) { global $descriptions; global $diff_descriptions; global $diff_mode; if ($diff_mode) { return $diff_descriptions[$stat]; } else { return $descriptions[$stat]; } } /** * Analyze raw data & generate the profiler report * (common for both single run mode and diff mode). * * @author: Kannan */ function profiler_report ($url_params, $rep_symbol, $sort, $run1, $run1_desc, $run1_data, $run2 = 0, $run2_desc = "", $run2_data = array()) { global $totals; global $totals_1; global $totals_2; global $stats; global $pc_stats; global $diff_mode; global $base_url; // if we are reporting on a specific function, we can trim down // the report(s) to just stuff that is relevant to this function. // That way compute_flat_info()/compute_diff() etc. do not have // to needlessly work hard on churning irrelevant data. if (!empty($rep_symbol)) { $run1_data = xhprof_trim_run($run1_data, array($rep_symbol)); if ($diff_mode) { $run2_data = xhprof_trim_run($run2_data, array($rep_symbol)); } } if ($diff_mode) { $run_delta = xhprof_compute_diff($run1_data, $run2_data); $symbol_tab = xhprof_compute_flat_info($run_delta, $totals); $symbol_tab1 = xhprof_compute_flat_info($run1_data, $totals_1); $symbol_tab2 = xhprof_compute_flat_info($run2_data, $totals_2); } else { $symbol_tab = xhprof_compute_flat_info($run1_data, $totals); } $run1_txt = sprintf("<b>Run #%s:</b> %s", $run1, $run1_desc); $base_url_params = xhprof_array_unset(xhprof_array_unset($url_params, 'symbol'), 'all'); $top_link_query_string = "$base_url?" . http_build_query($base_url_params); if ($diff_mode) { $diff_text = "Diff"; $base_url_params = xhprof_array_unset($base_url_params, 'run1'); $base_url_params = xhprof_array_unset($base_url_params, 'run2'); $run1_link = xhprof_render_link('View Run #' . $run1, "$base_url?" . http_build_query(xhprof_array_set($base_url_params, 'run', $run1))); $run2_txt = sprintf("<b>Run #%s:</b> %s", $run2, $run2_desc); $run2_link = xhprof_render_link('View Run #' . $run2, "$base_url?" . http_build_query(xhprof_array_set($base_url_params, 'run', $run2))); } else { $diff_text = "Run"; } // 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", $top_link_query_string); if ($diff_mode) { $inverted_params = $url_params; $inverted_params['run1'] = $url_params['run2']; $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', "$base_url?". http_build_query($inverted_params)); } // lookup function typeahead form $links [] = '<input class="function_typeahead" ' . ' type="input" size="40" maxlength="100" />'; echo xhprof_render_actions($links); echo '<dl class=phprof_report_info>' . ' <dt>' . $diff_text . ' Report</dt>' . ' <dd>' . ($diff_mode ? $run1_txt . '<br><b>vs.</b><br>' . $run2_txt : $run1_txt) . ' </dd>' . ' <dt>Tip</dt>' . ' <dd>Click a function name below to drill down.</dd>' . '</dl>' . '<div style="clear: both; margin: 3em 0em;"></div>'; // data tables if (!empty($rep_symbol)) { if (!isset($symbol_tab[$rep_symbol])) { echo "<hr>Symbol <b>$rep_symbol</b> not found in XHProf run</b><hr>"; return; } /* single function report with parent/child information */ if ($diff_mode) { $info1 = isset($symbol_tab1[$rep_symbol]) ? $symbol_tab1[$rep_symbol] : null; $info2 = isset($symbol_tab2[$rep_symbol]) ? $symbol_tab2[$rep_symbol] : null; symbol_report($url_params, $run_delta, $symbol_tab[$rep_symbol], $sort, $rep_symbol, $run1, $info1, $run2, $info2); } else { symbol_report($url_params, $run1_data, $symbol_tab[$rep_symbol], $sort, $rep_symbol, $run1); } } else { /* flat top-level report of all functions */ full_report($url_params, $symbol_tab, $sort, $run1, $run2); } } /** * Computes percentage for a pair of values, and returns it * in string format. */ function pct($a, $b) { if ($b == 0) { return "N/A"; } else { $res = (round(($a * 1000 / $b)) / 10); return $res; } } /** * Given a number, returns the td class to use for display. * * For instance, negative numbers in diff reports comparing two runs (run1 & run2) * represent improvement from run1 to run2. We use green to display those deltas, * and red for regression deltas. */ function get_print_class($num, $bold) { global $vbar; global $vbbar; global $vrbar; global $vgbar; global $diff_mode; if ($bold) { if ($diff_mode) { if ($num <= 0) { $class = $vgbar; // green (improvement) } else { $class = $vrbar; // red (regression) } } else { $class = $vbbar; // blue } } else { $class = $vbar; // default (black) } return $class; } /** * Prints a <td> element with a numeric value. */ function print_td_num($num, $fmt_func, $bold=false, $attributes=null) { $class = get_print_class($num, $bold); if (!empty($fmt_func) && is_numeric($num) ) { $num = call_user_func($fmt_func, $num); } print("<td $attributes $class>$num</td>\n"); } /** * Prints a <td> element with a pecentage. */ function print_td_pct($numer, $denom, $bold=false, $attributes=null) { global $vbar; global $vbbar; global $diff_mode; $class = get_print_class($numer, $bold); if ($denom == 0) { $pct = "N/A%"; } else { $pct = xhprof_percent_format($numer / abs($denom)); } print("<td $attributes $class>$pct</td>\n"); } /** * Print "flat" data corresponding to one function. * * @author Kannan */ function print_function_info($url_params, $info, $sort, $run1, $run2) { static $odd_even = 0; global $totals; global $sort_col; global $metrics; global $format_cbk; global $display_calls; global $base_url; // Toggle $odd_or_even $odd_even = 1 - $odd_even; if ($odd_even) { print("<tr>"); } else { print('<tr bgcolor="#e5e5e5">'); } $href = "$base_url?" . http_build_query(xhprof_array_set($url_params, 'symbol', $info["fn"])); print('<td>'); print(xhprof_render_link($info["fn"], $href)); print_source_link($info); print("</td>\n"); if ($display_calls) { // Call Count.. print_td_num($info["ct"], $format_cbk["ct"], ($sort_col == "ct")); print_td_pct($info["ct"], $totals["ct"], ($sort_col == "ct")); } // Other metrics.. foreach ($metrics as $metric) { // Inclusive metric print_td_num($info[$metric], $format_cbk[$metric], ($sort_col == $metric)); print_td_pct($info[$metric], $totals[$metric], ($sort_col == $metric)); // Exclusive Metric print_td_num($info["excl_" . $metric], $format_cbk["excl_" . $metric], ($sort_col == "excl_" . $metric)); print_td_pct($info["excl_" . $metric], $totals[$metric], ($sort_col == "excl_" . $metric)); } print("</tr>\n"); } /** * Print non-hierarchical (flat-view) of profiler data. * * @author Kannan */ function print_flat_data($url_params, $title, $flat_data, $sort, $run1, $run2, $limit) { global $stats; global $sortable_columns; global $vwbar; global $base_url; $size = count($flat_data); if (!$limit) { // no limit $limit = $size; $display_link = ""; } else { $display_link = xhprof_render_link(" [ <b class=bubble>display all </b>]", "$base_url?" . http_build_query(xhprof_array_set($url_params, 'all', 1))); } print("<h3 align=center>$title $display_link</h3><br>"); print('<table border=1 cellpadding=2 cellspacing=1 width="90%" ' .'rules=rows bordercolor="#bdc7d8" align=center>'); print('<tr bgcolor="#bdc7d8" align=right>'); foreach ($stats as $stat) { $desc = stat_description($stat); if (array_key_exists($stat, $sortable_columns)) { $href = "$base_url?" . http_build_query(xhprof_array_set($url_params, 'sort', $stat)); $header = xhprof_render_link($desc, $href); } else { $header = $desc; } if ($stat == "fn") print("<th align=left><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++) { 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++) { print_function_info($url_params, $flat_data[$size - $i - 1], $sort, $run1, $run2); } } print("</table>"); // let's print the display all link at the bottom as well... if ($display_link) { echo '<div style="text-align: left; padding: 2em">' . $display_link . '</div>'; } } /** * Generates a tabular report for all functions. This is the top-level report. * * @author Kannan */ function full_report($url_params, $symbol_tab, $sort, $run1, $run2) { global $vwbar; global $vbar; global $totals; global $totals_1; global $totals_2; global $metrics; global $diff_mode; global $descriptions; global $sort_col; global $format_cbk; global $display_calls; global $base_path; global $base_url; $possible_metrics = xhprof_get_possible_metrics(); if ($diff_mode) { $base_url_params = xhprof_array_unset(xhprof_array_unset($url_params, 'run1'), 'run2'); $href1 = "$base_url?" . http_build_query(xhprof_array_set($base_url_params, 'run', $run1)); $href2 = "$base_url?" . http_build_query(xhprof_array_set($base_url_params, 'run', $run2)); print("<h3><center>Overall Diff Summary</center></h3>"); print('<table border=1 cellpadding=2 cellspacing=1 width="30%" ' .'rules=rows bordercolor="#bdc7d8" align=center>' . "\n"); print('<tr bgcolor="#bdc7d8" align=right>'); print("<th></th>"); print("<th $vwbar>" . xhprof_render_link("Run #$run1", $href1) . "</th>"); print("<th $vwbar>" . xhprof_render_link("Run #$run2", $href2) . "</th>"); print("<th $vwbar>Diff</th>"); print("<th $vwbar>Diff%</th>"); print('</tr>'); if ($display_calls) { print('<tr>'); print("<td>Number of Function Calls</td>"); print_td_num($totals_1["ct"], $format_cbk["ct"]); print_td_num($totals_2["ct"], $format_cbk["ct"]); print_td_num($totals_2["ct"] - $totals_1["ct"], $format_cbk["ct"], true); print_td_pct($totals_2["ct"] - $totals_1["ct"], $totals_1["ct"], true); print('</tr>'); } foreach ($metrics as $metric) { $m = $metric; print('<tr>'); print("<td>" . str_replace("<br>", " ", $descriptions[$m]) . "</td>"); print_td_num($totals_1[$m], $format_cbk[$m]); print_td_num($totals_2[$m], $format_cbk[$m]); print_td_num($totals_2[$m] - $totals_1[$m], $format_cbk[$m], true); print_td_pct($totals_2[$m] - $totals_1[$m], $totals_1[$m], true); print('<tr>'); } print('</table>'); $callgraph_report_title = '[View Regressions/Improvements using Callgraph Diff]'; } else { print("<p><center>\n"); print('<table cellpadding=2 cellspacing=1 width="30%" ' .'bgcolor="#bdc7d8" align=center>' . "\n"); echo "<tr>"; echo "<th style='text-align:right'>Overall Summary</th>"; echo "<th></th>"; echo "</tr>"; foreach ($metrics as $metric) { echo "<tr>"; echo "<td style='text-align:right; font-weight:bold'>Total " . str_replace("<br>", " ", stat_description($metric)) . ":</td>"; echo "<td>" . number_format($totals[$metric]) . " " . $possible_metrics[$metric][1] . "</td>"; echo "</tr>"; } if ($display_calls) { echo "<tr>"; echo "<td style='text-align:right; font-weight:bold'>Number of Function Calls:</td>"; echo "<td>" . number_format($totals['ct']) . "</td>"; echo "</tr>"; } echo "</table>"; print("</center></p>\n"); $callgraph_report_title = '[View Full Callgraph]'; } print("<center><br><h3>" . xhprof_render_link($callgraph_report_title, "$base_path/callgraph.php" . "?" . http_build_query($url_params)) . "</h3></center>"); $flat_data = array(); foreach ($symbol_tab as $symbol => $info) { $tmp = $info; $tmp["fn"] = $symbol; $flat_data[] = $tmp; } usort($flat_data, 'sort_cbk'); print("<br>"); if (!empty($url_params['all'])) { $all = true; $limit = 0; // display all rows } else { $all = false; $limit = 100; // display only limited number of rows } $desc = str_replace("<br>", " ", $descriptions[$sort_col]); if ($diff_mode) { if ($all) { $title = "Total Diff Report: ' .'Sorted by absolute value of regression/improvement in $desc"; } else { $title = "Top 100 <i style='color:red'>Regressions</i>/" . "<i style='color:green'>Improvements</i>: " . "Sorted by $desc Diff"; } } else { if ($all) { $title = "Sorted by $desc"; } else { $title = "Displaying top $limit functions: Sorted by $desc"; } } print_flat_data($url_params, $title, $flat_data, $sort, $run1, $run2, $limit); } /** * Return attribute names and values to be used by javascript tooltip. */ function get_tooltip_attributes($type, $metric) { return "type='$type' metric='$metric'"; } /** * Print info for a parent or child function in the * parent & children report. * * @author Kannan */ function pc_info($info, $base_ct, $base_info, $parent) { global $sort_col; global $metrics; global $format_cbk; global $display_calls; if ($parent) $type = "Parent"; else $type = "Child"; if ($display_calls) { $mouseoverct = get_tooltip_attributes($type, "ct"); /* call count */ print_td_num($info["ct"], $format_cbk["ct"], ($sort_col == "ct"), $mouseoverct); print_td_pct($info["ct"], $base_ct, ($sort_col == "ct"), $mouseoverct); } /* Inclusive metric values */ foreach ($metrics as $metric) { print_td_num($info[$metric], $format_cbk[$metric], ($sort_col == $metric), get_tooltip_attributes($type, $metric)); print_td_pct($info[$metric], $base_info[$metric], ($sort_col == $metric), get_tooltip_attributes($type, $metric)); } } function print_pc_array($url_params, $results, $base_ct, $base_info, $parent, $run1, $run2) { global $base_url; // Construct section title if ($parent) { $title = 'Parent function'; } else { $title = 'Child function'; } if (count($results) > 1) { $title .= 's'; } print("<tr bgcolor='#e0e0ff'><td>"); print("<b><i><center>" . $title . "</center></i></b>"); print("</td></tr>"); $odd_even = 0; foreach ($results as $info) { $href = "$base_url?" . http_build_query(xhprof_array_set($url_params, 'symbol', $info["fn"])); $odd_even = 1 - $odd_even; if ($odd_even) { print('<tr>'); } else { print('<tr bgcolor="#e5e5e5">'); } 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) { $val = $symbol_info[$stat]; $desc = str_replace("<br>", " ", stat_description($stat)); print("$desc: </td>"); print(number_format($val)); print(" (" . pct($val, $base) . "% of overall)"); if (substr($stat, 0, 4) == "excl") { $func_base = $symbol_info[str_replace("excl_", "", $stat)]; print(" (" . pct($val, $func_base) . "% of this function)"); } print("<br>"); } /** * Generates a report for a single function/symbol. * * @author Kannan */ function symbol_report($url_params, $run_data, $symbol_info, $sort, $rep_symbol, $run1, $symbol_info1 = null, $run2 = 0, $symbol_info2 = null) { global $vwbar; global $vbar; global $totals; global $pc_stats; global $sortable_columns; global $metrics; global $diff_mode; global $descriptions; global $format_cbk; global $sort_col; global $display_calls; global $base_path; global $base_url; $possible_metrics = xhprof_get_possible_metrics(); if ($diff_mode) { $diff_text = "<b>Diff</b>"; $regr_impr = "<i style='color:red'>Regression</i>/<i style='color:green'>Improvement</i>"; } else { $diff_text = ""; $regr_impr = ""; } if ($diff_mode) { $base_url_params = xhprof_array_unset(xhprof_array_unset($url_params, 'run1'), 'run2'); $href1 = "$base_url?" . http_build_query(xhprof_array_set($base_url_params, 'run', $run1)); $href2 = "$base_url?" . http_build_query(xhprof_array_set($base_url_params, 'run', $run2)); print("<h3 align=center>$regr_impr summary for $rep_symbol<br><br></h3>"); print('<table border=1 cellpadding=2 cellspacing=1 width="30%" ' .'rules=rows bordercolor="#bdc7d8" align=center>' . "\n"); print('<tr bgcolor="#bdc7d8" align=right>'); print("<th align=left>$rep_symbol</th>"); print("<th $vwbar><a href=" . $href1 . ">Run #$run1</a></th>"); print("<th $vwbar><a href=" . $href2 . ">Run #$run2</a></th>"); print("<th $vwbar>Diff</th>"); print("<th $vwbar>Diff%</th>"); print('</tr>'); print('<tr>'); if ($display_calls) { print("<td>Number of Function Calls</td>"); print_td_num($symbol_info1["ct"], $format_cbk["ct"]); print_td_num($symbol_info2["ct"], $format_cbk["ct"]); print_td_num($symbol_info2["ct"] - $symbol_info1["ct"], $format_cbk["ct"], true); print_td_pct($symbol_info2["ct"] - $symbol_info1["ct"], $symbol_info1["ct"], true); print('</tr>'); } foreach ($metrics as $metric) { $m = $metric; // Inclusive stat for metric print('<tr>'); print("<td>" . str_replace("<br>", " ", $descriptions[$m]) . "</td>"); print_td_num($symbol_info1[$m], $format_cbk[$m]); print_td_num($symbol_info2[$m], $format_cbk[$m]); print_td_num($symbol_info2[$m] - $symbol_info1[$m], $format_cbk[$m], true); print_td_pct($symbol_info2[$m] - $symbol_info1[$m], $symbol_info1[$m], true); print('</tr>'); // AVG (per call) Inclusive stat for metric print('<tr>'); print("<td>" . str_replace("<br>", " ", $descriptions[$m]) . " per call </td>"); $avg_info1 = 'N/A'; $avg_info2 = 'N/A'; if ($symbol_info1['ct'] > 0) { $avg_info1 = ($symbol_info1[$m] / $symbol_info1['ct']); } if ($symbol_info2['ct'] > 0) { $avg_info2 = ($symbol_info2[$m] / $symbol_info2['ct']); } print_td_num($avg_info1, $format_cbk[$m]); print_td_num($avg_info2, $format_cbk[$m]); print_td_num($avg_info2 - $avg_info1, $format_cbk[$m], true); print_td_pct($avg_info2 - $avg_info1, $avg_info1, true); print('</tr>'); // Exclusive stat for metric $m = "excl_" . $metric; print('<tr style="border-bottom: 1px solid black;">'); print("<td>" . str_replace("<br>", " ", $descriptions[$m]) . "</td>"); print_td_num($symbol_info1[$m], $format_cbk[$m]); print_td_num($symbol_info2[$m], $format_cbk[$m]); print_td_num($symbol_info2[$m] - $symbol_info1[$m], $format_cbk[$m], true); print_td_pct($symbol_info2[$m] - $symbol_info1[$m], $symbol_info1[$m], true); print('</tr>'); } print('</table>'); } print("<br><h4><center>"); print("Parent/Child $regr_impr report for <b>$rep_symbol</b>"); $callgraph_href = "$base_path/callgraph.php?" . http_build_query(xhprof_array_set($url_params, 'func', $rep_symbol)); print(" <a href='$callgraph_href'>[View Callgraph $diff_text]</a><br>"); print("</center></h4><br>"); print('<table border=1 cellpadding=2 cellspacing=1 width="90%" ' .'rules=rows bordercolor="#bdc7d8" align=center>' . "\n"); print('<tr bgcolor="#bdc7d8" align=right>'); foreach ($pc_stats as $stat) { $desc = stat_description($stat); if (array_key_exists($stat, $sortable_columns)) { $href = "$base_url?" . http_build_query(xhprof_array_set($url_params, 'sort', $stat)); $header = xhprof_render_link($desc, $href); } else { $header = $desc; } if ($stat == "fn") print("<th align=left><nobr>$header</th>"); else print("<th " . $vwbar . "><nobr>$header</th>"); } print("</tr>"); print("<tr bgcolor='#e0e0ff'><td>"); print("<b><i><center>Current Function</center></i></b>"); print("</td></tr>"); print("<tr>"); // make this a self-reference to facilitate copy-pasting snippets to e-mails print("<td><a href=''>$rep_symbol</a>"); print_source_link(array('fn' => $rep_symbol)); print("</td>"); if ($display_calls) { // Call Count print_td_num($symbol_info["ct"], $format_cbk["ct"]); print_td_pct($symbol_info["ct"], $totals["ct"]); } // Inclusive Metrics for current function foreach ($metrics as $metric) { print_td_num($symbol_info[$metric], $format_cbk[$metric], ($sort_col == $metric)); print_td_pct($symbol_info[$metric], $totals[$metric], ($sort_col == $metric)); } print("</tr>"); print("<tr bgcolor='#ffffff'>"); print("<td style='text-align:right;color:blue'>" ."Exclusive Metrics $diff_text for Current Function</td>"); if ($display_calls) { // Call Count print("<td $vbar></td>"); print("<td $vbar></td>"); } // Exclusive Metrics for current function foreach ($metrics as $metric) { print_td_num($symbol_info["excl_" . $metric], $format_cbk["excl_" . $metric], ($sort_col == $metric), get_tooltip_attributes("Child", $metric)); print_td_pct($symbol_info["excl_" . $metric], $symbol_info[$metric], ($sort_col == $metric), get_tooltip_attributes("Child", $metric)); } print("</tr>"); // list of callers/parent functions $results = array(); if ($display_calls) { $base_ct = $symbol_info["ct"]; } else { $base_ct = 0; } foreach ($metrics as $metric) { $base_info[$metric] = $symbol_info[$metric]; } foreach ($run_data as $parent_child => $info) { list($parent, $child) = xhprof_parse_parent_child($parent_child); if (($child == $rep_symbol) && ($parent)) { $info_tmp = $info; $info_tmp["fn"] = $parent; $results[] = $info_tmp; } } usort($results, 'sort_cbk'); if (count($results) > 0) { print_pc_array($url_params, $results, $base_ct, $base_info, true, $run1, $run2); } // list of callees/child functions $results = array(); $base_ct = 0; foreach ($run_data as $parent_child => $info) { list($parent, $child) = xhprof_parse_parent_child($parent_child); if ($parent == $rep_symbol) { $info_tmp = $info; $info_tmp["fn"] = $child; $results[] = $info_tmp; if ($display_calls) { $base_ct += $info["ct"]; } } } usort($results, 'sort_cbk'); if (count($results)) { print_pc_array($url_params, $results, $base_ct, $base_info, false, $run1, $run2); } print("</table>"); // These will be used for pop-up tips/help. // Related javascript code is in: xhprof_report.js print("\n"); print('<script language="javascript">' . "\n"); 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_metrics = new Array();\n"); print("var metrics_col = new Array();\n"); print("var metrics_desc = new Array();\n"); if ($diff_mode) { print("var diff_mode = true;\n"); } else { print("var diff_mode = false;\n"); } $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("metrics_col[\"". $metric . "\"] = " . $column_index . ";\n"); print("metrics_desc[\"". $metric . "\"] = \"" . $possible_metrics[$metric][2] . "\";\n"); // each metric has two columns.. $column_index += 2; } print('</script>'); print("\n"); } /** * Generate the profiler report for a single run. * * @author Kannan */ function profiler_single_run_report ($url_params, $xhprof_data, $run_desc, $rep_symbol, $sort, $run) { init_metrics($xhprof_data, $rep_symbol, $sort, false); profiler_report($url_params, $rep_symbol, $sort, $run, $run_desc, $xhprof_data); } /** * Generate the profiler report for diff mode (delta between two runs). * * @author Kannan */ function profiler_diff_report($url_params, $xhprof_data1, $run1_desc, $xhprof_data2, $run2_desc, $rep_symbol, $sort, $run1, $run2) { // Initialize what metrics we'll display based on data in Run2 init_metrics($xhprof_data2, $rep_symbol, $sort, true); profiler_report($url_params, $rep_symbol, $sort, $run1, $run1_desc, $xhprof_data1, $run2, $run2_desc, $xhprof_data2); } /** * Generate a XHProf Display View given the various URL parameters * as arguments. The first argument is an object that implements * the iXHProfRuns interface. * * @param object $xhprof_runs_impl An object that implements * the iXHProfRuns interface *. * @param array $url_params Array of non-default URL params. * * @param string $source Category/type of the run. The source in * combination with the run id uniquely * determines a profiler run. * * @param string $run run id, or comma separated sequence of * run ids. The latter is used if an aggregate * report of the runs is desired. * * @param string $wts Comma separate list of integers. * Represents the weighted ratio in * which which a set of runs will be * aggregated. [Used only for aggregate * reports.] * * @param string $symbol Function symbol. If non-empty then the * parent/child view of this function is * displayed. If empty, a flat-profile view * of the functions is displayed. * * @param string $run1 Base run id (for diff reports) * * @param string $run2 New run id (for diff reports) * */ function displayXHProfReport($xhprof_runs_impl, $url_params, $source, $run, $wts, $symbol, $sort, $run1, $run2) { if ($run) { // specific run to display? // run may be a single run or a comma separate list of runs // that'll be aggregated. If "wts" (a comma separated list // of integral weights is specified), the runs will be // aggregated in that ratio. // $runs_array = explode(",", $run); if (count($runs_array) == 1) { $xhprof_data = $xhprof_runs_impl->get_run($runs_array[0], $source, $description); } else { if (!empty($wts)) { $wts_array = explode(",", $wts); } else { $wts_array = null; } $data = xhprof_aggregate_runs($xhprof_runs_impl, $runs_array, $wts_array, $source, false); $xhprof_data = $data['raw']; $description = $data['description']; } profiler_single_run_report($url_params, $xhprof_data, $description, $symbol, $sort, $run); } else if ($run1 && $run2) { // diff report for two runs $xhprof_data1 = $xhprof_runs_impl->get_run($run1, $source, $description1); $xhprof_data2 = $xhprof_runs_impl->get_run($run2, $source, $description2); profiler_diff_report($url_params, $xhprof_data1, $description1, $xhprof_data2, $description2, $symbol, $sort, $run1, $run2); } else { echo "No XHProf runs specified in the URL."; if (method_exists($xhprof_runs_impl, 'list_runs')) { $xhprof_runs_impl->list_runs(); } } } utils/xhprof_runs.php 0000644 00000011370 15152024132 0010767 0 ustar 00 <?php // // Copyright (c) 2009 Facebook // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // // This file defines the interface iXHProfRuns and also provides a default // implementation of the interface (class XHProfRuns). // /** * iXHProfRuns interface for getting/saving a XHProf run. * * Clients can either use the default implementation, * namely XHProfRuns_Default, of this interface or define * their own implementation. * * @author Kannan */ interface iXHProfRuns { /** * Returns XHProf data given a run id ($run) of a given * type ($type). * * Also, a brief description of the run is returned via the * $run_desc out parameter. */ public function get_run($run_id, $type, &$run_desc); /** * Save XHProf data for a profiler run of specified type * ($type). * * The caller may optionally pass in run_id (which they * promise to be unique). If a run_id is not passed in, * the implementation of this method must generated a * unique run id for this saved XHProf run. * * Returns the run id for the saved XHProf run. * */ public function save_run($xhprof_data, $type, $run_id = null); } /** * XHProfRuns_Default is the default implementation of the * iXHProfRuns interface for saving/fetching XHProf runs. * * It stores/retrieves runs to/from a filesystem directory * specified by the "xhprof.output_dir" ini parameter. * * @author Kannan */ class XHProfRuns_Default implements iXHProfRuns { private $dir = ''; private $suffix = 'xhprof'; private function gen_run_id($type) { return uniqid(); } private function file_name($run_id, $type) { $file = "$run_id.$type." . $this->suffix; if (!empty($this->dir)) { $file = $this->dir . "/" . $file; } return $file; } public function __construct($dir = null) { // if user hasn't passed a directory location, // we use the xhprof.output_dir ini setting // if specified, else we default to the directory // in which the error_log file resides. if (empty($dir) && !($dir = getenv('XHPROF_OUTPUT_DIR'))) { $dir = ini_get("xhprof.output_dir"); if (empty($dir)) { $dir = sys_get_temp_dir(); xhprof_error("Warning: Must specify directory location for XHProf runs. ". "Trying {$dir} as default. You can either pass the " . "directory location as an argument to the constructor ". "for XHProfRuns_Default() or set xhprof.output_dir ". "ini param, or set XHPROF_OUTPUT_DIR environment variable."); } } $this->dir = $dir; } public function get_run($run_id, $type, &$run_desc) { $file_name = $this->file_name($run_id, $type); if (!file_exists($file_name)) { xhprof_error("Could not find file $file_name"); $run_desc = "Invalid Run Id = $run_id"; return null; } $contents = file_get_contents($file_name); $run_desc = "XHProf Run (Namespace=$type)"; return unserialize($contents); } public function save_run($xhprof_data, $type, $run_id = null) { // Use PHP serialize function to store the XHProf's // raw profiler data. $xhprof_data = serialize($xhprof_data); if ($run_id === null) { $run_id = $this->gen_run_id($type); } $file_name = $this->file_name($run_id, $type); $file = fopen($file_name, 'w'); if ($file) { fwrite($file, $xhprof_data); fclose($file); } else { xhprof_error("Could not open $file_name\n"); } // 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, 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"; } } } utils/xhprof_lib.php 0000644 00000064174 15152024132 0010560 0 ustar 00 <?php // Copyright (c) 2009 Facebook // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // // This file contains various XHProf library (utility) functions. // Do not add any display specific code here. // if (!function_exists('xhprof_error')) { function xhprof_error($message) { error_log($message); } } /* * The list of possible metrics collected as part of XHProf that * require inclusive/exclusive handling while reporting. * * @author Kannan */ function xhprof_get_possible_metrics() { static $possible_metrics = 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"), "pmu" => array("PMUse", "bytes", "peak memory usage"), "samples" => array("Samples", "samples", "cpu time")); 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. * * @author Kannan */ function xhprof_get_metrics($xhprof_data) { // get list of valid metrics $possible_metrics = xhprof_get_possible_metrics(); // return those that are present in the raw data. // We'll just look at the root of the subtree for this. $metrics = array(); foreach ($possible_metrics as $metric => $desc) { if (isset($xhprof_data["main()"][$metric])) { $metrics[] = $metric; } } return $metrics; } /** * Takes a parent/child function name encoded as * "a==>b" and returns array("a", "b"). * * @author Kannan */ function xhprof_parse_parent_child($parent_child) { $ret = explode("==>", $parent_child); // Return if both parent and child are set if (isset($ret[1])) { return $ret; } return array(null, $ret[0]); } /** * Given parent & child function name, composes the key * in the format present in the raw data. * * @author Kannan */ function xhprof_build_parent_child_key($parent, $child) { if ($parent) { return $parent . "==>" . $child; } else { return $child; } } /** * Checks if XHProf raw data appears to be valid and not corrupted. * * @param int $run_id Run id of run to be pruned. * [Used only for reporting errors.] * @param array $raw_data XHProf raw data to be pruned * & validated. * * @return bool true on success, false on failure * * @author Kannan */ function xhprof_valid_run($run_id, $raw_data) { $main_info = $raw_data["main()"]; if (empty($main_info)) { xhprof_error("XHProf: main() missing in raw data for Run ID: $run_id"); return false; } // raw data should contain either wall time or samples information... if (isset($main_info["wt"])) { $metric = "wt"; } else if (isset($main_info["samples"])) { $metric = "samples"; } else { xhprof_error("XHProf: Wall Time information missing from Run ID: $run_id"); return false; } foreach ($raw_data as $info) { $val = $info[$metric]; // basic sanity checks... if ($val < 0) { xhprof_error("XHProf: $metric should not be negative: Run ID $run_id" . serialize($info)); return false; } if ($val > (86400000000)) { xhprof_error("XHProf: $metric > 1 day found in Run ID: $run_id " . serialize($info)); return false; } } return true; } /** * Return a trimmed version of the XHProf raw data. Note that the raw * data contains one entry for each unique parent/child function * combination.The trimmed version of raw data will only contain * entries where either the parent or child function is in the list * of $functions_to_keep. * * Note: Function main() is also always kept so that overall totals * can still be obtained from the trimmed version. * * @param array XHProf raw data * @param array array of function names * * @return array Trimmed XHProf Report * * @author Kannan */ function xhprof_trim_run($raw_data, $functions_to_keep) { // convert list of functions to a hash with function as the key $function_map = array_fill_keys($functions_to_keep, 1); // always keep main() as well so that overall totals can still // be computed if need be. $function_map['main()'] = 1; $new_raw_data = array(); foreach ($raw_data as $parent_child => $info) { list($parent, $child) = xhprof_parse_parent_child($parent_child); if (isset($function_map[$parent]) || isset($function_map[$child])) { $new_raw_data[$parent_child] = $info; } } return $new_raw_data; } /** * Takes raw XHProf data that was aggregated over "$num_runs" number * of runs averages/nomalizes the data. Essentially the various metrics * collected are divided by $num_runs. * * @author Kannan */ function xhprof_normalize_metrics($raw_data, $num_runs) { if (empty($raw_data) || ($num_runs == 0)) { return $raw_data; } $raw_data_total = array(); if (isset($raw_data["==>main()"]) && isset($raw_data["main()"])) { xhprof_error("XHProf Error: both ==>main() and main() set in raw data..."); } foreach ($raw_data as $parent_child => $info) { foreach ($info as $metric => $value) { $raw_data_total[$parent_child][$metric] = ($value / $num_runs); } } return $raw_data_total; } /** * Get raw data corresponding to specified array of runs * aggregated by certain weightage. * * Suppose you have run:5 corresponding to page1.php, * run:6 corresponding to page2.php, * and run:7 corresponding to page3.php * * and you want to accumulate these runs in a 2:4:1 ratio. You * can do so by calling: * * xhprof_aggregate_runs(array(5, 6, 7), array(2, 4, 1)); * * The above will return raw data for the runs aggregated * in 2:4:1 ratio. * * @param object $xhprof_runs_impl An object that implements * the iXHProfRuns interface * @param array $runs run ids of the XHProf runs.. * @param array $wts integral (ideally) weights for $runs * @param string $source source to fetch raw data for run from * @param bool $use_script_name If true, a fake edge from main() to * to __script::<scriptname> is introduced * in the raw data so that after aggregations * the script name is still preserved. * * @return array Return aggregated raw data * * @author Kannan */ function xhprof_aggregate_runs($xhprof_runs_impl, $runs, $wts, $source="phprof", $use_script_name=false) { $raw_data_total = null; $raw_data = null; $metrics = array(); $run_count = count($runs); $wts_count = count($wts); if (($run_count == 0) || (($wts_count > 0) && ($run_count != $wts_count))) { return array('description' => 'Invalid input..', 'raw' => null); } $bad_runs = array(); foreach ($runs as $idx => $run_id) { $raw_data = $xhprof_runs_impl->get_run($run_id, $source, $description); // use the first run to derive what metrics to aggregate on. if ($idx == 0) { foreach ($raw_data["main()"] as $metric => $val) { if ($metric != "pmu") { // for now, just to keep data size small, skip "peak" memory usage // data while aggregating. // The "regular" memory usage data will still be tracked. if (isset($val)) { $metrics[] = $metric; } } } } if (!xhprof_valid_run($run_id, $raw_data)) { $bad_runs[] = $run_id; continue; } if ($use_script_name) { $page = $description; // create a fake function '__script::$page', and have and edge from // main() to '__script::$page'. We will also need edges to transfer // all edges originating from main() to now originate from // '__script::$page' to all function called from main(). // // We also weight main() ever so slightly higher so that // 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) { $fake_edge[$metric] = $val; $new_main[$metric] = $val + 0.00001; } $raw_data["main()"] = $new_main; $raw_data[xhprof_build_parent_child_key("main()", "__script::$page")] = $fake_edge; } else { $use_script_name = false; } } // if no weights specified, use 1 as the default weightage.. $wt = ($wts_count == 0) ? 1 : $wts[$idx]; // aggregate $raw_data into $raw_data_total with appropriate weight ($wt) foreach ($raw_data as $parent_child => $info) { if ($use_script_name) { // 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); // ignore the newly added edge from main() if (substr($child, 0, 10) != "__script::") { $parent_child = xhprof_build_parent_child_key("__script::$page", $child); } } } if (!isset($raw_data_total[$parent_child])) { foreach ($metrics as $metric) { $raw_data_total[$parent_child][$metric] = ($wt * $info[$metric]); } } else { foreach ($metrics as $metric) { $raw_data_total[$parent_child][$metric] += ($wt * $info[$metric]); } } } } $runs_string = implode(",", $runs); if (isset($wts)) { $wts_string = "in the ratio (" . implode(":", $wts) . ")"; $normalization_count = array_sum($wts); } else { $wts_string = ""; $normalization_count = $run_count; } $run_count = $run_count - count($bad_runs); $data['description'] = "Aggregated Report for $run_count runs: ". "$runs_string $wts_string\n"; $data['raw'] = xhprof_normalize_metrics($raw_data_total, $normalization_count); $data['bad_runs'] = $bad_runs; return $data; } /** * Analyze hierarchical raw data, and compute per-function (flat) * inclusive and exclusive metrics. * * Also, store overall totals in the 2nd argument. * * @param array $raw_data XHProf format raw profiler data. * @param array &$overall_totals OUT argument for returning * overall totals for various * metrics. * @return array Returns a map from function name to its * call count and inclusive & exclusive metrics * (such as wall time, etc.). * * @author Kannan Muthukkaruppan */ function xhprof_compute_flat_info($raw_data, &$overall_totals) { global $display_calls; $metrics = xhprof_get_metrics($raw_data); $overall_totals = array("ct" => 0, "wt" => 0, "ut" => 0, "st" => 0, "cpu" => 0, "mu" => 0, "pmu" => 0, "samples" => 0 ); // compute inclusive times for each function $symbol_tab = xhprof_compute_inclusive_times($raw_data); /* total metric value is the metric value for "main()" */ foreach ($metrics as $metric) { $overall_totals[$metric] = $symbol_tab["main()"][$metric]; } /* * initialize exclusive (self) metric value to inclusive metric value * to start with. * In the same pass, also add up the total number of function calls. */ foreach ($symbol_tab as $symbol => $info) { foreach ($metrics as $metric) { $symbol_tab[$symbol]["excl_" . $metric] = $symbol_tab[$symbol][$metric]; } if ($display_calls) { /* keep track of total number of calls */ $overall_totals["ct"] += $info["ct"]; } } /* adjust exclusive times by deducting inclusive time of children */ foreach ($raw_data as $parent_child => $info) { list($parent, $child) = xhprof_parse_parent_child($parent_child); if ($parent) { foreach ($metrics as $metric) { // make sure the parent exists hasn't been pruned. if (isset($symbol_tab[$parent])) { $symbol_tab[$parent]["excl_" . $metric] -= $info[$metric]; } } } } return $symbol_tab; } /** * Hierarchical diff: * Compute and return difference of two call graphs: Run2 - Run1. * * @author Kannan */ function xhprof_compute_diff($xhprof_data1, $xhprof_data2) { global $display_calls; // use the second run to decide what metrics we will do the diff on $metrics = xhprof_get_metrics($xhprof_data2); $xhprof_delta = $xhprof_data2; foreach ($xhprof_data1 as $parent_child => $info) { if (!isset($xhprof_delta[$parent_child])) { // this pc combination was not present in run1; // initialize all values to zero. if ($display_calls) { $xhprof_delta[$parent_child] = array("ct" => 0); } else { $xhprof_delta[$parent_child] = array(); } foreach ($metrics as $metric) { $xhprof_delta[$parent_child][$metric] = 0; } } if ($display_calls) { $xhprof_delta[$parent_child]["ct"] -= $info["ct"]; } foreach ($metrics as $metric) { $xhprof_delta[$parent_child][$metric] -= $info[$metric]; } } return $xhprof_delta; } /** * Compute inclusive metrics for function. This code was factored out * of xhprof_compute_flat_info(). * * The raw data contains inclusive metrics of a function for each * unique parent function it is called from. The total inclusive metrics * for a function is therefore the sum of inclusive metrics for the * function across all parents. * * @return array Returns a map of function name to total (across all parents) * inclusive metrics for the function. * * @author Kannan */ function xhprof_compute_inclusive_times($raw_data) { global $display_calls; $metrics = xhprof_get_metrics($raw_data); $symbol_tab = array(); /* * First compute inclusive time for each function and total * call count for each function across all parents the * function is called from. */ foreach ($raw_data as $parent_child => $info) { list($parent, $child) = xhprof_parse_parent_child($parent_child); if ($parent == $child) { /* * XHProf PHP extension should never trigger this situation any more. * Recursion is handled in the XHProf PHP extension by giving nested * calls a unique recursion-depth appended name (for example, foo@1). */ xhprof_error("Error in Raw Data: parent & child are both: $parent"); return; } if (!isset($symbol_tab[$child])) { if ($display_calls) { $symbol_tab[$child] = array("ct" => $info["ct"]); } else { $symbol_tab[$child] = array(); } foreach ($metrics as $metric) { $symbol_tab[$child][$metric] = $info[$metric]; } } else { if ($display_calls) { /* increment call count for this child */ $symbol_tab[$child]["ct"] += $info["ct"]; } /* update inclusive times/metric for this child */ foreach ($metrics as $metric) { $symbol_tab[$child][$metric] += $info[$metric]; } } } return $symbol_tab; } /* * Prunes XHProf raw data: * * Any node whose inclusive walltime accounts for less than $prune_percent * of total walltime is pruned. [It is possible that a child function isn't * pruned, but one or more of its parents get pruned. In such cases, when * viewing the child function's hierarchical information, the cost due to * the pruned parent(s) will be attributed to a special function/symbol * "__pruned__()".] * * @param array $raw_data XHProf raw data to be pruned & validated. * @param double $prune_percent Any edges that account for less than * $prune_percent of time will be pruned * from the raw data. * * @return array Returns the pruned raw data. * * @author Kannan */ function xhprof_prune_run($raw_data, $prune_percent) { $main_info = $raw_data["main()"]; if (empty($main_info)) { xhprof_error("XHProf: main() missing in raw data"); return false; } // raw data should contain either wall time or samples information... if (isset($main_info["wt"])) { $prune_metric = "wt"; } else if (isset($main_info["samples"])) { $prune_metric = "samples"; } else { xhprof_error("XHProf: for main() we must have either wt " ."or samples attribute set"); return false; } // determine the metrics present in the raw data.. $metrics = array(); foreach ($main_info as $metric => $val) { if (isset($val)) { $metrics[] = $metric; } } $prune_threshold = (($main_info[$prune_metric] * $prune_percent) / 100.0); init_metrics($raw_data, null, null, false); $flat_info = xhprof_compute_inclusive_times($raw_data); foreach ($raw_data as $parent_child => $info) { list($parent, $child) = xhprof_parse_parent_child($parent_child); // is this child's overall total from all parents less than threshold? if ($flat_info[$child][$prune_metric] < $prune_threshold) { unset($raw_data[$parent_child]); // prune the edge } else if ($parent && ($parent != "__pruned__()") && ($flat_info[$parent][$prune_metric] < $prune_threshold)) { // Parent's overall inclusive metric is less than a threshold. // All edges to the parent node will get nuked, and this child will // be a dangling child. // So instead change its parent to be a special function __pruned__(). $pruned_edge = xhprof_build_parent_child_key("__pruned__()", $child); if (isset($raw_data[$pruned_edge])) { foreach ($metrics as $metric) { $raw_data[$pruned_edge][$metric]+=$raw_data[$parent_child][$metric]; } } else { $raw_data[$pruned_edge] = $raw_data[$parent_child]; } unset($raw_data[$parent_child]); // prune the edge } } return $raw_data; } /** * Set one key in an array and return the array * * @author Kannan */ function xhprof_array_set($arr, $k, $v) { $arr[$k] = $v; return $arr; } /** * Removes/unsets one key in an array and return the array * * @author Kannan */ function xhprof_array_unset($arr, $k) { unset($arr[$k]); return $arr; } /** * Type definitions for URL params */ define('XHPROF_STRING_PARAM', 1); define('XHPROF_UINT_PARAM', 2); define('XHPROF_FLOAT_PARAM', 3); define('XHPROF_BOOL_PARAM', 4); /** * Internal helper function used by various * xhprof_get_param* flavors for various * types of parameters. * * @param string name of the URL query string param * * @author Kannan */ function xhprof_get_param_helper($param) { $val = null; if (isset($_GET[$param])) $val = $_GET[$param]; else if (isset($_POST[$param])) { $val = $_POST[$param]; } return $val; } /** * Extracts value for string param $param from query * string. If param is not specified, return the * $default value. * * @author Kannan */ function xhprof_get_string_param($param, $default = '') { $val = xhprof_get_param_helper($param); if ($val === null) return $default; return $val; } /** * Extracts value for unsigned integer param $param from * query string. If param is not specified, return the * $default value. * * If value is not a valid unsigned integer, logs error * and returns null. * * @author Kannan */ function xhprof_get_uint_param($param, $default = 0) { $val = xhprof_get_param_helper($param); if ($val === null) $val = $default; // trim leading/trailing whitespace $val = trim($val); // if it only contains digits, then ok.. if (ctype_digit($val)) { return $val; } xhprof_error("$param is $val. It must be an unsigned integer."); return null; } /** * Extracts value for a float param $param from * query string. If param is not specified, return * the $default value. * * If value is not a valid unsigned integer, logs error * and returns null. * * @author Kannan */ function xhprof_get_float_param($param, $default = 0) { $val = xhprof_get_param_helper($param); if ($val === null) $val = $default; // trim leading/trailing whitespace $val = trim($val); // TBD: confirm the value is indeed a float. if (true) // for now.. return (float)$val; xhprof_error("$param is $val. It must be a float."); return null; } /** * Extracts value for a boolean param $param from * query string. If param is not specified, return * the $default value. * * If value is not a valid unsigned integer, logs error * and returns null. * * @author Kannan */ function xhprof_get_bool_param($param, $default = false) { $val = xhprof_get_param_helper($param); if ($val === null) $val = $default; // trim leading/trailing whitespace $val = trim($val); switch (strtolower($val)) { case '0': case '1': $val = (bool)$val; break; case 'true': case 'on': case 'yes': $val = true; break; case 'false': case 'off': case 'no': $val = false; break; default: xhprof_error("$param is $val. It must be a valid boolean string."); return null; } return $val; } /** * Initialize params from URL query string. The function * creates globals variables for each of the params * and if the URL query string doesn't specify a particular * param initializes them with the corresponding default * value specified in the input. * * @params array $params An array whose keys are the names * of URL params who value needs to * be retrieved from the URL query * string. PHP globals are created * with these names. The value is * itself an array with 2-elems (the * param type, and its default value). * If a param is not specified in the * query string the default value is * used. * @author Kannan */ function xhprof_param_init($params) { /* Create variables specified in $params keys, init defaults */ foreach ($params as $k => $v) { switch ($v[0]) { case XHPROF_STRING_PARAM: $p = xhprof_get_string_param($k, $v[1]); break; case XHPROF_UINT_PARAM: $p = xhprof_get_uint_param($k, $v[1]); break; case XHPROF_FLOAT_PARAM: $p = xhprof_get_float_param($k, $v[1]); break; case XHPROF_BOOL_PARAM: $p = xhprof_get_bool_param($k, $v[1]); break; default: xhprof_error("Invalid param type passed to xhprof_param_init: " . $v[0]); exit(); } if ($k === 'run') { $p = implode(',', array_filter(explode(',', $p), 'ctype_xdigit')); } if ($k == 'symbol') { $p = strip_tags($p); } // create a global variable using the parameter name. $GLOBALS[$k] = $p; } } /** * Given a partial query string $q return matching function names in * specified XHProf run. This is used for the type ahead function * selector. * * @author Kannan */ function xhprof_get_matching_functions($q, $xhprof_data) { $matches = array(); foreach ($xhprof_data as $parent_child => $info) { list($parent, $child) = xhprof_parse_parent_child($parent_child); if (stripos($parent, $q) !== false) { $matches[$parent] = 1; } if (stripos($child, $q) !== false) { $matches[$child] = 1; } } $res = array_keys($matches); // sort it so the answers are in some reliable order... asort($res); return ($res); } utils/callgraph_utils.php 0000644 00000041627 15152024132 0011577 0 ustar 00 <?php // Copyright (c) 2009 Facebook // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // /* * This file contains callgraph image generation related XHProf utility * functions * */ // Supported ouput format $xhprof_legal_image_types = array( "jpg" => 1, "gif" => 1, "png" => 1, "svg" => 1, // support scalable vector graphic "ps" => 1, ); /** * Send an HTTP header with the response. You MUST use this function instead * of header() so that we can debug header issues because they're virtually * impossible to debug otherwise. If you try to commit header(), SVN will * reject your commit. * * @param string HTTP header name, like 'Location' * @param string HTTP header value, like 'http://www.example.com/' * */ function xhprof_http_header($name, $value) { if (!$name) { xhprof_error('http_header usage'); return null; } if (!is_string($value)) { xhprof_error('http_header value not a string'); } header($name.': '.$value, true); } /** * Genearte and send MIME header for the output image to client browser. * * @author cjiang */ function xhprof_generate_mime_header($type, $length) { switch ($type) { case 'jpg': $mime = 'image/jpeg'; break; case 'gif': $mime = 'image/gif'; break; 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: $mime = false; } if ($mime) { xhprof_http_header('Content-type', $mime); xhprof_http_header('Content-length', (string)$length); } } /** * Generate image according to DOT script. This function will spawn a process * with "dot" command and pipe the "dot_script" to it and pipe out the * generated image content. * * @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. * @returns, binary content of the generated image on success. empty string on * failure. * * @author cjiang */ function xhprof_generate_image_by_dot($dot_script, $type) { $descriptorspec = array( // stdin is a pipe that the child will read from 0 => array("pipe", "r"), // stdout is a pipe that the child will write to 1 => array("pipe", "w"), // stderr is a pipe that the child will write to 2 => array("pipe", "w") ); // Start moodle modification: use $CFG->pathtodot for executing this. // $cmd = " dot -T".$type; global $CFG; $cmd = (!empty($CFG->pathtodot) ? $CFG->pathtodot : 'dot') . ' -T' . $type; // End moodle modification. $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]); $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 execute cmd \"$cmd\""; exit(); } /* * Get the children list of all nodes. */ function xhprof_get_children_table($raw_data) { $children_table = array(); foreach ($raw_data as $parent_child => $info) { list($parent, $child) = xhprof_parse_parent_child($parent_child); if (!isset($children_table[$parent])) { $children_table[$parent] = array($child); } else { $children_table[$parent][] = $child; } } return $children_table; } /** * Generate DOT script from the given raw phprof data. * * @param raw_data, phprof profile data. * @param threshold, float, the threshold value [0,1). The functions in the * raw_data whose exclusive wall times ratio are below the * threshold will be filtered out and won't apprear in the * generated image. * @param page, string(optional), the root node name. This can be used to * replace the 'main()' as the root node. * @param func, string, the focus function. * @param critical_path, bool, whether or not to display critical path with * bold lines. * @returns, string, the DOT script to generate image. * * @author cjiang */ function xhprof_generate_dot_script($raw_data, $threshold, $source, $page, $func, $critical_path, $right=null, $left=null) { $max_width = 5; $max_height = 3.5; $max_fontsize = 35; $max_sizing_ratio = 20; $totals; if ($left === null) { // init_metrics($raw_data, null, null); } $sym_table = xhprof_compute_flat_info($raw_data, $totals); if ($critical_path) { $children_table = xhprof_get_children_table($raw_data); $node = "main()"; $path = array(); $path_edges = array(); $visited = array(); while ($node) { $visited[$node] = true; if (isset($children_table[$node])) { $max_child = null; foreach ($children_table[$node] as $child) { if (isset($visited[$child])) { continue; } if ($max_child === null || abs($raw_data[xhprof_build_parent_child_key($node, $child)]["wt"]) > abs($raw_data[xhprof_build_parent_child_key($node, $max_child)]["wt"])) { $max_child = $child; } } if ($max_child !== null) { $path[$max_child] = true; $path_edges[xhprof_build_parent_child_key($node, $max_child)] = true; } $node = $max_child; } else { $node = null; } } } // if it is a benchmark callgraph, we make the benchmarked function the root. if ($source == "bm" && array_key_exists("main()", $sym_table)) { $total_times = $sym_table["main()"]["ct"]; $remove_funcs = array("main()", "hotprofiler_disable", "call_user_func_array", "xhprof_disable"); foreach ($remove_funcs as $cur_del_func) { if (array_key_exists($cur_del_func, $sym_table) && $sym_table[$cur_del_func]["ct"] == $total_times) { unset($sym_table[$cur_del_func]); } } } // use the function to filter out irrelevant functions. if (!empty($func)) { $interested_funcs = array(); foreach ($raw_data as $parent_child => $info) { list($parent, $child) = xhprof_parse_parent_child($parent_child); if ($parent == $func || $child == $func) { $interested_funcs[$parent] = 1; $interested_funcs[$child] = 1; } } foreach ($sym_table as $symbol => $info) { if (!array_key_exists($symbol, $interested_funcs)) { unset($sym_table[$symbol]); } } } $result = "digraph call_graph {\n"; // Filter out functions whose exclusive time ratio is below threshold, and // also assign a unique integer id for each function to be generated. In the // meantime, find the function with the most exclusive time (potentially the // performance bottleneck). $cur_id = 0; $max_wt = 0; foreach ($sym_table as $symbol => $info) { if (empty($func) && abs($info["wt"] / $totals["wt"]) < $threshold) { unset($sym_table[$symbol]); continue; } if ($max_wt == 0 || $max_wt < abs($info["excl_wt"])) { $max_wt = abs($info["excl_wt"]); } $sym_table[$symbol]["id"] = $cur_id; $cur_id ++; } // Generate all nodes' information. foreach ($sym_table as $symbol => $info) { if ($info["excl_wt"] == 0) { $sizing_factor = $max_sizing_ratio; } else { $sizing_factor = $max_wt / abs($info["excl_wt"]) ; if ($sizing_factor > $max_sizing_ratio) { $sizing_factor = $max_sizing_ratio; } } $fillcolor = (($sizing_factor < 1.5) ? ", style=filled, fillcolor=red" : ""); if ($critical_path) { // highlight nodes along critical path. if (!$fillcolor && array_key_exists($symbol, $path)) { $fillcolor = ", style=filled, fillcolor=yellow"; } } $fontsize = ", fontsize=" .(int)($max_fontsize / (($sizing_factor - 1) / 10 + 1)); $width = ", width=".sprintf("%.1f", $max_width / $sizing_factor); $height = ", height=".sprintf("%.1f", $max_height / $sizing_factor); if ($symbol == "main()") { $shape = "octagon"; $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"]).")"; } if ($left === null) { $label = ", label=\"".$name."\\nExcl: " .(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)) ." 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". "\\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)) ." ms"."\\nExcl: " .(sprintf("%.3f",$left[$symbol]["excl_wt"] / 1000.0)) ." ms - 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". "\\nExcl: 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"]))."\""; } } $result .= "N" . $sym_table[$symbol]["id"]; $result .= "[shape=$shape ".$label.$width .$height.$fontsize.$fillcolor."];\n"; } // Generate all the edges' information. foreach ($raw_data as $parent_child => $info) { list($parent, $child) = xhprof_parse_parent_child($parent_child); if (isset($sym_table[$parent]) && isset($sym_table[$child]) && (empty($func) || (!empty($func) && ($parent == $func || $child == $func)))) { $label = $info["ct"] == 1 ? $info["ct"]." call" : $info["ct"]." calls"; $headlabel = $sym_table[$child]["wt"] > 0 ? sprintf("%.1f%%", 100 * $info["wt"] / $sym_table[$child]["wt"]) : "0.0%"; $taillabel = ($sym_table[$parent]["wt"] > 0) ? sprintf("%.1f%%", 100 * $info["wt"] / ($sym_table[$parent]["wt"] - $sym_table["$parent"]["excl_wt"])) : "0.0%"; $linewidth = 1; $arrow_size = 1; if ($critical_path && isset($path_edges[xhprof_build_parent_child_key($parent, $child)])) { $linewidth = 10; $arrow_size = 2; } $result .= "N" . $sym_table[$parent]["id"] . " -> N" . $sym_table[$child]["id"]; $result .= "[arrowsize=$arrow_size, color=grey, style=\"setlinewidth($linewidth)\"," ." label=\"" .$label."\", headlabel=\"".$headlabel ."\", taillabel=\"".$taillabel."\" ]"; $result .= ";\n"; } } $result = $result . "\n}"; return $result; } function xhprof_render_diff_image($xhprof_runs_impl, $run1, $run2, $type, $threshold, $source) { $total1; $total2; $raw_data1 = $xhprof_runs_impl->get_run($run1, $source, $desc_unused); $raw_data2 = $xhprof_runs_impl->get_run($run2, $source, $desc_unused); // init_metrics($raw_data1, null, null); $children_table1 = xhprof_get_children_table($raw_data1); $children_table2 = xhprof_get_children_table($raw_data2); $symbol_tab1 = xhprof_compute_flat_info($raw_data1, $total1); $symbol_tab2 = xhprof_compute_flat_info($raw_data2, $total2); $run_delta = xhprof_compute_diff($raw_data1, $raw_data2); $script = xhprof_generate_dot_script($run_delta, $threshold, $source, null, null, true, $symbol_tab1, $symbol_tab2); $content = xhprof_generate_image_by_dot($script, $type); xhprof_generate_mime_header($type, strlen($content)); echo $content; } /** * Generate image content from phprof run id. * * @param object $xhprof_runs_impl An object that implements * the iXHProfRuns interface * @param run_id, integer, the unique id for the phprof run, this is the * primary key for phprof database table. * @param type, string, one of the supported image types. See also * $xhprof_legal_image_types. * @param threshold, float, the threshold value [0,1). The functions in the * raw_data whose exclusive wall times ratio are below the * threshold will be filtered out and won't apprear in the * generated image. * @param func, string, the focus function. * @returns, string, the DOT script to generate image. * * @author cjiang */ function xhprof_get_content_by_run($xhprof_runs_impl, $run_id, $type, $threshold, $func, $source, $critical_path) { if (!$run_id) return ""; $raw_data = $xhprof_runs_impl->get_run($run_id, $source, $description); if (!$raw_data) { xhprof_error("Raw data is empty"); return ""; } $script = xhprof_generate_dot_script($raw_data, $threshold, $source, $description, $func, $critical_path); $content = xhprof_generate_image_by_dot($script, $type); return $content; } /** * Generate image from phprof run id and send it to client. * * @param object $xhprof_runs_impl An object that implements * the iXHProfRuns interface * @param run_id, integer, the unique id for the phprof run, this is the * primary key for phprof database table. * @param type, string, one of the supported image types. See also * $xhprof_legal_image_types. * @param threshold, float, the threshold value [0,1). The functions in the * raw_data whose exclusive wall times ratio are below the * threshold will be filtered out and won't apprear in the * generated image. * @param func, string, the focus function. * @param bool, does this run correspond to a PHProfLive run or a dev run? * @author cjiang */ function xhprof_render_image($xhprof_runs_impl, $run_id, $type, $threshold, $func, $source, $critical_path) { $content = xhprof_get_content_by_run($xhprof_runs_impl, $run_id, $type, $threshold, $func, $source, $critical_path); if (!$content) { print "Error: either we can not find profile data for run_id ".$run_id ." or the threshold ".$threshold." is too small or you do not" ." have 'dot' image generation utility installed."; exit(); } xhprof_generate_mime_header($type, strlen($content)); echo $content; }
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | ���֧ߧ֧�ѧ�ڧ� ����ѧߧڧ��: 0 |
proxy
|
phpinfo
|
���ѧ����ۧܧ�