Skip to content

Commit f635788

Browse files
estodicakebaker
andauthored
pmap: implement --extended (#368)
* add inode in MapLine inode information is used by extended options * pmap: implement extended option * formatted files * updated test_pmap.rs * fixed lint errors * fixed missing fields and typo * fixed parse_smap_entries algorithm removed a redundant field * pmap: fixed `parse_smap_entries` algorithm to improve its endurance against platform variations * pmap: fixed typo * Update src/uu/pmap/src/smaps_format_parser.rs Incorporate the suggested change Co-authored-by: Daniel Hofstetter <[email protected]> --------- Co-authored-by: Daniel Hofstetter <[email protected]>
1 parent 888553f commit f635788

File tree

4 files changed

+680
-14
lines changed

4 files changed

+680
-14
lines changed

src/uu/pmap/src/maps_format_parser.rs

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,20 @@
66
use std::fmt;
77
use std::io::{Error, ErrorKind};
88

9-
// Represents a parsed single line from /proc/<PID>/maps for the default and device formats. It
10-
// omits the inode information because it's not used by those formats.
11-
#[derive(Debug, PartialEq)]
9+
// Represents a parsed single line from /proc/<PID>/maps.
10+
#[derive(Debug, Clone, Default, PartialEq)]
1211
pub struct MapLine {
1312
pub address: String,
1413
pub size_in_kb: u64,
1514
pub perms: Perms,
1615
pub offset: String,
1716
pub device: String,
17+
pub inode: u64,
1818
pub mapping: String,
1919
}
2020

2121
// Represents a set of permissions from the "perms" column of /proc/<PID>/maps.
22-
#[derive(Clone, Copy, Debug, PartialEq)]
22+
#[derive(Clone, Copy, Debug, Default, PartialEq)]
2323
pub struct Perms {
2424
pub readable: bool,
2525
pub writable: bool,
@@ -84,8 +84,12 @@ pub fn parse_map_line(line: &str) -> Result<MapLine, Error> {
8484
.ok_or_else(|| Error::from(ErrorKind::InvalidData))?;
8585
let device = parse_device(device)?;
8686

87-
// skip the "inode" column
88-
let mapping: String = rest.splitn(2, ' ').skip(1).collect();
87+
let (inode, mapping) = rest
88+
.split_once(' ')
89+
.ok_or_else(|| Error::from(ErrorKind::InvalidData))?;
90+
let inode = inode
91+
.parse::<u64>()
92+
.map_err(|_| Error::from(ErrorKind::InvalidData))?;
8993
let mapping = mapping.trim_ascii_start();
9094
let mapping = parse_mapping(mapping);
9195

@@ -95,6 +99,7 @@ pub fn parse_map_line(line: &str) -> Result<MapLine, Error> {
9599
perms,
96100
offset,
97101
device,
102+
inode,
98103
mapping,
99104
})
100105
}
@@ -146,6 +151,7 @@ mod test {
146151
perms: Perms,
147152
offset: &str,
148153
device: &str,
154+
inode: u64,
149155
mapping: &str,
150156
) -> MapLine {
151157
MapLine {
@@ -154,6 +160,7 @@ mod test {
154160
perms,
155161
offset: offset.to_string(),
156162
device: device.to_string(),
163+
inode,
157164
mapping: mapping.to_string(),
158165
}
159166
}
@@ -169,31 +176,31 @@ mod test {
169176
fn test_parse_map_line() {
170177
let data = [
171178
(
172-
create_map_line("000062442eb9e000", 16, Perms::from("r--p"), "0000000000000000", "008:00008", "konsole"),
179+
create_map_line("000062442eb9e000", 16, Perms::from("r--p"), "0000000000000000", "008:00008", 10813151, "konsole"),
173180
"62442eb9e000-62442eba2000 r--p 00000000 08:08 10813151 /usr/bin/konsole"
174181
),
175182
(
176-
create_map_line("000071af50000000", 132, Perms::from("rw-p"), "0000000000000000", "000:00000", " [ anon ]"),
183+
create_map_line("000071af50000000", 132, Perms::from("rw-p"), "0000000000000000", "000:00000", 0, " [ anon ]"),
177184
"71af50000000-71af50021000 rw-p 00000000 00:00 0 "
178185
),
179186
(
180-
create_map_line("00007ffc3f8df000", 132, Perms::from("rw-p"), "0000000000000000", "000:00000", " [ stack ]"),
187+
create_map_line("00007ffc3f8df000", 132, Perms::from("rw-p"), "0000000000000000", "000:00000", 0, " [ stack ]"),
181188
"7ffc3f8df000-7ffc3f900000 rw-p 00000000 00:00 0 [stack]"
182189
),
183190
(
184-
create_map_line("000071af8c9e6000", 16, Perms::from("rw-s"), "0000000105830000", "000:00010", " [ anon ]"),
191+
create_map_line("000071af8c9e6000", 16, Perms::from("rw-s"), "0000000105830000", "000:00010", 1075, " [ anon ]"),
185192
"71af8c9e6000-71af8c9ea000 rw-s 105830000 00:10 1075 anon_inode:i915.gem"
186193
),
187194
(
188-
create_map_line("000071af6cf0c000", 3560, Perms::from("rw-s"), "0000000000000000", "000:00001", "memfd:wayland-shm (deleted)"),
195+
create_map_line("000071af6cf0c000", 3560, Perms::from("rw-s"), "0000000000000000", "000:00001", 256481, "memfd:wayland-shm (deleted)"),
189196
"71af6cf0c000-71af6d286000 rw-s 00000000 00:01 256481 /memfd:wayland-shm (deleted)"
190197
),
191198
(
192-
create_map_line("ffffffffff600000", 4, Perms::from("--xp"), "0000000000000000", "000:00000", " [ anon ]"),
199+
create_map_line("ffffffffff600000", 4, Perms::from("--xp"), "0000000000000000", "000:00000", 0, " [ anon ]"),
193200
"ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]"
194201
),
195202
(
196-
create_map_line("00005e8187da8000", 24, Perms::from("r--p"), "0000000000000000", "008:00008", "hello world"),
203+
create_map_line("00005e8187da8000", 24, Perms::from("r--p"), "0000000000000000", "008:00008", 9524160, "hello world"),
197204
"5e8187da8000-5e8187dae000 r--p 00000000 08:08 9524160 /usr/bin/hello world"
198205
),
199206
];

src/uu/pmap/src/pmap.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55

66
use clap::{crate_version, Arg, ArgAction, Command};
77
use maps_format_parser::{parse_map_line, MapLine};
8+
use smaps_format_parser::{parse_smap_entries, SmapEntry};
89
use std::env;
910
use std::fs;
1011
use std::io::Error;
1112
use uucore::error::{set_exit_code, UResult};
1213
use uucore::{format_usage, help_about, help_usage};
1314

1415
mod maps_format_parser;
16+
mod smaps_format_parser;
1517

1618
const ABOUT: &str = help_about!("pmap.md");
1719
const USAGE: &str = help_usage!("pmap.md");
@@ -49,7 +51,11 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
4951
}
5052
}
5153

52-
if matches.get_flag(options::DEVICE) {
54+
if matches.get_flag(options::EXTENDED) {
55+
output_extended_format(pid)
56+
.map_err(|_| set_exit_code(1))
57+
.ok();
58+
} else if matches.get_flag(options::DEVICE) {
5359
output_device_format(pid).map_err(|_| set_exit_code(1)).ok();
5460
} else {
5561
output_default_format(pid)
@@ -90,6 +96,21 @@ where
9096
Ok(())
9197
}
9298

99+
fn process_smaps<F>(pid: &str, mut process_entry: F) -> Result<(), Error>
100+
where
101+
F: FnMut(&SmapEntry),
102+
{
103+
let path = format!("/proc/{pid}/smaps");
104+
let contents = fs::read_to_string(path)?;
105+
let smap_entries = parse_smap_entries(&contents)?;
106+
107+
for entry in smap_entries {
108+
process_entry(&entry);
109+
}
110+
111+
Ok(())
112+
}
113+
93114
fn output_default_format(pid: &str) -> Result<(), Error> {
94115
let mut total = 0;
95116

@@ -106,6 +127,34 @@ fn output_default_format(pid: &str) -> Result<(), Error> {
106127
Ok(())
107128
}
108129

130+
fn output_extended_format(pid: &str) -> Result<(), Error> {
131+
let mut total_mapped = 0;
132+
let mut total_rss = 0;
133+
let mut total_dirty = 0;
134+
135+
println!("Address Kbytes RSS Dirty Mode Mapping");
136+
137+
process_smaps(pid, |smap_entry| {
138+
println!(
139+
"{} {:>7} {:>7} {:>7} {} {}",
140+
smap_entry.map_line.address,
141+
smap_entry.map_line.size_in_kb,
142+
smap_entry.rss_in_kb,
143+
smap_entry.shared_dirty_in_kb + smap_entry.private_dirty_in_kb,
144+
smap_entry.map_line.perms,
145+
smap_entry.map_line.mapping
146+
);
147+
total_mapped += smap_entry.map_line.size_in_kb;
148+
total_rss += smap_entry.rss_in_kb;
149+
total_dirty += smap_entry.shared_dirty_in_kb + smap_entry.private_dirty_in_kb;
150+
})?;
151+
152+
println!("---------------- ------- ------- ------- ");
153+
println!("total kB {total_mapped:>7} {total_rss:>7} {total_dirty:>7}");
154+
155+
Ok(())
156+
}
157+
109158
fn output_device_format(pid: &str) -> Result<(), Error> {
110159
let mut total_mapped = 0;
111160
let mut total_writeable_private = 0;

0 commit comments

Comments
 (0)