strace_parse/
lib.rs

1// Copyright (c) 2019 Robert Collins. Licensed under the Apache-2.0 license.
2
3#![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    // Create the Error, ErrorKind, ResultExt, and Result types
14    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    // Inspired by https://github.com/Geal/nom/issues/581
27    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        // TODO: convert to u64/pointer width ints perhaps?
54        Value(String),
55        /// ?
56        Unknown,
57    }
58
59    /// A generic unmodelled syscall:
60    /// any name, with any args of any type and any result of any type
61    #[derive(Clone, Hash, Eq, Debug, PartialEq)]
62    pub struct GenericCall {
63        pub call: String,
64        /// Not currently modelled, but per-call-type
65        pub args: Vec<String>,
66        /// Not currently modelled, but per-call-type
67        pub result: CallResult,
68    }
69
70    /// The syscall or action that happened.
71    #[derive(Clone, Hash, Eq, Debug, PartialEq)]
72    pub enum Call {
73        /// A generic unmodelled syscall was made
74        Generic(GenericCall),
75        /// An incomplete call
76        Unfinished(String),
77        /// A resumed call
78        Resumed(String),
79        /// The process exited
80        Exited(u32),
81        /// The process was signalled
82        Signalled(String),
83    }
84
85    #[derive(Clone, Hash, Eq, Debug, PartialEq)]
86    pub struct Syscall {
87        pub pid: Option<u32>,
88        /// When the system call started, if known.
89        pub start: Option<Duration>,
90        pub call: Call,
91        /// When the system call finished_reading, if known. (always inferred from
92        /// start+ duration).
93        pub stop: Option<Duration>,
94        /// Duration of the system call, if known. Note that duration may be known
95        /// without knowing the start and stop as in the following example:
96        /// ```2 execve("foo", ["bar"], 0x7fffedc2f180 /* 20 vars */) = 0 <0.005029>```
97        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        /// The use of nom is not part of the contract of strace-parse; this is
133        /// solely public to permit the implementation to be well separated
134        /// internally.
135        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                // todo: make recursive for arbitrary length exprs
182                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        // Parse a single arg:
198        // '6' | '"string\""' | [vector, of things] |
199        // '0x1234213 /* 19 vars */ | NULL | F_OK |
200        // 3</path>
201        // {..., ...}
202        // {arg}
203        // foo && bar
204        // foo || bar
205        // foo == bar
206        // foo(1)=[...]
207        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                                // Commented hex
216                                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                        // It might be a vector ["foo", "bar", arg]
235                        complete!(recognize!(delimited!(char!('['),
236                                separated_list!(tag!(","), parse_arg),
237                                char!(']')))) |
238                        // It might be a vector ["foo" "bar" arg]
239                        complete!(recognize!(delimited!(char!('['),
240                                separated_list!(tag!(" "), parse_arg),
241                                char!(']')))) |
242                        // It might be a string "blah"
243                        complete!(recognize!(
244                            do_parse!(
245                                delimited!(char!('"'),
246                                opt!(escaped!(is_not!("\"\\"), '\\', one_of!("\"n\\0123456789vrtxf"))),
247                                char!('"')) >>
248                                opt!(tag!("...")) >> ()
249                                ))) |
250                        // literal NULL
251                        complete!(tag!("NULL")) |
252                        // It might be a fn(arg)
253                        // a symbolic constant or simple number or mapping or
254                        // expresions...? (12->34)
255                        // argh really need to rework this hole thing bottom up.
256                        complete!(recognize!(parse_expr)) |
257                        // It might be a struct {"foo", "bar"}
258                        complete!(recognize!(delimited!(char!('{'),
259                                separated_list!(tag!(","), parse_arg),
260                                char!('}')))) |
261                        // It might be a struct {foo bar}
262                        complete!(recognize!(delimited!(char!('{'),
263                                separated_list!(tag!(" "), parse_arg),
264                                char!('}')))) |
265                        // ellipsis
266                        complete!(tag!("..."))
267                            )
268                            >> (arg)
269                    ))
270                    >> (r)
271            )
272        );
273
274        // ? or -(number or hex) then comments.Duration
275        // currently Unknown is only for standalone ?, ? with comment
276        // goes into Value; as results are not fully modelled. Ugh.
277        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        // Parse the +++ exited with 0 +++ case
291        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        //
312        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"), // TODO: handle other EOL forms
320                                eol
321                            )),
322                            std::str::from_utf8) >>
323                        (Call::Resumed(l.into())))
324        );
325
326        // Signall events
327        // --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=16135, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
328        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        // The sys call fn(...) = xxx
340        // Modelled entirely to avoid guessing: either we recognise it or we
341        // do not.
342        named!(
343                pub parse_call<&[u8], Call>,
344                alt!(
345                    complete!(do_parse!(e: parse_exit_event >> (e)))
346                    // <... epoll_wait resumed> ....
347                    // must be before unfinished because of:
348                    // 15874 20:12:51.109551 <... epoll_wait resumed> <unfinished ...>) = ?
349                    | 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        // The duration ' <...>'
374        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        // The start 20:03:49.406540
406        //           20:03:49.612486
407        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        // The pid "1234 "
469        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        /// Merge an unfinished and a resumed syscall.
499        /// The syscalls are needed to get the timing data, but the
500        /// knowledge of type from the union is embedded, so it can error :/.
501        /// If you have ideas on making this nicer let me know.
502        /// TODO? move into the higher layer module?
503        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            // Take the start and pid from the original call -
532            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)",                   // end of args
566                    b" -1,",                  // not end of args, negatives
567                    b" 8192*1024)",           // Multiplication ?
568                    b" 3</etc/ld.so.cache>)", // File descriptor
569                ];
570                parse_inputs(inputs, parse_arg);
571            }
572
573            #[test]
574            fn parse_arg_string() {
575                let inputs: Vec<&[u8]> = vec![
576                    b" \"\"1",             // Empty
577                    b" \"A\")",            // simple alpha
578                    b" \"12\",",           // simple number
579                    b" \"\\33(B\\33[m\")", // "\33(B\33[m"
580                    b" \"aqwe\"...,",      // truncated
581                    b" \"\\x\")",          // \x - utf8 escape char.
582                    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                // ["./target/debug/rustup", "install", "nightly"],
591                let inputs: Vec<&[u8]> = vec![
592                    b" [],",            // Empty
593                    b" [])",            // Empty
594                    b" [\"a\"],",       // 1 element
595                    b" [\"a\", \"\"])", // 2 element2
596                    b" [1],",           // number elements
597                    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],", // space delimited
599                    b" [28->16],", // Mappings?
600                    b" msg_iov(1)=[],", // named vector
601                ];
602                parse_inputs(inputs, parse_arg);
603            }
604
605            #[test]
606            fn parse_arg_vars() {
607                // 0x7ffff2435d98 /* 19 vars */
608                let inputs: Vec<&[u8]> = vec![
609                    b" 0x7ffff2435d98 /* 19 vars */,",              // not-last
610                    b" 0x7ffff2435d98 /* 19 vars */)",              // last arg
611                    b" 0x14 /* NLMSG_??? */,",                      // Enum comment
612                    b" 1558857830 /* 2019-05-26T20:03:50+1200 */,", // datestamp comment
613                    b" /* 12 entries */,",                          // pure comment
614                ];
615                parse_inputs(inputs, parse_arg);
616            }
617
618            #[allow(non_snake_case)]
619            #[test]
620            fn parse_arg_NULL() {
621                // NULL
622                let inputs: Vec<&[u8]> = vec![
623                    // The leading ' ' is weird, but the parser has that outside
624                    // the opt and this lets us use the test helper. shrug.
625                    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            // epoll_wait(4,  <unfinished ...>
730            // <... epoll_wait resumed> [], 1024, 0) = 0 <0.000542>
731            #[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            //
776            #[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                    //println!("parsed: {:?}", value);
935                    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                                            // rollover
944                                            // increment our offset by a day
945                                            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            // bit ugly but gets a replacement handle to make
964            // the current one close (and let blocking iteration
965            // terminate correctly)
966            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            // TODO: switch to read_until to deal with non-UTF8 strings in
974            // the strace.
975            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            // Each line can be parsed separately.
1032            // next() thus does the following:
1033            // 0) try to pick up *a* line and submit it
1034            // 1) look for the next result in the completed cache
1035            // 2) alternate between iterating completed values
1036            //    looking for the next result
1037            // 3) when there is no more input, close the
1038            //    mpsc input handle to permit blocking reads
1039            // This isn't free-threading the reads to avoid just bloating all
1040            // memory all at once; that would be a possible future change.
1041            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                // perhaps the line we want is waiting in the mpsc queue
1055                for (id, parsed) in self.recv.try_iter() {
1056                    if id != self.next_yield {
1057                        // stash this line for later processing
1058                        self.pending_heap.push(Reverse(id));
1059                        self.pending_lines.insert(id, parsed);
1060                        continue;
1061                    }
1062                    return self.adjust_line(parsed);
1063                }
1064                // but if not, lets send another line for processing, todo: give
1065                // up CPU when the queue grows (e.g. single core machines)
1066                if let Some(e) = self.dispatch_lines() {
1067                    return e;
1068                }
1069                if self.finished_reading {
1070                    // We've finished reading, so there is nothing to do but
1071                    // wait for the line we want.
1072                    for (id, parsed) in self.recv.iter() {
1073                        if id != self.next_yield {
1074                            // stash this line for later processing
1075                            self.pending_heap.push(Reverse(id));
1076                            self.pending_lines.insert(id, parsed);
1077                            continue;
1078                        }
1079                        return self.adjust_line(parsed);
1080                    }
1081                    // And if we reach here there are no more lines.
1082                    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        // Underlying iterator
1132        input: T,
1133        // unfinished syscalls
1134        unfinished: HashMap<Option<u32>, Syscall>,
1135        // finished but yet to be iterated syscall
1136        // used when a call evicts an unfinished call.
1137        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                            // Remaining unfinished syscalls - forward as-is
1163                            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                                                // resume without unfinished -
1189                                                // forward as-is
1190                                                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                                            // The old event is something like
1200                                            // poll(), the new event will be
1201                                            // something like a group exit.
1202                                            // Forward the old event, stash the
1203                                            // new one for the next next() call.
1204                                            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}