summary refs log tree commit diff stats
path: root/tools/heapdumprepl.nim
diff options
context:
space:
mode:
Diffstat (limited to 'tools/heapdumprepl.nim')
-rw-r--r--tools/heapdumprepl.nim148
1 files changed, 148 insertions, 0 deletions
diff --git a/tools/heapdumprepl.nim b/tools/heapdumprepl.nim
new file mode 100644
index 000000000..4f06cf111
--- /dev/null
+++ b/tools/heapdumprepl.nim
@@ -0,0 +1,148 @@
+include std/prelude
+import intsets
+
+type
+  NodeKind = enum
+    internal, local, localInvalid, global, globalInvalid
+  Color = enum
+    white, grey, black
+  Node = ref object
+    id, rc: int
+    kids: seq[int]
+    k: NodeKind
+    col: Color
+  Graph = object
+    nodes: Table[int, Node]
+    roots: Table[int, NodeKind]
+
+proc add(father: Node; son: int) =
+  father.kids.add(son)
+
+proc renderNode(g: Graph; id: int) =
+  let n = g.nodes[id]
+  echo n[]
+
+proc toNodeId(aliases: var Table[string,int]; s: string): int =
+  result = aliases.getOrDefault(s)
+  if result == 0:
+    if s.startsWith("x"):
+      discard s.parseHex(result, 1)
+    else:
+      result = s.parseInt
+
+proc parseHex(s: string): int =
+  discard parseutils.parseHex(s, result, 0)
+
+proc reachable(g: Graph; stack: var seq[int]; goal: int): bool =
+  var t = initIntSet()
+  while stack.len > 0:
+    let it = stack.pop
+    if not t.containsOrIncl(it):
+      if it == goal: return true
+      if it in g.nodes:
+        for kid in g.nodes[it].kids:
+          stack.add(kid)
+
+const Help = """
+quit          -- quits this REPL
+locals, l     -- output the list of local stack roots
+globals, g    -- output the list of global roots
+alias name addr -- give addr a name. start 'addr' with 'x' for hexadecimal
+                   notation
+print name|addr  -- print a node by name or address
+reachable,r  l|g|node  dest   -- outputs TRUE or FALSE depending on whether
+                    dest is reachable by (l)ocals, (g)lobals or by the
+                    other given node. Nodes can be node names or node
+                    addresses.
+"""
+
+proc repl(g: Graph) =
+  var aliases = initTable[string,int]()
+  while true:
+    let line = stdin.readLine()
+    let data = line.split()
+    if data.len == 0: continue
+    case data[0]
+    of "quit":
+      break
+    of "help":
+      echo Help
+    of "locals", "l":
+      for k,v in g.roots:
+        if v == local: renderNode(g, k)
+    of "globals", "g":
+      for k,v in g.roots:
+        if v == global: renderNode(g, k)
+    of "alias", "a":
+      # generate alias
+      if data.len == 3:
+        aliases[data[1]] = toNodeId(aliases, data[2])
+    of "print", "p":
+      if data.len == 2:
+        renderNode(g, toNodeId(aliases, data[1]))
+    of "reachable", "r":
+      if data.len == 3:
+        var stack: seq[int] = @[]
+        case data[1]
+        of "locals", "l":
+          for k,v in g.roots:
+            if v == local: stack.add k
+        of "globals", "g":
+          for k,v in g.roots:
+            if v == global: stack.add k
+        else:
+          stack.add(toNodeId(aliases, data[1]))
+        let goal = toNodeId(aliases, data[2])
+        echo reachable(g, stack, goal)
+    else: discard
+
+proc importData(input: string): Graph =
+  #c_fprintf(file, "%s %p %d rc=%ld color=%c\n",
+  #          msg, c, kind, c.refcount shr rcShift, col)
+  # cell  0x10a908190 22 rc=2 color=w
+  var i: File
+  var
+    nodes = initTable[int, Node]()
+    roots = initTable[int, NodeKind]()
+  if open(i, input):
+    var currNode: Node
+    for line in lines(i):
+      let data = line.split()
+      if data.len == 0: continue
+      case data[0]
+      of "end":
+        currNode = nil
+      of "cell":
+        let rc = parseInt(data[3].substr("rc=".len))
+        let col = case data[4].substr("color=".len)
+                  of "b": black
+                  of "w": white
+                  of "g": grey
+                  else: (assert(false); grey)
+        let id = parseHex(data[1])
+        currNode = Node(id: id,
+          k: roots.getOrDefault(id),
+          rc: rc, col: col)
+        nodes[currNode.id] = currNode
+      of "child":
+        assert currNode != nil
+        currNode.add parseHex(data[1])
+      of "global_root":
+        roots[data[1].parseHex] = global
+      of "global_root_invalid":
+        roots[data[1].parseHex] = globalInvalid
+      of "onstack":
+        roots[data[1].parseHex] = local
+      of "onstack_invalid":
+        roots[data[1].parseHex] = localInvalid
+      else: discard
+    close(i)
+  else:
+    quit "error: cannot open " & input
+  result.nodes = move nodes
+  result.roots = move roots
+
+if paramCount() == 1:
+  repl(importData(paramStr(1)))
+else:
+  quit "usage: heapdumprepl inputfile"