blob: 567fce991bdf77fea144dd33a6e9b311e58b7a1c [file] [log] [blame]
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +00001// Copyright 2022, The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Functions for creating and collecting atoms.
16
David Brazdil49f96f52022-12-16 21:29:13 +000017use crate::aidl::{clone_file, GLOBAL_SERVICE};
Seungjae Yoo6d265d92022-11-15 10:51:33 +090018use crate::crosvm::VmMetric;
David Brazdil1f530702022-10-03 12:18:10 +010019use crate::get_calling_uid;
David Brazdil49f96f52022-12-16 21:29:13 +000020use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::DeathReason::DeathReason;
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000021use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
David Brazdil7d1e5ec2023-02-06 17:56:29 +000022 CpuTopology::CpuTopology,
Alan Stokes0d1ef782022-09-27 13:46:35 +010023 IVirtualMachine::IVirtualMachine,
24 VirtualMachineAppConfig::{Payload::Payload, VirtualMachineAppConfig},
25 VirtualMachineConfig::VirtualMachineConfig,
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000026};
27use android_system_virtualizationservice::binder::{Status, Strong};
David Brazdil49f96f52022-12-16 21:29:13 +000028use android_system_virtualizationservice_internal::aidl::android::system::virtualizationservice_internal::{
29 AtomVmBooted::AtomVmBooted,
30 AtomVmCreationRequested::AtomVmCreationRequested,
31 AtomVmExited::AtomVmExited,
32};
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000033use anyhow::{anyhow, Result};
David Brazdil1f530702022-10-03 12:18:10 +010034use binder::ParcelFileDescriptor;
David Brazdilafc9a9e2023-01-12 16:08:10 +000035use log::warn;
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000036use microdroid_payload_config::VmPayloadConfig;
David Brazdilafc9a9e2023-01-12 16:08:10 +000037use statslog_virtualization_rust::vm_creation_requested;
Shikha Panwarc93a42b2022-11-09 14:12:25 +000038use std::thread;
Seungjae Yoo2e7beea2022-08-24 16:09:12 +090039use std::time::{Duration, SystemTime};
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000040use zip::ZipArchive;
41
David Brazdil7d1e5ec2023-02-06 17:56:29 +000042const INVALID_NUM_CPUS: i32 = -1;
43
Alan Stokes0d1ef782022-09-27 13:46:35 +010044fn get_apex_list(config: &VirtualMachineAppConfig) -> String {
45 match &config.payload {
46 Payload::PayloadConfig(_) => String::new(),
47 Payload::ConfigPath(config_path) => {
48 let vm_payload_config = get_vm_payload_config(&config.apk, config_path);
49 if let Ok(vm_payload_config) = vm_payload_config {
50 vm_payload_config
51 .apexes
52 .iter()
53 .map(|x| x.name.clone())
54 .collect::<Vec<String>>()
55 .join(":")
56 } else {
57 "INFO: Can't get VmPayloadConfig".to_owned()
58 }
59 }
60 }
61}
62
63fn get_vm_payload_config(
64 apk_fd: &Option<ParcelFileDescriptor>,
65 config_path: &str,
66) -> Result<VmPayloadConfig> {
67 let apk = apk_fd.as_ref().ok_or_else(|| anyhow!("APK is none"))?;
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000068 let apk_file = clone_file(apk)?;
69 let mut apk_zip = ZipArchive::new(&apk_file)?;
Alan Stokes0d1ef782022-09-27 13:46:35 +010070 let config_file = apk_zip.by_name(config_path)?;
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000071 let vm_payload_config: VmPayloadConfig = serde_json::from_reader(config_file)?;
72 Ok(vm_payload_config)
73}
74
Seungjae Yoo2e7beea2022-08-24 16:09:12 +090075fn get_duration(vm_start_timestamp: Option<SystemTime>) -> Duration {
76 match vm_start_timestamp {
77 Some(vm_start_timestamp) => vm_start_timestamp.elapsed().unwrap_or_default(),
78 None => Duration::default(),
79 }
80}
81
David Brazdil7d1e5ec2023-02-06 17:56:29 +000082// Returns the number of CPUs configured in the host system.
83// This matches how crosvm determines the number of logical cores.
84// For telemetry purposes only.
85pub(crate) fn get_num_cpus() -> Option<usize> {
86 // SAFETY - Only integer constants passed back and forth.
87 let ret = unsafe { libc::sysconf(libc::_SC_NPROCESSORS_CONF) };
88 if ret > 0 {
89 ret.try_into().ok()
90 } else {
91 None
92 }
93}
94
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000095/// Write the stats of VMCreation to statsd
96pub fn write_vm_creation_stats(
97 config: &VirtualMachineConfig,
98 is_protected: bool,
99 ret: &binder::Result<Strong<dyn IVirtualMachine>>,
100) {
101 let creation_succeeded;
102 let binder_exception_code;
103 match ret {
104 Ok(_) => {
105 creation_succeeded = true;
106 binder_exception_code = Status::ok().exception_code() as i32;
107 }
108 Err(ref e) => {
109 creation_succeeded = false;
110 binder_exception_code = e.exception_code() as i32;
111 }
112 }
David Brazdil7d1e5ec2023-02-06 17:56:29 +0000113 let (vm_identifier, config_type, cpu_topology, memory_mib, apexes) = match config {
Alan Stokes0d1ef782022-09-27 13:46:35 +0100114 VirtualMachineConfig::AppConfig(config) => (
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000115 config.name.clone(),
Alan Stokes0d1ef782022-09-27 13:46:35 +0100116 vm_creation_requested::ConfigType::VirtualMachineAppConfig,
David Brazdil7d1e5ec2023-02-06 17:56:29 +0000117 config.cpuTopology,
Alan Stokes0d1ef782022-09-27 13:46:35 +0100118 config.memoryMib,
119 get_apex_list(config),
120 ),
121 VirtualMachineConfig::RawConfig(config) => (
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000122 config.name.clone(),
Alan Stokes0d1ef782022-09-27 13:46:35 +0100123 vm_creation_requested::ConfigType::VirtualMachineRawConfig,
David Brazdil7d1e5ec2023-02-06 17:56:29 +0000124 config.cpuTopology,
Alan Stokes0d1ef782022-09-27 13:46:35 +0100125 config.memoryMib,
126 String::new(),
127 ),
128 };
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +0000129
David Brazdil7d1e5ec2023-02-06 17:56:29 +0000130 let num_cpus: i32 = match cpu_topology {
131 CpuTopology::MATCH_HOST => {
132 get_num_cpus().and_then(|v| v.try_into().ok()).unwrap_or_else(|| {
133 warn!("Failed to determine the number of CPUs in the host");
134 INVALID_NUM_CPUS
135 })
136 }
137 _ => 1,
138 };
139
David Brazdil49f96f52022-12-16 21:29:13 +0000140 let atom = AtomVmCreationRequested {
David Brazdil1f530702022-10-03 12:18:10 +0100141 uid: get_calling_uid() as i32,
David Brazdil49f96f52022-12-16 21:29:13 +0000142 vmIdentifier: vm_identifier,
143 isProtected: is_protected,
144 creationSucceeded: creation_succeeded,
145 binderExceptionCode: binder_exception_code,
146 configType: config_type as i32,
147 numCpus: num_cpus,
148 memoryMib: memory_mib,
149 apexes,
150 };
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +0000151
David Brazdil49f96f52022-12-16 21:29:13 +0000152 thread::spawn(move || {
153 GLOBAL_SERVICE.atomVmCreationRequested(&atom).unwrap_or_else(|e| {
154 warn!("Failed to write VmCreationRequested atom: {e}");
155 });
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000156 });
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +0000157}
Seungjae Yooacf559a2022-08-12 04:44:51 +0000158
159/// Write the stats of VM boot to statsd
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000160/// The function creates a separate thread which waits fro statsd to start to push atom
Seungjae Yoo2e7beea2022-08-24 16:09:12 +0900161pub fn write_vm_booted_stats(
162 uid: i32,
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000163 vm_identifier: &str,
Seungjae Yoo2e7beea2022-08-24 16:09:12 +0900164 vm_start_timestamp: Option<SystemTime>,
165) {
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000166 let vm_identifier = vm_identifier.to_owned();
Seungjae Yoo2e7beea2022-08-24 16:09:12 +0900167 let duration = get_duration(vm_start_timestamp);
David Brazdil49f96f52022-12-16 21:29:13 +0000168
169 let atom = AtomVmBooted {
170 uid,
171 vmIdentifier: vm_identifier,
172 elapsedTimeMillis: duration.as_millis() as i64,
173 };
174
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000175 thread::spawn(move || {
David Brazdil49f96f52022-12-16 21:29:13 +0000176 GLOBAL_SERVICE.atomVmBooted(&atom).unwrap_or_else(|e| {
177 warn!("Failed to write VmCreationRequested atom: {e}");
178 });
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000179 });
Seungjae Yooacf559a2022-08-12 04:44:51 +0000180}
Seungjae Yoob4c07ba2022-08-12 04:44:52 +0000181
182/// Write the stats of VM exit to statsd
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000183/// The function creates a separate thread which waits fro statsd to start to push atom
Seungjae Yoo2e7beea2022-08-24 16:09:12 +0900184pub fn write_vm_exited_stats(
185 uid: i32,
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000186 vm_identifier: &str,
Seungjae Yoo2e7beea2022-08-24 16:09:12 +0900187 reason: DeathReason,
Seungjae Yoo93430e82022-12-05 16:37:42 +0900188 exit_signal: Option<i32>,
Seungjae Yoo6d265d92022-11-15 10:51:33 +0900189 vm_metric: &VmMetric,
Seungjae Yoo2e7beea2022-08-24 16:09:12 +0900190) {
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000191 let vm_identifier = vm_identifier.to_owned();
Seungjae Yoo6d265d92022-11-15 10:51:33 +0900192 let elapsed_time_millis = get_duration(vm_metric.start_timestamp).as_millis() as i64;
193 let guest_time_millis = vm_metric.cpu_guest_time.unwrap_or_default();
194 let rss = vm_metric.rss.unwrap_or_default();
195
David Brazdil49f96f52022-12-16 21:29:13 +0000196 let atom = AtomVmExited {
197 uid,
198 vmIdentifier: vm_identifier,
199 elapsedTimeMillis: elapsed_time_millis,
200 deathReason: reason,
201 guestTimeMillis: guest_time_millis,
202 rssVmKb: rss.vm,
203 rssCrosvmKb: rss.crosvm,
204 exitSignal: exit_signal.unwrap_or_default(),
205 };
206
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000207 thread::spawn(move || {
David Brazdil49f96f52022-12-16 21:29:13 +0000208 GLOBAL_SERVICE.atomVmExited(&atom).unwrap_or_else(|e| {
209 warn!("Failed to write VmExited atom: {e}");
210 });
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000211 });
212}