140 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"""Default class for controlling debuggers."""8 9from itertools import chain10import os11import time12 13from dex.debugger.DebuggerControllers.DebuggerControllerBase import (14 DebuggerControllerBase,15)16from dex.debugger.DebuggerControllers.ControllerHelpers import (17 in_source_file,18 update_step_watches,19)20from dex.utils.Exceptions import DebuggerException, LoadDebuggerException21from dex.utils.Timeout import Timeout22 23 24class EarlyExitCondition(object):25 def __init__(self, on_line, hit_count, expression, values):26 self.on_line = on_line27 self.hit_count = hit_count28 self.expression = expression29 self.values = values30 31 32class DefaultController(DebuggerControllerBase):33 def __init__(self, context, step_collection):34 self.source_files = context.options.source_files35 self.watches = set()36 self.step_index = 037 super(DefaultController, self).__init__(context, step_collection)38 39 def _break_point_all_lines(self):40 for s in self.context.options.source_files:41 with open(s, "r") as fp:42 num_lines = len(fp.readlines())43 for line in range(1, num_lines + 1):44 try:45 self.debugger.add_breakpoint(s, line)46 except DebuggerException:47 raise LoadDebuggerException(DebuggerException.msg)48 49 def _get_early_exit_conditions(self):50 commands = self.step_collection.commands51 early_exit_conditions = []52 if "DexFinishTest" in commands:53 finish_commands = commands["DexFinishTest"]54 for fc in finish_commands:55 condition = EarlyExitCondition(56 on_line=fc.on_line,57 hit_count=fc.hit_count,58 expression=fc.expression,59 values=fc.values,60 )61 early_exit_conditions.append(condition)62 return early_exit_conditions63 64 def _should_exit(self, early_exit_conditions, line_no):65 for condition in early_exit_conditions:66 if condition.on_line == line_no:67 exit_condition_hit = condition.expression is None68 if condition.expression is not None:69 # For the purposes of consistent behaviour with the70 # Conditional Controller, check equality in the debugger71 # rather than in python (as the two can differ).72 for value in condition.values:73 expr_val = self.debugger.evaluate_expression(74 f"({condition.expression}) == ({value})"75 )76 if expr_val.value == "true":77 exit_condition_hit = True78 break79 if exit_condition_hit:80 if condition.hit_count <= 0:81 return True82 else:83 condition.hit_count -= 184 return False85 86 def _run_debugger_custom(self, cmdline):87 self.step_collection.debugger = self.debugger.debugger_info88 self._break_point_all_lines()89 self.debugger.launch(cmdline)90 for command_obj in chain.from_iterable(self.step_collection.commands.values()):91 self.watches.update(command_obj.get_watches())92 early_exit_conditions = self._get_early_exit_conditions()93 timed_out = False94 total_timeout = Timeout(self.context.options.timeout_total)95 max_steps = self.context.options.max_steps96 for _ in range(max_steps):97 breakpoint_timeout = Timeout(self.context.options.timeout_breakpoint)98 while self.debugger.is_running and not timed_out:99 # Check to see whether we've timed out while we're waiting.100 if total_timeout.timed_out():101 self.context.logger.error(102 "Debugger session has been "103 f"running for {total_timeout.elapsed}s, timeout reached!"104 )105 timed_out = True106 if breakpoint_timeout.timed_out():107 self.context.logger.error(108 f"Debugger session has not "109 f"hit a breakpoint for {breakpoint_timeout.elapsed}s, timeout "110 "reached!"111 )112 timed_out = True113 114 if timed_out or self.debugger.is_finished:115 break116 117 self.step_index += 1118 step_info = self.debugger.get_step_info(self.watches, self.step_index)119 120 if step_info.current_frame:121 update_step_watches(122 step_info, self.watches, self.step_collection.commands123 )124 self.step_collection.new_step(self.context, step_info)125 if self._should_exit(126 early_exit_conditions, step_info.current_frame.loc.lineno127 ):128 break129 130 if in_source_file(self.source_files, step_info):131 self.debugger.step_in()132 else:133 self.debugger.go()134 135 time.sleep(self.context.options.pause_between_steps)136 else:137 raise DebuggerException(138 "maximum number of steps reached ({})".format(max_steps)139 )140