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:
parent
a6a6fe75ec
commit
86bc46a11e
139
src/mem.c
139
src/mem.c
@ -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 (count)
|
if (s_mem_tracking)
|
||||||
{
|
{
|
||||||
memcpy((void*)((intptr_t)ptr + sizeof(size_t) + size), &count, sizeof(count));
|
tf_mem_node_t* node = (tf_mem_node_t*)((intptr_t)ptr + sizeof(size_t) + rounded_up_size);
|
||||||
memcpy((void*)((intptr_t)ptr + sizeof(size_t) + size + sizeof(count)), buffer, sizeof(void*) * count);
|
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);
|
||||||
}
|
}
|
||||||
_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 (count)
|
if (s_mem_tracking)
|
||||||
{
|
{
|
||||||
memcpy((void*)((intptr_t)new_ptr + sizeof(size_t) + size), &count, sizeof(count));
|
tf_mem_node_t* node = (tf_mem_node_t*)((intptr_t)new_ptr + sizeof(size_t) + rounded_up_size);
|
||||||
memcpy((void*)((intptr_t)new_ptr + sizeof(size_t) + size + sizeof(count)), buffer, sizeof(void*) * count);
|
memcpy(node, &(tf_mem_node_t) { .ptr = new_ptr, .frames_count = count }, sizeof(tf_mem_node_t));
|
||||||
|
if (count)
|
||||||
|
{
|
||||||
|
memcpy(node->frames, 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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user