xref: /freebsd/sys/ddb/db_output.c (revision e453e498cbb88570a3ff7b3679de65c88707da95)
1 /*-
2  * SPDX-License-Identifier: MIT-CMU
3  *
4  * Mach Operating System
5  * Copyright (c) 1991,1990 Carnegie Mellon University
6  * All Rights Reserved.
7  *
8  * Permission to use, copy, modify and distribute this software and its
9  * documentation is hereby granted, provided that both the copyright
10  * notice and this permission notice appear in all copies of the
11  * software, derivative works or modified versions, and any portions
12  * thereof, and that both notices appear in supporting documentation.
13  *
14  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
15  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
16  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
17  *
18  * Carnegie Mellon requests users of this software to return to
19  *
20  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
21  *  School of Computer Science
22  *  Carnegie Mellon University
23  *  Pittsburgh PA 15213-3890
24  *
25  * any improvements or extensions that they make and grant Carnegie the
26  * rights to redistribute these changes.
27  */
28 /*
29  * 	Author: David B. Golub, Carnegie Mellon University
30  *	Date:	7/90
31  */
32 
33 /*
34  * Printf and character output for debugger.
35  */
36 
37 #include <sys/cdefs.h>
38 #include "opt_ddb.h"
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/cons.h>
43 #include <sys/kdb.h>
44 #include <sys/kernel.h>
45 #include <sys/stdarg.h>
46 #include <sys/sysctl.h>
47 
48 #include <ddb/ddb.h>
49 #include <ddb/db_output.h>
50 
51 struct dbputchar_arg {
52 	size_t	da_nbufr;
53 	size_t	da_remain;
54 	char	*da_pbufr;
55 	char	*da_pnext;
56 };
57 
58 /*
59  *	Character output - tracks position in line.
60  *	To do this correctly, we should know how wide
61  *	the output device is - then we could zero
62  *	the line position when the output device wraps
63  *	around to the start of the next line.
64  *
65  *	Instead, we count the number of spaces printed
66  *	since the last printing character so that we
67  *	don't print trailing spaces.  This avoids most
68  *	of the wraparounds.
69  */
70 static int	db_output_position = 0;		/* output column */
71 static int	db_last_non_space = 0;		/* last non-space character */
72 db_expr_t	db_tab_stop_width = 8;		/* how wide are tab stops? */
73 #define	NEXT_TAB(i) rounddown((i) + db_tab_stop_width, db_tab_stop_width)
74 db_expr_t	db_max_width = 79;		/* output line width */
75 db_expr_t	db_lines_per_page = 20;		/* lines per page */
76 volatile int	db_pager_quit;			/* user requested quit */
77 static int	db_newlines;			/* # lines this page */
78 static int	db_maxlines;			/* max lines/page when paging */
79 static int	ddb_use_printf = 0;
80 SYSCTL_INT(_debug, OID_AUTO, ddb_use_printf, CTLFLAG_RW, &ddb_use_printf, 0,
81     "use printf for all ddb output");
82 
83 static void	db_putc(int c);
84 static void	db_puts(const char *str);
85 static void	db_putchar(int c, void *arg);
86 static void	db_pager(void);
87 
88 /*
89  * Force pending whitespace.
90  */
91 void
db_force_whitespace(void)92 db_force_whitespace(void)
93 {
94 	int last_print, next_tab;
95 
96 	last_print = db_last_non_space;
97 	while (last_print < db_output_position) {
98 	    next_tab = NEXT_TAB(last_print);
99 	    if (next_tab <= db_output_position) {
100 		while (last_print < next_tab) { /* DON'T send a tab!!! */
101 			cnputc(' ');
102 			db_capture_writech(' ');
103 			last_print++;
104 		}
105 	    }
106 	    else {
107 		cnputc(' ');
108 		db_capture_writech(' ');
109 		last_print++;
110 	    }
111 	}
112 	db_last_non_space = db_output_position;
113 }
114 
115 /*
116  * Output character.  Buffer whitespace.
117  */
118 static void
db_putchar(int c,void * arg)119 db_putchar(int c, void *arg)
120 {
121 	struct dbputchar_arg *dap = arg;
122 
123 	if (dap->da_pbufr == NULL) {
124 		 /* No bufferized output is provided. */
125 		db_putc(c);
126 	} else {
127 		*dap->da_pnext++ = c;
128 		dap->da_remain--;
129 
130 		/* Leave always the buffer 0 terminated. */
131 		*dap->da_pnext = '\0';
132 
133 		/* Check if the buffer needs to be flushed. */
134 		if (dap->da_remain < 2 || c == '\n') {
135 			db_puts(dap->da_pbufr);
136 			dap->da_pnext = dap->da_pbufr;
137 			dap->da_remain = dap->da_nbufr;
138 			*dap->da_pnext = '\0';
139 		}
140 	}
141 }
142 
143 static void
db_putc(int c)144 db_putc(int c)
145 {
146 
147 	/*
148 	 * If not in the debugger or the user requests it, output data to
149 	 * both the console and the message buffer.
150 	 */
151 	if (!kdb_active || ddb_use_printf) {
152 		printf("%c", c);
153 		if (!kdb_active)
154 			return;
155 		if (c == '\r' || c == '\n')
156 			db_check_interrupt();
157 		if (c == '\n' && db_maxlines > 0) {
158 			db_newlines++;
159 			if (db_newlines >= db_maxlines)
160 				db_pager();
161 		}
162 		return;
163 	}
164 
165 	/* Otherwise, output data directly to the console. */
166 	if (c > ' ' && c <= '~') {
167 	    /*
168 	     * Printing character.
169 	     * If we have spaces to print, print them first.
170 	     * Use tabs if possible.
171 	     */
172 	    db_force_whitespace();
173 	    cnputc(c);
174 	    db_capture_writech(c);
175 	    db_output_position++;
176 	    db_last_non_space = db_output_position;
177 	}
178 	else if (c == '\n') {
179 	    /* Newline */
180 	    cnputc(c);
181 	    db_capture_writech(c);
182 	    db_output_position = 0;
183 	    db_last_non_space = 0;
184 	    db_check_interrupt();
185 	    if (db_maxlines > 0) {
186 		    db_newlines++;
187 		    if (db_newlines >= db_maxlines)
188 			    db_pager();
189 	    }
190 	}
191 	else if (c == '\r') {
192 	    /* Return */
193 	    cnputc(c);
194 	    db_capture_writech(c);
195 	    db_output_position = 0;
196 	    db_last_non_space = 0;
197 	    db_check_interrupt();
198 	}
199 	else if (c == '\t') {
200 	    /* assume tabs every 8 positions */
201 	    db_output_position = NEXT_TAB(db_output_position);
202 	}
203 	else if (c == ' ') {
204 	    /* space */
205 	    db_output_position++;
206 	}
207 	else if (c == '\007') {
208 	    /* bell */
209 	    cnputc(c);
210 	    /* No need to beep in a log: db_capture_writech(c); */
211 	}
212 	/* other characters are assumed non-printing */
213 }
214 
215 static void
db_puts(const char * str)216 db_puts(const char *str)
217 {
218 	int i;
219 
220 	for (i = 0; str[i] != '\0'; i++)
221 		db_putc(str[i]);
222 }
223 
224 /*
225  * Turn on the pager.
226  */
227 void
db_enable_pager(void)228 db_enable_pager(void)
229 {
230 	if (db_maxlines == 0) {
231 		db_maxlines = db_lines_per_page;
232 		db_newlines = 0;
233 		db_pager_quit = 0;
234 	}
235 }
236 
237 /*
238  * Turn off the pager.
239  */
240 void
db_disable_pager(void)241 db_disable_pager(void)
242 {
243 	db_maxlines = 0;
244 }
245 
246 /*
247  * A simple paging callout function.  It supports several simple more(1)-like
248  * commands as well as a quit command that sets db_pager_quit which db
249  * commands can poll to see if they should terminate early.
250  */
251 void
db_pager(void)252 db_pager(void)
253 {
254 	int c, done;
255 
256 	db_capture_enterpager();
257 	db_printf("--More--\r");
258 	done = 0;
259 	while (!done) {
260 		c = db_getc();
261 		switch (c) {
262 		case 'e':
263 		case 'j':
264 		case '\n':
265 			/* Just one more line. */
266 			db_maxlines = 1;
267 			done++;
268 			break;
269 		case 'd':
270 			/* Half a page. */
271 			db_maxlines = db_lines_per_page / 2;
272 			done++;
273 			break;
274 		case 'f':
275 		case ' ':
276 			/* Another page. */
277 			db_maxlines = db_lines_per_page;
278 			done++;
279 			break;
280 		case 'q':
281 		case 'Q':
282 		case 'x':
283 		case 'X':
284 			/* Quit */
285 			db_maxlines = 0;
286 			db_pager_quit = 1;
287 			done++;
288 			break;
289 #if 0
290 			/* FALLTHROUGH */
291 		default:
292 			cnputc('\007');
293 #endif
294 		}
295 	}
296 	db_printf("        ");
297 	db_force_whitespace();
298 	db_printf("\r");
299 	db_newlines = 0;
300 	db_capture_exitpager();
301 }
302 
303 /*
304  * Return output position
305  */
306 int
db_print_position(void)307 db_print_position(void)
308 {
309 	return (db_output_position);
310 }
311 
312 /*
313  * Printing
314  */
315 int
db_printf(const char * fmt,...)316 db_printf(const char *fmt, ...)
317 {
318 #ifdef DDB_BUFR_SIZE
319 	char bufr[DDB_BUFR_SIZE];
320 #endif
321 	struct dbputchar_arg dca;
322 	va_list	listp;
323 	int retval;
324 
325 #ifdef DDB_BUFR_SIZE
326 	dca.da_pbufr = bufr;
327 	dca.da_pnext = dca.da_pbufr;
328 	dca.da_nbufr = sizeof(bufr);
329 	dca.da_remain = sizeof(bufr);
330 	*dca.da_pnext = '\0';
331 #else
332 	dca.da_pbufr = NULL;
333 #endif
334 
335 	va_start(listp, fmt);
336 	retval = kvprintf (fmt, db_putchar, &dca, db_radix, listp);
337 	va_end(listp);
338 
339 #ifdef DDB_BUFR_SIZE
340 	if (*dca.da_pbufr != '\0')
341 		db_puts(dca.da_pbufr);
342 #endif
343 	return (retval);
344 }
345 
346 int db_indent;
347 
348 void
db_iprintf(const char * fmt,...)349 db_iprintf(const char *fmt,...)
350 {
351 #ifdef DDB_BUFR_SIZE
352 	char bufr[DDB_BUFR_SIZE];
353 #endif
354 	struct dbputchar_arg dca;
355 	int i;
356 	va_list listp;
357 
358 	for (i = db_indent; i >= 8; i -= 8)
359 		db_printf("\t");
360 	while (--i >= 0)
361 		db_printf(" ");
362 
363 #ifdef DDB_BUFR_SIZE
364 	dca.da_pbufr = bufr;
365 	dca.da_pnext = dca.da_pbufr;
366 	dca.da_nbufr = sizeof(bufr);
367 	dca.da_remain = sizeof(bufr);
368 	*dca.da_pnext = '\0';
369 #else
370 	dca.da_pbufr = NULL;
371 #endif
372 
373 	va_start(listp, fmt);
374 	kvprintf (fmt, db_putchar, &dca, db_radix, listp);
375 	va_end(listp);
376 
377 #ifdef DDB_BUFR_SIZE
378 	if (*dca.da_pbufr != '\0')
379 		db_puts(dca.da_pbufr);
380 #endif
381 }
382 
383 /*
384  * End line if too long.
385  */
386 void
db_end_line(int field_width)387 db_end_line(int field_width)
388 {
389 	if (db_output_position + field_width > db_max_width)
390 	    db_printf("\n");
391 }
392