最近在一些奇怪的地方使用 pytorch. 那里的 python 十分的鬼畜, 以至于 python-debug 用不了. c 的栈打出来大概是这样的.

#110 0x00004ffff061c03c in _PyEval_EvalFrameDefault (f=<optimized out>, throwflag=<optimized out>) at ../Python-3.6.8/Python/ceval.c:3351                                                   
#111 0x00004ffff061865c in PyEval_EvalFrameEx (f=0x6, throwflag=8387296) at ../Python-3.6.8/Python/ceval.c:754                                                                              
#112 0x00004ffff06192fc in _PyEval_EvalCodeWithName (_co=0x58000044c980, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=0, kwnames=<optimized out>, kwargs=0x58000d8621b8, kwcount=2, kwstep=1, defs=0x580000449340, defcount=2, kwdefs=0x0, closure=0x0, name=0x5800003431c0, qualname=0x5800003431c0) at ../Python-3.6.8/Python/ceval.c:4166
#113 0x00004ffff0619768 in fast_function (kwnames=<optimized out>, nargs=0, stack=0x58000d8621b8, func=0x58000296f6b8) at ../Python-3.6.8/Python/ceval.c:4992                               
#114 call_function (pp_stack=0x500000804400, oparg=<optimized out>, kwnames=<optimized out>) at ../Python-3.6.8/Python/ceval.c:4872   

众所周知, python-gdb 用了 _PyEval_EvalFrameDefault 里的 f 变量来查当前 frame, 如果 optimized out 了的话就用它上一层的 f. 然而这里上一层的 f 指针莫名其妙地变成了 0x6, 于是自然读不出来, 于是就没法知道执行到哪里了.

然后 laekov 就灵机一动发现 _PyEval_EvalCodeWithName 里面的 _co 是不是可以用啊. 然后看了一通代码发现 _co 是一个 PyCodeObject, 里面确实有文件名和行号之类的信息, 于是可以用来进行一些 back trace, 只是缺了关键的 “哪一行在调用” 的信息 qwq. 仔细观察了一通代码之后发现 f_lineno 是在 EvalFrameDefault 里面动的, 所以没有 f对象的位置的话, 就找不到它了捏, 呜呜呜.

但总之一通乱改之后它能打出一个能进行观察的栈了. 代码被放到了 github

总结一下这个事情其实是在逆向别人不知道怎么过的一个 cpython interpreter 呢 >_> 还不如把源码给窝呢草