Bug 1917685 - Merge minidump-analyzer into the crash reporter client r=gsvelto,fluent-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D225234
This commit is contained in:
Alex Franchuk 2024-10-23 13:58:12 +00:00
parent 7e6bb5be66
commit ceabd19bdd
30 changed files with 1356 additions and 968 deletions

5
Cargo.lock generated
View File

@ -1104,6 +1104,7 @@ dependencies = [
"libloading",
"lmdb-rkv-sys",
"log",
"minidump-analyzer",
"mozbuild",
"mozilla-central-workspace-hack",
"objc",
@ -3871,15 +3872,12 @@ dependencies = [
"anyhow",
"async-trait",
"breakpad-symbols",
"clap",
"env_logger",
"futures-executor",
"futures-util",
"lazy_static",
"log",
"minidump",
"minidump-unwind",
"mozilla-central-workspace-hack",
"serde_json",
"windows-sys",
]
@ -4070,7 +4068,6 @@ dependencies = [
"futures",
"futures-channel",
"futures-core",
"futures-executor",
"futures-sink",
"futures-util",
"getrandom",

View File

@ -17,7 +17,6 @@ members = [
"testing/geckodriver",
"toolkit/components/uniffi-bindgen-gecko-js",
"toolkit/crashreporter/client/app",
"toolkit/crashreporter/minidump-analyzer",
"toolkit/crashreporter/mozwer-rust",
"toolkit/crashreporter/rust_minidump_writer_linux",
"toolkit/library/gtest/rust",
@ -41,6 +40,7 @@ exclude = [
"media/mp4parse-rust/mp4parse",
"media/mp4parse-rust/mp4parse_capi",
"xpcom/rust/gkrust_utils",
"toolkit/crashreporter/minidump-analyzer",
"tools/lint/test/files/clippy",
"tools/fuzzing/rust",
"dom/base/rust",

View File

@ -13,9 +13,6 @@
#if defined(MOZ_ASAN) || defined(MOZ_TSAN) || defined(FUZZING)
/llvm-symbolizer
#endif
#if defined(MOZ_CRASHREPORTER)
/minidump-analyzer
#endif
/nmhproxy
/pingsender
/pk12util

View File

@ -401,12 +401,6 @@ bin/libfreebl_64int_3.so
#endif
#endif
; [ minidump-analyzer ]
;
#ifdef MOZ_CRASHREPORTER
@BINPATH@/minidump-analyzer@BIN_SUFFIX@
#endif
; [ Ping Sender ]
;
@BINPATH@/pingsender@BIN_SUFFIX@

View File

@ -1579,7 +1579,6 @@ ${RemoveDefaultBrowserAgentShortcut}
Push "xpcom.dll"
Push "crashreporter.exe"
Push "default-browser-agent.exe"
Push "minidump-analyzer.exe"
Push "nmhproxy.exe"
Push "pingsender.exe"
Push "updater.exe"

View File

@ -560,7 +560,6 @@ class LinuxArtifactJob(ArtifactJob):
"{product}/dependentlibs.list",
"{product}/{product}",
"{product}/{product}-bin",
"{product}/minidump-analyzer",
"{product}/pingsender",
"{product}/plugin-container",
"{product}/updater",
@ -659,7 +658,6 @@ class MacArtifactJob(ArtifactJob):
"{product}",
"{product}-bin",
"*.dylib",
"minidump-analyzer",
"nmhproxy",
"pingsender",
"plugin-container.app/Contents/MacOS/plugin-container",

View File

@ -2,8 +2,7 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<!--
Entitlements to apply to non-browser executables such as updater,
pingsender, minidump-analyzer, and crashreporter during codesigning of
developer builds.
pingsender, and crashreporter during codesigning of developer builds.
-->
<plist version="1.0">
<dict>

View File

@ -1067,7 +1067,6 @@ mac-signing:
- "/Contents/Library/LaunchServices/org.mozilla.updater"
- "/Contents/MacOS/XUL"
- "/Contents/MacOS/pingsender"
- "/Contents/MacOS/minidump-analyzer"
- "/Contents/MacOS/nmhproxy"
- "/Contents/MacOS/*.dylib"
- "/Contents/Resources/gmp-clearkey/*/*.dylib"
@ -1111,7 +1110,6 @@ mac-signing:
- "/Contents/MacOS/updater.app"
- "/Contents/Library/LaunchServices/org.mozilla.updater"
- "/Contents/MacOS/pingsender"
- "/Contents/MacOS/minidump-analyzer"
- "/Contents/MacOS/nmhproxy"
- "/Contents/Frameworks/ChannelPrefs.framework"

View File

@ -8,7 +8,8 @@ import { AsyncShutdown } from "resource://gre/modules/AsyncShutdown.sys.mjs";
// Set to true if the application is quitting
var gQuitting = false;
// Tracks all the running instances of the minidump-analyzer
// Tracks all the running instances of the crash reporter client (for minidump
// analysis).
var gRunningProcesses = new Set();
/**
@ -29,19 +30,24 @@ async function maybeRunMinidumpAnalyzer(minidumpPath, allThreads) {
);
}
function getMinidumpAnalyzerPath() {
function getCrashReporterPath() {
const binPrefix =
AppConstants.platform === "macosx"
? "crashreporter.app/Contents/MacOS/"
: "";
const binSuffix = AppConstants.platform === "win" ? ".exe" : "";
const exeName = "minidump-analyzer" + binSuffix;
const exeName = binPrefix + "crashreporter" + binSuffix;
let exe = Services.dirsvc.get("GreBinD", Ci.nsIFile);
exe.append(exeName);
exe.appendRelativePath(exeName);
return exe;
}
/**
* Run the minidump analyzer tool to gather stack traces from the minidump. The
* stack traces will be stored in the .extra file under the StackTraces= entry.
* Run the crash reporter's analyzer tool to gather stack traces from the
* minidump. The stack traces will be stored in the .extra file under the
* StackTraces= entry.
*
* @param minidumpPath {string} The path to the minidump file
* @param allThreads {bool} Gather stack traces for all threads, not just the
@ -53,8 +59,8 @@ function getMinidumpAnalyzerPath() {
function runMinidumpAnalyzer(minidumpPath, allThreads) {
return new Promise((resolve, reject) => {
try {
let exe = getMinidumpAnalyzerPath();
let args = [minidumpPath];
let exe = getCrashReporterPath();
let args = ["--analyze", minidumpPath];
let process = Cc["@mozilla.org/process/util;1"].createInstance(
Ci.nsIProcess
);
@ -63,7 +69,7 @@ function runMinidumpAnalyzer(minidumpPath, allThreads) {
process.noShell = true;
if (allThreads) {
args.unshift("--full");
args.push("--full");
}
process.runAsync(args, args.length, (subject, topic) => {

View File

@ -16,6 +16,7 @@ glean = { workspace = true }
intl-memoizer = "0.5"
libloading = "0.8"
log = "0.4.17"
minidump-analyzer = { path = "../../minidump-analyzer" }
mozbuild = "0.1"
mozilla-central-workspace-hack = { version = "0.1", features = ["crashreporter"], optional = true }
once_cell = "1"

View File

@ -0,0 +1,37 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
//! A frontend for minidump analysis.
use minidump_analyzer::MinidumpAnalyzer;
pub fn main() {
// Skip program name and `--analyze` argument.
let mut args = std::env::args_os().skip(2);
let mut minidump_path = None;
let mut analyze_all_threads = false;
while let Some(arg) = args.next() {
if arg == "--full" && !analyze_all_threads {
analyze_all_threads = true;
} else if minidump_path.is_none() {
minidump_path = Some(arg);
} else {
eprintln!("ignoring extraneous argument: {}", arg.to_string_lossy());
}
}
let Some(minidump_path) = minidump_path else {
eprintln!("expected minidump path to analyze");
std::process::exit(1);
};
if let Err(e) = MinidumpAnalyzer::new(minidump_path.as_ref())
.all_threads(analyze_all_threads)
.analyze()
{
eprintln!("minidump analyzer failed: {e}");
std::process::exit(1);
}
}

View File

@ -104,7 +104,7 @@ impl Config {
)
}
// We no longer use don't use `MOZ_CRASHREPORTER_RESTART_ARG_0`, see bug 1872920.
// We no longer use `MOZ_CRASHREPORTER_RESTART_ARG_0`, see bug 1872920.
self.restart_args = (1..)
.into_iter()
.map_while(|arg_num| std::env::var_os(format!("{}_{}", ekey!("RESTART_ARG"), arg_num)))

View File

@ -33,7 +33,6 @@
#![cfg_attr(windows, windows_subsystem = "windows")]
use crate::std::sync::Arc;
use anyhow::Context;
use config::Config;
// A few macros are defined here to allow use in all submodules via textual scope lookup.
@ -58,6 +57,7 @@ macro_rules! ekey {
};
}
mod analyze;
mod async_task;
mod config;
mod data;
@ -75,8 +75,22 @@ mod ui;
#[cfg(test)]
mod test;
#[cfg(not(mock))]
fn main() {
// Determine the mode in which to run. This is very simplistic, but need not be more permissive
// nor flexible since we control how the program is invoked.
if std::env::args_os()
.nth(1)
.map(|s| s == "--analyze")
.unwrap_or(false)
{
analyze::main()
} else {
report_main()
}
}
#[cfg(not(mock))]
fn report_main() {
let log_target = logging::init();
let mut config = Config::new();
@ -106,7 +120,7 @@ fn main() {
}
#[cfg(mock)]
fn main() {
fn report_main() {
// TODO it'd be nice to be able to set these values at runtime in some way when running the
// mock application.
@ -151,10 +165,6 @@ fn main() {
// Create a default mock environment which allows successful operation.
let mut mock = mock::builder();
mock.set(
Command::mock("work_dir/minidump-analyzer"),
Box::new(|_| Ok(crate::std::process::success_output())),
)
.set(
Command::mock("work_dir/pingsender"),
Box::new(|_| Ok(crate::std::process::success_output())),
)
@ -212,24 +222,16 @@ fn try_run(config: &mut Arc<Config>) -> anyhow::Result<bool> {
Ok(false)
}
} else {
// Run minidump-analyzer to gather stack traces.
// Use minidump-analyzer to gather stack traces.
#[cfg(not(mock))]
{
let analyzer_path = config.installation_program_path("minidump-analyzer");
let mut cmd = crate::process::background_command(&analyzer_path);
if config.dump_all_threads {
cmd.arg("--full");
}
cmd.arg(config.dump_file());
let output = cmd
.output()
.with_context(|| config.string("crashreporter-error-minidump-analyzer"))?;
if !output.status.success() {
log::warn!(
"minidump-analyzer failed to run ({});\n\nstderr: {}\n\nstdout: {}",
output.status,
String::from_utf8_lossy(&output.stderr),
String::from_utf8_lossy(&output.stdout),
);
if let Err(e) = minidump_analyzer::MinidumpAnalyzer::new(config.dump_file())
.all_threads(config.dump_all_threads)
.analyze()
{
// Minidump analysis gives optional additional information; if it fails, we should
// still proceed.
log::warn!("minidump analyzer failed: {e}");
}
}

View File

@ -192,10 +192,6 @@ impl GuiTest {
// Create a default mock environment which allows successful operation.
let mut mock = mock::builder();
mock.set(
Command::mock("work_dir/minidump-analyzer"),
Box::new(|_| Ok(crate::std::process::success_output())),
)
.set(
Command::mock("work_dir/pingsender"),
Box::new(|_| Ok(crate::std::process::success_output())),
)
@ -451,30 +447,9 @@ fn no_dump_file() {
assert!(try_run(&mut cfg).is_ok());
}
#[test]
fn minidump_analyzer_error() {
mock::builder()
.set(
Command::mock("work_dir/minidump-analyzer"),
Box::new(|_| Err(ErrorKind::NotFound.into())),
)
.set(
crate::std::env::MockCurrentExe,
"work_dir/crashreporter".into(),
)
.run(|| {
let cfg = test_config();
assert!(try_run(&mut Arc::new(cfg)).is_err());
});
}
#[test]
fn no_extra_file() {
mock::builder()
.set(
Command::mock("work_dir/minidump-analyzer"),
Box::new(|_| Ok(crate::std::process::success_output())),
)
.set(
crate::std::env::MockCurrentExe,
"work_dir/crashreporter".into(),

File diff suppressed because it is too large Load Diff

View File

@ -7,15 +7,10 @@ readme = "README.md"
license = "MPL-2.0"
edition = "2021"
[[bin]]
name = "minidump-analyzer"
[dependencies]
anyhow = "1.0.69"
async-trait = "0.1"
breakpad-symbols = "0.22"
clap = { version = "4", default-features = false, features = ["std", "cargo", "wrap_help", "derive"] }
env_logger = { version = "0.10.0", default-features = false }
futures-executor = { version = "0.3", features = ["thread-pool"] }
futures-util = { version = "0.3", features = ["channel"] }
lazy_static = "1.4.0"
@ -35,3 +30,10 @@ features = [
"Win32_Storage_FileSystem",
"Win32_UI_WindowsAndMessaging",
]
[dev-dependencies]
crash-handler = "0.6"
minidumper = "0.8"
json = "0.12.4"
sadness-generator = "0.5.0"
tempfile = "3.3.0"

View File

@ -0,0 +1,6 @@
# Testing
To run tests, comment out the `mozilla-central-workspace-hack` dependency, then run `cargo test`.
Otherwise, the dependency won't have the feature we need enabled. This is because the crate is
excluded from the workspace (as we don't want to vendor the dev-dependencies).
The `Cargo.lock` in this directory corresponds to this configuration.

View File

@ -1,662 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cfg_aliases"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
[[package]]
name = "concurrent-queue"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crash-context"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b85cef661eeca0c6675116310936972c520ebb0a33ddef16fd7efc957f4c1288"
dependencies = [
"cfg-if",
"libc",
"mach2",
]
[[package]]
name = "crash-handler"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed56890cb467e9ff5e6fe95254571beebb301990b5c7df5e97a7b203e4f0c4f0"
dependencies = [
"cfg-if",
"crash-context",
"libc",
"mach2",
"parking_lot",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
dependencies = [
"cfg-if",
]
[[package]]
name = "debugid"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d"
dependencies = [
"uuid",
]
[[package]]
name = "errno"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "fastrand"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
[[package]]
name = "goblin"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b363a30c165f666402fe6a3024d3bec7ebc898f96a4a23bd1c99f8dbf3f4f47"
dependencies = [
"log",
"plain",
"scroll",
]
[[package]]
name = "hermit-abi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "json"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
[[package]]
name = "libc"
version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "linux-raw-sys"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
name = "lock_api"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
[[package]]
name = "mach2"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8"
dependencies = [
"libc",
]
[[package]]
name = "memmap2"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322"
dependencies = [
"libc",
]
[[package]]
name = "memoffset"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
dependencies = [
"autocfg",
]
[[package]]
name = "minidump-analyzer-test"
version = "0.1.0"
dependencies = [
"crash-handler",
"json",
"minidumper",
"sadness-generator",
"tempfile",
]
[[package]]
name = "minidump-common"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bb6eaf88cc770fa58e6ae721cf2e40c2ca6a4c942ae8c7aa324d680bd3c6717"
dependencies = [
"bitflags 2.5.0",
"debugid",
"num-derive",
"num-traits",
"range-map",
"scroll",
"smart-default",
]
[[package]]
name = "minidump-writer"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abcd9c8a1e6e1e9d56ce3627851f39a17ea83e17c96bc510f29d7e43d78a7d"
dependencies = [
"bitflags 2.5.0",
"byteorder",
"cfg-if",
"crash-context",
"goblin",
"libc",
"log",
"mach2",
"memmap2",
"memoffset",
"minidump-common",
"nix",
"procfs-core",
"scroll",
"tempfile",
"thiserror",
]
[[package]]
name = "minidumper"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b99697a8483221ca2d163aedcbee44f3851c4f5916ab0e96ae6397fb6b6deb2"
dependencies = [
"cfg-if",
"crash-context",
"libc",
"log",
"minidump-writer",
"parking_lot",
"polling",
"scroll",
"thiserror",
"uds",
]
[[package]]
name = "nix"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
dependencies = [
"bitflags 2.5.0",
"cfg-if",
"cfg_aliases",
"libc",
]
[[package]]
name = "num-derive"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets 0.48.0",
]
[[package]]
name = "pin-project-lite"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
[[package]]
name = "plain"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
[[package]]
name = "polling"
version = "3.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "645493cf344456ef24219d02a768cf1fb92ddf8c92161679ae3d91b91a637be3"
dependencies = [
"cfg-if",
"concurrent-queue",
"hermit-abi",
"pin-project-lite",
"rustix",
"tracing",
"windows-sys",
]
[[package]]
name = "proc-macro2"
version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406"
dependencies = [
"unicode-ident",
]
[[package]]
name = "procfs-core"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d3554923a69f4ce04c4a754260c338f505ce22642d3830e049a399fc2059a29"
dependencies = [
"bitflags 2.5.0",
"hex",
]
[[package]]
name = "quote"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
dependencies = [
"proc-macro2",
]
[[package]]
name = "range-map"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12a5a2d6c7039059af621472a4389be1215a816df61aa4d531cfe85264aee95f"
dependencies = [
"num-traits",
]
[[package]]
name = "redox_syscall"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "rustix"
version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [
"bitflags 2.5.0",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "sadness-generator"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cdaf5eed5d14ebb5d32d864b419e760c9ddf123bcde880a30a90063494592d7"
dependencies = [
"libc",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "scroll"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ab8598aa408498679922eff7fa985c25d58a90771bd6be794434c5277eab1a6"
dependencies = [
"scroll_derive",
]
[[package]]
name = "scroll_derive"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f81c2fde025af7e69b1d1420531c8a8811ca898919db177141a85313b1cb932"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "smallvec"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]]
name = "smart-default"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eb01866308440fc64d6c44d9e86c5cc17adfe33c4d6eed55da9145044d0ffc1"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "syn"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tempfile"
version = "3.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
dependencies = [
"cfg-if",
"fastrand",
"rustix",
"windows-sys",
]
[[package]]
name = "thiserror"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing"
version = "0.1.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
dependencies = [
"cfg-if",
"pin-project-lite",
"tracing-core",
]
[[package]]
name = "tracing-core"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
[[package]]
name = "uds"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "885c31f06fce836457fe3ef09a59f83fe8db95d270b11cd78f40a4666c4d1661"
dependencies = [
"libc",
]
[[package]]
name = "unicode-ident"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
[[package]]
name = "uuid"
version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa2982af2eec27de306107c027578ff7f423d65f7250e40ce0fea8f45248b81"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.5",
]
[[package]]
name = "windows-targets"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
dependencies = [
"windows_aarch64_gnullvm 0.48.0",
"windows_aarch64_msvc 0.48.0",
"windows_i686_gnu 0.48.0",
"windows_i686_msvc 0.48.0",
"windows_x86_64_gnu 0.48.0",
"windows_x86_64_gnullvm 0.48.0",
"windows_x86_64_msvc 0.48.0",
]
[[package]]
name = "windows-targets"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [
"windows_aarch64_gnullvm 0.52.5",
"windows_aarch64_msvc 0.52.5",
"windows_i686_gnu 0.52.5",
"windows_i686_gnullvm",
"windows_i686_msvc 0.52.5",
"windows_x86_64_gnu 0.52.5",
"windows_x86_64_gnullvm 0.52.5",
"windows_x86_64_msvc 0.52.5",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_i686_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"

View File

@ -1,16 +0,0 @@
[package]
name = "minidump-analyzer-test"
description = "Tests minidump-analyzer using crates which we won't vendor."
version = "0.1.0"
authors = ["Alex Franchuk <alex.franchuk@gmail.com>"]
license = "MPL-2.0"
edition = "2018"
[dev-dependencies]
crash-handler = "0.6"
minidumper = "0.8"
json = "0.12.4"
sadness-generator = "0.5.0"
tempfile = "3.3.0"
[workspace]

View File

@ -1,6 +0,0 @@
To run the tests, execute:
`MINIDUMP_ANALYZER=/path/to/minidump-analyzer cargo test`
You likely want to set `MINIDUMP_ANALYZER` to `OBJDIR/dist/bin/minidump-analyzer` after running
`mach build`.

View File

@ -1,3 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

View File

@ -1,7 +0,0 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
RUST_PROGRAMS = ["minidump-analyzer"]

View File

@ -3,7 +3,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use anyhow::Context;
use clap::Parser;
use futures_executor::{block_on, ThreadPool};
use minidump::{
system_info::Cpu, Minidump, MinidumpException, MinidumpMemoryList, MinidumpMiscInfo,
@ -14,10 +13,11 @@ use minidump_unwind::{
symbols::debuginfo::DebugInfoSymbolProvider, symbols::SymbolProvider, walk_stack, CallStack,
CallStackInfo, SystemInfo,
};
use std::borrow::Cow;
use std::fmt::Debug;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use serde_json::{json, Value as JsonValue};
@ -26,26 +26,147 @@ use serde_json::{json, Value as JsonValue};
mod windows;
/// Analyze a minidump file to augment a corresponding .extra file with stack trace information.
#[derive(Parser, Debug)]
#[clap(version)]
struct Args {
/// Generate all stacks, rather than just those of the crashing thread.
#[clap(long = "full")]
#[derive(Debug)]
pub struct MinidumpAnalyzer<'a> {
all_stacks: bool,
/// The minidump file to analyze.
minidump: PathBuf,
minidump: &'a Path,
extras: Option<&'a Path>,
}
impl Args {
/// Get the extra file.
///
/// This file is derived from the minidump file path.
pub fn extra_file(&self) -> PathBuf {
let mut ret = self.minidump.clone();
ret.set_extension("extra");
ret
impl<'a> MinidumpAnalyzer<'a> {
/// Create a new MinidumpAnalyzer for the given minidump file.
pub fn new(minidump: &'a Path) -> Self {
MinidumpAnalyzer {
all_stacks: false,
minidump,
extras: None,
}
}
/// Generate all stacks, rather than just those of the crashing thread.
pub fn all_threads(mut self, value: bool) -> Self {
self.all_stacks = value;
self
}
/// Set the extras file explicitly, rather than deriving from the minidump path.
pub fn extras_file(mut self, path: &'a Path) -> Self {
self.extras = Some(path);
self
}
/// Analyze the thread(s) and put stacks in the associated .extra file.
pub fn analyze(self) -> anyhow::Result<()> {
let extra_file = self
.extras
.map(Cow::Borrowed)
.unwrap_or_else(|| Cow::Owned(extra_path_from_minidump(&self.minidump)));
let extra_file = extra_file.as_ref();
log::info!("minidump file path: {}", self.minidump.display());
log::info!("extra file path: {}", extra_file.display());
let minidump = Minidump::read_path(&self.minidump).context("while reading minidump")?;
let mut extra_json = parse_extra_file(extra_file)?;
let proc = processor::Processor::new(&minidump)?;
let (crashing_thread_idx, call_stacks) = proc.get_call_stacks(self.all_stacks)?;
let crash_type = proc.crash_reason();
let crash_address = proc.crash_address();
let used_modules = get_used_modules(&call_stacks, proc.main_module());
// Set the `StackTraces` field in the extra JSON data.
extra_json["StackTraces"] = json!({
"status": call_stack_status(&call_stacks),
"crash_info": {
"type": crash_type,
"address": format!("{crash_address:#x}"),
"crashing_thread": crashing_thread_idx
// TODO: "assertion" when there's no crash indicator
},
"main_module": proc.main_module().and_then(|m| module_index(&used_modules, m)),
"modules": used_modules.iter().map(|module| {
let code_file = module.code_file();
let code_file_path: &std::path::Path = code_file.as_ref().as_ref();
json!({
"base_addr": format!("{:#x}", module.base_address()),
"end_addr": format!("{:#x}", module.base_address() + module.size()),
"filename": code_file_path.file_name().map(|s| s.to_string_lossy()),
"code_id": module.code_identifier().as_ref().map(|id| id.as_str()),
"debug_file": module.debug_file().as_deref(),
"debug_id": module.debug_identifier().map(|debug| debug.breakpad().to_string()),
"version": module.version().as_deref()
})
}).collect::<Vec<_>>(),
"unloaded_modules": proc.unloaded_modules().map(|module| {
let code_file = module.code_file();
let code_file_path: &std::path::Path = code_file.as_ref().as_ref();
json!({
"base_addr": format!("{:#x}", module.base_address()),
"end_addr": format!("{:#x}", module.base_address() + module.size()),
"filename": code_file_path.file_name().map(|s| s.to_string_lossy()),
"code_id": module.code_identifier().as_ref().map(|id| id.as_str()),
})
}).collect::<Vec<_>>(),
"threads": call_stacks.iter().map(|call_stack| call_stack_to_json(call_stack, &used_modules)).collect::<Vec<_>>()
});
// StackTraces should not have null values (upstream processing expects the values to be
// omitted).
remove_nulls(&mut extra_json["StackTraces"]);
let module_signature_info = proc.module_signature_info();
if !module_signature_info.is_null() {
// ModuleSignatureInfo is sent as a crash annotation so must be string. This differs from
// StackTraces which isn't actually sent (it's just read and removed by the crash
// reporter client).
extra_json["ModuleSignatureInfo"] = serde_json::to_string(&module_signature_info)
.unwrap()
.into();
}
std::fs::write(extra_file, extra_json.to_string())
.context("while writing modified extra file")?;
Ok(())
}
}
fn extra_path_from_minidump(minidump: &Path) -> PathBuf {
let mut ret = minidump.to_owned();
ret.set_extension("extra");
ret
}
/// Parse the extra file (JSON).
fn parse_extra_file(path: &Path) -> anyhow::Result<JsonValue> {
let mut extra_file_content = String::new();
File::open(path)
.context("while opening extra file")?
.read_to_string(&mut extra_file_content)
.context("while reading extra file")?;
serde_json::from_str(&extra_file_content).context("while parsing extra file JSON")
}
/// Get a list of modules used in call stacks, ordered by base address.
fn get_used_modules<'a>(
call_stacks: impl IntoIterator<Item = &'a CallStack>,
main_module: Option<&'a MinidumpModule>,
) -> Vec<&'a MinidumpModule> {
let mut v = call_stacks
.into_iter()
.flat_map(|call_stack| call_stack.frames.iter())
.filter_map(|frame| frame.module.as_ref())
// Always include the main module.
.chain(main_module)
.collect::<Vec<_>>();
v.sort_by_key(|m| m.base_address());
v.dedup_by_key(|m| m.base_address());
v
}
mod processor {
@ -61,6 +182,7 @@ mod processor {
module_list: MinidumpModuleList,
unloaded_module_list: MinidumpUnloadedModuleList,
memory_list: UnifiedMemoryList<'a>,
thread_list: MinidumpThreadList<'a>,
system_info: MinidumpSystemInfo,
processor_system_info: SystemInfo,
exception: MinidumpException<'a>,
@ -110,6 +232,7 @@ mod processor {
let memory_list = minidump
.get_stream::<MinidumpMemoryList>()
.unwrap_or_default();
let thread_list = minidump.get_stream::<MinidumpThreadList>()?;
let exception = minidump.get_stream::<MinidumpException>()?;
// TODO Something like SystemInfo::current() to get the active system's info?
@ -140,6 +263,7 @@ mod processor {
module_list,
unloaded_module_list,
memory_list: UnifiedMemoryList::Memory(memory_list),
thread_list,
system_info,
processor_system_info,
exception,
@ -154,15 +278,25 @@ mod processor {
&self.context.system_info
}
/// Get the minidump exception.
pub fn exception(&self) -> &MinidumpException {
&self.context.exception
/// Get the crash reason string.
pub fn crash_reason(&self) -> String {
self.context
.exception
.get_crash_reason(self.system_info().os, self.system_info().cpu)
.to_string()
}
/// Get the crash address.
pub fn crash_address(&self) -> u64 {
self.context
.exception
.get_crash_address(self.system_info().os, self.system_info().cpu)
}
/// Get call stacks for the given threads.
///
/// Call stacks will be concurrently calculated.
pub fn thread_call_stacks<'b>(
fn thread_call_stacks<'b>(
&self,
threads: impl IntoIterator<Item = &'b MinidumpThread<'b>>,
) -> anyhow::Result<Vec<CallStack>> {
@ -176,6 +310,35 @@ mod processor {
.collect())
}
/// Get the call stacks to process.
///
/// The first value in the returned tuple is the crashing thread index. The second value is the
/// set of call stacks to process.
pub fn get_call_stacks(&self, all_stacks: bool) -> anyhow::Result<(usize, Vec<CallStack>)> {
// Derive additional arguments used in stack walking.
let crashing_thread = self
.context
.thread_list
.get_thread(self.context.exception.get_crashing_thread_id())
.ok_or(anyhow::anyhow!(
"exception thread id missing in thread list"
))?;
Ok(if all_stacks {
(
self.context
.thread_list
.threads
.iter()
.position(|t| t.raw.thread_id == crashing_thread.raw.thread_id)
.expect("get_thread() returned a thread that doesn't exist"),
self.thread_call_stacks(&self.context.thread_list.threads)?,
)
} else {
(0, self.thread_call_stacks([crashing_thread])?)
})
}
/// Get all modules, ordered by address.
#[cfg(windows)]
pub fn ordered_modules(&self) -> impl Iterator<Item = &MinidumpModule> {
@ -303,136 +466,6 @@ impl SymbolProvider for BoxedSymbolProvider {
}
}
use processor::Processor;
pub fn main() {
env_logger::init();
if let Err(e) = try_main() {
eprintln!("{e}");
std::process::exit(1);
}
}
fn try_main() -> anyhow::Result<()> {
let args = Args::parse();
let extra_file = args.extra_file();
log::info!("minidump file path: {}", args.minidump.display());
log::info!("extra file path: {}", extra_file.display());
let minidump = Minidump::read_path(&args.minidump).context("while reading minidump")?;
let mut extra_json: JsonValue = {
let mut extra_file_content = String::new();
File::open(&extra_file)
.context("while opening extra file")?
.read_to_string(&mut extra_file_content)
.context("while reading extra file")?;
serde_json::from_str(&extra_file_content).context("while parsing extra file JSON")?
};
// Read relevant information from the minidump.
let proc = Processor::new(&minidump)?;
let thread_list = minidump.get_stream::<MinidumpThreadList>()?;
// Derive additional arguments used in stack walking.
let crashing_thread = thread_list
.get_thread(proc.exception().get_crashing_thread_id())
.ok_or(anyhow::anyhow!(
"exception thread id missing in thread list"
))?;
let (crashing_thread_idx, call_stacks) = if args.all_stacks {
(
thread_list
.threads
.iter()
.position(|t| t.raw.thread_id == crashing_thread.raw.thread_id)
.expect("get_thread() returned a thread that doesn't exist"),
proc.thread_call_stacks(&thread_list.threads)?,
)
} else {
(0, proc.thread_call_stacks([crashing_thread])?)
};
let crash_type = proc
.exception()
.get_crash_reason(proc.system_info().os, proc.system_info().cpu)
.to_string();
let crash_address = proc
.exception()
.get_crash_address(proc.system_info().os, proc.system_info().cpu);
let used_modules = {
let mut v = call_stacks
.iter()
.flat_map(|call_stack| call_stack.frames.iter())
.filter_map(|frame| frame.module.as_ref())
// Always include the main module.
.chain(proc.main_module())
.collect::<Vec<_>>();
v.sort_by_key(|m| m.base_address());
v.dedup_by_key(|m| m.base_address());
v
};
extra_json["StackTraces"] = json!({
"status": call_stack_status(&call_stacks),
"crash_info": {
"type": crash_type,
"address": format!("{crash_address:#x}"),
"crashing_thread": crashing_thread_idx
// TODO: "assertion" when there's no crash indicator
},
"main_module": proc.main_module().and_then(|m| module_index(&used_modules, m)),
"modules": used_modules.iter().map(|module| {
let code_file = module.code_file();
let code_file_path: &std::path::Path = code_file.as_ref().as_ref();
json!({
"base_addr": format!("{:#x}", module.base_address()),
"end_addr": format!("{:#x}", module.base_address() + module.size()),
"filename": code_file_path.file_name().map(|s| s.to_string_lossy()),
"code_id": module.code_identifier().as_ref().map(|id| id.as_str()),
"debug_file": module.debug_file().as_deref(),
"debug_id": module.debug_identifier().map(|debug| debug.breakpad().to_string()),
"version": module.version().as_deref()
})
}).collect::<Vec<_>>(),
"unloaded_modules": proc.unloaded_modules().map(|module| {
let code_file = module.code_file();
let code_file_path: &std::path::Path = code_file.as_ref().as_ref();
json!({
"base_addr": format!("{:#x}", module.base_address()),
"end_addr": format!("{:#x}", module.base_address() + module.size()),
"filename": code_file_path.file_name().map(|s| s.to_string_lossy()),
"code_id": module.code_identifier().as_ref().map(|id| id.as_str()),
})
}).collect::<Vec<_>>(),
"threads": call_stacks.iter().map(|call_stack| call_stack_to_json(call_stack, &used_modules)).collect::<Vec<_>>()
});
// StackTraces should not have null values (upstream processing expects the values to be
// omitted).
remove_nulls(&mut extra_json["StackTraces"]);
let module_signature_info = proc.module_signature_info();
if !module_signature_info.is_null() {
// ModuleSignatureInfo is sent as a crash annotation so must be string. This differs from
// StackTraces which isn't actually sent (it's just read and removed by the crash
// reporter client).
extra_json["ModuleSignatureInfo"] = serde_json::to_string(&module_signature_info)
.unwrap()
.into();
}
std::fs::write(&extra_file, extra_json.to_string())
.context("while writing modified extra file")?;
Ok(())
}
/// Get the index of `needle` in `modules`.
fn module_index(modules: &[&MinidumpModule], needle: &MinidumpModule) -> Option<usize> {
modules

View File

@ -17,7 +17,7 @@ use windows_sys::Win32::{
};
// Our windows-targets doesn't link wintrust correctly.
#[link(name = "wintrust", kind = "static")]
#[link(name = "wintrust")]
extern "C" {}
type DWORD = u32;

View File

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use minidump_analyzer::MinidumpAnalyzer;
use std::fs::File;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
@ -119,10 +120,6 @@ fn analyze_basic_minidump() {
let minidump_file = dir.path().join("mini.dump");
let extra_file = dir.path().join("mini.extra");
let Some(analyzer) = std::env::var_os("MINIDUMP_ANALYZER") else {
panic!("Specify the path to the minidump analyzer binary as the MINIDUMP_ANALYZER environment variable.");
};
// Create minidump from test.
write_minidump(&minidump_file, FailureType::RaiseAbort);
@ -132,19 +129,7 @@ fn analyze_basic_minidump() {
write!(&mut extra, "{{}}").expect("failed to write to extra json file");
}
// Run minidump-analyzer
{
let output = Command::new(analyzer)
.env("RUST_BACKTRACE", "1")
.arg(&minidump_file)
.output()
.expect("failed to run minidump-analyzer");
assert!(
output.status.success(),
"stderr:\n{}",
std::str::from_utf8(&output.stderr).unwrap()
);
}
MinidumpAnalyzer::new(&minidump_file).analyze().unwrap();
// Check the output JSON
// The stack trace will actually be in cargo. It forks and execs the test program; there is no

View File

@ -64,10 +64,7 @@ if CONFIG["MOZ_CRASHREPORTER"]:
DIRS += ["rust_minidump_writer_linux"]
if CONFIG["OS_TARGET"] != "Android":
DIRS += [
"client/app",
"minidump-analyzer",
]
DIRS += ["client/app"]
DIRS += [
"mozannotation_client",

View File

@ -103,24 +103,25 @@ function getMinidump() {
return null;
}
function getMinidumpAnalyzerPath() {
function getCrashReporterPath() {
const binPrefix =
AppConstants.platform === "macosx"
? "crashreporter.app/Contents/MacOS/"
: "";
const binSuffix = AppConstants.platform === "win" ? ".exe" : "";
const exeName = "minidump-analyzer" + binSuffix;
const exeName = binPrefix + "crashreporter" + binSuffix;
let exe = Services.dirsvc.get("GreBinD", Ci.nsIFile);
exe.append(exeName);
exe.appendRelativePath(exeName);
return exe;
}
function runMinidumpAnalyzer(dumpFile, additionalArgs) {
let bin = getMinidumpAnalyzerPath();
function runMinidumpAnalyzer(dumpFile) {
let bin = getCrashReporterPath();
let process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
process.init(bin);
let args = [];
if (additionalArgs) {
args = args.concat(additionalArgs);
}
let args = ["--analyze"];
args.push(dumpFile.path);
process.run(true /* blocking */, args, args.length);
}

View File

@ -182,20 +182,19 @@ function assertStack(stack, expected) {
}
}
// Performs a crash, runs minidump-analyzer, and checks expected stack analysis.
// Performs a crash, runs crashreporter minidump analyzer, and checks expected
// stack analysis.
//
// how: The crash to perform. Constants defined in both CrashTestUtils.sys.mjs
// and nsTestCrasher.cpp (i.e. CRASH_X64CFI_PUSH_NONVOL)
// expectedStack: An array of {"symbol", "trust"} where trust is "cfi",
// "context", "scan", et al. May be null if you don't need to check the stack.
// minidumpAnalyzerArgs: An array of additional arguments to pass to
// minidump-analyzer.exe.
async function do_x64CFITest(how, expectedStack, minidumpAnalyzerArgs) {
async function do_x64CFITest(how, expectedStack) {
// Setup is run in the subprocess so we cannot use any closures.
let setupFn = "crashType = CrashTestUtils." + how + ";";
let callbackFn = async function (minidumpFile, extra, extraFile) {
runMinidumpAnalyzer(minidumpFile, minidumpAnalyzerArgs);
runMinidumpAnalyzer(minidumpFile);
// Refresh updated extra data
extra = await IOUtils.readJSON(extraFile.path);

View File

@ -51,7 +51,6 @@ crashreporter-crash-details = You can view details of this crash at { $url }.
# Error strings
crashreporter-error-minidump-analyzer = Failed to run minidump-analyzer
# $path (String) - the file path
crashreporter-error-opening-file = Failed to open file ({ $path })
# $path (String) - the file path