diff options
Diffstat (limited to 'debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio.py')
-rw-r--r-- | debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio.py | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio.py b/debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio.py new file mode 100644 index 00000000000..596dc31ab4a --- /dev/null +++ b/debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio.py @@ -0,0 +1,224 @@ +# DExTer : Debugging Experience Tester +# ~~~~~~ ~ ~~ ~ ~~ +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +"""Interface for communicating with the Visual Studio debugger via DTE.""" + +import abc +import imp +import os +import sys + +from dex.debugger.DebuggerBase import DebuggerBase +from dex.dextIR import FrameIR, LocIR, StepIR, StopReason, ValueIR +from dex.dextIR import StackFrame, SourceLocation, ProgramState +from dex.utils.Exceptions import Error, LoadDebuggerException +from dex.utils.ReturnCode import ReturnCode + + +def _load_com_module(): + try: + module_info = imp.find_module( + 'ComInterface', + [os.path.join(os.path.dirname(__file__), 'windows')]) + return imp.load_module('ComInterface', *module_info) + except ImportError as e: + raise LoadDebuggerException(e, sys.exc_info()) + + +class VisualStudio(DebuggerBase, metaclass=abc.ABCMeta): # pylint: disable=abstract-method + + # Constants for results of Debugger.CurrentMode + # (https://msdn.microsoft.com/en-us/library/envdte.debugger.currentmode.aspx) + dbgDesignMode = 1 + dbgBreakMode = 2 + dbgRunMode = 3 + + def __init__(self, *args): + self.com_module = None + self._debugger = None + self._solution = None + self._fn_step = None + self._fn_go = None + super(VisualStudio, self).__init__(*args) + + def _custom_init(self): + try: + self._debugger = self._interface.Debugger + self._debugger.HexDisplayMode = False + + self._interface.MainWindow.Visible = ( + self.context.options.show_debugger) + + self._solution = self._interface.Solution + self._solution.Create(self.context.working_directory.path, + 'DexterSolution') + + try: + self._solution.AddFromFile(self._project_file) + except OSError: + raise LoadDebuggerException( + 'could not debug the specified executable', sys.exc_info()) + + self._fn_step = self._debugger.StepInto + self._fn_go = self._debugger.Go + + except AttributeError as e: + raise LoadDebuggerException(str(e), sys.exc_info()) + + def _custom_exit(self): + if self._interface: + self._interface.Quit() + + @property + def _project_file(self): + return self.context.options.executable + + @abc.abstractproperty + def _dte_version(self): + pass + + @property + def _location(self): + bp = self._debugger.BreakpointLastHit + return { + 'path': getattr(bp, 'File', None), + 'lineno': getattr(bp, 'FileLine', None), + 'column': getattr(bp, 'FileColumn', None) + } + + @property + def _mode(self): + return self._debugger.CurrentMode + + def _load_interface(self): + self.com_module = _load_com_module() + return self.com_module.DTE(self._dte_version) + + @property + def version(self): + try: + return self._interface.Version + except AttributeError: + return None + + def clear_breakpoints(self): + for bp in self._debugger.Breakpoints: + bp.Delete() + + def add_breakpoint(self, file_, line): + self._debugger.Breakpoints.Add('', file_, line) + + def launch(self): + self.step() + + def step(self): + self._fn_step() + + def go(self) -> ReturnCode: + self._fn_go() + return ReturnCode.OK + + def set_current_stack_frame(self, idx: int = 0): + thread = self._debugger.CurrentThread + stack_frames = thread.StackFrames + try: + stack_frame = stack_frames[idx] + self._debugger.CurrentStackFrame = stack_frame.raw + except IndexError: + raise Error('attempted to access stack frame {} out of {}' + .format(idx, len(stack_frames))) + + def get_step_info(self): + thread = self._debugger.CurrentThread + stackframes = thread.StackFrames + + frames = [] + state_frames = [] + + + for idx, sf in enumerate(stackframes): + frame = FrameIR( + function=self._sanitize_function_name(sf.FunctionName), + is_inlined=sf.FunctionName.startswith('[Inline Frame]'), + loc=LocIR(path=None, lineno=None, column=None)) + + fname = frame.function or '' # pylint: disable=no-member + if any(name in fname for name in self.frames_below_main): + break + + + state_frame = StackFrame(function=frame.function, + is_inlined=frame.is_inlined, + watches={}) + + for watch in self.watches: + state_frame.watches[watch] = self.evaluate_expression( + watch, idx) + + + state_frames.append(state_frame) + frames.append(frame) + + loc = LocIR(**self._location) + if frames: + frames[0].loc = loc + state_frames[0].location = SourceLocation(**self._location) + + reason = StopReason.BREAKPOINT + if loc.path is None: # pylint: disable=no-member + reason = StopReason.STEP + + program_state = ProgramState(frames=state_frames) + + return StepIR( + step_index=self.step_index, frames=frames, stop_reason=reason, + program_state=program_state) + + @property + def is_running(self): + return self._mode == VisualStudio.dbgRunMode + + @property + def is_finished(self): + return self._mode == VisualStudio.dbgDesignMode + + @property + def frames_below_main(self): + return [ + '[Inline Frame] invoke_main', '__scrt_common_main_seh', + '__tmainCRTStartup', 'mainCRTStartup' + ] + + def evaluate_expression(self, expression, frame_idx=0) -> ValueIR: + self.set_current_stack_frame(frame_idx) + result = self._debugger.GetExpression(expression) + self.set_current_stack_frame(0) + value = result.Value + + is_optimized_away = any(s in value for s in [ + 'Variable is optimized away and not available', + 'Value is not available, possibly due to optimization', + ]) + + is_irretrievable = any(s in value for s in [ + '???', + '<Unable to read memory>', + ]) + + # an optimized away value is still counted as being able to be + # evaluated. + could_evaluate = (result.IsValidValue or is_optimized_away + or is_irretrievable) + + return ValueIR( + expression=expression, + value=value, + type_name=result.Type, + error_string=None, + is_optimized_away=is_optimized_away, + could_evaluate=could_evaluate, + is_irretrievable=is_irretrievable, + ) |