mirror of
https://github.com/RGBCube/superfreq
synced 2025-07-27 17:07:44 +00:00
cli: more command logic to a dedicated module; add debug cmd
This commit is contained in:
parent
ea84a2997b
commit
dde938b638
6 changed files with 254 additions and 3 deletions
166
src/cli/debug.rs
Normal file
166
src/cli/debug.rs
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
use crate::config::AppConfig;
|
||||||
|
use crate::conflict;
|
||||||
|
use crate::cpu;
|
||||||
|
use crate::monitor;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
/// Prints comprehensive debug information about the system
|
||||||
|
pub fn run_debug(config: &AppConfig) -> Result<(), Box<dyn Error>> {
|
||||||
|
println!("=== SUPERFREQ DEBUG INFORMATION ===");
|
||||||
|
println!("Version: {}", env!("CARGO_PKG_VERSION"));
|
||||||
|
|
||||||
|
// Current date and time
|
||||||
|
let now = std::time::SystemTime::now();
|
||||||
|
println!("Timestamp: {now:?}");
|
||||||
|
|
||||||
|
// Get system information and conflicts
|
||||||
|
match monitor::collect_system_report(config) {
|
||||||
|
Ok(report) => {
|
||||||
|
println!("\n--- SYSTEM INFORMATION ---");
|
||||||
|
println!("CPU Model: {}", report.system_info.cpu_model);
|
||||||
|
println!("Architecture: {}", report.system_info.architecture);
|
||||||
|
println!(
|
||||||
|
"Linux Distribution: {}",
|
||||||
|
report.system_info.linux_distribution
|
||||||
|
);
|
||||||
|
|
||||||
|
println!("\n--- CONFIGURATION ---");
|
||||||
|
println!("Current Configuration: {config:#?}");
|
||||||
|
|
||||||
|
println!("\n--- CPU INFORMATION ---");
|
||||||
|
println!("Current Governor: {:?}", report.cpu_global.current_governor);
|
||||||
|
println!(
|
||||||
|
"Available Governors: {}",
|
||||||
|
report.cpu_global.available_governors.join(", ")
|
||||||
|
);
|
||||||
|
println!("Turbo Status: {:?}", report.cpu_global.turbo_status);
|
||||||
|
println!(
|
||||||
|
"Energy Performance Preference (EPP): {:?}",
|
||||||
|
report.cpu_global.epp
|
||||||
|
);
|
||||||
|
println!("Energy Performance Bias (EPB): {:?}", report.cpu_global.epb);
|
||||||
|
|
||||||
|
// Add governor override information
|
||||||
|
if let Some(override_governor) = cpu::get_governor_override() {
|
||||||
|
println!("Governor Override: {}", override_governor.trim());
|
||||||
|
} else {
|
||||||
|
println!("Governor Override: None");
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("\n--- PLATFORM PROFILE ---");
|
||||||
|
println!(
|
||||||
|
"Current Platform Profile: {:?}",
|
||||||
|
report.cpu_global.platform_profile
|
||||||
|
);
|
||||||
|
match cpu::get_platform_profiles() {
|
||||||
|
Ok(profiles) => println!("Available Platform Profiles: {}", profiles.join(", ")),
|
||||||
|
Err(_) => println!("Available Platform Profiles: Not supported on this system"),
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("\n--- CPU CORES DETAIL ---");
|
||||||
|
println!("Total CPU Cores: {}", report.cpu_cores.len());
|
||||||
|
for core in &report.cpu_cores {
|
||||||
|
println!("Core {}:", core.core_id);
|
||||||
|
println!(
|
||||||
|
" Current Frequency: {} MHz",
|
||||||
|
core.current_frequency_mhz
|
||||||
|
.map_or_else(|| "N/A".to_string(), |f| f.to_string())
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
" Min Frequency: {} MHz",
|
||||||
|
core.min_frequency_mhz
|
||||||
|
.map_or_else(|| "N/A".to_string(), |f| f.to_string())
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
" Max Frequency: {} MHz",
|
||||||
|
core.max_frequency_mhz
|
||||||
|
.map_or_else(|| "N/A".to_string(), |f| f.to_string())
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
" Usage: {}%",
|
||||||
|
core.usage_percent
|
||||||
|
.map_or_else(|| "N/A".to_string(), |u| format!("{u:.1}"))
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
" Temperature: {}°C",
|
||||||
|
core.temperature_celsius
|
||||||
|
.map_or_else(|| "N/A".to_string(), |t| format!("{t:.1}"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("\n--- TEMPERATURE INFORMATION ---");
|
||||||
|
println!(
|
||||||
|
"Average CPU Temperature: {}",
|
||||||
|
report.cpu_global.average_temperature_celsius.map_or_else(
|
||||||
|
|| "N/A (CPU temperature sensor not detected)".to_string(),
|
||||||
|
|t| format!("{t:.1}°C")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
println!("\n--- BATTERY INFORMATION ---");
|
||||||
|
if report.batteries.is_empty() {
|
||||||
|
println!("No batteries found or all are ignored.");
|
||||||
|
} else {
|
||||||
|
for battery in &report.batteries {
|
||||||
|
println!("Battery: {}", battery.name);
|
||||||
|
println!(" AC Connected: {}", battery.ac_connected);
|
||||||
|
println!(
|
||||||
|
" Charging State: {}",
|
||||||
|
battery.charging_state.as_deref().unwrap_or("N/A")
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
" Capacity: {}%",
|
||||||
|
battery
|
||||||
|
.capacity_percent
|
||||||
|
.map_or_else(|| "N/A".to_string(), |c| c.to_string())
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
" Power Rate: {} W",
|
||||||
|
battery
|
||||||
|
.power_rate_watts
|
||||||
|
.map_or_else(|| "N/A".to_string(), |p| format!("{p:.2}"))
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
" Charge Start Threshold: {}",
|
||||||
|
battery
|
||||||
|
.charge_start_threshold
|
||||||
|
.map_or_else(|| "N/A".to_string(), |t| t.to_string())
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
" Charge Stop Threshold: {}",
|
||||||
|
battery
|
||||||
|
.charge_stop_threshold
|
||||||
|
.map_or_else(|| "N/A".to_string(), |t| t.to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("\n--- SYSTEM LOAD ---");
|
||||||
|
println!(
|
||||||
|
"Load Average (1 min): {:.2}",
|
||||||
|
report.system_load.load_avg_1min
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"Load Average (5 min): {:.2}",
|
||||||
|
report.system_load.load_avg_5min
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"Load Average (15 min): {:.2}",
|
||||||
|
report.system_load.load_avg_15min
|
||||||
|
);
|
||||||
|
|
||||||
|
println!("\n--- CONFLICT DETECTION ---");
|
||||||
|
let conflicts = conflict::detect_conflicts();
|
||||||
|
println!("{}", conflicts.get_conflict_message());
|
||||||
|
|
||||||
|
println!("\n--- DAEMON STATUS ---");
|
||||||
|
// Simple check for daemon status - can be expanded later
|
||||||
|
let daemon_status = fs::metadata("/var/run/superfreq.pid").is_ok();
|
||||||
|
println!("Daemon Running: {daemon_status}");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => Err(Box::new(e) as Box<dyn Error>),
|
||||||
|
}
|
||||||
|
}
|
1
src/cli/mod.rs
Normal file
1
src/cli/mod.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod debug;
|
73
src/config/watcher.rs
Normal file
73
src/config/watcher.rs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
use notify::{Config, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
|
||||||
|
use std::path::Path;
|
||||||
|
use std::sync::mpsc::{channel, Receiver};
|
||||||
|
use std::time::Duration;
|
||||||
|
use std::thread;
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
use crate::config::{load_config, AppConfig};
|
||||||
|
|
||||||
|
/// Watches a configuration file for changes and reloads it when modified
|
||||||
|
pub struct ConfigWatcher {
|
||||||
|
rx: Receiver<Result<Event, notify::Error>>,
|
||||||
|
_watcher: RecommendedWatcher, // keep watcher alive while watching
|
||||||
|
config_path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConfigWatcher {
|
||||||
|
/// Initialize a new config watcher for the given path
|
||||||
|
pub fn new(config_path: &str) -> Result<Self, notify::Error> {
|
||||||
|
let (tx, rx) = channel();
|
||||||
|
|
||||||
|
// Create a watcher with default config
|
||||||
|
let mut watcher = RecommendedWatcher::new(tx, Config::default())?;
|
||||||
|
|
||||||
|
// Start watching the config file
|
||||||
|
watcher.watch(Path::new(config_path), RecursiveMode::NonRecursive)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
rx,
|
||||||
|
_watcher: watcher,
|
||||||
|
config_path: config_path.to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check for config file changes and reload if necessary
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// `Some(AppConfig)` if the config was reloaded, `None`` otherwise
|
||||||
|
pub fn check_for_changes(&self) -> Option<Result<AppConfig, Box<dyn Error>>> {
|
||||||
|
// Non-blocking check for file events
|
||||||
|
match self.rx.try_recv() {
|
||||||
|
Ok(Ok(event)) => {
|
||||||
|
// Only process write/modify events
|
||||||
|
if matches!(event.kind, EventKind::Modify(_)) {
|
||||||
|
// Add a small delay to ensure the file write is complete
|
||||||
|
thread::sleep(Duration::from_millis(100));
|
||||||
|
|
||||||
|
// Attempt to reload the config
|
||||||
|
match load_config() {
|
||||||
|
Ok(config) => {
|
||||||
|
println!("Configuration file changed. Reloaded configuration.");
|
||||||
|
Some(Ok(config))
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error reloading configuration: {e}");
|
||||||
|
Some(Err(Box::new(e)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// No events or channel errors
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the path of the config file being watched
|
||||||
|
pub const fn config_path(&self) -> &String {
|
||||||
|
&self.config_path
|
||||||
|
}
|
||||||
|
}
|
|
@ -67,8 +67,8 @@ pub fn determine_and_apply_settings(
|
||||||
// If no batteries, assume AC power (desktop).
|
// If no batteries, assume AC power (desktop).
|
||||||
// Otherwise, check the ac_connected status from the (first) battery.
|
// Otherwise, check the ac_connected status from the (first) battery.
|
||||||
// XXX: This relies on the setting ac_connected in BatteryInfo being set correctly.
|
// XXX: This relies on the setting ac_connected in BatteryInfo being set correctly.
|
||||||
let on_ac_power = report.batteries.is_empty()
|
let on_ac_power =
|
||||||
|| report.batteries.first().map_or(false, |b| b.ac_connected);
|
report.batteries.is_empty() || report.batteries.first().is_some_and(|b| b.ac_connected);
|
||||||
|
|
||||||
if on_ac_power {
|
if on_ac_power {
|
||||||
println!("Engine: On AC power, selecting Charger profile.");
|
println!("Engine: On AC power, selecting Charger profile.");
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
mod cli;
|
||||||
mod config;
|
mod config;
|
||||||
mod conflict;
|
mod conflict;
|
||||||
mod core;
|
mod core;
|
||||||
|
@ -45,6 +46,8 @@ enum Commands {
|
||||||
#[clap(value_enum)]
|
#[clap(value_enum)]
|
||||||
setting: TurboSetting,
|
setting: TurboSetting,
|
||||||
},
|
},
|
||||||
|
/// Display comprehensive debug information
|
||||||
|
Debug,
|
||||||
/// Set Energy Performance Preference (EPP)
|
/// Set Energy Performance Preference (EPP)
|
||||||
SetEpp {
|
SetEpp {
|
||||||
epp: String,
|
epp: String,
|
||||||
|
@ -202,6 +205,7 @@ fn main() {
|
||||||
Some(Commands::SetPlatformProfile { profile }) => cpu::set_platform_profile(&profile)
|
Some(Commands::SetPlatformProfile { profile }) => cpu::set_platform_profile(&profile)
|
||||||
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>),
|
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>),
|
||||||
Some(Commands::Daemon { verbose }) => daemon::run_daemon(config, verbose),
|
Some(Commands::Daemon { verbose }) => daemon::run_daemon(config, verbose),
|
||||||
|
Some(Commands::Debug) => cli::debug::run_debug(&config),
|
||||||
None => {
|
None => {
|
||||||
println!("Welcome to superfreq! Use --help for commands.");
|
println!("Welcome to superfreq! Use --help for commands.");
|
||||||
println!("Current effective configuration: {config:?}");
|
println!("Current effective configuration: {config:?}");
|
||||||
|
|
|
@ -417,7 +417,14 @@ pub fn get_cpu_global_info(cpu_cores: &[CpuCoreInfo]) -> CpuGlobalInfo {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let available_governors = get_platform_profiles().unwrap_or_else(|_| vec![]);
|
let available_governors = if cpufreq_base.join("scaling_available_governors").exists() {
|
||||||
|
read_sysfs_file_trimmed(cpufreq_base.join("scaling_available_governors")).map_or_else(
|
||||||
|
|_| vec![],
|
||||||
|
|s| s.split_whitespace().map(String::from).collect(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
|
||||||
let turbo_status = if turbo_status_path.exists() {
|
let turbo_status = if turbo_status_path.exists() {
|
||||||
// 0 means turbo enabled, 1 means disabled for intel_pstate
|
// 0 means turbo enabled, 1 means disabled for intel_pstate
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue