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