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