| # DExTer : Debugging Experience Tester |
| # ~~~~~~ ~ ~~ ~ ~~ |
| # |
| # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| # See https://llvm.org/LICENSE.txt for license information. |
| # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| |
| from ctypes import * |
| from enum import * |
| from functools import partial |
| |
| from .utils import * |
| from . import control |
| from . import symbols |
| from . import sysobjs |
| |
| |
| class DebugAttach(IntFlag): |
| DEBUG_ATTACH_DEFAULT = 0 |
| DEBUG_ATTACH_NONINVASIVE = 1 |
| DEBUG_ATTACH_EXISTING = 2 |
| DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND = 4 |
| DEBUG_ATTACH_INVASIVE_NO_INITIAL_BREAK = 8 |
| DEBUG_ATTACH_INVASIVE_RESUME_PROCESS = 0x10 |
| DEBUG_ATTACH_NONINVASIVE_ALLOW_PARTIAL = 0x20 |
| |
| |
| # UUID for DebugClient7 interface. |
| DebugClient7IID = IID( |
| 0x13586BE3, |
| 0x542E, |
| 0x481E, |
| IID_Data4_Type(0xB1, 0xF2, 0x84, 0x97, 0xBA, 0x74, 0xF9, 0xA9), |
| ) |
| |
| |
| class DEBUG_CREATE_PROCESS_OPTIONS(Structure): |
| _fields_ = [ |
| ("CreateFlags", c_ulong), |
| ("EngCreateFlags", c_ulong), |
| ("VerifierFlags", c_ulong), |
| ("Reserved", c_ulong), |
| ] |
| |
| |
| class IDebugClient7(Structure): |
| pass |
| |
| |
| class IDebugClient7Vtbl(Structure): |
| wrp = partial(WINFUNCTYPE, c_long, POINTER(IDebugClient7)) |
| idc_queryinterface = wrp(POINTER(IID), POINTER(c_void_p)) |
| idc_attachprocess = wrp(c_longlong, c_long, c_long) |
| idc_detachprocesses = wrp() |
| idc_terminateprocesses = wrp() |
| idc_createprocessandattach2 = wrp( |
| c_ulonglong, c_char_p, c_void_p, c_ulong, c_char_p, c_char_p, c_ulong, c_ulong |
| ) |
| _fields_ = [ |
| ("QueryInterface", idc_queryinterface), |
| ("AddRef", c_void_p), |
| ("Release", c_void_p), |
| ("AttachKernel", c_void_p), |
| ("GetKernelConnectionOptions", c_void_p), |
| ("SetKernelConnectionOptions", c_void_p), |
| ("StartProcessServer", c_void_p), |
| ("ConnectProcessServer", c_void_p), |
| ("DisconnectProcessServer", c_void_p), |
| ("GetRunningProcessSystemIds", c_void_p), |
| ("GetRunningProcessSystemIdsByExecutableName", c_void_p), |
| ("GetRunningProcessDescription", c_void_p), |
| ("AttachProcess", idc_attachprocess), |
| ("CreateProcess", c_void_p), |
| ("CreateProcessAndAttach", c_void_p), |
| ("GetProcessOptions", c_void_p), |
| ("AddProcessOptions", c_void_p), |
| ("RemoveProcessOptions", c_void_p), |
| ("SetProcessOptions", c_void_p), |
| ("OpenDumpFile", c_void_p), |
| ("WriteDumpFile", c_void_p), |
| ("ConnectSession", c_void_p), |
| ("StartServer", c_void_p), |
| ("OutputServers", c_void_p), |
| ("TerminateProcesses", idc_terminateprocesses), |
| ("DetachProcesses", idc_detachprocesses), |
| ("EndSession", c_void_p), |
| ("GetExitCode", c_void_p), |
| ("DispatchCallbacks", c_void_p), |
| ("ExitDispatch", c_void_p), |
| ("CreateClient", c_void_p), |
| ("GetInputCallbacks", c_void_p), |
| ("SetInputCallbacks", c_void_p), |
| ("GetOutputCallbacks", c_void_p), |
| ("SetOutputCallbacks", c_void_p), |
| ("GetOutputMask", c_void_p), |
| ("SetOutputMask", c_void_p), |
| ("GetOtherOutputMask", c_void_p), |
| ("SetOtherOutputMask", c_void_p), |
| ("GetOutputWidth", c_void_p), |
| ("SetOutputWidth", c_void_p), |
| ("GetOutputLinePrefix", c_void_p), |
| ("SetOutputLinePrefix", c_void_p), |
| ("GetIdentity", c_void_p), |
| ("OutputIdentity", c_void_p), |
| ("GetEventCallbacks", c_void_p), |
| ("SetEventCallbacks", c_void_p), |
| ("FlushCallbacks", c_void_p), |
| ("WriteDumpFile2", c_void_p), |
| ("AddDumpInformationFile", c_void_p), |
| ("EndProcessServer", c_void_p), |
| ("WaitForProcessServerEnd", c_void_p), |
| ("IsKernelDebuggerEnabled", c_void_p), |
| ("TerminateCurrentProcess", c_void_p), |
| ("DetachCurrentProcess", c_void_p), |
| ("AbandonCurrentProcess", c_void_p), |
| ("GetRunningProcessSystemIdByExecutableNameWide", c_void_p), |
| ("GetRunningProcessDescriptionWide", c_void_p), |
| ("CreateProcessWide", c_void_p), |
| ("CreateProcessAndAttachWide", c_void_p), |
| ("OpenDumpFileWide", c_void_p), |
| ("WriteDumpFileWide", c_void_p), |
| ("AddDumpInformationFileWide", c_void_p), |
| ("GetNumberDumpFiles", c_void_p), |
| ("GetDumpFile", c_void_p), |
| ("GetDumpFileWide", c_void_p), |
| ("AttachKernelWide", c_void_p), |
| ("GetKernelConnectionOptionsWide", c_void_p), |
| ("SetKernelConnectionOptionsWide", c_void_p), |
| ("StartProcessServerWide", c_void_p), |
| ("ConnectProcessServerWide", c_void_p), |
| ("StartServerWide", c_void_p), |
| ("OutputServerWide", c_void_p), |
| ("GetOutputCallbacksWide", c_void_p), |
| ("SetOutputCallbacksWide", c_void_p), |
| ("GetOutputLinePrefixWide", c_void_p), |
| ("SetOutputLinePrefixWide", c_void_p), |
| ("GetIdentityWide", c_void_p), |
| ("OutputIdentityWide", c_void_p), |
| ("GetEventCallbacksWide", c_void_p), |
| ("SetEventCallbacksWide", c_void_p), |
| ("CreateProcess2", c_void_p), |
| ("CreateProcess2Wide", c_void_p), |
| ("CreateProcessAndAttach2", idc_createprocessandattach2), |
| ("CreateProcessAndAttach2Wide", c_void_p), |
| ("PushOutputLinePrefix", c_void_p), |
| ("PushOutputLinePrefixWide", c_void_p), |
| ("PopOutputLinePrefix", c_void_p), |
| ("GetNumberInputCallbacks", c_void_p), |
| ("GetNumberOutputCallbacks", c_void_p), |
| ("GetNumberEventCallbacks", c_void_p), |
| ("GetQuitLockString", c_void_p), |
| ("SetQuitLockString", c_void_p), |
| ("GetQuitLockStringWide", c_void_p), |
| ("SetQuitLockStringWide", c_void_p), |
| ("SetEventContextCallbacks", c_void_p), |
| ("SetClientContext", c_void_p), |
| ] |
| |
| |
| IDebugClient7._fields_ = [("lpVtbl", POINTER(IDebugClient7Vtbl))] |
| |
| |
| class Client(object): |
| def __init__(self): |
| DbgEng = WinDLL("DbgEng") |
| DbgEng.DebugCreate.argtypes = [POINTER(IID), POINTER(POINTER(IDebugClient7))] |
| DbgEng.DebugCreate.restype = c_ulong |
| |
| # Call DebugCreate to create a new debug client |
| ptr = POINTER(IDebugClient7)() |
| res = DbgEng.DebugCreate(byref(DebugClient7IID), ptr) |
| aborter(res, "DebugCreate") |
| self.client = ptr.contents |
| self.vt = vt = self.client.lpVtbl.contents |
| |
| def QI(iface, ptr): |
| return vt.QueryInterface(self.client, byref(iface), byref(ptr)) |
| |
| # Query for a control object |
| ptr = c_void_p() |
| res = QI(control.DebugControl7IID, ptr) |
| aborter(res, "QueryInterface control") |
| self.control_ptr = cast(ptr, POINTER(control.IDebugControl7)) |
| self.Control = control.Control(self.control_ptr) |
| |
| # Query for a SystemObjects object |
| ptr = c_void_p() |
| res = QI(sysobjs.DebugSystemObjects4IID, ptr) |
| aborter(res, "QueryInterface sysobjects") |
| self.sysobjects_ptr = cast(ptr, POINTER(sysobjs.IDebugSystemObjects4)) |
| self.SysObjects = sysobjs.SysObjects(self.sysobjects_ptr) |
| |
| # Query for a Symbols object |
| ptr = c_void_p() |
| res = QI(symbols.DebugSymbols5IID, ptr) |
| aborter(res, "QueryInterface debugsymbosl5") |
| self.symbols_ptr = cast(ptr, POINTER(symbols.IDebugSymbols5)) |
| self.Symbols = symbols.Symbols(self.symbols_ptr) |
| |
| def AttachProcess(self, pid): |
| # Zero process-server id means no process-server. |
| res = self.vt.AttachProcess( |
| self.client, 0, pid, DebugAttach.DEBUG_ATTACH_DEFAULT |
| ) |
| aborter(res, "AttachProcess") |
| return |
| |
| def DetachProcesses(self): |
| res = self.vt.DetachProcesses(self.client) |
| aborter(res, "DetachProcesses") |
| return |
| |
| def TerminateProcesses(self): |
| res = self.vt.TerminateProcesses(self.client) |
| aborter(res, "TerminateProcesses") |
| return |
| |
| def CreateProcessAndAttach2(self, cmdline): |
| options = DEBUG_CREATE_PROCESS_OPTIONS() |
| options.CreateFlags = 0x2 # DEBUG_ONLY_THIS_PROCESS |
| options.EngCreateFlags = 0 |
| options.VerifierFlags = 0 |
| options.Reserved = 0 |
| attach_flags = 0 |
| res = self.vt.CreateProcessAndAttach2( |
| self.client, |
| 0, |
| cmdline.encode("ascii"), |
| byref(options), |
| sizeof(options), |
| None, |
| None, |
| 0, |
| attach_flags, |
| ) |
| aborter(res, "CreateProcessAndAttach2") |
| return |