blob: e430c746ad04e78d3bd59466b08e01d95988a9a5 [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::{
Alan Stokes0d1ef782022-09-27 13:46:35 +010022 IVirtualMachine::IVirtualMachine,
23 VirtualMachineAppConfig::{Payload::Payload, VirtualMachineAppConfig},
24 VirtualMachineConfig::VirtualMachineConfig,
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000025};
26use android_system_virtualizationservice::binder::{Status, Strong};
David Brazdil49f96f52022-12-16 21:29:13 +000027use android_system_virtualizationservice_internal::aidl::android::system::virtualizationservice_internal::{
28 AtomVmBooted::AtomVmBooted,
29 AtomVmCreationRequested::AtomVmCreationRequested,
30 AtomVmExited::AtomVmExited,
31};
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000032use anyhow::{anyhow, Result};
David Brazdil1f530702022-10-03 12:18:10 +010033use binder::ParcelFileDescriptor;
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000034use log::{trace, warn};
35use microdroid_payload_config::VmPayloadConfig;
Shikha Panwarc93a42b2022-11-09 14:12:25 +000036use rustutils::system_properties;
Seungjae Yoodd91f0f2022-11-09 15:25:21 +090037use statslog_virtualization_rust::{vm_booted, vm_creation_requested, vm_exited};
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
Alan Stokes0d1ef782022-09-27 13:46:35 +010042fn get_apex_list(config: &VirtualMachineAppConfig) -> String {
43 match &config.payload {
44 Payload::PayloadConfig(_) => String::new(),
45 Payload::ConfigPath(config_path) => {
46 let vm_payload_config = get_vm_payload_config(&config.apk, config_path);
47 if let Ok(vm_payload_config) = vm_payload_config {
48 vm_payload_config
49 .apexes
50 .iter()
51 .map(|x| x.name.clone())
52 .collect::<Vec<String>>()
53 .join(":")
54 } else {
55 "INFO: Can't get VmPayloadConfig".to_owned()
56 }
57 }
58 }
59}
60
61fn get_vm_payload_config(
62 apk_fd: &Option<ParcelFileDescriptor>,
63 config_path: &str,
64) -> Result<VmPayloadConfig> {
65 let apk = apk_fd.as_ref().ok_or_else(|| anyhow!("APK is none"))?;
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000066 let apk_file = clone_file(apk)?;
67 let mut apk_zip = ZipArchive::new(&apk_file)?;
Alan Stokes0d1ef782022-09-27 13:46:35 +010068 let config_file = apk_zip.by_name(config_path)?;
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000069 let vm_payload_config: VmPayloadConfig = serde_json::from_reader(config_file)?;
70 Ok(vm_payload_config)
71}
72
Seungjae Yoo2e7beea2022-08-24 16:09:12 +090073fn get_duration(vm_start_timestamp: Option<SystemTime>) -> Duration {
74 match vm_start_timestamp {
75 Some(vm_start_timestamp) => vm_start_timestamp.elapsed().unwrap_or_default(),
76 None => Duration::default(),
77 }
78}
79
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000080/// Write the stats of VMCreation to statsd
81pub fn write_vm_creation_stats(
82 config: &VirtualMachineConfig,
83 is_protected: bool,
84 ret: &binder::Result<Strong<dyn IVirtualMachine>>,
85) {
86 let creation_succeeded;
87 let binder_exception_code;
88 match ret {
89 Ok(_) => {
90 creation_succeeded = true;
91 binder_exception_code = Status::ok().exception_code() as i32;
92 }
93 Err(ref e) => {
94 creation_succeeded = false;
95 binder_exception_code = e.exception_code() as i32;
96 }
97 }
Alan Stokes0d1ef782022-09-27 13:46:35 +010098 let (vm_identifier, config_type, num_cpus, memory_mib, apexes) = match config {
99 VirtualMachineConfig::AppConfig(config) => (
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000100 config.name.clone(),
Alan Stokes0d1ef782022-09-27 13:46:35 +0100101 vm_creation_requested::ConfigType::VirtualMachineAppConfig,
102 config.numCpus,
103 config.memoryMib,
104 get_apex_list(config),
105 ),
106 VirtualMachineConfig::RawConfig(config) => (
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000107 config.name.clone(),
Alan Stokes0d1ef782022-09-27 13:46:35 +0100108 vm_creation_requested::ConfigType::VirtualMachineRawConfig,
109 config.numCpus,
110 config.memoryMib,
111 String::new(),
112 ),
113 };
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +0000114
David Brazdil49f96f52022-12-16 21:29:13 +0000115 let atom = AtomVmCreationRequested {
David Brazdil1f530702022-10-03 12:18:10 +0100116 uid: get_calling_uid() as i32,
David Brazdil49f96f52022-12-16 21:29:13 +0000117 vmIdentifier: vm_identifier,
118 isProtected: is_protected,
119 creationSucceeded: creation_succeeded,
120 binderExceptionCode: binder_exception_code,
121 configType: config_type as i32,
122 numCpus: num_cpus,
123 memoryMib: memory_mib,
124 apexes,
125 };
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +0000126
David Brazdil49f96f52022-12-16 21:29:13 +0000127 thread::spawn(move || {
128 GLOBAL_SERVICE.atomVmCreationRequested(&atom).unwrap_or_else(|e| {
129 warn!("Failed to write VmCreationRequested atom: {e}");
130 });
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000131 });
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +0000132}
Seungjae Yooacf559a2022-08-12 04:44:51 +0000133
David Brazdil49f96f52022-12-16 21:29:13 +0000134pub fn forward_vm_creation_atom(atom: &AtomVmCreationRequested) {
135 let config_type = match atom.configType {
136 x if x == vm_creation_requested::ConfigType::VirtualMachineAppConfig as i32 => {
137 vm_creation_requested::ConfigType::VirtualMachineAppConfig
138 }
139 x if x == vm_creation_requested::ConfigType::VirtualMachineRawConfig as i32 => {
140 vm_creation_requested::ConfigType::VirtualMachineRawConfig
141 }
142 _ => vm_creation_requested::ConfigType::UnknownConfig,
143 };
144 let vm_creation_requested = vm_creation_requested::VmCreationRequested {
145 uid: atom.uid,
146 vm_identifier: &atom.vmIdentifier,
147 hypervisor: vm_creation_requested::Hypervisor::Pkvm,
148 is_protected: atom.isProtected,
149 creation_succeeded: atom.creationSucceeded,
150 binder_exception_code: atom.binderExceptionCode,
151 config_type,
152 num_cpus: atom.numCpus,
153 cpu_affinity: "", // deprecated
154 memory_mib: atom.memoryMib,
155 apexes: &atom.apexes,
156 // TODO(seungjaeyoo) Fill information about task_profile
157 // TODO(seungjaeyoo) Fill information about disk_image for raw config
158 };
159
160 wait_for_statsd().unwrap_or_else(|e| warn!("failed to wait for statsd with error: {}", e));
161 match vm_creation_requested.stats_write() {
162 Err(e) => warn!("statslog_rust failed with error: {}", e),
163 Ok(_) => trace!("statslog_rust succeeded for virtualization service"),
164 }
165}
166
Seungjae Yooacf559a2022-08-12 04:44:51 +0000167/// Write the stats of VM boot to statsd
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000168/// The function creates a separate thread which waits fro statsd to start to push atom
Seungjae Yoo2e7beea2022-08-24 16:09:12 +0900169pub fn write_vm_booted_stats(
170 uid: i32,
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000171 vm_identifier: &str,
Seungjae Yoo2e7beea2022-08-24 16:09:12 +0900172 vm_start_timestamp: Option<SystemTime>,
173) {
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000174 let vm_identifier = vm_identifier.to_owned();
Seungjae Yoo2e7beea2022-08-24 16:09:12 +0900175 let duration = get_duration(vm_start_timestamp);
David Brazdil49f96f52022-12-16 21:29:13 +0000176
177 let atom = AtomVmBooted {
178 uid,
179 vmIdentifier: vm_identifier,
180 elapsedTimeMillis: duration.as_millis() as i64,
181 };
182
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000183 thread::spawn(move || {
David Brazdil49f96f52022-12-16 21:29:13 +0000184 GLOBAL_SERVICE.atomVmBooted(&atom).unwrap_or_else(|e| {
185 warn!("Failed to write VmCreationRequested atom: {e}");
186 });
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000187 });
Seungjae Yooacf559a2022-08-12 04:44:51 +0000188}
Seungjae Yoob4c07ba2022-08-12 04:44:52 +0000189
David Brazdil49f96f52022-12-16 21:29:13 +0000190pub fn forward_vm_booted_atom(atom: &AtomVmBooted) {
191 let vm_booted = vm_booted::VmBooted {
192 uid: atom.uid,
193 vm_identifier: &atom.vmIdentifier,
194 elapsed_time_millis: atom.elapsedTimeMillis,
195 };
196
197 wait_for_statsd().unwrap_or_else(|e| warn!("failed to wait for statsd with error: {}", e));
198 match vm_booted.stats_write() {
199 Err(e) => warn!("statslog_rust failed with error: {}", e),
200 Ok(_) => trace!("statslog_rust succeeded for virtualization service"),
201 }
202}
203
Seungjae Yoob4c07ba2022-08-12 04:44:52 +0000204/// Write the stats of VM exit to statsd
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000205/// The function creates a separate thread which waits fro statsd to start to push atom
Seungjae Yoo2e7beea2022-08-24 16:09:12 +0900206pub fn write_vm_exited_stats(
207 uid: i32,
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000208 vm_identifier: &str,
Seungjae Yoo2e7beea2022-08-24 16:09:12 +0900209 reason: DeathReason,
Seungjae Yoo93430e82022-12-05 16:37:42 +0900210 exit_signal: Option<i32>,
Seungjae Yoo6d265d92022-11-15 10:51:33 +0900211 vm_metric: &VmMetric,
Seungjae Yoo2e7beea2022-08-24 16:09:12 +0900212) {
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000213 let vm_identifier = vm_identifier.to_owned();
Seungjae Yoo6d265d92022-11-15 10:51:33 +0900214 let elapsed_time_millis = get_duration(vm_metric.start_timestamp).as_millis() as i64;
215 let guest_time_millis = vm_metric.cpu_guest_time.unwrap_or_default();
216 let rss = vm_metric.rss.unwrap_or_default();
217
David Brazdil49f96f52022-12-16 21:29:13 +0000218 let atom = AtomVmExited {
219 uid,
220 vmIdentifier: vm_identifier,
221 elapsedTimeMillis: elapsed_time_millis,
222 deathReason: reason,
223 guestTimeMillis: guest_time_millis,
224 rssVmKb: rss.vm,
225 rssCrosvmKb: rss.crosvm,
226 exitSignal: exit_signal.unwrap_or_default(),
227 };
228
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000229 thread::spawn(move || {
David Brazdil49f96f52022-12-16 21:29:13 +0000230 GLOBAL_SERVICE.atomVmExited(&atom).unwrap_or_else(|e| {
231 warn!("Failed to write VmExited atom: {e}");
232 });
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000233 });
234}
235
David Brazdil49f96f52022-12-16 21:29:13 +0000236pub fn forward_vm_exited_atom(atom: &AtomVmExited) {
237 let death_reason = match atom.deathReason {
238 DeathReason::INFRASTRUCTURE_ERROR => vm_exited::DeathReason::InfrastructureError,
239 DeathReason::KILLED => vm_exited::DeathReason::Killed,
240 DeathReason::UNKNOWN => vm_exited::DeathReason::Unknown,
241 DeathReason::SHUTDOWN => vm_exited::DeathReason::Shutdown,
242 DeathReason::START_FAILED => vm_exited::DeathReason::Error,
243 DeathReason::REBOOT => vm_exited::DeathReason::Reboot,
244 DeathReason::CRASH => vm_exited::DeathReason::Crash,
245 DeathReason::PVM_FIRMWARE_PUBLIC_KEY_MISMATCH => {
246 vm_exited::DeathReason::PvmFirmwarePublicKeyMismatch
247 }
248 DeathReason::PVM_FIRMWARE_INSTANCE_IMAGE_CHANGED => {
249 vm_exited::DeathReason::PvmFirmwareInstanceImageChanged
250 }
251 DeathReason::BOOTLOADER_PUBLIC_KEY_MISMATCH => {
252 vm_exited::DeathReason::BootloaderPublicKeyMismatch
253 }
254 DeathReason::BOOTLOADER_INSTANCE_IMAGE_CHANGED => {
255 vm_exited::DeathReason::BootloaderInstanceImageChanged
256 }
257 DeathReason::MICRODROID_FAILED_TO_CONNECT_TO_VIRTUALIZATION_SERVICE => {
258 vm_exited::DeathReason::MicrodroidFailedToConnectToVirtualizationService
259 }
260 DeathReason::MICRODROID_PAYLOAD_HAS_CHANGED => {
261 vm_exited::DeathReason::MicrodroidPayloadHasChanged
262 }
263 DeathReason::MICRODROID_PAYLOAD_VERIFICATION_FAILED => {
264 vm_exited::DeathReason::MicrodroidPayloadVerificationFailed
265 }
266 DeathReason::MICRODROID_INVALID_PAYLOAD_CONFIG => {
267 vm_exited::DeathReason::MicrodroidInvalidPayloadConfig
268 }
269 DeathReason::MICRODROID_UNKNOWN_RUNTIME_ERROR => {
270 vm_exited::DeathReason::MicrodroidUnknownRuntimeError
271 }
272 DeathReason::HANGUP => vm_exited::DeathReason::Hangup,
273 _ => vm_exited::DeathReason::Unknown,
274 };
275
276 let vm_exited = vm_exited::VmExited {
277 uid: atom.uid,
278 vm_identifier: &atom.vmIdentifier,
279 elapsed_time_millis: atom.elapsedTimeMillis,
280 death_reason,
281 guest_time_millis: atom.guestTimeMillis,
282 rss_vm_kb: atom.rssVmKb,
283 rss_crosvm_kb: atom.rssCrosvmKb,
284 exit_signal: atom.exitSignal,
285 };
286
287 wait_for_statsd().unwrap_or_else(|e| warn!("failed to wait for statsd with error: {}", e));
288 match vm_exited.stats_write() {
289 Err(e) => warn!("statslog_rust failed with error: {}", e),
290 Ok(_) => trace!("statslog_rust succeeded for virtualization service"),
291 }
292}
293
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000294fn wait_for_statsd() -> Result<()> {
295 let mut prop = system_properties::PropertyWatcher::new("init.svc.statsd")?;
296 loop {
297 prop.wait()?;
298 match system_properties::read("init.svc.statsd")? {
299 Some(s) => {
300 if s == "running" {
301 break;
302 }
303 }
304 None => {
305 // This case never really happens because
306 // prop.wait() waits for property to be non-null.
307 break;
308 }
309 }
Seungjae Yoob4c07ba2022-08-12 04:44:52 +0000310 }
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000311 Ok(())
Seungjae Yoob4c07ba2022-08-12 04:44:52 +0000312}