1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
|
// 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 <Python.h>
#include <sys/sdt.h>
#include <stdlib.h>
// PR25841: ensure that the libHelperSDT.so file contains debuginfo
// for the tapset helper functions, so they don't have to look into libpython*
#include <frameobject.h>
PyFrameObject _dummy_frame;
#include <object.h>
PyVarObject _dummy_var;
#include <dictobject.h>
PyDictObject _dummy_dict;
#include <listobject.h>
PyListObject _dummy_list;
#include <tupleobject.h>
PyTupleObject _dummy_tuple;
#include <unicodeobject.h>
PyUnicodeObject _dummy_unicode;
#if PY_MAJOR_VERSION < 3
#include <stringobject.h>
PyStringObject _dummy_string;
#include <classobject.h>
PyClassObject _dummy_class;
PyDictEntry _dummy_dictentry;
PyInstanceObject _dummy_instance;
#include <intobject.h>
PyIntObject _dummy_int;
#else
PyASCIIObject _dummy_ascii;
PyCompactUnicodeObject _dummy_compactunicode;
// PyStringObject _dummy_string;
#include <bytesobject.h>
PyBytesObject _dummy_bytes;
#include <longobject.h>
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 <https://docs.python.org/3/c-api/module.html>:
//
// ====
// 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;
#ifdef __ia64__
asm ("nop 0" : "=r"(fptr) : "r"(fptr));
#else
asm ("nop" : "=r"(fptr) : "r"(fptr));
#endif
STAP_PROBE2(PROVIDER, Init, stap_module, fptr);
}
return module;
#endif
}
|