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"""Default class for controlling debuggers.""" 81364750dSJames Henderson 91364750dSJames Hendersonfrom itertools import chain 101364750dSJames Hendersonimport os 111364750dSJames Hendersonimport time 121364750dSJames Henderson 131364750dSJames Hendersonfrom dex.debugger.DebuggerControllers.DebuggerControllerBase import DebuggerControllerBase 141364750dSJames Hendersonfrom dex.debugger.DebuggerControllers.ControllerHelpers import in_source_file, update_step_watches 151364750dSJames Hendersonfrom dex.utils.Exceptions import DebuggerException, LoadDebuggerException 161364750dSJames Henderson 176cf69179SStephen Tozerclass EarlyExitCondition(object): 186cf69179SStephen Tozer def __init__(self, on_line, hit_count, expression, values): 196cf69179SStephen Tozer self.on_line = on_line 206cf69179SStephen Tozer self.hit_count = hit_count 216cf69179SStephen Tozer self.expression = expression 226cf69179SStephen Tozer self.values = values 236cf69179SStephen Tozer 241364750dSJames Hendersonclass DefaultController(DebuggerControllerBase): 251364750dSJames Henderson def __init__(self, context, step_collection): 26*3a094d8bSJeremy Morse self.source_files = context.options.source_files 271364750dSJames Henderson self.watches = set() 281364750dSJames Henderson self.step_index = 0 29*3a094d8bSJeremy Morse super(DefaultController, self).__init__(context, step_collection) 301364750dSJames Henderson 311364750dSJames Henderson def _break_point_all_lines(self): 321364750dSJames Henderson for s in self.context.options.source_files: 331364750dSJames Henderson with open(s, 'r') as fp: 341364750dSJames Henderson num_lines = len(fp.readlines()) 351364750dSJames Henderson for line in range(1, num_lines + 1): 361364750dSJames Henderson try: 371364750dSJames Henderson self.debugger.add_breakpoint(s, line) 381364750dSJames Henderson except DebuggerException: 391364750dSJames Henderson raise LoadDebuggerException(DebuggerException.msg) 401364750dSJames Henderson 416cf69179SStephen Tozer def _get_early_exit_conditions(self): 426cf69179SStephen Tozer commands = self.step_collection.commands 436cf69179SStephen Tozer early_exit_conditions = [] 446cf69179SStephen Tozer if 'DexFinishTest' in commands: 456cf69179SStephen Tozer finish_commands = commands['DexFinishTest'] 466cf69179SStephen Tozer for fc in finish_commands: 476cf69179SStephen Tozer condition = EarlyExitCondition(on_line=fc.on_line, 486cf69179SStephen Tozer hit_count=fc.hit_count, 496cf69179SStephen Tozer expression=fc.expression, 506cf69179SStephen Tozer values=fc.values) 516cf69179SStephen Tozer early_exit_conditions.append(condition) 526cf69179SStephen Tozer return early_exit_conditions 536cf69179SStephen Tozer 546cf69179SStephen Tozer def _should_exit(self, early_exit_conditions, line_no): 556cf69179SStephen Tozer for condition in early_exit_conditions: 566cf69179SStephen Tozer if condition.on_line == line_no: 576cf69179SStephen Tozer exit_condition_hit = condition.expression is None 586cf69179SStephen Tozer if condition.expression is not None: 596cf69179SStephen Tozer # For the purposes of consistent behaviour with the 606cf69179SStephen Tozer # Conditional Controller, check equality in the debugger 616cf69179SStephen Tozer # rather than in python (as the two can differ). 626cf69179SStephen Tozer for value in condition.values: 636cf69179SStephen Tozer expr_val = self.debugger.evaluate_expression(f'({condition.expression}) == ({value})') 646cf69179SStephen Tozer if expr_val.value == 'true': 656cf69179SStephen Tozer exit_condition_hit = True 666cf69179SStephen Tozer break 676cf69179SStephen Tozer if exit_condition_hit: 686cf69179SStephen Tozer if condition.hit_count <= 0: 696cf69179SStephen Tozer return True 706cf69179SStephen Tozer else: 716cf69179SStephen Tozer condition.hit_count -= 1 726cf69179SStephen Tozer return False 736cf69179SStephen Tozer 746cf69179SStephen Tozer 75*3a094d8bSJeremy Morse def _run_debugger_custom(self, cmdline): 761364750dSJames Henderson self.step_collection.debugger = self.debugger.debugger_info 771364750dSJames Henderson self._break_point_all_lines() 78*3a094d8bSJeremy Morse self.debugger.launch(cmdline) 791364750dSJames Henderson 801364750dSJames Henderson for command_obj in chain.from_iterable(self.step_collection.commands.values()): 811364750dSJames Henderson self.watches.update(command_obj.get_watches()) 826cf69179SStephen Tozer early_exit_conditions = self._get_early_exit_conditions() 831364750dSJames Henderson 841364750dSJames Henderson max_steps = self.context.options.max_steps 851364750dSJames Henderson for _ in range(max_steps): 861364750dSJames Henderson while self.debugger.is_running: 871364750dSJames Henderson pass 881364750dSJames Henderson 891364750dSJames Henderson if self.debugger.is_finished: 901364750dSJames Henderson break 911364750dSJames Henderson 921364750dSJames Henderson self.step_index += 1 931364750dSJames Henderson step_info = self.debugger.get_step_info(self.watches, self.step_index) 941364750dSJames Henderson 951364750dSJames Henderson if step_info.current_frame: 961364750dSJames Henderson update_step_watches(step_info, self.watches, self.step_collection.commands) 971364750dSJames Henderson self.step_collection.new_step(self.context, step_info) 986cf69179SStephen Tozer if self._should_exit(early_exit_conditions, step_info.current_frame.loc.lineno): 996cf69179SStephen Tozer break 1001364750dSJames Henderson 1011364750dSJames Henderson if in_source_file(self.source_files, step_info): 1021364750dSJames Henderson self.debugger.step() 1031364750dSJames Henderson else: 1041364750dSJames Henderson self.debugger.go() 1051364750dSJames Henderson 1061364750dSJames Henderson time.sleep(self.context.options.pause_between_steps) 1071364750dSJames Henderson else: 1081364750dSJames Henderson raise DebuggerException( 1091364750dSJames Henderson 'maximum number of steps reached ({})'.format(max_steps)) 110