Skip to content

Commit 958af7e

Browse files
fabiozint19h
authored andcommitted
Don't crash if running under CPython debug build. Fixes #152
1 parent 63c0fae commit 958af7e

File tree

10 files changed

+143
-106
lines changed

10 files changed

+143
-106
lines changed

src/debugpy/_vendored/pydevd/.travis.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,12 @@ matrix:
5757
- PYDEVD_USE_CYTHON=NO
5858
- PYDEVD_TEST_VM=CPYTHON
5959

60-
# Python 3.7
60+
# Python 3.8 debug
6161
- python: 2.7
6262
env:
63-
- PYDEVD_PYTHON_VERSION=3.7
64-
- PYDEVD_USE_CYTHON=YES
65-
- PYDEVD_TEST_VM=CPYTHON
63+
- PYDEVD_PYTHON_VERSION=3.8
64+
- PYDEVD_TEST_VM=CPYTHON_DEBUG
65+
- PYDEVD_USE_CONDA=NO
6666

6767
- python: 3.8
6868
env:
@@ -124,6 +124,7 @@ install:
124124
# On local machine with jython: c:\bin\jython2.7.0\bin\jython.exe -Dpython.path=.;jython_test_deps/ant.jar;jython_test_deps/junit.jar -m pytest
125125
# On remove machine with python: c:\bin\python27\python.exe -m pytest
126126
script:
127+
- if [[ ("$PYDEVD_TEST_VM" == "CPYTHON_DEBUG") ]]; then ./.travis/install_and_run_debug_py.sh; fi
127128
- if [[ ("$PYDEVD_TEST_VM" == "CPYTHON") ]]; then ./.travis/run_python_pytest.sh; fi
128129
- if [ "$PYDEVD_TEST_VM" == "PYPY" ]; then source activate build_env; pypy3 -m pytest -n auto; fi
129130
- if [ "$PYDEVD_TEST_VM" == "JYTHON" ]; then jython -Dpython.path=.:jython_test_deps/ant.jar:jython_test_deps/junit.jar -m pytest --tb=native; fi
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Build the cython extensions (to check that we don't crash when they're there in debug mode).
2+
python setup_cython.py build_ext --inplace
3+
4+
curl -L https://www.python.org/ftp/python/3.8.3/Python-3.8.3.tgz -o Python-3.8.3.tgz
5+
tar -xzf Python-3.8.3.tgz
6+
cd Python-3.8.3
7+
mkdir debug
8+
cd debug
9+
../configure --with-pydebug
10+
make
11+
12+
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
13+
./python get-pip.py
14+
15+
./python -m pip install "pytest"
16+
./python -m pip install "psutil"
17+
./python -m pip install "untangle"
18+
19+
# Check that it worked.
20+
./python -c "import pytest"
21+
./python -c "import psutil"
22+
./python -c "import untangle"
23+
24+
cd ..
25+
cd ..
26+
ls -la
27+
28+
./Python-3.8.3/debug/python -c "import sys;assert hasattr(sys,'gettotalrefcount')"
29+
30+
cd tests_python
31+
32+
# Although we compiled cython, all we're checking is that we don't crash (since it was built for the release env).
33+
../Python-3.8.3/debug/python -m pytest test_debugger_json.py -k "test_case_json_change_breaks or test_remote_debugger_basic"
34+
export PYTHONPATH=..
35+
../Python-3.8.3/debug/python -c "import check_debug_python;check_debug_python.check() "
Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,19 @@
11
# Defines which version of the PyDBAdditionalThreadInfo we'll use.
2+
from _pydevd_bundle.pydevd_constants import ENV_FALSE_LOWER_VALUES, USE_CYTHON_FLAG, \
3+
ENV_TRUE_LOWER_VALUES
24

3-
import os
4-
use_cython = os.getenv('PYDEVD_USE_CYTHON', None)
5-
6-
if use_cython == 'YES':
5+
if USE_CYTHON_FLAG in ENV_TRUE_LOWER_VALUES:
76
# We must import the cython version if forcing cython
87
from _pydevd_bundle.pydevd_cython_wrapper import PyDBAdditionalThreadInfo, set_additional_thread_info, _set_additional_thread_info_lock # @UnusedImport
98

10-
elif use_cython == 'NO':
9+
elif USE_CYTHON_FLAG in ENV_FALSE_LOWER_VALUES:
1110
# Use the regular version if not forcing cython
1211
from _pydevd_bundle.pydevd_additional_thread_info_regular import PyDBAdditionalThreadInfo, set_additional_thread_info, _set_additional_thread_info_lock # @UnusedImport @Reimport
1312

14-
elif use_cython is None:
13+
else:
1514
# Regular: use fallback if not found (message is already given elsewhere).
1615
try:
1716
from _pydevd_bundle.pydevd_cython_wrapper import PyDBAdditionalThreadInfo, set_additional_thread_info, _set_additional_thread_info_lock
1817
except ImportError:
1918
from _pydevd_bundle.pydevd_additional_thread_info_regular import PyDBAdditionalThreadInfo, set_additional_thread_info, _set_additional_thread_info_lock # @UnusedImport
20-
else:
21-
raise RuntimeError('Unexpected value for PYDEVD_USE_CYTHON: %s (accepted: YES, NO)' % (use_cython,))
22-
2319

src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_constants.py

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,23 @@ def version_str(v):
173173
except AttributeError:
174174
PY_IMPL_NAME = ''
175175

176-
SUPPORT_GEVENT = os.getenv('GEVENT_SUPPORT', 'False') in ('True', 'true', '1')
176+
ENV_TRUE_LOWER_VALUES = ('yes', 'true', '1')
177+
ENV_FALSE_LOWER_VALUES = ('no', 'false', '0')
178+
179+
180+
def is_true_in_env(env_key):
181+
if isinstance(env_key, tuple):
182+
# If a tuple, return True if any of those ends up being true.
183+
for v in env_key:
184+
if is_true_in_env(v):
185+
return True
186+
return False
187+
else:
188+
return os.getenv(env_key, '').lower() in ENV_TRUE_LOWER_VALUES
189+
190+
191+
# If true in env, use gevent mode.
192+
SUPPORT_GEVENT = is_true_in_env('GEVENT_SUPPORT')
177193

178194
GEVENT_SUPPORT_NOT_SET_MSG = os.getenv(
179195
'GEVENT_SUPPORT_NOT_SET_MSG',
@@ -187,14 +203,32 @@ def version_str(v):
187203

188204
INTERACTIVE_MODE_AVAILABLE = sys.platform in ('darwin', 'win32') or os.getenv('DISPLAY') is not None
189205

190-
SHOW_COMPILE_CYTHON_COMMAND_LINE = os.getenv('PYDEVD_SHOW_COMPILE_CYTHON_COMMAND_LINE', 'False') == 'True'
206+
# If true in env, forces cython to be used (raises error if not available).
207+
# If false in env, disables it.
208+
# If not specified, uses default heuristic to determine if it should be loaded.
209+
USE_CYTHON_FLAG = os.getenv('PYDEVD_USE_CYTHON')
210+
211+
# Use to disable loading the lib to set tracing to all threads (default is using heuristics based on where we're running).
212+
LOAD_NATIVE_LIB_FLAG = os.getenv('PYDEVD_LOAD_NATIVE_LIB', '').lower()
213+
214+
if USE_CYTHON_FLAG is not None:
215+
USE_CYTHON_FLAG = USE_CYTHON_FLAG.lower()
216+
if USE_CYTHON_FLAG not in ENV_TRUE_LOWER_VALUES and USE_CYTHON_FLAG not in ENV_FALSE_LOWER_VALUES:
217+
raise RuntimeError('Unexpected value for PYDEVD_USE_CYTHON: %s (enable with one of: %s, disable with one of: %s)' % (
218+
USE_CYTHON_FLAG, ENV_TRUE_LOWER_VALUES, ENV_FALSE_LOWER_VALUES))
219+
220+
else:
221+
if not CYTHON_SUPPORTED:
222+
USE_CYTHON_FLAG = 'no'
223+
224+
SHOW_COMPILE_CYTHON_COMMAND_LINE = is_true_in_env('PYDEVD_SHOW_COMPILE_CYTHON_COMMAND_LINE')
191225

192-
LOAD_VALUES_ASYNC = os.getenv('PYDEVD_LOAD_VALUES_ASYNC', 'False') == 'True'
226+
LOAD_VALUES_ASYNC = is_true_in_env('PYDEVD_LOAD_VALUES_ASYNC')
193227
DEFAULT_VALUE = "__pydevd_value_async"
194228
ASYNC_EVAL_TIMEOUT_SEC = 60
195229
NEXT_VALUE_SEPARATOR = "__pydev_val__"
196230
BUILTINS_MODULE_NAME = '__builtin__' if IS_PY2 else 'builtins'
197-
SHOW_DEBUG_INFO_ENV = os.getenv('PYCHARM_DEBUG') == 'True' or os.getenv('PYDEV_DEBUG') == 'True' or os.getenv('PYDEVD_DEBUG') == 'True'
231+
SHOW_DEBUG_INFO_ENV = is_true_in_env(('PYCHARM_DEBUG', 'PYDEV_DEBUG', 'PYDEVD_DEBUG'))
198232

199233
if SHOW_DEBUG_INFO_ENV:
200234
# show debug info before the debugger start

src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_trace_dispatch.py

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,12 @@
22
# Should give warning only here if cython is not available but supported.
33

44
import os
5-
from _pydevd_bundle.pydevd_constants import CYTHON_SUPPORTED
5+
from _pydevd_bundle.pydevd_constants import USE_CYTHON_FLAG, ENV_TRUE_LOWER_VALUES, \
6+
ENV_FALSE_LOWER_VALUES
67
from _pydev_bundle import pydev_log
78

8-
use_cython = os.getenv('PYDEVD_USE_CYTHON', None)
99
dirname = os.path.dirname(os.path.dirname(__file__))
1010
USING_CYTHON = False
11-
# Do not show incorrect warning for .egg files for Remote debugger
12-
if not CYTHON_SUPPORTED or dirname.endswith('.egg'):
13-
# Do not try to import cython extensions if cython isn't supported
14-
use_cython = 'NO'
1511

1612

1713
def delete_old_compiled_extensions():
@@ -35,16 +31,16 @@ def delete_old_compiled_extensions():
3531
"\"%s\" and \"%s\"" % (_pydevd_bundle_ext_dir, _pydevd_frame_eval_ext_dir))
3632

3733

38-
if use_cython == 'YES':
34+
if USE_CYTHON_FLAG in ENV_TRUE_LOWER_VALUES:
3935
# We must import the cython version if forcing cython
4036
from _pydevd_bundle.pydevd_cython_wrapper import trace_dispatch, global_cache_skips, global_cache_frame_skips, fix_top_level_trace_and_get_trace_func
4137
USING_CYTHON = True
4238

43-
elif use_cython == 'NO':
39+
elif USE_CYTHON_FLAG in ENV_FALSE_LOWER_VALUES:
4440
# Use the regular version if not forcing cython
4541
from _pydevd_bundle.pydevd_trace_dispatch_regular import trace_dispatch, global_cache_skips, global_cache_frame_skips, fix_top_level_trace_and_get_trace_func # @UnusedImport
4642

47-
elif use_cython is None:
43+
else:
4844
# Regular: use fallback if not found and give message to user
4945
try:
5046
from _pydevd_bundle.pydevd_cython_wrapper import trace_dispatch, global_cache_skips, global_cache_frame_skips, fix_top_level_trace_and_get_trace_func
@@ -63,6 +59,4 @@ def delete_old_compiled_extensions():
6359
except ImportError:
6460
from _pydevd_bundle.pydevd_trace_dispatch_regular import trace_dispatch, global_cache_skips, global_cache_frame_skips, fix_top_level_trace_and_get_trace_func # @UnusedImport
6561
pydev_log.show_compile_cython_command_line()
66-
else:
67-
raise RuntimeError('Unexpected value for PYDEVD_USE_CYTHON: %s (accepted: YES, NO)' % (use_cython,))
6862

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,33 @@
11
import os
2-
import sys
32

43
from _pydev_bundle import pydev_log
54
from _pydevd_bundle.pydevd_trace_dispatch import USING_CYTHON
6-
7-
IS_PY36_OR_GREATER = sys.version_info >= (3, 6)
5+
from _pydevd_bundle.pydevd_constants import USE_CYTHON_FLAG, ENV_FALSE_LOWER_VALUES, \
6+
ENV_TRUE_LOWER_VALUES, IS_PY36_OR_GREATER
87

98
frame_eval_func = None
109
stop_frame_eval = None
1110
dummy_trace_dispatch = None
1211
clear_thread_local_info = None
1312

14-
use_cython = os.getenv('PYDEVD_USE_CYTHON', None)
1513
USING_FRAME_EVAL = False
1614

1715
# "NO" means we should not use frame evaluation, 'YES' we should use it (and fail if not there) and unspecified uses if possible.
18-
use_frame_eval = os.environ.get('PYDEVD_USE_FRAME_EVAL', None)
16+
use_frame_eval = os.environ.get('PYDEVD_USE_FRAME_EVAL', '').lower()
1917

20-
if use_frame_eval == 'NO' or use_cython == 'NO' or not USING_CYTHON:
18+
if use_frame_eval in ENV_FALSE_LOWER_VALUES or USE_CYTHON_FLAG in ENV_FALSE_LOWER_VALUES or not USING_CYTHON:
2119
pass
2220

23-
elif use_frame_eval == 'YES':
21+
elif use_frame_eval in ENV_TRUE_LOWER_VALUES:
2422
# Fail if unable to use
2523
from _pydevd_frame_eval.pydevd_frame_eval_cython_wrapper import frame_eval_func, stop_frame_eval, dummy_trace_dispatch, clear_thread_local_info
2624
USING_FRAME_EVAL = True
2725

28-
elif use_frame_eval is None:
26+
else:
2927
# Try to use if possible
3028
if IS_PY36_OR_GREATER:
3129
try:
3230
from _pydevd_frame_eval.pydevd_frame_eval_cython_wrapper import frame_eval_func, stop_frame_eval, dummy_trace_dispatch, clear_thread_local_info
3331
USING_FRAME_EVAL = True
3432
except ImportError:
3533
pydev_log.show_compile_cython_command_line()
36-
37-
else:
38-
raise RuntimeError('Unexpected value for PYDEVD_USE_FRAME_EVAL: %s (accepted: YES, NO)' % (use_frame_eval,))

src/debugpy/_vendored/pydevd/build_tools/build.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,11 @@ def build():
158158

159159

160160
if __name__ == '__main__':
161-
use_cython = os.getenv('PYDEVD_USE_CYTHON', None)
162-
if use_cython == 'YES':
161+
use_cython = os.getenv('PYDEVD_USE_CYTHON', '').lower()
162+
# Note: don't import pydevd during build (so, accept just yes/no in this case).
163+
if use_cython == 'yes':
163164
build()
164-
elif use_cython == 'NO':
165+
elif use_cython == 'no':
165166
remove_binaries(['.pyd', '.so'])
166167
elif use_cython is None:
167168
# Regular process
@@ -170,5 +171,5 @@ def build():
170171
generate_cython_module()
171172
build()
172173
else:
173-
raise RuntimeError('Unexpected value for PYDEVD_USE_CYTHON: %s (accepted: YES, NO)' % (use_cython,))
174+
raise RuntimeError('Unexpected value for PYDEVD_USE_CYTHON: %s (accepted: yes, no)' % (use_cython,))
174175

src/debugpy/_vendored/pydevd/pydevd_attach_to_process/attach_script.py

Lines changed: 0 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,5 @@
11

22

3-
def load_python_helper_lib():
4-
import sys
5-
try:
6-
import ctypes
7-
except ImportError:
8-
ctypes = None
9-
10-
# Note: we cannot use import platform because it may end up importing threading,
11-
# but that should be ok because at this point we can only be in CPython (other
12-
# implementations wouldn't get to this point in the attach process).
13-
# IS_CPYTHON = platform.python_implementation() == 'CPython'
14-
IS_CPYTHON = True
15-
16-
import os
17-
IS_64BIT_PROCESS = sys.maxsize > (2 ** 32)
18-
IS_WINDOWS = sys.platform == 'win32'
19-
IS_LINUX = sys.platform in ('linux', 'linux2')
20-
IS_MAC = sys.platform == 'darwin'
21-
22-
if not IS_CPYTHON or ctypes is None or sys.version_info[:2] > (3, 7):
23-
return None
24-
25-
if IS_WINDOWS:
26-
if IS_64BIT_PROCESS:
27-
suffix = 'amd64'
28-
else:
29-
suffix = 'x86'
30-
31-
filename = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'pydevd_attach_to_process', 'attach_%s.dll' % (suffix,))
32-
33-
elif IS_LINUX:
34-
if IS_64BIT_PROCESS:
35-
suffix = 'amd64'
36-
else:
37-
suffix = 'x86'
38-
39-
filename = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'pydevd_attach_to_process', 'attach_linux_%s.so' % (suffix,))
40-
41-
elif IS_MAC:
42-
if IS_64BIT_PROCESS:
43-
suffix = 'x86_64.dylib'
44-
else:
45-
suffix = 'x86.dylib'
46-
47-
filename = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'pydevd_attach_to_process', 'attach_%s' % (suffix,))
48-
49-
else:
50-
return None
51-
52-
if not os.path.exists(filename):
53-
return None
54-
55-
try:
56-
# Load as pydll so that we don't release the gil.
57-
lib = ctypes.pydll.LoadLibrary(filename)
58-
return lib
59-
except:
60-
return None
61-
62-
633
def get_main_thread_instance(threading):
644
if hasattr(threading, 'main_thread'):
655
return threading.main_thread()

src/debugpy/_vendored/pydevd/pydevd_tracing.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
21
from _pydevd_bundle.pydevd_constants import get_frame, IS_CPYTHON, IS_64BIT_PROCESS, IS_WINDOWS, \
3-
IS_LINUX, IS_MAC, IS_PY2, DebugInfoHolder, ForkSafeLock
2+
IS_LINUX, IS_MAC, IS_PY2, DebugInfoHolder, ForkSafeLock, LOAD_NATIVE_LIB_FLAG, \
3+
ENV_FALSE_LOWER_VALUES
44
from _pydev_imps._pydev_saved_modules import thread, threading
55
from _pydev_bundle import pydev_log, pydev_monkey
66
from os.path import os
@@ -109,7 +109,7 @@ def restore_sys_set_trace_func():
109109

110110

111111
def load_python_helper_lib():
112-
if not IS_CPYTHON or ctypes is None or sys.version_info[:2] > (3, 8):
112+
if not IS_CPYTHON or ctypes is None or sys.version_info[:2] > (3, 8) or hasattr(sys, 'gettotalrefcount') or LOAD_NATIVE_LIB_FLAG in ENV_FALSE_LOWER_VALUES:
113113
return None
114114

115115
if IS_WINDOWS:
@@ -159,8 +159,11 @@ def load_python_helper_lib():
159159
def set_trace_to_threads(tracing_func):
160160
lib = load_python_helper_lib()
161161
if lib is None: # This is the case if it's not CPython.
162+
pydev_log.info('Unable to load helper lib to set tracing to all threads (unsupported python vm).')
162163
return -1
163164

165+
pydev_log.info('Successfully Loaded helper lib to set tracing to all threads.')
166+
164167
ret = 0
165168
set_trace_func = TracingFunctionHolder._original_tracing or sys.settrace
166169

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import sys
2+
import threading
3+
from _pydev_bundle import pydev_log
4+
5+
6+
def check():
7+
with pydev_log.log_context(3, sys.stderr):
8+
assert hasattr(sys, 'gettotalrefcount')
9+
import pydevd_tracing
10+
11+
proceed1 = threading.Event()
12+
proceed2 = threading.Event()
13+
14+
class SomeThread(threading.Thread):
15+
16+
def run(self):
17+
proceed1.set()
18+
proceed2.wait()
19+
20+
t = SomeThread()
21+
t.start()
22+
proceed1.wait()
23+
try:
24+
25+
def some_func(frame, event, arg):
26+
return some_func
27+
28+
pydevd_tracing.set_trace_to_threads(some_func)
29+
finally:
30+
proceed2.set()
31+
32+
lib = pydevd_tracing.load_python_helper_lib()
33+
assert lib is None
34+
print('Finished OK')
35+
36+
37+
if __name__ == '__main__':
38+
check()

0 commit comments

Comments
 (0)