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).
|
||||
// Otherwise, check the ac_connected status from the (first) battery.
|
||||
// XXX: This relies on the setting ac_connected in BatteryInfo being set correctly.
|
||||
let on_ac_power = report.batteries.is_empty()
|
||||
|| report.batteries.first().map_or(false, |b| b.ac_connected);
|
||||
let on_ac_power =
|
||||
report.batteries.is_empty() || report.batteries.first().is_some_and(|b| b.ac_connected);
|
||||
|
||||
if on_ac_power {
|
||||
println!("Engine: On AC power, selecting Charger profile.");
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
mod cli;
|
||||
mod config;
|
||||
mod conflict;
|
||||
mod core;
|
||||
|
@ -45,6 +46,8 @@ enum Commands {
|
|||
#[clap(value_enum)]
|
||||
setting: TurboSetting,
|
||||
},
|
||||
/// Display comprehensive debug information
|
||||
Debug,
|
||||
/// Set Energy Performance Preference (EPP)
|
||||
SetEpp {
|
||||
epp: String,
|
||||
|
@ -202,6 +205,7 @@ fn main() {
|
|||
Some(Commands::SetPlatformProfile { profile }) => cpu::set_platform_profile(&profile)
|
||||
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>),
|
||||
Some(Commands::Daemon { verbose }) => daemon::run_daemon(config, verbose),
|
||||
Some(Commands::Debug) => cli::debug::run_debug(&config),
|
||||
None => {
|
||||
println!("Welcome to superfreq! Use --help for commands.");
|
||||
println!("Current effective configuration: {config:?}");
|
||||
|
|
|
@ -417,7 +417,14 @@ pub fn get_cpu_global_info(cpu_cores: &[CpuCoreInfo]) -> CpuGlobalInfo {
|
|||
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() {
|
||||
// 0 means turbo enabled, 1 means disabled for intel_pstate
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue