def launch(self): self._process = self._target.LaunchSimple(None, None, os.getcwd()) if not self._process or self._process.GetNumThreads() == 0: raise DebuggerException('could not launch process') if self._process.GetNumThreads() != 1: raise DebuggerException('multiple threads not supported') self._thread = self._process.GetThreadAtIndex(0) assert self._thread, (self._process, self._thread)
def add_conditional_breakpoint(self, file_, line, condition): bp = self._target.BreakpointCreateByLocation(file_, line) if bp: bp.SetCondition(condition) else: raise DebuggerException( 'could not add breakpoint [{}:{}]'.format(file_, line))
def start(self): self.steps.clear_steps() self.launch() for command_obj in chain.from_iterable(self.steps.commands.values()): self.watches.update(command_obj.get_watches()) max_steps = self.context.options.max_steps for _ in range(max_steps): while self.is_running: pass if self.is_finished: break self.step_index += 1 step_info = self.get_step_info() if step_info.current_frame: self._update_step_watches(step_info) self.steps.new_step(self.context, step_info) if self.in_source_file(step_info): self.step() else: self.go() time.sleep(self.context.options.pause_between_steps) else: raise DebuggerException( 'maximum number of steps reached ({})'.format(max_steps))
def run_debugger_subprocess(debugger_controller, working_dir_path): with NamedTemporaryFile( dir=working_dir_path, delete=False, mode='wb') as fp: pickle.dump(debugger_controller, fp, protocol=pickle.HIGHEST_PROTOCOL) controller_path = fp.name dexter_py = os.path.basename(sys.argv[0]) if not os.path.isfile(dexter_py): dexter_py = os.path.join(get_root_directory(), '..', dexter_py) assert os.path.isfile(dexter_py) with NamedTemporaryFile(dir=working_dir_path) as fp: args = [ sys.executable, dexter_py, 'run-debugger-internal-', controller_path, '--working-directory={}'.format(working_dir_path), '--unittest=off', '--indent-timer-level={}'.format(Timer.indent + 2) ] try: with Timer('running external debugger process'): subprocess.check_call(args) except subprocess.CalledProcessError as e: raise DebuggerException(e) with open(controller_path, 'rb') as fp: debugger_controller = pickle.load(fp) return debugger_controller
def _build_bp_ranges(self): commands = self.step_collection.commands self._bp_ranges = [] try: limit_commands = commands['DexLimitSteps'] for lc in limit_commands: bpr = BreakpointRange( lc.expression, lc.path, lc.from_line, lc.to_line, lc.values, lc.hit_count, False) self._bp_ranges.append(bpr) except KeyError: raise DebuggerException('Missing DexLimitSteps commands, cannot conditionally step.') if 'DexFinishTest' in commands: finish_commands = commands['DexFinishTest'] for ic in finish_commands: bpr = BreakpointRange( ic.expression, ic.path, ic.on_line, ic.on_line, ic.values, ic.hit_count + 1, True) self._bp_ranges.append(bpr)
def _run_debugger_custom(self): self.step_collection.debugger = self.debugger.debugger_info self._break_point_all_lines() self.debugger.launch() for command_obj in chain.from_iterable(self.step_collection.commands.values()): self.watches.update(command_obj.get_watches()) max_steps = self.context.options.max_steps for _ in range(max_steps): while self.debugger.is_running: pass if self.debugger.is_finished: break self.step_index += 1 step_info = self.debugger.get_step_info(self.watches, self.step_index) if step_info.current_frame: update_step_watches(step_info, self.watches, self.step_collection.commands) self.step_collection.new_step(self.context, step_info) if in_source_file(self.source_files, step_info): self.debugger.step() else: self.debugger.go() time.sleep(self.context.options.pause_between_steps) else: raise DebuggerException( 'maximum number of steps reached ({})'.format(max_steps))
def get_debugger_steps(context): step_collection = empty_debugger_steps(context) with Timer('parsing commands'): try: step_collection.commands = _get_command_infos(context) except CommandParseError as e: msg = 'parser error: <d>{}({}):</> {}\n{}\n{}\n'.format( e.filename, e.lineno, e.info, e.src, e.caret) raise DebuggerException(msg) with NamedTemporaryFile(dir=context.working_directory.path, delete=False) as fp: pickle.dump(step_collection, fp, protocol=pickle.HIGHEST_PROTOCOL) steps_path = fp.name with NamedTemporaryFile(dir=context.working_directory.path, delete=False, mode='wb') as fp: pickle.dump(context.options, fp, protocol=pickle.HIGHEST_PROTOCOL) options_path = fp.name dexter_py = sys.argv[0] if not os.path.isfile(dexter_py): dexter_py = os.path.join(get_root_directory(), '..', dexter_py) assert os.path.isfile(dexter_py) with NamedTemporaryFile(dir=context.working_directory.path) as fp: args = [ sys.executable, dexter_py, 'run-debugger-internal-', steps_path, options_path, '--working-directory', context.working_directory.path, '--unittest=off', '--indent-timer-level={}'.format(Timer.indent + 2) ] try: with Timer('running external debugger process'): subprocess.check_call(args) except subprocess.CalledProcessError as e: raise DebuggerException(e) with open(steps_path, 'rb') as fp: step_collection = pickle.load(fp) return step_collection
def _run_debugger_custom(self, cmdline): # TODO: Add conditional and unconditional breakpoint support to dbgeng. if self.debugger.get_name() == 'dbgeng': raise DebuggerException('DexLimitSteps commands are not supported by dbgeng') self.step_collection.clear_steps() self._set_leading_bps() for command_obj in chain.from_iterable(self.step_collection.commands.values()): self._watches.update(command_obj.get_watches()) self.debugger.launch(cmdline) time.sleep(self._pause_between_steps) exit_desired = False while not self.debugger.is_finished: while self.debugger.is_running: pass step_info = self.debugger.get_step_info(self._watches, self._step_index) if step_info.current_frame: self._step_index += 1 update_step_watches(step_info, self._watches, self.step_collection.commands) self.step_collection.new_step(self.context, step_info) bp_to_delete = [] for bp_id in self.debugger.get_triggered_breakpoint_ids(): try: # See if this is one of our leading breakpoints. bpr = self._leading_bp_handles[bp_id] except KeyError: # This is a trailing bp. Mark it for removal. bp_to_delete.append(bp_id) continue bpr.add_hit() if bpr.should_be_removed(): if bpr.finish_on_remove: exit_desired = True bp_to_delete.append(bp_id) del self._leading_bp_handles[bp_id] # Add a range of trailing breakpoints covering the lines # requested in the DexLimitSteps command. Ignore first line as # that's covered by the leading bp we just hit and include the # final line. for line in range(bpr.range_from + 1, bpr.range_to + 1): self.debugger.add_breakpoint(bpr.path, line) # Remove any trailing or expired leading breakpoints we just hit. self.debugger.delete_breakpoints(bp_to_delete) if exit_desired: break self.debugger.go() time.sleep(self._pause_between_steps)
def _add_conditional_breakpoint(self, file_, line, condition): bp = self._target.BreakpointCreateByLocation(file_, line) if not bp: raise DebuggerException('could not add breakpoint [{}:{}]'.format( file_, line)) id = bp.GetID() if condition: bp.SetCondition(condition) assert id not in self._breakpoint_conditions self._breakpoint_conditions[id] = condition return id
def _run_debugger_custom(self): # TODO: Add conditional and unconditional breakpoint support to dbgeng. if self.debugger.get_name() == 'dbgeng': raise DebuggerException( 'DexLimitSteps commands are not supported by dbgeng') self.step_collection.clear_steps() self._set_conditional_bps() for command_obj in chain.from_iterable( self.step_collection.commands.values()): self._watches.update(command_obj.get_watches()) self.debugger.launch() time.sleep(self._pause_between_steps) while not self.debugger.is_finished: while self.debugger.is_running: pass step_info = self.debugger.get_step_info(self._watches, self._step_index) if step_info.current_frame: self._step_index += 1 update_step_watches(step_info, self._watches, self.step_collection.commands) self.step_collection.new_step(self.context, step_info) bp_to_delete = [] for bp_id in self.debugger.get_triggered_breakpoint_ids(): try: # See if this is one of our conditional breakpoints. cbp = self._conditional_bp_handles[bp_id] except KeyError: # This is an unconditional bp. Mark it for removal. bp_to_delete.append(bp_id) continue # We have triggered a breakpoint with a condition. Check that # the condition has been met. if self._conditional_met(cbp): # Add a range of unconditional breakpoints covering the # lines requested in the DexLimitSteps command. Ignore # first line as that's the conditional bp we just hit and # include the final line. for line in range(cbp.range_from + 1, cbp.range_to + 1): self.debugger.add_breakpoint(cbp.path, line) # Remove any unconditional breakpoints we just hit. for bp_id in bp_to_delete: self.debugger.delete_breakpoint(bp_id) self.debugger.go() time.sleep(self._pause_between_steps)
def _build_conditional_bp_ranges(self): commands = self.step_collection.commands self._conditional_bp_ranges = [] try: limit_commands = commands['DexLimitSteps'] for lc in limit_commands: conditional_bp = ConditionalBpRange(lc.expression, lc.path, lc.from_line, lc.to_line, lc.values) self._conditional_bp_ranges.append(conditional_bp) except KeyError: raise DebuggerException( 'Missing DexLimitSteps commands, cannot conditionally step.')
def get_command_infos(source_files): with Timer('parsing commands'): try: commands = _find_all_commands(source_files) command_infos = OrderedDict() for command_type in commands: for command in commands[command_type].values(): if command_type not in command_infos: command_infos[command_type] = [] command_infos[command_type].append(command) return OrderedDict(command_infos) except CommandParseError as e: msg = 'parser error: <d>{}({}):</> {}\n{}\n{}\n'.format( e.filename, e.lineno, e.info, e.src, e.caret) raise DebuggerException(msg)
def _run_debugger_custom(self): # TODO: Add conditional and unconditional breakpoint support to dbgeng. if self.debugger.get_name() == 'dbgeng': raise DebuggerException( 'DexLimitSteps commands are not supported by dbgeng') self.step_collection.clear_steps() self._set_conditional_bps() for command_obj in chain.from_iterable( self.step_collection.commands.values()): self._watches.update(command_obj.get_watches()) self.debugger.launch() time.sleep(self._pause_between_steps) while not self.debugger.is_finished: while self.debugger.is_running: pass step_info = self.debugger.get_step_info(self._watches, self._step_index) if step_info.current_frame: self._step_index += 1 update_step_watches(step_info, self._watches, self.step_collection.commands) self.step_collection.new_step(self.context, step_info) loc = step_info.current_location conditional_bp_key = (loc.path, loc.lineno) if conditional_bp_key in self._path_and_line_to_conditional_bp: conditional_bps = self._path_and_line_to_conditional_bp[ conditional_bp_key] for cbp in conditional_bps: if self._conditional_met(cbp): # Unconditional range should ignore first line as that's the # conditional bp we just hit and should be inclusive of final line for line in range(cbp.range_from + 1, cbp.range_to + 1): self.debugger.add_conditional_breakpoint( cbp.path, line, condition='') # Clear any uncondtional break points at this loc. self.debugger.delete_conditional_breakpoint(file_=loc.path, line=loc.lineno, condition='') self.debugger.go() time.sleep(self._pause_between_steps)
def add_breakpoint(self, file_, line): if not self._target.BreakpointCreateByLocation(file_, line): raise DebuggerException( 'could not add breakpoint [{}:{}]'.format(file_, line))
def _add_breakpoint(self, file_, line): bp = self._target.BreakpointCreateByLocation(file_, line) if not bp: raise DebuggerException('could not add breakpoint [{}:{}]'.format( file_, line)) return bp.GetID()