-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathcInstrumenter.cpp
More file actions
145 lines (135 loc) · 4.38 KB
/
cInstrumenter.cpp
File metadata and controls
145 lines (135 loc) · 4.38 KB
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
#include "cInstrumenter.hpp"
#include "cstring.h"
#include "events.hpp"
#include "pythonHelpers.hpp"
#include <algorithm>
#include <array>
#include <cstdint>
#include <string>
namespace scorepy
{
void CInstrumenter::init(InstrumenterInterface interface)
{
this->interface = interface;
threading_module = PyImport_ImportModule("threading");
if (threading_module)
{
const char* name = (interface == InstrumenterInterface::Trace) ? "settrace" : "setprofile";
threading_set_instrumenter = PyObject_GetAttrString(threading_module, name);
}
}
void CInstrumenter::deinit()
{
Py_CLEAR(threading_module);
Py_CLEAR(threading_set_instrumenter);
}
void CInstrumenter::enable_instrumenter()
{
const auto callback = [](PyObject* obj, PyFrameObject* frame, int what, PyObject* arg) -> int {
return from_PyObject(obj)->on_event(*frame, what, arg) ? 0 : -1;
};
if (threading_set_instrumenter)
{
PyRefObject result(PyObject_CallFunction(threading_set_instrumenter, "O", to_PyObject()),
adopt_object);
}
if (interface == InstrumenterInterface::Trace)
{
PyEval_SetTrace(callback, to_PyObject());
}
else
{
PyEval_SetProfile(callback, to_PyObject());
}
}
void CInstrumenter::disable_instrumenter()
{
if (interface == InstrumenterInterface::Trace)
{
PyEval_SetTrace(nullptr, nullptr);
}
else
{
PyEval_SetProfile(nullptr, nullptr);
}
if (threading_set_instrumenter)
{
PyRefObject result(PyObject_CallFunction(threading_set_instrumenter, "O", Py_None),
adopt_object);
}
}
/// Mapping of PyTrace_* to it's string representations
/// List taken from CPythons sysmodule.c
static const std::array<std::string, 8> WHAT_STRINGS = { "call", "exception", "line",
"return", "c_call", "c_exception",
"c_return", "opcode" };
template <typename TCollection, typename TElement>
int index_of(TCollection&& col, const TElement& element)
{
const auto it = std::find(col.cbegin(), col.cend(), element);
if (it == col.end())
{
return -1;
}
else
{
return std::distance(col.begin(), it);
}
}
// Required because: `sys.getprofile()` returns the user object (2nd arg to PyEval_SetTrace)
// So `sys.setprofile(sys.getprofile())` will not round-trip as it will try to call the
// 2nd arg through pythons dispatch function. Hence make the object callable.
// See https://nedbatchelder.com/text/trace-function.html for details
PyObject* CInstrumenter::operator()(PyFrameObject& frame, const char* what_string, PyObject* arg)
{
const int what = index_of(WHAT_STRINGS, what_string);
// To speed up further event processing install this class directly as the handler
// But we might be inside a `sys.settrace` call where the user wanted to set another function
// which would then be overwritten here. Hence use the CALL event which avoids the problem
if (what == PyTrace_CALL)
{
enable_instrumenter();
}
if (on_event(frame, what, arg))
{
Py_INCREF(to_PyObject());
return to_PyObject();
}
else
{
return nullptr;
}
}
bool CInstrumenter::on_event(PyFrameObject& frame, int what, PyObject*)
{
switch (what)
{
case PyTrace_CALL:
{
const PyCodeObject& code = *frame.f_code;
const CString name = get_string_from_python(*code.co_name);
const CString module_name = get_module_name(frame);
if (name != "_unsetprofile" && !module_name.starts_with("scorep"))
{
const int line_number = code.co_firstlineno;
const CString file_name = get_file_name(frame);
region_begin(name, module_name, file_name, line_number,
reinterpret_cast<std::uintptr_t>(&code));
}
break;
}
case PyTrace_RETURN:
{
const PyCodeObject& code = *frame.f_code;
const CString name = get_string_from_python(*code.co_name);
const CString module_name = get_module_name(frame);
if (name != "_unsetprofile" && !module_name.starts_with("scorep"))
{
region_end(name, module_name, reinterpret_cast<std::uintptr_t>(&code));
}
break;
}
}
return true;
}
} // namespace scorepy