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