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