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