Skip to content

Commit

Permalink
stats: Display basic emerge command stats
Browse files Browse the repository at this point in the history
I played around with more detailed stats, but they didn't seem that useful in the end, so for now
I'm just classifying commands as merge/clean/sync. Note that portage doesn't log all commands, so
the available data isn't as right as I thought.
  • Loading branch information
vincentdephily committed Nov 19, 2024
1 parent 8cef71b commit 2c43167
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 28 deletions.
59 changes: 52 additions & 7 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,12 +143,37 @@ impl Times {
}
}

/// Classify emerge commands by looking at their args.
///
/// Note that some commands don't get logged at all, so this enum is quite limited.
#[derive(PartialEq, Eq, PartialOrd, Ord)]
enum ArgKind {
All,
Merge,
Clean,
Sync,
}
impl ArgKind {
fn new(args: &str) -> Self {
for arg in args.split_ascii_whitespace() {
match arg {
"--deselect" | "--unmerge" | "--clean" | "--depclean" => return Self::Clean,
"--sync" => return Self::Sync,
_ => (),
}
}
Self::Merge
}
}

/// Summary display of merge events
///
/// First loop is like cmd_list but we store the merge time for each ebuild instead of printing it.
/// Then we compute the stats per ebuild, and print that.
pub fn cmd_stats(gc: Conf, sc: ConfStats) -> Result<bool, Error> {
let hist = get_hist(&gc.logfile, gc.from, gc.to, sc.show, &sc.search, sc.exact)?;
let h = [sc.group.name(), "Logged emerges", "Install/Update", "Unmerge/Clean", "Sync"];
let mut tblc = Table::new(&gc).margin(1, " ").header(h);
let h = [sc.group.name(), "Repo", "Syncs", "Total time", "Predict time"];
let mut tbls = Table::new(&gc).align_left(0).align_left(1).margin(1, " ").header(h);
let h = [sc.group.name(),
Expand All @@ -173,6 +198,7 @@ pub fn cmd_stats(gc: Conf, sc: ConfStats) -> Result<bool, Error> {
let mut pkg_time: BTreeMap<String, (Times, Times)> = BTreeMap::new();
let mut sync_start: Option<i64> = None;
let mut sync_time: BTreeMap<String, Times> = BTreeMap::new();
let mut cmd_args: BTreeMap<ArgKind, usize> = BTreeMap::new();
let mut nextts = 0;
let mut curts = 0;
for p in hist {
Expand All @@ -183,16 +209,20 @@ pub fn cmd_stats(gc: Conf, sc: ConfStats) -> Result<bool, Error> {
curts = t;
} else if t > nextts {
let group = sc.group.at(curts, gc.date_offset);
cmd_stats_group(&gc, &sc, &mut tbls, &mut tblp, &mut tblt, group, &sync_time,
&pkg_time);
cmd_stats_group(&gc, &sc, &mut tblc, &mut tbls, &mut tblp, &mut tblt, group,
&cmd_args, &sync_time, &pkg_time);
sync_time.clear();
pkg_time.clear();
cmd_args.clear();
nextts = sc.group.next(t, gc.date_offset);
curts = t;
}
}
match p {
Hist::CmdStart { .. } => todo!("CmdStart"),
Hist::CmdStart { args, .. } => {
*cmd_args.entry(ArgKind::All).or_insert(0) += 1;
*cmd_args.entry(ArgKind::new(&args)).or_insert(0) += 1;
},
Hist::MergeStart { ts, key, .. } => {
merge_start.insert(key, ts);
},
Expand Down Expand Up @@ -228,15 +258,20 @@ pub fn cmd_stats(gc: Conf, sc: ConfStats) -> Result<bool, Error> {
}
}
let group = sc.group.at(curts, gc.date_offset);
cmd_stats_group(&gc, &sc, &mut tbls, &mut tblp, &mut tblt, group, &sync_time, &pkg_time);
cmd_stats_group(&gc, &sc, &mut tblc, &mut tbls, &mut tblp, &mut tblt, group, &cmd_args,
&sync_time, &pkg_time);
// Controlled drop to ensure table order and insert blank lines
let (es, ep, et) = (!tbls.is_empty(), !tblp.is_empty(), !tblt.is_empty());
let (ec, es, ep, et) = (!tblc.is_empty(), !tbls.is_empty(), !tblp.is_empty(), !tblt.is_empty());
drop(tblc);
if ec && es {
println!();
}
drop(tbls);
if es && ep {
if (ec || es) && ep {
println!();
}
drop(tblp);
if (es || ep) && et {
if (ec || es || ep) && et {
println!();
}
drop(tblt);
Expand All @@ -247,12 +282,22 @@ pub fn cmd_stats(gc: Conf, sc: ConfStats) -> Result<bool, Error> {
#[allow(clippy::too_many_arguments)]
fn cmd_stats_group(gc: &Conf,
sc: &ConfStats,
tblc: &mut Table<5>,
tbls: &mut Table<5>,
tblp: &mut Table<8>,
tblt: &mut Table<7>,
group: String,
cmd_args: &BTreeMap<ArgKind, usize>,
sync_time: &BTreeMap<String, Times>,
pkg_time: &BTreeMap<String, (Times, Times)>) {
// Commands
if sc.show.cmd && !cmd_args.is_empty() {
tblc.row([&[&group],
&[&gc.cnt, cmd_args.get(&ArgKind::All).unwrap_or(&0)],
&[&gc.cnt, cmd_args.get(&ArgKind::Merge).unwrap_or(&0)],
&[&gc.cnt, cmd_args.get(&ArgKind::Clean).unwrap_or(&0)],
&[&gc.cnt, cmd_args.get(&ArgKind::Sync).unwrap_or(&0)]]);
}
// Syncs
if sc.show.sync && !sync_time.is_empty() {
for (repo, time) in sync_time {
Expand Down
2 changes: 1 addition & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ impl ConfPred {

impl ConfStats {
fn try_new(cli: &ArgMatches, toml: &Toml) -> Result<Self, Error> {
Ok(Self { show: sel!(cli, toml, stats, show, "ptsa", Show::p())?,
Ok(Self { show: sel!(cli, toml, stats, show, "cptsa", Show::p())?,
search: cli.get_many("search").unwrap_or_default().cloned().collect(),
exact: cli.get_flag("exact"),
lim: sel!(cli, toml, stats, limit, 1..=65000, 10)? as u16,
Expand Down
24 changes: 13 additions & 11 deletions src/config/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,19 @@ pub fn build_cli() -> Command {
u: Package unmerges\n \
s: Repository syncs\n \
a: All of the above");
let show_s = Arg::new("show").short('s')
.long("show")
.value_name("p,t,s,a")
.display_order(3)
.help_heading("Filter")
.help("Show (p)ackages, (t)otals, (s)yncs, and/or (a)ll")
.long_help("Show (any combination of)\n \
p: Individual package merges/unmerges\n \
t: Total package merges/unmerges\n \
s: Repository syncs\n \
a: All of the above");
let show_s =
Arg::new("show").short('s')
.long("show")
.value_name("c,p,t,s,a")
.display_order(3)
.help_heading("Filter")
.help("Show (c)commands, (p)ackages, (t)otals, (s)yncs, and/or (a)ll")
.long_help("Show (any combination of)\n \
c: Emerge commands\n \
p: Individual package merges/unmerges\n \
t: Total package merges/unmerges\n \
s: Repository syncs\n \
a: All of the above");
let show_p = Arg::new("show").short('s')
.long("show")
.value_name("e,m,t,a")
Expand Down
17 changes: 10 additions & 7 deletions src/parse/history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,17 +443,20 @@ mod tests {
}

#[test]
/// Basic counts, with every combination of merge/unmerge/sync
/// Basic counts, with every combination of command/merge/unmerge/sync
fn parse_hist_nofilter() {
for i in 0..8 {
let m = (i & 0b001) == 0;
let u = (i & 0b010) == 0;
let s = (i & 0b100) == 0;
let show = format!("{}{}{}",
for i in 0..16 {
let c = (i & 0b0001) == 0;
let m = (i & 0b0010) == 0;
let u = (i & 0b0100) == 0;
let s = (i & 0b1000) == 0;
let show = format!("{}{}{}{}",
if c { "c" } else { "" },
if m { "m" } else { "" },
if u { "u" } else { "" },
if s { "s" } else { "" });
let t = vec![("MStart", if m { 889 } else { 0 }),
let t = vec![("CStart", if c { 450 } else { 0 }),
("MStart", if m { 889 } else { 0 }),
("MStop", if m { 832 } else { 0 }),
("UStart", if u { 832 } else { 0 }),
("UStop", if u { 832 } else { 0 }),
Expand Down
6 changes: 4 additions & 2 deletions tests/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,9 @@ fn stats() {
0),
("%F10000.log s client -sst -oc", "11 24:00:24 2:10:56 10 27 2\n", 0),
("%F10000.log s client -sa -oc",
"kde-frameworks/kxmlrpcclient 2 47 23 2 4 2\n\
"450 267 20 163\n\
\n\
kde-frameworks/kxmlrpcclient 2 47 23 2 4 2\n\
mail-client/thunderbird 2 1:23:44 41:52 2 6 3\n\
www-client/chromium 3 21:41:24 7:42:07 3 12 3\n\
www-client/falkon 1 6:02 6:02 0 0 ?\n\
Expand Down Expand Up @@ -494,7 +496,7 @@ fn negative_merge_time() {
2019-06-05 10:21:02 ? >>> kde-plasma/kwin-5.15.5\n\
2019-06-08 21:33:36 3:10 >>> kde-plasma/kwin-5.15.5\n")),
// For `stats` the negative merge time is used for count but ignored for tottime/predtime.
("%Fnegtime.log s -sa -oc",
("%Fnegtime.log s -sstp -oc",
format!("gentoo 2 1:06 1:06\n\
\n\
kde-apps/libktnef 1 26 26 0 0 ?\n\
Expand Down

0 comments on commit 2c43167

Please sign in to comment.