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 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 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 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 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 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 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 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 308 db_print_position(void) 309 { 310 return (db_output_position); 311 } 312 313 /* 314 * Printing 315 */ 316 int 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 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 388 db_end_line(int field_width) 389 { 390 if (db_output_position + field_width > db_max_width) 391 db_printf("\n"); 392 } 393