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 #if defined(__amd64__) 35 static __inline int 36 dtrace_cmpset_long(volatile u_long *dst, u_long exp, u_long src) 37 { 38 u_char res; 39 40 __asm __volatile( 41 " lock ; " 42 " cmpxchgq %2,%1 ; " 43 " sete %0 ; " 44 "1: " 45 "# dtrace_cmpset_long" 46 : "=a" (res), /* 0 */ 47 "=m" (*dst) /* 1 */ 48 : "r" (src), /* 2 */ 49 "a" (exp), /* 3 */ 50 "m" (*dst) /* 4 */ 51 : "memory"); 52 53 return (res); 54 } 55 #elif defined(__i386__) 56 static __inline int 57 dtrace_cmpset_long(volatile u_long *dst, u_long exp, u_long src) 58 { 59 u_char res; 60 61 __asm __volatile( 62 " lock ; " 63 " cmpxchgl %2,%1 ; " 64 " sete %0 ; " 65 "1: " 66 "# dtrace_cmpset_long" 67 : "=a" (res), /* 0 */ 68 "=m" (*dst) /* 1 */ 69 : "r" (src), /* 2 */ 70 "a" (exp), /* 3 */ 71 "m" (*dst) /* 4 */ 72 : "memory"); 73 74 return (res); 75 } 76 #endif 77 78 #define DTRACE_DEBUG_BUFR_SIZE (32 * 1024) 79 80 struct dtrace_debug_data { 81 char bufr[DTRACE_DEBUG_BUFR_SIZE]; 82 char *first; 83 char *last; 84 char *next; 85 } dtrace_debug_data[MAXCPU]; 86 87 static char dtrace_debug_bufr[DTRACE_DEBUG_BUFR_SIZE]; 88 89 static volatile u_long dtrace_debug_flag[MAXCPU]; 90 91 static void 92 dtrace_debug_lock(int cpu) 93 { 94 while (dtrace_cmpset_long(&dtrace_debug_flag[cpu], 0, 1) == 0) 95 /* Loop until the lock is obtained. */ 96 ; 97 } 98 99 static void 100 dtrace_debug_unlock(int cpu) 101 { 102 dtrace_debug_flag[cpu] = 0; 103 } 104 105 static void 106 dtrace_debug_init(void *dummy) 107 { 108 int i; 109 struct dtrace_debug_data *d; 110 111 for (i = 0; i <= mp_maxid; i++) { 112 if (pcpu_find(i) == NULL) 113 continue; 114 115 d = &dtrace_debug_data[i]; 116 117 if (d->first == NULL) { 118 d->first = d->bufr; 119 d->next = d->bufr; 120 d->last = d->bufr + DTRACE_DEBUG_BUFR_SIZE - 1; 121 *(d->last) = '\0'; 122 } 123 } 124 } 125 126 SYSINIT(dtrace_debug_init, SI_SUB_KDTRACE, SI_ORDER_ANY, dtrace_debug_init, NULL); 127 SYSINIT(dtrace_debug_smpinit, SI_SUB_SMP, SI_ORDER_ANY, dtrace_debug_init, NULL); 128 129 static void 130 dtrace_debug_output(void) 131 { 132 char *p; 133 int i; 134 struct dtrace_debug_data *d; 135 uintptr_t count; 136 137 for (i = 0; i <= mp_maxid; i++) { 138 if (pcpu_find(i) == NULL) 139 continue; 140 141 dtrace_debug_lock(i); 142 143 d = &dtrace_debug_data[i]; 144 145 count = 0; 146 147 if (d->first < d->next) { 148 char *p1 = dtrace_debug_bufr; 149 150 count = (uintptr_t) d->next - (uintptr_t) d->first; 151 152 for (p = d->first; p < d->next; p++) 153 *p1++ = *p; 154 } else if (d->next > d->first) { 155 char *p1 = dtrace_debug_bufr; 156 157 count = (uintptr_t) d->last - (uintptr_t) d->first; 158 159 for (p = d->first; p < d->last; p++) 160 *p1++ = *p; 161 162 count += (uintptr_t) d->next - (uintptr_t) d->bufr; 163 164 for (p = d->bufr; p < d->next; p++) 165 *p1++ = *p; 166 } 167 168 d->first = d->bufr; 169 d->next = d->bufr; 170 171 dtrace_debug_unlock(i); 172 173 if (count > 0) { 174 char *last = dtrace_debug_bufr + count; 175 176 p = dtrace_debug_bufr; 177 178 while (p < last) { 179 if (*p == '\0') { 180 p++; 181 continue; 182 } 183 184 printf("%s", p); 185 186 p += strlen(p); 187 } 188 } 189 } 190 } 191 192 /* 193 * Functions below here are called from the probe context, so they can't call 194 * _any_ functions outside the dtrace module without running foul of the function 195 * boundary trace provider (fbt). The purpose of these functions is limited to 196 * buffering debug strings for output when the probe completes on the current CPU. 197 */ 198 199 static __inline void 200 dtrace_debug__putc(char c) 201 { 202 struct dtrace_debug_data *d = &dtrace_debug_data[curcpu]; 203 204 *d->next++ = c; 205 206 if (d->next == d->last) 207 d->next = d->bufr; 208 209 *(d->next) = '\0'; 210 211 if (d->next == d->first) 212 d->first++; 213 214 if (d->first == d->last) 215 d->first = d->bufr; 216 } 217 218 static void __used 219 dtrace_debug_putc(char c) 220 { 221 dtrace_debug_lock(curcpu); 222 223 dtrace_debug__putc(c); 224 225 dtrace_debug_unlock(curcpu); 226 } 227 228 static void __used 229 dtrace_debug_puts(const char *s) 230 { 231 dtrace_debug_lock(curcpu); 232 233 while (*s != '\0') 234 dtrace_debug__putc(*s++); 235 236 dtrace_debug__putc('\0'); 237 238 dtrace_debug_unlock(curcpu); 239 } 240 241 /* 242 * Snaffled from sys/kern/subr_prf.c 243 * 244 * Put a NUL-terminated ASCII number (base <= 36) in a buffer in reverse 245 * order; return an optional length and a pointer to the last character 246 * written in the buffer (i.e., the first character of the string). 247 * The buffer pointed to by `nbuf' must have length >= MAXNBUF. 248 */ 249 static char * 250 dtrace_debug_ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper) 251 { 252 char *p, c; 253 254 p = nbuf; 255 *p = '\0'; 256 do { 257 c = hex2ascii(num % base); 258 *++p = upper ? toupper(c) : c; 259 } while (num /= base); 260 if (lenp) 261 *lenp = p - nbuf; 262 return (p); 263 } 264 265 #define MAXNBUF (sizeof(intmax_t) * NBBY + 1) 266 267 static void 268 dtrace_debug_vprintf(const char *fmt, va_list ap) 269 { 270 char nbuf[MAXNBUF]; 271 const char *p, *percent, *q; 272 u_char *up; 273 int ch, n; 274 uintmax_t num; 275 int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot; 276 int cflag, hflag, jflag, tflag, zflag; 277 int dwidth, upper; 278 int radix = 10; 279 char padc; 280 int stop = 0, retval = 0; 281 282 num = 0; 283 284 if (fmt == NULL) 285 fmt = "(fmt null)\n"; 286 287 for (;;) { 288 padc = ' '; 289 width = 0; 290 while ((ch = (u_char)*fmt++) != '%' || stop) { 291 if (ch == '\0') { 292 dtrace_debug__putc('\0'); 293 return; 294 } 295 dtrace_debug__putc(ch); 296 } 297 percent = fmt - 1; 298 qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0; 299 sign = 0; dot = 0; dwidth = 0; upper = 0; 300 cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0; 301 reswitch: switch (ch = (u_char)*fmt++) { 302 case '.': 303 dot = 1; 304 goto reswitch; 305 case '#': 306 sharpflag = 1; 307 goto reswitch; 308 case '+': 309 sign = 1; 310 goto reswitch; 311 case '-': 312 ladjust = 1; 313 goto reswitch; 314 case '%': 315 dtrace_debug__putc(ch); 316 break; 317 case '*': 318 if (!dot) { 319 width = va_arg(ap, int); 320 if (width < 0) { 321 ladjust = !ladjust; 322 width = -width; 323 } 324 } else { 325 dwidth = va_arg(ap, int); 326 } 327 goto reswitch; 328 case '0': 329 if (!dot) { 330 padc = '0'; 331 goto reswitch; 332 } 333 case '1': case '2': case '3': case '4': 334 case '5': case '6': case '7': case '8': case '9': 335 for (n = 0;; ++fmt) { 336 n = n * 10 + ch - '0'; 337 ch = *fmt; 338 if (ch < '0' || ch > '9') 339 break; 340 } 341 if (dot) 342 dwidth = n; 343 else 344 width = n; 345 goto reswitch; 346 case 'b': 347 num = (u_int)va_arg(ap, int); 348 p = va_arg(ap, char *); 349 for (q = dtrace_debug_ksprintn(nbuf, num, *p++, NULL, 0); *q;) 350 dtrace_debug__putc(*q--); 351 352 if (num == 0) 353 break; 354 355 for (tmp = 0; *p;) { 356 n = *p++; 357 if (num & (1 << (n - 1))) { 358 dtrace_debug__putc(tmp ? ',' : '<'); 359 for (; (n = *p) > ' '; ++p) 360 dtrace_debug__putc(n); 361 tmp = 1; 362 } else 363 for (; *p > ' '; ++p) 364 continue; 365 } 366 if (tmp) 367 dtrace_debug__putc('>'); 368 break; 369 case 'c': 370 dtrace_debug__putc(va_arg(ap, int)); 371 break; 372 case 'D': 373 up = va_arg(ap, u_char *); 374 p = va_arg(ap, char *); 375 if (!width) 376 width = 16; 377 while(width--) { 378 dtrace_debug__putc(hex2ascii(*up >> 4)); 379 dtrace_debug__putc(hex2ascii(*up & 0x0f)); 380 up++; 381 if (width) 382 for (q=p;*q;q++) 383 dtrace_debug__putc(*q); 384 } 385 break; 386 case 'd': 387 case 'i': 388 base = 10; 389 sign = 1; 390 goto handle_sign; 391 case 'h': 392 if (hflag) { 393 hflag = 0; 394 cflag = 1; 395 } else 396 hflag = 1; 397 goto reswitch; 398 case 'j': 399 jflag = 1; 400 goto reswitch; 401 case 'l': 402 if (lflag) { 403 lflag = 0; 404 qflag = 1; 405 } else 406 lflag = 1; 407 goto reswitch; 408 case 'n': 409 if (jflag) 410 *(va_arg(ap, intmax_t *)) = retval; 411 else if (qflag) 412 *(va_arg(ap, quad_t *)) = retval; 413 else if (lflag) 414 *(va_arg(ap, long *)) = retval; 415 else if (zflag) 416 *(va_arg(ap, size_t *)) = retval; 417 else if (hflag) 418 *(va_arg(ap, short *)) = retval; 419 else if (cflag) 420 *(va_arg(ap, char *)) = retval; 421 else 422 *(va_arg(ap, int *)) = retval; 423 break; 424 case 'o': 425 base = 8; 426 goto handle_nosign; 427 case 'p': 428 base = 16; 429 sharpflag = (width == 0); 430 sign = 0; 431 num = (uintptr_t)va_arg(ap, void *); 432 goto number; 433 case 'q': 434 qflag = 1; 435 goto reswitch; 436 case 'r': 437 base = radix; 438 if (sign) 439 goto handle_sign; 440 goto handle_nosign; 441 case 's': 442 p = va_arg(ap, char *); 443 if (p == NULL) 444 p = "(null)"; 445 if (!dot) 446 n = strlen (p); 447 else 448 for (n = 0; n < dwidth && p[n]; n++) 449 continue; 450 451 width -= n; 452 453 if (!ladjust && width > 0) 454 while (width--) 455 dtrace_debug__putc(padc); 456 while (n--) 457 dtrace_debug__putc(*p++); 458 if (ladjust && width > 0) 459 while (width--) 460 dtrace_debug__putc(padc); 461 break; 462 case 't': 463 tflag = 1; 464 goto reswitch; 465 case 'u': 466 base = 10; 467 goto handle_nosign; 468 case 'X': 469 upper = 1; 470 case 'x': 471 base = 16; 472 goto handle_nosign; 473 case 'y': 474 base = 16; 475 sign = 1; 476 goto handle_sign; 477 case 'z': 478 zflag = 1; 479 goto reswitch; 480 handle_nosign: 481 sign = 0; 482 if (jflag) 483 num = va_arg(ap, uintmax_t); 484 else if (qflag) 485 num = va_arg(ap, u_quad_t); 486 else if (tflag) 487 num = va_arg(ap, ptrdiff_t); 488 else if (lflag) 489 num = va_arg(ap, u_long); 490 else if (zflag) 491 num = va_arg(ap, size_t); 492 else if (hflag) 493 num = (u_short)va_arg(ap, int); 494 else if (cflag) 495 num = (u_char)va_arg(ap, int); 496 else 497 num = va_arg(ap, u_int); 498 goto number; 499 handle_sign: 500 if (jflag) 501 num = va_arg(ap, intmax_t); 502 else if (qflag) 503 num = va_arg(ap, quad_t); 504 else if (tflag) 505 num = va_arg(ap, ptrdiff_t); 506 else if (lflag) 507 num = va_arg(ap, long); 508 else if (zflag) 509 num = va_arg(ap, size_t); 510 else if (hflag) 511 num = (short)va_arg(ap, int); 512 else if (cflag) 513 num = (char)va_arg(ap, int); 514 else 515 num = va_arg(ap, int); 516 number: 517 if (sign && (intmax_t)num < 0) { 518 neg = 1; 519 num = -(intmax_t)num; 520 } 521 p = dtrace_debug_ksprintn(nbuf, num, base, &tmp, upper); 522 if (sharpflag && num != 0) { 523 if (base == 8) 524 tmp++; 525 else if (base == 16) 526 tmp += 2; 527 } 528 if (neg) 529 tmp++; 530 531 if (!ladjust && padc != '0' && width 532 && (width -= tmp) > 0) 533 while (width--) 534 dtrace_debug__putc(padc); 535 if (neg) 536 dtrace_debug__putc('-'); 537 if (sharpflag && num != 0) { 538 if (base == 8) { 539 dtrace_debug__putc('0'); 540 } else if (base == 16) { 541 dtrace_debug__putc('0'); 542 dtrace_debug__putc('x'); 543 } 544 } 545 if (!ladjust && width && (width -= tmp) > 0) 546 while (width--) 547 dtrace_debug__putc(padc); 548 549 while (*p) 550 dtrace_debug__putc(*p--); 551 552 if (ladjust && width && (width -= tmp) > 0) 553 while (width--) 554 dtrace_debug__putc(padc); 555 556 break; 557 default: 558 while (percent < fmt) 559 dtrace_debug__putc(*percent++); 560 /* 561 * Since we ignore an formatting argument it is no 562 * longer safe to obey the remaining formatting 563 * arguments as the arguments will no longer match 564 * the format specs. 565 */ 566 stop = 1; 567 break; 568 } 569 } 570 571 dtrace_debug__putc('\0'); 572 } 573 574 void 575 dtrace_debug_printf(const char *fmt, ...) 576 { 577 va_list ap; 578 579 dtrace_debug_lock(curcpu); 580 581 va_start(ap, fmt); 582 583 dtrace_debug_vprintf(fmt, ap); 584 585 va_end(ap); 586 587 dtrace_debug_unlock(curcpu); 588 } 589 590 #else 591 592 #define dtrace_debug_output() 593 #define dtrace_debug_puts(_s) 594 #define dtrace_debug_printf(fmt, ...) 595 596 #endif 597