Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/lib/xhprof/xhprof_lib/utils/callgraph_utils.php
Go to the documentation of this file.
00001 <?php
00002 //  Copyright (c) 2009 Facebook
00003 //
00004 //  Licensed under the Apache License, Version 2.0 (the "License");
00005 //  you may not use this file except in compliance with the License.
00006 //  You may obtain a copy of the License at
00007 //
00008 //      http://www.apache.org/licenses/LICENSE-2.0
00009 //
00010 //  Unless required by applicable law or agreed to in writing, software
00011 //  distributed under the License is distributed on an "AS IS" BASIS,
00012 //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013 //  See the License for the specific language governing permissions and
00014 //  limitations under the License.
00015 //
00016 
00017 /*
00018  * This file contains callgraph image generation related XHProf utility
00019  * functions
00020  *
00021  */
00022 
00023 // Supported ouput format
00024 $xhprof_legal_image_types = array(
00025     "jpg" => 1,
00026     "gif" => 1,
00027     "png" => 1,
00028     "ps"  => 1,
00029     );
00030 
00041 function xhprof_http_header($name, $value) {
00042 
00043   if (!$name) {
00044     xhprof_error('http_header usage');
00045     return null;
00046   }
00047 
00048   if (!is_string($value)) {
00049     xhprof_error('http_header value not a string');
00050   }
00051 
00052   header($name.': '.$value, true);
00053 }
00054 
00060 function xhprof_generate_mime_header($type, $length) {
00061   switch ($type) {
00062     case 'jpg':
00063       $mime = 'image/jpeg';
00064       break;
00065     case 'gif':
00066       $mime = 'image/gif';
00067       break;
00068     case 'png':
00069       $mime = 'image/png';
00070       break;
00071     case 'ps':
00072       $mime = 'application/postscript';
00073     default:
00074       $mime = false;
00075   }
00076 
00077   if ($mime) {
00078     xhprof_http_header('Content-type', $mime);
00079     xhprof_http_header('Content-length', (string)$length);
00080   }
00081 }
00082 
00096 function xhprof_generate_image_by_dot($dot_script, $type) {
00097   $descriptorspec = array(
00098        // stdin is a pipe that the child will read from
00099        0 => array("pipe", "r"),
00100        // stdout is a pipe that the child will write to
00101        1 => array("pipe", "w"),
00102        // stderr is a pipe that the child will write to
00103        2 => array("pipe", "w")
00104        );
00105 
00106   // start moodle modification: use $CFG->pathtodot for executing this
00107   //$cmd = " dot -T".$type;
00108   global $CFG;
00109   $cmd = (!empty($CFG->pathtodot) ? $CFG->pathtodot : 'dot') . ' -T' . $type;
00110   // end moodle modification
00111 
00112   $process = proc_open($cmd, $descriptorspec, $pipes, "/tmp", array());
00113   if (is_resource($process)) {
00114     fwrite($pipes[0], $dot_script);
00115     fclose($pipes[0]);
00116 
00117     $output = stream_get_contents($pipes[1]);
00118 
00119     $err = stream_get_contents($pipes[2]);
00120     if (!empty($err)) {
00121       print "failed to execute cmd: \"$cmd\". stderr: `$err'\n";
00122       exit;
00123     }
00124 
00125     fclose($pipes[2]);
00126     fclose($pipes[1]);
00127     proc_close($process);
00128     return $output;
00129   }
00130   print "failed to execute cmd \"$cmd\"";
00131   exit();
00132 }
00133 
00134 /*
00135  * Get the children list of all nodes.
00136  */
00137 function xhprof_get_children_table($raw_data) {
00138   $children_table = array();
00139   foreach ($raw_data as $parent_child => $info) {
00140     list($parent, $child) = xhprof_parse_parent_child($parent_child);
00141     if (!isset($children_table[$parent])) {
00142       $children_table[$parent] = array($child);
00143     } else {
00144       $children_table[$parent][] = $child;
00145     }
00146   }
00147   return $children_table;
00148 }
00149 
00167 function xhprof_generate_dot_script($raw_data, $threshold, $source, $page,
00168                                     $func, $critical_path, $right=null,
00169                                     $left=null) {
00170 
00171   $max_width = 5;
00172   $max_height = 3.5;
00173   $max_fontsize = 35;
00174   $max_sizing_ratio = 20;
00175 
00176   $totals;
00177 
00178   if ($left === null) {
00179     // init_metrics($raw_data, null, null);
00180   }
00181   $sym_table = xhprof_compute_flat_info($raw_data, $totals);
00182 
00183   if ($critical_path) {
00184     $children_table = xhprof_get_children_table($raw_data);
00185     $node = "main()";
00186     $path = array();
00187     $path_edges = array();
00188     $visited = array();
00189     while ($node) {
00190       $visited[$node] = true;
00191       if (isset($children_table[$node])) {
00192         $max_child = null;
00193         foreach ($children_table[$node] as $child) {
00194 
00195           if (isset($visited[$child])) {
00196             continue;
00197           }
00198           if ($max_child === null ||
00199             abs($raw_data[xhprof_build_parent_child_key($node,
00200                                                         $child)]["wt"]) >
00201             abs($raw_data[xhprof_build_parent_child_key($node,
00202                                                         $max_child)]["wt"])) {
00203             $max_child = $child;
00204           }
00205         }
00206         if ($max_child !== null) {
00207           $path[$max_child] = true;
00208           $path_edges[xhprof_build_parent_child_key($node, $max_child)] = true;
00209         }
00210         $node = $max_child;
00211       } else {
00212         $node = null;
00213       }
00214     }
00215   }
00216 
00217   // if it is a benchmark callgraph, we make the benchmarked function the root.
00218  if ($source == "bm" && array_key_exists("main()", $sym_table)) {
00219     $total_times = $sym_table["main()"]["ct"];
00220     $remove_funcs = array("main()",
00221                           "hotprofiler_disable",
00222                           "call_user_func_array",
00223                           "xhprof_disable");
00224 
00225     foreach ($remove_funcs as $cur_del_func) {
00226       if (array_key_exists($cur_del_func, $sym_table) &&
00227           $sym_table[$cur_del_func]["ct"] == $total_times) {
00228         unset($sym_table[$cur_del_func]);
00229       }
00230     }
00231   }
00232 
00233   // use the function to filter out irrelevant functions.
00234   if (!empty($func)) {
00235     $interested_funcs = array();
00236     foreach ($raw_data as $parent_child => $info) {
00237       list($parent, $child) = xhprof_parse_parent_child($parent_child);
00238       if ($parent == $func || $child == $func) {
00239         $interested_funcs[$parent] = 1;
00240         $interested_funcs[$child] = 1;
00241       }
00242     }
00243     foreach ($sym_table as $symbol => $info) {
00244       if (!array_key_exists($symbol, $interested_funcs)) {
00245         unset($sym_table[$symbol]);
00246       }
00247     }
00248   }
00249 
00250   $result = "digraph call_graph {\n";
00251 
00252   // Filter out functions whose exclusive time ratio is below threshold, and
00253   // also assign a unique integer id for each function to be generated. In the
00254   // meantime, find the function with the most exclusive time (potentially the
00255   // performance bottleneck).
00256   $cur_id = 0; $max_wt = 0;
00257   foreach ($sym_table as $symbol => $info) {
00258     if (empty($func) && abs($info["wt"] / $totals["wt"]) < $threshold) {
00259       unset($sym_table[$symbol]);
00260       continue;
00261     }
00262     if ($max_wt == 0 || $max_wt < abs($info["excl_wt"])) {
00263       $max_wt = abs($info["excl_wt"]);
00264     }
00265     $sym_table[$symbol]["id"] = $cur_id;
00266     $cur_id ++;
00267   }
00268 
00269   // Generate all nodes' information.
00270   foreach ($sym_table as $symbol => $info) {
00271     if ($info["excl_wt"] == 0) {
00272       $sizing_factor = $max_sizing_ratio;
00273     } else {
00274       $sizing_factor = $max_wt / abs($info["excl_wt"]) ;
00275       if ($sizing_factor > $max_sizing_ratio) {
00276         $sizing_factor = $max_sizing_ratio;
00277       }
00278     }
00279     $fillcolor = (($sizing_factor < 1.5) ?
00280                   ", style=filled, fillcolor=red" : "");
00281 
00282     if ($critical_path) {
00283       // highlight nodes along critical path.
00284       if (!$fillcolor && array_key_exists($symbol, $path)) {
00285         $fillcolor = ", style=filled, fillcolor=yellow";
00286       }
00287     }
00288 
00289     $fontsize = ", fontsize="
00290                .(int)($max_fontsize / (($sizing_factor - 1) / 10 + 1));
00291 
00292     $width = ", width=".sprintf("%.1f", $max_width / $sizing_factor);
00293     $height = ", height=".sprintf("%.1f", $max_height / $sizing_factor);
00294 
00295     if ($symbol == "main()") {
00296       $shape = "octagon";
00297       $name = "Total: ".($totals["wt"] / 1000.0)." ms\\n";
00298       $name .= addslashes(isset($page) ? $page : $symbol);
00299     } else {
00300       $shape = "box";
00301       $name = addslashes($symbol)."\\nInc: ". sprintf("%.3f",$info["wt"] / 1000) .
00302               " ms (" . sprintf("%.1f%%", 100 * $info["wt"] / $totals["wt"]).")";
00303     }
00304     if ($left === null) {
00305       $label = ", label=\"".$name."\\nExcl: "
00306                .(sprintf("%.3f",$info["excl_wt"] / 1000.0))." ms ("
00307                .sprintf("%.1f%%", 100 * $info["excl_wt"] / $totals["wt"])
00308                . ")\\n".$info["ct"]." total calls\"";
00309     } else {
00310       if (isset($left[$symbol]) && isset($right[$symbol])) {
00311          $label = ", label=\"".addslashes($symbol).
00312                   "\\nInc: ".(sprintf("%.3f",$left[$symbol]["wt"] / 1000.0))
00313                   ." ms - "
00314                   .(sprintf("%.3f",$right[$symbol]["wt"] / 1000.0))." ms = "
00315                   .(sprintf("%.3f",$info["wt"] / 1000.0))." ms".
00316                   "\\nExcl: "
00317                   .(sprintf("%.3f",$left[$symbol]["excl_wt"] / 1000.0))
00318                   ." ms - ".(sprintf("%.3f",$right[$symbol]["excl_wt"] / 1000.0))
00319                    ." ms = ".(sprintf("%.3f",$info["excl_wt"] / 1000.0))." ms".
00320                   "\\nCalls: ".(sprintf("%.3f",$left[$symbol]["ct"]))." - "
00321                    .(sprintf("%.3f",$right[$symbol]["ct"]))." = "
00322                    .(sprintf("%.3f",$info["ct"]))."\"";
00323       } else if (isset($left[$symbol])) {
00324         $label = ", label=\"".addslashes($symbol).
00325                   "\\nInc: ".(sprintf("%.3f",$left[$symbol]["wt"] / 1000.0))
00326                    ." ms - 0 ms = ".(sprintf("%.3f",$info["wt"] / 1000.0))
00327                    ." ms"."\\nExcl: "
00328                    .(sprintf("%.3f",$left[$symbol]["excl_wt"] / 1000.0))
00329                    ." ms - 0 ms = "
00330                    .(sprintf("%.3f",$info["excl_wt"] / 1000.0))." ms".
00331                   "\\nCalls: ".(sprintf("%.3f",$left[$symbol]["ct"]))." - 0 = "
00332                   .(sprintf("%.3f",$info["ct"]))."\"";
00333       } else {
00334         $label = ", label=\"".addslashes($symbol).
00335                   "\\nInc: 0 ms - "
00336                   .(sprintf("%.3f",$right[$symbol]["wt"] / 1000.0))
00337                   ." ms = ".(sprintf("%.3f",$info["wt"] / 1000.0))." ms".
00338                   "\\nExcl: 0 ms - "
00339                   .(sprintf("%.3f",$right[$symbol]["excl_wt"] / 1000.0))
00340                   ." ms = ".(sprintf("%.3f",$info["excl_wt"] / 1000.0))." ms".
00341                   "\\nCalls: 0 - ".(sprintf("%.3f",$right[$symbol]["ct"]))
00342                   ." = ".(sprintf("%.3f",$info["ct"]))."\"";
00343       }
00344     }
00345     $result .= "N" . $sym_table[$symbol]["id"];
00346     $result .= "[shape=$shape ".$label.$width
00347                .$height.$fontsize.$fillcolor."];\n";
00348   }
00349 
00350   // Generate all the edges' information.
00351   foreach ($raw_data as $parent_child => $info) {
00352     list($parent, $child) = xhprof_parse_parent_child($parent_child);
00353 
00354     if (isset($sym_table[$parent]) && isset($sym_table[$child]) &&
00355         (empty($func) ||
00356          (!empty($func) && ($parent == $func || $child == $func)))) {
00357 
00358       $label = $info["ct"] == 1 ? $info["ct"]." call" : $info["ct"]." calls";
00359 
00360       $headlabel = $sym_table[$child]["wt"] > 0 ?
00361                   sprintf("%.1f%%", 100 * $info["wt"]
00362                                     / $sym_table[$child]["wt"])
00363                   : "0.0%";
00364 
00365       $taillabel = ($sym_table[$parent]["wt"] > 0) ?
00366         sprintf("%.1f%%",
00367                 100 * $info["wt"] /
00368                 ($sym_table[$parent]["wt"] - $sym_table["$parent"]["excl_wt"]))
00369         : "0.0%";
00370 
00371       $linewidth = 1;
00372       $arrow_size = 1;
00373 
00374       if ($critical_path &&
00375           isset($path_edges[xhprof_build_parent_child_key($parent, $child)])) {
00376         $linewidth = 10; $arrow_size = 2;
00377       }
00378 
00379       $result .= "N" . $sym_table[$parent]["id"] . " -> N"
00380                  . $sym_table[$child]["id"];
00381       $result .= "[arrowsize=$arrow_size, style=\"setlinewidth($linewidth)\","
00382                  ." label=\""
00383                  .$label."\", headlabel=\"".$headlabel
00384                  ."\", taillabel=\"".$taillabel."\" ]";
00385       $result .= ";\n";
00386 
00387     }
00388   }
00389   $result = $result . "\n}";
00390 
00391   return $result;
00392 }
00393 
00394 function  xhprof_render_diff_image($xhprof_runs_impl, $run1, $run2,
00395                                    $type, $threshold, $source) {
00396   $total1;
00397   $total2;
00398 
00399   $raw_data1 = $xhprof_runs_impl->get_run($run1, $source, $desc_unused);
00400   $raw_data2 = $xhprof_runs_impl->get_run($run2, $source, $desc_unused);
00401 
00402   // init_metrics($raw_data1, null, null);
00403   $children_table1 = xhprof_get_children_table($raw_data1);
00404   $children_table2 = xhprof_get_children_table($raw_data2);
00405   $symbol_tab1 = xhprof_compute_flat_info($raw_data1, $total1);
00406   $symbol_tab2 = xhprof_compute_flat_info($raw_data2, $total2);
00407   $run_delta = xhprof_compute_diff($raw_data1, $raw_data2);
00408   $script = xhprof_generate_dot_script($run_delta, $threshold, $source,
00409                                        null, null, true,
00410                                        $symbol_tab1, $symbol_tab2);
00411   $content = xhprof_generate_image_by_dot($script, $type);
00412 
00413   xhprof_generate_mime_header($type, strlen($content));
00414   echo $content;
00415 }
00416 
00435 function xhprof_get_content_by_run($xhprof_runs_impl, $run_id, $type,
00436                                    $threshold, $func, $source,
00437                                    $critical_path) {
00438   if (!$run_id)
00439     return "";
00440 
00441   $raw_data = $xhprof_runs_impl->get_run($run_id, $source, $description);
00442   if (!$raw_data) {
00443     xhprof_error("Raw data is empty");
00444     return "";
00445   }
00446 
00447   $script = xhprof_generate_dot_script($raw_data, $threshold, $source,
00448                                        $description, $func, $critical_path);
00449 
00450   $content = xhprof_generate_image_by_dot($script, $type);
00451   return $content;
00452 }
00453 
00471 function xhprof_render_image($xhprof_runs_impl, $run_id, $type, $threshold,
00472                              $func, $source, $critical_path) {
00473 
00474   $content = xhprof_get_content_by_run($xhprof_runs_impl, $run_id, $type,
00475                                        $threshold,
00476                                        $func, $source, $critical_path);
00477   if (!$content) {
00478     print "Error: either we can not find profile data for run_id ".$run_id
00479           ." or the threshold ".$threshold." is too small or you do not"
00480           ." have 'dot' image generation utility installed.";
00481     exit();
00482   }
00483 
00484   xhprof_generate_mime_header($type, strlen($content));
00485   echo $content;
00486 }
 All Data Structures Namespaces Files Functions Variables Enumerations