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
This commit is contained in:
Sam Leffler 2008-09-15 22:45:14 +00:00
parent ca3d37955c
commit 39297ba455
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=183054
9 changed files with 186 additions and 105 deletions

View File

@ -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;
@ -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

View File

@ -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)
/*
* 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.
*/

View File

@ -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;

View File

@ -12,7 +12,7 @@
#include <support/kdb.h>
#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");
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)
{