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