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