diff --git a/CHANGELOG.md b/CHANGELOG.md index a51ba52..61d2cf2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 + \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 797d585..21f086c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "papi-sys" -version = "0.1.1" +version = "0.2.0" authors = ["Clemens Lutz ", "Yeonsoo Kim "] description = "PAPI (Performance API) bindings for Rust" @@ -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 = [] \ No newline at end of file diff --git a/README.md b/README.md index 69aee2e..15626c5 100644 --- a/README.md +++ b/README.md @@ -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= && make && make install +``` + +You should find pkg-config folder in `/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=/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=/lib/pkgconfig LD_LIBRARY_PATH=/lib/ cargo test ``` ## Platforms @@ -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 diff --git a/build.rs b/build.rs index 899eb04..f71b384 100644 --- a/build.rs +++ b/build.rs @@ -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> { + 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::>(); + #[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()?; diff --git a/codegen.sh b/codegen.sh deleted file mode 100644 index 2f6cb3b..0000000 --- a/codegen.sh +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2019 Yeonsoo Kim -# Author: Yeonsoo Kim -# -# Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -# copied, modified, or distributed except according to those terms. - -gcc -I$PAPI_PREFIX/include -L$PAPI_PREFIX/lib -o codegen codegen.c -./codegen -rm -f codegen diff --git a/codegen.c b/src/codegen.c similarity index 100% rename from codegen.c rename to src/codegen.c diff --git a/src/lib.rs b/src/lib.rs index 2cf0ef5..3555fc0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ // Authors: // Clemens Lutz // Yeonsoo Kim +// Luka Rahne https://github.com/ra1u // // Licensed under the Apache License, Version 2.0, or the MIT license = 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); } } diff --git a/wrapper.h b/src/wrapper.h similarity index 100% rename from wrapper.h rename to src/wrapper.h