Merge branch 'wip-MDL-60313-master' of git://github.com/marinaglancy/moodle
[moodle.git] / lib / xhprof / xhprof_lib / display / xhprof.php
CommitLineData
6af80cae
EL
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// XHProf: A Hierarchical Profiler for PHP
19//
20// XHProf has two components:
21//
22// * This module is the UI/reporting component, used
23// for viewing results of XHProf runs from a browser.
24//
25// * Data collection component: This is implemented
26// as a PHP extension (XHProf).
27//
28// @author Kannan Muthukkaruppan
29//
30
31if (!isset($GLOBALS['XHPROF_LIB_ROOT'])) {
32 // by default, the parent directory is XHPROF lib root
33 $GLOBALS['XHPROF_LIB_ROOT'] = realpath(dirname(__FILE__) . '/..');
34}
35
36require_once $GLOBALS['XHPROF_LIB_ROOT'].'/utils/xhprof_lib.php';
37require_once $GLOBALS['XHPROF_LIB_ROOT'].'/utils/callgraph_utils.php';
38require_once $GLOBALS['XHPROF_LIB_ROOT'].'/utils/xhprof_runs.php';
39
40
41/**
42 * Our coding convention disallows relative paths in hrefs.
43 * Get the base URL path from the SCRIPT_NAME.
44 */
ffb41a8f 45$base_path = rtrim(dirname($_SERVER['SCRIPT_NAME']), '/\\');
6af80cae
EL
46
47
48/**
49 * Generate references to required stylesheets & javascript.
50 *
51 * If the calling script (such as index.php) resides in
52 * a different location that than 'xhprof_html' directory the
53 * caller must provide the URL path to 'xhprof_html' directory
54 * so that the correct location of the style sheets/javascript
55 * can be specified in the generated HTML.
56 *
57 */
58function xhprof_include_js_css($ui_dir_url_path = null) {
59
60 if (empty($ui_dir_url_path)) {
ffb41a8f 61 $ui_dir_url_path = rtrim(dirname($_SERVER['SCRIPT_NAME']), '/\\');
6af80cae
EL
62 }
63
64 // style sheets
65 echo "<link href='$ui_dir_url_path/css/xhprof.css' rel='stylesheet' ".
66 " type='text/css' />";
67 echo "<link href='$ui_dir_url_path/jquery/jquery.tooltip.css' ".
68 " rel='stylesheet' type='text/css' />";
69 echo "<link href='$ui_dir_url_path/jquery/jquery.autocomplete.css' ".
70 " rel='stylesheet' type='text/css' />";
71
72 // javascript
73 echo "<script src='$ui_dir_url_path/jquery/jquery-1.2.6.js'>".
74 "</script>";
75 echo "<script src='$ui_dir_url_path/jquery/jquery.tooltip.js'>".
76 "</script>";
77 echo "<script src='$ui_dir_url_path/jquery/jquery.autocomplete.js'>"
78 ."</script>";
79 echo "<script src='$ui_dir_url_path/js/xhprof_report.js'></script>";
80}
81
82
83/*
84 * Formats call counts for XHProf reports.
85 *
86 * Description:
87 * Call counts in single-run reports are integer values.
88 * However, call counts for aggregated reports can be
89 * fractional. This function will print integer values
90 * without decimal point, but with commas etc.
91 *
92 * 4000 ==> 4,000
93 *
94 * It'll round fractional values to decimal precision of 3
95 * 4000.1212 ==> 4,000.121
96 * 4000.0001 ==> 4,000
97 *
98 */
99function xhprof_count_format($num) {
100 $num = round($num, 3);
101 if (round($num) == $num) {
102 return number_format($num);
103 } else {
104 return number_format($num, 3);
105 }
106}
107
108function xhprof_percent_format($s, $precision = 1) {
109 return sprintf('%.'.$precision.'f%%', 100 * $s);
110}
111
112/**
113 * Implodes the text for a bunch of actions (such as links, forms,
114 * into a HTML list and returns the text.
115 */
116function xhprof_render_actions($actions) {
117 $out = array();
118
119 if (count($actions)) {
120 $out[] = '<ul class="xhprof_actions">';
121 foreach ($actions as $action) {
122 $out[] = '<li>'.$action.'</li>';
123 }
124 $out[] = '</ul>';
125 }
126
127 return implode('', $out);
128}
129
130
131/**
132 * @param html-str $content the text/image/innerhtml/whatever for the link
133 * @param raw-str $href
134 * @param raw-str $class
135 * @param raw-str $id
136 * @param raw-str $title
137 * @param raw-str $target
138 * @param raw-str $onclick
139 * @param raw-str $style
140 * @param raw-str $access
141 * @param raw-str $onmouseover
142 * @param raw-str $onmouseout
143 * @param raw-str $onmousedown
144 * @param raw-str $dir
145 * @param raw-str $rel
146 */
147function xhprof_render_link($content, $href, $class='', $id='', $title='',
148 $target='',
149 $onclick='', $style='', $access='', $onmouseover='',
150 $onmouseout='', $onmousedown='') {
151
152 if (!$content) {
153 return '';
154 }
155
156 if ($href) {
157 $link = '<a href="' . ($href) . '"';
158 } else {
159 $link = '<span';
160 }
161
162 if ($class) {
163 $link .= ' class="' . ($class) . '"';
164 }
165 if ($id) {
166 $link .= ' id="' . ($id) . '"';
167 }
168 if ($title) {
169 $link .= ' title="' . ($title) . '"';
170 }
171 if ($target) {
172 $link .= ' target="' . ($target) . '"';
173 }
174 if ($onclick && $href) {
175 $link .= ' onclick="' . ($onclick) . '"';
176 }
177 if ($style && $href) {
178 $link .= ' style="' . ($style) . '"';
179 }
180 if ($access && $href) {
181 $link .= ' accesskey="' . ($access) . '"';
182 }
183 if ($onmouseover) {
184 $link .= ' onmouseover="' . ($onmouseover) . '"';
185 }
186 if ($onmouseout) {
187 $link .= ' onmouseout="' . ($onmouseout) . '"';
188 }
189 if ($onmousedown) {
190 $link .= ' onmousedown="' . ($onmousedown) . '"';
191 }
192
193 $link .= '>';
194 $link .= $content;
195 if ($href) {
196 $link .= '</a>';
197 } else {
198 $link .= '</span>';
199 }
200
201 return $link;
202}
203
204
205// default column to sort on -- wall time
206$sort_col = "wt";
207
208// default is "single run" report
209$diff_mode = false;
210
211// call count data present?
212$display_calls = true;
213
214// The following column headers are sortable
215$sortable_columns = array("fn" => 1,
216 "ct" => 1,
217 "wt" => 1,
218 "excl_wt" => 1,
219 "ut" => 1,
220 "excl_ut" => 1,
221 "st" => 1,
222 "excl_st" => 1,
223 "mu" => 1,
224 "excl_mu" => 1,
225 "pmu" => 1,
226 "excl_pmu" => 1,
227 "cpu" => 1,
228 "excl_cpu" => 1,
229 "samples" => 1,
230 "excl_samples" => 1
231 );
232
233// Textual descriptions for column headers in "single run" mode
234$descriptions = array(
235 "fn" => "Function Name",
236 "ct" => "Calls",
237 "Calls%" => "Calls%",
238
239 "wt" => "Incl. Wall Time<br>(microsec)",
240 "IWall%" => "IWall%",
241 "excl_wt" => "Excl. Wall Time<br>(microsec)",
242 "EWall%" => "EWall%",
243
244 "ut" => "Incl. User<br>(microsecs)",
245 "IUser%" => "IUser%",
246 "excl_ut" => "Excl. User<br>(microsec)",
247 "EUser%" => "EUser%",
248
249 "st" => "Incl. Sys <br>(microsec)",
250 "ISys%" => "ISys%",
251 "excl_st" => "Excl. Sys <br>(microsec)",
252 "ESys%" => "ESys%",
253
254 "cpu" => "Incl. CPU<br>(microsecs)",
255 "ICpu%" => "ICpu%",
256 "excl_cpu" => "Excl. CPU<br>(microsec)",
257 "ECpu%" => "ECPU%",
258
259 "mu" => "Incl.<br>MemUse<br>(bytes)",
260 "IMUse%" => "IMemUse%",
261 "excl_mu" => "Excl.<br>MemUse<br>(bytes)",
262 "EMUse%" => "EMemUse%",
263
264 "pmu" => "Incl.<br> PeakMemUse<br>(bytes)",
265 "IPMUse%" => "IPeakMemUse%",
266 "excl_pmu" => "Excl.<br>PeakMemUse<br>(bytes)",
267 "EPMUse%" => "EPeakMemUse%",
268
269 "samples" => "Incl. Samples",
270 "ISamples%" => "ISamples%",
271 "excl_samples" => "Excl. Samples",
272 "ESamples%" => "ESamples%",
273 );
274
275// Formatting Callback Functions...
276$format_cbk = array(
277 "fn" => "",
278 "ct" => "xhprof_count_format",
279 "Calls%" => "xhprof_percent_format",
280
281 "wt" => "number_format",
282 "IWall%" => "xhprof_percent_format",
283 "excl_wt" => "number_format",
284 "EWall%" => "xhprof_percent_format",
285
286 "ut" => "number_format",
287 "IUser%" => "xhprof_percent_format",
288 "excl_ut" => "number_format",
289 "EUser%" => "xhprof_percent_format",
290
291 "st" => "number_format",
292 "ISys%" => "xhprof_percent_format",
293 "excl_st" => "number_format",
294 "ESys%" => "xhprof_percent_format",
295
296 "cpu" => "number_format",
297 "ICpu%" => "xhprof_percent_format",
298 "excl_cpu" => "number_format",
299 "ECpu%" => "xhprof_percent_format",
300
301 "mu" => "number_format",
302 "IMUse%" => "xhprof_percent_format",
303 "excl_mu" => "number_format",
304 "EMUse%" => "xhprof_percent_format",
305
306 "pmu" => "number_format",
307 "IPMUse%" => "xhprof_percent_format",
308 "excl_pmu" => "number_format",
309 "EPMUse%" => "xhprof_percent_format",
310
311 "samples" => "number_format",
312 "ISamples%" => "xhprof_percent_format",
313 "excl_samples" => "number_format",
314 "ESamples%" => "xhprof_percent_format",
315 );
316
317
318// Textual descriptions for column headers in "diff" mode
319$diff_descriptions = array(
320 "fn" => "Function Name",
321 "ct" => "Calls Diff",
322 "Calls%" => "Calls<br>Diff%",
323
324 "wt" => "Incl. Wall<br>Diff<br>(microsec)",
325 "IWall%" => "IWall<br> Diff%",
326 "excl_wt" => "Excl. Wall<br>Diff<br>(microsec)",
327 "EWall%" => "EWall<br>Diff%",
328
329 "ut" => "Incl. User Diff<br>(microsec)",
330 "IUser%" => "IUser<br>Diff%",
331 "excl_ut" => "Excl. User<br>Diff<br>(microsec)",
332 "EUser%" => "EUser<br>Diff%",
333
334 "cpu" => "Incl. CPU Diff<br>(microsec)",
335 "ICpu%" => "ICpu<br>Diff%",
336 "excl_cpu" => "Excl. CPU<br>Diff<br>(microsec)",
337 "ECpu%" => "ECpu<br>Diff%",
338
339 "st" => "Incl. Sys Diff<br>(microsec)",
340 "ISys%" => "ISys<br>Diff%",
341 "excl_st" => "Excl. Sys Diff<br>(microsec)",
342 "ESys%" => "ESys<br>Diff%",
343
344 "mu" => "Incl.<br>MemUse<br>Diff<br>(bytes)",
345 "IMUse%" => "IMemUse<br>Diff%",
346 "excl_mu" => "Excl.<br>MemUse<br>Diff<br>(bytes)",
347 "EMUse%" => "EMemUse<br>Diff%",
348
349 "pmu" => "Incl.<br> PeakMemUse<br>Diff<br>(bytes)",
350 "IPMUse%" => "IPeakMemUse<br>Diff%",
351 "excl_pmu" => "Excl.<br>PeakMemUse<br>Diff<br>(bytes)",
352 "EPMUse%" => "EPeakMemUse<br>Diff%",
353
354 "samples" => "Incl. Samples Diff",
355 "ISamples%" => "ISamples Diff%",
356 "excl_samples" => "Excl. Samples Diff",
357 "ESamples%" => "ESamples Diff%",
358 );
359
360// columns that'll be displayed in a top-level report
361$stats = array();
362
363// columns that'll be displayed in a function's parent/child report
364$pc_stats = array();
365
366// Various total counts
367$totals = 0;
368$totals_1 = 0;
369$totals_2 = 0;
370
371/*
372 * The subset of $possible_metrics that is present in the raw profile data.
373 */
374$metrics = null;
375
376/**
377 * Callback comparison operator (passed to usort() for sorting array of
378 * tuples) that compares array elements based on the sort column
379 * specified in $sort_col (global parameter).
380 *
381 * @author Kannan
382 */
383function sort_cbk($a, $b) {
384 global $sort_col;
385 global $diff_mode;
386
387 if ($sort_col == "fn") {
388
389 // case insensitive ascending sort for function names
390 $left = strtoupper($a["fn"]);
391 $right = strtoupper($b["fn"]);
392
393 if ($left == $right)
394 return 0;
395 return ($left < $right) ? -1 : 1;
396
397 } else {
398
399 // descending sort for all others
400 $left = $a[$sort_col];
401 $right = $b[$sort_col];
402
403 // if diff mode, sort by absolute value of regression/improvement
404 if ($diff_mode) {
405 $left = abs($left);
406 $right = abs($right);
407 }
408
409 if ($left == $right)
410 return 0;
411 return ($left > $right) ? -1 : 1;
412 }
413}
414
415/**
416 * Get the appropriate description for a statistic
417 * (depending upon whether we are in diff report mode
418 * or single run report mode).
419 *
420 * @author Kannan
421 */
422function stat_description($stat) {
423 global $descriptions;
424 global $diff_descriptions;
425 global $diff_mode;
426
427 if ($diff_mode) {
428 return $diff_descriptions[$stat];
429 } else {
430 return $descriptions[$stat];
431 }
432}
433
434
435/**
436 * Analyze raw data & generate the profiler report
437 * (common for both single run mode and diff mode).
438 *
439 * @author: Kannan
440 */
441function profiler_report ($url_params,
442 $rep_symbol,
443 $sort,
444 $run1,
445 $run1_desc,
446 $run1_data,
447 $run2 = 0,
448 $run2_desc = "",
449 $run2_data = array()) {
450 global $totals;
451 global $totals_1;
452 global $totals_2;
453 global $stats;
454 global $pc_stats;
455 global $diff_mode;
456 global $base_path;
457
458 // if we are reporting on a specific function, we can trim down
459 // the report(s) to just stuff that is relevant to this function.
460 // That way compute_flat_info()/compute_diff() etc. do not have
461 // to needlessly work hard on churning irrelevant data.
462 if (!empty($rep_symbol)) {
463 $run1_data = xhprof_trim_run($run1_data, array($rep_symbol));
464 if ($diff_mode) {
465 $run2_data = xhprof_trim_run($run2_data, array($rep_symbol));
466 }
467 }
468
469 if ($diff_mode) {
470 $run_delta = xhprof_compute_diff($run1_data, $run2_data);
471 $symbol_tab = xhprof_compute_flat_info($run_delta, $totals);
472 $symbol_tab1 = xhprof_compute_flat_info($run1_data, $totals_1);
473 $symbol_tab2 = xhprof_compute_flat_info($run2_data, $totals_2);
474 } else {
475 $symbol_tab = xhprof_compute_flat_info($run1_data, $totals);
476 }
477
478 $run1_txt = sprintf("<b>Run #%s:</b> %s",
479 $run1, $run1_desc);
480
481 $base_url_params = xhprof_array_unset(xhprof_array_unset($url_params,
482 'symbol'),
483 'all');
484
485 $top_link_query_string = "$base_path/?" . http_build_query($base_url_params);
486
487 if ($diff_mode) {
488 $diff_text = "Diff";
489 $base_url_params = xhprof_array_unset($base_url_params, 'run1');
490 $base_url_params = xhprof_array_unset($base_url_params, 'run2');
491 $run1_link = xhprof_render_link('View Run #' . $run1,
492 "$base_path/?" .
493 http_build_query(xhprof_array_set($base_url_params,
494 'run',
495 $run1)));
496 $run2_txt = sprintf("<b>Run #%s:</b> %s",
497 $run2, $run2_desc);
498
499 $run2_link = xhprof_render_link('View Run #' . $run2,
500 "$base_path/?" .
501 http_build_query(xhprof_array_set($base_url_params,
502 'run',
503 $run2)));
504 } else {
505 $diff_text = "Run";
506 }
507
508 // set up the action links for operations that can be done on this report
509 $links = array();
510 $links [] = xhprof_render_link("View Top Level $diff_text Report",
511 $top_link_query_string);
512
513 if ($diff_mode) {
514 $inverted_params = $url_params;
515 $inverted_params['run1'] = $url_params['run2'];
516 $inverted_params['run2'] = $url_params['run1'];
517
518 // view the different runs or invert the current diff
519 $links [] = $run1_link;
520 $links [] = $run2_link;
521 $links [] = xhprof_render_link('Invert ' . $diff_text . ' Report',
522 "$base_path/?".
523 http_build_query($inverted_params));
524 }
525
526 // lookup function typeahead form
527 $links [] = '<input class="function_typeahead" ' .
528 ' type="input" size="40" maxlength="100" />';
529
530 echo xhprof_render_actions($links);
531
532
533 echo
534 '<dl class=phprof_report_info>' .
535 ' <dt>' . $diff_text . ' Report</dt>' .
536 ' <dd>' . ($diff_mode ?
537 $run1_txt . '<br><b>vs.</b><br>' . $run2_txt :
538 $run1_txt) .
539 ' </dd>' .
540 ' <dt>Tip</dt>' .
541 ' <dd>Click a function name below to drill down.</dd>' .
542 '</dl>' .
543 '<div style="clear: both; margin: 3em 0em;"></div>';
544
545 // data tables
546 if (!empty($rep_symbol)) {
547 if (!isset($symbol_tab[$rep_symbol])) {
548 echo "<hr>Symbol <b>$rep_symbol</b> not found in XHProf run</b><hr>";
549 return;
550 }
551
552 /* single function report with parent/child information */
553 if ($diff_mode) {
554 $info1 = isset($symbol_tab1[$rep_symbol]) ?
555 $symbol_tab1[$rep_symbol] : null;
556 $info2 = isset($symbol_tab2[$rep_symbol]) ?
557 $symbol_tab2[$rep_symbol] : null;
558 symbol_report($url_params, $run_delta, $symbol_tab[$rep_symbol],
559 $sort, $rep_symbol,
560 $run1, $info1,
561 $run2, $info2);
562 } else {
563 symbol_report($url_params, $run1_data, $symbol_tab[$rep_symbol],
564 $sort, $rep_symbol, $run1);
565 }
566 } else {
567 /* flat top-level report of all functions */
568 full_report($url_params, $symbol_tab, $sort, $run1, $run2);
569 }
570}
571
572/**
573 * Computes percentage for a pair of values, and returns it
574 * in string format.
575 */
576function pct($a, $b) {
577 if ($b == 0) {
578 return "N/A";
579 } else {
580 $res = (round(($a * 1000 / $b)) / 10);
581 return $res;
582 }
583}
584
585/**
586 * Given a number, returns the td class to use for display.
587 *
588 * For instance, negative numbers in diff reports comparing two runs (run1 & run2)
589 * represent improvement from run1 to run2. We use green to display those deltas,
590 * and red for regression deltas.
591 */
592function get_print_class($num, $bold) {
593 global $vbar;
594 global $vbbar;
595 global $vrbar;
596 global $vgbar;
597 global $diff_mode;
598
599 if ($bold) {
600 if ($diff_mode) {
601 if ($num <= 0) {
602 $class = $vgbar; // green (improvement)
603 } else {
604 $class = $vrbar; // red (regression)
605 }
606 } else {
607 $class = $vbbar; // blue
608 }
609 }
610 else {
611 $class = $vbar; // default (black)
612 }
613
614 return $class;
615}
616
617/**
618 * Prints a <td> element with a numeric value.
619 */
620function print_td_num($num, $fmt_func, $bold=false, $attributes=null) {
621
622 $class = get_print_class($num, $bold);
623
ffb41a8f 624 if (!empty($fmt_func) && is_numeric($num) ) {
6af80cae
EL
625 $num = call_user_func($fmt_func, $num);
626 }
627
628 print("<td $attributes $class>$num</td>\n");
629}
630
631/**
632 * Prints a <td> element with a pecentage.
633 */
634function print_td_pct($numer, $denom, $bold=false, $attributes=null) {
635 global $vbar;
636 global $vbbar;
637 global $diff_mode;
638
639 $class = get_print_class($numer, $bold);
640
641 if ($denom == 0) {
642 $pct = "N/A%";
643 } else {
644 $pct = xhprof_percent_format($numer / abs($denom));
645 }
646
647 print("<td $attributes $class>$pct</td>\n");
648}
649
650/**
651 * Print "flat" data corresponding to one function.
652 *
653 * @author Kannan
654 */
655function print_function_info($url_params, $info, $sort, $run1, $run2) {
656 static $odd_even = 0;
657
658 global $totals;
659 global $sort_col;
660 global $metrics;
661 global $format_cbk;
662 global $display_calls;
663 global $base_path;
664
665 // Toggle $odd_or_even
666 $odd_even = 1 - $odd_even;
667
668 if ($odd_even) {
669 print("<tr>");
670 }
671 else {
672 print('<tr bgcolor="#e5e5e5">');
673 }
674
675 $href = "$base_path/?" .
676 http_build_query(xhprof_array_set($url_params,
677 'symbol', $info["fn"]));
678
679 print('<td>');
680 print(xhprof_render_link($info["fn"], $href));
681 print_source_link($info);
682 print("</td>\n");
683
684 if ($display_calls) {
685 // Call Count..
686 print_td_num($info["ct"], $format_cbk["ct"], ($sort_col == "ct"));
687 print_td_pct($info["ct"], $totals["ct"], ($sort_col == "ct"));
688 }
689
690 // Other metrics..
691 foreach ($metrics as $metric) {
692 // Inclusive metric
693 print_td_num($info[$metric], $format_cbk[$metric],
694 ($sort_col == $metric));
695 print_td_pct($info[$metric], $totals[$metric],
696 ($sort_col == $metric));
697
698 // Exclusive Metric
699 print_td_num($info["excl_" . $metric],
700 $format_cbk["excl_" . $metric],
701 ($sort_col == "excl_" . $metric));
702 print_td_pct($info["excl_" . $metric],
703 $totals[$metric],
704 ($sort_col == "excl_" . $metric));
705 }
706
707 print("</tr>\n");
708}
709
710/**
711 * Print non-hierarchical (flat-view) of profiler data.
712 *
713 * @author Kannan
714 */
715function print_flat_data($url_params, $title, $flat_data, $sort, $run1, $run2, $limit) {
716
717 global $stats;
718 global $sortable_columns;
719 global $vwbar;
720 global $base_path;
721
722 $size = count($flat_data);
723 if (!$limit) { // no limit
724 $limit = $size;
725 $display_link = "";
726 } else {
727 $display_link = xhprof_render_link(" [ <b class=bubble>display all </b>]",
728 "$base_path/?" .
729 http_build_query(xhprof_array_set($url_params,
730 'all', 1)));
731 }
732
733 print("<h3 align=center>$title $display_link</h3><br>");
734
735 print('<table border=1 cellpadding=2 cellspacing=1 width="90%" '
736 .'rules=rows bordercolor="#bdc7d8" align=center>');
737 print('<tr bgcolor="#bdc7d8" align=right>');
738
739 foreach ($stats as $stat) {
740 $desc = stat_description($stat);
741 if (array_key_exists($stat, $sortable_columns)) {
742 $href = "$base_path/?"
743 . http_build_query(xhprof_array_set($url_params, 'sort', $stat));
744 $header = xhprof_render_link($desc, $href);
745 } else {
746 $header = $desc;
747 }
748
749 if ($stat == "fn")
750 print("<th align=left><nobr>$header</th>");
751 else print("<th " . $vwbar . "><nobr>$header</th>");
752 }
753 print("</tr>\n");
754
755 if ($limit >= 0) {
756 $limit = min($size, $limit);
757 for ($i = 0; $i < $limit; $i++) {
758 print_function_info($url_params, $flat_data[$i], $sort, $run1, $run2);
759 }
760 } else {
761 // if $limit is negative, print abs($limit) items starting from the end
762 $limit = min($size, abs($limit));
763 for ($i = 0; $i < $limit; $i++) {
764 print_function_info($url_params, $flat_data[$size - $i - 1], $sort, $run1, $run2);
765 }
766 }
767 print("</table>");
768
769 // let's print the display all link at the bottom as well...
770 if ($display_link) {
771 echo '<div style="text-align: left; padding: 2em">' . $display_link . '</div>';
772 }
773
774}
775
776/**
777 * Generates a tabular report for all functions. This is the top-level report.
778 *
779 * @author Kannan
780 */
781function full_report($url_params, $symbol_tab, $sort, $run1, $run2) {
782 global $vwbar;
783 global $vbar;
784 global $totals;
785 global $totals_1;
786 global $totals_2;
787 global $metrics;
788 global $diff_mode;
789 global $descriptions;
790 global $sort_col;
791 global $format_cbk;
792 global $display_calls;
793 global $base_path;
794
795 $possible_metrics = xhprof_get_possible_metrics();
796
797 if ($diff_mode) {
798
799 $base_url_params = xhprof_array_unset(xhprof_array_unset($url_params,
800 'run1'),
801 'run2');
802 $href1 = "$base_path/?" .
803 http_build_query(xhprof_array_set($base_url_params,
804 'run', $run1));
805 $href2 = "$base_path/?" .
806 http_build_query(xhprof_array_set($base_url_params,
807 'run', $run2));
808
809 print("<h3><center>Overall Diff Summary</center></h3>");
810 print('<table border=1 cellpadding=2 cellspacing=1 width="30%" '
811 .'rules=rows bordercolor="#bdc7d8" align=center>' . "\n");
812 print('<tr bgcolor="#bdc7d8" align=right>');
813 print("<th></th>");
814 print("<th $vwbar>" . xhprof_render_link("Run #$run1", $href1) . "</th>");
815 print("<th $vwbar>" . xhprof_render_link("Run #$run2", $href2) . "</th>");
816 print("<th $vwbar>Diff</th>");
817 print("<th $vwbar>Diff%</th>");
818 print('</tr>');
819
820 if ($display_calls) {
821 print('<tr>');
822 print("<td>Number of Function Calls</td>");
823 print_td_num($totals_1["ct"], $format_cbk["ct"]);
824 print_td_num($totals_2["ct"], $format_cbk["ct"]);
825 print_td_num($totals_2["ct"] - $totals_1["ct"], $format_cbk["ct"], true);
826 print_td_pct($totals_2["ct"] - $totals_1["ct"], $totals_1["ct"], true);
827 print('</tr>');
828 }
829
830 foreach ($metrics as $metric) {
831 $m = $metric;
832 print('<tr>');
833 print("<td>" . str_replace("<br>", " ", $descriptions[$m]) . "</td>");
834 print_td_num($totals_1[$m], $format_cbk[$m]);
835 print_td_num($totals_2[$m], $format_cbk[$m]);
836 print_td_num($totals_2[$m] - $totals_1[$m], $format_cbk[$m], true);
837 print_td_pct($totals_2[$m] - $totals_1[$m], $totals_1[$m], true);
838 print('<tr>');
839 }
840 print('</table>');
841
842 $callgraph_report_title = '[View Regressions/Improvements using Callgraph Diff]';
843
844 } else {
845 print("<p><center>\n");
846
847 print('<table cellpadding=2 cellspacing=1 width="30%" '
848 .'bgcolor="#bdc7d8" align=center>' . "\n");
849 echo "<tr>";
850 echo "<th style='text-align:right'>Overall Summary</th>";
851 echo "<th></th>";
852 echo "</tr>";
853
854 foreach ($metrics as $metric) {
855 echo "<tr>";
856 echo "<td style='text-align:right; font-weight:bold'>Total "
857 . str_replace("<br>", " ", stat_description($metric)) . ":</td>";
858 echo "<td>" . number_format($totals[$metric]) . " "
859 . $possible_metrics[$metric][1] . "</td>";
860 echo "</tr>";
861 }
862
863 if ($display_calls) {
864 echo "<tr>";
865 echo "<td style='text-align:right; font-weight:bold'>Number of Function Calls:</td>";
866 echo "<td>" . number_format($totals['ct']) . "</td>";
867 echo "</tr>";
868 }
869
870 echo "</table>";
871 print("</center></p>\n");
872
873 $callgraph_report_title = '[View Full Callgraph]';
874 }
875
876 print("<center><br><h3>" .
877 xhprof_render_link($callgraph_report_title,
878 "$base_path/callgraph.php" . "?" . http_build_query($url_params))
879 . "</h3></center>");
880
881
882 $flat_data = array();
883 foreach ($symbol_tab as $symbol => $info) {
884 $tmp = $info;
885 $tmp["fn"] = $symbol;
886 $flat_data[] = $tmp;
887 }
888 usort($flat_data, 'sort_cbk');
889
890 print("<br>");
891
892 if (!empty($url_params['all'])) {
893 $all = true;
894 $limit = 0; // display all rows
895 } else {
896 $all = false;
897 $limit = 100; // display only limited number of rows
898 }
899
900 $desc = str_replace("<br>", " ", $descriptions[$sort_col]);
901
902 if ($diff_mode) {
903 if ($all) {
904 $title = "Total Diff Report: '
905 .'Sorted by absolute value of regression/improvement in $desc";
906 } else {
907 $title = "Top 100 <i style='color:red'>Regressions</i>/"
908 . "<i style='color:green'>Improvements</i>: "
909 . "Sorted by $desc Diff";
910 }
911 } else {
912 if ($all) {
913 $title = "Sorted by $desc";
914 } else {
915 $title = "Displaying top $limit functions: Sorted by $desc";
916 }
917 }
918 print_flat_data($url_params, $title, $flat_data, $sort, $run1, $run2, $limit);
919}
920
921
922/**
923 * Return attribute names and values to be used by javascript tooltip.
924 */
925function get_tooltip_attributes($type, $metric) {
926 return "type='$type' metric='$metric'";
927}
928
929/**
930 * Print info for a parent or child function in the
931 * parent & children report.
932 *
933 * @author Kannan
934 */
935function pc_info($info, $base_ct, $base_info, $parent) {
936 global $sort_col;
937 global $metrics;
938 global $format_cbk;
939 global $display_calls;
940
941 if ($parent)
942 $type = "Parent";
943 else $type = "Child";
944
945 if ($display_calls) {
946 $mouseoverct = get_tooltip_attributes($type, "ct");
947 /* call count */
948 print_td_num($info["ct"], $format_cbk["ct"], ($sort_col == "ct"), $mouseoverct);
949 print_td_pct($info["ct"], $base_ct, ($sort_col == "ct"), $mouseoverct);
950 }
951
952 /* Inclusive metric values */
953 foreach ($metrics as $metric) {
954 print_td_num($info[$metric], $format_cbk[$metric],
955 ($sort_col == $metric),
956 get_tooltip_attributes($type, $metric));
957 print_td_pct($info[$metric], $base_info[$metric], ($sort_col == $metric),
958 get_tooltip_attributes($type, $metric));
959 }
960}
961
962function print_pc_array($url_params, $results, $base_ct, $base_info, $parent,
963 $run1, $run2) {
964 global $base_path;
965
966 // Construct section title
967 if ($parent) {
968 $title = 'Parent function';
969 }
970 else {
971 $title = 'Child function';
972 }
973 if (count($results) > 1) {
974 $title .= 's';
975 }
976
977 print("<tr bgcolor='#e0e0ff'><td>");
978 print("<b><i><center>" . $title . "</center></i></b>");
979 print("</td></tr>");
980
981 $odd_even = 0;
982 foreach ($results as $info) {
983 $href = "$base_path/?" .
984 http_build_query(xhprof_array_set($url_params,
985 'symbol', $info["fn"]));
986
987 $odd_even = 1 - $odd_even;
988
989 if ($odd_even) {
990 print('<tr>');
991 }
992 else {
993 print('<tr bgcolor="#e5e5e5">');
994 }
995
996 print("<td>" . xhprof_render_link($info["fn"], $href));
997 print_source_link($info);
998 print("</td>");
999 pc_info($info, $base_ct, $base_info, $parent);
1000 print("</tr>");
1001 }
1002}
1003
1004function print_source_link($info) {
1005 if (strncmp($info['fn'], 'run_init', 8) && $info['fn'] !== 'main()') {
ffb41a8f 1006 if (defined('XHPROF_SYMBOL_LOOKUP_URL')) {
6af80cae
EL
1007 $link = xhprof_render_link(
1008 'source',
1009 XHPROF_SYMBOL_LOOKUP_URL . '?symbol='.rawurlencode($info["fn"]));
1010 print(' ('.$link.')');
1011 }
1012 }
1013}
1014
1015
1016function print_symbol_summary($symbol_info, $stat, $base) {
1017
1018 $val = $symbol_info[$stat];
1019 $desc = str_replace("<br>", " ", stat_description($stat));
1020
1021 print("$desc: </td>");
1022 print(number_format($val));
1023 print(" (" . pct($val, $base) . "% of overall)");
1024 if (substr($stat, 0, 4) == "excl") {
1025 $func_base = $symbol_info[str_replace("excl_", "", $stat)];
1026 print(" (" . pct($val, $func_base) . "% of this function)");
1027 }
1028 print("<br>");
1029}
1030
1031/**
1032 * Generates a report for a single function/symbol.
1033 *
1034 * @author Kannan
1035 */
1036function symbol_report($url_params,
1037 $run_data, $symbol_info, $sort, $rep_symbol,
1038 $run1,
1039 $symbol_info1 = null,
1040 $run2 = 0,
1041 $symbol_info2 = null) {
1042 global $vwbar;
1043 global $vbar;
1044 global $totals;
1045 global $pc_stats;
1046 global $sortable_columns;
1047 global $metrics;
1048 global $diff_mode;
1049 global $descriptions;
1050 global $format_cbk;
1051 global $sort_col;
1052 global $display_calls;
1053 global $base_path;
1054
1055 $possible_metrics = xhprof_get_possible_metrics();
1056
1057 if ($diff_mode) {
1058 $diff_text = "<b>Diff</b>";
1059 $regr_impr = "<i style='color:red'>Regression</i>/<i style='color:green'>Improvement</i>";
1060 } else {
1061 $diff_text = "";
1062 $regr_impr = "";
1063 }
1064
1065 if ($diff_mode) {
1066
1067 $base_url_params = xhprof_array_unset(xhprof_array_unset($url_params,
1068 'run1'),
1069 'run2');
1070 $href1 = "$base_path?"
1071 . http_build_query(xhprof_array_set($base_url_params, 'run', $run1));
1072 $href2 = "$base_path?"
1073 . http_build_query(xhprof_array_set($base_url_params, 'run', $run2));
1074
1075 print("<h3 align=center>$regr_impr summary for $rep_symbol<br><br></h3>");
1076 print('<table border=1 cellpadding=2 cellspacing=1 width="30%" '
1077 .'rules=rows bordercolor="#bdc7d8" align=center>' . "\n");
1078 print('<tr bgcolor="#bdc7d8" align=right>');
1079 print("<th align=left>$rep_symbol</th>");
1080 print("<th $vwbar><a href=" . $href1 . ">Run #$run1</a></th>");
1081 print("<th $vwbar><a href=" . $href2 . ">Run #$run2</a></th>");
1082 print("<th $vwbar>Diff</th>");
1083 print("<th $vwbar>Diff%</th>");
1084 print('</tr>');
1085 print('<tr>');
1086
1087 if ($display_calls) {
1088 print("<td>Number of Function Calls</td>");
1089 print_td_num($symbol_info1["ct"], $format_cbk["ct"]);
1090 print_td_num($symbol_info2["ct"], $format_cbk["ct"]);
1091 print_td_num($symbol_info2["ct"] - $symbol_info1["ct"],
1092 $format_cbk["ct"], true);
1093 print_td_pct($symbol_info2["ct"] - $symbol_info1["ct"],
1094 $symbol_info1["ct"], true);
1095 print('</tr>');
1096 }
1097
1098
1099 foreach ($metrics as $metric) {
1100 $m = $metric;
1101
1102 // Inclusive stat for metric
1103 print('<tr>');
1104 print("<td>" . str_replace("<br>", " ", $descriptions[$m]) . "</td>");
1105 print_td_num($symbol_info1[$m], $format_cbk[$m]);
1106 print_td_num($symbol_info2[$m], $format_cbk[$m]);
1107 print_td_num($symbol_info2[$m] - $symbol_info1[$m], $format_cbk[$m], true);
1108 print_td_pct($symbol_info2[$m] - $symbol_info1[$m], $symbol_info1[$m], true);
1109 print('</tr>');
1110
1111 // AVG (per call) Inclusive stat for metric
1112 print('<tr>');
1113 print("<td>" . str_replace("<br>", " ", $descriptions[$m]) . " per call </td>");
1114 $avg_info1 = 'N/A';
1115 $avg_info2 = 'N/A';
1116 if ($symbol_info1['ct'] > 0) {
1117 $avg_info1 = ($symbol_info1[$m] / $symbol_info1['ct']);
1118 }
1119 if ($symbol_info2['ct'] > 0) {
1120 $avg_info2 = ($symbol_info2[$m] / $symbol_info2['ct']);
1121 }
1122 print_td_num($avg_info1, $format_cbk[$m]);
1123 print_td_num($avg_info2, $format_cbk[$m]);
1124 print_td_num($avg_info2 - $avg_info1, $format_cbk[$m], true);
1125 print_td_pct($avg_info2 - $avg_info1, $avg_info1, true);
1126 print('</tr>');
1127
1128 // Exclusive stat for metric
1129 $m = "excl_" . $metric;
1130 print('<tr style="border-bottom: 1px solid black;">');
1131 print("<td>" . str_replace("<br>", " ", $descriptions[$m]) . "</td>");
1132 print_td_num($symbol_info1[$m], $format_cbk[$m]);
1133 print_td_num($symbol_info2[$m], $format_cbk[$m]);
1134 print_td_num($symbol_info2[$m] - $symbol_info1[$m], $format_cbk[$m], true);
1135 print_td_pct($symbol_info2[$m] - $symbol_info1[$m], $symbol_info1[$m], true);
1136 print('</tr>');
1137 }
1138
1139 print('</table>');
1140 }
1141
1142 print("<br><h4><center>");
1143 print("Parent/Child $regr_impr report for <b>$rep_symbol</b>");
1144
1145 $callgraph_href = "$base_path/callgraph.php?"
1146 . http_build_query(xhprof_array_set($url_params, 'func', $rep_symbol));
1147
1148 print(" <a href='$callgraph_href'>[View Callgraph $diff_text]</a><br>");
1149
1150 print("</center></h4><br>");
1151
1152 print('<table border=1 cellpadding=2 cellspacing=1 width="90%" '
1153 .'rules=rows bordercolor="#bdc7d8" align=center>' . "\n");
1154 print('<tr bgcolor="#bdc7d8" align=right>');
1155
1156 foreach ($pc_stats as $stat) {
1157 $desc = stat_description($stat);
1158 if (array_key_exists($stat, $sortable_columns)) {
1159
1160 $href = "$base_path/?" .
1161 http_build_query(xhprof_array_set($url_params,
1162 'sort', $stat));
1163 $header = xhprof_render_link($desc, $href);
1164 } else {
1165 $header = $desc;
1166 }
1167
1168 if ($stat == "fn")
1169 print("<th align=left><nobr>$header</th>");
1170 else print("<th " . $vwbar . "><nobr>$header</th>");
1171 }
1172 print("</tr>");
1173
1174 print("<tr bgcolor='#e0e0ff'><td>");
1175 print("<b><i><center>Current Function</center></i></b>");
1176 print("</td></tr>");
1177
1178 print("<tr>");
1179 // make this a self-reference to facilitate copy-pasting snippets to e-mails
1180 print("<td><a href=''>$rep_symbol</a>");
1181 print_source_link(array('fn' => $rep_symbol));
1182 print("</td>");
1183
1184 if ($display_calls) {
1185 // Call Count
1186 print_td_num($symbol_info["ct"], $format_cbk["ct"]);
1187 print_td_pct($symbol_info["ct"], $totals["ct"]);
1188 }
1189
1190 // Inclusive Metrics for current function
1191 foreach ($metrics as $metric) {
1192 print_td_num($symbol_info[$metric], $format_cbk[$metric], ($sort_col == $metric));
1193 print_td_pct($symbol_info[$metric], $totals[$metric], ($sort_col == $metric));
1194 }
1195 print("</tr>");
1196
1197 print("<tr bgcolor='#ffffff'>");
1198 print("<td style='text-align:right;color:blue'>"
1199 ."Exclusive Metrics $diff_text for Current Function</td>");
1200
1201 if ($display_calls) {
1202 // Call Count
1203 print("<td $vbar></td>");
1204 print("<td $vbar></td>");
1205 }
1206
1207 // Exclusive Metrics for current function
1208 foreach ($metrics as $metric) {
1209 print_td_num($symbol_info["excl_" . $metric], $format_cbk["excl_" . $metric],
1210 ($sort_col == $metric),
1211 get_tooltip_attributes("Child", $metric));
1212 print_td_pct($symbol_info["excl_" . $metric], $symbol_info[$metric],
1213 ($sort_col == $metric),
1214 get_tooltip_attributes("Child", $metric));
1215 }
1216 print("</tr>");
1217
1218 // list of callers/parent functions
1219 $results = array();
1220 if ($display_calls) {
1221 $base_ct = $symbol_info["ct"];
1222 } else {
1223 $base_ct = 0;
1224 }
1225 foreach ($metrics as $metric) {
1226 $base_info[$metric] = $symbol_info[$metric];
1227 }
1228 foreach ($run_data as $parent_child => $info) {
1229 list($parent, $child) = xhprof_parse_parent_child($parent_child);
1230 if (($child == $rep_symbol) && ($parent)) {
1231 $info_tmp = $info;
1232 $info_tmp["fn"] = $parent;
1233 $results[] = $info_tmp;
1234 }
1235 }
1236 usort($results, 'sort_cbk');
1237
1238 if (count($results) > 0) {
1239 print_pc_array($url_params, $results, $base_ct, $base_info, true,
1240 $run1, $run2);
1241 }
1242
1243 // list of callees/child functions
1244 $results = array();
1245 $base_ct = 0;
1246 foreach ($run_data as $parent_child => $info) {
1247 list($parent, $child) = xhprof_parse_parent_child($parent_child);
1248 if ($parent == $rep_symbol) {
1249 $info_tmp = $info;
1250 $info_tmp["fn"] = $child;
1251 $results[] = $info_tmp;
1252 if ($display_calls) {
1253 $base_ct += $info["ct"];
1254 }
1255 }
1256 }
1257 usort($results, 'sort_cbk');
1258
1259 if (count($results)) {
1260 print_pc_array($url_params, $results, $base_ct, $base_info, false,
1261 $run1, $run2);
1262 }
1263
1264 print("</table>");
1265
1266 // These will be used for pop-up tips/help.
1267 // Related javascript code is in: xhprof_report.js
1268 print("\n");
1269 print('<script language="javascript">' . "\n");
1270 print("var func_name = '\"" . $rep_symbol . "\"';\n");
1271 print("var total_child_ct = " . $base_ct . ";\n");
1272 if ($display_calls) {
1273 print("var func_ct = " . $symbol_info["ct"] . ";\n");
1274 }
1275 print("var func_metrics = new Array();\n");
1276 print("var metrics_col = new Array();\n");
1277 print("var metrics_desc = new Array();\n");
1278 if ($diff_mode) {
1279 print("var diff_mode = true;\n");
1280 } else {
1281 print("var diff_mode = false;\n");
1282 }
1283 $column_index = 3; // First three columns are Func Name, Calls, Calls%
1284 foreach ($metrics as $metric) {
1285 print("func_metrics[\"" . $metric . "\"] = " . round($symbol_info[$metric]) . ";\n");
1286 print("metrics_col[\"". $metric . "\"] = " . $column_index . ";\n");
1287 print("metrics_desc[\"". $metric . "\"] = \"" . $possible_metrics[$metric][2] . "\";\n");
1288
1289 // each metric has two columns..
1290 $column_index += 2;
1291 }
1292 print('</script>');
1293 print("\n");
1294
1295}
1296
1297/**
1298 * Generate the profiler report for a single run.
1299 *
1300 * @author Kannan
1301 */
1302function profiler_single_run_report ($url_params,
1303 $xhprof_data,
1304 $run_desc,
1305 $rep_symbol,
1306 $sort,
1307 $run) {
1308
1309 init_metrics($xhprof_data, $rep_symbol, $sort, false);
1310
1311 profiler_report($url_params, $rep_symbol, $sort, $run, $run_desc,
1312 $xhprof_data);
1313}
1314
1315
1316
1317/**
1318 * Generate the profiler report for diff mode (delta between two runs).
1319 *
1320 * @author Kannan
1321 */
1322function profiler_diff_report($url_params,
1323 $xhprof_data1,
1324 $run1_desc,
1325 $xhprof_data2,
1326 $run2_desc,
1327 $rep_symbol,
1328 $sort,
1329 $run1,
1330 $run2) {
1331
1332
1333 // Initialize what metrics we'll display based on data in Run2
1334 init_metrics($xhprof_data2, $rep_symbol, $sort, true);
1335
1336 profiler_report($url_params,
1337 $rep_symbol,
1338 $sort,
1339 $run1,
1340 $run1_desc,
1341 $xhprof_data1,
1342 $run2,
1343 $run2_desc,
1344 $xhprof_data2);
1345}
1346
1347
1348/**
1349 * Generate a XHProf Display View given the various URL parameters
1350 * as arguments. The first argument is an object that implements
1351 * the iXHProfRuns interface.
1352 *
1353 * @param object $xhprof_runs_impl An object that implements
1354 * the iXHProfRuns interface
1355 *.
1356 * @param array $url_params Array of non-default URL params.
1357 *
1358 * @param string $source Category/type of the run. The source in
1359 * combination with the run id uniquely
1360 * determines a profiler run.
1361 *
1362 * @param string $run run id, or comma separated sequence of
1363 * run ids. The latter is used if an aggregate
1364 * report of the runs is desired.
1365 *
1366 * @param string $wts Comma separate list of integers.
1367 * Represents the weighted ratio in
1368 * which which a set of runs will be
1369 * aggregated. [Used only for aggregate
1370 * reports.]
1371 *
1372 * @param string $symbol Function symbol. If non-empty then the
1373 * parent/child view of this function is
1374 * displayed. If empty, a flat-profile view
1375 * of the functions is displayed.
1376 *
1377 * @param string $run1 Base run id (for diff reports)
1378 *
1379 * @param string $run2 New run id (for diff reports)
1380 *
1381 */
1382function displayXHProfReport($xhprof_runs_impl, $url_params, $source,
1383 $run, $wts, $symbol, $sort, $run1, $run2) {
1384
1385 if ($run) { // specific run to display?
1386
1387 // run may be a single run or a comma separate list of runs
1388 // that'll be aggregated. If "wts" (a comma separated list
1389 // of integral weights is specified), the runs will be
1390 // aggregated in that ratio.
1391 //
1392 $runs_array = explode(",", $run);
1393
1394 if (count($runs_array) == 1) {
1395 $xhprof_data = $xhprof_runs_impl->get_run($runs_array[0],
1396 $source,
1397 $description);
1398 } else {
1399 if (!empty($wts)) {
1400 $wts_array = explode(",", $wts);
1401 } else {
1402 $wts_array = null;
1403 }
1404 $data = xhprof_aggregate_runs($xhprof_runs_impl,
1405 $runs_array, $wts_array, $source, false);
1406 $xhprof_data = $data['raw'];
1407 $description = $data['description'];
1408 }
1409
1410
1411 profiler_single_run_report($url_params,
1412 $xhprof_data,
1413 $description,
1414 $symbol,
1415 $sort,
1416 $run);
1417
1418 } else if ($run1 && $run2) { // diff report for two runs
1419
1420 $xhprof_data1 = $xhprof_runs_impl->get_run($run1, $source, $description1);
1421 $xhprof_data2 = $xhprof_runs_impl->get_run($run2, $source, $description2);
1422
1423 profiler_diff_report($url_params,
1424 $xhprof_data1,
1425 $description1,
1426 $xhprof_data2,
1427 $description2,
1428 $symbol,
1429 $sort,
1430 $run1,
1431 $run2);
1432
1433 } else {
1434 echo "No XHProf runs specified in the URL.";
1435 if (method_exists($xhprof_runs_impl, 'list_runs')) {
1436 $xhprof_runs_impl->list_runs();
1437 }
1438 }
1439}