| #include "parts.h" |
| #include "util.h" |
| |
| #include "monitoring.h" |
| |
| #define Py_BUILD_CORE |
| #include "internal/pycore_instruments.h" |
| |
| typedef struct { |
| PyObject_HEAD |
| PyMonitoringState *monitoring_states; |
| uint64_t version; |
| int num_events; |
| /* Other fields */ |
| } PyCodeLikeObject; |
| |
| |
| static PyObject * |
| CodeLike_new(PyTypeObject *type, PyObject *args, PyObject *kwds) |
| { |
| int num_events; |
| if (!PyArg_ParseTuple(args, "i", &num_events)) { |
| return NULL; |
| } |
| PyMonitoringState *states = (PyMonitoringState *)PyMem_Calloc( |
| num_events, sizeof(PyMonitoringState)); |
| if (states == NULL) { |
| return NULL; |
| } |
| PyCodeLikeObject *self = (PyCodeLikeObject *) type->tp_alloc(type, 0); |
| if (self != NULL) { |
| self->version = 0; |
| self->monitoring_states = states; |
| self->num_events = num_events; |
| } |
| else { |
| PyMem_Free(states); |
| } |
| return (PyObject *) self; |
| } |
| |
| static void |
| CodeLike_dealloc(PyCodeLikeObject *self) |
| { |
| if (self->monitoring_states) { |
| PyMem_Free(self->monitoring_states); |
| } |
| Py_TYPE(self)->tp_free((PyObject *) self); |
| } |
| |
| static PyObject * |
| CodeLike_str(PyCodeLikeObject *self) |
| { |
| PyObject *res = NULL; |
| PyObject *sep = NULL; |
| PyObject *parts = NULL; |
| if (self->monitoring_states) { |
| parts = PyList_New(0); |
| if (parts == NULL) { |
| goto end; |
| } |
| |
| PyObject *heading = PyUnicode_FromString("PyCodeLikeObject"); |
| if (heading == NULL) { |
| goto end; |
| } |
| int err = PyList_Append(parts, heading); |
| Py_DECREF(heading); |
| if (err < 0) { |
| goto end; |
| } |
| |
| for (int i = 0; i < self->num_events; i++) { |
| PyObject *part = PyUnicode_FromFormat(" %d", self->monitoring_states[i].active); |
| if (part == NULL) { |
| goto end; |
| } |
| int err = PyList_Append(parts, part); |
| Py_XDECREF(part); |
| if (err < 0) { |
| goto end; |
| } |
| } |
| sep = PyUnicode_FromString(": "); |
| if (sep == NULL) { |
| goto end; |
| } |
| res = PyUnicode_Join(sep, parts); |
| } |
| end: |
| Py_XDECREF(sep); |
| Py_XDECREF(parts); |
| return res; |
| } |
| |
| static PyTypeObject PyCodeLike_Type = { |
| .ob_base = PyVarObject_HEAD_INIT(NULL, 0) |
| .tp_name = "monitoring.CodeLike", |
| .tp_doc = PyDoc_STR("CodeLike objects"), |
| .tp_basicsize = sizeof(PyCodeLikeObject), |
| .tp_itemsize = 0, |
| .tp_flags = Py_TPFLAGS_DEFAULT, |
| .tp_new = CodeLike_new, |
| .tp_dealloc = (destructor) CodeLike_dealloc, |
| .tp_str = (reprfunc) CodeLike_str, |
| }; |
| |
| #define RAISE_UNLESS_CODELIKE(v) if (!Py_IS_TYPE((v), &PyCodeLike_Type)) { \ |
| PyErr_Format(PyExc_TypeError, "expected a code-like, got %s", Py_TYPE(v)->tp_name); \ |
| return NULL; \ |
| } |
| |
| /*******************************************************************/ |
| |
| static PyMonitoringState * |
| setup_fire(PyObject *codelike, int offset, PyObject *exc) |
| { |
| RAISE_UNLESS_CODELIKE(codelike); |
| PyCodeLikeObject *cl = ((PyCodeLikeObject *)codelike); |
| assert(offset >= 0 && offset < cl->num_events); |
| PyMonitoringState *state = &cl->monitoring_states[offset]; |
| |
| if (exc != NULL) { |
| PyErr_SetRaisedException(Py_NewRef(exc)); |
| } |
| return state; |
| } |
| |
| static int |
| teardown_fire(int res, PyMonitoringState *state, PyObject *exception) |
| { |
| if (res == -1) { |
| return -1; |
| } |
| if (exception) { |
| assert(PyErr_Occurred()); |
| assert(((PyObject*)Py_TYPE(exception)) == PyErr_Occurred()); |
| } |
| |
| else { |
| assert(!PyErr_Occurred()); |
| } |
| PyErr_Clear(); |
| return state->active; |
| } |
| |
| static PyObject * |
| fire_event_py_start(PyObject *self, PyObject *args) |
| { |
| PyObject *codelike; |
| int offset; |
| if (!PyArg_ParseTuple(args, "Oi", &codelike, &offset)) { |
| return NULL; |
| } |
| PyObject *exception = NULL; |
| PyMonitoringState *state = setup_fire(codelike, offset, exception); |
| if (state == NULL) { |
| return NULL; |
| } |
| int res = PyMonitoring_FirePyStartEvent(state, codelike, offset); |
| RETURN_INT(teardown_fire(res, state, exception)); |
| } |
| |
| static PyObject * |
| fire_event_py_resume(PyObject *self, PyObject *args) |
| { |
| PyObject *codelike; |
| int offset; |
| if (!PyArg_ParseTuple(args, "Oi", &codelike, &offset)) { |
| return NULL; |
| } |
| PyObject *exception = NULL; |
| PyMonitoringState *state = setup_fire(codelike, offset, exception); |
| if (state == NULL) { |
| return NULL; |
| } |
| int res = PyMonitoring_FirePyResumeEvent(state, codelike, offset); |
| RETURN_INT(teardown_fire(res, state, exception)); |
| } |
| |
| static PyObject * |
| fire_event_py_return(PyObject *self, PyObject *args) |
| { |
| PyObject *codelike; |
| int offset; |
| PyObject *retval; |
| if (!PyArg_ParseTuple(args, "OiO", &codelike, &offset, &retval)) { |
| return NULL; |
| } |
| PyObject *exception = NULL; |
| PyMonitoringState *state = setup_fire(codelike, offset, exception); |
| if (state == NULL) { |
| return NULL; |
| } |
| int res = PyMonitoring_FirePyReturnEvent(state, codelike, offset, retval); |
| RETURN_INT(teardown_fire(res, state, exception)); |
| } |
| |
| static PyObject * |
| fire_event_c_return(PyObject *self, PyObject *args) |
| { |
| PyObject *codelike; |
| int offset; |
| PyObject *retval; |
| if (!PyArg_ParseTuple(args, "OiO", &codelike, &offset, &retval)) { |
| return NULL; |
| } |
| PyObject *exception = NULL; |
| PyMonitoringState *state = setup_fire(codelike, offset, exception); |
| if (state == NULL) { |
| return NULL; |
| } |
| int res = PyMonitoring_FireCReturnEvent(state, codelike, offset, retval); |
| RETURN_INT(teardown_fire(res, state, exception)); |
| } |
| |
| static PyObject * |
| fire_event_py_yield(PyObject *self, PyObject *args) |
| { |
| PyObject *codelike; |
| int offset; |
| PyObject *retval; |
| if (!PyArg_ParseTuple(args, "OiO", &codelike, &offset, &retval)) { |
| return NULL; |
| } |
| PyObject *exception = NULL; |
| PyMonitoringState *state = setup_fire(codelike, offset, exception); |
| if (state == NULL) { |
| return NULL; |
| } |
| int res = PyMonitoring_FirePyYieldEvent(state, codelike, offset, retval); |
| RETURN_INT(teardown_fire(res, state, exception)); |
| } |
| |
| static PyObject * |
| fire_event_call(PyObject *self, PyObject *args) |
| { |
| PyObject *codelike; |
| int offset; |
| PyObject *callable, *arg0; |
| if (!PyArg_ParseTuple(args, "OiOO", &codelike, &offset, &callable, &arg0)) { |
| return NULL; |
| } |
| PyObject *exception = NULL; |
| PyMonitoringState *state = setup_fire(codelike, offset, exception); |
| if (state == NULL) { |
| return NULL; |
| } |
| int res = PyMonitoring_FireCallEvent(state, codelike, offset, callable, arg0); |
| RETURN_INT(teardown_fire(res, state, exception)); |
| } |
| |
| static PyObject * |
| fire_event_line(PyObject *self, PyObject *args) |
| { |
| PyObject *codelike; |
| int offset, lineno; |
| if (!PyArg_ParseTuple(args, "Oii", &codelike, &offset, &lineno)) { |
| return NULL; |
| } |
| PyObject *exception = NULL; |
| PyMonitoringState *state = setup_fire(codelike, offset, exception); |
| if (state == NULL) { |
| return NULL; |
| } |
| int res = PyMonitoring_FireLineEvent(state, codelike, offset, lineno); |
| RETURN_INT(teardown_fire(res, state, exception)); |
| } |
| |
| static PyObject * |
| fire_event_jump(PyObject *self, PyObject *args) |
| { |
| PyObject *codelike; |
| int offset; |
| PyObject *target_offset; |
| if (!PyArg_ParseTuple(args, "OiO", &codelike, &offset, &target_offset)) { |
| return NULL; |
| } |
| PyObject *exception = NULL; |
| PyMonitoringState *state = setup_fire(codelike, offset, exception); |
| if (state == NULL) { |
| return NULL; |
| } |
| int res = PyMonitoring_FireJumpEvent(state, codelike, offset, target_offset); |
| RETURN_INT(teardown_fire(res, state, exception)); |
| } |
| |
| static PyObject * |
| fire_event_branch(PyObject *self, PyObject *args) |
| { |
| PyObject *codelike; |
| int offset; |
| PyObject *target_offset; |
| if (!PyArg_ParseTuple(args, "OiO", &codelike, &offset, &target_offset)) { |
| return NULL; |
| } |
| PyObject *exception = NULL; |
| PyMonitoringState *state = setup_fire(codelike, offset, exception); |
| if (state == NULL) { |
| return NULL; |
| } |
| int res = PyMonitoring_FireBranchEvent(state, codelike, offset, target_offset); |
| RETURN_INT(teardown_fire(res, state, exception)); |
| } |
| |
| static PyObject * |
| fire_event_py_throw(PyObject *self, PyObject *args) |
| { |
| PyObject *codelike; |
| int offset; |
| PyObject *exception; |
| if (!PyArg_ParseTuple(args, "OiO", &codelike, &offset, &exception)) { |
| return NULL; |
| } |
| NULLABLE(exception); |
| PyMonitoringState *state = setup_fire(codelike, offset, exception); |
| if (state == NULL) { |
| return NULL; |
| } |
| int res = PyMonitoring_FirePyThrowEvent(state, codelike, offset); |
| RETURN_INT(teardown_fire(res, state, exception)); |
| } |
| |
| static PyObject * |
| fire_event_raise(PyObject *self, PyObject *args) |
| { |
| PyObject *codelike; |
| int offset; |
| PyObject *exception; |
| if (!PyArg_ParseTuple(args, "OiO", &codelike, &offset, &exception)) { |
| return NULL; |
| } |
| NULLABLE(exception); |
| PyMonitoringState *state = setup_fire(codelike, offset, exception); |
| if (state == NULL) { |
| return NULL; |
| } |
| int res = PyMonitoring_FireRaiseEvent(state, codelike, offset); |
| RETURN_INT(teardown_fire(res, state, exception)); |
| } |
| |
| static PyObject * |
| fire_event_c_raise(PyObject *self, PyObject *args) |
| { |
| PyObject *codelike; |
| int offset; |
| PyObject *exception; |
| if (!PyArg_ParseTuple(args, "OiO", &codelike, &offset, &exception)) { |
| return NULL; |
| } |
| NULLABLE(exception); |
| PyMonitoringState *state = setup_fire(codelike, offset, exception); |
| if (state == NULL) { |
| return NULL; |
| } |
| int res = PyMonitoring_FireCRaiseEvent(state, codelike, offset); |
| RETURN_INT(teardown_fire(res, state, exception)); |
| } |
| |
| static PyObject * |
| fire_event_reraise(PyObject *self, PyObject *args) |
| { |
| PyObject *codelike; |
| int offset; |
| PyObject *exception; |
| if (!PyArg_ParseTuple(args, "OiO", &codelike, &offset, &exception)) { |
| return NULL; |
| } |
| NULLABLE(exception); |
| PyMonitoringState *state = setup_fire(codelike, offset, exception); |
| if (state == NULL) { |
| return NULL; |
| } |
| int res = PyMonitoring_FireReraiseEvent(state, codelike, offset); |
| RETURN_INT(teardown_fire(res, state, exception)); |
| } |
| |
| static PyObject * |
| fire_event_exception_handled(PyObject *self, PyObject *args) |
| { |
| PyObject *codelike; |
| int offset; |
| PyObject *exception; |
| if (!PyArg_ParseTuple(args, "OiO", &codelike, &offset, &exception)) { |
| return NULL; |
| } |
| NULLABLE(exception); |
| PyMonitoringState *state = setup_fire(codelike, offset, exception); |
| if (state == NULL) { |
| return NULL; |
| } |
| int res = PyMonitoring_FireExceptionHandledEvent(state, codelike, offset); |
| RETURN_INT(teardown_fire(res, state, exception)); |
| } |
| |
| static PyObject * |
| fire_event_py_unwind(PyObject *self, PyObject *args) |
| { |
| PyObject *codelike; |
| int offset; |
| PyObject *exception; |
| if (!PyArg_ParseTuple(args, "OiO", &codelike, &offset, &exception)) { |
| return NULL; |
| } |
| NULLABLE(exception); |
| PyMonitoringState *state = setup_fire(codelike, offset, exception); |
| if (state == NULL) { |
| return NULL; |
| } |
| int res = PyMonitoring_FirePyUnwindEvent(state, codelike, offset); |
| RETURN_INT(teardown_fire(res, state, exception)); |
| } |
| |
| static PyObject * |
| fire_event_stop_iteration(PyObject *self, PyObject *args) |
| { |
| PyObject *codelike; |
| int offset; |
| PyObject *value; |
| if (!PyArg_ParseTuple(args, "OiO", &codelike, &offset, &value)) { |
| return NULL; |
| } |
| NULLABLE(value); |
| PyObject *exception = NULL; |
| PyMonitoringState *state = setup_fire(codelike, offset, exception); |
| if (state == NULL) { |
| return NULL; |
| } |
| int res = PyMonitoring_FireStopIterationEvent(state, codelike, offset, value); |
| RETURN_INT(teardown_fire(res, state, exception)); |
| } |
| |
| /*******************************************************************/ |
| |
| static PyObject * |
| enter_scope(PyObject *self, PyObject *args) |
| { |
| PyObject *codelike; |
| int event1, event2=0; |
| Py_ssize_t num_events = PyTuple_Size(args) - 1; |
| if (num_events == 1) { |
| if (!PyArg_ParseTuple(args, "Oi", &codelike, &event1)) { |
| return NULL; |
| } |
| } |
| else { |
| assert(num_events == 2); |
| if (!PyArg_ParseTuple(args, "Oii", &codelike, &event1, &event2)) { |
| return NULL; |
| } |
| } |
| RAISE_UNLESS_CODELIKE(codelike); |
| PyCodeLikeObject *cl = (PyCodeLikeObject *) codelike; |
| |
| uint8_t events[] = { event1, event2 }; |
| |
| PyMonitoring_EnterScope(cl->monitoring_states, |
| &cl->version, |
| events, |
| num_events); |
| |
| Py_RETURN_NONE; |
| } |
| |
| static PyObject * |
| exit_scope(PyObject *self, PyObject *args) |
| { |
| PyMonitoring_ExitScope(); |
| Py_RETURN_NONE; |
| } |
| |
| static PyMethodDef TestMethods[] = { |
| {"fire_event_py_start", fire_event_py_start, METH_VARARGS}, |
| {"fire_event_py_resume", fire_event_py_resume, METH_VARARGS}, |
| {"fire_event_py_return", fire_event_py_return, METH_VARARGS}, |
| {"fire_event_c_return", fire_event_c_return, METH_VARARGS}, |
| {"fire_event_py_yield", fire_event_py_yield, METH_VARARGS}, |
| {"fire_event_call", fire_event_call, METH_VARARGS}, |
| {"fire_event_line", fire_event_line, METH_VARARGS}, |
| {"fire_event_jump", fire_event_jump, METH_VARARGS}, |
| {"fire_event_branch", fire_event_branch, METH_VARARGS}, |
| {"fire_event_py_throw", fire_event_py_throw, METH_VARARGS}, |
| {"fire_event_raise", fire_event_raise, METH_VARARGS}, |
| {"fire_event_c_raise", fire_event_c_raise, METH_VARARGS}, |
| {"fire_event_reraise", fire_event_reraise, METH_VARARGS}, |
| {"fire_event_exception_handled", fire_event_exception_handled, METH_VARARGS}, |
| {"fire_event_py_unwind", fire_event_py_unwind, METH_VARARGS}, |
| {"fire_event_stop_iteration", fire_event_stop_iteration, METH_VARARGS}, |
| {"monitoring_enter_scope", enter_scope, METH_VARARGS}, |
| {"monitoring_exit_scope", exit_scope, METH_VARARGS}, |
| {NULL}, |
| }; |
| |
| int |
| _PyTestCapi_Init_Monitoring(PyObject *m) |
| { |
| if (PyType_Ready(&PyCodeLike_Type) < 0) { |
| return -1; |
| } |
| if (PyModule_AddObjectRef(m, "CodeLike", (PyObject *) &PyCodeLike_Type) < 0) { |
| Py_DECREF(m); |
| return -1; |
| } |
| if (PyModule_AddFunctions(m, TestMethods) < 0) { |
| return -1; |
| } |
| return 0; |
| } |