forked from OSchip/llvm-project
Revert "[Dexter] Add DexLimitSteps command and ConditionalController"
This reverts commit 81e836a5a6
.
Accidentally committed a diff file.
This commit is contained in:
parent
a48c76cf43
commit
05eabb5204
712
d.diff
712
d.diff
|
@ -1,712 +0,0 @@
|
|||
diff --git a/debuginfo-tests/dexter/Commands.md b/debuginfo-tests/dexter/Commands.md
|
||||
index c30a0d7214c..2e2fecfed92 100644
|
||||
--- a/debuginfo-tests/dexter/Commands.md
|
||||
+++ b/debuginfo-tests/dexter/Commands.md
|
||||
@@ -173,6 +173,34 @@ Expect the source line this is found on will never be stepped on to.
|
||||
[TODO]
|
||||
|
||||
|
||||
+----
|
||||
+## DexLimitSteps
|
||||
+ DexLimitSteps(expr, *values [, **from_line=1],[,**to_line=Max]
|
||||
+ [,**on_line])
|
||||
+
|
||||
+ Args:
|
||||
+ expr (str): variable or value to compare.
|
||||
+
|
||||
+ Arg list:
|
||||
+ values (str): At least one potential value the expr may evaluate to.
|
||||
+
|
||||
+ Keyword args:
|
||||
+ from_line (int): Define the start of the limited step range.
|
||||
+ to_line (int): Define the end of the limited step range.
|
||||
+ on_line (int): Define a range with length 1 starting and ending on the
|
||||
+ same line.
|
||||
+
|
||||
+### Description
|
||||
+Define a limited stepping range that is predicated on a condition. When
|
||||
+'(expr) == (values[n])', set a range of temporary, unconditional break points within
|
||||
+the test file defined by the range from_line and to_line or on_line.
|
||||
+
|
||||
+The condition is only evaluated on the line 'from_line' or 'on_line'. If the
|
||||
+condition is not true at the start of the range, the whole range is ignored.
|
||||
+
|
||||
+DexLimitSteps commands are useful for reducing the amount of steps gathered in
|
||||
+large test cases that would normally take much longer to complete.
|
||||
+
|
||||
----
|
||||
## DexLabel
|
||||
DexLabel(name)
|
||||
diff --git a/debuginfo-tests/dexter/dex/command/ParseCommand.py b/debuginfo-tests/dexter/dex/command/ParseCommand.py
|
||||
index 4cc9ae12592..8246ea9e3cf 100644
|
||||
--- a/debuginfo-tests/dexter/dex/command/ParseCommand.py
|
||||
+++ b/debuginfo-tests/dexter/dex/command/ParseCommand.py
|
||||
@@ -24,6 +24,7 @@ from dex.command.commands.DexExpectStepOrder import DexExpectStepOrder
|
||||
from dex.command.commands.DexExpectWatchType import DexExpectWatchType
|
||||
from dex.command.commands.DexExpectWatchValue import DexExpectWatchValue
|
||||
from dex.command.commands.DexLabel import DexLabel
|
||||
+from dex.command.commands.DexLimitSteps import DexLimitSteps
|
||||
from dex.command.commands.DexUnreachable import DexUnreachable
|
||||
from dex.command.commands.DexWatch import DexWatch
|
||||
from dex.utils import Timer
|
||||
@@ -42,6 +43,7 @@ def _get_valid_commands():
|
||||
DexExpectWatchType.get_name() : DexExpectWatchType,
|
||||
DexExpectWatchValue.get_name() : DexExpectWatchValue,
|
||||
DexLabel.get_name() : DexLabel,
|
||||
+ DexLimitSteps.get_name() : DexLimitSteps,
|
||||
DexUnreachable.get_name() : DexUnreachable,
|
||||
DexWatch.get_name() : DexWatch
|
||||
}
|
||||
diff --git a/debuginfo-tests/dexter/dex/command/commands/DexLimitSteps.py b/debuginfo-tests/dexter/dex/command/commands/DexLimitSteps.py
|
||||
new file mode 100644
|
||||
index 00000000000..d66401b5599
|
||||
--- /dev/null
|
||||
+++ b/debuginfo-tests/dexter/dex/command/commands/DexLimitSteps.py
|
||||
@@ -0,0 +1,54 @@
|
||||
+# 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
|
||||
+"""A Command that enables test writers to specify a limited number of break
|
||||
+points using an start condition and range.
|
||||
+"""
|
||||
+
|
||||
+from dex.command.CommandBase import CommandBase
|
||||
+
|
||||
+class DexLimitSteps(CommandBase):
|
||||
+ def __init__(self, *args, **kwargs):
|
||||
+ self.expression = args[0]
|
||||
+ self.values = [str(arg) for arg in args[1:]]
|
||||
+ try:
|
||||
+ on_line = kwargs.pop('on_line')
|
||||
+ self.from_line = on_line
|
||||
+ self.to_line = on_line
|
||||
+ except KeyError:
|
||||
+ self.from_line = kwargs.pop('from_line', 1)
|
||||
+ self.to_line = kwargs.pop('to_line', 999999)
|
||||
+ if kwargs:
|
||||
+ raise TypeError('unexpected named args: {}'.format(
|
||||
+ ', '.join(kwargs)))
|
||||
+ super(DexLimitSteps, self).__init__()
|
||||
+
|
||||
+ def resolve_label(self, label_line_pair):
|
||||
+ label, lineno = label_line_pair
|
||||
+ if isinstance(self.from_line, str):
|
||||
+ if self.from_line == label:
|
||||
+ self.from_line = lineno
|
||||
+ if isinstance(self.to_line, str):
|
||||
+ if self.to_line == label:
|
||||
+ self.to_line = lineno
|
||||
+
|
||||
+ def has_labels(self):
|
||||
+ return len(self.get_label_args()) > 0
|
||||
+
|
||||
+ def get_label_args(self):
|
||||
+ return [label for label in (self.from_line, self.to_line)
|
||||
+ if isinstance(label, str)]
|
||||
+
|
||||
+ def eval(self):
|
||||
+ raise NotImplementedError('DexLimitSteps commands cannot be evaled.')
|
||||
+
|
||||
+ @staticmethod
|
||||
+ def get_name():
|
||||
+ return __class__.__name__
|
||||
+
|
||||
+ @staticmethod
|
||||
+ def get_subcommands() -> dict:
|
||||
+ return None
|
||||
diff --git a/debuginfo-tests/dexter/dex/debugger/DebuggerBase.py b/debuginfo-tests/dexter/dex/debugger/DebuggerBase.py
|
||||
index 2261396b94b..12f4f4ab7a0 100644
|
||||
--- a/debuginfo-tests/dexter/dex/debugger/DebuggerBase.py
|
||||
+++ b/debuginfo-tests/dexter/dex/debugger/DebuggerBase.py
|
||||
@@ -120,6 +120,14 @@ class DebuggerBase(object, metaclass=abc.ABCMeta):
|
||||
def add_breakpoint(self, file_, line):
|
||||
pass
|
||||
|
||||
+ @abc.abstractmethod
|
||||
+ def add_conditional_breakpoint(self, file_, line, condition):
|
||||
+ pass
|
||||
+
|
||||
+ @abc.abstractmethod
|
||||
+ def delete_conditional_breakpoint(self, file_, line, condition):
|
||||
+ pass
|
||||
+
|
||||
@abc.abstractmethod
|
||||
def launch(self):
|
||||
pass
|
||||
diff --git a/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ConditionalController.py b/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ConditionalController.py
|
||||
new file mode 100644
|
||||
index 00000000000..4e4327b53f8
|
||||
--- /dev/null
|
||||
+++ b/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ConditionalController.py
|
||||
@@ -0,0 +1,127 @@
|
||||
+# 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
|
||||
+"""Conditional Controller Class for DExTer.-"""
|
||||
+
|
||||
+
|
||||
+import os
|
||||
+import time
|
||||
+from collections import defaultdict
|
||||
+from itertools import chain
|
||||
+
|
||||
+from dex.debugger.DebuggerControllers.ControllerHelpers import in_source_file, update_step_watches
|
||||
+from dex.debugger.DebuggerControllers.DebuggerControllerBase import DebuggerControllerBase
|
||||
+from dex.debugger.DebuggerBase import DebuggerBase
|
||||
+from dex.utils.Exceptions import DebuggerException
|
||||
+
|
||||
+
|
||||
+class ConditionalBpRange:
|
||||
+ """Represents a conditional range of breakpoints within a source file descending from
|
||||
+ one line to another."""
|
||||
+
|
||||
+ def __init__(self, expression: str, path: str, range_from: int, range_to: int, values: list):
|
||||
+ self.expression = expression
|
||||
+ self.path = path
|
||||
+ self.range_from = range_from
|
||||
+ self.range_to = range_to
|
||||
+ self.conditional_values = values
|
||||
+
|
||||
+ def get_conditional_expression_list(self):
|
||||
+ conditional_list = []
|
||||
+ for value in self.conditional_values:
|
||||
+ # (<expression>) == (<value>)
|
||||
+ conditional_expression = '({}) == ({})'.format(self.expression, value)
|
||||
+ conditional_list.append(conditional_expression)
|
||||
+ return conditional_list
|
||||
+
|
||||
+
|
||||
+class ConditionalController(DebuggerControllerBase):
|
||||
+ def __init__(self, context, step_collection):
|
||||
+ self.context = context
|
||||
+ self.step_collection = step_collection
|
||||
+ self._conditional_bps = None
|
||||
+ self._watches = set()
|
||||
+ self._step_index = 0
|
||||
+ self._build_conditional_bps()
|
||||
+ self._path_and_line_to_conditional_bp = defaultdict(list)
|
||||
+ self._pause_between_steps = context.options.pause_between_steps
|
||||
+ self._max_steps = context.options.max_steps
|
||||
+
|
||||
+ def _build_conditional_bps(self):
|
||||
+ commands = self.step_collection.commands
|
||||
+ self._conditional_bps = []
|
||||
+ 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_bps.append(conditional_bp)
|
||||
+ except KeyError:
|
||||
+ raise DebuggerException('Missing DexLimitSteps commands, cannot conditionally step.')
|
||||
+
|
||||
+ def _set_conditional_bps(self):
|
||||
+ # When we break in the debugger we need a quick and easy way to look up
|
||||
+ # which conditional bp we've breaked on.
|
||||
+ for cbp in self._conditional_bps:
|
||||
+ conditional_bp_list = self._path_and_line_to_conditional_bp[(cbp.path, cbp.range_from)]
|
||||
+ conditional_bp_list.append(cbp)
|
||||
+
|
||||
+ # Set break points only on the first line of any conditional range, we'll set
|
||||
+ # more break points for a range when the condition is satisfied.
|
||||
+ for cbp in self._conditional_bps:
|
||||
+ for cond_expr in cbp.get_conditional_expression_list():
|
||||
+ self.debugger.add_conditional_breakpoint(cbp.path, cbp.range_from, cond_expr)
|
||||
+
|
||||
+ def _conditional_met(self, cbp):
|
||||
+ for cond_expr in cbp.get_conditional_expression_list():
|
||||
+ valueIR = self.debugger.evaluate_expression(cond_expr)
|
||||
+ if valueIR.type_name == 'bool' and valueIR.value == 'true':
|
||||
+ return True
|
||||
+ return False
|
||||
+
|
||||
+ 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)
|
||||
diff --git a/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ControllerHelpers.py b/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ControllerHelpers.py
|
||||
new file mode 100644
|
||||
index 00000000000..adac7674aff
|
||||
--- /dev/null
|
||||
+++ b/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ControllerHelpers.py
|
||||
@@ -0,0 +1,37 @@
|
||||
+# 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
|
||||
+
|
||||
+import os
|
||||
+from itertools import chain
|
||||
+
|
||||
+def in_source_file(source_files, step_info):
|
||||
+ if not step_info.current_frame:
|
||||
+ return False
|
||||
+ if not step_info.current_location.path:
|
||||
+ return False
|
||||
+ if not os.path.exists(step_info.current_location.path):
|
||||
+ return False
|
||||
+ return any(os.path.samefile(step_info.current_location.path, f) \
|
||||
+ for f in source_files)
|
||||
+
|
||||
+def update_step_watches(step_info, watches, commands):
|
||||
+ watch_cmds = ['DexUnreachable', 'DexExpectStepOrder']
|
||||
+ towatch = chain.from_iterable(commands[x]
|
||||
+ for x in watch_cmds
|
||||
+ if x in commands)
|
||||
+ try:
|
||||
+ # Iterate over all watches of the types named in watch_cmds
|
||||
+ for watch in towatch:
|
||||
+ loc = step_info.current_location
|
||||
+ if (os.path.exists(loc.path)
|
||||
+ and os.path.samefile(watch.path, loc.path)
|
||||
+ and watch.lineno == loc.lineno):
|
||||
+ result = watch.eval(step_info)
|
||||
+ step_info.watches.update(result)
|
||||
+ break
|
||||
+ except KeyError:
|
||||
+ pass
|
||||
diff --git a/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/DebuggerControllerBase.py b/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/DebuggerControllerBase.py
|
||||
index ff98baa2d0e..87b13fc7f3a 100644
|
||||
--- a/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/DebuggerControllerBase.py
|
||||
+++ b/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/DebuggerControllerBase.py
|
||||
@@ -4,7 +4,7 @@
|
||||
# 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."""
|
||||
+"""Abstract Base class for controlling debuggers."""
|
||||
|
||||
import abc
|
||||
|
||||
diff --git a/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/DefaultController.py b/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/DefaultController.py
|
||||
index 0077a19e601..c41a3eff0d3 100644
|
||||
--- a/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/DefaultController.py
|
||||
+++ b/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/DefaultController.py
|
||||
@@ -4,61 +4,37 @@
|
||||
# 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
|
||||
-"""Base class for controlling debuggers."""
|
||||
+"""Default class for controlling debuggers."""
|
||||
|
||||
from itertools import chain
|
||||
import os
|
||||
import time
|
||||
|
||||
from dex.debugger.DebuggerControllers.DebuggerControllerBase import DebuggerControllerBase
|
||||
-from dex.utils.Exceptions import DebuggerException
|
||||
+from dex.debugger.DebuggerControllers.ControllerHelpers import in_source_file, update_step_watches
|
||||
+from dex.utils.Exceptions import DebuggerException, LoadDebuggerException
|
||||
|
||||
class DefaultController(DebuggerControllerBase):
|
||||
def __init__(self, context, step_collection):
|
||||
self.context = context
|
||||
self.step_collection = step_collection
|
||||
+ self.source_files = self.context.options.source_files
|
||||
self.watches = set()
|
||||
self.step_index = 0
|
||||
|
||||
- def _update_step_watches(self, step_info):
|
||||
- watch_cmds = ['DexUnreachable', 'DexExpectStepOrder']
|
||||
- towatch = chain.from_iterable(self.step_collection.commands[x]
|
||||
- for x in watch_cmds
|
||||
- if x in self.step_collection.commands)
|
||||
- try:
|
||||
- # Iterate over all watches of the types named in watch_cmds
|
||||
- for watch in towatch:
|
||||
- loc = step_info.current_location
|
||||
- if (os.path.exists(loc.path)
|
||||
- and os.path.samefile(watch.path, loc.path)
|
||||
- and watch.lineno == loc.lineno):
|
||||
- result = watch.eval(step_info)
|
||||
- step_info.watches.update(result)
|
||||
- break
|
||||
- except KeyError:
|
||||
- pass
|
||||
-
|
||||
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):
|
||||
- self.debugger.add_breakpoint(s, line)
|
||||
-
|
||||
- def _in_source_file(self, step_info):
|
||||
- if not step_info.current_frame:
|
||||
- return False
|
||||
- if not step_info.current_location.path:
|
||||
- return False
|
||||
- if not os.path.exists(step_info.current_location.path):
|
||||
- return False
|
||||
- return any(os.path.samefile(step_info.current_location.path, f) \
|
||||
- for f in self.context.options.source_files)
|
||||
+ try:
|
||||
+ self.debugger.add_breakpoint(s, line)
|
||||
+ except DebuggerException:
|
||||
+ raise LoadDebuggerException(DebuggerException.msg)
|
||||
|
||||
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()):
|
||||
@@ -76,10 +52,10 @@ class DefaultController(DebuggerControllerBase):
|
||||
step_info = self.debugger.get_step_info(self.watches, self.step_index)
|
||||
|
||||
if step_info.current_frame:
|
||||
- self._update_step_watches(step_info)
|
||||
+ update_step_watches(step_info, self.watches, self.step_collection.commands)
|
||||
self.step_collection.new_step(self.context, step_info)
|
||||
|
||||
- if self._in_source_file(step_info):
|
||||
+ if in_source_file(self.source_files, step_info):
|
||||
self.debugger.step()
|
||||
else:
|
||||
self.debugger.go()
|
||||
diff --git a/debuginfo-tests/dexter/dex/debugger/dbgeng/dbgeng.py b/debuginfo-tests/dexter/dex/debugger/dbgeng/dbgeng.py
|
||||
index 0afc748aecb..d812fd974f7 100644
|
||||
--- a/debuginfo-tests/dexter/dex/debugger/dbgeng/dbgeng.py
|
||||
+++ b/debuginfo-tests/dexter/dex/debugger/dbgeng/dbgeng.py
|
||||
@@ -77,11 +77,21 @@ class DbgEng(DebuggerBase):
|
||||
self.client.Control.RemoveBreakpoint(x)
|
||||
|
||||
def add_breakpoint(self, file_, line):
|
||||
- # This is something to implement in the future -- as it stands, Dexter
|
||||
- # doesn't test for such things as "I can set a breakpoint on this line".
|
||||
- # This is only called AFAICT right now to ensure we break on every step.
|
||||
+ # Breakpoint setting/deleting is not supported by dbgeng at this moment
|
||||
+ # but is something that should be considered in the future.
|
||||
+ # TODO: this method is called in the DefaultController but has no effect.
|
||||
pass
|
||||
|
||||
+ def add_conditional_breakpoint(self, file_, line, condition):
|
||||
+ # breakpoint setting/deleting is not supported by dbgeng at this moment
|
||||
+ # but is something that should be considered in the future.
|
||||
+ raise NotImplementedError('add_conditional_breakpoint is not yet implemented by dbgeng')
|
||||
+
|
||||
+ def delete_conditional_breakpoint(self, file_, line, condition):
|
||||
+ # breakpoint setting/deleting is not supported by dbgeng at this moment
|
||||
+ # but is something that should be considered in the future.
|
||||
+ raise NotImplementedError('delete_conditional_breakpoint is not yet implemented by dbgeng')
|
||||
+
|
||||
def launch(self):
|
||||
# We are, by this point, already launched.
|
||||
self.step_info = probe_process.probe_state(self.client)
|
||||
diff --git a/debuginfo-tests/dexter/dex/debugger/lldb/LLDB.py b/debuginfo-tests/dexter/dex/debugger/lldb/LLDB.py
|
||||
index a943431c888..c7bb74681d9 100644
|
||||
--- a/debuginfo-tests/dexter/dex/debugger/lldb/LLDB.py
|
||||
+++ b/debuginfo-tests/dexter/dex/debugger/lldb/LLDB.py
|
||||
@@ -105,9 +105,48 @@ class LLDB(DebuggerBase):
|
||||
|
||||
def add_breakpoint(self, file_, line):
|
||||
if not self._target.BreakpointCreateByLocation(file_, line):
|
||||
- raise LoadDebuggerException(
|
||||
+ raise DebuggerException(
|
||||
'could not add breakpoint [{}:{}]'.format(file_, line))
|
||||
|
||||
+ 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 delete_conditional_breakpoint(self, file_, line, condition):
|
||||
+ bp_count = self._target.GetNumBreakpoints()
|
||||
+ bps = [self._target.GetBreakpointAtIndex(ix) for ix in range(0, bp_count)]
|
||||
+
|
||||
+ for bp in bps:
|
||||
+ bp_cond = bp.GetCondition()
|
||||
+ bp_cond = bp_cond if bp_cond is not None else ''
|
||||
+
|
||||
+ if bp_cond != condition:
|
||||
+ continue
|
||||
+
|
||||
+ # If one of the bound bp locations for this bp is bound to the same
|
||||
+ # line in file_ above, then delete the entire parent bp and all
|
||||
+ # bp locs.
|
||||
+ # https://lldb.llvm.org/python_reference/lldb.SBBreakpoint-class.html
|
||||
+ for breakpoint_location in bp:
|
||||
+ sb_address = breakpoint_location.GetAddress()
|
||||
+
|
||||
+ sb_line_entry = sb_address.GetLineEntry()
|
||||
+ bl_line = sb_line_entry.GetLine()
|
||||
+
|
||||
+ sb_file_entry = sb_line_entry.GetFileSpec()
|
||||
+ bl_dir = sb_file_entry.GetDirectory()
|
||||
+ bl_file_name = sb_file_entry.GetFilename()
|
||||
+
|
||||
+ bl_file_path = os.path.join(bl_dir, bl_file_name)
|
||||
+
|
||||
+ if bl_file_path == file_ and bl_line == line:
|
||||
+ self._target.BreakpointDelete(bp.GetID())
|
||||
+ break
|
||||
+
|
||||
def launch(self):
|
||||
self._process = self._target.LaunchSimple(None, None, os.getcwd())
|
||||
if not self._process or self._process.GetNumThreads() == 0:
|
||||
diff --git a/debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio.py b/debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio.py
|
||||
index b9816f84f72..40a902bd205 100644
|
||||
--- a/debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio.py
|
||||
+++ b/debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio.py
|
||||
@@ -82,6 +82,9 @@ class VisualStudio(DebuggerBase, metaclass=abc.ABCMeta): # pylint: disable=abst
|
||||
|
||||
@property
|
||||
def _location(self):
|
||||
+ #TODO: Find a better way of determining path, line and column info
|
||||
+ # that doesn't require reading break points. This method requires
|
||||
+ # all lines to have a break point on them.
|
||||
bp = self._debugger.BreakpointLastHit
|
||||
return {
|
||||
'path': getattr(bp, 'File', None),
|
||||
@@ -111,8 +114,20 @@ class VisualStudio(DebuggerBase, metaclass=abc.ABCMeta): # pylint: disable=abst
|
||||
def add_breakpoint(self, file_, line):
|
||||
self._debugger.Breakpoints.Add('', file_, line)
|
||||
|
||||
+ def add_conditional_breakpoint(self, file_, line, condition):
|
||||
+ column = 1
|
||||
+ self._debugger.Breakpoints.Add('', file_, line, column, condition)
|
||||
+
|
||||
+ def delete_conditional_breakpoint(self, file_, line, condition):
|
||||
+ for bp in self._debugger.Breakpoints:
|
||||
+ for bound_bp in bp.Children:
|
||||
+ if (bound_bp.File == file_ and bound_bp.FileLine == line and
|
||||
+ bound_bp.Condition == condition):
|
||||
+ bp.Delete()
|
||||
+ break
|
||||
+
|
||||
def launch(self):
|
||||
- self.step()
|
||||
+ self._fn_go()
|
||||
|
||||
def step(self):
|
||||
self._fn_step()
|
||||
diff --git a/debuginfo-tests/dexter/dex/tools/test/Tool.py b/debuginfo-tests/dexter/dex/tools/test/Tool.py
|
||||
index a615c8cad90..43191fd44bd 100644
|
||||
--- a/debuginfo-tests/dexter/dex/tools/test/Tool.py
|
||||
+++ b/debuginfo-tests/dexter/dex/tools/test/Tool.py
|
||||
@@ -16,6 +16,7 @@ from dex.builder import run_external_build_script
|
||||
from dex.command.ParseCommand import get_command_infos
|
||||
from dex.debugger.Debuggers import run_debugger_subprocess
|
||||
from dex.debugger.DebuggerControllers.DefaultController import DefaultController
|
||||
+from dex.debugger.DebuggerControllers.ConditionalController import ConditionalController
|
||||
from dex.dextIR.DextIR import DextIR
|
||||
from dex.heuristic import Heuristic
|
||||
from dex.tools import TestToolBase
|
||||
@@ -136,9 +137,15 @@ class Tool(TestToolBase):
|
||||
executable_path=self.context.options.executable,
|
||||
source_paths=self.context.options.source_files,
|
||||
dexter_version=self.context.version)
|
||||
+
|
||||
step_collection.commands = get_command_infos(
|
||||
self.context.options.source_files)
|
||||
- debugger_controller = DefaultController(self.context, step_collection)
|
||||
+
|
||||
+ if 'DexLimitSteps' in step_collection.commands:
|
||||
+ debugger_controller = ConditionalController(self.context, step_collection)
|
||||
+ else:
|
||||
+ debugger_controller = DefaultController(self.context, step_collection)
|
||||
+
|
||||
return debugger_controller
|
||||
|
||||
def _get_steps(self, builderIR):
|
||||
diff --git a/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_check_json_step_count.cpp b/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_check_json_step_count.cpp
|
||||
new file mode 100644
|
||||
index 00000000000..45683fced2d
|
||||
--- /dev/null
|
||||
+++ b/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_check_json_step_count.cpp
|
||||
@@ -0,0 +1,20 @@
|
||||
+// Purpose:
|
||||
+// Check number of step lines are correctly reported in json output.
|
||||
+//
|
||||
+// REQUIRES: system-linux
|
||||
+//
|
||||
+// RUN: %dexter_regression_test --verbose -- %s | FileCheck %s
|
||||
+// CHECK: limit_steps_check_json_step_count.cpp
|
||||
+// CHECK: ## BEGIN ##
|
||||
+// CHECK-COUNT-3: json_step_count.cpp",
|
||||
+
|
||||
+int main() {
|
||||
+ int result = 0;
|
||||
+ for(int ix = 0; ix != 10; ++ix) {
|
||||
+ int index = ix;
|
||||
+ result += index; // DexLabel('check')
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+// DexExpectWatchValue('index', 2, 7, 9, on_line='check')
|
||||
+// DexLimitSteps('ix', 2, 7, 9, on_line='check')
|
||||
diff --git a/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_expect_loop.cpp b/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_expect_loop.cpp
|
||||
new file mode 100644
|
||||
index 00000000000..5946fa6ba46
|
||||
--- /dev/null
|
||||
+++ b/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_expect_loop.cpp
|
||||
@@ -0,0 +1,20 @@
|
||||
+// Purpose:
|
||||
+// Check the DexLimit steps only gathers step info for 2 iterations of a
|
||||
+// for loop.
|
||||
+//
|
||||
+// REQUIRES: system-linux
|
||||
+//
|
||||
+// RUN: %dexter_regression_test -- %s | FileCheck %s
|
||||
+// CHECK: limit_steps_expect_loop.cpp:
|
||||
+
|
||||
+int main(const int argc, const char * argv[]) {
|
||||
+ unsigned int sum = 1;
|
||||
+ for(unsigned int ix = 0; ix != 5; ++ix) {
|
||||
+ unsigned thing_to_add = ix + ix - ix; // DexLabel('start')
|
||||
+ sum += ix; // DexLabel('end')
|
||||
+ }
|
||||
+ return sum;
|
||||
+}
|
||||
+
|
||||
+// DexLimitSteps('ix', 0, 3, from_line='start', to_line='end')
|
||||
+// DexExpectWatchValue('ix', 0, 3, from_line='start', to_line='end')
|
||||
diff --git a/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_expect_value.cpp b/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_expect_value.cpp
|
||||
new file mode 100644
|
||||
index 00000000000..2715e28d66b
|
||||
--- /dev/null
|
||||
+++ b/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_expect_value.cpp
|
||||
@@ -0,0 +1,18 @@
|
||||
+// Purpose:
|
||||
+// Ensure that limited stepping breaks for all expected values.
|
||||
+//
|
||||
+// REQUIRES: system-linux
|
||||
+//
|
||||
+// RUN: %dexter_regression_test -- %s | FileCheck %s
|
||||
+// CHECK: limit_steps_expect_value.cpp
|
||||
+
|
||||
+int main() {
|
||||
+ int i = 0;
|
||||
+ i = 1; // DexLabel('from')
|
||||
+ i = 2;
|
||||
+ i = 3;
|
||||
+ return 0; // DexLabel('long_range')
|
||||
+}
|
||||
+
|
||||
+// DexLimitSteps('i', '0', from_line='from', to_line='long_range')
|
||||
+// DexExpectWatchValue('i', 0, 1, 2, 3, from_line='from', to_line='long_range')
|
||||
diff --git a/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_overlapping_ranges.cpp b/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_overlapping_ranges.cpp
|
||||
new file mode 100644
|
||||
index 00000000000..3200fe0979b
|
||||
--- /dev/null
|
||||
+++ b/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_overlapping_ranges.cpp
|
||||
@@ -0,0 +1,36 @@
|
||||
+// Purpose:
|
||||
+// Ensure that multiple overlapping \DexLimitSteps ranges do not interfere.
|
||||
+//
|
||||
+// REQUIRES: system-linux
|
||||
+//
|
||||
+// RUN: %dexter_regression_test -- %s | FileCheck %s
|
||||
+// CHECK: limit_steps_overlapping_ranges.cpp
|
||||
+
|
||||
+int main() {
|
||||
+ int val1;
|
||||
+ int val2;
|
||||
+ int placeholder;
|
||||
+ for (int ix = 0; ix != 10; ++ix) {
|
||||
+ placeholder=val1+val2; // DexLabel('from')
|
||||
+ if (ix == 0) {
|
||||
+ val1 = ix;
|
||||
+ val2 = ix; // DexLabel('val1_check')
|
||||
+ placeholder=val1+val2; // DexLabel('val1_check_to')
|
||||
+ }
|
||||
+ else if (ix == 2) {
|
||||
+ val2 = ix;
|
||||
+ val1 = ix; // DexLabel('val2_check')
|
||||
+ placeholder=val1+val2; // DexLabel('val2_check_to')
|
||||
+ }
|
||||
+ placeholder=val1+val2; // DexLabel('to')
|
||||
+ }
|
||||
+ return val1 + val2;
|
||||
+}
|
||||
+
|
||||
+// DexExpectWatchValue('ix', 0, 2, 5, from_line='from', to_line='to')
|
||||
+// DexExpectWatchValue('val1', 0, from_line='val1_check', to_line='val1_check_to')
|
||||
+// DexExpectWatchValue('val2', 2, from_line='val2_check', to_line='val2_check_to')
|
||||
+
|
||||
+// DexLimitSteps('ix', 5, from_line='from', to_line='to')
|
||||
+// DexLimitSteps('val1', 0, from_line='val1_check', to_line='val1_check_to')
|
||||
+// DexLimitSteps('val2', 2, from_line='val2_check', to_line='val2_check_to')
|
||||
diff --git a/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_same_line_conditional.cpp b/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_same_line_conditional.cpp
|
||||
new file mode 100644
|
||||
index 00000000000..060ff0d5fe7
|
||||
--- /dev/null
|
||||
+++ b/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/limit_steps_same_line_conditional.cpp
|
||||
@@ -0,0 +1,26 @@
|
||||
+// Purpose:
|
||||
+// Test that LimitStep commands can exist on the same from line.
|
||||
+//
|
||||
+// REQUIRES: system-linux
|
||||
+//
|
||||
+// RUN: %dexter_regression_test -- %s | FileCheck %s
|
||||
+// CHECK: limit_steps_same_line_conditional.cpp
|
||||
+
|
||||
+int main() {
|
||||
+ int val1 = 0;
|
||||
+
|
||||
+ int placeholder;
|
||||
+ for(int ix = 0; ix != 4; ++ix) {
|
||||
+ val1 = ix;
|
||||
+ placeholder = ix; // DexLabel('from')
|
||||
+ placeholder = ix;
|
||||
+ val1 += 2; // DexLabel('to')
|
||||
+ placeholder = ix; // DexLabel('extended_to')
|
||||
+ }
|
||||
+ return val1 + placeholder;
|
||||
+}
|
||||
+
|
||||
+// DexExpectWatchValue('val1', 0, 1, 3, from_line='from', to_line='extended_to')
|
||||
+
|
||||
+// DexLimitSteps('ix', 0, from_line='from', to_line='to')
|
||||
+// DexLimitSteps('ix', 1, from_line='from', to_line='extended_to')
|
|
@ -173,34 +173,6 @@ Expect the source line this is found on will never be stepped on to.
|
|||
[TODO]
|
||||
|
||||
|
||||
----
|
||||
## DexLimitSteps
|
||||
DexLimitSteps(expr, *values [, **from_line=1],[,**to_line=Max]
|
||||
[,**on_line])
|
||||
|
||||
Args:
|
||||
expr (str): variable or value to compare.
|
||||
|
||||
Arg list:
|
||||
values (str): At least one potential value the expr may evaluate to.
|
||||
|
||||
Keyword args:
|
||||
from_line (int): Define the start of the limited step range.
|
||||
to_line (int): Define the end of the limited step range.
|
||||
on_line (int): Define a range with length 1 starting and ending on the
|
||||
same line.
|
||||
|
||||
### Description
|
||||
Define a limited stepping range that is predicated on a condition. When
|
||||
'(expr) == (values[n])', set a range of temporary, unconditional break points within
|
||||
the test file defined by the range from_line and to_line or on_line.
|
||||
|
||||
The condition is only evaluated on the line 'from_line' or 'on_line'. If the
|
||||
condition is not true at the start of the range, the whole range is ignored.
|
||||
|
||||
DexLimitSteps commands are useful for reducing the amount of steps gathered in
|
||||
large test cases that would normally take much longer to complete.
|
||||
|
||||
----
|
||||
## DexLabel
|
||||
DexLabel(name)
|
||||
|
|
|
@ -24,7 +24,6 @@ from dex.command.commands.DexExpectStepOrder import DexExpectStepOrder
|
|||
from dex.command.commands.DexExpectWatchType import DexExpectWatchType
|
||||
from dex.command.commands.DexExpectWatchValue import DexExpectWatchValue
|
||||
from dex.command.commands.DexLabel import DexLabel
|
||||
from dex.command.commands.DexLimitSteps import DexLimitSteps
|
||||
from dex.command.commands.DexUnreachable import DexUnreachable
|
||||
from dex.command.commands.DexWatch import DexWatch
|
||||
from dex.utils import Timer
|
||||
|
@ -43,7 +42,6 @@ def _get_valid_commands():
|
|||
DexExpectWatchType.get_name() : DexExpectWatchType,
|
||||
DexExpectWatchValue.get_name() : DexExpectWatchValue,
|
||||
DexLabel.get_name() : DexLabel,
|
||||
DexLimitSteps.get_name() : DexLimitSteps,
|
||||
DexUnreachable.get_name() : DexUnreachable,
|
||||
DexWatch.get_name() : DexWatch
|
||||
}
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
# 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
|
||||
"""A Command that enables test writers to specify a limited number of break
|
||||
points using an start condition and range.
|
||||
"""
|
||||
|
||||
from dex.command.CommandBase import CommandBase
|
||||
|
||||
class DexLimitSteps(CommandBase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.expression = args[0]
|
||||
self.values = [str(arg) for arg in args[1:]]
|
||||
try:
|
||||
on_line = kwargs.pop('on_line')
|
||||
self.from_line = on_line
|
||||
self.to_line = on_line
|
||||
except KeyError:
|
||||
self.from_line = kwargs.pop('from_line', 1)
|
||||
self.to_line = kwargs.pop('to_line', 999999)
|
||||
if kwargs:
|
||||
raise TypeError('unexpected named args: {}'.format(
|
||||
', '.join(kwargs)))
|
||||
super(DexLimitSteps, self).__init__()
|
||||
|
||||
def resolve_label(self, label_line_pair):
|
||||
label, lineno = label_line_pair
|
||||
if isinstance(self.from_line, str):
|
||||
if self.from_line == label:
|
||||
self.from_line = lineno
|
||||
if isinstance(self.to_line, str):
|
||||
if self.to_line == label:
|
||||
self.to_line = lineno
|
||||
|
||||
def has_labels(self):
|
||||
return len(self.get_label_args()) > 0
|
||||
|
||||
def get_label_args(self):
|
||||
return [label for label in (self.from_line, self.to_line)
|
||||
if isinstance(label, str)]
|
||||
|
||||
def eval(self):
|
||||
raise NotImplementedError('DexLimitSteps commands cannot be evaled.')
|
||||
|
||||
@staticmethod
|
||||
def get_name():
|
||||
return __class__.__name__
|
||||
|
||||
@staticmethod
|
||||
def get_subcommands() -> dict:
|
||||
return None
|
|
@ -120,14 +120,6 @@ class DebuggerBase(object, metaclass=abc.ABCMeta):
|
|||
def add_breakpoint(self, file_, line):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def add_conditional_breakpoint(self, file_, line, condition):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_conditional_breakpoint(self, file_, line, condition):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def launch(self):
|
||||
pass
|
||||
|
|
|
@ -1,127 +0,0 @@
|
|||
# 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
|
||||
"""Conditional Controller Class for DExTer.-"""
|
||||
|
||||
|
||||
import os
|
||||
import time
|
||||
from collections import defaultdict
|
||||
from itertools import chain
|
||||
|
||||
from dex.debugger.DebuggerControllers.ControllerHelpers import in_source_file, update_step_watches
|
||||
from dex.debugger.DebuggerControllers.DebuggerControllerBase import DebuggerControllerBase
|
||||
from dex.debugger.DebuggerBase import DebuggerBase
|
||||
from dex.utils.Exceptions import DebuggerException
|
||||
|
||||
|
||||
class ConditionalBpRange:
|
||||
"""Represents a conditional range of breakpoints within a source file descending from
|
||||
one line to another."""
|
||||
|
||||
def __init__(self, expression: str, path: str, range_from: int, range_to: int, values: list):
|
||||
self.expression = expression
|
||||
self.path = path
|
||||
self.range_from = range_from
|
||||
self.range_to = range_to
|
||||
self.conditional_values = values
|
||||
|
||||
def get_conditional_expression_list(self):
|
||||
conditional_list = []
|
||||
for value in self.conditional_values:
|
||||
# (<expression>) == (<value>)
|
||||
conditional_expression = '({}) == ({})'.format(self.expression, value)
|
||||
conditional_list.append(conditional_expression)
|
||||
return conditional_list
|
||||
|
||||
|
||||
class ConditionalController(DebuggerControllerBase):
|
||||
def __init__(self, context, step_collection):
|
||||
self.context = context
|
||||
self.step_collection = step_collection
|
||||
self._conditional_bps = None
|
||||
self._watches = set()
|
||||
self._step_index = 0
|
||||
self._build_conditional_bps()
|
||||
self._path_and_line_to_conditional_bp = defaultdict(list)
|
||||
self._pause_between_steps = context.options.pause_between_steps
|
||||
self._max_steps = context.options.max_steps
|
||||
|
||||
def _build_conditional_bps(self):
|
||||
commands = self.step_collection.commands
|
||||
self._conditional_bps = []
|
||||
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_bps.append(conditional_bp)
|
||||
except KeyError:
|
||||
raise DebuggerException('Missing DexLimitSteps commands, cannot conditionally step.')
|
||||
|
||||
def _set_conditional_bps(self):
|
||||
# When we break in the debugger we need a quick and easy way to look up
|
||||
# which conditional bp we've breaked on.
|
||||
for cbp in self._conditional_bps:
|
||||
conditional_bp_list = self._path_and_line_to_conditional_bp[(cbp.path, cbp.range_from)]
|
||||
conditional_bp_list.append(cbp)
|
||||
|
||||
# Set break points only on the first line of any conditional range, we'll set
|
||||
# more break points for a range when the condition is satisfied.
|
||||
for cbp in self._conditional_bps:
|
||||
for cond_expr in cbp.get_conditional_expression_list():
|
||||
self.debugger.add_conditional_breakpoint(cbp.path, cbp.range_from, cond_expr)
|
||||
|
||||
def _conditional_met(self, cbp):
|
||||
for cond_expr in cbp.get_conditional_expression_list():
|
||||
valueIR = self.debugger.evaluate_expression(cond_expr)
|
||||
if valueIR.type_name == 'bool' and valueIR.value == 'true':
|
||||
return True
|
||||
return False
|
||||
|
||||
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)
|
|
@ -1,37 +0,0 @@
|
|||
# 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
|
||||
|
||||
import os
|
||||
from itertools import chain
|
||||
|
||||
def in_source_file(source_files, step_info):
|
||||
if not step_info.current_frame:
|
||||
return False
|
||||
if not step_info.current_location.path:
|
||||
return False
|
||||
if not os.path.exists(step_info.current_location.path):
|
||||
return False
|
||||
return any(os.path.samefile(step_info.current_location.path, f) \
|
||||
for f in source_files)
|
||||
|
||||
def update_step_watches(step_info, watches, commands):
|
||||
watch_cmds = ['DexUnreachable', 'DexExpectStepOrder']
|
||||
towatch = chain.from_iterable(commands[x]
|
||||
for x in watch_cmds
|
||||
if x in commands)
|
||||
try:
|
||||
# Iterate over all watches of the types named in watch_cmds
|
||||
for watch in towatch:
|
||||
loc = step_info.current_location
|
||||
if (os.path.exists(loc.path)
|
||||
and os.path.samefile(watch.path, loc.path)
|
||||
and watch.lineno == loc.lineno):
|
||||
result = watch.eval(step_info)
|
||||
step_info.watches.update(result)
|
||||
break
|
||||
except KeyError:
|
||||
pass
|
|
@ -4,7 +4,7 @@
|
|||
# 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
|
||||
"""Abstract Base class for controlling debuggers."""
|
||||
"""Default class for controlling debuggers."""
|
||||
|
||||
import abc
|
||||
|
||||
|
|
|
@ -4,37 +4,61 @@
|
|||
# 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."""
|
||||
"""Base 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.Exceptions import DebuggerException
|
||||
|
||||
class DefaultController(DebuggerControllerBase):
|
||||
def __init__(self, context, step_collection):
|
||||
self.context = context
|
||||
self.step_collection = step_collection
|
||||
self.source_files = self.context.options.source_files
|
||||
self.watches = set()
|
||||
self.step_index = 0
|
||||
|
||||
def _update_step_watches(self, step_info):
|
||||
watch_cmds = ['DexUnreachable', 'DexExpectStepOrder']
|
||||
towatch = chain.from_iterable(self.step_collection.commands[x]
|
||||
for x in watch_cmds
|
||||
if x in self.step_collection.commands)
|
||||
try:
|
||||
# Iterate over all watches of the types named in watch_cmds
|
||||
for watch in towatch:
|
||||
loc = step_info.current_location
|
||||
if (os.path.exists(loc.path)
|
||||
and os.path.samefile(watch.path, loc.path)
|
||||
and watch.lineno == loc.lineno):
|
||||
result = watch.eval(step_info)
|
||||
step_info.watches.update(result)
|
||||
break
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
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)
|
||||
self.debugger.add_breakpoint(s, line)
|
||||
|
||||
def _in_source_file(self, step_info):
|
||||
if not step_info.current_frame:
|
||||
return False
|
||||
if not step_info.current_location.path:
|
||||
return False
|
||||
if not os.path.exists(step_info.current_location.path):
|
||||
return False
|
||||
return any(os.path.samefile(step_info.current_location.path, f) \
|
||||
for f in self.context.options.source_files)
|
||||
|
||||
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()):
|
||||
|
@ -52,10 +76,10 @@ class DefaultController(DebuggerControllerBase):
|
|||
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._update_step_watches(step_info)
|
||||
self.step_collection.new_step(self.context, step_info)
|
||||
|
||||
if in_source_file(self.source_files, step_info):
|
||||
if self._in_source_file(step_info):
|
||||
self.debugger.step()
|
||||
else:
|
||||
self.debugger.go()
|
||||
|
|
|
@ -77,21 +77,11 @@ class DbgEng(DebuggerBase):
|
|||
self.client.Control.RemoveBreakpoint(x)
|
||||
|
||||
def add_breakpoint(self, file_, line):
|
||||
# Breakpoint setting/deleting is not supported by dbgeng at this moment
|
||||
# but is something that should be considered in the future.
|
||||
# TODO: this method is called in the DefaultController but has no effect.
|
||||
# This is something to implement in the future -- as it stands, Dexter
|
||||
# doesn't test for such things as "I can set a breakpoint on this line".
|
||||
# This is only called AFAICT right now to ensure we break on every step.
|
||||
pass
|
||||
|
||||
def add_conditional_breakpoint(self, file_, line, condition):
|
||||
# breakpoint setting/deleting is not supported by dbgeng at this moment
|
||||
# but is something that should be considered in the future.
|
||||
raise NotImplementedError('add_conditional_breakpoint is not yet implemented by dbgeng')
|
||||
|
||||
def delete_conditional_breakpoint(self, file_, line, condition):
|
||||
# breakpoint setting/deleting is not supported by dbgeng at this moment
|
||||
# but is something that should be considered in the future.
|
||||
raise NotImplementedError('delete_conditional_breakpoint is not yet implemented by dbgeng')
|
||||
|
||||
def launch(self):
|
||||
# We are, by this point, already launched.
|
||||
self.step_info = probe_process.probe_state(self.client)
|
||||
|
|
|
@ -105,48 +105,9 @@ class LLDB(DebuggerBase):
|
|||
|
||||
def add_breakpoint(self, file_, line):
|
||||
if not self._target.BreakpointCreateByLocation(file_, line):
|
||||
raise DebuggerException(
|
||||
raise LoadDebuggerException(
|
||||
'could not add breakpoint [{}:{}]'.format(file_, line))
|
||||
|
||||
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 delete_conditional_breakpoint(self, file_, line, condition):
|
||||
bp_count = self._target.GetNumBreakpoints()
|
||||
bps = [self._target.GetBreakpointAtIndex(ix) for ix in range(0, bp_count)]
|
||||
|
||||
for bp in bps:
|
||||
bp_cond = bp.GetCondition()
|
||||
bp_cond = bp_cond if bp_cond is not None else ''
|
||||
|
||||
if bp_cond != condition:
|
||||
continue
|
||||
|
||||
# If one of the bound bp locations for this bp is bound to the same
|
||||
# line in file_ above, then delete the entire parent bp and all
|
||||
# bp locs.
|
||||
# https://lldb.llvm.org/python_reference/lldb.SBBreakpoint-class.html
|
||||
for breakpoint_location in bp:
|
||||
sb_address = breakpoint_location.GetAddress()
|
||||
|
||||
sb_line_entry = sb_address.GetLineEntry()
|
||||
bl_line = sb_line_entry.GetLine()
|
||||
|
||||
sb_file_entry = sb_line_entry.GetFileSpec()
|
||||
bl_dir = sb_file_entry.GetDirectory()
|
||||
bl_file_name = sb_file_entry.GetFilename()
|
||||
|
||||
bl_file_path = os.path.join(bl_dir, bl_file_name)
|
||||
|
||||
if bl_file_path == file_ and bl_line == line:
|
||||
self._target.BreakpointDelete(bp.GetID())
|
||||
break
|
||||
|
||||
def launch(self):
|
||||
self._process = self._target.LaunchSimple(None, None, os.getcwd())
|
||||
if not self._process or self._process.GetNumThreads() == 0:
|
||||
|
|
|
@ -82,9 +82,6 @@ class VisualStudio(DebuggerBase, metaclass=abc.ABCMeta): # pylint: disable=abst
|
|||
|
||||
@property
|
||||
def _location(self):
|
||||
#TODO: Find a better way of determining path, line and column info
|
||||
# that doesn't require reading break points. This method requires
|
||||
# all lines to have a break point on them.
|
||||
bp = self._debugger.BreakpointLastHit
|
||||
return {
|
||||
'path': getattr(bp, 'File', None),
|
||||
|
@ -114,20 +111,8 @@ class VisualStudio(DebuggerBase, metaclass=abc.ABCMeta): # pylint: disable=abst
|
|||
def add_breakpoint(self, file_, line):
|
||||
self._debugger.Breakpoints.Add('', file_, line)
|
||||
|
||||
def add_conditional_breakpoint(self, file_, line, condition):
|
||||
column = 1
|
||||
self._debugger.Breakpoints.Add('', file_, line, column, condition)
|
||||
|
||||
def delete_conditional_breakpoint(self, file_, line, condition):
|
||||
for bp in self._debugger.Breakpoints:
|
||||
for bound_bp in bp.Children:
|
||||
if (bound_bp.File == file_ and bound_bp.FileLine == line and
|
||||
bound_bp.Condition == condition):
|
||||
bp.Delete()
|
||||
break
|
||||
|
||||
def launch(self):
|
||||
self._fn_go()
|
||||
self.step()
|
||||
|
||||
def step(self):
|
||||
self._fn_step()
|
||||
|
|
|
@ -16,7 +16,6 @@ from dex.builder import run_external_build_script
|
|||
from dex.command.ParseCommand import get_command_infos
|
||||
from dex.debugger.Debuggers import run_debugger_subprocess
|
||||
from dex.debugger.DebuggerControllers.DefaultController import DefaultController
|
||||
from dex.debugger.DebuggerControllers.ConditionalController import ConditionalController
|
||||
from dex.dextIR.DextIR import DextIR
|
||||
from dex.heuristic import Heuristic
|
||||
from dex.tools import TestToolBase
|
||||
|
@ -137,15 +136,9 @@ class Tool(TestToolBase):
|
|||
executable_path=self.context.options.executable,
|
||||
source_paths=self.context.options.source_files,
|
||||
dexter_version=self.context.version)
|
||||
|
||||
step_collection.commands = get_command_infos(
|
||||
self.context.options.source_files)
|
||||
|
||||
if 'DexLimitSteps' in step_collection.commands:
|
||||
debugger_controller = ConditionalController(self.context, step_collection)
|
||||
else:
|
||||
debugger_controller = DefaultController(self.context, step_collection)
|
||||
|
||||
debugger_controller = DefaultController(self.context, step_collection)
|
||||
return debugger_controller
|
||||
|
||||
def _get_steps(self, builderIR):
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
// Purpose:
|
||||
// Check number of step lines are correctly reported in json output.
|
||||
//
|
||||
// REQUIRES: system-linux
|
||||
//
|
||||
// RUN: %dexter_regression_test --verbose -- %s | FileCheck %s
|
||||
// CHECK: limit_steps_check_json_step_count.cpp
|
||||
// CHECK: ## BEGIN ##
|
||||
// CHECK-COUNT-3: json_step_count.cpp",
|
||||
|
||||
int main() {
|
||||
int result = 0;
|
||||
for(int ix = 0; ix != 10; ++ix) {
|
||||
int index = ix;
|
||||
result += index; // DexLabel('check')
|
||||
}
|
||||
}
|
||||
|
||||
// DexExpectWatchValue('index', 2, 7, 9, on_line='check')
|
||||
// DexLimitSteps('ix', 2, 7, 9, on_line='check')
|
|
@ -1,20 +0,0 @@
|
|||
// Purpose:
|
||||
// Check the DexLimit steps only gathers step info for 2 iterations of a
|
||||
// for loop.
|
||||
//
|
||||
// REQUIRES: system-linux
|
||||
//
|
||||
// RUN: %dexter_regression_test -- %s | FileCheck %s
|
||||
// CHECK: limit_steps_expect_loop.cpp:
|
||||
|
||||
int main(const int argc, const char * argv[]) {
|
||||
unsigned int sum = 1;
|
||||
for(unsigned int ix = 0; ix != 5; ++ix) {
|
||||
unsigned thing_to_add = ix + ix - ix; // DexLabel('start')
|
||||
sum += ix; // DexLabel('end')
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
// DexLimitSteps('ix', 0, 3, from_line='start', to_line='end')
|
||||
// DexExpectWatchValue('ix', 0, 3, from_line='start', to_line='end')
|
|
@ -1,18 +0,0 @@
|
|||
// Purpose:
|
||||
// Ensure that limited stepping breaks for all expected values.
|
||||
//
|
||||
// REQUIRES: system-linux
|
||||
//
|
||||
// RUN: %dexter_regression_test -- %s | FileCheck %s
|
||||
// CHECK: limit_steps_expect_value.cpp
|
||||
|
||||
int main() {
|
||||
int i = 0;
|
||||
i = 1; // DexLabel('from')
|
||||
i = 2;
|
||||
i = 3;
|
||||
return 0; // DexLabel('long_range')
|
||||
}
|
||||
|
||||
// DexLimitSteps('i', '0', from_line='from', to_line='long_range')
|
||||
// DexExpectWatchValue('i', 0, 1, 2, 3, from_line='from', to_line='long_range')
|
|
@ -1,36 +0,0 @@
|
|||
// Purpose:
|
||||
// Ensure that multiple overlapping \DexLimitSteps ranges do not interfere.
|
||||
//
|
||||
// REQUIRES: system-linux
|
||||
//
|
||||
// RUN: %dexter_regression_test -- %s | FileCheck %s
|
||||
// CHECK: limit_steps_overlapping_ranges.cpp
|
||||
|
||||
int main() {
|
||||
int val1;
|
||||
int val2;
|
||||
int placeholder;
|
||||
for (int ix = 0; ix != 10; ++ix) {
|
||||
placeholder=val1+val2; // DexLabel('from')
|
||||
if (ix == 0) {
|
||||
val1 = ix;
|
||||
val2 = ix; // DexLabel('val1_check')
|
||||
placeholder=val1+val2; // DexLabel('val1_check_to')
|
||||
}
|
||||
else if (ix == 2) {
|
||||
val2 = ix;
|
||||
val1 = ix; // DexLabel('val2_check')
|
||||
placeholder=val1+val2; // DexLabel('val2_check_to')
|
||||
}
|
||||
placeholder=val1+val2; // DexLabel('to')
|
||||
}
|
||||
return val1 + val2;
|
||||
}
|
||||
|
||||
// DexExpectWatchValue('ix', 0, 2, 5, from_line='from', to_line='to')
|
||||
// DexExpectWatchValue('val1', 0, from_line='val1_check', to_line='val1_check_to')
|
||||
// DexExpectWatchValue('val2', 2, from_line='val2_check', to_line='val2_check_to')
|
||||
|
||||
// DexLimitSteps('ix', 5, from_line='from', to_line='to')
|
||||
// DexLimitSteps('val1', 0, from_line='val1_check', to_line='val1_check_to')
|
||||
// DexLimitSteps('val2', 2, from_line='val2_check', to_line='val2_check_to')
|
|
@ -1,26 +0,0 @@
|
|||
// Purpose:
|
||||
// Test that LimitStep commands can exist on the same from line.
|
||||
//
|
||||
// REQUIRES: system-linux
|
||||
//
|
||||
// RUN: %dexter_regression_test -- %s | FileCheck %s
|
||||
// CHECK: limit_steps_same_line_conditional.cpp
|
||||
|
||||
int main() {
|
||||
int val1 = 0;
|
||||
|
||||
int placeholder;
|
||||
for(int ix = 0; ix != 4; ++ix) {
|
||||
val1 = ix;
|
||||
placeholder = ix; // DexLabel('from')
|
||||
placeholder = ix;
|
||||
val1 += 2; // DexLabel('to')
|
||||
placeholder = ix; // DexLabel('extended_to')
|
||||
}
|
||||
return val1 + placeholder;
|
||||
}
|
||||
|
||||
// DexExpectWatchValue('val1', 0, 1, 3, from_line='from', to_line='extended_to')
|
||||
|
||||
// DexLimitSteps('ix', 0, from_line='from', to_line='to')
|
||||
// DexLimitSteps('ix', 1, from_line='from', to_line='extended_to')
|
Loading…
Reference in New Issue