1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2023 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker
5*bb4ee6a4SAndroid Build Coastguard Worker use std::collections::HashMap;
6*bb4ee6a4SAndroid Build Coastguard Worker use std::env;
7*bb4ee6a4SAndroid Build Coastguard Worker use std::process::Command;
8*bb4ee6a4SAndroid Build Coastguard Worker use std::process::Stdio;
9*bb4ee6a4SAndroid Build Coastguard Worker use std::thread;
10*bb4ee6a4SAndroid Build Coastguard Worker use std::time::Duration;
11*bb4ee6a4SAndroid Build Coastguard Worker
12*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::bail;
13*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::Context;
14*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::Result;
15*bb4ee6a4SAndroid Build Coastguard Worker use rayon::prelude::*;
16*bb4ee6a4SAndroid Build Coastguard Worker
17*bb4ee6a4SAndroid Build Coastguard Worker /// The target device running crosvm to collect information from.
18*bb4ee6a4SAndroid Build Coastguard Worker struct Target {
19*bb4ee6a4SAndroid Build Coastguard Worker /// SSH host name.
20*bb4ee6a4SAndroid Build Coastguard Worker host: String,
21*bb4ee6a4SAndroid Build Coastguard Worker }
22*bb4ee6a4SAndroid Build Coastguard Worker
23*bb4ee6a4SAndroid Build Coastguard Worker impl Target {
do_command(&self, command: Vec<&str>) -> Result<String>24*bb4ee6a4SAndroid Build Coastguard Worker fn do_command(&self, command: Vec<&str>) -> Result<String> {
25*bb4ee6a4SAndroid Build Coastguard Worker let child = Command::new("ssh")
26*bb4ee6a4SAndroid Build Coastguard Worker .arg(&self.host)
27*bb4ee6a4SAndroid Build Coastguard Worker .args(&command)
28*bb4ee6a4SAndroid Build Coastguard Worker .stdout(Stdio::piped())
29*bb4ee6a4SAndroid Build Coastguard Worker .stderr(Stdio::piped())
30*bb4ee6a4SAndroid Build Coastguard Worker .spawn()
31*bb4ee6a4SAndroid Build Coastguard Worker .context("failed to execute process")?;
32*bb4ee6a4SAndroid Build Coastguard Worker let output = child
33*bb4ee6a4SAndroid Build Coastguard Worker .wait_with_output()
34*bb4ee6a4SAndroid Build Coastguard Worker .context("failed to wait on child")?;
35*bb4ee6a4SAndroid Build Coastguard Worker if !output.status.success() {
36*bb4ee6a4SAndroid Build Coastguard Worker bail!(format!("{:?}: output status: {}", command, output.status));
37*bb4ee6a4SAndroid Build Coastguard Worker }
38*bb4ee6a4SAndroid Build Coastguard Worker Ok(String::from_utf8(output.stdout).context("Failed to convert command output to utf8")?)
39*bb4ee6a4SAndroid Build Coastguard Worker }
40*bb4ee6a4SAndroid Build Coastguard Worker
get_file(&self, filename: &str) -> Result<String>41*bb4ee6a4SAndroid Build Coastguard Worker fn get_file(&self, filename: &str) -> Result<String> {
42*bb4ee6a4SAndroid Build Coastguard Worker Ok(self.do_command(vec!["cat", filename])?)
43*bb4ee6a4SAndroid Build Coastguard Worker }
44*bb4ee6a4SAndroid Build Coastguard Worker
do_fincore(&self, filenames: &Vec<String>) -> Result<Vec<(u64, u64)>>45*bb4ee6a4SAndroid Build Coastguard Worker fn do_fincore(&self, filenames: &Vec<String>) -> Result<Vec<(u64, u64)>> {
46*bb4ee6a4SAndroid Build Coastguard Worker let mut command = vec!["fincore", "--raw", "--bytes"];
47*bb4ee6a4SAndroid Build Coastguard Worker command.extend(filenames.iter().map(|x| &**x));
48*bb4ee6a4SAndroid Build Coastguard Worker parse_fincore(&self.do_command(command)?)
49*bb4ee6a4SAndroid Build Coastguard Worker }
50*bb4ee6a4SAndroid Build Coastguard Worker }
51*bb4ee6a4SAndroid Build Coastguard Worker
parse_fincore(text: &str) -> Result<Vec<(u64, u64)>>52*bb4ee6a4SAndroid Build Coastguard Worker fn parse_fincore(text: &str) -> Result<Vec<(u64, u64)>> {
53*bb4ee6a4SAndroid Build Coastguard Worker let mut result = vec![];
54*bb4ee6a4SAndroid Build Coastguard Worker for line in text.lines().skip(1) {
55*bb4ee6a4SAndroid Build Coastguard Worker // res(bytes) pages size filename.
56*bb4ee6a4SAndroid Build Coastguard Worker let mut words = line.split(" ");
57*bb4ee6a4SAndroid Build Coastguard Worker let resident =
58*bb4ee6a4SAndroid Build Coastguard Worker str::parse::<u64>(words.next().context("res")?).context("number from fincore")?;
59*bb4ee6a4SAndroid Build Coastguard Worker let _pages = words.next().context("pages")?;
60*bb4ee6a4SAndroid Build Coastguard Worker let size =
61*bb4ee6a4SAndroid Build Coastguard Worker str::parse::<u64>(words.next().context("size")?).context("number from fincore")?;
62*bb4ee6a4SAndroid Build Coastguard Worker result.push((resident, size));
63*bb4ee6a4SAndroid Build Coastguard Worker }
64*bb4ee6a4SAndroid Build Coastguard Worker Ok(result)
65*bb4ee6a4SAndroid Build Coastguard Worker }
66*bb4ee6a4SAndroid Build Coastguard Worker
67*bb4ee6a4SAndroid Build Coastguard Worker // Extract only lines with a number as the second parameter, from /proc/pid/status
parse_status(text: &str) -> Result<std::collections::HashMap<&str, u32>>68*bb4ee6a4SAndroid Build Coastguard Worker fn parse_status(text: &str) -> Result<std::collections::HashMap<&str, u32>> {
69*bb4ee6a4SAndroid Build Coastguard Worker let key_value_iter = text
70*bb4ee6a4SAndroid Build Coastguard Worker .lines()
71*bb4ee6a4SAndroid Build Coastguard Worker .skip(1)
72*bb4ee6a4SAndroid Build Coastguard Worker .filter_map(|line| {
73*bb4ee6a4SAndroid Build Coastguard Worker let mut split = line.split_whitespace();
74*bb4ee6a4SAndroid Build Coastguard Worker let key = split.next().expect("key");
75*bb4ee6a4SAndroid Build Coastguard Worker if let Ok(value) = str::parse::<u32>(split.next().expect("number")) {
76*bb4ee6a4SAndroid Build Coastguard Worker Some((key, value))
77*bb4ee6a4SAndroid Build Coastguard Worker } else {
78*bb4ee6a4SAndroid Build Coastguard Worker None
79*bb4ee6a4SAndroid Build Coastguard Worker }
80*bb4ee6a4SAndroid Build Coastguard Worker })
81*bb4ee6a4SAndroid Build Coastguard Worker .collect::<Vec<_>>();
82*bb4ee6a4SAndroid Build Coastguard Worker let key_value_map: HashMap<_, _> = HashMap::from_iter(key_value_iter);
83*bb4ee6a4SAndroid Build Coastguard Worker Ok(key_value_map)
84*bb4ee6a4SAndroid Build Coastguard Worker }
85*bb4ee6a4SAndroid Build Coastguard Worker
parse_smaps(smaps_rollup_text: &str) -> std::collections::HashMap<&str, u32>86*bb4ee6a4SAndroid Build Coastguard Worker fn parse_smaps(smaps_rollup_text: &str) -> std::collections::HashMap<&str, u32> {
87*bb4ee6a4SAndroid Build Coastguard Worker let key_value_iter = smaps_rollup_text.lines().skip(1).map(|x| {
88*bb4ee6a4SAndroid Build Coastguard Worker let mut split = x.split_whitespace();
89*bb4ee6a4SAndroid Build Coastguard Worker let key = split.next().expect("key");
90*bb4ee6a4SAndroid Build Coastguard Worker let value = str::parse::<u32>(split.next().unwrap()).expect("kB") * 1024;
91*bb4ee6a4SAndroid Build Coastguard Worker let kb = split.next().unwrap();
92*bb4ee6a4SAndroid Build Coastguard Worker assert!(kb == "kB");
93*bb4ee6a4SAndroid Build Coastguard Worker (key, value)
94*bb4ee6a4SAndroid Build Coastguard Worker });
95*bb4ee6a4SAndroid Build Coastguard Worker let key_value_map: HashMap<_, _> = HashMap::from_iter(key_value_iter);
96*bb4ee6a4SAndroid Build Coastguard Worker key_value_map
97*bb4ee6a4SAndroid Build Coastguard Worker }
98*bb4ee6a4SAndroid Build Coastguard Worker
99*bb4ee6a4SAndroid Build Coastguard Worker struct BlockFds<'a> {
100*bb4ee6a4SAndroid Build Coastguard Worker // fd number.
101*bb4ee6a4SAndroid Build Coastguard Worker fd: u32,
102*bb4ee6a4SAndroid Build Coastguard Worker // The file path that the fd points to.
103*bb4ee6a4SAndroid Build Coastguard Worker path: &'a str,
104*bb4ee6a4SAndroid Build Coastguard Worker }
105*bb4ee6a4SAndroid Build Coastguard Worker
find_block_fds(proc_fd: &str) -> Vec<BlockFds>106*bb4ee6a4SAndroid Build Coastguard Worker fn find_block_fds(proc_fd: &str) -> Vec<BlockFds> {
107*bb4ee6a4SAndroid Build Coastguard Worker proc_fd
108*bb4ee6a4SAndroid Build Coastguard Worker .lines()
109*bb4ee6a4SAndroid Build Coastguard Worker .skip(1)
110*bb4ee6a4SAndroid Build Coastguard Worker .filter_map(|line| {
111*bb4ee6a4SAndroid Build Coastguard Worker let items: Vec<_> = line.split_whitespace().collect();
112*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(items[9], "->");
113*bb4ee6a4SAndroid Build Coastguard Worker let path = items[10];
114*bb4ee6a4SAndroid Build Coastguard Worker if path.contains("/memfd:")
115*bb4ee6a4SAndroid Build Coastguard Worker || path.contains("/dev/kvm")
116*bb4ee6a4SAndroid Build Coastguard Worker || path.contains("/dev/null")
117*bb4ee6a4SAndroid Build Coastguard Worker || path.contains("/dev/net/")
118*bb4ee6a4SAndroid Build Coastguard Worker || path.contains("/dev/dri/")
119*bb4ee6a4SAndroid Build Coastguard Worker || path.contains("/sys/fs")
120*bb4ee6a4SAndroid Build Coastguard Worker || path == "/"
121*bb4ee6a4SAndroid Build Coastguard Worker {
122*bb4ee6a4SAndroid Build Coastguard Worker None
123*bb4ee6a4SAndroid Build Coastguard Worker } else if path.contains("/") {
124*bb4ee6a4SAndroid Build Coastguard Worker Some(BlockFds {
125*bb4ee6a4SAndroid Build Coastguard Worker fd: items[8].parse::<u32>().unwrap(),
126*bb4ee6a4SAndroid Build Coastguard Worker path,
127*bb4ee6a4SAndroid Build Coastguard Worker })
128*bb4ee6a4SAndroid Build Coastguard Worker } else {
129*bb4ee6a4SAndroid Build Coastguard Worker None
130*bb4ee6a4SAndroid Build Coastguard Worker }
131*bb4ee6a4SAndroid Build Coastguard Worker })
132*bb4ee6a4SAndroid Build Coastguard Worker .collect()
133*bb4ee6a4SAndroid Build Coastguard Worker }
134*bb4ee6a4SAndroid Build Coastguard Worker
parse_fd_blocks(target: &Target, who: &str, pid: u32) -> Result<String>135*bb4ee6a4SAndroid Build Coastguard Worker fn parse_fd_blocks(target: &Target, who: &str, pid: u32) -> Result<String> {
136*bb4ee6a4SAndroid Build Coastguard Worker let lines = target
137*bb4ee6a4SAndroid Build Coastguard Worker .do_command(vec!["ls", "-l", &format!("/proc/{}/fd/", pid)])
138*bb4ee6a4SAndroid Build Coastguard Worker .context("ls -l for proc/fd")?;
139*bb4ee6a4SAndroid Build Coastguard Worker let block_fds = find_block_fds(&lines);
140*bb4ee6a4SAndroid Build Coastguard Worker let message = block_fds.par_iter().map(|block_fd| {
141*bb4ee6a4SAndroid Build Coastguard Worker let fdinfo = target.get_file(&format!("/proc/{}/fdinfo/{}", pid, block_fd.fd)).expect("/proc/fdinfo");
142*bb4ee6a4SAndroid Build Coastguard Worker let flags = u32::from_str_radix(parse_proc_fdinfo_flags(&fdinfo), 8).expect("octal");
143*bb4ee6a4SAndroid Build Coastguard Worker let fincore = target.do_fincore(&vec![block_fd.path.to_string()]).unwrap();
144*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(fincore.len(), 1);
145*bb4ee6a4SAndroid Build Coastguard Worker
146*bb4ee6a4SAndroid Build Coastguard Worker format!(
147*bb4ee6a4SAndroid Build Coastguard Worker "{} {} {} flags: {:o} o_direct on x86_64 {}, o_direct on arm {} page cache: {} MB / {} MB",
148*bb4ee6a4SAndroid Build Coastguard Worker who,
149*bb4ee6a4SAndroid Build Coastguard Worker block_fd.path,
150*bb4ee6a4SAndroid Build Coastguard Worker block_fd.fd,
151*bb4ee6a4SAndroid Build Coastguard Worker flags,
152*bb4ee6a4SAndroid Build Coastguard Worker (flags & 0o40000) != 0,
153*bb4ee6a4SAndroid Build Coastguard Worker (flags & 0o200000) != 0,
154*bb4ee6a4SAndroid Build Coastguard Worker fincore[0].0 >> 20,
155*bb4ee6a4SAndroid Build Coastguard Worker fincore[0].1 >> 20,
156*bb4ee6a4SAndroid Build Coastguard Worker )
157*bb4ee6a4SAndroid Build Coastguard Worker }).collect::<Vec<_>>().join("\n");
158*bb4ee6a4SAndroid Build Coastguard Worker Ok(message)
159*bb4ee6a4SAndroid Build Coastguard Worker }
160*bb4ee6a4SAndroid Build Coastguard Worker
parse_proc_fdinfo_flags(proc_fdinfo: &str) -> &str161*bb4ee6a4SAndroid Build Coastguard Worker fn parse_proc_fdinfo_flags(proc_fdinfo: &str) -> &str {
162*bb4ee6a4SAndroid Build Coastguard Worker let lines: HashMap<_, _> = proc_fdinfo
163*bb4ee6a4SAndroid Build Coastguard Worker .lines()
164*bb4ee6a4SAndroid Build Coastguard Worker .map(|line| {
165*bb4ee6a4SAndroid Build Coastguard Worker let mut words = line.split(":");
166*bb4ee6a4SAndroid Build Coastguard Worker (words.next().unwrap(), words.next().unwrap().trim())
167*bb4ee6a4SAndroid Build Coastguard Worker })
168*bb4ee6a4SAndroid Build Coastguard Worker .collect();
169*bb4ee6a4SAndroid Build Coastguard Worker lines["flags"]
170*bb4ee6a4SAndroid Build Coastguard Worker }
171*bb4ee6a4SAndroid Build Coastguard Worker
parse_virtio_fs(target: &Target, pid: u32) -> Result<String>172*bb4ee6a4SAndroid Build Coastguard Worker fn parse_virtio_fs(target: &Target, pid: u32) -> Result<String> {
173*bb4ee6a4SAndroid Build Coastguard Worker let lines = target.do_command(vec!["ls", "-1", &format!("/proc/{}/task/", pid)])?;
174*bb4ee6a4SAndroid Build Coastguard Worker let task_pids: Vec<_> = lines.lines().map(|x| x.parse::<u32>().unwrap()).collect();
175*bb4ee6a4SAndroid Build Coastguard Worker let pid_name_pairs: Vec<_> = task_pids
176*bb4ee6a4SAndroid Build Coastguard Worker .par_iter()
177*bb4ee6a4SAndroid Build Coastguard Worker .map(|task_pid| {
178*bb4ee6a4SAndroid Build Coastguard Worker let comm = target
179*bb4ee6a4SAndroid Build Coastguard Worker .get_file(&format!("/proc/{}/comm", task_pid))
180*bb4ee6a4SAndroid Build Coastguard Worker .unwrap();
181*bb4ee6a4SAndroid Build Coastguard Worker let comm = comm.trim();
182*bb4ee6a4SAndroid Build Coastguard Worker (task_pid, comm.to_string())
183*bb4ee6a4SAndroid Build Coastguard Worker })
184*bb4ee6a4SAndroid Build Coastguard Worker .collect();
185*bb4ee6a4SAndroid Build Coastguard Worker let message = pid_name_pairs
186*bb4ee6a4SAndroid Build Coastguard Worker .iter()
187*bb4ee6a4SAndroid Build Coastguard Worker .map(|(task_pid, comm)| format!("{}:{}", task_pid, comm))
188*bb4ee6a4SAndroid Build Coastguard Worker .collect::<Vec<_>>()
189*bb4ee6a4SAndroid Build Coastguard Worker .join(" ");
190*bb4ee6a4SAndroid Build Coastguard Worker Ok(message)
191*bb4ee6a4SAndroid Build Coastguard Worker }
192*bb4ee6a4SAndroid Build Coastguard Worker
main() -> Result<()>193*bb4ee6a4SAndroid Build Coastguard Worker fn main() -> Result<()> {
194*bb4ee6a4SAndroid Build Coastguard Worker // Use a little less than 10 threads globally. 10 sessions is the limit on
195*bb4ee6a4SAndroid Build Coastguard Worker // sshd connection by default as it is on the chromebook.
196*bb4ee6a4SAndroid Build Coastguard Worker rayon::ThreadPoolBuilder::new()
197*bb4ee6a4SAndroid Build Coastguard Worker .num_threads(8)
198*bb4ee6a4SAndroid Build Coastguard Worker .build_global()
199*bb4ee6a4SAndroid Build Coastguard Worker .unwrap();
200*bb4ee6a4SAndroid Build Coastguard Worker
201*bb4ee6a4SAndroid Build Coastguard Worker let host = env::args().nth(1).unwrap();
202*bb4ee6a4SAndroid Build Coastguard Worker let target = Target { host: host.clone() };
203*bb4ee6a4SAndroid Build Coastguard Worker while target.do_command(vec!["uname", "-a"]).is_err() {
204*bb4ee6a4SAndroid Build Coastguard Worker println!("Retrying {}", host);
205*bb4ee6a4SAndroid Build Coastguard Worker thread::sleep(Duration::from_millis(1000));
206*bb4ee6a4SAndroid Build Coastguard Worker }
207*bb4ee6a4SAndroid Build Coastguard Worker let crosvm_pid =
208*bb4ee6a4SAndroid Build Coastguard Worker str::parse::<u32>(&target.do_command(vec!["pgrep", "crosvm"]).unwrap().trim()).unwrap();
209*bb4ee6a4SAndroid Build Coastguard Worker let crosvm_cmdline = target.get_file(&format!("/proc/{}/cmdline", crosvm_pid))?;
210*bb4ee6a4SAndroid Build Coastguard Worker let commandline_flags: Vec<_> = crosvm_cmdline.split("\0").collect();
211*bb4ee6a4SAndroid Build Coastguard Worker
212*bb4ee6a4SAndroid Build Coastguard Worker let mut shared_dir_params = vec![];
213*bb4ee6a4SAndroid Build Coastguard Worker let mut disk_params = vec![];
214*bb4ee6a4SAndroid Build Coastguard Worker let mut socket = "";
215*bb4ee6a4SAndroid Build Coastguard Worker for (i, line) in commandline_flags.iter().enumerate() {
216*bb4ee6a4SAndroid Build Coastguard Worker match *line {
217*bb4ee6a4SAndroid Build Coastguard Worker "--shared-dir" => shared_dir_params.push(commandline_flags[i + 1]),
218*bb4ee6a4SAndroid Build Coastguard Worker "--block" => disk_params.push(commandline_flags[i + 1]),
219*bb4ee6a4SAndroid Build Coastguard Worker "--socket" => socket = commandline_flags[i + 1],
220*bb4ee6a4SAndroid Build Coastguard Worker _ => {
221*bb4ee6a4SAndroid Build Coastguard Worker // Skip other flags.
222*bb4ee6a4SAndroid Build Coastguard Worker }
223*bb4ee6a4SAndroid Build Coastguard Worker }
224*bb4ee6a4SAndroid Build Coastguard Worker }
225*bb4ee6a4SAndroid Build Coastguard Worker println!("{:?}", shared_dir_params);
226*bb4ee6a4SAndroid Build Coastguard Worker println!("{:?}", disk_params);
227*bb4ee6a4SAndroid Build Coastguard Worker
228*bb4ee6a4SAndroid Build Coastguard Worker // Parsed command line for paths to virtio disk blocks. Concierge gives links to /proc/self/fd,
229*bb4ee6a4SAndroid Build Coastguard Worker // translate them to actual end paths after resolving symlinks.
230*bb4ee6a4SAndroid Build Coastguard Worker let disk_blocks: Vec<_> = disk_params
231*bb4ee6a4SAndroid Build Coastguard Worker .par_iter()
232*bb4ee6a4SAndroid Build Coastguard Worker .map(|disk| {
233*bb4ee6a4SAndroid Build Coastguard Worker let block_path_in_proc = disk.split(",").nth(0).unwrap();
234*bb4ee6a4SAndroid Build Coastguard Worker // this would be like /proc/self/fd/26
235*bb4ee6a4SAndroid Build Coastguard Worker assert!(block_path_in_proc.starts_with("/proc/self/fd/"));
236*bb4ee6a4SAndroid Build Coastguard Worker let fd_id =
237*bb4ee6a4SAndroid Build Coastguard Worker str::parse::<u32>(block_path_in_proc.split("/").nth(4).unwrap().trim()).unwrap();
238*bb4ee6a4SAndroid Build Coastguard Worker let disk_block = target
239*bb4ee6a4SAndroid Build Coastguard Worker .do_command(vec![
240*bb4ee6a4SAndroid Build Coastguard Worker "readlink",
241*bb4ee6a4SAndroid Build Coastguard Worker &format!("/proc/{}/fd/{}", crosvm_pid, fd_id),
242*bb4ee6a4SAndroid Build Coastguard Worker ])
243*bb4ee6a4SAndroid Build Coastguard Worker .unwrap();
244*bb4ee6a4SAndroid Build Coastguard Worker disk_block.trim().to_string()
245*bb4ee6a4SAndroid Build Coastguard Worker })
246*bb4ee6a4SAndroid Build Coastguard Worker .collect();
247*bb4ee6a4SAndroid Build Coastguard Worker println!("{:?}", disk_blocks);
248*bb4ee6a4SAndroid Build Coastguard Worker
249*bb4ee6a4SAndroid Build Coastguard Worker // Get fincore stats.
250*bb4ee6a4SAndroid Build Coastguard Worker for (i, (res, size)) in target.do_fincore(&disk_blocks).unwrap().iter().enumerate() {
251*bb4ee6a4SAndroid Build Coastguard Worker println!(
252*bb4ee6a4SAndroid Build Coastguard Worker "fincore {}: {} MB / {} MB",
253*bb4ee6a4SAndroid Build Coastguard Worker disk_blocks[i],
254*bb4ee6a4SAndroid Build Coastguard Worker res >> 20,
255*bb4ee6a4SAndroid Build Coastguard Worker size >> 20
256*bb4ee6a4SAndroid Build Coastguard Worker );
257*bb4ee6a4SAndroid Build Coastguard Worker }
258*bb4ee6a4SAndroid Build Coastguard Worker
259*bb4ee6a4SAndroid Build Coastguard Worker // Look at fds of crosvm map
260*bb4ee6a4SAndroid Build Coastguard Worker parse_fd_blocks(&target, "crosvm", crosvm_pid)?;
261*bb4ee6a4SAndroid Build Coastguard Worker
262*bb4ee6a4SAndroid Build Coastguard Worker // find children of the process
263*bb4ee6a4SAndroid Build Coastguard Worker let crosvm_child_pids: Vec<_> = target
264*bb4ee6a4SAndroid Build Coastguard Worker .get_file(&format!(
265*bb4ee6a4SAndroid Build Coastguard Worker "/proc/{}/task/{}/children",
266*bb4ee6a4SAndroid Build Coastguard Worker crosvm_pid, crosvm_pid
267*bb4ee6a4SAndroid Build Coastguard Worker ))?
268*bb4ee6a4SAndroid Build Coastguard Worker .trim()
269*bb4ee6a4SAndroid Build Coastguard Worker .split(" ")
270*bb4ee6a4SAndroid Build Coastguard Worker .map(|x| str::parse::<u32>(x).expect("pid"))
271*bb4ee6a4SAndroid Build Coastguard Worker .collect();
272*bb4ee6a4SAndroid Build Coastguard Worker
273*bb4ee6a4SAndroid Build Coastguard Worker // Scanning for crosvm child processes
274*bb4ee6a4SAndroid Build Coastguard Worker crosvm_child_pids.par_iter().for_each(|child_pid| {
275*bb4ee6a4SAndroid Build Coastguard Worker let task_name = target
276*bb4ee6a4SAndroid Build Coastguard Worker .get_file(&format!("/proc/{}/comm", child_pid))
277*bb4ee6a4SAndroid Build Coastguard Worker .unwrap()
278*bb4ee6a4SAndroid Build Coastguard Worker .trim()
279*bb4ee6a4SAndroid Build Coastguard Worker .to_string();
280*bb4ee6a4SAndroid Build Coastguard Worker // task/*/comm contains thread names which are useful to tell the device
281*bb4ee6a4SAndroid Build Coastguard Worker // type. smaps_rollup would be useful, smaps too.
282*bb4ee6a4SAndroid Build Coastguard Worker // values are in kB.
283*bb4ee6a4SAndroid Build Coastguard Worker let smaps_rollup = target
284*bb4ee6a4SAndroid Build Coastguard Worker .get_file(&format!("/proc/{}/smaps_rollup", child_pid))
285*bb4ee6a4SAndroid Build Coastguard Worker .unwrap();
286*bb4ee6a4SAndroid Build Coastguard Worker let parsed_smaps = parse_smaps(&smaps_rollup);
287*bb4ee6a4SAndroid Build Coastguard Worker let dirty = parsed_smaps["Private_Dirty:"];
288*bb4ee6a4SAndroid Build Coastguard Worker let rss = parsed_smaps["Rss:"];
289*bb4ee6a4SAndroid Build Coastguard Worker let status_text = target
290*bb4ee6a4SAndroid Build Coastguard Worker .get_file(&format!("/proc/{}/status", child_pid))
291*bb4ee6a4SAndroid Build Coastguard Worker .unwrap();
292*bb4ee6a4SAndroid Build Coastguard Worker let vmpte_kb = parse_status(&status_text).unwrap()["VmPTE:"];
293*bb4ee6a4SAndroid Build Coastguard Worker let message = match task_name.as_str() {
294*bb4ee6a4SAndroid Build Coastguard Worker "pcivirtio-block" => parse_fd_blocks(&target, "virtio-block", *child_pid),
295*bb4ee6a4SAndroid Build Coastguard Worker "pcivirtio-fs" => parse_virtio_fs(&target, *child_pid),
296*bb4ee6a4SAndroid Build Coastguard Worker _ => Ok("".to_string()),
297*bb4ee6a4SAndroid Build Coastguard Worker }
298*bb4ee6a4SAndroid Build Coastguard Worker .unwrap();
299*bb4ee6a4SAndroid Build Coastguard Worker
300*bb4ee6a4SAndroid Build Coastguard Worker // output in MBs.
301*bb4ee6a4SAndroid Build Coastguard Worker println!(
302*bb4ee6a4SAndroid Build Coastguard Worker "{} {} private_dirty: {} MB rss: {} MB VmPTE: {} KiB\n {}",
303*bb4ee6a4SAndroid Build Coastguard Worker task_name,
304*bb4ee6a4SAndroid Build Coastguard Worker child_pid,
305*bb4ee6a4SAndroid Build Coastguard Worker dirty >> 20,
306*bb4ee6a4SAndroid Build Coastguard Worker rss >> 20,
307*bb4ee6a4SAndroid Build Coastguard Worker vmpte_kb,
308*bb4ee6a4SAndroid Build Coastguard Worker message
309*bb4ee6a4SAndroid Build Coastguard Worker );
310*bb4ee6a4SAndroid Build Coastguard Worker });
311*bb4ee6a4SAndroid Build Coastguard Worker
312*bb4ee6a4SAndroid Build Coastguard Worker let balloon_stat_json = target.do_command(vec!["crosvm", "balloon_stats", socket])?;
313*bb4ee6a4SAndroid Build Coastguard Worker println!("{}", balloon_stat_json);
314*bb4ee6a4SAndroid Build Coastguard Worker
315*bb4ee6a4SAndroid Build Coastguard Worker Ok(())
316*bb4ee6a4SAndroid Build Coastguard Worker }
317*bb4ee6a4SAndroid Build Coastguard Worker
318*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(test)]
319*bb4ee6a4SAndroid Build Coastguard Worker mod tests {
320*bb4ee6a4SAndroid Build Coastguard Worker use super::*;
321*bb4ee6a4SAndroid Build Coastguard Worker
322*bb4ee6a4SAndroid Build Coastguard Worker #[test]
parse_smaps_basic()323*bb4ee6a4SAndroid Build Coastguard Worker fn parse_smaps_basic() {
324*bb4ee6a4SAndroid Build Coastguard Worker let smaps_rollup_text =
325*bb4ee6a4SAndroid Build Coastguard Worker "55c9706ac000-7fff1d38e000 ---p 00000000 00:00 0 [rollup]
326*bb4ee6a4SAndroid Build Coastguard Worker Rss: 5940 kB
327*bb4ee6a4SAndroid Build Coastguard Worker Pss: 580 kB
328*bb4ee6a4SAndroid Build Coastguard Worker Pss_Anon: 367 kB
329*bb4ee6a4SAndroid Build Coastguard Worker Pss_File: 213 kB
330*bb4ee6a4SAndroid Build Coastguard Worker Pss_Shmem: 0 kB
331*bb4ee6a4SAndroid Build Coastguard Worker Shared_Clean: 3760 kB
332*bb4ee6a4SAndroid Build Coastguard Worker Shared_Dirty: 1816 kB
333*bb4ee6a4SAndroid Build Coastguard Worker Private_Clean: 64 kB
334*bb4ee6a4SAndroid Build Coastguard Worker Private_Dirty: 300 kB
335*bb4ee6a4SAndroid Build Coastguard Worker Referenced: 4244 kB
336*bb4ee6a4SAndroid Build Coastguard Worker Anonymous: 2116 kB
337*bb4ee6a4SAndroid Build Coastguard Worker LazyFree: 0 kB
338*bb4ee6a4SAndroid Build Coastguard Worker AnonHugePages: 0 kB
339*bb4ee6a4SAndroid Build Coastguard Worker ShmemPmdMapped: 0 kB
340*bb4ee6a4SAndroid Build Coastguard Worker FilePmdMapped: 0 kB
341*bb4ee6a4SAndroid Build Coastguard Worker Shared_Hugetlb: 0 kB
342*bb4ee6a4SAndroid Build Coastguard Worker Private_Hugetlb: 0 kB
343*bb4ee6a4SAndroid Build Coastguard Worker Swap: 0 kB
344*bb4ee6a4SAndroid Build Coastguard Worker SwapPss: 0 kB
345*bb4ee6a4SAndroid Build Coastguard Worker Locked: 0 kB
346*bb4ee6a4SAndroid Build Coastguard Worker ";
347*bb4ee6a4SAndroid Build Coastguard Worker let key_value_map = parse_smaps(smaps_rollup_text);
348*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(key_value_map["Private_Dirty:"], 300 * 1024);
349*bb4ee6a4SAndroid Build Coastguard Worker }
350*bb4ee6a4SAndroid Build Coastguard Worker
351*bb4ee6a4SAndroid Build Coastguard Worker #[test]
parse_status_basic() -> Result<()>352*bb4ee6a4SAndroid Build Coastguard Worker fn parse_status_basic() -> Result<()> {
353*bb4ee6a4SAndroid Build Coastguard Worker let status_text = "Name: pcivirtio-block
354*bb4ee6a4SAndroid Build Coastguard Worker Umask: 0002
355*bb4ee6a4SAndroid Build Coastguard Worker State: S (sleeping)
356*bb4ee6a4SAndroid Build Coastguard Worker Tgid: 22698
357*bb4ee6a4SAndroid Build Coastguard Worker Ngid: 0
358*bb4ee6a4SAndroid Build Coastguard Worker Pid: 22698
359*bb4ee6a4SAndroid Build Coastguard Worker PPid: 22560
360*bb4ee6a4SAndroid Build Coastguard Worker TracerPid: 0
361*bb4ee6a4SAndroid Build Coastguard Worker Uid: 299 299 299 299
362*bb4ee6a4SAndroid Build Coastguard Worker Gid: 299 299 299 299
363*bb4ee6a4SAndroid Build Coastguard Worker FDSize: 512
364*bb4ee6a4SAndroid Build Coastguard Worker Groups: 27 299 333 400 413 418 600 601 603 20128 20136 20162
365*bb4ee6a4SAndroid Build Coastguard Worker NStgid: 22698 39 1
366*bb4ee6a4SAndroid Build Coastguard Worker NSpid: 22698 39 1
367*bb4ee6a4SAndroid Build Coastguard Worker NSpgid: 22560 12 0
368*bb4ee6a4SAndroid Build Coastguard Worker NSsid: 22142 0 0
369*bb4ee6a4SAndroid Build Coastguard Worker VmPeak: 15278960 kB
370*bb4ee6a4SAndroid Build Coastguard Worker VmSize: 15213424 kB
371*bb4ee6a4SAndroid Build Coastguard Worker VmLck: 0 kB
372*bb4ee6a4SAndroid Build Coastguard Worker VmPin: 0 kB
373*bb4ee6a4SAndroid Build Coastguard Worker VmHWM: 176796 kB
374*bb4ee6a4SAndroid Build Coastguard Worker VmRSS: 176796 kB
375*bb4ee6a4SAndroid Build Coastguard Worker RssAnon: 2320 kB
376*bb4ee6a4SAndroid Build Coastguard Worker RssFile: 6540 kB
377*bb4ee6a4SAndroid Build Coastguard Worker RssShmem: 167936 kB
378*bb4ee6a4SAndroid Build Coastguard Worker VmData: 3332 kB
379*bb4ee6a4SAndroid Build Coastguard Worker VmStk: 136 kB
380*bb4ee6a4SAndroid Build Coastguard Worker VmExe: 10628 kB
381*bb4ee6a4SAndroid Build Coastguard Worker VmLib: 15436 kB
382*bb4ee6a4SAndroid Build Coastguard Worker VmPTE: 132 kB
383*bb4ee6a4SAndroid Build Coastguard Worker VmSwap: 0 kB
384*bb4ee6a4SAndroid Build Coastguard Worker CoreDumping: 0
385*bb4ee6a4SAndroid Build Coastguard Worker THP_enabled: 1
386*bb4ee6a4SAndroid Build Coastguard Worker Threads: 2
387*bb4ee6a4SAndroid Build Coastguard Worker SigQ: 0/63125
388*bb4ee6a4SAndroid Build Coastguard Worker SigPnd: 0000000000000000
389*bb4ee6a4SAndroid Build Coastguard Worker ShdPnd: 0000000000000000
390*bb4ee6a4SAndroid Build Coastguard Worker SigBlk: 0000000000010000
391*bb4ee6a4SAndroid Build Coastguard Worker SigIgn: 0000000000001000
392*bb4ee6a4SAndroid Build Coastguard Worker SigCgt: 0000000100000440
393*bb4ee6a4SAndroid Build Coastguard Worker CapInh: 0000000000000000
394*bb4ee6a4SAndroid Build Coastguard Worker CapPrm: 0000000000000000
395*bb4ee6a4SAndroid Build Coastguard Worker CapEff: 0000000000000000
396*bb4ee6a4SAndroid Build Coastguard Worker CapBnd: 0000000000000000
397*bb4ee6a4SAndroid Build Coastguard Worker CapAmb: 0000000000000000
398*bb4ee6a4SAndroid Build Coastguard Worker NoNewPrivs: 1
399*bb4ee6a4SAndroid Build Coastguard Worker Seccomp: 2
400*bb4ee6a4SAndroid Build Coastguard Worker Seccomp_filters: 3
401*bb4ee6a4SAndroid Build Coastguard Worker Speculation_Store_Bypass: thread force mitigated
402*bb4ee6a4SAndroid Build Coastguard Worker SpeculationIndirectBranch: conditional force disabled
403*bb4ee6a4SAndroid Build Coastguard Worker Cpus_allowed: fff
404*bb4ee6a4SAndroid Build Coastguard Worker Cpus_allowed_list: 0-11
405*bb4ee6a4SAndroid Build Coastguard Worker Mems_allowed: 1
406*bb4ee6a4SAndroid Build Coastguard Worker Mems_allowed_list: 0
407*bb4ee6a4SAndroid Build Coastguard Worker voluntary_ctxt_switches: 533
408*bb4ee6a4SAndroid Build Coastguard Worker nonvoluntary_ctxt_switches: 3
409*bb4ee6a4SAndroid Build Coastguard Worker ";
410*bb4ee6a4SAndroid Build Coastguard Worker let status = parse_status(status_text)?;
411*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(status["VmPTE:"], 132);
412*bb4ee6a4SAndroid Build Coastguard Worker Ok(())
413*bb4ee6a4SAndroid Build Coastguard Worker }
414*bb4ee6a4SAndroid Build Coastguard Worker
415*bb4ee6a4SAndroid Build Coastguard Worker #[test]
fincore_test() -> Result<()>416*bb4ee6a4SAndroid Build Coastguard Worker fn fincore_test() -> Result<()> {
417*bb4ee6a4SAndroid Build Coastguard Worker let fincore_output = "RES PAGES SIZE FILE\n474968064 115959 667250688 /opt/google/vms/android/system.raw.img\n10452992 2552 140140544 /opt/google/vms/android/vendor.raw.img\n0 0 0 /dev/null\n0 0 0 /dev/null\n215543808 52623 11468791808 /run/daemon-store/crosvm/8b3488f8d78a9827054a417ae7a4b9bb62586267/YXJjdm0=.img\n";
418*bb4ee6a4SAndroid Build Coastguard Worker parse_fincore(fincore_output)?;
419*bb4ee6a4SAndroid Build Coastguard Worker Ok(())
420*bb4ee6a4SAndroid Build Coastguard Worker }
421*bb4ee6a4SAndroid Build Coastguard Worker
422*bb4ee6a4SAndroid Build Coastguard Worker #[test]
proc_fd_test()423*bb4ee6a4SAndroid Build Coastguard Worker fn proc_fd_test() {
424*bb4ee6a4SAndroid Build Coastguard Worker let proc_fd = "total 0
425*bb4ee6a4SAndroid Build Coastguard Worker lrwx------. 1 crosvm crosvm 64 Jul 19 09:20 54 -> 'anon_inode:[eventfd]'
426*bb4ee6a4SAndroid Build Coastguard Worker lrwx------. 1 crosvm crosvm 64 Jul 19 09:20 55 -> 'anon_inode:[eventfd]'
427*bb4ee6a4SAndroid Build Coastguard Worker lrwx------. 1 crosvm crosvm 64 Jul 19 09:20 56 -> 'anon_inode:[eventfd]'
428*bb4ee6a4SAndroid Build Coastguard Worker lrwx------. 1 crosvm crosvm 64 Jul 19 09:20 6 -> 'anon_inode:[eventfd]'
429*bb4ee6a4SAndroid Build Coastguard Worker lrwx------. 1 crosvm crosvm 64 Jul 19 09:20 7 -> '/memfd:crosvm_guest (deleted)'
430*bb4ee6a4SAndroid Build Coastguard Worker lrwx------. 1 crosvm crosvm 64 Jul 19 09:20 8 -> 'anon_inode:[eventfd]'
431*bb4ee6a4SAndroid Build Coastguard Worker lr-x------. 1 crosvm crosvm 64 Jul 9 09:20 85 -> /opt/google/vms/android/system.raw.img
432*bb4ee6a4SAndroid Build Coastguard Worker lrwx------. 1 crosvm crosvm 64 Jul 19 09:20 9 -> 'anon_inode:[eventfd]'
433*bb4ee6a4SAndroid Build Coastguard Worker ";
434*bb4ee6a4SAndroid Build Coastguard Worker
435*bb4ee6a4SAndroid Build Coastguard Worker let v = find_block_fds(proc_fd);
436*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(v.len(), 1);
437*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(v[0].fd, 85);
438*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(v[0].path, "/opt/google/vms/android/system.raw.img");
439*bb4ee6a4SAndroid Build Coastguard Worker }
440*bb4ee6a4SAndroid Build Coastguard Worker
441*bb4ee6a4SAndroid Build Coastguard Worker #[test]
test_fdinfo()442*bb4ee6a4SAndroid Build Coastguard Worker fn test_fdinfo() {
443*bb4ee6a4SAndroid Build Coastguard Worker let proc_fdinfo = "pos: 0
444*bb4ee6a4SAndroid Build Coastguard Worker flags: 0100002
445*bb4ee6a4SAndroid Build Coastguard Worker mnt_id: 25
446*bb4ee6a4SAndroid Build Coastguard Worker ino: 18
447*bb4ee6a4SAndroid Build Coastguard Worker ";
448*bb4ee6a4SAndroid Build Coastguard Worker
449*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(parse_proc_fdinfo_flags(proc_fdinfo), "0100002");
450*bb4ee6a4SAndroid Build Coastguard Worker }
451*bb4ee6a4SAndroid Build Coastguard Worker }
452