1 |
efrain |
1 |
<?php
|
|
|
2 |
// Copyright (c) 2009 Facebook
|
|
|
3 |
//
|
|
|
4 |
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
5 |
// you may not use this file except in compliance with the License.
|
|
|
6 |
// You may obtain a copy of the License at
|
|
|
7 |
//
|
|
|
8 |
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
9 |
//
|
|
|
10 |
// Unless required by applicable law or agreed to in writing, software
|
|
|
11 |
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
12 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
13 |
// See the License for the specific language governing permissions and
|
|
|
14 |
// limitations under the License.
|
|
|
15 |
//
|
|
|
16 |
|
|
|
17 |
/*
|
|
|
18 |
* This file contains callgraph image generation related XHProf utility
|
|
|
19 |
* functions
|
|
|
20 |
*
|
|
|
21 |
*/
|
|
|
22 |
|
|
|
23 |
// Supported ouput format
|
|
|
24 |
$xhprof_legal_image_types = array(
|
|
|
25 |
"jpg" => 1,
|
|
|
26 |
"gif" => 1,
|
|
|
27 |
"png" => 1,
|
|
|
28 |
"svg" => 1, // support scalable vector graphic
|
|
|
29 |
"ps" => 1,
|
|
|
30 |
);
|
|
|
31 |
|
|
|
32 |
/**
|
|
|
33 |
* Send an HTTP header with the response. You MUST use this function instead
|
|
|
34 |
* of header() so that we can debug header issues because they're virtually
|
|
|
35 |
* impossible to debug otherwise. If you try to commit header(), SVN will
|
|
|
36 |
* reject your commit.
|
|
|
37 |
*
|
|
|
38 |
* @param string HTTP header name, like 'Location'
|
|
|
39 |
* @param string HTTP header value, like 'http://www.example.com/'
|
|
|
40 |
*
|
|
|
41 |
*/
|
|
|
42 |
function xhprof_http_header($name, $value) {
|
|
|
43 |
|
|
|
44 |
if (!$name) {
|
|
|
45 |
xhprof_error('http_header usage');
|
|
|
46 |
return null;
|
|
|
47 |
}
|
|
|
48 |
|
|
|
49 |
if (!is_string($value)) {
|
|
|
50 |
xhprof_error('http_header value not a string');
|
|
|
51 |
}
|
|
|
52 |
|
|
|
53 |
header($name.': '.$value, true);
|
|
|
54 |
}
|
|
|
55 |
|
|
|
56 |
/**
|
|
|
57 |
* Genearte and send MIME header for the output image to client browser.
|
|
|
58 |
*
|
|
|
59 |
* @author cjiang
|
|
|
60 |
*/
|
|
|
61 |
function xhprof_generate_mime_header($type, $length) {
|
|
|
62 |
switch ($type) {
|
|
|
63 |
case 'jpg':
|
|
|
64 |
$mime = 'image/jpeg';
|
|
|
65 |
break;
|
|
|
66 |
case 'gif':
|
|
|
67 |
$mime = 'image/gif';
|
|
|
68 |
break;
|
|
|
69 |
case 'png':
|
|
|
70 |
$mime = 'image/png';
|
|
|
71 |
break;
|
|
|
72 |
case 'svg':
|
|
|
73 |
$mime = 'image/svg+xml'; // content type for scalable vector graphic
|
|
|
74 |
break;
|
|
|
75 |
case 'ps':
|
|
|
76 |
$mime = 'application/postscript';
|
|
|
77 |
default:
|
|
|
78 |
$mime = false;
|
|
|
79 |
}
|
|
|
80 |
|
|
|
81 |
if ($mime) {
|
|
|
82 |
xhprof_http_header('Content-type', $mime);
|
|
|
83 |
xhprof_http_header('Content-length', (string)$length);
|
|
|
84 |
}
|
|
|
85 |
}
|
|
|
86 |
|
|
|
87 |
/**
|
|
|
88 |
* Generate image according to DOT script. This function will spawn a process
|
|
|
89 |
* with "dot" command and pipe the "dot_script" to it and pipe out the
|
|
|
90 |
* generated image content.
|
|
|
91 |
*
|
|
|
92 |
* @param dot_script, string, the script for DOT to generate the image.
|
|
|
93 |
* @param type, one of the supported image types, see
|
|
|
94 |
* $xhprof_legal_image_types.
|
|
|
95 |
* @returns, binary content of the generated image on success. empty string on
|
|
|
96 |
* failure.
|
|
|
97 |
*
|
|
|
98 |
* @author cjiang
|
|
|
99 |
*/
|
|
|
100 |
function xhprof_generate_image_by_dot($dot_script, $type) {
|
|
|
101 |
$descriptorspec = array(
|
|
|
102 |
// stdin is a pipe that the child will read from
|
|
|
103 |
|
|
|
104 |
// stdout is a pipe that the child will write to
|
|
|
105 |
1 => array("pipe", "w"),
|
|
|
106 |
// stderr is a pipe that the child will write to
|
|
|
107 |
2 => array("pipe", "w")
|
|
|
108 |
);
|
|
|
109 |
|
|
|
110 |
// Start moodle modification: use $CFG->pathtodot for executing this.
|
|
|
111 |
// $cmd = " dot -T".$type;
|
|
|
112 |
global $CFG;
|
|
|
113 |
$cmd = (!empty($CFG->pathtodot) ? $CFG->pathtodot : 'dot') . ' -T' . $type;
|
|
|
114 |
// End moodle modification.
|
|
|
115 |
|
|
|
116 |
$process = proc_open( $cmd, $descriptorspec, $pipes, sys_get_temp_dir(), array( 'PATH' => getenv( 'PATH' ) ) );
|
|
|
117 |
if (is_resource($process)) {
|
|
|
118 |
fwrite($pipes[0], $dot_script);
|
|
|
119 |
fclose($pipes[0]);
|
|
|
120 |
|
|
|
121 |
$output = stream_get_contents($pipes[1]);
|
|
|
122 |
|
|
|
123 |
$err = stream_get_contents($pipes[2]);
|
|
|
124 |
if (!empty($err)) {
|
|
|
125 |
print "failed to execute cmd: \"$cmd\". stderr: `$err'\n";
|
|
|
126 |
exit;
|
|
|
127 |
}
|
|
|
128 |
|
|
|
129 |
fclose($pipes[2]);
|
|
|
130 |
fclose($pipes[1]);
|
|
|
131 |
proc_close($process);
|
|
|
132 |
return $output;
|
|
|
133 |
}
|
|
|
134 |
print "failed to execute cmd \"$cmd\"";
|
|
|
135 |
exit();
|
|
|
136 |
}
|
|
|
137 |
|
|
|
138 |
/*
|
|
|
139 |
* Get the children list of all nodes.
|
|
|
140 |
*/
|
|
|
141 |
function xhprof_get_children_table($raw_data) {
|
|
|
142 |
$children_table = array();
|
|
|
143 |
foreach ($raw_data as $parent_child => $info) {
|
|
|
144 |
list($parent, $child) = xhprof_parse_parent_child($parent_child);
|
|
|
145 |
if (!isset($children_table[$parent])) {
|
|
|
146 |
$children_table[$parent] = array($child);
|
|
|
147 |
} else {
|
|
|
148 |
$children_table[$parent][] = $child;
|
|
|
149 |
}
|
|
|
150 |
}
|
|
|
151 |
return $children_table;
|
|
|
152 |
}
|
|
|
153 |
|
|
|
154 |
/**
|
|
|
155 |
* Generate DOT script from the given raw phprof data.
|
|
|
156 |
*
|
|
|
157 |
* @param raw_data, phprof profile data.
|
|
|
158 |
* @param threshold, float, the threshold value [0,1). The functions in the
|
|
|
159 |
* raw_data whose exclusive wall times ratio are below the
|
|
|
160 |
* threshold will be filtered out and won't apprear in the
|
|
|
161 |
* generated image.
|
|
|
162 |
* @param page, string(optional), the root node name. This can be used to
|
|
|
163 |
* replace the 'main()' as the root node.
|
|
|
164 |
* @param func, string, the focus function.
|
|
|
165 |
* @param critical_path, bool, whether or not to display critical path with
|
|
|
166 |
* bold lines.
|
|
|
167 |
* @returns, string, the DOT script to generate image.
|
|
|
168 |
*
|
|
|
169 |
* @author cjiang
|
|
|
170 |
*/
|
|
|
171 |
function xhprof_generate_dot_script($raw_data, $threshold, $source, $page,
|
|
|
172 |
$func, $critical_path, $right=null,
|
|
|
173 |
$left=null) {
|
|
|
174 |
|
|
|
175 |
$max_width = 5;
|
|
|
176 |
$max_height = 3.5;
|
|
|
177 |
$max_fontsize = 35;
|
|
|
178 |
$max_sizing_ratio = 20;
|
|
|
179 |
|
|
|
180 |
$totals;
|
|
|
181 |
|
|
|
182 |
if ($left === null) {
|
|
|
183 |
// init_metrics($raw_data, null, null);
|
|
|
184 |
}
|
|
|
185 |
$sym_table = xhprof_compute_flat_info($raw_data, $totals);
|
|
|
186 |
|
|
|
187 |
if ($critical_path) {
|
|
|
188 |
$children_table = xhprof_get_children_table($raw_data);
|
|
|
189 |
$node = "main()";
|
|
|
190 |
$path = array();
|
|
|
191 |
$path_edges = array();
|
|
|
192 |
$visited = array();
|
|
|
193 |
while ($node) {
|
|
|
194 |
$visited[$node] = true;
|
|
|
195 |
if (isset($children_table[$node])) {
|
|
|
196 |
$max_child = null;
|
|
|
197 |
foreach ($children_table[$node] as $child) {
|
|
|
198 |
|
|
|
199 |
if (isset($visited[$child])) {
|
|
|
200 |
continue;
|
|
|
201 |
}
|
|
|
202 |
if ($max_child === null ||
|
|
|
203 |
abs($raw_data[xhprof_build_parent_child_key($node,
|
|
|
204 |
$child)]["wt"]) >
|
|
|
205 |
abs($raw_data[xhprof_build_parent_child_key($node,
|
|
|
206 |
$max_child)]["wt"])) {
|
|
|
207 |
$max_child = $child;
|
|
|
208 |
}
|
|
|
209 |
}
|
|
|
210 |
if ($max_child !== null) {
|
|
|
211 |
$path[$max_child] = true;
|
|
|
212 |
$path_edges[xhprof_build_parent_child_key($node, $max_child)] = true;
|
|
|
213 |
}
|
|
|
214 |
$node = $max_child;
|
|
|
215 |
} else {
|
|
|
216 |
$node = null;
|
|
|
217 |
}
|
|
|
218 |
}
|
|
|
219 |
}
|
|
|
220 |
|
|
|
221 |
// if it is a benchmark callgraph, we make the benchmarked function the root.
|
|
|
222 |
if ($source == "bm" && array_key_exists("main()", $sym_table)) {
|
|
|
223 |
$total_times = $sym_table["main()"]["ct"];
|
|
|
224 |
$remove_funcs = array("main()",
|
|
|
225 |
"hotprofiler_disable",
|
|
|
226 |
"call_user_func_array",
|
|
|
227 |
"xhprof_disable");
|
|
|
228 |
|
|
|
229 |
foreach ($remove_funcs as $cur_del_func) {
|
|
|
230 |
if (array_key_exists($cur_del_func, $sym_table) &&
|
|
|
231 |
$sym_table[$cur_del_func]["ct"] == $total_times) {
|
|
|
232 |
unset($sym_table[$cur_del_func]);
|
|
|
233 |
}
|
|
|
234 |
}
|
|
|
235 |
}
|
|
|
236 |
|
|
|
237 |
// use the function to filter out irrelevant functions.
|
|
|
238 |
if (!empty($func)) {
|
|
|
239 |
$interested_funcs = array();
|
|
|
240 |
foreach ($raw_data as $parent_child => $info) {
|
|
|
241 |
list($parent, $child) = xhprof_parse_parent_child($parent_child);
|
|
|
242 |
if ($parent == $func || $child == $func) {
|
|
|
243 |
$interested_funcs[$parent] = 1;
|
|
|
244 |
$interested_funcs[$child] = 1;
|
|
|
245 |
}
|
|
|
246 |
}
|
|
|
247 |
foreach ($sym_table as $symbol => $info) {
|
|
|
248 |
if (!array_key_exists($symbol, $interested_funcs)) {
|
|
|
249 |
unset($sym_table[$symbol]);
|
|
|
250 |
}
|
|
|
251 |
}
|
|
|
252 |
}
|
|
|
253 |
|
|
|
254 |
$result = "digraph call_graph {\n";
|
|
|
255 |
|
|
|
256 |
// Filter out functions whose exclusive time ratio is below threshold, and
|
|
|
257 |
// also assign a unique integer id for each function to be generated. In the
|
|
|
258 |
// meantime, find the function with the most exclusive time (potentially the
|
|
|
259 |
// performance bottleneck).
|
|
|
260 |
$cur_id = 0; $max_wt = 0;
|
|
|
261 |
foreach ($sym_table as $symbol => $info) {
|
|
|
262 |
if (empty($func) && abs($info["wt"] / $totals["wt"]) < $threshold) {
|
|
|
263 |
unset($sym_table[$symbol]);
|
|
|
264 |
continue;
|
|
|
265 |
}
|
|
|
266 |
if ($max_wt == 0 || $max_wt < abs($info["excl_wt"])) {
|
|
|
267 |
$max_wt = abs($info["excl_wt"]);
|
|
|
268 |
}
|
|
|
269 |
$sym_table[$symbol]["id"] = $cur_id;
|
|
|
270 |
$cur_id ++;
|
|
|
271 |
}
|
|
|
272 |
|
|
|
273 |
// Generate all nodes' information.
|
|
|
274 |
foreach ($sym_table as $symbol => $info) {
|
|
|
275 |
if ($info["excl_wt"] == 0) {
|
|
|
276 |
$sizing_factor = $max_sizing_ratio;
|
|
|
277 |
} else {
|
|
|
278 |
$sizing_factor = $max_wt / abs($info["excl_wt"]) ;
|
|
|
279 |
if ($sizing_factor > $max_sizing_ratio) {
|
|
|
280 |
$sizing_factor = $max_sizing_ratio;
|
|
|
281 |
}
|
|
|
282 |
}
|
|
|
283 |
$fillcolor = (($sizing_factor < 1.5) ?
|
|
|
284 |
", style=filled, fillcolor=red" : "");
|
|
|
285 |
|
|
|
286 |
if ($critical_path) {
|
|
|
287 |
// highlight nodes along critical path.
|
|
|
288 |
if (!$fillcolor && array_key_exists($symbol, $path)) {
|
|
|
289 |
$fillcolor = ", style=filled, fillcolor=yellow";
|
|
|
290 |
}
|
|
|
291 |
}
|
|
|
292 |
|
|
|
293 |
$fontsize = ", fontsize="
|
|
|
294 |
.(int)($max_fontsize / (($sizing_factor - 1) / 10 + 1));
|
|
|
295 |
|
|
|
296 |
$width = ", width=".sprintf("%.1f", $max_width / $sizing_factor);
|
|
|
297 |
$height = ", height=".sprintf("%.1f", $max_height / $sizing_factor);
|
|
|
298 |
|
|
|
299 |
if ($symbol == "main()") {
|
|
|
300 |
$shape = "octagon";
|
|
|
301 |
$name = "Total: ".($totals["wt"] / 1000.0)." ms\\n";
|
|
|
302 |
$name .= addslashes(isset($page) ? $page : $symbol);
|
|
|
303 |
} else {
|
|
|
304 |
$shape = "box";
|
|
|
305 |
$name = addslashes($symbol)."\\nInc: ". sprintf("%.3f",$info["wt"] / 1000) .
|
|
|
306 |
" ms (" . sprintf("%.1f%%", 100 * $info["wt"] / $totals["wt"]).")";
|
|
|
307 |
}
|
|
|
308 |
if ($left === null) {
|
|
|
309 |
$label = ", label=\"".$name."\\nExcl: "
|
|
|
310 |
.(sprintf("%.3f",$info["excl_wt"] / 1000.0))." ms ("
|
|
|
311 |
.sprintf("%.1f%%", 100 * $info["excl_wt"] / $totals["wt"])
|
|
|
312 |
. ")\\n".$info["ct"]." total calls\"";
|
|
|
313 |
} else {
|
|
|
314 |
if (isset($left[$symbol]) && isset($right[$symbol])) {
|
|
|
315 |
$label = ", label=\"".addslashes($symbol).
|
|
|
316 |
"\\nInc: ".(sprintf("%.3f",$left[$symbol]["wt"] / 1000.0))
|
|
|
317 |
." ms - "
|
|
|
318 |
.(sprintf("%.3f",$right[$symbol]["wt"] / 1000.0))." ms = "
|
|
|
319 |
.(sprintf("%.3f",$info["wt"] / 1000.0))." ms".
|
|
|
320 |
"\\nExcl: "
|
|
|
321 |
.(sprintf("%.3f",$left[$symbol]["excl_wt"] / 1000.0))
|
|
|
322 |
." ms - ".(sprintf("%.3f",$right[$symbol]["excl_wt"] / 1000.0))
|
|
|
323 |
." ms = ".(sprintf("%.3f",$info["excl_wt"] / 1000.0))." ms".
|
|
|
324 |
"\\nCalls: ".(sprintf("%.3f",$left[$symbol]["ct"]))." - "
|
|
|
325 |
.(sprintf("%.3f",$right[$symbol]["ct"]))." = "
|
|
|
326 |
.(sprintf("%.3f",$info["ct"]))."\"";
|
|
|
327 |
} else if (isset($left[$symbol])) {
|
|
|
328 |
$label = ", label=\"".addslashes($symbol).
|
|
|
329 |
"\\nInc: ".(sprintf("%.3f",$left[$symbol]["wt"] / 1000.0))
|
|
|
330 |
." ms - 0 ms = ".(sprintf("%.3f",$info["wt"] / 1000.0))
|
|
|
331 |
." ms"."\\nExcl: "
|
|
|
332 |
.(sprintf("%.3f",$left[$symbol]["excl_wt"] / 1000.0))
|
|
|
333 |
." ms - 0 ms = "
|
|
|
334 |
.(sprintf("%.3f",$info["excl_wt"] / 1000.0))." ms".
|
|
|
335 |
"\\nCalls: ".(sprintf("%.3f",$left[$symbol]["ct"]))." - 0 = "
|
|
|
336 |
.(sprintf("%.3f",$info["ct"]))."\"";
|
|
|
337 |
} else {
|
|
|
338 |
$label = ", label=\"".addslashes($symbol).
|
|
|
339 |
"\\nInc: 0 ms - "
|
|
|
340 |
.(sprintf("%.3f",$right[$symbol]["wt"] / 1000.0))
|
|
|
341 |
." ms = ".(sprintf("%.3f",$info["wt"] / 1000.0))." ms".
|
|
|
342 |
"\\nExcl: 0 ms - "
|
|
|
343 |
.(sprintf("%.3f",$right[$symbol]["excl_wt"] / 1000.0))
|
|
|
344 |
." ms = ".(sprintf("%.3f",$info["excl_wt"] / 1000.0))." ms".
|
|
|
345 |
"\\nCalls: 0 - ".(sprintf("%.3f",$right[$symbol]["ct"]))
|
|
|
346 |
." = ".(sprintf("%.3f",$info["ct"]))."\"";
|
|
|
347 |
}
|
|
|
348 |
}
|
|
|
349 |
$result .= "N" . $sym_table[$symbol]["id"];
|
|
|
350 |
$result .= "[shape=$shape ".$label.$width
|
|
|
351 |
.$height.$fontsize.$fillcolor."];\n";
|
|
|
352 |
}
|
|
|
353 |
|
|
|
354 |
// Generate all the edges' information.
|
|
|
355 |
foreach ($raw_data as $parent_child => $info) {
|
|
|
356 |
list($parent, $child) = xhprof_parse_parent_child($parent_child);
|
|
|
357 |
|
|
|
358 |
if (isset($sym_table[$parent]) && isset($sym_table[$child]) &&
|
|
|
359 |
(empty($func) ||
|
|
|
360 |
(!empty($func) && ($parent == $func || $child == $func)))) {
|
|
|
361 |
|
|
|
362 |
$label = $info["ct"] == 1 ? $info["ct"]." call" : $info["ct"]." calls";
|
|
|
363 |
|
|
|
364 |
$headlabel = $sym_table[$child]["wt"] > 0 ?
|
|
|
365 |
sprintf("%.1f%%", 100 * $info["wt"]
|
|
|
366 |
/ $sym_table[$child]["wt"])
|
|
|
367 |
: "0.0%";
|
|
|
368 |
|
|
|
369 |
$taillabel = ($sym_table[$parent]["wt"] > 0) ?
|
|
|
370 |
sprintf("%.1f%%",
|
|
|
371 |
100 * $info["wt"] /
|
|
|
372 |
($sym_table[$parent]["wt"] - $sym_table["$parent"]["excl_wt"]))
|
|
|
373 |
: "0.0%";
|
|
|
374 |
|
|
|
375 |
$linewidth = 1;
|
|
|
376 |
$arrow_size = 1;
|
|
|
377 |
|
|
|
378 |
if ($critical_path &&
|
|
|
379 |
isset($path_edges[xhprof_build_parent_child_key($parent, $child)])) {
|
|
|
380 |
$linewidth = 10; $arrow_size = 2;
|
|
|
381 |
}
|
|
|
382 |
|
|
|
383 |
$result .= "N" . $sym_table[$parent]["id"] . " -> N"
|
|
|
384 |
. $sym_table[$child]["id"];
|
|
|
385 |
$result .= "[arrowsize=$arrow_size, color=grey, style=\"setlinewidth($linewidth)\","
|
|
|
386 |
." label=\""
|
|
|
387 |
.$label."\", headlabel=\"".$headlabel
|
|
|
388 |
."\", taillabel=\"".$taillabel."\" ]";
|
|
|
389 |
$result .= ";\n";
|
|
|
390 |
|
|
|
391 |
}
|
|
|
392 |
}
|
|
|
393 |
$result = $result . "\n}";
|
|
|
394 |
|
|
|
395 |
return $result;
|
|
|
396 |
}
|
|
|
397 |
|
|
|
398 |
function xhprof_render_diff_image($xhprof_runs_impl, $run1, $run2,
|
|
|
399 |
$type, $threshold, $source) {
|
|
|
400 |
$total1;
|
|
|
401 |
$total2;
|
|
|
402 |
|
|
|
403 |
$raw_data1 = $xhprof_runs_impl->get_run($run1, $source, $desc_unused);
|
|
|
404 |
$raw_data2 = $xhprof_runs_impl->get_run($run2, $source, $desc_unused);
|
|
|
405 |
|
|
|
406 |
// init_metrics($raw_data1, null, null);
|
|
|
407 |
$children_table1 = xhprof_get_children_table($raw_data1);
|
|
|
408 |
$children_table2 = xhprof_get_children_table($raw_data2);
|
|
|
409 |
$symbol_tab1 = xhprof_compute_flat_info($raw_data1, $total1);
|
|
|
410 |
$symbol_tab2 = xhprof_compute_flat_info($raw_data2, $total2);
|
|
|
411 |
$run_delta = xhprof_compute_diff($raw_data1, $raw_data2);
|
|
|
412 |
$script = xhprof_generate_dot_script($run_delta, $threshold, $source,
|
|
|
413 |
null, null, true,
|
|
|
414 |
$symbol_tab1, $symbol_tab2);
|
|
|
415 |
$content = xhprof_generate_image_by_dot($script, $type);
|
|
|
416 |
|
|
|
417 |
xhprof_generate_mime_header($type, strlen($content));
|
|
|
418 |
echo $content;
|
|
|
419 |
}
|
|
|
420 |
|
|
|
421 |
/**
|
|
|
422 |
* Generate image content from phprof run id.
|
|
|
423 |
*
|
|
|
424 |
* @param object $xhprof_runs_impl An object that implements
|
|
|
425 |
* the iXHProfRuns interface
|
|
|
426 |
* @param run_id, integer, the unique id for the phprof run, this is the
|
|
|
427 |
* primary key for phprof database table.
|
|
|
428 |
* @param type, string, one of the supported image types. See also
|
|
|
429 |
* $xhprof_legal_image_types.
|
|
|
430 |
* @param threshold, float, the threshold value [0,1). The functions in the
|
|
|
431 |
* raw_data whose exclusive wall times ratio are below the
|
|
|
432 |
* threshold will be filtered out and won't apprear in the
|
|
|
433 |
* generated image.
|
|
|
434 |
* @param func, string, the focus function.
|
|
|
435 |
* @returns, string, the DOT script to generate image.
|
|
|
436 |
*
|
|
|
437 |
* @author cjiang
|
|
|
438 |
*/
|
|
|
439 |
function xhprof_get_content_by_run($xhprof_runs_impl, $run_id, $type,
|
|
|
440 |
$threshold, $func, $source,
|
|
|
441 |
$critical_path) {
|
|
|
442 |
if (!$run_id)
|
|
|
443 |
return "";
|
|
|
444 |
|
|
|
445 |
$raw_data = $xhprof_runs_impl->get_run($run_id, $source, $description);
|
|
|
446 |
if (!$raw_data) {
|
|
|
447 |
xhprof_error("Raw data is empty");
|
|
|
448 |
return "";
|
|
|
449 |
}
|
|
|
450 |
|
|
|
451 |
$script = xhprof_generate_dot_script($raw_data, $threshold, $source,
|
|
|
452 |
$description, $func, $critical_path);
|
|
|
453 |
|
|
|
454 |
$content = xhprof_generate_image_by_dot($script, $type);
|
|
|
455 |
return $content;
|
|
|
456 |
}
|
|
|
457 |
|
|
|
458 |
/**
|
|
|
459 |
* Generate image from phprof run id and send it to client.
|
|
|
460 |
*
|
|
|
461 |
* @param object $xhprof_runs_impl An object that implements
|
|
|
462 |
* the iXHProfRuns interface
|
|
|
463 |
* @param run_id, integer, the unique id for the phprof run, this is the
|
|
|
464 |
* primary key for phprof database table.
|
|
|
465 |
* @param type, string, one of the supported image types. See also
|
|
|
466 |
* $xhprof_legal_image_types.
|
|
|
467 |
* @param threshold, float, the threshold value [0,1). The functions in the
|
|
|
468 |
* raw_data whose exclusive wall times ratio are below the
|
|
|
469 |
* threshold will be filtered out and won't apprear in the
|
|
|
470 |
* generated image.
|
|
|
471 |
* @param func, string, the focus function.
|
|
|
472 |
* @param bool, does this run correspond to a PHProfLive run or a dev run?
|
|
|
473 |
* @author cjiang
|
|
|
474 |
*/
|
|
|
475 |
function xhprof_render_image($xhprof_runs_impl, $run_id, $type, $threshold,
|
|
|
476 |
$func, $source, $critical_path) {
|
|
|
477 |
|
|
|
478 |
$content = xhprof_get_content_by_run($xhprof_runs_impl, $run_id, $type,
|
|
|
479 |
$threshold,
|
|
|
480 |
$func, $source, $critical_path);
|
|
|
481 |
if (!$content) {
|
|
|
482 |
print "Error: either we can not find profile data for run_id ".$run_id
|
|
|
483 |
." or the threshold ".$threshold." is too small or you do not"
|
|
|
484 |
." have 'dot' image generation utility installed.";
|
|
|
485 |
exit();
|
|
|
486 |
}
|
|
|
487 |
|
|
|
488 |
xhprof_generate_mime_header($type, strlen($content));
|
|
|
489 |
echo $content;
|
|
|
490 |
}
|