예제 #1
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 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;
}
예제 #2
0
function xhprof_prune_run($raw_data, $prune_percent)
{
    $main_info = $raw_data["main()"];
    if (empty($main_info)) {
        xhprof_error("XHProf: main() missing in raw data");
        return false;
    }
    // raw data should contain either wall time or samples information...
    if (isset($main_info["wt"])) {
        $prune_metric = "wt";
    } else {
        if (isset($main_info["samples"])) {
            $prune_metric = "samples";
        } else {
            xhprof_error("XHProf: for main() we must have either wt " . "or samples attribute set");
            return false;
        }
    }
    // determine the metrics present in the raw data..
    $metrics = array();
    foreach ($main_info as $metric => $val) {
        if (isset($val)) {
            $metrics[] = $metric;
        }
    }
    $prune_threshold = $main_info[$prune_metric] * $prune_percent / 100.0;
    init_metrics($raw_data, null, null, false);
    $flat_info = xhprof_compute_inclusive_times($raw_data);
    foreach ($raw_data as $parent_child => $info) {
        list($parent, $child) = xhprof_parse_parent_child($parent_child);
        // is this child's overall total from all parents less than threshold?
        if ($flat_info[$child][$prune_metric] < $prune_threshold) {
            unset($raw_data[$parent_child]);
            // prune the edge
        } else {
            if ($parent && $parent != "__pruned__()" && $flat_info[$parent][$prune_metric] < $prune_threshold) {
                // Parent's overall inclusive metric is less than a threshold.
                // All edges to the parent node will get nuked, and this child will
                // be a dangling child.
                // So instead change its parent to be a special function __pruned__().
                $pruned_edge = xhprof_build_parent_child_key("__pruned__()", $child);
                if (isset($raw_data[$pruned_edge])) {
                    foreach ($metrics as $metric) {
                        $raw_data[$pruned_edge][$metric] += $raw_data[$parent_child][$metric];
                    }
                } else {
                    $raw_data[$pruned_edge] = $raw_data[$parent_child];
                }
                unset($raw_data[$parent_child]);
                // prune the edge
            }
        }
    }
    return $raw_data;
}