Skip to main content

qemu_command_builder/args/
icount.rs

1use std::path::PathBuf;
2use std::str::FromStr;
3
4use bon::Builder;
5use proptest_derive::Arbitrary;
6
7use crate::common::OnOff;
8use crate::parsers::{ARG_ICOUNT, DELIM_COMMA};
9use crate::to_command::{ToArg, ToCommand};
10
11#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
12pub enum Shift {
13    N(usize),
14    Auto,
15}
16
17/// Record/replay mode for `-icount`.
18#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
19pub enum RecordReplay {
20    Record,
21    Replay,
22}
23
24impl ToArg for RecordReplay {
25    fn to_arg(&self) -> &str {
26        match self {
27            RecordReplay::Record => "record",
28            RecordReplay::Replay => "replay",
29        }
30    }
31}
32/// A QEMU `-icount` definition.
33#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder, Arbitrary)]
34pub struct Icount {
35    /// Virtual time shift.
36    shift: Option<Shift>,
37    /// Host/guest clock alignment policy.
38    align: Option<OnOff>,
39    /// Virtual sleep policy.
40    sleep: Option<OnOff>,
41    /// Record or replay mode.
42    rr: Option<RecordReplay>,
43    /// Record/replay log filename.
44    rrfile: Option<PathBuf>,
45    /// Initial snapshot name for record/replay.
46    rrsnapshot: Option<String>,
47}
48
49impl ToCommand for Icount {
50    fn command(&self) -> String {
51        ARG_ICOUNT.to_string()
52    }
53    fn to_args(&self) -> Vec<String> {
54        let mut args = vec![];
55
56        if let Some(shift) = &self.shift {
57            match shift {
58                Shift::N(size) => {
59                    args.push(format!("shift={}", size));
60                }
61                Shift::Auto => {
62                    args.push("shift=auto".to_string());
63                }
64            }
65        }
66        if let Some(align) = &self.align {
67            args.push(format!("align={}", align.to_arg()));
68        }
69        if let Some(sleep) = &self.sleep {
70            args.push(format!("sleep={}", sleep.to_arg()));
71        }
72        if let Some(rr) = &self.rr {
73            args.push(format!("rr={}", rr.to_arg()));
74        }
75        if let Some(rrfile) = &self.rrfile {
76            args.push(format!("rrfile={}", rrfile.display()));
77        }
78        if let Some(rrsnapshot) = &self.rrsnapshot {
79            args.push(format!("rrsnapshot={}", rrsnapshot));
80        }
81
82        vec![args.join(DELIM_COMMA)]
83    }
84}
85
86impl FromStr for Icount {
87    type Err = String;
88
89    fn from_str(s: &str) -> Result<Self, Self::Err> {
90        let mut shift = None;
91        let mut align = None;
92        let mut sleep = None;
93        let mut rr = None;
94        let mut rrfile = None;
95        let mut rrsnapshot = None;
96
97        for part in s.split(DELIM_COMMA).filter(|part| !part.is_empty()) {
98            let (key, value) = part.split_once('=').ok_or_else(|| format!("invalid -icount option: {part}"))?;
99            match key {
100                "shift" => {
101                    shift = Some(if value == "auto" {
102                        Shift::Auto
103                    } else {
104                        Shift::N(value.parse::<usize>().map_err(|e| e.to_string())?)
105                    })
106                }
107                "align" => align = Some(value.parse::<OnOff>().map_err(|_| format!("invalid align value: {value}"))?),
108                "sleep" => sleep = Some(value.parse::<OnOff>().map_err(|_| format!("invalid sleep value: {value}"))?),
109                "rr" => {
110                    rr = Some(match value {
111                        "record" => RecordReplay::Record,
112                        "replay" => RecordReplay::Replay,
113                        _ => return Err(format!("invalid rr value: {value}")),
114                    })
115                }
116                "rrfile" => rrfile = Some(PathBuf::from(value)),
117                "rrsnapshot" => rrsnapshot = Some(value.to_string()),
118                other => return Err(format!("unsupported -icount option: {other}")),
119            }
120        }
121
122        Ok(Self {
123            shift,
124            align,
125            sleep,
126            rr,
127            rrfile,
128            rrsnapshot,
129        })
130    }
131}