summaryrefslogtreecommitdiffstats
path: root/sys/ddb
diff options
context:
space:
mode:
authorsam <sam@FreeBSD.org>2008-09-15 22:45:14 +0000
committersam <sam@FreeBSD.org>2008-09-15 22:45:14 +0000
commit05a7094fc1d2a54b7d63fa43d1371840293d2e39 (patch)
tree6a6cd52918de6f264f6071e5f55fc410c7f1633d /sys/ddb
parentc0edaf988ab997509a5c7a738c5fb5c5305cc014 (diff)
downloadFreeBSD-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.c139
-rw-r--r--sys/ddb/ddb.h136
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;
OpenPOWER on IntegriCloud