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>
Este commit está contenido en:
@@ -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();
|
||||
}
|
||||
@@ -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']
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Referencia en una nueva incidencia
Block a user