Revision control
Copy as Markdown
Other Tools
// Copyright 2015 Ted Mielczarek. See the COPYRIGHT
// file at the top-level directory of this distribution.
use debugid::{CodeId, DebugId};
use minidump::system_info::{Cpu, Os};
use minidump::*;
use minidump_common::format as md;
use num_traits::cast::FromPrimitive;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use std::time::SystemTime;
fn get_test_minidump_path(filename: &str) -> PathBuf {
let mut path = PathBuf::from(file!());
path.pop();
path.pop();
path.pop();
path.push("../");
path.push("testdata");
path.push(filename);
println!("{path:?}");
path
}
fn read_test_minidump() -> Result<MmapMinidump, Error> {
let path = get_test_minidump_path("test.dmp");
Minidump::read_path(path)
}
fn read_linux_minidump() -> Result<MmapMinidump, Error> {
let path = get_test_minidump_path("linux-mini.dmp");
Minidump::read_path(path)
}
#[ctor::ctor]
fn init_logger() {
env_logger::builder().is_test(true).init();
}
#[test]
fn test_minidump_read_path() {
read_test_minidump().unwrap();
}
#[test]
fn test_minidump_read() {
let path = get_test_minidump_path("test.dmp");
let mut f = File::open(path).unwrap();
let mut buf = vec![];
f.read_to_end(&mut buf).unwrap();
let _dump = Minidump::read(buf).unwrap();
}
#[test]
fn test_module_list() {
let dump = read_test_minidump().unwrap();
let module_list = dump.get_stream::<MinidumpModuleList>().unwrap();
assert_eq!(
module_list.module_at_address(0x400000).unwrap().code_file(),
"c:\\test_app.exe"
);
let modules = module_list.iter().collect::<Vec<_>>();
let module_files = modules.iter().map(|m| m.code_file()).collect::<Vec<_>>();
assert_eq!(modules.len(), 13);
assert_eq!(modules[0].base_address(), 0x400000);
assert_eq!(modules[0].size(), 0x2d000);
assert_eq!(modules[0].code_file(), "c:\\test_app.exe");
assert_eq!(
modules[0].code_identifier().unwrap(),
CodeId::new("45D35F6C2d000".to_string())
);
assert_eq!(modules[0].debug_file().unwrap(), "c:\\test_app.pdb");
assert_eq!(
modules[0].debug_identifier().unwrap(),
DebugId::from_breakpad("5A9832E5287241C1838ED98914E9B7FF1").unwrap()
);
assert!(modules[0].version().is_none());
assert_eq!(modules[12].base_address(), 0x76bf0000);
assert_eq!(modules[12].size(), 0xb000);
assert_eq!(modules[12].code_file(), "C:\\WINDOWS\\system32\\psapi.dll");
assert_eq!(
modules[12].code_identifier().unwrap(),
CodeId::new("411096CAb000".to_string())
);
assert_eq!(modules[12].debug_file().unwrap(), "psapi.pdb");
assert_eq!(
modules[12].debug_identifier().unwrap(),
DebugId::from_breakpad("A5C3A1F9689F43D8AD228A09293889702").unwrap()
);
assert_eq!(modules[12].version().unwrap(), "5.1.2600.2180");
assert_eq!(
module_files,
vec![
r"c:\test_app.exe",
r"C:\WINDOWS\system32\ntdll.dll",
r"C:\WINDOWS\system32\kernel32.dll",
r"C:\WINDOWS\system32\ole32.dll",
r"C:\WINDOWS\system32\advapi32.dll",
r"C:\WINDOWS\system32\rpcrt4.dll",
r"C:\WINDOWS\system32\gdi32.dll",
r"C:\WINDOWS\system32\user32.dll",
r"C:\WINDOWS\system32\msvcrt.dll",
r"C:\WINDOWS\system32\imm32.dll",
r"C:\WINDOWS\system32\dbghelp.dll",
r"C:\WINDOWS\system32\version.dll",
r"C:\WINDOWS\system32\psapi.dll",
]
);
assert_eq!(
module_list
.by_addr()
.map(|m| m.code_file())
.collect::<Vec<_>>(),
vec![
r"c:\test_app.exe",
r"C:\WINDOWS\system32\dbghelp.dll",
r"C:\WINDOWS\system32\imm32.dll",
r"C:\WINDOWS\system32\psapi.dll",
r"C:\WINDOWS\system32\ole32.dll",
r"C:\WINDOWS\system32\version.dll",
r"C:\WINDOWS\system32\msvcrt.dll",
r"C:\WINDOWS\system32\user32.dll",
r"C:\WINDOWS\system32\advapi32.dll",
r"C:\WINDOWS\system32\rpcrt4.dll",
r"C:\WINDOWS\system32\gdi32.dll",
r"C:\WINDOWS\system32\kernel32.dll",
r"C:\WINDOWS\system32\ntdll.dll",
]
);
}
#[test]
fn test_system_info() {
let dump = read_test_minidump().unwrap();
let system_info = dump.get_stream::<MinidumpSystemInfo>().unwrap();
assert_eq!(system_info.os, Os::Windows);
assert_eq!(system_info.cpu, Cpu::X86);
assert_eq!(
system_info.cpu_info().unwrap(),
"GenuineIntel family 6 model 13 stepping 8"
);
assert_eq!(&system_info.csd_version().unwrap(), "Service Pack 2");
}
#[test]
fn test_misc_info() {
let dump = read_test_minidump().unwrap();
let misc_info = dump.get_stream::<MinidumpMiscInfo>().unwrap();
assert_eq!(misc_info.raw.process_id(), Some(&3932));
assert_eq!(misc_info.raw.process_create_time(), Some(&0x45d35f73));
assert_eq!(
misc_info
.process_create_time()
.unwrap()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs(),
1171480435, // = 2007-02-14T19:13:55
);
}
#[test]
fn test_breakpad_info() {
let dump = read_test_minidump().unwrap();
let breakpad_info = dump.get_stream::<MinidumpBreakpadInfo>().unwrap();
assert_eq!(breakpad_info.dump_thread_id.unwrap(), 0x11c0);
assert_eq!(breakpad_info.requesting_thread_id.unwrap(), 0xbf4);
}
#[test]
fn test_crashpad_info() {
let path = get_test_minidump_path("simple-crashpad.dmp");
let dump = Minidump::read_path(&path).unwrap();
let crashpad_info = dump.get_stream::<MinidumpCrashpadInfo>().unwrap();
let report_id = md::GUID {
data1: 0x42F9_DE72,
data2: 0x518A,
data3: 0x43DD,
data4: [0x97, 0xD7, 0x8D, 0xDC, 0x32, 0x8D, 0x36, 0x62],
};
assert_eq!(crashpad_info.raw.report_id, report_id);
let client_id = md::GUID {
data1: 0x6FD2_B3B9,
data2: 0x9833,
data3: 0x4B2F,
data4: [0xBB, 0xF7, 0xB, 0xCF, 0x50, 0x1B, 0xAD, 0x7E],
};
assert_eq!(crashpad_info.raw.client_id, client_id);
assert_eq!(crashpad_info.simple_annotations["hello"], "world");
assert_eq!(crashpad_info.module_list.len(), 2);
let module = &crashpad_info.module_list[0];
assert_eq!(module.module_index, 16);
assert_eq!(module.list_annotations, vec!["abort() called".to_owned()]);
assert!(module.simple_annotations.is_empty());
assert!(module.annotation_objects.is_empty());
}
#[test]
fn test_assertion() {
let path = get_test_minidump_path("invalid-parameter.dmp");
let dump = Minidump::read_path(&path).unwrap();
let assertion = dump.get_stream::<MinidumpAssertion>().unwrap();
assert_eq!(assertion.expression().unwrap(), "format != nullptr");
assert_eq!(assertion.function().unwrap(), "common_vfprintf");
assert_eq!(
assertion.file().unwrap(),
r"minkernel\crts\ucrt\src\appcrt\stdio\output.cpp"
);
assert_eq!(assertion.raw.line, 32);
assert_eq!(
md::AssertionType::from_u32(assertion.raw._type),
Some(md::AssertionType::InvalidParameter)
);
}
#[test]
fn test_exception() {
let dump = read_test_minidump().unwrap();
let exception = dump.get_stream::<MinidumpException>().unwrap();
let system_info = dump.get_stream::<MinidumpSystemInfo>().unwrap();
let misc_info = dump.get_stream::<MinidumpMiscInfo>().ok();
assert_eq!(exception.thread_id, 0xbf4);
assert_eq!(exception.raw.exception_record.exception_code, 0xc0000005);
if let Some(ctx) = exception
.context(&system_info, misc_info.as_ref())
.as_deref()
{
assert_eq!(ctx.get_instruction_pointer(), 0x40429e);
assert_eq!(ctx.get_stack_pointer(), 0x12fe84);
if let MinidumpContext {
raw: MinidumpRawContext::X86(ref raw),
ref valid,
} = *ctx
{
assert_eq!(raw.eip, 0x40429e);
assert_eq!(*valid, MinidumpContextValidity::All);
} else {
panic!("Wrong context type");
}
} else {
panic!("Missing context");
}
}
#[test]
fn test_thread_list() {
let dump = read_test_minidump().unwrap();
let thread_list = dump.get_stream::<MinidumpThreadList<'_>>().unwrap();
let system_info = dump.get_stream::<MinidumpSystemInfo>().unwrap();
let misc_info = dump.get_stream::<MinidumpMiscInfo>().ok();
let memory_list = dump.get_memory().unwrap_or_default();
let threads = &thread_list.threads;
assert_eq!(threads.len(), 2);
assert_eq!(threads[0].raw.thread_id, 0xbf4);
assert_eq!(threads[1].raw.thread_id, 0x11c0);
let id = threads[1].raw.thread_id;
assert_eq!(thread_list.get_thread(id).unwrap().raw.thread_id, id);
if let Some(ctx) = threads[0]
.context(&system_info, misc_info.as_ref())
.as_deref()
{
assert_eq!(ctx.get_instruction_pointer(), 0x7c90eb94);
assert_eq!(ctx.get_stack_pointer(), 0x12f320);
if let MinidumpContext {
raw: MinidumpRawContext::X86(ref raw),
ref valid,
} = *ctx
{
assert_eq!(raw.eip, 0x7c90eb94);
assert_eq!(*valid, MinidumpContextValidity::All);
} else {
panic!("Wrong context type");
}
} else {
panic!("Missing context");
}
if let Some(ref stack) = threads[0].stack_memory(&memory_list) {
// Try the beginning
assert_eq!(stack.get_memory_at_address::<u8>(0x12f31c).unwrap(), 0);
assert_eq!(stack.get_memory_at_address::<u16>(0x12f31c).unwrap(), 0);
assert_eq!(stack.get_memory_at_address::<u32>(0x12f31c).unwrap(), 0);
assert_eq!(
stack.get_memory_at_address::<u64>(0x12f31c).unwrap(),
0x7c90e9c000000000
);
// And the end
assert_eq!(stack.get_memory_at_address::<u8>(0x12ffff).unwrap(), 0);
assert_eq!(stack.get_memory_at_address::<u16>(0x12fffe).unwrap(), 0);
assert_eq!(stack.get_memory_at_address::<u32>(0x12fffc).unwrap(), 0);
assert_eq!(
stack.get_memory_at_address::<u64>(0x12fff8).unwrap(),
0x405443
);
} else {
panic!("Missing stack memory");
}
}
#[test]
fn test_empty_minidump() {
match Minidump::read(&b""[..]) {
Ok(_) => panic!("Should have failed to read minidump"),
Err(e) => assert_eq!(e, Error::MissingHeader),
}
}
#[test]
fn backwards_range() {
let data = include_bytes!("../../testdata/invalid-range.dmp");
match Minidump::read(&data[..]) {
Ok(f) => {
// TODO verify this is correct
// This seems to call `MinidumpStream::read()` with a `bytes` that is the entire
// minidump!
let _ = f
.get_stream::<MinidumpLinuxMaps>()
.expect_err("range should be invalid");
}
Err(e) => {
panic!("Expected to parse the header, got {:?}", e);
}
}
}
#[test]
fn test_record_count_mac_info() {
let data = include_bytes!("../../testdata/invalid-record-count.dmp");
match Minidump::read(&data[..]) {
Ok(f) => {
let _ = f.get_stream::<MinidumpMacCrashInfo>();
}
Err(e) => {
panic!("Expected to parse the header, got {:?}", e);
}
}
}
#[test]
fn test_linux_os_version() {
let dump = read_linux_minidump().unwrap();
let system_info = dump.get_stream::<MinidumpSystemInfo>().unwrap();
assert_eq!(system_info.os_parts().0, "4.9.60-linuxkit-aufs");
assert_eq!(
system_info.os_parts().1.unwrap(),
"#1 SMP Mon Nov 6 16:00:12 UTC 2017",
);
}
#[test]
fn test_full_dump_memory() {
let path = get_test_minidump_path("full-dump.dmp");
let dump = Minidump::read_path(&path).unwrap();
let memory_list = dump.get_stream::<MinidumpMemory64List<'_>>().unwrap();
assert_eq!(memory_list.iter().count(), 54);
let blocks: Vec<_> = memory_list.iter().take(3).collect();
assert_eq!(blocks[0].base_address, 0x007FFE0000);
assert_eq!(blocks[0].size, 0x1000);
assert_eq!(blocks[0].bytes[0..8], [0, 0, 0, 0, 0, 0, 0xA0, 0x0F]);
assert_eq!(blocks[1].base_address, 0x007FFE9000);
assert_eq!(blocks[1].size, 0x1000);
assert_eq!(blocks[1].bytes[0..8], [0x48, 0x61, 0x6C, 0x54, 0, 0, 0, 0]);
assert_eq!(blocks[2].base_address, 0x9897D0D000);
assert_eq!(blocks[2].size, 0x3000);
assert_eq!(blocks[2].bytes[0..8], [0, 0, 0, 0, 0, 0, 0, 0]);
}