205 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 8import sys9import os10import platform11 12from dex.debugger.DebuggerBase import DebuggerBase, watch_is_active13from dex.dextIR import FrameIR, LocIR, StepIR, StopReason, ValueIR14from dex.dextIR import ProgramState, StackFrame, SourceLocation15from dex.utils.Exceptions import DebuggerException, LoadDebuggerException16from dex.utils.ReturnCode import ReturnCode17 18if platform.system() == "Windows":19 # Don't load on linux; _load_interface will croak before any names are used.20 from . import setup21 from . import probe_process22 from . import breakpoint23 24 25class DbgEng(DebuggerBase):26 def __init__(self, context, *args):27 self.breakpoints = []28 self.running = False29 self.finished = False30 self.step_info = None31 super(DbgEng, self).__init__(context, *args)32 33 def _custom_init(self):34 try:35 res = setup.setup_everything(self.context.options.executable)36 self.client = res37 self.running = True38 except Exception as e:39 raise Exception("Failed to start debuggee: {}".format(e))40 41 def _custom_exit(self):42 setup.cleanup(self.client)43 44 def _load_interface(self):45 arch = platform.architecture()[0]46 machine = platform.machine()47 if arch == "32bit" and machine == "AMD64":48 # This python process is 32 bits, but is sitting on a 64 bit machine.49 # Bad things may happen, don't support it.50 raise LoadDebuggerException(51 "Can't run Dexter dbgeng on 32 bit python in a 64 bit environment"52 )53 54 if platform.system() != "Windows":55 raise LoadDebuggerException("DbgEng supports Windows only")56 57 # Otherwise, everything was imported earlier58 59 @classmethod60 def get_name(cls):61 return "dbgeng"62 63 @classmethod64 def get_option_name(cls):65 return "dbgeng"66 67 @property68 def frames_below_main(self):69 return []70 71 @property72 def version(self):73 # I don't believe there's a well defined DbgEng version, outside of the74 # version of Windows being used.75 return "1"76 77 def clear_breakpoints(self):78 for x in self.breakpoints:79 x.RemoveFlags(breakpoint.BreakpointFlags.DEBUG_BREAKPOINT_ENABLED)80 self.client.Control.RemoveBreakpoint(x)81 82 def _add_breakpoint(self, file_, line):83 # Breakpoint setting/deleting is not supported by dbgeng at this moment84 # but is something that should be considered in the future.85 # TODO: this method is called in the DefaultController but has no effect.86 pass87 88 def _add_conditional_breakpoint(self, file_, line, condition):89 # breakpoint setting/deleting is not supported by dbgeng at this moment90 # but is something that should be considered in the future.91 raise NotImplementedError(92 "add_conditional_breakpoint is not yet implemented by dbgeng"93 )94 95 def get_triggered_breakpoint_ids(self):96 raise NotImplementedError(97 "get_triggered_breakpoint_ids is not yet implemented by dbgeng"98 )99 100 def delete_breakpoints(self, ids):101 # breakpoint setting/deleting is not supported by dbgeng at this moment102 # but is something that should be considered in the future.103 raise NotImplementedError(104 "delete_conditional_breakpoint is not yet implemented by dbgeng"105 )106 107 def launch(self, cmdline):108 assert (109 len(cmdline) == 0 and not self.context.options.target_run_args110 ), "Command lines unimplemented for dbgeng right now"111 # We are, by this point, already launched.112 self.step_info = probe_process.probe_state(self.client)113 114 def step_in(self):115 res = setup.step_once(self.client)116 if not res:117 self.finished = True118 self.step_info = res119 120 def go(self):121 # FIXME: running freely doesn't seem to reliably stop when back in a122 # relevant source file -- this is likely to be a problem when setting123 # breakpoints. Until that's fixed, single step instead of running124 # freely. This isn't very efficient, but at least makes progress.125 self.step_in()126 127 def _get_step_info(self, watches, step_index):128 frames = self.step_info129 state_frames = []130 131 # For now assume the base function is the... function, ignoring132 # inlining.133 dex_frames = []134 for i, x in enumerate(frames):135 # XXX Might be able to get columns out through136 # GetSourceEntriesByOffset, not a priority now137 loc = LocIR(path=x.source_file, lineno=x.line_no, column=0)138 new_frame = FrameIR(function=x.function_name, is_inlined=False, loc=loc)139 dex_frames.append(new_frame)140 141 state_frame = StackFrame(142 function=new_frame.function,143 is_inlined=new_frame.is_inlined,144 location=SourceLocation(path=x.source_file, lineno=x.line_no, column=0),145 watches={},146 )147 for expr in map(148 # Filter out watches that are not active in the current frame,149 # and then evaluate all the active watches.150 lambda watch_info, idx=i: self.evaluate_expression(151 watch_info.expression, idx152 ),153 filter(154 lambda watch_info, idx=i, line_no=loc.lineno, path=loc.path: watch_is_active(155 watch_info, path, idx, line_no156 ),157 watches,158 ),159 ):160 state_frame.watches[expr.expression] = expr161 state_frames.append(state_frame)162 163 return StepIR(164 step_index=step_index,165 frames=dex_frames,166 stop_reason=StopReason.STEP,167 program_state=ProgramState(state_frames),168 )169 170 @property171 def is_running(self):172 return False # We're never free-running173 174 @property175 def is_finished(self):176 return self.finished177 178 def evaluate_expression(self, expression, frame_idx=0):179 # XXX: cdb insists on using '->' to examine fields of structures,180 # as it appears to reserve '.' for other purposes.181 fixed_expr = expression.replace(".", "->")182 183 orig_scope_idx = self.client.Symbols.GetCurrentScopeFrameIndex()184 self.client.Symbols.SetScopeFrameByIndex(frame_idx)185 186 res = self.client.Control.Evaluate(fixed_expr)187 if res is not None:188 result, typename = self.client.Control.Evaluate(fixed_expr)189 could_eval = True190 else:191 result, typename = (None, None)192 could_eval = False193 194 self.client.Symbols.SetScopeFrameByIndex(orig_scope_idx)195 196 return ValueIR(197 expression=expression,198 value=str(result),199 type_name=typename,200 error_string="",201 could_evaluate=could_eval,202 is_optimized_away=False,203 is_irretrievable=not could_eval,204 )205