118 lines
3.9 KiB
C
118 lines
3.9 KiB
C
#define _GNU_SOURCE
|
|
#include <errno.h>
|
|
#include <stdint.h>
|
|
#include <pthread.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
/* glibc 验收探针,不进入运行库;插桩耗时与 RSS 不用于性能比较。
|
|
* 只跟踪这些入口实际返回的请求字节,忽略 loader 内部产生的未知指针。
|
|
* 固定表不调用分配器,表满直接使验收失败,不能悄悄漏记。 */
|
|
extern void *__libc_malloc(size_t size);
|
|
extern void *__libc_calloc(size_t count, size_t size);
|
|
extern void *__libc_realloc(void *pointer, size_t size);
|
|
extern void __libc_free(void *pointer);
|
|
extern void *__libc_memalign(size_t alignment, size_t size);
|
|
|
|
#define CAPACITY 131072
|
|
#define DELETED ((uintptr_t)1)
|
|
|
|
static struct { uintptr_t address; size_t size; } entries[CAPACITY];
|
|
static size_t allocations, releases, allocated_bytes, released_bytes, live_bytes, peak_bytes;
|
|
static int recording = 1;
|
|
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
static size_t slot(uintptr_t address, int insert) {
|
|
size_t first_deleted = CAPACITY;
|
|
size_t start = (address >> 4) % CAPACITY;
|
|
for (size_t step = 0; step < CAPACITY; step++) {
|
|
size_t index = (start + step) % CAPACITY;
|
|
if (entries[index].address == address) return index;
|
|
if (entries[index].address == DELETED && first_deleted == CAPACITY) first_deleted = index;
|
|
if (!entries[index].address) return insert && first_deleted != CAPACITY ? first_deleted : index;
|
|
}
|
|
if (insert && first_deleted != CAPACITY) return first_deleted;
|
|
abort();
|
|
}
|
|
|
|
static void added(void *pointer, size_t size) {
|
|
if (!recording || !pointer) return;
|
|
uintptr_t address = (uintptr_t)pointer;
|
|
size_t index = slot(address, 1);
|
|
if (entries[index].address == address) abort();
|
|
entries[index].address = address;
|
|
entries[index].size = size;
|
|
allocations++;
|
|
allocated_bytes += size;
|
|
live_bytes += size;
|
|
if (live_bytes > peak_bytes) peak_bytes = live_bytes;
|
|
}
|
|
|
|
static void removed(uintptr_t address) {
|
|
if (!recording || !address) return;
|
|
size_t index = slot(address, 0);
|
|
if (entries[index].address != address) return;
|
|
releases++;
|
|
released_bytes += entries[index].size;
|
|
live_bytes -= entries[index].size;
|
|
entries[index].address = DELETED;
|
|
}
|
|
|
|
void *malloc(size_t size) {
|
|
pthread_mutex_lock(&lock);
|
|
void *pointer = __libc_malloc(size);
|
|
added(pointer, size);
|
|
pthread_mutex_unlock(&lock);
|
|
return pointer;
|
|
}
|
|
|
|
void *calloc(size_t count, size_t size) {
|
|
pthread_mutex_lock(&lock);
|
|
void *pointer = __libc_calloc(count, size);
|
|
if (pointer) added(pointer, count * size);
|
|
pthread_mutex_unlock(&lock);
|
|
return pointer;
|
|
}
|
|
|
|
void *realloc(void *pointer, size_t size) {
|
|
pthread_mutex_lock(&lock);
|
|
uintptr_t address = (uintptr_t)pointer;
|
|
void *result = __libc_realloc(pointer, size);
|
|
/* 失败时旧块仍存活;glibc 的零大小 realloc 则释放旧块。 */
|
|
if (result || size == 0) {
|
|
removed(address);
|
|
added(result, size);
|
|
}
|
|
pthread_mutex_unlock(&lock);
|
|
return result;
|
|
}
|
|
|
|
int posix_memalign(void **result, size_t alignment, size_t size) {
|
|
if (alignment < sizeof(void *) || (alignment & (alignment - 1))) return EINVAL;
|
|
int previous_errno = errno;
|
|
pthread_mutex_lock(&lock);
|
|
void *pointer = __libc_memalign(alignment, size);
|
|
if (pointer) {
|
|
*result = pointer;
|
|
added(pointer, size);
|
|
}
|
|
pthread_mutex_unlock(&lock);
|
|
errno = previous_errno;
|
|
return pointer ? 0 : ENOMEM;
|
|
}
|
|
|
|
void free(void *pointer) {
|
|
pthread_mutex_lock(&lock);
|
|
removed((uintptr_t)pointer);
|
|
__libc_free(pointer);
|
|
pthread_mutex_unlock(&lock);
|
|
}
|
|
|
|
__attribute__((destructor)) static void report(void) {
|
|
pthread_mutex_lock(&lock);
|
|
recording = 0;
|
|
pthread_mutex_unlock(&lock);
|
|
fprintf(stderr, "heap_stats,%zu,%zu,%zu,%zu,%zu,%zu\n",
|
|
allocations, releases, allocated_bytes, released_bytes, live_bytes, peak_bytes);
|
|
}
|