Skip to content

Commit

Permalink
gdb: updates
Browse files Browse the repository at this point in the history
  • Loading branch information
Austin Clements committed Jul 26, 2020
1 parent e785700 commit 13420c2
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 31 deletions.
86 changes: 55 additions & 31 deletions gdb/heapgraph.py
@@ -1,7 +1,7 @@
# Compute the object graph of a Go heap and find the path from a root
# to a target address.

CONSERVATIVE_STACKS = True
CONSERVATIVE_STACKS = False

import gdb
import collections
Expand Down Expand Up @@ -122,6 +122,13 @@ def __init__(self, base, len, name, bitmap):
def __repr__(self):
return "Obj(%#x, %d, %s, %s)" % (self.base, self.len, self.name, self.bitmap)

def __str__(self):
s = "%#x" % self.base
if self.name:
s += " " + self.name
s += " [%d bytes]" % self.len
return s

def children(self, addrspace):
if not self.bitmap.hasPointers():
return
Expand All @@ -131,7 +138,7 @@ def children(self, addrspace):
val = addrspace.readWord(self.base + word * ptrSize)
if not addrspace.inheap(val):
continue
yield addrspace.heapObj(val, "")
yield (word * ptrSize, addrspace.heapObj(val, ""))

class OneBitBitmap(object):
def __init__(self, bitmap, startBit):
Expand Down Expand Up @@ -289,25 +296,29 @@ def roots(addrspace):
pid, lwpid, tid = thr.ptid
tidToThr[lwpid] = thr
haveGs = set()
for mp in iterlist(gdb.parse_and_eval("'runtime.allm'"), "alllink"):
thr = tidToThr.get(long(mp["procid"]), None)
if thr is None:
continue
thr.switch()
curg = mp["curg"]
if curg == 0:
continue
sp = gdb.parse_and_eval("$sp")
if not (curg["stack"]["lo"] < sp <= curg["stack"]["hi"]):
# We're on the g0 and curg's state is in sched.
continue
if CONSERVATIVE_STACKS:
for root in conservativeStackRoots(addrspace, sp, curg["stack"]["hi"]):
yield root
else:
for root in stackRoots(addrspace):
yield root
haveGs.add(curg["goid"])
curThr = gdb.selected_thread()
try:
for mp in iterlist(gdb.parse_and_eval("'runtime.allm'"), "alllink"):
thr = tidToThr.get(long(mp["procid"]), None)
if thr is None:
continue
thr.switch()
curg = mp["curg"]
if curg == 0:
continue
sp = gdb.parse_and_eval("$sp")
if not (curg["stack"]["lo"] < sp <= curg["stack"]["hi"]):
# We're on the g0 and curg's state is in sched.
continue
if CONSERVATIVE_STACKS:
for root in conservativeStackRoots(addrspace, sp, curg["stack"]["hi"]):
yield root
else:
for root in stackRoots(addrspace):
yield root
haveGs.add(curg["goid"])
finally:
curThr.switch()

# Stacks of non-running Gs
for g in SliceValue(gdb.parse_and_eval("'runtime.allgs'")):
Expand All @@ -318,16 +329,25 @@ def roots(addrspace):
sp, pc = g['syscallsp'], g['syscallpc']
else:
sp, pc = g['sched']['sp'], g['sched']['pc']
if sp == 0:
# TODO: When does this happen?
continue

if CONSERVATIVE_STACKS:
for root in conservativeStackRoots(addrspace, sp, g['stack']['hi']):
yield root
else:
# XXX Doesn't work on core files. :(
gdb.execute('set $sp = %#x' % sp)
gdb.execute('set $pc = %#x' % pc)
for root in stackRoots(addrspace):
yield root
oldsp, oldpc = gdb.parse_and_eval('$sp'), gdb.parse_and_eval('$pc')
try:
# TODO: This fails if we're not in the innermost frame.
gdb.execute('set $sp = %#x' % sp)
gdb.execute('set $pc = %#x' % pc)
for root in stackRoots(addrspace):
yield root
finally:
gdb.execute('set $sp = %#x' % oldsp)
gdb.execute('set $pc = %#x' % oldpc)

# Span specials
for s in heapSpans():
Expand All @@ -348,7 +368,7 @@ def stackRoots(addrspace):
while block.function != None:
for sym in block:
val = sym.value(fr)
name = "%s[%s]" % (fr.name(), sym.name)
name = "%s[%s]" % (fr.name(), sym.name.decode("utf8"))
base = long(val.address)
#print hex(base), name
#if addrspace.inheap(base):
Expand All @@ -362,6 +382,7 @@ def stackRoots(addrspace):
fr = fr.older()

def conservativeStackRoots(addrspace, sp, hi):
#print(str(sp), str(hi))
while sp < hi:
word = addrspace.readWord(sp)
if addrspace.inheap(word):
Expand Down Expand Up @@ -392,14 +413,16 @@ def computeParents():
scannedBytes += parent.len
scannedObjects += 1
#print id(parent), parent.base, parent.len, parent.marked
for obj in parent.children(addrspace):
for offset, obj in parent.children(addrspace):
if obj.parent == None:
# Record parents for roots too, to help find cycles.
obj.parent = parent
obj.parent = (parent, offset)
if not obj.marked:
#obj.marked = True
obj.marked += 1
q.append(obj)
sys.stdout.write("\x1b[2K")
sys.stdout.flush()
return addrspace

class FindPath(gdb.Command):
Expand All @@ -416,9 +439,10 @@ def invoke(self, arg, from_tty):
addrspace = computeParents()

o = addrspace.objs.get(target)
while o:
print o
o = o.parent
print o
while o.parent:
o, offset = o.parent
print o, "+", offset
FindPath()

class ObjOf(gdb.Command):
Expand Down
5 changes: 5 additions & 0 deletions gdb/printall.py
Expand Up @@ -5,6 +5,8 @@
# understand the associativity of '-' versus '.'. Currently this needs
# to be parenthesized.

# TODO: Support printing multiple things in a table. x[*].goid, x[*].stackAlloc.

# TODO: Support pointer chasing, e.g., list.next*.x

# TODO: Support nested wildcards. Maybe "*", "**", etc.
Expand All @@ -13,6 +15,9 @@

# TODO: Support / format specifiers.

# TODO: Perhaps a binding for-loop syntax would be simpler and better
# than implicit wildcards.

class PrintAll(gdb.Command):
"""print-all expr: print all values of expr, where expr may contain '[*]' wildcard indexes."""

Expand Down
1 change: 1 addition & 0 deletions gdbinit
Expand Up @@ -36,6 +36,7 @@ source ~/sys/dotfiles/gdb/hex.py
source ~/sys/dotfiles/gdb/qreg.py

add-auto-load-safe-path /home/amthrax/r/sv6/.gdbinit
add-auto-load-safe-path /home/austin/go.dev/src/runtime/

# Color prompt. Text in \[...\] is not counted toward the prompt length.
set extended-prompt \[\e[0;1;31m\](gdb) \[\e[0m\]
Expand Down

0 comments on commit 13420c2

Please sign in to comment.