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