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