/ 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_