summaryrefslogtreecommitdiffstats
path: root/sys/cddl/dev/dtrace/dtrace_debug.c
diff options
context:
space:
mode:
authorjb <jb@FreeBSD.org>2008-05-23 05:59:42 +0000
committerjb <jb@FreeBSD.org>2008-05-23 05:59:42 +0000
commitbe12e1a50471524fb128cc615816dd08e60b353d (patch)
tree19da43b8882490dac0da41ddf4c15e8294765564 /sys/cddl/dev/dtrace/dtrace_debug.c
parentd6da92763e434b5a352932bf992e8eda0f1de268 (diff)
downloadFreeBSD-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.c596
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
OpenPOWER on IntegriCloud