Skip to content

Commit

Permalink
Attach to pid fixes with threading. Fixes microsoft#1542,microsoft#1520
Browse files Browse the repository at this point in the history
  • Loading branch information
fabioz committed Jul 12, 2019
1 parent 6283741 commit e0b2c5e
Show file tree
Hide file tree
Showing 23 changed files with 1,135 additions and 667 deletions.
6 changes: 6 additions & 0 deletions .travis.yml
Expand Up @@ -9,6 +9,11 @@ python:
- "3.6"
- "3.7"

addons:
apt:
packages:
- gdb

stages:
- lint
- test
Expand All @@ -23,6 +28,7 @@ jobs:
- flake8

install:
- sudo sysctl kernel.yama.ptrace_scope=0
- pip install -U pip setuptools tox tox-travis

script:
Expand Down
14 changes: 12 additions & 2 deletions src/ptvsd/__main__.py
Expand Up @@ -376,22 +376,32 @@ def attach_to_pid():
show_debug_info_on_target_process = 0 # hard-coded (1 to debug)

ptvsd_dirname = os.path.dirname(os.path.dirname(__file__))
attach_script_ptvsd_pid_dirname = os.path.join(ptvsd_dirname, 'ptvsd')
assert os.path.exists(ptvsd_dirname)
assert os.path.exists(attach_script_ptvsd_pid_dirname)
log_dir = log_dir.replace('\\', '/')
setup = {'host': host, 'port': port, 'client': client, 'log_dir': log_dir, 'pid': pid}

if sys.platform == 'win32':
setup['pythonpath'] = ptvsd_dirname.replace('\\', '/')

# We need to be able to import attach_script_ptvsd_pid without importing ptvsd first.
setup['pythonpath2'] = attach_script_ptvsd_pid_dirname.replace('\\', '/')

python_code = '''import sys;
sys.path.append("%(pythonpath)s");
from ptvsd import attach_script_ptvsd_pid;
sys.path.append("%(pythonpath2)s");
import attach_script_ptvsd_pid;
attach_script_ptvsd_pid.attach(port=%(port)s, host="%(host)s", client=%(client)s, log_dir="%(log_dir)s");
'''.replace('\r\n', '').replace('\r', '').replace('\n', '')
else:
setup['pythonpath'] = ptvsd_dirname
setup['pythonpath2'] = attach_script_ptvsd_pid_dirname
# We have to pass it a bit differently for gdb
python_code = '''import sys;
sys.path.append(\\\"%(pythonpath)s\\\");
from ptvsd import attach_script_ptvsd_pid;
sys.path.append(\\\"%(pythonpath2)s\\\");
import attach_script_ptvsd_pid;
attach_script_ptvsd_pid.attach(port=%(port)s, host=\\\"%(host)s\\\", client=%(client)s, log_dir=\\\"%(log_dir)s\\\");
'''.replace('\r\n', '').replace('\r', '').replace('\n', '')

Expand Down
6 changes: 6 additions & 0 deletions src/ptvsd/_vendored/pydevd/.travis.yml
Expand Up @@ -3,6 +3,11 @@ language: python
services:
- xvfb

addons:
apt:
packages:
- gdb

matrix:
include:
# Note: python is always 2.7 because it's the installed version
Expand Down Expand Up @@ -92,6 +97,7 @@ before_install:
- "export DISPLAY=:99.0"
# Install packages
install:
- sudo sysctl kernel.yama.ptrace_scope=0
# Both
- export PYTHONPATH=.
# Python setup
Expand Down
73 changes: 44 additions & 29 deletions src/ptvsd/_vendored/pydevd/pydevd.py
Expand Up @@ -771,31 +771,43 @@ def on_breakpoints_changed(self, removed=False):
# we have to reset the tracing for the existing functions to be re-evaluated.
self.set_tracing_for_untraced_contexts()

def set_tracing_for_untraced_contexts(self, ignore_current_thread=False):
def set_tracing_for_untraced_contexts(self):
# Enable the tracing for existing threads (because there may be frames being executed that
# are currently untraced).
ignore_thread = None
if ignore_current_thread:
ignore_thread = threading.current_thread()

threads = threadingEnumerate()
try:
for t in threads:
if getattr(t, 'is_pydev_daemon_thread', False) or t is ignore_thread or getattr(t, 'pydev_do_not_trace', False):
continue
if IS_CPYTHON:
# Note: use sys._current_frames instead of threading.enumerate() because this way
# we also see C/C++ threads, not only the ones visible to the threading module.
tid_to_frame = sys._current_frames()

additional_info = set_additional_thread_info(t)
frame = additional_info.get_topmost_frame(t)
try:
if frame is not None:
self.set_trace_for_frame_and_parents(frame)
finally:
frame = None
finally:
frame = None
t = None
threads = None
additional_info = None
ignore_thread_ids = set(
t.ident for t in threadingEnumerate()
if getattr(t, 'is_pydev_daemon_thread', False) or getattr(t, 'pydev_do_not_trace', False)
)

for thread_id, frame in tid_to_frame.items():
if thread_id not in ignore_thread_ids:
self.set_trace_for_frame_and_parents(frame)

else:
try:
threads = threadingEnumerate()
for t in threads:
if getattr(t, 'is_pydev_daemon_thread', False) or getattr(t, 'pydev_do_not_trace', False):
continue

additional_info = set_additional_thread_info(t)
frame = additional_info.get_topmost_frame(t)
try:
if frame is not None:
self.set_trace_for_frame_and_parents(frame)
finally:
frame = None
finally:
frame = None
t = None
threads = None
additional_info = None

@property
def multi_threads_single_notification(self):
Expand Down Expand Up @@ -1737,11 +1749,15 @@ def set_trace_for_frame_and_parents(self, frame, **kwargs):

if file_type is None:
if disable:
pydev_log.debug('Disable tracing of frame: %s - %s', frame.f_code.co_filename, frame.f_code.co_name)
if frame.f_trace is not None and frame.f_trace is not NO_FTRACE:
frame.f_trace = NO_FTRACE

elif frame.f_trace is not self.trace_dispatch:
pydev_log.debug('Set tracing of frame: %s - %s', frame.f_code.co_filename, frame.f_code.co_name)
frame.f_trace = self.trace_dispatch
else:
pydev_log.debug('SKIP set tracing of frame: %s - %s', frame.f_code.co_filename, frame.f_code.co_name)

frame = frame.f_back

Expand Down Expand Up @@ -2234,13 +2250,6 @@ def _locked_settrace(
while not debugger.ready_to_run:
time.sleep(0.1) # busy wait until we receive run command

# Set the tracing only
debugger.set_trace_for_frame_and_parents(get_frame().f_back)

with CustomFramesContainer.custom_frames_lock: # @UndefinedVariable
for _frameId, custom_frame in dict_iter_items(CustomFramesContainer.custom_frames):
debugger.set_trace_for_frame_and_parents(custom_frame.frame)

debugger.start_auxiliary_daemon_threads()

if trace_only_current_thread:
Expand All @@ -2252,7 +2261,13 @@ def _locked_settrace(
debugger.enable_tracing(debugger.trace_dispatch, apply_to_all_threads=True)

# As this is the first connection, also set tracing for any untraced threads
debugger.set_tracing_for_untraced_contexts(ignore_current_thread=True)
debugger.set_tracing_for_untraced_contexts()

debugger.set_trace_for_frame_and_parents(get_frame().f_back)

with CustomFramesContainer.custom_frames_lock: # @UndefinedVariable
for _frameId, custom_frame in dict_iter_items(CustomFramesContainer.custom_frames):
debugger.set_trace_for_frame_and_parents(custom_frame.frame)

# Stop the tracing as the last thing before the actual shutdown for a clean exit.
atexit.register(stoptrace)
Expand Down
Binary file not shown.
Binary file not shown.

0 comments on commit e0b2c5e

Please sign in to comment.