The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/ddb/db_command.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: MIT-CMU
    3  *
    4  * Mach Operating System
    5  * Copyright (c) 1991,1990 Carnegie Mellon University
    6  * All Rights Reserved.
    7  *
    8  * Permission to use, copy, modify and distribute this software and its
    9  * documentation is hereby granted, provided that both the copyright
   10  * notice and this permission notice appear in all copies of the
   11  * software, derivative works or modified versions, and any portions
   12  * thereof, and that both notices appear in supporting documentation.
   13  *
   14  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
   15  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
   16  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
   17  *
   18  * Carnegie Mellon requests users of this software to return to
   19  *
   20  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
   21  *  School of Computer Science
   22  *  Carnegie Mellon University
   23  *  Pittsburgh PA 15213-3890
   24  *
   25  * any improvements or extensions that they make and grant Carnegie the
   26  * rights to redistribute these changes.
   27  */
   28 /*
   29  *      Author: David B. Golub, Carnegie Mellon University
   30  *      Date:   7/90
   31  */
   32 /*
   33  * Command dispatcher.
   34  */
   35 
   36 #include <sys/cdefs.h>
   37 __FBSDID("$FreeBSD$");
   38 
   39 #include <sys/param.h>
   40 #include <sys/conf.h>
   41 #include <sys/cons.h>
   42 #include <sys/eventhandler.h>
   43 #include <sys/kdb.h>
   44 #include <sys/kernel.h>
   45 #include <sys/linker_set.h>
   46 #include <sys/lock.h>
   47 #include <sys/mutex.h>
   48 #include <sys/proc.h>
   49 #include <sys/reboot.h>
   50 #include <sys/signalvar.h>
   51 #include <sys/systm.h>
   52 #include <sys/watchdog.h>
   53 
   54 #include <ddb/ddb.h>
   55 #include <ddb/db_command.h>
   56 #include <ddb/db_lex.h>
   57 #include <ddb/db_output.h>
   58 
   59 #include <machine/cpu.h>
   60 #include <machine/setjmp.h>
   61 
   62 #include <security/mac/mac_framework.h>
   63 
   64 /*
   65  * Exported global variables
   66  */
   67 int             db_cmd_loop_done;
   68 db_addr_t       db_dot;
   69 db_addr_t       db_last_addr;
   70 db_addr_t       db_prev;
   71 db_addr_t       db_next;
   72 
   73 static db_cmdfcn_t      db_dump;
   74 static db_cmdfcn_t      db_fncall;
   75 static db_cmdfcn_t      db_gdb;
   76 static db_cmdfcn_t      db_halt;
   77 static db_cmdfcn_t      db_kill;
   78 static db_cmdfcn_t      db_reset;
   79 static db_cmdfcn_t      db_stack_trace;
   80 static db_cmdfcn_t      db_stack_trace_active;
   81 static db_cmdfcn_t      db_stack_trace_all;
   82 static db_cmdfcn_t      db_watchdog;
   83 
   84 #define DB_CMD(_name, _func, _flags)    \
   85 {                                       \
   86         .name = (_name),                \
   87         .fcn =  (_func),                \
   88         .flag = (_flags),               \
   89         .more = NULL,                   \
   90 }
   91 #define DB_TABLE(_name, _more)          \
   92 {                                       \
   93         .name = (_name),                \
   94         .fcn =  NULL,                   \
   95         .more = (_more),                \
   96 }
   97 
   98 static struct db_command db_show_active_cmds[] = {
   99         DB_CMD("trace",         db_stack_trace_active,  DB_CMD_MEMSAFE),
  100 };
  101 struct db_command_table db_show_active_table =
  102     LIST_HEAD_INITIALIZER(db_show_active_table);
  103 
  104 static struct db_command db_show_all_cmds[] = {
  105         DB_CMD("trace",         db_stack_trace_all,     DB_CMD_MEMSAFE),
  106 };
  107 struct db_command_table db_show_all_table =
  108     LIST_HEAD_INITIALIZER(db_show_all_table);
  109 
  110 static struct db_command db_show_cmds[] = {
  111         DB_TABLE("active",      &db_show_active_table),
  112         DB_TABLE("all",         &db_show_all_table),
  113         DB_CMD("registers",     db_show_regs,           DB_CMD_MEMSAFE),
  114         DB_CMD("breaks",        db_listbreak_cmd,       DB_CMD_MEMSAFE),
  115         DB_CMD("threads",       db_show_threads,        DB_CMD_MEMSAFE),
  116 };
  117 struct db_command_table db_show_table = LIST_HEAD_INITIALIZER(db_show_table);
  118 
  119 static struct db_command db_cmds[] = {
  120         DB_TABLE("show",        &db_show_table),
  121         DB_CMD("print",         db_print_cmd,           0),
  122         DB_CMD("p",             db_print_cmd,           0),
  123         DB_CMD("examine",       db_examine_cmd,         CS_SET_DOT),
  124         DB_CMD("x",             db_examine_cmd,         CS_SET_DOT),
  125         DB_CMD("search",        db_search_cmd,          CS_OWN|CS_SET_DOT),
  126         DB_CMD("set",           db_set_cmd,             CS_OWN|DB_CMD_MEMSAFE),
  127         DB_CMD("write",         db_write_cmd,           CS_MORE|CS_SET_DOT),
  128         DB_CMD("w",             db_write_cmd,           CS_MORE|CS_SET_DOT),
  129         DB_CMD("delete",        db_delete_cmd,          0),
  130         DB_CMD("d",             db_delete_cmd,          0),
  131         DB_CMD("dump",          db_dump,                DB_CMD_MEMSAFE),
  132         DB_CMD("break",         db_breakpoint_cmd,      0),
  133         DB_CMD("b",             db_breakpoint_cmd,      0),
  134         DB_CMD("dwatch",        db_deletewatch_cmd,     0),
  135         DB_CMD("watch",         db_watchpoint_cmd,      CS_MORE),
  136         DB_CMD("dhwatch",       db_deletehwatch_cmd,    0),
  137         DB_CMD("hwatch",        db_hwatchpoint_cmd,     0),
  138         DB_CMD("step",          db_single_step_cmd,     DB_CMD_MEMSAFE),
  139         DB_CMD("s",             db_single_step_cmd,     DB_CMD_MEMSAFE),
  140         DB_CMD("continue",      db_continue_cmd,        DB_CMD_MEMSAFE),
  141         DB_CMD("c",             db_continue_cmd,        DB_CMD_MEMSAFE),
  142         DB_CMD("until",         db_trace_until_call_cmd, DB_CMD_MEMSAFE),
  143         DB_CMD("next",          db_trace_until_matching_cmd, DB_CMD_MEMSAFE),
  144         DB_CMD("match",         db_trace_until_matching_cmd, 0),
  145         DB_CMD("trace",         db_stack_trace,         CS_OWN|DB_CMD_MEMSAFE),
  146         DB_CMD("t",             db_stack_trace,         CS_OWN|DB_CMD_MEMSAFE),
  147         /* XXX alias for active trace */
  148         DB_CMD("acttrace",      db_stack_trace_active,  DB_CMD_MEMSAFE),
  149         /* XXX alias for all trace */
  150         DB_CMD("alltrace",      db_stack_trace_all,     DB_CMD_MEMSAFE),
  151         DB_CMD("where",         db_stack_trace,         CS_OWN|DB_CMD_MEMSAFE),
  152         DB_CMD("bt",            db_stack_trace,         CS_OWN|DB_CMD_MEMSAFE),
  153         DB_CMD("call",          db_fncall,              CS_OWN),
  154         DB_CMD("ps",            db_ps,                  DB_CMD_MEMSAFE),
  155         DB_CMD("gdb",           db_gdb,                 0),
  156         DB_CMD("halt",          db_halt,                DB_CMD_MEMSAFE),
  157         DB_CMD("reboot",        db_reset,               DB_CMD_MEMSAFE),
  158         DB_CMD("reset",         db_reset,               DB_CMD_MEMSAFE),
  159         DB_CMD("kill",          db_kill,                CS_OWN|DB_CMD_MEMSAFE),
  160         DB_CMD("watchdog",      db_watchdog,            CS_OWN|DB_CMD_MEMSAFE),
  161         DB_CMD("thread",        db_set_thread,          0),
  162         DB_CMD("run",           db_run_cmd,             CS_OWN|DB_CMD_MEMSAFE),
  163         DB_CMD("script",        db_script_cmd,          CS_OWN|DB_CMD_MEMSAFE),
  164         DB_CMD("scripts",       db_scripts_cmd,         DB_CMD_MEMSAFE),
  165         DB_CMD("unscript",      db_unscript_cmd,        CS_OWN|DB_CMD_MEMSAFE),
  166         DB_CMD("capture",       db_capture_cmd,         CS_OWN|DB_CMD_MEMSAFE),
  167         DB_CMD("textdump",      db_textdump_cmd,        CS_OWN|DB_CMD_MEMSAFE),
  168         DB_CMD("findstack",     db_findstack_cmd,       0),
  169 };
  170 struct db_command_table db_cmd_table = LIST_HEAD_INITIALIZER(db_cmd_table);
  171 
  172 #undef DB_CMD
  173 #undef DB_TABLE
  174 
  175 static struct db_command *db_last_command = NULL;
  176 
  177 /*
  178  * if 'ed' style: 'dot' is set at start of last item printed,
  179  * and '+' points to next line.
  180  * Otherwise: 'dot' points to next item, '..' points to last.
  181  */
  182 static bool     db_ed_style = true;
  183 
  184 /*
  185  * Utility routine - discard tokens through end-of-line.
  186  */
  187 void
  188 db_skip_to_eol(void)
  189 {
  190         int t;
  191 
  192         do {
  193                 t = db_read_token();
  194         } while (t != tEOL);
  195 }
  196 
  197 /*
  198  * Results of command search.
  199  */
  200 #define CMD_UNIQUE      0
  201 #define CMD_FOUND       1
  202 #define CMD_NONE        2
  203 #define CMD_AMBIGUOUS   3
  204 #define CMD_HELP        4
  205 
  206 static void     db_cmd_match(char *name, struct db_command *cmd,
  207                     struct db_command **cmdp, int *resultp);
  208 static void     db_cmd_list(struct db_command_table *table);
  209 static int      db_cmd_search(char *name, struct db_command_table *table,
  210                     struct db_command **cmdp);
  211 static void     db_command(struct db_command **last_cmdp,
  212                     struct db_command_table *cmd_table, bool dopager);
  213 
  214 /*
  215  * Initialize the command lists from the static tables.
  216  */
  217 void
  218 db_command_init(void)
  219 {
  220         int i;
  221 
  222         for (i = 0; i < nitems(db_cmds); i++)
  223                 db_command_register(&db_cmd_table, &db_cmds[i]);
  224         for (i = 0; i < nitems(db_show_cmds); i++)
  225                 db_command_register(&db_show_table, &db_show_cmds[i]);
  226         for (i = 0; i < nitems(db_show_active_cmds); i++)
  227                 db_command_register(&db_show_active_table,
  228                     &db_show_active_cmds[i]);
  229         for (i = 0; i < nitems(db_show_all_cmds); i++)
  230                 db_command_register(&db_show_all_table, &db_show_all_cmds[i]);
  231 }
  232 
  233 /*
  234  * Register a command.
  235  */
  236 void
  237 db_command_register(struct db_command_table *list, struct db_command *cmd)
  238 {
  239         struct db_command *c, *last;
  240 
  241 #ifdef MAC
  242         if (mac_ddb_command_register(list, cmd)) {
  243                 printf("%s: MAC policy refused registration of command %s\n",
  244                     __func__, cmd->name);
  245                 return;
  246         }
  247 #endif
  248         last = NULL;
  249         LIST_FOREACH(c, list, next) {
  250                 int n = strcmp(cmd->name, c->name);
  251 
  252                 /* Check that the command is not already present. */
  253                 if (n == 0) {
  254                         printf("%s: Warning, the command \"%s\" already exists;"
  255                              " ignoring request\n", __func__, cmd->name);
  256                         return;
  257                 }
  258                 if (n < 0) {
  259                         /* NB: keep list sorted lexicographically */
  260                         LIST_INSERT_BEFORE(c, cmd, next);
  261                         return;
  262                 }
  263                 last = c;
  264         }
  265         if (last == NULL)
  266                 LIST_INSERT_HEAD(list, cmd, next);
  267         else
  268                 LIST_INSERT_AFTER(last, cmd, next);
  269 }
  270 
  271 /*
  272  * Remove a command previously registered with db_command_register.
  273  */
  274 void
  275 db_command_unregister(struct db_command_table *list, struct db_command *cmd)
  276 {
  277         struct db_command *c;
  278 
  279         LIST_FOREACH(c, list, next) {
  280                 if (cmd == c) {
  281                         LIST_REMOVE(cmd, next);
  282                         return;
  283                 }
  284         }
  285         /* NB: intentionally quiet */
  286 }
  287 
  288 /*
  289  * Helper function to match a single command.
  290  */
  291 static void
  292 db_cmd_match(char *name, struct db_command *cmd, struct db_command **cmdp,
  293     int *resultp)
  294 {
  295         char *lp, *rp;
  296         int c;
  297 
  298         lp = name;
  299         rp = cmd->name;
  300         while ((c = *lp) == *rp) {
  301                 if (c == 0) {
  302                         /* complete match */
  303                         *cmdp = cmd;
  304                         *resultp = CMD_UNIQUE;
  305                         return;
  306                 }
  307                 lp++;
  308                 rp++;
  309         }
  310         if (c == 0) {
  311                 /* end of name, not end of command -
  312                    partial match */
  313                 if (*resultp == CMD_FOUND) {
  314                         *resultp = CMD_AMBIGUOUS;
  315                         /* but keep looking for a full match -
  316                            this lets us match single letters */
  317                 } else if (*resultp == CMD_NONE) {
  318                         *cmdp = cmd;
  319                         *resultp = CMD_FOUND;
  320                 }
  321         }
  322 }
  323 
  324 /*
  325  * Search for command prefix.
  326  */
  327 static int
  328 db_cmd_search(char *name, struct db_command_table *table,
  329     struct db_command **cmdp)
  330 {
  331         struct db_command *cmd;
  332         int result = CMD_NONE;
  333 
  334         LIST_FOREACH(cmd, table, next) {
  335                 db_cmd_match(name,cmd,cmdp,&result);
  336                 if (result == CMD_UNIQUE)
  337                         break;
  338         }
  339 
  340         if (result == CMD_NONE) {
  341                 /* check for 'help' */
  342                 if (name[0] == 'h' && name[1] == 'e'
  343                     && name[2] == 'l' && name[3] == 'p')
  344                         result = CMD_HELP;
  345         }
  346         return (result);
  347 }
  348 
  349 static void
  350 db_cmd_list(struct db_command_table *table)
  351 {
  352         struct db_command *cmd;
  353         int have_subcommands;
  354 
  355         have_subcommands = 0;
  356         LIST_FOREACH(cmd, table, next) {
  357                 if (cmd->more != NULL)
  358                         have_subcommands++;
  359                 db_printf("%-16s", cmd->name);
  360                 db_end_line(16);
  361         }
  362 
  363         if (have_subcommands > 0) {
  364                 db_printf("\nThe following have subcommands; append \"help\" "
  365                     "to list (e.g. \"show help\"):\n");
  366                 LIST_FOREACH(cmd, table, next) {
  367                         if (cmd->more == NULL)
  368                                 continue;
  369                         db_printf("%-16s", cmd->name);
  370                         db_end_line(16);
  371                 }
  372         }
  373 }
  374 
  375 static void
  376 db_command(struct db_command **last_cmdp, struct db_command_table *cmd_table,
  377     bool dopager)
  378 {
  379         char modif[TOK_STRING_SIZE];
  380         struct db_command *cmd = NULL;
  381         db_expr_t addr, count;
  382         int t, result;
  383         bool have_addr = false;
  384 
  385         t = db_read_token();
  386         if (t == tEOL) {
  387                 /* empty line repeats last command, at 'next' */
  388                 cmd = *last_cmdp;
  389                 addr = (db_expr_t)db_next;
  390                 have_addr = false;
  391                 count = 1;
  392                 modif[0] = '\0';
  393         } else if (t == tEXCL) {
  394                 db_fncall((db_expr_t)0, false, (db_expr_t)0, NULL);
  395                 return;
  396         } else if (t != tIDENT) {
  397                 db_printf("Unrecognized input; use \"help\" "
  398                     "to list available commands\n");
  399                 db_flush_lex();
  400                 return;
  401         } else {
  402                 /*
  403                  * Search for command
  404                  */
  405                 while (cmd_table != NULL) {
  406                         result = db_cmd_search(db_tok_string, cmd_table, &cmd);
  407                         switch (result) {
  408                         case CMD_NONE:
  409                                 db_printf("No such command; use \"help\" "
  410                                     "to list available commands\n");
  411                                 db_flush_lex();
  412                                 return;
  413                         case CMD_AMBIGUOUS:
  414                                 db_printf("Ambiguous\n");
  415                                 db_flush_lex();
  416                                 return;
  417                         case CMD_HELP:
  418                                 if (cmd_table == &db_cmd_table) {
  419                                         db_printf("This is ddb(4), the kernel debugger; "
  420                                             "see https://man.FreeBSD.org/ddb/4 for help.\n");
  421                                         db_printf("Use \"bt\" for backtrace, \"dump\" for "
  422                                             "kernel core dump, \"reset\" to reboot.\n");
  423                                         db_printf("Available commands:\n");
  424                                 }
  425                                 db_cmd_list(cmd_table);
  426                                 db_flush_lex();
  427                                 return;
  428                         case CMD_UNIQUE:
  429                         case CMD_FOUND:
  430                                 break;
  431                         }
  432                         if ((cmd_table = cmd->more) != NULL) {
  433                                 t = db_read_token();
  434                                 if (t != tIDENT) {
  435                                         db_printf("Subcommand required; "
  436                                             "available subcommands:\n");
  437                                         db_cmd_list(cmd_table);
  438                                         db_flush_lex();
  439                                         return;
  440                                 }
  441                         }
  442                 }
  443 
  444                 if ((cmd->flag & CS_OWN) == 0) {
  445                         /*
  446                          * Standard syntax:
  447                          * command [/modifier] [addr] [,count]
  448                          */
  449                         t = db_read_token();
  450                         if (t == tSLASH) {
  451                                 t = db_read_token();
  452                                 if (t != tIDENT) {
  453                                         db_printf("Bad modifier\n");
  454                                         db_flush_lex();
  455                                         return;
  456                                 }
  457                                 db_strcpy(modif, db_tok_string);
  458                         } else {
  459                                 db_unread_token(t);
  460                                 modif[0] = '\0';
  461                         }
  462 
  463                         if (db_expression(&addr)) {
  464                                 db_dot = (db_addr_t) addr;
  465                                 db_last_addr = db_dot;
  466                                 have_addr = true;
  467                         } else {
  468                                 addr = (db_expr_t) db_dot;
  469                                 have_addr = false;
  470                         }
  471 
  472                         t = db_read_token();
  473                         if (t == tCOMMA) {
  474                                 if (!db_expression(&count)) {
  475                                         db_printf("Count missing\n");
  476                                         db_flush_lex();
  477                                         return;
  478                                 }
  479                         } else {
  480                                 db_unread_token(t);
  481                                 count = -1;
  482                         }
  483 
  484                         if ((cmd->flag & CS_MORE) == 0) {
  485                                 db_skip_to_eol();
  486                         }
  487                 }
  488         }
  489 
  490         *last_cmdp = cmd;
  491         if (cmd != NULL) {
  492 #ifdef MAC
  493                 if (mac_ddb_command_exec(cmd, addr, have_addr, count, modif)) {
  494                         db_printf("MAC prevented execution of command %s\n",
  495                             cmd->name);
  496                         return;
  497                 }
  498 #endif
  499                 /*
  500                  * Execute the command.
  501                  */
  502                 if (dopager)
  503                         db_enable_pager();
  504                 else
  505                         db_disable_pager();
  506                 (*cmd->fcn)(addr, have_addr, count, modif);
  507                 if (dopager)
  508                         db_disable_pager();
  509 
  510                 if (cmd->flag & CS_SET_DOT) {
  511                         /*
  512                          * If command changes dot, set dot to previous address
  513                          * displayed (if 'ed' style).
  514                          */
  515                         db_dot = db_ed_style ? db_prev : db_next;
  516                 } else {
  517                         /*
  518                          * If command does not change dot, set 'next' location
  519                          * to be the same.
  520                          */
  521                         db_next = db_dot;
  522                 }
  523         }
  524 }
  525 
  526 /*
  527  * At least one non-optional command must be implemented using
  528  * DB_COMMAND() so that db_cmd_set gets created.  Here is one.
  529  */
  530 DB_COMMAND_FLAGS(panic, db_panic, DB_CMD_MEMSAFE)
  531 {
  532         db_disable_pager();
  533         panic("from debugger");
  534 }
  535 
  536 void
  537 db_command_loop(void)
  538 {
  539         /*
  540          * Initialize 'prev' and 'next' to dot.
  541          */
  542         db_prev = db_dot;
  543         db_next = db_dot;
  544 
  545         db_cmd_loop_done = 0;
  546         while (!db_cmd_loop_done) {
  547                 if (db_print_position() != 0)
  548                         db_printf("\n");
  549 
  550                 db_printf("db> ");
  551                 (void)db_read_line();
  552 
  553                 db_command(&db_last_command, &db_cmd_table, /* dopager */ true);
  554         }
  555 }
  556 
  557 /*
  558  * Execute a command on behalf of a script.  The caller is responsible for
  559  * making sure that the command string is < DB_MAXLINE or it will be
  560  * truncated.
  561  *
  562  * XXXRW: Runs by injecting faked input into DDB input stream; it would be
  563  * nicer to use an alternative approach that didn't mess with the previous
  564  * command buffer.
  565  */
  566 void
  567 db_command_script(const char *command)
  568 {
  569         db_prev = db_next = db_dot;
  570         db_inject_line(command);
  571         db_command(&db_last_command, &db_cmd_table, /* dopager */ false);
  572 }
  573 
  574 void
  575 db_error(const char *s)
  576 {
  577         if (s)
  578             db_printf("%s", s);
  579         db_flush_lex();
  580         kdb_reenter_silent();
  581 }
  582 
  583 static void
  584 db_dump(db_expr_t dummy, bool dummy2, db_expr_t dummy3, char *dummy4)
  585 {
  586         int error;
  587 
  588         if (textdump_pending) {
  589                 db_printf("textdump_pending set.\n"
  590                     "run \"textdump unset\" first or \"textdump dump\" for a textdump.\n");
  591                 return;
  592         }
  593         error = doadump(false);
  594         if (error) {
  595                 db_printf("Cannot dump: ");
  596                 switch (error) {
  597                 case EBUSY:
  598                         db_printf("debugger got invoked while dumping.\n");
  599                         break;
  600                 case ENXIO:
  601                         db_printf("no dump device specified.\n");
  602                         break;
  603                 default:
  604                         db_printf("unknown error (error=%d).\n", error);
  605                         break;
  606                 }
  607         }
  608 }
  609 
  610 /*
  611  * Call random function:
  612  * !expr(arg,arg,arg)
  613  */
  614 
  615 /* The generic implementation supports a maximum of 10 arguments. */
  616 typedef db_expr_t __db_f(db_expr_t, db_expr_t, db_expr_t, db_expr_t,
  617     db_expr_t, db_expr_t, db_expr_t, db_expr_t, db_expr_t, db_expr_t);
  618 
  619 static __inline int
  620 db_fncall_generic(db_expr_t addr, db_expr_t *rv, int nargs, db_expr_t args[])
  621 {
  622         __db_f *f = (__db_f *)addr;
  623 
  624         if (nargs > 10) {
  625                 db_printf("Too many arguments (max 10)\n");
  626                 return (0);
  627         }
  628         *rv = (*f)(args[0], args[1], args[2], args[3], args[4], args[5],
  629             args[6], args[7], args[8], args[9]);
  630         return (1);
  631 }
  632 
  633 static void
  634 db_fncall(db_expr_t dummy1, bool dummy2, db_expr_t dummy3, char *dummy4)
  635 {
  636         db_expr_t       fn_addr;
  637         db_expr_t       args[DB_MAXARGS];
  638         int             nargs = 0;
  639         db_expr_t       retval;
  640         int             t;
  641 
  642         if (!db_expression(&fn_addr)) {
  643             db_printf("Bad function\n");
  644             db_flush_lex();
  645             return;
  646         }
  647 
  648         t = db_read_token();
  649         if (t == tLPAREN) {
  650             if (db_expression(&args[0])) {
  651                 nargs++;
  652                 while ((t = db_read_token()) == tCOMMA) {
  653                     if (nargs == DB_MAXARGS) {
  654                         db_printf("Too many arguments (max %d)\n", DB_MAXARGS);
  655                         db_flush_lex();
  656                         return;
  657                     }
  658                     if (!db_expression(&args[nargs])) {
  659                         db_printf("Argument missing\n");
  660                         db_flush_lex();
  661                         return;
  662                     }
  663                     nargs++;
  664                 }
  665                 db_unread_token(t);
  666             }
  667             if (db_read_token() != tRPAREN) {
  668                 db_printf("Mismatched parens\n");
  669                 db_flush_lex();
  670                 return;
  671             }
  672         }
  673         db_skip_to_eol();
  674         db_disable_pager();
  675 
  676         if (DB_CALL(fn_addr, &retval, nargs, args))
  677                 db_printf("= %#lr\n", (long)retval);
  678 }
  679 
  680 static void
  681 db_halt(db_expr_t dummy, bool dummy2, db_expr_t dummy3, char *dummy4)
  682 {
  683 
  684         cpu_halt();
  685 }
  686 
  687 static void
  688 db_kill(db_expr_t dummy1, bool dummy2, db_expr_t dummy3, char *dummy4)
  689 {
  690         db_expr_t old_radix, pid, sig;
  691         struct proc *p;
  692 
  693 #define DB_ERROR(f)     do { db_printf f; db_flush_lex(); goto out; } while (0)
  694 
  695         /*
  696          * PIDs and signal numbers are typically represented in base
  697          * 10, so make that the default here.  It can, of course, be
  698          * overridden by specifying a prefix.
  699          */
  700         old_radix = db_radix;
  701         db_radix = 10;
  702         /* Retrieve arguments. */
  703         if (!db_expression(&sig))
  704                 DB_ERROR(("Missing signal number\n"));
  705         if (!db_expression(&pid))
  706                 DB_ERROR(("Missing process ID\n"));
  707         db_skip_to_eol();
  708         if (!_SIG_VALID(sig))
  709                 DB_ERROR(("Signal number out of range\n"));
  710 
  711         /*
  712          * Find the process in question.  allproc_lock is not needed
  713          * since we're in DDB.
  714          */
  715         /* sx_slock(&allproc_lock); */
  716         FOREACH_PROC_IN_SYSTEM(p)
  717             if (p->p_pid == pid)
  718                     break;
  719         /* sx_sunlock(&allproc_lock); */
  720         if (p == NULL)
  721                 DB_ERROR(("Can't find process with pid %ld\n", (long) pid));
  722 
  723         /* If it's already locked, bail; otherwise, do the deed. */
  724         if (PROC_TRYLOCK(p) == 0)
  725                 DB_ERROR(("Can't lock process with pid %ld\n", (long) pid));
  726         else {
  727                 pksignal(p, sig, NULL);
  728                 PROC_UNLOCK(p);
  729         }
  730 
  731 out:
  732         db_radix = old_radix;
  733 #undef DB_ERROR
  734 }
  735 
  736 /*
  737  * Reboot.  In case there is an additional argument, take it as delay in
  738  * seconds.  Default to 15s if we cannot parse it and make sure we will
  739  * never wait longer than 1 week.  Some code is similar to
  740  * kern_shutdown.c:shutdown_panic().
  741  */
  742 #ifndef DB_RESET_MAXDELAY
  743 #define DB_RESET_MAXDELAY       (3600 * 24 * 7)
  744 #endif
  745 
  746 static void
  747 db_reset(db_expr_t addr, bool have_addr, db_expr_t count __unused,
  748     char *modif)
  749 {
  750         int delay, loop;
  751 
  752         if (have_addr) {
  753                 delay = (int)db_hex2dec(addr);
  754 
  755                 /* If we parse to fail, use 15s. */
  756                 if (delay == -1)
  757                         delay = 15;
  758 
  759                 /* Cap at one week. */
  760                 if ((uintmax_t)delay > (uintmax_t)DB_RESET_MAXDELAY)
  761                         delay = DB_RESET_MAXDELAY;
  762 
  763                 db_printf("Automatic reboot in %d seconds - "
  764                     "press a key on the console to abort\n", delay);
  765                 for (loop = delay * 10; loop > 0; --loop) {
  766                         DELAY(1000 * 100); /* 1/10th second */
  767                         /* Did user type a key? */
  768                         if (cncheckc() != -1)
  769                                 return;
  770                 }
  771         }
  772 
  773         /*
  774          * Conditionally try the standard reboot path, so any registered
  775          * shutdown/reset handlers have a chance to run first. Some platforms
  776          * may not support the machine-dependent mechanism used by cpu_reset()
  777          * and rely on some other non-standard mechanism to perform the reset.
  778          * For example, the BCM2835 watchdog driver or gpio-poweroff driver.
  779          */
  780         if (modif[0] != 's') {
  781                 kern_reboot(RB_NOSYNC);
  782                 /* NOTREACHED */
  783         }
  784 
  785         cpu_reset();
  786 }
  787 
  788 static void
  789 db_watchdog(db_expr_t dummy1, bool dummy2, db_expr_t dummy3, char *dummy4)
  790 {
  791         db_expr_t old_radix, tout;
  792         int err, i;
  793 
  794         old_radix = db_radix;
  795         db_radix = 10;
  796         err = db_expression(&tout);
  797         db_skip_to_eol();
  798         db_radix = old_radix;
  799 
  800         /* If no argument is provided the watchdog will just be disabled. */
  801         if (err == 0) {
  802                 db_printf("No argument provided, disabling watchdog\n");
  803                 tout = 0;
  804         } else if ((tout & WD_INTERVAL) == WD_TO_NEVER) {
  805                 db_error("Out of range watchdog interval\n");
  806                 return;
  807         }
  808         EVENTHANDLER_INVOKE(watchdog_list, tout, &i);
  809 }
  810 
  811 static void
  812 db_gdb(db_expr_t dummy1, bool dummy2, db_expr_t dummy3, char *dummy4)
  813 {
  814 
  815         if (kdb_dbbe_select("gdb") != 0) {
  816                 db_printf("The remote GDB backend could not be selected.\n");
  817                 return;
  818         }
  819         /*
  820          * Mark that we are done in the debugger.  kdb_trap()
  821          * should re-enter with the new backend.
  822          */
  823         db_cmd_loop_done = 1;
  824         db_printf("(ctrl-c will return control to ddb)\n");
  825 }
  826 
  827 static void
  828 db_stack_trace(db_expr_t tid, bool hastid, db_expr_t count, char *modif)
  829 {
  830         struct thread *td;
  831         db_expr_t radix;
  832         pid_t pid;
  833         int t;
  834 
  835         /*
  836          * We parse our own arguments. We don't like the default radix.
  837          */
  838         radix = db_radix;
  839         db_radix = 10;
  840         hastid = db_expression(&tid);
  841         t = db_read_token();
  842         if (t == tCOMMA) {
  843                 if (!db_expression(&count)) {
  844                         db_printf("Count missing\n");
  845                         db_flush_lex();
  846                         db_radix = radix;
  847                         return;
  848                 }
  849         } else {
  850                 db_unread_token(t);
  851                 count = -1;
  852         }
  853         db_skip_to_eol();
  854         db_radix = radix;
  855 
  856         if (hastid) {
  857                 td = kdb_thr_lookup((lwpid_t)tid);
  858                 if (td == NULL)
  859                         td = kdb_thr_from_pid((pid_t)tid);
  860                 if (td == NULL) {
  861                         db_printf("Thread %d not found\n", (int)tid);
  862                         return;
  863                 }
  864         } else
  865                 td = kdb_thread;
  866         if (td->td_proc != NULL)
  867                 pid = td->td_proc->p_pid;
  868         else
  869                 pid = -1;
  870         db_printf("Tracing pid %d tid %ld td %p\n", pid, (long)td->td_tid, td);
  871         if (td->td_proc != NULL && (td->td_proc->p_flag & P_INMEM) == 0)
  872                 db_printf("--- swapped out\n");
  873         else
  874                 db_trace_thread(td, count);
  875 }
  876 
  877 static void
  878 _db_stack_trace_all(bool active_only)
  879 {
  880         struct thread *td;
  881         jmp_buf jb;
  882         void *prev_jb;
  883 
  884         for (td = kdb_thr_first(); td != NULL; td = kdb_thr_next(td)) {
  885                 prev_jb = kdb_jmpbuf(jb);
  886                 if (setjmp(jb) == 0) {
  887                         if (TD_IS_RUNNING(td))
  888                                 db_printf("\nTracing command %s pid %d"
  889                                     " tid %ld td %p (CPU %d)\n",
  890                                     td->td_proc->p_comm, td->td_proc->p_pid,
  891                                     (long)td->td_tid, td, td->td_oncpu);
  892                         else if (active_only)
  893                                 continue;
  894                         else
  895                                 db_printf("\nTracing command %s pid %d"
  896                                     " tid %ld td %p\n", td->td_proc->p_comm,
  897                                     td->td_proc->p_pid, (long)td->td_tid, td);
  898                         if (td->td_proc->p_flag & P_INMEM)
  899                                 db_trace_thread(td, -1);
  900                         else
  901                                 db_printf("--- swapped out\n");
  902                         if (db_pager_quit) {
  903                                 kdb_jmpbuf(prev_jb);
  904                                 return;
  905                         }
  906                 }
  907                 kdb_jmpbuf(prev_jb);
  908         }
  909 }
  910 
  911 static void
  912 db_stack_trace_active(db_expr_t dummy, bool dummy2, db_expr_t dummy3,
  913     char *dummy4)
  914 {
  915 
  916         _db_stack_trace_all(true);
  917 }
  918 
  919 static void
  920 db_stack_trace_all(db_expr_t dummy, bool dummy2, db_expr_t dummy3,
  921     char *dummy4)
  922 {
  923 
  924         _db_stack_trace_all(false);
  925 }
  926 
  927 /*
  928  * Take the parsed expression value from the command line that was parsed
  929  * as a hexadecimal value and convert it as if the expression was parsed
  930  * as a decimal value.  Returns -1 if the expression was not a valid
  931  * decimal value.
  932  */
  933 db_expr_t
  934 db_hex2dec(db_expr_t expr)
  935 {
  936         uintptr_t x, y;
  937         db_expr_t val;
  938 
  939         y = 1;
  940         val = 0;
  941         x = expr;
  942         while (x != 0) {
  943                 if (x % 16 > 9)
  944                         return (-1);
  945                 val += (x % 16) * (y);
  946                 x >>= 4;
  947                 y *= 10;
  948         }
  949         return (val);
  950 }

Cache object: c13d7d9fd0540c972597a1c5effa7532


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.