DebugClients/Python/ThreadExtension.py

branch
debugger speed
changeset 5207
7283629b02c0
child 5208
aa8045780ce4
diff -r 997064ba25d6 -r 7283629b02c0 DebugClients/Python/ThreadExtension.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DebugClients/Python/ThreadExtension.py	Thu Oct 06 22:28:12 2016 +0200
@@ -0,0 +1,182 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2014 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing an import hook patching thread modules to get debugged too.
+"""
+
+import os.path
+import sys
+
+if sys.version_info[0] == 2:
+    import thread as _thread
+else:
+    import _thread
+
+import threading
+
+from DebugBase import DebugBase
+
+
+class ThreadExtension(object):
+    """
+    Class implementing the thread support for the debugger.
+    
+    Provides methods for intercepting thread creation, retriving the running
+    threads and their name and state.
+    """
+    def __init__(self):
+        """
+        Constructor
+        """
+        self.threadNumber = 1
+        self._original_start_new_thread = None
+        
+        self.clientLock = threading.RLock()
+        
+        # dictionary of all threads running {id: DebugBase}
+        self.threads = {}
+
+        # the "current" thread, basically for variables view
+        self.currentThread = self
+        
+        # special objects representing the main scripts thread and frame
+        self.mainThread = self
+        
+        if sys.version_info[0] == 2:
+            self.threadModName = 'thread'
+        else:
+            self.threadModName = '_thread'
+        
+        # reset already imported thread module to apply hooks at next import
+        del sys.modules[self.threadModName]
+        del sys.modules['threading']
+        
+        # provide a hook to perform a hard breakpoint
+        # Use it like this:
+        # if hasattr(sys, 'breakpoint): sys.breakpoint()
+        sys.breakpoint = self.set_trace
+
+    def attachThread(self, target=None, args=None, kwargs=None,
+                     mainThread=False):
+        """
+        Public method to setup a thread for DebugClient to debug.
+        
+        If mainThread is True, then we are attaching to the already
+        started mainthread of the app and the rest of the args are ignored.
+        
+        @param target the start function of the target thread (i.e. the
+            user code)
+        @param args arguments to pass to target
+        @param kwargs keyword arguments to pass to target
+        @param mainThread True, if we are attaching to the already
+              started mainthread of the app
+        @return identifier of the created thread
+        """
+        return
+        try:
+            self.lockClient()
+            newThread = DebugThread(self, target, args, kwargs, mainThread)
+            ident = -1
+            if mainThread:
+                ident = _thread.get_ident()
+                self.mainThread = newThread
+                if self.debugging:
+                    sys.setprofile(newThread.profile)
+            else:
+                ident = _original_start_thread(newThread.bootstrap, ())
+                if self.mainThread is not None:
+                    self.tracePython = self.mainThread.tracePython
+            newThread.set_ident(ident)
+            self.threads[newThread.get_ident()] = newThread
+        finally:
+            self.unlockClient()
+        return ident
+    
+    def threadTerminated(self, dbgThread):
+        """
+        Public method called when a DebugThread has exited.
+        
+        @param dbgThread the DebugThread that has exited
+        """
+        self.lockClient()
+        try:
+            del self.threads[dbgThread.get_ident()]
+        except KeyError:
+            pass
+        finally:
+            self.unlockClient()
+    
+    def lockClient(self, blocking=True):
+        """
+        Public method to acquire the lock for this client.
+        
+        @param blocking flag to indicating a blocking lock
+        @type bool
+        @return flag indicating successful locking
+        @rtype bool
+        """
+        if blocking:
+            self.clientLock.acquire()
+        else:
+            return self.clientLock.acquire(blocking)
+    
+    def unlockClient(self):
+        """
+        Public method to release the lock for this client.
+        """
+        try:
+            self.clientLock.release()
+        except AssertionError:
+            pass
+    
+    def setCurrentThread(self, id):
+        """
+        Public method to set the current thread.
+
+        @param id the id the current thread should be set to.
+        """
+        try:
+            self.lockClient()
+            if id is None or id not in self.threads:
+                self.currentThread = None
+            else:
+                self.currentThread = self.threads[id]
+        finally:
+            self.unlockClient()
+    
+    def dumpThreadList(self):
+        """
+        Public method to send the list of threads.
+        """
+        threadList = []
+        if self.threads and self.currentThread:
+            # indication for the threaded debugger
+            currentId = self.currentThread.get_ident()
+            for t in self.threads.values():
+                d = {}
+                d["id"] = t.get_ident()
+                d["name"] = t.get_name()
+                d["broken"] = t.isBroken
+                threadList.append(d)
+        else:
+            currentId = -1
+            d = {}
+            d["id"] = -1
+            d["name"] = "MainThread"
+            if hasattr(self, "isBroken"):
+                d["broken"] = self.isBroken
+            else:
+                d["broken"] = False
+            threadList.append(d)
+        
+        self.sendJsonCommand("ResponseThreadList", {
+            "currentID": currentId,
+            "threadList": threadList,
+        })
+
+
+#
+# eflag: noqa = M702

eric ide

mercurial