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