@@ -32,6 +32,9 @@ def __init__(self, skip=None):
32
32
self .skip = set (skip ) if skip else None
33
33
self .breaks = {}
34
34
self .fncache = {}
35
+ self ._curframe = None
36
+ self .lasti = - 1
37
+ self .trace_opcodes = False
35
38
self .frame_returning = None
36
39
37
40
self ._load_breaks ()
@@ -84,6 +87,8 @@ def trace_dispatch(self, frame, event, arg):
84
87
85
88
The arg parameter depends on the previous event.
86
89
"""
90
+ self ._curframe = frame
91
+
87
92
if self .quitting :
88
93
return # None
89
94
if event == 'line' :
@@ -94,6 +99,8 @@ def trace_dispatch(self, frame, event, arg):
94
99
return self .dispatch_return (frame , arg )
95
100
if event == 'exception' :
96
101
return self .dispatch_exception (frame , arg )
102
+ if event == 'opcode' :
103
+ return self .dispatch_opcode (frame )
97
104
if event == 'c_call' :
98
105
return self .trace_dispatch
99
106
if event == 'c_exception' :
@@ -115,13 +122,30 @@ def dispatch_line(self, frame):
115
122
if self .quitting : raise BdbQuit
116
123
return self .trace_dispatch
117
124
125
+ def dispatch_opcode (self , frame ):
126
+ """Invoke user function and return trace function for opcode event.
127
+
128
+ If the debugger stops on the current opcode, invoke
129
+ self.user_opcode(). Raise BdbQuit if self.quitting is set.
130
+ Return self.trace_dispatch to continue tracing in this scope.
131
+ """
132
+ if self .stop_here (frame ) or self .break_here (frame ):
133
+ self .user_opcode (frame )
134
+ if self .quitting : raise BdbQuit
135
+ return self .trace_dispatch
136
+
118
137
def dispatch_call (self , frame , arg ):
119
138
"""Invoke user function and return trace function for call event.
120
139
121
140
If the debugger stops on this function call, invoke
122
141
self.user_call(). Raise BdbQuit if self.quitting is set.
123
142
Return self.trace_dispatch to continue tracing in this scope.
124
143
"""
144
+ if self .trace_opcodes :
145
+ frame .f_trace_opcodes = True
146
+ else :
147
+ frame .f_trace_opcodes = False
148
+
125
149
# XXX 'arg' is no longer used
126
150
if self .botframe is None :
127
151
# First call of dispatch since reset()
@@ -209,9 +233,15 @@ def stop_here(self, frame):
209
233
if frame is self .stopframe :
210
234
if self .stoplineno == - 1 :
211
235
return False
212
- return frame .f_lineno >= self .stoplineno
236
+ if self .trace_opcodes :
237
+ return self .lasti != frame .f_lasti
238
+ else :
239
+ return frame .f_lineno >= self .stoplineno
213
240
if not self .stopframe :
214
- return True
241
+ if self .trace_opcodes :
242
+ return self .lasti != frame .f_lasti
243
+ else :
244
+ return True
215
245
return False
216
246
217
247
def break_here (self , frame ):
@@ -272,7 +302,21 @@ def user_exception(self, frame, exc_info):
272
302
"""Called when we stop on an exception."""
273
303
pass
274
304
275
- def _set_stopinfo (self , stopframe , returnframe , stoplineno = 0 ):
305
+ def user_opcode (self , frame ):
306
+ """Called when we stop or break at a opcode."""
307
+ pass
308
+
309
+ def _set_trace_opcodes (self , trace_opcodes ):
310
+ if trace_opcodes != self .trace_opcodes :
311
+ self .trace_opcodes = trace_opcodes
312
+ frame = self ._curframe
313
+ while frame is not None :
314
+ frame .f_trace_opcodes = trace_opcodes
315
+ if frame is self .botframe :
316
+ break
317
+ frame = frame .f_back
318
+
319
+ def _set_stopinfo (self , stopframe , returnframe , stoplineno = 0 , lasti = None ):
276
320
"""Set the attributes for stopping.
277
321
278
322
If stoplineno is greater than or equal to 0, then stop at line
@@ -285,6 +329,12 @@ def _set_stopinfo(self, stopframe, returnframe, stoplineno=0):
285
329
# stoplineno >= 0 means: stop at line >= the stoplineno
286
330
# stoplineno -1 means: don't stop at all
287
331
self .stoplineno = stoplineno
332
+ if lasti :
333
+ # We are stopping at opcode level
334
+ self ._set_trace_opcodes (True )
335
+ self .lasti = lasti
336
+ else :
337
+ self ._set_trace_opcodes (False )
288
338
289
339
# Derived classes and clients can call the following methods
290
340
# to affect the stepping state.
@@ -309,10 +359,26 @@ def set_step(self):
309
359
caller_frame .f_trace = self .trace_dispatch
310
360
self ._set_stopinfo (None , None )
311
361
362
+ def set_stepinst (self , frame ):
363
+ """Stop after one opcode."""
364
+ # Issue #13183: pdb skips frames after hitting a breakpoint and running
365
+ # step commands.
366
+ # Restore the trace function in the caller (that may not have been set
367
+ # for performance reasons) when returning from the current frame.
368
+ if self .frame_returning :
369
+ caller_frame = self .frame_returning .f_back
370
+ if caller_frame and not caller_frame .f_trace :
371
+ caller_frame .f_trace = self .trace_dispatch
372
+ self ._set_stopinfo (None , None , lasti = frame .f_lasti )
373
+
312
374
def set_next (self , frame ):
313
375
"""Stop on the next line in or below the given frame."""
314
376
self ._set_stopinfo (frame , None )
315
377
378
+ def set_nextinst (self , frame ):
379
+ """Stop on the next line in or below the given frame."""
380
+ self ._set_stopinfo (frame , None , lasti = frame .f_lasti )
381
+
316
382
def set_return (self , frame ):
317
383
"""Stop when returning from the given frame."""
318
384
if frame .f_code .co_flags & GENERATOR_AND_COROUTINE_FLAGS :
0 commit comments