Created
February 2, 2026 16:52
-
-
Save xeioex/303d357c5b1d94462a1734fdb64200d3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| commit d5fae2e1ccc37de58573e4961958e82cf8d6d6b2 | |
| Author: Dmitry Volyntsev <[email protected]> | |
| Date: Fri Jan 30 17:43:17 2026 -0800 | |
| Attaching JS stack trace for exceptions thrown by C code. | |
| This fixes #1019 issue on Github. | |
| diff --git a/nginx/t/js_fetch_error_stack.t b/nginx/t/js_fetch_error_stack.t | |
| new file mode 100644 | |
| index 00000000..55183020 | |
| --- /dev/null | |
| +++ b/nginx/t/js_fetch_error_stack.t | |
| @@ -0,0 +1,111 @@ | |
| +#!/usr/bin/perl | |
| + | |
| +# (C) Dmitry Volyntsev | |
| +# (C) F5, Inc. | |
| + | |
| +# Tests for http njs module, fetch method error stack traces. | |
| + | |
| +############################################################################### | |
| + | |
| +use warnings; | |
| +use strict; | |
| + | |
| +use Test::More; | |
| + | |
| +BEGIN { use FindBin; chdir($FindBin::Bin); } | |
| + | |
| +use lib 'lib'; | |
| +use Test::Nginx; | |
| + | |
| +############################################################################### | |
| + | |
| +select STDERR; $| = 1; | |
| +select STDOUT; $| = 1; | |
| + | |
| +my $t = Test::Nginx->new()->has(qw/http/) | |
| + ->write_file_expand('nginx.conf', <<'EOF'); | |
| + | |
| +%%TEST_GLOBALS%% | |
| + | |
| +daemon off; | |
| + | |
| +events { | |
| +} | |
| + | |
| +http { | |
| + %%TEST_GLOBALS_HTTP%% | |
| + | |
| + js_import test.js; | |
| + | |
| + server { | |
| + listen 127.0.0.1:8080; | |
| + server_name localhost; | |
| + | |
| + location /testAsync { | |
| + js_content test.testAsync; | |
| + } | |
| + | |
| + location /testSync { | |
| + js_content test.testSync; | |
| + } | |
| + | |
| + location /testAsyncThrow { | |
| + js_content test.testAsyncThrow; | |
| + } | |
| + } | |
| +} | |
| + | |
| +EOF | |
| + | |
| +$t->write_file('test.js', <<'EOF'); | |
| + async function testAsync(r) { | |
| + try { | |
| + await ngx.fetch('http://127.0.0.1:12345'); | |
| + | |
| + } catch(e) { | |
| + r.return(200, `stack: ${e.stack}\n`); | |
| + } | |
| + } | |
| + | |
| + function testSync(r) { | |
| + try { | |
| + throw Error('oops'); | |
| + | |
| + } catch(e) { | |
| + r.return(200, `stack: ${e.stack}\n`); | |
| + } | |
| + } | |
| + | |
| + async function testAsyncThrow(r) { | |
| + try { | |
| + throw Error('oops'); | |
| + | |
| + } catch(e) { | |
| + r.return(200, `stack: ${e.stack}\n`); | |
| + } | |
| + } | |
| + | |
| + export default { testAsync, testSync, testAsyncThrow } | |
| +EOF | |
| + | |
| +$t->try_run('no njs'); | |
| + | |
| +$t->plan(6); | |
| + | |
| +############################################################################### | |
| + | |
| +like(http_get('/testSync'), qr/stack: Error: oops/s, 'sync stack exists'); | |
| +like(http_get('/testSync'), qr/at testSync \([^)]*test\.js:12\)/s, | |
| + 'sync stack line number'); | |
| + | |
| +like(http_get('/testAsync'), qr/stack: Error: connect failed/s, | |
| + 'async stack exists'); | |
| +like(http_get('/testAsync'), qr/at testAsync \([^)]*test\.js:3\)/s, | |
| + 'async stack line number'); | |
| + | |
| +like(http_get('/testAsyncThrow'), qr/stack: Error: oops/s, | |
| + 'async throw stack exists'); | |
| +like(http_get('/testAsyncThrow'), qr/at testAsyncThrow \([^)]*test\.js:21\)/s, | |
| + 'async throw stack line number'); | |
| + | |
| +############################################################################### | |
| diff --git a/src/njs_async.c b/src/njs_async.c | |
| index c352e86e..bc4769f4 100644 | |
| --- a/src/njs_async.c | |
| +++ b/src/njs_async.c | |
| @@ -82,6 +82,13 @@ njs_await_fulfilled(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, | |
| vm->active_frame = async_frame; | |
| if (exception) { | |
| + /* | |
| + * Attaching JS stack trace for exceptions thrown by | |
| + * C code (for example by njs_vm_error3()), in a C call stack. | |
| + */ | |
| + async->pc = ctx->await_pc; | |
| + njs_error_stack_attach(vm, *value, 0); | |
| + | |
| njs_vm_throw(vm, value); | |
| } else { | |
| diff --git a/src/njs_async.h b/src/njs_async.h | |
| index 3addb2d6..8e4111f6 100644 | |
| --- a/src/njs_async.h | |
| +++ b/src/njs_async.h | |
| @@ -13,6 +13,7 @@ typedef struct { | |
| njs_frame_t *await; | |
| uintptr_t index; | |
| u_char *pc; | |
| + u_char *await_pc; | |
| } njs_async_ctx_t; | |
| diff --git a/src/njs_vmcode.c b/src/njs_vmcode.c | |
| index 6d991042..7171b996 100644 | |
| --- a/src/njs_vmcode.c | |
| +++ b/src/njs_vmcode.c | |
| @@ -2772,6 +2772,7 @@ njs_vmcode_await(njs_vm_t *vm, njs_vmcode_await_t *await, | |
| } | |
| ctx->pc = (u_char *) await + sizeof(njs_vmcode_await_t); | |
| + ctx->await_pc = (u_char *) await; | |
| ctx->index = await->retval; | |
| frame = (njs_frame_t *) active; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment