From 2992b7e9558b3d95c1de9953b04810998371ff38 Mon Sep 17 00:00:00 2001 From: Cory McWilliams Date: Sat, 4 Jun 2022 03:01:12 +0000 Subject: [PATCH] Attempting to learn about a slow memory leak. git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3887 ed5197a5-7fde-0310-b194-c3ffbd925b24 --- src/file.js.c | 8 ++++++ src/main.c | 2 ++ src/task.c | 24 ++++++++++++++++ src/util.js.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/util.js.h | 3 ++ 5 files changed, 114 insertions(+) diff --git a/src/file.js.c b/src/file.js.c index 38fb4151..eb9e7244 100644 --- a/src/file.js.c +++ b/src/file.js.c @@ -89,6 +89,7 @@ static void _file_read_read_callback(uv_fs_t* req) int result = uv_fs_close(req->loop, req, fsreq->file, _file_async_close_callback); if (result < 0) { + uv_fs_req_cleanup(req); free(fsreq); } } @@ -111,6 +112,7 @@ static void _file_read_open_callback(uv_fs_t* req) result = uv_fs_close(req->loop, req, fsreq->file, _file_async_close_callback); if (result < 0) { + uv_fs_req_cleanup(req); free(fsreq); } } @@ -118,6 +120,7 @@ static void _file_read_open_callback(uv_fs_t* req) else { tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, uv_strerror(req->result))); + uv_fs_req_cleanup(req); free(req); } } @@ -142,6 +145,7 @@ static JSValue _file_read_file(JSContext* context, JSValueConst this_val, int ar if (result < 0) { tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, uv_strerror(result))); + uv_fs_req_cleanup(&req->fs); free(req); } JS_FreeCString(context, file_name); @@ -166,6 +170,7 @@ static void _file_write_write_callback(uv_fs_t* req) int result = uv_fs_close(req->loop, req, fsreq->file, _file_async_close_callback); if (result < 0) { + uv_fs_req_cleanup(req); free(fsreq); } } @@ -188,6 +193,7 @@ static void _file_write_open_callback(uv_fs_t* req) result = uv_fs_close(req->loop, req, fsreq->file, _file_async_close_callback); if (result < 0) { + uv_fs_req_cleanup(req); free(fsreq); } } @@ -195,6 +201,7 @@ static void _file_write_open_callback(uv_fs_t* req) else { tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, uv_strerror(req->result))); + uv_fs_req_cleanup(req); free(req); } } @@ -360,6 +367,7 @@ JSValue _file_stat(JSContext* context, JSValueConst this_val, int argc, JSValueC if (result) { tf_task_reject_promise(task, promise, JS_NewInt32(context, result)); + uv_fs_req_cleanup(&data->_request); free(data); } JS_FreeCString(context, path); diff --git a/src/main.c b/src/main.c index cb4833f4..9c31620d 100644 --- a/src/main.c +++ b/src/main.c @@ -6,6 +6,7 @@ #include "task.h" #include "taskstub.js.h" #include "tests.h" +#include "util.js.h" #include #include @@ -624,6 +625,7 @@ int main(int argc, char* argv[]) #if !defined(_WIN32) prctl(PR_SET_PDEATHSIG, SIGKILL); #endif + tf_util_replace_uv_allocator(); uv_setup_args(argc, argv); tf_taskstub_startup(); diff --git a/src/task.c b/src/task.c index 86cf9b73..638a4e5e 100644 --- a/src/task.c +++ b/src/task.c @@ -93,6 +93,7 @@ typedef struct _tf_task_t promiseid_t _nextPromise; uv_loop_t _loop; + uv_timer_t gc_timer; uv_timer_t trace_timer; uint64_t last_hrtime; uint64_t last_idle_time; @@ -732,6 +733,14 @@ static JSValue _tf_task_getStats(JSContext* context, JSValueConst this_val, int } JS_SetPropertyStr(context, result, "sqlite3_memory_percent", JS_NewFloat64(context, 100.0f * sqlite3_memory_used() / total_memory)); + + JSMemoryUsage js = { 0 }; + JSRuntime* runtime = JS_GetRuntime(context); + JS_ComputeMemoryUsage(runtime, &js); + JS_SetPropertyStr(context, result, "js_malloc_percent", JS_NewFloat64(context, 100.0f * js.malloc_size / total_memory)); + + JS_SetPropertyStr(context, result, "uv_malloc_percent", JS_NewFloat64(context, 100.0f * tf_util_uv_get_malloc_size() / total_memory)); + JS_SetPropertyStr(context, result, "socket_count", JS_NewInt32(context, tf_socket_get_count())); JS_SetPropertyStr(context, result, "socket_open_count", JS_NewInt32(context, tf_socket_get_open_count())); @@ -1309,6 +1318,12 @@ static void _tf_task_promise_rejection_tracker(JSContext* context, JSValueConst } } +static void _tf_task_gc_timer(uv_timer_t* timer) +{ + tf_task_t* task = timer->data; + JS_RunGC(task->_runtime); +} + static void _tf_task_trace_timer(uv_timer_t* timer) { tf_task_t* task = timer->data; @@ -1446,6 +1461,10 @@ tf_task_t* tf_task_create() uv_timer_init(&task->_loop, &task->trace_timer); uv_timer_start(&task->trace_timer, _tf_task_trace_timer, 100, 100); uv_unref((uv_handle_t*)&task->trace_timer); + task->gc_timer.data = task; + uv_timer_init(&task->_loop, &task->gc_timer); + uv_timer_start(&task->gc_timer, _tf_task_gc_timer, 10000, 10000); + uv_unref((uv_handle_t*)&task->gc_timer); task->idle.data = task; uv_idle_init(&task->_loop, &task->idle); uv_unref((uv_handle_t*)&task->idle); @@ -1645,10 +1664,15 @@ void tf_task_destroy(tf_task_t* task) { uv_close((uv_handle_t*)&task->trace_timer, _tf_task_on_handle_close); } + if (task->gc_timer.data && !uv_is_closing((uv_handle_t*)&task->gc_timer)) + { + uv_close((uv_handle_t*)&task->gc_timer, _tf_task_on_handle_close); + } uv_close((uv_handle_t*)&task->idle, _tf_task_on_handle_close); uv_close((uv_handle_t*)&task->prepare, _tf_task_on_handle_close); while (task->trace_timer.data || + task->gc_timer.data || task->idle.data || task->prepare.data) { diff --git a/src/util.js.c b/src/util.js.c index 43ea3d75..7c12025c 100644 --- a/src/util.js.c +++ b/src/util.js.c @@ -9,6 +9,83 @@ #include +static int64_t s_uv_malloc_size; + +static void* _tf_uv_alloc(size_t size) +{ + void* ptr = malloc(size + sizeof(size_t)); + if (ptr) + { + __atomic_add_fetch(&s_uv_malloc_size, size, __ATOMIC_RELAXED); + memcpy(ptr, &size, sizeof(size_t)); + return (void*)((intptr_t)ptr + sizeof(size_t)); + } + else + { + return NULL; + } +} + +static void* _tf_uv_realloc(void* ptr, size_t size) +{ + void* old_ptr = ptr ? (void*)((intptr_t)ptr - sizeof(size_t)) : NULL; + size_t old_size = 0; + if (old_ptr) + { + memcpy(&old_size, old_ptr, sizeof(size_t)); + } + void* new_ptr = realloc(old_ptr, size + sizeof(size_t)); + if (new_ptr) + { + __atomic_add_fetch(&s_uv_malloc_size, (int64_t)size - (int64_t)old_size, __ATOMIC_RELAXED); + memcpy(new_ptr, &size, sizeof(size_t)); + return (void*)((intptr_t)new_ptr + sizeof(size_t)); + } + else + { + __atomic_sub_fetch(&s_uv_malloc_size, old_size, __ATOMIC_RELAXED); + return NULL; + } +} + +static void* _tf_uv_calloc(size_t nmemb, size_t size) +{ + void* ptr = calloc(1, nmemb * size + sizeof(size_t)); + if (ptr) + { + size_t total_size = nmemb * size; + __atomic_add_fetch(&s_uv_malloc_size, total_size, __ATOMIC_RELAXED); + memcpy(ptr, &total_size, sizeof(size_t)); + return (void*)((intptr_t)ptr + sizeof(size_t)); + } + else + { + return NULL; + } +} + +static void _tf_uv_free(void* ptr) +{ + if (ptr) + { + void* old_ptr = (void*)((intptr_t)ptr - sizeof(size_t)); + size_t size = 0; + memcpy(&size, old_ptr, sizeof(size_t)); + __atomic_sub_fetch(&s_uv_malloc_size, size, __ATOMIC_RELAXED); + free(old_ptr); + } +} + +void tf_util_replace_uv_allocator() +{ + uv_replace_allocator(_tf_uv_alloc, _tf_uv_realloc, _tf_uv_calloc, _tf_uv_free); +} + +size_t tf_util_uv_get_malloc_size() +{ + return s_uv_malloc_size; +} + static JSValue _util_utf8_encode(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) { size_t length = 0; diff --git a/src/util.js.h b/src/util.js.h index 42e3e404..9bd00e03 100644 --- a/src/util.js.h +++ b/src/util.js.h @@ -4,6 +4,9 @@ #include +void tf_util_replace_uv_allocator(); +size_t tf_util_uv_get_malloc_size(); + void tf_util_register(JSContext* context); JSValue tf_util_utf8_decode(JSContext* context, JSValue value); uint8_t* tf_util_try_get_array_buffer(JSContext* context, size_t* psize, JSValueConst obj);