1 // 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 
17 use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::DeathReason::DeathReason;
18 use android_system_virtualizationservice_internal::aidl::android::system::virtualizationservice_internal::{
19     AtomVmBooted::AtomVmBooted,
20     AtomVmCreationRequested::AtomVmCreationRequested,
21     AtomVmExited::AtomVmExited,
22 };
23 use anyhow::Result;
24 use log::{trace, warn};
25 use rustutils::system_properties;
26 use statslog_virtualization_rust::{vm_booted, vm_creation_requested, vm_exited};
27 
forward_vm_creation_atom(atom: &AtomVmCreationRequested)28 pub fn forward_vm_creation_atom(atom: &AtomVmCreationRequested) {
29     let config_type = match atom.configType {
30         x if x == vm_creation_requested::ConfigType::VirtualMachineAppConfig as i32 => {
31             vm_creation_requested::ConfigType::VirtualMachineAppConfig
32         }
33         x if x == vm_creation_requested::ConfigType::VirtualMachineRawConfig as i32 => {
34             vm_creation_requested::ConfigType::VirtualMachineRawConfig
35         }
36         _ => vm_creation_requested::ConfigType::UnknownConfig,
37     };
38     let vm_creation_requested = vm_creation_requested::VmCreationRequested {
39         uid: atom.uid,
40         vm_identifier: &atom.vmIdentifier,
41         hypervisor: vm_creation_requested::Hypervisor::Pkvm,
42         is_protected: atom.isProtected,
43         creation_succeeded: atom.creationSucceeded,
44         binder_exception_code: atom.binderExceptionCode,
45         config_type,
46         num_cpus: atom.numCpus,
47         cpu_affinity: "", // deprecated
48         memory_mib: atom.memoryMib,
49         apexes: &atom.apexes,
50         // TODO(seungjaeyoo) Fill information about task_profile
51         // TODO(seungjaeyoo) Fill information about disk_image for raw config
52     };
53 
54     wait_for_statsd().unwrap_or_else(|e| warn!("failed to wait for statsd with error: {}", e));
55     match vm_creation_requested.stats_write() {
56         Err(e) => warn!("statslog_rust failed with error: {}", e),
57         Ok(_) => trace!("statslog_rust succeeded for virtualization service"),
58     }
59 }
60 
forward_vm_booted_atom(atom: &AtomVmBooted)61 pub fn forward_vm_booted_atom(atom: &AtomVmBooted) {
62     let vm_booted = vm_booted::VmBooted {
63         uid: atom.uid,
64         vm_identifier: &atom.vmIdentifier,
65         elapsed_time_millis: atom.elapsedTimeMillis,
66     };
67 
68     wait_for_statsd().unwrap_or_else(|e| warn!("failed to wait for statsd with error: {}", e));
69     match vm_booted.stats_write() {
70         Err(e) => warn!("statslog_rust failed with error: {}", e),
71         Ok(_) => trace!("statslog_rust succeeded for virtualization service"),
72     }
73 }
74 
forward_vm_exited_atom(atom: &AtomVmExited)75 pub fn forward_vm_exited_atom(atom: &AtomVmExited) {
76     let death_reason = match atom.deathReason {
77         DeathReason::INFRASTRUCTURE_ERROR => vm_exited::DeathReason::InfrastructureError,
78         DeathReason::KILLED => vm_exited::DeathReason::Killed,
79         DeathReason::UNKNOWN => vm_exited::DeathReason::Unknown,
80         DeathReason::SHUTDOWN => vm_exited::DeathReason::Shutdown,
81         DeathReason::START_FAILED => vm_exited::DeathReason::Error,
82         DeathReason::REBOOT => vm_exited::DeathReason::Reboot,
83         DeathReason::CRASH => vm_exited::DeathReason::Crash,
84         DeathReason::PVM_FIRMWARE_PUBLIC_KEY_MISMATCH => {
85             vm_exited::DeathReason::PvmFirmwarePublicKeyMismatch
86         }
87         DeathReason::PVM_FIRMWARE_INSTANCE_IMAGE_CHANGED => {
88             vm_exited::DeathReason::PvmFirmwareInstanceImageChanged
89         }
90         DeathReason::MICRODROID_FAILED_TO_CONNECT_TO_VIRTUALIZATION_SERVICE => {
91             vm_exited::DeathReason::MicrodroidFailedToConnectToVirtualizationService
92         }
93         DeathReason::MICRODROID_PAYLOAD_HAS_CHANGED => {
94             vm_exited::DeathReason::MicrodroidPayloadHasChanged
95         }
96         DeathReason::MICRODROID_PAYLOAD_VERIFICATION_FAILED => {
97             vm_exited::DeathReason::MicrodroidPayloadVerificationFailed
98         }
99         DeathReason::MICRODROID_INVALID_PAYLOAD_CONFIG => {
100             vm_exited::DeathReason::MicrodroidInvalidPayloadConfig
101         }
102         DeathReason::MICRODROID_UNKNOWN_RUNTIME_ERROR => {
103             vm_exited::DeathReason::MicrodroidUnknownRuntimeError
104         }
105         DeathReason::HANGUP => vm_exited::DeathReason::Hangup,
106         _ => vm_exited::DeathReason::Unknown,
107     };
108 
109     let vm_exited = vm_exited::VmExited {
110         uid: atom.uid,
111         vm_identifier: &atom.vmIdentifier,
112         elapsed_time_millis: atom.elapsedTimeMillis,
113         death_reason,
114         guest_time_millis: atom.guestTimeMillis,
115         rss_vm_kb: atom.rssVmKb,
116         rss_crosvm_kb: atom.rssCrosvmKb,
117         exit_signal: atom.exitSignal,
118     };
119 
120     wait_for_statsd().unwrap_or_else(|e| warn!("failed to wait for statsd with error: {}", e));
121     match vm_exited.stats_write() {
122         Err(e) => warn!("statslog_rust failed with error: {}", e),
123         Ok(_) => trace!("statslog_rust succeeded for virtualization service"),
124     }
125 }
126 
wait_for_statsd() -> Result<()>127 fn wait_for_statsd() -> Result<()> {
128     let mut prop = system_properties::PropertyWatcher::new("init.svc.statsd")?;
129     loop {
130         prop.wait()?;
131         match system_properties::read("init.svc.statsd")? {
132             Some(s) => {
133                 if s == "running" {
134                     break;
135                 }
136             }
137             None => {
138                 // This case never really happens because
139                 // prop.wait() waits for property to be non-null.
140                 break;
141             }
142         }
143     }
144     Ok(())
145 }
146