// systemtap python SDT marker C module // Copyright (C) 2016-2020 Red Hat Inc. // // This file is part of systemtap, and is free software. You can // redistribute it and/or modify it under the terms of the GNU General // Public License (GPL); either version 2, or (at your option) any // later version. #include #include #include // PR25841: ensure that the libHelperSDT.so file contains debuginfo // for the tapset helper functions, so they don't have to look into libpython* #include PyFrameObject _dummy_frame; #include PyVarObject _dummy_var; #include PyDictObject _dummy_dict; #include PyListObject _dummy_list; #include PyTupleObject _dummy_tuple; #include PyUnicodeObject _dummy_unicode; #if PY_MAJOR_VERSION < 3 #include PyStringObject _dummy_string; #include PyClassObject _dummy_class; PyDictEntry _dummy_dictentry; PyInstanceObject _dummy_instance; #include PyIntObject _dummy_int; #else PyASCIIObject _dummy_ascii; PyCompactUnicodeObject _dummy_compactunicode; // PyStringObject _dummy_string; #include PyBytesObject _dummy_bytes; #include PyLongObject _dummy_long; /* This is internal to libpython. */ #if PY_MINOR_VERSION == 6 /* python 3.6 */ typedef Py_ssize_t (*dict_lookup_func)(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject ***value_addr, Py_ssize_t *hashpos); typedef struct { Py_hash_t me_hash; PyObject *me_key; PyObject *me_value; } PyDictKeyEntry; PyDictKeyEntry _dummy_dictkeyentry; struct _dictkeysobject { Py_ssize_t dk_refcnt; Py_ssize_t dk_size; dict_lookup_func dk_lookup; Py_ssize_t dk_usable; Py_ssize_t dk_nentries; char dk_indices[]; /* char is required to avoid strict aliasing. */ }; #elif PY_MINOR_VERSION == 7 /* python 3.7 */ typedef Py_ssize_t (*dict_lookup_func)(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); typedef struct { Py_hash_t me_hash; PyObject *me_key; PyObject *me_value; } PyDictKeyEntry; PyDictKeyEntry _dummy_dictkeyentry; struct _dictkeysobject { Py_ssize_t dk_refcnt; Py_ssize_t dk_size; dict_lookup_func dk_lookup; Py_ssize_t dk_usable; Py_ssize_t dk_nentries; char dk_indices[]; }; #endif #endif #if PY_MAJOR_VERSION < 3 #define PROVIDER HelperSDT2 #else #define PROVIDER HelperSDT3 #endif static PyObject * trace_callback(PyObject *self, PyObject *args) { unsigned int what; PyObject *frame_obj, *arg_obj; char *module_name; unsigned int key; /* Parse the input tuple */ if (!PyArg_ParseTuple(args, "IOOsI", &what, &frame_obj, &arg_obj, &module_name, &key)) return NULL; /* We want to name the probes with the same name as the * define. This is tricky, so, we'll just save the define, * undefine it, call the STAP_PROBE macro, then redfine it. */ switch (what) { case PyTrace_CALL: #pragma push_macro("PyTrace_CALL") #undef PyTrace_CALL STAP_PROBE4(PROVIDER, PyTrace_CALL, module_name, key, frame_obj, arg_obj); #pragma pop_macro("PyTrace_CALL") break; case PyTrace_EXCEPTION: #pragma push_macro("PyTrace_EXCEPTION") #undef PyTrace_EXCEPTION STAP_PROBE4(PROVIDER, PyTrace_EXCEPTION, module_name, key, frame_obj, arg_obj); #pragma pop_macro("PyTrace_EXCEPTION") break; case PyTrace_LINE: #pragma push_macro("PyTrace_LINE") #undef PyTrace_LINE STAP_PROBE4(PROVIDER, PyTrace_LINE, module_name, key, frame_obj, arg_obj); #pragma pop_macro("PyTrace_LINE") break; case PyTrace_RETURN: #pragma push_macro("PyTrace_RETURN") #undef PyTrace_RETURN STAP_PROBE4(PROVIDER, PyTrace_RETURN, module_name, key, frame_obj, arg_obj); #pragma pop_macro("PyTrace_RETURN") break; // FIXME: What about PyTrace_C_CALL, PyTrace_C_EXCEPTION, // PyTrace_C_RETURN? Fold them into their non-'_C_' versions or // have unique probes? default: // FIXME: error/exception here? return NULL; } return Py_BuildValue("i", 0); } static PyMethodDef HelperSDT_methods[] = { {"trace_callback", trace_callback, METH_VARARGS, "Trace callback function."}, {NULL, NULL, 0, NULL} /* Sentinel */ }; PyDoc_STRVAR(HelperSDT_doc, "This module provides an interface for interfacing between Python tracing events and systemtap."); #if PY_MAJOR_VERSION >= 3 // // According to : // // ==== // Module state may be kept in a per-module memory area that can be // retrieved with PyModule_GetState(), rather than in static // globals. This makes modules safe for use in multiple // sub-interpreters. // // This memory area is allocated based on m_size on module creation, // and freed when the module object is deallocated, after the m_free // function has been called, if present. // // Setting m_size to -1 means that the module does not support // sub-interpreters, because it has global state. // // Setting it to a non-negative value means that the module can be // re-initialized and specifies the additional amount of memory it // requires for its state. Non-negative m_size is required for // multi-phase initialization. // ==== // // This C module has no module state, so we'll set m_size to -1 (and // m_slots, m_traverse, m_clear, and m_free to NULL). // // All state information is held by the python HelperSDT module, not // this _HelperSDT helper C extension module. static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "_HelperSDT", HelperSDT_doc, -1, /* m_size */ HelperSDT_methods, NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL /* m_free */ }; #endif PyMODINIT_FUNC #if PY_MAJOR_VERSION >= 3 PyInit__HelperSDT(void) #else init_HelperSDT(void) #endif { PyObject *module; #if PY_MAJOR_VERSION >= 3 char *stap_module; module = PyModule_Create(&moduledef); if (module == NULL) return NULL; #else module = Py_InitModule3("_HelperSDT", HelperSDT_methods, HelperSDT_doc); if (module == NULL) return; #endif // Add constants for the PyTrace_* values we use. PyModule_AddIntMacro(module, PyTrace_CALL); PyModule_AddIntMacro(module, PyTrace_EXCEPTION); PyModule_AddIntMacro(module, PyTrace_LINE); PyModule_AddIntMacro(module, PyTrace_RETURN); #if PY_MAJOR_VERSION >= 3 // Get the systemtap module name from the environment. If we found // it, let systemtap know information it needs. stap_module = getenv("SYSTEMTAP_MODULE"); if (stap_module) { // Here we force the compiler to fully resolve the function // pointer value by assigning it to a variable and accessing // it with the asm() statement. Otherwise we get a @GOTPCREL // reference which stap can't parse. void *fptr = &PyObject_GenericGetAttr; asm ("nop" : "=r"(fptr) : "r"(fptr)); STAP_PROBE2(PROVIDER, Init, stap_module, fptr); } return module; #endif }