diff options
Diffstat (limited to 'python/helpers/pydev/pydevd.py')
-rw-r--r-- | python/helpers/pydev/pydevd.py | 240 |
1 files changed, 149 insertions, 91 deletions
diff --git a/python/helpers/pydev/pydevd.py b/python/helpers/pydev/pydevd.py index 1733c26b5e91..9d0da096c07d 100644 --- a/python/helpers/pydev/pydevd.py +++ b/python/helpers/pydev/pydevd.py @@ -1,10 +1,13 @@ #IMPORTANT: pydevd_constants must be the 1st thing defined because it'll keep a reference to the original sys._getframe from __future__ import nested_scopes # Jython 2.1 support +from pydevd_constants import * # @UnusedWildImport + +import pydev_monkey_qt +pydev_monkey_qt.patch_qt() import traceback from django_debug import DjangoLineBreakpoint -from pydevd_signature import SignatureFactory from pydevd_frame import add_exception_to_frame import pydev_imports from pydevd_breakpoints import * #@UnusedWildImport @@ -59,7 +62,8 @@ from pydevd_comm import CMD_CHANGE_VARIABLE, \ StartServer, \ InternalSetNextStatementThread, \ ReloadCodeCommand, \ - CMD_SET_PY_EXCEPTION, \ + ID_TO_MEANING,\ + CMD_SET_PY_EXCEPTION, \ CMD_IGNORE_THROWN_EXCEPTION_AT,\ InternalGetBreakpointException, \ InternalSendCurrExceptionTrace,\ @@ -81,14 +85,12 @@ from pydevd_custom_frames import CustomFramesContainer, CustomFramesContainerIni import pydevd_dont_trace import pydevd_traceproperty -from _pydev_imps import _pydev_time as time +from _pydev_imps import _pydev_time as time, _pydev_thread -if USE_LIB_COPY: - import _pydev_threading as threading -else: - import threading +import _pydev_threading as threading import os +import atexit threadingEnumerate = threading.enumerate @@ -112,9 +114,11 @@ DONT_TRACE = { '_pydev_execfile.py':1, '_pydev_jython_execfile.py':1, '_pydev_threading':1, + '_pydev_Queue':1, 'django_debug.py':1, 'django_frame.py':1, 'pydev_log.py':1, + 'pydev_monkey.py':1 , 'pydevd.py':1 , 'pydevd_additional_thread_info.py':1, 'pydevd_comm.py':1, @@ -219,7 +223,7 @@ class PyDBCommandThread(PyDBDaemonThread): def killAllPydevThreads(): - threads = threadingEnumerate() + threads = DictKeys(PyDBDaemonThread.created_pydb_daemon_threads) for t in threads: if hasattr(t, 'doKillPydevThread'): t.doKillPydevThread() @@ -233,12 +237,23 @@ class PyDBCheckAliveThread(PyDBDaemonThread): def __init__(self, pyDb): PyDBDaemonThread.__init__(self) self.pyDb = pyDb - self.setDaemon(False) self.setName('pydevd.CheckAliveThread') def OnRun(self): if self.dontTraceMe: - self.pyDb.SetTrace(None) # no debugging on this thread + + disable_tracing = True + + if pydevd_vm_type.GetVmType() == pydevd_vm_type.PydevdVmType.JYTHON and sys.hexversion <= 0x020201f0: + # don't run untraced threads if we're in jython 2.2.1 or lower + # jython bug: if we start a thread and another thread changes the tracing facility + # it affects other threads (it's not set only for the thread but globally) + # Bug: http://sourceforge.net/tracker/index.php?func=detail&aid=1870039&group_id=12867&atid=112867 + disable_tracing = False + + if disable_tracing: + pydevd_tracing.SetTrace(None) # no debugging on this thread + while not self.killReceived: if not self.pyDb.haveAliveThreads(): try: @@ -298,8 +313,8 @@ class PyDB: self.django_exception_break = {} self.readyToRun = False - self._main_lock = threading.Lock() - self._lock_running_thread_ids = threading.Lock() + self._main_lock = _pydev_thread.allocate_lock() + self._lock_running_thread_ids = _pydev_thread.allocate_lock() self._py_db_command_thread_event = threading.Event() CustomFramesContainer._py_db_command_thread_event = self._py_db_command_thread_event self._finishDebuggingSession = False @@ -332,7 +347,17 @@ class PyDB: def haveAliveThreads(self): for t in threadingEnumerate(): - if not isinstance(t, PyDBDaemonThread) and isThreadAlive(t) and not t.isDaemon(): + if isinstance(t, PyDBDaemonThread): + pydev_log.error_once( + 'Error in debugger: Found PyDBDaemonThread through threading.enumerate().\n') + + if getattr(t, 'is_pydev_daemon_thread', False): + #Important: Jython 2.5rc4 has a bug where a thread created with thread.start_new_thread won't be + #set as a daemon thread, so, we also have to check for the 'is_pydev_daemon_thread' flag. + #See: https://github.com/fabioz/PyDev.Debugger/issues/11 + continue + + if isThreadAlive(t) and not t.isDaemon(): return True return False @@ -387,11 +412,9 @@ class PyDB: if thread_id == "*": threads = threadingEnumerate() for t in threads: - thread_name = t.getName() - if not thread_name.startswith('pydevd.') or thread_name == 'pydevd.CommandThread': - thread_id = GetThreadId(t) - queue = self.getInternalQueue(thread_id) - queue.put(int_cmd) + thread_id = GetThreadId(t) + queue = self.getInternalQueue(thread_id) + queue.put(int_cmd) else: queue = self.getInternalQueue(thread_id) @@ -442,7 +465,13 @@ class PyDB: for t in all_threads: thread_id = GetThreadId(t) - if not isinstance(t, PyDBDaemonThread) and isThreadAlive(t): + if isinstance(t, PyDBDaemonThread): + pydev_log.error_once('Found PyDBDaemonThread in threading.enumerate.') + + elif getattr(t, 'is_pydev_daemon_thread', False): + pass # I.e.: skip the DummyThreads created from pydev daemon threads + + elif isThreadAlive(t): program_threads_alive[thread_id] = t if not DictContains(self._running_thread_ids, thread_id): @@ -505,19 +534,18 @@ class PyDB: threads = threadingEnumerate() try: for t in threads: - if not t.getName().startswith('pydevd.'): - # TODO: optimize so that we only actually add that tracing if it's in - # the new breakpoint context. - additionalInfo = None - try: - additionalInfo = t.additionalInfo - except AttributeError: - pass # that's ok, no info currently set - - if additionalInfo is not None: - for frame in additionalInfo.IterFrames(): - if frame is not ignore_frame: - self.SetTraceForFrameAndParents(frame, overwrite_prev_trace=overwrite_prev_trace) + # TODO: optimize so that we only actually add that tracing if it's in + # the new breakpoint context. + additionalInfo = None + try: + additionalInfo = t.additionalInfo + except AttributeError: + pass # that's ok, no info currently set + + if additionalInfo is not None: + for frame in additionalInfo.IterFrames(): + if frame is not ignore_frame: + self.SetTraceForFrameAndParents(frame, overwrite_prev_trace=overwrite_prev_trace) finally: frame = None t = None @@ -592,7 +620,7 @@ class PyDB: it may be worth refactoring it (actually, reordering the ifs so that the ones used mostly come before probably will give better performance). ''' - #print ID_TO_MEANING[str(cmd_id)], repr(text) + #print(ID_TO_MEANING[str(cmd_id)], repr(text)) self._main_lock.acquire() try: @@ -834,7 +862,7 @@ class PyDB: id_to_pybreakpoint[breakpoint_id] = breakpoint self.consolidate_breakpoints(file, id_to_pybreakpoint, breakpoints) - self.setTracingForUntracedContexts() + self.setTracingForUntracedContexts(overwrite_prev_trace=True) elif cmd_id == CMD_REMOVE_BREAK: #command to remove some breakpoint @@ -862,7 +890,7 @@ class PyDB: raise NameError(breakpoint_type) try: - id_to_pybreakpoint = file_to_id_to_breakpoint[file] + id_to_pybreakpoint = file_to_id_to_breakpoint.get(file, {}) if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0: existing = id_to_pybreakpoint[breakpoint_id] sys.stderr.write('Removed breakpoint:%s - line:%s - func_name:%s (id: %s)\n' % ( @@ -1319,7 +1347,7 @@ class PyDB: if self._finishDebuggingSession and not self._terminationEventSent: #that was not working very well because jython gave some socket errors try: - threads = threadingEnumerate() + threads = DictKeys(PyDBDaemonThread.created_pydb_daemon_threads) for t in threads: if hasattr(t, 'doKillPydevThread'): t.doKillPydevThread() @@ -1332,10 +1360,10 @@ class PyDB: is_file_to_ignore = DictContains(DONT_TRACE, base) #we don't want to debug threading or anything related to pydevd + #print('trace_dispatch', base, frame.f_lineno, event, frame.f_code.co_name, is_file_to_ignore) if is_file_to_ignore: return None - #print('trace_dispatch', base, frame.f_lineno, event, frame.f_code.co_name) try: #this shouldn't give an exception, but it could happen... (python bug) #see http://mail.python.org/pipermail/python-bugs-list/2007-June/038796.html @@ -1378,9 +1406,14 @@ class PyDB: except Exception: # Log it - if traceback is not None: - # This can actually happen during the interpreter shutdown in Python 2.7 - traceback.print_exc() + try: + if traceback is not None: + # This can actually happen during the interpreter shutdown in Python 2.7 + traceback.print_exc() + except: + # Error logging? We're really in the interpreter shutdown... + # (https://github.com/fabioz/PyDev.Debugger/issues/8) + pass return None if USE_PSYCO_OPTIMIZATION: @@ -1401,8 +1434,9 @@ class PyDB: - def SetTraceForFrameAndParents(self, frame, also_add_to_passed_frame=True, overwrite_prev_trace=False): - dispatch_func = self.trace_dispatch + def SetTraceForFrameAndParents(self, frame, also_add_to_passed_frame=True, overwrite_prev_trace=False, dispatch_func=None): + if dispatch_func is None: + dispatch_func = self.trace_dispatch if also_add_to_passed_frame: self.update_trace(frame, dispatch_func, overwrite_prev_trace) @@ -1432,15 +1466,8 @@ class PyDB: def prepareToRun(self): ''' Shared code to prepare debugging by installing traces and registering threads ''' - - # for completeness, we'll register the pydevd.reader & pydevd.writer threads - net = NetCommand(str(CMD_THREAD_CREATE), 0, '<xml><thread name="pydevd.reader" id="-1"/></xml>') - self.writer.addCommand(net) - net = NetCommand(str(CMD_THREAD_CREATE), 0, '<xml><thread name="pydevd.writer" id="-1"/></xml>') - self.writer.addCommand(net) - - pydevd_tracing.SetTrace(self.trace_dispatch) self.patch_threads() + pydevd_tracing.SetTrace(self.trace_dispatch) PyDBCommandThread(self).start() @@ -1519,6 +1546,8 @@ class PyDB: pydev_imports.execfile(file, globals, locals) # execute the script + return globals + def exiting(self): sys.stdout.flush() sys.stderr.flush() @@ -1526,6 +1555,22 @@ class PyDB: cmd = self.cmdFactory.makeExitMessage() self.writer.addCommand(cmd) + def wait_for_commands(self, globals): + thread = threading.currentThread() + import pydevd_frame_utils + frame = pydevd_frame_utils.Frame(None, -1, pydevd_frame_utils.FCode("Console", + os.path.abspath(os.path.dirname(__file__))), globals, globals) + thread_id = GetThreadId(thread) + import pydevd_vars + pydevd_vars.addAdditionalFrameById(thread_id, {id(frame): frame}) + + cmd = self.cmdFactory.makeShowConsoleMessage(thread_id, frame) + self.writer.addCommand(cmd) + + while True: + self.processInternalCommands() + time.sleep(0.01) + def set_debug(setup): setup['DEBUG_RECORD_SOCKET_READS'] = True setup['DEBUG_TRACE_BREAKPOINTS'] = 1 @@ -1543,43 +1588,51 @@ def processCommandLine(argv): setup['multiproc'] = False #Used by PyCharm (reuses connection: ssh tunneling) setup['multiprocess'] = False # Used by PyDev (creates new connection to ide) setup['save-signatures'] = False + setup['print-in-debugger-startup'] = False + setup['cmd-line'] = False i = 0 del argv[0] while (i < len(argv)): - if (argv[i] == '--port'): + if argv[i] == '--port': del argv[i] setup['port'] = int(argv[i]) del argv[i] - elif (argv[i] == '--vm_type'): + elif argv[i] == '--vm_type': del argv[i] setup['vm_type'] = argv[i] del argv[i] - elif (argv[i] == '--client'): + elif argv[i] == '--client': del argv[i] setup['client'] = argv[i] del argv[i] - elif (argv[i] == '--server'): + elif argv[i] == '--server': del argv[i] setup['server'] = True - elif (argv[i] == '--file'): + elif argv[i] == '--file': del argv[i] setup['file'] = argv[i] i = len(argv) # pop out, file is our last argument - elif (argv[i] == '--DEBUG_RECORD_SOCKET_READS'): + elif argv[i] == '--DEBUG_RECORD_SOCKET_READS': del argv[i] setup['DEBUG_RECORD_SOCKET_READS'] = True - elif (argv[i] == '--DEBUG'): + elif argv[i] == '--DEBUG': del argv[i] set_debug(setup) - elif (argv[i] == '--multiproc'): + elif argv[i] == '--multiproc': del argv[i] setup['multiproc'] = True - elif (argv[i] == '--multiprocess'): + elif argv[i] == '--multiprocess': del argv[i] setup['multiprocess'] = True - elif (argv[i] == '--save-signatures'): + elif argv[i] == '--save-signatures': del argv[i] setup['save-signatures'] = True + elif argv[i] == '--print-in-debugger-startup': + del argv[i] + setup['print-in-debugger-startup'] = True + elif (argv[i] == '--cmd-line'): + del argv[i] + setup['cmd-line'] = True else: raise ValueError("unexpected option " + argv[i]) return setup @@ -1590,18 +1643,6 @@ def usage(doExit=0): if doExit: sys.exit(0) -def SetTraceForParents(frame, dispatch_func): - frame = frame.f_back - while frame: - if frame.f_trace is None: - frame.f_trace = dispatch_func - - frame = frame.f_back - del frame - -def exit_hook(): - debugger = GetGlobalDebugger() - debugger.exiting() def initStdoutRedirect(): if not getattr(sys, 'stdoutBuf', None): @@ -1666,7 +1707,7 @@ def settrace( -_set_trace_lock = threading.Lock() +_set_trace_lock = _pydev_thread.allocate_lock() def _locked_settrace( host, @@ -1705,11 +1746,6 @@ def _locked_settrace( bufferStdOutToServer = stdoutToServer bufferStdErrToServer = stderrToServer - net = NetCommand(str(CMD_THREAD_CREATE), 0, '<xml><thread name="pydevd.reader" id="-1"/></xml>') - debugger.writer.addCommand(net) - net = NetCommand(str(CMD_THREAD_CREATE), 0, '<xml><thread name="pydevd.writer" id="-1"/></xml>') - debugger.writer.addCommand(net) - if bufferStdOutToServer: initStdoutRedirect() @@ -1748,7 +1784,9 @@ def _locked_settrace( # As this is the first connection, also set tracing for any untraced threads debugger.setTracingForUntracedContexts(ignore_frame=GetFrame(), overwrite_prev_trace=overwrite_prev_trace) - sys.exitfunc = exit_hook + # Stop the tracing as the last thing before the actual shutdown for a clean exit. + atexit.register(stoptrace) + #Suspend as the last thing after all tracing is in place. if suspend: debugger.setSuspend(t, CMD_SET_BREAK) @@ -1793,16 +1831,15 @@ def stoptrace(): from pydev_monkey import undo_patch_thread_modules undo_patch_thread_modules() - + debugger = GetGlobalDebugger() - + if debugger: - debugger.trace_dispatch = None - - debugger.SetTraceForFrameAndParents(GetFrame(), False) - + + debugger.SetTraceForFrameAndParents( + GetFrame(), also_add_to_passed_frame=True, overwrite_prev_trace=True, dispatch_func=lambda *args:None) debugger.exiting() - + killAllPydevThreads() connected = False @@ -1830,6 +1867,11 @@ class DispatchReader(ReaderThread): self.dispatcher = dispatcher ReaderThread.__init__(self, self.dispatcher.client) + def OnRun(self): + dummy_thread = threading.currentThread() + dummy_thread.is_pydev_daemon_thread = False + return ReaderThread.OnRun(self) + def handleExcept(self): ReaderThread.handleExcept(self) @@ -1893,6 +1935,7 @@ class SetupHolder: # main #======================================================================================================================= if __name__ == '__main__': + # parse the command line. --file is our last argument that is required try: sys.original_argv = sys.argv[:] @@ -1902,6 +1945,12 @@ if __name__ == '__main__': traceback.print_exc() usage(1) + if setup['print-in-debugger-startup']: + try: + pid = ' (pid: %s)' % os.getpid() + except: + pid = '' + sys.stderr.write("pydev debugger: starting%s\n" % pid) fix_getpass.fixGetpass() @@ -2010,6 +2059,12 @@ if __name__ == '__main__': except: pass # It's ok not having stackless there... + debugger = PyDB() + + if setup['cmd-line']: + debugger.cmd_line = True + + if fix_app_engine_debug: sys.stderr.write("pydev debugger: google app engine integration enabled\n") curr_dir = os.path.dirname(__file__) @@ -2022,10 +2077,8 @@ if __name__ == '__main__': sys.argv.insert(3, '--automatic_restart=no') sys.argv.insert(4, '--max_module_instances=1') - debugger = PyDB() # Run the dev_appserver debugger.run(setup['file'], None, None, set_trace=False) - else: # as to get here all our imports are already resolved, the psyco module can be # changed and we'll still get the speedups in the debugger, as those functions @@ -2041,12 +2094,12 @@ if __name__ == '__main__': import pydevd_psyco_stub sys.modules['psyco'] = pydevd_psyco_stub - debugger = PyDB() - if setup['save-signatures']: if pydevd_vm_type.GetVmType() == pydevd_vm_type.PydevdVmType.JYTHON: sys.stderr.write("Collecting run-time type information is not supported for Jython\n") else: + # Only import it if we're going to use it! + from pydevd_signature import SignatureFactory debugger.signature_factory = SignatureFactory() try: @@ -2058,4 +2111,9 @@ if __name__ == '__main__': connected = True # Mark that we're connected when started from inside ide. - debugger.run(setup['file'], None, None) + globals = debugger.run(setup['file'], None, None) + + if setup['cmd-line']: + debugger.wait_for_commands(globals) + + |