Render folded stack profiles into interactive SVG flame graphs.
Dependencies

# macOS / Linux 安装 MoonBit
curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash# Windows 安装 MoonBit
irm https://cli.moonbitlang.com/install/powershell.ps1 | iexmoon version --all
moon update
moon run cmd/main -- testdata/demo.folded --out flame.svg
moon run cmd/main -- hotspots testdata/demo.folded --top 10moon run --target js cmd/main -- testdata/demo.folded --out flame.svgmain;compute;fft 900
main;compute 100| 冻结样例 | 含义 |
|---|---|
| testdata/demo.folded | 本项目演示程序的真实采样:32 条合并栈、最深 13 帧、总权重约 1893 ms |
| testdata/stress-recursive.folded | 上游样例的冻结剖面转换:最深 7254 帧,用于深度限制和不限深回归 |
| testdata/demo-baseline.folded | 人工构造的演示基线,不是另一次真实采样 |
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.svg]
moon run cmd/main -- delta <before> <after> [--top N]
moon run cmd/main -- filter <pattern> <input> [-o out.folded]| 出图参数 | 默认值 | 约束 |
|---|---|---|
| -o, --out | 标准输出 | 指定则写文件 |
| --width | 1400 | 有限且大于 20 像素(左右各留白 10) |
| --max-depth | 32 | 非负整数;0 表示不限深 |
| --unit | ns | 必须为已知单位;也接受代码中定义的完整单位别名 |
| --inverted | 关闭 | 冰柱方向 |
| 退出码 | 含义 |
|---|---|
| 0 | 成功、帮助、版本查询;过滤零匹配也是成功 |
| 1 | IO、输入数据、计算或数值校验失败 |
| 2 | 参数错误 |
moon run cmd/main -- callers testdata/demo.folded walk_tree
moon run cmd/main -- callees testdata/demo.folded build_strings
moon run cmd/main -- filter build_strings testdata/demo.folded -o sub.folded
moon run cmd/main -- sub.folded --out sub.svg
moon run cmd/main -- diff testdata/demo-baseline.folded testdata/demo.folded -o diff.svg
moon run cmd/main -- delta testdata/demo-baseline.folded testdata/demo.folded
| 操作 | 行为 |
|---|---|
| 悬停 | 帧名、权重、全图占比和变化量 |
| 单击 | 缩放到该子树;祖先半透明,点击祖先可扩大视图 |
| Reset Zoom | 恢复原始几何和显示状态 |
| Ctrl-F / Search | 正则搜索;再次点击重置 |
| Ctrl-I / ic | 切换是否忽略大小写 |
import {
"gywcs101/moonflame",
}///|
pub fn folded_to_svg(text : String) -> String raise {
let parsed = @moonflame.parse_folded(text)
if parsed.skipped_lines > 0 { fail("invalid profile") }
let tree = @moonflame.aggregate(parsed.samples, max_depth=32)
let layout = @moonflame.layout(tree, 1380.0, @moonflame.FRAME_HEIGHT)
@moonflame.render_svg(layout)
}| API | 参数含义 |
|---|---|
| parse_folded(text) | 有效样本及逐行诊断 |
| aggregate(samples, max_depth=...) | 非负样本聚合 |
| layout(tree, width, row_height) | 内容宽度,不含渲染留白 |
| render_svg(layout) | 生成完整 SVG |
| top_hotspots(tree, top) | 按自身权重聚合热点 |
| find_callers(tree, name) / find_callees(tree, name) | 调用关系覆盖统计 |
| diff_against(current, baseline) | 当前树的累计与自身变化 |
| compute_delta(before, after, top) | 函数自身权重差异 |
| filter_folded(text, pattern) | 保留匹配栈的原始权重 |
moon build --target wasm-gc
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.folded
moon run cmd/main -- app.folded --out flame.svgmoon check --target all --deny-warn
moon build --target all --deny-warn
moon test --target all --deny-warn
moon fmt --check
moon info
python scripts/test-cli.py
python scripts/test-cli.py --target js
python scripts/test-cli.py --target wasm
python scripts/test-consumer.py
python scripts/verify-fixtures.py
npm ci --prefix tests/browser --ignore-scripts
node tests/browser/node_modules/playwright/cli.js install chromium
node tests/browser/test.cjs
moon package
python scripts/verify-package.pypub(all) suberror ValidationError {
InvalidWeight(String)
InvalidTree(String)
InvalidGeometry(String)
}pub struct DeltaEntry {
name : String
before : Double
after : Double
change : Double
ratio : Double
} derive(Eq)pub(all) enum ParseIssueKind {
MissingWeight
InvalidNumber
NonFiniteWeight
NegativeWeight
} derive(Eq)pub struct ParseResult {
samples : Array[Sample]
skipped_lines : Int
diagnostics : Array[ParseIssue]
} derive(Eq)pub struct Rect {
x : Double
y : Double
width : Double
height : Double
name : String
value : Double
depth : Int
delta : Double?
} derive(Eq)fn compute_delta(before : CallTree, after : CallTree, top : Int) -> Array[DeltaEntry] raise ValidationErrorfn 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 layout(tree : CallTree, width : Double, row_height : Double, inverted? : Bool) -> Layout raise ValidationErrorfn 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, matched? : Bool) -> Stringfn render_svg(layout : Layout, min_width? : Double, font_size? : Double, show_labels? : Bool, unit? : WeightUnit, normalize_names? : Bool) -> String raise ValidationErrorfn version() -> StringInstall
Download zipRender folded stack profiles into interactive SVG flame graphs.
Dependencies