blob: 698dbca24f2f8a579713fbf61a492d098ebfbce7 [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 Brazdil49f96f52022-12-16 21:29:13 +000019use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::DeathReason::DeathReason;
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000020use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
Alan Stokes0d1ef782022-09-27 13:46:35 +010021 IVirtualMachine::IVirtualMachine,
22 VirtualMachineAppConfig::{Payload::Payload, VirtualMachineAppConfig},
23 VirtualMachineConfig::VirtualMachineConfig,
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000024};
25use android_system_virtualizationservice::binder::{Status, Strong};
David Brazdil49f96f52022-12-16 21:29:13 +000026use android_system_virtualizationservice_internal::aidl::android::system::virtualizationservice_internal::{
27 AtomVmBooted::AtomVmBooted,
28 AtomVmCreationRequested::AtomVmCreationRequested,
29 AtomVmExited::AtomVmExited,
30};
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000031use anyhow::{anyhow, Result};
Alan Stokes0d1ef782022-09-27 13:46:35 +010032use binder::{ParcelFileDescriptor, ThreadState};
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000033use log::{trace, warn};
34use microdroid_payload_config::VmPayloadConfig;
Shikha Panwarc93a42b2022-11-09 14:12:25 +000035use rustutils::system_properties;
Seungjae Yoodd91f0f2022-11-09 15:25:21 +090036use statslog_virtualization_rust::{vm_booted, vm_creation_requested, vm_exited};
Shikha Panwarc93a42b2022-11-09 14:12:25 +000037use std::thread;
Seungjae Yoo2e7beea2022-08-24 16:09:12 +090038use std::time::{Duration, SystemTime};
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000039use zip::ZipArchive;
40
Alan Stokes0d1ef782022-09-27 13:46:35 +010041fn get_apex_list(config: &VirtualMachineAppConfig) -> String {
42 match &config.payload {
43 Payload::PayloadConfig(_) => String::new(),
44 Payload::ConfigPath(config_path) => {
45 let vm_payload_config = get_vm_payload_config(&config.apk, config_path);
46 if let Ok(vm_payload_config) = vm_payload_config {
47 vm_payload_config
48 .apexes
49 .iter()
50 .map(|x| x.name.clone())
51 .collect::<Vec<String>>()
52 .join(":")
53 } else {
54 "INFO: Can't get VmPayloadConfig".to_owned()
55 }
56 }
57 }
58}
59
60fn get_vm_payload_config(
61 apk_fd: &Option<ParcelFileDescriptor>,
62 config_path: &str,
63) -> Result<VmPayloadConfig> {
64 let apk = apk_fd.as_ref().ok_or_else(|| anyhow!("APK is none"))?;
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000065 let apk_file = clone_file(apk)?;
66 let mut apk_zip = ZipArchive::new(&apk_file)?;
Alan Stokes0d1ef782022-09-27 13:46:35 +010067 let config_file = apk_zip.by_name(config_path)?;
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000068 let vm_payload_config: VmPayloadConfig = serde_json::from_reader(config_file)?;
69 Ok(vm_payload_config)
70}
71
Seungjae Yoo2e7beea2022-08-24 16:09:12 +090072fn get_duration(vm_start_timestamp: Option<SystemTime>) -> Duration {
73 match vm_start_timestamp {
74 Some(vm_start_timestamp) => vm_start_timestamp.elapsed().unwrap_or_default(),
75 None => Duration::default(),
76 }
77}
78
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +000079/// Write the stats of VMCreation to statsd
80pub fn write_vm_creation_stats(
81 config: &VirtualMachineConfig,
82 is_protected: bool,
83 ret: &binder::Result<Strong<dyn IVirtualMachine>>,
84) {
85 let creation_succeeded;
86 let binder_exception_code;
87 match ret {
88 Ok(_) => {
89 creation_succeeded = true;
90 binder_exception_code = Status::ok().exception_code() as i32;
91 }
92 Err(ref e) => {
93 creation_succeeded = false;
94 binder_exception_code = e.exception_code() as i32;
95 }
96 }
Alan Stokes0d1ef782022-09-27 13:46:35 +010097 let (vm_identifier, config_type, num_cpus, memory_mib, apexes) = match config {
98 VirtualMachineConfig::AppConfig(config) => (
Shikha Panwarc93a42b2022-11-09 14:12:25 +000099 config.name.clone(),
Alan Stokes0d1ef782022-09-27 13:46:35 +0100100 vm_creation_requested::ConfigType::VirtualMachineAppConfig,
101 config.numCpus,
102 config.memoryMib,
103 get_apex_list(config),
104 ),
105 VirtualMachineConfig::RawConfig(config) => (
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000106 config.name.clone(),
Alan Stokes0d1ef782022-09-27 13:46:35 +0100107 vm_creation_requested::ConfigType::VirtualMachineRawConfig,
108 config.numCpus,
109 config.memoryMib,
110 String::new(),
111 ),
112 };
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +0000113
David Brazdil49f96f52022-12-16 21:29:13 +0000114 let atom = AtomVmCreationRequested {
115 uid: ThreadState::get_calling_uid() as i32,
116 vmIdentifier: vm_identifier,
117 isProtected: is_protected,
118 creationSucceeded: creation_succeeded,
119 binderExceptionCode: binder_exception_code,
120 configType: config_type as i32,
121 numCpus: num_cpus,
122 memoryMib: memory_mib,
123 apexes,
124 };
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +0000125
David Brazdil49f96f52022-12-16 21:29:13 +0000126 thread::spawn(move || {
127 GLOBAL_SERVICE.atomVmCreationRequested(&atom).unwrap_or_else(|e| {
128 warn!("Failed to write VmCreationRequested atom: {e}");
129 });
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000130 });
Seungjae Yoo0a8c84c2022-07-11 08:19:15 +0000131}
Seungjae Yooacf559a2022-08-12 04:44:51 +0000132
David Brazdil49f96f52022-12-16 21:29:13 +0000133pub fn forward_vm_creation_atom(atom: &AtomVmCreationRequested) {
134 let config_type = match atom.configType {
135 x if x == vm_creation_requested::ConfigType::VirtualMachineAppConfig as i32 => {
136 vm_creation_requested::ConfigType::VirtualMachineAppConfig
137 }
138 x if x == vm_creation_requested::ConfigType::VirtualMachineRawConfig as i32 => {
139 vm_creation_requested::ConfigType::VirtualMachineRawConfig
140 }
141 _ => vm_creation_requested::ConfigType::UnknownConfig,
142 };
143 let vm_creation_requested = vm_creation_requested::VmCreationRequested {
144 uid: atom.uid,
145 vm_identifier: &atom.vmIdentifier,
146 hypervisor: vm_creation_requested::Hypervisor::Pkvm,
147 is_protected: atom.isProtected,
148 creation_succeeded: atom.creationSucceeded,
149 binder_exception_code: atom.binderExceptionCode,
150 config_type,
151 num_cpus: atom.numCpus,
152 cpu_affinity: "", // deprecated
153 memory_mib: atom.memoryMib,
154 apexes: &atom.apexes,
155 // TODO(seungjaeyoo) Fill information about task_profile
156 // TODO(seungjaeyoo) Fill information about disk_image for raw config
157 };
158
159 wait_for_statsd().unwrap_or_else(|e| warn!("failed to wait for statsd with error: {}", e));
160 match vm_creation_requested.stats_write() {
161 Err(e) => warn!("statslog_rust failed with error: {}", e),
162 Ok(_) => trace!("statslog_rust succeeded for virtualization service"),
163 }
164}
165
Seungjae Yooacf559a2022-08-12 04:44:51 +0000166/// Write the stats of VM boot to statsd
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000167/// The function creates a separate thread which waits fro statsd to start to push atom
Seungjae Yoo2e7beea2022-08-24 16:09:12 +0900168pub fn write_vm_booted_stats(
169 uid: i32,
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000170 vm_identifier: &str,
Seungjae Yoo2e7beea2022-08-24 16:09:12 +0900171 vm_start_timestamp: Option<SystemTime>,
172) {
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000173 let vm_identifier = vm_identifier.to_owned();
Seungjae Yoo2e7beea2022-08-24 16:09:12 +0900174 let duration = get_duration(vm_start_timestamp);
David Brazdil49f96f52022-12-16 21:29:13 +0000175
176 let atom = AtomVmBooted {
177 uid,
178 vmIdentifier: vm_identifier,
179 elapsedTimeMillis: duration.as_millis() as i64,
180 };
181
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000182 thread::spawn(move || {
David Brazdil49f96f52022-12-16 21:29:13 +0000183 GLOBAL_SERVICE.atomVmBooted(&atom).unwrap_or_else(|e| {
184 warn!("Failed to write VmCreationRequested atom: {e}");
185 });
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000186 });
Seungjae Yooacf559a2022-08-12 04:44:51 +0000187}
Seungjae Yoob4c07ba2022-08-12 04:44:52 +0000188
David Brazdil49f96f52022-12-16 21:29:13 +0000189pub fn forward_vm_booted_atom(atom: &AtomVmBooted) {
190 let vm_booted = vm_booted::VmBooted {
191 uid: atom.uid,
192 vm_identifier: &atom.vmIdentifier,
193 elapsed_time_millis: atom.elapsedTimeMillis,
194 };
195
196 wait_for_statsd().unwrap_or_else(|e| warn!("failed to wait for statsd with error: {}", e));
197 match vm_booted.stats_write() {
198 Err(e) => warn!("statslog_rust failed with error: {}", e),
199 Ok(_) => trace!("statslog_rust succeeded for virtualization service"),
200 }
201}
202
Seungjae Yoob4c07ba2022-08-12 04:44:52 +0000203/// Write the stats of VM exit to statsd
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000204/// The function creates a separate thread which waits fro statsd to start to push atom
Seungjae Yoo2e7beea2022-08-24 16:09:12 +0900205pub fn write_vm_exited_stats(
206 uid: i32,
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000207 vm_identifier: &str,
Seungjae Yoo2e7beea2022-08-24 16:09:12 +0900208 reason: DeathReason,
Seungjae Yoo93430e82022-12-05 16:37:42 +0900209 exit_signal: Option<i32>,
Seungjae Yoo6d265d92022-11-15 10:51:33 +0900210 vm_metric: &VmMetric,
Seungjae Yoo2e7beea2022-08-24 16:09:12 +0900211) {
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000212 let vm_identifier = vm_identifier.to_owned();
Seungjae Yoo6d265d92022-11-15 10:51:33 +0900213 let elapsed_time_millis = get_duration(vm_metric.start_timestamp).as_millis() as i64;
214 let guest_time_millis = vm_metric.cpu_guest_time.unwrap_or_default();
215 let rss = vm_metric.rss.unwrap_or_default();
216
David Brazdil49f96f52022-12-16 21:29:13 +0000217 let atom = AtomVmExited {
218 uid,
219 vmIdentifier: vm_identifier,
220 elapsedTimeMillis: elapsed_time_millis,
221 deathReason: reason,
222 guestTimeMillis: guest_time_millis,
223 rssVmKb: rss.vm,
224 rssCrosvmKb: rss.crosvm,
225 exitSignal: exit_signal.unwrap_or_default(),
226 };
227
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000228 thread::spawn(move || {
David Brazdil49f96f52022-12-16 21:29:13 +0000229 GLOBAL_SERVICE.atomVmExited(&atom).unwrap_or_else(|e| {
230 warn!("Failed to write VmExited atom: {e}");
231 });
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000232 });
233}
234
David Brazdil49f96f52022-12-16 21:29:13 +0000235pub fn forward_vm_exited_atom(atom: &AtomVmExited) {
236 let death_reason = match atom.deathReason {
237 DeathReason::INFRASTRUCTURE_ERROR => vm_exited::DeathReason::InfrastructureError,
238 DeathReason::KILLED => vm_exited::DeathReason::Killed,
239 DeathReason::UNKNOWN => vm_exited::DeathReason::Unknown,
240 DeathReason::SHUTDOWN => vm_exited::DeathReason::Shutdown,
241 DeathReason::START_FAILED => vm_exited::DeathReason::Error,
242 DeathReason::REBOOT => vm_exited::DeathReason::Reboot,
243 DeathReason::CRASH => vm_exited::DeathReason::Crash,
244 DeathReason::PVM_FIRMWARE_PUBLIC_KEY_MISMATCH => {
245 vm_exited::DeathReason::PvmFirmwarePublicKeyMismatch
246 }
247 DeathReason::PVM_FIRMWARE_INSTANCE_IMAGE_CHANGED => {
248 vm_exited::DeathReason::PvmFirmwareInstanceImageChanged
249 }
250 DeathReason::BOOTLOADER_PUBLIC_KEY_MISMATCH => {
251 vm_exited::DeathReason::BootloaderPublicKeyMismatch
252 }
253 DeathReason::BOOTLOADER_INSTANCE_IMAGE_CHANGED => {
254 vm_exited::DeathReason::BootloaderInstanceImageChanged
255 }
256 DeathReason::MICRODROID_FAILED_TO_CONNECT_TO_VIRTUALIZATION_SERVICE => {
257 vm_exited::DeathReason::MicrodroidFailedToConnectToVirtualizationService
258 }
259 DeathReason::MICRODROID_PAYLOAD_HAS_CHANGED => {
260 vm_exited::DeathReason::MicrodroidPayloadHasChanged
261 }
262 DeathReason::MICRODROID_PAYLOAD_VERIFICATION_FAILED => {
263 vm_exited::DeathReason::MicrodroidPayloadVerificationFailed
264 }
265 DeathReason::MICRODROID_INVALID_PAYLOAD_CONFIG => {
266 vm_exited::DeathReason::MicrodroidInvalidPayloadConfig
267 }
268 DeathReason::MICRODROID_UNKNOWN_RUNTIME_ERROR => {
269 vm_exited::DeathReason::MicrodroidUnknownRuntimeError
270 }
271 DeathReason::HANGUP => vm_exited::DeathReason::Hangup,
272 _ => vm_exited::DeathReason::Unknown,
273 };
274
275 let vm_exited = vm_exited::VmExited {
276 uid: atom.uid,
277 vm_identifier: &atom.vmIdentifier,
278 elapsed_time_millis: atom.elapsedTimeMillis,
279 death_reason,
280 guest_time_millis: atom.guestTimeMillis,
281 rss_vm_kb: atom.rssVmKb,
282 rss_crosvm_kb: atom.rssCrosvmKb,
283 exit_signal: atom.exitSignal,
284 };
285
286 wait_for_statsd().unwrap_or_else(|e| warn!("failed to wait for statsd with error: {}", e));
287 match vm_exited.stats_write() {
288 Err(e) => warn!("statslog_rust failed with error: {}", e),
289 Ok(_) => trace!("statslog_rust succeeded for virtualization service"),
290 }
291}
292
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000293fn wait_for_statsd() -> Result<()> {
294 let mut prop = system_properties::PropertyWatcher::new("init.svc.statsd")?;
295 loop {
296 prop.wait()?;
297 match system_properties::read("init.svc.statsd")? {
298 Some(s) => {
299 if s == "running" {
300 break;
301 }
302 }
303 None => {
304 // This case never really happens because
305 // prop.wait() waits for property to be non-null.
306 break;
307 }
308 }
Seungjae Yoob4c07ba2022-08-12 04:44:52 +0000309 }
Shikha Panwarc93a42b2022-11-09 14:12:25 +0000310 Ok(())
Seungjae Yoob4c07ba2022-08-12 04:44:52 +0000311}