blob: 33299aa7eb7a6f8a69105888c492538854469dde [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 Brazdilf50c7a62023-04-19 14:22:42 +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::{
Elie Kheirallahb4b2f242025-01-23 03:38:07 +000022 CpuOptions::CpuOptions,
23 CpuOptions::CpuTopology::CpuTopology,
Alan Stokes0d1ef782022-09-27 13:46:35 +010024 IVirtualMachine::IVirtualMachine,
25 VirtualMachineAppConfig::{Payload::Payload, VirtualMachineAppConfig},
26 VirtualMachineConfig::VirtualMachineConfig,
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000027};
28use android_system_virtualizationservice::binder::{Status, Strong};
David Brazdil49f96f52022-12-16 21:29:13 +000029use android_system_virtualizationservice_internal::aidl::android::system::virtualizationservice_internal::{
30 AtomVmBooted::AtomVmBooted,
31 AtomVmCreationRequested::AtomVmCreationRequested,
32 AtomVmExited::AtomVmExited,
33};
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000034use anyhow::{anyhow, Result};
David Brazdil1f530702022-10-03 12:18:10 +010035use binder::ParcelFileDescriptor;
Seungjae Yooeebe6442023-04-04 13:02:46 +090036use log::{info, warn};
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000037use microdroid_payload_config::VmPayloadConfig;
David Brazdilafc9a9e2023-01-12 16:08:10 +000038use statslog_virtualization_rust::vm_creation_requested;
Shikha Panwarc93a42b2022-11-09 14:12:25 +000039use std::thread;
Seungjae Yoo2e7beea2022-08-24 16:09:12 +090040use std::time::{Duration, SystemTime};
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000041use zip::ZipArchive;
42
David Brazdil7d1e5ec2023-02-06 17:56:29 +000043const INVALID_NUM_CPUS: i32 = -1;
44
Alan Stokes0d1ef782022-09-27 13:46:35 +010045fn get_apex_list(config: &VirtualMachineAppConfig) -> String {
46 match &config.payload {
47 Payload::PayloadConfig(_) => String::new(),
48 Payload::ConfigPath(config_path) => {
49 let vm_payload_config = get_vm_payload_config(&config.apk, config_path);
50 if let Ok(vm_payload_config) = vm_payload_config {
51 vm_payload_config
52 .apexes
53 .iter()
54 .map(|x| x.name.clone())
55 .collect::<Vec<String>>()
56 .join(":")
57 } else {
58 "INFO: Can't get VmPayloadConfig".to_owned()
59 }
60 }
61 }
62}
63
64fn get_vm_payload_config(
65 apk_fd: &Option<ParcelFileDescriptor>,
66 config_path: &str,
67) -> Result<VmPayloadConfig> {
68 let apk = apk_fd.as_ref().ok_or_else(|| anyhow!("APK is none"))?;
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000069 let apk_file = clone_file(apk)?;
70 let mut apk_zip = ZipArchive::new(&apk_file)?;
Alan Stokes0d1ef782022-09-27 13:46:35 +010071 let config_file = apk_zip.by_name(config_path)?;
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000072 let vm_payload_config: VmPayloadConfig = serde_json::from_reader(config_file)?;
73 Ok(vm_payload_config)
74}
75
Seungjae Yoo2e7beea2022-08-24 16:09:12 +090076fn get_duration(vm_start_timestamp: Option<SystemTime>) -> Duration {
77 match vm_start_timestamp {
78 Some(vm_start_timestamp) => vm_start_timestamp.elapsed().unwrap_or_default(),
79 None => Duration::default(),
80 }
81}
82
David Brazdil7d1e5ec2023-02-06 17:56:29 +000083// Returns the number of CPUs configured in the host system.
84// This matches how crosvm determines the number of logical cores.
85// For telemetry purposes only.
David Brazdil25e8c052023-02-17 12:53:01 +000086pub(crate) fn get_num_cpus() -> Option<usize> {
Andrew Walbranb58d1b42023-07-07 13:54:49 +010087 // SAFETY: Only integer constants passed back and forth.
David Brazdil7d1e5ec2023-02-06 17:56:29 +000088 let ret = unsafe { libc::sysconf(libc::_SC_NPROCESSORS_CONF) };
89 if ret > 0 {
90 ret.try_into().ok()
91 } else {
92 None
93 }
94}
95
Elie Kheirallahb4b2f242025-01-23 03:38:07 +000096fn get_num_vcpus(cpu_options: &CpuOptions) -> i32 {
97 match cpu_options.cpuTopology {
98 CpuTopology::MatchHost(_) => {
Frederick Mayle681254c2024-12-11 19:20:06 -080099 get_num_cpus().and_then(|v| v.try_into().ok()).unwrap_or_else(|| {
100 warn!("Failed to determine the number of CPUs in the host");
101 INVALID_NUM_CPUS
102 })
103 }
Elie Kheirallahb4b2f242025-01-23 03:38:07 +0000104 CpuTopology::CpuCount(count) => count,
Frederick Mayle681254c2024-12-11 19:20:06 -0800105 }
106}
107
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +0000108/// Write the stats of VMCreation to statsd
Seungjae Yoob2db3d22023-04-06 18:05:43 +0900109/// The function creates a separate thread which waits for statsd to start to push atom
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +0000110pub fn write_vm_creation_stats(
111 config: &VirtualMachineConfig,
112 is_protected: bool,
113 ret: &binder::Result<Strong<dyn IVirtualMachine>>,
114) {
Inseob Kimecde8c02024-09-03 13:11:08 +0900115 if cfg!(early) {
116 info!("Writing VmCreationRequested atom for early VMs is not implemented; skipping");
117 return;
118 }
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +0000119 let creation_succeeded;
120 let binder_exception_code;
121 match ret {
122 Ok(_) => {
123 creation_succeeded = true;
124 binder_exception_code = Status::ok().exception_code() as i32;
125 }
126 Err(ref e) => {
127 creation_succeeded = false;
128 binder_exception_code = e.exception_code() as i32;
129 }
130 }
Frederick Mayle681254c2024-12-11 19:20:06 -0800131
132 let (vm_identifier, config_type, num_cpus, memory_mib, apexes) = match config {
Alan Stokes0d1ef782022-09-27 13:46:35 +0100133 VirtualMachineConfig::AppConfig(config) => (
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000134 config.name.clone(),
Alan Stokes0d1ef782022-09-27 13:46:35 +0100135 vm_creation_requested::ConfigType::VirtualMachineAppConfig,
Elie Kheirallahb4b2f242025-01-23 03:38:07 +0000136 get_num_vcpus(&config.cpuOptions),
Alan Stokes0d1ef782022-09-27 13:46:35 +0100137 config.memoryMib,
138 get_apex_list(config),
139 ),
140 VirtualMachineConfig::RawConfig(config) => (
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000141 config.name.clone(),
Alan Stokes0d1ef782022-09-27 13:46:35 +0100142 vm_creation_requested::ConfigType::VirtualMachineRawConfig,
Elie Kheirallahb4b2f242025-01-23 03:38:07 +0000143 get_num_vcpus(&config.cpuOptions),
Alan Stokes0d1ef782022-09-27 13:46:35 +0100144 config.memoryMib,
145 String::new(),
146 ),
147 };
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +0000148
David Brazdil49f96f52022-12-16 21:29:13 +0000149 let atom = AtomVmCreationRequested {
David Brazdil1f530702022-10-03 12:18:10 +0100150 uid: get_calling_uid() as i32,
David Brazdil49f96f52022-12-16 21:29:13 +0000151 vmIdentifier: vm_identifier,
152 isProtected: is_protected,
153 creationSucceeded: creation_succeeded,
154 binderExceptionCode: binder_exception_code,
155 configType: config_type as i32,
156 numCpus: num_cpus,
157 memoryMib: memory_mib,
158 apexes,
159 };
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +0000160
Seungjae Yooeebe6442023-04-04 13:02:46 +0900161 info!("Writing VmCreationRequested atom into statsd.");
David Brazdil49f96f52022-12-16 21:29:13 +0000162 thread::spawn(move || {
163 GLOBAL_SERVICE.atomVmCreationRequested(&atom).unwrap_or_else(|e| {
164 warn!("Failed to write VmCreationRequested atom: {e}");
165 });
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000166 });
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +0000167}
Seungjae Yooacf559a2022-08-12 04:44:51 +0000168
169/// Write the stats of VM boot to statsd
Seungjae Yoob2db3d22023-04-06 18:05:43 +0900170/// The function creates a separate thread which waits for statsd to start to push atom
Seungjae Yoo2e7beea2022-08-24 16:09:12 +0900171pub fn write_vm_booted_stats(
172 uid: i32,
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000173 vm_identifier: &str,
Seungjae Yoo2e7beea2022-08-24 16:09:12 +0900174 vm_start_timestamp: Option<SystemTime>,
175) {
Inseob Kimecde8c02024-09-03 13:11:08 +0900176 if cfg!(early) {
177 info!("Writing VmCreationRequested atom for early VMs is not implemented; skipping");
178 return;
179 }
180
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000181 let vm_identifier = vm_identifier.to_owned();
Seungjae Yoo2e7beea2022-08-24 16:09:12 +0900182 let duration = get_duration(vm_start_timestamp);
David Brazdil49f96f52022-12-16 21:29:13 +0000183
184 let atom = AtomVmBooted {
185 uid,
186 vmIdentifier: vm_identifier,
187 elapsedTimeMillis: duration.as_millis() as i64,
188 };
189
Seungjae Yooeebe6442023-04-04 13:02:46 +0900190 info!("Writing VmBooted atom into statsd.");
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000191 thread::spawn(move || {
David Brazdil49f96f52022-12-16 21:29:13 +0000192 GLOBAL_SERVICE.atomVmBooted(&atom).unwrap_or_else(|e| {
Seungjae Yooeebe6442023-04-04 13:02:46 +0900193 warn!("Failed to write VmBooted atom: {e}");
David Brazdil49f96f52022-12-16 21:29:13 +0000194 });
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000195 });
Seungjae Yooacf559a2022-08-12 04:44:51 +0000196}
Seungjae Yoob4c07ba2022-08-12 04:44:52 +0000197
198/// Write the stats of VM exit to statsd
Seungjae Yoob2db3d22023-04-06 18:05:43 +0900199pub fn write_vm_exited_stats_sync(
Seungjae Yoo2e7beea2022-08-24 16:09:12 +0900200 uid: i32,
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000201 vm_identifier: &str,
Seungjae Yoo2e7beea2022-08-24 16:09:12 +0900202 reason: DeathReason,
Seungjae Yoo93430e82022-12-05 16:37:42 +0900203 exit_signal: Option<i32>,
Seungjae Yoo6d265d92022-11-15 10:51:33 +0900204 vm_metric: &VmMetric,
Seungjae Yoo2e7beea2022-08-24 16:09:12 +0900205) {
Inseob Kimecde8c02024-09-03 13:11:08 +0900206 if cfg!(early) {
207 info!("Writing VmExited atom for early VMs is not implemented; skipping");
208 return;
209 }
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000210 let vm_identifier = vm_identifier.to_owned();
Seungjae Yoo6d265d92022-11-15 10:51:33 +0900211 let elapsed_time_millis = get_duration(vm_metric.start_timestamp).as_millis() as i64;
212 let guest_time_millis = vm_metric.cpu_guest_time.unwrap_or_default();
213 let rss = vm_metric.rss.unwrap_or_default();
214
David Brazdil49f96f52022-12-16 21:29:13 +0000215 let atom = AtomVmExited {
216 uid,
217 vmIdentifier: vm_identifier,
218 elapsedTimeMillis: elapsed_time_millis,
219 deathReason: reason,
220 guestTimeMillis: guest_time_millis,
221 rssVmKb: rss.vm,
222 rssCrosvmKb: rss.crosvm,
223 exitSignal: exit_signal.unwrap_or_default(),
224 };
225
Seungjae Yooeebe6442023-04-04 13:02:46 +0900226 info!("Writing VmExited atom into statsd.");
Seungjae Yoob2db3d22023-04-06 18:05:43 +0900227 GLOBAL_SERVICE.atomVmExited(&atom).unwrap_or_else(|e| {
228 warn!("Failed to write VmExited atom: {e}");
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000229 });
230}