forked from cory/tildefriends
Use libbacktrace to generate better leak callstacks.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3986 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
14
src/main.c
14
src/main.c
@ -9,6 +9,7 @@
|
||||
#include "tests.h"
|
||||
#include "util.js.h"
|
||||
|
||||
#include <backtrace.h>
|
||||
#include <memcheck.h>
|
||||
#include <quickjs-libc.h>
|
||||
#include <quickjs.h>
|
||||
@ -83,6 +84,8 @@ const command_t k_commands[] = {
|
||||
{ "private", _tf_command_private, "Check for private messages the SSB database (just an experiment)." },
|
||||
};
|
||||
|
||||
struct backtrace_state* g_backtrace_state;
|
||||
|
||||
void shedPrivileges()
|
||||
{
|
||||
#if !defined(_WIN32)
|
||||
@ -684,8 +687,19 @@ static void _do_leak_checks(int sig)
|
||||
}
|
||||
#endif
|
||||
|
||||
static void _backtrace_error(void* data, const char* message, int errnum)
|
||||
{
|
||||
printf("libbacktrace error %d: %s\n", errnum, message);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
g_backtrace_state = backtrace_create_state(
|
||||
argv[0],
|
||||
0,
|
||||
_backtrace_error,
|
||||
NULL);
|
||||
|
||||
#if !defined(_WIN32)
|
||||
prctl(PR_SET_PDEATHSIG, SIGKILL);
|
||||
#endif
|
||||
|
97
src/task.c
97
src/task.c
@ -26,7 +26,10 @@
|
||||
#include "quickjs.h"
|
||||
#include "quickjs-libc.h"
|
||||
|
||||
#include <backtrace.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <execinfo.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
@ -35,6 +38,8 @@ static const char* k_version = "1.0";
|
||||
static JSClassID _import_class_id;
|
||||
static int _count;
|
||||
|
||||
extern struct backtrace_state* g_backtrace_state;
|
||||
|
||||
typedef struct _export_record_t export_record_t;
|
||||
typedef struct _import_record_t import_record_t;
|
||||
|
||||
@ -59,6 +64,8 @@ typedef struct _promise_stack_t
|
||||
{
|
||||
uint32_t hash;
|
||||
const char* stack;
|
||||
void* cstack[32];
|
||||
int cstack_count;
|
||||
int count;
|
||||
} promise_stack_t;
|
||||
|
||||
@ -731,18 +738,79 @@ static JSValue _tf_task_getStats(JSContext* context, JSValueConst this_val, int
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef struct _backtrace_t
|
||||
{
|
||||
JSContext* context;
|
||||
JSValue array;
|
||||
int count;
|
||||
} backtrace_t;
|
||||
|
||||
static int _tf_backtrace_callback(void* data, uintptr_t pc, const char* filename, int line_number, const char* function)
|
||||
{
|
||||
backtrace_t* bt = data;
|
||||
JSValue entry = JS_NewObject(bt->context);
|
||||
JS_SetPropertyStr(bt->context, entry, "pc", JS_NewInt64(bt->context, (int64_t)(intptr_t)pc));
|
||||
if (filename)
|
||||
{
|
||||
JS_SetPropertyStr(bt->context, entry, "filename", JS_NewString(bt->context, filename));
|
||||
}
|
||||
JS_SetPropertyStr(bt->context, entry, "line_number", JS_NewInt32(bt->context, line_number));
|
||||
if (function)
|
||||
{
|
||||
JS_SetPropertyStr(bt->context, entry, "function", JS_NewString(bt->context, function));
|
||||
}
|
||||
JS_SetPropertyUint32(bt->context, bt->array, bt->count++, entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _tf_backtrace_error(void* data, const char* message, int error)
|
||||
{
|
||||
backtrace_t* bt = data;
|
||||
JSValue entry = JS_NewObject(bt->context);
|
||||
if (message)
|
||||
{
|
||||
JS_SetPropertyStr(bt->context, entry, "error", JS_NewString(bt->context, message));
|
||||
}
|
||||
JS_SetPropertyStr(bt->context, entry, "code", JS_NewInt32(bt->context, error));
|
||||
JS_SetPropertyUint32(bt->context, bt->array, bt->count++, entry);
|
||||
}
|
||||
|
||||
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);
|
||||
JSValue promises = JS_NewArray(context);
|
||||
JS_SetPropertyStr(context, result, "promises", promises);
|
||||
int j = 0;
|
||||
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));
|
||||
JSValue entry = JS_NewObject(context);
|
||||
JS_SetPropertyStr(context, entry, "stack", JS_NewString(context, task->_promise_stacks[i].stack));
|
||||
if (task->_promise_stacks[i].cstack_count)
|
||||
{
|
||||
JSValue cstack = JS_NewArray(context);
|
||||
backtrace_t bt =
|
||||
{
|
||||
.context = context,
|
||||
.array = cstack,
|
||||
};
|
||||
for (int k = 0; k < task->_promise_stacks[i].cstack_count; k++)
|
||||
{
|
||||
backtrace_pcinfo(
|
||||
g_backtrace_state,
|
||||
(uintptr_t)task->_promise_stacks[i].cstack[k],
|
||||
_tf_backtrace_callback,
|
||||
_tf_backtrace_error,
|
||||
&bt);
|
||||
|
||||
}
|
||||
JS_SetPropertyStr(context, entry, "cstack", cstack);
|
||||
}
|
||||
JS_SetPropertyStr(context, entry, "count", JS_NewInt32(context, task->_promise_stacks[i].count));
|
||||
JS_SetPropertyUint32(context, promises, j++, entry);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1018,7 +1086,7 @@ static int _promise_stack_compare(const void* a, const void* 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)
|
||||
static void _add_promise_stack(tf_task_t* task, uint32_t hash, const char* stack, void** buffer, int count)
|
||||
{
|
||||
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)
|
||||
@ -1032,7 +1100,8 @@ static void _add_promise_stack(tf_task_t* task, uint32_t hash, const char* stack
|
||||
{
|
||||
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_stacks[index] = (promise_stack_t) { .hash = hash, .stack = tf_strdup(stack), .count = 1, .cstack_count = count };
|
||||
memcpy(task->_promise_stacks[index].cstack, buffer, sizeof(void*) * count);
|
||||
task->_promise_stack_count++;
|
||||
}
|
||||
}
|
||||
@ -1058,11 +1127,12 @@ static void _tf_task_free_promise(tf_task_t* task, promiseid_t id)
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t fnv32a(const char* buffer, uint32_t start)
|
||||
uint32_t fnv32a(const void* buffer, int length, uint32_t start)
|
||||
{
|
||||
uint32_t result = 0x811c9dc5;
|
||||
for (const char* p = buffer; *p; p++) {
|
||||
result ^= *p;
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
result ^= ((const uint8_t*)buffer)[i];
|
||||
result += (result << 1) + (result << 4) + (result << 7) + (result << 8) + (result << 24);
|
||||
}
|
||||
return result;
|
||||
@ -1073,9 +1143,16 @@ 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);
|
||||
size_t length = 0;
|
||||
const char* stack = JS_ToCStringLen(task->_context, &length, stack_value);
|
||||
uint32_t stack_hash = fnv32a((const void*)stack, (int)length, 0);
|
||||
int count = 0;
|
||||
void* buffer[32];
|
||||
#ifndef _WIN32
|
||||
count = backtrace(buffer, sizeof(buffer) / sizeof(*buffer));
|
||||
stack_hash = fnv32a((const void*)buffer, sizeof(void*) * count, stack_hash);
|
||||
#endif
|
||||
_add_promise_stack(task, stack_hash, stack, buffer, count);
|
||||
JS_FreeCString(task->_context, stack);
|
||||
JS_FreeValue(task->_context, stack_value);
|
||||
JS_FreeValue(task->_context, exception);
|
||||
|
Reference in New Issue
Block a user