我有一个应用程序,我敢肯定“泄漏”线程忘记调用pthread_join。 所以他们的堆栈没有被清理,随着时间的推移会消耗大量的虚拟地址空间。
有没有办法在创build这些线程的软件中find一个地方,或者至less在退出之前找出这些线程正在做什么?
我的应用程序很大,并创build了很多正确连接的线程。 所以捕捉所有的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的虚拟地址空间被占用
但'ps'和'gdb'只显示一个线程
我有我的应用程序中的一切资源。 所以我可以添加任何代码或其他所需的仪器。
换句话说,上面的应用程序正在运行的实例如何发现它有2000个“丢失”线程以及如何发现他们执行了worker()函数?
好问题。 一个可能的答案是使用libpthread
插器。 看到这篇文章 。
让我们让你的测试程序更有趣一点,所以它只“泄漏”了几个线程,并加入其中大部分:
#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_create
和pthread_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 } }
Voilà:你现在知道哪些线程没有被加入,哪些线程被调用,以及给了什么参数。
编辑
我的应用程序是静态链接的
在这种情况下,链接器--wrap=pthread_create
和--wrap=pthread_join
是你的朋友。 文档在这里 。