Handle multiple symbol dirs for a single binary.
This commit is contained in:
Родитель
e5b84627cb
Коммит
1da10ce58f
|
@ -26,9 +26,12 @@ must not have changed since the stack frames were produced. Otherwise, source
|
|||
locations in the output may be missing or incorrect.
|
||||
|
||||
Alternatively, you can use the `-b` option to tell `fix-stacks` to read
|
||||
Breakpad symbol files, as packaged by Firefox. In this case, the processed
|
||||
output will contain square brackets instead of parentheses, to make it
|
||||
detectable from the output that breakpad symbols were used.
|
||||
Breakpad symbol files, as packaged by Firefox. The argument must contain two
|
||||
paths, separated by a comma: the first path points to the Breakpad symbols
|
||||
directory, the second path points to the `fileid` executable in the Firefox
|
||||
objdir. In this case, the processed output will contain square brackets instead
|
||||
of parentheses, to make it detectable from the output that breakpad symbols
|
||||
were used.
|
||||
|
||||
`fix-stacks` works on Linux, Windows, and Mac.
|
||||
|
||||
|
|
78
src/main.rs
78
src/main.rs
|
@ -11,6 +11,8 @@ use std::env;
|
|||
use std::fs;
|
||||
use std::io::{self, BufRead, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use std::str;
|
||||
use symbolic_common::{Arch, Name};
|
||||
use symbolic_debuginfo::{Archive, FileFormat, Function, Object, ObjectDebugSession};
|
||||
use symbolic_demangle::{Demangle, DemangleFormat, DemangleOptions};
|
||||
|
@ -249,12 +251,18 @@ impl FileInfo {
|
|||
}
|
||||
}
|
||||
|
||||
/// Info provided via the `-b` flag.
|
||||
struct BreakpadInfo {
|
||||
syms_dir: String,
|
||||
fileid_exe: String,
|
||||
}
|
||||
|
||||
/// The top level structure that does the work.
|
||||
struct Fixer {
|
||||
re: Regex,
|
||||
file_infos: FxHashMap<String, FileInfo>,
|
||||
json_mode: JsonMode,
|
||||
bp_syms_dir: Option<String>,
|
||||
bp_info: Option<BreakpadInfo>,
|
||||
lb: char,
|
||||
rb: char,
|
||||
}
|
||||
|
@ -263,10 +271,10 @@ struct Fixer {
|
|||
type SymFuncAddrs = FxHashMap<String, u64>;
|
||||
|
||||
impl Fixer {
|
||||
fn new(json_mode: JsonMode, bp_syms_dir: Option<String>) -> Fixer {
|
||||
fn new(json_mode: JsonMode, bp_info: Option<BreakpadInfo>) -> Fixer {
|
||||
// We use parentheses with native debug info, and square brackets with
|
||||
// Breakpad symbols.
|
||||
let (lb, rb) = if bp_syms_dir == None {
|
||||
let (lb, rb) = if let None = bp_info {
|
||||
('(', ')')
|
||||
} else {
|
||||
('[', ']')
|
||||
|
@ -276,7 +284,7 @@ impl Fixer {
|
|||
re: Regex::new(r"^(.*#\d+: )(.+)\[(.+) \+0x([0-9A-Fa-f]+)\](.*)$").unwrap(),
|
||||
file_infos: FxHashMap::default(),
|
||||
json_mode,
|
||||
bp_syms_dir,
|
||||
bp_info,
|
||||
lb,
|
||||
rb,
|
||||
}
|
||||
|
@ -308,10 +316,10 @@ impl Fixer {
|
|||
/// Read the data from `file_name` and construct a `FileInfo` that we can
|
||||
/// subsequently query. Return a description of the failing operation on
|
||||
/// error.
|
||||
fn build_file_info(bin_file: &str, bp_syms_dir: &Option<String>) -> Result<FileInfo, String> {
|
||||
fn build_file_info(bin_file: &str, bp_info: &Option<BreakpadInfo>) -> Result<FileInfo, String> {
|
||||
// If we're using Breakpad symbols, we don't consult `bin_file`.
|
||||
if let Some(syms_dir) = bp_syms_dir {
|
||||
return Fixer::build_file_info_breakpad(bin_file, syms_dir);
|
||||
if let Some(bp_info) = bp_info {
|
||||
return Fixer::build_file_info_breakpad(bin_file, bp_info);
|
||||
}
|
||||
|
||||
// Otherwise, we read `bin_file`.
|
||||
|
@ -326,7 +334,15 @@ impl Fixer {
|
|||
}
|
||||
}
|
||||
|
||||
fn build_file_info_breakpad(bin_file: &str, syms_dir: &str) -> Result<FileInfo, String> {
|
||||
fn build_file_info_breakpad(
|
||||
bin_file: &str,
|
||||
bp_info: &BreakpadInfo,
|
||||
) -> Result<FileInfo, String> {
|
||||
let BreakpadInfo {
|
||||
syms_dir,
|
||||
fileid_exe,
|
||||
} = bp_info;
|
||||
|
||||
// We must find the `.sym` file for this `bin_file`, as produced by the
|
||||
// Firefox build system. A running example:
|
||||
// - `bin_file` is `bin/libxul.so`
|
||||
|
@ -357,10 +373,15 @@ impl Fixer {
|
|||
d.map_err(|_| format!("read breakpad symbols dir `{}` for", syms_bin_dir.display()))?
|
||||
.path()
|
||||
} else {
|
||||
return Err(format!(
|
||||
"read breakpad symbols dir `{}` (not exactly one subdir) for",
|
||||
syms_bin_dir.display()
|
||||
));
|
||||
// Use `fileid` to determine the right directory.
|
||||
let output = Command::new(fileid_exe)
|
||||
.arg(&bin_file)
|
||||
.output()
|
||||
.map_err(|_| format!("run `{}` for", fileid_exe))?;
|
||||
let uuid = str::from_utf8(&output.stdout).unwrap().trim_end();
|
||||
let mut syms_uuid_dir = syms_bin_dir;
|
||||
syms_uuid_dir.push(uuid);
|
||||
syms_uuid_dir
|
||||
};
|
||||
|
||||
// `sym_file` is `syms/libxul.so/<uuid>/libxul.so.sym`.
|
||||
|
@ -641,7 +662,7 @@ impl Fixer {
|
|||
let file_info = match self.file_infos.entry(raw_in_file_name.to_string()) {
|
||||
Entry::Occupied(o) => o.into_mut(),
|
||||
Entry::Vacant(v) => {
|
||||
match Fixer::build_file_info(&raw_in_file_name, &self.bp_syms_dir) {
|
||||
match Fixer::build_file_info(&raw_in_file_name, &self.bp_info) {
|
||||
Ok(file_info) => v.insert(file_info),
|
||||
Err(op) => {
|
||||
// Print an error message and then set up an empty
|
||||
|
@ -713,16 +734,20 @@ r##"usage: fix-stacks [options] < input > output
|
|||
Post-process the stack frames produced by MozFormatCodeAddress().
|
||||
|
||||
options:
|
||||
-h, --help Show this message and exit
|
||||
-j, --json Treat input and output as JSON fragments
|
||||
-b, --breakpad DIR Use breakpad symbols in directory DIR
|
||||
-h, --help Show this message and exit
|
||||
-j, --json Treat input and output as JSON fragments
|
||||
-b, --breakpad DIR,EXE Use breakpad symbols in directory DIR,
|
||||
and `fileid` EXE to choose among possibilities
|
||||
"##;
|
||||
|
||||
fn main_inner() -> io::Result<()> {
|
||||
// Process command line arguments. The arguments are simple enough for now
|
||||
// that using an external crate doesn't seem worthwhile.
|
||||
let mut json_mode = JsonMode::No;
|
||||
let mut bp_syms_dir = None;
|
||||
let mut bp_info = None;
|
||||
|
||||
let err = |msg| Err(io::Error::new(io::ErrorKind::Other, msg));
|
||||
|
||||
let mut args = env::args().skip(1);
|
||||
while let Some(arg) = args.next() {
|
||||
if arg == "-h" || arg == "--help" {
|
||||
|
@ -732,10 +757,19 @@ fn main_inner() -> io::Result<()> {
|
|||
json_mode = JsonMode::Yes;
|
||||
} else if arg == "-b" || arg == "--breakpad" {
|
||||
match args.next() {
|
||||
Some(arg) if !arg.starts_with('-') => bp_syms_dir = Some(arg),
|
||||
Some(arg2) => {
|
||||
let v: Vec<_> = arg2.split(',').collect();
|
||||
if v.len() == 2 {
|
||||
bp_info = Some(BreakpadInfo {
|
||||
syms_dir: v[0].to_string(),
|
||||
fileid_exe: v[1].to_string(),
|
||||
});
|
||||
} else {
|
||||
return err(format!("bad argument `{}` to option `{}`.", arg2, arg));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let msg = format!("missing or bad argument to option `{}`.", arg);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, msg));
|
||||
return err(format!("missing argument to option `{}`.", arg));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -743,13 +777,13 @@ fn main_inner() -> io::Result<()> {
|
|||
"bad argument `{}`. Run `fix-stacks -h` for more information.",
|
||||
arg
|
||||
);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, msg));
|
||||
return err(msg);
|
||||
}
|
||||
}
|
||||
|
||||
let reader = io::BufReader::new(io::stdin());
|
||||
|
||||
let mut fixer = Fixer::new(json_mode, bp_syms_dir);
|
||||
let mut fixer = Fixer::new(json_mode, bp_info);
|
||||
for line in reader.lines() {
|
||||
writeln!(io::stdout(), "{}", fixer.fix(line.unwrap()))?;
|
||||
}
|
||||
|
|
11
src/tests.rs
11
src/tests.rs
|
@ -297,7 +297,16 @@ fn test_breakpad() {
|
|||
// LINE 0x11d1 line=13 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(JsonMode::No, Some("tests/bpsyms".to_string()));
|
||||
// We can use "" for `fileid_exe` because that field is only used for
|
||||
// binaries with multiple Breakpad symbol dirs, which this test doesn't
|
||||
// have.
|
||||
let mut fixer = Fixer::new(
|
||||
JsonMode::No,
|
||||
Some(BreakpadInfo {
|
||||
syms_dir: "tests/bpsyms".to_string(),
|
||||
fileid_exe: "".to_string(),
|
||||
}),
|
||||
);
|
||||
|
||||
// Test various addresses.
|
||||
let mut func = |name, addr, linenum| {
|
||||
|
|
Загрузка…
Ссылка в новой задаче