xref: /aosp_15_r20/external/crosvm/tools/contrib/crosvmdump/src/main.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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