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