18
18
from dex .utils .Exceptions import DebuggerException
19
19
20
20
21
- class ConditionalBpRange :
22
- """Represents a conditional range of breakpoints within a source file descending from
23
- one line to another."""
21
+ class BreakpointRange :
22
+ """A range of breakpoints and a set of conditions.
23
+
24
+ The leading breakpoint (on line `range_from`) is always active.
25
+
26
+ When the leading breakpoint is hit the trailing range should be activated
27
+ when `expression` evaluates to any value in `values`. If there are no
28
+ conditions (`expression` is None) then the trailing breakpoint range should
29
+ always be activated upon hitting the leading breakpoint.
30
+
31
+ Args:
32
+ expression: None for no conditions, or a str expression to compare
33
+ against `values`.
34
+ """
24
35
25
36
def __init__ (self , expression : str , path : str , range_from : int , range_to : int , values : list ):
26
37
self .expression = expression
@@ -29,6 +40,9 @@ def __init__(self, expression: str, path: str, range_from: int, range_to: int, v
29
40
self .range_to = range_to
30
41
self .conditional_values = values
31
42
43
+ def has_conditions (self ):
44
+ return self .expression != None
45
+
32
46
def get_conditional_expression_list (self ):
33
47
conditional_list = []
34
48
for value in self .conditional_values :
@@ -42,48 +56,54 @@ class ConditionalController(DebuggerControllerBase):
42
56
def __init__ (self , context , step_collection ):
43
57
self .context = context
44
58
self .step_collection = step_collection
45
- self ._conditional_bp_ranges = None
46
- self ._build_conditional_bp_ranges ()
59
+ self ._bp_ranges = None
60
+ self ._build_bp_ranges ()
47
61
self ._watches = set ()
48
62
self ._step_index = 0
49
63
self ._pause_between_steps = context .options .pause_between_steps
50
64
self ._max_steps = context .options .max_steps
51
- # Map {id: ConditionalBpRange }
52
- self ._conditional_bp_handles = {}
65
+ # Map {id: BreakpointRange }
66
+ self ._leading_bp_handles = {}
53
67
54
- def _build_conditional_bp_ranges (self ):
68
+ def _build_bp_ranges (self ):
55
69
commands = self .step_collection .commands
56
- self ._conditional_bp_ranges = []
70
+ self ._bp_ranges = []
57
71
try :
58
72
limit_commands = commands ['DexLimitSteps' ]
59
73
for lc in limit_commands :
60
- conditional_bp = ConditionalBpRange (
74
+ bpr = BreakpointRange (
61
75
lc .expression ,
62
76
lc .path ,
63
77
lc .from_line ,
64
78
lc .to_line ,
65
79
lc .values )
66
- self ._conditional_bp_ranges .append (conditional_bp )
80
+ self ._bp_ranges .append (bpr )
67
81
except KeyError :
68
82
raise DebuggerException ('Missing DexLimitSteps commands, cannot conditionally step.' )
69
83
70
- def _set_conditional_bps (self ):
71
- # Set a conditional breakpoint for each ConditionalBpRange and build a
72
- # map of {id: ConditionalBpRange}.
73
- for cbp in self ._conditional_bp_ranges :
74
- for cond_expr in cbp .get_conditional_expression_list ():
75
- id = self .debugger .add_conditional_breakpoint (cbp .path ,
76
- cbp .range_from ,
77
- cond_expr )
78
- self ._conditional_bp_handles [id ] = cbp
84
+ def _set_leading_bps (self ):
85
+ # Set a leading breakpoint for each BreakpointRange, building a
86
+ # map of {leading bp id: BreakpointRange}.
87
+ for bpr in self ._bp_ranges :
88
+ if bpr .has_conditions ():
89
+ # Add a conditional breakpoint for each condition.
90
+ for cond_expr in bpr .get_conditional_expression_list ():
91
+ id = self .debugger .add_conditional_breakpoint (bpr .path ,
92
+ bpr .range_from ,
93
+ cond_expr )
94
+ self ._leading_bp_handles [id ] = bpr
95
+ else :
96
+ # Add an unconditional breakpoint.
97
+ id = self .debugger .add_breakpoint (bpr .path , bpr .range_from )
98
+ self ._leading_bp_handles [id ] = bpr
79
99
80
100
def _run_debugger_custom (self ):
81
101
# TODO: Add conditional and unconditional breakpoint support to dbgeng.
82
102
if self .debugger .get_name () == 'dbgeng' :
83
103
raise DebuggerException ('DexLimitSteps commands are not supported by dbgeng' )
84
104
85
105
self .step_collection .clear_steps ()
86
- self ._set_conditional_bps ()
106
+ self ._set_leading_bps ()
87
107
88
108
for command_obj in chain .from_iterable (self .step_collection .commands .values ()):
89
109
self ._watches .update (command_obj .get_watches ())
@@ -103,20 +123,20 @@ def _run_debugger_custom(self):
103
123
bp_to_delete = []
104
124
for bp_id in self .debugger .get_triggered_breakpoint_ids ():
105
125
try :
106
- # See if this is one of our conditional breakpoints.
107
- cbp = self ._conditional_bp_handles [bp_id ]
126
+ # See if this is one of our leading breakpoints.
127
+ bpr = self ._leading_bp_handles [bp_id ]
108
128
except KeyError :
109
- # This is an unconditional bp. Mark it for removal.
129
+ # This is a trailing bp. Mark it for removal.
110
130
bp_to_delete .append (bp_id )
111
131
continue
112
- # Add a range of unconditional breakpoints covering the lines
132
+ # Add a range of trailing breakpoints covering the lines
113
133
# requested in the DexLimitSteps command. Ignore first line as
114
- # that's the conditional bp we just hit and include the final
115
- # line.
116
- for line in range (cbp .range_from + 1 , cbp .range_to + 1 ):
117
- self .debugger .add_breakpoint (cbp .path , line )
134
+ # that's covered by the leading bp we just hit and include the
135
+ # final line.
136
+ for line in range (bpr .range_from + 1 , bpr .range_to + 1 ):
137
+ self .debugger .add_breakpoint (bpr .path , line )
118
138
119
- # Remove any unconditional breakpoints we just hit.
139
+ # Remove any trailing breakpoints we just hit.
120
140
for bp_id in bp_to_delete :
121
141
self .debugger .delete_breakpoint (bp_id )
122
142
0 commit comments