Skip to content

Commit 8467ac8

Browse files
authored
Merge pull request #39 from score-p/issue-#34
Closes #34, preparation of v2.0 release * separate subsystem generation and __main__ more clearly * introduce the notion of "instrumenter" and refactor the code accordingly * introduce context managers
2 parents 8a0d629 + 5d04928 commit 8467ac8

File tree

15 files changed

+541
-144
lines changed

15 files changed

+541
-144
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
dist: xenial
1+
dist: bionic
22

33
language: python
44
python:

README.md

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,16 @@ scorep is a module that allows tracing of python scripts using [Score-P](http://
55

66
# Install
77
You need at least Score-P 5.0, build with `--enable-shared`.
8-
Please make sure, that `scorep-config` is in your `PATH` variable.
8+
Please make sure that `scorep-config` is in your `PATH` variable.
9+
10+
Then run
911

10-
Then simply run
1112
```
1213
pip install .
1314
```
1415
# Use
1516

16-
To trace the full script you need to run
17+
To trace the full script, you need to run
1718

1819
```
1920
python -m scorep <script.py>
@@ -35,12 +36,6 @@ Since version 0.9 it is possible to pass the traditional Score-P commands to the
3536
python -m scorep --mpp=mpi --thread=pthread <script.py>
3637
```
3738

38-
To see all flags simply call:
39-
40-
```
41-
scorep --help
42-
```
43-
4439
## MPI
4540

4641
To use trace an MPI parallel application, please specify
@@ -50,40 +45,69 @@ python -m scorep --mpp=mpi <script.py>
5045
```
5146

5247
## User instrumentation
48+
### User Regions
49+
Since version 2.0 the python bindings support context managers for user regions:
5350

54-
In some cases, the user might want to define a region, log some parameters, or just disable tracing for a certain area. To do so the module implements a few functions:
51+
```
52+
with scorep.user.region("region_name"):
53+
do_something()
54+
```
55+
56+
The traditional calls to define a region and log some parameters, still exists:
5557

5658
```
5759
scorep.user.region_begin(name)
60+
scorep.user.parameter_int(name, val)
61+
scorep.user.parameter_uint(name, val)
62+
scorep.user.parameter_string(name, string)
5863
scorep.user.region_end(name)
5964
```
6065

61-
These functions allow the definition of user regions. `name` defines the name of a region. Each `user.region_begin` shall have a corresponding call to `user.region_end`.
66+
`name` defines the name of the parameter or region, while `val` or `string` represents the value that is passed to Score-P.
67+
68+
Disabeling the recording with Score-P is still also possilbe:
6269

6370
```
6471
scorep.user.enable_recording()
6572
scorep.user.disable_recording()
6673
```
6774

68-
These functions allow enabling and disabling of the tracing.
75+
However, please be aware that the runtime impact is rather small, as the instrumenter is still active. For details about the instrumenter, please see [Instrumenter](#Instrumenter).
76+
77+
### Instrumenter
78+
With version 2.0 of the python bindings, the term "instrumenter" is introduced. The instrumenter describes the class that maps the Python `trace` or `profile` events to Score-P. Please be aware, that `trace` and `profile` does not refer to the traditional Score-P terms of tracing and profiling, but to the Python functions [sys.settrace](https://docs.python.org/3/library/sys.html#sys.settrace) and [sys.setprofile](https://docs.python.org/3/library/sys.html#sys.setprofile).
79+
80+
The instrumenter that shall be used for tracing can be specified using `--instrumenter-type=<type>`.
81+
Currently there are the following tacers available:
82+
* `profile` (default) implements `call` and `return`
83+
* `trace` implements `call` and `return`
84+
* `dummy` does nothing, can be used without `-m scorep` (as done by user instrumentation)
85+
86+
The `profile` instrumenter should have a smaller overhead than `trace`.
87+
88+
Moreover it is possible to disable (and enable) the instrumenter in the sourcecode:
6989

7090
```
71-
scorep.user.parameter_int(name, val)
72-
scorep.user.parameter_uint(name, val)
73-
scorep.user.parameter_string(name, string)
91+
with scorep.instrumenter.disable():
92+
do_something()
93+
94+
with scorep.instrumenter.enable():
95+
do_something()
7496
```
7597

76-
These functions allow passing user parameters to Score-P. These parameters can be int, uint and string. `name` defines the name of the parameter, while `val` or `string` defines the value that is passed to Score-P.
77-
Please be aware, that the scorep module still needs to be preloaded, i.e. by using `python -m scorep`.
98+
or during startup with `--noinstrumenter`. Please be aware that the function calls override the Flag.
7899

79-
## Additional Flags
100+
## Overview about Flags
80101

81-
The Score-P bindings now also support a `--nopython` flag, which disables the python instrumentation.
82-
This might be helpful, if only user instrumentation is required, or only some instrumented libraries shall be traced.
102+
The following flags are special to the python bindings:
103+
104+
* `--noinstrumenter` disables the instrumentation of python code. Usefull for user instrumentation and to trace only specific code regions using `scorep.instrumenter.enable`.
105+
* `--instrumenter-type=<type>` choose an instrumenter. See [Instrumenter](#Instrumenter).
106+
* `--keep-files` temporary files are kept.
83107

84108
## Backward Compatibility
85109

86-
In order to maintain backwards Compatibility, the following flags are set per default:
110+
To maintain backwards compatibility, the following flags are set per default:
87111

88112
```
89113
python -m scorep --compiler --thread=pthread <script.py>
@@ -95,7 +119,7 @@ The traditional `--mpi` does still work, and is similar to the following call:
95119
python -m scorep --compiler --thread=pthread --mpp=mpi <script.py>
96120
```
97121

98-
To disable compiler instrumentation please specify:
122+
To disable compiler instrumentation, please specify:
99123

100124
```
101125
python -m scorep --nocompiler <script.py>
@@ -119,7 +143,4 @@ Please be aware the `--user` is always passed to Score-P, as this is needed for
119143

120144
## Not Working
121145
* python multiprocessing
122-
* Score-P does currently not support any non MPI or non SHMEM communication. So the different processes will not know from each other. You might want to take a look to https://mpi4py.readthedocs.io/en/stable/mpi4py.futures.html .
123-
124-
# Tracing
125-
The tracing uses Score-P User instrumentation. The python trace module was reworked, to pass the region names to `SCOREP_USER_REGION_BEGIN` and `SCOREP_USER_REGION_END` instead of printing. All other features of the tracing module where striped.
146+
* Score-P does currently only support MPI or SHMEM. Any other multiprocessing approach cannot be traced.

scorep/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
__all__ = ["trace", "scorep_bindings", "user", "subsystem"]
1+
import scorep.user
2+
import scorep.instrumenter
3+
__all__ = ["user", "instrumenter"]

scorep/__main__.py

Lines changed: 16 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
import sys
33
import importlib
44

5-
import scorep.trace
6-
import scorep.helper
5+
import scorep.instrumenter
76
import scorep.subsystem
87

98

@@ -12,37 +11,6 @@ def _err_exit(msg):
1211
sys.exit(1)
1312

1413

15-
def set_init_environment(scorep_config=[], keep_files=False):
16-
"""
17-
Set the inital needed environmet variables, to get everythin up an running.
18-
As a few variables interact with LD env vars, the programms needs to be restarted after this.
19-
The function set the env var `SCOREP_PYTHON_BINDINGS_INITALISED` to true, once it is done with
20-
initalising.
21-
22-
@param scorep_config configuration flags for score-p
23-
@param keep_files whether to keep the generated files, or not.
24-
"""
25-
26-
if ("LD_PRELOAD" in os.environ) and (
27-
"libscorep" in os.environ["LD_PRELOAD"]):
28-
raise RuntimeError(
29-
"Score-P is already loaded. This should not happen at this point")
30-
31-
subsystem_lib_name, temp_dir = scorep.subsystem.generate(
32-
scorep_config, keep_files)
33-
scorep_ld_preload = scorep.helper.generate_ld_preload(scorep_config)
34-
35-
scorep.helper.add_to_ld_library_path(temp_dir)
36-
37-
preload_str = scorep_ld_preload + " " + subsystem_lib_name
38-
if "LD_PRELOAD" in os.environ:
39-
sys.stderr.write(
40-
"LD_PRELOAD is already specified. If Score-P is already loaded this might lead to errors.")
41-
preload_str = preload_str + " " + os.environ["LD_PRELOAD"]
42-
os.environ["LD_PRELOAD"] = preload_str
43-
os.environ["SCOREP_PYTHON_BINDINGS_INITALISED"] = "true"
44-
45-
4614
def scorep_main(argv=None):
4715
# print(sys.flags)
4816
if argv is None:
@@ -55,7 +23,8 @@ def scorep_main(argv=None):
5523
keep_files = False
5624
no_default_threads = False
5725
no_default_compiler = False
58-
no_python = False
26+
no_instrumenter = False
27+
instrumenter_type = "profile"
5928

6029
for elem in argv[1:]:
6130
if parse_scorep_commands:
@@ -70,7 +39,12 @@ def scorep_main(argv=None):
7039
scorep_config.append(elem)
7140
no_default_compiler = True
7241
elif elem == "--nopython":
73-
no_python = True
42+
no_instrumenter = True
43+
elif elem == "--noinstrumenter":
44+
no_instrumenter = True
45+
elif "--instrumenter-type" in elem:
46+
param = elem.split("=")
47+
instrumenter_type = param[1]
7448
elif elem[0] == "-":
7549
scorep_config.append(elem)
7650
else:
@@ -90,20 +64,22 @@ def scorep_main(argv=None):
9064

9165
if ("SCOREP_PYTHON_BINDINGS_INITALISED" not in os.environ) or (
9266
os.environ["SCOREP_PYTHON_BINDINGS_INITALISED"] != "true"):
93-
set_init_environment(scorep_config, keep_files)
67+
scorep.subsystem.init_environment(scorep_config, keep_files)
68+
os.environ["SCOREP_PYTHON_BINDINGS_INITALISED"] = "true"
9469

9570
"""
9671
python -m starts the module as skript. i.e. sys.argv will loke like:
9772
['/home/gocht/Dokumente/code/scorep_python/scorep.py', '--mpi', 'mpi_test.py']
9873
99-
To restart python we need to remove this line, and add python -m scorep ... again
74+
To restart python we need to remove this line, and add `python -m scorep ...` again
10075
"""
10176
new_args = [sys.executable, "-m", "scorep"]
10277
for elem in sys.argv:
10378
if "scorep/__main__.py" in elem:
10479
continue
10580
else:
10681
new_args.append(elem)
82+
10783
os.execve(sys.executable, new_args, os.environ)
10884

10985
scorep_bindings = importlib.import_module("scorep.scorep_bindings")
@@ -113,11 +89,11 @@ def scorep_main(argv=None):
11389
progname = prog_argv[0]
11490
sys.path[0] = os.path.split(progname)[0]
11591

116-
tracer = scorep.trace.ScorepTrace(scorep_bindings, not no_python)
92+
tracer = scorep.instrumenter.get_instrumenter(
93+
scorep_bindings, not no_instrumenter, instrumenter_type)
11794
try:
11895
with open(progname) as fp:
11996
code = compile(fp.read(), progname, 'exec')
120-
target_code = code
12197
# try to emulate __main__ namespace as much as possible
12298
globs = {
12399
'__file__': progname,
@@ -152,5 +128,5 @@ def main(argv=None):
152128
else:
153129
if ("SCOREP_PYTHON_BINDINGS_INITALISED" not in os.environ) or (
154130
os.environ["SCOREP_PYTHON_BINDINGS_INITALISED"] != "true"):
155-
sys.stderr.write(
131+
logging.warning(
156132
"scorep needs to be loaded using \"python -m scorep <script>\". Please be aware that scorep might not work correctly!")

scorep/instrumenter.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import scorep.instrumenters.dummy
2+
import scorep.instrumenters.scorep_profile
3+
import scorep.instrumenters.scorep_trace
4+
5+
global_instrumenter = None
6+
7+
8+
def get_instrumenter(
9+
bindings=None,
10+
enable_instrumenter=False,
11+
instrumenter_type="dummy"):
12+
"""
13+
returns an instrumenter
14+
15+
@param bindings the c/c++ scorep bindings
16+
@param enable_instrumenter True if the Instrumenter should be enabled when run is called
17+
@param instrumenter_type which python tracing interface to use. Currently available: `profile` (default), `trace` and `dummy`
18+
"""
19+
global global_instrumenter
20+
if global_instrumenter is None:
21+
if instrumenter_type == "profile":
22+
global_instrumenter = scorep.instrumenters.scorep_profile.ScorepProfile(
23+
bindings, enable_instrumenter)
24+
elif instrumenter_type == "trace":
25+
global_instrumenter = scorep.instrumenters.scorep_trace.ScorepTrace(
26+
bindings, enable_instrumenter)
27+
elif instrumenter_type == "dummy":
28+
global_instrumenter = scorep.instrumenters.dummy.ScorepDummy(
29+
enable_instrumenter)
30+
else:
31+
raise RuntimeError(
32+
"instrumenter_type \"{}\" unkown".format(instrumenter_type))
33+
34+
return global_instrumenter
35+
36+
37+
def register():
38+
"""
39+
Reenables the python-tracing.
40+
"""
41+
get_instrumenter().register()
42+
43+
44+
def unregister():
45+
"""
46+
Disables the python-tracing.
47+
Disabling the python-tracing is more efficient than disable_recording, as python does not longer call the tracing module.
48+
However, all the other things that are traced by Score-P will still be recorded.
49+
Please call register() to enable tracing again.
50+
"""
51+
get_instrumenter().unregister()
52+
53+
54+
class enable():
55+
"""
56+
Context manager to enable tracing in a certain region:
57+
```
58+
with enable():
59+
do stuff
60+
```
61+
This overides --no-instrumenter (--nopython leagacy)
62+
"""
63+
64+
def __init__(self):
65+
pass
66+
67+
def __enter__(self):
68+
self.tracer_registered = get_instrumenter().register()
69+
70+
def __exit__(self, exc_type, exc_value, traceback):
71+
self.tracer_registered = get_instrumenter().unregister()
72+
73+
74+
class disable():
75+
"""
76+
Context manager to disable tracing in a certain region:
77+
```
78+
with disable():
79+
do stuff
80+
```
81+
This overides --no-instrumenter (--nopython leagacy)
82+
"""
83+
84+
def __init__(self):
85+
pass
86+
87+
def __enter__(self):
88+
self.tracer_registered = get_instrumenter().unregister()
89+
90+
def __exit__(self, exc_type, exc_value, traceback):
91+
self.tracer_registered = get_instrumenter().register()

scorep/instrumenters/__init__.py

Whitespace-only changes.

scorep/trace_dummy.py renamed to scorep/instrumenters/dummy.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
class ScorepTraceDummy:
2-
def __init__(self, scorep_bindings=None, trace=True):
1+
__all__ = ['ScorepDummy']
2+
3+
4+
class ScorepDummy:
5+
def __init__(self, scorep_bindings=None, enable_instrumenter=True):
36
pass
47

58
def register(self):
@@ -8,6 +11,9 @@ def register(self):
811
def unregister(self):
912
pass
1013

14+
def get_registered(self):
15+
return None
16+
1117
def run(self, cmd):
1218
pass
1319

0 commit comments

Comments
 (0)