1use std::sync::Arc;
7
8use anyhow::Result;
9use struct_field_names_as_array::FieldNamesAsArray;
10use tracing::error;
11
12use crate::units::SystemdUnitStats;
13
14#[derive(
15 serde::Serialize, serde::Deserialize, Clone, Debug, Default, Eq, FieldNamesAsArray, PartialEq,
16)]
17
18pub struct TimerStats {
20 pub accuracy_usec: u64,
21 pub fixed_random_delay: bool,
22 pub last_trigger_usec: u64,
23 pub last_trigger_usec_monotonic: u64,
24 pub next_elapse_usec_monotonic: u64,
25 pub next_elapse_usec_realtime: u64,
26 pub persistent: bool,
27 pub randomized_delay_usec: u64,
28 pub remain_after_elapse: bool,
29 pub service_unit_last_state_change_usec: u64,
30 pub service_unit_last_state_change_usec_monotonic: u64,
31}
32
33pub const TIMER_STATS_FIELD_NAMES: &[&str] = &TimerStats::FIELD_NAMES_AS_ARRAY;
34
35pub async fn collect_timer_stats(
36 connection: &zbus::Connection,
37 stats: &mut SystemdUnitStats,
38 unit: &crate::units::ListedUnit,
39) -> Result<TimerStats> {
40 let mut timer_stats = TimerStats::default();
41
42 let pt = Arc::new(
43 crate::dbus::zbus_timer::TimerProxy::builder(connection)
44 .path(unit.unit_object_path.clone())?
45 .build()
46 .await?,
47 );
48 let service_unit = pt.unit().await?;
51 let mut service_unit_last_state_change_usec: Result<u64, zbus::Error> = Ok(0);
52 let mut service_unit_last_state_change_usec_monotonic: Result<u64, zbus::Error> = Ok(0);
53 if service_unit.is_empty() {
54 error!("{}: No service unit name found for timer.", unit.name);
55 } else {
56 let mp = crate::dbus::zbus_systemd::ManagerProxy::new(connection).await?;
58 let service_unit_path = mp.get_unit(&service_unit).await?;
59 let up = crate::dbus::zbus_unit::UnitProxy::builder(connection)
61 .path(service_unit_path)?
62 .build()
63 .await?;
64
65 (
66 service_unit_last_state_change_usec,
67 service_unit_last_state_change_usec_monotonic,
68 ) = tokio::join!(
69 up.state_change_timestamp(),
70 up.state_change_timestamp_monotonic(),
71 );
72 }
73 timer_stats.service_unit_last_state_change_usec = service_unit_last_state_change_usec?;
74 timer_stats.service_unit_last_state_change_usec_monotonic =
75 service_unit_last_state_change_usec_monotonic?;
76
77 let (
79 accuracy_usec,
80 fixed_random_delay,
81 last_trigger_usec,
82 last_trigger_usec_monotonic,
83 persistent,
84 next_elapse_usec_monotonic,
85 next_elapse_usec_realtime,
86 randomized_delay_usec,
87 remain_after_elapse,
88 ) = tokio::join!(
89 tokio::spawn({
90 let spawn_pt = pt.clone();
91 async move { spawn_pt.accuracy_usec().await }
92 }),
93 tokio::spawn({
94 let spawn_pt = pt.clone();
95 async move { spawn_pt.fixed_random_delay().await }
96 }),
97 tokio::spawn({
98 let spawn_pt = pt.clone();
99 async move { spawn_pt.last_trigger_usec().await }
100 }),
101 tokio::spawn({
102 let spawn_pt = pt.clone();
103 async move { spawn_pt.last_trigger_usec_monotonic().await }
104 }),
105 tokio::spawn({
106 let spawn_pt = pt.clone();
107 async move { spawn_pt.persistent().await }
108 }),
109 tokio::spawn({
110 let spawn_pt = pt.clone();
111 async move { spawn_pt.next_elapse_usec_monotonic().await }
112 }),
113 tokio::spawn({
114 let spawn_pt = pt.clone();
115 async move { spawn_pt.next_elapse_usec_realtime().await }
116 }),
117 tokio::spawn({
118 let spawn_pt = pt.clone();
119 async move { spawn_pt.randomized_delay_usec().await }
120 }),
121 tokio::spawn({
122 let spawn_pt = pt.clone();
123 async move { spawn_pt.remain_after_elapse().await }
124 }),
125 );
126
127 timer_stats.accuracy_usec = accuracy_usec??;
128 timer_stats.fixed_random_delay = fixed_random_delay??;
129 timer_stats.last_trigger_usec = last_trigger_usec??;
130 timer_stats.last_trigger_usec_monotonic = last_trigger_usec_monotonic??;
131 timer_stats.persistent = persistent??;
132 timer_stats.next_elapse_usec_monotonic = next_elapse_usec_monotonic??;
133 timer_stats.next_elapse_usec_realtime = next_elapse_usec_realtime??;
134 timer_stats.randomized_delay_usec = randomized_delay_usec??;
135 timer_stats.remain_after_elapse = remain_after_elapse??;
136
137 if timer_stats.persistent {
138 stats.timer_persistent_units += 1;
139 }
140
141 if timer_stats.remain_after_elapse {
142 stats.timer_remain_after_elapse += 1;
143 }
144
145 Ok(timer_stats)
146}