summaryrefslogtreecommitdiff
path: root/ddb
diff options
context:
space:
mode:
authorThomas Bushnell <thomas@gnu.org>1997-02-25 21:28:37 +0000
committerThomas Bushnell <thomas@gnu.org>1997-02-25 21:28:37 +0000
commitf07a4c844da9f0ecae5bbee1ab94be56505f26f7 (patch)
tree12b07c7e578fc1a5f53dbfde2632408491ff2a70 /ddb
Initial source
Diffstat (limited to 'ddb')
-rw-r--r--ddb/db_access.c137
-rw-r--r--ddb/db_access.h73
-rw-r--r--ddb/db_aout.c507
-rw-r--r--ddb/db_break.c733
-rw-r--r--ddb/db_break.h86
-rw-r--r--ddb/db_command.c597
-rw-r--r--ddb/db_command.h70
-rw-r--r--ddb/db_cond.c181
-rw-r--r--ddb/db_examine.c506
-rw-r--r--ddb/db_expr.c391
-rw-r--r--ddb/db_expr.h26
-rw-r--r--ddb/db_ext_symtab.c123
-rw-r--r--ddb/db_input.c378
-rw-r--r--ddb/db_lex.c455
-rw-r--r--ddb/db_lex.h93
-rw-r--r--ddb/db_macro.c183
-rw-r--r--ddb/db_mp.c339
-rw-r--r--ddb/db_output.c240
-rw-r--r--ddb/db_output.h44
-rw-r--r--ddb/db_print.c511
-rw-r--r--ddb/db_print.h110
-rw-r--r--ddb/db_run.c441
-rw-r--r--ddb/db_run.h37
-rw-r--r--ddb/db_sym.c523
-rw-r--r--ddb/db_sym.h200
-rw-r--r--ddb/db_task_thread.c303
-rw-r--r--ddb/db_task_thread.h51
-rw-r--r--ddb/db_trap.c107
-rw-r--r--ddb/db_variables.c241
-rw-r--r--ddb/db_variables.h78
-rw-r--r--ddb/db_watch.c318
-rw-r--r--ddb/db_watch.h63
-rw-r--r--ddb/db_write_cmd.c109
-rw-r--r--ddb/nlist.h63
-rw-r--r--ddb/stab.h69
-rw-r--r--ddb/tr.h112
36 files changed, 8498 insertions, 0 deletions
diff --git a/ddb/db_access.c b/ddb/db_access.c
new file mode 100644
index 00000000..7a391446
--- /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 00000000..c01b6ce9
--- /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 00000000..6cb8294c
--- /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 00000000..d0ce1fc2
--- /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 00000000..4d4bd2cf
--- /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 00000000..50a02bd4
--- /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 00000000..60762b21
--- /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 00000000..c78f03c1
--- /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 00000000..c996fd18
--- /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 00000000..b9848bb6
--- /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 00000000..989b66be
--- /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 00000000..1a8ea8b6
--- /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 00000000..c175ae15
--- /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 00000000..617f1237
--- /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 00000000..c73b93e2
--- /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 00000000..c186ac6a
--- /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 00000000..607c24db
--- /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 00000000..d7e416d2
--- /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 00000000..a3f0de05
--- /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 00000000..727af233
--- /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 00000000..634c5be4
--- /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 00000000..47c39c28
--- /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 00000000..fcc8ece5
--- /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 00000000..c48a48fd
--- /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 00000000..dd320b7c
--- /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 00000000..8ab8cde0
--- /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 00000000..26a071cf
--- /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 00000000..dbb58920
--- /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 00000000..541dca1c
--- /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 00000000..8829aa99
--- /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 00000000..410c0a25
--- /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 00000000..9192bbd4
--- /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 00000000..a72102b2
--- /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 00000000..b948dfd3
--- /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 00000000..3ebc1af8
--- /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 00000000..1d6145ad
--- /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_ */