blob: 65b5a5fff4a726ec21bd3436a2f8bc425c0a40e6 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use argh::{ArgsInfo, FromArgs};
use ffx_core::ffx_command;
use std::time::Duration;
#[ffx_command()]
#[derive(ArgsInfo, FromArgs, Debug, PartialEq)]
#[argh(
subcommand,
name = "activity",
description = "Controls the metrics-logger component to log network activity. Logged samples \
will be available in syslog, via iquery under core/metrics-logger and via tracing in the \
`metrics_logger` category.",
example = "\
To poll network activity every 500 ms indefinitely:
$ ffx profile network activity start --interval 500ms
To poll network activity every 1 second for 30 seconds with output-to-syslog enabled:
$ ffx profile network activity start --interval 1s -d 30s --output-to-syslog",
note = "\
If the metrics-logger component is not available to the target, then this command will not work
properly. Add --with //src/power/metrics-logger to fx set."
)]
/// Top-level command for "ffx profile network activity".
pub struct Command {
#[argh(subcommand)]
pub subcommand: SubCommand,
}
#[derive(ArgsInfo, FromArgs, PartialEq, Debug)]
#[argh(subcommand)]
pub enum SubCommand {
Start(StartCommand),
Stop(StopCommand),
}
#[derive(ArgsInfo, FromArgs, PartialEq, Debug)]
/// Start logging on the target
#[argh(subcommand, name = "start")]
pub struct StartCommand {
#[argh(option, long = "interval", short = 's', from_str_fn(parse_duration))]
/// interval for polling the network activity
pub interval: Duration,
#[argh(switch)]
/// toggle for logging samples to syslog
pub output_to_syslog: bool,
#[argh(option, long = "duration", short = 'd', from_str_fn(parse_duration))]
/// duration for which to log; if omitted, logging will continue indefinitely
pub duration: Option<Duration>,
}
#[derive(ArgsInfo, FromArgs, PartialEq, Debug)]
/// Stop logging on the target
#[argh(subcommand, name = "stop")]
pub struct StopCommand {}
const DURATION_REGEX: &'static str = r"^(\d+)(h|m|s|ms)$";
/// Parses a Duration from string.
fn parse_duration(value: &str) -> Result<Duration, String> {
let re = regex::Regex::new(DURATION_REGEX).unwrap();
let captures = re
.captures(&value)
.ok_or_else(|| format!("Durations must be specified in the form {}", DURATION_REGEX))?;
let number: u64 = captures[1].parse().unwrap();
let unit = &captures[2];
match unit {
"ms" => Ok(Duration::from_millis(number)),
"s" => Ok(Duration::from_secs(number)),
"m" => Ok(Duration::from_secs(number * 60)),
"h" => Ok(Duration::from_secs(number * 3600)),
_ => Err(format!(
"Invalid duration string \"{}\"; must be of the form {}",
value, DURATION_REGEX
)),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_duration() {
assert_eq!(parse_duration("1h"), Ok(Duration::from_secs(3600)));
assert_eq!(parse_duration("3m"), Ok(Duration::from_secs(180)));
assert_eq!(parse_duration("10s"), Ok(Duration::from_secs(10)));
assert_eq!(parse_duration("100ms"), Ok(Duration::from_millis(100)));
}
#[test]
fn test_parse_duration_err() {
assert!(parse_duration("100").is_err());
assert!(parse_duration("10 0").is_err());
assert!(parse_duration("foobar").is_err());
}
}