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