4 die qq{$0 requires Mojolicious::Lite, which isn't installed.
6 Currently requires Mojolicious::Lite which isn't available for perl 5.8.
7 If this affects you you can run Devel::SizeMe with your normal perl and
8 run sizeme_graph.pl with a different perl, perhaps on a different machine.
10 unless eval "require Mojolicious::Lite";
15 sizeme_graph.pl - web server providing an interactive treemap of Devel::SizeMe data
19 sizeme_graph.pl --db sizeme.db daemon
21 sizeme_graph.pl daemon # same as above
23 Then open a web browser on http://127.0.0.1:3000
27 Reads a database created by sizeme_store.pl and provides a web interface with
28 an interactive treemap of the data.
30 Currently requires Mojolicious::Lite which isn't available for perl 5.8.
31 If this affects you you can run Devel::SizeMe with your normal perl and
32 run sizeme_graph.pl with a different perl, perhaps on a different machine.
36 Current implementation is all very alpha and rather hackish.
38 Split out the db and tree code into a separate module.
40 Use a history management library so the back button works and we can have
41 links to specific nodes.
43 Better tool-tip and/or add a scrollable information area below the treemap
44 that could contain details and links.
46 Make the treemap resize to fit the browser window (as NYTProf does).
48 Protect against nodes with thousands of children
49 perhaps replace all with single merged child that has no children itself
50 but just a warning as a title.
52 Implement other visualizations, such as a space-tree
53 http://thejit.org/static/v20/Jit/Examples/Spacetree/example2.html
60 use Mojolicious::Lite; # possibly needs v3
63 use Storable qw(dclone);
65 use Devel::SizeMe::Graph;
69 my $j = JSON::XS->new;
72 'db=s' => \(my $opt_db = 'sizeme.db'),
73 'showid!' => \my $opt_showid,
74 'debug!' => \my $opt_debug,
77 die "Can't open $opt_db: $!\n" unless -r $opt_db;
78 warn "Reading $opt_db\n";
80 # XXX currently uses ORLite but doesn't actually make use of it in any useful way
85 #user_version => 1, # XXX
89 my $static_dir = $INC{'Devel/SizeMe/Graph.pm'} or die 'panic';
90 $static_dir =~ s:\.pm$:/static:;
91 die "panic $static_dir" unless -d $static_dir;
92 push @{ app->static->paths}, $static_dir;
95 # Documentation browser under "/perldoc"
100 $self->render('index');
104 # /jit_tree are AJAX requests from the treemap visualization
105 get '/jit_tree/:id/:depth' => sub {
108 my $id = $self->stash('id');
109 my $depth = $self->stash('depth');
111 # hack, would be best done on the client side
112 my $logarea = (defined $self->param('logarea'))
113 ? $self->param('logarea')
114 : Mojo::URL->new($self->req->headers->referrer)->query->param('logarea');
116 my $node_tree = _fetch_node_tree($id, $depth);
117 my $jit_tree = _transform_node_tree($node_tree, sub {
119 my $children = delete $node->{children}; # XXX edits the src tree
120 my $area = $node->{self_size}+$node->{kids_size};
121 $node->{'$area'} = ($logarea) ? log($area) : $area; # XXX move to jit js
124 name => ($node->{title} || $node->{name}).($opt_showid ? " #$node->{id}" : ""),
127 $jit_node->{children} = $children if $children;
132 #use Data::Dump qw(pp);
133 local $jit_tree->{children};
134 Dwarn(dclone($jit_tree)); # dclone to avoid stringification
137 $self->render_json($jit_tree);
141 sub _fetch_node_tree {
142 my ($id, $depth) = @_;
144 warn "#$id fetching\n"
146 my $node = SizeMe->selectrow_hashref("select * from node where id = ?", undef, $id)
147 or die "Node '$id' not found"; # shouldn't die
148 $node->{$_} += 0 for (qw(child_count kids_node_count kids_size self_size));
149 $node->{leaves} = $j->decode(delete $node->{leaves_json});
150 $node->{attr} = $j->decode(delete $node->{attr_json});
151 $node->{name} .= "->" if $node->{type} == 2 && $node->{name};
153 if ($node->{child_ids}) {
154 my @child_ids = split /,/, $node->{child_ids};
157 # if this node has only one child then we merge that child into this node
158 # this makes the treemap more usable
160 # && $node->{type} == 2 # only collapse links XXX
162 warn "#$id fetch only child $child_ids[0]\n"
164 my $child = _fetch_node_tree($child_ids[0], $depth); # same depth
165 # merge node into child
166 # XXX id, depth, parent_id
167 warn "Merged $node->{name} (#$node->{id} d$node->{depth}) with only child $child->{name} #$child->{id}\n"
169 $child->{name} = "$node->{name} $child->{name}";
170 $child->{$_} += $node->{$_} for (qw(self_size));
171 $child->{$_} = $node->{$_} for (qw(parent_id));
173 $child->{title} = join " ", grep { defined && length } $child->{title}, $node->{title};
174 #warn "Titled $child->{title}" if $child->{title};
176 # somewhat hackish attribute merging
177 for my $attr_type (keys %{ $node->{attr} }) {
178 my $src = $node->{attr}{$attr_type};
179 if (ref $src eq 'HASH') { # eg NPattr_NAME: {attr}{1}{$name} = $value
180 my $dst = $child->{attr}{$attr_type} ||= {};
181 for my $k (keys %$src) {
182 warn "Node $child->{id} attr $attr_type:$k=$dst->{$k} overwritten by $src->{$k}\n"
183 if defined $dst->{$k};
184 $dst->{$k} = $src->{$k};
187 elsif (ref $src eq 'ARRAY') { # eg NPattr_PADNAME: {attr}{2}[$val] = $name
188 my $dst = $child->{attr}{$attr_type} ||= [];
190 while (--$idx >= 0) {
191 warn "Node $child->{id} attr $attr_type:$idx=$dst->[$idx] overwritten by $src->[$idx]\n"
192 if defined $dst->[$idx];
193 $dst->[$idx] = $src->[$idx];
196 else { # assume scalar
197 warn "Node $child->{id} attr $attr_type=$child->{attr}{$attr_type} overwritten by $src\n"
198 if exists $child->{attr}{$attr_type};
199 $child->{attr}{$attr_type} = $src;
203 $child->{leaves}{$_} += $node->{leaves}{$_}
204 for keys %{ $node->{leaves} };
206 $child->{_ids_merged} .= ",$node->{id}";
207 my @child_ids = split /,/, $node->{child_ids};
208 $child->{child_count} = @child_ids;
210 $node = $child; # use the merged child as this node
212 if ($depth) { # recurse to required depth
213 $children = [ map { _fetch_node_tree($_, $depth-1) } @child_ids ];
214 $node->{children} = $children;
215 $node->{child_count} = @$children;
222 sub _transform_node_tree { # recurse depth first
223 my ($node, $transform) = @_;
224 if (my $children = $node->{children}) {
225 $_ = _transform_node_tree($_, $transform) for @$children;
227 return $transform->($node);
233 { # just to reserve the namespace for future use
234 package Devel::SizeMe::Graph;
242 Welcome to the Mojolicious real-time web framework!
244 @@ layouts/default.html.ep
247 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
248 <title>Perl Memory Treemap</title>
251 <link type="text/css" href="css/base.css" rel="stylesheet" />
252 <link type="text/css" href="css/Treemap.css" rel="stylesheet" />
254 <!--[if IE]><script language="javascript" type="text/javascript" src="excanvas.js"></script><![endif]-->
256 <!-- JIT Library File -->
257 <script language="javascript" type="text/javascript" src="jit-yc.js"></script>
258 <script language="javascript" type="text/javascript" src="jquery-1.8.1-min.js"></script>
259 <script language="javascript" type="text/javascript" src="sprintf.js"></script>
260 <script language="javascript" type="text/javascript" src="treemap.js"></script>
263 <body onload="init();">
267 <div id="left-container">
270 <h4> Perl Memory TreeMap </h4>
271 Click on a node to zoom in.<br /><br />
274 <a id="back" href="#" class="theme button white">Go to Parent</a>
277 <label for="logarea"> Logarithmic scale
278 <input type=checkbox id="logarea" name="logarea">
283 <div id="center-container">
284 <div id="infovis"></div>
287 <div id="right-container">
288 <div id="inner-details"></div>