From 39297ba45528361278dc0522b99c659f3a38cbb8 Mon Sep 17 00:00:00 2001 From: Sam Leffler Date: Mon, 15 Sep 2008 22:45:14 +0000 Subject: [PATCH] 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 (original version) Reviewed by: jhb MFC after: 1 month --- sys/ddb/db_command.c | 139 +++++++++++++++++---------- sys/ddb/ddb.h | 134 +++++++++++++++++--------- sys/dev/aic7xxx/aic79xx_osm.c | 2 +- sys/gnu/fs/xfs/FreeBSD/support/kdb.c | 2 +- sys/kern/subr_pcpu.c | 3 +- sys/kern/subr_rman.c | 3 +- sys/kern/subr_sleepqueue.c | 2 +- sys/kern/subr_turnstile.c | 3 +- sys/kern/subr_witness.c | 3 +- 9 files changed, 186 insertions(+), 105 deletions(-) diff --git a/sys/ddb/db_command.c b/sys/ddb/db_command.c index 0395e75df44e..bac7803ecdce 100644 --- a/sys/ddb/db_command.c +++ b/sys/ddb/db_command.c @@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -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; @@ -196,6 +178,73 @@ static int db_cmd_search(char *name, struct command_table *table, 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. */ @@ -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 57c5fd15985c..1afdfa39a54e 100644 --- a/sys/ddb/ddb.h +++ b/sys/ddb/ddb.h @@ -43,6 +43,9 @@ SYSCTL_DECL(_debug_ddb); #include /* type definitions */ +#include /* LIST_* */ +#include /* 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) +/* + * 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 */ +}; -#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 \ +/* + * 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; @@ -178,28 +244,6 @@ db_cmdfcn_t db_unscript_cmd; 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. */ diff --git a/sys/dev/aic7xxx/aic79xx_osm.c b/sys/dev/aic7xxx/aic79xx_osm.c index f8c56627dac5..e375d2450568 100644 --- a/sys/dev/aic7xxx/aic79xx_osm.c +++ b/sys/dev/aic7xxx/aic79xx_osm.c @@ -1423,7 +1423,7 @@ DB_COMMAND(ahd_in, ahd_ddb_in) } } -DB_FUNC(ahd_out, ahd_ddb_out, db_cmd_set, CS_MORE, NULL) +DB_FUNC(ahd_out, ahd_ddb_out, db_cmd_table, CS_MORE, NULL) { db_expr_t old_value; db_expr_t new_value; diff --git a/sys/gnu/fs/xfs/FreeBSD/support/kdb.c b/sys/gnu/fs/xfs/FreeBSD/support/kdb.c index 508084bf3ce7..274f23d25b16 100644 --- a/sys/gnu/fs/xfs/FreeBSD/support/kdb.c +++ b/sys/gnu/fs/xfs/FreeBSD/support/kdb.c @@ -12,7 +12,7 @@ #include #ifdef DDB -DB_FUNC(xfs, xfs_ddb_cmd, db_cmd_set, CS_MORE, NULL) +DB_FUNC(xfs, xfs_ddb_cmd, db_cmd_table, CS_MORE, NULL) { db_error("No commands registered.\n"); } diff --git a/sys/kern/subr_pcpu.c b/sys/kern/subr_pcpu.c index d0fd0611f90c..e9fb8d0d1d81 100644 --- a/sys/kern/subr_pcpu.c +++ b/sys/kern/subr_pcpu.c @@ -155,7 +155,7 @@ DB_SHOW_COMMAND(pcpu, db_show_pcpu) show_pcpu(pc); } -DB_SHOW_COMMAND(allpcpu, db_show_cpu_all) +DB_SHOW_ALL_COMMAND(pcpu, db_show_cpu_all) { struct pcpu *pc; int id; @@ -169,4 +169,5 @@ DB_SHOW_COMMAND(allpcpu, db_show_cpu_all) } } } +DB_SHOW_ALIAS(allpcpu, db_show_cpu_all); #endif diff --git a/sys/kern/subr_rman.c b/sys/kern/subr_rman.c index c0a1c729d23b..ae350ed18d22 100644 --- a/sys/kern/subr_rman.c +++ b/sys/kern/subr_rman.c @@ -949,11 +949,12 @@ DB_SHOW_COMMAND(rman, db_show_rman) dump_rman((struct rman *)addr); } -DB_SHOW_COMMAND(allrman, db_show_all_rman) +DB_SHOW_ALL_COMMAND(rman, db_show_all_rman) { struct rman *rm; TAILQ_FOREACH(rm, &rman_head, rm_link) dump_rman(rm); } +DB_SHOW_ALIAS(allrman, db_show_all_rman); #endif diff --git a/sys/kern/subr_sleepqueue.c b/sys/kern/subr_sleepqueue.c index 54bd2b11606f..f9bac9b1fd23 100644 --- a/sys/kern/subr_sleepqueue.c +++ b/sys/kern/subr_sleepqueue.c @@ -1167,5 +1167,5 @@ DB_SHOW_COMMAND(sleepq, db_show_sleepqueue) } /* Alias 'show sleepqueue' to 'show sleepq'. */ -DB_SET(sleepqueue, db_show_sleepqueue, db_show_cmd_set, 0, NULL); +DB_SHOW_ALIAS(sleepqueue, db_show_sleepqueue); #endif diff --git a/sys/kern/subr_turnstile.c b/sys/kern/subr_turnstile.c index 6d96aa307f62..31c8cfc4837d 100644 --- a/sys/kern/subr_turnstile.c +++ b/sys/kern/subr_turnstile.c @@ -1150,7 +1150,7 @@ DB_SHOW_COMMAND(lockchain, db_show_lockchain) print_lockchain(td, ""); } -DB_SHOW_COMMAND(allchains, db_show_allchains) +DB_SHOW_ALL_COMMAND(chains, db_show_allchains) { struct thread *td; struct proc *p; @@ -1168,6 +1168,7 @@ DB_SHOW_COMMAND(allchains, db_show_allchains) } } } +DB_SHOW_ALIAS(allchains, db_show_allchains) /* * Show all the threads a particular thread is waiting on based on diff --git a/sys/kern/subr_witness.c b/sys/kern/subr_witness.c index 0e00107f5f38..9cdad8daaa9f 100644 --- a/sys/kern/subr_witness.c +++ b/sys/kern/subr_witness.c @@ -2248,7 +2248,7 @@ DB_SHOW_COMMAND(locks, db_witness_list) witness_ddb_list(td); } -DB_SHOW_COMMAND(alllocks, db_witness_list_all) +DB_SHOW_ALL_COMMAND(locks, db_witness_list_all) { struct thread *td; struct proc *p; @@ -2270,6 +2270,7 @@ DB_SHOW_COMMAND(alllocks, db_witness_list_all) } } } +DB_SHOW_ALIAS(alllocks, db_witness_list_all) DB_SHOW_COMMAND(witness, db_witness_display) {