dtrace_debug.c revision 248071
1/*-
2 * Copyright (C) 2008 John Birrell <jb@freebsd.org>.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice(s), this list of conditions and the following disclaimer as
10 *    the first lines of this file unmodified other than the possible
11 *    addition of one or more copyright notices.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice(s), this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
26 * DAMAGE.
27 *
28 * $FreeBSD: stable/9/sys/cddl/dev/dtrace/dtrace_debug.c 248071 2013-03-08 21:03:35Z rstone $
29 *
30 */
31
32#ifdef DEBUG
33
34#include <machine/atomic.h>
35
36#define	dtrace_cmpset_long	atomic_cmpset_long
37
38#define DTRACE_DEBUG_BUFR_SIZE	(32 * 1024)
39
40struct dtrace_debug_data {
41	char bufr[DTRACE_DEBUG_BUFR_SIZE];
42	char *first;
43	char *last;
44	char *next;
45} dtrace_debug_data[MAXCPU];
46
47static char dtrace_debug_bufr[DTRACE_DEBUG_BUFR_SIZE];
48
49static volatile u_long	dtrace_debug_flag[MAXCPU];
50
51static void
52dtrace_debug_lock(int cpu)
53{
54	while (dtrace_cmpset_long(&dtrace_debug_flag[cpu], 0, 1) == 0)
55		/* Loop until the lock is obtained. */
56		;
57}
58
59static void
60dtrace_debug_unlock(int cpu)
61{
62	dtrace_debug_flag[cpu] = 0;
63}
64
65static void
66dtrace_debug_init(void *dummy)
67{
68	int i;
69	struct dtrace_debug_data *d;
70
71	CPU_FOREACH(i) {
72		d = &dtrace_debug_data[i];
73
74		if (d->first == NULL) {
75			d->first = d->bufr;
76			d->next = d->bufr;
77			d->last = d->bufr + DTRACE_DEBUG_BUFR_SIZE - 1;
78			*(d->last) = '\0';
79		}
80	}
81}
82
83SYSINIT(dtrace_debug_init, SI_SUB_KDTRACE, SI_ORDER_ANY, dtrace_debug_init, NULL);
84SYSINIT(dtrace_debug_smpinit, SI_SUB_SMP, SI_ORDER_ANY, dtrace_debug_init, NULL);
85
86static void
87dtrace_debug_output(void)
88{
89	char *p;
90	int i;
91	struct dtrace_debug_data *d;
92	uintptr_t count;
93
94	CPU_FOREACH(i) {
95		dtrace_debug_lock(i);
96
97		d = &dtrace_debug_data[i];
98
99		count = 0;
100
101		if (d->first < d->next) {
102			char *p1 = dtrace_debug_bufr;
103
104			count = (uintptr_t) d->next - (uintptr_t) d->first;
105
106			for (p = d->first; p < d->next; p++)
107				*p1++ = *p;
108		} else if (d->next > d->first) {
109			char *p1 = dtrace_debug_bufr;
110
111			count = (uintptr_t) d->last - (uintptr_t) d->first;
112
113			for (p = d->first; p < d->last; p++)
114				*p1++ = *p;
115
116			count += (uintptr_t) d->next - (uintptr_t) d->bufr;
117
118			for (p = d->bufr; p < d->next; p++)
119				*p1++ = *p;
120		}
121
122		d->first = d->bufr;
123		d->next = d->bufr;
124
125		dtrace_debug_unlock(i);
126
127		if (count > 0) {
128			char *last = dtrace_debug_bufr + count;
129
130			p = dtrace_debug_bufr;
131
132			while (p < last) {
133				if (*p == '\0') {
134					p++;
135					continue;
136				}
137
138				printf("%s", p);
139
140				p += strlen(p);
141			}
142		}
143	}
144}
145
146/*
147 * Functions below here are called from the probe context, so they can't call
148 * _any_ functions outside the dtrace module without running foul of the function
149 * boundary trace provider (fbt). The purpose of these functions is limited to
150 * buffering debug strings for output when the probe completes on the current CPU.
151 */
152
153static __inline void
154dtrace_debug__putc(char c)
155{
156	struct dtrace_debug_data *d = &dtrace_debug_data[curcpu];
157
158	*d->next++ = c;
159
160	if (d->next == d->last)
161		d->next = d->bufr;
162
163	*(d->next) = '\0';
164
165	if (d->next == d->first)
166		d->first++;
167
168	if (d->first == d->last)
169		d->first = d->bufr;
170}
171
172static void __used
173dtrace_debug_putc(char c)
174{
175	dtrace_debug_lock(curcpu);
176
177	dtrace_debug__putc(c);
178
179	dtrace_debug_unlock(curcpu);
180}
181
182static void __used
183dtrace_debug_puts(const char *s)
184{
185	dtrace_debug_lock(curcpu);
186
187	while (*s != '\0')
188		dtrace_debug__putc(*s++);
189
190	dtrace_debug__putc('\0');
191
192	dtrace_debug_unlock(curcpu);
193}
194
195/*
196 * Snaffled from sys/kern/subr_prf.c
197 *
198 * Put a NUL-terminated ASCII number (base <= 36) in a buffer in reverse
199 * order; return an optional length and a pointer to the last character
200 * written in the buffer (i.e., the first character of the string).
201 * The buffer pointed to by `nbuf' must have length >= MAXNBUF.
202 */
203static char *
204dtrace_debug_ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper)
205{
206	char *p, c;
207
208	p = nbuf;
209	*p = '\0';
210	do {
211		c = hex2ascii(num % base);
212		*++p = upper ? toupper(c) : c;
213	} while (num /= base);
214	if (lenp)
215		*lenp = p - nbuf;
216	return (p);
217}
218
219#define MAXNBUF (sizeof(intmax_t) * NBBY + 1)
220
221static void
222dtrace_debug_vprintf(const char *fmt, va_list ap)
223{
224	char nbuf[MAXNBUF];
225	const char *p, *percent, *q;
226	u_char *up;
227	int ch, n;
228	uintmax_t num;
229	int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot;
230	int cflag, hflag, jflag, tflag, zflag;
231	int dwidth, upper;
232	int radix = 10;
233	char padc;
234	int stop = 0, retval = 0;
235
236	num = 0;
237
238	if (fmt == NULL)
239		fmt = "(fmt null)\n";
240
241	for (;;) {
242		padc = ' ';
243		width = 0;
244		while ((ch = (u_char)*fmt++) != '%' || stop) {
245			if (ch == '\0') {
246				dtrace_debug__putc('\0');
247				return;
248			}
249			dtrace_debug__putc(ch);
250		}
251		percent = fmt - 1;
252		qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0;
253		sign = 0; dot = 0; dwidth = 0; upper = 0;
254		cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0;
255reswitch:	switch (ch = (u_char)*fmt++) {
256		case '.':
257			dot = 1;
258			goto reswitch;
259		case '#':
260			sharpflag = 1;
261			goto reswitch;
262		case '+':
263			sign = 1;
264			goto reswitch;
265		case '-':
266			ladjust = 1;
267			goto reswitch;
268		case '%':
269			dtrace_debug__putc(ch);
270			break;
271		case '*':
272			if (!dot) {
273				width = va_arg(ap, int);
274				if (width < 0) {
275					ladjust = !ladjust;
276					width = -width;
277				}
278			} else {
279				dwidth = va_arg(ap, int);
280			}
281			goto reswitch;
282		case '0':
283			if (!dot) {
284				padc = '0';
285				goto reswitch;
286			}
287		case '1': case '2': case '3': case '4':
288		case '5': case '6': case '7': case '8': case '9':
289				for (n = 0;; ++fmt) {
290					n = n * 10 + ch - '0';
291					ch = *fmt;
292					if (ch < '0' || ch > '9')
293						break;
294				}
295			if (dot)
296				dwidth = n;
297			else
298				width = n;
299			goto reswitch;
300		case 'b':
301			num = (u_int)va_arg(ap, int);
302			p = va_arg(ap, char *);
303			for (q = dtrace_debug_ksprintn(nbuf, num, *p++, NULL, 0); *q;)
304				dtrace_debug__putc(*q--);
305
306			if (num == 0)
307				break;
308
309			for (tmp = 0; *p;) {
310				n = *p++;
311				if (num & (1 << (n - 1))) {
312					dtrace_debug__putc(tmp ? ',' : '<');
313					for (; (n = *p) > ' '; ++p)
314						dtrace_debug__putc(n);
315					tmp = 1;
316				} else
317					for (; *p > ' '; ++p)
318						continue;
319			}
320			if (tmp)
321				dtrace_debug__putc('>');
322			break;
323		case 'c':
324			dtrace_debug__putc(va_arg(ap, int));
325			break;
326		case 'D':
327			up = va_arg(ap, u_char *);
328			p = va_arg(ap, char *);
329			if (!width)
330				width = 16;
331			while(width--) {
332				dtrace_debug__putc(hex2ascii(*up >> 4));
333				dtrace_debug__putc(hex2ascii(*up & 0x0f));
334				up++;
335				if (width)
336					for (q=p;*q;q++)
337						dtrace_debug__putc(*q);
338			}
339			break;
340		case 'd':
341		case 'i':
342			base = 10;
343			sign = 1;
344			goto handle_sign;
345		case 'h':
346			if (hflag) {
347				hflag = 0;
348				cflag = 1;
349			} else
350				hflag = 1;
351			goto reswitch;
352		case 'j':
353			jflag = 1;
354			goto reswitch;
355		case 'l':
356			if (lflag) {
357				lflag = 0;
358				qflag = 1;
359			} else
360				lflag = 1;
361			goto reswitch;
362		case 'n':
363			if (jflag)
364				*(va_arg(ap, intmax_t *)) = retval;
365			else if (qflag)
366				*(va_arg(ap, quad_t *)) = retval;
367			else if (lflag)
368				*(va_arg(ap, long *)) = retval;
369			else if (zflag)
370				*(va_arg(ap, size_t *)) = retval;
371			else if (hflag)
372				*(va_arg(ap, short *)) = retval;
373			else if (cflag)
374				*(va_arg(ap, char *)) = retval;
375			else
376				*(va_arg(ap, int *)) = retval;
377			break;
378		case 'o':
379			base = 8;
380			goto handle_nosign;
381		case 'p':
382			base = 16;
383			sharpflag = (width == 0);
384			sign = 0;
385			num = (uintptr_t)va_arg(ap, void *);
386			goto number;
387		case 'q':
388			qflag = 1;
389			goto reswitch;
390		case 'r':
391			base = radix;
392			if (sign)
393				goto handle_sign;
394			goto handle_nosign;
395		case 's':
396			p = va_arg(ap, char *);
397			if (p == NULL)
398				p = "(null)";
399			if (!dot)
400				n = strlen (p);
401			else
402				for (n = 0; n < dwidth && p[n]; n++)
403					continue;
404
405			width -= n;
406
407			if (!ladjust && width > 0)
408				while (width--)
409					dtrace_debug__putc(padc);
410			while (n--)
411				dtrace_debug__putc(*p++);
412			if (ladjust && width > 0)
413				while (width--)
414					dtrace_debug__putc(padc);
415			break;
416		case 't':
417			tflag = 1;
418			goto reswitch;
419		case 'u':
420			base = 10;
421			goto handle_nosign;
422		case 'X':
423			upper = 1;
424		case 'x':
425			base = 16;
426			goto handle_nosign;
427		case 'y':
428			base = 16;
429			sign = 1;
430			goto handle_sign;
431		case 'z':
432			zflag = 1;
433			goto reswitch;
434handle_nosign:
435			sign = 0;
436			if (jflag)
437				num = va_arg(ap, uintmax_t);
438			else if (qflag)
439				num = va_arg(ap, u_quad_t);
440			else if (tflag)
441				num = va_arg(ap, ptrdiff_t);
442			else if (lflag)
443				num = va_arg(ap, u_long);
444			else if (zflag)
445				num = va_arg(ap, size_t);
446			else if (hflag)
447				num = (u_short)va_arg(ap, int);
448			else if (cflag)
449				num = (u_char)va_arg(ap, int);
450			else
451				num = va_arg(ap, u_int);
452			goto number;
453handle_sign:
454			if (jflag)
455				num = va_arg(ap, intmax_t);
456			else if (qflag)
457				num = va_arg(ap, quad_t);
458			else if (tflag)
459				num = va_arg(ap, ptrdiff_t);
460			else if (lflag)
461				num = va_arg(ap, long);
462			else if (zflag)
463				num = va_arg(ap, size_t);
464			else if (hflag)
465				num = (short)va_arg(ap, int);
466			else if (cflag)
467				num = (char)va_arg(ap, int);
468			else
469				num = va_arg(ap, int);
470number:
471			if (sign && (intmax_t)num < 0) {
472				neg = 1;
473				num = -(intmax_t)num;
474			}
475			p = dtrace_debug_ksprintn(nbuf, num, base, &tmp, upper);
476			if (sharpflag && num != 0) {
477				if (base == 8)
478					tmp++;
479				else if (base == 16)
480					tmp += 2;
481			}
482			if (neg)
483				tmp++;
484
485			if (!ladjust && padc != '0' && width
486			    && (width -= tmp) > 0)
487				while (width--)
488					dtrace_debug__putc(padc);
489			if (neg)
490				dtrace_debug__putc('-');
491			if (sharpflag && num != 0) {
492				if (base == 8) {
493					dtrace_debug__putc('0');
494				} else if (base == 16) {
495					dtrace_debug__putc('0');
496					dtrace_debug__putc('x');
497				}
498			}
499			if (!ladjust && width && (width -= tmp) > 0)
500				while (width--)
501					dtrace_debug__putc(padc);
502
503			while (*p)
504				dtrace_debug__putc(*p--);
505
506			if (ladjust && width && (width -= tmp) > 0)
507				while (width--)
508					dtrace_debug__putc(padc);
509
510			break;
511		default:
512			while (percent < fmt)
513				dtrace_debug__putc(*percent++);
514			/*
515			 * Since we ignore an formatting argument it is no
516			 * longer safe to obey the remaining formatting
517			 * arguments as the arguments will no longer match
518			 * the format specs.
519			 */
520			stop = 1;
521			break;
522		}
523	}
524
525	dtrace_debug__putc('\0');
526}
527
528void
529dtrace_debug_printf(const char *fmt, ...)
530{
531	va_list ap;
532
533	dtrace_debug_lock(curcpu);
534
535	va_start(ap, fmt);
536
537	dtrace_debug_vprintf(fmt, ap);
538
539	va_end(ap);
540
541	dtrace_debug_unlock(curcpu);
542}
543
544#else
545
546#define dtrace_debug_output()
547#define dtrace_debug_puts(_s)
548#define dtrace_debug_printf(fmt, ...)
549
550#endif
551