Skip to content

Commit a74b58a

Browse files
authored
Merge pull request #94 from data-pup/add-top-summary
Added summary and total rows to top command.
2 parents 8773b06 + 4ef7532 commit a74b58a

File tree

8 files changed

+144
-27
lines changed

8 files changed

+144
-27
lines changed

β€Žanalyze/analyze.rs

Lines changed: 109 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -126,35 +126,120 @@ struct Top {
126126
impl traits::Emit for Top {
127127
#[cfg(feature = "emit_text")]
128128
fn emit_text(&self, items: &ir::Items, dest: &mut io::Write) -> Result<(), traits::Error> {
129-
let sort_label = if self.opts.retained() {
130-
"Retained"
131-
} else {
132-
"Shallow"
129+
// A struct used to represent a row in the table that will be emitted.
130+
struct TableRow {
131+
size: u32,
132+
size_percent: f64,
133+
name: String,
134+
};
135+
136+
// Helper function used to process an item, and return a struct
137+
// representing a row containing its size and name.
138+
fn process_item(id: ir::Id, items: &ir::Items, retained: bool) -> TableRow {
139+
let item = &items[id];
140+
let size = if retained {
141+
items.retained_size(id)
142+
} else {
143+
item.size()
144+
};
145+
let size_percent = (f64::from(size)) / (f64::from(items.size())) * 100.0;
146+
let name = item.name().to_string();
147+
TableRow {
148+
size,
149+
size_percent,
150+
name,
151+
}
133152
};
134153

154+
// Helper function used to summnarize a sequence of table rows. This is
155+
// used to generate the remaining summary and total rows. Returns a tuple
156+
// containing the total size, total size percentage, and number of items.
157+
fn summarize_rows(rows: impl Iterator<Item = TableRow>) -> (u32, f64, u32) {
158+
rows.fold(
159+
(0, 0.0, 0),
160+
|(total_size, total_percent, remaining_count),
161+
TableRow {
162+
size,
163+
size_percent,
164+
name: _,
165+
}| {
166+
(
167+
total_size + size,
168+
total_percent + size_percent,
169+
remaining_count + 1,
170+
)
171+
},
172+
)
173+
}
174+
175+
// Access the options that are relevant to emitting the correct output.
176+
let max_items = self.opts.max_items() as usize;
177+
let retained = self.opts.retained();
178+
let sort_label = if retained { "Retained" } else { "Shallow" };
179+
180+
// Initialize a new table.
135181
let mut table = Table::with_header(vec![
136182
(Align::Right, format!("{} Bytes", sort_label)),
137183
(Align::Right, format!("{} %", sort_label)),
138184
(Align::Left, "Item".to_string()),
139185
]);
140186

141-
for &id in &self.items {
142-
let item = &items[id];
187+
// Process the number of items specified, and add them to the table.
188+
self.items
189+
.iter()
190+
.take(max_items)
191+
.map(|&id| process_item(id, items, retained))
192+
.for_each(
193+
|TableRow {
194+
size,
195+
size_percent,
196+
name,
197+
}| {
198+
table.add_row(vec![
199+
size.to_string(),
200+
format!("{:.2}%", size_percent),
201+
name,
202+
])
203+
},
204+
);
143205

144-
let size = if self.opts.retained() {
145-
items.retained_size(id)
206+
// Find the summary statistics by processing the remaining items.
207+
let remaining_rows = self
208+
.items
209+
.iter()
210+
.skip(max_items)
211+
.map(|&id| process_item(id, items, retained));
212+
let (rem_size, rem_size_percent, rem_count) = summarize_rows(remaining_rows);
213+
214+
// If there were items remaining, add a summary row to the table.
215+
if rem_count > 0 {
216+
let rem_name_col = format!("... and {} more.", rem_count);
217+
let (rem_size_col, rem_size_percent_col) = if retained {
218+
("...".to_string(), "...".to_string())
146219
} else {
147-
item.size()
220+
(rem_size.to_string(), format!("{:.2}%", rem_size_percent))
148221
};
149-
150-
let size_percent = (f64::from(size)) / (f64::from(items.size())) * 100.0;
151-
table.add_row(vec![
152-
size.to_string(),
153-
format!("{:.2}%", size_percent),
154-
item.name().to_string(),
155-
]);
222+
table.add_row(vec![rem_size_col, rem_size_percent_col, rem_name_col]);
156223
}
157224

225+
// Add a row containing the totals to the table.
226+
let all_rows = self
227+
.items
228+
.iter()
229+
.map(|&id| process_item(id, items, retained));
230+
let (total_size, total_size_percent, total_count) = summarize_rows(all_rows);
231+
let total_name_col = format!("Ξ£ [{} Total Rows]", total_count);
232+
let (total_size_col, total_size_percent_col) = if retained {
233+
("...".to_string(), "...".to_string())
234+
} else {
235+
(
236+
total_size.to_string(),
237+
format!("{:.2}%", total_size_percent),
238+
)
239+
};
240+
table.add_row(vec![total_size_col, total_size_percent_col, total_name_col]);
241+
242+
// Write the generated table out to the destination and return.
158243
write!(dest, "{}", &table)?;
159244
Ok(())
160245
}
@@ -163,7 +248,10 @@ impl traits::Emit for Top {
163248
fn emit_json(&self, items: &ir::Items, dest: &mut io::Write) -> Result<(), traits::Error> {
164249
let mut arr = json::array(dest)?;
165250

166-
for &id in &self.items {
251+
let max_items = self.opts.max_items() as usize;
252+
let items_iter = self.items.iter();
253+
254+
for &id in items_iter.take(max_items) {
167255
let item = &items[id];
168256

169257
let mut obj = arr.object()?;
@@ -199,7 +287,10 @@ impl traits::Emit for Top {
199287
retained_size_percent: Option<f64>,
200288
}
201289

202-
for &id in &self.items {
290+
let max_items = self.opts.max_items() as usize;
291+
let items_iter = self.items.iter();
292+
293+
for &id in items_iter.take(max_items) {
203294
let item = &items[id];
204295

205296
let (shallow_size, shallow_size_percent) = {
@@ -252,8 +343,6 @@ pub fn top(items: &mut ir::Items, opts: &opt::Top) -> Result<Box<traits::Emit>,
252343
.cmp(&items.retained_size(a.id())),
253344
});
254345

255-
top_items.truncate(opts.number() as usize);
256-
257346
let top_items: Vec<_> = top_items.into_iter().map(|i| i.id()).collect();
258347

259348
let top = Top {

β€Žopt/definitions.rs

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ pub enum Options {
4343
}
4444

4545
/// List the top code size offenders in a binary.
46-
#[derive(Clone, Debug, Default)]
46+
#[derive(Clone, Debug)]
4747
#[derive(StructOpt)]
4848
#[wasm_bindgen]
4949
pub struct Top {
@@ -63,8 +63,8 @@ pub struct Top {
6363
output_format: traits::OutputFormat,
6464

6565
/// The maximum number of items to display.
66-
#[structopt(short = "n")]
67-
number: Option<u32>,
66+
#[structopt(short = "n", default_value = "4294967295")]
67+
max_items: u32,
6868

6969
/// Display retaining paths.
7070
#[structopt(short = "r", long = "retaining-paths")]
@@ -75,6 +75,23 @@ pub struct Top {
7575
retained: bool,
7676
}
7777

78+
impl Default for Top {
79+
fn default() -> Top {
80+
Top {
81+
#[cfg(feature = "cli")]
82+
input: Default::default(),
83+
#[cfg(feature = "cli")]
84+
output_destination: Default::default(),
85+
#[cfg(feature = "cli")]
86+
output_format: Default::default(),
87+
88+
max_items: 4294967295,
89+
retaining_paths: false,
90+
retained: false,
91+
}
92+
}
93+
}
94+
7895
#[wasm_bindgen]
7996
impl Top {
8097
/// Construct a new, default `Top`.
@@ -83,8 +100,8 @@ impl Top {
83100
}
84101

85102
/// The maximum number of items to display.
86-
pub fn number(&self) -> u32 {
87-
self.number.unwrap_or(u32::MAX)
103+
pub fn max_items(&self) -> u32 {
104+
self.max_items
88105
}
89106

90107
/// Display retaining paths.
@@ -98,8 +115,8 @@ impl Top {
98115
}
99116

100117
/// Set the maximum number of items to display.
101-
pub fn set_number(&mut self, n: u32) {
102-
self.number = Some(n);
118+
pub fn set_max_items(&mut self, n: u32) {
119+
self.max_items = n;
103120
}
104121

105122
/// Set whether to display and compute retaining paths.

β€Žtwiggy/tests/expectations/elf_top_25_hello_world_rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,5 @@
2525
2158 β”Š 0.10% β”Š prof_tdata_destroy_locked
2626
2041 β”Š 0.10% β”Š stats_print_helper
2727
1897 β”Š 0.09% β”Š imemalign
28+
150168 β”Š 7.15% β”Š ... and 753 more.
29+
256639 β”Š 12.23% β”Š Ξ£ [778 Total Rows]

β€Žtwiggy/tests/expectations/elf_top_hello_world_rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,3 +778,4 @@
778778
0 β”Š 0.00% β”Š je_prof_boot1
779779
0 β”Š 0.00% β”Š je_prof_boot2
780780
0 β”Š 0.00% β”Š je_malloc_tsd_no_cleanup
781+
256639 β”Š 12.23% β”Š Ξ£ [778 Total Rows]

β€Žtwiggy/tests/expectations/top_mappings

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@
1010
1219 β”Š 2.69% β”Š memset
1111
1217 β”Š 2.69% β”Š __powidf2
1212
1142 β”Š 2.52% β”Š __udivsi3
13+
23762 β”Š 52.53% β”Š ... and 338 more.
14+
45181 β”Š 99.89% β”Š Ξ£ [348 Total Rows]

β€Žtwiggy/tests/expectations/top_retained_mappings

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@
1010
3149 β”Š 6.96% β”Š __powidf2
1111
2722 β”Š 6.02% β”Š func[78]
1212
2721 β”Š 6.02% β”Š __divsf3
13+
... β”Š ... β”Š ... and 338 more.
14+
... β”Š ... β”Š Ξ£ [348 Total Rows]

β€Žtwiggy/tests/expectations/top_retained_wee_alloc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@
1010
226 β”Š 8.02% β”Š func[3]
1111
225 β”Š 7.99% β”Š wee_alloc::alloc_first_fit::h9a72de3af77ef93f
1212
136 β”Š 4.83% β”Š <wee_alloc::size_classes::SizeClassAllocPolicy<'a> as wee_alloc::AllocPolicy>::new_cell_for_free_list::h3987e3054b8224e6
13+
... β”Š ... β”Š ... and 29 more.
14+
... β”Š ... β”Š Ξ£ [39 Total Rows]

β€Žtwiggy/tests/expectations/top_wee_alloc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@
1010
44 β”Š 1.56% β”Š goodbye
1111
25 β”Š 0.89% β”Š data[1]
1212
25 β”Š 0.89% β”Š data[2]
13+
117 β”Š 4.15% β”Š ... and 29 more.
14+
2772 β”Š 98.40% β”Š Ξ£ [39 Total Rows]

0 commit comments

Comments
Β (0)