1179237Sjb/*- 2179237Sjb * Copyright (C) 2008 John Birrell <jb@freebsd.org>. 3179237Sjb * All rights reserved. 4179237Sjb * 5179237Sjb * Redistribution and use in source and binary forms, with or without 6179237Sjb * modification, are permitted provided that the following conditions 7179237Sjb * are met: 8179237Sjb * 1. Redistributions of source code must retain the above copyright 9179237Sjb * notice(s), this list of conditions and the following disclaimer as 10179237Sjb * the first lines of this file unmodified other than the possible 11179237Sjb * addition of one or more copyright notices. 12179237Sjb * 2. Redistributions in binary form must reproduce the above copyright 13179237Sjb * notice(s), this list of conditions and the following disclaimer in the 14179237Sjb * documentation and/or other materials provided with the distribution. 15179237Sjb * 16179237Sjb * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY 17179237Sjb * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18179237Sjb * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19179237Sjb * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY 20179237Sjb * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21179237Sjb * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22179237Sjb * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23179237Sjb * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24179237Sjb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25179237Sjb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 26179237Sjb * DAMAGE. 27179237Sjb * 28179237Sjb * $FreeBSD$ 29179237Sjb * 30179237Sjb */ 31179237Sjb 32179237Sjb#ifdef DEBUG 33179237Sjb 34226452Smarcel#include <machine/atomic.h> 35179237Sjb 36179237Sjb#define DTRACE_DEBUG_BUFR_SIZE (32 * 1024) 37179237Sjb 38179237Sjbstruct dtrace_debug_data { 39244631Srstone uintptr_t lock __aligned(CACHE_LINE_SIZE); 40179237Sjb char bufr[DTRACE_DEBUG_BUFR_SIZE]; 41179237Sjb char *first; 42179237Sjb char *last; 43179237Sjb char *next; 44179237Sjb} dtrace_debug_data[MAXCPU]; 45179237Sjb 46179237Sjbstatic char dtrace_debug_bufr[DTRACE_DEBUG_BUFR_SIZE]; 47179237Sjb 48179237Sjbstatic void 49179237Sjbdtrace_debug_lock(int cpu) 50179237Sjb{ 51244631Srstone uintptr_t tid; 52244631Srstone 53244631Srstone tid = (uintptr_t)curthread; 54244631Srstone spinlock_enter(); 55244631Srstone while (atomic_cmpset_acq_ptr(&dtrace_debug_data[cpu].lock, 0, tid) == 0) /* Loop until the lock is obtained. */ 56179237Sjb ; 57179237Sjb} 58179237Sjb 59179237Sjbstatic void 60179237Sjbdtrace_debug_unlock(int cpu) 61179237Sjb{ 62244631Srstone atomic_store_rel_ptr(&dtrace_debug_data[cpu].lock, 0); 63244631Srstone spinlock_exit(); 64179237Sjb} 65179237Sjb 66179237Sjbstatic void 67179237Sjbdtrace_debug_init(void *dummy) 68179237Sjb{ 69179237Sjb int i; 70179237Sjb struct dtrace_debug_data *d; 71179237Sjb 72209059Sjhb CPU_FOREACH(i) { 73179237Sjb d = &dtrace_debug_data[i]; 74179237Sjb 75179237Sjb if (d->first == NULL) { 76179237Sjb d->first = d->bufr; 77179237Sjb d->next = d->bufr; 78179237Sjb d->last = d->bufr + DTRACE_DEBUG_BUFR_SIZE - 1; 79179237Sjb *(d->last) = '\0'; 80179237Sjb } 81179237Sjb } 82179237Sjb} 83179237Sjb 84179237SjbSYSINIT(dtrace_debug_init, SI_SUB_KDTRACE, SI_ORDER_ANY, dtrace_debug_init, NULL); 85179237SjbSYSINIT(dtrace_debug_smpinit, SI_SUB_SMP, SI_ORDER_ANY, dtrace_debug_init, NULL); 86179237Sjb 87179237Sjbstatic void 88179237Sjbdtrace_debug_output(void) 89179237Sjb{ 90179237Sjb char *p; 91179237Sjb int i; 92179237Sjb struct dtrace_debug_data *d; 93179237Sjb uintptr_t count; 94179237Sjb 95209059Sjhb CPU_FOREACH(i) { 96179237Sjb dtrace_debug_lock(i); 97179237Sjb 98179237Sjb d = &dtrace_debug_data[i]; 99179237Sjb 100179237Sjb count = 0; 101179237Sjb 102179237Sjb if (d->first < d->next) { 103179237Sjb char *p1 = dtrace_debug_bufr; 104179237Sjb 105179237Sjb count = (uintptr_t) d->next - (uintptr_t) d->first; 106179237Sjb 107179237Sjb for (p = d->first; p < d->next; p++) 108179237Sjb *p1++ = *p; 109179237Sjb } else if (d->next > d->first) { 110179237Sjb char *p1 = dtrace_debug_bufr; 111179237Sjb 112179237Sjb count = (uintptr_t) d->last - (uintptr_t) d->first; 113179237Sjb 114179237Sjb for (p = d->first; p < d->last; p++) 115179237Sjb *p1++ = *p; 116179237Sjb 117179237Sjb count += (uintptr_t) d->next - (uintptr_t) d->bufr; 118179237Sjb 119179237Sjb for (p = d->bufr; p < d->next; p++) 120179237Sjb *p1++ = *p; 121179237Sjb } 122179237Sjb 123179237Sjb d->first = d->bufr; 124179237Sjb d->next = d->bufr; 125179237Sjb 126179237Sjb dtrace_debug_unlock(i); 127179237Sjb 128179237Sjb if (count > 0) { 129179237Sjb char *last = dtrace_debug_bufr + count; 130179237Sjb 131179237Sjb p = dtrace_debug_bufr; 132179237Sjb 133179237Sjb while (p < last) { 134179237Sjb if (*p == '\0') { 135179237Sjb p++; 136179237Sjb continue; 137179237Sjb } 138179237Sjb 139179237Sjb printf("%s", p); 140179237Sjb 141179237Sjb p += strlen(p); 142179237Sjb } 143179237Sjb } 144179237Sjb } 145179237Sjb} 146179237Sjb 147179237Sjb/* 148179237Sjb * Functions below here are called from the probe context, so they can't call 149179237Sjb * _any_ functions outside the dtrace module without running foul of the function 150179237Sjb * boundary trace provider (fbt). The purpose of these functions is limited to 151179237Sjb * buffering debug strings for output when the probe completes on the current CPU. 152179237Sjb */ 153179237Sjb 154179237Sjbstatic __inline void 155244631Srstonedtrace_debug__putc(int cpu, char c) 156179237Sjb{ 157244631Srstone struct dtrace_debug_data *d; 158179237Sjb 159244631Srstone d = &dtrace_debug_data[cpu]; 160179237Sjb *d->next++ = c; 161179237Sjb 162179237Sjb if (d->next == d->last) 163179237Sjb d->next = d->bufr; 164179237Sjb 165179237Sjb *(d->next) = '\0'; 166179237Sjb 167179237Sjb if (d->next == d->first) 168179237Sjb d->first++; 169179237Sjb 170179237Sjb if (d->first == d->last) 171179237Sjb d->first = d->bufr; 172179237Sjb} 173179237Sjb 174179237Sjbstatic void __used 175179237Sjbdtrace_debug_putc(char c) 176179237Sjb{ 177244631Srstone int cpu; 178179237Sjb 179244631Srstone cpu = curcpu; 180244631Srstone dtrace_debug_lock(cpu); 181179237Sjb 182244631Srstone dtrace_debug__putc(cpu, c); 183244631Srstone 184244631Srstone dtrace_debug_unlock(cpu); 185179237Sjb} 186179237Sjb 187179237Sjbstatic void __used 188179237Sjbdtrace_debug_puts(const char *s) 189179237Sjb{ 190244631Srstone int cpu; 191244631Srstone 192244631Srstone cpu = curcpu; 193244631Srstone dtrace_debug_lock(cpu); 194179237Sjb 195179237Sjb while (*s != '\0') 196244631Srstone dtrace_debug__putc(cpu, *s++); 197179237Sjb 198244631Srstone dtrace_debug__putc(cpu, '\0'); 199179237Sjb 200244631Srstone dtrace_debug_unlock(cpu); 201179237Sjb} 202179237Sjb 203179237Sjb/* 204179237Sjb * Snaffled from sys/kern/subr_prf.c 205179237Sjb * 206179237Sjb * Put a NUL-terminated ASCII number (base <= 36) in a buffer in reverse 207179237Sjb * order; return an optional length and a pointer to the last character 208179237Sjb * written in the buffer (i.e., the first character of the string). 209179237Sjb * The buffer pointed to by `nbuf' must have length >= MAXNBUF. 210179237Sjb */ 211179237Sjbstatic char * 212179237Sjbdtrace_debug_ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper) 213179237Sjb{ 214179237Sjb char *p, c; 215179237Sjb 216179237Sjb p = nbuf; 217179237Sjb *p = '\0'; 218179237Sjb do { 219179237Sjb c = hex2ascii(num % base); 220179237Sjb *++p = upper ? toupper(c) : c; 221179237Sjb } while (num /= base); 222179237Sjb if (lenp) 223179237Sjb *lenp = p - nbuf; 224179237Sjb return (p); 225179237Sjb} 226179237Sjb 227179237Sjb#define MAXNBUF (sizeof(intmax_t) * NBBY + 1) 228179237Sjb 229179237Sjbstatic void 230244631Srstonedtrace_debug_vprintf(int cpu, const char *fmt, va_list ap) 231179237Sjb{ 232179237Sjb char nbuf[MAXNBUF]; 233179237Sjb const char *p, *percent, *q; 234179237Sjb u_char *up; 235179237Sjb int ch, n; 236179237Sjb uintmax_t num; 237179237Sjb int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot; 238179237Sjb int cflag, hflag, jflag, tflag, zflag; 239179237Sjb int dwidth, upper; 240179237Sjb int radix = 10; 241179237Sjb char padc; 242179237Sjb int stop = 0, retval = 0; 243179237Sjb 244179237Sjb num = 0; 245179237Sjb 246179237Sjb if (fmt == NULL) 247179237Sjb fmt = "(fmt null)\n"; 248179237Sjb 249179237Sjb for (;;) { 250179237Sjb padc = ' '; 251179237Sjb width = 0; 252179237Sjb while ((ch = (u_char)*fmt++) != '%' || stop) { 253179237Sjb if (ch == '\0') { 254244631Srstone dtrace_debug__putc(cpu, '\0'); 255179237Sjb return; 256179237Sjb } 257244631Srstone dtrace_debug__putc(cpu, ch); 258179237Sjb } 259179237Sjb percent = fmt - 1; 260179237Sjb qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0; 261179237Sjb sign = 0; dot = 0; dwidth = 0; upper = 0; 262179237Sjb cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0; 263179237Sjbreswitch: switch (ch = (u_char)*fmt++) { 264179237Sjb case '.': 265179237Sjb dot = 1; 266179237Sjb goto reswitch; 267179237Sjb case '#': 268179237Sjb sharpflag = 1; 269179237Sjb goto reswitch; 270179237Sjb case '+': 271179237Sjb sign = 1; 272179237Sjb goto reswitch; 273179237Sjb case '-': 274179237Sjb ladjust = 1; 275179237Sjb goto reswitch; 276179237Sjb case '%': 277244631Srstone dtrace_debug__putc(cpu, ch); 278179237Sjb break; 279179237Sjb case '*': 280179237Sjb if (!dot) { 281179237Sjb width = va_arg(ap, int); 282179237Sjb if (width < 0) { 283179237Sjb ladjust = !ladjust; 284179237Sjb width = -width; 285179237Sjb } 286179237Sjb } else { 287179237Sjb dwidth = va_arg(ap, int); 288179237Sjb } 289179237Sjb goto reswitch; 290179237Sjb case '0': 291179237Sjb if (!dot) { 292179237Sjb padc = '0'; 293179237Sjb goto reswitch; 294179237Sjb } 295179237Sjb case '1': case '2': case '3': case '4': 296179237Sjb case '5': case '6': case '7': case '8': case '9': 297179237Sjb for (n = 0;; ++fmt) { 298179237Sjb n = n * 10 + ch - '0'; 299179237Sjb ch = *fmt; 300179237Sjb if (ch < '0' || ch > '9') 301179237Sjb break; 302179237Sjb } 303179237Sjb if (dot) 304179237Sjb dwidth = n; 305179237Sjb else 306179237Sjb width = n; 307179237Sjb goto reswitch; 308179237Sjb case 'b': 309179237Sjb num = (u_int)va_arg(ap, int); 310179237Sjb p = va_arg(ap, char *); 311179237Sjb for (q = dtrace_debug_ksprintn(nbuf, num, *p++, NULL, 0); *q;) 312244631Srstone dtrace_debug__putc(cpu, *q--); 313179237Sjb 314179237Sjb if (num == 0) 315179237Sjb break; 316179237Sjb 317179237Sjb for (tmp = 0; *p;) { 318179237Sjb n = *p++; 319179237Sjb if (num & (1 << (n - 1))) { 320244631Srstone dtrace_debug__putc(cpu, tmp ? ',' : '<'); 321179237Sjb for (; (n = *p) > ' '; ++p) 322244631Srstone dtrace_debug__putc(cpu, n); 323179237Sjb tmp = 1; 324179237Sjb } else 325179237Sjb for (; *p > ' '; ++p) 326179237Sjb continue; 327179237Sjb } 328179237Sjb if (tmp) 329244631Srstone dtrace_debug__putc(cpu, '>'); 330179237Sjb break; 331179237Sjb case 'c': 332244631Srstone dtrace_debug__putc(cpu, va_arg(ap, int)); 333179237Sjb break; 334179237Sjb case 'D': 335179237Sjb up = va_arg(ap, u_char *); 336179237Sjb p = va_arg(ap, char *); 337179237Sjb if (!width) 338179237Sjb width = 16; 339179237Sjb while(width--) { 340244631Srstone dtrace_debug__putc(cpu, hex2ascii(*up >> 4)); 341244631Srstone dtrace_debug__putc(cpu, hex2ascii(*up & 0x0f)); 342179237Sjb up++; 343179237Sjb if (width) 344179237Sjb for (q=p;*q;q++) 345244631Srstone dtrace_debug__putc(cpu, *q); 346179237Sjb } 347179237Sjb break; 348179237Sjb case 'd': 349179237Sjb case 'i': 350179237Sjb base = 10; 351179237Sjb sign = 1; 352179237Sjb goto handle_sign; 353179237Sjb case 'h': 354179237Sjb if (hflag) { 355179237Sjb hflag = 0; 356179237Sjb cflag = 1; 357179237Sjb } else 358179237Sjb hflag = 1; 359179237Sjb goto reswitch; 360179237Sjb case 'j': 361179237Sjb jflag = 1; 362179237Sjb goto reswitch; 363179237Sjb case 'l': 364179237Sjb if (lflag) { 365179237Sjb lflag = 0; 366179237Sjb qflag = 1; 367179237Sjb } else 368179237Sjb lflag = 1; 369179237Sjb goto reswitch; 370179237Sjb case 'n': 371179237Sjb if (jflag) 372179237Sjb *(va_arg(ap, intmax_t *)) = retval; 373179237Sjb else if (qflag) 374179237Sjb *(va_arg(ap, quad_t *)) = retval; 375179237Sjb else if (lflag) 376179237Sjb *(va_arg(ap, long *)) = retval; 377179237Sjb else if (zflag) 378179237Sjb *(va_arg(ap, size_t *)) = retval; 379179237Sjb else if (hflag) 380179237Sjb *(va_arg(ap, short *)) = retval; 381179237Sjb else if (cflag) 382179237Sjb *(va_arg(ap, char *)) = retval; 383179237Sjb else 384179237Sjb *(va_arg(ap, int *)) = retval; 385179237Sjb break; 386179237Sjb case 'o': 387179237Sjb base = 8; 388179237Sjb goto handle_nosign; 389179237Sjb case 'p': 390179237Sjb base = 16; 391179237Sjb sharpflag = (width == 0); 392179237Sjb sign = 0; 393179237Sjb num = (uintptr_t)va_arg(ap, void *); 394179237Sjb goto number; 395179237Sjb case 'q': 396179237Sjb qflag = 1; 397179237Sjb goto reswitch; 398179237Sjb case 'r': 399179237Sjb base = radix; 400179237Sjb if (sign) 401179237Sjb goto handle_sign; 402179237Sjb goto handle_nosign; 403179237Sjb case 's': 404179237Sjb p = va_arg(ap, char *); 405179237Sjb if (p == NULL) 406179237Sjb p = "(null)"; 407179237Sjb if (!dot) 408179237Sjb n = strlen (p); 409179237Sjb else 410179237Sjb for (n = 0; n < dwidth && p[n]; n++) 411179237Sjb continue; 412179237Sjb 413179237Sjb width -= n; 414179237Sjb 415179237Sjb if (!ladjust && width > 0) 416179237Sjb while (width--) 417244631Srstone dtrace_debug__putc(cpu, padc); 418179237Sjb while (n--) 419244631Srstone dtrace_debug__putc(cpu, *p++); 420179237Sjb if (ladjust && width > 0) 421179237Sjb while (width--) 422244631Srstone dtrace_debug__putc(cpu, padc); 423179237Sjb break; 424179237Sjb case 't': 425179237Sjb tflag = 1; 426179237Sjb goto reswitch; 427179237Sjb case 'u': 428179237Sjb base = 10; 429179237Sjb goto handle_nosign; 430179237Sjb case 'X': 431179237Sjb upper = 1; 432179237Sjb case 'x': 433179237Sjb base = 16; 434179237Sjb goto handle_nosign; 435179237Sjb case 'y': 436179237Sjb base = 16; 437179237Sjb sign = 1; 438179237Sjb goto handle_sign; 439179237Sjb case 'z': 440179237Sjb zflag = 1; 441179237Sjb goto reswitch; 442179237Sjbhandle_nosign: 443179237Sjb sign = 0; 444179237Sjb if (jflag) 445179237Sjb num = va_arg(ap, uintmax_t); 446179237Sjb else if (qflag) 447179237Sjb num = va_arg(ap, u_quad_t); 448179237Sjb else if (tflag) 449179237Sjb num = va_arg(ap, ptrdiff_t); 450179237Sjb else if (lflag) 451179237Sjb num = va_arg(ap, u_long); 452179237Sjb else if (zflag) 453179237Sjb num = va_arg(ap, size_t); 454179237Sjb else if (hflag) 455179237Sjb num = (u_short)va_arg(ap, int); 456179237Sjb else if (cflag) 457179237Sjb num = (u_char)va_arg(ap, int); 458179237Sjb else 459179237Sjb num = va_arg(ap, u_int); 460179237Sjb goto number; 461179237Sjbhandle_sign: 462179237Sjb if (jflag) 463179237Sjb num = va_arg(ap, intmax_t); 464179237Sjb else if (qflag) 465179237Sjb num = va_arg(ap, quad_t); 466179237Sjb else if (tflag) 467179237Sjb num = va_arg(ap, ptrdiff_t); 468179237Sjb else if (lflag) 469179237Sjb num = va_arg(ap, long); 470179237Sjb else if (zflag) 471179237Sjb num = va_arg(ap, size_t); 472179237Sjb else if (hflag) 473179237Sjb num = (short)va_arg(ap, int); 474179237Sjb else if (cflag) 475179237Sjb num = (char)va_arg(ap, int); 476179237Sjb else 477179237Sjb num = va_arg(ap, int); 478179237Sjbnumber: 479179237Sjb if (sign && (intmax_t)num < 0) { 480179237Sjb neg = 1; 481179237Sjb num = -(intmax_t)num; 482179237Sjb } 483179237Sjb p = dtrace_debug_ksprintn(nbuf, num, base, &tmp, upper); 484179237Sjb if (sharpflag && num != 0) { 485179237Sjb if (base == 8) 486179237Sjb tmp++; 487179237Sjb else if (base == 16) 488179237Sjb tmp += 2; 489179237Sjb } 490179237Sjb if (neg) 491179237Sjb tmp++; 492179237Sjb 493179237Sjb if (!ladjust && padc != '0' && width 494179237Sjb && (width -= tmp) > 0) 495179237Sjb while (width--) 496244631Srstone dtrace_debug__putc(cpu, padc); 497179237Sjb if (neg) 498244631Srstone dtrace_debug__putc(cpu, '-'); 499179237Sjb if (sharpflag && num != 0) { 500179237Sjb if (base == 8) { 501244631Srstone dtrace_debug__putc(cpu, '0'); 502179237Sjb } else if (base == 16) { 503244631Srstone dtrace_debug__putc(cpu, '0'); 504244631Srstone dtrace_debug__putc(cpu, 'x'); 505179237Sjb } 506179237Sjb } 507179237Sjb if (!ladjust && width && (width -= tmp) > 0) 508179237Sjb while (width--) 509244631Srstone dtrace_debug__putc(cpu, padc); 510179237Sjb 511179237Sjb while (*p) 512244631Srstone dtrace_debug__putc(cpu, *p--); 513179237Sjb 514179237Sjb if (ladjust && width && (width -= tmp) > 0) 515179237Sjb while (width--) 516244631Srstone dtrace_debug__putc(cpu, padc); 517179237Sjb 518179237Sjb break; 519179237Sjb default: 520179237Sjb while (percent < fmt) 521244631Srstone dtrace_debug__putc(cpu, *percent++); 522179237Sjb /* 523179237Sjb * Since we ignore an formatting argument it is no 524179237Sjb * longer safe to obey the remaining formatting 525179237Sjb * arguments as the arguments will no longer match 526179237Sjb * the format specs. 527179237Sjb */ 528179237Sjb stop = 1; 529179237Sjb break; 530179237Sjb } 531179237Sjb } 532179237Sjb 533244631Srstone dtrace_debug__putc(cpu, '\0'); 534179237Sjb} 535179237Sjb 536179237Sjbvoid 537179237Sjbdtrace_debug_printf(const char *fmt, ...) 538179237Sjb{ 539179237Sjb va_list ap; 540244631Srstone int cpu; 541179237Sjb 542244631Srstone cpu = curcpu; 543244631Srstone dtrace_debug_lock(cpu); 544179237Sjb 545179237Sjb va_start(ap, fmt); 546179237Sjb 547244631Srstone dtrace_debug_vprintf(cpu, fmt, ap); 548179237Sjb 549179237Sjb va_end(ap); 550179237Sjb 551244631Srstone dtrace_debug_unlock(cpu); 552179237Sjb} 553179237Sjb 554179237Sjb#else 555179237Sjb 556179237Sjb#define dtrace_debug_output() 557179237Sjb#define dtrace_debug_puts(_s) 558179237Sjb#define dtrace_debug_printf(fmt, ...) 559179237Sjb 560179237Sjb#endif 561