/** * 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; $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_path}/index.php?" . http_build_query(xhprof_array_set($base_url_params, 'run', $run1)); $href2 = "{$base_path}/index.php?" . 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_path}/index.php?" . 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 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}, style=\"setlinewidth({$linewidth})\"," . " label=\"" . $label . "\", headlabel=\"" . $headlabel . "\", taillabel=\"" . $taillabel . "\" ]"; $result .= ";\n"; } } $result = $result . "\n}"; return $result; }
/** * 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; }
/** * 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; $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_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)); print '<div id="diff_page" class="title left hdblock" style="margin-left:20px;" >zPerfmon/Profile Diff</div>'; print '<button id="back_button" style="float:right;width:100px;" type=button >Back</button>'; print '<button id="home_button" style="float:right;width:100px;" type=button >Home</button>'; print '<br /><br /><br />'; print "<div style='font-size:16px' align=center>{$regr_impr} summary for {$rep_symbol}<br><br></div>"; $file1_arr = explode("/", $run1); $file2_arr = explode("/", $run2); $time_page1 = $file1_arr[count($file1_arr) - 1]; $time_page2 = $file2_arr[count($file2_arr) - 1]; $game_id1 = $file1_arr[count($file1_arr) - 3]; $game_id2 = $file2_arr[count($file2_arr) - 3]; $game_me1 = explode("_", $game_id1); $game_me2 = explode("_", $game_id2); $game1 = $game_me1[0]; $game2 = $game_me2[0]; if (sizeof($game_me1) > 1) { $array1 = $game_me1[1]; } else { $array1 = "all"; } if (sizeof($game_me2) > 1) { $array2 = $game_me2[1]; } else { $array2 = "all"; } if (sizeof($game_me1) > 1) { if (is_numeric($game_me1[sizeof($game_me1) - 1])) { for ($i = 1; $i < sizeof($game_me1) - 1; $i++) { $game1 = $game1 . "_" . $game_me1[$i]; } $array1 = $game_me1[sizeof($game_me1) - 1]; } else { for ($i = 1; $i < sizeof($game_me1); $i++) { $game1 = $game1 . "_" . $game_me1[$i]; } $array1 = "all"; } } else { $array1 = "all"; } if (sizeof($game_me2) > 1) { if (is_numeric($game_me2[sizeof($game_me2) - 1])) { for ($i = 1; $i < sizeof($game_me2) - 1; $i++) { $game2 = $game2 . "_" . $game_me2[$i]; } $array2 = $game_me2[sizeof($game_me2) - 1]; } else { for ($i = 1; $i < sizeof($game_me2); $i++) { $game2 = $game2 . "_" . $game_me2[$i]; } $array2 = "all"; } } else { $array2 = "all"; } $timestamp1 = intval($time_page1 . substr(0, 10)); $timestamp2 = intval($time_page2 . substr(0, 10)); $page1 = substr($time_page1, 11, strlen($time_page1) - 18); $page2 = substr($time_page2, 11, strlen($time_page2) - 18); print '<table id="summary" class="summary" style="border-color:grey;font-size:16px; border:1px solid ;border-color:grey; margin:30px;">' . "\n"; print '<tr style="background: #D8D8DA url(http://yui.yahooapis.com/2.9.0/build/assets/skins/sam/sprite.png) repeat-x 0 0;text-align:center">'; print "<th align=left>{$rep_symbol}</th>"; print "<th {$vwbar} style ='text-align:center;' >{$page1} <br /> <div id='time1'>{$timestamp1}</div> </th>"; print "<th {$vwbar} style ='text-align:center;' >{$page2} <br /> <div id ='time2'>{$timestamp2}</div> </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>'; } echo "<tr><td>Game:</td><td style= 'text-align:right'>" . $game1 . "</td><td style= 'text-align:right'>" . $game2 . "</td><td></td><td></td></tr>"; echo "<tr><td>Array:</td><td style= 'text-align:right'>" . $array1 . "</td><td style= 'text-align:right'>" . $array2 . "</td><td></td></tr>"; print '</table>'; echo "<div id='function_table' </div>"; return; } 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="yellow" 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_path}/?" . 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></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"; }
public static function render($run_data, $symbol_info, $rep_symbol) { global $metrics; HeaderTemplate::prepareColumns($symbol_info, true); $columnsCount = HeaderTemplate::getColumnsCount(); ?> <div class="panel panel-default panel-functions"> <div class="panel-heading form-inline"> <h3 class="panel-title" style="display: inline-block;">Parent/Child report for <strong><?php ShortenNameHelper::render($rep_symbol, 45); ?> </strong> </h3> <?php SymbolSearchInputTemplate::render(); ?> <a class="btn btn-primary btn-sm" target="_blank" href="<?php echo static::callgraphUrl($rep_symbol); ?> "> <i class="fa fa-pie-chart"></i> View Callgraph </a> </div> <table class="table table-functions table-condensed table-bordered"> <?php HeaderTemplate::render(); ?> <tr class="no-hover"> <td><b><i><center>Current Function</center></i></b></td> <td colspan="<?php echo $columnsCount; ?> "></td> </tr> <?php print_function_info($symbol_info); ?> <tr> <?php $exclColumns = HeaderTemplate::getExclColumns(); foreach (HeaderTemplate::getColumns() as $column => $meta) { ?> <?php if ($column == 'fn') { ?> <td style='text-align:right;'>Exclusive Metrics for Current Function</td> <?php } elseif (isset($exclColumns['excl_' . $column])) { ?> <?php print_column_info($symbol_info, 'excl_' . $column, $exclColumns['excl_' . $column]); ?> <?php } else { ?> <td></td> <?php if (!empty($meta['percentage'])) { ?> <td></td> <?php } ?> <?php } ?> <?php } ?> </tr> <?php // list of callers/parent functions $results = array(); // $base_ct = $symbol_info["ct"]; $base_info = array(); 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) { $title = 'Parent functions'; if (count($results) > 1) { $title .= 's'; } print "<tr class=\"no-hover\"><td>"; print "<b><i><center>" . $title . "</center></i></b>"; print "</td><td colspan='{$columnsCount}'></td></tr>"; foreach ($results as $info) { print_function_info($info); } } // 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; $base_ct += $info["ct"]; } } usort($results, 'sort_cbk'); if (count($results)) { $title = 'Child function'; if (count($results) > 1) { $title .= 's'; } print "<tr class=\"no-hover\"><td>"; print "<b><i><center>" . $title . "</center></i></b>"; print "</td><td colspan='{$columnsCount}'></td></tr>"; foreach ($results as $info) { print_function_info($info); } } ?> </table> </div> <?php }
/** * 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($child, $q) !== false) { if (!isset($matches[$child])) { $matches[$child] = array('ct' => $info['ct'], 'wt' => $info['wt'], 'bcc' => (int) $info['bcc'], 'value' => $child); } else { $matches[$child]['ct'] += $info['ct']; $matches[$child]['wt'] += $info['wt']; } } } // sort it so the answers are in some reliable order... ksort($matches); return $matches; }