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