Example #1
0
/**
 * Given a partial query string $q return matching function names in
 * specified uprofiler run. This is used for the type ahead function
 * selector.
 *
 * @author Kannan
 */
function uprofiler_get_matching_functions($q, $uprofiler_data)
{
    $matches = array();
    foreach ($uprofiler_data as $parent_child => $info) {
        list($parent, $child) = uprofiler_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;
}
Example #2
0
/**
 * 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 = uprofiler_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 = uprofiler_array_unset(uprofiler_array_unset($url_params, 'run1'), 'run2');
        $href1 = "{$base_path}?" . http_build_query(uprofiler_array_set($base_url_params, 'run', $run1));
        $href2 = "{$base_path}?" . http_build_query(uprofiler_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(uprofiler_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}/?" . http_build_query(uprofiler_array_set($url_params, 'sort', $stat));
            $header = uprofiler_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) = uprofiler_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) = uprofiler_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: uprofiler_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";
}
Example #3
0
/**
 * 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 uprofiler_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 = array();
    if ($left === null) {
        // init_metrics($raw_data, null, null);
    }
    $sym_table = uprofiler_compute_flat_info($raw_data, $totals);
    if ($critical_path) {
        $children_table = uprofiler_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[uprofiler_build_parent_child_key($node, $child)]["wt"]) > abs($raw_data[uprofiler_build_parent_child_key($node, $max_child)]["wt"])) {
                        $max_child = $child;
                    }
                }
                if ($max_child !== null) {
                    $path[$max_child] = true;
                    $path_edges[uprofiler_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", "uprofiler_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) = uprofiler_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) = uprofiler_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[uprofiler_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;
}