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