mooncallgrind

    Portable Callgrind records for MoonBit: call sites, target positions and exact counters.

    callgrind
    profiling
    parser
    developer-tools
    Download zip
    Version
    0.1.0
    License
    MIT
    Last updated
    7 days ago
    Downloads
    17

    No dependencies

    #MoonCallgrind

    MoonBit 的 Callgrind 文本格式库。把压缩文件解析为类型化记录,保留调用点、 目标位置、直接递归、分段边界和 UInt64 计数,由使用者决定怎样聚合。

    尚未发布到 Mooncakes、尚未正式申报、没有获得赛事初审认可。 AI 辅助了实现、测试和文档;参赛者需理解并对代码负责,正式人工申报另行完成。

    #用途

    同一个 main -> work 可以来自第 10、20 行,目标位置也可能不同。先合并函数边 会失去这些差异。本库保留成本行与对应 calls= 记录,供 MoonBit 程序直接处理:

    • 在性能检查中区分同一 caller/callee 的不同调用点。
    • 在 Wasm 或 JS 工具中导入现有 Callgrind 文件,读取完整调用记录。
    • 依据不同分段的事件表做自定义汇总,或先规范化再交给其他工具。

    不包含采样器、GUI、符号解析或 pprof 转换。totals、diff 是消费示例; 本库不将指令计数解释为耗时,不推导递归 inclusive 总成本。

    #运行

    需要官方 MoonBit 工具链;CLI 另外需要 Node.js 22+。

    git clone https://github.com/Rindoncreate/mooncallgrind.git cd mooncallgrind moon check --target js --deny-warn moon build --target js --deny-warn moon test --target js --deny-warn moon run examples/sites --target js node bin/mooncallgrind.mjs inspect examples/two-sites.callgrind node bin/mooncallgrind.mjs totals examples/two-sites.callgrind Ir node bin/mooncallgrind.mjs normalize examples/two-sites.callgrind normalized.callgrind node bin/mooncallgrind.mjs diff examples/two-sites.callgrind normalized.callgrind Ir

    规范化输出文件必须不存在,避免覆盖。CLI 严格解码 UTF-8;JSON 中的计数、位置和 调用次数全部是十进制字符串,避免 JavaScript Number 截断。

    发布前可使用源码或 MoonBit workspace 引入模块。源码包消费者检查不等于 Mooncakes 安装验证;正式发布后再补经过实测的 registry 安装步骤。

    #MoonBit API

    在包的 moon.pkg 中导入 "Rindoncreate/mooncallgrind" @cg:

    let p = @cg.parse("events: Ir\nfn=main\ncfn=work\ncalls=2 70\n10 30\n")
    for record in p.records {
    match record.kind {
    SelfCost => ()
    Call(count, target, positions) =>
    println("\{record.positions[0]} -> \{target.function_name}:\{positions[0]}, calls=\{count}")
    }
    }

    片段应位于能处理 @cg.ParseError 的函数中。完整示例见 examples/sites,公共签名见 pkg.generated.mbti。

    接口行为
    parse(text)返回 Profile;失败时抛出带行号的 Syntax、Unsupported 或 Resource
    normalize(text)规范化为绝对位置和显式名称 ID,保留支持的记录与头字段
    profile.self_totals(event)按每段事件名定位列,合并函数自身成本,排除调用边成本
    diff(before, after, event)返回精确 before/after 值,不推断回归原因

    格式解释、数值检查、规范化和分析均为 MoonBit;Node 仅处理文件和输出。 库不需要 Python、Rust 或 Valgrind 进程;Valgrind 仅用于独立兼容性测试。

    #验证、范围与来源

    源码 MIT;真实样本 Apache-2.0,归属和原始许可证见 fixtures/NOTICE.md。 AI 辅助开发。通用 CI 导出和源码包消费者脚手架改编自作者此前的 moondhcp4, 没有复用 DHCP 协议实现。

    ParseError

    pub(all) suberror ParseError {
    Syntax(Int, String)
    Unsupported(Int, String)
    Resource(Int, String)
    } derive(
    Debug
    )

    Change

    pub(all) struct Change {
    object : String
    file : String
    function_name : String
    before : UInt64
    after : UInt64
    } derive(Eq,
    Debug
    )

    Change::equal

    fn Change::equal(Change, Change) -> Bool

    Change::not_equal

    fn Change::not_equal(x : Change, y : Change) -> Bool

    Change::to_repr

    Context

    pub(all) struct Context {
    object : String
    file : String
    function_name : String
    cost_file : String
    } derive(Eq,
    Debug
    )

    Function identity and the actual source file of this record (including inlining).

    Context::equal

    fn Context::equal(Context, Context) -> Bool

    Context::not_equal

    fn Context::not_equal(x : Context, y : Context) -> Bool

    Context::to_repr

    pub(all) struct Header {
    key : String
    value : String
    line : Int
    } derive(Eq,
    Debug
    )

    Header::equal

    fn Header::equal(Header, Header) -> Bool

    Header::not_equal

    fn Header::not_equal(x : Header, y : Header) -> Bool

    Header::to_repr

    Part

    pub(all) struct Part {
    headers : Array[Header]
    events : Array[String]
    positions : Array[String]
    } derive(Eq,
    Debug
    )

    Physical event order and subposition order are local to each dump part.

    Part::equal

    fn Part::equal(Part, Part) -> Bool

    Part::not_equal

    fn Part::not_equal(x : Part, y : Part) -> Bool

    Part::to_repr

    Profile

    pub(all) struct Profile {
    parts : Array[Part]
    records : Array[Record]
    } derive(Eq,
    Debug
    )

    Arrays are caller-owned; do not mutate them during an operation.

    Profile::equal

    fn Profile::equal(Profile, Profile) -> Bool

    Profile::not_equal

    fn Profile::not_equal(x : Profile, y : Profile) -> Bool

    Profile::self_totals

    fn Profile::self_totals(self : Profile, event : String) -> Array[Total] raise ParseError

    No inferred recursion or inclusive totals. Overflow is a checked error.

    Profile::to_repr

    Record

    pub(all) struct Record {
    part : Int
    line : Int
    context : Context
    positions : Array[UInt64]
    values : Array[UInt64]
    kind : RecordKind
    } derive(Eq,
    Debug
    )

    Values are raw counters, not time estimates. Costs of calls are not self costs.

    Record::equal

    fn Record::equal(Record, Record) -> Bool

    Record::not_equal

    fn Record::not_equal(x : Record, y : Record) -> Bool

    Record::to_repr

    RecordKind

    pub(all) enum RecordKind {
    SelfCost
    Call(UInt64, Context, Array[UInt64])
    } derive(Eq,
    Debug
    )

    Call records retain the count, target identity and target subpositions.

    RecordKind::equal

    fn RecordKind::equal(RecordKind, RecordKind) -> Bool

    RecordKind::not_equal

    fn RecordKind::not_equal(x : RecordKind, y : RecordKind) -> Bool

    Total

    pub(all) struct Total {
    object : String
    file : String
    function_name : String
    value : UInt64
    } derive(Eq,
    Debug
    )

    Total::equal

    fn Total::equal(Total, Total) -> Bool

    Total::not_equal

    fn Total::not_equal(x : Total, y : Total) -> Bool

    Total::to_repr

    diff

    fn diff(before : Profile, after : Profile, event : String) -> Array[Change] raise ParseError

    event meaning, build and collection conditions are comparable.

    normalize

    fn normalize(text : String) -> String raise ParseError

    original whitespace are not retained. Counters and all supported records are.

    parse

    fn parse(text : String) -> Profile raise ParseError

    Parse the documented cost/call profile. Jumps and unknown body directives fail closed.

    profile_version

    fn profile_version() -> String