diff --git a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_constants.py b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_constants.py index 8c0e1b7b..b053d001 100644 --- a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_constants.py +++ b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_constants.py @@ -103,6 +103,7 @@ else: IS_PY3K = False IS_PY34_OR_GREATER = False IS_PY36_OR_GREATER = False +IS_PY37_OR_GREATER = False IS_PY2 = True IS_PY27 = False IS_PY24 = False @@ -112,6 +113,7 @@ try: IS_PY2 = False IS_PY34_OR_GREATER = sys.version_info >= (3, 4) IS_PY36_OR_GREATER = sys.version_info >= (3, 6) + IS_PY37_OR_GREATER = sys.version_info >= (3, 7) elif sys.version_info[0] == 2 and sys.version_info[1] == 7: IS_PY27 = True elif sys.version_info[0] == 2 and sys.version_info[1] == 4: diff --git a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_amd64.dll b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_amd64.dll index 8453936d..ee992ed3 100644 Binary files a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_amd64.dll and b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_amd64.dll differ diff --git a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_amd64.pdb b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_amd64.pdb index aea3e840..36f5b915 100644 Binary files a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_amd64.pdb and b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_amd64.pdb differ diff --git a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_linux_amd64.so b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_linux_amd64.so index c74c7a84..d5391157 100755 Binary files a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_linux_amd64.so and b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_linux_amd64.so differ diff --git a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_linux_x86.so b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_linux_x86.so index 99e1effe..79186ec3 100755 Binary files a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_linux_x86.so and b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_linux_x86.so differ diff --git a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_x86.dll b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_x86.dll index 5fd9ed63..9e9c1ee5 100644 Binary files a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_x86.dll and b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_x86.dll differ diff --git a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_x86.dylib b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_x86.dylib index 434744b4..cd5ababf 100644 Binary files a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_x86.dylib and b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_x86.dylib differ diff --git a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_x86.pdb b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_x86.pdb index 1564fda7..ebd80123 100644 Binary files a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_x86.pdb and b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_x86.pdb differ diff --git a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_x86_64.dylib b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_x86_64.dylib index f4e3d676..09e5d3e8 100644 Binary files a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_x86_64.dylib and b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/attach_x86_64.dylib differ diff --git a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/common/py_settrace.hpp b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/common/py_settrace.hpp index b0e46236..12659e20 100644 --- a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/common/py_settrace.hpp +++ b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/common/py_settrace.hpp @@ -4,6 +4,7 @@ #include "ref_utils.hpp" #include "py_utils.hpp" #include "python.h" +#include "py_settrace_37.hpp" #include @@ -36,7 +37,14 @@ DWORD GetPythonThreadId(PythonVersion version, PyThreadState* curThread) { /** * This function may be called to set a tracing function to existing python threads. */ -int InternalSetSysTraceFunc(MODULE_TYPE module, bool isDebug, bool showDebugInfo, PyObjectHolder* traceFunc, PyObjectHolder* setTraceFunc, unsigned int threadId) +int InternalSetSysTraceFunc( + MODULE_TYPE module, + bool isDebug, + bool showDebugInfo, + PyObjectHolder* traceFunc, + PyObjectHolder* setTraceFunc, + unsigned int threadId, + PyObjectHolder* pyNone) { if(showDebugInfo){ @@ -71,15 +79,8 @@ int InternalSetSysTraceFunc(MODULE_TYPE module, bool isDebug, bool showDebugInfo intFromLong = intFromLongPy2; } - DEFINE_PROC(errOccurred, PyErr_Occurred*, "PyErr_Occurred", 210); - DEFINE_PROC(pyErrFetch, PyErr_Fetch*, "PyErr_Fetch", 220); - DEFINE_PROC(pyErrRestore, PyErr_Restore*, "PyErr_Restore", 230); - DEFINE_PROC(pyImportMod, PyImport_ImportModule*, "PyImport_ImportModule", 240); DEFINE_PROC(pyGetAttr, PyObject_GetAttrString*, "PyObject_GetAttrString", 250); DEFINE_PROC(pyHasAttr, PyObject_HasAttrString*, "PyObject_HasAttrString", 260); - DEFINE_PROC(getThreadTls, PyThread_get_key_value*, "PyThread_get_key_value", 270); - DEFINE_PROC(setThreadTls, PyThread_set_key_value*, "PyThread_set_key_value", 280); - DEFINE_PROC(delThreadTls, PyThread_delete_key_value*, "PyThread_delete_key_value", 290); DEFINE_PROC_NO_CHECK(PyCFrame_Type, PyTypeObject*, "PyCFrame_Type", 300); // optional DEFINE_PROC_NO_CHECK(curPythonThread, PyThreadState**, "_PyThreadState_Current", 310); // optional @@ -111,67 +112,117 @@ int InternalSetSysTraceFunc(MODULE_TYPE module, bool isDebug, bool showDebugInfo } - int threadStateIndex = -1; - for (int i = 0; i < 100000; i++) { - void* value = getThreadTls(i); - if (value == curPyThread) { - threadStateIndex = i; + if (version < PythonVersion_37) + { + DEFINE_PROC(errOccurred, PyErr_Occurred*, "PyErr_Occurred", 210); + DEFINE_PROC(pyErrFetch, PyErr_Fetch*, "PyErr_Fetch", 220); + DEFINE_PROC(pyErrRestore, PyErr_Restore*, "PyErr_Restore", 230); + DEFINE_PROC(getThreadTls, PyThread_get_key_value*, "PyThread_get_key_value", 270); + DEFINE_PROC(setThreadTls, PyThread_set_key_value*, "PyThread_set_key_value", 280); + DEFINE_PROC(delThreadTls, PyThread_delete_key_value*, "PyThread_delete_key_value", 290); + int threadStateIndex = -1; + for (int i = 0; i < 100000; i++) { + void* value = getThreadTls(i); + if (value == curPyThread) { + threadStateIndex = i; + break; + } + } + + if(threadStateIndex == -1){ + printf("Unable to find threadStateIndex for the current thread. curPyThread: %p\n", curPyThread); + return 350; + } + + + bool found = false; + for (auto curThread = threadHead(head); curThread != nullptr; curThread = threadNext(curThread)) { + if (GetPythonThreadId(version, curThread) != threadId) { + continue; + } + found = true; + + + // switch to our new thread so we can call sys.settrace on it... + // all of the work here needs to be minimal - in particular we shouldn't + // ever evaluate user defined code as we could end up switching to this + // thread on the main thread and corrupting state. + delThreadTls(threadStateIndex); + setThreadTls(threadStateIndex, curThread); + auto prevThread = threadSwap(curThread); + + // save and restore the error in case something funky happens... + auto errOccured = errOccurred(); + PyObject* type = nullptr; + PyObject* value = nullptr; + PyObject* traceback = nullptr; + if (errOccured) { + pyErrFetch(&type, &value, &traceback); + retVal = 1; + } + + if(showDebugInfo){ + printf("setting trace for thread: %d\n", threadId); + } + + DecRef(call(setTraceFunc->ToPython(), traceFunc->ToPython(), nullptr), isDebug); + + if (errOccured) { + pyErrRestore(type, value, traceback); + } + + delThreadTls(threadStateIndex); + setThreadTls(threadStateIndex, prevThread); + threadSwap(prevThread); break; } - } - - if(threadStateIndex == -1){ - printf("Unable to find threadStateIndex for the current thread. curPyThread: %p\n", curPyThread); - return 350; - } - - - bool found = false; - for (auto curThread = threadHead(head); curThread != nullptr; curThread = threadNext(curThread)) { - if (GetPythonThreadId(version, curThread) != threadId) { - continue; - } - found = true; - - // switch to our new thread so we can call sys.settrace on it... - // all of the work here needs to be minimal - in particular we shouldn't - // ever evaluate user defined code as we could end up switching to this - // thread on the main thread and corrupting state. - delThreadTls(threadStateIndex); - setThreadTls(threadStateIndex, curThread); - auto prevThread = threadSwap(curThread); - - // save and restore the error in case something funky happens... - auto errOccured = errOccurred(); - PyObject* type = nullptr; - PyObject* value = nullptr; - PyObject* traceback = nullptr; - if (errOccured) { - pyErrFetch(&type, &value, &traceback); - retVal = 1; + if(!found) { + retVal = 500; } - - if(showDebugInfo){ - printf("setting trace for thread: %d\n", threadId); + } + else + { + // See comments on py_settrace_37.hpp for why we need a different implementation in Python 3.7 onwards. + DEFINE_PROC(pyUnicode_InternFromString, PyUnicode_InternFromString*, "PyUnicode_InternFromString", 520); + DEFINE_PROC(pyObject_FastCallDict, _PyObject_FastCallDict*, "_PyObject_FastCallDict", 530); + DEFINE_PROC(pyTraceBack_Here, PyTraceBack_Here*, "PyTraceBack_Here", 540); + DEFINE_PROC(pyEval_SetTrace, PyEval_SetTrace*, "PyEval_SetTrace", 550); + + bool found = false; + for (PyThreadState* curThread = threadHead(head); curThread != nullptr; curThread = threadNext(curThread)) { + if (GetPythonThreadId(version, curThread) != threadId) { + continue; + } + found = true; + + if(showDebugInfo){ + printf("setting trace for thread: %d\n", threadId); + } + + if(!InternalIsTraceInitialized_37()) + { + InternalInitializeSettrace_37 *internalInitializeSettrace_37 = new InternalInitializeSettrace_37(); + + IncRef(pyNone->ToPython()); + internalInitializeSettrace_37->pyNone = pyNone->ToPython(); + + internalInitializeSettrace_37->pyUnicode_InternFromString = pyUnicode_InternFromString; + internalInitializeSettrace_37->pyObject_FastCallDict = pyObject_FastCallDict; + internalInitializeSettrace_37->isDebug = isDebug; + internalInitializeSettrace_37->pyTraceBack_Here = pyTraceBack_Here; + internalInitializeSettrace_37->pyEval_SetTrace = pyEval_SetTrace; + + InternalTraceInit_37(internalInitializeSettrace_37); + } + InternalPySetTrace_37(curThread, traceFunc, isDebug); + break; } - - DecRef(call(setTraceFunc->ToPython(), traceFunc->ToPython(), nullptr), isDebug); - - if (errOccured) { - pyErrRestore(type, value, traceback); + if(!found) { + retVal = 501; } - - delThreadTls(threadStateIndex); - setThreadTls(threadStateIndex, prevThread); - threadSwap(prevThread); - break; } - - if(!found) { - retVal = 500; - } - + return retVal; } diff --git a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/common/py_settrace_37.hpp b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/common/py_settrace_37.hpp new file mode 100644 index 00000000..c1118b36 --- /dev/null +++ b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/common/py_settrace_37.hpp @@ -0,0 +1,176 @@ +#ifndef _PY_SETTRACE_37_HPP_ +#define _PY_SETTRACE_37_HPP_ + +#include "python.h" +#include "py_utils.hpp" + +// On Python 3.7 onwards the thread state is not kept in PyThread_set_key_value (rather +// it uses PyThread_tss_set using PyThread_tss_set(&_PyRuntime.gilstate.autoTSSkey, (void *)tstate) +// and we don't have access to that key from here (thus, we can't use the previous approach which +// made CPython think that the current thread had the thread state where we wanted to set the tracing). +// +// So, the solution implemented here is not faking that change and reimplementing PyEval_SetTrace. +// The implementation is mostly the same from the one in CPython, but we have one shortcoming: +// +// When CPython sets the tracing for a thread it increments _Py_TracingPossible (actually +// _PyRuntime.ceval.tracing_possible). This implementation has one issue: it only works on +// deltas when the tracing is set (so, a settrace(func) will increase the _Py_TracingPossible global value and a +// settrace(None) will decrease it, but when a thread dies it's kept as is and is not decreased). +// -- as we don't currently have access to _PyRuntime we have to create a thread, set the tracing +// and let it die so that the count is increased (this is really hacky, but better than having +// to create a local copy of the whole _PyRuntime (defined in pystate.h with several inner structs) +// which would need to be kept up to date for each new CPython version just to increment that variable). + + +struct InternalInitializeSettrace_37 { + PyUnicode_InternFromString* pyUnicode_InternFromString; + PyObject* pyNone; + _PyObject_FastCallDict* pyObject_FastCallDict; + PyTraceBack_Here* pyTraceBack_Here; + PyEval_SetTrace* pyEval_SetTrace; + bool isDebug; +}; + +/** + * Helper information to access CPython internals. + */ +static InternalInitializeSettrace_37 *internalInitializeSettrace_37 = NULL; + +/* + * Cached interned string objects used for calling the profile and + * trace functions. Initialized by InternalTraceInit_37(). + */ +static PyObject *InternalWhatstrings_37[8] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; + + +static int +InternalIsTraceInitialized_37() +{ + return internalInitializeSettrace_37 != NULL; +} + + +static int +InternalTraceInit_37(InternalInitializeSettrace_37 *p_internalInitializeSettrace_37) +{ + internalInitializeSettrace_37 = p_internalInitializeSettrace_37; + static const char * const whatnames[8] = { + "call", "exception", "line", "return", + "c_call", "c_exception", "c_return", + "opcode" + }; + PyObject *name; + int i; + for (i = 0; i < 8; ++i) { + if (InternalWhatstrings_37[i] == NULL) { + name = internalInitializeSettrace_37->pyUnicode_InternFromString(whatnames[i]); + if (name == NULL) + return -1; + InternalWhatstrings_37[i] = name; + } + } + return 0; +} + + +static PyObject * +InternalCallTrampoline_37(PyObject* callback, + PyFrameObject *frame, int what, PyObject *arg) +{ + PyObject *result; + PyObject *stack[3]; + +// Note: this is commented out from CPython (we shouldn't need it and it adds a reasonable overhead). +// if (PyFrame_FastToLocalsWithError(frame) < 0) { +// return NULL; +// } +// + stack[0] = (PyObject *)frame; + stack[1] = InternalWhatstrings_37[what]; + stack[2] = (arg != NULL) ? arg : internalInitializeSettrace_37->pyNone; + + // call the Python-level function + // result = _PyObject_FastCall(callback, stack, 3); + // + // Note that _PyObject_FastCall is actually a define: + // #define _PyObject_FastCall(func, args, nargs) _PyObject_FastCallDict((func), (args), (nargs), NULL) + + result = internalInitializeSettrace_37->pyObject_FastCallDict(callback, stack, 3, NULL); + + +// Note: this is commented out from CPython (we shouldn't need it and it adds a reasonable overhead). +// PyFrame_LocalsToFast(frame, 1); + + if (result == NULL) { + internalInitializeSettrace_37->pyTraceBack_Here(frame); + } + + return result; +} + +static int +InternalTraceTrampoline_37(PyObject *self, PyFrameObject *frame, + int what, PyObject *arg) +{ + PyObject *callback; + PyObject *result; + + if (what == PyTrace_CALL){ + callback = self; + } else { + callback = frame->f_trace; + } + + if (callback == NULL){ + return 0; + } + + result = InternalCallTrampoline_37(callback, frame, what, arg); + if (result == NULL) { + // Note: calling the original sys.settrace here. + internalInitializeSettrace_37->pyEval_SetTrace(NULL, NULL); + PyObject *temp_f_trace = frame->f_trace; + frame->f_trace = NULL; + if(temp_f_trace != NULL){ + DecRef(temp_f_trace, internalInitializeSettrace_37->isDebug); + } + return -1; + } + if (result != internalInitializeSettrace_37->pyNone) { + PyObject *tmp = frame->f_trace; + frame->f_trace = result; + DecRef(tmp, internalInitializeSettrace_37->isDebug); + } + else { + DecRef(result, internalInitializeSettrace_37->isDebug); + } + return 0; +} + +void InternalPySetTrace_37(PyThreadState* curThread, PyObjectHolder* traceFunc, bool isDebug) +{ + PyThreadState_37* tstate = reinterpret_cast(curThread); + PyObject *temp = tstate->c_traceobj; + + // We can't increase _Py_TracingPossible. Everything else should be equal to CPython. + // runtime->ceval.tracing_possible += (func != NULL) - (tstate->c_tracefunc != NULL); + + PyObject *arg = traceFunc->ToPython(); + IncRef(arg); + tstate->c_tracefunc = NULL; + tstate->c_traceobj = NULL; + /* Must make sure that profiling is not ignored if 'temp' is freed */ + tstate->use_tracing = tstate->c_profilefunc != NULL; + if(temp != NULL){ + DecRef(temp, isDebug); + } + tstate->c_tracefunc = InternalTraceTrampoline_37; + tstate->c_traceobj = arg; + /* Flag that tracing or profiling is turned on */ + tstate->use_tracing = ((InternalTraceTrampoline_37 != NULL) + || (tstate->c_profilefunc != NULL)); + +} + + +#endif //_PY_SETTRACE_37_HPP_ \ No newline at end of file diff --git a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/common/py_utils.hpp b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/common/py_utils.hpp index 04267a09..455a4bc3 100644 --- a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/common/py_utils.hpp +++ b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/common/py_utils.hpp @@ -25,6 +25,13 @@ typedef int (PyThread_set_key_value)(int, void*); typedef void (PyThread_delete_key_value)(int); typedef int (PyObject_Not) (PyObject *o); typedef PyObject* (PyDict_New)(); +typedef PyObject* (PyUnicode_InternFromString)(const char *u); +typedef PyObject * (_PyObject_FastCallDict)( + PyObject *callable, PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs); +typedef int (PyTraceBack_Here)(PyFrameObject *frame); + +typedef void (PyEval_SetTrace)(Py_tracefunc, PyObject *); +typedef int (*Py_tracefunc)(PyObject *, PyFrameObject *frame, int, PyObject *); // holder to ensure we release the GIL even in error conditions class GilHolder { diff --git a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/linux_and_mac/attach.cpp b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/linux_and_mac/attach.cpp index e3975c2f..6502c069 100644 --- a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/linux_and_mac/attach.cpp +++ b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/linux_and_mac/attach.cpp @@ -40,13 +40,6 @@ int hello() } -// This is the function which enables us to set the sys.settrace for all the threads -// which are already running. -// isDebug is pretty important! Must be true on python debug builds (python_d) -// If this value is passed wrongly the program will crash. -extern "C" int AttachDebuggerTracing(bool showDebugInfo, void* pSetTraceFunc, void* pTraceFunc, unsigned int threadId); -extern "C" int DoAttach(bool isDebug, const char *command, bool showDebugInfo); - // Internal function to keep on the tracing int _PYDEVD_ExecWithGILSetSysStrace(bool showDebugInfo, bool isDebug); @@ -56,6 +49,9 @@ typedef int (*PyEval_ThreadsInitialized)(); typedef unsigned long (*_PyEval_GetSwitchInterval)(void); typedef void (*_PyEval_SetSwitchInterval)(unsigned long microseconds); +// isDebug is pretty important! Must be true on python debug builds (python_d) +// If this value is passed wrongly the program will crash. +extern "C" int DoAttach(bool isDebug, const char *command, bool showDebugInfo); int DoAttach(bool isDebug, const char *command, bool showDebugInfo) { @@ -99,12 +95,17 @@ int DoAttach(bool isDebug, const char *command, bool showDebugInfo) } -int AttachDebuggerTracing(bool showDebugInfo, void* pSetTraceFunc, void* pTraceFunc, unsigned int threadId) +// This is the function which enables us to set the sys.settrace for all the threads +// which are already running. +extern "C" int AttachDebuggerTracing(bool showDebugInfo, void* pSetTraceFunc, void* pTraceFunc, unsigned int threadId, void* pPyNone); + +int AttachDebuggerTracing(bool showDebugInfo, void* pSetTraceFunc, void* pTraceFunc, unsigned int threadId, void* pPyNone) { void *module = dlopen(nullptr, 0x2); bool isDebug = false; PyObjectHolder traceFunc(isDebug, (PyObject*) pTraceFunc, true); PyObjectHolder setTraceFunc(isDebug, (PyObject*) pSetTraceFunc, true); - return InternalSetSysTraceFunc(module, isDebug, showDebugInfo, &traceFunc, &setTraceFunc, threadId); + PyObjectHolder pyNone(isDebug, reinterpret_cast(pPyNone), true); + return InternalSetSysTraceFunc(module, isDebug, showDebugInfo, &traceFunc, &setTraceFunc, threadId, &pyNone); } diff --git a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/windows/attach.cpp b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/windows/attach.cpp index 4492c945..005d4472 100644 --- a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/windows/attach.cpp +++ b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/windows/attach.cpp @@ -463,11 +463,15 @@ extern "C" SuspendThreads(suspendedThreads, addPendingCall, threadsInited); if(!threadsInited()){ // Check again with threads suspended. - std::cout << "ENTERED if (!threadsInited()) {" << std::endl << std::flush; + if (showDebugInfo) { + std::cout << "ENTERED if (!threadsInited()) {" << std::endl << std::flush; + } auto curPyThread = getPythonThread ? getPythonThread() : *curPythonThread; if (curPyThread == nullptr) { - std::cout << "ENTERED if (curPyThread == nullptr) {" << std::endl << std::flush; + if (showDebugInfo) { + std::cout << "ENTERED if (curPyThread == nullptr) {" << std::endl << std::flush; + } // no threads are currently running, it is safe to initialize multi threading. PyGILState_STATE gilState; if (version >= PythonVersion_34) { @@ -497,7 +501,9 @@ extern "C" gilState = PyGILState_LOCKED; // prevent compiler warning } - std::cout << "Called initThreads()" << std::endl << std::flush; + if (showDebugInfo) { + std::cout << "Called initThreads()" << std::endl << std::flush; + } // Initialize threads in our secondary thread (this is NOT ideal because // this thread will be the thread head), but is still better than not being // able to attach if the main thread is not actually running any code. @@ -670,7 +676,7 @@ extern "C" /** * This function may be called to set a tracing function to existing python threads. **/ - DECLDIR int AttachDebuggerTracing(bool showDebugInfo, void* pSetTraceFunc, void* pTraceFunc, unsigned int threadId) + DECLDIR int AttachDebuggerTracing(bool showDebugInfo, void* pSetTraceFunc, void* pTraceFunc, unsigned int threadId, void* pPyNone) { ModuleInfo moduleInfo = GetPythonModule(); if (moduleInfo.errorGettingModule != 0) { @@ -683,7 +689,9 @@ extern "C" int attached = 0; PyObjectHolder traceFunc(moduleInfo.isDebug, reinterpret_cast(pTraceFunc), true); PyObjectHolder setTraceFunc(moduleInfo.isDebug, reinterpret_cast(pSetTraceFunc), true); - int temp = InternalSetSysTraceFunc(module, moduleInfo.isDebug, showDebugInfo, &traceFunc, &setTraceFunc, threadId); + PyObjectHolder pyNone(moduleInfo.isDebug, reinterpret_cast(pPyNone), true); + + int temp = InternalSetSysTraceFunc(module, moduleInfo.isDebug, showDebugInfo, &traceFunc, &setTraceFunc, threadId, &pyNone); if (temp == 0) { // we've successfully attached the debugger return 0; diff --git a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/windows/attach.h b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/windows/attach.h index 41253761..3a2b582a 100644 --- a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/windows/attach.h +++ b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/windows/attach.h @@ -49,7 +49,8 @@ extern "C" bool showDebugInfo, void* pSetTraceFunc, // Actually PyObject*, but we don't want to include it here. void* pTraceFunc, // Actually PyObject*, but we don't want to include it here. - unsigned int threadId + unsigned int threadId, + void* pPyNone // Actually PyObject*, but we don't want to include it here. ); } diff --git a/src/ptvsd/_vendored/pydevd/pydevd_tracing.py b/src/ptvsd/_vendored/pydevd/pydevd_tracing.py index 0bb00bcb..d9da1178 100644 --- a/src/ptvsd/_vendored/pydevd/pydevd_tracing.py +++ b/src/ptvsd/_vendored/pydevd/pydevd_tracing.py @@ -1,8 +1,8 @@ from _pydevd_bundle.pydevd_constants import get_frame, IS_CPYTHON, IS_64BIT_PROCESS, IS_WINDOWS, \ - IS_LINUX, IS_MAC, IS_PY2, DebugInfoHolder + IS_LINUX, IS_MAC, IS_PY2, IS_PY37_OR_GREATER, DebugInfoHolder from _pydev_imps._pydev_saved_modules import thread, threading -from _pydev_bundle import pydev_log +from _pydev_bundle import pydev_log, pydev_monkey from os.path import os try: import ctypes @@ -146,7 +146,10 @@ def load_python_helper_lib(): lib = ctypes.pydll.LoadLibrary(filename) return lib except: - pydev_log.exception('Error loading: %s', filename) + if DebugInfoHolder.DEBUG_TRACE_LEVEL >= 1: + # Only show message if tracing is on (we don't have pre-compiled + # binaries for all architectures -- i.e.: ARM). + pydev_log.exception('Error loading: %s', filename) return None @@ -163,9 +166,12 @@ def set_trace_to_threads(tracing_func): prev_value = get_interval() ret = 0 try: - # Prevent going to any other thread... if we switch the thread during this operation we - # could potentially corrupt the interpreter. - set_interval(2 ** 15) + 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 @@ -216,12 +222,37 @@ def set_trace_to_threads(tracing_func): # show_debug_info = 1 if DebugInfoHolder.DEBUG_TRACE_LEVEL >= 1 else 0 show_debug_info = 0 - 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)) + 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: - set_interval(prev_value) + if not IS_PY37_OR_GREATER: + set_interval(prev_value) return ret diff --git a/src/ptvsd/_vendored/pydevd/tests_python/test_debugger_json.py b/src/ptvsd/_vendored/pydevd/tests_python/test_debugger_json.py index b5eccdce..7613f72c 100644 --- a/src/ptvsd/_vendored/pydevd/tests_python/test_debugger_json.py +++ b/src/ptvsd/_vendored/pydevd/tests_python/test_debugger_json.py @@ -728,7 +728,7 @@ def test_case_skipping_filters(case_setup, custom_setup): other_filename = os.path.join(not_my_code_dir, 'other.py') response = json_facade.write_set_breakpoints(1, filename=other_filename, verified=False) assert response.body.breakpoints == [ - {'verified': False, 'message': 'Breakpoint in file excluded by filters.', 'source': {}, 'line': 1}] + {'verified': False, 'message': 'Breakpoint in file excluded by filters.', 'source': {'path': other_filename}, 'line': 1}] # Note: there's actually a use-case where we'd hit that breakpoint even if it was excluded # by filters, so, we must actually clear it afterwards (the use-case is that when we're # stepping into the context with the breakpoint we wouldn't skip it). @@ -737,7 +737,7 @@ def test_case_skipping_filters(case_setup, custom_setup): other_filename = os.path.join(not_my_code_dir, 'file_that_does_not_exist.py') response = json_facade.write_set_breakpoints(1, filename=other_filename, verified=False) assert response.body.breakpoints == [ - {'verified': False, 'message': 'Breakpoint in file that does not exist.', 'source': {}, 'line': 1}] + {'verified': False, 'message': 'Breakpoint in file that does not exist.', 'source': {'path': other_filename}, 'line': 1}] elif custom_setup == 'set_exclude_launch_module_full': json_facade.write_launch( @@ -767,7 +767,7 @@ def test_case_skipping_filters(case_setup, custom_setup): assert response.body.breakpoints == [{ 'verified': False, 'message': 'Breakpoint in file excluded by filters.\nNote: may be excluded because of "justMyCode" option (default == true).', - 'source': {}, + 'source': {'path': other_filename}, 'line': 14 }] elif custom_setup == 'set_just_my_code_and_include':