• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
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 
16 use std::env;
17 extern crate getopts;
18 use getopts::Options;
19 
20 use std::collections::HashMap;
21 
22 use regex::RegexBuilder;
23 
24 use prettytable::{Table, Row, Cell, format};
25 use prettytable::format::*;
26 
27 struct VmStruct {
28     name:   String,
29     start:  u64,
30     end:    u64,
31     off:    u64,
32     perm:   String,
33     dev:    String,
34     inode:  u64,
35     counts: usize,
36     value:  HashMap<String, u64>
37 }
38 impl VmStruct {
add(&mut self, key: &String, val: u64)39     fn add(&mut self, key: &String, val: u64) {
40         let v = self.value.entry(key.clone()).or_insert(0);
41         *v += val;
42     }
incress_counts(&mut self)43     fn incress_counts(&mut self) {
44         self.counts += 1;
45     }
46 }
47 
main()48 fn main() {
49     let args: Vec<String> = env::args().collect();
50     let program = args[0].clone();
51 
52     let mut opts = Options::new();
53     opts.optopt("p", "pid", "parse target pid smaps", "PID");
54     opts.optopt("f", "file", "print target file", "FILE");
55     opts.optflag("v", "verbose", "verbose mode, not combine");
56     opts.optflag("h", "help", "print this help menu");
57     let matches = match opts.parse(&args[1..]) {
58         Ok(m) => { m }
59         Err(_f) => { return print_usage(&program, opts) }
60     };
61     if matches.opt_present("h") {
62         print_usage(&program, opts);
63         return;
64     }
65     if matches.opt_present("p") && matches.opt_present("f") {
66         print_usage(&program, opts);
67         return;
68     }
69     let need_combine = if matches.opt_present("v") { false } else { true };
70 
71     let pid = match matches.opt_str("p") {
72         Some(s) => s.parse().unwrap_or(-1),
73         None => -1,
74     };
75     let mut file_path = match matches.opt_str("f") {
76         Some(s) => s,
77         None => String::from(""),
78     };
79 
80     if pid == -1 && &file_path == "" {
81         print_usage(&program, opts);
82         return;
83     }
84 
85     if pid != -1 {
86         file_path = format!("/proc/{:?}/smaps", pid);
87     }
88 
89     let smaps_info = read_smaps(&file_path, need_combine);
90 
91     if need_combine {
92         print_smaps_combined(smaps_info);
93     } else {
94         print_smaps_verbose(smaps_info);
95     }
96 
97     std::process::exit(0);
98 }
99 
print_smaps_combined(infos: Vec<VmStruct>)100 fn print_smaps_combined(infos: Vec<VmStruct>) {
101     let value_keys = vec![
102         ("Size",          "d"),
103         ("Rss",           "d"),
104         ("Pss",           "d"),
105         ("Shared\nClean", "d"),
106         ("Shared\nDirty", "d"),
107         ("Private\nClean","d"),
108         ("Private\nDirty","d"),
109         ("Swap",          "d"),
110         ("SwapPss",       "d")
111     ];
112     let info_keys = vec![
113         ("Counts",        ""),
114         ("Name",          ""),
115     ];
116     print_smaps_core(infos, &value_keys, &info_keys);
117 }
118 
print_smaps_verbose(infos: Vec<VmStruct>)119 fn print_smaps_verbose(infos: Vec<VmStruct>) {
120     let value_keys = vec![
121         ("Size",          "d"),
122         ("Rss",           "d"),
123         ("Pss",           "d"),
124         ("Shared\nClean","d"),
125         ("Shared\nDirty", "d"),
126         ("Private\nClean","d"),
127         ("Private\nDirty","d"),
128         ("Swap",          "d"),
129         ("SwapPss",       "d")
130     ];
131     let info_keys = vec![
132         ("Start",         ""),
133         ("End",           ""),
134         ("Name",          ""),
135     ];
136     print_smaps_core(infos, &value_keys, &info_keys);
137 }
138 
print_smaps_core(infos: Vec<VmStruct>, value_keys: &Vec<(&str, &str)>, info_keys: &Vec<(&str, &str)>)139 fn print_smaps_core(infos: Vec<VmStruct>, value_keys: &Vec<(&str, &str)>, info_keys: &Vec<(&str, &str)>) {
140     let mut summary = VmStruct {
141         name:   String::from("Summary"),
142         start:  0,
143         end:    0,
144         off:    0,
145         perm:   String::from(""),
146         dev:    String::from(""),
147         inode:  0,
148         counts: 0,
149         value:  HashMap::from([])
150     };
151 
152     let mut table = Table::new();
153     table.set_format(*format::consts::FORMAT_NO_LINESEP_WITH_TITLE);
154 
155     table.set_titles(Row::new(value_keys.into_iter().chain(info_keys.into_iter()).map(|&x| Cell::new(x.0)).collect()));
156     for i in infos {
157         // make a row
158         let mut r = Row::new(value_keys.into_iter().map(|&x| value_to_cell(&i, x.0, x.1)).collect());
159         for ik in info_keys {
160             r.add_cell(info_to_cell(&i, ik.0, false));
161         }
162         table.add_row(r);
163 
164         // calculate summary
165         for (n, v) in i.value {
166             summary.add(&n, v);
167         }
168         summary.counts += i.counts;
169     }
170 
171     table.add_empty_row();
172 
173     // add summary row
174     let mut rsum = Row::new(value_keys.into_iter().map(|&x| value_to_cell(&summary, x.0, x.1)).collect());
175     for ik in info_keys {
176         rsum.add_cell(info_to_cell(&summary, ik.0, true));
177     }
178     table.add_row(rsum);
179 
180     // Print the table to stdout
181     table.printstd();
182 }
183 
184 
value_to_cell(i: &VmStruct, k: &str, t: &str) -> Cell185 fn value_to_cell(i: &VmStruct, k: &str, t: &str) -> Cell {
186     if i.value.contains_key(k) {
187         match t {
188             "x" => Cell::new_align(format!("{:x}", i.value.get(k).unwrap()).as_str(), Alignment::RIGHT),
189             "d" => Cell::new_align(format!("{}", i.value.get(k).unwrap()).as_str(), Alignment::RIGHT),
190             "s" => Cell::new(format!("{}", i.value.get(k).unwrap()).as_str()),
191             _ => Cell::new(""),
192         }
193     } else {
194         Cell::new("")
195     }
196 }
197 
info_to_cell(i: &VmStruct, k: &str, is_summary: bool) -> Cell198 fn info_to_cell(i: &VmStruct, k: &str, is_summary: bool) -> Cell {
199     if is_summary {
200         match k {
201             "Counts" => Cell::new_align(format!("{}", i.counts).as_str(), Alignment::RIGHT),
202             "Name" => Cell::new(format!("{}", i.name).as_str()),
203             _ => Cell::new("")
204         }
205     } else {
206         match k {
207             "Name" => Cell::new(format!("{}", i.name).as_str()),
208             "Start" => Cell::new(format!("{:x}", i.start).as_str()),
209             "End" => Cell::new(format!("{:x}", i.end).as_str()),
210             "Off" => Cell::new(format!("{:x}", i.off).as_str()),
211             "Perm" => Cell::new(format!("{}", i.perm).as_str()),
212             "Dev" => Cell::new(format!("{}", i.dev).as_str()),
213             "Inode" => Cell::new(format!("{}", i.inode).as_str()),
214             "Counts" => Cell::new_align(format!("{}", i.counts).as_str(), Alignment::RIGHT),
215             _ => Cell::new("")
216         }
217     }
218 }
219 
read_smaps(file_path: &String, need_combine: bool) -> Vec<VmStruct>220 fn read_smaps(file_path: &String, need_combine: bool) -> Vec<VmStruct> {
221     let mut output : Vec<VmStruct> = Vec::new();
222 
223     // 55de5fa6e000-55de5fad3000 r--p 0011e000 103:03 5908202                   /usr/lib/systemd/systemd
224     let regex_head = RegexBuilder::new("^(?P<start>[0-9a-f]+)-(?P<end>[0-9a-f]+)[ \t]+(?P<perm>[^ ]+)[ \t]+(?P<off>[0-9a-f]+)[ \t]+(?P<dev>[0-9a-f:]+)[ \t]+(?P<inode>[0-9a-z]+)[ \t]*(?P<name>.*)")
225         .multi_line(true)
226         .build()
227         .unwrap();
228     let regex_val = RegexBuilder::new("^(?P<key>[a-zA-Z_]+):[ \t]+(?P<val>[0-9]+)[ \t]+kB")
229         .multi_line(true)
230         .build()
231         .unwrap();
232 
233     let file_context = std::fs::read_to_string(file_path)
234         .unwrap();
235 
236     let vms: Vec<_> = regex_head
237         .find_iter(&file_context)
238         .map(|matched| matched.start())
239         .chain(std::iter::once(file_context.len()))
240         .collect();
241 
242     let mut vm_map = HashMap::new();
243     for vm in vms.windows(2) {
244         let caps = regex_head.captures(&file_context[vm[0]..vm[1]]).unwrap();
245         let start = u64::from_str_radix(caps.name("start").unwrap().as_str(), 16).unwrap();
246         let end = u64::from_str_radix(caps.name("end").unwrap().as_str(), 16).unwrap();
247         let off = u64::from_str_radix(caps.name("off").unwrap().as_str(), 16).unwrap();
248         let perm = caps.name("perm").unwrap().as_str();
249         let dev = caps.name("dev").unwrap().as_str();
250         let inode = u64::from_str_radix(caps.name("inode").unwrap().as_str(), 10).unwrap();
251 
252         let mut name = String::from("[anon]");
253         if caps.name("name").unwrap().as_str() != "" {
254             name = caps.name("name").unwrap().as_str().to_string();
255         }
256 
257         let value_map: HashMap<String, u64> = regex_val
258             .captures_iter(&file_context[vm[0]..vm[1]])
259             .map(|cap| {
260                 let key = cap.get(1).unwrap().as_str().to_owned();
261                 let value = cap.get(2).unwrap().as_str().parse().unwrap();
262                 (key.clone().replace("_", "\n"), value)
263             })
264         .collect();
265 
266         if need_combine && vm_map.contains_key(&name) {
267             let &idx: &usize = vm_map.get(&name).unwrap();
268             for (n, v) in value_map {
269                 output.get_mut(idx).unwrap().add(&n, v);
270             }
271             output.get_mut(idx).unwrap().incress_counts();
272         } else {
273             let mut vms = VmStruct{
274                     name: name.clone(),
275                     start: start,
276                     end: end,
277                     off: off,
278                     perm: perm.to_string(),
279                     dev: dev.to_string(),
280                     inode: inode,
281                     counts: 1,
282                     value: HashMap::from([])};
283 
284             for (n, v) in value_map {
285                 vms.add(&n, v);
286             }
287             output.push(vms);
288             if need_combine {
289                 vm_map.insert(name.clone(), output.len() - 1);
290             }
291         }
292     }
293     output
294 }
295 
print_usage(program: &str, opts: Options)296 fn print_usage(program: &str, opts: Options) {
297     let brief = format!("Usage: {} [options] PID/FILE ", program);
298     print!("{}", opts.usage(&brief));
299 }
300