• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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::debug;
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 }
47 
48 impl MetricSender for Metrics {
add_start_event(&mut self, command_line: &str, source_root: &str)49     fn add_start_event(&mut self, command_line: &str, source_root: &str) {
50         let mut start_event = AdeviceStartEvent::default();
51         start_event.set_command_line(command_line.to_string());
52         start_event.set_source_root(source_root.to_string());
53         start_event.set_target(env::var(ENV_TARGET).unwrap_or("".to_string()));
54 
55         let mut event = self.default_log_event();
56         event.set_adevice_start_event(start_event);
57         self.events.push(LogEvent {
58             event_time_ms: Some(UNIX_EPOCH.elapsed().unwrap().as_millis() as i64),
59             source_extension: Some(protobuf::Message::write_to_bytes(&event).unwrap()),
60             ..Default::default()
61         });
62     }
63 
add_action_event(&mut self, action: &str, duration: std::time::Duration)64     fn add_action_event(&mut self, action: &str, duration: std::time::Duration) {
65         self.add_action_event_with_files_changed(action, duration, Vec::new())
66     }
67 
add_action_event_with_files_changed( &mut self, action: &str, duration: std::time::Duration, files_changed: std::vec::Vec<String>, )68     fn add_action_event_with_files_changed(
69         &mut self,
70         action: &str,
71         duration: std::time::Duration,
72         files_changed: std::vec::Vec<String>,
73     ) {
74         let action_event = AdeviceActionEvent {
75             action: Some(action.to_string()),
76             outcome: ::std::option::Option::None,
77             file_changed: files_changed,
78             duration: protobuf::MessageField::some(Duration {
79                 seconds: Some(duration.as_secs() as i64),
80                 nanos: Some(duration.as_nanos() as i32),
81                 ..Default::default()
82             }),
83             ..Default::default()
84         };
85 
86         let mut event: AdeviceLogEvent = self.default_log_event();
87         event.set_adevice_action_event(action_event);
88         self.events.push(LogEvent {
89             event_time_ms: Some(UNIX_EPOCH.elapsed().unwrap().as_millis() as i64),
90             source_extension: Some(protobuf::Message::write_to_bytes(&event).unwrap()),
91             ..Default::default()
92         });
93     }
94 
add_exit_event(&mut self, output: &str, exit_code: i32)95     fn add_exit_event(&mut self, output: &str, exit_code: i32) {
96         let mut exit_event = AdeviceExitEvent::default();
97         exit_event.set_logs(output.to_string());
98         exit_event.set_exit_code(exit_code);
99 
100         let mut event = self.default_log_event();
101         event.set_adevice_exit_event(exit_event);
102         self.events.push(LogEvent {
103             event_time_ms: Some(UNIX_EPOCH.elapsed().unwrap().as_millis() as i64),
104             source_extension: Some(protobuf::Message::write_to_bytes(&event).unwrap()),
105             ..Default::default()
106         });
107     }
108 
add_profiler_events(&mut self, profiler: &Profiler)109     fn add_profiler_events(&mut self, profiler: &Profiler) {
110         self.add_action_event("device_fingerprint", profiler.device_fingerprint);
111         self.add_action_event("host_fingerprint", profiler.host_fingerprint);
112         self.add_action_event("ninja_deps_computer", profiler.ninja_deps_computer);
113         self.add_action_event("adb_cmds", profiler.adb_cmds);
114         self.add_action_event(&profiler.restart_type, profiler.restart);
115         self.add_action_event("wait_for_device", profiler.wait_for_device);
116         self.add_action_event("wait_for_boot_completed", profiler.wait_for_boot_completed);
117         self.add_action_event("first_remount_rw", profiler.first_remount_rw);
118         self.add_action_event("total", profiler.total);
119         // Compute the time we aren't capturing in a category.
120         // We could graph total, but sometimes it is easier to just graph this
121         // to see if we are missing significant chunks.
122         self.add_action_event(
123             "other",
124             profiler.total
125                 - profiler.device_fingerprint
126                 - profiler.host_fingerprint
127                 - profiler.ninja_deps_computer
128                 - profiler.adb_cmds
129                 - profiler.restart
130                 - profiler.wait_for_device
131                 - profiler.wait_for_boot_completed
132                 - profiler.first_remount_rw,
133         );
134     }
135 
display_survey(&mut self)136     fn display_survey(&mut self) {
137         let survey = env::var(ENV_SURVEY_BANNER).unwrap_or("".to_string());
138         if !survey.is_empty() {
139             println!("\n{}", survey);
140         }
141     }
142 }
143 
144 impl Default for Metrics {
default() -> Self145     fn default() -> Self {
146         Metrics {
147             events: Vec::new(),
148             user: env::var(ENV_USER).unwrap_or("".to_string()),
149             invocation_id: Uuid::new_v4().to_string(),
150         }
151     }
152 }
153 
154 impl Metrics {
send(&self) -> Result<()>155     fn send(&self) -> Result<()> {
156         // Only send for internal users, check for metrics_uploader
157         if fs::metadata(METRICS_UPLOADER).is_err() {
158             return Err(anyhow!("Not internal user: Metrics not sent since uploader not found"));
159         }
160 
161         // Serialize
162         let body = {
163             let mut log_request = LogRequest::default();
164             log_request.set_log_source(ADEVICE_LOG_SOURCE);
165 
166             for e in &*self.events {
167                 log_request.log_event.push(e.clone());
168             }
169             let res: Vec<u8> = protobuf::Message::write_to_bytes(&log_request).unwrap();
170             res
171         };
172 
173         let out = env::var(ENV_OUT).unwrap_or("/tmp".to_string());
174         let temp_dir = format!("{}/adevice", out);
175         let temp_file_path = format!("{}/adevice/adevice.bin", out);
176         fs::create_dir_all(temp_dir).expect("Failed to create folder for metrics");
177         fs::write(temp_file_path.clone(), body).expect("Failed to write to metrics file");
178         Command::new(METRICS_UPLOADER)
179             .args([&temp_file_path])
180             .stdin(Stdio::null())
181             .stdout(Stdio::null())
182             .stderr(Stdio::null())
183             .spawn()
184             .expect("Failed to send metrics");
185 
186         // TODO implement next_request_wait_millis that comes back in response
187 
188         Ok(())
189     }
190 
default_log_event(&self) -> AdeviceLogEvent191     fn default_log_event(&self) -> AdeviceLogEvent {
192         let mut event = AdeviceLogEvent::default();
193         event.set_user_key(self.user.to_string());
194         event.set_invocation_id(self.invocation_id.to_string());
195         event
196     }
197 }
198 
199 impl Drop for Metrics {
drop(&mut self)200     fn drop(&mut self) {
201         match self.send() {
202             Ok(_) => (),
203             Err(e) => debug!("Failed to send metrics: {}", e),
204         };
205     }
206 }
207 
208 #[cfg(test)]
209 #[allow(unused)]
210 mod tests {
211     use super::*;
212 
213     #[test]
test_print_events()214     fn test_print_events() {
215         let mut metrics = Metrics::default();
216         metrics.user = "test_user".to_string();
217         metrics.add_start_event("adevice status", "/home/test/aosp-main-with-phones");
218         metrics.add_start_event("adevice track SomeModule", "/home/test/aosp-main-with-phones");
219 
220         assert_eq!(metrics.events.len(), 2);
221         metrics.send();
222         metrics.events.clear();
223         assert_eq!(metrics.events.len(), 0);
224     }
225 }
226