xref: /aosp_15_r20/tools/asuite/adevice/src/metrics.rs (revision c2e18aaa1096c836b086f94603d04f4eb9cf37f5)
1*c2e18aaaSAndroid Build Coastguard Worker //! Metrics client
2*c2e18aaaSAndroid Build Coastguard Worker 
3*c2e18aaaSAndroid Build Coastguard Worker use crate::adevice::Profiler;
4*c2e18aaaSAndroid Build Coastguard Worker use adevice_proto::clientanalytics::LogEvent;
5*c2e18aaaSAndroid Build Coastguard Worker use adevice_proto::clientanalytics::LogRequest;
6*c2e18aaaSAndroid Build Coastguard Worker use adevice_proto::user_log::adevice_log_event::AdeviceActionEvent;
7*c2e18aaaSAndroid Build Coastguard Worker use adevice_proto::user_log::adevice_log_event::AdeviceExitEvent;
8*c2e18aaaSAndroid Build Coastguard Worker use adevice_proto::user_log::adevice_log_event::AdeviceStartEvent;
9*c2e18aaaSAndroid Build Coastguard Worker use adevice_proto::user_log::AdeviceLogEvent;
10*c2e18aaaSAndroid Build Coastguard Worker use adevice_proto::user_log::Duration;
11*c2e18aaaSAndroid Build Coastguard Worker 
12*c2e18aaaSAndroid Build Coastguard Worker use anyhow::{anyhow, Result};
13*c2e18aaaSAndroid Build Coastguard Worker use std::env;
14*c2e18aaaSAndroid Build Coastguard Worker use std::fs;
15*c2e18aaaSAndroid Build Coastguard Worker use std::process::{Command, Stdio};
16*c2e18aaaSAndroid Build Coastguard Worker use std::time::UNIX_EPOCH;
17*c2e18aaaSAndroid Build Coastguard Worker use tracing::info;
18*c2e18aaaSAndroid Build Coastguard Worker use uuid::Uuid;
19*c2e18aaaSAndroid Build Coastguard Worker 
20*c2e18aaaSAndroid Build Coastguard Worker const ENV_OUT: &str = "OUT";
21*c2e18aaaSAndroid Build Coastguard Worker const ENV_USER: &str = "USER";
22*c2e18aaaSAndroid Build Coastguard Worker const ENV_TARGET: &str = "TARGET_PRODUCT";
23*c2e18aaaSAndroid Build Coastguard Worker const ENV_SURVEY_BANNER: &str = "ADEVICE_SURVEY_BANNER";
24*c2e18aaaSAndroid Build Coastguard Worker const METRICS_UPLOADER: &str = "/google/bin/releases/adevice-dev/metrics_uploader";
25*c2e18aaaSAndroid Build Coastguard Worker const ADEVICE_LOG_SOURCE: i32 = 2265;
26*c2e18aaaSAndroid Build Coastguard Worker 
27*c2e18aaaSAndroid Build Coastguard Worker pub trait MetricSender {
add_start_event(&mut self, command_line: &str, source_root: &str)28*c2e18aaaSAndroid Build Coastguard Worker     fn add_start_event(&mut self, command_line: &str, source_root: &str);
add_action_event(&mut self, action: &str, duration: std::time::Duration)29*c2e18aaaSAndroid Build Coastguard Worker     fn add_action_event(&mut self, action: &str, duration: std::time::Duration);
add_action_event_with_files_changed( &mut self, action: &str, duration: std::time::Duration, files_changed: std::vec::Vec<String>, )30*c2e18aaaSAndroid Build Coastguard Worker     fn add_action_event_with_files_changed(
31*c2e18aaaSAndroid Build Coastguard Worker         &mut self,
32*c2e18aaaSAndroid Build Coastguard Worker         action: &str,
33*c2e18aaaSAndroid Build Coastguard Worker         duration: std::time::Duration,
34*c2e18aaaSAndroid Build Coastguard Worker         files_changed: std::vec::Vec<String>,
35*c2e18aaaSAndroid Build Coastguard Worker     );
add_profiler_events(&mut self, profiler: &Profiler)36*c2e18aaaSAndroid Build Coastguard Worker     fn add_profiler_events(&mut self, profiler: &Profiler);
add_exit_event(&mut self, output: &str, exit_code: i32)37*c2e18aaaSAndroid Build Coastguard Worker     fn add_exit_event(&mut self, output: &str, exit_code: i32);
display_survey(&mut self)38*c2e18aaaSAndroid Build Coastguard Worker     fn display_survey(&mut self);
39*c2e18aaaSAndroid Build Coastguard Worker }
40*c2e18aaaSAndroid Build Coastguard Worker 
41*c2e18aaaSAndroid Build Coastguard Worker #[derive(Debug, Clone)]
42*c2e18aaaSAndroid Build Coastguard Worker pub struct Metrics {
43*c2e18aaaSAndroid Build Coastguard Worker     events: Vec<LogEvent>,
44*c2e18aaaSAndroid Build Coastguard Worker     user: String,
45*c2e18aaaSAndroid Build Coastguard Worker     invocation_id: String,
46*c2e18aaaSAndroid Build Coastguard Worker     hostname: String,
47*c2e18aaaSAndroid Build Coastguard Worker }
48*c2e18aaaSAndroid Build Coastguard Worker 
49*c2e18aaaSAndroid Build Coastguard Worker impl MetricSender for Metrics {
add_start_event(&mut self, command_line: &str, source_root: &str)50*c2e18aaaSAndroid Build Coastguard Worker     fn add_start_event(&mut self, command_line: &str, source_root: &str) {
51*c2e18aaaSAndroid Build Coastguard Worker         let mut start_event = AdeviceStartEvent::default();
52*c2e18aaaSAndroid Build Coastguard Worker         start_event.set_command_line(command_line.to_string());
53*c2e18aaaSAndroid Build Coastguard Worker         start_event.set_source_root(source_root.to_string());
54*c2e18aaaSAndroid Build Coastguard Worker         start_event.set_target(env::var(ENV_TARGET).unwrap_or("".to_string()));
55*c2e18aaaSAndroid Build Coastguard Worker         start_event.set_hostname(self.hostname.to_string());
56*c2e18aaaSAndroid Build Coastguard Worker 
57*c2e18aaaSAndroid Build Coastguard Worker         let mut event = self.default_log_event();
58*c2e18aaaSAndroid Build Coastguard Worker         event.set_adevice_start_event(start_event);
59*c2e18aaaSAndroid Build Coastguard Worker         self.events.push(LogEvent {
60*c2e18aaaSAndroid Build Coastguard Worker             event_time_ms: Some(UNIX_EPOCH.elapsed().unwrap().as_millis() as i64),
61*c2e18aaaSAndroid Build Coastguard Worker             source_extension: Some(protobuf::Message::write_to_bytes(&event).unwrap()),
62*c2e18aaaSAndroid Build Coastguard Worker             ..Default::default()
63*c2e18aaaSAndroid Build Coastguard Worker         });
64*c2e18aaaSAndroid Build Coastguard Worker     }
65*c2e18aaaSAndroid Build Coastguard Worker 
add_action_event(&mut self, action: &str, duration: std::time::Duration)66*c2e18aaaSAndroid Build Coastguard Worker     fn add_action_event(&mut self, action: &str, duration: std::time::Duration) {
67*c2e18aaaSAndroid Build Coastguard Worker         self.add_action_event_with_files_changed(action, duration, Vec::new())
68*c2e18aaaSAndroid Build Coastguard Worker     }
69*c2e18aaaSAndroid Build Coastguard Worker 
add_action_event_with_files_changed( &mut self, action: &str, duration: std::time::Duration, files_changed: std::vec::Vec<String>, )70*c2e18aaaSAndroid Build Coastguard Worker     fn add_action_event_with_files_changed(
71*c2e18aaaSAndroid Build Coastguard Worker         &mut self,
72*c2e18aaaSAndroid Build Coastguard Worker         action: &str,
73*c2e18aaaSAndroid Build Coastguard Worker         duration: std::time::Duration,
74*c2e18aaaSAndroid Build Coastguard Worker         files_changed: std::vec::Vec<String>,
75*c2e18aaaSAndroid Build Coastguard Worker     ) {
76*c2e18aaaSAndroid Build Coastguard Worker         let action_event = AdeviceActionEvent {
77*c2e18aaaSAndroid Build Coastguard Worker             action: Some(action.to_string()),
78*c2e18aaaSAndroid Build Coastguard Worker             outcome: ::std::option::Option::None,
79*c2e18aaaSAndroid Build Coastguard Worker             file_changed: files_changed,
80*c2e18aaaSAndroid Build Coastguard Worker             duration: protobuf::MessageField::some(Duration {
81*c2e18aaaSAndroid Build Coastguard Worker                 seconds: Some(duration.as_secs() as i64),
82*c2e18aaaSAndroid Build Coastguard Worker                 nanos: Some(duration.as_nanos() as i32),
83*c2e18aaaSAndroid Build Coastguard Worker                 ..Default::default()
84*c2e18aaaSAndroid Build Coastguard Worker             }),
85*c2e18aaaSAndroid Build Coastguard Worker             ..Default::default()
86*c2e18aaaSAndroid Build Coastguard Worker         };
87*c2e18aaaSAndroid Build Coastguard Worker 
88*c2e18aaaSAndroid Build Coastguard Worker         let mut event: AdeviceLogEvent = self.default_log_event();
89*c2e18aaaSAndroid Build Coastguard Worker         event.set_adevice_action_event(action_event);
90*c2e18aaaSAndroid Build Coastguard Worker         self.events.push(LogEvent {
91*c2e18aaaSAndroid Build Coastguard Worker             event_time_ms: Some(UNIX_EPOCH.elapsed().unwrap().as_millis() as i64),
92*c2e18aaaSAndroid Build Coastguard Worker             source_extension: Some(protobuf::Message::write_to_bytes(&event).unwrap()),
93*c2e18aaaSAndroid Build Coastguard Worker             ..Default::default()
94*c2e18aaaSAndroid Build Coastguard Worker         });
95*c2e18aaaSAndroid Build Coastguard Worker     }
96*c2e18aaaSAndroid Build Coastguard Worker 
add_exit_event(&mut self, output: &str, exit_code: i32)97*c2e18aaaSAndroid Build Coastguard Worker     fn add_exit_event(&mut self, output: &str, exit_code: i32) {
98*c2e18aaaSAndroid Build Coastguard Worker         let mut exit_event = AdeviceExitEvent::default();
99*c2e18aaaSAndroid Build Coastguard Worker         exit_event.set_logs(output.to_string());
100*c2e18aaaSAndroid Build Coastguard Worker         exit_event.set_exit_code(exit_code);
101*c2e18aaaSAndroid Build Coastguard Worker 
102*c2e18aaaSAndroid Build Coastguard Worker         let mut event = self.default_log_event();
103*c2e18aaaSAndroid Build Coastguard Worker         event.set_adevice_exit_event(exit_event);
104*c2e18aaaSAndroid Build Coastguard Worker         self.events.push(LogEvent {
105*c2e18aaaSAndroid Build Coastguard Worker             event_time_ms: Some(UNIX_EPOCH.elapsed().unwrap().as_millis() as i64),
106*c2e18aaaSAndroid Build Coastguard Worker             source_extension: Some(protobuf::Message::write_to_bytes(&event).unwrap()),
107*c2e18aaaSAndroid Build Coastguard Worker             ..Default::default()
108*c2e18aaaSAndroid Build Coastguard Worker         });
109*c2e18aaaSAndroid Build Coastguard Worker     }
110*c2e18aaaSAndroid Build Coastguard Worker 
add_profiler_events(&mut self, profiler: &Profiler)111*c2e18aaaSAndroid Build Coastguard Worker     fn add_profiler_events(&mut self, profiler: &Profiler) {
112*c2e18aaaSAndroid Build Coastguard Worker         self.add_action_event("device_fingerprint", profiler.device_fingerprint);
113*c2e18aaaSAndroid Build Coastguard Worker         self.add_action_event("host_fingerprint", profiler.host_fingerprint);
114*c2e18aaaSAndroid Build Coastguard Worker         self.add_action_event("ninja_deps_computer", profiler.ninja_deps_computer);
115*c2e18aaaSAndroid Build Coastguard Worker         self.add_action_event("adb_cmds", profiler.adb_cmds);
116*c2e18aaaSAndroid Build Coastguard Worker         self.add_action_event(&profiler.restart_type, profiler.restart);
117*c2e18aaaSAndroid Build Coastguard Worker         self.add_action_event("wait_for_device", profiler.wait_for_device);
118*c2e18aaaSAndroid Build Coastguard Worker         self.add_action_event("wait_for_boot_completed", profiler.wait_for_boot_completed);
119*c2e18aaaSAndroid Build Coastguard Worker         self.add_action_event("first_remount_rw", profiler.first_remount_rw);
120*c2e18aaaSAndroid Build Coastguard Worker         self.add_action_event("total", profiler.total);
121*c2e18aaaSAndroid Build Coastguard Worker         // Compute the time we aren't capturing in a category.
122*c2e18aaaSAndroid Build Coastguard Worker         // We could graph total, but sometimes it is easier to just graph this
123*c2e18aaaSAndroid Build Coastguard Worker         // to see if we are missing significant chunks.
124*c2e18aaaSAndroid Build Coastguard Worker         self.add_action_event(
125*c2e18aaaSAndroid Build Coastguard Worker             "other",
126*c2e18aaaSAndroid Build Coastguard Worker             profiler.total
127*c2e18aaaSAndroid Build Coastguard Worker                 - profiler.device_fingerprint
128*c2e18aaaSAndroid Build Coastguard Worker                 - profiler.host_fingerprint
129*c2e18aaaSAndroid Build Coastguard Worker                 - profiler.ninja_deps_computer
130*c2e18aaaSAndroid Build Coastguard Worker                 - profiler.adb_cmds
131*c2e18aaaSAndroid Build Coastguard Worker                 - profiler.restart
132*c2e18aaaSAndroid Build Coastguard Worker                 - profiler.wait_for_device
133*c2e18aaaSAndroid Build Coastguard Worker                 - profiler.wait_for_boot_completed
134*c2e18aaaSAndroid Build Coastguard Worker                 - profiler.first_remount_rw,
135*c2e18aaaSAndroid Build Coastguard Worker         );
136*c2e18aaaSAndroid Build Coastguard Worker     }
137*c2e18aaaSAndroid Build Coastguard Worker 
display_survey(&mut self)138*c2e18aaaSAndroid Build Coastguard Worker     fn display_survey(&mut self) {
139*c2e18aaaSAndroid Build Coastguard Worker         let survey = env::var(ENV_SURVEY_BANNER).unwrap_or("".to_string());
140*c2e18aaaSAndroid Build Coastguard Worker         if !survey.is_empty() {
141*c2e18aaaSAndroid Build Coastguard Worker             println!("\n{}", survey);
142*c2e18aaaSAndroid Build Coastguard Worker         }
143*c2e18aaaSAndroid Build Coastguard Worker     }
144*c2e18aaaSAndroid Build Coastguard Worker }
145*c2e18aaaSAndroid Build Coastguard Worker 
146*c2e18aaaSAndroid Build Coastguard Worker impl Default for Metrics {
default() -> Self147*c2e18aaaSAndroid Build Coastguard Worker     fn default() -> Self {
148*c2e18aaaSAndroid Build Coastguard Worker         Metrics {
149*c2e18aaaSAndroid Build Coastguard Worker             events: Vec::new(),
150*c2e18aaaSAndroid Build Coastguard Worker             user: env::var(ENV_USER).unwrap_or("".to_string()),
151*c2e18aaaSAndroid Build Coastguard Worker             invocation_id: Uuid::new_v4().to_string(),
152*c2e18aaaSAndroid Build Coastguard Worker             hostname: get_hostname(),
153*c2e18aaaSAndroid Build Coastguard Worker         }
154*c2e18aaaSAndroid Build Coastguard Worker     }
155*c2e18aaaSAndroid Build Coastguard Worker }
156*c2e18aaaSAndroid Build Coastguard Worker 
157*c2e18aaaSAndroid Build Coastguard Worker impl Metrics {
send(&self) -> Result<()>158*c2e18aaaSAndroid Build Coastguard Worker     fn send(&self) -> Result<()> {
159*c2e18aaaSAndroid Build Coastguard Worker         // Only send for internal users, check for metrics_uploader
160*c2e18aaaSAndroid Build Coastguard Worker         if fs::metadata(METRICS_UPLOADER).is_err() {
161*c2e18aaaSAndroid Build Coastguard Worker             return Err(anyhow!("Not internal user: Metrics not sent since uploader not found"));
162*c2e18aaaSAndroid Build Coastguard Worker         }
163*c2e18aaaSAndroid Build Coastguard Worker         if self.user.is_empty() {
164*c2e18aaaSAndroid Build Coastguard Worker             return Err(anyhow!("USER env not set: Metrics not sent since no user set"));
165*c2e18aaaSAndroid Build Coastguard Worker         }
166*c2e18aaaSAndroid Build Coastguard Worker         // Serialize
167*c2e18aaaSAndroid Build Coastguard Worker         let body = {
168*c2e18aaaSAndroid Build Coastguard Worker             let mut log_request = LogRequest::default();
169*c2e18aaaSAndroid Build Coastguard Worker             log_request.set_log_source(ADEVICE_LOG_SOURCE);
170*c2e18aaaSAndroid Build Coastguard Worker 
171*c2e18aaaSAndroid Build Coastguard Worker             for e in &*self.events {
172*c2e18aaaSAndroid Build Coastguard Worker                 log_request.log_event.push(e.clone());
173*c2e18aaaSAndroid Build Coastguard Worker             }
174*c2e18aaaSAndroid Build Coastguard Worker             let res: Vec<u8> = protobuf::Message::write_to_bytes(&log_request).unwrap();
175*c2e18aaaSAndroid Build Coastguard Worker             res
176*c2e18aaaSAndroid Build Coastguard Worker         };
177*c2e18aaaSAndroid Build Coastguard Worker 
178*c2e18aaaSAndroid Build Coastguard Worker         let out = env::var(ENV_OUT).unwrap_or("/tmp".to_string());
179*c2e18aaaSAndroid Build Coastguard Worker         let temp_dir = format!("{}/adevice", out);
180*c2e18aaaSAndroid Build Coastguard Worker         let temp_file_path = format!("{}/adevice/adevice.bin", out);
181*c2e18aaaSAndroid Build Coastguard Worker         fs::create_dir_all(temp_dir).expect("Failed to create folder for metrics");
182*c2e18aaaSAndroid Build Coastguard Worker         fs::write(temp_file_path.clone(), body).expect("Failed to write to metrics file");
183*c2e18aaaSAndroid Build Coastguard Worker         if let Err(e) = Command::new(METRICS_UPLOADER)
184*c2e18aaaSAndroid Build Coastguard Worker             .args([&temp_file_path])
185*c2e18aaaSAndroid Build Coastguard Worker             .stdin(Stdio::null())
186*c2e18aaaSAndroid Build Coastguard Worker             .stdout(Stdio::null())
187*c2e18aaaSAndroid Build Coastguard Worker             .stderr(Stdio::null())
188*c2e18aaaSAndroid Build Coastguard Worker             .spawn()
189*c2e18aaaSAndroid Build Coastguard Worker         {
190*c2e18aaaSAndroid Build Coastguard Worker             return Err(anyhow!("Failed to send metrics {}", e));
191*c2e18aaaSAndroid Build Coastguard Worker         }
192*c2e18aaaSAndroid Build Coastguard Worker         // TODO implement next_request_wait_millis that comes back in response
193*c2e18aaaSAndroid Build Coastguard Worker 
194*c2e18aaaSAndroid Build Coastguard Worker         Ok(())
195*c2e18aaaSAndroid Build Coastguard Worker     }
196*c2e18aaaSAndroid Build Coastguard Worker 
default_log_event(&self) -> AdeviceLogEvent197*c2e18aaaSAndroid Build Coastguard Worker     fn default_log_event(&self) -> AdeviceLogEvent {
198*c2e18aaaSAndroid Build Coastguard Worker         let mut event = AdeviceLogEvent::default();
199*c2e18aaaSAndroid Build Coastguard Worker         event.set_user_key(self.user.to_string());
200*c2e18aaaSAndroid Build Coastguard Worker         event.set_invocation_id(self.invocation_id.to_string());
201*c2e18aaaSAndroid Build Coastguard Worker         event
202*c2e18aaaSAndroid Build Coastguard Worker     }
203*c2e18aaaSAndroid Build Coastguard Worker }
204*c2e18aaaSAndroid Build Coastguard Worker 
get_hostname() -> String205*c2e18aaaSAndroid Build Coastguard Worker fn get_hostname() -> String {
206*c2e18aaaSAndroid Build Coastguard Worker     Command::new("hostname").output().map_or_else(
207*c2e18aaaSAndroid Build Coastguard Worker         |_err| String::new(),
208*c2e18aaaSAndroid Build Coastguard Worker         |output| {
209*c2e18aaaSAndroid Build Coastguard Worker             if output.status.success() {
210*c2e18aaaSAndroid Build Coastguard Worker                 String::from_utf8_lossy(&output.stdout).trim().to_string()
211*c2e18aaaSAndroid Build Coastguard Worker             } else {
212*c2e18aaaSAndroid Build Coastguard Worker                 String::new()
213*c2e18aaaSAndroid Build Coastguard Worker             }
214*c2e18aaaSAndroid Build Coastguard Worker         },
215*c2e18aaaSAndroid Build Coastguard Worker     )
216*c2e18aaaSAndroid Build Coastguard Worker }
217*c2e18aaaSAndroid Build Coastguard Worker 
218*c2e18aaaSAndroid Build Coastguard Worker impl Drop for Metrics {
drop(&mut self)219*c2e18aaaSAndroid Build Coastguard Worker     fn drop(&mut self) {
220*c2e18aaaSAndroid Build Coastguard Worker         match self.send() {
221*c2e18aaaSAndroid Build Coastguard Worker             Ok(_) => (),
222*c2e18aaaSAndroid Build Coastguard Worker             Err(e) => info!("Failed to send metrics: {}", e),
223*c2e18aaaSAndroid Build Coastguard Worker         };
224*c2e18aaaSAndroid Build Coastguard Worker     }
225*c2e18aaaSAndroid Build Coastguard Worker }
226*c2e18aaaSAndroid Build Coastguard Worker 
227*c2e18aaaSAndroid Build Coastguard Worker #[cfg(test)]
228*c2e18aaaSAndroid Build Coastguard Worker #[allow(unused)]
229*c2e18aaaSAndroid Build Coastguard Worker mod tests {
230*c2e18aaaSAndroid Build Coastguard Worker     use super::*;
231*c2e18aaaSAndroid Build Coastguard Worker 
232*c2e18aaaSAndroid Build Coastguard Worker     #[test]
test_print_events()233*c2e18aaaSAndroid Build Coastguard Worker     fn test_print_events() {
234*c2e18aaaSAndroid Build Coastguard Worker         let mut metrics = Metrics::default();
235*c2e18aaaSAndroid Build Coastguard Worker         metrics.user = "test_user".to_string();
236*c2e18aaaSAndroid Build Coastguard Worker         metrics.add_start_event("adevice status", "/home/test/aosp-main-with-phones");
237*c2e18aaaSAndroid Build Coastguard Worker         metrics.add_start_event("adevice track SomeModule", "/home/test/aosp-main-with-phones");
238*c2e18aaaSAndroid Build Coastguard Worker 
239*c2e18aaaSAndroid Build Coastguard Worker         assert_eq!(metrics.events.len(), 2);
240*c2e18aaaSAndroid Build Coastguard Worker         metrics.send();
241*c2e18aaaSAndroid Build Coastguard Worker         metrics.events.clear();
242*c2e18aaaSAndroid Build Coastguard Worker         assert_eq!(metrics.events.len(), 0);
243*c2e18aaaSAndroid Build Coastguard Worker     }
244*c2e18aaaSAndroid Build Coastguard Worker }
245