如何在一个线程启动后为另一个线程添加systrace处理器?

4 投票
1 回答
1089 浏览
提问于 2025-04-16 21:08

我正在制作一个小型的调试工具,用于Python,这样我就可以在程序运行的中间暂停代码,或者在某个变量变化时中断程序,或者逐行执行代码等等。基本上就是类似于pdb,但可以在运行中附加调试(所以更像gdb,假设pystack不工作)。

对于单线程的程序,可以使用 sys.settrace() 来设置调试功能;而对于多线程程序,如果没有线程已经启动,可以使用 threading.settrace()。但据我所知,问题在于你只能调试整个程序(或者在特定线程的特定点进行调试)。

我在想,是否有办法在Python中对已经启动的特定线程使用类似 sys.settrace() 的功能呢?

1 个回答

4

好的,看起来这件事似乎不太可能做到。

我找到的最接近的办法是听说了线程2(可杀死的线程),它调用了 PyThreadState_SetAsyncExc,这个方法会引发一个异常,而构造函数是在另一个线程中被调用的,这样做其实没什么用。

我的解决方案是完全不使用 sys.settrace,而是直接使用 sys.setcheckinterval,把它设置为 1,当我离开调试线程时这样做,而当我进入调试线程时再把它改回一个很大的数字(可能是 sys.maxint),同时还要获取全局解释器锁(GIL)。另外,我还会查看 sys._current_frames()

这个方法几乎和 sys.settrace 一样好,可能会慢一点,但可以在任何线程上随时运行,持续时间也不受限制。

编辑

首先,我发现使用 setcheckinterval 并不足以停止其他线程,虽然在某些特定的 Python 版本中,可以通过设置全局的 _Py_Ticker(在 ceval.c 中,而不是静态全局变量,所以你可以使用 ctypes)来做到这一点,并确保在你的代码中不释放 GIL。

关于最初的问题,我查看了 CPython 的 C 代码,找到了遍历线程状态对象的方法,并调整它们以包含 tracefunc 和 traceobj;你可以在我的调试器中看到我是怎么做的,链接在这里 -- https://github.com/bobfrank/pydebug/blob/master/debug.py#L291

撰写回答