perf: Make promise stack trace collection opt-in.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled

This commit is contained in:
Cory McWilliams 2025-02-27 19:41:21 -05:00
parent 8912212d8e
commit 8b47938238
4 changed files with 50 additions and 31 deletions

View File

@ -22,8 +22,6 @@ static int64_t s_tls_malloc_size;
static int64_t s_js_malloc_size; static int64_t s_js_malloc_size;
static int64_t s_sqlite_malloc_size; static int64_t s_sqlite_malloc_size;
extern uint32_t fnv32a(const void* buffer, int length, uint32_t start);
static size_t _tf_mem_round_up(size_t size) static size_t _tf_mem_round_up(size_t size)
{ {
return (size + 7) & ~7; return (size + 7) & ~7;
@ -156,7 +154,7 @@ static void _tf_mem_summarize(void* ptr, size_t size, int frames_count, void* co
{ {
summary_t* summary = user_data; summary_t* summary = user_data;
tf_mem_allocation_t allocation = { tf_mem_allocation_t allocation = {
.stack_hash = fnv32a(frames, sizeof(void*) * frames_count, 0), .stack_hash = tf_util_fnv32a(frames, sizeof(void*) * frames_count, 0),
.count = 1, .count = 1,
.size = size, .size = size,
.frames_count = frames_count, .frames_count = frames_count,

View File

@ -164,6 +164,7 @@ typedef struct _tf_task_t
promise_stack_t* _promise_stacks; promise_stack_t* _promise_stacks;
int _promise_stack_count; int _promise_stack_count;
bool _promise_stack_debug;
timeout_t* timeouts; timeout_t* timeouts;
@ -1252,10 +1253,13 @@ static void _add_promise_stack(tf_task_t* task, uint32_t hash, const char* stack
static void _remove_promise_stack(tf_task_t* task, uint32_t hash) 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 (task->_promise_stack_debug)
if (found)
{ {
found->count--; promise_stack_t* found = bsearch(&hash, task->_promise_stacks, task->_promise_stack_count, sizeof(promise_stack_t), _promise_stack_compare);
if (found)
{
found->count--;
}
} }
} }
@ -1271,33 +1275,26 @@ static void _tf_task_free_promise(tf_task_t* task, promiseid_t id)
} }
} }
uint32_t fnv32a(const void* buffer, int length, uint32_t start)
{
uint32_t result = 0x811c9dc5;
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;
}
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"); uint32_t stack_hash = 0;
JSValue exception = JS_GetException(task->_context); if (task->_promise_stack_debug)
JSValue stack_value = JS_GetPropertyStr(task->_context, exception, "stack"); {
size_t length = 0; JSValue error = JS_ThrowInternalError(task->_context, "promise callstack");
const char* stack = JS_ToCStringLen(task->_context, &length, stack_value); JSValue exception = JS_GetException(task->_context);
uint32_t stack_hash = fnv32a((const void*)stack, (int)length, 0); JSValue stack_value = JS_GetPropertyStr(task->_context, exception, "stack");
void* buffer[32]; size_t length = 0;
int count = tf_util_backtrace(buffer, sizeof(buffer) / sizeof(*buffer)); const char* stack = JS_ToCStringLen(task->_context, &length, stack_value);
stack_hash = fnv32a((const void*)buffer, sizeof(void*) * count, stack_hash); stack_hash = tf_util_fnv32a((const void*)stack, (int)length, 0);
_add_promise_stack(task, stack_hash, stack, buffer, count); void* buffer[32];
JS_FreeCString(task->_context, stack); int count = tf_util_backtrace(buffer, sizeof(buffer) / sizeof(*buffer));
JS_FreeValue(task->_context, stack_value); stack_hash = tf_util_fnv32a((const void*)buffer, sizeof(void*) * count, stack_hash);
JS_FreeValue(task->_context, exception); _add_promise_stack(task, stack_hash, stack, buffer, count);
JS_FreeValue(task->_context, error); 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
@ -1592,6 +1589,10 @@ tf_task_t* tf_task_create()
*task = (tf_task_t) { 0 }; *task = (tf_task_t) { 0 };
++_count; ++_count;
char buffer[8] = { 0 };
size_t buffer_size = sizeof(buffer);
task->_promise_stack_debug = uv_os_getenv("TF_PROMISE_DEBUG", buffer, &buffer_size) == 0 && strcmp(buffer, "1") == 0;
JSMallocFunctions funcs = { 0 }; JSMallocFunctions funcs = { 0 };
tf_get_js_malloc_functions(&funcs); tf_get_js_malloc_functions(&funcs);
task->_runtime = JS_NewRuntime2(&funcs, NULL); task->_runtime = JS_NewRuntime2(&funcs, NULL);

View File

@ -683,3 +683,14 @@ bool tf_util_is_mobile()
{ {
return TF_IS_MOBILE != 0; return TF_IS_MOBILE != 0;
} }
uint32_t tf_util_fnv32a(const void* buffer, int length, uint32_t start)
{
uint32_t result = 0x811c9dc5;
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;
}

View File

@ -224,4 +224,13 @@ void tf_util_document_settings(const char* line_prefix);
*/ */
bool tf_util_is_mobile(); bool tf_util_is_mobile();
/**
** Compute a 32-bit hash of a buffer.
** @param buffer The data.
** @param length The size of the buffer in bytes.
** @param start The hash seed.
** @return The computed hash.
*/
uint32_t tf_util_fnv32a(const void* buffer, int length, uint32_t start);
/** @} */ /** @} */