Add rust bindings for amdsmi c interface (#14)

It consists of two main steps:

1. Generating Bindings with `bindgen`:
- The `build.rs` script uses `bindgen` to generate Rust FFI (Foreign
Function Interface) bindings for the AMD SMI C library. This step
automatically exports all enums, structs, unions, and unsafe functions
from the C library into Rust. This provides a comprehensive low-level
interface to the AMD SMI library.

2. Implementing Safe Rust Wrappers:
- The generated bindings are then wrapped in safe Rust functions. These
safe wrappers handle error checking, resource management, and provide a
more idiomatic Rust interface. This ensures that users of the library
can interact with the AMD SMI functions without dealing with unsafe code
directly.

Change-Id: I7d5e49e59826164fc911ced04ef7ca5706b7cc05

Signed-off-by: Tim Huang <tim.huang@amd.com>

[ROCm/amdsmi commit: d32f2a109a]
Tento commit je obsažen v:
Huang, Tim
2025-01-08 07:19:46 +08:00
odevzdal GitHub
rodič a1d60ef088
revize 5e82aac4f8
19 změnil soubory, kde provedl 11542 přidání a 0 odebrání
+4
Zobrazit soubor
@@ -76,6 +76,7 @@ include(CMakeDependentOption)
# these options don't work without BUILD_SHARED_LIBS
cmake_dependent_option(BUILD_WRAPPER "Rebuild AMDSMI-wrapper" OFF "BUILD_SHARED_LIBS" OFF)
cmake_dependent_option(BUILD_CLI "Build AMDSMI-CLI and install" ON "BUILD_SHARED_LIBS" OFF)
cmake_dependent_option(BUILD_RUST_WRAPPER "Build rust wrapper and install" OFF "BUILD_SHARED_LIBS" OFF)
cmake_dependent_option(ENABLE_LDCONFIG "Set library links and caches using ldconfig." ON "BUILD_SHARED_LIBS" OFF)
# Set share path here because project name != amd_smi
@@ -193,6 +194,9 @@ if(BUILD_SHARED_LIBS)
if(BUILD_CLI)
add_subdirectory("amdsmi_cli")
endif()
if(BUILD_RUST_WRAPPER)
add_subdirectory("rust-interface")
endif()
endif()
include(CMakePackageConfigHelpers)
+4
Zobrazit soubor
@@ -0,0 +1,4 @@
# Auto generated by Carge
target
build
*.lock
+124
Zobrazit soubor
@@ -0,0 +1,124 @@
# Generate rust-wrapper and package targets
# This string is the installation directory containing all rust files
set(RUST_WRAPPER_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/rust-wrapper" CACHE STRING "Rust wrapper installation directory")
# Optional to build examples
option(BUILD_RUST_EXAMPLES "Build rust-interface examples" OFF)
# Optional to regenerate the Rust wrapper amdsmi_wrapper.rs
option(REGENERATE_RUST_WRAPPER "Re-generate AMD SMI Rust wrapper file" OFF)
# Determine the Cargo build type based on the CMake build type
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CARGO_BUILD_TYPE debug)
set(CARGO_BUILD_TYPE_ARG) # Default is debug for Cargo, no argument "--debug"
else()
set(CARGO_BUILD_TYPE release)
set(CARGO_BUILD_TYPE_ARG --release)
endif()
set(RUST_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/target)
set(RUST_PACKAGE_SROUCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/source)
set(RUST_OUTPUT_DIR ${RUST_BUILD_DIR}/${CARGO_BUILD_TYPE})
if(REGENERATE_RUST_WRAPPER)
set(AMDSMI_GENERATE_RUST_WRAPPER "AMDSMI_GENERATE_RUST_WRAPPER=1")
else()
set(AMDSMI_GENERATE_RUST_WRAPPER "")
endif()
# Create source tar.gz archive
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/source.tar.gz
COMMAND ${CMAKE_COMMAND} -E make_directory ${RUST_PACKAGE_SROUCE_DIR}
COMMAND ${CMAKE_COMMAND} -E make_directory ${RUST_PACKAGE_SROUCE_DIR}/include/amd_smi
COMMAND ${CMAKE_COMMAND} -E make_directory ${RUST_PACKAGE_SROUCE_DIR}/lib
COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/include/amd_smi/amdsmi.h ${RUST_PACKAGE_SROUCE_DIR}/include/amd_smi/
COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/src/libamd_smi.so ${RUST_PACKAGE_SROUCE_DIR}/lib/
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/build.rs ${RUST_PACKAGE_SROUCE_DIR}/
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/callbacks.rs ${RUST_PACKAGE_SROUCE_DIR}/
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/Cargo.toml ${RUST_PACKAGE_SROUCE_DIR}/
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/README.md ${RUST_PACKAGE_SROUCE_DIR}/
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/src ${RUST_PACKAGE_SROUCE_DIR}/src
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/examples ${RUST_PACKAGE_SROUCE_DIR}/examples
COMMAND ${CMAKE_COMMAND} -E tar cfz ${CMAKE_CURRENT_BINARY_DIR}/source.tar.gz --format=gnutar ${RUST_PACKAGE_SROUCE_DIR}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS ${AMD_SMI}
${CMAKE_CURRENT_SOURCE_DIR}/build.rs
${CMAKE_CURRENT_SOURCE_DIR}/callbacks.rs
${CMAKE_CURRENT_SOURCE_DIR}/Cargo.toml
${CMAKE_CURRENT_SOURCE_DIR}/README.md
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/examples
COMMENT "Creating AMD SMI Rust wrapper source tar.gz archive"
)
add_custom_target(amdsmi_rust_source ALL
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/source.tar.gz
)
find_program(CARGO_EXECUTABLE NAMES cargo)
if(NOT CARGO_EXECUTABLE)
message(STATUS "Cargo not found. Installing Rust and Cargo...")
execute_process(
COMMAND /bin/sh -c "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y"
RESULT_VARIABLE result
)
if(result)
message(FATAL_ERROR "Failed to install Rust and Cargo.")
endif()
# Add Cargo to the PATH
set(CARGO_EXECUTABLE "$ENV{HOME}/.cargo/bin/cargo")
endif()
# Determine the Cargo build command
set(CARGO_BUILD_COMMAND ${CARGO_EXECUTABLE} build ${CARGO_BUILD_TYPE_ARG} --target-dir ${RUST_BUILD_DIR})
# Use make amdsmi_rust to build the library
add_custom_target(amdsmi_rust
COMMAND ${CMAKE_COMMAND} -E env ${AMDSMI_GENERATE_RUST_WRAPPER} AMDSMI_LIB_DIR=${PROJECT_BINARY_DIR}/src ${CARGO_BUILD_COMMAND}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS ${AMD_SMI}
COMMENT "Build the AMD SMI Rust library"
)
if(BUILD_RUST_EXAMPLES)
# Determine the build command for examples if enabled
set(CARGO_BUILD_EXAMPLES_COMMAND ${CARGO_EXECUTABLE} build ${CARGO_BUILD_TYPE_ARG} --target-dir ${RUST_BUILD_DIR} --examples)
# Define the build command for the Rust examples
add_custom_command(
OUTPUT ${RUST_OUTPUT_DIR}/examples/amdsmi_get_gpu_info
${RUST_OUTPUT_DIR}/examples/amdsmi_exporter
COMMAND ${CMAKE_COMMAND} -E env AMDSMI_LIB_DIR=${PROJECT_BINARY_DIR}/src ${CARGO_BUILD_COMMAND}
COMMAND ${CMAKE_COMMAND} -E env AMDSMI_LIB_DIR=${PROJECT_BINARY_DIR}/src ${CARGO_BUILD_EXAMPLES_COMMAND}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Building AMD SMI Rust examples"
)
add_custom_target(rust_examples ALL
DEPENDS ${RUST_BUILD_DIR}/${CARGO_BUILD_TYPE}/examples/amdsmi_get_gpu_info
${RUST_OUTPUT_DIR}/examples/amdsmi_exporter
)
endif()
# Deploy the Rust interface with source code only, as using static or dynamic library binaries is not recommended by the Rust ecosystem.
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/source.tar.gz
DESTINATION ${RUST_WRAPPER_INSTALL_DIR}
COMPONENT dev)
# Deploy rust-interface examples
if(BUILD_RUST_EXAMPLES)
install(DIRECTORY DESTINATION ${RUST_WRAPPER_INSTALL_DIR}/examples COMPONENT dev)
install(
FILES ${RUST_OUTPUT_DIR}/examples/amdsmi_get_gpu_info
${RUST_OUTPUT_DIR}/examples/amdsmi_exporter
DESTINATION ${RUST_WRAPPER_INSTALL_DIR}/examples
PERMISSIONS OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ
COMPONENT dev)
endif()
+31
Zobrazit soubor
@@ -0,0 +1,31 @@
[package]
name = "amdsmi"
version = "0.1.1"
edition = "2021"
description = "A rust binding for the AMD System Management Interface (AMD-SMI) library"
repository = "https://github.com/ROCm/amdsmi/rust-interface"
readme = "README.md"
keywords = ["amdsmi", "rust binding"]
license = "Copyright (c) 2019-2024 Advanced Micro Devices, Inc"
[build-dependencies]
bindgen = "0.70.1"
[lib]
name = "amdsmi"
path = "src/lib.rs"
[[example]]
name = "amdsmi_get_gpu_info"
crate-type = ["bin"]
[[example]]
name = "amdsmi_exporter"
crate-type = ["bin"]
[dev-dependencies]
prometheus-client = "0.22.3"
axum = "0.5"
hyper = "0.14"
clap = "3.0"
tokio = { version = "1", features = ["full"] }
+246
Zobrazit soubor
@@ -0,0 +1,246 @@
# AMD SMI Rust Binding
This rust crate provides Rust bindings for the AMD System Management Interface (AMD-SMI) library. It allows you to interact with AMD GPUs and retrieve various information using Rust.
## Table of Contents
- [Overview](#overview)
- [Hello World Example](#hello-world-example)
- [Directory Structure](#directory-structure)
- [Building](#building)
- [Running Tests](#running-tests)
- [Running Examples](#running-examples)
- [Generating Documentation](#generating-documentation)
- [Adding New API and Tests](#adding-new-api-and-tests)
## Overview
The AMD SMI Rust binding crate automates the generation of bindings and ensures safety, maintainability, and ease of use. The implementation consists of two main steps:
1. **Generating Bindings with `bindgen`**:
- The `build.rs` script uses `bindgen` to automatically generate Rust FFI (Foreign Function Interface) bindings for the AMD SMI C library. This step exports all enums, structs, unions, and unsafe functions from the C library into Rust, providing a comprehensive low-level interface to the AMD SMI library.
2. **Implementing Safe Rust Wrappers**:
- The generated bindings are then wrapped in safe Rust functions. These safe wrappers handle error checking, resource management, and provide a more idiomatic Rust interface. This ensures that users of the library can interact with the AMD SMI functions without dealing with unsafe code directly.
## Hello World Example
Here is a simple "Hello World" example to get you started with the AMD SMI Rust bindings. This example initializes the AMD SMI library, retrieves the GPU information, and prints it to the console.
```
use amdsmi::*;
fn main() {
// Initialize the AMD SMI library
if let Err(e) = amdsmi_init(AmdsmiInitFlagsT::AmdsmiInitAmdGpus) {
eprintln!("Failed to initialize AMD SMI: {}", e);
return;
}
// Get socket handles
let socket_handles = match amdsmi_get_socket_handles() {
Ok(handles) => handles,
Err(e) => {
eprintln!("Failed to get socket handles: {}", e);
amdsmi_shut_down().expect("Failed to shutdown AMD SMI");
return;
}
};
for socket_handle in socket_handles {
// Get processor handles for each socket handle
let processor_handles = match amdsmi_get_processor_handles(socket_handle) {
Ok(handles) => handles,
Err(e) => {
eprintln!(
"Failed to get processor handles for socket {:?}: {}",
socket_handle, e
);
continue;
}
};
for processor_handle in processor_handles {
// Get GPU ID using the processor handle
match amdsmi_get_gpu_id(processor_handle) {
Ok(gpu_id) => println!("GPU ID: {}", gpu_id),
Err(e) => eprintln!("Failed to get GPU ID: {}", e),
}
// Get GPU revision using the processor handle
match amdsmi_get_gpu_revision(processor_handle) {
Ok(gpu_revision) => println!("GPU Revision: {}", gpu_revision),
Err(e) => eprintln!("Failed to get GPU revision: {}", e),
}
// Get GPU vendor name using the processor handle
match amdsmi_get_gpu_vendor_name(processor_handle) {
Ok(gpu_vendor_name) => println!("GPU Vendor Name: {}", gpu_vendor_name),
Err(e) => eprintln!("Failed to get GPU vendor name: {}", e),
}
// Get GPU VRAM vendor using the processor handle
match amdsmi_get_gpu_vram_vendor(processor_handle) {
Ok(gpu_vram_vendor) => println!("GPU VRAM Vendor: {}", gpu_vram_vendor),
Err(e) => eprintln!("Failed to get GPU VRAM vendor: {}", e),
}
// Get GPU BDF using the processor handle
match amdsmi_get_gpu_device_bdf(processor_handle) {
Ok(gpu_bdf) => println!("GPU BDF: {}", gpu_bdf),
Err(e) => eprintln!("Failed to get GPU BDF: {}", e),
}
println!();
}
}
// Shutdown the AMD SMI library
if let Err(e) = amdsmi_shut_down() {
eprintln!("Failed to shutdown AMD SMI: {}", e);
}
}
```
## Directory Structure
```
amd-smi/rust-interface
├── Cargo.toml # Cargo configuration file
├── CMakeLists.txt # CMake configuration file for building the AMD SMI library
├── build.rs # Build script for generating bindings
├── src/
│ ├── lib.rs # Library entry point
│ ├── amdsmi.rs # Main module for AMD SMI bindings
│ ├── utils.rs # Utility functions and helpers
│ └── amdsmi_wrapper.rs # Automatically generate FFI bindings by `bindgen`
└── examples # Example programs demonstrating usage
```
## Building
To build this Rust binding, you need to have Rust and Cargo installed.
### Option 1: Using CMake
Navigate to the project's root directory and use the project's CMake build system. You need to define the `BUILD_RUST_WRAPPER` option. This will build this Rust interface and integrate it with the rest of the project.
```sh
mkdir build
cd build
cmake -DBUILD_RUST_WRAPPER=ON ..
make
sudo make install
```
### Option 2: Using Cargo
Alternatively, you can navigate to the `rust-interface` folder and build the project using Cargo.
```sh
cd rust-interface
cargo build
```
## Running Tests
To run the tests, use the following command:
```sh
cargo test --doc -- --show-output --test-threads=1
```
This command will execute all the unit tests that are defined in the API's example code within the API's documentation comments.
## Running Examples
To run the examples, use the following command:
```sh
cargo run --example example_name
```
Replace `example_name` with the name of the example you want to run. For example, if you have an example named `get_gpu_info`, you can run it as follows:
```sh
cargo run --example get_gpu_info
```
## Generating Documentation
To generate the documentation for the project, use the following command:
```sh
cargo doc --open
```
This will generate the documentation and open it in your default web browser.
## Adding New API and Tests
### Adding New API
1. **Update Bindings**:
- Rebuilding this Rust library will automatically generate the bindings using `bindgen` according to the latest `amdsmi.h`.
2. **Define the API**:
- Add the new API function in the appropriate Rust file, e.g., `src/amdsmi.rs`.
- Implement the function to use the `call_unsafe!` macro to call the `bindgen` generated unsafe rust FFI function in `amdsmi_wrapper.rs`.
3. **Document the API**:
- Use Rust doc comments `///` to document the new API function. Include a description, parameters, return values, and examples.
### Adding Tests
Include examples in the documentation comments of your functions. These examples will be compiled and run as tests when you run `cargo test`.
### Example of New API
Here is an example of adding a new API function and documenting it with a doc test:
```rust
/// Retrieves the GPU ID for a given processor handle.
///
/// This function returns the GPU ID associated with the specified processor handle.
/// The GPU ID is a unique identifier for the GPU.
///
/// # Arguments
///
/// * `processor_handle` - A handle to the processor for which the GPU ID is being queried.
///
/// # Returns
///
/// * `AmdsmiResult<u32>` - Returns `Ok(u32)` containing the GPU ID if successful, or an error if it fails.
///
/// # Example
///
/// ```rust
/// # use amdsmi::*;
/// #
/// # fn main() {
/// # // Initialize the AMD SMI library
/// # amdsmi_init(AmdsmiInitFlagsT::AmdsmiInitAmdGpus).expect("Failed to initialize AMD SMI");
/// #
/// // Example processor_handle, assuming the number of processors is greater than zero
/// let processor_handle = amdsmi_get_processor_handles!()[0];
///
/// // Retrieve the GPU ID
/// match amdsmi_get_gpu_id(processor_handle) {
/// Ok(gpu_id) => println!("GPU ID: {}", gpu_id),
/// Err(e) => panic!("Failed to get GPU ID: {}", e),
/// }
/// #
/// # // Shut down the AMD SMI library
/// # amdsmi_shut_down().expect("Failed to shut down AMD SMI");
/// # }
/// ```
///
/// # Errors
///
/// This function will return the error in [`AmdsmiStatusT`] if the underlying `amdsmi_wrapper::amdsmi_get_gpu_id` call fails.
pub fn amdsmi_get_gpu_id(processor_handle: AmdsmiProcessorHandle) -> AmdsmiResult<u16> {
let mut id: u16 = 0;
call_unsafe!(amdsmi_wrapper::amdsmi_get_gpu_id(processor_handle, &mut id));
Ok(id)
}
```
+188
Zobrazit soubor
@@ -0,0 +1,188 @@
// Copyright (C) 2024 Advanced Micro Devices. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
extern crate bindgen;
use std::env;
use std::fs;
use std::io::Result;
use std::path::{Path, PathBuf};
mod callbacks;
fn get_rocm_dir() -> Option<PathBuf> {
// Look for the latest folder in /opt that begins with "rocm"
let opt_path = Path::new("/opt");
if let Ok(entries) = fs::read_dir(opt_path) {
let mut rocm_dirs: Vec<PathBuf> = entries
.filter_map(|entry| entry.ok())
.map(|entry| entry.path())
.filter(|path| {
path.is_dir()
&& path
.file_name()
.unwrap_or_default()
.to_string_lossy()
.starts_with("rocm")
})
.collect();
// Sort the directories by name and get the latest one
rocm_dirs.sort();
if let Some(latest_rocm_dir) = rocm_dirs.last() {
return Some(latest_rocm_dir.clone());
}
}
None
}
fn get_amdsmi_lib_dir() -> Result<String> {
let amdsmi_file = "libamd_smi.so";
// Check the environment variable AMDSMI_LIB_DIR to get the library directory
if let Ok(lib_dir) = env::var("AMDSMI_LIB_DIR") {
if PathBuf::from(lib_dir.clone()).join(amdsmi_file).exists() {
return Ok(lib_dir);
}
}
// Check the current directory
if let Ok(current_dir) = env::current_dir() {
let current_lib_dir = current_dir.join("lib");
if current_lib_dir.join(amdsmi_file).exists() {
return Ok(current_lib_dir.to_string_lossy().into_owned());
}
}
// Check the ROCm directory
if let Some(lib_dir) = get_rocm_dir() {
if lib_dir.join("lib").join(amdsmi_file).exists() {
return Ok(lib_dir.join("lib").to_string_lossy().into_owned());
}
}
Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"The libamd_smi.so library was not found. Please set the AMDSMI_LIB_DIR environment variable to the directory containing the library.",
))
}
fn get_amdsmi_header_file() -> Result<String> {
let amdsmi_header_path = "include/amd_smi/amdsmi.h";
// Use the project's header as the first priority
let default_path = PathBuf::from("../").join(amdsmi_header_path);
if default_path.exists() {
return Ok(default_path.to_string_lossy().into_owned());
}
// Check the current directory
if let Ok(current_dir) = env::current_dir() {
let fallback_path = current_dir.join(amdsmi_header_path);
if fallback_path.exists() {
return Ok(fallback_path.to_string_lossy().into_owned());
}
}
// Check the ROCm directory
if let Some(rocm_dir) = get_rocm_dir() {
let fallback_path = rocm_dir.join(amdsmi_header_path);
if fallback_path.exists() {
return Ok(fallback_path.to_string_lossy().into_owned());
}
}
Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"The amdsmi.h header file was not found",
))
}
fn generate_amdsmi_wrapper(amdsmi_header_file: &str) {
let bindings = bindgen::Builder::default()
.header(amdsmi_header_file)
.generate_comments(false)
.prepend_enum_name(false)
.rustified_enum("^(.*)$")
.allowlist_type("^(amdsmi.*)$")
.allowlist_function("^(amdsmi.*)$")
.allowlist_var("^(AMDSMI.*)$")
.parse_callbacks(Box::new(callbacks::UpperCamelCaseCallbacks))
.raw_line("// Copyright (C) 2024 Advanced Micro Devices. All rights reserved.")
.raw_line("//")
.raw_line(
"// Permission is hereby granted, free of charge, to any person obtaining a copy of",
)
.raw_line(
"// this software and associated documentation files (the \"Software\"), to deal in",
)
.raw_line("// the Software without restriction, including without limitation the rights to")
.raw_line(
"// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of",
)
.raw_line(
"// the Software, and to permit persons to whom the Software is furnished to do so,",
)
.raw_line("// subject to the following conditions:")
.raw_line("//")
.raw_line(
"// The above copyright notice and this permission notice shall be included in all",
)
.raw_line("// copies or substantial portions of the Software.")
.raw_line("//")
.raw_line("// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR")
.raw_line(
"// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS",
)
.raw_line(
"// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR",
)
.raw_line(
"// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER",
)
.raw_line("// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN")
.raw_line("// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.")
.raw_line("")
.raw_line("#![allow(non_upper_case_globals)]")
.generate()
.expect("Unable to binding wrapper for amdsmi C interface!");
let bindings_path = PathBuf::from("./src/amdsmi_wrapper.rs");
bindings
.write_to_file(&bindings_path)
.expect("Couldn't write binding wrapper for amdsmi C interface!");
println!("Wrapper generated at: {:?}", bindings_path);
}
fn main() {
// Get the amd_smi library directory
let amdsmi_lib_dir = get_amdsmi_lib_dir().expect("Failed to get the amd_smi library path");
// Tell cargo to tell rustc to link the AMD-SMI library
println!("cargo:rustc-link-lib=amd_smi");
println!("cargo:rustc-link-search=native={}", amdsmi_lib_dir);
let generate_wrapper = env::var("AMDSMI_GENERATE_RUST_WRAPPER").is_ok();
if generate_wrapper {
// Get the amdsmi.h header file path
let amdsmi_header_file = get_amdsmi_header_file().expect("Failed to get the amd_smi header file");
// Generate the amdsmi wrapper
generate_amdsmi_wrapper(&amdsmi_header_file);
}
}
+71
Zobrazit soubor
@@ -0,0 +1,71 @@
// Copyright (C) 2024 Advanced Micro Devices. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
use bindgen::callbacks::EnumVariantValue;
#[derive(Debug)]
pub struct UpperCamelCaseCallbacks;
impl bindgen::callbacks::ParseCallbacks for UpperCamelCaseCallbacks {
fn item_name(&self, original_item_name: &str) -> Option<String> {
if original_item_name.starts_with("amdsmi")
&& (original_item_name.ends_with("_t") || original_item_name.contains("_t_"))
{
Some(convert_to_upper_camel_case(original_item_name))
} else {
match original_item_name {
"amdsmi_processor_handle"
| "amdsmi_socket_handle"
| "processor_type_t"
| "amd_metrics_table_header_t" => {
Some(convert_to_upper_camel_case(original_item_name))
}
_ => None,
}
}
}
fn enum_variant_name(
&self,
_enum_name: Option<&str>,
original_variant_name: &str,
_variant_value: EnumVariantValue,
) -> Option<String> {
Some(convert_to_upper_camel_case(original_variant_name))
}
}
fn convert_to_upper_camel_case(s: &str) -> String {
let mut result = String::new();
let mut capitalize_next = true;
for c in s.chars() {
if c == '_' {
capitalize_next = true;
} else if capitalize_next {
result.push(c.to_ascii_uppercase());
capitalize_next = false;
} else {
result.push(c.to_ascii_lowercase());
}
}
result
}
+96
Zobrazit soubor
@@ -0,0 +1,96 @@
# AMDSMI Exporter
The `AMDSMI Exporter` is a tool that exports AMD SMI metrics and runs an HTTP server to serve these metrics. It provides options to list supported GPUs and metrics' information, as well as to export metrics with optional filtering using whitelist and blacklist. This enables fine-grained control over which metrics are included or excluded.
## Features
- List supported GPUs and detailed metrics information.
- Export AMD SMI metrics and serve them via an HTTP server.
- Filter metrics using whitelist and blacklist.
- Filter metrics by GPU BDF or ID.
## Usage
### List Supported GPUs and Metrics
To list the supported GPUs and metrics' information, use the `list` subcommand:
```sh
amdsmi_exporter list
```
### Export Metrics and Run HTTP Server
To export AMD SMI metrics and run the HTTP server, use the `metric` subcommand:
```sh
amdsmi_exporter metric [OPTIONS]
```
#### Options
- `-p, --port <PORT>`: Sets the port to listen on (default: 9898).
- `-g, --gpu <GPU>`: Sets the GPU BDF or the zero-based index of the socket ID and processor ID for export.
Example:
- `--gpu 0000:03:00.0`
- `--gpu 0.0`
- `-w, --whitelist <METRIC>`: Comma-separated list of metric field names to include.
- `-b, --blacklist <METRIC>`: Comma-separated list of metric field names to exclude.
### Examples
#### List Supported GPUs and Metrics
```sh
amdsmi_exporter list
```
#### Export Metrics and Run HTTP Server on Port 8080 for a Specific GPU
```sh
amdsmi_exporter metric --port 8080 --gpu 0000:03:00.0
```
#### Export Metrics with Whitelist Filter
```sh
amdsmi_exporter metric --whitelist AMDSMI_FI_GFX_ACTIVITY,AMDSMI_FI_UMC_ACTIVITY
```
#### Export Metrics with Blacklist Filter
```sh
amdsmi_exporter metric --blacklist AMDSMI_FI_GFX_VOLTAGE,AMDSMI_FI_SOC_VOLTAGE
```
### Set Up Prometheus Docker Server to Connect to This Client Exporter
To set up a Prometheus Docker server to connect to this client exporter, follow these steps:
1. **Create a Prometheus Configuration File**
Create a file named prometheus.yml with the following content:
```yaml
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'amdsmi_exporter'
static_configs:
- targets: ['localhost:9898']
```
Replace `localhost:9898` with the appropriate address and port if you are running the exporter on a different host or port.
2. **Run Prometheus Docker Container**
Run the Prometheus Docker container with the configuration file:
```sh
docker run -d --name=prometheus -p 9090:9090 -v $(pwd)/prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus
```
3. **Access Prometheus Web Interface**
Open your web browser and go to `http://localhost:9090` to access the Prometheus web interface. You should see the `amdsmi_exporter` job listed under the "Targets" section.
@@ -0,0 +1,527 @@
// Copyright (C) 2024 Advanced Micro Devices. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
use crate::amdsmi_metric::*;
use crate::collectors::collectors_create;
use amdsmi::*;
use prometheus_client::metrics::family::Family;
use prometheus_client::metrics::gauge::Gauge;
use prometheus_client::registry::Registry;
use std::collections::HashMap;
use std::fmt::Debug;
use std::fmt::Display;
pub struct AmdsmiCollectors {
pub collectors: HashMap<&'static str, AmdsmiCollector>,
pub gpu_filter: Option<Vec<String>>,
pub whitelist: Option<Vec<String>>,
pub blacklist: Option<Vec<String>>,
}
impl Default for AmdsmiCollectors {
fn default() -> Self {
AmdsmiCollectors::new()
}
}
impl AmdsmiCollectors {
pub fn new() -> Self {
let mut instance = AmdsmiCollectors {
collectors: HashMap::new(),
gpu_filter: None,
whitelist: None,
blacklist: None,
};
collectors_create(&mut instance);
instance
}
pub fn set_gpu_filter(&mut self, filters: Option<Vec<String>>) -> &mut Self {
self.gpu_filter = filters;
self
}
pub fn set_field_filter(
&mut self,
whitelist: Option<Vec<String>>,
blacklist: Option<Vec<String>>,
) -> &mut Self {
self.whitelist = whitelist;
self.blacklist = blacklist;
// Re-generate the collectors after setting the field filters
self.collectors.clear();
collectors_create(self);
self
}
pub fn add_metric<T, F>(&mut self, collector: AmdsmiGauge<T, F>) -> &mut Self
where
T: 'static + Into<i64> + Display + Copy,
F: 'static + Fn(AmdsmiProcessorHandle) -> AmdsmiResult<T> + Send + Sync,
{
let name = collector.info.name;
let description = collector.info.description;
let unit = collector.info.unit;
let func = collector.func;
// Apply whitelist and blacklist filters before inserting the collector
if let Some(ref whitelist) = self.whitelist {
if !whitelist.contains(&name.to_string()) {
return self;
}
}
if let Some(ref blacklist) = self.blacklist {
if blacklist.contains(&name.to_string()) {
return self;
}
}
self.collectors.insert(
name,
Box::new(move |processor_handle| match func(processor_handle) {
Ok(value) => vec![AmdsmiMetric {
info: AmdsmiInfo {
name,
description,
unit: unit.clone(),
},
value: value.into(),
status: AmdsmiStatusT::AmdsmiStatusSuccess,
}],
Err(e) => vec![AmdsmiMetric {
info: AmdsmiInfo {
name,
description,
unit: unit.clone(),
},
value: 0,
status: e,
}],
}),
);
self
}
pub fn add_metric_group<T, F>(&mut self, collector: &AmdsmiGaugeGroup<T, F>) -> &mut Self
where
T: 'static + Debug,
F: 'static + Fn(AmdsmiProcessorHandle) -> AmdsmiResult<T> + Send + Sync + Clone,
{
let name = collector.name;
let func = collector.func.clone();
// Apply whitelist and blacklist filters before the Box::new
let filtered_info_list: Vec<_> = collector
.info_list
.iter()
.filter(|(info, _)| {
if let Some(ref whitelist) = self.whitelist {
if !whitelist.contains(&info.name.to_string()) {
return false;
}
}
if let Some(ref blacklist) = self.blacklist {
if blacklist.contains(&info.name.to_string()) {
return false;
}
}
true
})
.cloned()
.collect();
// If the filtered_info_list is empty, return early
if filtered_info_list.is_empty() {
return self;
}
self.collectors.insert(
name,
Box::new(move |processor_handle| {
let mut results = Vec::new();
match func(processor_handle) {
Ok(value) => {
for (info, accessor) in &filtered_info_list {
results.push(AmdsmiMetric {
info: AmdsmiInfo {
name: info.name,
description: info.description,
unit: info.unit.clone(),
},
value: accessor(&value),
status: AmdsmiStatusT::AmdsmiStatusSuccess,
});
}
}
Err(e) => {
for (info, _) in &filtered_info_list {
results.push(AmdsmiMetric {
info: AmdsmiInfo {
name: info.name,
description: info.description,
unit: info.unit.clone(),
},
value: 0,
status: e,
});
}
}
}
results
}),
);
self
}
pub fn print_supported(&self) {
// Initialize the AMD SMI library
if let Err(e) = amdsmi_init(AmdsmiInitFlagsT::AmdsmiInitAmdGpus) {
eprintln!("Failed to initialize AMD SMI: {}", e);
return;
}
// Get socket handles
let socket_handles = match amdsmi_get_socket_handles() {
Ok(handles) => handles,
Err(e) => {
eprintln!("Failed to get socket handles: {}", e);
return;
}
};
for (socket_index, socket_handle) in socket_handles.iter().enumerate() {
let socket_id = socket_index.to_string();
// Get processor handles for each socket handle
let processor_handles = match amdsmi_get_processor_handles(*socket_handle) {
Ok(handles) => handles,
Err(e) => {
eprintln!("Failed to get processor handles: {}", e);
continue;
}
};
for (processor_index, processor_handle) in processor_handles.iter().enumerate() {
let processor_id = processor_index.to_string();
let bdf = match amdsmi_get_gpu_device_bdf(*processor_handle) {
Ok(bdf) => bdf.to_string(),
Err(e) => {
eprintln!("Failed to get GPU device BDF: {}", e);
continue;
}
};
println!("========================================");
println!("GPU Information:");
println!(" Socket ID: {}", socket_id);
println!(" Processor ID: {}", processor_id);
println!(" BDF: {}", bdf);
println!("----------------------------------------");
println!("Supported Metrics:");
println!(
"{:<30} | {:<10} | {:<10} | {:<50}",
"Metric ID", "Value", "Unit", "Help"
);
println!("{:-<30}-+-{:-<10}-+-{:-<10}-+-{:-<50}", "", "", "", "");
// Collect metrics
for (_, collector) in &self.collectors {
let results = collector(*processor_handle);
for metric in results {
let value_str = if metric.status == AmdsmiStatusT::AmdsmiStatusSuccess {
metric.value.to_string()
} else {
"NA".to_string()
};
println!(
"{:<30} | {:<10} | {:<10} | {:<50}",
metric.info.name,
value_str,
metric.info.unit.as_str(),
metric.info.description
);
}
}
println!("========================================\n");
}
}
// Shutdown the AMD SMI library
if let Err(e) = amdsmi_shut_down() {
eprintln!("Failed to shutdown AMD SMI: {}", e);
}
}
pub fn run_collect(&mut self) -> Registry {
// Initialize the AMD SMI library
if let Err(e) = amdsmi_init(AmdsmiInitFlagsT::AmdsmiInitAmdGpus) {
eprintln!("Failed to initialize AMD SMI: {}", e);
return Registry::default();
}
let mut registry = Registry::default();
// Get socket handles
let socket_handles = match amdsmi_get_socket_handles() {
Ok(handles) => handles,
Err(e) => {
eprintln!("Failed to get socket handles: {}", e);
return registry;
}
};
for (socket_index, socket_handle) in socket_handles.iter().enumerate() {
let socket_id = socket_index.to_string();
// Get processor handles for each socket handle
let processor_handles = match amdsmi_get_processor_handles(*socket_handle) {
Ok(handles) => handles,
Err(e) => {
eprintln!("Failed to get processor handles: {}", e);
continue;
}
};
for (processor_index, processor_handle) in processor_handles.iter().enumerate() {
let processor_id = processor_index.to_string();
let bdf = match amdsmi_get_gpu_device_bdf(*processor_handle) {
Ok(bdf) => bdf.to_string(),
Err(e) => {
eprintln!("Failed to get GPU device BDF: {}", e);
continue;
}
};
let labels = AmdsmiLabels {
socket_id: socket_id.clone(),
processor_id: processor_id.clone(),
bdf,
};
// Only for GPUs which match the provided gpu filter argument
if let Some(ref filters) = self.gpu_filter {
if !filters.iter().any(|filter| {
filter == &labels.bdf
|| filter == &format!("{}.{}", socket_id, processor_id)
}) {
continue;
}
}
// Collect metrics
for (_, collector) in &self.collectors {
let results = collector(*processor_handle);
for metric in results {
if metric.status == AmdsmiStatusT::AmdsmiStatusSuccess {
let metric_family =
Family::<AmdsmiLabels, Gauge>::new_with_constructor(Gauge::default);
if metric.info.unit.as_str().is_empty() {
registry.register(
metric.info.name,
metric.info.description,
metric_family.clone(),
);
} else {
registry.register_with_unit(
metric.info.name,
metric.info.description,
metric.info.unit.clone(),
metric_family.clone(),
);
}
metric_family.get_or_create(&labels).set(metric.value);
} else {
#[cfg(debug_assertions)]
eprintln!(
"Failed to collect metric {}: {} for socket {} processor {}",
metric.info.name,
metric.status,
labels.socket_id,
labels.processor_id
);
}
}
}
}
}
// Shutdown the AMD SMI library
if let Err(e) = amdsmi_shut_down() {
eprintln!("Failed to shutdown AMD SMI: {}", e);
}
registry
}
}
/// A macro to add a metric collector.
///
/// This macro creates an `AmdsmiGauge` instance and adds it to the collectors.
///
/// # Parameters
/// - `$collectors`: The collectors to add the metric to.
/// - `$name`: The name of the metric.
/// - `$description`: The description of the metric.
/// - `$unit`: The unit of the metric.
/// - `$func`: The function to collect the metric.
///
/// # Example
///
/// ```rust
/// # use amdsmi::*;
/// # use amdsmi_collectors::*;
///
/// # fn main() {
/// let mut collectors = AmdsmiCollectors::default();
/// add_metric!(
/// collectors,
/// "AMDSMI_FI_TEMPERATURE_EDGE",
/// "Currnt edge temperature",
/// Unit::Celsius,
/// amdsmi_func!(amdsmi_get_temp_metric, AmdsmiTemperatureTypeT::AmdsmiTemperatureTypeEdge, AmdsmiTemperatureMetricT::AmdsmiTempCurrent)
/// );
/// # }
/// ```
macro_rules! add_metric {
($collectors:expr, $name:expr, $description:expr, $unit:expr, $func:expr) => {
let collector = AmdsmiGauge::new($name, $description, $unit, $func);
$collectors.add_metric(collector);
};
}
/// A macro to add a single metric from a struct to the collectors.
///
/// This macro creates an `AmdsmiGauge` instance from a field of struct and adds it to the collectors.
///
/// # Parameters
/// - `$collectors`: The collectors to add the metric to.
/// - `$name`: The name of the metric.
/// - `$description`: The description of the metric.
/// - `$unit`: The unit of the metric.
/// - `$func`: The function to collect the metric.
/// - `$field`: The struct field from which the metric is collected.
///
/// # Example
///
/// ```rust
/// # use amdsmi::*;
/// # use amdsmi_collectors::*;
///
/// # fn main() {
/// let mut collectors = AmdsmiCollectors::default();
/// add_metric_field!(
/// collectors,
/// "AMDSMI_FI_CURRENT_SOCKET_POWER",
/// "Current Socket Power",
/// Unit::Watts,
/// amdsmi_get_power_info,
/// current_socket_power
/// );
/// # }
/// ```
#[allow(unused_macros)]
macro_rules! add_metric_field {
($collectors:expr, $name:expr, $description:expr, $unit:expr, $func:expr, $field:ident) => {
let collector = AmdsmiGauge::new($name, $description, $unit, |handle| {
$func(handle).map(|info| info.$field.into())
});
$collectors.add_metric(collector);
};
}
/// A macro to add a group of metrics within a single struct.
///
/// This macro creates an `AmdsmiGaugeGroup` instance and adds it to the collectors.
///
/// # Parameters
/// - `$collectors`: The collectors to add the metric group to.
/// - `$group_name`: The name of the metric group.
/// - `$func`: The function to collect the metrics.
/// - `$t`: The type of the struct containing the metrics.
/// - `$name`: The name of each metric.
/// - `$description`: The description of each metric.
/// - `$unit`: The unit of each metric.
/// - `$field`: The struct field from which each metric is collected.
///
/// # Example
///
/// ```rust
/// # use amdsmi::*;
/// # use amdsmi_collectors::*;
///
/// # fn main() {
/// let mut collectors = AmdsmiCollectors::default();
/// add_metric_group!(
/// collectors,
/// "AmdsmiPowerInfoMetrics",
/// amdsmi_get_power_info,
/// AmdsmiPowerInfoT,
/// ("AMDSMI_FI_CURRENT_SOCKET_POWER", "Current Socket Power", Unit::Watts, current_socket_power),
/// ("AMDSMI_FI_AVERAGE_SOCKET_POWER", "Average Socket Power", Unit::Watts, average_socket_power),
/// ("AMDSMI_FI_GFX_VOLTAGE", "GFX Voltage", Unit::Volts, gfx_voltage),
/// ("AMDSMI_FI_SOC_VOLTAGE", "SOC Voltage", Unit::Volts, soc_voltage),
/// ("AMDSMI_FI_MEM_VOLTAGE", "Memory Voltage", Unit::Volts, mem_voltage),
/// ("AMDSMI_FI_POWER_LIMIT", "Power Limit", Unit::Watts, power_limit)
/// );
/// # }
/// ```
#[allow(unused_macros)]
macro_rules! add_metric_group {
($collectors:expr, $group_name:expr, $func:expr, $t:ty, $(($name:expr, $description:expr, $unit:expr, $field:ident)),+ $(,)?) => {
let info_list = vec![
$(
(
$name,
$description,
$unit,
Arc::new(|info: &$t| info.$field as i64) as Arc<dyn Fn(&$t) -> i64 + Send + Sync>,
),
)+
];
let mut collector = AmdsmiGaugeGroup::new($group_name, info_list, $func);
$collectors.add_metric_group(&mut collector);
};
}
/// A macro to create a closure for an AMD SMI function with dynamic arguments.
///
/// This macro generates a closure that calls the specified function with the provided arguments.
///
/// # Parameters
/// - `$func`: The function to create the closure for.
/// - `$arg`: The arguments to pass to the function.
///
/// # Example
///
/// ```rust
/// # use amdsmi::*;
/// # use amdsmi_collectors::*;
///
/// # fn main() {
/// let func = amdsmi_func!(amdsmi_get_temp_metric, AmdsmiTemperatureTypeT::AmdsmiTemperatureTypeEdge, AmdsmiTemperatureMetricT::AmdsmiTempCurrent);
/// # }
/// ``
#[allow(unused_macros)]
macro_rules! amdsmi_func {
($func:expr, $($arg:expr),*) => {
move |handle| $func(handle, $($arg),*)
};
}
@@ -0,0 +1,138 @@
// Copyright (C) 2024 Advanced Micro Devices. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
use amdsmi::{AmdsmiProcessorHandle, AmdsmiResult, AmdsmiStatusT};
use prometheus_client::encoding::EncodeLabelSet;
use prometheus_client::registry::Unit;
use std::fmt::Debug;
use std::sync::Arc;
#[derive(Clone, Hash, PartialEq, Eq, EncodeLabelSet, Debug)]
pub struct AmdsmiLabels {
pub socket_id: String,
pub processor_id: String,
pub bdf: String,
}
#[derive(Clone)]
pub struct AmdsmiInfo {
pub name: &'static str,
pub description: &'static str,
pub unit: Unit,
}
#[derive(Clone)]
pub struct AmdsmiMetric {
pub info: AmdsmiInfo,
pub value: i64,
pub status: AmdsmiStatusT,
}
pub type AmdsmiCollector = Box<dyn Fn(AmdsmiProcessorHandle) -> Vec<AmdsmiMetric> + Send + Sync>;
pub struct AmdsmiGauge<T, F>
where
F: Fn(AmdsmiProcessorHandle) -> AmdsmiResult<T> + Send + Sync,
{
pub info: AmdsmiInfo,
pub func: F,
}
impl<T, F> AmdsmiGauge<T, F>
where
F: Fn(AmdsmiProcessorHandle) -> AmdsmiResult<T> + Send + Sync,
{
pub fn new(name: &'static str, description: &'static str, unit: Unit, func: F) -> Self {
Self {
info: AmdsmiInfo {
name,
description,
unit,
},
func,
}
}
}
pub struct AmdsmiGaugeGroup<T, F>
where
F: Fn(AmdsmiProcessorHandle) -> AmdsmiResult<T> + Send + Sync + Clone,
{
pub name: &'static str, /* name of the group */
pub info_list: Vec<(
AmdsmiInfo,
Arc<dyn Fn(&T) -> i64 + Send + Sync>, /* field accessor */
)>,
pub func: F,
}
impl<T, F> AmdsmiGaugeGroup<T, F>
where
F: Fn(AmdsmiProcessorHandle) -> AmdsmiResult<T> + Send + Sync + Clone,
{
pub fn new(
name: &'static str, /* name of the group */
info_list: Vec<(
&'static str, /* name */
&'static str, /* description */
Unit, /* unit */
Arc<dyn Fn(&T) -> i64 + Send + Sync>, /* field accessor */
)>,
func: F,
) -> Self {
Self {
name,
info_list: info_list
.into_iter()
.map(|(name, description, unit, accessor)| {
(
AmdsmiInfo {
name,
description,
unit,
},
accessor,
)
})
.collect(),
func,
}
}
}
/// A macro to define a unit as `Unit::Other`.
///
/// This macro takes a unit string as an argument and generates the appropriate `Unit` value.
///
/// # Parameters
/// - `$unit_str`: The unit string to define the unit.
///
/// # Example
///
/// ```rust
/// # use amdsmi::*;
/// # use amdsmi_metric::*;
///
/// let unit = metric_unit_other!("W");
/// assert_eq!(unit, Unit::Other("W".to_string()));
/// ```
macro_rules! metric_unit_other {
($unit_str:expr) => {
Unit::Other($unit_str.to_string())
};
}
@@ -0,0 +1,191 @@
// Copyright (C) 2024 Advanced Micro Devices. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
use crate::amdsmi_collectors::AmdsmiCollectors;
use crate::amdsmi_metric::*;
use amdsmi::*;
use prometheus_client::registry::Unit;
use std::sync::Arc;
pub fn collectors_create(collectors: &mut AmdsmiCollectors) {
add_metric_group!(
collectors,
"AmdsmiEngineUsageMetrics",
amdsmi_get_gpu_activity,
AmdsmiEngineUsageT,
(
"AMDSMI_FI_GFX_ACTIVITY",
"GFX Activity",
Unit::Ratios,
gfx_activity
),
(
"AMDSMI_FI_UMC_ACTIVITY",
"UMC Activity",
Unit::Ratios,
umc_activity
),
(
"AMDSMI_FI_MM_ACTIVITY",
"MM Activity",
Unit::Ratios,
mm_activity
)
);
add_metric_group!(
collectors,
"AmdsmiPowerInfoMetrics",
amdsmi_get_power_info,
AmdsmiPowerInfoT,
(
"AMDSMI_FI_CURRENT_SOCKET_POWER",
"Current Socket Power",
metric_unit_other!("W"),
current_socket_power
),
(
"AMDSMI_FI_AVERAGE_SOCKET_POWER",
"Average Socket Power",
metric_unit_other!("W"),
average_socket_power
),
(
"AMDSMI_FI_GFX_VOLTAGE",
"GFX Voltage",
metric_unit_other!("mV"),
gfx_voltage
),
(
"AMDSMI_FI_SOC_VOLTAGE",
"SOC Voltage",
metric_unit_other!("mV"),
soc_voltage
),
(
"AMDSMI_FI_MEM_VOLTAGE",
"Memory Voltage",
metric_unit_other!("mV"),
mem_voltage
),
);
add_metric_group!(
collectors,
"AmdsmiGfxClockMetrics",
amdsmi_func!(amdsmi_get_clock_info, AmdsmiClkTypeT::AmdsmiClkTypeGfx),
AmdsmiClkInfoT,
(
"AMDSMI_FI_GFX_CLK",
"GFX Clock",
metric_unit_other!("MHz"),
clk
),
(
"AMDSMI_FI_GFX_MIN_CLK",
"GFX Min Clock",
metric_unit_other!("MHz"),
min_clk
),
(
"AMDSMI_FI_GFX_MAX_CLK",
"GFX Max Clock",
metric_unit_other!("MHz"),
max_clk
)
);
add_metric_group!(
collectors,
"AmdsmiVRAMUsageMetrics",
amdsmi_get_gpu_vram_usage,
AmdsmiVramUsageT,
(
"AMDSMI_FI_VRAM_TOTAL",
"VRAM total",
metric_unit_other!("MB"),
vram_total
),
(
"AMDSMI_FI_VRAM_USED",
"VRAM used",
metric_unit_other!("MB"),
vram_used
),
);
add_metric_group!(
collectors,
"AmdsmiMemClockMetrics",
amdsmi_func!(amdsmi_get_clock_info, AmdsmiClkTypeT::AmdsmiClkTypeMem),
AmdsmiClkInfoT,
(
"AMDSMI_FI_MEM_CLK",
"Memory Clock",
metric_unit_other!("MHz"),
clk
),
(
"AMDSMI_FI_MEM_MIN_CLK",
"Memory Min Clock",
metric_unit_other!("MHz"),
min_clk
),
(
"AMDSMI_FI_MEM_MAX_CLK",
"Memory Max Clock",
metric_unit_other!("MHz"),
max_clk
)
);
add_metric!(
collectors,
"AMDSMI_FI_TEMPERATURE_EDGE",
"Current edge temperature",
Unit::Celsius,
amdsmi_func!(
amdsmi_get_temp_metric,
AmdsmiTemperatureTypeT::AmdsmiTemperatureTypeEdge,
AmdsmiTemperatureMetricT::AmdsmiTempCurrent
)
);
add_metric!(
collectors,
"AMDSMI_FI_TEMPERATURE_HOTSPOT",
"Current Hotspot temperature",
Unit::Celsius,
amdsmi_func!(
amdsmi_get_temp_metric,
AmdsmiTemperatureTypeT::AmdsmiTemperatureTypeHotspot,
AmdsmiTemperatureMetricT::AmdsmiTempCurrent
)
);
add_metric!(
collectors,
"AMDSMI_FI_TEMPERATURE_MEM",
"Current VRAM temperature",
Unit::Celsius,
amdsmi_func!(
amdsmi_get_temp_metric,
AmdsmiTemperatureTypeT::AmdsmiTemperatureTypeVram,
AmdsmiTemperatureMetricT::AmdsmiTempCurrent
)
);
}
@@ -0,0 +1,63 @@
// Copyright (C) 2024 Advanced Micro Devices. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
use crate::amdsmi_collectors::AmdsmiCollectors;
use axum::response::{IntoResponse, Response};
use axum::{routing::get, Router};
use hyper::Server;
use prometheus_client::encoding::text::encode;
use prometheus_client::registry::Registry;
use std::net::SocketAddr;
use std::sync::Arc;
use tokio::sync::Mutex;
async fn serve_req(registry: Arc<Mutex<Registry>>) -> impl IntoResponse {
let mut buffer = String::new();
let registry = registry.lock().await;
encode(&mut buffer, &*registry).unwrap();
Response::builder()
.header("Content-Type", "text/plain; version=0.0.4")
.body(buffer)
.unwrap()
}
pub async fn run_http_server(collectors: &Arc<Mutex<AmdsmiCollectors>>, addr: SocketAddr) {
let app = Router::new().route(
"/metrics",
get({
let collectors = Arc::clone(&collectors);
move || {
let collectors = Arc::clone(&collectors);
async move {
let mut collectors = collectors.lock().await;
let registry = Arc::new(Mutex::new(collectors.run_collect()));
serve_req(registry).await
}
}
}),
);
println!("Listening on http://{}", addr);
Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
+133
Zobrazit soubor
@@ -0,0 +1,133 @@
// Copyright (C) 2024 Advanced Micro Devices. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
#[macro_use]
mod amdsmi_collectors;
#[macro_use]
mod amdsmi_metric;
mod collectors;
mod http_server;
use crate::amdsmi_collectors::*;
use clap::{App, Arg, SubCommand};
use http_server::run_http_server;
use std::net::SocketAddr;
use std::sync::Arc;
use tokio::sync::Mutex;
const APP_VERSION_STR: &str = "1.0";
#[tokio::main]
async fn main() {
// Parse command line arguments
let cli_matches = App::new("AMDSMI Exporter")
.version(APP_VERSION_STR)
.author("AMD <amd-smi.support@amd.com>")
.about("Exports AMD SMI metrics")
.subcommand(
SubCommand::with_name("list")
.about("Lists the supported GPUs and metrics' information")
)
.subcommand(
SubCommand::with_name("metric")
.about("Exports AMD SMI metrics and runs the HTTP server")
.arg(
Arg::new("port")
.short('p')
.long("port")
.value_name("PORT")
.help("Sets the port to listen on")
.takes_value(true),
)
.arg(
Arg::new("gpu")
.short('g')
.long("gpu")
.value_name("GPU")
.help("Sets the GPU BDF or the zero-based index of the socket ID and processor ID for export. Example: --gpu 0000:03:00.0 or --gpu 0.0. You can specify multiple GPUs separated by commas")
.takes_value(true)
)
.arg(
Arg::new("whitelist")
.short('w')
.long("whitelist")
.value_name("METRIC")
.help("Comma-separated list of metric field names to include")
.takes_value(true),
)
.arg(
Arg::new("blacklist")
.short('b')
.long("blacklist")
.value_name("METRIC")
.help("Comma-separated list of metric field names to exclude")
.takes_value(true),
)
)
.get_matches();
let collectors = Arc::new(Mutex::new(AmdsmiCollectors::default()));
match cli_matches.subcommand() {
Some(("list", _)) => {
collectors.lock().await.print_supported();
}
Some(("metric", arg_options)) => {
export_metrics(arg_options, &collectors).await;
}
_ => {
eprintln!("Invalid command. Please use 'list' to list supported GPUs and metrics, or 'metric' to export metrics and run the HTTP server. Use --help for more information.");
}
}
}
async fn export_metrics(arg_options: &clap::ArgMatches, collectors: &Arc<Mutex<AmdsmiCollectors>>) {
let port: u16 = arg_options
.get_one::<String>("port")
.unwrap_or(&"9898".to_string())
.parse::<u16>()
.unwrap_or_else(|_| {
eprintln!("Invalid port number provided. Using default port 9898.");
9898
});
let gpu_filters: Option<Vec<String>> = arg_options
.get_one::<String>("gpu")
.map(|s| s.split(',').map(|s| s.trim().to_string()).collect());
let whitelist: Option<Vec<String>> = arg_options
.get_one::<String>("whitelist")
.map(|s| s.split(',').map(|s| s.trim().to_string()).collect());
let blacklist: Option<Vec<String>> = arg_options
.get_one::<String>("blacklist")
.map(|s| s.split(',').map(|s| s.trim().to_string()).collect());
collectors
.lock()
.await
.set_gpu_filter(gpu_filters)
.set_field_filter(whitelist, blacklist);
let addr = SocketAddr::from(([0, 0, 0, 0], port));
// Run the HTTP server
run_http_server(&collectors, addr).await;
}
@@ -0,0 +1,7 @@
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'amdsmi_exporter'
static_configs:
- targets: ['localhost:9898']
+110
Zobrazit soubor
@@ -0,0 +1,110 @@
// Copyright (C) 2024 Advanced Micro Devices. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
use amdsmi::*;
fn main() {
// Initialize the AMD SMI library
if let Err(e) = amdsmi_init(AmdsmiInitFlagsT::AmdsmiInitAmdGpus) {
eprintln!("Failed to initialize AMD SMI: {}", e);
return;
}
// Get socket handles
let socket_handles = match amdsmi_get_socket_handles() {
Ok(handles) => handles,
Err(e) => {
eprintln!("Failed to get socket handles: {}", e);
amdsmi_shut_down().expect("Failed to shutdown AMD SMI");
return;
}
};
for socket_handle in socket_handles {
// Get processor handles for each socket handle
let processor_handles = match amdsmi_get_processor_handles(socket_handle) {
Ok(handles) => handles,
Err(e) => {
eprintln!(
"Failed to get processor handles for socket {:?}: {}",
socket_handle, e
);
continue;
}
};
for processor_handle in processor_handles {
// Get GPU ID using the processor handle
match amdsmi_get_gpu_id(processor_handle) {
Ok(gpu_id) => println!("GPU ID: {}", gpu_id),
Err(e) => eprintln!("Failed to get GPU ID: {}", e),
}
// Get GPU revision using the processor handle
match amdsmi_get_gpu_revision(processor_handle) {
Ok(gpu_revision) => println!("GPU Revision: {}", gpu_revision),
Err(e) => eprintln!("Failed to get GPU revision: {}", e),
}
// Get GPU vendor name using the processor handle
match amdsmi_get_gpu_vendor_name(processor_handle) {
Ok(gpu_vendor_name) => println!("GPU Vendor Name: {}", gpu_vendor_name),
Err(e) => eprintln!("Failed to get GPU vendor name: {}", e),
}
// Get GPU VRAM vendor using the processor handle
match amdsmi_get_gpu_vram_vendor(processor_handle) {
Ok(gpu_vram_vendor) => println!("GPU VRAM Vendor: {}", gpu_vram_vendor),
Err(e) => eprintln!("Failed to get GPU VRAM vendor: {}", e),
}
// Get GPU subsystem ID using the processor handle
match amdsmi_get_gpu_subsystem_id(processor_handle) {
Ok(gpu_subsystem_id) => println!("GPU Subsystem ID: {}", gpu_subsystem_id),
Err(e) => eprintln!("Failed to get GPU subsystem ID: {}", e),
}
// Get GPU subsystem name using the processor handle
match amdsmi_get_gpu_subsystem_name(processor_handle) {
Ok(gpu_subsystem_name) => println!("GPU Subsystem Name: {}", gpu_subsystem_name),
Err(e) => eprintln!("Failed to get GPU subsystem name: {}", e),
}
// Get GPU BDF using the processor handle
match amdsmi_get_gpu_device_bdf(processor_handle) {
Ok(gpu_bdf) => println!("GPU BDF: {}", gpu_bdf),
Err(e) => eprintln!("Failed to get GPU BDF: {}", e),
}
// Get GPU BDF ID using the processor handle
match amdsmi_get_gpu_bdf_id(processor_handle) {
Ok(gpu_bdf_id) => println!("GPU BDF ID: {}", gpu_bdf_id),
Err(e) => eprintln!("Failed to get GPU BDF ID: {}", e),
}
println!();
}
}
// Shutdown the AMD SMI library
if let Err(e) = amdsmi_shut_down() {
eprintln!("Failed to shutdown AMD SMI: {}", e);
}
}
Rozdílový obsah nebyl zobrazen, protože je příliš veliký Načíst rozdílové porovnání
Rozdílový obsah nebyl zobrazen, protože je příliš veliký Načíst rozdílové porovnání
+29
Zobrazit soubor
@@ -0,0 +1,29 @@
// Copyright (C) 2024 Advanced Micro Devices. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
#![allow(dead_code)]
mod amdsmi_wrapper;
#[macro_use]
mod utils;
mod amdsmi;
pub use utils::*;
pub use amdsmi::*;
+184
Zobrazit soubor
@@ -0,0 +1,184 @@
// Copyright (C) 2024 Advanced Micro Devices. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
use std::fmt;
// Re-export all the alias type
pub use crate::amdsmi_wrapper::{AmdsmiEventHandleT, AmdsmiProcessorHandle, AmdsmiSocketHandle};
// Re-export all the enums type
pub use crate::amdsmi_wrapper::{
AmdsmiCachePropertyTypeT, AmdsmiCardFormFactorT, AmdsmiClkLimitTypeT, AmdsmiClkTypeT,
AmdsmiComputePartitionTypeT, AmdsmiCounterCommandT, AmdsmiDevPerfLevelT, AmdsmiEventGroupT,
AmdsmiEventTypeT, AmdsmiEvtNotificationTypeT, AmdsmiFreqIndT, AmdsmiFwBlockT, AmdsmiGpuBlockT,
AmdsmiInitFlagsT, AmdsmiIoLinkTypeT, AmdsmiMemoryPartitionTypeT, AmdsmiMemoryTypeT,
AmdsmiPowerProfilePresetMasksT, AmdsmiPowerTypeT, AmdsmiRasErrStateT, AmdsmiStatusT,
AmdsmiTemperatureMetricT, AmdsmiTemperatureTypeT, AmdsmiUtilizationCounterTypeT,
AmdsmiVoltageMetricT, AmdsmiVoltageTypeT, AmdsmiXgmiStatusT, ProcessorTypeT, AmdsmiAcceleratorPartitionTypeT
};
// Re-export all the struct type
pub use crate::amdsmi_wrapper::{
AmdMetricsTableHeaderT, AmdsmiAcceleratorPartitionProfileT, AmdsmiAsicInfoT, AmdsmiBoardInfoT,
AmdsmiClkInfoT, AmdsmiCounterValueT, AmdsmiDpmPolicyEntryT, AmdsmiDpmPolicyT,
AmdsmiDriverInfoT, AmdsmiEngineUsageT, AmdsmiErrorCountT, AmdsmiEvtNotificationDataT,
AmdsmiFreqVoltRegionT, AmdsmiFrequenciesT, AmdsmiFrequencyRangeT, AmdsmiFwInfoT,
AmdsmiGpuCacheInfoT, AmdsmiGpuCacheInfoTCache, AmdsmiGpuMetricsT, AmdsmiKfdInfoT,
AmdsmiLinkMetricsT, AmdsmiLinkMetricsTLinks, AmdsmiLinkTypeT, AmdsmiNameValueT,
AmdsmiOdVoltFreqDataT, AmdsmiP2pCapabilityT, AmdsmiPcieBandwidthT, AmdsmiPcieInfoT,
AmdsmiPcieInfoTPcieMetric, AmdsmiPcieInfoTPcieStatic, AmdsmiPowerCapInfoT, AmdsmiPowerInfoT,
AmdsmiPowerProfileStatusT, AmdsmiProcInfoT, AmdsmiProcInfoTEngineUsage,
AmdsmiProcInfoTMemoryUsage, AmdsmiProcessInfoT, AmdsmiRangeT, AmdsmiRasFeatureT,
AmdsmiRegTypeT, AmdsmiRetiredPageRecordT, AmdsmiTopologyNearestT, AmdsmiUtilizationCounterT,
AmdsmiVbiosInfoT, AmdsmiVersionT, AmdsmiViolationStatusT, AmdsmiVramInfoT, AmdsmiVramUsageT,
AmdsmiXgmiInfoT, AmdsmiNpsCapsT, AmdsmiNpsCapsTNpsFlags
};
//Re-export all the union type
pub use crate::amdsmi_wrapper::AmdsmiBdfT;
//Re-export the constant type
pub use crate::amdsmi_wrapper::{
AMDSMI_MAX_AID, AMDSMI_MAX_CACHE_TYPES, AMDSMI_MAX_CONTAINER_TYPE, AMDSMI_MAX_DEVICES,
AMDSMI_MAX_ENGINES, AMDSMI_MAX_FAN_SPEED, AMDSMI_MAX_MM_IP_COUNT, AMDSMI_MAX_NUM_CLKS,
AMDSMI_MAX_NUM_FREQUENCIES, AMDSMI_MAX_NUM_GFX_CLKS, AMDSMI_MAX_NUM_JPEG,
AMDSMI_MAX_NUM_PM_POLICIES, AMDSMI_MAX_NUM_VCN, AMDSMI_MAX_NUM_XGMI_LINKS,
AMDSMI_MAX_NUM_XGMI_PHYSICAL_LINK, AMDSMI_MAX_UTILIZATION_VALUES, AMDSMI_NUM_HBM_INSTANCES,
AMDSMI_NUM_VOLTAGE_CURVE_POINTS,
};
pub type AmdsmiResult<T> = Result<T, AmdsmiStatusT>;
//#[macro_export]
macro_rules! call_unsafe {
($call:expr) => {{
let status = unsafe { $call };
if status != AmdsmiStatusT::AmdsmiStatusSuccess {
return Err(status);
}
}};
}
macro_rules! define_cstr {
($len:expr) => {{
let buf = vec![0 as std::os::raw::c_char; $len as usize];
let len = $len as usize;
(buf, len)
}};
}
macro_rules! cstr_to_string {
($vec:expr) => {{
let c_str = unsafe { std::ffi::CStr::from_ptr($vec.as_ptr()) };
c_str.to_string_lossy().into_owned()
}};
}
// Implements getter methods for C string fields in the specified struct.
//
// This macro generates methods that convert C string fields to Rust `String`.
//
// # Arguments
//
// * `$struct_name` - The name of the struct for which the getter methods are being generated.
// * `$($field_name),+` - The names of the fields for which the getter methods are being generated.
//
// # Example
///
// ```rust
//
// use std::ffi::c_char;
//
// pub struct AmdsmiVbiosInfoT {
// pub name: [c_char; 64usize],
// pub version: [c_char; 32usize],
// }
//
// impl_cstr_getters!(AmdsmiVbiosInfoT, name, version);
// ```
macro_rules! impl_cstr_getters {
($struct_name:ident, $($field_name:ident),+) => {
impl $struct_name {
$(
pub fn $field_name(&self) -> String {
unsafe {
std::ffi::CStr::from_ptr(self.$field_name.as_ptr())
.to_string_lossy()
.into_owned()
}
}
)+
}
};
}
impl fmt::Display for AmdsmiStatusT {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let status_str = crate::amdsmi_status_code_to_string(*self)
.unwrap_or("An unknown error occurred".to_string());
write!(f, "{}", status_str)
}
}
impl fmt::Display for AmdsmiBdfT {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_human_readable_string())
}
}
impl AmdsmiBdfT {
/// Converts the AmdsmiBdfT structure to a human-readable string.
pub fn to_human_readable_string(&self) -> String {
format!(
"{:04x}:{:02x}:{:02x}.{:x}",
unsafe { self.__bindgen_anon_1.domain_number() },
unsafe { self.__bindgen_anon_1.bus_number() },
unsafe { self.__bindgen_anon_1.device_number() },
unsafe { self.__bindgen_anon_1.function_number() }
)
}
}
// Implement the getters for the C string fields in AmdsmiVbiosInfoT
impl_cstr_getters!(AmdsmiVbiosInfoT, name, build_date, part_number, version);
// Implement the getters for the C string fields in AmdsmiAsicInfoT
impl_cstr_getters!(AmdsmiAsicInfoT, market_name, vendor_name, asic_serial);
// Implement the getters for the C string fields in AmdsmiBoardInfoT
impl_cstr_getters!(
AmdsmiBoardInfoT,
model_number,
product_serial,
fru_id,
product_name,
manufacturer_name
);
// Implement the getters for the C string fields in AmdsmiDriverInfoT
impl_cstr_getters!(AmdsmiDriverInfoT, driver_version, driver_date, driver_name);
// Implement the getters for the C string fields in AmdsmiNameValueT
impl_cstr_getters!(AmdsmiNameValueT, name);
// Implement the getters for the C string fields in AmdsmiProcInfoT
impl_cstr_getters!(AmdsmiProcInfoT, name, container_name);
// Implement the getters for the C string fields in AmdsmiDpmPolicyEntryT
impl_cstr_getters!(AmdsmiDpmPolicyEntryT, policy_description);