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 xhprof_Name", "ct" => "Calls", "Calls%" => "Calls%", "wt" => "Incl. Wall Time
(microsec)", "IWall%" => "IWall%", "excl_wt" => "Excl. Wall Time
(microsec)", "EWall%" => "EWall%", "ut" => "Incl. User
(microsecs)", "IUser%" => "IUser%", "excl_ut" => "Excl. User
(microsec)", "EUser%" => "EUser%", "st" => "Incl. Sys
(microsec)", "ISys%" => "ISys%", "excl_st" => "Excl. Sys
(microsec)", "ESys%" => "ESys%", "cpu" => "Incl. CPU
(microsecs)", "ICpu%" => "ICpu%", "excl_cpu" => "Excl. CPU
(microsec)", "ECpu%" => "ECPU%", "mu" => "Incl.
MemUse
(bytes)", "IMUse%" => "IMemUse%", "excl_mu" => "Excl.
MemUse
(bytes)", "EMUse%" => "EMemUse%", "pmu" => "Incl.
PeakMemUse
(bytes)", "IPMUse%" => "IPeakMemUse%", "excl_pmu" => "Excl.
PeakMemUse
(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 xhprof_Name", "ct" => "Calls Diff", "Calls%" => "Calls
Diff%", "wt" => "Incl. Wall
Diff
(microsec)", "IWall%" => "IWall
Diff%", "excl_wt" => "Excl. Wall
Diff
(microsec)", "EWall%" => "EWall
Diff%", "ut" => "Incl. User Diff
(microsec)", "IUser%" => "IUser
Diff%", "excl_ut" => "Excl. User
Diff
(microsec)", "EUser%" => "EUser
Diff%", "cpu" => "Incl. CPU Diff
(microsec)", "ICpu%" => "ICpu
Diff%", "excl_cpu" => "Excl. CPU
Diff
(microsec)", "ECpu%" => "ECpu
Diff%", "st" => "Incl. Sys Diff
(microsec)", "ISys%" => "ISys
Diff%", "excl_st" => "Excl. Sys Diff
(microsec)", "ESys%" => "ESys
Diff%", "mu" => "Incl.
MemUse
Diff
(bytes)", "IMUse%" => "IMemUse
Diff%", "excl_mu" => "Excl.
MemUse
Diff
(bytes)", "EMUse%" => "EMemUse
Diff%", "pmu" => "Incl.
PeakMemUse
Diff
(bytes)", "IPMUse%" => "IPeakMemUse
Diff%", "excl_pmu" => "Excl.
PeakMemUse
Diff
(bytes)", "EPMUse%" => "EPeakMemUse
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; // 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. // /** * Our coding convention disallows relative paths in hrefs. * Get the base URL path from the SCRIPT_NAME. */ $base_path = rtrim(dirname($_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_xhprof_include_js_css($ui_dir_url_path = NULL) { if (empty($ui_dir_url_path)) { $ui_dir_url_path = rtrim(dirname($_SERVER['SCRIPT_NAME']), "/"); } // style sheets echo ""; echo ""; echo ""; // javascript echo ""; echo ""; echo ""; echo ""; } /* * 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 xhprof_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[] = ''; } 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_xhprof_render_link($content, $href, $class='', $id='', $title='', $target='', $onclick='', $style='', $access='', $onmouseover='', $onmouseout='', $onmousedown='') { if (!$content) { return ''; } if ($href) { $link = ' $right) ? -1 : 1; } } /** * Initialize the metrics we'll display based on the information * in the raw data. * * @author Kannan */ function xhprof_init_metrics($xhprof_data, $rep_symbol, $sort, $diff_report = FALSE) { global $stats; global $pc_stats; global $metrics; global $diff_mode; global $sort_col; global $display_calls; $diff_mode = $diff_report; if (!empty($sort)) { if (array_key_exists($sort, xhprof_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 * or single run report mode). * * @author Kannan */ function xhprof_stat_description($stat, $desc = FALSE) { global $diff_mode; // Textual descriptions for column headers in "single run" mode $descriptions = array( "fn" => "Function Name", "ct" => "Calls", "Calls%" => "Calls%", "wt" => "Incl. Wall Time (microsec)", "IWall%" => "IWall%", "excl_wt" => "Excl. Wall Time (microsec)", "EWall%" => "EWall%", "ut" => "Incl. User (microsecs)", "IUser%" => "IUser%", "excl_ut" => "Excl. User (microsec)", "EUser%" => "EUser%", "st" => "Incl. Sys (microsec)", "ISys%" => "ISys%", "excl_st" => "Excl. Sys (microsec)", "ESys%" => "ESys%", "cpu" => "Incl. CPU (microsecs)", "ICpu%" => "ICpu%", "excl_cpu" => "Excl. CPU (microsec)", "ECpu%" => "ECPU%", "mu" => "Incl. MemUse (bytes)", "IMUse%" => "IMemUse%", "excl_mu" => "Excl. MemUse (bytes)", "EMUse%" => "EMemUse%", "pmu" => "Incl. PeakMemUse (bytes)", "IPMUse%" => "IPeakMemUse%", "excl_pmu" => "Excl. PeakMemUse (bytes)", "EPMUse%" => "EPeakMemUse%", "samples" => "Incl. Samples", "ISamples%" => "ISamples%", "excl_samples" => "Excl. Samples", "ESamples%" => "ESamples%", ); // Textual descriptions for column headers in "diff" mode $diff_descriptions = array( "fn" => "Function Name", "ct" => "Calls Diff", "Calls%" => "Calls Diff%", "wt" => "Incl. Wall Diff (microsec)", "IWall%" => "IWall Diff%", "excl_wt" => "Excl. Wall Diff (microsec)", "EWall%" => "EWall Diff%", "ut" => "Incl. User Diff (microsec)", "IUser%" => "IUser Diff%", "excl_ut" => "Excl. User Diff (microsec)", "EUser%" => "EUser Diff%", "cpu" => "Incl. CPU Diff (microsec)", "ICpu%" => "ICpu Diff%", "excl_cpu" => "Excl. CPU Diff (microsec)", "ECpu%" => "ECpu Diff%", "st" => "Incl. Sys Diff (microsec)", "ISys%" => "ISys Diff%", "excl_st" => "Excl. Sys Diff (microsec)", "ESys%" => "ESys Diff%", "mu" => "Incl. MemUse Diff (bytes)", "IMUse%" => "IMemUse Diff%", "excl_mu" => "Excl. MemUse Diff (bytes)", "EMUse%" => "EMemUse Diff%", "pmu" => "Incl. PeakMemUse Diff (bytes)", "IPMUse%" => "IPeakMemUse Diff%", "excl_pmu" => "Excl. PeakMemUse Diff (bytes)", "EPMUse%" => "EPeakMemUse Diff%", "samples" => "Incl. Samples Diff", "ISamples%" => "ISamples Diff%", "excl_samples" => "Excl. Samples Diff", "ESamples%" => "ESamples Diff%", ); if ($diff_mode) { if ($desc) $diff_descriptions = array_flip($diff_descriptions); return $diff_descriptions[$stat]; } else { if ($desc) $descriptions = array_flip($descriptions); return $descriptions[$stat]; } } /** * Analyze raw data & generate the profiler report * (common for both single run mode and diff mode). * * @author: Kannan */ function xhprof_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_path; $output = ''; // 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("Run #%s: %s", $run1, $run1_desc); if ($diff_mode) { $diff_text = "Diff"; $run1_link = l('View Run #' . $run1, xhprof_path_for_run($run1)); $run2_txt = sprintf("Run #%s: %s", $run2, $run2_desc); $run2_link = l('View Run #' . $run2, xhprof_path_for_run($run2)); } else { $diff_text = "Run"; } // set up the action links for operations that can be done on this report $links = array(); $links[] = l("View Top Level $diff_text Report", xhprof_path_for_run($run1, $run2)); 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[] = l('Invert ' . $diff_text . ' Report', xhprof_path_for_run($run2, $run1)); } $output .= xhprof_render_actions($links); $output .= '
' . '
' . $diff_text . ' Report
' . '
' . ($diff_mode ? $run1_txt . '
vs.
' . $run2_txt : $run1_txt) . '
' . '
'; // data tables if (!empty($rep_symbol)) { if (!isset($symbol_tab[$rep_symbol])) { drupal_set_message(t("Symbol $rep_symbol not found in XHProf run"));; return $output; } // Single function xhprof_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; $output .= xhprof_symbol_report($url_params, $run_delta, $symbol_tab[$rep_symbol], $sort, $rep_symbol, $run1, $info1, $run2, $info2); } else { $output .= xhprof_symbol_report($url_params, $run1_data, $symbol_tab[$rep_symbol], $sort, $rep_symbol, $run1); } } else { // flat top-level report of all functions. $output .= xhprof_full_report($url_params, $symbol_tab, $sort, $run1, $run2); } return $output; } /** * 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 xhprof_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 element with a numeric value. */ function xhprof_print_num($num, $fmt_func = NULL, $bold = FALSE, $attributes = NULL) { $class = xhprof_get_print_class($num, $bold); if (!empty($fmt_func)) { $num = call_user_func($fmt_func, $num); } //return "$num\n"; $num = number_format($num); return "$num\n"; //return $num; } /** * Prints a element with a pecentage. */ function xhprof_print_pct($numer, $denom, $bold = FALSE, $attributes = NULL) { global $vbar; global $vbbar; global $diff_mode; $class = xhprof_get_print_class($numer, $bold); if ($denom == 0) { $pct = "N/A%"; } else { $pct = xhprof_percent_format($numer / abs($denom)); } return "$pct"; } /** * Print "flat" data corresponding to one function. * * @author Kannan */ function xhprof_print_function_info($url_params, $info, $sort, $run1, $run2) { global $totals; global $sort_col; global $metrics; global $format_cbk; global $display_calls; global $base_path; $output = ''; $href = "$base_path/?" . http_build_query(xhprof_array_set($url_params, 'symbol', $info["fn"])); //$output .= ''; //$output .= xhprof_xhprof_render_link($info["fn"], $href); if ($display_calls) { // Call Count.. $output .= xhprof_print_num_cell($info["ct"], $format_cbk["ct"], ($sort_col == "ct")); $output .= xhprof_print_pct_cell($info["ct"], $totals["ct"], ($sort_col == "ct")); } // Other metrics.. foreach ($metrics as $metric) { // Inclusive metric $output .= xhprof_print_num_cell($info[$metric], $format_cbk[$metric], ($sort_col == $metric)); $output .= xhprof_print_pct_cell($info[$metric], $totals[$metric], ($sort_col == $metric)); // Exclusive Metric $output .= xhprof_print_num_cell($info["excl_" . $metric], $format_cbk["excl_" . $metric], ($sort_col == "excl_" . $metric)); $output .= xhprof_print_pct_cell($info["excl_" . $metric], $totals[$metric], ($sort_col == "excl_" . $metric)); } return $output; } /** * Print non-hierarchical (flat-view) of profiler data. * * @author Kannan */ function xhprof_print_flat_data($url_params, $title, $flat_data, $sort, $run1, $run2, $limit) { global $stats; global $pc_stats; global $totals; global $vwbar; global $base_path; $output = ''; // Table attributes $attributes = array('id' => 'xhprof-run-table'); // Headers. $header = array(); foreach ($stats as $stat) { $desc = xhprof_stat_description($stat); if (array_key_exists($stat, xhprof_sortable_columns($stat))) { if (isset($_GET['sort']) && $stat == $_GET['sort']) { $header_desc = l(t($desc), current_path(), array('query' => array('sort' => $stat), t($desc))); $header[] = array('data' => t($header_desc) . theme('tablesort_indicator', array('style' => 'desc'))); } else { $header_desc = l(t($desc), current_path(), array('query' => array('sort' => $stat), t($desc))); $header[] = array('data' => t($header_desc)); } } else { $header[] = array('data' => t($desc)); } } // Table rows $rows = array(); $trail = menu_get_active_trail(); foreach ($flat_data as $data) { $row = array( array('data' => l($data["fn"], $trail[1]["href"] . '/' . $data["fn"])), array('data' => $data['ct'], 'class' => 'xhprof_micro'), array('data' => xhprof_print_pct($data['ct'], $totals['ct']), 'class' => 'xhprof_percent'), array('data' => $data['wt'], 'class' => 'xhprof_micro'), array('data' => xhprof_print_pct($data['wt'], $totals['wt'])), array('data' => $data['excl_wt'], 'class' => 'xhprof_percent'), array('data' => xhprof_print_pct($data['excl_wt'], $totals['wt']), 'class' => 'xhprof_micro'), array('data' => $data['cpu'], 'class' => 'xhprof_percent'), array('data' => xhprof_print_pct($data['cpu'], $totals['cpu']), 'class' => 'xhprof_percent'), array('data' => $data['excl_cpu'], 'class' => 'xhprof_micro'), array('data' => xhprof_print_pct($data['excl_cpu'], $totals['cpu']), 'class' => 'xhprof_percent'), array('data' => $data['mu'], 'class' => 'xhprof_micro'), array('data' => xhprof_print_pct($data['mu'], $totals['mu']), 'class' => 'xhprof_percent'), array('data' => $data['excl_mu'], 'class' => 'xhprof_micro'), array('data' => xhprof_print_pct($data['excl_mu'], $totals['mu']), 'xhprof_percent'), array('data' => $data['pmu'], 'class' => 'xhprof_micro'), array('data' => xhprof_print_pct($data['pmu'], $totals['pmu']), 'xhprof_percent'), array('data' => $data['excl_pmu'], 'xhprof_micro'), array('data' => xhprof_print_pct($data['excl_pmu'], $totals['pmu']), 'class' => 'xhprof_percent'), ); $rows[] = $row; } $size = count($flat_data); if (!$limit) { // no limit $limit = $size; $display_link = ""; } else { $display_link = xhprof_xhprof_render_link(" [ display all ]", "$base_path/?" . http_build_query(xhprof_array_set($url_params, 'all', 1))); } $output .= "

$title $display_link


"; $output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => $attributes)); return $output; } /** * Generates a tabular report for all functions. This is the top-level report. * * @author Kannan */ function xhprof_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 $stats; $possible_metrics = xhprof_get_possible_metrics(); $output = ''; if ($diff_mode) { $base_url_params = xhprof_array_unset(xhprof_array_unset($url_params, 'run1'), 'run2'); $href1 = "$base_path/?" . http_build_query(xhprof_array_set($base_url_params, 'run', $run1)); $href2 = "$base_path/?" . http_build_query(xhprof_array_set($base_url_params, 'run', $run2)); $output .= "

Overall Diff Summary

"; ///$output .= '' . "\n"; //$output .= ''; $headers = array(); $headers[] = ""; $headers[] = xhprof_xhprof_render_link("Run #$run1", $href1); $headers[] = xhprof_xhprof_render_link("Run #$run2", $href2); $headers[] = 'Diff'; $headers[] = 'Diff%'; $rows = array(); if ($display_calls) { $row = array( array('data' => 'Number of function xhprof_Calls'), array('data' => xhprof_print_num($totals_1["ct"], $format_cbk["ct"]), 'class' => 'xhprof_micro'), array('data' => xhprof_print_num($totals_2["ct"], $format_cbk["ct"]), 'class' => 'xhprof_micro'), array('data' => xhprof_print_num($totals_2["ct"] - $totals_1["ct"], $format_cbk["ct"], TRUE), 'class' => 'xhprof_micro'), array('data' => xhprof_print_pct($totals_2["ct"] - $totals_1["ct"], $totals_1["ct"], TRUE), 'class' => 'xhprof_percent'), ); $rows[] = $row; } foreach ($metrics as $m) { $desc = xhprof_stat_description($m, $desc = FALSE); $row = array( array('data' => str_replace("
", " ", $desc)), array('data' => xhprof_print_num($totals_1[$m], $format_cbk[$m]), 'class' => 'xhprof_micro'), array('data' => xhprof_print_num($totals_2[$m], $format_cbk[$m]), 'class' => 'xhprof_micro'), array('data' => xhprof_print_num($totals_2[$m] - $totals_1[$m], $format_cbk[$m], TRUE), 'class' => 'xhprof_micro'), array('data' => xhprof_print_pct($totals_2[$m] - $totals_1[$m], $totals_1[$m], TRUE), 'class' => 'xhprof_percent'), ); $rows[] = $row; } $attributes = array('class' => array('xhprof-table', 'xhprof-summary-table')); $output .= theme('table', array('header' => $headers, 'rows' => $rows, 'attributes' => $attributes)); $callgraph_report_title = '[View Regressions/Improvements using Callgraph Diff]'; } else { $vars = array( 'totals' => $totals, 'possible_metrics' => $possible_metrics, 'metrics' => $metrics, 'display_calls' => $display_calls, ); $output .= theme('xhprof_overall_summary', $vars); } $output .= "

"; //$output .= xhprof_xhprof_render_link($callgraph_report_title, "$base_path/callgraph.php" . "?" . http_build_query($url_params)); $output .= "

"; $flat_data = array(); foreach ($symbol_tab as $symbol => $info) { $tmp = $info; $tmp["fn"] = $symbol; $flat_data[] = $tmp; } usort($flat_data, 'xhprof_sort_cbk'); $output .= "
"; 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("
", " ", $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 Regressions/" . "Improvements: " . "Sorted by $desc Diff"; } } else { if ($all) { $title = "Sorted by $desc"; } else { $title = "Displaying top $limit functions: Sorted by $desc"; } } $vars = array( 'stats' => $stats, 'totals' => $totals, 'url_params' => $url_params, 'title' => $title, 'flat_data' => $flat_data, 'limit' => $limit, 'run1' => $run1, 'run2' => $run2, ); $output .= theme('xhprof_run_table', $vars); return $output; } /** * Return attribute names and values to be used by javascript tooltip. */ function xhprof_get_tooltip_attributes($type, $metric) { return "type='$type' metric='$metric'"; } /** * Print info for a parent or child function xhprof_in the * parent & children report. * * @author Kannan */ function xhprof_pc_info($info, $base_ct, $base_info, $parent) { global $sort_col; global $metrics; global $format_cbk; global $display_calls; $type = $parent ? "Parent" : "Child"; if ($display_calls) { $mouseoverct = xhprof_get_tooltip_attributes($type, "ct"); // Call count. $row = array(xhprof_print_num_cell($info["ct"], $format_cbk["ct"], ($sort_col == "ct"), $mouseoverct)); $row[] = xhprof_print_pct_cell($info["ct"], $base_ct, ($sort_col == "ct"), $mouseoverct); } // Inclusive metric values. foreach ($metrics as $metric) { $row[] = xhprof_print_num_cell($info[$metric], $format_cbk[$metric], ($sort_col == $metric), xhprof_get_tooltip_attributes($type, $metric)); $row[] = xhprof_print_pct_cell($info[$metric], $base_info[$metric], ($sort_col == $metric), xhprof_get_tooltip_attributes($type, $metric)); } return $row; } function xhprof_print_pc_array($url_params, $results, $base_ct, $base_info, $parent, $run1, $run2) { global $base_path; $rows = array(); // Construct section title $title = $parent ? 'Parent function' : 'Child function'; if (count($results) > 1) { $title = '' . $title . 's'; } $rows[] = array(array('data' => $title, 'colspan' => 11)); usort($results, 'xhprof_sort_cbk'); foreach ($results as $info) { $row = array(); $row[] = l($info["fn"], xhprof_path_for_symbol($info["fn"], $run1, $run2)); $row = array_merge($row, xhprof_pc_info($info, $base_ct, $base_info, $parent)); $rows[] = $row; } return $rows; } function xhprof_print_symbol_summary($symbol_info, $stat, $base) { $val = $symbol_info[$stat]; $desc = str_replace("
", " ", xhprof_stat_description($stat)); $output .= "$desc: "; $output .= number_format($val); $output .= " (" . pct($val, $base) . "% of overall)"; if (substr($stat, 0, 4) == "excl") { $func_base = $symbol_info[str_replace("excl_", "", $stat)]; $output .= " (" . pct($val, $func_base) . "% of this function)"; } $output .= "
"; return $output; } /** * Generates a report for a single function/symbol. * * @author Kannan */ function xhprof_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 $metrics; global $diff_mode; global $descriptions; global $format_cbk; global $sort_col; global $display_calls; global $base_path; $output = ''; $possible_metrics = xhprof_get_possible_metrics(); if ($diff_mode) { $diff_text = "Diff"; $regr_impr = "Regression/Improvement"; } else { $diff_text = ""; $regr_impr = ""; } if ($diff_mode) { $base_url_params = xhprof_array_unset(xhprof_array_unset($url_params, 'run1'), 'run2'); $href1 = "$base_path?" . http_build_query(xhprof_array_set($base_url_params, 'run', $run1)); $href2 = "$base_path?" . http_build_query(xhprof_array_set($base_url_params, 'run', $run2)); $output .= "

$regr_impr summary for $rep_symbol

"; $output .= '
' . "\n"; $output .= ''; $output .= ""; $output .= ""; $output .= ""; $output .= ""; $output .= ""; $output .= ''; $output .= ''; if ($display_calls) { $output .= ""; $output .= xhprof_print_num_cell($symbol_info1["ct"], $format_cbk["ct"]); $output .= xhprof_print_num_cell($symbol_info2["ct"], $format_cbk["ct"]); $output .= xhprof_print_num_cell($symbol_info2["ct"] - $symbol_info1["ct"], $format_cbk["ct"], TRUE); $output .= xhprof_print_pct_cell($symbol_info2["ct"] - $symbol_info1["ct"], $symbol_info1["ct"], TRUE); $output .= ''; } foreach ($metrics as $metric) { $m = $metric; // Inclusive stat for metric $output .= ''; $output .= ""; $output .= xhprof_print_num($symbol_info1[$m], $format_cbk[$m]); $output .= xhprof_print_num($symbol_info2[$m], $format_cbk[$m]); $output .= xhprof_print_num($symbol_info2[$m] - $symbol_info1[$m], $format_cbk[$m], TRUE); $output .= xhprof_print_pct($symbol_info2[$m] - $symbol_info1[$m], $symbol_info1[$m], TRUE); $output .= ''; // AVG (per call) Inclusive stat for metric $output .= ''; $output .= ""; $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']); } $output .= xhprof_print_num($avg_info1, $format_cbk[$m]); $output .= xhprof_print_num($avg_info2, $format_cbk[$m]); $output .= xhprof_print_num($avg_info2 - $avg_info1, $format_cbk[$m], TRUE); $output .= xhprof_print_pct($avg_info2 - $avg_info1, $avg_info1, TRUE); $output .= ''; // Exclusive stat for metric $m = "excl_" . $metric; $output .= ''; $output .= ""; $output .= xhprof_print_num($symbol_info1[$m], $format_cbk[$m]); $output .= xhprof_print_num($symbol_info2[$m], $format_cbk[$m]); $output .= xhprof_print_num($symbol_info2[$m] - $symbol_info1[$m], $format_cbk[$m], TRUE); $output .= xhprof_print_pct($symbol_info2[$m] - $symbol_info1[$m], $symbol_info1[$m], TRUE); $output .= ''; } $output .= '
$rep_symbolRun #$run1Run #$run2DiffDiff%
Number of function xhprof_Calls
" . str_replace("
", " ", $descriptions[$m]) . "
" . str_replace("
", " ", $descriptions[$m]) . " per call
" . str_replace("
", " ", $descriptions[$m]) . "
'; } $output .= "

"; $output .= "Parent/Child $regr_impr report for $rep_symbol"; // TODO: Maybe include this? //$callgraph_href = "$base_path/callgraph.php?" . http_build_query(xhprof_array_set($url_params, 'func', $rep_symbol)); //$output .= " [View Callgraph $diff_text]"; $output .= "

"; $headers = array(); $rows = array(); // Headers. foreach ($pc_stats as $stat) { $desc = xhprof_stat_description($stat); if (array_key_exists($stat, xhprof_sortable_columns($stat))) { if ($stat == $sort) { $header_desc = l(t($desc), current_path(), array('query' => array('sort' => $stat), t($desc))); $headers[] = array('data' => t($header_desc) . theme('tablesort_indicator', array('style' => 'desc'))); } else { $header_desc = l(t($desc), current_path(), array('query' => array('sort' => $stat), t($desc))); $headers[] = array('data' => t($header_desc)); } } else { $headers[] = array('data' => t($desc)); } } $rows[] = array(array('data' => 'Current Function', 'colspan' => 11)); // Make this a self-reference to facilitate copy-pasting snippets to e-mails. $row = array("$rep_symbol"); if ($display_calls) { // Call Count. $row[] = xhprof_print_num_cell($symbol_info["ct"], $format_cbk["ct"]); $row[] = xhprof_print_pct_cell($symbol_info["ct"], $totals["ct"]); } // Inclusive Metrics for current function. foreach ($metrics as $metric) { $row[] = xhprof_print_num_cell($symbol_info[$metric], $format_cbk[$metric], ($sort_col == $metric)); $row[] = xhprof_print_pct_cell($symbol_info[$metric], $totals[$metric], ($sort_col == $metric)); } $rows[] = $row; $row = array( "" . t("Exclusive Metrics $diff_text for Current Function") . "", ); if ($display_calls) { // Call Count $row[] = "$vbar"; $row[] = "$vbar"; } // Exclusive Metrics for current function foreach ($metrics as $metric) { $row[] = xhprof_print_num_cell($symbol_info["excl_" . $metric], $format_cbk["excl_" . $metric], ($sort_col == $metric), xhprof_get_tooltip_attributes("Child", $metric)); $row[] = xhprof_print_pct_cell($symbol_info["excl_" . $metric], $symbol_info[$metric], ($sort_col == $metric), xhprof_get_tooltip_attributes("Child", $metric)); } $rows[] = $row; // list of callers/parent functions $results = array(); $base_ct = $display_calls ? $symbol_info["ct"] : $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, 'xhprof_sort_cbk'); if (count($results) > 0) { $pc_row = xhprof_print_pc_array($url_params, $results, $base_ct, $base_info, TRUE, $run1, $run2); $rows = array_merge($rows, $pc_row); } // 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, 'xhprof_sort_cbk'); if (count($results)) { $pc_row = xhprof_print_pc_array($url_params, $results, $base_ct, $base_info, FALSE, $run1, $run2); $rows = array_merge($rows, $pc_row); } $attributes = array('class' => array('xhprof-table')); $output .= theme('table', array('header' => $headers, 'rows' => $rows, 'attributes' => $attributes)); // TODO: Convert tooltips. // These will be used for pop-up tips/help. // Related javascript code is in: xhprof_report.js $output .= "\n"; $output .= ''; $output .= "\n"; return $output; } /** * Generate the profiler report for a single run. * * @author Kannan */ /* function xhprof_profiler_single_run_report ($url_params, $xhprof_data, $run_desc, $rep_symbol, $sort, $run) { $output = ''; xhprof_init_metrics($xhprof_data, $rep_symbol, $sort, FALSE); $output .= xhprof_profiler_report($url_params, $rep_symbol, $sort, $run, $run_desc, $xhprof_data); return $output; } */ /** * Generate the profiler report for diff mode (delta between two runs). * * @author Kannan */ //function xhprof_profiler_diff_report($url_params, // $xhprof_data1, // $run1_desc, // $xhprof_data2, // $run2_desc, // $rep_symbol, // $sort, // $run1, // $run2) { // // $output = ''; // // Initialize what metrics we'll display based on data in Run2 // $output .= xhprof_init_metrics($xhprof_data2, $rep_symbol, $sort, TRUE); // // $output .= xhprof_profiler_report($url_params, // $rep_symbol, // $sort, // $run1, // $run1_desc, // $xhprof_data1, // $run2, // $run2_desc, // $xhprof_data2); // return $output; //} /** * 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 xhprof_symbol. If non-empty then the * parent/child view of this function xhprof_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 xhprof_displayXHProfReport($xhprof_runs_impl, $options) { // Extract options: $url_params, $source, $run, $wts, $symbol, $sort, $run1, $run2; extract($options); 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 (isset($_GET['order'])) { $sort = xhprof_stat_description($_GET['order'], TRUE); } if (count($runs_array) == 1) { $xhprof_data = $xhprof_runs_impl->get_run($runs_array[0], $source, $description, $sort); } 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']; } $output .= xhprof_profiler_single_run_report($url_params, $xhprof_data, $description, $symbol, $sort, $run); } elseif ($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); $output .= xhprof_profiler_diff_report($url_params, $xhprof_data1, $description1, $xhprof_data2, $description2, $symbol, $sort, $run1, $run2); } else { $output .= "No XHProf runs specified in the URL."; } return $output; } /** * 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; } /** * 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; } /** * 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; } /* * 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]); } /** * 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) { $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]; } // 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; } function xhprof_compute_inclusive_times($raw_data) { $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). */ watchdog("Error in Raw Data: parent & child are both: %parent", array('%parent' => $parent)); return; } if (!isset($symbol_tab[$child])) { $symbol_tab[$child] = array("ct" => $info["ct"]); foreach ($metrics as $metric) { $symbol_tab[$child][$metric] = $info[$metric]; } } else { // 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; } /* * The list of possible metrics collected as part of XHProf that * require inclusive/exclusive handling while reporting. * * @author Kannan */ function xhprof_get_possible_metrics() { $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; }