Insert import hook to modify thread module to use our bootstrap. debugger speed

Fri, 07 Oct 2016 22:50:48 +0200

author
T.Rzepka <Tobias.Rzepka@gmail.com>
date
Fri, 07 Oct 2016 22:50:48 +0200
branch
debugger speed
changeset 5209
cd058aa6af37
parent 5208
aa8045780ce4
child 5221
960afd19c1b6

Insert import hook to modify thread module to use our bootstrap.

DebugClients/Python/DebugBase.py file | annotate | diff | comparison | revisions
DebugClients/Python/ThreadExtension.py file | annotate | diff | comparison | revisions
--- 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):
--- a/DebugClients/Python/ThreadExtension.py	Thu Oct 06 22:51:04 2016 +0200
+++ b/DebugClients/Python/ThreadExtension.py	Fri Oct 07 22:50:48 2016 +0200
@@ -32,6 +32,7 @@
         Constructor
         """
         self.threadNumber = 1
+        self.enableImportHooks = True
         self._original_start_new_thread = None
         
         self.clientLock = threading.RLock()
@@ -54,6 +55,8 @@
         del sys.modules[self.threadModName]
         del sys.modules['threading']
         
+        sys.meta_path.insert(0, self)
+        
         # provide a hook to perform a hard breakpoint
         # Use it like this:
         # if hasattr(sys, 'breakpoint): sys.breakpoint()
@@ -79,7 +82,7 @@
             ident = _thread.get_ident()
             name = 'MainThread'
             newThread = self.mainThread
-            newThread._mainThread = True
+            newThread.isMainThread = True
             if self.debugging:
                 sys.setprofile(newThread.profile)
             
@@ -97,15 +100,16 @@
 
         return ident
     
-    def threadTerminated(self, dbgThread):
+    def threadTerminated(self, threadId):
         """
         Public method called when a DebugThread has exited.
         
-        @param dbgThread the DebugThread that has exited
+        @param threadId id of the DebugThread that has exited
+        @type int
         """
         self.lockClient()
         try:
-            del self.threads[dbgThread.get_ident()]
+            del self.threads[threadId]
         except KeyError:
             pass
         finally:
@@ -142,10 +146,10 @@
         """
         try:
             self.lockClient()
-            if id is None or id not in self.threads:
+            if id is None:
                 self.currentThread = None
             else:
-                self.currentThread = self.threads[id]
+                self.currentThread = self.threads.get(id)
         finally:
             self.unlockClient()
     
@@ -180,6 +184,46 @@
             "currentID": currentId,
             "threadList": threadList,
         })
+    
+    def find_module(self, fullname, path=None):
+        """
+        Public method returning the module loader.
+        
+        @param fullname name of the module to be loaded
+        @type str
+        @param path path to resolve the module name
+        @type str
+        @return module loader object
+        @rtype object
+        """
+        if fullname in sys.modules or not self.debugging:
+            return None
+        if fullname == self.threadModName and self.enableImportHooks:
+            # Disable hook to be able to import original module
+            self.enableImportHooks = False
+            return self
+        
+        return None
+    
+    def load_module(self, fullname):
+        """
+        Public method to load a module.
+        
+        @param fullname name of the module to be loaded
+        @type str
+        @return reference to the loaded module
+        @rtype module
+        """
+        module = __import__(fullname)
+        sys.modules[fullname] = module
+        if (fullname == self.threadModName and
+                self._original_start_new_thread is None):
+            # make thread hooks available to system
+            self._original_start_new_thread = module.start_new_thread
+            module.start_new_thread = self.attachThread
+        
+        self.enableImportHooks = True
+        return module
 
 
 #

eric ide

mercurial