Mega rename to Devel::Memory commit
[p5sagit/Devel-Size.git] / static / dmemview.pl
CommitLineData
b2fc39a5 1#!/usr/bin/env perl
2
3use strict;
4use warnings;
5
6use Mojolicious::Lite;
d3b8a135 7use JSON::XS;
e8f4c506 8use Getopt::Long;
945e07d7 9use Storable qw(dclone);
ee6e37bf 10use Devel::Dwarn;
e8f4c506 11
d3b8a135 12=pod NOTE
13
14 Needs to be run from the static/. directory.
15 For example:
16
17 ./dmemview.pl daemon
18
7aa9284e 19=pod TODO
20
d3b8a135 21 Move all the static files into the DATA section of ths script so the script
22 is entirely self-contained and doesn't need any static files installed.
23 Or, work out how to install the static files and reference them from the script.
24
25 Remove ORLite (for now)
26
27 Make the treemap resize to fit the browser window (as NYTProf does).
28
7aa9284e 29 Protect against nodes with thousands of children
d3b8a135 30 perhaps replace all with single merged child that has no children itself
31 but just a warning as a title.
7aa9284e 32
33=cut
34
e8f4c506 35GetOptions(
d3b8a135 36 'db=s' => \(my $opt_db = '../dmemtree.db'),
945e07d7 37 'debug!' => \my $opt_debug,
e8f4c506 38) or exit 1;
b2fc39a5 39
945e07d7 40#warn "Reading from $opt_db\n";
41
d3b8a135 42# XXX currently uses ORLite but doesn't actually make use of it in any useful way
43# should be removed and replaced with plain DBI till we have an obvious need for it
b2fc39a5 44use ORLite {
d3b8a135 45 file => '../dmemtree.db',
b2fc39a5 46 package => "MemView",
47 #user_version => 1,
48 readonly => 1,
49 #unicode => 1,
50};
51
f60f09e5 52my $j = JSON::XS->new;
53
b2fc39a5 54# Documentation browser under "/perldoc"
55plugin 'PODRenderer';
56
57get '/' => sub {
58 my $self = shift;
59 $self->render('index');
60};
61
d3b8a135 62
63# /jit_tree are AJAX requests from the treemap visualization
b2fc39a5 64get '/jit_tree/:id/:depth' => sub {
65 my $self = shift;
e8f4c506 66
b2fc39a5 67 my $id = $self->stash('id');
68 my $depth = $self->stash('depth');
ee6e37bf 69
d3b8a135 70 # hack, would be best done on the client side
ee6e37bf 71 my $logarea = (defined $self->param('logarea'))
72 ? $self->param('logarea')
73 : Mojo::URL->new($self->req->headers->referrer)->query->param('logarea');
74
875c1073 75 my $node_tree = _fetch_node_tree($id, $depth);
76 my $jit_tree = _transform_node_tree($node_tree, sub {
77 my ($node) = @_;
78 my $children = delete $node->{children}; # XXX edits the src tree
e8f4c506 79 my $area = $node->{self_size}+$node->{kids_size};
945e07d7 80 $node->{'$area'} = ($logarea) ? log($area) : $area; # XXX move to jit js
bb66f8a1 81 my $jit_node = {
82 id => $node->{id},
e8f4c506 83 name => $node->{title} || $node->{name},
bb66f8a1 84 data => $node,
85 };
86 $jit_node->{children} = $children if $children;
87 return $jit_node;
b2fc39a5 88 });
ee6e37bf 89
d3b8a135 90 if(1){ # debug
ee6e37bf 91 use Devel::Dwarn;
92 use Data::Dump qw(pp);
93 local $jit_tree->{children};
94 pp(dclone($jit_tree)); # dclone to avoid stringification
95 }
96
b2fc39a5 97 $self->render_json($jit_tree);
98};
99
d3b8a135 100
875c1073 101sub _fetch_node_tree {
102 my ($id, $depth) = @_;
d3b8a135 103
875c1073 104 my $node = MemView->selectrow_hashref("select * from node where id = ?", undef, $id)
d3b8a135 105 or die "Node '$id' not found"; # shouldn't die
945e07d7 106 $node->{$_} += 0 for (qw(child_count kids_node_count kids_size self_size));
e78b28ca 107 $node->{leaves} = $j->decode(delete $node->{leaves_json});
e8f4c506 108 $node->{attr} = $j->decode(delete $node->{attr_json});
47a0d085 109 $node->{name} .= "->" if $node->{type} == 2 && $node->{name};
e78b28ca 110
5a78486c 111 if ($node->{child_ids}) {
112 my @child_ids = split /,/, $node->{child_ids};
875c1073 113 my $children;
d3b8a135 114
115 # if this node has only one child then we merge that child into this node
116 # this makes the treemap more usable
ee6e37bf 117 if (@child_ids == 1
d3b8a135 118 && $node->{type} == 2 # currently we only collapse links XXX
ee6e37bf 119 ) {
5a78486c 120 my $child = _fetch_node_tree($child_ids[0], $depth); # same depth
f9d8678b 121 # merge node into child
5a78486c 122 # XXX id, depth, parent_id
945e07d7 123 warn "Merged $node->{name} #$node->{id} with only child $child->{name} #$child->{id}\n"
124 if $opt_debug;
e74c1998 125 $child->{name} = "$node->{name} $child->{name}";
f9d8678b 126 $child->{$_} += $node->{$_} for (qw(self_size));
5a78486c 127 $child->{$_} = $node->{$_} for (qw(parent_id));
e8f4c506 128
e74c1998 129 $child->{title} = join " ", grep { defined && length } $child->{title}, $node->{title};
945e07d7 130 #warn "Titled $child->{title}" if $child->{title};
e8f4c506 131
d3b8a135 132 # somewhat hackish attribute merging
e8f4c506 133 for my $attr_type (keys %{ $node->{attr} }) {
134 my $src = $node->{attr}{$attr_type};
135 if (ref $src eq 'HASH') { # eg NPattr_NAME: {attr}{1}{$name} = $value
136 my $dst = $child->{attr}{$attr_type} ||= {};
137 for my $k (keys %$src) {
138 warn "Node $child->{id} attr $attr_type:$k=$dst->{$k} overwritten by $src->{$k}\n"
139 if defined $dst->{$k};
140 $dst->{$k} = $src->{$k};
141 }
142 }
47a0d085 143 elsif (ref $src eq 'ARRAY') { # eg NPattr_PADNAME: {attr}{2}[$val] = $name
e8f4c506 144 my $dst = $child->{attr}{$attr_type} ||= [];
145 my $idx = @$src;
146 while (--$idx >= 0) {
147 warn "Node $child->{id} attr $attr_type:$idx=$dst->[$idx] overwritten by $src->[$idx]\n"
148 if defined $dst->[$idx];
149 $dst->[$idx] = $src->[$idx];
150 }
151 }
47a0d085 152 else { # assume scalar
153 warn "Node $child->{id} attr $attr_type=$child->{attr}{$attr_type} overwritten by $src\n"
154 if exists $child->{attr}{$attr_type};
155 $child->{attr}{$attr_type} = $src;
156 }
e8f4c506 157 }
158
e78b28ca 159 $child->{leaves}{$_} += $node->{leaves}{$_}
160 for keys %{ $node->{leaves} };
5a78486c 161
162 $child->{_ids_merged} .= ",$node->{id}";
163 my @child_ids = split /,/, $node->{child_ids};
164 $child->{child_count} = @child_ids;
165
d3b8a135 166 $node = $child; # use the merged child as this node
875c1073 167 }
d3b8a135 168 # XXX this elsif() should possibly be a plain if(), maybe with tweaks to the above
169 # because we want to allow the recursion
170 elsif ($depth) { # recurse to required depth
5a78486c 171 $children = [ map { _fetch_node_tree($_, $depth-1) } @child_ids ];
f9d8678b 172 $node->{children} = $children;
173 $node->{child_count} = @$children;
875c1073 174 }
b2fc39a5 175 }
b2fc39a5 176 return $node;
177}
178
d3b8a135 179
f9d8678b 180sub _transform_node_tree { # recurse depth first
875c1073 181 my ($node, $transform) = @_;
182 if (my $children = $node->{children}) {
183 $_ = _transform_node_tree($_, $transform) for @$children;
184 }
185 return $transform->($node);
186}
187
b2fc39a5 188
189app->start;
d3b8a135 190
b2fc39a5 191__DATA__
192@@ index.html.ep
193% layout 'default';
194% title 'Welcome';
195Welcome to the Mojolicious real-time web framework!
196
197@@ layouts/default.html.ep
198<!DOCTYPE html>
199<head>
200<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
bb66f8a1 201<title>Perl Memory Treemap</title>
b2fc39a5 202
203<!-- CSS Files -->
204<link type="text/css" href="css/base.css" rel="stylesheet" />
205<link type="text/css" href="css/Treemap.css" rel="stylesheet" />
206
207<!--[if IE]><script language="javascript" type="text/javascript" src="excanvas.js"></script><![endif]-->
208
209<!-- JIT Library File -->
210<script language="javascript" type="text/javascript" src="jit.js"></script>
2a627489 211<script language="javascript" type="text/javascript" src="jquery-1.8.1-min.js"></script>
b2fc39a5 212
bb66f8a1 213<script language="javascript" type="text/javascript" src="sprintf.js"></script>
d3b8a135 214<script language="javascript" type="text/javascript" src="dmemtreemap.js"></script>
b2fc39a5 215</head>
216
217<body onload="init();">
218<div id="container">
219
220<div id="left-container">
221
b2fc39a5 222<div class="text">
223<h4>
bb66f8a1 224Perl Memory TreeMap
b2fc39a5 225</h4>
47a0d085 226 Click on a node to zoom in.<br /><br />
b2fc39a5 227</div>
228
229<a id="back" href="#" class="theme button white">Go to Parent</a>
e8f4c506 230
231<br />
232<form name=params>
233<label for="logarea">&nbsp;Logarithmic scale
234<input type=checkbox id="logarea" name="logarea">
235</form>
236
b2fc39a5 237</div>
238
239<div id="center-container">
240 <div id="infovis"></div>
241</div>
242
243<div id="right-container">
244
245<div id="inner-details"></div>
246
247</div>
248
249<div id="log"></div>
250</div>
251</body>
252</html>