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 54 struct 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 */ 73 static int db_output_position = 0; /* output column */ 74 static int db_last_non_space = 0; /* last non-space character */ 75 db_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) 77 db_expr_t db_max_width = 79; /* output line width */ 78 db_expr_t db_lines_per_page = 20; /* lines per page */ 79 volatile int db_pager_quit; /* user requested quit */ 80 static int db_newlines; /* # lines this page */ 81 static int db_maxlines; /* max lines/page when paging */ 82 static int ddb_use_printf = 0; 83 SYSCTL_INT(_debug, OID_AUTO, ddb_use_printf, CTLFLAG_RW, &ddb_use_printf, 0, 84 "use printf for all ddb output"); 85 86 static void db_putc(int c); 87 static void db_puts(const char *str); 88 static void db_putchar(int c, void *arg); 89 static void db_pager(void); 90 91 /* 92 * Force pending whitespace. 93 */ 94 void 95 db_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 */ 121 static void 122 db_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 146 static void 147 db_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 218 static void 219 db_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 */ 230 void 231 db_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 */ 243 void 244 db_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 */ 254 void 255 db_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 */ 309 int 310 db_print_position(void) 311 { 312 return (db_output_position); 313 } 314 315 /* 316 * Printing 317 */ 318 int 319 db_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 349 int db_indent; 350 351 void 352 db_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 */ 389 void 390 db_end_line(int field_width) 391 { 392 if (db_output_position + field_width > db_max_width) 393 db_printf("\n"); 394 } 395