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