mirror of
https://gitlab.steamos.cloud/holo/steamos-manager.git
synced 2025-07-06 06:30:27 -04:00
manager: Expose new CaptureDebugTraceOutput method
This commit is contained in:
parent
a2af4d1bc5
commit
7b7afffc46
8 changed files with 126 additions and 11 deletions
|
@ -3,9 +3,6 @@ name = "steamos-manager"
|
||||||
version = "24.5.1"
|
version = "24.5.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
tempfile = "3"
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
strip="symbols"
|
strip="symbols"
|
||||||
|
|
||||||
|
@ -22,6 +19,7 @@ nix = { version = "0.29", default-features = false, features = ["fs", "poll", "s
|
||||||
num_enum = "0.7"
|
num_enum = "0.7"
|
||||||
regex = "1"
|
regex = "1"
|
||||||
serde = { version = "1.0", default-features = false, features = ["derive"] }
|
serde = { version = "1.0", default-features = false, features = ["derive"] }
|
||||||
|
tempfile = "3"
|
||||||
tokio = { version = "1", default-features = false, features = ["fs", "io-std", "io-util", "macros", "process", "rt-multi-thread", "signal", "sync"] }
|
tokio = { version = "1", default-features = false, features = ["fs", "io-std", "io-util", "macros", "process", "rt-multi-thread", "signal", "sync"] }
|
||||||
tokio-stream = { version = "0.1", default-features = false }
|
tokio-stream = { version = "0.1", default-features = false }
|
||||||
tokio-util = { version = "0.7", default-features = false }
|
tokio-util = { version = "0.7", default-features = false }
|
||||||
|
|
|
@ -361,6 +361,19 @@
|
||||||
-->
|
-->
|
||||||
<property name="WifiBackend" type="s" access="readwrite"/>
|
<property name="WifiBackend" type="s" access="readwrite"/>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
CaptureDebugTraceOutput:
|
||||||
|
|
||||||
|
@path: The path to the captured file. This will be world-readable and
|
||||||
|
in a temporary directory, so make sure to move it to a permanent
|
||||||
|
location if keeping it is desired.
|
||||||
|
|
||||||
|
Extract the Wi-Fi trace to a file, if present.
|
||||||
|
-->
|
||||||
|
<method name="CaptureDebugTraceOutput">
|
||||||
|
<arg type="s" name="path" direction="out"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
</interface>
|
</interface>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
|
|
@ -133,6 +133,9 @@ enum Commands {
|
||||||
/// Get Wi-Fi debug mode
|
/// Get Wi-Fi debug mode
|
||||||
GetWifiDebugMode,
|
GetWifiDebugMode,
|
||||||
|
|
||||||
|
/// Capture the current Wi-Fi debug trace
|
||||||
|
CaptureWifiDebugTraceOutput,
|
||||||
|
|
||||||
/// Set the Wi-Fi power management state
|
/// Set the Wi-Fi power management state
|
||||||
SetWifiPowerManagementState {
|
SetWifiPowerManagementState {
|
||||||
/// Valid modes are `enabled`, `disabled`
|
/// Valid modes are `enabled`, `disabled`
|
||||||
|
@ -380,6 +383,11 @@ async fn main() -> Result<()> {
|
||||||
Err(_) => println!("Got unknown value {mode} from backend"),
|
Err(_) => println!("Got unknown value {mode} from backend"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Commands::CaptureWifiDebugTraceOutput => {
|
||||||
|
let proxy = WifiDebugDump1Proxy::new(&conn).await?;
|
||||||
|
let path = proxy.generate_debug_dump().await?;
|
||||||
|
println!("{path}");
|
||||||
|
}
|
||||||
Commands::SetWifiPowerManagementState { state } => {
|
Commands::SetWifiPowerManagementState { state } => {
|
||||||
let proxy = WifiPowerManagement1Proxy::new(&conn).await?;
|
let proxy = WifiPowerManagement1Proxy::new(&conn).await?;
|
||||||
proxy.set_wifi_power_management_state(*state as u32).await?;
|
proxy.set_wifi_power_management_state(*state as u32).await?;
|
||||||
|
|
|
@ -29,8 +29,8 @@ use crate::power::{
|
||||||
};
|
};
|
||||||
use crate::process::{run_script, script_output};
|
use crate::process::{run_script, script_output};
|
||||||
use crate::wifi::{
|
use crate::wifi::{
|
||||||
set_wifi_backend, set_wifi_debug_mode, set_wifi_power_management_state, WifiBackend,
|
extract_wifi_trace, set_wifi_backend, set_wifi_debug_mode, set_wifi_power_management_state,
|
||||||
WifiDebugMode, WifiPowerManagement,
|
WifiBackend, WifiDebugMode, WifiPowerManagement,
|
||||||
};
|
};
|
||||||
use crate::{path, API_VERSION};
|
use crate::{path, API_VERSION};
|
||||||
|
|
||||||
|
@ -369,6 +369,16 @@ impl SteamOSManager {
|
||||||
.map_err(to_zbus_fdo_error)
|
.map_err(to_zbus_fdo_error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn capture_debug_trace_output(&self) -> fdo::Result<String> {
|
||||||
|
Ok(extract_wifi_trace()
|
||||||
|
.await
|
||||||
|
.inspect_err(|message| error!("Error capturing trace output: {message}"))
|
||||||
|
.map_err(to_zbus_fdo_error)?
|
||||||
|
.into_os_string()
|
||||||
|
.to_string_lossy()
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
|
||||||
#[zbus(property)]
|
#[zbus(property)]
|
||||||
async fn inhibit_ds(&self) -> fdo::Result<bool> {
|
async fn inhibit_ds(&self) -> fdo::Result<bool> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
|
|
|
@ -486,6 +486,10 @@ impl WifiDebug1 {
|
||||||
async fn set_wifi_backend(&self, backend: &str) -> zbus::Result<()> {
|
async fn set_wifi_backend(&self, backend: &str) -> zbus::Result<()> {
|
||||||
self.proxy.call("SetWifiBackend", &(backend)).await
|
self.proxy.call("SetWifiBackend", &(backend)).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn capture_debug_trace_output(&self) -> fdo::Result<String> {
|
||||||
|
method!(self, "CaptureDebugTraceOutput")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[interface(name = "com.steampowered.SteamOSManager1.WifiPowerManagement1")]
|
#[interface(name = "com.steampowered.SteamOSManager1.WifiPowerManagement1")]
|
||||||
|
|
|
@ -7,11 +7,18 @@
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
|
use tokio::process::ChildStdout;
|
||||||
|
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use nix::{fcntl::OFlag, unistd::pipe2};
|
||||||
|
#[cfg(test)]
|
||||||
|
use std::{fs::File, io::Write};
|
||||||
|
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
pub async fn script_exit_code(
|
pub async fn script_exit_code(
|
||||||
executable: impl AsRef<OsStr>,
|
executable: impl AsRef<OsStr>,
|
||||||
|
@ -73,6 +80,35 @@ pub async fn script_output(
|
||||||
cb(executable.as_ref(), args.as_ref()).map(|(_, res)| res)
|
cb(executable.as_ref(), args.as_ref()).map(|(_, res)| res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
|
pub async fn script_pipe_output(
|
||||||
|
executable: impl AsRef<OsStr>,
|
||||||
|
args: &[impl AsRef<OsStr>],
|
||||||
|
) -> Result<ChildStdout> {
|
||||||
|
// Run given command and return the output given, as an fd instead of a String
|
||||||
|
let child = Command::new(executable)
|
||||||
|
.args(args)
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::null())
|
||||||
|
.spawn()?;
|
||||||
|
|
||||||
|
child.stdout.ok_or(anyhow!("Failed to get stdout"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub async fn script_pipe_output(
|
||||||
|
executable: impl AsRef<OsStr>,
|
||||||
|
args: &[impl AsRef<OsStr>],
|
||||||
|
) -> Result<ChildStdout> {
|
||||||
|
let test = crate::testing::current();
|
||||||
|
let args: Vec<&OsStr> = args.iter().map(std::convert::AsRef::as_ref).collect();
|
||||||
|
let cb = test.process_cb.get();
|
||||||
|
let string = cb(executable.as_ref(), args.as_ref()).map(|(_, res)| res)?;
|
||||||
|
let (rx, tx) = pipe2(OFlag::O_CLOEXEC)?;
|
||||||
|
File::from(tx).write(string.as_bytes())?;
|
||||||
|
Ok(ChildStdout::from_std(std::process::ChildStdout::from(rx))?)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod test {
|
pub(crate) mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -19,6 +19,9 @@ use zbus::proxy;
|
||||||
assume_defaults = true
|
assume_defaults = true
|
||||||
)]
|
)]
|
||||||
pub trait WifiDebug1 {
|
pub trait WifiDebug1 {
|
||||||
|
/// CaptureDebugTraceOutput method
|
||||||
|
fn capture_debug_trace_output(&self) -> zbus::Result<String>;
|
||||||
|
|
||||||
/// SetWifiDebugMode method
|
/// SetWifiDebugMode method
|
||||||
fn set_wifi_debug_mode(
|
fn set_wifi_debug_mode(
|
||||||
&self,
|
&self,
|
||||||
|
|
55
src/wifi.rs
55
src/wifi.rs
|
@ -8,14 +8,20 @@
|
||||||
use anyhow::{bail, ensure, Result};
|
use anyhow::{bail, ensure, Result};
|
||||||
use config::builder::AsyncState;
|
use config::builder::AsyncState;
|
||||||
use config::{ConfigBuilder, FileFormat};
|
use config::{ConfigBuilder, FileFormat};
|
||||||
|
use nix::sys::stat::{self, Mode};
|
||||||
use num_enum::TryFromPrimitive;
|
use num_enum::TryFromPrimitive;
|
||||||
|
use std::fs::Permissions;
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use strum::{Display, EnumString};
|
use strum::{Display, EnumString};
|
||||||
|
use tempfile::Builder as TempFileBuilder;
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
use zbus::Connection;
|
use zbus::Connection;
|
||||||
|
|
||||||
use crate::process::{run_script, script_output};
|
use crate::process::{run_script, script_output, script_pipe_output};
|
||||||
use crate::systemd::{daemon_reload, SystemdUnit};
|
use crate::systemd::{daemon_reload, SystemdUnit};
|
||||||
use crate::{path, read_config_directory};
|
use crate::{path, read_config_directory};
|
||||||
|
|
||||||
|
@ -28,7 +34,6 @@ const OVERRIDE_PATH: &str = "/etc/systemd/system/iwd.service.d/99-valve-override
|
||||||
|
|
||||||
// Only use one path for output for now. If needed we can add a timestamp later
|
// Only use one path for output for now. If needed we can add a timestamp later
|
||||||
// to have multiple files, etc.
|
// to have multiple files, etc.
|
||||||
const OUTPUT_FILE: &str = "/var/log/wifitrace.dat";
|
|
||||||
const TRACE_CMD_PATH: &str = "/usr/bin/trace-cmd";
|
const TRACE_CMD_PATH: &str = "/usr/bin/trace-cmd";
|
||||||
|
|
||||||
const MIN_BUFFER_SIZE: u32 = 100;
|
const MIN_BUFFER_SIZE: u32 = 100;
|
||||||
|
@ -114,10 +119,7 @@ async fn restart_iwd(connection: Connection) -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn stop_tracing() -> Result<()> {
|
async fn stop_tracing() -> Result<()> {
|
||||||
// Stop tracing and extract ring buffer to disk for capture
|
run_script(TRACE_CMD_PATH, &["stop"]).await
|
||||||
run_script(TRACE_CMD_PATH, &["stop"]).await?;
|
|
||||||
// stop tracing worked
|
|
||||||
run_script(TRACE_CMD_PATH, &["extract", "-o", OUTPUT_FILE]).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn start_tracing(buffer_size: u32) -> Result<()> {
|
async fn start_tracing(buffer_size: u32) -> Result<()> {
|
||||||
|
@ -130,6 +132,32 @@ async fn start_tracing(buffer_size: u32) -> Result<()> {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_tempfile(prefix: &str) -> Result<(fs::File, PathBuf)> {
|
||||||
|
let umask = stat::umask(Mode::from_bits_truncate(0));
|
||||||
|
let output = TempFileBuilder::new()
|
||||||
|
.prefix(prefix)
|
||||||
|
.permissions(Permissions::from_mode(0o666))
|
||||||
|
.tempfile()?;
|
||||||
|
let (output, path) = output.keep()?;
|
||||||
|
let output = fs::File::from_std(output);
|
||||||
|
stat::umask(umask);
|
||||||
|
|
||||||
|
Ok((output, path))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn extract_wifi_trace() -> Result<PathBuf> {
|
||||||
|
let (mut output, path) = make_tempfile("wifi-trace-")?;
|
||||||
|
let mut pipe = script_pipe_output("trace-cmd", &["extract"]).await?;
|
||||||
|
let mut buf = [0; 4096];
|
||||||
|
loop {
|
||||||
|
let read = pipe.read(&mut buf).await?;
|
||||||
|
if read == 0 {
|
||||||
|
break Ok(path);
|
||||||
|
}
|
||||||
|
output.write_all(&buf[..read]).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn set_wifi_debug_mode(
|
pub(crate) async fn set_wifi_debug_mode(
|
||||||
mode: WifiDebugMode,
|
mode: WifiDebugMode,
|
||||||
buffer_size: u32,
|
buffer_size: u32,
|
||||||
|
@ -469,4 +497,19 @@ mod test {
|
||||||
assert!(WifiBackend::try_from(2).is_err());
|
assert!(WifiBackend::try_from(2).is_err());
|
||||||
assert!(WifiBackend::from_str("iwl").is_err());
|
assert!(WifiBackend::from_str("iwl").is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn trace_extract() {
|
||||||
|
let h = testing::start();
|
||||||
|
|
||||||
|
fn process_output(_: &OsStr, _: &[&OsStr]) -> Result<(i32, String)> {
|
||||||
|
Ok((0, String::from("output")))
|
||||||
|
}
|
||||||
|
h.test.process_cb.set(process_output);
|
||||||
|
|
||||||
|
let pathbuf = extract_wifi_trace().await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(fs::read_to_string(&pathbuf).await.unwrap(), "output");
|
||||||
|
fs::remove_file(pathbuf).await.unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue