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 functools import partial
10
11from .utils import *
12from .breakpoint import *
13
14class DEBUG_STACK_FRAME_EX(Structure):
15  _fields_ = [
16      ("InstructionOffset", c_ulonglong),
17      ("ReturnOffset", c_ulonglong),
18      ("FrameOffset", c_ulonglong),
19      ("StackOffset", c_ulonglong),
20      ("FuncTableEntry", c_ulonglong),
21      ("Params", c_ulonglong * 4),
22      ("Reserved", c_ulonglong * 6),
23      ("Virtual", c_bool),
24      ("FrameNumber", c_ulong),
25      ("InlineFrameContext", c_ulong),
26      ("Reserved1", c_ulong)
27    ]
28PDEBUG_STACK_FRAME_EX = POINTER(DEBUG_STACK_FRAME_EX)
29
30class DEBUG_VALUE_U(Union):
31  _fields_ = [
32      ("I8", c_byte),
33      ("I16", c_short),
34      ("I32", c_int),
35      ("I64", c_long),
36      ("F32", c_float),
37      ("F64", c_double),
38      ("RawBytes", c_ubyte * 24) # Force length to 24b.
39    ]
40
41class DEBUG_VALUE(Structure):
42  _fields_ = [
43      ("U", DEBUG_VALUE_U),
44      ("TailOfRawBytes", c_ulong),
45      ("Type", c_ulong)
46    ]
47PDEBUG_VALUE = POINTER(DEBUG_VALUE)
48
49class DebugValueType(IntEnum):
50  DEBUG_VALUE_INVALID      = 0
51  DEBUG_VALUE_INT8         = 1
52  DEBUG_VALUE_INT16        = 2
53  DEBUG_VALUE_INT32        = 3
54  DEBUG_VALUE_INT64        = 4
55  DEBUG_VALUE_FLOAT32      = 5
56  DEBUG_VALUE_FLOAT64      = 6
57  DEBUG_VALUE_FLOAT80      = 7
58  DEBUG_VALUE_FLOAT82      = 8
59  DEBUG_VALUE_FLOAT128     = 9
60  DEBUG_VALUE_VECTOR64     = 10
61  DEBUG_VALUE_VECTOR128    = 11
62  DEBUG_VALUE_TYPES        = 12
63
64# UUID for DebugControl7 interface.
65DebugControl7IID = IID(0xb86fb3b1, 0x80d4, 0x475b, IID_Data4_Type(0xae, 0xa3, 0xcf, 0x06, 0x53, 0x9c, 0xf6, 0x3a))
66
67class IDebugControl7(Structure):
68  pass
69
70class IDebugControl7Vtbl(Structure):
71  wrp = partial(WINFUNCTYPE, c_long, POINTER(IDebugControl7))
72  idc_getnumbereventfilters = wrp(c_ulong_p, c_ulong_p, c_ulong_p)
73  idc_setexceptionfiltersecondcommand = wrp(c_ulong, c_char_p)
74  idc_waitforevent = wrp(c_long, c_long)
75  idc_execute = wrp(c_long, c_char_p, c_long)
76  idc_setexpressionsyntax = wrp(c_ulong)
77  idc_addbreakpoint2 = wrp(c_ulong, c_ulong, POINTER(POINTER(DebugBreakpoint2)))
78  idc_setexecutionstatus = wrp(c_ulong)
79  idc_getexecutionstatus = wrp(c_ulong_p)
80  idc_getstacktraceex = wrp(c_ulonglong, c_ulonglong, c_ulonglong, PDEBUG_STACK_FRAME_EX, c_ulong, c_ulong_p)
81  idc_evaluate = wrp(c_char_p, c_ulong, PDEBUG_VALUE, c_ulong_p)
82  idc_setengineoptions = wrp(c_ulong)
83  _fields_ = [
84      ("QueryInterface", c_void_p),
85      ("AddRef", c_void_p),
86      ("Release", c_void_p),
87      ("GetInterrupt", c_void_p),
88      ("SetInterrupt", c_void_p),
89      ("GetInterruptTimeout", c_void_p),
90      ("SetInterruptTimeout", c_void_p),
91      ("GetLogFile", c_void_p),
92      ("OpenLogFile", c_void_p),
93      ("CloseLogFile", c_void_p),
94      ("GetLogMask", c_void_p),
95      ("SetLogMask", c_void_p),
96      ("Input", c_void_p),
97      ("ReturnInput", c_void_p),
98      ("Output", c_void_p),
99      ("OutputVaList", c_void_p),
100      ("ControlledOutput", c_void_p),
101      ("ControlledOutputVaList", c_void_p),
102      ("OutputPrompt", c_void_p),
103      ("OutputPromptVaList", c_void_p),
104      ("GetPromptText", c_void_p),
105      ("OutputCurrentState", c_void_p),
106      ("OutputVersionInformation", c_void_p),
107      ("GetNotifyEventHandle", c_void_p),
108      ("SetNotifyEventHandle", c_void_p),
109      ("Assemble", c_void_p),
110      ("Disassemble", c_void_p),
111      ("GetDisassembleEffectiveOffset", c_void_p),
112      ("OutputDisassembly", c_void_p),
113      ("OutputDisassemblyLines", c_void_p),
114      ("GetNearInstruction", c_void_p),
115      ("GetStackTrace", c_void_p),
116      ("GetReturnOffset", c_void_p),
117      ("OutputStackTrace", c_void_p),
118      ("GetDebuggeeType", c_void_p),
119      ("GetActualProcessorType", c_void_p),
120      ("GetExecutingProcessorType", c_void_p),
121      ("GetNumberPossibleExecutingProcessorTypes", c_void_p),
122      ("GetPossibleExecutingProcessorTypes", c_void_p),
123      ("GetNumberProcessors", c_void_p),
124      ("GetSystemVersion", c_void_p),
125      ("GetPageSize", c_void_p),
126      ("IsPointer64Bit", c_void_p),
127      ("ReadBugCheckData", c_void_p),
128      ("GetNumberSupportedProcessorTypes", c_void_p),
129      ("GetSupportedProcessorTypes", c_void_p),
130      ("GetProcessorTypeNames", c_void_p),
131      ("GetEffectiveProcessorType", c_void_p),
132      ("SetEffectiveProcessorType", c_void_p),
133      ("GetExecutionStatus", idc_getexecutionstatus),
134      ("SetExecutionStatus", idc_setexecutionstatus),
135      ("GetCodeLevel", c_void_p),
136      ("SetCodeLevel", c_void_p),
137      ("GetEngineOptions", c_void_p),
138      ("AddEngineOptions", c_void_p),
139      ("RemoveEngineOptions", c_void_p),
140      ("SetEngineOptions", idc_setengineoptions),
141      ("GetSystemErrorControl", c_void_p),
142      ("SetSystemErrorControl", c_void_p),
143      ("GetTextMacro", c_void_p),
144      ("SetTextMacro", c_void_p),
145      ("GetRadix", c_void_p),
146      ("SetRadix", c_void_p),
147      ("Evaluate", idc_evaluate),
148      ("CoerceValue", c_void_p),
149      ("CoerceValues", c_void_p),
150      ("Execute", idc_execute),
151      ("ExecuteCommandFile", c_void_p),
152      ("GetNumberBreakpoints", c_void_p),
153      ("GetBreakpointByIndex", c_void_p),
154      ("GetBreakpointById", c_void_p),
155      ("GetBreakpointParameters", c_void_p),
156      ("AddBreakpoint", c_void_p),
157      ("RemoveBreakpoint", c_void_p),
158      ("AddExtension", c_void_p),
159      ("RemoveExtension", c_void_p),
160      ("GetExtensionByPath", c_void_p),
161      ("CallExtension", c_void_p),
162      ("GetExtensionFunction", c_void_p),
163      ("GetWindbgExtensionApis32", c_void_p),
164      ("GetWindbgExtensionApis64", c_void_p),
165      ("GetNumberEventFilters", idc_getnumbereventfilters),
166      ("GetEventFilterText", c_void_p),
167      ("GetEventFilterCommand", c_void_p),
168      ("SetEventFilterCommand", c_void_p),
169      ("GetSpecificFilterParameters", c_void_p),
170      ("SetSpecificFilterParameters", c_void_p),
171      ("GetSpecificFilterArgument", c_void_p),
172      ("SetSpecificFilterArgument", c_void_p),
173      ("GetExceptionFilterParameters", c_void_p),
174      ("SetExceptionFilterParameters", c_void_p),
175      ("GetExceptionFilterSecondCommand", c_void_p),
176      ("SetExceptionFilterSecondCommand", idc_setexceptionfiltersecondcommand),
177      ("WaitForEvent", idc_waitforevent),
178      ("GetLastEventInformation", c_void_p),
179      ("GetCurrentTimeDate", c_void_p),
180      ("GetCurrentSystemUpTime", c_void_p),
181      ("GetDumpFormatFlags", c_void_p),
182      ("GetNumberTextReplacements", c_void_p),
183      ("GetTextReplacement", c_void_p),
184      ("SetTextReplacement", c_void_p),
185      ("RemoveTextReplacements", c_void_p),
186      ("OutputTextReplacements", c_void_p),
187      ("GetAssemblyOptions", c_void_p),
188      ("AddAssemblyOptions", c_void_p),
189      ("RemoveAssemblyOptions", c_void_p),
190      ("SetAssemblyOptions", c_void_p),
191      ("GetExpressionSyntax", c_void_p),
192      ("SetExpressionSyntax", idc_setexpressionsyntax),
193      ("SetExpressionSyntaxByName", c_void_p),
194      ("GetNumberExpressionSyntaxes", c_void_p),
195      ("GetExpressionSyntaxNames", c_void_p),
196      ("GetNumberEvents", c_void_p),
197      ("GetEventIndexDescription", c_void_p),
198      ("GetCurrentEventIndex", c_void_p),
199      ("SetNextEventIndex", c_void_p),
200      ("GetLogFileWide", c_void_p),
201      ("OpenLogFileWide", c_void_p),
202      ("InputWide", c_void_p),
203      ("ReturnInputWide", c_void_p),
204      ("OutputWide", c_void_p),
205      ("OutputVaListWide", c_void_p),
206      ("ControlledOutputWide", c_void_p),
207      ("ControlledOutputVaListWide", c_void_p),
208      ("OutputPromptWide", c_void_p),
209      ("OutputPromptVaListWide", c_void_p),
210      ("GetPromptTextWide", c_void_p),
211      ("AssembleWide", c_void_p),
212      ("DisassembleWide", c_void_p),
213      ("GetProcessrTypeNamesWide", c_void_p),
214      ("GetTextMacroWide", c_void_p),
215      ("SetTextMacroWide", c_void_p),
216      ("EvaluateWide", c_void_p),
217      ("ExecuteWide", c_void_p),
218      ("ExecuteCommandFileWide", c_void_p),
219      ("GetBreakpointByIndex2", c_void_p),
220      ("GetBreakpointById2", c_void_p),
221      ("AddBreakpoint2", idc_addbreakpoint2),
222      ("RemoveBreakpoint2", c_void_p),
223      ("AddExtensionWide", c_void_p),
224      ("GetExtensionByPathWide", c_void_p),
225      ("CallExtensionWide", c_void_p),
226      ("GetExtensionFunctionWide", c_void_p),
227      ("GetEventFilterTextWide", c_void_p),
228      ("GetEventfilterCommandWide", c_void_p),
229      ("SetEventFilterCommandWide", c_void_p),
230      ("GetSpecificFilterArgumentWide", c_void_p),
231      ("SetSpecificFilterArgumentWide", c_void_p),
232      ("GetExceptionFilterSecondCommandWide", c_void_p),
233      ("SetExceptionFilterSecondCommandWider", c_void_p),
234      ("GetLastEventInformationWide", c_void_p),
235      ("GetTextReplacementWide", c_void_p),
236      ("SetTextReplacementWide", c_void_p),
237      ("SetExpressionSyntaxByNameWide", c_void_p),
238      ("GetExpressionSyntaxNamesWide", c_void_p),
239      ("GetEventIndexDescriptionWide", c_void_p),
240      ("GetLogFile2", c_void_p),
241      ("OpenLogFile2", c_void_p),
242      ("GetLogFile2Wide", c_void_p),
243      ("OpenLogFile2Wide", c_void_p),
244      ("GetSystemVersionValues", c_void_p),
245      ("GetSystemVersionString", c_void_p),
246      ("GetSystemVersionStringWide", c_void_p),
247      ("GetContextStackTrace", c_void_p),
248      ("OutputContextStackTrace", c_void_p),
249      ("GetStoredEventInformation", c_void_p),
250      ("GetManagedStatus", c_void_p),
251      ("GetManagedStatusWide", c_void_p),
252      ("ResetManagedStatus", c_void_p),
253      ("GetStackTraceEx", idc_getstacktraceex),
254      ("OutputStackTraceEx", c_void_p),
255      ("GetContextStackTraceEx", c_void_p),
256      ("OutputContextStackTraceEx", c_void_p),
257      ("GetBreakpointByGuid", c_void_p),
258      ("GetExecutionStatusEx", c_void_p),
259      ("GetSynchronizationStatus", c_void_p),
260      ("GetDebuggeeType2", c_void_p)
261    ]
262
263IDebugControl7._fields_ = [("lpVtbl", POINTER(IDebugControl7Vtbl))]
264
265class DebugStatus(IntEnum):
266  DEBUG_STATUS_NO_CHANGE =            0
267  DEBUG_STATUS_GO =                   1
268  DEBUG_STATUS_GO_HANDLED =           2
269  DEBUG_STATUS_GO_NOT_HANDLED =       3
270  DEBUG_STATUS_STEP_OVER =            4
271  DEBUG_STATUS_STEP_INTO =            5
272  DEBUG_STATUS_BREAK =                6
273  DEBUG_STATUS_NO_DEBUGGEE =          7
274  DEBUG_STATUS_STEP_BRANCH =          8
275  DEBUG_STATUS_IGNORE_EVENT =         9
276  DEBUG_STATUS_RESTART_REQUESTED =   10
277  DEBUG_STATUS_REVERSE_GO =          11
278  DEBUG_STATUS_REVERSE_STEP_BRANCH = 12
279  DEBUG_STATUS_REVERSE_STEP_OVER =   13
280  DEBUG_STATUS_REVERSE_STEP_INTO =   14
281  DEBUG_STATUS_OUT_OF_SYNC =         15
282  DEBUG_STATUS_WAIT_INPUT =          16
283  DEBUG_STATUS_TIMEOUT =             17
284
285class DebugSyntax(IntEnum):
286  DEBUG_EXPR_MASM = 0
287  DEBUG_EXPR_CPLUSPLUS = 1
288
289class Control(object):
290  def __init__(self, control):
291    self.ptr = control
292    self.control = control.contents
293    self.vt = self.control.lpVtbl.contents
294    # Keep a handy ulong for passing into C methods.
295    self.ulong = c_ulong()
296
297  def GetExecutionStatus(self, doprint=False):
298    ret = self.vt.GetExecutionStatus(self.control, byref(self.ulong))
299    aborter(ret, "GetExecutionStatus")
300    status = DebugStatus(self.ulong.value)
301    if doprint:
302      print("Execution status: {}".format(status))
303    return status
304
305  def SetExecutionStatus(self, status):
306    assert isinstance(status, DebugStatus)
307    res = self.vt.SetExecutionStatus(self.control, status.value)
308    aborter(res, "SetExecutionStatus")
309
310  def WaitForEvent(self, timeout=100):
311    # No flags are taken by WaitForEvent, hence 0
312    ret = self.vt.WaitForEvent(self.control, 0, timeout)
313    aborter(ret, "WaitforEvent", ignore=[S_FALSE])
314    return ret
315
316  def GetNumberEventFilters(self):
317    specific_events = c_ulong()
318    specific_exceptions = c_ulong()
319    arbitrary_exceptions = c_ulong()
320    res = self.vt.GetNumberEventFilters(self.control, byref(specific_events),
321                                    byref(specific_exceptions),
322                                    byref(arbitrary_exceptions))
323    aborter(res, "GetNumberEventFilters")
324    return (specific_events.value, specific_exceptions.value,
325            arbitrary_exceptions.value)
326
327  def SetExceptionFilterSecondCommand(self, index, command):
328    buf = create_string_buffer(command.encode('ascii'))
329    res = self.vt.SetExceptionFilterSecondCommand(self.control, index, buf)
330    aborter(res, "SetExceptionFilterSecondCommand")
331    return
332
333  def AddBreakpoint2(self, offset=None, enabled=None):
334    breakpoint = POINTER(DebugBreakpoint2)()
335    res = self.vt.AddBreakpoint2(self.control, BreakpointTypes.DEBUG_BREAKPOINT_CODE, DEBUG_ANY_ID, byref(breakpoint))
336    aborter(res, "Add breakpoint 2")
337    bp = Breakpoint(breakpoint)
338
339    if offset is not None:
340      bp.SetOffset(offset)
341    if enabled is not None and enabled:
342      bp.SetFlags(BreakpointFlags.DEBUG_BREAKPOINT_ENABLED)
343
344    return bp
345
346  def RemoveBreakpoint(self, bp):
347    res = self.vt.RemoveBreakpoint2(self.control, bp.breakpoint)
348    aborter(res, "RemoveBreakpoint2")
349    bp.die()
350
351  def GetStackTraceEx(self):
352    # XXX -- I can't find a way to query for how many stack frames there _are_
353    # in  advance. Guess 128 for now.
354    num_frames_buffer = 128
355
356    frames = (DEBUG_STACK_FRAME_EX * num_frames_buffer)()
357    numframes = c_ulong()
358
359    # First three args are frame/stack/IP offsets -- leave them as zero to
360    # default to the current instruction.
361    res = self.vt.GetStackTraceEx(self.control, 0, 0, 0, frames, num_frames_buffer, byref(numframes))
362    aborter(res, "GetStackTraceEx")
363    return frames, numframes.value
364
365  def Execute(self, command):
366    # First zero is DEBUG_OUTCTL_*, which we leave as a default, second
367    # zero is DEBUG_EXECUTE_* flags, of which we set none.
368    res = self.vt.Execute(self.control, 0, command.encode('ascii'), 0)
369    aborter(res, "Client execute")
370
371  def SetExpressionSyntax(self, cpp=True):
372    if cpp:
373      syntax = DebugSyntax.DEBUG_EXPR_CPLUSPLUS
374    else:
375      syntax = DebugSyntax.DEBUG_EXPR_MASM
376
377    res = self.vt.SetExpressionSyntax(self.control, syntax)
378    aborter(res, "SetExpressionSyntax")
379
380  def Evaluate(self, expr):
381    ptr = DEBUG_VALUE()
382    res = self.vt.Evaluate(self.control, expr.encode("ascii"), DebugValueType.DEBUG_VALUE_INVALID, byref(ptr), None)
383    aborter(res, "Evaluate", ignore=[E_INTERNALEXCEPTION, E_FAIL])
384    if res != 0:
385      return None
386
387    val_type = DebugValueType(ptr.Type)
388
389    # Here's a map from debug value types to fields. Unclear what happens
390    # with unsigned values, as DbgEng doesn't present any unsigned fields.
391
392    extract_map = {
393      DebugValueType.DEBUG_VALUE_INT8    : ("I8", "char"),
394      DebugValueType.DEBUG_VALUE_INT16   : ("I16", "short"),
395      DebugValueType.DEBUG_VALUE_INT32   : ("I32", "int"),
396      DebugValueType.DEBUG_VALUE_INT64   : ("I64", "long"),
397      DebugValueType.DEBUG_VALUE_FLOAT32 : ("F32", "float"),
398      DebugValueType.DEBUG_VALUE_FLOAT64 : ("F64", "double")
399    } # And everything else is invalid.
400
401    if val_type not in extract_map:
402      raise Exception("Unexpected debug value type {} when evalutaing".format(val_type))
403
404    # Also produce a type name...
405
406    return getattr(ptr.U, extract_map[val_type][0]), extract_map[val_type][1]
407
408  def SetEngineOptions(self, opt):
409    res = self.vt.SetEngineOptions(self.control, opt)
410    aborter(res, "SetEngineOptions")
411    return
412