[gjs: 4/11] heapgraph: Add ability to label nodes



commit 724c4a50eb24f67d2662c0d187234ac3650b893b
Author: Philip Chimento <philip chimento gmail com>
Date:   Thu Aug 8 22:08:41 2019 -0700

    heapgraph: Add ability to label nodes
    
    The profusion of Object or GObject_Object nodes can be quite confusing.
    If you add a __heapgraph_name property to an object in your code with a
    string value, then it should be visible in the heap dump, and heapgraph
    will now detect this situation and display it as a label.
    
        myObj.__heapgraph_name = 'My object';

 tools/heapdot.py   | 16 ++++++++++------
 tools/heapgraph.md | 14 ++++++++++++++
 tools/heapgraph.py | 38 +++++++++++++++++++++++++++++++-------
 3 files changed, 55 insertions(+), 13 deletions(-)
---
diff --git a/tools/heapdot.py b/tools/heapdot.py
index f455d01c..3f829cb7 100644
--- a/tools/heapdot.py
+++ b/tools/heapdot.py
@@ -126,13 +126,17 @@ def output_dot_file(args, graph, targs, fname):
             color = 'red'
             style = 'bold'
 
-        if args.no_addr:
-            outf.write('  node [label="{0}", color={1}, shape={2}, style="{3}"] q{4};\n'.format(label, 
color, shape, style, addr))
-        else:
+        node_label = label
+        if not args.no_addr:
+            node_label += '\\njsobj@' + addr
             if native:
-                outf.write('  node [label="{0}\\njsobj@{4}\\nnative@{5}", color={1}, shape={2}, style="{3}"] 
q{4};\n'.format(label, color, shape, style, addr, native))
-            else:
-                outf.write('  node [label="{0}\\njsobj@{4}", color={1}, shape={2}, style="{3}"] 
q{4};\n'.format(label, color, shape, style, addr))
+                node_label += '\\nnative@' + native
+        annotation = graph.annotations.get(addr, None)
+        if annotation:
+            node_label += '\\n\\"{}\\"'.format(annotation)
+
+        node_text = '  node [label="{0}", color={1}, shape={2}, style="{3}"] q{4};\n'.format(node_label, 
color, shape, style, addr)
+        outf.write(node_text)
 
     # Edges (relationships)
     for origin, destinations in edges.items():
diff --git a/tools/heapgraph.md b/tools/heapgraph.md
index fb5dcd84..c0193a13 100644
--- a/tools/heapgraph.md
+++ b/tools/heapgraph.md
@@ -117,6 +117,20 @@ $ ./heapgraph.py --hide-addr 0x7f6ef022c060 \
 $ ./heapgraph.py --hide-node 0x55e93cf5deb0 /home/user/myApp2.heap Object
 ```
 
+### Labeling Nodes
+
+It can be hard to see what some nodes mean, especially if all the nodes
+you are interested in are labeled `GObject_Object`.
+Luckily there is a way to label the nodes in your program so that they
+are visible in the heap graph.
+Add a property named `__heapgraph_name` with a simple string value to
+your object:
+```js
+myObj.__heapgraph_name = 'My object';
+```
+Heapgraph will detect this and display the name as part of the node's
+label, e.g. GObject_Object "My object".
+
 ### Command-Line Arguments
 
**NOTE:** Command line arguments are subject to change; Check
diff --git a/tools/heapgraph.py b/tools/heapgraph.py
index adec6e4c..bf1ae58e 100755
--- a/tools/heapgraph.py
+++ b/tools/heapgraph.py
@@ -20,6 +20,7 @@ try:
 except ImportError:
     sys.stderr.write('DOT graph output not available\n')
 
+NAME_ANNOTATION = '__heapgraph_name'
 
 ########################################################
 # Command line arguments.
@@ -100,7 +101,7 @@ filt_opts.add_argument('--hide-node', '-hn', dest='hide_nodes', action='append',
                        help='Don\'t show nodes with labels containing LABEL')
 
 filt_opts.add_argument('--hide-edge', '-he', dest='hide_edges', action='append',
-                       metavar='LABEL', default=[],
+                       metavar='LABEL', default=[NAME_ANNOTATION],
                        help="Don't show edges labeled LABEL")
 
 
@@ -108,7 +109,8 @@ filt_opts.add_argument('--hide-edge', '-he', dest='hide_edges', action='append',
 # Heap Patterns
 ###############################################################################
 
-GraphAttribs = namedtuple('GraphAttribs', 'edge_labels node_labels roots root_labels weakMapEntries')
+GraphAttribs = namedtuple('GraphAttribs',
+                          'edge_labels node_labels roots root_labels weakMapEntries annotations')
 WeakMapEntry = namedtuple('WeakMapEntry', 'weakMap key keyDelegate value')
 
 
@@ -119,6 +121,7 @@ wme_regex = re.compile(r'WeakMapEntry map=((?:0x)?[a-zA-Z0-9]+|\(nil\)) key=((?:
 
 func_regex = re.compile('Function(?: ([^/]+)(?:/([<|\w]+))?)?')
 gobj_regex = re.compile('([^ ]+) (0x[a-fA-F0-9]+$)')
+atom_regex = re.compile(r'^string <atom: length (?:\d+)> (.*)\r?$')
 
 ###############################################################################
 # Heap Parsing
@@ -172,6 +175,7 @@ def parse_graph(fobj):
     edges = {}
     edge_labels = {}
     node_labels = {}
+    annotations = {}
 
     def addNode (addr, node_label):
         edges[addr] = {}
@@ -192,9 +196,15 @@ def parse_graph(fobj):
         e = edge_regex.match(line)
 
         if e:
+            target, edge_label = e.group(1, 3)
+            if edge_label == NAME_ANNOTATION:
+                a = atom_regex.match(node_labels[target])
+                if a:
+                    annotations[node_addr] = a.group(1)
+
             if (node_addr not in args.hide_addrs and
-                    e.group(3) not in args.hide_edges):
-                addEdge(node_addr, e.group(1), e.group(3))
+                    edge_label not in args.hide_edges):
+                addEdge(node_addr, target, edge_label)
         else:
             node = node_regex.match(line)
 
@@ -217,7 +227,7 @@ def parse_graph(fobj):
                 sys.stderr.write('Error: Unknown line: {}\n'.format(line[:-1]))
 
     # yar, should pass the root crud in and wedge it in here, or somewhere
-    return [edges, edge_labels, node_labels]
+    return [edges, edge_labels, node_labels, annotations]
 
 
 def parse_heap(fname):
@@ -230,12 +240,12 @@ def parse_heap(fname):
         exit(-1)
 
     [roots, root_labels, weakMapEntries] = parse_roots(fobj)
-    [edges, edge_labels, node_labels] = parse_graph(fobj)
+    [edges, edge_labels, node_labels, annotations] = parse_graph(fobj)
     fobj.close()
 
     graph = GraphAttribs(edge_labels=edge_labels, node_labels=node_labels,
                          roots=roots, root_labels=root_labels,
-                         weakMapEntries=weakMapEntries)
+                         weakMapEntries=weakMapEntries, annotations=annotations)
 
     return (edges, graph)
 
@@ -309,6 +319,7 @@ class style:
     BOLD = '\033[1m'
     ITALIC = '\033[3m'
     UNDERLINE = '\033[4m'
+    PURPLE = '\033[0;36m'
     END = '\033[0m'
 
 
@@ -356,6 +367,10 @@ def get_node_label(graph, addr):
     return label[:50]
 
 
+def get_node_annotation(graph, addr):
+    return graph.annotations.get(addr, None)
+
+
 def output_tree_graph(graph, data, base='', parent=''):
     while data:
         addr, children = data.popitem()
@@ -367,6 +382,7 @@ def output_tree_graph(graph, data, base='', parent=''):
             edge = graph.root_labels[addr]
 
         node = get_node_label(graph, addr)
+        annotation = get_node_annotation(graph, addr)
         has_native = gobj_regex.match(node)
 
         # Color/Style
@@ -390,6 +406,14 @@ def output_tree_graph(graph, data, base='', parent=''):
                 node = has_native.group(1)
                 orig += ' native@' + has_native.group(2)
 
+        # Add the annotation
+        if annotation:
+            if os.isatty(1):
+                node += ' "' + style.PURPLE + annotation + style.END + '"'
+            else:
+                node += ' "' + annotation + '"'
+
+
         # Print the path segment
         if args.no_addr:
             if data:


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]