mirror of
https://gitlab.steamos.cloud/holo/steamos-manager.git
synced 2025-07-05 22:20:33 -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"
|
||||
edition = "2021"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3"
|
||||
|
||||
[profile.release]
|
||||
strip="symbols"
|
||||
|
||||
|
@ -22,6 +19,7 @@ nix = { version = "0.29", default-features = false, features = ["fs", "poll", "s
|
|||
num_enum = "0.7"
|
||||
regex = "1"
|
||||
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-stream = { version = "0.1", default-features = false }
|
||||
tokio-util = { version = "0.7", default-features = false }
|
||||
|
|
|
@ -361,6 +361,19 @@
|
|||
-->
|
||||
<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>
|
||||
|
||||
<!--
|
||||
|
|
|
@ -133,6 +133,9 @@ enum Commands {
|
|||
/// Get Wi-Fi debug mode
|
||||
GetWifiDebugMode,
|
||||
|
||||
/// Capture the current Wi-Fi debug trace
|
||||
CaptureWifiDebugTraceOutput,
|
||||
|
||||
/// Set the Wi-Fi power management state
|
||||
SetWifiPowerManagementState {
|
||||
/// Valid modes are `enabled`, `disabled`
|
||||
|
@ -380,6 +383,11 @@ async fn main() -> Result<()> {
|
|||
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 } => {
|
||||
let proxy = WifiPowerManagement1Proxy::new(&conn).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::wifi::{
|
||||
set_wifi_backend, set_wifi_debug_mode, set_wifi_power_management_state, WifiBackend,
|
||||
WifiDebugMode, WifiPowerManagement,
|
||||
extract_wifi_trace, set_wifi_backend, set_wifi_debug_mode, set_wifi_power_management_state,
|
||||
WifiBackend, WifiDebugMode, WifiPowerManagement,
|
||||
};
|
||||
use crate::{path, API_VERSION};
|
||||
|
||||
|
@ -369,6 +369,16 @@ impl SteamOSManager {
|
|||
.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)]
|
||||
async fn inhibit_ds(&self) -> fdo::Result<bool> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
|
|
@ -486,6 +486,10 @@ impl WifiDebug1 {
|
|||
async fn set_wifi_backend(&self, backend: &str) -> zbus::Result<()> {
|
||||
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")]
|
||||
|
|
|
@ -7,11 +7,18 @@
|
|||
|
||||
use anyhow::{anyhow, Result};
|
||||
use std::ffi::OsStr;
|
||||
use tokio::process::ChildStdout;
|
||||
|
||||
#[cfg(not(test))]
|
||||
use std::process::Stdio;
|
||||
#[cfg(not(test))]
|
||||
use tokio::process::Command;
|
||||
|
||||
#[cfg(test)]
|
||||
use nix::{fcntl::OFlag, unistd::pipe2};
|
||||
#[cfg(test)]
|
||||
use std::{fs::File, io::Write};
|
||||
|
||||
#[cfg(not(test))]
|
||||
pub async fn script_exit_code(
|
||||
executable: impl AsRef<OsStr>,
|
||||
|
@ -73,6 +80,35 @@ pub async fn script_output(
|
|||
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)]
|
||||
pub(crate) mod test {
|
||||
use super::*;
|
||||
|
|
|
@ -19,6 +19,9 @@ use zbus::proxy;
|
|||
assume_defaults = true
|
||||
)]
|
||||
pub trait WifiDebug1 {
|
||||
/// CaptureDebugTraceOutput method
|
||||
fn capture_debug_trace_output(&self) -> zbus::Result<String>;
|
||||
|
||||
/// SetWifiDebugMode method
|
||||
fn set_wifi_debug_mode(
|
||||
&self,
|
||||
|
|
55
src/wifi.rs
55
src/wifi.rs
|
@ -8,14 +8,20 @@
|
|||
use anyhow::{bail, ensure, Result};
|
||||
use config::builder::AsyncState;
|
||||
use config::{ConfigBuilder, FileFormat};
|
||||
use nix::sys::stat::{self, Mode};
|
||||
use num_enum::TryFromPrimitive;
|
||||
use std::fs::Permissions;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use strum::{Display, EnumString};
|
||||
use tempfile::Builder as TempFileBuilder;
|
||||
use tokio::fs;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tracing::error;
|
||||
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::{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
|
||||
// to have multiple files, etc.
|
||||
const OUTPUT_FILE: &str = "/var/log/wifitrace.dat";
|
||||
const TRACE_CMD_PATH: &str = "/usr/bin/trace-cmd";
|
||||
|
||||
const MIN_BUFFER_SIZE: u32 = 100;
|
||||
|
@ -114,10 +119,7 @@ async fn restart_iwd(connection: Connection) -> Result<()> {
|
|||
}
|
||||
|
||||
async fn stop_tracing() -> Result<()> {
|
||||
// Stop tracing and extract ring buffer to disk for capture
|
||||
run_script(TRACE_CMD_PATH, &["stop"]).await?;
|
||||
// stop tracing worked
|
||||
run_script(TRACE_CMD_PATH, &["extract", "-o", OUTPUT_FILE]).await
|
||||
run_script(TRACE_CMD_PATH, &["stop"]).await
|
||||
}
|
||||
|
||||
async fn start_tracing(buffer_size: u32) -> Result<()> {
|
||||
|
@ -130,6 +132,32 @@ async fn start_tracing(buffer_size: u32) -> Result<()> {
|
|||
.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(
|
||||
mode: WifiDebugMode,
|
||||
buffer_size: u32,
|
||||
|
@ -469,4 +497,19 @@ mod test {
|
|||
assert!(WifiBackend::try_from(2).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