void db_stack_trace_print(db_expr_t addr, boolean_t have_addr, db_expr_t count, char *modif, int (*pr)(const char *, ...)) { struct x86_64_frame *frame, *lastframe; long *argp; db_addr_t callpc; int is_trap = 0; boolean_t kernel_only = TRUE; boolean_t trace_thread = FALSE; #if 0 if (!db_trace_symbols_found) db_find_trace_symbols(); #endif { char *cp = modif; char c; while ((c = *cp++) != 0) { if (c == 't') trace_thread = TRUE; if (c == 'u') kernel_only = FALSE; } } if (!have_addr) { frame = (struct x86_64_frame *)ddb_regs.tf_rbp; callpc = (db_addr_t)ddb_regs.tf_rip; } else { #if 0 if (trace_thread) { struct proc *p; struct user *u; struct lwp *l; (*pr)("trace: pid %d ", (int)addr); p = pfind(addr); if (p == NULL) { (*pr)("not found\n"); return; } l = proc_representative_lwp(p); if (!(l->l_flag&L_INMEM)) { (*pr)("swapped out\n"); return; } u = l->l_addr; frame = (struct x86_64_frame *) u->u_pcb.pcb_rbp; (*pr)("at %p\n", frame); } else #endif frame = (struct x86_64_frame *)addr; callpc = (db_addr_t) db_get_value((db_addr_t)&frame->f_retaddr, 8, FALSE); frame = (struct x86_64_frame *)frame->f_frame; } lastframe = 0; while (count && frame != 0) { int narg; char * name; db_expr_t offset; db_sym_t sym; #define MAXNARG 16 char *argnames[MAXNARG], **argnp = NULL; sym = db_search_symbol(callpc, DB_STGY_ANY, &offset); db_symbol_values(sym, &name, NULL); if (lastframe == 0 && sym == NULL) { /* Symbol not found, peek at code */ long instr = db_get_value(callpc, 8, FALSE); offset = 1; if ((instr & 0x00ffffff) == 0x00e58955 || /* enter: pushl %ebp, movl %esp, %ebp */ (instr & 0x0000ffff) == 0x0000e589 /* enter+1: movl %esp, %ebp */) { offset = 0; } } if (INKERNEL(frame) && name) { #ifdef __ELF__ if (!strcmp(name, "trap")) { is_trap = TRAP; } else if (!strcmp(name, "syscall")) { is_trap = SYSCALL; } else if (name[0] == 'X') { if (!strncmp(name, "Xintr", 5) || !strncmp(name, "Xresume", 7) || !strncmp(name, "Xstray", 6) || !strncmp(name, "Xhold", 5) || !strncmp(name, "Xrecurse", 8) || !strcmp(name, "Xdoreti") || !strncmp(name, "Xsoft", 5)) { is_trap = INTERRUPT; } else goto normal; } else goto normal; narg = 0; #else if (!strcmp(name, "_trap")) { is_trap = TRAP; } else if (!strcmp(name, "_syscall")) { is_trap = SYSCALL; } else if (name[0] == '_' && name[1] == 'X') { if (!strncmp(name, "_Xintr", 6) || !strncmp(name, "_Xresume", 8) || !strncmp(name, "_Xstray", 7) || !strncmp(name, "_Xhold", 6) || !strncmp(name, "_Xrecurse", 9) || !strcmp(name, "_Xdoreti") || !strncmp(name, "_Xsoft", 6)) { is_trap = INTERRUPT; } else goto normal; } else goto normal; narg = 0; #endif /* __ELF__ */ } else { normal: is_trap = NONE; narg = MAXNARG; if (db_sym_numargs(sym, &narg, argnames)) argnp = argnames; else narg = db_numargs(frame); } (*pr)("%s(", name); if (lastframe == 0 && offset == 0 && !have_addr) { /* * We have a breakpoint before the frame is set up * Use %esp instead */ argp = &((struct x86_64_frame *)(ddb_regs.tf_rsp-8))->f_arg0; } else { argp = &frame->f_arg0; } while (narg) { if (argnp) (*pr)("%s=", *argnp++); (*pr)("%lx", db_get_value((db_addr_t)argp, 8, FALSE)); argp++; if (--narg != 0) (*pr)(","); } (*pr)(") at "); db_printsym(callpc, DB_STGY_PROC, pr); (*pr)("\n"); if (lastframe == 0 && offset == 0 && !have_addr) { /* Frame really belongs to next callpc */ lastframe = (struct x86_64_frame *)(ddb_regs.tf_rsp-8); callpc = (db_addr_t) db_get_value((db_addr_t)&lastframe->f_retaddr, 8, FALSE); continue; } lastframe = frame; db_nextframe(&frame, &callpc, &frame->f_arg0, is_trap, pr); if (frame == 0) { /* end of chain */ break; } if (INKERNEL(frame)) { /* staying in kernel */ if (frame <= lastframe) { (*pr)("Bad frame pointer: %p\n", frame); break; } } else if (INKERNEL(lastframe)) { /* switch from user to kernel */ if (kernel_only) { (*pr)("end of kernel\n"); break; /* kernel stack only */ } } else { /* in user */ if (frame <= lastframe) { (*pr)("Bad user frame pointer: %p\n", frame); break; } } --count; } (*pr)("end trace frame: 0x%lx, count: %d\n", frame, count); if (count && is_trap != NONE) { db_printsym(callpc, DB_STGY_XTRN, pr); (*pr)(":\n"); } }
void db_stack_trace_print(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif, void (*pr)(const char *, ...)) { long *frame, *lastframe; long *retaddr, *arg0; long *argp; db_addr_t callpc; int is_trap; bool kernel_only = true; bool trace_thread = false; bool lwpaddr = false; #if 0 if (!db_trace_symbols_found) db_find_trace_symbols(); #endif { const char *cp = modif; char c; while ((c = *cp++) != 0) { if (c == 'a') { lwpaddr = true; trace_thread = true; } if (c == 't') trace_thread = true; if (c == 'u') kernel_only = false; } } if (!have_addr) { frame = (long *)ddb_regs.tf_rbp; callpc = (db_addr_t)ddb_regs.tf_rip; } else { if (trace_thread) { struct proc *p; struct user *u; struct lwp *l; if (lwpaddr) { l = (struct lwp *)addr; p = l->l_proc; (*pr)("trace: pid %d ", p->p_pid); } else { (*pr)("trace: pid %d ", (int)addr); p = p_find(addr, PFIND_LOCKED); if (p == NULL) { (*pr)("not found\n"); return; } l = LIST_FIRST(&p->p_lwps); KASSERT(l != NULL); } (*pr)("lid %d ", l->l_lid); if (!(l->l_flag & LW_INMEM)) { (*pr)("swapped out\n"); return; } u = l->l_addr; if (p == curproc && l == curlwp) { frame = (long *)ddb_regs.tf_rbp; callpc = (db_addr_t)ddb_regs.tf_rip; (*pr)("at %p\n", frame); } else { frame = (long *)u->u_pcb.pcb_rbp; callpc = (db_addr_t) db_get_value((long)(frame + 1), 8, false); (*pr)("at %p\n", frame); frame = (long *)*frame; /* XXXfvdl db_get_value? */ } } else { frame = (long *)addr; callpc = (db_addr_t) db_get_value((long)(frame + 1), 8, false); frame = (long *)*frame; /* XXXfvdl db_get_value? */ } } retaddr = frame + 1; arg0 = frame + 2; lastframe = 0; while (count && frame != 0) { int narg; const char * name; db_expr_t offset; db_sym_t sym; char *argnames[16], **argnp = NULL; db_addr_t lastcallpc; name = "?"; is_trap = NONE; offset = 0; sym = db_frame_info(frame, callpc, &name, &offset, &is_trap, &narg); if (lastframe == 0 && sym == (db_sym_t)0) { /* Symbol not found, peek at code */ u_long instr = db_get_value(callpc, 4, false); offset = 1; if (instr == 0xe5894855 || /* enter: pushq %rbp, movq %rsp, %rbp */ (instr & 0x00ffffff) == 0x0048e589 /* enter+1: movq %rsp, %rbp */) { offset = 0; } } if (is_trap == NONE) { if (db_sym_numargs(sym, &narg, argnames)) argnp = argnames; else narg = db_numargs(frame); } (*pr)("%s(", name); if (lastframe == 0 && offset == 0 && !have_addr) { /* * We have a breakpoint before the frame is set up * Use %rsp instead */ argp = &((struct x86_64_frame *)(ddb_regs.tf_rsp-8))->f_arg0; } else { argp = frame + 2; } while (narg) { if (argnp) (*pr)("%s=", *argnp++); (*pr)("%lx", db_get_value((long)argp, 8, false)); argp++; if (--narg != 0) (*pr)(","); } (*pr)(") at "); db_printsym(callpc, DB_STGY_PROC, pr); (*pr)("\n"); if (lastframe == 0 && offset == 0 && !have_addr) { /* Frame really belongs to next callpc */ struct x86_64_frame *fp = (void *)(ddb_regs.tf_rsp-8); lastframe = (long *)fp; callpc = (db_addr_t) db_get_value((db_addr_t)&fp->f_retaddr, 8, false); continue; } lastframe = frame; lastcallpc = callpc; if (!db_nextframe(&frame, &retaddr, &arg0, &callpc, frame + 2, is_trap, pr)) break; if (INKERNEL((long)frame)) { /* staying in kernel */ if (frame < lastframe || (frame == lastframe && callpc == lastcallpc)) { (*pr)("Bad frame pointer: %p\n", frame); break; } } else if (INKERNEL((long)lastframe)) { /* switch from user to kernel */ if (kernel_only) break; /* kernel stack only */ } else { /* in user */ if (frame <= lastframe) { (*pr)("Bad user frame pointer: %p\n", frame); break; } } --count; } if (count && is_trap != NONE) { db_printsym(callpc, DB_STGY_XTRN, pr); (*pr)(":\n"); } }