wip(nexus): debug crash 0xc000041d khi mở GUI Nexus — lưu trạng thái điều tra (attached() hang; probe exact11 PASS, bridge hang; g_engine hypothesis disproven; chờ phân tích waitscan wait-object)

This commit is contained in:
2026-08-14 22:29:45 +07:00
parent 2596372ab1
commit 1964544d2d
18 changed files with 4996 additions and 129 deletions
+218
View File
@@ -0,0 +1,218 @@
# Stack walker v5: dbghelp StackWalk64 on every thread of a live process.
# Usage: python stackwalk.py --bridge | --probe [secs]
import ctypes, ctypes.wintypes as w, struct, subprocess, sys, os, time
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
MODE = sys.argv[1] if len(sys.argv) > 1 else "--bridge"
SECS = int(sys.argv[2]) if len(sys.argv) > 2 else 15
SHM_NAME = "SonicForge_DAW_IPC_VERIFY"
SIZE = 32768
BRIDGE = r"C:\Users\locpham\SonicForgeStudio\native_bridge\build\Release\daw_vst_bridge.exe"
PROBE = r"C:\Users\locpham\SonicForgeStudio\native_bridge\build\Release\gui_probe.exe"
NEXUS = r"C:\Program Files\Common Files\VST3\Nexus.vst3"
LOG = os.path.join(os.path.dirname(os.path.abspath(__file__)),
"stack_sample.log" if MODE == "--bridge" else "stack_probe.log")
k32 = ctypes.windll.kernel32
k32.CreateFileMappingA.argtypes = [ctypes.c_void_p, ctypes.c_void_p, w.DWORD, w.DWORD, w.DWORD, ctypes.c_char_p]
k32.CreateFileMappingA.restype = ctypes.c_void_p
k32.MapViewOfFile.argtypes = [ctypes.c_void_p, w.DWORD, w.DWORD, w.DWORD, ctypes.c_size_t]
k32.MapViewOfFile.restype = ctypes.c_void_p
INVALID_HANDLE_VALUE = ctypes.c_void_p(-1).value
hMap = k32.CreateFileMappingA(INVALID_HANDLE_VALUE, None, 0x04, 0, SIZE, SHM_NAME.encode())
ptr = k32.MapViewOfFile(hMap, 0xF001F, 0, 0, 0)
def wait_log(pattern, timeout):
end = time.time() + timeout
while time.time() < end:
try:
with open(LOG, 'r', encoding='utf-8', errors='replace') as f:
if pattern in f.read():
return True
except FileNotFoundError:
pass
time.sleep(0.2)
return False
if MODE == "--bridge":
extra = dict(os.environ)
for k in list(extra):
if k.startswith("SF_"):
del extra[k]
extra.update({"SF_SHM_NAME": SHM_NAME, "SF_SAMPLE_RATE": "48000",
"SF_BLOCK_SIZE": "256", "SF_ONCE": "1", "SF_AUTOGUI": "0"})
proc = subprocess.Popen([BRIDGE, "--shm", SHM_NAME], env=extra,
stdout=open(LOG, 'wb'), stderr=subprocess.STDOUT)
marker = "isPlatformTypeSupported=0"
wait_after = 3
else:
proc = subprocess.Popen([PROBE, NEXUS, "bridge_exact11", str(SECS)],
stdout=open(LOG, 'wb'), stderr=subprocess.STDOUT)
marker = "openGUI: attached=0"
wait_after = 2
print("pid", proc.pid, "mode", MODE)
if not wait_log(marker, 120):
print("TIMEOUT waiting for", marker)
sys.exit(1)
time.sleep(wait_after)
pid = proc.pid
PROCESS_QUERY_INFORMATION = 0x0400
PROCESS_VM_READ = 0x0010
hProc = k32.OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, False, pid)
if not hProc:
print("OpenProcess failed", ctypes.get_last_error())
sys.exit(1)
# module map
psapi = ctypes.windll.psapi
class MODULEINFO(ctypes.Structure):
_fields_ = [("lpBaseOfDll", ctypes.c_void_p), ("SizeOfImage", w.DWORD),
("pad", w.DWORD), ("EntryPoint", ctypes.c_void_p)]
psapi.EnumProcessModulesEx.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p), w.DWORD, ctypes.POINTER(w.DWORD), w.DWORD]
psapi.GetModuleBaseNameA.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_char_p, w.DWORD]
psapi.GetModuleInformation.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.POINTER(MODULEINFO), w.DWORD]
mods = []
buf = (ctypes.c_void_p * 1024)()
needed = w.DWORD(0)
if psapi.EnumProcessModulesEx(hProc, buf, ctypes.sizeof(buf), ctypes.byref(needed), 3):
n = needed.value // ctypes.sizeof(ctypes.c_void_p)
for i in range(min(n, 1024)):
base = buf[i]
name = ctypes.create_string_buffer(260)
psapi.GetModuleBaseNameA(hProc, base, name, 260)
info = MODULEINFO()
psapi.GetModuleInformation(hProc, base, ctypes.byref(info), ctypes.sizeof(info))
mods.append((base, info.SizeOfImage, name.value.decode('utf-8', 'replace')))
mods.sort(key=lambda m: m[0])
def modname(addr):
if addr == 0:
return "null"
for b, s, n in mods:
if b <= addr < b + s:
return "%s+%x" % (n, addr - b)
return "??%x" % addr
# dbghelp stack walk
dbg = ctypes.WinDLL("dbghelp")
dbg.SymInitializeW.argtypes = [ctypes.c_void_p, ctypes.c_wchar_p, w.BOOL]
dbg.SymInitializeW.restype = w.BOOL
dbg.SymSetOptions.argtypes = [w.DWORD]
dbg.SymSetOptions.restype = w.DWORD
dbg.StackWalk64.argtypes = [w.DWORD, ctypes.c_void_p, ctypes.c_void_p,
ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p,
ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, w.DWORD]
dbg.StackWalk64.restype = w.BOOL
class STACKFRAME64(ctypes.Structure):
class ADDR(ctypes.Structure):
_fields_ = [("Offset", ctypes.c_uint64), ("Segment", w.DWORD),
("Mode", w.DWORD)]
_fields_ = [("AddrPC", ADDR), ("AddrReturn", ADDR), ("AddrFrame", ADDR),
("AddrStack", ADDR), ("AddrBStore", ADDR), ("FuncTableEntry", ctypes.c_void_p),
("Params", ctypes.c_uint64 * 4), ("Far", w.BOOL),
("Virtual", w.BOOL), ("Reserved", ctypes.c_uint64 * 3),
("KdHelp", ctypes.c_uint64 * 2)]
class CONTEXT64(ctypes.Structure):
_fields_ = [
("P1Home", ctypes.c_uint64), ("P2Home", ctypes.c_uint64),
("P3Home", ctypes.c_uint64), ("P4Home", ctypes.c_uint64),
("P5Home", ctypes.c_uint64), ("P6Home", ctypes.c_uint64),
("ContextFlags", w.DWORD), ("MxCsr", w.DWORD),
("SegCs", w.WORD), ("SegDs", w.WORD), ("SegEs", w.WORD),
("SegFs", w.WORD), ("SegGs", w.WORD), ("SegSs", w.WORD),
("EFlags", w.DWORD),
("Dr0", ctypes.c_uint64), ("Dr1", ctypes.c_uint64),
("Dr2", ctypes.c_uint64), ("Dr3", ctypes.c_uint64),
("Dr6", ctypes.c_uint64), ("Dr7", ctypes.c_uint64),
("Rax", ctypes.c_uint64), ("Rcx", ctypes.c_uint64),
("Rdx", ctypes.c_uint64), ("Rbx", ctypes.c_uint64),
("Rsp", ctypes.c_uint64), ("Rbp", ctypes.c_uint64),
("Rsi", ctypes.c_uint64), ("Rdi", ctypes.c_uint64),
("R8", ctypes.c_uint64), ("R9", ctypes.c_uint64),
("R10", ctypes.c_uint64), ("R11", ctypes.c_uint64),
("R12", ctypes.c_uint64), ("R13", ctypes.c_uint64),
("R14", ctypes.c_uint64), ("R15", ctypes.c_uint64),
("Rip", ctypes.c_uint64),
]
ReadProcessMemory_cb = ctypes.CFUNCTYPE(w.BOOL, ctypes.c_void_p, ctypes.c_uint64,
ctypes.c_void_p, w.DWORD, ctypes.POINTER(w.DWORD))
@ReadProcessMemory_cb
def read_mem(hProc, addr, buf, size, nread):
return k32.ReadProcessMemory(hProc, ctypes.c_void_p(addr), buf, size, nread)
class THREADENTRY32(ctypes.Structure):
_fields_ = [("dwSize", w.DWORD), ("cntUsage", w.DWORD), ("th32ThreadID", w.DWORD),
("th32OwnerProcessID", w.DWORD), ("tpBasePri", ctypes.c_long),
("tpDeltaPri", ctypes.c_long), ("dwFlags", w.DWORD)]
TH32CS_SNAPTHREAD = 0x4
THREAD_SUSPEND_RESUME = 0x0002
THREAD_GET_CONTEXT = 0x0008
THREAD_QUERY_INFORMATION = 0x0040
CONTEXT_CTRL_INT_SEG = 0x100001 | 0x2 | 0x20
IMAGE_FILE_MACHINE_AMD64 = 0x8664
dbg.SymInitializeW(hProc, None, True)
dbg.SymSetOptions(0x2) # SYMOPT_DEFERRED_LOADS
snap = k32.CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0)
te = THREADENTRY32(); te.dwSize = ctypes.sizeof(THREADENTRY32)
threads = []
ok = k32.Thread32First(snap, ctypes.byref(te))
while ok:
if te.th32OwnerProcessID == pid:
threads.append(te.th32ThreadID)
ok = k32.Thread32Next(snap, ctypes.byref(te))
print("threads(%d): %s" % (len(threads), threads))
for tid in threads:
hT = k32.OpenThread(THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION, False, tid)
if not hT:
continue
k32.SuspendThread(hT)
ctx = CONTEXT64(); ctx.ContextFlags = CONTEXT_CTRL_INT_SEG
r = k32.GetThreadContext(hT, ctypes.byref(ctx))
if not r:
k32.ResumeThread(hT); k32.CloseHandle(hT)
print("tid %d GetThreadContext fail" % tid)
continue
sf = STACKFRAME64()
sf.AddrPC.Offset = ctx.Rip
sf.AddrPC.Mode = 0
sf.AddrReturn.Offset = ctx.Rsp # will be set by walker
sf.AddrFrame.Offset = ctx.Rbp
sf.AddrStack.Offset = ctx.Rsp
frames = []
for i in range(48):
if not dbg.StackWalk64(IMAGE_FILE_MACHINE_AMD64, hProc, hT, ctypes.byref(sf),
ctypes.byref(ctx), read_mem,
dbg.SymFunctionTableAccess64, dbg.SymGetModuleBase64,
None, 0):
break
if sf.AddrPC.Offset == 0:
break
frames.append(modname(sf.AddrPC.Offset))
if sf.AddrPC.Offset == sf.AddrReturn.Offset:
break
if i > 0 and frames[-1] == frames[-2]:
break
k32.ResumeThread(hT)
k32.CloseHandle(hT)
print("=== tid %d (rip %s) ===" % (tid, modname(ctx.Rip)))
for f in frames:
print(" ", f)
print("--- log tail ---")
try:
with open(LOG, 'rb') as f:
f.seek(max(0, os.path.getsize(LOG) - 1200))
print(f.read().decode('utf-8', 'replace'))
except FileNotFoundError:
pass