十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SpiderMonkey 的 iongraph:用 GraphViz 可视化 IonMonkey 优化图的完整指南

SpiderMonkey 的 iongraph:用 GraphViz 可视化 IonMonkey 优化图的完整指南 SpiderMonkey 的 iongraph用 GraphViz 可视化 IonMonkey 优化图的完整指南【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongo本文基于 iongraph 官方文档 撰写并结合 SpiderMonkey 源码中 JIT Spew 子系统JitSpewer.cpp、JSONSpewer.cpp与 iongraph 工具本体 进行深度扩充。导读iongraph是 SpiderMonkeyMozilla 的 JavaScript 引擎内嵌于本仓库 MongoDB 的src/third_party/mozjs中自带的 IonMonkey 优化中间表示IR可视化工具它把 JIT 编译过程中产生的 MIR/LIR 图序列导出为 GraphViz 的 Dot 格式再渲染成 PDF/SVG/PNG帮助引擎开发者直观看到每次优化 pass 前后指令流与控制流的变化。读完本文你将掌握从 Debug 构建、IONFLAGSlogs采集日志到iongraph一键出图的完整工作流并能读懂输出图中每种颜色、线型、标注背后对应的 MIR 标志位含义。iongraph 是什么iongraph位于仓库的 devtools/iongraph 目录 下是一个 Python 3 脚本原名iongraph最初由 Sean Stangl 编写见目录内 LICENSE。其官方定位一句话即可概括Visualizer for IonMonkey graphs using GraphViz.它的工作本质是一条流水线采集以IONFLAGSlogs环境变量运行jsSpiderMonkey 的 shellIonMonkey 编译器在每一个优化 pass 前后把 MIR/LIR 图以 JSON 形式写到磁盘转换iongraph读取 JSON为每个 pass 构造两个 Dot 图MIR 一张、LIR 一张优先输出 LIR渲染调用 GraphViz 的dot程序把 Dot 图渲染成 PDF默认或 SVG/PNG并按函数/ pass 命名输出文件。在 iongraph 脚本 中可以看到完整的数据流json.loads(parenthesize(s))读取 JSON其中parenthesize()会在日志因崩溃而截断时补全缺失的闭合括号保证还能解析出已写完的部分随后buildGraphs(func)/buildOnlyFinalPass(func)构造 Dot 图最后由genfiles/genmergedpdfs调用dot渲染iongraph。环境要求与前置条件使用 iongraph 需要以下组件组件用途备注SpiderMonkeyDebug 构建的jsshell产出 IONFLAGS 日志官方 README 明确要求 debug build因为 JIT Spew 代码被#ifdef JS_JITSPEW包裹见 JitSpewer.cpp只有 debug 构建才启用GraphViz 的dot可执行文件渲染 Dot 图为最终格式缺失时脚本直接报错退出ERROR: graphviz (dot) is not installediongraphpdftk或qpdf仅当输出格式为合并的pdf时需要用于把单个函数的多个 pass 页面合并成一个 PDF不安装也可以改用--format pdfs生成每图一个 PDFiongraph此外日志默认写到/tmp/ion.jsonLinux/macOSWindows 上默认写到当前目录.Android 上写到/data/local/tmp这一默认值由 JitSpewer.cpp 中的JIT_SPEW_DIR宏决定。基本使用步骤第一步采集 IonMonkey 日志用IONFLAGSlogs环境变量运行一个 JS 模块IONFLAGSlogs js -m mymodule.mjsSpiderMonkey 会把编译过程中关于图状态的观测信息每个 pass 的 MIR/LIR、指令属性、ResumePoint、寄存器分配区间等dump 到/tmp/ion.json。第二步生成图iongraph此时工作目录下会为日志中的每个函数生成一个 PDF默认格式每个 pass 对应一页。两步合一iongraph js子命令为了方便iongraph 提供了js子命令把运行js与生成图合并为一次调用iongraph js -- -m mymodule.mjs以--为界--之前的参数属于iongraph--之后的参数原样传给js。例如只针对 1 号函数出图iongraph js --funcnum 1 -- -m mymodule.mjs从源码看js_and_gen会先把logs强制追加进IONFLAGS即使你只设了别的 flag 也会保证日志开启再执行js最后自动以/tmp/ion.json为输入调用生成流程iongraph。命令行参数详解以下参数定义来自 iongraph 脚本的add_main_arguments对顶层命令、js子命令与jit-test子命令都适用参数说明默认值-o, --outdir输出文件存放目录.当前目录-f, --funcnum只处理指定下标的函数多个用逗号分隔如1,5,234全部-n, --funcname只处理指定名字的函数多个用逗号分隔如foo,bar,baz全部-p, --passnum只处理指定序号的 pass多个用逗号分隔全部--format输出格式可选gv/pdf/pdfs/png/svgpdf--final只生成最终优化后的 MIR/LIR 图关--out-mir把选中的 MIR 图写入指定文件无--out-lir把选中的 LIR 图写入指定文件无input位置参数IonMonkey 生成的 JSON 日志路径/tmp/ion.json几个值得注意的行为细节pdf与pdfs的区别pdf会把每个函数的所有 pass 图合并成一个多页 PDF依赖pdftk/qpdfpdfs与其它格式一样每张图单独一个文件iongraph。默认输出策略每个 pass 通常只输出 MIR/LIR 中的一种优先 LIR因为 LIR 图不含后继信息绘制边时需要借助 MIR 图见下文原理。只有在--final模式下才同时输出 MIR 与 LIRiongraph。--funcnum与--passnum的下标函数序号取自 JSON 中functions数组的下标从 0 开始pass 序号同理。日志中没有指定函数时会提示abort during SSA construction正常则打印success; N passesiongraph。jit-test 子命令除了js子命令iongraph 还支持jit-test子命令可配合 SpiderMonkey 的jit_test.py运行单个测试用例并出图iongraph jit-test --funcnum 1 -- category/test.js它内部以js/src/jit-test/jit_test.py --one test的形式执行测试同样自动追加logs到IONFLAGSiongraph。输出文件命名与格式生成的 Dot 文件命名规则在 outputPass 函数 中定义funcNN-passNN-pass名-mir|lir.gv函数序号与 pass 序号都是两位补零如func01-pass03图名使用 pass 名如LICM、GVN等优化 pass 名称mir/lir后缀标明是 MIR 图还是 LIR 图。渲染后gv原样拷贝png/svg/pdf(s)调用dot -Tformat转换pdf模式再用pdftk/qpdf把同一函数的所有页面合并genmergedpdfs。生成的 Dot 图中图的标题标签是「函数名 - pass 名」并附带图例蓝色movable、下划线guard、红色in worklist、灰色recovered on bailoutiongraph。读懂输出图图形属性语义这一节是官方 README 的核心内容逐条对应到源码实现块Block的属性绿色边框limegreen循环头loop header。对应 JSON 中块的loopheader属性源码在 JSONSpewer.cpp 中由block-isLoopHeader()输出iongraph 据此给节点加color: limegreeniongraph。红色边框crimson包含循环回边loop backedge的块即其某个后继是循环头。对应 JSON 的backedge属性由block-isLoopBackedge()输出iongraph 给节点加color: crimsonJSONSpewer.cpp、iongraph。虚线边框dashed临界边分裂critical edge splitting过程中创建的块。对应splitedge属性由block-isSplitEdge()输出iongraph 给节点加style: dashedJSONSpewer.cpp、iongraph。此外块标签中还会显示块使用计数Count: N与上述属性的文字标注如(loop header)、(backedge)、(split edge)见 getBlockLabel。指令Instruction的属性蓝色可移动Movable指令。iongraph 在指令属性列表里检测到Movable时把指令名染成蓝色iongraph。下划线Guard守卫指令例如类型检查、边界检查等。检测到Guard属性时给指令名加下划线iongraph。红色仍在 worklist 中的指令InWorklistSSA 构建/处理期间的状态检测到该属性时染红iongraph。灰色gray50发生 bailout退出优化时被恢复RecoveredOnBailout的指令表示该指令的结果可以在遇到 bailout 时由 interpreter 重新计算无需保存在栈快照里iongraph。这些属性全部来自 MIR 定义上的标志位由 JSONSpewer.cpp 的spewMDef通过MIR_FLAG_LIST宏展开后逐个输出到 JSON 的attributes列表中。MResumePointMResumePoint 作为指令行呈现着色为灰色。每个 MResumePoint 保存了指令执行点处的环境快照用于 bailout 恢复与 OSR。JSON 中它以resumePoint字段出现包含caller内联调用者所属块、mode如At/After等来自ResumeModeToString以及按序排列的operands跨内联调用链时用|分隔见 JSONSpewer.cpp 的spewMResumePoint。iongraph 中 ResumePoint 行显示为resumepoint 操作数列表同样以灰色渲染getResumePointRow。块入口处的 ResumePoint 会出现在块顶部指令自带的 ResumePoint 则以At/After两种 mode 分别出现在指令前/后。边的标注以条件分支结尾的块其出边会用真值 0 或 1 标注。实现上当块有 2 个后继时指向successors[0]的边标1真分支指向successors[1]的边标0假分支iongraph。这对应控制流图中条件的布尔真值与 MIR 中分支指令的两个出边约定一致。关于 MIR 与 LIR 两套图每个 pass 会输出 MIR 与 LIR 两张候选图buildGraphsForPass。两者共享相同的块划分LIR 图不含后继successor信息因此绘制 LIR 图的边时直接复用 MIR 图中对应块的successors列表见 buildGraphForIR 的注释。这解释了为什么默认只输出 LIR 而非同时输出两张。底层原理JSON 日志是如何产生的日志的产生完全在 SpiderMonkey 侧与 iongraph 无关理解它有助于排查为什么没有输出的问题。IONFLAGS 环境变量IONFLAGS是逗号分隔的 flag 列表在 JitSpewer.cpp 的CheckLogging()中被解析。与 iongraph 相关的两个关键 flaglogs开启异步JSON 日志EnableIonDebugAsyncLogging。所谓异步是指每个 pass 的图先缓存在内存中到函数编译结束时才一次性写入文件对应 endFunction 中的 dump 时机。logs-sync开启同步日志EnableIonDebugSyncLogging。每个 pass 结束后立即 flush 到文件对应 spewPass这在编译中途崩溃时能保留更多现场但官方注释建议此时关闭 off-thread 编译。在 shell 中可用IONFLAGShelp js打印完整的 flag 列表其中包括logs JSON visualization logging与logs-sync Same as logs, but flushes between each pass等说明JitSpewer.cpp。输出文件与 JSON 结构文件路径固定为JIT_SPEW_DIR/ion.jsonLinux 上即/tmp/ion.json若设置了环境变量ION_SPEW_BY_PID则改为/tmp/ionpid.json便于多进程并行实验JitSpewer.cpp。文件顶层结构为{functions: [...]}JitSpewer.cpp 与析构时的\n]}收尾。每个函数条目由 JSONSpewer.cpp 写出beginFunction记录函数名格式为文件名:行号如mymodule.mjs:12并开启passes列表L21-L25每个 pass 记录name、mir块列表与lir块列表L33-L36、L134-L239MIR 块包含number、attributes、predecessors、successors、instructions、入口resumePointL138-L186每个 MIR 指令包含id、opcode、attributes、inputs操作数 id、uses使用方 id、memInputs内存依赖、typeMIRType含范围分析结果与截断标记(t)以及可选的resumePointL65-L132在带寄存器分配的 pass 中还会额外输出ranges记录每个虚拟寄存器vreg的活跃区间allocation、start、end这是回溯式寄存器分配器BacktrackingAllocator的输出L241-L282。IONFILTER只记录关心的函数日志可能极其庞大。环境变量IONFILTER提供了按源文件/行号过滤的机制值为逗号分隔的文件或文件:行号表达式只有匹配的函数才会被记录不匹配时计入filteredOutCompilations见 JitSpewer.cpp 的FilterContainsLocation。例如IONFILTERmymodule.mjs IONFLAGSlogs js -m mymodule.mjs典型调试工作流示例综合以上内容一个完整的端到端示例# 1) 只记录 mymodule.mjs 中的函数异步采集日志 IONFILTERmymodule.mjs IONFLAGSlogs js -m mymodule.mjs # 2) 只处理第 1 个函数、只生成最终优化后的 MIR/LIR输出为 SVG iongraph -f 1 --final --format svg # 3) 或一步到位先运行 js 再出图-- 后的参数传给 js iongraph js --funcnum 1 --format png -- -m mymodule.mjs # 4) 需要单页 PDF不依赖 pdftk/qpdf时 iongraph --format pdfs出图后在生成的func01-passNN-pass名-mir.gv或-lir.gv中绿框定位循环结构、红框定位回边、蓝字定位可移动指令、下划线定位守卫指令、灰色定位可恢复指令与 ResumePoint配合0/1边标注即可逐 pass 追踪优化前后的形态差异。局限与注意事项必须是 Debug 构建JitSpewer与JSONSpewer的实现都包裹在#ifdef JS_JITSPEW内JitSpewer.cpp、JSONSpewer.cppRelease 构建不会编译这些代码也不会有任何日志输出。日志默认路径固定为/tmp/ion.json可用ION_SPEW_BY_PID改为带 pid 的文件名若目录不可写或文件被占用init()会失败并返回 falseJitSpewer.cpp。合并 PDF 需要额外工具不装pdftk/qpdf时请改用--format pdfs否则脚本会报错退出。JSON 容错iongraph的parenthesize()会为崩溃截断的日志补全闭合括号尽量保住已生成的部分iongraph但它明确不处理转义字符串极端场景下可能解析失败。参考文件索引官方使用文档README.mdiongraph 工具本体Python 3iongraphIONFLAGS 解析与日志写出JitSpewer.cppJSON 图序列化MIR/LIR/ResumePoint/RangesJSONSpewer.cpp【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表