Skip to content

Commit

Permalink
lazy stack symbolizer
Browse files Browse the repository at this point in the history
  • Loading branch information
xeioex committed Oct 16, 2024
1 parent cc45d43 commit ff9f365
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 42 deletions.
162 changes: 121 additions & 41 deletions src/njs_error.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@
#include <njs_main.h>


typedef struct {
union {
njs_function_t *function;
u_char *pc;
} u;
uint8_t native;
} njs_stack_entry_t;


typedef struct {
njs_str_t name;
njs_str_t file;
Expand All @@ -16,7 +25,7 @@ typedef struct {


static njs_int_t njs_add_backtrace_entry(njs_vm_t *vm, njs_arr_t *stack,
njs_native_frame_t *native_frame);
njs_stack_entry_t *se);
static njs_int_t njs_backtrace_to_string(njs_vm_t *vm, njs_arr_t *backtrace,
njs_str_t *dst);

Expand Down Expand Up @@ -87,33 +96,34 @@ njs_error_fmt_new(njs_vm_t *vm, njs_value_t *dst, njs_object_type_t type,


static njs_int_t
njs_error_stack_new(njs_vm_t *vm, njs_object_t *error, njs_value_t *retval)
njs_error_stack_new(njs_vm_t *vm, njs_object_value_t *error)
{
njs_int_t ret;
njs_str_t string;
njs_arr_t *stack;
njs_value_t value;
njs_stack_entry_t *se;
njs_native_frame_t *frame;

njs_set_object(&value, error);

ret = njs_error_to_string(vm, retval, &value);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}

stack = njs_arr_create(vm->mem_pool, 4, sizeof(njs_backtrace_entry_t));
stack = njs_arr_create(vm->mem_pool, 4, sizeof(njs_stack_entry_t));
if (njs_slow_path(stack == NULL)) {
return NJS_ERROR;
}

frame = vm->top_frame;

for ( ;; ) {
if ((frame->native || frame->pc != NULL)
&& njs_add_backtrace_entry(vm, stack, frame) != NJS_OK)
{
break;
if (frame->native || frame->pc != NULL) {
se = njs_arr_add(stack);
if (njs_slow_path(se == NULL)) {
return NJS_ERROR;
}

se->native = frame->native;

if (se->native) {
se->u.function = frame->function;

} else {
se->u.pc = frame->pc;
}
}

frame = frame->previous;
Expand All @@ -123,25 +133,16 @@ njs_error_stack_new(njs_vm_t *vm, njs_object_t *error, njs_value_t *retval)
}
}

njs_string_get(retval, &string);

ret = njs_backtrace_to_string(vm, stack, &string);

njs_arr_destroy(stack);
njs_data(&error->value) = stack;

if (njs_slow_path(ret != NJS_OK)) {
return ret;
}

return njs_string_create(vm, retval, string.start, string.length);
return NJS_OK;
}


njs_int_t
njs_error_stack_attach(njs_vm_t *vm, njs_value_t value)
{
njs_int_t ret;
njs_value_t stack;
njs_int_t ret;

if (njs_slow_path(!njs_is_error(&value))
|| njs_object(&value)->stack_attached)
Expand All @@ -153,18 +154,15 @@ njs_error_stack_attach(njs_vm_t *vm, njs_value_t value)
return NJS_OK;
}

ret = njs_error_stack_new(vm, njs_object(&value), &stack);
ret = njs_error_stack_new(vm, value.data.u.object_value);
if (njs_slow_path(ret != NJS_OK)) {
njs_internal_error(vm, "njs_error_stack_new() failed");
return NJS_ERROR;
}

njs_object(&value)->stack_attached = 1;

return njs_object_prop_define(vm, &value,
njs_value_arg(&njs_error_stack_string),
&stack, NJS_OBJECT_PROP_VALUE_CW,
NJS_STACK_HASH);
return NJS_OK;
}


Expand Down Expand Up @@ -194,16 +192,20 @@ njs_error_alloc(njs_vm_t *vm, njs_object_t *proto, const njs_value_t *name,
njs_int_t ret;
njs_object_t *error;
njs_object_prop_t *prop;
njs_object_value_t *ov;
njs_lvlhsh_query_t lhq;

error = njs_mp_alloc(vm->mem_pool, sizeof(njs_object_t));
if (njs_slow_path(error == NULL)) {
ov = njs_mp_alloc(vm->mem_pool, sizeof(njs_object_value_t));
if (njs_slow_path(ov == NULL)) {
goto memory_error;
}

njs_set_data(&ov->value, NULL, NJS_DATA_TAG_ANY);

error = &ov->object;
njs_lvlhsh_init(&error->hash);
njs_lvlhsh_init(&error->shared_hash);
error->type = NJS_OBJECT;
error->type = NJS_OBJECT_VALUE;
error->shared = 0;
error->extensible = 1;
error->fast_array = 0;
Expand Down Expand Up @@ -691,6 +693,81 @@ njs_error_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
}


static njs_int_t
njs_error_prototype_stack(njs_vm_t *vm, njs_object_prop_t *prop,
njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
{
njs_int_t ret;
njs_str_t string;
njs_arr_t *stack, *backtrace;
njs_uint_t i;
njs_value_t rv;
njs_stack_entry_t *se;

if (!njs_is_error(value)) {
njs_type_error(vm, "\"this\" argument is not an Error object");
return NJS_ERROR;
}

if (retval != NULL) {
if (njs_is_string(njs_object_value(value))) {
*retval = *njs_object_value(value);
return NJS_OK;
}

if (njs_object_data(value) != NULL) {
stack = njs_object_data(value);
se = stack->start;

backtrace = njs_arr_create(vm->mem_pool, stack->items,
sizeof(njs_backtrace_entry_t));
if (njs_slow_path(backtrace == NULL)) {
return NJS_ERROR;
}

for (i = 0; i < stack->items; i++) {
if (njs_add_backtrace_entry(vm, backtrace, &se[i]) != NJS_OK) {
return NJS_ERROR;
}
}

ret = njs_error_to_string2(vm, &rv, value, 0);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}

njs_string_get(&rv, &string);

ret = njs_backtrace_to_string(vm, backtrace, &string);

njs_arr_destroy(backtrace);
njs_arr_destroy(stack);

if (njs_slow_path(ret != NJS_OK)) {
return ret;
}

ret = njs_string_create(vm, njs_object_value(value), string.start,
string.length);

if (njs_slow_path(ret != NJS_OK)) {
return ret;
}

njs_value_assign(retval, njs_object_value(value));

return NJS_OK;
}

njs_set_undefined(retval);
return NJS_OK;
}

njs_internal_error(vm, "\"stack\" property is read-only");
return NJS_ERROR;
}


njs_int_t
njs_error_to_string(njs_vm_t *vm, njs_value_t *retval, const njs_value_t *error)
{
Expand All @@ -717,6 +794,9 @@ static const njs_object_prop_t njs_error_prototype_properties[] =
NJS_DECLARE_PROP_NATIVE("valueOf", njs_error_prototype_value_of, 0, 0),

NJS_DECLARE_PROP_NATIVE("toString", njs_error_prototype_to_string, 0, 0),

NJS_DECLARE_PROP_HANDLER("stack", njs_error_prototype_stack,
0, 0, NJS_OBJECT_PROP_VALUE_CW),
};


Expand Down Expand Up @@ -986,14 +1066,14 @@ const njs_object_type_init_t njs_aggregate_error_type_init = {

static njs_int_t
njs_add_backtrace_entry(njs_vm_t *vm, njs_arr_t *stack,
njs_native_frame_t *native_frame)
njs_stack_entry_t *se)
{
njs_int_t ret;
njs_vm_code_t *code;
njs_function_t *function;
njs_backtrace_entry_t *be;

function = native_frame->function;
function = se->native ? se->u.function : NULL;

if (function != NULL && function->bound != NULL) {
/* Skip. */
Expand All @@ -1019,7 +1099,7 @@ njs_add_backtrace_entry(njs_vm_t *vm, njs_arr_t *stack,
return NJS_OK;
}

code = njs_lookup_code(vm, native_frame->pc);
code = njs_lookup_code(vm, se->u.pc);

if (code != NULL) {
be->name = code->name;
Expand All @@ -1028,7 +1108,7 @@ njs_add_backtrace_entry(njs_vm_t *vm, njs_arr_t *stack,
be->name = njs_entry_anonymous;
}

be->line = njs_lookup_line(code->lines, native_frame->pc - code->start);
be->line = njs_lookup_line(code->lines, se->u.pc - code->start);
if (!vm->options.quiet) {
be->file = code->file;
}
Expand Down
5 changes: 4 additions & 1 deletion src/test/njs_unit_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -15943,6 +15943,7 @@ static njs_unit_test_t njs_test[] =
" 'caller',"
" 'arguments',"
" 'description',"
" 'stack',"
" ];"
" return Object.getOwnPropertyNames(o)"
" .filter(v => !except.includes(v)"
Expand Down Expand Up @@ -15976,6 +15977,7 @@ static njs_unit_test_t njs_test[] =
" 'caller',"
" 'arguments',"
" 'description',"
" 'stack',"
" ];"
" return Object.getOwnPropertyNames(o)"
" .filter(v => !except.includes(v)"
Expand Down Expand Up @@ -18948,6 +18950,7 @@ static njs_unit_test_t njs_test[] =
" 'caller',"
" 'arguments',"
" 'description',"
" 'stack',"
" ];"
" return Object.getOwnPropertyNames(o)"
" .filter(v => !except.includes(v)"
Expand Down Expand Up @@ -22257,7 +22260,7 @@ static njs_unit_test_t njs_backtraces_test[] =

{ njs_str("$shared.method({}.a.a)"),
njs_str("TypeError: cannot get property \"a\" of undefined\n"
" at $shared.method (native)\n"
" at External.method (native)\n"
" at main (:1)\n") },

{ njs_str("new Function(\n\n@)"),
Expand Down

0 comments on commit ff9f365

Please sign in to comment.