Python updates (#38)

* silence SFINAE disabled for fork_gotcha

* Python updates

- Options for --{module,function}-include
- libpyomnitrace is_initialized and is_finalized
- source instrumentation auto init
- atexit finalization
- improved python testing

* Documentation Update

* Fix to 'cmake -E cat' not available < cmake v3.18

* Fix for inverse tests

* Update cancelling.yml

[ROCm/rocprofiler-systems commit: 593b3b69b8]
Dieser Commit ist enthalten in:
Jonathan R. Madsen
2022-04-05 20:40:27 -05:00
committet von GitHub
Ursprung 6daac0f60c
Commit e7546b201a
21 geänderte Dateien mit 916 neuen und 237 gelöschten Zeilen
@@ -6,10 +6,10 @@
:maxdepth: 4
```
[Browse Omnitrace source code on Github](https://github.com/AMDResearch/omnitrace)
> ***[Omnitrace](https://github.com/AMDResearch/omnitrace) is an AMD research project and should***
> ***not be treated as an offical part of the ROCm software stack.***
> [Omnitrace](https://github.com/AMDResearch/omnitrace) is an AMD research project and should
> not be treated as an offical part of the ROCm software stack.
[Browse Omnitrace source code on Github](https://github.com/AMDResearch/omnitrace)
[Omnitrace](https://github.com/AMDResearch/omnitrace) is designed for both high-level and
comprehensive application tracing and profiling on both the CPU and GPU.
@@ -36,7 +36,7 @@ project = "omnitrace"
copyright = "2022, Advanced Micro Devices, Inc."
author = "Audacious Software Group"
version = open(os.path.join("..", "VERSION")).read().strip()
version = open(os.path.join("..", "..", "VERSION")).read().strip()
# The full version, including alpha/beta/rc tags
release = version
@@ -15,5 +15,5 @@ string(REGEX REPLACE "(\n|\r)" "" FULL_VERSION_STRING "${FULL_VERSION_STRING}")
string(REGEX REPLACE "([0-9]+)\\.([0-9]+)\\.([0-9]+)(.*)" "\\1.\\2.\\3" OMNITRACE_VERSION
"${FULL_VERSION_STRING}")
configure_file(${SOURCE_DIR}/docs-source/omnitrace.dox.in
${SOURCE_DIR}/docs-source/omnitrace.dox @ONLY)
configure_file(${SOURCE_DIR}/source/docs/omnitrace.dox.in
${SOURCE_DIR}/source/docs/omnitrace.dox @ONLY)
@@ -9,3 +9,40 @@
runtime
critical_trace
```
## Configuring Environment
Source the `setup-env.sh` script to prefix the `PATH`, `LD_LIBRARY_PATH`, etc. environment variables:
```bash
source /opt/omnitrace/share/omnitrace/setup-env.sh
```
Alternatively, if environment modules are supported, add the `<prefix>/share/modulefiles` directory to `MODULEPATH` via:
```bash
module use /opt/omnitrace/share/modulefiles
```
> ***Alternatively, the above line can be added to the `${HOME}/.modulerc` file.***
Once omnitrace is in the `MODULEPATH`, omnitrace can be loaded via `module load omnitrace/<VERSION>` and unloaded via `module unload omnitrace/<VERSION>`, e.g.:
```bash
module load omnitrace/1.0.0
module unload omnitrace/1.0.0
```
> ***You may need to also add the path to the ROCm libraries to `LD_LIBRARY_PATH`, e.g. `export LD_LIBRARY_PATH=/opt/rocm/lib:${LD_LIBRARY_PATH}`***
### Validating Environment Configuration
If all the following commands execute successfully with output, then you are ready to use omnitrace:
```bash
which omnitrace
which omnitrace-avail
omnitrace --help
omnitrace-avail --all
```
@@ -12,4 +12,5 @@
getting_started
output
user_api
python
```
@@ -180,8 +180,8 @@ There are three ways to perform instrumentation:
see [Binary Rewriting a Library](#binary-rewriting-a-library) for help
> NOTE: Attaching to a running process is an alpha feature and support for detaching from the target process
> without ending the target process is not currently supported.
> ***Attaching to a running process is an alpha feature and support for detaching from the target process***
> ***without ending the target process is not currently supported.***
The general syntax for separating omnitrace command line arguments from the application arguments follows the
is consistent with the LLVM style of using a standalone double-hyphen (`--`). All arguments preceding the double-hyphen
@@ -322,7 +322,7 @@ These options are always applied regardless of whether the module or function sa
#### Example Available Module and Function Info Output
> `omnitrace -o lulesh.inst --label file line args --simulate -- lulesh`
> ***`omnitrace -o lulesh.inst --label file line args --simulate -- lulesh`***
```console
AddressRange Module Function FunctionSignature
@@ -540,7 +540,7 @@ These options are always applied regardless of whether the module or function sa
#### Example Instrumented Module and Function Info Output
> `omnitrace -o lulesh.inst --label file line args --simulate -- lulesh`
> ***`omnitrace -o lulesh.inst --label file line args --simulate -- lulesh`***
After the heuristics are applied in [Example Available Module and Function Info Output](#example-available-module-and-function-info-output),
the selected module/functions are:
@@ -674,7 +674,7 @@ You will have to remove or modify the rpath in order to get `foo.inst` to resolv
#### Modifying RPATH
> Requires `patchelf` package
> ***Requires `patchelf` package***
```shell
patchelf --remove-rpath <exe-or-library>
@@ -61,7 +61,7 @@ $ omnitrace -- ./foo
### Core Configuration Settings
> See also: [Customizing Omnitrace Runtime](runtime.md)
> ***See also: [Customizing Omnitrace Runtime](runtime.md)***
| Setting | Value | Description |
|---------------------------|--------------------|---------------------------------------------------------------------------------------------------|
@@ -105,10 +105,10 @@ set `OMNITRACE_OUTPUT_PREFIX="%argt%-"` and let omnitrace cleanly organize the o
| `%r` | Shorthand for `%rank%` |
| `%s` | Shorthand for `%size%` |
> NOTE: any output prefix key which contain a '/' will have the `/` characters
> replaced with `_` and any leading underscores will be stripped, e.g. if `%arg0%` is `/usr/bin/foo`, this
> will translate to `usr_bin_foo`. Additionally, any `%arg<N>%` keys which do not have a command line argument
> at position `<N>` will be ignored.
> ***Any output prefix key which contain a `/` will have the `/` characters***
> ***replaced with `_` and any leading underscores will be stripped, e.g. if `%arg0%` is `/usr/bin/foo`, this***
> ***will translate to `usr_bin_foo`. Additionally, any `%arg<N>%` keys which do not have a command line argument***
> ***at position `<N>` will be ignored.***
## Perfetto Output
@@ -140,7 +140,7 @@ requires significantly less memory than perfetto, this is not the case in timeli
### Timemory Text Output
> Hint: the generation of text output is configurable via `OMNITRACE_TEXT_OUTPUT`
> ***Hint: the generation of text output is configurable via `OMNITRACE_TEXT_OUTPUT`***
Timemory text output files are meant for human-consumption (use JSON formats for analysis)
and as such, some fields such as the `LABEL` fields may be truncated for readability.
@@ -336,7 +336,7 @@ component explicitly sets type-traits which specify that the data is only releva
### Timemory Flat JSON Output
> Hint: the generation of flat JSON output is configurable via `OMNITRACE_JSON_OUTPUT`
> ***Hint: the generation of flat JSON output is configurable via `OMNITRACE_JSON_OUTPUT`***
Timemory provides two JSON output formats. The flat JSON output files are similar to the text files: the hierarchical information
is represented by the indentation of the `"prefix"` field and the `"depth"` field. All the data entries are in a single JSON array,
@@ -521,7 +521,7 @@ This script applied to the corresponding JSON output from [Text Output Example](
### Timemory Hierarchical JSON Output
> Hint: the generation of hierarchical JSON output is configurable via `OMNITRACE_TREE_OUTPUT`
> ***Hint: the generation of hierarchical JSON output is configurable via `OMNITRACE_TREE_OUTPUT`***
The hierarchical JSON output (extension: `.tree.json`) contains the very similar data to the flat JSON output, however,
it's structure requires processing through recursion. The main use of these files are their analysis support
@@ -0,0 +1,297 @@
# Python Support
```eval_rst
.. toctree::
:glob:
:maxdepth: 3
```
[Omnitrace](https://github.com/AMDResearch/omnitrace) supports profiling Python code at the source-level and/or the script-level.
Python support is enabled via the `OMNITRACE_USE_PYTHON` and `OMNITRACE_PYTHON_VERSION=<MAJOR>.<MINOR>` CMake options.
> ***When using omnitrace for Python, the Python interpreter major and minor version (e.g. 3.7) must match the interpreter major and minor version***
> ***used when compiling the Python bindings, i.e. when building omnitrace, a `libpyomnitrace.<IMPL>-<VERSION>-<ARCH>-<OS>-<ABI>.so` will be generated***
> ***where `IMPL` is the Python implementation, `VERSION` is the major and minor version, `ARCH` is the architecture,***
> ***`OS` is the operating system, and `ABI` is the application binary interface; Example: `libpyomnitrace.cpython-38-x86_64-linux-gnu.so`.***
## Getting Started
The omnitrace Python package is installed in `lib/pythonX.Y/site-packages/omnitrace`. In order to ensure the Python interpreter can find the omnitrace package,
add this path to the `PYTHONPATH` environment variable, e.g.:
```bash
export PYTHONPATH=/opt/omnitrace/lib/python3.8/site-packages:${PYTHONPATH}
```
If using either the `share/omnitrace/setup-env.sh` script or the modulefile in `share/modulefiles/omnitrace`, prefixing the `PYTHONPATH`
environment variable is automatically handled.
## Running Omnitrace on a Python Script
Omnitrace provides an `omnitrace-python` helper bash script which effectively handles ensuring `PYTHONPATH` is properly set and the correct python interpreter is used.
Thus the following are effectively equivalent:
```bash
omnitrace-python --help
export PYTHONPATH=/opt/omnitrace/lib/python3.8/site-packages:${PYTHONPATH}
python3.8 -m omnitrace --help
```
> ***`omnitrace-python` / `python -m omnitrace` uses the same command-line syntax as the `omnitrace` executable (i.e. `omnitrace-python <OMNITRACE_ARGS> -- <SCRIPT> <SCRIPT_ARGS>`) and has similar options.***
### Command Line Options
Use `omnitrace-python --help` to view the available options:
```console
usage: omnitrace [-h] [-b] [-c FILE] [-s FILE] [--trace-c [BOOL]] [-a [BOOL]] [-l [BOOL]] [-f [BOOL]] [-F [BOOL]] [-I FUNC [FUNC ...]] [-E FUNC [FUNC ...]] [-R FUNC [FUNC ...]] [-MI FILE [FILE ...]] [-ME FILE [FILE ...]] [-MR FILE [FILE ...]] [-v VERBOSITY]
optional arguments:
-h, --help show this help message and exit
-b, --builtin Put 'profile' in the builtins. Use '@profile' to decorate a single function, or 'with profile:' to profile a single section of code.
-c FILE, --config FILE
Omnitrace configuration file
-s FILE, --setup FILE
Code to execute before the code to profile
--trace-c [BOOL] Enable profiling C functions
-a [BOOL], --include-args [BOOL]
Encode the argument values
-l [BOOL], --include-line [BOOL]
Encode the function line number
-f [BOOL], --include-file [BOOL]
Encode the function filename
-F [BOOL], --full-filepath [BOOL]
Encode the full function filename (instead of basename)
-I FUNC [FUNC ...], --function-include FUNC [FUNC ...]
Include any entries with these function names
-E FUNC [FUNC ...], --function-exclude FUNC [FUNC ...]
Filter out any entries with these function names
-R FUNC [FUNC ...], --function-restrict FUNC [FUNC ...]
Select only entries with these function names
-MI FILE [FILE ...], --module-include FILE [FILE ...]
Include any entries from these files
-ME FILE [FILE ...], --module-exclude FILE [FILE ...]
Filter out any entries from these files
-MR FILE [FILE ...], --module-restrict FILE [FILE ...]
Select only entries from these files
-v VERBOSITY, --verbosity VERBOSITY
Logging verbosity
usage: python3 -m omnitrace <OMNITRACE_ARGS> -- <SCRIPT> <SCRIPT_ARGS>
```
> ***The `--trace-c` option does not incorporate omnitrace's dynamic instrumentation support, rather it just enables profiling the underlying C function call within the Python interpreter.***
### Selective Instrumentation
Similar to the `omnitrace` executable, command-line options exist for restricting, including, and excluded the desired functions and modules, e.g. `--function-exclude "^__init__$"`.
Alternatively, adding `@profile` decorator to the primary function of interest in combination with the `-b` / `--builtin` option will narrow the scope of the
instrumentation to these function(s) and their children.
Consider the following Python code (`example.py`):
```python
import sys
def fib(n):
return n if n < 2 else (fib(n - 1) + fib(n - 2))
def inefficient(n):
a = 0
for i in range(n):
a += i
for j in range(n):
a += j
return a
def run(n):
return fib(n) + inefficient(n)
if __name__ == "__main__":
run(20)
```
Using `omnitrace-python ./example.py` with `OMNITRACE_USE_TIMEMORY=ON` and `OMNITRACE_TIMEMORY_COMPONENTS=trip_count` would produce:
```console
|-------------------------------------------------------------------------------------------|
| COUNTS NUMBER OF INVOCATIONS |
|-------------------------------------------------------------------------------------------|
| LABEL | COUNT | DEPTH | METRIC | SUM |
|---------------------------------------------------|--------|--------|------------|--------|
| |0>>> run | 1 | 0 | trip_count | 1 |
| |0>>> |_fib | 10946 | 1 | trip_count | 10946 |
| |0>>> |_fib | 4181 | 2 | trip_count | 4181 |
| |0>>> |_fib | 2584 | 3 | trip_count | 2584 |
| |0>>> |_fib | 1597 | 4 | trip_count | 1597 |
| |0>>> |_fib | 987 | 5 | trip_count | 987 |
| |0>>> |_fib | 610 | 6 | trip_count | 610 |
| |0>>> |_fib | 377 | 7 | trip_count | 377 |
| |0>>> |_fib | 233 | 8 | trip_count | 233 |
| |0>>> |_fib | 144 | 9 | trip_count | 144 |
| |0>>> |_fib | 89 | 10 | trip_count | 89 |
| |0>>> |_fib | 55 | 11 | trip_count | 55 |
| |0>>> |_fib | 34 | 12 | trip_count | 34 |
| |0>>> |_fib | 21 | 13 | trip_count | 21 |
| |0>>> |_fib | 13 | 14 | trip_count | 13 |
| |0>>> |_fib | 8 | 15 | trip_count | 8 |
| |0>>> |_fib | 5 | 16 | trip_count | 5 |
| |0>>> |_fib | 3 | 17 | trip_count | 3 |
| |0>>> |_fib | 2 | 18 | trip_count | 2 |
| |0>>> |_fib | 1 | 19 | trip_count | 1 |
| |0>>> |_fib | 1 | 20 | trip_count | 1 |
| |0>>> |_inefficient | 1 | 1 | trip_count | 1 |
|-------------------------------------------------------------------------------------------|
```
If the `inefficient` function were decorated with `@profile`:
```python
@profile
def inefficient(n):
# ...
```
And executed with `omnitrace-python -b -- ./example.py`, omnitrace would produce:
```console
|-----------------------------------------------------------|
| COUNTS NUMBER OF INVOCATIONS |
|-----------------------------------------------------------|
| LABEL | COUNT | DEPTH | METRIC | SUM |
|-------------------|--------|--------|------------|--------|
| |0>>> inefficient | 1 | 0 | trip_count | 1 |
|-----------------------------------------------------------|
```
## Omnitrace Python Source Instrumentation
Starting from the unmodified `example.py` script above, we start by importing the `omnitrace` module:
```python
import sys
import omnitrace # import omnitrace
def fib(n):
# ... etc. ...
```
Then, we can add `@omnitrace.profile()` to the `run` function:
```python
@omnitrace.profile()
def run(n):
# ...
```
Or we can use `omnitrace.profile()` as a context-manager around `run(20)`:
```python
if __name__ == "__main__":
with omnitrace.profile():
run(20)
```
The results for both of the source-level instrumentation modes are identical to the original `omnitrace-python ./example.py` results:
```console
|-------------------------------------------------------------------------------------------|
| COUNTS NUMBER OF INVOCATIONS |
|-------------------------------------------------------------------------------------------|
| LABEL | COUNT | DEPTH | METRIC | SUM |
|---------------------------------------------------|--------|--------|------------|--------|
| |0>>> run | 1 | 0 | trip_count | 1 |
| |0>>> |_fib | 10946 | 1 | trip_count | 10946 |
| |0>>> |_fib | 4181 | 2 | trip_count | 4181 |
| |0>>> |_fib | 2584 | 3 | trip_count | 2584 |
| |0>>> |_fib | 1597 | 4 | trip_count | 1597 |
| |0>>> |_fib | 987 | 5 | trip_count | 987 |
| |0>>> |_fib | 610 | 6 | trip_count | 610 |
| |0>>> |_fib | 377 | 7 | trip_count | 377 |
| |0>>> |_fib | 233 | 8 | trip_count | 233 |
| |0>>> |_fib | 144 | 9 | trip_count | 144 |
| |0>>> |_fib | 89 | 10 | trip_count | 89 |
| |0>>> |_fib | 55 | 11 | trip_count | 55 |
| |0>>> |_fib | 34 | 12 | trip_count | 34 |
| |0>>> |_fib | 21 | 13 | trip_count | 21 |
| |0>>> |_fib | 13 | 14 | trip_count | 13 |
| |0>>> |_fib | 8 | 15 | trip_count | 8 |
| |0>>> |_fib | 5 | 16 | trip_count | 5 |
| |0>>> |_fib | 3 | 17 | trip_count | 3 |
| |0>>> |_fib | 2 | 18 | trip_count | 2 |
| |0>>> |_fib | 1 | 19 | trip_count | 1 |
| |0>>> |_fib | 1 | 20 | trip_count | 1 |
| |0>>> |_inefficient | 1 | 1 | trip_count | 1 |
|-------------------------------------------------------------------------------------------|
```
> ***When `omnitrace-python` is used without built-ins, the profiling results will likely be cluttered by***
> ***numerous functions called during the importing of more complex modules, e.g. `import numpy`.***
### Omnitrace Python Source Instrumentation Configuration
Within the Python source code, the profiler can be configured by directly modifying the `omnitrace.profiler.config` data fields.
```python
import sys
def fib(n):
return n if n < 2 else (fib(n - 1) + fib(n - 2))
def inefficient(n):
a = 0
for i in range(n):
a += i
for j in range(n):
a += j
return a
def run(n):
return fib(n) + inefficient(n)
if __name__ == "__main__":
from omnitrace.profiler import config
from omnitrace import profile
config.include_args = True
config.include_filename = False
config.include_line = False
config.restrict_functions += ["fib", "run"]
with profile():
run(5)
```
Executing this script would produce:
```console
|------------------------------------------------------------------|
| COUNTS NUMBER OF INVOCATIONS |
|------------------------------------------------------------------|
| LABEL | COUNT | DEPTH | METRIC | SUM |
|--------------------------|--------|--------|------------|--------|
| |0>>> run(n=5) | 1 | 0 | trip_count | 1 |
| |0>>> |_fib(n=5) | 1 | 1 | trip_count | 1 |
| |0>>> |_fib(n=4) | 1 | 2 | trip_count | 1 |
| |0>>> |_fib(n=3) | 1 | 3 | trip_count | 1 |
| |0>>> |_fib(n=2) | 1 | 4 | trip_count | 1 |
| |0>>> |_fib(n=1) | 1 | 5 | trip_count | 1 |
| |0>>> |_fib(n=0) | 1 | 5 | trip_count | 1 |
| |0>>> |_fib(n=1) | 1 | 4 | trip_count | 1 |
| |0>>> |_fib(n=2) | 1 | 3 | trip_count | 1 |
| |0>>> |_fib(n=1) | 1 | 4 | trip_count | 1 |
| |0>>> |_fib(n=0) | 1 | 4 | trip_count | 1 |
| |0>>> |_fib(n=3) | 1 | 2 | trip_count | 1 |
| |0>>> |_fib(n=2) | 1 | 3 | trip_count | 1 |
| |0>>> |_fib(n=1) | 1 | 4 | trip_count | 1 |
| |0>>> |_fib(n=0) | 1 | 4 | trip_count | 1 |
| |0>>> |_fib(n=1) | 1 | 3 | trip_count | 1 |
|------------------------------------------------------------------|
```
@@ -22,7 +22,7 @@ In order to view the list of the available runtime settings, their current value
omnitrace-avail --description
```
> HINT: use `--brief` to suppress printing current value and/or `-c 0` to suppress truncation of the descriptions
> ***Hint: use `--brief` to suppress printing current value and/or `-c 0` to suppress truncation of the descriptions***
Any setting which is boolean (`omnitrace-avail --settings --value --brief --filter bool`) accepts a case insensitive
match to nearly all common expressions for boolean logic: ON, OFF, YES, NO, TRUE, FALSE, 0, 1, etc.
@@ -5,14 +5,15 @@ message()
echo -e "\n\n##### ${@}... #####\n"
}
WORK_DIR=$(dirname ${BASH_SOURCE[0]})
WORK_DIR=$(cd $(dirname ${BASH_SOURCE[0]}) && pwd)
SOURCE_DIR=$(cd ${WORK_DIR}/../.. &> /dev/null && pwd)
message "Working directory is ${WORK_DIR}"
message "Source directory is ${SOURCE_DIR}"
message "Changing directory to ${WORK_DIR}"
cd ${WORK_DIR}
SOURCE_DIR=$(cd ${WORK_DIR}/.. &> /dev/null && pwd)
message "Source directory is ${SOURCE_DIR}"
message "Generating omnitrace.dox"
cmake -DSOURCE_DIR=${SOURCE_DIR} -P ${WORK_DIR}/generate-doxyfile.cmake
@@ -22,8 +23,10 @@ doxygen omnitrace.dox
message "Building html documentation"
make html
message "Removing stale documentation in ${SOURCE_DIR}/docs/"
rm -rf ${SOURCE_DIR}/docs/*
if [ -d ${SOURCE_DIR}/docs ]; then
message "Removing stale documentation in ${SOURCE_DIR}/docs/"
echo rm -rf ${SOURCE_DIR}/docs/*
message "Copying docs-source/_build/html/* to docs/"
cp -r ${WORK_DIR}/_build/html/* ${SOURCE_DIR}/docs/
message "Copying source/docs/_build/html/* to docs/"
echo cp -r ${WORK_DIR}/_build/html/* ${SOURCE_DIR}/docs/
fi
@@ -2,7 +2,7 @@
WORK_DIR=$(dirname ${BASH_SOURCE[0]})
SOURCE_DIR=$(cd ${WORK_DIR}/.. &> /dev/null && pwd)
SOURCE_DIR=$(cd ${WORK_DIR}/../.. &> /dev/null && pwd)
cmake -DSOURCE_DIR=${SOURCE_DIR} -P generate-doxyfile.cmake