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