Reworked attribute pipeline.
Added title concept.
Added logarea (incomplete)
#define PATH_TRACKING
#ifdef PATH_TRACKING
-#define NPathAddSizeCb(st, name, bytes) (st->add_attr_cb && st->add_attr_cb(st, NP-1, 0, (name), (bytes))),
#define pPATH npath_node_t *NPathArg
/* A subtle point here is that dNPathNodes and NPathPushNode leaves NP pointing
#define NPtype_MAGIC 0x04
#define NPtype_OP 0x05
+#define NPattr_LEAFSIZE 0x00
+#define NPattr_NAME 0x01
+#define NPattr_PADFAKE 0x02
+#define NPattr_PADNAME 0x03
+#define NPattr_PADTMP 0x04
+
#define NPathLink(nodeid) ((NP->id = nodeid), (NP->type = NPtype_LINK), (NP->seqn = 0), NP)
#define NPathOpLink (NPathArg)
+#define NPathAddSizeCb(st, name, bytes) (st->add_attr_cb && st->add_attr_cb(st, NP-1, NPattr_LEAFSIZE, (name), (bytes))),
#define ADD_ATTR(st, attr_type, attr_name, attr_value) (st->add_attr_cb && st->add_attr_cb(st, NP-1, attr_type, attr_name, attr_value))
#else
if (!attr_type && !attr_value)
return 0; /* ignore zero sized leaf items */
np_walk_new_nodes(st, npath_node, NULL, np_stream_formatted_node);
- if (attr_type) {
- fprintf(st->node_stream, "A %lu ", npath_node->seqn); /* Attribute name and value */
+ if (attr_type) { /* Attribute type, name and value */
+ fprintf(st->node_stream, "%lu %lu ", attr_type, npath_node->seqn);
}
- else {
- fprintf(st->node_stream, "L %lu ", npath_node->seqn); /* Leaf name and memory size */
+ else { /* Leaf name and memory size */
+ fprintf(st->node_stream, "L %lu ", npath_node->seqn);
}
fprintf(st->node_stream, "%lu %s\n", attr_value, attr_name);
return 0;
}
if (namesv) {
if (SvFAKE(namesv))
- ADD_ATTR(st, 1, SvPVX_const(namesv), ix);
+ ADD_ATTR(st, NPattr_PADFAKE, SvPVX_const(namesv), ix);
else
- ADD_ATTR(st, 1, SvPVX_const(namesv), ix);
+ ADD_ATTR(st, NPattr_PADNAME, SvPVX_const(namesv), ix);
}
else {
- ADD_ATTR(st, 1, "SVs_PADTMP", ix);
+ ADD_ATTR(st, NPattr_PADTMP, "SVs_PADTMP", ix);
}
}
/* Now the array of buckets */
ADD_SIZE(st, "hv_max", (sizeof(HE *) * (HvMAX(thing) + 1)));
if (HvENAME(thing)) {
- ADD_ATTR(st, 1, HvENAME(thing), 0);
+ ADD_ATTR(st, NPattr_NAME, HvENAME(thing), 0);
}
/* Now walk the bucket chain */
if (HvARRAY(thing)) {
#else
ADD_SIZE(st, "GvNAMELEN", GvNAMELEN(thing));
#endif
- ADD_ATTR(st, 1, GvNAME_get(thing), 0);
+ ADD_ATTR(st, NPattr_NAME, GvNAME_get(thing), 0);
#ifdef GvFILE_HEK
hek_size(aTHX_ st, GvFILE_HEK(thing), 1, NPathLink("GvFILE_HEK"));
#elif defined(GvFILE)
perl_size()
CODE:
{
- dNPathNodes(1, NULL);
+ dNPathNodes(2, NULL);
struct state *st = new_state(aTHX);
NPathPushNode("perl_size", NPtype_NAME); /* provide a root node */
* this seems to include PL_defgv, PL_incgv etc but I've listed them anyway
*/
sv_size(aTHX_ st, NPathLink("PL_defstash"), (SV*)PL_defstash, TOTAL_SIZE_RECURSION);
+
+ NPathPushNode("others", NPtype_NAME); /* group these (typically much smaller) items */
sv_size(aTHX_ st, NPathLink("PL_defgv"), (SV*)PL_defgv, TOTAL_SIZE_RECURSION);
sv_size(aTHX_ st, NPathLink("PL_incgv"), (SV*)PL_incgv, TOTAL_SIZE_RECURSION);
sv_size(aTHX_ st, NPathLink("PL_rs"), (SV*)PL_rs, TOTAL_SIZE_RECURSION);
use strict;
use warnings;
-use DBI;
+use DBI qw(looks_like_number);
use DBD::SQLite;
use JSON::XS;
GetOptions(
'json!' => \my $opt_json,
'db=s' => \my $opt_db,
+ 'verbose|v!' => \my $opt_verbose,
+ 'debug|d!' => \my $opt_debug,
) or exit 1;
my $j = JSON::XS->new->ascii->pretty(0);
CREATE TABLE node (
id integer primary key,
name text,
+ title text,
depth integer,
parent_id integer,
)
});
my $node_ins_sth = $dbh->prepare(q{
- INSERT INTO node VALUES (?,?,?,?, ?,?,?,?,?,?)
+ INSERT INTO node VALUES (?,?,?,?,?, ?,?,?,?,?,?)
});
my @stack;
my $attr_json = $j->encode($x->{attr});
my $leaves_json = $j->encode($x->{leaves});
$node_ins_sth->execute(
- $x->{id}, $x->{name}, $x->{depth}, $x->{parent_id},
+ $x->{id}, $x->{name}, $x->{title}, $x->{depth}, $x->{parent_id},
$x->{self_size}, $x->{kids_size}, $x->{kids_node_count},
$x->{child_id} ? join(",", @{$x->{child_id}}) : undef,
$attr_json, $leaves_json,
if ($type eq "N") { # Node ($val is depth)
while ($val < @stack) {
leave_node(my $x = pop @stack);
- warn "N $id d$val ends $x->{id} d$x->{depth}: size $x->{self_size}+$x->{kids_size}\n";
+ warn "N $id d$val ends $x->{id} d$x->{depth}: size $x->{self_size}+$x->{kids_size}\n"
+ if $opt_verbose;
}
die 1 if $stack[$val];
- my $node = $stack[$val] = { id => $id, name => $name, extra => $extra, attr => [], leaves => {}, depth => $val, self_size=>0, kids_size=>0 };
+ my $node = $stack[$val] = { id => $id, name => $name, extra => $extra, attr => {}, leaves => {}, depth => $val, self_size=>0, kids_size=>0 };
enter_node($node);
$seqn2node{$id} = $node;
}
my $node = $seqn2node{$id} || die;
$node->{leaves}{$name} += $val;
}
- elsif ($type eq "A") { # Attribute name and value
+ elsif (looks_like_number($type)) { # Attribute type, name and value
my $node = $seqn2node{$id} || die;
- push @{ $node->{attr} }, $name, $val; # pairs
+ my $attr = $node->{attr} || die;
+ if ($type == 1) { # NPattr_NAME
+ warn "Node $id already has attribute $type:$name (value $attr->{$type}{$name})\n"
+ if exists $attr->{$type}{$name};
+ $attr->{$type}{$name} = $val || $id;
+ warn "A \@$id: '$name' $val\n";
+ $node->{title} = $name if $type == 1 and !$val;
+ }
+ elsif (2 <= $type and $type <= 4) { # NPattr_PAD*
+ warn "Node $id already has attribute $type:$name (value $attr->{$type}[$val])\n"
+ if defined $attr->{$type}[$val];
+ $attr->{$type}[$val] = $name;
+ }
+ else {
+ warn "Invalid attribute type '$type' on line $. ($_)";
+ }
}
else {
warn "Invalid type '$type' on line $. ($_)";
+ next;
}
$dbh->commit if $dbh and $id % 10_000 == 0;
}
use JSON::XS;
use Mojolicious::Lite;
+use Getopt::Long;
+
+GetOptions(
+ 'db=s' => \(my $opt_db = '../x.db'),
+) or exit 1;
use ORLite {
file => '../x.db',
get '/jit_tree/:id/:depth' => sub {
my $self = shift;
+ my $logarea = $self->param('logarea');
+
my $id = $self->stash('id');
my $depth = $self->stash('depth');
warn "jit_tree $id $depth";
my $jit_tree = _transform_node_tree($node_tree, sub {
my ($node) = @_;
my $children = delete $node->{children}; # XXX edits the src tree
- $node->{'$area'} = $node->{self_size}+$node->{kids_size};
+ my $area = $node->{self_size}+$node->{kids_size};
+ $node->{'$area'} = ($logarea) ? log($area) : $area;
my $jit_node = {
id => $node->{id},
- name => $node->{name},
+ name => $node->{title} || $node->{name},
data => $node,
};
$jit_node->{children} = $children if $children;
my ($id, $depth) = @_;
my $node = MemView->selectrow_hashref("select * from node where id = ?", undef, $id)
or die "Node '$id' not found";
- $node->{attr}{self} = $j->decode(delete $node->{attr_json});
$node->{leaves} = $j->decode(delete $node->{leaves_json});
+ $node->{attr} = $j->decode(delete $node->{attr_json});
if ($node->{child_ids}) {
my @child_ids = split /,/, $node->{child_ids};
$child->{name} = "$node->{name} + $child->{name}";
$child->{$_} += $node->{$_} for (qw(self_size));
$child->{$_} = $node->{$_} for (qw(parent_id));
- $child->{attr}{$node->{id}} = $node->{attr};
+
+ $child->{title} = join " + ", grep { defined && length } $child->{title}, $node->{title};
+ warn "Titled $child->{title}" if $child->{title};
+
+ for my $attr_type (keys %{ $node->{attr} }) {
+ my $src = $node->{attr}{$attr_type};
+ if (ref $src eq 'HASH') { # eg NPattr_NAME: {attr}{1}{$name} = $value
+ my $dst = $child->{attr}{$attr_type} ||= {};
+ for my $k (keys %$src) {
+ warn "Node $child->{id} attr $attr_type:$k=$dst->{$k} overwritten by $src->{$k}\n"
+ if defined $dst->{$k};
+ $dst->{$k} = $src->{$k};
+ }
+ }
+ else { # ARRAY eg NPattr_PADNAME: {attr}{2}[$val] = $name
+ my $dst = $child->{attr}{$attr_type} ||= [];
+ my $idx = @$src;
+ while (--$idx >= 0) {
+ warn "Node $child->{id} attr $attr_type:$idx=$dst->[$idx] overwritten by $src->[$idx]\n"
+ if defined $dst->[$idx];
+ $dst->[$idx] = $src->[$idx];
+ }
+ }
+ }
+
$child->{leaves}{$_} += $node->{leaves}{$_}
for keys %{ $node->{leaves} };
</div>
<a id="back" href="#" class="theme button white">Go to Parent</a>
+
+<br />
+<form name=params>
+<label for="logarea"> Logarithmic scale
+<input type=checkbox id="logarea" name="logarea">
+</form>
+
</div>
<div id="center-container">
+function request_jit_tree(nodeId, level, onComplete){
+ var params = { logarea: 1 };
+ jQuery.getJSON('jit_tree/'+nodeId+'/1', params, onComplete);
+}
+
+
function init(){
//init data
//end
//add content to the tooltip when a node
//is hovered
onShow: function(tip, node, isLeaf, domElement) {
- var html = "<div class=\"tip-title\">" + node.name
- + "</div><div class=\"tip-text\">";
var data = node.data;
+ var html = "<div class=\"tip-title\">"
+ + (data.title ? data.title : "")
+ + " " + data.name
+ + "</div><div class=\"tip-text\">";
html += "<br />";
html += sprintf("Size: %d (%d + %d)<br />", data.self_size+data.kids_size, data.self_size, data.kids_size);
html += "<br />";
}
+
html += sprintf("Attributes:<br />");
bySortedValue(data.attr,
function(a, b) { return a[0] > b[0] ? 1 : a[0] < b[0] ? -1 : 0 },
html += sprintf("Depth: %d<br />", data.depth);
html += sprintf("Parent: %d<br />", data.parent_id);
+ html += JSON.stringify(data.attr, undefined, 4);
+ //html += JSON.stringify(data, undefined, 4);
+
tip.innerHTML = html;
}
},
//call for the requested subtree. When completed, the onComplete
//callback method should be called.
request: function(nodeId, level, onComplete){
- if (true) {
- jQuery.getJSON('jit_tree/'+nodeId+'/1', function(data) {
- console.log("Node "+nodeId);
+ request_jit_tree(nodeId, level, function(data) {
+ console.log("Fetched node "+nodeId);
console.log(data);
onComplete.onComplete(nodeId, data);
});
- }
- else {
- var tree = memnodes[0];
- var subtree = $jit.json.getSubtree(tree, nodeId);
- $jit.json.prune(subtree, 2);
- onComplete.onComplete(nodeId, subtree);
- }
},
//Add the name of the node in the corresponding label
//This method is called once, on label creation and only for DOM labels.
domElement.innerHTML = node.name;
}
});
-
-if(true) {
- jQuery.getJSON('jit_tree/1/1', function(data) {
+
+ request_jit_tree(1, 0, function(data) {
console.log(data);
tm.loadJSON(data);
tm.refresh();
});
-}
-else {
- //var pjson = eval('(' + json + ')');
- var pjson = memnodes[0];
- $jit.json.prune(pjson, 2);
- console.log(pjson);
- tm.loadJSON(pjson);
- tm.refresh();
-}
- //add event to the back button
- var back = $jit.id('back');
- $jit.util.addEvent(back, 'click', function() {
- tm.out();
- });
+ //add event to buttons
+ $jit.util.addEvent($jit.id('back'), 'click', function() { tm.out() });
+ $jit.util.addEvent($jit.id('logarea'), 'onchange', function() { tm.refresh() });
+
}
+
+