pthread_key泄露问题检测

pthread_key泄露问题检测

问题描述

进程启动后业务处理异常,经过调试发现是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变量存放的区域。在这里可以找到很多其他使用线程变量的析构函数。幸运的是,确实有很多变量使用了相同的析构函数。
    coredump 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<typename T>
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
fullcore        true                                 Enable full CORE dump                             True
  • 开启fullcore模式
chdev -l sys0 -a fullcore=true 
  • 手动coredump进程
gencore ProcessID FileName
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值