Implement collecting VmStatus in microdroid_manager
Bug: 236253808
Test: Manually run statsd_testdrive 512 while running atest MicrodroidTestCase#testTelemetryPushedAtoms
Change-Id: I55ebb75f93943a3095f27c74fcef577a1d054865
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 78f8bb4..f465d43 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -18,6 +18,7 @@
mod instance;
mod ioutil;
mod payload;
+mod procutil;
mod swap;
mod vm_payload_service;
@@ -25,8 +26,12 @@
use crate::instance::{ApexData, ApkData, InstanceDisk, MicrodroidData, RootHash};
use crate::vm_payload_service::register_vm_payload_service;
use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::ErrorCode::ErrorCode;
-use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::{
- VM_BINDER_SERVICE_PORT, VM_STREAM_SERVICE_PORT, IVirtualMachineService,
+use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::{
+ IVirtualMachineService::{
+ IVirtualMachineService, VM_BINDER_SERVICE_PORT, VM_STREAM_SERVICE_PORT,
+ },
+ VirtualMachineCpuStatus::VirtualMachineCpuStatus,
+ VirtualMachineMemStatus::VirtualMachineMemStatus,
};
use anyhow::{anyhow, bail, ensure, Context, Error, Result};
use apkverify::{get_public_key_der, verify, V4Signature};
@@ -36,9 +41,10 @@
use itertools::sorted;
use log::{error, info};
use microdroid_metadata::{write_metadata, Metadata, PayloadMetadata};
-use microdroid_payload_config::{Task, TaskType, VmPayloadConfig, OsConfig};
+use microdroid_payload_config::{OsConfig, Task, TaskType, VmPayloadConfig};
use openssl::sha::Sha512;
use payload::{get_apex_data_from_payload, load_metadata, to_metadata};
+use procutil::{get_cpu_time, get_mem_info};
use rand::Fill;
use rpcbinder::get_vsock_rpc_interface;
use rustutils::system_properties;
@@ -50,6 +56,7 @@
use std::path::Path;
use std::process::{Child, Command, Stdio};
use std::str;
+use std::thread;
use std::time::{Duration, SystemTime};
use vsock::VsockStream;
@@ -89,6 +96,42 @@
InvalidConfig(String),
}
+fn send_vm_status() -> Result<()> {
+ let service = get_vms_rpc_binder()
+ .context("cannot connect to VirtualMachineService")
+ .map_err(|e| MicrodroidError::FailedToConnectToVirtualizationService(e.to_string()))?;
+
+ let one_second = Duration::from_millis(1000);
+ loop {
+ // Collect VM CPU time information and creating VmCpuStatus atom for metrics.
+ let cpu_time = get_cpu_time()?;
+ let vm_cpu_status = VirtualMachineCpuStatus {
+ cpu_time_user: cpu_time.user,
+ cpu_time_nice: cpu_time.nice,
+ cpu_time_sys: cpu_time.sys,
+ cpu_time_idle: cpu_time.idle,
+ };
+ service
+ .notifyCpuStatus(&vm_cpu_status)
+ .expect("Can't send information about VM CPU status");
+
+ // Collect VM memory information and creating VmMemStatus atom for metrics.
+ let mem_info = get_mem_info()?;
+ let vm_mem_status = VirtualMachineMemStatus {
+ mem_total: mem_info.total,
+ mem_free: mem_info.free,
+ mem_available: mem_info.available,
+ mem_buffer: mem_info.buffer,
+ mem_cached: mem_info.cached,
+ };
+ service
+ .notifyMemStatus(&vm_mem_status)
+ .expect("Can't send information about VM memory status");
+
+ thread::sleep(one_second);
+ }
+}
+
fn translate_error(err: &Error) -> (ErrorCode, String) {
if let Some(e) = err.downcast_ref::<MicrodroidError>() {
match e {
@@ -177,6 +220,13 @@
let service = get_vms_rpc_binder()
.context("cannot connect to VirtualMachineService")
.map_err(|e| MicrodroidError::FailedToConnectToVirtualizationService(e.to_string()))?;
+
+ thread::spawn(move || {
+ if let Err(e) = send_vm_status() {
+ error!("failed to get virtual machine status: {:?}", e);
+ }
+ });
+
match try_run_payload(&service) {
Ok(code) => {
info!("notifying payload finished");
diff --git a/microdroid_manager/src/procutil.rs b/microdroid_manager/src/procutil.rs
new file mode 100644
index 0000000..b323ca9
--- /dev/null
+++ b/microdroid_manager/src/procutil.rs
@@ -0,0 +1,125 @@
+// Copyright 2022, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use anyhow::{Context, Result};
+use libc::{sysconf, _SC_CLK_TCK};
+use regex::Regex;
+use std::fs::{self, File};
+use std::io::{BufRead, BufReader};
+
+const MILLIS_PER_SEC: i64 = 1000;
+
+pub struct CpuTime {
+ pub user: i64,
+ pub nice: i64,
+ pub sys: i64,
+ pub idle: i64,
+}
+
+pub struct MemInfo {
+ pub total: i64,
+ pub free: i64,
+ pub available: i64,
+ pub buffer: i64,
+ pub cached: i64,
+}
+
+// Get CPU time information from /proc/stat
+//
+// /proc/stat example(omitted):
+// cpu 24790952 21104390 10771070 10480973587 1700955 0 410931 0 316532 0
+// cpu0 169636 141307 61153 81785791 9605 0 183524 0 1345 0
+// cpu1 182431 198327 68273 81431817 10445 0 32392 0 2616 0
+// cpu2 183209 174917 68591 81933935 12239 0 10042 0 2415 0
+// cpu3 183413 177758 69908 81927474 13354 0 5853 0 2491 0
+// intr 7913477443 39 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+// ctxt 10326710014
+// btime 1664123605
+// processes 9225712
+// procs_running 1
+// procs_blocked 0
+// softirq 2683914305 14595298 304837101 1581 327291100 16397051 0 208857783 1024640365 787932 786506094
+//
+// expected output:
+// user: 24790952
+// nice: 21104390
+// sys: 10771070
+// idle: 10480973587
+pub fn get_cpu_time() -> Result<CpuTime> {
+ let re = Regex::new(r"^cpu\s+([\d]+)\s([\d]+)\s([\d]+)\s([\d]+)").unwrap();
+
+ let mut proc_stat = BufReader::new(File::open("/proc/stat")?);
+ let mut line = String::new();
+ proc_stat.read_line(&mut line)?;
+ let data_list = re.captures(&line).context("Failed to capture values")?;
+
+ let ticks_per_sec = unsafe { sysconf(_SC_CLK_TCK) } as i64;
+ let cpu_time = CpuTime {
+ user: data_list.get(1).unwrap().as_str().parse::<i64>()? * MILLIS_PER_SEC / ticks_per_sec,
+ nice: data_list.get(2).unwrap().as_str().parse::<i64>()? * MILLIS_PER_SEC / ticks_per_sec,
+ sys: data_list.get(3).unwrap().as_str().parse::<i64>()? * MILLIS_PER_SEC / ticks_per_sec,
+ idle: data_list.get(4).unwrap().as_str().parse::<i64>()? * MILLIS_PER_SEC / ticks_per_sec,
+ };
+ Ok(cpu_time)
+}
+
+// Get memory information from /proc/meminfo
+//
+// /proc/meminfo example(omitted):
+// MemTotal: 263742736 kB
+// MemFree: 37144204 kB
+// MemAvailable: 249168700 kB
+// Buffers: 10231296 kB
+// Cached: 189502836 kB
+// SwapCached: 113848 kB
+// Active: 132266424 kB
+// Inactive: 73587504 kB
+// Active(anon): 1455240 kB
+// Inactive(anon): 6993584 kB
+// Active(file): 130811184 kB
+// Inactive(file): 66593920 kB
+// Unevictable: 56436 kB
+// Mlocked: 56436 kB
+// SwapTotal: 255123452 kB
+// SwapFree: 254499068 kB
+// Dirty: 596 kB
+// Writeback: 0 kB
+// AnonPages: 5295864 kB
+// Mapped: 3512608 kB
+//
+// expected output:
+// total: 263742736
+// free: 37144204
+// available: 249168700
+// buffer: 10231296
+// cached: 189502836
+pub fn get_mem_info() -> Result<MemInfo> {
+ let re = Regex::new(r"^.*?:\s+([0-9]+)\skB").unwrap();
+
+ let proc_mem_info = fs::read_to_string("/proc/meminfo")?;
+ let data_list: Vec<_> = proc_mem_info
+ .trim()
+ .splitn(6, '\n')
+ .map(|s| re.captures(s).context("Failed to capture values").ok()?.get(1))
+ .collect();
+
+ let mem_info = MemInfo {
+ total: data_list[0].unwrap().as_str().parse::<i64>()?,
+ free: data_list[1].unwrap().as_str().parse::<i64>()?,
+ available: data_list[2].unwrap().as_str().parse::<i64>()?,
+ buffer: data_list[3].unwrap().as_str().parse::<i64>()?,
+ cached: data_list[4].unwrap().as_str().parse::<i64>()?,
+ };
+ Ok(mem_info)
+}