Track and expose hitches in some suspect callbacks.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4261 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
parent
30014040e7
commit
956ea0df56
@ -861,6 +861,10 @@ loadSettings().then(function() {
|
|||||||
let data = JSON.stringify(getDebug(), null, 2);
|
let data = JSON.stringify(getDebug(), null, 2);
|
||||||
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 = /^\/hitches$/.exec(request.uri)) {
|
||||||
|
let data = JSON.stringify(getHitches(), null, 2);
|
||||||
|
response.writeHead(200, {"Content-Type": "application/json; charset=utf-8", "Content-Length": data.length.toString()});
|
||||||
|
return response.end(data);
|
||||||
} else if (match = /^\/mem$/.exec(request.uri)) {
|
} else if (match = /^\/mem$/.exec(request.uri)) {
|
||||||
let data = JSON.stringify(getAllocations(), null, 2);
|
let data = JSON.stringify(getAllocations(), null, 2);
|
||||||
response.writeHead(200, {
|
response.writeHead(200, {
|
||||||
|
36
src/ssb.c
36
src/ssb.c
@ -41,6 +41,9 @@
|
|||||||
#define CYAN "\e[1;36m"
|
#define CYAN "\e[1;36m"
|
||||||
#define RESET "\e[0m"
|
#define RESET "\e[0m"
|
||||||
|
|
||||||
|
#define PRE_CALLBACK(ssb, cb) uint64_t pre_callback_hrtime_ns = _tf_ssb_callback_pre(ssb)
|
||||||
|
#define POST_CALLBACK(ssb, cb) _tf_ssb_callback_post(ssb, cb, pre_callback_hrtime_ns)
|
||||||
|
|
||||||
static_assert(k_id_base64_len == sodium_base64_ENCODED_LEN(9 + crypto_box_PUBLICKEYBYTES, sodium_base64_VARIANT_ORIGINAL), "k_id_base64_len");
|
static_assert(k_id_base64_len == sodium_base64_ENCODED_LEN(9 + crypto_box_PUBLICKEYBYTES, sodium_base64_VARIANT_ORIGINAL), "k_id_base64_len");
|
||||||
static_assert(k_id_bin_len == crypto_box_PUBLICKEYBYTES, "k_id_bin_len");
|
static_assert(k_id_bin_len == crypto_box_PUBLICKEYBYTES, "k_id_bin_len");
|
||||||
static_assert(k_blob_id_len == (sodium_base64_ENCODED_LEN(crypto_hash_sha256_BYTES, sodium_base64_VARIANT_ORIGINAL) + 8), "k_blob_id_len");
|
static_assert(k_blob_id_len == (sodium_base64_ENCODED_LEN(crypto_hash_sha256_BYTES, sodium_base64_VARIANT_ORIGINAL) + 8), "k_blob_id_len");
|
||||||
@ -229,6 +232,9 @@ typedef struct _tf_ssb_t
|
|||||||
|
|
||||||
tf_thread_work_time_t* thread_time;
|
tf_thread_work_time_t* thread_time;
|
||||||
int thread_time_count;
|
int thread_time_count;
|
||||||
|
|
||||||
|
void (*hitch_callback)(const char* name, uint64_t duration, void* user_data);
|
||||||
|
void* hitch_user_data;
|
||||||
} tf_ssb_t;
|
} tf_ssb_t;
|
||||||
|
|
||||||
typedef struct _tf_ssb_connection_message_request_t
|
typedef struct _tf_ssb_connection_message_request_t
|
||||||
@ -1008,6 +1014,22 @@ bool tf_ssb_id_str_to_bin(uint8_t* bin, const char* str)
|
|||||||
return tf_base64_decode(author_id, type - author_id, bin, crypto_box_PUBLICKEYBYTES) != 0;
|
return tf_base64_decode(author_id, type - author_id, bin, crypto_box_PUBLICKEYBYTES) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint64_t _tf_ssb_callback_pre(tf_ssb_t* ssb)
|
||||||
|
{
|
||||||
|
return uv_hrtime();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _tf_ssb_callback_post(tf_ssb_t* ssb, void* callback, uint64_t pre)
|
||||||
|
{
|
||||||
|
if (ssb->hitch_callback)
|
||||||
|
{
|
||||||
|
uint64_t post = uv_hrtime();
|
||||||
|
const char* name = tf_util_function_to_string(callback);
|
||||||
|
ssb->hitch_callback(name, post - pre, ssb->hitch_user_data);
|
||||||
|
tf_free((void*)name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void _tf_ssb_notify_connections_changed(tf_ssb_t* ssb, tf_ssb_change_t change, tf_ssb_connection_t* connection)
|
static void _tf_ssb_notify_connections_changed(tf_ssb_t* ssb, tf_ssb_change_t change, tf_ssb_connection_t* connection)
|
||||||
{
|
{
|
||||||
tf_ssb_connections_changed_callback_node_t* next = NULL;
|
tf_ssb_connections_changed_callback_node_t* next = NULL;
|
||||||
@ -1015,7 +1037,9 @@ static void _tf_ssb_notify_connections_changed(tf_ssb_t* ssb, tf_ssb_change_t ch
|
|||||||
{
|
{
|
||||||
next = node->next;
|
next = node->next;
|
||||||
tf_trace_begin(ssb->trace, "connections_changed");
|
tf_trace_begin(ssb->trace, "connections_changed");
|
||||||
|
PRE_CALLBACK(ssb, node->callback);
|
||||||
node->callback(ssb, change, connection, node->user_data);
|
node->callback(ssb, change, connection, node->user_data);
|
||||||
|
POST_CALLBACK(ssb, node->callback);
|
||||||
tf_trace_end(ssb->trace);
|
tf_trace_end(ssb->trace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1451,7 +1475,9 @@ static void _tf_ssb_connection_rpc_recv(tf_ssb_connection_t* connection, uint8_t
|
|||||||
if (_tf_ssb_name_equals(context, val, it->name))
|
if (_tf_ssb_name_equals(context, val, it->name))
|
||||||
{
|
{
|
||||||
tf_trace_begin(connection->ssb->trace, it->flattened_name);
|
tf_trace_begin(connection->ssb->trace, it->flattened_name);
|
||||||
|
PRE_CALLBACK(connection->ssb, it->callback);
|
||||||
it->callback(connection, flags, request_number, val, message, size, it->user_data);
|
it->callback(connection, flags, request_number, val, message, size, it->user_data);
|
||||||
|
POST_CALLBACK(connection->ssb, it->callback);
|
||||||
tf_trace_end(connection->ssb->trace);
|
tf_trace_end(connection->ssb->trace);
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
@ -1469,7 +1495,9 @@ static void _tf_ssb_connection_rpc_recv(tf_ssb_connection_t* connection, uint8_t
|
|||||||
char buffer[64];
|
char buffer[64];
|
||||||
snprintf(buffer, sizeof(buffer), "request %d", request_number);
|
snprintf(buffer, sizeof(buffer), "request %d", request_number);
|
||||||
tf_trace_begin(connection->ssb->trace, buffer);
|
tf_trace_begin(connection->ssb->trace, buffer);
|
||||||
|
PRE_CALLBACK(connection->ssb, callback);
|
||||||
callback(connection, flags, request_number, val, message, size, user_data);
|
callback(connection, flags, request_number, val, message, size, user_data);
|
||||||
|
POST_CALLBACK(connection->ssb, callback);
|
||||||
tf_trace_end(connection->ssb->trace);
|
tf_trace_end(connection->ssb->trace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1501,7 +1529,9 @@ static void _tf_ssb_connection_rpc_recv(tf_ssb_connection_t* connection, uint8_t
|
|||||||
char buffer[64];
|
char buffer[64];
|
||||||
snprintf(buffer, sizeof(buffer), "request %d", request_number);
|
snprintf(buffer, sizeof(buffer), "request %d", request_number);
|
||||||
tf_trace_begin(connection->ssb->trace, buffer);
|
tf_trace_begin(connection->ssb->trace, buffer);
|
||||||
|
PRE_CALLBACK(connection->ssb, callback);
|
||||||
callback(connection, flags, request_number, JS_UNDEFINED, message, size, user_data);
|
callback(connection, flags, request_number, JS_UNDEFINED, message, size, user_data);
|
||||||
|
POST_CALLBACK(connection->ssb, callback);
|
||||||
tf_trace_end(connection->ssb->trace);
|
tf_trace_end(connection->ssb->trace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3462,3 +3492,9 @@ uint64_t tf_ssb_get_average_thread_time(tf_ssb_t* ssb)
|
|||||||
}
|
}
|
||||||
return ssb->thread_time_count ? total / ssb->thread_time_count : 0;
|
return ssb->thread_time_count ? total / ssb->thread_time_count : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tf_ssb_set_hitch_callback(tf_ssb_t* ssb, void (*callback)(const char* name, uint64_t duration_ns, void* user_data), void* user_data)
|
||||||
|
{
|
||||||
|
ssb->hitch_callback = callback;
|
||||||
|
ssb->hitch_user_data = user_data;
|
||||||
|
}
|
||||||
|
@ -189,3 +189,5 @@ JSValue tf_ssb_get_disconnection_debug(tf_ssb_t* ssb, JSContext* context);
|
|||||||
|
|
||||||
void tf_ssb_record_thread_time(tf_ssb_t* ssb, int64_t thread_id, uint64_t hrtime);
|
void tf_ssb_record_thread_time(tf_ssb_t* ssb, int64_t thread_id, uint64_t hrtime);
|
||||||
uint64_t tf_ssb_get_average_thread_time(tf_ssb_t* ssb);
|
uint64_t tf_ssb_get_average_thread_time(tf_ssb_t* ssb);
|
||||||
|
|
||||||
|
void tf_ssb_set_hitch_callback(tf_ssb_t* ssb, void (*callback)(const char* name, uint64_t duration_ns, void* user_data), void* user_data);
|
||||||
|
48
src/task.c
48
src/task.c
@ -34,6 +34,10 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if !defined(_countof)
|
||||||
|
#define _countof(a) ((int)(sizeof((a)) / sizeof(*(a))))
|
||||||
|
#endif
|
||||||
|
|
||||||
static const char* k_version = "1.0";
|
static const char* k_version = "1.0";
|
||||||
|
|
||||||
static JSClassID _import_class_id;
|
static JSClassID _import_class_id;
|
||||||
@ -71,6 +75,12 @@ typedef struct _promise_stack_t
|
|||||||
int count;
|
int count;
|
||||||
} promise_stack_t;
|
} promise_stack_t;
|
||||||
|
|
||||||
|
typedef struct _hitch_t
|
||||||
|
{
|
||||||
|
char name[256];
|
||||||
|
uint64_t duration_ns;
|
||||||
|
} hitch_t;
|
||||||
|
|
||||||
typedef struct _tf_task_t
|
typedef struct _tf_task_t
|
||||||
{
|
{
|
||||||
taskid_t _nextTask;
|
taskid_t _nextTask;
|
||||||
@ -130,6 +140,8 @@ typedef struct _tf_task_t
|
|||||||
|
|
||||||
promise_stack_t* _promise_stacks;
|
promise_stack_t* _promise_stacks;
|
||||||
int _promise_stack_count;
|
int _promise_stack_count;
|
||||||
|
|
||||||
|
hitch_t hitches[32];
|
||||||
} tf_task_t;
|
} tf_task_t;
|
||||||
|
|
||||||
typedef struct _export_record_t
|
typedef struct _export_record_t
|
||||||
@ -870,6 +882,22 @@ static JSValue _tf_task_getDebug(JSContext* context, JSValueConst this_val, int
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static JSValue _tf_task_getHitches(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||||
|
{
|
||||||
|
tf_task_t* task = JS_GetContextOpaque(context);
|
||||||
|
tf_trace_begin(task->_trace, __func__);
|
||||||
|
JSValue result = JS_NewObject(context);
|
||||||
|
for (int i = 0; i < _countof(task->hitches); i++)
|
||||||
|
{
|
||||||
|
if (*task->hitches[i].name)
|
||||||
|
{
|
||||||
|
JS_SetPropertyStr(context, result, task->hitches[i].name, JS_NewFloat64(context, task->hitches[i].duration_ns / 1e9f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tf_trace_end(task->_trace);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static JSValue _tf_task_getAllocations(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
static JSValue _tf_task_getAllocations(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||||
{
|
{
|
||||||
tf_task_t* task = JS_GetContextOpaque(context);
|
tf_task_t* task = JS_GetContextOpaque(context);
|
||||||
@ -1576,6 +1604,24 @@ static void _tf_task_trace_to_parent(tf_trace_t* trace, const char* buffer, size
|
|||||||
tf_packetstream_send(tf_taskstub_get_stream(task->_parent), kTaskTrace, buffer, size);
|
tf_packetstream_send(tf_taskstub_get_stream(task->_parent), kTaskTrace, buffer, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _tf_task_record_hitch(const char* name, uint64_t duration_ns, void* user_data)
|
||||||
|
{
|
||||||
|
tf_task_t* task = user_data;
|
||||||
|
for (int i = 0; i < _countof(task->hitches); i++)
|
||||||
|
{
|
||||||
|
if (duration_ns > task->hitches[i].duration_ns)
|
||||||
|
{
|
||||||
|
if (i + 1 < _countof(task->hitches))
|
||||||
|
{
|
||||||
|
memmove(task->hitches + i + 1, task->hitches + i, sizeof(hitch_t) * (_countof(task->hitches) - i - 1));
|
||||||
|
}
|
||||||
|
snprintf(task->hitches[i].name, sizeof(task->hitches[i].name), "%s", name);
|
||||||
|
task->hitches[i].duration_ns = duration_ns;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void tf_task_activate(tf_task_t* task)
|
void tf_task_activate(tf_task_t* task)
|
||||||
{
|
{
|
||||||
assert(!task->_activated);
|
assert(!task->_activated);
|
||||||
@ -1643,6 +1689,7 @@ void tf_task_activate(tf_task_t* task)
|
|||||||
task->_ssb = tf_ssb_create(&task->_loop, task->_context, task->_db_path);
|
task->_ssb = tf_ssb_create(&task->_loop, task->_context, task->_db_path);
|
||||||
tf_ssb_set_trace(task->_ssb, task->_trace);
|
tf_ssb_set_trace(task->_ssb, task->_trace);
|
||||||
tf_ssb_register(context, task->_ssb);
|
tf_ssb_register(context, task->_ssb);
|
||||||
|
tf_ssb_set_hitch_callback(task->_ssb, _tf_task_record_hitch, task);
|
||||||
|
|
||||||
if (task->_ssb_port)
|
if (task->_ssb_port)
|
||||||
{
|
{
|
||||||
@ -1654,6 +1701,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));
|
JS_SetPropertyStr(context, global, "getDebug", JS_NewCFunction(context, _tf_task_getDebug, "getDebug", 0));
|
||||||
|
JS_SetPropertyStr(context, global, "getHitches", JS_NewCFunction(context, _tf_task_getHitches, "getHitches", 0));
|
||||||
JS_SetPropertyStr(context, global, "getAllocations", JS_NewCFunction(context, _tf_task_getAllocations, "getAllocations", 0));
|
JS_SetPropertyStr(context, global, "getAllocations", JS_NewCFunction(context, _tf_task_getAllocations, "getAllocations", 0));
|
||||||
JS_SetPropertyStr(context, global, "disconnectionsDebug", JS_NewCFunction(context, _tf_task_disconnectionsDebug, "disconnectionsDebug", 0));
|
JS_SetPropertyStr(context, global, "disconnectionsDebug", JS_NewCFunction(context, _tf_task_disconnectionsDebug, "disconnectionsDebug", 0));
|
||||||
}
|
}
|
||||||
|
@ -462,6 +462,30 @@ const char* tf_util_backtrace_to_string(void* const* buffer, int count)
|
|||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int _tf_util_backtrace_single_callback(void* data, uintptr_t pc, const char* filename, int line_number, const char* function)
|
||||||
|
{
|
||||||
|
char** stack = data;
|
||||||
|
char line[256];
|
||||||
|
int length = snprintf(line, sizeof(line), "%s", function);
|
||||||
|
int current = *stack ? strlen(*stack) : 0;
|
||||||
|
*stack = tf_resize_vec(*stack, current + length + 1);
|
||||||
|
memcpy(*stack + current, line, length + 1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* tf_util_function_to_string(void* function)
|
||||||
|
{
|
||||||
|
extern struct backtrace_state* g_backtrace_state;
|
||||||
|
char* string = NULL;
|
||||||
|
backtrace_pcinfo(
|
||||||
|
g_backtrace_state,
|
||||||
|
(uintptr_t)function,
|
||||||
|
_tf_util_backtrace_single_callback,
|
||||||
|
_tf_util_backtrace_error,
|
||||||
|
&string);
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
const char* tf_util_backtrace_string()
|
const char* tf_util_backtrace_string()
|
||||||
{
|
{
|
||||||
void* buffer[32];
|
void* buffer[32];
|
||||||
|
@ -19,3 +19,5 @@ size_t tf_base64_decode(const char* source, size_t source_length, uint8_t* out,
|
|||||||
int tf_util_backtrace(void** buffer, int count);
|
int tf_util_backtrace(void** buffer, int count);
|
||||||
const char* tf_util_backtrace_to_string(void* const* buffer, int count);
|
const char* tf_util_backtrace_to_string(void* const* buffer, int count);
|
||||||
const char* tf_util_backtrace_string();
|
const char* tf_util_backtrace_string();
|
||||||
|
|
||||||
|
const char* tf_util_function_to_string(void* function);
|
||||||
|
Loading…
Reference in New Issue
Block a user