--- a/DebugClients/Python/DebugBase.py Thu Oct 06 22:51:04 2016 +0200 +++ b/DebugClients/Python/DebugBase.py Fri Oct 07 22:50:48 2016 +0200 @@ -74,7 +74,8 @@ """ self._dbgClient = dbgClient - self._mainThread = True + # Some informations about the thread + self.isMainThread = False self.quitting = False self.id = -1 self.name = '' @@ -321,10 +322,12 @@ if self.stopframe and frame.f_code.co_flags & CO_GENERATOR: return # The program has finished if we have just left the first frame - if (frame == self.botframe and - self._mainThread): - atexit._run_exitfuncs() - self._dbgClient.progTerminated(arg) + if frame == self.botframe: + if self.isMainThread: + atexit._run_exitfuncs() + self._dbgClient.progTerminated(arg) + else: + self._dbgClient.threadTerminated(self.id) if self.quitting and not self._dbgClient.passive: raise SystemExit @@ -384,8 +387,9 @@ frame.f_trace = self.trace_dispatch while frame is not None: - # stop at erics debugger frame - if frame.f_back.f_code == stopOnHandleLine: + # stop at erics debugger frame or the threading bootstrap + if (frame.f_back.f_code == stopOnHandleLine or + frame.f_back.f_code.co_name == bootstrap): frame.f_trace = self.trace_dispatch self.botframe = frame break @@ -396,24 +400,37 @@ sys.settrace(self.trace_dispatch) sys.setprofile(self._dbgClient.callTraceEnabled) - def bootstrap(self): + def bootstrap(self, target, args, kwargs): """ - Public method to bootstrap the thread. + Public method to bootstrap a thread. It wraps the call to the user function to enable tracing before hand. + + @param target function which is called in the new created thread + @type function pointer + @param args arguments to pass to target + @type tuple + @param kwargs keyword arguments to pass to target + @type dict """ try: - self._threadRunning = True - self.traceThread() - self._target(*self._args, **self._kwargs) + frame = sys._getframe() + self.botframe = frame + # First time the dispach function is called, a "base debug" + # function has to be returned, which is called at every user code + # function call. Because of setting botframe manually, the + # specific branch returning the trace_dispatch itself is skipped. + # Because the next step is always in threading.py and we assume + # that there is no breakpoint in it, it's save to return + # trace_dispatch unconditionally. + sys.settrace(lambda frame, event, arg: self.trace_dispatch) + frame.f_trace = self.trace_dispatch + + target(*args, **kwargs) except SystemExit: pass finally: - self._threadRunning = False - self.quitting = True - self._dbgClient.threadTerminated(self) - sys.settrace(None) sys.setprofile(None) def run(self, cmd, globals=None, locals=None):