db_command.c revision 131952
1254721Semaste/* 2254721Semaste * Mach Operating System 3254721Semaste * Copyright (c) 1991,1990 Carnegie Mellon University 4254721Semaste * All Rights Reserved. 5254721Semaste * 6254721Semaste * Permission to use, copy, modify and distribute this software and its 7254721Semaste * documentation is hereby granted, provided that both the copyright 8254721Semaste * notice and this permission notice appear in all copies of the 9254721Semaste * software, derivative works or modified versions, and any portions 10254721Semaste * thereof, and that both notices appear in supporting documentation. 11254721Semaste * 12254721Semaste * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS 13254721Semaste * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 14254721Semaste * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 15254721Semaste * 16254721Semaste * Carnegie Mellon requests users of this software to return to 17254721Semaste * 18254721Semaste * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 19254721Semaste * School of Computer Science 20254721Semaste * Carnegie Mellon University 21254721Semaste * Pittsburgh PA 15213-3890 22254721Semaste * 23254721Semaste * any improvements or extensions that they make and grant Carnegie the 24254721Semaste * rights to redistribute these changes. 25254721Semaste */ 26254721Semaste/* 27254721Semaste * Author: David B. Golub, Carnegie Mellon University 28254721Semaste * Date: 7/90 29254721Semaste */ 30254721Semaste/* 31254721Semaste * Command dispatcher. 32254721Semaste */ 33254721Semaste 34254721Semaste#include <sys/cdefs.h> 35254721Semaste__FBSDID("$FreeBSD: head/sys/ddb/db_command.c 131952 2004-07-10 23:47:20Z marcel $"); 36254721Semaste 37254721Semaste#include <sys/param.h> 38254721Semaste#include <sys/linker_set.h> 39254721Semaste#include <sys/lock.h> 40254721Semaste#include <sys/kdb.h> 41254721Semaste#include <sys/mutex.h> 42254721Semaste#include <sys/proc.h> 43254721Semaste#include <sys/reboot.h> 44254721Semaste#include <sys/signalvar.h> 45254721Semaste#include <sys/systm.h> 46254721Semaste#include <sys/cons.h> 47254721Semaste#include <sys/watchdog.h> 48254721Semaste 49254721Semaste#include <ddb/ddb.h> 50254721Semaste#include <ddb/db_command.h> 51254721Semaste#include <ddb/db_lex.h> 52254721Semaste#include <ddb/db_output.h> 53254721Semaste 54254721Semaste#include <machine/cpu.h> 55254721Semaste#include <machine/setjmp.h> 56254721Semaste 57254721Semaste/* 58254721Semaste * Exported global variables 59254721Semaste */ 60254721Semasteboolean_t db_cmd_loop_done; 61254721Semastedb_addr_t db_dot; 62254721Semastedb_addr_t db_last_addr; 63254721Semastedb_addr_t db_prev; 64254721Semastedb_addr_t db_next; 65254721Semaste 66254721SemasteSET_DECLARE(db_cmd_set, struct command); 67254721SemasteSET_DECLARE(db_show_cmd_set, struct command); 68254721Semaste 69254721Semastestatic db_cmdfcn_t db_fncall; 70254721Semastestatic db_cmdfcn_t db_kill; 71254721Semastestatic db_cmdfcn_t db_reset; 72254721Semastestatic db_cmdfcn_t db_watchdog; 73254721Semaste 74254721Semaste/* XXX this is actually forward-static. */ 75254721Semasteextern struct command db_show_cmds[]; 76254721Semaste 77254721Semaste/* 78254721Semaste * if 'ed' style: 'dot' is set at start of last item printed, 79254721Semaste * and '+' points to next line. 80254721Semaste * Otherwise: 'dot' points to next item, '..' points to last. 81254721Semaste */ 82254721Semastestatic boolean_t db_ed_style = TRUE; 83254721Semaste 84254721Semaste/* 85254721Semaste * Utility routine - discard tokens through end-of-line. 86254721Semaste */ 87254721Semastevoid 88254721Semastedb_skip_to_eol() 89254721Semaste{ 90254721Semaste int t; 91254721Semaste do { 92254721Semaste t = db_read_token(); 93254721Semaste } while (t != tEOL); 94254721Semaste} 95 96/* 97 * Results of command search. 98 */ 99#define CMD_UNIQUE 0 100#define CMD_FOUND 1 101#define CMD_NONE 2 102#define CMD_AMBIGUOUS 3 103#define CMD_HELP 4 104 105static void db_cmd_list(struct command *table, struct command **aux_tablep, 106 struct command **aux_tablep_end); 107static int db_cmd_search(char *name, struct command *table, 108 struct command **aux_tablep, 109 struct command **aux_tablep_end, struct command **cmdp); 110static void db_command(struct command **last_cmdp, 111 struct command *cmd_table, struct command **aux_cmd_tablep, 112 struct command **aux_cmd_tablep_end); 113 114/* 115 * Search for command prefix. 116 */ 117static int 118db_cmd_search(name, table, aux_tablep, aux_tablep_end, cmdp) 119 char * name; 120 struct command *table; 121 struct command **aux_tablep; 122 struct command **aux_tablep_end; 123 struct command **cmdp; /* out */ 124{ 125 struct command *cmd; 126 struct command **aux_cmdp; 127 int result = CMD_NONE; 128 129 for (cmd = table; cmd->name != 0; cmd++) { 130 register char *lp; 131 register char *rp; 132 register int c; 133 134 lp = name; 135 rp = cmd->name; 136 while ((c = *lp) == *rp) { 137 if (c == 0) { 138 /* complete match */ 139 *cmdp = cmd; 140 return (CMD_UNIQUE); 141 } 142 lp++; 143 rp++; 144 } 145 if (c == 0) { 146 /* end of name, not end of command - 147 partial match */ 148 if (result == CMD_FOUND) { 149 result = CMD_AMBIGUOUS; 150 /* but keep looking for a full match - 151 this lets us match single letters */ 152 } 153 else { 154 *cmdp = cmd; 155 result = CMD_FOUND; 156 } 157 } 158 } 159 if (result == CMD_NONE && aux_tablep != 0) 160 /* XXX repeat too much code. */ 161 for (aux_cmdp = aux_tablep; aux_cmdp < aux_tablep_end; aux_cmdp++) { 162 register char *lp; 163 register char *rp; 164 register int c; 165 166 lp = name; 167 rp = (*aux_cmdp)->name; 168 while ((c = *lp) == *rp) { 169 if (c == 0) { 170 /* complete match */ 171 *cmdp = *aux_cmdp; 172 return (CMD_UNIQUE); 173 } 174 lp++; 175 rp++; 176 } 177 if (c == 0) { 178 /* end of name, not end of command - 179 partial match */ 180 if (result == CMD_FOUND) { 181 result = CMD_AMBIGUOUS; 182 /* but keep looking for a full match - 183 this lets us match single letters */ 184 } 185 else { 186 *cmdp = *aux_cmdp; 187 result = CMD_FOUND; 188 } 189 } 190 } 191 if (result == CMD_NONE) { 192 /* check for 'help' */ 193 if (name[0] == 'h' && name[1] == 'e' 194 && name[2] == 'l' && name[3] == 'p') 195 result = CMD_HELP; 196 } 197 return (result); 198} 199 200static void 201db_cmd_list(table, aux_tablep, aux_tablep_end) 202 struct command *table; 203 struct command **aux_tablep; 204 struct command **aux_tablep_end; 205{ 206 register struct command *cmd; 207 register struct command **aux_cmdp; 208 209 for (cmd = table; cmd->name != 0; cmd++) { 210 db_printf("%-12s", cmd->name); 211 db_end_line(); 212 } 213 if (aux_tablep == 0) 214 return; 215 for (aux_cmdp = aux_tablep; aux_cmdp < aux_tablep_end; aux_cmdp++) { 216 db_printf("%-12s", (*aux_cmdp)->name); 217 db_end_line(); 218 } 219} 220 221static void 222db_command(last_cmdp, cmd_table, aux_cmd_tablep, aux_cmd_tablep_end) 223 struct command **last_cmdp; /* IN_OUT */ 224 struct command *cmd_table; 225 struct command **aux_cmd_tablep; 226 struct command **aux_cmd_tablep_end; 227{ 228 struct command *cmd; 229 int t; 230 char modif[TOK_STRING_SIZE]; 231 db_expr_t addr, count; 232 boolean_t have_addr = FALSE; 233 int result; 234 235 t = db_read_token(); 236 if (t == tEOL) { 237 /* empty line repeats last command, at 'next' */ 238 cmd = *last_cmdp; 239 addr = (db_expr_t)db_next; 240 have_addr = FALSE; 241 count = 1; 242 modif[0] = '\0'; 243 } 244 else if (t == tEXCL) { 245 db_fncall((db_expr_t)0, (boolean_t)0, (db_expr_t)0, (char *)0); 246 return; 247 } 248 else if (t != tIDENT) { 249 db_printf("?\n"); 250 db_flush_lex(); 251 return; 252 } 253 else { 254 /* 255 * Search for command 256 */ 257 while (cmd_table) { 258 result = db_cmd_search(db_tok_string, 259 cmd_table, 260 aux_cmd_tablep, 261 aux_cmd_tablep_end, 262 &cmd); 263 switch (result) { 264 case CMD_NONE: 265 db_printf("No such command\n"); 266 db_flush_lex(); 267 return; 268 case CMD_AMBIGUOUS: 269 db_printf("Ambiguous\n"); 270 db_flush_lex(); 271 return; 272 case CMD_HELP: 273 db_cmd_list(cmd_table, aux_cmd_tablep, aux_cmd_tablep_end); 274 db_flush_lex(); 275 return; 276 default: 277 break; 278 } 279 if ((cmd_table = cmd->more) != 0) { 280 /* XXX usually no more aux's. */ 281 aux_cmd_tablep = 0; 282 if (cmd_table == db_show_cmds) { 283 aux_cmd_tablep = SET_BEGIN(db_show_cmd_set); 284 aux_cmd_tablep_end = SET_LIMIT(db_show_cmd_set); 285 } 286 287 t = db_read_token(); 288 if (t != tIDENT) { 289 db_cmd_list(cmd_table, aux_cmd_tablep, aux_cmd_tablep_end); 290 db_flush_lex(); 291 return; 292 } 293 } 294 } 295 296 if ((cmd->flag & CS_OWN) == 0) { 297 /* 298 * Standard syntax: 299 * command [/modifier] [addr] [,count] 300 */ 301 t = db_read_token(); 302 if (t == tSLASH) { 303 t = db_read_token(); 304 if (t != tIDENT) { 305 db_printf("Bad modifier\n"); 306 db_flush_lex(); 307 return; 308 } 309 db_strcpy(modif, db_tok_string); 310 } 311 else { 312 db_unread_token(t); 313 modif[0] = '\0'; 314 } 315 316 if (db_expression(&addr)) { 317 db_dot = (db_addr_t) addr; 318 db_last_addr = db_dot; 319 have_addr = TRUE; 320 } 321 else { 322 addr = (db_expr_t) db_dot; 323 have_addr = FALSE; 324 } 325 t = db_read_token(); 326 if (t == tCOMMA) { 327 if (!db_expression(&count)) { 328 db_printf("Count missing\n"); 329 db_flush_lex(); 330 return; 331 } 332 } 333 else { 334 db_unread_token(t); 335 count = -1; 336 } 337 if ((cmd->flag & CS_MORE) == 0) { 338 db_skip_to_eol(); 339 } 340 } 341 } 342 *last_cmdp = cmd; 343 if (cmd != 0) { 344 /* 345 * Execute the command. 346 */ 347 (*cmd->fcn)(addr, have_addr, count, modif); 348 db_setup_paging(NULL, NULL, -1); 349 350 if (cmd->flag & CS_SET_DOT) { 351 /* 352 * If command changes dot, set dot to 353 * previous address displayed (if 'ed' style). 354 */ 355 if (db_ed_style) { 356 db_dot = db_prev; 357 } 358 else { 359 db_dot = db_next; 360 } 361 } 362 else { 363 /* 364 * If command does not change dot, 365 * set 'next' location to be the same. 366 */ 367 db_next = db_dot; 368 } 369 } 370} 371 372/* 373 * 'show' commands 374 */ 375 376static struct command db_show_all_cmds[] = { 377 { "procs", db_ps, 0, 0 }, 378 { (char *)0 } 379}; 380 381static struct command db_show_cmds[] = { 382 { "all", 0, 0, db_show_all_cmds }, 383 { "registers", db_show_regs, 0, 0 }, 384 { "breaks", db_listbreak_cmd, 0, 0 }, 385 { "threads", db_show_threads, 0, 0 }, 386 { (char *)0, } 387}; 388 389static struct command db_command_table[] = { 390 { "print", db_print_cmd, 0, 0 }, 391 { "p", db_print_cmd, 0, 0 }, 392 { "examine", db_examine_cmd, CS_SET_DOT, 0 }, 393 { "x", db_examine_cmd, CS_SET_DOT, 0 }, 394 { "search", db_search_cmd, CS_OWN|CS_SET_DOT, 0 }, 395 { "set", db_set_cmd, CS_OWN, 0 }, 396 { "write", db_write_cmd, CS_MORE|CS_SET_DOT, 0 }, 397 { "w", db_write_cmd, CS_MORE|CS_SET_DOT, 0 }, 398 { "delete", db_delete_cmd, 0, 0 }, 399 { "d", db_delete_cmd, 0, 0 }, 400 { "break", db_breakpoint_cmd, 0, 0 }, 401 { "dwatch", db_deletewatch_cmd, 0, 0 }, 402 { "watch", db_watchpoint_cmd, CS_MORE,0 }, 403 { "dhwatch", db_deletehwatch_cmd, 0, 0 }, 404 { "hwatch", db_hwatchpoint_cmd, 0, 0 }, 405 { "step", db_single_step_cmd, 0, 0 }, 406 { "s", db_single_step_cmd, 0, 0 }, 407 { "continue", db_continue_cmd, 0, 0 }, 408 { "c", db_continue_cmd, 0, 0 }, 409 { "until", db_trace_until_call_cmd,0, 0 }, 410 { "next", db_trace_until_matching_cmd,0, 0 }, 411 { "match", db_trace_until_matching_cmd,0, 0 }, 412 { "trace", db_stack_trace_cmd, 0, 0 }, 413 { "where", db_stack_trace_cmd, 0, 0 }, 414 { "call", db_fncall, CS_OWN, 0 }, 415 { "show", 0, 0, db_show_cmds }, 416 { "ps", db_ps, 0, 0 }, 417 { "reset", db_reset, 0, 0 }, 418 { "kill", db_kill, CS_OWN, 0 }, 419 { "watchdog", db_watchdog, 0, 0 }, 420 { "thread", db_set_thread, CS_OWN, 0 }, 421 { (char *)0, } 422}; 423 424static struct command *db_last_command = 0; 425 426/* 427 * At least one non-optional command must be implemented using 428 * DB_COMMAND() so that db_cmd_set gets created. Here is one. 429 */ 430DB_COMMAND(panic, db_panic) 431{ 432 panic("from debugger"); 433} 434 435void 436db_command_loop() 437{ 438 /* 439 * Initialize 'prev' and 'next' to dot. 440 */ 441 db_prev = db_dot; 442 db_next = db_dot; 443 444 db_cmd_loop_done = 0; 445 while (!db_cmd_loop_done) { 446 if (db_print_position() != 0) 447 db_printf("\n"); 448 449 db_printf("db> "); 450 (void) db_read_line(); 451 452 db_command(&db_last_command, db_command_table, 453 SET_BEGIN(db_cmd_set), SET_LIMIT(db_cmd_set)); 454 } 455} 456 457void 458db_error(s) 459 const char *s; 460{ 461 if (s) 462 db_printf("%s", s); 463 db_flush_lex(); 464 kdb_reenter(); 465} 466 467 468/* 469 * Call random function: 470 * !expr(arg,arg,arg) 471 */ 472static void 473db_fncall(dummy1, dummy2, dummy3, dummy4) 474 db_expr_t dummy1; 475 boolean_t dummy2; 476 db_expr_t dummy3; 477 char * dummy4; 478{ 479 db_expr_t fn_addr; 480#define MAXARGS 11 /* XXX only 10 are passed */ 481 db_expr_t args[MAXARGS]; 482 int nargs = 0; 483 db_expr_t retval; 484 typedef db_expr_t fcn_10args_t(db_expr_t, db_expr_t, db_expr_t, 485 db_expr_t, db_expr_t, db_expr_t, db_expr_t, 486 db_expr_t, db_expr_t, db_expr_t); 487 fcn_10args_t *func; 488 int t; 489 490 if (!db_expression(&fn_addr)) { 491 db_printf("Bad function\n"); 492 db_flush_lex(); 493 return; 494 } 495 func = (fcn_10args_t *)fn_addr; /* XXX */ 496 497 t = db_read_token(); 498 if (t == tLPAREN) { 499 if (db_expression(&args[0])) { 500 nargs++; 501 while ((t = db_read_token()) == tCOMMA) { 502 if (nargs == MAXARGS) { 503 db_printf("Too many arguments\n"); 504 db_flush_lex(); 505 return; 506 } 507 if (!db_expression(&args[nargs])) { 508 db_printf("Argument missing\n"); 509 db_flush_lex(); 510 return; 511 } 512 nargs++; 513 } 514 db_unread_token(t); 515 } 516 if (db_read_token() != tRPAREN) { 517 db_printf("?\n"); 518 db_flush_lex(); 519 return; 520 } 521 } 522 db_skip_to_eol(); 523 524 while (nargs < MAXARGS) { 525 args[nargs++] = 0; 526 } 527 528 retval = (*func)(args[0], args[1], args[2], args[3], args[4], 529 args[5], args[6], args[7], args[8], args[9] ); 530 db_printf("%#lr\n", (long)retval); 531} 532 533static void 534db_kill(dummy1, dummy2, dummy3, dummy4) 535 db_expr_t dummy1; 536 boolean_t dummy2; 537 db_expr_t dummy3; 538 char * dummy4; 539{ 540 db_expr_t old_radix, pid, sig; 541 struct proc *p; 542 543#define DB_ERROR(f) do { db_printf f; db_flush_lex(); goto out; } while (0) 544 545 /* 546 * PIDs and signal numbers are typically represented in base 547 * 10, so make that the default here. It can, of course, be 548 * overridden by specifying a prefix. 549 */ 550 old_radix = db_radix; 551 db_radix = 10; 552 /* Retrieve arguments. */ 553 if (!db_expression(&sig)) 554 DB_ERROR(("Missing signal number\n")); 555 if (!db_expression(&pid)) 556 DB_ERROR(("Missing process ID\n")); 557 db_skip_to_eol(); 558 if (sig < 0 || sig > _SIG_MAXSIG) 559 DB_ERROR(("Signal number out of range\n")); 560 561 /* 562 * Find the process in question. allproc_lock is not needed 563 * since we're in DDB. 564 */ 565 /* sx_slock(&allproc_lock); */ 566 LIST_FOREACH(p, &allproc, p_list) 567 if (p->p_pid == pid) 568 break; 569 /* sx_sunlock(&allproc_lock); */ 570 if (p == NULL) 571 DB_ERROR(("Can't find process with pid %ld\n", (long) pid)); 572 573 /* If it's already locked, bail; otherwise, do the deed. */ 574 if (PROC_TRYLOCK(p) == 0) 575 DB_ERROR(("Can't lock process with pid %ld\n", (long) pid)); 576 else { 577 psignal(p, sig); 578 PROC_UNLOCK(p); 579 } 580 581out: 582 db_radix = old_radix; 583#undef DB_ERROR 584} 585 586static void 587db_reset(dummy1, dummy2, dummy3, dummy4) 588 db_expr_t dummy1; 589 boolean_t dummy2; 590 db_expr_t dummy3; 591 char * dummy4; 592{ 593 594 cpu_reset(); 595} 596 597static void 598db_watchdog(dummy1, dummy2, dummy3, dummy4) 599 db_expr_t dummy1; 600 boolean_t dummy2; 601 db_expr_t dummy3; 602 char * dummy4; 603{ 604 int i; 605 606 /* 607 * XXX: It might make sense to be able to set the watchdog to a 608 * XXX: timeout here so that failure or hang as a result of subsequent 609 * XXX: ddb commands could be recovered by a reset. 610 */ 611 612 EVENTHANDLER_INVOKE(watchdog_list, 0, &i); 613} 614