diff options
author | Thomas Bushnell <thomas@gnu.org> | 1997-02-25 21:28:37 +0000 |
---|---|---|
committer | Thomas Bushnell <thomas@gnu.org> | 1997-02-25 21:28:37 +0000 |
commit | f07a4c844da9f0ecae5bbee1ab94be56505f26f7 (patch) | |
tree | 12b07c7e578fc1a5f53dbfde2632408491ff2a70 /ddb |
Initial source
Diffstat (limited to 'ddb')
36 files changed, 8498 insertions, 0 deletions
diff --git a/ddb/db_access.c b/ddb/db_access.c new file mode 100644 index 0000000..7a39144 --- /dev/null +++ b/ddb/db_access.c @@ -0,0 +1,137 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ +#include "mach_kdb.h" +#if MACH_KDB + +#include <mach/boolean.h> +#include <machine/db_machdep.h> /* type definitions */ +#include <machine/setjmp.h> +#include <kern/task.h> +#include <ddb/db_access.h> + + + +/* + * Access unaligned data items on aligned (longword) + * boundaries. + */ + +extern void db_read_bytes(); /* machine-dependent */ +extern void db_write_bytes(); /* machine-dependent */ + +int db_access_level = DB_ACCESS_LEVEL; + +/* + * This table is for sign-extending things. + * Therefore its entries are signed, and yes + * they are infact negative numbers. + * So don't you put no more Us in it. Or Ls either. + * Otherwise there is no point having it, n'est pas ? + */ +static int db_extend[sizeof(int)+1] = { /* table for sign-extending */ + 0, + 0xFFFFFF80, + 0xFFFF8000, + 0xFF800000, + 0x80000000 +}; + +db_expr_t +db_get_task_value(addr, size, is_signed, task) + db_addr_t addr; + register int size; + boolean_t is_signed; + task_t task; +{ + char data[sizeof(db_expr_t)]; + register db_expr_t value; + register int i; + + db_read_bytes((void*)addr, size, data, task); + + value = 0; +#if BYTE_MSF + for (i = 0; i < size; i++) +#else /* BYTE_LSF */ + for (i = size - 1; i >= 0; i--) +#endif + { + value = (value << 8) + (data[i] & 0xFF); + } + + if (size <= sizeof(int)) { + if (is_signed && (value & db_extend[size]) != 0) + value |= db_extend[size]; + } + return (value); +} + +void +db_put_task_value(addr, size, value, task) + db_addr_t addr; + register int size; + register db_expr_t value; + task_t task; +{ + char data[sizeof(db_expr_t)]; + register int i; + +#if BYTE_MSF + for (i = size - 1; i >= 0; i--) +#else /* BYTE_LSF */ + for (i = 0; i < size; i++) +#endif + { + data[i] = value & 0xFF; + value >>= 8; + } + + db_write_bytes((void*)addr, size, data, task); +} + +db_expr_t +db_get_value(addr, size, is_signed) + db_addr_t addr; + int size; + boolean_t is_signed; +{ + return(db_get_task_value(addr, size, is_signed, TASK_NULL)); +} + +void +db_put_value(addr, size, value) + db_addr_t addr; + int size; + db_expr_t value; +{ + db_put_task_value(addr, size, value, TASK_NULL); +} + +#endif MACH_KDB diff --git a/ddb/db_access.h b/ddb/db_access.h new file mode 100644 index 0000000..c01b6ce --- /dev/null +++ b/ddb/db_access.h @@ -0,0 +1,73 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ +/* + * Data access functions for debugger. + */ +#include <mach/boolean.h> +#include <machine/db_machdep.h> +#include <ddb/db_task_thread.h> +#include "vm_param.h" + +/* implementation dependent access capability */ +#define DB_ACCESS_KERNEL 0 /* only kernel space */ +#define DB_ACCESS_CURRENT 1 /* kernel or current task space */ +#define DB_ACCESS_ANY 2 /* any space */ + +#ifndef DB_ACCESS_LEVEL +#define DB_ACCESS_LEVEL DB_ACCESS_KERNEL +#endif DB_ACCESS_LEVEL + +#ifndef DB_VALID_KERN_ADDR +#define DB_VALID_KERN_ADDR(addr) ((addr) >= VM_MIN_KERNEL_ADDRESS \ + && (addr) < VM_MAX_KERNEL_ADDRESS) +#define DB_VALID_ADDRESS(addr,user) ((user != 0) ^ DB_VALID_KERN_ADDR(addr)) +#define DB_PHYS_EQ(task1,addr1,task2,addr2) 0 +#define DB_CHECK_ACCESS(addr,size,task) db_is_current_task(task) +#endif DB_VALID_KERN_ADDR + +extern int db_access_level; + +extern db_expr_t db_get_value( db_addr_t addr, + int size, + boolean_t is_signed ); + +extern void db_put_value( db_addr_t addr, + int size, + db_expr_t value ); + +extern db_expr_t db_get_task_value( db_addr_t addr, + int size, + boolean_t is_signed, + task_t task ); + +extern void db_put_task_value( db_addr_t addr, + int size, + db_expr_t value, + task_t task ); diff --git a/ddb/db_aout.c b/ddb/db_aout.c new file mode 100644 index 0000000..6cb8294 --- /dev/null +++ b/ddb/db_aout.c @@ -0,0 +1,507 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ +#include "mach_kdb.h" +#if MACH_KDB + +/* + * Symbol table routines for a.out format files. + */ + +#include <mach/std_types.h> +#include <machine/db_machdep.h> /* data types */ +#include <ddb/db_sym.h> + +#ifndef DB_NO_AOUT + +#include <ddb/nlist.h> /* a.out symbol table */ +#include <ddb/stab.h> + +#define private static + +/* + * An a.out symbol table as loaded into the kernel debugger: + * + * symtab -> size of symbol entries, in bytes + * sp -> first symbol entry + * ... + * ep -> last symbol entry + 1 + * strtab == start of string table + * size of string table in bytes, + * including this word + * -> strings + */ + +/* + * Find pointers to the start and end of the symbol entries, + * given a pointer to the start of the symbol table. + */ +#define db_get_aout_symtab(symtab, sp, ep) \ + (sp = (struct nlist *)((vm_offset_t *)(symtab) + 1), \ + ep = (struct nlist *)((char *)sp + *((int*)symtab))) + +boolean_t +aout_db_sym_init(symtab, esymtab, name, task_addr) + char * symtab; /* pointer to start of symbol table */ + char * esymtab; /* pointer to end of string table, + for checking - may be rounded up to + integer boundary */ + char * name; + char * task_addr; /* use for this task only */ +{ + register struct nlist *sym_start, *sym_end; + register struct nlist *sp; + register char * strtab; + register int strlen; + char * estrtab; + + db_get_aout_symtab(symtab, sym_start, sym_end); + + strtab = (char *)sym_end; + strlen = *(int *)strtab; + estrtab = strtab + strlen; + +#define round_to_size(x) \ + (((vm_offset_t)(x) + sizeof(vm_size_t) - 1) & ~(sizeof(vm_size_t) - 1)) + + if (round_to_size(estrtab) != round_to_size(esymtab)) { + db_printf("[ %s symbol table not valid ]\n", name); + return (FALSE); + } + +#undef round_to_size + + for (sp = sym_start; sp < sym_end; sp++) { + register long strx; + strx = sp->n_un.n_strx; + if (strx != 0) { + if (strx > strlen) { + db_printf("Bad string table index (%#x)\n", strx); + sp->n_un.n_name = 0; + continue; + } + sp->n_un.n_name = strtab + strx; + } + } + + if (db_add_symbol_table(SYMTAB_AOUT, + (char *)sym_start, + (char *)sym_end, + name, + symtab, + task_addr)) + { + /* Successfully added symbol table */ + db_printf("[ preserving %d bytes of %s symbol table ]\n", + esymtab - (char *)symtab, name); + return TRUE; + } + else + return FALSE; +} + +/* + * check file name or not (check xxxx.x pattern) + */ +private boolean_t +aout_db_is_filename(name) + register char *name; +{ + while (*name) { + if (*name == '.') { + if (name[1]) + return(TRUE); + } + name++; + } + return(FALSE); +} + +/* + * special name comparison routine with a name in the symbol table entry + */ +private boolean_t +aout_db_eq_name(sp, name) + struct nlist *sp; + char *name; +{ + register char *s1, *s2; + + s1 = sp->n_un.n_name; + s2 = name; + if (*s1 == '_' && *s2 && *s2 != '_') + s1++; + while (*s2) { + if (*s1++ != *s2++) { + /* + * check .c .o file name comparison case + */ + if (*s2 == 0 && sp->n_un.n_name <= s1 - 2 + && s1[-2] == '.' && s1[-1] == 'o') + return(TRUE); + return(FALSE); + } + } + /* + * do special check for + * xxx:yyy for N_FUN + * xxx.ttt for N_DATA and N_BSS + */ + return(*s1 == 0 || (*s1 == ':' && sp->n_type == N_FUN) || + (*s1 == '.' && (sp->n_type == N_DATA || sp->n_type == N_BSS))); +} + +/* + * search a symbol table with name and type + * fp(in,out): last found text file name symbol entry + */ +private struct nlist * +aout_db_search_name(sp, ep, name, type, fp) + register struct nlist *sp; + struct nlist *ep; + char *name; + int type; + struct nlist **fp; +{ + struct nlist *file_sp = *fp; + struct nlist *found_sp = 0; + + for ( ; sp < ep; sp++) { + if (sp->n_type == N_TEXT && aout_db_is_filename(sp->n_un.n_name)) + *fp = sp; + if (type) { + if (sp->n_type == type) { + if (aout_db_eq_name(sp, name)) + return(sp); + } + if (sp->n_type == N_SO) + *fp = sp; + continue; + } + if (sp->n_type & N_STAB) + continue; + if (sp->n_un.n_name && aout_db_eq_name(sp, name)) { + /* + * In case of qaulified search by a file, + * return it immediately with some check. + * Otherwise, search external one + */ + if (file_sp) { + if ((file_sp == *fp) || (sp->n_type & N_EXT)) + return(sp); + } else if (sp->n_type & N_EXT) + return(sp); + else + found_sp = sp; + } + } + return(found_sp); +} + +/* + * search a symbol with file, func and line qualification + */ +private db_sym_t +aout_db_qualified_search(stab, file, sym, line) + db_symtab_t *stab; + char *file; + char *sym; + int line; +{ + register struct nlist *sp = (struct nlist *)stab->start; + struct nlist *ep = (struct nlist *)stab->end; + struct nlist *fp = 0; + struct nlist *found_sp; + unsigned long func_top; + boolean_t in_file; + + if (file == 0 && sym == 0) + return(0); + if (file) { + if ((sp = aout_db_search_name(sp, ep, file, N_TEXT, &fp)) == 0) + return(0); + } + if (sym) { + sp = aout_db_search_name(sp, ep, sym, (line > 0)? N_FUN: 0, &fp); + if (sp == 0) + return(0); + } + if (line > 0) { + if (file && !aout_db_eq_name(fp, file)) + return(0); + found_sp = 0; + if (sp->n_type == N_FUN) { + /* + * qualified by function name + * search backward because line number entries + * for the function are above it in this case. + */ + func_top = sp->n_value; + for (sp--; sp >= (struct nlist *)stab->start; sp--) { + if (sp->n_type != N_SLINE) + continue; + if (sp->n_value < func_top) + break; + if (sp->n_desc <= line) { + if (found_sp == 0 || found_sp->n_desc < sp->n_desc) + found_sp = sp; + if (sp->n_desc == line) + break; + } + } + if (sp->n_type != N_SLINE || sp->n_value < func_top) + return(0); + } else { + /* + * qualified by only file name + * search forward in this case + */ + in_file = TRUE; + for (sp++; sp < ep; sp++) { + if (sp->n_type == N_TEXT + && aout_db_is_filename(sp->n_un.n_name)) + break; /* enter into another file */ + if (sp->n_type == N_SOL) { + in_file = aout_db_eq_name(sp, file); + continue; + } + if (!in_file || sp->n_type != N_SLINE) + continue; + if (sp->n_desc <= line) { + if (found_sp == 0 || found_sp->n_desc < sp->n_desc) + found_sp = sp; + if (sp->n_desc == line) + break; + } + } + } + sp = found_sp; + } + return((db_sym_t) sp); +} + +/* + * lookup symbol by name + */ +db_sym_t +aout_db_lookup(stab, symstr) + db_symtab_t *stab; + char * symstr; +{ + db_sym_t db_sym_parse_and_lookup(); + + return(db_sym_parse_and_lookup(aout_db_qualified_search, stab, symstr)); +} + +db_sym_t +aout_db_search_symbol(symtab, off, strategy, diffp) + db_symtab_t * symtab; + register + db_addr_t off; + db_strategy_t strategy; + db_expr_t *diffp; /* in/out */ +{ + register unsigned long diff = *diffp; + register struct nlist *symp = 0; + register struct nlist *sp, *ep; + + sp = (struct nlist *)symtab->start; + ep = (struct nlist *)symtab->end; + + for (; sp < ep; sp++) { + if (sp->n_un.n_name == 0) + continue; + if ((sp->n_type & N_STAB) != 0) + continue; + if (strategy == DB_STGY_XTRN && (sp->n_type & N_EXT) == 0) + continue; + if (off >= sp->n_value) { + + unsigned int type = sp->n_type; + + if (type == N_FN) continue; + if (off - sp->n_value < diff) { + diff = off - sp->n_value; + symp = sp; + if (diff == 0 && (type & N_EXT)) + break; + } + else if (off - sp->n_value == diff) { + if (symp == 0) + symp = sp; + else if ((symp->n_type & N_EXT) == 0 && + (type & N_EXT) != 0) + symp = sp; /* pick the external symbol */ + } + } + } + if (symp == 0) { + *diffp = off; + } + else { + *diffp = diff; + } + return ((db_sym_t)symp); +} + +/* + * Return the name and value for a symbol. + */ +void +aout_db_symbol_values(sym, namep, valuep) + db_sym_t sym; + char **namep; + db_expr_t *valuep; +{ + register struct nlist *sp; + + sp = (struct nlist *)sym; + if (namep) + *namep = sp->n_un.n_name; + if (valuep) + *valuep = sp->n_value; +} + +#define X_DB_MAX_DIFF 8 /* maximum allowable diff at the end of line */ + +/* + * search symbol by value + */ +private boolean_t +aout_db_search_by_addr(stab, addr, file, func, line, diff) + db_symtab_t *stab; + register vm_offset_t addr; + char **file; + char **func; + int *line; + unsigned long *diff; +{ + register struct nlist *sp; + register struct nlist *line_sp, *func_sp, *file_sp, *line_func; + register vm_size_t func_diff, line_diff; + boolean_t found_line = FALSE; + struct nlist *ep = (struct nlist *)stab->end; + + line_sp = func_sp = file_sp = line_func = 0; + *file = *func = 0; + *line = 0; + func_diff = line_diff = ~0; + for (sp = (struct nlist *)stab->start; sp < ep; sp++) { + switch(sp->n_type) { + case N_SLINE: + if (sp->n_value <= addr) { + if (line_sp == 0 || line_diff >= addr - sp->n_value) { + if (line_func) + line_func = 0; + line_sp = sp; + line_diff = addr - sp->n_value; + } + } + if (sp->n_value >= addr && line_sp) + found_line = TRUE; + continue; + case N_FUN: + if ((found_line || (line_sp && line_diff < X_DB_MAX_DIFF)) + && line_func == 0) + line_func = sp; + continue; + case N_SO: + if (sp->n_value > addr) + continue; + if (file_sp == 0 || file_sp->n_value <= sp->n_value) + file_sp = sp; + continue; + case N_TEXT: + if (aout_db_is_filename(sp->n_un.n_name)) { + if (sp->n_value > addr) + continue; + if (file_sp == 0 || file_sp->n_value <= sp->n_value) + file_sp = sp; + } else if (sp->n_value <= addr && + (func_sp == 0 || func_diff > addr - sp->n_value)) { + func_sp = sp; + func_diff = addr - sp->n_value; + } + continue; + case N_TEXT|N_EXT: + if (sp->n_value <= addr && + (func_sp == 0 || func_diff >= addr - sp->n_value)) { + func_sp = sp; + func_diff = addr - sp->n_value; + if (func_diff == 0 && file_sp && func_sp) + break; + } + default: + continue; + } + break; + } + if (line_sp) { + if (line_func == 0 || func_sp == 0 + || line_func->n_value != func_sp->n_value) + line_sp = 0; + } + if (file_sp) { + *diff = addr - file_sp->n_value; + *file = file_sp->n_un.n_name; + } + if (func_sp) { + *diff = addr - func_sp->n_value; + *func = (func_sp->n_un.n_name[0] == '_')? + func_sp->n_un.n_name + 1: func_sp->n_un.n_name; + } + if (line_sp) { + *diff = addr - line_sp->n_value; + *line = line_sp->n_desc; + } + return(file_sp || func_sp || line_sp); +} + +/* + * Find filename and lineno within, given the current pc. + */ +boolean_t +aout_db_line_at_pc(stab, sym, file, line, pc) + db_symtab_t *stab; + db_sym_t sym; + char **file; + int *line; + db_expr_t pc; +{ + char *func; + unsigned long diff; + boolean_t found; + + found = aout_db_search_by_addr(stab,(vm_offset_t)pc,file,&func,line,&diff); + return(found && func && *file); +} + +#endif /* DB_NO_AOUT */ + +#endif MACH_KDB diff --git a/ddb/db_break.c b/ddb/db_break.c new file mode 100644 index 0000000..d0ce1fc --- /dev/null +++ b/ddb/db_break.c @@ -0,0 +1,733 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ +#include "mach_kdb.h" +#if MACH_KDB + + +/* + * Breakpoints. + */ +#include <mach/boolean.h> +#include <machine/db_machdep.h> +#include <ddb/db_lex.h> +#include <ddb/db_break.h> +#include <ddb/db_access.h> +#include <ddb/db_sym.h> +#include <ddb/db_variables.h> +#include <ddb/db_command.h> +#include <ddb/db_task_thread.h> + +#define NBREAKPOINTS 100 +#define NTHREAD_LIST (NBREAKPOINTS*3) + +struct db_breakpoint db_break_table[NBREAKPOINTS]; +db_breakpoint_t db_next_free_breakpoint = &db_break_table[0]; +db_breakpoint_t db_free_breakpoints = 0; +db_breakpoint_t db_breakpoint_list = 0; + +static struct db_thread_breakpoint db_thread_break_list[NTHREAD_LIST]; +static db_thread_breakpoint_t db_free_thread_break_list = 0; +static boolean_t db_thread_break_init = FALSE; +static int db_breakpoint_number = 0; + +db_breakpoint_t +db_breakpoint_alloc() +{ + register db_breakpoint_t bkpt; + + if ((bkpt = db_free_breakpoints) != 0) { + db_free_breakpoints = bkpt->link; + return (bkpt); + } + if (db_next_free_breakpoint == &db_break_table[NBREAKPOINTS]) { + db_printf("All breakpoints used.\n"); + return (0); + } + bkpt = db_next_free_breakpoint; + db_next_free_breakpoint++; + + return (bkpt); +} + +void +db_breakpoint_free(bkpt) + register db_breakpoint_t bkpt; +{ + bkpt->link = db_free_breakpoints; + db_free_breakpoints = bkpt; +} + +static int +db_add_thread_breakpoint(bkpt, task_thd, count, task_bpt) + register db_breakpoint_t bkpt; + vm_offset_t task_thd; + boolean_t task_bpt; +{ + register db_thread_breakpoint_t tp; + + if (db_thread_break_init == FALSE) { + for (tp = db_thread_break_list; + tp < &db_thread_break_list[NTHREAD_LIST-1]; tp++) + tp->tb_next = tp+1; + tp->tb_next = 0; + db_free_thread_break_list = db_thread_break_list; + db_thread_break_init = TRUE; + } + if (db_free_thread_break_list == 0) + return (-1); + tp = db_free_thread_break_list; + db_free_thread_break_list = tp->tb_next; + tp->tb_is_task = task_bpt; + tp->tb_task_thd = task_thd; + tp->tb_count = count; + tp->tb_init_count = count; + tp->tb_cond = 0; + tp->tb_number = ++db_breakpoint_number; + tp->tb_next = bkpt->threads; + bkpt->threads = tp; + return(0); +} + +static int +db_delete_thread_breakpoint(bkpt, task_thd) + register db_breakpoint_t bkpt; + vm_offset_t task_thd; +{ + register db_thread_breakpoint_t tp; + register db_thread_breakpoint_t *tpp; + void db_cond_free(); + + if (task_thd == 0) { + /* delete all the thread-breakpoints */ + + for (tpp = &bkpt->threads; (tp = *tpp) != 0; tpp = &tp->tb_next) + db_cond_free(tp); + + *tpp = db_free_thread_break_list; + db_free_thread_break_list = bkpt->threads; + bkpt->threads = 0; + return 0; + } else { + /* delete the specified thread-breakpoint */ + + for (tpp = &bkpt->threads; (tp = *tpp) != 0; tpp = &tp->tb_next) + if (tp->tb_task_thd == task_thd) { + db_cond_free(tp); + *tpp = tp->tb_next; + tp->tb_next = db_free_thread_break_list; + db_free_thread_break_list = tp; + return 0; + } + + return -1; /* not found */ + } +} + +static db_thread_breakpoint_t +db_find_thread_breakpoint(bkpt, thread) + db_breakpoint_t bkpt; + thread_t thread; +{ + register db_thread_breakpoint_t tp; + register task_t task = (thread == THREAD_NULL)? TASK_NULL: thread->task; + + for (tp = bkpt->threads; tp; tp = tp->tb_next) { + if (tp->tb_is_task) { + if (tp->tb_task_thd == (vm_offset_t)task) + break; + continue; + } + if (tp->tb_task_thd == (vm_offset_t)thread || tp->tb_task_thd == 0) + break; + } + return(tp); +} + +db_thread_breakpoint_t +db_find_thread_breakpoint_here(task, addr) + task_t task; + db_addr_t addr; +{ + db_breakpoint_t bkpt; + + bkpt = db_find_breakpoint(task, (db_addr_t)addr); + if (bkpt == 0) + return(0); + return(db_find_thread_breakpoint(bkpt, current_thread())); +} + +db_thread_breakpoint_t +db_find_breakpoint_number(num, bkptp) + int num; + db_breakpoint_t *bkptp; +{ + register db_thread_breakpoint_t tp; + register db_breakpoint_t bkpt; + + for (bkpt = db_breakpoint_list; bkpt != 0; bkpt = bkpt->link) { + for (tp = bkpt->threads; tp; tp = tp->tb_next) { + if (tp->tb_number == num) { + if (bkptp) + *bkptp = bkpt; + return(tp); + } + } + } + return(0); +} + +static void +db_force_delete_breakpoint(bkpt, task_thd, is_task) + db_breakpoint_t bkpt; + vm_offset_t task_thd; + boolean_t is_task; +{ + db_printf("deleted a stale breakpoint at "); + if (bkpt->task == TASK_NULL || db_lookup_task(bkpt->task) >= 0) + db_task_printsym(bkpt->address, DB_STGY_PROC, bkpt->task); + else + db_printf("%#X", bkpt->address); + if (bkpt->task) + db_printf(" in task %X", bkpt->task); + if (task_thd) + db_printf(" for %s %X", (is_task)? "task": "thread", task_thd); + db_printf("\n"); + db_delete_thread_breakpoint(bkpt, task_thd); +} + +void +db_check_breakpoint_valid() +{ + register db_thread_breakpoint_t tbp, tbp_next; + register db_breakpoint_t bkpt, *bkptp; + + bkptp = &db_breakpoint_list; + for (bkpt = *bkptp; bkpt; bkpt = *bkptp) { + if (bkpt->task != TASK_NULL) { + if (db_lookup_task(bkpt->task) < 0) { + db_force_delete_breakpoint(bkpt, 0, FALSE); + *bkptp = bkpt->link; + db_breakpoint_free(bkpt); + continue; + } + } else { + for (tbp = bkpt->threads; tbp; tbp = tbp_next) { + tbp_next = tbp->tb_next; + if (tbp->tb_task_thd == 0) + continue; + if ((tbp->tb_is_task && + db_lookup_task((task_t)(tbp->tb_task_thd)) < 0) || + (!tbp->tb_is_task && + db_lookup_thread((thread_t)(tbp->tb_task_thd)) < 0)) { + db_force_delete_breakpoint(bkpt, + tbp->tb_task_thd, tbp->tb_is_task); + } + } + if (bkpt->threads == 0) { + db_put_task_value(bkpt->address, BKPT_SIZE, + bkpt->bkpt_inst, bkpt->task); + *bkptp = bkpt->link; + db_breakpoint_free(bkpt); + continue; + } + } + bkptp = &bkpt->link; + } +} + +void +db_set_breakpoint(task, addr, count, thread, task_bpt) + task_t task; + db_addr_t addr; + int count; + thread_t thread; + boolean_t task_bpt; +{ + register db_breakpoint_t bkpt; + db_breakpoint_t alloc_bkpt = 0; + vm_offset_t task_thd; + + bkpt = db_find_breakpoint(task, addr); + if (bkpt) { + if (thread == THREAD_NULL + || db_find_thread_breakpoint(bkpt, thread)) { + db_printf("Already set.\n"); + return; + } + } else { + if (!DB_CHECK_ACCESS(addr, BKPT_SIZE, task)) { + db_printf("Cannot set break point at %X\n", addr); + return; + } + alloc_bkpt = bkpt = db_breakpoint_alloc(); + if (bkpt == 0) { + db_printf("Too many breakpoints.\n"); + return; + } + bkpt->task = task; + bkpt->flags = (task && thread == THREAD_NULL)? + (BKPT_USR_GLOBAL|BKPT_1ST_SET): 0; + bkpt->address = addr; + bkpt->threads = 0; + } + if (db_breakpoint_list == 0) + db_breakpoint_number = 0; + task_thd = (task_bpt)? (vm_offset_t)(thread->task): (vm_offset_t)thread; + if (db_add_thread_breakpoint(bkpt, task_thd, count, task_bpt) < 0) { + if (alloc_bkpt) + db_breakpoint_free(alloc_bkpt); + db_printf("Too many thread_breakpoints.\n"); + } else { + db_printf("set breakpoint #%d\n", db_breakpoint_number); + if (alloc_bkpt) { + bkpt->link = db_breakpoint_list; + db_breakpoint_list = bkpt; + } + } +} + +void +db_delete_breakpoint(task, addr, task_thd) + task_t task; + db_addr_t addr; + vm_offset_t task_thd; +{ + register db_breakpoint_t bkpt; + register db_breakpoint_t *prev; + + for (prev = &db_breakpoint_list; (bkpt = *prev) != 0; + prev = &bkpt->link) { + if ((bkpt->task == task + || (task != TASK_NULL && (bkpt->flags & BKPT_USR_GLOBAL))) + && bkpt->address == addr) + break; + } + if (bkpt && (bkpt->flags & BKPT_SET_IN_MEM)) { + db_printf("cannot delete it now.\n"); + return; + } + if (bkpt == 0 + || db_delete_thread_breakpoint(bkpt, task_thd) < 0) { + db_printf("Not set.\n"); + return; + } + if (bkpt->threads == 0) { + *prev = bkpt->link; + db_breakpoint_free(bkpt); + } +} + +db_breakpoint_t +db_find_breakpoint(task, addr) + task_t task; + db_addr_t addr; +{ + register db_breakpoint_t bkpt; + + for (bkpt = db_breakpoint_list; bkpt != 0; bkpt = bkpt->link) { + if ((bkpt->task == task + || (task != TASK_NULL && (bkpt->flags & BKPT_USR_GLOBAL))) + && bkpt->address == addr) + return (bkpt); + } + return (0); +} + +boolean_t +db_find_breakpoint_here(task, addr) + task_t task; + db_addr_t addr; +{ + register db_breakpoint_t bkpt; + + for (bkpt = db_breakpoint_list; bkpt != 0; bkpt = bkpt->link) { + if ((bkpt->task == task + || (task != TASK_NULL && (bkpt->flags & BKPT_USR_GLOBAL))) + && bkpt->address == addr) + return(TRUE); + if ((bkpt->flags & BKPT_USR_GLOBAL) == 0 && + DB_PHYS_EQ(task, (vm_offset_t)addr, bkpt->task, (vm_offset_t)bkpt->address)) + return (TRUE); + } + return(FALSE); +} + +boolean_t db_breakpoints_inserted = TRUE; + +void +db_set_breakpoints() +{ + register db_breakpoint_t bkpt; + register task_t task; + db_expr_t inst; + task_t cur_task; + + cur_task = (current_thread())? current_thread()->task: TASK_NULL; + if (!db_breakpoints_inserted) { + for (bkpt = db_breakpoint_list; bkpt != 0; bkpt = bkpt->link) { + if (bkpt->flags & BKPT_SET_IN_MEM) + continue; + task = bkpt->task; + if (bkpt->flags & BKPT_USR_GLOBAL) { + if ((bkpt->flags & BKPT_1ST_SET) == 0) { + if (cur_task == TASK_NULL) + continue; + task = cur_task; + } else + bkpt->flags &= ~BKPT_1ST_SET; + } + if (DB_CHECK_ACCESS(bkpt->address, BKPT_SIZE, task)) { + inst = db_get_task_value(bkpt->address, BKPT_SIZE, FALSE, + task); + if (inst == BKPT_SET(inst)) + continue; + bkpt->bkpt_inst = inst; + db_put_task_value(bkpt->address, + BKPT_SIZE, + BKPT_SET(bkpt->bkpt_inst), task); + bkpt->flags |= BKPT_SET_IN_MEM; + } else { + db_printf("Warning: cannot set breakpoint at %X ", + bkpt->address); + if (task) + db_printf("in task %X\n", task); + else + db_printf("in kernel space\n"); + } + } + db_breakpoints_inserted = TRUE; + } +} + +void +db_clear_breakpoints() +{ + register db_breakpoint_t bkpt, *bkptp; + register task_t task; + task_t cur_task; + db_expr_t inst; + + cur_task = (current_thread())? current_thread()->task: TASK_NULL; + if (db_breakpoints_inserted) { + bkptp = &db_breakpoint_list; + for (bkpt = *bkptp; bkpt; bkpt = *bkptp) { + task = bkpt->task; + if (bkpt->flags & BKPT_USR_GLOBAL) { + if (cur_task == TASK_NULL) { + bkptp = &bkpt->link; + continue; + } + task = cur_task; + } + if ((bkpt->flags & BKPT_SET_IN_MEM) + && DB_CHECK_ACCESS(bkpt->address, BKPT_SIZE, task)) { + inst = db_get_task_value(bkpt->address, BKPT_SIZE, FALSE, + task); + if (inst != BKPT_SET(inst)) { + if (bkpt->flags & BKPT_USR_GLOBAL) { + bkptp = &bkpt->link; + continue; + } + db_force_delete_breakpoint(bkpt, 0, FALSE); + *bkptp = bkpt->link; + db_breakpoint_free(bkpt); + continue; + } + db_put_task_value(bkpt->address, BKPT_SIZE, + bkpt->bkpt_inst, task); + bkpt->flags &= ~BKPT_SET_IN_MEM; + } + bkptp = &bkpt->link; + } + db_breakpoints_inserted = FALSE; + } +} + +/* + * Set a temporary breakpoint. + * The instruction is changed immediately, + * so the breakpoint does not have to be on the breakpoint list. + */ +db_breakpoint_t +db_set_temp_breakpoint(task, addr) + task_t task; + db_addr_t addr; +{ + register db_breakpoint_t bkpt; + + bkpt = db_breakpoint_alloc(); + if (bkpt == 0) { + db_printf("Too many breakpoints.\n"); + return 0; + } + bkpt->task = task; + bkpt->address = addr; + bkpt->flags = BKPT_TEMP; + bkpt->threads = 0; + if (db_add_thread_breakpoint(bkpt, 0, 1, FALSE) < 0) { + if (bkpt) + db_breakpoint_free(bkpt); + db_printf("Too many thread_breakpoints.\n"); + return 0; + } + bkpt->bkpt_inst = db_get_task_value(bkpt->address, BKPT_SIZE, + FALSE, task); + db_put_task_value(bkpt->address, BKPT_SIZE, + BKPT_SET(bkpt->bkpt_inst), task); + return bkpt; +} + +void +db_delete_temp_breakpoint(task, bkpt) + task_t task; + db_breakpoint_t bkpt; +{ + db_put_task_value(bkpt->address, BKPT_SIZE, bkpt->bkpt_inst, task); + db_delete_thread_breakpoint(bkpt, 0); + db_breakpoint_free(bkpt); +} + +/* + * List breakpoints. + */ +void +db_list_breakpoints() +{ + register db_breakpoint_t bkpt; + + if (db_breakpoint_list == 0) { + db_printf("No breakpoints set\n"); + return; + } + + db_printf(" No Space Thread Cnt Address(Cond)\n"); + for (bkpt = db_breakpoint_list; + bkpt != 0; + bkpt = bkpt->link) + { + register db_thread_breakpoint_t tp; + int task_id; + int thread_id; + + if (bkpt->threads) { + for (tp = bkpt->threads; tp; tp = tp->tb_next) { + db_printf("%3d ", tp->tb_number); + if (bkpt->flags & BKPT_USR_GLOBAL) + db_printf("user "); + else if (bkpt->task == TASK_NULL) + db_printf("kernel "); + else if ((task_id = db_lookup_task(bkpt->task)) < 0) + db_printf("%0*X ", 2*sizeof(vm_offset_t), bkpt->task); + else + db_printf("task%-3d ", task_id); + if (tp->tb_task_thd == 0) { + db_printf("all "); + } else { + if (tp->tb_is_task) { + task_id = db_lookup_task((task_t)(tp->tb_task_thd)); + if (task_id < 0) + db_printf("%0*X ", 2*sizeof(vm_offset_t), + tp->tb_task_thd); + else + db_printf("task%03d ", task_id); + } else { + thread_t thd = (thread_t)(tp->tb_task_thd); + task_id = db_lookup_task(thd->task); + thread_id = db_lookup_task_thread(thd->task, thd); + if (task_id < 0 || thread_id < 0) + db_printf("%0*X ", 2*sizeof(vm_offset_t), + tp->tb_task_thd); + else + db_printf("task%03d.%-3d ", task_id, thread_id); + } + } + db_printf("%3d ", tp->tb_init_count); + db_task_printsym(bkpt->address, DB_STGY_PROC, bkpt->task); + if (tp->tb_cond > 0) { + db_printf("("); + db_cond_print(tp); + db_printf(")"); + } + db_printf("\n"); + } + } else { + if (bkpt->task == TASK_NULL) + db_printf(" ? kernel "); + else + db_printf("%*X ", 2*sizeof(vm_offset_t), bkpt->task); + db_printf("(?) "); + db_task_printsym(bkpt->address, DB_STGY_PROC, bkpt->task); + db_printf("\n"); + } + } +} + +/* Delete breakpoint */ +/*ARGSUSED*/ +void +db_delete_cmd() +{ + register n; + thread_t thread; + vm_offset_t task_thd; + boolean_t user_global = FALSE; + boolean_t task_bpt = FALSE; + boolean_t user_space = FALSE; + boolean_t thd_bpt = FALSE; + db_expr_t addr; + int t; + + t = db_read_token(); + if (t == tSLASH) { + t = db_read_token(); + if (t != tIDENT) { + db_printf("Bad modifier \"%s\"\n", db_tok_string); + db_error(0); + } + user_global = db_option(db_tok_string, 'U'); + user_space = (user_global)? TRUE: db_option(db_tok_string, 'u'); + task_bpt = db_option(db_tok_string, 'T'); + thd_bpt = db_option(db_tok_string, 't'); + if (task_bpt && user_global) + db_error("Cannot specify both 'T' and 'U' option\n"); + t = db_read_token(); + } + if (t == tHASH) { + db_thread_breakpoint_t tbp; + db_breakpoint_t bkpt; + + if (db_read_token() != tNUMBER) { + db_printf("Bad break point number #%s\n", db_tok_string); + db_error(0); + } + if ((tbp = db_find_breakpoint_number(db_tok_number, &bkpt)) == 0) { + db_printf("No such break point #%d\n", db_tok_number); + db_error(0); + } + db_delete_breakpoint(bkpt->task, bkpt->address, tbp->tb_task_thd); + return; + } + db_unread_token(t); + if (!db_expression(&addr)) { + /* + * We attempt to pick up the user_space indication from db_dot, + * so that a plain "d" always works. + */ + addr = (db_expr_t)db_dot; + if (!user_space && !DB_VALID_ADDRESS((vm_offset_t)addr, FALSE)) + user_space = TRUE; + } + if (!DB_VALID_ADDRESS((vm_offset_t) addr, user_space)) { + db_printf("Address %#X is not in %s space\n", addr, + (user_space)? "user": "kernel"); + db_error(0); + } + if (thd_bpt || task_bpt) { + for (n = 0; db_get_next_thread(&thread, n); n++) { + if (thread == THREAD_NULL) + db_error("No active thread\n"); + if (task_bpt) { + if (thread->task == TASK_NULL) + db_error("No task\n"); + task_thd = (vm_offset_t) (thread->task); + } else + task_thd = (user_global)? 0: (vm_offset_t) thread; + db_delete_breakpoint(db_target_space(thread, user_space), + (db_addr_t)addr, task_thd); + } + } else { + db_delete_breakpoint(db_target_space(THREAD_NULL, user_space), + (db_addr_t)addr, 0); + } +} + +/* Set breakpoint with skip count */ +/*ARGSUSED*/ +void +db_breakpoint_cmd(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char * modif; +{ + register n; + thread_t thread; + boolean_t user_global = db_option(modif, 'U'); + boolean_t task_bpt = db_option(modif, 'T'); + boolean_t user_space; + + if (count == -1) + count = 1; + + if (!task_bpt && db_option(modif,'t')) + task_bpt = TRUE; + + if (task_bpt && user_global) + db_error("Cannot specify both 'T' and 'U'\n"); + user_space = (user_global)? TRUE: db_option(modif, 'u'); + if (user_space && db_access_level < DB_ACCESS_CURRENT) + db_error("User space break point is not supported\n"); + if (!task_bpt && !DB_VALID_ADDRESS((vm_offset_t)addr, user_space)) { + /* if the user has explicitly specified user space, + do not insert a breakpoint into the kernel */ + if (user_space) + db_error("Invalid user space address\n"); + user_space = TRUE; + db_printf("%#X is in user space\n", addr); + } + if (db_option(modif, 't') || task_bpt) { + for (n = 0; db_get_next_thread(&thread, n); n++) { + if (thread == THREAD_NULL) + db_error("No active thread\n"); + if (task_bpt && thread->task == TASK_NULL) + db_error("No task\n"); + if (db_access_level <= DB_ACCESS_CURRENT && user_space + && thread->task != db_current_task()) + db_error("Cannot set break point in inactive user space\n"); + db_set_breakpoint(db_target_space(thread, user_space), + (db_addr_t)addr, count, + (user_global)? THREAD_NULL: thread, + task_bpt); + } + } else { + db_set_breakpoint(db_target_space(THREAD_NULL, user_space), + (db_addr_t)addr, + count, THREAD_NULL, FALSE); + } +} + +/* list breakpoints */ +void +db_listbreak_cmd() +{ + db_list_breakpoints(); +} + +#endif MACH_KDB diff --git a/ddb/db_break.h b/ddb/db_break.h new file mode 100644 index 0000000..4d4bd2c --- /dev/null +++ b/ddb/db_break.h @@ -0,0 +1,86 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ +#ifndef _DDB_DB_BREAK_H_ +#define _DDB_DB_BREAK_H_ + +#include <machine/db_machdep.h> +#include <kern/thread.h> +#include <kern/task.h> +#include <mach/boolean.h> + +/* + * thread list at the same breakpoint address + */ +struct db_thread_breakpoint { + vm_offset_t tb_task_thd; /* target task or thread */ + boolean_t tb_is_task; /* task qualified */ + short tb_number; /* breakpoint number */ + short tb_init_count; /* skip count(initial value) */ + short tb_count; /* current skip count */ + short tb_cond; /* break condition */ + struct db_thread_breakpoint *tb_next; /* next chain */ +}; + +typedef struct db_thread_breakpoint *db_thread_breakpoint_t; + +/* + * Breakpoint. + */ + +struct db_breakpoint { + task_t task; /* target task */ + db_addr_t address; /* set here */ + db_thread_breakpoint_t threads; /* thread */ + int flags; /* flags: */ +#define BKPT_SINGLE_STEP 0x2 /* to simulate single step */ +#define BKPT_TEMP 0x4 /* temporary */ +#define BKPT_USR_GLOBAL 0x8 /* global user space break point */ +#define BKPT_SET_IN_MEM 0x10 /* break point is set in memory */ +#define BKPT_1ST_SET 0x20 /* 1st time set of user global bkpt */ + vm_size_t bkpt_inst; /* saved instruction at bkpt */ + struct db_breakpoint *link; /* link in in-use or free chain */ +}; + +typedef struct db_breakpoint *db_breakpoint_t; + +extern db_breakpoint_t db_find_breakpoint( task_t task, db_addr_t addr); +extern boolean_t db_find_breakpoint_here( task_t task, db_addr_t addr); +extern void db_set_breakpoints(); +extern void db_clear_breakpoints(); +extern db_thread_breakpoint_t db_find_thread_breakpoint_here + ( task_t task, db_addr_t addr ); +extern db_thread_breakpoint_t db_find_breakpoint_number + ( int num, db_breakpoint_t *bkptp); + +extern db_breakpoint_t db_set_temp_breakpoint( task_t task, db_addr_t addr); +extern void db_delete_temp_breakpoint + ( task_t task, db_breakpoint_t bkpt); + +#endif _DDB_DB_BREAK_H_ diff --git a/ddb/db_command.c b/ddb/db_command.c new file mode 100644 index 0000000..50a02bd --- /dev/null +++ b/ddb/db_command.c @@ -0,0 +1,597 @@ +/* + * Mach Operating System + * Copyright (c) 1991 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#include "mach_kdb.h" +#if MACH_KDB + +/* + * Command dispatcher. + */ +#include <cpus.h> +#include <norma_ipc.h> +#include <norma_vm.h> + +#include <mach/boolean.h> +#include <kern/strings.h> +#include <machine/db_machdep.h> + +#include <ddb/db_lex.h> +#include <ddb/db_output.h> +#include <ddb/db_command.h> +#include <ddb/db_task_thread.h> + +#include <machine/setjmp.h> +#include <kern/thread.h> +#include <ipc/ipc_pset.h> /* 4proto */ +#include <ipc/ipc_port.h> /* 4proto */ + + + +/* + * Exported global variables + */ +boolean_t db_cmd_loop_done; +jmp_buf_t *db_recover = 0; +db_addr_t db_dot; +db_addr_t db_last_addr; +db_addr_t db_prev; +db_addr_t db_next; + +/* + * if 'ed' style: 'dot' is set at start of last item printed, + * and '+' points to next line. + * Otherwise: 'dot' points to next item, '..' points to last. + */ +boolean_t db_ed_style = TRUE; + +/* + * Results of command search. + */ +#define CMD_UNIQUE 0 +#define CMD_FOUND 1 +#define CMD_NONE 2 +#define CMD_AMBIGUOUS 3 +#define CMD_HELP 4 + +/* + * Search for command prefix. + */ +int +db_cmd_search(name, table, cmdp) + char * name; + struct db_command *table; + struct db_command **cmdp; /* out */ +{ + struct db_command *cmd; + int result = CMD_NONE; + + for (cmd = table; cmd->name != 0; cmd++) { + register char *lp; + register char *rp; + register int c; + + lp = name; + rp = cmd->name; + while ((c = *lp) == *rp) { + if (c == 0) { + /* complete match */ + *cmdp = cmd; + return (CMD_UNIQUE); + } + lp++; + rp++; + } + if (c == 0) { + /* end of name, not end of command - + partial match */ + if (result == CMD_FOUND) { + result = CMD_AMBIGUOUS; + /* but keep looking for a full match - + this lets us match single letters */ + } + else { + *cmdp = cmd; + result = CMD_FOUND; + } + } + } + if (result == CMD_NONE) { + /* check for 'help' */ + if (!strncmp(name, "help", strlen(name))) + result = CMD_HELP; + } + return (result); +} + +void +db_cmd_list(table) + struct db_command *table; +{ + register struct db_command *cmd; + + for (cmd = table; cmd->name != 0; cmd++) { + db_printf("%-12s", cmd->name); + db_end_line(); + } +} + +void +db_command(last_cmdp, cmd_table) + struct db_command **last_cmdp; /* IN_OUT */ + struct db_command *cmd_table; +{ + struct db_command *cmd; + int t; + char modif[TOK_STRING_SIZE]; + db_expr_t addr, count; + boolean_t have_addr = FALSE; + int result; + + t = db_read_token(); + if (t == tEOL || t == tSEMI_COLON) { + /* empty line repeats last command, at 'next' */ + cmd = *last_cmdp; + addr = (db_expr_t)db_next; + have_addr = FALSE; + count = 1; + modif[0] = '\0'; + if (t == tSEMI_COLON) + db_unread_token(t); + } + else if (t == tEXCL) { + void db_fncall(); + db_fncall(); + return; + } + else if (t != tIDENT) { + db_printf("?\n"); + db_flush_lex(); + return; + } + else { + /* + * Search for command + */ + while (cmd_table) { + result = db_cmd_search(db_tok_string, + cmd_table, + &cmd); + switch (result) { + case CMD_NONE: + if (db_exec_macro(db_tok_string) == 0) + return; + db_printf("No such command \"%s\"\n", db_tok_string); + db_flush_lex(); + return; + case CMD_AMBIGUOUS: + db_printf("Ambiguous\n"); + db_flush_lex(); + return; + case CMD_HELP: + db_cmd_list(cmd_table); + db_flush_lex(); + return; + default: + break; + } + if ((cmd_table = cmd->more) != 0) { + t = db_read_token(); + if (t != tIDENT) { + db_cmd_list(cmd_table); + db_flush_lex(); + return; + } + } + } + + if ((cmd->flag & CS_OWN) == 0) { + /* + * Standard syntax: + * command [/modifier] [addr] [,count] + */ + t = db_read_token(); + if (t == tSLASH) { + t = db_read_token(); + if (t != tIDENT) { + db_printf("Bad modifier \"/%s\"\n", db_tok_string); + db_flush_lex(); + return; + } + db_strcpy(modif, db_tok_string); + } + else { + db_unread_token(t); + modif[0] = '\0'; + } + + if (db_expression(&addr)) { + db_dot = (db_addr_t) addr; + db_last_addr = db_dot; + have_addr = TRUE; + } + else { + addr = (db_expr_t) db_dot; + have_addr = FALSE; + } + t = db_read_token(); + if (t == tCOMMA) { + if (!db_expression(&count)) { + db_printf("Count missing after ','\n"); + db_flush_lex(); + return; + } + } + else { + db_unread_token(t); + count = -1; + } + } + } + *last_cmdp = cmd; + if (cmd != 0) { + /* + * Execute the command. + */ + (*cmd->fcn)(addr, have_addr, count, modif); + + if (cmd->flag & CS_SET_DOT) { + /* + * If command changes dot, set dot to + * previous address displayed (if 'ed' style). + */ + if (db_ed_style) { + db_dot = db_prev; + } + else { + db_dot = db_next; + } + } + else { + /* + * If command does not change dot, + * set 'next' location to be the same. + */ + db_next = db_dot; + } + } +} + +void +db_command_list(last_cmdp, cmd_table) + struct db_command **last_cmdp; /* IN_OUT */ + struct db_command *cmd_table; +{ + void db_skip_to_eol(); + + do { + db_command(last_cmdp, cmd_table); + db_skip_to_eol(); + } while (db_read_token() == tSEMI_COLON && db_cmd_loop_done == 0); +} + +/* + * 'show' commands + */ +extern void db_listbreak_cmd(); +extern void db_listwatch_cmd(); +extern void db_show_regs(), db_show_one_thread(), db_show_one_task(); +extern void db_show_all_threads(); +extern void db_show_macro(); +extern void vm_map_print(), vm_object_print(), vm_page_print(); +extern void vm_map_copy_print(); +extern void ipc_port_print(), ipc_pset_print(), db_show_all_slocks(); +extern void ipc_kmsg_print(), ipc_msg_print(); +extern void db_show_port_id(); +void db_show_help(); +#if NORMA_IPC +extern void netipc_packet_print(), netipc_pcs_print(), db_show_all_uids(); +extern void db_show_all_proxies(), db_show_all_principals(); +extern void db_show_all_uids_verbose(); +#endif NORMA_IPC +#if NORMA_VM +extern void xmm_obj_print(), xmm_reply_print(); +#endif NORMA_VM + +struct db_command db_show_all_cmds[] = { + { "threads", db_show_all_threads, 0, 0 }, + { "slocks", db_show_all_slocks, 0, 0 }, +#if NORMA_IPC + { "uids", db_show_all_uids, 0, 0 }, + { "proxies", db_show_all_proxies, 0, 0 }, + { "principals", db_show_all_principals, 0, 0 }, + { "vuids", db_show_all_uids_verbose, 0, 0 }, +#endif NORMA_IPC + { (char *)0 } +}; + +struct db_command db_show_cmds[] = { + { "all", 0, 0, db_show_all_cmds }, + { "registers", db_show_regs, 0, 0 }, + { "breaks", db_listbreak_cmd, 0, 0 }, + { "watches", db_listwatch_cmd, 0, 0 }, + { "thread", db_show_one_thread, 0, 0 }, + { "task", db_show_one_task, 0, 0 }, + { "macro", db_show_macro, CS_OWN, 0 }, + { "map", vm_map_print, 0, 0 }, + { "object", vm_object_print, 0, 0 }, + { "page", vm_page_print, 0, 0 }, + { "copy", vm_map_copy_print, 0, 0 }, + { "port", ipc_port_print, 0, 0 }, + { "pset", ipc_pset_print, 0, 0 }, + { "kmsg", ipc_kmsg_print, 0, 0 }, + { "msg", ipc_msg_print, 0, 0 }, + { "ipc_port", db_show_port_id, 0, 0 }, +#if NORMA_IPC + { "packet", netipc_packet_print, 0, 0 }, + { "pcs", netipc_pcs_print, 0, 0 }, +#endif NORMA_IPC +#if NORMA_VM + { "xmm_obj", xmm_obj_print, 0, 0 }, + { "xmm_reply", xmm_reply_print, 0, 0 }, +#endif NORMA_VM + { (char *)0, } +}; + +extern void db_print_cmd(), db_examine_cmd(), db_set_cmd(); +extern void db_examine_forward(), db_examine_backward(); +extern void db_search_cmd(); +extern void db_write_cmd(); +extern void db_delete_cmd(), db_breakpoint_cmd(); +extern void db_deletewatch_cmd(), db_watchpoint_cmd(); +extern void db_single_step_cmd(), db_trace_until_call_cmd(), + db_trace_until_matching_cmd(), db_continue_cmd(); +extern void db_stack_trace_cmd(), db_cond_cmd(); +void db_help_cmd(); +void db_def_macro_cmd(), db_del_macro_cmd(); +void db_fncall(); +extern void db_reset_cpu(); + +struct db_command db_command_table[] = { +#ifdef DB_MACHINE_COMMANDS + /* this must be the first entry, if it exists */ + { "machine", 0, 0, 0}, +#endif + { "print", db_print_cmd, CS_OWN, 0 }, + { "examine", db_examine_cmd, CS_MORE|CS_SET_DOT, 0 }, + { "x", db_examine_cmd, CS_MORE|CS_SET_DOT, 0 }, + { "xf", db_examine_forward, CS_SET_DOT, 0 }, + { "xb", db_examine_backward, CS_SET_DOT, 0 }, + { "search", db_search_cmd, CS_OWN|CS_SET_DOT, 0 }, + { "set", db_set_cmd, CS_OWN, 0 }, + { "write", db_write_cmd, CS_MORE|CS_SET_DOT, 0 }, + { "w", db_write_cmd, CS_MORE|CS_SET_DOT, 0 }, + { "delete", db_delete_cmd, CS_OWN, 0 }, + { "d", db_delete_cmd, CS_OWN, 0 }, + { "break", db_breakpoint_cmd, CS_MORE, 0 }, + { "dwatch", db_deletewatch_cmd, CS_MORE, 0 }, + { "watch", db_watchpoint_cmd, CS_MORE, 0 }, + { "step", db_single_step_cmd, 0, 0 }, + { "s", db_single_step_cmd, 0, 0 }, + { "continue", db_continue_cmd, 0, 0 }, + { "c", db_continue_cmd, 0, 0 }, + { "until", db_trace_until_call_cmd,0, 0 }, + { "next", db_trace_until_matching_cmd,0, 0 }, + { "match", db_trace_until_matching_cmd,0, 0 }, + { "trace", db_stack_trace_cmd, 0, 0 }, + { "cond", db_cond_cmd, CS_OWN, 0 }, + { "call", db_fncall, CS_OWN, 0 }, + { "macro", db_def_macro_cmd, CS_OWN, 0 }, + { "dmacro", db_del_macro_cmd, CS_OWN, 0 }, + { "show", 0, 0, db_show_cmds }, + { "reset", db_reset_cpu, 0, 0 }, + { "reboot", db_reset_cpu, 0, 0 }, + { (char *)0, } +}; + +#ifdef DB_MACHINE_COMMANDS + +/* this function should be called to install the machine dependent + commands. It should be called before the debugger is enabled */ +void db_machine_commands_install(ptr) +struct db_command *ptr; +{ + db_command_table[0].more = ptr; + return; +} + +#endif + + +struct db_command *db_last_command = 0; + +void +db_help_cmd() +{ + struct db_command *cmd = db_command_table; + + while (cmd->name != 0) { + db_printf("%-12s", cmd->name); + db_end_line(); + cmd++; + } +} + +int (*ddb_display)(); + +void +db_command_loop() +{ + jmp_buf_t db_jmpbuf; + jmp_buf_t *prev = db_recover; + extern int db_output_line; + extern int db_macro_level; +#if NORMA_IPC + extern int _node_self; /* node_self() may not be callable yet */ +#endif NORMA_IPC + + /* + * Initialize 'prev' and 'next' to dot. + */ + db_prev = db_dot; + db_next = db_dot; + + if (ddb_display) + (*ddb_display)(); + + db_cmd_loop_done = 0; + while (!db_cmd_loop_done) { + (void) _setjmp(db_recover = &db_jmpbuf); + db_macro_level = 0; + if (db_print_position() != 0) + db_printf("\n"); + db_output_line = 0; + db_printf("db%s", (db_default_thread)? "t": ""); +#if NORMA_IPC + db_printf("%d", _node_self); +#endif +#if NCPUS > 1 + db_printf("{%d}", cpu_number()); +#endif + db_printf("> "); + + (void) db_read_line("!!"); + db_command_list(&db_last_command, db_command_table); + } + + db_recover = prev; +} + +boolean_t +db_exec_cmd_nest(cmd, size) + char *cmd; + int size; +{ + struct db_lex_context lex_context; + + db_cmd_loop_done = 0; + if (cmd) { + db_save_lex_context(&lex_context); + db_switch_input(cmd, size /**OLD, &lex_context OLD**/); + } + db_command_list(&db_last_command, db_command_table); + if (cmd) + db_restore_lex_context(&lex_context); + return(db_cmd_loop_done == 0); +} + +#ifdef __GNUC__ +extern __volatile__ void _longjmp(); +#endif + +void db_error(s) + char *s; +{ + extern int db_macro_level; + + db_macro_level = 0; + if (db_recover) { + if (s) + db_printf(s); + db_flush_lex(); + _longjmp(db_recover, 1); + } + else + { + if (s) + db_printf(s); + panic("db_error"); + } +} + +/* + * Call random function: + * !expr(arg,arg,arg) + */ +void +db_fncall() +{ + db_expr_t fn_addr; +#define MAXARGS 11 + db_expr_t args[MAXARGS]; + int nargs = 0; + db_expr_t retval; + db_expr_t (*func)(); + int t; + + if (!db_expression(&fn_addr)) { + db_printf("Bad function \"%s\"\n", db_tok_string); + db_flush_lex(); + return; + } + func = (db_expr_t (*) ()) fn_addr; + + t = db_read_token(); + if (t == tLPAREN) { + if (db_expression(&args[0])) { + nargs++; + while ((t = db_read_token()) == tCOMMA) { + if (nargs == MAXARGS) { + db_printf("Too many arguments\n"); + db_flush_lex(); + return; + } + if (!db_expression(&args[nargs])) { + db_printf("Argument missing\n"); + db_flush_lex(); + return; + } + nargs++; + } + db_unread_token(t); + } + if (db_read_token() != tRPAREN) { + db_printf("?\n"); + db_flush_lex(); + return; + } + } + while (nargs < MAXARGS) { + args[nargs++] = 0; + } + + retval = (*func)(args[0], args[1], args[2], args[3], args[4], + args[5], args[6], args[7], args[8], args[9] ); + db_printf(" %#N\n", retval); +} + +boolean_t +db_option(modif, option) + char *modif; + int option; +{ + register char *p; + + for (p = modif; *p; p++) + if (*p == option) + return(TRUE); + return(FALSE); +} + +#endif MACH_KDB diff --git a/ddb/db_command.h b/ddb/db_command.h new file mode 100644 index 0000000..60762b2 --- /dev/null +++ b/ddb/db_command.h @@ -0,0 +1,70 @@ +/* + * Mach Operating System + * Copyright (c) 1992,1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ +#include "mach_kdb.h" +#if MACH_KDB + +/* + * Command loop declarations. + */ + +#include <machine/db_machdep.h> +#include <machine/setjmp.h> + +extern void db_command_loop(); +extern boolean_t db_exec_conditional_cmd(); +extern boolean_t db_option(/* char *, int */); + +extern void db_error(/* char * */); /* report error */ + +extern db_addr_t db_dot; /* current location */ +extern db_addr_t db_last_addr; /* last explicit address typed */ +extern db_addr_t db_prev; /* last address examined + or written */ +extern db_addr_t db_next; /* next address to be examined + or written */ +extern jmp_buf_t * db_recover; /* error recovery */ + +extern jmp_buf_t * db_recover; /* error recovery */ + +/* + * Command table + */ +struct db_command { + char * name; /* command name */ + void (*fcn)(); /* function to call */ + int flag; /* extra info: */ +#define CS_OWN 0x1 /* non-standard syntax */ +#define CS_MORE 0x2 /* standard syntax, but may have other + words at end */ +#define CS_SET_DOT 0x100 /* set dot after command */ + struct db_command *more; /* another level of command */ +}; + +#endif MACH_KDB diff --git a/ddb/db_cond.c b/ddb/db_cond.c new file mode 100644 index 0000000..c78f03c --- /dev/null +++ b/ddb/db_cond.c @@ -0,0 +1,181 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +#include "mach_kdb.h" +#if MACH_KDB + +#include <machine/db_machdep.h> +#include <machine/setjmp.h> + +#include <ddb/db_lex.h> +#include <ddb/db_break.h> +#include <ddb/db_command.h> + + + +#define DB_MAX_COND 10 /* maximum conditions to be set */ + +int db_ncond_free = DB_MAX_COND; /* free condition */ +struct db_cond { + int c_size; /* size of cond */ + char c_cond_cmd[DB_LEX_LINE_SIZE]; /* cond & cmd */ +} db_cond[DB_MAX_COND]; + +void +db_cond_free(bkpt) + db_thread_breakpoint_t bkpt; +{ + if (bkpt->tb_cond > 0) { + db_cond[bkpt->tb_cond-1].c_size = 0; + db_ncond_free++; + bkpt->tb_cond = 0; + } +} + +boolean_t +db_cond_check(bkpt) + db_thread_breakpoint_t bkpt; +{ + register struct db_cond *cp; + db_expr_t value; + int t; + jmp_buf_t db_jmpbuf; + extern jmp_buf_t *db_recover; + + if (bkpt->tb_cond <= 0) /* no condition */ + return(TRUE); + db_dot = PC_REGS(DDB_REGS); + db_prev = db_dot; + db_next = db_dot; + if (_setjmp(db_recover = &db_jmpbuf)) { + /* + * in case of error, return true to enter interactive mode + */ + return(TRUE); + } + + /* + * switch input, and evalutate condition + */ + cp = &db_cond[bkpt->tb_cond - 1]; + db_switch_input(cp->c_cond_cmd, cp->c_size); + if (!db_expression(&value)) { + db_printf("error: condition evaluation error\n"); + return(TRUE); + } + if (value == 0 || --(bkpt->tb_count) > 0) + return(FALSE); + + /* + * execute a command list if exist + */ + bkpt->tb_count = bkpt->tb_init_count; + if ((t = db_read_token()) != tEOL) { + db_unread_token(t); + return(db_exec_cmd_nest(0, 0)); + } + return(TRUE); +} + +void +db_cond_print(bkpt) + db_thread_breakpoint_t bkpt; +{ + register char *p, *ep; + register struct db_cond *cp; + + if (bkpt->tb_cond <= 0) + return; + cp = &db_cond[bkpt->tb_cond-1]; + p = cp->c_cond_cmd; + ep = p + cp->c_size; + while (p < ep) { + if (*p == '\n' || *p == 0) + break; + db_putchar(*p++); + } +} + +void +db_cond_cmd() +{ + register c; + register struct db_cond *cp; + register char *p; + db_expr_t value; + db_thread_breakpoint_t bkpt; + + if (db_read_token() != tHASH || db_read_token() != tNUMBER) { + db_printf("#<number> expected instead of \"%s\"\n", db_tok_string); + db_error(0); + return; + } + if ((bkpt = db_find_breakpoint_number(db_tok_number, 0)) == 0) { + db_printf("No such break point #%d\n", db_tok_number); + db_error(0); + return; + } + /* + * if the break point already has a condition, free it first + */ + if (bkpt->tb_cond > 0) { + cp = &db_cond[bkpt->tb_cond - 1]; + db_cond_free(bkpt); + } else { + if (db_ncond_free <= 0) { + db_error("Too many conditions\n"); + return; + } + for (cp = db_cond; cp < &db_cond[DB_MAX_COND]; cp++) + if (cp->c_size == 0) + break; + if (cp >= &db_cond[DB_MAX_COND]) + panic("bad db_cond_free"); + } + for (c = db_read_char(); c == ' ' || c == '\t'; c = db_read_char()); + for (p = cp->c_cond_cmd; c >= 0; c = db_read_char()) + *p++ = c; + /* + * switch to saved data and call db_expression to check the condition. + * If no condition is supplied, db_expression will return false. + * In this case, clear previous condition of the break point. + * If condition is supplied, set the condition to the permanent area. + * Note: db_expression will not return here, if the condition + * expression is wrong. + */ + db_switch_input(cp->c_cond_cmd, p - cp->c_cond_cmd); + if (!db_expression(&value)) { + /* since condition is already freed, do nothing */ + db_flush_lex(); + return; + } + db_flush_lex(); + db_ncond_free--; + cp->c_size = p - cp->c_cond_cmd; + bkpt->tb_cond = (cp - db_cond) + 1; +} + +#endif MACH_KDB diff --git a/ddb/db_examine.c b/ddb/db_examine.c new file mode 100644 index 0000000..c996fd1 --- /dev/null +++ b/ddb/db_examine.c @@ -0,0 +1,506 @@ +/* + * Mach Operating System + * Copyright (c) 1992,1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ +#include "mach_kdb.h" +#if MACH_KDB + +#include <mach/boolean.h> +#include <machine/db_machdep.h> + +#include <ddb/db_access.h> +#include <ddb/db_lex.h> +#include <ddb/db_output.h> +#include <ddb/db_command.h> +#include <ddb/db_sym.h> +#include <ddb/db_task_thread.h> +#include <kern/thread.h> +#include <kern/task.h> +#include <mach/vm_param.h> + +#define db_thread_to_task(thread) ((thread)? thread->task: TASK_NULL) + +char db_examine_format[TOK_STRING_SIZE] = "x"; +int db_examine_count = 1; +db_addr_t db_examine_prev_addr = 0; +thread_t db_examine_thread = THREAD_NULL; + +extern db_addr_t db_disasm(db_addr_t pc, boolean_t altform, task_t task); + /* instruction disassembler */ + +void db_examine();/*forwards*/ +void db_strcpy(); + +/* + * Examine (print) data. + */ +/*ARGSUSED*/ +void +db_examine_cmd(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char * modif; +{ + thread_t thread; + boolean_t db_option(); + + if (modif[0] != '\0') + db_strcpy(db_examine_format, modif); + + if (count == -1) + count = 1; + db_examine_count = count; + if (db_option(modif, 't')) + { + if (!db_get_next_thread(&thread, 0)) + return; + } + else + if (db_option(modif,'u')) + thread = current_thread(); + else + thread = THREAD_NULL; + + db_examine_thread = thread; + db_examine((db_addr_t) addr, db_examine_format, count, + db_thread_to_task(thread)); +} + +/* ARGSUSED */ +void +db_examine_forward(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char * modif; +{ + db_examine(db_next, db_examine_format, db_examine_count, + db_thread_to_task(db_examine_thread)); +} + +/* ARGSUSED */ +void +db_examine_backward(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char * modif; +{ + + db_examine(db_examine_prev_addr - (db_next - db_examine_prev_addr), + db_examine_format, db_examine_count, + db_thread_to_task(db_examine_thread)); +} + +void +db_examine(addr, fmt, count, task) + register + db_addr_t addr; + char * fmt; /* format string */ + int count; /* repeat count */ + task_t task; +{ + int c; + db_expr_t value; + int size; /* in bytes */ + int width; + char * fp; + + db_examine_prev_addr = addr; + while (--count >= 0) { + fp = fmt; + size = sizeof(int); + width = 4*size; + while ((c = *fp++) != 0) { + switch (c) { + case 'b': + size = sizeof(char); + width = 4*size; + break; + case 'h': + size = sizeof(short); + width = 4*size; + break; + case 'l': + size = sizeof(long); + width = 4*size; + break; + case 'a': /* address */ + case 'A': /* function address */ + /* always forces a new line */ + if (db_print_position() != 0) + db_printf("\n"); + db_prev = addr; + db_task_printsym(addr, + (c == 'a')?DB_STGY_ANY:DB_STGY_PROC, + task); + db_printf(":\t"); + break; + case 'm': + db_next = db_xcdump(addr, size, count+1, task); + return; + default: + if (db_print_position() == 0) { + /* If we hit a new symbol, print it */ + char * name; + db_addr_t off; + + db_find_task_sym_and_offset(addr,&name,&off,task); + if (off == 0) + db_printf("%s:\t", name); + else + db_printf("\t\t"); + + db_prev = addr; + } + + switch (c) { + case ',': /* skip one unit w/o printing */ + addr += size; + break; + + case 'r': /* signed, current radix */ + value = db_get_task_value(addr,size,TRUE,task); + addr += size; + db_printf("%-*R", width, value); + break; + case 'x': /* unsigned hex */ + value = db_get_task_value(addr,size,FALSE,task); + addr += size; + db_printf("%-*X", width, value); + break; + case 'z': /* signed hex */ + value = db_get_task_value(addr,size,TRUE,task); + addr += size; + db_printf("%-*Z", width, value); + break; + case 'd': /* signed decimal */ + value = db_get_task_value(addr,size,TRUE,task); + addr += size; + db_printf("%-*D", width, value); + break; + case 'U': /* unsigned decimal */ + value = db_get_task_value(addr,size,FALSE,task); + addr += size; + db_printf("%-*U", width, value); + break; + case 'o': /* unsigned octal */ + value = db_get_task_value(addr,size,FALSE,task); + addr += size; + db_printf("%-*O", width, value); + break; + case 'c': /* character */ + value = db_get_task_value(addr,1,FALSE,task); + addr += 1; + if (value >= ' ' && value <= '~') + db_printf("%c", value); + else + db_printf("\\%03o", value); + break; + case 's': /* null-terminated string */ + for (;;) { + value = db_get_task_value(addr,1,FALSE,task); + addr += 1; + if (value == 0) + break; + if (value >= ' ' && value <= '~') + db_printf("%c", value); + else + db_printf("\\%03o", value); + } + break; + case 'i': /* instruction */ + addr = db_disasm(addr, FALSE, task); + break; + case 'I': /* instruction, alternate form */ + addr = db_disasm(addr, TRUE, task); + break; + default: + break; + } + if (db_print_position() != 0) + db_end_line(); + break; + } + } + } + db_next = addr; +} + +/* + * Print value. + */ +char db_print_format = 'x'; + +/*ARGSUSED*/ +void +db_print_cmd() +{ + db_expr_t value; + int t; + task_t task = TASK_NULL; + + if ((t = db_read_token()) == tSLASH) { + if (db_read_token() != tIDENT) { + db_printf("Bad modifier \"/%s\"\n", db_tok_string); + db_error(0); + /* NOTREACHED */ + } + if (db_tok_string[0]) + db_print_format = db_tok_string[0]; + if (db_option(db_tok_string, 't') && db_default_thread) + task = db_default_thread->task; + } else + db_unread_token(t); + + for ( ; ; ) { + t = db_read_token(); + if (t == tSTRING) { + db_printf("%s", db_tok_string); + continue; + } + db_unread_token(t); + if (!db_expression(&value)) + break; + switch (db_print_format) { + case 'a': + db_task_printsym((db_addr_t)value, DB_STGY_ANY, task); + break; + case 'r': + db_printf("%*r", 3+2*sizeof(db_expr_t), value); + break; + case 'x': + db_printf("%*x", 2*sizeof(db_expr_t), value); + break; + case 'z': + db_printf("%*z", 2*sizeof(db_expr_t), value); + break; + case 'd': + db_printf("%*d", 3+2*sizeof(db_expr_t), value); + break; + case 'u': + db_printf("%*u", 3+2*sizeof(db_expr_t), value); + break; + case 'o': + db_printf("%o", 4*sizeof(db_expr_t), value); + break; + case 'c': + value = value & 0xFF; + if (value >= ' ' && value <= '~') + db_printf("%c", value); + else + db_printf("\\%03o", value); + break; + default: + db_printf("Unknown format %c\n", db_print_format); + db_print_format = 'x'; + db_error(0); + } + } +} + +void +db_print_loc_and_inst(loc, task) + db_addr_t loc; + task_t task; +{ + db_task_printsym(loc, DB_STGY_PROC, task); + db_printf(":\t"); + (void) db_disasm(loc, TRUE, task); +} + +void +db_strcpy(dst, src) + register char *dst; + register char *src; +{ + while (*dst++ = *src++) + ; +} + +void db_search(); /*forward*/ +/* + * Search for a value in memory. + * Syntax: search [/bhl] addr value [mask] [,count] [thread] + */ +void +db_search_cmd() +{ + int t; + db_addr_t addr; + int size = 0; + db_expr_t value; + db_expr_t mask; + db_addr_t count; + thread_t thread; + boolean_t thread_flag = FALSE; + register char *p; + + t = db_read_token(); + if (t == tSLASH) { + t = db_read_token(); + if (t != tIDENT) { + bad_modifier: + db_printf("Bad modifier \"/%s\"\n", db_tok_string); + db_flush_lex(); + return; + } + + for (p = db_tok_string; *p; p++) { + switch(*p) { + case 'b': + size = sizeof(char); + break; + case 'h': + size = sizeof(short); + break; + case 'l': + size = sizeof(long); + break; + case 't': + thread_flag = TRUE; + break; + default: + goto bad_modifier; + } + } + } else { + db_unread_token(t); + size = sizeof(int); + } + + if (!db_expression(&addr)) { + db_printf("Address missing\n"); + db_flush_lex(); + return; + } + + if (!db_expression(&value)) { + db_printf("Value missing\n"); + db_flush_lex(); + return; + } + + if (!db_expression(&mask)) + mask = ~0; + + t = db_read_token(); + if (t == tCOMMA) { + if (!db_expression(&count)) { + db_printf("Count missing\n"); + db_flush_lex(); + return; + } + } else { + db_unread_token(t); + count = -1; /* effectively forever */ + } + if (thread_flag) { + if (!db_get_next_thread(&thread, 0)) + return; + } else + thread = THREAD_NULL; + + db_search(addr, size, value, mask, count, db_thread_to_task(thread)); +} + +void +db_search(addr, size, value, mask, count, task) + register + db_addr_t addr; + int size; + db_expr_t value; + db_expr_t mask; + unsigned int count; + task_t task; +{ + while (count-- != 0) { + db_prev = addr; + if ((db_get_task_value(addr,size,FALSE,task) & mask) == value) + break; + addr += size; + } + db_next = addr; +} + +#define DB_XCDUMP_NC 16 + +int +db_xcdump(addr, size, count, task) + db_addr_t addr; + int size; + int count; + task_t task; +{ + register i, n; + db_expr_t value; + int bcount; + db_addr_t off; + char *name; + char data[DB_XCDUMP_NC]; + + db_find_task_sym_and_offset(addr, &name, &off, task); + for (n = count*size; n > 0; n -= bcount) { + db_prev = addr; + if (off == 0) { + db_printf("%s:\n", name); + off = -1; + } + db_printf("%0*X:%s", 2*sizeof(db_addr_t), addr, + (size != 1)? " ": ""); + bcount = ((n > DB_XCDUMP_NC)? DB_XCDUMP_NC: n); + if (trunc_page(addr) != trunc_page(addr+bcount-1)) { + db_addr_t next_page_addr = trunc_page(addr+bcount-1); + if (!DB_CHECK_ACCESS(next_page_addr, sizeof(int), task)) + bcount = next_page_addr - addr; + } + db_read_bytes((char *)addr, bcount, data, task); + for (i = 0; i < bcount && off != 0; i += size) { + if (i % 4 == 0) + db_printf(" "); + value = db_get_task_value(addr, size, FALSE, task); + db_printf("%0*x ", size*2, value); + addr += size; + db_find_task_sym_and_offset(addr, &name, &off, task); + } + db_printf("%*s", + ((DB_XCDUMP_NC-i)/size)*(size*2+1)+(DB_XCDUMP_NC-i)/4, + ""); + bcount = i; + db_printf("%s*", (size != 1)? " ": ""); + for (i = 0; i < bcount; i++) { + value = data[i]; + db_printf("%c", (value >= ' ' && value <= '~')? value: '.'); + } + db_printf("*\n"); + } + return(addr); +} + +#endif MACH_KDB diff --git a/ddb/db_expr.c b/ddb/db_expr.c new file mode 100644 index 0000000..b9848bb --- /dev/null +++ b/ddb/db_expr.c @@ -0,0 +1,391 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ +#include "mach_kdb.h" +#if MACH_KDB + +#include <mach/boolean.h> +#include <machine/db_machdep.h> +#include <ddb/db_lex.h> +#include <ddb/db_access.h> +#include <ddb/db_command.h> +#include <kern/task.h> + + +boolean_t +db_term(valuep) + db_expr_t *valuep; +{ + int t; + + switch(t = db_read_token()) { + case tIDENT: + if (!db_value_of_name(db_tok_string, valuep)) { + db_printf("Symbol \"%s\" not found\n", db_tok_string); + db_error(0); + /*NOTREACHED*/ + } + return (TRUE); + case tNUMBER: + *valuep = db_tok_number; + return (TRUE); + case tDOT: + *valuep = (db_expr_t)db_dot; + return (TRUE); + case tDOTDOT: + *valuep = (db_expr_t)db_prev; + return (TRUE); + case tPLUS: + *valuep = (db_expr_t) db_next; + return (TRUE); + case tQUOTE: + *valuep = (db_expr_t)db_last_addr; + return (TRUE); + case tDOLLAR: + if (!db_get_variable(valuep)) + return (FALSE); + return (TRUE); + case tLPAREN: + if (!db_expression(valuep)) { + db_error("Unmached ()s\n"); + /*NOTREACHED*/ + } + t = db_read_token(); + if (t != tRPAREN) { + db_printf("')' expected at \"%s...\"\n", db_tok_string); + db_error(0); + /*NOTREACHED*/ + } + return (TRUE); + default: + db_unread_token(t); + return (FALSE); + } +} + +int +db_size_option(modif, u_option, t_option) + char *modif; + boolean_t *u_option; + boolean_t *t_option; +{ + register char *p; + int size = sizeof(int); + + *u_option = FALSE; + *t_option = FALSE; + for (p = modif; *p; p++) { + switch(*p) { + case 'b': + size = sizeof(char); + break; + case 'h': + size = sizeof(short); + break; + case 'l': + size = sizeof(long); + break; + case 'u': + *u_option = TRUE; + break; + case 't': + *t_option = TRUE; + break; + } + } + return(size); +} + +boolean_t +db_unary(valuep) + db_expr_t *valuep; +{ + int t; + int size; + boolean_t u_opt, t_opt; + task_t task; + extern task_t db_default_task; + + t = db_read_token(); + if (t == tMINUS) { + if (!db_unary(valuep)) { + db_error("Expression syntax error after '-'\n"); + /*NOTREACHED*/ + } + *valuep = -*valuep; + return (TRUE); + } + if (t == tSTAR) { + /* indirection */ + if (!db_unary(valuep)) { + db_error("Expression syntax error after '*'\n"); + /*NOTREACHED*/ + } + task = TASK_NULL; + size = sizeof(db_addr_t); + u_opt = FALSE; + t = db_read_token(); + if (t == tIDENT && db_tok_string[0] == ':') { + size = db_size_option(&db_tok_string[1], &u_opt, &t_opt); + if (t_opt) + task = db_default_task; + } else + db_unread_token(t); + *valuep = db_get_task_value((db_addr_t)*valuep, size, !u_opt, task); + return (TRUE); + } + if (t == tEXCL) { + if (!db_unary(valuep)) { + db_error("Expression syntax error after '!'\n"); + /*NOTREACHED*/ + } + *valuep = (!(*valuep)); + return (TRUE); + } + db_unread_token(t); + return (db_term(valuep)); +} + +boolean_t +db_mult_expr(valuep) + db_expr_t *valuep; +{ + db_expr_t lhs, rhs; + int t; + char c; + + if (!db_unary(&lhs)) + return (FALSE); + + t = db_read_token(); + while (t == tSTAR || t == tSLASH || t == tPCT || t == tHASH + || t == tBIT_AND) { + c = db_tok_string[0]; + if (!db_term(&rhs)) { + db_printf("Expression syntax error after '%c'\n", c); + db_error(0); + /*NOTREACHED*/ + } + switch(t) { + case tSTAR: + lhs *= rhs; + break; + case tBIT_AND: + lhs &= rhs; + break; + default: + if (rhs == 0) { + db_error("Divide by 0\n"); + /*NOTREACHED*/ + } + if (t == tSLASH) + lhs /= rhs; + else if (t == tPCT) + lhs %= rhs; + else + lhs = ((lhs+rhs-1)/rhs)*rhs; + } + t = db_read_token(); + } + db_unread_token(t); + *valuep = lhs; + return (TRUE); +} + +boolean_t +db_add_expr(valuep) + db_expr_t *valuep; +{ + db_expr_t lhs, rhs; + int t; + char c; + + if (!db_mult_expr(&lhs)) + return (FALSE); + + t = db_read_token(); + while (t == tPLUS || t == tMINUS || t == tBIT_OR) { + c = db_tok_string[0]; + if (!db_mult_expr(&rhs)) { + db_printf("Expression syntax error after '%c'\n", c); + db_error(0); + /*NOTREACHED*/ + } + if (t == tPLUS) + lhs += rhs; + else if (t == tMINUS) + lhs -= rhs; + else + lhs |= rhs; + t = db_read_token(); + } + db_unread_token(t); + *valuep = lhs; + return (TRUE); +} + +boolean_t +db_shift_expr(valuep) + db_expr_t *valuep; +{ + db_expr_t lhs, rhs; + int t; + + if (!db_add_expr(&lhs)) + return (FALSE); + + t = db_read_token(); + while (t == tSHIFT_L || t == tSHIFT_R) { + if (!db_add_expr(&rhs)) { + db_printf("Expression syntax error after \"%s\"\n", + (t == tSHIFT_L)? "<<": ">>"); + db_error(0); + /*NOTREACHED*/ + } + if (rhs < 0) { + db_error("Negative shift amount\n"); + /*NOTREACHED*/ + } + if (t == tSHIFT_L) + lhs <<= rhs; + else { + /* Shift right is unsigned */ + lhs = (natural_t) lhs >> rhs; + } + t = db_read_token(); + } + db_unread_token(t); + *valuep = lhs; + return (TRUE); +} + +boolean_t +db_logical_relation_expr(valuep) + db_expr_t *valuep; +{ + db_expr_t lhs, rhs; + int t; + char op[3]; + + if (!db_shift_expr(&lhs)) + return(FALSE); + + t = db_read_token(); + while (t == tLOG_EQ || t == tLOG_NOT_EQ + || t == tGREATER || t == tGREATER_EQ + || t == tLESS || t == tLESS_EQ) { + op[0] = db_tok_string[0]; + op[1] = db_tok_string[1]; + op[2] = 0; + if (!db_shift_expr(&rhs)) { + db_printf("Expression syntax error after \"%s\"\n", op); + db_error(0); + /*NOTREACHED*/ + } + switch(t) { + case tLOG_EQ: + lhs = (lhs == rhs); + break; + case tLOG_NOT_EQ: + lhs = (lhs != rhs); + break; + case tGREATER: + lhs = (lhs > rhs); + break; + case tGREATER_EQ: + lhs = (lhs >= rhs); + break; + case tLESS: + lhs = (lhs < rhs); + break; + case tLESS_EQ: + lhs = (lhs <= rhs); + break; + } + t = db_read_token(); + } + db_unread_token(t); + *valuep = lhs; + return (TRUE); +} + +boolean_t +db_logical_and_expr(valuep) + db_expr_t *valuep; +{ + db_expr_t lhs, rhs; + int t; + + if (!db_logical_relation_expr(&lhs)) + return(FALSE); + + t = db_read_token(); + while (t == tLOG_AND) { + if (!db_logical_relation_expr(&rhs)) { + db_error("Expression syntax error after \"&&\"\n"); + /*NOTREACHED*/ + } + lhs = (lhs && rhs); + } + db_unread_token(t); + *valuep = lhs; + return (TRUE); +} + +boolean_t +db_logical_or_expr(valuep) + db_expr_t *valuep; +{ + db_expr_t lhs, rhs; + int t; + + if (!db_logical_and_expr(&lhs)) + return(FALSE); + + t = db_read_token(); + while (t == tLOG_OR) { + if (!db_logical_and_expr(&rhs)) { + db_error("Expression syntax error after \"||\"\n"); + /*NOTREACHED*/ + } + lhs = (lhs || rhs); + } + db_unread_token(t); + *valuep = lhs; + return (TRUE); +} + +int +db_expression(valuep) + db_expr_t *valuep; +{ + return (db_logical_or_expr(valuep)); +} + +#endif MACH_KDB diff --git a/ddb/db_expr.h b/ddb/db_expr.h new file mode 100644 index 0000000..989b66b --- /dev/null +++ b/ddb/db_expr.h @@ -0,0 +1,26 @@ +/* + * (c) Copyright 1992, 1993, 1994, 1995 OPEN SOFTWARE FOUNDATION, INC. + * ALL RIGHTS RESERVED + */ +/* + * OSF RI nmk19b2 5/2/95 + */ + +#ifndef _DDB_DB_EXPR_H_ +#define _DDB_DB_EXPR_H_ + +#include <mach/boolean.h> +#include <machine/db_machdep.h> + + +/* Prototypes for functions exported by this module. + */ + +int db_size_option( + char *modif, + boolean_t *u_option, + boolean_t *t_option); + +int db_expression(db_expr_t *valuep); + +#endif /* !_DDB_DB_EXPR_H_ */ diff --git a/ddb/db_ext_symtab.c b/ddb/db_ext_symtab.c new file mode 100644 index 0000000..1a8ea8b --- /dev/null +++ b/ddb/db_ext_symtab.c @@ -0,0 +1,123 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +#include "mach_kdb.h" +#if MACH_KDB + +#include <mach_debug.h> + +#if MACH_DEBUG + +#include <mach/mach_types.h> /* vm_address_t */ +#include <mach/std_types.h> /* pointer_t */ +#include <mach/vm_param.h> +#include <vm/vm_map.h> +#include <vm/vm_kern.h> +#include <kern/host.h> +#include <kern/task.h> +#include <ddb/db_sym.h> + + + +/* + * Loads a symbol table for an external file into the kernel debugger. + * The symbol table data is an array of characters. It is assumed that + * the caller and the kernel debugger agree on its format. + */ +kern_return_t +host_load_symbol_table(host, task, name, symtab, symtab_count) + host_t host; + task_t task; + char * name; + pointer_t symtab; + unsigned int symtab_count; +{ + kern_return_t result; + vm_offset_t symtab_start; + vm_offset_t symtab_end; + vm_map_t map; + vm_map_copy_t symtab_copy_object; + + if (host == HOST_NULL) + return (KERN_INVALID_ARGUMENT); + + /* + * Copy the symbol table array into the kernel. + * We make a copy of the copy object, and clear + * the old one, so that returning error will not + * deallocate the data twice. + */ + symtab_copy_object = (vm_map_copy_t) symtab; + result = vm_map_copyout( + kernel_map, + &symtab_start, + vm_map_copy_copy(symtab_copy_object)); + if (result != KERN_SUCCESS) + return (result); + + symtab_end = symtab_start + symtab_count; + + /* + * Add the symbol table. + * Do not keep a reference for the task map. XXX + */ + if (task == TASK_NULL) + map = VM_MAP_NULL; + else + map = task->map; + if (!X_db_sym_init((char *)symtab_start, + (char *)symtab_end, + name, + (char *)map)) + { + /* + * Not enough room for symbol table - failure. + */ + (void) vm_deallocate(kernel_map, + symtab_start, + symtab_count); + return (KERN_FAILURE); + } + + /* + * Wire down the symbol table + */ + (void) vm_map_pageable(kernel_map, + symtab_start, + round_page(symtab_end), + VM_PROT_READ|VM_PROT_WRITE); + + /* + * Discard the original copy object + */ + vm_map_copy_discard(symtab_copy_object); + + return (KERN_SUCCESS); +} + +#endif /* MACH_DEBUG */ + +#endif MACH_KDB diff --git a/ddb/db_input.c b/ddb/db_input.c new file mode 100644 index 0000000..c175ae1 --- /dev/null +++ b/ddb/db_input.c @@ -0,0 +1,378 @@ +/* + * Mach Operating System + * Copyright (c) 1992,1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ +#include "mach_kdb.h" +#if MACH_KDB + +#include <mach/boolean.h> +#include <machine/db_machdep.h> +#include <ddb/db_output.h> + + + +#ifndef DB_HISTORY_SIZE +#define DB_HISTORY_SIZE 4000 +#endif DB_HISTORY_SIZE + +/* + * Character input and editing. + */ + +/* + * We don't track output position while editing input, + * since input always ends with a new-line. We just + * reset the line position at the end. + */ +char * db_lbuf_start; /* start of input line buffer */ +char * db_lbuf_end; /* end of input line buffer */ +char * db_lc; /* current character */ +char * db_le; /* one past last character */ +#if DB_HISTORY_SIZE != 0 +char db_history[DB_HISTORY_SIZE]; /* start of history buffer */ +int db_history_size = DB_HISTORY_SIZE;/* size of history buffer */ +char * db_history_curr = db_history; /* start of current line */ +char * db_history_last = db_history; /* start of last line */ +char * db_history_prev = (char *) 0; /* start of previous line */ +#endif + +#define CTRL(c) ((c) & 0x1f) +#define isspace(c) ((c) == ' ' || (c) == '\t') +#define BLANK ' ' +#define BACKUP '\b' + +void +db_putstring(s, count) + char *s; + int count; +{ + while (--count >= 0) + cnputc(*s++); +} + +void +db_putnchars(c, count) + int c; + int count; +{ + while (--count >= 0) + cnputc(c); +} + +/* + * Delete N characters, forward or backward + */ +#define DEL_FWD 0 +#define DEL_BWD 1 +void +db_delete(n, bwd) + int n; + int bwd; +{ + register char *p; + + if (bwd) { + db_lc -= n; + db_putnchars(BACKUP, n); + } + for (p = db_lc; p < db_le-n; p++) { + *p = *(p+n); + cnputc(*p); + } + db_putnchars(BLANK, n); + db_putnchars(BACKUP, db_le - db_lc); + db_le -= n; +} + +void +db_delete_line() +{ + db_delete(db_le - db_lc, DEL_FWD); + db_delete(db_lc - db_lbuf_start, DEL_BWD); + db_le = db_lc = db_lbuf_start; +} + +#if DB_HISTORY_SIZE != 0 +#define INC_DB_CURR() \ + do { \ + db_history_curr++; \ + if (db_history_curr > \ + db_history + db_history_size - 1) \ + db_history_curr = db_history; \ + } while (0) +#define DEC_DB_CURR() \ + do { \ + db_history_curr--; \ + if (db_history_curr < db_history) \ + db_history_curr = db_history + \ + db_history_size - 1; \ + } while (0) +#endif + +/* returns TRUE at end-of-line */ +boolean_t +db_inputchar(c) + int c; +{ + switch (c) { + case CTRL('b'): + /* back up one character */ + if (db_lc > db_lbuf_start) { + cnputc(BACKUP); + db_lc--; + } + break; + case CTRL('f'): + /* forward one character */ + if (db_lc < db_le) { + cnputc(*db_lc); + db_lc++; + } + break; + case CTRL('a'): + /* beginning of line */ + while (db_lc > db_lbuf_start) { + cnputc(BACKUP); + db_lc--; + } + break; + case CTRL('e'): + /* end of line */ + while (db_lc < db_le) { + cnputc(*db_lc); + db_lc++; + } + break; + case CTRL('h'): + case 0177: + /* erase previous character */ + if (db_lc > db_lbuf_start) + db_delete(1, DEL_BWD); + break; + case CTRL('d'): + /* erase next character */ + if (db_lc < db_le) + db_delete(1, DEL_FWD); + break; + case CTRL('k'): + /* delete to end of line */ + if (db_lc < db_le) + db_delete(db_le - db_lc, DEL_FWD); + break; + case CTRL('u'): + /* delete line */ + db_delete_line(); + break; + case CTRL('t'): + /* twiddle last 2 characters */ + if (db_lc >= db_lbuf_start + 2) { + c = db_lc[-2]; + db_lc[-2] = db_lc[-1]; + db_lc[-1] = c; + cnputc(BACKUP); + cnputc(BACKUP); + cnputc(db_lc[-2]); + cnputc(db_lc[-1]); + } + break; +#if DB_HISTORY_SIZE != 0 + case CTRL('p'): + DEC_DB_CURR(); + while (db_history_curr != db_history_last) { + DEC_DB_CURR(); + if (*db_history_curr == '\0') + break; + } + db_delete_line(); + if (db_history_curr == db_history_last) { + INC_DB_CURR(); + db_le = db_lc = db_lbuf_start; + } else { + register char *p; + INC_DB_CURR(); + for (p = db_history_curr, db_le = db_lbuf_start; + *p; ) { + *db_le++ = *p++; + if (p == db_history + db_history_size) { + p = db_history; + } + } + db_lc = db_le; + } + db_putstring(db_lbuf_start, db_le - db_lbuf_start); + break; + case CTRL('n'): + while (db_history_curr != db_history_last) { + if (*db_history_curr == '\0') + break; + INC_DB_CURR(); + } + if (db_history_curr != db_history_last) { + INC_DB_CURR(); + db_delete_line(); + if (db_history_curr != db_history_last) { + register char *p; + for (p = db_history_curr, + db_le = db_lbuf_start; *p;) { + *db_le++ = *p++; + if (p == db_history + + db_history_size) { + p = db_history; + } + } + db_lc = db_le; + } + db_putstring(db_lbuf_start, db_le - db_lbuf_start); + } + break; +#endif + case CTRL('r'): + db_putstring("^R\n", 3); + if (db_le > db_lbuf_start) { + db_putstring(db_lbuf_start, db_le - db_lbuf_start); + db_putnchars(BACKUP, db_le - db_lc); + } + break; + case '\n': + case '\r': +#if DB_HISTORY_SIZE != 0 + /* + * Check whether current line is the same + * as previous saved line. If it is, don`t + * save it. + */ + if (db_history_curr == db_history_prev) { + register char *pp, *pc; + + /* + * Is it the same? + */ + for (pp = db_history_prev, pc = db_lbuf_start; + pc != db_le && *pp; ) { + if (*pp != *pc) + break; + if (++pp == db_history + db_history_size) { + pp = db_history; + } + pc++; + } + if (!*pp && pc == db_le) { + /* + * Repeated previous line. Don`t save. + */ + db_history_curr = db_history_last; + *db_le++ = c; + return (TRUE); + } + } + if (db_le != db_lbuf_start) { + register char *p; + db_history_prev = db_history_last; + for (p = db_lbuf_start; p != db_le; p++) { + *db_history_last++ = *p; + if (db_history_last == db_history + + db_history_size) { + db_history_last = db_history; + } + } + *db_history_last++ = '\0'; + } + db_history_curr = db_history_last; +#endif + *db_le++ = c; + return (TRUE); + default: + if (db_le == db_lbuf_end) { + cnputc('\007'); + } + else if (c >= ' ' && c <= '~') { + register char *p; + + for (p = db_le; p > db_lc; p--) + *p = *(p-1); + *db_lc++ = c; + db_le++; + cnputc(c); + db_putstring(db_lc, db_le - db_lc); + db_putnchars(BACKUP, db_le - db_lc); + } + break; + } + return (FALSE); +} + +int +db_readline(lstart, lsize) + char * lstart; + int lsize; +{ + db_force_whitespace(); /* synch output position */ + + db_lbuf_start = lstart; + db_lbuf_end = lstart + lsize - 1; + db_lc = lstart; + db_le = lstart; + + while (!db_inputchar(cngetc())) + continue; + + db_putchar('\n'); /* synch output position */ + + *db_le = 0; + return (db_le - db_lbuf_start); +} + +void +db_check_interrupt() +{ + register int c; + + c = cnmaygetc(); + switch (c) { + case -1: /* no character */ + return; + + case CTRL('c'): + db_error((char *)0); + /*NOTREACHED*/ + + case CTRL('s'): + do { + c = cnmaygetc(); + if (c == CTRL('c')) + db_error((char *)0); + } while (c != CTRL('q')); + break; + + default: + /* drop on floor */ + break; + } +} + +#endif MACH_KDB diff --git a/ddb/db_lex.c b/ddb/db_lex.c new file mode 100644 index 0000000..617f123 --- /dev/null +++ b/ddb/db_lex.c @@ -0,0 +1,455 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ +#include "mach_kdb.h" +#if MACH_KDB + +/* + * Lexical analyzer. + */ +#include <machine/db_machdep.h> +#include <kern/strings.h> +#include <ddb/db_lex.h> + +char db_line[DB_LEX_LINE_SIZE]; +char db_last_line[DB_LEX_LINE_SIZE]; +char *db_lp, *db_endlp; +char *db_last_lp; +int db_look_char = 0; +db_expr_t db_look_token = 0; + +int +db_read_line(repeat_last) + char *repeat_last; +{ + int i; + + i = db_readline(db_line, sizeof(db_line)); + if (i == 0) + return (0); /* EOI */ + if (repeat_last) { + if (strncmp(db_line, repeat_last, strlen(repeat_last)) == 0) { + db_strcpy(db_line, db_last_line); + db_printf("%s", db_line); + i = strlen(db_line); + } else if (db_line[0] != '\n' && db_line[0] != 0) + db_strcpy(db_last_line, db_line); + } + db_lp = db_line; + db_endlp = db_lp + i; + db_last_lp = db_lp; + db_look_char = 0; + db_look_token = 0; + return (i); +} + +void +db_flush_line() +{ + db_lp = db_line; + db_last_lp = db_lp; + db_endlp = db_line; +} + +void +db_switch_input(buffer, size) + char *buffer; + int size; +{ + db_lp = buffer; + db_last_lp = db_lp; + db_endlp = buffer + size; + db_look_char = 0; + db_look_token = 0; +} + +void +db_save_lex_context(lp) + register struct db_lex_context *lp; +{ + lp->l_ptr = db_lp; + lp->l_eptr = db_endlp; + lp->l_char = db_look_char; + lp->l_token = db_look_token; +} + +void +db_restore_lex_context(lp) + register struct db_lex_context *lp; +{ + db_lp = lp->l_ptr; + db_last_lp = db_lp; + db_endlp = lp->l_eptr; + db_look_char = lp->l_char; + db_look_token = lp->l_token; +} + +int +db_read_char() +{ + int c; + + if (db_look_char != 0) { + c = db_look_char; + db_look_char = 0; + } + else if (db_lp >= db_endlp) + c = -1; + else + c = *db_lp++; + return (c); +} + +void +db_unread_char(c) + int c; +{ + db_look_char = c; +} + +void +db_unread_token(t) + int t; +{ + db_look_token = t; +} + +int +db_read_token() +{ + int t; + + if (db_look_token) { + t = db_look_token; + db_look_token = 0; + } + else { + db_last_lp = db_lp; + if (db_look_char) + db_last_lp--; + t = db_lex(); + } + return (t); +} + +db_expr_t db_tok_number; +char db_tok_string[TOK_STRING_SIZE]; +db_expr_t db_radix = 16; + +void +db_flush_lex() +{ + db_flush_line(); + db_look_char = 0; + db_look_token = 0; +} + +#define DB_DISP_SKIP 40 /* number of chars to display skip */ + +void +db_skip_to_eol() +{ + register skip; + register t; + register n; + register char *p; + + t = db_read_token(); + p = db_last_lp; + for (skip = 0; t != tEOL && t != tSEMI_COLON && t != tEOF; skip++) + t = db_read_token(); + if (t == tSEMI_COLON) + db_unread_token(t); + if (skip != 0) { + while (p < db_last_lp && (*p == ' ' || *p == '\t')) + p++; + db_printf("Warning: Skipped input data \""); + for (n = 0; n < DB_DISP_SKIP && p < db_last_lp; n++) + db_printf("%c", *p++); + if (n >= DB_DISP_SKIP) + db_printf("...."); + db_printf("\"\n"); + } +} + +int +db_lex() +{ + register char *cp; + register c; + + c = db_read_char(); + while (c <= ' ' || c > '~') { + if (c == '\n' || c == -1) + return (tEOL); + c = db_read_char(); + } + + cp = db_tok_string; + *cp++ = c; + + if (c >= '0' && c <= '9') { + /* number */ + int r, digit; + + if (c > '0') + r = db_radix; + else { + c = db_read_char(); + if (c == 'O' || c == 'o') + r = 8; + else if (c == 'T' || c == 't') + r = 10; + else if (c == 'X' || c == 'x') + r = 16; + else { + cp--; + r = db_radix; + db_unread_char(c); + } + c = db_read_char(); + *cp++ = c; + } + db_tok_number = 0; + for (;;) { + if (c >= '0' && c <= ((r == 8) ? '7' : '9')) + digit = c - '0'; + else if (r == 16 && ((c >= 'A' && c <= 'F') || + (c >= 'a' && c <= 'f'))) { + if (c >= 'a') + digit = c - 'a' + 10; + else + digit = c - 'A' + 10; + } + else + break; + db_tok_number = db_tok_number * r + digit; + c = db_read_char(); + if (cp < &db_tok_string[sizeof(db_tok_string)-1]) + *cp++ = c; + } + cp[-1] = 0; + if ((c >= '0' && c <= '9') || + (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + (c == '_')) + { + db_printf("Bad character '%c' after number %s\n", + c, db_tok_string); + db_error(0); + db_flush_lex(); + return (tEOF); + } + db_unread_char(c); + return (tNUMBER); + } + if ((c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + c == '_' || c == '\\' || c == ':') + { + /* identifier */ + if (c == '\\') { + c = db_read_char(); + if (c == '\n' || c == -1) + db_error("Bad '\\' at the end of line\n"); + cp[-1] = c; + } + while (1) { + c = db_read_char(); + if ((c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || + c == '_' || c == '\\' || c == ':' || c == '.') + { + if (c == '\\') { + c = db_read_char(); + if (c == '\n' || c == -1) + db_error("Bad '\\' at the end of line\n"); + } + *cp++ = c; + if (cp == db_tok_string+sizeof(db_tok_string)) { + db_error("String too long\n"); + db_flush_lex(); + return (tEOF); + } + continue; + } + else { + *cp = '\0'; + break; + } + } + db_unread_char(c); + return (tIDENT); + } + + *cp = 0; + switch (c) { + case '+': + return (tPLUS); + case '-': + return (tMINUS); + case '.': + c = db_read_char(); + if (c == '.') { + *cp++ = c; + *cp = 0; + return (tDOTDOT); + } + db_unread_char(c); + return (tDOT); + case '*': + return (tSTAR); + case '/': + return (tSLASH); + case '=': + c = db_read_char(); + if (c == '=') { + *cp++ = c; + *cp = 0; + return(tLOG_EQ); + } + db_unread_char(c); + return (tEQ); + case '%': + return (tPCT); + case '#': + return (tHASH); + case '(': + return (tLPAREN); + case ')': + return (tRPAREN); + case ',': + return (tCOMMA); + case '\'': + return (tQUOTE); + case '"': + /* string */ + cp = db_tok_string; + c = db_read_char(); + while (c != '"' && c > 0 && c != '\n') { + if (cp >= &db_tok_string[sizeof(db_tok_string)-1]) { + db_error("Too long string\n"); + db_flush_lex(); + return (tEOF); + } + if (c == '\\') { + c = db_read_char(); + switch(c) { + case 'n': + c = '\n'; break; + case 't': + c = '\t'; break; + case '\\': + case '"': + break; + default: + db_printf("Bad escape sequence '\\%c'\n", c); + db_error(0); + db_flush_lex(); + return (tEOF); + } + } + *cp++ = c; + c = db_read_char(); + } + *cp = 0; + if (c != '"') { + db_error("Non terminated string constant\n"); + db_flush_lex(); + return (tEOF); + } + return (tSTRING); + case '$': + return (tDOLLAR); + case '!': + c = db_read_char(); + if (c == '=') { + *cp++ = c; + *cp = 0; + return(tLOG_NOT_EQ); + } + db_unread_char(c); + return (tEXCL); + case '&': + c = db_read_char(); + if (c == '&') { + *cp++ = c; + *cp = 0; + return(tLOG_AND); + } + db_unread_char(c); + return(tBIT_AND); + case '|': + c = db_read_char(); + if (c == '|') { + *cp++ = c; + *cp = 0; + return(tLOG_OR); + } + db_unread_char(c); + return(tBIT_OR); + case '<': + c = db_read_char(); + *cp++ = c; + *cp = 0; + if (c == '<') + return (tSHIFT_L); + if (c == '=') + return (tLESS_EQ); + cp[-1] = 0; + db_unread_char(c); + return(tLESS); + break; + case '>': + c = db_read_char(); + *cp++ = c; + *cp = 0; + if (c == '>') + return (tSHIFT_R); + if (c == '=') + return (tGREATER_EQ); + cp[-1] = 0; + db_unread_char(c); + return (tGREATER); + break; + case ';': + return (tSEMI_COLON); + case '?': + return (tQUESTION); + case -1: + db_strcpy(db_tok_string, "<EOL>"); + return (tEOF); + } + db_printf("Bad character '%c'\n", c); + db_flush_lex(); + return (tEOF); +} + +#endif MACH_KDB diff --git a/ddb/db_lex.h b/ddb/db_lex.h new file mode 100644 index 0000000..c73b93e --- /dev/null +++ b/ddb/db_lex.h @@ -0,0 +1,93 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ +/* + * Lexical analyzer. + */ + +#define TOK_STRING_SIZE 64 +#define DB_LEX_LINE_SIZE 256 + +struct db_lex_context { + int l_char; /* peek char */ + int l_token; /* peek token */ + char *l_ptr; /* line pointer */ + char *l_eptr; /* line end pointer */ +}; + +extern int db_read_line(/* char *rep_str */); +extern void db_flush_line(); +extern int db_read_char(); +extern void db_unread_char(/* char c */); +extern int db_read_token(); +extern void db_unread_token(/* int t */); +extern void db_flush_lex(); +extern void db_switch_input(/* char *, int */); +extern void db_save_lex_context(/* struct db_lex_context * */); +extern void db_restore_lex_context(/* struct db_lex_context * */); +extern void db_skip_to_eol(); + +extern db_expr_t db_tok_number; +extern char db_tok_string[TOK_STRING_SIZE]; +extern db_expr_t db_radix; + +#define tEOF (-1) +#define tEOL 1 +#define tNUMBER 2 +#define tIDENT 3 +#define tPLUS 4 +#define tMINUS 5 +#define tDOT 6 +#define tSTAR 7 +#define tSLASH 8 +#define tEQ 9 +#define tLPAREN 10 +#define tRPAREN 11 +#define tPCT 12 +#define tHASH 13 +#define tCOMMA 14 +#define tQUOTE 15 +#define tDOLLAR 16 +#define tEXCL 17 +#define tSHIFT_L 18 +#define tSHIFT_R 19 +#define tDOTDOT 20 +#define tSEMI_COLON 21 +#define tLOG_EQ 22 +#define tLOG_NOT_EQ 23 +#define tLESS 24 +#define tLESS_EQ 25 +#define tGREATER 26 +#define tGREATER_EQ 27 +#define tBIT_AND 28 +#define tBIT_OR 29 +#define tLOG_AND 30 +#define tLOG_OR 31 +#define tSTRING 32 +#define tQUESTION 33 diff --git a/ddb/db_macro.c b/ddb/db_macro.c new file mode 100644 index 0000000..c186ac6 --- /dev/null +++ b/ddb/db_macro.c @@ -0,0 +1,183 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +#include "mach_kdb.h" +#if MACH_KDB + +#include <kern/thread.h> + +#include <machine/db_machdep.h> +#include <ddb/db_lex.h> +#include <ddb/db_variables.h> +#include <ddb/db_command.h> + + + +/* + * debugger macro support + */ + +#define DB_MACRO_LEVEL 5 /* max macro nesting */ +#define DB_NARGS 10 /* max args */ +#define DB_NUSER_MACRO 10 /* max user macros */ + +int db_macro_free = DB_NUSER_MACRO; +struct db_user_macro { + char m_name[TOK_STRING_SIZE]; + char m_lbuf[DB_LEX_LINE_SIZE]; + int m_size; +} db_user_macro[DB_NUSER_MACRO]; + +int db_macro_level = 0; +db_expr_t db_macro_args[DB_MACRO_LEVEL][DB_NARGS]; + +static struct db_user_macro * +db_lookup_macro(name) + char *name; +{ + register struct db_user_macro *mp; + + for (mp = db_user_macro; mp < &db_user_macro[DB_NUSER_MACRO]; mp++) { + if (mp->m_name[0] == 0) + continue; + if (strcmp(mp->m_name, name) == 0) + return(mp); + } + return(0); +} + +void +db_def_macro_cmd() +{ + register char *p; + register c; + register struct db_user_macro *mp, *ep; + + if (db_read_token() != tIDENT) { + db_printf("Bad macro name \"%s\"\n", db_tok_string); + db_error(0); + /* NOTREACHED */ + } + if ((mp = db_lookup_macro(db_tok_string)) == 0) { + if (db_macro_free <= 0) + db_error("Too many macros\n"); + /* NOTREACHED */ + ep = &db_user_macro[DB_NUSER_MACRO]; + for (mp = db_user_macro; mp < ep && mp->m_name[0]; mp++); + if (mp >= ep) + db_error("ddb: internal error(macro)\n"); + /* NOTREACHED */ + db_macro_free--; + db_strcpy(mp->m_name, db_tok_string); + } + for (c = db_read_char(); c == ' ' || c == '\t'; c = db_read_char()); + for (p = mp->m_lbuf; c > 0; c = db_read_char()) + *p++ = c; + *p = 0; + mp->m_size = p - mp->m_lbuf; +} + +void +db_del_macro_cmd() +{ + register struct db_user_macro *mp; + + if (db_read_token() != tIDENT + || (mp = db_lookup_macro(db_tok_string)) == 0) { + db_printf("No such macro \"%s\"\n", db_tok_string); + db_error(0); + /* NOTREACHED */ + } else { + mp->m_name[0] = 0; + db_macro_free++; + } +} + +void +db_show_macro() +{ + register struct db_user_macro *mp; + int t; + char *name = 0; + + if ((t = db_read_token()) == tIDENT) + name = db_tok_string; + else + db_unread_token(t); + for (mp = db_user_macro; mp < &db_user_macro[DB_NUSER_MACRO]; mp++) { + if (mp->m_name[0] == 0) + continue; + if (name && strcmp(mp->m_name, name)) + continue; + db_printf("%s: %s", mp->m_name, mp->m_lbuf); + } +} + +int +db_exec_macro(name) + char *name; +{ + register struct db_user_macro *mp; + register n; + + if ((mp = db_lookup_macro(name)) == 0) + return(-1); + if (db_macro_level+1 >= DB_MACRO_LEVEL) { + db_macro_level = 0; + db_error("Too many macro nest\n"); + /* NOTREACHED */ + } + for (n = 0; + n < DB_NARGS && + db_expression(&db_macro_args[db_macro_level+1][n]); + n++); + while (n < DB_NARGS) + db_macro_args[db_macro_level+1][n++] = 0; + db_macro_level++; + db_exec_cmd_nest(mp->m_lbuf, mp->m_size); + db_macro_level--; + return(0); +} + +int +/* ARGSUSED */ +db_arg_variable(vp, valuep, flag, ap) + struct db_variable *vp; + db_expr_t *valuep; + int flag; + db_var_aux_param_t ap; +{ + if (ap->level != 1 || ap->suffix[0] < 1 || ap->suffix[0] > DB_NARGS) { + db_error("Bad $arg variable\n"); + /* NOTREACHED */ + } + if (flag == DB_VAR_GET) + *valuep = db_macro_args[db_macro_level][ap->suffix[0]-1]; + else + db_macro_args[db_macro_level][ap->suffix[0]-1] = *valuep; + return(0); +} + +#endif MACH_KDB diff --git a/ddb/db_mp.c b/ddb/db_mp.c new file mode 100644 index 0000000..607c24d --- /dev/null +++ b/ddb/db_mp.c @@ -0,0 +1,339 @@ +/* + * Mach Operating System + * Copyright (c) 1993,1992 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +#include "mach_kdb.h" +#if MACH_KDB + +#include <cpus.h> + +#if NCPUS > 1 + +#include <mach/boolean.h> +#include <mach/machine.h> + +#include <kern/cpu_number.h> +#include <kern/lock.h> + +#include <machine/db_machdep.h> + +#include <ddb/db_command.h> +#include <ddb/db_run.h> + +/* + * Routines to interlock access to the kernel debugger on + * multiprocessors. + */ + +decl_simple_lock_data(,db_lock) /* lock to enter debugger */ +volatile int db_cpu = -1; /* CPU currently in debugger */ + /* -1 if none */ +int db_active[NCPUS] = { 0 }; /* count recursive entries + into debugger */ +int db_slave[NCPUS] = { 0 }; /* nonzero if cpu interrupted + by another cpu in debugger */ + +int db_enter_debug = 0; + +void remote_db(); /* forward */ +void lock_db(); +void unlock_db(); + + +/* + * Called when entering kernel debugger. + * Takes db lock. If we were called remotely (slave state) we just + * wait for db_cpu to be equal to cpu_number(). Otherwise enter debugger + * if not active on another cpu + */ + +boolean_t +db_enter() +{ + int mycpu = cpu_number(); + + /* + * Count recursive entries to debugger. + */ + db_active[mycpu]++; + + /* + * Wait for other CPUS to leave debugger. + */ + lock_db(); + + if (db_enter_debug) + db_printf( + "db_enter: cpu %d[%d], master %d, db_cpu %d, run mode %d\n", + mycpu, db_slave[mycpu], master_cpu, db_cpu, db_run_mode); + + /* + * If no CPU in debugger, and I am not being stopped, + * enter the debugger. + */ + if (db_cpu == -1 && !db_slave[mycpu]) { + remote_db(); /* stop other cpus */ + db_cpu = mycpu; + return TRUE; + } + /* + * If I am already in the debugger (recursive entry + * or returning from single step), enter debugger. + */ + else if (db_cpu == mycpu) + return TRUE; + /* + * Otherwise, cannot enter debugger. + */ + else + return FALSE; +} + +/* + * Leave debugger. + */ +void +db_leave() +{ + int mycpu = cpu_number(); + + /* + * If continuing, give up debugger + */ + if (db_run_mode == STEP_CONTINUE) + db_cpu = -1; + + /* + * If I am a slave, drop my slave count. + */ + if (db_slave[mycpu]) + db_slave[mycpu]--; + if (db_enter_debug) + db_printf("db_leave: cpu %d[%d], db_cpu %d, run_mode %d\n", + mycpu, db_slave[mycpu], db_cpu, db_run_mode); + /* + * Unlock debugger. + */ + unlock_db(); + + /* + * Drop recursive entry count. + */ + db_active[mycpu]--; +} + + +/* + * invoke kernel debugger on slave processors + */ + +void +remote_db() { + int my_cpu = cpu_number(); + register int i; + + for (i = 0; i < NCPUS; i++) { + if (i != my_cpu && + machine_slot[i].is_cpu && + machine_slot[i].running) + { + cpu_interrupt_to_db(i); + } + } +} + +/* + * Save and restore DB global registers. + * + * DB_SAVE_CTXT must be at the start of a block, and + * DB_RESTORE_CTXT must be in the same block. + */ + +#ifdef __STDC__ +#define DB_SAVE(type, name) extern type name; type name##_save = name +#define DB_RESTORE(name) name = name##_save +#else /* __STDC__ */ +#define DB_SAVE(type, name) extern type name; type name/**/_save = name +#define DB_RESTORE(name) name = name/**/_save +#endif /* __STDC__ */ + +#define DB_SAVE_CTXT() \ + DB_SAVE(int, db_run_mode); \ + DB_SAVE(boolean_t, db_sstep_print); \ + DB_SAVE(int, db_loop_count); \ + DB_SAVE(int, db_call_depth); \ + DB_SAVE(int, db_inst_count); \ + DB_SAVE(int, db_last_inst_count); \ + DB_SAVE(int, db_load_count); \ + DB_SAVE(int, db_store_count); \ + DB_SAVE(boolean_t, db_cmd_loop_done); \ + DB_SAVE(jmp_buf_t *, db_recover); \ + DB_SAVE(db_addr_t, db_dot); \ + DB_SAVE(db_addr_t, db_last_addr); \ + DB_SAVE(db_addr_t, db_prev); \ + DB_SAVE(db_addr_t, db_next); \ + SAVE_DDB_REGS + +#define DB_RESTORE_CTXT() \ + DB_RESTORE(db_run_mode); \ + DB_RESTORE(db_sstep_print); \ + DB_RESTORE(db_loop_count); \ + DB_RESTORE(db_call_depth); \ + DB_RESTORE(db_inst_count); \ + DB_RESTORE(db_last_inst_count); \ + DB_RESTORE(db_load_count); \ + DB_RESTORE(db_store_count); \ + DB_RESTORE(db_cmd_loop_done); \ + DB_RESTORE(db_recover); \ + DB_RESTORE(db_dot); \ + DB_RESTORE(db_last_addr); \ + DB_RESTORE(db_prev); \ + DB_RESTORE(db_next); \ + RESTORE_DDB_REGS + +/* + * switch to another cpu + */ +void +db_on(cpu) + int cpu; +{ + /* + * Save ddb global variables + */ + DB_SAVE_CTXT(); + + /* + * Don`t do if bad CPU number. + * CPU must also be spinning in db_entry. + */ + if (cpu < 0 || cpu >= NCPUS || !db_active[cpu]) + return; + + /* + * Give debugger to that CPU + */ + db_cpu = cpu; + unlock_db(); + + /* + * Wait for it to come back again + */ + lock_db(); + + /* + * Restore ddb globals + */ + DB_RESTORE_CTXT(); + + if (db_cpu == -1) /* someone continued */ + db_continue_cmd(0, 0, 0, ""); +} + +/* + * Called by interprocessor interrupt when one CPU is + * in kernel debugger and wants to stop other CPUs + */ +void +remote_db_enter() +{ + db_slave[cpu_number()]++; + kdb_kintr(); +} + +/* + * Acquire kernel debugger. + * Conditional code for forwarding characters from slave to console + * if console on master only. + */ + +/* + * As long as db_cpu is not -1 or cpu_number(), we know that debugger + * is active on another cpu. + */ +void +lock_db() +{ + int my_cpu = cpu_number(); + + for (;;) { +#if CONSOLE_ON_MASTER + if (my_cpu == master_cpu) { + db_console(); + } +#endif + if (db_cpu != -1 && db_cpu != my_cpu) + continue; + +#if CONSOLE_ON_MASTER + if (my_cpu == master_cpu) { + if (!simple_lock_try(&db_lock)) + continue; + } + else { + simple_lock(&db_lock); + } +#else + simple_lock(&db_lock); +#endif + if (db_cpu == -1 || db_cpu == my_cpu) + break; + simple_unlock(&db_lock); + } +} + +void +unlock_db() +{ + simple_unlock(&db_lock); +} + +#ifdef sketch +void +db_console() +{ + if (i_bit(CBUS_PUT_CHAR, my_word)) { + volatile u_char c = cbus_ochar; + i_bit_clear(CBUS_PUT_CHAR, my_word); + cnputc(c); + } else if (i_bit(CBUS_GET_CHAR, my_word)) { + if (cbus_wait_char) + cbus_ichar = cngetc(); + else + cbus_ichar = cnmaygetc(); + i_bit_clear(CBUS_GET_CHAR, my_word); +#ifndef notdef + } else if (!cnmaygetc()) { +#else /* notdef */ + } else if (com_is_char() && !com_getc(TRUE)) { +#endif /* notdef */ + simple_unlock(&db_lock); + db_cpu = my_cpu; + } +} +#endif /* sketch */ + +#endif /* NCPUS > 1 */ + +#endif MACH_KDB diff --git a/ddb/db_output.c b/ddb/db_output.c new file mode 100644 index 0000000..d7e416d --- /dev/null +++ b/ddb/db_output.c @@ -0,0 +1,240 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#include "mach_kdb.h" +#if MACH_KDB + +/* + * Printf and character output for debugger. + */ + +#include <mach/boolean.h> +#include <sys/varargs.h> +#include <machine/db_machdep.h> +#include <ddb/db_lex.h> +#include <ddb/db_output.h> + +/* + * Character output - tracks position in line. + * To do this correctly, we should know how wide + * the output device is - then we could zero + * the line position when the output device wraps + * around to the start of the next line. + * + * Instead, we count the number of spaces printed + * since the last printing character so that we + * don't print trailing spaces. This avoids most + * of the wraparounds. + */ + +#ifndef DB_MAX_LINE +#define DB_MAX_LINE 24 /* maximum line */ +#define DB_MAX_WIDTH 80 /* maximum width */ +#endif DB_MAX_LINE + +#define DB_MIN_MAX_WIDTH 20 /* minimum max width */ +#define DB_MIN_MAX_LINE 3 /* minimum max line */ +#define CTRL(c) ((c) & 0xff) + +int db_output_position = 0; /* output column */ +int db_output_line = 0; /* output line number */ +int db_last_non_space = 0; /* last non-space character */ +int db_tab_stop_width = 8; /* how wide are tab stops? */ +#define NEXT_TAB(i) \ + ((((i) + db_tab_stop_width) / db_tab_stop_width) * db_tab_stop_width) +int db_max_line = DB_MAX_LINE; /* output max lines */ +int db_max_width = DB_MAX_WIDTH; /* output line width */ + +extern void db_check_interrupt(); + +/* + * Force pending whitespace. + */ +void +db_force_whitespace() +{ + register int last_print, next_tab; + + last_print = db_last_non_space; + while (last_print < db_output_position) { + next_tab = NEXT_TAB(last_print); + if (next_tab <= db_output_position) { + cnputc('\t'); + last_print = next_tab; + } + else { + cnputc(' '); + last_print++; + } + } + db_last_non_space = db_output_position; +} + +static void +db_more() +{ + register char *p; + boolean_t quit_output = FALSE; + + for (p = "--db_more--"; *p; p++) + cnputc(*p); + switch(cngetc()) { + case ' ': + db_output_line = 0; + break; + case 'q': + case CTRL('c'): + db_output_line = 0; + quit_output = TRUE; + break; + default: + db_output_line--; + break; + } + p = "\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b"; + while (*p) + cnputc(*p++); + if (quit_output) { + db_error(0); + /* NOTREACHED */ + } +} + +/* + * Output character. Buffer whitespace. + */ +void +db_putchar(c) + int c; /* character to output */ +{ + if (db_max_line >= DB_MIN_MAX_LINE && db_output_line >= db_max_line-1) + db_more(); + if (c > ' ' && c <= '~') { + /* + * Printing character. + * If we have spaces to print, print them first. + * Use tabs if possible. + */ + db_force_whitespace(); + cnputc(c); + db_output_position++; + if (db_max_width >= DB_MIN_MAX_WIDTH + && db_output_position >= db_max_width-1) { + /* auto new line */ + cnputc('\n'); + db_output_position = 0; + db_last_non_space = 0; + db_output_line++; + } + db_last_non_space = db_output_position; + } + else if (c == '\n') { + /* Return */ + cnputc(c); + db_output_position = 0; + db_last_non_space = 0; + db_output_line++; + db_check_interrupt(); + } + else if (c == '\t') { + /* assume tabs every 8 positions */ + db_output_position = NEXT_TAB(db_output_position); + } + else if (c == ' ') { + /* space */ + db_output_position++; + } + else if (c == '\007') { + /* bell */ + cnputc(c); + } + /* other characters are assumed non-printing */ +} + +void +db_id_putc(char c, vm_offset_t dummy) +{ + db_putchar(c); +} + +/* + * Return output position + */ +int +db_print_position() +{ + return (db_output_position); +} + +/* + * End line if too long. + */ +void db_end_line() +{ + if (db_output_position >= db_max_width-1) + db_printf("\n"); +} + +/* + * Printing + */ +extern void _doprnt(); + +/*VARARGS1*/ +void +db_printf( fmt, va_alist) + char * fmt; + va_dcl +{ + va_list listp; + +#ifdef db_printf_enter + db_printf_enter(); /* optional multiP serialization */ +#endif + va_start(listp); + _doprnt(fmt, &listp, db_id_putc, db_radix, 0); + va_end(listp); +} + +/* alternate name */ + +/*VARARGS1*/ +void +kdbprintf(fmt, va_alist) + char * fmt; + va_dcl +{ + va_list listp; + va_start(listp); + _doprnt(fmt, &listp, db_id_putc, db_radix, 0); + va_end(listp); +} + +#endif MACH_KDB diff --git a/ddb/db_output.h b/ddb/db_output.h new file mode 100644 index 0000000..a3f0de0 --- /dev/null +++ b/ddb/db_output.h @@ -0,0 +1,44 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 8/90 + */ + +/* + * Printing routines for kernel debugger. + */ + +extern void db_force_whitespace(); +extern int db_print_position(); +extern void db_end_line(); +#if 1 +extern void db_printf(); +#else +extern void db_printf( char *fmt, ...); +#endif + + diff --git a/ddb/db_print.c b/ddb/db_print.c new file mode 100644 index 0000000..727af23 --- /dev/null +++ b/ddb/db_print.c @@ -0,0 +1,511 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#include "mach_kdb.h" +#if MACH_KDB + +/* + * Miscellaneous printing. + */ +#include <mach/port.h> +#include <kern/strings.h> +#include <kern/task.h> +#include <kern/thread.h> +#include <kern/queue.h> +#include <ipc/ipc_port.h> +#include <ipc/ipc_space.h> + +#include <machine/db_machdep.h> +#include <machine/thread.h> + +#include <ddb/db_lex.h> +#include <ddb/db_variables.h> +#include <ddb/db_sym.h> +#include <ddb/db_task_thread.h> + +extern unsigned int db_maxoff; + +/* ARGSUSED */ +void +db_show_regs(addr, have_addr, count, modif) + db_expr_t addr; + boolean_t have_addr; + db_expr_t count; + char *modif; +{ + register struct db_variable *regp; + db_expr_t value; + db_addr_t offset; + char * name; + register i; + struct db_var_aux_param aux_param; + task_t task = TASK_NULL; + + aux_param.modif = modif; + aux_param.thread = THREAD_NULL; + if (db_option(modif, 't')) { + if (have_addr) { + if (!db_check_thread_address_valid((thread_t)addr)) + return; + aux_param.thread = (thread_t)addr; + } else + aux_param.thread = db_default_thread; + if (aux_param.thread != THREAD_NULL) + task = aux_param.thread->task; + } + for (regp = db_regs; regp < db_eregs; regp++) { + if (regp->max_level > 1) { + db_printf("bad multi-suffixed register %s\n", regp->name); + continue; + } + aux_param.level = regp->max_level; + for (i = regp->low; i <= regp->high; i++) { + aux_param.suffix[0] = i; + db_read_write_variable(regp, &value, DB_VAR_GET, &aux_param); + if (regp->max_level > 0) + db_printf("%s%d%*s", regp->name, i, + 12-strlen(regp->name)-((i<10)?1:2), ""); + else + db_printf("%-12s", regp->name); + db_printf("%#*N", 2+2*sizeof(vm_offset_t), value); + db_find_xtrn_task_sym_and_offset((db_addr_t)value, &name, + &offset, task); + if (name != 0 && offset <= db_maxoff && offset != value) { + db_printf("\t%s", name); + if (offset != 0) + db_printf("+%#r", offset); + } + db_printf("\n"); + } + } +} + +#define OPTION_LONG 0x001 /* long print option */ +#define OPTION_USER 0x002 /* print ps-like stuff */ +#define OPTION_INDENT 0x100 /* print with indent */ +#define OPTION_THREAD_TITLE 0x200 /* print thread title */ +#define OPTION_TASK_TITLE 0x400 /* print thread title */ + +#ifndef DB_TASK_NAME +#define DB_TASK_NAME(task) /* no task name */ +#define DB_TASK_NAME_TITLE "" /* no task name */ +#endif DB_TASK_NAME + +#ifndef db_thread_fp_used +#define db_thread_fp_used(thread) FALSE +#endif + +char * +db_thread_stat(thread, status) + register thread_t thread; + char *status; +{ + register char *p = status; + + *p++ = (thread->state & TH_RUN) ? 'R' : '.'; + *p++ = (thread->state & TH_WAIT) ? 'W' : '.'; + *p++ = (thread->state & TH_SUSP) ? 'S' : '.'; + *p++ = (thread->state & TH_SWAPPED) ? 'O' : '.'; + *p++ = (thread->state & TH_UNINT) ? 'N' : '.'; + /* show if the FPU has been used */ + *p++ = db_thread_fp_used(thread) ? 'F' : '.'; + *p++ = 0; + return(status); +} + +void +db_print_thread(thread, thread_id, flag) + thread_t thread; + int thread_id; + int flag; +{ + if (flag & OPTION_USER) { + char status[8]; + char *indent = ""; + + if (flag & OPTION_LONG) { + if (flag & OPTION_INDENT) + indent = " "; + if (flag & OPTION_THREAD_TITLE) { + db_printf("%s ID: THREAD STAT STACK PCB", indent); + db_printf(" SUS PRI CONTINUE,WAIT_FUNC\n"); + } + db_printf("%s%3d%c %0*X %s %0*X %0*X %3d %3d ", + indent, thread_id, + (thread == current_thread())? '#': ':', + 2*sizeof(vm_offset_t), thread, + db_thread_stat(thread, status), + 2*sizeof(vm_offset_t), thread->kernel_stack, + 2*sizeof(vm_offset_t), thread->pcb, + thread->suspend_count, thread->sched_pri); + if ((thread->state & TH_SWAPPED) && thread->swap_func) { + db_task_printsym((db_addr_t)thread->swap_func, + DB_STGY_ANY, kernel_task); + db_printf(", "); + } + if (thread->state & TH_WAIT) + db_task_printsym((db_addr_t)thread->wait_event, + DB_STGY_ANY, kernel_task); + db_printf("\n"); + } else { + if (thread_id % 3 == 0) { + if (flag & OPTION_INDENT) + db_printf("\n "); + } else + db_printf(" "); + db_printf("%3d%c(%0*X,%s)", thread_id, + (thread == current_thread())? '#': ':', + 2*sizeof(vm_offset_t), thread, + db_thread_stat(thread, status)); + } + } else { + if (flag & OPTION_INDENT) + db_printf(" %3d (%0*X) ", thread_id, + 2*sizeof(vm_offset_t), thread); + else + db_printf("(%0*X) ", 2*sizeof(vm_offset_t), thread); + db_printf("%c%c%c%c%c", + (thread->state & TH_RUN) ? 'R' : ' ', + (thread->state & TH_WAIT) ? 'W' : ' ', + (thread->state & TH_SUSP) ? 'S' : ' ', + (thread->state & TH_UNINT)? 'N' : ' ', + db_thread_fp_used(thread) ? 'F' : ' '); + if (thread->state & TH_SWAPPED) { + if (thread->swap_func) { + db_printf("("); + db_task_printsym((db_addr_t)thread->swap_func, + DB_STGY_ANY, kernel_task); + db_printf(")"); + } else { + db_printf("(swapped)"); + } + } + if (thread->state & TH_WAIT) { + db_printf(" "); + db_task_printsym((db_addr_t)thread->wait_event, + DB_STGY_ANY, kernel_task); + } + db_printf("\n"); + } +} + +void +db_print_task(task, task_id, flag) + task_t task; + int task_id; + int flag; +{ + thread_t thread; + int thread_id; + + if (flag & OPTION_USER) { + if (flag & OPTION_TASK_TITLE) { + db_printf(" ID: TASK MAP THD SUS PR %s", + DB_TASK_NAME_TITLE); + if ((flag & OPTION_LONG) == 0) + db_printf(" THREADS"); + db_printf("\n"); + } + db_printf("%3d: %0*X %0*X %3d %3d %2d ", + task_id, 2*sizeof(vm_offset_t), task, + 2*sizeof(vm_offset_t), task->map, task->thread_count, + task->suspend_count, task->priority); + DB_TASK_NAME(task); + if (flag & OPTION_LONG) { + if (flag & OPTION_TASK_TITLE) + flag |= OPTION_THREAD_TITLE; + db_printf("\n"); + } else if (task->thread_count <= 1) + flag &= ~OPTION_INDENT; + thread_id = 0; + queue_iterate(&task->thread_list, thread, thread_t, thread_list) { + db_print_thread(thread, thread_id, flag); + flag &= ~OPTION_THREAD_TITLE; + thread_id++; + } + if ((flag & OPTION_LONG) == 0) + db_printf("\n"); + } else { + if (flag & OPTION_TASK_TITLE) + db_printf(" TASK THREADS\n"); + db_printf("%3d (%0*X): ", task_id, 2*sizeof(vm_offset_t), task); + if (task->thread_count == 0) { + db_printf("no threads\n"); + } else { + if (task->thread_count > 1) { + db_printf("%d threads: \n", task->thread_count); + flag |= OPTION_INDENT; + } else + flag &= ~OPTION_INDENT; + thread_id = 0; + queue_iterate(&task->thread_list, thread, + thread_t, thread_list) + db_print_thread(thread, thread_id++, flag); + } + } +} + +/*ARGSUSED*/ +void +db_show_all_threads(addr, have_addr, count, modif) + db_expr_t addr; + boolean_t have_addr; + db_expr_t count; + char * modif; +{ + task_t task; + int task_id; + int flag; + processor_set_t pset; + + flag = OPTION_TASK_TITLE|OPTION_INDENT; + if (db_option(modif, 'u')) + flag |= OPTION_USER; + if (db_option(modif, 'l')) + flag |= OPTION_LONG; + + task_id = 0; + queue_iterate(&all_psets, pset, processor_set_t, all_psets) { + queue_iterate(&pset->tasks, task, task_t, pset_tasks) { + db_print_task(task, task_id, flag); + flag &= ~OPTION_TASK_TITLE; + task_id++; + } + } +} + +db_addr_t +db_task_from_space( + ipc_space_t space, + int *task_id) +{ + task_t task; + int tid = 0; + processor_set_t pset; + + queue_iterate(&all_psets, pset, processor_set_t, all_psets) { + queue_iterate(&pset->tasks, task, task_t, pset_tasks) { + if (task->itk_space == space) { + *task_id = tid; + return (db_addr_t)task; + } + tid++; + } + } + *task_id = 0; + return (0); +} + +/*ARGSUSED*/ +void +db_show_one_thread(addr, have_addr, count, modif) + db_expr_t addr; + boolean_t have_addr; + db_expr_t count; + char * modif; +{ + int flag; + int thread_id; + thread_t thread; + + flag = OPTION_THREAD_TITLE; + if (db_option(modif, 'u')) + flag |= OPTION_USER; + if (db_option(modif, 'l')) + flag |= OPTION_LONG; + + if (!have_addr) { + thread = current_thread(); + if (thread == THREAD_NULL) { + db_error("No thread\n"); + /*NOTREACHED*/ + } + } else + thread = (thread_t) addr; + + if ((thread_id = db_lookup_thread(thread)) < 0) { + db_printf("bad thread address %#X\n", addr); + db_error(0); + /*NOTREACHED*/ + } + + if (flag & OPTION_USER) { + db_printf("TASK%d(%0*X):\n", + db_lookup_task(thread->task), + 2*sizeof(vm_offset_t), thread->task); + db_print_thread(thread, thread_id, flag); + } else { + db_printf("task %d(%0*X): thread %d", + db_lookup_task(thread->task), + 2*sizeof(vm_offset_t), thread->task, thread_id); + db_print_thread(thread, thread_id, flag); + } +} + +/*ARGSUSED*/ +void +db_show_one_task(addr, have_addr, count, modif) + db_expr_t addr; + boolean_t have_addr; + db_expr_t count; + char * modif; +{ + int flag; + int task_id; + task_t task; + + flag = OPTION_TASK_TITLE; + if (db_option(modif, 'u')) + flag |= OPTION_USER; + if (db_option(modif, 'l')) + flag |= OPTION_LONG; + + if (!have_addr) { + task = db_current_task(); + if (task == TASK_NULL) { + db_error("No task\n"); + /*NOTREACHED*/ + } + } else + task = (task_t) addr; + + if ((task_id = db_lookup_task(task)) < 0) { + db_printf("bad task address %#X\n", addr); + db_error(0); + /*NOTREACHED*/ + } + + db_print_task(task, task_id, flag); +} + +int +db_port_iterate(thread, func) + thread_t thread; + void (*func)(); +{ + ipc_entry_t entry; + int index; + int n = 0; + int size; + ipc_space_t space; + + space = thread->task->itk_space; + entry = space->is_table; + size = space->is_table_size; + for (index = 0; index < size; index++, entry++) { + if (entry->ie_bits & MACH_PORT_TYPE_PORT_RIGHTS) + (*func)(index, (ipc_port_t) entry->ie_object, + entry->ie_bits, n++); + } + return(n); +} + +ipc_port_t +db_lookup_port(thread, id) + thread_t thread; + int id; +{ + register ipc_space_t space; + register ipc_entry_t entry; + + if (thread == THREAD_NULL) + return(0); + space = thread->task->itk_space; + if (id < 0 || id >= space->is_table_size) + return(0); + entry = &space->is_table[id]; + if (entry->ie_bits & MACH_PORT_TYPE_PORT_RIGHTS) + return((ipc_port_t)entry->ie_object); + return(0); +} + +static void +db_print_port_id(id, port, bits, n) + int id; + ipc_port_t port; + unsigned bits; + int n; +{ + if (n != 0 && n % 3 == 0) + db_printf("\n"); + db_printf("\tport%d(%s,%x)", id, + (bits & MACH_PORT_TYPE_RECEIVE)? "r": + (bits & MACH_PORT_TYPE_SEND)? "s": "S", port); +} + +static void +db_print_port_id_long( + int id, + ipc_port_t port, + unsigned bits, + int n) +{ + if (n != 0) + db_printf("\n"); + db_printf("\tport%d(%s, port=0x%x", id, + (bits & MACH_PORT_TYPE_RECEIVE)? "r": + (bits & MACH_PORT_TYPE_SEND)? "s": "S", port); + db_printf(", receiver_name=0x%x)", port->ip_receiver_name); +} + +/* ARGSUSED */ +void +db_show_port_id(addr, have_addr, count, modif) + db_expr_t addr; + boolean_t have_addr; + db_expr_t count; + char * modif; +{ + thread_t thread; + + if (!have_addr) { + thread = current_thread(); + if (thread == THREAD_NULL) { + db_error("No thread\n"); + /*NOTREACHED*/ + } + } else + thread = (thread_t) addr; + if (db_lookup_thread(thread) < 0) { + db_printf("Bad thread address %#X\n", addr); + db_error(0); + /*NOTREACHED*/ + } + if (db_option(modif, 'l')) + { + if (db_port_iterate(thread, db_print_port_id_long)) + db_printf("\n"); + return; + } + if (db_port_iterate(thread, db_print_port_id)) + db_printf("\n"); +} + +#endif MACH_KDB diff --git a/ddb/db_print.h b/ddb/db_print.h new file mode 100644 index 0000000..634c5be --- /dev/null +++ b/ddb/db_print.h @@ -0,0 +1,110 @@ +/* + * (c) Copyright 1992, 1993, 1994, 1995 OPEN SOFTWARE FOUNDATION, INC. + * ALL RIGHTS RESERVED + */ +/* + * OSF RI nmk19b2 5/2/95 + */ + +#ifndef _DDB_DB_PRINT_H_ +#define _DDB_DB_PRINT_H_ + +#include <mach/boolean.h> +#include <machine/db_machdep.h> + +/* Prototypes for functions exported by this module. + */ +void db_show_regs( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + char *modif); + +void db_show_all_acts( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + char * modif); + +void db_show_one_act( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + char * modif); + +void db_show_one_task( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + char * modif); + +void db_show_shuttle( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + char * modif); + +void db_show_port_id( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + char * modif); + +void db_show_one_task_vm( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + char *modif); + +void db_show_all_task_vm( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + char *modif); + +void db_show_one_space( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + char * modif); + +void db_show_all_spaces( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + char * modif); + +void db_sys(void); + +int db_port_kmsg_count( + ipc_port_t port); + +db_addr_t db_task_from_space( + ipc_space_t space, + int *task_id); + +void db_show_one_simple_lock( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + char * modif); + +void db_show_one_mutex( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + char * modif); + +void db_show_subsystem( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + char * modif); + +void db_show_runq( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + char * modif); + +#endif /* !_DDB_DB_PRINT_H_ */ diff --git a/ddb/db_run.c b/ddb/db_run.c new file mode 100644 index 0000000..47c39c2 --- /dev/null +++ b/ddb/db_run.c @@ -0,0 +1,441 @@ +/* + * Mach Operating System + * Copyright (c) 1993-1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#include "mach_kdb.h" +#if MACH_KDB + +/* + * Commands to run process. + */ +#include <mach/boolean.h> +#include <machine/db_machdep.h> + +#include <ddb/db_lex.h> +#include <ddb/db_break.h> +#include <ddb/db_access.h> +#include <ddb/db_run.h> +#include <ddb/db_task_thread.h> + +int db_run_mode; + +boolean_t db_sstep_print; +int db_loop_count; +int db_call_depth; + +int db_inst_count; +int db_last_inst_count; +int db_load_count; +int db_store_count; + +#ifndef db_set_single_step +void db_set_task_single_step(/* db_regs_t *, task_t */);/* forward */ +#else +#define db_set_task_single_step(regs,task) db_set_single_step(regs) +#endif +#ifndef db_clear_single_step +void db_clear_task_single_step(/* db_regs_t *, task_t */); +#else +#define db_clear_task_single_step(regs,task) db_clear_single_step(regs) +#endif + +boolean_t +db_stop_at_pc(is_breakpoint, task) + boolean_t *is_breakpoint; + task_t task; +{ + register db_addr_t pc; + register db_thread_breakpoint_t bkpt; + boolean_t db_cond_check(); + + db_clear_task_single_step(DDB_REGS, task); + db_clear_breakpoints(); + db_clear_watchpoints(); + pc = PC_REGS(DDB_REGS); + +#ifdef FIXUP_PC_AFTER_BREAK + if (*is_breakpoint) { + /* + * Breakpoint trap. Fix up the PC if the + * machine requires it. + */ + FIXUP_PC_AFTER_BREAK + pc = PC_REGS(DDB_REGS); + } +#endif + + /* + * Now check for a breakpoint at this address. + */ + bkpt = db_find_thread_breakpoint_here(task, pc); + if (bkpt) { + if (db_cond_check(bkpt)) { + *is_breakpoint = TRUE; + return (TRUE); /* stop here */ + } + } + *is_breakpoint = FALSE; + + if (db_run_mode == STEP_INVISIBLE) { + db_run_mode = STEP_CONTINUE; + return (FALSE); /* continue */ + } + if (db_run_mode == STEP_COUNT) { + return (FALSE); /* continue */ + } + if (db_run_mode == STEP_ONCE) { + if (--db_loop_count > 0) { + if (db_sstep_print) { + db_print_loc_and_inst(pc, task); + } + return (FALSE); /* continue */ + } + } + if (db_run_mode == STEP_RETURN) { + /* WARNING: the following assumes an instruction fits an int */ + db_expr_t ins = db_get_task_value(pc, sizeof(int), FALSE, task); + + /* continue until matching return */ + + if (!inst_trap_return(ins) && + (!inst_return(ins) || --db_call_depth != 0)) { + if (db_sstep_print) { + if (inst_call(ins) || inst_return(ins)) { + register int i; + + db_printf("[after %6d /%4d] ", + db_inst_count, + db_inst_count - db_last_inst_count); + db_last_inst_count = db_inst_count; + for (i = db_call_depth; --i > 0; ) + db_printf(" "); + db_print_loc_and_inst(pc, task); + db_printf("\n"); + } + } + if (inst_call(ins)) + db_call_depth++; + return (FALSE); /* continue */ + } + } + if (db_run_mode == STEP_CALLT) { + /* WARNING: the following assumes an instruction fits an int */ + db_expr_t ins = db_get_task_value(pc, sizeof(int), FALSE, task); + + /* continue until call or return */ + + if (!inst_call(ins) && + !inst_return(ins) && + !inst_trap_return(ins)) { + return (FALSE); /* continue */ + } + } + if (db_find_breakpoint_here(task, pc)) + return(FALSE); + db_run_mode = STEP_NONE; + return (TRUE); +} + +void +db_restart_at_pc(watchpt, task) + boolean_t watchpt; + task_t task; +{ + register db_addr_t pc = PC_REGS(DDB_REGS), brpc; + + if ((db_run_mode == STEP_COUNT) || + (db_run_mode == STEP_RETURN) || + (db_run_mode == STEP_CALLT)) { + db_expr_t ins; + + /* + * We are about to execute this instruction, + * so count it now. + */ + + ins = db_get_task_value(pc, sizeof(int), FALSE, task); + db_inst_count++; + db_load_count += inst_load(ins); + db_store_count += inst_store(ins); +#ifdef SOFTWARE_SSTEP + /* Account for instructions in delay slots */ + brpc = next_instr_address(pc,1,task); + if ((brpc != pc) && (inst_branch(ins) || inst_call(ins))) { + /* Note: this ~assumes an instruction <= sizeof(int) */ + ins = db_get_task_value(brpc, sizeof(int), FALSE, task); + db_inst_count++; + db_load_count += inst_load(ins); + db_store_count += inst_store(ins); + } +#endif /* SOFTWARE_SSTEP */ + } + + if (db_run_mode == STEP_CONTINUE) { + if (watchpt || db_find_breakpoint_here(task, pc)) { + /* + * Step over breakpoint/watchpoint. + */ + db_run_mode = STEP_INVISIBLE; + db_set_task_single_step(DDB_REGS, task); + } else { + db_set_breakpoints(); + db_set_watchpoints(); + } + } else { + db_set_task_single_step(DDB_REGS, task); + } +} + +void +db_single_step(regs, task) + db_regs_t *regs; + task_t task; +{ + if (db_run_mode == STEP_CONTINUE) { + db_run_mode = STEP_INVISIBLE; + db_set_task_single_step(regs, task); + } +} + +#ifdef SOFTWARE_SSTEP +/* + * Software implementation of single-stepping. + * If your machine does not have a trace mode + * similar to the vax or sun ones you can use + * this implementation, done for the mips. + * Just define the above conditional and provide + * the functions/macros defined below. + * + * extern boolean_t + * inst_branch(), returns true if the instruction might branch + * extern unsigned + * branch_taken(), return the address the instruction might + * branch to + * db_getreg_val(); return the value of a user register, + * as indicated in the hardware instruction + * encoding, e.g. 8 for r8 + * + * next_instr_address(pc,bd,task) returns the address of the first + * instruction following the one at "pc", + * which is either in the taken path of + * the branch (bd==1) or not. This is + * for machines (mips) with branch delays. + * + * A single-step may involve at most 2 breakpoints - + * one for branch-not-taken and one for branch taken. + * If one of these addresses does not already have a breakpoint, + * we allocate a breakpoint and save it here. + * These breakpoints are deleted on return. + */ +db_breakpoint_t db_not_taken_bkpt = 0; +db_breakpoint_t db_taken_bkpt = 0; + +db_breakpoint_t +db_find_temp_breakpoint(task, addr) + task_t task; + db_addr_t addr; +{ + if (db_taken_bkpt && (db_taken_bkpt->address == addr) && + db_taken_bkpt->task == task) + return db_taken_bkpt; + if (db_not_taken_bkpt && (db_not_taken_bkpt->address == addr) && + db_not_taken_bkpt->task == task) + return db_not_taken_bkpt; + return 0; +} + +void +db_set_task_single_step(regs, task) + register db_regs_t *regs; + task_t task; +{ + db_addr_t pc = PC_REGS(regs), brpc; + register unsigned int inst; + register boolean_t unconditional; + + /* + * User was stopped at pc, e.g. the instruction + * at pc was not executed. + */ + inst = db_get_task_value(pc, sizeof(int), FALSE, task); + if (inst_branch(inst) || inst_call(inst)) { + extern db_expr_t getreg_val(); + + brpc = branch_taken(inst, pc, getreg_val, regs); + if (brpc != pc) { /* self-branches are hopeless */ + db_taken_bkpt = db_set_temp_breakpoint(task, brpc); + } else + db_taken_bkpt = 0; + pc = next_instr_address(pc,1,task); + } + + /* check if this control flow instruction is an unconditional transfer */ + unconditional = inst_unconditional_flow_transfer(inst); + + pc = next_instr_address(pc,0,task); + /* + We only set the sequential breakpoint if previous instruction was not + an unconditional change of flow of control. If the previous instruction + is an unconditional change of flow of control, setting a breakpoint in the + next sequential location may set a breakpoint in data or in another routine, + which could screw up either the program or the debugger. + (Consider, for instance, that the next sequential instruction is the + start of a routine needed by the debugger.) + */ + if (!unconditional && db_find_breakpoint_here(task, pc) == 0) { + db_not_taken_bkpt = db_set_temp_breakpoint(task, pc); + } + else + db_not_taken_bkpt = 0; +} + +void +db_clear_task_single_step(regs, task) + db_regs_t *regs; + task_t task; +{ + if (db_taken_bkpt != 0) { + db_delete_temp_breakpoint(task, db_taken_bkpt); + db_taken_bkpt = 0; + } + if (db_not_taken_bkpt != 0) { + db_delete_temp_breakpoint(task, db_not_taken_bkpt); + db_not_taken_bkpt = 0; + } +} + +#endif /* SOFTWARE_SSTEP */ + + +extern int db_cmd_loop_done; + +/* single-step */ +/*ARGSUSED*/ +void +db_single_step_cmd(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char * modif; +{ + boolean_t print = FALSE; + + if (count == -1) + count = 1; + + if (modif[0] == 'p') + print = TRUE; + + db_run_mode = STEP_ONCE; + db_loop_count = count; + db_sstep_print = print; + db_inst_count = 0; + db_last_inst_count = 0; + db_load_count = 0; + db_store_count = 0; + + db_cmd_loop_done = 1; +} + +/* trace and print until call/return */ +/*ARGSUSED*/ +void +db_trace_until_call_cmd(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char * modif; +{ + boolean_t print = FALSE; + + if (modif[0] == 'p') + print = TRUE; + + db_run_mode = STEP_CALLT; + db_sstep_print = print; + db_inst_count = 0; + db_last_inst_count = 0; + db_load_count = 0; + db_store_count = 0; + + db_cmd_loop_done = 1; +} + +/*ARGSUSED*/ +void +db_trace_until_matching_cmd(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char * modif; +{ + boolean_t print = FALSE; + + if (modif[0] == 'p') + print = TRUE; + + db_run_mode = STEP_RETURN; + db_call_depth = 1; + db_sstep_print = print; + db_inst_count = 0; + db_last_inst_count = 0; + db_load_count = 0; + db_store_count = 0; + + db_cmd_loop_done = 1; +} + +/* continue */ +/*ARGSUSED*/ +void +db_continue_cmd(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char * modif; +{ + if (modif[0] == 'c') + db_run_mode = STEP_COUNT; + else + db_run_mode = STEP_CONTINUE; + db_inst_count = 0; + db_last_inst_count = 0; + db_load_count = 0; + db_store_count = 0; + + db_cmd_loop_done = 1; +} + +boolean_t +db_in_single_step() +{ + return(db_run_mode != STEP_NONE && db_run_mode != STEP_CONTINUE); +} + +#endif MACH_KDB diff --git a/ddb/db_run.h b/ddb/db_run.h new file mode 100644 index 0000000..fcc8ece --- /dev/null +++ b/ddb/db_run.h @@ -0,0 +1,37 @@ +/* + * Mach Operating System + * Copyright (c) 1991 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +extern int db_run_mode; + +/* modes the system may be running in */ + +#define STEP_NONE 0 +#define STEP_ONCE 1 +#define STEP_RETURN 2 +#define STEP_CALLT 3 +#define STEP_CONTINUE 4 +#define STEP_INVISIBLE 5 +#define STEP_COUNT 6 diff --git a/ddb/db_sym.c b/ddb/db_sym.c new file mode 100644 index 0000000..c48a48f --- /dev/null +++ b/ddb/db_sym.c @@ -0,0 +1,523 @@ +/* + * Mach Operating System + * Copyright (c) 1992,1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#include "mach_kdb.h" +#if MACH_KDB + +#include <mach/std_types.h> +#include <kern/strings.h> +#include <machine/db_machdep.h> +#include <ddb/db_sym.h> +#include <ddb/db_task_thread.h> + +#include <vm/vm_map.h> /* vm_map_t */ + +/* + * Multiple symbol tables + */ +#define MAXNOSYMTABS 5 /* mach, bootstrap, ux, emulator, 1 spare */ + +db_symtab_t db_symtabs[MAXNOSYMTABS] = {{0,},}; +int db_nsymtab = 0; + +db_symtab_t *db_last_symtab; + +db_sym_t db_lookup(); /* forward */ + +/* + * Add symbol table, with given name, to list of symbol tables. + */ +boolean_t +db_add_symbol_table(type, start, end, name, ref, map_pointer) + int type; + char *start; + char *end; + char *name; + char *ref; + char *map_pointer; +{ + register db_symtab_t *st; + extern vm_map_t kernel_map; + + if (db_nsymtab >= MAXNOSYMTABS) + return (FALSE); + + st = &db_symtabs[db_nsymtab]; + st->type = type; + st->start = start; + st->end = end; + st->private = ref; + st->map_pointer = (map_pointer == (char *)kernel_map)? 0: map_pointer; + strcpy(st->name, name); + + db_nsymtab++; + + return (TRUE); +} + +/* + * db_qualify("vm_map", "ux") returns "ux::vm_map". + * + * Note: return value points to static data whose content is + * overwritten by each call... but in practice this seems okay. + */ +static char * +db_qualify(symname, symtabname) + char *symname; + register char *symtabname; +{ + static char tmp[256]; + register char *s; + + s = tmp; + while (*s++ = *symtabname++) { + } + s[-1] = ':'; + *s++ = ':'; + while (*s++ = *symname++) { + } + return tmp; +} + + +boolean_t +db_eqname( char* src, char* dst, char c ) +{ + if (!strcmp(src, dst)) + return (TRUE); + if (src[0] == c) + return (!strcmp(src+1,dst)); + return (FALSE); +} + +boolean_t +db_value_of_name(name, valuep) + char *name; + db_expr_t *valuep; +{ + db_sym_t sym; + + sym = db_lookup(name); + if (sym == DB_SYM_NULL) + return (FALSE); + db_symbol_values(0, sym, &name, valuep); + return (TRUE); +} + +/* + * Lookup a symbol. + * If the symbol has a qualifier (e.g., ux::vm_map), + * then only the specified symbol table will be searched; + * otherwise, all symbol tables will be searched. + */ +db_sym_t +db_lookup(symstr) + char *symstr; +{ + db_sym_t sp; + register int i; + int symtab_start = 0; + int symtab_end = db_nsymtab; + register char *cp; + + /* + * Look for, remove, and remember any symbol table specifier. + */ + for (cp = symstr; *cp; cp++) { + if (*cp == ':' && cp[1] == ':') { + *cp = '\0'; + for (i = 0; i < db_nsymtab; i++) { + if (! strcmp(symstr, db_symtabs[i].name)) { + symtab_start = i; + symtab_end = i + 1; + break; + } + } + *cp = ':'; + if (i == db_nsymtab) + db_error("Invalid symbol table name\n"); + symstr = cp+2; + } + } + + /* + * Look in the specified set of symbol tables. + * Return on first match. + */ + for (i = symtab_start; i < symtab_end; i++) { + if (sp = X_db_lookup(&db_symtabs[i], symstr)) { + db_last_symtab = &db_symtabs[i]; + return sp; + } + } + return 0; +} + +/* + * Common utility routine to parse a symbol string into a file + * name, a symbol name and line number. + * This routine is called from X_db_lookup if the object dependent + * handler supports qualified search with a file name or a line number. + * It parses the symbol string, and call an object dependent routine + * with parsed file name, symbol name and line number. + */ +db_sym_t +db_sym_parse_and_lookup(func, symtab, symstr) + db_sym_t (*func)(); + db_symtab_t *symtab; + char *symstr; +{ + register char *p; + register n; + int n_name; + int line_number; + char *file_name = 0; + char *sym_name = 0; + char *component[3]; + db_sym_t found = DB_SYM_NULL; + + /* + * disassemble the symbol into components: + * [file_name:]symbol[:line_nubmer] + */ + component[0] = symstr; + component[1] = component[2] = 0; + for (p = symstr, n = 1; *p; p++) { + if (*p == ':') { + if (n >= 3) + break; + *p = 0; + component[n++] = p+1; + } + } + if (*p != 0) + goto out; + line_number = 0; + n_name = n; + p = component[n-1]; + if (*p >= '0' && *p <= '9') { + if (n == 1) + goto out; + for (line_number = 0; *p; p++) { + if (*p < '0' || *p > '9') + goto out; + line_number = line_number*10 + *p - '0'; + } + n_name--; + } else if (n >= 3) + goto out; + if (n_name == 1) { + for (p = component[0]; *p && *p != '.'; p++); + if (*p == '.') { + file_name = component[0]; + sym_name = 0; + } else { + file_name = 0; + sym_name = component[0]; + } + } else { + file_name = component[0]; + sym_name = component[1]; + } + found = func(symtab, file_name, sym_name, line_number); + +out: + while (--n >= 1) + component[n][-1] = ':'; + return(found); +} + +/* + * Does this symbol name appear in more than one symbol table? + * Used by db_symbol_values to decide whether to qualify a symbol. + */ +boolean_t db_qualify_ambiguous_names = FALSE; + +boolean_t +db_name_is_ambiguous(sym_name) + char *sym_name; +{ + register int i; + register + boolean_t found_once = FALSE; + + if (!db_qualify_ambiguous_names) + return FALSE; + + for (i = 0; i < db_nsymtab; i++) { + if (X_db_lookup(&db_symtabs[i], sym_name)) { + if (found_once) + return TRUE; + found_once = TRUE; + } + } + return FALSE; +} + + +db_sym_t db_search_in_task_symbol(); + +/* + * Find the closest symbol to val, and return its name + * and the difference between val and the symbol found. + * + * Logic change. If the task argument is non NULL and a + * matching symbol is found in a symbol table which explictly + * specifies its map to be task->map, that symbol will have + * precedence over any symbol from a symbol table will a null + * map. This allows overlapping kernel/user maps to work correctly. + * + */ +db_sym_t +db_search_task_symbol(val, strategy, offp, task) + register db_addr_t val; + db_strategy_t strategy; + db_addr_t *offp; /* better be unsigned */ + task_t task; +{ + db_sym_t ret; + + if (task != TASK_NULL) + ret = db_search_in_task_symbol(val, strategy, offp, task); + else + { + ret = db_search_in_task_symbol(val, strategy, offp, task); + /* + db_search_in_task_symbol will return success with + a very large offset when it should have failed. + */ + if (ret == DB_SYM_NULL || (*offp) > 0x1000000) + { + task = db_current_task(); + ret = db_search_in_task_symbol(val, strategy, offp, task); + } + } + + return ret; +} + +db_sym_t +db_search_in_task_symbol(val, strategy, offp, task) + register db_addr_t val; + db_strategy_t strategy; + db_addr_t *offp; + task_t task; +{ + register vm_size_t diff; + vm_size_t newdiff; + register int i; + db_symtab_t *sp; + db_sym_t ret = DB_SYM_NULL, sym; + vm_map_t map_for_val; + + map_for_val = (task == TASK_NULL)? VM_MAP_NULL: task->map; + newdiff = diff = ~0; + db_last_symtab = (db_symtab_t *) 0; + for (sp = &db_symtabs[0], i = 0; i < db_nsymtab; sp++, i++) + { + newdiff = ~0; + if ((vm_map_t)sp->map_pointer == VM_MAP_NULL || + (vm_map_t)sp->map_pointer == map_for_val) + { + sym = X_db_search_symbol(sp, val, strategy, (db_expr_t*)&newdiff); + if (sym == DB_SYM_NULL) + continue; + if (db_last_symtab == (db_symtab_t *) 0) + { /* first hit */ + db_last_symtab = sp; + diff = newdiff; + ret = sym; + continue; + } + if ((vm_map_t) sp->map_pointer == VM_MAP_NULL && + (vm_map_t) db_last_symtab->map_pointer == VM_MAP_NULL && + newdiff < diff ) + { /* closer null map match */ + db_last_symtab = sp; + diff = newdiff; + ret = sym; + continue; + } + if ((vm_map_t) sp->map_pointer != VM_MAP_NULL && + (newdiff < 0x100000) && + ((vm_map_t) db_last_symtab->map_pointer == VM_MAP_NULL || + newdiff < diff )) + { /* update if new is in matching map and symbol is "close", + and + old is VM_MAP_NULL or old in is matching map but is further away + */ + db_last_symtab = sp; + diff = newdiff; + ret = sym; + continue; + } + } + } + + *offp = diff; + return ret; +} + +/* + * Return name and value of a symbol + */ +void +db_symbol_values(stab, sym, namep, valuep) + db_symtab_t *stab; + db_sym_t sym; + char **namep; + db_expr_t *valuep; +{ + db_expr_t value; + char *name; + + if (sym == DB_SYM_NULL) { + *namep = 0; + return; + } + if (stab == 0) + stab = db_last_symtab; + + X_db_symbol_values(stab, sym, &name, &value); + + if (db_name_is_ambiguous(name)) + *namep = db_qualify(name, db_last_symtab->name); + else + *namep = name; + if (valuep) + *valuep = value; +} + + +/* + * Print the closest symbol to value + * + * After matching the symbol according to the given strategy + * we print it in the name+offset format, provided the symbol's + * value is close enough (eg smaller than db_maxoff). + * We also attempt to print [filename:linenum] when applicable + * (eg for procedure names). + * + * If we could not find a reasonable name+offset representation, + * then we just print the value in hex. Small values might get + * bogus symbol associations, e.g. 3 might get some absolute + * value like _INCLUDE_VERSION or something, therefore we do + * not accept symbols whose value is zero (and use plain hex). + */ + +unsigned int db_maxoff = 0x4000; + +void +db_task_printsym(off, strategy, task) + db_expr_t off; + db_strategy_t strategy; + task_t task; +{ + db_addr_t d; + char *filename; + char *name; + db_expr_t value; + int linenum; + db_sym_t cursym; + + cursym = db_search_task_symbol(off, strategy, &d, task); + db_symbol_values(0, cursym, &name, &value); + if (name == 0 || d >= db_maxoff || value == 0) { + db_printf("%#n", off); + return; + } + db_printf("%s", name); + if (d) + db_printf("+0x%x", d); + if (strategy == DB_STGY_PROC) { + if (db_line_at_pc(cursym, &filename, &linenum, off)) { + db_printf(" [%s", filename); + if (linenum > 0) + db_printf(":%d", linenum); + db_printf("]"); + } + } +} + +void +db_printsym(off, strategy) + db_expr_t off; + db_strategy_t strategy; +{ + db_task_printsym(off, strategy, TASK_NULL); +} + +boolean_t +db_line_at_pc( sym, filename, linenum, pc) + db_sym_t sym; + char **filename; + int *linenum; + db_expr_t pc; +{ + return (db_last_symtab) ? + X_db_line_at_pc( db_last_symtab, sym, filename, linenum, pc) : + FALSE; +} + +/* + * Switch into symbol-table specific routines + */ + +extern boolean_t aout_db_sym_init(), aout_db_line_at_pc(); +extern db_sym_t aout_db_lookup(), aout_db_search_symbol(); +extern void aout_db_symbol_values(); + +extern boolean_t coff_db_sym_init(), coff_db_line_at_pc(); +extern db_sym_t coff_db_lookup(), coff_db_search_symbol(); +extern void coff_db_symbol_values(); + +struct db_sym_switch x_db[] = { + + /* BSD a.out format (really, sdb/dbx(1) symtabs) */ +#ifdef DB_NO_AOUT + { 0,}, +#else /* DB_NO_AOUT */ + { aout_db_sym_init, aout_db_lookup, aout_db_search_symbol, + aout_db_line_at_pc, aout_db_symbol_values }, +#endif /* DB_NO_AOUT */ + +#ifdef DB_NO_COFF + { 0,}, +#else /* DB_NO_COFF */ + { coff_db_sym_init, coff_db_lookup, coff_db_search_symbol, + coff_db_line_at_pc, coff_db_symbol_values }, +#endif /* DB_NO_COFF */ + + /* Machdep, not inited here */ + { 0,} + +}; + +#endif MACH_KDB diff --git a/ddb/db_sym.h b/ddb/db_sym.h new file mode 100644 index 0000000..dd320b7 --- /dev/null +++ b/ddb/db_sym.h @@ -0,0 +1,200 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 8/90 + */ + +#include <mach/boolean.h> +#include <mach/machine/vm_types.h> +#include <machine/db_machdep.h> + +/* + * This module can handle multiple symbol tables, + * of multiple types, at the same time + */ +#define SYMTAB_NAME_LEN 32 + +typedef struct { + int type; +#define SYMTAB_AOUT 0 +#define SYMTAB_COFF 1 +#define SYMTAB_MACHDEP 2 + char *start; /* symtab location */ + char *end; + char *private; /* optional machdep pointer */ + char *map_pointer; /* symbols are for this map only, + if not null */ + char name[SYMTAB_NAME_LEN]; + /* symtab name */ +} db_symtab_t; + +extern db_symtab_t *db_last_symtab; /* where last symbol was found */ + +/* + * Symbol representation is specific to the symtab style: + * BSD compilers use dbx' nlist, other compilers might use + * a different one + */ +typedef char * db_sym_t; /* opaque handle on symbols */ +#define DB_SYM_NULL ((db_sym_t)0) + +/* + * Non-stripped symbol tables will have duplicates, for instance + * the same string could match a parameter name, a local var, a + * global var, etc. + * We are most concerned with the following matches. + */ +typedef int db_strategy_t; /* search strategy */ + +#define DB_STGY_ANY 0 /* anything goes */ +#define DB_STGY_XTRN 1 /* only external symbols */ +#define DB_STGY_PROC 2 /* only procedures */ + +extern boolean_t db_qualify_ambiguous_names; + /* if TRUE, check across symbol tables + * for multiple occurrences of a name. + * Might slow down quite a bit + * ..but the machine has nothing + * else to do, now does it ? */ + +/* + * Functions exported by the symtable module + */ + +/* extend the list of symbol tables */ + +extern boolean_t db_add_symbol_table( int type, + char * start, + char * end, + char *name, + char *ref, + char *map_pointer ); + +/* find symbol value given name */ + +extern int db_value_of_name( char* name, db_expr_t* valuep); + +/* find symbol given value */ + +extern db_sym_t db_search_task_symbol( db_addr_t val, + db_strategy_t strategy, + db_addr_t *offp, + task_t task ); + +/* return name and value of symbol */ + +extern void db_symbol_values( db_symtab_t *stab, + db_sym_t sym, + char** namep, + db_expr_t* valuep); + +/* find name&value given approx val */ + +#define db_find_sym_and_offset(val,namep,offp) \ + db_symbol_values(0, db_search_symbol(val,DB_STGY_ANY,offp),namep,0) + +/* ditto, but no locals */ +#define db_find_xtrn_sym_and_offset(val,namep,offp) \ + db_symbol_values(0, db_search_symbol(val,DB_STGY_XTRN,offp),namep,0) + +/* find name&value given approx val */ + +#define db_find_task_sym_and_offset(val,namep,offp,task) \ + db_symbol_values(0, db_search_task_symbol(val,DB_STGY_ANY,offp,task), \ + namep, 0) + +/* ditto, but no locals */ +#define db_find_xtrn_task_sym_and_offset(val,namep,offp,task) \ + db_symbol_values(0, db_search_task_symbol(val,DB_STGY_XTRN,offp,task), \ + namep,0) + +/* find symbol in current task */ +#define db_search_symbol(val,strgy,offp) \ + db_search_task_symbol(val,strgy,offp,0) + +/* strcmp, modulo leading char */ +extern boolean_t db_eqname( char* src, char* dst, char c ); + +/* print closest symbol to a value */ +extern void db_task_printsym( db_expr_t off, + db_strategy_t strategy, + task_t task); + +/* print closest symbol to a value */ +extern void db_printsym( db_expr_t off, db_strategy_t strategy); + +/* + * Symbol table switch, defines the interface + * to symbol-table specific routines. + * [NOTE: incomplete prototypes cuz broken compiler] + */ + +extern struct db_sym_switch { + + boolean_t (*init)( +/* char *start, + char *end, + char *name, + char *task_addr +*/ ); + + db_sym_t (*lookup)( +/* db_symtab_t *stab, + char *symstr +*/ ); + db_sym_t (*search_symbol)( +/* db_symtab_t *stab, + db_addr_t off, + db_strategy_t strategy, + db_expr_t *diffp +*/ ); + + boolean_t (*line_at_pc)( +/* db_symtab_t *stab, + db_sym_t sym, + char **file, + int *line, + db_expr_t pc +*/ ); + + void (*symbol_values)( +/* db_sym_t sym, + char **namep, + db_expr_t *valuep +*/ ); + +} x_db[]; + +#ifndef symtab_type +#define symtab_type(s) SYMTAB_AOUT +#endif + +#define X_db_sym_init(s,e,n,t) x_db[symtab_type(s)].init(s,e,n,t) +#define X_db_lookup(s,n) x_db[(s)->type].lookup(s,n) +#define X_db_search_symbol(s,o,t,d) x_db[(s)->type].search_symbol(s,o,t,d) +#define X_db_line_at_pc(s,p,f,l,a) x_db[(s)->type].line_at_pc(s,p,f,l,a) +#define X_db_symbol_values(s,p,n,v) x_db[(s)->type].symbol_values(p,n,v) diff --git a/ddb/db_task_thread.c b/ddb/db_task_thread.c new file mode 100644 index 0000000..8ab8cde --- /dev/null +++ b/ddb/db_task_thread.c @@ -0,0 +1,303 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +#include "mach_kdb.h" +#if MACH_KDB + +#include <machine/db_machdep.h> +#include <ddb/db_task_thread.h> +#include <ddb/db_variables.h> + + + +/* + * Following constants are used to prevent infinite loop of task + * or thread search due to the incorrect list. + */ +#define DB_MAX_TASKID 0x10000 /* max # of tasks */ +#define DB_MAX_THREADID 0x10000 /* max # of threads in a task */ +#define DB_MAX_PSETS 0x10000 /* max # of processor sets */ + +task_t db_default_task; /* default target task */ +thread_t db_default_thread; /* default target thread */ + +/* + * search valid task queue, and return the queue position as the task id + */ +int +db_lookup_task(target_task) + task_t target_task; +{ + register task_t task; + register task_id; + register processor_set_t pset; + register npset = 0; + + task_id = 0; + if (queue_first(&all_psets) == 0) + return(-1); + queue_iterate(&all_psets, pset, processor_set_t, all_psets) { + if (npset++ >= DB_MAX_PSETS) + return(-1); + if (queue_first(&pset->tasks) == 0) + continue; + queue_iterate(&pset->tasks, task, task_t, pset_tasks) { + if (target_task == task) + return(task_id); + if (task_id++ >= DB_MAX_TASKID) + return(-1); + } + } + return(-1); +} + +/* + * search thread queue of the task, and return the queue position + */ +int +db_lookup_task_thread(task, target_thread) + task_t task; + thread_t target_thread; +{ + register thread_t thread; + register thread_id; + + thread_id = 0; + if (queue_first(&task->thread_list) == 0) + return(-1); + queue_iterate(&task->thread_list, thread, thread_t, thread_list) { + if (target_thread == thread) + return(thread_id); + if (thread_id++ >= DB_MAX_THREADID) + return(-1); + } + return(-1); +} + +/* + * search thread queue of every valid task, and return the queue position + * as the thread id. + */ +int +db_lookup_thread(target_thread) + thread_t target_thread; +{ + register thread_id; + register task_t task; + register processor_set_t pset; + register ntask = 0; + register npset = 0; + + if (queue_first(&all_psets) == 0) + return(-1); + queue_iterate(&all_psets, pset, processor_set_t, all_psets) { + if (npset++ >= DB_MAX_PSETS) + return(-1); + if (queue_first(&pset->tasks) == 0) + continue; + queue_iterate(&pset->tasks, task, task_t, pset_tasks) { + if (ntask++ > DB_MAX_TASKID) + return(-1); + if (task->thread_count == 0) + continue; + thread_id = db_lookup_task_thread(task, target_thread); + if (thread_id >= 0) + return(thread_id); + } + } + return(-1); +} + +/* + * check the address is a valid thread address + */ +boolean_t +db_check_thread_address_valid(thread) + thread_t thread; +{ + if (db_lookup_thread(thread) < 0) { + db_printf("Bad thread address 0x%x\n", thread); + db_flush_lex(); + return(FALSE); + } else + return(TRUE); +} + +/* + * convert task_id(queue postion) to task address + */ +task_t +db_lookup_task_id(task_id) + register task_id; +{ + register task_t task; + register processor_set_t pset; + register npset = 0; + + if (task_id > DB_MAX_TASKID) + return(TASK_NULL); + if (queue_first(&all_psets) == 0) + return(TASK_NULL); + queue_iterate(&all_psets, pset, processor_set_t, all_psets) { + if (npset++ >= DB_MAX_PSETS) + return(TASK_NULL); + if (queue_first(&pset->tasks) == 0) + continue; + queue_iterate(&pset->tasks, task, task_t, pset_tasks) { + if (task_id-- <= 0) + return(task); + } + } + return(TASK_NULL); +} + +/* + * convert (task_id, thread_id) pair to thread address + */ +static thread_t +db_lookup_thread_id(task, thread_id) + task_t task; + register thread_id; +{ + register thread_t thread; + + + if (thread_id > DB_MAX_THREADID) + return(THREAD_NULL); + if (queue_first(&task->thread_list) == 0) + return(THREAD_NULL); + queue_iterate(&task->thread_list, thread, thread_t, thread_list) { + if (thread_id-- <= 0) + return(thread); + } + return(THREAD_NULL); +} + +/* + * get next parameter from a command line, and check it as a valid + * thread address + */ +boolean_t +db_get_next_thread(threadp, position) + thread_t *threadp; + int position; +{ + db_expr_t value; + thread_t thread; + + *threadp = THREAD_NULL; + if (db_expression(&value)) { + thread = (thread_t) value; + if (!db_check_thread_address_valid(thread)) { + db_flush_lex(); + return(FALSE); + } + } else if (position <= 0) { + thread = db_default_thread; + } else + return(FALSE); + *threadp = thread; + return(TRUE); +} + +/* + * check the default thread is still valid + * ( it is called in entering DDB session ) + */ +void +db_init_default_thread() +{ + if (db_lookup_thread(db_default_thread) < 0) { + db_default_thread = THREAD_NULL; + db_default_task = TASK_NULL; + } else + db_default_task = db_default_thread->task; +} + +/* + * set or get default thread which is used when /t or :t option is specified + * in the command line + */ +/* ARGSUSED */ +int +db_set_default_thread(vp, valuep, flag) + struct db_variable *vp; + db_expr_t *valuep; + int flag; +{ + thread_t thread; + + if (flag != DB_VAR_SET) { + *valuep = (db_expr_t) db_default_thread; + return(0); + } + thread = (thread_t) *valuep; + if (thread != THREAD_NULL && !db_check_thread_address_valid(thread)) + db_error(0); + /* NOTREACHED */ + db_default_thread = thread; + if (thread) + db_default_task = thread->task; + return(0); +} + +/* + * convert $taskXXX[.YYY] type DDB variable to task or thread address + */ +int +db_get_task_thread(vp, valuep, flag, ap) + struct db_variable *vp; + db_expr_t *valuep; + int flag; + db_var_aux_param_t ap; +{ + task_t task; + thread_t thread; + + if (flag != DB_VAR_GET) { + db_error("Cannot set to $task variable\n"); + /* NOTREACHED */ + } + if ((task = db_lookup_task_id(ap->suffix[0])) == TASK_NULL) { + db_printf("no such task($task%d)\n", ap->suffix[0]); + db_error(0); + /* NOTREACHED */ + } + if (ap->level <= 1) { + *valuep = (db_expr_t) task; + return(0); + } + if ((thread = db_lookup_thread_id(task, ap->suffix[1])) == THREAD_NULL){ + db_printf("no such thread($task%d.%d)\n", + ap->suffix[0], ap->suffix[1]); + db_error(0); + /* NOTREACHED */ + } + *valuep = (db_expr_t) thread; + return(0); +} + +#endif MACH_KDB diff --git a/ddb/db_task_thread.h b/ddb/db_task_thread.h new file mode 100644 index 0000000..26a071c --- /dev/null +++ b/ddb/db_task_thread.h @@ -0,0 +1,51 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +#ifndef _DDB_DB_TASK_THREAD_H_ +#define _DDB_DB_TASK_THREAD_H_ + +#include <kern/task.h> +#include <kern/thread.h> + +#define db_current_task() \ + ((current_thread())? current_thread()->task: TASK_NULL) +#define db_target_space(thread, user_space) \ + ((!(user_space))? TASK_NULL: \ + (thread)? (thread)->task: db_current_task()) +#define db_is_current_task(task) \ + ((task) == TASK_NULL || (task) == db_current_task()) + +extern task_t db_default_task; /* default target task */ +extern thread_t db_default_thread; /* default target thread */ + +extern int db_lookup_task(/* task_t */); +extern int db_lookup_thread(/* thread_t */); +extern int db_lookup_task_thread(/* task_t, thread_t */); +extern boolean_t db_check_thread_address_valid(/* db_expr_t */); +extern boolean_t db_get_next_thread(/* thread_t *, int */); +extern void db_init_default_thread(); + +#endif _DDB_DB_TASK_THREAD_H_ diff --git a/ddb/db_trap.c b/ddb/db_trap.c new file mode 100644 index 0000000..dbb5892 --- /dev/null +++ b/ddb/db_trap.c @@ -0,0 +1,107 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#include "mach_kdb.h" +#if MACH_KDB + +/* + * Trap entry point to kernel debugger. + */ +#include <mach/boolean.h> +#include <machine/db_machdep.h> +#include <ddb/db_command.h> +#include <ddb/db_access.h> +#include <ddb/db_break.h> +#include <ddb/db_task_thread.h> + + + +extern jmp_buf_t *db_recover; + +extern void db_restart_at_pc(); +extern boolean_t db_stop_at_pc(); + +extern int db_inst_count; +extern int db_load_count; +extern int db_store_count; + +void +db_task_trap(type, code, user_space) + int type, code; + boolean_t user_space; +{ + jmp_buf_t db_jmpbuf; + jmp_buf_t *prev; + boolean_t bkpt; + boolean_t watchpt; + void db_init_default_thread(); + void db_check_breakpoint_valid(); + task_t task_space; + + task_space = db_target_space(current_thread(), user_space); + bkpt = IS_BREAKPOINT_TRAP(type, code); + watchpt = IS_WATCHPOINT_TRAP(type, code); + + db_init_default_thread(); + db_check_breakpoint_valid(); + if (db_stop_at_pc(&bkpt, task_space)) { + if (db_inst_count) { + db_printf("After %d instructions (%d loads, %d stores),\n", + db_inst_count, db_load_count, db_store_count); + } + if (bkpt) + db_printf("Breakpoint at "); + else if (watchpt) + db_printf("Watchpoint at "); + else + db_printf("Stopped at "); + db_dot = PC_REGS(DDB_REGS); + + prev = db_recover; + if (_setjmp(db_recover = &db_jmpbuf) == 0) + db_print_loc_and_inst(db_dot, task_space); + else + db_printf("Trouble printing location %#X.\n", db_dot); + db_recover = prev; + + db_command_loop(); + } + + db_restart_at_pc(watchpt, task_space); +} + +void +db_trap(type, code) + int type, code; +{ + db_task_trap(type, code, !DB_VALID_KERN_ADDR(PC_REGS(DDB_REGS))); +} + +#endif MACH_KDB diff --git a/ddb/db_variables.c b/ddb/db_variables.c new file mode 100644 index 0000000..541dca1 --- /dev/null +++ b/ddb/db_variables.c @@ -0,0 +1,241 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#include "mach_kdb.h" +#if MACH_KDB + +#include <machine/db_machdep.h> + +#include <ddb/db_lex.h> +#include <ddb/db_variables.h> +#include <ddb/db_task_thread.h> + +extern unsigned long db_maxoff; + +extern db_expr_t db_radix; +extern db_expr_t db_max_width; +extern db_expr_t db_tab_stop_width; +extern db_expr_t db_max_line; +extern int db_set_default_thread(); +extern int db_get_task_thread(); +extern int db_arg_variable(); + +#define DB_NWORK 32 /* number of work variable */ + +db_expr_t db_work[DB_NWORK]; /* work variable */ + +struct db_variable db_vars[] = { + { "radix", &db_radix, FCN_NULL }, + { "maxoff", (db_expr_t*)&db_maxoff, FCN_NULL }, + { "maxwidth", &db_max_width, FCN_NULL }, + { "tabstops", &db_tab_stop_width, FCN_NULL }, + { "lines", &db_max_line, FCN_NULL }, + { "thread", 0, db_set_default_thread }, + { "task", 0, db_get_task_thread, + 1, 2, -1, -1 }, + { "work", &db_work[0], FCN_NULL, + 1, 1, 0, DB_NWORK-1 }, + { "arg", 0, db_arg_variable, + 1, 1, -1, -1 }, +}; +struct db_variable *db_evars = db_vars + sizeof(db_vars)/sizeof(db_vars[0]); + +char * +db_get_suffix(suffix, suffix_value) + register char *suffix; + short *suffix_value; +{ + register value; + + for (value = 0; *suffix && *suffix != '.' && *suffix != ':'; suffix++) { + if (*suffix < '0' || *suffix > '9') + return(0); + value = value*10 + *suffix - '0'; + } + *suffix_value = value; + if (*suffix == '.') + suffix++; + return(suffix); +} + +static boolean_t +db_cmp_variable_name(vp, name, ap) + struct db_variable *vp; + char *name; + register db_var_aux_param_t ap; +{ + register char *var_np, *np; + register level; + + for (np = name, var_np = vp->name; *var_np; ) { + if (*np++ != *var_np++) + return(FALSE); + } + for (level = 0; *np && *np != ':' && level < vp->max_level; level++){ + if ((np = db_get_suffix(np, &ap->suffix[level])) == 0) + return(FALSE); + } + if ((*np && *np != ':') || level < vp->min_level + || (level > 0 && (ap->suffix[0] < vp->low + || (vp->high >= 0 && ap->suffix[0] > vp->high)))) + return(FALSE); + db_strcpy(ap->modif, (*np)? np+1: ""); + ap->thread = (db_option(ap->modif, 't')?db_default_thread: THREAD_NULL); + ap->level = level; + return(TRUE); +} + +int +db_find_variable(varp, ap) + struct db_variable **varp; + db_var_aux_param_t ap; +{ + int t; + struct db_variable *vp; + + t = db_read_token(); + if (t == tIDENT) { + for (vp = db_vars; vp < db_evars; vp++) { + if (db_cmp_variable_name(vp, db_tok_string, ap)) { + *varp = vp; + return (1); + } + } + for (vp = db_regs; vp < db_eregs; vp++) { + if (db_cmp_variable_name(vp, db_tok_string, ap)) { + *varp = vp; + return (1); + } + } + } + db_printf("Unknown variable \"$%s\"\n", db_tok_string); + db_error(0); + return (0); +} + + +void db_read_write_variable(); /* forward */ + +int +db_get_variable(valuep) + db_expr_t *valuep; +{ + struct db_variable *vp; + struct db_var_aux_param aux_param; + char modif[TOK_STRING_SIZE]; + + aux_param.modif = modif; + if (!db_find_variable(&vp, &aux_param)) + return (0); + + db_read_write_variable(vp, valuep, DB_VAR_GET, &aux_param); + + return (1); +} + +int +db_set_variable(value) + db_expr_t value; +{ + struct db_variable *vp; + struct db_var_aux_param aux_param; + char modif[TOK_STRING_SIZE]; + + aux_param.modif = modif; + if (!db_find_variable(&vp, &aux_param)) + return (0); + + db_read_write_variable(vp, &value, DB_VAR_SET, &aux_param); + + return (1); +} + +void +db_read_write_variable(vp, valuep, rw_flag, ap) + struct db_variable *vp; + db_expr_t *valuep; + int rw_flag; + db_var_aux_param_t ap; +{ + int (*func)() = vp->fcn; + struct db_var_aux_param aux_param; + + if (ap == 0) { + ap = &aux_param; + ap->modif = ""; + ap->level = 0; + ap->thread = THREAD_NULL; + } + if (func == FCN_NULL) { + if (rw_flag == DB_VAR_SET) + vp->valuep[(ap->level)? (ap->suffix[0] - vp->low): 0] = *valuep; + else + *valuep = vp->valuep[(ap->level)? (ap->suffix[0] - vp->low): 0]; + } else + (*func)(vp, valuep, rw_flag, ap); +} + +void +db_set_cmd() +{ + db_expr_t value; + int t; + struct db_variable *vp; + struct db_var_aux_param aux_param; + char modif[TOK_STRING_SIZE]; + + aux_param.modif = modif; + t = db_read_token(); + if (t != tDOLLAR) { + db_error("Variable name should be prefixed with $\n"); + return; + } + if (!db_find_variable(&vp, &aux_param)) { + db_error("Unknown variable\n"); + return; + } + + t = db_read_token(); + if (t != tEQ) + db_unread_token(t); + + if (!db_expression(&value)) { + db_error("No value\n"); + return; + } + if ((t = db_read_token()) == tSEMI_COLON) + db_unread_token(t); + else if (t != tEOL) + db_error("?\n"); + + db_read_write_variable(vp, &value, DB_VAR_SET, &aux_param); +} + +#endif MACH_KDB diff --git a/ddb/db_variables.h b/ddb/db_variables.h new file mode 100644 index 0000000..8829aa9 --- /dev/null +++ b/ddb/db_variables.h @@ -0,0 +1,78 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#ifndef _DB_VARIABLES_H_ +#define _DB_VARIABLES_H_ + +#include <kern/thread.h> + +/* + * Debugger variables. + */ +struct db_variable { + char *name; /* Name of variable */ + db_expr_t *valuep; /* pointer to value of variable */ + /* function to call when reading/writing */ + int (*fcn)(/* db_variable, db_expr_t, int, db_var_aux_param_t */); + short min_level; /* number of minimum suffix levels */ + short max_level; /* number of maximum suffix levels */ + short low; /* low value of level 1 suffix */ + short high; /* high value of level 1 suffix */ +#define DB_VAR_GET 0 +#define DB_VAR_SET 1 +}; +#define FCN_NULL ((int (*)())0) + +#define DB_VAR_LEVEL 3 /* maximum number of suffix level */ + +#define db_read_variable(vp, valuep) \ + db_read_write_variable(vp, valuep, DB_VAR_GET, 0) +#define db_write_variable(vp, valuep) \ + db_read_write_variable(vp, valuep, DB_VAR_SET, 0) + +/* + * auxiliary parameters passed to a variable handler + */ +struct db_var_aux_param { + char *modif; /* option strings */ + short level; /* number of levels */ + short suffix[DB_VAR_LEVEL]; /* suffix */ + thread_t thread; /* target task */ +}; + +typedef struct db_var_aux_param *db_var_aux_param_t; + + +extern struct db_variable db_vars[]; /* debugger variables */ +extern struct db_variable *db_evars; +extern struct db_variable db_regs[]; /* machine registers */ +extern struct db_variable *db_eregs; + +#endif /* _DB_VARIABLES_H_ */ diff --git a/ddb/db_watch.c b/ddb/db_watch.c new file mode 100644 index 0000000..410c0a2 --- /dev/null +++ b/ddb/db_watch.c @@ -0,0 +1,318 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Author: Richard P. Draves, Carnegie Mellon University + * Date: 10/90 + */ + +#include "mach_kdb.h" +#if MACH_KDB + +#include <mach/boolean.h> +#include <mach/vm_param.h> +#include <mach/machine/vm_types.h> +#include <mach/machine/vm_param.h> +#include <vm/vm_map.h> + +#include <machine/db_machdep.h> +#include <ddb/db_lex.h> +#include <ddb/db_watch.h> +#include <ddb/db_access.h> +#include <ddb/db_sym.h> +#include <ddb/db_task_thread.h> + + + +/* + * Watchpoints. + */ + +boolean_t db_watchpoints_inserted = TRUE; + +#define NWATCHPOINTS 100 +struct db_watchpoint db_watch_table[NWATCHPOINTS]; +db_watchpoint_t db_next_free_watchpoint = &db_watch_table[0]; +db_watchpoint_t db_free_watchpoints = 0; +db_watchpoint_t db_watchpoint_list = 0; + +extern vm_map_t kernel_map; + +db_watchpoint_t +db_watchpoint_alloc() +{ + register db_watchpoint_t watch; + + if ((watch = db_free_watchpoints) != 0) { + db_free_watchpoints = watch->link; + return (watch); + } + if (db_next_free_watchpoint == &db_watch_table[NWATCHPOINTS]) { + db_printf("All watchpoints used.\n"); + return (0); + } + watch = db_next_free_watchpoint; + db_next_free_watchpoint++; + + return (watch); +} + +void +db_watchpoint_free(watch) + register db_watchpoint_t watch; +{ + watch->link = db_free_watchpoints; + db_free_watchpoints = watch; +} + +void +db_set_watchpoint(task, addr, size) + task_t task; + db_addr_t addr; + vm_size_t size; +{ + register db_watchpoint_t watch; + + /* + * Should we do anything fancy with overlapping regions? + */ + + for (watch = db_watchpoint_list; watch != 0; watch = watch->link) { + if (watch->task == task && + (watch->loaddr == addr) && + (watch->hiaddr == addr+size)) { + db_printf("Already set.\n"); + return; + } + } + + watch = db_watchpoint_alloc(); + if (watch == 0) { + db_printf("Too many watchpoints.\n"); + return; + } + + watch->task = task; + watch->loaddr = addr; + watch->hiaddr = addr+size; + + watch->link = db_watchpoint_list; + db_watchpoint_list = watch; + + db_watchpoints_inserted = FALSE; +} + +void +db_delete_watchpoint(task, addr) + task_t task; + db_addr_t addr; +{ + register db_watchpoint_t watch; + register db_watchpoint_t *prev; + + for (prev = &db_watchpoint_list; (watch = *prev) != 0; + prev = &watch->link) { + if (watch->task == task && + (watch->loaddr <= addr) && + (addr < watch->hiaddr)) { + *prev = watch->link; + db_watchpoint_free(watch); + return; + } + } + + db_printf("Not set.\n"); +} + +void +db_list_watchpoints() +{ + register db_watchpoint_t watch; + int task_id; + + if (db_watchpoint_list == 0) { + db_printf("No watchpoints set\n"); + return; + } + + db_printf("Space Address Size\n"); + for (watch = db_watchpoint_list; watch != 0; watch = watch->link) { + if (watch->task == TASK_NULL) + db_printf("kernel "); + else { + task_id = db_lookup_task(watch->task); + if (task_id < 0) + db_printf("%*X", 2*sizeof(vm_offset_t), watch->task); + else + db_printf("task%-3d ", task_id); + } + db_printf(" %*X %X\n", 2*sizeof(vm_offset_t), watch->loaddr, + watch->hiaddr - watch->loaddr); + } +} + +static int +db_get_task(modif, taskp, addr) + char *modif; + task_t *taskp; + db_addr_t addr; +{ + task_t task = TASK_NULL; + db_expr_t value; + boolean_t user_space; + + user_space = db_option(modif, 'T'); + if (user_space) { + if (db_expression(&value)) { + task = (task_t)value; + if (db_lookup_task(task) < 0) { + db_printf("bad task address %X\n", task); + return(-1); + } + } else { + task = db_default_task; + if (task == TASK_NULL) { + if ((task = db_current_task()) == TASK_NULL) { + db_printf("no task\n"); + return(-1); + } + } + } + } + if (!DB_VALID_ADDRESS(addr, user_space)) { + db_printf("Address %#X is not in %s space\n", addr, + (user_space)? "user": "kernel"); + return(-1); + } + *taskp = task; + return(0); +} + +/* Delete watchpoint */ +/*ARGSUSED*/ +void +db_deletewatch_cmd(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char * modif; +{ + task_t task; + + if (db_get_task(modif, &task, addr) < 0) + return; + db_delete_watchpoint(task, addr); +} + +/* Set watchpoint */ +/*ARGSUSED*/ +void +db_watchpoint_cmd(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char * modif; +{ + vm_size_t size; + db_expr_t value; + task_t task; + boolean_t db_option(); + + if (db_get_task(modif, &task, addr) < 0) + return; + if (db_expression(&value)) + size = (vm_size_t) value; + else + size = sizeof(int); + db_set_watchpoint(task, addr, size); +} + +/* list watchpoints */ +void +db_listwatch_cmd() +{ + db_list_watchpoints(); +} + +void +db_set_watchpoints() +{ + register db_watchpoint_t watch; + vm_map_t map; + + if (!db_watchpoints_inserted) { + for (watch = db_watchpoint_list; watch != 0; watch = watch->link) { + map = (watch->task)? watch->task->map: kernel_map; + pmap_protect(map->pmap, + trunc_page(watch->loaddr), + round_page(watch->hiaddr), + VM_PROT_READ); + } + db_watchpoints_inserted = TRUE; + } +} + +void +db_clear_watchpoints() +{ + db_watchpoints_inserted = FALSE; +} + +boolean_t +db_find_watchpoint(map, addr, regs) + vm_map_t map; + db_addr_t addr; + db_regs_t *regs; +{ + register db_watchpoint_t watch; + db_watchpoint_t found = 0; + register task_t task_space; + + task_space = (map == kernel_map)? TASK_NULL: db_current_task(); + for (watch = db_watchpoint_list; watch != 0; watch = watch->link) { + if (watch->task == task_space) { + if ((watch->loaddr <= addr) && (addr < watch->hiaddr)) + return (TRUE); + else if ((trunc_page(watch->loaddr) <= addr) && + (addr < round_page(watch->hiaddr))) + found = watch; + } + } + + /* + * We didn't hit exactly on a watchpoint, but we are + * in a protected region. We want to single-step + * and then re-protect. + */ + + if (found) { + db_watchpoints_inserted = FALSE; + db_single_step(regs, task_space); + } + + return (FALSE); +} + +#endif MACH_KDB diff --git a/ddb/db_watch.h b/ddb/db_watch.h new file mode 100644 index 0000000..9192bbd --- /dev/null +++ b/ddb/db_watch.h @@ -0,0 +1,63 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 10/90 + */ + +#include "mach_kdb.h" +#if MACH_KDB + +#ifndef _DDB_DB_WATCH_ +#define _DDB_DB_WATCH_ + +#include <mach/machine/vm_types.h> +#include <kern/task.h> +#include <machine/db_machdep.h> + +/* + * Watchpoint. + */ + +typedef struct db_watchpoint { + task_t task; /* in this map */ + db_addr_t loaddr; /* from this address */ + db_addr_t hiaddr; /* to this address */ + struct db_watchpoint *link; /* link in in-use or free chain */ +} *db_watchpoint_t; + +extern boolean_t db_find_watchpoint(/* task_t task, db_addr_t addr, + db_regs_t *regs */); +extern void db_set_watchpoints(); +extern void db_clear_watchpoints(); + +extern void db_set_watchpoint(/* task_t task, db_addr_t addr, vm_size_t size */); +extern void db_delete_watchpoint(/* task_t task, db_addr_t addr */); +extern void db_list_watchpoints(); + +#endif _DDB_DB_WATCH_ + +#endif MACH_KDB diff --git a/ddb/db_write_cmd.c b/ddb/db_write_cmd.c new file mode 100644 index 0000000..a72102b --- /dev/null +++ b/ddb/db_write_cmd.c @@ -0,0 +1,109 @@ +/* + * Mach Operating System + * Copyright (c) 1992,1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 7/90 + */ + +#include "mach_kdb.h" +#if MACH_KDB + +#include <mach/boolean.h> +#include <kern/task.h> +#include <kern/thread.h> + +#include <machine/db_machdep.h> + +#include <ddb/db_lex.h> +#include <ddb/db_access.h> +#include <ddb/db_command.h> +#include <ddb/db_sym.h> +#include <ddb/db_task_thread.h> + + + +/* + * Write to file. + */ +/*ARGSUSED*/ +void +db_write_cmd(address, have_addr, count, modif) + db_expr_t address; + boolean_t have_addr; + db_expr_t count; + char * modif; +{ + register db_addr_t addr; + register db_expr_t old_value; + db_expr_t new_value; + register int size; + boolean_t wrote_one = FALSE; + boolean_t t_opt, u_opt; + thread_t thread; + task_t task; + + addr = (db_addr_t) address; + + size = db_size_option(modif, &u_opt, &t_opt); + if (t_opt) + { + if (!db_get_next_thread(&thread, 0)) + return; + task = thread->task; + } + else + task = db_current_task(); + + /* if user space is not explicitly specified, + look in the kernel */ + if (!u_opt) + task = TASK_NULL; + + if (!DB_VALID_ADDRESS(addr, u_opt)) { + db_printf("Bad address %#*X\n", 2*sizeof(vm_offset_t), addr); + return; + } + + while (db_expression(&new_value)) { + old_value = db_get_task_value(addr, size, FALSE, task); + db_task_printsym(addr, DB_STGY_ANY, task); + db_printf("\t\t%#*N\t=\t%#*N\n", + 2*sizeof(db_expr_t), old_value, + 2*sizeof(db_expr_t), new_value); + db_put_task_value(addr, size, new_value, task); + addr += size; + + wrote_one = TRUE; + } + + if (!wrote_one) + db_error("Nothing written.\n"); + + db_next = addr; + db_prev = addr - size; +} + +#endif MACH_KDB diff --git a/ddb/nlist.h b/ddb/nlist.h new file mode 100644 index 0000000..b948dfd --- /dev/null +++ b/ddb/nlist.h @@ -0,0 +1,63 @@ +/* + * Mach Operating System + * Copyright (c) 1991 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * nlist.h - symbol table entry structure for an a.out file + * derived from FSF's a.out.gnu.h + * + */ + +#ifndef _DDB_NLIST_H_ +#define _DDB_NLIST_H_ + +struct nlist { + union n_un { + char *n_name; /* symbol name */ + long n_strx; /* index into file string table */ + } n_un; + unsigned char n_type; /* type flag, i.e. N_TEXT etc; see below */ + unsigned char n_other; /* machdep uses */ + short n_desc; /* see <stab.h> */ +#if alpha + int n_pad; /* alignment, used to carry framesize info */ +#endif + vm_offset_t n_value; /* value of this symbol (or sdb offset) */ +}; + +/* + * Simple values for n_type. + */ +#define N_UNDF 0 /* undefined */ +#define N_ABS 2 /* absolute */ +#define N_TEXT 4 /* text */ +#define N_DATA 6 /* data */ +#define N_BSS 8 /* bss */ +#define N_FN 0x1f /* file name symbol */ +#define N_EXT 1 /* external bit, or'ed in */ +#define N_TYPE 0x1e /* mask for all the type bits */ +#define N_STAB 0xe0 /* if any of these bits set, a SDB entry */ + + +#endif /* _DDB_NLIST_H_ */ diff --git a/ddb/stab.h b/ddb/stab.h new file mode 100644 index 0000000..3ebc1af --- /dev/null +++ b/ddb/stab.h @@ -0,0 +1,69 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)stab.h 5.2 (Berkeley) 4/4/91 + */ + + +/* + * The following are symbols used by various debuggers and by the Pascal + * compiler. Each of them must have one (or more) of the bits defined by + * the N_STAB mask set. + */ + +#define N_GSYM 0x20 /* global symbol */ +#define N_FNAME 0x22 /* F77 function name */ +#define N_FUN 0x24 /* procedure name */ +#define N_STSYM 0x26 /* data segment variable */ +#define N_LCSYM 0x28 /* bss segment variable */ +#define N_MAIN 0x2a /* main function name */ +#define N_PC 0x30 /* global Pascal symbol */ +#define N_FRAME 0x34 /* stack frame descriptor */ +#define N_RSYM 0x40 /* register variable */ +#define N_SLINE 0x44 /* text segment line number */ +#define N_DSLINE 0x46 /* data segment line number */ +#define N_BSLINE 0x48 /* bss segment line number */ +#define N_SSYM 0x60 /* structure/union element */ +#define N_SO 0x64 /* main source file name */ +#define N_LSYM 0x80 /* stack variable */ +#define N_BINCL 0x82 /* include file beginning */ +#define N_SOL 0x84 /* included source file name */ +#define N_PSYM 0xa0 /* parameter variable */ +#define N_EINCL 0xa2 /* include file end */ +#define N_ENTRY 0xa4 /* alternate entry point */ +#define N_LBRAC 0xc0 /* left bracket */ +#define N_EXCL 0xc2 /* deleted include file */ +#define N_RBRAC 0xe0 /* right bracket */ +#define N_BCOMM 0xe2 /* begin common */ +#define N_ECOMM 0xe4 /* end common */ +#define N_ECOML 0xe8 /* end common (local name) */ +#define N_LENG 0xfe /* length of preceding entry */ diff --git a/ddb/tr.h b/ddb/tr.h new file mode 100644 index 0000000..1d6145a --- /dev/null +++ b/ddb/tr.h @@ -0,0 +1,112 @@ +/* + * (c) Copyright 1992, 1993, 1994, 1995 OPEN SOFTWARE FOUNDATION, INC. + * ALL RIGHTS RESERVED + */ +/* + * OSF RI nmk19b2 5/2/95 + */ + +/* + * File: ddb/tr.h + * Author: Alan Langerman, Jeffrey Heller + * Date: 1992 + * + * Internal trace routines. Like old-style XPRs but + * less formatting. + */ + +#include <mach_assert.h> +#include <mach_tr.h> + +/* + * Originally, we only wanted tracing when + * MACH_TR and MACH_ASSERT were turned on + * together. Now, there's no reason why + * MACH_TR and MACH_ASSERT can't be completely + * orthogonal. + */ +#define TRACE_BUFFER (MACH_TR) + +/* + * Log events in a circular trace buffer for future debugging. + * Events are unsigned integers. Each event has a descriptive + * message. + * + * TR_DECL must be used at the beginning of a routine using + * one of the tr calls. The macro should be passed the name + * of the function surrounded by quotation marks, e.g., + * TR_DECL("netipc_recv_intr"); + * and should be terminated with a semi-colon. The TR_DECL + * must be the *last* declaration in the variable declaration + * list, or syntax errors will be introduced when TRACE_BUFFER + * is turned off. + */ +#ifndef _DDB_TR_H_ +#define _DDB_TR_H_ + +#if TRACE_BUFFER + +#include <machine/db_machdep.h> + +#define __ui__ (unsigned int) +#define TR_INIT() tr_init() +#define TR_SHOW(a,b,c) show_tr((a),(b),(c)) +#define TR_DECL(funcname) char *__ntr_func_name__ = funcname +#define tr1(msg) \ + tr(__ntr_func_name__, __FILE__, __LINE__, (msg), \ + 0,0,0,0) +#define tr2(msg,tag1) \ + tr(__ntr_func_name__, __FILE__, __LINE__, (msg), \ + __ui__(tag1),0,0,0) +#define tr3(msg,tag1,tag2) \ + tr(__ntr_func_name__, __FILE__, __LINE__, (msg), \ + __ui__(tag1),__ui__(tag2),0,0) +#define tr4(msg,tag1,tag2,tag3) \ + tr(__ntr_func_name__, __FILE__, __LINE__, (msg), \ + __ui__(tag1),__ui__(tag2),__ui__(tag3),0) +#define tr5(msg,tag1,tag2,tag3,tag4) \ + tr(__ntr_func_name__, __FILE__, __LINE__, (msg), \ + __ui__(tag1),__ui__(tag2),__ui__(tag3),__ui__(tag4)) + +/* + * Adjust tr log indentation based on function + * call graph; this method is quick-and-dirty + * and only works safely on a uniprocessor. + */ +extern int tr_indent; +#define tr_start() tr_indent++ +#define tr_stop() tr_indent-- + +extern void tr_init(void); +extern void tr( + char *funcname, + char *file, + unsigned int lineno, + char *fmt, + unsigned int tag1, + unsigned int tag2, + unsigned int tag3, + unsigned int tag4); + +extern void db_show_tr( + db_expr_t addr, + boolean_t have_addr, + db_expr_t count, + char * modif); + +#else /* TRACE_BUFFER */ + +#define TR_INIT() +#define TR_SHOW(a,b,c) +#define TR_DECL(funcname) +#define tr1(msg) +#define tr2(msg, tag1) +#define tr3(msg, tag1, tag2) +#define tr4(msg, tag1, tag2, tag3) +#define tr5(msg, tag1, tag2, tag3, tag4) +#define tr_start() +#define tr_stop() + +#endif /* TRACE_BUFFER */ + +#endif /* _DDB_TR_H_ */ |