1#![recursion_limit = "1024"]
4
5extern crate nom;
6
7#[macro_use]
8extern crate error_chain;
9
10pub mod errors {
11 use std::fmt;
12
13 error_chain! {
15 foreign_links {
16 Io(::std::io::Error);
17 }
18 errors {
19 NomError(desc: String) {
20 description("nom error")
21 display("nom error: '{}'", desc)
22 }
23 }
24 }
25
26 impl<E: fmt::Debug + Clone> From<nom::Err<E>> for Error {
28 fn from(error: nom::Err<E>) -> Self {
29 let desc = match &error {
30 nom::Err::Incomplete(needed) => format!("ran out of bytes: {:?}", needed),
31 nom::Err::Error(_) => format!("{:?}", error),
32 nom::Err::Failure(_) => format!("{:?}", error),
33 };
34
35 Error::from_kind(ErrorKind::NomError(desc))
36 }
37 }
38}
39
40pub mod raw {
41 use crate::errors::*;
42
43 use std::cell::Cell;
44 use std::cmp::Reverse;
45 use std::collections::BinaryHeap;
46 use std::collections::HashMap;
47 use std::io::{BufRead, BufReader, Read};
48 use std::sync::mpsc::{channel, Receiver, Sender};
49 use std::time::Duration;
50
51 #[derive(Clone, Hash, Eq, Debug, PartialEq)]
52 pub enum CallResult {
53 Value(String),
55 Unknown,
57 }
58
59 #[derive(Clone, Hash, Eq, Debug, PartialEq)]
62 pub struct GenericCall {
63 pub call: String,
64 pub args: Vec<String>,
66 pub result: CallResult,
68 }
69
70 #[derive(Clone, Hash, Eq, Debug, PartialEq)]
72 pub enum Call {
73 Generic(GenericCall),
75 Unfinished(String),
77 Resumed(String),
79 Exited(u32),
81 Signalled(String),
83 }
84
85 #[derive(Clone, Hash, Eq, Debug, PartialEq)]
86 pub struct Syscall {
87 pub pid: Option<u32>,
88 pub start: Option<Duration>,
90 pub call: Call,
91 pub stop: Option<Duration>,
94 pub duration: Option<Duration>,
98 }
99
100 impl Syscall {
101 pub fn new(
102 pid: Option<u32>,
103 call: Call,
104 start: Option<Duration>,
105 duration: Option<Duration>,
106 ) -> Self {
107 Syscall {
108 pid,
109 call,
110 start,
111 stop: None,
112 duration,
113 }
114 }
115
116 pub fn set_stop(&mut self) {
117 self.stop = self
118 .start
119 .and_then(|start| self.duration.and_then(|duration| Some(start + duration)));
120 }
121 }
122
123 pub type Line = Result<Syscall>;
124
125 macro_rules! nom {
126 ($expr:expr) => {
127 $expr.map_err(Error::from)
128 };
129 }
130
131 pub mod parsers {
132 use super::{Call, CallResult, Duration, GenericCall, Syscall};
136 use crate::errors::*;
137 use nom::character::complete::digit1;
138 use nom::character::is_space;
139 use nom::{
140 alt, char, complete, delimited, do_parse, escaped, is_a, is_not, map_res, named,
141 none_of, one_of, opt, recognize, separated_list, tag, take_until1, take_while,
142 terminated, tuple, AsChar, InputTakeAtPosition,
143 };
144
145 pub fn symbol1<'a, E: nom::error::ParseError<&'a [u8]>>(
146 input: &'a [u8],
147 ) -> nom::IResult<&'a [u8], &'a [u8], E> {
148 input.split_at_position1_complete(
149 |item| !(item.is_alphanum() || item == b'_'),
150 nom::error::ErrorKind::Alpha,
151 )
152 }
153
154 named!(
155 parse_term,
156 alt!(
157 complete!(recognize!(do_parse!(
158 symbol1
159 >> delimited!(
160 char!('('),
161 separated_list!(tag!(","), parse_arg),
162 char!(')')
163 )
164 >> ()
165 ))) | complete!(recognize!(is_a!(
166 "0123456789_|ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz->*"
167 )))
168 )
169 );
170
171 named!(
172 parse_op,
173 recognize!(alt!(
174 complete!(tag!("&&")) | complete!(tag!("||")) | complete!(tag!("=="))
175 ))
176 );
177
178 named!(
179 parse_expr<&[u8], &[u8]>,
180 alt!(
181 recognize!(complete!(tuple!(
183 parse_term,
184 tag!(" "),
185 parse_op,
186 tag!(" "),
187 parse_term,
188 tag!(" "),
189 parse_op,
190 tag!(" "),
191 parse_term
192 ))) |
193 complete!(parse_term)
194 )
195 );
196
197 named!(
208 parse_arg,
209 do_parse!(
210 opt!(complete!(take_while!(is_space)))
211 >> r: recognize!(do_parse!(
212 opt!(complete!(terminated!(parse_term, tag!("="))))
213 >> opt!(complete!(tag!("&")))
214 >> arg: alt!(
215 complete!(recognize!(do_parse!(
217 opt!(complete!(is_a!("0123456789abcdefx"))) >>
218 alt!(
219 do_parse!(opt!(complete!(tag!(" "))) >>
220 tag!("/* ") >>
221 opt!(complete!(is_a!("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-:+"))) >>
222 opt!(complete!(tag!("???"))) >>
223 opt!(alt!(complete!(tag!(" vars")) | complete!(tag!(" entries")) )) >>
224 tag!(" */") >> ()) |
225 do_parse!(
226 tag!("<") >>
227 escaped!(none_of!(">\\\n"), '\\', one_of!(">\\n")) >>
228 tag!(">")
229 >> ())
230 )
231
232 >> ()
233 ))) |
234 complete!(recognize!(delimited!(char!('['),
236 separated_list!(tag!(","), parse_arg),
237 char!(']')))) |
238 complete!(recognize!(delimited!(char!('['),
240 separated_list!(tag!(" "), parse_arg),
241 char!(']')))) |
242 complete!(recognize!(
244 do_parse!(
245 delimited!(char!('"'),
246 opt!(escaped!(is_not!("\"\\"), '\\', one_of!("\"n\\0123456789vrtxf"))),
247 char!('"')) >>
248 opt!(tag!("...")) >> ()
249 ))) |
250 complete!(tag!("NULL")) |
252 complete!(recognize!(parse_expr)) |
257 complete!(recognize!(delimited!(char!('{'),
259 separated_list!(tag!(","), parse_arg),
260 char!('}')))) |
261 complete!(recognize!(delimited!(char!('{'),
263 separated_list!(tag!(" "), parse_arg),
264 char!('}')))) |
265 complete!(tag!("..."))
267 )
268 >> (arg)
269 ))
270 >> (r)
271 )
272 );
273
274 named!(
278 parse_result<&[u8], CallResult>,
279 alt!(
280 complete!(do_parse!(val: map_res!(recognize!(do_parse!(
281 alt!(tag!("-") | is_a!("?0123456789xabcdef")) >>
282 is_not!(")") >>
283 tag!(")") >>
284 () )), std::str::from_utf8) >> (CallResult::Value(val.into()))))
285 | do_parse!(val: map_res!(is_a!("0123456789xabcdef"), std::str::from_utf8) >> (CallResult::Value(val.into())))
286 | do_parse!(tag!("?") >> (CallResult::Unknown))
287 )
288 );
289
290 named!(
292 parse_exit_event<&[u8], Call>,
293 do_parse!(
294 ret: delimited!(tag!("+++ exited with "),
295 map_res!(
296 map_res!(
297 digit1,
298 std::str::from_utf8
299 ),
300 |s: &str| s.parse::<u32>()
301 ),
302 tag!(" +++")) >>
303 (Call::Exited(ret)))
304 );
305
306 named!(
307 eol,
308 alt!(complete!(tag!("\r\n")) | complete!(tag!("\n")) | complete!(tag!("\r")))
309 );
310
311 named!(
313 parse_resumed<&[u8], Call>,
314 do_parse!(
315 delimited!(tag!("<... "), symbol1, tag!(" resumed>")) >>
316 opt!(complete!(tag!(" "))) >>
317 l: map_res!(
318 recognize!(tuple!(
319 take_until1!("\n"), eol
321 )),
322 std::str::from_utf8) >>
323 (Call::Resumed(l.into())))
324 );
325
326 named!(
329 parse_signalled<&[u8], Call>,
330 delimited!(tag!("--- "),
331 do_parse!(
332 symbol1 >>
333 v: map_res!(parse_arg, std::str::from_utf8) >>
334 (Call::Signalled(v.into()))
335
336 ),tag!(" ---"))
337 );
338
339 named!(
343 pub parse_call<&[u8], Call>,
344 alt!(
345 complete!(do_parse!(e: parse_exit_event >> (e)))
346 | complete!(parse_resumed)
350 | complete!(do_parse!(
351 l: terminated!(
352 map_res!(take_until1!(" <unfinished ...>"),
353 std::str::from_utf8),
354 alt!(complete!(tag!(" <unfinished ...>) = ?")) |
355 complete!(tag!(" <unfinished ...>")))) >>
356 eol >>
357 (Call::Unfinished(l.into())))
358 )
359 | complete!(parse_signalled)
360 | complete!(do_parse!(
361 call: map_res!(is_not!("("), std::str::from_utf8) >>
362 args: delimited!(char!('('),
363 separated_list!(tag!(","), map_res!(parse_arg, std::str::from_utf8)),
364 char!(')')) >>
365 is_a!(" ") >>
366 tag!("= ") >>
367 result: parse_result >>
368 (Call::Generic(GenericCall {call:call.into(), args:args.into_iter().map(|s|s.into()).collect(), result:result.into()})))
369 )
370 )
371 );
372
373 named!(
375 pub parse_duration<&[u8], Option<Duration>>,
376 alt!(
377 complete!(do_parse!(
378 tag!(" ") >>
379 duration:
380 delimited!(char!('<'),
381 do_parse!(
382 s: map_res!(
383 map_res!(
384 digit1, std::str::from_utf8
385 ),
386 |s: &str| s.parse::<u64>()
387 ) >>
388 tag!(".") >>
389 micro: map_res!(
390 map_res!(
391 digit1, std::str::from_utf8
392 ),
393 |s: &str| s.parse::<u64>()
394 ) >>
395 ((s * 1_000_000) + micro)
396 ),
397 char!('>')
398 ) >>
399 ( Some(Duration::from_micros(duration)))
400 ))
401 |do_parse!((None))
402 )
403 );
404
405 named!(
408 pub parse_start<&[u8], Option<Duration>>,
409 opt!(alt!(
410 complete!(do_parse!(
411 hour: map_res!(
412 map_res!(
413 digit1, std::str::from_utf8
414 ),
415 |s: &str| s.parse::<u64>()
416 ) >>
417 tag!(":") >>
418 minute: map_res!(
419 map_res!(
420 digit1, std::str::from_utf8
421 ),
422 |s: &str| s.parse::<u64>()
423 ) >>
424 tag!(":") >>
425 second: map_res!(
426 map_res!(
427 digit1, std::str::from_utf8
428 ),
429 |s: &str| s.parse::<u64>()
430 ) >>
431 tag!(".") >>
432 micros: map_res!(
433 map_res!(
434 digit1, std::str::from_utf8
435 ),
436 |s: &str| s.parse::<u64>()
437 ) >>
438 tag!(" ") >>
439 ( Duration::from_micros(
440 ((hour * 60
441 + minute) * 60
442 + second) * 1_000_000
443 + micros
444 ) )
445 ))
446 |complete!(do_parse!(
447 second: map_res!(
448 map_res!(
449 digit1, std::str::from_utf8
450 ),
451 |s: &str| s.parse::<u64>()
452 ) >>
453 tag!(".") >>
454 micros: map_res!(
455 map_res!(
456 digit1, std::str::from_utf8
457 ),
458 |s: &str| s.parse::<u64>()
459 ) >>
460 tag!(" ") >>
461 ( Duration::from_micros(second * 1_000_000 + micros) )
462 )
463 )
464 )
465 )
466 );
467
468 named!(parse_pid<&[u8], u32>,
470 complete!(map_res!(
471 map_res!(
472 terminated!(digit1, tag!(" ")),
473 std::str::from_utf8
474 ), |s: &str| s.parse::<u32>()
475 ))
476 );
477
478 named!(
479 pub parser<&[u8], Syscall>,
480 do_parse!(pid: opt!(parse_pid) >>
481 opt!(complete!(tag!(" "))) >>
482 start: parse_start >>
483 call: parse_call >>
484 duration: parse_duration >>
485 opt!(complete!(eol)) >>
486 (Syscall::new(pid, call, start, duration)))
487 );
488
489 named!(
490 merge_parser<&[u8], Syscall>,
491 do_parse!(
492 call: parse_call >>
493 duration: parse_duration >>
494 opt!(complete!(eol)) >>
495 (Syscall::new(None, call, None, duration)))
496 );
497
498 pub fn merge_resumed(unfinished: Syscall, resumed: Syscall) -> Result<Syscall> {
504 if unfinished.pid != resumed.pid {
505 return Err("pid mismatch".into());
506 }
507 let prefix = if let Call::Unfinished(prefix) = unfinished.call {
508 prefix
509 } else {
510 return Err("bad call in unfinished".into());
511 };
512 let suffix = if let Call::Resumed(suffix) = resumed.call {
513 suffix
514 } else {
515 return Err("bad call in resumed".into());
516 };
517
518 let line = prefix + &suffix;
519 let (remainder, mut value) = nom!(merge_parser(line.as_bytes()))?;
520 if remainder.len() != 0 {
521 return Err(ErrorKind::NomError(
522 format!(
523 "unused input {:?} from {:?}",
524 std::str::from_utf8(remainder),
525 &line
526 )
527 .into(),
528 )
529 .into());
530 }
531 value.pid = unfinished.pid;
533 value.start = unfinished.start;
534 value.set_stop();
535 Ok(value)
536 }
537
538 #[cfg(test)]
539 mod tests {
540 use super::*;
541 use crate::raw::Syscall;
542
543 fn parse_inputs<F: Fn(&[u8]) -> nom::IResult<&[u8], &[u8]>>(
544 inputs: Vec<&[u8]>,
545 parse: F,
546 ) {
547 for input in inputs.into_iter() {
548 let end = input.len() - 1;
549 let input = &input[..];
550 let expected = &input[1..end];
551 let remainder = &input[end..];
552 let result = parse(input);
553 assert_eq!(result, Ok((remainder, expected)));
554 }
555 }
556
557 #[test]
558 fn parse_arg_int() {
559 let input = &b" 2,"[..];
560
561 let result = parse_arg(input);
562 assert_eq!(result, Ok((&b","[..], &b"2"[..])));
563
564 let inputs: Vec<&[u8]> = vec![
565 b" 2)", b" -1,", b" 8192*1024)", b" 3</etc/ld.so.cache>)", ];
570 parse_inputs(inputs, parse_arg);
571 }
572
573 #[test]
574 fn parse_arg_string() {
575 let inputs: Vec<&[u8]> = vec![
576 b" \"\"1", b" \"A\")", b" \"12\",", b" \"\\33(B\\33[m\")", b" \"aqwe\"...,", b" \"\\x\")", b" \"R9\\203\\200\\0\\1\\0\\t\\0\\f\\0\\0\\6static\\trust-lang\\3or\"...,",
583 b" \"\\v\\0\\22\"...,",
584 ];
585 parse_inputs(inputs, parse_arg);
586 }
587
588 #[test]
589 fn parse_arg_vector() {
590 let inputs: Vec<&[u8]> = vec![
592 b" [],", b" [])", b" [\"a\"],", b" [\"a\", \"\"])", b" [1],", b" [{msg_hdr={msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base=\" l\\1\\0\\0\\1\\0\\0\\0\\0\\0\\0\\6static\\trust-lang\\3or\"..., iov_len=38}], msg_iovlen=1, msg_controllen=0, msg_flags=MSG_TRUNC|MSG_DONTWAIT|MSG_FIN|MSG_SYN|MSG_CONFIRM|MSG_ZEROCOPY|MSG_FASTOPEN|0x10000010}, msg_len=38}, {msg_hdr={msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base=\"R9\\1\\0\\0\\1\\0\\0\\0\\0\\0\\0\\6static\\trust-lang\\3or\"..., iov_len=38}], msg_iovlen=1, msg_controllen=0, msg_flags=MSG_EOR|MSG_WAITALL|MSG_NOSIGNAL|MSG_MORE|MSG_BATCH|MSG_CMSG_CLOEXEC|0x38a0000}, msg_len=38}],",
598 b" [RTMIN RT_1],", b" [28->16],", b" msg_iov(1)=[],", ];
602 parse_inputs(inputs, parse_arg);
603 }
604
605 #[test]
606 fn parse_arg_vars() {
607 let inputs: Vec<&[u8]> = vec![
609 b" 0x7ffff2435d98 /* 19 vars */,", b" 0x7ffff2435d98 /* 19 vars */)", b" 0x14 /* NLMSG_??? */,", b" 1558857830 /* 2019-05-26T20:03:50+1200 */,", b" /* 12 entries */,", ];
615 parse_inputs(inputs, parse_arg);
616 }
617
618 #[allow(non_snake_case)]
619 #[test]
620 fn parse_arg_NULL() {
621 let inputs: Vec<&[u8]> = vec![
623 b" NULL)",
626 ];
627 parse_inputs(inputs, parse_arg);
628 }
629
630 #[test]
631 fn parse_arg_symbols() {
632 let inputs: Vec<&[u8]> = vec![
633 b" F_OK,",
634 b" O_RDONLY|O_CLOEXEC)",
635 b" st_mode=S_IFREG|0644)",
636 b" st_size=36160,",
637 b" echo,",
638 b" &sin6_addr,",
639 b" WIFEXITED(s) && WEXITSTATUS(s) == 0,",
640 ];
641 parse_inputs(inputs, parse_arg);
642 }
643
644 #[test]
645 fn parse_arg_structs() {
646 let inputs: Vec<&[u8]> = vec![
647 b" {st_mode=S_IFREG|0644, st_size=36160, ...},",
648 b" {st_mode=S_IFREG|0644, st_size=36160, ...})",
649 b" {u32=4294967295, u64=18446744073709551615},",
650 ];
651 parse_inputs(inputs, parse_arg);
652 }
653
654 #[test]
655 fn parse_arg_space_separated_structs() {
656 let inputs: Vec<&[u8]> = vec![
657 b" {st_mode=S_IFREG|0644, st_size=36160},",
658 b" {B38400 opost isig icanon echo ...},",
659 ];
660 parse_inputs(inputs, parse_arg);
661 }
662
663 #[test]
664 fn parse_nested_structs() {
665 let inputs: Vec<&[u8]> = vec![
666 b" {EPOLLIN|EPOLLET, {u32=4294967295, u64=18446744073709551615}},",
667 b" {EPOLLIN|EPOLLET, {u32=4294967295, u64=18446744073709551615}})",
668 ];
669 parse_inputs(inputs, parse_arg);
670 }
671
672 #[test]
673 fn parse_fn_calls() {
674 let inputs: Vec<&[u8]> = vec![b" st_rdev=makedev(1, 9),"];
675 parse_inputs(inputs, parse_arg);
676 }
677
678 #[test]
679 fn parse_call_exited() {
680 let input = &b"+++ exited with 0 +++"[..];
681
682 let result = parse_call(input);
683 assert_eq!(result, Ok((&b""[..], Call::Exited(0))));
684 }
685
686 #[test]
687 fn parse_call_signalled() {
688 let input = &b"--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=16135, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---"[..];
689 let result = parse_call(input);
690 assert_eq!(result, Ok((&b""[..], Call::Signalled("{si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=16135, si_uid=1000, si_status=0, si_utime=0, si_stime=0}".into()))));
691 }
692
693 #[test]
694 fn parse_call_write_6() {
695 let input = &b"write(2, \"\\33(B\\33[m\", 6) = 6\n"[..];
696
697 let result = parse_call(input);
698 assert_eq!(
699 result,
700 Ok((
701 &b"\n"[..],
702 Call::Generic(GenericCall {
703 call: "write".into(),
704 args: vec!["2".into(), "\"\\33(B\\33[m\"".into(), "6".into()],
705 result: CallResult::Value("6".into())
706 })
707 ))
708 );
709 }
710
711 #[test]
712 fn parse_call_named_arg() {
713 let input = &b"write(fred_hollows=2) = 6\n"[..];
714
715 let result = parse_call(input);
716 assert_eq!(
717 result,
718 Ok((
719 &b"\n"[..],
720 Call::Generic(GenericCall {
721 call: "write".into(),
722 args: vec!["fred_hollows=2".into()],
723 result: CallResult::Value("6".into())
724 })
725 ))
726 );
727 }
728
729 #[test]
732 fn parse_call_unfinished_resumed() -> Result<()> {
733 let input = &b"epoll_wait(4, <unfinished ...>\n"[..];
734 let result = parse_call(input);
735 assert_eq!(
736 result,
737 Ok((&b""[..], Call::Unfinished("epoll_wait(4, ".into())))
738 );
739 let u = Syscall::new(
740 Some(1),
741 result.unwrap().1,
742 Some(Duration::from_secs(500)),
743 None,
744 );
745
746 let input = &b"<... epoll_wait resumed> [], 1024, 0) = 0 <0.000542>\n"[..];
747 let result = parse_call(input);
748 assert_eq!(
749 result,
750 Ok((
751 &b""[..],
752 Call::Resumed("[], 1024, 0) = 0 <0.000542>\n".into())
753 ))
754 );
755 let r = Syscall::new(Some(1), result.unwrap().1, None, None);
756
757 let result = merge_resumed(u, r)?;
758 assert_eq!(
759 result,
760 Syscall {
761 pid: Some(1),
762 call: Call::Generic(GenericCall {
763 call: "epoll_wait".into(),
764 args: vec!["4".into(), "[]".into(), "1024".into(), "0".into()],
765 result: CallResult::Value("0".into()),
766 }),
767 start: Some(Duration::from_secs(500)),
768 stop: Some(Duration::from_micros(500_000542)),
769 duration: Some(Duration::from_micros(542))
770 }
771 );
772 Ok(())
773 }
774
775 #[test]
777 fn merge_examples() -> Result<()> {
778 let (u, r) = (&b"15873 20:12:49.689201 poll([{fd=3, events=POLLIN}, {fd=10, events=POLLIN}], 2, 9997 <unfinished ...>\n"[..],
779 &b"15873 20:12:50.070613 <... poll resumed> ) = ? ERESTART_RESTARTBLOCK (Interrupted by signal) <0.380816>\n"[..]);
780
781 let u_p = parser(u)?;
782 assert_eq!(&b""[..], u_p.0);
783 let r_p = parser(r)?;
784 assert_eq!(&b""[..], r_p.0);
785
786 merge_resumed(u_p.1, r_p.1)?;
787 Ok(())
788 }
789
790 #[test]
791 fn parse_call_unfinished() {
792 let input = &b"set_robust_list(0x7f1c43b009e0, 24 <unfinished ...>\n"[..];
793 let result = parse_call(input);
794 assert_eq!(
795 result,
796 Ok((
797 &b""[..],
798 Call::Unfinished("set_robust_list(0x7f1c43b009e0, 24".into())
799 ))
800 );
801 }
802
803 #[test]
804 fn test_parse_resumed() {
805 let input = &b"<... epoll_wait resumed> [], 1024, 0) = 0 <0.000542>\n"[..];
806 let result = parse_resumed(input);
807 assert_eq!(
808 result,
809 Ok((
810 &b""[..],
811 Call::Resumed("[], 1024, 0) = 0 <0.000542>\n".into())
812 ))
813 );
814 }
815
816 #[test]
817 fn parse_exited() {
818 let input = &b"+++ exited with 0 +++"[..];
819
820 let result = parse_exit_event(input);
821 assert_eq!(result, Ok((&b""[..], Call::Exited(0))));
822 }
823
824 #[test]
825 fn result_description1() {
826 let inputs: Vec<&[u8]> = vec![
827 b"-1 ENOENT (No such file or directory)",
828 b"? ERESTART_RESTARTBLOCK (Interrupted by signal)",
829 ];
830 for input in inputs.into_iter() {
831 let result = parse_result(input);
832 assert_eq!(
833 result,
834 Ok((
835 &b""[..],
836 CallResult::Value(std::str::from_utf8(input).unwrap().into())
837 ))
838 );
839 }
840 }
841
842 #[test]
843 fn result_description2() {
844 let input = &b"0x1 (flags FD_CLOEXEC)"[..];
845 let result = parse_result(input);
846 assert_eq!(
847 result,
848 Ok((&b""[..], CallResult::Value("0x1 (flags FD_CLOEXEC)".into())))
849 );
850 }
851
852 #[test]
853 fn result_timestamp() {
854 let input = &b"1558857830 (2019-05-26T20:03:50+1200)"[..];
855 let result = parse_result(input);
856 assert_eq!(
857 result,
858 Ok((
859 &b""[..],
860 CallResult::Value("1558857830 (2019-05-26T20:03:50+1200)".into())
861 ))
862 );
863 }
864
865 #[test]
866 fn test_parse_start() {
867 let inputs: Vec<(&[u8], Duration)> =
868 vec![(b"20:03:49.612486 ", Duration::from_micros(72229_612_486))];
869
870 for (input, expected) in inputs.into_iter() {
871 let input = &input[..];
872 let result = parse_start(input);
873 assert_eq!(result, Ok((&b""[..], Some(expected))));
874 }
875 }
876
877 #[test]
878 fn test_parser() {
879 let cases: Vec<(&[u8], Syscall)> = vec![
880 (
881 b"1 set(0) <unfinished ...>\n",
882 Syscall {
883 pid: Some(1),
884 call: Call::Unfinished("set(0)".into()),
885 start: None,
886 stop: None,
887 duration: None,
888 },
889 ),
890 (
891 b"2064 1560918200.817883 <... set_robust_list resumed> ) = 0 <0.000155>\n",
892 Syscall {
893 pid: Some(2064),
894 call: Call::Resumed(") = 0 <0.000155>\n".into()),
895 start: Some(Duration::from_micros(1560918200_817883)),
896 stop: None,
897 duration: None,
898 },
899 ),
900 ];
901
902 for (input, output) in cases.into_iter() {
903 let input = &input[..];
904 let result = parser(input);
905 assert_eq!(result, Ok((&b""[..], output)));
906 }
907 }
908 }
909 }
910
911 type Pool = std::cell::Cell<threadpool::ThreadPool>;
912 type SendHandle = Sender<(u64, Line)>;
913 type RecvHandle = Receiver<(u64, Line)>;
914
915 struct ParseLines<T: BufRead> {
916 lines: T,
917 finished_reading: bool,
918 last_start: Option<Duration>,
919 start_offset: Duration,
920 threadpool: Pool,
921 serial: u64,
922 next_yield: u64,
923 send: SendHandle,
924 recv: RecvHandle,
925 pending_heap: BinaryHeap<Reverse<u64>>,
926 pending_lines: HashMap<u64, Line>,
927 }
928
929 impl<T: BufRead> ParseLines<T> {
930 fn adjust_line(&mut self, parsed: Line) -> Option<Line> {
931 self.next_yield += 1;
932 return match parsed {
933 Ok(mut value) => {
934 match value.start {
936 None => (),
937 Some(start) => {
938 if start < Duration::from_secs(86402) {
939 match self.last_start {
940 None => {}
941 Some(last_start) => {
942 if last_start > start {
943 self.start_offset += Duration::from_secs(86400);
946 }
947 }
948 }
949 self.last_start = value.start;
950 }
951 value.start = value.start.map(|start| start + self.start_offset);
952 value.set_stop();
953 }
954 }
955 Some(Ok(value))
956 }
957 Err(e) => Some(Err(e)),
958 };
959 }
960
961 fn finish_reading(&mut self) {
962 self.finished_reading = true;
963 let (send, _recv) = channel();
967 std::mem::replace(&mut self.send, send);
968 }
969
970 fn dispatch_line(&mut self) -> Result<()> {
971 use parsers::*;
972 let mut line = String::new();
973 let len = self.lines.read_line(&mut line);
976 match len {
977 Ok(len) => {
978 if len == 0 {
979 self.finish_reading();
980 return Ok(());
981 }
982 }
983 Err(e) => {
984 self.finish_reading();
985 return Err(e.into());
986 }
987 }
988 let id = self.serial;
989 self.serial += 1;
990 let send = self.send.clone();
991 self.threadpool.get_mut().execute(move || {
992 let line = line.as_bytes();
993 let parsed = nom!(parser(&line));
994 let result = match parsed {
995 Ok((remainder, value)) => {
996 if remainder.len() != 0 {
997 Err(ErrorKind::NomError(
998 format!("unused input {:?}", std::str::from_utf8(remainder)).into(),
999 )
1000 .into())
1001 } else {
1002 Ok(value)
1003 }
1004 }
1005 Err(e) => {
1006 println!("Failed {:?} {:?}", std::str::from_utf8(line).unwrap(), e);
1007 Err(e)
1008 }
1009 };
1010 send.send((id, result)).expect("receiver must not quit");
1011 });
1012 Ok(())
1013 }
1014
1015 fn dispatch_lines(&mut self) -> Option<Option<Line>> {
1016 for _ in 0..self.threadpool.get_mut().max_count() {
1017 if !self.finished_reading {
1018 if let Err(e) = self.dispatch_line() {
1019 return Some(Some(Err(e)));
1020 }
1021 }
1022 }
1023 None
1024 }
1025 }
1026
1027 impl<T: BufRead> Iterator for ParseLines<T> {
1028 type Item = Line;
1029
1030 fn next(&mut self) -> Option<Line> {
1031 if let Some(e) = self.dispatch_lines() {
1042 return e;
1043 }
1044
1045 if let Some(available) = self.pending_heap.peek() {
1046 if available.0 == self.next_yield {
1047 self.pending_heap.pop();
1048 let parsed = self.pending_lines.remove(&self.next_yield).unwrap();
1049 return self.adjust_line(parsed);
1050 }
1051 }
1052
1053 loop {
1054 for (id, parsed) in self.recv.try_iter() {
1056 if id != self.next_yield {
1057 self.pending_heap.push(Reverse(id));
1059 self.pending_lines.insert(id, parsed);
1060 continue;
1061 }
1062 return self.adjust_line(parsed);
1063 }
1064 if let Some(e) = self.dispatch_lines() {
1067 return e;
1068 }
1069 if self.finished_reading {
1070 for (id, parsed) in self.recv.iter() {
1073 if id != self.next_yield {
1074 self.pending_heap.push(Reverse(id));
1076 self.pending_lines.insert(id, parsed);
1077 continue;
1078 }
1079 return self.adjust_line(parsed);
1080 }
1081 return None;
1083 }
1084 }
1085 }
1086 }
1087
1088 pub fn parse<T: Read>(source: T) -> impl Iterator<Item = Line> {
1089 let pool = Cell::new(threadpool::ThreadPool::default());
1090 let (send, recv) = channel();
1091 ParseLines {
1092 lines: BufReader::new(source),
1093 finished_reading: false,
1094 last_start: None,
1095 start_offset: Duration::from_secs(0),
1096 threadpool: pool,
1097 serial: 0,
1098 next_yield: 0,
1099 send,
1100 recv,
1101 pending_heap: BinaryHeap::default(),
1102 pending_lines: HashMap::default(),
1103 }
1104 }
1105
1106 pub fn parse_with_pool<T: Read>(source: T, threadpool: Pool) -> impl Iterator<Item = Line> {
1107 let (send, recv) = channel();
1108 ParseLines {
1109 lines: BufReader::new(source),
1110 finished_reading: false,
1111 last_start: None,
1112 start_offset: Duration::from_secs(0),
1113 threadpool,
1114 serial: 0,
1115 next_yield: 0,
1116 send,
1117 recv,
1118 pending_heap: BinaryHeap::default(),
1119 pending_lines: HashMap::default(),
1120 }
1121 }
1122}
1123
1124pub mod structure {
1125 use crate::raw::parsers::merge_resumed;
1126 use crate::raw::{Call, Line, Syscall};
1127
1128 use std::collections::HashMap;
1129
1130 struct MergeUnfinished<T: Iterator<Item = Line>> {
1131 input: T,
1133 unfinished: HashMap<Option<u32>, Syscall>,
1135 next: Option<Syscall>,
1138 }
1139
1140 impl<T: Iterator<Item = Line>> MergeUnfinished<T> {
1141 fn find_one(&self) -> Option<Option<u32>> {
1142 let residue: Vec<(&Option<u32>, &Syscall)> = self.unfinished.iter().take(1).collect();
1143 for (k, _) in residue.into_iter() {
1144 return Some(*k);
1145 }
1146 None
1147 }
1148 }
1149
1150 impl<T: Iterator<Item = Line>> Iterator for MergeUnfinished<T> {
1151 type Item = Line;
1152
1153 fn next(&mut self) -> Option<Self::Item> {
1154 loop {
1155 if self.next.is_some() {
1156 let next = std::mem::replace(&mut self.next, None);
1157 return next.and_then(|next| Some(Ok(next)));
1158 }
1159 match self.input.next() {
1160 None => {
1161 for k in self.find_one() {
1162 let syscall = self.unfinished.remove(&k).unwrap();
1164 return Some(Ok(syscall));
1165 }
1166 return None;
1167 }
1168 Some(item) => {
1169 match item {
1170 Err(e) => return Some(Err(e)),
1171 Ok(mut syscall) => {
1172 match &syscall.call {
1173 Call::Unfinished(_) => {
1174 if let Some(oldcall) =
1175 self.unfinished.insert(syscall.pid, syscall)
1176 {
1177 panic!(
1178 "double unfinished rainbow {:?} {:?}",
1179 &oldcall,
1180 self.unfinished.get(&oldcall.pid).unwrap()
1181 );
1182 }
1183 continue;
1184 }
1185 Call::Resumed(_) => {
1186 match self.unfinished.remove(&syscall.pid) {
1187 None => {
1188 return Some(Ok(syscall));
1191 }
1192 Some(unfinished) => {
1193 return Some(merge_resumed(unfinished, syscall));
1194 }
1195 }
1196 }
1197 _ => {
1198 if self.unfinished.contains_key(&syscall.pid) {
1199 let result =
1205 self.unfinished.remove(&syscall.pid).unwrap();
1206 self.next = Some(syscall);
1207 syscall = result;
1208 }
1209 return Some(Ok(syscall));
1210 }
1211 }
1212 }
1213 }
1214 }
1215 }
1216 }
1217 }
1218 }
1219
1220 pub fn iter_finished<T: Iterator<Item = Line>>(input: T) -> impl Iterator<Item = Line> {
1221 Box::new(MergeUnfinished {
1222 input,
1223 unfinished: HashMap::default(),
1224 next: None,
1225 })
1226 }
1227
1228 #[cfg(test)]
1229 mod tests {
1230 use crate::raw;
1231 use crate::raw::{Call, CallResult, GenericCall, Syscall};
1232
1233 use std::time::Duration;
1234
1235 use super::iter_finished;
1236
1237 #[test]
1238 fn test_iter_finished_syscalls() {
1239 let strace_content = r#"15873 20:12:50.110067 futex(0x7ffff644d58c, FUTEX_WAIT_PRIVATE, 0, {tv_sec=29, tv_nsec=996182600} <unfinished ...>
124015874 20:12:50.111078 futex(0x7f1c3c0e4df8, FUTEX_WAKE_PRIVATE, 1) = 1 <0.000037>
124115876 20:12:50.111684 <... futex resumed> ) = 0 <540.139763>
124215874 20:12:50.112197 clock_gettime(CLOCK_MONOTONIC, <unfinished ...>
124315876 20:12:50.112686 futex(0x7f1c3c014940, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
124415874 20:12:50.113193 <... clock_gettime resumed> {tv_sec=343451, tv_nsec=974375300}) = 0 <0.000527>
124515876 20:12:50.113692 <... futex resumed> ) = 0 <0.000514>
124615874 20:12:50.114199 clock_gettime(CLOCK_MONOTONIC, <unfinished ...>
124715876 20:12:50.114686 futex(0x7f1c3c0143f0, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
124815874 20:12:50.115252 <... clock_gettime resumed> {tv_sec=343451, tv_nsec=976375200}) = 0 <0.000545>
124915876 20:12:50.115759 <... futex resumed> ) = 1 <0.000559>
125015879 20:12:51.065041 epoll_wait(4, <unfinished ...>
125115879 20:12:51.109551 <... epoll_wait resumed> <unfinished ...>) = ?
125215879 20:12:51.110377 +++ exited with 0 +++
12532064 1560918478.324920 epoll_wait(5, <unfinished ...>
12542064 1560918478.352466 +++ exited with 0 +++
1255"#.as_bytes();
1256 let intermediate = raw::parse(strace_content);
1257 let expected: Vec<raw::Line> = vec![
1258 Ok(Syscall {
1259 pid: Some(15874),
1260 call: Call::Generic(GenericCall {
1261 call:"futex".into(),
1262 args:vec!["0x7f1c3c0e4df8".into(), "FUTEX_WAKE_PRIVATE".into(), "1".into()],
1263 result:CallResult::Value("1".into())
1264 }),
1265 start: Some(Duration::from_micros(72770_111078)),
1266 stop: Some(Duration::from_micros(72770_111115)),
1267 duration: Some(Duration::from_micros(37)),
1268 }),
1269 Ok(Syscall {
1270 pid: Some(15876),
1271 call: Call::Resumed(") = 0 <540.139763>\n".into()),
1272 start: Some(Duration::from_micros(72770_111684)),
1273 stop: None,
1274 duration: None,
1275 }),
1276 Ok(Syscall {
1277 pid: Some(15874),
1278 call: Call::Generic(GenericCall {
1279 call:"clock_gettime".into(),
1280 args:vec!["CLOCK_MONOTONIC".into(), "{tv_sec=343451, tv_nsec=974375300}".into()],
1281 result:CallResult::Value("0".into())
1282 }),
1283 start: Some(Duration::from_micros(72770_112197)),
1284 stop: Some(Duration::from_micros(72770_112724)),
1285 duration: Some(Duration::from_micros(527)),
1286 }),
1287 Ok(Syscall {
1288 pid: Some(15876),
1289 call: Call::Generic(GenericCall { call: "futex".into(),
1290 args: vec!["0x7f1c3c014940".into(), "FUTEX_WAKE_PRIVATE".into(), "1".into()], result: CallResult::Value("0".into()) }),
1291 start: Some(Duration::from_micros(72770_112686)),
1292 stop: Some(Duration::from_micros(72770_113200)),
1293 duration: Some(Duration::from_micros(514)),
1294 }),
1295 Ok(Syscall {
1296 pid: Some(15874),
1297 call: Call::Generic(GenericCall { call: "clock_gettime".into(), args: vec!["CLOCK_MONOTONIC".into(), "{tv_sec=343451, tv_nsec=976375200}".into()], result: CallResult::Value("0".into()) }),
1298 start: Some(Duration::from_micros(72770_114199)),
1299 stop: Some(Duration::from_micros(72770_114744)),
1300 duration: Some(Duration::from_micros(545)),
1301 }),
1302 Ok(Syscall {
1303 pid: Some(15876),
1304 call: Call::Generic(GenericCall { call: "futex".into(), args: vec!["0x7f1c3c0143f0".into(), "FUTEX_WAKE_PRIVATE".into(), "1".into()], result: CallResult::Value("1".into()) }),
1305 start: Some(Duration::from_micros(72770_114686)),
1306 stop: Some(Duration::from_micros(72770_115245)),
1307 duration: Some(Duration::from_micros(559)),
1308 }),
1309 Ok(Syscall {
1310 pid: Some(15879),
1311 call: Call::Unfinished("epoll_wait(4,".into()),
1312 start: Some(Duration::from_micros(72771_065041)),
1313 stop: None,
1314 duration: None,
1315 }),
1316 Ok(Syscall {
1317 pid: Some(15879),
1318 call: Call::Exited(0),
1319 start: Some(Duration::from_micros(72771_110377)),
1320 stop: None,
1321 duration: None,
1322 }),
1323 Ok(Syscall {
1324 pid: Some(2064),
1325 call: Call::Unfinished("epoll_wait(5, ".into()),
1326 start: Some(Duration::from_micros(1560918478_324920)),
1327 stop: None,
1328 duration: None,
1329 }),
1330 Ok(Syscall {
1331 pid: Some(2064),
1332 call: Call::Exited(0),
1333 start: Some(Duration::from_micros(1560918478_352466)),
1334 stop: None,
1335 duration: None,
1336 }),
1337 Ok(Syscall {
1338 pid: Some(15873),
1339 call: Call::Unfinished("futex(0x7ffff644d58c, FUTEX_WAIT_PRIVATE, 0, {tv_sec=29, tv_nsec=996182600}".into()),
1340 start: Some(Duration::from_micros(72770_110067)),
1341 stop: None,
1342 duration: None,
1343 }),
1344 ];
1345 let parsed: Vec<raw::Line> = iter_finished(intermediate).collect();
1346 assert_eq!(parsed.len(), expected.len());
1347 for (l, r) in parsed.into_iter().zip(expected.into_iter()) {
1348 assert_eq!(l.unwrap(), r.unwrap());
1349 }
1350 }
1351 }
1352}
1353
1354#[cfg(test)]
1355mod tests {
1356 use crate::raw::{Call, CallResult, GenericCall, Syscall};
1357 use std::time::Duration;
1358
1359 #[test]
1360 fn nopid() {
1361 let strace_content = r#"execve("./target/debug/rustup", ["./target/debug/rustup", "uninstall", "nightly"], 0x7fffedc2f180 /* 20 vars */) = 0 <0.005029>
1362"#.as_bytes();
1363 let parsed: Vec<super::raw::Line> = super::raw::parse(strace_content).collect();
1364 let expected: Vec<super::raw::Line> = vec![Ok(Syscall {
1365 pid: None,
1366 call: Call::Generic(GenericCall {
1367 call: "execve".into(),
1368 args: vec![
1369 r#""./target/debug/rustup""#.into(),
1370 r#"["./target/debug/rustup", "uninstall", "nightly"]"#.into(),
1371 "0x7fffedc2f180 /* 20 vars */".into(),
1372 ],
1373 result: CallResult::Value("0".into()),
1374 }),
1375 start: None,
1376 stop: None,
1377 duration: Some(Duration::from_micros(5029)),
1378 })];
1379 assert_eq!(parsed.len(), expected.len());
1380 for (l, r) in parsed.into_iter().zip(expected.into_iter()) {
1381 assert_eq!(l.unwrap(), r.unwrap());
1382 }
1383 }
1384
1385 #[test]
1386 fn durationonly() {
1387 let strace_content = r#"15860 execve("./target/debug/rustup", ["./target/debug/rustup", "uninstall", "nightly"], 0x7fffedc2f180 /* 20 vars */) = 0 <0.005029>
138815860 brk(NULL) = 0x7fffc415f000 <0.000034>
138915860 exit_group(0) = ?
139015860 +++ exited with 0 +++
1391"#.as_bytes();
1392 let parsed: Vec<super::raw::Line> = super::raw::parse(strace_content).collect();
1393 let expected: Vec<super::raw::Line> = vec![
1394 Ok(Syscall {
1395 pid: Some(15860),
1396 call: Call::Generic(GenericCall {
1397 call: "execve".into(),
1398 args: vec![
1399 r#""./target/debug/rustup""#.into(),
1400 r#"["./target/debug/rustup", "uninstall", "nightly"]"#.into(),
1401 "0x7fffedc2f180 /* 20 vars */".into(),
1402 ],
1403 result: CallResult::Value("0".into()),
1404 }),
1405 start: None,
1406 stop: None,
1407 duration: Some(Duration::from_micros(5029)),
1408 }),
1409 Ok(Syscall {
1410 pid: Some(15860),
1411 call: Call::Generic(GenericCall {
1412 call: "brk".into(),
1413 args: vec!["NULL".into()],
1414 result: CallResult::Value("0x7fffc415f000".into()),
1415 }),
1416 start: None,
1417 stop: None,
1418 duration: Some(Duration::from_micros(34)),
1419 }),
1420 Ok(Syscall {
1421 pid: Some(15860),
1422 call: Call::Generic(GenericCall {
1423 call: "exit_group".into(),
1424 args: vec!["0".into()],
1425 result: CallResult::Unknown,
1426 }),
1427 start: None,
1428 stop: None,
1429 duration: None,
1430 }),
1431 Ok(Syscall {
1432 pid: Some(15860),
1433 call: Call::Exited(0),
1434 start: None,
1435 stop: None,
1436 duration: None,
1437 }),
1438 ];
1439 assert_eq!(parsed.len(), expected.len());
1440 for (l, r) in parsed.into_iter().zip(expected.into_iter()) {
1441 assert_eq!(l.unwrap(), r.unwrap());
1442 }
1443 }
1444
1445 #[test]
1446 fn datemicroandduration() {
1447 let strace_content = r#"15873 20:03:49.395909 execve("./target/debug/rustup", ["./target/debug/rustup", "install", "nightly"], 0x7ffff2435d98 /* 19 vars */) = 0 <0.008768>
144815873 20:03:49.406540 brk(NULL) = 0x7ffff644d000 <0.000058>
144916135 20:12:50.069441 exit_group(0) = ?
145016135 20:12:50.070014 +++ exited with 0 +++
1451"#.as_bytes();
1452 let parsed: Vec<super::raw::Line> = super::raw::parse(strace_content).collect();
1453 let expected: Vec<super::raw::Line> = vec![
1454 Ok(Syscall {
1455 pid: Some(15873),
1456 call: Call::Generic(GenericCall {
1457 call: "execve".into(),
1458 args: vec![
1459 r#""./target/debug/rustup""#.into(),
1460 r#"["./target/debug/rustup", "install", "nightly"]"#.into(),
1461 "0x7ffff2435d98 /* 19 vars */".into(),
1462 ],
1463 result: CallResult::Value("0".into()),
1464 }),
1465 start: Some(Duration::from_micros(
1466 49_395909 + (((20 * 60) + 3) * 60 * 1_000000),
1467 )),
1468 stop: Some(Duration::from_micros(
1469 49_395909 + (((20 * 60) + 3) * 60 * 1_000000) + 8768,
1470 )),
1471 duration: Some(Duration::from_micros(8768)),
1472 }),
1473 Ok(Syscall {
1474 pid: Some(15873),
1475 call: Call::Generic(GenericCall {
1476 call: "brk".into(),
1477 args: vec!["NULL".into()],
1478 result: CallResult::Value("0x7ffff644d000".into()),
1479 }),
1480 start: Some(Duration::from_micros(
1481 49_406540 + (((20 * 60) + 3) * 60 * 1_000000),
1482 )),
1483 stop: Some(Duration::from_micros(
1484 49_406540 + (((20 * 60) + 3) * 60 * 1_000000) + 58,
1485 )),
1486 duration: Some(Duration::from_micros(58)),
1487 }),
1488 Ok(Syscall {
1489 pid: Some(16135),
1490 call: Call::Generic(GenericCall {
1491 call: "exit_group".into(),
1492 args: vec!["0".into()],
1493 result: CallResult::Unknown,
1494 }),
1495 start: Some(Duration::from_micros(
1496 50_069441 + (((20 * 60) + 12) * 60 * 1_000000),
1497 )),
1498 stop: None,
1499 duration: None,
1500 }),
1501 Ok(Syscall {
1502 pid: Some(16135),
1503 call: Call::Exited(0),
1504 start: Some(Duration::from_micros(
1505 50_070014 + (((20 * 60) + 12) * 60 * 1_000000),
1506 )),
1507 stop: None,
1508 duration: None,
1509 }),
1510 ];
1511 assert_eq!(parsed.len(), expected.len());
1512 for (l, r) in parsed.into_iter().zip(expected.into_iter()) {
1513 assert_eq!(l.unwrap(), r.unwrap());
1514 }
1515 }
1516
1517 #[test]
1518 fn datemicrorollover() {
1519 let strace_content = r#"1 23:59:59.000000 exit_group(0) = ?
15202 0:0:0.000001 +++ exited with 0 +++ <0.000058>
1521"#
1522 .as_bytes();
1523 let parsed: Vec<super::raw::Line> = super::raw::parse(strace_content).collect();
1524 let expected: Vec<super::raw::Line> = vec![
1525 Ok(Syscall {
1526 pid: Some(1),
1527 call: Call::Generic(GenericCall {
1528 call: "exit_group".into(),
1529 args: vec!["0".into()],
1530 result: CallResult::Unknown,
1531 }),
1532 start: Some(Duration::from_micros(
1533 59_000000 + (((23 * 60) + 59) * 60 * 1_000000),
1534 )),
1535 stop: None,
1536 duration: None,
1537 }),
1538 Ok(Syscall {
1539 pid: Some(2),
1540 call: Call::Exited(0),
1541 start: Some(Duration::from_micros(
1542 00_000001 + (((24 * 60) + 0) * 60 * 1_000000),
1543 )),
1544 stop: Some(Duration::from_micros(
1545 00_000001 + (((24 * 60) + 0) * 60 * 1_000000) + 58,
1546 )),
1547 duration: Some(Duration::from_micros(58)),
1548 }),
1549 ];
1550 assert_eq!(parsed.len(), expected.len());
1551 for (l, r) in parsed.into_iter().zip(expected.into_iter()) {
1552 assert_eq!(l.unwrap(), r.unwrap());
1553 }
1554 }
1555
1556 #[test]
1557 fn absmicros() {
1558 let strace_content = r#"1 1560417690.065275 exit_group(0) = ?
15592 1560417690.082832 +++ exited with 0 +++ <0.000058>
1560"#
1561 .as_bytes();
1562 let parsed: Vec<super::raw::Line> = super::raw::parse(strace_content).collect();
1563 let expected: Vec<super::raw::Line> = vec![
1564 Ok(Syscall {
1565 pid: Some(1),
1566 call: Call::Generic(GenericCall {
1567 call: "exit_group".into(),
1568 args: vec!["0".into()],
1569 result: CallResult::Unknown,
1570 }),
1571 start: Some(Duration::from_micros(1560417690_065275)),
1572 stop: None,
1573 duration: None,
1574 }),
1575 Ok(Syscall {
1576 pid: Some(2),
1577 call: Call::Exited(0),
1578 start: Some(Duration::from_micros(1560417690_082832)),
1579 stop: Some(Duration::from_micros(1560417690_082832 + 58)),
1580 duration: Some(Duration::from_micros(58)),
1581 }),
1582 ];
1583 assert_eq!(parsed.len(), expected.len());
1584 for (l, r) in parsed.into_iter().zip(expected.into_iter()) {
1585 assert_eq!(l.unwrap(), r.unwrap())
1586 }
1587 }
1588}