纪念一下跑测试跑了几天才找出的一个内存泄漏,这个函数源于 UNP,还以为 UNP 有 bug 呢,找到原书当 getaddreinfo 失败或者 res==NULL 的时候直接退出了。但是写这个代码的同学当然不想连接不上直接退出,于是忘记了 freeaddrinfo 调用直接返回,那个 struct addrinfo 就没释放。很多错误都是这种,涉及到库函数的时候更加难查。
int tcp_connect(const char host, const char serv){
int sockfd, n;
struct addrinfo hints, res, ressave;
bzero(&hints, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0)
{
log_sprintf(“tcp_connect error for %s, %s: %s”, host, serv, gai_strerror(n));
freeaddrinfo(res); //oops: memory leak
return -1;
}
ressave = res;
do {
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sockfd < 0)
continue; / ignore this one /
if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0)
break; / success /
close(sockfd); / ignore this one /
} while ( (res = res->ai_next) != NULL);
if (res == NULL) / errno set from final connect() /
{
log_sprintf(“tcp_connect error for %s, %s”, host, serv);
freeaddrinfo(ressave); //oops: memory leak
return -1;
}
freeaddrinfo(ressave);
return(sockfd);
}
上一篇博文中说到自己包装的内存检测方法,这还有个问题当时没发现,就是那个包装 malloc 之类的方法对于库函数中的内存申请调用没法记录,所以是不会发现上面这个 bug 的。这个 Memwatch 倒是把原生的 malloc 都重定义了,但是最好的 Linux 下检测内存泄漏的工具还是 valgrind,这真是个神器,在代码上不用做一点修改,这东西甚至能测试程序的 cache 命中率。看了一下 valgrind 的相关论文,对于检测方法都是一种称之为 shadow value 的方法,也就是用信息来记录每一个 byte 内存的使用情况。这种方式的一个缺点都是会拖慢速度,前面提到的那种稍微包装了一下的方式可能还好 (因为使用的是静态数组), Memwatch 里面使用了不少链表也会拖慢速度。再看看 valgrind 的实现,以后工作可能会碰上类似的。