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