@@ -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+ }
0 commit comments