44import  sys 
55import  os 
66from  inspect  import  CO_GENERATOR , CO_COROUTINE , CO_ASYNC_GENERATOR 
7+ from  functools  import  partial 
78
89__all__  =  ["BdbQuit" , "Bdb" , "Breakpoint" ]
910
@@ -14,6 +15,142 @@ class BdbQuit(Exception):
1415    """Exception to give up completely.""" 
1516
1617
18+ E  =  sys .monitoring .events 
19+ 
20+ class  _MonitoringTracer :
21+     def  __init__ (self ):
22+         self ._tool_id  =  sys .monitoring .DEBUGGER_ID 
23+         self ._name  =  'bdbtracer' 
24+         self ._tracefunc  =  None 
25+ 
26+     def  start_trace (self , tracefunc ):
27+         self ._tracefunc  =  tracefunc 
28+         curr_tool  =  sys .monitoring .get_tool (self ._tool_id )
29+         if  curr_tool  is  None :
30+             sys .monitoring .use_tool_id (self ._tool_id , self ._name )
31+         elif  curr_tool  ==  self ._name :
32+             sys .monitoring .set_events (self ._tool_id , 0 )
33+         else :
34+             raise  ValueError ('Another debugger is using the monitoring tool' )
35+         E  =  sys .monitoring .events 
36+         all_events  =  0 
37+         for  event  in  (E .PY_START , E .PY_RESUME , E .PY_THROW ):
38+             sys .monitoring .register_callback (self ._tool_id , event , self .call_callback )
39+             all_events  |=  event 
40+         for  event  in  (E .LINE , ):
41+             sys .monitoring .register_callback (self ._tool_id , event , self .line_callback )
42+             all_events  |=  event 
43+         for  event  in  (E .JUMP , ):
44+             sys .monitoring .register_callback (self ._tool_id , event , self .jump_callback )
45+             all_events  |=  event 
46+         for  event  in  (E .PY_RETURN , E .PY_YIELD ):
47+             sys .monitoring .register_callback (self ._tool_id , event , self .return_callback )
48+             all_events  |=  event 
49+         for  event  in  (E .PY_UNWIND , ):
50+             sys .monitoring .register_callback (self ._tool_id , event , self .unwind_callback )
51+             all_events  |=  event 
52+         for  event  in  (E .RAISE , E .STOP_ITERATION ):
53+             sys .monitoring .register_callback (self ._tool_id , event , self .exception_callback )
54+             all_events  |=  event 
55+         for  event  in  (E .INSTRUCTION , ):
56+             sys .monitoring .register_callback (self ._tool_id , event , self .opcode_callback )
57+         self .check_trace_opcodes ()
58+         sys .monitoring .set_events (self ._tool_id , all_events )
59+ 
60+     def  stop_trace (self ):
61+         curr_tool  =  sys .monitoring .get_tool (self ._tool_id )
62+         if  curr_tool  !=  self ._name :
63+             return 
64+         for  event  in  (E .PY_START , E .PY_RESUME , E .PY_RETURN , E .PY_YIELD , E .RAISE , E .LINE ,
65+                       E .JUMP , E .PY_UNWIND , E .PY_THROW , E .STOP_ITERATION ):
66+             sys .monitoring .register_callback (self ._tool_id , event , None )
67+         sys .monitoring .set_events (self ._tool_id , 0 )
68+         self .check_trace_opcodes ()
69+         sys .monitoring .free_tool_id (self ._tool_id )
70+ 
71+     def  callback_wrapper (func ):
72+         def  wrapper (self , * args ):
73+             try :
74+                 frame  =  sys ._getframe ().f_back 
75+                 return  func (self , frame , * args )
76+             except  Exception :
77+                 self .stop_trace ()
78+                 raise 
79+         return  wrapper 
80+ 
81+     @callback_wrapper  
82+     def  call_callback (self , frame , code , * args ):
83+         local_tracefunc  =  self ._tracefunc (frame , 'call' , None )
84+         if  local_tracefunc  is  not None :
85+             frame .f_trace  =  local_tracefunc 
86+ 
87+     @callback_wrapper  
88+     def  return_callback (self , frame , code , offset , retval ):
89+         if  frame .f_trace :
90+             frame .f_trace (frame , 'return' , retval )
91+ 
92+     @callback_wrapper  
93+     def  unwind_callback (self , frame , code , * args ):
94+         if  frame .f_trace :
95+             frame .f_trace (frame , 'return' , None )
96+ 
97+     @callback_wrapper  
98+     def  line_callback (self , frame , code , * args ):
99+         if  frame .f_trace  and  frame .f_trace_lines :
100+             frame .f_trace (frame , 'line' , None )
101+ 
102+     @callback_wrapper  
103+     def  jump_callback (self , frame , code , inst_offset , dest_offset ):
104+         if  dest_offset  >  inst_offset :
105+             return  sys .monitoring .DISABLE 
106+         inst_lineno  =  self ._get_lineno (code , inst_offset )
107+         dest_lineno  =  self ._get_lineno (code , dest_offset )
108+         if  inst_lineno  !=  dest_lineno :
109+             return  sys .monitoring .DISABLE 
110+         if  frame .f_trace  and  frame .f_trace_lines :
111+             frame .f_trace (frame , 'line' , None )
112+ 
113+     @callback_wrapper  
114+     def  exception_callback (self , frame , code , offset , exc ):
115+         if  frame .f_trace :
116+             if  exc .__traceback__  and  hasattr (exc .__traceback__ , 'tb_frame' ):
117+                 tb  =  exc .__traceback__ 
118+                 while  tb :
119+                     if  tb .tb_frame .f_locals .get ('self' ) is  self :
120+                         return 
121+                     tb  =  tb .tb_next 
122+             frame .f_trace (frame , 'exception' , (type (exc ), exc , exc .__traceback__ ))
123+ 
124+     @callback_wrapper  
125+     def  opcode_callback (self , frame , code , offset ):
126+         if  frame .f_trace  and  frame .f_trace_opcodes :
127+             frame .f_trace (frame , 'opcode' , None )
128+ 
129+     def  check_trace_opcodes (self , frame = None ):
130+         if  frame  is  None :
131+             frame  =  sys ._getframe ().f_back 
132+         while  frame  is  not None :
133+             self .set_trace_opcodes (frame , frame .f_trace_opcodes )
134+             frame  =  frame .f_back 
135+ 
136+     def  set_trace_opcodes (self , frame , trace_opcodes ):
137+         if  sys .monitoring .get_tool (self ._tool_id ) !=  self ._name :
138+             return 
139+         if  trace_opcodes :
140+             sys .monitoring .set_local_events (self ._tool_id , frame .f_code , E .INSTRUCTION )
141+         else :
142+             sys .monitoring .set_local_events (self ._tool_id , frame .f_code , 0 )
143+ 
144+     def  _get_lineno (self , code , offset ):
145+         import  dis 
146+         last_lineno  =  None 
147+         for  start , lineno  in  dis .findlinestarts (code ):
148+             if  offset  <  start :
149+                 return  last_lineno 
150+             last_lineno  =  lineno 
151+         return  last_lineno 
152+ 
153+ 
17154class  Bdb :
18155    """Generic Python debugger base class. 
19156
@@ -28,14 +165,19 @@ class Bdb:
28165    is determined by the __name__ in the frame globals. 
29166    """ 
30167
31-     def  __init__ (self , skip = None ):
168+     def  __init__ (self , skip = None ,  backend = 'monitoring' ):
32169        self .skip  =  set (skip ) if  skip  else  None 
33170        self .breaks  =  {}
34171        self .fncache  =  {}
35172        self .frame_trace_lines_opcodes  =  {}
36173        self .frame_returning  =  None 
37174        self .trace_opcodes  =  False 
38175        self .enterframe  =  None 
176+         self .backend  =  backend 
177+         if  backend  ==  'monitoring' :
178+             self .monitoring_tracer  =  _MonitoringTracer ()
179+         else :
180+             self .monitoring_tracer  =  None 
39181
40182        self ._load_breaks ()
41183
@@ -56,6 +198,18 @@ def canonic(self, filename):
56198            self .fncache [filename ] =  canonic 
57199        return  canonic 
58200
201+     def  start_trace (self , trace_dispatch ):
202+         if  self .backend  ==  'monitoring' :
203+             self .monitoring_tracer .start_trace (trace_dispatch )
204+         else :
205+             sys .settrace (self .trace_dispatch )
206+ 
207+     def  stop_trace (self ):
208+         if  self .backend  ==  'monitoring' :
209+             self .monitoring_tracer .stop_trace ()
210+         else :
211+             sys .settrace (None )
212+ 
59213    def  reset (self ):
60214        """Set values of attributes as ready to start debugging.""" 
61215        import  linecache 
@@ -306,6 +460,8 @@ def _set_trace_opcodes(self, trace_opcodes):
306460            frame  =  self .enterframe 
307461            while  frame  is  not None :
308462                frame .f_trace_opcodes  =  trace_opcodes 
463+                 if  self .backend  ==  'monitoring' :
464+                     self .monitoring_tracer .set_trace_opcodes (frame , trace_opcodes )
309465                if  frame  is  self .botframe :
310466                    break 
311467                frame  =  frame .f_back 
@@ -369,7 +525,7 @@ def set_trace(self, frame=None):
369525
370526        If frame is not specified, debugging starts from caller's frame. 
371527        """ 
372-         sys . settrace ( None )
528+         self . stop_trace ( )
373529        if  frame  is  None :
374530            frame  =  sys ._getframe ().f_back 
375531        self .reset ()
@@ -382,7 +538,7 @@ def set_trace(self, frame=None):
382538            frame .f_trace_lines  =  True 
383539            frame  =  frame .f_back 
384540        self .set_stepinstr ()
385-         sys . settrace (self .trace_dispatch )
541+         self . start_trace (self .trace_dispatch )
386542
387543    def  set_continue (self ):
388544        """Stop only at breakpoints or when finished. 
@@ -393,13 +549,15 @@ def set_continue(self):
393549        self ._set_stopinfo (self .botframe , None , - 1 )
394550        if  not  self .breaks :
395551            # no breakpoints; run without debugger overhead 
396-             sys . settrace ( None )
552+             self . stop_trace ( )
397553            frame  =  sys ._getframe ().f_back 
398554            while  frame  and  frame  is  not self .botframe :
399555                del  frame .f_trace 
400556                frame  =  frame .f_back 
401557            for  frame , (trace_lines , trace_opcodes ) in  self .frame_trace_lines_opcodes .items ():
402558                frame .f_trace_lines , frame .f_trace_opcodes  =  trace_lines , trace_opcodes 
559+                 if  self .backend  ==  'monitoring' :
560+                     self .monitoring_tracer .set_trace_opcodes (frame , trace_opcodes )
403561            self .frame_trace_lines_opcodes  =  {}
404562
405563    def  set_quit (self ):
@@ -410,7 +568,7 @@ def set_quit(self):
410568        self .stopframe  =  self .botframe 
411569        self .returnframe  =  None 
412570        self .quitting  =  True 
413-         sys . settrace ( None )
571+         self . stop_trace ( )
414572
415573    # Derived classes and clients can call the following methods 
416574    # to manipulate breakpoints.  These methods return an 
@@ -647,14 +805,14 @@ def run(self, cmd, globals=None, locals=None):
647805        self .reset ()
648806        if  isinstance (cmd , str ):
649807            cmd  =  compile (cmd , "<string>" , "exec" )
650-         sys . settrace (self .trace_dispatch )
808+         self . start_trace (self .trace_dispatch )
651809        try :
652810            exec (cmd , globals , locals )
653811        except  BdbQuit :
654812            pass 
655813        finally :
656814            self .quitting  =  True 
657-             sys . settrace ( None )
815+             self . stop_trace ( )
658816
659817    def  runeval (self , expr , globals = None , locals = None ):
660818        """Debug an expression executed via the eval() function. 
@@ -667,14 +825,14 @@ def runeval(self, expr, globals=None, locals=None):
667825        if  locals  is  None :
668826            locals  =  globals 
669827        self .reset ()
670-         sys . settrace (self .trace_dispatch )
828+         self . start_trace (self .trace_dispatch )
671829        try :
672830            return  eval (expr , globals , locals )
673831        except  BdbQuit :
674832            pass 
675833        finally :
676834            self .quitting  =  True 
677-             sys . settrace ( None )
835+             self . stop_trace ( )
678836
679837    def  runctx (self , cmd , globals , locals ):
680838        """For backwards-compatibility.  Defers to run().""" 
@@ -689,15 +847,15 @@ def runcall(self, func, /, *args, **kwds):
689847        Return the result of the function call. 
690848        """ 
691849        self .reset ()
692-         sys . settrace (self .trace_dispatch )
850+         self . start_trace (self .trace_dispatch )
693851        res  =  None 
694852        try :
695853            res  =  func (* args , ** kwds )
696854        except  BdbQuit :
697855            pass 
698856        finally :
699857            self .quitting  =  True 
700-             sys . settrace ( None )
858+             self . stop_trace ( )
701859        return  res 
702860
703861
0 commit comments