]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
DEBUG: lua: add tainted flags for stuck Lua contexts
authorWilly Tarreau <w@1wt.eu>
Wed, 25 Oct 2023 13:02:59 +0000 (15:02 +0200)
committerWilly Tarreau <w@1wt.eu>
Wed, 25 Oct 2023 13:48:02 +0000 (15:48 +0200)
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.

include/haproxy/bug.h
src/debug.c

index a716568f90219effa6bdbf734e6f2aa6483420bd..fbed1203d9b62010f424acea18552830d9894f02 100644 (file)
@@ -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 */
index 5598d212d2160e65bf6f363c977ef93aba7222b6..fbab47e2e281f7e29b0c6899476b8d793244b734 100644 (file)
@@ -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();
 }