9 use Storable qw(dclone);
11 use Devel::SizeMe::Graph;
17 Needs to be run from the static/. directory.
20 sizeme_graph.pl daemon
24 Move all the static files into the DATA section of ths script so the script
25 is entirely self-contained and doesn't need any static files installed.
26 Or, work out how to install the static files and reference them from the script.
28 Make the treemap resize to fit the browser window (as NYTProf does).
30 Protect against nodes with thousands of children
31 perhaps replace all with single merged child that has no children itself
32 but just a warning as a title.
36 my $j = JSON::XS->new;
39 'db=s' => \(my $opt_db = 'sizeme.db'),
40 'showid!' => \my $opt_showid,
41 'debug!' => \my $opt_debug,
44 die "Can't open $opt_db: $!\n" unless -r $opt_db;
45 warn "Reading $opt_db\n";
47 # XXX currently uses ORLite but doesn't actually make use of it in any useful way
52 #user_version => 1, # XXX
56 my $static_dir = $INC{'Devel/SizeMe/Graph.pm'} or die 'panic';
57 $static_dir =~ s:\.pm$:/static:;
58 die "panic $static_dir" unless -d $static_dir;
59 push @{ app->static->paths}, $static_dir;
62 # Documentation browser under "/perldoc"
67 $self->render('index');
71 # /jit_tree are AJAX requests from the treemap visualization
72 get '/jit_tree/:id/:depth' => sub {
75 my $id = $self->stash('id');
76 my $depth = $self->stash('depth');
78 # hack, would be best done on the client side
79 my $logarea = (defined $self->param('logarea'))
80 ? $self->param('logarea')
81 : Mojo::URL->new($self->req->headers->referrer)->query->param('logarea');
83 my $node_tree = _fetch_node_tree($id, $depth);
84 my $jit_tree = _transform_node_tree($node_tree, sub {
86 my $children = delete $node->{children}; # XXX edits the src tree
87 my $area = $node->{self_size}+$node->{kids_size};
88 $node->{'$area'} = ($logarea) ? log($area) : $area; # XXX move to jit js
91 name => ($node->{title} || $node->{name}).($opt_showid ? " #$node->{id}" : ""),
94 $jit_node->{children} = $children if $children;
100 use Data::Dump qw(pp);
101 local $jit_tree->{children};
102 pp(dclone($jit_tree)); # dclone to avoid stringification
105 $self->render_json($jit_tree);
109 sub _fetch_node_tree {
110 my ($id, $depth) = @_;
112 warn "#$id fetching\n"
114 my $node = SizeMe->selectrow_hashref("select * from node where id = ?", undef, $id)
115 or die "Node '$id' not found"; # shouldn't die
116 $node->{$_} += 0 for (qw(child_count kids_node_count kids_size self_size));
117 $node->{leaves} = $j->decode(delete $node->{leaves_json});
118 $node->{attr} = $j->decode(delete $node->{attr_json});
119 $node->{name} .= "->" if $node->{type} == 2 && $node->{name};
121 if ($node->{child_ids}) {
122 my @child_ids = split /,/, $node->{child_ids};
125 # if this node has only one child then we merge that child into this node
126 # this makes the treemap more usable
128 # && $node->{type} == 2 # only collapse links XXX
130 warn "#$id fetch only child $child_ids[0]\n"
132 my $child = _fetch_node_tree($child_ids[0], $depth); # same depth
133 # merge node into child
134 # XXX id, depth, parent_id
135 warn "Merged $node->{name} (#$node->{id} d$node->{depth}) with only child $child->{name} #$child->{id}\n"
137 $child->{name} = "$node->{name} $child->{name}";
138 $child->{$_} += $node->{$_} for (qw(self_size));
139 $child->{$_} = $node->{$_} for (qw(parent_id));
141 $child->{title} = join " ", grep { defined && length } $child->{title}, $node->{title};
142 #warn "Titled $child->{title}" if $child->{title};
144 # somewhat hackish attribute merging
145 for my $attr_type (keys %{ $node->{attr} }) {
146 my $src = $node->{attr}{$attr_type};
147 if (ref $src eq 'HASH') { # eg NPattr_NAME: {attr}{1}{$name} = $value
148 my $dst = $child->{attr}{$attr_type} ||= {};
149 for my $k (keys %$src) {
150 warn "Node $child->{id} attr $attr_type:$k=$dst->{$k} overwritten by $src->{$k}\n"
151 if defined $dst->{$k};
152 $dst->{$k} = $src->{$k};
155 elsif (ref $src eq 'ARRAY') { # eg NPattr_PADNAME: {attr}{2}[$val] = $name
156 my $dst = $child->{attr}{$attr_type} ||= [];
158 while (--$idx >= 0) {
159 warn "Node $child->{id} attr $attr_type:$idx=$dst->[$idx] overwritten by $src->[$idx]\n"
160 if defined $dst->[$idx];
161 $dst->[$idx] = $src->[$idx];
164 else { # assume scalar
165 warn "Node $child->{id} attr $attr_type=$child->{attr}{$attr_type} overwritten by $src\n"
166 if exists $child->{attr}{$attr_type};
167 $child->{attr}{$attr_type} = $src;
171 $child->{leaves}{$_} += $node->{leaves}{$_}
172 for keys %{ $node->{leaves} };
174 $child->{_ids_merged} .= ",$node->{id}";
175 my @child_ids = split /,/, $node->{child_ids};
176 $child->{child_count} = @child_ids;
178 $node = $child; # use the merged child as this node
180 if ($depth) { # recurse to required depth
181 $children = [ map { _fetch_node_tree($_, $depth-1) } @child_ids ];
182 $node->{children} = $children;
183 $node->{child_count} = @$children;
190 sub _transform_node_tree { # recurse depth first
191 my ($node, $transform) = @_;
192 if (my $children = $node->{children}) {
193 $_ = _transform_node_tree($_, $transform) for @$children;
195 return $transform->($node);
205 Welcome to the Mojolicious real-time web framework!
207 @@ layouts/default.html.ep
210 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
211 <title>Perl Memory Treemap</title>
214 <link type="text/css" href="css/base.css" rel="stylesheet" />
215 <link type="text/css" href="css/Treemap.css" rel="stylesheet" />
217 <!--[if IE]><script language="javascript" type="text/javascript" src="excanvas.js"></script><![endif]-->
219 <!-- JIT Library File -->
220 <script language="javascript" type="text/javascript" src="jit-yc.js"></script>
221 <script language="javascript" type="text/javascript" src="jquery-1.8.1-min.js"></script>
222 <script language="javascript" type="text/javascript" src="sprintf.js"></script>
223 <script language="javascript" type="text/javascript" src="treemap.js"></script>
226 <body onload="init();">
230 <div id="left-container">
233 <h4> Perl Memory TreeMap </h4>
234 Click on a node to zoom in.<br /><br />
237 <a id="back" href="#" class="theme button white">Go to Parent</a>
240 <label for="logarea"> Logarithmic scale
241 <input type=checkbox id="logarea" name="logarea">
246 <div id="center-container">
247 <div id="infovis"></div>
250 <div id="right-container">
251 <div id="inner-details"></div>