Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [0.1.0] - 2019-11-14

- Initial release.

## [0.2.0] - 2025-3-21

- supported version papi 7.x
- use pkg_config for folder discovery
- static linkinkage - optional with featureflag - static-linkage
- put generated code in propper folders

11 changes: 7 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "papi-sys"
version = "0.1.1"
version = "0.2.0"
authors = ["Clemens Lutz <lutzcle@cml.li>", "Yeonsoo Kim <alkorang@outlook.com>"]

description = "PAPI (Performance API) bindings for Rust"
Expand All @@ -22,7 +22,10 @@ maintenance = { status = "actively-developed" }
[dependencies]

[build-dependencies]
bindgen = "0.50"
bindgen = "0.71"
pkg-config = "0.3.32"

[dev-dependencies]
lazy_static = "1.4.0"

[features]
default = []
static-linkage = []
46 changes: 34 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,44 @@ counters. Visit the [PAPI website](http://icl.utk.edu/papi) for more information

Note that this crate does not provide a high-level interface to PAPI.

## Environment Variables
## Building

There are two environment variables to specify a custom PAPI library path:
- `PAPI_PREFIX`: required to generate `bindings.rs`
- `LD_LIBRARY_PATH`: required to dynamically link `libpapi.so`
tested with papi version 7.2

By default pkg-config is used to detect libraray paths.
Install papi and pkg-config with your distro package manager.

```bash
apt install libpapi-dev pkg-config
```

build and test this library

```bash
$ cargo test
```

# Custom library path

If papi libraray is not installed where pkg-config can discover it automaticaly
pass variable `PKG_CONFIG_PATH` to installed folder accordingly.

For example if you compiled and installed papi libraray as

```bash
$ ./configure --prefix=<INSTALL_DIR> && make && make install
```

You should find pkg-config folder in `<INSTALL_DIR>/lib/pkgconfig`

Let's assume you installed PAPI in `/opt/papi/5.7.0/`, then you can test by
```bash
$ PAPI_PREFIX=/opt/papi/5.7.0/ LD_LIBRARY_PATH=/opt/papi/5.7.0/lib:$LD_LIBRARY_PATH cargo test
$ PKG_CONFIG_PATH=<INSTALL_DIR>/lib/pkgconfig cargo test --features static-linkage
```
Feature flag static-linkage enables static linkage, or else you will need to set
`LD_LIBRARY_PATH` accordingly for each run of binary

To avoid setting `LD_LIBRARY_PATH`, you can configure the search path
globally by running:
```bash
$ sudo echo "/opt/papi/5.7.0/" > /etc/ld.so.conf.d/papi.conf
$ sudo ldconfig
$ PKG_CONFIG_PATH=<INSTALL_DIR>/lib/pkgconfig LD_LIBRARY_PATH=<INSTALL_DIR>/lib/ cargo test
```

## Platforms
Expand All @@ -49,8 +71,8 @@ The following platforms are currently tested:

The following dependency versions are currently required:

* `rustc` >= 1.36
* `gcc` >= 4.8 or `clang` >= 3.8
* `rustc / cargo` >= 1.70
* `clang`

## License

Expand Down
88 changes: 56 additions & 32 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,51 +14,75 @@ use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
use std::process::Command;
use std::process::{Command, Output};
use std::vec::Vec;

fn main() -> std::io::Result<()> {
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
let papi_prefix_path = env::var("PAPI_PREFIX").map(|p| PathBuf::from(p)).ok();

let clang_args = if let Some(p) = papi_prefix_path {
println!("cargo:rustc-link-search={}", p.join("lib").display());
println!("cargo:rust-flags=-L{}", p.join("lib").display());
fn assert_command_ok(output: &Output, error_message: &str) -> std::io::Result<()> {
if !output.status.success() {
let to_str = |s: &[u8]| String::from_utf8_lossy(s).to_string();
panic!(
"{}: {} {}",
error_message,
to_str(&output.stdout),
to_str(&output.stderr)
);
}
Ok(())
}

vec![
format!("-I{}", p.join("include").display()),
format!("-L{}", p.join("lib").display()),
]
} else {
Vec::new()
};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
let library = pkg_config::probe_library("papi")?;
let clang_args = library
.include_paths
.iter()
.map(|path| format!("-I{}", path.to_string_lossy()))
.collect::<Vec<_>>();

#[cfg(feature = "static-linkage")]
println!("cargo:rustc-link-lib=static=papi");
#[cfg(not(feature = "static-linkage"))]
println!("cargo:rustc-link-lib=papi");

bindgen::builder()
.rustfmt_bindings(false)
.header("wrapper.h")
.header("src/wrapper.h")
.clang_args(clang_args.iter())
.whitelist_recursively(false)
.whitelist_type("^PAPI_[[:alpha:]_]+")
.whitelist_type("^_papi_[[:alpha:]_]+")
.whitelist_function("^PAPI_[[:alpha:]_]+")
.whitelist_function("^_papi_[[:alpha:]_]+")
.whitelist_var("^PAPI_[[:alpha:]_]+")
.whitelist_var("^_papi_[[:alpha:]_]+")
.whitelist_type("caddr_t")
.whitelist_type("__caddr_t")
.whitelist_type("_dmem_t")
.whitelist_type("event_info")
.allowlist_recursively(false)
.allowlist_type("^PAPI_[[:alpha:]_]+")
.allowlist_type("^_papi_[[:alpha:]_]+")
.allowlist_function("^PAPI_[[:alpha:]_]+")
.allowlist_function("^_papi_[[:alpha:]_]+")
.allowlist_var("^PAPI_[[:alpha:]_]+")
.allowlist_var("^_papi_[[:alpha:]_]+")
.allowlist_type("caddr_t")
.allowlist_type("__caddr_t")
.allowlist_type("_dmem_t")
.allowlist_type("event_info")
.allowlist_type("vptr_t")
.generate()
.expect("Unable to generate PAPI bindings")
.write_to_file(out_path.join("bindings.rs"))
.expect("Unable to write PAPI bindings");

let codegen_stdout = Command::new("sh")
.arg("codegen.sh")
// generate trivial binary that exposes versions of PAPI
// use clang to generate codegen
let codegen_binary_path = out_path.join("codegen");
let codegen = Command::new("clang")
.args(clang_args.iter())
.args(["-o", codegen_binary_path.to_str().unwrap()])
.arg("src/codegen.c")
.output()
.unwrap()
.stdout;
.expect("failed to execute process");
assert_command_ok(&codegen, "Codegen failed")?;

// run codegen and fetch output
let codegen_run = Command::new(codegen_binary_path)
.output()
.expect("failed to execute process");
assert_command_ok(&codegen_run, "Codegen run failed")?;

let codegen_stdout = codegen_run.stdout;

let mut file = File::create(out_path.join("codegen.rs"))?;
file.write_all(&codegen_stdout)?;
file.sync_all()?;
Expand Down
11 changes: 0 additions & 11 deletions codegen.sh

This file was deleted.

File renamed without changes.
49 changes: 30 additions & 19 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// Authors:
// Clemens Lutz <clemens.lutz@dfki.de>
// Yeonsoo Kim <alkorang@outlook.com>
// Luka Rahne https://github.com/ra1u
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
Expand All @@ -19,36 +20,46 @@ include!(concat!(env!("OUT_DIR"), "/codegen.rs"));

#[cfg(test)]
mod tests {
use lazy_static::lazy_static;

lazy_static! {
static ref IS_PAPI_INITED: bool = {
do_papi_init();
true
};
}

use super::*;
use std::sync::OnceLock;
static PAPI_INIT: OnceLock<i32> = OnceLock::new();

fn do_papi_init() {
unsafe {
let ver = PAPI_library_init(PAPI_VER_CURRENT);
assert_eq!(ver, PAPI_VER_CURRENT);
}

let is_inited = unsafe { PAPI_is_initialized() };
assert_ne!(is_inited, PAPI_NOT_INITED as i32);
fn do_papi_init() {
// init only once
// PAPI_init is not thread safe, so we use OnceLock
// to PAPI_library_init is only called once in each test
let ver = *PAPI_INIT.get_or_init(|| {
unsafe { PAPI_library_init(PAPI_VER_CURRENT) }
});
assert_eq!(ver, PAPI_VER_CURRENT);
let initialised = unsafe { PAPI_is_initialized() };
assert_ne!(initialised, PAPI_NOT_INITED as i32);
}

#[test]
fn get_real_cyc() {
do_papi_init();
let cycles = unsafe { PAPI_get_real_cyc() };
assert!(cycles >= 0);
}

#[test]
fn get_num_counters() {
let num_hwcntrs = unsafe { PAPI_num_counters() };
assert!(num_hwcntrs >= 0);
do_papi_init();
unsafe {
let num_hwcntrs = PAPI_get_opt(PAPI_MAX_HWCTRS as i32, std::ptr::null_mut());
assert!(num_hwcntrs >= 0);
}
}

#[test]
fn hw_info() {
do_papi_init();
let hw_info = unsafe {
let hwi = PAPI_get_hardware_info();
assert!(!hwi.is_null());
&*hwi
};
assert!(hw_info.totalcpus >= 1);
}
}
File renamed without changes.