1 /*- 2 * Copyright (C) 2008 John Birrell <jb@freebsd.org>. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice(s), this list of conditions and the following disclaimer as 10 * the first lines of this file unmodified other than the possible 11 * addition of one or more copyright notices. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice(s), this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY 17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY 20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 26 * DAMAGE. 27 * 28 * $FreeBSD$ 29 * 30 */ 31 32 #ifdef DEBUG 33 34 #include <machine/atomic.h> 35 36 #define dtrace_cmpset_long atomic_cmpset_long 37 38 #define DTRACE_DEBUG_BUFR_SIZE (32 * 1024) 39 40 struct dtrace_debug_data { 41 char bufr[DTRACE_DEBUG_BUFR_SIZE]; 42 char *first; 43 char *last; 44 char *next; 45 } dtrace_debug_data[MAXCPU]; 46 47 static char dtrace_debug_bufr[DTRACE_DEBUG_BUFR_SIZE]; 48 49 static volatile u_long dtrace_debug_flag[MAXCPU]; 50 51 static void 52 dtrace_debug_lock(int cpu) 53 { 54 while (dtrace_cmpset_long(&dtrace_debug_flag[cpu], 0, 1) == 0) 55 /* Loop until the lock is obtained. */ 56 ; 57 } 58 59 static void 60 dtrace_debug_unlock(int cpu) 61 { 62 dtrace_debug_flag[cpu] = 0; 63 } 64 65 static void 66 dtrace_debug_init(void *dummy) 67 { 68 int i; 69 struct dtrace_debug_data *d; 70 71 CPU_FOREACH(i) { 72 d = &dtrace_debug_data[i]; 73 74 if (d->first == NULL) { 75 d->first = d->bufr; 76 d->next = d->bufr; 77 d->last = d->bufr + DTRACE_DEBUG_BUFR_SIZE - 1; 78 *(d->last) = '\0'; 79 } 80 } 81 } 82 83 SYSINIT(dtrace_debug_init, SI_SUB_KDTRACE, SI_ORDER_ANY, dtrace_debug_init, NULL); 84 SYSINIT(dtrace_debug_smpinit, SI_SUB_SMP, SI_ORDER_ANY, dtrace_debug_init, NULL); 85 86 static void 87 dtrace_debug_output(void) 88 { 89 char *p; 90 int i; 91 struct dtrace_debug_data *d; 92 uintptr_t count; 93 94 CPU_FOREACH(i) { 95 dtrace_debug_lock(i); 96 97 d = &dtrace_debug_data[i]; 98 99 count = 0; 100 101 if (d->first < d->next) { 102 char *p1 = dtrace_debug_bufr; 103 104 count = (uintptr_t) d->next - (uintptr_t) d->first; 105 106 for (p = d->first; p < d->next; p++) 107 *p1++ = *p; 108 } else if (d->next > d->first) { 109 char *p1 = dtrace_debug_bufr; 110 111 count = (uintptr_t) d->last - (uintptr_t) d->first; 112 113 for (p = d->first; p < d->last; p++) 114 *p1++ = *p; 115 116 count += (uintptr_t) d->next - (uintptr_t) d->bufr; 117 118 for (p = d->bufr; p < d->next; p++) 119 *p1++ = *p; 120 } 121 122 d->first = d->bufr; 123 d->next = d->bufr; 124 125 dtrace_debug_unlock(i); 126 127 if (count > 0) { 128 char *last = dtrace_debug_bufr + count; 129 130 p = dtrace_debug_bufr; 131 132 while (p < last) { 133 if (*p == '\0') { 134 p++; 135 continue; 136 } 137 138 printf("%s", p); 139 140 p += strlen(p); 141 } 142 } 143 } 144 } 145 146 /* 147 * Functions below here are called from the probe context, so they can't call 148 * _any_ functions outside the dtrace module without running foul of the function 149 * boundary trace provider (fbt). The purpose of these functions is limited to 150 * buffering debug strings for output when the probe completes on the current CPU. 151 */ 152 153 static __inline void 154 dtrace_debug__putc(char c) 155 { 156 struct dtrace_debug_data *d = &dtrace_debug_data[curcpu]; 157 158 *d->next++ = c; 159 160 if (d->next == d->last) 161 d->next = d->bufr; 162 163 *(d->next) = '\0'; 164 165 if (d->next == d->first) 166 d->first++; 167 168 if (d->first == d->last) 169 d->first = d->bufr; 170 } 171 172 static void __used 173 dtrace_debug_putc(char c) 174 { 175 dtrace_debug_lock(curcpu); 176 177 dtrace_debug__putc(c); 178 179 dtrace_debug_unlock(curcpu); 180 } 181 182 static void __used 183 dtrace_debug_puts(const char *s) 184 { 185 dtrace_debug_lock(curcpu); 186 187 while (*s != '\0') 188 dtrace_debug__putc(*s++); 189 190 dtrace_debug__putc('\0'); 191 192 dtrace_debug_unlock(curcpu); 193 } 194 195 /* 196 * Snaffled from sys/kern/subr_prf.c 197 * 198 * Put a NUL-terminated ASCII number (base <= 36) in a buffer in reverse 199 * order; return an optional length and a pointer to the last character 200 * written in the buffer (i.e., the first character of the string). 201 * The buffer pointed to by `nbuf' must have length >= MAXNBUF. 202 */ 203 static char * 204 dtrace_debug_ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper) 205 { 206 char *p, c; 207 208 p = nbuf; 209 *p = '\0'; 210 do { 211 c = hex2ascii(num % base); 212 *++p = upper ? toupper(c) : c; 213 } while (num /= base); 214 if (lenp) 215 *lenp = p - nbuf; 216 return (p); 217 } 218 219 #define MAXNBUF (sizeof(intmax_t) * NBBY + 1) 220 221 static void 222 dtrace_debug_vprintf(const char *fmt, va_list ap) 223 { 224 char nbuf[MAXNBUF]; 225 const char *p, *percent, *q; 226 u_char *up; 227 int ch, n; 228 uintmax_t num; 229 int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot; 230 int cflag, hflag, jflag, tflag, zflag; 231 int dwidth, upper; 232 int radix = 10; 233 char padc; 234 int stop = 0, retval = 0; 235 236 num = 0; 237 238 if (fmt == NULL) 239 fmt = "(fmt null)\n"; 240 241 for (;;) { 242 padc = ' '; 243 width = 0; 244 while ((ch = (u_char)*fmt++) != '%' || stop) { 245 if (ch == '\0') { 246 dtrace_debug__putc('\0'); 247 return; 248 } 249 dtrace_debug__putc(ch); 250 } 251 percent = fmt - 1; 252 qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0; 253 sign = 0; dot = 0; dwidth = 0; upper = 0; 254 cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0; 255 reswitch: switch (ch = (u_char)*fmt++) { 256 case '.': 257 dot = 1; 258 goto reswitch; 259 case '#': 260 sharpflag = 1; 261 goto reswitch; 262 case '+': 263 sign = 1; 264 goto reswitch; 265 case '-': 266 ladjust = 1; 267 goto reswitch; 268 case '%': 269 dtrace_debug__putc(ch); 270 break; 271 case '*': 272 if (!dot) { 273 width = va_arg(ap, int); 274 if (width < 0) { 275 ladjust = !ladjust; 276 width = -width; 277 } 278 } else { 279 dwidth = va_arg(ap, int); 280 } 281 goto reswitch; 282 case '0': 283 if (!dot) { 284 padc = '0'; 285 goto reswitch; 286 } 287 case '1': case '2': case '3': case '4': 288 case '5': case '6': case '7': case '8': case '9': 289 for (n = 0;; ++fmt) { 290 n = n * 10 + ch - '0'; 291 ch = *fmt; 292 if (ch < '0' || ch > '9') 293 break; 294 } 295 if (dot) 296 dwidth = n; 297 else 298 width = n; 299 goto reswitch; 300 case 'b': 301 num = (u_int)va_arg(ap, int); 302 p = va_arg(ap, char *); 303 for (q = dtrace_debug_ksprintn(nbuf, num, *p++, NULL, 0); *q;) 304 dtrace_debug__putc(*q--); 305 306 if (num == 0) 307 break; 308 309 for (tmp = 0; *p;) { 310 n = *p++; 311 if (num & (1 << (n - 1))) { 312 dtrace_debug__putc(tmp ? ',' : '<'); 313 for (; (n = *p) > ' '; ++p) 314 dtrace_debug__putc(n); 315 tmp = 1; 316 } else 317 for (; *p > ' '; ++p) 318 continue; 319 } 320 if (tmp) 321 dtrace_debug__putc('>'); 322 break; 323 case 'c': 324 dtrace_debug__putc(va_arg(ap, int)); 325 break; 326 case 'D': 327 up = va_arg(ap, u_char *); 328 p = va_arg(ap, char *); 329 if (!width) 330 width = 16; 331 while(width--) { 332 dtrace_debug__putc(hex2ascii(*up >> 4)); 333 dtrace_debug__putc(hex2ascii(*up & 0x0f)); 334 up++; 335 if (width) 336 for (q=p;*q;q++) 337 dtrace_debug__putc(*q); 338 } 339 break; 340 case 'd': 341 case 'i': 342 base = 10; 343 sign = 1; 344 goto handle_sign; 345 case 'h': 346 if (hflag) { 347 hflag = 0; 348 cflag = 1; 349 } else 350 hflag = 1; 351 goto reswitch; 352 case 'j': 353 jflag = 1; 354 goto reswitch; 355 case 'l': 356 if (lflag) { 357 lflag = 0; 358 qflag = 1; 359 } else 360 lflag = 1; 361 goto reswitch; 362 case 'n': 363 if (jflag) 364 *(va_arg(ap, intmax_t *)) = retval; 365 else if (qflag) 366 *(va_arg(ap, quad_t *)) = retval; 367 else if (lflag) 368 *(va_arg(ap, long *)) = retval; 369 else if (zflag) 370 *(va_arg(ap, size_t *)) = retval; 371 else if (hflag) 372 *(va_arg(ap, short *)) = retval; 373 else if (cflag) 374 *(va_arg(ap, char *)) = retval; 375 else 376 *(va_arg(ap, int *)) = retval; 377 break; 378 case 'o': 379 base = 8; 380 goto handle_nosign; 381 case 'p': 382 base = 16; 383 sharpflag = (width == 0); 384 sign = 0; 385 num = (uintptr_t)va_arg(ap, void *); 386 goto number; 387 case 'q': 388 qflag = 1; 389 goto reswitch; 390 case 'r': 391 base = radix; 392 if (sign) 393 goto handle_sign; 394 goto handle_nosign; 395 case 's': 396 p = va_arg(ap, char *); 397 if (p == NULL) 398 p = "(null)"; 399 if (!dot) 400 n = strlen (p); 401 else 402 for (n = 0; n < dwidth && p[n]; n++) 403 continue; 404 405 width -= n; 406 407 if (!ladjust && width > 0) 408 while (width--) 409 dtrace_debug__putc(padc); 410 while (n--) 411 dtrace_debug__putc(*p++); 412 if (ladjust && width > 0) 413 while (width--) 414 dtrace_debug__putc(padc); 415 break; 416 case 't': 417 tflag = 1; 418 goto reswitch; 419 case 'u': 420 base = 10; 421 goto handle_nosign; 422 case 'X': 423 upper = 1; 424 case 'x': 425 base = 16; 426 goto handle_nosign; 427 case 'y': 428 base = 16; 429 sign = 1; 430 goto handle_sign; 431 case 'z': 432 zflag = 1; 433 goto reswitch; 434 handle_nosign: 435 sign = 0; 436 if (jflag) 437 num = va_arg(ap, uintmax_t); 438 else if (qflag) 439 num = va_arg(ap, u_quad_t); 440 else if (tflag) 441 num = va_arg(ap, ptrdiff_t); 442 else if (lflag) 443 num = va_arg(ap, u_long); 444 else if (zflag) 445 num = va_arg(ap, size_t); 446 else if (hflag) 447 num = (u_short)va_arg(ap, int); 448 else if (cflag) 449 num = (u_char)va_arg(ap, int); 450 else 451 num = va_arg(ap, u_int); 452 goto number; 453 handle_sign: 454 if (jflag) 455 num = va_arg(ap, intmax_t); 456 else if (qflag) 457 num = va_arg(ap, quad_t); 458 else if (tflag) 459 num = va_arg(ap, ptrdiff_t); 460 else if (lflag) 461 num = va_arg(ap, long); 462 else if (zflag) 463 num = va_arg(ap, size_t); 464 else if (hflag) 465 num = (short)va_arg(ap, int); 466 else if (cflag) 467 num = (char)va_arg(ap, int); 468 else 469 num = va_arg(ap, int); 470 number: 471 if (sign && (intmax_t)num < 0) { 472 neg = 1; 473 num = -(intmax_t)num; 474 } 475 p = dtrace_debug_ksprintn(nbuf, num, base, &tmp, upper); 476 if (sharpflag && num != 0) { 477 if (base == 8) 478 tmp++; 479 else if (base == 16) 480 tmp += 2; 481 } 482 if (neg) 483 tmp++; 484 485 if (!ladjust && padc != '0' && width 486 && (width -= tmp) > 0) 487 while (width--) 488 dtrace_debug__putc(padc); 489 if (neg) 490 dtrace_debug__putc('-'); 491 if (sharpflag && num != 0) { 492 if (base == 8) { 493 dtrace_debug__putc('0'); 494 } else if (base == 16) { 495 dtrace_debug__putc('0'); 496 dtrace_debug__putc('x'); 497 } 498 } 499 if (!ladjust && width && (width -= tmp) > 0) 500 while (width--) 501 dtrace_debug__putc(padc); 502 503 while (*p) 504 dtrace_debug__putc(*p--); 505 506 if (ladjust && width && (width -= tmp) > 0) 507 while (width--) 508 dtrace_debug__putc(padc); 509 510 break; 511 default: 512 while (percent < fmt) 513 dtrace_debug__putc(*percent++); 514 /* 515 * Since we ignore an formatting argument it is no 516 * longer safe to obey the remaining formatting 517 * arguments as the arguments will no longer match 518 * the format specs. 519 */ 520 stop = 1; 521 break; 522 } 523 } 524 525 dtrace_debug__putc('\0'); 526 } 527 528 void 529 dtrace_debug_printf(const char *fmt, ...) 530 { 531 va_list ap; 532 533 dtrace_debug_lock(curcpu); 534 535 va_start(ap, fmt); 536 537 dtrace_debug_vprintf(fmt, ap); 538 539 va_end(ap); 540 541 dtrace_debug_unlock(curcpu); 542 } 543 544 #else 545 546 #define dtrace_debug_output() 547 #define dtrace_debug_puts(_s) 548 #define dtrace_debug_printf(fmt, ...) 549 550 #endif 551