On Tue, Mar 01, 2016 at 11:50:28PM +0100, Martin Pieuchot wrote:
> 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.
The diff below seems to improve things, so I would say go for it.
-ml
>
>
> 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 */
>