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