Skip to content

Commit

Permalink
Introduce run and replay subcommands
Browse files Browse the repository at this point in the history
  • Loading branch information
sile committed Oct 21, 2023
1 parent be53dde commit 6c1c2e0
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 31 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,11 @@ Usage
Just execute the following command:

```console
$ erldash $TARGET_ERLANG_NODE
$ erldash run $TARGET_ERLANG_NODE
```

If you need to specify a cookie value other than `$HOME/.erlang.cookie`, please specify that to `--cookie` option.

`$ erldash --help` shows the detailed help message.

You can record the collected metrics to a file via `--record <FILE>` option and replay the recorded run using `$ erldash replay <FILE>` command.
25 changes: 18 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,17 @@ pub mod erlang;
pub mod metrics;
pub mod ui;

#[derive(Debug, Clone, clap::Parser)]
pub struct Options {
#[derive(Debug, Clone, clap::Subcommand)]
pub enum Command {
/// Run the dashboard.
Run(RunArgs),

/// Replay a previously recorded dashboard session.
Replay(ReplayArgs),
}

#[derive(Debug, Clone, clap::Args)]
pub struct RunArgs {
/// Target Erlang node name.
pub erlang_node: erl_dist::node::NodeName,

Expand All @@ -22,13 +31,9 @@ pub struct Options {
/// If specified, the collected metrics will be recorded to the given file and can be replayed later.
#[clap(long, value_name = "FILE")]
pub record: Option<PathBuf>,

/// If specified, the recorded metrics will be replayed.
#[clap(long, requires = "record")]
pub replay: bool,
}

impl Options {
impl RunArgs {
pub fn find_cookie(&self) -> anyhow::Result<String> {
if let Some(cookie) = &self.cookie {
Ok(cookie.clone())
Expand All @@ -37,3 +42,9 @@ impl Options {
}
}
}

#[derive(Debug, Clone, clap::Args)]
pub struct ReplayArgs {
/// Path to a file containing recorded metrics.
pub file: PathBuf,
}
6 changes: 3 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use erldash::{metrics, ui};
#[derive(Debug, Parser)]
#[clap(version)]
struct Args {
#[clap(flatten)]
options: erldash::Options,
#[clap(subcommand)]
command: erldash::Command,

#[clap(hide = true, long)]
logfile: Option<std::path::PathBuf>,
Expand All @@ -23,7 +23,7 @@ fn main() -> anyhow::Result<()> {
let args = Args::parse();
setup_logger(&args)?;

let poller = metrics::MetricsPoller::start_thread(args.options)?;
let poller = metrics::MetricsPoller::start_thread(args.command)?;
let app = ui::App::new(poller)?;
app.run()?;
Ok(())
Expand Down
37 changes: 17 additions & 20 deletions src/metrics.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::erlang::{MSAccThread, RpcClient, SystemVersion};
use crate::Options;
use crate::{Command, ReplayArgs, RunArgs};
use anyhow::Context;
use serde::{Deserialize, Serialize};
use smol::fs::File;
Expand Down Expand Up @@ -241,11 +241,10 @@ pub enum MetricsPoller {
}

impl MetricsPoller {
pub fn start_thread(options: Options) -> anyhow::Result<Self> {
if options.replay {
ReplayMetricsPoller::new(options).map(Self::Replay)
} else {
RealtimeMetricsPoller::start_thread(options).map(Self::Realtime)
pub fn start_thread(command: Command) -> anyhow::Result<Self> {
match command {
Command::Run(args) => RealtimeMetricsPoller::start_thread(args).map(Self::Realtime),
Command::Replay(args) => ReplayMetricsPoller::new(args).map(Self::Replay),
}
}

Expand Down Expand Up @@ -309,10 +308,8 @@ pub struct ReplayMetricsPoller {
}

impl ReplayMetricsPoller {
fn new(options: Options) -> anyhow::Result<Self> {
let Some(record_file_path) = options.record else {
anyhow::bail!("`--record` is required for replay mode");
};
fn new(args: ReplayArgs) -> anyhow::Result<Self> {
let record_file_path = args.file;
let file = std::fs::File::open(&record_file_path).with_context(|| {
format!("failed to open record file: {}", record_file_path.display())
})?;
Expand Down Expand Up @@ -350,8 +347,8 @@ pub struct RealtimeMetricsPoller {
}

impl RealtimeMetricsPoller {
fn start_thread(options: Options) -> anyhow::Result<Self> {
MetricsPollerThread::start_thread(options)
fn start_thread(args: RunArgs) -> anyhow::Result<Self> {
MetricsPollerThread::start_thread(args)
}
}

Expand All @@ -372,7 +369,7 @@ impl Drop for RealtimeMetricsPoller {

#[derive(Debug)]
struct MetricsPollerThread {
options: Options,
args: RunArgs,
rpc_client: RpcClient,
tx: MetricsSender,
prev_metrics: Metrics,
Expand All @@ -382,12 +379,12 @@ struct MetricsPollerThread {
}

impl MetricsPollerThread {
fn start_thread(options: Options) -> anyhow::Result<RealtimeMetricsPoller> {
fn start_thread(args: RunArgs) -> anyhow::Result<RealtimeMetricsPoller> {
let (tx, rx) = mpsc::channel();

let rpc_client: RpcClient = smol::block_on(async {
let cookie = options.find_cookie()?;
let client = RpcClient::connect(&options.erlang_node, &cookie).await?;
let cookie = args.find_cookie()?;
let client = RpcClient::connect(&args.erlang_node, &cookie).await?;
Ok(client) as anyhow::Result<_>
})?;
let system_version = smol::block_on(rpc_client.get_system_version())?;
Expand All @@ -399,7 +396,7 @@ impl MetricsPollerThread {

let header = Header {
system_version: system_version.clone(),
node_name: options.erlang_node.to_string(),
node_name: args.erlang_node.to_string(),
start_time: chrono::Local::now(),
};
let poller = RealtimeMetricsPoller {
Expand All @@ -409,7 +406,7 @@ impl MetricsPollerThread {
old_microstate_accounting_flag,
};

let record_file = if let Some(path) = &options.record {
let record_file = if let Some(path) = &args.record {
Some(File::from(std::fs::File::create(path).with_context(
|| format!("failed to record file {}", path.display()),
)?))
Expand All @@ -420,7 +417,7 @@ impl MetricsPollerThread {
std::thread::spawn(|| {
let start = Instant::now();
Self {
options,
args,
rpc_client,
tx,
prev_metrics: Metrics::new(start),
Expand All @@ -444,7 +441,7 @@ impl MetricsPollerThread {
}

fn run(mut self) {
let interval = Duration::from_secs(self.options.polling_interval.get() as u64);
let interval = Duration::from_secs(self.args.polling_interval.get() as u64);
let mut next_time = Duration::from_secs(0);
smol::block_on(async {
if let Err(e) = self.write_json_line(&self.header.clone()).await {
Expand Down

0 comments on commit 6c1c2e0

Please sign in to comment.