gdb - 你能找到持有内部 glibc 锁的线程吗?

问题描述 投票:0回答:1

我认为我的问题不是这个问题的重复,它描述了如何找到哪个线程拥有pthread_mutex_t。我想知道如何找到内部 glibc 锁的所有者,我认为它与 pthread_mutex_t 不同。考虑从我的应用程序中获取的回溯线程,该线程是挂起的(可能是死锁?):

Thread 1 (Thread 0x7f8478e1b700 (LWP 24662)):
#0  0x00007f847cf277fc in __lll_lock_wait_private () from /lib64/libc.so.6
#1  0x00007f847cea350c in _L_lock_5314 () from /lib64/libc.so.6
#2  0x00007f847ce9c108 in _int_free () from /lib64/libc.so.6
... <unnecessary details which lead up to this thread calling free> ...
#11 0x00007f847db18ea5 in start_thread () from /lib64/libpthread.so.0
#12 0x00007f847cf19b0d in clone () from /lib64/libc.so.6

我可以使用类似于链接问题或另一个网页的任何技巧来找出哪个线程持有内部 glibc 锁吗?

其他详细信息:

注意:这个回溯是从客户端的核心文件生成的,所以我不能简单地自己测试 gdb 命令,否则我会尝试更多地使用它。

程序中还有一堆其他线程;我不会全部复制它们,但我认为调用 fork() 的线程正在持有锁:

Thread 4 (Thread 0x7f8479724700 (LWP 24775)):
#0  0x00007f847cee0b12 in fork () from /lib64/libc.so.6
...
#6  0x00007f847db18ea5 in start_thread () from /lib64/libpthread.so.0
#7  0x00007f847cf19b0d in clone () from /lib64/libc.so.6

我怀疑 fork() 可能需要与 free() 相同的锁,但我想确定这一点,并了解有关内部 libc 状态的更多信息。

这个问题的“XY”部分(我真正想知道的)是为什么线程 4 似乎没有进展。我不知道,我正在与客户合作了解更多信息,但他们说该程序在他们注意到并杀死之前被卡在这里 3 个小时。我想把这个问题集中在用 gdb 调试内部 glibc 的东西上。

注意:我知道在多线程进程中使用 fork() 的危险:子进程在调用 exec() 之前只能调用异步信号安全函数。无论如何,我的问题与孩子无关,而是与家长有关。

c multithreading gdb glibc
1个回答
0
投票

不幸的是,你是对的,内部 glibc 锁不是

pthread_mutex_t
。它们只是 futexes,即只是整数(pthread 互斥体也是通过底层的 futexes 实现的,但包装在包含更多信息的结构中)。 glibc 内部 lowlevellock.h 顶部的
这个文档注释
解释了它们是如何更详细地实现的。由于我们正在处理原始 futexes,因此您无法使用您提到的技巧来了解所有者。

根据所使用的 futex 类型,调试当前情况可能或多或少简单。一般来说,你可以有两种 futexes:

  • 正常的futexes。这些通常被实现为小整数,它们本身不会跟踪所有者(它们可能会在其他事物的帮助下这样做,但 futex 词本身不保存所有者信息)。
  • 稳健的 futexes(请参阅稳健的 futexes 简介稳健的 futex ABI 文档)。这些确实将跟踪所有者作为一种从异常情况(例如在持有 futex 时发生崩溃)中恢复的机制。 futex 字包含所有者线程 ID(可选)与一些标志进行或运算(请参阅前面的文档链接)。它们的管理比普通 futex 的管理要复杂一些,并且还需要更多的系统调用 (
    {set,get}_robust_list
    ) 才能正常工作。

如果您正在处理强大的 futexes,您可以检查 futex 字(在 GDB 中很容易做到这一点)并恢复所有者的 TID。

现在,不幸的是(x2),在我看来,您正在处理的锁不是一个强大的 futex。特别是

__lll_lock_wait_private()
似乎并不处理 TID,而是比较/存储 futex 单词中的小常量(例如
1
2
)(必须检查您正在处理的确切 glibc 版本) ,您可以更改上一个链接中的版本,但仍然如此)。

如果我是对的,情况确实如此,我真的没有看到很多选择,我能想到的唯一几个是:

  • 使用
    strace -f -e futex
    检测
    futex
    系统调用并对输出进行推理。在每个线程中插入调试打印语句将有助于了解哪个 TID 是哪个,并且查看传递给
    futex
    的标志将有助于了解谁在等待哪个 futex。在死锁的情况下,您可能会看到线程永远等待
    futex
    suscall。
  • 编写 eBPF 过滤器来跟踪
    futex
    系统调用:参见例如本教程。与前一种情况没有太大区别,但允许您调试程序,同时还通过 eBPF 跟踪它,如果您使用
    strace
    则无法做到这一点(一个进程一次只能由一个跟踪器跟踪)。

最终我建议的是首先添加尽可能多的调试打印,同时确保问题仍然可重现,然后开始缩小范围。

© www.soinside.com 2019 - 2024. All rights reserved.