re: withdraw the 'unrestorable rbx' claim — the misses are WaitMultiple
The 8 threads whose [rbx] did not resolve to a vtable were never in XObject::Wait. The backtrace grep matched WaitMultiple as a substring, and there %rbx is the XObject** array (mov %rsi,%rbx) with the count in %ebp, so [rbx] is objects[0] -- an object pointer, needing a second deref -- not a vtable. The unwind restored rbx correctly for all 18. freeze_waitobj.sh now takes the function and frame index from the backtrace and applies the matching read, and captures twice in one run (healthy and after the ~270s black-screen) so the comparison is within-run. waitobj_report.py tabulates both and diffs them, discarding any value info symbol cannot resolve.
This commit is contained in:
74
tools/re-capture/waitobj_report.py
Executable file
74
tools/re-capture/waitobj_report.py
Executable file
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Turn the gdb wait-object dumps into a table, discarding what does not check out.
|
||||
|
||||
The two waiting functions do not put the same thing in %rbx (see
|
||||
freeze_waitobj.sh), so they are parsed separately:
|
||||
|
||||
Wait rbx = this -> [rbx] is the vtable
|
||||
WaitMultiple rbx = XObject** -> [[rbx+8i]] is object i's vtable
|
||||
ebp = count -> entries past the count are garbage
|
||||
|
||||
Every reading is validated the same way: gdb's `info symbol` must resolve it to
|
||||
a `vtable for ...` symbol. A polymorphic object's first word always is one, so
|
||||
anything else is a misread and is COUNTED but not interpreted. That check is
|
||||
what the reading is worth -- without it a stale register looks like a result.
|
||||
"""
|
||||
import re, sys, collections
|
||||
|
||||
VT = re.compile(r'vtable for ([\w:]+) \+ (\d+)')
|
||||
HDR = re.compile(r'=== (\w+) T(\d+) (Wait|WaitMultiple) f(\d+) ===')
|
||||
|
||||
def parse(tag):
|
||||
"""-> list of (thread, kind, count, [vtable-or-None per slot])"""
|
||||
try:
|
||||
txt = open('/tmp/fz-obj-%s.txt' % tag, errors='replace').read()
|
||||
except FileNotFoundError:
|
||||
return []
|
||||
out, cur = [], None
|
||||
for line in txt.splitlines():
|
||||
line = line.replace('(gdb) ', '')
|
||||
h = HDR.search(line)
|
||||
if h:
|
||||
if cur: out.append(cur)
|
||||
cur = dict(th=h.group(2), kind=h.group(3), count=None, slots=[], bad=0)
|
||||
continue
|
||||
if not cur: continue
|
||||
m = re.search(r'rbp\s+0x[0-9a-f]+\s+(\d+)', line)
|
||||
if m: cur['count'] = int(m.group(1)) & 0xffffffff
|
||||
if 'info symbol' in line or line.startswith('$'): continue
|
||||
v = VT.search(line)
|
||||
if v:
|
||||
cur['slots'].append(v.group(1))
|
||||
elif 'No symbol matches' in line:
|
||||
cur['slots'].append(None); cur['bad'] += 1
|
||||
if cur: out.append(cur)
|
||||
return out
|
||||
|
||||
def report(tag):
|
||||
recs = parse(tag)
|
||||
print('=== %s: %d wait frames ===' % (tag, len(recs)))
|
||||
if not recs: return collections.Counter()
|
||||
tally = collections.Counter()
|
||||
for r in recs:
|
||||
n = r['count'] if r['kind'] == 'WaitMultiple' and r['count'] else len(r['slots'])
|
||||
n = min(n, len(r['slots']))
|
||||
live = r['slots'][:n]
|
||||
for s in live:
|
||||
tally[s if s else '<unresolved>'] += 1
|
||||
print(' T%-4s %-13s count=%-4s %s' % (
|
||||
r['th'], r['kind'], r['count'] if r['count'] is not None else '-',
|
||||
', '.join(s or '?' for s in live) or '(nothing readable)'))
|
||||
print(' --- objects waited on:')
|
||||
for k, c in tally.most_common():
|
||||
print(' %-45s %d' % (k, c))
|
||||
return tally
|
||||
|
||||
if __name__ == '__main__':
|
||||
tallies = {t: report(t) for t in (sys.argv[1:] or ['healthy'])}
|
||||
if len(tallies) > 1:
|
||||
a, b = list(tallies)
|
||||
print('=== %s -> %s ===' % (a, b))
|
||||
keys = set(tallies[a]) | set(tallies[b])
|
||||
for k in sorted(keys):
|
||||
x, y = tallies[a][k], tallies[b][k]
|
||||
print(' %-45s %3d -> %-3d %s' % (k, x, y, '' if x == y else ' CHANGED'))
|
||||
Reference in New Issue
Block a user