Repository: luser/rust-minidump
Pull request #486 (2022-03-08 15:13:53)

Update deps
- Update scroll to 0.11
- Update clap to 3.1
- Update fuzz lockfile

This updates scroll to 0.11, which aligns with the version used by goblin (though not pdb), and clap to 3.1, only making changes to remove deprecation warnings/renames, but otherwise not changing it to the derive macros that are now built in to clap. During this I noticed that the fuzz lockfile was severely outdated so update it.

Gankra: Oh nice, it looks like clap 3 fixed the newlines in `--help`, so we should be able to remove my grody `\n\n\n`'s from all of the help strings. Otherwise the new output looks better.

Please also take a copy of the --help-markdown output and update the end of minidump-stackwalk/README.md with it.

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 7cf1a4c..13cf272 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -8,15 +8,6 @@ version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
 
-[[package]]
-name = "ansi_term"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
-dependencies = [
- "winapi",
-]
-
 [[package]]
 name = "arbitrary"
 version = "1.1.0"
@@ -147,18 +138,19 @@ checksum = "b0fc239e0f6cb375d2402d48afb92f76f5404fd1df208a41930ec81eda078bea"
 
 [[package]]
 name = "clap"
-version = "2.34.0"
+version = "3.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
+checksum = "d8c93436c21e4698bacadf42917db28b23017027a4deccb35dbe47a7e7840123"
 dependencies = [
- "ansi_term",
  "atty",
  "bitflags",
+ "indexmap",
+ "lazy_static",
+ "os_str_bytes",
  "strsim",
- "term_size",
+ "termcolor",
+ "terminal_size",
  "textwrap",
- "unicode-width",
- "vec_map",
 ]
 
 [[package]]
@@ -687,7 +679,6 @@ version = "0.9.6"
 dependencies = [
  "async-trait",
  "breakpad-symbols",
- "clap",
  "debugid",
  "doc-comment",
  "log",
@@ -880,6 +871,15 @@ dependencies = [
  "vcpkg",
 ]
 
+[[package]]
+name = "os_str_bytes"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
+dependencies = [
+ "memchr",
+]
+
 [[package]]
 name = "parking_lot"
 version = "0.11.2"
@@ -1036,18 +1036,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
 
 [[package]]
 name = "scroll"
-version = "0.10.2"
+version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec"
+checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da"
 dependencies = [
  "scroll_derive",
 ]
 
 [[package]]
 name = "scroll_derive"
-version = "0.10.5"
+version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aaaae8f38bb311444cfb7f1979af0bc9240d95795f75f9ceddf6a59b79ceffa0"
+checksum = "bdbda6ac5cd1321e724fa9cee216f3a61885889b896f073b8f82322789c5250e"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1193,9 +1193,9 @@ dependencies = [
 
 [[package]]
 name = "strsim"
-version = "0.8.0"
+version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
 
 [[package]]
 name = "syn"
@@ -1232,16 +1232,6 @@ dependencies = [
  "winapi",
 ]
 
-[[package]]
-name = "term_size"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9"
-dependencies = [
- "libc",
- "winapi",
-]
-
 [[package]]
 name = "termcolor"
 version = "1.1.2"
@@ -1272,12 +1262,11 @@ dependencies = [
 
 [[package]]
 name = "textwrap"
-version = "0.11.0"
+version = "0.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
+checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
 dependencies = [
- "term_size",
- "unicode-width",
+ "terminal_size",
 ]
 
 [[package]]
@@ -1438,12 +1427,6 @@ dependencies = [
  "tinyvec",
 ]
 
-[[package]]
-name = "unicode-width"
-version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
-
 [[package]]
 name = "unicode-xid"
 version = "0.2.2"
@@ -1474,12 +1457,6 @@ version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
 
-[[package]]
-name = "vec_map"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
-
 [[package]]
 name = "want"
 version = "0.3.0"
diff --git a/breakpad-symbols/src/lib.rs b/breakpad-symbols/src/lib.rs
index 90c2bdc..796a393 100644
--- a/breakpad-symbols/src/lib.rs
+++ b/breakpad-symbols/src/lib.rs
@@ -212,6 +212,10 @@ pub enum SymbolError {
     /// In this case other symbol providers may still be able to find it!
     #[error("symbol file not found")]
     NotFound,
+    /// The module was lacking either the debug file or debug id, as such the
+    /// path of the symbol could not be generated.
+    #[error("the debug file or id were missing")]
+    MissingDebugFileOrId,
     /// Symbol file could not be loaded into memory.
     #[error("couldn't read input stream")]
     LoadError(#[from] std::io::Error),
@@ -435,7 +439,7 @@ fn commit_cache_file(mut temp: NamedTempFile, final_path: &Path, url: &Url) -> i
 async fn fetch_symbol_file(
     client: &Client,
     base_url: &Url,
-    rel_path: &str,
+    module: &(dyn Module + Sync),
     cache: &Path,
     tmp: &Path,
 ) -> Result<SymbolFile, SymbolError> {
@@ -449,7 +453,14 @@ async fn fetch_symbol_file(
     // give up on caching but let the parse+download continue.
 
     // First try to GET the file from a server
-    let url = base_url.join(rel_path).map_err(|_| SymbolError::NotFound)?;
+    let rel_path = relative_symbol_path(module, "sym").ok_or(SymbolError::MissingDebugFileOrId)?;
+    let mut url = base_url
+        .join(&rel_path)
+        .map_err(|_| SymbolError::NotFound)?;
+    let code_id = module.code_identifier().unwrap_or_default();
+    url.query_pairs_mut()
+        .append_pair("code_id", code_id.as_str())
+        .append_pair("code_file", &module.code_file());
     debug!("Trying {}", url);
     let res = client
         .get(url.clone())
@@ -504,13 +515,11 @@ impl SymbolSupplier for HttpSymbolSupplier {
             return local_result;
         }
         // Now try urls
-        if let Some(rel_path) = relative_symbol_path(module, "sym") {
-            for url in &self.urls {
-                if let Ok(file) =
-                    fetch_symbol_file(&self.client, url, &rel_path, &self.cache, &self.tmp).await
-                {
-                    return Ok(file);
-                }
+        for url in &self.urls {
+            if let Ok(file) =
+                fetch_symbol_file(&self.client, url, module, &self.cache, &self.tmp).await
+            {
+                return Ok(file);
             }
         }
         // If we get this far, we have failed to find anything
@@ -741,6 +750,9 @@ impl Symbolizer {
                     Err(SymbolError::NotFound) => {
                         stats.loaded_symbols = false;
                     }
+                    Err(SymbolError::MissingDebugFileOrId) => {
+                        stats.loaded_symbols = false;
+                    }
                     Err(SymbolError::LoadError(_)) => {
                         stats.loaded_symbols = false;
                     }
diff --git a/minidump-common/Cargo.toml b/minidump-common/Cargo.toml
index 0d76ea1..6d30150 100644
--- a/minidump-common/Cargo.toml
+++ b/minidump-common/Cargo.toml
@@ -20,5 +20,5 @@ enum-primitive-derive = "0.2.2"
 log = "0.4.1"
 num-traits = "0.2"
 range-map = "0.1.5"
-scroll = { version = "0.10.2", features = ["derive"] }
+scroll = { version = "0.11.0", features = ["derive"] }
 smart-default = "0.6.0"
diff --git a/minidump-processor/Cargo.toml b/minidump-processor/Cargo.toml
index adda3d2..5672079 100644
--- a/minidump-processor/Cargo.toml
+++ b/minidump-processor/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "minidump-processor"
-description = "A library and tool for producing stack traces and other useful information from minidump files."
+description = "A library for producing stack traces and other useful information from minidump files."
 version = "0.9.6"
 authors = ["Ted Mielczarek <ted@mielczarek.org>"]
 license = "MIT"
@@ -22,12 +22,11 @@ symbolic-syms = []
 [dependencies]
 async-trait = "0.1.51"
 breakpad-symbols = { version = "0.9.6", path = "../breakpad-symbols", optional = true }
-clap = "2.34"
 debugid = "0.7.3"
 log = "0.4"
 memmap2 = "0.5.3"
 minidump = { version = "0.9.6", path = "../minidump" }
-scroll = "0.10.2"
+scroll = "0.11.0"
 serde = { version = "1.0", features = ["derive"] }
 serde_json = "1.0"
 thiserror = "1.0.30"
@@ -36,4 +35,4 @@ thiserror = "1.0.30"
 doc-comment = "0.3.3"
 synth-minidump = { path = "../synth-minidump" }
 test-assembler = "0.1.6"
-tokio =  { version = "1.12.0", features = ["full"] }
+tokio = { version = "1.12.0", features = ["full"] }
diff --git a/minidump-processor/fuzz/fuzz_targets/walk_stack.rs b/minidump-processor/fuzz/fuzz_targets/walk_stack.rs
index 2499102..e8aa16c 100644
--- a/minidump-processor/fuzz/fuzz_targets/walk_stack.rs
+++ b/minidump-processor/fuzz/fuzz_targets/walk_stack.rs
@@ -3,8 +3,9 @@ use libfuzzer_sys::fuzz_target;
 
 use minidump::{MinidumpContext, MinidumpContextValidity, MinidumpMemory};
 use minidump::{MinidumpModule, MinidumpModuleList};
+use minidump::system_info::{Cpu, Os};
 use minidump_processor::walk_stack;
-use minidump_processor::{string_symbol_supplier, CallStack, Symbolizer};
+use minidump_processor::{string_symbol_supplier, CallStack, Symbolizer, SystemInfo};
 use std::collections::HashMap;
 use test_assembler::Section;
 
@@ -43,6 +44,15 @@ impl TestFixture {
             size,
             bytes: &stack,
         };
+        let system_info = SystemInfo {
+            os: Os::Windows,
+            os_version: None,
+            os_build: None,
+            cpu: Cpu::X86_64,
+            cpu_info: None,
+            cpu_microcode_version: None,
+            cpu_count: 1,
+        };
 
         let symbolizer = Symbolizer::new(string_symbol_supplier(self.symbols.clone()));
 
@@ -51,6 +61,7 @@ impl TestFixture {
                 &Some(&context),
                 Some(&stack_memory),
                 &self.modules,
+                &system_info,
                 &symbolizer,
             )
             .await,
diff --git a/minidump-processor/src/processor.rs b/minidump-processor/src/processor.rs
index d171a91..d563844 100644
--- a/minidump-processor/src/processor.rs
+++ b/minidump-processor/src/processor.rs
@@ -370,9 +370,14 @@ where
 
         let stack_memory = thread.stack_memory(&memory_list);
 
-        let mut stack =
-            stackwalker::walk_stack(&context, stack_memory.as_deref(), &modules, symbol_provider)
-                .await;
+        let mut stack = stackwalker::walk_stack(
+            &context,
+            stack_memory.as_deref(),
+            &modules,
+            &system_info,
+            symbol_provider,
+        )
+        .await;
         stack.thread_id = id;
         for frame in &mut stack.frames {
             // If the frame doesn't have a loaded module, try to find an unloaded module
diff --git a/minidump-processor/src/stackwalker/amd64.rs b/minidump-processor/src/stackwalker/amd64.rs
index 65eb8b0..6e50461 100644
--- a/minidump-processor/src/stackwalker/amd64.rs
+++ b/minidump-processor/src/stackwalker/amd64.rs
@@ -9,9 +9,10 @@
 use crate::process_state::{FrameTrust, StackFrame};
 use crate::stackwalker::unwind::Unwind;
 use crate::stackwalker::CfiStackWalker;
-use crate::SymbolProvider;
+use crate::{SymbolProvider, SystemInfo};
 use log::trace;
 use minidump::format::CONTEXT_AMD64;
+use minidump::system_info::Os;
 use minidump::{
     MinidumpContext, MinidumpContextValidity, MinidumpMemory, MinidumpModuleList,
     MinidumpRawContext,
@@ -108,11 +109,24 @@ fn get_caller_by_frame_pointer<P>(
     callee: &StackFrame,
     stack_memory: &MinidumpMemory<'_>,
     _modules: &MinidumpModuleList,
+    system_info: &SystemInfo,
     _symbol_provider: &P,
 ) -> Option<StackFrame>
 where
     P: SymbolProvider + Sync,
 {
+    // On Windows x64, frame-pointer unwinding purely with the data on the stack
+    // is not possible, as proper unwinding requires access to `UNWIND_INFO`,
+    // because the frame pointer does not necessarily point to the end of the
+    // frame.
+    // In particular, the docs state that:
+    // > [The frame register] offset permits pointing the FP register into the
+    // > middle of the local stack allocation [...]
+    // https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64
+    if system_info.os == Os::Windows {
+        return None;
+    }
+
     trace!("unwind: trying frame pointer");
     if let MinidumpContextValidity::Some(ref which) = callee.context.valid {
         if !which.contains(FRAME_POINTER_REGISTER) {
@@ -416,6 +430,7 @@ impl Unwind for CONTEXT_AMD64 {
         grand_callee: Option<&StackFrame>,
         stack_memory: Option<&MinidumpMemory<'_>>,
         modules: &MinidumpModuleList,
+        system_info: &SystemInfo,
         syms: &P,
     ) -> Option<StackFrame>
     where
@@ -429,7 +444,7 @@ impl Unwind for CONTEXT_AMD64 {
             frame = get_caller_by_cfi(self, callee, grand_callee, stack, modules, syms).await;
         }
         if frame.is_none() {
-            frame = get_caller_by_frame_pointer(self, callee, stack, modules, syms);
+            frame = get_caller_by_frame_pointer(self, callee, stack, modules, system_info, syms);
         }
         if frame.is_none() {
             frame = get_caller_by_scan(self, callee, stack, modules, syms).await;
diff --git a/minidump-processor/src/stackwalker/arm.rs b/minidump-processor/src/stackwalker/arm.rs
index d2e9a2d..f1c0a30 100644
--- a/minidump-processor/src/stackwalker/arm.rs
+++ b/minidump-processor/src/stackwalker/arm.rs
@@ -7,8 +7,9 @@
 use crate::process_state::{FrameTrust, StackFrame};
 use crate::stackwalker::unwind::Unwind;
 use crate::stackwalker::CfiStackWalker;
-use crate::SymbolProvider;
+use crate::{SymbolProvider, SystemInfo};
 use log::trace;
+use minidump::system_info::Os;
 use minidump::{
     CpuContext, MinidumpContext, MinidumpContextValidity, MinidumpMemory, MinidumpModuleList,
     MinidumpRawContext,
@@ -99,11 +100,22 @@ fn get_caller_by_frame_pointer<P>(
     callee: &StackFrame,
     stack_memory: &MinidumpMemory<'_>,
     _modules: &MinidumpModuleList,
+    system_info: &SystemInfo,
     _symbol_provider: &P,
 ) -> Option<StackFrame>
 where
     P: SymbolProvider + Sync,
 {
+    // The ARM manual states that:
+    // > LR can be used for other purposes when it is not required to support
+    // > a return from a subroutine.
+    // In other words, we need to be conservative and treat it as a general
+    // purpose register. Except on iOS, which has stricter conventions around
+    // register use, and does guarantee that LR contains a valid return addr.
+    if system_info.os != Os::Ios {
+        return None;
+    }
+
     trace!("unwind: trying frame pointer");
     // Assume that the standard %fp-using ARM calling convention is in use.
     // The main quirk of this ABI is that the return address doesn't need to
@@ -305,6 +317,7 @@ impl Unwind for ArmContext {
         grand_callee: Option<&StackFrame>,
         stack_memory: Option<&MinidumpMemory<'_>>,
         modules: &MinidumpModuleList,
+        system_info: &SystemInfo,
         syms: &P,
     ) -> Option<StackFrame>
     where
@@ -318,7 +331,7 @@ impl Unwind for ArmContext {
             frame = get_caller_by_cfi(self, callee, grand_callee, stack, modules, syms).await;
         }
         if frame.is_none() {
-            frame = get_caller_by_frame_pointer(self, callee, stack, modules, syms);
+            frame = get_caller_by_frame_pointer(self, callee, stack, modules, system_info, syms);
         }
         if frame.is_none() {
             frame = get_caller_by_scan(self, callee, stack, modules, syms).await;
diff --git a/minidump-processor/src/stackwalker/arm64.rs b/minidump-processor/src/stackwalker/arm64.rs
index 3aa98bc..d534ed5 100644
--- a/minidump-processor/src/stackwalker/arm64.rs
+++ b/minidump-processor/src/stackwalker/arm64.rs
@@ -7,7 +7,7 @@
 use crate::process_state::{FrameTrust, StackFrame};
 use crate::stackwalker::unwind::Unwind;
 use crate::stackwalker::CfiStackWalker;
-use crate::SymbolProvider;
+use crate::{SymbolProvider, SystemInfo};
 use log::trace;
 use minidump::{
     CpuContext, MinidumpContext, MinidumpContextValidity, MinidumpMemory, MinidumpModuleList,
@@ -425,6 +425,7 @@ impl Unwind for ArmContext {
         grand_callee: Option<&StackFrame>,
         stack_memory: Option<&MinidumpMemory<'_>>,
         modules: &MinidumpModuleList,
+        _system_info: &SystemInfo,
         syms: &P,
     ) -> Option<StackFrame>
     where
diff --git a/minidump-processor/src/stackwalker/arm64_old.rs b/minidump-processor/src/stackwalker/arm64_old.rs
index 1f34f99..1afd5bc 100644
--- a/minidump-processor/src/stackwalker/arm64_old.rs
+++ b/minidump-processor/src/stackwalker/arm64_old.rs
@@ -7,7 +7,7 @@
 use crate::process_state::{FrameTrust, StackFrame};
 use crate::stackwalker::unwind::Unwind;
 use crate::stackwalker::CfiStackWalker;
-use crate::SymbolProvider;
+use crate::{SymbolProvider, SystemInfo};
 use log::trace;
 use minidump::{
     CpuContext, MinidumpContext, MinidumpContextValidity, MinidumpMemory, MinidumpModuleList,
@@ -422,6 +422,7 @@ impl Unwind for ArmContext {
         grand_callee: Option<&StackFrame>,
         stack_memory: Option<&MinidumpMemory<'_>>,
         modules: &MinidumpModuleList,
+        _system_info: &SystemInfo,
         syms: &P,
     ) -> Option<StackFrame>
     where
diff --git a/minidump-processor/src/stackwalker/mod.rs b/minidump-processor/src/stackwalker/mod.rs
index c237cb0..6928dff 100644
--- a/minidump-processor/src/stackwalker/mod.rs
+++ b/minidump-processor/src/stackwalker/mod.rs
@@ -11,7 +11,7 @@ mod unwind;
 mod x86;
 
 use crate::process_state::*;
-use crate::{FrameWalker, SymbolProvider};
+use crate::{FrameWalker, SymbolProvider, SystemInfo};
 use log::trace;
 use minidump::*;
 use scroll::ctx::{SizeWith, TryFromCtx};
@@ -85,6 +85,7 @@ async fn get_caller_frame<P>(
     grand_callee_frame: Option<&StackFrame>,
     stack_memory: Option<&MinidumpMemory<'_>>,
     modules: &MinidumpModuleList,
+    system_info: &SystemInfo,
     symbol_provider: &P,
 ) -> Option<StackFrame>
 where
@@ -103,6 +104,7 @@ where
                 grand_callee_frame,
                 stack_memory,
                 modules,
+                system_info,
                 symbol_provider,
             )
             .await
@@ -113,6 +115,7 @@ where
                 grand_callee_frame,
                 stack_memory,
                 modules,
+                system_info,
                 symbol_provider,
             )
             .await
@@ -123,6 +126,7 @@ where
                 grand_callee_frame,
                 stack_memory,
                 modules,
+                system_info,
                 symbol_provider,
             )
             .await
@@ -133,6 +137,7 @@ where
                 grand_callee_frame,
                 stack_memory,
                 modules,
+                system_info,
                 symbol_provider,
             )
             .await
@@ -143,6 +148,7 @@ where
                 grand_callee_frame,
                 stack_memory,
                 modules,
+                system_info,
                 symbol_provider,
             )
             .await
@@ -173,6 +179,7 @@ pub async fn walk_stack<P>(
     maybe_context: &Option<&MinidumpContext>,
     stack_memory: Option<&MinidumpMemory<'_>>,
     modules: &MinidumpModuleList,
+    system_info: &SystemInfo,
     symbol_provider: &P,
 ) -> CallStack
 where
@@ -203,6 +210,7 @@ where
                 grand_callee_frame,
                 stack_memory,
                 modules,
+                system_info,
                 symbol_provider,
             )
             .await;
diff --git a/minidump-processor/src/stackwalker/unwind.rs b/minidump-processor/src/stackwalker/unwind.rs
index 480e25f..64b9dba 100644
--- a/minidump-processor/src/stackwalker/unwind.rs
+++ b/minidump-processor/src/stackwalker/unwind.rs
@@ -2,7 +2,7 @@
 // file at the top-level directory of this distribution.
 
 use crate::process_state::StackFrame;
-use crate::SymbolProvider;
+use crate::{SymbolProvider, SystemInfo};
 use minidump::{MinidumpMemory, MinidumpModuleList};
 
 /// A trait for things that can unwind to a caller.
@@ -15,6 +15,7 @@ pub trait Unwind {
         grand_callee: Option<&StackFrame>,
         stack_memory: Option<&MinidumpMemory<'_>>,
         modules: &MinidumpModuleList,
+        system_info: &SystemInfo,
         symbol_provider: &P,
     ) -> Option<StackFrame>
     where
diff --git a/minidump-processor/src/stackwalker/x86.rs b/minidump-processor/src/stackwalker/x86.rs
index a88df08..15f2df4 100644
--- a/minidump-processor/src/stackwalker/x86.rs
+++ b/minidump-processor/src/stackwalker/x86.rs
@@ -9,7 +9,7 @@
 use crate::process_state::{FrameTrust, StackFrame};
 use crate::stackwalker::unwind::Unwind;
 use crate::stackwalker::CfiStackWalker;
-use crate::SymbolProvider;
+use crate::{SymbolProvider, SystemInfo};
 use log::trace;
 use minidump::format::CONTEXT_X86;
 use minidump::{
@@ -392,6 +392,7 @@ impl Unwind for CONTEXT_X86 {
         grand_callee: Option<&StackFrame>,
         stack_memory: Option<&MinidumpMemory<'_>>,
         modules: &MinidumpModuleList,
+        _system_info: &SystemInfo,
         syms: &P,
     ) -> Option<StackFrame>
     where
diff --git a/minidump-stackwalk/Cargo.toml b/minidump-stackwalk/Cargo.toml
index 6d03aff..b5b2dca 100644
--- a/minidump-stackwalk/Cargo.toml
+++ b/minidump-stackwalk/Cargo.toml
@@ -15,13 +15,13 @@ edition = "2018"
 travis-ci = { repository = "luser/rust-minidump" }
 
 [dependencies]
-clap = { version = "2.34", features=["wrap_help"] }
+clap = { version = "3.1", features = ["cargo", "wrap_help"] }
 log = "0.4"
 minidump = { version = "0.9.6", path = "../minidump" }
 minidump-common = { version = "0.9.6", path = "../minidump-common" }
 minidump-processor = { version = "0.9.6", path = "../minidump-processor" }
 simplelog = "0.11.2"
-tokio =  { version = "1.12.0", features = ["full"] }
+tokio = { version = "1.12.0", features = ["full"] }
 
 [features]
 vendored-openssl = ["openssl/vendored"]
@@ -33,4 +33,4 @@ optional = true
 [dev-dependencies]
 insta = "1.10.0"
 synth-minidump = { version = "0.9.6", path = "../synth-minidump" }
-test-assembler = "0.1.6"
\ No newline at end of file
+test-assembler = "0.1.6"
diff --git a/minidump-stackwalk/README.md b/minidump-stackwalk/README.md
index f2ea5ae..3a070ed 100644
--- a/minidump-stackwalk/README.md
+++ b/minidump-stackwalk/README.md
@@ -1,6 +1,7 @@
 # minidump-stackwalk
 
-[![crates.io](https://img.shields.io/crates/v/minidump-stackwalk.svg)](https://crates.io/crates/minidump-stackwalk) [![](https://docs.rs/minidump-stackwalk/badge.svg)](https://docs.rs/minidump-stackwalk)
+[![crates.io](https://img.shields.io/crates/v/minidump-stackwalk.svg)](https://crates.io/crates/minidump-stackwalk)
+[![docs.rs](https://docs.rs/minidump-stackwalk/badge.svg)](https://docs.rs/minidump-stackwalk)
 
 A CLI frontend for [minidump-processor](https://crates.io/crates/minidump-processor), providing both machine-readable and human-readable digests of a minidump, with backtraces and symbolication.
 
@@ -18,15 +19,11 @@ The easiest way to use this is by `cargo install`ing it:
 Full documentation of the CLI can be found in the "minidump-stackwalk CLI manual" section below
 (`--help` will produce the same output).
 
+## Enabling Extra Analysis
 
-# Enabling Extra Analysis
+The [--features](#--features-features) flag can be used to blindly opt into "more analysis". See that entry in the CLI docs for more details on choosing an appropriate value, but the tl;dr is that you can turn everything on and checkout "what's new" with `--features=unstable-all`.
 
-The [--features](#--features-features) flag can be used to blindly opt into "more analysis". See that
-entry in the CLI docs for more details on choosing an appropriate value, but the tl;dr is that you can
-turn everything on and checkout "what's new" with `--features=unstable-all`.
-
-
-# Output Formats
+## Output Formats
 
 Quick Reference:
 
@@ -42,7 +39,7 @@ If you pass **the --json flag** you will get machine-readable (JSON) output. As
 If you pass **the --human flag**, minidump-stackwalk will output a report in a more human-friendly format with no particular structure. (--brief will make this output less verbose.)
 
 By default, output is written to stdout, but `--output-file=some/file/name.txt` allows you to specify a file to write the output to instead. We will create and completely overwrite the specified file. If there is a fatal error, we will try to avoid writing anything to the output,
-which may result in `--output-file` not being created/cleared at all. 
+which may result in `--output-file` not being created/cleared at all.
 
 Similarly, errors and warnings are written to stderr by default, which can be configured with `--log-file=...`. `--verbose=...` can be used to set the log level (defaults to "error").
 
@@ -50,9 +47,7 @@ If pass **the --cyborg flag** you will get both --human and --json output in one
 
 Finally, **the --dump flag** will get you "raw" output of the minidump, for debugging its contents. The precise meaning of this is purposefully vague; the output will contain whatever we find useful to include for debugging. Most other flags will be fairly irrelevant in this mode, because `minidump_processor` will not be invoked (we only use the `minidump` crate for basic parsing of each stream). This is equivalent to the old minidump_dump tool.
 
-
-
-# Getting Symbols
+## Getting Symbols
 
 minidump-stackwalk can operate without any symbols, but if you provide them you will get richer output in two ways:
 
@@ -65,36 +60,27 @@ To generate those files from your build artifacts, use either [Mozilla's dump_sy
 
 You can then either provide those symbol files directly as `symbols-path` values (passed positionally, see the cli manual below), or indirectly by setting up a symbol server that conforms to mozilla's [Tecken protocol](https://tecken.readthedocs.io/en/latest/download.html) and passing a URL to that server with the `--symbols-url` flag. (The protocol is basically a static file server with a specific path format.)
 
-
-
-
-# Analyzing Firefox Minidumps
+## Analyzing Firefox Minidumps
 
 If you're trying to analyze firefox minidumps, you'll want to point minidump-stackwalk to [Mozilla's Tecken server](https://symbols.mozilla.org/).
 
+```sh
+minidump-stackwalk --symbols-url=https://symbols.mozilla.org/ /path/to/minidump.dmp
 ```
-> minidump-stackwalk --symbols-url=https://symbols.mozilla.org/ /path/to/minidump.dmp
-```
-
-Alternatively, if you want to locally reprocess a crash report on https://crash-stats.mozilla.org (socorro), you may want to use [socc-pair](https://github.com/Gankra/socc-pair), which automates this process (and diffs the local result with the one that server had).
-
-
 
+Alternatively, if you want to locally reprocess a crash report on <https://crash-stats.mozilla.org> (socorro), you may want to use [socc-pair](https://github.com/Gankra/socc-pair), which automates this process (and diffs the local result with the one that server had).
 
-
-
-
-# Debugging Stackwalking
+## Debugging Stackwalking
 
 rust-minidump includes detailed trace-logging of its stackwalker, which you can enabled with `--verbose=trace` (we recommend against running this mode in production, it's *really* verbose, and degenerate inputs may produce enormous logs).
 
 Some tips on reading these logs:
 
 * All stackwalking lines will start with `[TRACE] unwind` (other logs may get interspersed).
-* Each thread's unwind will: 
-  * start with "starting stack unwind" 
+* Each thread's unwind will:
+  * start with "starting stack unwind"
   * end with "finished stack unwind"
-* Each frame's unwind will: 
+* Each frame's unwind will:
   * start with "unwinding \<name\>"
   * end with "\<unwinding method\> seems valid"
   * include the final instruction pointer and stack pointer values at the end
@@ -104,7 +90,7 @@ Some tips on reading these logs:
   * scan
 
 If you see "trying scan" or "trying framepointer", this means the previous
-unwinding method failed. Sometimes the reason for failure will be logged, 
+unwinding method failed. Sometimes the reason for failure will be logged,
 but other times the failure is in a weird place we don't have any logging for.
 If that happens, you can still potentially infer what went wrong based on what
 usually comes after that step.
@@ -126,13 +112,11 @@ If you instead see:
 ```
 
 This suggests the cfi analysis couldn't *even* get to "found symbols for address". So,
-presumably, it *couldn't* find symbols for the current instruction pointer. This may 
+presumably, it *couldn't* find symbols for the current instruction pointer. This may
 be because it didn't map to a known module, or because there were no symbols for that module.
 
-
 Here is an example stackwalking trace:
 
-
 ```text
 [TRACE] unwind: starting stack unwind
 [TRACE] unwind: unwinding NtGetContextThread
@@ -176,12 +160,7 @@ Here is an example stackwalking trace:
 
 (This is a particularly nasty/useless stack to unwind, but it shows the two extreme cases of CFI unwinding in a known function and scan unwinding in a totally unknown function.)
 
-
-
-
-
-
-
+<!-- markdownlint-disable -->
 # minidump-stackwalk CLI manual
 
 > This manual can be regenerated with `minidump-stackwalk --help-markdown please`
@@ -193,7 +172,17 @@ Analyzes minidumps and produces a report (either human-readable or JSON).
 # USAGE
 minidump-stackwalk [FLAGS] [OPTIONS] <minidump> [--] [symbols-path]...
 
-# FLAGS
+# ARGS
+### `<minidump>`
+Path to the minidump file to analyze.
+
+### `<symbols-path>...`
+Path to a symbol file.
+
+If multiple symbols-path values are provided, all symbol files will be merged into
+minidump-stackwalk's symbol database.
+
+# OPTIONS
 ### `--json`
 Emit a machine-readable JSON report.
 
@@ -203,52 +192,33 @@ https://github.com/luser/rust-minidump/blob/master/minidump-processor/json-schem
 ### `--human`
 Emit a human-readable report (the default).
 
-The human-readable report does not have a specified format, and may not have as many details as the JSON
-format. It is intended for quickly inspecting a crash or debugging rust-minidump itself.
-
-### `--dump`
-Dump the 'raw' contents of the minidump.
-
-This is an implementation of the functionality of the old minidump_dump tool. It minimally parses and
-interprets the minidump in an attempt to produce a fairly 'raw' dump of the minidump's contents. This is
-most useful for debugging minidump-stackwalk itself, or a misbehaving minidump generator.
-
-### `--pretty`
-Pretty-print --json output.
-
-### `--brief`
-Provide a briefer --human report.
-
-Only provides the top-level summary and a backtrace of the crashing thread.
-
-### `--recover-function-args`
-**[UNSTABLE]** Heuristically recover function arguments
-
-This is an experimental feature, which currently only shows up in --human output.
-
-### `-h, --help`
-Prints help information
-
-### `-V, --version`
-Prints version information
-
+The human-readable report does not have a specified format, and may not have as many
+details as the JSON format. It is intended for quickly inspecting a crash or debugging
+rust-minidump itself.
 
-# OPTIONS
 ### `--cyborg <cyborg>`
 Combine --human and --json
 
-Because this creates two output streams, you must specify a path to write the --json output to. The --human
-output will be the 'primary' output and default to stdout, which can be configured with --output-file as
-normal.
+Because this creates two output streams, you must specify a path to write the --json
+output to. The --human output will be the 'primary' output and default to stdout, which
+can be configured with --output-file as normal.
+
+### `--dump`
+Dump the 'raw' contents of the minidump.
+
+This is an implementation of the functionality of the old minidump_dump tool. It
+minimally parses and interprets the minidump in an attempt to produce a fairly 'raw'
+dump of the minidump's contents. This is most useful for debugging minidump-stackwalk
+itself, or a misbehaving minidump generator.
 
 ### `--features <features>`
 Specify at a high-level how much analysis to perform.
 
-This flag provides a way to more blindly opt into Extra Analysis without having to know about the specific
-features of minidump-stackwalk. This is equivalent to ProcessorOptions in minidump-processor. The current
-supported values are:
+This flag provides a way to more blindly opt into Extra Analysis without having to know
+about the specific features of minidump-stackwalk. This is equivalent to
+ProcessorOptions in minidump-processor. The current supported values are:
 
-* stable-basic (default): give me solid detailed analysis that most people would want.
+* stable-basic (default): give me solid detailed analysis that most people would want
 * stable-all: turn on extra detailed analysis.
 * unstable-all: turn on the weird and experimental stuff.
 
@@ -256,20 +226,20 @@ stable-all enables: nothing (currently identical to stable-basic)
 
 unstable-all enables: `--recover-function-args`
 
-minidump-stackwalk wants to be a reliable and stable tool, but we also want to be able to introduce new
-features which may be experimental or expensive. To balance these two concerns, new features will usually be
-disabled by default and given a specific flag, but still more easily 'discovered' by anyone who uses this
-flag.
+minidump-stackwalk wants to be a reliable and stable tool, but we also want to be able
+to introduce new features which may be experimental or expensive. To balance these two
+concerns, new features will usually be disabled by default and given a specific flag,
+but still more easily 'discovered' by anyone who uses this flag.
 
-Anyone using minidump-stackwalk who is *really* worried about the output being stable should probably not
-use this flag in production, but its use is recommended for casual
+Anyone using minidump-stackwalk who is *really* worried about the output being stable
+should probably not use this flag in production, but its use is recommended for casual
 human usage or for checking "what's new".
 
-Features under unstable-all may be deprecated and become noops. Features which require additional input
-(such as `--evil-json`) cannot be affected by this, and must still be manually 'discovered'.
+Features under unstable-all may be deprecated and become noops. Features which require
+additional input (such as `--evil-json`) cannot be affected by this, and must still be
+manually 'discovered'.[default: stable-basic]
+\[possible values: stable-basic, stable-all, unstable-all]
 
-
-\[default: stable-basic]  [possible values: stable-basic, stable-all, unstable-all]
 ### `--output-file <output-file>`
 Where to write the output to (if unspecified, stdout is used)
 
@@ -279,25 +249,39 @@ Where to write logs to (if unspecified, stderr is used)
 ### `--verbose <verbose>`
 Set the logging level.
 
-The unwinder has been heavily instrumented with `trace` logging, so if you want to debug why an unwind
-happened the way it did, --verbose=trace is very useful (all unwinder logging will be prefixed with
-`unwind:`).
+The unwinder has been heavily instrumented with `trace` logging, so if you want to debug
+why an unwind happened the way it did, --verbose=trace is very useful (all unwinder
+logging will be prefixed with `unwind:`).[default: error]
+\[possible values: off, error, warn, info, debug, trace]
+
+### `--pretty`
+Pretty-print --json output.
+
+### `--brief`
+Provide a briefer --human report.
 
+Only provides the top-level summary and a backtrace of the crashing thread.
 
-\[default: error]  [possible values: off, error, warn, info, debug, trace]
 ### `--evil-json <evil-json>`
 **[UNSTABLE]** An input JSON file with the extra information.
 
-This is a gross hack for some legacy side-channel information that mozilla uses. It will hopefully be phased
-out and deprecated in favour of just using custom streams in the minidump itself.
+This is a gross hack for some legacy side-channel information that mozilla uses. It will
+hopefully be phased out and deprecated in favour of just using custom streams in the
+minidump itself.
+
+### `--recover-function-args`
+**[UNSTABLE]** Heuristically recover function arguments
 
-### `--symbols-url <symbols-url>...`
+This is an experimental feature, which currently only shows up in --human output.
+
+### `--symbols-url <symbols-url>`
 base URL from which URLs to symbol files can be constructed.
 
-If multiple symbols-url values are provided, they will each be tried in order until one resolves.
+If multiple symbols-url values are provided, they will each be tried in order until one
+resolves.
 
-The server the base URL points to is expected to conform to the Tecken symbol server protocol. For more
-details, see the Tecken docs:
+The server the base URL points to is expected to conform to the Tecken symbol server
+protocol. For more details, see the Tecken docs:
 
 https://tecken.readthedocs.io/en/latest/
 
@@ -306,26 +290,29 @@ Example symbols-url value: https://symbols.mozilla.org/
 ### `--symbols-cache <symbols-cache>`
 A directory in which downloaded symbols can be stored.
 
-Symbol files can be very large, so we recommend placing cached files in your system's temp directory so that
-it can garbage collect unused ones for you. To this end, the default value for this flag is a `rust-
-minidump-cache` subdirectory of `std::env::temp_dir()` (usually /tmp/rust-minidump-cache on linux).
+Symbol files can be very large, so we recommend placing cached files in your system's
+temp directory so that it can garbage collect unused ones for you. To this end, the
+default value for this flag is a `rust-minidump-cache` subdirectory of
+`std::env::temp_dir()` (usually /tmp/rust-minidump-cache on linux).
 
-symbols-cache must be on the same filesystem as symbols-tmp (if that doesn't mean anything to you, don't
-worry about it, you're probably not doing something that will run afoul of it).
+symbols-cache must be on the same filesystem as symbols-tmp (if that doesn't mean
+anything to you, don't worry about it, you're probably not doing something that will run
+afoul of it).
 
 ### `--symbols-tmp <symbols-tmp>`
 A directory to use as temp space for downloading symbols.
 
-A temp dir is necessary to allow for multiple rust-minidump instances to share a cache without race
-conditions. Files to be added to the cache will be constructed in this location before being atomically
-moved to the cache.
+A temp dir is necessary to allow for multiple rust-minidump instances to share a cache
+without race conditions. Files to be added to the cache will be constructed in this
+location before being atomically moved to the cache.
 
-If no path is specified, `std::env::temp_dir()` will be used to improve portability. See the rust
-documentation for how to set that value if you wish to use something other than your system's default temp
-directory.
+If no path is specified, `std::env::temp_dir()` will be used to improve portability. See
+the rust documentation for how to set that value if you wish to use something other than
+your system's default temp directory.
 
-symbols-tmp must be on the same filesystem as symbols-cache (if that doesn't mean anything to you, don't
-worry about it, you're probably not doing something that will run afoul of it).
+symbols-tmp must be on the same filesystem as symbols-cache (if that doesn't mean
+anything to you, don't worry about it, you're probably not doing something that will run
+afoul of it).
 
 ### `--symbol-download-timeout-secs <symbol-download-timeout-secs>`
 The maximum amount of time (in seconds) a symbol file download is allowed to take.
@@ -334,16 +321,11 @@ This is necessary to enforce forward progress on misbehaving http responses.
 
 \[default: 1000]
 
-# ARGS
-### `<minidump>`
-Path to the minidump file to analyze.
-
-### `<symbols-path>...`
-Path to a symbol file.
-
-If multiple symbols-path values are provided, all symbol files will be merged into minidump-stackwalk's
-symbol database.
+### `-h, --help`
+Print help information
 
+### `-V, --version`
+Print version information
 
 
 # NOTES
@@ -354,24 +336,20 @@ Symbols are used for two purposes:
 
 1. To fill in more information about each frame of the backtraces. (function names, lines, etc.)
 
-2. To do produce a more *accurate* backtrace. This is primarily accomplished with call frame information (CFI), but
-just knowing what parts of a module maps to actual code is also useful!
-
-
+2. To do produce a more *accurate* backtrace. This is primarily accomplished with call frame
+information (CFI), but just knowing what parts of a module maps to actual code is also useful!
 
 ## Supported Symbol Formats
 
-Currently only breakpad text symbol files are supported, although we hope to eventually support native formats like
-PDB and DWARF as well.
-
-
+Currently only breakpad text symbol files are supported, although we hope to eventually support
+native formats like PDB and DWARF as well.
 
 ## Breakpad Symbol Files
 
-Breakpad symbol files are basically a simplified version of the information found in native debuginfo formats. We
-recommend using a version of dump_syms to generate them.
+Breakpad symbol files are basically a simplified version of the information found in native
+debuginfo formats. We recommend using a version of dump_syms to generate them.
 
 See:
-* symbol file docs: https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/symbol_files.md
+* https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/symbol_files.md
 * mozilla's dump_syms (co-developed with this program): https://github.com/mozilla/dump_syms
 
diff --git a/minidump-stackwalk/src/main.rs b/minidump-stackwalk/src/main.rs
index fe6a7fb..9a6f5d5 100644
--- a/minidump-stackwalk/src/main.rs
+++ b/minidump-stackwalk/src/main.rs
@@ -16,79 +16,80 @@ use minidump_processor::{
     http_symbol_supplier, simple_symbol_supplier, MultiSymbolProvider, ProcessorOptions, Symbolizer,
 };
 
-use clap::{crate_version, App, AppSettings, Arg, ArgGroup};
+use clap::{AppSettings, Arg, ArgGroup, Command};
 use log::error;
 use simplelog::{
     ColorChoice, ConfigBuilder, Level, LevelFilter, TermLogger, TerminalMode, WriteLogger,
 };
 
-fn make_app() -> App<'static, 'static> {
-    App::new("minidump-stackwalk")
-        .version(crate_version!())
+fn make_app() -> Command<'static> {
+    Command::new("minidump-stackwalk")
+        .version(clap::crate_version!())
         .about("Analyzes minidumps and produces a report (either human-readable or JSON).")
-        .setting(AppSettings::NextLineHelp)
-        .setting(AppSettings::ColoredHelp)
+        .next_line_help(true)
         .setting(AppSettings::DeriveDisplayOrder)
-        .usage("minidump-stackwalk [FLAGS] [OPTIONS] <minidump> [--] [symbols-path]...")
-        .arg(
-            Arg::with_name("json")
-                .long("json")
-                .long_help("Emit a machine-readable JSON report.
+        .override_usage("minidump-stackwalk [FLAGS] [OPTIONS] <minidump> [--] [symbols-path]...")
+        .arg(Arg::new("json").long("json").long_help(
+            "Emit a machine-readable JSON report.
 
 The schema for this output is officially documented here:
-https://github.com/luser/rust-minidump/blob/master/minidump-processor/json-schema.md\n\n\n")
-        )
-        .arg(
-            Arg::with_name("human")
-                .long("human")
-                .long_help("Emit a human-readable report (the default).
+https://github.com/luser/rust-minidump/blob/master/minidump-processor/json-schema.md",
+        ))
+        .arg(Arg::new("human").long("human").long_help(
+            "Emit a human-readable report (the default).
 
 The human-readable report does not have a specified format, and may not have as \
 many details as the JSON format. It is intended for quickly inspecting \
-a crash or debugging rust-minidump itself.\n\n\n")
-        )
+a crash or debugging rust-minidump itself.",
+        ))
         .arg(
-            Arg::with_name("cyborg")
+            Arg::new("cyborg")
                 .long("cyborg")
                 .takes_value(true)
-                .long_help("Combine --human and --json
+                .allow_invalid_utf8(true)
+                .long_help(
+                    "Combine --human and --json
 
 Because this creates two output streams, you must specify a path to write the --json \
 output to. The --human output will be the 'primary' output and default to stdout, which \
-can be configured with --output-file as normal.\n\n\n")
+can be configured with --output-file as normal.",
+                ),
         )
-        .arg(
-            Arg::with_name("dump")
-                .long("dump")
-                .long_help("Dump the 'raw' contents of the minidump.
+        .arg(Arg::new("dump").long("dump").long_help(
+            "Dump the 'raw' contents of the minidump.
 
 This is an implementation of the functionality of the old minidump_dump tool. \
 It minimally parses and interprets the minidump in an attempt to produce a \
 fairly 'raw' dump of the minidump's contents. This is most useful for debugging \
-minidump-stackwalk itself, or a misbehaving minidump generator.\n\n\n")
-        )
+minidump-stackwalk itself, or a misbehaving minidump generator.",
+        ))
         .arg(
-            Arg::with_name("help-markdown")
+            Arg::new("help-markdown")
                 .long("help-markdown")
                 .long_help("Print --help but formatted as markdown (used for generating docs)")
-                .hidden(true)
-        )
-        .group(ArgGroup::with_name("output-format")
-            .args(&["json", "human", "cyborg", "dump", "help-markdown"])
+                .hide(true),
         )
+        .group(ArgGroup::new("output-format").args(&[
+            "json",
+            "human",
+            "cyborg",
+            "dump",
+            "help-markdown",
+        ]))
         .arg(
-            Arg::with_name("features")
+            Arg::new("features")
                 .long("features")
                 .possible_values(&["stable-basic", "stable-all", "unstable-all"])
                 .default_value("stable-basic")
                 .takes_value(true)
-                .long_help("Specify at a high-level how much analysis to perform.
+                .long_help(
+                    "Specify at a high-level how much analysis to perform.
 
 This flag provides a way to more blindly opt into Extra Analysis without having to know about \
 the specific features of minidump-stackwalk. This is equivalent to ProcessorOptions in \
 minidump-processor. The current supported values are:
 
-* stable-basic (default): give me solid detailed analysis that most people would want.
+* stable-basic (default): give me solid detailed analysis that most people would want
 * stable-all: turn on extra detailed analysis.
 * unstable-all: turn on the weird and experimental stuff.
 
@@ -107,68 +108,77 @@ human usage or for checking \"what's new\".
 
 Features under unstable-all may be deprecated and become noops. Features which require \
 additional input (such as `--evil-json`) cannot be affected by this, and must still be \
-manually 'discovered'.\n\n\n")
+manually 'discovered'.",
+                ),
         )
         .arg(
-            Arg::with_name("output-file")
+            Arg::new("output-file")
                 .long("output-file")
                 .takes_value(true)
-                .help("Where to write the output to (if unspecified, stdout is used)")
+                .allow_invalid_utf8(true)
+                .help("Where to write the output to (if unspecified, stdout is used)"),
         )
         .arg(
-            Arg::with_name("log-file")
+            Arg::new("log-file")
                 .long("log-file")
                 .takes_value(true)
-                .help("Where to write logs to (if unspecified, stderr is used)")
+                .allow_invalid_utf8(true)
+                .help("Where to write logs to (if unspecified, stderr is used)"),
         )
         .arg(
-            Arg::with_name("verbose")
+            Arg::new("verbose")
                 .long("verbose")
                 .possible_values(&["off", "error", "warn", "info", "debug", "trace"])
                 .default_value("error")
                 .takes_value(true)
-                .long_help("Set the logging level.
+                .long_help(
+                    "Set the logging level.
 
 The unwinder has been heavily instrumented with `trace` logging, so if you want to debug why \
 an unwind happened the way it did, --verbose=trace is very useful (all unwinder logging will \
-be prefixed with `unwind:`).\n\n\n")
+be prefixed with `unwind:`).",
+                ),
         )
         .arg(
-            Arg::with_name("pretty")
+            Arg::new("pretty")
                 .long("pretty")
-                .help("Pretty-print --json output.")
+                .help("Pretty-print --json output."),
         )
-        .arg(
-            Arg::with_name("brief")
-                .long("brief")
-                .help("Provide a briefer --human report.
+        .arg(Arg::new("brief").long("brief").help(
+            "Provide a briefer --human report.
 
-Only provides the top-level summary and a backtrace of the crashing thread.\n\n\n")
-        )
+Only provides the top-level summary and a backtrace of the crashing thread.",
+        ))
         .arg(
-            Arg::with_name("evil-json")
+            Arg::new("evil-json")
                 .long("evil-json")
                 .takes_value(true)
-                .long_help("**[UNSTABLE]** An input JSON file with the extra information.
+                .allow_invalid_utf8(true)
+                .long_help(
+                    "**[UNSTABLE]** An input JSON file with the extra information.
 
 This is a gross hack for some legacy side-channel information that mozilla uses. It will \
 hopefully be phased out and deprecated in favour of just using custom streams in the \
-minidump itself.\n\n\n")
+minidump itself.",
+                ),
         )
         .arg(
-            Arg::with_name("recover-function-args")
+            Arg::new("recover-function-args")
                 .long("recover-function-args")
-                .help("**[UNSTABLE]** Heuristically recover function arguments
+                .help(
+                    "**[UNSTABLE]** Heuristically recover function arguments
 
-This is an experimental feature, which currently only shows up in --human output.\n\n\n")
+This is an experimental feature, which currently only shows up in --human output.",
+                ),
         )
         .arg(
-            Arg::with_name("symbols-url")
+            Arg::new("symbols-url")
                 .long("symbols-url")
-                .multiple(true)
+                .multiple_values(true)
                 .takes_value(true)
                 .number_of_values(1)
-                .long_help("base URL from which URLs to symbol files can be constructed.
+                .long_help(
+                    "base URL from which URLs to symbol files can be constructed.
 
 If multiple symbols-url values are provided, they will each be tried in order until \
 one resolves.
@@ -178,13 +188,16 @@ symbol server protocol. For more details, see the Tecken docs:
 
 https://tecken.readthedocs.io/en/latest/
 
-Example symbols-url value: https://symbols.mozilla.org/\n\n\n")
+Example symbols-url value: https://symbols.mozilla.org/",
+                ),
         )
         .arg(
-            Arg::with_name("symbols-cache")
+            Arg::new("symbols-cache")
                 .long("symbols-cache")
                 .takes_value(true)
-                .long_help("A directory in which downloaded symbols can be stored.
+                .allow_invalid_utf8(true)
+                .long_help(
+                    "A directory in which downloaded symbols can be stored.
                     
 Symbol files can be very large, so we recommend placing cached files in your \
 system's temp directory so that it can garbage collect unused ones for you. \
@@ -192,13 +205,16 @@ To this end, the default value for this flag is a `rust-minidump-cache` \
 subdirectory of `std::env::temp_dir()` (usually /tmp/rust-minidump-cache on linux).
 
 symbols-cache must be on the same filesystem as symbols-tmp (if that doesn't mean anything to \
-you, don't worry about it, you're probably not doing something that will run afoul of it).\n\n\n")
+you, don't worry about it, you're probably not doing something that will run afoul of it).",
+                ),
         )
         .arg(
-            Arg::with_name("symbols-tmp")
+            Arg::new("symbols-tmp")
                 .long("symbols-tmp")
                 .takes_value(true)
-                .long_help("A directory to use as temp space for downloading symbols.
+                .allow_invalid_utf8(true)
+                .long_help(
+                    "A directory to use as temp space for downloading symbols.
 
 A temp dir is necessary to allow for multiple rust-minidump instances to share a cache without \
 race conditions. Files to be added to the cache will be constructed in this location before \
@@ -209,34 +225,42 @@ See the rust documentation for how to set that value if you wish to use somethin
 your system's default temp directory.
 
 symbols-tmp must be on the same filesystem as symbols-cache (if that doesn't mean anything to \
-you, don't worry about it, you're probably not doing something that will run afoul of it).\n\n\n")
+you, don't worry about it, you're probably not doing something that will run afoul of it).",
+                ),
         )
         .arg(
-            Arg::with_name("symbol-download-timeout-secs")
+            Arg::new("symbol-download-timeout-secs")
                 .long("symbol-download-timeout-secs")
                 .default_value("1000")
                 .takes_value(true)
-                .help("The maximum amount of time (in seconds) a symbol file download is allowed \
+                .help(
+                    "The maximum amount of time (in seconds) a symbol file download is allowed \
 to take.
 
-This is necessary to enforce forward progress on misbehaving http responses.\n\n")
+This is necessary to enforce forward progress on misbehaving http responses.",
+                ),
         )
         .arg(
-            Arg::with_name("minidump")
-                .required(true)
+            Arg::new("minidump")
+                .required_unless_present("help-markdown")
                 .takes_value(true)
-                .help("Path to the minidump file to analyze.")
+                .allow_invalid_utf8(true)
+                .help("Path to the minidump file to analyze."),
         )
         .arg(
-            Arg::with_name("symbols-path")
-                .multiple(true)
+            Arg::new("symbols-path")
+                .multiple_values(true)
                 .takes_value(true)
-                .long_help("Path to a symbol file.
+                .allow_invalid_utf8(true)
+                .long_help(
+                    "Path to a symbol file.
 
 If multiple symbols-path values are provided, all symbol files will be merged \
-into minidump-stackwalk's symbol database.\n\n\n")   
+into minidump-stackwalk's symbol database.",
+                ),
         )
-        .after_help("
+        .after_help(
+            "
 NOTES:
 
 Purpose of Symbols:
@@ -249,25 +273,22 @@ Purpose of Symbols:
 call frame information (CFI), but just knowing what parts of a module maps to actual \
 code is also useful!
 
-
-
 Supported Symbol Formats:
 
   Currently only breakpad text symbol files are supported, although we hope to eventually \
 support native formats like PDB and DWARF as well.
 
-
-
 Breakpad Symbol Files:
 
   Breakpad symbol files are basically a simplified version of the information found in \
 native debuginfo formats. We recommend using a version of dump_syms to generate them.
 
   See:
-    * symbol file docs: https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/symbol_files.md
+    * https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/symbol_files.md
     * mozilla's dump_syms (co-developed with this program): https://github.com/mozilla/dump_syms
 
-")
+",
+        )
 }
 
 #[cfg_attr(test, allow(dead_code))]
diff --git a/minidump/Cargo.toml b/minidump/Cargo.toml
index f93f6ff..1d8f15e 100644
--- a/minidump/Cargo.toml
+++ b/minidump/Cargo.toml
@@ -20,7 +20,7 @@ memmap2 = "0.5.3"
 minidump-common = { version = "0.9.6", path = "../minidump-common" }
 num-traits = "0.2"
 range-map = "0.1.5"
-scroll = "0.10.2"
+scroll = "0.11.0"
 thiserror = "1.0.30"
 time = { version = "0.3.6", features = ["formatting"] }
 uuid = "0.8"
diff --git a/minidump/fuzz/Cargo.lock b/minidump/fuzz/Cargo.lock
index de80553..74371a0 100644
--- a/minidump/fuzz/Cargo.lock
+++ b/minidump/fuzz/Cargo.lock
@@ -2,47 +2,17 @@
 # It is not intended for manual editing.
 version = 3
 
-[[package]]
-name = "addr2line"
-version = "0.17.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
-dependencies = [
- "gimli",
-]
-
-[[package]]
-name = "adler"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
-
 [[package]]
 name = "arbitrary"
-version = "1.0.3"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "510c76ecefdceada737ea728f4f9a84bd2e1ef29f1ba555e560940fe279954de"
+checksum = "c38b6b6b79f671c25e1a3e785b7b82d7562ffc9cd3efdc98627e5668a2472490"
 
 [[package]]
 name = "autocfg"
-version = "1.0.1"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
-
-[[package]]
-name = "backtrace"
-version = "0.3.63"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "321629d8ba6513061f26707241fa9bc89524ff1cd7a915a97ef0c62c666ce1b6"
-dependencies = [
- "addr2line",
- "cc",
- "cfg-if",
- "libc",
- "miniz_oxide",
- "object",
- "rustc-demangle",
-]
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
 [[package]]
 name = "bitflags"
@@ -52,9 +22,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
 name = "cc"
-version = "1.0.72"
+version = "1.0.73"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
+checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
 
 [[package]]
 name = "cfg-if"
@@ -63,16 +33,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
-name = "chrono"
-version = "0.4.19"
+name = "debugid"
+version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
+checksum = "d6ee87af31d84ef885378aebca32be3d682b0e0dc119d5b4860a2c5bb5046730"
 dependencies = [
- "libc",
- "num-integer",
- "num-traits 0.2.14",
- "time",
- "winapi",
+ "uuid",
 ]
 
 [[package]]
@@ -151,44 +117,22 @@ dependencies = [
 ]
 
 [[package]]
-name = "failure"
-version = "0.1.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86"
-dependencies = [
- "backtrace",
- "failure_derive",
-]
-
-[[package]]
-name = "failure_derive"
-version = "0.1.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
- "synstructure",
-]
-
-[[package]]
-name = "gimli"
-version = "0.26.1"
+name = "itoa"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
+checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
 
 [[package]]
 name = "libc"
-version = "0.2.114"
+version = "0.2.119"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0005d08a8f7b65fb8073cb697aa0b12b631ed251ce73d862ce50eeb52ce3b50"
+checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
 
 [[package]]
 name = "libfuzzer-sys"
-version = "0.4.2"
+version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36a9a84a6e8b55dfefb04235e55edb2b9a2a18488fcae777a6bdaa6f06f1deb3"
+checksum = "336244aaeab6a12df46480dc585802aa743a72d66b11937844c61bbca84c991d"
 dependencies = [
  "arbitrary",
  "cc",
@@ -205,34 +149,29 @@ dependencies = [
 ]
 
 [[package]]
-name = "memchr"
-version = "2.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
-
-[[package]]
-name = "memmap"
-version = "0.7.0"
+name = "memmap2"
+version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
+checksum = "057a3db23999c867821a7a59feb06a578fcb03685e983dff90daf9e7d24ac08f"
 dependencies = [
  "libc",
- "winapi",
 ]
 
 [[package]]
 name = "minidump"
 version = "0.9.6"
 dependencies = [
- "chrono",
+ "debugid",
  "encoding",
- "failure",
  "log",
- "memmap",
+ "memmap2",
  "minidump-common",
  "num-traits 0.2.14",
  "range-map",
  "scroll",
+ "thiserror",
+ "time",
+ "uuid",
 ]
 
 [[package]]
@@ -240,6 +179,7 @@ name = "minidump-common"
 version = "0.9.6"
 dependencies = [
  "bitflags",
+ "debugid",
  "enum-primitive-derive",
  "log",
  "num-traits 0.2.14",
@@ -256,26 +196,6 @@ dependencies = [
  "minidump",
 ]
 
-[[package]]
-name = "miniz_oxide"
-version = "0.4.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
-dependencies = [
- "adler",
- "autocfg",
-]
-
-[[package]]
-name = "num-integer"
-version = "0.1.44"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
-dependencies = [
- "autocfg",
- "num-traits 0.2.14",
-]
-
 [[package]]
 name = "num-traits"
 version = "0.1.43"
@@ -295,19 +215,19 @@ dependencies = [
 ]
 
 [[package]]
-name = "object"
-version = "0.27.1"
+name = "num_threads"
+version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9"
+checksum = "97ba99ba6393e2c3734791401b66902d981cb03bf190af674ca69949b6d5fb15"
 dependencies = [
- "memchr",
+ "libc",
 ]
 
 [[package]]
 name = "once_cell"
-version = "1.9.0"
+version = "1.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
+checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
 
 [[package]]
 name = "proc-macro2"
@@ -336,26 +256,20 @@ dependencies = [
  "num-traits 0.1.43",
 ]
 
-[[package]]
-name = "rustc-demangle"
-version = "0.1.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
-
 [[package]]
 name = "scroll"
-version = "0.10.2"
+version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec"
+checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da"
 dependencies = [
  "scroll_derive",
 ]
 
 [[package]]
 name = "scroll_derive"
-version = "0.10.5"
+version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aaaae8f38bb311444cfb7f1979af0bc9240d95795f75f9ceddf6a59b79ceffa0"
+checksum = "bdbda6ac5cd1321e724fa9cee216f3a61885889b896f073b8f82322789c5250e"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -385,26 +299,34 @@ dependencies = [
 ]
 
 [[package]]
-name = "synstructure"
-version = "0.12.6"
+name = "thiserror"
+version = "1.0.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
+checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
 dependencies = [
  "proc-macro2",
  "quote",
  "syn",
- "unicode-xid",
 ]
 
 [[package]]
 name = "time"
-version = "0.1.44"
+version = "0.3.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
+checksum = "004cbc98f30fa233c61a38bc77e96a9106e65c88f2d3bef182ae952027e5753d"
 dependencies = [
+ "itoa",
  "libc",
- "wasi",
- "winapi",
+ "num_threads",
 ]
 
 [[package]]
@@ -414,29 +336,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
 
 [[package]]
-name = "wasi"
-version = "0.10.0+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
-
-[[package]]
-name = "winapi"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
-dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
-]
-
-[[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
-
-[[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
+name = "uuid"
+version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
diff --git a/synth-minidump/Cargo.toml b/synth-minidump/Cargo.toml
index bf4bd76..9da05b4 100644
--- a/synth-minidump/Cargo.toml
+++ b/synth-minidump/Cargo.toml
@@ -10,4 +10,4 @@ readme = "README.md"
 test-assembler = "0.1.5"
 minidump-common = { version = "0.9.6", path = "../minidump-common" }
 encoding = "0.2"
-scroll = "0.10.2"
+scroll = "0.11.0"


Test patch:
diff --git a/minidump-processor/src/stackwalker/amd64_unittest.rs b/minidump-processor/src/stackwalker/amd64_unittest.rs
index f1791f6..59d0cf7 100644
--- a/minidump-processor/src/stackwalker/amd64_unittest.rs
+++ b/minidump-processor/src/stackwalker/amd64_unittest.rs
@@ -3,8 +3,9 @@
 
 use crate::process_state::*;
 use crate::stackwalker::walk_stack;
-use crate::{string_symbol_supplier, Symbolizer};
+use crate::{string_symbol_supplier, Symbolizer, SystemInfo};
 use minidump::format::CONTEXT_AMD64;
+use minidump::system_info::{Cpu, Os};
 use minidump::*;
 use std::collections::HashMap;
 use test_assembler::*;
@@ -12,6 +13,7 @@ use test_assembler::*;
 struct TestFixture {
     pub raw: CONTEXT_AMD64,
     pub modules: MinidumpModuleList,
+    pub system_info: SystemInfo,
     pub symbols: HashMap<String, String>,
 }
 
@@ -25,6 +27,15 @@ impl TestFixture {
                 MinidumpModule::new(0x00007400c0000000, 0x10000, "module1"),
                 MinidumpModule::new(0x00007500b0000000, 0x10000, "module2"),
             ]),
+            system_info: SystemInfo {
+                os: Os::Linux,
+                os_version: None,
+                os_build: None,
+                cpu: Cpu::X86_64,
+                cpu_info: None,
+                cpu_microcode_version: None,
+                cpu_count: 1,
+            },
             symbols: HashMap::new(),
         }
     }
@@ -48,6 +59,7 @@ impl TestFixture {
             &Some(&context),
             Some(&stack_memory),
             &self.modules,
+            &self.system_info,
             &symbolizer,
         )
         .await
@@ -149,6 +161,74 @@ async fn test_caller_pushed_rbp() {
     }
 }
 
+#[tokio::test]
+async fn test_windows_rbp_scan() {
+    let mut f = TestFixture::new();
+    f.system_info.os = Os::Windows;
+
+    let mut stack = Section::new();
+    let stack_start = 0x8000000080000000;
+    let return_address = 0x00007500b0000110;
+    stack.start().set_const(stack_start);
+
+    let frame0_rbp = Label::new();
+    let frame1_sp = Label::new();
+    let frame1_rbp = Label::new();
+
+    stack = stack
+        // frame 0
+        .append_repeated(0, 16) // space
+        .D64(0x00000000b0000000) // junk that's not
+        .D64(0x00000000b0000000) // a return address
+        .mark(&frame0_rbp) // the FP can point to the middle of the stack on Windows
+        .D64(0x00000000c0001000)
+        .D64(0x00000000b000aaaa)
+        .D64(&frame1_rbp) // caller-pushed %rbp
+        .D64(return_address) // actual return address
+        // frame 1
+        .mark(&frame1_sp)
+        .append_repeated(0, 32) // body of frame1
+        .mark(&frame1_rbp) // end of stack
+        .D64(0);
+
+    f.raw.rip = 0x00007400c0000200;
+    f.raw.rbp = frame0_rbp.value().unwrap();
+    f.raw.rsp = stack.start().value().unwrap();
+
+    let s = f.walk_stack(stack).await;
+    assert_eq!(s.frames.len(), 2);
+
+    {
+        // To avoid reusing locals by mistake
+        let f0 = &s.frames[0];
+        assert_eq!(f0.trust, FrameTrust::Context);
+        assert_eq!(f0.context.valid, MinidumpContextValidity::All);
+        if let MinidumpRawContext::Amd64(ctx) = &f0.context.raw {
+            assert_eq!(ctx.rbp, frame0_rbp.value().unwrap());
+        } else {
+            unreachable!();
+        }
+    }
+
+    {
+        // To avoid reusing locals by mistake
+        let f1 = &s.frames[1];
+        assert_eq!(f1.trust, FrameTrust::Scan);
+        if let MinidumpContextValidity::Some(ref which) = f1.context.valid {
+            assert!(which.contains("rip"));
+            assert!(which.contains("rsp"));
+        } else {
+            unreachable!();
+        }
+        if let MinidumpRawContext::Amd64(ctx) = &f1.context.raw {
+            assert_eq!(ctx.rip, return_address);
+            assert_eq!(ctx.rsp, frame1_sp.value().unwrap());
+        } else {
+            unreachable!();
+        }
+    }
+}
+
 #[tokio::test]
 async fn test_scan_without_symbols() {
     // When the stack walker resorts to scanning the stack,
diff --git a/minidump-processor/src/stackwalker/arm64_unittest.rs b/minidump-processor/src/stackwalker/arm64_unittest.rs
index 203beb9..13d3233 100644
--- a/minidump-processor/src/stackwalker/arm64_unittest.rs
+++ b/minidump-processor/src/stackwalker/arm64_unittest.rs
@@ -6,7 +6,8 @@
 
 use crate::process_state::*;
 use crate::stackwalker::walk_stack;
-use crate::{string_symbol_supplier, Symbolizer};
+use crate::{string_symbol_supplier, Symbolizer, SystemInfo};
+use minidump::system_info::{Cpu, Os};
 use minidump::*;
 use std::collections::HashMap;
 use test_assembler::*;
@@ -47,11 +48,21 @@ impl TestFixture {
             size,
             bytes: &stack,
         };
+        let system_info = SystemInfo {
+            os: Os::Windows,
+            os_version: None,
+            os_build: None,
+            cpu: Cpu::Arm64,
+            cpu_info: None,
+            cpu_microcode_version: None,
+            cpu_count: 1,
+        };
         let symbolizer = Symbolizer::new(string_symbol_supplier(self.symbols.clone()));
         walk_stack(
             &Some(&context),
             Some(&stack_memory),
             &self.modules,
+            &system_info,
             &symbolizer,
         )
         .await
diff --git a/minidump-processor/src/stackwalker/arm_unittest.rs b/minidump-processor/src/stackwalker/arm_unittest.rs
index 6f265cd..9452ca5 100644
--- a/minidump-processor/src/stackwalker/arm_unittest.rs
+++ b/minidump-processor/src/stackwalker/arm_unittest.rs
@@ -3,8 +3,9 @@
 
 use crate::process_state::*;
 use crate::stackwalker::walk_stack;
-use crate::{string_symbol_supplier, Symbolizer};
+use crate::{string_symbol_supplier, Symbolizer, SystemInfo};
 use minidump::format::CONTEXT_ARM;
+use minidump::system_info::{Cpu, Os};
 use minidump::*;
 use std::collections::HashMap;
 use test_assembler::*;
@@ -12,6 +13,7 @@ use test_assembler::*;
 struct TestFixture {
     pub raw: CONTEXT_ARM,
     pub modules: MinidumpModuleList,
+    pub system_info: SystemInfo,
     pub symbols: HashMap<String, String>,
 }
 
@@ -25,6 +27,15 @@ impl TestFixture {
                 MinidumpModule::new(0x40000000, 0x10000, "module1"),
                 MinidumpModule::new(0x50000000, 0x10000, "module2"),
             ]),
+            system_info: SystemInfo {
+                os: Os::Ios,
+                os_version: None,
+                os_build: None,
+                cpu: Cpu::Arm,
+                cpu_info: None,
+                cpu_microcode_version: None,
+                cpu_count: 1,
+            },
             symbols: HashMap::new(),
         }
     }
@@ -48,6 +59,7 @@ impl TestFixture {
             &Some(&context),
             Some(&stack_memory),
             &self.modules,
+            &self.system_info,
             &symbolizer,
         )
         .await
@@ -237,6 +249,102 @@ async fn test_scan_first_frame() {
     }
 }
 
+#[tokio::test]
+async fn test_invalid_lr() {
+    let mut f = TestFixture::new();
+    f.system_info.os = Os::Linux;
+
+    let mut stack = Section::new();
+    stack.start().set_const(0x80000000);
+
+    let lr = Label::new();
+    let return_address1 = 0x50000100u32;
+    let return_address2 = 0x50000900u32;
+    let frame1_sp = Label::new();
+    let frame2_sp = Label::new();
+    let frame1_fp = Label::new();
+    let frame2_fp = Label::new();
+
+    stack = stack
+        // frame 0
+        .append_repeated(0, 32) // space
+        .mark(&lr) // the LR points to something on the stack
+        .D32(0x0000000D) // junk that's not
+        .D32(0xF0000000) // a return address
+        .mark(&frame1_fp) // next fp will point to the next value
+        .D32(&frame2_fp) // save current frame pointer
+        .D32(return_address1) // save current link register
+        .mark(&frame1_sp)
+        // frame 1
+        .append_repeated(0, 32) // space
+        .D32(0x0000000D) // junk that's not
+        .D32(0xF0000000) // a return address
+        .mark(&frame2_fp)
+        .D32(0)
+        .D32(return_address2)
+        .mark(&frame2_sp);
+
+    f.raw.set_register("pc", 0x40005510);
+    f.raw.set_register("lr", lr.value().unwrap() as u32);
+    f.raw.set_register("fp", frame1_fp.value().unwrap() as u32);
+    f.raw
+        .set_register("sp", stack.start().value().unwrap() as u32);
+
+    let s = f.walk_stack(stack).await;
+    assert_eq!(s.frames.len(), 3);
+
+    {
+        // Frame 0
+        let frame = &s.frames[0];
+        assert_eq!(frame.trust, FrameTrust::Context);
+        assert_eq!(frame.context.valid, MinidumpContextValidity::All);
+    }
+
+    {
+        // Frame 1
+        let frame = &s.frames[1];
+        let valid = &frame.context.valid;
+        assert_eq!(frame.trust, FrameTrust::Scan);
+        if let MinidumpContextValidity::Some(ref which) = valid {
+            assert_eq!(which.len(), 2);
+        } else {
+            unreachable!();
+        }
+
+        if let MinidumpRawContext::Arm(ctx) = &frame.context.raw {
+            assert_eq!(ctx.get_register("pc", valid).unwrap(), return_address1);
+            assert_eq!(
+                ctx.get_register("sp", valid).unwrap(),
+                frame1_sp.value().unwrap() as u32
+            );
+        } else {
+            unreachable!();
+        }
+    }
+
+    {
+        // Frame 2
+        let frame = &s.frames[2];
+        let valid = &frame.context.valid;
+        assert_eq!(frame.trust, FrameTrust::Scan);
+        if let MinidumpContextValidity::Some(ref which) = valid {
+            assert_eq!(which.len(), 2);
+        } else {
+            unreachable!();
+        }
+
+        if let MinidumpRawContext::Arm(ctx) = &frame.context.raw {
+            assert_eq!(ctx.get_register("pc", valid).unwrap(), return_address2);
+            assert_eq!(
+                ctx.get_register("sp", valid).unwrap(),
+                frame2_sp.value().unwrap() as u32
+            );
+        } else {
+            unreachable!();
+        }
+    }
+}
+
 #[tokio::test]
 async fn test_frame_pointer() {
     // Frame-pointer-based unwinding
diff --git a/minidump-processor/src/stackwalker/x86_unittest.rs b/minidump-processor/src/stackwalker/x86_unittest.rs
index fe13a18..bff2061 100644
--- a/minidump-processor/src/stackwalker/x86_unittest.rs
+++ b/minidump-processor/src/stackwalker/x86_unittest.rs
@@ -3,8 +3,9 @@
 
 use crate::process_state::*;
 use crate::stackwalker::walk_stack;
-use crate::{string_symbol_supplier, Symbolizer};
+use crate::{string_symbol_supplier, Symbolizer, SystemInfo};
 use minidump::format::CONTEXT_X86;
+use minidump::system_info::{Cpu, Os};
 use minidump::*;
 use std::collections::HashMap;
 use test_assembler::*;
@@ -43,11 +44,21 @@ impl TestFixture {
             size,
             bytes: &stack,
         };
+        let system_info = SystemInfo {
+            os: Os::Windows,
+            os_version: None,
+            os_build: None,
+            cpu: Cpu::X86,
+            cpu_info: None,
+            cpu_microcode_version: None,
+            cpu_count: 1,
+        };
         let symbolizer = Symbolizer::new(string_symbol_supplier(self.symbols.clone()));
         walk_stack(
             &Some(&context),
             Some(&stack_memory),
             &self.modules,
+            &system_info,
             &symbolizer,
         )
         .await
diff --git a/minidump-stackwalk/tests/snapshots/test_minidump_stackwalk__long-help.snap b/minidump-stackwalk/tests/snapshots/test_minidump_stackwalk__long-help.snap
index 430baec..b14099a 100644
--- a/minidump-stackwalk/tests/snapshots/test_minidump_stackwalk__long-help.snap
+++ b/minidump-stackwalk/tests/snapshots/test_minidump_stackwalk__long-help.snap
@@ -1,8 +1,6 @@
 ---
 source: minidump-stackwalk/tests/test-minidump-stackwalk.rs
-assertion_line: 465
 expression: stdout
-
 ---
 minidump-stackwalk 0.9.6
 Analyzes minidumps and produces a report (either human-readable or JSON).
@@ -10,62 +8,53 @@ Analyzes minidumps and produces a report (either human-readable or JSON).
 USAGE:
     minidump-stackwalk [FLAGS] [OPTIONS] <minidump> [--] [symbols-path]...
 
-FLAGS:
+ARGS:
+    <minidump>
+            Path to the minidump file to analyze.
+
+    <symbols-path>...
+            Path to a symbol file.
+            
+            If multiple symbols-path values are provided, all symbol files will be merged into
+            minidump-stackwalk's symbol database.
+
+OPTIONS:
         --json
             Emit a machine-readable JSON report.
             
             The schema for this output is officially documented here:
             https://github.com/luser/rust-minidump/blob/master/minidump-processor/json-schema.md
-            
+
         --human
             Emit a human-readable report (the default).
             
-            The human-readable report does not have a specified format, and may not have as many details as the JSON
-            format. It is intended for quickly inspecting a crash or debugging rust-minidump itself.
-            
-        --dump
-            Dump the 'raw' contents of the minidump.
-            
-            This is an implementation of the functionality of the old minidump_dump tool. It minimally parses and
-            interprets the minidump in an attempt to produce a fairly 'raw' dump of the minidump's contents. This is
-            most useful for debugging minidump-stackwalk itself, or a misbehaving minidump generator.
-            
-        --pretty
-            Pretty-print --json output.
+            The human-readable report does not have a specified format, and may not have as many
+            details as the JSON format. It is intended for quickly inspecting a crash or debugging
+            rust-minidump itself.
 
-        --brief
-            Provide a briefer --human report.
-            
-            Only provides the top-level summary and a backtrace of the crashing thread.
-            
-        --recover-function-args
-            **[UNSTABLE]** Heuristically recover function arguments
-            
-            This is an experimental feature, which currently only shows up in --human output.
-            
-    -h, --help
-            Prints help information
-
-    -V, --version
-            Prints version information
-
-
-OPTIONS:
         --cyborg <cyborg>
             Combine --human and --json
             
-            Because this creates two output streams, you must specify a path to write the --json output to. The --human
-            output will be the 'primary' output and default to stdout, which can be configured with --output-file as
-            normal.
+            Because this creates two output streams, you must specify a path to write the --json
+            output to. The --human output will be the 'primary' output and default to stdout, which
+            can be configured with --output-file as normal.
+
+        --dump
+            Dump the 'raw' contents of the minidump.
             
+            This is an implementation of the functionality of the old minidump_dump tool. It
+            minimally parses and interprets the minidump in an attempt to produce a fairly 'raw'
+            dump of the minidump's contents. This is most useful for debugging minidump-stackwalk
+            itself, or a misbehaving minidump generator.
+
         --features <features>
             Specify at a high-level how much analysis to perform.
             
-            This flag provides a way to more blindly opt into Extra Analysis without having to know about the specific
-            features of minidump-stackwalk. This is equivalent to ProcessorOptions in minidump-processor. The current
-            supported values are:
+            This flag provides a way to more blindly opt into Extra Analysis without having to know
+            about the specific features of minidump-stackwalk. This is equivalent to
+            ProcessorOptions in minidump-processor. The current supported values are:
             
-            * stable-basic (default): give me solid detailed analysis that most people would want.
+            * stable-basic (default): give me solid detailed analysis that most people would want
             * stable-all: turn on extra detailed analysis.
             * unstable-all: turn on the weird and experimental stuff.
             
@@ -73,20 +62,20 @@ OPTIONS:
             
             unstable-all enables: `--recover-function-args`
             
-            minidump-stackwalk wants to be a reliable and stable tool, but we also want to be able to introduce new
-            features which may be experimental or expensive. To balance these two concerns, new features will usually be
-            disabled by default and given a specific flag, but still more easily 'discovered' by anyone who uses this
-            flag.
+            minidump-stackwalk wants to be a reliable and stable tool, but we also want to be able
+            to introduce new features which may be experimental or expensive. To balance these two
+            concerns, new features will usually be disabled by default and given a specific flag,
+            but still more easily 'discovered' by anyone who uses this flag.
             
-            Anyone using minidump-stackwalk who is *really* worried about the output being stable should probably not
-            use this flag in production, but its use is recommended for casual
+            Anyone using minidump-stackwalk who is *really* worried about the output being stable
+            should probably not use this flag in production, but its use is recommended for casual
             human usage or for checking "what's new".
             
-            Features under unstable-all may be deprecated and become noops. Features which require additional input
-            (such as `--evil-json`) cannot be affected by this, and must still be manually 'discovered'.
-            
-            
-             [default: stable-basic]  [possible values: stable-basic, stable-all, unstable-all]
+            Features under unstable-all may be deprecated and become noops. Features which require
+            additional input (such as `--evil-json`) cannot be affected by this, and must still be
+            manually 'discovered'.[default: stable-basic]
+            [possible values: stable-basic, stable-all, unstable-all]
+
         --output-file <output-file>
             Where to write the output to (if unspecified, stdout is used)
 
@@ -96,71 +85,83 @@ OPTIONS:
         --verbose <verbose>
             Set the logging level.
             
-            The unwinder has been heavily instrumented with `trace` logging, so if you want to debug why an unwind
-            happened the way it did, --verbose=trace is very useful (all unwinder logging will be prefixed with
-            `unwind:`).
-            
+            The unwinder has been heavily instrumented with `trace` logging, so if you want to debug
+            why an unwind happened the way it did, --verbose=trace is very useful (all unwinder
+            logging will be prefixed with `unwind:`).[default: error]
+            [possible values: off, error, warn, info, debug, trace]
+
+        --pretty
+            Pretty-print --json output.
+
+        --brief
+            Provide a briefer --human report.
             
-             [default: error]  [possible values: off, error, warn, info, debug, trace]
+            Only provides the top-level summary and a backtrace of the crashing thread.
+
         --evil-json <evil-json>
             **[UNSTABLE]** An input JSON file with the extra information.
             
-            This is a gross hack for some legacy side-channel information that mozilla uses. It will hopefully be phased
-            out and deprecated in favour of just using custom streams in the minidump itself.
+            This is a gross hack for some legacy side-channel information that mozilla uses. It will
+            hopefully be phased out and deprecated in favour of just using custom streams in the
+            minidump itself.
+
+        --recover-function-args
+            **[UNSTABLE]** Heuristically recover function arguments
             
-        --symbols-url <symbols-url>...
+            This is an experimental feature, which currently only shows up in --human output.
+
+        --symbols-url <symbols-url>
             base URL from which URLs to symbol files can be constructed.
             
-            If multiple symbols-url values are provided, they will each be tried in order until one resolves.
+            If multiple symbols-url values are provided, they will each be tried in order until one
+            resolves.
             
-            The server the base URL points to is expected to conform to the Tecken symbol server protocol. For more
-            details, see the Tecken docs:
+            The server the base URL points to is expected to conform to the Tecken symbol server
+            protocol. For more details, see the Tecken docs:
             
             https://tecken.readthedocs.io/en/latest/
             
             Example symbols-url value: https://symbols.mozilla.org/
-            
+
         --symbols-cache <symbols-cache>
             A directory in which downloaded symbols can be stored.
-                                
-            Symbol files can be very large, so we recommend placing cached files in your system's temp directory so that
-            it can garbage collect unused ones for you. To this end, the default value for this flag is a `rust-
-            minidump-cache` subdirectory of `std::env::temp_dir()` (usually /tmp/rust-minidump-cache on linux).
             
-            symbols-cache must be on the same filesystem as symbols-tmp (if that doesn't mean anything to you, don't
-            worry about it, you're probably not doing something that will run afoul of it).
+            Symbol files can be very large, so we recommend placing cached files in your system's
+            temp directory so that it can garbage collect unused ones for you. To this end, the
+            default value for this flag is a `rust-minidump-cache` subdirectory of
+            `std::env::temp_dir()` (usually /tmp/rust-minidump-cache on linux).
             
+            symbols-cache must be on the same filesystem as symbols-tmp (if that doesn't mean
+            anything to you, don't worry about it, you're probably not doing something that will run
+            afoul of it).
+
         --symbols-tmp <symbols-tmp>
             A directory to use as temp space for downloading symbols.
             
-            A temp dir is necessary to allow for multiple rust-minidump instances to share a cache without race
-            conditions. Files to be added to the cache will be constructed in this location before being atomically
-            moved to the cache.
+            A temp dir is necessary to allow for multiple rust-minidump instances to share a cache
+            without race conditions. Files to be added to the cache will be constructed in this
+            location before being atomically moved to the cache.
             
-            If no path is specified, `std::env::temp_dir()` will be used to improve portability. See the rust
-            documentation for how to set that value if you wish to use something other than your system's default temp
-            directory.
-            
-            symbols-tmp must be on the same filesystem as symbols-cache (if that doesn't mean anything to you, don't
-            worry about it, you're probably not doing something that will run afoul of it).
+            If no path is specified, `std::env::temp_dir()` will be used to improve portability. See
+            the rust documentation for how to set that value if you wish to use something other than
+            your system's default temp directory.
             
+            symbols-tmp must be on the same filesystem as symbols-cache (if that doesn't mean
+            anything to you, don't worry about it, you're probably not doing something that will run
+            afoul of it).
+
         --symbol-download-timeout-secs <symbol-download-timeout-secs>
             The maximum amount of time (in seconds) a symbol file download is allowed to take.
             
             This is necessary to enforce forward progress on misbehaving http responses.
             
-             [default: 1000]
+            [default: 1000]
 
-ARGS:
-    <minidump>
-            Path to the minidump file to analyze.
+    -h, --help
+            Print help information
 
-    <symbols-path>...
-            Path to a symbol file.
-            
-            If multiple symbols-path values are provided, all symbol files will be merged into minidump-stackwalk's
-            symbol database.
-            
+    -V, --version
+            Print version information
 
 
 NOTES:
@@ -171,25 +172,21 @@ Purpose of Symbols:
 
   1. To fill in more information about each frame of the backtraces. (function names, lines, etc.)
 
-  2. To do produce a more *accurate* backtrace. This is primarily accomplished with call frame information (CFI), but
-just knowing what parts of a module maps to actual code is also useful!
-
-
+  2. To do produce a more *accurate* backtrace. This is primarily accomplished with call frame
+information (CFI), but just knowing what parts of a module maps to actual code is also useful!
 
 Supported Symbol Formats:
 
-  Currently only breakpad text symbol files are supported, although we hope to eventually support native formats like
-PDB and DWARF as well.
-
-
+  Currently only breakpad text symbol files are supported, although we hope to eventually support
+native formats like PDB and DWARF as well.
 
 Breakpad Symbol Files:
 
-  Breakpad symbol files are basically a simplified version of the information found in native debuginfo formats. We
-recommend using a version of dump_syms to generate them.
+  Breakpad symbol files are basically a simplified version of the information found in native
+debuginfo formats. We recommend using a version of dump_syms to generate them.
 
   See:
-    * symbol file docs: https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/symbol_files.md
+    * https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/symbol_files.md
     * mozilla's dump_syms (co-developed with this program): https://github.com/mozilla/dump_syms
 
 
diff --git a/minidump-stackwalk/tests/snapshots/test_minidump_stackwalk__markdown-help.snap b/minidump-stackwalk/tests/snapshots/test_minidump_stackwalk__markdown-help.snap
index 0c7f6b5..36a0ba1 100644
--- a/minidump-stackwalk/tests/snapshots/test_minidump_stackwalk__markdown-help.snap
+++ b/minidump-stackwalk/tests/snapshots/test_minidump_stackwalk__markdown-help.snap
@@ -1,8 +1,6 @@
 ---
 source: minidump-stackwalk/tests/test-minidump-stackwalk.rs
-assertion_line: 502
 expression: stdout
-
 ---
 # minidump-stackwalk CLI manual
 
@@ -15,7 +13,17 @@ Analyzes minidumps and produces a report (either human-readable or JSON).
 # USAGE
 minidump-stackwalk [FLAGS] [OPTIONS] <minidump> [--] [symbols-path]...
 
-# FLAGS
+# ARGS
+### `<minidump>`
+Path to the minidump file to analyze.
+
+### `<symbols-path>...`
+Path to a symbol file.
+
+If multiple symbols-path values are provided, all symbol files will be merged into
+minidump-stackwalk's symbol database.
+
+# OPTIONS
 ### `--json`
 Emit a machine-readable JSON report.
 
@@ -25,52 +33,33 @@ https://github.com/luser/rust-minidump/blob/master/minidump-processor/json-schem
 ### `--human`
 Emit a human-readable report (the default).
 
-The human-readable report does not have a specified format, and may not have as many details as the JSON
-format. It is intended for quickly inspecting a crash or debugging rust-minidump itself.
-
-### `--dump`
-Dump the 'raw' contents of the minidump.
-
-This is an implementation of the functionality of the old minidump_dump tool. It minimally parses and
-interprets the minidump in an attempt to produce a fairly 'raw' dump of the minidump's contents. This is
-most useful for debugging minidump-stackwalk itself, or a misbehaving minidump generator.
-
-### `--pretty`
-Pretty-print --json output.
-
-### `--brief`
-Provide a briefer --human report.
-
-Only provides the top-level summary and a backtrace of the crashing thread.
-
-### `--recover-function-args`
-**[UNSTABLE]** Heuristically recover function arguments
-
-This is an experimental feature, which currently only shows up in --human output.
-
-### `-h, --help`
-Prints help information
-
-### `-V, --version`
-Prints version information
-
+The human-readable report does not have a specified format, and may not have as many
+details as the JSON format. It is intended for quickly inspecting a crash or debugging
+rust-minidump itself.
 
-# OPTIONS
 ### `--cyborg <cyborg>`
 Combine --human and --json
 
-Because this creates two output streams, you must specify a path to write the --json output to. The --human
-output will be the 'primary' output and default to stdout, which can be configured with --output-file as
-normal.
+Because this creates two output streams, you must specify a path to write the --json
+output to. The --human output will be the 'primary' output and default to stdout, which
+can be configured with --output-file as normal.
+
+### `--dump`
+Dump the 'raw' contents of the minidump.
+
+This is an implementation of the functionality of the old minidump_dump tool. It
+minimally parses and interprets the minidump in an attempt to produce a fairly 'raw'
+dump of the minidump's contents. This is most useful for debugging minidump-stackwalk
+itself, or a misbehaving minidump generator.
 
 ### `--features <features>`
 Specify at a high-level how much analysis to perform.
 
-This flag provides a way to more blindly opt into Extra Analysis without having to know about the specific
-features of minidump-stackwalk. This is equivalent to ProcessorOptions in minidump-processor. The current
-supported values are:
+This flag provides a way to more blindly opt into Extra Analysis without having to know
+about the specific features of minidump-stackwalk. This is equivalent to
+ProcessorOptions in minidump-processor. The current supported values are:
 
-* stable-basic (default): give me solid detailed analysis that most people would want.
+* stable-basic (default): give me solid detailed analysis that most people would want
 * stable-all: turn on extra detailed analysis.
 * unstable-all: turn on the weird and experimental stuff.
 
@@ -78,20 +67,20 @@ stable-all enables: nothing (currently identical to stable-basic)
 
 unstable-all enables: `--recover-function-args`
 
-minidump-stackwalk wants to be a reliable and stable tool, but we also want to be able to introduce new
-features which may be experimental or expensive. To balance these two concerns, new features will usually be
-disabled by default and given a specific flag, but still more easily 'discovered' by anyone who uses this
-flag.
+minidump-stackwalk wants to be a reliable and stable tool, but we also want to be able
+to introduce new features which may be experimental or expensive. To balance these two
+concerns, new features will usually be disabled by default and given a specific flag,
+but still more easily 'discovered' by anyone who uses this flag.
 
-Anyone using minidump-stackwalk who is *really* worried about the output being stable should probably not
-use this flag in production, but its use is recommended for casual
+Anyone using minidump-stackwalk who is *really* worried about the output being stable
+should probably not use this flag in production, but its use is recommended for casual
 human usage or for checking "what's new".
 
-Features under unstable-all may be deprecated and become noops. Features which require additional input
-(such as `--evil-json`) cannot be affected by this, and must still be manually 'discovered'.
-
+Features under unstable-all may be deprecated and become noops. Features which require
+additional input (such as `--evil-json`) cannot be affected by this, and must still be
+manually 'discovered'.[default: stable-basic]
+\[possible values: stable-basic, stable-all, unstable-all]
 
-\[default: stable-basic]  [possible values: stable-basic, stable-all, unstable-all]
 ### `--output-file <output-file>`
 Where to write the output to (if unspecified, stdout is used)
 
@@ -101,25 +90,39 @@ Where to write logs to (if unspecified, stderr is used)
 ### `--verbose <verbose>`
 Set the logging level.
 
-The unwinder has been heavily instrumented with `trace` logging, so if you want to debug why an unwind
-happened the way it did, --verbose=trace is very useful (all unwinder logging will be prefixed with
-`unwind:`).
+The unwinder has been heavily instrumented with `trace` logging, so if you want to debug
+why an unwind happened the way it did, --verbose=trace is very useful (all unwinder
+logging will be prefixed with `unwind:`).[default: error]
+\[possible values: off, error, warn, info, debug, trace]
+
+### `--pretty`
+Pretty-print --json output.
+
+### `--brief`
+Provide a briefer --human report.
 
+Only provides the top-level summary and a backtrace of the crashing thread.
 
-\[default: error]  [possible values: off, error, warn, info, debug, trace]
 ### `--evil-json <evil-json>`
 **[UNSTABLE]** An input JSON file with the extra information.
 
-This is a gross hack for some legacy side-channel information that mozilla uses. It will hopefully be phased
-out and deprecated in favour of just using custom streams in the minidump itself.
+This is a gross hack for some legacy side-channel information that mozilla uses. It will
+hopefully be phased out and deprecated in favour of just using custom streams in the
+minidump itself.
+
+### `--recover-function-args`
+**[UNSTABLE]** Heuristically recover function arguments
+
+This is an experimental feature, which currently only shows up in --human output.
 
-### `--symbols-url <symbols-url>...`
+### `--symbols-url <symbols-url>`
 base URL from which URLs to symbol files can be constructed.
 
-If multiple symbols-url values are provided, they will each be tried in order until one resolves.
+If multiple symbols-url values are provided, they will each be tried in order until one
+resolves.
 
-The server the base URL points to is expected to conform to the Tecken symbol server protocol. For more
-details, see the Tecken docs:
+The server the base URL points to is expected to conform to the Tecken symbol server
+protocol. For more details, see the Tecken docs:
 
 https://tecken.readthedocs.io/en/latest/
 
@@ -128,26 +131,29 @@ Example symbols-url value: https://symbols.mozilla.org/
 ### `--symbols-cache <symbols-cache>`
 A directory in which downloaded symbols can be stored.
 
-Symbol files can be very large, so we recommend placing cached files in your system's temp directory so that
-it can garbage collect unused ones for you. To this end, the default value for this flag is a `rust-
-minidump-cache` subdirectory of `std::env::temp_dir()` (usually /tmp/rust-minidump-cache on linux).
+Symbol files can be very large, so we recommend placing cached files in your system's
+temp directory so that it can garbage collect unused ones for you. To this end, the
+default value for this flag is a `rust-minidump-cache` subdirectory of
+`std::env::temp_dir()` (usually /tmp/rust-minidump-cache on linux).
 
-symbols-cache must be on the same filesystem as symbols-tmp (if that doesn't mean anything to you, don't
-worry about it, you're probably not doing something that will run afoul of it).
+symbols-cache must be on the same filesystem as symbols-tmp (if that doesn't mean
+anything to you, don't worry about it, you're probably not doing something that will run
+afoul of it).
 
 ### `--symbols-tmp <symbols-tmp>`
 A directory to use as temp space for downloading symbols.
 
-A temp dir is necessary to allow for multiple rust-minidump instances to share a cache without race
-conditions. Files to be added to the cache will be constructed in this location before being atomically
-moved to the cache.
+A temp dir is necessary to allow for multiple rust-minidump instances to share a cache
+without race conditions. Files to be added to the cache will be constructed in this
+location before being atomically moved to the cache.
 
-If no path is specified, `std::env::temp_dir()` will be used to improve portability. See the rust
-documentation for how to set that value if you wish to use something other than your system's default temp
-directory.
+If no path is specified, `std::env::temp_dir()` will be used to improve portability. See
+the rust documentation for how to set that value if you wish to use something other than
+your system's default temp directory.
 
-symbols-tmp must be on the same filesystem as symbols-cache (if that doesn't mean anything to you, don't
-worry about it, you're probably not doing something that will run afoul of it).
+symbols-tmp must be on the same filesystem as symbols-cache (if that doesn't mean
+anything to you, don't worry about it, you're probably not doing something that will run
+afoul of it).
 
 ### `--symbol-download-timeout-secs <symbol-download-timeout-secs>`
 The maximum amount of time (in seconds) a symbol file download is allowed to take.
@@ -156,16 +162,11 @@ This is necessary to enforce forward progress on misbehaving http responses.
 
 \[default: 1000]
 
-# ARGS
-### `<minidump>`
-Path to the minidump file to analyze.
-
-### `<symbols-path>...`
-Path to a symbol file.
-
-If multiple symbols-path values are provided, all symbol files will be merged into minidump-stackwalk's
-symbol database.
+### `-h, --help`
+Print help information
 
+### `-V, --version`
+Print version information
 
 
 # NOTES
@@ -176,24 +177,21 @@ Symbols are used for two purposes:
 
 1. To fill in more information about each frame of the backtraces. (function names, lines, etc.)
 
-2. To do produce a more *accurate* backtrace. This is primarily accomplished with call frame information (CFI), but
-just knowing what parts of a module maps to actual code is also useful!
-
-
+2. To do produce a more *accurate* backtrace. This is primarily accomplished with call frame
+information (CFI), but just knowing what parts of a module maps to actual code is also useful!
 
 ## Supported Symbol Formats
 
-Currently only breakpad text symbol files are supported, although we hope to eventually support native formats like
-PDB and DWARF as well.
-
-
+Currently only breakpad text symbol files are supported, although we hope to eventually support
+native formats like PDB and DWARF as well.
 
 ## Breakpad Symbol Files
 
-Breakpad symbol files are basically a simplified version of the information found in native debuginfo formats. We
-recommend using a version of dump_syms to generate them.
+Breakpad symbol files are basically a simplified version of the information found in native
+debuginfo formats. We recommend using a version of dump_syms to generate them.
 
 See:
-* symbol file docs: https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/symbol_files.md
+* https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/symbol_files.md
 * mozilla's dump_syms (co-developed with this program): https://github.com/mozilla/dump_syms
 
+
diff --git a/minidump-stackwalk/tests/snapshots/test_minidump_stackwalk__short-help.snap b/minidump-stackwalk/tests/snapshots/test_minidump_stackwalk__short-help.snap
index 080babd..81050c6 100644
--- a/minidump-stackwalk/tests/snapshots/test_minidump_stackwalk__short-help.snap
+++ b/minidump-stackwalk/tests/snapshots/test_minidump_stackwalk__short-help.snap
@@ -1,8 +1,6 @@
 ---
 source: minidump-stackwalk/tests/test-minidump-stackwalk.rs
-assertion_line: 483
 expression: stdout
-
 ---
 minidump-stackwalk 0.9.6
 Analyzes minidumps and produces a report (either human-readable or JSON).
@@ -10,62 +8,53 @@ Analyzes minidumps and produces a report (either human-readable or JSON).
 USAGE:
     minidump-stackwalk [FLAGS] [OPTIONS] <minidump> [--] [symbols-path]...
 
-FLAGS:
+ARGS:
+    <minidump>
+            Path to the minidump file to analyze.
+
+    <symbols-path>...
+            Path to a symbol file.
+            
+            If multiple symbols-path values are provided, all symbol files will be merged into
+            minidump-stackwalk's symbol database.
+
+OPTIONS:
         --json
             Emit a machine-readable JSON report.
             
             The schema for this output is officially documented here:
             https://github.com/luser/rust-minidump/blob/master/minidump-processor/json-schema.md
-            
+
         --human
             Emit a human-readable report (the default).
             
-            The human-readable report does not have a specified format, and may not have as many details as the JSON
-            format. It is intended for quickly inspecting a crash or debugging rust-minidump itself.
-            
-        --dump
-            Dump the 'raw' contents of the minidump.
-            
-            This is an implementation of the functionality of the old minidump_dump tool. It minimally parses and
-            interprets the minidump in an attempt to produce a fairly 'raw' dump of the minidump's contents. This is
-            most useful for debugging minidump-stackwalk itself, or a misbehaving minidump generator.
-            
-        --pretty
-            Pretty-print --json output.
-
-        --brief
-            Provide a briefer --human report.
-            
-            Only provides the top-level summary and a backtrace of the crashing thread.
-            
-        --recover-function-args
-            **[UNSTABLE]** Heuristically recover function arguments
-            
-            This is an experimental feature, which currently only shows up in --human output.
-            
-    -h, --help
-            Prints help information
-
-    -V, --version
-            Prints version information
-
+            The human-readable report does not have a specified format, and may not have as many
+            details as the JSON format. It is intended for quickly inspecting a crash or debugging
+            rust-minidump itself.
 
-OPTIONS:
         --cyborg <cyborg>
             Combine --human and --json
             
-            Because this creates two output streams, you must specify a path to write the --json output to. The --human
-            output will be the 'primary' output and default to stdout, which can be configured with --output-file as
-            normal.
+            Because this creates two output streams, you must specify a path to write the --json
+            output to. The --human output will be the 'primary' output and default to stdout, which
+            can be configured with --output-file as normal.
+
+        --dump
+            Dump the 'raw' contents of the minidump.
             
+            This is an implementation of the functionality of the old minidump_dump tool. It
+            minimally parses and interprets the minidump in an attempt to produce a fairly 'raw'
+            dump of the minidump's contents. This is most useful for debugging minidump-stackwalk
+            itself, or a misbehaving minidump generator.
+
         --features <features>
             Specify at a high-level how much analysis to perform.
             
-            This flag provides a way to more blindly opt into Extra Analysis without having to know about the specific
-            features of minidump-stackwalk. This is equivalent to ProcessorOptions in minidump-processor. The current
-            supported values are:
+            This flag provides a way to more blindly opt into Extra Analysis without having to know
+            about the specific features of minidump-stackwalk. This is equivalent to
+            ProcessorOptions in minidump-processor. The current supported values are:
             
-            * stable-basic (default): give me solid detailed analysis that most people would want.
+            * stable-basic (default): give me solid detailed analysis that most people would want
             * stable-all: turn on extra detailed analysis.
             * unstable-all: turn on the weird and experimental stuff.
             
@@ -73,20 +62,20 @@ OPTIONS:
             
             unstable-all enables: `--recover-function-args`
             
-            minidump-stackwalk wants to be a reliable and stable tool, but we also want to be able to introduce new
-            features which may be experimental or expensive. To balance these two concerns, new features will usually be
-            disabled by default and given a specific flag, but still more easily 'discovered' by anyone who uses this
-            flag.
+            minidump-stackwalk wants to be a reliable and stable tool, but we also want to be able
+            to introduce new features which may be experimental or expensive. To balance these two
+            concerns, new features will usually be disabled by default and given a specific flag,
+            but still more easily 'discovered' by anyone who uses this flag.
             
-            Anyone using minidump-stackwalk who is *really* worried about the output being stable should probably not
-            use this flag in production, but its use is recommended for casual
+            Anyone using minidump-stackwalk who is *really* worried about the output being stable
+            should probably not use this flag in production, but its use is recommended for casual
             human usage or for checking "what's new".
             
-            Features under unstable-all may be deprecated and become noops. Features which require additional input
-            (such as `--evil-json`) cannot be affected by this, and must still be manually 'discovered'.
-            
-            
-             [default: stable-basic]  [possible values: stable-basic, stable-all, unstable-all]
+            Features under unstable-all may be deprecated and become noops. Features which require
+            additional input (such as `--evil-json`) cannot be affected by this, and must still be
+            manually 'discovered'.[default: stable-basic] [possible values: stable-basic, stable-
+            all, unstable-all]
+
         --output-file <output-file>
             Where to write the output to (if unspecified, stdout is used)
 
@@ -96,71 +85,82 @@ OPTIONS:
         --verbose <verbose>
             Set the logging level.
             
-            The unwinder has been heavily instrumented with `trace` logging, so if you want to debug why an unwind
-            happened the way it did, --verbose=trace is very useful (all unwinder logging will be prefixed with
-            `unwind:`).
-            
+            The unwinder has been heavily instrumented with `trace` logging, so if you want to debug
+            why an unwind happened the way it did, --verbose=trace is very useful (all unwinder
+            logging will be prefixed with `unwind:`).[default: error] [possible values: off, error,
+            warn, info, debug, trace]
+
+        --pretty
+            Pretty-print --json output.
+
+        --brief
+            Provide a briefer --human report.
             
-             [default: error]  [possible values: off, error, warn, info, debug, trace]
+            Only provides the top-level summary and a backtrace of the crashing thread.
+
         --evil-json <evil-json>
             **[UNSTABLE]** An input JSON file with the extra information.
             
-            This is a gross hack for some legacy side-channel information that mozilla uses. It will hopefully be phased
-            out and deprecated in favour of just using custom streams in the minidump itself.
+            This is a gross hack for some legacy side-channel information that mozilla uses. It will
+            hopefully be phased out and deprecated in favour of just using custom streams in the
+            minidump itself.
+
+        --recover-function-args
+            **[UNSTABLE]** Heuristically recover function arguments
             
-        --symbols-url <symbols-url>...
+            This is an experimental feature, which currently only shows up in --human output.
+
+        --symbols-url <symbols-url>
             base URL from which URLs to symbol files can be constructed.
             
-            If multiple symbols-url values are provided, they will each be tried in order until one resolves.
+            If multiple symbols-url values are provided, they will each be tried in order until one
+            resolves.
             
-            The server the base URL points to is expected to conform to the Tecken symbol server protocol. For more
-            details, see the Tecken docs:
+            The server the base URL points to is expected to conform to the Tecken symbol server
+            protocol. For more details, see the Tecken docs:
             
             https://tecken.readthedocs.io/en/latest/
             
             Example symbols-url value: https://symbols.mozilla.org/
-            
+
         --symbols-cache <symbols-cache>
             A directory in which downloaded symbols can be stored.
-                                
-            Symbol files can be very large, so we recommend placing cached files in your system's temp directory so that
-            it can garbage collect unused ones for you. To this end, the default value for this flag is a `rust-
-            minidump-cache` subdirectory of `std::env::temp_dir()` (usually /tmp/rust-minidump-cache on linux).
             
-            symbols-cache must be on the same filesystem as symbols-tmp (if that doesn't mean anything to you, don't
-            worry about it, you're probably not doing something that will run afoul of it).
+            Symbol files can be very large, so we recommend placing cached files in your system's
+            temp directory so that it can garbage collect unused ones for you. To this end, the
+            default value for this flag is a `rust-minidump-cache` subdirectory of
+            `std::env::temp_dir()` (usually /tmp/rust-minidump-cache on linux).
             
+            symbols-cache must be on the same filesystem as symbols-tmp (if that doesn't mean
+            anything to you, don't worry about it, you're probably not doing something that will run
+            afoul of it).
+
         --symbols-tmp <symbols-tmp>
             A directory to use as temp space for downloading symbols.
             
-            A temp dir is necessary to allow for multiple rust-minidump instances to share a cache without race
-            conditions. Files to be added to the cache will be constructed in this location before being atomically
-            moved to the cache.
-            
-            If no path is specified, `std::env::temp_dir()` will be used to improve portability. See the rust
-            documentation for how to set that value if you wish to use something other than your system's default temp
-            directory.
+            A temp dir is necessary to allow for multiple rust-minidump instances to share a cache
+            without race conditions. Files to be added to the cache will be constructed in this
+            location before being atomically moved to the cache.
             
-            symbols-tmp must be on the same filesystem as symbols-cache (if that doesn't mean anything to you, don't
-            worry about it, you're probably not doing something that will run afoul of it).
+            If no path is specified, `std::env::temp_dir()` will be used to improve portability. See
+            the rust documentation for how to set that value if you wish to use something other than
+            your system's default temp directory.
             
+            symbols-tmp must be on the same filesystem as symbols-cache (if that doesn't mean
+            anything to you, don't worry about it, you're probably not doing something that will run
+            afoul of it).
+
         --symbol-download-timeout-secs <symbol-download-timeout-secs>
             The maximum amount of time (in seconds) a symbol file download is allowed to take.
             
-            This is necessary to enforce forward progress on misbehaving http responses.
-            
-             [default: 1000]
+            This is necessary to enforce forward progress on misbehaving http responses. [default:
+            1000]
 
-ARGS:
-    <minidump>
-            Path to the minidump file to analyze.
+    -h, --help
+            Print help information
 
-    <symbols-path>...
-            Path to a symbol file.
-            
-            If multiple symbols-path values are provided, all symbol files will be merged into minidump-stackwalk's
-            symbol database.
-            
+    -V, --version
+            Print version information
 
 
 NOTES:
@@ -171,25 +171,21 @@ Purpose of Symbols:
 
   1. To fill in more information about each frame of the backtraces. (function names, lines, etc.)
 
-  2. To do produce a more *accurate* backtrace. This is primarily accomplished with call frame information (CFI), but
-just knowing what parts of a module maps to actual code is also useful!
-
-
+  2. To do produce a more *accurate* backtrace. This is primarily accomplished with call frame
+information (CFI), but just knowing what parts of a module maps to actual code is also useful!
 
 Supported Symbol Formats:
 
-  Currently only breakpad text symbol files are supported, although we hope to eventually support native formats like
-PDB and DWARF as well.
-
-
+  Currently only breakpad text symbol files are supported, although we hope to eventually support
+native formats like PDB and DWARF as well.
 
 Breakpad Symbol Files:
 
-  Breakpad symbol files are basically a simplified version of the information found in native debuginfo formats. We
-recommend using a version of dump_syms to generate them.
+  Breakpad symbol files are basically a simplified version of the information found in native
+debuginfo formats. We recommend using a version of dump_syms to generate them.
 
   See:
-    * symbol file docs: https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/symbol_files.md
+    * https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/symbol_files.md
     * mozilla's dump_syms (co-developed with this program): https://github.com/mozilla/dump_syms