Brute force memory tracking.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4186 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
211
src/mem.c
211
src/mem.c
@ -1,27 +1,213 @@
|
||||
#include "mem.h"
|
||||
|
||||
#include "util.js.h"
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
#include <openssl/crypto.h>
|
||||
|
||||
#include <quickjs.h>
|
||||
#include <sqlite3.h>
|
||||
#include <uv.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
static uv_mutex_t s_tracking_mutex;
|
||||
static bool s_mem_tracking;
|
||||
static void** s_mem_tracked;
|
||||
static int s_mem_tracked_count;
|
||||
static int s_mem_tracked_capacity;
|
||||
static int64_t s_tf_malloc_size;
|
||||
static int64_t s_uv_malloc_size;
|
||||
static int64_t s_tls_malloc_size;
|
||||
static int64_t s_js_malloc_size;
|
||||
static int64_t s_sqlite_malloc_size;
|
||||
|
||||
extern uint32_t fnv32a(const void* buffer, int length, uint32_t start);
|
||||
|
||||
void tf_mem_startup(bool tracking)
|
||||
{
|
||||
s_mem_tracking = tracking;
|
||||
uv_mutex_init(&s_tracking_mutex);
|
||||
}
|
||||
|
||||
void tf_mem_shutdown()
|
||||
{
|
||||
s_mem_tracking = false;
|
||||
free(s_mem_tracked);
|
||||
s_mem_tracked = NULL;
|
||||
s_mem_tracked_capacity = 0;
|
||||
uv_mutex_destroy(&s_tracking_mutex);
|
||||
}
|
||||
|
||||
static void _tf_mem_add_tracked_allocation(void* ptr)
|
||||
{
|
||||
if (s_mem_tracking)
|
||||
{
|
||||
uv_mutex_lock(&s_tracking_mutex);
|
||||
if (s_mem_tracked_count + 1 >= s_mem_tracked_capacity)
|
||||
{
|
||||
s_mem_tracked_capacity = s_mem_tracked_capacity ? (s_mem_tracked_capacity * 2) : 256;
|
||||
s_mem_tracked = realloc(s_mem_tracked, sizeof(void*) * s_mem_tracked_capacity);
|
||||
}
|
||||
s_mem_tracked[s_mem_tracked_count++] = ptr;
|
||||
uv_mutex_unlock(&s_tracking_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static void _tf_mem_remove_tracked_allocation(void* ptr)
|
||||
{
|
||||
if (s_mem_tracking)
|
||||
{
|
||||
uv_mutex_lock(&s_tracking_mutex);
|
||||
for (int i = 0; i < s_mem_tracked_count; i++)
|
||||
{
|
||||
if (s_mem_tracked[i] == ptr)
|
||||
{
|
||||
s_mem_tracked[i] = s_mem_tracked[--s_mem_tracked_count];
|
||||
break;
|
||||
}
|
||||
}
|
||||
uv_mutex_unlock(&s_tracking_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
void tf_mem_walk_allocations(void (*callback)(void* ptr, size_t size, int frames_count, void* const* frames, void* user_data), void* user_data)
|
||||
{
|
||||
uv_mutex_lock(&s_tracking_mutex);
|
||||
for (int i = 0; i < s_mem_tracked_count; i++)
|
||||
{
|
||||
size_t size = 0;
|
||||
int frames_count = 0;
|
||||
void* frames[32];
|
||||
memcpy(&size, s_mem_tracked[i], sizeof(size));
|
||||
if (s_mem_tracking)
|
||||
{
|
||||
memcpy(&frames_count, (void*)((intptr_t)s_mem_tracked[i] + sizeof(size_t) + size), sizeof(frames_count));
|
||||
if (frames_count)
|
||||
{
|
||||
memcpy(frames, (void*)((intptr_t)s_mem_tracked[i] + sizeof(size_t) + size + sizeof(frames_count)), sizeof(void*) * frames_count);
|
||||
}
|
||||
}
|
||||
callback(
|
||||
(void*)((intptr_t)s_mem_tracked[i] + sizeof(size_t)),
|
||||
size,
|
||||
frames_count,
|
||||
frames_count ? frames : NULL,
|
||||
user_data);
|
||||
}
|
||||
uv_mutex_unlock(&s_tracking_mutex);
|
||||
}
|
||||
|
||||
typedef struct _summary_t
|
||||
{
|
||||
tf_mem_allocation_t* allocations;
|
||||
int count;
|
||||
int capacity;
|
||||
} summary_t;
|
||||
|
||||
static int _tf_mem_hash_stack_compare(const void* a, const void* b)
|
||||
{
|
||||
const tf_mem_allocation_t* aa = a;
|
||||
const tf_mem_allocation_t* ab = b;
|
||||
if (aa->stack_hash != ab->stack_hash)
|
||||
{
|
||||
return aa->stack_hash < ab->stack_hash ? -1 : 1;
|
||||
}
|
||||
if (aa->frames_count != ab->frames_count)
|
||||
{
|
||||
return aa->frames_count < ab->frames_count ? -1 : 1;
|
||||
}
|
||||
return memcmp(aa->frames, ab->frames, sizeof(void*) * aa->frames_count);
|
||||
}
|
||||
|
||||
static int _tf_mem_size_compare(const void* a, const void* b)
|
||||
{
|
||||
const tf_mem_allocation_t* aa = a;
|
||||
const tf_mem_allocation_t* ab = b;
|
||||
if (aa->size > ab->size)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if (ab->size > aa->size)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _tf_mem_summarize(void* ptr, size_t size, int frames_count, void* const* frames, void* user_data)
|
||||
{
|
||||
summary_t* summary = user_data;
|
||||
tf_mem_allocation_t allocation =
|
||||
{
|
||||
.stack_hash = fnv32a(frames, sizeof(void*) * frames_count, 0),
|
||||
.count = 1,
|
||||
.size = size,
|
||||
.frames_count = frames_count,
|
||||
};
|
||||
memcpy(allocation.frames, frames, sizeof(void*) * frames_count);
|
||||
|
||||
int index = tf_util_insert_index(&allocation, summary->allocations, summary->count, sizeof(tf_mem_allocation_t), _tf_mem_hash_stack_compare);
|
||||
if (index < summary->count &&
|
||||
allocation.stack_hash == summary->allocations[index].stack_hash &&
|
||||
allocation.frames_count == summary->allocations[index].frames_count &&
|
||||
memcmp(frames, summary->allocations[index].frames, sizeof(void*) * frames_count) == 0)
|
||||
{
|
||||
summary->allocations[index].count++;
|
||||
summary->allocations[index].size += size;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (summary->count + 1 >= summary->capacity)
|
||||
{
|
||||
summary->capacity = summary->capacity ? summary->capacity * 2 : 256;
|
||||
summary->allocations = realloc(summary->allocations, sizeof(tf_mem_allocation_t) * summary->capacity);
|
||||
}
|
||||
if (index < summary->count)
|
||||
{
|
||||
memmove(summary->allocations + index + 1, summary->allocations + index, sizeof(tf_mem_allocation_t) * (summary->count - index));
|
||||
}
|
||||
summary->allocations[index] = allocation;
|
||||
summary->count++;
|
||||
}
|
||||
}
|
||||
|
||||
tf_mem_allocation_t* tf_mem_summarize_allocations(int* out_count)
|
||||
{
|
||||
summary_t summary = { 0 };
|
||||
tf_mem_walk_allocations(_tf_mem_summarize, &summary);
|
||||
qsort(summary.allocations, summary.count, sizeof(tf_mem_allocation_t), _tf_mem_size_compare);
|
||||
*out_count = summary.count;
|
||||
tf_mem_allocation_t* result = tf_malloc(sizeof(tf_mem_allocation_t) * summary.count);
|
||||
memcpy(result, summary.allocations, sizeof(tf_mem_allocation_t) * summary.count);
|
||||
free(summary.allocations);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void* _tf_alloc(int64_t* total, size_t size)
|
||||
{
|
||||
void* ptr = malloc(size + sizeof(size_t));
|
||||
size_t overhead = sizeof(size_t);
|
||||
void* buffer[32];
|
||||
int count = 0;
|
||||
if (s_mem_tracking)
|
||||
{
|
||||
count = tf_util_backtrace(buffer, sizeof(buffer) / sizeof(*buffer));
|
||||
overhead += sizeof(count) + sizeof(void*) * count;
|
||||
}
|
||||
|
||||
void* ptr = malloc(size + overhead);
|
||||
if (ptr)
|
||||
{
|
||||
__atomic_add_fetch(total, size, __ATOMIC_RELAXED);
|
||||
memcpy(ptr, &size, sizeof(size_t));
|
||||
if (count)
|
||||
{
|
||||
memcpy((void*)((intptr_t)ptr + sizeof(size_t) + size), &count, sizeof(count));
|
||||
memcpy((void*)((intptr_t)ptr + sizeof(size_t) + size + sizeof(count)), buffer, sizeof(void*) * count);
|
||||
}
|
||||
_tf_mem_add_tracked_allocation(ptr);
|
||||
return (void*)((intptr_t)ptr + sizeof(size_t));
|
||||
}
|
||||
else
|
||||
@ -32,6 +218,15 @@ static void* _tf_alloc(int64_t* total, size_t size)
|
||||
|
||||
static void* _tf_realloc(int64_t* total, void* ptr, size_t size)
|
||||
{
|
||||
void* buffer[32];
|
||||
int count = 0;
|
||||
size_t overhead = sizeof(size_t);
|
||||
if (s_mem_tracking)
|
||||
{
|
||||
count = tf_util_backtrace(buffer, sizeof(buffer) / sizeof(*buffer));
|
||||
overhead += sizeof(count) + sizeof(void*) * count;
|
||||
}
|
||||
|
||||
void* old_ptr = ptr ? (void*)((intptr_t)ptr - sizeof(size_t)) : NULL;
|
||||
size_t old_size = 0;
|
||||
if (old_ptr)
|
||||
@ -41,16 +236,27 @@ static void* _tf_realloc(int64_t* total, void* ptr, size_t size)
|
||||
void* new_ptr = NULL;
|
||||
if (old_ptr && !size)
|
||||
{
|
||||
_tf_mem_remove_tracked_allocation(old_ptr);
|
||||
free(old_ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
new_ptr = realloc(old_ptr, size + sizeof(size_t));
|
||||
if (old_ptr)
|
||||
{
|
||||
_tf_mem_remove_tracked_allocation(old_ptr);
|
||||
}
|
||||
new_ptr = realloc(old_ptr, size + overhead);
|
||||
}
|
||||
if (new_ptr)
|
||||
{
|
||||
__atomic_add_fetch(total, (int64_t)size - (int64_t)old_size, __ATOMIC_RELAXED);
|
||||
memcpy(new_ptr, &size, sizeof(size_t));
|
||||
if (count)
|
||||
{
|
||||
memcpy((void*)((intptr_t)new_ptr + sizeof(size_t) + size), &count, sizeof(count));
|
||||
memcpy((void*)((intptr_t)new_ptr + sizeof(size_t) + size + sizeof(count)), buffer, sizeof(void*) * count);
|
||||
}
|
||||
_tf_mem_add_tracked_allocation(new_ptr);
|
||||
return (void*)((intptr_t)new_ptr + sizeof(size_t));
|
||||
}
|
||||
else
|
||||
@ -68,6 +274,7 @@ static void _tf_free(int64_t* total, void* ptr)
|
||||
size_t size = 0;
|
||||
memcpy(&size, old_ptr, sizeof(size_t));
|
||||
__atomic_sub_fetch(total, size, __ATOMIC_RELAXED);
|
||||
_tf_mem_remove_tracked_allocation(old_ptr);
|
||||
free(old_ptr);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user