#!/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 ''] += 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'))