Windows应用程序Crash日志:Dr.Watson与WER归纳总结
从Dr.Watson到WER:Windows崩溃日志的演变
Windows平台上的crash日志机制,经历过一次大换代。早期XP时代靠Dr.Watson,Vista之后换成了Windows Error Reporting(WER)。
说白了,这两个东西干的活差不多——程序崩了,操作系统帮忙记录现场。但细节差别很大。我刚开始做Windows crash分析时,老同事扔给我一个.dmp文件,说“用Dr.Watson看看”。结果我找了半天,发现系统里根本没有Dr.Watson……后来才知道版本不同。
那会儿我意识到:搞crash分析,得先搞清楚你的目标环境用的是哪个机制。不然连日志都找不到。
Dr.Watson:老牌但还在用
触发方式:当应用程序发生未处理异常,Windows会调用drwtsn32.exe进程来生成日志。默认路径在:
C:\Documents and Settings\All Users\Application Data\Microsoft\Dr Watson\
生成的日志包含两部分:drwtsn32.log(文本摘要)和user.dmp(内存转储文件)。
日志里记录了什么?我列一下关键字段:
| 内容 | 说明 |
|---|---|
| 异常类型 | Access Violation, Divide by Zero 等 |
| 发生地址 | 模块名+偏移量,比如 kernel32.dll!0x77e5b3a4 |
| 调用栈 | 从当前指令一级级往上回溯 |
| 寄存器值 | EAX, EBX, ECX 等关键寄存器 |
| 加载模块列表 | 进程所有DLL及基址 |
| 系统信息 | 操作系统版本、补丁号等 |
限制:Dr.Watson只能针对一个默认调试器,多个程序崩溃时会互相覆盖。而且它的内存转储文件默认是小型转储,信息量有限。我当年帮客户分析一个老工业软件crash,他们的Windows 2000机器上只装了Dr.Watson,崩溃日志里连完整的调用栈都没有,最后我只能靠日志里的地址偏移量去反查符号。
WER:现代Windows的崩溃处理中心
从Windows Vista开始,微软用WER全面替代了Dr.Watson。WER的配置和日志管理统一在注册表和werconv.exe里。日志存储位置:
C:\ProgramData\Microsoft\Windows\WER\ReportQueue\
每个crash生成一个子文件夹,包含:Report.wer(结构化XML)、内存转储(*.dmp)、以及WER上传前后的元数据。
Report.wer长什么样子?我打开过一个实际的:
Version=1
EventType=APPCRASH
EventTime=131234567890123456
Response=Not available
Stage=1
...
Sig[0].Name=Application Name
Sig[0].Value=MyApp.exe
Sig[1].Name=Application Version
Sig[1].Value=1.2.3.4
Sig[2].Name=Fault Module Name
Sig[2].Value=mycore.dll
Sig[3].Name=Fault Offset
Sig[3].Value=0x0001a4b0
...
DynamicSig[1].Name=OS Version
DynamicSig[1].Value=10.0.19041.2.0.0.256.48
...
这比Dr.Watson的日志清晰多了。每个字段都有唯一签名,WER还会把这些签名上传到微软服务器做聚合分析。大型软件公司利用WER后台能看到一个Bug影响了多少用户。
实战:我如何快速定位Dr.Watson和WER日志
- 先确认操作系统版本:用
ver命令或winver。XP及以下看Dr.Watson,Vista及以上看WER。 - 检查日志是否存在:如果用户远程发来“工具崩溃了但没任何提示”,我会让他在崩溃后马上打开事件查看器(Event Viewer),看Windows日志 → 应用程序里有没有WER错误事件。
- 提取转储文件:对于WER,直接去
ReportQueue下按时间排序找到最近的*.dmp。Dr.Watson的user.dmp默认只有一个,容易被覆盖——这是个大坑。 - 用WinDbg加载:设置好符号路径,执行
!analyze -v查看自动化分析结果。
关键对比总结
| 特性 | Dr.Watson | WER |
|---|---|---|
| 首次引入 | Windows 95 | Windows Vista |
| 日志文件格式 | 纯文本+二进制dmp | XML结构化+转储 |
| 支持多实例 | 否(单实例) | 是(队列机制) |
| 自动上传 | 否 | 是(可配置) |
| 调用栈完整性 | 较差(依赖框架帧) | 较好(含Shadow Stack) |
| 适用于企业调试 | 个体分析 | 大规模聚合分析 |
避坑指南:为什么我看不到完整的crash日志?
我曾经帮一个游戏公司追查线上崩溃,结果发现用户反馈的日志文件只有几KB。打开一看,全是0x00000000的地址。查了半天,原来是WER在生成转储时把Minidump类型设为MiniDumpNormal,只记录了线程信息,没有堆内存。修改HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\DumpType为0(满转储)即可获取完整信息。
注意事项
- Dr.Watson在Windows 10中已被彻底移除,但部分旧版Windows Server仍通过兼容模式保留。
- WER默认不生成完整转储,需要手动开启:
HKLM\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps。 - 企业环境可通过组策略禁止WER上传敏感数据,但仍然保留本地日志。
- 跨平台分析时,注意WER的
Report.wer文件包含的应用程序签名和异常签名,这两组签名组合唯一标识一个bug类型。
核心知识结构
下面的流程图展示了一次应用程序崩溃后,Dr.Watson与WER两种机制分别如何产生日志,以及它们之间的关键差异。
我的个人习惯:收到一个crash日志后,我第一步不是开WinDbg,而是先看Report.wer里的Fault Offset和Fault Module。如果偏移量落在已知的DLL里,我会直接去查那个DLL的版本和符号。这招在快速排查“升级后立刻崩溃”的问题时特别好使。比如有一次用户把驱动DLL从1.0升级到2.0,crash偏移量在0x1a4b0,我查了旧版符号发现那里是一个已废弃的函数,新版直接删掉了,导致调用空指针。
总结:选哪个?
如果你还在维护XP/2003系统,只能用Dr.Watson。但哪怕能升级,我强烈建议迁移到WER。理由很实在:
- WER的日志结构更规整,自动化解析脚本好写
- 支持多实例缓存,不会互相覆盖
- 企业级聚合分析能力
- 转储类型可配置,满足不同分析深度
最后说一个我很早以前踩过的坑:在Windows 10上以管理员模式运行的程序,WER有时不会写入ReportQueue,而是写到ReportArchive里。如果你按默认路径找不到日志,记得检查C:\ProgramData\Microsoft\Windows\WER\ReportArchive\。嗯,这玩意儿文档里可没写那么细。