Skip to content

Commit 39006a2

Browse files
authored
Use the codeobject pointer to idetify regions (#105)
* separate user functions and instrumented functions * using the code object pointer to identify instrumented regions * decorated regions without a region name are now semi instrumented regions, as they use the object pointer as well * handle a few potential Score-P bugs.
1 parent 6a766ae commit 39006a2

17 files changed

Lines changed: 359 additions & 125 deletions

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ Please be aware the `--user` is always passed to Score-P, as this is needed for
261261
## Not Working
262262
* python multiprocessing
263263
* Score-P does currently only support MPI or SHMEM. Any other multiprocessing approach cannot be traced.
264+
* tracking `importlib.reload()`
264265

265266
# Acknowledgments
266267
The European Union initially supported this work as part of the European Union’s Horizon 2020 project READEX (grant agreement number 671657).

scorep/_instrumenters/base_instrumenter.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ def run(self, cmd, globals=None, locals=None):
3030
pass
3131

3232
@abc.abstractmethod
33-
def region_begin(self, module_name, function_name, file_name, line_number):
33+
def region_begin(self, module_name, function_name, file_name, line_number, code_object):
3434
pass
3535

3636
@abc.abstractmethod
37-
def region_end(self, module_name, function_name):
37+
def region_end(self, module_name, function_name, code_object):
3838
pass
3939

4040
@abc.abstractmethod

scorep/_instrumenters/dummy.py

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

26-
def region_begin(self, module_name, function_name, file_name, line_number):
26+
def region_begin(self, module_name, function_name, file_name, line_number, code_object=None):
2727
pass
2828

29-
def region_end(self, module_name, function_name):
29+
def region_end(self, module_name, function_name, code_object=None):
3030
pass
3131

3232
def rewind_begin(self, name, file_name=None, line_number=None):

scorep/_instrumenters/scorep_instrumenter.py

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

59-
def region_begin(self, module_name, function_name, file_name, line_number):
59+
def region_begin(self, module_name, function_name, file_name, line_number, code_object=None):
6060
"""Record a region begin event"""
6161
scorep._bindings.region_begin(
62-
module_name, function_name, file_name, line_number)
62+
module_name, function_name, file_name, line_number, code_object)
6363

64-
def region_end(self, module_name, function_name):
64+
def region_end(self, module_name, function_name, code_object=None):
6565
"""Record a region end event"""
66-
scorep._bindings.region_end(module_name, function_name)
66+
scorep._bindings.region_end(module_name, function_name, code_object)
6767

6868
def rewind_begin(self, name, file_name=None, line_number=None):
6969
"""

scorep/_instrumenters/scorep_profile.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,13 @@ def _globaltrace(self, frame, why, arg):
3939
if why == 'call':
4040
code = frame.f_code
4141
modulename = get_module_name(frame)
42+
4243
if not code.co_name == "_unsetprofile" and not modulename[:6] == "scorep":
4344
full_file_name = get_file_name(frame)
4445
line_number = code.co_firstlineno
45-
scorep._bindings.region_begin(modulename, code.co_name, full_file_name, line_number)
46+
scorep._bindings.region_begin(modulename, code.co_name, full_file_name, line_number, code)
4647
elif why == 'return':
4748
code = frame.f_code
4849
modulename = get_module_name(frame)
4950
if not code.co_name == "_unsetprofile" and not modulename[:6] == "scorep":
50-
scorep._bindings.region_end(modulename, code.co_name)
51+
scorep._bindings.region_end(modulename, code.co_name, code)

scorep/_instrumenters/scorep_trace.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,13 @@ def _globaltrace(self, frame, why, arg):
4040
if not code.co_name == "_unsettrace" and not modulename[:6] == "scorep":
4141
full_file_name = get_file_name(frame)
4242
line_number = code.co_firstlineno
43-
scorep._bindings.region_begin(modulename, code.co_name, full_file_name, line_number)
43+
scorep._bindings.region_begin(modulename, code.co_name, full_file_name, line_number, code)
4444
return self._localtrace
4545
return None
4646

4747
def _localtrace(self, frame, why, arg):
4848
if why == 'return':
4949
code = frame.f_code
5050
modulename = get_module_name(frame)
51-
scorep._bindings.region_end(modulename, code.co_name)
51+
scorep._bindings.region_end(modulename, code.co_name, code)
5252
return self._localtrace

scorep/user.py

Lines changed: 40 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ def __enter__(self):
7474
initally_registered = scorep.instrumenter.get_instrumenter().get_registered()
7575
with scorep.instrumenter.disable():
7676
if(self.user_region_name):
77+
# The user did specify a region name, so its a user_region
7778
self.module_name = "user"
7879
frame = inspect.currentframe().f_back
7980
file_name = frame.f_globals.get('__file__', None)
@@ -85,46 +86,53 @@ def __enter__(self):
8586

8687
scorep.instrumenter.get_instrumenter().region_begin(
8788
self.module_name, self.region_name, full_file_name, line_number)
88-
elif(callable(self.func)):
89-
"""
90-
looks like the decorator is invoked
91-
"""
92-
if not initally_registered:
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-
98-
if file_name is not None:
99-
full_file_name = os.path.abspath(file_name)
100-
else:
101-
full_file_name = "None"
102-
103-
scorep.instrumenter.get_instrumenter().region_begin(
104-
self.module_name, self.region_name, full_file_name, line_number)
89+
elif callable(self.func) and not initally_registered:
90+
# 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__
93+
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)
10599
else:
106-
"""
107-
do not need to decorate a function, when we are registerd. It is instrumented any way.
108-
"""
109-
pass
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)
104+
elif callable(self.func) and initally_registered:
105+
# The user did not specify a region name, and it's a callable, so it's a
106+
# semi instrumented region. However, the instrumenter is active, so there
107+
# is nothing to do.
108+
pass
110109
else:
111-
raise RuntimeError("a region name needs to be specified")
110+
# The user did not specify a region name, and it's not a callable. So it
111+
# is a context region without a region name. Throw an error.
112+
raise RuntimeError("A region name needs to be specified.")
112113

113114
return self
114115

115116
def __exit__(self, exc_type, exc_value, traceback):
116-
if (callable(self.func)
117-
and scorep.instrumenter.get_instrumenter().get_registered()
118-
and not self.user_region_name):
119-
"""
120-
looks like there is a decorator, we are registered and the name is not specified by the user,
121-
so we do not need to do anything. The Instrumentation will take care.
122-
"""
123-
return False
124-
else:
117+
initally_registered = scorep.instrumenter.get_instrumenter().get_registered()
118+
if self.user_region_name:
119+
# The user did specify a region name, so its a user_region
125120
scorep.instrumenter.get_instrumenter().region_end(
126121
self.module_name, self.region_name)
127-
return False
122+
elif callable(self.func) and not initally_registered:
123+
# 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)
126+
elif callable(self.func) and initally_registered:
127+
# The user did not specify a region name, and it's a callable, so it's a
128+
# semi instrumented region. However, the instrumenter is active, so there
129+
# is nothing to do.
130+
pass
131+
else:
132+
# The user did not specify a region name, and it's not a callable. So it
133+
# is a context region without a region name. Throw an error.
134+
raise RuntimeError("Something wen't wrong. Please do a Bug Report.")
135+
return False
128136

129137

130138
def rewind_begin(name, file_name=None, line_number=None):

src/methods.cpp

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#include <cstdint>
55
#include <scorep/SCOREP_User_Functions.h>
66

7+
#include <iostream>
8+
79
extern "C"
810
{
911

@@ -28,15 +30,26 @@ extern "C"
2830
static PyObject* region_begin(PyObject* self, PyObject* args)
2931
{
3032
const char* module;
31-
const char* region_name;
33+
const char* function_name;
3234
const char* file_name;
35+
PyObject* identifier = nullptr;
3336
std::uint64_t line_number = 0;
3437

35-
if (!PyArg_ParseTuple(args, "sssK", &module, &region_name, &file_name, &line_number))
38+
if (!PyArg_ParseTuple(args, "sssKO", &module, &function_name, &file_name, &line_number,
39+
&identifier))
40+
{
3641
return NULL;
37-
38-
const std::string& region = scorepy::make_region_name(module, region_name);
39-
scorepy::region_begin(region, module, file_name, line_number);
42+
}
43+
44+
if (identifier == nullptr or identifier == Py_None)
45+
{
46+
scorepy::region_begin(function_name, module, file_name, line_number);
47+
}
48+
else
49+
{
50+
scorepy::region_begin(function_name, module, file_name, line_number,
51+
reinterpret_cast<std::uintptr_t>(identifier));
52+
}
4053

4154
Py_RETURN_NONE;
4255
}
@@ -47,13 +60,23 @@ extern "C"
4760
static PyObject* region_end(PyObject* self, PyObject* args)
4861
{
4962
const char* module;
50-
const char* region_name;
63+
const char* function_name;
64+
PyObject* identifier = nullptr;
5165

52-
if (!PyArg_ParseTuple(args, "ss", &module, &region_name))
66+
if (!PyArg_ParseTuple(args, "ssO", &module, &function_name, &identifier))
67+
{
5368
return NULL;
54-
55-
const std::string& region = scorepy::make_region_name(module, region_name);
56-
scorepy::region_end(region);
69+
}
70+
71+
if (identifier == nullptr or identifier == Py_None)
72+
{
73+
scorepy::region_end(function_name, module);
74+
}
75+
else
76+
{
77+
scorepy::region_end(function_name, module,
78+
reinterpret_cast<std::uintptr_t>(identifier));
79+
}
5780

5881
Py_RETURN_NONE;
5982
}

src/scorepy/cInstrumenter.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "pythonHelpers.hpp"
44
#include <algorithm>
55
#include <array>
6+
#include <cstdint>
67
#include <string>
78

89
namespace scorepy
@@ -122,9 +123,9 @@ bool CInstrumenter::on_event(PyFrameObject& frame, int what, PyObject*)
122123
if (std::string(name) != "_unsetprofile" && std::string(module_name, 0, 6) != "scorep")
123124
{
124125
const int line_number = code.co_firstlineno;
125-
const auto& region_name = make_region_name(module_name, name);
126126
const auto file_name = get_file_name(frame);
127-
region_begin(region_name, module_name, file_name, line_number);
127+
region_begin(name, module_name, file_name, line_number,
128+
reinterpret_cast<std::uintptr_t>(&code));
128129
}
129130
break;
130131
}
@@ -138,8 +139,7 @@ bool CInstrumenter::on_event(PyFrameObject& frame, int what, PyObject*)
138139
// TODO: Use string_view/CString comparison?
139140
if (std::string(name) != "_unsetprofile" && std::string(module_name, 0, 6) != "scorep")
140141
{
141-
const auto& region_name = make_region_name(module_name, name);
142-
region_end(region_name);
142+
region_end(name, module_name, reinterpret_cast<std::uintptr_t>(&code));
143143
}
144144
break;
145145
}

0 commit comments

Comments
 (0)