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