aboutsummaryrefslogtreecommitdiff
path: root/sys/cddl/dev/dtrace/dtrace_debug.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/cddl/dev/dtrace/dtrace_debug.c')
-rw-r--r--sys/cddl/dev/dtrace/dtrace_debug.c560
1 files changed, 560 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 000000000000..3b296624c34a
--- /dev/null
+++ b/sys/cddl/dev/dtrace/dtrace_debug.c
@@ -0,0 +1,560 @@
+/*-
+ * 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
+
+#include <machine/atomic.h>
+
+#define DTRACE_DEBUG_BUFR_SIZE (32 * 1024)
+
+struct dtrace_debug_data {
+ uintptr_t lock __aligned(CACHE_LINE_SIZE);
+ 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 void
+dtrace_debug_lock(int cpu)
+{
+ uintptr_t tid;
+
+ tid = (uintptr_t)curthread;
+ spinlock_enter();
+ while (atomic_cmpset_acq_ptr(&dtrace_debug_data[cpu].lock, 0, tid) == 0) /* Loop until the lock is obtained. */
+ ;
+}
+
+static void
+dtrace_debug_unlock(int cpu)
+{
+ atomic_store_rel_ptr(&dtrace_debug_data[cpu].lock, 0);
+ spinlock_exit();
+}
+
+static void
+dtrace_debug_init(void *dummy)
+{
+ int i;
+ struct dtrace_debug_data *d;
+
+ CPU_FOREACH(i) {
+ 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;
+
+ CPU_FOREACH(i) {
+ 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(int cpu, char c)
+{
+ struct dtrace_debug_data *d;
+
+ d = &dtrace_debug_data[cpu];
+ *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)
+{
+ int cpu;
+
+ cpu = curcpu;
+ dtrace_debug_lock(cpu);
+
+ dtrace_debug__putc(cpu, c);
+
+ dtrace_debug_unlock(cpu);
+}
+
+static void __used
+dtrace_debug_puts(const char *s)
+{
+ int cpu;
+
+ cpu = curcpu;
+ dtrace_debug_lock(cpu);
+
+ while (*s != '\0')
+ dtrace_debug__putc(cpu, *s++);
+
+ dtrace_debug__putc(cpu, '\0');
+
+ dtrace_debug_unlock(cpu);
+}
+
+/*
+ * 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(int cpu, 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(cpu, '\0');
+ return;
+ }
+ dtrace_debug__putc(cpu, 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(cpu, 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(cpu, *q--);
+
+ if (num == 0)
+ break;
+
+ for (tmp = 0; *p;) {
+ n = *p++;
+ if (num & (1 << (n - 1))) {
+ dtrace_debug__putc(cpu, tmp ? ',' : '<');
+ for (; (n = *p) > ' '; ++p)
+ dtrace_debug__putc(cpu, n);
+ tmp = 1;
+ } else
+ for (; *p > ' '; ++p)
+ continue;
+ }
+ if (tmp)
+ dtrace_debug__putc(cpu, '>');
+ break;
+ case 'c':
+ dtrace_debug__putc(cpu, 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(cpu, hex2ascii(*up >> 4));
+ dtrace_debug__putc(cpu, hex2ascii(*up & 0x0f));
+ up++;
+ if (width)
+ for (q=p;*q;q++)
+ dtrace_debug__putc(cpu, *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(cpu, padc);
+ while (n--)
+ dtrace_debug__putc(cpu, *p++);
+ if (ladjust && width > 0)
+ while (width--)
+ dtrace_debug__putc(cpu, 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(cpu, padc);
+ if (neg)
+ dtrace_debug__putc(cpu, '-');
+ if (sharpflag && num != 0) {
+ if (base == 8) {
+ dtrace_debug__putc(cpu, '0');
+ } else if (base == 16) {
+ dtrace_debug__putc(cpu, '0');
+ dtrace_debug__putc(cpu, 'x');
+ }
+ }
+ if (!ladjust && width && (width -= tmp) > 0)
+ while (width--)
+ dtrace_debug__putc(cpu, padc);
+
+ while (*p)
+ dtrace_debug__putc(cpu, *p--);
+
+ if (ladjust && width && (width -= tmp) > 0)
+ while (width--)
+ dtrace_debug__putc(cpu, padc);
+
+ break;
+ default:
+ while (percent < fmt)
+ dtrace_debug__putc(cpu, *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(cpu, '\0');
+}
+
+void
+dtrace_debug_printf(const char *fmt, ...)
+{
+ va_list ap;
+ int cpu;
+
+ cpu = curcpu;
+ dtrace_debug_lock(cpu);
+
+ va_start(ap, fmt);
+
+ dtrace_debug_vprintf(cpu, fmt, ap);
+
+ va_end(ap);
+
+ dtrace_debug_unlock(cpu);
+}
+
+#else
+
+#define dtrace_debug_output()
+#define dtrace_debug_puts(_s)
+#define dtrace_debug_printf(fmt, ...)
+
+#endif