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