1 /* $NetBSD: db_command.c,v 1.25 1998/02/10 14:09:16 mrg 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 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_uvm.h" 30 31 /* 32 * Command dispatcher. 33 */ 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/reboot.h> 37 #include <sys/proc.h> 38 39 #include <machine/db_machdep.h> /* type definitions */ 40 41 #include <ddb/db_lex.h> 42 #include <ddb/db_output.h> 43 #include <ddb/db_command.h> 44 #include <ddb/db_break.h> 45 #include <ddb/db_watch.h> 46 #include <ddb/db_run.h> 47 #include <ddb/db_variables.h> 48 #include <ddb/db_interface.h> 49 #include <ddb/db_sym.h> 50 #include <ddb/db_extern.h> 51 52 #include <vm/vm.h> 53 54 #if defined(UVM) 55 #include <uvm/uvm_extern.h> 56 #endif 57 58 /* 59 * Exported global variables 60 */ 61 boolean_t db_cmd_loop_done; 62 label_t *db_recover; 63 64 /* 65 * if 'ed' style: 'dot' is set at start of last item printed, 66 * and '+' points to next line. 67 * Otherwise: 'dot' points to next item, '..' points to last. 68 */ 69 boolean_t db_ed_style = TRUE; 70 71 /* 72 * Utility routine - discard tokens through end-of-line. 73 */ 74 void 75 db_skip_to_eol() 76 { 77 int t; 78 do { 79 t = db_read_token(); 80 } while (t != tEOL); 81 } 82 83 /* 84 * Results of command search. 85 */ 86 #define CMD_UNIQUE 0 87 #define CMD_FOUND 1 88 #define CMD_NONE 2 89 #define CMD_AMBIGUOUS 3 90 #define CMD_HELP 4 91 92 /* 93 * Search for command prefix. 94 */ 95 int 96 db_cmd_search(name, table, cmdp) 97 char *name; 98 struct db_command *table; 99 struct db_command **cmdp; /* out */ 100 { 101 struct db_command *cmd; 102 int result = CMD_NONE; 103 104 for (cmd = table; cmd->name != 0; cmd++) { 105 register char *lp; 106 register char *rp; 107 register int c; 108 109 lp = name; 110 rp = cmd->name; 111 while ((c = *lp) == *rp) { 112 if (c == 0) { 113 /* complete match */ 114 *cmdp = cmd; 115 return (CMD_UNIQUE); 116 } 117 lp++; 118 rp++; 119 } 120 if (c == 0) { 121 /* end of name, not end of command - 122 partial match */ 123 if (result == CMD_FOUND) { 124 result = CMD_AMBIGUOUS; 125 /* but keep looking for a full match - 126 this lets us match single letters */ 127 } 128 else { 129 *cmdp = cmd; 130 result = CMD_FOUND; 131 } 132 } 133 } 134 if (result == CMD_NONE) { 135 /* check for 'help' */ 136 if (name[0] == 'h' && name[1] == 'e' 137 && name[2] == 'l' && name[3] == 'p') 138 result = CMD_HELP; 139 } 140 return (result); 141 } 142 143 void 144 db_cmd_list(table) 145 struct db_command *table; 146 { 147 register struct db_command *cmd; 148 149 for (cmd = table; cmd->name != 0; cmd++) { 150 db_printf("%-12s", cmd->name); 151 db_end_line(); 152 } 153 } 154 155 void 156 db_command(last_cmdp, cmd_table) 157 struct db_command **last_cmdp; /* IN_OUT */ 158 struct db_command *cmd_table; 159 { 160 struct db_command *cmd; 161 int t; 162 char modif[TOK_STRING_SIZE]; 163 db_expr_t addr, count; 164 boolean_t have_addr = FALSE; 165 int result; 166 167 t = db_read_token(); 168 if (t == tEOL) { 169 /* empty line repeats last command, at 'next' */ 170 cmd = *last_cmdp; 171 addr = (db_expr_t)db_next; 172 have_addr = FALSE; 173 count = 1; 174 modif[0] = '\0'; 175 } 176 else if (t == tEXCL) { 177 db_fncall(0, 0, 0, NULL); 178 return; 179 } 180 else if (t != tIDENT) { 181 db_printf("?\n"); 182 db_flush_lex(); 183 return; 184 } 185 else { 186 /* 187 * Search for command 188 */ 189 while (cmd_table) { 190 result = db_cmd_search(db_tok_string, 191 cmd_table, 192 &cmd); 193 switch (result) { 194 case CMD_NONE: 195 db_printf("No such command\n"); 196 db_flush_lex(); 197 return; 198 case CMD_AMBIGUOUS: 199 db_printf("Ambiguous\n"); 200 db_flush_lex(); 201 return; 202 case CMD_HELP: 203 db_cmd_list(cmd_table); 204 db_flush_lex(); 205 return; 206 default: 207 break; 208 } 209 if ((cmd_table = cmd->more) != 0) { 210 t = db_read_token(); 211 if (t != tIDENT) { 212 db_cmd_list(cmd_table); 213 db_flush_lex(); 214 return; 215 } 216 } 217 } 218 219 if ((cmd->flag & CS_OWN) == 0) { 220 /* 221 * Standard syntax: 222 * command [/modifier] [addr] [,count] 223 */ 224 t = db_read_token(); 225 if (t == tSLASH) { 226 t = db_read_token(); 227 if (t != tIDENT) { 228 db_printf("Bad modifier\n"); 229 db_flush_lex(); 230 return; 231 } 232 db_strcpy(modif, db_tok_string); 233 } 234 else { 235 db_unread_token(t); 236 modif[0] = '\0'; 237 } 238 239 if (db_expression(&addr)) { 240 db_dot = (db_addr_t) addr; 241 db_last_addr = db_dot; 242 have_addr = TRUE; 243 } 244 else { 245 addr = (db_expr_t) db_dot; 246 have_addr = FALSE; 247 } 248 t = db_read_token(); 249 if (t == tCOMMA) { 250 if (!db_expression(&count)) { 251 db_printf("Count missing\n"); 252 db_flush_lex(); 253 return; 254 } 255 } 256 else { 257 db_unread_token(t); 258 count = -1; 259 } 260 if ((cmd->flag & CS_MORE) == 0) { 261 db_skip_to_eol(); 262 } 263 } 264 } 265 *last_cmdp = cmd; 266 if (cmd != 0) { 267 /* 268 * Execute the command. 269 */ 270 (*cmd->fcn)(addr, have_addr, count, modif); 271 272 if (cmd->flag & CS_SET_DOT) { 273 /* 274 * If command changes dot, set dot to 275 * previous address displayed (if 'ed' style). 276 */ 277 if (db_ed_style) { 278 db_dot = db_prev; 279 } 280 else { 281 db_dot = db_next; 282 } 283 } 284 else { 285 /* 286 * If command does not change dot, 287 * set 'next' location to be the same. 288 */ 289 db_next = db_dot; 290 } 291 } 292 } 293 294 /*ARGSUSED*/ 295 void 296 db_map_print_cmd(addr, have_addr, count, modif) 297 db_expr_t addr; 298 int have_addr; 299 db_expr_t count; 300 char * modif; 301 { 302 boolean_t full = FALSE; 303 304 if (modif[0] == 'f') 305 full = TRUE; 306 307 #if defined(UVM) 308 uvm_map_printit((vm_map_t) addr, full, db_printf); 309 #else 310 _vm_map_print((vm_map_t) addr, full, db_printf); 311 #endif 312 } 313 314 /*ARGSUSED*/ 315 void 316 db_object_print_cmd(addr, have_addr, count, modif) 317 db_expr_t addr; 318 int have_addr; 319 db_expr_t count; 320 char * modif; 321 { 322 boolean_t full = FALSE; 323 324 if (modif[0] == 'f') 325 full = TRUE; 326 327 #if defined(UVM) 328 uvm_object_printit((struct uvm_object *) addr, full, db_printf); 329 #else 330 _vm_object_print((vm_object_t) addr, full, db_printf); 331 #endif 332 } 333 334 /*ARGSUSED*/ 335 void 336 db_page_print_cmd(addr, have_addr, count, modif) 337 db_expr_t addr; 338 int have_addr; 339 db_expr_t count; 340 char * modif; 341 { 342 boolean_t full = FALSE; 343 344 if (modif[0] == 'f') 345 full = TRUE; 346 347 #if defined(UVM) 348 uvm_page_printit((struct vm_page *) addr, full, db_printf); 349 #else 350 printf("only supported by UVM\n"); 351 #endif 352 } 353 354 /* 355 * 'show' commands 356 */ 357 358 struct db_command db_show_all_cmds[] = { 359 { "procs", db_show_all_procs, 0, NULL }, 360 { "callout", db_show_callout, 0, NULL }, 361 { NULL, NULL, 0, NULL } 362 }; 363 364 struct db_command db_show_cmds[] = { 365 { "all", NULL, 0, db_show_all_cmds }, 366 { "registers", db_show_regs, 0, NULL }, 367 { "breaks", db_listbreak_cmd, 0, NULL }, 368 { "watches", db_listwatch_cmd, 0, NULL }, 369 { "map", db_map_print_cmd, 0, NULL }, 370 { "object", db_object_print_cmd, 0, NULL }, 371 { "page", db_page_print_cmd, 0, NULL }, 372 { NULL, NULL, 0, NULL, } 373 }; 374 375 struct db_command db_command_table[] = { 376 #ifdef DB_MACHINE_COMMANDS 377 /* this must be the first entry, if it exists */ 378 { "machine", NULL, 0, NULL}, 379 #endif 380 { "print", db_print_cmd, 0, NULL }, 381 { "examine", db_examine_cmd, CS_SET_DOT, NULL }, 382 { "x", db_examine_cmd, CS_SET_DOT, NULL }, 383 { "search", db_search_cmd, CS_OWN|CS_SET_DOT, NULL }, 384 { "set", db_set_cmd, CS_OWN, NULL }, 385 { "write", db_write_cmd, CS_MORE|CS_SET_DOT, NULL }, 386 { "w", db_write_cmd, CS_MORE|CS_SET_DOT, NULL }, 387 { "delete", db_delete_cmd, 0, NULL }, 388 { "d", db_delete_cmd, 0, NULL }, 389 { "break", db_breakpoint_cmd, 0, NULL }, 390 { "dwatch", db_deletewatch_cmd, 0, NULL }, 391 { "watch", db_watchpoint_cmd, CS_MORE, NULL }, 392 { "step", db_single_step_cmd, 0, NULL }, 393 { "s", db_single_step_cmd, 0, NULL }, 394 { "continue", db_continue_cmd, 0, NULL }, 395 { "c", db_continue_cmd, 0, NULL }, 396 { "until", db_trace_until_call_cmd,0, NULL }, 397 { "next", db_trace_until_matching_cmd,0, NULL }, 398 { "match", db_trace_until_matching_cmd,0, NULL }, 399 { "trace", db_stack_trace_cmd, 0, NULL }, 400 { "call", db_fncall, CS_OWN, NULL }, 401 { "ps", db_show_all_procs, 0, NULL }, 402 { "kill", db_kill_proc, CS_OWN, NULL }, 403 { "callout", db_show_callout, 0, NULL }, 404 { "reboot", db_reboot_cmd, CS_OWN, NULL }, 405 { "show", NULL, 0, db_show_cmds }, 406 { NULL, NULL, 0, NULL } 407 }; 408 409 #ifdef DB_MACHINE_COMMANDS 410 411 /* this function should be called to install the machine dependent 412 commands. It should be called before the debugger is enabled */ 413 void db_machine_commands_install(ptr) 414 struct db_command *ptr; 415 { 416 db_command_table[0].more = ptr; 417 return; 418 } 419 420 #endif 421 422 struct db_command *db_last_command = 0; 423 424 void 425 db_help_cmd() 426 { 427 struct db_command *cmd = db_command_table; 428 429 while (cmd->name != 0) { 430 db_printf("%-12s", cmd->name); 431 db_end_line(); 432 cmd++; 433 } 434 } 435 436 void 437 db_command_loop() 438 { 439 label_t db_jmpbuf; 440 label_t *savejmp; 441 extern int db_output_line; 442 443 /* 444 * Initialize 'prev' and 'next' to dot. 445 */ 446 db_prev = db_dot; 447 db_next = db_dot; 448 449 db_cmd_loop_done = 0; 450 451 savejmp = db_recover; 452 db_recover = &db_jmpbuf; 453 (void) setjmp(&db_jmpbuf); 454 455 while (!db_cmd_loop_done) { 456 if (db_print_position() != 0) 457 db_printf("\n"); 458 db_output_line = 0; 459 460 db_printf("db> "); 461 (void) db_read_line(); 462 463 db_command(&db_last_command, db_command_table); 464 } 465 466 db_recover = savejmp; 467 } 468 469 void 470 db_error(s) 471 char *s; 472 { 473 if (s) 474 db_printf(s); 475 db_flush_lex(); 476 longjmp(db_recover); 477 } 478 479 480 /* 481 * Call random function: 482 * !expr(arg,arg,arg) 483 */ 484 /*ARGSUSED*/ 485 void 486 db_fncall(addr, have_addr, count, modif) 487 db_expr_t addr; 488 int have_addr; 489 db_expr_t count; 490 char * modif; 491 { 492 db_expr_t fn_addr; 493 #define MAXARGS 11 494 db_expr_t args[MAXARGS]; 495 int nargs = 0; 496 db_expr_t retval; 497 db_expr_t (*func) __P((db_expr_t, ...)); 498 int t; 499 500 if (!db_expression(&fn_addr)) { 501 db_printf("Bad function\n"); 502 db_flush_lex(); 503 return; 504 } 505 func = (db_expr_t (*) __P((db_expr_t, ...))) fn_addr; 506 507 t = db_read_token(); 508 if (t == tLPAREN) { 509 if (db_expression(&args[0])) { 510 nargs++; 511 while ((t = db_read_token()) == tCOMMA) { 512 if (nargs == MAXARGS) { 513 db_printf("Too many arguments\n"); 514 db_flush_lex(); 515 return; 516 } 517 if (!db_expression(&args[nargs])) { 518 db_printf("Argument missing\n"); 519 db_flush_lex(); 520 return; 521 } 522 nargs++; 523 } 524 db_unread_token(t); 525 } 526 if (db_read_token() != tRPAREN) { 527 db_printf("?\n"); 528 db_flush_lex(); 529 return; 530 } 531 } 532 db_skip_to_eol(); 533 534 while (nargs < MAXARGS) { 535 args[nargs++] = 0; 536 } 537 538 retval = (*func)(args[0], args[1], args[2], args[3], args[4], 539 args[5], args[6], args[7], args[8], args[9]); 540 db_printf("%#ln\n", retval); 541 } 542 543 void 544 db_reboot_cmd(addr, have_addr, count, modif) 545 db_expr_t addr; 546 int have_addr; 547 db_expr_t count; 548 char * modif; 549 { 550 db_expr_t bootflags; 551 552 /* Flags, default to RB_AUTOBOOT */ 553 if (!db_expression(&bootflags)) 554 bootflags = (db_expr_t)RB_AUTOBOOT; 555 if (db_read_token() != tEOL) { 556 db_error("?\n"); 557 /*NOTREACHED*/ 558 } 559 cpu_reboot((int)bootflags, NULL); 560 } 561