pthread_key泄露问题检测

作者: 羽飞 2017-01-30 09:52:53

问题描述


进程启动后业务处理异常,经过调试发现是pthread_key_create返回失败。pthread_key_create创建一个TLS变量,因为用的少,不会占用其它系统资源,并且创建失败后,进程肯定不能正常运行,就像内存申请失败,也无需多做处理,所以就没有捕获异常,只是调试的时候才发现。


在Linux系统上,TLS默认支持1024个,这个数量已经相当大,我们在Linux上面也没有失败,因此最初的测试也没有发现这个问题。


但是在IBM的AIX系统上面,这个值默认是450,而且不是系统参数,不能调整大小。这样我们就只能查出来到底是哪里用了这么多的pthread_key。


 


查找原因


pthread_key_create实现方法


众所周知,TLS在Linux系统上的实现,是用类似于数组的方法,用户申请时,找到一个空闲位置,记录用户指定的析构函数,并将此位置标记。AIX系统的做法是类似的,这可以通过反汇编pthread_key_create验证。


了解了pthread_key_create的实现方式,那就很容易查问题了。


找出使用pthread_key_create的模块


1.找到一个TLS变量析构函数的函数地址


对于我们自己的模块,可以通过dbx(AIX上类似于gdb的调试工具)attach到进程,找到我们自己的析构函数(我们自己调用的pthread_key_create并不是全部失败的),记录下函数地址。


2.查找TLS


AIX下可以使用gencore pid core-file来生成一个core文件。用16进制工具打开core文件,根据记录的函数地址查找,找到TLS变量存放的区域。在这里可以找到很多其他使用线程变量的析构函数。幸运的是,确实有很多变量使用了相同的析构函数。




3.查找泄露的模块


很幸运的,我们找到了泄露TLS变量的析构函数,准确的说应该是存放析构函数地址的地址值。拿到了这个地址值,就可以用dbx找到析构函数的地址。


(dbx) p *(long long *)0x09001000a8114840


648518347166419584


转换成16进制就是0x09000000312D8A80,抱歉太傻,当时没有直接打印16进制数据。不过我们还是拿到了函数的地址,现在只需要找到这个地址是哪个模块的就可以了。dbx提供了一个很强大的命令map,他可以根据指定的地址,找出来所属的模块,代码段信息和数据段信息。


(dbx) map verbose 0x9000000312D8A80


Entry 66:


Object name: /data01/usergrp/billmdb/lib/libpublic_common_baseD.so


Text origin:     0x900000031004000


Text end:        0x90000003136ea69


Text length:     0x36aa69


Data origin:     0x9001000a80deb38


Data end:        0x9001000a8199300


Data length:     0xba7c8


File descriptor: 0x84


这样就一目了然了,我们在public_common_base模块的代码中搜索了一下pthread_key_create,找到了下面的代码:


template


class CPThreadKeyHolder {


public:


//default construtor


CPThreadKeyHolder() : m_pthread_key_t() 


{


pthread_key_create(&m_pthread_key_t,DestructThreadObject);


};


// ...


};


罪魁祸首原来就在这里。在模板类中使用pthread_key_create,那么每个使用这个模板的类都会创建一个甚至多个(内联会自动展开)TLS变量。我们在core文件中打印了一下DestructThreadObject函数的地址,也确实对应了上面在core中找到的地址。


问题已经找到了,至于解决方案,方法很多,最简单的就是让所有的模板类共用同一个TLS变量。


 


TIPS


AIX默认使用partialcore模式,仅仅dump进程的出现问题的线程堆栈信息。但是查问题时很多时候需要查看全局变量或者TLS变量,因此需要开启fullcore模式。AIX还支持fullcore时不dump共享内存,具体的方法可以man core。


查看是否fullcore开启


root%lsattr -El sys0 | grep fullcore


fullcoretrue Enable full CORE dump True


开启fullcore模式


chdev -l sys0 -a fullcore=true 


手动coredump进程


gencore ProcessID FileName


 


本文永久更新地址:http://www.linuxdiyf.com/linux/28043.html

相关资讯