Track memory allocations with a linked list. This is only about 3x slower than without tracking instead of 5x and growing.

git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4192 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
Cory McWilliams 2023-02-19 22:28:36 +00:00
parent a6a6fe75ec
commit 86bc46a11e
2 changed files with 97 additions and 44 deletions

135
src/mem.c
View File

@ -13,11 +13,11 @@
#include <string.h> #include <string.h>
#include <stdbool.h> #include <stdbool.h>
typedef struct _tf_mem_node_t tf_mem_node_t;
static uv_mutex_t s_tracking_mutex; static uv_mutex_t s_tracking_mutex;
static bool s_mem_tracking; static bool s_mem_tracking;
static void** s_mem_tracked; static tf_mem_node_t* 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_tf_malloc_size;
static int64_t s_uv_malloc_size; static int64_t s_uv_malloc_size;
static int64_t s_tls_malloc_size; static int64_t s_tls_malloc_size;
@ -26,6 +26,11 @@ static int64_t s_sqlite_malloc_size;
extern uint32_t fnv32a(const void* buffer, int length, uint32_t start); extern uint32_t fnv32a(const void* buffer, int length, uint32_t start);
static size_t _tf_mem_round_up(size_t size)
{
return (size + 7) & ~7;
}
void tf_mem_startup(bool tracking) void tf_mem_startup(bool tracking)
{ {
s_mem_tracking = tracking; s_mem_tracking = tracking;
@ -35,39 +40,54 @@ void tf_mem_startup(bool tracking)
void tf_mem_shutdown() void tf_mem_shutdown()
{ {
s_mem_tracking = false; s_mem_tracking = false;
free(s_mem_tracked);
s_mem_tracked = NULL; s_mem_tracked = NULL;
s_mem_tracked_capacity = 0;
uv_mutex_destroy(&s_tracking_mutex); uv_mutex_destroy(&s_tracking_mutex);
} }
static void _tf_mem_add_tracked_allocation(void* ptr) typedef struct _tf_mem_node_t
{
void* ptr;
tf_mem_node_t* next;
tf_mem_node_t* previous;
int frames_count;
void* frames[];
} tf_mem_node_t;
static void _tf_mem_add_tracked_allocation(tf_mem_node_t* node)
{ {
if (s_mem_tracking) if (s_mem_tracking)
{ {
uv_mutex_lock(&s_tracking_mutex); uv_mutex_lock(&s_tracking_mutex);
if (s_mem_tracked_count + 1 >= s_mem_tracked_capacity) if (s_mem_tracked)
{ {
s_mem_tracked_capacity = s_mem_tracked_capacity ? (s_mem_tracked_capacity * 2) : 256; node->next = s_mem_tracked;
s_mem_tracked = realloc(s_mem_tracked, sizeof(void*) * s_mem_tracked_capacity); node->previous = s_mem_tracked->previous;
s_mem_tracked->previous->next = node;
s_mem_tracked->previous = node;
}
else
{
s_mem_tracked = node;
node->next = node->previous = node;
} }
s_mem_tracked[s_mem_tracked_count++] = ptr;
uv_mutex_unlock(&s_tracking_mutex); uv_mutex_unlock(&s_tracking_mutex);
} }
} }
static void _tf_mem_remove_tracked_allocation(void* ptr) static void _tf_mem_remove_tracked_allocation(tf_mem_node_t* node)
{ {
if (s_mem_tracking) if (s_mem_tracking)
{ {
uv_mutex_lock(&s_tracking_mutex); uv_mutex_lock(&s_tracking_mutex);
for (int i = 0; i < s_mem_tracked_count; i++) tf_mem_node_t* previous = node->previous;
tf_mem_node_t* next = node->next;
next->previous = previous;
previous->next = next;
node->next = NULL;
node->previous = NULL;
if (node == s_mem_tracked)
{ {
if (s_mem_tracked[i] == ptr) s_mem_tracked = next != node ? next : NULL;
{
s_mem_tracked[i] = s_mem_tracked[--s_mem_tracked_count];
break;
}
} }
uv_mutex_unlock(&s_tracking_mutex); uv_mutex_unlock(&s_tracking_mutex);
} }
@ -76,26 +96,28 @@ static void _tf_mem_remove_tracked_allocation(void* ptr)
void tf_mem_walk_allocations(void (*callback)(void* ptr, size_t size, int frames_count, void* const* frames, void* user_data), void* user_data) 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); uv_mutex_lock(&s_tracking_mutex);
for (int i = 0; i < s_mem_tracked_count; i++) for (tf_mem_node_t* node = s_mem_tracked ? s_mem_tracked->next : NULL; node; node = node->next)
{ {
size_t size = 0; size_t size = 0;
int frames_count = 0;
void* frames[32]; void* frames[32];
memcpy(&size, s_mem_tracked[i], sizeof(size)); memcpy(&size, node->ptr, sizeof(size));
if (s_mem_tracking) if (s_mem_tracking)
{ {
memcpy(&frames_count, (void*)((intptr_t)s_mem_tracked[i] + sizeof(size_t) + size), sizeof(frames_count)); if (node->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); memcpy(frames, node->frames, sizeof(void*) * node->frames_count);
} }
} }
callback( callback(
(void*)((intptr_t)s_mem_tracked[i] + sizeof(size_t)), (void*)((intptr_t)node->ptr + sizeof(size_t)),
size, size,
frames_count, node->frames_count,
frames_count ? frames : NULL, node->frames_count ? frames : NULL,
user_data); user_data);
if (node == s_mem_tracked)
{
break;
}
} }
uv_mutex_unlock(&s_tracking_mutex); uv_mutex_unlock(&s_tracking_mutex);
} }
@ -194,20 +216,25 @@ static void* _tf_alloc(int64_t* total, size_t size)
if (s_mem_tracking) if (s_mem_tracking)
{ {
count = tf_util_backtrace(buffer, sizeof(buffer) / sizeof(*buffer)); count = tf_util_backtrace(buffer, sizeof(buffer) / sizeof(*buffer));
overhead += sizeof(count) + sizeof(void*) * count; overhead += sizeof(tf_mem_node_t) + sizeof(void*) * count;
} }
void* ptr = malloc(size + overhead); size_t rounded_up_size = _tf_mem_round_up(size);
void* ptr = malloc(rounded_up_size + overhead);
if (ptr) if (ptr)
{ {
__atomic_add_fetch(total, size, __ATOMIC_RELAXED); __atomic_add_fetch(total, size, __ATOMIC_RELAXED);
memcpy(ptr, &size, sizeof(size_t)); memcpy(ptr, &size, sizeof(size_t));
if (s_mem_tracking)
{
tf_mem_node_t* node = (tf_mem_node_t*)((intptr_t)ptr + sizeof(size_t) + rounded_up_size);
memcpy(node, &(tf_mem_node_t) { .ptr = ptr, .frames_count = count }, sizeof(tf_mem_node_t));
if (count) if (count)
{ {
memcpy((void*)((intptr_t)ptr + sizeof(size_t) + size), &count, sizeof(count)); memcpy(node->frames, buffer, sizeof(void*) * count);
memcpy((void*)((intptr_t)ptr + sizeof(size_t) + size + sizeof(count)), buffer, sizeof(void*) * count); }
_tf_mem_add_tracked_allocation(node);
} }
_tf_mem_add_tracked_allocation(ptr);
return (void*)((intptr_t)ptr + sizeof(size_t)); return (void*)((intptr_t)ptr + sizeof(size_t));
} }
else else
@ -229,7 +256,7 @@ static void* _tf_realloc(int64_t* total, void* ptr, size_t size)
if (s_mem_tracking) if (s_mem_tracking)
{ {
count = tf_util_backtrace(buffer, sizeof(buffer) / sizeof(*buffer)); count = tf_util_backtrace(buffer, sizeof(buffer) / sizeof(*buffer));
overhead += sizeof(count) + sizeof(void*) * count; overhead += sizeof(tf_mem_node_t) + sizeof(void*) * count;
} }
void* old_ptr = ptr ? (void*)((intptr_t)ptr - sizeof(size_t)) : NULL; void* old_ptr = ptr ? (void*)((intptr_t)ptr - sizeof(size_t)) : NULL;
@ -239,29 +266,35 @@ static void* _tf_realloc(int64_t* total, void* ptr, size_t size)
memcpy(&old_size, old_ptr, sizeof(size_t)); memcpy(&old_size, old_ptr, sizeof(size_t));
} }
void* new_ptr = NULL; void* new_ptr = NULL;
tf_mem_node_t* node = (void*)((intptr_t)ptr + _tf_mem_round_up(old_size));
size_t rounded_up_size = _tf_mem_round_up(size);
if (old_ptr && !size) if (old_ptr && !size)
{ {
_tf_mem_remove_tracked_allocation(old_ptr); _tf_mem_remove_tracked_allocation(node);
free(old_ptr); free(old_ptr);
} }
else else
{ {
if (old_ptr) if (old_ptr)
{ {
_tf_mem_remove_tracked_allocation(old_ptr); _tf_mem_remove_tracked_allocation(node);
} }
new_ptr = realloc(old_ptr, size + overhead); new_ptr = realloc(old_ptr, rounded_up_size + overhead);
} }
if (new_ptr) if (new_ptr)
{ {
__atomic_add_fetch(total, (int64_t)size - (int64_t)old_size, __ATOMIC_RELAXED); __atomic_add_fetch(total, (int64_t)size - (int64_t)old_size, __ATOMIC_RELAXED);
memcpy(new_ptr, &size, sizeof(size_t)); memcpy(new_ptr, &size, sizeof(size_t));
if (s_mem_tracking)
{
tf_mem_node_t* node = (tf_mem_node_t*)((intptr_t)new_ptr + sizeof(size_t) + rounded_up_size);
memcpy(node, &(tf_mem_node_t) { .ptr = new_ptr, .frames_count = count }, sizeof(tf_mem_node_t));
if (count) if (count)
{ {
memcpy((void*)((intptr_t)new_ptr + sizeof(size_t) + size), &count, sizeof(count)); memcpy(node->frames, buffer, sizeof(void*) * count);
memcpy((void*)((intptr_t)new_ptr + sizeof(size_t) + size + sizeof(count)), buffer, sizeof(void*) * count); }
_tf_mem_add_tracked_allocation(node);
} }
_tf_mem_add_tracked_allocation(new_ptr);
return (void*)((intptr_t)new_ptr + sizeof(size_t)); return (void*)((intptr_t)new_ptr + sizeof(size_t));
} }
else else
@ -278,8 +311,9 @@ static void _tf_free(int64_t* total, void* ptr)
void* old_ptr = (void*)((intptr_t)ptr - sizeof(size_t)); void* old_ptr = (void*)((intptr_t)ptr - sizeof(size_t));
size_t size = 0; size_t size = 0;
memcpy(&size, old_ptr, sizeof(size_t)); memcpy(&size, old_ptr, sizeof(size_t));
tf_mem_node_t* node = (void*)((intptr_t)ptr + _tf_mem_round_up(size));
__atomic_sub_fetch(total, size, __ATOMIC_RELAXED); __atomic_sub_fetch(total, size, __ATOMIC_RELAXED);
_tf_mem_remove_tracked_allocation(old_ptr); _tf_mem_remove_tracked_allocation(node);
free(old_ptr); free(old_ptr);
} }
} }
@ -296,12 +330,31 @@ static void* _tf_uv_realloc(void* ptr, size_t size)
static void* _tf_uv_calloc(size_t nmemb, size_t size) static void* _tf_uv_calloc(size_t nmemb, size_t size)
{ {
void* ptr = calloc(1, nmemb * size + sizeof(size_t)); size_t total_size = nmemb * size;
size_t rounded_up_size = _tf_mem_round_up(total_size);
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(tf_mem_node_t) + sizeof(void*) * count;
}
void* ptr = calloc(1, nmemb * rounded_up_size + overhead);
if (ptr) if (ptr)
{ {
size_t total_size = nmemb * size;
__atomic_add_fetch(&s_uv_malloc_size, total_size, __ATOMIC_RELAXED); __atomic_add_fetch(&s_uv_malloc_size, total_size, __ATOMIC_RELAXED);
memcpy(ptr, &total_size, sizeof(size_t)); memcpy(ptr, &total_size, sizeof(size_t));
if (s_mem_tracking)
{
tf_mem_node_t* node = (tf_mem_node_t*)((intptr_t)ptr + sizeof(size_t) + rounded_up_size);
memcpy(node, &(tf_mem_node_t) { .ptr = ptr, .frames_count = count }, sizeof(tf_mem_node_t));
if (count)
{
memcpy(node->frames, buffer, sizeof(void*) * count);
}
_tf_mem_add_tracked_allocation(node);
}
return (void*)((intptr_t)ptr + sizeof(size_t)); return (void*)((intptr_t)ptr + sizeof(size_t));
} }
else else

View File

@ -851,7 +851,7 @@ static JSValue _tf_task_getAllocations(JSContext* context, JSValueConst this_val
JS_SetPropertyStr(context, allocation, "size", JS_NewInt64(context, allocation_info[i].size)); JS_SetPropertyStr(context, allocation, "size", JS_NewInt64(context, allocation_info[i].size));
JS_SetPropertyStr(context, allocation, "count", JS_NewInt32(context, allocation_info[i].count)); JS_SetPropertyStr(context, allocation, "count", JS_NewInt32(context, allocation_info[i].count));
const char* stack = tf_util_backtrace_to_string(allocation_info[i].frames, allocation_info[i].frames_count); const char* stack = tf_util_backtrace_to_string(allocation_info[i].frames, allocation_info[i].frames_count);
JS_SetPropertyStr(context, allocation, "stack", JS_NewString(context, stack)); JS_SetPropertyStr(context, allocation, "stack", JS_NewString(context, stack ? stack : ""));
tf_free((void*)stack); tf_free((void*)stack);
JS_SetPropertyUint32(context, allocations, i, allocation); JS_SetPropertyUint32(context, allocations, i, allocation);
} }