123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299 |
- #!/usr/bin/perl -w
- #
- # handlerviz.pl
- # ~~~~~~~~~~~~~
- #
- # A visualisation tool for post-processing the debug output generated by
- # Asio-based programs. Programs write this output to the standard error stream
- # when compiled with the define `BOOST_ASIO_ENABLE_HANDLER_TRACKING'.
- #
- # This tool generates output intended for use with the GraphViz tool `dot'. For
- # example, to convert output to a PNG image, use:
- #
- # perl handlerviz.pl < output.txt | dot -Tpng > output.png
- #
- # To convert to a PDF file, use:
- #
- # perl handlerviz.pl < output.txt | dot -Tpdf > output.pdf
- #
- # Copyright (c) 2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
- #
- # Distributed under the Boost Software License, Version 1.0. (See accompanying
- # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- #
- use strict;
- my %nodes = ();
- my @edges = ();
- my %anon_nodes = ();
- my $anon_id = 0;
- my %all_nodes = ();
- #-------------------------------------------------------------------------------
- # Parse the debugging output and populate the nodes and edges.
- sub parse_debug_output()
- {
- while (my $line = <>)
- {
- chomp($line);
- if ($line =~ /\@asio\|([^|]*)\|([^|]*)\|(.*)$/)
- {
- my $timestamp = $1;
- my $action = $2;
- my $description = $3;
- # Handler creation.
- if ($action =~ /^([0-9]+)\*([0-9]+)$/)
- {
- my $begin = $1;
- my $end = $2;
- my $label = $description;
- $label =~ s/\./\\n/g;
- if ($begin eq "0")
- {
- $begin = "a" . $anon_id++;
- $anon_nodes{$begin} = $timestamp;
- $all_nodes{"$timestamp-$begin"} = $begin;
- }
- my %edge = ( begin=>$begin, end=>$end, label=>$label );
- push(@edges, \%edge);
- }
- # Begin handler invocation.
- elsif ($action =~ /^>([0-9]+)$/)
- {
- my %new_node = ( label=>$description, entry=>$timestamp );
- $new_node{content} = ();
- $nodes{$1} = \%new_node;
- $all_nodes{"$timestamp-$1"} = $1;
- }
- # End handler invocation.
- elsif ($action =~ /^<([0-9]+)$/)
- {
- $nodes{$1}->{exit} = $timestamp;
- }
- # Handler threw exception.
- elsif ($action =~ /^!([0-9]+)$/)
- {
- push(@{$nodes{$1}->{content}}, "exception");
- }
- # Handler was destroyed without being invoked.
- elsif ($action =~ /^~([0-9]+)$/)
- {
- my %new_node = ( label=>"$timestamp destroyed" );
- $new_node{content} = ();
- $nodes{$1} = \%new_node;
- $all_nodes{"$timestamp-$1"} = $1;
- }
- # Handler performed some operation.
- elsif ($action =~ /^([0-9]+)$/)
- {
- if ($1 eq "0")
- {
- my $id = "a" . $anon_id++;
- $anon_nodes{$id} = "$timestamp\\l$description";
- $all_nodes{"$timestamp-$id"} = $id;
- }
- else
- {
- push(@{$nodes{$1}->{content}}, "$description");
- }
- }
- }
- }
- }
- #-------------------------------------------------------------------------------
- # Helper function to convert a string to escaped HTML text.
- sub escape($)
- {
- my $text = shift;
- $text =~ s/&/\&\;/g;
- $text =~ s/</\<\;/g;
- $text =~ s/>/\>\;/g;
- $text =~ s/\t/ /g;
- return $text;
- }
- #-------------------------------------------------------------------------------
- # Templates for dot output.
- my $graph_header = <<"EOF";
- /* Generated by asioviz.pl */
- digraph G
- {
- graph [ nodesep="1" ];
- node [ shape="box", fontsize="9" ];
- edge [ arrowtail="dot", fontsize="9" ];
- EOF
- my $graph_footer = <<"EOF";
- }
- EOF
- my $node_header = <<"EOF";
- "%name%"
- [
- label=<<table border="0" cellspacing="0">
- <tr><td align="left" bgcolor="gray" border="0">%label%</td></tr>
- EOF
- my $node_footer = <<"EOF";
- </table>>
- ]
- EOF
- my $node_content = <<"EOF";
- <tr><td align="left" bgcolor="white" border="0">
- <font face="mono" point-size="9">%content%</font>
- </td></tr>
- EOF
- my $anon_nodes_header = <<"EOF";
- {
- node [ shape="record" ];
- EOF
- my $anon_nodes_footer = <<"EOF";
- }
- EOF
- my $anon_node = <<"EOF";
- "%name%" [ label="%label%", color="gray" ];
- EOF
- my $edges_header = <<"EOF";
- {
- edge [ style="dashed", arrowhead="open", weight="100" ];
- EOF
- my $edges_footer = <<"EOF";
- }
- EOF
- my $edge = <<"EOF";
- "%begin%" -> "%end%" [ label="%label%" ]
- EOF
- my $node_order_header = <<"EOF";
- {
- edge [ style="invis", weight="1" ];
- EOF
- my $node_order_footer = <<"EOF";
- }
- EOF
- my $node_order = <<"EOF";
- "%begin%" -> "%end%"
- EOF
- #-------------------------------------------------------------------------------
- # Generate dot output from the nodes and edges.
- sub print_nodes()
- {
- foreach my $name (sort keys %nodes)
- {
- my $node = $nodes{$name};
- my $entry = $node->{entry};
- my $exit = $node->{exit};
- my $label = escape($node->{label});
- my $header = $node_header;
- $header =~ s/%name%/$name/g;
- $header =~ s/%label%/$label/g;
- print($header);
- my $line = $node_content;
- my $content = $entry . " + " . sprintf("%.6f", $exit - $entry) . "s";
- $line =~ s/%content%/$content/g;
- print($line);
- foreach my $content (@{$node->{content}})
- {
- $content = escape($content);
- $content = " " if length($content) == 0;
- my $line = $node_content;
- $line =~ s/%content%/$content/g;
- print($line);
- }
- print($node_footer);
- }
- }
- sub print_anon_nodes()
- {
- print($anon_nodes_header);
- foreach my $name (sort keys %anon_nodes)
- {
- my $label = $anon_nodes{$name};
- my $line = $anon_node;
- $line =~ s/%name%/$name/g;
- $line =~ s/%label%/$label/g;
- print($line);
- }
- print($edges_footer);
- }
- sub print_edges()
- {
- print($edges_header);
- foreach my $e (@edges)
- {
- my $begin = $e->{begin};
- my $end = $e->{end};
- my $label = $e->{label};
- my $line = $edge;
- $line =~ s/%begin%/$begin/g;
- $line =~ s/%end%/$end/g;
- $line =~ s/%label%/$label/g;
- print($line);
- }
- print($edges_footer);
- }
- sub print_node_order()
- {
- my $prev = "";
- print($node_order_header);
- foreach my $name (sort keys %all_nodes)
- {
- if ($prev ne "")
- {
- my $begin = $prev;
- my $end = $all_nodes{$name};
- my $line = $node_order;
- $line =~ s/%begin%/$begin/g;
- $line =~ s/%end%/$end/g;
- print($line);
- }
- $prev = $all_nodes{$name};
- }
- print($node_order_footer);
- }
- sub generate_dot()
- {
- print($graph_header);
- print_nodes();
- print_anon_nodes();
- print_edges();
- print_node_order();
- print($graph_footer);
- }
- #-------------------------------------------------------------------------------
- parse_debug_output();
- generate_dot();
|