Module Name: src Committed By: christos Date: Sun Apr 10 20:36:49 UTC 2011
Modified Files: src/sys/arch/amd64/amd64: db_disasm.c src/sys/arch/amd64/conf: files.amd64 src/sys/arch/amd64/include: db_machdep.h src/sys/arch/i386/conf: files.i386 src/sys/arch/i386/include: db_machdep.h src/sys/arch/x86/conf: files.x86 Added Files: src/sys/arch/amd64/amd64: db_machdep.c src/sys/arch/i386/i386: db_machdep.c src/sys/arch/x86/include: db_machdep.h src/sys/arch/x86/x86: db_trace.c Removed Files: src/sys/arch/amd64/amd64: db_trace.c src/sys/arch/i386/i386: db_trace.c Log Message: Merge db_trace for x86. From: Vladimir Kirillov proger at wilab dot org dot ua To generate a diff of this commit: cvs rdiff -u -r1.14 -r1.15 src/sys/arch/amd64/amd64/db_disasm.c cvs rdiff -u -r0 -r1.1 src/sys/arch/amd64/amd64/db_machdep.c cvs rdiff -u -r1.18 -r0 src/sys/arch/amd64/amd64/db_trace.c cvs rdiff -u -r1.71 -r1.72 src/sys/arch/amd64/conf/files.amd64 cvs rdiff -u -r1.9 -r1.10 src/sys/arch/amd64/include/db_machdep.h cvs rdiff -u -r1.356 -r1.357 src/sys/arch/i386/conf/files.i386 cvs rdiff -u -r0 -r1.1 src/sys/arch/i386/i386/db_machdep.c cvs rdiff -u -r1.66 -r0 src/sys/arch/i386/i386/db_trace.c cvs rdiff -u -r1.27 -r1.28 src/sys/arch/i386/include/db_machdep.h cvs rdiff -u -r1.66 -r1.67 src/sys/arch/x86/conf/files.x86 cvs rdiff -u -r0 -r1.1 src/sys/arch/x86/include/db_machdep.h cvs rdiff -u -r0 -r1.1 src/sys/arch/x86/x86/db_trace.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/amd64/amd64/db_disasm.c diff -u src/sys/arch/amd64/amd64/db_disasm.c:1.14 src/sys/arch/amd64/amd64/db_disasm.c:1.15 --- src/sys/arch/amd64/amd64/db_disasm.c:1.14 Fri Feb 18 13:00:52 2011 +++ src/sys/arch/amd64/amd64/db_disasm.c Sun Apr 10 16:36:49 2011 @@ -1,4 +1,4 @@ -/* $NetBSD: db_disasm.c,v 1.14 2011/02/18 18:00:52 drochner Exp $ */ +/* $NetBSD: db_disasm.c,v 1.15 2011/04/10 20:36:49 christos Exp $ */ /* * Mach Operating System @@ -33,10 +33,9 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: db_disasm.c,v 1.14 2011/02/18 18:00:52 drochner Exp $"); +__KERNEL_RCSID(0, "$NetBSD: db_disasm.c,v 1.15 2011/04/10 20:36:49 christos Exp $"); #ifndef _KERNEL -#include "stubs.h" #include <sys/types.h> #include <sys/time.h> #include <sys/ksyms.h> Index: src/sys/arch/amd64/conf/files.amd64 diff -u src/sys/arch/amd64/conf/files.amd64:1.71 src/sys/arch/amd64/conf/files.amd64:1.72 --- src/sys/arch/amd64/conf/files.amd64:1.71 Mon Apr 4 17:35:31 2011 +++ src/sys/arch/amd64/conf/files.amd64 Sun Apr 10 16:36:49 2011 @@ -1,4 +1,4 @@ -# $NetBSD: files.amd64,v 1.71 2011/04/04 21:35:31 dyoung Exp $ +# $NetBSD: files.amd64,v 1.72 2011/04/10 20:36:49 christos Exp $ # # new style config file for amd64 architecture # @@ -34,9 +34,9 @@ file arch/amd64/amd64/cpu_in_cksum.S (inet | inet6) & cpu_in_cksum file arch/amd64/amd64/cpufunc.S file arch/amd64/amd64/db_disasm.c ddb -file arch/amd64/amd64/db_interface.c ddb +file arch/amd64/amd64/db_interface.c ddb +file arch/amd64/amd64/db_machdep.c ddb file arch/amd64/amd64/db_memrw.c ddb | kgdb -file arch/amd64/amd64/db_trace.c ddb file arch/amd64/amd64/kobj_machdep.c modular file arch/amd64/amd64/kgdb_machdep.c kgdb file kern/subr_disk_mbr.c disk Index: src/sys/arch/amd64/include/db_machdep.h diff -u src/sys/arch/amd64/include/db_machdep.h:1.9 src/sys/arch/amd64/include/db_machdep.h:1.10 --- src/sys/arch/amd64/include/db_machdep.h:1.9 Sat Mar 14 10:45:54 2009 +++ src/sys/arch/amd64/include/db_machdep.h Sun Apr 10 16:36:49 2011 @@ -1,4 +1,4 @@ -/* $NetBSD: db_machdep.h,v 1.9 2009/03/14 14:45:54 dsl Exp $ */ +/* $NetBSD: db_machdep.h,v 1.10 2011/04/10 20:36:49 christos Exp $ */ /* * Mach Operating System @@ -44,6 +44,13 @@ typedef long db_expr_t; /* expression - signed */ typedef struct trapframe db_regs_t; + +struct x86_64_frame { + struct x86_64_frame *f_frame; + long f_retaddr; + long f_arg0; +}; + #ifndef MULTIPROCESSOR extern db_regs_t ddb_regs; /* register state */ #define DDB_REGS (&ddb_regs) @@ -125,10 +132,12 @@ int kdb_trap(int, int, db_regs_t *); +#ifdef _KERNEL /* * We define some of our own commands */ #define DB_MACHINE_COMMANDS +#endif #define DB_ELF_SYMBOLS #define DB_ELFSIZE 64 Index: src/sys/arch/i386/conf/files.i386 diff -u src/sys/arch/i386/conf/files.i386:1.356 src/sys/arch/i386/conf/files.i386:1.357 --- src/sys/arch/i386/conf/files.i386:1.356 Thu Feb 24 05:56:01 2011 +++ src/sys/arch/i386/conf/files.i386 Sun Apr 10 16:36:48 2011 @@ -1,4 +1,4 @@ -# $NetBSD: files.i386,v 1.356 2011/02/24 10:56:01 jruoho Exp $ +# $NetBSD: files.i386,v 1.357 2011/04/10 20:36:48 christos Exp $ # # new style config file for i386 architecture # @@ -67,8 +67,8 @@ file arch/i386/i386/db_dbgreg.S ddb | kstack_check_dr0 file arch/i386/i386/db_disasm.c ddb file arch/i386/i386/db_interface.c ddb +file arch/i386/i386/db_machdep.c ddb file arch/i386/i386/db_memrw.c ddb | kgdb -file arch/i386/i386/db_trace.c ddb file arch/i386/i386/dumpsys.c file kern/subr_disk_mbr.c disk file kern/subr_spldebug.c spldebug Index: src/sys/arch/i386/include/db_machdep.h diff -u src/sys/arch/i386/include/db_machdep.h:1.27 src/sys/arch/i386/include/db_machdep.h:1.28 --- src/sys/arch/i386/include/db_machdep.h:1.27 Sat Mar 7 17:02:16 2009 +++ src/sys/arch/i386/include/db_machdep.h Sun Apr 10 16:36:48 2011 @@ -1,4 +1,4 @@ -/* $NetBSD: db_machdep.h,v 1.27 2009/03/07 22:02:16 ad Exp $ */ +/* $NetBSD: db_machdep.h,v 1.28 2011/04/10 20:36:48 christos Exp $ */ /* * Mach Operating System @@ -44,6 +44,13 @@ typedef long db_expr_t; /* expression - signed */ typedef struct trapframe db_regs_t; + +struct i386_frame { + struct i386_frame *f_frame; + int f_retaddr; + int f_arg0; +}; + #ifndef MULTIPROCESSOR extern db_regs_t ddb_regs; /* register state */ #define DDB_REGS (&ddb_regs) @@ -142,4 +149,7 @@ extern void cpu_debug_dump(void); +/* i386/db_machdep.c */ +bool db_intrstack_p(const void *); + #endif /* _I386_DB_MACHDEP_H_ */ Index: src/sys/arch/x86/conf/files.x86 diff -u src/sys/arch/x86/conf/files.x86:1.66 src/sys/arch/x86/conf/files.x86:1.67 --- src/sys/arch/x86/conf/files.x86:1.66 Mon Apr 4 17:33:49 2011 +++ src/sys/arch/x86/conf/files.x86 Sun Apr 10 16:36:48 2011 @@ -1,4 +1,4 @@ -# $NetBSD: files.x86,v 1.66 2011/04/04 21:33:49 dyoung Exp $ +# $NetBSD: files.x86,v 1.67 2011/04/10 20:36:48 christos Exp $ # options for MP configuration through the MP spec defflag opt_mpbios.h MPBIOS MPVERBOSE MPDEBUG MPBIOS_SCANPCI @@ -67,6 +67,7 @@ file arch/x86/x86/consinit.c file arch/x86/x86/core_machdep.c coredump file arch/x86/x86/cpu_topology.c +file arch/x86/x86/db_trace.c ddb file arch/x86/x86/errata.c file arch/x86/x86/genfb_machdep.c file arch/x86/x86/identcpu.c Added files: Index: src/sys/arch/amd64/amd64/db_machdep.c diff -u /dev/null src/sys/arch/amd64/amd64/db_machdep.c:1.1 --- /dev/null Sun Apr 10 16:36:50 2011 +++ src/sys/arch/amd64/amd64/db_machdep.c Sun Apr 10 16:36:49 2011 @@ -0,0 +1,239 @@ +/* $NetBSD: db_machdep.c,v 1.1 2011/04/10 20:36:49 christos Exp $ */ + +/* + * 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.distribut...@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 <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: db_machdep.c,v 1.1 2011/04/10 20:36:49 christos Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> + +#include <machine/frame.h> +#include <machine/trap.h> +#include <machine/intrdefs.h> + +#include <machine/db_machdep.h> +#include <ddb/db_sym.h> +#include <ddb/db_access.h> +#include <ddb/db_variables.h> +#include <ddb/db_output.h> +#include <ddb/db_interface.h> +#include <ddb/db_user.h> +#include <ddb/db_proc.h> +#include <ddb/db_command.h> +#include <x86/db_machdep.h> + +#define dbreg(xx) (long *)offsetof(db_regs_t, tf_ ## xx) + +/* + * Machine register set. + */ +const struct db_variable db_regs[] = { + { "ds", dbreg(ds), db_x86_regop, NULL }, + { "es", dbreg(es), db_x86_regop, NULL }, + { "fs", dbreg(fs), db_x86_regop, NULL }, + { "gs", dbreg(gs), db_x86_regop, NULL }, + { "rdi", dbreg(rdi), db_x86_regop, NULL }, + { "rsi", dbreg(rsi), db_x86_regop, NULL }, + { "rbp", dbreg(rbp), db_x86_regop, NULL }, + { "rbx", dbreg(rbx), db_x86_regop, NULL }, + { "rdx", dbreg(rdx), db_x86_regop, NULL }, + { "rcx", dbreg(rcx), db_x86_regop, NULL }, + { "rax", dbreg(rax), db_x86_regop, NULL }, + { "r8", dbreg(r8), db_x86_regop, NULL }, + { "r9", dbreg(r9), db_x86_regop, NULL }, + { "r10", dbreg(r10), db_x86_regop, NULL }, + { "r11", dbreg(r11), db_x86_regop, NULL }, + { "r12", dbreg(r12), db_x86_regop, NULL }, + { "r13", dbreg(r13), db_x86_regop, NULL }, + { "r14", dbreg(r14), db_x86_regop, NULL }, + { "r15", dbreg(r15), db_x86_regop, NULL }, + { "rip", dbreg(rip), db_x86_regop, NULL }, + { "cs", dbreg(cs), db_x86_regop, NULL }, + { "rflags", dbreg(rflags), db_x86_regop, NULL }, + { "rsp", dbreg(rsp), db_x86_regop, NULL }, + { "ss", dbreg(ss), db_x86_regop, NULL }, +}; +const struct db_variable * const db_eregs = + db_regs + sizeof(db_regs)/sizeof(db_regs[0]); + +/* + * Figure out how many arguments were passed into the frame at "fp". + * We can probably figure out how many arguments where passed above + * the first 6 (which are in registers), but since we can't + * reliably determine the values currently, just return 0. + */ +int +db_numargs(long *retaddrp) +{ + return 0; +} + +/* + * Figure out the next frame up in the call stack. + * For trap(), we print the address of the faulting instruction and + * proceed with the calling frame. We return the ip that faulted. + * If the trap was caused by jumping through a bogus pointer, then + * the next line in the backtrace will list some random function as + * being called. It should get the argument list correct, though. + * It might be possible to dig out from the next frame up the name + * of the function that faulted, but that could get hairy. + */ +int +db_nextframe(long **nextframe, long **retaddr, long **arg0, db_addr_t *ip, + long *argp, int is_trap, void (*pr)(const char *, ...)) +{ + struct trapframe *tf; + struct x86_64_frame *fp; + struct intrframe *ifp; + int traptype, trapno, err, i; + + switch (is_trap) { + case NONE: + *ip = (db_addr_t) + db_get_value((long)*retaddr, 8, false); + fp = (struct x86_64_frame *) + db_get_value((long)*nextframe, 8, false); + if (fp == NULL) + return 0; + *nextframe = (long *)&fp->f_frame; + *retaddr = (long *)&fp->f_retaddr; + *arg0 = (long *)&fp->f_arg0; + break; + + case TRAP: + case SYSCALL: + case INTERRUPT: + default: + + /* The only argument to trap() or syscall() is the trapframe. */ + tf = *(struct trapframe **)argp; + switch (is_trap) { + case TRAP: + (*pr)("--- trap (number %d) ---\n", tf->tf_trapno); + break; + case SYSCALL: + (*pr)("--- syscall (number %ld) ---\n", tf->tf_rax); + break; + case INTERRUPT: + (*pr)("--- interrupt ---\n"); + tf = (struct trapframe *)argp; + break; + } + *ip = (db_addr_t)tf->tf_rip; + fp = (struct x86_64_frame *)tf->tf_rbp; + if (fp == NULL) + return 0; + *nextframe = (long *)&fp->f_frame; + *retaddr = (long *)&fp->f_retaddr; + *arg0 = (long *)&fp->f_arg0; + break; + } + + /* + * A bit of a hack. Since %rbp may be used in the stub code, + * walk the stack looking for a valid interrupt frame. Such + * a frame can be recognized by always having + * err 0 or IREENT_MAGIC and trapno T_ASTFLT. + */ + if (db_frame_info(*nextframe, (db_addr_t)*ip, NULL, NULL, &traptype, + NULL) != (db_sym_t)0 + && traptype == INTERRUPT) { + for (i = 0; i < 4; i++) { + ifp = (struct intrframe *)(argp + i); + err = db_get_value((long)&ifp->if_tf.tf_err, + sizeof(long), false); + trapno = db_get_value((long)&ifp->if_tf.tf_trapno, + sizeof(long), false); + if ((err == 0 || err == IREENT_MAGIC) + && trapno == T_ASTFLT) { + *nextframe = (long *)ifp - 1; + break; + } + } + if (i == 4) { + (*pr)("DDB lost frame for "); + db_printsym(*ip, DB_STGY_ANY, pr); + (*pr)(", trying %p\n",argp); + *nextframe = argp; + } + } + return 1; +} + +db_sym_t +db_frame_info(long *frame, db_addr_t callpc, const char **namep, + db_expr_t *offp, int *is_trap, int *nargp) +{ + db_expr_t offset; + db_sym_t sym; + int narg; + const char *name; + + sym = db_search_symbol(callpc, DB_STGY_ANY, &offset); + db_symbol_values(sym, &name, NULL); + if (sym == (db_sym_t)0) + return (db_sym_t)0; + + *is_trap = NONE; + narg = 0; + + if (INKERNEL((long)frame) && name) { + /* + * XXX traps should be based off of the Xtrap* + * locations rather than on trap, since some traps + * (e.g., npxdna) don't go through trap() + */ + if (!strcmp(name, "trap")) { + *is_trap = TRAP; + narg = 0; + } else if (!strcmp(name, "syscall_plain") || + !strcmp(name, "syscall_fancy")) { + *is_trap = SYSCALL; + narg = 0; + } 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; + narg = 0; + } + } + } + + if (offp != NULL) + *offp = offset; + if (nargp != NULL) + *nargp = narg; + if (namep != NULL) + *namep = name; + return sym; +} Index: src/sys/arch/i386/i386/db_machdep.c diff -u /dev/null src/sys/arch/i386/i386/db_machdep.c:1.1 --- /dev/null Sun Apr 10 16:36:50 2011 +++ src/sys/arch/i386/i386/db_machdep.c Sun Apr 10 16:36:48 2011 @@ -0,0 +1,325 @@ +/* $NetBSD: db_machdep.c,v 1.1 2011/04/10 20:36:48 christos Exp $ */ + +/* + * 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.distribut...@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 <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: db_machdep.c,v 1.1 2011/04/10 20:36:48 christos Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> + +#include <machine/frame.h> +#include <machine/trap.h> +#include <machine/intrdefs.h> + +#include <machine/db_machdep.h> +#include <ddb/db_sym.h> +#include <ddb/db_access.h> +#include <ddb/db_variables.h> +#include <ddb/db_output.h> +#include <ddb/db_interface.h> +#include <ddb/db_user.h> +#include <ddb/db_proc.h> +#include <ddb/db_command.h> +#include <ddb/db_cpu.h> +#include <x86/db_machdep.h> + +#define dbreg(xx) (long *)offsetof(db_regs_t, tf_ ## xx) + +const struct db_variable db_regs[] = { + { "ds", dbreg(ds), db_x86_regop, NULL }, + { "es", dbreg(es), db_x86_regop, NULL }, + { "fs", dbreg(fs), db_x86_regop, NULL }, + { "gs", dbreg(gs), db_x86_regop, NULL }, + { "edi", dbreg(edi), db_x86_regop, NULL }, + { "esi", dbreg(esi), db_x86_regop, NULL }, + { "ebp", dbreg(ebp), db_x86_regop, NULL }, + { "ebx", dbreg(ebx), db_x86_regop, NULL }, + { "edx", dbreg(edx), db_x86_regop, NULL }, + { "ecx", dbreg(ecx), db_x86_regop, NULL }, + { "eax", dbreg(eax), db_x86_regop, NULL }, + { "eip", dbreg(eip), db_x86_regop, NULL }, + { "cs", dbreg(cs), db_x86_regop, NULL }, + { "eflags", dbreg(eflags), db_x86_regop, NULL }, + { "esp", dbreg(esp), db_x86_regop, NULL }, + { "ss", dbreg(ss), db_x86_regop, NULL }, +}; +const struct db_variable * const db_eregs = + db_regs + sizeof(db_regs)/sizeof(db_regs[0]); + +/* + * Figure out how many arguments were passed into the frame at "fp". + */ +int +db_numargs(long *retaddrp) +{ + int *argp; + int inst; + int args; + extern char etext[]; + + argp = (int *)db_get_value((int)retaddrp, 4, false); + if (argp < (int *)VM_MIN_KERNEL_ADDRESS || argp > (int *)etext) { + args = 10; + } else { + inst = db_get_value((int)argp, 4, false); + if ((inst & 0xff) == 0x59) /* popl %ecx */ + args = 1; + else if ((inst & 0xffff) == 0xc483) /* addl %n, %esp */ + args = ((inst >> 16) & 0xff) / 4; + else + args = 10; + } + return (args); +} + +db_sym_t +db_frame_info(long *frame, db_addr_t callpc, const char **namep, db_expr_t *offp, + int *is_trap, int *nargp) +{ + db_expr_t offset; + db_sym_t sym; + int narg; + const char *name; + + sym = db_search_symbol(callpc, DB_STGY_ANY, &offset); + db_symbol_values(sym, &name, NULL); + if (sym == (db_sym_t)0) + return (db_sym_t)0; + + *is_trap = NONE; + narg = MAXNARG; + + if (INKERNEL((int)frame) && name) { + /* + * XXX traps should be based off of the Xtrap* + * locations rather than on trap, since some traps + * (e.g., npxdna) don't go through trap() + */ +#ifdef __ELF__ + if (!strcmp(name, "trap_tss")) { + *is_trap = TRAP_TSS; + narg = 0; + } else if (!strcmp(name, "trap")) { + *is_trap = TRAP; + narg = 0; + } else if (!strcmp(name, "syscall_plain") || + !strcmp(name, "syscall_fancy")) { + *is_trap = SYSCALL; + narg = 0; + } 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; + narg = 0; + } else if (!strncmp(name, "Xtss_", 5)) { + *is_trap = INTERRUPT_TSS; + narg = 0; + } + } +#else + if (!strcmp(name, "_trap_tss")) { + *is_trap = TRAP_TSS; + narg = 0; + } else if (!strcmp(name, "_trap")) { + *is_trap = TRAP; + narg = 0; + } else if (!strcmp(name, "_syscall")) { + *is_trap = SYSCALL; + narg = 0; + } 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; + narg = 0; + } else if (!strncmp(name, "_Xtss_", 6)) { + *is_trap = INTERRUPT_TSS; + narg = 0; + } + } +#endif /* __ELF__ */ + } + + if (offp != NULL) + *offp = offset; + if (nargp != NULL) + *nargp = narg; + if (namep != NULL) + *namep = name; + return sym; +} + +/* + * Figure out the next frame up in the call stack. + * For trap(), we print the address of the faulting instruction and + * proceed with the calling frame. We return the ip that faulted. + * If the trap was caused by jumping through a bogus pointer, then + * the next line in the backtrace will list some random function as + * being called. It should get the argument list correct, though. + * It might be possible to dig out from the next frame up the name + * of the function that faulted, but that could get hairy. + */ + +int +db_nextframe( + long **nextframe, /* IN/OUT */ + long **retaddr, /* IN/OUT */ + long **arg0, /* OUT */ + db_addr_t *ip, /* OUT */ + long *argp, /* IN */ + int is_trap, void (*pr)(const char *, ...)) +{ + static struct trapframe tf; + static struct i386tss tss; + struct i386_frame *fp; + struct intrframe *ifp; + int traptype, trapno, err, i; + uintptr_t ptr; + + switch (is_trap) { + case NONE: + *ip = (db_addr_t) + db_get_value((int)*retaddr, 4, false); + fp = (struct i386_frame *) + db_get_value((int)*nextframe, 4, false); + if (fp == NULL) + return 0; + *nextframe = (long *)&fp->f_frame; + *retaddr = (long *)&fp->f_retaddr; + *arg0 = (long *)&fp->f_arg0; + break; + + case TRAP_TSS: + case INTERRUPT_TSS: + ptr = db_get_value((int)argp, 4, false); + db_read_bytes((db_addr_t)ptr, sizeof(tss), (char *)&tss); + *ip = tss.__tss_eip; + fp = (struct i386_frame *)tss.tss_ebp; + if (fp == NULL) + return 0; + *nextframe = (long *)&fp->f_frame; + *retaddr = (long *)&fp->f_retaddr; + *arg0 = (long *)&fp->f_arg0; + if (is_trap == INTERRUPT_TSS) + (*pr)("--- interrupt via task gate ---\n"); + else + (*pr)("--- trap via task gate ---\n"); + break; + + case TRAP: + case SYSCALL: + case INTERRUPT: + default: + /* The only argument to trap() or syscall() is the trapframe. */ + ptr = db_get_value((int)argp, 4, false); + db_read_bytes((db_addr_t)ptr, sizeof(tf), (char *)&tf); + switch (is_trap) { + case TRAP: + (*pr)("--- trap (number %d) ---\n", tf.tf_trapno); + break; + case SYSCALL: + (*pr)("--- syscall (number %d) ---\n", tf.tf_eax); + break; + case INTERRUPT: + (*pr)("--- interrupt ---\n"); + /* + * Get intrframe address as saved when switching + * to interrupt stack, and convert to trapframe + * (add 4). See frame.h. + */ + ptr = db_get_value((int)(argp - 1) + 4, 4, false); + db_read_bytes((db_addr_t)ptr, sizeof(tf), (char *)&tf); + break; + } + *ip = (db_addr_t)tf.tf_eip; + fp = (struct i386_frame *)tf.tf_ebp; + if (fp == NULL) + return 0; + *nextframe = (long *)&fp->f_frame; + *retaddr = (long *)&fp->f_retaddr; + *arg0 = (long *)&fp->f_arg0; + break; + } + + /* + * A bit of a hack. Since %ebp may be used in the stub code, + * walk the stack looking for a valid interrupt frame. Such + * a frame can be recognized by always having + * err 0 or IREENT_MAGIC and trapno T_ASTFLT. + */ + if (db_frame_info(*nextframe, (db_addr_t)*ip, NULL, NULL, &traptype, + NULL) != (db_sym_t)0 + && traptype == INTERRUPT) { + for (i = 0; i < 4; i++) { + ifp = (struct intrframe *)(argp + i); + err = db_get_value((int)&ifp->__if_err, 4, false); + trapno = db_get_value((int)&ifp->__if_trapno, 4, false); + if ((err == 0 || err == IREENT_MAGIC) && trapno == T_ASTFLT) { + *nextframe = (long *)ifp - 1; + break; + } + } + if (i == 4) { + (*pr)("DDB lost frame for "); + db_printsym(*ip, DB_STGY_ANY, pr); + (*pr)(", trying %p\n",argp); + *nextframe = argp; + } + } + return 1; +} + +bool +db_intrstack_p(const void *vp) +{ + struct cpu_info *ci; + const char *cp; + + for (ci = db_cpu_first(); ci != NULL; ci = db_cpu_next(ci)) { + db_read_bytes((db_addr_t)&ci->ci_intrstack, sizeof(cp), + (char *)&cp); + if (cp == NULL) { + continue; + } + if ((cp - INTRSTACKSIZE + 4) <= (const char *)vp && + (const char *)vp <= cp) { + return true; + } + } + return false; +} Index: src/sys/arch/x86/include/db_machdep.h diff -u /dev/null src/sys/arch/x86/include/db_machdep.h:1.1 --- /dev/null Sun Apr 10 16:36:50 2011 +++ src/sys/arch/x86/include/db_machdep.h Sun Apr 10 16:36:48 2011 @@ -0,0 +1,40 @@ +/* $NetBSD: db_machdep.h,v 1.1 2011/04/10 20:36:48 christos Exp $ * + +#ifndef _X86_DB_MACHDEP_H_ +#define _X86_DB_MACHDEP_H_ + +#define INKERNEL(va) (((vaddr_t)(va)) >= VM_MIN_KERNEL_ADDRESS) + +#define NONE 0 +#define TRAP 1 +#define SYSCALL 2 +#define INTERRUPT 3 +#define INTERRUPT_TSS 4 +#define TRAP_TSS 5 + +#define MAXNARG 16 + +struct db_variable; + +#ifdef __x86_64__ +#define tf_sp tf_rsp +#define tf_ip tf_rip +#define tf_bp tf_rbp +#define pcb_bp pcb_rbp +#define x86_frame x86_64_frame +#else +#define tf_sp tf_esp +#define tf_ip tf_eip +#define tf_bp tf_ebp +#define pcb_bp pcb_ebp +#define x86_frame i386_frame +#endif + +int db_x86_regop(const struct db_variable *, db_expr_t *, int); +int db_numargs(long *); +int db_nextframe(long **, long **, long **, db_addr_t *, long *, int, + void (*) (const char *, ...)); +db_sym_t db_frame_info(long *, db_addr_t, const char **, db_expr_t *, + int *, int *); + +#endif /* _X86_DB_MACHDEP_H_ */ Index: src/sys/arch/x86/x86/db_trace.c diff -u /dev/null src/sys/arch/x86/x86/db_trace.c:1.1 --- /dev/null Sun Apr 10 16:36:50 2011 +++ src/sys/arch/x86/x86/db_trace.c Sun Apr 10 16:36:49 2011 @@ -0,0 +1,307 @@ +/* $NetBSD: db_trace.c,v 1.1 2011/04/10 20:36:49 christos Exp $ */ + +/* + * 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.distribut...@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 <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: db_trace.c,v 1.1 2011/04/10 20:36:49 christos Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> + +#include <machine/frame.h> +#include <machine/trap.h> +#include <machine/intrdefs.h> + +#include <machine/db_machdep.h> +#include <ddb/db_sym.h> +#include <ddb/db_access.h> +#include <ddb/db_variables.h> +#include <ddb/db_output.h> +#include <ddb/db_interface.h> +#include <ddb/db_user.h> +#include <ddb/db_proc.h> +#include <ddb/db_command.h> +#include <x86/db_machdep.h> + +int +db_x86_regop(const struct db_variable *vp, db_expr_t *val, int opcode) +{ + db_expr_t *regaddr = + (db_expr_t *)(((uint8_t *)DDB_REGS) + ((size_t)vp->valuep)); + + switch (opcode) { + case DB_VAR_GET: + *val = *regaddr; + break; + case DB_VAR_SET: + *regaddr = *val; + break; + default: + db_printf("db_x86_regop: unknown op %d", opcode); + db_error(NULL); + } + return 0; +} + +/* + * Stack trace. + */ + +#if 0 +db_addr_t db_trap_symbol_value = 0; +db_addr_t db_syscall_symbol_value = 0; +db_addr_t db_kdintr_symbol_value = 0; +bool db_trace_symbols_found = false; + +void db_find_trace_symbols(void); + +void +db_find_trace_symbols(void) +{ + db_expr_t value; + + if (db_value_of_name("_trap", &value)) + db_trap_symbol_value = (db_addr_t) value; + if (db_value_of_name("_kdintr", &value)) + db_kdintr_symbol_value = (db_addr_t) value; + if (db_value_of_name("_syscall", &value)) + db_syscall_symbol_value = (db_addr_t) value; + db_trace_symbols_found = true; +} +#endif + +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; + } + } + +#define set_frame_callpc() do { \ + frame = (long *)ddb_regs.tf_bp; \ + callpc = (db_addr_t)ddb_regs.tf_ip; \ + } while (/*CONSTCCOND*/0) + + if (have_addr && trace_thread) { + struct pcb *pcb; + proc_t p; + lwp_t l; + + if (lwpaddr) { + db_read_bytes(addr, sizeof(l), + (char *)&l); + db_read_bytes((db_addr_t)l.l_proc, + sizeof(p), (char *)&p); + (*pr)("trace: pid %d ", p.p_pid); + } else { + proc_t *pp; + + (*pr)("trace: pid %d ", (int)addr); + if ((pp = db_proc_find((pid_t)addr)) == 0) { + (*pr)("not found\n"); + return; + } + db_read_bytes((db_addr_t)pp, sizeof(p), (char *)&p); + db_read_bytes((db_addr_t)p.p_lwps.lh_first, + sizeof(l), (char *)&l); + } + (*pr)("lid %d ", l.l_lid); + pcb = lwp_getpcb(&l); +#ifdef _KERNEL + if (l.l_proc == curproc && (lwp_t *)lwpaddr == curlwp) + set_frame_callpc(); + else +#endif + { + db_read_bytes((db_addr_t)&pcb->pcb_bp, + sizeof(frame), (char *)&frame); + db_read_bytes((db_addr_t)(frame + 1), + sizeof(callpc), (char *)&callpc); + db_read_bytes((db_addr_t)frame, + sizeof(frame), (char *)&frame); + } + (*pr)("at %p\n", frame); + } else if (have_addr) { + frame = (long *)addr; + db_read_bytes((db_addr_t)(frame + 1), + sizeof(callpc), (char *)&callpc); + db_read_bytes((db_addr_t)frame, + sizeof(frame), (char *)&frame); + } else + set_frame_callpc(); + 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[MAXNARG], **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 && callpc != 0) { + /* Symbol not found, peek at code */ + u_long instr = db_get_value(callpc, 4, false); + + offset = 1; + if ( +#ifdef __x86_64__ + instr == 0xe5894855 || + /* enter: pushq %rbp, movq %rsp, %rbp */ + (instr & 0x00ffffff) == 0x0048e589 + /* enter+1: movq %rsp, %rbp */) +#else + (instr & 0x00ffffff) == 0x00e58955 || + /* enter: pushl %ebp, movl %esp, %ebp */ + (instr & 0x0000ffff) == 0x0000e589 + /* enter+1: movl %esp, %ebp */) +#endif + { + 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 %[er]sp instead + */ + argp = (long *)&((struct x86_frame *) + (ddb_regs.tf_sp-sizeof(long)))->f_arg0; + } else { + argp = frame + 2; + } + + while (narg) { + if (argnp) + (*pr)("%s=", *argnp++); + (*pr)("%lx", db_get_value((long)argp, sizeof(long), + 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_frame *fp = (void *) + (ddb_regs.tf_sp-sizeof(long)); + lastframe = (long *)fp; + callpc = (db_addr_t) + db_get_value((db_addr_t)&fp->f_retaddr, + sizeof(long), 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 */ +#ifdef __i386__ + if (!db_intrstack_p(frame) + && db_intrstack_p(lastframe)) { + (*pr)("--- switch to interrupt stack ---\n"); + } else +#endif + 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"); + } +#undef set_frame_callpc +}