";
$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 .= "
";
$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;
}