brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.0 KiB · fab423c Raw
158 lines · python
1# DExTer : Debugging Experience Tester2# ~~~~~~   ~         ~~         ~   ~~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-exception7 8from ctypes import *9 10from . import client11from . import control12from . import symbols13from .probe_process import probe_state14from .utils import *15 16 17class STARTUPINFOA(Structure):18    _fields_ = [19        ("cb", c_ulong),20        ("lpReserved", c_char_p),21        ("lpDesktop", c_char_p),22        ("lpTitle", c_char_p),23        ("dwX", c_ulong),24        ("dwY", c_ulong),25        ("dwXSize", c_ulong),26        ("dwYSize", c_ulong),27        ("dwXCountChars", c_ulong),28        ("dwYCountChars", c_ulong),29        ("dwFillAttribute", c_ulong),30        ("wShowWindow", c_ushort),31        ("cbReserved2", c_ushort),32        ("lpReserved2", c_char_p),33        ("hStdInput", c_void_p),34        ("hStdOutput", c_void_p),35        ("hStdError", c_void_p),36    ]37 38 39class PROCESS_INFORMATION(Structure):40    _fields_ = [41        ("hProcess", c_void_p),42        ("hThread", c_void_p),43        ("dwProcessId", c_ulong),44        ("dwThreadId", c_ulong),45    ]46 47 48def fetch_local_function_syms(Symbols, prefix):49    syms = Symbols.get_all_functions()50 51    def is_sym_in_src_dir(sym):52        name, data = sym53        symdata = Symbols.GetLineByOffset(data.Offset)54        if symdata is not None:55            srcfile, line = symdata56            if prefix in srcfile:57                return True58        return False59 60    syms = [x for x in syms if is_sym_in_src_dir(x)]61    return syms62 63 64def break_on_all_but_main(Control, Symbols, main_offset):65    mainfile, _ = Symbols.GetLineByOffset(main_offset)66    prefix = "\\".join(mainfile.split("\\")[:-1])67 68    for name, rec in fetch_local_function_syms(Symbols, prefix):69        if name == "main":70            continue71        bp = Control.AddBreakpoint2(offset=rec.Offset, enabled=True)72 73    # All breakpoints are currently discarded: we just sys.exit for cleanup74    return75 76 77def setup_everything(binfile):78    from . import client79    from . import symbols80 81    Client = client.Client()82 83    Client.Control.SetEngineOptions(0x20)  # DEBUG_ENGOPT_INITIAL_BREAK84 85    Client.CreateProcessAndAttach2(binfile)86 87    # Load lines as well as general symbols88    sym_opts = Client.Symbols.GetSymbolOptions()89    sym_opts |= symbols.SymbolOptionFlags.SYMOPT_LOAD_LINES90    Client.Symbols.SetSymbolOptions(sym_opts)91 92    # Need to enter the debugger engine to let it attach properly.93    res = Client.Control.WaitForEvent(timeout=1000)94    if res == S_FALSE:95        # The debugee apparently didn't do anything at all. Rather than risk96        # hanging, bail out at this point.97        client.TerminateProcesses()98        raise Exception("Debuggee did not start in a timely manner")99 100    # Enable line stepping.101    Client.Control.Execute("l+t")102    # Enable C++ expression interpretation.103    Client.Control.SetExpressionSyntax(cpp=True)104 105    # We've requested to break into the process at the earliest opportunity,106    # and WaitForEvent'ing means we should have reached that break state.107    # Now set a breakpoint on the main symbol, and "go" until we reach it.108    module_name = Client.Symbols.get_exefile_module_name()109    offset = Client.Symbols.GetOffsetByName("{}!main".format(module_name))110    breakpoint = Client.Control.AddBreakpoint2(offset=offset, enabled=True)111    Client.Control.SetExecutionStatus(control.DebugStatus.DEBUG_STATUS_GO)112 113    # Problem: there is no guarantee that the client will ever reach main,114    # something else exciting could happen in that time, the host system may115    # be very loaded, and similar. Wait for some period, say, five seconds, and116    # abort afterwards: this is a trade-off between spurious timeouts and117    # completely hanging in the case of a environmental/programming error.118    res = Client.Control.WaitForEvent(timeout=5000)119    if res == S_FALSE:120        client.TerminateProcesses()121        raise Exception("Debuggee did not reach main function in a timely manner")122 123    break_on_all_but_main(Client.Control, Client.Symbols, offset)124 125    # Set the default action on all exceptions to be "quit and detach". If we126    # don't, dbgeng will merrily spin at the exception site forever.127    filts = Client.Control.GetNumberEventFilters()128    for x in range(filts[0], filts[0] + filts[1]):129        Client.Control.SetExceptionFilterSecondCommand(x, "qd")130 131    return Client132 133 134def step_once(client):135    client.Control.Execute("p")136    try:137        client.Control.WaitForEvent()138    except Exception as e:139        if (140            client.Control.GetExecutionStatus()141            == control.DebugStatus.DEBUG_STATUS_NO_DEBUGGEE142        ):143            return None  # Debuggee has gone away, likely due to an exception.144        raise e145    # Could assert here that we're in the "break" state146    client.Control.GetExecutionStatus()147    return probe_state(client)148 149 150def main_loop(client):151    res = True152    while res is not None:153        res = step_once(client)154 155 156def cleanup(client):157    client.TerminateProcesses()158