Path: blob/main/sys/cddl/dev/dtrace/dtrace_debug.c
48254 views
/*-1* Copyright (C) 2008 John Birrell <[email protected]>.2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice(s), this list of conditions and the following disclaimer as9* the first lines of this file unmodified other than the possible10* addition of one or more copyright notices.11* 2. Redistributions in binary form must reproduce the above copyright12* notice(s), this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY16* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED17* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE18* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY19* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES20* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR21* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER22* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH25* DAMAGE.26*27*/2829#ifdef DEBUG3031#include <machine/atomic.h>3233#define DTRACE_DEBUG_BUFR_SIZE (32 * 1024)3435struct dtrace_debug_data {36uintptr_t lock __aligned(CACHE_LINE_SIZE);37char bufr[DTRACE_DEBUG_BUFR_SIZE];38char *first;39char *last;40char *next;41} dtrace_debug_data[MAXCPU];4243static char dtrace_debug_bufr[DTRACE_DEBUG_BUFR_SIZE];4445static void46dtrace_debug_lock(int cpu)47{48uintptr_t tid;4950tid = (uintptr_t)curthread;51spinlock_enter();52while (atomic_cmpset_acq_ptr(&dtrace_debug_data[cpu].lock, 0, tid) == 0) /* Loop until the lock is obtained. */53;54}5556static void57dtrace_debug_unlock(int cpu)58{59atomic_store_rel_ptr(&dtrace_debug_data[cpu].lock, 0);60spinlock_exit();61}6263static void64dtrace_debug_init(void *dummy)65{66int i;67struct dtrace_debug_data *d;6869CPU_FOREACH(i) {70d = &dtrace_debug_data[i];7172if (d->first == NULL) {73d->first = d->bufr;74d->next = d->bufr;75d->last = d->bufr + DTRACE_DEBUG_BUFR_SIZE - 1;76*(d->last) = '\0';77}78}79}8081SYSINIT(dtrace_debug_init, SI_SUB_KDTRACE, SI_ORDER_ANY, dtrace_debug_init, NULL);82SYSINIT(dtrace_debug_smpinit, SI_SUB_SMP, SI_ORDER_ANY, dtrace_debug_init, NULL);8384static void85dtrace_debug_output(void)86{87char *p;88int i;89struct dtrace_debug_data *d;90uintptr_t count;9192CPU_FOREACH(i) {93dtrace_debug_lock(i);9495d = &dtrace_debug_data[i];9697count = 0;9899if (d->first < d->next) {100char *p1 = dtrace_debug_bufr;101102count = (uintptr_t) d->next - (uintptr_t) d->first;103104for (p = d->first; p < d->next; p++)105*p1++ = *p;106} else if (d->first > d->next) {107char *p1 = dtrace_debug_bufr;108109count = (uintptr_t) d->last - (uintptr_t) d->first;110111for (p = d->first; p < d->last; p++)112*p1++ = *p;113114count += (uintptr_t) d->next - (uintptr_t) d->bufr;115116for (p = d->bufr; p < d->next; p++)117*p1++ = *p;118}119120d->first = d->bufr;121d->next = d->bufr;122123dtrace_debug_unlock(i);124125if (count > 0) {126char *last = dtrace_debug_bufr + count;127128p = dtrace_debug_bufr;129130while (p < last) {131if (*p == '\0') {132p++;133continue;134}135136printf("%s", p);137138p += strlen(p);139}140}141}142}143144/*145* Functions below here are called from the probe context, so they can't call146* _any_ functions outside the dtrace module without running foul of the function147* boundary trace provider (fbt). The purpose of these functions is limited to148* buffering debug strings for output when the probe completes on the current CPU.149*/150151static __inline void152dtrace_debug__putc(int cpu, char c)153{154struct dtrace_debug_data *d;155156d = &dtrace_debug_data[cpu];157*d->next++ = c;158159if (d->next == d->last)160d->next = d->bufr;161162*(d->next) = '\0';163164if (d->next == d->first)165d->first++;166167if (d->first == d->last)168d->first = d->bufr;169}170171static void __used172dtrace_debug_putc(char c)173{174int cpu;175176cpu = curcpu;177dtrace_debug_lock(cpu);178179dtrace_debug__putc(cpu, c);180181dtrace_debug_unlock(cpu);182}183184static void __used185dtrace_debug_puts(const char *s)186{187int cpu;188189cpu = curcpu;190dtrace_debug_lock(cpu);191192while (*s != '\0')193dtrace_debug__putc(cpu, *s++);194195dtrace_debug__putc(cpu, '\0');196197dtrace_debug_unlock(cpu);198}199200/*201* Snaffled from sys/kern/subr_prf.c202*203* Put a NUL-terminated ASCII number (base <= 36) in a buffer in reverse204* order; return an optional length and a pointer to the last character205* written in the buffer (i.e., the first character of the string).206* The buffer pointed to by `nbuf' must have length >= MAXNBUF.207*/208static char *209dtrace_debug_ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper)210{211char *p, c;212213p = nbuf;214*p = '\0';215do {216c = hex2ascii(num % base);217*++p = upper ? toupper(c) : c;218} while (num /= base);219if (lenp)220*lenp = p - nbuf;221return (p);222}223224#define MAXNBUF (sizeof(intmax_t) * NBBY + 1)225226static void227dtrace_debug_vprintf(int cpu, const char *fmt, va_list ap)228{229char nbuf[MAXNBUF];230const char *p, *percent, *q;231u_char *up;232int ch, n;233uintmax_t num;234int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot;235int cflag, hflag, jflag, tflag, zflag;236int dwidth, upper;237int radix = 10;238char padc;239int stop = 0, retval = 0;240241num = 0;242243if (fmt == NULL)244fmt = "(fmt null)\n";245246for (;;) {247padc = ' ';248width = 0;249while ((ch = (u_char)*fmt++) != '%' || stop) {250if (ch == '\0') {251dtrace_debug__putc(cpu, '\0');252return;253}254dtrace_debug__putc(cpu, ch);255}256percent = fmt - 1;257qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0;258sign = 0; dot = 0; dwidth = 0; upper = 0;259cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0;260reswitch: switch (ch = (u_char)*fmt++) {261case '.':262dot = 1;263goto reswitch;264case '#':265sharpflag = 1;266goto reswitch;267case '+':268sign = 1;269goto reswitch;270case '-':271ladjust = 1;272goto reswitch;273case '%':274dtrace_debug__putc(cpu, ch);275break;276case '*':277if (!dot) {278width = va_arg(ap, int);279if (width < 0) {280ladjust = !ladjust;281width = -width;282}283} else {284dwidth = va_arg(ap, int);285}286goto reswitch;287case '0':288if (!dot) {289padc = '0';290goto reswitch;291}292case '1': case '2': case '3': case '4':293case '5': case '6': case '7': case '8': case '9':294for (n = 0;; ++fmt) {295n = n * 10 + ch - '0';296ch = *fmt;297if (ch < '0' || ch > '9')298break;299}300if (dot)301dwidth = n;302else303width = n;304goto reswitch;305case 'b':306num = (u_int)va_arg(ap, int);307p = va_arg(ap, char *);308for (q = dtrace_debug_ksprintn(nbuf, num, *p++, NULL, 0); *q;)309dtrace_debug__putc(cpu, *q--);310311if (num == 0)312break;313314for (tmp = 0; *p;) {315n = *p++;316if (num & (1 << (n - 1))) {317dtrace_debug__putc(cpu, tmp ? ',' : '<');318for (; (n = *p) > ' '; ++p)319dtrace_debug__putc(cpu, n);320tmp = 1;321} else322for (; *p > ' '; ++p)323continue;324}325if (tmp)326dtrace_debug__putc(cpu, '>');327break;328case 'c':329dtrace_debug__putc(cpu, va_arg(ap, int));330break;331case 'D':332up = va_arg(ap, u_char *);333p = va_arg(ap, char *);334if (!width)335width = 16;336while(width--) {337dtrace_debug__putc(cpu, hex2ascii(*up >> 4));338dtrace_debug__putc(cpu, hex2ascii(*up & 0x0f));339up++;340if (width)341for (q=p;*q;q++)342dtrace_debug__putc(cpu, *q);343}344break;345case 'd':346case 'i':347base = 10;348sign = 1;349goto handle_sign;350case 'h':351if (hflag) {352hflag = 0;353cflag = 1;354} else355hflag = 1;356goto reswitch;357case 'j':358jflag = 1;359goto reswitch;360case 'l':361if (lflag) {362lflag = 0;363qflag = 1;364} else365lflag = 1;366goto reswitch;367case 'n':368if (jflag)369*(va_arg(ap, intmax_t *)) = retval;370else if (qflag)371*(va_arg(ap, quad_t *)) = retval;372else if (lflag)373*(va_arg(ap, long *)) = retval;374else if (zflag)375*(va_arg(ap, size_t *)) = retval;376else if (hflag)377*(va_arg(ap, short *)) = retval;378else if (cflag)379*(va_arg(ap, char *)) = retval;380else381*(va_arg(ap, int *)) = retval;382break;383case 'o':384base = 8;385goto handle_nosign;386case 'p':387base = 16;388sharpflag = (width == 0);389sign = 0;390num = (uintptr_t)va_arg(ap, void *);391goto number;392case 'q':393qflag = 1;394goto reswitch;395case 'r':396base = radix;397if (sign)398goto handle_sign;399goto handle_nosign;400case 's':401p = va_arg(ap, char *);402if (p == NULL)403p = "(null)";404if (!dot)405n = strlen (p);406else407for (n = 0; n < dwidth && p[n]; n++)408continue;409410width -= n;411412if (!ladjust && width > 0)413while (width--)414dtrace_debug__putc(cpu, padc);415while (n--)416dtrace_debug__putc(cpu, *p++);417if (ladjust && width > 0)418while (width--)419dtrace_debug__putc(cpu, padc);420break;421case 't':422tflag = 1;423goto reswitch;424case 'u':425base = 10;426goto handle_nosign;427case 'X':428upper = 1;429case 'x':430base = 16;431goto handle_nosign;432case 'y':433base = 16;434sign = 1;435goto handle_sign;436case 'z':437zflag = 1;438goto reswitch;439handle_nosign:440sign = 0;441if (jflag)442num = va_arg(ap, uintmax_t);443else if (qflag)444num = va_arg(ap, u_quad_t);445else if (tflag)446num = va_arg(ap, ptrdiff_t);447else if (lflag)448num = va_arg(ap, u_long);449else if (zflag)450num = va_arg(ap, size_t);451else if (hflag)452num = (u_short)va_arg(ap, int);453else if (cflag)454num = (u_char)va_arg(ap, int);455else456num = va_arg(ap, u_int);457goto number;458handle_sign:459if (jflag)460num = va_arg(ap, intmax_t);461else if (qflag)462num = va_arg(ap, quad_t);463else if (tflag)464num = va_arg(ap, ptrdiff_t);465else if (lflag)466num = va_arg(ap, long);467else if (zflag)468num = va_arg(ap, size_t);469else if (hflag)470num = (short)va_arg(ap, int);471else if (cflag)472num = (char)va_arg(ap, int);473else474num = va_arg(ap, int);475number:476if (sign && (intmax_t)num < 0) {477neg = 1;478num = -(intmax_t)num;479}480p = dtrace_debug_ksprintn(nbuf, num, base, &tmp, upper);481if (sharpflag && num != 0) {482if (base == 8)483tmp++;484else if (base == 16)485tmp += 2;486}487if (neg)488tmp++;489490if (!ladjust && padc != '0' && width491&& (width -= tmp) > 0)492while (width--)493dtrace_debug__putc(cpu, padc);494if (neg)495dtrace_debug__putc(cpu, '-');496if (sharpflag && num != 0) {497if (base == 8) {498dtrace_debug__putc(cpu, '0');499} else if (base == 16) {500dtrace_debug__putc(cpu, '0');501dtrace_debug__putc(cpu, 'x');502}503}504if (!ladjust && width && (width -= tmp) > 0)505while (width--)506dtrace_debug__putc(cpu, padc);507508while (*p)509dtrace_debug__putc(cpu, *p--);510511if (ladjust && width && (width -= tmp) > 0)512while (width--)513dtrace_debug__putc(cpu, padc);514515break;516default:517while (percent < fmt)518dtrace_debug__putc(cpu, *percent++);519/*520* Since we ignore an formatting argument it is no521* longer safe to obey the remaining formatting522* arguments as the arguments will no longer match523* the format specs.524*/525stop = 1;526break;527}528}529530dtrace_debug__putc(cpu, '\0');531}532533void534dtrace_debug_printf(const char *fmt, ...)535{536va_list ap;537int cpu;538539cpu = curcpu;540dtrace_debug_lock(cpu);541542va_start(ap, fmt);543544dtrace_debug_vprintf(cpu, fmt, ap);545546va_end(ap);547548dtrace_debug_unlock(cpu);549}550551#else552553#define dtrace_debug_output()554#define dtrace_debug_puts(_s)555#define dtrace_debug_printf(fmt, ...)556557#endif558559560