我想要检测一个大型 Python 项目,以便能够调试如下所示的生产问题:
23321 07:49:57.925037 futex(0x23b2c20, FUTEX_WAIT_PRIVATE, 0, NULL <unfinished ...>
23321 07:50:12.435793 <... futex resumed> ) = 0
这里,一个线程在某个锁(或条件变量)上等待了 14.4 秒。
我不确定该锁是在 Python 代码中还是在某些第 3 方 C 扩展中创建的,但从统计角度来看,Python 是一个不错的猜测。
当我附加
strace
时,这个futex已经创建了。我怀疑它是在应用程序启动期间创建的。
我想戳
threading.Lock()
和 threading.Condition()
对象并找出它们底层的 futex id。
至少我会记录这些 id,以便稍后,如果我必须跟踪正在运行的应用程序,我可以将我看到的 futex 调用解析为合乎逻辑的内容。
你可以通过rr记录程序执行情况,然后你可以像这样设置条件断点:
b futex if $rdi==0x23b2c20
我成功地为 Python2 破解了一些东西,这是一个演示:
$ strace -T -e signal=none -e futex python2 test.py
futex(0x7f6da47be0a8, FUTEX_WAKE_PRIVATE, 2147483647) = 0 <0.000006>
('futex address', '0x55de8d1105b0')
futex(0x55de8d123a30, FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, 0, NULL, ffffffff) = 0 <0.000038>
futex(0x55de8d074bf0, FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, 0, NULL, ffffffff) = 0 <0.000032>
futex(0x55de8d074bf0, FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, 0, NULL, ffffffff) = 0 <0.000036>
before
futex(0x55de8d1105b0, FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, 0, NULL, ffffffff) = 0 <5.017658>
inside
+++ exited with 0 +++
请注意,Python 和
strace
报告相同的 futex 地址,0x55de8d1105b0
代码:
import threading
import time
import sys
import ctypes
l = threading.Lock()
if sys.getsizeof(l) == 48:
OFFSET = 4 # debug build: next, prev, refcnt, type, payload(lock_lock, ...)
elif sys.getsizeof(l) == 32:
OFFSET = 2 # normal build: refcnt, type, payload(lock_lock, ...)
else:
assert 0, "Don't do this to me"
lp = ctypes.cast(id(l), ctypes.POINTER(ctypes.POINTER(ctypes.c_ubyte)))[OFFSET]
print("futex address", hex(ctypes.addressof(lp.contents)))
class holder(threading.Thread):
def run(self):
with l:
time.sleep(5)
holder().start()
print("before")
with l:
print("inside")