首页 / 资讯中心 / 文章详情

用Madeira破解erl_crash.dump:Erlang崩溃现场的结构化分析利器

用Madeira破解erl_crash.dump:Erlang崩溃现场的结构化分析利器 ★ FEATURED ARTICLE
如果你跟 Erlang/OTP 线上节点打过交道一定见过erl_crash.dump。每次节点异常退出工作目录里就会落下一个几百兆甚至几个 G 的文本文件里面是进程、内存、端口、ETS 表、消息队列的完整快照。然而直接打开它你会看到满屏的process、十六进制地址和密密麻麻的 dictionary 数据基本没法下眼。Madeira 就是专门干这个的它把这个 dump 解析成结构化的分析报告用浏览器查看哪个进程在持有内存、哪条消息队列快爆了、哪个 ETS 表异常膨胀。这篇文章就用实际排查过程来拆解 Madeira 的用法和原理顺带把 crash dump 的阅读方法也讲清楚。先补充说明一句Madeira 这个名字在大众语境里容易联想到马德拉群岛或者马德拉酒但在 Erlang 生态里它特指一个开源的 crash dump 解析与可视化工具。下面默认说的是后者。1. 项目概述与核心价值1.1 Madeira 到底是什么Madeira 是一个把 Erlang VM 生成的 crash dump 文件转换成可读分析报告的解析器。它由 Haskell 编写目标是让运维人员和后端开发者摆脱对着几十万行 dump 文本用 grep 拼凑信息的原始状态。它的典型输出是一份 HTML 报告也可以通过本地 Web 服务的方式启动在浏览器里做交互式查询。我最初接触这个项目时正好赶上一次线上节点反复 OOM。当时没有工具大家轮流对着erl_crash.dump里那几十万个process段落手算内存排名效率惨不忍睹。后来把 Madeira 接入排查流程定位问题从一两天缩短到一两个小时。从那时候起我就在自己的工具链里长期保留它。需要明确的是Madeira 不是监控系统。它处理的是事后快照解决的是节点已经崩溃怎么从尸体上快速找原因这个场景。它和 Observer、New Relic 这类实时观察工具是互补关系。1.2 崩溃 dump 为什么这么难读要理解 Madeira 的价值得先理解erl_crash.dump本身。这个文件不是 JSON也不是结构化二进制而是一种带标签的纯文本格式。文件头部有版本号例如erl_crash_dump:0.4后面跟着内存信息、调度器信息、进程列表、端口列表、ETS 表列表等区域。每个区域用process、port、ets这类标记开头过程内部又有Name、State、Slogan、Current function、Heap、Stack、Message queue length等字段。问题在于这些字段的排列在底层细节上依赖 OTP 版本不同版本之间会有差异。进程的堆内存和二进制引用经常分散在多个字段里手工统计时很容易漏算。更难受的是体量。一个稍微繁忙的节点崩溃时可能有几十万甚至上百万个进程。每个进程在 dump 里占据几十行整个文件轻松超过一个 G。用 grep 去搜某个 PID 容易但要对全部进程按内存占用排序或者在消息队列长度上做 Top N靠命令行文本处理基本是噩梦。Madeira 做的事就是把这些文本解析成真正的数据结构然后给你一套聚合视图排序、过滤、搜索、跳转。它的解析逻辑相当于把崩溃现场重新整理了一遍让每个进程都变成一张可点开的卡片。1.3 适合谁来用如果你是以下角色Madeira 会非常值得放进工具箱负责 Erlang/OTP 服务的后端开发尤其是自研网关、消息中间件、游戏服务器这类 BEAM 上运行的高并发服务。线上环境偶发 OOM、节点被 kill、消息积压需要从崩溃现场找根因的排查人员。SRE 或稳定性工程师需要在一个小时内对 dump 做初步鉴定的人。正在从 RabbitMQ、CouchDB 等 BEAM 系开源项目里挖内部机制的人。它不适合用来做实时告警也不适合替代 APM。它是事故处理工具箱里的一件重要器具而不是监控大盘。2. 环境准备与安装2.1 依赖项与版本疑虑Madeira 是 Haskell 项目所以本质上你只需要一个能跑 Haskell 编译器的环境。不同发布时间段的项目对 GHCGlasgow Haskell Compiler版本要求不同老项目用老 GHC新分支往往需要更新的版本。我最常用的安装方式有两种用 stack 构建依赖版本管理由stack.yaml锁定稳定推荐。用 cabal 构建灵活性高但可能出现依赖版本冲突。如果你用的是 macOS可以先装好 Homebrew然后执行brew install ghc cabal-installUbuntu/Debian 下用系统包管理器sudo apt update sudo apt install ghc cabal-install这里要提醒一件事每个人的 OTP 版本不一样dump 格式版本也不一样Madeira 不同 commit 的兼容性差异很大。安装前最好确认一下你手里的 dump 是哪个格式版本最有把握的做法是提前在测试环境制造一份 dump 跑一遍解析验证。2.2 源码构建步骤以 stack 为例常规流程是拉仓库、切到与你 OTP 版本匹配的分支或 tag然后构建git clone make-address-from-your-fork cd madeira stack build stack installstack install会把可执行文件放到~/.local/bin下。如果你的PATH没有包含这个目录shell 会提示找不到命令记得加一下export PATH$HOME/.local/bin:$PATH如果你更习惯 cabal可以这样cabal update cabal install madeira但 cabal 从 Hackage 上拉下来的往往不是最新源码我建议还是用仓库构建方便切换版本和查看对 OTP 版本的支持。2.3 安装验证构建完成后执行madeira --help能列出子命令说明安装正常。不同版本子命令可能叫parse、analyze也可能叫serve、web先看帮助输出再继续。千万别照着老教程里的命令盲敲Haskell 项目重构起来很奔放。3. 基本使用从 dump 到分析报告3.1 先拿到一份 crash dump分析的前提是手上有一份 dump。Erlang VM 默认在崩溃时会在当前工作目录生成名如erl_crash.dump的文件。生产环境通常要显式控制它的位置和写入时间防止在/根目录或者临时目录乱拉一堆巨型文件。推荐的做法是设置环境变量export ERL_CRASH_DUMP/data/dumps/erl_crash.dump export ERL_CRASH_DUMP_SECONDS30ERL_CRASH_DUMP_SECONDS表示崩溃后允许 VM 花多少秒写 dump。给 30 秒通常足够设成负数可能无限期等待在一些场景下会让崩溃看起来像卡死。磁盘空间紧张时也可以设ERL_CRASH_DUMP_NICE调低写 dump 的 CPU 优先级。调试时前想拿到一份最小复现 dump可以临时把ERL_CRASH_DUMP_SECONDS调到 5然后故意让节点崩溃。拿到文件后别忘了先看一眼磁盘空间和文件大小ls -lh erl_crash.dump head -20 erl_crash.dump头部会显示类似erl_crash_dump:0.4的版本信息后面memory、process这些段落标签也都能看到。3.2 命令行解析与 Web 查看拿到 dump 后接下来就是用 Madeira 解析。以我手头常用版本为例格式大概是这样madeira parse -f erl_crash.dump -o /data/reports/crash.html如果你希望直接在浏览器里看交互式页面可以走 Web 模式madeira serve -d /data/reports --port 8080再打开http://localhost:8080就能看到报告首页。不同版本 UI 长得不太一样但核心板块通常包括崩溃原因概览、调度器状态、进程列表、ETS 表列表、端口列表、消息队列排名。如果你只是想临时看某个区域不想解析全量也可以用文本命令快速捞sed -n /process/,/port/p erl_crash.dump processes.txt这只是应急办法完整分析还是建议交给 Madeira。3.3 报告里的关键指标怎么读报告打开后第一步先看概览区。崩溃原因、VM uptime、总内存、调度器情况都会列在这里。第二步看进程排序。按Heap Old Heap或总内存倒序排列排在前面的是内存大头按Message queue length倒序排列能看到谁在积压消息。还需要看几个容易被忽略的字段Slogan进程退出原因比如out of memory或某个具体异常。Current function进程当前停在哪个函数上崩溃前的位置线索。Group leader父进程是谁可以顺着进程关系找调用链。Stack Heap这两个字段一起看能判断是栈溢出还是堆膨胀。ReductionsCPU 消耗的近似指标注意如果一个进程 reductions 极高但内存不高它是忙等而不是泄漏。看进程详情时dictionary段会列出进程字典里的 key 和 valuestack段是栈上的返回地址线索。某些版本里还能点开 binary 引用定位到二进制数据被哪个进程持有。这些信息组合起来基本能把崩溃现场还原出八成。4. 实战排查案例4.1 案例一内存泄漏定位线上一个 Erlang 网关进程每两天内存涨到 30G 后触发 OOM。拿到崩溃 dump 后我在 Madeira 报告里按内存倒序浏览排第一的进程是负责连接管理的 gen_server持有 22G 左右内存。点进进程详情发现堆里挂着大量binary引用而且这些 binary 都关联到同一个缓存的原始数据。顺着代码往上查发现服务启动时加载了一批规则数据之后每次有客户端连接代码都做了从缓存数据里 slice 一份 binary 塞进进程状态的操作但没做任何过期清理。连接数越多进程状态里的 binary 引用不断累积。排查到这里已经能定位根因。如果靠 grep你需要把几十个 G 的文件里的 binary 引用段全部导出再手工关联没有一小时下不来。Madeira 在报告里把这些数据全串好了。处理方式也很直接把缓存数据改成 ETS 表存储进程状态里只留下表 ID不持有 binary。加了一组回归测试后内存曲线恢复平稳。4.2 案例二消息队列堆积排查另一次故障表现是节点活活卡死但没立刻崩溃等到最后被外部健康检查探活失败才重启。重启后我把最近的 dump 拉进 Madeira按Message queue length排序发现有一批 worker 进程的消息队列长度到了几十万。点开其中一个进程的详情看队列里的消息类型前缀都指向同一个gen_server:call。也就是说这些 worker 都在阻塞等待同一个 gen_server 返回而 gen_server 本身的消息队列是空的状态为Waiting。看起来像是死锁但真实原因是 gen_server 在handle_call里调用了一个会发起外部 HTTP 请求的模块而那个模块的 socket 又依赖另一个 worker 进程的响应形成了跨进程环路。用 Madeira 对比那个 gen_server 的Current function和调用来源进程的信息很快找到环路。如果是纯看日志这类问题很容易被误判为网络超时。修复方案是给 gen_server 调用加超时并且把所有跨进程调用改成异步消息。改完后消息队列堆积不再出现。4.3 案例三多份 dump 对比找回归偶发性崩溃最难查因为单凭一份 dump 很难判断这是不是一直存在的隐患。我的习惯是定期收集线上节点的 dump按日期归档。当某一天事故发生后我会拿事故 dump 和上一周的正常 dump 一起对比。Madeira 虽然核心功能不是专门的 diff但多份报告并行打开时可以快速对比几个数字总进程数、总内存、前 50 大内存进程的分布、消息队列长度前 10 名的数值量级。之前遇到过一次发布后崩溃率升高的问题。正常 dump 里前 50 大内存进程最高不过 500M事故 dump 里第 10 名就已经到了 3G。顺着差异最大的进程找发现新版本引入了一个每秒钟往进程状态里塞列表项的逻辑列表不断膨胀最终拖垮 VM。如果只看事故当天的 dump可能会以为是数据量增长导致的自然演进但和历史 dump 一对比回归点立刻暴露。5. 常见问题与排查技巧实录5.1 常见问题速查表现象可能原因处理方式解析 dump 时内存溢出dump 文件几个 G工具进程被系统 OOM加大机器内存或先做区域截取再局部解析报告里进程列表为空dump 格式版本过老工具不支持检查 OTP 版本升级 Madeira 或换分支崩溃原因字段为空进程不是被 Erlang 异常干掉的而是被系统 SIGKILL查dmesg/ journald 里的 OOM Kill 记录找不到某个特定进程进程在崩溃前已经结束dump 只记录存活进程无法恢复只能对照日志推理ETS 表名称显示乱码atom 包含非 UTF8 字节忽略名称按 owner 进程和内存占用定位解析速度极慢文件太大且磁盘 IO 慢换 SSD或限制目录里的历史 dump 数量dump 文件没生成工作目录不可写或ERL_CRASH_DUMP_SECONDS0检查目录权限设置独立 dump 目录5.2 实操心得与避坑第一条心得是别等到真的崩溃了才研究 dump。找一台测试机故意压垮一个 Erlang 节点拿真实的 dump 跑一遍 Madeira。这样你会熟悉报告里各个板块长什么样省得事故发生时临时学。第二条是分析前先把 dump 复制到独立目录再启动 Madeira。它读大文件时会吃内存在事故现场机器上直接解析可能加剧系统压力。把 dump 拷贝到一台有空闲内存的机器上是更稳妥的做法。第三条是报告不能替代对业务代码的理解。Madeira 能帮你快速定位哪个进程、哪个函数、哪个队列但它不会告诉你这个函数为什么会出现这个状态。你需要结合日志、发布记录和代码改动的上下文才能给出真正结论。还有一个小技巧如果你在生产环境设置了ERL_CRASH_DUMP_SECONDS记得同时加一个日志记录把 dump 文件路径打出来。事故时大家本来就慌能直接告诉我文件在哪可以少浪费十分钟。我自己的习惯是每两周收集一次线上节点的 dump 做全量对比像体检一样。排查现场最怕手忙脚乱把 Madeira 接入崩溃响应流程之后定位内存类问题基本能控制在半小时内。如果你的节点还没有这个工具建议先在测试环境拿一份真实崩溃 dump 跑一遍试试你会回来谢我的。
阅读完成 · 觉得有帮助?
咨询建站