From owner-svn-src-stable@freebsd.org Thu Nov 12 22:45:53 2015 Return-Path: Delivered-To: svn-src-stable@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 45314A2E162; Thu, 12 Nov 2015 22:45:53 +0000 (UTC) (envelope-from jhb@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 224D3180C; Thu, 12 Nov 2015 22:45:53 +0000 (UTC) (envelope-from jhb@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id tACMjqYi006168; Thu, 12 Nov 2015 22:45:52 GMT (envelope-from jhb@FreeBSD.org) Received: (from jhb@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id tACMjqkV006166; Thu, 12 Nov 2015 22:45:52 GMT (envelope-from jhb@FreeBSD.org) Message-Id: <201511122245.tACMjqkV006166@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: jhb set sender to jhb@FreeBSD.org using -f From: John Baldwin Date: Thu, 12 Nov 2015 22:45:52 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-9@freebsd.org Subject: svn commit: r290730 - in stable: 10/sys/amd64/amd64 10/sys/i386/i386 9/sys/amd64/amd64 9/sys/i386/i386 X-SVN-Group: stable-9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-stable@freebsd.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: SVN commit messages for all the -stable branches of the src tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 12 Nov 2015 22:45:53 -0000 Author: jhb Date: Thu Nov 12 22:45:51 2015 New Revision: 290730 URL: https://svnweb.freebsd.org/changeset/base/290730 Log: MFC 285773,285775,285776: Various fixes for stack unwinding in DDB on x86. 285773: Remove some dead code from DDB's amd64 stack unwinder. The amd64 port copied some code from i386 to fetch function arguments and display them in backtraces. However, it was commented out and can't easily be implemented since the function arguments are passed in registers rather than on the stack in amd64. Remove it in preparation for some bug fixes in this area. 285775: Improve stack unwinding on i386 and amd64 after an IP fault. If we can't find a symbol corresponding to the faulting instruction, assume that the previously-executed function is a call and attempt to find the calling function using the return address on the stack. Otherwise we end up associating the last stack frame with the current call, which is incorrect and causes the unwinder to skip printing of the calling function, resulting in a confusing backtrace. 285776: Let the unwinder handle faults during function prologues or epilogues. The i386 and amd64 DDB stack unwinders contain code to detect and handle the case where the first frame is not completely set up or torn down. This code was accidentally unused however, since db_backtrace() was never called with a non-NULL trap frame. This change fixes that. Also remove get_rsp() from the amd64 code. It appears to have come from i386, which needs to take into account whether the exception triggered a CPL switch, since SS:ESP is only pushed onto the stack if so. On amd64, SS:RSP is pushed regardless, so get_rsp() was doing the wrong thing for kernel-mode exceptions. As a result, we can also remove custom print functions for these registers. Modified: stable/9/sys/amd64/amd64/db_trace.c stable/9/sys/i386/i386/db_trace.c Directory Properties: stable/9/sys/ (props changed) Changes in other areas also in this revision: Modified: stable/10/sys/amd64/amd64/db_trace.c stable/10/sys/i386/i386/db_trace.c Directory Properties: stable/10/ (props changed) Modified: stable/9/sys/amd64/amd64/db_trace.c ============================================================================== --- stable/9/sys/amd64/amd64/db_trace.c Thu Nov 12 22:24:39 2015 (r290729) +++ stable/9/sys/amd64/amd64/db_trace.c Thu Nov 12 22:45:51 2015 (r290730) @@ -61,8 +61,6 @@ static db_varfcn_t db_dr5; static db_varfcn_t db_dr6; static db_varfcn_t db_dr7; static db_varfcn_t db_frame; -static db_varfcn_t db_rsp; -static db_varfcn_t db_ss; CTASSERT(sizeof(struct dbreg) == sizeof(((struct pcpu *)NULL)->pc_dbreg)); @@ -76,12 +74,12 @@ struct db_variable db_regs[] = { { "es", DB_OFFSET(tf_es), db_frame }, { "fs", DB_OFFSET(tf_fs), db_frame }, { "gs", DB_OFFSET(tf_gs), db_frame }, - { "ss", NULL, db_ss }, + { "ss", DB_OFFSET(tf_ss), db_frame }, { "rax", DB_OFFSET(tf_rax), db_frame }, { "rcx", DB_OFFSET(tf_rcx), db_frame }, { "rdx", DB_OFFSET(tf_rdx), db_frame }, { "rbx", DB_OFFSET(tf_rbx), db_frame }, - { "rsp", NULL, db_rsp }, + { "rsp", DB_OFFSET(tf_rsp), db_frame }, { "rbp", DB_OFFSET(tf_rbp), db_frame }, { "rsi", DB_OFFSET(tf_rsi), db_frame }, { "rdi", DB_OFFSET(tf_rdi), db_frame }, @@ -130,13 +128,6 @@ DB_DRX_FUNC(dr5) DB_DRX_FUNC(dr6) DB_DRX_FUNC(dr7) -static __inline long -get_rsp(struct trapframe *tf) -{ - return ((ISPL(tf->tf_cs)) ? tf->tf_rsp : - (db_expr_t)tf + offsetof(struct trapframe, tf_rsp)); -} - static int db_frame(struct db_variable *vp, db_expr_t *valuep, int op) { @@ -153,34 +144,6 @@ db_frame(struct db_variable *vp, db_expr return (1); } -static int -db_rsp(struct db_variable *vp, db_expr_t *valuep, int op) -{ - - if (kdb_frame == NULL) - return (0); - - if (op == DB_VAR_GET) - *valuep = get_rsp(kdb_frame); - else if (ISPL(kdb_frame->tf_cs)) - kdb_frame->tf_rsp = *valuep; - return (1); -} - -static int -db_ss(struct db_variable *vp, db_expr_t *valuep, int op) -{ - - if (kdb_frame == NULL) - return (0); - - if (op == DB_VAR_GET) - *valuep = (ISPL(kdb_frame->tf_cs)) ? kdb_frame->tf_ss : rss(); - else if (ISPL(kdb_frame->tf_cs)) - kdb_frame->tf_ss = *valuep; - return (1); -} - #define NORMAL 0 #define TRAP 1 #define INTERRUPT 2 @@ -188,9 +151,7 @@ db_ss(struct db_variable *vp, db_expr_t #define TRAP_INTERRUPT 5 static void db_nextframe(struct amd64_frame **, db_addr_t *, struct thread *); -static int db_numargs(struct amd64_frame *); -static void db_print_stack_entry(const char *, int, char **, long *, db_addr_t, - void *); +static void db_print_stack_entry(const char *, db_addr_t, void *); static void decode_syscall(int, struct thread *); static const char * watchtype_str(int type); @@ -198,62 +159,11 @@ int amd64_set_watch(int watchnum, unsig int access, struct dbreg *d); int amd64_clr_watch(int watchnum, struct dbreg *d); -/* - * Figure out how many arguments were passed into the frame at "fp". - */ -static int -db_numargs(fp) - struct amd64_frame *fp; -{ -#if 1 - return (0); /* regparm, needs dwarf2 info */ -#else - long *argp; - int inst; - int args; - - argp = (long *)db_get_value((long)&fp->f_retaddr, 8, FALSE); - /* - * XXX etext is wrong for LKMs. We should attempt to interpret - * the instruction at the return address in all cases. This - * may require better fault handling. - */ - if (argp < (long *)btext || argp >= (long *)etext) { - args = 5; - } else { - inst = db_get_value((long)argp, 4, FALSE); - if ((inst & 0xff) == 0x59) /* popl %ecx */ - args = 1; - else if ((inst & 0xffff) == 0xc483) /* addl $Ibs, %esp */ - args = ((inst >> 16) & 0xff) / 4; - else - args = 5; - } - return (args); -#endif -} - static void -db_print_stack_entry(name, narg, argnp, argp, callpc, frame) - const char *name; - int narg; - char **argnp; - long *argp; - db_addr_t callpc; - void *frame; +db_print_stack_entry(const char *name, db_addr_t callpc, void *frame) { - db_printf("%s(", name); -#if 0 - while (narg) { - if (argnp) - db_printf("%s=", *argnp++); - db_printf("%lr", (long)db_get_value((long)argp, 8, FALSE)); - argp++; - if (--narg != 0) - db_printf(","); - } -#endif - db_printf(") at "); + + db_printf("%s() at ", name != NULL ? name : "??"); db_printsym(callpc, DB_STGY_PROC); if (frame != NULL) db_printf("/frame 0x%lx", (register_t)frame); @@ -348,7 +258,7 @@ db_nextframe(struct amd64_frame **fp, db return; } - db_print_stack_entry(name, 0, 0, 0, rip, &(*fp)->f_frame); + db_print_stack_entry(name, rip, &(*fp)->f_frame); /* * Point to base of trapframe which is just above the @@ -357,7 +267,7 @@ db_nextframe(struct amd64_frame **fp, db tf = (struct trapframe *)((long)*fp + 16); if (INKERNEL((long) tf)) { - rsp = get_rsp(tf); + rsp = tf->tf_rsp; rip = tf->tf_rip; rbp = tf->tf_rbp; switch (frame_type) { @@ -384,17 +294,13 @@ db_nextframe(struct amd64_frame **fp, db } static int -db_backtrace(struct thread *td, struct trapframe *tf, - struct amd64_frame *frame, db_addr_t pc, int count) +db_backtrace(struct thread *td, struct trapframe *tf, struct amd64_frame *frame, + db_addr_t pc, register_t sp, int count) { struct amd64_frame *actframe; -#define MAXNARG 16 - char *argnames[MAXNARG], **argnp = NULL; const char *name; - long *argp; db_expr_t offset; c_db_sym_t sym; - int narg; boolean_t first; if (count == -1) @@ -418,48 +324,51 @@ db_backtrace(struct thread *td, struct t */ actframe = frame; if (first) { - if (tf != NULL) { + first = FALSE; + if (sym == C_DB_SYM_NULL && sp != 0) { + /* + * If a symbol couldn't be found, we've probably + * jumped to a bogus location, so try and use + * the return address to find our caller. + */ + db_print_stack_entry(name, pc, NULL); + pc = db_get_value(sp, 8, FALSE); + if (db_search_symbol(pc, DB_STGY_PROC, + &offset) == C_DB_SYM_NULL) + break; + continue; + } else if (tf != NULL) { int instr; instr = db_get_value(pc, 4, FALSE); if ((instr & 0xffffffff) == 0xe5894855) { /* pushq %rbp; movq %rsp, %rbp */ - actframe = (void *)(get_rsp(tf) - 8); + actframe = (void *)(tf->tf_rsp - 8); } else if ((instr & 0xffffff) == 0xe58948) { /* movq %rsp, %rbp */ - actframe = (void *)get_rsp(tf); + actframe = (void *)tf->tf_rsp; if (tf->tf_rbp == 0) { /* Fake frame better. */ frame = actframe; } } else if ((instr & 0xff) == 0xc3) { /* ret */ - actframe = (void *)(get_rsp(tf) - 8); + actframe = (void *)(tf->tf_rsp - 8); } else if (offset == 0) { /* Probably an assembler symbol. */ - actframe = (void *)(get_rsp(tf) - 8); + actframe = (void *)(tf->tf_rsp - 8); } } else if (strcmp(name, "fork_trampoline") == 0) { /* * Don't try to walk back on a stack for a * process that hasn't actually been run yet. */ - db_print_stack_entry(name, 0, 0, 0, pc, - actframe); + db_print_stack_entry(name, pc, actframe); break; } - first = FALSE; - } - - argp = &actframe->f_arg0; - narg = MAXNARG; - if (sym != NULL && db_sym_numargs(sym, &narg, argnames)) { - argnp = argnames; - } else { - narg = db_numargs(frame); } - db_print_stack_entry(name, narg, argnp, argp, pc, actframe); + db_print_stack_entry(name, pc, actframe); if (actframe != frame) { /* `frame' belongs to caller. */ @@ -473,7 +382,7 @@ db_backtrace(struct thread *td, struct t if (INKERNEL((long)pc) && !INKERNEL((long)frame)) { sym = db_search_symbol(pc, DB_STGY_ANY, &offset); db_symbol_values(sym, &name, NULL); - db_print_stack_entry(name, 0, 0, 0, pc, frame); + db_print_stack_entry(name, pc, frame); break; } if (!INKERNEL((long) frame)) { @@ -495,17 +404,19 @@ db_trace_self(void) frame = (struct amd64_frame *)rbp; callpc = (db_addr_t)db_get_value((long)&frame->f_retaddr, 8, FALSE); frame = frame->f_frame; - db_backtrace(curthread, NULL, frame, callpc, -1); + db_backtrace(curthread, NULL, frame, callpc, 0, -1); } int db_trace_thread(struct thread *thr, int count) { struct pcb *ctx; + struct trapframe *tf; ctx = kdb_thr_ctx(thr); - return (db_backtrace(thr, NULL, (struct amd64_frame *)ctx->pcb_rbp, - ctx->pcb_rip, count)); + tf = thr == kdb_thread ? kdb_frame : NULL; + return (db_backtrace(thr, tf, (struct amd64_frame *)ctx->pcb_rbp, + ctx->pcb_rip, ctx->pcb_rsp, count)); } int Modified: stable/9/sys/i386/i386/db_trace.c ============================================================================== --- stable/9/sys/i386/i386/db_trace.c Thu Nov 12 22:24:39 2015 (r290729) +++ stable/9/sys/i386/i386/db_trace.c Thu Nov 12 22:45:51 2015 (r290730) @@ -390,7 +390,7 @@ db_nextframe(struct i386_frame **fp, db_ static int db_backtrace(struct thread *td, struct trapframe *tf, struct i386_frame *frame, - db_addr_t pc, int count) + db_addr_t pc, register_t sp, int count) { struct i386_frame *actframe; #define MAXNARG 16 @@ -447,7 +447,21 @@ db_backtrace(struct thread *td, struct t */ actframe = frame; if (first) { - if (tf != NULL) { + first = FALSE; + if (sym == C_DB_SYM_NULL && sp != 0) { + /* + * If a symbol couldn't be found, we've probably + * jumped to a bogus location, so try and use + * the return address to find our caller. + */ + db_print_stack_entry(name, 0, 0, 0, pc, + NULL); + pc = db_get_value(sp, 4, FALSE); + if (db_search_symbol(pc, DB_STGY_PROC, + &offset) == C_DB_SYM_NULL) + break; + continue; + } else if (tf != NULL) { instr = db_get_value(pc, 4, FALSE); if ((instr & 0xffffff) == 0x00e58955) { /* pushl %ebp; movl %esp, %ebp */ @@ -475,7 +489,6 @@ db_backtrace(struct thread *td, struct t actframe); break; } - first = FALSE; } argp = &actframe->f_arg0; @@ -522,17 +535,19 @@ db_trace_self(void) frame = (struct i386_frame *)ebp; callpc = (db_addr_t)db_get_value((int)&frame->f_retaddr, 4, FALSE); frame = frame->f_frame; - db_backtrace(curthread, NULL, frame, callpc, -1); + db_backtrace(curthread, NULL, frame, callpc, 0, -1); } int db_trace_thread(struct thread *thr, int count) { struct pcb *ctx; + struct trapframe *tf; ctx = kdb_thr_ctx(thr); - return (db_backtrace(thr, NULL, (struct i386_frame *)ctx->pcb_ebp, - ctx->pcb_eip, count)); + tf = thr == kdb_thread ? kdb_frame : NULL; + return (db_backtrace(thr, tf, (struct i386_frame *)ctx->pcb_ebp, + ctx->pcb_eip, ctx->pcb_esp, count)); } int