summaryrefslogtreecommitdiffstats
path: root/tools/guest-diag/dumpctx.py
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-24 03:29:40 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-24 03:29:40 -0400
commit7186010504c242c90caeec06136d8ebd7b47bada (patch)
treed0043ff3af76dfb1335ebeef67621401ca8e40d6 /tools/guest-diag/dumpctx.py
parent2849119d1512dc5f6c9ca05976473148fbc88481 (diff)
downloadopen-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.py148
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)