diff --git a/share/man/man4/ddb.4 b/share/man/man4/ddb.4 --- a/share/man/man4/ddb.4 +++ b/share/man/man4/ddb.4 @@ -26,7 +26,7 @@ .\" .\" $FreeBSD$ .\" -.Dd January 26, 2021 +.Dd March 14, 2021 .Dt DDB 4 .Os .Sh NAME @@ -1571,6 +1571,19 @@ .Xr sysctl 8 .Va debug.kdb.enter to 1. +.Pp +Output may be interrupted, paused, and resumed with the control +characters CTRL-C, CTRL-S, and CTRL-Q. +Because these control characters are received as in-band data from the +console, there is an input buffer, and once that buffer fills +.Nm +must either stop responding to control characters or drop additional +input while continuing to search for control characters. +This behavior is controlled by the tunable +.Xr sysctl 8 +.Va debug.ddb.prioritize_control_input , +which defaults to 1. +The input buffer size is 512 bytes. .Sh FILES Header files mentioned in this manual page can be found below .Pa /usr/include diff --git a/sys/ddb/db_input.c b/sys/ddb/db_input.c --- a/sys/ddb/db_input.c +++ b/sys/ddb/db_input.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -54,6 +55,19 @@ static char * db_lc; /* current character */ static char * db_le; /* one past last character */ +/* + * Raw input buffer, processed only for certain control characters. + */ +#define DB_RAW_SIZE 512 +static char db_raw[DB_RAW_SIZE]; +static u_int db_raw_pos; +static u_int db_raw_cnt; +static int db_raw_warned; +static int ddb_prioritize_control_input = 1; +SYSCTL_INT(_debug_ddb, OID_AUTO, prioritize_control_input, CTLFLAG_RWTUN, + &ddb_prioritize_control_input, 0, + "Drop input when the buffer fills in order to keep servicing ^C/^S/^Q"); + /* * Simple input line history support. */ @@ -65,11 +79,13 @@ #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 int db_raw_pop(void); +static void db_raw_push(int); +static int db_raw_space(void); static void db_putstring(s, count) @@ -307,10 +323,50 @@ return (0); } +/* Get a character from the console, first checking the raw input buffer. */ +int +db_getc(void) +{ + int c; + + if (db_raw_cnt == 0) { + c = cngetc(); + } else { + c = db_raw_pop(); + if (c == '\r') + c = '\n'; + } + return (c); +} + +/* Whether the raw input buffer has space to accept another character. */ static int -cnmaygetc() +db_raw_space(void) +{ + + return (db_raw_cnt < DB_RAW_SIZE); +} + +/* Un-get a character from the console by buffering it. */ +static void +db_raw_push(int c) { - return (-1); + + if (!db_raw_space()) + db_error(NULL); + db_raw[(db_raw_pos + db_raw_cnt++) % DB_RAW_SIZE] = c; +} + +/* Drain a character from the raw input buffer. */ +static int +db_raw_pop(void) +{ + + if (db_raw_cnt == 0) + return (-1); + db_raw_cnt--; + db_raw_warned = 0; + return (db_raw[db_raw_pos++ % DB_RAW_SIZE]); } int @@ -339,7 +395,7 @@ db_lc = lstart; db_le = lstart; - while (!db_inputchar(cngetc())) + while (!db_inputchar(db_getc())) continue; db_capture_write(lstart, db_le - db_lbuf_start); @@ -361,30 +417,54 @@ return (db_le - db_lbuf_start); } +static void +db_do_interrupt(const char *reason) +{ + + /* Do a pager quit too because some commands have jmpbuf handling. */ + db_disable_pager(); + db_pager_quit = 1; + db_error(reason); +} + void db_check_interrupt(void) { 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; + /* + * Check console input for control characters. Non-control input is + * buffered. When buffer space is exhausted, either stop responding to + * control input or drop further non-control input on the floor. + */ + for (;;) { + if (!ddb_prioritize_control_input && !db_raw_space()) + return; + c = cncheckc(); + switch (c) { + case -1: /* no character */ + return; + + case CTRL('c'): + db_do_interrupt("^C"); + /*NOTREACHED*/ + + case CTRL('s'): + do { + c = cncheckc(); + if (c == CTRL('c')) + db_do_interrupt("^C"); + } while (c != CTRL('q')); + break; - default: - /* drop on floor */ - break; + default: + if (db_raw_space()) { + db_raw_push(c); + } else if (!db_raw_warned) { + db_raw_warned = 1; + db_printf("\n--Exceeded input buffer--\n"); + } + break; + } } } diff --git a/sys/ddb/db_output.c b/sys/ddb/db_output.c --- a/sys/ddb/db_output.c +++ b/sys/ddb/db_output.c @@ -260,7 +260,7 @@ db_printf("--More--\r"); done = 0; while (!done) { - c = cngetc(); + c = db_getc(); switch (c) { case 'e': case 'j': diff --git a/sys/ddb/ddb.h b/sys/ddb/ddb.h --- a/sys/ddb/ddb.h +++ b/sys/ddb/ddb.h @@ -197,6 +197,7 @@ /* instruction disassembler */ void db_error(const char *s); int db_expression(db_expr_t *valuep); +int db_getc(void); 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);