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.
|
locations in the output may be missing or incorrect.
|
||||||
|
|
||||||
Alternatively, you can use the `-b` option to tell `fix-stacks` to read
|
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
|
Breakpad symbol files, as packaged by Firefox. The argument must contain two
|
||||||
output will contain square brackets instead of parentheses, to make it
|
paths, separated by a comma: the first path points to the Breakpad symbols
|
||||||
detectable from the output that breakpad symbols were used.
|
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.
|
`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::fs;
|
||||||
use std::io::{self, BufRead, Write};
|
use std::io::{self, BufRead, Write};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::process::Command;
|
||||||
|
use std::str;
|
||||||
use symbolic_common::{Arch, Name};
|
use symbolic_common::{Arch, Name};
|
||||||
use symbolic_debuginfo::{Archive, FileFormat, Function, Object, ObjectDebugSession};
|
use symbolic_debuginfo::{Archive, FileFormat, Function, Object, ObjectDebugSession};
|
||||||
use symbolic_demangle::{Demangle, DemangleFormat, DemangleOptions};
|
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.
|
/// The top level structure that does the work.
|
||||||
struct Fixer {
|
struct Fixer {
|
||||||
re: Regex,
|
re: Regex,
|
||||||
file_infos: FxHashMap<String, FileInfo>,
|
file_infos: FxHashMap<String, FileInfo>,
|
||||||
json_mode: JsonMode,
|
json_mode: JsonMode,
|
||||||
bp_syms_dir: Option<String>,
|
bp_info: Option<BreakpadInfo>,
|
||||||
lb: char,
|
lb: char,
|
||||||
rb: char,
|
rb: char,
|
||||||
}
|
}
|
||||||
|
@ -263,10 +271,10 @@ struct Fixer {
|
||||||
type SymFuncAddrs = FxHashMap<String, u64>;
|
type SymFuncAddrs = FxHashMap<String, u64>;
|
||||||
|
|
||||||
impl Fixer {
|
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
|
// We use parentheses with native debug info, and square brackets with
|
||||||
// Breakpad symbols.
|
// Breakpad symbols.
|
||||||
let (lb, rb) = if bp_syms_dir == None {
|
let (lb, rb) = if let None = bp_info {
|
||||||
('(', ')')
|
('(', ')')
|
||||||
} else {
|
} else {
|
||||||
('[', ']')
|
('[', ']')
|
||||||
|
@ -276,7 +284,7 @@ impl Fixer {
|
||||||
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_mode,
|
json_mode,
|
||||||
bp_syms_dir,
|
bp_info,
|
||||||
lb,
|
lb,
|
||||||
rb,
|
rb,
|
||||||
}
|
}
|
||||||
|
@ -308,10 +316,10 @@ impl Fixer {
|
||||||
/// 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.
|
||||||
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 we're using Breakpad symbols, we don't consult `bin_file`.
|
||||||
if let Some(syms_dir) = bp_syms_dir {
|
if let Some(bp_info) = bp_info {
|
||||||
return Fixer::build_file_info_breakpad(bin_file, syms_dir);
|
return Fixer::build_file_info_breakpad(bin_file, bp_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, we read `bin_file`.
|
// 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
|
// We must find the `.sym` file for this `bin_file`, as produced by the
|
||||||
// Firefox build system. A running example:
|
// Firefox build system. A running example:
|
||||||
// - `bin_file` is `bin/libxul.so`
|
// - `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()))?
|
d.map_err(|_| format!("read breakpad symbols dir `{}` for", syms_bin_dir.display()))?
|
||||||
.path()
|
.path()
|
||||||
} else {
|
} else {
|
||||||
return Err(format!(
|
// Use `fileid` to determine the right directory.
|
||||||
"read breakpad symbols dir `{}` (not exactly one subdir) for",
|
let output = Command::new(fileid_exe)
|
||||||
syms_bin_dir.display()
|
.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`.
|
// `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()) {
|
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) => {
|
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),
|
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
|
||||||
|
@ -713,16 +734,20 @@ 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 Treat input and output as JSON fragments
|
-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<()> {
|
fn main_inner() -> io::Result<()> {
|
||||||
// Process command line arguments. The arguments are simple enough for now
|
// Process command line arguments. The arguments are simple enough for now
|
||||||
// that using an external crate doesn't seem worthwhile.
|
// that using an external crate doesn't seem worthwhile.
|
||||||
let mut json_mode = JsonMode::No;
|
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);
|
let mut args = env::args().skip(1);
|
||||||
while let Some(arg) = args.next() {
|
while let Some(arg) = args.next() {
|
||||||
if arg == "-h" || arg == "--help" {
|
if arg == "-h" || arg == "--help" {
|
||||||
|
@ -732,10 +757,19 @@ fn main_inner() -> io::Result<()> {
|
||||||
json_mode = JsonMode::Yes;
|
json_mode = JsonMode::Yes;
|
||||||
} else if arg == "-b" || arg == "--breakpad" {
|
} else if arg == "-b" || arg == "--breakpad" {
|
||||||
match args.next() {
|
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(format!("missing argument to option `{}`.", arg));
|
||||||
return Err(io::Error::new(io::ErrorKind::Other, msg));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -743,13 +777,13 @@ fn main_inner() -> io::Result<()> {
|
||||||
"bad argument `{}`. Run `fix-stacks -h` for more information.",
|
"bad argument `{}`. Run `fix-stacks -h` for more information.",
|
||||||
arg
|
arg
|
||||||
);
|
);
|
||||||
return Err(io::Error::new(io::ErrorKind::Other, msg));
|
return err(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let reader = io::BufReader::new(io::stdin());
|
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() {
|
for line in reader.lines() {
|
||||||
writeln!(io::stdout(), "{}", fixer.fix(line.unwrap()))?;
|
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 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
|
// 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.
|
// Test various addresses.
|
||||||
let mut func = |name, addr, linenum| {
|
let mut func = |name, addr, linenum| {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче