Trying to get a backtrace after setting a breakpoint on the ``syscall''
symbol on ddb(4) does not work well. On am64 it faults:
Breakpoint at syscall: pushq %rbp
ddb{0}> tr
syscall() at syscall
Xsyscall() at Xsyscall+0xcb
uvm_fault(0xffffff000f6c3100, 0x1008, 0, 1) -> e
kernel: page fault trap, code=0
Faulted in DDB; continuing...
This is simply because the logic in db_stack_trace_print()/db_nextframe()
is rather simple and assume the first frame is completely in kernel.
The diff below improves the situation, but is not completely correct
since we cannot really compare the frame pointers.
Breakpoint at syscall: pushq %rbp
ddb{0}> tr
syscall() at syscall
--- syscall (number 74) ---
Bad user frame pointer: 0x1000
end trace frame: 0x1000, count: -1
0x70116b2845a:
I'm interested in any input as I'm facing the same problem when I'm
instrumenting any kernel entry point. But if you think the approach
below is still a step forward I'll happily commit it.
Index: arch/amd64/amd64/db_trace.c
===================================================================
RCS file: /cvs/src/sys/arch/amd64/amd64/db_trace.c,v
retrieving revision 1.16
diff -u -p -r1.16 db_trace.c
--- arch/amd64/amd64/db_trace.c 1 Mar 2016 21:35:13 -0000 1.16
+++ arch/amd64/amd64/db_trace.c 1 Mar 2016 22:49:10 -0000
@@ -176,7 +176,7 @@ db_stack_trace_print(db_expr_t addr, boo
char *modif, int (*pr)(const char *, ...))
{
struct callframe *frame, *lastframe;
- long *argp;
+ long *argp, *arg0;
db_addr_t callpc;
int is_trap = 0;
boolean_t kernel_only = TRUE;
@@ -235,7 +235,7 @@ db_stack_trace_print(db_expr_t addr, boo
offset = 0;
}
}
- if (INKERNEL(frame) && name) {
+ if (INKERNEL(callpc) && name) {
if (!strcmp(name, "trap")) {
is_trap = TRAP;
} else if (!strcmp(name, "ast")) {
@@ -265,14 +265,15 @@ db_stack_trace_print(db_expr_t addr, boo
if (lastframe == 0 && offset == 0 && !have_addr) {
/*
* We have a breakpoint before the frame is set up
- * Use %esp instead
+ * Use %rsp instead
*/
- argp = &((struct callframe
*)(ddb_regs.tf_rsp-8))->f_arg0;
+ arg0 =
+ &((struct callframe *)(ddb_regs.tf_rsp-8))->f_arg0;
} else {
- argp = &frame->f_arg0;
+ arg0 = &frame->f_arg0;
}
- while (narg) {
+ for (argp = arg0; narg > 0; ) {
(*pr)("%lx", db_get_value((db_addr_t)argp, 8, FALSE));
argp++;
if (--narg != 0)
@@ -282,7 +283,7 @@ db_stack_trace_print(db_expr_t addr, boo
db_printsym(callpc, DB_STGY_PROC, pr);
(*pr)("\n");
- if (lastframe == 0 && offset == 0 && !have_addr) {
+ if (lastframe == 0 && offset == 0 && !have_addr && !is_trap) {
/* Frame really belongs to next callpc */
lastframe = (struct callframe *)(ddb_regs.tf_rsp-8);
callpc = (db_addr_t)
@@ -299,9 +300,11 @@ db_stack_trace_print(db_expr_t addr, boo
* trapframe as with syscalls and traps.
*/
frame = (struct callframe *)&lastframe->f_retaddr;
+ arg0 = &frame->f_arg0;
}
+
lastframe = frame;
- db_nextframe(&frame, &callpc, &frame->f_arg0, is_trap, pr);
+ db_nextframe(&frame, &callpc, arg0, is_trap, pr);
if (frame == 0) {
/* end of chain */
Index: arch/i386/i386/db_trace.c
===================================================================
RCS file: /cvs/src/sys/arch/i386/i386/db_trace.c,v
retrieving revision 1.18
diff -u -p -r1.18 db_trace.c
--- arch/i386/i386/db_trace.c 1 Mar 2016 21:35:13 -0000 1.18
+++ arch/i386/i386/db_trace.c 1 Mar 2016 22:32:08 -0000
@@ -158,7 +158,7 @@ db_stack_trace_print(db_expr_t addr, boo
char *modif, int (*pr)(const char *, ...))
{
struct callframe *frame, *lastframe;
- int *argp;
+ int *argp, *arg0;
db_addr_t callpc;
int is_trap = 0;
boolean_t kernel_only = TRUE;
@@ -224,7 +224,7 @@ db_stack_trace_print(db_expr_t addr, boo
offset = 0;
}
}
- if (INKERNEL((int)frame) && name) {
+ if (INKERNEL(callpc) && name) {
if (!strcmp(name, "trap")) {
is_trap = TRAP;
} else if (!strcmp(name, "ast")) {
@@ -255,12 +255,13 @@ db_stack_trace_print(db_expr_t addr, boo
* We have a breakpoint before the frame is set up
* Use %esp instead
*/
- argp = &((struct callframe
*)(ddb_regs.tf_esp-4))->f_arg0;
+ arg0 =
+ &((struct callframe *)(ddb_regs.tf_esp-4))->f_arg0;
} else {
- argp = &frame->f_arg0;
+ arg0 = &frame->f_arg0;
}
- while (narg) {
+ for (argp = arg0; narg > 0; ) {
(*pr)("%x", db_get_value((int)argp, 4, FALSE));
argp++;
if (--narg != 0)
@@ -270,7 +271,7 @@ db_stack_trace_print(db_expr_t addr, boo
db_printsym(callpc, DB_STGY_PROC, pr);
(*pr)("\n");
- if (lastframe == 0 && offset == 0 && !have_addr) {
+ if (lastframe == 0 && offset == 0 && !have_addr && !is_trap) {
/* Frame really belongs to next callpc */
lastframe = (struct callframe *)(ddb_regs.tf_esp-4);
callpc = (db_addr_t)
@@ -279,7 +280,7 @@ db_stack_trace_print(db_expr_t addr, boo
}
lastframe = frame;
- db_nextframe(&frame, &callpc, &frame->f_arg0, is_trap, pr);
+ db_nextframe(&frame, &callpc, arg0, is_trap, pr);
if (frame == 0) {
/* end of chain */