The issue with these test failures is that the dSYM was not being found by lldb, which is why setting breakpoints was failing and lldb quit without performing any steps. This change copies the dSYM to the same temp directory that the executable is copied to.
140 lines
5.5 KiB
Python
140 lines
5.5 KiB
Python
# 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
|
|
"""Default class for controlling debuggers."""
|
|
|
|
from itertools import chain
|
|
import os
|
|
import time
|
|
|
|
from dex.debugger.DebuggerControllers.DebuggerControllerBase import (
|
|
DebuggerControllerBase,
|
|
)
|
|
from dex.debugger.DebuggerControllers.ControllerHelpers import (
|
|
in_source_file,
|
|
update_step_watches,
|
|
)
|
|
from dex.utils.Exceptions import DebuggerException, LoadDebuggerException
|
|
from dex.utils.Timeout import Timeout
|
|
|
|
|
|
class EarlyExitCondition(object):
|
|
def __init__(self, on_line, hit_count, expression, values):
|
|
self.on_line = on_line
|
|
self.hit_count = hit_count
|
|
self.expression = expression
|
|
self.values = values
|
|
|
|
|
|
class DefaultController(DebuggerControllerBase):
|
|
def __init__(self, context, step_collection):
|
|
self.source_files = context.options.source_files
|
|
self.watches = set()
|
|
self.step_index = 0
|
|
super(DefaultController, self).__init__(context, step_collection)
|
|
|
|
def _break_point_all_lines(self):
|
|
for s in self.context.options.source_files:
|
|
with open(s, "r") as fp:
|
|
num_lines = len(fp.readlines())
|
|
for line in range(1, num_lines + 1):
|
|
try:
|
|
self.debugger.add_breakpoint(s, line)
|
|
except DebuggerException:
|
|
raise LoadDebuggerException(DebuggerException.msg)
|
|
|
|
def _get_early_exit_conditions(self):
|
|
commands = self.step_collection.commands
|
|
early_exit_conditions = []
|
|
if "DexFinishTest" in commands:
|
|
finish_commands = commands["DexFinishTest"]
|
|
for fc in finish_commands:
|
|
condition = EarlyExitCondition(
|
|
on_line=fc.on_line,
|
|
hit_count=fc.hit_count,
|
|
expression=fc.expression,
|
|
values=fc.values,
|
|
)
|
|
early_exit_conditions.append(condition)
|
|
return early_exit_conditions
|
|
|
|
def _should_exit(self, early_exit_conditions, line_no):
|
|
for condition in early_exit_conditions:
|
|
if condition.on_line == line_no:
|
|
exit_condition_hit = condition.expression is None
|
|
if condition.expression is not None:
|
|
# For the purposes of consistent behaviour with the
|
|
# Conditional Controller, check equality in the debugger
|
|
# rather than in python (as the two can differ).
|
|
for value in condition.values:
|
|
expr_val = self.debugger.evaluate_expression(
|
|
f"({condition.expression}) == ({value})"
|
|
)
|
|
if expr_val.value == "true":
|
|
exit_condition_hit = True
|
|
break
|
|
if exit_condition_hit:
|
|
if condition.hit_count <= 0:
|
|
return True
|
|
else:
|
|
condition.hit_count -= 1
|
|
return False
|
|
|
|
def _run_debugger_custom(self, cmdline):
|
|
self.step_collection.debugger = self.debugger.debugger_info
|
|
self._break_point_all_lines()
|
|
self.debugger.launch(cmdline)
|
|
for command_obj in chain.from_iterable(self.step_collection.commands.values()):
|
|
self.watches.update(command_obj.get_watches())
|
|
early_exit_conditions = self._get_early_exit_conditions()
|
|
timed_out = False
|
|
total_timeout = Timeout(self.context.options.timeout_total)
|
|
max_steps = self.context.options.max_steps
|
|
for _ in range(max_steps):
|
|
breakpoint_timeout = Timeout(self.context.options.timeout_breakpoint)
|
|
while self.debugger.is_running and not timed_out:
|
|
# Check to see whether we've timed out while we're waiting.
|
|
if total_timeout.timed_out():
|
|
self.context.logger.error(
|
|
"Debugger session has been "
|
|
f"running for {total_timeout.elapsed}s, timeout reached!"
|
|
)
|
|
timed_out = True
|
|
if breakpoint_timeout.timed_out():
|
|
self.context.logger.error(
|
|
f"Debugger session has not "
|
|
f"hit a breakpoint for {breakpoint_timeout.elapsed}s, timeout "
|
|
"reached!"
|
|
)
|
|
timed_out = True
|
|
|
|
if timed_out or 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 self._should_exit(
|
|
early_exit_conditions, step_info.current_frame.loc.lineno
|
|
):
|
|
break
|
|
|
|
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)
|
|
)
|