9 use Storable qw(dclone);
16 Needs to be run from the static/. directory.
19 sizeme_graph.pl daemon
23 Move all the static files into the DATA section of ths script so the script
24 is entirely self-contained and doesn't need any static files installed.
25 Or, work out how to install the static files and reference them from the script.
27 Make the treemap resize to fit the browser window (as NYTProf does).
29 Protect against nodes with thousands of children
30 perhaps replace all with single merged child that has no children itself
31 but just a warning as a title.
35 my $j = JSON::XS->new;
38 'db=s' => \(my $opt_db = 'sizeme.db'),
39 'showid!' => \my $opt_showid,
40 'debug!' => \my $opt_debug,
43 die "Can't open $opt_db: $!\n" unless -r $opt_db;
44 warn "Reading $opt_db\n";
46 # XXX currently uses ORLite but doesn't actually make use of it in any useful way
51 #user_version => 1, # XXX
56 # Documentation browser under "/perldoc"
61 $self->render('index');
65 # /jit_tree are AJAX requests from the treemap visualization
66 get '/jit_tree/:id/:depth' => sub {
69 my $id = $self->stash('id');
70 my $depth = $self->stash('depth');
72 # hack, would be best done on the client side
73 my $logarea = (defined $self->param('logarea'))
74 ? $self->param('logarea')
75 : Mojo::URL->new($self->req->headers->referrer)->query->param('logarea');
77 my $node_tree = _fetch_node_tree($id, $depth);
78 my $jit_tree = _transform_node_tree($node_tree, sub {
80 my $children = delete $node->{children}; # XXX edits the src tree
81 my $area = $node->{self_size}+$node->{kids_size};
82 $node->{'$area'} = ($logarea) ? log($area) : $area; # XXX move to jit js
85 name => ($node->{title} || $node->{name}).($opt_showid ? " #$node->{id}" : ""),
88 $jit_node->{children} = $children if $children;
94 use Data::Dump qw(pp);
95 local $jit_tree->{children};
96 pp(dclone($jit_tree)); # dclone to avoid stringification
99 $self->render_json($jit_tree);
103 sub _fetch_node_tree {
104 my ($id, $depth) = @_;
106 warn "#$id fetching\n"
108 my $node = SizeMe->selectrow_hashref("select * from node where id = ?", undef, $id)
109 or die "Node '$id' not found"; # shouldn't die
110 $node->{$_} += 0 for (qw(child_count kids_node_count kids_size self_size));
111 $node->{leaves} = $j->decode(delete $node->{leaves_json});
112 $node->{attr} = $j->decode(delete $node->{attr_json});
113 $node->{name} .= "->" if $node->{type} == 2 && $node->{name};
115 if ($node->{child_ids}) {
116 my @child_ids = split /,/, $node->{child_ids};
119 # if this node has only one child then we merge that child into this node
120 # this makes the treemap more usable
122 # && $node->{type} == 2 # only collapse links XXX
124 warn "#$id fetch only child $child_ids[0]\n"
126 my $child = _fetch_node_tree($child_ids[0], $depth); # same depth
127 # merge node into child
128 # XXX id, depth, parent_id
129 warn "Merged $node->{name} (#$node->{id} d$node->{depth}) with only child $child->{name} #$child->{id}\n"
131 $child->{name} = "$node->{name} $child->{name}";
132 $child->{$_} += $node->{$_} for (qw(self_size));
133 $child->{$_} = $node->{$_} for (qw(parent_id));
135 $child->{title} = join " ", grep { defined && length } $child->{title}, $node->{title};
136 #warn "Titled $child->{title}" if $child->{title};
138 # somewhat hackish attribute merging
139 for my $attr_type (keys %{ $node->{attr} }) {
140 my $src = $node->{attr}{$attr_type};
141 if (ref $src eq 'HASH') { # eg NPattr_NAME: {attr}{1}{$name} = $value
142 my $dst = $child->{attr}{$attr_type} ||= {};
143 for my $k (keys %$src) {
144 warn "Node $child->{id} attr $attr_type:$k=$dst->{$k} overwritten by $src->{$k}\n"
145 if defined $dst->{$k};
146 $dst->{$k} = $src->{$k};
149 elsif (ref $src eq 'ARRAY') { # eg NPattr_PADNAME: {attr}{2}[$val] = $name
150 my $dst = $child->{attr}{$attr_type} ||= [];
152 while (--$idx >= 0) {
153 warn "Node $child->{id} attr $attr_type:$idx=$dst->[$idx] overwritten by $src->[$idx]\n"
154 if defined $dst->[$idx];
155 $dst->[$idx] = $src->[$idx];
158 else { # assume scalar
159 warn "Node $child->{id} attr $attr_type=$child->{attr}{$attr_type} overwritten by $src\n"
160 if exists $child->{attr}{$attr_type};
161 $child->{attr}{$attr_type} = $src;
165 $child->{leaves}{$_} += $node->{leaves}{$_}
166 for keys %{ $node->{leaves} };
168 $child->{_ids_merged} .= ",$node->{id}";
169 my @child_ids = split /,/, $node->{child_ids};
170 $child->{child_count} = @child_ids;
172 $node = $child; # use the merged child as this node
174 if ($depth) { # recurse to required depth
175 $children = [ map { _fetch_node_tree($_, $depth-1) } @child_ids ];
176 $node->{children} = $children;
177 $node->{child_count} = @$children;
184 sub _transform_node_tree { # recurse depth first
185 my ($node, $transform) = @_;
186 if (my $children = $node->{children}) {
187 $_ = _transform_node_tree($_, $transform) for @$children;
189 return $transform->($node);
199 Welcome to the Mojolicious real-time web framework!
201 @@ layouts/default.html.ep
204 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
205 <title>Perl Memory Treemap</title>
208 <link type="text/css" href="css/base.css" rel="stylesheet" />
209 <link type="text/css" href="css/Treemap.css" rel="stylesheet" />
211 <!--[if IE]><script language="javascript" type="text/javascript" src="excanvas.js"></script><![endif]-->
213 <!-- JIT Library File -->
214 <script language="javascript" type="text/javascript" src="jit-yc.js"></script>
215 <script language="javascript" type="text/javascript" src="jquery-1.8.1-min.js"></script>
216 <script language="javascript" type="text/javascript" src="sprintf.js"></script>
217 <script language="javascript" type="text/javascript" src="treemap.js"></script>
220 <body onload="init();">
224 <div id="left-container">
227 <h4> Perl Memory TreeMap </h4>
228 Click on a node to zoom in.<br /><br />
231 <a id="back" href="#" class="theme button white">Go to Parent</a>
234 <label for="logarea"> Logarithmic scale
235 <input type=checkbox id="logarea" name="logarea">
240 <div id="center-container">
241 <div id="infovis"></div>
244 <div id="right-container">
245 <div id="inner-details"></div>