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
This commit is contained in:
parent
5622db92a7
commit
2992b7e955
@ -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);
|
int result = uv_fs_close(req->loop, req, fsreq->file, _file_async_close_callback);
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
{
|
{
|
||||||
|
uv_fs_req_cleanup(req);
|
||||||
free(fsreq);
|
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);
|
result = uv_fs_close(req->loop, req, fsreq->file, _file_async_close_callback);
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
{
|
{
|
||||||
|
uv_fs_req_cleanup(req);
|
||||||
free(fsreq);
|
free(fsreq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,6 +120,7 @@ static void _file_read_open_callback(uv_fs_t* req)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, uv_strerror(req->result)));
|
tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, uv_strerror(req->result)));
|
||||||
|
uv_fs_req_cleanup(req);
|
||||||
free(req);
|
free(req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -142,6 +145,7 @@ static JSValue _file_read_file(JSContext* context, JSValueConst this_val, int ar
|
|||||||
if (result < 0)
|
if (result < 0)
|
||||||
{
|
{
|
||||||
tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, uv_strerror(result)));
|
tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, uv_strerror(result)));
|
||||||
|
uv_fs_req_cleanup(&req->fs);
|
||||||
free(req);
|
free(req);
|
||||||
}
|
}
|
||||||
JS_FreeCString(context, file_name);
|
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);
|
int result = uv_fs_close(req->loop, req, fsreq->file, _file_async_close_callback);
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
{
|
{
|
||||||
|
uv_fs_req_cleanup(req);
|
||||||
free(fsreq);
|
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);
|
result = uv_fs_close(req->loop, req, fsreq->file, _file_async_close_callback);
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
{
|
{
|
||||||
|
uv_fs_req_cleanup(req);
|
||||||
free(fsreq);
|
free(fsreq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -195,6 +201,7 @@ static void _file_write_open_callback(uv_fs_t* req)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, uv_strerror(req->result)));
|
tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, uv_strerror(req->result)));
|
||||||
|
uv_fs_req_cleanup(req);
|
||||||
free(req);
|
free(req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -360,6 +367,7 @@ JSValue _file_stat(JSContext* context, JSValueConst this_val, int argc, JSValueC
|
|||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
tf_task_reject_promise(task, promise, JS_NewInt32(context, result));
|
tf_task_reject_promise(task, promise, JS_NewInt32(context, result));
|
||||||
|
uv_fs_req_cleanup(&data->_request);
|
||||||
free(data);
|
free(data);
|
||||||
}
|
}
|
||||||
JS_FreeCString(context, path);
|
JS_FreeCString(context, path);
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "task.h"
|
#include "task.h"
|
||||||
#include "taskstub.js.h"
|
#include "taskstub.js.h"
|
||||||
#include "tests.h"
|
#include "tests.h"
|
||||||
|
#include "util.js.h"
|
||||||
|
|
||||||
#include <quickjs-libc.h>
|
#include <quickjs-libc.h>
|
||||||
#include <quickjs.h>
|
#include <quickjs.h>
|
||||||
@ -624,6 +625,7 @@ int main(int argc, char* argv[])
|
|||||||
#if !defined(_WIN32)
|
#if !defined(_WIN32)
|
||||||
prctl(PR_SET_PDEATHSIG, SIGKILL);
|
prctl(PR_SET_PDEATHSIG, SIGKILL);
|
||||||
#endif
|
#endif
|
||||||
|
tf_util_replace_uv_allocator();
|
||||||
uv_setup_args(argc, argv);
|
uv_setup_args(argc, argv);
|
||||||
tf_taskstub_startup();
|
tf_taskstub_startup();
|
||||||
|
|
||||||
|
24
src/task.c
24
src/task.c
@ -93,6 +93,7 @@ typedef struct _tf_task_t
|
|||||||
promiseid_t _nextPromise;
|
promiseid_t _nextPromise;
|
||||||
uv_loop_t _loop;
|
uv_loop_t _loop;
|
||||||
|
|
||||||
|
uv_timer_t gc_timer;
|
||||||
uv_timer_t trace_timer;
|
uv_timer_t trace_timer;
|
||||||
uint64_t last_hrtime;
|
uint64_t last_hrtime;
|
||||||
uint64_t last_idle_time;
|
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));
|
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_count", JS_NewInt32(context, tf_socket_get_count()));
|
||||||
JS_SetPropertyStr(context, result, "socket_open_count", JS_NewInt32(context, tf_socket_get_open_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)
|
static void _tf_task_trace_timer(uv_timer_t* timer)
|
||||||
{
|
{
|
||||||
tf_task_t* task = timer->data;
|
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_init(&task->_loop, &task->trace_timer);
|
||||||
uv_timer_start(&task->trace_timer, _tf_task_trace_timer, 100, 100);
|
uv_timer_start(&task->trace_timer, _tf_task_trace_timer, 100, 100);
|
||||||
uv_unref((uv_handle_t*)&task->trace_timer);
|
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;
|
task->idle.data = task;
|
||||||
uv_idle_init(&task->_loop, &task->idle);
|
uv_idle_init(&task->_loop, &task->idle);
|
||||||
uv_unref((uv_handle_t*)&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);
|
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->idle, _tf_task_on_handle_close);
|
||||||
uv_close((uv_handle_t*)&task->prepare, _tf_task_on_handle_close);
|
uv_close((uv_handle_t*)&task->prepare, _tf_task_on_handle_close);
|
||||||
|
|
||||||
while (task->trace_timer.data ||
|
while (task->trace_timer.data ||
|
||||||
|
task->gc_timer.data ||
|
||||||
task->idle.data ||
|
task->idle.data ||
|
||||||
task->prepare.data)
|
task->prepare.data)
|
||||||
{
|
{
|
||||||
|
@ -9,6 +9,83 @@
|
|||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
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)
|
static JSValue _util_utf8_encode(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||||
{
|
{
|
||||||
size_t length = 0;
|
size_t length = 0;
|
||||||
|
@ -4,6 +4,9 @@
|
|||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
void tf_util_replace_uv_allocator();
|
||||||
|
size_t tf_util_uv_get_malloc_size();
|
||||||
|
|
||||||
void tf_util_register(JSContext* context);
|
void tf_util_register(JSContext* context);
|
||||||
JSValue tf_util_utf8_decode(JSContext* context, JSValue value);
|
JSValue tf_util_utf8_decode(JSContext* context, JSValue value);
|
||||||
uint8_t* tf_util_try_get_array_buffer(JSContext* context, size_t* psize, JSValueConst obj);
|
uint8_t* tf_util_try_get_array_buffer(JSContext* context, size_t* psize, JSValueConst obj);
|
||||||
|
Loading…
Reference in New Issue
Block a user