First improvements on debugger speed: Cheaper detection of unwanted files and debugger speed

Sun, 26 Jun 2016 16:32:01 +0200

author
T.Rzepka <Tobias.Rzepka@gmail.com>
date
Sun, 26 Jun 2016 16:32:01 +0200
branch
debugger speed
changeset 5005
684f5ba04f0b
parent 5004
556528860c7a
child 5006
82e91d2f59a1

First improvements on debugger speed: Cheaper detection of unwanted files and
some litte style changes to be equal between Python 2/3.

DebugClients/Python/DebugBase.py file | annotate | diff | comparison | revisions
DebugClients/Python/DebugClientBase.py file | annotate | diff | comparison | revisions
DebugClients/Python3/DebugBase.py file | annotate | diff | comparison | revisions
DebugClients/Python3/DebugClientBase.py file | annotate | diff | comparison | revisions
diff -r 556528860c7a -r 684f5ba04f0b DebugClients/Python/DebugBase.py
--- a/DebugClients/Python/DebugBase.py	Thu Jun 23 19:24:09 2016 +0200
+++ b/DebugClients/Python/DebugBase.py	Sun Jun 26 16:32:01 2016 +0200
@@ -48,6 +48,13 @@
     Provides simple wrapper methods around bdb for the 'owning' client to
     call to step etc.
     """
+    # Don't thrust distutils.sysconfig.get_python_lib: possible case mismatch
+    #  on Windows
+    lib = os.path.dirname(bdb.__file__)
+    # tuple required because it's accessed a lot of times by startswith method
+    pathsToSkip = ('<', os.path.dirname(__file__), bdb.__file__[:-1])
+    filesToSkip = {}
+
     def __init__(self, dbgClient):
         """
         Constructor
@@ -60,6 +67,7 @@
         self._mainThread = 1
         
         self.breaks = self._dbgClient.breakpoints
+        self.tracePythonLibs(0)
         
         self.__event = ""
         self.__isBroken = ""
@@ -197,7 +205,8 @@
         @param toFrame destination frame (frame)
         """
         if self._dbgClient.callTraceEnabled:
-            if not self.__skip_it(fromFrame) and not self.__skip_it(toFrame):
+            if (not self.__skipFrame(fromFrame) and
+                    not self.__skipFrame(toFrame)):
                 if event in ["call", "return"]:
                     fr = fromFrame
                     fromStr = "%s:%s:%s" % (
@@ -248,7 +257,7 @@
             return self.trace_dispatch
         if event == 'c_return':
             return self.trace_dispatch
-        print 'DebugBase.trace_dispatch: unknown debugging event:', repr(event) # __IGNORE_WARNING__
+        print 'DebugBase.trace_dispatch: unknown debugging event:', repr(event)  # __IGNORE_WARNING__
         return self.trace_dispatch
 
     def dispatch_line(self, frame):
@@ -293,7 +302,7 @@
         @return local trace function
         @exception bdb.BdbQuit raised to indicate the end of the debug session
         """
-        if not self.__skip_it(frame):
+        if not self.__skipFrame(frame):
             self.user_exception(frame, arg)
             if self.quitting:
                 raise bdb.BdbQuit
@@ -534,8 +543,8 @@
         Because eric6 supports only one breakpoint per line, this overwritten
         method will return this one and only breakpoint.
         
-        @param filename filename of the bp to retrieve (string)
-        @param lineno linenumber of the bp to retrieve (integer)
+        @param filename the filename of the bp to retrieve (string)
+        @param lineno the linenumber of the bp to retrieve (integer)
         @return breakpoint or None, if there is no bp
         """
         filename = self.canonic(filename)
@@ -772,8 +781,8 @@
 
     def user_return(self, frame, retval):
         """
-        Public method reimplemented to report program termination to the
-        debug server.
+        Public method reimplemented to report program termination to the debug
+        server.
         
         @param frame the frame object
         @param retval the return value of the program
@@ -797,11 +806,30 @@
         @param frame the frame object
         @return flag indicating whether the debugger should stop here
         """
-        if self.__skip_it(frame):
+        if self.__skipFrame(frame):
             return 0
         return bdb.Bdb.stop_here(self, frame)
 
-    def __skip_it(self, frame):
+    def tracePythonLibs(self, enable):
+        """
+        Public methode to update the settings to trace into Python libraries.
+        
+        @param enable let's debug into Python libraries (int)
+        """
+        pathsToSkip = list(self.pathsToSkip)
+        # don't trace into Python library?
+        if enable:
+            pathsToSkip = [x for x in pathsToSkip if not x.endswith(
+                ("site-packages", "dist-packages", self.lib))]
+        else:
+            pathsToSkip.append(self.lib)
+            localLib = [x for x in sys.path if x.endswith(("site-packages",
+                        "dist-packages")) and not x.startswith(self.lib)]
+            pathsToSkip.extend(localLib)
+        
+        self.pathsToSkip = tuple(pathsToSkip)
+
+    def __skipFrame(self, frame):
         """
         Private method to filter out debugger files.
         
@@ -811,33 +839,15 @@
         @param frame the frame object
         @return flag indicating whether the debugger should skip this frame
         """
-        if frame is None:
-            return 1
-        
-        fn = self.fix_frame_filename(frame)
-
-        # Eliminate things like <string> and <stdin>.
-        if fn[0] == '<':
-            return 1
-
-        #XXX - think of a better way to do this.  It's only a convenience for
-        #debugging the debugger - when the debugger code is in the current
-        #directory.
-        if os.path.basename(fn) in [
-            'AsyncFile.py', 'AsyncIO.py',
-            'DebugConfig.py', 'DCTestResult.py',
-            'DebugBase.py', 'DebugClientBase.py',
-            'DebugClientCapabilities.py', 'DebugClient.py',
-            'DebugClientThreads.py', 'DebugProtocol.py',
-            'DebugThread.py', 'FlexCompleter.py',
-            'PyProfile.py'] or \
-           os.path.dirname(fn).endswith("coverage"):
-            return 1
-
-        if self._dbgClient.shouldSkip(fn):
-            return 1
-        
-        return 0
+        try:
+            return self.filesToSkip[frame.f_code.co_filename]
+        except KeyError:
+            ret = frame.f_code.co_filename.startswith(self.pathsToSkip)
+            self.filesToSkip[frame.f_code.co_filename] = ret
+            return ret
+        except AttributeError:
+            # if frame is None
+            return True
     
     def isBroken(self):
         """
diff -r 556528860c7a -r 684f5ba04f0b DebugClients/Python/DebugClientBase.py
--- a/DebugClients/Python/DebugClientBase.py	Thu Jun 23 19:24:09 2016 +0200
+++ b/DebugClients/Python/DebugClientBase.py	Sun Jun 26 16:32:01 2016 +0200
@@ -194,8 +194,8 @@
         # dictionary of all threads running
         self.threads = {}
         
-        # the "current" thread, basically the thread we are at a
-        # breakpoint for.
+        # the "current" thread, basically the thread we are at a breakpoint
+        # for.
         self.currentThread = self
         
         # special objects representing the main scripts thread and frame
@@ -295,9 +295,6 @@
         If mainThread is non-zero, then we are attaching to the already
         started mainthread of the app and the rest of the args are ignored.
         
-        This is just an empty function and is overridden in the threaded
-        debugger.
-        
         @param target the start function of the target thread (i.e. the user
             code)
         @param args arguments to pass to target
@@ -512,7 +509,6 @@
                     os.chdir(sys.path[1])
                 else:
                     os.chdir(wd)
-                tracePython = int(tracePython)
                 self.running = sys.argv[0]
                 self.mainFrame = None
                 self.inRawMode = 0
@@ -526,11 +522,11 @@
                 sys.excepthook = self.__unhandled_exception
                 self.__interceptSignals()
                 
-                # clear all old breakpoints, they'll get set after we
-                # have started
+                # clear all old breakpoints, they'll get set after we have
+                # started
                 self.mainThread.clear_all_breaks()
                 
-                self.mainThread.tracePython = tracePython
+                self.mainThread.tracePythonLibs(int(tracePython))
                 
                 # This will eventually enter a local event loop.
                 # Note the use of backquotes to cause a repr of self.running.
@@ -573,7 +569,7 @@
                 sys.excepthook = self.__unhandled_exception
                 self.__interceptSignals()
                 
-                self.mainThread.tracePython = 0
+                self.mainThread.tracePythonLibs(0)
                 
                 self.debugMod.__dict__['__file__'] = sys.argv[0]
                 sys.modules['__main__'] = self.debugMod
@@ -770,7 +766,8 @@
             if cmd == DebugProtocol.RequestEval:
                 try:
                     value = eval(
-                        arg, self.currentThread.getCurrentFrame().f_globals,
+                        arg,
+                        self.currentThread.getCurrentFrame().f_globals,
                         self.currentThread.getFrameLocals(self.framenr))
                     self.currentThread.storeFrameLocals(self.framenr)
                 except Exception:
@@ -1337,8 +1334,8 @@
         @param frmnr distance of frame reported on. 0 is the current frame
             (int)
         @param scope 1 to report global variables, 0 for local variables (int)
-        @param filter the indices of variable types to be filtered (list of
-            int)
+        @param filter the indices of variable types to be filtered
+            (list of int)
         """
         if self.currentThread is None:
             return
@@ -1489,7 +1486,7 @@
                             oaccess = ''
                         try:
                             exec 'mdict = dict%s.__dict__' % access
-                            ndict.update(mdict)     # __IGNORE_WARNING__
+                            ndict.update(mdict)
                             exec 'obj = dict%s' % access
                             if "PyQt4." in str(type(obj)) or \
                                     "PyQt5." in str(type(obj)):
@@ -1501,8 +1498,8 @@
                             pass
                         try:
                             exec 'mcdict = dict%s.__class__.__dict__' % access
-                            ndict.update(mcdict)     # __IGNORE_WARNING__
-                            if mdict and "sipThis" not in mdict.keys():  # __IGNORE_WARNING__
+                            ndict.update(mcdict)
+                            if mdict and "sipThis" not in mdict.keys():
                                 del rvar[0:2]
                                 access = ""
                         except Exception:
@@ -1628,9 +1625,9 @@
         
     def __formatQtVariable(self, value, vtype):
         """
-        Private method to produce a formated output of a simple Qt4/Qt5 type.
+        Private method to produce a formatted output of a simple Qt4/Qt5 type.
         
-        @param value variable to be formated
+        @param value variable to be formatted
         @param vtype type of the variable to be formatted (string)
         @return A tuple consisting of a list of formatted variables. Each
             variable entry is a tuple of three elements, the variable name,
@@ -1977,7 +1974,7 @@
         self.debugging = 1
         
         self.attachThread(mainThread=1)
-        self.mainThread.tracePython = tracePython
+        self.mainThread.tracePythonLibs(tracePython)
         
         # set the system exception handling function to ensure, that
         # we report on all unhandled exceptions
@@ -2035,7 +2032,7 @@
         self.__interact()
         
         self.attachThread(mainThread=1)
-        self.mainThread.tracePython = tracePython
+        self.mainThread.tracePythonLibs(tracePython)
         
         # set the system exception handling function to ensure, that
         # we report on all unhandled exceptions
diff -r 556528860c7a -r 684f5ba04f0b DebugClients/Python3/DebugBase.py
--- a/DebugClients/Python3/DebugBase.py	Thu Jun 23 19:24:09 2016 +0200
+++ b/DebugClients/Python3/DebugBase.py	Sun Jun 26 16:32:01 2016 +0200
@@ -49,6 +49,13 @@
     Provides simple wrapper methods around bdb for the 'owning' client to
     call to step etc.
     """
+    # Don't thrust distutils.sysconfig.get_python_lib: possible case mismatch
+    #  on Windows
+    lib = os.path.dirname(bdb.__file__)
+    # tuple required because it's accessed a lot of times by startswith method
+    pathsToSkip = ('<', os.path.dirname(__file__), bdb.__file__[:-1])
+    filesToSkip = {}
+
     def __init__(self, dbgClient):
         """
         Constructor
@@ -61,6 +68,7 @@
         self._mainThread = True
         
         self.breaks = self._dbgClient.breakpoints
+        self.tracePythonLibs(0)
         
         self.__event = ""
         self.__isBroken = ""
@@ -198,7 +206,8 @@
         @param toFrame destination frame (frame)
         """
         if self._dbgClient.callTraceEnabled:
-            if not self.__skip_it(fromFrame) and not self.__skip_it(toFrame):
+            if (not self.__skipFrame(fromFrame) and
+                    not self.__skipFrame(toFrame)):
                 if event in ["call", "return"]:
                     fr = fromFrame
                     fromStr = "{0}:{1}:{2}".format(
@@ -297,7 +306,7 @@
         @return local trace function
         @exception bdb.BdbQuit raised to indicate the end of the debug session
         """
-        if not self.__skip_it(frame):
+        if not self.__skipFrame(frame):
             # When stepping with next/until/return in a generator frame,
             # skip the internal StopIteration exception (with no traceback)
             # triggered by a subiterator run with the 'yield from'
@@ -845,12 +854,31 @@
         @param frame the frame object
         @return flag indicating whether the debugger should stop here
         """
-        if self.__skip_it(frame):
+        if self.__skipFrame(frame):
             return False
         
         return bdb.Bdb.stop_here(self, frame)
 
-    def __skip_it(self, frame):
+    def tracePythonLibs(self, enable):
+        """
+        Public methode to update the settings to trace into Python libraries.
+        
+        @param enable let's debug into Python libraries (int)
+        """
+        pathsToSkip = list(self.pathsToSkip)
+        # don't trace into Python library?
+        if enable:
+            pathsToSkip = [x for x in pathsToSkip if not x.endswith(
+                ("site-packages", "dist-packages", self.lib))]
+        else:
+            pathsToSkip.append(self.lib)
+            localLib = [x for x in sys.path if x.endswith(("site-packages",
+                        "dist-packages")) and not x.startswith(self.lib)]
+            pathsToSkip.extend(localLib)
+        
+        self.pathsToSkip = tuple(pathsToSkip)
+
+    def __skipFrame(self, frame):
         """
         Private method to filter out debugger files.
         
@@ -860,33 +888,15 @@
         @param frame the frame object
         @return flag indicating whether the debugger should skip this frame
         """
-        if frame is None:
-            return True
-        
-        fn = self.fix_frame_filename(frame)
-
-        # Eliminate things like <string> and <stdin>.
-        if fn[0] == '<':
+        try:
+            return self.filesToSkip[frame.f_code.co_filename]
+        except KeyError:
+            ret = frame.f_code.co_filename.startswith(self.pathsToSkip)
+            self.filesToSkip[frame.f_code.co_filename] = ret
+            return ret
+        except AttributeError:
+            # if frame is None
             return True
-
-        #XXX - think of a better way to do this.  It's only a convenience for
-        #debugging the debugger - when the debugger code is in the current
-        #directory.
-        if os.path.basename(fn) in [
-            'AsyncFile.py', 'AsyncIO.py',
-            'DebugConfig.py', 'DCTestResult.py',
-            'DebugBase.py', 'DebugClientBase.py',
-            'DebugClientCapabilities.py', 'DebugClient.py',
-            'DebugClientThreads.py', 'DebugProtocol.py',
-            'DebugThread.py', 'FlexCompleter.py',
-            'PyProfile.py'] or \
-           os.path.dirname(fn).endswith("coverage"):
-            return True
-
-        if self._dbgClient.shouldSkip(fn):
-            return True
-        
-        return False
     
     def isBroken(self):
         """
diff -r 556528860c7a -r 684f5ba04f0b DebugClients/Python3/DebugClientBase.py
--- a/DebugClients/Python3/DebugClientBase.py	Thu Jun 23 19:24:09 2016 +0200
+++ b/DebugClients/Python3/DebugClientBase.py	Sun Jun 26 16:32:01 2016 +0200
@@ -267,8 +267,8 @@
         If mainThread is non-zero, 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 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
@@ -502,7 +502,6 @@
                     os.chdir(sys.path[1])
                 else:
                     os.chdir(wd)
-                tracePython = int(tracePython)
                 self.running = sys.argv[0]
                 self.mainFrame = None
                 self.inRawMode = False
@@ -520,7 +519,7 @@
                 # started
                 self.mainThread.clear_all_breaks()
                 
-                self.mainThread.tracePython = tracePython
+                self.mainThread.tracePythonLibs(int(tracePython))
                 
                 # This will eventually enter a local event loop.
                 # Note the use of backquotes to cause a repr of self.running.
@@ -562,7 +561,7 @@
                 sys.excepthook = self.__unhandled_exception
                 self.__interceptSignals()
                 
-                self.mainThread.tracePython = False
+                self.mainThread.tracePythonLibs(0)
                 
                 self.debugMod.__dict__['__file__'] = sys.argv[0]
                 sys.modules['__main__'] = self.debugMod
@@ -2028,7 +2027,7 @@
         self.debugging = True
         
         self.attachThread(mainThread=True)
-        self.mainThread.tracePython = tracePython
+        self.mainThread.tracePythonLibs(tracePython)
         
         # set the system exception handling function to ensure, that
         # we report on all unhandled exceptions
@@ -2086,7 +2085,7 @@
         self.__interact()
         
         self.attachThread(mainThread=True)
-        self.mainThread.tracePython = tracePython
+        self.mainThread.tracePythonLibs(tracePython)
         
         # set the system exception handling function to ensure, that
         # we report on all unhandled exceptions

eric ide

mercurial