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