Debug features for leaked promises. And then chased down some subsequent use after free issues.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3985 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
parent
ab1f47ee9a
commit
8279ec5e9e
4
Makefile
4
Makefile
@ -18,7 +18,8 @@ CFLAGS += \
|
|||||||
-fdata-sections
|
-fdata-sections
|
||||||
LDFLAGS += -Wl,-gc-sections
|
LDFLAGS += -Wl,-gc-sections
|
||||||
|
|
||||||
debug windebug: CFLAGS += -Og -g
|
debug windebug: CFLAGS += -Og -g -fno-omit-frame-pointer
|
||||||
|
debug release: LDFLAGS += -rdynamic
|
||||||
release winrelease: CFLAGS += -DNDEBUG -O3 -g
|
release winrelease: CFLAGS += -DNDEBUG -O3 -g
|
||||||
windebug winrelease: CC = i686-w64-mingw32-gcc-win32
|
windebug winrelease: CC = i686-w64-mingw32-gcc-win32
|
||||||
windebug winrelease: AS = $(CC)
|
windebug winrelease: AS = $(CC)
|
||||||
@ -68,6 +69,7 @@ UV_SOURCES := \
|
|||||||
deps/libuv/src/inet.c \
|
deps/libuv/src/inet.c \
|
||||||
deps/libuv/src/random.c \
|
deps/libuv/src/random.c \
|
||||||
deps/libuv/src/strscpy.c \
|
deps/libuv/src/strscpy.c \
|
||||||
|
deps/libuv/src/strtok.c \
|
||||||
deps/libuv/src/threadpool.c \
|
deps/libuv/src/threadpool.c \
|
||||||
deps/libuv/src/timer.c \
|
deps/libuv/src/timer.c \
|
||||||
deps/libuv/src/uv-common.c \
|
deps/libuv/src/uv-common.c \
|
||||||
|
@ -793,6 +793,10 @@ loadSettings().then(function() {
|
|||||||
var data = trace();
|
var data = trace();
|
||||||
response.writeHead(200, {"Content-Type": "application/json; charset=utf-8", "Content-Length": data.length.toString()});
|
response.writeHead(200, {"Content-Type": "application/json; charset=utf-8", "Content-Length": data.length.toString()});
|
||||||
return response.end(data);
|
return response.end(data);
|
||||||
|
} else if (match = /^\/debug$/.exec(request.uri)) {
|
||||||
|
var data = JSON.stringify(getDebug(), null, 2);
|
||||||
|
response.writeHead(200, {"Content-Type": "application/json; charset=utf-8", "Content-Length": data.length.toString()});
|
||||||
|
return response.end(data);
|
||||||
} else if (request.uri == "/robots.txt") {
|
} else if (request.uri == "/robots.txt") {
|
||||||
return blobHandler(request, response, null, request.uri);
|
return blobHandler(request, response, null, request.uri);
|
||||||
} else if ((match = /^\/.well-known\/(.*)/.exec(request.uri)) && request.uri.indexOf("..") == -1) {
|
} else if ((match = /^\/.well-known\/(.*)/.exec(request.uri)) && request.uri.indexOf("..") == -1) {
|
||||||
|
@ -95,22 +95,19 @@ static void _socket_reportError(socket_t* socket, const char* error);
|
|||||||
static void _socket_set_handler(socket_t* socket, JSValue* handler, JSValue new_value)
|
static void _socket_set_handler(socket_t* socket, JSValue* handler, JSValue new_value)
|
||||||
{
|
{
|
||||||
JSContext* context = tf_task_get_context(socket->_task);
|
JSContext* context = tf_task_get_context(socket->_task);
|
||||||
bool had_handler = !JS_IsUndefined(*handler);
|
|
||||||
if (!had_handler && !JS_IsUndefined(new_value))
|
JSValue old_handler = *handler;
|
||||||
|
|
||||||
|
if (JS_IsUndefined(old_handler) && !JS_IsUndefined(new_value))
|
||||||
{
|
{
|
||||||
JS_DupValue(context, socket->_object);
|
JS_DupValue(context, socket->_object);
|
||||||
}
|
}
|
||||||
if (had_handler)
|
|
||||||
{
|
|
||||||
JSValue value = *handler;
|
|
||||||
*handler = JS_UNDEFINED;
|
|
||||||
JS_FreeValue(context, value);
|
|
||||||
}
|
|
||||||
if (!JS_IsUndefined(new_value))
|
|
||||||
{
|
|
||||||
*handler = JS_DupValue(context, new_value);
|
*handler = JS_DupValue(context, new_value);
|
||||||
}
|
|
||||||
if (had_handler && JS_IsUndefined(new_value))
|
JS_FreeValue(context, old_handler);
|
||||||
|
|
||||||
|
if (!JS_IsUndefined(old_handler) && JS_IsUndefined(new_value))
|
||||||
{
|
{
|
||||||
JS_FreeValue(context, socket->_object);
|
JS_FreeValue(context, socket->_object);
|
||||||
}
|
}
|
||||||
@ -280,7 +277,9 @@ void _socket_reportError(socket_t* socket, const char* error)
|
|||||||
if (JS_IsFunction(context, socket-> _onError))
|
if (JS_IsFunction(context, socket-> _onError))
|
||||||
{
|
{
|
||||||
JSValue exception = JS_ThrowInternalError(context, "%s", error);
|
JSValue exception = JS_ThrowInternalError(context, "%s", error);
|
||||||
|
JSValue cb_ref = JS_DupValue(context, socket->_onError);
|
||||||
JSValue result = JS_Call(context, socket->_onError, socket->_object, 1, &exception);
|
JSValue result = JS_Call(context, socket->_onError, socket->_object, 1, &exception);
|
||||||
|
JS_FreeValue(context, cb_ref);
|
||||||
tf_util_report_error(context, result);
|
tf_util_report_error(context, result);
|
||||||
JS_FreeValue(context, exception);
|
JS_FreeValue(context, exception);
|
||||||
JS_FreeValue(context, result);
|
JS_FreeValue(context, result);
|
||||||
@ -547,7 +546,7 @@ JSValue _socket_listen(JSContext* context, JSValueConst this_val, int argc, JSVa
|
|||||||
{
|
{
|
||||||
socket_t* socket = JS_GetOpaque(this_val, _classId);
|
socket_t* socket = JS_GetOpaque(this_val, _classId);
|
||||||
socket->_listening = true;
|
socket->_listening = true;
|
||||||
int backlog = 1;
|
int backlog = 16;
|
||||||
JS_ToInt32(context, &backlog, argv[0]);
|
JS_ToInt32(context, &backlog, argv[0]);
|
||||||
if (JS_IsUndefined(socket->_onConnect))
|
if (JS_IsUndefined(socket->_onConnect))
|
||||||
{
|
{
|
||||||
@ -572,7 +571,9 @@ void _socket_onNewConnection(uv_stream_t* server, int status)
|
|||||||
JSValue ref = JS_DupValue(context, socket->_object);
|
JSValue ref = JS_DupValue(context, socket->_object);
|
||||||
if (!JS_IsUndefined(socket->_onConnect))
|
if (!JS_IsUndefined(socket->_onConnect))
|
||||||
{
|
{
|
||||||
|
JSValue cb_ref = JS_DupValue(context, socket->_onConnect);
|
||||||
JSValue result = JS_Call(context, socket->_onConnect, socket->_object, 0, NULL);
|
JSValue result = JS_Call(context, socket->_onConnect, socket->_object, 0, NULL);
|
||||||
|
JS_FreeValue(context, cb_ref);
|
||||||
tf_util_report_error(context, result);
|
tf_util_report_error(context, result);
|
||||||
JS_FreeValue(context, result);
|
JS_FreeValue(context, result);
|
||||||
}
|
}
|
||||||
@ -586,6 +587,7 @@ JSValue _socket_accept(JSContext* context, JSValueConst this_val, int argc, JSVa
|
|||||||
socket_t* client = _socket_create_internal(context);
|
socket_t* client = _socket_create_internal(context);
|
||||||
client->_direction = kAccept;
|
client->_direction = kAccept;
|
||||||
promiseid_t promise;
|
promiseid_t promise;
|
||||||
|
JSValue ref = JS_DupValue(context, client->_object);
|
||||||
JSValue result = tf_task_allocate_promise(socket->_task, &promise);
|
JSValue result = tf_task_allocate_promise(socket->_task, &promise);
|
||||||
int status = uv_accept((uv_stream_t*)&socket->_socket, (uv_stream_t*)&client->_socket);
|
int status = uv_accept((uv_stream_t*)&socket->_socket, (uv_stream_t*)&client->_socket);
|
||||||
if (status == 0)
|
if (status == 0)
|
||||||
@ -603,7 +605,7 @@ JSValue _socket_accept(JSContext* context, JSValueConst this_val, int argc, JSVa
|
|||||||
{
|
{
|
||||||
tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(context, "uv_accept: %s", uv_strerror(status)));
|
tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(context, "uv_accept: %s", uv_strerror(status)));
|
||||||
}
|
}
|
||||||
JS_FreeValue(context, client->_object);
|
JS_FreeValue(context, ref);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -728,7 +730,9 @@ void _socket_onRead(uv_stream_t* stream, ssize_t readSize, const uv_buf_t* buffe
|
|||||||
if (!JS_IsUndefined(socket->_onRead))
|
if (!JS_IsUndefined(socket->_onRead))
|
||||||
{
|
{
|
||||||
JSValue args[] = { JS_UNDEFINED };
|
JSValue args[] = { JS_UNDEFINED };
|
||||||
|
JSValue cb_ref = JS_DupValue(context, socket->_onRead);
|
||||||
JSValue result = JS_Call(context, socket->_onRead, socket->_object, 1, args);
|
JSValue result = JS_Call(context, socket->_onRead, socket->_object, 1, args);
|
||||||
|
JS_FreeValue(context, cb_ref);
|
||||||
tf_util_report_error(context, result);
|
tf_util_report_error(context, result);
|
||||||
JS_FreeValue(context, result);
|
JS_FreeValue(context, result);
|
||||||
}
|
}
|
||||||
@ -784,7 +788,9 @@ void _socket_onRead(uv_stream_t* stream, ssize_t readSize, const uv_buf_t* buffe
|
|||||||
if (!JS_IsUndefined(socket->_onRead))
|
if (!JS_IsUndefined(socket->_onRead))
|
||||||
{
|
{
|
||||||
JSValue args[] = { JS_UNDEFINED };
|
JSValue args[] = { JS_UNDEFINED };
|
||||||
|
JSValue cb_ref = JS_DupValue(context, socket->_onRead);
|
||||||
JSValue result = JS_Call(context, socket->_onRead, socket->_object, 1, args);
|
JSValue result = JS_Call(context, socket->_onRead, socket->_object, 1, args);
|
||||||
|
JS_FreeValue(context, cb_ref);
|
||||||
tf_util_report_error(context, result);
|
tf_util_report_error(context, result);
|
||||||
JS_FreeValue(context, result);
|
JS_FreeValue(context, result);
|
||||||
}
|
}
|
||||||
@ -833,7 +839,9 @@ void _socket_notifyDataRead(socket_t* socket, const char* data, size_t length)
|
|||||||
JSValue args[] = { typedArray };
|
JSValue args[] = { typedArray };
|
||||||
if (!JS_IsUndefined(socket->_onRead))
|
if (!JS_IsUndefined(socket->_onRead))
|
||||||
{
|
{
|
||||||
|
JSValue cb_ref = JS_DupValue(context, socket->_onRead);
|
||||||
JSValue result = JS_Call(context, socket->_onRead, socket->_object, 1, args);
|
JSValue result = JS_Call(context, socket->_onRead, socket->_object, 1, args);
|
||||||
|
JS_FreeValue(context, cb_ref);
|
||||||
tf_util_report_error(context, result);
|
tf_util_report_error(context, result);
|
||||||
JS_FreeValue(context, result);
|
JS_FreeValue(context, result);
|
||||||
}
|
}
|
||||||
@ -913,6 +921,7 @@ JSValue _socket_write(JSContext* context, JSValueConst this_val, int argc, JSVal
|
|||||||
socket_t* socket = JS_GetOpaque(this_val, _classId);
|
socket_t* socket = JS_GetOpaque(this_val, _classId);
|
||||||
promiseid_t promise = -1;
|
promiseid_t promise = -1;
|
||||||
JSValue write_result = tf_task_allocate_promise(socket->_task, &promise);
|
JSValue write_result = tf_task_allocate_promise(socket->_task, &promise);
|
||||||
|
JSValue ref = JS_DupValue(context, socket->_object);
|
||||||
if (!JS_IsUndefined(argv[0]))
|
if (!JS_IsUndefined(argv[0]))
|
||||||
{
|
{
|
||||||
if (socket->_tls)
|
if (socket->_tls)
|
||||||
@ -950,6 +959,7 @@ JSValue _socket_write(JSContext* context, JSValueConst this_val, int argc, JSVal
|
|||||||
{
|
{
|
||||||
tf_task_reject_promise(socket->_task, promise, JS_NewInt32(context, -2));
|
tf_task_reject_promise(socket->_task, promise, JS_NewInt32(context, -2));
|
||||||
}
|
}
|
||||||
|
JS_FreeValue(context, ref);
|
||||||
return write_result;
|
return write_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
93
src/task.c
93
src/task.c
@ -52,8 +52,16 @@ typedef struct _promise_t
|
|||||||
{
|
{
|
||||||
promiseid_t id;
|
promiseid_t id;
|
||||||
JSValue values[2];
|
JSValue values[2];
|
||||||
|
uint32_t stack_hash;
|
||||||
} promise_t;
|
} promise_t;
|
||||||
|
|
||||||
|
typedef struct _promise_stack_t
|
||||||
|
{
|
||||||
|
uint32_t hash;
|
||||||
|
const char* stack;
|
||||||
|
int count;
|
||||||
|
} promise_stack_t;
|
||||||
|
|
||||||
typedef struct _tf_task_t
|
typedef struct _tf_task_t
|
||||||
{
|
{
|
||||||
taskid_t _nextTask;
|
taskid_t _nextTask;
|
||||||
@ -107,6 +115,9 @@ typedef struct _tf_task_t
|
|||||||
char _db_path[256];
|
char _db_path[256];
|
||||||
char _secrets_path[256];
|
char _secrets_path[256];
|
||||||
const char* _args;
|
const char* _args;
|
||||||
|
|
||||||
|
promise_stack_t* _promise_stacks;
|
||||||
|
int _promise_stack_count;
|
||||||
} tf_task_t;
|
} tf_task_t;
|
||||||
|
|
||||||
typedef struct _export_record_t
|
typedef struct _export_record_t
|
||||||
@ -720,6 +731,24 @@ static JSValue _tf_task_getStats(JSContext* context, JSValueConst this_val, int
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static JSValue _tf_task_getDebug(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||||
|
{
|
||||||
|
tf_task_t* task = JS_GetContextOpaque(context);
|
||||||
|
JSValue result = JS_NewObject(context);
|
||||||
|
|
||||||
|
JSValue promises = JS_NewObject(context);
|
||||||
|
JS_SetPropertyStr(context, result, "promises", promises);
|
||||||
|
for (int i = 0; i < task->_promise_stack_count; i++)
|
||||||
|
{
|
||||||
|
if (task->_promise_stacks[i].count)
|
||||||
|
{
|
||||||
|
JS_SetPropertyStr(context, promises, task->_promise_stacks[i].stack, JS_NewInt32(context, task->_promise_stacks[i].count));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static JSValue _tf_task_getFile(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
static JSValue _tf_task_getFile(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||||
{
|
{
|
||||||
tf_task_t* task = JS_GetContextOpaque(context);
|
tf_task_t* task = JS_GetContextOpaque(context);
|
||||||
@ -982,19 +1011,76 @@ static promise_t* _tf_task_find_promise(tf_task_t* task, promiseid_t id)
|
|||||||
return it ? it : NULL;
|
return it ? it : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int _promise_stack_compare(const void* a, const void* b)
|
||||||
|
{
|
||||||
|
const uint32_t* pa = a;
|
||||||
|
const promise_stack_t* pb = b;
|
||||||
|
return *pa < pb->hash ? -1 : *pa > pb->hash ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _add_promise_stack(tf_task_t* task, uint32_t hash, const char* stack)
|
||||||
|
{
|
||||||
|
int index = tf_util_insert_index(&hash, task->_promise_stacks, task->_promise_stack_count, sizeof(promise_stack_t), _promise_stack_compare);
|
||||||
|
if (index < task->_promise_stack_count && task->_promise_stacks[index].hash == hash)
|
||||||
|
{
|
||||||
|
task->_promise_stacks[index].count++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
task->_promise_stacks = tf_resize_vec(task->_promise_stacks, sizeof(promise_stack_t) * (task->_promise_stack_count + 1));
|
||||||
|
if (task->_promise_stack_count - index)
|
||||||
|
{
|
||||||
|
memmove(task->_promise_stacks + index + 1, task->_promise_stacks + index, sizeof(promise_stack_t) * (task->_promise_stack_count - index));
|
||||||
|
}
|
||||||
|
task->_promise_stacks[index] = (promise_stack_t) { .hash = hash, .stack = tf_strdup(stack), .count = 1 };
|
||||||
|
task->_promise_stack_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _remove_promise_stack(tf_task_t* task, uint32_t hash)
|
||||||
|
{
|
||||||
|
promise_stack_t* found = bsearch(&hash, task->_promise_stacks, task->_promise_stack_count, sizeof(promise_stack_t), _promise_stack_compare);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
found->count--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void _tf_task_free_promise(tf_task_t* task, promiseid_t id)
|
static void _tf_task_free_promise(tf_task_t* task, promiseid_t id)
|
||||||
{
|
{
|
||||||
promise_t* it = bsearch((void*)(intptr_t)id, task->_promises, task->_promise_count, sizeof(promise_t), _promise_compare);
|
promise_t* it = bsearch((void*)(intptr_t)id, task->_promises, task->_promise_count, sizeof(promise_t), _promise_compare);
|
||||||
if (it)
|
if (it)
|
||||||
{
|
{
|
||||||
|
_remove_promise_stack(task, it->stack_hash);
|
||||||
int index = it - task->_promises;
|
int index = it - task->_promises;
|
||||||
memmove(it, it + 1, sizeof(promise_t) * (task->_promise_count - index - 1));
|
memmove(it, it + 1, sizeof(promise_t) * (task->_promise_count - index - 1));
|
||||||
task->_promise_count--;
|
task->_promise_count--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t fnv32a(const char* buffer, uint32_t start)
|
||||||
|
{
|
||||||
|
uint32_t result = 0x811c9dc5;
|
||||||
|
for (const char* p = buffer; *p; p++) {
|
||||||
|
result ^= *p;
|
||||||
|
result += (result << 1) + (result << 4) + (result << 7) + (result << 8) + (result << 24);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
JSValue tf_task_allocate_promise(tf_task_t* task, promiseid_t* out_promise)
|
JSValue tf_task_allocate_promise(tf_task_t* task, promiseid_t* out_promise)
|
||||||
{
|
{
|
||||||
|
JSValue error = JS_ThrowInternalError(task->_context, "promise callstack");
|
||||||
|
JSValue exception = JS_GetException(task->_context);
|
||||||
|
JSValue stack_value = JS_GetPropertyStr(task->_context, exception, "stack");
|
||||||
|
const char* stack = JS_ToCString(task->_context, stack_value);
|
||||||
|
uint32_t stack_hash = fnv32a(stack, 0);
|
||||||
|
_add_promise_stack(task, stack_hash, stack);
|
||||||
|
JS_FreeCString(task->_context, stack);
|
||||||
|
JS_FreeValue(task->_context, stack_value);
|
||||||
|
JS_FreeValue(task->_context, exception);
|
||||||
|
JS_FreeValue(task->_context, error);
|
||||||
|
|
||||||
promiseid_t promiseId;
|
promiseid_t promiseId;
|
||||||
do {
|
do {
|
||||||
promiseId = task->_nextPromise++;
|
promiseId = task->_nextPromise++;
|
||||||
@ -1004,6 +1090,7 @@ JSValue tf_task_allocate_promise(tf_task_t* task, promiseid_t* out_promise)
|
|||||||
{
|
{
|
||||||
.id = promiseId,
|
.id = promiseId,
|
||||||
.values = { JS_NULL, JS_NULL },
|
.values = { JS_NULL, JS_NULL },
|
||||||
|
.stack_hash = stack_hash,
|
||||||
};
|
};
|
||||||
JSValue result = JS_NewPromiseCapability(task->_context, promise.values);
|
JSValue result = JS_NewPromiseCapability(task->_context, promise.values);
|
||||||
int index = tf_util_insert_index((void*)(intptr_t)promiseId, task->_promises, task->_promise_count, sizeof(promise_t), _promise_compare);
|
int index = tf_util_insert_index((void*)(intptr_t)promiseId, task->_promises, task->_promise_count, sizeof(promise_t), _promise_compare);
|
||||||
@ -1384,6 +1471,7 @@ void tf_task_activate(tf_task_t* task)
|
|||||||
|
|
||||||
JS_SetPropertyStr(context, global, "trace", JS_NewCFunction(context, _tf_task_trace, "trace", 1));
|
JS_SetPropertyStr(context, global, "trace", JS_NewCFunction(context, _tf_task_trace, "trace", 1));
|
||||||
JS_SetPropertyStr(context, global, "getStats", JS_NewCFunction(context, _tf_task_getStats, "getStats", 0));
|
JS_SetPropertyStr(context, global, "getStats", JS_NewCFunction(context, _tf_task_getStats, "getStats", 0));
|
||||||
|
JS_SetPropertyStr(context, global, "getDebug", JS_NewCFunction(context, _tf_task_getDebug, "getDebug", 0));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1506,6 +1594,11 @@ void tf_task_destroy(tf_task_t* task)
|
|||||||
tf_trace_destroy(task->_trace);
|
tf_trace_destroy(task->_trace);
|
||||||
}
|
}
|
||||||
--_count;
|
--_count;
|
||||||
|
for (int i = 0; i < task->_promise_stack_count; i++)
|
||||||
|
{
|
||||||
|
tf_free((void*)task->_promise_stacks[i].stack);
|
||||||
|
}
|
||||||
|
tf_free(task->_promise_stacks);
|
||||||
tf_free((void*)task->_path);
|
tf_free((void*)task->_path);
|
||||||
tf_free(task);
|
tf_free(task);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user