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 
34 static char const hex2ascii_data[] = "0123456789abcdefghijklmnopqrstuvwxyz";
35 #define	hex2ascii(hex)	(hex2ascii_data[hex])
36 
37 #ifdef DEBUG
38 
39 #if defined(__amd64__)
40 static __inline int
41 dtrace_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__)
61 static __inline int
62 dtrace_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 
85 struct 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 
92 static char dtrace_debug_bufr[DTRACE_DEBUG_BUFR_SIZE];
93 
94 static volatile u_long	dtrace_debug_flag[MAXCPUS];
95 
96 static void
97 dtrace_debug_lock(int cpu)
98 {
99 	while (dtrace_cmpset_long(&dtrace_debug_flag[cpu], 0, 1) == 0)
100 		/* Loop until the lock is obtained. */
101 		;
102 }
103 
104 static void
105 dtrace_debug_unlock(int cpu)
106 {
107 	dtrace_debug_flag[cpu] = 0;
108 }
109 
110 static void
111 dtrace_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 
132 static void
133 dtrace_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 
203 static __inline void
204 dtrace_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 
222 static void __used
223 dtrace_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 
232 static void __used
233 dtrace_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 `nbuf' must have length >= MAXNBUF.
252  */
253 static char *
254 dtrace_debug_ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper)
255 {
256 	char *p, c;
257 
258 	p = nbuf;
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 - nbuf;
266 	return (p);
267 }
268 
269 #define MAXNBUF (sizeof(intmax_t) * NBBY + 1)
270 
271 static void
272 dtrace_debug_vprintf(const char *fmt, va_list ap)
273 {
274 	char nbuf[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;
305 reswitch:	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(nbuf, 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;
484 handle_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;
503 handle_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);
520 number:
521 			if (sign && (intmax_t)num < 0) {
522 				neg = 1;
523 				num = -(intmax_t)num;
524 			}
525 			p = dtrace_debug_ksprintn(nbuf, 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 
578 void
579 dtrace_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