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.
diff --git a/include/haproxy/debug.h b/include/haproxy/debug.h
index 5cec243..dd1668d 100644
--- a/include/haproxy/debug.h
+++ b/include/haproxy/debug.h
@@ -28,7 +28,7 @@
extern unsigned int debug_commands_issued;
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_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_thread_dump_all_to_trash();
void ha_panic();
diff --git a/src/debug.c b/src/debug.c
index 7d73fdc..fc2de92 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -55,16 +55,22 @@
/* 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
* 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;
char pfx2[100];
void *callers[100];
int j, nptrs;
const void *addr;
- int dump = 0;
nptrs = my_backtrace(callers, sizeof(callers)/sizeof(*callers));
if (!nptrs)
@@ -77,43 +83,50 @@
* produce similar output to the following:
*/
chunk_appendf(buf, "%scall trace(%d):\n", prefix, nptrs);
- for (j = 0; (j < nptrs || dump < 2); j++) {
- if (j == nptrs && !dump) {
+ for (j = 0; (j < nptrs || (dump & 3) < 2); j++) {
+ if (j == nptrs && !(dump & 3)) {
/* we failed to spot the starting point of the
* dump, let's start over dumping everything we
* have.
*/
- dump = 2;
+ dump += 2;
j = 0;
}
bak = *buf;
dump_addr_and_bytes(buf, pfx2, callers[j], 8);
addr = resolve_sym_name(buf, ": ", callers[j]);
- if (dump == 0) {
+ if ((dump & 3) == 0) {
/* dump not started, will start *after*
* ha_thread_dump_all_to_trash, ha_panic and ha_backtrace_to_stderr
*/
if (addr == ha_thread_dump_all_to_trash || addr == ha_panic ||
addr == ha_backtrace_to_stderr)
- dump = 1;
+ dump++;
*buf = bak;
continue;
}
- if (dump == 1) {
+ if ((dump & 3) == 1) {
/* starting */
if (addr == ha_thread_dump_all_to_trash || addr == ha_panic ||
addr == ha_backtrace_to_stderr) {
*buf = bak;
continue;
}
- dump = 2;
+ dump++;
}
- if (dump == 2) {
- /* dumping */
- if (addr == run_poll_loop || addr == main || addr == run_tasks_from_lists) {
- dump = 3;
+ if ((dump & 3) == 2) {
+ /* still dumping */
+ if (dump == 6) {
+ /* 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;
break;
}
@@ -129,7 +142,7 @@
char area[2048];
struct buffer b = b_make(area, sizeof(area), 0, 0);
- ha_dump_backtrace(&b, " ");
+ ha_dump_backtrace(&b, " ", 4);
if (b.data)
write(2, b.area, b.data);
}
@@ -189,7 +202,7 @@
* so that the compiler uses tail merging and the current
* function does not appear in the stack.
*/
- ha_dump_backtrace(buf, " ");
+ ha_dump_backtrace(buf, " ", 0);
}
}