Skip to content

Commit 00a9e13

Browse files
authored
Compat python 2.7 (#137)
* Add cTrace and cProfile for Python2 * Improve performance of the backend, by avoiding `str::string` and moving to std::string_view (c++17) * Test if the region is known also for Trace and Profile instrumenter
1 parent 9880b26 commit 00a9e13

20 files changed

Lines changed: 262 additions & 127 deletions

scorep/_instrumenters/dummy.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,15 @@ def run(self, cmd, globals=None, locals=None):
2323
locals = {}
2424
exec(cmd, globals, locals)
2525

26+
def try_region_begin(self, code_object):
27+
pass
28+
2629
def region_begin(self, module_name, function_name, file_name, line_number, code_object=None):
2730
pass
2831

32+
def try_region_end(self, code_object):
33+
pass
34+
2935
def region_end(self, module_name, function_name, code_object=None):
3036
pass
3137

scorep/_instrumenters/scorep_instrumenter.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,19 @@ def run(self, cmd, globals=None, locals=None):
5656
finally:
5757
self.unregister()
5858

59+
def try_region_begin(self, code_object):
60+
"""Tries to record a region begin event. Retruns True on success"""
61+
return scorep._bindings.try_region_begin(code_object)
62+
5963
def region_begin(self, module_name, function_name, file_name, line_number, code_object=None):
6064
"""Record a region begin event"""
6165
scorep._bindings.region_begin(
6266
module_name, function_name, file_name, line_number, code_object)
6367

68+
def try_region_end(self, code_object):
69+
"""Tries to record a region end event. Retruns True on success"""
70+
return scorep._bindings.try_region_end(code_object)
71+
6472
def region_end(self, module_name, function_name, code_object=None):
6573
"""Record a region end event"""
6674
scorep._bindings.region_end(module_name, function_name, code_object)

scorep/_instrumenters/scorep_profile.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
__all__ = ['ScorepProfile']
22

33
import sys
4-
from scorep._instrumenters.utils import get_module_name, get_file_name
4+
from scorep._instrumenters.utils import get_module_name
55
from scorep._instrumenters.scorep_instrumenter import ScorepInstrumenter
66
import scorep._bindings
77

@@ -38,14 +38,17 @@ def _globaltrace(self, frame, why, arg):
3838
"""
3939
if why == 'call':
4040
code = frame.f_code
41-
modulename = get_module_name(frame)
42-
43-
if not code.co_name == "_unsetprofile" and not modulename[:6] == "scorep":
44-
full_file_name = get_file_name(frame)
45-
line_number = code.co_firstlineno
46-
scorep._bindings.region_begin(modulename, code.co_name, full_file_name, line_number, code)
41+
if not scorep._bindings.try_region_begin(code):
42+
if not code.co_name == "_unsetprofile":
43+
modulename = get_module_name(frame)
44+
if not modulename[:6] == "scorep":
45+
file_name = code.co_filename
46+
line_number = code.co_firstlineno
47+
scorep._bindings.region_begin(modulename, code.co_name, file_name, line_number, code)
4748
elif why == 'return':
4849
code = frame.f_code
49-
modulename = get_module_name(frame)
50-
if not code.co_name == "_unsetprofile" and not modulename[:6] == "scorep":
51-
scorep._bindings.region_end(modulename, code.co_name, code)
50+
if not scorep._bindings.try_region_end(code):
51+
if not code.co_name == "_unsetprofile":
52+
modulename = get_module_name(frame)
53+
if not modulename[:6] == "scorep":
54+
scorep._bindings.region_end(modulename, code.co_name, code)

scorep/_instrumenters/scorep_trace.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
__all__ = ['ScorepTrace']
22

33
import sys
4-
from scorep._instrumenters.utils import get_module_name, get_file_name
4+
from scorep._instrumenters.utils import get_module_name
55
from scorep._instrumenters.scorep_instrumenter import ScorepInstrumenter
66
import scorep._bindings
77

@@ -36,17 +36,22 @@ def _globaltrace(self, frame, why, arg):
3636
"""
3737
if why == 'call':
3838
code = frame.f_code
39-
modulename = get_module_name(frame)
40-
if not code.co_name == "_unsettrace" and not modulename[:6] == "scorep":
41-
full_file_name = get_file_name(frame)
42-
line_number = code.co_firstlineno
43-
scorep._bindings.region_begin(modulename, code.co_name, full_file_name, line_number, code)
44-
return self._localtrace
39+
if not scorep._bindings.try_region_begin(code):
40+
if not code.co_name == "_unsetprofile":
41+
modulename = get_module_name(frame)
42+
if not modulename[:6] == "scorep":
43+
full_file_name = code.co_filename
44+
line_number = code.co_firstlineno
45+
scorep._bindings.region_begin(modulename, code.co_name, full_file_name, line_number, code)
46+
return self._localtrace
4547
return None
4648

4749
def _localtrace(self, frame, why, arg):
4850
if why == 'return':
4951
code = frame.f_code
50-
modulename = get_module_name(frame)
51-
scorep._bindings.region_end(modulename, code.co_name, code)
52+
if not scorep._bindings.try_region_end(code):
53+
if not code.co_name == "_unsetprofile":
54+
modulename = get_module_name(frame)
55+
if not modulename[:6] == "scorep":
56+
scorep._bindings.region_end(modulename, code.co_name, code)
5257
return self._localtrace

scorep/instrumenter.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import inspect
22
import os
33
import platform
4-
import sys
54
import functools
65

76
global_instrumenter = None
@@ -11,7 +10,7 @@ def has_c_instrumenter():
1110
"""Return true if the C instrumenter(s) are available"""
1211
# We are using the UTF-8 string features from Python 3
1312
# The C Instrumenter functions are not available on PyPy
14-
return sys.version_info.major >= 3 and platform.python_implementation() != 'PyPy'
13+
return platform.python_implementation() != 'PyPy'
1514

1615

1716
def get_instrumenter(enable_instrumenter=False,

scorep/user.py

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -88,19 +88,14 @@ def __enter__(self):
8888
self.module_name, self.region_name, full_file_name, line_number)
8989
elif callable(self.func) and not initally_registered:
9090
# The user did not specify a region name, and it's a callable, so it's a semi instrumented region
91-
self.region_name = self.func.__name__
92-
self.module_name = self.func.__module__
9391
self.code_obj = self.func.__code__
94-
file_name = self.func.__code__.co_filename
95-
line_number = self.func.__code__.co_firstlineno
96-
97-
if file_name is not None:
98-
full_file_name = os.path.abspath(file_name)
99-
else:
100-
full_file_name = "None"
101-
102-
scorep.instrumenter.get_instrumenter().region_begin(
103-
self.module_name, self.region_name, full_file_name, line_number, self.code_obj)
92+
if not scorep.instrumenter.get_instrumenter().try_region_begin(self.code_obj):
93+
self.region_name = self.func.__name__
94+
self.module_name = self.func.__module__
95+
file_name = self.func.__code__.co_filename
96+
line_number = self.func.__code__.co_firstlineno
97+
scorep.instrumenter.get_instrumenter().region_begin(
98+
self.module_name, self.region_name, file_name, line_number, self.code_obj)
10499
elif callable(self.func) and initally_registered:
105100
# The user did not specify a region name, and it's a callable, so it's a
106101
# semi instrumented region. However, the instrumenter is active, so there
@@ -121,8 +116,8 @@ def __exit__(self, exc_type, exc_value, traceback):
121116
self.module_name, self.region_name)
122117
elif callable(self.func) and not initally_registered:
123118
# The user did not specify a region name, and it's a callable, so it's a semi instrumented region
124-
scorep.instrumenter.get_instrumenter().region_end(
125-
self.module_name, self.region_name, self.code_obj)
119+
if not scorep.instrumenter.get_instrumenter().try_region_end(self.code_obj):
120+
scorep.instrumenter.get_instrumenter().region_end(self.module_name, self.region_name, self.code_obj)
126121
elif callable(self.func) and initally_registered:
127122
# The user did not specify a region name, and it's a callable, so it's a
128123
# semi instrumented region. However, the instrumenter is active, so there

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
"scorep._bindings",
5454
include_dirs=include,
5555
define_macros=define_macros,
56-
extra_compile_args=["-std=c++11"],
56+
extra_compile_args=["-std=c++17"],
5757
sources=sources,
5858
)
5959
)

src/classes.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ extern "C"
9494
const char* event;
9595
PyObject* arg;
9696

97-
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!sO", const_cast<char**>(kwlist),
98-
&PyFrame_Type, &frame, &event, &arg))
97+
if (!PyArg_ParseTupleAndKeywords(args, kwds, "OsO", const_cast<char**>(kwlist), &frame,
98+
&event, &arg))
9999
{
100100
return nullptr;
101101
}

src/methods.cpp

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,58 +28,112 @@ extern "C"
2828
Py_RETURN_NONE;
2929
}
3030

31+
static PyObject* try_region_begin(PyObject* self, PyObject* args)
32+
{
33+
PyObject* identifier = nullptr;
34+
if (!PyArg_ParseTuple(args, "O", &identifier))
35+
{
36+
return NULL;
37+
}
38+
39+
bool success = scorepy::try_region_begin(reinterpret_cast<PyCodeObject*>(identifier));
40+
if (success)
41+
{
42+
Py_RETURN_TRUE;
43+
}
44+
else
45+
{
46+
Py_RETURN_FALSE;
47+
}
48+
}
49+
3150
/** This code is not thread save. However, this does not matter as the python GIL is not
3251
* released.
3352
*/
3453
static PyObject* region_begin(PyObject* self, PyObject* args)
3554
{
36-
const char* module;
37-
const char* function_name;
38-
const char* file_name;
55+
const char* module_cstr;
56+
const char* function_name_cstr;
57+
const char* file_name_cstr;
58+
Py_ssize_t module_len;
59+
Py_ssize_t function_name_len;
60+
Py_ssize_t file_name_len;
61+
3962
PyObject* identifier = nullptr;
4063
std::uint64_t line_number = 0;
4164

42-
if (!PyArg_ParseTuple(args, "sssKO", &module, &function_name, &file_name, &line_number,
65+
if (!PyArg_ParseTuple(args, "s#s#s#KO", &module_cstr, &module_len, &function_name_cstr,
66+
&function_name_len, &file_name_cstr, &file_name_len, &line_number,
4367
&identifier))
4468
{
4569
return NULL;
4670
}
4771

72+
std::string_view module(module_cstr, module_len);
73+
std::string_view function_name(function_name_cstr, function_name_len);
74+
std::string_view file_name(file_name_cstr, file_name_len);
75+
76+
std::string file_name_abs = scorepy::abspath(file_name);
77+
4878
if (identifier == nullptr or identifier == Py_None)
4979
{
50-
scorepy::region_begin(function_name, module, file_name, line_number);
80+
scorepy::region_begin(function_name, module, file_name_abs, line_number);
5181
}
5282
else
5383
{
54-
scorepy::region_begin(function_name, module, file_name, line_number,
55-
reinterpret_cast<std::uintptr_t>(identifier));
84+
scorepy::region_begin(function_name, module, file_name_abs, line_number,
85+
reinterpret_cast<PyCodeObject*>(identifier));
5686
}
5787

5888
Py_RETURN_NONE;
5989
}
6090

91+
static PyObject* try_region_end(PyObject* self, PyObject* args)
92+
{
93+
PyObject* identifier = nullptr;
94+
if (!PyArg_ParseTuple(args, "O", &identifier))
95+
{
96+
return NULL;
97+
}
98+
99+
bool success = scorepy::try_region_end(reinterpret_cast<PyCodeObject*>(identifier));
100+
if (success)
101+
{
102+
Py_RETURN_TRUE;
103+
}
104+
else
105+
{
106+
Py_RETURN_FALSE;
107+
}
108+
}
109+
61110
/** This code is not thread save. However, this does not matter as the python GIL is not
62111
* released.
63112
*/
64113
static PyObject* region_end(PyObject* self, PyObject* args)
65114
{
66-
const char* module;
67-
const char* function_name;
115+
const char* module_cstr;
116+
const char* function_name_cstr;
117+
Py_ssize_t module_len;
118+
Py_ssize_t function_name_len;
68119
PyObject* identifier = nullptr;
69120

70-
if (!PyArg_ParseTuple(args, "ssO", &module, &function_name, &identifier))
121+
if (!PyArg_ParseTuple(args, "s#s#O", &module_cstr, &module_len, &function_name_cstr,
122+
&function_name_len, &identifier))
71123
{
72124
return NULL;
73125
}
74126

127+
std::string_view module(module_cstr, module_len);
128+
std::string_view function_name(function_name_cstr, function_name_len);
129+
75130
if (identifier == nullptr or identifier == Py_None)
76131
{
77132
scorepy::region_end(function_name, module);
78133
}
79134
else
80135
{
81-
scorepy::region_end(function_name, module,
82-
reinterpret_cast<std::uintptr_t>(identifier));
136+
scorepy::region_end(function_name, module, reinterpret_cast<PyCodeObject*>(identifier));
83137
}
84138

85139
Py_RETURN_NONE;
@@ -182,7 +236,11 @@ extern "C"
182236

183237
static PyMethodDef ScorePMethods[] = {
184238
{ "region_begin", region_begin, METH_VARARGS, "enter a region." },
239+
{ "try_region_begin", try_region_begin, METH_VARARGS,
240+
"Tries to begin a region, returns True on Sucess." },
185241
{ "region_end", region_end, METH_VARARGS, "exit a region." },
242+
{ "try_region_end", try_region_end, METH_VARARGS,
243+
"Tries to end a region, returns True on Sucess." },
186244
{ "rewind_begin", rewind_begin, METH_VARARGS, "rewind begin." },
187245
{ "rewind_end", rewind_end, METH_VARARGS, "rewind end." },
188246
{ "enable_recording", enable_recording, METH_VARARGS, "disable scorep recording." },

src/scorep_bindings.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
1+
#include <Python.h>
2+
3+
#include <iostream>
4+
15
#include "classes.hpp"
26
#include "methods.hpp"
3-
#include <Python.h>
47

58
#if PY_VERSION_HEX < 0x03000000
69
PyMODINIT_FUNC init_bindings(void)
710
{
8-
(void)Py_InitModule("_bindings", scorepy::getMethodTable());
11+
PyObject* m;
12+
#if SCOREPY_ENABLE_CINSTRUMENTER
13+
static PyTypeObject ctracerType = scorepy::getCInstrumenterType();
14+
if (PyType_Ready(&ctracerType) < 0)
15+
return;
16+
#endif
17+
m = Py_InitModule("_bindings", scorepy::getMethodTable());
18+
#if SCOREPY_ENABLE_CINSTRUMENTER
19+
Py_INCREF(&ctracerType);
20+
PyModule_AddObject(m, "CInstrumenter", (PyObject*)&ctracerType);
21+
#endif
922
}
1023
#else /*python 3*/
1124
static struct PyModuleDef scorepmodule = { PyModuleDef_HEAD_INIT, "_bindings", /* name of module */

0 commit comments

Comments
 (0)