diff options
author | jb <jb@FreeBSD.org> | 2008-05-23 05:59:42 +0000 |
---|---|---|
committer | jb <jb@FreeBSD.org> | 2008-05-23 05:59:42 +0000 |
commit | be12e1a50471524fb128cc615816dd08e60b353d (patch) | |
tree | 19da43b8882490dac0da41ddf4c15e8294765564 /sys/cddl/dev/dtrace/dtrace_debug.c | |
parent | d6da92763e434b5a352932bf992e8eda0f1de268 (diff) | |
download | FreeBSD-src-be12e1a50471524fb128cc615816dd08e60b353d.zip FreeBSD-src-be12e1a50471524fb128cc615816dd08e60b353d.tar.gz |
Custom DTrace kernel module files plus FreeBSD-specific DTrace providers.
Diffstat (limited to 'sys/cddl/dev/dtrace/dtrace_debug.c')
-rw-r--r-- | sys/cddl/dev/dtrace/dtrace_debug.c | 596 |
1 files changed, 596 insertions, 0 deletions
diff --git a/sys/cddl/dev/dtrace/dtrace_debug.c b/sys/cddl/dev/dtrace/dtrace_debug.c new file mode 100644 index 0000000..24a7a09 --- /dev/null +++ b/sys/cddl/dev/dtrace/dtrace_debug.c @@ -0,0 +1,596 @@ +/*- + * Copyright (C) 2008 John Birrell <jb@freebsd.org>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice(s), this list of conditions and the following disclaimer as + * the first lines of this file unmodified other than the possible + * addition of one or more copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * $FreeBSD$ + * + */ + +#ifdef DEBUG + +#if defined(__amd64__) +static __inline int +dtrace_cmpset_long(volatile u_long *dst, u_long exp, u_long src) +{ + u_char res; + + __asm __volatile( + " lock ; " + " cmpxchgq %2,%1 ; " + " sete %0 ; " + "1: " + "# dtrace_cmpset_long" + : "=a" (res), /* 0 */ + "=m" (*dst) /* 1 */ + : "r" (src), /* 2 */ + "a" (exp), /* 3 */ + "m" (*dst) /* 4 */ + : "memory"); + + return (res); +} +#elif defined(__i386__) +static __inline int +dtrace_cmpset_long(volatile u_long *dst, u_long exp, u_long src) +{ + u_char res; + + __asm __volatile( + " lock ; " + " cmpxchgl %2,%1 ; " + " sete %0 ; " + "1: " + "# dtrace_cmpset_long" + : "=a" (res), /* 0 */ + "=m" (*dst) /* 1 */ + : "r" (src), /* 2 */ + "a" (exp), /* 3 */ + "m" (*dst) /* 4 */ + : "memory"); + + return (res); +} +#endif + +#define DTRACE_DEBUG_BUFR_SIZE (32 * 1024) + +struct dtrace_debug_data { + char bufr[DTRACE_DEBUG_BUFR_SIZE]; + char *first; + char *last; + char *next; +} dtrace_debug_data[MAXCPU]; + +static char dtrace_debug_bufr[DTRACE_DEBUG_BUFR_SIZE]; + +static volatile u_long dtrace_debug_flag[MAXCPU]; + +static void +dtrace_debug_lock(int cpu) +{ + while (dtrace_cmpset_long(&dtrace_debug_flag[cpu], 0, 1) == 0) + /* Loop until the lock is obtained. */ + ; +} + +static void +dtrace_debug_unlock(int cpu) +{ + dtrace_debug_flag[cpu] = 0; +} + +static void +dtrace_debug_init(void *dummy) +{ + int i; + struct dtrace_debug_data *d; + + for (i = 0; i <= mp_maxid; i++) { + if (pcpu_find(i) == NULL) + continue; + + d = &dtrace_debug_data[i]; + + if (d->first == NULL) { + d->first = d->bufr; + d->next = d->bufr; + d->last = d->bufr + DTRACE_DEBUG_BUFR_SIZE - 1; + *(d->last) = '\0'; + } + } +} + +SYSINIT(dtrace_debug_init, SI_SUB_KDTRACE, SI_ORDER_ANY, dtrace_debug_init, NULL); +SYSINIT(dtrace_debug_smpinit, SI_SUB_SMP, SI_ORDER_ANY, dtrace_debug_init, NULL); + +static void +dtrace_debug_output(void) +{ + char *p; + int i; + struct dtrace_debug_data *d; + uintptr_t count; + + for (i = 0; i <= mp_maxid; i++) { + if (pcpu_find(i) == NULL) + continue; + + dtrace_debug_lock(i); + + d = &dtrace_debug_data[i]; + + count = 0; + + if (d->first < d->next) { + char *p1 = dtrace_debug_bufr; + + count = (uintptr_t) d->next - (uintptr_t) d->first; + + for (p = d->first; p < d->next; p++) + *p1++ = *p; + } else if (d->next > d->first) { + char *p1 = dtrace_debug_bufr; + + count = (uintptr_t) d->last - (uintptr_t) d->first; + + for (p = d->first; p < d->last; p++) + *p1++ = *p; + + count += (uintptr_t) d->next - (uintptr_t) d->bufr; + + for (p = d->bufr; p < d->next; p++) + *p1++ = *p; + } + + d->first = d->bufr; + d->next = d->bufr; + + dtrace_debug_unlock(i); + + if (count > 0) { + char *last = dtrace_debug_bufr + count; + + p = dtrace_debug_bufr; + + while (p < last) { + if (*p == '\0') { + p++; + continue; + } + + printf("%s", p); + + p += strlen(p); + } + } + } +} + +/* + * Functions below here are called from the probe context, so they can't call + * _any_ functions outside the dtrace module without running foul of the function + * boundary trace provider (fbt). The purpose of these functions is limited to + * buffering debug strings for output when the probe completes on the current CPU. + */ + +static __inline void +dtrace_debug__putc(char c) +{ + struct dtrace_debug_data *d = &dtrace_debug_data[curcpu]; + + *d->next++ = c; + + if (d->next == d->last) + d->next = d->bufr; + + *(d->next) = '\0'; + + if (d->next == d->first) + d->first++; + + if (d->first == d->last) + d->first = d->bufr; +} + +static void __used +dtrace_debug_putc(char c) +{ + dtrace_debug_lock(curcpu); + + dtrace_debug__putc(c); + + dtrace_debug_unlock(curcpu); +} + +static void __used +dtrace_debug_puts(const char *s) +{ + dtrace_debug_lock(curcpu); + + while (*s != '\0') + dtrace_debug__putc(*s++); + + dtrace_debug__putc('\0'); + + dtrace_debug_unlock(curcpu); +} + +/* + * Snaffled from sys/kern/subr_prf.c + * + * Put a NUL-terminated ASCII number (base <= 36) in a buffer in reverse + * order; return an optional length and a pointer to the last character + * written in the buffer (i.e., the first character of the string). + * The buffer pointed to by `nbuf' must have length >= MAXNBUF. + */ +static char * +dtrace_debug_ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper) +{ + char *p, c; + + p = nbuf; + *p = '\0'; + do { + c = hex2ascii(num % base); + *++p = upper ? toupper(c) : c; + } while (num /= base); + if (lenp) + *lenp = p - nbuf; + return (p); +} + +#define MAXNBUF (sizeof(intmax_t) * NBBY + 1) + +static void +dtrace_debug_vprintf(const char *fmt, va_list ap) +{ + char nbuf[MAXNBUF]; + const char *p, *percent, *q; + u_char *up; + int ch, n; + uintmax_t num; + int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot; + int cflag, hflag, jflag, tflag, zflag; + int dwidth, upper; + int radix = 10; + char padc; + int stop = 0, retval = 0; + + num = 0; + + if (fmt == NULL) + fmt = "(fmt null)\n"; + + for (;;) { + padc = ' '; + width = 0; + while ((ch = (u_char)*fmt++) != '%' || stop) { + if (ch == '\0') { + dtrace_debug__putc('\0'); + return; + } + dtrace_debug__putc(ch); + } + percent = fmt - 1; + qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0; + sign = 0; dot = 0; dwidth = 0; upper = 0; + cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0; +reswitch: switch (ch = (u_char)*fmt++) { + case '.': + dot = 1; + goto reswitch; + case '#': + sharpflag = 1; + goto reswitch; + case '+': + sign = 1; + goto reswitch; + case '-': + ladjust = 1; + goto reswitch; + case '%': + dtrace_debug__putc(ch); + break; + case '*': + if (!dot) { + width = va_arg(ap, int); + if (width < 0) { + ladjust = !ladjust; + width = -width; + } + } else { + dwidth = va_arg(ap, int); + } + goto reswitch; + case '0': + if (!dot) { + padc = '0'; + goto reswitch; + } + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + for (n = 0;; ++fmt) { + n = n * 10 + ch - '0'; + ch = *fmt; + if (ch < '0' || ch > '9') + break; + } + if (dot) + dwidth = n; + else + width = n; + goto reswitch; + case 'b': + num = (u_int)va_arg(ap, int); + p = va_arg(ap, char *); + for (q = dtrace_debug_ksprintn(nbuf, num, *p++, NULL, 0); *q;) + dtrace_debug__putc(*q--); + + if (num == 0) + break; + + for (tmp = 0; *p;) { + n = *p++; + if (num & (1 << (n - 1))) { + dtrace_debug__putc(tmp ? ',' : '<'); + for (; (n = *p) > ' '; ++p) + dtrace_debug__putc(n); + tmp = 1; + } else + for (; *p > ' '; ++p) + continue; + } + if (tmp) + dtrace_debug__putc('>'); + break; + case 'c': + dtrace_debug__putc(va_arg(ap, int)); + break; + case 'D': + up = va_arg(ap, u_char *); + p = va_arg(ap, char *); + if (!width) + width = 16; + while(width--) { + dtrace_debug__putc(hex2ascii(*up >> 4)); + dtrace_debug__putc(hex2ascii(*up & 0x0f)); + up++; + if (width) + for (q=p;*q;q++) + dtrace_debug__putc(*q); + } + break; + case 'd': + case 'i': + base = 10; + sign = 1; + goto handle_sign; + case 'h': + if (hflag) { + hflag = 0; + cflag = 1; + } else + hflag = 1; + goto reswitch; + case 'j': + jflag = 1; + goto reswitch; + case 'l': + if (lflag) { + lflag = 0; + qflag = 1; + } else + lflag = 1; + goto reswitch; + case 'n': + if (jflag) + *(va_arg(ap, intmax_t *)) = retval; + else if (qflag) + *(va_arg(ap, quad_t *)) = retval; + else if (lflag) + *(va_arg(ap, long *)) = retval; + else if (zflag) + *(va_arg(ap, size_t *)) = retval; + else if (hflag) + *(va_arg(ap, short *)) = retval; + else if (cflag) + *(va_arg(ap, char *)) = retval; + else + *(va_arg(ap, int *)) = retval; + break; + case 'o': + base = 8; + goto handle_nosign; + case 'p': + base = 16; + sharpflag = (width == 0); + sign = 0; + num = (uintptr_t)va_arg(ap, void *); + goto number; + case 'q': + qflag = 1; + goto reswitch; + case 'r': + base = radix; + if (sign) + goto handle_sign; + goto handle_nosign; + case 's': + p = va_arg(ap, char *); + if (p == NULL) + p = "(null)"; + if (!dot) + n = strlen (p); + else + for (n = 0; n < dwidth && p[n]; n++) + continue; + + width -= n; + + if (!ladjust && width > 0) + while (width--) + dtrace_debug__putc(padc); + while (n--) + dtrace_debug__putc(*p++); + if (ladjust && width > 0) + while (width--) + dtrace_debug__putc(padc); + break; + case 't': + tflag = 1; + goto reswitch; + case 'u': + base = 10; + goto handle_nosign; + case 'X': + upper = 1; + case 'x': + base = 16; + goto handle_nosign; + case 'y': + base = 16; + sign = 1; + goto handle_sign; + case 'z': + zflag = 1; + goto reswitch; +handle_nosign: + sign = 0; + if (jflag) + num = va_arg(ap, uintmax_t); + else if (qflag) + num = va_arg(ap, u_quad_t); + else if (tflag) + num = va_arg(ap, ptrdiff_t); + else if (lflag) + num = va_arg(ap, u_long); + else if (zflag) + num = va_arg(ap, size_t); + else if (hflag) + num = (u_short)va_arg(ap, int); + else if (cflag) + num = (u_char)va_arg(ap, int); + else + num = va_arg(ap, u_int); + goto number; +handle_sign: + if (jflag) + num = va_arg(ap, intmax_t); + else if (qflag) + num = va_arg(ap, quad_t); + else if (tflag) + num = va_arg(ap, ptrdiff_t); + else if (lflag) + num = va_arg(ap, long); + else if (zflag) + num = va_arg(ap, size_t); + else if (hflag) + num = (short)va_arg(ap, int); + else if (cflag) + num = (char)va_arg(ap, int); + else + num = va_arg(ap, int); +number: + if (sign && (intmax_t)num < 0) { + neg = 1; + num = -(intmax_t)num; + } + p = dtrace_debug_ksprintn(nbuf, num, base, &tmp, upper); + if (sharpflag && num != 0) { + if (base == 8) + tmp++; + else if (base == 16) + tmp += 2; + } + if (neg) + tmp++; + + if (!ladjust && padc != '0' && width + && (width -= tmp) > 0) + while (width--) + dtrace_debug__putc(padc); + if (neg) + dtrace_debug__putc('-'); + if (sharpflag && num != 0) { + if (base == 8) { + dtrace_debug__putc('0'); + } else if (base == 16) { + dtrace_debug__putc('0'); + dtrace_debug__putc('x'); + } + } + if (!ladjust && width && (width -= tmp) > 0) + while (width--) + dtrace_debug__putc(padc); + + while (*p) + dtrace_debug__putc(*p--); + + if (ladjust && width && (width -= tmp) > 0) + while (width--) + dtrace_debug__putc(padc); + + break; + default: + while (percent < fmt) + dtrace_debug__putc(*percent++); + /* + * Since we ignore an formatting argument it is no + * longer safe to obey the remaining formatting + * arguments as the arguments will no longer match + * the format specs. + */ + stop = 1; + break; + } + } + + dtrace_debug__putc('\0'); +} + +void +dtrace_debug_printf(const char *fmt, ...) +{ + va_list ap; + + dtrace_debug_lock(curcpu); + + va_start(ap, fmt); + + dtrace_debug_vprintf(fmt, ap); + + va_end(ap); + + dtrace_debug_unlock(curcpu); +} + +#else + +#define dtrace_debug_output() +#define dtrace_debug_puts(_s) +#define dtrace_debug_printf(fmt, ...) + +#endif |