ddb: enable the use of ^C and ^S/^Q

This lets one interrupt DDB's output, which is useful if paging is
disabled and the output device is slow.

This follows a previous implementation in svn r311952 / git
5fddef7999 which was reverted because it
broke DDB type-ahead.

Now, try this again, but with a 512-byte type-ahead buffer.  While there
is buffer space, control input is handled and non-control input is
buffered.  When the buffer is exhausted, the default is to print a
warning and drop further non-control input in order to continue handling
control input.  sysctl debug.ddb.prioritize_control_input can be set to
0 to instead preserve all input but lose immediate handling of control
input.  This could for example effect pasting of a large script into the
ddb console.

Suggested by:	Anton Rang <rang@acm.org>
Reviewed by:	markj
Discussed with:	imp
Sponsored by:	Dell EMC Isilon
Differential Revision:	https://reviews.freebsd.org/D28676
This commit is contained in:
Ryan Libby 2021-03-14 16:04:27 -07:00
parent 588ce1a3ac
commit 3e5e9939cd
4 changed files with 118 additions and 24 deletions

View File

@ -26,7 +26,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd January 26, 2021
.Dd March 14, 2021
.Dt DDB 4
.Os
.Sh NAME
@ -1571,6 +1571,19 @@ The debugger may be entered by setting the
.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

View File

@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/cons.h>
#include <sys/sysctl.h>
#include <ddb/ddb.h>
#include <ddb/db_output.h>
@ -54,6 +55,19 @@ static char * db_lbuf_end; /* end of input line buffer */
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 @@ static int db_lhist_nlines;
#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 @@ db_inputchar(c)
return (0);
}
static int
cnmaygetc()
/* Get a character from the console, first checking the raw input buffer. */
int
db_getc(void)
{
return (-1);
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
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)
{
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_readline(lstart, lsize)
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 @@ db_readline(lstart, lsize)
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;
/*
* 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_error((char *)0);
/*NOTREACHED*/
case CTRL('c'):
db_do_interrupt("^C");
/*NOTREACHED*/
case CTRL('s'):
do {
c = cnmaygetc();
if (c == CTRL('c'))
db_error((char *)0);
} while (c != CTRL('q'));
break;
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;
}
}
}

View File

@ -260,7 +260,7 @@ db_pager(void)
db_printf("--More--\r");
done = 0;
while (!done) {
c = cngetc();
c = db_getc();
switch (c) {
case 'e':
case 'j':

View File

@ -197,6 +197,7 @@ db_addr_t db_disasm(db_addr_t loc, bool altfmt);
/* 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);