0/1 已展开

LLM 分析

selftests/sched_ext:让被打断的测试运行正确返回失败

系列概况

  • 标题: [PATCH] selftests/sched_ext: Fail interrupted test runs
  • 作者: Tianyi Chen <hi@tychen.cc>
  • 版本: v1(单封 patch,没有 vN 标记)
  • 规模: 1 个文件,1 insertion(+),1 deletion(-)
  • 修改文件: tools/testing/selftests/sched_ext/runner.c
  • 代码统计: 1 file changed, 1 insertion(+), 1 deletion(-)
  • Message-ID: 20260906135507.749280-1-hi@tychen.cc
  • 完整性: 完整。包含 diff hunk、Fixes tag、Assisted-by LLM、Signed-off-by,并附带 GDB 手工验证四种信号投递 case 的结果。

补丁目的

selftests/sched_ext 的 runner 在收到 SIGINT/SIGTERM 中断时返回非零退出码,避免把"被打断的运行"误报成"全部通过",从而让 CI 和脚本能可靠区分两种语义完全不同的结束状态。

旧流程的问题

runner.c 在收到信号时会让信号处理函数置 exit_req = 1,主循环据此跳出。但 main() 末尾的退出码判定只看 failed

return failed > 0 ? 1 : 0;

只要被打断前没有任何测试失败,进程就以 0 退出。这是经典的"沉默退出"——CI 流水线看到 0 会认为测试全部通过,掩盖了"中途被 Ctrl+C / build system 超时杀掉"的事实。先前的修复 commit 9d851afa4826 只解决了"中止循环",却没解决"中止后正确报错"。

新流程

将退出码判定改为同时考虑 failedexit_req

return failed > 0 || exit_req ? 1 : 0;

短路求值先看显式失败,再看中断标志。failed 计数器仍只统计真正跑过的测试,所以"被中断时跑过的若干测试都过"不会被记为失败,但整体 run 仍以非零退出,调用方可以可靠地感知异常。

关键实现

patch 的 hunk 只有一行,作用于 main() 末尾的返回值表达式;不改动信号处理、不改动测试循环、不改动输出。

-       return failed > 0 ? 1 : 0;
+       return failed > 0 || exit_req ? 1 : 0;

关键观察:

  • exit_req 由先前的 commit 9d851afa4826 ("selftests/sched_ext: Abort test loop on signal") 引入,信号处理函数里置位,主循环里读取,无需在 main 中再次注册信号。
  • failed 计数语义保持不变:如果中断前已有测试失败,会先因 failed > 0 返回 1;只有"零失败 + 被中断"才走 exit_req 这条新分支。
  • 作者在 GDB 下用四种 case 验证:信号在"第一个测试前"送达 vs "example 通过后"送达;原 runner 返回 0,修复后返回 1;正常 -h / -l / -t example 调用仍返回 0。

状态机

runner 主循环的状态转移:

  [INIT]
     |
     v
  parse_args (-h / -l / -t)
     |
     v
  setup tests
     |
     v
  +------------------------------+
  | while (next && !exit_req)   |-- SIGINT/SIGTERM --+
  |   run test                  |                   |
  |   failed += result          |<------------------+
  +------------------------------+
     |
     v
  print summary
     |
     v
  return failed > 0 ? 1 : 0                (OLD: ignore exit_req)
  return failed > 0 || exit_req ? 1 : 0    (NEW: interrupt -> 1)

类比

把 runner 想象成一场马拉松的终点裁判:完赛证书只应该发给"跑完全程且没有退赛"的选手。

  • 旧逻辑:只要到终点时还没人摔倒,就盖章"完赛"——哪怕选手是被强制拦下来的,也算"通过"。
  • 新逻辑:必须真的跑完全程才能盖章;被拦下的即使没摔倒,也得标记 DNF。

CI 看到盖章(退出码 0)就放心,看到不盖章(退出码非 0)就知道这次跑得有问题。

Highlight:风险与注意点

  • 行为变更:依赖"Ctrl+C runner 后 exit 0"的本地脚本或流水线需要注意——但这是与 POSIX/惯例一致的方向(被信号终止应返回非零),多数 CI 反而会更喜欢。
  • failed 的语义保持不变:只有"零失败 + 被中断"才走 exit_req 新分支;已有失败仍先由 failed > 0 触发返回 1。
  • stdout 输出不区分:runner 在被中断路径下不会打印完整 summary,调用方若要进一步区分"全过被中断"和"中断后清理",得结合 stdout 最后一行而不是只靠退出码。
  • Assisted-by: LLM 标签:部分 maintainer 对 AI-assisted patch 持保留态度,可能会要求作者对修改做更详细的解释,或对变更做更彻底的人工 review。

一句话总结

selftests/sched_ext runner 现在把"被打断"也算作失败,让 CI 能从退出码可靠地察觉异常中断。