@@ -32,8 +32,10 @@ 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 .frame_trace_lines = {}
35
+ self .frame_trace_lines_opcodes = {}
36
36
self .frame_returning = None
37
+ self .trace_opcodes = False
38
+ self .enterframe = None
37
39
38
40
self ._load_breaks ()
39
41
@@ -85,6 +87,9 @@ def trace_dispatch(self, frame, event, arg):
85
87
86
88
The arg parameter depends on the previous event.
87
89
"""
90
+
91
+ self .enterframe = frame
92
+
88
93
if self .quitting :
89
94
return # None
90
95
if event == 'line' :
@@ -101,6 +106,8 @@ def trace_dispatch(self, frame, event, arg):
101
106
return self .trace_dispatch
102
107
if event == 'c_return' :
103
108
return self .trace_dispatch
109
+ if event == 'opcode' :
110
+ return self .dispatch_opcode (frame , arg )
104
111
print ('bdb.Bdb.dispatch: unknown debugging event:' , repr (event ))
105
112
return self .trace_dispatch
106
113
@@ -187,6 +194,17 @@ def dispatch_exception(self, frame, arg):
187
194
188
195
return self .trace_dispatch
189
196
197
+ def dispatch_opcode (self , frame , arg ):
198
+ """Invoke user function and return trace function for opcode event.
199
+ If the debugger stops on the current opcode, invoke
200
+ self.user_opcode(). Raise BdbQuit if self.quitting is set.
201
+ Return self.trace_dispatch to continue tracing in this scope.
202
+ """
203
+ if self .stop_here (frame ) or self .break_here (frame ):
204
+ self .user_opcode (frame )
205
+ if self .quitting : raise BdbQuit
206
+ return self .trace_dispatch
207
+
190
208
# Normally derived classes don't override the following
191
209
# methods, but they may if they want to redefine the
192
210
# definition of stopping and breakpoints.
@@ -273,7 +291,21 @@ def user_exception(self, frame, exc_info):
273
291
"""Called when we stop on an exception."""
274
292
pass
275
293
276
- def _set_stopinfo (self , stopframe , returnframe , stoplineno = 0 ):
294
+ def user_opcode (self , frame ):
295
+ """Called when we are about to execute an opcode."""
296
+ pass
297
+
298
+ def _set_trace_opcodes (self , trace_opcodes ):
299
+ if trace_opcodes != self .trace_opcodes :
300
+ self .trace_opcodes = trace_opcodes
301
+ frame = self .enterframe
302
+ while frame is not None :
303
+ frame .f_trace_opcodes = trace_opcodes
304
+ if frame is self .botframe :
305
+ break
306
+ frame = frame .f_back
307
+
308
+ def _set_stopinfo (self , stopframe , returnframe , stoplineno = 0 , opcode = False ):
277
309
"""Set the attributes for stopping.
278
310
279
311
If stoplineno is greater than or equal to 0, then stop at line
@@ -286,6 +318,17 @@ def _set_stopinfo(self, stopframe, returnframe, stoplineno=0):
286
318
# stoplineno >= 0 means: stop at line >= the stoplineno
287
319
# stoplineno -1 means: don't stop at all
288
320
self .stoplineno = stoplineno
321
+ self ._set_trace_opcodes (opcode )
322
+
323
+ def _set_caller_tracefunc (self ):
324
+ # Issue #13183: pdb skips frames after hitting a breakpoint and running
325
+ # step commands.
326
+ # Restore the trace function in the caller (that may not have been set
327
+ # for performance reasons) when returning from the current frame.
328
+ if self .frame_returning :
329
+ caller_frame = self .frame_returning .f_back
330
+ if caller_frame and not caller_frame .f_trace :
331
+ caller_frame .f_trace = self .trace_dispatch
289
332
290
333
# Derived classes and clients can call the following methods
291
334
# to affect the stepping state.
@@ -300,16 +343,14 @@ def set_until(self, frame, lineno=None):
300
343
301
344
def set_step (self ):
302
345
"""Stop after one line of code."""
303
- # Issue #13183: pdb skips frames after hitting a breakpoint and running
304
- # step commands.
305
- # Restore the trace function in the caller (that may not have been set
306
- # for performance reasons) when returning from the current frame.
307
- if self .frame_returning :
308
- caller_frame = self .frame_returning .f_back
309
- if caller_frame and not caller_frame .f_trace :
310
- caller_frame .f_trace = self .trace_dispatch
346
+ self ._set_caller_tracefunc ()
311
347
self ._set_stopinfo (None , None )
312
348
349
+ def set_stepinstr (self ):
350
+ """Stop before the next instruction."""
351
+ self ._set_caller_tracefunc ()
352
+ self ._set_stopinfo (None , None , opcode = True )
353
+
313
354
def set_next (self , frame ):
314
355
"""Stop on the next line in or below the given frame."""
315
356
self ._set_stopinfo (frame , None )
@@ -329,11 +370,12 @@ def set_trace(self, frame=None):
329
370
if frame is None :
330
371
frame = sys ._getframe ().f_back
331
372
self .reset ()
373
+ self .enterframe = frame
332
374
while frame :
333
375
frame .f_trace = self .trace_dispatch
334
376
self .botframe = frame
335
- # We need f_trace_liens == True for the debugger to work
336
- self . frame_trace_lines [ frame ] = frame . f_trace_lines
377
+ self . frame_trace_lines_opcodes [ frame ] = ( frame . f_trace_lines , frame . f_trace_opcodes )
378
+ # We need f_trace_lines == True for the debugger to work
337
379
frame .f_trace_lines = True
338
380
frame = frame .f_back
339
381
self .set_step ()
@@ -353,9 +395,9 @@ def set_continue(self):
353
395
while frame and frame is not self .botframe :
354
396
del frame .f_trace
355
397
frame = frame .f_back
356
- for frame , prev_trace_lines in self .frame_trace_lines .items ():
357
- frame .f_trace_lines = prev_trace_lines
358
- self .frame_trace_lines = {}
398
+ for frame , ( trace_lines , trace_opcodes ) in self .frame_trace_lines_opcodes .items ():
399
+ frame .f_trace_lines , frame . f_trace_opcodes = trace_lines , trace_opcodes
400
+ self .frame_trace_lines_opcodes = {}
359
401
360
402
def set_quit (self ):
361
403
"""Set quitting attribute to True.
0 commit comments