1# DExTer : Debugging Experience Tester
2# ~~~~~~   ~         ~~         ~   ~~
3#
4# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5# See https://llvm.org/LICENSE.txt for license information.
6# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7
8from ctypes import *
9from enum import *
10from functools import partial
11
12from .utils import *
13from . import control
14from . import symbols
15from . import sysobjs
16
17class DebugAttach(IntFlag):
18  DEBUG_ATTACH_DEFAULT =                      0
19  DEBUG_ATTACH_NONINVASIVE =                  1
20  DEBUG_ATTACH_EXISTING =                     2
21  DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND =       4
22  DEBUG_ATTACH_INVASIVE_NO_INITIAL_BREAK =    8
23  DEBUG_ATTACH_INVASIVE_RESUME_PROCESS =   0x10
24  DEBUG_ATTACH_NONINVASIVE_ALLOW_PARTIAL = 0x20
25
26# UUID for DebugClient7 interface.
27DebugClient7IID = IID(0x13586be3, 0x542e, 0x481e, IID_Data4_Type(0xb1, 0xf2, 0x84, 0x97, 0xba, 0x74, 0xf9, 0xa9 ))
28
29class DEBUG_CREATE_PROCESS_OPTIONS(Structure):
30  _fields_ = [
31    ("CreateFlags", c_ulong),
32    ("EngCreateFlags", c_ulong),
33    ("VerifierFlags", c_ulong),
34    ("Reserved", c_ulong)
35  ]
36
37class IDebugClient7(Structure):
38  pass
39
40class IDebugClient7Vtbl(Structure):
41  wrp = partial(WINFUNCTYPE, c_long, POINTER(IDebugClient7))
42  idc_queryinterface = wrp(POINTER(IID), POINTER(c_void_p))
43  idc_attachprocess = wrp(c_longlong, c_long, c_long)
44  idc_detachprocesses = wrp()
45  idc_terminateprocesses = wrp()
46  idc_createprocessandattach2 = wrp(c_ulonglong, c_char_p, c_void_p, c_ulong, c_char_p, c_char_p, c_ulong, c_ulong)
47  _fields_ = [
48      ("QueryInterface", idc_queryinterface),
49      ("AddRef", c_void_p),
50      ("Release", c_void_p),
51      ("AttachKernel", c_void_p),
52      ("GetKernelConnectionOptions", c_void_p),
53      ("SetKernelConnectionOptions", c_void_p),
54      ("StartProcessServer", c_void_p),
55      ("ConnectProcessServer", c_void_p),
56      ("DisconnectProcessServer", c_void_p),
57      ("GetRunningProcessSystemIds", c_void_p),
58      ("GetRunningProcessSystemIdsByExecutableName", c_void_p),
59      ("GetRunningProcessDescription", c_void_p),
60      ("AttachProcess", idc_attachprocess),
61      ("CreateProcess", c_void_p),
62      ("CreateProcessAndAttach", c_void_p),
63      ("GetProcessOptions", c_void_p),
64      ("AddProcessOptions", c_void_p),
65      ("RemoveProcessOptions", c_void_p),
66      ("SetProcessOptions", c_void_p),
67      ("OpenDumpFile", c_void_p),
68      ("WriteDumpFile", c_void_p),
69      ("ConnectSession", c_void_p),
70      ("StartServer", c_void_p),
71      ("OutputServers", c_void_p),
72      ("TerminateProcesses", idc_terminateprocesses),
73      ("DetachProcesses", idc_detachprocesses),
74      ("EndSession", c_void_p),
75      ("GetExitCode", c_void_p),
76      ("DispatchCallbacks", c_void_p),
77      ("ExitDispatch", c_void_p),
78      ("CreateClient", c_void_p),
79      ("GetInputCallbacks", c_void_p),
80      ("SetInputCallbacks", c_void_p),
81      ("GetOutputCallbacks", c_void_p),
82      ("SetOutputCallbacks", c_void_p),
83      ("GetOutputMask", c_void_p),
84      ("SetOutputMask", c_void_p),
85      ("GetOtherOutputMask", c_void_p),
86      ("SetOtherOutputMask", c_void_p),
87      ("GetOutputWidth", c_void_p),
88      ("SetOutputWidth", c_void_p),
89      ("GetOutputLinePrefix", c_void_p),
90      ("SetOutputLinePrefix", c_void_p),
91      ("GetIdentity", c_void_p),
92      ("OutputIdentity", c_void_p),
93      ("GetEventCallbacks", c_void_p),
94      ("SetEventCallbacks", c_void_p),
95      ("FlushCallbacks", c_void_p),
96      ("WriteDumpFile2", c_void_p),
97      ("AddDumpInformationFile", c_void_p),
98      ("EndProcessServer", c_void_p),
99      ("WaitForProcessServerEnd", c_void_p),
100      ("IsKernelDebuggerEnabled", c_void_p),
101      ("TerminateCurrentProcess", c_void_p),
102      ("DetachCurrentProcess", c_void_p),
103      ("AbandonCurrentProcess", c_void_p),
104      ("GetRunningProcessSystemIdByExecutableNameWide", c_void_p),
105      ("GetRunningProcessDescriptionWide", c_void_p),
106      ("CreateProcessWide", c_void_p),
107      ("CreateProcessAndAttachWide", c_void_p),
108      ("OpenDumpFileWide", c_void_p),
109      ("WriteDumpFileWide", c_void_p),
110      ("AddDumpInformationFileWide", c_void_p),
111      ("GetNumberDumpFiles", c_void_p),
112      ("GetDumpFile", c_void_p),
113      ("GetDumpFileWide", c_void_p),
114      ("AttachKernelWide", c_void_p),
115      ("GetKernelConnectionOptionsWide", c_void_p),
116      ("SetKernelConnectionOptionsWide", c_void_p),
117      ("StartProcessServerWide", c_void_p),
118      ("ConnectProcessServerWide", c_void_p),
119      ("StartServerWide", c_void_p),
120      ("OutputServerWide", c_void_p),
121      ("GetOutputCallbacksWide", c_void_p),
122      ("SetOutputCallbacksWide", c_void_p),
123      ("GetOutputLinePrefixWide", c_void_p),
124      ("SetOutputLinePrefixWide", c_void_p),
125      ("GetIdentityWide", c_void_p),
126      ("OutputIdentityWide", c_void_p),
127      ("GetEventCallbacksWide", c_void_p),
128      ("SetEventCallbacksWide", c_void_p),
129      ("CreateProcess2", c_void_p),
130      ("CreateProcess2Wide", c_void_p),
131      ("CreateProcessAndAttach2", idc_createprocessandattach2),
132      ("CreateProcessAndAttach2Wide", c_void_p),
133      ("PushOutputLinePrefix", c_void_p),
134      ("PushOutputLinePrefixWide", c_void_p),
135      ("PopOutputLinePrefix", c_void_p),
136      ("GetNumberInputCallbacks", c_void_p),
137      ("GetNumberOutputCallbacks", c_void_p),
138      ("GetNumberEventCallbacks", c_void_p),
139      ("GetQuitLockString", c_void_p),
140      ("SetQuitLockString", c_void_p),
141      ("GetQuitLockStringWide", c_void_p),
142      ("SetQuitLockStringWide", c_void_p),
143      ("SetEventContextCallbacks", c_void_p),
144      ("SetClientContext", c_void_p),
145    ]
146
147IDebugClient7._fields_ = [("lpVtbl", POINTER(IDebugClient7Vtbl))]
148
149class Client(object):
150  def __init__(self):
151    DbgEng = WinDLL("DbgEng")
152    DbgEng.DebugCreate.argtypes = [POINTER(IID), POINTER(POINTER(IDebugClient7))]
153    DbgEng.DebugCreate.restype = c_ulong
154
155    # Call DebugCreate to create a new debug client
156    ptr = POINTER(IDebugClient7)()
157    res = DbgEng.DebugCreate(byref(DebugClient7IID), ptr)
158    aborter(res, "DebugCreate")
159    self.client = ptr.contents
160    self.vt = vt = self.client.lpVtbl.contents
161
162    def QI(iface, ptr):
163      return vt.QueryInterface(self.client, byref(iface), byref(ptr))
164
165    # Query for a control object
166    ptr = c_void_p()
167    res = QI(control.DebugControl7IID, ptr)
168    aborter(res, "QueryInterface control")
169    self.control_ptr = cast(ptr, POINTER(control.IDebugControl7))
170    self.Control = control.Control(self.control_ptr)
171
172    # Query for a SystemObjects object
173    ptr = c_void_p()
174    res = QI(sysobjs.DebugSystemObjects4IID, ptr)
175    aborter(res, "QueryInterface sysobjects")
176    self.sysobjects_ptr = cast(ptr, POINTER(sysobjs.IDebugSystemObjects4))
177    self.SysObjects = sysobjs.SysObjects(self.sysobjects_ptr)
178
179    # Query for a Symbols object
180    ptr = c_void_p()
181    res = QI(symbols.DebugSymbols5IID, ptr)
182    aborter(res, "QueryInterface debugsymbosl5")
183    self.symbols_ptr = cast(ptr, POINTER(symbols.IDebugSymbols5))
184    self.Symbols = symbols.Symbols(self.symbols_ptr)
185
186  def AttachProcess(self, pid):
187    # Zero process-server id means no process-server.
188    res = self.vt.AttachProcess(self.client, 0, pid, DebugAttach.DEBUG_ATTACH_DEFAULT)
189    aborter(res, "AttachProcess")
190    return
191
192  def DetachProcesses(self):
193    res = self.vt.DetachProcesses(self.client)
194    aborter(res, "DetachProcesses")
195    return
196
197  def TerminateProcesses(self):
198    res = self.vt.TerminateProcesses(self.client)
199    aborter(res, "TerminateProcesses")
200    return
201
202  def CreateProcessAndAttach2(self, cmdline):
203    options = DEBUG_CREATE_PROCESS_OPTIONS()
204    options.CreateFlags = 0x2 # DEBUG_ONLY_THIS_PROCESS
205    options.EngCreateFlags  = 0
206    options.VerifierFlags = 0
207    options.Reserved = 0
208    attach_flags = 0
209    res = self.vt.CreateProcessAndAttach2(self.client, 0, cmdline.encode("ascii"), byref(options), sizeof(options), None, None, 0, attach_flags)
210    aborter(res, "CreateProcessAndAttach2")
211    return
212