Skip to content

Commit ff9f365

Browse files
committed
lazy stack symbolizer
1 parent cc45d43 commit ff9f365

File tree

2 files changed

+125
-42
lines changed

2 files changed

+125
-42
lines changed

src/njs_error.c

Lines changed: 121 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@
88
#include <njs_main.h>
99

1010

11+
typedef struct {
12+
union {
13+
njs_function_t *function;
14+
u_char *pc;
15+
} u;
16+
uint8_t native;
17+
} njs_stack_entry_t;
18+
19+
1120
typedef struct {
1221
njs_str_t name;
1322
njs_str_t file;
@@ -16,7 +25,7 @@ typedef struct {
1625

1726

1827
static njs_int_t njs_add_backtrace_entry(njs_vm_t *vm, njs_arr_t *stack,
19-
njs_native_frame_t *native_frame);
28+
njs_stack_entry_t *se);
2029
static njs_int_t njs_backtrace_to_string(njs_vm_t *vm, njs_arr_t *backtrace,
2130
njs_str_t *dst);
2231

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

8897

8998
static njs_int_t
90-
njs_error_stack_new(njs_vm_t *vm, njs_object_t *error, njs_value_t *retval)
99+
njs_error_stack_new(njs_vm_t *vm, njs_object_value_t *error)
91100
{
92-
njs_int_t ret;
93-
njs_str_t string;
94101
njs_arr_t *stack;
95-
njs_value_t value;
102+
njs_stack_entry_t *se;
96103
njs_native_frame_t *frame;
97104

98-
njs_set_object(&value, error);
99-
100-
ret = njs_error_to_string(vm, retval, &value);
101-
if (njs_slow_path(ret != NJS_OK)) {
102-
return ret;
103-
}
104-
105-
stack = njs_arr_create(vm->mem_pool, 4, sizeof(njs_backtrace_entry_t));
105+
stack = njs_arr_create(vm->mem_pool, 4, sizeof(njs_stack_entry_t));
106106
if (njs_slow_path(stack == NULL)) {
107107
return NJS_ERROR;
108108
}
109109

110110
frame = vm->top_frame;
111111

112112
for ( ;; ) {
113-
if ((frame->native || frame->pc != NULL)
114-
&& njs_add_backtrace_entry(vm, stack, frame) != NJS_OK)
115-
{
116-
break;
113+
if (frame->native || frame->pc != NULL) {
114+
se = njs_arr_add(stack);
115+
if (njs_slow_path(se == NULL)) {
116+
return NJS_ERROR;
117+
}
118+
119+
se->native = frame->native;
120+
121+
if (se->native) {
122+
se->u.function = frame->function;
123+
124+
} else {
125+
se->u.pc = frame->pc;
126+
}
117127
}
118128

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

126-
njs_string_get(retval, &string);
127-
128-
ret = njs_backtrace_to_string(vm, stack, &string);
129-
130-
njs_arr_destroy(stack);
136+
njs_data(&error->value) = stack;
131137

132-
if (njs_slow_path(ret != NJS_OK)) {
133-
return ret;
134-
}
135-
136-
return njs_string_create(vm, retval, string.start, string.length);
138+
return NJS_OK;
137139
}
138140

139141

140142
njs_int_t
141143
njs_error_stack_attach(njs_vm_t *vm, njs_value_t value)
142144
{
143-
njs_int_t ret;
144-
njs_value_t stack;
145+
njs_int_t ret;
145146

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

156-
ret = njs_error_stack_new(vm, njs_object(&value), &stack);
157+
ret = njs_error_stack_new(vm, value.data.u.object_value);
157158
if (njs_slow_path(ret != NJS_OK)) {
158159
njs_internal_error(vm, "njs_error_stack_new() failed");
159160
return NJS_ERROR;
160161
}
161162

162163
njs_object(&value)->stack_attached = 1;
163164

164-
return njs_object_prop_define(vm, &value,
165-
njs_value_arg(&njs_error_stack_string),
166-
&stack, NJS_OBJECT_PROP_VALUE_CW,
167-
NJS_STACK_HASH);
165+
return NJS_OK;
168166
}
169167

170168

@@ -194,16 +192,20 @@ njs_error_alloc(njs_vm_t *vm, njs_object_t *proto, const njs_value_t *name,
194192
njs_int_t ret;
195193
njs_object_t *error;
196194
njs_object_prop_t *prop;
195+
njs_object_value_t *ov;
197196
njs_lvlhsh_query_t lhq;
198197

199-
error = njs_mp_alloc(vm->mem_pool, sizeof(njs_object_t));
200-
if (njs_slow_path(error == NULL)) {
198+
ov = njs_mp_alloc(vm->mem_pool, sizeof(njs_object_value_t));
199+
if (njs_slow_path(ov == NULL)) {
201200
goto memory_error;
202201
}
203202

203+
njs_set_data(&ov->value, NULL, NJS_DATA_TAG_ANY);
204+
205+
error = &ov->object;
204206
njs_lvlhsh_init(&error->hash);
205207
njs_lvlhsh_init(&error->shared_hash);
206-
error->type = NJS_OBJECT;
208+
error->type = NJS_OBJECT_VALUE;
207209
error->shared = 0;
208210
error->extensible = 1;
209211
error->fast_array = 0;
@@ -691,6 +693,81 @@ njs_error_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
691693
}
692694

693695

696+
static njs_int_t
697+
njs_error_prototype_stack(njs_vm_t *vm, njs_object_prop_t *prop,
698+
njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
699+
{
700+
njs_int_t ret;
701+
njs_str_t string;
702+
njs_arr_t *stack, *backtrace;
703+
njs_uint_t i;
704+
njs_value_t rv;
705+
njs_stack_entry_t *se;
706+
707+
if (!njs_is_error(value)) {
708+
njs_type_error(vm, "\"this\" argument is not an Error object");
709+
return NJS_ERROR;
710+
}
711+
712+
if (retval != NULL) {
713+
if (njs_is_string(njs_object_value(value))) {
714+
*retval = *njs_object_value(value);
715+
return NJS_OK;
716+
}
717+
718+
if (njs_object_data(value) != NULL) {
719+
stack = njs_object_data(value);
720+
se = stack->start;
721+
722+
backtrace = njs_arr_create(vm->mem_pool, stack->items,
723+
sizeof(njs_backtrace_entry_t));
724+
if (njs_slow_path(backtrace == NULL)) {
725+
return NJS_ERROR;
726+
}
727+
728+
for (i = 0; i < stack->items; i++) {
729+
if (njs_add_backtrace_entry(vm, backtrace, &se[i]) != NJS_OK) {
730+
return NJS_ERROR;
731+
}
732+
}
733+
734+
ret = njs_error_to_string2(vm, &rv, value, 0);
735+
if (njs_slow_path(ret != NJS_OK)) {
736+
return ret;
737+
}
738+
739+
njs_string_get(&rv, &string);
740+
741+
ret = njs_backtrace_to_string(vm, backtrace, &string);
742+
743+
njs_arr_destroy(backtrace);
744+
njs_arr_destroy(stack);
745+
746+
if (njs_slow_path(ret != NJS_OK)) {
747+
return ret;
748+
}
749+
750+
ret = njs_string_create(vm, njs_object_value(value), string.start,
751+
string.length);
752+
753+
if (njs_slow_path(ret != NJS_OK)) {
754+
return ret;
755+
}
756+
757+
njs_value_assign(retval, njs_object_value(value));
758+
759+
return NJS_OK;
760+
}
761+
762+
njs_set_undefined(retval);
763+
return NJS_OK;
764+
}
765+
766+
njs_internal_error(vm, "\"stack\" property is read-only");
767+
return NJS_ERROR;
768+
}
769+
770+
694771
njs_int_t
695772
njs_error_to_string(njs_vm_t *vm, njs_value_t *retval, const njs_value_t *error)
696773
{
@@ -717,6 +794,9 @@ static const njs_object_prop_t njs_error_prototype_properties[] =
717794
NJS_DECLARE_PROP_NATIVE("valueOf", njs_error_prototype_value_of, 0, 0),
718795

719796
NJS_DECLARE_PROP_NATIVE("toString", njs_error_prototype_to_string, 0, 0),
797+
798+
NJS_DECLARE_PROP_HANDLER("stack", njs_error_prototype_stack,
799+
0, 0, NJS_OBJECT_PROP_VALUE_CW),
720800
};
721801

722802

@@ -986,14 +1066,14 @@ const njs_object_type_init_t njs_aggregate_error_type_init = {
9861066

9871067
static njs_int_t
9881068
njs_add_backtrace_entry(njs_vm_t *vm, njs_arr_t *stack,
989-
njs_native_frame_t *native_frame)
1069+
njs_stack_entry_t *se)
9901070
{
9911071
njs_int_t ret;
9921072
njs_vm_code_t *code;
9931073
njs_function_t *function;
9941074
njs_backtrace_entry_t *be;
9951075

996-
function = native_frame->function;
1076+
function = se->native ? se->u.function : NULL;
9971077

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

1022-
code = njs_lookup_code(vm, native_frame->pc);
1102+
code = njs_lookup_code(vm, se->u.pc);
10231103

10241104
if (code != NULL) {
10251105
be->name = code->name;
@@ -1028,7 +1108,7 @@ njs_add_backtrace_entry(njs_vm_t *vm, njs_arr_t *stack,
10281108
be->name = njs_entry_anonymous;
10291109
}
10301110

1031-
be->line = njs_lookup_line(code->lines, native_frame->pc - code->start);
1111+
be->line = njs_lookup_line(code->lines, se->u.pc - code->start);
10321112
if (!vm->options.quiet) {
10331113
be->file = code->file;
10341114
}

src/test/njs_unit_test.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15943,6 +15943,7 @@ static njs_unit_test_t njs_test[] =
1594315943
" 'caller',"
1594415944
" 'arguments',"
1594515945
" 'description',"
15946+
" 'stack',"
1594615947
" ];"
1594715948
" return Object.getOwnPropertyNames(o)"
1594815949
" .filter(v => !except.includes(v)"
@@ -15976,6 +15977,7 @@ static njs_unit_test_t njs_test[] =
1597615977
" 'caller',"
1597715978
" 'arguments',"
1597815979
" 'description',"
15980+
" 'stack',"
1597915981
" ];"
1598015982
" return Object.getOwnPropertyNames(o)"
1598115983
" .filter(v => !except.includes(v)"
@@ -18948,6 +18950,7 @@ static njs_unit_test_t njs_test[] =
1894818950
" 'caller',"
1894918951
" 'arguments',"
1895018952
" 'description',"
18953+
" 'stack',"
1895118954
" ];"
1895218955
" return Object.getOwnPropertyNames(o)"
1895318956
" .filter(v => !except.includes(v)"
@@ -22257,7 +22260,7 @@ static njs_unit_test_t njs_backtraces_test[] =
2225722260

2225822261
{ njs_str("$shared.method({}.a.a)"),
2225922262
njs_str("TypeError: cannot get property \"a\" of undefined\n"
22260-
" at $shared.method (native)\n"
22263+
" at External.method (native)\n"
2226122264
" at main (:1)\n") },
2226222265

2226322266
{ njs_str("new Function(\n\n@)"),

0 commit comments

Comments
 (0)