--- a/src/eric7/DebugClients/Python/DebugBase.py Sun Nov 10 15:21:04 2024 +0100 +++ b/src/eric7/DebugClients/Python/DebugBase.py Mon Nov 11 15:00:58 2024 +0100 @@ -106,6 +106,10 @@ self.returnframe = None self.stop_everywhere = False + # frame, where opcode tracing could start + self.enterframe = None + self.traceOpcodes = False + self.__recursionDepth = -1 self.setRecursionDepth(inspect.currentframe()) @@ -327,7 +331,7 @@ # check if we are still managing all exceptions self._dbgClient.checkExceptionHook() - if event == "line": + if event in ("line", "opcode"): # handle both events identically if self.stop_here(frame) or self.break_here(frame): if ( self.stop_everywhere @@ -393,11 +397,8 @@ self.user_exception(arg) return None - if event == "c_call": - return None - if event == "c_exception": - return None - if event == "c_return": + if event in ("c_call", "c_exception", "c_return"): + # ignore C events return None print( # __IGNORE_WARNING_M801__ @@ -423,16 +424,19 @@ stopOnHandleCommand = self._dbgClient.handleJsonCommand.__code__ - frame.f_trace = self.trace_dispatch - while frame.f_back is not None: - # stop at eric's debugger frame or a threading bootstrap - if frame.f_back.f_code == stopOnHandleCommand: - frame.f_trace = self.trace_dispatch + self.enterframe = frame + while frame is not None: + frame.f_trace = self.trace_dispatch + # We need f_trace_lines == True for the debugger to work. This should + # already be the case per default, but play it safe. + frame.f_trace_lines = True + frame = frame.f_back + if frame and frame.f_code is stopOnHandleCommand: + # stop at eric's debugger frame or a threading bootstrap break - frame = frame.f_back - self.stop_everywhere = True + self.set_stepinstr() sys.settrace(self.trace_dispatch) sys.setprofile(self._dbgClient.callTraceEnabled) @@ -522,7 +526,26 @@ sys.settrace(None) return exitcode - def _set_stopinfo(self, stopframe, returnframe, stoplineno=0): + def _set_trace_opcodes(self, traceOpcodes): + """ + Protected method to set tracing on opcode level enabled or disabled. + + @param traceOpcodes opcode tracing state + @type bool + """ + if traceOpcodes != self.traceOpcodes: + stopOnHandleCommand = self._dbgClient.handleJsonCommand.__code__ + + self.traceOpcodes = traceOpcodes + frame = self.enterframe + while frame is not None: + frame.f_trace_opcodes = traceOpcodes + frame = frame.f_back + if frame and frame.f_code is stopOnHandleCommand: + # stop at eric's debugger frame or a threading bootstrap + break + + def _set_stopinfo(self, stopframe, returnframe, stoplineno=0, traceOpcodes=False): """ Protected method to update the frame pointers. @@ -532,8 +555,10 @@ @type frame object @param stoplineno line number to stop at. If stoplineno is greater than or equal to 0, then stop at line greater than or equal to the - stopline. If stoplineno is -1, then don't stop at all. - @type int + stopline. If stoplineno is -1, then don't stop at all. (defaults to 0) + @type int (optional) + @param traceOpcodes opcode tracing state (defaults to False) + @type bool (optional) """ self.stopframe = stopframe self.returnframe = returnframe @@ -546,6 +571,8 @@ returnframe.f_trace = self.trace_dispatch self.stop_everywhere = False + self._set_trace_opcodes(traceOpcodes) + def set_continue(self, special): """ Public method to stop only on next breakpoint. @@ -586,6 +613,12 @@ self._set_stopinfo(None, None) self.stop_everywhere = True + def set_stepinstr(self): + """ + Public method to stop before the next instruction. + """ + self._set_stopinfo(None, None, opcode=True) + def set_next(self, frame): """ Public method to stop on the next line in or below the given frame.