import minidump analyze

This commit is contained in:
Julian Freeman
2025-11-26 10:18:40 -04:00
parent 1ad180a413
commit 4fe4150069
2 changed files with 84 additions and 24 deletions

View File

@@ -9,11 +9,10 @@ use sysinfo::{System, Disks};
use wmi::{COMLibrary, WMIConnection};
use std::fs;
use std::path::Path;
use std::io::Read; // [新增] 用于读取文件头
use tauri::Emitter;
use chrono::{Duration, FixedOffset, Local, NaiveDate, TimeZone, DateTime};
// 只引入 minidump 基础库
use minidump::{Minidump, MinidumpException, MinidumpSystemInfo};
// [新增] 引入 Deref 用于泛型约束
use std::ops::Deref;
// --- 1. 数据结构 (保持不变) ---
@@ -190,22 +189,28 @@ fn translate_bugcheck_u32(code: u32) -> (String, String) {
}
}
// [修复] 抽离公共分析逻辑,使用泛型 T 适配不同数据源
// [公共逻辑] 泛型函数,适配文件和内存
fn analyze_dump_data<T>(dump: Minidump<T>) -> Result<BsodAnalysisReport, String>
where T: Deref<Target = [u8]>
{
let exception_stream = dump.get_stream::<MinidumpException>()
.map_err(|_| "无法找到异常信息流 (No Exception Stream),可能是非标准 Dump 文件。".to_string())?;
let exception_code = exception_stream.raw.exception_record.exception_code;
let exception_address = exception_stream.raw.exception_record.exception_address;
let (exception_code, exception_address) = match dump.get_stream::<MinidumpException>() {
Ok(stream) => (
stream.raw.exception_record.exception_code,
stream.raw.exception_record.exception_address
),
Err(_) => (0, 0)
};
let sys_info_str = match dump.get_stream::<MinidumpSystemInfo>() {
Ok(info) => format!("Windows Build {}", info.raw.build_number),
Err(_) => "Unknown OS".to_string(),
};
let (reason_str, recommend) = translate_bugcheck_u32(exception_code);
let (reason_str, recommend) = if exception_code != 0 {
translate_bugcheck_u32(exception_code)
} else {
("未找到异常记录".to_string(), "该文件有效但未包含标准异常流。可能是手动生成的 Dump 或被截断。".to_string())
};
Ok(BsodAnalysisReport {
crash_reason: reason_str,
@@ -217,6 +222,30 @@ where T: Deref<Target = [u8]>
})
}
// [新增] 辅助函数:检查文件头签名
fn check_dump_signature(sig: &[u8]) -> Result<(), String> {
if sig.len() < 4 {
return Err("文件太小,无效的 Dump 文件。".to_string());
}
// 1. 检查标准 Minidump (MDMP)
if sig.starts_with(b"MDMP") {
return Ok(());
}
// 2. 检查 Kernel Dump (PAGE / PAGEDU64)
// 即使文件很小,只要头是 PAGE它就是 Kernel Dump 格式minidump crate 无法解析
if sig.starts_with(b"PAGE") {
return Err("不支持的文件格式:检测到 '内核转储' (PAGEDU64/PAGE)。".to_string());
}
Err(format!(
"文件签名错误!期望 'MDMP', 实际收到: {:02X?} ('{}')。\n这可能不是一个有效的蓝屏文件。",
&sig[0..4],
String::from_utf8_lossy(&sig[0..4]).replace('\0', "")
))
}
// --- 命令:列出 Minidump 文件 ---
#[tauri::command]
async fn list_minidumps() -> Result<Vec<BsodFileItem>, String> {
@@ -256,14 +285,26 @@ async fn list_minidumps() -> Result<Vec<BsodFileItem>, String> {
#[tauri::command]
async fn analyze_minidump(filepath: String) -> Result<BsodAnalysisReport, String> {
let path = Path::new(&filepath);
let dump = Minidump::read_path(path).map_err(|e| format!("无法读取文件: {}", e))?;
// [优化] 先读取头部进行校验,避免 minidump 库报晦涩错误
let mut file = fs::File::open(path).map_err(|e| format!("无法打开文件: {}", e))?;
let mut header = [0u8; 4];
if let Ok(_) = file.read_exact(&mut header) {
check_dump_signature(&header)?;
}
// 重新读取完整文件 (minidump crate 需要完整路径重新打开)
let dump = Minidump::read_path(path).map_err(|e| format!("解析错误: {}", e))?;
analyze_dump_data(dump)
}
// [新增] 命令:分析二进制内容的 Minidump (用于前端导入)
// --- 命令:分析二进制内容的 Minidump ---
#[tauri::command]
async fn analyze_minidump_bytes(file_content: Vec<u8>) -> Result<BsodAnalysisReport, String> {
let dump = Minidump::read(file_content).map_err(|e| format!("无法解析文件内容: {}", e))?;
// [优化] 头部校验
check_dump_signature(&file_content)?;
let dump = Minidump::read(file_content).map_err(|e| format!("解析器拒绝处理: {}", e))?;
analyze_dump_data(dump)
}
@@ -271,14 +312,13 @@ async fn analyze_minidump_bytes(file_content: Vec<u8>) -> Result<BsodAnalysisRep
#[tauri::command]
async fn run_diagnosis(window: tauri::Window) -> Result<(), String> {
std::thread::spawn(move || {
// ... (run_diagnosis 的内容保持不变,这里省略以节省篇幅,请确保保留原有逻辑) ...
// 为了确保代码完整性,这里包含 run_diagnosis 的第一部分作为占位,实际应用中请保持原样
// ... (保持原有逻辑不变) ...
// 占位符
{
let mut sys = System::new();
sys.refresh_memory();
sys.refresh_cpu();
let wmi_con = WMIConnection::new(COMLibrary::new().unwrap()).ok();
// ... 硬件概览逻辑 ...
let mut bios_ver = "Unknown".to_string();
let mut mobo_vendor = "Unknown".to_string();
let mut mobo_product = "Unknown".to_string();
@@ -323,7 +363,6 @@ async fn run_diagnosis(window: tauri::Window) -> Result<(), String> {
};
let _ = window.emit("report-hardware", hardware);
}
// ... (其他部分保持不变) ...
let wmi_con = WMIConnection::new(COMLibrary::new().unwrap()).ok();
{
let mut storage = Vec::new();