From 7d0c6c9fc5e79c4b9d4da498c0eb7af073d990da Mon Sep 17 00:00:00 2001 From: Sam Leffler Date: Thu, 23 Mar 2006 23:06:14 +0000 Subject: [PATCH] add support for copying console messages to a remote gdb Reviewed by: kan --- sys/conf/files | 1 + sys/gdb/gdb_cons.c | 172 +++++++++++++++++++++++++++++++++++++++++++++ sys/gdb/gdb_int.h | 3 + sys/gdb/gdb_main.c | 12 +++- 4 files changed, 186 insertions(+), 2 deletions(-) create mode 100644 sys/gdb/gdb_cons.c diff --git a/sys/conf/files b/sys/conf/files index 1c38cb7fa197..688b826c4fbd 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1088,6 +1088,7 @@ fs/umapfs/umap_vnops.c optional umapfs fs/unionfs/union_subr.c optional unionfs fs/unionfs/union_vfsops.c optional unionfs fs/unionfs/union_vnops.c optional unionfs +gdb/gdb_cons.c optional gdb gdb/gdb_main.c optional gdb gdb/gdb_packet.c optional gdb geom/bde/g_bde.c optional geom_bde diff --git a/sys/gdb/gdb_cons.c b/sys/gdb/gdb_cons.c new file mode 100644 index 000000000000..750b031f1443 --- /dev/null +++ b/sys/gdb/gdb_cons.c @@ -0,0 +1,172 @@ +/*- + * Copyright (c) 2006 Sam Leffler + * 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 AUTHORS ``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 AUTHORS 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. + */ + +/* + * Support for redirecting console msgs to gdb. We register + * a pseudo console to hook cnputc and send stuff to the gdb + * port. The only trickiness here is buffering output so this + * isn't dog slow. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +struct gdbcons { + int npending; + /* /2 for hex conversion, -6 for protocol glue */ + char buf[GDB_BUFSZ/2 - 6]; + struct callout flush; +}; +static struct gdbcons state = { -1 }; + +static int gdbcons_enable = 0; +SYSCTL_INT(_debug, OID_AUTO, gdbcons, CTLFLAG_RW, &gdbcons_enable, + 0, "copy console messages to gdb"); +TUNABLE_INT("debug.gdbcons", &gdbcons_enable); + +static void +gdb_cnprobe(struct consdev *cp) +{ + sprintf(cp->cn_name, "gdb"); + cp->cn_pri = CN_LOW; /* XXX no way to say "write only" */ +} + +static void +gdb_cninit(struct consdev *cp) +{ + struct gdbcons *c = &state; + + /* setup tx buffer and callout */ + if (c->npending == -1) { + c->npending = 0; + callout_init(&c->flush, CALLOUT_MPSAFE); + cp->cn_arg = c; + } +} + +static int +gdb_nogetc(struct consdev *cp) +{ + return -1; +} + +static void +gdb_tx_puthex(int c) +{ + const char *hex = "0123456789abcdef"; + + gdb_tx_char(hex[(c>>4)&0xf]); + gdb_tx_char(hex[(c>>0)&0xf]); +} + +static void +gdb_cnflush(void *arg) +{ + struct gdbcons *gc = arg; + int i; + + gdb_tx_begin('O'); + for (i = 0; i < gc->npending; i++) + gdb_tx_puthex(gc->buf[i]); + gdb_tx_end(); + gc->npending = 0; +} + +/* + * This glop is to figure out when it's safe to use callouts + * to defer buffer flushing. There's probably a better way + * and/or an earlier point in the boot process when it's ok. + */ +static int calloutok = 0; +static void +oktousecallout(void *data __unused) +{ + calloutok = 1; +} +SYSINIT(gdbhack, SI_SUB_RUN_SCHEDULER, SI_ORDER_ANY, oktousecallout, NULL) + +static void +gdb_cnputc(struct consdev *cp, int c) +{ + struct gdbcons *gc; + + if (gdbcons_enable && gdb_cur != NULL && gdb_listening) { + gc = cp->cn_arg; + if (gc->npending != 0) { + /* + * Cancel any pending callout and flush the + * buffer if there's no space for this byte. + */ + if (calloutok) + callout_stop(&gc->flush); + if (gc->npending == sizeof(gc->buf)) + gdb_cnflush(gc); + } + gc->buf[gc->npending++] = c; + /* + * Flush on end of line; this is especially helpful + * during boot when we don't have callouts to flush + * the buffer. Otherwise we defer flushing; a 1/4 + * second is a guess. + */ + if (c == '\n') + gdb_cnflush(gc); + else if (calloutok) + callout_reset(&gc->flush, hz/4, gdb_cnflush, gc); + } +} + +/* NB: no get interface, we supply nogetc for checkc too */ +CONS_DRIVER(gdb, gdb_cnprobe, gdb_cninit, NULL, gdb_nogetc, gdb_nogetc, + gdb_cnputc, NULL); + +/* + * Our console device only gets attached if the system is booted + * with RB_MULTIPLE set so gdb_init also calls us to attach the + * console so we're setup regardless. + */ +void +gdb_consinit(void) +{ + gdb_cnprobe(&gdb_consdev); + gdb_cninit(&gdb_consdev); + cnadd(&gdb_consdev); +} diff --git a/sys/gdb/gdb_int.h b/sys/gdb/gdb_int.h index c9e1bdcb117e..d06943a2a002 100644 --- a/sys/gdb/gdb_int.h +++ b/sys/gdb/gdb_int.h @@ -31,6 +31,9 @@ extern struct gdb_dbgport *gdb_cur; +extern int gdb_listening; +void gdb_consinit(void); + extern char *gdb_rxp; extern size_t gdb_rxsz; extern char *gdb_txp; diff --git a/sys/gdb/gdb_main.c b/sys/gdb/gdb_main.c index 9e361c811046..813d6b7173b4 100644 --- a/sys/gdb/gdb_main.c +++ b/sys/gdb/gdb_main.c @@ -50,6 +50,7 @@ GDB_DBGPORT(null, NULL, NULL, NULL, NULL, NULL, NULL); SET_DECLARE(gdb_dbgport_set, struct gdb_dbgport); struct gdb_dbgport *gdb_cur = NULL; +int gdb_listening = 0; static int gdb_init(void) @@ -82,9 +83,10 @@ gdb_init(void) gdb_cur->gdb_init(); printf("GDB: current port: %s\n", gdb_cur->gdb_name); } - if (gdb_cur != NULL) + if (gdb_cur != NULL) { cur_pri = (boothowto & RB_GDB) ? 2 : 0; - else + gdb_consinit(); + } else cur_pri = -1; return (cur_pri); } @@ -94,6 +96,7 @@ gdb_trap(int type, int code) { struct thread *thr_iter; + gdb_listening = 0; /* * Send a T packet. We currently do not support watchpoints (the * awatch, rwatch or watch elements). @@ -126,6 +129,7 @@ gdb_trap(int type, int code) gdb_cpu_setreg(GDB_REG_PC, &pc); } kdb_cpu_clear_singlestep(); + gdb_listening = 1; return (1); } case 'C': { /* Continue with signal. */ @@ -137,6 +141,7 @@ gdb_trap(int type, int code) gdb_cpu_setreg(GDB_REG_PC, &pc); } kdb_cpu_clear_singlestep(); + gdb_listening = 1; return (1); } case 'g': { /* Read registers. */ @@ -171,6 +176,7 @@ gdb_trap(int type, int code) } case 'k': /* Kill request. */ kdb_cpu_clear_singlestep(); + gdb_listening = 1; return (1); case 'm': { /* Read memory. */ uintmax_t addr, size; @@ -243,6 +249,7 @@ gdb_trap(int type, int code) gdb_cpu_setreg(GDB_REG_PC, &pc); } kdb_cpu_set_singlestep(); + gdb_listening = 1; return (1); } case 'S': { /* Step with signal. */ @@ -254,6 +261,7 @@ gdb_trap(int type, int code) gdb_cpu_setreg(GDB_REG_PC, &pc); } kdb_cpu_set_singlestep(); + gdb_listening = 1; return (1); } case 'T': { /* Thread alive. */