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:
2023-02-18 21:00:39 +00:00
parent 53cb80ebf7
commit 88c7d91858
8 changed files with 347 additions and 66 deletions

211
src/mem.c
View File

@ -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);
}
}