diff --git a/sys/amd64/amd64/db_trace.c b/sys/amd64/amd64/db_trace.c index beeb5cd6e9f3..4c569c456e92 100644 --- a/sys/amd64/amd64/db_trace.c +++ b/sys/amd64/amd64/db_trace.c @@ -1,408 +1,393 @@ /*- * 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 __FBSDID("$FreeBSD$"); #include #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_nextframe(struct amd64_frame **, db_addr_t *, struct thread *); static void db_print_stack_entry(const char *, db_addr_t, void *); static void decode_syscall(int, struct thread *); static void db_print_stack_entry(const char *name, db_addr_t callpc, void *frame) { db_printf("%s() at ", name != NULL ? name : "??"); db_printsym(callpc, DB_STGY_PROC); if (frame != NULL) db_printf("/frame 0x%lx", (register_t)frame); db_printf("\n"); } static void decode_syscall(int number, struct thread *td) { struct proc *p; c_db_sym_t sym; db_expr_t diff; sy_call_t *f; const char *symname; db_printf(" (%d", number); p = (td != NULL) ? td->td_proc : NULL; if (p != NULL && 0 <= number && number < p->p_sysent->sv_size) { f = p->p_sysent->sv_table[number].sy_call; sym = db_search_symbol((db_addr_t)f, DB_STGY_ANY, &diff); if (sym != DB_SYM_NULL && diff == 0) { db_symbol_values(sym, &symname, NULL); db_printf(", %s, %s", p->p_sysent->sv_name, symname); } } db_printf(")"); } /* * Figure out the next frame up in the call stack. */ static void db_nextframe(struct amd64_frame **fp, db_addr_t *ip, struct thread *td) { struct trapframe *tf; int frame_type; long rip, rsp, rbp; db_expr_t offset; c_db_sym_t sym; const char *name; rip = db_get_value((long) &(*fp)->f_retaddr, 8, FALSE); rbp = db_get_value((long) &(*fp)->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 = (db_addr_t) rip; *fp = (struct amd64_frame *) rbp; return; } db_print_stack_entry(name, rip, &(*fp)->f_frame); /* * Point to base of trapframe which is just above the * current frame. */ tf = (struct trapframe *)((long)*fp + 16); if (INKERNEL((long) tf)) { 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"); decode_syscall(tf->tf_rax, td); break; case INTERRUPT: db_printf("--- interrupt"); break; default: panic("The moon has moved again."); } db_printf(", rip = %#lr, rsp = %#lr, rbp = %#lr ---\n", rip, rsp, rbp); } *ip = (db_addr_t) rip; *fp = (struct amd64_frame *) rbp; } static int 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; 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, 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 *)(tf->tf_rsp - 8); } else if ((instr & 0xffffff) == 0xe58948) { /* movq %rsp, %rbp */ actframe = (void *)tf->tf_rsp; if (tf->tf_rbp == 0) { /* Fake frame better. */ frame = actframe; } } else if ((instr & 0xff) == 0xc3) { /* ret */ actframe = (void *)(tf->tf_rsp - 8); } else if (offset == 0) { /* Probably an assembler symbol. */ actframe = (void *)(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_addr_t) db_get_value((long)&actframe->f_retaddr, 8, FALSE); continue; } db_nextframe(&frame, &pc, td); 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, pc, frame); break; } if (!INKERNEL((long) frame)) { break; } } return (0); } void db_trace_self(void) { struct amd64_frame *frame; db_addr_t callpc; register_t rbp; __asm __volatile("movq %%rbp,%0" : "=r" (rbp)); 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, 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, (struct amd64_frame *)ctx->pcb_rbp, ctx->pcb_rip, ctx->pcb_rsp, count)); } -int -db_md_set_watchpoint(db_expr_t addr, db_expr_t size) -{ - - return (dbreg_set_watchpoint((vm_offset_t)addr, (vm_size_t)size, - DBREG_DR7_WRONLY)); -} - -int -db_md_clr_watchpoint(db_expr_t addr, db_expr_t size) -{ - - return (dbreg_clr_watchpoint((vm_offset_t)addr, (vm_size_t)size)); -} - void db_md_list_watchpoints(void) { dbreg_list_watchpoints(); } diff --git a/sys/arm/arm/db_trace.c b/sys/arm/arm/db_trace.c index 195ed0f4e3d9..3f308c9f546c 100644 --- a/sys/arm/arm/db_trace.c +++ b/sys/arm/arm/db_trace.c @@ -1,186 +1,172 @@ /* $NetBSD: db_trace.c,v 1.8 2003/01/17 22:28:48 thorpej Exp $ */ /*- * Copyright (c) 2000, 2001 Ben Harris * Copyright (c) 1996 Scott K. Stevens * * 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 "AS IS" * 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 "opt_ddb.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void db_stack_trace_cmd(struct unwind_state *state) { const char *name; db_expr_t value; db_expr_t offset; c_db_sym_t sym; u_int reg, i; char *sep; uint16_t upd_mask; bool finished; finished = false; while (!finished) { finished = unwind_stack_one(state, 1); /* Print the frame details */ sym = db_search_symbol(state->start_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(state->start_pc, DB_STGY_PROC); db_printf("\n"); db_printf("\t pc = 0x%08x lr = 0x%08x (", state->start_pc, state->registers[LR]); db_printsym(state->registers[LR], DB_STGY_PROC); db_printf(")\n"); db_printf("\t sp = 0x%08x fp = 0x%08x", state->registers[SP], state->registers[FP]); /* Don't print the registers we have already printed */ upd_mask = state->update_mask & ~((1 << SP) | (1 << FP) | (1 << LR) | (1 << PC)); sep = "\n\t"; for (i = 0, reg = 0; upd_mask != 0; upd_mask >>= 1, reg++) { if ((upd_mask & 1) != 0) { db_printf("%s%sr%d = 0x%08x", sep, (reg < 10) ? " " : "", reg, state->registers[reg]); i++; if (i == 2) { sep = "\n\t"; i = 0; } else sep = " "; } } db_printf("\n"); if (finished) break; /* * Stop if directed to do so, or if we've unwound back to the * kernel entry point, or if the unwind function didn't change * anything (to avoid getting stuck in this loop forever). * If the latter happens, it's an indication that the unwind * information is incorrect somehow for the function named in * the last frame printed before you see the unwind failure * message (maybe it needs a STOP_UNWINDING). */ if (state->registers[PC] < VM_MIN_KERNEL_ADDRESS) { db_printf("Unable to unwind into user mode\n"); finished = true; } else if (state->update_mask == 0) { db_printf("Unwind failure (no registers changed)\n"); finished = true; } } } void db_md_list_watchpoints(void) { dbg_show_watchpoint(); } -int -db_md_clr_watchpoint(db_expr_t addr, db_expr_t size) -{ - - return (dbg_remove_watchpoint(addr, size)); -} - -int -db_md_set_watchpoint(db_expr_t addr, db_expr_t size) -{ - - return (dbg_setup_watchpoint(addr, size, HW_WATCHPOINT_RW)); -} - int db_trace_thread(struct thread *thr, int count) { struct unwind_state state; struct pcb *ctx; if (thr != curthread) { ctx = kdb_thr_ctx(thr); state.registers[FP] = ctx->pcb_regs.sf_r11; state.registers[SP] = ctx->pcb_regs.sf_sp; state.registers[LR] = ctx->pcb_regs.sf_lr; state.registers[PC] = ctx->pcb_regs.sf_pc; db_stack_trace_cmd(&state); } else db_trace_self(); return (0); } void db_trace_self(void) { struct unwind_state state; uint32_t sp; /* Read the stack pointer */ __asm __volatile("mov %0, sp" : "=&r" (sp)); state.registers[FP] = (uint32_t)__builtin_frame_address(0); state.registers[SP] = sp; state.registers[LR] = (uint32_t)__builtin_return_address(0); state.registers[PC] = (uint32_t)db_trace_self; db_stack_trace_cmd(&state); } diff --git a/sys/arm64/arm64/db_trace.c b/sys/arm64/arm64/db_trace.c index 1743d7765861..9f9d00620652 100644 --- a/sys/arm64/arm64/db_trace.c +++ b/sys/arm64/arm64/db_trace.c @@ -1,131 +1,117 @@ /*- * Copyright (c) 2015 The FreeBSD Foundation * All rights reserved. * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include void db_md_list_watchpoints() { dbg_show_watchpoint(); } -int -db_md_clr_watchpoint(db_expr_t addr, db_expr_t size) -{ - - return (dbg_remove_watchpoint(NULL, addr, size)); -} - -int -db_md_set_watchpoint(db_expr_t addr, db_expr_t size) -{ - - return (dbg_setup_watchpoint(NULL, addr, size, HW_BREAKPOINT_RW)); -} - static void 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; while (1) { uintptr_t pc = frame->pc; if (!unwind_frame(td, frame)) break; 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"); db_printf("\t pc = 0x%016lx lr = 0x%016lx\n", pc, frame->pc); db_printf("\t sp = 0x%016lx fp = 0x%016lx\n", frame->sp, frame->fp); /* TODO: Show some more registers */ db_printf("\n"); } } int db_trace_thread(struct thread *thr, int count) { struct unwind_state frame; struct pcb *ctx; if (thr != curthread) { ctx = kdb_thr_ctx(thr); frame.sp = (uintptr_t)ctx->pcb_sp; frame.fp = (uintptr_t)ctx->pcb_x[29]; frame.pc = (uintptr_t)ctx->pcb_lr; db_stack_trace_cmd(thr, &frame); } else db_trace_self(); return (0); } void db_trace_self(void) { struct unwind_state frame; uintptr_t sp; __asm __volatile("mov %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); } diff --git a/sys/ddb/db_watch.c b/sys/ddb/db_watch.c index f0fbb6768c12..3226b050a4c3 100644 --- a/sys/ddb/db_watch.c +++ b/sys/ddb/db_watch.c @@ -1,310 +1,337 @@ /*- * SPDX-License-Identifier: MIT-CMU * * 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. */ /* * Author: Richard P. Draves, Carnegie Mellon University * Date: 10/90 */ #include __FBSDID("$FreeBSD$"); #include +#include #include #include #include #include #include #include +#include + #include #include /* * Watchpoints. */ static bool db_watchpoints_inserted = true; #define NWATCHPOINTS 100 static struct db_watchpoint db_watch_table[NWATCHPOINTS]; static db_watchpoint_t db_next_free_watchpoint = &db_watch_table[0]; static db_watchpoint_t db_free_watchpoints = 0; static db_watchpoint_t db_watchpoint_list = 0; static db_watchpoint_t db_watchpoint_alloc(void); static void db_watchpoint_free(db_watchpoint_t watch); static void db_delete_watchpoint(vm_map_t map, db_addr_t addr); #ifdef notused static bool db_find_watchpoint(vm_map_t map, db_addr_t addr, db_regs_t *regs); #endif static void db_list_watchpoints(void); static void db_set_watchpoint(vm_map_t map, db_addr_t addr, vm_size_t size); static db_watchpoint_t db_watchpoint_alloc(void) { db_watchpoint_t watch; if ((watch = db_free_watchpoints) != 0) { db_free_watchpoints = watch->link; return (watch); } if (db_next_free_watchpoint == &db_watch_table[NWATCHPOINTS]) { db_printf("All watchpoints used.\n"); return (0); } watch = db_next_free_watchpoint; db_next_free_watchpoint++; return (watch); } static void db_watchpoint_free(db_watchpoint_t watch) { watch->link = db_free_watchpoints; db_free_watchpoints = watch; } static void db_set_watchpoint(vm_map_t map, db_addr_t addr, vm_size_t size) { db_watchpoint_t watch; if (map == NULL) { db_printf("No map.\n"); return; } /* * Should we do anything fancy with overlapping regions? */ for (watch = db_watchpoint_list; watch != 0; watch = watch->link) if (db_map_equal(watch->map, map) && (watch->loaddr == addr) && (watch->hiaddr == addr+size)) { db_printf("Already set.\n"); return; } watch = db_watchpoint_alloc(); if (watch == 0) { db_printf("Too many watchpoints.\n"); return; } watch->map = map; watch->loaddr = addr; watch->hiaddr = addr+size; watch->link = db_watchpoint_list; db_watchpoint_list = watch; db_watchpoints_inserted = false; } static void db_delete_watchpoint(vm_map_t map, db_addr_t addr) { db_watchpoint_t watch; db_watchpoint_t *prev; for (prev = &db_watchpoint_list; (watch = *prev) != 0; prev = &watch->link) if (db_map_equal(watch->map, map) && (watch->loaddr <= addr) && (addr < watch->hiaddr)) { *prev = watch->link; db_watchpoint_free(watch); return; } db_printf("Not set.\n"); } static void db_list_watchpoints(void) { db_watchpoint_t watch; if (db_watchpoint_list == 0) { db_printf("No watchpoints set\n"); return; } #ifdef __LP64__ db_printf(" Map Address Size\n"); #else db_printf(" Map Address Size\n"); #endif for (watch = db_watchpoint_list; watch != 0; watch = watch->link) #ifdef __LP64__ db_printf("%s%16p %16lx %lx\n", #else db_printf("%s%8p %8lx %lx\n", #endif db_map_current(watch->map) ? "*" : " ", (void *)watch->map, (long)watch->loaddr, (long)watch->hiaddr - (long)watch->loaddr); } /* Delete watchpoint */ /*ARGSUSED*/ void db_deletewatch_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) { db_delete_watchpoint(db_map_addr(addr), addr); } /* Set watchpoint */ /*ARGSUSED*/ void db_watchpoint_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) { vm_size_t size; db_expr_t value; if (db_expression(&value)) size = (vm_size_t) value; else size = 4; db_skip_to_eol(); db_set_watchpoint(db_map_addr(addr), addr, size); } /* * At least one non-optional show-command must be implemented using * DB_SHOW_COMMAND() so that db_show_cmd_set gets created. Here is one. */ DB_SHOW_COMMAND(watches, db_listwatch_cmd) { db_list_watchpoints(); db_md_list_watchpoints(); } void db_set_watchpoints(void) { db_watchpoint_t watch; if (!db_watchpoints_inserted) { for (watch = db_watchpoint_list; watch != 0; watch = watch->link) pmap_protect(watch->map->pmap, trunc_page(watch->loaddr), round_page(watch->hiaddr), VM_PROT_READ); db_watchpoints_inserted = true; } } void db_clear_watchpoints(void) { db_watchpoints_inserted = false; } #ifdef notused static bool db_find_watchpoint(vm_map_t map, db_addr_t addr, db_regs_t regs) { db_watchpoint_t watch; db_watchpoint_t found = 0; for (watch = db_watchpoint_list; watch != 0; watch = watch->link) if (db_map_equal(watch->map, map)) { if ((watch->loaddr <= addr) && (addr < watch->hiaddr)) return (true); else if ((trunc_page(watch->loaddr) <= addr) && (addr < round_page(watch->hiaddr))) found = watch; } /* * We didn't hit exactly on a watchpoint, but we are * in a protected region. We want to single-step * and then re-protect. */ if (found) { db_watchpoints_inserted = false; db_single_step(regs); } return (false); } #endif /* Delete hardware watchpoint */ -/*ARGSUSED*/ void -db_deletehwatch_cmd(db_expr_t addr, bool have_addr, db_expr_t count, +db_deletehwatch_cmd(db_expr_t addr, bool have_addr, db_expr_t size, char *modif) { int rc; - if (count < 0) - count = 4; - - rc = db_md_clr_watchpoint(addr, count); - if (rc < 0) - db_printf("hardware watchpoint could not be deleted\n"); + if (size < 0) + size = 4; + + rc = kdb_cpu_clr_watchpoint((vm_offset_t)addr, (vm_size_t)size); + switch (rc) { + case ENXIO: + /* Not supported, ignored. */ + break; + case EINVAL: + db_printf("Invalid watchpoint address or size.\n"); + break; + default: + if (rc != 0) + db_printf("Hardware watchpoint could not be deleted, " + "status=%d\n", rc); + break; + } } /* Set hardware watchpoint */ -/*ARGSUSED*/ void -db_hwatchpoint_cmd(db_expr_t addr, bool have_addr, db_expr_t count, +db_hwatchpoint_cmd(db_expr_t addr, bool have_addr, db_expr_t size, char *modif) { int rc; - if (count < 0) - count = 4; - - rc = db_md_set_watchpoint(addr, count); - if (rc < 0) - db_printf("hardware watchpoint could not be set\n"); + if (size < 0) + size = 4; + + rc = kdb_cpu_set_watchpoint((vm_offset_t)addr, (vm_size_t)size, + KDB_DBG_ACCESS_W); + + switch (rc) { + case EINVAL: + db_printf("Invalid watchpoint size or address.\n"); + break; + case EBUSY: + db_printf("No hardware watchpoints available.\n"); + break; + case ENXIO: + db_printf("Hardware watchpoints are not supported on this platform.\n"); + break; + default: + if (rc != 0) + db_printf("Could not set hardware watchpoint, " + "status=%d\n", rc); + } } diff --git a/sys/ddb/ddb.h b/sys/ddb/ddb.h index 81448474c514..21bc0712117f 100644 --- a/sys/ddb/ddb.h +++ b/sys/ddb/ddb.h @@ -1,299 +1,297 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1993, Garrett A. Wollman. * Copyright (c) 1993, University of Vermont and State Agricultural College. * All rights reserved. * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * 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. * * $FreeBSD$ */ /* * Necessary declarations for the `ddb' kernel debugger. */ #ifndef _DDB_DDB_H_ #define _DDB_DDB_H_ #ifdef SYSCTL_DECL SYSCTL_DECL(_debug_ddb); #endif #include /* type definitions */ #include /* LIST_* */ #include /* SYSINIT */ #ifndef DB_MAXARGS #define DB_MAXARGS 10 #endif #ifndef DB_MAXLINE #define DB_MAXLINE 120 #endif #ifndef DB_MAXSCRIPTS #define DB_MAXSCRIPTS 8 #endif #ifndef DB_MAXSCRIPTNAME #define DB_MAXSCRIPTNAME 32 #endif #ifndef DB_MAXSCRIPTLEN #define DB_MAXSCRIPTLEN 128 #endif #ifndef DB_MAXSCRIPTRECURSION #define DB_MAXSCRIPTRECURSION 3 #endif #ifndef DB_CALL #define DB_CALL db_fncall_generic #else int DB_CALL(db_expr_t, db_expr_t *, int, db_expr_t[]); #endif /* * Extern variables to set the address and size of the symtab and strtab. * Most users should use db_fetch_symtab in order to set them from the * boot loader provided values. */ extern vm_offset_t ksymtab, kstrtab, ksymtab_size, ksymtab_relbase; /* * There are three "command tables": * - One for simple commands; a list of these is displayed * by typing 'help' at the debugger prompt. * - One for sub-commands of 'show'; to see this type 'show' * without any arguments. * - The last one for sub-commands of 'show all'; type 'show all' * without any argument to get a list. */ struct command; LIST_HEAD(command_table, command); extern struct command_table db_cmd_table; extern struct command_table db_show_table; extern struct command_table db_show_all_table; /* * Type signature for a function implementing a ddb command. */ typedef void db_cmdfcn_t(db_expr_t addr, bool have_addr, db_expr_t count, char *modif); /* * Command table entry. */ struct command { char * name; /* command name */ db_cmdfcn_t *fcn; /* function to call */ int flag; /* extra info: */ #define CS_OWN 0x1 /* non-standard syntax */ #define CS_MORE 0x2 /* standard syntax, but may have other words * at end */ #define CS_SET_DOT 0x100 /* set dot after command */ struct command_table *more; /* another level of command */ LIST_ENTRY(command) next; /* next entry in the command table */ }; /* * Arrange for the specified ddb command to be defined and * bound to the specified function. Commands can be defined * in modules in which case they will be available only when * the module is loaded. */ #define _DB_SET(_suffix, _name, _func, list, _flag, _more) \ static struct command __CONCAT(_name,_suffix) = { \ .name = __STRING(_name), \ .fcn = _func, \ .flag = _flag, \ .more = _more \ }; \ static void __CONCAT(__CONCAT(_name,_suffix),_add)(void *arg __unused) \ { db_command_register(&list, &__CONCAT(_name,_suffix)); } \ SYSINIT(__CONCAT(_name,_suffix), SI_SUB_KLD, SI_ORDER_ANY, \ __CONCAT(__CONCAT(_name,_suffix),_add), NULL); \ static void __CONCAT(__CONCAT(_name,_suffix),_del)(void *arg __unused) \ { db_command_unregister(&list, &__CONCAT(_name,_suffix)); } \ SYSUNINIT(__CONCAT(_name,_suffix), SI_SUB_KLD, SI_ORDER_ANY, \ __CONCAT(__CONCAT(_name,_suffix),_del), NULL); /* * Like _DB_SET but also create the function declaration which * must be followed immediately by the body; e.g. * _DB_FUNC(_cmd, panic, db_panic, db_cmd_table, 0, NULL) * { * ...panic implementation... * } * * This macro is mostly used to define commands placed in one of * the ddb command tables; see DB_COMMAND, etc. below. */ #define _DB_FUNC(_suffix, _name, _func, list, _flag, _more) \ static db_cmdfcn_t _func; \ _DB_SET(_suffix, _name, _func, list, _flag, _more); \ static void \ _func(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) /* common idom provided for backwards compatibility */ #define DB_FUNC(_name, _func, list, _flag, _more) \ _DB_FUNC(_cmd, _name, _func, list, _flag, _more) #define DB_COMMAND(cmd_name, func_name) \ _DB_FUNC(_cmd, cmd_name, func_name, db_cmd_table, 0, NULL) #define DB_ALIAS(alias_name, func_name) \ _DB_SET(_cmd, alias_name, func_name, db_cmd_table, 0, NULL) #define DB_SHOW_COMMAND(cmd_name, func_name) \ _DB_FUNC(_show, cmd_name, func_name, db_show_table, 0, NULL) #define DB_SHOW_ALIAS(alias_name, func_name) \ _DB_SET(_show, alias_name, func_name, db_show_table, 0, NULL) #define DB_SHOW_ALL_COMMAND(cmd_name, func_name) \ _DB_FUNC(_show_all, cmd_name, func_name, db_show_all_table, 0, NULL) #define DB_SHOW_ALL_ALIAS(alias_name, func_name) \ _DB_SET(_show_all, alias_name, func_name, db_show_all_table, 0, NULL) extern db_expr_t db_maxoff; extern int db_indent; extern int db_inst_count; extern int db_load_count; extern int db_store_count; extern volatile int db_pager_quit; extern db_expr_t db_radix; extern db_expr_t db_max_width; extern db_expr_t db_tab_stop_width; extern db_expr_t db_lines_per_page; struct thread; struct vm_map; void db_check_interrupt(void); void db_clear_watchpoints(void); db_addr_t db_disasm(db_addr_t loc, bool altfmt); /* instruction disassembler */ void db_error(const char *s); int db_expression(db_expr_t *valuep); int db_get_variable(db_expr_t *valuep); void db_iprintf(const char *,...) __printflike(1, 2); struct proc *db_lookup_proc(db_expr_t addr); struct thread *db_lookup_thread(db_expr_t addr, bool check_pid); struct vm_map *db_map_addr(vm_offset_t); bool db_map_current(struct vm_map *); bool db_map_equal(struct vm_map *, struct vm_map *); -int db_md_set_watchpoint(db_expr_t addr, db_expr_t size); -int db_md_clr_watchpoint(db_expr_t addr, db_expr_t size); void db_md_list_watchpoints(void); void db_print_loc_and_inst(db_addr_t loc); void db_print_thread(void); int db_printf(const char *fmt, ...) __printflike(1, 2); int db_read_bytes(vm_offset_t addr, size_t size, char *data); /* machine-dependent */ int db_readline(char *lstart, int lsize); void db_restart_at_pc(bool watchpt); int db_set_variable(db_expr_t value); void db_set_watchpoints(void); void db_skip_to_eol(void); bool db_stop_at_pc(int type, int code, bool *is_breakpoint, bool *is_watchpoint); #define db_strcpy strcpy void db_trace_self(void); int db_trace_thread(struct thread *, int); bool db_value_of_name(const char *name, db_expr_t *valuep); bool db_value_of_name_pcpu(const char *name, db_expr_t *valuep); bool db_value_of_name_vnet(const char *name, db_expr_t *valuep); int db_write_bytes(vm_offset_t addr, size_t size, char *data); void db_command_register(struct command_table *, struct command *); void db_command_unregister(struct command_table *, struct command *); int db_fetch_ksymtab(vm_offset_t ksym_start, vm_offset_t ksym_end, vm_offset_t relbase); db_cmdfcn_t db_breakpoint_cmd; db_cmdfcn_t db_capture_cmd; db_cmdfcn_t db_continue_cmd; db_cmdfcn_t db_delete_cmd; db_cmdfcn_t db_deletehwatch_cmd; db_cmdfcn_t db_deletewatch_cmd; db_cmdfcn_t db_examine_cmd; db_cmdfcn_t db_findstack_cmd; db_cmdfcn_t db_hwatchpoint_cmd; db_cmdfcn_t db_listbreak_cmd; db_cmdfcn_t db_scripts_cmd; db_cmdfcn_t db_print_cmd; db_cmdfcn_t db_ps; db_cmdfcn_t db_run_cmd; db_cmdfcn_t db_script_cmd; db_cmdfcn_t db_search_cmd; db_cmdfcn_t db_set_cmd; db_cmdfcn_t db_set_thread; db_cmdfcn_t db_show_regs; db_cmdfcn_t db_show_threads; db_cmdfcn_t db_single_step_cmd; db_cmdfcn_t db_textdump_cmd; db_cmdfcn_t db_trace_until_call_cmd; db_cmdfcn_t db_trace_until_matching_cmd; db_cmdfcn_t db_unscript_cmd; db_cmdfcn_t db_watchpoint_cmd; db_cmdfcn_t db_write_cmd; /* * Interface between DDB and the DDB output capture facility. */ struct dumperinfo; void db_capture_dump(struct dumperinfo *di); void db_capture_enterpager(void); void db_capture_exitpager(void); void db_capture_write(char *buffer, u_int buflen); void db_capture_writech(char ch); /* * Interface between DDB and the script facility. */ void db_script_kdbenter(const char *eventname); /* KDB enter event. */ /* * Interface between DDB and the textdump facility. * * Text dump blocks are of a fixed size; textdump_block_buffer is a * statically allocated buffer that code interacting with textdumps can use * to prepare and hold a pending block in when calling writenextblock(). */ #define TEXTDUMP_BLOCKSIZE 512 extern char textdump_block_buffer[TEXTDUMP_BLOCKSIZE]; void textdump_mkustar(char *block_buffer, const char *filename, u_int size); void textdump_restoreoff(off_t offset); void textdump_saveoff(off_t *offsetp); int textdump_writenextblock(struct dumperinfo *di, char *buffer); /* * Interface between the kernel and textdumps. */ extern int textdump_pending; /* Call textdump_dumpsys() instead. */ void textdump_dumpsys(struct dumperinfo *di); #endif /* !_DDB_DDB_H_ */ diff --git a/sys/i386/i386/db_trace.c b/sys/i386/i386/db_trace.c index 50fb1fa6355d..80ef0fe715d8 100644 --- a/sys/i386/i386/db_trace.c +++ b/sys/i386/i386/db_trace.c @@ -1,634 +1,620 @@ /*- * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static db_varfcn_t db_esp; static db_varfcn_t db_frame; static db_varfcn_t db_frame_seg; static db_varfcn_t db_gs; static db_varfcn_t db_ss; /* * 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", NULL, db_gs }, { "ss", NULL, db_ss }, { "eax", DB_OFFSET(tf_eax), db_frame }, { "ecx", DB_OFFSET(tf_ecx), db_frame }, { "edx", DB_OFFSET(tf_edx), db_frame }, { "ebx", DB_OFFSET(tf_ebx), db_frame }, { "esp", NULL, db_esp }, { "ebp", DB_OFFSET(tf_ebp), db_frame }, { "esi", DB_OFFSET(tf_esi), db_frame }, { "edi", DB_OFFSET(tf_edi), db_frame }, { "eip", DB_OFFSET(tf_eip), db_frame }, { "efl", DB_OFFSET(tf_eflags), db_frame }, }; struct db_variable *db_eregs = db_regs + nitems(db_regs); static __inline int get_esp(struct trapframe *tf) { return (TF_HAS_STACKREGS(tf) ? tf->tf_esp : (intptr_t)&tf->tf_esp); } static int db_frame(struct db_variable *vp, db_expr_t *valuep, int op) { int *reg; if (kdb_frame == NULL) return (0); reg = (int *)((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_seg(struct db_variable *vp, db_expr_t *valuep, int op) { struct trapframe_vm86 *tfp; int off; uint16_t *reg; if (kdb_frame == NULL) return (0); off = (intptr_t)vp->valuep; if (kdb_frame->tf_eflags & PSL_VM) { tfp = (void *)kdb_frame; switch ((intptr_t)vp->valuep) { case (intptr_t)DB_OFFSET(tf_cs): reg = (uint16_t *)&tfp->tf_cs; break; case (intptr_t)DB_OFFSET(tf_ds): reg = (uint16_t *)&tfp->tf_vm86_ds; break; case (intptr_t)DB_OFFSET(tf_es): reg = (uint16_t *)&tfp->tf_vm86_es; break; case (intptr_t)DB_OFFSET(tf_fs): reg = (uint16_t *)&tfp->tf_vm86_fs; break; } } else reg = (uint16_t *)((uintptr_t)kdb_frame + off); if (op == DB_VAR_GET) *valuep = *reg; else *reg = *valuep; return (1); } static int db_esp(struct db_variable *vp, db_expr_t *valuep, int op) { if (kdb_frame == NULL) return (0); if (op == DB_VAR_GET) *valuep = get_esp(kdb_frame); else if (TF_HAS_STACKREGS(kdb_frame)) kdb_frame->tf_esp = *valuep; return (1); } static int db_gs(struct db_variable *vp, db_expr_t *valuep, int op) { struct trapframe_vm86 *tfp; if (kdb_frame != NULL && kdb_frame->tf_eflags & PSL_VM) { tfp = (void *)kdb_frame; if (op == DB_VAR_GET) *valuep = tfp->tf_vm86_gs; else tfp->tf_vm86_gs = *valuep; return (1); } if (op == DB_VAR_GET) *valuep = rgs(); else load_gs(*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 = TF_HAS_STACKREGS(kdb_frame) ? kdb_frame->tf_ss : rss(); else if (TF_HAS_STACKREGS(kdb_frame)) kdb_frame->tf_ss = *valuep; return (1); } #define NORMAL 0 #define TRAP 1 #define INTERRUPT 2 #define SYSCALL 3 #define DOUBLE_FAULT 4 static void db_nextframe(struct i386_frame **, db_addr_t *, struct thread *); static int db_numargs(struct i386_frame *); static void db_print_stack_entry(const char *, int, char **, int *, db_addr_t, void *); static void decode_syscall(int, struct thread *); /* * Figure out how many arguments were passed into the frame at "fp". */ static int db_numargs(fp) struct i386_frame *fp; { char *argp; int inst; int args; argp = (char *)db_get_value((int)&fp->f_retaddr, 4, 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 < btext || argp >= etext) { args = -1; } else { retry: inst = db_get_value((int)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 if ((inst & 0xf8ff) == 0xc089) { /* movl %eax, %Reg */ argp += 2; goto retry; } else args = -1; } return (args); } static void db_print_stack_entry(name, narg, argnp, argp, callpc, frame) const char *name; int narg; char **argnp; int *argp; db_addr_t callpc; void *frame; { int n = narg >= 0 ? narg : 5; db_printf("%s(", name); while (n) { if (argnp) db_printf("%s=", *argnp++); db_printf("%r", db_get_value((int)argp, 4, false)); argp++; if (--n != 0) db_printf(","); } if (narg < 0) db_printf(",..."); db_printf(") at "); db_printsym(callpc, DB_STGY_PROC); if (frame != NULL) db_printf("/frame 0x%r", (register_t)frame); db_printf("\n"); } static void decode_syscall(int number, struct thread *td) { struct proc *p; c_db_sym_t sym; db_expr_t diff; sy_call_t *f; const char *symname; db_printf(" (%d", number); p = (td != NULL) ? td->td_proc : NULL; if (p != NULL && 0 <= number && number < p->p_sysent->sv_size) { f = p->p_sysent->sv_table[number].sy_call; sym = db_search_symbol((db_addr_t)f, DB_STGY_ANY, &diff); if (sym != DB_SYM_NULL && diff == 0) { db_symbol_values(sym, &symname, NULL); db_printf(", %s, %s", p->p_sysent->sv_name, symname); } } db_printf(")"); } /* * Figure out the next frame up in the call stack. */ static void db_nextframe(struct i386_frame **fp, db_addr_t *ip, struct thread *td) { struct trapframe *tf; int frame_type; int eip, esp, ebp; db_expr_t offset; c_db_sym_t sym; const char *name; eip = db_get_value((int) &(*fp)->f_retaddr, 4, false); ebp = db_get_value((int) &(*fp)->f_frame, 4, 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; if (eip >= PMAP_TRM_MIN_ADDRESS) { sym = db_search_symbol(eip - 1 - setidt_disp, DB_STGY_ANY, &offset); } else { sym = db_search_symbol(eip - 1, DB_STGY_ANY, &offset); } db_symbol_values(sym, &name, NULL); if (name != NULL) { if (strcmp(name, "calltrap") == 0 || strcmp(name, "fork_trampoline") == 0) frame_type = TRAP; else if (strncmp(name, "Xatpic_intr", 11) == 0 || strncmp(name, "Xapic_isr", 9) == 0) { frame_type = INTERRUPT; } else if (strcmp(name, "Xlcall_syscall") == 0 || strcmp(name, "Xint0x80_syscall") == 0) frame_type = SYSCALL; else if (strcmp(name, "dblfault_handler") == 0) frame_type = DOUBLE_FAULT; else if (strcmp(name, "Xtimerint") == 0 || strcmp(name, "Xxen_intr_upcall") == 0) frame_type = INTERRUPT; else if (strcmp(name, "Xcpustop") == 0 || strcmp(name, "Xrendezvous") == 0 || strcmp(name, "Xipi_intr_bitmap_handler") == 0) { /* No arguments. */ frame_type = INTERRUPT; } } /* * Normal frames need no special processing. */ if (frame_type == NORMAL) { *ip = (db_addr_t) eip; *fp = (struct i386_frame *) ebp; return; } db_print_stack_entry(name, 0, 0, 0, eip, &(*fp)->f_frame); /* * For a double fault, we have to snag the values from the * previous TSS since a double fault uses a task gate to * switch to a known good state. */ if (frame_type == DOUBLE_FAULT) { esp = PCPU_GET(common_tssp)->tss_esp; eip = PCPU_GET(common_tssp)->tss_eip; ebp = PCPU_GET(common_tssp)->tss_ebp; db_printf( "--- trap 0x17, eip = %#r, esp = %#r, ebp = %#r ---\n", eip, esp, ebp); *ip = (db_addr_t) eip; *fp = (struct i386_frame *) ebp; return; } /* * Point to base of trapframe which is just above the current * frame. Pointer to it was put into %ebp by the kernel entry * code. */ tf = (struct trapframe *)(*fp)->f_frame; /* * This can be the case for e.g. fork_trampoline, last frame * of a kernel thread stack. */ if (tf == NULL) { *ip = 0; *fp = 0; db_printf("--- kthread start\n"); return; } esp = get_esp(tf); eip = tf->tf_eip; ebp = tf->tf_ebp; switch (frame_type) { case TRAP: db_printf("--- trap %#r", tf->tf_trapno); break; case SYSCALL: db_printf("--- syscall"); decode_syscall(tf->tf_eax, td); break; case INTERRUPT: db_printf("--- interrupt"); break; default: panic("The moon has moved again."); } db_printf(", eip = %#r, esp = %#r, ebp = %#r ---\n", eip, esp, ebp); /* * Detect the last (trap) frame on the kernel stack, where we * entered kernel from usermode. Terminate tracing in this * case. */ switch (frame_type) { case TRAP: case INTERRUPT: if (!TRAPF_USERMODE(tf)) break; /* FALLTHROUGH */ case SYSCALL: ebp = 0; eip = 0; break; } *ip = (db_addr_t) eip; *fp = (struct i386_frame *) ebp; } static int db_backtrace(struct thread *td, struct trapframe *tf, struct i386_frame *frame, db_addr_t pc, register_t sp, int count) { struct i386_frame *actframe; #define MAXNARG 16 char *argnames[MAXNARG], **argnp = NULL; const char *name; int *argp; db_expr_t offset; c_db_sym_t sym; int instr, narg; bool first; if (db_segsize(tf) == 16) { db_printf( "--- 16-bit%s, cs:eip = %#x:%#x, ss:esp = %#x:%#x, ebp = %#x, tf = %p ---\n", (tf->tf_eflags & PSL_VM) ? " (vm86)" : "", tf->tf_cs, tf->tf_eip, TF_HAS_STACKREGS(tf) ? tf->tf_ss : rss(), TF_HAS_STACKREGS(tf) ? tf->tf_esp : (intptr_t)&tf->tf_esp, tf->tf_ebp, tf); return (0); } /* 'frame' can be null initially. Just print the pc then. */ if (frame == NULL) goto out; /* * If an indirect call via an invalid pointer caused a trap, * %pc contains the invalid address while the return address * of the unlucky caller has been saved by CPU on the stack * just before the trap frame. In this case, try to recover * the caller's address so that the first frame is assigned * to the right spot in the right function, for that is where * the failure actually happened. * * This trick depends on the fault address stashed in tf_err * by trap_fatal() before entering KDB. */ if (kdb_frame && pc == kdb_frame->tf_err) { /* * Find where the trap frame actually ends. * It won't contain tf_esp or tf_ss unless crossing rings. */ if (TF_HAS_STACKREGS(kdb_frame)) instr = (int)(kdb_frame + 1); else instr = (int)&kdb_frame->tf_esp; pc = db_get_value(instr, 4, false); } 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, 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 */ actframe = (void *)(get_esp(tf) - 4); } else if ((instr & 0xffff) == 0x0000e589) { /* movl %esp, %ebp */ actframe = (void *)get_esp(tf); if (tf->tf_ebp == 0) { /* Fake frame better. */ frame = actframe; } } else if ((instr & 0xff) == 0x000000c3) { /* ret */ actframe = (void *)(get_esp(tf) - 4); } else if (offset == 0) { /* Probably an assembler symbol. */ actframe = (void *)(get_esp(tf) - 4); } } 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); break; } } 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); if (actframe != frame) { /* `frame' belongs to caller. */ pc = (db_addr_t) db_get_value((int)&actframe->f_retaddr, 4, false); continue; } db_nextframe(&frame, &pc, td); out: /* * 'frame' can be null here, either because it was initially * null or because db_nextframe() found no frame. * db_nextframe() may also have found a non-kernel frame. * !INKERNEL() classifies both. Stop tracing if either, * after printing the pc if it is the kernel. */ if (frame == NULL || frame <= actframe) { if (pc != 0) { 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); } break; } } return (0); } void db_trace_self(void) { struct i386_frame *frame; db_addr_t callpc; register_t ebp; __asm __volatile("movl %%ebp,%0" : "=r" (ebp)); 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, 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, (struct i386_frame *)ctx->pcb_ebp, ctx->pcb_eip, ctx->pcb_esp, count)); } -int -db_md_set_watchpoint(db_expr_t addr, db_expr_t size) -{ - - return (dbreg_set_watchpoint((vm_offset_t)addr, (vm_size_t)size)); -} - -int -db_md_clr_watchpoint(db_expr_t addr, db_expr_t size) -{ - - return (dbreg_clr_watchpoint((vm_offset_t)addr, (vm_size_t)size)); -} - void db_md_list_watchpoints(void) { dbreg_list_watchpoints(); } diff --git a/sys/mips/mips/db_trace.c b/sys/mips/mips/db_trace.c index 0762fdb8a1da..4903b6d3d432 100644 --- a/sys/mips/mips/db_trace.c +++ b/sys/mips/mips/db_trace.c @@ -1,460 +1,446 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2004-2005, Juniper Networks, Inc. * All rights reserved. * * 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. * * JNPR: db_trace.c,v 1.8 2007/08/09 11:23:32 katta */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern char _locore[]; extern char _locoreEnd[]; extern char edata[]; /* * A function using a stack frame has the following instruction as the first * one: [d]addiu sp,sp,- * * We make use of this to detect starting address of a function. This works * better than using 'j ra' instruction to signify end of the previous * function (for e.g. functions like boot() or panic() do not actually * emit a 'j ra' instruction). * * XXX the abi does not require that the addiu instruction be the first one. */ #define MIPS_START_OF_FUNCTION(ins) ((((ins) & 0xffff8000) == 0x27bd8000) \ || (((ins) & 0xffff8000) == 0x67bd8000)) /* * LLD will insert invalid instruction traps between functions. * Currently this is 0xefefefef but it may change in the future. */ #define MIPS_LLD_PADDING_BETWEEN_FUNCTIONS(ins) ((ins) == 0xefefefef) #if defined(__mips_n64) # define MIPS_IS_VALID_KERNELADDR(reg) ((((reg) & 3) == 0) && \ ((vm_offset_t)(reg) >= MIPS_XKPHYS_START)) #else # define MIPS_IS_VALID_KERNELADDR(reg) ((((reg) & 3) == 0) && \ ((vm_offset_t)(reg) >= MIPS_KSEG0_START)) #endif static void stacktrace_subr(register_t pc, register_t sp, register_t ra) { InstFmt i; /* * Arrays for a0..a3 registers and flags if content * of these registers is valid, e.g. obtained from the stack */ int valid_args[4]; register_t args[4]; register_t va, subr, cause, badvaddr; unsigned instr, mask; unsigned int frames = 0; int more, stksize, j; register_t next_ra; bool trapframe; /* Jump here when done with a frame, to start a new one */ loop: /* * Invalidate arguments values */ valid_args[0] = 0; valid_args[1] = 0; valid_args[2] = 0; valid_args[3] = 0; next_ra = 0; stksize = 0; subr = 0; trapframe = false; if (frames++ > 100) { db_printf("\nstackframe count exceeded\n"); return; } /* Check for bad SP: could foul up next frame. */ if (!MIPS_IS_VALID_KERNELADDR(sp)) { db_printf("SP 0x%jx: not in kernel\n", (uintmax_t)sp); ra = 0; subr = 0; goto done; } #define Between(x, y, z) \ ( ((x) <= (y)) && ((y) < (z)) ) #define pcBetween(a,b) \ Between((uintptr_t)a, pc, (uintptr_t)b) /* * Check for current PC in exception handler code that don't have a * preceding "j ra" at the tail of the preceding function. Depends * on relative ordering of functions in exception.S, swtch.S. */ if (pcBetween(MipsKernGenException, MipsUserGenException)) { subr = (uintptr_t)MipsKernGenException; trapframe = true; } else if (pcBetween(MipsUserGenException, MipsKernIntr)) subr = (uintptr_t)MipsUserGenException; else if (pcBetween(MipsKernIntr, MipsUserIntr)) { subr = (uintptr_t)MipsKernIntr; trapframe = true; } else if (pcBetween(MipsUserIntr, MipsTLBInvalidException)) subr = (uintptr_t)MipsUserIntr; else if (pcBetween(MipsTLBInvalidException, MipsTLBMissException)) { subr = (uintptr_t)MipsTLBInvalidException; if (pc == (uintptr_t)MipsKStackOverflow) trapframe = true; } else if (pcBetween(fork_trampoline, savectx)) subr = (uintptr_t)fork_trampoline; else if (pcBetween(savectx, cpu_throw)) subr = (uintptr_t)savectx; else if (pcBetween(cpu_throw, cpu_switch)) subr = (uintptr_t)cpu_throw; else if (pcBetween(cpu_switch, MipsSwitchFPState)) subr = (uintptr_t)cpu_switch; else if (pcBetween(_locore, _locoreEnd)) { subr = (uintptr_t)_locore; ra = 0; goto done; } /* Check for bad PC. */ if (!MIPS_IS_VALID_KERNELADDR(pc)) { db_printf("PC 0x%jx: not in kernel\n", (uintmax_t)pc); ra = 0; goto done; } /* * For a trapframe, skip to the output and afterwards pull the * previous registers out of the trapframe instead of decoding * the function prologue. */ if (trapframe) goto done; /* * Find the beginning of the current subroutine by scanning * backwards from the current PC for the end of the previous * subroutine. */ if (!subr) { va = pc; while (1) { instr = kdbpeek((int *)va); /* LLD fills padding between functions with 0xefefefef */ if (MIPS_LLD_PADDING_BETWEEN_FUNCTIONS(instr)) break; if (MIPS_START_OF_FUNCTION(instr)) break; va -= sizeof(int); } /* * Skip over nulls/trap padding which might separate * object files or functions. */ instr = kdbpeek((int *)va); while (instr == 0 || MIPS_LLD_PADDING_BETWEEN_FUNCTIONS(instr)) { va += sizeof(int); instr = kdbpeek((int *)va); } subr = va; } /* scan forwards to find stack size and any saved registers */ stksize = 0; more = 3; mask = 0; for (va = subr; more; va += sizeof(int), more = (more == 3) ? 3 : more - 1) { /* stop if hit our current position */ if (va >= pc) break; instr = kdbpeek((int *)va); i.word = instr; switch (i.JType.op) { case OP_SPECIAL: switch (i.RType.func) { case OP_JR: case OP_JALR: more = 2; /* stop after next instruction */ break; case OP_SYSCALL: case OP_BREAK: more = 1; /* stop now */ } break; case OP_BCOND: case OP_J: case OP_JAL: case OP_BEQ: case OP_BNE: case OP_BLEZ: case OP_BGTZ: more = 2; /* stop after next instruction */ break; case OP_COP0: case OP_COP1: case OP_COP2: case OP_COP3: switch (i.RType.rs) { case OP_BCx: case OP_BCy: more = 2; /* stop after next instruction */ } break; case OP_SW: /* look for saved registers on the stack */ if (i.IType.rs != 29) break; /* * only restore the first one except RA for * MipsKernGenException case */ if (mask & (1 << i.IType.rt)) { if (subr == (uintptr_t)MipsKernGenException && i.IType.rt == 31) next_ra = kdbpeek((int *)(sp + (short)i.IType.imm)); break; } mask |= (1 << i.IType.rt); switch (i.IType.rt) { case 4:/* a0 */ args[0] = kdbpeek((int *)(sp + (short)i.IType.imm)); valid_args[0] = 1; break; case 5:/* a1 */ args[1] = kdbpeek((int *)(sp + (short)i.IType.imm)); valid_args[1] = 1; break; case 6:/* a2 */ args[2] = kdbpeek((int *)(sp + (short)i.IType.imm)); valid_args[2] = 1; break; case 7:/* a3 */ args[3] = kdbpeek((int *)(sp + (short)i.IType.imm)); valid_args[3] = 1; break; case 31: /* ra */ ra = kdbpeek((int *)(sp + (short)i.IType.imm)); } break; case OP_SD: /* look for saved registers on the stack */ if (i.IType.rs != 29) break; /* only restore the first one */ if (mask & (1 << i.IType.rt)) break; mask |= (1 << i.IType.rt); switch (i.IType.rt) { case 4:/* a0 */ args[0] = kdbpeekd((int *)(sp + (short)i.IType.imm)); valid_args[0] = 1; break; case 5:/* a1 */ args[1] = kdbpeekd((int *)(sp + (short)i.IType.imm)); valid_args[1] = 1; break; case 6:/* a2 */ args[2] = kdbpeekd((int *)(sp + (short)i.IType.imm)); valid_args[2] = 1; break; case 7:/* a3 */ args[3] = kdbpeekd((int *)(sp + (short)i.IType.imm)); valid_args[3] = 1; break; case 31: /* ra */ ra = kdbpeekd((int *)(sp + (short)i.IType.imm)); } break; case OP_ADDI: case OP_ADDIU: case OP_DADDI: case OP_DADDIU: /* look for stack pointer adjustment */ if (i.IType.rs != 29 || i.IType.rt != 29) break; stksize = -((short)i.IType.imm); } } done: db_printsym(pc, DB_STGY_PROC); db_printf(" ("); for (j = 0; j < 4; j ++) { if (j > 0) db_printf(","); if (valid_args[j]) db_printf("%jx", (uintmax_t)(u_register_t)args[j]); else db_printf("?"); } db_printf(") ra %jx sp %jx sz %d\n", (uintmax_t)(u_register_t) ra, (uintmax_t)(u_register_t) sp, stksize); if (trapframe) { #define TF_REG(base, reg) ((base) + CALLFRAME_SIZ + ((reg) * SZREG)) #if defined(__mips_n64) || defined(__mips_n32) pc = kdbpeekd((int *)TF_REG(sp, PC)); ra = kdbpeekd((int *)TF_REG(sp, RA)); sp = kdbpeekd((int *)TF_REG(sp, SP)); cause = kdbpeekd((int *)TF_REG(sp, CAUSE)); badvaddr = kdbpeekd((int *)TF_REG(sp, BADVADDR)); #else pc = kdbpeek((int *)TF_REG(sp, PC)); ra = kdbpeek((int *)TF_REG(sp, RA)); sp = kdbpeek((int *)TF_REG(sp, SP)); cause = kdbpeek((int *)TF_REG(sp, CAUSE)); badvaddr = kdbpeek((int *)TF_REG(sp, BADVADDR)); #endif #undef TF_REG db_printf("--- exception, cause %jx badvaddr %jx ---\n", (uintmax_t)cause, (uintmax_t)badvaddr); goto loop; } else if (ra) { /* * We subtract two instructions from ra to convert it * from a return address to a calling address, * accounting for the delay slot. */ register_t next_pc = ra - 2 * sizeof(int); if (pc == next_pc && stksize == 0) db_printf("stacktrace: loop!\n"); else { pc = next_pc; sp += stksize; ra = next_ra; goto loop; } } } -int -db_md_set_watchpoint(db_expr_t addr, db_expr_t size) -{ - - return(0); -} - -int -db_md_clr_watchpoint(db_expr_t addr, db_expr_t size) -{ - - return(0); -} - void db_md_list_watchpoints() { } void db_trace_self(void) { register_t pc, ra, sp; sp = (register_t)(intptr_t)__builtin_frame_address(0); ra = (register_t)(intptr_t)__builtin_return_address(0); __asm __volatile( "jal 99f\n" "nop\n" "99:\n" "move %0, $31\n" /* get ra */ "move $31, %1\n" /* restore ra */ : "=r" (pc) : "r" (ra)); stacktrace_subr(pc, sp, ra); return; } int db_trace_thread(struct thread *thr, int count) { register_t pc, ra, sp; struct pcb *ctx; ctx = kdb_thr_ctx(thr); sp = (register_t)ctx->pcb_context[PCB_REG_SP]; pc = (register_t)ctx->pcb_context[PCB_REG_PC]; ra = (register_t)ctx->pcb_context[PCB_REG_RA]; stacktrace_subr(pc, sp, ra); return (0); } void db_show_mdpcpu(struct pcpu *pc) { db_printf("ipis = 0x%x\n", pc->pc_pending_ipis); db_printf("next ASID = %d\n", pc->pc_next_asid); db_printf("GENID = %d\n", pc->pc_asid_generation); return; } diff --git a/sys/powerpc/powerpc/db_hwwatch.c b/sys/powerpc/powerpc/db_hwwatch.c index 37b4a9148495..aa9018a58417 100644 --- a/sys/powerpc/powerpc/db_hwwatch.c +++ b/sys/powerpc/powerpc/db_hwwatch.c @@ -1,56 +1,42 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2002 Benno Rice. * All rights reserved. * * 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 Benno Rice ``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 TOOLS GMBH 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include -int -db_md_set_watchpoint(db_expr_t addr, db_expr_t size) -{ - - return (0); -} - -int -db_md_clr_watchpoint(db_expr_t addr, db_expr_t size) -{ - - return (0); -} - void db_md_list_watchpoints(void) { } diff --git a/sys/riscv/riscv/db_trace.c b/sys/riscv/riscv/db_trace.c index b8421fa3aa7a..dbc2ba92bce3 100644 --- a/sys/riscv/riscv/db_trace.c +++ b/sys/riscv/riscv/db_trace.c @@ -1,159 +1,145 @@ /*- * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include void db_md_list_watchpoints() { } -int -db_md_clr_watchpoint(db_expr_t addr, db_expr_t size) -{ - - return (0); -} - -int -db_md_set_watchpoint(db_expr_t addr, db_expr_t size) -{ - - return (0); -} - 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 (!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 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); }