1
- extern crate clap;
2
- extern crate libc;
3
- extern crate ref_slice;
1
+ #![ deny( warnings) ]
4
2
3
+ extern crate chrono;
4
+ extern crate clap;
5
+ extern crate env_logger;
6
+ extern crate itm;
5
7
#[ macro_use]
6
- extern crate error_chain;
7
-
8
- use std:: io:: { Read , Write } ;
9
- use std:: path:: PathBuf ;
10
- use std:: time:: Duration ;
11
- use std:: { env, fs, io, process, thread} ;
8
+ extern crate log;
12
9
13
- #[ cfg( not( unix) ) ]
14
- use std:: fs:: OpenOptions ;
15
-
16
- #[ cfg( unix) ]
17
- use std:: ffi:: CString ;
18
- #[ cfg( unix) ]
10
+ use clap:: { Arg , App , ArgMatches } ;
11
+ use itm:: { packet, Decoder } ;
12
+ use itm:: error:: { Error , ErrorKind , Result , ResultExt } ;
13
+ use log:: { LogRecord , LogLevelFilter } ;
19
14
use std:: fs:: File ;
20
- #[ cfg( unix) ]
21
- use std:: os:: unix:: ffi:: OsStringExt ;
22
-
23
- use clap:: { App , Arg } ;
24
- use ref_slice:: ref_slice_mut;
25
-
26
- use errors:: * ;
27
-
28
- mod errors {
29
- error_chain ! ( ) ;
30
- }
15
+ use std:: io:: Write ;
16
+ use std:: time:: Duration ;
17
+ use std:: { env, io, process, thread} ;
31
18
32
19
fn main ( ) {
20
+ // Initialise logging.
21
+ env_logger:: LogBuilder :: new ( )
22
+ . format ( |r : & LogRecord |
23
+ format ! ( "\n {time} {lvl} {mod} @{file}:{line}\n {args}" ,
24
+ time = chrono:: Utc :: now( ) . format( "%Y-%m-%d %H:%M:%S%.3f_UTC" ) ,
25
+ lvl = r. level( ) ,
26
+ mod = r. location( ) . module_path( ) ,
27
+ file = r. location( ) . file( ) ,
28
+ line = r. location( ) . line( ) ,
29
+ args = r. args( ) )
30
+ )
31
+ . filter ( None , LogLevelFilter :: Info )
32
+ . parse ( & env:: var ( "RUST_LOG" ) . unwrap_or ( String :: from ( "" ) ) )
33
+ . init ( ) . unwrap ( ) ;
34
+
33
35
fn show_backtrace ( ) -> bool {
34
36
env:: var ( "RUST_BACKTRACE" ) . as_ref ( ) . map ( |s| & s[ ..] ) == Ok ( "1" )
35
37
}
@@ -38,15 +40,15 @@ fn main() {
38
40
let stderr = io:: stderr ( ) ;
39
41
let mut stderr = stderr. lock ( ) ;
40
42
41
- writeln ! ( stderr, "{}" , e) . ok ( ) ;
43
+ writeln ! ( stderr, "{}" , e) . unwrap ( ) ;
42
44
43
45
for e in e. iter ( ) . skip ( 1 ) {
44
- writeln ! ( stderr, "caused by: {}" , e) . ok ( ) ;
46
+ writeln ! ( stderr, "caused by: {}" , e) . unwrap ( ) ;
45
47
}
46
48
47
49
if show_backtrace ( ) {
48
50
if let Some ( backtrace) = e. backtrace ( ) {
49
- writeln ! ( stderr, "{:?}" , backtrace) . ok ( ) ;
51
+ writeln ! ( stderr, "{:?}" , backtrace) . unwrap ( ) ;
50
52
}
51
53
}
52
54
@@ -57,93 +59,95 @@ fn main() {
57
59
fn run ( ) -> Result < ( ) > {
58
60
let matches = App :: new ( "itmdump" )
59
61
. version ( include_str ! ( concat!( env!( "OUT_DIR" ) , "/commit-info.txt" ) ) )
60
- . arg ( Arg :: with_name ( "PATH" ) . help ( "Named pipe to use" ) . required ( true ) )
62
+ . about ( "\n \
63
+ Reads data from an ARM CPU ITM and decodes it. \n \
64
+ \n \
65
+ Input is from an existing file (or named pipe) at a \
66
+ supplied path, or else from standard input.")
67
+ . arg ( Arg :: with_name ( "file" )
68
+ . long ( "file" )
69
+ . short ( "f" )
70
+ . help ( "Path to file (or named pipe) to read from" )
71
+ . takes_value ( true ) )
72
+ . arg ( Arg :: with_name ( "follow" )
73
+ . long ( "follow" )
74
+ . short ( "F" )
75
+ . help ( "Keep the file open after reading through it and \
76
+ append new output as it is written. Like `tail -f'.") )
77
+ . arg ( Arg :: with_name ( "port" )
78
+ . long ( "stimulus" )
79
+ . short ( "s" )
80
+ . help ( "Stimulus port to extract ITM data for." )
81
+ . takes_value ( true )
82
+ . default_value ( "0" )
83
+ . validator ( |s| match s. parse :: < u8 > ( ) {
84
+ Ok ( _) => Ok ( ( ) ) ,
85
+ Err ( e) => Err ( e. to_string ( ) )
86
+ } ) )
61
87
. get_matches ( ) ;
62
88
63
- let pipe = PathBuf :: from ( matches. value_of ( "PATH" ) . unwrap ( ) ) ;
64
- let pipe_ = pipe. display ( ) ;
65
-
66
- if pipe. exists ( ) {
67
- try!( fs:: remove_file ( & pipe)
68
- . chain_err ( || format ! ( "couldn't remove {}" , pipe_) ) ) ;
69
- }
70
-
71
- let mut stream = match ( ) {
72
- #[ cfg( unix) ]
73
- ( ) => {
74
- let cpipe =
75
- try!( CString :: new ( pipe. clone ( ) . into_os_string ( ) . into_vec ( ) )
76
- . chain_err ( || {
77
- format ! ( "error converting {} to a C string" , pipe_)
78
- } ) ) ;
79
-
80
- match unsafe { libc:: mkfifo ( cpipe. as_ptr ( ) , 0o644 ) } {
81
- 0 => { }
82
- e => {
83
- try!( Err ( io:: Error :: from_raw_os_error ( e) ) . chain_err ( || {
84
- format ! ( "couldn't create a named pipe in {}" , pipe_)
85
- } ) )
86
- }
87
- }
89
+ let port = matches. value_of ( "port" )
90
+ . unwrap ( ) // We supplied a default value
91
+ . parse :: < u8 > ( )
92
+ . expect ( "Arg validator should ensure this parses" ) ;
88
93
89
- try!( File :: open ( & pipe)
90
- . chain_err ( || format ! ( "couldn't open {}" , pipe_) ) )
91
- }
92
- #[ cfg( not( unix) ) ]
93
- ( ) => {
94
- try!( OpenOptions :: new ( )
95
- . create ( true )
96
- . read ( true )
97
- . write ( true )
98
- . open ( & pipe)
99
- . chain_err ( || format ! ( "couldn't open {}" , pipe_) ) )
100
- }
101
- } ;
94
+ let follow = matches. is_present ( "follow" ) ;
102
95
103
- let mut header = 0 ;
96
+ let read = open_read ( & matches) ?;
97
+ let mut decoder = Decoder :: new ( read) ;
104
98
105
- let ( stdout, stderr ) = ( io:: stdout ( ) , io :: stderr ( ) ) ;
106
- let ( mut stdout, mut stderr ) = ( stdout. lock ( ) , stderr . lock ( ) ) ;
99
+ let stdout = io:: stdout ( ) ;
100
+ let mut stdout = stdout. lock ( ) ;
107
101
loop {
108
- if let Err ( e) = ( || {
109
- try!( stream. read_exact ( ref_slice_mut ( & mut header) ) ) ;
110
- let port = header >> 3 ;
111
-
112
- // Ignore all the packets that don't come from the stimulus port 0
113
- if port != 0 {
114
- return Ok ( ( ) ) ;
115
- }
116
-
117
- match header & 0b111 {
118
- 0b01 => {
119
- let mut payload = 0 ;
120
- try!( stream. read_exact ( ref_slice_mut ( & mut payload) ) ) ;
121
- stdout. write_all ( & [ payload] )
122
- }
123
- 0b10 => {
124
- let mut payload = [ 0 ; 2 ] ;
125
- try!( stream. read_exact ( & mut payload) ) ;
126
- stdout. write_all ( & payload)
127
- }
128
- 0b11 => {
129
- let mut payload = [ 0 ; 4 ] ;
130
- try!( stream. read_exact ( & mut payload) ) ;
131
- stdout. write_all ( & payload)
132
- }
133
- _ => {
134
- // Not a valid header, skip.
135
- Ok ( ( ) )
102
+ let p = decoder. read_packet ( ) ;
103
+ match p {
104
+ Ok ( p) => {
105
+ match p. kind ( ) {
106
+ & packet:: Kind :: Instrumentation ( ref i) if i. port ( ) == port => {
107
+ stdout. write_all ( & i. payload ( ) ) ?;
108
+ }
109
+ _ => ( ) ,
136
110
}
137
111
}
138
- } ) ( ) {
139
- match e. kind ( ) {
140
- io:: ErrorKind :: UnexpectedEof => {
112
+ Err ( e @ Error ( ErrorKind :: UnknownHeader ( _) , _) ) => {
113
+ // We don't know this header type; warn and continue.
114
+ debug ! ( "{}" , e) ;
115
+ } ,
116
+ Err ( Error ( ErrorKind :: EofBeforePacket , _) ) => {
117
+ if follow {
141
118
thread:: sleep ( Duration :: from_millis ( 100 ) ) ;
119
+ } else {
120
+ // !follow and EOF. Exit.
121
+ return Ok ( ( ) )
142
122
}
143
- _ => {
144
- writeln ! ( stderr, "error: {:?}" , e. kind( ) ) . ok ( ) ;
145
- }
146
- }
123
+ } ,
124
+
125
+ // FIXME: If in follow mode, we may try to read a packet
126
+ // but reach the end of the file in the middle. Currently
127
+ // we'd just return an error and hence exit. When we
128
+ // receive an error, we've already read and discarded some
129
+ // data, so we can't just go around this loop again.
130
+ //
131
+ // We could make a file following wrapper around `read`
132
+ // that blocks in a loop retrying the read and sleeping if
133
+ // there's no data.
134
+ Err ( e) => return Err ( e) ,
147
135
}
148
- }
136
+ } // end of read loop
137
+
138
+ // Unreachable.
139
+ }
140
+
141
+ fn open_read ( matches : & ArgMatches ) -> Result < Box < io:: Read + ' static > > {
142
+ let path = matches. value_of ( "file" ) ;
143
+ Ok ( match path {
144
+ Some ( path) => {
145
+ let f =
146
+ File :: open ( path)
147
+ . chain_err ( || format ! ( "Couldn't open source file '{}'" ,
148
+ path) ) ?;
149
+ Box :: new ( f) as Box < io:: Read + ' static >
150
+ } ,
151
+ None => Box :: new ( io:: stdin ( ) ) as Box < io:: Read + ' static > ,
152
+ } )
149
153
}
0 commit comments