From 9053f1efb50a25a6de9d96104be5d6902f8499a2 Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Sat, 23 Dec 2023 10:10:50 +0200 Subject: [PATCH] feat: add option to generate a debug snapshot of the SysFS (#231) * feat: daemon implementation for generating sysfs snapshot * feat: snapshot gui * fix: dialog title * chore: update bug reporting info --- Cargo.lock | 307 ++++++++++++++++++++++++++---- README.md | 11 +- lact-client/src/lib.rs | 1 + lact-daemon/Cargo.toml | 2 + lact-daemon/src/server/handler.rs | 135 ++++++++++++- lact-daemon/src/server/mod.rs | 1 + lact-daemon/src/server/system.rs | 2 +- lact-gui/src/app/header.rs | 19 +- lact-gui/src/app/mod.rs | 57 ++++++ lact-schema/src/request.rs | 1 + 10 files changed, 491 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e313ece..440b4e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,6 +49,21 @@ dependencies = [ "serde", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.4" @@ -84,7 +99,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -94,7 +109,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -168,11 +183,11 @@ dependencies = [ "futures-lite 2.0.1", "parking", "polling 3.3.0", - "rustix 0.38.25", + "rustix 0.38.28", "slab", "tracing", "waker-fn", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -208,8 +223,8 @@ dependencies = [ "cfg-if", "event-listener 3.1.0", "futures-lite 1.13.0", - "rustix 0.38.25", - "windows-sys", + "rustix 0.38.28", + "windows-sys 0.48.0", ] [[package]] @@ -235,10 +250,10 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 0.38.25", + "rustix 0.38.28", "signal-hook-registry", "slab", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -331,6 +346,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + [[package]] name = "bytemuck" version = "1.14.0" @@ -413,6 +434,20 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.48.5", +] + [[package]] name = "clap" version = "4.4.8" @@ -642,12 +677,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -702,6 +737,18 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "windows-sys 0.52.0", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1142,6 +1189,29 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "iana-time-zone" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1186,7 +1256,7 @@ checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi", "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1195,6 +1265,15 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +[[package]] +name = "js-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "lact" version = "0.5.1" @@ -1233,6 +1312,7 @@ version = "0.5.1" dependencies = [ "anyhow", "bincode", + "chrono", "futures", "lact-schema", "libdrm_amdgpu_sys", @@ -1242,6 +1322,7 @@ dependencies = [ "serde_json", "serde_with", "serde_yaml", + "tar", "tokio", "tracing", "tracing-subscriber", @@ -1424,7 +1505,7 @@ checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ "libc", "wasi", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1460,6 +1541,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + [[package]] name = "objc" version = "0.2.7" @@ -1551,7 +1641,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -1605,7 +1695,7 @@ dependencies = [ "libc", "log", "pin-project-lite", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1617,9 +1707,9 @@ dependencies = [ "cfg-if", "concurrent-queue", "pin-project-lite", - "rustix 0.38.25", + "rustix 0.38.28", "tracing", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1808,20 +1898,20 @@ dependencies = [ "io-lifetimes", "libc", "linux-raw-sys 0.3.8", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "rustix" -version = "0.38.25" +version = "0.38.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" dependencies = [ "bitflags 2.4.1", "errno", "libc", "linux-raw-sys 0.4.11", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1989,7 +2079,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2039,6 +2129,17 @@ dependencies = [ "version-compare", ] +[[package]] +name = "tar" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +dependencies = [ + "filetime", + "libc", + "xattr", +] + [[package]] name = "target-lexicon" version = "0.12.12" @@ -2054,8 +2155,8 @@ dependencies = [ "cfg-if", "fastrand 2.0.1", "redox_syscall", - "rustix 0.38.25", - "windows-sys", + "rustix 0.38.28", + "windows-sys 0.48.0", ] [[package]] @@ -2103,7 +2204,7 @@ dependencies = [ "socket2 0.5.5", "tokio-macros", "tracing", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2335,6 +2436,60 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.39", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" + [[package]] name = "winapi" version = "0.3.9" @@ -2357,13 +2512,31 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", ] [[package]] @@ -2372,13 +2545,28 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] @@ -2387,42 +2575,84 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + [[package]] name = "winnow" version = "0.5.19" @@ -2432,6 +2662,17 @@ dependencies = [ "memchr", ] +[[package]] +name = "xattr" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7dae5072fe1f8db8f8d29059189ac175196e410e40ba42d5d4684ae2f750995" +dependencies = [ + "libc", + "linux-raw-sys 0.4.11", + "rustix 0.38.28", +] + [[package]] name = "xdg-home" version = "1.0.0" diff --git a/README.md b/README.md index 8dab132..db72f4c 100644 --- a/README.md +++ b/README.md @@ -135,9 +135,16 @@ The functionality of the CLI is quite limited. If you want to integrate LACT wit # Reporting issues When reporting issues, please include your system info and GPU model. + +If you're having an issue with changing the GPU's configuration, it's highly recommended to include a debug snapshot in the bug report. +You can generate one using the option in the dropdown menu: + +![image](https://github.com/ilya-zlobintsev/LACT/assets/22796665/36dda5e3-981b-47e7-914e-6e29f30616b4) + +The snapshot is an archive which includes the SysFS that LACT uses to interact with the GPU. -If there's a crash, run `lact gui` from the command line to get logs, or use `journalctl -u lactd` to see if the daemon crashed. - +If there's a crash, run `lact gui` from the command line to get GUI logs, check daemon logs in `journalctl -u lactd` for errors, +and see `dmesg` for kernel logs that might include information about driver and system issues. # Alternatives diff --git a/lact-client/src/lib.rs b/lact-client/src/lib.rs index d64d9d2..17efdfb 100644 --- a/lact-client/src/lib.rs +++ b/lact-client/src/lib.rs @@ -128,6 +128,7 @@ impl DaemonClient { request_plain!(get_system_info, SystemInfo, SystemInfo); request_plain!(enable_overdrive, EnableOverdrive, ()); + request_plain!(generate_debug_snapshot, GenerateSnapshot, String); request_with_id!(get_device_info, DeviceInfo, DeviceInfo); request_with_id!(get_device_stats, DeviceStats, DeviceStats); request_with_id!(get_device_clocks_info, DeviceClocksInfo, ClocksInfo); diff --git a/lact-daemon/Cargo.toml b/lact-daemon/Cargo.toml index ec4208f..e093e69 100644 --- a/lact-daemon/Cargo.toml +++ b/lact-daemon/Cargo.toml @@ -34,3 +34,5 @@ serde_with = { version = "3.3.0", default-features = false, features = [ ] } zbus = { version = "3.14.1", default-features = false, features = ["tokio"] } libdrm_amdgpu_sys = { optional = true, version = "0.2.1" } +tar = "0.4.40" +chrono = "0.4.31" diff --git a/lact-daemon/src/server/handler.rs b/lact-daemon/src/server/handler.rs index 48832d3..39591bf 100644 --- a/lact-daemon/src/server/handler.rs +++ b/lact-daemon/src/server/handler.rs @@ -1,21 +1,65 @@ -use super::gpu_controller::{fan_control::FanCurve, GpuController}; +use super::{ + gpu_controller::{fan_control::FanCurve, GpuController}, + system::PP_FEATURE_MASK_PATH, +}; use crate::config::{self, default_fan_static_speed, Config, FanControlSettings}; use anyhow::{anyhow, Context}; use lact_schema::{ - amdgpu_sysfs::gpu_handle::{ - power_profile_mode::PowerProfileModesTable, PerformanceLevel, PowerLevelKind, + amdgpu_sysfs::{ + gpu_handle::{ + power_profile_mode::PowerProfileModesTable, PerformanceLevel, PowerLevelKind, + }, + sysfs::SysFS, }, default_fan_curve, request::{ConfirmCommand, SetClocksCommand}, ClocksInfo, DeviceInfo, DeviceListEntry, DeviceStats, FanControlMode, FanCurveMap, PowerStates, }; -use std::{cell::RefCell, collections::BTreeMap, env, path::PathBuf, rc::Rc, time::Duration}; +use std::{ + cell::RefCell, + collections::BTreeMap, + env, + fs::{File, Permissions}, + io::{BufWriter, Cursor, Write}, + os::unix::fs::{MetadataExt, PermissionsExt}, + path::{Path, PathBuf}, + rc::Rc, + time::Duration, +}; use tokio::{sync::oneshot, time::sleep}; use tracing::{debug, error, info, trace, warn}; const CONTROLLERS_LOAD_RETRY_ATTEMPTS: u8 = 5; const CONTROLLERS_LOAD_RETRY_INTERVAL: u64 = 1; +const SNAPSHOT_GLOBAL_FILES: &[&str] = &[ + PP_FEATURE_MASK_PATH, + "/etc/lact/config.yaml", + "/proc/version", +]; +const SNAPSHOT_DEVICE_FILES: &[&str] = &[ + "uevent", + "vendor", + "pp_cur_state", + "pp_dpm_mclk", + "pp_dpm_pcie", + "pp_dpm_sclk", + "pp_dpm_socclk", + "pp_features", + "pp_force_state", + "pp_mclk_od", + "pp_num_states", + "pp_od_clk_voltage", + "pp_power_profile_mode", + "pp_sclk_od", + "pp_table", + "vbios_version", + "gpu_busy_percent", + "current_link_speed", + "current_link_width", +]; +const SNAPSHOT_HWMON_FILE_PREFIXES: &[&str] = &["fan", "pwm", "power", "temp", "freq", "in"]; + #[derive(Clone)] pub struct Handler { pub config: Rc>, @@ -368,6 +412,62 @@ impl<'a> Handler { .await } + pub fn generate_snapshot(&self) -> anyhow::Result { + let datetime = chrono::Local::now().format("%Y%m%d-%H%M%S"); + let out_path = format!("/tmp/LACT-sysfs-snapshot-{datetime}.tar"); + + let out_file = File::create(&out_path) + .with_context(|| "Could not create output file at {out_path}")?; + let out_writer = BufWriter::new(out_file); + + let mut archive = tar::Builder::new(out_writer); + + for path in SNAPSHOT_GLOBAL_FILES { + let path = Path::new(path); + add_path_to_archive(&mut archive, path)?; + } + + for controller in self.gpu_controllers.values() { + let controller_path = controller.handle.get_path(); + + for device_file in SNAPSHOT_DEVICE_FILES { + let full_path = controller_path.join(device_file); + add_path_to_archive(&mut archive, &full_path)?; + } + + for hw_mon in &controller.handle.hw_monitors { + let hw_mon_path = hw_mon.get_path(); + let hw_mon_entries = + std::fs::read_dir(hw_mon_path).context("Could not read HwMon dir")?; + + 'entries: for entry in hw_mon_entries.flatten() { + if !entry.metadata().is_ok_and(|metadata| metadata.is_file()) { + continue; + } + + if let Some(name) = entry.file_name().to_str() { + for prefix in SNAPSHOT_HWMON_FILE_PREFIXES { + if name.starts_with(prefix) { + add_path_to_archive(&mut archive, &entry.path())?; + continue 'entries; + } + } + } + } + } + } + + let mut writer = archive.into_inner().context("Could not finish archive")?; + writer.flush().context("Could not flush output file")?; + + writer + .into_inner()? + .set_permissions(Permissions::from_mode(0o775)) + .context("Could not set permissions on output file")?; + + Ok(out_path) + } + pub fn confirm_pending_config(&self, command: ConfirmCommand) -> anyhow::Result<()> { if let Some(tx) = self .confirm_config_tx @@ -446,3 +546,30 @@ fn load_controllers() -> anyhow::Result> { Ok(controllers) } + +fn add_path_to_archive( + archive: &mut tar::Builder, + full_path: &Path, +) -> anyhow::Result<()> { + let archive_path = full_path + .strip_prefix("/") + .context("Path should always start at root")?; + + if let Ok(metadata) = std::fs::metadata(full_path) { + debug!("adding {full_path:?} to snapshot"); + let data = std::fs::read(full_path) + .with_context(|| format!("Could not read file at {full_path:?}"))?; + + let mut header = tar::Header::new_gnu(); + header.set_size(data.len().try_into().unwrap()); + header.set_mode(metadata.mode()); + header.set_uid(metadata.uid().into()); + header.set_gid(metadata.gid().into()); + header.set_cksum(); + + archive + .append_data(&mut header, archive_path, Cursor::new(data)) + .context("Could not write data to archive")?; + } + Ok(()) +} diff --git a/lact-daemon/src/server/mod.rs b/lact-daemon/src/server/mod.rs index 60bd90f..c7994ea 100644 --- a/lact-daemon/src/server/mod.rs +++ b/lact-daemon/src/server/mod.rs @@ -118,6 +118,7 @@ async fn handle_request<'a>(request: Request<'a>, handler: &'a Handler) -> anyho ok_response(handler.set_enabled_power_states(id, kind, states).await?) } Request::EnableOverdrive => ok_response(system::enable_overdrive()?), + Request::GenerateSnapshot => ok_response(handler.generate_snapshot()?), Request::ConfirmPendingConfig(command) => { ok_response(handler.confirm_pending_config(command)?) } diff --git a/lact-daemon/src/server/system.rs b/lact-daemon/src/server/system.rs index f2564ce..5951667 100644 --- a/lact-daemon/src/server/system.rs +++ b/lact-daemon/src/server/system.rs @@ -8,7 +8,7 @@ use std::{ }; const PP_OVERDRIVE_MASK: u64 = 0x4000; -const PP_FEATURE_MASK_PATH: &str = "/sys/module/amdgpu/parameters/ppfeaturemask"; +pub const PP_FEATURE_MASK_PATH: &str = "/sys/module/amdgpu/parameters/ppfeaturemask"; pub const MODULE_CONF_PATH: &str = "/etc/modprobe.d/99-amdgpu-overdrive.conf"; pub fn info() -> anyhow::Result> { diff --git a/lact-gui/src/app/header.rs b/lact-gui/src/app/header.rs index abd9e13..f107a77 100644 --- a/lact-gui/src/app/header.rs +++ b/lact-gui/src/app/header.rs @@ -13,16 +13,25 @@ pub struct Header { impl Header { pub fn new() -> Self { let container = HeaderBar::new(); - - container.set_title_widget(Some(&Box::default())); // Workaround to hide the title - container.set_show_title_buttons(true); + let switcher = StackSwitcher::new(); + container.set_title_widget(Some(&switcher)); + let gpu_selector = ComboBoxText::new(); container.pack_start(&gpu_selector); - let switcher = StackSwitcher::new(); - container.pack_start(&switcher); + let menu = gio::Menu::new(); + menu.append( + Some("Generate debug snapshot"), + Some("app.generate-debug-snapshot"), + ); + + let menu_button = MenuButton::builder() + .icon_name("open-menu-symbolic") + .menu_model(&menu) + .build(); + container.pack_end(&menu_button); Header { container, diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs index 27e7f9e..ad93496 100644 --- a/lact-gui/src/app/mod.rs +++ b/lact-gui/src/app/mod.rs @@ -8,6 +8,7 @@ use crate::{APP_ID, GUI_VERSION}; use anyhow::{anyhow, Context}; use apply_revealer::ApplyRevealer; use glib::clone; +use gtk::gio::ActionEntry; use gtk::glib::{timeout_future, ControlFlow}; use gtk::{gio::ApplicationFlags, prelude::*, *}; use header::Header; @@ -150,6 +151,14 @@ impl App { })); } + let snapshot_action = ActionEntry::builder("generate-debug-snapshot") + .activate(clone!(@strong app => move |_, _, _| { + app.generate_debug_snapshot(); + })) + .build(); + + app.application.add_action_entries([snapshot_action]); + app.start_stats_update_loop(current_gpu_id); app.window.show(); @@ -468,6 +477,54 @@ impl App { Ok(()) } + fn generate_debug_snapshot(&self) { + match self + .daemon_client + .generate_debug_snapshot() + .and_then(|response| response.inner()) + { + Ok(path) => { + let path_label = Label::builder() + .use_markup(true) + .label(format!("{path}")) + .selectable(true) + .build(); + + let vbox = Box::builder() + .orientation(Orientation::Vertical) + .margin_top(10) + .margin_bottom(10) + .margin_start(10) + .margin_end(10) + .build(); + + vbox.append(&Label::new(Some("Debug snapshot saved at:"))); + vbox.append(&path_label); + + let diag = MessageDialog::builder() + .title("Snapshot generated") + .message_type(MessageType::Info) + .use_markup(true) + .text(format!("Debug snapshot saved at {path}")) + .buttons(ButtonsType::Ok) + .transient_for(&self.window) + .build(); + + let message_box = diag.message_area().downcast::().unwrap(); + for child in message_box.observe_children().into_iter().flatten() { + if let Ok(label) = child.downcast::