summary refs log tree commit diff stats
path: root/tools/heapdumprepl.nim
blob: 4f06cf11183bd16e18a360183072e402df398484 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
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"