1 /* $OpenBSD: db_command.c,v 1.51 2009/01/20 22:46:49 thib Exp $ */ 2 /* $NetBSD: db_command.c,v 1.20 1996/03/30 22:30:05 christos Exp $ */ 3 4 /* 5 * Mach Operating System 6 * Copyright (c) 1993,1992,1991,1990 Carnegie Mellon University 7 * All Rights Reserved. 8 * 9 * Permission to use, copy, modify and distribute this software and its 10 * documentation is hereby granted, provided that both the copyright 11 * notice and this permission notice appear in all copies of the 12 * software, derivative works or modified versions, and any portions 13 * thereof, and that both notices appear in supporting documentation. 14 * 15 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 16 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 17 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 18 * 19 * Carnegie Mellon requests users of this software to return to 20 * 21 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 22 * School of Computer Science 23 * Carnegie Mellon University 24 * Pittsburgh PA 15213-3890 25 * 26 * any improvements or extensions that they make and grant Carnegie Mellon 27 * the rights to redistribute these changes. 28 */ 29 30 /* 31 * Command dispatcher. 32 */ 33 #include <sys/param.h> 34 #include <sys/systm.h> 35 #include <sys/proc.h> 36 #include <sys/reboot.h> 37 #include <sys/extent.h> 38 #include <sys/pool.h> 39 #include <sys/msgbuf.h> 40 #include <sys/malloc.h> 41 #include <sys/mount.h> 42 43 #include <uvm/uvm_extern.h> 44 #include <machine/db_machdep.h> /* type definitions */ 45 46 #include <ddb/db_lex.h> 47 #include <ddb/db_output.h> 48 #include <ddb/db_command.h> 49 #include <ddb/db_break.h> 50 #include <ddb/db_watch.h> 51 #include <ddb/db_run.h> 52 #include <ddb/db_variables.h> 53 #include <ddb/db_interface.h> 54 #include <ddb/db_sym.h> 55 #include <ddb/db_extern.h> 56 57 #include <uvm/uvm_ddb.h> 58 59 /* 60 * Exported global variables 61 */ 62 int db_cmd_loop_done; 63 label_t *db_recover; 64 65 /* 66 * if 'ed' style: 'dot' is set at start of last item printed, 67 * and '+' points to next line. 68 * Otherwise: 'dot' points to next item, '..' points to last. 69 */ 70 boolean_t db_ed_style = TRUE; 71 72 db_addr_t db_dot; /* current location */ 73 db_addr_t db_last_addr; /* last explicit address typed */ 74 db_addr_t db_prev; /* last address examined 75 or written */ 76 db_addr_t db_next; /* next address to be examined 77 or written */ 78 79 /* 80 * Utility routine - discard tokens through end-of-line. 81 */ 82 void 83 db_skip_to_eol(void) 84 { 85 int t; 86 do { 87 t = db_read_token(); 88 } while (t != tEOL); 89 } 90 91 /* 92 * Results of command search. 93 */ 94 #define CMD_UNIQUE 0 95 #define CMD_FOUND 1 96 #define CMD_NONE 2 97 #define CMD_AMBIGUOUS 3 98 99 /* 100 * Search for command prefix. 101 */ 102 int 103 db_cmd_search(char *name, struct db_command *table, struct db_command **cmdp) 104 { 105 struct db_command *cmd; 106 int result = CMD_NONE; 107 108 for (cmd = table; cmd->name != 0; cmd++) { 109 char *lp; 110 char *rp; 111 int c; 112 113 lp = name; 114 rp = cmd->name; 115 while ((c = *lp) == *rp) { 116 if (c == 0) { 117 /* complete match */ 118 *cmdp = cmd; 119 return (CMD_UNIQUE); 120 } 121 lp++; 122 rp++; 123 } 124 if (c == 0) { 125 /* end of name, not end of command - 126 partial match */ 127 if (result == CMD_FOUND) { 128 result = CMD_AMBIGUOUS; 129 /* but keep looking for a full match - 130 this lets us match single letters */ 131 } 132 else { 133 *cmdp = cmd; 134 result = CMD_FOUND; 135 } 136 } 137 } 138 return (result); 139 } 140 141 void 142 db_cmd_list(struct db_command *table) 143 { 144 struct db_command *cmd; 145 146 for (cmd = table; cmd->name != 0; cmd++) { 147 db_printf("%-12s", cmd->name); 148 db_end_line(12); 149 } 150 } 151 152 void 153 db_command(struct db_command **last_cmdp, struct db_command *cmd_table) 154 { 155 struct db_command *cmd; 156 int t; 157 char modif[TOK_STRING_SIZE]; 158 db_expr_t addr, count; 159 boolean_t have_addr = FALSE; 160 int result; 161 162 t = db_read_token(); 163 if (t == tEOL) { 164 /* empty line repeats last command, at 'next' */ 165 cmd = *last_cmdp; 166 addr = (db_expr_t)db_next; 167 have_addr = FALSE; 168 count = 1; 169 modif[0] = '\0'; 170 } 171 else if (t == tEXCL) { 172 db_fncall(0, 0, 0, NULL); 173 return; 174 } 175 else if (t != tIDENT) { 176 db_printf("?\n"); 177 db_flush_lex(); 178 return; 179 } 180 else { 181 /* 182 * Search for command 183 */ 184 while (cmd_table) { 185 result = db_cmd_search(db_tok_string, 186 cmd_table, 187 &cmd); 188 switch (result) { 189 case CMD_NONE: 190 db_printf("No such command\n"); 191 db_flush_lex(); 192 return; 193 case CMD_AMBIGUOUS: 194 db_printf("Ambiguous\n"); 195 db_flush_lex(); 196 return; 197 default: 198 break; 199 } 200 if ((cmd_table = cmd->more) != 0) { 201 t = db_read_token(); 202 if (t != tIDENT) { 203 db_cmd_list(cmd_table); 204 db_flush_lex(); 205 return; 206 } 207 } 208 } 209 210 if ((cmd->flag & CS_OWN) == 0) { 211 /* 212 * Standard syntax: 213 * command [/modifier] [addr] [,count] 214 */ 215 t = db_read_token(); 216 if (t == tSLASH) { 217 t = db_read_token(); 218 if (t != tIDENT) { 219 db_printf("Bad modifier\n"); 220 db_flush_lex(); 221 return; 222 } 223 db_strlcpy(modif, db_tok_string, sizeof(modif)); 224 } 225 else { 226 db_unread_token(t); 227 modif[0] = '\0'; 228 } 229 230 if (db_expression(&addr)) { 231 db_dot = (db_addr_t) addr; 232 db_last_addr = db_dot; 233 have_addr = TRUE; 234 } 235 else { 236 addr = (db_expr_t) db_dot; 237 have_addr = FALSE; 238 } 239 t = db_read_token(); 240 if (t == tCOMMA) { 241 if (!db_expression(&count)) { 242 db_printf("Count missing\n"); 243 db_flush_lex(); 244 return; 245 } 246 } 247 else { 248 db_unread_token(t); 249 count = -1; 250 } 251 if ((cmd->flag & CS_MORE) == 0) { 252 db_skip_to_eol(); 253 } 254 } 255 } 256 *last_cmdp = cmd; 257 if (cmd != 0) { 258 /* 259 * Execute the command. 260 */ 261 (*cmd->fcn)(addr, have_addr, count, modif); 262 263 if (cmd->flag & CS_SET_DOT) { 264 /* 265 * If command changes dot, set dot to 266 * previous address displayed (if 'ed' style). 267 */ 268 if (db_ed_style) { 269 db_dot = db_prev; 270 } 271 else { 272 db_dot = db_next; 273 } 274 } 275 else { 276 /* 277 * If command does not change dot, 278 * set 'next' location to be the same. 279 */ 280 db_next = db_dot; 281 } 282 } 283 } 284 285 /*ARGSUSED*/ 286 void 287 db_buf_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 288 { 289 boolean_t full = FALSE; 290 291 if (modif[0] == 'f') 292 full = TRUE; 293 294 vfs_buf_print((struct buf *) addr, full, db_printf); 295 } 296 297 /*ARGSUSED*/ 298 void 299 db_map_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 300 { 301 boolean_t full = FALSE; 302 303 if (modif[0] == 'f') 304 full = TRUE; 305 306 uvm_map_printit((struct vm_map *) addr, full, db_printf); 307 } 308 309 /*ARGSUSED*/ 310 void 311 db_malloc_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 312 { 313 #if defined(MALLOC_DEBUG) 314 extern void debug_malloc_printit(int (*)(const char *, ...), vaddr_t); 315 316 if (!have_addr) 317 addr = 0; 318 319 debug_malloc_printit(db_printf, (vaddr_t)addr); 320 #else 321 malloc_printit(db_printf); 322 #endif 323 } 324 325 /*ARGSUSED*/ 326 void 327 db_mount_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 328 { 329 boolean_t full = FALSE; 330 331 if (modif[0] == 'f') 332 full = TRUE; 333 334 vfs_mount_print((struct mount *) addr, full, db_printf); 335 } 336 337 void 338 db_show_all_mounts(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 339 { 340 boolean_t full = FALSE; 341 struct mount *mp; 342 343 if (modif[0] == 'f') 344 full = TRUE; 345 346 CIRCLEQ_FOREACH(mp, &mountlist, mnt_list) 347 vfs_mount_print(mp, full, db_printf); 348 } 349 350 /*ARGSUSED*/ 351 void 352 db_object_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 353 { 354 boolean_t full = FALSE; 355 356 if (modif[0] == 'f') 357 full = TRUE; 358 359 uvm_object_printit((struct uvm_object *) addr, full, db_printf); 360 } 361 362 /*ARGSUSED*/ 363 void 364 db_page_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 365 { 366 boolean_t full = FALSE; 367 368 if (modif[0] == 'f') 369 full = TRUE; 370 371 uvm_page_printit((struct vm_page *) addr, full, db_printf); 372 } 373 374 /*ARGSUSED*/ 375 void 376 db_vnode_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 377 { 378 boolean_t full = FALSE; 379 380 if (modif[0] == 'f') 381 full = TRUE; 382 383 vfs_vnode_print((struct vnode *) addr, full, db_printf); 384 } 385 386 #ifdef NFSCLIENT 387 /*ARGSUSED*/ 388 void 389 db_nfsreq_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 390 { 391 boolean_t full = FALSE; 392 393 if (modif[0] == 'f') 394 full = TRUE; 395 396 db_nfsreq_print((struct nfsreq *) addr, full, db_printf); 397 } 398 #endif 399 400 401 /*ARGSUSED*/ 402 void 403 db_show_panic_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 404 { 405 if (panicstr) 406 db_printf("%s\n", panicstr); 407 else 408 db_printf("the kernel did not panic\n"); /* yet */ 409 } 410 411 /*ARGSUSED*/ 412 void 413 db_extent_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 414 { 415 extent_print_all(); 416 } 417 418 /*ARGSUSED*/ 419 void 420 db_pool_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 421 { 422 pool_printit((struct pool *)addr, modif, db_printf); 423 } 424 425 /*ARGSUSED*/ 426 void 427 db_proc_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 428 { 429 if (!have_addr) 430 addr = (db_expr_t)curproc; 431 432 proc_printit((struct proc *)addr, modif, db_printf); 433 } 434 435 /*ARGSUSED*/ 436 void 437 db_uvmexp_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 438 { 439 uvmexp_print(db_printf); 440 } 441 442 /* 443 * 'show' commands 444 */ 445 446 struct db_command db_show_all_cmds[] = { 447 { "procs", db_show_all_procs, 0, NULL }, 448 { "callout", db_show_callout, 0, NULL }, 449 { "pools", db_show_all_pools, 0, NULL }, 450 { "mounts", db_show_all_mounts, 0, NULL }, 451 #ifdef NFSCLIENT 452 { "nfsreq", db_show_all_nfsreqs, 0, NULL }, 453 #endif 454 { NULL, NULL, 0, NULL } 455 }; 456 457 struct db_command db_show_cmds[] = { 458 { "all", NULL, 0, db_show_all_cmds }, 459 { "breaks", db_listbreak_cmd, 0, NULL }, 460 { "buf", db_buf_print_cmd, 0, NULL }, 461 { "extents", db_extent_print_cmd, 0, NULL }, 462 { "malloc", db_malloc_print_cmd, 0, NULL }, 463 { "map", db_map_print_cmd, 0, NULL }, 464 { "mount", db_mount_print_cmd, 0, NULL }, 465 { "object", db_object_print_cmd, 0, NULL }, 466 { "page", db_page_print_cmd, 0, NULL }, 467 { "panic", db_show_panic_cmd, 0, NULL }, 468 { "pool", db_pool_print_cmd, 0, NULL }, 469 { "proc", db_proc_print_cmd, 0, NULL }, 470 { "registers", db_show_regs, 0, NULL }, 471 { "uvmexp", db_uvmexp_print_cmd, 0, NULL }, 472 { "vnode", db_vnode_print_cmd, 0, NULL }, 473 #ifdef NFSCLIENT 474 { "nfsreq", db_nfsreq_print_cmd, 0, NULL }, 475 #endif 476 { "watches", db_listwatch_cmd, 0, NULL }, 477 { NULL, NULL, 0, NULL } 478 }; 479 480 struct db_command db_boot_cmds[] = { 481 { "sync", db_boot_sync_cmd, 0, 0 }, 482 { "crash", db_boot_crash_cmd, 0, 0 }, 483 { "dump", db_boot_dump_cmd, 0, 0 }, 484 { "halt", db_boot_halt_cmd, 0, 0 }, 485 { "reboot", db_boot_reboot_cmd, 0, 0 }, 486 { "poweroff", db_boot_poweroff_cmd, 0, 0 }, 487 { NULL, } 488 }; 489 490 struct db_command db_command_table[] = { 491 #ifdef DB_MACHINE_COMMANDS 492 /* this must be the first entry, if it exists */ 493 { "machine", NULL, 0, NULL}, 494 #endif 495 { "print", db_print_cmd, 0, NULL }, 496 { "examine", db_examine_cmd, CS_SET_DOT, NULL }, 497 { "x", db_examine_cmd, CS_SET_DOT, NULL }, 498 { "search", db_search_cmd, CS_OWN|CS_SET_DOT, NULL }, 499 { "set", db_set_cmd, CS_OWN, NULL }, 500 { "write", db_write_cmd, CS_MORE|CS_SET_DOT, NULL }, 501 { "w", db_write_cmd, CS_MORE|CS_SET_DOT, NULL }, 502 { "delete", db_delete_cmd, 0, NULL }, 503 { "d", db_delete_cmd, 0, NULL }, 504 { "break", db_breakpoint_cmd, 0, NULL }, 505 { "dwatch", db_deletewatch_cmd, 0, NULL }, 506 { "watch", db_watchpoint_cmd, CS_MORE, NULL }, 507 { "step", db_single_step_cmd, 0, NULL }, 508 { "s", db_single_step_cmd, 0, NULL }, 509 { "continue", db_continue_cmd, 0, NULL }, 510 { "c", db_continue_cmd, 0, NULL }, 511 { "until", db_trace_until_call_cmd,0, NULL }, 512 { "next", db_trace_until_matching_cmd,0, NULL }, 513 { "match", db_trace_until_matching_cmd,0, NULL }, 514 { "trace", db_stack_trace_cmd, 0, NULL }, 515 { "call", db_fncall, CS_OWN, NULL }, 516 { "ps", db_show_all_procs, 0, NULL }, 517 { "callout", db_show_callout, 0, NULL }, 518 { "show", NULL, 0, db_show_cmds }, 519 { "boot", NULL, 0, db_boot_cmds }, 520 { "help", db_help_cmd, 0, NULL }, 521 { "hangman", db_hangman, 0, NULL }, 522 { "dmesg", db_dmesg_cmd, 0, NULL }, 523 { NULL, NULL, 0, NULL } 524 }; 525 526 #ifdef DB_MACHINE_COMMANDS 527 528 /* this function should be called to install the machine dependent 529 commands. It should be called before the debugger is enabled */ 530 void db_machine_commands_install(struct db_command *ptr) 531 { 532 db_command_table[0].more = ptr; 533 return; 534 } 535 536 #endif 537 538 struct db_command *db_last_command = 0; 539 540 void 541 db_help_cmd(db_expr_t addr, int haddr, db_expr_t count, char *modif) 542 { 543 db_cmd_list(db_command_table); 544 } 545 546 void 547 db_command_loop(void) 548 { 549 label_t db_jmpbuf; 550 label_t *savejmp; 551 extern int db_output_line; 552 553 /* 554 * Initialize 'prev' and 'next' to dot. 555 */ 556 db_prev = db_dot; 557 db_next = db_dot; 558 559 db_cmd_loop_done = 0; 560 561 savejmp = db_recover; 562 db_recover = &db_jmpbuf; 563 (void) setjmp(&db_jmpbuf); 564 565 while (!db_cmd_loop_done) { 566 567 if (db_print_position() != 0) 568 db_printf("\n"); 569 db_output_line = 0; 570 571 #ifdef MULTIPROCESSOR 572 db_printf("ddb{%d}> ", CPU_INFO_UNIT(curcpu())); 573 #else 574 db_printf("ddb> "); 575 #endif 576 (void) db_read_line(); 577 578 db_command(&db_last_command, db_command_table); 579 } 580 581 db_recover = savejmp; 582 } 583 584 void 585 db_error(char *s) 586 { 587 if (s) 588 db_printf("%s", s); 589 db_flush_lex(); 590 longjmp(db_recover); 591 } 592 593 594 /* 595 * Call random function: 596 * !expr(arg,arg,arg) 597 */ 598 /*ARGSUSED*/ 599 void 600 db_fncall(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 601 { 602 db_expr_t fn_addr; 603 #define MAXARGS 11 604 db_expr_t args[MAXARGS]; 605 int nargs = 0; 606 db_expr_t retval; 607 db_expr_t (*func)(db_expr_t, ...); 608 int t; 609 char tmpfmt[28]; 610 611 if (!db_expression(&fn_addr)) { 612 db_printf("Bad function\n"); 613 db_flush_lex(); 614 return; 615 } 616 func = (db_expr_t (*)(db_expr_t, ...)) fn_addr; 617 618 t = db_read_token(); 619 if (t == tLPAREN) { 620 if (db_expression(&args[0])) { 621 nargs++; 622 while ((t = db_read_token()) == tCOMMA) { 623 if (nargs == MAXARGS) { 624 db_printf("Too many arguments\n"); 625 db_flush_lex(); 626 return; 627 } 628 if (!db_expression(&args[nargs])) { 629 db_printf("Argument missing\n"); 630 db_flush_lex(); 631 return; 632 } 633 nargs++; 634 } 635 db_unread_token(t); 636 } 637 if (db_read_token() != tRPAREN) { 638 db_printf("?\n"); 639 db_flush_lex(); 640 return; 641 } 642 } 643 db_skip_to_eol(); 644 645 while (nargs < MAXARGS) { 646 args[nargs++] = 0; 647 } 648 649 retval = (*func)(args[0], args[1], args[2], args[3], args[4], 650 args[5], args[6], args[7], args[8], args[9]); 651 db_printf("%s\n", db_format(tmpfmt, sizeof tmpfmt, retval, 652 DB_FORMAT_N, 1, 0)); 653 } 654 655 void 656 db_boot_sync_cmd(db_expr_t addr, int haddr, db_expr_t count, char *modif) 657 { 658 boot(RB_AUTOBOOT | RB_TIMEBAD | RB_USERREQ); 659 } 660 661 void 662 db_boot_crash_cmd(db_expr_t addr, int haddr, db_expr_t count, char *modif) 663 { 664 boot(RB_NOSYNC | RB_DUMP | RB_TIMEBAD | RB_USERREQ); 665 } 666 667 void 668 db_boot_dump_cmd(db_expr_t addr, int haddr, db_expr_t count, char *modif) 669 { 670 boot(RB_DUMP | RB_TIMEBAD | RB_USERREQ); 671 } 672 673 void 674 db_boot_halt_cmd(db_expr_t addr, int haddr, db_expr_t count, char *modif) 675 { 676 boot(RB_NOSYNC | RB_HALT | RB_TIMEBAD | RB_USERREQ); 677 } 678 679 void 680 db_boot_reboot_cmd(db_expr_t addr, int haddr, db_expr_t count, char *modif) 681 { 682 boot(RB_AUTOBOOT | RB_NOSYNC | RB_TIMEBAD | RB_USERREQ); 683 } 684 685 void 686 db_boot_poweroff_cmd(db_expr_t addr, int haddr, db_expr_t count, char *modif) 687 { 688 boot(RB_NOSYNC | RB_HALT | RB_POWERDOWN | RB_TIMEBAD | RB_USERREQ); 689 } 690 691 void 692 db_dmesg_cmd(db_expr_t addr, int haddr, db_expr_t count, char *modif) 693 { 694 int i, off; 695 char *p; 696 697 if (!msgbufp || msgbufp->msg_magic != MSG_MAGIC) 698 return; 699 off = msgbufp->msg_bufx; 700 if (off > msgbufp->msg_bufs) 701 off = 0; 702 for (i = 0, p = msgbufp->msg_bufc + off; 703 i < msgbufp->msg_bufs; i++, p++) { 704 if (p >= msgbufp->msg_bufc + msgbufp->msg_bufs) 705 p = msgbufp->msg_bufc; 706 if (*p != '\0') 707 db_putchar(*p); 708 } 709 db_putchar('\n'); 710 } 711 712 void 713 db_stack_trace_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, 714 char *modif) 715 { 716 db_stack_trace_print(addr, have_addr, count, modif, db_printf); 717 } 718