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:
2022-09-22 00:38:26 +00:00
parent ab1f47ee9a
commit 8279ec5e9e
4 changed files with 125 additions and 16 deletions

View File

@ -52,8 +52,16 @@ typedef struct _promise_t
{
promiseid_t id;
JSValue values[2];
uint32_t stack_hash;
} promise_t;
typedef struct _promise_stack_t
{
uint32_t hash;
const char* stack;
int count;
} promise_stack_t;
typedef struct _tf_task_t
{
taskid_t _nextTask;
@ -107,6 +115,9 @@ typedef struct _tf_task_t
char _db_path[256];
char _secrets_path[256];
const char* _args;
promise_stack_t* _promise_stacks;
int _promise_stack_count;
} tf_task_t;
typedef struct _export_record_t
@ -720,6 +731,24 @@ static JSValue _tf_task_getStats(JSContext* context, JSValueConst this_val, int
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)
{
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;
}
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)
{
promise_t* it = bsearch((void*)(intptr_t)id, task->_promises, task->_promise_count, sizeof(promise_t), _promise_compare);
if (it)
{
_remove_promise_stack(task, it->stack_hash);
int index = it - task->_promises;
memmove(it, it + 1, sizeof(promise_t) * (task->_promise_count - index - 1));
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 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;
do {
promiseId = task->_nextPromise++;
@ -1004,6 +1090,7 @@ JSValue tf_task_allocate_promise(tf_task_t* task, promiseid_t* out_promise)
{
.id = promiseId,
.values = { JS_NULL, JS_NULL },
.stack_hash = stack_hash,
};
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);
@ -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, "getStats", JS_NewCFunction(context, _tf_task_getStats, "getStats", 0));
JS_SetPropertyStr(context, global, "getDebug", JS_NewCFunction(context, _tf_task_getDebug, "getDebug", 0));
}
else
{
@ -1506,6 +1594,11 @@ void tf_task_destroy(tf_task_t* task)
tf_trace_destroy(task->_trace);
}
--_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(task);
}