From: Willy Tarreau Date: Wed, 25 Oct 2023 13:02:59 +0000 (+0200) Subject: DEBUG: lua: add tainted flags for stuck Lua contexts X-Git-Tag: v2.9-dev9~29 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=26a6481f007c0f8a264c6d9bca891a67780edb9b;p=thirdparty%2Fhaproxy.git DEBUG: lua: add tainted flags for stuck Lua contexts William suggested that since we can detect the presence of Lua in the stack, let's combine it with stuck detection to set a new pair of flags indicating a stuck Lua context and a stuck Lua shared context. Now, executing an infinite loop in a Lua sample fetch function with yield disabled crashes with tainted=0xe40 if loaded from a lua-load statement, or tainted=0x640 from a lua-load-per-thread statement. In addition, at the end of the panic dump, we can check if Lua was seen stuck and emit recommendations about lua-load-per-thread and the choice of dependencies depending on the presence of threads and/or shared context. --- diff --git a/include/haproxy/bug.h b/include/haproxy/bug.h index a716568f90..fbed1203d9 100644 --- a/include/haproxy/bug.h +++ b/include/haproxy/bug.h @@ -249,6 +249,8 @@ enum tainted_flags { TAINTED_REDEFINITION = 0x00000080, /* symbol redefinition detected */ TAINTED_REPLACED_MEM_ALLOCATOR = 0x00000100, /* memory allocator was replaced using LD_PRELOAD */ TAINTED_PANIC = 0x00000200, /* a panic dump has started */ + TAINTED_LUA_STUCK = 0x00000400, /* stuck in a Lua context */ + TAINTED_LUA_STUCK_SHARED = 0x00000800, /* stuck in a shared Lua context */ }; /* this is a bit field made of TAINTED_*, and is declared in haproxy.c */ diff --git a/src/debug.c b/src/debug.c index 5598d212d2..fbab47e2e2 100644 --- a/src/debug.c +++ b/src/debug.c @@ -227,6 +227,19 @@ void ha_thread_dump_one(int thr, int from_signal) ha_task_dump(buf, th_ctx->current, " "); if (stuck && thr == tid) { +#ifdef USE_LUA + if (th_ctx->current && + th_ctx->current->process == process_stream && th_ctx->current->context) { + const struct stream *s = (const struct stream *)th_ctx->current->context; + struct hlua *hlua = s ? s->hlua : NULL; + + if (hlua && hlua->T) { + mark_tainted(TAINTED_LUA_STUCK); + if (hlua->state_id == 0) + mark_tainted(TAINTED_LUA_STUCK_SHARED); + } + } +#endif /* We only emit the backtrace for stuck threads in order not to * waste precious output buffer space with non-interesting data. * Please leave this as the last instruction in this function @@ -437,6 +450,25 @@ void ha_panic() chunk_reset(&trash); } +#ifdef USE_LUA + if (get_tainted() & TAINTED_LUA_STUCK_SHARED && global.nbthread > 1) { + chunk_printf(&trash, + "### Note: at least one thread was stuck in a Lua context loaded using the\n" + " 'lua-load' directive, which is known for causing heavy contention\n" + " when used with threads. Please consider using 'lua-load-per-thread'\n" + " instead if your code is safe to run in parallel on multiple threads.\n"); + DISGUISE(write(2, trash.area, trash.data)); + } + else if (get_tainted() & TAINTED_LUA_STUCK) { + chunk_printf(&trash, + "### Note: at least one thread was stuck in a Lua context in a way that suggests\n" + " heavy processing inside a dependency or a long loop that can't yield.\n" + " Please make sure any external code you may rely on is safe for use in\n" + " an event-driven engine.\n"); + DISGUISE(write(2, trash.area, trash.data)); + } +#endif + for (;;) abort(); }