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