Skip to content
yihong0618's Blog
Go back

一次艰难的 cpython issue 排查过程,以及我学到了什么

Updated:

Originally published as GitHub issue #325.

引子

最近依然在尝试做一些大模型目前做不到的事儿,去满足一些存在感。但是 TiDB 的 issues 没有以前多了,恰好某一天 @kemingy 发现 cpython 源码里有些写的不好的地方,我想 cpython 也许也可以?但我万万没想到一个 patch 进入到 cpython 是多么的难,我已经前前后后修了 4 个 cpython 的 bug 但是每个都在过程中,过程中学到了不少东西,但更开心的是认识到了目前即使最强的大模型的局限。

bug ?

尝试的过程

下面是我尝试的过程,如果能同样帮到喜欢 bug 的朋友就更好了

  1. 当然是尝试复现,3.13 在 repl 使用 issue 中的语句 hang, 3.14 不 hang 住,是 3.13 only 的问题
  2. Claude Code 启动,先让大模型帮我定位下,大模型前前后后走了几圈,都是错的,而且大模型的思路有问题,它认为 3.14 修了就是有的 commit 修了,再不停的查 commit
  3. 自己来吧,首先先缩小范围
    • 在 repl 里必复现
    • 直接使用 ./python xxx.py 没事
    • pty 没事儿
    • subprocess 没事
    • 卧槽只有 repl 里?
    • 强制使用旧 repl PYTHON_BASIC_REPL=1 ./python 也没事儿!
  4. OK 那是 new repl only, 定位好了问题

能复现就是成功的一半?

  1. no 这个 issue 并不是
  2. 这个是 c 层面的还是 python 层面的?因为只有在 new repl 里我开始以为是 python 层面的
  3. 在所有可能是 memory error 的地方接上 except: nothing work
  4. 在源码里加 print 卧槽加上 print bug 消失了,妈的
  5. 因为上条我怀疑我走错路了,大概率是 c 层面的,但是我得定位到哪一行造成 c 层面的错误
  6. 再一顿 print debug 之后发现是 console.py 里的这一行 exec(code, self.locals)
  7. OK 找到这个 bug 就可以简化为在 new repl 里执行 exec("_testcapi.set_nomemory(0)")

但是但是

  1. 我在 c 层面一路找 memroy 相关的没头绪,能改的地方都改了
  2. 过程中还熟悉了一点 cpython 的代码
  3. 但是还是不行

先放弃了,但还想着

找朋友吧

修复

diff --git a/Python/ceval.c b/Python/ceval.c
index 8c0cb29863c..3fe97a2c74c 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -912,7 +912,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
                 int frame_lasti = _PyInterpreterFrame_LASTI(frame);
                 PyObject *lasti = PyLong_FromLong(frame_lasti);
                 if (lasti == NULL) {
-                    goto exception_unwind;
+                    // If we can't allocate memory for lasti during exception handling,
+                    // this likely means we're in a severe memory shortage situation.
+                    // Instead of going back to exception_unwind (which would cause
+                    // infinite recursion), directly exit to let the original exception
+                    // propagate up and hopefully be handled at a higher level.
+                    _PyFrame_SetStackPointer(frame, stack_pointer);
+                    goto exit_unwind;
                 }
                 PUSH(lasti);
             }

修复的对么?

感想

---- update ----

https://t.me/c/1459082815/900

和 gray 学习。

以防有人没有 tg

Image Image

以及 gray 非常精彩的 pycon

GDB for CPython - PyCon 25.pdf

ppt 在附件中

Reactions

👍 30 · 🎉 1 · ❤️ 1

Comments

yuchanns

View comment · 2025-09-04T04:17:59Z

太厉害了!已经开始修 cpy 了! 所以大模型一开始的思路也没错,就是找 commit 。只是没找到🤣

baiwangao

View comment · 2025-09-04T04:24:54Z

学到很多 向大佬学习

yihong0618

View comment · 2025-09-04T04:25:43Z

太厉害了!已经开始修 cpy 了! 所以大模型一开始的思路也没错,就是找 commit 。只是没找到🤣

应该是找不到的,那个属于意外的修了

yihong0618

View comment · 2025-09-04T05:06:15Z

学到很多 向大佬学习

你可以重点看最下面的链接 gray 特别特别厉害

haowei93

View comment · 2025-09-04T07:52:13Z

很久没有在跑步的时候灵光乍现了, 哈哈哈

adam8157

View comment · 2025-09-04T08:37:50Z

git bisect也可以用new和old标记

yihong0618

View comment · 2025-09-04T08:39:28Z

git bisect也可以用new和old标记

学习了!

swif2

View comment · 2025-09-06T15:32:28Z

厉害,本码农在编程上已经停滞不前好久了😭

yihong0618

View comment · 2025-09-10T21:56:07Z

更新。修复对了,现在合并了。

hwenwur

View comment · 2025-11-26T12:40:59Z

精彩~ 总结一下,这种 hung 住的 bug,别管那么多上来先定位到卡在什么位置。

在 x86 平台, rip 寄存器记录了当前指令的地址,只需要 gdb -p $(pgrep python3),然后采样几次 info registers 基本就能确认 hung 住的位置:_PyEval_EvalFrameDefault+40180

Image

yihong0618

View comment · 2025-11-26T12:49:13Z

精彩~ 总结一下,这种 hung 住的 bug,别管那么多上来先定位到卡在什么位置。

在 x86 平台, rip 寄存器记录了当前指令的地址,只需要 gdb -p $(pgrep python3),然后采样几次 info registers 基本就能确认 hung 住的位置:_PyEval_EvalFrameDefault+40180

Image

学习了!



Previous Post
Things I don’t like
Next Post
如何判断一个项目是个好的开源项目并且想贡献?