[njs] Introduced function level backtrace.

Dmitry Volyntsev xeioex at nginx.com
Tue Aug 29 11:29:01 UTC 2017


details:   http://hg.nginx.org/njs/rev/82dc332cb4c8
branches:  
changeset: 400:82dc332cb4c8
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Tue Aug 29 14:06:23 2017 +0300
description:
Introduced function level backtrace.

diffstat:

 njs/njs.c                       |   40 +++++++++++-
 njs/njs_builtin.c               |  133 ++++++++++++++++++++++++++++++++++++++++
 njs/njs_generator.c             |   48 ++++++++++++++-
 njs/njs_parser.c                |    2 +
 njs/njs_vm.c                    |   77 +++++++++++++++++++++++
 njs/njs_vm.h                    |   12 +++
 njs/njscript.c                  |   38 +++++++++++
 njs/njscript.h                  |    8 ++
 njs/test/njs_interactive_test.c |  102 ++++++++++++++++++++++++++++++-
 9 files changed, 454 insertions(+), 6 deletions(-)

diffs (731 lines):

diff -r 746c49e6c195 -r 82dc332cb4c8 njs/njs.c
--- a/njs/njs.c	Thu Aug 24 16:16:37 2017 +0300
+++ b/njs/njs.c	Tue Aug 29 14:06:23 2017 +0300
@@ -59,6 +59,7 @@ static nxt_int_t njs_interactive_shell(n
 static nxt_int_t njs_process_file(njs_opts_t *opts, njs_vm_opt_t *vm_options);
 static nxt_int_t njs_process_script(njs_vm_t *vm, njs_opts_t *opts,
     const nxt_str_t *script, nxt_str_t *out);
+static void njs_print_backtrace(nxt_array_t *backtrace);
 static nxt_int_t njs_editline_init(njs_vm_t *vm);
 static char **njs_completion_handler(const char *text, int start, int end);
 static char *njs_completion_generator(const char *text, int state);
@@ -93,6 +94,7 @@ main(int argc, char **argv)
 
     vm_options.mcp = mcp;
     vm_options.accumulative = 1;
+    vm_options.backtrace = 1;
 
     if (opts.interactive) {
         ret = njs_interactive_shell(&opts, &vm_options);
@@ -150,9 +152,10 @@ njs_get_options(njs_opts_t *opts, int ar
 static nxt_int_t
 njs_interactive_shell(njs_opts_t *opts, njs_vm_opt_t *vm_options)
 {
-    njs_vm_t   *vm;
-    nxt_int_t  ret;
-    nxt_str_t  line, out;
+    njs_vm_t     *vm;
+    nxt_int_t    ret;
+    nxt_str_t    line, out;
+    nxt_array_t  *backtrace;
 
     vm = njs_vm_create(vm_options);
     if (vm == NULL) {
@@ -188,6 +191,11 @@ njs_interactive_shell(njs_opts_t *opts, 
 
         printf("%.*s\n", (int) out.length, out.start);
 
+        backtrace = njs_vm_backtrace(vm);
+        if (backtrace != NULL) {
+            njs_print_backtrace(backtrace);
+        }
+
         /* editline allocs a new buffer every time. */
         free(line.start);
     }
@@ -208,6 +216,7 @@ njs_process_file(njs_opts_t *opts, njs_v
     nxt_int_t    ret;
     nxt_str_t    out, script;
     struct stat  sb;
+    nxt_array_t  *backtrace;
 
     file = opts->file;
 
@@ -287,6 +296,11 @@ njs_process_file(njs_opts_t *opts, njs_v
 
     if (!opts->disassemble) {
         printf("%.*s\n", (int) out.length, out.start);
+
+        backtrace = njs_vm_backtrace(vm);
+        if (backtrace != NULL) {
+            njs_print_backtrace(backtrace);
+        }
     }
 
     return NXT_OK;
@@ -329,6 +343,26 @@ njs_process_script(njs_vm_t *vm, njs_opt
 }
 
 
+static void
+njs_print_backtrace(nxt_array_t *backtrace)
+{
+    nxt_uint_t             i;
+    njs_backtrace_entry_t  *be;
+
+    be = backtrace->start;
+
+    for (i = 0; i < backtrace->items; i++) {
+        if (be[i].line != 0) {
+            printf("at %.*s (:%d)\n", (int) be[i].name.length, be[i].name.start,
+                   be[i].line);
+
+        } else {
+            printf("at %.*s\n", (int) be[i].name.length, be[i].name.start);
+        }
+    }
+}
+
+
 static nxt_int_t
 njs_editline_init(njs_vm_t *vm)
 {
diff -r 746c49e6c195 -r 82dc332cb4c8 njs/njs_builtin.c
--- a/njs/njs_builtin.c	Thu Aug 24 16:16:37 2017 +0300
+++ b/njs/njs_builtin.c	Tue Aug 29 14:06:23 2017 +0300
@@ -514,3 +514,136 @@ njs_builtin_completions(njs_vm_t *vm, si
 
     return NXT_OK;
 }
+
+
+nxt_int_t
+njs_builtin_match_native_function(njs_vm_t *vm, njs_function_t *function,
+    nxt_str_t *name)
+{
+    char                    *buf;
+    size_t                  len;
+    nxt_str_t               string;
+    nxt_uint_t              i;
+    njs_object_t            *objects;
+    njs_function_t          *constructors;
+    njs_object_prop_t       *prop;
+    nxt_lvlhsh_each_t       lhe;
+    njs_object_prototype_t  *prototypes;
+
+    objects = vm->shared->objects;
+
+    for (i = NJS_OBJECT_THIS; i < NJS_OBJECT_MAX; i++) {
+        if (object_init[i] == NULL) {
+            continue;
+        }
+
+        nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
+
+        for ( ;; ) {
+            prop = nxt_lvlhsh_each(&objects[i].shared_hash, &lhe);
+
+            if (prop == NULL) {
+                break;
+            }
+
+            if (!njs_is_function(&prop->value)) {
+                continue;
+            }
+
+            if (function == prop->value.data.u.function) {
+                njs_string_get(&prop->name, &string);
+                len = object_init[i]->name.length + string.length
+                      + sizeof(".");
+
+                buf = nxt_mem_cache_zalloc(vm->mem_cache_pool, len);
+                if (buf == NULL) {
+                    return NXT_ERROR;
+                }
+
+                snprintf(buf, len, "%s.%s", object_init[i]->name.start,
+                         string.start);
+
+                name->length = len;
+                name->start = (u_char *) buf;
+
+                return NXT_OK;
+            }
+        }
+    }
+
+    prototypes = vm->shared->prototypes;
+
+    for (i = NJS_PROTOTYPE_OBJECT; i < NJS_PROTOTYPE_MAX; i++) {
+        nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
+
+        for ( ;; ) {
+            prop = nxt_lvlhsh_each(&prototypes[i].object.shared_hash, &lhe);
+
+            if (prop == NULL) {
+                break;
+            }
+
+            if (!njs_is_function(&prop->value)) {
+                continue;
+            }
+
+            if (function == prop->value.data.u.function) {
+                njs_string_get(&prop->name, &string);
+                len = prototype_init[i]->name.length + string.length
+                      + sizeof(".prototype.");
+
+                buf = nxt_mem_cache_zalloc(vm->mem_cache_pool, len);
+                if (buf == NULL) {
+                    return NXT_ERROR;
+                }
+
+                snprintf(buf, len, "%s.prototype.%s",
+                         prototype_init[i]->name.start, string.start);
+
+                name->length = len;
+                name->start = (u_char *) buf;
+
+                return NXT_OK;
+            }
+        }
+    }
+
+    constructors = vm->shared->constructors;
+
+    for (i = NJS_CONSTRUCTOR_OBJECT; i < NJS_CONSTRUCTOR_MAX; i++) {
+        nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
+
+        for ( ;; ) {
+            prop = nxt_lvlhsh_each(&constructors[i].object.shared_hash, &lhe);
+
+            if (prop == NULL) {
+                break;
+            }
+
+            if (!njs_is_function(&prop->value)) {
+                continue;
+            }
+
+            if (function == prop->value.data.u.function) {
+                njs_string_get(&prop->name, &string);
+                len = constructor_init[i]->name.length + string.length
+                      + sizeof(".");
+
+                buf = nxt_mem_cache_zalloc(vm->mem_cache_pool, len);
+                if (buf == NULL) {
+                    return NXT_ERROR;
+                }
+
+                snprintf(buf, len, "%s.%s", constructor_init[i]->name.start,
+                         string.start);
+
+                name->length = len;
+                name->start = (u_char *) buf;
+
+                return NXT_OK;
+            }
+        }
+    }
+
+    return NXT_DECLINED;
+}
diff -r 746c49e6c195 -r 82dc332cb4c8 njs/njs_generator.c
--- a/njs/njs_generator.c	Thu Aug 24 16:16:37 2017 +0300
+++ b/njs/njs_generator.c	Tue Aug 29 14:06:23 2017 +0300
@@ -123,6 +123,9 @@ static nxt_noinline nxt_int_t njs_genera
 static nxt_noinline nxt_int_t njs_generator_index_release(njs_vm_t *vm,
     njs_parser_t *parser, njs_index_t index);
 
+static nxt_int_t njs_generate_function_debug(njs_vm_t *vm, nxt_str_t *name,
+    njs_function_lambda_t *lambda, uint32_t line);
+
 
 static const nxt_str_t  no_label = { 0, NULL };
 
@@ -1609,6 +1612,13 @@ njs_generate_function(njs_vm_t *vm, njs_
         return ret;
     }
 
+    if (vm->debug != NULL) {
+        ret = njs_generate_function_debug(vm, NULL, lambda, node->token_line);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return ret;
+        }
+    }
+
     njs_generate_code(parser, njs_vmcode_function_t, function);
     function->code.operation = njs_vmcode_function;
     function->code.operands = NJS_VMCODE_1OPERAND;
@@ -1964,6 +1974,7 @@ static nxt_int_t
 njs_generate_function_declaration(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *node)
 {
+    nxt_int_t              ret;
     njs_variable_t         *var;
     njs_function_lambda_t  *lambda;
 
@@ -1974,7 +1985,17 @@ njs_generate_function_declaration(njs_vm
 
     lambda = var->value.data.u.function->u.lambda;
 
-    return njs_generate_function_scope(vm, lambda, node);
+    ret = njs_generate_function_scope(vm, lambda, node);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return ret;
+    }
+
+    if (vm->debug != NULL) {
+        ret = njs_generate_function_debug(vm, &var->name, lambda,
+                                          node->token_line);
+    }
+
+    return ret;
 }
 
 
@@ -2628,3 +2649,28 @@ njs_generator_index_release(njs_vm_t *vm
 
     return NXT_ERROR;
 }
+
+
+static nxt_int_t
+njs_generate_function_debug(njs_vm_t *vm, nxt_str_t *name,
+    njs_function_lambda_t *lambda, uint32_t line)
+{
+    njs_function_debug_t  *debug;
+
+    debug = nxt_array_add(vm->debug, &njs_array_mem_proto, vm->mem_cache_pool);
+    if (nxt_slow_path(debug == NULL)) {
+        return NXT_ERROR;
+    }
+
+    if (name != NULL) {
+        debug->name = *name;
+
+    } else {
+        debug->name = no_label;
+    }
+
+    debug->lambda = lambda;
+    debug->line = line;
+
+    return NXT_OK;
+}
diff -r 746c49e6c195 -r 82dc332cb4c8 njs/njs_parser.c
--- a/njs/njs_parser.c	Thu Aug 24 16:16:37 2017 +0300
+++ b/njs/njs_parser.c	Tue Aug 29 14:06:23 2017 +0300
@@ -465,6 +465,7 @@ njs_parser_function_declaration(njs_vm_t
     }
 
     node->token = NJS_TOKEN_FUNCTION;
+    node->token_line = parser->lexer->token_line;
 
     token = njs_parser_token(parser);
     if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
@@ -532,6 +533,7 @@ njs_parser_function_expression(njs_vm_t 
     }
 
     node->token = NJS_TOKEN_FUNCTION_EXPRESSION;
+    node->token_line = parser->lexer->token_line;
     node->scope = parser->scope;
     parser->node = node;
     parser->code_size += sizeof(njs_vmcode_function_t);
diff -r 746c49e6c195 -r 82dc332cb4c8 njs/njs_vm.c
--- a/njs/njs_vm.c	Thu Aug 24 16:16:37 2017 +0300
+++ b/njs/njs_vm.c	Tue Aug 29 14:06:23 2017 +0300
@@ -113,6 +113,8 @@ static njs_ret_t njs_object_value_to_str
 static njs_ret_t njs_vmcode_value_to_string(njs_vm_t *vm, njs_value_t *invld1,
     njs_value_t *invld2);
 
+static njs_ret_t njs_vm_add_backtrace_entry(njs_vm_t *vm, njs_frame_t *frame);
+
 void njs_debug(njs_index_t index, njs_value_t *value);
 
 
@@ -264,9 +266,20 @@ start:
 
             if (catch != NULL) {
                 vm->current = catch;
+
+                if (vm->debug != NULL) {
+                    nxt_array_reset(vm->backtrace);
+                }
+
                 goto start;
             }
 
+            if (vm->debug != NULL
+                && njs_vm_add_backtrace_entry(vm, frame) != NXT_OK)
+            {
+                return NXT_ERROR;
+            }
+
             previous = frame->native.previous;
             if (previous == NULL) {
                 return NXT_ERROR;
@@ -3433,6 +3446,70 @@ njs_vm_throw_exception(njs_vm_t *vm, con
 }
 
 
+static njs_ret_t
+njs_vm_add_backtrace_entry(njs_vm_t *vm, njs_frame_t *frame)
+{
+    nxt_int_t              ret;
+    nxt_uint_t             i;
+    njs_function_t         *function;
+    njs_native_frame_t     *native_frame;
+    njs_function_debug_t   *debug_entry;
+    njs_function_lambda_t  *lambda;
+    njs_backtrace_entry_t  *be;
+
+    static const nxt_str_t  entry_main =        nxt_string("main");
+    static const nxt_str_t  entry_native =      nxt_string("native");
+    static const nxt_str_t  entry_unknown =     nxt_string("unknown");
+    static const nxt_str_t  entry_anonymous =   nxt_string("anonymous");
+
+    native_frame = &frame->native;
+    function = native_frame->function;
+
+    be = nxt_array_add(vm->backtrace, &njs_array_mem_proto, vm->mem_cache_pool);
+    if (nxt_slow_path(be == NULL)) {
+        return NXT_ERROR;
+    }
+
+    be->line = 0;
+
+    if (function == NULL) {
+        be->name = entry_main;
+        return NXT_OK;
+    }
+
+    if (function->native) {
+        ret = njs_builtin_match_native_function(vm, function, &be->name);
+        if (ret != NXT_OK) {
+            be->name = entry_native;
+        }
+
+        return NXT_OK;
+    }
+
+    lambda = function->u.lambda;
+    debug_entry = vm->debug->start;
+
+    for (i = 0; i < vm->debug->items; i++) {
+        if (lambda == debug_entry[i].lambda) {
+            if (debug_entry[i].name.length != 0) {
+                be->name = debug_entry[i].name;
+
+            } else {
+                be->name = entry_anonymous;
+            }
+
+            be->line = debug_entry[i].line;
+
+            return NXT_OK;
+        }
+    }
+
+    be->name = entry_unknown;
+
+    return NXT_OK;
+}
+
+
 void
 njs_debug(njs_index_t index, njs_value_t *value)
 {
diff -r 746c49e6c195 -r 82dc332cb4c8 njs/njs_vm.h
--- a/njs/njs_vm.h	Thu Aug 24 16:16:37 2017 +0300
+++ b/njs/njs_vm.h	Tue Aug 29 14:06:23 2017 +0300
@@ -853,6 +853,13 @@ typedef struct {
 } njs_vm_trap_t;
 
 
+typedef struct {
+    uint32_t                  line;
+    nxt_str_t                 name;
+    njs_function_lambda_t     *lambda;
+} njs_function_debug_t;
+
+
 struct njs_vm_s {
     /* njs_vm_t must be aligned to njs_value_t due to scratch value. */
     njs_value_t              retval;
@@ -903,6 +910,9 @@ struct njs_vm_s {
     nxt_trace_t              trace;
     nxt_random_t             random;
 
+    nxt_array_t              *debug;
+    nxt_array_t              *backtrace;
+
     uint8_t                  trailer;  /* 1 bit */
     uint8_t                  accumulative; /* 1 bit */
 };
@@ -1080,6 +1090,8 @@ void njs_vm_throw_exception(njs_vm_t *vm
 
 nxt_int_t njs_builtin_objects_create(njs_vm_t *vm);
 nxt_int_t njs_builtin_objects_clone(njs_vm_t *vm);
+nxt_int_t njs_builtin_match_native_function(njs_vm_t *vm,
+    njs_function_t *function, nxt_str_t *name);
 
 
 void *njs_lvlhsh_alloc(void *data, size_t size, nxt_uint_t nalloc);
diff -r 746c49e6c195 -r 82dc332cb4c8 njs/njscript.c
--- a/njs/njscript.c	Thu Aug 24 16:16:37 2017 +0300
+++ b/njs/njscript.c	Tue Aug 29 14:06:23 2017 +0300
@@ -106,6 +106,7 @@ njs_vm_create(njs_vm_opt_t *options)
 {
     njs_vm_t              *vm;
     nxt_int_t             ret;
+    nxt_array_t           *debug;
     nxt_mem_cache_pool_t  *mcp;
     njs_regexp_pattern_t  *pattern;
 
@@ -176,6 +177,17 @@ njs_vm_create(njs_vm_opt_t *options)
 
         vm->trailer = options->trailer;
 
+        if (options->backtrace) {
+            debug = nxt_array_create(4, sizeof(njs_function_debug_t),
+                                     &njs_array_mem_proto,
+                                     vm->mem_cache_pool);
+            if (nxt_slow_path(debug == NULL)) {
+                return NULL;
+            }
+
+            vm->debug = debug;
+        }
+
         vm->accumulative = options->accumulative;
         if (vm->accumulative) {
             ret = njs_vm_init(vm);
@@ -248,6 +260,10 @@ njs_vm_compile(njs_vm_t *vm, u_char **st
      */
     vm->code = NULL;
 
+    if (vm->backtrace != NULL) {
+        nxt_array_reset(vm->backtrace);
+    }
+
     ret = njs_generate_scope(vm, parser, node);
     if (nxt_slow_path(ret != NXT_OK)) {
         goto fail;
@@ -334,6 +350,7 @@ njs_vm_init(njs_vm_t *vm)
     u_char       *values;
     nxt_int_t    ret;
     njs_frame_t  *frame;
+    nxt_array_t  *backtrace;
 
     scope_size = vm->scope_size + NJS_INDEX_GLOBAL_OFFSET;
 
@@ -371,6 +388,16 @@ njs_vm_init(njs_vm_t *vm)
         return NXT_ERROR;
     }
 
+    if (vm->debug != NULL) {
+        backtrace = nxt_array_create(4, sizeof(njs_backtrace_entry_t),
+                                     &njs_array_mem_proto, vm->mem_cache_pool);
+        if (nxt_slow_path(backtrace == NULL)) {
+            return NXT_ERROR;
+        }
+
+        vm->backtrace = backtrace;
+    }
+
     vm->retval = njs_value_void;
 
     vm->trace.level = NXT_LEVEL_TRACE;
@@ -493,3 +520,14 @@ njs_vm_exception(njs_vm_t *vm, nxt_str_t
 {
     return njs_value_to_ext_string(vm, retval, vm->exception);
 }
+
+
+nxt_array_t *
+njs_vm_backtrace(njs_vm_t *vm)
+{
+    if (!nxt_array_is_empty(vm->backtrace)) {
+        return vm->backtrace;
+    }
+
+    return NULL;
+}
diff -r 746c49e6c195 -r 82dc332cb4c8 njs/njscript.h
--- a/njs/njscript.h	Thu Aug 24 16:16:37 2017 +0300
+++ b/njs/njscript.h	Tue Aug 29 14:06:23 2017 +0300
@@ -72,9 +72,16 @@ typedef struct {
 
     uint8_t                         trailer;         /* 1 bit */
     uint8_t                         accumulative;    /* 1 bit */
+    uint8_t                         backtrace;       /* 1 bit */
 } njs_vm_opt_t;
 
 
+typedef struct {
+    nxt_str_t                       name;
+    uint32_t                        line;
+} njs_backtrace_entry_t;
+
+
 #define NJS_OK                      NXT_OK
 #define NJS_ERROR                   NXT_ERROR
 #define NJS_AGAIN                   NXT_AGAIN
@@ -103,6 +110,7 @@ NXT_EXPORT njs_ret_t njs_vm_return_strin
     size_t size);
 NXT_EXPORT nxt_int_t njs_vm_retval(njs_vm_t *vm, nxt_str_t *retval);
 NXT_EXPORT nxt_int_t njs_vm_exception(njs_vm_t *vm, nxt_str_t *retval);
+NXT_EXPORT nxt_array_t *njs_vm_backtrace(njs_vm_t *vm);
 
 NXT_EXPORT void njs_disassembler(njs_vm_t *vm);
 
diff -r 746c49e6c195 -r 82dc332cb4c8 njs/test/njs_interactive_test.c
--- a/njs/test/njs_interactive_test.c	Thu Aug 24 16:16:37 2017 +0300
+++ b/njs/test/njs_interactive_test.c	Tue Aug 29 14:06:23 2017 +0300
@@ -110,17 +110,80 @@ static njs_interactive_test_t  njs_test[
       nxt_string("4") },
 
     { nxt_string("function ff(o) {return o.a.a}" ENTER
+                 "function f(o) {try {return ff(o)} "
+                                 "finally {return 1}}" ENTER
+                 "f({})" ENTER),
+      nxt_string("1") },
+
+    /* Backtraces */
+
+    { nxt_string("function ff(o) {return o.a.a}" ENTER
                  "function f(o) {return ff(o)}" ENTER
                  "f({})" ENTER),
-      nxt_string("TypeError") },
+      nxt_string("TypeError\n"
+                 "at ff (:1)\n"
+                 "at f (:1)\n"
+                 "at main\n") },
+
+    { nxt_string("function ff(o) {return o.a.a}" ENTER
+                 "function f(o) {try {return ff(o)} "
+                                 "finally {return o.a.a}}" ENTER
+                 "f({})" ENTER),
+      nxt_string("TypeError\n"
+                 "at f (:1)\n"
+                 "at main\n") },
 
     { nxt_string("function f(ff, o) {return ff(o)}" ENTER
                  "f(function (o) {return o.a.a}, {})" ENTER),
-      nxt_string("TypeError") },
+      nxt_string("TypeError\n"
+                 "at anonymous (:1)\n"
+                 "at f (:1)\n"
+                 "at main\n") },
+
+    { nxt_string("'str'.replace(/t/g,"
+                 "              function(m) {return m.a.a})" ENTER),
+      nxt_string("TypeError\n"
+                 "at anonymous (:1)\n"
+                 "at String.prototype.replace\n"
+                 "at main\n") },
+
+    { nxt_string("function f(o) {return Object.keys(o)}" ENTER
+                 "f()" ENTER),
+      nxt_string("TypeError\n"
+                 "at Object.keys\n"
+                 "at f (:1)\n"
+                 "at main\n") },
+
+    { nxt_string("String.fromCharCode(3.14)" ENTER),
+      nxt_string("RangeError\n"
+                 "at String.fromCharCode\n"
+                 "at main\n") },
+
+    { nxt_string("Math.log({}.a.a)" ENTER),
+      nxt_string("TypeError\n"
+                 "at Math.log\n"
+                 "at main\n") },
+
+    { nxt_string("function f(o) {function f_in(o) {return o.a.a};"
+                 "               return f_in(o)}; f({})" ENTER),
+      nxt_string("TypeError\n"
+                 "at f_in (:1)\n"
+                 "at f (:1)\n"
+                 "at main\n") },
+
+    { nxt_string("function f(o) {var ff = function (o) {return o.a.a};"
+                 "               return ff(o)}; f({})" ENTER),
+      nxt_string("TypeError\n"
+                 "at anonymous (:1)\n"
+                 "at f (:1)\n"
+                 "at main\n") },
 
 };
 
 
+static void njs_report_backtrace(nxt_array_t *backtrace, nxt_str_t *s);
+
+
 static nxt_int_t
 njs_interactive_test(void)
 {
@@ -130,6 +193,7 @@ njs_interactive_test(void)
     nxt_str_t               s;
     nxt_uint_t              i;
     nxt_bool_t              success;
+    nxt_array_t             *backtrace;
     njs_vm_opt_t            options;
     nxt_mem_cache_pool_t    *mcp;
     njs_interactive_test_t  *test;
@@ -153,6 +217,7 @@ njs_interactive_test(void)
 
         options.mcp = mcp;
         options.accumulative = 1;
+        options.backtrace = 1;
 
         vm = njs_vm_create(&options);
         if (vm == NULL) {
@@ -184,6 +249,11 @@ njs_interactive_test(void)
 
         } else {
             njs_vm_exception(vm, &s);
+
+            backtrace = njs_vm_backtrace(vm);
+            if (backtrace != NULL) {
+                njs_report_backtrace(backtrace, &s);
+            }
         }
 
         success = nxt_strstr_eq(&test->ret, &s);
@@ -211,6 +281,34 @@ fail:
 }
 
 
+static void
+njs_report_backtrace(nxt_array_t *backtrace, nxt_str_t *s)
+{
+    char                   *p;
+    nxt_uint_t             i;
+    njs_backtrace_entry_t  *be;
+
+    static char            buf[4096];
+
+    p = buf + sprintf(buf, "%.*s\n", (int) s->length, s->start);
+
+    be = backtrace->start;
+    for (i = 0; i < backtrace->items; i++) {
+        if (be[i].line != 0) {
+            p += sprintf(p, "at %.*s (:%d)\n", (int) be[i].name.length,
+                         be[i].name.start, be[i].line);
+
+        } else {
+            p += sprintf(p, "at %.*s\n", (int) be[i].name.length,
+                         be[i].name.start);
+        }
+    }
+
+    s->length = strlen(buf);
+    s->start = (u_char *) buf;
+}
+
+
 int nxt_cdecl
 main(int argc, char **argv)
 {


More information about the nginx-devel mailing list