Skip to content
This repository was archived by the owner on Dec 15, 2021. It is now read-only.

Commit 19a77e0

Browse files
committed
Auto merge of #11 - fluffysquirrels:input, r=japaric
More input handling options In particular: * Arguments to read to the end of a file or follow the end (like tail -f) * Extract a chosen stimulus port instead of only 0 Also: * Logging with env_logger
2 parents 7d06b40 + feba5a0 commit 19a77e0

File tree

9 files changed

+762
-223
lines changed

9 files changed

+762
-223
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ repository = "https://github.com/japaric/itm"
1010
version = "0.1.1"
1111

1212
[dependencies]
13-
clap = "2.14.0"
14-
error-chain = "0.5.0"
15-
libc = "0.2.17"
16-
ref_slice = "1.1.0"
13+
chrono = "^0.4"
14+
clap = "^2.14.0"
15+
env_logger = "^0.4.3"
16+
error-chain = "^0.11.0"
17+
log = "^0.3.8"
1718

1819
[dev-dependencies]
1920
tempdir = "0.3.5"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
# `itm`
55

6-
> Tool to parse and dump [ITM] packets
6+
> A Rust crate and tool `itmdump` to parse and dump ARM [ITM] packets.
77
88
[ITM]: http://infocenter.arm.com/help/topic/com.arm.doc.ddi0314h/Chdbicbg.html
99

src/bin/itmdump.rs

Lines changed: 111 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,37 @@
1-
extern crate clap;
2-
extern crate libc;
3-
extern crate ref_slice;
1+
#![deny(warnings)]
42

3+
extern crate chrono;
4+
extern crate clap;
5+
extern crate env_logger;
6+
extern crate itm;
57
#[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;
129

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};
1914
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};
3118

3219
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+
3335
fn show_backtrace() -> bool {
3436
env::var("RUST_BACKTRACE").as_ref().map(|s| &s[..]) == Ok("1")
3537
}
@@ -38,15 +40,15 @@ fn main() {
3840
let stderr = io::stderr();
3941
let mut stderr = stderr.lock();
4042

41-
writeln!(stderr, "{}", e).ok();
43+
writeln!(stderr, "{}", e).unwrap();
4244

4345
for e in e.iter().skip(1) {
44-
writeln!(stderr, "caused by: {}", e).ok();
46+
writeln!(stderr, "caused by: {}", e).unwrap();
4547
}
4648

4749
if show_backtrace() {
4850
if let Some(backtrace) = e.backtrace() {
49-
writeln!(stderr, "{:?}", backtrace).ok();
51+
writeln!(stderr, "{:?}", backtrace).unwrap();
5052
}
5153
}
5254

@@ -57,93 +59,95 @@ fn main() {
5759
fn run() -> Result<()> {
5860
let matches = App::new("itmdump")
5961
.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+
}))
6187
.get_matches();
6288

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");
8893

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");
10295

103-
let mut header = 0;
96+
let read = open_read(&matches)?;
97+
let mut decoder = Decoder::new(read);
10498

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();
107101
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+
_ => (),
136110
}
137111
}
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 {
141118
thread::sleep(Duration::from_millis(100));
119+
} else {
120+
// !follow and EOF. Exit.
121+
return Ok(())
142122
}
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),
147135
}
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+
})
149153
}

0 commit comments

Comments
 (0)