handlerviz.pl 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. #!/usr/bin/perl -w
  2. #
  3. # handlerviz.pl
  4. # ~~~~~~~~~~~~~
  5. #
  6. # A visualisation tool for post-processing the debug output generated by
  7. # Asio-based programs. Programs write this output to the standard error stream
  8. # when compiled with the define `BOOST_ASIO_ENABLE_HANDLER_TRACKING'.
  9. #
  10. # This tool generates output intended for use with the GraphViz tool `dot'. For
  11. # example, to convert output to a PNG image, use:
  12. #
  13. # perl handlerviz.pl < output.txt | dot -Tpng > output.png
  14. #
  15. # To convert to a PDF file, use:
  16. #
  17. # perl handlerviz.pl < output.txt | dot -Tpdf > output.pdf
  18. #
  19. # Copyright (c) 2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
  20. #
  21. # Distributed under the Boost Software License, Version 1.0. (See accompanying
  22. # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  23. #
  24. use strict;
  25. my %nodes = ();
  26. my @edges = ();
  27. my %anon_nodes = ();
  28. my $anon_id = 0;
  29. my %all_nodes = ();
  30. #-------------------------------------------------------------------------------
  31. # Parse the debugging output and populate the nodes and edges.
  32. sub parse_debug_output()
  33. {
  34. while (my $line = <>)
  35. {
  36. chomp($line);
  37. if ($line =~ /\@asio\|([^|]*)\|([^|]*)\|(.*)$/)
  38. {
  39. my $timestamp = $1;
  40. my $action = $2;
  41. my $description = $3;
  42. # Handler creation.
  43. if ($action =~ /^([0-9]+)\*([0-9]+)$/)
  44. {
  45. my $begin = $1;
  46. my $end = $2;
  47. my $label = $description;
  48. $label =~ s/\./\\n/g;
  49. if ($begin eq "0")
  50. {
  51. $begin = "a" . $anon_id++;
  52. $anon_nodes{$begin} = $timestamp;
  53. $all_nodes{"$timestamp-$begin"} = $begin;
  54. }
  55. my %edge = ( begin=>$begin, end=>$end, label=>$label );
  56. push(@edges, \%edge);
  57. }
  58. # Begin handler invocation.
  59. elsif ($action =~ /^>([0-9]+)$/)
  60. {
  61. my %new_node = ( label=>$description, entry=>$timestamp );
  62. $new_node{content} = ();
  63. $nodes{$1} = \%new_node;
  64. $all_nodes{"$timestamp-$1"} = $1;
  65. }
  66. # End handler invocation.
  67. elsif ($action =~ /^<([0-9]+)$/)
  68. {
  69. $nodes{$1}->{exit} = $timestamp;
  70. }
  71. # Handler threw exception.
  72. elsif ($action =~ /^!([0-9]+)$/)
  73. {
  74. push(@{$nodes{$1}->{content}}, "exception");
  75. }
  76. # Handler was destroyed without being invoked.
  77. elsif ($action =~ /^~([0-9]+)$/)
  78. {
  79. my %new_node = ( label=>"$timestamp destroyed" );
  80. $new_node{content} = ();
  81. $nodes{$1} = \%new_node;
  82. $all_nodes{"$timestamp-$1"} = $1;
  83. }
  84. # Handler performed some operation.
  85. elsif ($action =~ /^([0-9]+)$/)
  86. {
  87. if ($1 eq "0")
  88. {
  89. my $id = "a" . $anon_id++;
  90. $anon_nodes{$id} = "$timestamp\\l$description";
  91. $all_nodes{"$timestamp-$id"} = $id;
  92. }
  93. else
  94. {
  95. push(@{$nodes{$1}->{content}}, "$description");
  96. }
  97. }
  98. }
  99. }
  100. }
  101. #-------------------------------------------------------------------------------
  102. # Helper function to convert a string to escaped HTML text.
  103. sub escape($)
  104. {
  105. my $text = shift;
  106. $text =~ s/&/\&amp\;/g;
  107. $text =~ s/</\&lt\;/g;
  108. $text =~ s/>/\&gt\;/g;
  109. $text =~ s/\t/ /g;
  110. return $text;
  111. }
  112. #-------------------------------------------------------------------------------
  113. # Templates for dot output.
  114. my $graph_header = <<"EOF";
  115. /* Generated by asioviz.pl */
  116. digraph G
  117. {
  118. graph [ nodesep="1" ];
  119. node [ shape="box", fontsize="9" ];
  120. edge [ arrowtail="dot", fontsize="9" ];
  121. EOF
  122. my $graph_footer = <<"EOF";
  123. }
  124. EOF
  125. my $node_header = <<"EOF";
  126. "%name%"
  127. [
  128. label=<<table border="0" cellspacing="0">
  129. <tr><td align="left" bgcolor="gray" border="0">%label%</td></tr>
  130. EOF
  131. my $node_footer = <<"EOF";
  132. </table>>
  133. ]
  134. EOF
  135. my $node_content = <<"EOF";
  136. <tr><td align="left" bgcolor="white" border="0">
  137. <font face="mono" point-size="9">%content%</font>
  138. </td></tr>
  139. EOF
  140. my $anon_nodes_header = <<"EOF";
  141. {
  142. node [ shape="record" ];
  143. EOF
  144. my $anon_nodes_footer = <<"EOF";
  145. }
  146. EOF
  147. my $anon_node = <<"EOF";
  148. "%name%" [ label="%label%", color="gray" ];
  149. EOF
  150. my $edges_header = <<"EOF";
  151. {
  152. edge [ style="dashed", arrowhead="open", weight="100" ];
  153. EOF
  154. my $edges_footer = <<"EOF";
  155. }
  156. EOF
  157. my $edge = <<"EOF";
  158. "%begin%" -> "%end%" [ label="%label%" ]
  159. EOF
  160. my $node_order_header = <<"EOF";
  161. {
  162. edge [ style="invis", weight="1" ];
  163. EOF
  164. my $node_order_footer = <<"EOF";
  165. }
  166. EOF
  167. my $node_order = <<"EOF";
  168. "%begin%" -> "%end%"
  169. EOF
  170. #-------------------------------------------------------------------------------
  171. # Generate dot output from the nodes and edges.
  172. sub print_nodes()
  173. {
  174. foreach my $name (sort keys %nodes)
  175. {
  176. my $node = $nodes{$name};
  177. my $entry = $node->{entry};
  178. my $exit = $node->{exit};
  179. my $label = escape($node->{label});
  180. my $header = $node_header;
  181. $header =~ s/%name%/$name/g;
  182. $header =~ s/%label%/$label/g;
  183. print($header);
  184. my $line = $node_content;
  185. my $content = $entry . " + " . sprintf("%.6f", $exit - $entry) . "s";
  186. $line =~ s/%content%/$content/g;
  187. print($line);
  188. foreach my $content (@{$node->{content}})
  189. {
  190. $content = escape($content);
  191. $content = " " if length($content) == 0;
  192. my $line = $node_content;
  193. $line =~ s/%content%/$content/g;
  194. print($line);
  195. }
  196. print($node_footer);
  197. }
  198. }
  199. sub print_anon_nodes()
  200. {
  201. print($anon_nodes_header);
  202. foreach my $name (sort keys %anon_nodes)
  203. {
  204. my $label = $anon_nodes{$name};
  205. my $line = $anon_node;
  206. $line =~ s/%name%/$name/g;
  207. $line =~ s/%label%/$label/g;
  208. print($line);
  209. }
  210. print($edges_footer);
  211. }
  212. sub print_edges()
  213. {
  214. print($edges_header);
  215. foreach my $e (@edges)
  216. {
  217. my $begin = $e->{begin};
  218. my $end = $e->{end};
  219. my $label = $e->{label};
  220. my $line = $edge;
  221. $line =~ s/%begin%/$begin/g;
  222. $line =~ s/%end%/$end/g;
  223. $line =~ s/%label%/$label/g;
  224. print($line);
  225. }
  226. print($edges_footer);
  227. }
  228. sub print_node_order()
  229. {
  230. my $prev = "";
  231. print($node_order_header);
  232. foreach my $name (sort keys %all_nodes)
  233. {
  234. if ($prev ne "")
  235. {
  236. my $begin = $prev;
  237. my $end = $all_nodes{$name};
  238. my $line = $node_order;
  239. $line =~ s/%begin%/$begin/g;
  240. $line =~ s/%end%/$end/g;
  241. print($line);
  242. }
  243. $prev = $all_nodes{$name};
  244. }
  245. print($node_order_footer);
  246. }
  247. sub generate_dot()
  248. {
  249. print($graph_header);
  250. print_nodes();
  251. print_anon_nodes();
  252. print_edges();
  253. print_node_order();
  254. print($graph_footer);
  255. }
  256. #-------------------------------------------------------------------------------
  257. parse_debug_output();
  258. generate_dot();