Merge pull request #14 from nnethercote/fix-JSON-handling-more
Fix JSON handling more
This commit is contained in:
Коммит
b0abef037e
71
src/main.rs
71
src/main.rs
|
@ -53,7 +53,7 @@ impl Interner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum JsonEscaping {
|
enum JsonMode {
|
||||||
No,
|
No,
|
||||||
Yes,
|
Yes,
|
||||||
}
|
}
|
||||||
|
@ -252,22 +252,23 @@ impl FileInfo {
|
||||||
struct Fixer {
|
struct Fixer {
|
||||||
re: Regex,
|
re: Regex,
|
||||||
file_infos: FxHashMap<String, FileInfo>,
|
file_infos: FxHashMap<String, FileInfo>,
|
||||||
json_escaping: JsonEscaping,
|
json_mode: JsonMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Records address of functions from a symbol table.
|
/// Records address of functions from a symbol table.
|
||||||
type SymFuncAddrs = FxHashMap<String, u64>;
|
type SymFuncAddrs = FxHashMap<String, u64>;
|
||||||
|
|
||||||
impl Fixer {
|
impl Fixer {
|
||||||
fn new(json_escaping: JsonEscaping) -> Fixer {
|
fn new(json_mode: JsonMode) -> Fixer {
|
||||||
Fixer {
|
Fixer {
|
||||||
// Matches lines produced by MozFormatCodeAddress().
|
// Matches lines produced by MozFormatCodeAddress().
|
||||||
re: Regex::new(r"^(.*#\d+: )(.+)\[(.+) \+0x([0-9A-Fa-f]+)\](.*)$").unwrap(),
|
re: Regex::new(r"^(.*#\d+: )(.+)\[(.+) \+0x([0-9A-Fa-f]+)\](.*)$").unwrap(),
|
||||||
file_infos: FxHashMap::default(),
|
file_infos: FxHashMap::default(),
|
||||||
json_escaping,
|
json_mode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add JSON escapes to a fragment of text.
|
||||||
fn json_escape(string: &str) -> String {
|
fn json_escape(string: &str) -> String {
|
||||||
// Do the escaping.
|
// Do the escaping.
|
||||||
let escaped = serde_json::to_string(string).unwrap();
|
let escaped = serde_json::to_string(string).unwrap();
|
||||||
|
@ -276,6 +277,20 @@ impl Fixer {
|
||||||
escaped[1..escaped.len() - 1].to_string()
|
escaped[1..escaped.len() - 1].to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Remove JSON escapes from a fragment of text.
|
||||||
|
fn json_unescape(string: &str) -> String {
|
||||||
|
// Add quotes.
|
||||||
|
let quoted = format!("\"{}\"", string);
|
||||||
|
|
||||||
|
// Do the unescaping, which also removes the quotes.
|
||||||
|
let value = serde_json::from_str("ed).unwrap();
|
||||||
|
if let serde_json::Value::String(unescaped) = value {
|
||||||
|
unescaped
|
||||||
|
} else {
|
||||||
|
panic!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Read the data from `file_name` and construct a `FileInfo` that we can
|
/// Read the data from `file_name` and construct a `FileInfo` that we can
|
||||||
/// subsequently query. Return a description of the failing operation on
|
/// subsequently query. Return a description of the failing operation on
|
||||||
/// error.
|
/// error.
|
||||||
|
@ -547,11 +562,19 @@ impl Fixer {
|
||||||
let address = u64::from_str_radix(&captures[4], 16).unwrap();
|
let address = u64::from_str_radix(&captures[4], 16).unwrap();
|
||||||
let after = &captures[5];
|
let after = &captures[5];
|
||||||
|
|
||||||
|
// In JSON mode, unescape the function name before using it for
|
||||||
|
// lookups, error messages, etc.
|
||||||
|
let raw_in_file_name = if let JsonMode::Yes = self.json_mode {
|
||||||
|
Fixer::json_unescape(in_file_name)
|
||||||
|
} else {
|
||||||
|
in_file_name.to_string()
|
||||||
|
};
|
||||||
|
|
||||||
// If we haven't seen this file yet, parse and record its contents, for
|
// If we haven't seen this file yet, parse and record its contents, for
|
||||||
// this lookup and any future lookups.
|
// this lookup and any future lookups.
|
||||||
let file_info = match self.file_infos.entry(in_file_name.to_string()) {
|
let file_info = match self.file_infos.entry(raw_in_file_name.to_string()) {
|
||||||
Entry::Occupied(o) => o.into_mut(),
|
Entry::Occupied(o) => o.into_mut(),
|
||||||
Entry::Vacant(v) => match Fixer::build_file_info(in_file_name) {
|
Entry::Vacant(v) => match Fixer::build_file_info(&raw_in_file_name) {
|
||||||
Ok(file_info) => v.insert(file_info),
|
Ok(file_info) => v.insert(file_info),
|
||||||
Err(op) => {
|
Err(op) => {
|
||||||
// Print an error message and then set up an empty
|
// Print an error message and then set up an empty
|
||||||
|
@ -561,32 +584,32 @@ impl Fixer {
|
||||||
// first occurrence.
|
// first occurrence.
|
||||||
// - The line will still receive some transformation, using
|
// - The line will still receive some transformation, using
|
||||||
// the "no symbols or debug info" case below.
|
// the "no symbols or debug info" case below.
|
||||||
eprintln!("fix-stacks error: failed to {} `{}`", op, in_file_name);
|
eprintln!("fix-stacks error: failed to {} `{}`", op, raw_in_file_name);
|
||||||
v.insert(FileInfo::default())
|
v.insert(FileInfo::default())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// If JSON escaping is enabled, we need to escape any new strings we
|
// In JSON mode, we need to escape any new strings we produce. However,
|
||||||
// produce. However, strings that came in from the text (i.e.
|
// strings from the input (i.e. `in_func_name` and `in_file_name`),
|
||||||
// `in_func_name` and `in_file_name`), will already be escaped, so if
|
// will already be escaped, so if they are used in the output they
|
||||||
// they become part of the output they shouldn't be escaped.
|
// shouldn't be re-escaped.
|
||||||
if let Some(func_info) = file_info.func_info(address) {
|
if let Some(func_info) = file_info.func_info(address) {
|
||||||
let raw_func_name = func_info.demangled_name();
|
let raw_out_func_name = func_info.demangled_name();
|
||||||
let out_func_name = if let JsonEscaping::Yes = self.json_escaping {
|
let out_func_name = if let JsonMode::Yes = self.json_mode {
|
||||||
Fixer::json_escape(&raw_func_name)
|
Fixer::json_escape(&raw_out_func_name)
|
||||||
} else {
|
} else {
|
||||||
raw_func_name
|
raw_out_func_name
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(line_info) = func_info.line_info(address) {
|
if let Some(line_info) = func_info.line_info(address) {
|
||||||
// We have the function name, filename, and line number from
|
// We have the function name, filename, and line number from
|
||||||
// the debug info.
|
// the debug info.
|
||||||
let raw_file_name = file_info.interner.get(line_info.path);
|
let raw_out_file_name = file_info.interner.get(line_info.path);
|
||||||
let out_file_name = if let JsonEscaping::Yes = self.json_escaping {
|
let out_file_name = if let JsonMode::Yes = self.json_mode {
|
||||||
Fixer::json_escape(&raw_file_name)
|
Fixer::json_escape(&raw_out_file_name)
|
||||||
} else {
|
} else {
|
||||||
raw_file_name.to_string()
|
raw_out_file_name.to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
format!(
|
format!(
|
||||||
|
@ -622,19 +645,19 @@ r##"usage: fix-stacks [options] < input > output
|
||||||
Post-process the stack frames produced by MozFormatCodeAddress().
|
Post-process the stack frames produced by MozFormatCodeAddress().
|
||||||
|
|
||||||
options:
|
options:
|
||||||
-h, --help show this message and exit
|
-h, --help Show this message and exit
|
||||||
-j, --json Use JSON escaping for printed function names and file names
|
-j, --json Treat input and output as JSON fragments
|
||||||
"##;
|
"##;
|
||||||
|
|
||||||
fn main_inner() -> io::Result<()> {
|
fn main_inner() -> io::Result<()> {
|
||||||
// Process command line arguments.
|
// Process command line arguments.
|
||||||
let mut json_escaping = JsonEscaping::No;
|
let mut json_mode = JsonMode::No;
|
||||||
for arg in env::args().skip(1) {
|
for arg in env::args().skip(1) {
|
||||||
if arg == "-h" || arg == "--help" {
|
if arg == "-h" || arg == "--help" {
|
||||||
println!("{}", USAGE_MSG);
|
println!("{}", USAGE_MSG);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
} else if arg == "-j" || arg == "--json" {
|
} else if arg == "-j" || arg == "--json" {
|
||||||
json_escaping = JsonEscaping::Yes;
|
json_mode = JsonMode::Yes;
|
||||||
} else {
|
} else {
|
||||||
let msg = format!(
|
let msg = format!(
|
||||||
"bad argument `{}`. Run `fix-stacks -h` for more information.",
|
"bad argument `{}`. Run `fix-stacks -h` for more information.",
|
||||||
|
@ -646,7 +669,7 @@ fn main_inner() -> io::Result<()> {
|
||||||
|
|
||||||
let reader = io::BufReader::new(io::stdin());
|
let reader = io::BufReader::new(io::stdin());
|
||||||
|
|
||||||
let mut fixer = Fixer::new(json_escaping);
|
let mut fixer = Fixer::new(json_mode);
|
||||||
for line in reader.lines() {
|
for line in reader.lines() {
|
||||||
writeln!(io::stdout(), "{}", fixer.fix(line.unwrap()))?;
|
writeln!(io::stdout(), "{}", fixer.fix(line.unwrap()))?;
|
||||||
}
|
}
|
||||||
|
|
16
src/tests.rs
16
src/tests.rs
|
@ -31,7 +31,7 @@ fn test_linux() {
|
||||||
// LINE 0x11cd line=13 file=/home/njn/moz/fix-stacks/tests/example.c
|
// LINE 0x11cd line=13 file=/home/njn/moz/fix-stacks/tests/example.c
|
||||||
// LINE 0x11db line=14 file=/home/njn/moz/fix-stacks/tests/example.c
|
// LINE 0x11db line=14 file=/home/njn/moz/fix-stacks/tests/example.c
|
||||||
|
|
||||||
let mut fixer = Fixer::new(JsonEscaping::No);
|
let mut fixer = Fixer::new(JsonMode::No);
|
||||||
|
|
||||||
// Test various addresses.
|
// Test various addresses.
|
||||||
let mut func = |name, addr, linenum| {
|
let mut func = |name, addr, linenum| {
|
||||||
|
@ -62,7 +62,7 @@ fn test_linux() {
|
||||||
func("g", 0x11de, 14);
|
func("g", 0x11de, 14);
|
||||||
|
|
||||||
// Try a new Fixer.
|
// Try a new Fixer.
|
||||||
fixer = Fixer::new(JsonEscaping::No);
|
fixer = Fixer::new(JsonMode::No);
|
||||||
|
|
||||||
// Test various addresses outside `main`, `f`, and `g`.
|
// Test various addresses outside `main`, `f`, and `g`.
|
||||||
let mut outside = |addr| {
|
let mut outside = |addr| {
|
||||||
|
@ -110,7 +110,7 @@ fn test_windows() {
|
||||||
// outputs contains backwards slashes, though, because that is what is used
|
// outputs contains backwards slashes, though, because that is what is used
|
||||||
// within the debug info.
|
// within the debug info.
|
||||||
|
|
||||||
let mut fixer = Fixer::new(JsonEscaping::Yes);
|
let mut fixer = Fixer::new(JsonMode::Yes);
|
||||||
|
|
||||||
// Test various addresses using `example-windows`, which redirects to
|
// Test various addresses using `example-windows`, which redirects to
|
||||||
// `example-windows.pdb`.
|
// `example-windows.pdb`.
|
||||||
|
@ -142,8 +142,8 @@ fn test_windows() {
|
||||||
func("g", 0x6c49, 12);
|
func("g", 0x6c49, 12);
|
||||||
func("g", 0x6c63, 14);
|
func("g", 0x6c63, 14);
|
||||||
|
|
||||||
// Try a new Fixer, without JSON escaping.
|
// Try a new Fixer, without JSON mode.
|
||||||
fixer = Fixer::new(JsonEscaping::No);
|
fixer = Fixer::new(JsonMode::No);
|
||||||
|
|
||||||
// Test various addresses outside `main`, `f`, and `g`, using
|
// Test various addresses outside `main`, `f`, and `g`, using
|
||||||
// `example-windows.pdb` directly.
|
// `example-windows.pdb` directly.
|
||||||
|
@ -229,7 +229,7 @@ fn test_mac() {
|
||||||
// LINE 0xf38 line=10 file=/Users/njn/moz/fix-stacks/tests/mac-lib2.c
|
// LINE 0xf38 line=10 file=/Users/njn/moz/fix-stacks/tests/mac-lib2.c
|
||||||
// LINE 0xf49 line=11 file=/Users/njn/moz/fix-stacks/tests/mac-lib2.c
|
// LINE 0xf49 line=11 file=/Users/njn/moz/fix-stacks/tests/mac-lib2.c
|
||||||
|
|
||||||
let mut fixer = Fixer::new(JsonEscaping::No);
|
let mut fixer = Fixer::new(JsonMode::No);
|
||||||
|
|
||||||
// Test addresses from all the object files that `mac-multi` references.
|
// Test addresses from all the object files that `mac-multi` references.
|
||||||
let mut func = |name, addr, full_path, locn| {
|
let mut func = |name, addr, full_path, locn| {
|
||||||
|
@ -266,7 +266,7 @@ fn test_mac() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_regex() {
|
fn test_regex() {
|
||||||
let mut fixer = Fixer::new(JsonEscaping::No);
|
let mut fixer = Fixer::new(JsonMode::No);
|
||||||
|
|
||||||
// Test various different unchanged line forms, that don't match the regex.
|
// Test various different unchanged line forms, that don't match the regex.
|
||||||
let mut unchanged = |line: &str| {
|
let mut unchanged = |line: &str| {
|
||||||
|
@ -301,7 +301,7 @@ fn test_regex() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_files() {
|
fn test_files() {
|
||||||
let mut fixer = Fixer::new(JsonEscaping::Yes);
|
let mut fixer = Fixer::new(JsonMode::Yes);
|
||||||
|
|
||||||
// Test various different file errors. An error message is also printed to
|
// Test various different file errors. An error message is also printed to
|
||||||
// stderr for each one, but we don't test for that.
|
// stderr for each one, but we don't test for that.
|
||||||
|
|
Загрузка…
Ссылка в новой задаче