diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-09-24 03:29:40 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-09-24 03:29:40 -0400 |
| commit | 7186010504c242c90caeec06136d8ebd7b47bada (patch) | |
| tree | d0043ff3af76dfb1335ebeef67621401ca8e40d6 /tools/guest-diag/dumpctx.py | |
| parent | 2849119d1512dc5f6c9ca05976473148fbc88481 (diff) | |
| download | open-iov-7186010504c242c90caeec06136d8ebd7b47bada.tar.gz open-iov-7186010504c242c90caeec06136d8ebd7b47bada.zip | |
tools/guest-diag: add UVM wait diagnostics, tracker poke and VA->PA map injector
Diffstat (limited to 'tools/guest-diag/dumpctx.py')
| -rw-r--r-- | tools/guest-diag/dumpctx.py | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/tools/guest-diag/dumpctx.py b/tools/guest-diag/dumpctx.py new file mode 100644 index 0000000..983997e --- /dev/null +++ b/tools/guest-diag/dumpctx.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 +# gdb script (run with: sudo gdb -p <pid> -batch -x /tmp/dumpctx.py) +# Dumps the libcuda client wait-loop context: the ctx object's queue/entry +# array (ctx+0x10 = count, ctx+0x18 = array of 0x28-byte entries) plus each +# entry's target object, so the host emulation knows which tracker / +# notification memory it must write to complete the wait. +import gdb + +def r32(a): + return int.from_bytes(gdb.selected_inferior().read_memory(a, 4).tobytes(), "little") +def r64(a): + return int.from_bytes(gdb.selected_inferior().read_memory(a, 8).tobytes(), "little") +def rmem(a, n): + return gdb.selected_inferior().read_memory(a, n).tobytes() + +out = gdb.execute("info proc mappings", to_string=True) +base = None +for line in out.splitlines(): + if "libcuda" in line and "r-xp" in line: + f = line.split() + # gdb format: start end size offset objfile (offset is field 3) + base = int(f[0], 16) - int(f[3], 16) + print("MAPLINE " + line.strip()) + break +if base is None: + print("NO_LIBCUDA_BASE") + raise SystemExit +LO = base + 0x23f700 +HI = base + 0x23fd10 +print("LIBBASE=0x%x SPIN=[0x%x,0x%x)" % (base, LO, HI)) + +found = None +for t in gdb.selected_inferior().threads(): + t.switch() + f = gdb.newest_frame() + for i in range(14): + if f is None: + break + try: + pc = int(f.pc()) + except Exception: + pc = 0 + if LO <= pc < HI: + try: + rb = int(f.read_register("rbx")) & 0xffffffffffffffff + except Exception: + rb = None + found = (t, pc, rb, i) + break + f = f.older() + if found: + break + +if not found: + print("NO_SPIN_FRAME") + for t in gdb.selected_inferior().threads(): + t.switch() + top = "" + try: + f0 = gdb.newest_frame() + top = f0.name() or "" + except Exception: + pass + print("== tid=%s top=%s" % (t.num, top)) + try: + print(gdb.execute("bt 6", to_string=True).rstrip()) + except Exception as e: + print("bt_fail %s" % e) + try: + print(gdb.execute("info registers rdi rsi rdx rax rip", to_string=True).rstrip()) + except Exception as e: + print("regs_fail %s" % e) + if "ioctl" in top or "poll" in top: + try: + rax = int(gdb.parse_and_eval("$rax")) & 0xffffffffffffffff + rdx = int(gdb.parse_and_eval("$rdx")) & 0xffffffffffffffff + print("ARG64@0x%x=%s" % (rdx, rmem(rdx, 0x40).hex())) + except Exception as e: + print("arg_fail %s" % e) + print("REGS_HINT") + try: + print(gdb.execute("thread apply all info registers rip rbx r13 r12", to_string=True).rstrip()) + except Exception as e: + print("regs_fail %s" % e) + print("DONE_NOSPIN") + raise SystemExit + +t, pc, ctx, depth = found +print("SPINNER tid=%s pc=0x%x depth=%d ctx=0x%x" % (t.num, pc, depth, ctx)) +if ctx is None: + raise SystemExit +try: + count = r32(ctx + 0x10) + ent = r64(ctx + 0x18) + ctx0 = r64(ctx) + gpu = r64(ctx0 + 8) + print("CTX=0x%x count=%d entries=0x%x ctx0=0x%x gpu=0x%x gpu+3de0=0x%x" % + (ctx, count, ent, ctx0, gpu, r64(gpu + 0x3de0))) +except Exception as e: + print("CTX_READ_FAIL %s" % e) + raise SystemExit +n = min(count, 32) +for i in range(n): + e = ent + i * 0x28 + try: + typ = r32(e + 0) + p = r64(e + 8) + v = r32(e + 0x10) + x18 = r64(e + 0x18) + x20 = r64(e + 0x20) + except Exception as ex: + print("e%d @0x%x UNREADABLE %s" % (i, e, ex)) + continue + line = "e%d @0x%x type=%d p=0x%x v=0x%x x18=0x%x x20=0x%x" % (i, e, typ, p, v, x18, x20) + try: + hdr = rmem(p, 0x20) + line += " p32=%d p64=0x%x p[0:0x20]=%s" % ( + int.from_bytes(hdr[0:4], "little"), + int.from_bytes(hdr[0:8], "little"), + hdr.hex()) + except Exception: + line += " p_UNREADABLE" + if typ == 1: + try: + trk = rmem(p + 0x9410, 0x40) + vals = [int.from_bytes(trk[o:o+8], "little") for o in (0, 8, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38)] + line += " TRK=%s q=%s" % (trk.hex(), ",".join("0x%x" % x for x in vals)) + except Exception: + line += " TRK_UNREADABLE" + print(line) +print("DONE_SPIN") + +import os +if os.environ.get("CTX_POKE"): + inf = gdb.selected_inferior() + npk = 0 + for i in range(n): + e = ent + i * 0x28 + try: + if r32(e) != 1: + continue + p = r64(e + 8) + inf.write_memory(p + 0x9410 + 0x10, (1).to_bytes(8, "little")) + inf.write_memory(p + 0x9410 + 0x18, (1).to_bytes(8, "little")) + npk += 1 + except Exception as ex: + print("poke e%d fail %s" % (i, ex)) + print("POKED=%d" % npk) |
