Erlang/OTP Crashdump Viewer(cdv)使用指南:从崩溃转储到问题定位的可视化分析
【免费下载链接】otpErlang/OTP项目地址: https://gitcode.com/gh_mirrors/ot/otp
导读
本文介绍 Erlang/OTP Observer 应用中的Crashdump Viewer(命令行入口为cdv)——一个基于 WxWidgets 的图形化工具,用于浏览和分析 Erlang 运行时系统生成的崩溃转储(crashdump)文件。当 BEAM 虚拟机因内存耗尽、进程异常或其他致命错误而崩溃时,它会留下一个文本格式的erl_crash.dump,其中记录了进程、端口、ETS 表、定时器、调度器、原子表等海量原始信息;手动解析这些原始文件非常繁琐,Crashdump Viewer 则将它们组织成带索引的图形界面,帮助开发者快速定位崩溃原因。读完本文,你将掌握cdv的三种启动方式、主窗口各信息标签页的含义与用法、通用字段语义,以及各标签页背后的解析实现与测试验证方式。
什么是 Crashdump Viewer
Crashdump Viewer 是 Observer 应用(lib/observer)的一部分,核心实现位于 crashdump_viewer.erl,其模块文档明确描述为 "A WxWidgets based tool for browsing Erlang crashdumps",并直接链接到本文对应的官方用户指南。从源码结构看,Crashdump Viewer 由两类模块协作完成:
- 服务端后端:crashdump_viewer.erl 是一个
gen_server,负责读取并索引 crashdump 文件; - GUI 前端:
cdv_wx(主窗口,见 cdv_wx.erl)、cdv_table_wx、cdv_virtual_list_wx等负责渲染,以及一组按数据类别划分的回调模块cdv_gen_cb、cdv_proc_cb、cdv_port_cb、cdv_ets_cb、cdv_timer_cb、cdv_sched_cb、cdv_fun_cb、cdv_atom_cb、cdv_dist_cb、cdv_mod_cb、cdv_mem_cb、cdv_int_tab_cb、cdv_persistent_cb等,它们分别负责渲染各标签页内容。
这些模块全部登记在 observer.app.src 的modules列表和 src/Makefile 中,编译产物随 Observer 应用一起发布。
快速上手:三种启动方式
Crashdump Viewer 可以从操作系统命令行、Erlang 节点或 Windows 批处理三种方式启动。
1. 使用cdv脚本(推荐)
最简单的方式是调用 Observer 应用priv目录下的cdvshell 脚本,并把 crashdump 的完整路径作为参数传入:
cdv /path/to/erl_crash.dump该脚本位于 Observer 应用的priv目录(安装后位于$ERL_TOP/lib/observer-*/priv)。如果省略文件名参数,则会弹出文件选择对话框,由用户在文件系统中挑选要加载的 dump 文件。
在 Windows 平台上,使用同目录下的批处理脚本cdv.bat。
cdv脚本的用法同样记录在 cdv_cmd.md 中,其命令行语法为cdv [file]:参数file可选,省略时弹出文件对话框。
从源码看,脚本启动最终调用的是 crashdump_viewer.erl 中的script_start/0、script_start/1和do_script_start/1。script_start([FileAtom])会先用filelib:is_regular/1校验文件是否存在,不存在时输出错误信息cdv error: the given file does not exist并打印用法:
usage: cdv [file] The 'file' must be an existing erlang crash dump. If omitted a file dialog will be opened.加载成功后,do_script_start/1会将当前进程与cdv_wx进程链接(link/1),并等待其退出;若 GUI 进程异常退出,则输出cdv crash: <Reason>。这也是cdv脚本能保持在前台、随 GUI 关闭而退出的原理。
2. 从 Erlang 节点启动
在已运行的 Erlang 节点中,可以调用:
crashdump_viewer:start(). % 打开文件选择对话框 crashdump_viewer:start(File). % 直接加载指定 crashdump,File 为 string()start/0:启动 GUI 并弹出文件对话框,对应源码 crashdump_viewer.erl;start/1:启动 GUI 并加载指定文件,标注since => "OTP 17.0"(见 crashdump_viewer.erl)。
两者内部都委托给cdv_wx:start/1。需要关闭时调用crashdump_viewer:stop/0,它会监控后端服务进程并等待其退出。
3. Windows 批处理
Windows 用户使用与cdv同目录的cdv.bat,用法与cdv完全一致:cdv.bat [file]。该脚本在构建时由 src/Makefile 中的WIN32_EXECUTABLES目标生成并安装到priv/bin。
GUI 主窗口结构
当 Crashdump Viewer 成功加载一个 crashdump 后,主窗口随之打开,从上到下包含四部分:
- 标题栏:显示当前加载的 crashdump 文件名;
- 菜单栏:包含File和Help两个菜单;
- File菜单:加载新的 crashdump,或退出工具;
- Help菜单:打开本用户指南,以及 ERTS 应用中的 "How to interpret the Erlang crash dumps" 章节(该章节详细讲解原始 crashdump 的每个字段含义,也可在 OTP 在线文档中查看);
- 信息标签页区域:窗口中央区域,每个标签页显示某一类信息;点击标签标题即可切换;
- 状态栏:位于窗口底部,当当前加载的 dump 被截断(truncated)时显示警告。
标签页的完整集合定义在 cdv_wx.erl 的宏中:General、Processes、Ports、ETS Tables、Timers、Schedulers、Funs、Atoms、Nodes、Modules、Memory、Persistent Terms、Internal Tables。
列表类标签页与详情窗口
对于展示条目列表的标签页(例如Processes和Ports),可以通过以下方式打开详情窗口:
- 双击列表中的某一行;
- 右键该行,从弹出菜单中选择对应项。
详情窗口可以为进程(processes)、端口(ports)、节点(nodes)和模块(modules)打开。详情窗口中显示的信息可能包含指向其他进程或端口的链接,点击链接即可打开对应条目的详情窗口:
- 如果目标进程/端口位于远程节点,本地没有其信息,此时点击链接会弹出一个对话框,询问是否打开该远程节点的详情窗口;
- 部分标签页(如Memory、Internal Tables)左侧带有子项菜单,点击左侧行,右侧信息区即显示对应内容。
标签页内容为空的原因
如果某个标签页找不到对应信息,页面会显示为空。原因有以下几种:
- dump 来自较早的 OTP 版本,该版本中并未写入这一项;
- 崩溃发生时系统中本来就不存在该项(例如某个进程当时尚未创建);
- dump 被截断——此时主窗口状态栏会显示警告。
即使部分信息存在,如果 dump 来自旧版本 OTP,某些字段也可能是空的。需要注意:任何字段取值为-1都表示 "unknown"(未知),多数情况下意味着 dump 在靠近该字段的位置被截断了。
各标签页详解
以下字段是原始 crashdump 中不存在、或与原始字段有差异的,由 Crashdump Viewer 加工生成;其余字段请参考 ERTS 用户指南 "How to interpret the Erlang crash dumps" 章节(可从Help菜单直接打开)。
General 标签页(概览)
General标签页给出整个 dump 的简要概览,以下字段为该页特有:
| 字段 | 含义 |
|---|---|
Crashdump created on | 崩溃发生的时间 |
Memory allocated | 当前已分配的总字节数,等价于erlang:memory(total)的返回值 |
Memory maximum | 节点生命周期内分配过的最大字节数;仅当 Erlang 运行时以 instrumented(插桩)方式编译运行时才会显示 |
Atoms | 若 dump 中有原子表大小信息,则为原子表内原子总数;否则为 dump 中可见的原子数量 |
Processes | dump 中可见的进程数 |
ETS tables | dump 中可见的 ETS 表数量 |
Funs | dump 中可见的 fun 数量 |
在源码层面,这些字段由 cdv_gen_cb.erl 的info_fields/0定义,字段名包括slogan、node_name、created、system_vsn、compile_time、taints、mem_tot(Memory allocated)、mem_max(Memory maximum)、num_atoms、num_procs、num_ets、num_timers、num_fun、thread(Calling Thread)。其中mem_tot和mem_max通过{bytes, ...}包装,显示时自动格式化为字节单位。
Processes 标签页(进程)
Processes标签页列出 crashdump 中所有进程及其简要信息。默认按 pid 排序,点击列标题可按其他字段排序。
- _Memory 列:显示自 Erlang/OTP R16B01 起写入 crashdump 的 'Memory' 字段,即该进程使用的内存总量;对于更早版本的 dump,此列回退显示 'Stack+heap' 字段。单位始终为字节。
- 查看进程详细信息:双击该行,或右键选择Properties for <pid>。
Ports 标签页(端口)
Ports标签页与Processes标签页类似,区别在于列出的是 crashdump 中的所有端口。
- 查看端口详情:双击该行,或右键选择Properties for <port>;
- 右键菜单中还可以选择Properties for <pid>,其中
<pid>是与该端口相连的进程,用于直接跳转到端口控制进程的详情页。
ETS Tables 标签页(ETS 表)
ETS Tables标签页显示 dump 中所有 ETS 表信息:
- _Id:与原始 crashdump 中的 'Table' 字段相同;
- _Memory:原始 crashdump 中 'Words' 字段换算为字节后的值;
- 对于树形表(tree tables),'Objects' 字段没有值。
操作方式:双击行或右键选择Properties for 'Identifier'打开表详情;右键选择Properties for <pid>打开该 ETS 表属主进程的详情页。
Timers 标签页(定时器)
Timers标签页显示 dump 中所有定时器信息。右键选择Properties for <pid>可打开定时器属主进程的详情页;注意:双击Timers标签页中的行没有任何效果(源码中该页未绑定双击事件)。
Schedulers 标签页(调度器)
Schedulers标签页显示 dump 中所有调度器(包括普通调度器与 dirty schedulers)的信息。双击行或右键选择Properties for 'Identifier'打开对应调度器的详情页。
Funs 标签页(fun 对象)
Funs标签页显示 dump 中所有 fun 对象的信息。右键选择Properties for <mod>打开该 fun 所属模块的详情页;与Timers页一样,双击Funs页中的行没有效果。
Atoms 标签页(原子)
Atoms标签页列出 dump 中所有原子。默认按创建顺序从先到后排序——这与原始 crashdump 正好相反(原始文件中的原子从后到前排列)。因此,如果 dump 在原子列表中间被截断,Atoms标签页中只能看到最后创建的原子。
Nodes 标签页(分布式节点)
Nodes标签页列出 crashdump 中引用到的所有外部 Erlang 节点。页面为空时表示以下情况之一:
- 崩溃节点未启用分布式(not distributed);
- 节点启用了分布式,但没有对其他节点的引用;
- dump 被截断。
如果节点是分布式的,所有被引用的节点都会显示。_Connection type列表示节点的连接状态:
| 状态 | 含义 |
|---|---|
visible | 存活节点,且与崩溃节点保持活跃连接 |
hidden | 与 visible 相同,但该节点以-hidden标志启动 |
not connected | 已不再与崩溃节点连接,但仍存在引用(如进程或端口标识符) |
- 查看节点详情:双击行,或右键选择Properties for node <node>;右键菜单中还可选择Properties for <port>打开该节点控制端口的详情窗口;
- 节点详情窗口会显示崩溃节点与连接节点之间进程的全部 links 和 monitors;_Extra Info字段可能包含调试信息(emulator 以 debug 方式编译时写入的特殊信息)或错误信息。
Modules 标签页(模块)
Modules标签页列出崩溃节点上加载的所有模块及当前代码大小;如果存在旧代码(old code),还会显示旧代码的大小。双击行或右键选择Properties for <mod>查看模块详情。
Memory 标签页(内存与分配器)
Memory标签页展示内存与分配器信息,通过左侧菜单选择子项:
- _Memory:总体内存信息;
- _Allocator Summary:其下所有分配器的汇总值;
- _<Allocator>:每个分配器一项,逐项展示;
- _Allocated Areas:已分配区域信息。
Internal Tables 标签页(内部表)
Internal Tables标签页通过左侧菜单提供三类子项:Hash Tables(哈希表)、Index Tables(索引表)、Internal ETS Tables(内部 ETS 表)。
源码级实现原理
后端解析:标签索引机制
Crashdump Viewer 的后端 crashdump_viewer.erl 是一个gen_server,负责读取并索引 crashdump。其注释明确描述了内部使用的几张 ETS 表:
cdv_dump_index_table:保存从 crashdump 中读到的除binary之外的所有标签(tag)。在 crashdump 文件中,每个标签都以行首的=开头;表中每个条目记录该标签对应信息在文件中的起始位置;cdv_binary_index_table:保存所有binary标签。每个二进制对象的十六进制地址先转换为整数值,再以Address -> Start Position的映射存入,查询时从不使用十六进制形式;cdv_reg_proc_table:保存 pid 与注册名之间的映射,用于定时器和监视器(monitor)信息的展示;cdv_heap_file_chars:为每个proc_heap和literals标签记录需要从文件中读取的字符数,用于解析这些数据时以百分比形式显示进度。
解析时以 1000 字节为一块(-define(chunk_size, 1000))分块读取文件,并定义max_dump_version为[0,5],即当前版本最多支持 0.5 版本的 dump 格式。代码中通过宏定义了全部标签,包括proc、port、ets、timer、scheduler、fu、atom、node、memory、allocator、allocated_areas、hash_table、index_table、internal_ets、persistent_terms等,标签与信息标签页一一对应。
版本兼容性验证
测试套件 crashdump_viewer_SUITE.erl 覆盖了关键行为:
- 无效文件处理:向后端加载内容为
=unexpected_tag:xyz的文件或空文件时,会返回错误"<file> is not an Erlang crash dump\n"(见该套件中相关用例); - 版本上限检查:加载版本高于当前支持上限的 dump 时返回错误
"This Crashdump Viewer is too old"(见 crashdump_viewer_SUITE.erl),测试中还通过crashdump_viewer:get_dump_versions/0校验当前 dump 版本与 cdv 的最大支持版本一致; - 加载与浏览:
load_file用例通过crashdump_viewer:start_link()启动后端,逐一加载预置的r*_dump.*文件并浏览所有页面(见 crashdump_viewer_SUITE.erl)。
这些测试印证了 "dump 太旧/被截断导致信息缺失" 以及 "版本过新的 dump 无法加载" 等文档描述的实际行为。
前端渲染:按类别的回调模块
主窗口 cdv_wx.erl 以wx_object行为实现,内部用一个wxNotebook承载全部标签页,并为每个标签页维护独立的 panel(gen_panel、pro_panel、port_panel、ets_panel、timer_panel、sched_panel、fun_panel、atom_panel、dist_panel、mod_panel、mem_panel、persistent_panel、int_panel)。各回调模块通过调用crashdump_viewer的导出函数获取数据,例如:
cdv_gen_cb:get_info/0调用crashdump_viewer:general_info()获取概览字段(见 cdv_gen_cb.erl);cdv_proc_cb对应processes/0、proc_details/1;cdv_ets_cb对应ets_tables/1、internal_ets_tables/0;cdv_dist_cb对应dist_info/0、node_info/1;- 后端还提供
memory/0、allocator_info/0、allocated_areas/0、hash_tables/0、index_tables/0、schedulers/0、funs/0、atoms/0、timers/1、loaded_modules/0等接口,与标签页一一对应(完整接口见 crashdump_viewer.erl)。
值得注意的是,后端还有一个expand_binary/1接口,配合cdv_bin_cb用于在详情窗口中展开二进制对象内容——这正是点击详情窗口中的二进制引用时能够查看其内部数据的原因。
使用建议与局限
- 先看状态栏:打开 dump 后,如果状态栏出现截断警告,那么
-1字段、空标签页、缺失进程等异常都属于预期现象,不要误判为工具缺陷; - 结合原始 dump 交叉验证:Crashdump Viewer 是对原始
erl_crash.dump的可视化包装,字段含义的权威解释仍在 ERTS 的 "How to interpret the Erlang crash dumps" 文档中,遇到疑点可从Help菜单直接查阅; - 版本限制:当前实现的
max_dump_version为[0,5],加载更高版本的 dump 会提示 "This Crashdump Viewer is too old",需要升级 OTP;加载旧版本 OTP 产生的 dump 时,部分字段(如Processes页的 Memory 列、Memory页的 Memory maximum)会缺失或回退显示; - 远程节点信息不可用:分布式场景下,如果进程或端口位于远程节点,详情窗口只提供节点级信息,无法查看其本地细节;
- 依赖 WxWidgets:Crashdump Viewer 是 GUI 工具,运行环境需要 Erlang/OTP 以 wx 应用支持(即带 Wx 绑定的发行版)编译,在无图形界面的服务器上建议改用 crash_dump 原始文件解析 或编写脚本处理。
相关文档与源码索引
- 用户指南原文:crashdump_ug.md
- 命令行参考:cdv_cmd.md
- 主模块实现:crashdump_viewer.erl
- GUI 主窗口:cdv_wx.erl
- General 页回调:cdv_gen_cb.erl
- 测试套件:crashdump_viewer_SUITE.erl
- 原始 dump 格式详解:ERTS 文档 crash_dump.md(即 "How to interpret the Erlang crash dumps")
【免费下载链接】otpErlang/OTP项目地址: https://gitcode.com/gh_mirrors/ot/otp
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考