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