Release 4.15 kernel/debug/kdb/kdb_main.c
/*
* Kernel Debugger Architecture Independent Main Code
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 1999-2004 Silicon Graphics, Inc. All Rights Reserved.
* Copyright (C) 2000 Stephane Eranian <eranian@hpl.hp.com>
* Xscale (R) modifications copyright (C) 2003 Intel Corporation.
* Copyright (c) 2009 Wind River Systems, Inc. All Rights Reserved.
*/
#include <linux/ctype.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/kmsg_dump.h>
#include <linux/reboot.h>
#include <linux/sched.h>
#include <linux/sched/loadavg.h>
#include <linux/sched/stat.h>
#include <linux/sched/debug.h>
#include <linux/sysrq.h>
#include <linux/smp.h>
#include <linux/utsname.h>
#include <linux/vmalloc.h>
#include <linux/atomic.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/kallsyms.h>
#include <linux/kgdb.h>
#include <linux/kdb.h>
#include <linux/notifier.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/nmi.h>
#include <linux/time.h>
#include <linux/ptrace.h>
#include <linux/sysctl.h>
#include <linux/cpu.h>
#include <linux/kdebug.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include "kdb_private.h"
#undef MODULE_PARAM_PREFIX
#define MODULE_PARAM_PREFIX "kdb."
static int kdb_cmd_enabled = CONFIG_KDB_DEFAULT_ENABLE;
module_param_named(cmd_enable, kdb_cmd_enabled, int, 0600);
char kdb_grep_string[KDB_GREP_STRLEN];
int kdb_grepping_flag;
EXPORT_SYMBOL(kdb_grepping_flag);
int kdb_grep_leading;
int kdb_grep_trailing;
/*
* Kernel debugger state flags
*/
int kdb_flags;
/*
* kdb_lock protects updates to kdb_initial_cpu. Used to
* single thread processors through the kernel debugger.
*/
int kdb_initial_cpu = -1;
/* cpu number that owns kdb */
int kdb_nextline = 1;
int kdb_state;
/* General KDB state */
struct task_struct *kdb_current_task;
EXPORT_SYMBOL(kdb_current_task);
struct pt_regs *kdb_current_regs;
const char *kdb_diemsg;
static int kdb_go_count;
#ifdef CONFIG_KDB_CONTINUE_CATASTROPHIC
static unsigned int kdb_continue_catastrophic =
CONFIG_KDB_CONTINUE_CATASTROPHIC;
#else
static unsigned int kdb_continue_catastrophic;
#endif
/* kdb_commands describes the available commands. */
static kdbtab_t *kdb_commands;
#define KDB_BASE_CMD_MAX 50
static int kdb_max_commands = KDB_BASE_CMD_MAX;
static kdbtab_t kdb_base_commands[KDB_BASE_CMD_MAX];
#define for_each_kdbcmd(cmd, num) \
for ((cmd) = kdb_base_commands, (num) = 0; \
num < kdb_max_commands; \
num++, num == KDB_BASE_CMD_MAX ? cmd = kdb_commands : cmd++)
typedef struct _kdbmsg {
int km_diag; /* kdb diagnostic */
char *km_msg; /* Corresponding message text */
} kdbmsg_t;
#define KDBMSG(msgnum, text) \
{ KDB_##msgnum, text }
static kdbmsg_t kdbmsgs[] = {
KDBMSG(NOTFOUND, "Command Not Found"),
KDBMSG(ARGCOUNT, "Improper argument count, see usage."),
KDBMSG(BADWIDTH, "Illegal value for BYTESPERWORD use 1, 2, 4 or 8, "
"8 is only allowed on 64 bit systems"),
KDBMSG(BADRADIX, "Illegal value for RADIX use 8, 10 or 16"),
KDBMSG(NOTENV, "Cannot find environment variable"),
KDBMSG(NOENVVALUE, "Environment variable should have value"),
KDBMSG(NOTIMP, "Command not implemented"),
KDBMSG(ENVFULL, "Environment full"),
KDBMSG(ENVBUFFULL, "Environment buffer full"),
KDBMSG(TOOMANYBPT, "Too many breakpoints defined"),
#ifdef CONFIG_CPU_XSCALE
KDBMSG(TOOMANYDBREGS, "More breakpoints than ibcr registers defined"),
#else
KDBMSG(TOOMANYDBREGS, "More breakpoints than db registers defined"),
#endif
KDBMSG(DUPBPT, "Duplicate breakpoint address"),
KDBMSG(BPTNOTFOUND, "Breakpoint not found"),
KDBMSG(BADMODE, "Invalid IDMODE"),
KDBMSG(BADINT, "Illegal numeric value"),
KDBMSG(INVADDRFMT, "Invalid symbolic address format"),
KDBMSG(BADREG, "Invalid register name"),
KDBMSG(BADCPUNUM, "Invalid cpu number"),
KDBMSG(BADLENGTH, "Invalid length field"),
KDBMSG(NOBP, "No Breakpoint exists"),
KDBMSG(BADADDR, "Invalid address"),
KDBMSG(NOPERM, "Permission denied"),
};
#undef KDBMSG
static const int __nkdb_err = ARRAY_SIZE(kdbmsgs);
/*
* Initial environment. This is all kept static and local to
* this file. We don't want to rely on the memory allocation
* mechanisms in the kernel, so we use a very limited allocate-only
* heap for new and altered environment variables. The entire
* environment is limited to a fixed number of entries (add more
* to __env[] if required) and a fixed amount of heap (add more to
* KDB_ENVBUFSIZE if required).
*/
static char *__env[] = {
#if defined(CONFIG_SMP)
"PROMPT=[%d]kdb> ",
#else
"PROMPT=kdb> ",
#endif
"MOREPROMPT=more> ",
"RADIX=16",
"MDCOUNT=8", /* lines of md output */
KDB_PLATFORM_ENV,
"DTABCOUNT=30",
"NOSECT=1",
(char *)0,
(char *)0,
(char *)0,
(char *)0,
(char *)0,
(char *)0,
(char *)0,
(char *)0,
(char *)0,
(char *)0,
(char *)0,
(char *)0,
(char *)0,
(char *)0,
(char *)0,
(char *)0,
(char *)0,
(char *)0,
(char *)0,
(char *)0,
(char *)0,
(char *)0,
(char *)0,
(char *)0,
};
static const int __nenv = ARRAY_SIZE(__env);
struct task_struct *kdb_curr_task(int cpu)
{
struct task_struct *p = curr_task(cpu);
#ifdef _TIF_MCA_INIT
if ((task_thread_info(p)->flags & _TIF_MCA_INIT) && KDB_TSK(cpu))
p = krp->p;
#endif
return p;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jason Wessel | 52 | 100.00% | 1 | 100.00% |
Total | 52 | 100.00% | 1 | 100.00% |
/*
* Check whether the flags of the current command and the permissions
* of the kdb console has allow a command to be run.
*/
static inline bool kdb_check_flags(kdb_cmdflags_t flags, int permissions,
bool no_args)
{
/* permissions comes from userspace so needs massaging slightly */
permissions &= KDB_ENABLE_MASK;
permissions |= KDB_ENABLE_ALWAYS_SAFE;
/* some commands change group when launched with no arguments */
if (no_args)
permissions |= permissions << KDB_ENABLE_NO_ARGS_SHIFT;
flags |= KDB_ENABLE_ALL;
return permissions & flags;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Daniel R Thompson | 45 | 100.00% | 1 | 100.00% |
Total | 45 | 100.00% | 1 | 100.00% |
/*
* kdbgetenv - This function will return the character string value of
* an environment variable.
* Parameters:
* match A character string representing an environment variable.
* Returns:
* NULL No environment variable matches 'match'
* char* Pointer to string value of environment variable.
*/
char *kdbgetenv(const char *match)
{
char **ep = __env;
int matchlen = strlen(match);
int i;
for (i = 0; i < __nenv; i++) {
char *e = *ep++;
if (!e)
continue;
if ((strncmp(match, e, matchlen) == 0)
&& ((e[matchlen] == '\0')
|| (e[matchlen] == '='))) {
char *cp = strchr(e, '=');
return cp ? ++cp : "";
}
}
return NULL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jason Wessel | 117 | 100.00% | 1 | 100.00% |
Total | 117 | 100.00% | 1 | 100.00% |
/*
* kdballocenv - This function is used to allocate bytes for
* environment entries.
* Parameters:
* match A character string representing a numeric value
* Outputs:
* *value the unsigned long representation of the env variable 'match'
* Returns:
* Zero on success, a kdb diagnostic on failure.
* Remarks:
* We use a static environment buffer (envbuffer) to hold the values
* of dynamically generated environment variables (see kdb_set). Buffer
* space once allocated is never free'd, so over time, the amount of space
* (currently 512 bytes) will be exhausted if env variables are changed
* frequently.
*/
static char *kdballocenv(size_t bytes)
{
#define KDB_ENVBUFSIZE 512
static char envbuffer[KDB_ENVBUFSIZE];
static int envbufsize;
char *ep = NULL;
if ((KDB_ENVBUFSIZE - envbufsize) >= bytes) {
ep = &envbuffer[envbufsize];
envbufsize += bytes;
}
return ep;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jason Wessel | 58 | 100.00% | 1 | 100.00% |
Total | 58 | 100.00% | 1 | 100.00% |
/*
* kdbgetulenv - This function will return the value of an unsigned
* long-valued environment variable.
* Parameters:
* match A character string representing a numeric value
* Outputs:
* *value the unsigned long represntation of the env variable 'match'
* Returns:
* Zero on success, a kdb diagnostic on failure.
*/
static int kdbgetulenv(const char *match, unsigned long *value)
{
char *ep;
ep = kdbgetenv(match);
if (!ep)
return KDB_NOTENV;
if (strlen(ep) == 0)
return KDB_NOENVVALUE;
*value = simple_strtoul(ep, NULL, 0);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jason Wessel | 62 | 100.00% | 1 | 100.00% |
Total | 62 | 100.00% | 1 | 100.00% |
/*
* kdbgetintenv - This function will return the value of an
* integer-valued environment variable.
* Parameters:
* match A character string representing an integer-valued env variable
* Outputs:
* *value the integer representation of the environment variable 'match'
* Returns:
* Zero on success, a kdb diagnostic on failure.
*/
int kdbgetintenv(const char *match, int *value)
{
unsigned long val;
int diag;
diag = kdbgetulenv(match, &val);
if (!diag)
*value = (int) val;
return diag;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jason Wessel | 47 | 100.00% | 1 | 100.00% |
Total | 47 | 100.00% | 1 | 100.00% |
/*
* kdbgetularg - This function will convert a numeric string into an
* unsigned long value.
* Parameters:
* arg A character string representing a numeric value
* Outputs:
* *value the unsigned long represntation of arg.
* Returns:
* Zero on success, a kdb diagnostic on failure.
*/
int kdbgetularg(const char *arg, unsigned long *value)
{
char *endp;
unsigned long val;
val = simple_strtoul(arg, &endp, 0);
if (endp == arg) {
/*
* Also try base 16, for us folks too lazy to type the
* leading 0x...
*/
val = simple_strtoul(arg, &endp, 16);
if (endp == arg)
return KDB_BADINT;
}
*value = val;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jason Wessel | 73 | 100.00% | 2 | 100.00% |
Total | 73 | 100.00% | 2 | 100.00% |
int kdbgetu64arg(const char *arg, u64 *value)
{
char *endp;
u64 val;
val = simple_strtoull(arg, &endp, 0);
if (endp == arg) {
val = simple_strtoull(arg, &endp, 16);
if (endp == arg)
return KDB_BADINT;
}
*value = val;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jason Wessel | 70 | 100.00% | 1 | 100.00% |
Total | 70 | 100.00% | 1 | 100.00% |
/*
* kdb_set - This function implements the 'set' command. Alter an
* existing environment variable or create a new one.
*/
int kdb_set(int argc, const char **argv)
{
int i;
char *ep;
size_t varlen, vallen;
/*
* we can be invoked two ways:
* set var=value argv[1]="var", argv[2]="value"
* set var = value argv[1]="var", argv[2]="=", argv[3]="value"
* - if the latter, shift 'em down.
*/
if (argc == 3) {
argv[2] = argv[3];
argc--;
}
if (argc != 2)
return KDB_ARGCOUNT;
/*
* Check for internal variables
*/
if (strcmp(argv[1], "KDBDEBUG") == 0) {
unsigned int debugflags;
char *cp;
debugflags = simple_strtoul(argv[2], &cp, 0);
if (cp == argv[2] || debugflags & ~KDB_DEBUG_FLAG_MASK) {
kdb_printf("kdb: illegal debug flags '%s'\n",
argv[2]);
return 0;
}
kdb_flags = (kdb_flags &
~(KDB_DEBUG_FLAG_MASK << KDB_DEBUG_FLAG_SHIFT))
| (debugflags << KDB_DEBUG_FLAG_SHIFT);
return 0;
}
/*
* Tokenizer squashed the '=' sign. argv[1] is variable
* name, argv[2] = value.
*/
varlen = strlen(argv[1]);
vallen = strlen(argv[2]);
ep = kdballocenv(varlen + vallen + 2);
if (ep == (char *)0)
return KDB_ENVBUFFULL;
sprintf(ep, "%s=%s", argv[1], argv[2]);
ep[varlen+vallen+1] = '\0';
for (i = 0; i < __nenv; i++) {
if (__env[i]
&& ((strncmp(__env[i], argv[1], varlen) == 0)
&& ((__env[i][varlen] == '\0')
|| (__env[i][varlen] == '=')))) {
__env[i] = ep;
return 0;
}
}
/*
* Wasn't existing variable. Fit into slot.
*/
for (i = 0; i < __nenv-1; i++) {
if (__env[i] == (char *)0) {
__env[i] = ep;
return 0;
}
}
return KDB_ENVFULL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jason Wessel | 348 | 100.00% | 1 | 100.00% |
Total | 348 | 100.00% | 1 | 100.00% |
static int kdb_check_regs(void)
{
if (!kdb_current_regs) {
kdb_printf("No current kdb registers."
" You may need to select another task\n");
return KDB_BADREG;
}
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jason Wessel | 27 | 100.00% | 1 | 100.00% |
Total | 27 | 100.00% | 1 | 100.00% |
/*
* kdbgetaddrarg - This function is responsible for parsing an
* address-expression and returning the value of the expression,
* symbol name, and offset to the caller.
*
* The argument may consist of a numeric value (decimal or
* hexidecimal), a symbol name, a register name (preceded by the
* percent sign), an environment variable with a numeric value
* (preceded by a dollar sign) or a simple arithmetic expression
* consisting of a symbol name, +/-, and a numeric constant value
* (offset).
* Parameters:
* argc - count of arguments in argv
* argv - argument vector
* *nextarg - index to next unparsed argument in argv[]
* regs - Register state at time of KDB entry
* Outputs:
* *value - receives the value of the address-expression
* *offset - receives the offset specified, if any
* *name - receives the symbol name, if any
* *nextarg - index to next unparsed argument in argv[]
* Returns:
* zero is returned on success, a kdb diagnostic code is
* returned on error.
*/
int kdbgetaddrarg(int argc, const char **argv, int *nextarg,
unsigned long *value, long *offset,
char **name)
{
unsigned long addr;
unsigned long off = 0;
int positive;
int diag;
int found = 0;
char *symname;
char symbol = '\0';
char *cp;
kdb_symtab_t symtab;
/*
* If the enable flags prohibit both arbitrary memory access
* and flow control then there are no reasonable grounds to
* provide symbol lookup.
*/
if (!kdb_check_flags(KDB_ENABLE_MEM_READ | KDB_ENABLE_FLOW_CTRL,
kdb_cmd_enabled, false))
return KDB_NOPERM;
/*
* Process arguments which follow the following syntax:
*
* symbol | numeric-address [+/- numeric-offset]
* %register
* $environment-variable
*/
if (*nextarg > argc)
return KDB_ARGCOUNT;
symname = (char *)argv[*nextarg];
/*
* If there is no whitespace between the symbol
* or address and the '+' or '-' symbols, we
* remember the character and replace it with a
* null so the symbol/value can be properly parsed
*/
cp = strpbrk(symname, "+-");
if (cp != NULL) {
symbol = *cp;
*cp++ = '\0';
}
if (symname[0] == '$') {
diag = kdbgetulenv(&symname[1], &addr);
if (diag)
return diag;
} else if (symname[0] == '%') {
diag = kdb_check_regs();
if (diag)
return diag;
/* Implement register values with % at a later time as it is
* arch optional.
*/
return KDB_NOTIMP;
} else {
found = kdbgetsymval(symname, &symtab);
if (found) {
addr = symtab.sym_start;
} else {
diag = kdbgetularg(argv[*nextarg], &addr);
if (diag)
return diag;
}
}
if (!found)
found = kdbnearsym(addr, &symtab);
(*nextarg)++;
if (name)
*name = symname;
if (value)
*value = addr;
if (offset && name && *name)
*offset = addr - symtab.sym_start;
if ((*nextarg > argc)
&& (symbol == '\0'))
return 0;
/*
* check for +/- and offset
*/
if (symbol == '\0') {
if ((argv[*nextarg][0] != '+')
&& (argv[*nextarg][0] != '-')) {
/*
* Not our argument. Return.
*/
return 0;
} else {
positive = (argv[*nextarg][0] == '+');
(*nextarg)++;
}
} else
positive = (symbol == '+');
/*
* Now there must be an offset!
*/
if ((*nextarg > argc)
&& (symbol == '\0')) {
return KDB_INVADDRFMT;
}
if (!symbol) {
cp = (char *)argv[*nextarg];
(*nextarg)++;
}
diag = kdbgetularg(cp, &off);
if (diag)
return diag;
if (!positive)
off = -off;
if (offset)
*offset += off;
if (value)
*value += off;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jason Wessel | 475 | 96.35% | 1 | 50.00% |
Anton Vorontsov | 18 | 3.65% | 1 | 50.00% |
Total | 493 | 100.00% | 2 | 100.00% |
static void kdb_cmderror(int diag)
{
int i;
if (diag >= 0) {
kdb_printf("no error detected (diagnostic is %d)\n", diag);
return;
}
for (i = 0; i < __nkdb_err; i++) {
if (kdbmsgs[i].km_diag == diag) {
kdb_printf("diag: %d: %s\n", diag, kdbmsgs[i].km_msg);
return;
}
}
kdb_printf("Unknown diag %d\n", -diag);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jason Wessel | 79 | 100.00% | 1 | 100.00% |
Total | 79 | 100.00% | 1 | 100.00% |
/*
* kdb_defcmd, kdb_defcmd2 - This function implements the 'defcmd'
* command which defines one command as a set of other commands,
* terminated by endefcmd. kdb_defcmd processes the initial
* 'defcmd' command, kdb_defcmd2 is invoked from kdb_parse for
* the following commands until 'endefcmd'.
* Inputs:
* argc argument count
* argv argument vector
* Returns:
* zero for success, a kdb diagnostic if error
*/
struct defcmd_set {
int count;
int usable;
char *name;
char *usage;
char *help;
char **command;
};
static struct defcmd_set *defcmd_set;
static int defcmd_set_count;
static int defcmd_in_progress;
/* Forward references */
static int kdb_exec_defcmd(int argc, const char **argv);
static int kdb_defcmd2(const char *cmdstr, const char *argv0)
{
struct defcmd_set *s = defcmd_set + defcmd_set_count - 1;
char **save_command = s->command;
if (strcmp(argv0, "endefcmd") == 0) {
defcmd_in_progress = 0;
if (!s->count)
s->usable = 0;
if (s->usable)
/* macros are always safe because when executed each
* internal command re-enters kdb_parse() and is
* safety checked individually.
*/
kdb_register_flags(s->name, kdb_exec_defcmd, s->usage,
s->help, 0,
KDB_ENABLE_ALWAYS_SAFE);
return 0;
}
if (!s->usable)
return KDB_NOTIMP;
s->command = kzalloc((s->count + 1) * sizeof(*(s->command)), GFP_KDB);
if (!s->command) {
kdb_printf("Could not allocate new kdb_defcmd table for %s\n",
cmdstr);
s->usable = 0;
return KDB_NOTIMP;
}
memcpy(s->command, save_command, s->count * sizeof(*(s->command)));
s->command[s->count++] = kdb_strdup(cmdstr, GFP_KDB);
kfree(save_command);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jason Wessel | 202 | 97.58% | 1 | 33.33% |
Daniel R Thompson | 4 | 1.93% | 1 | 33.33% |
Jovi Zhang | 1 | 0.48% | 1 | 33.33% |
Total | 207 | 100.00% | 3 | 100.00% |
static int kdb_defcmd(int argc, const char **argv)
{
struct defcmd_set *save_defcmd_set = defcmd_set, *s;
if (defcmd_in_progress) {
kdb_printf("kdb: nested defcmd detected, assuming missing "
"endefcmd\n");
kdb_defcmd2("endefcmd", "endefcmd");
}
if (argc == 0) {
int i;
for (s = defcmd_set; s < defcmd_set + defcmd_set_count; ++s) {
kdb_printf("defcmd %s \"%s\" \"%s\"\n", s->name,
s->usage, s->help);
for (i = 0; i < s->count; ++i)
kdb_printf("%s", s->command[i]);
kdb_printf("endefcmd\n");
}
return 0;
}
if (argc != 3)
return KDB_ARGCOUNT;
if (in_dbg_master()) {
kdb_printf("Command only available during kdb_init()\n");
return KDB_NOTIMP;
}
defcmd_set = kmalloc((defcmd_set_count + 1) * sizeof(*defcmd_set),
GFP_KDB);
if (!defcmd_set)
goto fail_defcmd;
memcpy(defcmd_set, save_defcmd_set,
defcmd_set_count * sizeof(*defcmd_set));
s = defcmd_set + defcmd_set_count;
memset(s, 0, sizeof(*s));
s->usable = 1;
s->name = kdb_strdup(argv[1], GFP_KDB);
if (!s->name)
goto fail_name;
s->usage = kdb_strdup(argv[2], GFP_KDB);
if (!s->usage)
goto fail_usage;
s->help = kdb_strdup(argv[3], GFP_KDB);
if (!s->help)
goto fail_help;
if (s->usage[0] == '"') {
strcpy(s->usage, argv[2]+1);
s->usage[strlen(s->usage)-1] = '\0';
}
if (s->help[0] == '"') {
strcpy(s->help, argv[3]+1);
s->help[strlen(s->help)-1] = '\0';
}
++defcmd_set_count;
defcmd_in_progress = 1;
kfree(save_defcmd_set);
return 0;
fail_help:
kfree(s->usage);
fail_usage:
kfree(s->name);
fail_name:
kfree(defcmd_set);
fail_defcmd:
kdb_printf("Could not allocate new defcmd_set entry for %s\n", argv[1]);
defcmd_set = save_defcmd_set;
return KDB_NOTIMP;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jason Wessel | 432 | 100.00% | 3 | 100.00% |
Total | 432 | 100.00% | 3 | 100.00% |
/*
* kdb_exec_defcmd - Execute the set of commands associated with this
* defcmd name.
* Inputs:
* argc argument count
* argv argument vector
* Returns:
* zero for success, a kdb diagnostic if error
*/
static int kdb_exec_defcmd(int argc, const char **argv)
{
int i, ret;
struct defcmd_set *s;
if (argc != 0)
return KDB_ARGCOUNT;
for (s = defcmd_set, i = 0; i < defcmd_set_count; ++i