c++ - 查找僵尸线程的来源

标签 c++ linux multithreading gdb pthreads

我有一个应用程序,我很确定它会因为忘记调用 pthread_join 而“泄漏”线程。所以他们的堆栈没有被清理,随着时间的推移,这个过程消耗了大量的虚拟地址空间。

有没有办法在软件中找到创建这些线程的位置,或者至少找出这些线程在退出前在做什么?

我的应用程序很大,创建了很多正确连接的线程。所以捕获所有 pthread 操作是不切实际的。我需要更精确的东西。

我能够想出一个我认为正在发生的事情的独立复制器。

#include <pthread.h>
#include <unistd.h>

void* worker (void* unusued)
{
    // Do nothing
}

int main()
{
    pthread_t thread_id;

    for(int i=0; i < 2000; i++)
    {
            pthread_create(&thread_id, NULL, &worker, NULL);
    }
    sleep(1000);
    return 0;
}

运行后'top'显示消耗了16GB的虚拟地址空间

enter image description here

但是 'ps' 和 'gdb' 只显示一个线程

enter image description here

enter image description here

我的应用程序中的所有内容都有源代码。所以我可以添加任何代码或其他需要的工具。

换句话说,上述应用程序的运行实例如何发现它有 2000 个“丢失”线程以及如何发现它们执行了 worker() 函数?

最佳答案

好问题。一种可能的答案是使用 libpthread 插入器。参见 this article .

让我们让你的测试程序更有趣一点,所以它只“泄漏”几个线程,并加入其中的大部分:

#include <pthread.h>
#include <unistd.h>

void* worker(void* unusued)
{
  // Do nothing
}

int main()
{
  pthread_t thread_id;

  for (int i = 0; i < 10; i++) {
    pthread_create(&thread_id, NULL, &worker, (void*)i);
    if (i != 4 && i != 7) pthread_join(thread_id, NULL);
  }
  sleep(1000);
  return 0;
}

现在让我们为 pthread_createpthread_join 构建一个中介层:

#include <assert.h>
#include <dlfcn.h>
#include <pthread.h>
#include <map>

static pthread_mutex_t mtx;
typedef std::pair<void *, void *> elem_t;
typedef std::map<pthread_t, elem_t> map_t;
static map_t thr_map;

extern "C"
int pthread_create(pthread_t *tid, const pthread_attr_t *attr,
                   void *(*start_routine)(void*), void *arg)
{
  static __decltype(pthread_create) *real
    = reinterpret_cast<__decltype(pthread_create) *>(dlsym(RTLD_NEXT,
                                                           "pthread_create"));
  int rc = (*real)(tid, attr, start_routine, arg);
  if (rc == 0) {
    pthread_mutex_lock(&mtx);
    thr_map[*tid] = std::make_pair((void*)start_routine, arg);
    pthread_mutex_unlock(&mtx);
  }
  return rc;
}

extern "C"
int pthread_join(pthread_t tid, void **arg)
{
  static __decltype(pthread_join) *real
    = reinterpret_cast<__decltype(pthread_join) *>(dlsym(RTLD_NEXT,
                                                         "pthread_join"));
  int rc = (*real)(tid, arg);
  if (rc == 0) {
    pthread_mutex_lock(&mtx);
    const auto it = thr_map.find(tid);
    assert(it != thr_map.end());
    thr_map.erase(it);
    pthread_mutex_unlock(&mtx);
  }
  return rc;
}

构建它:g++ -g -fPIC -shared -o thr.so thr.cc -ldl -std=c++11 并使用它:

LD_PRELOAD=./thr.so ./a.out &
[1] 37057

gdb -q -p 37057

Attaching to process 37057
Reading symbols from /tmp/a.out...done.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib64/libthread_db.so.1".
0x00007f95831a2f3d in nanosleep () at ../sysdeps/unix/syscall-template.S:81
81  ../sysdeps/unix/syscall-template.S: No such file or directory.

(gdb) set print pretty
(gdb) p thr_map
$1 = std::map with 2 elements = {
  [140280106567424] = {
    first = 0x40069d <worker(void*)>,
    second = 0x7
  },
  [140280114960128] = {
    first = 0x40069d <worker(void*)>,
    second = 0x4
  }
}

瞧:您现在知道哪些线程没有被加入,它们调用了哪些例程,以及给它们的参数是什么。

编辑

My application is linked statically

在那种情况下,链接器 --wrap=pthread_create--wrap=pthread_join 是你的 friend 。文档 here .

关于c++ - 查找僵尸线程的来源,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31765867/

相关文章:

c++ - 如果属性匹配,则 TinyXML2 查询文本

c++ - chrono::high_resolution_clock 延迟不一致

c++ - 客户端服务器编程 - 写入缓冲区

linux - grep 不显示从文件中读取 id 的结果

linux - Git 自动补全看起来很奇怪(而且只有一半功能)

c - C 中带有管道的互斥体

c++ - 类的奇怪问题

linux - Systemtap 不显示内核函数中的所有局部变量

java - 用于线程正确性的断言或注释

c - 线程内的 while 循环导致程序阻塞 (C)