diff --git a/sys/amd64/amd64/db_trace.c b/sys/amd64/amd64/db_trace.c index 9d9a2cd4a7bb..a57b38dd0818 100644 --- a/sys/amd64/amd64/db_trace.c +++ b/sys/amd64/amd64/db_trace.c @@ -1,375 +1,376 @@ /*- * Mach Operating System * Copyright (c) 1991,1990 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static db_varfcn_t db_frame; static db_varfcn_t db_frame_seg; CTASSERT(sizeof(struct dbreg) == sizeof(((struct pcpu *)NULL)->pc_dbreg)); /* * Machine register set. */ #define DB_OFFSET(x) (db_expr_t *)offsetof(struct trapframe, x) struct db_variable db_regs[] = { { "cs", DB_OFFSET(tf_cs), db_frame_seg }, { "ds", DB_OFFSET(tf_ds), db_frame_seg }, { "es", DB_OFFSET(tf_es), db_frame_seg }, { "fs", DB_OFFSET(tf_fs), db_frame_seg }, { "gs", DB_OFFSET(tf_gs), db_frame_seg }, { "ss", DB_OFFSET(tf_ss), db_frame_seg }, { "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", 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 }, { "r8", DB_OFFSET(tf_r8), db_frame }, { "r9", DB_OFFSET(tf_r9), db_frame }, { "r10", DB_OFFSET(tf_r10), db_frame }, { "r11", DB_OFFSET(tf_r11), db_frame }, { "r12", DB_OFFSET(tf_r12), db_frame }, { "r13", DB_OFFSET(tf_r13), db_frame }, { "r14", DB_OFFSET(tf_r14), db_frame }, { "r15", DB_OFFSET(tf_r15), db_frame }, { "rip", DB_OFFSET(tf_rip), db_frame }, { "rflags", DB_OFFSET(tf_rflags), db_frame }, }; struct db_variable *db_eregs = db_regs + nitems(db_regs); static int db_frame_seg(struct db_variable *vp, db_expr_t *valuep, int op) { uint16_t *reg; if (kdb_frame == NULL) return (0); reg = (uint16_t *)((uintptr_t)kdb_frame + (db_expr_t)vp->valuep); if (op == DB_VAR_GET) *valuep = *reg; else *reg = *valuep; return (1); } static int db_frame(struct db_variable *vp, db_expr_t *valuep, int op) { long *reg; if (kdb_frame == NULL) return (0); reg = (long *)((uintptr_t)kdb_frame + (db_expr_t)vp->valuep); if (op == DB_VAR_GET) *valuep = *reg; else *reg = *valuep; return (1); } #define NORMAL 0 #define TRAP 1 #define INTERRUPT 2 #define SYSCALL 3 static void db_print_stack_entry(const char *name, db_addr_t callpc, db_addr_t frame) { db_printf("%s() at ", name != NULL ? name : "??"); db_printsym(callpc, DB_STGY_PROC); if (frame != 0) db_printf("/frame %#lx", frame); db_printf("\n"); } /* * Figure out the next frame up in the call stack. */ static void db_nextframe(db_addr_t *fp, db_addr_t *ip, struct thread *td) { struct trapframe *tf; db_addr_t tf_addr; int frame_type; long rip, rsp, rbp; db_expr_t offset; c_db_sym_t sym; const char *name; rip = db_get_value(*fp + offsetof(struct amd64_frame, f_retaddr), 8, FALSE); rbp = db_get_value(*fp + offsetof(struct amd64_frame, f_frame), 8, FALSE); /* * Figure out frame type. We look at the address just before * the saved instruction pointer as the saved EIP is after the * call function, and if the function being called is marked as * dead (such as panic() at the end of dblfault_handler()), then * the instruction at the saved EIP will be part of a different * function (syscall() in this example) rather than the one that * actually made the call. */ frame_type = NORMAL; sym = db_search_symbol(rip - 1, DB_STGY_ANY, &offset); db_symbol_values(sym, &name, NULL); if (name != NULL) { if (strcmp(name, "calltrap") == 0 || strcmp(name, "fork_trampoline") == 0 || strcmp(name, "mchk_calltrap") == 0 || strcmp(name, "nmi_calltrap") == 0 || strcmp(name, "Xdblfault") == 0) frame_type = TRAP; else if (strncmp(name, "Xatpic_intr", 11) == 0 || strncmp(name, "Xapic_isr", 9) == 0 || strcmp(name, "Xxen_intr_upcall") == 0 || strcmp(name, "Xtimerint") == 0 || strcmp(name, "Xipi_intr_bitmap_handler") == 0 || strcmp(name, "Xcpustop") == 0 || strcmp(name, "Xcpususpend") == 0 || strcmp(name, "Xrendezvous") == 0) frame_type = INTERRUPT; else if (strcmp(name, "Xfast_syscall") == 0 || strcmp(name, "Xfast_syscall_pti") == 0 || strcmp(name, "fast_syscall_common") == 0) frame_type = SYSCALL; #ifdef COMPAT_FREEBSD32 else if (strcmp(name, "Xint0x80_syscall") == 0) frame_type = SYSCALL; #endif } /* * Normal frames need no special processing. */ if (frame_type == NORMAL) { *ip = rip; *fp = rbp; return; } db_print_stack_entry(name, rip, *fp); /* * Point to base of trapframe which is just above the * current frame. */ tf_addr = *fp + 16; - if (!__is_aligned(tf_addr, _Alignof(*tf)) || !INKERNEL(tf_addr)) { + if (!__is_aligned(tf_addr, _Alignof(struct trapframe)) || + !INKERNEL(tf_addr)) { db_printf("--- invalid trapframe %p\n", (void *)tf_addr); *ip = 0; *fp = 0; return; } tf = (struct trapframe *)tf_addr; rsp = tf->tf_rsp; rip = tf->tf_rip; rbp = tf->tf_rbp; switch (frame_type) { case TRAP: db_printf("--- trap %#r", tf->tf_trapno); break; case SYSCALL: db_printf("--- syscall"); db_decode_syscall(td, tf->tf_rax); break; case INTERRUPT: db_printf("--- interrupt"); break; default: __assert_unreachable(); } db_printf(", rip = %#lr, rsp = %#lr, rbp = %#lr ---\n", rip, rsp, rbp); *ip = rip; *fp = rbp; } static int __nosanitizeaddress __nosanitizememory db_backtrace(struct thread *td, struct trapframe *tf, db_addr_t frame, db_addr_t pc, register_t sp, int count) { db_addr_t actframe; const char *name; db_expr_t offset; c_db_sym_t sym; boolean_t first; if (count == -1) count = 1024; first = TRUE; while (count-- && !db_pager_quit) { sym = db_search_symbol(pc, DB_STGY_ANY, &offset); db_symbol_values(sym, &name, NULL); /* * Attempt to determine a (possibly fake) frame that gives * the caller's pc. It may differ from `frame' if the * current function never sets up a standard frame or hasn't * set one up yet or has just discarded one. The last two * cases can be guessed fairly reliably for code generated * by gcc. The first case is too much trouble to handle in * general because the amount of junk on the stack depends * on the pc (the special handling of "calltrap", etc. in * db_nextframe() works because the `next' pc is special). */ actframe = frame; if (first) { 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, 0); 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 = tf->tf_rsp - 8; } else if ((instr & 0xffffff) == 0xe58948) { /* movq %rsp, %rbp */ actframe = tf->tf_rsp; if (tf->tf_rbp == 0) { /* Fake frame better. */ frame = actframe; } } else if ((instr & 0xff) == 0xc3) { /* ret */ actframe = tf->tf_rsp - 8; } else if (offset == 0) { /* Probably an assembler symbol. */ actframe = tf->tf_rsp - 8; } } else if (name != NULL && 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, pc, actframe); break; } } db_print_stack_entry(name, pc, actframe); if (actframe != frame) { /* `frame' belongs to caller. */ pc = db_get_value(actframe + offsetof(struct amd64_frame, f_retaddr), 8, FALSE); continue; } db_nextframe(&frame, &pc, td); if (frame == 0) break; if (!__is_aligned(frame, _Alignof(struct amd64_frame)) || !INKERNEL(frame)) { if (INKERNEL(pc)) { sym = db_search_symbol(pc, DB_STGY_ANY, &offset); db_symbol_values(sym, &name, NULL); db_print_stack_entry(name, pc, frame); break; } break; } } return (0); } void db_trace_self(void) { db_addr_t callpc, frame; register_t rbp; __asm __volatile("movq %%rbp,%0" : "=r" (rbp)); callpc = db_get_value(rbp + offsetof(struct amd64_frame, f_retaddr), 8, FALSE); frame = db_get_value(rbp + offsetof(struct amd64_frame, f_frame), 8, FALSE); 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); tf = thr == kdb_thread ? kdb_frame : NULL; return (db_backtrace(thr, tf, ctx->pcb_rbp, ctx->pcb_rip, ctx->pcb_rsp, count)); } void db_md_list_watchpoints(void) { dbreg_list_watchpoints(); } diff --git a/sys/arm64/arm64/db_trace.c b/sys/arm64/arm64/db_trace.c index 1c9290f19ed5..4bc6af26a023 100644 --- a/sys/arm64/arm64/db_trace.c +++ b/sys/arm64/arm64/db_trace.c @@ -1,162 +1,162 @@ /*- * Copyright (c) 2015 The FreeBSD Foundation * * This software was developed by Semihalf under * the sponsorship of the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #define FRAME_NORMAL 0 #define FRAME_SYNC 1 #define FRAME_IRQ 2 #define FRAME_SERROR 3 #define FRAME_UNHANDLED 4 void db_md_list_watchpoints(void) { dbg_show_watchpoint(); } static void __nosanitizeaddress db_stack_trace_cmd(struct thread *td, struct unwind_state *frame) { c_db_sym_t sym; const char *name; db_expr_t value; db_expr_t offset; int frame_type; while (1) { sym = db_search_symbol(frame->pc, DB_STGY_ANY, &offset); if (sym == C_DB_SYM_NULL) { value = 0; name = "(null)"; } else db_symbol_values(sym, &name, &value); db_printf("%s() at ", name); db_printsym(frame->pc, DB_STGY_PROC); db_printf("\n"); if (strcmp(name, "handle_el0_sync") == 0 || strcmp(name, "handle_el1h_sync") == 0) frame_type = FRAME_SYNC; else if (strcmp(name, "handle_el0_irq") == 0 || strcmp(name, "handle_el1h_irq") == 0) frame_type = FRAME_IRQ; else if (strcmp(name, "handle_serror") == 0) frame_type = FRAME_SERROR; else if (strcmp(name, "handle_empty_exception") == 0) frame_type = FRAME_UNHANDLED; else frame_type = FRAME_NORMAL; if (frame_type != FRAME_NORMAL) { struct trapframe *tf; tf = (struct trapframe *)(uintptr_t)frame->fp - 1; - if (!__is_aligned(tf, _Alignof(*tf)) || + if (!__is_aligned(tf, _Alignof(struct trapframe)) || !kstack_contains(td, (vm_offset_t)tf, sizeof(*tf))) { db_printf("--- invalid trapframe %p\n", tf); break; } switch (frame_type) { case FRAME_SYNC: db_printf("--- exception, esr %#lx\n", tf->tf_esr); break; case FRAME_IRQ: db_printf("--- interrupt\n"); break; case FRAME_SERROR: db_printf("--- system error, esr %#lx\n", tf->tf_esr); break; case FRAME_UNHANDLED: db_printf("--- unhandled exception, esr %#lx\n", tf->tf_esr); break; default: __assert_unreachable(); break; } frame->fp = tf->tf_x[29]; frame->pc = ADDR_MAKE_CANONICAL(tf->tf_elr); if (!INKERNEL(frame->fp)) break; } else { if (strcmp(name, "fork_trampoline") == 0) break; if (!unwind_frame(td, frame)) break; } } } int __nosanitizeaddress db_trace_thread(struct thread *thr, int count) { struct unwind_state frame; struct pcb *ctx; if (thr != curthread) { ctx = kdb_thr_ctx(thr); frame.fp = (uintptr_t)ctx->pcb_x[PCB_FP]; frame.pc = (uintptr_t)ctx->pcb_x[PCB_LR]; db_stack_trace_cmd(thr, &frame); } else db_trace_self(); return (0); } void __nosanitizeaddress db_trace_self(void) { struct unwind_state frame; frame.fp = (uintptr_t)__builtin_frame_address(0); frame.pc = (uintptr_t)db_trace_self; db_stack_trace_cmd(curthread, &frame); } diff --git a/sys/riscv/riscv/db_trace.c b/sys/riscv/riscv/db_trace.c index d3a52ecf59bf..0d374b1493d7 100644 --- a/sys/riscv/riscv/db_trace.c +++ b/sys/riscv/riscv/db_trace.c @@ -1,148 +1,148 @@ /*- * Copyright (c) 2015 The FreeBSD Foundation * Copyright (c) 2016 Ruslan Bukin * All rights reserved. * Copyright (c) 2020 John Baldwin * * Portions of this software were developed by Semihalf under * the sponsorship of the FreeBSD Foundation. * * Portions of this software were developed by SRI International and the * University of Cambridge Computer Laboratory under DARPA/AFRL contract * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme. * * Portions of this software were developed by the University of Cambridge * Computer Laboratory as part of the CTSRD Project, with support from the * UK Higher Education Innovation Fund (HEIF). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include void db_md_list_watchpoints(void) { } static void db_stack_trace_cmd(struct thread *td, struct unwind_state *frame) { const char *name; db_expr_t offset; db_expr_t value; c_db_sym_t sym; uint64_t pc; while (1) { pc = frame->pc; sym = db_search_symbol(pc, DB_STGY_ANY, &offset); if (sym == C_DB_SYM_NULL) { value = 0; name = "(null)"; } else db_symbol_values(sym, &name, &value); db_printf("%s() at ", name); db_printsym(frame->pc, DB_STGY_PROC); db_printf("\n"); if (strcmp(name, "cpu_exception_handler_supervisor") == 0 || strcmp(name, "cpu_exception_handler_user") == 0) { struct trapframe *tf; tf = (struct trapframe *)(uintptr_t)frame->sp; - if (!__is_aligned(tf, _Alignof(*tf)) || + if (!__is_aligned(tf, _Alignof(struct trapframe)) || !kstack_contains(td, (vm_offset_t)tf, sizeof(*tf))) { db_printf("--- invalid trapframe %p\n", tf); break; } if ((tf->tf_scause & SCAUSE_INTR) != 0) { db_printf("--- interrupt %ld\n", tf->tf_scause & SCAUSE_CODE); } else if (tf->tf_scause == SCAUSE_ECALL_USER) { db_printf("--- syscall"); db_decode_syscall(td, td->td_sa.code); db_printf("\n"); } else { db_printf("--- exception %ld, tval = %#lx\n", tf->tf_scause & SCAUSE_CODE, tf->tf_stval); } frame->sp = tf->tf_sp; frame->fp = tf->tf_s[0]; frame->pc = tf->tf_sepc; if (!INKERNEL(frame->fp)) break; continue; } if (strcmp(name, "fork_trampoline") == 0) break; if (!unwind_frame(td, frame)) break; } } int db_trace_thread(struct thread *thr, int count) { struct unwind_state frame; struct pcb *ctx; ctx = kdb_thr_ctx(thr); frame.sp = ctx->pcb_sp; frame.fp = ctx->pcb_s[0]; frame.pc = ctx->pcb_ra; db_stack_trace_cmd(thr, &frame); return (0); } void db_trace_self(void) { struct unwind_state frame; uintptr_t sp; __asm __volatile("mv %0, sp" : "=&r" (sp)); frame.sp = sp; frame.fp = (uintptr_t)__builtin_frame_address(0); frame.pc = (uintptr_t)db_trace_self; db_stack_trace_cmd(curthread, &frame); }