db_command.c revision 85944
1/* 2 * Mach Operating System 3 * Copyright (c) 1991,1990 Carnegie Mellon University 4 * All Rights Reserved. 5 * 6 * Permission to use, copy, modify and distribute this software and its 7 * documentation is hereby granted, provided that both the copyright 8 * notice and this permission notice appear in all copies of the 9 * software, derivative works or modified versions, and any portions 10 * thereof, and that both notices appear in supporting documentation. 11 * 12 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS 13 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 14 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 15 * 16 * Carnegie Mellon requests users of this software to return to 17 * 18 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 19 * School of Computer Science 20 * Carnegie Mellon University 21 * Pittsburgh PA 15213-3890 22 * 23 * any improvements or extensions that they make and grant Carnegie the 24 * rights to redistribute these changes. 25 * 26 * $FreeBSD: head/sys/ddb/db_command.c 85944 2001-11-03 04:55:48Z peter $ 27 */ 28 29/* 30 * Author: David B. Golub, Carnegie Mellon University 31 * Date: 7/90 32 */ 33 34/* 35 * Command dispatcher. 36 */ 37#include <sys/param.h> 38#include <sys/linker_set.h> 39#include <sys/reboot.h> 40#include <sys/systm.h> 41#include <sys/cons.h> 42 43#include <ddb/ddb.h> 44#include <ddb/db_command.h> 45#include <ddb/db_lex.h> 46#include <ddb/db_output.h> 47 48#include <machine/setjmp.h> 49 50/* 51 * Exported global variables 52 */ 53boolean_t db_cmd_loop_done; 54db_addr_t db_dot; 55jmp_buf db_jmpbuf; 56db_addr_t db_last_addr; 57db_addr_t db_prev; 58db_addr_t db_next; 59 60SET_DECLARE(db_cmd_set, struct command); 61SET_DECLARE(db_show_cmd_set, struct command); 62 63static db_cmdfcn_t db_fncall; 64static db_cmdfcn_t db_gdb; 65static db_cmdfcn_t db_reset; 66 67/* XXX this is actually forward-static. */ 68extern struct command db_show_cmds[]; 69 70/* 71 * if 'ed' style: 'dot' is set at start of last item printed, 72 * and '+' points to next line. 73 * Otherwise: 'dot' points to next item, '..' points to last. 74 */ 75static boolean_t db_ed_style = TRUE; 76 77/* 78 * Utility routine - discard tokens through end-of-line. 79 */ 80void 81db_skip_to_eol() 82{ 83 int t; 84 do { 85 t = db_read_token(); 86 } while (t != tEOL); 87} 88 89/* 90 * Results of command search. 91 */ 92#define CMD_UNIQUE 0 93#define CMD_FOUND 1 94#define CMD_NONE 2 95#define CMD_AMBIGUOUS 3 96#define CMD_HELP 4 97 98static void db_cmd_list __P((struct command *table, 99 struct command **aux_tablep, 100 struct command **aux_tablep_end)); 101static int db_cmd_search __P((char *name, struct command *table, 102 struct command **aux_tablep, 103 struct command **aux_tablep_end, 104 struct command **cmdp)); 105static void db_command __P((struct command **last_cmdp, 106 struct command *cmd_table, 107 struct command **aux_cmd_tablep, 108 struct command **aux_cmd_tablep_end)); 109 110/* 111 * Search for command prefix. 112 */ 113static int 114db_cmd_search(name, table, aux_tablep, aux_tablep_end, cmdp) 115 char * name; 116 struct command *table; 117 struct command **aux_tablep; 118 struct command **aux_tablep_end; 119 struct command **cmdp; /* out */ 120{ 121 struct command *cmd; 122 struct command **aux_cmdp; 123 int result = CMD_NONE; 124 125 for (cmd = table; cmd->name != 0; cmd++) { 126 register char *lp; 127 register char *rp; 128 register int c; 129 130 lp = name; 131 rp = cmd->name; 132 while ((c = *lp) == *rp) { 133 if (c == 0) { 134 /* complete match */ 135 *cmdp = cmd; 136 return (CMD_UNIQUE); 137 } 138 lp++; 139 rp++; 140 } 141 if (c == 0) { 142 /* end of name, not end of command - 143 partial match */ 144 if (result == CMD_FOUND) { 145 result = CMD_AMBIGUOUS; 146 /* but keep looking for a full match - 147 this lets us match single letters */ 148 } 149 else { 150 *cmdp = cmd; 151 result = CMD_FOUND; 152 } 153 } 154 } 155 if (result == CMD_NONE && aux_tablep != 0) 156 /* XXX repeat too much code. */ 157 for (aux_cmdp = aux_tablep; aux_cmdp < aux_tablep_end; aux_cmdp++) { 158 register char *lp; 159 register char *rp; 160 register int c; 161 162 lp = name; 163 rp = (*aux_cmdp)->name; 164 while ((c = *lp) == *rp) { 165 if (c == 0) { 166 /* complete match */ 167 *cmdp = *aux_cmdp; 168 return (CMD_UNIQUE); 169 } 170 lp++; 171 rp++; 172 } 173 if (c == 0) { 174 /* end of name, not end of command - 175 partial match */ 176 if (result == CMD_FOUND) { 177 result = CMD_AMBIGUOUS; 178 /* but keep looking for a full match - 179 this lets us match single letters */ 180 } 181 else { 182 *cmdp = *aux_cmdp; 183 result = CMD_FOUND; 184 } 185 } 186 } 187 if (result == CMD_NONE) { 188 /* check for 'help' */ 189 if (name[0] == 'h' && name[1] == 'e' 190 && name[2] == 'l' && name[3] == 'p') 191 result = CMD_HELP; 192 } 193 return (result); 194} 195 196static void 197db_cmd_list(table, aux_tablep, aux_tablep_end) 198 struct command *table; 199 struct command **aux_tablep; 200 struct command **aux_tablep_end; 201{ 202 register struct command *cmd; 203 register struct command **aux_cmdp; 204 205 for (cmd = table; cmd->name != 0; cmd++) { 206 db_printf("%-12s", cmd->name); 207 db_end_line(); 208 } 209 if (aux_tablep == 0) 210 return; 211 for (aux_cmdp = aux_tablep; aux_cmdp < aux_tablep_end; aux_cmdp++) { 212 db_printf("%-12s", (*aux_cmdp)->name); 213 db_end_line(); 214 } 215} 216 217static void 218db_command(last_cmdp, cmd_table, aux_cmd_tablep, aux_cmd_tablep_end) 219 struct command **last_cmdp; /* IN_OUT */ 220 struct command *cmd_table; 221 struct command **aux_cmd_tablep; 222 struct command **aux_cmd_tablep_end; 223{ 224 struct command *cmd; 225 int t; 226 char modif[TOK_STRING_SIZE]; 227 db_expr_t addr, count; 228 boolean_t have_addr = FALSE; 229 int result; 230 231 t = db_read_token(); 232 if (t == tEOL) { 233 /* empty line repeats last command, at 'next' */ 234 cmd = *last_cmdp; 235 addr = (db_expr_t)db_next; 236 have_addr = FALSE; 237 count = 1; 238 modif[0] = '\0'; 239 } 240 else if (t == tEXCL) { 241 db_fncall((db_expr_t)0, (boolean_t)0, (db_expr_t)0, (char *)0); 242 return; 243 } 244 else if (t != tIDENT) { 245 db_printf("?\n"); 246 db_flush_lex(); 247 return; 248 } 249 else { 250 /* 251 * Search for command 252 */ 253 while (cmd_table) { 254 result = db_cmd_search(db_tok_string, 255 cmd_table, 256 aux_cmd_tablep, 257 aux_cmd_tablep_end, 258 &cmd); 259 switch (result) { 260 case CMD_NONE: 261 db_printf("No such command\n"); 262 db_flush_lex(); 263 return; 264 case CMD_AMBIGUOUS: 265 db_printf("Ambiguous\n"); 266 db_flush_lex(); 267 return; 268 case CMD_HELP: 269 db_cmd_list(cmd_table, aux_cmd_tablep, aux_cmd_tablep_end); 270 db_flush_lex(); 271 return; 272 default: 273 break; 274 } 275 if ((cmd_table = cmd->more) != 0) { 276 /* XXX usually no more aux's. */ 277 aux_cmd_tablep = 0; 278 if (cmd_table == db_show_cmds) 279 aux_cmd_tablep = SET_BEGIN(db_show_cmd_set); 280 aux_cmd_tablep_end = SET_LIMIT(db_show_cmd_set); 281 282 t = db_read_token(); 283 if (t != tIDENT) { 284 db_cmd_list(cmd_table, aux_cmd_tablep, aux_cmd_tablep_end); 285 db_flush_lex(); 286 return; 287 } 288 } 289 } 290 291 if ((cmd->flag & CS_OWN) == 0) { 292 /* 293 * Standard syntax: 294 * command [/modifier] [addr] [,count] 295 */ 296 t = db_read_token(); 297 if (t == tSLASH) { 298 t = db_read_token(); 299 if (t != tIDENT) { 300 db_printf("Bad modifier\n"); 301 db_flush_lex(); 302 return; 303 } 304 db_strcpy(modif, db_tok_string); 305 } 306 else { 307 db_unread_token(t); 308 modif[0] = '\0'; 309 } 310 311 if (db_expression(&addr)) { 312 db_dot = (db_addr_t) addr; 313 db_last_addr = db_dot; 314 have_addr = TRUE; 315 } 316 else { 317 addr = (db_expr_t) db_dot; 318 have_addr = FALSE; 319 } 320 t = db_read_token(); 321 if (t == tCOMMA) { 322 if (!db_expression(&count)) { 323 db_printf("Count missing\n"); 324 db_flush_lex(); 325 return; 326 } 327 } 328 else { 329 db_unread_token(t); 330 count = -1; 331 } 332 if ((cmd->flag & CS_MORE) == 0) { 333 db_skip_to_eol(); 334 } 335 } 336 } 337 *last_cmdp = cmd; 338 if (cmd != 0) { 339 /* 340 * Execute the command. 341 */ 342 (*cmd->fcn)(addr, have_addr, count, modif); 343 344 if (cmd->flag & CS_SET_DOT) { 345 /* 346 * If command changes dot, set dot to 347 * previous address displayed (if 'ed' style). 348 */ 349 if (db_ed_style) { 350 db_dot = db_prev; 351 } 352 else { 353 db_dot = db_next; 354 } 355 } 356 else { 357 /* 358 * If command does not change dot, 359 * set 'next' location to be the same. 360 */ 361 db_next = db_dot; 362 } 363 } 364} 365 366/* 367 * 'show' commands 368 */ 369 370static struct command db_show_all_cmds[] = { 371#if 0 372 { "threads", db_show_all_threads, 0, 0 }, 373#endif 374 { "procs", db_ps, 0, 0 }, 375 { (char *)0 } 376}; 377 378static struct command db_show_cmds[] = { 379 { "all", 0, 0, db_show_all_cmds }, 380 { "registers", db_show_regs, 0, 0 }, 381 { "breaks", db_listbreak_cmd, 0, 0 }, 382#if 0 383 { "thread", db_show_one_thread, 0, 0 }, 384#endif 385#if 0 386 { "port", ipc_port_print, 0, 0 }, 387#endif 388 { (char *)0, } 389}; 390 391static struct command db_command_table[] = { 392 { "print", db_print_cmd, 0, 0 }, 393 { "p", db_print_cmd, 0, 0 }, 394 { "examine", db_examine_cmd, CS_SET_DOT, 0 }, 395 { "x", db_examine_cmd, CS_SET_DOT, 0 }, 396 { "search", db_search_cmd, CS_OWN|CS_SET_DOT, 0 }, 397 { "set", db_set_cmd, CS_OWN, 0 }, 398 { "write", db_write_cmd, CS_MORE|CS_SET_DOT, 0 }, 399 { "w", db_write_cmd, CS_MORE|CS_SET_DOT, 0 }, 400 { "delete", db_delete_cmd, 0, 0 }, 401 { "d", db_delete_cmd, 0, 0 }, 402 { "break", db_breakpoint_cmd, 0, 0 }, 403 { "dwatch", db_deletewatch_cmd, 0, 0 }, 404 { "watch", db_watchpoint_cmd, CS_MORE,0 }, 405 { "dhwatch", db_deletehwatch_cmd, 0, 0 }, 406 { "hwatch", db_hwatchpoint_cmd, 0, 0 }, 407 { "step", db_single_step_cmd, 0, 0 }, 408 { "s", db_single_step_cmd, 0, 0 }, 409 { "continue", db_continue_cmd, 0, 0 }, 410 { "c", db_continue_cmd, 0, 0 }, 411 { "until", db_trace_until_call_cmd,0, 0 }, 412 { "next", db_trace_until_matching_cmd,0, 0 }, 413 { "match", db_trace_until_matching_cmd,0, 0 }, 414 { "trace", db_stack_trace_cmd, 0, 0 }, 415 { "call", db_fncall, CS_OWN, 0 }, 416 { "show", 0, 0, db_show_cmds }, 417 { "ps", db_ps, 0, 0 }, 418 { "gdb", db_gdb, 0, 0 }, 419 { "reset", db_reset, 0, 0 }, 420 { (char *)0, } 421}; 422 423static struct command *db_last_command = 0; 424 425#if 0 426void 427db_help_cmd() 428{ 429 struct command *cmd = db_command_table; 430 431 while (cmd->name != 0) { 432 db_printf("%-12s", cmd->name); 433 db_end_line(); 434 cmd++; 435 } 436} 437#endif 438 439/* 440 * At least one non-optional command must be implemented using 441 * DB_COMMAND() so that db_cmd_set gets created. Here is one. 442 */ 443DB_COMMAND(panic, db_panic) 444{ 445 panic("from debugger"); 446} 447 448void 449db_command_loop() 450{ 451 /* 452 * Initialize 'prev' and 'next' to dot. 453 */ 454 db_prev = db_dot; 455 db_next = db_dot; 456 457 db_cmd_loop_done = 0; 458 while (!db_cmd_loop_done) { 459 460 (void) setjmp(db_jmpbuf); 461 if (db_print_position() != 0) 462 db_printf("\n"); 463 464 db_printf("db> "); 465 (void) db_read_line(); 466 467 db_command(&db_last_command, db_command_table, 468 SET_BEGIN(db_cmd_set), SET_LIMIT(db_cmd_set)); 469 } 470} 471 472void 473db_error(s) 474 char *s; 475{ 476 if (s) 477 db_printf("%s", s); 478 db_flush_lex(); 479 longjmp(db_jmpbuf, 1); 480} 481 482 483/* 484 * Call random function: 485 * !expr(arg,arg,arg) 486 */ 487static void 488db_fncall(dummy1, dummy2, dummy3, dummy4) 489 db_expr_t dummy1; 490 boolean_t dummy2; 491 db_expr_t dummy3; 492 char * dummy4; 493{ 494 db_expr_t fn_addr; 495#define MAXARGS 11 /* XXX only 10 are passed */ 496 db_expr_t args[MAXARGS]; 497 int nargs = 0; 498 db_expr_t retval; 499 typedef db_expr_t fcn_10args_t __P((db_expr_t, db_expr_t, db_expr_t, 500 db_expr_t, db_expr_t, db_expr_t, 501 db_expr_t, db_expr_t, db_expr_t, 502 db_expr_t)); 503 fcn_10args_t *func; 504 int t; 505 506 if (!db_expression(&fn_addr)) { 507 db_printf("Bad function\n"); 508 db_flush_lex(); 509 return; 510 } 511 func = (fcn_10args_t *)fn_addr; /* XXX */ 512 513 t = db_read_token(); 514 if (t == tLPAREN) { 515 if (db_expression(&args[0])) { 516 nargs++; 517 while ((t = db_read_token()) == tCOMMA) { 518 if (nargs == MAXARGS) { 519 db_printf("Too many arguments\n"); 520 db_flush_lex(); 521 return; 522 } 523 if (!db_expression(&args[nargs])) { 524 db_printf("Argument missing\n"); 525 db_flush_lex(); 526 return; 527 } 528 nargs++; 529 } 530 db_unread_token(t); 531 } 532 if (db_read_token() != tRPAREN) { 533 db_printf("?\n"); 534 db_flush_lex(); 535 return; 536 } 537 } 538 db_skip_to_eol(); 539 540 while (nargs < MAXARGS) { 541 args[nargs++] = 0; 542 } 543 544 retval = (*func)(args[0], args[1], args[2], args[3], args[4], 545 args[5], args[6], args[7], args[8], args[9] ); 546 db_printf("%#lr\n", (long)retval); 547} 548 549/* Enter GDB remote protocol debugger on the next trap. */ 550 551dev_t gdbdev = NODEV; 552cn_getc_t *gdb_getc; 553cn_putc_t *gdb_putc; 554 555static void 556db_gdb (dummy1, dummy2, dummy3, dummy4) 557 db_expr_t dummy1; 558 boolean_t dummy2; 559 db_expr_t dummy3; 560 char * dummy4; 561{ 562 563 if (gdbdev == NODEV) { 564 db_printf("No gdb port enabled. Set flag 0x80 on desired port\n"); 565 db_printf("in your configuration file (currently sio only).\n"); 566 return; 567 } 568 boothowto ^= RB_GDB; 569 570 db_printf("Next trap will enter %s\n", 571 boothowto & RB_GDB ? "GDB remote protocol mode" 572 : "DDB debugger"); 573} 574 575static void 576db_reset(dummy1, dummy2, dummy3, dummy4) 577 db_expr_t dummy1; 578 boolean_t dummy2; 579 db_expr_t dummy3; 580 char * dummy4; 581{ 582 583 cpu_reset(); 584} 585