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