From 469833f418e1704331f8dccb06ccd391f8d726e1 Mon Sep 17 00:00:00 2001 From: OCHyams Date: Fri, 21 May 2021 12:43:13 +0100 Subject: [PATCH] [dexter] Add hit_count keyword arg to DexLimitSteps The DexLimitSteps command leading breakpoint will be deleted after triggering 'hit_count' number of times if the argument is provided. All the lit tests pass on linux (with lldb), and I've tested this on windows (with vs2017) manually as the lit tests for DexLimitSteps are currently unsupported on windows. Reviewed By: StephenTozer, chrisjackson Differential Revision: https://reviews.llvm.org/D101527 --- debuginfo-tests/dexter/Commands.md | 7 +++-- .../dex/command/commands/DexLimitSteps.py | 1 + .../ConditionalController.py | 26 ++++++++++++++++--- .../perfect/limit_steps/hit_count.cpp | 21 +++++++++++++++ 4 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/hit_count.cpp diff --git a/debuginfo-tests/dexter/Commands.md b/debuginfo-tests/dexter/Commands.md index f61418bbdeed..5de685906c01 100644 --- a/debuginfo-tests/dexter/Commands.md +++ b/debuginfo-tests/dexter/Commands.md @@ -178,7 +178,7 @@ Expect the source line this is found on will never be stepped on to. ---- ## DexLimitSteps DexLimitSteps([expr, *values][, **from_line=1][,**to_line=Max] - [,**on_line]) + [,**on_line][,**hit_count]) Args: expr (str): variable or value to compare. @@ -191,12 +191,15 @@ Expect the source line this is found on will never be stepped on to. 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. + hit_count (int): If provided, limit the number of times the command + triggers. ### Description Define a limited stepping range that may be predicated on a condition. When the leading line is stepped on and any condition '(expr) == (values[n])' is true or there are no conditions, set a range of temporary breakpoints within the test -file defined by the range 'from_line' and 'to_line' or 'on_line'. +file defined by the range 'from_line' and 'to_line' or 'on_line'. This only +happens 'hit_count' number of times if the argument is provided. 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, or that line is never stepped diff --git a/debuginfo-tests/dexter/dex/command/commands/DexLimitSteps.py b/debuginfo-tests/dexter/dex/command/commands/DexLimitSteps.py index 5090ff40ccb4..d779539e07fe 100644 --- a/debuginfo-tests/dexter/dex/command/commands/DexLimitSteps.py +++ b/debuginfo-tests/dexter/dex/command/commands/DexLimitSteps.py @@ -27,6 +27,7 @@ class DexLimitSteps(CommandBase): except KeyError: self.from_line = kwargs.pop('from_line', 1) self.to_line = kwargs.pop('to_line', 999999) + self.hit_count = kwargs.pop('hit_count', None) if kwargs: raise TypeError('unexpected named args: {}'.format( ', '.join(kwargs))) diff --git a/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ConditionalController.py b/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ConditionalController.py index a910ac054dc5..0c20ecf6b262 100644 --- a/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ConditionalController.py +++ b/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ConditionalController.py @@ -31,14 +31,20 @@ class BreakpointRange: Args: expression: None for no conditions, or a str expression to compare against `values`. + + hit_count: None for no limit, or int to set the number of times the + leading breakpoint is triggered before it is removed. """ - def __init__(self, expression: str, path: str, range_from: int, range_to: int, values: list): + def __init__(self, expression: str, path: str, range_from: int, range_to: int, + values: list, hit_count: int): self.expression = expression self.path = path self.range_from = range_from self.range_to = range_to self.conditional_values = values + self.max_hit_count = hit_count + self.current_hit_count = 0 def has_conditions(self): return self.expression != None @@ -51,6 +57,14 @@ class BreakpointRange: conditional_list.append(conditional_expression) return conditional_list + def add_hit(self): + self.current_hit_count += 1 + + def should_be_removed(self): + if self.max_hit_count == None: + return False + return self.current_hit_count >= self.max_hit_count + class ConditionalController(DebuggerControllerBase): def __init__(self, context, step_collection): @@ -76,7 +90,8 @@ class ConditionalController(DebuggerControllerBase): lc.path, lc.from_line, lc.to_line, - lc.values) + lc.values, + lc.hit_count) self._bp_ranges.append(bpr) except KeyError: raise DebuggerException('Missing DexLimitSteps commands, cannot conditionally step.') @@ -129,6 +144,11 @@ class ConditionalController(DebuggerControllerBase): # This is a trailing bp. Mark it for removal. bp_to_delete.append(bp_id) continue + + bpr.add_hit() + if bpr.should_be_removed(): + 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 @@ -136,7 +156,7 @@ class ConditionalController(DebuggerControllerBase): for line in range(bpr.range_from + 1, bpr.range_to + 1): self.debugger.add_breakpoint(bpr.path, line) - # Remove any trailing breakpoints we just hit. + # Remove any trailing or expired leading breakpoints we just hit. for bp_id in bp_to_delete: self.debugger.delete_breakpoint(bp_id) diff --git a/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/hit_count.cpp b/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/hit_count.cpp new file mode 100644 index 000000000000..e7cd903c756c --- /dev/null +++ b/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/hit_count.cpp @@ -0,0 +1,21 @@ +// Purpose: +// Test that \DexLimitSteps keyword argument hit_count correctly limits +// the number of times the command can trigger. +// +// REQUIRES: system-linux +// +// RUN: %dexter_regression_test -- %s | FileCheck %s +// CHECK: hit_count.cpp + +int a; +int main() { + for (int i = 0; i < 4; i++) { + a = i; // DexLabel('check') + } + return 0; +} + +//// Unconditionally limit dexter's view of the program to 'on_line' and check +//// for i=0, i=1. The test will fail if dexter sees any other value for test. +// DexLimitSteps(hit_count=2, on_line=ref('check')) +// DexExpectWatchValue('i', '0', '1')