├── .flake8 ├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── gh-pages.yml │ ├── python-ci.yml │ └── python-packages.yml ├── .gitignore ├── .gitmodules ├── Dockerfile ├── LICENSE ├── MANIFEST.in ├── README.rst ├── RELEASE.rst ├── docs ├── conf.py ├── consts.rst ├── events.rst ├── exceptions.rst ├── index.rst ├── install.rst ├── licenses.rst ├── papi_high.rst ├── papi_low.rst └── structs.rst ├── noxfile.py ├── pypapi ├── __init__.py ├── consts.py ├── events.py ├── exceptions.py ├── papi.h ├── papi_build.py ├── papi_high.py ├── papi_low.py └── structs.py ├── pyproject.toml ├── requirements.txt ├── setup.py └── tools ├── generate_manifest_in.sh └── generate_papi_events.py /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = E203, E241, W503, E501 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: flozz 2 | custom: 3 | - https://www.paypal.me/0xflozz 4 | - https://www.buymeacoffee.com/flozz 5 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /.github/workflows/gh-pages.yml: -------------------------------------------------------------------------------- 1 | name: Build and deploy Github pages 2 | 3 | on: 4 | push: 5 | tags: "v[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+" 6 | 7 | jobs: 8 | 9 | build-and-deploy: 10 | runs-on: ubuntu-latest 11 | steps: 12 | 13 | - name: "Pull the repository" 14 | uses: actions/checkout@v4 15 | with: 16 | submodules: true 17 | 18 | - name: "Set up Python" 19 | uses: actions/setup-python@v5 20 | with: 21 | python-version: "3.13" 22 | 23 | - name: "Install build dependencies" 24 | run: | 25 | sudo apt-get update 26 | sudo apt-get install -y build-essential python3-dev 27 | 28 | - name: "Install Python dependencies" 29 | run: | 30 | pip3 install setuptools 31 | pip3 install nox 32 | 33 | - name: "Build Sphinx Doc" 34 | run: | 35 | nox --session gendoc 36 | 37 | - name: "Deploy Github Pages" 38 | uses: JamesIves/github-pages-deploy-action@v4 39 | with: 40 | BRANCH: gh-pages 41 | FOLDER: build/html 42 | -------------------------------------------------------------------------------- /.github/workflows/python-ci.yml: -------------------------------------------------------------------------------- 1 | name: Lint and Tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | 7 | test: 8 | 9 | runs-on: ubuntu-latest 10 | 11 | strategy: 12 | matrix: 13 | python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] 14 | 15 | steps: 16 | 17 | - name: "Pull the repository" 18 | uses: actions/checkout@v4 19 | with: 20 | submodules: true 21 | 22 | - name: "Install OS Dependencies" 23 | run: | 24 | sudo apt-get update 25 | sudo apt-get install -y build-essential python3-dev 26 | 27 | - name: "Set up Python ${{ matrix.python-version }}" 28 | uses: actions/setup-python@v5 29 | with: 30 | python-version: ${{ matrix.python-version }} 31 | 32 | - name: "Install Nox" 33 | run: | 34 | pip3 install setuptools 35 | pip3 install nox 36 | 37 | - name: "Lint with Flake8 and Black" 38 | run: | 39 | python3 -m nox --session lint 40 | 41 | - name: "Check PyPAPI can be built" 42 | run: | 43 | python setup.py build 44 | -------------------------------------------------------------------------------- /.github/workflows/python-packages.yml: -------------------------------------------------------------------------------- 1 | 2 | name: "Build and Publish Python Packages" 3 | 4 | on: 5 | push: 6 | tags: 7 | - "v[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+" 8 | - "v[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+-[0-9]+" 9 | 10 | jobs: 11 | 12 | build_sdist: 13 | 14 | name: "Source distribution" 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | 19 | - name: "Checkout the repository" 20 | uses: actions/checkout@v4 21 | with: 22 | submodules: true 23 | 24 | - name: "Set up Python" 25 | uses: actions/setup-python@v5 26 | with: 27 | python-version: "3.13" 28 | 29 | - name: "Install python dependencies" 30 | run: | 31 | pip install setuptools 32 | 33 | - name: "Build source distribution" 34 | run: | 35 | python setup.py sdist 36 | 37 | - name: "Upload artifacts" 38 | uses: actions/upload-artifact@v4 39 | with: 40 | name: sdist 41 | path: dist/ 42 | retention-days: 1 43 | 44 | build_wheels: 45 | 46 | name: "Build wheels on ${{ matrix.os }}" 47 | runs-on: ${{ matrix.os }} 48 | 49 | strategy: 50 | fail-fast: false 51 | matrix: 52 | os: 53 | - ubuntu-22.04 54 | # windows-2019 # TODO not working, see 55 | # macos-11 # https://github.com/flozz/pypapi/issues/39 56 | 57 | steps: 58 | 59 | - name: "Checkout the repository" 60 | uses: actions/checkout@v4 61 | with: 62 | submodules: true 63 | 64 | - name: "Build wheels" 65 | uses: pypa/cibuildwheel@v2.23.3 66 | env: 67 | CIBW_ARCHS_MACOS: x86_64 arm64 universal2 68 | CIBW_SKIP: cp*-win32 cp*-musllinux_* 69 | 70 | - name: "Upload artifacts" 71 | uses: actions/upload-artifact@v4 72 | with: 73 | name: wheels-${{ matrix.os }} 74 | path: ./wheelhouse/*.whl 75 | retention-days: 1 76 | 77 | publish_pypi: 78 | 79 | name: "Publish packages on PyPI" 80 | runs-on: ubuntu-latest 81 | needs: 82 | - build_sdist 83 | - build_wheels 84 | 85 | steps: 86 | 87 | - name: "Download artifacts" 88 | uses: actions/download-artifact@v4 89 | 90 | - name: "Move packages to the dist/ folder" 91 | run: | 92 | mkdir dist/ 93 | mv sdist/* dist/ 94 | mv wheels-*/*.whl dist/ 95 | 96 | - name: "Publish packages on PyPI" 97 | uses: pypa/gh-action-pypi-publish@release/v1 98 | with: 99 | password: ${{ secrets.PYPI_API_TOKEN }} 100 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | *.pyc 4 | _*.c 5 | __pycache__ 6 | __env__ 7 | .eggs 8 | *.egg-info 9 | build 10 | dist 11 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "papi"] 2 | path = papi 3 | url = https://github.com/icl-utk-edu/papi.git 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 as build_stage 2 | 3 | RUN apt-get update && \ 4 | DEBIAN_FRONTEND=noninteractive apt-get install -y \ 5 | build-essential \ 6 | python3 \ 7 | python3-pip \ 8 | libffi-dev \ 9 | && \ 10 | rm -rf /var/lib/apt/lists/* 11 | 12 | 13 | 14 | WORKDIR /pypapi 15 | 16 | COPY papi papi 17 | 18 | WORKDIR /pypapi/papi/src 19 | 20 | ENV CFLAGS="-fPIC -Werror=format-truncation=0" 21 | ENV PAPI_COMPONENTS="net powercap rapl" 22 | RUN ./configure --with-components="${PAPI_COMPONENTS}" && \ 23 | make 24 | 25 | WORKDIR /pypapi 26 | 27 | RUN pip install cffi==1.16.0 28 | 29 | COPY setup.py setup.py 30 | 31 | COPY pypapi pypapi 32 | 33 | RUN python3 pypapi/papi_build.py 34 | 35 | RUN pip install . 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | 15 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | PyPAPI 2 | ====== 3 | 4 | |GitHub| |PyPI| |License| |Discord| |Github Actions| |Black| 5 | 6 | PyPAPI is a Python binding for the `PAPI (Performance Application Programming 7 | Interface) `__ library. PyPAPI 8 | implements the whole PAPI High Level API and partially the Low Level API. 9 | 10 | .. NOTE:: 11 | 12 | Starting with **v5.5.1.4**, PyPAPI is only compatible with GCC 7.0 or 13 | higher. Please use previous releases for older GCC version. 14 | 15 | 16 | Documentation: 17 | -------------- 18 | 19 | * https://flozz.github.io/pypapi/ 20 | 21 | 22 | Installing PyPAPI 23 | ----------------- 24 | 25 | See this page of the documentation: 26 | 27 | * https://flozz.github.io/pypapi/install.html 28 | 29 | 30 | Hacking 31 | ------- 32 | 33 | Building PyPAPI For Local Development 34 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 35 | 36 | To work on PyPAPI, you first have to clone this repositiory and 37 | initialize and update submodules:: 38 | 39 | git clone https://github.com/flozz/pypapi.git 40 | cd pypapi 41 | 42 | git submodule init 43 | git submodule update 44 | 45 | Then you have to build both PAPI and the C library inside the ``pypapi`` 46 | module. This can be done with the following commands:: 47 | 48 | python setup.py build 49 | python pypapi/papi_build.py 50 | 51 | 52 | Linting and Code formatting 53 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 54 | 55 | To build lint the code, you first need to install Nox:: 56 | 57 | pip install nox 58 | 59 | Then, run the following command:: 60 | 61 | nox -s lint 62 | 63 | To automatically coding style issues, run:: 64 | 65 | nox -s black_fix 66 | 67 | 68 | Generating Documentation 69 | ~~~~~~~~~~~~~~~~~~~~~~~~ 70 | 71 | To build the Sphinx documentation, you first need to install Nox:: 72 | 73 | pip install nox 74 | 75 | Then, run the following command:: 76 | 77 | nox -s gendoc 78 | 79 | 80 | Support this project 81 | -------------------- 82 | 83 | Want to support this project? 84 | 85 | * `☕️ Buy me a coffee `__ 86 | * `💵️ Give me a tip on PayPal `__ 87 | * `❤️ Sponsor me on GitHub `__ 88 | 89 | 90 | Changelog 91 | --------- 92 | 93 | 94 | * **[NEXT]** (changes on ``master``, but not released yet): 95 | 96 | * Nothing yet ;) 97 | 98 | * **v6.0.0.2:** 99 | 100 | * misc: Added Python 3.13 support (@flozz) 101 | * misc!: Removed Python 3.8 support (@flozz) 102 | 103 | * **v6.0.0.1:** 104 | 105 | * feat!: Updated the PAPI library to v6.0.0.1 (@SerodioJ, #37) 106 | 107 | * **v5.5.1.6:** 108 | 109 | * chore: Added code linting with Flake8 (@flozz) 110 | * chore: Added code formatter and reformatted all files with Black (@flozz) 111 | * chore: Added Nox to run code linting, code formatting, doc building tasks (@flozz) 112 | * chore: Updated dev dependnecies (@flozz) 113 | * chore: Automatically build and publish sdist package and wheels for Linux (@flozz, #39) 114 | * docs: Updated documentation (@flozz) 115 | 116 | * **v5.5.1.5:** 117 | 118 | * fix: Fixed issue with module named ``types.py`` (@mcopik, #19) 119 | 120 | * **v5.5.1.4:** 121 | 122 | * chore: Fixed compilation with GCC 8 and newer (@maresmar, #18) 123 | * chore!: PyPAPI is no more compatible with GCC < 7.0 124 | 125 | * **v5.5.1.3:** 126 | 127 | * chore: Removed ``.o``, ``.lo`` and other generated objects from the package 128 | 129 | * **v5.5.1.2:** 130 | 131 | * feat: Partial bindings for the low level API 132 | 133 | * **v5.5.1.1:** 134 | 135 | * chore: Added missing files to build PAPI 136 | 137 | * **v5.5.1.0:** 138 | 139 | * feat: Initial release (binding for papy 5.5.1) 140 | 141 | 142 | .. |GitHub| image:: https://img.shields.io/github/stars/flozz/pypapi?label=GitHub&logo=github 143 | :target: https://github.com/flozz/pypapi 144 | 145 | .. |PyPI| image:: https://img.shields.io/pypi/v/python_papi.svg 146 | :target: https://pypi.python.org/pypi/python_papi 147 | 148 | .. |License| image:: https://img.shields.io/github/license/flozz/pypapi 149 | :target: https://flozz.github.io/pypapi/licenses.html 150 | 151 | .. |Discord| image:: https://img.shields.io/badge/chat-Discord-8c9eff?logo=discord&logoColor=ffffff 152 | :target: https://discord.gg/P77sWhuSs4 153 | 154 | .. |Github Actions| image:: https://github.com/flozz/pypapi/actions/workflows/python-ci.yml/badge.svg 155 | :target: https://github.com/flozz/pypapi/actions 156 | 157 | .. |Black| image:: https://img.shields.io/badge/code%20style-black-000000.svg 158 | :target: https://black.readthedocs.io/en/stable 159 | -------------------------------------------------------------------------------- /RELEASE.rst: -------------------------------------------------------------------------------- 1 | Things to do when releasing a new version 2 | ========================================= 3 | 4 | This file is a memo for the maintainer. 5 | 6 | 7 | 0. Check 8 | -------- 9 | 10 | * Check copyright year in ``docs/conf.py`` 11 | 12 | 13 | 1. Release 14 | ---------- 15 | 16 | * Update version number in ``setup.py`` 17 | * Update version number in ``docs/conf.py`` 18 | * Edit / update changelog in ``README.rst`` 19 | * Commit / tag (``git commit -m vX.Y.Z.ZZ && git tag vX.Y.Z.ZZ && git push && git push --tags``) 20 | 21 | 22 | 2. Publish PyPI package 23 | ----------------------- 24 | 25 | Publish source dist and wheels on PyPI. 26 | 27 | → Automated :) 28 | 29 | 30 | 3. Publish Github Release 31 | ------------------------- 32 | 33 | * Make a release on Github 34 | * Add changelog 35 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # PyPAPI documentation build configuration file, created by 4 | # sphinx-quickstart on Sun Jul 30 16:25:59 2017. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | # If extensions (or modules to document with autodoc) are in another directory, 16 | # add these directories to sys.path here. If the directory is relative to the 17 | # documentation root, use os.path.abspath to make it absolute, like shown here. 18 | # 19 | # import os 20 | # import sys 21 | # sys.path.insert(0, os.path.abspath('.')) 22 | 23 | 24 | # -- General configuration ------------------------------------------------ 25 | 26 | # If your documentation needs a minimal Sphinx version, state it here. 27 | # 28 | # needs_sphinx = '1.0' 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be 31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 32 | # ones. 33 | extensions = [ 34 | "sphinx.ext.autodoc", 35 | "sphinx.ext.doctest", 36 | "sphinx.ext.githubpages", 37 | ] 38 | 39 | # Add any paths that contain templates here, relative to this directory. 40 | templates_path = ["_templates"] 41 | 42 | # The suffix(es) of source filenames. 43 | # You can specify multiple suffix as a list of string: 44 | # 45 | # source_suffix = ['.rst', '.md'] 46 | source_suffix = ".rst" 47 | 48 | # The master toctree document. 49 | master_doc = "index" 50 | 51 | # General information about the project. 52 | project = "PyPAPI" 53 | copyright = "2017 - 2024, Fabien LOISON, Mathilde BOUTIGNY" 54 | author = "Fabien LOISON, Mathilde BOUTIGNY" 55 | 56 | # The version info for the project you're documenting, acts as replacement for 57 | # |version| and |release|, also used in various other places throughout the 58 | # built documents. 59 | # 60 | # The short X.Y version. 61 | version = "6.0.0" 62 | # The full version, including alpha/beta/rc tags. 63 | release = "6.0.0.2" 64 | 65 | # The language for content autogenerated by Sphinx. Refer to documentation 66 | # for a list of supported languages. 67 | # 68 | # This is also used if you do content translation via gettext catalogs. 69 | # Usually you set "language" from the command line for these cases. 70 | language = None 71 | 72 | # List of patterns, relative to source directory, that match files and 73 | # directories to ignore when looking for source files. 74 | # This patterns also effect to html_static_path and html_extra_path 75 | exclude_patterns = [] 76 | 77 | # The name of the Pygments (syntax highlighting) style to use. 78 | pygments_style = "sphinx" 79 | 80 | # If true, `todo` and `todoList` produce output, else they produce nothing. 81 | todo_include_todos = False 82 | 83 | 84 | # -- Options for HTML output ---------------------------------------------- 85 | 86 | # The theme to use for HTML and HTML Help pages. See the documentation for 87 | # a list of builtin themes. 88 | # 89 | html_theme = "sphinx_rtd_theme" 90 | 91 | # Theme options are theme-specific and customize the look and feel of a theme 92 | # further. For a list of options available for each theme, see the 93 | # documentation. 94 | # 95 | # html_theme_options = {} 96 | 97 | # Add any paths that contain custom static files (such as style sheets) here, 98 | # relative to this directory. They are copied after the builtin static files, 99 | # so a file named "default.css" will overwrite the builtin "default.css". 100 | html_static_path = ["_static"] 101 | 102 | # Custom sidebar templates, must be a dictionary that maps document names 103 | # to template names. 104 | # 105 | # This is required for the alabaster theme 106 | # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars 107 | html_sidebars = { 108 | "**": [ 109 | "about.html", 110 | "navigation.html", 111 | "relations.html", # needs 'show_related': True theme option to display 112 | "searchbox.html", 113 | "donate.html", 114 | ] 115 | } 116 | 117 | 118 | # -- Options for HTMLHelp output ------------------------------------------ 119 | 120 | # Output file base name for HTML help builder. 121 | htmlhelp_basename = "PyPAPIdoc" 122 | 123 | 124 | # -- Options for LaTeX output --------------------------------------------- 125 | 126 | latex_elements = { 127 | # The paper size ('letterpaper' or 'a4paper'). 128 | # 129 | # 'papersize': 'letterpaper', 130 | # The font size ('10pt', '11pt' or '12pt'). 131 | # 132 | # 'pointsize': '10pt', 133 | # Additional stuff for the LaTeX preamble. 134 | # 135 | # 'preamble': '', 136 | # Latex figure (float) alignment 137 | # 138 | # 'figure_align': 'htbp', 139 | } 140 | 141 | # Grouping the document tree into LaTeX files. List of tuples 142 | # (source start file, target name, title, 143 | # author, documentclass [howto, manual, or own class]). 144 | latex_documents = [ 145 | ( 146 | master_doc, 147 | "PyPAPI.tex", 148 | "PyPAPI Documentation", 149 | "Fabien LOISON, Mathilde BOUTIGNY", 150 | "manual", 151 | ), 152 | ] 153 | 154 | 155 | # -- Options for manual page output --------------------------------------- 156 | 157 | # One entry per manual page. List of tuples 158 | # (source start file, name, description, authors, manual section). 159 | man_pages = [(master_doc, "pypapi", "PyPAPI Documentation", [author], 1)] 160 | 161 | 162 | # -- Options for Texinfo output ------------------------------------------- 163 | 164 | # Grouping the document tree into Texinfo files. List of tuples 165 | # (source start file, target name, title, author, 166 | # dir menu entry, description, category) 167 | texinfo_documents = [ 168 | ( 169 | master_doc, 170 | "PyPAPI", 171 | "PyPAPI Documentation", 172 | author, 173 | "PyPAPI", 174 | "One line description of project.", 175 | "Miscellaneous", 176 | ), 177 | ] 178 | -------------------------------------------------------------------------------- /docs/consts.rst: -------------------------------------------------------------------------------- 1 | Constants 2 | ========= 3 | 4 | 5 | PAPI Version Constants 6 | ---------------------- 7 | 8 | .. autodata:: pypapi.consts.PAPI_VERSION 9 | .. autodata:: pypapi.consts.PAPI_VER_CURRENT 10 | 11 | 12 | .. _consts_init: 13 | 14 | PAPI Initialization Constants 15 | ----------------------------- 16 | 17 | .. autodata:: pypapi.consts.PAPI_NOT_INITED 18 | .. autodata:: pypapi.consts.PAPI_LOW_LEVEL_INITED 19 | .. autodata:: pypapi.consts.PAPI_HIGH_LEVEL_INITED 20 | .. autodata:: pypapi.consts.PAPI_THREAD_LEVEL_INITED 21 | 22 | 23 | .. _consts_state: 24 | 25 | PAPI State Constants 26 | -------------------- 27 | 28 | .. autodata:: pypapi.consts.PAPI_STOPPED 29 | .. autodata:: pypapi.consts.PAPI_RUNNING 30 | .. autodata:: pypapi.consts.PAPI_PAUSED 31 | .. autodata:: pypapi.consts.PAPI_NOT_INIT 32 | .. autodata:: pypapi.consts.PAPI_OVERFLOWING 33 | .. autodata:: pypapi.consts.PAPI_PROFILING 34 | .. autodata:: pypapi.consts.PAPI_MULTIPLEXING 35 | .. autodata:: pypapi.consts.PAPI_ATTACHED 36 | .. autodata:: pypapi.consts.PAPI_CPU_ATTACHED 37 | 38 | 39 | .. _consts_mask: 40 | 41 | PAPI Mask Constants 42 | -------------------- 43 | 44 | .. autodata:: pypapi.consts.PAPI_NATIVE_MASK 45 | .. autodata:: pypapi.consts.PAPI_PRESET_MASK 46 | 47 | 48 | .. _consts_option: 49 | 50 | PAPI Option Constants 51 | --------------------- 52 | 53 | .. autodata:: pypapi.consts.PAPI_MIN_STR_LEN 54 | .. autodata:: pypapi.consts.PAPI_MAX_STR_LEN 55 | .. autodata:: pypapi.consts.PAPI_2MAX_STR_LEN 56 | .. autodata:: pypapi.consts.PAPI_HUGE_STR_LEN 57 | .. autodata:: pypapi.consts.PAPI_MAX_INFO_TERMS 58 | 59 | 60 | .. _consts_error: 61 | 62 | PAPI Error Constants 63 | -------------------- 64 | 65 | .. autodata:: pypapi.consts.PAPI_QUIET 66 | .. autodata:: pypapi.consts.PAPI_VERB_ECONT 67 | .. autodata:: pypapi.consts.PAPI_VERB_ESTOP 68 | 69 | 70 | .. _consts_domain: 71 | 72 | PAPI Domain Constants 73 | --------------------- 74 | 75 | .. autodata:: pypapi.consts.PAPI_DOM_USER 76 | .. autodata:: pypapi.consts.PAPI_DOM_MIN 77 | .. autodata:: pypapi.consts.PAPI_DOM_KERNEL 78 | .. autodata:: pypapi.consts.PAPI_DOM_OTHER 79 | .. autodata:: pypapi.consts.PAPI_DOM_SUPERVISOR 80 | .. autodata:: pypapi.consts.PAPI_DOM_ALL 81 | .. autodata:: pypapi.consts.PAPI_DOM_MAX 82 | .. autodata:: pypapi.consts.PAPI_DOM_HWSPEC 83 | 84 | 85 | .. _consts_granularity: 86 | 87 | PAPI Granularity Constants 88 | -------------------------- 89 | 90 | .. autodata:: pypapi.consts.PAPI_GRN_THR 91 | .. autodata:: pypapi.consts.PAPI_GRN_MIN 92 | .. autodata:: pypapi.consts.PAPI_GRN_PROC 93 | .. autodata:: pypapi.consts.PAPI_GRN_PROCG 94 | .. autodata:: pypapi.consts.PAPI_GRN_SYS 95 | .. autodata:: pypapi.consts.PAPI_GRN_SYS_CPU 96 | .. autodata:: pypapi.consts.PAPI_GRN_MAX 97 | 98 | 99 | .. _consts_locking: 100 | 101 | PAPI Locking Mechanisms Constants 102 | --------------------------------- 103 | 104 | 105 | .. autodata:: pypapi.consts.PAPI_USR1_LOCK 106 | .. autodata:: pypapi.consts.PAPI_USR2_LOCK 107 | .. autodata:: pypapi.consts.PAPI_NUM_LOCK 108 | .. autodata:: pypapi.consts.PAPI_LOCK_USR1 109 | .. autodata:: pypapi.consts.PAPI_LOCK_USR2 110 | .. autodata:: pypapi.consts.PAPI_LOCK_NUM 111 | 112 | .. _consts_flops: 113 | 114 | PAPI FLIPS/FLOPS Constants 115 | -------------------------- 116 | 117 | .. autodata:: pypapi.consts.PAPI_FP_INS 118 | .. autodata:: pypapi.consts.PAPI_VEC_SP 119 | .. autodata:: pypapi.consts.PAPI_VEC_DP 120 | .. autodata:: pypapi.consts.PAPI_FP_OPS 121 | .. autodata:: pypapi.consts.PAPI_SP_OPS 122 | .. autodata:: pypapi.consts.PAPI_DP_OPS 123 | 124 | 125 | Other PAPI Constants 126 | -------------------- 127 | 128 | .. autodata:: pypapi.consts.PAPI_NULL 129 | -------------------------------------------------------------------------------- /docs/events.rst: -------------------------------------------------------------------------------- 1 | Events 2 | ====== 3 | 4 | 5 | Bellow the list of all events supported by PAPI. They can be used with the 6 | :py:func:`~pypapi.papi.start_counters` function: 7 | 8 | :: 9 | 10 | from pypapi import papi_high 11 | from pypapi import events as papi_events 12 | 13 | papi_high.start_counters([ 14 | papi_events.PAPI_FP_OPS, 15 | papi_events.PAPI_TOT_CYC 16 | ]) 17 | 18 | 19 | Event List 20 | ---------- 21 | 22 | .. automodule:: pypapi.events 23 | :members: 24 | -------------------------------------------------------------------------------- /docs/exceptions.rst: -------------------------------------------------------------------------------- 1 | Exceptions 2 | ========== 3 | 4 | .. automodule:: pypapi.exceptions 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to PyPAPI's documentation! 2 | ================================== 3 | 4 | PyPAPI is a Python binding for the PAPI (Performance Application Programming 5 | Interface) library. PyPAPI implements the whole PAPI High Level API and 6 | partially the Low Level API. 7 | 8 | Example usage: 9 | -------------- 10 | 11 | :: 12 | 13 | # High Level API 14 | 15 | from pypapi import papi_high 16 | 17 | papi_high.hl_region_begin("computation") 18 | 19 | # computation 20 | 21 | papi_high.hl_region_end("computation") 22 | 23 | 24 | :: 25 | 26 | # Low Level API 27 | 28 | from pypapi import papi_low as papi 29 | from pypapi import events 30 | 31 | papi.library_init() 32 | 33 | evs = papi.create_eventset() 34 | papi.add_event(evs, events.PAPI_FP_OPS) 35 | 36 | papi.start(evs) 37 | 38 | # Do some computation here 39 | 40 | result = papi.stop(evs) 41 | print(result) 42 | 43 | papi.cleanup_eventset(evs) 44 | papi.destroy_eventset(evs) 45 | 46 | 47 | .. toctree:: 48 | :maxdepth: 2 49 | :caption: Contents: 50 | 51 | install 52 | papi_high 53 | papi_low 54 | structs 55 | events 56 | consts 57 | exceptions 58 | licenses 59 | 60 | * :ref:`genindex` 61 | * :ref:`modindex` 62 | 63 | * `Github `_ 64 | -------------------------------------------------------------------------------- /docs/install.rst: -------------------------------------------------------------------------------- 1 | Installing PyPAPI 2 | ================= 3 | 4 | As PAPI is a C library, it must be compiled. On Ubuntu / Debian, you can 5 | install the ``build-essential`` package. 6 | 7 | 8 | From PyPI 9 | --------- 10 | 11 | To install PyPAPI from PYPI, simply use pip:: 12 | 13 | pip install python_papi 14 | 15 | 16 | From Source 17 | ----------- 18 | 19 | First clone the project's repository and go into it:: 20 | 21 | git clone https://github.com/flozz/pypapi.git 22 | cd pypapi 23 | 24 | Then initialize and update git sub-modules:: 25 | 26 | git submodule init 27 | git submodule update 28 | 29 | Finally, execute the following command:: 30 | 31 | python setup.py install 32 | 33 | .. note:: 34 | 35 | you may require root permission if you want to install the package 36 | system-wide. 37 | 38 | -------------------------------------------------------------------------------- /docs/licenses.rst: -------------------------------------------------------------------------------- 1 | Licenses 2 | ======== 3 | 4 | * The **PyPAPI bindings** are licensed under the **WTFPL 2.0** license: 5 | 6 | .. code-block:: text 7 | 8 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 9 | Version 2, December 2004 10 | 11 | Copyright (C) 2004 Sam Hocevar 12 | 13 | Everyone is permitted to copy and distribute verbatim or modified 14 | copies of this license document, and changing it is allowed as long 15 | as the name is changed. 16 | 17 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 18 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 19 | 20 | 0. You just DO WHAT THE FUCK YOU WANT TO. 21 | 22 | 23 | * The **PAPI library** is licensed under the **BSD 3 Clause** license: 24 | 25 | .. code-block:: text 26 | 27 | Copyright (c) 2005 - 2010 28 | Innovative Computing Laboratory 29 | Dept of Electrical Engineering & Computer Science 30 | University of Tennessee, 31 | Knoxville, TN. 32 | All Rights Reserved. 33 | 34 | 35 | Redistribution and use in source and binary forms, with or without modification, 36 | are permitted provided that the following conditions are met: 37 | 38 | * Redistributions of source code must retain the above copyright notice, 39 | this list of conditions and the following disclaimer. 40 | * Redistributions in binary form must reproduce the above copyright notice, 41 | this list of conditions and the following disclaimer in the documentation 42 | and/or other materials provided with the distribution. 43 | * Neither the name of the University of Tennessee nor the names of its 44 | contributors may be used to endorse or promote products derived from this 45 | software without specific prior written permission. 46 | 47 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 48 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 49 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 50 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 51 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 52 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 53 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 54 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 55 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 56 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 57 | 58 | 59 | This open source software license conforms to the BSD License template. 60 | 61 | -------------------------------------------------------------------------------- /docs/papi_high.rst: -------------------------------------------------------------------------------- 1 | PAPI High Level API 2 | =================== 3 | 4 | .. automodule:: pypapi.papi_high 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/papi_low.rst: -------------------------------------------------------------------------------- 1 | PAPI Low Level API 2 | ================== 3 | 4 | .. automodule:: pypapi.papi_low 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/structs.rst: -------------------------------------------------------------------------------- 1 | PAPI Structs 2 | ============ 3 | 4 | .. automodule:: pypapi.structs 5 | :members: 6 | -------------------------------------------------------------------------------- /noxfile.py: -------------------------------------------------------------------------------- 1 | import nox 2 | 3 | 4 | PYTHON_FILES = [ 5 | "pypapi", 6 | "setup.py", 7 | "noxfile.py", 8 | "docs/conf.py", 9 | ] 10 | 11 | 12 | @nox.session(reuse_venv=True) 13 | def lint(session): 14 | session.install("flake8", "black") 15 | session.run("flake8", *PYTHON_FILES) 16 | session.run("black", "--check", "--diff", "--color", *PYTHON_FILES) 17 | 18 | 19 | @nox.session(reuse_venv=True) 20 | def black_fix(session): 21 | session.install("black") 22 | session.run("black", *PYTHON_FILES) 23 | 24 | 25 | @nox.session(reuse_venv=True) 26 | def gendoc(session): 27 | session.install("sphinx", "sphinx-rtd-theme") 28 | session.install("-e", ".") 29 | session.run("sphinx-build", "-M", "html", "docs", "build") 30 | -------------------------------------------------------------------------------- /pypapi/__init__.py: -------------------------------------------------------------------------------- 1 | from . import papi_high 2 | from . import papi_low 3 | from . import events 4 | from . import consts 5 | from . import exceptions 6 | from . import structs 7 | 8 | __all__ = [ 9 | "papi_high", 10 | "papi_low", 11 | "events", 12 | "consts", 13 | "exceptions", 14 | "structs", 15 | ] 16 | -------------------------------------------------------------------------------- /pypapi/consts.py: -------------------------------------------------------------------------------- 1 | """ 2 | Some constants used by PAPI. 3 | 4 | .. NOTE:: 5 | 6 | Event contants are located in an other file, see :doc:events 7 | """ 8 | 9 | from ctypes import c_int 10 | 11 | from ._papi import lib 12 | 13 | 14 | # Version 15 | 16 | 17 | def _papi_version_number(maj, min_, rev, inc): 18 | return maj << 24 | min_ << 16 | rev << 8 | inc 19 | 20 | 21 | #: PAPI version, as used internaly 22 | PAPI_VERSION = _papi_version_number(6, 0, 0, 1) 23 | 24 | #: PAPI version, without the revision and increment part 25 | PAPI_VER_CURRENT = PAPI_VERSION & 0xFFFF0000 26 | 27 | 28 | # PAPI Initialization 29 | 30 | #: PAPI is not initilized 31 | PAPI_NOT_INITED = lib.PAPI_NOT_INITED 32 | 33 | #: Low level has called library init 34 | PAPI_LOW_LEVEL_INITED = lib.PAPI_LOW_LEVEL_INITED 35 | 36 | #: High level has called library init 37 | PAPI_HIGH_LEVEL_INITED = lib.PAPI_HIGH_LEVEL_INITED 38 | 39 | #: Threads have been inited 40 | PAPI_THREAD_LEVEL_INITED = lib.PAPI_THREAD_LEVEL_INITED 41 | 42 | 43 | # PAPI State 44 | 45 | #: EventSet stopped 46 | PAPI_STOPPED = lib.PAPI_STOPPED 47 | 48 | #: EventSet running 49 | PAPI_RUNNING = lib.PAPI_RUNNING 50 | 51 | #: EventSet temp. disabled by the library 52 | PAPI_PAUSED = lib.PAPI_PAUSED 53 | 54 | #: EventSet defined, but not initialized 55 | PAPI_NOT_INIT = lib.PAPI_NOT_INIT 56 | 57 | #: EventSet has overflowing enabled 58 | PAPI_OVERFLOWING = lib.PAPI_OVERFLOWING 59 | 60 | #: EventSet has profiling enabled 61 | PAPI_PROFILING = lib.PAPI_PROFILING 62 | 63 | #: EventSet has multiplexing enabled 64 | PAPI_MULTIPLEXING = lib.PAPI_MULTIPLEXING 65 | 66 | #: EventSet is attached to another thread/process 67 | PAPI_ATTACHED = lib.PAPI_ATTACHED 68 | 69 | #: EventSet is attached to a specific cpu (not counting thread of execution) 70 | PAPI_CPU_ATTACHED = lib.PAPI_CPU_ATTACHED 71 | 72 | 73 | # PAPI Mask 74 | 75 | #: Mask to indicate the event is a native event 76 | PAPI_NATIVE_MASK = c_int(lib.PAPI_NATIVE_MASK).value 77 | 78 | #: Mask to indicate the event is a preset event 79 | PAPI_PRESET_MASK = c_int(lib.PAPI_PRESET_MASK).value 80 | 81 | 82 | # PAPI Option 83 | 84 | #: For small strings, like names & stuff 85 | PAPI_MIN_STR_LEN = lib.PAPI_MIN_STR_LEN 86 | 87 | #: For average run-of-the-mill strings 88 | PAPI_MAX_STR_LEN = lib.PAPI_MAX_STR_LEN 89 | 90 | #: For somewhat longer run-of-the-mill strings 91 | PAPI_2MAX_STR_LEN = lib.PAPI_2MAX_STR_LEN 92 | 93 | #: This should be defined in terms of a system parameter 94 | PAPI_HUGE_STR_LEN = lib.PAPI_HUGE_STR_LEN 95 | 96 | #: Dhould match PAPI_EVENTS_IN_DERIVED_EVENT defined in papi_internal.h 97 | PAPI_MAX_INFO_TERMS = lib.PAPI_MAX_INFO_TERMS 98 | 99 | 100 | # PAPI Error 101 | 102 | #: Option to turn off automatic reporting of return codes < 0 to stderr. 103 | PAPI_QUIET = lib.PAPI_QUIET 104 | 105 | #: Option to automatically report any return codes < 0 to stderr and continue 106 | PAPI_VERB_ECONT = lib.PAPI_VERB_ECONT 107 | 108 | #: Option to automatically report any return codes < 0 to stderr and exit. 109 | PAPI_VERB_ESTOP = lib.PAPI_VERB_ESTOP 110 | 111 | 112 | # PAPI Domain 113 | 114 | #: User context counted 115 | PAPI_DOM_USER = lib.PAPI_DOM_USER 116 | 117 | #: Same as PAPI_DOM_USER 118 | PAPI_DOM_MIN = PAPI_DOM_USER 119 | 120 | #: Kernel/OS context counted 121 | PAPI_DOM_KERNEL = lib.PAPI_DOM_KERNEL 122 | 123 | #: Exception/transient mode (like user TLB misses) 124 | PAPI_DOM_OTHER = lib.PAPI_DOM_OTHER 125 | 126 | #: Supervisor/hypervisor context counted 127 | PAPI_DOM_SUPERVISOR = lib.PAPI_DOM_SUPERVISOR 128 | 129 | #: All contexts counted 130 | PAPI_DOM_ALL = ( 131 | lib.PAPI_DOM_USER 132 | | lib.PAPI_DOM_KERNEL 133 | | lib.PAPI_DOM_OTHER 134 | | lib.PAPI_DOM_SUPERVISOR 135 | ) 136 | 137 | #: Same as PAPI_DOM_ALL 138 | PAPI_DOM_MAX = PAPI_DOM_ALL 139 | 140 | #: Flag that indicates we are not reading CPU like stuff. The lower 31 bits can be decoded by the component into something meaningful. i.e. SGI HUB counters 141 | PAPI_DOM_HWSPEC = lib.PAPI_DOM_HWSPEC 142 | 143 | 144 | # PAPI Granularity 145 | 146 | #: PAPI counters for each individual thread 147 | PAPI_GRN_THR = lib.PAPI_GRN_THR 148 | 149 | #: Same as PAPI_GRN_THR 150 | PAPI_GRN_MIN = PAPI_GRN_THR 151 | 152 | #: PAPI counters for each individual process 153 | PAPI_GRN_PROC = lib.PAPI_GRN_PROC 154 | 155 | #: PAPI counters for each individual process group 156 | PAPI_GRN_PROCG = lib.PAPI_GRN_PROCG 157 | 158 | #: PAPI counters for the current CPU, are you bound? 159 | PAPI_GRN_SYS = lib.PAPI_GRN_SYS 160 | 161 | #: PAPI counters for all CPUs individually 162 | PAPI_GRN_SYS_CPU = lib.PAPI_GRN_SYS_CPU 163 | 164 | #: Same as PAPI_GRN_SYS_CPU 165 | PAPI_GRN_MAX = PAPI_GRN_SYS_CPU 166 | 167 | 168 | # PAPI Locking Mechanisms 169 | 170 | #: User controlled locks 171 | PAPI_USR1_LOCK = lib.PAPI_USR1_LOCK 172 | 173 | #: User controlled locks 174 | PAPI_USR2_LOCK = lib.PAPI_USR2_LOCK 175 | 176 | #: Used with setting up array 177 | PAPI_NUM_LOCK = lib.PAPI_NUM_LOCK 178 | 179 | #: Same as PAPI_USR1_LOCK 180 | PAPI_LOCK_USR1 = PAPI_USR1_LOCK 181 | 182 | #: Same as PAPI_USR2_LOCK 183 | PAPI_LOCK_USR2 = PAPI_USR2_LOCK 184 | 185 | #: Same as PAPI_NUM_LOCK 186 | PAPI_LOCK_NUM = PAPI_NUM_LOCK 187 | 188 | # PAPI FLIPS/FLOPS 189 | 190 | #: Floating point instructions executed 191 | PAPI_FP_INS = lib.PAPI_FP_INS | PAPI_PRESET_MASK 192 | 193 | #: Single precision vector/SIMD instructions 194 | PAPI_VEC_SP = lib.PAPI_VEC_SP | PAPI_PRESET_MASK 195 | 196 | #: Double precision vector/SIMD instructions 197 | PAPI_VEC_DP = lib.PAPI_VEC_DP | PAPI_PRESET_MASK 198 | 199 | #: Floating point operations executed 200 | PAPI_FP_OPS = lib.PAPI_FP_OPS | PAPI_PRESET_MASK 201 | 202 | #: Floating point operations executed; optimized to count scaled single precision vector operations 203 | PAPI_SP_OPS = lib.PAPI_SP_OPS | PAPI_PRESET_MASK 204 | 205 | #: Floating point operations executed; optimized to count scaled double precision vector operations 206 | PAPI_DP_OPS = lib.PAPI_DP_OPS | PAPI_PRESET_MASK 207 | 208 | 209 | # Others 210 | 211 | #: A nonexistent hardware event used as a placeholder 212 | PAPI_NULL = lib.PAPI_NULL 213 | -------------------------------------------------------------------------------- /pypapi/events.py: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by the 2 | # 'tools/generate_papi_events.py' script. 3 | # DO NOT EDIT! 4 | 5 | 6 | # flake8: noqa 7 | 8 | 9 | PAPI_PRESET_MASK = ( 10 | 0x80000000 if not 0x80000000 & 0x80000000 else 0x80000000 | ~0x7FFFFFFF 11 | ) 12 | 13 | PAPI_NATIVE_MASK = ( 14 | 0x40000000 if not 0x40000000 & 0x80000000 else 0x40000000 | ~0x7FFFFFFF 15 | ) 16 | 17 | PAPI_UE_MASK = 0xC0000000 if not 0xC0000000 & 0x80000000 else 0xC0000000 | ~0x7FFFFFFF 18 | 19 | PAPI_PRESET_AND_MASK = 0x7FFFFFFF 20 | 21 | #: this masks just the native bit 22 | PAPI_NATIVE_AND_MASK = 0xBFFFFFFF 23 | 24 | PAPI_UE_AND_MASK = 0x3FFFFFFF 25 | 26 | #: The maxmimum number of preset events 27 | PAPI_MAX_PRESET_EVENTS = 128 28 | 29 | #: The maxmimum number of user defined events 30 | PAPI_MAX_USER_EVENTS = 50 31 | 32 | #: The maximum length of the operation string for user defined events 33 | USER_EVENT_OPERATION_LEN = 512 34 | 35 | #: Level 1 data cache misses 36 | PAPI_L1_DCM = 0x00 | PAPI_PRESET_MASK 37 | 38 | #: Level 1 instruction cache misses 39 | PAPI_L1_ICM = 0x01 | PAPI_PRESET_MASK 40 | 41 | #: Level 2 data cache misses 42 | PAPI_L2_DCM = 0x02 | PAPI_PRESET_MASK 43 | 44 | #: Level 2 instruction cache misses 45 | PAPI_L2_ICM = 0x03 | PAPI_PRESET_MASK 46 | 47 | #: Level 3 data cache misses 48 | PAPI_L3_DCM = 0x04 | PAPI_PRESET_MASK 49 | 50 | #: Level 3 instruction cache misses 51 | PAPI_L3_ICM = 0x05 | PAPI_PRESET_MASK 52 | 53 | #: Level 1 total cache misses 54 | PAPI_L1_TCM = 0x06 | PAPI_PRESET_MASK 55 | 56 | #: Level 2 total cache misses 57 | PAPI_L2_TCM = 0x07 | PAPI_PRESET_MASK 58 | 59 | #: Level 3 total cache misses 60 | PAPI_L3_TCM = 0x08 | PAPI_PRESET_MASK 61 | 62 | #: Snoops 63 | PAPI_CA_SNP = 0x09 | PAPI_PRESET_MASK 64 | 65 | #: Request for shared cache line (SMP) 66 | PAPI_CA_SHR = 0x0A | PAPI_PRESET_MASK 67 | 68 | #: Request for clean cache line (SMP) 69 | PAPI_CA_CLN = 0x0B | PAPI_PRESET_MASK 70 | 71 | #: Request for cache line Invalidation (SMP) 72 | PAPI_CA_INV = 0x0C | PAPI_PRESET_MASK 73 | 74 | #: Request for cache line Intervention (SMP) 75 | PAPI_CA_ITV = 0x0D | PAPI_PRESET_MASK 76 | 77 | #: Level 3 load misses 78 | PAPI_L3_LDM = 0x0E | PAPI_PRESET_MASK 79 | 80 | #: Level 3 store misses 81 | PAPI_L3_STM = 0x0F | PAPI_PRESET_MASK 82 | 83 | #: Cycles branch units are idle 84 | PAPI_BRU_IDL = 0x10 | PAPI_PRESET_MASK 85 | 86 | #: Cycles integer units are idle 87 | PAPI_FXU_IDL = 0x11 | PAPI_PRESET_MASK 88 | 89 | #: Cycles floating point units are idle 90 | PAPI_FPU_IDL = 0x12 | PAPI_PRESET_MASK 91 | 92 | #: Cycles load/store units are idle 93 | PAPI_LSU_IDL = 0x13 | PAPI_PRESET_MASK 94 | 95 | #: Data translation lookaside buffer misses 96 | PAPI_TLB_DM = 0x14 | PAPI_PRESET_MASK 97 | 98 | #: Instr translation lookaside buffer misses 99 | PAPI_TLB_IM = 0x15 | PAPI_PRESET_MASK 100 | 101 | #: Total translation lookaside buffer misses 102 | PAPI_TLB_TL = 0x16 | PAPI_PRESET_MASK 103 | 104 | #: Level 1 load misses 105 | PAPI_L1_LDM = 0x17 | PAPI_PRESET_MASK 106 | 107 | #: Level 1 store misses 108 | PAPI_L1_STM = 0x18 | PAPI_PRESET_MASK 109 | 110 | #: Level 2 load misses 111 | PAPI_L2_LDM = 0x19 | PAPI_PRESET_MASK 112 | 113 | #: Level 2 store misses 114 | PAPI_L2_STM = 0x1A | PAPI_PRESET_MASK 115 | 116 | #: BTAC miss 117 | PAPI_BTAC_M = 0x1B | PAPI_PRESET_MASK 118 | 119 | #: Prefetch data instruction caused a miss 120 | PAPI_PRF_DM = 0x1C | PAPI_PRESET_MASK 121 | 122 | #: Level 3 Data Cache Hit 123 | PAPI_L3_DCH = 0x1D | PAPI_PRESET_MASK 124 | 125 | #: Xlation lookaside buffer shootdowns (SMP) 126 | PAPI_TLB_SD = 0x1E | PAPI_PRESET_MASK 127 | 128 | #: Failed store conditional instructions 129 | PAPI_CSR_FAL = 0x1F | PAPI_PRESET_MASK 130 | 131 | #: Successful store conditional instructions 132 | PAPI_CSR_SUC = 0x20 | PAPI_PRESET_MASK 133 | 134 | #: Total store conditional instructions 135 | PAPI_CSR_TOT = 0x21 | PAPI_PRESET_MASK 136 | 137 | #: Cycles Stalled Waiting for Memory Access 138 | PAPI_MEM_SCY = 0x22 | PAPI_PRESET_MASK 139 | 140 | #: Cycles Stalled Waiting for Memory Read 141 | PAPI_MEM_RCY = 0x23 | PAPI_PRESET_MASK 142 | 143 | #: Cycles Stalled Waiting for Memory Write 144 | PAPI_MEM_WCY = 0x24 | PAPI_PRESET_MASK 145 | 146 | #: Cycles with No Instruction Issue 147 | PAPI_STL_ICY = 0x25 | PAPI_PRESET_MASK 148 | 149 | #: Cycles with Maximum Instruction Issue 150 | PAPI_FUL_ICY = 0x26 | PAPI_PRESET_MASK 151 | 152 | #: Cycles with No Instruction Completion 153 | PAPI_STL_CCY = 0x27 | PAPI_PRESET_MASK 154 | 155 | #: Cycles with Maximum Instruction Completion 156 | PAPI_FUL_CCY = 0x28 | PAPI_PRESET_MASK 157 | 158 | #: Hardware interrupts 159 | PAPI_HW_INT = 0x29 | PAPI_PRESET_MASK 160 | 161 | #: Unconditional branch instructions executed 162 | PAPI_BR_UCN = 0x2A | PAPI_PRESET_MASK 163 | 164 | #: Conditional branch instructions executed 165 | PAPI_BR_CN = 0x2B | PAPI_PRESET_MASK 166 | 167 | #: Conditional branch instructions taken 168 | PAPI_BR_TKN = 0x2C | PAPI_PRESET_MASK 169 | 170 | #: Conditional branch instructions not taken 171 | PAPI_BR_NTK = 0x2D | PAPI_PRESET_MASK 172 | 173 | #: Conditional branch instructions mispred 174 | PAPI_BR_MSP = 0x2E | PAPI_PRESET_MASK 175 | 176 | #: Conditional branch instructions corr. pred 177 | PAPI_BR_PRC = 0x2F | PAPI_PRESET_MASK 178 | 179 | #: FMA instructions completed 180 | PAPI_FMA_INS = 0x30 | PAPI_PRESET_MASK 181 | 182 | #: Total instructions issued 183 | PAPI_TOT_IIS = 0x31 | PAPI_PRESET_MASK 184 | 185 | #: Total instructions executed 186 | PAPI_TOT_INS = 0x32 | PAPI_PRESET_MASK 187 | 188 | #: Integer instructions executed 189 | PAPI_INT_INS = 0x33 | PAPI_PRESET_MASK 190 | 191 | #: Floating point instructions executed 192 | PAPI_FP_INS = 0x34 | PAPI_PRESET_MASK 193 | 194 | #: Load instructions executed 195 | PAPI_LD_INS = 0x35 | PAPI_PRESET_MASK 196 | 197 | #: Store instructions executed 198 | PAPI_SR_INS = 0x36 | PAPI_PRESET_MASK 199 | 200 | #: Total branch instructions executed 201 | PAPI_BR_INS = 0x37 | PAPI_PRESET_MASK 202 | 203 | #: Vector/SIMD instructions executed (could include integer) 204 | PAPI_VEC_INS = 0x38 | PAPI_PRESET_MASK 205 | 206 | #: Cycles processor is stalled on resource 207 | PAPI_RES_STL = 0x39 | PAPI_PRESET_MASK 208 | 209 | #: Cycles any FP units are stalled 210 | PAPI_FP_STAL = 0x3A | PAPI_PRESET_MASK 211 | 212 | #: Total cycles executed 213 | PAPI_TOT_CYC = 0x3B | PAPI_PRESET_MASK 214 | 215 | #: Total load/store inst. executed 216 | PAPI_LST_INS = 0x3C | PAPI_PRESET_MASK 217 | 218 | #: Sync. inst. executed 219 | PAPI_SYC_INS = 0x3D | PAPI_PRESET_MASK 220 | 221 | #: L1 D Cache Hit 222 | PAPI_L1_DCH = 0x3E | PAPI_PRESET_MASK 223 | 224 | #: L2 D Cache Hit 225 | PAPI_L2_DCH = 0x3F | PAPI_PRESET_MASK 226 | 227 | #: L1 D Cache Access 228 | PAPI_L1_DCA = 0x40 | PAPI_PRESET_MASK 229 | 230 | #: L2 D Cache Access 231 | PAPI_L2_DCA = 0x41 | PAPI_PRESET_MASK 232 | 233 | #: L3 D Cache Access 234 | PAPI_L3_DCA = 0x42 | PAPI_PRESET_MASK 235 | 236 | #: L1 D Cache Read 237 | PAPI_L1_DCR = 0x43 | PAPI_PRESET_MASK 238 | 239 | #: L2 D Cache Read 240 | PAPI_L2_DCR = 0x44 | PAPI_PRESET_MASK 241 | 242 | #: L3 D Cache Read 243 | PAPI_L3_DCR = 0x45 | PAPI_PRESET_MASK 244 | 245 | #: L1 D Cache Write 246 | PAPI_L1_DCW = 0x46 | PAPI_PRESET_MASK 247 | 248 | #: L2 D Cache Write 249 | PAPI_L2_DCW = 0x47 | PAPI_PRESET_MASK 250 | 251 | #: L3 D Cache Write 252 | PAPI_L3_DCW = 0x48 | PAPI_PRESET_MASK 253 | 254 | #: L1 instruction cache hits 255 | PAPI_L1_ICH = 0x49 | PAPI_PRESET_MASK 256 | 257 | #: L2 instruction cache hits 258 | PAPI_L2_ICH = 0x4A | PAPI_PRESET_MASK 259 | 260 | #: L3 instruction cache hits 261 | PAPI_L3_ICH = 0x4B | PAPI_PRESET_MASK 262 | 263 | #: L1 instruction cache accesses 264 | PAPI_L1_ICA = 0x4C | PAPI_PRESET_MASK 265 | 266 | #: L2 instruction cache accesses 267 | PAPI_L2_ICA = 0x4D | PAPI_PRESET_MASK 268 | 269 | #: L3 instruction cache accesses 270 | PAPI_L3_ICA = 0x4E | PAPI_PRESET_MASK 271 | 272 | #: L1 instruction cache reads 273 | PAPI_L1_ICR = 0x4F | PAPI_PRESET_MASK 274 | 275 | #: L2 instruction cache reads 276 | PAPI_L2_ICR = 0x50 | PAPI_PRESET_MASK 277 | 278 | #: L3 instruction cache reads 279 | PAPI_L3_ICR = 0x51 | PAPI_PRESET_MASK 280 | 281 | #: L1 instruction cache writes 282 | PAPI_L1_ICW = 0x52 | PAPI_PRESET_MASK 283 | 284 | #: L2 instruction cache writes 285 | PAPI_L2_ICW = 0x53 | PAPI_PRESET_MASK 286 | 287 | #: L3 instruction cache writes 288 | PAPI_L3_ICW = 0x54 | PAPI_PRESET_MASK 289 | 290 | #: L1 total cache hits 291 | PAPI_L1_TCH = 0x55 | PAPI_PRESET_MASK 292 | 293 | #: L2 total cache hits 294 | PAPI_L2_TCH = 0x56 | PAPI_PRESET_MASK 295 | 296 | #: L3 total cache hits 297 | PAPI_L3_TCH = 0x57 | PAPI_PRESET_MASK 298 | 299 | #: L1 total cache accesses 300 | PAPI_L1_TCA = 0x58 | PAPI_PRESET_MASK 301 | 302 | #: L2 total cache accesses 303 | PAPI_L2_TCA = 0x59 | PAPI_PRESET_MASK 304 | 305 | #: L3 total cache accesses 306 | PAPI_L3_TCA = 0x5A | PAPI_PRESET_MASK 307 | 308 | #: L1 total cache reads 309 | PAPI_L1_TCR = 0x5B | PAPI_PRESET_MASK 310 | 311 | #: L2 total cache reads 312 | PAPI_L2_TCR = 0x5C | PAPI_PRESET_MASK 313 | 314 | #: L3 total cache reads 315 | PAPI_L3_TCR = 0x5D | PAPI_PRESET_MASK 316 | 317 | #: L1 total cache writes 318 | PAPI_L1_TCW = 0x5E | PAPI_PRESET_MASK 319 | 320 | #: L2 total cache writes 321 | PAPI_L2_TCW = 0x5F | PAPI_PRESET_MASK 322 | 323 | #: L3 total cache writes 324 | PAPI_L3_TCW = 0x60 | PAPI_PRESET_MASK 325 | 326 | #: FM ins 327 | PAPI_FML_INS = 0x61 | PAPI_PRESET_MASK 328 | 329 | #: FA ins 330 | PAPI_FAD_INS = 0x62 | PAPI_PRESET_MASK 331 | 332 | #: FD ins 333 | PAPI_FDV_INS = 0x63 | PAPI_PRESET_MASK 334 | 335 | #: FSq ins 336 | PAPI_FSQ_INS = 0x64 | PAPI_PRESET_MASK 337 | 338 | #: Finv ins 339 | PAPI_FNV_INS = 0x65 | PAPI_PRESET_MASK 340 | 341 | #: Floating point operations executed 342 | PAPI_FP_OPS = 0x66 | PAPI_PRESET_MASK 343 | 344 | #: Floating point operations executed; optimized to count scaled single precision vector operations 345 | PAPI_SP_OPS = 0x67 | PAPI_PRESET_MASK 346 | 347 | #: Floating point operations executed; optimized to count scaled double precision vector operations 348 | PAPI_DP_OPS = 0x68 | PAPI_PRESET_MASK 349 | 350 | #: Single precision vector/SIMD instructions 351 | PAPI_VEC_SP = 0x69 | PAPI_PRESET_MASK 352 | 353 | #: Double precision vector/SIMD instructions 354 | PAPI_VEC_DP = 0x6A | PAPI_PRESET_MASK 355 | 356 | #: Reference clock cycles 357 | PAPI_REF_CYC = 0x6B | PAPI_PRESET_MASK 358 | 359 | #: This should always be last! 360 | PAPI_END = 0x6C | PAPI_PRESET_MASK 361 | -------------------------------------------------------------------------------- /pypapi/exceptions.py: -------------------------------------------------------------------------------- 1 | import functools 2 | 3 | from ._papi import lib 4 | 5 | 6 | class PapiError(Exception): 7 | """Base classe for PAPI exceptions.""" 8 | 9 | c_name = None 10 | c_value = None 11 | 12 | def __init__(self, message=None): 13 | Exception.__init__( 14 | self, "%s (%s)" % (message if message else self.__doc__, self.c_name) 15 | ) 16 | 17 | 18 | class PapiInvalidValueError(PapiError): 19 | """Invalid Argument.""" 20 | 21 | c_name = "PAPI_EINVAL" 22 | c_value = lib.PAPI_EINVAL 23 | 24 | 25 | class PapiNoMemoryError(PapiError): 26 | """Insufficient memory.""" 27 | 28 | c_name = "PAPI_ENOMEM" 29 | c_value = lib.PAPI_ENOMEM 30 | 31 | 32 | class PapiSystemError(PapiError): 33 | """A System/C library call failed.""" 34 | 35 | c_name = "PAPI_ESYS" 36 | c_value = lib.PAPI_ESYS 37 | 38 | 39 | class PapiComponentError(PapiError): 40 | """Not supported by component.""" 41 | 42 | c_name = "PAPI_ECMP, PAPI_ESBSTR" 43 | c_value = lib.PAPI_ECMP 44 | 45 | 46 | class PapiCountersLost(PapiError): 47 | """Access to the counters was lost or interrupted.""" 48 | 49 | c_name = "PAPI_ECLOST" 50 | c_value = lib.PAPI_ECLOST 51 | 52 | 53 | class PapiBugError(PapiError): 54 | """Internal error, please send mail to the developers.""" 55 | 56 | c_name = "PAPI_EBUG" 57 | c_value = lib.PAPI_EBUG 58 | 59 | 60 | class PapiNoEventError(PapiError): 61 | """Event does not exist.""" 62 | 63 | c_name = "PAPI_ENOEVNT" 64 | c_value = lib.PAPI_ENOEVNT 65 | 66 | 67 | class PapiConflictError(PapiError): 68 | """Event exists, but cannot be counted due to counter resource limitations.""" # noqa 69 | 70 | c_name = "PAPI_ECNFLCT" 71 | c_value = lib.PAPI_ECNFLCT 72 | 73 | 74 | class PapiNotRunningError(PapiError): 75 | """EventSet is currently not running.""" 76 | 77 | c_name = "PAPI_ENOTRUN" 78 | c_value = lib.PAPI_ENOTRUN 79 | 80 | 81 | class PapiIsRunningError(PapiError): 82 | """EventSet is currently counting.""" 83 | 84 | c_name = "PAPI_EISRUN" 85 | c_value = lib.PAPI_EISRUN 86 | 87 | 88 | class PapiNoEventSetError(PapiError): 89 | """No such EventSet Available.""" 90 | 91 | c_name = "PAPI_ENOEVST" 92 | c_value = lib.PAPI_ENOEVST 93 | 94 | 95 | class PapiNotPresetError(PapiError): 96 | """Event in argument is not a valid preset.""" 97 | 98 | c_name = "PAPI_ENOTPRESET" 99 | c_value = lib.PAPI_ENOTPRESET 100 | 101 | 102 | class PapiNoCounterError(PapiError): 103 | """Hardware does not support performance counters.""" 104 | 105 | c_name = "PAPI_ENOCNTR" 106 | c_value = lib.PAPI_ENOCNTR 107 | 108 | 109 | class PapiMiscellaneousError(PapiError): 110 | """Unknown error code.""" 111 | 112 | c_name = "PAPI_EMISC" 113 | c_value = lib.PAPI_EMISC 114 | 115 | 116 | class PapiPermissionError(PapiError): 117 | """Permission level does not permit operation.""" 118 | 119 | c_name = "PAPI_EPERM" 120 | c_value = lib.PAPI_EPERM 121 | 122 | 123 | class PapiInitializationError(PapiError): 124 | """PAPI hasn't been initialized yet.""" 125 | 126 | c_name = "PAPI_ENOINIT" 127 | c_value = lib.PAPI_ENOINIT 128 | 129 | 130 | class PapiNoComponentError(PapiError): 131 | """Component Index isn't set.""" 132 | 133 | c_name = "PAPI_ENOCMP" 134 | c_value = lib.PAPI_ENOCMP 135 | 136 | 137 | class PapiNotSupportedError(PapiError): 138 | """Not supported.""" 139 | 140 | c_name = "PAPI_ENOSUPP" 141 | c_value = lib.PAPI_ENOSUPP 142 | 143 | 144 | class PapiNotImplementedError(PapiError): 145 | """Not implemented.""" 146 | 147 | c_name = "PAPI_ENOIMPL" 148 | c_value = lib.PAPI_ENOIMPL 149 | 150 | 151 | class PapiBufferError(PapiError): 152 | """Buffer size exceeded.""" 153 | 154 | c_name = "PAPI_EBUF" 155 | c_value = lib.PAPI_EBUF 156 | 157 | 158 | class PapiInvalidDomainError(PapiError): 159 | """EventSet domain is not supported for the operation.""" 160 | 161 | c_name = "PAPI_EINVAL_DOM" 162 | c_value = lib.PAPI_EINVAL_DOM 163 | 164 | 165 | class PapiAttributeError(PapiError): 166 | """Invalid or missing event attributes.""" 167 | 168 | c_name = "PAPI_EATTR" 169 | c_value = lib.PAPI_EATTR 170 | 171 | 172 | class PapiCountError(PapiError): 173 | """Too many events or attributes.""" 174 | 175 | c_name = "PAPI_ECOUNT" 176 | c_value = lib.PAPI_ECOUNT 177 | 178 | 179 | class PapiCombinationError(PapiError): 180 | """Bad combination of feature.""" 181 | 182 | c_name = "PAPI_ECOMBO" 183 | c_value = lib.PAPI_ECOMBO 184 | 185 | 186 | def papi_error(function): 187 | """Decorator to raise PAPI errors.""" 188 | 189 | @functools.wraps(function) 190 | def papi_error_wrapper(*args, **kwargs): 191 | rcode, rvalue = function(*args, **kwargs) 192 | if rcode < 0: 193 | for name, object_ in globals().items(): 194 | if ( 195 | object_ and hasattr(object_, "c_value") and object_.c_value == rcode 196 | ): # noqa 197 | raise object_() 198 | raise PapiMiscellaneousError() 199 | return rvalue 200 | 201 | return papi_error_wrapper 202 | -------------------------------------------------------------------------------- /pypapi/papi.h: -------------------------------------------------------------------------------- 1 | // PAPI error code (definitions from papi.h) 2 | 3 | #define PAPI_OK 0 /**< No error */ 4 | #define PAPI_EINVAL -1 /**< Invalid argument */ 5 | #define PAPI_ENOMEM -2 /**< Insufficient memory */ 6 | #define PAPI_ESYS -3 /**< A System/C library call failed */ 7 | #define PAPI_ECMP -4 /**< Not supported by component */ 8 | #define PAPI_ESBSTR -4 /**< Backwards compatibility */ 9 | #define PAPI_ECLOST -5 /**< Access to the counters was lost or interrupted */ 10 | #define PAPI_EBUG -6 /**< Internal error, please send mail to the developers */ 11 | #define PAPI_ENOEVNT -7 /**< Event does not exist */ 12 | #define PAPI_ECNFLCT -8 /**< Event exists, but cannot be counted due to counter resource limitations */ 13 | #define PAPI_ENOTRUN -9 /**< EventSet is currently not running */ 14 | #define PAPI_EISRUN -10 /**< EventSet is currently counting */ 15 | #define PAPI_ENOEVST -11 /**< No such EventSet Available */ 16 | #define PAPI_ENOTPRESET -12 /**< Event in argument is not a valid preset */ 17 | #define PAPI_ENOCNTR -13 /**< Hardware does not support performance counters */ 18 | #define PAPI_EMISC -14 /**< Unknown error code */ 19 | #define PAPI_EPERM -15 /**< Permission level does not permit operation */ 20 | #define PAPI_ENOINIT -16 /**< PAPI hasn't been initialized yet */ 21 | #define PAPI_ENOCMP -17 /**< Component Index isn't set */ 22 | #define PAPI_ENOSUPP -18 /**< Not supported */ 23 | #define PAPI_ENOIMPL -19 /**< Not implemented */ 24 | #define PAPI_EBUF -20 /**< Buffer size exceeded */ 25 | #define PAPI_EINVAL_DOM -21 /**< EventSet domain is not supported for the operation */ 26 | #define PAPI_EATTR -22 /**< Invalid or missing event attributes */ 27 | #define PAPI_ECOUNT -23 /**< Too many events or attributes */ 28 | #define PAPI_ECOMBO -24 /**< Bad combination of features */ 29 | #define PAPI_ECMP_DISABLED -25 /**< Component containing event is disabled */ 30 | #define PAPI_NUM_ERRORS 26 /**< Number of error messages specified in this API */ 31 | 32 | 33 | // PAPI initialization state (definitions from papi.h) 34 | 35 | #define PAPI_NOT_INITED 0 36 | #define PAPI_LOW_LEVEL_INITED 1 /* Low level has called library init */ 37 | #define PAPI_HIGH_LEVEL_INITED 2 /* High level has called library init */ 38 | #define PAPI_THREAD_LEVEL_INITED 4 /* Threads have been inited */ 39 | 40 | 41 | // PAPI states (definitions from papi.h) 42 | 43 | #define PAPI_STOPPED 0x01 /**< EventSet stopped */ 44 | #define PAPI_RUNNING 0x02 /**< EventSet running */ 45 | #define PAPI_PAUSED 0x04 /**< EventSet temp. disabled by the library */ 46 | #define PAPI_NOT_INIT 0x08 /**< EventSet defined, but not initialized */ 47 | #define PAPI_OVERFLOWING 0x10 /**< EventSet has overflowing enabled */ 48 | #define PAPI_PROFILING 0x20 /**< EventSet has profiling enabled */ 49 | #define PAPI_MULTIPLEXING 0x40 /**< EventSet has multiplexing enabled */ 50 | #define PAPI_ATTACHED 0x80 /**< EventSet is attached to another thread/process */ 51 | #define PAPI_CPU_ATTACHED 0x100 /**< EventSet is attached to a specific cpu (not counting thread of execution) */ 52 | 53 | 54 | // Other PAPI constants (definitions from papi.h) 55 | 56 | #define PAPI_NULL -1 /**`_. 4 | 5 | Example using the High Level API: 6 | 7 | :: 8 | 9 | from pypapi import papi_high 10 | 11 | papi_high.hl_region_begin("computation") 12 | 13 | # computation 14 | 15 | papi_high.hl_region_end("computation") 16 | 17 | 18 | To change where results are stored or which events to record is achieved with 19 | environment variables. 20 | 21 | Bash: 22 | 23 | .. code-block:: bash 24 | 25 | export PAPI_EVENTS="PAPI_TOT_INS,PAPI_TOT_CYC" 26 | export PAPI_OUTPUT_DIRECTORY="path/to/output" 27 | 28 | 29 | Python:: 30 | 31 | import os 32 | 33 | os.environ["PAPI_EVENTS"] = "PAPI_TOT_INS,PAPI_TOT_CYC" 34 | os.environ["PAPI_OUTPUT_DIRECTORY"] = "path/to/output" 35 | 36 | """ 37 | 38 | from ._papi import lib, ffi 39 | from .exceptions import papi_error 40 | 41 | 42 | # int PAPI_hl_region_begin(const char* region); /**< read performance events at the beginning of a region */ 43 | @papi_error 44 | def hl_region_begin(region): 45 | """Read performance events at the beginning of a region. 46 | 47 | :param string region: name of instrumented region 48 | 49 | :returns: Operation status 50 | :rtype: int 51 | 52 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 53 | :raises PapiSystemError: A system or C library call failed inside PAPI. 54 | """ 55 | cregion = ffi.new("char[]", region.encode("ascii")) 56 | rcode = lib.PAPI_hl_region_begin(cregion) 57 | return rcode, rcode 58 | 59 | 60 | # int PAPI_hl_read(const char* region); 61 | @papi_error 62 | def hl_read(region): 63 | """Read performance events inside of a region and store the difference to 64 | the corresponding beginning of the region. 65 | 66 | :param string region: name of instrumented region 67 | 68 | :returns: Operation status 69 | :rtype: int 70 | 71 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 72 | :raises PapiSystemError: A system or C library call failed inside PAPI. 73 | """ 74 | cregion = ffi.new("char[]", region.encode("ascii")) 75 | rcode = lib.PAPI_hl_read(cregion) 76 | return rcode, rcode 77 | 78 | 79 | # int PAPI_hl_region_end(const char* region); 80 | @papi_error 81 | def hl_region_end(region): 82 | """Read performance events at the end of a region and store the difference 83 | to the corresponding beginning of the region. 84 | 85 | :param string region: name of instrumented region 86 | 87 | :returns: Operation status 88 | :rtype: int 89 | 90 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 91 | :raises PapiSystemError: A system or C library call failed inside PAPI. 92 | """ 93 | cregion = ffi.new("char[]", region.encode("ascii")) 94 | rcode = lib.PAPI_hl_region_end(cregion) 95 | return rcode, rcode 96 | 97 | 98 | # int PAPI_hl_stop(); 99 | @papi_error 100 | def hl_stop(): 101 | """Stops a running high-level event set. 102 | 103 | :returns: Operation status 104 | :rtype: int 105 | 106 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 107 | :raises PapiSystemError: A system or C library call failed inside PAPI. 108 | """ 109 | rcode = lib.PAPI_hl_stop() 110 | return rcode, rcode 111 | -------------------------------------------------------------------------------- /pypapi/papi_low.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module binds the PAPI Low Level API. 3 | 4 | Despite our desire to stay as close as possible as the original C API, we had 5 | to make a lot of change to make this API more *pythonic*. If you are used to 6 | the C API, please read carefully this documentation. 7 | 8 | Simple example:: 9 | 10 | from pypapi import papi_low as papi 11 | from pypapi import events 12 | 13 | papi.library_init() 14 | 15 | evs = papi.create_eventset() 16 | papi.add_event(evs, events.PAPI_FP_OPS) 17 | 18 | papi.start(evs) 19 | 20 | # Do some computation here 21 | 22 | result = papi.stop(evs) 23 | print(result) 24 | 25 | papi.cleanup_eventset(evs) 26 | papi.destroy_eventset(evs) 27 | 28 | .. NOTE:: 29 | 30 | This binding is currently very partial, there is a lot of missing function. 31 | If you need one of the missing functions, please `fill an issue on Github 32 | `_. 33 | """ 34 | 35 | from ctypes import c_longlong, c_ulonglong 36 | 37 | from ._papi import lib, ffi 38 | from .exceptions import papi_error, PapiError, PapiInvalidValueError 39 | from .consts import ( 40 | PAPI_VER_CURRENT, 41 | PAPI_NULL, 42 | PAPI_PRESET_MASK, 43 | PAPI_NATIVE_MASK, 44 | PAPI_MAX_STR_LEN, 45 | ) 46 | from .structs import ( 47 | EVENT_info, 48 | HARDWARE_info, 49 | DMEM_info, 50 | EXECUTABLE_info, 51 | COMPONENT_info, 52 | SHARED_LIB_info, 53 | Flips, 54 | Flops, 55 | IPC, 56 | EPC, 57 | ) 58 | 59 | 60 | # int PAPI_accum(int EventSet, long long * values); 61 | @papi_error 62 | def accum(eventSet, values): 63 | """Adds the counters of the indicated event set into the array values. The 64 | counters are zeroed and continue counting after the operation. 65 | 66 | :param int eventSet: An integer handle for a PAPI Event Set as created by 67 | :py:func:`create_eventset`. 68 | :param list(int) values: A list to hold the counter values of the counting 69 | events. 70 | 71 | :rtype: list(int) 72 | 73 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 74 | :raises PapiSystemError: A system or C library call failed inside PAPI, see 75 | the errno variable. 76 | :raises PapiNoEventSetError: The event set specified does not exist. 77 | """ 78 | eventCount_p = ffi.new("int*", 0) 79 | rcode = lib.PAPI_list_events(eventSet, ffi.NULL, eventCount_p) 80 | 81 | if rcode < 0: 82 | return rcode, None 83 | 84 | eventCount = ffi.unpack(eventCount_p, 1)[0] 85 | 86 | if len(values) != eventCount: 87 | raise PapiInvalidValueError( 88 | message="the length of the 'value' list " 89 | "(%i) is different of the one of " 90 | "the event set (%i)" % (len(values), eventCount) 91 | ) 92 | 93 | values = ffi.new("long long[]", values) 94 | 95 | rcode = lib.PAPI_accum(eventSet, values) 96 | 97 | return rcode, ffi.unpack(values, eventCount) 98 | 99 | 100 | # int PAPI_add_event(int EventSet, int Event); 101 | @papi_error 102 | def add_event(eventSet, eventCode): 103 | """Add single PAPI preset or native hardware event to an event set. 104 | 105 | :param int eventSet: An integer handle for a PAPI Event Set as created by 106 | :py:func:`create_eventset`. 107 | :param int eventCode: A defined event such as ``PAPI_TOT_INS`` (from 108 | :doc:`events`). 109 | 110 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 111 | :raises PapiNoMemoryError: Insufficient memory to complete the operation. 112 | :raises PapiNoEventSetError: The event set specified does not exist. 113 | :raises PapiIsRunningError: The event set is currently counting events. 114 | :raises PapiConflictError: The underlying counter hardware can not count 115 | this event and other events in the event set simultaneously. 116 | :raises PapiNoEventError: The PAPI preset is not available on the underlying 117 | hardware. 118 | :raises PapiBugError: Internal error, please send mail to the developers. 119 | """ 120 | rcode = lib.PAPI_add_event(eventSet, eventCode) 121 | 122 | if rcode > 0: 123 | raise PapiError( 124 | message="Unable to add some of the given events: %i of" 125 | " 1 event added to the event set" % rcode 126 | ) 127 | 128 | return rcode, None 129 | 130 | 131 | # int PAPI_add_named_event(int EventSet, const char *EventName); 132 | @papi_error 133 | def add_named_event(eventSet, eventName): 134 | """Add an event by name to a PAPI event set. 135 | 136 | :param int eventSet: An integer handle for a PAPI Event Set as created by 137 | :py:func:`create_eventset`. 138 | :param str eventName: Name of a defined event 139 | 140 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 141 | :raises PapiInitializationError: The PAPI library has not been initialized. 142 | :raises PapiNoMemoryError: Insufficient memory to complete the operation. 143 | :raises PapiNoEventSetError: The event set specified does not exist. 144 | :raises PapiIsRunningError: The event set is currently counting events. 145 | :raises PapiConflictError: The underlying counter hardware can not count 146 | this event and other events in the event set simultaneously. 147 | :raises PapiNoEventError: The PAPI preset is not available on the underlying 148 | hardware. 149 | :raises PapiBugError: Internal error, please send mail to the developers. 150 | """ 151 | eventName_p = ffi.new("char[]", eventName.encode("ascii")) 152 | rcode = lib.PAPI_add_named_event(eventSet, eventName_p) 153 | 154 | return rcode, None 155 | 156 | 157 | # int PAPI_add_events(int EventSet, int *Events, int number); 158 | @papi_error 159 | def add_events(eventSet, eventCodes): 160 | """Add list of PAPI preset or native hardware events to an event set. 161 | 162 | :param int eventSet: An integer handle for a PAPI Event Set as created by 163 | :py:func:`create_eventset`. 164 | :param list(int) eventCodes: A list of defined events (from :doc:`events`). 165 | 166 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 167 | :raises PapiNoMemoryError: Insufficient memory to complete the operation. 168 | :raises PapiNoEventSetError: The event set specified does not exist. 169 | :raises PapiIsRunningError: The event set is currently counting events. 170 | :raises PapiConflictError: The underlying counter hardware can not count 171 | this event and other events in the event set simultaneously. 172 | :raises PapiNoEventError: The PAPI preset is not available on the underlying 173 | hardware. 174 | :raises PapiBugError: Internal error, please send mail to the developers. 175 | """ 176 | number = len(eventCodes) 177 | eventCodes_p = ffi.new("int[]", eventCodes) 178 | rcode = lib.PAPI_add_events(eventSet, eventCodes_p, number) 179 | 180 | if rcode > 0: 181 | raise PapiError( 182 | message="Unable to add some of the given events: %i of" 183 | " %i events added to the event set" % (rcode, number) 184 | ) 185 | 186 | return rcode, None 187 | 188 | 189 | # int PAPI_assign_eventset_component(int EventSet, int cidx); 190 | @papi_error 191 | def assign_eventset_component(eventSet, component): 192 | """Assign a component index to an existing but empty eventset. 193 | 194 | :param int eventSet: An integer handle for a PAPI Event Set as created by 195 | :py:func:`create_eventset`. 196 | :param int component: An integer identifier for a component. 197 | By convention, component 0 is always the cpu component. 198 | 199 | 200 | :raises PapiNoComponentError: The argument component is not a valid component. 201 | :raises PapiNoEventSetError: The event set specified does not exist. 202 | :raises PapiNoMemoryError: Insufficient memory to complete the operation. 203 | """ 204 | rcode = lib.PAPI_assign_eventset_component(eventSet, component) 205 | 206 | return rcode, None 207 | 208 | 209 | # int PAPI_attach(int EventSet, unsigned long tid); 210 | @papi_error 211 | def attach(eventSet, pid): 212 | """Attach specified event set to a specific process or thread id. 213 | 214 | :param int eventSet: An integer handle for a PAPI Event Set as created by 215 | :py:func:`create_eventset`. 216 | :param int pid: A process id. 217 | 218 | :raises PapiComponentError: This feature is unsupported on this component. 219 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 220 | :raises PapiNoEventSetError: The event set specified does not exist. 221 | :raises PapiIsRunningError: The event set is currently counting events. 222 | """ 223 | rcode = lib.PAPI_attach(eventSet, pid) 224 | return rcode, None 225 | 226 | 227 | # int PAPI_cleanup_eventset(int EventSet); 228 | @papi_error 229 | def cleanup_eventset(eventSet): 230 | """Remove all PAPI events from an event set and turns off profiling and 231 | overflow for all events in the EventSet. This can not be called if the 232 | EventSet is not stopped. 233 | 234 | :param int eventSet: An integer handle for a PAPI Event Set as created by 235 | :py:func:`create_eventset`. 236 | 237 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 238 | :raises PapiNoEventSetError: The event set specified does not exist. 239 | :raises PapiIsRunningError: The event set is currently counting events. 240 | :raises PapiBugError: Internal error, please send mail to the developers. 241 | 242 | .. WARNING:: 243 | 244 | If the user has set profile on an event with the call, then when 245 | destroying the EventSet the memory allocated by will not be freed. The 246 | user should turn off profiling on the Events before destroying the 247 | EventSet to prevent this behavior. 248 | """ 249 | rcode = lib.PAPI_cleanup_eventset(eventSet) 250 | return rcode, None 251 | 252 | 253 | # int PAPI_create_eventset(int *EventSet); 254 | @papi_error 255 | def create_eventset(): 256 | """Create a new empty PAPI event set. The user may then add hardware events to 257 | the event set by calling :py:func:`add_event` or similar routines. 258 | 259 | :returns: the event set handle. 260 | :rtype: int 261 | 262 | :raises PapiInvalidValueError: The argument handle has not been initialized 263 | to PAPI_NULL or the argument is a NULL pointer. 264 | :raises PapiNoMemoryError: Insufficient memory to complete the operation. 265 | 266 | .. NOTE:: 267 | 268 | PAPI-C uses a late binding model to bind EventSets to components. When 269 | an EventSet is first created it is not bound to a component. This will 270 | cause some API calls that modify EventSet options to fail. An EventSet 271 | can be bound to a component explicitly by calling 272 | :py:func:`assign_eventset_component` or implicitly by calling 273 | :py:func:`add_event` or similar routines. 274 | """ 275 | eventSet = ffi.new("int*", PAPI_NULL) 276 | rcode = lib.PAPI_create_eventset(eventSet) 277 | return rcode, ffi.unpack(eventSet, 1)[0] 278 | 279 | 280 | # int PAPI_detach(int EventSet); 281 | @papi_error 282 | def detach(eventSet): 283 | """Detach specified event set from a previously specified process or 284 | thread id. 285 | 286 | :param int eventSet: An integer handle for a PAPI Event Set as created by 287 | :py:func:`create_eventset`. 288 | 289 | :raises PapiComponentError: This feature is unsupported on this component. 290 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 291 | :raises PapiNoEventSetError: The event set specified does not exist. 292 | :raises PapiIsRunningError: The event set is currently counting events. 293 | """ 294 | rcode = lib.PAPI_detach(eventSet) 295 | return rcode, None 296 | 297 | 298 | # int PAPI_destroy_eventset(int *EventSet); 299 | @papi_error 300 | def destroy_eventset(eventSet): 301 | """Deallocates memory associated with an empty PAPI event set. 302 | 303 | :param int eventSet: An integer handle for a PAPI Event Set as created by 304 | :py:func:`create_eventset`. 305 | 306 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 307 | Attempting to destroy a non-empty event set or passing in a null 308 | pointer to be destroyed. 309 | :raises PapiNoEventSetError: The event set specified does not exist. 310 | :raises PapiIsRunningError: The event set is currently counting events. 311 | :raises PapiBugError: Internal error, please send mail to the developers. 312 | 313 | .. WARNING:: 314 | 315 | If the user has set profile on an event with the call, then when 316 | destroying the EventSet the memory allocated by will not be freed. The 317 | user should turn off profiling on the Events before destroying the 318 | EventSet to prevent this behavior. 319 | """ 320 | eventSet_p = ffi.new("int*", eventSet) 321 | rcode = lib.PAPI_destroy_eventset(eventSet_p) 322 | return rcode, None 323 | 324 | 325 | # int PAPI_enum_event(int *EventCode, int modifier); 326 | def enum_event(): 327 | """Enumerate PAPI preset or native events. 328 | 329 | :returns: dictionary of PRESET and NATIVE events. 330 | :rtype: dict 331 | """ 332 | 333 | return enum_cmp_event(0) 334 | 335 | 336 | # int PAPI_enum_cmp_event(int *EventCode, int modifier, int cidx) 337 | def enum_cmp_event(component): 338 | """Enumerate PAPI preset or native events for a given component. 339 | 340 | :param int component: Specifies the component to search in. 341 | 342 | :returns: dictionary of PRESET and NATIVE events. 343 | :rtype: dict 344 | """ 345 | events = {"native": [], "preset": []} 346 | 347 | eventCode_p = ffi.new("int*", 0 | PAPI_NATIVE_MASK) 348 | rcode = lib.PAPI_enum_cmp_event(eventCode_p, 1, component) 349 | if rcode == 0: 350 | info = get_event_info(ffi.unpack(eventCode_p, 1)[0]) 351 | events["native"].append(info) 352 | while lib.PAPI_enum_cmp_event(eventCode_p, 0, component) == 0: 353 | info = get_event_info(ffi.unpack(eventCode_p, 1)[0]) 354 | events["native"].append(info) 355 | 356 | eventCode_p = ffi.new("int*", 0 | PAPI_PRESET_MASK) 357 | rcode = lib.PAPI_enum_cmp_event(eventCode_p, 1, component) 358 | if rcode == 0: 359 | info = get_event_info(ffi.unpack(eventCode_p, 1)[0]) 360 | events["preset"].append(info) 361 | while lib.PAPI_enum_cmp_event(eventCode_p, 0, component) == 0: 362 | info = get_event_info(ffi.unpack(eventCode_p, 1)[0]) 363 | events["preset"].append(info) 364 | 365 | return events 366 | 367 | 368 | # int PAPI_event_code_to_name(int EventCode, char *out); /**< translate an integer PAPI event code into an ASCII PAPI preset or native name */ 369 | @papi_error 370 | def event_code_to_name(eventCode): 371 | """Convert a numeric hardware event code to a name. 372 | 373 | :param int eventCode: The numeric code for the event. 374 | 375 | :returns: the event name 376 | :rtype: str 377 | 378 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 379 | :raises PapiNotPresetError: The hardware event specified is not a valid PAPI preset. 380 | :raises PapiNoEventError: The hardware event is not available on the underlying hardware. 381 | """ 382 | out_p = ffi.new(f"char[{PAPI_MAX_STR_LEN}]") 383 | rcode = lib.PAPI_event_code_to_name(eventCode, out_p) 384 | return rcode, ffi.string(out_p, PAPI_MAX_STR_LEN).decode("ascii") 385 | 386 | 387 | # int PAPI_event_name_to_code(const char *in, int *out); 388 | @papi_error 389 | def event_name_to_code(eventName): 390 | """Convert a name to a numeric hardware event code. 391 | 392 | :param str eventName: A string containing the event name as listed in 393 | PAPI_presets or discussed in PAPI_native. 394 | 395 | :returns: the event code. 396 | :rtype: int 397 | 398 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 399 | :raises PapiNotPresetError: The hardware event specified is not a valid PAPI preset. 400 | :raises PapiInitializationError: The PAPI library has not been initialized. 401 | :raises PapiNoEventError: The hardware event is not available on the underlying hardware. 402 | """ 403 | eventName_p = ffi.new("char[]", eventName.encode()) 404 | eventCode_p = ffi.new("int *", PAPI_NULL) 405 | rcode = lib.PAPI_event_name_to_code(eventName_p, eventCode_p) 406 | return rcode, ffi.unpack(eventCode_p, 1)[0] 407 | 408 | 409 | # int PAPI_get_dmem_info(PAPI_dmem_info_t *dest); 410 | @papi_error 411 | def get_dmem_info(): 412 | """Get information about the dynamic memory usage of the current program. 413 | 414 | :returns: dynamic memory usage information 415 | :rtype: DMEM_info 416 | 417 | :raises PapiComponentError: The function is not implemented for the current component. 418 | :raises PapiInvalidValueError: Any value in the structure or array may be undefined as indicated by this error value. 419 | :raises PapiSystemError: A system error occurred. 420 | """ 421 | info_p = DMEM_info.alloc_empty() 422 | rcode = lib.PAPI_get_dmem_info(info_p) 423 | 424 | return rcode, DMEM_info(info_p) 425 | 426 | 427 | # int PAPI_get_event_info(int EventCode, PAPI_event_info_t * info); 428 | @papi_error 429 | def get_event_info(eventCode): 430 | """Get the event's name and description info. 431 | 432 | :param int eventCode: event code (preset or native). 433 | 434 | :returns: event information 435 | :rtype: EVENT_info 436 | 437 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 438 | :raises PapiNotPresetError: The PAPI preset mask was set, 439 | but the hardware event specified is not a valid PAPI preset. 440 | :raises PapiNoEventError: The PAPI preset is not available on the underlying hardware. 441 | This function fills the event information into a structure. 442 | In Fortran, some fields of the structure are returned explicitly. 443 | This function works with existing PAPI preset and native event codes. 444 | """ 445 | info_p = EVENT_info.alloc_empty() 446 | rcode = lib.PAPI_get_event_info(eventCode, info_p) 447 | 448 | return rcode, EVENT_info(info_p) 449 | 450 | 451 | # const PAPI_exe_info_t *PAPI_get_executable_info(void); 452 | def get_executable_info(): 453 | """Get the executable's address space info. 454 | 455 | :returns: executable information 456 | :rtype: EXECUTABLE_info 457 | 458 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 459 | """ 460 | info_p = lib.PAPI_get_executable_info() 461 | 462 | return None if info_p == ffi.NULL else EXECUTABLE_info(info_p) 463 | 464 | 465 | # const PAPI_hw_info_t *PAPI_get_hardware_info(void); 466 | def get_hardware_info(): 467 | """Get information about the system hardware. 468 | In C, this function returns a pointer to a structure containing information about 469 | the hardware on which the program runs. 470 | In Fortran, the values of the structure are returned explicitly. 471 | 472 | :returns: hardware information 473 | :rtype: HARDWARE_info 474 | 475 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 476 | """ 477 | info_p = lib.PAPI_get_hardware_info() 478 | 479 | return None if info_p == ffi.NULL else HARDWARE_info(info_p) 480 | 481 | 482 | # const PAPI_component_info_t *PAPI_get_component_info(int cidx); 483 | def get_component_info(component): 484 | """Get information about a specific software component. 485 | 486 | :returns: component information 487 | :rtype: COMPONENT_info 488 | """ 489 | info_p = lib.PAPI_get_component_info(component) 490 | 491 | return None if info_p == ffi.NULL else COMPONENT_info(info_p) 492 | 493 | 494 | # int PAPI_get_multiplex(int EventSet); 495 | def get_multiplex(enventSet): 496 | return lib.get_multiplex(enventSet) 497 | 498 | 499 | # int PAPI_get_opt(int option, PAPI_option_t * ptr); 500 | 501 | # int PAPI_get_cmp_opt(int option, PAPI_option_t * ptr,int cidx); 502 | 503 | 504 | # long long PAPI_get_real_cyc(void); 505 | def get_real_cyc(): 506 | """Get real time counter value in clock cycles. Returns the total real 507 | time passed since some arbitrary starting point. The time is returned in 508 | clock cycles. This call is equivalent to wall clock time. 509 | 510 | :returns: total real time passed since some arbitrary starting point 511 | :rtype: ctypes.c_longlong 512 | """ 513 | return c_longlong(lib.PAPI_get_real_cyc()) 514 | 515 | 516 | # long long PAPI_get_real_nsec(void); 517 | def get_real_nsec(): 518 | """Returns total number of nanoseconds since some arbitrary starting point. 519 | 520 | :rtype: ctypes.c_longlong 521 | """ 522 | return c_longlong(lib.PAPI_get_real_nsec()) 523 | 524 | 525 | # long long PAPI_get_real_usec(void); 526 | def get_real_usec(): 527 | """Get real time counter value in microseconds. This function returns 528 | the total real time passed since some arbitrary starting point. 529 | The time is returned in microseconds. This call is equivalent 530 | to wall clock time. 531 | 532 | :returns: total real time passed since some arbitrary starting point 533 | :rtype: ctypes.c_longlong 534 | """ 535 | return c_longlong(lib.PAPI_get_real_usec()) 536 | 537 | 538 | # const PAPI_shlib_info_t *PAPI_get_shared_lib_info(void); 539 | def get_shared_lib_info(): 540 | """Get address info about the shared libraries used by the process. 541 | In C, this function returns a pointer to a structure containing 542 | information about the shared library used by the program. 543 | There is no Fortran equivalent call. 544 | 545 | :returns: shared libraries information 546 | :rtype: SHARED_LIB_info 547 | """ 548 | info_p = lib.PAPI_get_shared_lib_info() 549 | 550 | return None if info_p == ffi.NULL else SHARED_LIB_info(info_p) 551 | 552 | 553 | # int PAPI_get_thr_specific(int tag, void **ptr); /**< return a pointer to a thread specific stored data structure */ 554 | # int PAPI_get_overflow_event_index(int Eventset, long long overflow_vector, int *array, int *number); /**< # decomposes an overflow_vector into an event index array */ 555 | 556 | 557 | # long long PAPI_get_virt_cyc(void); 558 | def get_virt_cyc(): 559 | """Get virtual time counter value in clock cycles 560 | 561 | :returns: virtual time counter value in clock cycles 562 | :rtype: ctypes.c_longlong 563 | """ 564 | return c_longlong(lib.PAPI_get_virt_cyc()) 565 | 566 | 567 | # long long PAPI_get_virt_nsec(void); 568 | def get_virt_nsec(): 569 | """Get virtual time counter values in nanoseconds. 570 | 571 | :returns: virtual time counter value in nanoseconds 572 | :rtype: c_longlong 573 | """ 574 | return c_longlong(lib.PAPI_get_virt_nsec()) 575 | 576 | 577 | # long long PAPI_get_virt_usec(void); 578 | def get_virt_usec(): 579 | """Get virtual time counter values in microseconds. 580 | 581 | :returns: virtual time counter value in microseconds 582 | :rtype: c_longlong 583 | """ 584 | return c_longlong(lib.PAPI_get_virt_usec()) 585 | 586 | 587 | # int PAPI_is_initialized(void); 588 | def is_initialized(): 589 | """Returns the initialized state of the PAPI library. 590 | 591 | :returns: the initialized state of the PAPI library (one of the 592 | :ref:`consts_init`). 593 | :rtype: int 594 | """ 595 | return lib.PAPI_is_initialized() 596 | 597 | 598 | # int PAPI_library_init(int version); 599 | @papi_error 600 | def library_init(version=PAPI_VER_CURRENT): 601 | """Initializes the PAPI library. 602 | 603 | :param int version: upon initialization, PAPI checks the argument against 604 | the internal value of ``PAPI_VER_CURRENT`` when the library was 605 | compiled. This guards against portability problems when updating the 606 | PAPI shared libraries on your system (optional, default: 607 | :py:data:`pypapi.consts.PAPI_VER_CURRENT`). 608 | 609 | :raises PapiInvalidValueError: papi.h is different from the version used to 610 | compile the PAPI library. 611 | :raises PapiNoMemoryError: Insufficient memory to complete the operation. 612 | :raises PapiComponentError: This component does not support the underlying 613 | hardware. 614 | :raises PapiSystemError: A system or C library call failed inside PAPI. 615 | 616 | .. WARNING:: 617 | 618 | If you don't call this before using any of the low level PAPI 619 | calls, your application could core dump. 620 | """ 621 | rcode = lib.PAPI_library_init(version) 622 | return rcode, None 623 | 624 | 625 | # int PAPI_list_events(int EventSet, int *Events, int *number); 626 | @papi_error 627 | def list_events(eventSet): 628 | """List the events that are members of an event set 629 | 630 | :param int eventSet: An integer handle for a PAPI Event Set as created by 631 | :py:func:`create_eventset`. 632 | 633 | :returns: the list of events. 634 | :rtype: list(int) 635 | 636 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 637 | :raises PapiNoEventSetError: The event set specified does not exist. 638 | """ 639 | number = ffi.new("int*", 0) 640 | 641 | rcode = lib.PAPI_list_events(eventSet, ffi.NULL, number) 642 | 643 | if rcode < 0: 644 | return rcode, None 645 | 646 | eventCount = ffi.unpack(number, 1)[0] 647 | events = ffi.new("int[]", eventCount) 648 | 649 | rcode = lib.PAPI_list_events(eventSet, events, number) 650 | 651 | return rcode, ffi.unpack(events, eventCount) 652 | 653 | 654 | # int PAPI_list_threads(unsigned long *tids, int *number); 655 | @papi_error 656 | def list_threads(): 657 | """List the registered thread ids. 658 | 659 | ``list_threads()`` returns to the caller a list of all thread IDs known to 660 | PAPI. This call assumes an initialized PAPI library 661 | 662 | :returns: the list of threads. 663 | :rtype: list(ctypes.c_ulonglong) 664 | 665 | :raises PapiInvalidValueError: Internal argument is invalid. 666 | """ 667 | number = ffi.new("int*", 0) 668 | 669 | rcode = lib.PAPI_list_threads(ffi.NULL, number) 670 | 671 | if rcode < 0: 672 | return rcode, None 673 | 674 | threadCount = ffi.unpack(number, 1)[0] 675 | threads = ffi.new("unsigned long[]", threadCount) 676 | 677 | rcode = lib.PAPI_list_threads(threads, number) 678 | 679 | return rcode, ffi.unpack(threads, threadCount) 680 | 681 | 682 | # int PAPI_lock(int); 683 | @papi_error 684 | def lock(lock): 685 | """Locks one of two mutex variables defined in papi.h. 686 | 687 | ``lock()`` grabs access to one of the two PAPI mutex variables. 688 | This function is provided to the user to have a platform independent 689 | call to a (hopefully) efficiently implemented mutex. 690 | 691 | :param int lock: an integer value specifying one of the two user locks: 692 | :py:const:`~pypapi.consts.PAPI_USR1_LOCK` or 693 | :py:const:`~pypapi.consts.PAPI_USR2_LOCK`. 694 | 695 | :returns: There is no return value for this call. Upon return from 696 | PAPI_lock the current thread has acquired exclusive access to the 697 | specified PAPI mutex. 698 | """ 699 | rcode = lib.PAPI_lock(lock) 700 | 701 | return rcode, None 702 | 703 | 704 | # int PAPI_multiplex_init(void); 705 | @papi_error 706 | def multiplex_init(): 707 | """Initializes multiplex support in the PAPI library. 708 | 709 | ``multiplex_init()`` enables and initializes multiplex support in the 710 | PAPI library. Multiplexing allows a user to count more events than total 711 | physical counters by time sharing the existing counters at some loss in 712 | precision. Applications that make no use of multiplexing do not need to 713 | call this routine. 714 | """ 715 | 716 | rcode = lib.PAPI_multiplex_init() 717 | 718 | return rcode, None 719 | 720 | 721 | # int PAPI_num_cmp_hwctrs(int cidx); 722 | def num_cmp_hwctrs(component): 723 | """Returns the number of hardware counters for the specified component. 724 | 725 | ``num_cmp_hwctrs()`` returns the number of counters present in the 726 | specified component. By convention, component 0 is always the cpu. On some 727 | components, especially for CPUs, the value returned is a theoretical 728 | maximum for estimation purposes only. It might not be possible to easily 729 | create an EventSet that contains the full number of events. This can be due 730 | to a variety of reasons: 731 | 732 | 1. CPUs (especially Intel and POWER) have the notion of fixed counters that 733 | can only measure one thing, usually cycles. 734 | 735 | 2. Some CPUs have very explicit rules about which event can run in which 736 | counter. In this case it might not be possible to add a wanted event 737 | even if counters are free. 738 | 739 | 3. Some CPUs halve the number of counters available when running with SMT 740 | (multiple CPU threads) enabled. 741 | 742 | 4. Some operating systems "steal" a counter to use for things such as NMI 743 | Watchdog timers. 744 | 745 | The only sure way to see if events will fit is to attempt adding events to 746 | an EventSet, and doing something sensible if an error is generated. 747 | :py:func:`library_init` must be called in order for this function to return 748 | anything greater than ``0``. 749 | 750 | :param int component: An integer identifier for a component. By convention, 751 | component 0 is always the cpu component. 752 | 753 | :returns: On success, this function returns a value greater than zero. 754 | A zero result usually means the library has not been initialized. 755 | :rtype: int 756 | """ 757 | 758 | return lib.PAPI_num_cmp_hwctrs(component) 759 | 760 | 761 | # int PAPI_num_events(int EventSet); 762 | @papi_error 763 | def num_events(eventSet): 764 | """Returns the number of events in an event set. 765 | 766 | ``num_events()`` returns the number of preset and/or native events 767 | contained in an event set. The event set should be created by 768 | create_eventset. 769 | 770 | :param int eventSet: an integer handle for a PAPI event set created by 771 | create_eventset. 772 | 773 | :returns: On success, this function returns the positive number of events 774 | in the event set. 775 | :rtype: int 776 | 777 | :raises PapiInvalidValueError: The event count is zero; only if code is compiled with debug enabled. 778 | :raises PapiNoEventSetError: The EventSet specified does not exist. 779 | """ 780 | rcode = lib.PAPI_num_events(eventSet) 781 | 782 | return rcode, rcode 783 | 784 | 785 | # int PAPI_overflow(int EventSet, int EventCode, int threshold, int flags, PAPI_overflow_handler_t handler); 786 | # void PAPI_perror(const char *msg ); 787 | def perror(msg): 788 | """Produces a string on standard error, describing the last library error. 789 | 790 | :param str msg: Optional message to print before the string describing the 791 | last error message. The routine ``perror()`` produces a message on the 792 | standard error output, describing the last error encountered during a 793 | call to PAPI. If s is not ``NULL``, s is printed, followed by a colon 794 | and a space. Then the error message and a new-line are printed 795 | """ 796 | 797 | msg_p = ffi.new("char[]", msg.encode("ascii")) 798 | lib.PAPI_perror(msg_p) 799 | 800 | return None 801 | 802 | 803 | # int PAPI_profil(void *buf, unsigned bufsiz, caddr_t offset, unsigned scale, int EventSet, int EventCode, int threshold, int flags); 804 | 805 | 806 | # int PAPI_query_event(int EventCode); 807 | @papi_error 808 | def query_event(eventCode): 809 | """Query if PAPI event exists. 810 | 811 | :param int eventCode: a defined event such as 812 | :py:const:`~pypapi.events.PAPI_TOT_INS`. 813 | 814 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 815 | :raises PapiNoEventError: The PAPI preset is not available on the underlying hardware. 816 | """ 817 | 818 | rcode = lib.PAPI_query_event(eventCode) 819 | 820 | return rcode, None 821 | 822 | 823 | # int PAPI_query_named_event(const char *EventName); 824 | def query_named_event(eventName): 825 | """Query if a named PAPI event exists. 826 | 827 | :param str eventName: a defined event such as 828 | :py:const:`~pypapi.events.PAPI_TOT_INS`. 829 | 830 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 831 | :raises PapiNoEventError: The PAPI preset is not available on the 832 | underlying hardware. 833 | """ 834 | 835 | eventName_p = ffi.new("char[]", eventName.encode("ascii")) 836 | rcode = lib.PAPI_query_named_event(eventName_p) 837 | 838 | return rcode, None 839 | 840 | 841 | # int PAPI_read(int EventSet, long long * values); 842 | @papi_error 843 | def read(eventSet): 844 | """Copies the counters of the indicated event set into the provided array. 845 | The counters continue counting after the read and are not reseted. 846 | 847 | :param int eventSet: An integer handle for a PAPI Event Set as created by 848 | :py:func:`create_eventset`. 849 | 850 | :rtype: list(int) 851 | 852 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 853 | :raises PapiSystemError: A system or C library call failed inside PAPI, see 854 | the errno variable. 855 | :raises PapiNoEventSetError: The event set specified does not exist. 856 | """ 857 | eventCount_p = ffi.new("int*", 0) 858 | rcode = lib.PAPI_list_events(eventSet, ffi.NULL, eventCount_p) 859 | 860 | if rcode < 0: 861 | return rcode, None 862 | 863 | eventCount = ffi.unpack(eventCount_p, 1)[0] 864 | values = ffi.new("long long[]", eventCount) 865 | 866 | rcode = lib.PAPI_read(eventSet, values) 867 | 868 | return rcode, ffi.unpack(values, eventCount) 869 | 870 | 871 | # int PAPI_read_ts(int EventSet, long long * values, long long *cyc); 872 | 873 | 874 | # int PAPI_register_thread(void); 875 | @papi_error 876 | def register_thread(): 877 | """Notify PAPI that a thread has 'appeared'. 878 | 879 | :raises PapiNoMemoryError: Space could not be allocated to store the new thread information. 880 | :raises PapiSystemError: A system or C library call failed inside PAPI, see the errno variable. 881 | :raises PapiComponentError: Hardware counters for this thread could not be initialized. 882 | """ 883 | rcode = lib.PAPI_register_thread() 884 | 885 | return rcode, None 886 | 887 | 888 | # int PAPI_remove_named_event(int EventSet, const char *EventName); 889 | @papi_error 890 | def remove_named_event(eventSet, eventName): 891 | """Removes a named hardware event from a PAPI event set. 892 | 893 | A hardware event can be either a PAPI Preset or a native hardware event 894 | code. For a list of PAPI preset events, see PAPI_presets or run the 895 | papi_avail utility in the PAPI distribution. PAPI Presets can be passed to 896 | :py:func:`query_event` to see if they exist on the underlying 897 | architecture. For a list of native events available on the current 898 | platform, run papi_native_avail in the PAPI distribution. 899 | 900 | :param int eventSet: An integer handle for a PAPI Event Set as created by 901 | :py:func:`create_eventset`. 902 | :param str eventName: defined event such as 903 | :py:const:`~pypapi.events.PAPI_TOT_INS` or a native event. 904 | 905 | 906 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 907 | :raises PapiNoEventSetError: The event set specified does not exist. 908 | :raises PapiIsRunningError: The event set is currently counting events. 909 | :raises PapiConflictError: The underlying counter hardware can not count 910 | this event and other events in the event set simultaneously. 911 | :raises PapiNoEventError: The PAPI preset is not available on the underlying 912 | hardware. 913 | """ 914 | 915 | name_p = ffi.new("char[]", eventName.encode("ascii")) 916 | rcode = lib.PAPI_remove_named_event(eventSet, name_p) 917 | 918 | return rcode, None 919 | 920 | 921 | # int PAPI_remove_event(int EventSet, int EventCode); 922 | @papi_error 923 | def remove_event(eventSet, eventCode): 924 | """Removes a hardware event from a PAPI event set. 925 | 926 | :param int eventSet: An integer handle for a PAPI Event Set as created by 927 | :py:func:`create_eventset`. 928 | :param int eventCode: A defined event such as 929 | :py:const:`~pypapi.events.PAPI_TOT_INS` or a native event. (from 930 | :doc:`events`). 931 | 932 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 933 | :raises PapiNoEventSetError: The event set specified does not exist. 934 | :raises PapiIsRunningError: The event set is currently counting events. 935 | :raises PapiConflictError: The underlying counter hardware can not count 936 | this event and other events in the event set simultaneously. 937 | :raises PapiNoEventError: The PAPI preset is not available on the underlying 938 | hardware. 939 | """ 940 | rcode = lib.PAPI_remove_event(eventSet, eventCode) 941 | return rcode, None 942 | 943 | 944 | # int PAPI_remove_events(int EventSet, int *Events, int number); 945 | @papi_error 946 | def remove_events(eventSet, eventCodes): 947 | """Removes an list of hardware events from a PAPI event set. 948 | 949 | :param int eventSet: An integer handle for a PAPI Event Set as created by 950 | :py:func:`create_eventset`. 951 | :param int eventCodes: A list of defined event (from :doc:`events`). 952 | 953 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 954 | :raises PapiNoEventSetError: The event set specified does not exist. 955 | :raises PapiIsRunningError: The event set is currently counting events. 956 | :raises PapiConflictError: The underlying counter hardware can not count 957 | this event and other events in the event set simultaneously. 958 | :raises PapiNoEventError: The PAPI preset is not available on the underlying 959 | hardware. 960 | """ 961 | number = len(eventCodes) 962 | eventCodes_p = ffi.new("int[]", eventCodes) 963 | rcode = lib.PAPI_remove_events(eventSet, eventCodes_p, number) 964 | 965 | if rcode > 0: 966 | raise PapiError( 967 | message="Unable to remove some of the given events: " 968 | "%i of %i events added to the event set" % (rcode, number) 969 | ) 970 | 971 | return rcode, None 972 | 973 | 974 | # int PAPI_reset(int EventSet); 975 | @papi_error 976 | def reset(eventSet): 977 | """Reset the hardware event counts in an event set. 978 | 979 | :param int eventSet: An integer handle for a PAPI Event Set as created by 980 | :py:func:`create_eventset`. 981 | 982 | :raises PapiNoEventSetError: The event set specified does not exist. 983 | :raises PapiSystemError: A system or C library call failed inside PAPI, see the errno variable. 984 | """ 985 | rcode = lib.PAPI_reset(eventSet) 986 | 987 | return rcode, None 988 | 989 | 990 | # int PAPI_set_debug(int level); 991 | @papi_error 992 | def set_debug(level): 993 | """Set the current debug level for error output from PAPI. 994 | 995 | :param int level: one of the constants shown in the table below and defined 996 | in the consts.py file. The possible debug levels for debugging are: 997 | 998 | * :py:const:`~pypapi.consts.PAPI_QUIET` Do not print anything, just 999 | return the error code 1000 | * :py:const:`~pypapi.consts.PAPI_VERB_ECONT` Print error message and 1001 | continue 1002 | * :py:const:`~pypapi.consts.PAPI_VERB_ESTOP` Print error message and 1003 | exit 1004 | 1005 | :raises PapiInvalidValueError: The debug level is invalid. The current debug level is used by both the internal error and debug message handler subroutines. 1006 | """ 1007 | 1008 | rcode = lib.PAPI_set_debug(level) 1009 | return rcode, None 1010 | 1011 | 1012 | # int PAPI_set_cmp_domain(int domain, int cidx); 1013 | @papi_error 1014 | def set_cmp_domain(domain, component): 1015 | """Set the default counting domain for new event sets bound to the 1016 | specified component. 1017 | 1018 | Sets the default counting domain for all new event sets in all threads, and 1019 | requires an explicit component argument. Event sets that are already in 1020 | existence are not affected. To change the domain of an existing event set, 1021 | please see :py:func:`set_opt`. The reader should note that the domain of 1022 | an event set affects only the mode in which the counter continues to run. 1023 | Counts are still aggregated for the current process, and not for any other 1024 | processes in the system. Thus when requesting 1025 | :py:const:`~pypapi.consts.PAPI_DOM_KERNEL`, the user is asking for events 1026 | that occur on behalf of the process, inside the kernel. 1027 | 1028 | :param int domain: one of the following constants as defined in 1029 | :doc:`consts`: 1030 | 1031 | * :py:const:`~pypapi.consts.PAPI_DOM_USER` User context counted 1032 | * :py:const:`~pypapi.consts.PAPI_DOM_KERNEL` Kernel/OS context counted 1033 | * :py:const:`~pypapi.consts.PAPI_DOM_OTHER` Exception/transient mode 1034 | counted 1035 | * :py:const:`~pypapi.consts.PAPI_DOM_SUPERVISOR` Supervisor/hypervisor 1036 | context counted 1037 | * :py:const:`~pypapi.consts.PAPI_DOM_ALL` All above contexts counted 1038 | * :py:const:`~pypapi.consts.PAPI_DOM_MIN` The smallest available 1039 | context 1040 | * :py:const:`~pypapi.consts.PAPI_DOM_MAX` The largest available context 1041 | * :py:const:`~pypapi.consts.PAPI_DOM_HWSPEC` Something other than CPU 1042 | like stuff. 1043 | 1044 | Individual components can decode low order bits for more meaning. 1045 | 1046 | :param int component: An integer identifier for a component. 1047 | By convention, component 0 is always the cpu component. 1048 | 1049 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 1050 | :raises PapiNoComponentError: The argument component is not a valid component. 1051 | """ 1052 | rcode = lib.PAPI_set_cmp_domain(domain, component) 1053 | 1054 | return rcode, None 1055 | 1056 | 1057 | # int PAPI_set_domain(int domain); 1058 | @papi_error 1059 | def set_domain(domain): 1060 | """Set the default counting domain for new event sets bound to the cpu 1061 | component. 1062 | 1063 | Sets the default counting domain for all new event sets created by 1064 | :py:func:`create_eventset` in all threads. This call implicitly sets the 1065 | domain for the cpu component (component 0) and is included to preserve 1066 | backward compatibility. 1067 | 1068 | :param int domain: one of the following constants as defined in 1069 | :doc:`consts`: 1070 | 1071 | * :py:const:`~pypapi.consts.PAPI_DOM_USER` User context counted 1072 | * :py:const:`~pypapi.consts.PAPI_DOM_KERNEL` Kernel/OS context counted 1073 | * :py:const:`~pypapi.consts.PAPI_DOM_OTHER` Exception/transient mode 1074 | counted 1075 | * :py:const:`~pypapi.consts.PAPI_DOM_SUPERVISOR` Supervisor/hypervisor 1076 | context counted 1077 | * :py:const:`~pypapi.consts.PAPI_DOM_ALL` All above contexts counted 1078 | * :py:const:`~pypapi.consts.PAPI_DOM_MAX` The largest available context 1079 | 1080 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 1081 | """ 1082 | 1083 | rcode = lib.PAPI_set_domain(domain) 1084 | 1085 | return rcode, None 1086 | 1087 | 1088 | # int PAPI_set_cmp_granularity(int granularity, int cidx); 1089 | @papi_error 1090 | def set_cmp_granularity(granularity, component): 1091 | """Sets the default counting granularity for eventsets bound to the 1092 | specified component. 1093 | 1094 | Sets the default counting granularity for all new event sets, and requires 1095 | an explicit component argument. Event sets that are already in existence 1096 | are not affected. To change the granularity of an existing event set, 1097 | please see :py:func:`set_opt`. The reader should note that the granularity of an 1098 | event set affects only the mode in which the counter continues to run. 1099 | 1100 | :param int granularity: one of the following constants as defined in 1101 | :doc:`consts`: 1102 | 1103 | * :py:const:`~pypapi.consts.PAPI_GRN_THR` Count each individual thread 1104 | * :py:const:`~pypapi.consts.PAPI_GRN_PROC` Count each individual 1105 | process 1106 | * :py:const:`~pypapi.consts.PAPI_GRN_PROCG` Count each individual 1107 | process group 1108 | * :py:const:`~pypapi.consts.PAPI_GRN_SYS` Count the current CPU 1109 | * :py:const:`~pypapi.consts.PAPI_GRN_SYS_CPU` Count all CPUs 1110 | individually 1111 | * :py:const:`~pypapi.consts.PAPI_GRN_MIN` The finest available 1112 | granularity 1113 | * :py:const:`~pypapi.consts.PAPI_GRN_MAX` The coarsest available 1114 | granularity 1115 | 1116 | :param int component: An integer identifier for a component. 1117 | By convention, component 0 is always the cpu component. 1118 | 1119 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 1120 | :raises PapiNoComponentError: The argument component is not a valid component. 1121 | """ 1122 | 1123 | rcode = lib.PAPI_set_cmp_granularity(granularity, component) 1124 | 1125 | return rcode, None 1126 | 1127 | 1128 | # int PAPI_set_granularity(int granularity); 1129 | @papi_error 1130 | def set_granularity(granularity): 1131 | """Sets the default counting granularity for eventsets bound to the cpu 1132 | component. 1133 | 1134 | Sets the default counting granularity for all new event sets created by 1135 | create_eventset. This call implicitly sets the granularity for the cpu 1136 | component (component 0) and is included to preserve backward compatibility. 1137 | 1138 | :param int granularity: one of the following constants as defined in 1139 | :doc:`consts`: 1140 | 1141 | * :py:const:`~pypapi.consts.PAPI_GRN_THR` Count each individual thread 1142 | * :py:const:`~pypapi.consts.PAPI_GRN_PROC` Count each individual 1143 | process 1144 | * :py:const:`~pypapi.consts.PAPI_GRN_PROCG` Count each individual 1145 | process group 1146 | * :py:const:`~pypapi.consts.PAPI_GRN_SYS` Count the current CPU 1147 | * :py:const:`~pypapi.consts.PAPI_GRN_SYS_CPU` Count all CPUs 1148 | individually 1149 | * :py:const:`~pypapi.consts.PAPI_GRN_MIN` The finest available 1150 | granularity 1151 | * :py:const:`~pypapi.consts.PAPI_GRN_MAX` The coarsest available 1152 | granularity 1153 | 1154 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 1155 | """ 1156 | 1157 | rcode = lib.PAPI_set_granularity(granularity) 1158 | 1159 | return rcode, None 1160 | 1161 | 1162 | # int PAPI_set_multiplex(int EventSet); 1163 | @papi_error 1164 | def set_multiplex(eventSet): 1165 | """Converts a standard event set to a multiplexed event set. 1166 | 1167 | :param int eventSet: An integer handle for a PAPI Event Set as created by 1168 | :py:func:`create_eventset`. 1169 | 1170 | :raises PapiInvalidValueError: One or more of the arguments is invalid, or the EventSet is already multiplexed. 1171 | :raises PapiNoComponentError: The EventSet specified is not yet bound to a component. 1172 | :raises PapiNoEventSetError: The EventSet specified does not exist. 1173 | :raises PapiIsRunningError: The EventSet is currently counting events. 1174 | :raises PapiNoMemoryError: Insufficient memory to complete the operation. 1175 | """ 1176 | rcode = lib.PAPI_set_multiplex(eventSet) 1177 | return rcode, None 1178 | 1179 | 1180 | # int PAPI_set_opt(int option, PAPI_option_t * ptr); 1181 | def set_opt(*args): 1182 | """ 1183 | .. WARNING:: 1184 | 1185 | Not implemented in the Python bindings. Will raise ``NotImplementedError``. 1186 | """ 1187 | # XXX Function defined for doc reference. 1188 | raise NotImplementedError() # TODO 1189 | 1190 | 1191 | # int PAPI_set_thr_specific(int tag, void *ptr); 1192 | 1193 | 1194 | # void PAPI_shutdown(void); 1195 | def shutdown(): 1196 | """Finishes using PAPI and free all related resources.""" 1197 | lib.PAPI_shutdown() 1198 | return None 1199 | 1200 | 1201 | # int PAPI_sprofil(PAPI_sprofil_t * prof, int profcnt, int EventSet, int EventCode, int threshold, int flags); 1202 | 1203 | 1204 | # int PAPI_start(int EventSet); 1205 | @papi_error 1206 | def start(eventSet): 1207 | """Starts counting all of the hardware events contained in the EventSet. All 1208 | counters are implicitly set to zero before counting. 1209 | 1210 | :param int eventSet: An integer handle for a PAPI Event Set as created by 1211 | :py:func:`create_eventset`. 1212 | 1213 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 1214 | :raises PapiSystemError: A system or C library call failed inside PAPI, see 1215 | the errno variable. 1216 | :raises PapiNoEventSetError: The event set specified does not exist. 1217 | :raises PapiIsRunningError: The event set is currently counting events. 1218 | :raises PapiConflictError: The underlying counter hardware can not count 1219 | this event and other events in the event set simultaneously. 1220 | :raises PapiNoEventError: The PAPI preset is not available on the underlying 1221 | hardware. 1222 | """ 1223 | rcode = lib.PAPI_start(eventSet) 1224 | return rcode, None 1225 | 1226 | 1227 | # int PAPI_state(int EventSet, int *status); 1228 | @papi_error 1229 | def state(eventSet): 1230 | """Returns the counting state of the specified event set. 1231 | 1232 | :param int eventSet: An integer handle for a PAPI Event Set as created by 1233 | :py:func:`create_eventset`. 1234 | 1235 | :returns: the initialized state of the PAPI library (one of the 1236 | :ref:`consts_state`). 1237 | :rtype: int 1238 | 1239 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 1240 | :raises PapiNoEventSetError: The event set specified does not exist. 1241 | """ 1242 | status = ffi.new("int*", 0) 1243 | rcode = lib.PAPI_state(eventSet, status) 1244 | return rcode, ffi.unpack(status, 1)[0] 1245 | 1246 | 1247 | # int PAPI_stop(int EventSet, long long * values); 1248 | @papi_error 1249 | def stop(eventSet): 1250 | """Stops counting hardware events in an event set and return current 1251 | values. 1252 | 1253 | :param int eventSet: An integer handle for a PAPI Event Set as created by 1254 | :py:func:`create_eventset`. 1255 | 1256 | :rtype: list(int) 1257 | 1258 | :raises PapiInvalidValueError: One or more of the arguments is invalid. 1259 | :raises PapiSystemError: A system or C library call failed inside PAPI, see 1260 | the errno variable. 1261 | :raises PapiNoEventSetError: The event set specified does not exist. 1262 | :raises PapiNotRunningError: The EventSet is currently not running. 1263 | """ 1264 | eventCount_p = ffi.new("int*", 0) 1265 | rcode = lib.PAPI_list_events(eventSet, ffi.NULL, eventCount_p) 1266 | 1267 | if rcode < 0: 1268 | return rcode, None 1269 | 1270 | eventCount = ffi.unpack(eventCount_p, 1)[0] 1271 | values = ffi.new("long long[]", eventCount) 1272 | 1273 | rcode = lib.PAPI_stop(eventSet, values) 1274 | 1275 | return rcode, ffi.unpack(values, eventCount) 1276 | 1277 | 1278 | # char *PAPI_strerror(int); 1279 | def strerror(errCode): 1280 | """Returns a string describing the PAPI error code. 1281 | 1282 | :returns: Error string. If None is returned, errCode is invalid. 1283 | :rtype: str 1284 | """ 1285 | errStr_p = lib.PAPI_strerror(errCode) 1286 | 1287 | return ffi.string(errStr_p).decode("ascii") if errStr_p != ffi.NULL else None 1288 | 1289 | 1290 | # unsigned long PAPI_thread_id(void); 1291 | @papi_error 1292 | def thread_id(): 1293 | """Get the thread identifier of the current thread. 1294 | 1295 | :returns: a valid thread identifier. 1296 | :rtype: ctypes.c_ulonglong 1297 | 1298 | :raises PapiMiscellaneousError: if there are no threads registered. 1299 | :raises PapiInvalidValueError: if the thread id function returns an error. 1300 | """ 1301 | rval = lib.PAPI_thread_id() 1302 | return rval, c_ulonglong(rval) 1303 | 1304 | 1305 | # int PAPI_thread_init(unsigned long (*id_fn) (void)); 1306 | 1307 | 1308 | # int PAPI_unlock(int); 1309 | @papi_error 1310 | def unlock(lock): 1311 | """Unlocks one of the mutex variables. 1312 | 1313 | :param int lock: an integer value specifying one of the two user locks: 1314 | :py:const:`~pypapi.consts.PAPI_USR1_LOCK` or 1315 | :py:const:`~pypapi.consts.PAPI_USR2_LOCK`. 1316 | 1317 | """ 1318 | rcode = lib.PAPI_unlock(lock) 1319 | 1320 | return rcode, None 1321 | 1322 | 1323 | # int PAPI_unregister_thread(void); 1324 | @papi_error 1325 | def unregister_thread(): 1326 | """Notify PAPI that a thread has 'disappeared'. 1327 | 1328 | :raises PapiNoMemoryError: Space could not be allocated to store the new thread information. 1329 | :raises PapiSystemError: A system or C library call failed inside PAPI, see the errno variable. 1330 | :raises PapiComponentError: Hardware counters for this thread could not be initialized. 1331 | """ 1332 | rcode = lib.PAPI_unregister_thread() 1333 | 1334 | return rcode, None 1335 | 1336 | 1337 | # int PAPI_write(int EventSet, long long * values); 1338 | @papi_error 1339 | def write(eventSet): 1340 | """Write counter values into counters. 1341 | 1342 | :param int eventSet: An integer handle for a PAPI Event Set as created by 1343 | :py:func:`create_eventset`. 1344 | 1345 | :raises PapiNoEventSetError: The EventSet specified does not exist. 1346 | :raises PapiComponentError: write() is not implemented for this architecture. 1347 | :raises PapiSystemError: The EventSet is currently counting events and the 1348 | component could not change the values of the running counters. write() 1349 | writes the counter values provided in the array values into the event 1350 | set EventSet. The virtual counters managed by the PAPI library will be 1351 | set to the values provided. If the event set is running, an attempt 1352 | will be made to write the values to the running counters. This 1353 | operation is not permitted by all components and may result in a 1354 | run-time error. 1355 | """ 1356 | 1357 | return None, None 1358 | 1359 | 1360 | # int PAPI_get_event_component(int EventCode); 1361 | @papi_error 1362 | def get_event_component(eventCode): 1363 | """Return component an event belongs to. 1364 | 1365 | :param int eventCode: EventCode for which we want to know the component 1366 | index. 1367 | 1368 | :returns: component index 1369 | :rtype: int 1370 | 1371 | :raises PapiNoComponentError: component does not exist 1372 | """ 1373 | rcode = lib.PAPI_get_event_component(eventCode) 1374 | 1375 | return rcode, rcode 1376 | 1377 | 1378 | # int PAPI_get_eventset_component(int EventSet); 1379 | @papi_error 1380 | def get_eventset_component(eventSet): 1381 | """Returns index for component an EventSet is assigned to. 1382 | 1383 | :param int eventSet: EventSet for which we want to know the component 1384 | index. 1385 | 1386 | :returns: component index 1387 | :rtype: int 1388 | 1389 | :raises PapiNoEventSetError: EventSet does not exist. 1390 | :raises PapiNoComponentError: component is invalid or does not exist 1391 | positive value valid component index. 1392 | """ 1393 | rcode = lib.PAPI_get_eventset_component(eventSet) 1394 | 1395 | return rcode, rcode 1396 | 1397 | 1398 | # int PAPI_get_component_index(const char *name); 1399 | @papi_error 1400 | def get_component_index(componentName): 1401 | """Returns the component index for the named component. 1402 | 1403 | :param int componentName: name of component to find index for 1404 | 1405 | :returns: component index 1406 | :rtype: int 1407 | 1408 | :raises PapiNoComponentError: component does not exist 1409 | """ 1410 | componentName_p = ffi.new("char[]", componentName.encode("ascii")) 1411 | rcode = lib.PAPI_get_component_index(componentName_p) 1412 | 1413 | return rcode, rcode 1414 | 1415 | 1416 | # int PAPI_disable_component(int cidx); 1417 | @papi_error 1418 | def disable_component(component): 1419 | """Disables the specified component. 1420 | 1421 | :param int component: component index of component to be disabled 1422 | 1423 | :returns: component index 1424 | :rtype: int 1425 | 1426 | :raises PapiNoComponentError: component does not exist 1427 | :raises PapiInitializationError: cannot disable as PAPI has already been 1428 | initialized 1429 | """ 1430 | rcode = lib.PAPI_disable_component(component) 1431 | 1432 | return rcode, None 1433 | 1434 | 1435 | # int PAPI_disable_component_by_name(const char *name ); 1436 | @papi_error 1437 | def disable_component_by_name(componentName): 1438 | """Disables the named component. 1439 | 1440 | :param str componentName: name of the component to disable. 1441 | 1442 | :returns: component index 1443 | :rtype: int 1444 | 1445 | :raises PapiNoComponentError: component does not exist 1446 | :raises PapiInitializationError: unable to disable the component, the 1447 | library has already been initialized 1448 | """ 1449 | componentName_p = ffi.new("char[]", componentName.encode("ascii")) 1450 | rcode = lib.PAPI_disable_component_by_name(componentName_p) 1451 | 1452 | return rcode, None 1453 | 1454 | 1455 | # int PAPI_num_components(void); 1456 | def num_components(): 1457 | """Get the number of components available on the system. 1458 | 1459 | :returns: Number of components available on the system. Query the library 1460 | for a component count. 1461 | :rtype: int 1462 | """ 1463 | return lib.PAPI_num_components() 1464 | 1465 | 1466 | # int PAPI_flips_rate(int event, float *rtime, float *ptime, long long *flpins, float *mflips); 1467 | @papi_error 1468 | def flips_rate(event): 1469 | """Simplified call to get Mflips/s (floating point instruction rate), real 1470 | and processor time. 1471 | 1472 | :param int event: one of the three presets in :doc:`consts`: 1473 | 1474 | * :py:const:`~pypapi.consts.PAPI_FP_INS`, 1475 | * :py:const:`~pypapi.consts.PAPI_VEC_SP`, 1476 | * :py:const:`~pypapi.consts.PAPI_VEC_DP` 1477 | 1478 | :rtype: structs.Flips 1479 | 1480 | :raises PapiInvalidValueError: The counters were already started by 1481 | something other than :py:func:`flips_rate`. 1482 | :raises PapiNoEventError: The floating point operations or total cycles 1483 | event does not exist. 1484 | :raises PapiNoMemoryError: Insufficient memory to complete the operation. 1485 | """ 1486 | rtime = ffi.new("float*", 0) 1487 | ptime = ffi.new("float*", 0) 1488 | flpins = ffi.new("long long*", 0) 1489 | mflips = ffi.new("float*", 0) 1490 | 1491 | rcode = lib.PAPI_flips_rate(event, rtime, ptime, flpins, mflips) 1492 | 1493 | return rcode, Flips( 1494 | event, 1495 | ffi.unpack(rtime, 1)[0], 1496 | ffi.unpack(ptime, 1)[0], 1497 | ffi.unpack(flpins, 1)[0], 1498 | ffi.unpack(mflips, 1)[0], 1499 | ) 1500 | 1501 | 1502 | # int PAPI_flops_rate(int event, float *rtime, float *ptime, long long * flpops, float *mflops); 1503 | @papi_error 1504 | def flops_rate(event): 1505 | """Simplified call to get Mflops/s (floating point operation rate), real 1506 | and processor time. 1507 | 1508 | :param int event: one of the three presets in :doc:`consts`: 1509 | 1510 | * :py:const:`~pypapi.consts.PAPI_FP_OPS`, 1511 | * :py:const:`~pypapi.consts.PAPI_SP_OPS`, 1512 | * :py:const:`~pypapi.consts.PAPI_DP_OPS` 1513 | 1514 | :rtype: structs.Flops 1515 | 1516 | :raises PapiInvalidValueError: The counters were already started by 1517 | something other than :py:func:`flops_rate`. 1518 | :raises PapiNoEventError: The floating point instructions or total cycles 1519 | event does not exist. 1520 | :raises PapiNoMemoryError: Insufficient memory to complete the operation. 1521 | """ 1522 | rtime = ffi.new("float*", 0) 1523 | ptime = ffi.new("float*", 0) 1524 | flpops = ffi.new("long long*", 0) 1525 | mflops = ffi.new("float*", 0) 1526 | 1527 | rcode = lib.PAPI_flops_rate(event, rtime, ptime, flpops, mflops) 1528 | 1529 | return rcode, Flops( 1530 | event, 1531 | ffi.unpack(rtime, 1)[0], 1532 | ffi.unpack(ptime, 1)[0], 1533 | ffi.unpack(flpops, 1)[0], 1534 | ffi.unpack(mflops, 1)[0], 1535 | ) 1536 | 1537 | 1538 | # int PAPI_ipc(float *rtime, float *ptime, long long *ins, float *ipc); 1539 | @papi_error 1540 | def ipc(): 1541 | """Simplified call to get instructions per cycle, real and processor time. 1542 | 1543 | :rtype: structs.IPC 1544 | 1545 | :raises PapiInvalidValueError: The counters were already started by 1546 | something other than :py:func:`ipc`. 1547 | :raises PapiNoEventError: The total instructions or total cycles event does 1548 | not exist. 1549 | :raises PapiNoMemoryError: Insufficient memory to complete the operation. 1550 | """ 1551 | rtime = ffi.new("float*", 0) 1552 | ptime = ffi.new("float*", 0) 1553 | ins = ffi.new("long long*", 0) 1554 | ipc_ = ffi.new("float*", 0) 1555 | 1556 | rcode = lib.PAPI_ipc(rtime, ptime, ins, ipc_) 1557 | 1558 | return rcode, IPC( 1559 | ffi.unpack(rtime, 1)[0], 1560 | ffi.unpack(ptime, 1)[0], 1561 | ffi.unpack(ins, 1)[0], 1562 | ffi.unpack(ipc_, 1)[0], 1563 | ) 1564 | 1565 | 1566 | # int PAPI_epc(int event, float *rtime, float *ptime, long long *ref, 1567 | # long long *core, long long *evt, float *epc); 1568 | @papi_error 1569 | def epc(event=0): 1570 | """Simplified call to get arbitrary events per cycle, real and processor 1571 | time. 1572 | 1573 | :param int event: event code to be measured (from :doc:`events`, default: 1574 | :py:const:`~pypapi.events.PAPI_TOT_INS`). 1575 | 1576 | :rtype: structs.EPC 1577 | 1578 | :raises PapiInvalidValueError: The counters were already started by 1579 | something other than :py:func:`epc`. 1580 | :raises PapiNoEventError: The total instructions or total cycles event does 1581 | not exist. 1582 | :raises PapiNoMemoryError: Insufficient memory to complete the operation. 1583 | """ 1584 | rtime = ffi.new("float*", 0) 1585 | ptime = ffi.new("float*", 0) 1586 | ref = ffi.new("long long*", 0) 1587 | core = ffi.new("long long*", 0) 1588 | evt = ffi.new("long long*", 0) 1589 | epc_ = ffi.new("float*", 0) 1590 | 1591 | rcode = lib.PAPI_epc(event, rtime, ptime, ref, core, evt, epc_) 1592 | 1593 | return rcode, EPC( 1594 | ffi.unpack(rtime, 1)[0], 1595 | ffi.unpack(ptime, 1)[0], 1596 | ffi.unpack(ref, 1)[0], 1597 | ffi.unpack(core, 1)[0], 1598 | ffi.unpack(evt, 1)[0], 1599 | ffi.unpack(epc_, 1)[0], 1600 | ) 1601 | 1602 | 1603 | # int PAPI_rate_stop(); 1604 | def rate_stop(): 1605 | """Stops a running event set of a rate function. 1606 | 1607 | :raises PapiNoEventError: The EventSet is not started yet. 1608 | :raises PapiNoMemoryError: Insufficient memory to complete the operation. 1609 | """ 1610 | rcode = lib.PAPI_rate_stop() 1611 | 1612 | return rcode, None 1613 | -------------------------------------------------------------------------------- /pypapi/structs.py: -------------------------------------------------------------------------------- 1 | from collections import namedtuple 2 | 3 | import ctypes 4 | 5 | from ._papi import ffi 6 | 7 | 8 | class PAPI_Base: 9 | """Base class for PAPI structs.""" 10 | 11 | fields = {} 12 | """Fields of the struct (refer to PAPI's documentation for each field's meaning)""" 13 | s_fields = {} 14 | """Special Fields of the struct (refer to PAPI's documentation for each field's meaning)""" 15 | 16 | def __init__(self, cdata): 17 | for field, f_type in self.fields.items(): 18 | setattr(self, field, self.cdata_to_python(getattr(cdata, field), f_type)) 19 | for field, f_tuple in self.s_fields.items(): 20 | setattr( 21 | self, 22 | field, 23 | self.special_cdata_to_python( 24 | getattr(cdata, field), f_tuple[1], f_tuple[0] 25 | ), 26 | ) 27 | 28 | def __repr__(self): 29 | attr = [f"\t{field}={getattr(self, field)}\n" for field in self.fields] 30 | s_attr = [f"\t{field}={getattr(self, field)}\n" for field in self.s_fields] 31 | return f"{self.__class__.__name__}(\n{''.join(attr + s_attr)})" 32 | 33 | @staticmethod 34 | def cdata_to_python(cdata, data_type): 35 | """Converts C data to Python objects.""" 36 | split = data_type.index(":") 37 | data_type, nested_type = data_type[:split], data_type[split + 1 :] 38 | 39 | if data_type == "num": 40 | return getattr(ctypes, nested_type)(cdata).value 41 | if data_type == "str": 42 | return ffi.string(cdata).decode("ascii") if cdata != ffi.NULL else None 43 | if data_type == "arr": 44 | return [ 45 | PAPI_Base.cdata_to_python(nested_cdata, nested_type) 46 | for nested_cdata in cdata 47 | ] 48 | return None 49 | 50 | def special_cdata_to_python(self, cdata, level, data_type): 51 | """Converts special C data, such as structs, to Python objects.""" 52 | if isinstance(level, str): # dynamic array 53 | return [data_type(cdata[i]) for i in range(getattr(self, level))] 54 | if level == 0: 55 | return data_type(cdata) 56 | 57 | return [ 58 | self.special_cdata_to_python(nested_cdata, level - 1, data_type) 59 | for nested_cdata in cdata 60 | ] 61 | 62 | 63 | class DMEM_info(PAPI_Base): 64 | """Dynamic memory usage info class. Maps to PAPI_dmem_info_t data structure.""" 65 | 66 | fields = { 67 | "peak": "num:c_longlong", 68 | "size": "num:c_longlong", 69 | "resident": "num:c_longlong", 70 | "high_water_mark": "num:c_longlong", 71 | "shared": "num:c_longlong", 72 | "text": "num:c_longlong", 73 | "library": "num:c_longlong", 74 | "heap": "num:c_longlong", 75 | "locked": "num:c_longlong", 76 | "stack": "num:c_longlong", 77 | "pagesize": "num:c_longlong", 78 | "pte": "num:c_longlong", 79 | } 80 | 81 | @classmethod 82 | def alloc_empty(cls): 83 | """Allocate empty PAPI_dmem_info_t using ffi.new""" 84 | return ffi.new("PAPI_dmem_info_t *") 85 | 86 | 87 | class EVENT_info(PAPI_Base): 88 | """Envent info class. Maps to PAPI_event_info_t data structure.""" 89 | 90 | fields = { 91 | "event_code": "num:c_int", 92 | "symbol": "str:", 93 | "short_descr": "str:", 94 | "long_descr": "str:", 95 | "component_index": "num:c_int", 96 | "units": "str:", 97 | "location": "num:c_int", 98 | "data_type": "num:c_int", 99 | "value_type": "num:c_int", 100 | "timescope": "num:c_int", 101 | "update_type": "num:c_int", 102 | "update_freq": "num:c_int", 103 | "count": "num:c_uint", 104 | "event_type": "num:c_uint", 105 | "derived": "str:", 106 | "postfix": "str:", 107 | "code": "arr:num:c_uint", 108 | "name": "arr:str:", 109 | "note": "str:", 110 | } 111 | 112 | @classmethod 113 | def alloc_empty(cls): 114 | """Allocate empty PAPI_event_info_t using ffi.new""" 115 | return ffi.new("PAPI_event_info_t *") 116 | 117 | 118 | class MH_tlb(PAPI_Base): 119 | """Memory Hierarchy TLB info class. Maps to PAPI_mh_tlb_info_t data structure.""" 120 | 121 | fields = { 122 | "type": "num:c_int", 123 | "num_entries": "num:c_int", 124 | "page_size": "num:c_int", 125 | "associativity": "num:c_int", 126 | } 127 | 128 | 129 | class MH_cache(PAPI_Base): 130 | """Memory Hierarchy Cache info class. Maps to PAPI_mh_cache_info_t data structure.""" 131 | 132 | fields = { 133 | "type": "num:c_int", 134 | "size": "num:c_int", 135 | "line_size": "num:c_int", 136 | "num_lines": "num:c_int", 137 | "associativity": "num:c_int", 138 | } 139 | 140 | 141 | class MH_level(PAPI_Base): 142 | """Memory Hierarchy Level info class. Maps to PAPI_mh_level_t data structure.""" 143 | 144 | s_fields = {"tlb": (MH_tlb, 1), "cache": (MH_cache, 1)} 145 | 146 | 147 | class MH_info(PAPI_Base): 148 | """Memory Hierarchy info class. Maps to PAPI_mh_info_t data structure.""" 149 | 150 | fields = {"levels": "num:c_int"} 151 | 152 | s_fields = {"level": (MH_level, 1)} 153 | 154 | 155 | class HARDWARE_info(PAPI_Base): 156 | """Hardware info class. Maps to PAPI_hw_info_t data structure.""" 157 | 158 | fields = { 159 | "ncpu": "num:c_int", 160 | "threads": "num:c_int", 161 | "cores": "num:c_int", 162 | "sockets": "num:c_int", 163 | "nnodes": "num:c_int", 164 | "totalcpus": "num:c_int", 165 | "vendor": "num:c_int", 166 | "vendor_string": "str:", 167 | "model": "num:c_int", 168 | "model_string": "str:", 169 | "revision": "num:c_float", 170 | "cpuid_family": "num:c_int", 171 | "cpuid_model": "num:c_int", 172 | "cpuid_stepping": "num:c_int", 173 | "cpu_max_mhz": "num:c_int", 174 | "cpu_min_mhz": "num:c_int", 175 | "virtualized": "num:c_int", 176 | "virtual_vendor_string": "str:", 177 | "virtual_vendor_version": "str:", 178 | } 179 | 180 | s_field = {"mem_hierarchy": (MH_info, 1)} 181 | 182 | 183 | class ADDR_p(PAPI_Base): 184 | """Address pointer class.""" 185 | 186 | def __init__(self, cdata): 187 | if cdata == ffi.NULL: 188 | self.addr = None 189 | else: 190 | self.addr = int(cdata.__repr__().split()[-1].strip()[:-1], base=16) 191 | 192 | def __repr__(self): 193 | return f"ADDR_p(addr={hex(self.addr) if self.addr is not None else 'NULL'})" 194 | 195 | 196 | class ADDR_map(PAPI_Base): 197 | """Address map class. Maps to PAPI_address_map_t data structure.""" 198 | 199 | fields = {"name": "str:"} 200 | s_fields = { 201 | "text_start": (ADDR_p, 0), 202 | "text_end": (ADDR_p, 0), 203 | "data_start": (ADDR_p, 0), 204 | "data_end": (ADDR_p, 0), 205 | "bss_start": (ADDR_p, 0), 206 | "bss_end": (ADDR_p, 0), 207 | } 208 | 209 | 210 | class EXECUTABLE_info(PAPI_Base): 211 | """Executable info class. Maps to PAPI_exe_info_t data structure.""" 212 | 213 | fields = { 214 | "fullname": "str:", 215 | } 216 | 217 | s_field = {"address_info": (ADDR_map, 0)} 218 | 219 | 220 | class COMPONENT_info(PAPI_Base): 221 | """Component info class. Maps to PAPI_component_info_t data structure.""" 222 | 223 | fields = { 224 | "name": "str:", 225 | "short_name": "str:", 226 | "description": "str:", 227 | "version": "str:", 228 | "support_version": "str:", 229 | "kernel_version": "str:", 230 | "disabled_reason": "str:", 231 | "disabled": "num:c_int", 232 | "CmpIdx": "num:c_int", 233 | "num_cntrs": "num:c_int", 234 | "num_mpx_cntrs": "num:c_int", 235 | "num_preset_events": "num:c_int", 236 | "num_native_events": "num:c_int", 237 | "default_domain": "num:c_int", 238 | "available_domains": "num:c_int", 239 | "default_granularity": "num:c_int", 240 | "available_granularities": "num:c_int", 241 | "hardware_intr_sig": "num:c_int", 242 | "component_type": "num:c_int", 243 | "pmu_names": "arr:str:", 244 | "reserved": "arr:num:c_uint", 245 | "hardware_intr": "num:c_uint", 246 | "precise_intr": "num:c_uint", 247 | "posix1b_timers": "num:c_uint", 248 | "kernel_profile": "num:c_uint", 249 | "kernel_multiplex": "num:c_uint", 250 | "fast_counter_read": "num:c_uint", 251 | "fast_real_timer": "num:c_uint", 252 | "fast_virtual_timer": "num:c_uint", 253 | "attach": "num:c_uint", 254 | "attach_must_ptrace": "num:c_uint", 255 | "cntr_umasks": "num:c_uint", 256 | "cpu": "num:c_uint", 257 | "inherit": "num:c_uint", 258 | "reserved_bits": "num:c_uint", 259 | } 260 | 261 | 262 | class SHARED_LIB_info(PAPI_Base): 263 | """Shared Lib info class. Maps to PAPI_shlib_info_t data structure.""" 264 | 265 | fields = { 266 | "count": "num:c_int", 267 | } 268 | 269 | s_fields = {"map": (ADDR_map, "count")} 270 | 271 | 272 | Flips = namedtuple("Flips", "event rtime ptime flpins mflips") 273 | 274 | Flops = namedtuple("Flops", "event rtime ptime flpops mflops") 275 | 276 | IPC = namedtuple("IPC", "rtime ptime ins ipc") 277 | 278 | EPC = namedtuple("EPC", "rtime ptime ref core evt epc") 279 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | target-version = ['py39'] 3 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp==3.10.11 2 | aiosignal==1.3.1 3 | alabaster==0.7.13 4 | argcomplete==3.2.1 5 | attrs==23.1.0 6 | Babel==2.13.1 7 | black==24.3.0 8 | certifi==2024.7.4 9 | cffi==1.16.0 10 | charset-normalizer==3.3.2 11 | click==8.1.7 12 | colorlog==6.8.0 13 | distlib==0.3.8 14 | docutils==0.20.1 15 | filelock==3.13.1 16 | flake8==6.1.0 17 | frozenlist==1.4.0 18 | idna==3.7 19 | imagesize==1.4.1 20 | Jinja2==3.1.6 21 | MarkupSafe==2.1.3 22 | mccabe==0.7.0 23 | multidict==6.0.4 24 | mypy-extensions==1.0.0 25 | nox==2023.4.22 26 | packaging==23.2 27 | pathspec==0.12.1 28 | platformdirs==4.1.0 29 | pycodestyle==2.11.1 30 | pycparser==2.21 31 | pyflakes==3.1.0 32 | Pygments==2.17.2 33 | requests==2.32.0 34 | snowballstemmer==2.2.0 35 | Sphinx==7.2.6 36 | sphinx-rtd-theme==2.0.0 37 | sphinxcontrib-applehelp==1.0.7 38 | sphinxcontrib-devhelp==1.0.5 39 | sphinxcontrib-htmlhelp==2.0.4 40 | sphinxcontrib-jquery==4.1 41 | sphinxcontrib-jsmath==1.0.1 42 | sphinxcontrib-qthelp==1.0.6 43 | sphinxcontrib-serializinghtml==1.1.9 44 | urllib3==2.2.2 45 | virtualenv==20.26.6 46 | yarl==1.9.4 47 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: UTF-8 3 | 4 | import os 5 | import subprocess 6 | 7 | from setuptools import setup, find_packages 8 | from setuptools.command.build_py import build_py 9 | 10 | 11 | class CustomBuildPy(build_py): 12 | def run(self): 13 | os.environ["CFLAGS"] = "%s -fPIC -Werror=format-truncation=0" % os.environ.get( 14 | "CFLAGS", "" 15 | ) 16 | subprocess.call( 17 | f"cd papi/src/ && ./configure --with-components='{os.environ.get('PAPI_COMPONENTS', '')}' --prefix=$PWD/install", 18 | shell=True, 19 | ) # noqa 20 | subprocess.call("cd papi/src/ && make", shell=True) # noqa 21 | build_py.run(self) 22 | 23 | 24 | long_description = "" 25 | if os.path.isfile("README.rst"): 26 | long_description = open("README.rst", "r").read() 27 | 28 | 29 | setup( 30 | name="python_papi", 31 | version="6.0.0.2", 32 | description="Python binding for the PAPI library", 33 | url="https://github.com/flozz/pypapi", 34 | project_urls={ 35 | "Source Code": "https://github.com/flozz/pypapi", 36 | "Documentation": "https://flozz.github.io/pypapi/", 37 | "Changelog": "https://github.com/flozz/pypapi#changelog", 38 | "Issues": "https://github.com/flozz/pypapi/issues", 39 | "Chat": "https://discord.gg/P77sWhuSs4", 40 | "Donate": "https://github.com/flozz/pypapi#support-this-project", 41 | }, 42 | license="WTFPL", 43 | long_description=long_description, 44 | keywords="papi perf performance", 45 | author="Fabien LOISON, Mathilde BOUTIGNY", 46 | # author_email="", 47 | packages=find_packages(), 48 | setup_requires=["cffi>=1.0.0"], 49 | install_requires=["cffi>=1.0.0"], 50 | extras_require={ 51 | "dev": [ 52 | "nox", 53 | "flake8", 54 | "black", 55 | "sphinx", 56 | "sphinx-rtd-theme", 57 | ] 58 | }, 59 | cffi_modules=["pypapi/papi_build.py:ffibuilder"], 60 | cmdclass={ 61 | "build_py": CustomBuildPy, 62 | }, 63 | ) 64 | -------------------------------------------------------------------------------- /tools/generate_manifest_in.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ## 4 | ## Lists the files to include in sdist distribution. This can be used to 5 | ## generate the MANIFEST.in contents: 6 | ## 7 | ## tools/generate_manifest_in.sh > MANIFEST.in 8 | ## 9 | 10 | cd papi/src > /dev/null 11 | make clean > /dev/null 12 | cd - > /dev/null 13 | 14 | echo "include README.md" 15 | echo "include README.rst" 16 | 17 | echo 18 | 19 | find pypapi -name "*.h" -exec echo "include" "{}" ";" 20 | 21 | echo 22 | 23 | find papi -type f -exec echo "include" "{}" ";" 24 | -------------------------------------------------------------------------------- /tools/generate_papi_events.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | """Generates pypapi/events.py from papiStdEventDefs.h 5 | 6 | USAGE: 7 | tools/generate_papi_events.py > pypapi/events.py 8 | """ 9 | 10 | 11 | import os 12 | import re 13 | 14 | 15 | _ROOT = os.path.abspath(os.path.dirname(__file__)) 16 | _PAPISTDEVENTDEFS_H = os.path.join(_ROOT, "..", "papi", "src", "papiStdEventDefs.h") # noqa 17 | 18 | 19 | def _parse_defines(code): 20 | result = [] 21 | define_regexp = re.compile("^#define\s+([^_][A-Z0-9_]+)\s+([^/]+)\s*(/\*(.+)\*/)?\s*$") # noqa 22 | for line in code.split("\n"): 23 | if define_regexp.match(line): 24 | name = define_regexp.match(line).group(1) 25 | value = define_regexp.match(line).group(2) 26 | comment = define_regexp.match(line).group(4) 27 | result.append([ 28 | name, 29 | value.strip(), 30 | comment.strip() if comment else None 31 | ]) 32 | return result 33 | 34 | 35 | def _parse_enum(code): 36 | result = {} 37 | count = 0 38 | is_enum = False 39 | for line in code.split("\n"): 40 | if not is_enum: 41 | if re.match("(^|\s|;)enum(\{|\s|$)", line): 42 | is_enum = True 43 | continue 44 | if re.match("^\s*(PAPI_[A-Z0-9_]+_idx)", line): 45 | name = re.match("^\s*(PAPI_[A-Z0-9_]+_idx)", line).group(1) 46 | result[name] = count 47 | count += 1 48 | return result 49 | 50 | 51 | def _resolve_defines(defines, enum): 52 | evt_regext = re.compile("^\((PAPI_[A-Z0-9_]+_idx)\s*\|\s*(PAPI_[A-Z0-9_]+_MASK)\)$") # noqa 53 | mask_int_regexp = re.compile("^\(\s*\(\s*int\s*\)\s*([0-9A-Fa-fxX.-]+)\s*\)$") # noqa 54 | for define in defines: 55 | if evt_regext.match(define[1]): 56 | idx = evt_regext.match(define[1]).group(1) 57 | mask = evt_regext.match(define[1]).group(2) 58 | define[1] = "0x%02X | %s" % (enum[idx], mask) 59 | continue 60 | if mask_int_regexp.match(define[1]): 61 | value = mask_int_regexp.match(define[1]).group(1) 62 | define[1] = "%s if not %s & 0x80000000 else %s | ~0x7FFFFFFF" % ( 63 | value, value, value) 64 | 65 | return defines 66 | 67 | 68 | def _defines_to_python(defines): 69 | result = "" 70 | for name, value, doc in defines: 71 | if doc: 72 | result += "#: %s\n" % doc 73 | result += "%s = %s\n\n" % (name, value) 74 | return result 75 | 76 | 77 | if __name__ == "__main__": 78 | code = open(_PAPISTDEVENTDEFS_H, "r").read() 79 | defines = _parse_defines(code) 80 | enum = _parse_enum(code) 81 | _resolve_defines(defines, enum) 82 | python = _defines_to_python(defines) 83 | print("# This file is automatically generated by the") 84 | print("# 'tools/generate_papi_events.py' script.") 85 | print("# DO NOT EDIT!\n") 86 | print("\n# flake8: noqa\n\n") 87 | print(python) 88 | --------------------------------------------------------------------------------