Handle multiple symbol dirs for a single binary.

This commit is contained in:
Nicholas Nethercote 2020-03-11 13:41:07 +11:00
Родитель e5b84627cb
Коммит 1da10ce58f
3 изменённых файлов: 72 добавлений и 26 удалений

Просмотреть файл

@ -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.

Просмотреть файл

@ -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
@ -715,14 +736,18 @@ 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
-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()))?;
}

Просмотреть файл

@ -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| {