diff options
author | sam <sam@FreeBSD.org> | 2006-03-23 23:06:14 +0000 |
---|---|---|
committer | sam <sam@FreeBSD.org> | 2006-03-23 23:06:14 +0000 |
commit | 2b0d21fae435871bcb97cd52a823b403fb7a5a55 (patch) | |
tree | 855a84c30a67fcd62c068c0e206ea48418594df5 /sys/gdb/gdb_cons.c | |
parent | 79229ffb86c8082c630f448fed0aa91ec9413bc0 (diff) | |
download | FreeBSD-src-2b0d21fae435871bcb97cd52a823b403fb7a5a55.zip FreeBSD-src-2b0d21fae435871bcb97cd52a823b403fb7a5a55.tar.gz |
add support for copying console messages to a remote gdb
Reviewed by: kan
Diffstat (limited to 'sys/gdb/gdb_cons.c')
-rw-r--r-- | sys/gdb/gdb_cons.c | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/sys/gdb/gdb_cons.c b/sys/gdb/gdb_cons.c new file mode 100644 index 0000000..750b031 --- /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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/cons.h> +#include <sys/kdb.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/reboot.h> +#include <sys/sysctl.h> + +#include <machine/gdb_machdep.h> +#include <machine/kdb.h> + +#include <gdb/gdb.h> +#include <gdb/gdb_int.h> + +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); +} |