Index: head/sys/ddb/db_break.c =================================================================== --- head/sys/ddb/db_break.c (revision 272957) +++ head/sys/ddb/db_break.c (revision 272958) @@ -1,367 +1,367 @@ /*- * 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: David B. Golub, Carnegie Mellon University * Date: 7/90 */ /* * Breakpoints. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #define NBREAKPOINTS 100 static struct db_breakpoint db_break_table[NBREAKPOINTS]; static db_breakpoint_t db_next_free_breakpoint = &db_break_table[0]; static db_breakpoint_t db_free_breakpoints = 0; static db_breakpoint_t db_breakpoint_list = 0; static db_breakpoint_t db_breakpoint_alloc(void); static void db_breakpoint_free(db_breakpoint_t bkpt); static void db_delete_breakpoint(vm_map_t map, db_addr_t addr); static db_breakpoint_t db_find_breakpoint(vm_map_t map, db_addr_t addr); static void db_list_breakpoints(void); static void db_set_breakpoint(vm_map_t map, db_addr_t addr, int count); static db_breakpoint_t db_breakpoint_alloc() { register db_breakpoint_t bkpt; if ((bkpt = db_free_breakpoints) != 0) { db_free_breakpoints = bkpt->link; return (bkpt); } if (db_next_free_breakpoint == &db_break_table[NBREAKPOINTS]) { db_printf("All breakpoints used.\n"); return (0); } bkpt = db_next_free_breakpoint; db_next_free_breakpoint++; return (bkpt); } static void db_breakpoint_free(bkpt) register db_breakpoint_t bkpt; { bkpt->link = db_free_breakpoints; db_free_breakpoints = bkpt; } static void db_set_breakpoint(map, addr, count) vm_map_t map; db_addr_t addr; int count; { register db_breakpoint_t bkpt; if (db_find_breakpoint(map, addr)) { db_printf("Already set.\n"); return; } bkpt = db_breakpoint_alloc(); if (bkpt == 0) { db_printf("Too many breakpoints.\n"); return; } bkpt->map = map; bkpt->address = addr; bkpt->flags = 0; bkpt->init_count = count; bkpt->count = count; bkpt->link = db_breakpoint_list; db_breakpoint_list = bkpt; } static void db_delete_breakpoint(map, addr) vm_map_t map; db_addr_t addr; { register db_breakpoint_t bkpt; register db_breakpoint_t *prev; for (prev = &db_breakpoint_list; (bkpt = *prev) != 0; prev = &bkpt->link) { if (db_map_equal(bkpt->map, map) && (bkpt->address == addr)) { *prev = bkpt->link; break; } } if (bkpt == 0) { db_printf("Not set.\n"); return; } db_breakpoint_free(bkpt); } static db_breakpoint_t db_find_breakpoint(map, addr) vm_map_t map; db_addr_t addr; { register db_breakpoint_t bkpt; for (bkpt = db_breakpoint_list; bkpt != 0; bkpt = bkpt->link) { if (db_map_equal(bkpt->map, map) && (bkpt->address == addr)) return (bkpt); } return (0); } db_breakpoint_t db_find_breakpoint_here(addr) db_addr_t addr; { - return db_find_breakpoint(db_map_addr(addr), addr); + return db_find_breakpoint(db_map_addr(addr), addr); } static boolean_t db_breakpoints_inserted = TRUE; #ifndef BKPT_WRITE -#define BKPT_WRITE(addr, storage) \ +#define BKPT_WRITE(addr, storage) \ do { \ *storage = db_get_value(addr, BKPT_SIZE, FALSE); \ db_put_value(addr, BKPT_SIZE, BKPT_SET(*storage)); \ } while (0) #endif #ifndef BKPT_CLEAR -#define BKPT_CLEAR(addr, storage) \ +#define BKPT_CLEAR(addr, storage) \ db_put_value(addr, BKPT_SIZE, *storage) #endif void db_set_breakpoints() { register db_breakpoint_t bkpt; if (!db_breakpoints_inserted) { for (bkpt = db_breakpoint_list; bkpt != 0; bkpt = bkpt->link) if (db_map_current(bkpt->map)) { BKPT_WRITE(bkpt->address, &bkpt->bkpt_inst); } db_breakpoints_inserted = TRUE; } } void db_clear_breakpoints() { register db_breakpoint_t bkpt; if (db_breakpoints_inserted) { for (bkpt = db_breakpoint_list; bkpt != 0; bkpt = bkpt->link) if (db_map_current(bkpt->map)) { BKPT_CLEAR(bkpt->address, &bkpt->bkpt_inst); } db_breakpoints_inserted = FALSE; } } #ifdef SOFTWARE_SSTEP /* * Set a temporary breakpoint. * The instruction is changed immediately, * so the breakpoint does not have to be on the breakpoint list. */ db_breakpoint_t db_set_temp_breakpoint(addr) db_addr_t addr; { register db_breakpoint_t bkpt; bkpt = db_breakpoint_alloc(); if (bkpt == 0) { db_printf("Too many breakpoints.\n"); return 0; } bkpt->map = NULL; bkpt->address = addr; bkpt->flags = BKPT_TEMP; bkpt->init_count = 1; bkpt->count = 1; BKPT_WRITE(bkpt->address, &bkpt->bkpt_inst); return bkpt; } void db_delete_temp_breakpoint(bkpt) db_breakpoint_t bkpt; { BKPT_CLEAR(bkpt->address, &bkpt->bkpt_inst); db_breakpoint_free(bkpt); } #endif /* SOFTWARE_SSTEP */ /* * List breakpoints. */ static void db_list_breakpoints() { register db_breakpoint_t bkpt; if (db_breakpoint_list == 0) { db_printf("No breakpoints set\n"); return; } db_printf(" Map Count Address\n"); for (bkpt = db_breakpoint_list; bkpt != 0; bkpt = bkpt->link) { db_printf("%s%8p %5d ", db_map_current(bkpt->map) ? "*" : " ", (void *)bkpt->map, bkpt->init_count); db_printsym(bkpt->address, DB_STGY_PROC); db_printf("\n"); } } /* Delete breakpoint */ /*ARGSUSED*/ void db_delete_cmd(addr, have_addr, count, modif) db_expr_t addr; boolean_t have_addr; db_expr_t count; char * modif; { db_delete_breakpoint(db_map_addr(addr), (db_addr_t)addr); } /* Set breakpoint with skip count */ /*ARGSUSED*/ void db_breakpoint_cmd(addr, have_addr, count, modif) db_expr_t addr; boolean_t have_addr; db_expr_t count; char * modif; { if (count == -1) count = 1; db_set_breakpoint(db_map_addr(addr), (db_addr_t)addr, count); } /* list breakpoints */ void db_listbreak_cmd(dummy1, dummy2, dummy3, dummy4) db_expr_t dummy1; boolean_t dummy2; db_expr_t dummy3; char * dummy4; { db_list_breakpoints(); } /* * We want ddb to be usable before most of the kernel has been * initialized. In particular, current_thread() or kernel_map * (or both) may be null. */ boolean_t db_map_equal(map1, map2) vm_map_t map1, map2; { return ((map1 == map2) || ((map1 == NULL) && (map2 == kernel_map)) || ((map1 == kernel_map) && (map2 == NULL))); } boolean_t db_map_current(map) vm_map_t map; { #if 0 thread_t thread; return ((map == NULL) || (map == kernel_map) || (((thread = current_thread()) != NULL) && (map == thread->task->map))); #else return (1); #endif } vm_map_t db_map_addr(addr) vm_offset_t addr; { #if 0 thread_t thread; /* * We want to return kernel_map for all * non-user addresses, even when debugging * kernel tasks with their own maps. */ if ((VM_MIN_ADDRESS <= addr) && (addr < VM_MAX_ADDRESS) && ((thread = current_thread()) != NULL)) return thread->task->map; else #endif return kernel_map; } Index: head/sys/ddb/db_command.c =================================================================== --- head/sys/ddb/db_command.c (revision 272957) +++ head/sys/ddb/db_command.c (revision 272958) @@ -1,872 +1,872 @@ /*- * 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: David B. Golub, Carnegie Mellon University * Date: 7/90 */ /* * Command dispatcher. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Exported global variables */ boolean_t db_cmd_loop_done; db_addr_t db_dot; db_addr_t db_last_addr; db_addr_t db_prev; db_addr_t db_next; static db_cmdfcn_t db_dump; static db_cmdfcn_t db_fncall; static db_cmdfcn_t db_gdb; static db_cmdfcn_t db_halt; static db_cmdfcn_t db_kill; static db_cmdfcn_t db_reset; static db_cmdfcn_t db_stack_trace; static db_cmdfcn_t db_stack_trace_all; static db_cmdfcn_t db_watchdog; /* * 'show' commands */ static struct command db_show_all_cmds[] = { { "trace", db_stack_trace_all, 0, 0 }, }; struct command_table db_show_all_table = LIST_HEAD_INITIALIZER(db_show_all_table); static struct command db_show_cmds[] = { { "all", 0, 0, &db_show_all_table }, { "registers", db_show_regs, 0, 0 }, { "breaks", db_listbreak_cmd, 0, 0 }, { "threads", db_show_threads, 0, 0 }, }; struct command_table db_show_table = LIST_HEAD_INITIALIZER(db_show_table); static struct command db_cmds[] = { { "print", db_print_cmd, 0, 0 }, { "p", db_print_cmd, 0, 0 }, { "examine", db_examine_cmd, CS_SET_DOT, 0 }, { "x", db_examine_cmd, CS_SET_DOT, 0 }, { "search", db_search_cmd, CS_OWN|CS_SET_DOT, 0 }, { "set", db_set_cmd, CS_OWN, 0 }, { "write", db_write_cmd, CS_MORE|CS_SET_DOT, 0 }, { "w", db_write_cmd, CS_MORE|CS_SET_DOT, 0 }, { "delete", db_delete_cmd, 0, 0 }, { "d", db_delete_cmd, 0, 0 }, { "dump", db_dump, 0, 0 }, { "break", db_breakpoint_cmd, 0, 0 }, { "b", db_breakpoint_cmd, 0, 0 }, { "dwatch", db_deletewatch_cmd, 0, 0 }, { "watch", db_watchpoint_cmd, CS_MORE,0 }, { "dhwatch", db_deletehwatch_cmd, 0, 0 }, { "hwatch", db_hwatchpoint_cmd, 0, 0 }, { "step", db_single_step_cmd, 0, 0 }, { "s", db_single_step_cmd, 0, 0 }, { "continue", db_continue_cmd, 0, 0 }, { "c", db_continue_cmd, 0, 0 }, { "until", db_trace_until_call_cmd,0, 0 }, { "next", db_trace_until_matching_cmd,0, 0 }, { "match", db_trace_until_matching_cmd,0, 0 }, { "trace", db_stack_trace, CS_OWN, 0 }, { "t", db_stack_trace, CS_OWN, 0 }, /* XXX alias for all trace */ { "alltrace", db_stack_trace_all, 0, 0 }, { "where", db_stack_trace, CS_OWN, 0 }, { "bt", db_stack_trace, CS_OWN, 0 }, { "call", db_fncall, CS_OWN, 0 }, { "show", 0, 0, &db_show_table }, { "ps", db_ps, 0, 0 }, { "gdb", db_gdb, 0, 0 }, { "halt", db_halt, 0, 0 }, { "reboot", db_reset, 0, 0 }, { "reset", db_reset, 0, 0 }, { "kill", db_kill, CS_OWN, 0 }, { "watchdog", db_watchdog, CS_OWN, 0 }, { "thread", db_set_thread, CS_OWN, 0 }, { "run", db_run_cmd, CS_OWN, 0 }, { "script", db_script_cmd, CS_OWN, 0 }, { "scripts", db_scripts_cmd, 0, 0 }, { "unscript", db_unscript_cmd, CS_OWN, 0 }, { "capture", db_capture_cmd, CS_OWN, 0 }, { "textdump", db_textdump_cmd, CS_OWN, 0 }, { "findstack", db_findstack_cmd, 0, 0 }, }; struct command_table db_cmd_table = LIST_HEAD_INITIALIZER(db_cmd_table); static struct command *db_last_command = 0; /* * if 'ed' style: 'dot' is set at start of last item printed, * and '+' points to next line. * Otherwise: 'dot' points to next item, '..' points to last. */ static boolean_t db_ed_style = TRUE; /* * Utility routine - discard tokens through end-of-line. */ void db_skip_to_eol() { int t; do { t = db_read_token(); } while (t != tEOL); } /* * Results of command search. */ #define CMD_UNIQUE 0 #define CMD_FOUND 1 #define CMD_NONE 2 #define CMD_AMBIGUOUS 3 #define CMD_HELP 4 static void db_cmd_match(char *name, struct command *cmd, struct command **cmdp, int *resultp); static void db_cmd_list(struct command_table *table); static int db_cmd_search(char *name, struct command_table *table, struct command **cmdp); static void db_command(struct command **last_cmdp, struct command_table *cmd_table, int dopager); /* * Initialize the command lists from the static tables. */ void db_command_init(void) { #define N(a) (sizeof(a) / sizeof(a[0])) int i; for (i = 0; i < N(db_cmds); i++) db_command_register(&db_cmd_table, &db_cmds[i]); for (i = 0; i < N(db_show_cmds); i++) db_command_register(&db_show_table, &db_show_cmds[i]); for (i = 0; i < N(db_show_all_cmds); i++) db_command_register(&db_show_all_table, &db_show_all_cmds[i]); #undef N } /* * Register a command. */ void db_command_register(struct command_table *list, struct command *cmd) { struct command *c, *last; last = NULL; LIST_FOREACH(c, list, next) { int n = strcmp(cmd->name, c->name); /* Check that the command is not already present. */ if (n == 0) { printf("%s: Warning, the command \"%s\" already exists;" " ignoring request\n", __func__, cmd->name); return; } if (n < 0) { /* NB: keep list sorted lexicographically */ LIST_INSERT_BEFORE(c, cmd, next); return; } last = c; } if (last == NULL) LIST_INSERT_HEAD(list, cmd, next); else LIST_INSERT_AFTER(last, cmd, next); } /* * Remove a command previously registered with db_command_register. */ void db_command_unregister(struct command_table *list, struct command *cmd) { struct command *c; LIST_FOREACH(c, list, next) { if (cmd == c) { LIST_REMOVE(cmd, next); return; } } /* NB: intentionally quiet */ } /* * Helper function to match a single command. */ static void db_cmd_match(name, cmd, cmdp, resultp) char * name; struct command *cmd; struct command **cmdp; /* out */ int * resultp; { char *lp, *rp; int c; lp = name; rp = cmd->name; while ((c = *lp) == *rp) { if (c == 0) { /* complete match */ *cmdp = cmd; *resultp = CMD_UNIQUE; return; } lp++; rp++; } if (c == 0) { /* end of name, not end of command - partial match */ if (*resultp == CMD_FOUND) { *resultp = CMD_AMBIGUOUS; /* but keep looking for a full match - this lets us match single letters */ } else { *cmdp = cmd; *resultp = CMD_FOUND; } } } /* * Search for command prefix. */ static int db_cmd_search(name, table, cmdp) char * name; struct command_table *table; struct command **cmdp; /* out */ { struct command *cmd; int result = CMD_NONE; LIST_FOREACH(cmd, table, next) { db_cmd_match(name,cmd,cmdp,&result); if (result == CMD_UNIQUE) break; } if (result == CMD_NONE) { /* check for 'help' */ if (name[0] == 'h' && name[1] == 'e' && name[2] == 'l' && name[3] == 'p') result = CMD_HELP; } return (result); } static void db_cmd_list(table) struct command_table *table; { register struct command *cmd; LIST_FOREACH(cmd, table, next) { db_printf("%-16s", cmd->name); db_end_line(16); } } static void db_command(last_cmdp, cmd_table, dopager) struct command **last_cmdp; /* IN_OUT */ struct command_table *cmd_table; int dopager; { struct command *cmd = NULL; int t; char modif[TOK_STRING_SIZE]; db_expr_t addr, count; boolean_t have_addr = FALSE; int result; t = db_read_token(); if (t == tEOL) { /* empty line repeats last command, at 'next' */ cmd = *last_cmdp; addr = (db_expr_t)db_next; have_addr = FALSE; count = 1; modif[0] = '\0'; } else if (t == tEXCL) { db_fncall((db_expr_t)0, (boolean_t)0, (db_expr_t)0, (char *)0); return; } else if (t != tIDENT) { db_printf("?\n"); db_flush_lex(); return; } else { /* * Search for command */ while (cmd_table) { result = db_cmd_search(db_tok_string, cmd_table, &cmd); switch (result) { case CMD_NONE: db_printf("No such command\n"); db_flush_lex(); return; case CMD_AMBIGUOUS: db_printf("Ambiguous\n"); db_flush_lex(); return; case CMD_HELP: db_cmd_list(cmd_table); db_flush_lex(); return; default: break; } if ((cmd_table = cmd->more) != NULL) { t = db_read_token(); if (t != tIDENT) { db_cmd_list(cmd_table); db_flush_lex(); return; } } } if ((cmd->flag & CS_OWN) == 0) { /* * Standard syntax: * command [/modifier] [addr] [,count] */ t = db_read_token(); if (t == tSLASH) { t = db_read_token(); if (t != tIDENT) { db_printf("Bad modifier\n"); db_flush_lex(); return; } db_strcpy(modif, db_tok_string); } else { db_unread_token(t); modif[0] = '\0'; } if (db_expression(&addr)) { db_dot = (db_addr_t) addr; db_last_addr = db_dot; have_addr = TRUE; } else { addr = (db_expr_t) db_dot; have_addr = FALSE; } t = db_read_token(); if (t == tCOMMA) { if (!db_expression(&count)) { db_printf("Count missing\n"); db_flush_lex(); return; } } else { db_unread_token(t); count = -1; } if ((cmd->flag & CS_MORE) == 0) { db_skip_to_eol(); } } } *last_cmdp = cmd; if (cmd != 0) { /* * Execute the command. */ if (dopager) db_enable_pager(); else db_disable_pager(); (*cmd->fcn)(addr, have_addr, count, modif); if (dopager) db_disable_pager(); if (cmd->flag & CS_SET_DOT) { /* * If command changes dot, set dot to * previous address displayed (if 'ed' style). */ if (db_ed_style) { db_dot = db_prev; } else { db_dot = db_next; } } else { /* * If command does not change dot, * set 'next' location to be the same. */ db_next = db_dot; } } } /* * At least one non-optional command must be implemented using * DB_COMMAND() so that db_cmd_set gets created. Here is one. */ DB_COMMAND(panic, db_panic) { db_disable_pager(); panic("from debugger"); } void db_command_loop() { /* * Initialize 'prev' and 'next' to dot. */ db_prev = db_dot; db_next = db_dot; db_cmd_loop_done = 0; while (!db_cmd_loop_done) { if (db_print_position() != 0) db_printf("\n"); db_printf("db> "); (void) db_read_line(); db_command(&db_last_command, &db_cmd_table, /* dopager */ 1); } } /* * Execute a command on behalf of a script. The caller is responsible for * making sure that the command string is < DB_MAXLINE or it will be * truncated. * * XXXRW: Runs by injecting faked input into DDB input stream; it would be * nicer to use an alternative approach that didn't mess with the previous * command buffer. */ void db_command_script(const char *command) { db_prev = db_next = db_dot; db_inject_line(command); db_command(&db_last_command, &db_cmd_table, /* dopager */ 0); } void db_error(s) const char *s; { if (s) db_printf("%s", s); db_flush_lex(); kdb_reenter(); } static void db_dump(db_expr_t dummy, boolean_t dummy2, db_expr_t dummy3, char *dummy4) { int error; if (textdump_pending) { db_printf("textdump_pending set.\n" "run \"textdump unset\" first or \"textdump dump\" for a textdump.\n"); return; } error = doadump(FALSE); if (error) { db_printf("Cannot dump: "); switch (error) { case EBUSY: db_printf("debugger got invoked while dumping.\n"); break; case ENXIO: db_printf("no dump device specified.\n"); break; default: db_printf("unknown error (error=%d).\n", error); break; } } } /* * Call random function: * !expr(arg,arg,arg) */ /* The generic implementation supports a maximum of 10 arguments. */ typedef db_expr_t __db_f(db_expr_t, db_expr_t, db_expr_t, db_expr_t, db_expr_t, db_expr_t, db_expr_t, db_expr_t, db_expr_t, db_expr_t); static __inline int db_fncall_generic(db_expr_t addr, db_expr_t *rv, int nargs, db_expr_t args[]) { __db_f *f = (__db_f *)addr; if (nargs > 10) { db_printf("Too many arguments (max 10)\n"); return (0); } *rv = (*f)(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]); return (1); } static void db_fncall(dummy1, dummy2, dummy3, dummy4) db_expr_t dummy1; boolean_t dummy2; db_expr_t dummy3; char * dummy4; { db_expr_t fn_addr; db_expr_t args[DB_MAXARGS]; int nargs = 0; db_expr_t retval; int t; if (!db_expression(&fn_addr)) { db_printf("Bad function\n"); db_flush_lex(); return; } t = db_read_token(); if (t == tLPAREN) { if (db_expression(&args[0])) { nargs++; while ((t = db_read_token()) == tCOMMA) { if (nargs == DB_MAXARGS) { db_printf("Too many arguments (max %d)\n", DB_MAXARGS); db_flush_lex(); return; } if (!db_expression(&args[nargs])) { db_printf("Argument missing\n"); db_flush_lex(); return; } nargs++; } db_unread_token(t); } if (db_read_token() != tRPAREN) { db_printf("?\n"); db_flush_lex(); return; } } db_skip_to_eol(); db_disable_pager(); if (DB_CALL(fn_addr, &retval, nargs, args)) db_printf("= %#lr\n", (long)retval); } static void db_halt(db_expr_t dummy, boolean_t dummy2, db_expr_t dummy3, char *dummy4) { cpu_halt(); } static void db_kill(dummy1, dummy2, dummy3, dummy4) db_expr_t dummy1; boolean_t dummy2; db_expr_t dummy3; char * dummy4; { db_expr_t old_radix, pid, sig; struct proc *p; -#define DB_ERROR(f) do { db_printf f; db_flush_lex(); goto out; } while (0) +#define DB_ERROR(f) do { db_printf f; db_flush_lex(); goto out; } while (0) /* * PIDs and signal numbers are typically represented in base * 10, so make that the default here. It can, of course, be * overridden by specifying a prefix. */ old_radix = db_radix; db_radix = 10; /* Retrieve arguments. */ if (!db_expression(&sig)) DB_ERROR(("Missing signal number\n")); if (!db_expression(&pid)) DB_ERROR(("Missing process ID\n")); db_skip_to_eol(); if (!_SIG_VALID(sig)) DB_ERROR(("Signal number out of range\n")); /* * Find the process in question. allproc_lock is not needed * since we're in DDB. */ /* sx_slock(&allproc_lock); */ FOREACH_PROC_IN_SYSTEM(p) if (p->p_pid == pid) break; /* sx_sunlock(&allproc_lock); */ if (p == NULL) DB_ERROR(("Can't find process with pid %ld\n", (long) pid)); /* If it's already locked, bail; otherwise, do the deed. */ if (PROC_TRYLOCK(p) == 0) DB_ERROR(("Can't lock process with pid %ld\n", (long) pid)); else { pksignal(p, sig, NULL); PROC_UNLOCK(p); } out: db_radix = old_radix; #undef DB_ERROR } /* * Reboot. In case there is an additional argument, take it as delay in * seconds. Default to 15s if we cannot parse it and make sure we will * never wait longer than 1 week. Some code is similar to * kern_shutdown.c:shutdown_panic(). */ #ifndef DB_RESET_MAXDELAY #define DB_RESET_MAXDELAY (3600 * 24 * 7) #endif static void db_reset(db_expr_t addr, boolean_t have_addr, db_expr_t count __unused, char *modif __unused) { int delay, loop; if (have_addr) { delay = (int)db_hex2dec(addr); /* If we parse to fail, use 15s. */ if (delay == -1) delay = 15; /* Cap at one week. */ if ((uintmax_t)delay > (uintmax_t)DB_RESET_MAXDELAY) delay = DB_RESET_MAXDELAY; db_printf("Automatic reboot in %d seconds - " "press a key on the console to abort\n", delay); for (loop = delay * 10; loop > 0; --loop) { DELAY(1000 * 100); /* 1/10th second */ /* Did user type a key? */ if (cncheckc() != -1) return; } } cpu_reset(); } static void db_watchdog(dummy1, dummy2, dummy3, dummy4) db_expr_t dummy1; boolean_t dummy2; db_expr_t dummy3; char * dummy4; { db_expr_t old_radix, tout; int err, i; old_radix = db_radix; db_radix = 10; err = db_expression(&tout); db_skip_to_eol(); db_radix = old_radix; /* If no argument is provided the watchdog will just be disabled. */ if (err == 0) { db_printf("No argument provided, disabling watchdog\n"); tout = 0; } else if ((tout & WD_INTERVAL) == WD_TO_NEVER) { db_error("Out of range watchdog interval\n"); return; } EVENTHANDLER_INVOKE(watchdog_list, tout, &i); } static void db_gdb(db_expr_t dummy1, boolean_t dummy2, db_expr_t dummy3, char *dummy4) { if (kdb_dbbe_select("gdb") != 0) { db_printf("The remote GDB backend could not be selected.\n"); return; } /* * Mark that we are done in the debugger. kdb_trap() * should re-enter with the new backend. */ db_cmd_loop_done = 1; db_printf("(ctrl-c will return control to ddb)\n"); } static void db_stack_trace(db_expr_t tid, boolean_t hastid, db_expr_t count, char *modif) { struct thread *td; db_expr_t radix; pid_t pid; int t; /* * We parse our own arguments. We don't like the default radix. */ radix = db_radix; db_radix = 10; hastid = db_expression(&tid); t = db_read_token(); if (t == tCOMMA) { if (!db_expression(&count)) { db_printf("Count missing\n"); db_flush_lex(); return; } } else { db_unread_token(t); count = -1; } db_skip_to_eol(); db_radix = radix; if (hastid) { td = kdb_thr_lookup((lwpid_t)tid); if (td == NULL) td = kdb_thr_from_pid((pid_t)tid); if (td == NULL) { db_printf("Thread %d not found\n", (int)tid); return; } } else td = kdb_thread; if (td->td_proc != NULL) pid = td->td_proc->p_pid; else pid = -1; db_printf("Tracing pid %d tid %ld td %p\n", pid, (long)td->td_tid, td); db_trace_thread(td, count); } static void db_stack_trace_all(db_expr_t dummy, boolean_t dummy2, db_expr_t dummy3, char *dummy4) { struct proc *p; struct thread *td; jmp_buf jb; void *prev_jb; FOREACH_PROC_IN_SYSTEM(p) { prev_jb = kdb_jmpbuf(jb); if (setjmp(jb) == 0) { FOREACH_THREAD_IN_PROC(p, td) { db_printf("\nTracing command %s pid %d tid %ld td %p\n", p->p_comm, p->p_pid, (long)td->td_tid, td); db_trace_thread(td, -1); if (db_pager_quit) { kdb_jmpbuf(prev_jb); return; } } } kdb_jmpbuf(prev_jb); } } /* * Take the parsed expression value from the command line that was parsed * as a hexadecimal value and convert it as if the expression was parsed * as a decimal value. Returns -1 if the expression was not a valid * decimal value. */ db_expr_t db_hex2dec(db_expr_t expr) { uintptr_t x, y; db_expr_t val; y = 1; val = 0; x = expr; while (x != 0) { if (x % 16 > 9) return (-1); val += (x % 16) * (y); x >>= 4; y *= 10; } return (val); } Index: head/sys/ddb/db_output.h =================================================================== --- head/sys/ddb/db_output.h (revision 272957) +++ head/sys/ddb/db_output.h (revision 272958) @@ -1,47 +1,47 @@ /*- * 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. * * $FreeBSD$ */ #ifndef _DDB_DB_OUTPUT_H_ #define _DDB_DB_OUTPUT_H_ /* - * Author: David B. Golub, Carnegie Mellon University + * Author: David B. Golub, Carnegie Mellon University * Date: 8/90 */ /* * Printing routines for kernel debugger. */ void db_disable_pager(void); void db_enable_pager(void); void db_end_line(int); void db_force_whitespace(void); int db_print_position(void); #endif /* !_DDB_DB_OUTPUT_H_ */ Index: head/sys/ddb/db_print.c =================================================================== --- head/sys/ddb/db_print.c (revision 272957) +++ head/sys/ddb/db_print.c (revision 272958) @@ -1,70 +1,70 @@ /*- * 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: David B. Golub, Carnegie Mellon University + * Author: David B. Golub, Carnegie Mellon University * Date: 7/90 */ /* * Miscellaneous printing. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include void db_show_regs(db_expr_t _1, boolean_t _2, db_expr_t _3, char *_4) { struct db_variable *regp; db_expr_t value, offset; const char *name; for (regp = db_regs; regp < db_eregs; regp++) { if (!db_read_variable(regp, &value)) continue; db_printf("%-12s%#10lr", regp->name, (unsigned long)value); db_find_xtrn_sym_and_offset((db_addr_t)value, &name, &offset); if (name != NULL && offset <= (unsigned long)db_maxoff && offset != value) { db_printf("\t%s", name); if (offset != 0) db_printf("+%+#lr", (long)offset); } db_printf("\n"); } db_print_loc_and_inst(PC_REGS()); } Index: head/sys/ddb/db_ps.c =================================================================== --- head/sys/ddb/db_ps.c (revision 272957) +++ head/sys/ddb/db_ps.c (revision 272958) @@ -1,468 +1,468 @@ /*- * Copyright (c) 1993 The Regents of the University of California. * 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. * 4. 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 REGENTS 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 REGENTS 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 #include #include #include static void dumpthread(volatile struct proc *p, volatile struct thread *td, int all); /* * At least one non-optional show-command must be implemented using * DB_SHOW_ALL_COMMAND() so that db_show_all_cmd_set gets created. * Here is one. */ DB_SHOW_ALL_COMMAND(procs, db_procs_cmd) { db_ps(addr, have_addr, count, modif); } /* * Layout: * - column counts * - header * - single-threaded process * - multi-threaded process * - thread in a MT process * * 1 2 3 4 5 6 7 * 1234567890123456789012345678901234567890123456789012345678901234567890 * pid ppid pgrp uid state wmesg wchan cmd * < wmesg > < wchan > * (threaded) * < wmesg > < wchan > * * For machines with 64-bit pointers, we expand the wchan field 8 more * characters. */ void db_ps(db_expr_t addr, boolean_t hasaddr, db_expr_t count, char *modif) { volatile struct proc *p, *pp; volatile struct thread *td; struct ucred *cred; struct pgrp *pgrp; char state[9]; int np, rflag, sflag, dflag, lflag, wflag; np = nprocs; if (!LIST_EMPTY(&allproc)) p = LIST_FIRST(&allproc); else p = &proc0; #ifdef __LP64__ db_printf(" pid ppid pgrp uid state wmesg wchan cmd\n"); #else db_printf(" pid ppid pgrp uid state wmesg wchan cmd\n"); #endif while (--np >= 0 && !db_pager_quit) { if (p == NULL) { db_printf("oops, ran out of processes early!\n"); break; } pp = p->p_pptr; if (pp == NULL) pp = p; cred = p->p_ucred; pgrp = p->p_pgrp; db_printf("%5d %5d %5d %5d ", p->p_pid, pp->p_pid, pgrp != NULL ? pgrp->pg_id : 0, cred != NULL ? cred->cr_ruid : 0); /* Determine our primary process state. */ switch (p->p_state) { case PRS_NORMAL: if (P_SHOULDSTOP(p)) state[0] = 'T'; else { /* * One of D, L, R, S, W. For a * multithreaded process we will use * the state of the thread with the * highest precedence. The * precendence order from high to low * is R, L, D, S, W. If no thread is * in a sane state we use '?' for our * primary state. */ rflag = sflag = dflag = lflag = wflag = 0; FOREACH_THREAD_IN_PROC(p, td) { if (td->td_state == TDS_RUNNING || td->td_state == TDS_RUNQ || td->td_state == TDS_CAN_RUN) rflag++; if (TD_ON_LOCK(td)) lflag++; if (TD_IS_SLEEPING(td)) { if (!(td->td_flags & TDF_SINTR)) dflag++; else sflag++; } if (TD_AWAITING_INTR(td)) wflag++; } if (rflag) state[0] = 'R'; else if (lflag) state[0] = 'L'; else if (dflag) state[0] = 'D'; else if (sflag) state[0] = 'S'; else if (wflag) state[0] = 'W'; else - state[0] = '?'; + state[0] = '?'; } break; case PRS_NEW: state[0] = 'N'; break; case PRS_ZOMBIE: state[0] = 'Z'; break; default: state[0] = 'U'; break; } state[1] = '\0'; /* Additional process state flags. */ if (!(p->p_flag & P_INMEM)) strlcat(state, "W", sizeof(state)); if (p->p_flag & P_TRACED) strlcat(state, "X", sizeof(state)); if (p->p_flag & P_WEXIT && p->p_state != PRS_ZOMBIE) strlcat(state, "E", sizeof(state)); if (p->p_flag & P_PPWAIT) strlcat(state, "V", sizeof(state)); if (p->p_flag & P_SYSTEM || p->p_lock > 0) strlcat(state, "L", sizeof(state)); if (p->p_session != NULL && SESS_LEADER(p)) strlcat(state, "s", sizeof(state)); /* Cheated here and didn't compare pgid's. */ if (p->p_flag & P_CONTROLT) strlcat(state, "+", sizeof(state)); if (cred != NULL && jailed(cred)) strlcat(state, "J", sizeof(state)); db_printf(" %-6.6s ", state); if (p->p_flag & P_HADTHREADS) { #ifdef __LP64__ db_printf(" (threaded) "); #else db_printf(" (threaded) "); #endif if (p->p_flag & P_SYSTEM) db_printf("["); db_printf("%s", p->p_comm); if (p->p_flag & P_SYSTEM) db_printf("]"); db_printf("\n"); } FOREACH_THREAD_IN_PROC(p, td) { dumpthread(p, td, p->p_flag & P_HADTHREADS); if (db_pager_quit) break; } p = LIST_NEXT(p, p_list); if (p == NULL && np > 0) p = LIST_FIRST(&zombproc); - } + } } static void dumpthread(volatile struct proc *p, volatile struct thread *td, int all) { char state[9], wprefix; const char *wmesg; void *wchan; if (all) { db_printf("%6d ", td->td_tid); switch (td->td_state) { case TDS_RUNNING: snprintf(state, sizeof(state), "Run"); break; case TDS_RUNQ: snprintf(state, sizeof(state), "RunQ"); break; case TDS_CAN_RUN: snprintf(state, sizeof(state), "CanRun"); break; case TDS_INACTIVE: snprintf(state, sizeof(state), "Inactv"); break; case TDS_INHIBITED: state[0] = '\0'; if (TD_ON_LOCK(td)) strlcat(state, "L", sizeof(state)); if (TD_IS_SLEEPING(td)) { if (td->td_flags & TDF_SINTR) strlcat(state, "S", sizeof(state)); else strlcat(state, "D", sizeof(state)); } if (TD_IS_SWAPPED(td)) strlcat(state, "W", sizeof(state)); if (TD_AWAITING_INTR(td)) strlcat(state, "I", sizeof(state)); if (TD_IS_SUSPENDED(td)) strlcat(state, "s", sizeof(state)); if (state[0] != '\0') break; default: snprintf(state, sizeof(state), "???"); } db_printf(" %-6.6s ", state); } wprefix = ' '; if (TD_ON_LOCK(td)) { wprefix = '*'; wmesg = td->td_lockname; wchan = td->td_blocked; } else if (TD_ON_SLEEPQ(td)) { wmesg = td->td_wmesg; wchan = td->td_wchan; } else if (TD_IS_RUNNING(td)) { snprintf(state, sizeof(state), "CPU %d", td->td_oncpu); wmesg = state; wchan = NULL; } else { wmesg = ""; wchan = NULL; } db_printf("%c%-8.8s ", wprefix, wmesg); if (wchan == NULL) #ifdef __LP64__ db_printf("%18s ", ""); #else db_printf("%10s ", ""); #endif else db_printf("%p ", wchan); if (p->p_flag & P_SYSTEM) db_printf("["); if (td->td_name[0] != '\0') db_printf("%s", td->td_name); else db_printf("%s", td->td_proc->p_comm); if (p->p_flag & P_SYSTEM) db_printf("]"); db_printf("\n"); } DB_SHOW_COMMAND(thread, db_show_thread) { struct thread *td; struct lock_object *lock; boolean_t comma; /* Determine which thread to examine. */ if (have_addr) td = db_lookup_thread(addr, FALSE); else td = kdb_thread; lock = (struct lock_object *)td->td_lock; db_printf("Thread %d at %p:\n", td->td_tid, td); db_printf(" proc (pid %d): %p\n", td->td_proc->p_pid, td->td_proc); if (td->td_name[0] != '\0') db_printf(" name: %s\n", td->td_name); db_printf(" stack: %p-%p\n", (void *)td->td_kstack, (void *)(td->td_kstack + td->td_kstack_pages * PAGE_SIZE - 1)); db_printf(" flags: %#x ", td->td_flags); db_printf(" pflags: %#x\n", td->td_pflags); db_printf(" state: "); switch (td->td_state) { case TDS_INACTIVE: db_printf("INACTIVE\n"); break; case TDS_CAN_RUN: db_printf("CAN RUN\n"); break; case TDS_RUNQ: db_printf("RUNQ\n"); break; case TDS_RUNNING: db_printf("RUNNING (CPU %d)\n", td->td_oncpu); break; case TDS_INHIBITED: db_printf("INHIBITED: {"); comma = FALSE; if (TD_IS_SLEEPING(td)) { db_printf("SLEEPING"); comma = TRUE; } if (TD_IS_SUSPENDED(td)) { if (comma) db_printf(", "); db_printf("SUSPENDED"); comma = TRUE; } if (TD_IS_SWAPPED(td)) { if (comma) db_printf(", "); db_printf("SWAPPED"); comma = TRUE; } if (TD_ON_LOCK(td)) { if (comma) db_printf(", "); db_printf("LOCK"); comma = TRUE; } if (TD_AWAITING_INTR(td)) { if (comma) db_printf(", "); db_printf("IWAIT"); } db_printf("}\n"); break; default: db_printf("??? (%#x)\n", td->td_state); break; } if (TD_ON_LOCK(td)) db_printf(" lock: %s turnstile: %p\n", td->td_lockname, td->td_blocked); if (TD_ON_SLEEPQ(td)) db_printf(" wmesg: %s wchan: %p\n", td->td_wmesg, td->td_wchan); db_printf(" priority: %d\n", td->td_priority); db_printf(" container lock: %s (%p)\n", lock->lo_name, lock); } DB_SHOW_COMMAND(proc, db_show_proc) { struct thread *td; struct proc *p; int i; /* Determine which process to examine. */ if (have_addr) p = db_lookup_proc(addr); else p = kdb_thread->td_proc; db_printf("Process %d (%s) at %p:\n", p->p_pid, p->p_comm, p); db_printf(" state: "); switch (p->p_state) { case PRS_NEW: db_printf("NEW\n"); break; case PRS_NORMAL: db_printf("NORMAL\n"); break; case PRS_ZOMBIE: db_printf("ZOMBIE\n"); break; default: db_printf("??? (%#x)\n", p->p_state); } if (p->p_ucred != NULL) { db_printf(" uid: %d gids: ", p->p_ucred->cr_uid); for (i = 0; i < p->p_ucred->cr_ngroups; i++) { db_printf("%d", p->p_ucred->cr_groups[i]); if (i < (p->p_ucred->cr_ngroups - 1)) db_printf(", "); } db_printf("\n"); } if (p->p_pptr != NULL) db_printf(" parent: pid %d at %p\n", p->p_pptr->p_pid, p->p_pptr); if (p->p_leader != NULL && p->p_leader != p) db_printf(" leader: pid %d at %p\n", p->p_leader->p_pid, p->p_leader); if (p->p_sysent != NULL) db_printf(" ABI: %s\n", p->p_sysent->sv_name); if (p->p_args != NULL) db_printf(" arguments: %.*s\n", (int)p->p_args->ar_length, p->p_args->ar_args); db_printf(" threads: %d\n", p->p_numthreads); FOREACH_THREAD_IN_PROC(p, td) { dumpthread(p, td, 1); if (db_pager_quit) break; } } void db_findstack_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t dummy3 __unused, char *dummy4 __unused) { struct proc *p; struct thread *td; struct kstack_cache_entry *ks_ce; vm_offset_t saddr; if (have_addr) saddr = addr; else { db_printf("Usage: findstack
\n"); return; } FOREACH_PROC_IN_SYSTEM(p) { FOREACH_THREAD_IN_PROC(p, td) { if (td->td_kstack <= saddr && saddr < td->td_kstack + PAGE_SIZE * td->td_kstack_pages) { db_printf("Thread %p\n", td); return; } } } for (ks_ce = kstack_cache; ks_ce != NULL; ks_ce = ks_ce->next_ks_entry) { if ((vm_offset_t)ks_ce <= saddr && saddr < (vm_offset_t)ks_ce + PAGE_SIZE * KSTACK_PAGES) { db_printf("Cached stack %p\n", ks_ce); return; } } } Index: head/sys/ddb/db_run.c =================================================================== --- head/sys/ddb/db_run.c (revision 272957) +++ head/sys/ddb/db_run.c (revision 272958) @@ -1,392 +1,392 @@ /*- * 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: David B. Golub, Carnegie Mellon University * Date: 7/90 */ /* * Commands to run process. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include static int db_run_mode; #define STEP_NONE 0 #define STEP_ONCE 1 #define STEP_RETURN 2 #define STEP_CALLT 3 #define STEP_CONTINUE 4 -#define STEP_INVISIBLE 5 +#define STEP_INVISIBLE 5 #define STEP_COUNT 6 static boolean_t db_sstep_print; static int db_loop_count; static int db_call_depth; int db_inst_count; int db_load_count; int db_store_count; #ifndef db_set_single_step void db_set_single_step(void); #endif #ifndef db_clear_single_step void db_clear_single_step(void); #endif #ifdef SOFTWARE_SSTEP db_breakpoint_t db_not_taken_bkpt = 0; db_breakpoint_t db_taken_bkpt = 0; #endif boolean_t db_stop_at_pc(is_breakpoint) boolean_t *is_breakpoint; { register db_addr_t pc; register db_breakpoint_t bkpt; pc = PC_REGS(); #ifdef SOFTWARE_SSTEP if ((db_not_taken_bkpt != 0 && pc == db_not_taken_bkpt->address) || (db_taken_bkpt != 0 && pc == db_taken_bkpt->address)) *is_breakpoint = FALSE; #endif db_clear_single_step(); db_clear_breakpoints(); db_clear_watchpoints(); #ifdef FIXUP_PC_AFTER_BREAK if (*is_breakpoint) { /* * Breakpoint trap. Fix up the PC if the * machine requires it. */ FIXUP_PC_AFTER_BREAK pc = PC_REGS(); } #endif /* * Now check for a breakpoint at this address. */ bkpt = db_find_breakpoint_here(pc); if (bkpt) { if (--bkpt->count == 0) { bkpt->count = bkpt->init_count; *is_breakpoint = TRUE; return (TRUE); /* stop here */ } } else if (*is_breakpoint) { #ifdef BKPT_SKIP BKPT_SKIP; #endif } *is_breakpoint = FALSE; if (db_run_mode == STEP_INVISIBLE) { db_run_mode = STEP_CONTINUE; return (FALSE); /* continue */ } if (db_run_mode == STEP_COUNT) { return (FALSE); /* continue */ } if (db_run_mode == STEP_ONCE) { if (--db_loop_count > 0) { if (db_sstep_print) { db_printf("\t\t"); db_print_loc_and_inst(pc); db_printf("\n"); } return (FALSE); /* continue */ } } if (db_run_mode == STEP_RETURN) { /* continue until matching return */ db_expr_t ins; ins = db_get_value(pc, sizeof(int), FALSE); if (!inst_trap_return(ins) && (!inst_return(ins) || --db_call_depth != 0)) { if (db_sstep_print) { if (inst_call(ins) || inst_return(ins)) { register int i; db_printf("[after %6d] ", db_inst_count); for (i = db_call_depth; --i > 0; ) db_printf(" "); db_print_loc_and_inst(pc); db_printf("\n"); } } if (inst_call(ins)) db_call_depth++; return (FALSE); /* continue */ } } if (db_run_mode == STEP_CALLT) { /* continue until call or return */ db_expr_t ins; ins = db_get_value(pc, sizeof(int), FALSE); if (!inst_call(ins) && !inst_return(ins) && !inst_trap_return(ins)) { return (FALSE); /* continue */ } } db_run_mode = STEP_NONE; return (TRUE); } void db_restart_at_pc(watchpt) boolean_t watchpt; { register db_addr_t pc = PC_REGS(); if ((db_run_mode == STEP_COUNT) || (db_run_mode == STEP_RETURN) || (db_run_mode == STEP_CALLT)) { /* * We are about to execute this instruction, * so count it now. */ #ifdef SOFTWARE_SSTEP db_expr_t ins = #endif db_get_value(pc, sizeof(int), FALSE); db_inst_count++; db_load_count += inst_load(ins); db_store_count += inst_store(ins); #ifdef SOFTWARE_SSTEP /* XXX works on mips, but... */ if (inst_branch(ins) || inst_call(ins)) { ins = db_get_value(next_instr_address(pc,1), sizeof(int), FALSE); db_inst_count++; db_load_count += inst_load(ins); db_store_count += inst_store(ins); } #endif /* SOFTWARE_SSTEP */ } if (db_run_mode == STEP_CONTINUE) { if (watchpt || db_find_breakpoint_here(pc)) { /* * Step over breakpoint/watchpoint. */ db_run_mode = STEP_INVISIBLE; db_set_single_step(); } else { db_set_breakpoints(); db_set_watchpoints(); } } else { db_set_single_step(); } } #ifdef SOFTWARE_SSTEP /* * Software implementation of single-stepping. * If your machine does not have a trace mode * similar to the vax or sun ones you can use * this implementation, done for the mips. * Just define the above conditional and provide * the functions/macros defined below. * * extern boolean_t * inst_branch(), returns true if the instruction might branch * extern unsigned * branch_taken(), return the address the instruction might * branch to * db_getreg_val(); return the value of a user register, * as indicated in the hardware instruction * encoding, e.g. 8 for r8 * * next_instr_address(pc,bd) returns the address of the first * instruction following the one at "pc", * which is either in the taken path of * the branch (bd==1) or not. This is * for machines (mips) with branch delays. * * A single-step may involve at most 2 breakpoints - * one for branch-not-taken and one for branch taken. * If one of these addresses does not already have a breakpoint, * we allocate a breakpoint and save it here. * These breakpoints are deleted on return. */ void db_set_single_step(void) { db_addr_t pc = PC_REGS(), brpc; unsigned inst; /* * User was stopped at pc, e.g. the instruction * at pc was not executed. */ inst = db_get_value(pc, sizeof(int), FALSE); if (inst_branch(inst) || inst_call(inst) || inst_return(inst)) { brpc = branch_taken(inst, pc); if (brpc != pc) { /* self-branches are hopeless */ db_taken_bkpt = db_set_temp_breakpoint(brpc); } pc = next_instr_address(pc, 1); } pc = next_instr_address(pc, 0); db_not_taken_bkpt = db_set_temp_breakpoint(pc); } void db_clear_single_step(void) { if (db_not_taken_bkpt != 0) { db_delete_temp_breakpoint(db_not_taken_bkpt); db_not_taken_bkpt = 0; } if (db_taken_bkpt != 0) { db_delete_temp_breakpoint(db_taken_bkpt); db_taken_bkpt = 0; } } #endif /* SOFTWARE_SSTEP */ extern int db_cmd_loop_done; /* single-step */ /*ARGSUSED*/ void db_single_step_cmd(addr, have_addr, count, modif) db_expr_t addr; boolean_t have_addr; db_expr_t count; char * modif; { boolean_t print = FALSE; if (count == -1) count = 1; if (modif[0] == 'p') print = TRUE; db_run_mode = STEP_ONCE; db_loop_count = count; db_sstep_print = print; db_inst_count = 0; db_load_count = 0; db_store_count = 0; db_cmd_loop_done = 1; } /* trace and print until call/return */ /*ARGSUSED*/ void db_trace_until_call_cmd(addr, have_addr, count, modif) db_expr_t addr; boolean_t have_addr; db_expr_t count; char * modif; { boolean_t print = FALSE; if (modif[0] == 'p') print = TRUE; db_run_mode = STEP_CALLT; db_sstep_print = print; db_inst_count = 0; db_load_count = 0; db_store_count = 0; db_cmd_loop_done = 1; } /*ARGSUSED*/ void db_trace_until_matching_cmd(addr, have_addr, count, modif) db_expr_t addr; boolean_t have_addr; db_expr_t count; char * modif; { boolean_t print = FALSE; if (modif[0] == 'p') print = TRUE; db_run_mode = STEP_RETURN; db_call_depth = 1; db_sstep_print = print; db_inst_count = 0; db_load_count = 0; db_store_count = 0; db_cmd_loop_done = 1; } /* continue */ /*ARGSUSED*/ void db_continue_cmd(addr, have_addr, count, modif) db_expr_t addr; boolean_t have_addr; db_expr_t count; char * modif; { if (modif[0] == 'c') db_run_mode = STEP_COUNT; else db_run_mode = STEP_CONTINUE; db_inst_count = 0; db_load_count = 0; db_store_count = 0; db_cmd_loop_done = 1; } Index: head/sys/ddb/db_sym.h =================================================================== --- head/sys/ddb/db_sym.h (revision 272957) +++ head/sys/ddb/db_sym.h (revision 272958) @@ -1,106 +1,106 @@ /*- * 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. * * $FreeBSD$ */ #ifndef _DDB_DB_SYM_H_ #define _DDB_DB_SYM_H_ /* - * Author: Alessandro Forin, Carnegie Mellon University + * Author: Alessandro Forin, Carnegie Mellon University * Date: 8/90 */ /* * This module can handle multiple symbol tables */ typedef struct { char *name; /* symtab name */ char *start; /* symtab location */ char *end; char *private; /* optional machdep pointer */ } db_symtab_t; /* * Symbol representation is specific to the symtab style: * BSD compilers use dbx' nlist, other compilers might use * a different one */ typedef char * db_sym_t; /* opaque handle on symbols */ typedef const char * c_db_sym_t; /* const opaque handle on symbols */ #define DB_SYM_NULL ((db_sym_t)0) #define C_DB_SYM_NULL ((c_db_sym_t)0) /* * Non-stripped symbol tables will have duplicates, for instance * the same string could match a parameter name, a local var, a * global var, etc. * We are most concern with the following matches. */ typedef int db_strategy_t; /* search strategy */ #define DB_STGY_ANY 0 /* anything goes */ #define DB_STGY_XTRN 1 /* only external symbols */ #define DB_STGY_PROC 2 /* only procedures */ /* * Functions exported by the symtable module */ void db_add_symbol_table(char *, char *, char *, char *); /* extend the list of symbol tables */ c_db_sym_t db_search_symbol(db_addr_t, db_strategy_t, db_expr_t *); /* find symbol given value */ void db_symbol_values(c_db_sym_t, const char **, db_expr_t *); /* return name and value of symbol */ #define db_find_sym_and_offset(val,namep,offp) \ db_symbol_values(db_search_symbol(val,DB_STGY_ANY,offp),namep,0) /* find name&value given approx val */ #define db_find_xtrn_sym_and_offset(val,namep,offp) \ db_symbol_values(db_search_symbol(val,DB_STGY_XTRN,offp),namep,0) /* ditto, but no locals */ int db_eqname(const char *, const char *, int); /* strcmp, modulo leading char */ void db_printsym(db_expr_t, db_strategy_t); /* print closest symbol to a value */ int db_sym_numargs(c_db_sym_t, int *, char **); boolean_t X_db_line_at_pc(db_symtab_t *symtab, c_db_sym_t cursym, char **filename, int *linenum, db_expr_t off); c_db_sym_t X_db_lookup(db_symtab_t *stab, const char *symstr); c_db_sym_t X_db_search_symbol(db_symtab_t *symtab, db_addr_t off, db_strategy_t strategy, db_expr_t *diffp); int X_db_sym_numargs(db_symtab_t *, c_db_sym_t, int *, char **); void X_db_symbol_values(db_symtab_t *symtab, c_db_sym_t sym, const char **namep, db_expr_t *valuep); #endif /* !_DDB_DB_SYM_H_ */ Index: head/sys/ddb/db_textdump.c =================================================================== --- head/sys/ddb/db_textdump.c (revision 272957) +++ head/sys/ddb/db_textdump.c (revision 272958) @@ -1,550 +1,550 @@ /*- * Copyright (c) 2007 Robert N. M. Watson * 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. */ /*- * Kernel text-dump support: write a series of text files to the dump * partition for later recovery, including captured DDB output, kernel * configuration, message buffer, and panic message. This allows for a more * compact representation of critical debugging information than traditional * binary dumps, as well as allowing dump information to be used without * access to kernel symbols, source code, etc. * * Storage Layout * -------------- * * Crash dumps are aligned to the end of the dump or swap partition in order * to minimize the chances of swap duing fsck eating into the dump. However, * unlike a memory dump, we don't know the size of the textdump a priori, so * can't just write it out sequentially in order from a known starting point * calculated with respect to the end of the partition. In order to address * this, we actually write out the textdump in reverse block order, allowing * us to directly align it to the end of the partition and then write out the * dump header and trailer before and after it once done. savecore(8) must * know to reverse the order of the blocks in order to produce a readable * file. * * Data is written out in the ustar file format so that we can write data * incrementally as a stream without reference to previous files. * * TODO * ---- * * - Allow subsytems to register to submit files for inclusion in the text * dump in a generic way. */ #include __FBSDID("$FreeBSD$"); #include "opt_config.h" #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include static SYSCTL_NODE(_debug_ddb, OID_AUTO, textdump, CTLFLAG_RW, 0, "DDB textdump options"); /* * Don't touch the first SIZEOF_METADATA bytes on the dump device. This is * to protect us from metadata and metadata from us. */ #define SIZEOF_METADATA (64*1024) /* * Data is written out as a series of files in the ustar tar format. ustar * is a simple streamed format consiting of a series of files prefixed with * headers, and all padded to 512-byte block boundaries, which maps * conveniently to our requirements. */ struct ustar_header { char uh_filename[100]; char uh_mode[8]; char uh_tar_owner[8]; char uh_tar_group[8]; char uh_size[12]; char uh_mtime[12]; char uh_sum[8]; char uh_type; char uh_linkfile[100]; char uh_ustar[6]; char uh_version[2]; char uh_owner[32]; char uh_group[32]; char uh_major[8]; char uh_minor[8]; char uh_filenameprefix[155]; char uh_zeropad[12]; } __packed; /* * Various size assertions -- pretty much everything must be one block in * size. */ CTASSERT(sizeof(struct kerneldumpheader) == TEXTDUMP_BLOCKSIZE); CTASSERT(sizeof(struct ustar_header) == TEXTDUMP_BLOCKSIZE); /* * Is a textdump scheduled? If so, the shutdown code will invoke our dumpsys * routine instead of the machine-dependent kernel dump routine. */ #ifdef TEXTDUMP_PREFERRED int textdump_pending = 1; #else int textdump_pending = 0; #endif SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, pending, CTLFLAG_RW, &textdump_pending, 0, "Perform textdump instead of regular kernel dump."); /* * Various constants for tar headers and contents. */ #define TAR_USER "root" #define TAR_GROUP "wheel" #define TAR_UID "0" #define TAR_GID "0" #define TAR_MODE "0600" #define TAR_USTAR "ustar" #define TAR_CONFIG_FILENAME "config.txt" /* Kernel configuration. */ #define TAR_MSGBUF_FILENAME "msgbuf.txt" /* Kernel messsage buffer. */ #define TAR_PANIC_FILENAME "panic.txt" /* Panic message. */ #define TAR_VERSION_FILENAME "version.txt" /* Kernel version. */ /* * Configure which files will be dumped. */ #ifdef INCLUDE_CONFIG_FILE static int textdump_do_config = 1; SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, do_config, CTLFLAG_RW, &textdump_do_config, 0, "Dump kernel configuration in textdump"); #endif static int textdump_do_ddb = 1; SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, do_ddb, CTLFLAG_RW, &textdump_do_ddb, 0, "Dump DDB captured output in textdump"); static int textdump_do_msgbuf = 1; SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, do_msgbuf, CTLFLAG_RW, &textdump_do_msgbuf, 0, "Dump kernel message buffer in textdump"); static int textdump_do_panic = 1; SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, do_panic, CTLFLAG_RW, &textdump_do_panic, 0, "Dump kernel panic message in textdump"); static int textdump_do_version = 1; SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, do_version, CTLFLAG_RW, &textdump_do_version, 0, "Dump kernel version string in textdump"); /* * State related to incremental writing of blocks to disk. */ static off_t textdump_offset; /* Offset of next sequential write. */ static int textdump_error; /* Carried write error, if any. */ /* * Statically allocate space to prepare block-sized headers and data. */ char textdump_block_buffer[TEXTDUMP_BLOCKSIZE]; static struct kerneldumpheader kdh; /* * Calculate and fill in the checksum for a ustar header. */ static void ustar_checksum(struct ustar_header *uhp) { u_int sum; int i; for (i = 0; i < sizeof(uhp->uh_sum); i++) uhp->uh_sum[i] = ' '; sum = 0; for (i = 0; i < sizeof(*uhp); i++) sum += ((u_char *)uhp)[i]; snprintf(uhp->uh_sum, sizeof(uhp->uh_sum), "%6o", sum); } /* * Each file in the tarball has a block-sized header with its name and other, * largely hard-coded, properties. */ void textdump_mkustar(char *block_buffer, const char *filename, u_int size) { struct ustar_header *uhp; #ifdef TEXTDUMP_VERBOSE if (textdump_error == 0) printf("textdump: creating '%s'.\n", filename); #endif uhp = (struct ustar_header *)block_buffer; bzero(uhp, sizeof(*uhp)); strlcpy(uhp->uh_filename, filename, sizeof(uhp->uh_filename)); strlcpy(uhp->uh_mode, TAR_MODE, sizeof(uhp->uh_mode)); snprintf(uhp->uh_size, sizeof(uhp->uh_size), "%o", size); strlcpy(uhp->uh_tar_owner, TAR_UID, sizeof(uhp->uh_tar_owner)); strlcpy(uhp->uh_tar_group, TAR_GID, sizeof(uhp->uh_tar_group)); strlcpy(uhp->uh_owner, TAR_USER, sizeof(uhp->uh_owner)); strlcpy(uhp->uh_group, TAR_GROUP, sizeof(uhp->uh_group)); snprintf(uhp->uh_mtime, sizeof(uhp->uh_mtime), "%lo", (unsigned long)time_second); uhp->uh_type = 0; strlcpy(uhp->uh_ustar, TAR_USTAR, sizeof(uhp->uh_ustar)); ustar_checksum(uhp); } /* * textdump_writeblock() writes TEXTDUMP_BLOCKSIZE-sized blocks of data to * the space between di->mediaoffset and di->mediaoffset + di->mediasize. It * accepts an offset relative to di->mediaoffset. If we're carrying any * error from previous I/O, return that error and don't continue to try to * write. Most writers ignore the error and forge ahead on the basis that * there's not much you can do. */ static int textdump_writeblock(struct dumperinfo *di, off_t offset, char *buffer) { if (textdump_error) return (textdump_error); if (offset + TEXTDUMP_BLOCKSIZE > di->mediasize) return (EIO); if (offset < SIZEOF_METADATA) return (ENOSPC); textdump_error = dump_write(di, buffer, 0, offset + di->mediaoffset, TEXTDUMP_BLOCKSIZE); if (textdump_error) printf("textdump_writeblock: offset %jd, error %d\n", (intmax_t)offset, textdump_error); return (textdump_error); } /* * Interfaces to save and restore the dump offset, so that printers can go * back to rewrite a header if required, while avoiding their knowing about * the global layout of the blocks. * * If we ever want to support writing textdumps to tape or other * stream-oriented target, we'll need to remove this. */ void textdump_saveoff(off_t *offsetp) { *offsetp = textdump_offset; } void textdump_restoreoff(off_t offset) { textdump_offset = offset; } /* * Interface to write the "next block" relative to the current offset; since * we write backwards from the end of the partition, we subtract, but there's * no reason for the caller to know this. */ int textdump_writenextblock(struct dumperinfo *di, char *buffer) { int error; error = textdump_writeblock(di, textdump_offset, buffer); textdump_offset -= TEXTDUMP_BLOCKSIZE; return (error); } #ifdef INCLUDE_CONFIG_FILE extern char kernconfstring[]; /* * Dump kernel configuration. */ static void textdump_dump_config(struct dumperinfo *di) { u_int count, fullblocks, len; len = strlen(kernconfstring); textdump_mkustar(textdump_block_buffer, TAR_CONFIG_FILENAME, len); (void)textdump_writenextblock(di, textdump_block_buffer); /* * Write out all full blocks directly from the string, and handle any * left-over bits by copying it to out to the local buffer and * zero-padding it. */ fullblocks = len / TEXTDUMP_BLOCKSIZE; for (count = 0; count < fullblocks; count++) (void)textdump_writenextblock(di, kernconfstring + count * TEXTDUMP_BLOCKSIZE); if (len % TEXTDUMP_BLOCKSIZE != 0) { bzero(textdump_block_buffer, TEXTDUMP_BLOCKSIZE); bcopy(kernconfstring + count * TEXTDUMP_BLOCKSIZE, textdump_block_buffer, len % TEXTDUMP_BLOCKSIZE); (void)textdump_writenextblock(di, textdump_block_buffer); } } #endif /* INCLUDE_CONFIG_FILE */ /* * Dump kernel message buffer. */ static void textdump_dump_msgbuf(struct dumperinfo *di) { off_t end_offset, tarhdr_offset; u_int i, len, offset, seq, total_len; char buf[16]; /* * Write out a dummy tar header to advance the offset; we'll rewrite * it later once we know the true size. */ textdump_saveoff(&tarhdr_offset); textdump_mkustar(textdump_block_buffer, TAR_MSGBUF_FILENAME, 0); (void)textdump_writenextblock(di, textdump_block_buffer); /* * Copy out the data in small chunks, but don't copy nuls that may be * present if the message buffer has not yet completely filled at * least once. */ total_len = 0; offset = 0; - msgbuf_peekbytes(msgbufp, NULL, 0, &seq); - while ((len = msgbuf_peekbytes(msgbufp, buf, sizeof(buf), &seq)) > 0) { + msgbuf_peekbytes(msgbufp, NULL, 0, &seq); + while ((len = msgbuf_peekbytes(msgbufp, buf, sizeof(buf), &seq)) > 0) { for (i = 0; i < len; i++) { if (buf[i] == '\0') continue; textdump_block_buffer[offset] = buf[i]; offset++; if (offset != sizeof(textdump_block_buffer)) continue; (void)textdump_writenextblock(di, textdump_block_buffer); total_len += offset; offset = 0; } - } + } total_len += offset; /* Without the zero-padding. */ if (offset != 0) { bzero(textdump_block_buffer + offset, sizeof(textdump_block_buffer) - offset); (void)textdump_writenextblock(di, textdump_block_buffer); } /* * Rewrite tar header to reflect how much was actually written. */ textdump_saveoff(&end_offset); textdump_restoreoff(tarhdr_offset); textdump_mkustar(textdump_block_buffer, TAR_MSGBUF_FILENAME, total_len); (void)textdump_writenextblock(di, textdump_block_buffer); textdump_restoreoff(end_offset); } static void textdump_dump_panic(struct dumperinfo *di) { u_int len; /* * Write out tar header -- we store up to one block of panic message. */ len = min(strlen(panicstr), TEXTDUMP_BLOCKSIZE); textdump_mkustar(textdump_block_buffer, TAR_PANIC_FILENAME, len); (void)textdump_writenextblock(di, textdump_block_buffer); /* * Zero-pad the panic string and write out block. */ bzero(textdump_block_buffer, sizeof(textdump_block_buffer)); bcopy(panicstr, textdump_block_buffer, len); (void)textdump_writenextblock(di, textdump_block_buffer); } static void textdump_dump_version(struct dumperinfo *di) { u_int len; /* * Write out tar header -- at most one block of version information. */ len = min(strlen(version), TEXTDUMP_BLOCKSIZE); textdump_mkustar(textdump_block_buffer, TAR_VERSION_FILENAME, len); (void)textdump_writenextblock(di, textdump_block_buffer); /* * Zero pad the version string and write out block. */ bzero(textdump_block_buffer, sizeof(textdump_block_buffer)); bcopy(version, textdump_block_buffer, len); (void)textdump_writenextblock(di, textdump_block_buffer); } /* * Commit text dump to disk. */ void textdump_dumpsys(struct dumperinfo *di) { off_t dumplen, trailer_offset; if (di->blocksize != TEXTDUMP_BLOCKSIZE) { printf("Dump partition block size (%ju) not textdump " "block size (%ju)", (uintmax_t)di->blocksize, (uintmax_t)TEXTDUMP_BLOCKSIZE); return; } /* * We don't know a priori how large the dump will be, but we do know * that we need to reserve space for metadata and that we need two * dump headers. Also leave room for one ustar header and one block * of data. */ if (di->mediasize < SIZEOF_METADATA + 2 * sizeof(kdh)) { printf("Insufficient space on dump partition for minimal textdump.\n"); return; } textdump_error = 0; /* * Position the start of the dump so that we'll write the kernel dump * trailer immediately before the end of the partition, and then work * our way back. We will rewrite this header later to reflect the * true size if things go well. */ textdump_offset = di->mediasize - sizeof(kdh); textdump_saveoff(&trailer_offset); mkdumpheader(&kdh, TEXTDUMPMAGIC, KERNELDUMP_TEXT_VERSION, 0, TEXTDUMP_BLOCKSIZE); (void)textdump_writenextblock(di, (char *)&kdh); /* * Write a series of files in ustar format. */ if (textdump_do_ddb) db_capture_dump(di); #ifdef INCLUDE_CONFIG_FILE if (textdump_do_config) textdump_dump_config(di); #endif if (textdump_do_msgbuf) textdump_dump_msgbuf(di); if (textdump_do_panic && panicstr != NULL) textdump_dump_panic(di); if (textdump_do_version) textdump_dump_version(di); /* * Now that we know the true size, we can write out the header, then * seek back to the end and rewrite the trailer with the correct * size. */ dumplen = trailer_offset - (textdump_offset + TEXTDUMP_BLOCKSIZE); mkdumpheader(&kdh, TEXTDUMPMAGIC, KERNELDUMP_TEXT_VERSION, dumplen, TEXTDUMP_BLOCKSIZE); (void)textdump_writenextblock(di, (char *)&kdh); textdump_restoreoff(trailer_offset); (void)textdump_writenextblock(di, (char *)&kdh); /* * Terminate the dump, report any errors, and clear the pending flag. */ if (textdump_error == 0) (void)dump_write(di, NULL, 0, 0, 0); if (textdump_error == ENOSPC) printf("Textdump: Insufficient space on dump partition\n"); else if (textdump_error != 0) printf("Textdump: Error %d writing dump\n", textdump_error); else printf("Textdump complete.\n"); textdump_pending = 0; } /*- * DDB(4) command to manage textdumps: * * textdump set - request a textdump * textdump status - print DDB output textdump status * textdump unset - clear textdump request */ static void db_textdump_usage(void) { db_printf("textdump [unset|set|status|dump]\n"); } void db_textdump_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, char *modif) { int t; t = db_read_token(); if (t != tIDENT) { db_textdump_usage(); return; } if (db_read_token() != tEOL) { db_textdump_usage(); return; } if (strcmp(db_tok_string, "set") == 0) { textdump_pending = 1; db_printf("textdump set\n"); } else if (strcmp(db_tok_string, "status") == 0) { if (textdump_pending) db_printf("textdump is set\n"); else db_printf("textdump is not set\n"); } else if (strcmp(db_tok_string, "unset") == 0) { textdump_pending = 0; db_printf("textdump unset\n"); } else if (strcmp(db_tok_string, "dump") == 0) { textdump_pending = 1; doadump(TRUE); } else { db_textdump_usage(); } } Index: head/sys/ddb/db_variables.h =================================================================== --- head/sys/ddb/db_variables.h (revision 272957) +++ head/sys/ddb/db_variables.h (revision 272958) @@ -1,63 +1,63 @@ /*- * 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. * * $FreeBSD$ */ /* - * Author: David B. Golub, Carnegie Mellon University + * Author: David B. Golub, Carnegie Mellon University * Date: 7/90 */ #ifndef _DDB_DB_VARIABLES_H_ #define _DDB_DB_VARIABLES_H_ /* * Debugger variables. */ struct db_variable; typedef int db_varfcn_t(struct db_variable *vp, db_expr_t *valuep, int op); struct db_variable { char *name; /* Name of variable */ db_expr_t *valuep; /* value of variable */ /* function to call when reading/writing */ db_varfcn_t *fcn; #define DB_VAR_GET 0 #define DB_VAR_SET 1 }; #define FCN_NULL ((db_varfcn_t *)0) extern struct db_variable db_regs[]; /* machine registers */ extern struct db_variable *db_eregs; extern db_varfcn_t db_var_curcpu; /* DPCPU default CPU */ extern db_varfcn_t db_var_curvnet; /* Default vnet */ extern db_varfcn_t db_var_db_cpu; /* DPCPU active CPU */ extern db_varfcn_t db_var_db_vnet; /* Active vnet */ int db_read_variable(struct db_variable *, db_expr_t *); int db_write_variable(struct db_variable *, db_expr_t); #endif /* _!DDB_DB_VARIABLES_H_ */ Index: head/sys/ddb/db_watch.c =================================================================== --- head/sys/ddb/db_watch.c (revision 272957) +++ head/sys/ddb/db_watch.c (revision 272958) @@ -1,331 +1,331 @@ /*- * 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 /* * Watchpoints. */ static boolean_t 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 boolean_t 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() { register 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(watch) register db_watchpoint_t watch; { watch->link = db_free_watchpoints; db_free_watchpoints = watch; } static void db_set_watchpoint(map, addr, size) vm_map_t map; db_addr_t addr; vm_size_t size; { register 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(map, addr) vm_map_t map; db_addr_t addr; { register db_watchpoint_t watch; register 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() { register 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(addr, have_addr, count, modif) db_expr_t addr; boolean_t have_addr; db_expr_t count; char * modif; { db_delete_watchpoint(db_map_addr(addr), addr); } /* Set watchpoint */ /*ARGSUSED*/ void db_watchpoint_cmd(addr, have_addr, count, modif) db_expr_t addr; boolean_t 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() { register 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() { db_watchpoints_inserted = FALSE; } #ifdef notused static boolean_t db_find_watchpoint(map, addr, regs) vm_map_t map; db_addr_t addr; db_regs_t *regs; { register 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(addr, have_addr, count, modif) db_expr_t addr; boolean_t have_addr; db_expr_t count; char * modif; { int rc; - if (count < 0) - count = 4; + if (count < 0) + count = 4; rc = db_md_clr_watchpoint(addr, count); if (rc < 0) db_printf("hardware watchpoint could not be deleted\n"); } /* Set hardware watchpoint */ /*ARGSUSED*/ void db_hwatchpoint_cmd(addr, have_addr, count, modif) db_expr_t addr; boolean_t have_addr; db_expr_t count; char * modif; { int rc; - if (count < 0) - count = 4; + if (count < 0) + count = 4; rc = db_md_set_watchpoint(addr, count); if (rc < 0) db_printf("hardware watchpoint could not be set\n"); } Index: head/sys/ddb/db_watch.h =================================================================== --- head/sys/ddb/db_watch.h (revision 272957) +++ head/sys/ddb/db_watch.h (revision 272958) @@ -1,48 +1,48 @@ /*- * 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. * * $FreeBSD$ */ /* - * Author: David B. Golub, Carnegie Mellon University + * Author: David B. Golub, Carnegie Mellon University * Date: 10/90 */ #ifndef _DDB_DB_WATCH_H_ #define _DDB_DB_WATCH_H_ /* * Watchpoint. */ typedef struct db_watchpoint { vm_map_t map; /* in this map */ db_addr_t loaddr; /* from this address */ db_addr_t hiaddr; /* to this address */ struct db_watchpoint *link; /* link in in-use or free chain */ } *db_watchpoint_t; #endif /* !_DDB_DB_WATCH_H_ */