summaryrefslogtreecommitdiffstats
path: root/sys/ddb
diff options
context:
space:
mode:
Diffstat (limited to 'sys/ddb')
-rw-r--r--sys/ddb/db_access.c113
-rw-r--r--sys/ddb/db_access.h42
-rw-r--r--sys/ddb/db_break.c367
-rw-r--r--sys/ddb/db_break.h67
-rw-r--r--sys/ddb/db_capture.c361
-rw-r--r--sys/ddb/db_command.c872
-rw-r--r--sys/ddb/db_command.h57
-rw-r--r--sys/ddb/db_examine.c341
-rw-r--r--sys/ddb/db_expr.c228
-rw-r--r--sys/ddb/db_input.c374
-rw-r--r--sys/ddb/db_lex.c314
-rw-r--r--sys/ddb/db_lex.h73
-rw-r--r--sys/ddb/db_main.c262
-rw-r--r--sys/ddb/db_output.c395
-rw-r--r--sys/ddb/db_output.h47
-rw-r--r--sys/ddb/db_print.c70
-rw-r--r--sys/ddb/db_ps.c468
-rw-r--r--sys/ddb/db_run.c392
-rw-r--r--sys/ddb/db_script.c562
-rw-r--r--sys/ddb/db_sym.c505
-rw-r--r--sys/ddb/db_sym.h106
-rw-r--r--sys/ddb/db_textdump.c550
-rw-r--r--sys/ddb/db_thread.c172
-rw-r--r--sys/ddb/db_variables.c159
-rw-r--r--sys/ddb/db_variables.h63
-rw-r--r--sys/ddb/db_watch.c331
-rw-r--r--sys/ddb/db_watch.h48
-rw-r--r--sys/ddb/db_write_cmd.c96
-rw-r--r--sys/ddb/ddb.h287
29 files changed, 7722 insertions, 0 deletions
diff --git a/sys/ddb/db_access.c b/sys/ddb/db_access.c
new file mode 100644
index 0000000..2f4d049
--- /dev/null
+++ b/sys/ddb/db_access.c
@@ -0,0 +1,113 @@
+/*-
+ * 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
+ * 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 the
+ * rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kdb.h>
+
+#include <ddb/ddb.h>
+#include <ddb/db_access.h>
+
+/*
+ * Access unaligned data items on aligned (longword)
+ * boundaries.
+ */
+
+static unsigned db_extend[] = { /* table for sign-extending */
+ 0,
+ 0xFFFFFF80U,
+ 0xFFFF8000U,
+ 0xFF800000U
+};
+
+#ifndef BYTE_MSF
+#define BYTE_MSF 0
+#endif
+
+db_expr_t
+db_get_value(addr, size, is_signed)
+ db_addr_t addr;
+ register int size;
+ boolean_t is_signed;
+{
+ char data[sizeof(u_int64_t)];
+ register db_expr_t value;
+ register int i;
+
+ if (db_read_bytes(addr, size, data) != 0) {
+ db_printf("*** error reading from address %llx ***\n",
+ (long long)addr);
+ kdb_reenter();
+ }
+
+ 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 < 4) {
+ if (is_signed && (value & db_extend[size]) != 0)
+ value |= db_extend[size];
+ }
+ return (value);
+}
+
+void
+db_put_value(addr, size, value)
+ db_addr_t addr;
+ register int size;
+ register db_expr_t value;
+{
+ char data[sizeof(int)];
+ 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;
+ }
+
+ if (db_write_bytes(addr, size, data) != 0) {
+ db_printf("*** error writing to address %llx ***\n",
+ (long long)addr);
+ kdb_reenter();
+ }
+}
diff --git a/sys/ddb/db_access.h b/sys/ddb/db_access.h
new file mode 100644
index 0000000..44915e3
--- /dev/null
+++ b/sys/ddb/db_access.h
@@ -0,0 +1,42 @@
+/*-
+ * 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
+ * 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 the
+ * rights to redistribute these changes.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _DDB_DB_ACCESS_H_
+#define _DDB_DB_ACCESS_H_
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+/*
+ * Data access functions for debugger.
+ */
+db_expr_t db_get_value(db_addr_t addr, int size, boolean_t is_signed);
+void db_put_value(db_addr_t addr, int size, db_expr_t value);
+
+#endif /* !_DDB_DB_ACCESS_H_ */
diff --git a/sys/ddb/db_break.c b/sys/ddb/db_break.c
new file mode 100644
index 0000000..02833ec
--- /dev/null
+++ b/sys/ddb/db_break.c
@@ -0,0 +1,367 @@
+/*-
+ * 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
+ * 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 the
+ * rights to redistribute these changes.
+ *
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+/*
+ * Breakpoints.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <vm/vm.h>
+#include <vm/vm_kern.h>
+
+#include <ddb/ddb.h>
+#include <ddb/db_break.h>
+#include <ddb/db_access.h>
+#include <ddb/db_sym.h>
+
+#define NBREAKPOINTS 100
+static struct db_breakpoint db_break_table[NBREAKPOINTS];
+static db_breakpoint_t db_next_free_breakpoint = &db_break_table[0];
+static db_breakpoint_t db_free_breakpoints = 0;
+static db_breakpoint_t db_breakpoint_list = 0;
+
+static db_breakpoint_t db_breakpoint_alloc(void);
+static void db_breakpoint_free(db_breakpoint_t bkpt);
+static void db_delete_breakpoint(vm_map_t map, db_addr_t addr);
+static db_breakpoint_t db_find_breakpoint(vm_map_t map, db_addr_t addr);
+static void db_list_breakpoints(void);
+static void db_set_breakpoint(vm_map_t map, db_addr_t addr, int count);
+
+static 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);
+}
+
+static void
+db_breakpoint_free(bkpt)
+ register db_breakpoint_t bkpt;
+{
+ bkpt->link = db_free_breakpoints;
+ db_free_breakpoints = bkpt;
+}
+
+static void
+db_set_breakpoint(map, addr, count)
+ vm_map_t map;
+ db_addr_t addr;
+ int count;
+{
+ register db_breakpoint_t bkpt;
+
+ if (db_find_breakpoint(map, addr)) {
+ db_printf("Already set.\n");
+ return;
+ }
+
+ bkpt = db_breakpoint_alloc();
+ if (bkpt == 0) {
+ db_printf("Too many breakpoints.\n");
+ return;
+ }
+
+ bkpt->map = map;
+ bkpt->address = addr;
+ bkpt->flags = 0;
+ bkpt->init_count = count;
+ bkpt->count = count;
+
+ bkpt->link = db_breakpoint_list;
+ db_breakpoint_list = bkpt;
+}
+
+static void
+db_delete_breakpoint(map, addr)
+ vm_map_t map;
+ db_addr_t addr;
+{
+ register db_breakpoint_t bkpt;
+ register db_breakpoint_t *prev;
+
+ for (prev = &db_breakpoint_list;
+ (bkpt = *prev) != 0;
+ prev = &bkpt->link) {
+ if (db_map_equal(bkpt->map, map) &&
+ (bkpt->address == addr)) {
+ *prev = bkpt->link;
+ break;
+ }
+ }
+ if (bkpt == 0) {
+ db_printf("Not set.\n");
+ return;
+ }
+
+ db_breakpoint_free(bkpt);
+}
+
+static db_breakpoint_t
+db_find_breakpoint(map, addr)
+ vm_map_t map;
+ db_addr_t addr;
+{
+ register db_breakpoint_t bkpt;
+
+ for (bkpt = db_breakpoint_list;
+ bkpt != 0;
+ bkpt = bkpt->link)
+ {
+ if (db_map_equal(bkpt->map, map) &&
+ (bkpt->address == addr))
+ return (bkpt);
+ }
+ return (0);
+}
+
+db_breakpoint_t
+db_find_breakpoint_here(addr)
+ db_addr_t addr;
+{
+ return db_find_breakpoint(db_map_addr(addr), addr);
+}
+
+static boolean_t db_breakpoints_inserted = TRUE;
+
+#ifndef BKPT_WRITE
+#define BKPT_WRITE(addr, storage) \
+do { \
+ *storage = db_get_value(addr, BKPT_SIZE, FALSE); \
+ db_put_value(addr, BKPT_SIZE, BKPT_SET(*storage)); \
+} while (0)
+#endif
+
+#ifndef BKPT_CLEAR
+#define BKPT_CLEAR(addr, storage) \
+ db_put_value(addr, BKPT_SIZE, *storage)
+#endif
+
+void
+db_set_breakpoints()
+{
+ register db_breakpoint_t bkpt;
+
+ if (!db_breakpoints_inserted) {
+
+ for (bkpt = db_breakpoint_list;
+ bkpt != 0;
+ bkpt = bkpt->link)
+ if (db_map_current(bkpt->map)) {
+ BKPT_WRITE(bkpt->address, &bkpt->bkpt_inst);
+ }
+ db_breakpoints_inserted = TRUE;
+ }
+}
+
+void
+db_clear_breakpoints()
+{
+ register db_breakpoint_t bkpt;
+
+ if (db_breakpoints_inserted) {
+
+ for (bkpt = db_breakpoint_list;
+ bkpt != 0;
+ bkpt = bkpt->link)
+ if (db_map_current(bkpt->map)) {
+ BKPT_CLEAR(bkpt->address, &bkpt->bkpt_inst);
+ }
+ db_breakpoints_inserted = FALSE;
+ }
+}
+
+#ifdef SOFTWARE_SSTEP
+/*
+ * 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(addr)
+ 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->map = NULL;
+ bkpt->address = addr;
+ bkpt->flags = BKPT_TEMP;
+ bkpt->init_count = 1;
+ bkpt->count = 1;
+
+ BKPT_WRITE(bkpt->address, &bkpt->bkpt_inst);
+ return bkpt;
+}
+
+void
+db_delete_temp_breakpoint(bkpt)
+ db_breakpoint_t bkpt;
+{
+ BKPT_CLEAR(bkpt->address, &bkpt->bkpt_inst);
+ db_breakpoint_free(bkpt);
+}
+#endif /* SOFTWARE_SSTEP */
+
+/*
+ * List breakpoints.
+ */
+static void
+db_list_breakpoints()
+{
+ register db_breakpoint_t bkpt;
+
+ if (db_breakpoint_list == 0) {
+ db_printf("No breakpoints set\n");
+ return;
+ }
+
+ db_printf(" Map Count Address\n");
+ for (bkpt = db_breakpoint_list;
+ bkpt != 0;
+ bkpt = bkpt->link) {
+ db_printf("%s%8p %5d ",
+ db_map_current(bkpt->map) ? "*" : " ",
+ (void *)bkpt->map, bkpt->init_count);
+ db_printsym(bkpt->address, DB_STGY_PROC);
+ db_printf("\n");
+ }
+}
+
+/* Delete breakpoint */
+/*ARGSUSED*/
+void
+db_delete_cmd(addr, have_addr, count, modif)
+ db_expr_t addr;
+ boolean_t have_addr;
+ db_expr_t count;
+ char * modif;
+{
+ db_delete_breakpoint(db_map_addr(addr), (db_addr_t)addr);
+}
+
+/* Set breakpoint with skip count */
+/*ARGSUSED*/
+void
+db_breakpoint_cmd(addr, have_addr, count, modif)
+ db_expr_t addr;
+ boolean_t have_addr;
+ db_expr_t count;
+ char * modif;
+{
+ if (count == -1)
+ count = 1;
+
+ db_set_breakpoint(db_map_addr(addr), (db_addr_t)addr, count);
+}
+
+/* list breakpoints */
+void
+db_listbreak_cmd(dummy1, dummy2, dummy3, dummy4)
+ db_expr_t dummy1;
+ boolean_t dummy2;
+ db_expr_t dummy3;
+ char * dummy4;
+{
+ db_list_breakpoints();
+}
+
+/*
+ * We want ddb to be usable before most of the kernel has been
+ * initialized. In particular, current_thread() or kernel_map
+ * (or both) may be null.
+ */
+
+boolean_t
+db_map_equal(map1, map2)
+ vm_map_t map1, map2;
+{
+ return ((map1 == map2) ||
+ ((map1 == NULL) && (map2 == kernel_map)) ||
+ ((map1 == kernel_map) && (map2 == NULL)));
+}
+
+boolean_t
+db_map_current(map)
+ vm_map_t map;
+{
+#if 0
+ thread_t thread;
+
+ return ((map == NULL) ||
+ (map == kernel_map) ||
+ (((thread = current_thread()) != NULL) &&
+ (map == thread->task->map)));
+#else
+ return (1);
+#endif
+}
+
+vm_map_t
+db_map_addr(addr)
+ vm_offset_t addr;
+{
+#if 0
+ thread_t thread;
+
+ /*
+ * We want to return kernel_map for all
+ * non-user addresses, even when debugging
+ * kernel tasks with their own maps.
+ */
+
+ if ((VM_MIN_ADDRESS <= addr) &&
+ (addr < VM_MAX_ADDRESS) &&
+ ((thread = current_thread()) != NULL))
+ return thread->task->map;
+ else
+#endif
+ return kernel_map;
+}
diff --git a/sys/ddb/db_break.h b/sys/ddb/db_break.h
new file mode 100644
index 0000000..f30b933
--- /dev/null
+++ b/sys/ddb/db_break.h
@@ -0,0 +1,67 @@
+/*-
+ * 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
+ * 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 the
+ * rights to redistribute these changes.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+#ifndef _DDB_DB_BREAK_H_
+#define _DDB_DB_BREAK_H_
+
+/*
+ * Breakpoint.
+ */
+
+#ifndef BKPT_INST_TYPE
+#define BKPT_INST_TYPE int
+#endif
+
+struct db_breakpoint {
+ vm_map_t map; /* in this map */
+ db_addr_t address; /* set here */
+ int init_count; /* number of times to skip bkpt */
+ int count; /* current count */
+ int flags; /* flags: */
+#define BKPT_SINGLE_STEP 0x2 /* to simulate single step */
+#define BKPT_TEMP 0x4 /* temporary */
+ BKPT_INST_TYPE bkpt_inst; /* saved instruction at bkpt */
+ struct db_breakpoint *link; /* link in in-use or free chain */
+};
+typedef struct db_breakpoint *db_breakpoint_t;
+
+void db_clear_breakpoints(void);
+#ifdef SOFTWARE_SSTEP
+void db_delete_temp_breakpoint(db_breakpoint_t);
+#endif
+db_breakpoint_t db_find_breakpoint_here(db_addr_t addr);
+void db_set_breakpoints(void);
+#ifdef SOFTWARE_SSTEP
+db_breakpoint_t db_set_temp_breakpoint(db_addr_t);
+#endif
+
+#endif /* !_DDB_DB_BREAK_H_ */
diff --git a/sys/ddb/db_capture.c b/sys/ddb/db_capture.c
new file mode 100644
index 0000000..50ae2cf
--- /dev/null
+++ b/sys/ddb/db_capture.c
@@ -0,0 +1,361 @@
+/*-
+ * Copyright (c) 2007 Robert N. M. Watson
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+/*
+ * DDB capture support: capture kernel debugger output into a fixed-size
+ * buffer for later dumping to disk or extraction from user space.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_ddb.h"
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/kerneldump.h>
+#include <sys/malloc.h>
+#include <sys/msgbuf.h>
+#include <sys/priv.h>
+#include <sys/sx.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+
+#include <ddb/ddb.h>
+#include <ddb/db_lex.h>
+
+/*
+ * While it would be desirable to use a small block-sized buffer and dump
+ * incrementally to disk in fixed-size blocks, it's not possible to enter
+ * kernel dumper routines without restarting the kernel, which is undesirable
+ * in the midst of debugging. Instead, we maintain a large static global
+ * buffer that we fill from DDB's output routines.
+ *
+ * We enforce an invariant at runtime that buffer sizes are even multiples of
+ * the textdump block size, which is a design choice that we might want to
+ * reconsider.
+ */
+static MALLOC_DEFINE(M_DDB_CAPTURE, "ddb_capture", "DDB capture buffer");
+
+#ifndef DDB_CAPTURE_DEFAULTBUFSIZE
+#define DDB_CAPTURE_DEFAULTBUFSIZE 48*1024
+#endif
+#ifndef DDB_CAPTURE_MAXBUFSIZE
+#define DDB_CAPTURE_MAXBUFSIZE 5*1024*1024
+#endif
+#define DDB_CAPTURE_FILENAME "ddb.txt" /* Captured DDB output. */
+
+static char *db_capture_buf;
+static u_int db_capture_bufsize = DDB_CAPTURE_DEFAULTBUFSIZE;
+static u_int db_capture_maxbufsize = DDB_CAPTURE_MAXBUFSIZE; /* Read-only. */
+static u_int db_capture_bufoff; /* Next location to write in buffer. */
+static u_int db_capture_bufpadding; /* Amount of zero padding. */
+static int db_capture_inpager; /* Suspend capture in pager. */
+static int db_capture_inprogress; /* DDB capture currently in progress. */
+
+struct sx db_capture_sx; /* Lock against user thread races. */
+SX_SYSINIT(db_capture_sx, &db_capture_sx, "db_capture_sx");
+
+static SYSCTL_NODE(_debug_ddb, OID_AUTO, capture, CTLFLAG_RW, 0,
+ "DDB capture options");
+
+SYSCTL_UINT(_debug_ddb_capture, OID_AUTO, bufoff, CTLFLAG_RD,
+ &db_capture_bufoff, 0, "Bytes of data in DDB capture buffer");
+
+SYSCTL_UINT(_debug_ddb_capture, OID_AUTO, maxbufsize, CTLFLAG_RD,
+ &db_capture_maxbufsize, 0,
+ "Maximum value for debug.ddb.capture.bufsize");
+
+SYSCTL_INT(_debug_ddb_capture, OID_AUTO, inprogress, CTLFLAG_RD,
+ &db_capture_inprogress, 0, "DDB output capture in progress");
+
+/*
+ * Boot-time allocation of the DDB capture buffer, if any. Force all buffer
+ * sizes, including the maximum size, to be rounded to block sizes.
+ */
+static void
+db_capture_sysinit(__unused void *dummy)
+{
+
+ TUNABLE_INT_FETCH("debug.ddb.capture.bufsize", &db_capture_bufsize);
+ db_capture_maxbufsize = roundup(db_capture_maxbufsize,
+ TEXTDUMP_BLOCKSIZE);
+ db_capture_bufsize = roundup(db_capture_bufsize, TEXTDUMP_BLOCKSIZE);
+ if (db_capture_bufsize > db_capture_maxbufsize)
+ db_capture_bufsize = db_capture_maxbufsize;
+ if (db_capture_bufsize != 0)
+ db_capture_buf = malloc(db_capture_bufsize, M_DDB_CAPTURE,
+ M_WAITOK);
+}
+SYSINIT(db_capture, SI_SUB_DDB_SERVICES, SI_ORDER_ANY, db_capture_sysinit,
+ NULL);
+
+/*
+ * Run-time adjustment of the capture buffer.
+ */
+static int
+sysctl_debug_ddb_capture_bufsize(SYSCTL_HANDLER_ARGS)
+{
+ u_int len, size;
+ char *buf;
+ int error;
+
+ size = db_capture_bufsize;
+ error = sysctl_handle_int(oidp, &size, 0, req);
+ if (error || req->newptr == NULL)
+ return (error);
+ size = roundup(size, TEXTDUMP_BLOCKSIZE);
+ if (size > db_capture_maxbufsize)
+ return (EINVAL);
+ sx_xlock(&db_capture_sx);
+ if (size != 0) {
+ /*
+ * Potentially the buffer is quite large, so if we can't
+ * allocate it, fail rather than waiting.
+ */
+ buf = malloc(size, M_DDB_CAPTURE, M_NOWAIT);
+ if (buf == NULL) {
+ sx_xunlock(&db_capture_sx);
+ return (ENOMEM);
+ }
+ len = min(db_capture_bufoff, size);
+ } else {
+ buf = NULL;
+ len = 0;
+ }
+ if (db_capture_buf != NULL && buf != NULL)
+ bcopy(db_capture_buf, buf, len);
+ if (db_capture_buf != NULL)
+ free(db_capture_buf, M_DDB_CAPTURE);
+ db_capture_bufoff = len;
+ db_capture_buf = buf;
+ db_capture_bufsize = size;
+ sx_xunlock(&db_capture_sx);
+
+ KASSERT(db_capture_bufoff <= db_capture_bufsize,
+ ("sysctl_debug_ddb_capture_bufsize: bufoff > bufsize"));
+ KASSERT(db_capture_bufsize <= db_capture_maxbufsize,
+ ("sysctl_debug_ddb_capture_maxbufsize: bufsize > maxbufsize"));
+
+ return (0);
+}
+SYSCTL_PROC(_debug_ddb_capture, OID_AUTO, bufsize, CTLTYPE_UINT|CTLFLAG_RW,
+ 0, 0, sysctl_debug_ddb_capture_bufsize, "IU",
+ "Size of DDB capture buffer");
+
+/*
+ * Sysctl to read out the capture buffer from userspace. We require
+ * privilege as sensitive process/memory information may be accessed.
+ */
+static int
+sysctl_debug_ddb_capture_data(SYSCTL_HANDLER_ARGS)
+{
+ int error;
+ char ch;
+
+ error = priv_check(req->td, PRIV_DDB_CAPTURE);
+ if (error)
+ return (error);
+
+ sx_slock(&db_capture_sx);
+ error = SYSCTL_OUT(req, db_capture_buf, db_capture_bufoff);
+ sx_sunlock(&db_capture_sx);
+ if (error)
+ return (error);
+ ch = '\0';
+ return (SYSCTL_OUT(req, &ch, sizeof(ch)));
+}
+SYSCTL_PROC(_debug_ddb_capture, OID_AUTO, data, CTLTYPE_STRING | CTLFLAG_RD,
+ NULL, 0, sysctl_debug_ddb_capture_data, "A", "DDB capture data");
+
+/*
+ * Routines for capturing DDB output into a fixed-size buffer. These are
+ * invoked from DDB's input and output routines. If we hit the limit on the
+ * buffer, we simply drop further data.
+ */
+void
+db_capture_write(char *buffer, u_int buflen)
+{
+ u_int len;
+
+ if (db_capture_inprogress == 0 || db_capture_inpager)
+ return;
+ len = min(buflen, db_capture_bufsize - db_capture_bufoff);
+ bcopy(buffer, db_capture_buf + db_capture_bufoff, len);
+ db_capture_bufoff += len;
+
+ KASSERT(db_capture_bufoff <= db_capture_bufsize,
+ ("db_capture_write: bufoff > bufsize"));
+}
+
+void
+db_capture_writech(char ch)
+{
+
+ return (db_capture_write(&ch, sizeof(ch)));
+}
+
+void
+db_capture_enterpager(void)
+{
+
+ db_capture_inpager = 1;
+}
+
+void
+db_capture_exitpager(void)
+{
+
+ db_capture_inpager = 0;
+}
+
+/*
+ * Zero out any bytes left in the last block of the DDB capture buffer. This
+ * is run shortly before writing the blocks to disk, rather than when output
+ * capture is stopped, in order to avoid injecting nul's into the middle of
+ * output.
+ */
+static void
+db_capture_zeropad(void)
+{
+ u_int len;
+
+ len = min(TEXTDUMP_BLOCKSIZE, (db_capture_bufsize -
+ db_capture_bufoff) % TEXTDUMP_BLOCKSIZE);
+ bzero(db_capture_buf + db_capture_bufoff, len);
+ db_capture_bufpadding = len;
+}
+
+/*
+ * Reset capture state, which flushes buffers.
+ */
+static void
+db_capture_reset(void)
+{
+
+ db_capture_inprogress = 0;
+ db_capture_bufoff = 0;
+ db_capture_bufpadding = 0;
+}
+
+/*
+ * Start capture. Only one session is allowed at any time, but we may
+ * continue a previous session, so the buffer isn't reset.
+ */
+static void
+db_capture_start(void)
+{
+
+ if (db_capture_inprogress) {
+ db_printf("Capture already started\n");
+ return;
+ }
+ db_capture_inprogress = 1;
+}
+
+/*
+ * Terminate DDB output capture--real work is deferred to db_capture_dump,
+ * which executes outside of the DDB context. We don't zero pad here because
+ * capture may be started again before the dump takes place.
+ */
+static void
+db_capture_stop(void)
+{
+
+ if (db_capture_inprogress == 0) {
+ db_printf("Capture not started\n");
+ return;
+ }
+ db_capture_inprogress = 0;
+}
+
+/*
+ * Dump DDB(4) captured output (and resets capture buffers).
+ */
+void
+db_capture_dump(struct dumperinfo *di)
+{
+ u_int offset;
+
+ if (db_capture_bufoff == 0)
+ return;
+
+ db_capture_zeropad();
+ textdump_mkustar(textdump_block_buffer, DDB_CAPTURE_FILENAME,
+ db_capture_bufoff);
+ (void)textdump_writenextblock(di, textdump_block_buffer);
+ for (offset = 0; offset < db_capture_bufoff + db_capture_bufpadding;
+ offset += TEXTDUMP_BLOCKSIZE)
+ (void)textdump_writenextblock(di, db_capture_buf + offset);
+ db_capture_bufoff = 0;
+ db_capture_bufpadding = 0;
+}
+
+/*-
+ * DDB(4) command to manage capture:
+ *
+ * capture on - start DDB output capture
+ * capture off - stop DDB output capture
+ * capture reset - reset DDB capture buffer (also stops capture)
+ * capture status - print DDB output capture status
+ */
+static void
+db_capture_usage(void)
+{
+
+ db_error("capture [on|off|reset|status]\n");
+}
+
+void
+db_capture_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count,
+ char *modif)
+{
+ int t;
+
+ t = db_read_token();
+ if (t != tIDENT) {
+ db_capture_usage();
+ return;
+ }
+ if (db_read_token() != tEOL)
+ db_error("?\n");
+ if (strcmp(db_tok_string, "on") == 0)
+ db_capture_start();
+ else if (strcmp(db_tok_string, "off") == 0)
+ db_capture_stop();
+ else if (strcmp(db_tok_string, "reset") == 0)
+ db_capture_reset();
+ else if (strcmp(db_tok_string, "status") == 0) {
+ db_printf("%u/%u bytes used\n", db_capture_bufoff,
+ db_capture_bufsize);
+ if (db_capture_inprogress)
+ db_printf("capture is on\n");
+ else
+ db_printf("capture is off\n");
+ } else
+ db_capture_usage();
+}
diff --git a/sys/ddb/db_command.c b/sys/ddb/db_command.c
new file mode 100644
index 0000000..cc4bde9
--- /dev/null
+++ b/sys/ddb/db_command.c
@@ -0,0 +1,872 @@
+/*-
+ * 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
+ * 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 the
+ * rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+/*
+ * Command dispatcher.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/linker_set.h>
+#include <sys/lock.h>
+#include <sys/kdb.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/reboot.h>
+#include <sys/signalvar.h>
+#include <sys/systm.h>
+#include <sys/cons.h>
+#include <sys/conf.h>
+#include <sys/watchdog.h>
+#include <sys/kernel.h>
+
+#include <ddb/ddb.h>
+#include <ddb/db_command.h>
+#include <ddb/db_lex.h>
+#include <ddb/db_output.h>
+
+#include <machine/cpu.h>
+#include <machine/setjmp.h>
+
+/*
+ * Exported global variables
+ */
+boolean_t db_cmd_loop_done;
+db_addr_t db_dot;
+db_addr_t db_last_addr;
+db_addr_t db_prev;
+db_addr_t db_next;
+
+static db_cmdfcn_t db_dump;
+static db_cmdfcn_t db_fncall;
+static db_cmdfcn_t db_gdb;
+static db_cmdfcn_t db_halt;
+static db_cmdfcn_t db_kill;
+static db_cmdfcn_t db_reset;
+static db_cmdfcn_t db_stack_trace;
+static db_cmdfcn_t db_stack_trace_all;
+static db_cmdfcn_t db_watchdog;
+
+/*
+ * 'show' commands
+ */
+
+static struct command db_show_all_cmds[] = {
+ { "trace", db_stack_trace_all, 0, 0 },
+};
+struct command_table db_show_all_table =
+ LIST_HEAD_INITIALIZER(db_show_all_table);
+
+static struct command db_show_cmds[] = {
+ { "all", 0, 0, &db_show_all_table },
+ { "registers", db_show_regs, 0, 0 },
+ { "breaks", db_listbreak_cmd, 0, 0 },
+ { "threads", db_show_threads, 0, 0 },
+};
+struct command_table db_show_table = LIST_HEAD_INITIALIZER(db_show_table);
+
+static struct command db_cmds[] = {
+ { "print", db_print_cmd, 0, 0 },
+ { "p", db_print_cmd, 0, 0 },
+ { "examine", db_examine_cmd, CS_SET_DOT, 0 },
+ { "x", db_examine_cmd, 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, 0, 0 },
+ { "d", db_delete_cmd, 0, 0 },
+ { "dump", db_dump, 0, 0 },
+ { "break", db_breakpoint_cmd, 0, 0 },
+ { "b", db_breakpoint_cmd, 0, 0 },
+ { "dwatch", db_deletewatch_cmd, 0, 0 },
+ { "watch", db_watchpoint_cmd, CS_MORE,0 },
+ { "dhwatch", db_deletehwatch_cmd, 0, 0 },
+ { "hwatch", db_hwatchpoint_cmd, 0, 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, CS_OWN, 0 },
+ { "t", db_stack_trace, CS_OWN, 0 },
+ /* XXX alias for all trace */
+ { "alltrace", db_stack_trace_all, 0, 0 },
+ { "where", db_stack_trace, CS_OWN, 0 },
+ { "bt", db_stack_trace, CS_OWN, 0 },
+ { "call", db_fncall, CS_OWN, 0 },
+ { "show", 0, 0, &db_show_table },
+ { "ps", db_ps, 0, 0 },
+ { "gdb", db_gdb, 0, 0 },
+ { "halt", db_halt, 0, 0 },
+ { "reboot", db_reset, 0, 0 },
+ { "reset", db_reset, 0, 0 },
+ { "kill", db_kill, CS_OWN, 0 },
+ { "watchdog", db_watchdog, CS_OWN, 0 },
+ { "thread", db_set_thread, CS_OWN, 0 },
+ { "run", db_run_cmd, CS_OWN, 0 },
+ { "script", db_script_cmd, CS_OWN, 0 },
+ { "scripts", db_scripts_cmd, 0, 0 },
+ { "unscript", db_unscript_cmd, CS_OWN, 0 },
+ { "capture", db_capture_cmd, CS_OWN, 0 },
+ { "textdump", db_textdump_cmd, CS_OWN, 0 },
+ { "findstack", db_findstack_cmd, 0, 0 },
+};
+struct command_table db_cmd_table = LIST_HEAD_INITIALIZER(db_cmd_table);
+
+static struct command *db_last_command = 0;
+
+/*
+ * 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.
+ */
+static boolean_t db_ed_style = TRUE;
+
+/*
+ * Utility routine - discard tokens through end-of-line.
+ */
+void
+db_skip_to_eol()
+{
+ int t;
+ do {
+ t = db_read_token();
+ } while (t != tEOL);
+}
+
+/*
+ * Results of command search.
+ */
+#define CMD_UNIQUE 0
+#define CMD_FOUND 1
+#define CMD_NONE 2
+#define CMD_AMBIGUOUS 3
+#define CMD_HELP 4
+
+static void db_cmd_match(char *name, struct command *cmd,
+ struct command **cmdp, int *resultp);
+static void db_cmd_list(struct command_table *table);
+static int db_cmd_search(char *name, struct command_table *table,
+ struct command **cmdp);
+static void db_command(struct command **last_cmdp,
+ struct command_table *cmd_table, int dopager);
+
+/*
+ * Initialize the command lists from the static tables.
+ */
+void
+db_command_init(void)
+{
+#define N(a) (sizeof(a) / sizeof(a[0]))
+ int i;
+
+ for (i = 0; i < N(db_cmds); i++)
+ db_command_register(&db_cmd_table, &db_cmds[i]);
+ for (i = 0; i < N(db_show_cmds); i++)
+ db_command_register(&db_show_table, &db_show_cmds[i]);
+ for (i = 0; i < N(db_show_all_cmds); i++)
+ db_command_register(&db_show_all_table, &db_show_all_cmds[i]);
+#undef N
+}
+
+/*
+ * Register a command.
+ */
+void
+db_command_register(struct command_table *list, struct command *cmd)
+{
+ struct command *c, *last;
+
+ last = NULL;
+ LIST_FOREACH(c, list, next) {
+ int n = strcmp(cmd->name, c->name);
+
+ /* Check that the command is not already present. */
+ if (n == 0) {
+ printf("%s: Warning, the command \"%s\" already exists;"
+ " ignoring request\n", __func__, cmd->name);
+ return;
+ }
+ if (n < 0) {
+ /* NB: keep list sorted lexicographically */
+ LIST_INSERT_BEFORE(c, cmd, next);
+ return;
+ }
+ last = c;
+ }
+ if (last == NULL)
+ LIST_INSERT_HEAD(list, cmd, next);
+ else
+ LIST_INSERT_AFTER(last, cmd, next);
+}
+
+/*
+ * Remove a command previously registered with db_command_register.
+ */
+void
+db_command_unregister(struct command_table *list, struct command *cmd)
+{
+ struct command *c;
+
+ LIST_FOREACH(c, list, next) {
+ if (cmd == c) {
+ LIST_REMOVE(cmd, next);
+ return;
+ }
+ }
+ /* NB: intentionally quiet */
+}
+
+/*
+ * Helper function to match a single command.
+ */
+static void
+db_cmd_match(name, cmd, cmdp, resultp)
+ char * name;
+ struct command *cmd;
+ struct command **cmdp; /* out */
+ int * resultp;
+{
+ char *lp, *rp;
+ int c;
+
+ lp = name;
+ rp = cmd->name;
+ while ((c = *lp) == *rp) {
+ if (c == 0) {
+ /* complete match */
+ *cmdp = cmd;
+ *resultp = CMD_UNIQUE;
+ return;
+ }
+ lp++;
+ rp++;
+ }
+ if (c == 0) {
+ /* end of name, not end of command -
+ partial match */
+ if (*resultp == CMD_FOUND) {
+ *resultp = CMD_AMBIGUOUS;
+ /* but keep looking for a full match -
+ this lets us match single letters */
+ } else {
+ *cmdp = cmd;
+ *resultp = CMD_FOUND;
+ }
+ }
+}
+
+/*
+ * Search for command prefix.
+ */
+static int
+db_cmd_search(name, table, cmdp)
+ char * name;
+ struct command_table *table;
+ struct command **cmdp; /* out */
+{
+ struct command *cmd;
+ int result = CMD_NONE;
+
+ LIST_FOREACH(cmd, table, next) {
+ db_cmd_match(name,cmd,cmdp,&result);
+ if (result == CMD_UNIQUE)
+ break;
+ }
+
+ if (result == CMD_NONE) {
+ /* check for 'help' */
+ if (name[0] == 'h' && name[1] == 'e'
+ && name[2] == 'l' && name[3] == 'p')
+ result = CMD_HELP;
+ }
+ return (result);
+}
+
+static void
+db_cmd_list(table)
+ struct command_table *table;
+{
+ register struct command *cmd;
+
+ LIST_FOREACH(cmd, table, next) {
+ db_printf("%-12s", cmd->name);
+ db_end_line(12);
+ }
+}
+
+static void
+db_command(last_cmdp, cmd_table, dopager)
+ struct command **last_cmdp; /* IN_OUT */
+ struct command_table *cmd_table;
+ int dopager;
+{
+ struct command *cmd = NULL;
+ 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) {
+ /* empty line repeats last command, at 'next' */
+ cmd = *last_cmdp;
+ addr = (db_expr_t)db_next;
+ have_addr = FALSE;
+ count = 1;
+ modif[0] = '\0';
+ }
+ else if (t == tEXCL) {
+ db_fncall((db_expr_t)0, (boolean_t)0, (db_expr_t)0, (char *)0);
+ 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:
+ db_printf("No such command\n");
+ 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) != NULL) {
+ 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\n");
+ 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\n");
+ db_flush_lex();
+ return;
+ }
+ }
+ else {
+ db_unread_token(t);
+ count = -1;
+ }
+ if ((cmd->flag & CS_MORE) == 0) {
+ db_skip_to_eol();
+ }
+ }
+ }
+ *last_cmdp = cmd;
+ if (cmd != 0) {
+ /*
+ * Execute the command.
+ */
+ if (dopager)
+ db_enable_pager();
+ else
+ db_disable_pager();
+ (*cmd->fcn)(addr, have_addr, count, modif);
+ if (dopager)
+ db_disable_pager();
+
+ 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;
+ }
+ }
+}
+
+/*
+ * At least one non-optional command must be implemented using
+ * DB_COMMAND() so that db_cmd_set gets created. Here is one.
+ */
+DB_COMMAND(panic, db_panic)
+{
+ db_disable_pager();
+ panic("from debugger");
+}
+
+void
+db_command_loop()
+{
+ /*
+ * Initialize 'prev' and 'next' to dot.
+ */
+ db_prev = db_dot;
+ db_next = db_dot;
+
+ db_cmd_loop_done = 0;
+ while (!db_cmd_loop_done) {
+ if (db_print_position() != 0)
+ db_printf("\n");
+
+ db_printf("db> ");
+ (void) db_read_line();
+
+ db_command(&db_last_command, &db_cmd_table, /* dopager */ 1);
+ }
+}
+
+/*
+ * Execute a command on behalf of a script. The caller is responsible for
+ * making sure that the command string is < DB_MAXLINE or it will be
+ * truncated.
+ *
+ * XXXRW: Runs by injecting faked input into DDB input stream; it would be
+ * nicer to use an alternative approach that didn't mess with the previous
+ * command buffer.
+ */
+void
+db_command_script(const char *command)
+{
+ db_prev = db_next = db_dot;
+ db_inject_line(command);
+ db_command(&db_last_command, &db_cmd_table, /* dopager */ 0);
+}
+
+void
+db_error(s)
+ const char *s;
+{
+ if (s)
+ db_printf("%s", s);
+ db_flush_lex();
+ kdb_reenter();
+}
+
+static void
+db_dump(db_expr_t dummy, boolean_t dummy2, db_expr_t dummy3, char *dummy4)
+{
+ int error;
+
+ if (textdump_pending) {
+ db_printf("textdump_pending set.\n"
+ "run \"textdump unset\" first or \"textdump dump\" for a textdump.\n");
+ return;
+ }
+ error = doadump(FALSE);
+ if (error) {
+ db_printf("Cannot dump: ");
+ switch (error) {
+ case EBUSY:
+ db_printf("debugger got invoked while dumping.\n");
+ break;
+ case ENXIO:
+ db_printf("no dump device specified.\n");
+ break;
+ default:
+ db_printf("unknown error (error=%d).\n", error);
+ break;
+ }
+ }
+}
+
+/*
+ * Call random function:
+ * !expr(arg,arg,arg)
+ */
+
+/* The generic implementation supports a maximum of 10 arguments. */
+typedef db_expr_t __db_f(db_expr_t, db_expr_t, db_expr_t, db_expr_t,
+ db_expr_t, db_expr_t, db_expr_t, db_expr_t, db_expr_t, db_expr_t);
+
+static __inline int
+db_fncall_generic(db_expr_t addr, db_expr_t *rv, int nargs, db_expr_t args[])
+{
+ __db_f *f = (__db_f *)addr;
+
+ if (nargs > 10) {
+ db_printf("Too many arguments (max 10)\n");
+ return (0);
+ }
+ *rv = (*f)(args[0], args[1], args[2], args[3], args[4], args[5],
+ args[6], args[7], args[8], args[9]);
+ return (1);
+}
+
+static void
+db_fncall(dummy1, dummy2, dummy3, dummy4)
+ db_expr_t dummy1;
+ boolean_t dummy2;
+ db_expr_t dummy3;
+ char * dummy4;
+{
+ db_expr_t fn_addr;
+ db_expr_t args[DB_MAXARGS];
+ int nargs = 0;
+ db_expr_t retval;
+ int t;
+
+ if (!db_expression(&fn_addr)) {
+ db_printf("Bad function\n");
+ db_flush_lex();
+ return;
+ }
+
+ t = db_read_token();
+ if (t == tLPAREN) {
+ if (db_expression(&args[0])) {
+ nargs++;
+ while ((t = db_read_token()) == tCOMMA) {
+ if (nargs == DB_MAXARGS) {
+ db_printf("Too many arguments (max %d)\n", DB_MAXARGS);
+ 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;
+ }
+ }
+ db_skip_to_eol();
+ db_disable_pager();
+
+ if (DB_CALL(fn_addr, &retval, nargs, args))
+ db_printf("= %#lr\n", (long)retval);
+}
+
+static void
+db_halt(db_expr_t dummy, boolean_t dummy2, db_expr_t dummy3, char *dummy4)
+{
+
+ cpu_halt();
+}
+
+static void
+db_kill(dummy1, dummy2, dummy3, dummy4)
+ db_expr_t dummy1;
+ boolean_t dummy2;
+ db_expr_t dummy3;
+ char * dummy4;
+{
+ db_expr_t old_radix, pid, sig;
+ struct proc *p;
+
+#define DB_ERROR(f) do { db_printf f; db_flush_lex(); goto out; } while (0)
+
+ /*
+ * PIDs and signal numbers are typically represented in base
+ * 10, so make that the default here. It can, of course, be
+ * overridden by specifying a prefix.
+ */
+ old_radix = db_radix;
+ db_radix = 10;
+ /* Retrieve arguments. */
+ if (!db_expression(&sig))
+ DB_ERROR(("Missing signal number\n"));
+ if (!db_expression(&pid))
+ DB_ERROR(("Missing process ID\n"));
+ db_skip_to_eol();
+ if (!_SIG_VALID(sig))
+ DB_ERROR(("Signal number out of range\n"));
+
+ /*
+ * Find the process in question. allproc_lock is not needed
+ * since we're in DDB.
+ */
+ /* sx_slock(&allproc_lock); */
+ FOREACH_PROC_IN_SYSTEM(p)
+ if (p->p_pid == pid)
+ break;
+ /* sx_sunlock(&allproc_lock); */
+ if (p == NULL)
+ DB_ERROR(("Can't find process with pid %ld\n", (long) pid));
+
+ /* If it's already locked, bail; otherwise, do the deed. */
+ if (PROC_TRYLOCK(p) == 0)
+ DB_ERROR(("Can't lock process with pid %ld\n", (long) pid));
+ else {
+ pksignal(p, sig, NULL);
+ PROC_UNLOCK(p);
+ }
+
+out:
+ db_radix = old_radix;
+#undef DB_ERROR
+}
+
+/*
+ * Reboot. In case there is an additional argument, take it as delay in
+ * seconds. Default to 15s if we cannot parse it and make sure we will
+ * never wait longer than 1 week. Some code is similar to
+ * kern_shutdown.c:shutdown_panic().
+ */
+#ifndef DB_RESET_MAXDELAY
+#define DB_RESET_MAXDELAY (3600 * 24 * 7)
+#endif
+
+static void
+db_reset(db_expr_t addr, boolean_t have_addr, db_expr_t count __unused,
+ char *modif __unused)
+{
+ int delay, loop;
+
+ if (have_addr) {
+ delay = (int)db_hex2dec(addr);
+
+ /* If we parse to fail, use 15s. */
+ if (delay == -1)
+ delay = 15;
+
+ /* Cap at one week. */
+ if ((uintmax_t)delay > (uintmax_t)DB_RESET_MAXDELAY)
+ delay = DB_RESET_MAXDELAY;
+
+ db_printf("Automatic reboot in %d seconds - "
+ "press a key on the console to abort\n", delay);
+ for (loop = delay * 10; loop > 0; --loop) {
+ DELAY(1000 * 100); /* 1/10th second */
+ /* Did user type a key? */
+ if (cncheckc() != -1)
+ return;
+ }
+ }
+
+ cpu_reset();
+}
+
+static void
+db_watchdog(dummy1, dummy2, dummy3, dummy4)
+ db_expr_t dummy1;
+ boolean_t dummy2;
+ db_expr_t dummy3;
+ char * dummy4;
+{
+ db_expr_t old_radix, tout;
+ int err, i;
+
+ old_radix = db_radix;
+ db_radix = 10;
+ err = db_expression(&tout);
+ db_skip_to_eol();
+ db_radix = old_radix;
+
+ /* If no argument is provided the watchdog will just be disabled. */
+ if (err == 0) {
+ db_printf("No argument provided, disabling watchdog\n");
+ tout = 0;
+ } else if ((tout & WD_INTERVAL) == WD_TO_NEVER) {
+ db_error("Out of range watchdog interval\n");
+ return;
+ }
+ EVENTHANDLER_INVOKE(watchdog_list, tout, &i);
+}
+
+static void
+db_gdb(db_expr_t dummy1, boolean_t dummy2, db_expr_t dummy3, char *dummy4)
+{
+
+ if (kdb_dbbe_select("gdb") != 0) {
+ db_printf("The remote GDB backend could not be selected.\n");
+ return;
+ }
+ /*
+ * Mark that we are done in the debugger. kdb_trap()
+ * should re-enter with the new backend.
+ */
+ db_cmd_loop_done = 1;
+ db_printf("(ctrl-c will return control to ddb)\n");
+}
+
+static void
+db_stack_trace(db_expr_t tid, boolean_t hastid, db_expr_t count, char *modif)
+{
+ struct thread *td;
+ db_expr_t radix;
+ pid_t pid;
+ int t;
+
+ /*
+ * We parse our own arguments. We don't like the default radix.
+ */
+ radix = db_radix;
+ db_radix = 10;
+ hastid = db_expression(&tid);
+ 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;
+ }
+ db_skip_to_eol();
+ db_radix = radix;
+
+ if (hastid) {
+ td = kdb_thr_lookup((lwpid_t)tid);
+ if (td == NULL)
+ td = kdb_thr_from_pid((pid_t)tid);
+ if (td == NULL) {
+ db_printf("Thread %d not found\n", (int)tid);
+ return;
+ }
+ } else
+ td = kdb_thread;
+ if (td->td_proc != NULL)
+ pid = td->td_proc->p_pid;
+ else
+ pid = -1;
+ db_printf("Tracing pid %d tid %ld td %p\n", pid, (long)td->td_tid, td);
+ db_trace_thread(td, count);
+}
+
+static void
+db_stack_trace_all(db_expr_t dummy, boolean_t dummy2, db_expr_t dummy3,
+ char *dummy4)
+{
+ struct proc *p;
+ struct thread *td;
+ jmp_buf jb;
+ void *prev_jb;
+
+ FOREACH_PROC_IN_SYSTEM(p) {
+ prev_jb = kdb_jmpbuf(jb);
+ if (setjmp(jb) == 0) {
+ FOREACH_THREAD_IN_PROC(p, td) {
+ db_printf("\nTracing command %s pid %d tid %ld td %p\n",
+ p->p_comm, p->p_pid, (long)td->td_tid, td);
+ db_trace_thread(td, -1);
+ if (db_pager_quit) {
+ kdb_jmpbuf(prev_jb);
+ return;
+ }
+ }
+ }
+ kdb_jmpbuf(prev_jb);
+ }
+}
+
+/*
+ * Take the parsed expression value from the command line that was parsed
+ * as a hexadecimal value and convert it as if the expression was parsed
+ * as a decimal value. Returns -1 if the expression was not a valid
+ * decimal value.
+ */
+db_expr_t
+db_hex2dec(db_expr_t expr)
+{
+ uintptr_t x, y;
+ db_expr_t val;
+
+ y = 1;
+ val = 0;
+ x = expr;
+ while (x != 0) {
+ if (x % 16 > 9)
+ return (-1);
+ val += (x % 16) * (y);
+ x >>= 4;
+ y *= 10;
+ }
+ return (val);
+}
diff --git a/sys/ddb/db_command.h b/sys/ddb/db_command.h
new file mode 100644
index 0000000..2103a02
--- /dev/null
+++ b/sys/ddb/db_command.h
@@ -0,0 +1,57 @@
+/*-
+ * 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
+ * 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 the
+ * rights to redistribute these changes.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _DDB_DB_COMMAND_H_
+#define _DDB_DB_COMMAND_H_
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+
+/*
+ * Helper functions.
+ */
+db_expr_t db_hex2dec(db_expr_t expr);
+
+/*
+ * Command loop declarations.
+ */
+
+void db_command_init(void);
+void db_command_loop(void);
+void db_command_script(const char *command);
+
+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 */
+
+#endif /* !_DDB_DB_COMMAND_H_ */
diff --git a/sys/ddb/db_examine.c b/sys/ddb/db_examine.c
new file mode 100644
index 0000000..fc4f472
--- /dev/null
+++ b/sys/ddb/db_examine.c
@@ -0,0 +1,341 @@
+/*-
+ * 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
+ * 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 the
+ * rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <ddb/ddb.h>
+
+#include <ddb/db_lex.h>
+#include <ddb/db_output.h>
+#include <ddb/db_command.h>
+#include <ddb/db_sym.h>
+#include <ddb/db_access.h>
+
+static char db_examine_format[TOK_STRING_SIZE] = "x";
+
+static void db_examine(db_addr_t, char *, int);
+static void db_search(db_addr_t, int, db_expr_t, db_expr_t, u_int);
+
+/*
+ * Examine (print) data.
+ */
+/*ARGSUSED*/
+void
+db_examine_cmd(addr, have_addr, count, modif)
+ db_expr_t addr;
+ boolean_t have_addr;
+ db_expr_t count;
+ char * modif;
+{
+ if (modif[0] != '\0')
+ db_strcpy(db_examine_format, modif);
+
+ if (count == -1)
+ count = 1;
+
+ db_examine((db_addr_t) addr, db_examine_format, count);
+}
+
+static void
+db_examine(addr, fmt, count)
+ register
+ db_addr_t addr;
+ char * fmt; /* format string */
+ int count; /* repeat count */
+{
+ int c;
+ db_expr_t value;
+ int size;
+ int width;
+ char * fp;
+
+ while (--count >= 0 && !db_pager_quit) {
+ fp = fmt;
+ size = 4;
+ while ((c = *fp++) != 0) {
+ switch (c) {
+ case 'b':
+ size = 1;
+ break;
+ case 'h':
+ size = 2;
+ break;
+ case 'l':
+ size = 4;
+ break;
+ case 'g':
+ size = 8;
+ break;
+ case 'a': /* address */
+ size = sizeof(void *);
+ /* always forces a new line */
+ if (db_print_position() != 0)
+ db_printf("\n");
+ db_prev = addr;
+ db_printsym(addr, DB_STGY_ANY);
+ db_printf(":\t");
+ break;
+ default:
+ if (db_print_position() == 0) {
+ /* Print the address. */
+ db_printsym(addr, DB_STGY_ANY);
+ db_printf(":\t");
+ db_prev = addr;
+ }
+
+ width = size * 4;
+ switch (c) {
+ case 'r': /* signed, current radix */
+ value = db_get_value(addr, size, TRUE);
+ addr += size;
+ db_printf("%+-*lr", width, (long)value);
+ break;
+ case 'x': /* unsigned hex */
+ value = db_get_value(addr, size, FALSE);
+ addr += size;
+ db_printf("%-*lx", width, (long)value);
+ break;
+ case 'z': /* signed hex */
+ value = db_get_value(addr, size, TRUE);
+ addr += size;
+ db_printf("%-*ly", width, (long)value);
+ break;
+ case 'd': /* signed decimal */
+ value = db_get_value(addr, size, TRUE);
+ addr += size;
+ db_printf("%-*ld", width, (long)value);
+ break;
+ case 'u': /* unsigned decimal */
+ value = db_get_value(addr, size, FALSE);
+ addr += size;
+ db_printf("%-*lu", width, (long)value);
+ break;
+ case 'o': /* unsigned octal */
+ value = db_get_value(addr, size, FALSE);
+ addr += size;
+ db_printf("%-*lo", width, (long)value);
+ break;
+ case 'c': /* character */
+ value = db_get_value(addr, 1, FALSE);
+ addr += 1;
+ if (value >= ' ' && value <= '~')
+ db_printf("%c", (int)value);
+ else
+ db_printf("\\%03o", (int)value);
+ break;
+ case 's': /* null-terminated string */
+ for (;;) {
+ value = db_get_value(addr, 1, FALSE);
+ addr += 1;
+ if (value == 0)
+ break;
+ if (value >= ' ' && value <= '~')
+ db_printf("%c", (int)value);
+ else
+ db_printf("\\%03o", (int)value);
+ }
+ break;
+ case 'S': /* symbol */
+ value = db_get_value(addr, sizeof(void *),
+ FALSE);
+ addr += sizeof(void *);
+ db_printsym(value, DB_STGY_ANY);
+ break;
+ case 'i': /* instruction */
+ addr = db_disasm(addr, FALSE);
+ break;
+ case 'I': /* instruction, alternate form */
+ addr = db_disasm(addr, TRUE);
+ break;
+ default:
+ break;
+ }
+ if (db_print_position() != 0)
+ db_end_line(1);
+ break;
+ }
+ }
+ }
+ db_next = addr;
+}
+
+/*
+ * Print value.
+ */
+static char db_print_format = 'x';
+
+/*ARGSUSED*/
+void
+db_print_cmd(addr, have_addr, count, modif)
+ db_expr_t addr;
+ boolean_t have_addr;
+ db_expr_t count;
+ char * modif;
+{
+ db_expr_t value;
+
+ if (modif[0] != '\0')
+ db_print_format = modif[0];
+
+ switch (db_print_format) {
+ case 'a':
+ db_printsym((db_addr_t)addr, DB_STGY_ANY);
+ break;
+ case 'r':
+ db_printf("%+11lr", (long)addr);
+ break;
+ case 'x':
+ db_printf("%8lx", (unsigned long)addr);
+ break;
+ case 'z':
+ db_printf("%8ly", (long)addr);
+ break;
+ case 'd':
+ db_printf("%11ld", (long)addr);
+ break;
+ case 'u':
+ db_printf("%11lu", (unsigned long)addr);
+ break;
+ case 'o':
+ db_printf("%16lo", (unsigned long)addr);
+ break;
+ case 'c':
+ value = addr & 0xFF;
+ if (value >= ' ' && value <= '~')
+ db_printf("%c", (int)value);
+ else
+ db_printf("\\%03o", (int)value);
+ break;
+ }
+ db_printf("\n");
+}
+
+void
+db_print_loc_and_inst(loc)
+ db_addr_t loc;
+{
+ db_printsym(loc, DB_STGY_PROC);
+ db_printf(":\t");
+ (void) db_disasm(loc, TRUE);
+}
+
+/*
+ * Search for a value in memory.
+ * Syntax: search [/bhl] addr value [mask] [,count]
+ */
+void
+db_search_cmd(dummy1, dummy2, dummy3, dummy4)
+ db_expr_t dummy1;
+ boolean_t dummy2;
+ db_expr_t dummy3;
+ char * dummy4;
+{
+ int t;
+ db_addr_t addr;
+ int size;
+ db_expr_t value;
+ db_expr_t mask;
+ db_expr_t count;
+
+ t = db_read_token();
+ if (t == tSLASH) {
+ t = db_read_token();
+ if (t != tIDENT) {
+ bad_modifier:
+ db_printf("Bad modifier\n");
+ db_flush_lex();
+ return;
+ }
+
+ if (!strcmp(db_tok_string, "b"))
+ size = 1;
+ else if (!strcmp(db_tok_string, "h"))
+ size = 2;
+ else if (!strcmp(db_tok_string, "l"))
+ size = 4;
+ else
+ goto bad_modifier;
+ } else {
+ db_unread_token(t);
+ size = 4;
+ }
+
+ if (!db_expression((db_expr_t *)&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 = 0xffffffffUL;
+
+ 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 */
+ }
+ db_skip_to_eol();
+
+ db_search(addr, size, value, mask, count);
+}
+
+static void
+db_search(addr, size, value, mask, count)
+ register
+ db_addr_t addr;
+ int size;
+ db_expr_t value;
+ db_expr_t mask;
+ unsigned int count;
+{
+ while (count-- != 0) {
+ db_prev = addr;
+ if ((db_get_value(addr, size, FALSE) & mask) == value)
+ break;
+ addr += size;
+ }
+ db_next = addr;
+}
diff --git a/sys/ddb/db_expr.c b/sys/ddb/db_expr.c
new file mode 100644
index 0000000..424384c
--- /dev/null
+++ b/sys/ddb/db_expr.c
@@ -0,0 +1,228 @@
+/*-
+ * 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
+ * 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 the
+ * rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <ddb/ddb.h>
+#include <ddb/db_lex.h>
+#include <ddb/db_access.h>
+#include <ddb/db_command.h>
+
+static boolean_t db_add_expr(db_expr_t *valuep);
+static boolean_t db_mult_expr(db_expr_t *valuep);
+static boolean_t db_shift_expr(db_expr_t *valuep);
+static boolean_t db_term(db_expr_t *valuep);
+static boolean_t db_unary(db_expr_t *valuep);
+
+static boolean_t
+db_term(db_expr_t *valuep)
+{
+ int t;
+
+ t = db_read_token();
+ if (t == tIDENT) {
+ if (!db_value_of_name(db_tok_string, valuep) &&
+ !db_value_of_name_pcpu(db_tok_string, valuep) &&
+ !db_value_of_name_vnet(db_tok_string, valuep)) {
+ db_error("Symbol not found\n");
+ /*NOTREACHED*/
+ }
+ return (TRUE);
+ }
+ if (t == tNUMBER) {
+ *valuep = (db_expr_t)db_tok_number;
+ return (TRUE);
+ }
+ if (t == tDOT) {
+ *valuep = (db_expr_t)db_dot;
+ return (TRUE);
+ }
+ if (t == tDOTDOT) {
+ *valuep = (db_expr_t)db_prev;
+ return (TRUE);
+ }
+ if (t == tPLUS) {
+ *valuep = (db_expr_t) db_next;
+ return (TRUE);
+ }
+ if (t == tDITTO) {
+ *valuep = (db_expr_t)db_last_addr;
+ return (TRUE);
+ }
+ if (t == tDOLLAR) {
+ if (!db_get_variable(valuep))
+ return (FALSE);
+ return (TRUE);
+ }
+ if (t == tLPAREN) {
+ if (!db_expression(valuep)) {
+ db_error("Syntax error\n");
+ /*NOTREACHED*/
+ }
+ t = db_read_token();
+ if (t != tRPAREN) {
+ db_error("Syntax error\n");
+ /*NOTREACHED*/
+ }
+ return (TRUE);
+ }
+ db_unread_token(t);
+ return (FALSE);
+}
+
+static boolean_t
+db_unary(db_expr_t *valuep)
+{
+ int t;
+
+ t = db_read_token();
+ if (t == tMINUS) {
+ if (!db_unary(valuep)) {
+ db_error("Syntax error\n");
+ /*NOTREACHED*/
+ }
+ *valuep = -*valuep;
+ return (TRUE);
+ }
+ if (t == tSTAR) {
+ /* indirection */
+ if (!db_unary(valuep)) {
+ db_error("Syntax error\n");
+ /*NOTREACHED*/
+ }
+ *valuep = db_get_value((db_addr_t)*valuep, sizeof(void *), FALSE);
+ return (TRUE);
+ }
+ db_unread_token(t);
+ return (db_term(valuep));
+}
+
+static boolean_t
+db_mult_expr(db_expr_t *valuep)
+{
+ db_expr_t lhs, rhs;
+ int t;
+
+ if (!db_unary(&lhs))
+ return (FALSE);
+
+ t = db_read_token();
+ while (t == tSTAR || t == tSLASH || t == tPCT || t == tHASH) {
+ if (!db_term(&rhs)) {
+ db_error("Syntax error\n");
+ /*NOTREACHED*/
+ }
+ if (t == tSTAR)
+ lhs *= rhs;
+ else {
+ 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);
+}
+
+static boolean_t
+db_add_expr(db_expr_t *valuep)
+{
+ db_expr_t lhs, rhs;
+ int t;
+
+ if (!db_mult_expr(&lhs))
+ return (FALSE);
+
+ t = db_read_token();
+ while (t == tPLUS || t == tMINUS) {
+ if (!db_mult_expr(&rhs)) {
+ db_error("Syntax error\n");
+ /*NOTREACHED*/
+ }
+ if (t == tPLUS)
+ lhs += rhs;
+ else
+ lhs -= rhs;
+ t = db_read_token();
+ }
+ db_unread_token(t);
+ *valuep = lhs;
+ return (TRUE);
+}
+
+static boolean_t
+db_shift_expr(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_error("Syntax error\n");
+ /*NOTREACHED*/
+ }
+ if (rhs < 0) {
+ db_error("Negative shift amount\n");
+ /*NOTREACHED*/
+ }
+ if (t == tSHIFT_L)
+ lhs <<= rhs;
+ else {
+ /* Shift right is unsigned */
+ lhs = (unsigned) lhs >> rhs;
+ }
+ t = db_read_token();
+ }
+ db_unread_token(t);
+ *valuep = lhs;
+ return (TRUE);
+}
+
+int
+db_expression(db_expr_t *valuep)
+{
+ return (db_shift_expr(valuep));
+}
diff --git a/sys/ddb/db_input.c b/sys/ddb/db_input.c
new file mode 100644
index 0000000..171b3da
--- /dev/null
+++ b/sys/ddb/db_input.c
@@ -0,0 +1,374 @@
+/*-
+ * 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
+ * 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 the
+ * rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/cons.h>
+
+#include <ddb/ddb.h>
+#include <ddb/db_output.h>
+
+/*
+ * 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.
+ */
+static char * db_lbuf_start; /* start of input line buffer */
+static char * db_lbuf_end; /* end of input line buffer */
+static char * db_lc; /* current character */
+static char * db_le; /* one past last character */
+
+/*
+ * Simple input line history support.
+ */
+static char db_lhistory[2048];
+static int db_lhistlsize, db_lhistidx, db_lhistcur;
+static int db_lhist_nlines;
+
+#define CTRL(c) ((c) & 0x1f)
+#define BLANK ' '
+#define BACKUP '\b'
+
+static int cnmaygetc(void);
+static void db_delete(int n, int bwd);
+static int db_inputchar(int c);
+static void db_putnchars(int c, int count);
+static void db_putstring(char *s, int count);
+
+static void
+db_putstring(s, count)
+ char *s;
+ int count;
+{
+ while (--count >= 0)
+ cnputc(*s++);
+}
+
+static 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
+static 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;
+}
+
+/* returns TRUE at end-of-line */
+static int
+db_inputchar(c)
+ int c;
+{
+ static int escstate;
+
+ if (escstate == 1) {
+ /* ESC seen, look for [ or O */
+ if (c == '[' || c == 'O')
+ escstate++;
+ else
+ escstate = 0; /* re-init state machine */
+ return (0);
+ } else if (escstate == 2) {
+ escstate = 0;
+ /*
+ * If a valid cursor key has been found, translate
+ * into an emacs-style control key, and fall through.
+ * Otherwise, drop off.
+ */
+ switch (c) {
+ case 'A': /* up */
+ c = CTRL('p');
+ break;
+ case 'B': /* down */
+ c = CTRL('n');
+ break;
+ case 'C': /* right */
+ c = CTRL('f');
+ break;
+ case 'D': /* left */
+ c = CTRL('b');
+ break;
+ default:
+ return (0);
+ }
+ }
+
+ switch (c) {
+ case CTRL('['):
+ escstate = 1;
+ break;
+ 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('u'):
+ /* kill entire line: */
+ /* at first, delete to beginning of line */
+ if (db_lc > db_lbuf_start)
+ db_delete(db_lc - db_lbuf_start, DEL_BWD);
+ /* FALLTHROUGH */
+ case CTRL('k'):
+ /* delete to end of line */
+ if (db_lc < db_le)
+ db_delete(db_le - db_lc, DEL_FWD);
+ 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;
+ case CTRL('r'):
+ db_putstring("^R\n", 3);
+ redraw:
+ 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 CTRL('p'):
+ /* Make previous history line the active one. */
+ if (db_lhistcur >= 0) {
+ bcopy(db_lhistory + db_lhistcur * db_lhistlsize,
+ db_lbuf_start, db_lhistlsize);
+ db_lhistcur--;
+ goto hist_redraw;
+ }
+ break;
+ case CTRL('n'):
+ /* Make next history line the active one. */
+ if (db_lhistcur < db_lhistidx - 1) {
+ db_lhistcur += 2;
+ bcopy(db_lhistory + db_lhistcur * db_lhistlsize,
+ db_lbuf_start, db_lhistlsize);
+ } else {
+ /*
+ * ^N through tail of history, reset the
+ * buffer to zero length.
+ */
+ *db_lbuf_start = '\0';
+ db_lhistcur = db_lhistidx;
+ }
+
+ hist_redraw:
+ db_putnchars(BACKUP, db_lc - db_lbuf_start);
+ db_putnchars(BLANK, db_le - db_lbuf_start);
+ db_putnchars(BACKUP, db_le - db_lbuf_start);
+ db_le = strchr(db_lbuf_start, '\0');
+ if (db_le[-1] == '\r' || db_le[-1] == '\n')
+ *--db_le = '\0';
+ db_lc = db_le;
+ goto redraw;
+
+ case -1:
+ /*
+ * eek! the console returned eof.
+ * probably that means we HAVE no console.. we should try bail
+ * XXX
+ */
+ c = '\r';
+ case '\n':
+ /* FALLTHROUGH */
+ case '\r':
+ *db_le++ = c;
+ return (1);
+ 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 (0);
+}
+
+static int
+cnmaygetc()
+{
+ return (-1);
+}
+
+int
+db_readline(lstart, lsize)
+ char * lstart;
+ int lsize;
+{
+
+ if (lsize < 2)
+ return (0);
+ if (lsize != db_lhistlsize) {
+ /*
+ * (Re)initialize input line history. Throw away any
+ * existing history.
+ */
+ db_lhist_nlines = sizeof(db_lhistory) / lsize;
+ db_lhistlsize = lsize;
+ db_lhistidx = -1;
+ }
+ db_lhistcur = db_lhistidx;
+
+ db_force_whitespace(); /* synch output position */
+
+ db_lbuf_start = lstart;
+ db_lbuf_end = lstart + lsize - 2; /* Will append NL and NUL. */
+ db_lc = lstart;
+ db_le = lstart;
+
+ while (!db_inputchar(cngetc()))
+ continue;
+
+ db_capture_write(lstart, db_le - db_lbuf_start);
+ db_printf("\n"); /* synch output position */
+ *db_le = 0;
+
+ if (db_le - db_lbuf_start > 1) {
+ /* Maintain input line history for non-empty lines. */
+ if (++db_lhistidx == db_lhist_nlines) {
+ /* Rotate history. */
+ bcopy(db_lhistory + db_lhistlsize, db_lhistory,
+ db_lhistlsize * (db_lhist_nlines - 1));
+ db_lhistidx--;
+ }
+ bcopy(lstart, db_lhistory + db_lhistidx * db_lhistlsize,
+ db_lhistlsize);
+ }
+
+ 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;
+ }
+}
diff --git a/sys/ddb/db_lex.c b/sys/ddb/db_lex.c
new file mode 100644
index 0000000..1c8151e
--- /dev/null
+++ b/sys/ddb/db_lex.c
@@ -0,0 +1,314 @@
+/*-
+ * 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
+ * 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 the
+ * rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+/*
+ * Lexical analyzer.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/libkern.h>
+
+#include <ddb/ddb.h>
+#include <ddb/db_lex.h>
+
+static char db_line[DB_MAXLINE];
+static char * db_lp, *db_endlp;
+
+static int db_lex(void);
+static void db_flush_line(void);
+static int db_read_char(void);
+static void db_unread_char(int);
+
+int
+db_read_line()
+{
+ int i;
+
+ i = db_readline(db_line, sizeof(db_line));
+ if (i == 0)
+ return (0); /* EOI */
+ db_lp = db_line;
+ db_endlp = db_lp + i;
+ return (i);
+}
+
+/*
+ * Simulate a line of input into DDB.
+ */
+void
+db_inject_line(const char *command)
+{
+
+ strlcpy(db_line, command, sizeof(db_line));
+ db_lp = db_line;
+ db_endlp = db_lp + strlen(command);
+}
+
+/*
+ * In rare cases, we may want to pull the remainder of the line input
+ * verbatim, rather than lexing it. For example, when assigning literal
+ * values associated with scripts. In that case, return a static pointer to
+ * the current location in the input buffer. The caller must be aware that
+ * the contents are not stable if other lex/input calls are made.
+ */
+char *
+db_get_line(void)
+{
+
+ return (db_lp);
+}
+
+static void
+db_flush_line()
+{
+ db_lp = db_line;
+ db_endlp = db_line;
+}
+
+static int db_look_char = 0;
+
+static 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);
+}
+
+static void
+db_unread_char(c)
+ int c;
+{
+ db_look_char = c;
+}
+
+static int db_look_token = 0;
+
+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
+ 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;
+}
+
+static int
+db_lex()
+{
+ int c;
+
+ c = db_read_char();
+ while (c <= ' ' || c > '~') {
+ if (c == '\n' || c == -1)
+ return (tEOL);
+ c = db_read_char();
+ }
+
+ if (c >= '0' && c <= '9') {
+ /* number */
+ int r, digit = 0;
+
+ 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 {
+ r = db_radix;
+ db_unread_char(c);
+ }
+ c = db_read_char();
+ }
+ 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 if (c >= 'A')
+ digit = c - 'A' + 10;
+ }
+ else
+ break;
+ db_tok_number = db_tok_number * r + digit;
+ c = db_read_char();
+ }
+ if ((c >= '0' && c <= '9') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= 'a' && c <= 'z') ||
+ (c == '_'))
+ {
+ db_error("Bad character in number\n");
+ db_flush_lex();
+ return (tEOF);
+ }
+ db_unread_char(c);
+ return (tNUMBER);
+ }
+ if ((c >= 'A' && c <= 'Z') ||
+ (c >= 'a' && c <= 'z') ||
+ c == '_' || c == '\\')
+ {
+ /* string */
+ char *cp;
+
+ cp = db_tok_string;
+ if (c == '\\') {
+ c = db_read_char();
+ if (c == '\n' || c == -1)
+ db_error("Bad escape\n");
+ }
+ *cp++ = 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 escape\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);
+ }
+
+ switch (c) {
+ case '+':
+ return (tPLUS);
+ case '-':
+ return (tMINUS);
+ case '.':
+ c = db_read_char();
+ if (c == '.')
+ return (tDOTDOT);
+ db_unread_char(c);
+ return (tDOT);
+ case '*':
+ return (tSTAR);
+ case '/':
+ return (tSLASH);
+ case '=':
+ return (tEQ);
+ case '%':
+ return (tPCT);
+ case '#':
+ return (tHASH);
+ case '(':
+ return (tLPAREN);
+ case ')':
+ return (tRPAREN);
+ case ',':
+ return (tCOMMA);
+ case '"':
+ return (tDITTO);
+ case '$':
+ return (tDOLLAR);
+ case '!':
+ return (tEXCL);
+ case ';':
+ return (tSEMI);
+ case '<':
+ c = db_read_char();
+ if (c == '<')
+ return (tSHIFT_L);
+ db_unread_char(c);
+ break;
+ case '>':
+ c = db_read_char();
+ if (c == '>')
+ return (tSHIFT_R);
+ db_unread_char(c);
+ break;
+ case -1:
+ return (tEOF);
+ }
+ db_printf("Bad character\n");
+ db_flush_lex();
+ return (tEOF);
+}
diff --git a/sys/ddb/db_lex.h b/sys/ddb/db_lex.h
new file mode 100644
index 0000000..8713a27
--- /dev/null
+++ b/sys/ddb/db_lex.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
+ * 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 the
+ * rights to redistribute these changes.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _DDB_DB_LEX_H_
+#define _DDB_DB_LEX_H_
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+/*
+ * Lexical analyzer.
+ */
+void db_flush_lex(void);
+char *db_get_line(void);
+void db_inject_line(const char *command);
+int db_read_line(void);
+int db_read_token(void);
+void db_unread_token(int t);
+
+extern db_expr_t db_tok_number;
+#define TOK_STRING_SIZE 120
+extern char db_tok_string[TOK_STRING_SIZE];
+
+#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 tDITTO 15
+#define tDOLLAR 16
+#define tEXCL 17
+#define tSHIFT_L 18
+#define tSHIFT_R 19
+#define tDOTDOT 20
+#define tSEMI 21
+
+#endif /* !_DDB_DB_LEX_H_ */
diff --git a/sys/ddb/db_main.c b/sys/ddb/db_main.c
new file mode 100644
index 0000000..6e9286c
--- /dev/null
+++ b/sys/ddb/db_main.c
@@ -0,0 +1,262 @@
+/*-
+ * 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
+ * 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 the
+ * rights to redistribute these changes.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/cons.h>
+#include <sys/linker.h>
+#include <sys/kdb.h>
+#include <sys/kernel.h>
+#include <sys/pcpu.h>
+#include <sys/proc.h>
+#include <sys/reboot.h>
+#include <sys/sysctl.h>
+
+#include <machine/kdb.h>
+#include <machine/pcb.h>
+#include <machine/setjmp.h>
+
+#include <ddb/ddb.h>
+#include <ddb/db_command.h>
+#include <ddb/db_sym.h>
+
+SYSCTL_NODE(_debug, OID_AUTO, ddb, CTLFLAG_RW, 0, "DDB settings");
+
+static dbbe_init_f db_init;
+static dbbe_trap_f db_trap;
+static dbbe_trace_f db_trace_self_wrapper;
+static dbbe_trace_thread_f db_trace_thread_wrapper;
+
+KDB_BACKEND(ddb, db_init, db_trace_self_wrapper, db_trace_thread_wrapper,
+ db_trap);
+
+vm_offset_t ksym_start, ksym_end;
+
+boolean_t
+X_db_line_at_pc(db_symtab_t *symtab, c_db_sym_t sym, char **file, int *line,
+ db_expr_t off)
+{
+ return (FALSE);
+}
+
+c_db_sym_t
+X_db_lookup(db_symtab_t *symtab, const char *symbol)
+{
+ c_linker_sym_t lsym;
+ Elf_Sym *sym;
+
+ if (symtab->private == NULL) {
+ return ((c_db_sym_t)((!linker_ddb_lookup(symbol, &lsym))
+ ? lsym : NULL));
+ } else {
+ sym = (Elf_Sym *)symtab->start;
+ while ((char *)sym < symtab->end) {
+ if (sym->st_name != 0 &&
+ !strcmp(symtab->private + sym->st_name, symbol))
+ return ((c_db_sym_t)sym);
+ sym++;
+ }
+ }
+ return (NULL);
+}
+
+c_db_sym_t
+X_db_search_symbol(db_symtab_t *symtab, db_addr_t off, db_strategy_t strat,
+ db_expr_t *diffp)
+{
+ c_linker_sym_t lsym;
+ Elf_Sym *sym, *match;
+ unsigned long diff;
+
+ if (symtab->private == NULL) {
+ if (!linker_ddb_search_symbol((caddr_t)off, &lsym, &diff)) {
+ *diffp = (db_expr_t)diff;
+ return ((c_db_sym_t)lsym);
+ }
+ return (NULL);
+ }
+
+ diff = ~0UL;
+ match = NULL;
+ for (sym = (Elf_Sym*)symtab->start; (char*)sym < symtab->end; sym++) {
+ if (sym->st_name == 0)
+ continue;
+ if (off < sym->st_value)
+ continue;
+ if (ELF_ST_TYPE(sym->st_info) != STT_OBJECT &&
+ ELF_ST_TYPE(sym->st_info) != STT_FUNC &&
+ ELF_ST_TYPE(sym->st_info) != STT_NOTYPE)
+ continue;
+ if ((off - sym->st_value) > diff)
+ continue;
+ if ((off - sym->st_value) < diff) {
+ diff = off - sym->st_value;
+ match = sym;
+ } else {
+ if (match == NULL)
+ match = sym;
+ else if (ELF_ST_BIND(match->st_info) == STB_LOCAL &&
+ ELF_ST_BIND(sym->st_info) != STB_LOCAL)
+ match = sym;
+ }
+ if (diff == 0) {
+ if (strat == DB_STGY_PROC &&
+ ELF_ST_TYPE(sym->st_info) == STT_FUNC &&
+ ELF_ST_BIND(sym->st_info) != STB_LOCAL)
+ break;
+ if (strat == DB_STGY_ANY &&
+ ELF_ST_BIND(sym->st_info) != STB_LOCAL)
+ break;
+ }
+ }
+
+ *diffp = (match == NULL) ? off : diff;
+ return ((c_db_sym_t)match);
+}
+
+boolean_t
+X_db_sym_numargs(db_symtab_t *symtab, c_db_sym_t sym, int *nargp,
+ char **argp)
+{
+ return (FALSE);
+}
+
+void
+X_db_symbol_values(db_symtab_t *symtab, c_db_sym_t sym, const char **namep,
+ db_expr_t *valp)
+{
+ linker_symval_t lval;
+
+ if (symtab->private == NULL) {
+ linker_ddb_symbol_values((c_linker_sym_t)sym, &lval);
+ if (namep != NULL)
+ *namep = (const char*)lval.name;
+ if (valp != NULL)
+ *valp = (db_expr_t)lval.value;
+ } else {
+ if (namep != NULL)
+ *namep = (const char *)symtab->private +
+ ((const Elf_Sym *)sym)->st_name;
+ if (valp != NULL)
+ *valp = (db_expr_t)((const Elf_Sym *)sym)->st_value;
+ }
+}
+
+static int
+db_init(void)
+{
+ uintptr_t symtab, strtab;
+ Elf_Size tabsz, strsz;
+
+ db_command_init();
+ if (ksym_end > ksym_start && ksym_start != 0) {
+ symtab = ksym_start;
+ tabsz = *((Elf_Size*)symtab);
+ symtab += sizeof(Elf_Size);
+ strtab = symtab + tabsz;
+ strsz = *((Elf_Size*)strtab);
+ strtab += sizeof(Elf_Size);
+ if (strtab + strsz <= ksym_end) {
+ db_add_symbol_table((char *)symtab,
+ (char *)(symtab + tabsz), "elf", (char *)strtab);
+ }
+ }
+ db_add_symbol_table(NULL, NULL, "kld", NULL);
+ return (1); /* We're the default debugger. */
+}
+
+static int
+db_trap(int type, int code)
+{
+ jmp_buf jb;
+ void *prev_jb;
+ boolean_t bkpt, watchpt;
+ const char *why;
+
+ /*
+ * Don't handle the trap if the console is unavailable (i.e. it
+ * is in graphics mode).
+ */
+ if (cnunavailable())
+ return (0);
+
+ bkpt = IS_BREAKPOINT_TRAP(type, code);
+ watchpt = IS_WATCHPOINT_TRAP(type, code);
+
+ if (db_stop_at_pc(&bkpt)) {
+ if (db_inst_count) {
+ db_printf("After %d instructions (%d loads, %d stores),\n",
+ db_inst_count, db_load_count, db_store_count);
+ }
+ prev_jb = kdb_jmpbuf(jb);
+ if (setjmp(jb) == 0) {
+ db_dot = PC_REGS();
+ db_print_thread();
+ if (bkpt)
+ db_printf("Breakpoint at\t");
+ else if (watchpt)
+ db_printf("Watchpoint at\t");
+ else
+ db_printf("Stopped at\t");
+ db_print_loc_and_inst(db_dot);
+ }
+ why = kdb_why;
+ db_script_kdbenter(why != KDB_WHY_UNSET ? why : "unknown");
+ db_command_loop();
+ (void)kdb_jmpbuf(prev_jb);
+ }
+
+ db_restart_at_pc(watchpt);
+
+ return (1);
+}
+
+static void
+db_trace_self_wrapper(void)
+{
+ jmp_buf jb;
+ void *prev_jb;
+
+ prev_jb = kdb_jmpbuf(jb);
+ if (setjmp(jb) == 0)
+ db_trace_self();
+ (void)kdb_jmpbuf(prev_jb);
+}
+
+static void
+db_trace_thread_wrapper(struct thread *td)
+{
+ jmp_buf jb;
+ void *prev_jb;
+
+ prev_jb = kdb_jmpbuf(jb);
+ if (setjmp(jb) == 0)
+ db_trace_thread(td, -1);
+ (void)kdb_jmpbuf(prev_jb);
+}
diff --git a/sys/ddb/db_output.c b/sys/ddb/db_output.c
new file mode 100644
index 0000000..8390a86
--- /dev/null
+++ b/sys/ddb/db_output.c
@@ -0,0 +1,395 @@
+/*-
+ * 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
+ * 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 the
+ * rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+
+/*
+ * Printf and character output for debugger.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_ddb.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/cons.h>
+#include <sys/kdb.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+
+#include <machine/stdarg.h>
+
+#include <ddb/ddb.h>
+#include <ddb/db_output.h>
+
+struct dbputchar_arg {
+ size_t da_nbufr;
+ size_t da_remain;
+ char *da_pbufr;
+ char *da_pnext;
+};
+
+/*
+ * 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.
+ */
+static int db_output_position = 0; /* output column */
+static int db_last_non_space = 0; /* last non-space character */
+db_expr_t 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)
+db_expr_t db_max_width = 79; /* output line width */
+db_expr_t db_lines_per_page = 20; /* lines per page */
+volatile int db_pager_quit; /* user requested quit */
+static int db_newlines; /* # lines this page */
+static int db_maxlines; /* max lines/page when paging */
+static int ddb_use_printf = 0;
+SYSCTL_INT(_debug, OID_AUTO, ddb_use_printf, CTLFLAG_RW, &ddb_use_printf, 0,
+ "use printf for all ddb output");
+
+static void db_putc(int c);
+static void db_puts(const char *str);
+static void db_putchar(int c, void *arg);
+static void db_pager(void);
+
+/*
+ * 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) {
+ while (last_print < next_tab) { /* DON'T send a tab!!! */
+ cnputc(' ');
+ db_capture_writech(' ');
+ last_print++;
+ }
+ }
+ else {
+ cnputc(' ');
+ db_capture_writech(' ');
+ last_print++;
+ }
+ }
+ db_last_non_space = db_output_position;
+}
+
+/*
+ * Output character. Buffer whitespace.
+ */
+static void
+db_putchar(int c, void *arg)
+{
+ struct dbputchar_arg *dap = arg;
+
+ if (dap->da_pbufr == NULL) {
+
+ /* No bufferized output is provided. */
+ db_putc(c);
+ } else {
+
+ *dap->da_pnext++ = c;
+ dap->da_remain--;
+
+ /* Leave always the buffer 0 terminated. */
+ *dap->da_pnext = '\0';
+
+ /* Check if the buffer needs to be flushed. */
+ if (dap->da_remain < 2 || c == '\n') {
+ db_puts(dap->da_pbufr);
+ dap->da_pnext = dap->da_pbufr;
+ dap->da_remain = dap->da_nbufr;
+ *dap->da_pnext = '\0';
+ }
+ }
+}
+
+static void
+db_putc(int c)
+{
+
+ /*
+ * If not in the debugger or the user requests it, output data to
+ * both the console and the message buffer.
+ */
+ if (!kdb_active || ddb_use_printf) {
+ printf("%c", c);
+ if (!kdb_active)
+ return;
+ if (c == '\r' || c == '\n')
+ db_check_interrupt();
+ if (c == '\n' && db_maxlines > 0) {
+ db_newlines++;
+ if (db_newlines >= db_maxlines)
+ db_pager();
+ }
+ return;
+ }
+
+ /* Otherwise, output data directly to the console. */
+ if (c > ' ' && c <= '~') {
+ /*
+ * Printing character.
+ * If we have spaces to print, print them first.
+ * Use tabs if possible.
+ */
+ db_force_whitespace();
+ cnputc(c);
+ db_capture_writech(c);
+ db_output_position++;
+ db_last_non_space = db_output_position;
+ }
+ else if (c == '\n') {
+ /* Newline */
+ cnputc(c);
+ db_capture_writech(c);
+ db_output_position = 0;
+ db_last_non_space = 0;
+ db_check_interrupt();
+ if (db_maxlines > 0) {
+ db_newlines++;
+ if (db_newlines >= db_maxlines)
+ db_pager();
+ }
+ }
+ else if (c == '\r') {
+ /* Return */
+ cnputc(c);
+ db_capture_writech(c);
+ db_output_position = 0;
+ db_last_non_space = 0;
+ 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);
+ /* No need to beep in a log: db_capture_writech(c); */
+ }
+ /* other characters are assumed non-printing */
+}
+
+static void
+db_puts(const char *str)
+{
+ int i;
+
+ for (i = 0; str[i] != '\0'; i++)
+ db_putc(str[i]);
+}
+
+/*
+ * Turn on the pager.
+ */
+void
+db_enable_pager(void)
+{
+ if (db_maxlines == 0) {
+ db_maxlines = db_lines_per_page;
+ db_newlines = 0;
+ db_pager_quit = 0;
+ }
+}
+
+/*
+ * Turn off the pager.
+ */
+void
+db_disable_pager(void)
+{
+ db_maxlines = 0;
+}
+
+/*
+ * A simple paging callout function. It supports several simple more(1)-like
+ * commands as well as a quit command that sets db_pager_quit which db
+ * commands can poll to see if they should terminate early.
+ */
+void
+db_pager(void)
+{
+ int c, done;
+
+ db_capture_enterpager();
+ db_printf("--More--\r");
+ done = 0;
+ while (!done) {
+ c = cngetc();
+ switch (c) {
+ case 'e':
+ case 'j':
+ case '\n':
+ /* Just one more line. */
+ db_maxlines = 1;
+ done++;
+ break;
+ case 'd':
+ /* Half a page. */
+ db_maxlines = db_lines_per_page / 2;
+ done++;
+ break;
+ case 'f':
+ case ' ':
+ /* Another page. */
+ db_maxlines = db_lines_per_page;
+ done++;
+ break;
+ case 'q':
+ case 'Q':
+ case 'x':
+ case 'X':
+ /* Quit */
+ db_maxlines = 0;
+ db_pager_quit = 1;
+ done++;
+ break;
+#if 0
+ /* FALLTHROUGH */
+ default:
+ cnputc('\007');
+#endif
+ }
+ }
+ db_printf(" ");
+ db_force_whitespace();
+ db_printf("\r");
+ db_newlines = 0;
+ db_capture_exitpager();
+}
+
+/*
+ * Return output position
+ */
+int
+db_print_position()
+{
+ return (db_output_position);
+}
+
+/*
+ * Printing
+ */
+int
+db_printf(const char *fmt, ...)
+{
+#ifdef DDB_BUFR_SIZE
+ char bufr[DDB_BUFR_SIZE];
+#endif
+ struct dbputchar_arg dca;
+ va_list listp;
+ int retval;
+
+#ifdef DDB_BUFR_SIZE
+ dca.da_pbufr = bufr;
+ dca.da_pnext = dca.da_pbufr;
+ dca.da_nbufr = sizeof(bufr);
+ dca.da_remain = sizeof(bufr);
+ *dca.da_pnext = '\0';
+#else
+ dca.da_pbufr = NULL;
+#endif
+
+ va_start(listp, fmt);
+ retval = kvprintf (fmt, db_putchar, &dca, db_radix, listp);
+ va_end(listp);
+
+#ifdef DDB_BUFR_SIZE
+ if (*dca.da_pbufr != '\0')
+ db_puts(dca.da_pbufr);
+#endif
+ return (retval);
+}
+
+int db_indent;
+
+void
+db_iprintf(const char *fmt,...)
+{
+#ifdef DDB_BUFR_SIZE
+ char bufr[DDB_BUFR_SIZE];
+#endif
+ struct dbputchar_arg dca;
+ register int i;
+ va_list listp;
+
+ for (i = db_indent; i >= 8; i -= 8)
+ db_printf("\t");
+ while (--i >= 0)
+ db_printf(" ");
+
+#ifdef DDB_BUFR_SIZE
+ dca.da_pbufr = bufr;
+ dca.da_pnext = dca.da_pbufr;
+ dca.da_nbufr = sizeof(bufr);
+ dca.da_remain = sizeof(bufr);
+ *dca.da_pnext = '\0';
+#else
+ dca.da_pbufr = NULL;
+#endif
+
+ va_start(listp, fmt);
+ kvprintf (fmt, db_putchar, &dca, db_radix, listp);
+ va_end(listp);
+
+#ifdef DDB_BUFR_SIZE
+ if (*dca.da_pbufr != '\0')
+ db_puts(dca.da_pbufr);
+#endif
+}
+
+/*
+ * End line if too long.
+ */
+void
+db_end_line(int field_width)
+{
+ if (db_output_position + field_width > db_max_width)
+ db_printf("\n");
+}
diff --git a/sys/ddb/db_output.h b/sys/ddb/db_output.h
new file mode 100644
index 0000000..cbf9f0e
--- /dev/null
+++ b/sys/ddb/db_output.h
@@ -0,0 +1,47 @@
+/*-
+ * 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
+ * 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 the
+ * rights to redistribute these changes.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _DDB_DB_OUTPUT_H_
+#define _DDB_DB_OUTPUT_H_
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 8/90
+ */
+
+/*
+ * Printing routines for kernel debugger.
+ */
+
+void db_disable_pager(void);
+void db_enable_pager(void);
+void db_end_line(int);
+void db_force_whitespace(void);
+int db_print_position(void);
+
+#endif /* !_DDB_DB_OUTPUT_H_ */
diff --git a/sys/ddb/db_print.c b/sys/ddb/db_print.c
new file mode 100644
index 0000000..6cffd6d
--- /dev/null
+++ b/sys/ddb/db_print.c
@@ -0,0 +1,70 @@
+/*-
+ * 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
+ * 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 the
+ * rights to redistribute these changes.
+ *
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+
+/*
+ * Miscellaneous printing.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kdb.h>
+#include <sys/proc.h>
+
+#include <machine/pcb.h>
+
+#include <ddb/ddb.h>
+#include <ddb/db_variables.h>
+#include <ddb/db_sym.h>
+
+void
+db_show_regs(db_expr_t _1, boolean_t _2, db_expr_t _3, char *_4)
+{
+ struct db_variable *regp;
+ db_expr_t value, offset;
+ const char *name;
+
+ for (regp = db_regs; regp < db_eregs; regp++) {
+ if (!db_read_variable(regp, &value))
+ continue;
+ db_printf("%-12s%#10lr", regp->name, (unsigned long)value);
+ db_find_xtrn_sym_and_offset((db_addr_t)value, &name, &offset);
+ if (name != NULL && offset <= (unsigned long)db_maxoff &&
+ offset != value) {
+ db_printf("\t%s", name);
+ if (offset != 0)
+ db_printf("+%+#lr", (long)offset);
+ }
+ db_printf("\n");
+ }
+ db_print_loc_and_inst(PC_REGS());
+}
diff --git a/sys/ddb/db_ps.c b/sys/ddb/db_ps.c
new file mode 100644
index 0000000..81e141e
--- /dev/null
+++ b/sys/ddb/db_ps.c
@@ -0,0 +1,468 @@
+/*-
+ * Copyright (c) 1993 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.
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/cons.h>
+#include <sys/jail.h>
+#include <sys/kdb.h>
+#include <sys/proc.h>
+#include <sys/sysent.h>
+#include <sys/systm.h>
+#include <sys/_kstack_cache.h>
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+
+#include <ddb/ddb.h>
+
+static void dumpthread(volatile struct proc *p, volatile struct thread *td,
+ int all);
+/*
+ * At least one non-optional show-command must be implemented using
+ * DB_SHOW_ALL_COMMAND() so that db_show_all_cmd_set gets created.
+ * Here is one.
+ */
+DB_SHOW_ALL_COMMAND(procs, db_procs_cmd)
+{
+ db_ps(addr, have_addr, count, modif);
+}
+
+/*
+ * Layout:
+ * - column counts
+ * - header
+ * - single-threaded process
+ * - multi-threaded process
+ * - thread in a MT process
+ *
+ * 1 2 3 4 5 6 7
+ * 1234567890123456789012345678901234567890123456789012345678901234567890
+ * pid ppid pgrp uid state wmesg wchan cmd
+ * <pid> <ppi> <pgi> <uid> <stat> < wmesg > < wchan > <name>
+ * <pid> <ppi> <pgi> <uid> <stat> (threaded) <command>
+ * <tid > <stat> < wmesg > < wchan > <name>
+ *
+ * For machines with 64-bit pointers, we expand the wchan field 8 more
+ * characters.
+ */
+void
+db_ps(db_expr_t addr, boolean_t hasaddr, db_expr_t count, char *modif)
+{
+ volatile struct proc *p, *pp;
+ volatile struct thread *td;
+ struct ucred *cred;
+ struct pgrp *pgrp;
+ char state[9];
+ int np, rflag, sflag, dflag, lflag, wflag;
+
+ np = nprocs;
+
+ if (!LIST_EMPTY(&allproc))
+ p = LIST_FIRST(&allproc);
+ else
+ p = &proc0;
+
+#ifdef __LP64__
+ db_printf(" pid ppid pgrp uid state wmesg wchan cmd\n");
+#else
+ db_printf(" pid ppid pgrp uid state wmesg wchan cmd\n");
+#endif
+ while (--np >= 0 && !db_pager_quit) {
+ if (p == NULL) {
+ db_printf("oops, ran out of processes early!\n");
+ break;
+ }
+ pp = p->p_pptr;
+ if (pp == NULL)
+ pp = p;
+
+ cred = p->p_ucred;
+ pgrp = p->p_pgrp;
+ db_printf("%5d %5d %5d %5d ", p->p_pid, pp->p_pid,
+ pgrp != NULL ? pgrp->pg_id : 0,
+ cred != NULL ? cred->cr_ruid : 0);
+
+ /* Determine our primary process state. */
+ switch (p->p_state) {
+ case PRS_NORMAL:
+ if (P_SHOULDSTOP(p))
+ state[0] = 'T';
+ else {
+ /*
+ * One of D, L, R, S, W. For a
+ * multithreaded process we will use
+ * the state of the thread with the
+ * highest precedence. The
+ * precendence order from high to low
+ * is R, L, D, S, W. If no thread is
+ * in a sane state we use '?' for our
+ * primary state.
+ */
+ rflag = sflag = dflag = lflag = wflag = 0;
+ FOREACH_THREAD_IN_PROC(p, td) {
+ if (td->td_state == TDS_RUNNING ||
+ td->td_state == TDS_RUNQ ||
+ td->td_state == TDS_CAN_RUN)
+ rflag++;
+ if (TD_ON_LOCK(td))
+ lflag++;
+ if (TD_IS_SLEEPING(td)) {
+ if (!(td->td_flags & TDF_SINTR))
+ dflag++;
+ else
+ sflag++;
+ }
+ if (TD_AWAITING_INTR(td))
+ wflag++;
+ }
+ if (rflag)
+ state[0] = 'R';
+ else if (lflag)
+ state[0] = 'L';
+ else if (dflag)
+ state[0] = 'D';
+ else if (sflag)
+ state[0] = 'S';
+ else if (wflag)
+ state[0] = 'W';
+ else
+ state[0] = '?';
+ }
+ break;
+ case PRS_NEW:
+ state[0] = 'N';
+ break;
+ case PRS_ZOMBIE:
+ state[0] = 'Z';
+ break;
+ default:
+ state[0] = 'U';
+ break;
+ }
+ state[1] = '\0';
+
+ /* Additional process state flags. */
+ if (!(p->p_flag & P_INMEM))
+ strlcat(state, "W", sizeof(state));
+ if (p->p_flag & P_TRACED)
+ strlcat(state, "X", sizeof(state));
+ if (p->p_flag & P_WEXIT && p->p_state != PRS_ZOMBIE)
+ strlcat(state, "E", sizeof(state));
+ if (p->p_flag & P_PPWAIT)
+ strlcat(state, "V", sizeof(state));
+ if (p->p_flag & P_SYSTEM || p->p_lock > 0)
+ strlcat(state, "L", sizeof(state));
+ if (p->p_session != NULL && SESS_LEADER(p))
+ strlcat(state, "s", sizeof(state));
+ /* Cheated here and didn't compare pgid's. */
+ if (p->p_flag & P_CONTROLT)
+ strlcat(state, "+", sizeof(state));
+ if (cred != NULL && jailed(cred))
+ strlcat(state, "J", sizeof(state));
+ db_printf(" %-6.6s ", state);
+ if (p->p_flag & P_HADTHREADS) {
+#ifdef __LP64__
+ db_printf(" (threaded) ");
+#else
+ db_printf(" (threaded) ");
+#endif
+ if (p->p_flag & P_SYSTEM)
+ db_printf("[");
+ db_printf("%s", p->p_comm);
+ if (p->p_flag & P_SYSTEM)
+ db_printf("]");
+ db_printf("\n");
+ }
+ FOREACH_THREAD_IN_PROC(p, td) {
+ dumpthread(p, td, p->p_flag & P_HADTHREADS);
+ if (db_pager_quit)
+ break;
+ }
+
+ p = LIST_NEXT(p, p_list);
+ if (p == NULL && np > 0)
+ p = LIST_FIRST(&zombproc);
+ }
+}
+
+static void
+dumpthread(volatile struct proc *p, volatile struct thread *td, int all)
+{
+ char state[9], wprefix;
+ const char *wmesg;
+ void *wchan;
+
+ if (all) {
+ db_printf("%6d ", td->td_tid);
+ switch (td->td_state) {
+ case TDS_RUNNING:
+ snprintf(state, sizeof(state), "Run");
+ break;
+ case TDS_RUNQ:
+ snprintf(state, sizeof(state), "RunQ");
+ break;
+ case TDS_CAN_RUN:
+ snprintf(state, sizeof(state), "CanRun");
+ break;
+ case TDS_INACTIVE:
+ snprintf(state, sizeof(state), "Inactv");
+ break;
+ case TDS_INHIBITED:
+ state[0] = '\0';
+ if (TD_ON_LOCK(td))
+ strlcat(state, "L", sizeof(state));
+ if (TD_IS_SLEEPING(td)) {
+ if (td->td_flags & TDF_SINTR)
+ strlcat(state, "S", sizeof(state));
+ else
+ strlcat(state, "D", sizeof(state));
+ }
+ if (TD_IS_SWAPPED(td))
+ strlcat(state, "W", sizeof(state));
+ if (TD_AWAITING_INTR(td))
+ strlcat(state, "I", sizeof(state));
+ if (TD_IS_SUSPENDED(td))
+ strlcat(state, "s", sizeof(state));
+ if (state[0] != '\0')
+ break;
+ default:
+ snprintf(state, sizeof(state), "???");
+ }
+ db_printf(" %-6.6s ", state);
+ }
+ wprefix = ' ';
+ if (TD_ON_LOCK(td)) {
+ wprefix = '*';
+ wmesg = td->td_lockname;
+ wchan = td->td_blocked;
+ } else if (TD_ON_SLEEPQ(td)) {
+ wmesg = td->td_wmesg;
+ wchan = td->td_wchan;
+ } else if (TD_IS_RUNNING(td)) {
+ snprintf(state, sizeof(state), "CPU %d", td->td_oncpu);
+ wmesg = state;
+ wchan = NULL;
+ } else {
+ wmesg = "";
+ wchan = NULL;
+ }
+ db_printf("%c%-8.8s ", wprefix, wmesg);
+ if (wchan == NULL)
+#ifdef __LP64__
+ db_printf("%18s ", "");
+#else
+ db_printf("%10s ", "");
+#endif
+ else
+ db_printf("%p ", wchan);
+ if (p->p_flag & P_SYSTEM)
+ db_printf("[");
+ if (td->td_name[0] != '\0')
+ db_printf("%s", td->td_name);
+ else
+ db_printf("%s", td->td_proc->p_comm);
+ if (p->p_flag & P_SYSTEM)
+ db_printf("]");
+ db_printf("\n");
+}
+
+DB_SHOW_COMMAND(thread, db_show_thread)
+{
+ struct thread *td;
+ struct lock_object *lock;
+ boolean_t comma;
+
+ /* Determine which thread to examine. */
+ if (have_addr)
+ td = db_lookup_thread(addr, FALSE);
+ else
+ td = kdb_thread;
+ lock = (struct lock_object *)td->td_lock;
+
+ db_printf("Thread %d at %p:\n", td->td_tid, td);
+ db_printf(" proc (pid %d): %p\n", td->td_proc->p_pid, td->td_proc);
+ if (td->td_name[0] != '\0')
+ db_printf(" name: %s\n", td->td_name);
+ db_printf(" stack: %p-%p\n", (void *)td->td_kstack,
+ (void *)(td->td_kstack + td->td_kstack_pages * PAGE_SIZE - 1));
+ db_printf(" flags: %#x ", td->td_flags);
+ db_printf(" pflags: %#x\n", td->td_pflags);
+ db_printf(" state: ");
+ switch (td->td_state) {
+ case TDS_INACTIVE:
+ db_printf("INACTIVE\n");
+ break;
+ case TDS_CAN_RUN:
+ db_printf("CAN RUN\n");
+ break;
+ case TDS_RUNQ:
+ db_printf("RUNQ\n");
+ break;
+ case TDS_RUNNING:
+ db_printf("RUNNING (CPU %d)\n", td->td_oncpu);
+ break;
+ case TDS_INHIBITED:
+ db_printf("INHIBITED: {");
+ comma = FALSE;
+ if (TD_IS_SLEEPING(td)) {
+ db_printf("SLEEPING");
+ comma = TRUE;
+ }
+ if (TD_IS_SUSPENDED(td)) {
+ if (comma)
+ db_printf(", ");
+ db_printf("SUSPENDED");
+ comma = TRUE;
+ }
+ if (TD_IS_SWAPPED(td)) {
+ if (comma)
+ db_printf(", ");
+ db_printf("SWAPPED");
+ comma = TRUE;
+ }
+ if (TD_ON_LOCK(td)) {
+ if (comma)
+ db_printf(", ");
+ db_printf("LOCK");
+ comma = TRUE;
+ }
+ if (TD_AWAITING_INTR(td)) {
+ if (comma)
+ db_printf(", ");
+ db_printf("IWAIT");
+ }
+ db_printf("}\n");
+ break;
+ default:
+ db_printf("??? (%#x)\n", td->td_state);
+ break;
+ }
+ if (TD_ON_LOCK(td))
+ db_printf(" lock: %s turnstile: %p\n", td->td_lockname,
+ td->td_blocked);
+ if (TD_ON_SLEEPQ(td))
+ db_printf(" wmesg: %s wchan: %p\n", td->td_wmesg,
+ td->td_wchan);
+ db_printf(" priority: %d\n", td->td_priority);
+ db_printf(" container lock: %s (%p)\n", lock->lo_name, lock);
+}
+
+DB_SHOW_COMMAND(proc, db_show_proc)
+{
+ struct thread *td;
+ struct proc *p;
+ int i;
+
+ /* Determine which process to examine. */
+ if (have_addr)
+ p = db_lookup_proc(addr);
+ else
+ p = kdb_thread->td_proc;
+
+ db_printf("Process %d (%s) at %p:\n", p->p_pid, p->p_comm, p);
+ db_printf(" state: ");
+ switch (p->p_state) {
+ case PRS_NEW:
+ db_printf("NEW\n");
+ break;
+ case PRS_NORMAL:
+ db_printf("NORMAL\n");
+ break;
+ case PRS_ZOMBIE:
+ db_printf("ZOMBIE\n");
+ break;
+ default:
+ db_printf("??? (%#x)\n", p->p_state);
+ }
+ if (p->p_ucred != NULL) {
+ db_printf(" uid: %d gids: ", p->p_ucred->cr_uid);
+ for (i = 0; i < p->p_ucred->cr_ngroups; i++) {
+ db_printf("%d", p->p_ucred->cr_groups[i]);
+ if (i < (p->p_ucred->cr_ngroups - 1))
+ db_printf(", ");
+ }
+ db_printf("\n");
+ }
+ if (p->p_pptr != NULL)
+ db_printf(" parent: pid %d at %p\n", p->p_pptr->p_pid,
+ p->p_pptr);
+ if (p->p_leader != NULL && p->p_leader != p)
+ db_printf(" leader: pid %d at %p\n", p->p_leader->p_pid,
+ p->p_leader);
+ if (p->p_sysent != NULL)
+ db_printf(" ABI: %s\n", p->p_sysent->sv_name);
+ if (p->p_args != NULL)
+ db_printf(" arguments: %.*s\n", (int)p->p_args->ar_length,
+ p->p_args->ar_args);
+ db_printf(" threads: %d\n", p->p_numthreads);
+ FOREACH_THREAD_IN_PROC(p, td) {
+ dumpthread(p, td, 1);
+ if (db_pager_quit)
+ break;
+ }
+}
+
+void
+db_findstack_cmd(db_expr_t addr, boolean_t have_addr,
+ db_expr_t dummy3 __unused, char *dummy4 __unused)
+{
+ struct proc *p;
+ struct thread *td;
+ struct kstack_cache_entry *ks_ce;
+ vm_offset_t saddr;
+
+ if (have_addr)
+ saddr = addr;
+ else {
+ db_printf("Usage: findstack <address>\n");
+ return;
+ }
+
+ FOREACH_PROC_IN_SYSTEM(p) {
+ FOREACH_THREAD_IN_PROC(p, td) {
+ if (td->td_kstack <= saddr && saddr < td->td_kstack +
+ PAGE_SIZE * td->td_kstack_pages) {
+ db_printf("Thread %p\n", td);
+ return;
+ }
+ }
+ }
+
+ for (ks_ce = kstack_cache; ks_ce != NULL;
+ ks_ce = ks_ce->next_ks_entry) {
+ if ((vm_offset_t)ks_ce <= saddr && saddr < (vm_offset_t)ks_ce +
+ PAGE_SIZE * KSTACK_PAGES) {
+ db_printf("Cached stack %p\n", ks_ce);
+ return;
+ }
+ }
+}
diff --git a/sys/ddb/db_run.c b/sys/ddb/db_run.c
new file mode 100644
index 0000000..f0b31bf
--- /dev/null
+++ b/sys/ddb/db_run.c
@@ -0,0 +1,392 @@
+/*-
+ * 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
+ * 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 the
+ * rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+
+/*
+ * Commands to run process.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kdb.h>
+#include <sys/proc.h>
+
+#include <machine/kdb.h>
+#include <machine/pcb.h>
+
+#include <vm/vm.h>
+
+#include <ddb/ddb.h>
+#include <ddb/db_break.h>
+#include <ddb/db_access.h>
+
+static int db_run_mode;
+#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
+
+static boolean_t db_sstep_print;
+static int db_loop_count;
+static int db_call_depth;
+
+int db_inst_count;
+int db_load_count;
+int db_store_count;
+
+#ifndef db_set_single_step
+void db_set_single_step(void);
+#endif
+#ifndef db_clear_single_step
+void db_clear_single_step(void);
+#endif
+
+#ifdef SOFTWARE_SSTEP
+db_breakpoint_t db_not_taken_bkpt = 0;
+db_breakpoint_t db_taken_bkpt = 0;
+#endif
+
+boolean_t
+db_stop_at_pc(is_breakpoint)
+ boolean_t *is_breakpoint;
+{
+ register db_addr_t pc;
+ register db_breakpoint_t bkpt;
+
+ pc = PC_REGS();
+#ifdef SOFTWARE_SSTEP
+ if ((db_not_taken_bkpt != 0 && pc == db_not_taken_bkpt->address)
+ || (db_taken_bkpt != 0 && pc == db_taken_bkpt->address))
+ *is_breakpoint = FALSE;
+#endif
+
+ db_clear_single_step();
+ db_clear_breakpoints();
+ db_clear_watchpoints();
+
+#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();
+ }
+#endif
+
+ /*
+ * Now check for a breakpoint at this address.
+ */
+ bkpt = db_find_breakpoint_here(pc);
+ if (bkpt) {
+ if (--bkpt->count == 0) {
+ bkpt->count = bkpt->init_count;
+ *is_breakpoint = TRUE;
+ return (TRUE); /* stop here */
+ }
+ } else if (*is_breakpoint) {
+#ifdef BKPT_SKIP
+ BKPT_SKIP;
+#endif
+ }
+
+ *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_printf("\t\t");
+ db_print_loc_and_inst(pc);
+ db_printf("\n");
+ }
+ return (FALSE); /* continue */
+ }
+ }
+ if (db_run_mode == STEP_RETURN) {
+ /* continue until matching return */
+ db_expr_t ins;
+
+ ins = db_get_value(pc, sizeof(int), FALSE);
+ 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] ", db_inst_count);
+ for (i = db_call_depth; --i > 0; )
+ db_printf(" ");
+ db_print_loc_and_inst(pc);
+ db_printf("\n");
+ }
+ }
+ if (inst_call(ins))
+ db_call_depth++;
+ return (FALSE); /* continue */
+ }
+ }
+ if (db_run_mode == STEP_CALLT) {
+ /* continue until call or return */
+ db_expr_t ins;
+
+ ins = db_get_value(pc, sizeof(int), FALSE);
+ if (!inst_call(ins) &&
+ !inst_return(ins) &&
+ !inst_trap_return(ins)) {
+ return (FALSE); /* continue */
+ }
+ }
+ db_run_mode = STEP_NONE;
+ return (TRUE);
+}
+
+void
+db_restart_at_pc(watchpt)
+ boolean_t watchpt;
+{
+ register db_addr_t pc = PC_REGS();
+
+ 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_value(pc, sizeof(int), FALSE);
+ db_inst_count++;
+ db_load_count += inst_load(ins);
+ db_store_count += inst_store(ins);
+#ifdef SOFTWARE_SSTEP
+ /* XXX works on mips, but... */
+ if (inst_branch(ins) || inst_call(ins)) {
+ ins = db_get_value(next_instr_address(pc,1),
+ sizeof(int), FALSE);
+ 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(pc)) {
+ /*
+ * Step over breakpoint/watchpoint.
+ */
+ db_run_mode = STEP_INVISIBLE;
+ db_set_single_step();
+ } else {
+ db_set_breakpoints();
+ db_set_watchpoints();
+ }
+ } else {
+ db_set_single_step();
+ }
+}
+
+#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) 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.
+ */
+
+void
+db_set_single_step(void)
+{
+ db_addr_t pc = PC_REGS(), brpc;
+ unsigned inst;
+
+ /*
+ * User was stopped at pc, e.g. the instruction
+ * at pc was not executed.
+ */
+ inst = db_get_value(pc, sizeof(int), FALSE);
+ if (inst_branch(inst) || inst_call(inst) || inst_return(inst)) {
+ brpc = branch_taken(inst, pc);
+ if (brpc != pc) { /* self-branches are hopeless */
+ db_taken_bkpt = db_set_temp_breakpoint(brpc);
+ }
+ pc = next_instr_address(pc, 1);
+ }
+ pc = next_instr_address(pc, 0);
+ db_not_taken_bkpt = db_set_temp_breakpoint(pc);
+}
+
+void
+db_clear_single_step(void)
+{
+
+ if (db_not_taken_bkpt != 0) {
+ db_delete_temp_breakpoint(db_not_taken_bkpt);
+ db_not_taken_bkpt = 0;
+ }
+ if (db_taken_bkpt != 0) {
+ db_delete_temp_breakpoint(db_taken_bkpt);
+ db_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;
+ boolean_t 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_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;
+ boolean_t 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_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;
+ boolean_t 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_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;
+ boolean_t 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_load_count = 0;
+ db_store_count = 0;
+
+ db_cmd_loop_done = 1;
+}
diff --git a/sys/ddb/db_script.c b/sys/ddb/db_script.c
new file mode 100644
index 0000000..34215f8
--- /dev/null
+++ b/sys/ddb/db_script.c
@@ -0,0 +1,562 @@
+/*-
+ * Copyright (c) 2007 Robert N. M. Watson
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+/*-
+ * Simple DDB scripting mechanism. Each script consists of a named list of
+ * DDB commands to execute sequentially. A more sophisticated scripting
+ * language might be desirable, but would be significantly more complex to
+ * implement. A more interesting syntax might allow general use of variables
+ * and extracting of useful values, such as a thread's process identifier,
+ * for passing into further DDB commands. Certain scripts are run
+ * automatically at kdb_enter(), if defined, based on how the debugger is
+ * entered, allowing scripted responses to panics, break signals, etc.
+ *
+ * Scripts may be managed from within DDB using the script, scripts, and
+ * unscript commands. They may also be managed from userspace using ddb(8),
+ * which operates using a set of sysctls.
+ *
+ * TODO:
+ * - Allow scripts to be defined using tunables so that they can be defined
+ * before boot and be present in single-user mode without boot scripts
+ * running.
+ * - Memory allocation is not possible from within DDB, so we use a set of
+ * statically allocated buffers to hold defined scripts. However, when
+ * scripts are being defined from userspace via sysctl, we could in fact be
+ * using malloc(9) and therefore not impose a static limit, giving greater
+ * flexibility and avoiding hard-defined buffer limits.
+ * - When scripts run automatically on entrance to DDB, placing "continue" at
+ * the end still results in being in the debugger, as we unconditionally
+ * run db_command_loop() after the script. There should be a way to avoid
+ * this.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kdb.h>
+#include <sys/kernel.h>
+#include <sys/libkern.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/sbuf.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+
+#include <ddb/ddb.h>
+#include <ddb/db_command.h>
+#include <ddb/db_lex.h>
+
+#include <machine/setjmp.h>
+
+/*
+ * struct ddb_script describes an individual script.
+ */
+struct ddb_script {
+ char ds_scriptname[DB_MAXSCRIPTNAME];
+ char ds_script[DB_MAXSCRIPTLEN];
+};
+
+/*
+ * Global list of scripts -- defined scripts have non-empty name fields.
+ */
+static struct ddb_script db_script_table[DB_MAXSCRIPTS];
+
+/*
+ * While executing a script, we parse it using strsep(), so require a
+ * temporary buffer that may be used destructively. Since we support weak
+ * recursion of scripts (one may reference another), we need one buffer for
+ * each concurrently executing script.
+ */
+static struct db_recursion_data {
+ char drd_buffer[DB_MAXSCRIPTLEN];
+} db_recursion_data[DB_MAXSCRIPTRECURSION];
+static int db_recursion = -1;
+
+/*
+ * We use a separate static buffer for script validation so that it is safe
+ * to validate scripts from within a script. This is used only in
+ * db_script_valid(), which should never be called reentrantly.
+ */
+static char db_static_buffer[DB_MAXSCRIPTLEN];
+
+/*
+ * Synchronization is not required from within the debugger, as it is
+ * singe-threaded (although reentrance must be carefully considered).
+ * However, it is required when interacting with scripts from user space
+ * processes. Sysctl procedures acquire db_script_mtx before accessing the
+ * global script data structures.
+ */
+static struct mtx db_script_mtx;
+MTX_SYSINIT(db_script_mtx, &db_script_mtx, "db_script_mtx", MTX_DEF);
+
+/*
+ * Some script names have special meaning, such as those executed
+ * automatically when KDB is entered.
+ */
+#define DB_SCRIPT_KDBENTER_PREFIX "kdb.enter" /* KDB has entered. */
+#define DB_SCRIPT_KDBENTER_DEFAULT "kdb.enter.default"
+
+/*
+ * Find the existing script slot for a named script, if any.
+ */
+static struct ddb_script *
+db_script_lookup(const char *scriptname)
+{
+ int i;
+
+ for (i = 0; i < DB_MAXSCRIPTS; i++) {
+ if (strcmp(db_script_table[i].ds_scriptname, scriptname) ==
+ 0)
+ return (&db_script_table[i]);
+ }
+ return (NULL);
+}
+
+/*
+ * Find a new slot for a script, if available. Does not mark as allocated in
+ * any way--this must be done by the caller.
+ */
+static struct ddb_script *
+db_script_new(void)
+{
+ int i;
+
+ for (i = 0; i < DB_MAXSCRIPTS; i++) {
+ if (strlen(db_script_table[i].ds_scriptname) == 0)
+ return (&db_script_table[i]);
+ }
+ return (NULL);
+}
+
+/*
+ * Perform very rudimentary validation of a proposed script. It would be
+ * easy to imagine something more comprehensive. The script string is
+ * validated in a static buffer.
+ */
+static int
+db_script_valid(const char *scriptname, const char *script)
+{
+ char *buffer, *command;
+
+ if (strlen(scriptname) == 0)
+ return (EINVAL);
+ if (strlen(scriptname) >= DB_MAXSCRIPTNAME)
+ return (EINVAL);
+ if (strlen(script) >= DB_MAXSCRIPTLEN)
+ return (EINVAL);
+ buffer = db_static_buffer;
+ strcpy(buffer, script);
+ while ((command = strsep(&buffer, ";")) != NULL) {
+ if (strlen(command) >= DB_MAXLINE)
+ return (EINVAL);
+ }
+ return (0);
+}
+
+/*
+ * Modify an existing script or add a new script with the specified script
+ * name and contents. If there are no script slots available, an error will
+ * be returned.
+ */
+static int
+db_script_set(const char *scriptname, const char *script)
+{
+ struct ddb_script *dsp;
+ int error;
+
+ error = db_script_valid(scriptname, script);
+ if (error)
+ return (error);
+ dsp = db_script_lookup(scriptname);
+ if (dsp == NULL) {
+ dsp = db_script_new();
+ if (dsp == NULL)
+ return (ENOSPC);
+ strlcpy(dsp->ds_scriptname, scriptname,
+ sizeof(dsp->ds_scriptname));
+ }
+ strlcpy(dsp->ds_script, script, sizeof(dsp->ds_script));
+ return (0);
+}
+
+/*
+ * Delete an existing script by name, if found.
+ */
+static int
+db_script_unset(const char *scriptname)
+{
+ struct ddb_script *dsp;
+
+ dsp = db_script_lookup(scriptname);
+ if (dsp == NULL)
+ return (ENOENT);
+ strcpy(dsp->ds_scriptname, "");
+ strcpy(dsp->ds_script, "");
+ return (0);
+}
+
+/*
+ * Trim leading/trailing white space in a command so that we don't pass
+ * carriage returns, etc, into DDB command parser.
+ */
+static int
+db_command_trimmable(char ch)
+{
+
+ switch (ch) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ return (1);
+
+ default:
+ return (0);
+ }
+}
+
+static void
+db_command_trim(char **commandp)
+{
+ char *command;
+
+ command = *commandp;
+ while (db_command_trimmable(*command))
+ command++;
+ while ((strlen(command) > 0) &&
+ db_command_trimmable(command[strlen(command) - 1]))
+ command[strlen(command) - 1] = 0;
+ *commandp = command;
+}
+
+/*
+ * Execute a script, breaking it up into individual commands and passing them
+ * sequentially into DDB's input processing. Use the KDB jump buffer to
+ * restore control to the main script loop if things get too wonky when
+ * processing a command -- i.e., traps, etc. Also, make sure we don't exceed
+ * practical limits on recursion.
+ *
+ * XXXRW: If any individual command is too long, it will be truncated when
+ * injected into the input at a lower layer. We should validate the script
+ * before configuring it to avoid this scenario.
+ */
+static int
+db_script_exec(const char *scriptname, int warnifnotfound)
+{
+ struct db_recursion_data *drd;
+ struct ddb_script *dsp;
+ char *buffer, *command;
+ void *prev_jb;
+ jmp_buf jb;
+
+ dsp = db_script_lookup(scriptname);
+ if (dsp == NULL) {
+ if (warnifnotfound)
+ db_printf("script '%s' not found\n", scriptname);
+ return (ENOENT);
+ }
+
+ if (db_recursion >= DB_MAXSCRIPTRECURSION) {
+ db_printf("Script stack too deep\n");
+ return (E2BIG);
+ }
+ db_recursion++;
+ drd = &db_recursion_data[db_recursion];
+
+ /*
+ * Parse script in temporary buffer, since strsep() is destructive.
+ */
+ buffer = drd->drd_buffer;
+ strcpy(buffer, dsp->ds_script);
+ while ((command = strsep(&buffer, ";")) != NULL) {
+ db_printf("db:%d:%s> %s\n", db_recursion, scriptname,
+ command);
+ db_command_trim(&command);
+ prev_jb = kdb_jmpbuf(jb);
+ if (setjmp(jb) == 0)
+ db_command_script(command);
+ else
+ db_printf("Script command '%s' returned error\n",
+ command);
+ kdb_jmpbuf(prev_jb);
+ }
+ db_recursion--;
+ return (0);
+}
+
+/*
+ * Wrapper for exec path that is called on KDB enter. Map reason for KDB
+ * enter to a script name, and don't whine if the script doesn't exist. If
+ * there is no matching script, try the catch-all script.
+ */
+void
+db_script_kdbenter(const char *eventname)
+{
+ char scriptname[DB_MAXSCRIPTNAME];
+
+ snprintf(scriptname, sizeof(scriptname), "%s.%s",
+ DB_SCRIPT_KDBENTER_PREFIX, eventname);
+ if (db_script_exec(scriptname, 0) == ENOENT)
+ (void)db_script_exec(DB_SCRIPT_KDBENTER_DEFAULT, 0);
+}
+
+/*-
+ * DDB commands for scripting, as reached via the DDB user interface:
+ *
+ * scripts - lists scripts
+ * run <scriptname> - run a script
+ * script <scriptname> - prints script
+ * script <scriptname> <script> - set a script
+ * unscript <scriptname> - remove a script
+ */
+
+/*
+ * List scripts and their contents.
+ */
+void
+db_scripts_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count,
+ char *modif)
+{
+ int i;
+
+ for (i = 0; i < DB_MAXSCRIPTS; i++) {
+ if (strlen(db_script_table[i].ds_scriptname) != 0) {
+ db_printf("%s=%s\n",
+ db_script_table[i].ds_scriptname,
+ db_script_table[i].ds_script);
+ }
+ }
+}
+
+/*
+ * Execute a script.
+ */
+void
+db_run_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, char *modif)
+{
+ int t;
+
+ /*
+ * Right now, we accept exactly one argument. In the future, we
+ * might want to accept flags and arguments to the script itself.
+ */
+ t = db_read_token();
+ if (t != tIDENT)
+ db_error("?\n");
+
+ if (db_read_token() != tEOL)
+ db_error("?\n");
+
+ db_script_exec(db_tok_string, 1);
+}
+
+/*
+ * Print or set a named script, with the set portion broken out into its own
+ * function. We must directly access the remainder of the DDB line input as
+ * we do not wish to use db_lex's token processing.
+ */
+void
+db_script_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count,
+ char *modif)
+{
+ char *buf, scriptname[DB_MAXSCRIPTNAME];
+ struct ddb_script *dsp;
+ int error, t;
+
+ t = db_read_token();
+ if (t != tIDENT) {
+ db_printf("usage: script scriptname=script\n");
+ db_skip_to_eol();
+ return;
+ }
+
+ if (strlcpy(scriptname, db_tok_string, sizeof(scriptname)) >=
+ sizeof(scriptname)) {
+ db_printf("scriptname too long\n");
+ db_skip_to_eol();
+ return;
+ }
+
+ t = db_read_token();
+ if (t == tEOL) {
+ dsp = db_script_lookup(scriptname);
+ if (dsp == NULL) {
+ db_printf("script '%s' not found\n", scriptname);
+ db_skip_to_eol();
+ return;
+ }
+ db_printf("%s=%s\n", scriptname, dsp->ds_script);
+ } else if (t == tEQ) {
+ buf = db_get_line();
+ if (buf[strlen(buf)-1] == '\n')
+ buf[strlen(buf)-1] = '\0';
+ error = db_script_set(scriptname, buf);
+ if (error != 0)
+ db_printf("Error: %d\n", error);
+ } else
+ db_printf("?\n");
+ db_skip_to_eol();
+}
+
+/*
+ * Remove a named script.
+ */
+void
+db_unscript_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count,
+ char *modif)
+{
+ int error, t;
+
+ t = db_read_token();
+ if (t != tIDENT) {
+ db_printf("?\n");
+ db_skip_to_eol();
+ return;
+ }
+
+ error = db_script_unset(db_tok_string);
+ if (error == ENOENT) {
+ db_printf("script '%s' not found\n", db_tok_string);
+ db_skip_to_eol();
+ return;
+ }
+ db_skip_to_eol();
+}
+
+/*
+ * Sysctls for managing DDB scripting:
+ *
+ * debug.ddb.scripting.script - Define a new script
+ * debug.ddb.scripting.scripts - List of names *and* scripts
+ * debug.ddb.scripting.unscript - Remove an existing script
+ *
+ * Since we don't want to try to manage arbitrary extensions to the sysctl
+ * name space from the debugger, the script/unscript sysctls are a bit more
+ * like RPCs and a bit less like normal get/set requests. The ddb(8) command
+ * line tool wraps them to make things a bit more user-friendly.
+ */
+static SYSCTL_NODE(_debug_ddb, OID_AUTO, scripting, CTLFLAG_RW, 0,
+ "DDB script settings");
+
+static int
+sysctl_debug_ddb_scripting_scripts(SYSCTL_HANDLER_ARGS)
+{
+ struct sbuf sb;
+ int error, i, len;
+ char *buffer;
+
+ /*
+ * Make space to include a maximum-length name, = symbol,
+ * maximum-length script, and carriage return for every script that
+ * may be defined.
+ */
+ len = DB_MAXSCRIPTS * (DB_MAXSCRIPTNAME + 1 + DB_MAXSCRIPTLEN + 1);
+ buffer = malloc(len, M_TEMP, M_WAITOK);
+ (void)sbuf_new(&sb, buffer, len, SBUF_FIXEDLEN);
+ mtx_lock(&db_script_mtx);
+ for (i = 0; i < DB_MAXSCRIPTS; i++) {
+ if (strlen(db_script_table[i].ds_scriptname) == 0)
+ continue;
+ (void)sbuf_printf(&sb, "%s=%s\n",
+ db_script_table[i].ds_scriptname,
+ db_script_table[i].ds_script);
+ }
+ mtx_unlock(&db_script_mtx);
+ sbuf_finish(&sb);
+ error = SYSCTL_OUT(req, sbuf_data(&sb), sbuf_len(&sb) + 1);
+ sbuf_delete(&sb);
+ free(buffer, M_TEMP);
+ return (error);
+}
+SYSCTL_PROC(_debug_ddb_scripting, OID_AUTO, scripts, CTLTYPE_STRING |
+ CTLFLAG_RD, 0, 0, sysctl_debug_ddb_scripting_scripts, "A",
+ "List of defined scripts");
+
+static int
+sysctl_debug_ddb_scripting_script(SYSCTL_HANDLER_ARGS)
+{
+ char *buffer, *script, *scriptname;
+ int error, len;
+
+ /*
+ * Maximum length for an input string is DB_MAXSCRIPTNAME + '='
+ * symbol + DB_MAXSCRIPT.
+ */
+ len = DB_MAXSCRIPTNAME + DB_MAXSCRIPTLEN + 1;
+ buffer = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
+ error = sysctl_handle_string(oidp, buffer, len, req);
+ if (error)
+ goto out;
+
+ /*
+ * Argument will be in form scriptname=script, so split into the
+ * scriptname and script.
+ */
+ script = buffer;
+ scriptname = strsep(&script, "=");
+ if (script == NULL) {
+ error = EINVAL;
+ goto out;
+ }
+ mtx_lock(&db_script_mtx);
+ error = db_script_set(scriptname, script);
+ mtx_unlock(&db_script_mtx);
+out:
+ free(buffer, M_TEMP);
+ return (error);
+}
+SYSCTL_PROC(_debug_ddb_scripting, OID_AUTO, script, CTLTYPE_STRING |
+ CTLFLAG_RW, 0, 0, sysctl_debug_ddb_scripting_script, "A",
+ "Set a script");
+
+/*
+ * debug.ddb.scripting.unscript has somewhat unusual sysctl semantics -- set
+ * the name of the script that you want to delete.
+ */
+static int
+sysctl_debug_ddb_scripting_unscript(SYSCTL_HANDLER_ARGS)
+{
+ char name[DB_MAXSCRIPTNAME];
+ int error;
+
+ bzero(name, sizeof(name));
+ error = sysctl_handle_string(oidp, name, sizeof(name), req);
+ if (error)
+ return (error);
+ if (req->newptr == NULL)
+ return (0);
+ mtx_lock(&db_script_mtx);
+ error = db_script_unset(name);
+ mtx_unlock(&db_script_mtx);
+ if (error == ENOENT)
+ return (EINVAL); /* Don't confuse sysctl consumers. */
+ return (0);
+}
+SYSCTL_PROC(_debug_ddb_scripting, OID_AUTO, unscript, CTLTYPE_STRING |
+ CTLFLAG_RW, 0, 0, sysctl_debug_ddb_scripting_unscript, "A",
+ "Unset a script");
diff --git a/sys/ddb/db_sym.c b/sys/ddb/db_sym.c
new file mode 100644
index 0000000..04af1eb
--- /dev/null
+++ b/sys/ddb/db_sym.c
@@ -0,0 +1,505 @@
+/*-
+ * 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
+ * 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 the
+ * rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/pcpu.h>
+#include <sys/smp.h>
+#include <sys/systm.h>
+
+#include <net/vnet.h>
+
+#include <ddb/ddb.h>
+#include <ddb/db_sym.h>
+#include <ddb/db_variables.h>
+
+#include <opt_ddb.h>
+
+/*
+ * Multiple symbol tables
+ */
+#ifndef MAXNOSYMTABS
+#define MAXNOSYMTABS 3 /* mach, ux, emulator */
+#endif
+
+static db_symtab_t db_symtabs[MAXNOSYMTABS] = {{0,},};
+static int db_nsymtab = 0;
+
+static db_symtab_t *db_last_symtab; /* where last symbol was found */
+
+static c_db_sym_t db_lookup( const char *symstr);
+static char *db_qualify(c_db_sym_t sym, char *symtabname);
+static boolean_t db_symbol_is_ambiguous(c_db_sym_t sym);
+static boolean_t db_line_at_pc(c_db_sym_t, char **, int *, db_expr_t);
+
+static int db_cpu = -1;
+
+#ifdef VIMAGE
+static void *db_vnet = NULL;
+#endif
+
+/*
+ * Validate the CPU number used to interpret per-CPU variables so we can
+ * avoid later confusion if an invalid CPU is requested.
+ */
+int
+db_var_db_cpu(struct db_variable *vp, db_expr_t *valuep, int op)
+{
+
+ switch (op) {
+ case DB_VAR_GET:
+ *valuep = db_cpu;
+ return (1);
+
+ case DB_VAR_SET:
+ if (*(int *)valuep < -1 && *(int *)valuep > mp_maxid) {
+ db_printf("Invalid value: %d", *(int*)valuep);
+ return (0);
+ }
+ db_cpu = *(int *)valuep;
+ return (1);
+
+ default:
+ db_printf("db_var_db_cpu: unknown operation\n");
+ return (0);
+ }
+}
+
+/*
+ * Read-only variable reporting the current CPU, which is what we use when
+ * db_cpu is set to -1.
+ */
+int
+db_var_curcpu(struct db_variable *vp, db_expr_t *valuep, int op)
+{
+
+ switch (op) {
+ case DB_VAR_GET:
+ *valuep = curcpu;
+ return (1);
+
+ case DB_VAR_SET:
+ db_printf("Read-only variable.\n");
+ return (0);
+
+ default:
+ db_printf("db_var_curcpu: unknown operation\n");
+ return (0);
+ }
+}
+
+#ifdef VIMAGE
+/*
+ * Validate the virtual network pointer used to interpret per-vnet global
+ * variable expansion. Right now we don't do much here, really we should
+ * walk the global vnet list to check it's an OK pointer.
+ */
+int
+db_var_db_vnet(struct db_variable *vp, db_expr_t *valuep, int op)
+{
+
+ switch (op) {
+ case DB_VAR_GET:
+ *valuep = (db_expr_t)db_vnet;
+ return (1);
+
+ case DB_VAR_SET:
+ db_vnet = *(void **)valuep;
+ return (1);
+
+ default:
+ db_printf("db_var_db_vnet: unknown operation\n");
+ return (0);
+ }
+}
+
+/*
+ * Read-only variable reporting the current vnet, which is what we use when
+ * db_vnet is set to NULL.
+ */
+int
+db_var_curvnet(struct db_variable *vp, db_expr_t *valuep, int op)
+{
+
+ switch (op) {
+ case DB_VAR_GET:
+ *valuep = (db_expr_t)curvnet;
+ return (1);
+
+ case DB_VAR_SET:
+ db_printf("Read-only variable.\n");
+ return (0);
+
+ default:
+ db_printf("db_var_curcpu: unknown operation\n");
+ return (0);
+ }
+}
+#endif
+
+/*
+ * Add symbol table, with given name, to list of symbol tables.
+ */
+void
+db_add_symbol_table(start, end, name, ref)
+ char *start;
+ char *end;
+ char *name;
+ char *ref;
+{
+ if (db_nsymtab >= MAXNOSYMTABS) {
+ printf ("No slots left for %s symbol table", name);
+ panic ("db_sym.c: db_add_symbol_table");
+ }
+
+ db_symtabs[db_nsymtab].start = start;
+ db_symtabs[db_nsymtab].end = end;
+ db_symtabs[db_nsymtab].name = name;
+ db_symtabs[db_nsymtab].private = ref;
+ db_nsymtab++;
+}
+
+/*
+ * db_qualify("vm_map", "ux") returns "unix: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(sym, symtabname)
+ c_db_sym_t sym;
+ register char *symtabname;
+{
+ const char *symname;
+ static char tmp[256];
+
+ db_symbol_values(sym, &symname, 0);
+ snprintf(tmp, sizeof(tmp), "%s:%s", symtabname, symname);
+ return tmp;
+}
+
+
+boolean_t
+db_eqname(src, dst, c)
+ const char *src;
+ const char *dst;
+ int 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)
+ const char *name;
+ db_expr_t *valuep;
+{
+ c_db_sym_t sym;
+
+ sym = db_lookup(name);
+ if (sym == C_DB_SYM_NULL)
+ return (FALSE);
+ db_symbol_values(sym, &name, valuep);
+ return (TRUE);
+}
+
+boolean_t
+db_value_of_name_pcpu(name, valuep)
+ const char *name;
+ db_expr_t *valuep;
+{
+ static char tmp[256];
+ db_expr_t value;
+ c_db_sym_t sym;
+ int cpu;
+
+ if (db_cpu != -1)
+ cpu = db_cpu;
+ else
+ cpu = curcpu;
+ snprintf(tmp, sizeof(tmp), "pcpu_entry_%s", name);
+ sym = db_lookup(tmp);
+ if (sym == C_DB_SYM_NULL)
+ return (FALSE);
+ db_symbol_values(sym, &name, &value);
+ if (value < DPCPU_START || value >= DPCPU_STOP)
+ return (FALSE);
+ *valuep = (db_expr_t)((uintptr_t)value + dpcpu_off[cpu]);
+ return (TRUE);
+}
+
+boolean_t
+db_value_of_name_vnet(name, valuep)
+ const char *name;
+ db_expr_t *valuep;
+{
+#ifdef VIMAGE
+ static char tmp[256];
+ db_expr_t value;
+ c_db_sym_t sym;
+ struct vnet *vnet;
+
+ if (db_vnet != NULL)
+ vnet = db_vnet;
+ else
+ vnet = curvnet;
+ snprintf(tmp, sizeof(tmp), "vnet_entry_%s", name);
+ sym = db_lookup(tmp);
+ if (sym == C_DB_SYM_NULL)
+ return (FALSE);
+ db_symbol_values(sym, &name, &value);
+ if (value < VNET_START || value >= VNET_STOP)
+ return (FALSE);
+ *valuep = (db_expr_t)((uintptr_t)value + vnet->vnet_data_base);
+ return (TRUE);
+#else
+ return (FALSE);
+#endif
+}
+
+/*
+ * 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.
+ */
+static c_db_sym_t
+db_lookup(symstr)
+ const char *symstr;
+{
+ c_db_sym_t sp;
+ register int i;
+ int symtab_start = 0;
+ int symtab_end = db_nsymtab;
+ register const char *cp;
+
+ /*
+ * Look for, remove, and remember any symbol table specifier.
+ */
+ for (cp = symstr; *cp; cp++) {
+ if (*cp == ':') {
+ for (i = 0; i < db_nsymtab; i++) {
+ int n = strlen(db_symtabs[i].name);
+
+ if (
+ n == (cp - symstr) &&
+ strncmp(symstr, db_symtabs[i].name, n) == 0
+ ) {
+ symtab_start = i;
+ symtab_end = i + 1;
+ break;
+ }
+ }
+ if (i == db_nsymtab) {
+ db_error("invalid symbol table name");
+ }
+ symstr = cp+1;
+ }
+ }
+
+ /*
+ * Look in the specified set of symbol tables.
+ * Return on first match.
+ */
+ for (i = symtab_start; i < symtab_end; i++) {
+ sp = X_db_lookup(&db_symtabs[i], symstr);
+ if (sp) {
+ db_last_symtab = &db_symtabs[i];
+ return sp;
+ }
+ }
+ return 0;
+}
+
+/*
+ * If TRUE, check across symbol tables for multiple occurrences
+ * of a name. Might slow things down quite a bit.
+ */
+static volatile boolean_t db_qualify_ambiguous_names = FALSE;
+
+/*
+ * Does this symbol name appear in more than one symbol table?
+ * Used by db_symbol_values to decide whether to qualify a symbol.
+ */
+static boolean_t
+db_symbol_is_ambiguous(sym)
+ c_db_sym_t sym;
+{
+ const char *sym_name;
+ register int i;
+ register
+ boolean_t found_once = FALSE;
+
+ if (!db_qualify_ambiguous_names)
+ return FALSE;
+
+ db_symbol_values(sym, &sym_name, 0);
+ 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;
+}
+
+/*
+ * Find the closest symbol to val, and return its name
+ * and the difference between val and the symbol found.
+ */
+c_db_sym_t
+db_search_symbol( val, strategy, offp)
+ register db_addr_t val;
+ db_strategy_t strategy;
+ db_expr_t *offp;
+{
+ register
+ unsigned int diff;
+ size_t newdiff;
+ register int i;
+ c_db_sym_t ret = C_DB_SYM_NULL, sym;
+
+ newdiff = diff = ~0;
+ for (i = 0; i < db_nsymtab; i++) {
+ sym = X_db_search_symbol(&db_symtabs[i], val, strategy, &newdiff);
+ if (newdiff < diff) {
+ db_last_symtab = &db_symtabs[i];
+ diff = newdiff;
+ ret = sym;
+ }
+ }
+ *offp = diff;
+ return ret;
+}
+
+/*
+ * Return name and value of a symbol
+ */
+void
+db_symbol_values(sym, namep, valuep)
+ c_db_sym_t sym;
+ const char **namep;
+ db_expr_t *valuep;
+{
+ db_expr_t value;
+
+ if (sym == DB_SYM_NULL) {
+ *namep = 0;
+ return;
+ }
+
+ X_db_symbol_values(db_last_symtab, sym, namep, &value);
+
+ if (db_symbol_is_ambiguous(sym))
+ *namep = db_qualify(sym, db_last_symtab->name);
+ if (valuep)
+ *valuep = value;
+}
+
+
+/*
+ * Print a 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 "small" (and use plain hex).
+ */
+
+db_expr_t db_maxoff = 0x10000;
+
+void
+db_printsym(off, strategy)
+ db_expr_t off;
+ db_strategy_t strategy;
+{
+ db_expr_t d;
+ char *filename;
+ const char *name;
+ db_expr_t value;
+ int linenum;
+ c_db_sym_t cursym;
+
+ cursym = db_search_symbol(off, strategy, &d);
+ db_symbol_values(cursym, &name, &value);
+ if (name == 0)
+ value = off;
+ if (value >= DB_SMALL_VALUE_MIN && value <= DB_SMALL_VALUE_MAX) {
+ db_printf("%+#lr", (long)off);
+ return;
+ }
+ if (name == 0 || d >= (unsigned long)db_maxoff) {
+ db_printf("%#lr", (unsigned long)off);
+ return;
+ }
+#ifdef DDB_NUMSYM
+ db_printf("%#lr = %s", (unsigned long)off, name);
+#else
+ db_printf("%s", name);
+#endif
+ if (d)
+ db_printf("+%+#lr", (long)d);
+ if (strategy == DB_STGY_PROC) {
+ if (db_line_at_pc(cursym, &filename, &linenum, off))
+ db_printf(" [%s:%d]", filename, linenum);
+ }
+}
+
+static boolean_t
+db_line_at_pc( sym, filename, linenum, pc)
+ c_db_sym_t sym;
+ char **filename;
+ int *linenum;
+ db_expr_t pc;
+{
+ return X_db_line_at_pc( db_last_symtab, sym, filename, linenum, pc);
+}
+
+int
+db_sym_numargs(sym, nargp, argnames)
+ c_db_sym_t sym;
+ int *nargp;
+ char **argnames;
+{
+ return X_db_sym_numargs(db_last_symtab, sym, nargp, argnames);
+}
diff --git a/sys/ddb/db_sym.h b/sys/ddb/db_sym.h
new file mode 100644
index 0000000..f68ccbc
--- /dev/null
+++ b/sys/ddb/db_sym.h
@@ -0,0 +1,106 @@
+/*-
+ * 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
+ * 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 the
+ * rights to redistribute these changes.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _DDB_DB_SYM_H_
+#define _DDB_DB_SYM_H_
+
+/*
+ * Author: Alessandro Forin, Carnegie Mellon University
+ * Date: 8/90
+ */
+
+/*
+ * This module can handle multiple symbol tables
+ */
+typedef struct {
+ char *name; /* symtab name */
+ char *start; /* symtab location */
+ char *end;
+ char *private; /* optional machdep pointer */
+} db_symtab_t;
+
+/*
+ * 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 */
+typedef const char * c_db_sym_t; /* const opaque handle on symbols */
+#define DB_SYM_NULL ((db_sym_t)0)
+#define C_DB_SYM_NULL ((c_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 concern 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 */
+
+/*
+ * Functions exported by the symtable module
+ */
+void db_add_symbol_table(char *, char *, char *, char *);
+ /* extend the list of symbol tables */
+
+c_db_sym_t db_search_symbol(db_addr_t, db_strategy_t, db_expr_t *);
+ /* find symbol given value */
+
+void db_symbol_values(c_db_sym_t, const char **, db_expr_t *);
+ /* return name and value of symbol */
+
+#define db_find_sym_and_offset(val,namep,offp) \
+ db_symbol_values(db_search_symbol(val,DB_STGY_ANY,offp),namep,0)
+ /* find name&value given approx val */
+
+#define db_find_xtrn_sym_and_offset(val,namep,offp) \
+ db_symbol_values(db_search_symbol(val,DB_STGY_XTRN,offp),namep,0)
+ /* ditto, but no locals */
+
+int db_eqname(const char *, const char *, int);
+ /* strcmp, modulo leading char */
+
+void db_printsym(db_expr_t, db_strategy_t);
+ /* print closest symbol to a value */
+
+int db_sym_numargs(c_db_sym_t, int *, char **);
+
+boolean_t X_db_line_at_pc(db_symtab_t *symtab, c_db_sym_t cursym,
+ char **filename, int *linenum, db_expr_t off);
+c_db_sym_t X_db_lookup(db_symtab_t *stab, const char *symstr);
+c_db_sym_t X_db_search_symbol(db_symtab_t *symtab, db_addr_t off,
+ db_strategy_t strategy, db_expr_t *diffp);
+int X_db_sym_numargs(db_symtab_t *, c_db_sym_t, int *, char **);
+void X_db_symbol_values(db_symtab_t *symtab, c_db_sym_t sym,
+ const char **namep, db_expr_t *valuep);
+
+#endif /* !_DDB_DB_SYM_H_ */
diff --git a/sys/ddb/db_textdump.c b/sys/ddb/db_textdump.c
new file mode 100644
index 0000000..e664870
--- /dev/null
+++ b/sys/ddb/db_textdump.c
@@ -0,0 +1,550 @@
+/*-
+ * Copyright (c) 2007 Robert N. M. Watson
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+/*-
+ * Kernel text-dump support: write a series of text files to the dump
+ * partition for later recovery, including captured DDB output, kernel
+ * configuration, message buffer, and panic message. This allows for a more
+ * compact representation of critical debugging information than traditional
+ * binary dumps, as well as allowing dump information to be used without
+ * access to kernel symbols, source code, etc.
+ *
+ * Storage Layout
+ * --------------
+ *
+ * Crash dumps are aligned to the end of the dump or swap partition in order
+ * to minimize the chances of swap duing fsck eating into the dump. However,
+ * unlike a memory dump, we don't know the size of the textdump a priori, so
+ * can't just write it out sequentially in order from a known starting point
+ * calculated with respect to the end of the partition. In order to address
+ * this, we actually write out the textdump in reverse block order, allowing
+ * us to directly align it to the end of the partition and then write out the
+ * dump header and trailer before and after it once done. savecore(8) must
+ * know to reverse the order of the blocks in order to produce a readable
+ * file.
+ *
+ * Data is written out in the ustar file format so that we can write data
+ * incrementally as a stream without reference to previous files.
+ *
+ * TODO
+ * ----
+ *
+ * - Allow subsytems to register to submit files for inclusion in the text
+ * dump in a generic way.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_config.h"
+
+#include "opt_ddb.h"
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/kerneldump.h>
+#include <sys/msgbuf.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+
+#include <ddb/ddb.h>
+#include <ddb/db_lex.h>
+
+static SYSCTL_NODE(_debug_ddb, OID_AUTO, textdump, CTLFLAG_RW, 0,
+ "DDB textdump options");
+
+/*
+ * Don't touch the first SIZEOF_METADATA bytes on the dump device. This is
+ * to protect us from metadata and metadata from us.
+ */
+#define SIZEOF_METADATA (64*1024)
+
+/*
+ * Data is written out as a series of files in the ustar tar format. ustar
+ * is a simple streamed format consiting of a series of files prefixed with
+ * headers, and all padded to 512-byte block boundaries, which maps
+ * conveniently to our requirements.
+ */
+struct ustar_header {
+ char uh_filename[100];
+ char uh_mode[8];
+ char uh_tar_owner[8];
+ char uh_tar_group[8];
+ char uh_size[12];
+ char uh_mtime[12];
+ char uh_sum[8];
+ char uh_type;
+ char uh_linkfile[100];
+ char uh_ustar[6];
+ char uh_version[2];
+ char uh_owner[32];
+ char uh_group[32];
+ char uh_major[8];
+ char uh_minor[8];
+ char uh_filenameprefix[155];
+ char uh_zeropad[12];
+} __packed;
+
+/*
+ * Various size assertions -- pretty much everything must be one block in
+ * size.
+ */
+CTASSERT(sizeof(struct kerneldumpheader) == TEXTDUMP_BLOCKSIZE);
+CTASSERT(sizeof(struct ustar_header) == TEXTDUMP_BLOCKSIZE);
+
+/*
+ * Is a textdump scheduled? If so, the shutdown code will invoke our dumpsys
+ * routine instead of the machine-dependent kernel dump routine.
+ */
+#ifdef TEXTDUMP_PREFERRED
+int textdump_pending = 1;
+#else
+int textdump_pending = 0;
+#endif
+SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, pending, CTLFLAG_RW,
+ &textdump_pending, 0,
+ "Perform textdump instead of regular kernel dump.");
+
+/*
+ * Various constants for tar headers and contents.
+ */
+#define TAR_USER "root"
+#define TAR_GROUP "wheel"
+#define TAR_UID "0"
+#define TAR_GID "0"
+#define TAR_MODE "0600"
+#define TAR_USTAR "ustar"
+
+#define TAR_CONFIG_FILENAME "config.txt" /* Kernel configuration. */
+#define TAR_MSGBUF_FILENAME "msgbuf.txt" /* Kernel messsage buffer. */
+#define TAR_PANIC_FILENAME "panic.txt" /* Panic message. */
+#define TAR_VERSION_FILENAME "version.txt" /* Kernel version. */
+
+/*
+ * Configure which files will be dumped.
+ */
+#ifdef INCLUDE_CONFIG_FILE
+static int textdump_do_config = 1;
+SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, do_config, CTLFLAG_RW,
+ &textdump_do_config, 0, "Dump kernel configuration in textdump");
+#endif
+
+static int textdump_do_ddb = 1;
+SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, do_ddb, CTLFLAG_RW,
+ &textdump_do_ddb, 0, "Dump DDB captured output in textdump");
+
+static int textdump_do_msgbuf = 1;
+SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, do_msgbuf, CTLFLAG_RW,
+ &textdump_do_msgbuf, 0, "Dump kernel message buffer in textdump");
+
+static int textdump_do_panic = 1;
+SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, do_panic, CTLFLAG_RW,
+ &textdump_do_panic, 0, "Dump kernel panic message in textdump");
+
+static int textdump_do_version = 1;
+SYSCTL_INT(_debug_ddb_textdump, OID_AUTO, do_version, CTLFLAG_RW,
+ &textdump_do_version, 0, "Dump kernel version string in textdump");
+
+/*
+ * State related to incremental writing of blocks to disk.
+ */
+static off_t textdump_offset; /* Offset of next sequential write. */
+static int textdump_error; /* Carried write error, if any. */
+
+/*
+ * Statically allocate space to prepare block-sized headers and data.
+ */
+char textdump_block_buffer[TEXTDUMP_BLOCKSIZE];
+static struct kerneldumpheader kdh;
+
+/*
+ * Calculate and fill in the checksum for a ustar header.
+ */
+static void
+ustar_checksum(struct ustar_header *uhp)
+{
+ u_int sum;
+ int i;
+
+ for (i = 0; i < sizeof(uhp->uh_sum); i++)
+ uhp->uh_sum[i] = ' ';
+ sum = 0;
+ for (i = 0; i < sizeof(*uhp); i++)
+ sum += ((u_char *)uhp)[i];
+ snprintf(uhp->uh_sum, sizeof(uhp->uh_sum), "%6o", sum);
+}
+
+/*
+ * Each file in the tarball has a block-sized header with its name and other,
+ * largely hard-coded, properties.
+ */
+void
+textdump_mkustar(char *block_buffer, const char *filename, u_int size)
+{
+ struct ustar_header *uhp;
+
+#ifdef TEXTDUMP_VERBOSE
+ if (textdump_error == 0)
+ printf("textdump: creating '%s'.\n", filename);
+#endif
+ uhp = (struct ustar_header *)block_buffer;
+ bzero(uhp, sizeof(*uhp));
+ strlcpy(uhp->uh_filename, filename, sizeof(uhp->uh_filename));
+ strlcpy(uhp->uh_mode, TAR_MODE, sizeof(uhp->uh_mode));
+ snprintf(uhp->uh_size, sizeof(uhp->uh_size), "%o", size);
+ strlcpy(uhp->uh_tar_owner, TAR_UID, sizeof(uhp->uh_tar_owner));
+ strlcpy(uhp->uh_tar_group, TAR_GID, sizeof(uhp->uh_tar_group));
+ strlcpy(uhp->uh_owner, TAR_USER, sizeof(uhp->uh_owner));
+ strlcpy(uhp->uh_group, TAR_GROUP, sizeof(uhp->uh_group));
+ snprintf(uhp->uh_mtime, sizeof(uhp->uh_mtime), "%lo",
+ (unsigned long)time_second);
+ uhp->uh_type = 0;
+ strlcpy(uhp->uh_ustar, TAR_USTAR, sizeof(uhp->uh_ustar));
+ ustar_checksum(uhp);
+}
+
+/*
+ * textdump_writeblock() writes TEXTDUMP_BLOCKSIZE-sized blocks of data to
+ * the space between di->mediaoffset and di->mediaoffset + di->mediasize. It
+ * accepts an offset relative to di->mediaoffset. If we're carrying any
+ * error from previous I/O, return that error and don't continue to try to
+ * write. Most writers ignore the error and forge ahead on the basis that
+ * there's not much you can do.
+ */
+static int
+textdump_writeblock(struct dumperinfo *di, off_t offset, char *buffer)
+{
+
+ if (textdump_error)
+ return (textdump_error);
+ if (offset + TEXTDUMP_BLOCKSIZE > di->mediasize)
+ return (EIO);
+ if (offset < SIZEOF_METADATA)
+ return (ENOSPC);
+ textdump_error = dump_write(di, buffer, 0, offset + di->mediaoffset,
+ TEXTDUMP_BLOCKSIZE);
+ if (textdump_error)
+ printf("textdump_writeblock: offset %jd, error %d\n", (intmax_t)offset,
+ textdump_error);
+ return (textdump_error);
+}
+
+/*
+ * Interfaces to save and restore the dump offset, so that printers can go
+ * back to rewrite a header if required, while avoiding their knowing about
+ * the global layout of the blocks.
+ *
+ * If we ever want to support writing textdumps to tape or other
+ * stream-oriented target, we'll need to remove this.
+ */
+void
+textdump_saveoff(off_t *offsetp)
+{
+
+ *offsetp = textdump_offset;
+}
+
+void
+textdump_restoreoff(off_t offset)
+{
+
+ textdump_offset = offset;
+}
+
+/*
+ * Interface to write the "next block" relative to the current offset; since
+ * we write backwards from the end of the partition, we subtract, but there's
+ * no reason for the caller to know this.
+ */
+int
+textdump_writenextblock(struct dumperinfo *di, char *buffer)
+{
+ int error;
+
+ error = textdump_writeblock(di, textdump_offset, buffer);
+ textdump_offset -= TEXTDUMP_BLOCKSIZE;
+ return (error);
+}
+
+#ifdef INCLUDE_CONFIG_FILE
+extern char kernconfstring[];
+
+/*
+ * Dump kernel configuration.
+ */
+static void
+textdump_dump_config(struct dumperinfo *di)
+{
+ u_int count, fullblocks, len;
+
+ len = strlen(kernconfstring);
+ textdump_mkustar(textdump_block_buffer, TAR_CONFIG_FILENAME, len);
+ (void)textdump_writenextblock(di, textdump_block_buffer);
+
+ /*
+ * Write out all full blocks directly from the string, and handle any
+ * left-over bits by copying it to out to the local buffer and
+ * zero-padding it.
+ */
+ fullblocks = len / TEXTDUMP_BLOCKSIZE;
+ for (count = 0; count < fullblocks; count++)
+ (void)textdump_writenextblock(di, kernconfstring + count *
+ TEXTDUMP_BLOCKSIZE);
+ if (len % TEXTDUMP_BLOCKSIZE != 0) {
+ bzero(textdump_block_buffer, TEXTDUMP_BLOCKSIZE);
+ bcopy(kernconfstring + count * TEXTDUMP_BLOCKSIZE,
+ textdump_block_buffer, len % TEXTDUMP_BLOCKSIZE);
+ (void)textdump_writenextblock(di, textdump_block_buffer);
+ }
+}
+#endif /* INCLUDE_CONFIG_FILE */
+
+/*
+ * Dump kernel message buffer.
+ */
+static void
+textdump_dump_msgbuf(struct dumperinfo *di)
+{
+ off_t end_offset, tarhdr_offset;
+ u_int i, len, offset, seq, total_len;
+ char buf[16];
+
+ /*
+ * Write out a dummy tar header to advance the offset; we'll rewrite
+ * it later once we know the true size.
+ */
+ textdump_saveoff(&tarhdr_offset);
+ textdump_mkustar(textdump_block_buffer, TAR_MSGBUF_FILENAME, 0);
+ (void)textdump_writenextblock(di, textdump_block_buffer);
+
+ /*
+ * Copy out the data in small chunks, but don't copy nuls that may be
+ * present if the message buffer has not yet completely filled at
+ * least once.
+ */
+ total_len = 0;
+ offset = 0;
+ msgbuf_peekbytes(msgbufp, NULL, 0, &seq);
+ while ((len = msgbuf_peekbytes(msgbufp, buf, sizeof(buf), &seq)) > 0) {
+ for (i = 0; i < len; i++) {
+ if (buf[i] == '\0')
+ continue;
+ textdump_block_buffer[offset] = buf[i];
+ offset++;
+ if (offset != sizeof(textdump_block_buffer))
+ continue;
+ (void)textdump_writenextblock(di,
+ textdump_block_buffer);
+ total_len += offset;
+ offset = 0;
+ }
+ }
+ total_len += offset; /* Without the zero-padding. */
+ if (offset != 0) {
+ bzero(textdump_block_buffer + offset,
+ sizeof(textdump_block_buffer) - offset);
+ (void)textdump_writenextblock(di, textdump_block_buffer);
+ }
+
+ /*
+ * Rewrite tar header to reflect how much was actually written.
+ */
+ textdump_saveoff(&end_offset);
+ textdump_restoreoff(tarhdr_offset);
+ textdump_mkustar(textdump_block_buffer, TAR_MSGBUF_FILENAME,
+ total_len);
+ (void)textdump_writenextblock(di, textdump_block_buffer);
+ textdump_restoreoff(end_offset);
+}
+
+static void
+textdump_dump_panic(struct dumperinfo *di)
+{
+ u_int len;
+
+ /*
+ * Write out tar header -- we store up to one block of panic message.
+ */
+ len = min(strlen(panicstr), TEXTDUMP_BLOCKSIZE);
+ textdump_mkustar(textdump_block_buffer, TAR_PANIC_FILENAME, len);
+ (void)textdump_writenextblock(di, textdump_block_buffer);
+
+ /*
+ * Zero-pad the panic string and write out block.
+ */
+ bzero(textdump_block_buffer, sizeof(textdump_block_buffer));
+ bcopy(panicstr, textdump_block_buffer, len);
+ (void)textdump_writenextblock(di, textdump_block_buffer);
+}
+
+static void
+textdump_dump_version(struct dumperinfo *di)
+{
+ u_int len;
+
+ /*
+ * Write out tar header -- at most one block of version information.
+ */
+ len = min(strlen(version), TEXTDUMP_BLOCKSIZE);
+ textdump_mkustar(textdump_block_buffer, TAR_VERSION_FILENAME, len);
+ (void)textdump_writenextblock(di, textdump_block_buffer);
+
+ /*
+ * Zero pad the version string and write out block.
+ */
+ bzero(textdump_block_buffer, sizeof(textdump_block_buffer));
+ bcopy(version, textdump_block_buffer, len);
+ (void)textdump_writenextblock(di, textdump_block_buffer);
+}
+
+/*
+ * Commit text dump to disk.
+ */
+void
+textdump_dumpsys(struct dumperinfo *di)
+{
+ off_t dumplen, trailer_offset;
+
+ if (di->blocksize != TEXTDUMP_BLOCKSIZE) {
+ printf("Dump partition block size (%ju) not textdump "
+ "block size (%ju)", (uintmax_t)di->blocksize,
+ (uintmax_t)TEXTDUMP_BLOCKSIZE);
+ return;
+ }
+
+ /*
+ * We don't know a priori how large the dump will be, but we do know
+ * that we need to reserve space for metadata and that we need two
+ * dump headers. Also leave room for one ustar header and one block
+ * of data.
+ */
+ if (di->mediasize < SIZEOF_METADATA + 2 * sizeof(kdh)) {
+ printf("Insufficient space on dump partition for minimal textdump.\n");
+ return;
+ }
+ textdump_error = 0;
+
+ /*
+ * Position the start of the dump so that we'll write the kernel dump
+ * trailer immediately before the end of the partition, and then work
+ * our way back. We will rewrite this header later to reflect the
+ * true size if things go well.
+ */
+ textdump_offset = di->mediasize - sizeof(kdh);
+ textdump_saveoff(&trailer_offset);
+ mkdumpheader(&kdh, TEXTDUMPMAGIC, KERNELDUMP_TEXT_VERSION, 0, TEXTDUMP_BLOCKSIZE);
+ (void)textdump_writenextblock(di, (char *)&kdh);
+
+ /*
+ * Write a series of files in ustar format.
+ */
+ if (textdump_do_ddb)
+ db_capture_dump(di);
+#ifdef INCLUDE_CONFIG_FILE
+ if (textdump_do_config)
+ textdump_dump_config(di);
+#endif
+ if (textdump_do_msgbuf)
+ textdump_dump_msgbuf(di);
+ if (textdump_do_panic && panicstr != NULL)
+ textdump_dump_panic(di);
+ if (textdump_do_version)
+ textdump_dump_version(di);
+
+ /*
+ * Now that we know the true size, we can write out the header, then
+ * seek back to the end and rewrite the trailer with the correct
+ * size.
+ */
+ dumplen = trailer_offset - (textdump_offset + TEXTDUMP_BLOCKSIZE);
+ mkdumpheader(&kdh, TEXTDUMPMAGIC, KERNELDUMP_TEXT_VERSION, dumplen,
+ TEXTDUMP_BLOCKSIZE);
+ (void)textdump_writenextblock(di, (char *)&kdh);
+ textdump_restoreoff(trailer_offset);
+ (void)textdump_writenextblock(di, (char *)&kdh);
+
+ /*
+ * Terminate the dump, report any errors, and clear the pending flag.
+ */
+ if (textdump_error == 0)
+ (void)dump_write(di, NULL, 0, 0, 0);
+ if (textdump_error == ENOSPC)
+ printf("Textdump: Insufficient space on dump partition\n");
+ else if (textdump_error != 0)
+ printf("Textdump: Error %d writing dump\n", textdump_error);
+ else
+ printf("Textdump complete.\n");
+ textdump_pending = 0;
+}
+
+/*-
+ * DDB(4) command to manage textdumps:
+ *
+ * textdump set - request a textdump
+ * textdump status - print DDB output textdump status
+ * textdump unset - clear textdump request
+ */
+static void
+db_textdump_usage(void)
+{
+
+ db_printf("textdump [unset|set|status|dump]\n");
+}
+
+void
+db_textdump_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count,
+ char *modif)
+{
+ int t;
+
+ t = db_read_token();
+ if (t != tIDENT) {
+ db_textdump_usage();
+ return;
+ }
+ if (db_read_token() != tEOL) {
+ db_textdump_usage();
+ return;
+ }
+ if (strcmp(db_tok_string, "set") == 0) {
+ textdump_pending = 1;
+ db_printf("textdump set\n");
+ } else if (strcmp(db_tok_string, "status") == 0) {
+ if (textdump_pending)
+ db_printf("textdump is set\n");
+ else
+ db_printf("textdump is not set\n");
+ } else if (strcmp(db_tok_string, "unset") == 0) {
+ textdump_pending = 0;
+ db_printf("textdump unset\n");
+ } else if (strcmp(db_tok_string, "dump") == 0) {
+ textdump_pending = 1;
+ doadump(TRUE);
+ } else {
+ db_textdump_usage();
+ }
+}
diff --git a/sys/ddb/db_thread.c b/sys/ddb/db_thread.c
new file mode 100644
index 0000000..f6712a0
--- /dev/null
+++ b/sys/ddb/db_thread.c
@@ -0,0 +1,172 @@
+/*-
+ * Copyright (c) 2004 Marcel Moolenaar
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kdb.h>
+#include <sys/proc.h>
+
+#include <machine/pcb.h>
+
+#include <ddb/ddb.h>
+#include <ddb/db_command.h>
+#include <ddb/db_sym.h>
+
+void
+db_print_thread(void)
+{
+ pid_t pid;
+
+ pid = -1;
+ if (kdb_thread->td_proc != NULL)
+ pid = kdb_thread->td_proc->p_pid;
+ db_printf("[ thread pid %d tid %ld ]\n", pid, (long)kdb_thread->td_tid);
+}
+
+void
+db_set_thread(db_expr_t tid, boolean_t hastid, db_expr_t cnt, char *mod)
+{
+ struct thread *thr;
+ db_expr_t radix;
+ int err;
+
+ /*
+ * We parse our own arguments. We don't like the default radix.
+ */
+ radix = db_radix;
+ db_radix = 10;
+ hastid = db_expression(&tid);
+ db_radix = radix;
+ db_skip_to_eol();
+
+ if (hastid) {
+ thr = kdb_thr_lookup(tid);
+ if (thr != NULL) {
+ err = kdb_thr_select(thr);
+ if (err != 0) {
+ db_printf("unable to switch to thread %ld\n",
+ (long)thr->td_tid);
+ return;
+ }
+ db_dot = PC_REGS();
+ } else {
+ db_printf("%d: invalid thread\n", (int)tid);
+ return;
+ }
+ }
+
+ db_print_thread();
+ db_print_loc_and_inst(PC_REGS());
+}
+
+void
+db_show_threads(db_expr_t addr, boolean_t hasaddr, db_expr_t cnt, char *mod)
+{
+ jmp_buf jb;
+ void *prev_jb;
+ struct thread *thr;
+
+ thr = kdb_thr_first();
+ while (!db_pager_quit && thr != NULL) {
+ db_printf(" %6ld (%p) (stack %p) ", (long)thr->td_tid, thr,
+ (void *)thr->td_kstack);
+ prev_jb = kdb_jmpbuf(jb);
+ if (setjmp(jb) == 0) {
+ if (db_trace_thread(thr, 1) != 0)
+ db_printf("***\n");
+ }
+ kdb_jmpbuf(prev_jb);
+ thr = kdb_thr_next(thr);
+ }
+}
+
+/*
+ * Lookup a thread based on a db expression address. We assume that the
+ * address was parsed in hexadecimal. We reparse the address in decimal
+ * first and try to treat it as a thread ID to find an associated thread.
+ * If that fails and check_pid is true, we treat the decimal value as a
+ * PID. If that matches a process, we return the first thread in that
+ * process. Otherwise, we treat the addr as a pointer to a thread.
+ */
+struct thread *
+db_lookup_thread(db_expr_t addr, boolean_t check_pid)
+{
+ struct thread *td;
+ db_expr_t decaddr;
+ struct proc *p;
+
+ /*
+ * If the parsed address was not a valid decimal expression,
+ * assume it is a thread pointer.
+ */
+ decaddr = db_hex2dec(addr);
+ if (decaddr == -1)
+ return ((struct thread *)addr);
+
+ td = kdb_thr_lookup(decaddr);
+ if (td != NULL)
+ return (td);
+ if (check_pid) {
+ FOREACH_PROC_IN_SYSTEM(p) {
+ if (p->p_pid == decaddr)
+ return (FIRST_THREAD_IN_PROC(p));
+ }
+ LIST_FOREACH(p, &zombproc, p_list) {
+ if (p->p_pid == decaddr)
+ return (FIRST_THREAD_IN_PROC(p));
+ }
+ }
+ return ((struct thread *)addr);
+}
+
+/*
+ * Lookup a process based on a db expression address. We assume that the
+ * address was parsed in hexadecimal. We reparse the address in decimal
+ * first and try to treat it as a PID to find an associated process.
+ * If that fails we treat the addr as a pointer to a process.
+ */
+struct proc *
+db_lookup_proc(db_expr_t addr)
+{
+ db_expr_t decaddr;
+ struct proc *p;
+
+ decaddr = db_hex2dec(addr);
+ if (decaddr != -1) {
+ FOREACH_PROC_IN_SYSTEM(p) {
+ if (p->p_pid == decaddr)
+ return (p);
+ }
+ LIST_FOREACH(p, &zombproc, p_list) {
+ if (p->p_pid == decaddr)
+ return (p);
+ }
+ }
+ return ((struct proc *)addr);
+}
diff --git a/sys/ddb/db_variables.c b/sys/ddb/db_variables.c
new file mode 100644
index 0000000..69c11ae
--- /dev/null
+++ b/sys/ddb/db_variables.c
@@ -0,0 +1,159 @@
+/*-
+ * 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
+ * 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 the
+ * rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <ddb/ddb.h>
+#include <ddb/db_lex.h>
+#include <ddb/db_variables.h>
+
+static int db_find_variable(struct db_variable **varp);
+
+static struct db_variable db_vars[] = {
+ { "radix", &db_radix, FCN_NULL },
+ { "maxoff", &db_maxoff, FCN_NULL },
+ { "maxwidth", &db_max_width, FCN_NULL },
+ { "tabstops", &db_tab_stop_width, FCN_NULL },
+ { "lines", &db_lines_per_page, FCN_NULL },
+ { "curcpu", NULL, db_var_curcpu },
+ { "db_cpu", NULL, db_var_db_cpu },
+#ifdef VIMAGE
+ { "curvnet", NULL, db_var_curvnet },
+ { "db_vnet", NULL, db_var_db_vnet },
+#endif
+};
+static struct db_variable *db_evars =
+ db_vars + sizeof(db_vars)/sizeof(db_vars[0]);
+
+static int
+db_find_variable(struct db_variable **varp)
+{
+ struct db_variable *vp;
+ int t;
+
+ t = db_read_token();
+ if (t == tIDENT) {
+ for (vp = db_vars; vp < db_evars; vp++) {
+ if (!strcmp(db_tok_string, vp->name)) {
+ *varp = vp;
+ return (1);
+ }
+ }
+ for (vp = db_regs; vp < db_eregs; vp++) {
+ if (!strcmp(db_tok_string, vp->name)) {
+ *varp = vp;
+ return (1);
+ }
+ }
+ }
+ db_error("Unknown variable\n");
+ return (0);
+}
+
+int
+db_get_variable(db_expr_t *valuep)
+{
+ struct db_variable *vp;
+
+ if (!db_find_variable(&vp))
+ return (0);
+
+ return (db_read_variable(vp, valuep));
+}
+
+int
+db_set_variable(db_expr_t value)
+{
+ struct db_variable *vp;
+
+ if (!db_find_variable(&vp))
+ return (0);
+
+ return (db_write_variable(vp, value));
+}
+
+int
+db_read_variable(struct db_variable *vp, db_expr_t *valuep)
+{
+ db_varfcn_t *func = vp->fcn;
+
+ if (func == FCN_NULL) {
+ *valuep = *(vp->valuep);
+ return (1);
+ }
+ return ((*func)(vp, valuep, DB_VAR_GET));
+}
+
+int
+db_write_variable(struct db_variable *vp, db_expr_t value)
+{
+ db_varfcn_t *func = vp->fcn;
+
+ if (func == FCN_NULL) {
+ *(vp->valuep) = value;
+ return (1);
+ }
+ return ((*func)(vp, &value, DB_VAR_SET));
+}
+
+void
+db_set_cmd(db_expr_t dummy1, boolean_t dummy2, db_expr_t dummy3, char *dummy4)
+{
+ struct db_variable *vp;
+ db_expr_t value;
+ int t;
+
+ t = db_read_token();
+ if (t != tDOLLAR) {
+ db_error("Unknown variable\n");
+ return;
+ }
+ if (!db_find_variable(&vp)) {
+ 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 (db_read_token() != tEOL)
+ db_error("?\n");
+
+ db_write_variable(vp, value);
+}
diff --git a/sys/ddb/db_variables.h b/sys/ddb/db_variables.h
new file mode 100644
index 0000000..42a3bf9
--- /dev/null
+++ b/sys/ddb/db_variables.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
+ * 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 the
+ * rights to redistribute these changes.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+
+#ifndef _DDB_DB_VARIABLES_H_
+#define _DDB_DB_VARIABLES_H_
+
+/*
+ * Debugger variables.
+ */
+struct db_variable;
+typedef int db_varfcn_t(struct db_variable *vp, db_expr_t *valuep, int op);
+struct db_variable {
+ char *name; /* Name of variable */
+ db_expr_t *valuep; /* value of variable */
+ /* function to call when reading/writing */
+ db_varfcn_t *fcn;
+#define DB_VAR_GET 0
+#define DB_VAR_SET 1
+};
+#define FCN_NULL ((db_varfcn_t *)0)
+
+extern struct db_variable db_regs[]; /* machine registers */
+extern struct db_variable *db_eregs;
+
+extern db_varfcn_t db_var_curcpu; /* DPCPU default CPU */
+extern db_varfcn_t db_var_curvnet; /* Default vnet */
+extern db_varfcn_t db_var_db_cpu; /* DPCPU active CPU */
+extern db_varfcn_t db_var_db_vnet; /* Active vnet */
+
+int db_read_variable(struct db_variable *, db_expr_t *);
+int db_write_variable(struct db_variable *, db_expr_t);
+
+#endif /* _!DDB_DB_VARIABLES_H_ */
diff --git a/sys/ddb/db_watch.c b/sys/ddb/db_watch.c
new file mode 100644
index 0000000..b2be970
--- /dev/null
+++ b/sys/ddb/db_watch.c
@@ -0,0 +1,331 @@
+/*-
+ * 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
+ * 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 the
+ * rights to redistribute these changes.
+ */
+/*
+ * Author: Richard P. Draves, Carnegie Mellon University
+ * Date: 10/90
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/proc.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <vm/vm_map.h>
+
+#include <ddb/ddb.h>
+#include <ddb/db_watch.h>
+
+/*
+ * Watchpoints.
+ */
+
+static boolean_t db_watchpoints_inserted = TRUE;
+
+#define NWATCHPOINTS 100
+static struct db_watchpoint db_watch_table[NWATCHPOINTS];
+static db_watchpoint_t db_next_free_watchpoint = &db_watch_table[0];
+static db_watchpoint_t db_free_watchpoints = 0;
+static db_watchpoint_t db_watchpoint_list = 0;
+
+static db_watchpoint_t db_watchpoint_alloc(void);
+static void db_watchpoint_free(db_watchpoint_t watch);
+static void db_delete_watchpoint(vm_map_t map, db_addr_t addr);
+#ifdef notused
+static boolean_t db_find_watchpoint(vm_map_t map, db_addr_t addr,
+ db_regs_t *regs);
+#endif
+static void db_list_watchpoints(void);
+static void db_set_watchpoint(vm_map_t map, db_addr_t addr,
+ vm_size_t size);
+
+static 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);
+}
+
+static void
+db_watchpoint_free(watch)
+ register db_watchpoint_t watch;
+{
+ watch->link = db_free_watchpoints;
+ db_free_watchpoints = watch;
+}
+
+static void
+db_set_watchpoint(map, addr, size)
+ vm_map_t map;
+ db_addr_t addr;
+ vm_size_t size;
+{
+ register db_watchpoint_t watch;
+
+ if (map == NULL) {
+ db_printf("No map.\n");
+ return;
+ }
+
+ /*
+ * Should we do anything fancy with overlapping regions?
+ */
+
+ for (watch = db_watchpoint_list;
+ watch != 0;
+ watch = watch->link)
+ if (db_map_equal(watch->map, map) &&
+ (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->map = map;
+ watch->loaddr = addr;
+ watch->hiaddr = addr+size;
+
+ watch->link = db_watchpoint_list;
+ db_watchpoint_list = watch;
+
+ db_watchpoints_inserted = FALSE;
+}
+
+static void
+db_delete_watchpoint(map, addr)
+ vm_map_t map;
+ 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 (db_map_equal(watch->map, map) &&
+ (watch->loaddr <= addr) &&
+ (addr < watch->hiaddr)) {
+ *prev = watch->link;
+ db_watchpoint_free(watch);
+ return;
+ }
+
+ db_printf("Not set.\n");
+}
+
+static void
+db_list_watchpoints()
+{
+ register db_watchpoint_t watch;
+
+ if (db_watchpoint_list == 0) {
+ db_printf("No watchpoints set\n");
+ return;
+ }
+
+#ifdef __LP64__
+ db_printf(" Map Address Size\n");
+#else
+ db_printf(" Map Address Size\n");
+#endif
+ for (watch = db_watchpoint_list;
+ watch != 0;
+ watch = watch->link)
+#ifdef __LP64__
+ db_printf("%s%16p %16lx %lx\n",
+#else
+ db_printf("%s%8p %8lx %lx\n",
+#endif
+ db_map_current(watch->map) ? "*" : " ",
+ (void *)watch->map, (long)watch->loaddr,
+ (long)watch->hiaddr - (long)watch->loaddr);
+}
+
+/* Delete watchpoint */
+/*ARGSUSED*/
+void
+db_deletewatch_cmd(addr, have_addr, count, modif)
+ db_expr_t addr;
+ boolean_t have_addr;
+ db_expr_t count;
+ char * modif;
+{
+ db_delete_watchpoint(db_map_addr(addr), addr);
+}
+
+/* Set watchpoint */
+/*ARGSUSED*/
+void
+db_watchpoint_cmd(addr, have_addr, count, modif)
+ db_expr_t addr;
+ boolean_t have_addr;
+ db_expr_t count;
+ char * modif;
+{
+ vm_size_t size;
+ db_expr_t value;
+
+ if (db_expression(&value))
+ size = (vm_size_t) value;
+ else
+ size = 4;
+ db_skip_to_eol();
+
+ db_set_watchpoint(db_map_addr(addr), addr, size);
+}
+
+/*
+ * At least one non-optional show-command must be implemented using
+ * DB_SHOW_COMMAND() so that db_show_cmd_set gets created. Here is one.
+ */
+DB_SHOW_COMMAND(watches, db_listwatch_cmd)
+{
+ db_list_watchpoints();
+ db_md_list_watchpoints();
+}
+
+void
+db_set_watchpoints()
+{
+ register db_watchpoint_t watch;
+
+ if (!db_watchpoints_inserted) {
+ for (watch = db_watchpoint_list;
+ watch != 0;
+ watch = watch->link)
+ pmap_protect(watch->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;
+}
+
+#ifdef notused
+static 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;
+
+ for (watch = db_watchpoint_list;
+ watch != 0;
+ watch = watch->link)
+ if (db_map_equal(watch->map, map)) {
+ 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);
+ }
+
+ return (FALSE);
+}
+#endif
+
+
+
+/* Delete hardware watchpoint */
+/*ARGSUSED*/
+void
+db_deletehwatch_cmd(addr, have_addr, count, modif)
+ db_expr_t addr;
+ boolean_t have_addr;
+ db_expr_t count;
+ char * modif;
+{
+ int rc;
+
+ if (count < 0)
+ count = 4;
+
+ rc = db_md_clr_watchpoint(addr, count);
+ if (rc < 0)
+ db_printf("hardware watchpoint could not be deleted\n");
+}
+
+/* Set hardware watchpoint */
+/*ARGSUSED*/
+void
+db_hwatchpoint_cmd(addr, have_addr, count, modif)
+ db_expr_t addr;
+ boolean_t have_addr;
+ db_expr_t count;
+ char * modif;
+{
+ int rc;
+
+ if (count < 0)
+ count = 4;
+
+ rc = db_md_set_watchpoint(addr, count);
+ if (rc < 0)
+ db_printf("hardware watchpoint could not be set\n");
+}
diff --git a/sys/ddb/db_watch.h b/sys/ddb/db_watch.h
new file mode 100644
index 0000000..7c48ce8
--- /dev/null
+++ b/sys/ddb/db_watch.h
@@ -0,0 +1,48 @@
+/*-
+ * 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
+ * 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 the
+ * rights to redistribute these changes.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 10/90
+ */
+
+#ifndef _DDB_DB_WATCH_H_
+#define _DDB_DB_WATCH_H_
+
+/*
+ * Watchpoint.
+ */
+
+typedef struct db_watchpoint {
+ vm_map_t map; /* 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;
+
+#endif /* !_DDB_DB_WATCH_H_ */
diff --git a/sys/ddb/db_write_cmd.c b/sys/ddb/db_write_cmd.c
new file mode 100644
index 0000000..64d3473
--- /dev/null
+++ b/sys/ddb/db_write_cmd.c
@@ -0,0 +1,96 @@
+/*-
+ * 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
+ * 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 the
+ * rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <ddb/ddb.h>
+#include <ddb/db_access.h>
+#include <ddb/db_command.h>
+#include <ddb/db_sym.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;
+
+ addr = (db_addr_t) address;
+
+ switch (modif[0]) {
+ case 'b':
+ size = 1;
+ break;
+ case 'h':
+ size = 2;
+ break;
+ case 'l':
+ case '\0':
+ size = 4;
+ break;
+ default:
+ db_error("Unknown size\n");
+ return;
+ }
+
+ while (db_expression(&new_value)) {
+ old_value = db_get_value(addr, size, FALSE);
+ db_printsym(addr, DB_STGY_ANY);
+ db_printf("\t\t%#8lr\t=\t%#8lr\n", (long)old_value,(long)new_value);
+ db_put_value(addr, size, new_value);
+ addr += size;
+
+ wrote_one = TRUE;
+ }
+
+ if (!wrote_one)
+ db_error("Nothing written.\n");
+
+ db_next = addr;
+ db_prev = addr - size;
+
+ db_skip_to_eol();
+}
+
diff --git a/sys/ddb/ddb.h b/sys/ddb/ddb.h
new file mode 100644
index 0000000..93bf713
--- /dev/null
+++ b/sys/ddb/ddb.h
@@ -0,0 +1,287 @@
+/*-
+ * Copyright (c) 1993, Garrett A. Wollman.
+ * Copyright (c) 1993, University of Vermont and State Agricultural College.
+ * 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. 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 AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Necessary declarations for the `ddb' kernel debugger.
+ */
+
+#ifndef _DDB_DDB_H_
+#define _DDB_DDB_H_
+
+#ifdef SYSCTL_DECL
+SYSCTL_DECL(_debug_ddb);
+#endif
+
+#include <machine/db_machdep.h> /* type definitions */
+
+#include <sys/queue.h> /* LIST_* */
+#include <sys/kernel.h> /* SYSINIT */
+
+#ifndef DB_MAXARGS
+#define DB_MAXARGS 10
+#endif
+
+#ifndef DB_MAXLINE
+#define DB_MAXLINE 120
+#endif
+
+#ifndef DB_MAXSCRIPTS
+#define DB_MAXSCRIPTS 8
+#endif
+
+#ifndef DB_MAXSCRIPTNAME
+#define DB_MAXSCRIPTNAME 32
+#endif
+
+#ifndef DB_MAXSCRIPTLEN
+#define DB_MAXSCRIPTLEN 128
+#endif
+
+#ifndef DB_MAXSCRIPTRECURSION
+#define DB_MAXSCRIPTRECURSION 3
+#endif
+
+#ifndef DB_CALL
+#define DB_CALL db_fncall_generic
+#else
+int DB_CALL(db_expr_t, db_expr_t *, int, db_expr_t[]);
+#endif
+
+/*
+ * There are three "command tables":
+ * - One for simple commands; a list of these is displayed
+ * by typing 'help' at the debugger prompt.
+ * - One for sub-commands of 'show'; to see this type 'show'
+ * without any arguments.
+ * - The last one for sub-commands of 'show all'; type 'show all'
+ * without any argument to get a list.
+ */
+struct command;
+LIST_HEAD(command_table, command);
+extern struct command_table db_cmd_table;
+extern struct command_table db_show_table;
+extern struct command_table db_show_all_table;
+
+/*
+ * Type signature for a function implementing a ddb command.
+ */
+typedef void db_cmdfcn_t(db_expr_t addr, boolean_t have_addr, db_expr_t count,
+ char *modif);
+
+/*
+ * Command table entry.
+ */
+struct command {
+ char * name; /* command name */
+ db_cmdfcn_t *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 command_table *more; /* another level of command */
+ LIST_ENTRY(command) next; /* next entry in the command table */
+};
+
+/*
+ * Arrange for the specified ddb command to be defined and
+ * bound to the specified function. Commands can be defined
+ * in modules in which case they will be available only when
+ * the module is loaded.
+ */
+#define _DB_SET(_suffix, _name, _func, list, _flag, _more) \
+static struct command __CONCAT(_name,_suffix) = { \
+ .name = __STRING(_name), \
+ .fcn = _func, \
+ .flag = _flag, \
+ .more = _more \
+}; \
+static void __CONCAT(__CONCAT(_name,_suffix),_add)(void *arg __unused) \
+ { db_command_register(&list, &__CONCAT(_name,_suffix)); } \
+SYSINIT(__CONCAT(_name,_suffix), SI_SUB_KLD, SI_ORDER_ANY, \
+ __CONCAT(__CONCAT(_name,_suffix),_add), NULL); \
+static void __CONCAT(__CONCAT(_name,_suffix),_del)(void *arg __unused) \
+ { db_command_unregister(&list, &__CONCAT(_name,_suffix)); } \
+SYSUNINIT(__CONCAT(_name,_suffix), SI_SUB_KLD, SI_ORDER_ANY, \
+ __CONCAT(__CONCAT(_name,_suffix),_del), NULL);
+
+/*
+ * Like _DB_SET but also create the function declaration which
+ * must be followed immediately by the body; e.g.
+ * _DB_FUNC(_cmd, panic, db_panic, db_cmd_table, 0, NULL)
+ * {
+ * ...panic implementation...
+ * }
+ *
+ * This macro is mostly used to define commands placed in one of
+ * the ddb command tables; see DB_COMMAND, etc. below.
+ */
+#define _DB_FUNC(_suffix, _name, _func, list, _flag, _more) \
+static db_cmdfcn_t _func; \
+_DB_SET(_suffix, _name, _func, list, _flag, _more); \
+static void \
+_func(db_expr_t addr, boolean_t have_addr, db_expr_t count, char *modif)
+
+/* common idom provided for backwards compatibility */
+#define DB_FUNC(_name, _func, list, _flag, _more) \
+ _DB_FUNC(_cmd, _name, _func, list, _flag, _more)
+
+#define DB_COMMAND(cmd_name, func_name) \
+ _DB_FUNC(_cmd, cmd_name, func_name, db_cmd_table, 0, NULL)
+#define DB_ALIAS(alias_name, func_name) \
+ _DB_SET(_cmd, alias_name, func_name, db_cmd_table, 0, NULL)
+#define DB_SHOW_COMMAND(cmd_name, func_name) \
+ _DB_FUNC(_show, cmd_name, func_name, db_show_table, 0, NULL)
+#define DB_SHOW_ALIAS(alias_name, func_name) \
+ _DB_SET(_show, alias_name, func_name, db_show_table, 0, NULL)
+#define DB_SHOW_ALL_COMMAND(cmd_name, func_name) \
+ _DB_FUNC(_show_all, cmd_name, func_name, db_show_all_table, 0, NULL)
+#define DB_SHOW_ALL_ALIAS(alias_name, func_name) \
+ _DB_SET(_show_all, alias_name, func_name, db_show_all_table, 0, NULL)
+
+extern db_expr_t db_maxoff;
+extern int db_indent;
+extern int db_inst_count;
+extern int db_load_count;
+extern int db_store_count;
+extern volatile int db_pager_quit;
+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_lines_per_page;
+
+struct thread;
+struct vm_map;
+
+void db_check_interrupt(void);
+void db_clear_watchpoints(void);
+db_addr_t db_disasm(db_addr_t loc, boolean_t altfmt);
+ /* instruction disassembler */
+void db_error(const char *s);
+int db_expression(db_expr_t *valuep);
+int db_get_variable(db_expr_t *valuep);
+void db_iprintf(const char *,...) __printflike(1, 2);
+struct proc *db_lookup_proc(db_expr_t addr);
+struct thread *db_lookup_thread(db_expr_t addr, boolean_t check_pid);
+struct vm_map *db_map_addr(vm_offset_t);
+boolean_t db_map_current(struct vm_map *);
+boolean_t db_map_equal(struct vm_map *, struct vm_map *);
+int db_md_set_watchpoint(db_expr_t addr, db_expr_t size);
+int db_md_clr_watchpoint(db_expr_t addr, db_expr_t size);
+void db_md_list_watchpoints(void);
+void db_print_loc_and_inst(db_addr_t loc);
+void db_print_thread(void);
+int db_printf(const char *fmt, ...) __printflike(1, 2);
+int db_read_bytes(vm_offset_t addr, size_t size, char *data);
+ /* machine-dependent */
+int db_readline(char *lstart, int lsize);
+void db_restart_at_pc(boolean_t watchpt);
+int db_set_variable(db_expr_t value);
+void db_set_watchpoints(void);
+void db_skip_to_eol(void);
+boolean_t db_stop_at_pc(boolean_t *is_breakpoint);
+#define db_strcpy strcpy
+void db_trace_self(void);
+int db_trace_thread(struct thread *, int);
+int db_value_of_name(const char *name, db_expr_t *valuep);
+int db_value_of_name_pcpu(const char *name, db_expr_t *valuep);
+int db_value_of_name_vnet(const char *name, db_expr_t *valuep);
+int db_write_bytes(vm_offset_t addr, size_t size, char *data);
+void db_command_register(struct command_table *, struct command *);
+void db_command_unregister(struct command_table *, struct command *);
+
+db_cmdfcn_t db_breakpoint_cmd;
+db_cmdfcn_t db_capture_cmd;
+db_cmdfcn_t db_continue_cmd;
+db_cmdfcn_t db_delete_cmd;
+db_cmdfcn_t db_deletehwatch_cmd;
+db_cmdfcn_t db_deletewatch_cmd;
+db_cmdfcn_t db_examine_cmd;
+db_cmdfcn_t db_findstack_cmd;
+db_cmdfcn_t db_hwatchpoint_cmd;
+db_cmdfcn_t db_listbreak_cmd;
+db_cmdfcn_t db_scripts_cmd;
+db_cmdfcn_t db_print_cmd;
+db_cmdfcn_t db_ps;
+db_cmdfcn_t db_run_cmd;
+db_cmdfcn_t db_script_cmd;
+db_cmdfcn_t db_search_cmd;
+db_cmdfcn_t db_set_cmd;
+db_cmdfcn_t db_set_thread;
+db_cmdfcn_t db_show_regs;
+db_cmdfcn_t db_show_threads;
+db_cmdfcn_t db_single_step_cmd;
+db_cmdfcn_t db_textdump_cmd;
+db_cmdfcn_t db_trace_until_call_cmd;
+db_cmdfcn_t db_trace_until_matching_cmd;
+db_cmdfcn_t db_unscript_cmd;
+db_cmdfcn_t db_watchpoint_cmd;
+db_cmdfcn_t db_write_cmd;
+
+/*
+ * Interface between DDB and the DDB output capture facility.
+ */
+struct dumperinfo;
+void db_capture_dump(struct dumperinfo *di);
+void db_capture_enterpager(void);
+void db_capture_exitpager(void);
+void db_capture_write(char *buffer, u_int buflen);
+void db_capture_writech(char ch);
+
+/*
+ * Interface between DDB and the script facility.
+ */
+void db_script_kdbenter(const char *eventname); /* KDB enter event. */
+
+/*
+ * Interface between DDB and the textdump facility.
+ *
+ * Text dump blocks are of a fixed size; textdump_block_buffer is a
+ * statically allocated buffer that code interacting with textdumps can use
+ * to prepare and hold a pending block in when calling writenextblock().
+ */
+#define TEXTDUMP_BLOCKSIZE 512
+extern char textdump_block_buffer[TEXTDUMP_BLOCKSIZE];
+
+void textdump_mkustar(char *block_buffer, const char *filename,
+ u_int size);
+void textdump_restoreoff(off_t offset);
+void textdump_saveoff(off_t *offsetp);
+int textdump_writenextblock(struct dumperinfo *di, char *buffer);
+
+/*
+ * Interface between the kernel and textdumps.
+ */
+extern int textdump_pending; /* Call textdump_dumpsys() instead. */
+void textdump_dumpsys(struct dumperinfo *di);
+
+#endif /* !_DDB_DDB_H_ */
OpenPOWER on IntegriCloud