1 /* $NetBSD: db_command.c,v 1.62 2001/08/17 01:00:10 thorpej Exp $ */ 2 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 "AS IS" 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 #include "opt_ddb.h" 30 #include "opt_inet.h" 31 32 /* 33 * Command dispatcher. 34 */ 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/reboot.h> 38 #include <sys/proc.h> 39 #include <sys/vnode.h> 40 #include <sys/pool.h> 41 #include <sys/namei.h> 42 #include <sys/malloc.h> 43 44 #include <machine/db_machdep.h> /* type definitions */ 45 46 #if defined(_KERNEL_OPT) 47 #include "opt_multiprocessor.h" 48 #endif 49 #ifdef MULTIPROCESSOR 50 #include <machine/cpu.h> 51 #endif 52 53 #include <ddb/db_lex.h> 54 #include <ddb/db_output.h> 55 #include <ddb/db_command.h> 56 #include <ddb/db_break.h> 57 #include <ddb/db_watch.h> 58 #include <ddb/db_run.h> 59 #include <ddb/db_variables.h> 60 #include <ddb/db_interface.h> 61 #include <ddb/db_sym.h> 62 #include <ddb/db_extern.h> 63 64 #include <uvm/uvm_extern.h> 65 #include <uvm/uvm_ddb.h> 66 67 #include "arp.h" 68 69 /* 70 * Exported global variables 71 */ 72 boolean_t db_cmd_loop_done; 73 label_t *db_recover; 74 75 /* 76 * if 'ed' style: 'dot' is set at start of last item printed, 77 * and '+' points to next line. 78 * Otherwise: 'dot' points to next item, '..' points to last. 79 */ 80 boolean_t db_ed_style = TRUE; 81 82 /* 83 * Utility routine - discard tokens through end-of-line. 84 */ 85 void 86 db_skip_to_eol() 87 { 88 int t; 89 do { 90 t = db_read_token(); 91 } while (t != tEOL); 92 } 93 94 /* 95 * Results of command search. 96 */ 97 #define CMD_UNIQUE 0 98 #define CMD_FOUND 1 99 #define CMD_NONE 2 100 #define CMD_AMBIGUOUS 3 101 #define CMD_HELP 4 102 103 /* 104 * Search for command prefix. 105 */ 106 int 107 db_cmd_search(name, table, cmdp) 108 const char *name; 109 const struct db_command *table; 110 const struct db_command **cmdp; /* out */ 111 { 112 const struct db_command *cmd; 113 int result = CMD_NONE; 114 115 for (cmd = table; cmd->name != 0; cmd++) { 116 const char *lp; 117 const char *rp; 118 int c; 119 120 lp = name; 121 rp = cmd->name; 122 while ((c = *lp) == *rp) { 123 if (c == 0) { 124 /* complete match */ 125 *cmdp = cmd; 126 return (CMD_UNIQUE); 127 } 128 lp++; 129 rp++; 130 } 131 if (c == 0) { 132 /* end of name, not end of command - 133 partial match */ 134 if (result == CMD_FOUND) { 135 result = CMD_AMBIGUOUS; 136 /* but keep looking for a full match - 137 this lets us match single letters */ 138 } 139 else { 140 *cmdp = cmd; 141 result = CMD_FOUND; 142 } 143 } 144 } 145 if (result == CMD_NONE) { 146 /* check for 'help' */ 147 if (name[0] == 'h' && name[1] == 'e' 148 && name[2] == 'l' && name[3] == 'p') 149 result = CMD_HELP; 150 } 151 return (result); 152 } 153 154 void 155 db_cmd_list(table) 156 const struct db_command *table; 157 { 158 int i, j, w, columns, lines, width=0, items, numcmds; 159 const char *p; 160 161 for (numcmds = 0; table[numcmds].name != NULL; numcmds++) { 162 w = strlen(table[numcmds].name); 163 if (w > width) 164 width = w; 165 } 166 width = DB_NEXT_TAB(width); 167 items = 0; 168 169 columns = db_max_width / width; 170 if (columns == 0) 171 columns = 1; 172 lines = (numcmds + columns - 1) / columns; 173 for (i = 0; i < lines; i++) { 174 for (j = 0; j < columns; j++) { 175 p = table[j * lines + i].name; 176 if (p) 177 db_printf("%s", p); 178 if (j * lines + i + lines >= numcmds) { 179 db_putchar('\n'); 180 break; 181 } 182 w = strlen(p); 183 while (w < width) { 184 w = DB_NEXT_TAB(w); 185 db_putchar('\t'); 186 } 187 } 188 } 189 } 190 191 void 192 db_command(last_cmdp, cmd_table) 193 const struct db_command **last_cmdp; /* IN_OUT */ 194 const struct db_command *cmd_table; 195 { 196 const struct db_command *cmd; 197 int t; 198 char modif[TOK_STRING_SIZE]; 199 db_expr_t addr, count; 200 boolean_t have_addr = FALSE; 201 int result; 202 203 static db_expr_t last_count = 0; 204 205 t = db_read_token(); 206 if ((t == tEOL) || (t == tCOMMA)) { 207 /* 208 * An empty line repeats last command, at 'next'. 209 * Only a count repeats the last command with the new count. 210 */ 211 cmd = *last_cmdp; 212 addr = (db_expr_t)db_next; 213 if (t == tCOMMA) { 214 if (!db_expression(&count)) { 215 db_printf("Count missing\n"); 216 db_flush_lex(); 217 return; 218 } 219 } else 220 count = last_count; 221 have_addr = FALSE; 222 modif[0] = '\0'; 223 db_skip_to_eol(); 224 } 225 else if (t == tEXCL) { 226 db_fncall(0, 0, 0, NULL); 227 return; 228 } 229 else if (t != tIDENT) { 230 db_printf("?\n"); 231 db_flush_lex(); 232 return; 233 } 234 else { 235 /* 236 * Search for command 237 */ 238 while (cmd_table) { 239 result = db_cmd_search(db_tok_string, 240 cmd_table, 241 &cmd); 242 switch (result) { 243 case CMD_NONE: 244 db_printf("No such command\n"); 245 db_flush_lex(); 246 return; 247 case CMD_AMBIGUOUS: 248 db_printf("Ambiguous\n"); 249 db_flush_lex(); 250 return; 251 case CMD_HELP: 252 db_cmd_list(cmd_table); 253 db_flush_lex(); 254 return; 255 default: 256 break; 257 } 258 if ((cmd_table = cmd->more) != 0) { 259 t = db_read_token(); 260 if (t != tIDENT) { 261 db_cmd_list(cmd_table); 262 db_flush_lex(); 263 return; 264 } 265 } 266 } 267 268 if ((cmd->flag & CS_OWN) == 0) { 269 /* 270 * Standard syntax: 271 * command [/modifier] [addr] [,count] 272 */ 273 t = db_read_token(); 274 if (t == tSLASH) { 275 t = db_read_token(); 276 if (t != tIDENT) { 277 db_printf("Bad modifier\n"); 278 db_flush_lex(); 279 return; 280 } 281 db_strcpy(modif, db_tok_string); 282 } 283 else { 284 db_unread_token(t); 285 modif[0] = '\0'; 286 } 287 288 if (db_expression(&addr)) { 289 db_dot = (db_addr_t) addr; 290 db_last_addr = db_dot; 291 have_addr = TRUE; 292 } 293 else { 294 addr = (db_expr_t) db_dot; 295 have_addr = FALSE; 296 } 297 t = db_read_token(); 298 if (t == tCOMMA) { 299 if (!db_expression(&count)) { 300 db_printf("Count missing\n"); 301 db_flush_lex(); 302 return; 303 } 304 } 305 else { 306 db_unread_token(t); 307 count = -1; 308 } 309 if ((cmd->flag & CS_MORE) == 0) { 310 db_skip_to_eol(); 311 } 312 } 313 } 314 *last_cmdp = cmd; 315 last_count = count; 316 if (cmd != 0) { 317 /* 318 * Execute the command. 319 */ 320 (*cmd->fcn)(addr, have_addr, count, modif); 321 322 if (cmd->flag & CS_SET_DOT) { 323 /* 324 * If command changes dot, set dot to 325 * previous address displayed (if 'ed' style). 326 */ 327 if (db_ed_style) { 328 db_dot = db_prev; 329 } 330 else { 331 db_dot = db_next; 332 } 333 } 334 else { 335 /* 336 * If command does not change dot, 337 * set 'next' location to be the same. 338 */ 339 db_next = db_dot; 340 } 341 } 342 } 343 344 /*ARGSUSED*/ 345 void 346 db_map_print_cmd(addr, have_addr, count, modif) 347 db_expr_t addr; 348 int have_addr; 349 db_expr_t count; 350 char * modif; 351 { 352 boolean_t full = FALSE; 353 354 if (modif[0] == 'f') 355 full = TRUE; 356 357 if (have_addr == FALSE) 358 addr = (db_expr_t) kernel_map; 359 360 uvm_map_printit((struct vm_map *) addr, full, db_printf); 361 } 362 363 /*ARGSUSED*/ 364 void 365 db_malloc_print_cmd(addr, have_addr, count, modif) 366 db_expr_t addr; 367 int have_addr; 368 db_expr_t count; 369 char * modif; 370 { 371 #ifdef MALLOC_DEBUG 372 if (!have_addr) 373 addr = 0; 374 375 debug_malloc_printit(db_printf, (vaddr_t) addr); 376 #else 377 db_printf("The kernel is not built with the MALLOC_DEBUG option.\n"); 378 #endif /* MALLOC_DEBUG */ 379 } 380 381 /*ARGSUSED*/ 382 void 383 db_object_print_cmd(addr, have_addr, count, modif) 384 db_expr_t addr; 385 int have_addr; 386 db_expr_t count; 387 char * modif; 388 { 389 boolean_t full = FALSE; 390 391 if (modif[0] == 'f') 392 full = TRUE; 393 394 uvm_object_printit((struct uvm_object *) addr, full, db_printf); 395 } 396 397 /*ARGSUSED*/ 398 void 399 db_page_print_cmd(addr, have_addr, count, modif) 400 db_expr_t addr; 401 int have_addr; 402 db_expr_t count; 403 char * modif; 404 { 405 boolean_t full = FALSE; 406 407 if (modif[0] == 'f') 408 full = TRUE; 409 410 uvm_page_printit((struct vm_page *) addr, full, db_printf); 411 } 412 413 /*ARGSUSED*/ 414 void 415 db_buf_print_cmd(addr, have_addr, count, modif) 416 db_expr_t addr; 417 int have_addr; 418 db_expr_t count; 419 char * modif; 420 { 421 boolean_t full = FALSE; 422 423 if (modif[0] == 'f') 424 full = TRUE; 425 426 vfs_buf_print((struct buf *)addr, full, db_printf); 427 } 428 429 /*ARGSUSED*/ 430 void 431 db_vnode_print_cmd(addr, have_addr, count, modif) 432 db_expr_t addr; 433 int have_addr; 434 db_expr_t count; 435 char * modif; 436 { 437 boolean_t full = FALSE; 438 439 if (modif[0] == 'f') 440 full = TRUE; 441 442 vfs_vnode_print((struct vnode *)addr, full, db_printf); 443 } 444 445 /*ARGSUSED*/ 446 void 447 db_pool_print_cmd(addr, have_addr, count, modif) 448 db_expr_t addr; 449 int have_addr; 450 db_expr_t count; 451 char * modif; 452 { 453 pool_printit((struct pool *)addr, modif, db_printf); 454 } 455 456 /*ARGSUSED*/ 457 void 458 db_namecache_print_cmd(addr, have_addr, count, modif) 459 db_expr_t addr; 460 int have_addr; 461 db_expr_t count; 462 char * modif; 463 { 464 namecache_print((struct vnode *)addr, db_printf); 465 } 466 467 /*ARGSUSED*/ 468 void 469 db_uvmexp_print_cmd(addr, have_addr, count, modif) 470 db_expr_t addr; 471 int have_addr; 472 db_expr_t count; 473 char * modif; 474 { 475 uvmexp_print(db_printf); 476 } 477 478 /* 479 * 'show' commands 480 */ 481 482 static const struct db_command db_show_all_cmds[] = { 483 { "callout", db_show_callout, 0, NULL }, 484 { "procs", db_show_all_procs, 0, NULL }, 485 { NULL, NULL, 0, NULL } 486 }; 487 488 static const struct db_command db_show_cmds[] = { 489 { "all", NULL, 0, db_show_all_cmds }, 490 #if defined(INET) && (NARP > 0) 491 { "arptab", db_show_arptab, 0, NULL }, 492 #endif 493 { "breaks", db_listbreak_cmd, 0, NULL }, 494 { "buf", db_buf_print_cmd, 0, NULL }, 495 { "map", db_map_print_cmd, 0, NULL }, 496 { "ncache", db_namecache_print_cmd, 0, NULL }, 497 { "object", db_object_print_cmd, 0, NULL }, 498 { "page", db_page_print_cmd, 0, NULL }, 499 { "pool", db_pool_print_cmd, 0, NULL }, 500 { "registers", db_show_regs, 0, NULL }, 501 { "uvmexp", db_uvmexp_print_cmd, 0, NULL }, 502 { "vnode", db_vnode_print_cmd, 0, NULL }, 503 { "watches", db_listwatch_cmd, 0, NULL }, 504 { "malloc", db_malloc_print_cmd, 0, NULL }, 505 { NULL, NULL, 0, NULL } 506 }; 507 508 static const struct db_command db_command_table[] = { 509 { "break", db_breakpoint_cmd, 0, NULL }, 510 { "c", db_continue_cmd, 0, NULL }, 511 { "call", db_fncall, CS_OWN, NULL }, 512 { "callout", db_show_callout, 0, NULL }, 513 { "continue", db_continue_cmd, 0, NULL }, 514 { "d", db_delete_cmd, 0, NULL }, 515 { "delete", db_delete_cmd, 0, NULL }, 516 { "dmesg", db_dmesg, 0, NULL }, 517 { "dwatch", db_deletewatch_cmd, 0, NULL }, 518 { "examine", db_examine_cmd, CS_SET_DOT, NULL }, 519 { "kill", db_kill_proc, CS_OWN, NULL }, 520 #ifdef DB_MACHINE_COMMANDS 521 { "machine", NULL, 0, db_machine_command_table }, 522 #endif 523 { "match", db_trace_until_matching_cmd,0, NULL }, 524 { "next", db_trace_until_matching_cmd,0, NULL }, 525 { "p", db_print_cmd, 0, NULL }, 526 { "print", db_print_cmd, 0, NULL }, 527 { "ps", db_show_all_procs, 0, NULL }, 528 { "reboot", db_reboot_cmd, CS_OWN, NULL }, 529 { "s", db_single_step_cmd, 0, NULL }, 530 { "search", db_search_cmd, CS_OWN|CS_SET_DOT, NULL }, 531 { "set", db_set_cmd, CS_OWN, NULL }, 532 { "show", NULL, 0, db_show_cmds }, 533 { "sifting", db_sifting_cmd, CS_OWN, NULL }, 534 { "step", db_single_step_cmd, 0, NULL }, 535 { "sync", db_sync_cmd, CS_OWN, NULL }, 536 { "trace", db_stack_trace_cmd, 0, NULL }, 537 { "until", db_trace_until_call_cmd,0, NULL }, 538 { "w", db_write_cmd, CS_MORE|CS_SET_DOT, NULL }, 539 { "watch", db_watchpoint_cmd, CS_MORE, NULL }, 540 { "write", db_write_cmd, CS_MORE|CS_SET_DOT, NULL }, 541 { "x", db_examine_cmd, CS_SET_DOT, NULL }, 542 { NULL, NULL, 0, NULL } 543 }; 544 545 const struct db_command *db_last_command = NULL; 546 547 void 548 db_command_loop() 549 { 550 label_t db_jmpbuf; 551 label_t *savejmp; 552 extern int db_output_line; 553 554 /* 555 * Initialize 'prev' and 'next' to dot. 556 */ 557 db_prev = db_dot; 558 db_next = db_dot; 559 560 db_cmd_loop_done = 0; 561 562 savejmp = db_recover; 563 db_recover = &db_jmpbuf; 564 (void) setjmp(&db_jmpbuf); 565 566 while (!db_cmd_loop_done) { 567 if (db_print_position() != 0) 568 db_printf("\n"); 569 db_output_line = 0; 570 571 572 #ifdef MULTIPROCESSOR 573 db_printf("db{%ld}> ", (long)cpu_number()); 574 #else 575 db_printf("db> "); 576 #endif 577 (void) db_read_line(); 578 579 db_command(&db_last_command, db_command_table); 580 } 581 582 db_recover = savejmp; 583 } 584 585 void 586 db_error(s) 587 char *s; 588 { 589 if (s) 590 db_printf("%s", s); 591 db_flush_lex(); 592 longjmp(db_recover); 593 } 594 595 596 /* 597 * Call random function: 598 * !expr(arg,arg,arg) 599 */ 600 /*ARGSUSED*/ 601 void 602 db_fncall(addr, have_addr, count, modif) 603 db_expr_t addr; 604 int have_addr; 605 db_expr_t count; 606 char * modif; 607 { 608 db_expr_t fn_addr; 609 #define MAXARGS 11 610 db_expr_t args[MAXARGS]; 611 int nargs = 0; 612 db_expr_t retval; 613 db_expr_t (*func) __P((db_expr_t, ...)); 614 int t; 615 616 if (!db_expression(&fn_addr)) { 617 db_printf("Bad function\n"); 618 db_flush_lex(); 619 return; 620 } 621 func = (db_expr_t (*) __P((db_expr_t, ...))) fn_addr; 622 623 t = db_read_token(); 624 if (t == tLPAREN) { 625 if (db_expression(&args[0])) { 626 nargs++; 627 while ((t = db_read_token()) == tCOMMA) { 628 if (nargs == MAXARGS) { 629 db_printf("Too many arguments\n"); 630 db_flush_lex(); 631 return; 632 } 633 if (!db_expression(&args[nargs])) { 634 db_printf("Argument missing\n"); 635 db_flush_lex(); 636 return; 637 } 638 nargs++; 639 } 640 db_unread_token(t); 641 } 642 if (db_read_token() != tRPAREN) { 643 db_printf("?\n"); 644 db_flush_lex(); 645 return; 646 } 647 } 648 db_skip_to_eol(); 649 650 while (nargs < MAXARGS) { 651 args[nargs++] = 0; 652 } 653 654 retval = (*func)(args[0], args[1], args[2], args[3], args[4], 655 args[5], args[6], args[7], args[8], args[9]); 656 db_printf("%s\n", db_num_to_str(retval)); 657 } 658 659 void 660 db_reboot_cmd(addr, have_addr, count, modif) 661 db_expr_t addr; 662 int have_addr; 663 db_expr_t count; 664 char * modif; 665 { 666 db_expr_t bootflags; 667 668 /* Flags, default to RB_AUTOBOOT */ 669 if (!db_expression(&bootflags)) 670 bootflags = (db_expr_t)RB_AUTOBOOT; 671 if (db_read_token() != tEOL) { 672 db_error("?\n"); 673 /*NOTREACHED*/ 674 } 675 /* 676 * We are leaving DDB, never to return upward. 677 * Clear db_recover so that we can debug faults in functions 678 * called from cpu_reboot. 679 */ 680 db_recover = 0; 681 cpu_reboot((int)bootflags, NULL); 682 } 683 684 void 685 db_sifting_cmd(addr, have_addr, count, omodif) 686 db_expr_t addr; 687 int have_addr; 688 db_expr_t count; 689 char * omodif; 690 { 691 int mode, t; 692 693 t = db_read_token(); 694 if (t == tSLASH) { 695 t = db_read_token(); 696 if (t != tIDENT) { 697 bad_modifier: 698 db_printf("Bad modifier\n"); 699 db_flush_lex(); 700 return; 701 } 702 if (!strcmp(db_tok_string, "F")) 703 mode = 'F'; 704 else 705 goto bad_modifier; 706 t = db_read_token(); 707 } else 708 mode = 0; 709 710 if (t == tIDENT) 711 db_sifting(db_tok_string, mode); 712 else { 713 db_printf("Bad argument (non-string)\n"); 714 db_flush_lex(); 715 } 716 } 717 718 void 719 db_stack_trace_cmd(addr, have_addr, count, modif) 720 db_expr_t addr; 721 boolean_t have_addr; 722 db_expr_t count; 723 char *modif; 724 { 725 if (count == -1) 726 count = 65535; 727 728 db_stack_trace_print(addr, have_addr, count, modif, db_printf); 729 } 730 731 void 732 db_sync_cmd(addr, have_addr, count, modif) 733 db_expr_t addr; 734 int have_addr; 735 db_expr_t count; 736 char * modif; 737 { 738 /* 739 * We are leaving DDB, never to return upward. 740 * Clear db_recover so that we can debug faults in functions 741 * called from cpu_reboot. 742 */ 743 db_recover = 0; 744 cpu_reboot(RB_DUMP, NULL); 745 } 746