(root)/
Python-3.12.0/
Python/
legacy_tracing.c
       1  /* Support for legacy tracing on top of PEP 669 instrumentation
       2   * Provides callables to forward PEP 669 events to legacy events.
       3   */
       4  
       5  #include <stddef.h>
       6  #include "Python.h"
       7  #include "opcode.h"
       8  #include "pycore_ceval.h"
       9  #include "pycore_object.h"
      10  #include "pycore_sysmodule.h"
      11  
      12  typedef struct _PyLegacyEventHandler {
      13      PyObject_HEAD
      14      vectorcallfunc vectorcall;
      15      int event;
      16  } _PyLegacyEventHandler;
      17  
      18  /* The Py_tracefunc function expects the following arguments:
      19   *   obj: the trace object (PyObject *)
      20   *   frame: the current frame (PyFrameObject *)
      21   *   kind: the kind of event, see PyTrace_XXX #defines (int)
      22   *   arg: The arg (a PyObject *)
      23   */
      24  
      25  static PyObject *
      26  call_profile_func(_PyLegacyEventHandler *self, PyObject *arg)
      27  {
      28      PyThreadState *tstate = _PyThreadState_GET();
      29      if (tstate->c_profilefunc == NULL) {
      30          Py_RETURN_NONE;
      31      }
      32      PyFrameObject *frame = PyEval_GetFrame();
      33      if (frame == NULL) {
      34          PyErr_SetString(PyExc_SystemError,
      35                          "Missing frame when calling profile function.");
      36          return NULL;
      37      }
      38      Py_INCREF(frame);
      39      int err = tstate->c_profilefunc(tstate->c_profileobj, frame, self->event, arg);
      40      Py_DECREF(frame);
      41      if (err) {
      42          return NULL;
      43      }
      44      Py_RETURN_NONE;
      45  }
      46  
      47  static PyObject *
      48  sys_profile_func2(
      49      _PyLegacyEventHandler *self, PyObject *const *args,
      50      size_t nargsf, PyObject *kwnames
      51  ) {
      52      assert(kwnames == NULL);
      53      assert(PyVectorcall_NARGS(nargsf) == 2);
      54      return call_profile_func(self, Py_None);
      55  }
      56  
      57  static PyObject *
      58  sys_profile_func3(
      59      _PyLegacyEventHandler *self, PyObject *const *args,
      60      size_t nargsf, PyObject *kwnames
      61  ) {
      62      assert(kwnames == NULL);
      63      assert(PyVectorcall_NARGS(nargsf) == 3);
      64      return call_profile_func(self, args[2]);
      65  }
      66  
      67  static PyObject *
      68  sys_profile_unwind(
      69      _PyLegacyEventHandler *self, PyObject *const *args,
      70      size_t nargsf, PyObject *kwnames
      71  ) {
      72      assert(kwnames == NULL);
      73      assert(PyVectorcall_NARGS(nargsf) == 3);
      74      return call_profile_func(self, Py_None);
      75  }
      76  
      77  static PyObject *
      78  sys_profile_call_or_return(
      79      _PyLegacyEventHandler *self, PyObject *const *args,
      80      size_t nargsf, PyObject *kwnames
      81  ) {
      82      assert(kwnames == NULL);
      83      assert(PyVectorcall_NARGS(nargsf) == 4);
      84      PyObject *callable = args[2];
      85      if (PyCFunction_Check(callable)) {
      86          return call_profile_func(self, callable);
      87      }
      88      if (Py_TYPE(callable) == &PyMethodDescr_Type) {
      89          PyObject *self_arg = args[3];
      90          /* For backwards compatibility need to
      91           * convert to builtin method */
      92  
      93          /* If no arg, skip */
      94          if (self_arg == &_PyInstrumentation_MISSING) {
      95              Py_RETURN_NONE;
      96          }
      97          PyObject *meth = Py_TYPE(callable)->tp_descr_get(
      98              callable, self_arg, (PyObject*)Py_TYPE(self_arg));
      99          if (meth == NULL) {
     100              return NULL;
     101          }
     102          PyObject *res =  call_profile_func(self, meth);
     103          Py_DECREF(meth);
     104          return res;
     105      }
     106      Py_RETURN_NONE;
     107  }
     108  
     109  static PyObject *
     110  call_trace_func(_PyLegacyEventHandler *self, PyObject *arg)
     111  {
     112      PyThreadState *tstate = _PyThreadState_GET();
     113      if (tstate->c_tracefunc == NULL) {
     114          Py_RETURN_NONE;
     115      }
     116      PyFrameObject *frame = PyEval_GetFrame();
     117      if (frame == NULL) {
     118          PyErr_SetString(PyExc_SystemError,
     119                          "Missing frame when calling trace function.");
     120          return NULL;
     121      }
     122      Py_INCREF(frame);
     123      int err = tstate->c_tracefunc(tstate->c_traceobj, frame, self->event, arg);
     124      Py_DECREF(frame);
     125      if (err) {
     126          return NULL;
     127      }
     128      Py_RETURN_NONE;
     129  }
     130  
     131  static PyObject *
     132  sys_trace_exception_func(
     133      _PyLegacyEventHandler *self, PyObject *const *args,
     134      size_t nargsf, PyObject *kwnames
     135  ) {
     136      assert(kwnames == NULL);
     137      assert(PyVectorcall_NARGS(nargsf) == 3);
     138      PyObject *exc = args[2];
     139      assert(PyExceptionInstance_Check(exc));
     140      PyObject *type = (PyObject *)Py_TYPE(exc);
     141      PyObject *tb = PyException_GetTraceback(exc);
     142      if (tb == NULL) {
     143          tb = Py_NewRef(Py_None);
     144      }
     145      PyObject *tuple = PyTuple_Pack(3, type, exc, tb);
     146      Py_DECREF(tb);
     147      if (tuple == NULL) {
     148          return NULL;
     149      }
     150      PyObject *res = call_trace_func(self, tuple);
     151      Py_DECREF(tuple);
     152      return res;
     153  }
     154  
     155  static PyObject *
     156  sys_trace_func2(
     157      _PyLegacyEventHandler *self, PyObject *const *args,
     158      size_t nargsf, PyObject *kwnames
     159  ) {
     160      assert(kwnames == NULL);
     161      assert(PyVectorcall_NARGS(nargsf) == 2);
     162      return call_trace_func(self, Py_None);
     163  }
     164  
     165  static PyObject *
     166  sys_trace_func3(
     167      _PyLegacyEventHandler *self, PyObject *const *args,
     168      size_t nargsf, PyObject *kwnames
     169  ) {
     170      assert(kwnames == NULL);
     171      assert(PyVectorcall_NARGS(nargsf) == 3);
     172      return call_trace_func(self, Py_None);
     173  }
     174  
     175  static PyObject *
     176  sys_trace_return(
     177      _PyLegacyEventHandler *self, PyObject *const *args,
     178      size_t nargsf, PyObject *kwnames
     179  ) {
     180      assert(!PyErr_Occurred());
     181      assert(kwnames == NULL);
     182      assert(PyVectorcall_NARGS(nargsf) == 3);
     183      assert(PyCode_Check(args[0]));
     184      PyObject *val = args[2];
     185      PyObject *res = call_trace_func(self, val);
     186      return res;
     187  }
     188  
     189  static PyObject *
     190  sys_trace_yield(
     191      _PyLegacyEventHandler *self, PyObject *const *args,
     192      size_t nargsf, PyObject *kwnames
     193  ) {
     194      assert(kwnames == NULL);
     195      assert(PyVectorcall_NARGS(nargsf) == 3);
     196      return call_trace_func(self, args[2]);
     197  }
     198  
     199  static PyObject *
     200  sys_trace_instruction_func(
     201      _PyLegacyEventHandler *self, PyObject *const *args,
     202      size_t nargsf, PyObject *kwnames
     203  ) {
     204      assert(kwnames == NULL);
     205      assert(PyVectorcall_NARGS(nargsf) == 2);
     206      PyFrameObject *frame = PyEval_GetFrame();
     207      if (frame == NULL) {
     208          PyErr_SetString(PyExc_SystemError,
     209                          "Missing frame when calling trace function.");
     210          return NULL;
     211      }
     212      if (!frame->f_trace_opcodes) {
     213          Py_RETURN_NONE;
     214      }
     215      Py_INCREF(frame);
     216      PyThreadState *tstate = _PyThreadState_GET();
     217      int err = tstate->c_tracefunc(tstate->c_traceobj, frame, self->event, Py_None);
     218      frame->f_lineno = 0;
     219      Py_DECREF(frame);
     220      if (err) {
     221          return NULL;
     222      }
     223      Py_RETURN_NONE;
     224  }
     225  
     226  static PyObject *
     227  trace_line(
     228      PyThreadState *tstate, _PyLegacyEventHandler *self,
     229      PyFrameObject *frame, int line
     230  ) {
     231      if (!frame->f_trace_lines) {
     232          Py_RETURN_NONE;
     233      }
     234      if (line < 0) {
     235          Py_RETURN_NONE;
     236      }
     237      Py_INCREF(frame);
     238      frame->f_lineno = line;
     239      int err = tstate->c_tracefunc(tstate->c_traceobj, frame, self->event, Py_None);
     240      frame->f_lineno = 0;
     241      Py_DECREF(frame);
     242      if (err) {
     243          return NULL;
     244      }
     245      Py_RETURN_NONE;
     246  }
     247  
     248  static PyObject *
     249  sys_trace_line_func(
     250      _PyLegacyEventHandler *self, PyObject *const *args,
     251      size_t nargsf, PyObject *kwnames
     252  ) {
     253      assert(kwnames == NULL);
     254      PyThreadState *tstate = _PyThreadState_GET();
     255      if (tstate->c_tracefunc == NULL) {
     256          Py_RETURN_NONE;
     257      }
     258      assert(PyVectorcall_NARGS(nargsf) == 2);
     259      int line = _PyLong_AsInt(args[1]);
     260      assert(line >= 0);
     261      PyFrameObject *frame = PyEval_GetFrame();
     262      if (frame == NULL) {
     263          PyErr_SetString(PyExc_SystemError,
     264                          "Missing frame when calling trace function.");
     265          return NULL;
     266      }
     267      assert(args[0] == (PyObject *)frame->f_frame->f_code);
     268      return trace_line(tstate, self, frame, line);
     269  }
     270  
     271  /* sys.settrace generates line events for all backward
     272   * edges, even if on the same line.
     273   * Handle that case here */
     274  static PyObject *
     275  sys_trace_jump_func(
     276      _PyLegacyEventHandler *self, PyObject *const *args,
     277      size_t nargsf, PyObject *kwnames
     278  ) {
     279      assert(kwnames == NULL);
     280      PyThreadState *tstate = _PyThreadState_GET();
     281      if (tstate->c_tracefunc == NULL) {
     282          Py_RETURN_NONE;
     283      }
     284      assert(PyVectorcall_NARGS(nargsf) == 3);
     285      int from = _PyLong_AsInt(args[1])/sizeof(_Py_CODEUNIT);
     286      assert(from >= 0);
     287      int to = _PyLong_AsInt(args[2])/sizeof(_Py_CODEUNIT);
     288      assert(to >= 0);
     289      if (to > from) {
     290          /* Forward jump */
     291          return &_PyInstrumentation_DISABLE;
     292      }
     293      PyCodeObject *code = (PyCodeObject *)args[0];
     294      assert(PyCode_Check(code));
     295      /* We can call _Py_Instrumentation_GetLine because we always set
     296      * line events for tracing */
     297      int to_line = _Py_Instrumentation_GetLine(code, to);
     298      int from_line = _Py_Instrumentation_GetLine(code, from);
     299      if (to_line != from_line) {
     300          /* Will be handled by target INSTRUMENTED_LINE */
     301          return &_PyInstrumentation_DISABLE;
     302      }
     303      PyFrameObject *frame = PyEval_GetFrame();
     304      if (frame == NULL) {
     305          PyErr_SetString(PyExc_SystemError,
     306                          "Missing frame when calling trace function.");
     307          return NULL;
     308      }
     309      assert(code == frame->f_frame->f_code);
     310      if (!frame->f_trace_lines) {
     311          Py_RETURN_NONE;
     312      }
     313      return trace_line(tstate, self, frame, to_line);
     314  }
     315  
     316  PyTypeObject _PyLegacyEventHandler_Type = {
     317      PyVarObject_HEAD_INIT(&PyType_Type, 0)
     318      "sys.legacy_event_handler",
     319      sizeof(_PyLegacyEventHandler),
     320      .tp_dealloc = (destructor)PyObject_Free,
     321      .tp_vectorcall_offset = offsetof(_PyLegacyEventHandler, vectorcall),
     322      .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
     323          Py_TPFLAGS_HAVE_VECTORCALL | Py_TPFLAGS_DISALLOW_INSTANTIATION,
     324      .tp_call = PyVectorcall_Call,
     325  };
     326  
     327  static int
     328  set_callbacks(int tool, vectorcallfunc vectorcall, int legacy_event, int event1, int event2)
     329  {
     330      _PyLegacyEventHandler *callback =
     331          PyObject_NEW(_PyLegacyEventHandler, &_PyLegacyEventHandler_Type);
     332      if (callback == NULL) {
     333          return -1;
     334      }
     335      callback->vectorcall = vectorcall;
     336      callback->event = legacy_event;
     337      Py_XDECREF(_PyMonitoring_RegisterCallback(tool, event1, (PyObject *)callback));
     338      if (event2 >= 0) {
     339          Py_XDECREF(_PyMonitoring_RegisterCallback(tool, event2, (PyObject *)callback));
     340      }
     341      Py_DECREF(callback);
     342      return 0;
     343  }
     344  
     345  #ifndef NDEBUG
     346  /* Ensure that tstate is valid: sanity check for PyEval_AcquireThread() and
     347     PyEval_RestoreThread(). Detect if tstate memory was freed. It can happen
     348     when a thread continues to run after Python finalization, especially
     349     daemon threads. */
     350  static int
     351  is_tstate_valid(PyThreadState *tstate)
     352  {
     353      assert(!_PyMem_IsPtrFreed(tstate));
     354      assert(!_PyMem_IsPtrFreed(tstate->interp));
     355      return 1;
     356  }
     357  #endif
     358  
     359  int
     360  _PyEval_SetProfile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
     361  {
     362      assert(is_tstate_valid(tstate));
     363      /* The caller must hold the GIL */
     364      assert(PyGILState_Check());
     365  
     366      /* Call _PySys_Audit() in the context of the current thread state,
     367         even if tstate is not the current thread state. */
     368      PyThreadState *current_tstate = _PyThreadState_GET();
     369      if (_PySys_Audit(current_tstate, "sys.setprofile", NULL) < 0) {
     370          return -1;
     371      }
     372      /* Setup PEP 669 monitoring callbacks and events. */
     373      if (!tstate->interp->sys_profile_initialized) {
     374          tstate->interp->sys_profile_initialized = true;
     375          if (set_callbacks(PY_MONITORING_SYS_PROFILE_ID,
     376              (vectorcallfunc)sys_profile_func2, PyTrace_CALL,
     377                          PY_MONITORING_EVENT_PY_START, PY_MONITORING_EVENT_PY_RESUME)) {
     378              return -1;
     379          }
     380          if (set_callbacks(PY_MONITORING_SYS_PROFILE_ID,
     381              (vectorcallfunc)sys_profile_func3, PyTrace_RETURN,
     382                          PY_MONITORING_EVENT_PY_RETURN, PY_MONITORING_EVENT_PY_YIELD)) {
     383              return -1;
     384          }
     385          if (set_callbacks(PY_MONITORING_SYS_PROFILE_ID,
     386              (vectorcallfunc)sys_profile_unwind, PyTrace_RETURN,
     387                          PY_MONITORING_EVENT_PY_UNWIND, -1)) {
     388              return -1;
     389          }
     390          if (set_callbacks(PY_MONITORING_SYS_PROFILE_ID,
     391              (vectorcallfunc)sys_profile_call_or_return, PyTrace_C_CALL,
     392                          PY_MONITORING_EVENT_CALL, -1)) {
     393              return -1;
     394          }
     395          if (set_callbacks(PY_MONITORING_SYS_PROFILE_ID,
     396              (vectorcallfunc)sys_profile_call_or_return, PyTrace_C_RETURN,
     397                          PY_MONITORING_EVENT_C_RETURN, -1)) {
     398              return -1;
     399          }
     400          if (set_callbacks(PY_MONITORING_SYS_PROFILE_ID,
     401              (vectorcallfunc)sys_profile_call_or_return, PyTrace_C_EXCEPTION,
     402                          PY_MONITORING_EVENT_C_RAISE, -1)) {
     403              return -1;
     404          }
     405      }
     406  
     407      int delta = (func != NULL) - (tstate->c_profilefunc != NULL);
     408      tstate->c_profilefunc = func;
     409      PyObject *old_profileobj = tstate->c_profileobj;
     410      tstate->c_profileobj = Py_XNewRef(arg);
     411      Py_XDECREF(old_profileobj);
     412      tstate->interp->sys_profiling_threads += delta;
     413      assert(tstate->interp->sys_profiling_threads >= 0);
     414  
     415      uint32_t events = 0;
     416      if (tstate->interp->sys_profiling_threads) {
     417          events =
     418              (1 << PY_MONITORING_EVENT_PY_START) | (1 << PY_MONITORING_EVENT_PY_RESUME) |
     419              (1 << PY_MONITORING_EVENT_PY_RETURN) | (1 << PY_MONITORING_EVENT_PY_YIELD) |
     420              (1 << PY_MONITORING_EVENT_CALL) | (1 << PY_MONITORING_EVENT_PY_UNWIND);
     421      }
     422      return _PyMonitoring_SetEvents(PY_MONITORING_SYS_PROFILE_ID, events);
     423  }
     424  
     425  int
     426  _PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
     427  {
     428      assert(is_tstate_valid(tstate));
     429      /* The caller must hold the GIL */
     430      assert(PyGILState_Check());
     431  
     432      /* Call _PySys_Audit() in the context of the current thread state,
     433         even if tstate is not the current thread state. */
     434      PyThreadState *current_tstate = _PyThreadState_GET();
     435      if (_PySys_Audit(current_tstate, "sys.settrace", NULL) < 0) {
     436          return -1;
     437      }
     438  
     439      assert(tstate->interp->sys_tracing_threads >= 0);
     440      /* Setup PEP 669 monitoring callbacks and events. */
     441      if (!tstate->interp->sys_trace_initialized) {
     442          tstate->interp->sys_trace_initialized = true;
     443          if (set_callbacks(PY_MONITORING_SYS_TRACE_ID,
     444              (vectorcallfunc)sys_trace_func2, PyTrace_CALL,
     445                          PY_MONITORING_EVENT_PY_START, PY_MONITORING_EVENT_PY_RESUME)) {
     446              return -1;
     447          }
     448          if (set_callbacks(PY_MONITORING_SYS_TRACE_ID,
     449              (vectorcallfunc)sys_trace_func3, PyTrace_CALL,
     450                          PY_MONITORING_EVENT_PY_THROW, -1)) {
     451              return -1;
     452          }
     453          if (set_callbacks(PY_MONITORING_SYS_TRACE_ID,
     454              (vectorcallfunc)sys_trace_return, PyTrace_RETURN,
     455                          PY_MONITORING_EVENT_PY_RETURN, -1)) {
     456              return -1;
     457          }
     458          if (set_callbacks(PY_MONITORING_SYS_TRACE_ID,
     459              (vectorcallfunc)sys_trace_yield, PyTrace_RETURN,
     460                          PY_MONITORING_EVENT_PY_YIELD, -1)) {
     461              return -1;
     462          }
     463          if (set_callbacks(PY_MONITORING_SYS_TRACE_ID,
     464              (vectorcallfunc)sys_trace_exception_func, PyTrace_EXCEPTION,
     465                          PY_MONITORING_EVENT_RAISE, PY_MONITORING_EVENT_STOP_ITERATION)) {
     466              return -1;
     467          }
     468          if (set_callbacks(PY_MONITORING_SYS_TRACE_ID,
     469              (vectorcallfunc)sys_trace_line_func, PyTrace_LINE,
     470                          PY_MONITORING_EVENT_LINE, -1)) {
     471              return -1;
     472          }
     473          if (set_callbacks(PY_MONITORING_SYS_TRACE_ID,
     474              (vectorcallfunc)sys_trace_func3, PyTrace_RETURN,
     475                          PY_MONITORING_EVENT_PY_UNWIND, -1)) {
     476              return -1;
     477          }
     478          if (set_callbacks(PY_MONITORING_SYS_TRACE_ID,
     479              (vectorcallfunc)sys_trace_jump_func, PyTrace_LINE,
     480                          PY_MONITORING_EVENT_JUMP, -1)) {
     481              return -1;
     482          }
     483          if (set_callbacks(PY_MONITORING_SYS_TRACE_ID,
     484              (vectorcallfunc)sys_trace_instruction_func, PyTrace_OPCODE,
     485                          PY_MONITORING_EVENT_INSTRUCTION, -1)) {
     486              return -1;
     487          }
     488      }
     489  
     490      int delta = (func != NULL) - (tstate->c_tracefunc != NULL);
     491      tstate->c_tracefunc = func;
     492      PyObject *old_traceobj = tstate->c_traceobj;
     493      tstate->c_traceobj = Py_XNewRef(arg);
     494      Py_XDECREF(old_traceobj);
     495      tstate->interp->sys_tracing_threads += delta;
     496      assert(tstate->interp->sys_tracing_threads >= 0);
     497  
     498      uint32_t events = 0;
     499      if (tstate->interp->sys_tracing_threads) {
     500          events =
     501              (1 << PY_MONITORING_EVENT_PY_START) | (1 << PY_MONITORING_EVENT_PY_RESUME) |
     502              (1 << PY_MONITORING_EVENT_PY_RETURN) | (1 << PY_MONITORING_EVENT_PY_YIELD) |
     503              (1 << PY_MONITORING_EVENT_RAISE) | (1 << PY_MONITORING_EVENT_LINE) |
     504              (1 << PY_MONITORING_EVENT_JUMP) | (1 << PY_MONITORING_EVENT_BRANCH) |
     505              (1 << PY_MONITORING_EVENT_PY_UNWIND) | (1 << PY_MONITORING_EVENT_PY_THROW) |
     506              (1 << PY_MONITORING_EVENT_STOP_ITERATION) |
     507              (1 << PY_MONITORING_EVENT_EXCEPTION_HANDLED);
     508          if (tstate->interp->f_opcode_trace_set) {
     509              events |= (1 << PY_MONITORING_EVENT_INSTRUCTION);
     510          }
     511      }
     512      return _PyMonitoring_SetEvents(PY_MONITORING_SYS_TRACE_ID, events);
     513  }