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;
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;
133 use Data::Dump qw(pp);
134 local $jit_tree->{children};
135 pp(dclone($jit_tree)); # dclone to avoid stringification
138 $self->render_json($jit_tree);
142 sub _fetch_node_tree {
143 my ($id, $depth) = @_;
145 warn "#$id fetching\n"
147 my $node = SizeMe->selectrow_hashref("select * from node where id = ?", undef, $id)
148 or die "Node '$id' not found"; # shouldn't die
149 $node->{$_} += 0 for (qw(child_count kids_node_count kids_size self_size));
150 $node->{leaves} = $j->decode(delete $node->{leaves_json});
151 $node->{attr} = $j->decode(delete $node->{attr_json});
152 $node->{name} .= "->" if $node->{type} == 2 && $node->{name};
154 if ($node->{child_ids}) {
155 my @child_ids = split /,/, $node->{child_ids};
158 # if this node has only one child then we merge that child into this node
159 # this makes the treemap more usable
161 # && $node->{type} == 2 # only collapse links XXX
163 warn "#$id fetch only child $child_ids[0]\n"
165 my $child = _fetch_node_tree($child_ids[0], $depth); # same depth
166 # merge node into child
167 # XXX id, depth, parent_id
168 warn "Merged $node->{name} (#$node->{id} d$node->{depth}) with only child $child->{name} #$child->{id}\n"
170 $child->{name} = "$node->{name} $child->{name}";
171 $child->{$_} += $node->{$_} for (qw(self_size));
172 $child->{$_} = $node->{$_} for (qw(parent_id));
174 $child->{title} = join " ", grep { defined && length } $child->{title}, $node->{title};
175 #warn "Titled $child->{title}" if $child->{title};
177 # somewhat hackish attribute merging
178 for my $attr_type (keys %{ $node->{attr} }) {
179 my $src = $node->{attr}{$attr_type};
180 if (ref $src eq 'HASH') { # eg NPattr_NAME: {attr}{1}{$name} = $value
181 my $dst = $child->{attr}{$attr_type} ||= {};
182 for my $k (keys %$src) {
183 warn "Node $child->{id} attr $attr_type:$k=$dst->{$k} overwritten by $src->{$k}\n"
184 if defined $dst->{$k};
185 $dst->{$k} = $src->{$k};
188 elsif (ref $src eq 'ARRAY') { # eg NPattr_PADNAME: {attr}{2}[$val] = $name
189 my $dst = $child->{attr}{$attr_type} ||= [];
191 while (--$idx >= 0) {
192 warn "Node $child->{id} attr $attr_type:$idx=$dst->[$idx] overwritten by $src->[$idx]\n"
193 if defined $dst->[$idx];
194 $dst->[$idx] = $src->[$idx];
197 else { # assume scalar
198 warn "Node $child->{id} attr $attr_type=$child->{attr}{$attr_type} overwritten by $src\n"
199 if exists $child->{attr}{$attr_type};
200 $child->{attr}{$attr_type} = $src;
204 $child->{leaves}{$_} += $node->{leaves}{$_}
205 for keys %{ $node->{leaves} };
207 $child->{_ids_merged} .= ",$node->{id}";
208 my @child_ids = split /,/, $node->{child_ids};
209 $child->{child_count} = @child_ids;
211 $node = $child; # use the merged child as this node
213 if ($depth) { # recurse to required depth
214 $children = [ map { _fetch_node_tree($_, $depth-1) } @child_ids ];
215 $node->{children} = $children;
216 $node->{child_count} = @$children;
223 sub _transform_node_tree { # recurse depth first
224 my ($node, $transform) = @_;
225 if (my $children = $node->{children}) {
226 $_ = _transform_node_tree($_, $transform) for @$children;
228 return $transform->($node);
234 { # just to reserve the namespace for future use
235 package Devel::SizeMe::Graph;
243 Welcome to the Mojolicious real-time web framework!
245 @@ layouts/default.html.ep
248 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
249 <title>Perl Memory Treemap</title>
252 <link type="text/css" href="css/base.css" rel="stylesheet" />
253 <link type="text/css" href="css/Treemap.css" rel="stylesheet" />
255 <!--[if IE]><script language="javascript" type="text/javascript" src="excanvas.js"></script><![endif]-->
257 <!-- JIT Library File -->
258 <script language="javascript" type="text/javascript" src="jit-yc.js"></script>
259 <script language="javascript" type="text/javascript" src="jquery-1.8.1-min.js"></script>
260 <script language="javascript" type="text/javascript" src="sprintf.js"></script>
261 <script language="javascript" type="text/javascript" src="treemap.js"></script>
264 <body onload="init();">
268 <div id="left-container">
271 <h4> Perl Memory TreeMap </h4>
272 Click on a node to zoom in.<br /><br />
275 <a id="back" href="#" class="theme button white">Go to Parent</a>
278 <label for="logarea"> Logarithmic scale
279 <input type=checkbox id="logarea" name="logarea">
284 <div id="center-container">
285 <div id="infovis"></div>
288 <div id="right-container">
289 <div id="inner-details"></div>