def set_trace_to_threads(tracing_func): lib = load_python_helper_lib() if lib is None: # This is the case if it's not CPython. return -1 if hasattr(sys, 'getswitchinterval'): get_interval, set_interval = sys.getswitchinterval, sys.setswitchinterval else: get_interval, set_interval = sys.getcheckinterval, sys.setcheckinterval prev_value = get_interval() ret = 0 try: if not IS_PY37_OR_GREATER: # Prevent going to any other thread... if we switch the thread during this operation we # could potentially corrupt the interpreter. # Note: on CPython 3.7 onwards this is not needed (we have a different implementation # for setting the tracing for other threads in this case). set_interval(2 ** 15) set_trace_func = TracingFunctionHolder._original_tracing or sys.settrace # Note: use sys._current_frames() keys to get the thread ids because it'll return # thread ids created in C/C++ where there's user code running, unlike the APIs # from the threading module which see only threads created through it (unless # a call for threading.current_thread() was previously done in that thread, # in which case a dummy thread would've been created for it). thread_idents = set(sys._current_frames().keys()) thread_idents = thread_idents.difference( # Ignore pydevd threads. set(t.ident for t in threading.enumerate() if getattr(t, 'pydev_do_not_trace', False)) ) curr_ident = thread.get_ident() curr_thread = threading._active.get(curr_ident) for thread_ident in thread_idents: # If that thread is not available in the threading module we also need to create a # dummy thread for it (otherwise it'll be invisible to the debugger). if thread_ident not in threading._active: class _DummyThread(threading._DummyThread): def _set_ident(self): # Note: Hack to set the thread ident that we want. if IS_PY2: self._Thread__ident = thread_ident else: self._ident = thread_ident t = _DummyThread() # Reset to the base class (don't expose our own version of the class). t.__class__ = threading._DummyThread with threading._active_limbo_lock: # On Py2 it'll put in active getting the current indent, not using the # ident that was set, so, we have to update it (should be harmless on Py3 # so, do it always). threading._active[thread_ident] = t threading._active[curr_ident] = curr_thread if t.ident != thread_ident: # Check if it actually worked. pydev_log.error('pydevd: creation of _DummyThread with fixed thread ident did not succeed.') # Some (ptvsd) tests failed because of this, so, leave it always disabled for now. # show_debug_info = 1 if DebugInfoHolder.DEBUG_TRACE_LEVEL >= 1 else 0 show_debug_info = 0 if IS_PY37_OR_GREATER: # Hack to increase _Py_TracingPossible. # See comments on py_settrace_37.hpp proceed = thread.allocate_lock() proceed.acquire() def dummy_trace_on_py37(frame, event, arg): return dummy_trace_on_py37 def increase_tracing_count_on_py37(): SetTrace(dummy_trace_on_py37) proceed.release() start_new_thread = pydev_monkey.get_original_start_new_thread(thread) start_new_thread(increase_tracing_count_on_py37, ()) proceed.acquire() # Only proceed after the release() is done. proceed = None result = lib.AttachDebuggerTracing( ctypes.c_int(show_debug_info), ctypes.py_object(set_trace_func), ctypes.py_object(tracing_func), ctypes.c_uint(thread_ident), ctypes.py_object(None), ) if result != 0: pydev_log.info('Unable to set tracing for existing threads. Result: %s' % result) ret = result finally: if not IS_PY37_OR_GREATER: set_interval(prev_value) return ret
def set_trace_to_threads(tracing_func): lib = load_python_helper_lib() if lib is None: # This is the case if it's not CPython. pydev_log.info( 'Unable to load helper lib to set tracing to all threads (unsupported python vm).' ) return -1 pydev_log.info( 'Successfully Loaded helper lib to set tracing to all threads.') ret = 0 set_trace_func = TracingFunctionHolder._original_tracing or sys.settrace # Note: use sys._current_frames() keys to get the thread ids because it'll return # thread ids created in C/C++ where there's user code running, unlike the APIs # from the threading module which see only threads created through it (unless # a call for threading.current_thread() was previously done in that thread, # in which case a dummy thread would've been created for it). thread_idents = set(sys._current_frames().keys()) thread_idents = thread_idents.difference( # Ignore pydevd threads. set(t.ident for t in threading.enumerate() if getattr(t, 'pydev_do_not_trace', False))) curr_ident = thread.get_ident() curr_thread = threading._active.get(curr_ident) for thread_ident in thread_idents: # If that thread is not available in the threading module we also need to create a # dummy thread for it (otherwise it'll be invisible to the debugger). if thread_ident not in threading._active: class _DummyThread(threading._DummyThread): def _set_ident(self): # Note: Hack to set the thread ident that we want. if IS_PY2: self._Thread__ident = thread_ident else: self._ident = thread_ident t = _DummyThread() # Reset to the base class (don't expose our own version of the class). t.__class__ = threading._DummyThread with threading._active_limbo_lock: # On Py2 it'll put in active getting the current indent, not using the # ident that was set, so, we have to update it (should be harmless on Py3 # so, do it always). threading._active[thread_ident] = t threading._active[curr_ident] = curr_thread if t.ident != thread_ident: # Check if it actually worked. pydev_log.critical( 'pydevd: creation of _DummyThread with fixed thread ident did not succeed.' ) # Some (ptvsd) tests failed because of this, so, leave it always disabled for now. # show_debug_info = 1 if DebugInfoHolder.DEBUG_TRACE_LEVEL >= 1 else 0 show_debug_info = 0 # Hack to increase _Py_TracingPossible. # See comments on py_custom_pyeval_settrace.hpp proceed = thread.allocate_lock() proceed.acquire() def dummy_trace(frame, event, arg): return dummy_trace def increase_tracing_count(): SetTrace(dummy_trace) proceed.release() start_new_thread = pydev_monkey.get_original_start_new_thread(thread) start_new_thread(increase_tracing_count, ()) proceed.acquire() # Only proceed after the release() is done. proceed = None result = lib.AttachDebuggerTracing( ctypes.c_int(show_debug_info), ctypes.py_object(set_trace_func), ctypes.py_object(tracing_func), ctypes.c_uint(thread_ident), ctypes.py_object(None), ) if result != 0: pydev_log.info( 'Unable to set tracing for existing thread. Result: %s', result) ret = result return ret
def set_trace_to_threads(tracing_func, thread_idents=None, create_dummy_thread=True): assert tracing_func is not None ret = 0 # Note: use sys._current_frames() keys to get the thread ids because it'll return # thread ids created in C/C++ where there's user code running, unlike the APIs # from the threading module which see only threads created through it (unless # a call for threading.current_thread() was previously done in that thread, # in which case a dummy thread would've been created for it). if thread_idents is None: thread_idents = set(sys._current_frames().keys()) for t in threading.enumerate(): # PY-44778: ignore pydevd threads and also add any thread that wasn't found on # sys._current_frames() as some existing threads may not appear in # sys._current_frames() but may be available through the `threading` module. if getattr(t, 'pydev_do_not_trace', False): thread_idents.discard(t.ident) else: thread_idents.add(t.ident) curr_ident = thread.get_ident() curr_thread = threading._active.get(curr_ident) if curr_ident in thread_idents and len(thread_idents) != 1: # The current thread must be updated first (because we need to set # the reference to `curr_thread`). thread_idents = list(thread_idents) thread_idents.remove(curr_ident) thread_idents.insert(0, curr_ident) for thread_ident in thread_idents: # If that thread is not available in the threading module we also need to create a # dummy thread for it (otherwise it'll be invisible to the debugger). if create_dummy_thread: if thread_ident not in threading._active: class _DummyThread(threading._DummyThread): def _set_ident(self): # Note: Hack to set the thread ident that we want. if IS_PY2: self._Thread__ident = thread_ident else: self._ident = thread_ident t = _DummyThread() # Reset to the base class (don't expose our own version of the class). t.__class__ = threading._DummyThread if thread_ident == curr_ident: curr_thread = t with threading._active_limbo_lock: # On Py2 it'll put in active getting the current indent, not using the # ident that was set, so, we have to update it (should be harmless on Py3 # so, do it always). threading._active[thread_ident] = t threading._active[curr_ident] = curr_thread if t.ident != thread_ident: # Check if it actually worked. pydev_log.critical( 'pydevd: creation of _DummyThread with fixed thread ident did not succeed.' ) # Some (ptvsd) tests failed because of this, so, leave it always disabled for now. # show_debug_info = 1 if DebugInfoHolder.DEBUG_TRACE_LEVEL >= 1 else 0 show_debug_info = 0 # Hack to increase _Py_TracingPossible. # See comments on py_custom_pyeval_settrace.hpp proceed = thread.allocate_lock() proceed.acquire() def dummy_trace(frame, event, arg): return dummy_trace def increase_tracing_count(): set_trace = TracingFunctionHolder._original_tracing or sys.settrace set_trace(dummy_trace) proceed.release() start_new_thread = pydev_monkey.get_original_start_new_thread(thread) start_new_thread(increase_tracing_count, ()) proceed.acquire() # Only proceed after the release() is done. proceed = None # Note: The set_trace_func is not really used anymore in the C side. set_trace_func = TracingFunctionHolder._original_tracing or sys.settrace lib = _load_python_helper_lib() if lib is None: # This is the case if it's not CPython. pydev_log.info( 'Unable to load helper lib to set tracing to all threads (unsupported python vm).' ) ret = -1 else: try: result = lib.AttachDebuggerTracing( ctypes.c_int(show_debug_info), ctypes.py_object(set_trace_func), ctypes.py_object(tracing_func), ctypes.c_uint(thread_ident), ctypes.py_object(None), ) except: if DebugInfoHolder.DEBUG_TRACE_LEVEL >= 1: # There is no need to show this unless debug tracing is enabled. pydev_log.exception('Error attaching debugger tracing') ret = -1 else: if result != 0: pydev_log.info( 'Unable to set tracing for existing thread. Result: %s', result) ret = result return ret