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 128 /* No bufferized output is provided. */ 129 db_putc(c); 130 } else { 131 132 *dap->da_pnext++ = c; 133 dap->da_remain--; 134 135 /* Leave always the buffer 0 terminated. */ 136 *dap->da_pnext = '\0'; 137 138 /* Check if the buffer needs to be flushed. */ 139 if (dap->da_remain < 2 || c == '\n') { 140 db_puts(dap->da_pbufr); 141 dap->da_pnext = dap->da_pbufr; 142 dap->da_remain = dap->da_nbufr; 143 *dap->da_pnext = '\0'; 144 } 145 } 146 } 147 148 static void 149 db_putc(int c) 150 { 151 152 /* 153 * If not in the debugger or the user requests it, output data to 154 * both the console and the message buffer. 155 */ 156 if (!kdb_active || ddb_use_printf) { 157 printf("%c", c); 158 if (!kdb_active) 159 return; 160 if (c == '\r' || c == '\n') 161 db_check_interrupt(); 162 if (c == '\n' && db_maxlines > 0) { 163 db_newlines++; 164 if (db_newlines >= db_maxlines) 165 db_pager(); 166 } 167 return; 168 } 169 170 /* Otherwise, output data directly to the console. */ 171 if (c > ' ' && c <= '~') { 172 /* 173 * Printing character. 174 * If we have spaces to print, print them first. 175 * Use tabs if possible. 176 */ 177 db_force_whitespace(); 178 cnputc(c); 179 db_capture_writech(c); 180 db_output_position++; 181 db_last_non_space = db_output_position; 182 } 183 else if (c == '\n') { 184 /* Newline */ 185 cnputc(c); 186 db_capture_writech(c); 187 db_output_position = 0; 188 db_last_non_space = 0; 189 db_check_interrupt(); 190 if (db_maxlines > 0) { 191 db_newlines++; 192 if (db_newlines >= db_maxlines) 193 db_pager(); 194 } 195 } 196 else if (c == '\r') { 197 /* Return */ 198 cnputc(c); 199 db_capture_writech(c); 200 db_output_position = 0; 201 db_last_non_space = 0; 202 db_check_interrupt(); 203 } 204 else if (c == '\t') { 205 /* assume tabs every 8 positions */ 206 db_output_position = NEXT_TAB(db_output_position); 207 } 208 else if (c == ' ') { 209 /* space */ 210 db_output_position++; 211 } 212 else if (c == '\007') { 213 /* bell */ 214 cnputc(c); 215 /* No need to beep in a log: db_capture_writech(c); */ 216 } 217 /* other characters are assumed non-printing */ 218 } 219 220 static void 221 db_puts(const char *str) 222 { 223 int i; 224 225 for (i = 0; str[i] != '\0'; i++) 226 db_putc(str[i]); 227 } 228 229 /* 230 * Turn on the pager. 231 */ 232 void 233 db_enable_pager(void) 234 { 235 if (db_maxlines == 0) { 236 db_maxlines = db_lines_per_page; 237 db_newlines = 0; 238 db_pager_quit = 0; 239 } 240 } 241 242 /* 243 * Turn off the pager. 244 */ 245 void 246 db_disable_pager(void) 247 { 248 db_maxlines = 0; 249 } 250 251 /* 252 * A simple paging callout function. It supports several simple more(1)-like 253 * commands as well as a quit command that sets db_pager_quit which db 254 * commands can poll to see if they should terminate early. 255 */ 256 void 257 db_pager(void) 258 { 259 int c, done; 260 261 db_capture_enterpager(); 262 db_printf("--More--\r"); 263 done = 0; 264 while (!done) { 265 c = cngetc(); 266 switch (c) { 267 case 'e': 268 case 'j': 269 case '\n': 270 /* Just one more line. */ 271 db_maxlines = 1; 272 done++; 273 break; 274 case 'd': 275 /* Half a page. */ 276 db_maxlines = db_lines_per_page / 2; 277 done++; 278 break; 279 case 'f': 280 case ' ': 281 /* Another page. */ 282 db_maxlines = db_lines_per_page; 283 done++; 284 break; 285 case 'q': 286 case 'Q': 287 case 'x': 288 case 'X': 289 /* Quit */ 290 db_maxlines = 0; 291 db_pager_quit = 1; 292 done++; 293 break; 294 #if 0 295 /* FALLTHROUGH */ 296 default: 297 cnputc('\007'); 298 #endif 299 } 300 } 301 db_printf(" "); 302 db_force_whitespace(); 303 db_printf("\r"); 304 db_newlines = 0; 305 db_capture_exitpager(); 306 } 307 308 /* 309 * Return output position 310 */ 311 int 312 db_print_position(void) 313 { 314 return (db_output_position); 315 } 316 317 /* 318 * Printing 319 */ 320 int 321 db_printf(const char *fmt, ...) 322 { 323 #ifdef DDB_BUFR_SIZE 324 char bufr[DDB_BUFR_SIZE]; 325 #endif 326 struct dbputchar_arg dca; 327 va_list listp; 328 int retval; 329 330 #ifdef DDB_BUFR_SIZE 331 dca.da_pbufr = bufr; 332 dca.da_pnext = dca.da_pbufr; 333 dca.da_nbufr = sizeof(bufr); 334 dca.da_remain = sizeof(bufr); 335 *dca.da_pnext = '\0'; 336 #else 337 dca.da_pbufr = NULL; 338 #endif 339 340 va_start(listp, fmt); 341 retval = kvprintf (fmt, db_putchar, &dca, db_radix, listp); 342 va_end(listp); 343 344 #ifdef DDB_BUFR_SIZE 345 if (*dca.da_pbufr != '\0') 346 db_puts(dca.da_pbufr); 347 #endif 348 return (retval); 349 } 350 351 int db_indent; 352 353 void 354 db_iprintf(const char *fmt,...) 355 { 356 #ifdef DDB_BUFR_SIZE 357 char bufr[DDB_BUFR_SIZE]; 358 #endif 359 struct dbputchar_arg dca; 360 int i; 361 va_list listp; 362 363 for (i = db_indent; i >= 8; i -= 8) 364 db_printf("\t"); 365 while (--i >= 0) 366 db_printf(" "); 367 368 #ifdef DDB_BUFR_SIZE 369 dca.da_pbufr = bufr; 370 dca.da_pnext = dca.da_pbufr; 371 dca.da_nbufr = sizeof(bufr); 372 dca.da_remain = sizeof(bufr); 373 *dca.da_pnext = '\0'; 374 #else 375 dca.da_pbufr = NULL; 376 #endif 377 378 va_start(listp, fmt); 379 kvprintf (fmt, db_putchar, &dca, db_radix, listp); 380 va_end(listp); 381 382 #ifdef DDB_BUFR_SIZE 383 if (*dca.da_pbufr != '\0') 384 db_puts(dca.da_pbufr); 385 #endif 386 } 387 388 /* 389 * End line if too long. 390 */ 391 void 392 db_end_line(int field_width) 393 { 394 if (db_output_position + field_width > db_max_width) 395 db_printf("\n"); 396 } 397