/ lib / debugpy / _vendored / pydevd / pydevd_attach_to_process / common / py_custom_pyeval_settrace.hpp
py_custom_pyeval_settrace.hpp
1 #ifndef _PY_CUSTOM_PYEVAL_SETTRACE_HPP_ 2 #define _PY_CUSTOM_PYEVAL_SETTRACE_HPP_ 3 4 #include "python.h" 5 #include "py_utils.hpp" 6 #include "py_custom_pyeval_settrace_common.hpp" 7 #include "py_custom_pyeval_settrace_310.hpp" 8 #include "py_custom_pyeval_settrace_311.hpp" 9 10 // On Python 3.7 onwards the thread state is not kept in PyThread_set_key_value (rather 11 // it uses PyThread_tss_set using PyThread_tss_set(&_PyRuntime.gilstate.autoTSSkey, (void *)tstate) 12 // and we don't have access to that key from here (thus, we can't use the previous approach which 13 // made CPython think that the current thread had the thread state where we wanted to set the tracing). 14 // 15 // So, the solution implemented here is not faking that change and reimplementing PyEval_SetTrace. 16 // The implementation is mostly the same from the one in CPython, but we have one shortcoming: 17 // 18 // When CPython sets the tracing for a thread it increments _Py_TracingPossible (actually 19 // _PyRuntime.ceval.tracing_possible). This implementation has one issue: it only works on 20 // deltas when the tracing is set (so, a settrace(func) will increase the _Py_TracingPossible global value and a 21 // settrace(None) will decrease it, but when a thread dies it's kept as is and is not decreased). 22 // -- as we don't currently have access to _PyRuntime we have to create a thread, set the tracing 23 // and let it die so that the count is increased (this is really hacky, but better than having 24 // to create a local copy of the whole _PyRuntime (defined in pystate.h with several inner structs) 25 // which would need to be kept up to date for each new CPython version just to increment that variable). 26 27 28 29 /** 30 * This version is used in internalInitializeCustomPyEvalSetTrace->pyObject_FastCallDict on older 31 * versions of CPython (pre 3.7). 32 */ 33 static PyObject * 34 PyObject_FastCallDictCustom(PyObject* callback, PyObject *stack[3], int ignoredStackSizeAlways3, void* ignored) 35 { 36 PyObject *args = internalInitializeCustomPyEvalSetTrace->pyTuple_New(3); 37 PyObject *result; 38 39 if (args == NULL) { 40 return NULL; 41 } 42 43 IncRef(stack[0]); 44 IncRef(stack[1]); 45 IncRef(stack[2]); 46 47 // I.e.: same thing as: PyTuple_SET_ITEM(args, 0, stack[0]); 48 reinterpret_cast<PyTupleObject *>(args)->ob_item[0] = stack[0]; 49 reinterpret_cast<PyTupleObject *>(args)->ob_item[1] = stack[1]; 50 reinterpret_cast<PyTupleObject *>(args)->ob_item[2] = stack[2]; 51 52 /* call the Python-level function */ 53 result = internalInitializeCustomPyEvalSetTrace->pyEval_CallObjectWithKeywords(callback, args, (PyObject*)NULL); 54 55 /* cleanup */ 56 DecRef(args, internalInitializeCustomPyEvalSetTrace->isDebug); 57 return result; 58 } 59 60 static PyObject * 61 InternalCallTrampoline(PyObject* callback, 62 PyFrameObjectBaseUpTo39 *frame, int what, PyObject *arg) 63 { 64 PyObject *result; 65 PyObject *stack[3]; 66 67 // Note: this is commented out from CPython (we shouldn't need it and it adds a reasonable overhead). 68 // if (PyFrame_FastToLocalsWithError(frame) < 0) { 69 // return NULL; 70 // } 71 // 72 stack[0] = (PyObject *)frame; 73 stack[1] = InternalWhatstrings_37[what]; 74 stack[2] = (arg != NULL) ? arg : internalInitializeCustomPyEvalSetTrace->pyNone; 75 76 77 // Helpers to print info. 78 // printf("%s\n", internalInitializeCustomPyEvalSetTrace->pyUnicode_AsUTF8(internalInitializeCustomPyEvalSetTrace->pyObject_Repr((PyObject *)stack[0]))); 79 // printf("%s\n", internalInitializeCustomPyEvalSetTrace->pyUnicode_AsUTF8(internalInitializeCustomPyEvalSetTrace->pyObject_Repr((PyObject *)stack[1]))); 80 // printf("%s\n", internalInitializeCustomPyEvalSetTrace->pyUnicode_AsUTF8(internalInitializeCustomPyEvalSetTrace->pyObject_Repr((PyObject *)stack[2]))); 81 // printf("%s\n", internalInitializeCustomPyEvalSetTrace->pyUnicode_AsUTF8(internalInitializeCustomPyEvalSetTrace->pyObject_Repr((PyObject *)callback))); 82 83 // call the Python-level function 84 // result = _PyObject_FastCall(callback, stack, 3); 85 // 86 // Note that _PyObject_FastCall is actually a define: 87 // #define _PyObject_FastCall(func, args, nargs) _PyObject_FastCallDict((func), (args), (nargs), NULL) 88 89 result = internalInitializeCustomPyEvalSetTrace->pyObject_FastCallDict(callback, stack, 3, NULL); 90 91 92 // Note: this is commented out from CPython (we shouldn't need it and it adds a reasonable overhead). 93 // PyFrame_LocalsToFast(frame, 1); 94 95 if (result == NULL) { 96 internalInitializeCustomPyEvalSetTrace->pyTraceBack_Here(frame); 97 } 98 99 return result; 100 } 101 102 static int 103 InternalTraceTrampoline(PyObject *self, PyFrameObject *frameParam, 104 int what, PyObject *arg) 105 { 106 PyObject *callback; 107 PyObject *result; 108 109 PyFrameObjectBaseUpTo39 *frame = reinterpret_cast<PyFrameObjectBaseUpTo39*>(frameParam); 110 111 if (what == PyTrace_CALL){ 112 callback = self; 113 } else { 114 callback = frame->f_trace; 115 } 116 117 if (callback == NULL){ 118 return 0; 119 } 120 121 result = InternalCallTrampoline(callback, frame, what, arg); 122 if (result == NULL) { 123 // Note: calling the original sys.settrace here. 124 internalInitializeCustomPyEvalSetTrace->pyEval_SetTrace(NULL, NULL); 125 PyObject *temp_f_trace = frame->f_trace; 126 frame->f_trace = NULL; 127 if(temp_f_trace != NULL){ 128 DecRef(temp_f_trace, internalInitializeCustomPyEvalSetTrace->isDebug); 129 } 130 return -1; 131 } 132 if (result != internalInitializeCustomPyEvalSetTrace->pyNone) { 133 PyObject *tmp = frame->f_trace; 134 frame->f_trace = result; 135 DecRef(tmp, internalInitializeCustomPyEvalSetTrace->isDebug); 136 } 137 else { 138 DecRef(result, internalInitializeCustomPyEvalSetTrace->isDebug); 139 } 140 return 0; 141 } 142 143 // Based on ceval.c (PyEval_SetTrace(Py_tracefunc func, PyObject *arg)) 144 template<typename T> 145 void InternalPySetTrace_Template(T tstate, PyObjectHolder* traceFunc, bool isDebug) 146 { 147 PyObject *temp = tstate->c_traceobj; 148 149 // We can't increase _Py_TracingPossible. Everything else should be equal to CPython. 150 // runtime->ceval.tracing_possible += (func != NULL) - (tstate->c_tracefunc != NULL); 151 152 PyObject *arg = traceFunc->ToPython(); 153 IncRef(arg); 154 tstate->c_tracefunc = NULL; 155 tstate->c_traceobj = NULL; 156 /* Must make sure that profiling is not ignored if 'temp' is freed */ 157 tstate->use_tracing = tstate->c_profilefunc != NULL; 158 if(temp != NULL){ 159 DecRef(temp, isDebug); 160 } 161 tstate->c_tracefunc = InternalTraceTrampoline; 162 tstate->c_traceobj = arg; 163 /* Flag that tracing or profiling is turned on */ 164 tstate->use_tracing = ((InternalTraceTrampoline != NULL) 165 || (tstate->c_profilefunc != NULL)); 166 167 }; 168 169 170 void InternalPySetTrace(PyThreadState* curThread, PyObjectHolder* traceFunc, bool isDebug, PythonVersion version) 171 { 172 if (PyThreadState_25_27::IsFor(version)) { 173 InternalPySetTrace_Template<PyThreadState_25_27*>(reinterpret_cast<PyThreadState_25_27*>(curThread), traceFunc, isDebug); 174 } else if (PyThreadState_30_33::IsFor(version)) { 175 InternalPySetTrace_Template<PyThreadState_30_33*>(reinterpret_cast<PyThreadState_30_33*>(curThread), traceFunc, isDebug); 176 } else if (PyThreadState_34_36::IsFor(version)) { 177 InternalPySetTrace_Template<PyThreadState_34_36*>(reinterpret_cast<PyThreadState_34_36*>(curThread), traceFunc, isDebug); 178 } else if (PyThreadState_37_38::IsFor(version)) { 179 InternalPySetTrace_Template<PyThreadState_37_38*>(reinterpret_cast<PyThreadState_37_38*>(curThread), traceFunc, isDebug); 180 } else if (PyThreadState_39::IsFor(version)) { 181 InternalPySetTrace_Template<PyThreadState_39*>(reinterpret_cast<PyThreadState_39*>(curThread), traceFunc, isDebug); 182 } else if (PyThreadState_310::IsFor(version)) { 183 // 3.10 has other changes on the actual algorithm (use_tracing is per-frame now), so, we have a full new version for it. 184 InternalPySetTrace_Template310<PyThreadState_310*>(reinterpret_cast<PyThreadState_310*>(curThread), traceFunc, isDebug); 185 } else if (PyThreadState_311::IsFor(version)) { 186 InternalPySetTrace_Template311<PyThreadState_311*>(reinterpret_cast<PyThreadState_311*>(curThread), traceFunc, isDebug); 187 } else { 188 printf("Unable to set trace to target thread with Python version: %d", version); 189 } 190 } 191 192 193 #endif //_PY_CUSTOM_PYEVAL_SETTRACE_HPP_