崩溃复查与“回声“bug:异常码判案、漏网之鱼和耳塞开关

崩溃复查与"回声"bug:异常码判案、漏网之鱼和耳塞开关

2026-08-21

昨天修了"数据库瞬断打死程序"的 bug,今天现场又崩了三次。复盘后发现两个教训:"修崩溃要搜干净所有隐患点"和"改完代码要确认部署"。另外今天还揪出一个"点几下才发作"的界面 bug------三个环节互相触发转圈。一篇讲完。


一、又崩了:排除法四查,锁定"没接住的异常"

今天现场程序又崩了 3 次,报错和昨天一模一样。这次用了排除法逐项排查:

查什么 怎么查 结果
数据库崩了没 看 binlog 时间戳(还在写=没崩)+ 数据库错误日志 没崩 ✅
连接数耗尽没 查最大连接数配置 + 当前连接数 没满(2/151)✅
系统网络/磁盘故障没 查系统事件日志 无故障 ✅
程序自己扛住没 看崩溃异常码 没扛住 ❌

前三项全排除,凶手还在程序自己身上。

生活类比:人死了,先查死因------是他杀(数据库挂了)、饿死(连接耗尽)、意外(系统故障),还是自身疾病(程序没接住异常)。四项逐一排除,最后一项就是答案。


二、概念:异常码是"死亡报告单"

崩溃时系统会留下一个异常码,它就是一纸死亡报告:

异常码 含义 结论
0xe0434352 托管异常没被捕获(程序抛了没接住的异常) 程序自身的 bug,可修
0xc0000005 内存访问越界 野指针/越界类问题
0xc00000fd 栈溢出 无限递归类问题

看到 0xe0434352 = 好消息:它明确告诉你"有代码 throw 了、没人 catch",而不是玄学硬件问题。找到没接住的位置,包上 try-catch 就修好了。

类比:医生看到化验单上的特定指标,就能判断是病毒(可治)还是基因病(难治)。0xe0434352 就是那个"可治"的指标。


三、概念:为什么认证插件"多一步"更容易被瞬断打断

报错里总有认证插件的身影(caching_sha2_password)。它不是根因,但它解释了为什么瞬断总发生在认证这一步:

  • 新认证插件(caching_sha2_password)比老插件(mysql_native_password)多一次 RSA 公钥交换------连接建立过程更长、步骤更多
  • 瞬断是随机的,步骤越多,被打断的概率越大------所以断连总是"恰好"死在认证环节,留下误导性的报错
  • 换成老插件或允许客户端取公钥,能减少 断连概率,但根治不了------连接永远可能偶发失败

核心认知:数据库没坏 ≠ 不用修程序。网络的世界没有"永不失败",程序必须自己 try-catch + 重试。


四、教训一:修崩溃要"搜干净",别漏网

昨天修了 2 个崩溃点,今天又崩------因为还有第 3 个:保存生产记录的后台方法里也查了数据库,同样没包 try-catch。

教训 :修崩溃时,先搜索所有数据库操作点,逐个检查有没有异常保护。修一个漏一个,等于没修。

排查句式:找出"所有会查库/连库的地方",列清单,逐个过------清单式排查,而不是想起哪个修哪个

类比:家里闹老鼠,补了一个洞就以为太平了。正确的做法是全屋搜洞,补完一个必须问"还有没有下一个洞"。


五、教训二:改完代码 ≠ 修好了,还要部署

更扎心的一层:现场机器跑的还是旧版程序 ------昨天的修复根本没部署上去。改完代码要确认部署,否则修了也白修。

类比:医生开好了药方,但病人吃的还是昨天的旧药。药方再好,没吃进嘴等于零。


六、新 bug:三个环节互相触发的"回声"

今天另一个 bug 很有意思------界面列表连点几下才开始发作:列表自动滚、条目自动选中、图形自动高亮,来回跳停不下来。

根因:事件回环自激(三环相扣)

复制代码
列表选中 ──绑定──> 图形聚焦状态 ──变化──> 监听器发现"变了"
   ↑                                          │
   └────── 滚动方法又写了一次"选中" <─────────┘
  1. 列表条目选中 ↔ 图形的聚焦状态,两者双向绑定
  2. 监听器盯住聚焦状态,一变就"改选中 + 滚列表"
  3. 滚动方法里又写了一句"选中 = 这个图形"------等于又改了一次选中

第 3 步还是异步排队的:连点几下,队列堆满,A→B→C 来回跳,停不下来。

特征识别 :这类 bug 不是一点就崩,而是"点几下才发作 ""来回跳 "------看到这种描述,第一反应就应该是反馈回环

排查小技巧 :先定位"监听器"(谁订阅了变化事件)→ 再找"回写点"(谁在回调里又设置了一次选中/聚焦)→ 中间那条"又改一次"的语句,往往就是元凶


七、语法:抑制标志 + try/finally(耳塞开关)

修复思路:程序自己滚列表时,让监听器"装聋"。

csharp 复制代码
// ① 加一个开关(默认关)
private bool 抑制标志 = false;

// ② 监听器开头检查开关:开着就 return,不响应
void 监听器(参数)
{
    if (抑制标志) return;   // 耳塞戴着,听不见
    ...原来的滚动逻辑...
}

// ③ 程序自己回写选中时:戴耳塞 → 写 → 摘耳塞
void 滚动定位方法(目标)
{
    抑制标志 = true;         // 戴耳塞
    try
    {
        选中 = 目标;         // 音箱出声(触发监听器,但被耳塞挡住)
    }
    finally
    {
        抑制标志 = false;    // 摘耳塞------finally 保证一定会摘
    }
}

耳塞比喻:麦克风(监听器)对着音箱(滚动方法)。

  • 音箱播放前,给麦克风戴耳塞(标志=true)
  • 音箱自己的声音(回写触发的聚焦变化),麦克风听不见(return)
  • 播完摘耳塞(标志=false)
  • 用户自己点(不戴耳塞)→ 麦克风正常听见 → 照常响应

语法详讲 try/finallyfinally 里的代码无论 try 里正常结束还是抛异常,都一定会执行------用它摘耳塞,保证麦克风不会永远聋着(标志不会卡死在 true,否则用户手点也失效了)。

通用教训 :任何"监听 X 变化 → 又去改 X"的写法都要警惕------改之前先问自己一句:"这次改动会不会再触发我自己?"。异步回调里的回写更隐蔽(把一次振荡放大成排队级联),比同步死循环难查。


八、试卷通用概念重点讲

今天批改的试卷里有几个通用概念值得展开------它们不局限于某个项目,是行业通识。

1. 判定字段的取值语义(1=OK、0=NG 的坑)

判定结果字段,值约定是 1 = OK,0 = NG 。坑在哪?字段名以"NG"字样开头,极易误读成"0 才是合格"------实际是 1 = OK

通用教训 :字段名的字面意思 ≠ 取值语义。读代码要看赋值处判定值 == 1 ? "合格" : "不合格"),别靠名字猜。

类比:门牌写着"非营业时间",实际含义却是"1=开门"。门牌是给人看的,含义要看实际开没开。

2. 生产者-消费者通道(Channel)

场景:探测器(生产者)不断拍图,处理程序(消费者)逐张处理------两边速度不匹配,中间需要一个缓冲。

复制代码
生产者(拍图) ──WriteAsync──> [ 传送带 Channel ] ──TryRead──> 消费者(处理)
                               (无限长,先进先出)
  • Channel.CreateUnbounded<T>() = 造一条现成的传送带
  • Writer.WriteAsync(x) = 把货放上去
  • Reader.TryRead(out x) = 把货取下来

对比ConcurrentQueue + ManualResetEvent 是"手工做传送带"(线程安全队列 + 信号灯自己摆);Channel 是"成品传送带"------有现成的,别自己造。

3. 背钻与灰度阈值(图像里怎么"量"深度)

概念 :PCB 通孔镀铜后,孔里会残留多余铜段(stub),高频信号经过会反射------背钻就是把这截多余铜钻掉。

检测逻辑:量钻孔深度对不对------太浅残留 stub、太深伤到板层,都判不合格。

图像原理 :切片图里铜暗、孔亮;灰度阈值就是亮度分界线,靠它找孔壁边界、量出深度。

复制代码
表面 ▓▓▓▓▓▓▓▓▓▓
镀铜 ▓▓░░░░░░▓▓   ← 孔里多余的铜(stub)要钻掉
板层 ▓▓▓▓▓▓▓▓▓▓
     背钻深度:太浅留 stub / 太深伤层

:灰度阈值、信号系数、深度范围是三个不同参数,别搞混。

4. SECS/GEM 三件套(半导体设备通信标准)

设备要和工厂 MES 系统对话,靠的是行业标准三件套:

组件 管什么
SECS-II 报文格式(信怎么写)
GEM 设备模型(设备该报告什么、听什么命令)
HSMS TCP 传输(信怎么送)

记法 :.NET 实现库的名字 = "SECS for .NET" 的缩写------凡是跟 SECS 相关的库,名字里必有 "SECS" 字样,选项里出现别的名字就是干扰项。

类比:三件套 = 信封格式 + 岗位职责 + 快递公司。

5. 切格子 vs 走路径(两个算法类型,最爱混着考)

类型 管什么 取值含义
切格子(Generate) 视野怎么切 0 = 全局最优(可跨板);1 = 一个视野只含一块板的元件
走路径(Path) 拍摄顺序怎么排 0 = 整体规划;1 = 一块板一个规划

记法:Gen = Generate(切格子)、Path = 路径(走顺序)。题目问的是哪个,答案就是哪个------把"走路径"的描述安到"切格子"头上,就是错因。

类比:切格子 = 蛋糕分几块;走路径 = 先吃哪块的顺序。

6. 权限控制的两种显示状态

机制 :要什么权限 + 无权限时怎么显示。注意------只有两种显示状态

状态 效果
隐藏(Collapsed) 控件完全消失,位置都不占
禁用(Disabled) 看得见,点不了

:题目会塞"第三种状态"当干扰项,或者塞"允许/拒绝"当选项------那是权限结论 ,不是显示状态,两个维度别混淆。

7. 占比类阈值的判定模式

还有一类通用判定:计算某成分占比,和阈值比较 (比如填充率类指标)------高于阈值合格、低于不合格(或反之)。记住这是"占比 vs 阈值"的判定模式即可,具体阈值是配方参数,换机种就变,不背数值。


附录:今日概念速查

试卷通用概念(判定字段语义 / Channel / 背钻阈值 / SECS/GEM / 切格子与走路径 / 权限显示状态)详见第八章。

概念 一句话
0xe0434352 托管异常没被捕获------"程序有 bug,可修"
0xc0000005 / 0xc00000fd 内存越界 / 栈溢出
caching_sha2 认证 多一次公钥交换,瞬断更容易打断它------不是根因
排除法查崩溃 库没崩 + 连接没满 + 系统没事 = 程序自己的锅
事件回环自激 监听 X 又改 X,三环相扣转圈;特征"点几下才发作"
抑制标志 + try/finally 耳塞开关;finally 保证摘耳塞
合并撞号检测 `grep -ohE "编号-0-9+"
相关推荐
mmsx1 天前
我明明调用了 zoomToBounds,地图却总是停在别处?延迟加到 5 秒也没用,真相只有一个
android·人工智能·bug·地图
mmsx1 天前
置灰按钮为什么自己又“亮“了?一次状态缓存“双写冲突“的排查记录
java·缓存·bug·livedata
MartinYeung52 天前
[bug 分析] Orchard 伪造漏洞: 深度代码剖析
安全·区块链·bug
为你奋斗!2 天前
禅道Bug导出CSV文件批量转Word+图片离线部署操作手册
word·bug
隔窗听雨眠4 天前
Library Cache Lock性能故障深度解析:JDBC驱动Bug、绑定变量长度与游标激增的连环效应
bug·library
盗理者4 天前
AI Agent 工程实践|让 AI Agent 自动修 Bug:定位、修改、测试与人工审核
人工智能·bug·agent
程序员AlbertTu4 天前
Linux 系统 Bug 调试操作手册
linux·postgresql·bug
深念Y6 天前
Opencode Event 表写入优化方案
数据库·人工智能·ai·node.js·bug·优化·opencode
姚青&7 天前
Bug基本概念
bug