Render folded stack profiles into interactive SVG flame graphs.
Dependencies

上图由 moon run cmd/main -- testdata/demo.folded --out flame.svg 生成,输入是真实采样数据。 想体验点击缩放、Ctrl-F 搜索等交互,请下载 examples/flame.svg 用浏览器打开 (内嵌脚本在 <img> 引用下不会执行,原因见已知限制)。
折叠栈文本 ──▶ MoonFlame ──▶ 单个 SVG 文件____moonbit__main;moonflame::demo::bench__sorting;bubble__sort;array::Array::at 73382500| 功能 | 说明 |
|---|---|
| 折叠栈解析 | 容忍超深栈(实测 7254 层)、超长单行(181K 字符)、空帧名等各种真实数据里的脏形状 |
| 调用树聚合 | 同父同名合并累加;计算自身耗时与累计耗时 |
| 限深合并 | 超过 --max-depth 的部分合并为 (deeper) 合成帧,而不是丢弃——否则深层耗时会被错算到最后一个可见帧上 |
| 布局 | 火焰图(根在底部)/ 冰柱图两种方向;按值比例切分,父宽严格等于子宽之和 |
| 可交互 SVG | 经典火焰图配色 + 内嵌脚本:悬停信息栏、点击缩放、Reset Zoom、Ctrl-F 正则搜索、Ctrl-I 大小写开关、命中占比 |
| 差异火焰图 | 对比两份剖面,红 = 变多、蓝 = 变少、白 = 无变化 |
| 文本报告 | hotspots / callers / callees / delta 四个子命令,回答「谁最慢」「谁调用它」「它调用谁」「这次改动让什么变慢了」 |
| 确定性输出 | 同一输入必得逐字节相同的 SVG,可做快照测试与版本间对比 |
| 零依赖核心库 | 根包纯计算、无任何外部依赖;文件 IO 只出现在命令行入口 |
moon run cmd/main -- testdata/demo.folded --out flame.svgmoon run cmd/main -- hotspots testdata/demo.folded --top 10| 文件 | 用途 |
|---|---|
| testdata/demo.folded | ⭐ 主样例:32 栈 / 深度 3–13 / 1893 ms |
| testdata/stress-recursive.folded | 极限用例:79 栈 / 最深 7254 层 / 5.5 MB,用来验证限深与合并 |
moon build --target wasm-gc必须用 wasm-gc:上游采样器基于 wasmtime,加载不了原生可执行文件。
moon-pprof profile --wasm-gc _build/wasm-gc/debug/build/cmd/main/main.wasm --iterations 3 --out app.pb.gz
moon-pprof pprof2folded app.pb.gz app.foldedmoon run cmd/main -- app.folded --out flame.svgpowershell demo/reproduce.ps1 # 编译 → 采样 → 转折叠栈 → 打印热点采样有随机性:重跑会得到不同的栈数与总耗时(实测行数 32–36、总耗时 1887–1917 ms),但热点排序稳定。 折叠栈里一行 = 一个不同的调用栈,重复出现的栈会被合并、权重累加,所以文件 32 行而原始样本有 490 个。
moon run cmd/main -- <input> [options] 出图(默认)
moon run cmd/main -- hotspots <input> [--top N] 热点榜
moon run cmd/main -- callers <input> <name> 谁调用了它
moon run cmd/main -- callees <input> <name> 它调用了谁
moon run cmd/main -- diff <before> <after> [-o out] 差异火焰图
moon run cmd/main -- delta <before> <after> [--top N] 差异文本排行
moon run cmd/main -- filter <pattern> <input> [-o out] 栈过滤| 参数 | 默认 | 说明 |
|---|---|---|
| <input> | 必填 | 折叠栈文件 |
| -o, --out <path> | 标准输出 | 输出路径;不传就打到标准输出 |
| --max-depth <n> | 32 | 每个栈最多保留的帧数,0 表示不限制 |
| --width <px> | 1400 | 画布宽度 |
| --unit <u> | ns | 权重单位:ns / us / ms / s / count |
| --inverted | 关 | 输出冰柱方向(根在顶部);默认是火焰图方向(根在底部) |
为什么需要 --unit? 折叠栈的第 2 列是不透明的权重:moon-pprof 给纳秒, perf / py-spy 给采样计数。数值本身区分不出来(42 既可能是 42 纳秒也可能是 42 次采样), 所以由你声明。时间会按数量级自动缩放(871808200 → 871.81 ms),计数则加千位分隔、不带后缀。
moon run cmd/main -- hotspots testdata/demo.folded --top 5
moon run cmd/main -- callers testdata/demo.folded walk_tree
moon run cmd/main -- callees testdata/demo.folded build_strings
moon run cmd/main -- delta testdata/demo-baseline.folded testdata/demo.foldedmoon run cmd/main -- filter build_strings testdata/demo.folded -o sub.folded
moon run cmd/main -- sub.folded --out sub.svg| 数据里的样子 | 显示为 |
|---|---|
| moonflame::demo::build__strings | build_strings |
| moonflame4demo13run__workload | moonflame::demo::run_workload |
| array5Array3set | array::Array::set |
| ____moonbit__main | 不变(下划线开头不做折叠) |
| 操作 | 效果 |
|---|---|
| 悬停 | 底部信息栏显示该帧的完整名字、耗时与占比 |
| 单击某帧 | 以它为根缩放展开;祖先帧变半透明,无关帧隐藏 |
| Reset Zoom | 复原到全图 |
| Ctrl-F / Search | 正则搜索,命中帧高亮为品红,右下角显示命中占比 |
| Ctrl-I / ic | 切换搜索是否区分大小写 |
moon run cmd/main -- diff testdata/demo-baseline.folded testdata/demo.folded -o flame-diff.svg
| 视觉通道 | 含义 |
|---|---|
| 宽度 | 按当前(第二个)剖面 —— 看清现在的时间花在哪 |
| 🔴 红 | 该帧变多了(劣化),越红变化越大 |
| 🔵 蓝 | 该帧变少了(改善) |
| ⚪ 白 | 变化可以忽略(注意不是灰色——灰字在浅色底上读不出来) |
| 悬停 | 额外显示带符号的变化量 |
⚠️ testdata/demo-baseline.folded 是构造的基线,仅用于演示与测试差异模式,不是真实采样。 构造规则只有两条,可逐行核对:含 bubble__sort 的行权重 ×3、含 build__strings 的行权重 ×0.8 (向下取整),其余行不变。真实基线应当来自优化前的那次采样。
.
├── LICENSE MIT
├── moon.mod / moon.pkg 模块与包配置
├── pkg.generated.mbti 公开接口(由 moon info 生成,须与源码同步)
│
├── folded.mbt 折叠栈解析
├── calltree.mbt 调用树聚合 / 限深合并 / 差异标注
├── unit.mbt 权重单位与格式化
├── names.mbt 符号名归一化
├── hotspot.mbt Top-N 热点报告
├── report.mbt 文本报告(调用关系 / 差异排行 / 栈过滤)
├── layout.mbt 矩形布局(火焰图 / 冰柱)
├── svg.mbt SVG 渲染(经典配色 + 内嵌交互脚本)
├── *_test.mbt 核心逻辑的测试(166 个用例)
│
├── cli/ 命令行参数解析(独立包,可脱离文件系统测试)
├── cmd/main/ 入口:读文件 → 调用核心库 → 写文件
│
├── .github/workflows/ci.yml 持续集成(三平台 × 四后端 + 端到端出图)
├── demo/ 演示负载(独立模块)
│ ├── demo.mbt 四点负载:排序 / 字符串 / 矩阵 / 递归
│ └── reproduce.ps1 一键复现:编译 → 采样 → 转折叠栈
├── examples/ 示例图(SVG 可交互,PNG 供 README 展示)
└── testdata/ 冻结的样例数据(主样例 + 极限用例 + 上游样例)moon check # 类型检查
moon test # 全部测试(122 个)
moon info && moon fmt # 提交前更新接口并格式化
moon run cmd/main -- --helppub struct DeltaEntry {
name : String
before : Double
after : Double
change : Double
ratio : Double
} derive(Eq)pub struct Rect {
x : Double
y : Double
width : Double
height : Double
name : String
value : Double
depth : Int
delta : Double?
} derive(Eq)fn fill_for_delta(delta : Double, max_delta : Double) -> String变化 > 0: rgb(255, s, s) s = 210 * (max - delta) / max
变化 < 0: rgb(s, s, 255) s = 210 * (max + delta) / max
变化 = 0: rgb(255, 255, 255)fn fill_of(name : String) -> Stringv = 名称哈希([0,1))
r = 205 + int(50 * v)
g = 0 + int(230 * v)
b = 0 + int(55 * v)fn filter_folded(text : String, pattern : String) -> Stringfn normalize_name(name : String) -> Stringfn render_delta(entries : Array[DeltaEntry], before_total : Double, after_total : Double, unit : WeightUnit) -> Stringfn render_relations(relations : Array[Relation], wanted : String, kind : String, unit : WeightUnit) -> Stringfn render_svg(layout : Layout, min_width? : Double, font_size? : Double, show_labels? : Bool, unit? : WeightUnit, normalize_names? : Bool) -> StringInstall
Download zipRender folded stack profiles into interactive SVG flame graphs.
Dependencies