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:
parent
ca3d37955c
commit
39297ba455
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=183054
@ -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
|
||||
|
134
sys/ddb/ddb.h
134
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)
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user