1 /* $OpenBSD: db_command.c,v 1.60 2009/08/17 13:11:58 jasper Exp $ */ 2 /* $NetBSD: db_command.c,v 1.20 1996/03/30 22:30:05 christos Exp $ */ 3 4 /* 5 * Mach Operating System 6 * Copyright (c) 1993,1992,1991,1990 Carnegie Mellon University 7 * All Rights Reserved. 8 * 9 * Permission to use, copy, modify and distribute this software and its 10 * documentation is hereby granted, provided that both the copyright 11 * notice and this permission notice appear in all copies of the 12 * software, derivative works or modified versions, and any portions 13 * thereof, and that both notices appear in supporting documentation. 14 * 15 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 16 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 17 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 18 * 19 * Carnegie Mellon requests users of this software to return to 20 * 21 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 22 * School of Computer Science 23 * Carnegie Mellon University 24 * Pittsburgh PA 15213-3890 25 * 26 * any improvements or extensions that they make and grant Carnegie Mellon 27 * the rights to redistribute these changes. 28 */ 29 30 /* 31 * Command dispatcher. 32 */ 33 #include <sys/param.h> 34 #include <sys/systm.h> 35 #include <sys/proc.h> 36 #include <sys/reboot.h> 37 #include <sys/extent.h> 38 #include <sys/pool.h> 39 #include <sys/msgbuf.h> 40 #include <sys/malloc.h> 41 #include <sys/mount.h> 42 43 #include <uvm/uvm_extern.h> 44 #include <machine/db_machdep.h> /* type definitions */ 45 46 #include <ddb/db_lex.h> 47 #include <ddb/db_output.h> 48 #include <ddb/db_command.h> 49 #include <ddb/db_break.h> 50 #include <ddb/db_watch.h> 51 #include <ddb/db_run.h> 52 #include <ddb/db_variables.h> 53 #include <ddb/db_interface.h> 54 #include <ddb/db_sym.h> 55 #include <ddb/db_extern.h> 56 57 #include <uvm/uvm_ddb.h> 58 59 /* 60 * Exported global variables 61 */ 62 int db_cmd_loop_done; 63 label_t *db_recover; 64 65 /* 66 * if 'ed' style: 'dot' is set at start of last item printed, 67 * and '+' points to next line. 68 * Otherwise: 'dot' points to next item, '..' points to last. 69 */ 70 boolean_t db_ed_style = TRUE; 71 72 db_addr_t db_dot; /* current location */ 73 db_addr_t db_last_addr; /* last explicit address typed */ 74 db_addr_t db_prev; /* last address examined 75 or written */ 76 db_addr_t db_next; /* next address to be examined 77 or written */ 78 79 /* 80 * Utility routine - discard tokens through end-of-line. 81 */ 82 void 83 db_skip_to_eol(void) 84 { 85 int t; 86 do { 87 t = db_read_token(); 88 } while (t != tEOL); 89 } 90 91 /* 92 * Results of command search. 93 */ 94 #define CMD_UNIQUE 0 95 #define CMD_FOUND 1 96 #define CMD_NONE 2 97 #define CMD_AMBIGUOUS 3 98 99 /* 100 * Search for command prefix. 101 */ 102 int 103 db_cmd_search(char *name, struct db_command *table, struct db_command **cmdp) 104 { 105 struct db_command *cmd; 106 int result = CMD_NONE; 107 108 for (cmd = table; cmd->name != 0; cmd++) { 109 char *lp; 110 char *rp; 111 int c; 112 113 lp = name; 114 rp = cmd->name; 115 while ((c = *lp) == *rp) { 116 if (c == 0) { 117 /* complete match */ 118 *cmdp = cmd; 119 return (CMD_UNIQUE); 120 } 121 lp++; 122 rp++; 123 } 124 if (c == 0) { 125 /* end of name, not end of command - 126 partial match */ 127 if (result == CMD_FOUND) { 128 result = CMD_AMBIGUOUS; 129 /* but keep looking for a full match - 130 this lets us match single letters */ 131 } 132 else { 133 *cmdp = cmd; 134 result = CMD_FOUND; 135 } 136 } 137 } 138 return (result); 139 } 140 141 void 142 db_cmd_list(struct db_command *table) 143 { 144 struct db_command *cmd; 145 146 for (cmd = table; cmd->name != 0; cmd++) { 147 db_printf("%-12s", cmd->name); 148 db_end_line(12); 149 } 150 } 151 152 void 153 db_command(struct db_command **last_cmdp, struct db_command *cmd_table) 154 { 155 struct db_command *cmd; 156 int t; 157 char modif[TOK_STRING_SIZE]; 158 db_expr_t addr, count; 159 boolean_t have_addr = FALSE; 160 int result; 161 162 t = db_read_token(); 163 if (t == tEOL) { 164 /* empty line repeats last command, at 'next' */ 165 cmd = *last_cmdp; 166 addr = (db_expr_t)db_next; 167 have_addr = FALSE; 168 count = 1; 169 modif[0] = '\0'; 170 } 171 else if (t == tEXCL) { 172 db_fncall(0, 0, 0, NULL); 173 return; 174 } 175 else if (t != tIDENT) { 176 db_printf("?\n"); 177 db_flush_lex(); 178 return; 179 } 180 else { 181 /* 182 * Search for command 183 */ 184 while (cmd_table) { 185 result = db_cmd_search(db_tok_string, 186 cmd_table, 187 &cmd); 188 switch (result) { 189 case CMD_NONE: 190 db_printf("No such command\n"); 191 db_flush_lex(); 192 return; 193 case CMD_AMBIGUOUS: 194 db_printf("Ambiguous\n"); 195 db_flush_lex(); 196 return; 197 default: 198 break; 199 } 200 if ((cmd_table = cmd->more) != 0) { 201 t = db_read_token(); 202 if (t != tIDENT) { 203 db_cmd_list(cmd_table); 204 db_flush_lex(); 205 return; 206 } 207 } 208 } 209 210 if ((cmd->flag & CS_OWN) == 0) { 211 /* 212 * Standard syntax: 213 * command [/modifier] [addr] [,count] 214 */ 215 t = db_read_token(); 216 if (t == tSLASH) { 217 t = db_read_token(); 218 if (t != tIDENT) { 219 db_printf("Bad modifier\n"); 220 db_flush_lex(); 221 return; 222 } 223 db_strlcpy(modif, db_tok_string, sizeof(modif)); 224 } 225 else { 226 db_unread_token(t); 227 modif[0] = '\0'; 228 } 229 230 if (db_expression(&addr)) { 231 db_dot = (db_addr_t) addr; 232 db_last_addr = db_dot; 233 have_addr = TRUE; 234 } 235 else { 236 addr = (db_expr_t) db_dot; 237 have_addr = FALSE; 238 } 239 t = db_read_token(); 240 if (t == tCOMMA) { 241 if (!db_expression(&count)) { 242 db_printf("Count missing\n"); 243 db_flush_lex(); 244 return; 245 } 246 } 247 else { 248 db_unread_token(t); 249 count = -1; 250 } 251 if ((cmd->flag & CS_MORE) == 0) { 252 db_skip_to_eol(); 253 } 254 } 255 } 256 *last_cmdp = cmd; 257 if (cmd != 0) { 258 /* 259 * Execute the command. 260 */ 261 (*cmd->fcn)(addr, have_addr, count, modif); 262 263 if (cmd->flag & CS_SET_DOT) { 264 /* 265 * If command changes dot, set dot to 266 * previous address displayed (if 'ed' style). 267 */ 268 if (db_ed_style) { 269 db_dot = db_prev; 270 } 271 else { 272 db_dot = db_next; 273 } 274 } 275 else { 276 /* 277 * If command does not change dot, 278 * set 'next' location to be the same. 279 */ 280 db_next = db_dot; 281 } 282 } 283 } 284 285 /*ARGSUSED*/ 286 void 287 db_buf_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 288 { 289 boolean_t full = FALSE; 290 291 if (modif[0] == 'f') 292 full = TRUE; 293 294 vfs_buf_print((void *) addr, full, db_printf); 295 } 296 297 /*ARGSUSED*/ 298 void 299 db_map_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 300 { 301 boolean_t full = FALSE; 302 303 if (modif[0] == 'f') 304 full = TRUE; 305 306 uvm_map_printit((struct vm_map *) addr, full, db_printf); 307 } 308 309 /*ARGSUSED*/ 310 void 311 db_malloc_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 312 { 313 #if defined(MALLOC_DEBUG) 314 extern void debug_malloc_printit(int (*)(const char *, ...), vaddr_t); 315 316 if (!have_addr) 317 addr = 0; 318 319 debug_malloc_printit(db_printf, (vaddr_t)addr); 320 #else 321 malloc_printit(db_printf); 322 #endif 323 } 324 325 /*ARGSUSED*/ 326 void 327 db_mount_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 328 { 329 boolean_t full = FALSE; 330 331 if (modif[0] == 'f') 332 full = TRUE; 333 334 vfs_mount_print((struct mount *) addr, full, db_printf); 335 } 336 337 void 338 db_show_all_mounts(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 339 { 340 boolean_t full = FALSE; 341 struct mount *mp; 342 343 if (modif[0] == 'f') 344 full = TRUE; 345 346 CIRCLEQ_FOREACH(mp, &mountlist, mnt_list) 347 vfs_mount_print(mp, full, db_printf); 348 } 349 350 extern struct pool vnode_pool; 351 void 352 db_show_all_vnodes(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 353 { 354 boolean_t full = FALSE; 355 356 if (modif[0] == 'f') 357 full = TRUE; 358 359 pool_walk(&vnode_pool, full, db_printf, vfs_vnode_print); 360 } 361 362 extern struct pool bufpool; 363 void 364 db_show_all_bufs(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 365 { 366 boolean_t full = FALSE; 367 368 if (modif[0] == 'f') 369 full = TRUE; 370 371 pool_walk(&bufpool, full, db_printf, vfs_buf_print); 372 } 373 374 /*ARGSUSED*/ 375 void 376 db_object_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 377 { 378 boolean_t full = FALSE; 379 380 if (modif[0] == 'f') 381 full = TRUE; 382 383 uvm_object_printit((struct uvm_object *) addr, full, db_printf); 384 } 385 386 /*ARGSUSED*/ 387 void 388 db_page_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 389 { 390 boolean_t full = FALSE; 391 392 if (modif[0] == 'f') 393 full = TRUE; 394 395 uvm_page_printit((struct vm_page *) addr, full, db_printf); 396 } 397 398 /*ARGSUSED*/ 399 void 400 db_vnode_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 401 { 402 boolean_t full = FALSE; 403 404 if (modif[0] == 'f') 405 full = TRUE; 406 407 vfs_vnode_print((void *)addr, full, db_printf); 408 } 409 410 #ifdef NFSCLIENT 411 /*ARGSUSED*/ 412 void 413 db_nfsreq_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, 414 char *modif) 415 { 416 boolean_t full = FALSE; 417 418 if (modif[0] == 'f') 419 full = TRUE; 420 421 nfs_request_print((void *)addr, full, db_printf); 422 } 423 424 /*ARGSUSED*/ 425 void 426 db_nfsnode_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, 427 char *modif) 428 { 429 boolean_t full = FALSE; 430 431 if (modif[0] == 'f') 432 full = TRUE; 433 434 nfs_node_print((void *)addr, full, db_printf); 435 } 436 #endif 437 438 439 /*ARGSUSED*/ 440 void 441 db_show_panic_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 442 { 443 if (panicstr) 444 db_printf("%s\n", panicstr); 445 else 446 db_printf("the kernel did not panic\n"); /* yet */ 447 } 448 449 /*ARGSUSED*/ 450 void 451 db_extent_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 452 { 453 extent_print_all(); 454 } 455 456 /*ARGSUSED*/ 457 void 458 db_pool_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 459 { 460 pool_printit((struct pool *)addr, modif, db_printf); 461 } 462 463 /*ARGSUSED*/ 464 void 465 db_proc_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 466 { 467 if (!have_addr) 468 addr = (db_expr_t)curproc; 469 470 proc_printit((struct proc *)addr, modif, db_printf); 471 } 472 473 /*ARGSUSED*/ 474 void 475 db_uvmexp_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 476 { 477 uvmexp_print(db_printf); 478 } 479 480 void bcstats_print(int (*)(const char *, ...)); 481 482 /*ARGSUSED*/ 483 void 484 db_bcstats_print_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 485 { 486 bcstats_print(db_printf); 487 } 488 489 /* 490 * 'show' commands 491 */ 492 493 struct db_command db_show_all_cmds[] = { 494 { "procs", db_show_all_procs, 0, NULL }, 495 { "callout", db_show_callout, 0, NULL }, 496 { "pools", db_show_all_pools, 0, NULL }, 497 { "mounts", db_show_all_mounts, 0, NULL }, 498 { "vnodes", db_show_all_vnodes, 0, NULL }, 499 { "bufs", db_show_all_bufs, 0, NULL }, 500 #ifdef NFSCLIENT 501 { "nfsreqs", db_show_all_nfsreqs, 0, NULL }, 502 { "nfsnodes", db_show_all_nfsnodes, 0, NULL }, 503 #endif 504 { NULL, NULL, 0, NULL } 505 }; 506 507 struct db_command db_show_cmds[] = { 508 { "all", NULL, 0, db_show_all_cmds }, 509 { "bcstats", db_bcstats_print_cmd, 0, NULL }, 510 { "breaks", db_listbreak_cmd, 0, NULL }, 511 { "buf", db_buf_print_cmd, 0, NULL }, 512 { "extents", db_extent_print_cmd, 0, NULL }, 513 { "malloc", db_malloc_print_cmd, 0, NULL }, 514 { "map", db_map_print_cmd, 0, NULL }, 515 { "mount", db_mount_print_cmd, 0, NULL }, 516 #ifdef NFSCLIENT 517 { "nfsreq", db_nfsreq_print_cmd, 0, NULL }, 518 { "nfsnode", db_nfsnode_print_cmd, 0, NULL }, 519 #endif 520 { "object", db_object_print_cmd, 0, NULL }, 521 #ifdef DDB_STRUCT_INFORMATION 522 { "offset", db_struct_offset_cmd, CS_OWN, NULL }, 523 #endif 524 { "page", db_page_print_cmd, 0, NULL }, 525 { "panic", db_show_panic_cmd, 0, NULL }, 526 { "pool", db_pool_print_cmd, 0, NULL }, 527 { "proc", db_proc_print_cmd, 0, NULL }, 528 { "registers", db_show_regs, 0, NULL }, 529 #ifdef DDB_STRUCT_INFORMATION 530 { "struct", db_struct_layout_cmd, CS_OWN, NULL }, 531 #endif 532 { "uvmexp", db_uvmexp_print_cmd, 0, NULL }, 533 { "vnode", db_vnode_print_cmd, 0, NULL }, 534 { "watches", db_listwatch_cmd, 0, NULL }, 535 { NULL, NULL, 0, NULL } 536 }; 537 538 struct db_command db_boot_cmds[] = { 539 { "sync", db_boot_sync_cmd, 0, 0 }, 540 { "crash", db_boot_crash_cmd, 0, 0 }, 541 { "dump", db_boot_dump_cmd, 0, 0 }, 542 { "halt", db_boot_halt_cmd, 0, 0 }, 543 { "reboot", db_boot_reboot_cmd, 0, 0 }, 544 { "poweroff", db_boot_poweroff_cmd, 0, 0 }, 545 { NULL, } 546 }; 547 548 struct db_command db_command_table[] = { 549 #ifdef DB_MACHINE_COMMANDS 550 /* this must be the first entry, if it exists */ 551 { "machine", NULL, 0, NULL}, 552 #endif 553 { "print", db_print_cmd, 0, NULL }, 554 { "examine", db_examine_cmd, CS_SET_DOT, NULL }, 555 { "x", db_examine_cmd, CS_SET_DOT, NULL }, 556 { "search", db_search_cmd, CS_OWN|CS_SET_DOT, NULL }, 557 { "set", db_set_cmd, CS_OWN, NULL }, 558 { "write", db_write_cmd, CS_MORE|CS_SET_DOT, NULL }, 559 { "w", db_write_cmd, CS_MORE|CS_SET_DOT, NULL }, 560 { "delete", db_delete_cmd, 0, NULL }, 561 { "d", db_delete_cmd, 0, NULL }, 562 { "break", db_breakpoint_cmd, 0, NULL }, 563 { "dwatch", db_deletewatch_cmd, 0, NULL }, 564 { "watch", db_watchpoint_cmd, CS_MORE, NULL }, 565 { "step", db_single_step_cmd, 0, NULL }, 566 { "s", db_single_step_cmd, 0, NULL }, 567 { "continue", db_continue_cmd, 0, NULL }, 568 { "c", db_continue_cmd, 0, NULL }, 569 { "until", db_trace_until_call_cmd,0, NULL }, 570 { "next", db_trace_until_matching_cmd,0, NULL }, 571 { "match", db_trace_until_matching_cmd,0, NULL }, 572 { "trace", db_stack_trace_cmd, 0, NULL }, 573 { "call", db_fncall, CS_OWN, NULL }, 574 { "ps", db_show_all_procs, 0, NULL }, 575 { "callout", db_show_callout, 0, NULL }, 576 { "show", NULL, 0, db_show_cmds }, 577 { "boot", NULL, 0, db_boot_cmds }, 578 { "help", db_help_cmd, 0, NULL }, 579 { "hangman", db_hangman, 0, NULL }, 580 { "dmesg", db_dmesg_cmd, 0, NULL }, 581 { NULL, NULL, 0, NULL } 582 }; 583 584 #ifdef DB_MACHINE_COMMANDS 585 586 /* this function should be called to install the machine dependent 587 commands. It should be called before the debugger is enabled */ 588 void db_machine_commands_install(struct db_command *ptr) 589 { 590 db_command_table[0].more = ptr; 591 return; 592 } 593 594 #endif 595 596 struct db_command *db_last_command = 0; 597 598 void 599 db_help_cmd(db_expr_t addr, int haddr, db_expr_t count, char *modif) 600 { 601 db_cmd_list(db_command_table); 602 } 603 604 void 605 db_command_loop(void) 606 { 607 label_t db_jmpbuf; 608 label_t *savejmp; 609 extern int db_output_line; 610 611 /* 612 * Initialize 'prev' and 'next' to dot. 613 */ 614 db_prev = db_dot; 615 db_next = db_dot; 616 617 db_cmd_loop_done = 0; 618 619 savejmp = db_recover; 620 db_recover = &db_jmpbuf; 621 (void) setjmp(&db_jmpbuf); 622 623 while (!db_cmd_loop_done) { 624 625 if (db_print_position() != 0) 626 db_printf("\n"); 627 db_output_line = 0; 628 629 #ifdef MULTIPROCESSOR 630 db_printf("ddb{%d}> ", CPU_INFO_UNIT(curcpu())); 631 #else 632 db_printf("ddb> "); 633 #endif 634 (void) db_read_line(); 635 636 db_command(&db_last_command, db_command_table); 637 } 638 639 db_recover = savejmp; 640 } 641 642 void 643 db_error(char *s) 644 { 645 if (s) 646 db_printf("%s", s); 647 db_flush_lex(); 648 if (db_recover != NULL) 649 longjmp(db_recover); 650 } 651 652 653 /* 654 * Call random function: 655 * !expr(arg,arg,arg) 656 */ 657 /*ARGSUSED*/ 658 void 659 db_fncall(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 660 { 661 db_expr_t fn_addr; 662 #define MAXARGS 11 663 db_expr_t args[MAXARGS]; 664 int nargs = 0; 665 db_expr_t retval; 666 db_expr_t (*func)(db_expr_t, ...); 667 int t; 668 char tmpfmt[28]; 669 670 if (!db_expression(&fn_addr)) { 671 db_printf("Bad function\n"); 672 db_flush_lex(); 673 return; 674 } 675 func = (db_expr_t (*)(db_expr_t, ...)) fn_addr; 676 677 t = db_read_token(); 678 if (t == tLPAREN) { 679 if (db_expression(&args[0])) { 680 nargs++; 681 while ((t = db_read_token()) == tCOMMA) { 682 if (nargs == MAXARGS) { 683 db_printf("Too many arguments\n"); 684 db_flush_lex(); 685 return; 686 } 687 if (!db_expression(&args[nargs])) { 688 db_printf("Argument missing\n"); 689 db_flush_lex(); 690 return; 691 } 692 nargs++; 693 } 694 db_unread_token(t); 695 } 696 if (db_read_token() != tRPAREN) { 697 db_printf("?\n"); 698 db_flush_lex(); 699 return; 700 } 701 } 702 db_skip_to_eol(); 703 704 while (nargs < MAXARGS) { 705 args[nargs++] = 0; 706 } 707 708 retval = (*func)(args[0], args[1], args[2], args[3], args[4], 709 args[5], args[6], args[7], args[8], args[9]); 710 db_printf("%s\n", db_format(tmpfmt, sizeof tmpfmt, retval, 711 DB_FORMAT_N, 1, 0)); 712 } 713 714 void 715 db_boot_sync_cmd(db_expr_t addr, int haddr, db_expr_t count, char *modif) 716 { 717 boot(RB_AUTOBOOT | RB_TIMEBAD | RB_USERREQ); 718 } 719 720 void 721 db_boot_crash_cmd(db_expr_t addr, int haddr, db_expr_t count, char *modif) 722 { 723 boot(RB_NOSYNC | RB_DUMP | RB_TIMEBAD | RB_USERREQ); 724 } 725 726 void 727 db_boot_dump_cmd(db_expr_t addr, int haddr, db_expr_t count, char *modif) 728 { 729 boot(RB_DUMP | RB_TIMEBAD | RB_USERREQ); 730 } 731 732 void 733 db_boot_halt_cmd(db_expr_t addr, int haddr, db_expr_t count, char *modif) 734 { 735 boot(RB_NOSYNC | RB_HALT | RB_TIMEBAD | RB_USERREQ); 736 } 737 738 void 739 db_boot_reboot_cmd(db_expr_t addr, int haddr, db_expr_t count, char *modif) 740 { 741 boot(RB_AUTOBOOT | RB_NOSYNC | RB_TIMEBAD | RB_USERREQ); 742 } 743 744 void 745 db_boot_poweroff_cmd(db_expr_t addr, int haddr, db_expr_t count, char *modif) 746 { 747 boot(RB_NOSYNC | RB_HALT | RB_POWERDOWN | RB_TIMEBAD | RB_USERREQ); 748 } 749 750 void 751 db_dmesg_cmd(db_expr_t addr, int haddr, db_expr_t count, char *modif) 752 { 753 int i, off; 754 char *p; 755 756 if (!msgbufp || msgbufp->msg_magic != MSG_MAGIC) 757 return; 758 off = msgbufp->msg_bufx; 759 if (off > msgbufp->msg_bufs) 760 off = 0; 761 for (i = 0, p = msgbufp->msg_bufc + off; 762 i < msgbufp->msg_bufs; i++, p++) { 763 if (p >= msgbufp->msg_bufc + msgbufp->msg_bufs) 764 p = msgbufp->msg_bufc; 765 if (*p != '\0') 766 db_putchar(*p); 767 } 768 db_putchar('\n'); 769 } 770 771 void 772 db_stack_trace_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, 773 char *modif) 774 { 775 db_stack_trace_print(addr, have_addr, count, modif, db_printf); 776 } 777