test: add tests for retrieving data from various debug snapshots (#429)

* test: add tests for retrieving data from various debug snapshots

* chore: add power profiles mode to snapshot, add rx 7700s test data

* fix: update rust workflow
This commit is contained in:
Ilya Zlobintsev 2024-12-18 23:55:31 +02:00 committed by GitHub
parent 571d5d9fcf
commit 0625b55748
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
484 changed files with 2472 additions and 45 deletions

View File

@ -10,23 +10,21 @@ env:
jobs: jobs:
build-test: build-test:
if: ${{ github.event_name == 'push' || !github.event.pull_request.draft }} if: ${{ github.event_name == 'push' || !github.event.pull_request.draft }}
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Update repos - name: Update repos
run: sudo apt update run: sudo apt update
- name: Install dependencies - name: Install dependencies
run: sudo apt install libgtk-4-dev pkg-config libvulkan-dev libdrm-dev run: sudo apt install -y libgtk-4-dev pkg-config libvulkan-dev libdrm-dev blueprint-compiler
- name: Install blueprint-compiler
run: curl -o /tmp/blueprint-compiler.deb http://de.archive.ubuntu.com/ubuntu/pool/universe/b/blueprint-compiler/blueprint-compiler_0.10.0-3_all.deb && sudo apt install -y /tmp/blueprint-compiler.deb
- name: Build - name: Build
run: cargo build run: cargo build
- name: Run tests - name: Run tests
run: cargo test --verbose --no-default-features -p lact run: cargo test --verbose --no-default-features -p lact
check-format: check-format:
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
if: ${{ github.event_name == 'push' || !github.event.pull_request.draft }} if: ${{ github.event_name == 'push' || !github.event.pull_request.draft }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2

44
Cargo.lock generated
View File

@ -481,6 +481,18 @@ dependencies = [
"crossbeam-utils", "crossbeam-utils",
] ]
[[package]]
name = "console"
version = "0.15.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b"
dependencies = [
"encode_unicode",
"libc",
"once_cell",
"windows-sys 0.59.0",
]
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.9.4" version = "0.9.4"
@ -638,6 +650,12 @@ version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "encode_unicode"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
[[package]] [[package]]
name = "endi" name = "endi"
version = "1.1.0" version = "1.1.0"
@ -1262,6 +1280,19 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "insta"
version = "1.41.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e9ffc4d4892617c50a928c52b2961cb5174b6fc6ebf252b2fac9d21955c48b8"
dependencies = [
"console",
"lazy_static",
"linked-hash-map",
"serde",
"similar",
]
[[package]] [[package]]
name = "is_terminal_polyfill" name = "is_terminal_polyfill"
version = "1.70.1" version = "1.70.1"
@ -1358,6 +1389,7 @@ dependencies = [
"chrono", "chrono",
"futures", "futures",
"indexmap", "indexmap",
"insta",
"lact-schema", "lact-schema",
"libdrm_amdgpu_sys", "libdrm_amdgpu_sys",
"libflate", "libflate",
@ -1524,6 +1556,12 @@ dependencies = [
"redox_syscall", "redox_syscall",
] ]
[[package]]
name = "linked-hash-map"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.4.14" version = "0.4.14"
@ -2275,6 +2313,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "similar"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e"
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.9" version = "0.4.9"

View File

@ -40,3 +40,6 @@ tar = "0.4.40"
libflate = "2.0.0" libflate = "2.0.0"
os-release = "0.1.0" os-release = "0.1.0"
notify = { version = "6.1.1", default-features = false } notify = { version = "6.1.1", default-features = false }
[dev-dependencies]
insta = { version = "1.41.1", features = ["json"] }

View File

@ -5,6 +5,8 @@ mod config;
mod server; mod server;
mod socket; mod socket;
mod suspend; mod suspend;
#[cfg(test)]
mod tests;
use anyhow::Context; use anyhow::Context;
use config::Config; use config::Config;

View File

@ -57,12 +57,16 @@ pub struct AmdGpuController {
} }
impl AmdGpuController { impl AmdGpuController {
pub fn new_from_path(sysfs_path: PathBuf, pci_db: &Database) -> anyhow::Result<Self> { pub fn new_from_path(
sysfs_path: PathBuf,
pci_db: &Database,
skip_drm: bool,
) -> anyhow::Result<Self> {
let handle = GpuHandle::new_from_path(sysfs_path) let handle = GpuHandle::new_from_path(sysfs_path)
.map_err(|error| anyhow!("failed to initialize gpu handle: {error}"))?; .map_err(|error| anyhow!("failed to initialize gpu handle: {error}"))?;
let mut drm_handle = None; let mut drm_handle = None;
if matches!(handle.get_driver(), "amdgpu" | "radeon") { if matches!(handle.get_driver(), "amdgpu" | "radeon") && !skip_drm {
match get_drm_handle(&handle) { match get_drm_handle(&handle) {
Ok(handle) => { Ok(handle) => {
drm_handle = Some(handle); drm_handle = Some(handle);

View File

@ -67,6 +67,8 @@ const SNAPSHOT_DEVICE_FILES: &[&str] = &[
"current_link_speed", "current_link_speed",
"current_link_width", "current_link_width",
]; ];
/// Prefixes for entries that will be recursively included in the debug snapshot
const SNAPSHOT_DEVICE_RECURSIVE_PATHS_PREFIXES: &[&str] = &["tile"];
const SNAPSHOT_FAN_CTRL_FILES: &[&str] = &[ const SNAPSHOT_FAN_CTRL_FILES: &[&str] = &[
"fan_curve", "fan_curve",
"acoustic_limit_rpm_threshold", "acoustic_limit_rpm_threshold",
@ -89,13 +91,25 @@ pub struct Handler {
impl<'a> Handler { impl<'a> Handler {
pub async fn new(config: Config) -> anyhow::Result<Self> { pub async fn new(config: Config) -> anyhow::Result<Self> {
let base_path = match env::var("_LACT_DRM_SYSFS_PATH") {
Ok(custom_path) => PathBuf::from(custom_path),
Err(_) => PathBuf::from("/sys/class/drm"),
};
Self::with_base_path(&base_path, config, false).await
}
pub(crate) async fn with_base_path(
base_path: &Path,
config: Config,
sysfs_only: bool,
) -> anyhow::Result<Self> {
let mut controllers = BTreeMap::new(); let mut controllers = BTreeMap::new();
// Sometimes LACT starts too early in the boot process, before the sysfs is initialized. // Sometimes LACT starts too early in the boot process, before the sysfs is initialized.
// For such scenarios there is a retry logic when no GPUs were found, // For such scenarios there is a retry logic when no GPUs were found,
// or if some of the PCI devices don't have a drm entry yet. // or if some of the PCI devices don't have a drm entry yet.
for i in 1..=CONTROLLERS_LOAD_RETRY_ATTEMPTS { for i in 1..=CONTROLLERS_LOAD_RETRY_ATTEMPTS {
controllers = load_controllers()?; controllers = load_controllers(base_path, sysfs_only)?;
let mut should_retry = false; let mut should_retry = false;
if let Ok(devices) = fs::read_dir("/sys/bus/pci/devices") { if let Ok(devices) = fs::read_dir("/sys/bus/pci/devices") {
@ -538,6 +552,25 @@ impl<'a> Handler {
add_path_to_archive(&mut archive, &full_path)?; add_path_to_archive(&mut archive, &full_path)?;
} }
let device_files = fs::read_dir(controller.get_path())
.context("Could not read device dir")?
.flatten();
for device_entry in device_files {
if let Some(entry_name) = device_entry.file_name().to_str() {
if SNAPSHOT_DEVICE_RECURSIVE_PATHS_PREFIXES
.iter()
.any(|prefix| entry_name.starts_with(prefix))
{
add_path_recursively(
&mut archive,
&device_entry.path(),
controller.get_path(),
)?;
}
}
}
let fan_ctrl_path = controller_path.join("gpu_od").join("fan_ctrl"); let fan_ctrl_path = controller_path.join("gpu_od").join("fan_ctrl");
for fan_ctrl_file in SNAPSHOT_FAN_CTRL_FILES { for fan_ctrl_file in SNAPSHOT_FAN_CTRL_FILES {
let full_path = fan_ctrl_path.join(fan_ctrl_file); let full_path = fan_ctrl_path.join(fan_ctrl_file);
@ -547,7 +580,7 @@ impl<'a> Handler {
for hw_mon in controller.hw_monitors() { for hw_mon in controller.hw_monitors() {
let hw_mon_path = hw_mon.get_path(); let hw_mon_path = hw_mon.get_path();
let hw_mon_entries = let hw_mon_entries =
std::fs::read_dir(hw_mon_path).context("Could not read HwMon dir")?; fs::read_dir(hw_mon_path).context("Could not read HwMon dir")?;
'entries: for entry in hw_mon_entries.flatten() { 'entries: for entry in hw_mon_entries.flatten() {
if !entry.metadata().is_ok_and(|metadata| metadata.is_file()) { if !entry.metadata().is_ok_and(|metadata| metadata.is_file()) {
@ -599,30 +632,10 @@ impl<'a> Handler {
Err(err) => Some(err.to_string().into()), Err(err) => Some(err.to_string().into()),
}; };
let devices: BTreeMap<String, serde_json::Value> = self
.gpu_controllers
.iter()
.map(|(id, controller)| {
let config = self.config.try_borrow();
let gpu_config = config
.as_ref()
.ok()
.and_then(|config| config.gpus().ok()?.get(id));
let data = json!({
"pci_info": controller.get_pci_info(),
"info": controller.get_info(),
"stats": controller.get_stats(gpu_config),
"clocks_info": controller.get_clocks_info().ok(),
});
(id.clone(), data)
})
.collect();
let info = json!({ let info = json!({
"system_info": system_info, "system_info": system_info,
"initramfs_type": initramfs_type, "initramfs_type": initramfs_type,
"devices": devices, "devices": self.generate_snapshot_device_info(),
}); });
let info_data = serde_json::to_vec_pretty(&info).unwrap(); let info_data = serde_json::to_vec_pretty(&info).unwrap();
@ -647,6 +660,28 @@ impl<'a> Handler {
Ok(out_path) Ok(out_path)
} }
pub(crate) fn generate_snapshot_device_info(&self) -> BTreeMap<String, serde_json::Value> {
self.gpu_controllers
.iter()
.map(|(id, controller)| {
let config = self.config.try_borrow();
let gpu_config = config
.as_ref()
.ok()
.and_then(|config| config.gpus().ok()?.get(id));
let data = json!({
"pci_info": controller.get_pci_info(),
"info": controller.get_info(),
"stats": controller.get_stats(gpu_config),
"clocks_info": controller.get_clocks_info().ok(),
"power_profile_modes": controller.get_power_profile_modes().ok(),
});
(id.clone(), data)
})
.collect()
}
pub fn list_profiles(&self) -> ProfilesInfo { pub fn list_profiles(&self) -> ProfilesInfo {
let config = self.config.borrow(); let config = self.config.borrow();
ProfilesInfo { ProfilesInfo {
@ -743,14 +778,13 @@ impl<'a> Handler {
} }
} }
fn load_controllers() -> anyhow::Result<BTreeMap<String, Box<dyn GpuController>>> { /// `sysfs_only` disables initialization of any external data sources, such as libdrm and nvml
fn load_controllers(
base_path: &Path,
sysfs_only: bool,
) -> anyhow::Result<BTreeMap<String, Box<dyn GpuController>>> {
let mut controllers = BTreeMap::new(); let mut controllers = BTreeMap::new();
let base_path = match env::var("_LACT_DRM_SYSFS_PATH") {
Ok(custom_path) => PathBuf::from(custom_path),
Err(_) => PathBuf::from("/sys/class/drm"),
};
let pci_db = Database::read().unwrap_or_else(|err| { let pci_db = Database::read().unwrap_or_else(|err| {
warn!("could not read PCI ID database: {err}, device information will be limited"); warn!("could not read PCI ID database: {err}, device information will be limited");
Database { Database {
@ -759,14 +793,18 @@ fn load_controllers() -> anyhow::Result<BTreeMap<String, Box<dyn GpuController>>
} }
}); });
let nvml = match Nvml::init() { let nvml = if sysfs_only {
Ok(nvml) => { None
info!("NVML initialized"); } else {
Some(Rc::new(nvml)) match Nvml::init() {
} Ok(nvml) => {
Err(err) => { info!("NVML initialized");
info!("Nvidia support disabled, {err}"); Some(Rc::new(nvml))
None }
Err(err) => {
info!("Nvidia support disabled, {err}");
None
}
} }
}; };
@ -783,7 +821,7 @@ fn load_controllers() -> anyhow::Result<BTreeMap<String, Box<dyn GpuController>>
if name.starts_with("card") && !name.contains('-') { if name.starts_with("card") && !name.contains('-') {
trace!("trying gpu controller at {:?}", entry.path()); trace!("trying gpu controller at {:?}", entry.path());
let device_path = entry.path().join("device"); let device_path = entry.path().join("device");
match AmdGpuController::new_from_path(device_path, &pci_db) { match AmdGpuController::new_from_path(device_path, &pci_db, sysfs_only) {
Ok(controller) => match controller.get_id() { Ok(controller) => match controller.get_id() {
Ok(id) => { Ok(id) => {
let path = controller.get_path(); let path = controller.get_path();
@ -844,6 +882,40 @@ fn load_controllers() -> anyhow::Result<BTreeMap<String, Box<dyn GpuController>>
Ok(controllers) Ok(controllers)
} }
fn add_path_recursively(
archive: &mut tar::Builder<impl Write>,
entry_path: &Path,
controller_path: &Path,
) -> anyhow::Result<()> {
if let Ok(entries) = fs::read_dir(entry_path) {
for entry in entries.flatten() {
match entry.metadata() {
Ok(metadata) => {
// Skip symlinks
if metadata.is_symlink() {
continue;
}
let full_path = controller_path.join(entry.path());
if metadata.is_file() {
add_path_to_archive(archive, &full_path)?;
} else if metadata.is_dir() {
add_path_recursively(archive, &full_path, controller_path)?;
}
}
Err(err) => {
warn!(
"could not include file '{}' in snapshot: {err}",
entry.path().display()
);
}
}
}
}
Ok(())
}
fn add_path_to_archive( fn add_path_to_archive(
archive: &mut tar::Builder<impl Write>, archive: &mut tar::Builder<impl Write>,
full_path: &Path, full_path: &Path,

View File

@ -0,0 +1 @@
0

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1 @@
0

View File

@ -0,0 +1 @@
40

View File

@ -0,0 +1 @@
(null)

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1 @@
798080000

View File

@ -0,0 +1 @@
1750000000

View File

@ -0,0 +1 @@
amdgpu

View File

@ -0,0 +1 @@
155000000

View File

@ -0,0 +1 @@
0-15

View File

@ -0,0 +1 @@
0000ffff

View File

@ -0,0 +1 @@
8.0 GT/s PCIe

View File

@ -0,0 +1 @@
16

View File

@ -0,0 +1 @@
8342712320

View File

@ -0,0 +1 @@
92377088

View File

@ -0,0 +1 @@
268435456

View File

@ -0,0 +1 @@
150302720

View File

@ -0,0 +1 @@
4294967296

View File

@ -0,0 +1 @@
536870912

View File

@ -0,0 +1 @@
unknown

View File

@ -0,0 +1 @@
pci:v00001002d000067DFsv00001DA2sd0000E387bc03sc00i00

View File

@ -0,0 +1 @@
-1

View File

@ -0,0 +1 @@
11886 2528 256

View File

@ -0,0 +1 @@
performance

View File

@ -0,0 +1 @@
D0

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
0: 300Mhz
1: 1000Mhz
2: 1750Mhz *

View File

@ -0,0 +1,2 @@
0: 2.5GT/s, x8
1: 8.0GT/s, x16 *

View File

@ -0,0 +1,8 @@
0: 300Mhz
1: 600Mhz
2: 900Mhz *
3: 1145Mhz
4: 1215Mhz
5: 1257Mhz
6: 1300Mhz
7: 1366Mhz

View File

@ -0,0 +1 @@
0

View File

@ -0,0 +1,3 @@
states: 2
0 boot
1 performance

View File

@ -0,0 +1,17 @@
OD_SCLK:
0: 300MHz 750mV
1: 600MHz 769mV
2: 900MHz 912mV
3: 1145MHz 1125mV
4: 1215MHz 1150mV
5: 1257MHz 1150mV
6: 1300MHz 1150mV
7: 1366MHz 1150mV
OD_MCLK:
0: 300MHz 750mV
1: 1000MHz 825mV
2: 1750MHz 975mV
OD_RANGE:
SCLK: 300MHz 2000MHz
MCLK: 300MHz 2250MHz
VDDC: 750mV 1200mV

View File

@ -0,0 +1,8 @@
NUM MODE_NAME SCLK_UP_HYST SCLK_DOWN_HYST SCLK_ACTIVE_LEVEL MCLK_UP_HYST MCLK_DOWN_HYST MCLK_ACTIVE_LEVEL
0 BOOTUP_DEFAULT: - - - - - -
1 3D_FULL_SCREEN *: 0 100 30 10 60 25
2 POWER_SAVING: 10 0 30 - - -
3 VIDEO: - - - 10 16 31
4 VR: 0 11 50 0 100 10
5 COMPUTE: 0 5 30 - - -
6 CUSTOM: - - - - - -

View File

@ -0,0 +1 @@
0

View File

@ -0,0 +1 @@
bus

View File

@ -0,0 +1,13 @@
0x00000000d0000000 0x00000000dfffffff 0x000000000014220c
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x00000000e0000000 0x00000000e01fffff 0x000000000014220c
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x000000000000d000 0x000000000000d0ff 0x0000000000040101
0x00000000fce00000 0x00000000fce3ffff 0x0000000000040200
0x00000000000c0000 0x00000000000dffff 0x0000000000000212
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000

View File

@ -0,0 +1 @@
0xe7

View File

@ -0,0 +1 @@
0xe387

View File

@ -0,0 +1 @@
0x1da2

View File

@ -0,0 +1,6 @@
DRIVER=amdgpu
PCI_CLASS=30000
PCI_ID=1002:67DF
PCI_SUBSYS_ID=1DA2:E387
PCI_SLOT_NAME=0000:09:00.0
MODALIAS=pci:v00001002d000067DFsv00001DA2sd0000E387bc03sc00i00

View File

@ -0,0 +1 @@
113-1E3871U-O4C

View File

@ -0,0 +1 @@
0x1002

View File

@ -0,0 +1 @@
0x030000

View File

@ -0,0 +1 @@
40

View File

@ -0,0 +1 @@
8.0 GT/s PCIe

View File

@ -0,0 +1 @@
16

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1 @@
0x67df

Some files were not shown because too many files have changed in this diff Show More