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