11364750dSJames Henderson# DExTer : Debugging Experience Tester 21364750dSJames Henderson# ~~~~~~ ~ ~~ ~ ~~ 31364750dSJames Henderson# 41364750dSJames Henderson# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 51364750dSJames Henderson# See https://llvm.org/LICENSE.txt for license information. 61364750dSJames Henderson# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 71364750dSJames Henderson"""Interface for communicating with the Visual Studio debugger via DTE.""" 81364750dSJames Henderson 91364750dSJames Hendersonimport abc 101364750dSJames Hendersonimport os 111364750dSJames Hendersonimport sys 12832b91fcSOrlando Cazalet-Hyamsfrom enum import IntEnum 1373a01952SStephen Tozerfrom pathlib import PurePath, Path 147e46a721SStephen Tozerfrom collections import defaultdict, namedtuple 151364750dSJames Henderson 167e46a721SStephen Tozerfrom dex.command.CommandBase import StepExpectInfo 177e46a721SStephen Tozerfrom dex.debugger.DebuggerBase import DebuggerBase, watch_is_active 181364750dSJames Hendersonfrom dex.dextIR import FrameIR, LocIR, StepIR, StopReason, ValueIR 191364750dSJames Hendersonfrom dex.dextIR import StackFrame, SourceLocation, ProgramState 201364750dSJames Hendersonfrom dex.utils.Exceptions import Error, LoadDebuggerException 216779376eSStephen Tozerfrom dex.utils.Imports import load_module 221364750dSJames Hendersonfrom dex.utils.ReturnCode import ReturnCode 231364750dSJames Henderson 241364750dSJames Hendersondef _load_com_module(): 251364750dSJames Henderson try: 266779376eSStephen Tozer return load_module( 2723309d7dSStephen Tozer "ComInterface", 2823309d7dSStephen Tozer os.path.join(os.path.dirname(__file__), "windows"), 2923309d7dSStephen Tozer "ComInterface.py", 30f98ee40fSTobias Hieta ) 311364750dSJames Henderson except ImportError as e: 321364750dSJames Henderson raise LoadDebuggerException(e, sys.exc_info()) 331364750dSJames Henderson 341364750dSJames Henderson 351364750dSJames Henderson# VSBreakpoint(path: PurePath, line: int, col: int, cond: str). This is enough 361364750dSJames Henderson# info to identify breakpoint equivalence in visual studio based on the 371364750dSJames Henderson# properties we set through dexter currently. 38f98ee40fSTobias HietaVSBreakpoint = namedtuple("VSBreakpoint", "path, line, col, cond") 391364750dSJames Henderson 40f98ee40fSTobias Hieta 41832b91fcSOrlando Cazalet-Hyams# Visual Studio events. 42832b91fcSOrlando Cazalet-Hyams# https://learn.microsoft.com/en-us/dotnet/api/envdte.dbgeventreason?view=visualstudiosdk-2022 43832b91fcSOrlando Cazalet-Hyamsclass DbgEvent(IntEnum): 44832b91fcSOrlando Cazalet-Hyams dbgEventReasonNone = 1 45832b91fcSOrlando Cazalet-Hyams dbgEventReasonGo = 2 46832b91fcSOrlando Cazalet-Hyams dbgEventReasonAttachProgram = 3 47832b91fcSOrlando Cazalet-Hyams dbgEventReasonDetachProgram = 4 48832b91fcSOrlando Cazalet-Hyams dbgEventReasonLaunchProgram = 5 49832b91fcSOrlando Cazalet-Hyams dbgEventReasonEndProgram = 6 50832b91fcSOrlando Cazalet-Hyams dbgEventReasonStopDebugging = 7 51832b91fcSOrlando Cazalet-Hyams dbgEventReasonStep = 8 52832b91fcSOrlando Cazalet-Hyams dbgEventReasonBreakpoint = 9 53832b91fcSOrlando Cazalet-Hyams dbgEventReasonExceptionThrown = 10 54832b91fcSOrlando Cazalet-Hyams dbgEventReasonExceptionNotHandled = 11 55832b91fcSOrlando Cazalet-Hyams dbgEventReasonUserBreak = 12 56832b91fcSOrlando Cazalet-Hyams dbgEventReasonContextSwitch = 13 57832b91fcSOrlando Cazalet-Hyams 58832b91fcSOrlando Cazalet-Hyams first = dbgEventReasonNone 59832b91fcSOrlando Cazalet-Hyams last = dbgEventReasonContextSwitch 60832b91fcSOrlando Cazalet-Hyams 61f98ee40fSTobias Hietaclass VisualStudio( 62f98ee40fSTobias Hieta DebuggerBase, metaclass=abc.ABCMeta 63f98ee40fSTobias Hieta): # pylint: disable=abstract-method 641364750dSJames Henderson # Constants for results of Debugger.CurrentMode 651364750dSJames Henderson # (https://msdn.microsoft.com/en-us/library/envdte.debugger.currentmode.aspx) 661364750dSJames Henderson dbgDesignMode = 1 671364750dSJames Henderson dbgBreakMode = 2 681364750dSJames Henderson dbgRunMode = 3 691364750dSJames Henderson 701364750dSJames Henderson def __init__(self, *args): 711364750dSJames Henderson self.com_module = None 721364750dSJames Henderson self._debugger = None 731364750dSJames Henderson self._solution = None 741364750dSJames Henderson self._fn_step = None 751364750dSJames Henderson self._fn_go = None 761364750dSJames Henderson # The next available unique breakpoint id. Use self._get_next_id(). 771364750dSJames Henderson self._next_bp_id = 0 781364750dSJames Henderson # VisualStudio appears to common identical breakpoints. That is, if you 791364750dSJames Henderson # ask for a breakpoint that already exists the Breakpoints list will 801364750dSJames Henderson # not grow. DebuggerBase requires all breakpoints have a unique id, 811364750dSJames Henderson # even for duplicates, so we'll need to do some bookkeeping. Map 821364750dSJames Henderson # {VSBreakpoint: list(id)} where id is the unique dexter-side id for 831364750dSJames Henderson # the requested breakpoint. 841364750dSJames Henderson self._vs_to_dex_ids = defaultdict(list) 851364750dSJames Henderson # Map {id: VSBreakpoint} where id is unique and VSBreakpoint identifies 861364750dSJames Henderson # a breakpoint in Visual Studio. There may be many ids mapped to a 871364750dSJames Henderson # single VSBreakpoint. Use self._vs_to_dex_ids to find (dexter) 881364750dSJames Henderson # breakpoints mapped to the same visual studio breakpoint. 891364750dSJames Henderson self._dex_id_to_vs = {} 901364750dSJames Henderson 911364750dSJames Henderson super(VisualStudio, self).__init__(*args) 921364750dSJames Henderson 9375b31692SStephen Tozer def _create_solution(self): 94f98ee40fSTobias Hieta self._solution.Create(self.context.working_directory.path, "DexterSolution") 9575b31692SStephen Tozer try: 9675b31692SStephen Tozer self._solution.AddFromFile(self._project_file) 9775b31692SStephen Tozer except OSError: 9875b31692SStephen Tozer raise LoadDebuggerException( 99f98ee40fSTobias Hieta "could not debug the specified executable", sys.exc_info() 100f98ee40fSTobias Hieta ) 10175b31692SStephen Tozer 10275b31692SStephen Tozer def _load_solution(self): 10375b31692SStephen Tozer try: 10475b31692SStephen Tozer self._solution.Open(self.context.options.vs_solution) 10575b31692SStephen Tozer except: 10675b31692SStephen Tozer raise LoadDebuggerException( 107f98ee40fSTobias Hieta "could not load specified vs solution at {}".format( 108f98ee40fSTobias Hieta self.context.options.vs_solution 109f98ee40fSTobias Hieta ), 110f98ee40fSTobias Hieta sys.exc_info(), 111f98ee40fSTobias Hieta ) 11275b31692SStephen Tozer 1131364750dSJames Henderson def _custom_init(self): 1141364750dSJames Henderson try: 1151364750dSJames Henderson self._debugger = self._interface.Debugger 1161364750dSJames Henderson self._debugger.HexDisplayMode = False 1171364750dSJames Henderson 118f98ee40fSTobias Hieta self._interface.MainWindow.Visible = self.context.options.show_debugger 1191364750dSJames Henderson 1201364750dSJames Henderson self._solution = self._interface.Solution 12175b31692SStephen Tozer if self.context.options.vs_solution is None: 12275b31692SStephen Tozer self._create_solution() 12375b31692SStephen Tozer else: 12475b31692SStephen Tozer self._load_solution() 1251364750dSJames Henderson 1261364750dSJames Henderson self._fn_step = self._debugger.StepInto 1271364750dSJames Henderson self._fn_go = self._debugger.Go 1281364750dSJames Henderson 1291364750dSJames Henderson except AttributeError as e: 1301364750dSJames Henderson raise LoadDebuggerException(str(e), sys.exc_info()) 1311364750dSJames Henderson 1321364750dSJames Henderson def _custom_exit(self): 1331364750dSJames Henderson if self._interface: 1341364750dSJames Henderson self._interface.Quit() 1351364750dSJames Henderson 1361364750dSJames Henderson @property 1371364750dSJames Henderson def _project_file(self): 1381364750dSJames Henderson return self.context.options.executable 1391364750dSJames Henderson 1401364750dSJames Henderson @abc.abstractproperty 1411364750dSJames Henderson def _dte_version(self): 1421364750dSJames Henderson pass 1431364750dSJames Henderson 1441364750dSJames Henderson @property 1451364750dSJames Henderson def _location(self): 1461364750dSJames Henderson # TODO: Find a better way of determining path, line and column info 1471364750dSJames Henderson # that doesn't require reading break points. This method requires 1481364750dSJames Henderson # all lines to have a break point on them. 1491364750dSJames Henderson bp = self._debugger.BreakpointLastHit 1501364750dSJames Henderson return { 151f98ee40fSTobias Hieta "path": getattr(bp, "File", None), 152f98ee40fSTobias Hieta "lineno": getattr(bp, "FileLine", None), 153f98ee40fSTobias Hieta "column": getattr(bp, "FileColumn", None), 1541364750dSJames Henderson } 1551364750dSJames Henderson 1561364750dSJames Henderson @property 1571364750dSJames Henderson def _mode(self): 1581364750dSJames Henderson return self._debugger.CurrentMode 1591364750dSJames Henderson 1601364750dSJames Henderson def _load_interface(self): 1611364750dSJames Henderson self.com_module = _load_com_module() 1621364750dSJames Henderson return self.com_module.DTE(self._dte_version) 1631364750dSJames Henderson 1641364750dSJames Henderson @property 1651364750dSJames Henderson def version(self): 1661364750dSJames Henderson try: 1671364750dSJames Henderson return self._interface.Version 1681364750dSJames Henderson except AttributeError: 1691364750dSJames Henderson return None 1701364750dSJames Henderson 1711364750dSJames Henderson def clear_breakpoints(self): 1721364750dSJames Henderson for bp in self._debugger.Breakpoints: 1731364750dSJames Henderson bp.Delete() 1741364750dSJames Henderson self._vs_to_dex_ids.clear() 1751364750dSJames Henderson self._dex_id_to_vs.clear() 1761364750dSJames Henderson 1771364750dSJames Henderson def _add_breakpoint(self, file_, line): 178f98ee40fSTobias Hieta return self._add_conditional_breakpoint(file_, line, "") 1791364750dSJames Henderson 1801364750dSJames Henderson def _get_next_id(self): 1811364750dSJames Henderson # "Generate" a new unique id for the breakpoint. 1821364750dSJames Henderson id = self._next_bp_id 1831364750dSJames Henderson self._next_bp_id += 1 1841364750dSJames Henderson return id 1851364750dSJames Henderson 1861364750dSJames Henderson def _add_conditional_breakpoint(self, file_, line, condition): 1871364750dSJames Henderson col = 1 1881364750dSJames Henderson vsbp = VSBreakpoint(PurePath(file_), line, col, condition) 1891364750dSJames Henderson new_id = self._get_next_id() 1901364750dSJames Henderson 1911364750dSJames Henderson # Do we have an exact matching breakpoint already? 1921364750dSJames Henderson if vsbp in self._vs_to_dex_ids: 1931364750dSJames Henderson self._vs_to_dex_ids[vsbp].append(new_id) 1941364750dSJames Henderson self._dex_id_to_vs[new_id] = vsbp 1951364750dSJames Henderson return new_id 1961364750dSJames Henderson 1971364750dSJames Henderson # Breakpoint doesn't exist already. Add it now. 1981364750dSJames Henderson count_before = self._debugger.Breakpoints.Count 199f98ee40fSTobias Hieta self._debugger.Breakpoints.Add("", file_, line, col, condition) 2001364750dSJames Henderson # Our internal representation of VS says that the breakpoint doesn't 2011364750dSJames Henderson # already exist so we do not expect this operation to fail here. 2021364750dSJames Henderson assert count_before < self._debugger.Breakpoints.Count 2031364750dSJames Henderson # We've added a new breakpoint, record its id. 2041364750dSJames Henderson self._vs_to_dex_ids[vsbp].append(new_id) 2051364750dSJames Henderson self._dex_id_to_vs[new_id] = vsbp 2061364750dSJames Henderson return new_id 2071364750dSJames Henderson 2081364750dSJames Henderson def get_triggered_breakpoint_ids(self): 209f98ee40fSTobias Hieta """Returns a set of opaque ids for just-triggered breakpoints.""" 2101364750dSJames Henderson bps_hit = self._debugger.AllBreakpointsLastHit 2111364750dSJames Henderson bp_id_list = [] 2121364750dSJames Henderson # Intuitively, AllBreakpointsLastHit breakpoints are the last hit 2131364750dSJames Henderson # _bound_ breakpoints. A bound breakpoint's parent holds the info of 2141364750dSJames Henderson # the breakpoint the user requested. Our internal state tracks the user 2151364750dSJames Henderson # requested breakpoints so we look at the Parent of these triggered 2161364750dSJames Henderson # breakpoints to determine which have been hit. 2171364750dSJames Henderson for bp in bps_hit: 2181364750dSJames Henderson # All bound breakpoints should have the user-defined breakpoint as 2191364750dSJames Henderson # a parent. 2201364750dSJames Henderson assert bp.Parent 221f98ee40fSTobias Hieta vsbp = VSBreakpoint( 222f98ee40fSTobias Hieta PurePath(bp.Parent.File), 223f98ee40fSTobias Hieta bp.Parent.FileLine, 224f98ee40fSTobias Hieta bp.Parent.FileColumn, 225f98ee40fSTobias Hieta bp.Parent.Condition, 226f98ee40fSTobias Hieta ) 2271364750dSJames Henderson try: 2281364750dSJames Henderson ids = self._vs_to_dex_ids[vsbp] 2291364750dSJames Henderson except KeyError: 2301364750dSJames Henderson pass 2311364750dSJames Henderson else: 2321364750dSJames Henderson bp_id_list += ids 2331364750dSJames Henderson return set(bp_id_list) 2341364750dSJames Henderson 235b3f14802Sgbtozers def delete_breakpoints(self, ids): 236b3f14802Sgbtozers """Delete breakpoints by their ids. 2371364750dSJames Henderson 2381364750dSJames Henderson Raises a KeyError if no breakpoint with this id exists. 2391364750dSJames Henderson """ 240b3f14802Sgbtozers vsbp_set = set() 241b3f14802Sgbtozers for id in ids: 2421364750dSJames Henderson vsbp = self._dex_id_to_vs[id] 2431364750dSJames Henderson 2441364750dSJames Henderson # Remove our id from the associated list of dex ids. 2451364750dSJames Henderson self._vs_to_dex_ids[vsbp].remove(id) 2461364750dSJames Henderson del self._dex_id_to_vs[id] 2471364750dSJames Henderson 2481364750dSJames Henderson # Bail if there are other uses of this vsbp. 2491364750dSJames Henderson if len(self._vs_to_dex_ids[vsbp]) > 0: 250b3f14802Sgbtozers continue 2511364750dSJames Henderson # Otherwise find and delete it. 252b3f14802Sgbtozers vsbp_set.add(vsbp) 253b3f14802Sgbtozers 254b3f14802Sgbtozers vsbp_to_del_count = len(vsbp_set) 255b3f14802Sgbtozers 2561364750dSJames Henderson for bp in self._debugger.Breakpoints: 257b3f14802Sgbtozers # We're looking at the user-set breakpoints so there should be no 2581364750dSJames Henderson # Parent. 259*ca92bdfaSEisuke Kawashima assert bp.Parent is None 260f98ee40fSTobias Hieta this_vsbp = VSBreakpoint( 261f98ee40fSTobias Hieta PurePath(bp.File), bp.FileLine, bp.FileColumn, bp.Condition 262f98ee40fSTobias Hieta ) 263b3f14802Sgbtozers if this_vsbp in vsbp_set: 2641364750dSJames Henderson bp.Delete() 265b3f14802Sgbtozers vsbp_to_del_count -= 1 266b3f14802Sgbtozers if vsbp_to_del_count == 0: 2671364750dSJames Henderson break 268b3f14802Sgbtozers if vsbp_to_del_count: 269f98ee40fSTobias Hieta raise KeyError("did not find breakpoint to be deleted") 2701364750dSJames Henderson 2713a094d8bSJeremy Morse def _fetch_property(self, props, name): 2723a094d8bSJeremy Morse num_props = props.Count 2733a094d8bSJeremy Morse result = None 2743a094d8bSJeremy Morse for x in range(1, num_props + 1): 2753a094d8bSJeremy Morse item = props.Item(x) 2763a094d8bSJeremy Morse if item.Name == name: 2773a094d8bSJeremy Morse return item 2783a094d8bSJeremy Morse assert False, "Couldn't find property {}".format(name) 2793a094d8bSJeremy Morse 2803a094d8bSJeremy Morse def launch(self, cmdline): 28173a01952SStephen Tozer exe_path = Path(self.context.options.executable) 28273a01952SStephen Tozer self.context.logger.note(f"VS: Using executable: '{exe_path}'") 283f98ee40fSTobias Hieta cmdline_str = " ".join(cmdline) 2842e7f3393SStephen Tozer if self.context.options.target_run_args: 2852e7f3393SStephen Tozer cmdline_str += f" {self.context.options.target_run_args}" 28673a01952SStephen Tozer if cmdline_str: 28773a01952SStephen Tozer self.context.logger.note(f"VS: Using executable args: '{cmdline_str}'") 2883a094d8bSJeremy Morse 2893a094d8bSJeremy Morse # In a slightly baroque manner, lookup the VS project that runs when 2903a094d8bSJeremy Morse # you click "run", and set its command line options to the desired 2913a094d8bSJeremy Morse # command line options. 292f98ee40fSTobias Hieta startup_proj_name = str( 293f98ee40fSTobias Hieta self._fetch_property(self._interface.Solution.Properties, "StartupProject") 294f98ee40fSTobias Hieta ) 2953a094d8bSJeremy Morse project = self._fetch_property(self._interface.Solution, startup_proj_name) 296f98ee40fSTobias Hieta ActiveConfiguration = self._fetch_property( 297f98ee40fSTobias Hieta project.Properties, "ActiveConfiguration" 298f98ee40fSTobias Hieta ).Object 2993a094d8bSJeremy Morse ActiveConfiguration.DebugSettings.CommandArguments = cmdline_str 300e1c0e7e5SStephen Tozer ConfigurationName = ActiveConfiguration.ConfigurationName 301e1c0e7e5SStephen Tozer SolConfig = self._fetch_property( 302e1c0e7e5SStephen Tozer self._interface.Solution.SolutionBuild.SolutionConfigurations, 303e1c0e7e5SStephen Tozer ConfigurationName, 304e1c0e7e5SStephen Tozer ) 305e1c0e7e5SStephen Tozer for Context in SolConfig.SolutionContexts: 306e1c0e7e5SStephen Tozer Context.ShouldBuild = False 3073a094d8bSJeremy Morse 30873a01952SStephen Tozer self.context.logger.note("Launching VS debugger...") 3096376c5b9SStephen Tozer self._fn_go(False) 3101364750dSJames Henderson 3111364750dSJames Henderson def step(self): 3126376c5b9SStephen Tozer self._fn_step(False) 3131364750dSJames Henderson 3141364750dSJames Henderson def go(self) -> ReturnCode: 3156376c5b9SStephen Tozer self._fn_go(False) 3161364750dSJames Henderson return ReturnCode.OK 3171364750dSJames Henderson 3181364750dSJames Henderson def set_current_stack_frame(self, idx: int = 0): 3191364750dSJames Henderson thread = self._debugger.CurrentThread 3201364750dSJames Henderson stack_frames = thread.StackFrames 3211364750dSJames Henderson try: 3221364750dSJames Henderson stack_frame = stack_frames[idx] 3231364750dSJames Henderson self._debugger.CurrentStackFrame = stack_frame.raw 3241364750dSJames Henderson except IndexError: 325f98ee40fSTobias Hieta raise Error( 326f98ee40fSTobias Hieta "attempted to access stack frame {} out of {}".format( 327f98ee40fSTobias Hieta idx, len(stack_frames) 328f98ee40fSTobias Hieta ) 329f98ee40fSTobias Hieta ) 3301364750dSJames Henderson 331832b91fcSOrlando Cazalet-Hyams def _translate_stop_reason(self, reason): 332832b91fcSOrlando Cazalet-Hyams if reason == DbgEvent.dbgEventReasonNone: 333832b91fcSOrlando Cazalet-Hyams return None 334832b91fcSOrlando Cazalet-Hyams if reason == DbgEvent.dbgEventReasonBreakpoint: 335832b91fcSOrlando Cazalet-Hyams return StopReason.BREAKPOINT 336832b91fcSOrlando Cazalet-Hyams if reason == DbgEvent.dbgEventReasonStep: 337832b91fcSOrlando Cazalet-Hyams return StopReason.STEP 338832b91fcSOrlando Cazalet-Hyams if reason == DbgEvent.dbgEventReasonEndProgram: 339832b91fcSOrlando Cazalet-Hyams return StopReason.PROGRAM_EXIT 340832b91fcSOrlando Cazalet-Hyams if reason == DbgEvent.dbgEventReasonExceptionNotHandled: 341832b91fcSOrlando Cazalet-Hyams return StopReason.ERROR 342832b91fcSOrlando Cazalet-Hyams assert reason <= DbgEvent.last and reason >= DbgEvent.first 343832b91fcSOrlando Cazalet-Hyams return StopReason.OTHER 344832b91fcSOrlando Cazalet-Hyams 3451364750dSJames Henderson def _get_step_info(self, watches, step_index): 3461364750dSJames Henderson thread = self._debugger.CurrentThread 3471364750dSJames Henderson stackframes = thread.StackFrames 3481364750dSJames Henderson 3491364750dSJames Henderson frames = [] 3501364750dSJames Henderson state_frames = [] 3511364750dSJames Henderson 3527e46a721SStephen Tozer loc = LocIR(**self._location) 3537e46a721SStephen Tozer valid_loc_for_watch = loc.path and os.path.exists(loc.path) 3547e46a721SStephen Tozer 3551364750dSJames Henderson for idx, sf in enumerate(stackframes): 3561364750dSJames Henderson frame = FrameIR( 3571364750dSJames Henderson function=self._sanitize_function_name(sf.FunctionName), 358f98ee40fSTobias Hieta is_inlined=sf.FunctionName.startswith("[Inline Frame]"), 359f98ee40fSTobias Hieta loc=LocIR(path=None, lineno=None, column=None), 360f98ee40fSTobias Hieta ) 3611364750dSJames Henderson 362f98ee40fSTobias Hieta fname = frame.function or "" # pylint: disable=no-member 3631364750dSJames Henderson if any(name in fname for name in self.frames_below_main): 3641364750dSJames Henderson break 3651364750dSJames Henderson 366f98ee40fSTobias Hieta state_frame = StackFrame( 367f98ee40fSTobias Hieta function=frame.function, is_inlined=frame.is_inlined, watches={} 368f98ee40fSTobias Hieta ) 3691364750dSJames Henderson 3707e46a721SStephen Tozer if valid_loc_for_watch and idx == 0: 3717e46a721SStephen Tozer for watch_info in watches: 3727e46a721SStephen Tozer if watch_is_active(watch_info, loc.path, idx, loc.lineno): 3737e46a721SStephen Tozer watch_expr = watch_info.expression 374f98ee40fSTobias Hieta state_frame.watches[watch_expr] = self.evaluate_expression( 375f98ee40fSTobias Hieta watch_expr, idx 376f98ee40fSTobias Hieta ) 3771364750dSJames Henderson 3781364750dSJames Henderson state_frames.append(state_frame) 3791364750dSJames Henderson frames.append(frame) 3801364750dSJames Henderson 3811364750dSJames Henderson if frames: 3821364750dSJames Henderson frames[0].loc = loc 3831364750dSJames Henderson state_frames[0].location = SourceLocation(**self._location) 3841364750dSJames Henderson 385832b91fcSOrlando Cazalet-Hyams stop_reason = self._translate_stop_reason(self._debugger.LastBreakReason) 3861364750dSJames Henderson program_state = ProgramState(frames=state_frames) 3871364750dSJames Henderson 3881364750dSJames Henderson return StepIR( 389f98ee40fSTobias Hieta step_index=step_index, 390f98ee40fSTobias Hieta frames=frames, 391832b91fcSOrlando Cazalet-Hyams stop_reason=stop_reason, 392f98ee40fSTobias Hieta program_state=program_state, 393f98ee40fSTobias Hieta ) 3941364750dSJames Henderson 3951364750dSJames Henderson @property 3961364750dSJames Henderson def is_running(self): 3971364750dSJames Henderson return self._mode == VisualStudio.dbgRunMode 3981364750dSJames Henderson 3991364750dSJames Henderson @property 4001364750dSJames Henderson def is_finished(self): 4011364750dSJames Henderson return self._mode == VisualStudio.dbgDesignMode 4021364750dSJames Henderson 4031364750dSJames Henderson @property 4041364750dSJames Henderson def frames_below_main(self): 4051364750dSJames Henderson return [ 406f98ee40fSTobias Hieta "[Inline Frame] invoke_main", 407f98ee40fSTobias Hieta "__scrt_common_main_seh", 408f98ee40fSTobias Hieta "__tmainCRTStartup", 409f98ee40fSTobias Hieta "mainCRTStartup", 4101364750dSJames Henderson ] 4111364750dSJames Henderson 4121364750dSJames Henderson def evaluate_expression(self, expression, frame_idx=0) -> ValueIR: 4137e46a721SStephen Tozer if frame_idx != 0: 4141364750dSJames Henderson self.set_current_stack_frame(frame_idx) 4151364750dSJames Henderson result = self._debugger.GetExpression(expression) 4167e46a721SStephen Tozer if frame_idx != 0: 4171364750dSJames Henderson self.set_current_stack_frame(0) 4181364750dSJames Henderson value = result.Value 4191364750dSJames Henderson 420f98ee40fSTobias Hieta is_optimized_away = any( 421f98ee40fSTobias Hieta s in value 422f98ee40fSTobias Hieta for s in [ 423f98ee40fSTobias Hieta "Variable is optimized away and not available", 424f98ee40fSTobias Hieta "Value is not available, possibly due to optimization", 425f98ee40fSTobias Hieta ] 426f98ee40fSTobias Hieta ) 4271364750dSJames Henderson 428f98ee40fSTobias Hieta is_irretrievable = any( 429f98ee40fSTobias Hieta s in value 430f98ee40fSTobias Hieta for s in [ 431f98ee40fSTobias Hieta "???", 432f98ee40fSTobias Hieta "<Unable to read memory>", 433f98ee40fSTobias Hieta ] 434f98ee40fSTobias Hieta ) 4351364750dSJames Henderson 4361364750dSJames Henderson # an optimized away value is still counted as being able to be 4371364750dSJames Henderson # evaluated. 438f98ee40fSTobias Hieta could_evaluate = result.IsValidValue or is_optimized_away or is_irretrievable 4391364750dSJames Henderson 4401364750dSJames Henderson return ValueIR( 4411364750dSJames Henderson expression=expression, 4421364750dSJames Henderson value=value, 4431364750dSJames Henderson type_name=result.Type, 4441364750dSJames Henderson error_string=None, 4451364750dSJames Henderson is_optimized_away=is_optimized_away, 4461364750dSJames Henderson could_evaluate=could_evaluate, 4471364750dSJames Henderson is_irretrievable=is_irretrievable, 4481364750dSJames Henderson ) 449