diff --git a/.gitignore b/.gitignore index e95c34ea..2abc816e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,9 +3,6 @@ __pycache__/ *.py[cod] *$py.class -# C extensions -*.so - # Distribution / packaging .Python env/ diff --git a/src/ptvsd/__main__.py b/src/ptvsd/__main__.py index a28f9d13..1d384241 100644 --- a/src/ptvsd/__main__.py +++ b/src/ptvsd/__main__.py @@ -350,66 +350,50 @@ def run_code(): def attach_to_pid(): - def quoted_str(s): - if s is None: - return s - assert not isinstance(s, bytes) - unescaped = set(chr(ch) for ch in range(32, 127)) - {'"', "'", '\\'} - - def escape(ch): - return ch if ch in unescaped else '\\u%04X' % ord(ch) - - return 'u"' + ''.join(map(escape, s)) + '"' - ptvsd.log.info('Attaching to process with ID {0}', ptvsd.options.target) pid = ptvsd.options.target - host = quoted_str(ptvsd.options.host) + host = ptvsd.options.host port = ptvsd.options.port client = ptvsd.options.client - log_dir = quoted_str(ptvsd.options.log_dir) - - ptvsd_path = os.path.abspath(os.path.join(ptvsd.__file__, '../..')) - if isinstance(ptvsd_path, bytes): - ptvsd_path = ptvsd_path.decode(sys.getfilesystemencoding()) - ptvsd_path = quoted_str(ptvsd_path) - - # pydevd requires injected code to not contain any single quotes. - code = ''' -import os -assert os.getpid() == {pid} - -import sys -sys.path.insert(0, {ptvsd_path}) -import ptvsd -del sys.path[0] - -import ptvsd.options -ptvsd.options.log_dir = {log_dir} -ptvsd.options.client = {client} -ptvsd.options.host = {host} -ptvsd.options.port = {port} - -import ptvsd.log -ptvsd.log.to_file() -ptvsd.log.info("Debugger successfully injected") - -if ptvsd.options.client: - from ptvsd._remote import attach - attach(({host}, {port})) -else: - ptvsd.enable_attach() -'''.format(**locals()) - - ptvsd.log.debug('Injecting debugger into target process: \n"""{0}\n"""'.format(code)) - assert "'" not in code, 'Injected code should not contain any single quotes' + log_dir = ptvsd.options.log_dir + if log_dir is None: + log_dir = "" + # pydevd requires injected code to not contain any single quotes nor new lines and + # double quotes must be escaped properly. pydevd_attach_to_process_path = os.path.join( os.path.dirname(pydevd.__file__), 'pydevd_attach_to_process') - sys.path.insert(0, pydevd_attach_to_process_path) - from add_code_to_python_process import run_python_code - run_python_code(pid, code, connect_debugger_tracing=True) + + sys.path.append(pydevd_attach_to_process_path) + + import add_code_to_python_process # noqa + show_debug_info_on_target_process = 0 # hard-coded (1 to debug) + + ptvsd_dirname = os.path.dirname(os.path.dirname(__file__)) + 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('\\', '/') + python_code = '''import sys; +sys.path.append("%(pythonpath)s"); +from ptvsd 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 + # 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; +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', '') + + python_code = python_code % setup + add_code_to_python_process.run_python_code( + setup['pid'], python_code, connect_debugger_tracing=True, show_debug_info=show_debug_info_on_target_process) def main(argv=sys.argv): diff --git a/src/ptvsd/_vendored/pydevd/pydevd.py b/src/ptvsd/_vendored/pydevd/pydevd.py index c62f5a8d..085dc13a 100644 --- a/src/ptvsd/_vendored/pydevd/pydevd.py +++ b/src/ptvsd/_vendored/pydevd/pydevd.py @@ -912,6 +912,7 @@ class PyDB(object): self._finish_debugging_session = True def initialize_network(self, sock, terminate_on_socket_close=True): + assert sock is not None try: sock.settimeout(None) # infinite, no timeouts from now on - jython does not have it except: 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 new file mode 100755 index 00000000..50333156 Binary files /dev/null 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 new file mode 100755 index 00000000..392a3c14 Binary files /dev/null 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/linux/attach_linux.c b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/linux/attach_linux.c index 3687ba56..0e28faad 100644 --- a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/linux/attach_linux.c +++ b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/linux/attach_linux.c @@ -60,6 +60,7 @@ typedef int (*PyEval_ThreadsInitialized)(); typedef unsigned long (*_PyEval_GetSwitchInterval)(void); typedef void (*_PyEval_SetSwitchInterval)(unsigned long microseconds); typedef int (*PyRun_SimpleString)(const char *command); +typedef int (*PyObject_Not) (PyObject *o); // Helper so that we get a PyObject where we can access its fields (in debug or release). PyObject* GetPyObjectPointerNoDebugInfo(bool isDebug, PyObject* object) { @@ -121,7 +122,7 @@ public: }; -# define CHECK_NULL(ptr, msg, returnVal) if(ptr == NULL){if(showDebugInfo){printf(msg);} return returnVal;} +# define CHECK_NULL(ptr, msg, returnVal) if(ptr == NULL){printf(msg); return returnVal;} int DoAttach(bool isDebug, const char *command, bool showDebugInfo) { @@ -161,7 +162,7 @@ int DoAttach(bool isDebug, const char *command, bool showDebugInfo) PyRun_SimpleString pyRun_SimpleString; *(void**)(&pyRun_SimpleString) = dlsym(main_hndl, "PyRun_SimpleString"); - CHECK_NULL(pyRun_SimpleString, "PyRun_SimpleString not found.\n", 6); + CHECK_NULL(pyRun_SimpleString, "PyRun_SimpleString not found.\n", 7); PyGILState_STATE pyGILState = pyGilStateEnsureFunc(); pyRun_SimpleString(command); @@ -171,7 +172,7 @@ int DoAttach(bool isDebug, const char *command, bool showDebugInfo) // All of the code below is the same as: -// sys.settrace(pydevd.GetGlobalDebugger().trace_dispatch) +// sys.settrace(pydevd.get_global_debugger().trace_dispatch) // // (with error checking) int SetSysTraceFunc(bool showDebugInfo, bool isDebug) @@ -188,25 +189,25 @@ int SetSysTraceFunc(bool showDebugInfo, bool isDebug) if(showDebugInfo){ printf("Py_IsInitialized returned false.\n"); } - return 2; + return 20; } PythonVersion version = GetPythonVersion(); PyInterpreterState_Head interpHeadFunc; *(void**)(&interpHeadFunc) = dlsym(main_hndl, "PyInterpreterState_Head"); - CHECK_NULL(interpHeadFunc, "PyInterpreterState_Head not found.\n", 3); + CHECK_NULL(interpHeadFunc, "PyInterpreterState_Head not found.\n", 21); PyInterpreterState* head = interpHeadFunc(); - CHECK_NULL(head, "Interpreter not initialized.\n", 4); + CHECK_NULL(head, "Interpreter not initialized.\n", 22); PyGILState_Ensure pyGilStateEnsureFunc; *(void**)(&pyGilStateEnsureFunc) = dlsym(main_hndl, "PyGILState_Ensure"); - CHECK_NULL(pyGilStateEnsureFunc, "PyGILState_Ensure not found.\n", 5); + CHECK_NULL(pyGilStateEnsureFunc, "PyGILState_Ensure not found.\n", 23); PyGILState_Release pyGilStateReleaseFunc; *(void**)(&pyGilStateReleaseFunc) = dlsym(main_hndl, "PyGILState_Release"); - CHECK_NULL(pyGilStateReleaseFunc, "PyGILState_Release not found.\n", 6); + CHECK_NULL(pyGilStateReleaseFunc, "PyGILState_Release not found.\n", 24); PyGILState_STATE pyGILState = pyGilStateEnsureFunc(); int ret = _PYDEVD_ExecWithGILSetSysStrace(showDebugInfo, isDebug); @@ -221,63 +222,91 @@ int _PYDEVD_ExecWithGILSetSysStrace(bool showDebugInfo, bool isDebug){ void *main_hndl = dlopen(NULL, 0x2); *(void**)(&boolFromLongFunc) = dlsym(main_hndl, "PyBool_FromLong"); - CHECK_NULL(boolFromLongFunc, "PyBool_FromLong not found.\n", 7); + CHECK_NULL(boolFromLongFunc, "PyBool_FromLong not found.\n", 41); PyObject_HasAttrString pyHasAttrFunc; *(void**)(&pyHasAttrFunc) = dlsym(main_hndl, "PyObject_HasAttrString"); - CHECK_NULL(pyHasAttrFunc, "PyObject_HasAttrString not found.\n", 7); + CHECK_NULL(pyHasAttrFunc, "PyObject_HasAttrString not found.\n", 42); //Important: we need a non-blocking import here: PyImport_ImportModule //could end up crashing (this makes us work only from 2.6 onwards). PyImport_ImportModuleNoBlock pyImportModFunc; *(void**)(&pyImportModFunc) = dlsym(main_hndl, "PyImport_ImportModuleNoBlock"); - CHECK_NULL(pyImportModFunc, "PyImport_ImportModuleNoBlock not found.\n", 8); + CHECK_NULL(pyImportModFunc, "PyImport_ImportModuleNoBlock not found.\n", 43); + + PyObject_Not pyObjectNot; + *(void**)(&pyObjectNot) = dlsym(main_hndl, "PyObject_Not"); + CHECK_NULL(pyObjectNot, "PyObject_Not not found.\n", 44); PyObjectHolder pydevdTracingMod = PyObjectHolder(isDebug, pyImportModFunc("pydevd_tracing")); - CHECK_NULL(pydevdTracingMod.ToPython(), "pydevd_tracing module null.\n", 9); + CHECK_NULL(pydevdTracingMod.ToPython(), "pydevd_tracing module null.\n", 45); if(!pyHasAttrFunc(pydevdTracingMod.ToPython(), "_original_settrace")){ if(showDebugInfo){ printf("pydevd_tracing module has no _original_settrace!\n"); } - return 8; + return 46; } PyObject_GetAttrString pyGetAttr; *(void**)(&pyGetAttr) = dlsym(main_hndl, "PyObject_GetAttrString"); - CHECK_NULL(pyGetAttr, "PyObject_GetAttrString not found.\n", 8); + CHECK_NULL(pyGetAttr, "PyObject_GetAttrString not found.\n", 47); PyObjectHolder settrace = PyObjectHolder(isDebug, pyGetAttr(pydevdTracingMod.ToPython(), "_original_settrace")); - CHECK_NULL(settrace.ToPython(), "pydevd_tracing._original_settrace null!\n", 10); + CHECK_NULL(settrace.ToPython(), "pydevd_tracing._original_settrace null!\n", 48); + + if(pyObjectNot(settrace.ToPython())){ + printf("_original_settrace is None."); + return 49; + } PyObjectHolder pydevdMod = PyObjectHolder(isDebug, pyImportModFunc("pydevd")); - CHECK_NULL(pydevdMod.ToPython(), "pydevd module null.\n", 10); + CHECK_NULL(pydevdMod.ToPython(), "pydevd module null.\n", 50); - PyObjectHolder getGlobalDebugger = PyObjectHolder(isDebug, pyGetAttr(pydevdMod.ToPython(), "GetGlobalDebugger")); - CHECK_NULL(getGlobalDebugger.ToPython(), "pydevd.GetGlobalDebugger null.\n", 11); + if(pyObjectNot(pydevdMod.ToPython())){ + printf("pydevdMod is None."); + return 51; + } + + if(!pyHasAttrFunc(pydevdMod.ToPython(), "get_global_debugger")){ + printf("pydevd module has no attribute get_global_debugger!\n"); + return 52; + } + + + PyObjectHolder getGlobalDebugger = PyObjectHolder(isDebug, pyGetAttr(pydevdMod.ToPython(), "get_global_debugger")); + CHECK_NULL(getGlobalDebugger.ToPython(), "pydevd.get_global_debugger is null.\n", 53); + + if(pyObjectNot(getGlobalDebugger.ToPython())){ + printf("getGlobalDebugger returned None."); + return 54; + } PyObject_CallFunctionObjArgs call; *(void**)(&call) = dlsym(main_hndl, "PyObject_CallFunctionObjArgs"); - CHECK_NULL(call, "PyObject_CallFunctionObjArgs not found.\n", 11); + CHECK_NULL(call, "PyObject_CallFunctionObjArgs not found.\n", 55); PyObjectHolder globalDbg = PyObjectHolder(isDebug, call(getGlobalDebugger.ToPython(), NULL)); - CHECK_NULL(globalDbg.ToPython(), "pydevd.GetGlobalDebugger() returned null.\n", 12); + CHECK_NULL(globalDbg.ToPython(), "pydevd.get_global_debugger() returned null.\n", 56); if(!pyHasAttrFunc(globalDbg.ToPython(), "trace_dispatch")){ - if(showDebugInfo){ - printf("pydevd.GetGlobalDebugger() has no attribute trace_dispatch!\n"); - } - return 13; + printf("pydevd.get_global_debugger() has no attribute trace_dispatch!\n"); + return 57; } PyObjectHolder traceFunc = PyObjectHolder(isDebug, pyGetAttr(globalDbg.ToPython(), "trace_dispatch")); - CHECK_NULL(traceFunc.ToPython(), "pydevd.GetGlobalDebugger().trace_dispatch returned null!\n", 14); + CHECK_NULL(traceFunc.ToPython(), "pydevd.get_global_debugger().trace_dispatch returned null!\n", 58); + + if(pyObjectNot(traceFunc.ToPython())){ + printf("pydevd.get_global_debugger().trace_dispatch is None."); + return 59; + } DecRef(call(settrace.ToPython(), traceFunc.ToPython(), NULL), isDebug); if(showDebugInfo){ - printf("sys.settrace(pydevd.GetGlobalDebugger().trace_dispatch) worked.\n"); + printf("sys.settrace(pydevd.get_global_debugger().trace_dispatch) worked.\n"); } return 0; diff --git a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/linux/compile_so.sh b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/linux/compile_so.sh old mode 100644 new mode 100755 diff --git a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/linux/python.h b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/linux/python.h index 93bfe6e4..0238cfc1 100644 --- a/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/linux/python.h +++ b/src/ptvsd/_vendored/pydevd/pydevd_attach_to_process/linux/python.h @@ -26,7 +26,10 @@ enum PythonVersion { PythonVersion_31 = 0x0301, PythonVersion_32 = 0x0302, PythonVersion_33 = 0x0303, - PythonVersion_34 = 0x0304 + PythonVersion_34 = 0x0304, + PythonVersion_35 = 0x0305, + PythonVersion_36 = 0x0306, + PythonVersion_37 = 0x0307 }; @@ -111,8 +114,8 @@ public: } }; -// 3.3-3.4 -class PyCodeObject33_34 : public PyObject { +// 3.3-3.5 +class PyCodeObject33_35 : public PyObject { public: int co_argcount; /* #arguments, except *args */ int co_kwonlyargcount; /* #keyword only arguments */ @@ -134,15 +137,77 @@ public: void *co_zombieframe; /* for optimization only (see frameobject.c) */ static bool IsFor(int majorVersion, int minorVersion) { - return majorVersion == 3 && (minorVersion >= 3 && minorVersion <= 4); + return majorVersion == 3 && (minorVersion >= 3 && minorVersion <= 5); } static bool IsFor(PythonVersion version) { - return version >= PythonVersion_33 && version <= PythonVersion_34; + return version >= PythonVersion_33 && version <= PythonVersion_35; } }; -// 2.5 - 3.1 +// 3.6 +class PyCodeObject36 : public PyObject { +public: + int co_argcount; /* #arguments, except *args */ + int co_kwonlyargcount; /* #keyword only arguments */ + int co_nlocals; /* #local variables */ + int co_stacksize; /* #entries needed for evaluation stack */ + int co_flags; /* CO_..., see below */ + int co_firstlineno; /* first source line number */ + PyObject *co_code; /* instruction opcodes */ + PyObject *co_consts; /* list (constants used) */ + PyObject *co_names; /* list of strings (names used) */ + PyObject *co_varnames; /* tuple of strings (local variable names) */ + PyObject *co_freevars; /* tuple of strings (free variable names) */ + PyObject *co_cellvars; /* tuple of strings (cell variable names) */ + /* The rest doesn't count for hash or comparisons */ + unsigned char *co_cell2arg; /* Maps cell vars which are arguments. */ + PyObject *co_filename; /* unicode (where it was loaded from) */ + PyObject *co_name; /* unicode (name, for reference) */ + PyObject *co_lnotab; /* string (encoding addr<->lineno mapping) */ + void *co_zombieframe; /* for optimization only (see frameobject.c) */ + + static bool IsFor(int majorVersion, int minorVersion) { + return majorVersion == 3 && minorVersion == 6; + } + + static bool IsFor(PythonVersion version) { + return version == PythonVersion_36; + } +}; + +// 3.7 +class PyCodeObject37 : public PyObject { +public: + int co_argcount; /* #arguments, except *args */ + int co_kwonlyargcount; /* #keyword only arguments */ + int co_nlocals; /* #local variables */ + int co_stacksize; /* #entries needed for evaluation stack */ + int co_flags; /* CO_..., see below */ + int co_firstlineno; /* first source line number */ + PyObject *co_code; /* instruction opcodes */ + PyObject *co_consts; /* list (constants used) */ + PyObject *co_names; /* list of strings (names used) */ + PyObject *co_varnames; /* tuple of strings (local variable names) */ + PyObject *co_freevars; /* tuple of strings (free variable names) */ + PyObject *co_cellvars; /* tuple of strings (cell variable names) */ + /* The rest doesn't count for hash or comparisons */ + ssize_t *co_cell2arg; /* Maps cell vars which are arguments. */ + PyObject *co_filename; /* unicode (where it was loaded from) */ + PyObject *co_name; /* unicode (name, for reference) */ + PyObject *co_lnotab; /* string (encoding addr<->lineno mapping) */ + void *co_zombieframe; /* for optimization only (see frameobject.c) */ + + static bool IsFor(int majorVersion, int minorVersion) { + return majorVersion == 3 && minorVersion >= 7; + } + + static bool IsFor(PythonVersion version) { + return version >= PythonVersion_37; + } +}; + +// 2.5 - 3.7 class PyFunctionObject : public PyObject { public: PyObject *func_code; /* A code object */ @@ -165,7 +230,7 @@ public: */ }; -// 2.4 - 3.2 compatible +// 2.4 - 3.7 compatible typedef struct { PyObject_HEAD size_t length; /* Length of raw Unicode data in buffer */ @@ -173,7 +238,7 @@ typedef struct { long hash; /* Hash value; -1 if not set */ } PyUnicodeObject; -// 2.4 - 3.4 compatible +// 2.4 - 3.7 compatible class PyFrameObject : public PyVarObject { public: PyFrameObject *f_back; /* previous frame, or NULL */ @@ -187,7 +252,6 @@ public: to the current stack top. */ PyObject **f_stacktop; PyObject *f_trace; /* Trace function */ - PyObject *f_exc_type, *f_exc_value, *f_exc_traceback; }; #define CO_MAXBLOCKS 20 @@ -199,6 +263,7 @@ typedef struct { class PyFrameObject25_33 : public PyFrameObject { public: + PyObject * f_exc_type, *f_exc_value, *f_exc_traceback; PyThreadState* f_tstate; int f_lasti; /* Last instruction if called */ /* As of 2.3 f_lineno is only valid when tracing is active (i.e. when @@ -214,8 +279,9 @@ public: } }; -class PyFrameObject34 : public PyFrameObject { +class PyFrameObject34_36 : public PyFrameObject { public: + PyObject * f_exc_type, *f_exc_value, *f_exc_traceback; /* Borrowed reference to a generator, or NULL */ PyObject *f_gen; @@ -229,22 +295,43 @@ public: PyObject *f_localsplus[1]; /* locals+stack, dynamically sized */ static bool IsFor(int majorVersion, int minorVersion) { - return majorVersion == 3 && minorVersion == 4; + return majorVersion == 3 && minorVersion >= 4 && minorVersion <= 6; + } +}; + +class PyFrameObject37 : public PyFrameObject { +public: + char f_trace_lines; /* Emit per-line trace events? */ + char f_trace_opcodes; /* Emit per-opcode trace events? */ + /* Borrowed reference to a generator, or NULL */ + PyObject *f_gen; + + int f_lasti; /* Last instruction if called */ + /* As of 2.3 f_lineno is only valid when tracing is active (i.e. when + f_trace is set) -- at other times use PyCode_Addr2Line instead. */ + int f_lineno; /* Current line number */ + int f_iblock; /* index in f_blockstack */ + char f_executing; /* whether the frame is still executing */ + PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */ + PyObject *f_localsplus[1]; /* locals+stack, dynamically sized */ + + static bool IsFor(int majorVersion, int minorVersion) { + return majorVersion == 3 && minorVersion >= 7; } }; typedef void (*destructor)(PyObject *); -// 2.4 - 3.4 +// 2.4 - 3.7 class PyMethodDef { public: char *ml_name; /* The name of the built-in function/method */ }; -// -// 2.4 - 3.4, 2.4 has different compat in 64-bit but we don't support any of the released 64-bit platforms (which includes only IA-64) +// +// 2.5 - 3.7 // While these are compatible there are fields only available on later versions. class PyTypeObject : public PyVarObject { public: @@ -257,7 +344,10 @@ public: void* tp_print; void* tp_getattr; void* tp_setattr; - void* tp_compare; + union { + void* tp_compare; /* 2.4 - 3.4 */ + void *tp_as_async; /* 3.5 - 3.7 */ + }; void* tp_repr; /* Method suites for standard classes */ @@ -326,7 +416,7 @@ public: unsigned int tp_version_tag; }; -// 2.4 - 3.4 +// 2.4 - 3.7 class PyTupleObject : public PyVarObject { public: PyObject *ob_item[1]; @@ -337,7 +427,7 @@ public: */ }; -// 2.4 - 3.4 +// 2.4 - 3.7 class PyCFunctionObject : public PyObject { public: PyMethodDef *m_ml; /* Description of the C function to call */ @@ -422,7 +512,7 @@ public: int recursion_depth; char overflowed; /* The stack has overflowed. Allow 50 more calls to handle the runtime error. */ - char recursion_critical; /* The current calls must not cause + char recursion_critical; /* The current calls must not cause a stack overflow. */ /* 'tracing' keeps track of the execution depth when tracing/profiling. This is to prevent the actual trace/profile code from being recorded in @@ -468,7 +558,7 @@ public: } }; -class PyThreadState_34 : public PyThreadState { +class PyThreadState_34_36 : public PyThreadState { public: PyThreadState *prev; PyThreadState *next; @@ -478,7 +568,7 @@ public: int recursion_depth; char overflowed; /* The stack has overflowed. Allow 50 more calls to handle the runtime error. */ - char recursion_critical; /* The current calls must not cause + char recursion_critical; /* The current calls must not cause a stack overflow. */ /* 'tracing' keeps track of the execution depth when tracing/profiling. This is to prevent the actual trace/profile code from being recorded in @@ -504,15 +594,70 @@ public: int gilstate_counter; PyObject *async_exc; /* Asynchronous exception to raise */ - long thread_id; /* Thread id where this tstate was created */ + long thread_id; /* Thread id where this tstate was created */ /* XXX signal handlers should also be here */ + static bool IsFor(int majorVersion, int minorVersion) { - return majorVersion == 3 && minorVersion == 4; + return majorVersion == 3 && minorVersion >= 4 && minorVersion <= 6; } static bool IsFor(PythonVersion version) { - return version == PythonVersion_34; + return version >= PythonVersion_34 && version <= PythonVersion_36; + } +}; + +struct _PyErr_StackItem { + PyObject *exc_type, *exc_value, *exc_traceback; + struct _PyErr_StackItem *previous_item; +}; + +class PyThreadState_37 : public PyThreadState { +public: + PyThreadState * prev; + PyThreadState *next; + PyInterpreterState *interp; + + PyFrameObject *frame; + int recursion_depth; + char overflowed; /* The stack has overflowed. Allow 50 more calls + to handle the runtime error. */ + char recursion_critical; /* The current calls must not cause + a stack overflow. */ + /* 'tracing' keeps track of the execution depth when tracing/profiling. + This is to prevent the actual trace/profile code from being recorded in + the trace/profile. */ + int stackcheck_counter; + + int tracing; + int use_tracing; + + Py_tracefunc c_profilefunc; + Py_tracefunc c_tracefunc; + PyObject *c_profileobj; + PyObject *c_traceobj; + + PyObject *curexc_type; + PyObject *curexc_value; + PyObject *curexc_traceback; + + _PyErr_StackItem exc_state; + _PyErr_StackItem *exc_info; + + PyObject *dict; /* Stores per-thread state */ + + int gilstate_counter; + + PyObject *async_exc; /* Asynchronous exception to raise */ + + unsigned long thread_id; /* Thread id where this tstate was created */ + + static bool IsFor(int majorVersion, int minorVersion) { + return majorVersion == 3 && minorVersion >= 7; + } + + static bool IsFor(PythonVersion version) { + return version >= PythonVersion_37; } }; @@ -553,19 +698,23 @@ static PythonVersion GetPythonVersion() { if(versionFunc != NULL) { const char* version = versionFunc(); if(version != NULL && strlen(version) >= 3 && version[1] == '.') { - if(version[0] == '2') { - switch(version[2]) { + if (version[0] == '2') { + switch (version[2]) { case '5': return PythonVersion_25; case '6': return PythonVersion_26; case '7': return PythonVersion_27; } - } else if(version[0] == '3') { - switch(version[2]) { + } + else if (version[0] == '3') { + switch (version[2]) { case '0': return PythonVersion_30; case '1': return PythonVersion_31; case '2': return PythonVersion_32; case '3': return PythonVersion_33; case '4': return PythonVersion_34; + case '5': return PythonVersion_35; + case '6': return PythonVersion_36; + case '7': return PythonVersion_37; } } } diff --git a/src/ptvsd/attach_script_ptvsd_pid.py b/src/ptvsd/attach_script_ptvsd_pid.py new file mode 100644 index 00000000..1aae105c --- /dev/null +++ b/src/ptvsd/attach_script_ptvsd_pid.py @@ -0,0 +1,24 @@ +def attach(port, host, client, log_dir): + try: + if not log_dir: + log_dir = None + + import ptvsd.options + ptvsd.options.log_dir = log_dir + ptvsd.options.client = client + ptvsd.options.host = host + ptvsd.options.port = port + + import ptvsd.log + ptvsd.log.to_file() + ptvsd.log.info("Debugger successfully injected") + + if ptvsd.options.client: + from ptvsd._remote import attach + attach((host, port)) + else: + ptvsd.enable_attach((host, port)) + + except: + import traceback + traceback.print_exc() diff --git a/src/ptvsd/pydevd_hooks.py b/src/ptvsd/pydevd_hooks.py index a12377d6..07e092b4 100644 --- a/src/ptvsd/pydevd_hooks.py +++ b/src/ptvsd/pydevd_hooks.py @@ -127,9 +127,15 @@ def install(pydevd_module, address, addr = Address.from_raw(address) daemon = Daemon(**kwargs) - _start_server = (lambda p: start_server(daemon, addr.host, p)) + def _start_server(p): + ptvsd.log.debug('ptvsd: install._start_server.') + return start_server(daemon, addr.host, p) + + def _start_client(h, p): + ptvsd.log.debug('ptvsd: install._start_client.') + return start_client(daemon, h, p) + _start_server.orig = start_server - _start_client = (lambda h, p: start_client(daemon, h, p)) _start_client.orig = start_client # These are the functions pydevd invokes to get a socket to the client.