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]
This commit is contained in:
کامیت شده توسط
GitHub
والد
6daac0f60c
کامیت
e7546b201a
@@ -65,7 +65,12 @@ parse:
|
||||
RUN_ARGS: '*'
|
||||
ENVIRONMENT: '*'
|
||||
LABELS: '*'
|
||||
DEPENDS: '*'
|
||||
COMMAND: '*'
|
||||
PROPERTIES: '*'
|
||||
PASS_REGULAR_EXPRESSION: '*'
|
||||
FAIL_REGULAR_EXPRESSION: '*'
|
||||
SKIP_REGULAR_EXPRESSION: '*'
|
||||
rocm_version_message:
|
||||
flags:
|
||||
- STATUS
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
name: cancel-builds-on-update
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ['formatting', 'ubuntu-bionic', 'ubuntu-focal-dyninst-package', 'ubuntu-focal-external-rocm', 'ubuntu-focal-external', 'ubuntu-focal', 'cpack-bionic', 'cpack-bionic-rocm', 'cpack-focal', cpack-focal-rocm']
|
||||
workflows: ['formatting', 'ubuntu-bionic', 'ubuntu-focal-external-rocm', 'ubuntu-focal-external', 'ubuntu-focal', 'cpack-bionic', 'cpack-bionic-rocm', 'cpack-focal', cpack-focal-rocm']
|
||||
types: ['requested']
|
||||
|
||||
jobs:
|
||||
cancel-duplicate-workflow-runs:
|
||||
name: "Cancel duplicate workflow runs"
|
||||
name: "Cancel duplicate ${{ github.event.workflow_run.id }} workflow runs"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: potiuk/cancel-workflow-runs@master
|
||||
name: "Cancel duplicate workflow runs"
|
||||
name: "Cancel duplicate ${{ github.event.workflow_run.id }} workflow runs"
|
||||
with:
|
||||
cancelMode: duplicates
|
||||
cancelFutureDuplicates: true
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
sourceRunId: ${{ github.event.workflow_run.id }}
|
||||
notifyPRCancel: true
|
||||
notifyPRCancel: false
|
||||
skipEventTypes: '["schedule"]'
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
[](https://github.com/AMDResearch/omnitrace/actions/workflows/ubuntu-focal.yml)
|
||||
[](https://github.com/AMDResearch/omnitrace/actions/workflows/ubuntu-focal-external-rocm.yml)
|
||||
|
||||
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.***
|
||||
|
||||
The documentation for omnitrace is available at [amdresearch.github.io/omnitrace](https://amdresearch.github.io/omnitrace/).
|
||||
|
||||
## Using Omnitrace Executable
|
||||
@@ -19,7 +21,7 @@ omnitrace <omnitrace-options> -- <exe-or-library> <exe-options>
|
||||
|
||||
`omnitrace-avail -Sd` will provide a list of all the possible omnitrace settings, their current value, and a description of the setting.
|
||||
|
||||
> NOTE: Some settings may only affect the timemory backend.
|
||||
> ***Some settings may only affect the timemory backend.***
|
||||
|
||||
These settings can be set via environment variables or placed in a config file and specified via `OMNITRACE_CONFIG_FILE=/path/to/config/file`. The config file
|
||||
can be a text, JSON, or XML file. Some of the most relevant settings are provided below:
|
||||
@@ -91,8 +93,8 @@ omnitrace -R '^hip' -o ./lib/libamdhip64.so.4 -- /opt/rocm/lib/libamdhip64.so.4
|
||||
export LD_LIBRARY_PATH=${PWD}/lib:${LD_LIBRARY_PATH}
|
||||
```
|
||||
|
||||
> NOTE: Verify via `ldd` that your executable will load the instrumented library -- if you built your executable with
|
||||
> an RPATH to the original library's directory, then prefixing `LD_LIBRARY_PATH` will have no effect.
|
||||
> ***Verify via `ldd` that your executable will load the instrumented library -- if you built your executable with***
|
||||
> ***an RPATH to the original library's directory, then prefixing `LD_LIBRARY_PATH` will have no effect.***
|
||||
|
||||
Once you have rewritten your executable and/or libraries with instrumentation, you can just run the (instrumented) executable
|
||||
or exectuable which loads the instrumented libraries normally, e.g.:
|
||||
@@ -186,10 +188,10 @@ Once Julia is installed, install the necessary packages (this operation only nee
|
||||
julia -e 'using Pkg; for name in ["JSON", "DataFrames", "Dates", "CSV", "Chain", "PrettyTables"]; Pkg.add(name); end'
|
||||
```
|
||||
|
||||
> NOTE: Using `rocprof` externally for tracing is deprecated. The current version has built-in support for
|
||||
> recording the GPU activity and HIP API calls. If you want to use an external rocprof, either
|
||||
> configure CMake with `-DOMNITRACE_USE_ROCTRACER=OFF` or explicitly set `OMNITRACE_ROCTRACER_ENABLED=OFF` in the
|
||||
> environment.
|
||||
> ***Using `rocprof` externally for tracing is deprecated. The current version has built-in support for***
|
||||
> ***recording the GPU activity and HIP API calls. If you want to use an external rocprof, either***
|
||||
> ***configure CMake with `-DOMNITRACE_USE_ROCTRACER=OFF` or explicitly set `OMNITRACE_ROCTRACER_ENABLED=OFF` in the***
|
||||
> ***environment.***
|
||||
|
||||
Use the `omnitrace-merge.jl` Julia script to merge rocprof and perfetto traces.
|
||||
|
||||
|
||||
@@ -30,9 +30,7 @@ def run(nfib):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
omnitrace.initialize(__file__)
|
||||
nfib = int(sys.argv[1]) if len(sys.argv) > 1 else 20
|
||||
for i in range(5):
|
||||
ans = run(nfib)
|
||||
print(f"[{i}] fibonacci({nfib}) = {ans}")
|
||||
omnitrace.finalize()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -46,6 +46,10 @@ struct fork_gotcha : comp::base<fork_gotcha, void>
|
||||
|
||||
// this will get called right after fork with the return value
|
||||
static void audit(const gotcha_data_t& _data, audit::outgoing, pid_t _pid);
|
||||
|
||||
// silence SFINAE disabled for omnitrace::fork_gotcha warnings
|
||||
static inline void start() {}
|
||||
static inline void stop() {}
|
||||
};
|
||||
|
||||
using fork_gotcha_t = comp::gotcha<4, tim::component_tuple<fork_gotcha>, api::omnitrace>;
|
||||
|
||||
@@ -25,15 +25,23 @@
|
||||
|
||||
#include <timemory/backends/process.hpp>
|
||||
#include <timemory/backends/threading.hpp>
|
||||
#include <timemory/environment.hpp>
|
||||
#include <timemory/mpl/apply.hpp>
|
||||
#include <timemory/utility/macros.hpp>
|
||||
#include <timemory/utility/types.hpp>
|
||||
#include <timemory/variadic/macros.hpp>
|
||||
|
||||
#include <pybind11/detail/common.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pyerrors.h>
|
||||
|
||||
#include <cctype>
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <locale>
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace pyomnitrace
|
||||
@@ -48,18 +56,94 @@ generate(py::module& _pymod);
|
||||
PYBIND11_MODULE(libpyomnitrace, omni)
|
||||
{
|
||||
using namespace pyomnitrace;
|
||||
py::doc("omnitrace profiler for python");
|
||||
pyprofile::generate(omni);
|
||||
|
||||
static bool _is_initialized = false;
|
||||
static bool _is_finalized = false;
|
||||
static auto _get_use_mpi = []() {
|
||||
bool _use_mpi = false;
|
||||
try
|
||||
{
|
||||
py::module::import("mpi4py");
|
||||
_use_mpi = true;
|
||||
} catch(py::error_already_set& _exc)
|
||||
{
|
||||
if(!_exc.matches(PyExc_ImportError)) throw;
|
||||
}
|
||||
return _use_mpi;
|
||||
};
|
||||
|
||||
omni.def(
|
||||
"is_initialized", []() { return _is_initialized; }, "Initialization state");
|
||||
|
||||
omni.def(
|
||||
"is_finalized", []() { return _is_finalized; }, "Finalization state");
|
||||
|
||||
omni.def(
|
||||
"initialize",
|
||||
[](const std::string& _v) {
|
||||
omnitrace_set_mpi(false, false);
|
||||
if(_is_initialized)
|
||||
throw std::runtime_error("Error! omnitrace is already initialized");
|
||||
_is_initialized = true;
|
||||
omnitrace_set_mpi(_get_use_mpi(), false);
|
||||
omnitrace_init("trace", false, _v.c_str());
|
||||
},
|
||||
"Initialize omnitrace");
|
||||
|
||||
omni.def(
|
||||
"finalize", []() { omnitrace_finalize(); }, "Initialize omnitrace");
|
||||
"initialize",
|
||||
[](const py::list& _v) {
|
||||
if(_is_initialized)
|
||||
throw std::runtime_error("Error! omnitrace is already initialized");
|
||||
_is_initialized = true;
|
||||
omnitrace_set_mpi(_get_use_mpi(), false);
|
||||
std::string _cmd = {};
|
||||
std::string _cmd_line = {};
|
||||
for(auto&& itr : _v)
|
||||
{
|
||||
if(_cmd.empty()) _cmd = itr.cast<std::string>();
|
||||
_cmd_line += " " + itr.cast<std::string>();
|
||||
}
|
||||
if(!_cmd_line.empty())
|
||||
{
|
||||
_cmd_line.substr(_cmd_line.find_first_not_of(' '));
|
||||
tim::set_env("OMNITRACE_COMMAND_LINE", _cmd_line, 0);
|
||||
}
|
||||
omnitrace_init("trace", false, _cmd.c_str());
|
||||
},
|
||||
"Initialize omnitrace");
|
||||
|
||||
omni.def(
|
||||
"initialize",
|
||||
[](const std::string& _v) {
|
||||
if(_is_initialized)
|
||||
throw std::runtime_error("Error! omnitrace is already initialized");
|
||||
_is_initialized = true;
|
||||
bool _use_mpi = false;
|
||||
try
|
||||
{
|
||||
py::module::import("mpi4py");
|
||||
_use_mpi = true;
|
||||
} catch(py::error_already_set& _exc)
|
||||
{
|
||||
if(!_exc.matches(PyExc_ImportError)) throw;
|
||||
}
|
||||
omnitrace_set_mpi(_use_mpi, false);
|
||||
omnitrace_init("trace", false, _v.c_str());
|
||||
},
|
||||
"Initialize omnitrace");
|
||||
|
||||
omni.def(
|
||||
"finalize",
|
||||
[]() {
|
||||
if(_is_finalized)
|
||||
throw std::runtime_error("Error! omnitrace is already finalized");
|
||||
_is_finalized = true;
|
||||
omnitrace_finalize();
|
||||
},
|
||||
"Initialize omnitrace");
|
||||
|
||||
py::doc("omnitrace profiler for python");
|
||||
pyprofile::generate(omni);
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
@@ -69,38 +153,39 @@ namespace pyomnitrace
|
||||
namespace pyprofile
|
||||
{
|
||||
//
|
||||
using profiler_t = std::pair<std::function<void()>, std::function<void()>>;
|
||||
using profiler_t = std::function<void()>;
|
||||
using profiler_vec_t = std::vector<profiler_t>;
|
||||
using profiler_label_map_t = std::unordered_map<std::string, profiler_vec_t>;
|
||||
using profiler_index_map_t = std::unordered_map<uint32_t, profiler_label_map_t>;
|
||||
using strset_t = std::unordered_set<std::string>;
|
||||
//
|
||||
namespace
|
||||
{
|
||||
strset_t default_exclude_functions = { "^<.*>$" };
|
||||
strset_t default_exclude_filenames = { "(encoder|decoder|threading).py$", "^<.*>$" };
|
||||
} // namespace
|
||||
//
|
||||
struct config
|
||||
{
|
||||
bool is_running = false;
|
||||
bool trace_c = false;
|
||||
bool include_internal = false;
|
||||
bool include_args = false;
|
||||
bool include_line = false;
|
||||
bool include_filename = false;
|
||||
bool full_filepath = false;
|
||||
int32_t ignore_stack_depth = 0;
|
||||
int32_t base_stack_depth = -1;
|
||||
std::string base_module_path = {};
|
||||
strset_t include_functions = {};
|
||||
strset_t include_filenames = {};
|
||||
strset_t exclude_functions = { "^(FILE|FUNC|LINE)$",
|
||||
"^get_fcode$",
|
||||
"^_(_exit__|handle_fromlist|shutdown|get_sep)$",
|
||||
"^is(function|class)$",
|
||||
"^basename$",
|
||||
"^<.*>$" };
|
||||
strset_t exclude_filenames = {
|
||||
"(__init__|__main__|functools|encoder|decoder|_pylab_helpers|threading).py$",
|
||||
"^<.*>$"
|
||||
};
|
||||
profiler_index_map_t records = {};
|
||||
int32_t verbose = 0;
|
||||
bool is_running = false;
|
||||
bool trace_c = false;
|
||||
bool include_internal = false;
|
||||
bool include_args = false;
|
||||
bool include_line = false;
|
||||
bool include_filename = false;
|
||||
bool full_filepath = false;
|
||||
int32_t ignore_stack_depth = 0;
|
||||
int32_t base_stack_depth = -1;
|
||||
int32_t verbose = 0;
|
||||
int64_t depth_tracker = 0;
|
||||
std::string base_module_path = {};
|
||||
strset_t restrict_functions = {};
|
||||
strset_t restrict_filenames = {};
|
||||
strset_t include_functions = {};
|
||||
strset_t include_filenames = {};
|
||||
strset_t exclude_functions = default_exclude_functions;
|
||||
strset_t exclude_filenames = default_exclude_filenames;
|
||||
std::vector<profiler_t> records = {};
|
||||
};
|
||||
//
|
||||
inline config&
|
||||
@@ -112,20 +197,24 @@ get_config()
|
||||
auto _cnt = _count++;
|
||||
if(_cnt == 0) return _instance;
|
||||
|
||||
auto* _tmp = new config{};
|
||||
_tmp->is_running = _instance->is_running;
|
||||
_tmp->trace_c = _instance->trace_c;
|
||||
_tmp->include_internal = _instance->include_internal;
|
||||
_tmp->include_args = _instance->include_args;
|
||||
_tmp->include_line = _instance->include_line;
|
||||
_tmp->include_filename = _instance->include_filename;
|
||||
_tmp->full_filepath = _instance->full_filepath;
|
||||
_tmp->base_module_path = _instance->base_module_path;
|
||||
_tmp->include_functions = _instance->include_functions;
|
||||
_tmp->include_filenames = _instance->include_filenames;
|
||||
_tmp->exclude_functions = _instance->exclude_functions;
|
||||
_tmp->exclude_filenames = _instance->exclude_filenames;
|
||||
_tmp->verbose = _instance->verbose;
|
||||
auto* _tmp = new config{};
|
||||
_tmp->is_running = _instance->is_running;
|
||||
_tmp->trace_c = _instance->trace_c;
|
||||
_tmp->include_internal = _instance->include_internal;
|
||||
_tmp->include_args = _instance->include_args;
|
||||
_tmp->include_line = _instance->include_line;
|
||||
_tmp->include_filename = _instance->include_filename;
|
||||
_tmp->full_filepath = _instance->full_filepath;
|
||||
_tmp->base_module_path = _instance->base_module_path;
|
||||
_tmp->restrict_functions = _instance->restrict_functions;
|
||||
_tmp->restrict_filenames = _instance->restrict_filenames;
|
||||
_tmp->include_functions = _instance->include_functions;
|
||||
_tmp->include_filenames = _instance->include_filenames;
|
||||
_tmp->exclude_functions = _instance->exclude_functions;
|
||||
_tmp->exclude_filenames = _instance->exclude_filenames;
|
||||
_tmp->verbose = _instance->verbose;
|
||||
// if full filepath is specified, include filename is implied
|
||||
if(_tmp->full_filepath && !_tmp->include_filename) _tmp->include_filename = true;
|
||||
return _tmp;
|
||||
}();
|
||||
return *_tl_instance;
|
||||
@@ -160,7 +249,6 @@ profiler_function(py::object pframe, const char* swhat, py::object arg)
|
||||
: (strcmp(swhat, "return") == 0) ? PyTrace_RETURN
|
||||
: (strcmp(swhat, "c_return") == 0) ? PyTrace_C_RETURN
|
||||
: -1;
|
||||
|
||||
// only support PyTrace_{CALL,C_CALL,RETURN,C_RETURN}
|
||||
if(what < 0)
|
||||
{
|
||||
@@ -170,6 +258,29 @@ profiler_function(py::object pframe, const char* swhat, py::object arg)
|
||||
return;
|
||||
}
|
||||
|
||||
auto _update_ignore_stack_depth = [what]() {
|
||||
switch(what)
|
||||
{
|
||||
case PyTrace_CALL: ++_config.ignore_stack_depth; break;
|
||||
case PyTrace_RETURN: --_config.ignore_stack_depth; break;
|
||||
default: break;
|
||||
}
|
||||
};
|
||||
|
||||
if(_config.ignore_stack_depth > 0)
|
||||
{
|
||||
if(_config.verbose > 2)
|
||||
TIMEMORY_PRINT_HERE("%s :: %s :: %u", "Ignoring call/return", swhat,
|
||||
_config.ignore_stack_depth);
|
||||
_update_ignore_stack_depth();
|
||||
return;
|
||||
}
|
||||
else if(_config.ignore_stack_depth < 0)
|
||||
{
|
||||
TIMEMORY_PRINT_HERE("WARNING! ignore_stack_depth is < 0 :: ",
|
||||
_config.ignore_stack_depth);
|
||||
}
|
||||
|
||||
// if PyTrace_C_{CALL,RETURN} is not enabled
|
||||
if(!_config.trace_c && (what == PyTrace_C_CALL || what == PyTrace_C_RETURN))
|
||||
{
|
||||
@@ -178,23 +289,6 @@ profiler_function(py::object pframe, const char* swhat, py::object arg)
|
||||
return;
|
||||
}
|
||||
|
||||
// get the function name
|
||||
auto _get_funcname = [&]() -> std::string {
|
||||
return py::cast<std::string>(frame->f_code->co_name);
|
||||
};
|
||||
|
||||
// get the filename
|
||||
auto _get_filename = [&]() -> std::string {
|
||||
return py::cast<std::string>(frame->f_code->co_filename);
|
||||
};
|
||||
|
||||
// get the basename of the filename
|
||||
auto _get_basename = [&](const std::string& _fullpath) {
|
||||
if(_fullpath.find('/') != std::string::npos)
|
||||
return _fullpath.substr(_fullpath.find_last_of('/') + 1);
|
||||
return _fullpath;
|
||||
};
|
||||
|
||||
// get the arguments
|
||||
auto _get_args = [&]() {
|
||||
auto inspect = py::module::import("inspect");
|
||||
@@ -239,35 +333,54 @@ profiler_function(py::object pframe, const char* swhat, py::object arg)
|
||||
auto _find_matching = [](const strset_t& _expr, const std::string& _name) {
|
||||
const auto _rconstants =
|
||||
std::regex_constants::egrep | std::regex_constants::optimize;
|
||||
for(const auto& itr : _expr)
|
||||
for(const auto& itr : _expr) // NOLINT
|
||||
{
|
||||
if(std::regex_search(_name, std::regex(itr, _rconstants))) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
auto& _only_funcs = _config.include_functions;
|
||||
bool _force = false;
|
||||
auto& _only_funcs = _config.restrict_functions;
|
||||
auto& _incl_funcs = _config.include_functions;
|
||||
auto& _skip_funcs = _config.exclude_functions;
|
||||
auto _func = _get_funcname();
|
||||
auto _func = py::cast<std::string>(frame->f_code->co_name);
|
||||
|
||||
if(!_only_funcs.empty() && !_find_matching(_only_funcs, _func))
|
||||
if(!_only_funcs.empty())
|
||||
{
|
||||
if(_config.verbose > 1)
|
||||
TIMEMORY_PRINT_HERE("Skipping non-included function: %s", _func.c_str());
|
||||
return;
|
||||
_force = _find_matching(_only_funcs, _func);
|
||||
if(!_force)
|
||||
{
|
||||
if(_config.verbose > 2)
|
||||
TIMEMORY_PRINT_HERE("Skipping non-restricted function: %s",
|
||||
_func.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(_find_matching(_skip_funcs, _func))
|
||||
if(!_force)
|
||||
{
|
||||
if(_config.verbose > 1)
|
||||
TIMEMORY_PRINT_HERE("Skipping designated function: '%s'", _func.c_str());
|
||||
return;
|
||||
if(_find_matching(_incl_funcs, _func))
|
||||
{
|
||||
_force = true;
|
||||
}
|
||||
else if(_find_matching(_skip_funcs, _func))
|
||||
{
|
||||
if(_config.verbose > 1)
|
||||
TIMEMORY_PRINT_HERE("Skipping designated function: '%s'", _func.c_str());
|
||||
if(!_find_matching(default_exclude_functions, _func))
|
||||
_update_ignore_stack_depth();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto& _only_files = _config.include_filenames;
|
||||
auto& _only_files = _config.restrict_filenames;
|
||||
auto& _incl_files = _config.include_filenames;
|
||||
auto& _skip_files = _config.exclude_filenames;
|
||||
auto _full = _get_filename();
|
||||
auto _file = _get_basename(_full);
|
||||
auto _full = py::cast<std::string>(frame->f_code->co_filename);
|
||||
auto _file = (_full.find('/') != std::string::npos)
|
||||
? _full.substr(_full.find_last_of('/') + 1)
|
||||
: _full;
|
||||
|
||||
if(!_config.include_internal &&
|
||||
strncmp(_full.c_str(), _omnitrace_path.c_str(), _omnitrace_path.length()) == 0)
|
||||
@@ -277,18 +390,29 @@ profiler_function(py::object pframe, const char* swhat, py::object arg)
|
||||
return;
|
||||
}
|
||||
|
||||
if(!_only_files.empty() && !_find_matching(_only_files, _full))
|
||||
if(!_force && !_only_files.empty())
|
||||
{
|
||||
if(_config.verbose > 2)
|
||||
TIMEMORY_PRINT_HERE("Skipping non-included file: %s", _full.c_str());
|
||||
return;
|
||||
_force = _find_matching(_only_files, _full);
|
||||
if(!_force)
|
||||
{
|
||||
if(_config.verbose > 2)
|
||||
TIMEMORY_PRINT_HERE("Skipping non-restricted file: %s", _full.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(_find_matching(_skip_files, _full))
|
||||
if(!_force)
|
||||
{
|
||||
if(_config.verbose > 2)
|
||||
TIMEMORY_PRINT_HERE("Skipping non-included file: %s", _full.c_str());
|
||||
return;
|
||||
if(_find_matching(_incl_files, _full))
|
||||
{
|
||||
_force = true;
|
||||
}
|
||||
else if(_find_matching(_skip_files, _full))
|
||||
{
|
||||
if(_config.verbose > 2)
|
||||
TIMEMORY_PRINT_HERE("Skipping non-included file: %s", _full.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TIMEMORY_CONDITIONAL_PRINT_HERE(_config.verbose > 3, "%8s | %s%s | %s | %s", swhat,
|
||||
@@ -301,36 +425,17 @@ profiler_function(py::object pframe, const char* swhat, py::object arg)
|
||||
static thread_local strset_t _labels{};
|
||||
const auto& _label_ref = *_labels.emplace(_label).first;
|
||||
|
||||
// get the depth of the frame
|
||||
// auto _fdepth = get_depth(frame);
|
||||
static thread_local int32_t _depth_tracker = 0;
|
||||
auto _fdepth = _depth_tracker;
|
||||
switch(what)
|
||||
{
|
||||
case PyTrace_CALL:
|
||||
case PyTrace_C_CALL: _fdepth = _depth_tracker++; break;
|
||||
case PyTrace_RETURN:
|
||||
case PyTrace_C_RETURN: _fdepth = --_depth_tracker; break;
|
||||
}
|
||||
|
||||
// start function
|
||||
auto _profiler_call = [&]() {
|
||||
auto& _entry = _config.records[_fdepth][_label];
|
||||
_entry.emplace_back(
|
||||
[&_label_ref]() { omnitrace_push_region(_label_ref.c_str()); },
|
||||
_config.records.emplace_back(
|
||||
[&_label_ref]() { omnitrace_pop_region(_label_ref.c_str()); });
|
||||
_entry.back().first();
|
||||
omnitrace_push_region(_label_ref.c_str());
|
||||
};
|
||||
|
||||
// stop function
|
||||
auto _profiler_return = [&]() {
|
||||
auto fitr = _config.records.find(_fdepth);
|
||||
if(fitr == _config.records.end()) return;
|
||||
auto litr = fitr->second.find(_label);
|
||||
if(litr == fitr->second.end()) return;
|
||||
if(litr->second.empty()) return;
|
||||
litr->second.back().second();
|
||||
litr->second.pop_back();
|
||||
_config.records.back()();
|
||||
_config.records.pop_back();
|
||||
};
|
||||
|
||||
// process what
|
||||
@@ -430,13 +535,21 @@ generate(py::module& _pymod)
|
||||
CONFIGURATION_PROPERTY_LAMBDA(NAME, DOC, GET, SET) \
|
||||
}
|
||||
|
||||
CONFIGURATION_STRSET("only_functions", "Function regexes to collect exclusively",
|
||||
CONFIGURATION_STRSET("restrict_functions", "Function regexes to collect exclusively",
|
||||
get_config().restrict_functions)
|
||||
CONFIGURATION_STRSET("restrict_modules", "Filename regexes to collect exclusively",
|
||||
get_config().restrict_filenames)
|
||||
CONFIGURATION_STRSET("include_functions",
|
||||
"Function regexes to always include in collection",
|
||||
get_config().include_functions)
|
||||
CONFIGURATION_STRSET("only_filenames", "Filename regexes to collect exclusively",
|
||||
CONFIGURATION_STRSET("include_modules",
|
||||
"Filename regexes to always include in collection",
|
||||
get_config().include_filenames)
|
||||
CONFIGURATION_STRSET("skip_functions", "Function regexes to filter out of collection",
|
||||
CONFIGURATION_STRSET("exclude_functions",
|
||||
"Function regexes to filter out of collection",
|
||||
get_config().exclude_functions)
|
||||
CONFIGURATION_STRSET("skip_filenames", "Filename regexes to filter out of collection",
|
||||
CONFIGURATION_STRSET("exclude_modules",
|
||||
"Filename regexes to filter out of collection",
|
||||
get_config().exclude_filenames)
|
||||
|
||||
return _prof;
|
||||
|
||||
@@ -43,6 +43,8 @@ try:
|
||||
)
|
||||
from .libpyomnitrace import initialize
|
||||
from .libpyomnitrace import finalize
|
||||
from .libpyomnitrace import is_initialized
|
||||
from .libpyomnitrace import is_finalized
|
||||
from .libpyomnitrace.profiler import config as Config
|
||||
|
||||
config = Config
|
||||
@@ -52,6 +54,8 @@ try:
|
||||
__all__ = [
|
||||
"initialize",
|
||||
"finalize",
|
||||
"is_initialized",
|
||||
"is_finalized",
|
||||
"Profiler",
|
||||
"Config",
|
||||
"FakeProfiler",
|
||||
@@ -63,5 +67,13 @@ try:
|
||||
"noprofile",
|
||||
]
|
||||
|
||||
import atexit
|
||||
|
||||
def _finalize_at_exit():
|
||||
if not is_finalized():
|
||||
finalize()
|
||||
|
||||
atexit.register(_finalize_at_exit)
|
||||
|
||||
except Exception as e:
|
||||
print("{}".format(e))
|
||||
|
||||
@@ -93,33 +93,42 @@ def parse_args(args=None):
|
||||
else:
|
||||
raise argparse.ArgumentTypeError("Boolean value expected.")
|
||||
|
||||
parser = argparse.ArgumentParser(add_help=True)
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--config",
|
||||
default=None,
|
||||
type=str,
|
||||
help="Omnitrace configuration file",
|
||||
parser = argparse.ArgumentParser(
|
||||
"omnitrace",
|
||||
add_help=True,
|
||||
epilog="usage: {} -m omnitrace <OMNITRACE_ARGS> -- <SCRIPT> <SCRIPT_ARGS>".format(
|
||||
os.path.basename(sys.executable)
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"-b",
|
||||
"--builtin",
|
||||
action="store_true",
|
||||
help="Put 'profile' in the builtins. Use 'profile.enable()' and "
|
||||
"'profile.disable()' in your code to turn it on and off, or "
|
||||
"'@profile' to decorate a single function, or 'with profile:' "
|
||||
"to profile a single section of code.",
|
||||
help=(
|
||||
"Put 'profile' in the builtins. Use '@profile' to decorate a single function, "
|
||||
"or 'with profile:' to profile a single section of code."
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--config",
|
||||
default=None,
|
||||
type=str,
|
||||
metavar="FILE",
|
||||
help="Omnitrace configuration file",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--setup",
|
||||
default=None,
|
||||
metavar="FILE",
|
||||
help="Code to execute before the code to profile",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--trace-c",
|
||||
type=str2bool,
|
||||
nargs="?",
|
||||
metavar="BOOL",
|
||||
const=True,
|
||||
default=_profiler_config.trace_c,
|
||||
help="Enable profiling C functions",
|
||||
@@ -129,6 +138,7 @@ def parse_args(args=None):
|
||||
"--include-args",
|
||||
type=str2bool,
|
||||
nargs="?",
|
||||
metavar="BOOL",
|
||||
const=True,
|
||||
default=_profiler_config.include_args,
|
||||
help="Encode the argument values",
|
||||
@@ -138,6 +148,7 @@ def parse_args(args=None):
|
||||
"--include-line",
|
||||
type=str2bool,
|
||||
nargs="?",
|
||||
metavar="BOOL",
|
||||
const=True,
|
||||
default=_profiler_config.include_line,
|
||||
help="Encode the function line number",
|
||||
@@ -147,6 +158,7 @@ def parse_args(args=None):
|
||||
"--include-file",
|
||||
type=str2bool,
|
||||
nargs="?",
|
||||
metavar="BOOL",
|
||||
const=True,
|
||||
default=_profiler_config.include_filename,
|
||||
help="Encode the function filename",
|
||||
@@ -156,36 +168,63 @@ def parse_args(args=None):
|
||||
"--full-filepath",
|
||||
type=str2bool,
|
||||
nargs="?",
|
||||
metavar="BOOL",
|
||||
const=True,
|
||||
default=_profiler_config.full_filepath,
|
||||
help="Encode the full function filename (instead of basename)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--skip-funcs",
|
||||
"-I",
|
||||
"--function-include",
|
||||
type=str,
|
||||
nargs="+",
|
||||
default=_profiler_config.skip_functions,
|
||||
metavar="FUNC",
|
||||
default=_profiler_config.include_functions,
|
||||
help="Include any entries with these function names",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-E",
|
||||
"--function-exclude",
|
||||
type=str,
|
||||
nargs="+",
|
||||
metavar="FUNC",
|
||||
default=_profiler_config.exclude_functions,
|
||||
help="Filter out any entries with these function names",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--skip-files",
|
||||
"-R",
|
||||
"--function-restrict",
|
||||
type=str,
|
||||
nargs="+",
|
||||
default=_profiler_config.skip_filenames,
|
||||
help="Filter out any entries from these files",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--only-funcs",
|
||||
type=str,
|
||||
nargs="+",
|
||||
default=_profiler_config.only_functions,
|
||||
metavar="FUNC",
|
||||
default=_profiler_config.restrict_functions,
|
||||
help="Select only entries with these function names",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--only-files",
|
||||
"-MI",
|
||||
"--module-include",
|
||||
type=str,
|
||||
nargs="+",
|
||||
default=_profiler_config.only_filenames,
|
||||
metavar="FILE",
|
||||
default=_profiler_config.include_modules,
|
||||
help="Include any entries from these files",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-ME",
|
||||
"--module-exclude",
|
||||
type=str,
|
||||
nargs="+",
|
||||
metavar="FILE",
|
||||
default=_profiler_config.exclude_modules,
|
||||
help="Filter out any entries from these files",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-MR",
|
||||
"--module-restrict",
|
||||
type=str,
|
||||
nargs="+",
|
||||
metavar="FILE",
|
||||
default=_profiler_config.restrict_modules,
|
||||
help="Select only entries from these files",
|
||||
)
|
||||
parser.add_argument(
|
||||
@@ -282,7 +321,7 @@ def main():
|
||||
if os.path.isfile(argv[0]):
|
||||
argv[0] = os.path.realpath(argv[0])
|
||||
|
||||
initialize(argv[0])
|
||||
initialize(argv)
|
||||
|
||||
from .libpyomnitrace.profiler import config as _profiler_config
|
||||
|
||||
@@ -291,10 +330,12 @@ def main():
|
||||
_profiler_config.include_line = opts.include_line
|
||||
_profiler_config.include_filename = opts.include_file
|
||||
_profiler_config.full_filepath = opts.full_filepath
|
||||
_profiler_config.skip_functions = opts.skip_funcs
|
||||
_profiler_config.skip_filenames = opts.skip_files
|
||||
_profiler_config.only_functions = opts.only_funcs
|
||||
_profiler_config.only_filenames = opts.only_files
|
||||
_profiler_config.include_functions = opts.function_include
|
||||
_profiler_config.include_modules = opts.module_include
|
||||
_profiler_config.exclude_functions = opts.function_exclude
|
||||
_profiler_config.exclude_modules = opts.module_exclude
|
||||
_profiler_config.restrict_functions = opts.function_restrict
|
||||
_profiler_config.restrict_modules = opts.module_restrict
|
||||
_profiler_config.verbosity = opts.verbosity
|
||||
|
||||
print("[omnitrace]> profiling: {}".format(argv))
|
||||
|
||||
@@ -30,6 +30,7 @@ __version__ = "@PROJECT_VERSION@"
|
||||
__maintainer__ = "AMD Research"
|
||||
__status__ = "Development"
|
||||
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
from functools import wraps
|
||||
@@ -80,6 +81,68 @@ config = _profiler_config
|
||||
Config = _profiler_config
|
||||
|
||||
|
||||
def _file(back=2, only_basename=True, use_dirname=False, noquotes=True):
|
||||
"""
|
||||
Returns the file name
|
||||
"""
|
||||
|
||||
from os.path import basename, dirname
|
||||
|
||||
def get_fcode(back):
|
||||
fname = "<module>"
|
||||
try:
|
||||
fname = sys._getframe(back).f_code.co_filename
|
||||
except Exception as e:
|
||||
print(e)
|
||||
fname = "<module>"
|
||||
return fname
|
||||
|
||||
result = None
|
||||
if only_basename is True:
|
||||
if use_dirname is True:
|
||||
result = "{}".format(
|
||||
join(
|
||||
basename(dirname(get_fcode(back))),
|
||||
basename(get_fcode(back)),
|
||||
)
|
||||
)
|
||||
else:
|
||||
result = "{}".format(basename(get_fcode(back)))
|
||||
else:
|
||||
result = "{}".format(get_fcode(back))
|
||||
|
||||
if noquotes is False:
|
||||
result = "'{}'".format(result)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _get_argv(init_file, argv=None):
|
||||
if argv is None:
|
||||
argv = sys.argv[:]
|
||||
|
||||
if "--" in argv:
|
||||
_idx = argv.index("--")
|
||||
argv = sys.argv[(_idx + 1) :]
|
||||
|
||||
if len(argv) > 1:
|
||||
if argv[0] == "-m":
|
||||
argv = argv[1:]
|
||||
elif argv[0] == "-c":
|
||||
argv[0] = os.path.basename(sys.executable)
|
||||
else:
|
||||
while len(argv) > 1 and argv[0].startswith("-"):
|
||||
argv = argv[1:]
|
||||
if os.path.exists(argv[0]):
|
||||
break
|
||||
if len(argv) == 0:
|
||||
argv = [init_file]
|
||||
elif not os.path.exists(argv[0]):
|
||||
argv[0] = init_file
|
||||
|
||||
return argv
|
||||
|
||||
|
||||
#
|
||||
class Profiler:
|
||||
"""Provides decorators and context-manager for the omnitrace profilers"""
|
||||
@@ -118,8 +181,11 @@ class Profiler:
|
||||
)
|
||||
self._unset = 0
|
||||
self._use = (
|
||||
not _profiler_config._is_running and Profiler.is_enabled() is True
|
||||
not _profiler_config._is_running
|
||||
and Profiler.is_enabled() is True
|
||||
and not libpyomnitrace.is_finalized()
|
||||
)
|
||||
self._file = _file()
|
||||
self.debug = kwargs["debug"] if "debug" in kwargs else False
|
||||
|
||||
# ---------------------------------------------------------------------------------- #
|
||||
@@ -135,6 +201,9 @@ class Profiler:
|
||||
def configure(self):
|
||||
"""Initialize, configure the bundle, store original profiler function"""
|
||||
|
||||
if not libpyomnitrace.is_initialized():
|
||||
libpyomnitrace.initialize(_get_argv(self._file))
|
||||
|
||||
_profiler_init()
|
||||
|
||||
# store original
|
||||
@@ -157,6 +226,7 @@ class Profiler:
|
||||
not _profiler_config._is_running
|
||||
and Profiler.is_enabled() is True
|
||||
and sys.getprofile() == self._original_function
|
||||
and not libpyomnitrace.is_finalized()
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------------------------- #
|
||||
|
||||
@@ -71,17 +71,15 @@ set(_timemory_environment
|
||||
|
||||
set(_test_environment ${_base_environment} "OMNITRACE_CRITICAL_TRACE=OFF")
|
||||
|
||||
set(_fast_environment
|
||||
"OMNITRACE_USE_PERFETTO=OFF"
|
||||
"OMNITRACE_USE_TIMEMORY=OFF"
|
||||
set(_python_environment
|
||||
"OMNITRACE_USE_PERFETTO=ON"
|
||||
"OMNITRACE_USE_TIMEMORY=ON"
|
||||
"OMNITRACE_USE_SAMPLING=OFF"
|
||||
"OMNITRACE_CRITICAL_TRACE=OFF"
|
||||
"OMNITRACE_TIME_OUTPUT=OFF"
|
||||
"OMP_PROC_BIND=spread"
|
||||
"OMP_PLACES=threads"
|
||||
"OMP_NUM_THREADS=2"
|
||||
"OMNITRACE_USE_PID=OFF"
|
||||
"OMNITRACE_TIMEMORY_COMPONENTS=trip_count"
|
||||
"LD_LIBRARY_PATH=${PROJECT_BINARY_DIR}:${OMNITRACE_DYNINST_API_RT_DIR}:$ENV{LD_LIBRARY_PATH}"
|
||||
)
|
||||
"PYTHONPATH=${CMAKE_BINARY_DIR}/python:$ENV{PYTHONPATH}")
|
||||
|
||||
# -------------------------------------------------------------------------------------- #
|
||||
|
||||
@@ -245,7 +243,8 @@ function(OMNITRACE_ADD_PYTHON_TEST)
|
||||
TEST
|
||||
"STANDALONE" # options
|
||||
"NAME;FILE;TIMEOUT" # single value args
|
||||
"PROFILE_ARGS;RUN_ARGS;ENVIRONMENT;LABELS;PROPERTIES" # multiple value args
|
||||
"PROFILE_ARGS;RUN_ARGS;ENVIRONMENT;LABELS;PROPERTIES;PASS_REGULAR_EXPRESSION;FAIL_REGULAR_EXPRESSION;SKIP_REGULAR_EXPRESSION;DEPENDS;COMMAND" # multiple
|
||||
# value args
|
||||
${ARGN})
|
||||
|
||||
if(NOT TEST_TIMEOUT)
|
||||
@@ -253,56 +252,81 @@ function(OMNITRACE_ADD_PYTHON_TEST)
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED TEST_ENVIRONMENT OR "${TEST_ENVIRONMENT}" STREQUAL "")
|
||||
set(TEST_ENVIRONMENT "${_test_environment}")
|
||||
set(TEST_ENVIRONMENT "${_python_environment}")
|
||||
endif()
|
||||
|
||||
list(APPEND TEST_ENVIRONMENT "OMNITRACE_CI=ON")
|
||||
list(APPEND TEST_ENVIRONMENT "PYTHONPATH=${CMAKE_BINARY_DIR}/python:$ENV{PYTHONPATH}")
|
||||
list(APPEND TEST_LABELS "python")
|
||||
list(APPEND TEST_ENVIRONMENT "OMNITRACE_CI=ON"
|
||||
"OMNITRACE_OUTPUT_PATH=omnitrace-tests-output"
|
||||
"OMNITRACE_OUTPUT_PREFIX=${TEST_NAME}/")
|
||||
|
||||
get_filename_component(_TEST_FILE "${TEST_FILE}" NAME)
|
||||
configure_file(${TEST_FILE} ${CMAKE_BINARY_DIR}/${_TEST_FILE} @ONLY)
|
||||
|
||||
if(NOT TEST_STANDALONE)
|
||||
|
||||
endif()
|
||||
|
||||
if(TEST_STANDALONE)
|
||||
add_test(
|
||||
NAME ${TEST_NAME}-run
|
||||
COMMAND ${PYTHON_EXECUTABLE} ${_TEST_FILE} ${TEST_RUN_ARGS}
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
if(NOT TEST_COMMAND)
|
||||
get_filename_component(_TEST_FILE "${TEST_FILE}" NAME)
|
||||
configure_file(${TEST_FILE} ${CMAKE_BINARY_DIR}/${_TEST_FILE} @ONLY)
|
||||
if(TEST_STANDALONE)
|
||||
add_test(
|
||||
NAME ${TEST_NAME}
|
||||
COMMAND ${PYTHON_EXECUTABLE} ${_TEST_FILE} ${TEST_RUN_ARGS}
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
else()
|
||||
add_test(
|
||||
NAME ${TEST_NAME}
|
||||
COMMAND ${PYTHON_EXECUTABLE} -m omnitrace ${TEST_PROFILE_ARGS} --
|
||||
${_TEST_FILE} ${TEST_RUN_ARGS}
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
endif()
|
||||
else()
|
||||
add_test(
|
||||
NAME ${TEST_NAME}-run
|
||||
COMMAND ${PYTHON_EXECUTABLE} -m omnitrace ${TEST_PROFILE_ARGS} --
|
||||
${_TEST_FILE} ${TEST_RUN_ARGS}
|
||||
NAME ${TEST_NAME}
|
||||
COMMAND ${TEST_COMMAND} ${TEST_FILE}
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
add_test(
|
||||
NAME ${TEST_NAME}-inverse
|
||||
COMMAND ${TEST_COMMAND} ${TEST_FILE}
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
foreach(_TEST run)
|
||||
string(REGEX REPLACE "-run(-|/)" "\\1" _prefix "${TEST_NAME}-${_TEST}/")
|
||||
set(_environ "${TEST_ENVIRONMENT}")
|
||||
set(_labels "${_TEST}")
|
||||
set(_timeout ${TEST_TIMEOUT})
|
||||
list(APPEND _environ "OMNITRACE_OUTPUT_PATH=omnitrace-tests-output"
|
||||
"OMNITRACE_OUTPUT_PREFIX=${_prefix}")
|
||||
string(REPLACE "-run" "" _labels "${_TEST}")
|
||||
string(REPLACE "-sampling" ";sampling" _labels "${_labels}")
|
||||
set(_props)
|
||||
if("${_TEST}" MATCHES "run|baseline")
|
||||
set(_props ${TEST_PROPERTIES})
|
||||
if(NOT "RUN_SERIAL" IN_LIST _props)
|
||||
list(APPEND _props RUN_SERIAL ON)
|
||||
endif()
|
||||
endif()
|
||||
if(TEST ${TEST_NAME}-${_TEST})
|
||||
set_tests_properties(
|
||||
${TEST_NAME}-${_TEST}
|
||||
PROPERTIES ENVIRONMENT "${_environ}" TIMEOUT ${_timeout} LABELS
|
||||
"${_labels};${TEST_LABELS}" ${_props})
|
||||
endif()
|
||||
endforeach()
|
||||
set_tests_properties(
|
||||
${TEST_NAME}
|
||||
PROPERTIES ENVIRONMENT
|
||||
"${TEST_ENVIRONMENT}"
|
||||
TIMEOUT
|
||||
${TEST_TIMEOUT}
|
||||
LABELS
|
||||
"${TEST_LABELS}"
|
||||
DEPENDS
|
||||
"${TEST_DEPENDS}"
|
||||
PASS_REGULAR_EXPRESSION
|
||||
"${TEST_PASS_REGULAR_EXPRESSION}"
|
||||
FAIL_REGULAR_EXPRESSION
|
||||
"${TEST_FAIL_REGULAR_EXPRESSION}"
|
||||
SKIP_REGULAR_EXPRESSION
|
||||
"${TEST_SKIP_REGULAR_EXPRESSION}"
|
||||
REQUIRED_FILES
|
||||
"${TEST_FILE}"
|
||||
${TEST_PROPERTIES})
|
||||
|
||||
if(TEST ${TEST_NAME}-inverse)
|
||||
set_tests_properties(
|
||||
${TEST_NAME}-inverse
|
||||
PROPERTIES ENVIRONMENT
|
||||
"${TEST_ENVIRONMENT}"
|
||||
TIMEOUT
|
||||
${TEST_TIMEOUT}
|
||||
LABELS
|
||||
"${TEST_LABELS}"
|
||||
DEPENDS
|
||||
"${TEST_DEPENDS}"
|
||||
PASS_REGULAR_EXPRESSION
|
||||
"${TEST_FAIL_REGULAR_EXPRESSION}"
|
||||
FAIL_REGULAR_EXPRESSION
|
||||
"${TEST_PASS_REGULAR_EXPRESSION}"
|
||||
WILL_FAIL
|
||||
ON
|
||||
REQUIRED_FILES
|
||||
"${TEST_FILE}"
|
||||
${TEST_PROPERTIES})
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# -------------------------------------------------------------------------------------- #
|
||||
@@ -465,23 +489,95 @@ omnitrace_add_test(
|
||||
RUNTIME_TIMEOUT 360
|
||||
ENVIRONMENT "${_ompt_environment}")
|
||||
|
||||
# -------------------------------------------------------------------------------------- #
|
||||
#
|
||||
# python tests
|
||||
#
|
||||
# -------------------------------------------------------------------------------------- #
|
||||
|
||||
omnitrace_add_python_test(
|
||||
NAME python-external
|
||||
FILE ${CMAKE_SOURCE_DIR}/examples/python/external.py
|
||||
PROFILE_ARGS -l -f
|
||||
PROFILE_ARGS -f
|
||||
RUN_ARGS 10
|
||||
ENVIRONMENT "${_base_environment};OMNITRACE_USE_PID=OFF")
|
||||
ENVIRONMENT "${_python_environment}")
|
||||
|
||||
omnitrace_add_python_test(
|
||||
NAME python-external-exclude-inefficient
|
||||
FILE ${CMAKE_SOURCE_DIR}/examples/python/external.py
|
||||
PROFILE_ARGS -E "^inefficient$"
|
||||
RUN_ARGS 10
|
||||
ENVIRONMENT "${_python_environment}")
|
||||
|
||||
omnitrace_add_python_test(
|
||||
NAME python-builtin
|
||||
FILE ${CMAKE_SOURCE_DIR}/examples/python/builtin.py
|
||||
PROFILE_ARGS -b -l -f
|
||||
RUN_ARGS 10
|
||||
ENVIRONMENT "${_base_environment};OMNITRACE_USE_PID=OFF")
|
||||
ENVIRONMENT "${_python_environment}")
|
||||
|
||||
omnitrace_add_python_test(
|
||||
STANDALONE
|
||||
NAME python-source
|
||||
FILE ${CMAKE_SOURCE_DIR}/examples/python/source.py
|
||||
RUN_ARGS 5
|
||||
ENVIRONMENT "${_base_environment};OMNITRACE_USE_PID=OFF")
|
||||
ENVIRONMENT "${_python_environment}")
|
||||
|
||||
# -------------------------------------------------------------------------------------- #
|
||||
#
|
||||
# python output tests
|
||||
#
|
||||
# -------------------------------------------------------------------------------------- #
|
||||
|
||||
if(CMAKE_VERSION VERSION_LESS "3.18.0")
|
||||
find_program(
|
||||
OMNITRACE_CAT_EXE
|
||||
NAMES cat
|
||||
PATH_SUFFIXES bin)
|
||||
if(OMNITRACE_CAT_EXE)
|
||||
set(OMNITRACE_CAT_COMMAND ${OMNITRACE_CAT_EXE})
|
||||
endif()
|
||||
else()
|
||||
set(OMNITRACE_CAT_COMMAND ${CMAKE_COMMAND} -E cat)
|
||||
endif()
|
||||
|
||||
if(OMNITRACE_CAT_COMMAND)
|
||||
omnitrace_add_python_test(
|
||||
NAME python-external-check
|
||||
COMMAND ${OMNITRACE_CAT_COMMAND}
|
||||
FILE omnitrace-tests-output/python-external/trip_count.txt
|
||||
PASS_REGULAR_EXPRESSION
|
||||
"(\\\[compile\\\]).*(\\\| \\\|0>>> \\\[run\\\]\\\[external.py\\\]).*(\\\| \\\|0>>> \\\|_\\\[fib\\\]\\\[external.py\\\]).*(\\\| \\\|0>>> \\\|_\\\[inefficient\\\]\\\[external.py\\\])"
|
||||
DEPENDS python-external
|
||||
ENVIRONMENT "${_python_environment}")
|
||||
|
||||
omnitrace_add_python_test(
|
||||
NAME python-external-exclude-inefficient-check
|
||||
COMMAND ${OMNITRACE_CAT_COMMAND}
|
||||
FILE omnitrace-tests-output/python-external-exclude-inefficient/trip_count.txt
|
||||
FAIL_REGULAR_EXPRESSION "(\\\|_inefficient).*(\\\|_sum)"
|
||||
DEPENDS python-external-exclude-inefficient
|
||||
ENVIRONMENT "${_python_environment}")
|
||||
|
||||
omnitrace_add_python_test(
|
||||
NAME python-builtin-check
|
||||
COMMAND ${OMNITRACE_CAT_COMMAND}
|
||||
FILE omnitrace-tests-output/python-builtin/trip_count.txt
|
||||
PASS_REGULAR_EXPRESSION "\\\[inefficient\\\]\\\[builtin.py:11\\\]"
|
||||
DEPENDS python-builtin
|
||||
ENVIRONMENT "${_python_environment}")
|
||||
|
||||
omnitrace_add_python_test(
|
||||
NAME python-source-check
|
||||
COMMAND ${OMNITRACE_CAT_COMMAND}
|
||||
FILE omnitrace-tests-output/python-source/trip_count.txt
|
||||
PASS_REGULAR_EXPRESSION
|
||||
"(\\\| \\\|0>>> run \\\| 5).*(\\\| \\\|0>>> \\\|_fib \\\| 40).*(\\\| \\\|0>>> \\\|_fib \\\| 5).*(\\\| \\\|0>>> \\\|_inefficient \\\| 5).*(\\\| \\\|0>>> \\\|__sum \\\| 5)"
|
||||
DEPENDS python-source
|
||||
ENVIRONMENT "${_python_environment}")
|
||||
else()
|
||||
omnitrace_message(
|
||||
WARNING
|
||||
"Neither 'cat' nor 'cmake -E cat' are available. Python source checks are disabled"
|
||||
)
|
||||
endif()
|
||||
|
||||
مرجع در شماره جدید
Block a user