MINOR: debug: let ha_dump_backtrace() dump a bit further for some callers

The dump state is now passed to the function so that the caller can adjust
the behavior. A new series of 4 values allow to stop *after* dumping main
instead of before it or any of the usual loops. This allows to also report
BUG_ON() that could happen very high in the call graph (e.g. startup, or
the scheduler itself) while still understanding what the call path was.
This commit is contained in:
Willy Tarreau 2021-01-22 14:48:34 +01:00
parent 5baf4fe31a
commit 2bfce7e424
2 changed files with 30 additions and 17 deletions

View File

@ -28,7 +28,7 @@ extern volatile unsigned long threads_to_dump;
extern unsigned int debug_commands_issued; extern unsigned int debug_commands_issued;
void ha_task_dump(struct buffer *buf, const struct task *task, const char *pfx); void ha_task_dump(struct buffer *buf, const struct task *task, const char *pfx);
void ha_thread_dump(struct buffer *buf, int thr, int calling_tid); void ha_thread_dump(struct buffer *buf, int thr, int calling_tid);
void ha_dump_backtrace(struct buffer *buf, const char *prefix); void ha_dump_backtrace(struct buffer *buf, const char *prefix, int dump);
void ha_backtrace_to_stderr(); void ha_backtrace_to_stderr();
void ha_thread_dump_all_to_trash(); void ha_thread_dump_all_to_trash();
void ha_panic(); void ha_panic();

View File

@ -55,16 +55,22 @@ static unsigned int debug_prng()
/* dumps a backtrace of the current thread that is appended to buffer <buf>. /* dumps a backtrace of the current thread that is appended to buffer <buf>.
* Lines are prefixed with the string <prefix> which may be empty (used for * Lines are prefixed with the string <prefix> which may be empty (used for
* indenting). It is recommended to use this at a function's tail so that * indenting). It is recommended to use this at a function's tail so that
* the function does not appear in the call stack. * the function does not appear in the call stack. The <dump> argument
* indicates what dump state to start from, and should usually be zero. It
* may be among the following values:
* - 0: search usual callers before step 1, or directly jump to 2
* - 1: skip usual callers before step 2
* - 2: dump until polling loop, scheduler, or main() (excluded)
* - 3: end
* - 4-7: like 0 but stops *after* main.
*/ */
void ha_dump_backtrace(struct buffer *buf, const char *prefix) void ha_dump_backtrace(struct buffer *buf, const char *prefix, int dump)
{ {
struct buffer bak; struct buffer bak;
char pfx2[100]; char pfx2[100];
void *callers[100]; void *callers[100];
int j, nptrs; int j, nptrs;
const void *addr; const void *addr;
int dump = 0;
nptrs = my_backtrace(callers, sizeof(callers)/sizeof(*callers)); nptrs = my_backtrace(callers, sizeof(callers)/sizeof(*callers));
if (!nptrs) if (!nptrs)
@ -77,43 +83,50 @@ void ha_dump_backtrace(struct buffer *buf, const char *prefix)
* produce similar output to the following: * produce similar output to the following:
*/ */
chunk_appendf(buf, "%scall trace(%d):\n", prefix, nptrs); chunk_appendf(buf, "%scall trace(%d):\n", prefix, nptrs);
for (j = 0; (j < nptrs || dump < 2); j++) { for (j = 0; (j < nptrs || (dump & 3) < 2); j++) {
if (j == nptrs && !dump) { if (j == nptrs && !(dump & 3)) {
/* we failed to spot the starting point of the /* we failed to spot the starting point of the
* dump, let's start over dumping everything we * dump, let's start over dumping everything we
* have. * have.
*/ */
dump = 2; dump += 2;
j = 0; j = 0;
} }
bak = *buf; bak = *buf;
dump_addr_and_bytes(buf, pfx2, callers[j], 8); dump_addr_and_bytes(buf, pfx2, callers[j], 8);
addr = resolve_sym_name(buf, ": ", callers[j]); addr = resolve_sym_name(buf, ": ", callers[j]);
if (dump == 0) { if ((dump & 3) == 0) {
/* dump not started, will start *after* /* dump not started, will start *after*
* ha_thread_dump_all_to_trash, ha_panic and ha_backtrace_to_stderr * ha_thread_dump_all_to_trash, ha_panic and ha_backtrace_to_stderr
*/ */
if (addr == ha_thread_dump_all_to_trash || addr == ha_panic || if (addr == ha_thread_dump_all_to_trash || addr == ha_panic ||
addr == ha_backtrace_to_stderr) addr == ha_backtrace_to_stderr)
dump = 1; dump++;
*buf = bak; *buf = bak;
continue; continue;
} }
if (dump == 1) { if ((dump & 3) == 1) {
/* starting */ /* starting */
if (addr == ha_thread_dump_all_to_trash || addr == ha_panic || if (addr == ha_thread_dump_all_to_trash || addr == ha_panic ||
addr == ha_backtrace_to_stderr) { addr == ha_backtrace_to_stderr) {
*buf = bak; *buf = bak;
continue; continue;
} }
dump = 2; dump++;
} }
if (dump == 2) { if ((dump & 3) == 2) {
/* dumping */ /* still dumping */
if (addr == run_poll_loop || addr == main || addr == run_tasks_from_lists) { if (dump == 6) {
dump = 3; /* we only stop *after* main and we must send the LF */
if (addr == main) {
j = nptrs;
dump++;
}
}
else if (addr == run_poll_loop || addr == main || addr == run_tasks_from_lists) {
dump++;
*buf = bak; *buf = bak;
break; break;
} }
@ -129,7 +142,7 @@ void ha_backtrace_to_stderr()
char area[2048]; char area[2048];
struct buffer b = b_make(area, sizeof(area), 0, 0); struct buffer b = b_make(area, sizeof(area), 0, 0);
ha_dump_backtrace(&b, " "); ha_dump_backtrace(&b, " ", 4);
if (b.data) if (b.data)
write(2, b.area, b.data); write(2, b.area, b.data);
} }
@ -189,7 +202,7 @@ void ha_thread_dump(struct buffer *buf, int thr, int calling_tid)
* so that the compiler uses tail merging and the current * so that the compiler uses tail merging and the current
* function does not appear in the stack. * function does not appear in the stack.
*/ */
ha_dump_backtrace(buf, " "); ha_dump_backtrace(buf, " ", 0);
} }
} }