使用Linux的PRELOAD重载malloc/free的函数实现,然后可以记录下来所有的malloc/free操作。
定义PRELOAD库的源文件
$ cat mymalloc.c
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <string.h>
#include <stdarg.h>
#include <pthread.h>
static pthread_mutex_t mymem_lock = PTHREAD_MUTEX_INITIALIZER;
//typedef void *(*malloc_t)(size_t size);
//static malloc_t malloc_f = NULL;
static void * (*malloc_f)(size_t) = NULL;
static void * (*realloc_f)(void *, size_t) = NULL;
//typedef void (*free_t)(void* p);
//static free_t free_f = NULL;
static void (*free_f)(void *) = NULL;
void *malloc(size_t sz) {
pthread_mutex_lock(&mymem_lock);
if (malloc_f == NULL) {
malloc_f = dlsym(RTLD_NEXT, "malloc");
}
void * mem = malloc_f(sz);
pthread_mutex_unlock(&mymem_lock);
return mem;
}
void *realloc(void *ptr, size_t sz) {
pthread_mutex_lock(&mymem_lock);
if (realloc_f == NULL) {
realloc_f = dlsym(RTLD_NEXT, "realloc");
}
void * mem = realloc_f(ptr, sz);
pthread_mutex_unlock(&mymem_lock);
return mem;
}
void free(void *mem) {
if (mem != NULL) {
pthread_mutex_lock(&mymem_lock);
if (free_f == NULL) {
free_f = dlsym(RTLD_NEXT, "free");
}
free_f(mem);
pthread_mutex_unlock(&mymem_lock);
}
}
编译运行:
$ gcc -o libmymalloc.so -g -shared -fPIC -ldl mymalloc.c
$ LD_PRELOAD=/path/to/libmymalloc.so ./yourprogram
这样yourprogram就会使用mymalloc里面定义的malloc和free,而不会使用系统库的malloc和free。
可以增强的地方:
- 把malloc和free的请求打印出来
static void logmallocfree(const char * fmt, ...) {
va_list args;
char fmtbuffer[80] = { '\0' };
va_start(args, fmt);
vsnprintf(fmtbuffer, 80, fmt, args);
va_end(args);
char logbuffer[120] = { '\0' };
snprintf(logbuffer, 120, "ts=%d, pid=%d, %s\n", time(NULL), getpid(), fmtbuffer);
int fd = open("mymalloc.log", O_CREAT | O_WRONLY | O_APPEND, S_IRUSR | S_IWUSR);
write(fd, logbuffer, strlen(logbuffer));
close(fd);
}
void *malloc(size_t sz) {
...
void * mem = malloc_f(sz);
logmallocfree("mymalloc=%p, size=%d", mem, sz);
...
}
这里把每次请求的时间戳,进程号,以及分配的地址和大小都打印到日志文件中。
另一可以增强的地方,对于满足条件(现在只是基于大小一致)的malloc把调用栈打出来,遗憾的是找不到办法打印出栈,只能让进程sleep一段时间,然后这期间用pstack来dump出来。
static int monitoralloc() {
int sz = 0;
char buffer[80] = { '\0' };
int fd = open("mymalloc.cfg", O_RDONLY);
if (fd != -1) {
if (read(fd, buffer, sizeof(buffer)) != -1) {
sz = atoi(buffer);
}
close(fd);
}
return sz;
}
void *malloc(size_t sz) {
...
if (sz == monitoralloc()) {
logmallocfree("mymalloc sleep when size=%d", sz);
sleep(10);
}
...
}
monitoralloc从配置文件读取一个数字,表示要monitor如果请求大小是这个值,那么就sleep(10),并打印出一句提示,当用户看到提示出来后,就调用pstack。
另外这里要注意的是,因为重载了malloc/free,所以在mymalloc.c里面就不能再直接或者间接调用malloc/free了,所以想访问文件的很多libc函数都不能用了,必须用最基础的系统IO函数,而不是libc的函数来访问文件,因为libc的函数也会使用malloc/free倒是嵌套循环。
网友评论