diff options
author | sam <sam@FreeBSD.org> | 2008-09-15 22:45:14 +0000 |
---|---|---|
committer | sam <sam@FreeBSD.org> | 2008-09-15 22:45:14 +0000 |
commit | 05a7094fc1d2a54b7d63fa43d1371840293d2e39 (patch) | |
tree | 6a6cd52918de6f264f6071e5f55fc410c7f1633d /sys/ddb | |
parent | c0edaf988ab997509a5c7a738c5fb5c5305cc014 (diff) | |
download | FreeBSD-src-05a7094fc1d2a54b7d63fa43d1371840293d2e39.zip FreeBSD-src-05a7094fc1d2a54b7d63fa43d1371840293d2e39.tar.gz |
Make ddb command registration dynamic so modules can extend
the command set (only so long as the module is present):
o add db_command_register and db_command_unregister to add and remove
commands, respectively
o replace linker sets with SYSINIT's (and SYSUINIT's) that register
commands
o expose 3 list heads: db_cmd_table, db_show_table, and db_show_all_table
for registering top-level commands, show operands, and show all operands,
respectively
While here also:
o sort command lists
o add DB_ALIAS, DB_SHOW_ALIAS, and DB_SHOW_ALL_ALIAS to add aliases
for existing commands
o add "show all trace" as an alias for "show alltrace"
o add "show all locks" as an alias for "show alllocks"
Submitted by: Guillaume Ballet <gballet@gmail.com> (original version)
Reviewed by: jhb
MFC after: 1 month
Diffstat (limited to 'sys/ddb')
-rw-r--r-- | sys/ddb/db_command.c | 139 | ||||
-rw-r--r-- | sys/ddb/ddb.h | 136 |
2 files changed, 176 insertions, 99 deletions
diff --git a/sys/ddb/db_command.c b/sys/ddb/db_command.c index 0395e75..bac7803 100644 --- a/sys/ddb/db_command.c +++ b/sys/ddb/db_command.c @@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$"); #include <sys/systm.h> #include <sys/cons.h> #include <sys/watchdog.h> +#include <sys/kernel.h> #include <ddb/ddb.h> #include <ddb/db_command.h> @@ -63,10 +64,6 @@ db_addr_t db_last_addr; db_addr_t db_prev; db_addr_t db_next; -SET_DECLARE(db_cmd_set, struct command); -SET_DECLARE(db_show_cmd_set, struct command); -SET_DECLARE(db_show_all_cmd_set, struct command); - static db_cmdfcn_t db_fncall; static db_cmdfcn_t db_gdb; static db_cmdfcn_t db_halt; @@ -81,30 +78,20 @@ static db_cmdfcn_t db_watchdog; */ static struct command db_show_all_cmds[] = { - { (char *)0 } -}; - -static struct command_table db_show_all_table = { - db_show_all_cmds, - SET_BEGIN(db_show_all_cmd_set), - SET_LIMIT(db_show_all_cmd_set) + { "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 }, - { (char *)0, } }; +struct command_table db_show_table = LIST_HEAD_INITIALIZER(db_show_table); -static struct command_table db_show_table = { - db_show_cmds, - SET_BEGIN(db_show_cmd_set), - SET_LIMIT(db_show_cmd_set) -}; - -static struct command db_commands[] = { +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 }, @@ -130,6 +117,7 @@ static struct command db_commands[] = { { "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 }, @@ -149,14 +137,8 @@ static struct command db_commands[] = { { "unscript", db_unscript_cmd, CS_OWN, 0 }, { "capture", db_capture_cmd, CS_OWN, 0 }, { "textdump", db_textdump_cmd, CS_OWN, 0 }, - { (char *)0, } -}; - -static struct command_table db_command_table = { - db_commands, - SET_BEGIN(db_cmd_set), - SET_LIMIT(db_cmd_set) }; +struct command_table db_cmd_table = LIST_HEAD_INITIALIZER(db_cmd_table); static struct command *db_last_command = 0; @@ -197,6 +179,73 @@ static void db_command(struct command **last_cmdp, struct command_table *cmd_table, int dopager); /* + * Initialize the command lists from the static tables. + */ +static void +db_cmd_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 +} +SYSINIT(_cmd_init, SI_SUB_KLD, SI_ORDER_FIRST, db_cmd_init, NULL); + +/* + * 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 @@ -245,22 +294,14 @@ db_cmd_search(name, table, cmdp) struct command **cmdp; /* out */ { struct command *cmd; - struct command **aux_cmdp; int result = CMD_NONE; - for (cmd = table->table; cmd->name != 0; cmd++) { - db_cmd_match(name, cmd, cmdp, &result); + LIST_FOREACH(cmd, table, next) { + db_cmd_match(name,cmd,cmdp,&result); if (result == CMD_UNIQUE) - return (CMD_UNIQUE); + break; } - if (table->aux_tablep != NULL) - for (aux_cmdp = table->aux_tablep; - aux_cmdp < table->aux_tablep_end; - aux_cmdp++) { - db_cmd_match(name, *aux_cmdp, cmdp, &result); - if (result == CMD_UNIQUE) - return (CMD_UNIQUE); - } + if (result == CMD_NONE) { /* check for 'help' */ if (name[0] == 'h' && name[1] == 'e' @@ -274,19 +315,11 @@ static void db_cmd_list(table) struct command_table *table; { - register struct command *cmd; - register struct command **aux_cmdp; + register struct command *cmd; - for (cmd = table->table; cmd->name != 0; cmd++) { - db_printf("%-12s", cmd->name); - db_end_line(12); - } - if (table->aux_tablep == NULL) - return; - for (aux_cmdp = table->aux_tablep; aux_cmdp < table->aux_tablep_end; - aux_cmdp++) { - db_printf("%-12s", (*aux_cmdp)->name); - db_end_line(12); + LIST_FOREACH(cmd, table, next) { + db_printf("%-12s", cmd->name); + db_end_line(12); } } @@ -296,7 +329,7 @@ db_command(last_cmdp, cmd_table, dopager) struct command_table *cmd_table; int dopager; { - struct command *cmd; + struct command *cmd = NULL; int t; char modif[TOK_STRING_SIZE]; db_expr_t addr, count; @@ -463,7 +496,7 @@ db_command_loop() db_printf("db> "); (void) db_read_line(); - db_command(&db_last_command, &db_command_table, /* dopager */ 1); + db_command(&db_last_command, &db_cmd_table, /* dopager */ 1); } } @@ -481,7 +514,7 @@ db_command_script(const char *command) { db_prev = db_next = db_dot; db_inject_line(command); - db_command(&db_last_command, &db_command_table, /* dopager */ 0); + db_command(&db_last_command, &db_cmd_table, /* dopager */ 0); } void diff --git a/sys/ddb/ddb.h b/sys/ddb/ddb.h index 57c5fd1..1afdfa3 100644 --- a/sys/ddb/ddb.h +++ b/sys/ddb/ddb.h @@ -43,6 +43,9 @@ SYSCTL_DECL(_debug_ddb); #include <machine/db_machdep.h> /* type definitions */ +#include <sys/queue.h> /* LIST_* */ +#include <sys/kernel.h> /* SYSINIT */ + #ifndef DB_MAXARGS #define DB_MAXARGS 10 #endif @@ -73,36 +76,97 @@ SYSCTL_DECL(_debug_ddb); int DB_CALL(db_expr_t, db_expr_t *, int, db_expr_t[]); #endif +/* + * There are three "command tables": + * - One for simple commands; a list of these is displayed + * by typing 'help' at the debugger prompt. + * - One for sub-commands of 'show'; to see this type 'show' + * without any arguments. + * - The last one for sub-commands of 'show all'; type 'show all' + * without any argument to get a list. + */ +struct command; +LIST_HEAD(command_table, command); +extern struct command_table db_cmd_table; +extern struct command_table db_show_table; +extern struct command_table db_show_all_table; + +/* + * Type signature for a function implementing a ddb command. + */ typedef void db_cmdfcn_t(db_expr_t addr, boolean_t have_addr, db_expr_t count, char *modif); -#define DB_COMMAND(cmd_name, func_name) \ - DB_FUNC(cmd_name, func_name, db_cmd_set, 0, NULL) -#define DB_SHOW_COMMAND(cmd_name, func_name) \ - DB_FUNC(cmd_name, func_name, db_show_cmd_set, 0, NULL) -#define DB_SHOW_ALL_COMMAND(cmd_name, func_name) \ - DB_FUNC(cmd_name, func_name, db_show_all_cmd_set, 0, NULL) - -#define DB_SET(cmd_name, func_name, set, flag, more) \ -static const struct command __CONCAT(cmd_name,_cmd) = { \ - __STRING(cmd_name), \ - func_name, \ - flag, \ - more \ +/* + * Command table entry. + */ +struct command { + char * name; /* command name */ + db_cmdfcn_t *fcn; /* function to call */ + int flag; /* extra info: */ +#define CS_OWN 0x1 /* non-standard syntax */ +#define CS_MORE 0x2 /* standard syntax, but may have other words + * at end */ +#define CS_SET_DOT 0x100 /* set dot after command */ + struct command_table *more; /* another level of command */ + LIST_ENTRY(command) next; /* next entry in the command table */ +}; + +/* + * Arrange for the specified ddb command to be defined and + * bound to the specified function. Commands can be defined + * in modules in which case they will be available only when + * the module is loaded. + */ +#define _DB_SET(_suffix, _name, _func, list, _flag, _more) \ +static struct command __CONCAT(_name,_suffix) = { \ + .name = __STRING(_name), \ + .fcn = _func, \ + .flag = _flag, \ + .more = _more \ }; \ -TEXT_SET(set, __CONCAT(cmd_name,_cmd)) +static void __CONCAT(__CONCAT(_name,_suffix),_add)(void *arg __unused) \ + { db_command_register(&list, &__CONCAT(_name,_suffix)); } \ +SYSINIT(__CONCAT(_name,_suffix), SI_SUB_KLD, SI_ORDER_ANY, \ + __CONCAT(__CONCAT(_name,_suffix),_add), NULL); \ +static void __CONCAT(__CONCAT(_name,_suffix),_del)(void *arg __unused) \ + { db_command_unregister(&list, &__CONCAT(_name,_suffix)); } \ +SYSUNINIT(__CONCAT(_name,_suffix), SI_SUB_KLD, SI_ORDER_ANY, \ + __CONCAT(__CONCAT(_name,_suffix),_del), NULL); -#define DB_FUNC(cmd_name, func_name, set, flag, more) \ -static db_cmdfcn_t func_name; \ - \ -DB_SET(cmd_name, func_name, set, flag, more); \ - \ +/* + * Like _DB_SET but also create the function declaration which + * must be followed immediately by the body; e.g. + * _DB_FUNC(_cmd, panic, db_panic, db_cmd_table, 0, NULL) + * { + * ...panic implementation... + * } + * + * This macro is mostly used to define commands placed in one of + * the ddb command tables; see DB_COMMAND, etc. below. + */ +#define _DB_FUNC(_suffix, _name, _func, list, _flag, _more) \ +static db_cmdfcn_t _func; \ +_DB_SET(_suffix, _name, _func, list, _flag, _more); \ static void \ -func_name(addr, have_addr, count, modif) \ - db_expr_t addr; \ - boolean_t have_addr; \ - db_expr_t count; \ - char *modif; +_func(db_expr_t addr, boolean_t have_addr, db_expr_t count, char *modif) + +/* common idom provided for backwards compatibility */ +#define DB_FUNC(_name, _func, list, _flag, _more) \ + _DB_FUNC(_cmd, _name, _func, list, _flag, _more) + +#define DB_COMMAND(cmd_name, func_name) \ + _DB_FUNC(_cmd, cmd_name, func_name, db_cmd_table, 0, NULL) +#define DB_ALIAS(alias_name, func_name) \ + _DB_SET(_cmd, alias_name, func_name, db_cmd_table, 0, NULL) +#define DB_SHOW_COMMAND(cmd_name, func_name) \ + _DB_FUNC(_show, cmd_name, func_name, db_show_table, 0, NULL) +#define DB_SHOW_ALIAS(alias_name, func_name) \ + _DB_SET(_show, alias_name, func_name, db_show_table, 0, NULL) +#define DB_SHOW_ALL_COMMAND(cmd_name, func_name) \ + _DB_FUNC(_show_all, cmd_name, func_name, db_show_all_table, 0, NULL) +#define DB_SHOW_ALL_ALIAS(alias_name, func_name) \ + _DB_SET(_show_all, alias_name, func_name, db_show_all_table, 0, NULL) extern db_expr_t db_maxoff; extern int db_indent; @@ -150,6 +214,8 @@ void db_trace_self(void); int db_trace_thread(struct thread *, int); int db_value_of_name(const char *name, db_expr_t *valuep); int db_write_bytes(vm_offset_t addr, size_t size, char *data); +void db_command_register(struct command_table *, struct command *); +void db_command_unregister(struct command_table *, struct command *); db_cmdfcn_t db_breakpoint_cmd; db_cmdfcn_t db_capture_cmd; @@ -179,28 +245,6 @@ db_cmdfcn_t db_watchpoint_cmd; db_cmdfcn_t db_write_cmd; /* - * Command table. - */ -struct command; - -struct command_table { - struct command *table; - struct command **aux_tablep; - struct command **aux_tablep_end; -}; - -struct command { - char * name; /* command name */ - db_cmdfcn_t *fcn; /* function to call */ - int flag; /* extra info: */ -#define CS_OWN 0x1 /* non-standard syntax */ -#define CS_MORE 0x2 /* standard syntax, but may have other words - * at end */ -#define CS_SET_DOT 0x100 /* set dot after command */ - struct command_table *more; /* another level of command */ -}; - -/* * Interface between DDB and the DDB output capture facility. */ struct dumperinfo; |