Skip to content

Commit 7ec4a41

Browse files
pmap: implemented --range option (#461)
* pmap: implemented range option * pmap: added tests for range option * fixed lint errors --------- Co-authored-by: Krysztal Huang <[email protected]>
1 parent 71e96db commit 7ec4a41

File tree

4 files changed

+261
-54
lines changed

4 files changed

+261
-54
lines changed

src/uu/pmap/src/maps_format_parser.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ impl Address {
3939
pub fn zero_pad(&self) -> String {
4040
format!("{:0>16}", self.start)
4141
}
42+
43+
// Checks whether an entry's address range overlaps the limits specified by the range option.
44+
// Note: Even if a reversed range (high < low) is given, an entry still hits
45+
// only if the specified range lies entirely within the entry's address range.
46+
pub fn is_within_range(&self, pmap_config: &PmapConfig) -> bool {
47+
pmap_config.range_low < self.high && self.low <= pmap_config.range_high
48+
}
4249
}
4350

4451
// Represents a set of permissions from the "perms" column of /proc/<PID>/maps.
@@ -341,6 +348,90 @@ mod test {
341348
assert!(parse_address("ffffffffff600000-zfffffffff601000").is_err());
342349
}
343350

351+
fn limit_address_range_and_assert(address: &Address, low: u64, high: u64, expected: bool) {
352+
let mut pmap_config = PmapConfig::default();
353+
(pmap_config.range_low, pmap_config.range_high) = (low, high);
354+
assert_eq!(
355+
address.is_within_range(&pmap_config),
356+
expected,
357+
"`--range 0x{low:x},0x{high:x}` expected to be {expected} for address 0x{:x},0x{:x}",
358+
address.low,
359+
address.high,
360+
);
361+
}
362+
363+
#[test]
364+
fn test_limit_address_range() {
365+
let low: u64 = 0x71af50000000;
366+
let high: u64 = 0x71af50021000;
367+
let address = Address {
368+
start: "0x71af50000000".to_string(),
369+
low,
370+
high,
371+
};
372+
373+
limit_address_range_and_assert(&address, 0x0, u64::MAX, true);
374+
limit_address_range_and_assert(&address, 0x70000000, 0xffffffffffff, true);
375+
376+
limit_address_range_and_assert(&address, 0x0, 0x0, false);
377+
limit_address_range_and_assert(&address, 0x0, 0x70000000, false);
378+
limit_address_range_and_assert(&address, 0x0, low - 1, false);
379+
limit_address_range_and_assert(&address, low - 1, low - 1, false);
380+
381+
limit_address_range_and_assert(&address, 0x0, low + 0, true);
382+
limit_address_range_and_assert(&address, low - 1, low + 0, true);
383+
limit_address_range_and_assert(&address, low + 0, low + 0, true);
384+
385+
limit_address_range_and_assert(&address, low - 1, high - 1, true);
386+
limit_address_range_and_assert(&address, low - 1, high + 0, true);
387+
limit_address_range_and_assert(&address, low - 1, high + 1, true);
388+
limit_address_range_and_assert(&address, low + 0, high - 1, true);
389+
limit_address_range_and_assert(&address, low + 0, high + 0, true);
390+
limit_address_range_and_assert(&address, low + 0, high + 1, true);
391+
limit_address_range_and_assert(&address, low + 1, high - 1, true);
392+
limit_address_range_and_assert(&address, low + 1, high + 0, true);
393+
limit_address_range_and_assert(&address, low + 1, high + 1, true);
394+
395+
limit_address_range_and_assert(&address, high - 1, high - 1, true);
396+
limit_address_range_and_assert(&address, high - 1, high + 0, true);
397+
limit_address_range_and_assert(&address, high - 1, u64::MAX, true);
398+
399+
limit_address_range_and_assert(&address, high + 0, high + 0, false);
400+
limit_address_range_and_assert(&address, high + 0, high + 1, false);
401+
limit_address_range_and_assert(&address, high + 0, u64::MAX, false);
402+
limit_address_range_and_assert(&address, 0xffffffffffff, u64::MAX, false);
403+
limit_address_range_and_assert(&address, u64::MAX, u64::MAX, false);
404+
405+
// Reversed range
406+
407+
limit_address_range_and_assert(&address, u64::MAX, 0, false);
408+
limit_address_range_and_assert(&address, 0xffffffffffff, 0x70000000, false);
409+
410+
limit_address_range_and_assert(&address, 0x70000000, 0x0, false);
411+
limit_address_range_and_assert(&address, low - 1, 0x0, false);
412+
limit_address_range_and_assert(&address, low - 1, low - 1, false);
413+
414+
limit_address_range_and_assert(&address, low + 0, 0x0, false);
415+
limit_address_range_and_assert(&address, low + 0, low - 1, false);
416+
417+
limit_address_range_and_assert(&address, high - 1, low - 1, false);
418+
limit_address_range_and_assert(&address, high + 0, low - 1, false);
419+
limit_address_range_and_assert(&address, high + 1, low - 1, false);
420+
limit_address_range_and_assert(&address, high - 1, low + 0, true); // true
421+
limit_address_range_and_assert(&address, high + 0, low + 0, false);
422+
limit_address_range_and_assert(&address, high + 1, low + 0, false);
423+
limit_address_range_and_assert(&address, high - 1, low + 1, true); // true
424+
limit_address_range_and_assert(&address, high + 0, low + 1, false);
425+
limit_address_range_and_assert(&address, high + 1, low + 1, false);
426+
427+
limit_address_range_and_assert(&address, high + 0, high - 1, false);
428+
limit_address_range_and_assert(&address, u64::MAX, high - 1, false);
429+
430+
limit_address_range_and_assert(&address, high + 1, high + 0, false);
431+
limit_address_range_and_assert(&address, u64::MAX, high + 0, false);
432+
limit_address_range_and_assert(&address, u64::MAX, 0xffffffffffff, false);
433+
}
434+
344435
#[test]
345436
fn test_parse_device() {
346437
assert_eq!("12:34", parse_device("12:34").unwrap().to_string());

src/uu/pmap/src/pmap.rs

Lines changed: 91 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use pmap_config::{create_rc, pmap_field_name, PmapConfig};
99
use smaps_format_parser::{parse_smaps, SmapTable};
1010
use std::env;
1111
use std::fs;
12-
use std::io::Error;
13-
use uucore::error::{set_exit_code, UResult};
12+
use std::io::{Error, ErrorKind};
13+
use uucore::error::{set_exit_code, UResult, USimpleError};
1414
use uucore::{format_usage, help_about, help_usage};
1515

1616
mod maps_format_parser;
@@ -98,8 +98,45 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
9898

9999
// Options independent with field selection:
100100
pmap_config.quiet = matches.get_flag(options::QUIET);
101-
if matches.get_flag(options::SHOW_PATH) {
102-
pmap_config.show_path = true;
101+
pmap_config.show_path = matches.get_flag(options::SHOW_PATH);
102+
103+
if let Some(range) = matches.get_one::<String>(options::RANGE) {
104+
match range.matches(',').count() {
105+
0 => {
106+
let address = u64::from_str_radix(range, 16).map_err(|_| {
107+
USimpleError::new(1, format!("failed to parse argument: '{range}'"))
108+
})?;
109+
pmap_config.range_low = address;
110+
pmap_config.range_high = address;
111+
}
112+
1 => {
113+
let (low, high) = range
114+
.split_once(',')
115+
.ok_or_else(|| Error::from(ErrorKind::InvalidData))?;
116+
pmap_config.range_low = if low.is_empty() {
117+
0
118+
} else {
119+
u64::from_str_radix(low, 16).map_err(|_| {
120+
USimpleError::new(1, format!("failed to parse argument: '{range}'"))
121+
})?
122+
};
123+
pmap_config.range_high = if high.is_empty() {
124+
u64::MAX
125+
} else {
126+
u64::from_str_radix(high, 16).map_err(|_| {
127+
USimpleError::new(1, format!("failed to parse argument: '{range}'"))
128+
})?
129+
};
130+
}
131+
_ => {
132+
eprintln!("pmap: failed to parse argument: '{range}'");
133+
set_exit_code(1);
134+
return Ok(());
135+
}
136+
}
137+
} else {
138+
pmap_config.range_low = 0;
139+
pmap_config.range_high = u64::MAX;
103140
}
104141

105142
let pids = matches
@@ -182,14 +219,16 @@ fn output_default_format(pid: &str, pmap_config: &PmapConfig) -> Result<(), Erro
182219
let mut total = 0;
183220

184221
process_maps(pid, None, |map_line| {
185-
println!(
186-
"{} {:>6}K {} {}",
187-
map_line.address.zero_pad(),
188-
map_line.size_in_kb,
189-
map_line.perms.mode(),
190-
map_line.parse_mapping(pmap_config)
191-
);
192-
total += map_line.size_in_kb;
222+
if map_line.address.is_within_range(pmap_config) {
223+
println!(
224+
"{} {:>6}K {} {}",
225+
map_line.address.zero_pad(),
226+
map_line.size_in_kb,
227+
map_line.perms.mode(),
228+
map_line.parse_mapping(pmap_config)
229+
);
230+
total += map_line.size_in_kb;
231+
}
193232
})?;
194233

195234
if !pmap_config.quiet {
@@ -206,25 +245,31 @@ fn output_extended_format(pid: &str, pmap_config: &PmapConfig) -> Result<(), Err
206245
println!("Address Kbytes RSS Dirty Mode Mapping");
207246
}
208247

248+
let mut total_size_in_kb = 0;
249+
let mut total_rss_in_kb = 0;
250+
let mut total_dirty_in_kb = 0;
251+
209252
for smap_entry in smap_table.entries {
210-
println!(
211-
"{} {:>7} {:>7} {:>7} {} {}",
212-
smap_entry.map_line.address.zero_pad(),
213-
smap_entry.map_line.size_in_kb,
214-
smap_entry.rss_in_kb,
215-
smap_entry.shared_dirty_in_kb + smap_entry.private_dirty_in_kb,
216-
smap_entry.map_line.perms.mode(),
217-
smap_entry.map_line.parse_mapping(pmap_config)
218-
);
253+
if smap_entry.map_line.address.is_within_range(pmap_config) {
254+
println!(
255+
"{} {:>7} {:>7} {:>7} {} {}",
256+
smap_entry.map_line.address.zero_pad(),
257+
smap_entry.map_line.size_in_kb,
258+
smap_entry.rss_in_kb,
259+
smap_entry.shared_dirty_in_kb + smap_entry.private_dirty_in_kb,
260+
smap_entry.map_line.perms.mode(),
261+
smap_entry.map_line.parse_mapping(pmap_config)
262+
);
263+
total_size_in_kb += smap_entry.map_line.size_in_kb;
264+
total_rss_in_kb += smap_entry.rss_in_kb;
265+
total_dirty_in_kb += smap_entry.shared_dirty_in_kb + smap_entry.private_dirty_in_kb;
266+
}
219267
}
220268

221269
if !pmap_config.quiet {
222270
println!("---------------- ------- ------- ------- ");
223271
println!(
224-
"total kB {:>7} {:>7} {:>7}",
225-
smap_table.info.total_size_in_kb,
226-
smap_table.info.total_rss_in_kb,
227-
smap_table.info.total_shared_dirty_in_kb + smap_table.info.total_private_dirty_in_kb,
272+
"total kB {total_size_in_kb:>7} {total_rss_in_kb:>7} {total_dirty_in_kb:>7}"
228273
);
229274
}
230275

@@ -360,23 +405,25 @@ fn output_device_format(pid: &str, pmap_config: &PmapConfig) -> Result<(), Error
360405
None
361406
},
362407
|map_line| {
363-
println!(
364-
"{} {:>7} {} {:0>16} {} {}",
365-
map_line.address.zero_pad(),
366-
map_line.size_in_kb,
367-
map_line.perms.mode(),
368-
map_line.offset,
369-
map_line.device.device(),
370-
map_line.parse_mapping(pmap_config)
371-
);
372-
total_mapped += map_line.size_in_kb;
408+
if map_line.address.is_within_range(pmap_config) {
409+
println!(
410+
"{} {:>7} {} {:0>16} {} {}",
411+
map_line.address.zero_pad(),
412+
map_line.size_in_kb,
413+
map_line.perms.mode(),
414+
map_line.offset,
415+
map_line.device.device(),
416+
map_line.parse_mapping(pmap_config)
417+
);
418+
total_mapped += map_line.size_in_kb;
373419

374-
if map_line.perms.writable && !map_line.perms.shared {
375-
total_writeable_private += map_line.size_in_kb;
376-
}
420+
if map_line.perms.writable && !map_line.perms.shared {
421+
total_writeable_private += map_line.size_in_kb;
422+
}
377423

378-
if map_line.perms.shared {
379-
total_shared += map_line.size_in_kb;
424+
if map_line.perms.shared {
425+
total_shared += map_line.size_in_kb;
426+
}
380427
}
381428
},
382429
)?;
@@ -547,7 +594,9 @@ pub fn uu_app() -> Command {
547594
Arg::new(options::RANGE)
548595
.short('A')
549596
.long("range")
550-
.num_args(1..=2)
551-
.help("limit results to the given range"),
597+
.num_args(1)
598+
.help("limit results to the given range <low>[,<high>]"),
599+
// This option applies only to the default, extended, or device formats,
600+
// yet it will not raise an error in any other case.
552601
)
553602
}

src/uu/pmap/src/pmap_config.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ pub struct PmapConfig {
8181
// Misc
8282
pub quiet: bool,
8383
pub custom_format_enabled: bool,
84+
pub range_low: u64,
85+
pub range_high: u64,
8486
}
8587

8688
impl PmapConfig {

0 commit comments

Comments
 (0)