SpyGlass 段错误/崩溃:sg_shell segfault crash 速查
更新时间:2026-10-23。FAQ 速查页,属于 EDA 工具排错系列。SpyGlass(sg_shell / VC SpyGlass)在跑 lint_rtl、cdc_verify 等 goal 时跑到一半直接
Segmentation fault (core dumped)退出,log 在崩溃点戛然而止,是最没有上下文的一类故障。好消息是收敛套路固定:先分清是 OOM 误杀还是真段错误,再按内存、递归展开、版本错配、磁盘、已知 bug 五条线逐条排。通用的段错误分析见 core dump 分析,同类工具的崩溃排查见 PrimeTime 段错误/崩溃速查,SpyGlass 正常用法见 RTL Lint 与 SpyGlass 流程。
本文要回答的问题
- sg_shell 直接 segfault 没有任何报错,第一步做什么?
- 怎么区分「内存吃爆被 OOM killer 干掉」和「真的踩了非法地址」?
- 递归 `include、旧库新版读、磁盘写满,为什么会表现成段错误?
- 怀疑命中已知 bug,怎么快速验证并收集证据?
一、报错原文与现象
Segmentation fault (core dumped)
sg_shell terminated abnormally, exit code 139
*** Error: internal error, please contact Synopsys support
run_lint_rtl: process killed during elaborate stage先认指纹:退出码 139 = 128 + 11(SIGSEGV),是真段错误;137(128+9,SIGKILL)几乎总是 OOM killer 干的,那不是 segfault,是内存问题走另一条通道;134(SIGABRT)多半是堆破坏或被运行时检查主动中止。另外一类迷惑性现象是「log 看着像崩溃其实是被杀」——elaborate 阶段 RSS 一路涨到机器 RAM 上限,进程悄无声息消失,这种情况去 dmesg 里一定能找到 OOM 记录。
二、可能原因总览
| 报错现象 | 可能原因 | 处置 |
|---|---|---|
| 退出码 137,dmesg 有 OOM 记录 | 超大设计 elaborate 峰值内存超 RAM | 加内存/降 goal 范围/拆设计 |
| 退出码 139 且留下 core | 真段错误(空指针/栈溢出) | gdb 取栈回溯,最小化用例 |
| 读入特定文件就崩 | 递归 `include 或宏无限展开打爆解析栈 | 展开 include 树,改写法 |
| 换机器/清缓存后不崩 | spyglass 缓存目录里半成品或版本错配 | 清 spyglass 工作目录重跑 |
| 新版 sg_shell 读旧工程崩 | 库/缓存与二进制版本不匹配 | 对应版本重建库与缓存 |
| 崩溃前磁盘报警 | 磁盘满导致中间文件写半截 | 清空间,删残留重跑 |
| .awl waiver 一加载就崩 | waiver 文件与当前规则版本不兼容 | 按当前版本重新生成 waiver |
| 同用例换小版本就好 | 命中已知 bug | 版本二分,报厂商要 patch |
三、逐条排查展开
先分清谁杀的,这是总分流阀。 崩溃后立刻做三件事:看退出码(139 / 137 / 134);dmesg | tail -50 找 OOM killer 记录;确认 core 是否生成(ulimit -c 是否为 0、core 目录是否可写)。有 OOM 记录就走内存通道:lint_rtl 全量 elaborate 大设计时峰值内存冲到几十 GB 很常见,机器 RAM 或队列配额不够就会被杀,处置是加内存、拆 goal 分批跑、或换大内存节点。139 且有 core 才是真段错误,往下走。
超大设计的内存画像要先量出来。 别凭感觉说「设计太大」,用 /usr/bin/time -v sg_shell ... 或跑批时挂 top/pidstat 记录峰值 RSS,和设计规模(模块数、elaborate 后实例数)建对照。峰值贴着机器 RAM 抖动的,先降内存压力再谈别的——比如把 cdc 和 lint 分开跑、关掉不需要的 rule group、黑盒掉不关心的 IP。内存问题不解决,后面所有「段错误」都可能是 OOM 的变种表现。
**递归 include 与宏无限展开是 SpyGlass 特有的压垮点。** SpyGlass 的 HDL 解析器对 include 套 include、宏互相递归展开没有深度保护,栈打爆就是段错误。特征是小设计不崩、读到某个文件必崩、且崩溃点在 parse/elaborate 早期。排查:用 sg_shell -show_include`(或预处理 dump)把 include 树展开看有没有环;工程里 grep 循环引用(A include B、B 又 include A);宏定义互相嵌套的改成手动展开。语法类问题的前置排查见 SpyGlass 语法解析失败速查。
版本与库错配是换环境后崩溃的第一嫌疑。 新版 sg_shell 读旧版编译的库、旧工程残留的 spyglass 缓存目录(里面有按旧版本格式序列化的中间结果),都可能因格式不兼容直接段错误而不是给出友好的版本报错。处置分两步:先整目录清掉 spyglass 工作目录(工程下 spyglass/ 或 SPYGLASS_WORK_DIR 指向处)与编译缓存重跑;再确认 .lib/.db 库与当前 sg_shell 版本配套,不配套的用对应版本重新编译。缓存类疑难另见 SpyGlass 缓存过期问题。
磁盘满写半截文件会引发「延迟爆炸」。 elaborate 阶段中间文件写一半磁盘满了,后续阶段读到截断文件,轻则解析报错,重则内部状态错乱段错误——而崩溃点离真正病因(磁盘满)可能已经隔了几个阶段。崩溃后先 df -h 看工作目录和 /tmp,满了清空间、删掉半成品中间文件重跑;队列环境还要确认临时目录配额。
waiver 文件与规则版本不兼容会崩在加载早期。 .awl 文件是按生成时的规则版本序列化的,跨大版本直接加载旧 waiver,可能触发内部断言或段错误。特征是加 -waiver 参数就崩、去掉就好。处置:用当前版本重新生成 waiver(旧 waiver 只作人工迁移的参考),不要跨大版本直接复用。
版本二分是最便宜的「是不是已知 bug」判据。 手头有相邻两三个版本的 sg_shell:同一最小用例旧版不崩新版崩,是新版本回归,短期回退顶住并报 bug;新版不崩旧版崩,说明 bug 已修,升级即可。三次运行定性,比任何调试都快。配合最小化用例(二分砍设计、砍 waiver、砍 rule,直到几十行文件稳定复现),就是向厂商报 bug 的完整证据包。
sg_shell崩溃退出 -> 分谁杀的: 退出码与dmesg与core
分谁杀的: 退出码与dmesg与core -> 137被kill: 查OOM与峰值RSS
分谁杀的: 退出码与dmesg与core -> 139真段错误: gdb取栈回溯定阶段
139真段错误: gdb取栈回溯定阶段 -> 读文件期崩: 展开include树查递归
139真段错误: gdb取栈回溯定阶段 -> 加载期崩: 清缓存重建库查版本错配
137被kill: 查OOM与峰值RSS -> 修复: 加内存或拆goal降峰值
读文件期崩: 展开include树查递归 -> 修复: 解开include环与递归宏
加载期崩: 清缓存重建库查版本错配 -> 修复: 配套版本重建并重生成waiver
修复: 加内存或拆goal降峰值 -> 归档: 版本二分与最小化用例沉淀
修复: 解开include环与递归宏 -> 归档: 版本二分与最小化用例沉淀
修复: 配套版本重建并重生成waiver -> 归档: 版本二分与最小化用例沉淀
sg_shell崩溃退出.style: { fill: "#FDECEC"; stroke: "#D9534F" }
分谁杀的: 退出码与dmesg与core.style: { fill: "#EAF2FE"; stroke: "#3B6FD4" }
137被kill: 查OOM与峰值RSS.style: { fill: "#EAF2FE"; stroke: "#3B6FD4" }
139真段错误: gdb取栈回溯定阶段.style: { fill: "#EAF2FE"; stroke: "#3B6FD4" }
读文件期崩: 展开include树查递归.style: { fill: "#EAF2FE"; stroke: "#3B6FD4" }
加载期崩: 清缓存重建库查版本错配.style: { fill: "#EAF2FE"; stroke: "#3B6FD4" }
修复: 加内存或拆goal降峰值.style: { fill: "#FEF3E2"; stroke: "#E08A1E" }
修复: 解开include环与递归宏.style: { fill: "#FEF3E2"; stroke: "#E08A1E" }
修复: 配套版本重建并重生成waiver.style: { fill: "#FEF3E2"; stroke: "#E08A1E" }
归档: 版本二分与最小化用例沉淀.style: { fill: "#F1ECFD"; stroke: "#7A5BD4" }读这张图抓住主线:颜色按语义分角色——红是崩溃入口,蓝是排查动作,橙是修复,紫是收尾沉淀。第一步永远是用退出码 + dmesg + core 三件事把 OOM 误杀(137)和真段错误(139)分开;真段错误再按崩溃阶段分流——读文件期崩查递归 `include 与宏,加载期崩查缓存与版本错配;两条线收敛后都要落到「版本二分 + 最小化用例」沉淀成可复现的证据包,而不是一句「工具又崩了」。
四、预防与纪律
- 跑批默认开 core 保留:作业模板里
ulimit -c unlimited,core 目录落在可写、有配额的盘上,崩溃即有现场。 - 内存峰值建档:每个设计的 elaborate 峰值 RSS 入库,队列申请按历史峰值加裕量,不让 OOM 伪装成 segfault。
- include 树定期查环:工程级脚本周期展开 include 依赖,循环引用在 review 阶段拦截,不进跑批。
- 版本升级配套重建:升 sg_shell 版本时,库、缓存、waiver 三件套同步重建,不留跨版本混用。
- 磁盘水位监控:工作目录与 /tmp 加告警,水位到 85% 先清再跑,避免半截文件引发二次崩溃。
- 崩溃证据包模板化:core 的 bt 输出、log 尾部、最小化用例、版本与环境快照四件套固定格式,上报厂商与内部复盘都省事。
这一篇在知识体系里的位置
本篇是 EDA 排错速查簇中「SpyGlass 工具崩溃」节点的 FAQ 页。通用的段错误与 core 分析见 core dump 分析;SpyGlass 其他高频问题见 SpyGlass 语法解析失败、SpyGlass 缓存过期、SpyGlass license 检出失败;同类工具的崩溃排查见 PrimeTime 段错误/崩溃、Design Compiler 段错误/崩溃;SpyGlass 正常流程见 RTL Lint 与 SpyGlass 流程。
一句话总结
SpyGlass 段错误先用「退出码 + dmesg + core 是否生成」把 OOM 误杀(137)与真段错误(139)分开;真段错误按崩溃阶段分流——读文件期崩查递归 `include 与宏无限展开,加载期崩查 spyglass 缓存与库版本错配,全程警惕磁盘满写半截文件的延迟爆炸;waiver 跨版本直接加载也是经典崩点,须按当前版本重生成;最后靠「版本二分三次运行定性 + 最小化用例」沉淀证据包,并靠 core 默认保留、内存峰值建档、include 查环、磁盘水位监控让每次崩溃都留下可复盘的现场。