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