Skip to content

Commit 61f6bc6

Browse files
committed
Add SDE into benchmark tool
1 parent c980ae0 commit 61f6bc6

File tree

6 files changed

+262
-10
lines changed

6 files changed

+262
-10
lines changed

benchmarking-tool/Cargo.lock

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

benchmarking-tool/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ name = "benchmark"
88
path = "main.rs"
99

1010
[dependencies]
11+
json-builder-macro = "0.0.3"
1112
rustc-demangle = "0.1.26"
13+
sde-output-parser = { path = "sde-output-parser", version = "*" }
1214

1315
# [target.unix.dependencies]
1416
# perf-event-open = "0.4.2"

benchmarking-tool/main.rs

Lines changed: 203 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ fn main() {
5454
run_qbdi(args);
5555
}
5656
"sde" => {
57-
todo!()
57+
run_sde(args);
5858
}
5959
"time" => {
6060
todo!()
@@ -74,20 +74,26 @@ fn run_qbdi(mut args: impl Iterator<Item = String>) {
7474
let root = std::env::current_exe().unwrap();
7575
let library = root.parent().unwrap().join(library_name);
7676
if !library.is_file() {
77-
eprintln!("{library_name:?} not adjacent to {root:?}. {library} does not exist", library=library.display());
77+
eprintln!(
78+
"{library_name:?} not adjacent to {root:?}. {library} does not exist",
79+
library = library.display()
80+
);
7881
return;
7982
}
8083
command.env("DYLD_BIND_AT_LAUNCH", "1");
8184
command.env("DYLD_INSERT_LIBRARIES", &library.display().to_string());
8285
}
83-
86+
8487
#[cfg(target_os = "linux")]
8588
{
8689
let library_name = "libqbdi_tracer.so";
8790
let root = std::env::current_exe().unwrap();
8891
let library = root.parent().unwrap().join(library_name);
8992
if !library.is_file() {
90-
eprintln!("{library_name:?} not adjacent to {root:?}. {library} does not exist", library=library.display());
93+
eprintln!(
94+
"{library_name:?} not adjacent to {root:?}. {library} does not exist",
95+
library = library.display()
96+
);
9197
return;
9298
}
9399
command.env("LD_PRELOAD", &library.display().to_string());
@@ -159,9 +165,7 @@ fn run_qbdi(mut args: impl Iterator<Item = String>) {
159165
}
160166
}
161167

162-
let item = items
163-
.entry(func)
164-
.or_default();
168+
let item = items.entry(func).or_default();
165169

166170
item.total += count;
167171
item.instruction_kind.push((kind.to_owned(), count));
@@ -174,11 +178,201 @@ fn run_qbdi(mut args: impl Iterator<Item = String>) {
174178
items.sort_unstable_by_key(|(_, value)| usize::MAX - value.total);
175179

176180
for (func, mut item) in items {
177-
print!("{func} - {total} instructions", total=item.total);
178-
item.instruction_kind.sort_unstable_by_key(|(_, value)| usize::MAX - value);
181+
print!("{func} - {total} instructions", total = item.total);
182+
item.instruction_kind
183+
.sort_unstable_by_key(|(_, value)| usize::MAX - value);
179184
for (kind, count) in &item.instruction_kind {
180185
print!(" ({kind}={count})");
181186
}
182187
println!();
183188
}
184189
}
190+
191+
fn run_sde(mut args: impl Iterator<Item = String>) {
192+
const TEMP_FILE: &str = "sde-out.txt";
193+
194+
let mut command_string = None;
195+
let mut blocks = 20;
196+
let mut keep = None;
197+
198+
let mut format = None;
199+
let mut sort = None;
200+
let mut sort_direction = utilities::Direction::Ascending;
201+
let mut limit = 25;
202+
let mut skip_rust_internals = true;
203+
204+
while let Some(arg) = args.next() {
205+
match arg.as_str() {
206+
"program" => {
207+
// command_args.push(arg.value.unwrap());
208+
command_string = args.next();
209+
}
210+
"format" => {
211+
format = args.next();
212+
}
213+
"sort" => {
214+
sort = args.next();
215+
}
216+
"sort-desc" => {
217+
sort_direction = utilities::Direction::Descending;
218+
}
219+
"blocks" => {
220+
blocks = args.next().unwrap().parse().expect("invalid top blocks");
221+
}
222+
"limit" => {
223+
limit = args.next().unwrap().parse().expect("invalid limit");
224+
}
225+
"keep" => {
226+
keep = args.next();
227+
}
228+
"all" => {
229+
skip_rust_internals = false;
230+
}
231+
arg => {
232+
eprintln!("unknown {arg:?}");
233+
}
234+
}
235+
}
236+
237+
let file_path: &str = keep.as_deref().unwrap_or(TEMP_FILE);
238+
239+
{
240+
let mut command = Command::new("sde");
241+
command.args([
242+
"-omix",
243+
file_path,
244+
"-mix_filter_no_shared_libs",
245+
"-top_blocks",
246+
&blocks.to_string(),
247+
"--",
248+
]);
249+
{
250+
// TODO use argument parser instead of split
251+
let arguments = command_string.expect("no command!");
252+
let arguments = arguments.split(' ').collect::<Vec<_>>();
253+
command.args(arguments);
254+
}
255+
// command.args(command_args);
256+
command.stdout(Stdio::piped());
257+
command.stderr(Stdio::piped());
258+
259+
let mut child = command.spawn().unwrap();
260+
let _ = child.wait().unwrap();
261+
}
262+
263+
let file = std::fs::File::open(file_path).unwrap();
264+
265+
let sort = sort.as_deref().map(|field| utilities::Sorting {
266+
field,
267+
direction: sort_direction,
268+
});
269+
270+
let format = format.as_deref().unwrap_or("plain");
271+
272+
{
273+
fn parse_and_print(
274+
out: impl BufRead,
275+
skip_rust_internals: bool,
276+
sort: Option<utilities::Sorting>,
277+
limit: usize,
278+
format: &str,
279+
) {
280+
const MAX_WIDTH: usize = 100;
281+
const WHITESPACE: &str = if let Ok(result) = str::from_utf8(&[b' '; MAX_WIDTH]) {
282+
result
283+
} else {
284+
""
285+
};
286+
287+
let mut rows = sde_output_parser::parse(out, skip_rust_internals);
288+
289+
if let Some(sort) = sort {
290+
match sort.field {
291+
"name" => {
292+
rows.sort_unstable_by(|lhs, rhs| sort.direction.compare(&lhs.0, &rhs.0));
293+
}
294+
"total" => {
295+
rows.sort_unstable_by(|lhs, rhs| {
296+
sort.direction.compare(&lhs.1.total, &rhs.1.total)
297+
});
298+
}
299+
field => {
300+
eprintln!("unknown field {field:?}");
301+
}
302+
}
303+
}
304+
305+
let skip = if let Some(utilities::Sorting {
306+
direction: utilities::Direction::Descending,
307+
..
308+
}) = sort
309+
{
310+
rows.len().saturating_sub(limit)
311+
} else {
312+
0
313+
};
314+
315+
if let "plain" = format {
316+
let max_name_width = {
317+
let mut max_name_width = 0;
318+
for (name, _) in rows.iter().skip(skip).take(limit) {
319+
max_name_width = std::cmp::max(max_name_width, name.len());
320+
}
321+
std::cmp::min(max_name_width, MAX_WIDTH)
322+
};
323+
324+
for (section, count) in rows.iter().skip(skip).take(limit) {
325+
use std::borrow::Cow;
326+
use utilities::to_denary;
327+
328+
let section: Cow<'_, str> = if section.len() > MAX_WIDTH {
329+
Cow::Owned(format!("{prefix}...", prefix = &section[..MAX_WIDTH - 3]))
330+
} else {
331+
Cow::Borrowed(section)
332+
};
333+
let fill = &WHITESPACE[..max_name_width - section.len()];
334+
335+
// TODO wip
336+
print!("{section}:{fill}");
337+
print!(" t: {}", to_denary(count.total as usize, "\u{00A0}"));
338+
print!(" mr: {}", to_denary(count.mem_read as usize, "\u{00A0}"));
339+
print!(" mw: {}", to_denary(count.mem_write as usize, "\u{00A0}"));
340+
print!(" call: {}", to_denary(count.call as usize, "\u{00A0}"));
341+
println!();
342+
}
343+
} else if let "json" = format {
344+
let mut buf = String::from("[");
345+
for (section, count) in rows.iter().skip(skip).take(limit) {
346+
if buf.len() > 1 {
347+
buf.push(',');
348+
}
349+
buf.push_str(&json_builder_macro::json! {
350+
name: section.as_str(),
351+
total: count.total,
352+
mem_read: count.mem_read,
353+
mem_write: count.mem_write,
354+
stack_read: count.stack_read,
355+
stack_write: count.stack_write,
356+
call: count.call,
357+
});
358+
}
359+
buf.push(']');
360+
println!("{buf}");
361+
} else {
362+
eprintln!("unknown format {format:?}");
363+
}
364+
}
365+
366+
parse_and_print(
367+
BufReader::new(file),
368+
skip_rust_internals,
369+
sort,
370+
limit,
371+
format,
372+
);
373+
}
374+
375+
if keep.is_none() {
376+
std::fs::remove_file(file_path).unwrap();
377+
}
378+
}

benchmarking-tool/sde-output-parser/Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

benchmarking-tool/sde-output-parser/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,3 @@ publish = false
66

77
[lib]
88
path = "lib.rs"
9-

benchmarking-tool/utilities.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,44 @@ impl<'a> Iterator for ArgumentIter<'a> {
7474
}
7575
}
7676

77+
#[derive(Clone, Copy, Debug)]
78+
pub(crate) struct Sorting<'a> {
79+
pub field: &'a str,
80+
pub direction: Direction,
81+
}
82+
83+
#[derive(Clone, Copy, Debug)]
84+
pub(crate) enum Direction {
85+
Ascending,
86+
Descending,
87+
}
88+
89+
impl Direction {
90+
pub fn compare<T: std::cmp::Ord>(self, a: &T, b: &T) -> std::cmp::Ordering {
91+
let order = a.cmp(b);
92+
if let Self::Ascending = self {
93+
order.reverse()
94+
} else {
95+
order
96+
}
97+
}
98+
}
99+
100+
pub(crate) fn to_denary(value: usize, seperator: &str) -> String {
101+
if value == 0 {
102+
return "0".to_owned();
103+
}
104+
let mut buf = String::new();
105+
for i in (0..=value.ilog10()).rev() {
106+
let j = (value / 10i32.pow(i) as usize) % 10;
107+
buf.push(b"0123456789"[j] as char);
108+
if i > 0 && i % 3 == 0 {
109+
buf.push_str(seperator);
110+
}
111+
}
112+
buf
113+
}
114+
77115
#[cfg(test)]
78116
mod tests {
79117
use super::*;

0 commit comments

Comments
 (0)