xref: /illumos-gate/usr/src/lib/libdtrace/common/dt_printf.c (revision 7f7322febbcfe774b7270abc3b191c094bfcc517)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*
24  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 #include <sys/sysmacros.h>
31 #include <strings.h>
32 #include <stdlib.h>
33 #include <alloca.h>
34 #include <assert.h>
35 #include <ctype.h>
36 #include <errno.h>
37 #include <limits.h>
38 
39 #include <dt_printf.h>
40 #include <dt_string.h>
41 #include <dt_impl.h>
42 
43 /*ARGSUSED*/
44 static int
45 pfcheck_addr(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp)
46 {
47 	return (dt_node_is_pointer(dnp) || dt_node_is_integer(dnp));
48 }
49 
50 /*ARGSUSED*/
51 static int
52 pfcheck_kaddr(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp)
53 {
54 	return (dt_node_is_pointer(dnp) || dt_node_is_integer(dnp) ||
55 	    dt_node_is_symaddr(dnp));
56 }
57 
58 /*ARGSUSED*/
59 static int
60 pfcheck_uaddr(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp)
61 {
62 	dtrace_hdl_t *dtp = pfv->pfv_dtp;
63 	dt_ident_t *idp = dt_idhash_lookup(dtp->dt_macros, "target");
64 
65 	if (dt_node_is_usymaddr(dnp))
66 		return (1);
67 
68 	if (idp == NULL || idp->di_id == 0)
69 		return (0);
70 
71 	return (dt_node_is_pointer(dnp) || dt_node_is_integer(dnp));
72 }
73 
74 /*ARGSUSED*/
75 static int
76 pfcheck_stack(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp)
77 {
78 	return (dt_node_is_stack(dnp));
79 }
80 
81 /*ARGSUSED*/
82 static int
83 pfcheck_time(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp)
84 {
85 	return (dt_node_is_integer(dnp) &&
86 	    dt_node_type_size(dnp) == sizeof (uint64_t));
87 }
88 
89 /*ARGSUSED*/
90 static int
91 pfcheck_str(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp)
92 {
93 	ctf_file_t *ctfp;
94 	ctf_encoding_t e;
95 	ctf_arinfo_t r;
96 	ctf_id_t base;
97 	uint_t kind;
98 
99 	if (dt_node_is_string(dnp))
100 		return (1);
101 
102 	ctfp = dnp->dn_ctfp;
103 	base = ctf_type_resolve(ctfp, dnp->dn_type);
104 	kind = ctf_type_kind(ctfp, base);
105 
106 	return (kind == CTF_K_ARRAY && ctf_array_info(ctfp, base, &r) == 0 &&
107 	    (base = ctf_type_resolve(ctfp, r.ctr_contents)) != CTF_ERR &&
108 	    ctf_type_encoding(ctfp, base, &e) == 0 && IS_CHAR(e));
109 }
110 
111 /*ARGSUSED*/
112 static int
113 pfcheck_wstr(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp)
114 {
115 	ctf_file_t *ctfp = dnp->dn_ctfp;
116 	ctf_id_t base = ctf_type_resolve(ctfp, dnp->dn_type);
117 	uint_t kind = ctf_type_kind(ctfp, base);
118 
119 	ctf_encoding_t e;
120 	ctf_arinfo_t r;
121 
122 	return (kind == CTF_K_ARRAY && ctf_array_info(ctfp, base, &r) == 0 &&
123 	    (base = ctf_type_resolve(ctfp, r.ctr_contents)) != CTF_ERR &&
124 	    ctf_type_kind(ctfp, base) == CTF_K_INTEGER &&
125 	    ctf_type_encoding(ctfp, base, &e) == 0 && e.cte_bits == 32);
126 }
127 
128 /*ARGSUSED*/
129 static int
130 pfcheck_csi(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp)
131 {
132 	return (dt_node_is_integer(dnp) &&
133 	    dt_node_type_size(dnp) <= sizeof (int));
134 }
135 
136 /*ARGSUSED*/
137 static int
138 pfcheck_fp(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp)
139 {
140 	return (dt_node_is_float(dnp));
141 }
142 
143 /*ARGSUSED*/
144 static int
145 pfcheck_xint(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp)
146 {
147 	return (dt_node_is_integer(dnp));
148 }
149 
150 /*ARGSUSED*/
151 static int
152 pfcheck_dint(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp)
153 {
154 	if (dnp->dn_flags & DT_NF_SIGNED)
155 		pfd->pfd_flags |= DT_PFCONV_SIGNED;
156 	else
157 		pfd->pfd_fmt[strlen(pfd->pfd_fmt) - 1] = 'u';
158 
159 	return (dt_node_is_integer(dnp));
160 }
161 
162 /*ARGSUSED*/
163 static int
164 pfcheck_xshort(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp)
165 {
166 	ctf_file_t *ctfp = dnp->dn_ctfp;
167 	ctf_id_t type = ctf_type_resolve(ctfp, dnp->dn_type);
168 	char n[DT_TYPE_NAMELEN];
169 
170 	return (ctf_type_name(ctfp, type, n, sizeof (n)) != NULL && (
171 	    strcmp(n, "short") == 0 || strcmp(n, "signed short") == 0 ||
172 	    strcmp(n, "unsigned short") == 0));
173 }
174 
175 /*ARGSUSED*/
176 static int
177 pfcheck_xlong(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp)
178 {
179 	ctf_file_t *ctfp = dnp->dn_ctfp;
180 	ctf_id_t type = ctf_type_resolve(ctfp, dnp->dn_type);
181 	char n[DT_TYPE_NAMELEN];
182 
183 	return (ctf_type_name(ctfp, type, n, sizeof (n)) != NULL && (
184 	    strcmp(n, "long") == 0 || strcmp(n, "signed long") == 0 ||
185 	    strcmp(n, "unsigned long") == 0));
186 }
187 
188 /*ARGSUSED*/
189 static int
190 pfcheck_xlonglong(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp)
191 {
192 	ctf_file_t *ctfp = dnp->dn_ctfp;
193 	ctf_id_t type = dnp->dn_type;
194 	char n[DT_TYPE_NAMELEN];
195 
196 	if (ctf_type_name(ctfp, ctf_type_resolve(ctfp, type), n,
197 	    sizeof (n)) != NULL && (strcmp(n, "long long") == 0 ||
198 	    strcmp(n, "signed long long") == 0 ||
199 	    strcmp(n, "unsigned long long") == 0))
200 		return (1);
201 
202 	/*
203 	 * If the type used for %llx or %llX is not an [unsigned] long long, we
204 	 * also permit it to be a [u]int64_t or any typedef thereof.  We know
205 	 * that these typedefs are guaranteed to work with %ll[xX] in either
206 	 * compilation environment even though they alias to "long" in LP64.
207 	 */
208 	while (ctf_type_kind(ctfp, type) == CTF_K_TYPEDEF) {
209 		if (ctf_type_name(ctfp, type, n, sizeof (n)) != NULL &&
210 		    (strcmp(n, "int64_t") == 0 || strcmp(n, "uint64_t") == 0))
211 			return (1);
212 
213 		type = ctf_type_reference(ctfp, type);
214 	}
215 
216 	return (0);
217 }
218 
219 /*ARGSUSED*/
220 static int
221 pfcheck_type(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp)
222 {
223 	return (ctf_type_compat(dnp->dn_ctfp, ctf_type_resolve(dnp->dn_ctfp,
224 	    dnp->dn_type), pfd->pfd_conv->pfc_dctfp, pfd->pfd_conv->pfc_dtype));
225 }
226 
227 /*ARGSUSED*/
228 static int
229 pfprint_sint(dtrace_hdl_t *dtp, FILE *fp, const char *format,
230     const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t unormal)
231 {
232 	int64_t normal = (int64_t)unormal;
233 	int32_t n = (int32_t)normal;
234 
235 	switch (size) {
236 	case sizeof (int8_t):
237 		return (dt_printf(dtp, fp, format,
238 		    (int32_t)*((int8_t *)addr) / n));
239 	case sizeof (int16_t):
240 		return (dt_printf(dtp, fp, format,
241 		    (int32_t)*((int16_t *)addr) / n));
242 	case sizeof (int32_t):
243 		return (dt_printf(dtp, fp, format,
244 		    *((int32_t *)addr) / n));
245 	case sizeof (int64_t):
246 		return (dt_printf(dtp, fp, format,
247 		    *((int64_t *)addr) / normal));
248 	default:
249 		return (dt_set_errno(dtp, EDT_DMISMATCH));
250 	}
251 }
252 
253 /*ARGSUSED*/
254 static int
255 pfprint_uint(dtrace_hdl_t *dtp, FILE *fp, const char *format,
256     const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
257 {
258 	uint32_t n = (uint32_t)normal;
259 
260 	switch (size) {
261 	case sizeof (uint8_t):
262 		return (dt_printf(dtp, fp, format,
263 		    (uint32_t)*((uint8_t *)addr) / n));
264 	case sizeof (uint16_t):
265 		return (dt_printf(dtp, fp, format,
266 		    (uint32_t)*((uint16_t *)addr) / n));
267 	case sizeof (uint32_t):
268 		return (dt_printf(dtp, fp, format,
269 		    *((uint32_t *)addr) / n));
270 	case sizeof (uint64_t):
271 		return (dt_printf(dtp, fp, format,
272 		    *((uint64_t *)addr) / normal));
273 	default:
274 		return (dt_set_errno(dtp, EDT_DMISMATCH));
275 	}
276 }
277 
278 static int
279 pfprint_dint(dtrace_hdl_t *dtp, FILE *fp, const char *format,
280     const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
281 {
282 	if (pfd->pfd_flags & DT_PFCONV_SIGNED)
283 		return (pfprint_sint(dtp, fp, format, pfd, addr, size, normal));
284 	else
285 		return (pfprint_uint(dtp, fp, format, pfd, addr, size, normal));
286 }
287 
288 /*ARGSUSED*/
289 static int
290 pfprint_fp(dtrace_hdl_t *dtp, FILE *fp, const char *format,
291     const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
292 {
293 	double n = (double)normal;
294 	long double ldn = (long double)normal;
295 
296 	switch (size) {
297 	case sizeof (float):
298 		return (dt_printf(dtp, fp, format,
299 		    (double)*((float *)addr) / n));
300 	case sizeof (double):
301 		return (dt_printf(dtp, fp, format,
302 		    *((double *)addr) / n));
303 	case sizeof (long double):
304 		return (dt_printf(dtp, fp, format,
305 		    *((long double *)addr) / ldn));
306 	default:
307 		return (dt_set_errno(dtp, EDT_DMISMATCH));
308 	}
309 }
310 
311 /*ARGSUSED*/
312 static int
313 pfprint_addr(dtrace_hdl_t *dtp, FILE *fp, const char *format,
314     const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
315 {
316 	char *s;
317 	int n, len = 256;
318 	uint64_t val;
319 
320 	switch (size) {
321 	case sizeof (uint32_t):
322 		val = *((uint32_t *)addr);
323 		break;
324 	case sizeof (uint64_t):
325 		val = *((uint64_t *)addr);
326 		break;
327 	default:
328 		return (dt_set_errno(dtp, EDT_DMISMATCH));
329 	}
330 
331 	do {
332 		n = len;
333 		s = alloca(n);
334 	} while ((len = dtrace_addr2str(dtp, val, s, n)) >= n);
335 
336 	return (dt_printf(dtp, fp, format, s));
337 }
338 
339 /*ARGSUSED*/
340 static int
341 pfprint_mod(dtrace_hdl_t *dtp, FILE *fp, const char *format,
342     const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
343 {
344 	return (dt_print_mod(dtp, fp, format, (caddr_t)addr));
345 }
346 
347 /*ARGSUSED*/
348 static int
349 pfprint_umod(dtrace_hdl_t *dtp, FILE *fp, const char *format,
350     const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
351 {
352 	return (dt_print_umod(dtp, fp, format, (caddr_t)addr));
353 }
354 
355 /*ARGSUSED*/
356 static int
357 pfprint_uaddr(dtrace_hdl_t *dtp, FILE *fp, const char *format,
358     const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
359 {
360 	char *s;
361 	int n, len = 256;
362 	uint64_t val, pid = 0;
363 
364 	dt_ident_t *idp = dt_idhash_lookup(dtp->dt_macros, "target");
365 
366 	switch (size) {
367 	case sizeof (uint32_t):
368 		val = (u_longlong_t)*((uint32_t *)addr);
369 		break;
370 	case sizeof (uint64_t):
371 		val = (u_longlong_t)*((uint64_t *)addr);
372 		break;
373 	case sizeof (uint64_t) * 2:
374 		pid = ((uint64_t *)(uintptr_t)addr)[0];
375 		val = ((uint64_t *)(uintptr_t)addr)[1];
376 		break;
377 	default:
378 		return (dt_set_errno(dtp, EDT_DMISMATCH));
379 	}
380 
381 	if (pid == 0 && dtp->dt_vector == NULL && idp != NULL)
382 		pid = idp->di_id;
383 
384 	do {
385 		n = len;
386 		s = alloca(n);
387 	} while ((len = dtrace_uaddr2str(dtp, pid, val, s, n)) >= n);
388 
389 	return (dt_printf(dtp, fp, format, s));
390 }
391 
392 /*ARGSUSED*/
393 static int
394 pfprint_stack(dtrace_hdl_t *dtp, FILE *fp, const char *format,
395     const dt_pfargd_t *pfd, const void *vaddr, size_t size, uint64_t normal)
396 {
397 	int width;
398 	dtrace_optval_t saved = dtp->dt_options[DTRACEOPT_STACKINDENT];
399 	const dtrace_recdesc_t *rec = pfd->pfd_rec;
400 	caddr_t addr = (caddr_t)vaddr;
401 	int err = 0;
402 
403 	/*
404 	 * We have stashed the value of the STACKINDENT option, and we will
405 	 * now override it for the purposes of formatting the stack.  If the
406 	 * field has been specified as left-aligned (i.e. (%-#), we set the
407 	 * indentation to be the width.  This is a slightly odd semantic, but
408 	 * it's useful functionality -- and it's slightly odd to begin with to
409 	 * be using a single format specifier to be formatting multiple lines
410 	 * of text...
411 	 */
412 	if (pfd->pfd_dynwidth < 0) {
413 		assert(pfd->pfd_flags & DT_PFCONV_DYNWIDTH);
414 		width = -pfd->pfd_dynwidth;
415 	} else if (pfd->pfd_flags & DT_PFCONV_LEFT) {
416 		width = pfd->pfd_dynwidth ? pfd->pfd_dynwidth : pfd->pfd_width;
417 	} else {
418 		width = 0;
419 	}
420 
421 	dtp->dt_options[DTRACEOPT_STACKINDENT] = width;
422 
423 	switch (rec->dtrd_action) {
424 	case DTRACEACT_USTACK:
425 	case DTRACEACT_JSTACK:
426 		err = dt_print_ustack(dtp, fp, format, addr, rec->dtrd_arg);
427 		break;
428 
429 	case DTRACEACT_STACK:
430 		err = dt_print_stack(dtp, fp, format, addr, rec->dtrd_arg,
431 		    rec->dtrd_size / rec->dtrd_arg);
432 		break;
433 
434 	default:
435 		assert(0);
436 	}
437 
438 	dtp->dt_options[DTRACEOPT_STACKINDENT] = saved;
439 
440 	return (err);
441 }
442 
443 /*ARGSUSED*/
444 static int
445 pfprint_time(dtrace_hdl_t *dtp, FILE *fp, const char *format,
446     const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
447 {
448 	char src[32], buf[32], *dst = buf;
449 	hrtime_t time = *((uint64_t *)addr);
450 	time_t sec = (time_t)(time / NANOSEC);
451 	int i;
452 
453 	/*
454 	 * ctime(3C) returns a string of the form "Dec  3 17:20:00 1973\n\0".
455 	 * Below, we turn this into the canonical adb/mdb /[yY] format,
456 	 * "1973 Dec  3 17:20:00".
457 	 */
458 	(void) ctime_r(&sec, src, sizeof (src));
459 
460 	/*
461 	 * Place the 4-digit year at the head of the string...
462 	 */
463 	for (i = 20; i < 24; i++)
464 		*dst++ = src[i];
465 
466 	/*
467 	 * ...and follow it with the remainder (month, day, hh:mm:ss).
468 	 */
469 	for (i = 3; i < 19; i++)
470 		*dst++ = src[i];
471 
472 	*dst = '\0';
473 	return (dt_printf(dtp, fp, format, buf));
474 }
475 
476 /*
477  * This prints the time in RFC 822 standard form.  This is useful for emitting
478  * notions of time that are consumed by standard tools (e.g., as part of an
479  * RSS feed).
480  */
481 /*ARGSUSED*/
482 static int
483 pfprint_time822(dtrace_hdl_t *dtp, FILE *fp, const char *format,
484     const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
485 {
486 	hrtime_t time = *((uint64_t *)addr);
487 	time_t sec = (time_t)(time / NANOSEC);
488 	struct tm tm;
489 	char buf[64];
490 
491 	(void) localtime_r(&sec, &tm);
492 	(void) strftime(buf, sizeof (buf), "%a, %d %b %G %T %Z", &tm);
493 	return (dt_printf(dtp, fp, format, buf));
494 }
495 
496 /*ARGSUSED*/
497 static int
498 pfprint_cstr(dtrace_hdl_t *dtp, FILE *fp, const char *format,
499     const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
500 {
501 	char *s = alloca(size + 1);
502 
503 	bcopy(addr, s, size);
504 	s[size] = '\0';
505 	return (dt_printf(dtp, fp, format, s));
506 }
507 
508 /*ARGSUSED*/
509 static int
510 pfprint_wstr(dtrace_hdl_t *dtp, FILE *fp, const char *format,
511     const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
512 {
513 	wchar_t *ws = alloca(size + sizeof (wchar_t));
514 
515 	bcopy(addr, ws, size);
516 	ws[size / sizeof (wchar_t)] = L'\0';
517 	return (dt_printf(dtp, fp, format, ws));
518 }
519 
520 /*ARGSUSED*/
521 static int
522 pfprint_estr(dtrace_hdl_t *dtp, FILE *fp, const char *format,
523     const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
524 {
525 	char *s;
526 	int n;
527 
528 	if ((s = strchr2esc(addr, size)) == NULL)
529 		return (dt_set_errno(dtp, EDT_NOMEM));
530 
531 	n = dt_printf(dtp, fp, format, s);
532 	free(s);
533 	return (n);
534 }
535 
536 static int
537 pfprint_echr(dtrace_hdl_t *dtp, FILE *fp, const char *format,
538     const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
539 {
540 	char c;
541 
542 	switch (size) {
543 	case sizeof (int8_t):
544 		c = *(int8_t *)addr;
545 		break;
546 	case sizeof (int16_t):
547 		c = *(int16_t *)addr;
548 		break;
549 	case sizeof (int32_t):
550 		c = *(int32_t *)addr;
551 		break;
552 	default:
553 		return (dt_set_errno(dtp, EDT_DMISMATCH));
554 	}
555 
556 	return (pfprint_estr(dtp, fp, format, pfd, &c, 1, normal));
557 }
558 
559 /*ARGSUSED*/
560 static int
561 pfprint_pct(dtrace_hdl_t *dtp, FILE *fp, const char *format,
562     const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
563 {
564 	return (dt_printf(dtp, fp, "%%"));
565 }
566 
567 static const char pfproto_xint[] = "char, short, int, long, or long long";
568 static const char pfproto_csi[] = "char, short, or int";
569 static const char pfproto_fp[] = "float, double, or long double";
570 static const char pfproto_addr[] = "pointer or integer";
571 static const char pfproto_uaddr[] =
572 	"pointer or integer (with -p/-c) or _usymaddr (without -p/-c)";
573 static const char pfproto_cstr[] = "char [] or string (or use stringof)";
574 static const char pfproto_wstr[] = "wchar_t []";
575 
576 /*
577  * Printf format conversion dictionary.  This table should match the set of
578  * conversions offered by printf(3C), as well as some additional extensions.
579  * The second parameter is an ASCII string which is either an actual type
580  * name we should look up (if pfcheck_type is specified), or just a descriptive
581  * string of the types expected for use in error messages.
582  */
583 static const dt_pfconv_t _dtrace_conversions[] = {
584 { "a", "s", pfproto_addr, pfcheck_kaddr, pfprint_addr },
585 { "A", "s", pfproto_uaddr, pfcheck_uaddr, pfprint_uaddr },
586 { "c", "c", pfproto_csi, pfcheck_csi, pfprint_sint },
587 { "C", "s", pfproto_csi, pfcheck_csi, pfprint_echr },
588 { "d", "d", pfproto_xint, pfcheck_dint, pfprint_dint },
589 { "e", "e", pfproto_fp, pfcheck_fp, pfprint_fp },
590 { "E", "E", pfproto_fp, pfcheck_fp, pfprint_fp },
591 { "f", "f", pfproto_fp, pfcheck_fp, pfprint_fp },
592 { "g", "g", pfproto_fp, pfcheck_fp, pfprint_fp },
593 { "G", "G", pfproto_fp, pfcheck_fp, pfprint_fp },
594 { "hd", "d", "short", pfcheck_type, pfprint_sint },
595 { "hi", "i", "short", pfcheck_type, pfprint_sint },
596 { "ho", "o", "unsigned short", pfcheck_type, pfprint_uint },
597 { "hu", "u", "unsigned short", pfcheck_type, pfprint_uint },
598 { "hx", "x", "short", pfcheck_xshort, pfprint_uint },
599 { "hX", "X", "short", pfcheck_xshort, pfprint_uint },
600 { "i", "i", pfproto_xint, pfcheck_dint, pfprint_dint },
601 { "k", "s", "stack", pfcheck_stack, pfprint_stack },
602 { "lc", "lc", "int", pfcheck_type, pfprint_sint }, /* a.k.a. wint_t */
603 { "ld",	"d", "long", pfcheck_type, pfprint_sint },
604 { "li",	"i", "long", pfcheck_type, pfprint_sint },
605 { "lo",	"o", "unsigned long", pfcheck_type, pfprint_uint },
606 { "lu", "u", "unsigned long", pfcheck_type, pfprint_uint },
607 { "ls",	"ls", pfproto_wstr, pfcheck_wstr, pfprint_wstr },
608 { "lx",	"x", "long", pfcheck_xlong, pfprint_uint },
609 { "lX",	"X", "long", pfcheck_xlong, pfprint_uint },
610 { "lld", "d", "long long", pfcheck_type, pfprint_sint },
611 { "lli", "i", "long long", pfcheck_type, pfprint_sint },
612 { "llo", "o", "unsigned long long", pfcheck_type, pfprint_uint },
613 { "llu", "u", "unsigned long long", pfcheck_type, pfprint_uint },
614 { "llx", "x", "long long", pfcheck_xlonglong, pfprint_uint },
615 { "llX", "X", "long long", pfcheck_xlonglong, pfprint_uint },
616 { "Le",	"e", "long double", pfcheck_type, pfprint_fp },
617 { "LE",	"E", "long double", pfcheck_type, pfprint_fp },
618 { "Lf",	"f", "long double", pfcheck_type, pfprint_fp },
619 { "Lg",	"g", "long double", pfcheck_type, pfprint_fp },
620 { "LG",	"G", "long double", pfcheck_type, pfprint_fp },
621 { "o", "o", pfproto_xint, pfcheck_xint, pfprint_uint },
622 { "p", "x", pfproto_addr, pfcheck_addr, pfprint_uint },
623 { "s", "s", "char [] or string (or use stringof)", pfcheck_str, pfprint_cstr },
624 { "S", "s", pfproto_cstr, pfcheck_str, pfprint_estr },
625 { "T", "s", "int64_t", pfcheck_time, pfprint_time822 },
626 { "u", "u", pfproto_xint, pfcheck_xint, pfprint_uint },
627 { "wc",	"wc", "int", pfcheck_type, pfprint_sint }, /* a.k.a. wchar_t */
628 { "ws", "ws", pfproto_wstr, pfcheck_wstr, pfprint_wstr },
629 { "x", "x", pfproto_xint, pfcheck_xint, pfprint_uint },
630 { "X", "X", pfproto_xint, pfcheck_xint, pfprint_uint },
631 { "Y", "s", "int64_t", pfcheck_time, pfprint_time },
632 { "%", "%", "void", pfcheck_type, pfprint_pct },
633 { NULL, NULL, NULL, NULL, NULL }
634 };
635 
636 int
637 dt_pfdict_create(dtrace_hdl_t *dtp)
638 {
639 	uint_t n = _dtrace_strbuckets;
640 	const dt_pfconv_t *pfd;
641 	dt_pfdict_t *pdi;
642 
643 	if ((pdi = malloc(sizeof (dt_pfdict_t))) == NULL ||
644 	    (pdi->pdi_buckets = malloc(sizeof (dt_pfconv_t *) * n)) == NULL) {
645 		free(pdi);
646 		return (dt_set_errno(dtp, EDT_NOMEM));
647 	}
648 
649 	dtp->dt_pfdict = pdi;
650 	bzero(pdi->pdi_buckets, sizeof (dt_pfconv_t *) * n);
651 	pdi->pdi_nbuckets = n;
652 
653 	for (pfd = _dtrace_conversions; pfd->pfc_name != NULL; pfd++) {
654 		dtrace_typeinfo_t dtt;
655 		dt_pfconv_t *pfc;
656 		uint_t h;
657 
658 		if ((pfc = malloc(sizeof (dt_pfconv_t))) == NULL) {
659 			dt_pfdict_destroy(dtp);
660 			return (dt_set_errno(dtp, EDT_NOMEM));
661 		}
662 
663 		bcopy(pfd, pfc, sizeof (dt_pfconv_t));
664 		h = dt_strtab_hash(pfc->pfc_name, NULL) % n;
665 		pfc->pfc_next = pdi->pdi_buckets[h];
666 		pdi->pdi_buckets[h] = pfc;
667 
668 		dtt.dtt_ctfp = NULL;
669 		dtt.dtt_type = CTF_ERR;
670 
671 		/*
672 		 * The "D" container or its parent must contain a definition of
673 		 * any type referenced by a printf conversion.  If none can be
674 		 * found, we fail to initialize the printf dictionary.
675 		 */
676 		if (pfc->pfc_check == &pfcheck_type && dtrace_lookup_by_type(
677 		    dtp, DTRACE_OBJ_DDEFS, pfc->pfc_tstr, &dtt) != 0) {
678 			dt_pfdict_destroy(dtp);
679 			return (dt_set_errno(dtp, EDT_NOCONV));
680 		}
681 
682 		pfc->pfc_dctfp = dtt.dtt_ctfp;
683 		pfc->pfc_dtype = dtt.dtt_type;
684 
685 		/*
686 		 * The "C" container may contain an alternate definition of an
687 		 * explicit conversion type.  If it does, use it; otherwise
688 		 * just set pfc_ctype to pfc_dtype so it is always valid.
689 		 */
690 		if (pfc->pfc_check == &pfcheck_type && dtrace_lookup_by_type(
691 		    dtp, DTRACE_OBJ_CDEFS, pfc->pfc_tstr, &dtt) == 0) {
692 			pfc->pfc_cctfp = dtt.dtt_ctfp;
693 			pfc->pfc_ctype = dtt.dtt_type;
694 		} else {
695 			pfc->pfc_cctfp = pfc->pfc_dctfp;
696 			pfc->pfc_ctype = pfc->pfc_dtype;
697 		}
698 
699 		if (pfc->pfc_check == NULL || pfc->pfc_print == NULL ||
700 		    pfc->pfc_ofmt == NULL || pfc->pfc_tstr == NULL) {
701 			dt_pfdict_destroy(dtp);
702 			return (dt_set_errno(dtp, EDT_BADCONV));
703 		}
704 
705 		dt_dprintf("loaded printf conversion %%%s\n", pfc->pfc_name);
706 	}
707 
708 	return (0);
709 }
710 
711 void
712 dt_pfdict_destroy(dtrace_hdl_t *dtp)
713 {
714 	dt_pfdict_t *pdi = dtp->dt_pfdict;
715 	dt_pfconv_t *pfc, *nfc;
716 	uint_t i;
717 
718 	if (pdi == NULL)
719 		return;
720 
721 	for (i = 0; i < pdi->pdi_nbuckets; i++) {
722 		for (pfc = pdi->pdi_buckets[i]; pfc != NULL; pfc = nfc) {
723 			nfc = pfc->pfc_next;
724 			free(pfc);
725 		}
726 	}
727 
728 	free(pdi->pdi_buckets);
729 	free(pdi);
730 	dtp->dt_pfdict = NULL;
731 }
732 
733 static const dt_pfconv_t *
734 dt_pfdict_lookup(dtrace_hdl_t *dtp, const char *name)
735 {
736 	dt_pfdict_t *pdi = dtp->dt_pfdict;
737 	uint_t h = dt_strtab_hash(name, NULL) % pdi->pdi_nbuckets;
738 	const dt_pfconv_t *pfc;
739 
740 	for (pfc = pdi->pdi_buckets[h]; pfc != NULL; pfc = pfc->pfc_next) {
741 		if (strcmp(pfc->pfc_name, name) == 0)
742 			break;
743 	}
744 
745 	return (pfc);
746 }
747 
748 static dt_pfargv_t *
749 dt_printf_error(dtrace_hdl_t *dtp, int err)
750 {
751 	if (yypcb != NULL)
752 		longjmp(yypcb->pcb_jmpbuf, err);
753 
754 	(void) dt_set_errno(dtp, err);
755 	return (NULL);
756 }
757 
758 dt_pfargv_t *
759 dt_printf_create(dtrace_hdl_t *dtp, const char *s)
760 {
761 	dt_pfargd_t *pfd, *nfd = NULL;
762 	dt_pfargv_t *pfv;
763 	const char *p, *q;
764 	char *format;
765 
766 	if ((pfv = malloc(sizeof (dt_pfargv_t))) == NULL ||
767 	    (format = strdup(s)) == NULL) {
768 		free(pfv);
769 		return (dt_printf_error(dtp, EDT_NOMEM));
770 	}
771 
772 	pfv->pfv_format = format;
773 	pfv->pfv_argv = NULL;
774 	pfv->pfv_argc = 0;
775 	pfv->pfv_flags = 0;
776 	pfv->pfv_dtp = dtp;
777 
778 	for (q = format; (p = strchr(q, '%')) != NULL; q = *p ? p + 1 : p) {
779 		uint_t namelen = 0;
780 		int digits = 0;
781 		int dot = 0;
782 
783 		char name[8];
784 		char c;
785 		int n;
786 
787 		if ((pfd = malloc(sizeof (dt_pfargd_t))) == NULL) {
788 			dt_printf_destroy(pfv);
789 			return (dt_printf_error(dtp, EDT_NOMEM));
790 		}
791 
792 		if (pfv->pfv_argv != NULL)
793 			nfd->pfd_next = pfd;
794 		else
795 			pfv->pfv_argv = pfd;
796 
797 		bzero(pfd, sizeof (dt_pfargd_t));
798 		pfv->pfv_argc++;
799 		nfd = pfd;
800 
801 		if (p > q) {
802 			pfd->pfd_preflen = (size_t)(p - q);
803 			pfd->pfd_prefix = q;
804 		}
805 
806 		fmt_switch:
807 		switch (c = *++p) {
808 		case '0': case '1': case '2': case '3': case '4':
809 		case '5': case '6': case '7': case '8': case '9':
810 			if (dot == 0 && digits == 0 && c == '0') {
811 				pfd->pfd_flags |= DT_PFCONV_ZPAD;
812 				pfd->pfd_flags &= ~DT_PFCONV_LEFT;
813 				goto fmt_switch;
814 			}
815 
816 			for (n = 0; isdigit(c); c = *++p)
817 				n = n * 10 + c - '0';
818 
819 			if (dot)
820 				pfd->pfd_prec = n;
821 			else
822 				pfd->pfd_width = n;
823 
824 			p--;
825 			digits++;
826 			goto fmt_switch;
827 
828 		case '#':
829 			pfd->pfd_flags |= DT_PFCONV_ALT;
830 			goto fmt_switch;
831 
832 		case '*':
833 			n = dot ? DT_PFCONV_DYNPREC : DT_PFCONV_DYNWIDTH;
834 
835 			if (pfd->pfd_flags & n) {
836 				yywarn("format conversion #%u has more than "
837 				    "one '*' specified for the output %s\n",
838 				    pfv->pfv_argc, n ? "precision" : "width");
839 
840 				dt_printf_destroy(pfv);
841 				return (dt_printf_error(dtp, EDT_COMPILER));
842 			}
843 
844 			pfd->pfd_flags |= n;
845 			goto fmt_switch;
846 
847 		case '+':
848 			pfd->pfd_flags |= DT_PFCONV_SPOS;
849 			goto fmt_switch;
850 
851 		case '-':
852 			pfd->pfd_flags |= DT_PFCONV_LEFT;
853 			pfd->pfd_flags &= ~DT_PFCONV_ZPAD;
854 			goto fmt_switch;
855 
856 		case '.':
857 			if (dot++ != 0) {
858 				yywarn("format conversion #%u has more than "
859 				    "one '.' specified\n", pfv->pfv_argc);
860 
861 				dt_printf_destroy(pfv);
862 				return (dt_printf_error(dtp, EDT_COMPILER));
863 			}
864 			digits = 0;
865 			goto fmt_switch;
866 
867 		case '?':
868 			if (dtp->dt_conf.dtc_ctfmodel == CTF_MODEL_LP64)
869 				pfd->pfd_width = 16;
870 			else
871 				pfd->pfd_width = 8;
872 			goto fmt_switch;
873 
874 		case '@':
875 			pfd->pfd_flags |= DT_PFCONV_AGG;
876 			goto fmt_switch;
877 
878 		case '\'':
879 			pfd->pfd_flags |= DT_PFCONV_GROUP;
880 			goto fmt_switch;
881 
882 		case ' ':
883 			pfd->pfd_flags |= DT_PFCONV_SPACE;
884 			goto fmt_switch;
885 
886 		case '$':
887 			yywarn("format conversion #%u uses unsupported "
888 			    "positional format (%%n$)\n", pfv->pfv_argc);
889 
890 			dt_printf_destroy(pfv);
891 			return (dt_printf_error(dtp, EDT_COMPILER));
892 
893 		case '%':
894 			if (p[-1] == '%')
895 				goto default_lbl; /* if %% then use "%" conv */
896 
897 			yywarn("format conversion #%u cannot be combined "
898 			    "with other format flags: %%%%\n", pfv->pfv_argc);
899 
900 			dt_printf_destroy(pfv);
901 			return (dt_printf_error(dtp, EDT_COMPILER));
902 
903 		case '\0':
904 			yywarn("format conversion #%u name expected before "
905 			    "end of format string\n", pfv->pfv_argc);
906 
907 			dt_printf_destroy(pfv);
908 			return (dt_printf_error(dtp, EDT_COMPILER));
909 
910 		case 'h':
911 		case 'l':
912 		case 'L':
913 		case 'w':
914 			if (namelen < sizeof (name) - 2)
915 				name[namelen++] = c;
916 			goto fmt_switch;
917 
918 		default_lbl:
919 		default:
920 			name[namelen++] = c;
921 			name[namelen] = '\0';
922 		}
923 
924 		pfd->pfd_conv = dt_pfdict_lookup(dtp, name);
925 
926 		if (pfd->pfd_conv == NULL) {
927 			yywarn("format conversion #%u is undefined: %%%s\n",
928 			    pfv->pfv_argc, name);
929 			dt_printf_destroy(pfv);
930 			return (dt_printf_error(dtp, EDT_COMPILER));
931 		}
932 	}
933 
934 	if (*q != '\0' || *format == '\0') {
935 		if ((pfd = malloc(sizeof (dt_pfargd_t))) == NULL) {
936 			dt_printf_destroy(pfv);
937 			return (dt_printf_error(dtp, EDT_NOMEM));
938 		}
939 
940 		if (pfv->pfv_argv != NULL)
941 			nfd->pfd_next = pfd;
942 		else
943 			pfv->pfv_argv = pfd;
944 
945 		bzero(pfd, sizeof (dt_pfargd_t));
946 		pfv->pfv_argc++;
947 
948 		pfd->pfd_prefix = q;
949 		pfd->pfd_preflen = strlen(q);
950 	}
951 
952 	return (pfv);
953 }
954 
955 void
956 dt_printf_destroy(dt_pfargv_t *pfv)
957 {
958 	dt_pfargd_t *pfd, *nfd;
959 
960 	for (pfd = pfv->pfv_argv; pfd != NULL; pfd = nfd) {
961 		nfd = pfd->pfd_next;
962 		free(pfd);
963 	}
964 
965 	free(pfv->pfv_format);
966 	free(pfv);
967 }
968 
969 void
970 dt_printf_validate(dt_pfargv_t *pfv, uint_t flags,
971     dt_ident_t *idp, int foff, dtrace_actkind_t kind, dt_node_t *dnp)
972 {
973 	dt_pfargd_t *pfd = pfv->pfv_argv;
974 	const char *func = idp->di_name;
975 
976 	char n[DT_TYPE_NAMELEN];
977 	dtrace_typeinfo_t dtt;
978 	const char *aggtype;
979 	dt_node_t aggnode;
980 	int i, j;
981 
982 	if (pfv->pfv_format[0] == '\0') {
983 		xyerror(D_PRINTF_FMT_EMPTY,
984 		    "%s( ) format string is empty\n", func);
985 	}
986 
987 	pfv->pfv_flags = flags;
988 
989 	/*
990 	 * We fake up a parse node representing the type that can be used with
991 	 * an aggregation result conversion, which -- for all but count() --
992 	 * is a signed quantity.
993 	 */
994 	if (kind != DTRACEAGG_COUNT)
995 		aggtype = "int64_t";
996 	else
997 		aggtype = "uint64_t";
998 
999 	if (dt_type_lookup(aggtype, &dtt) != 0)
1000 		xyerror(D_TYPE_ERR, "failed to lookup agg type %s\n", aggtype);
1001 
1002 	bzero(&aggnode, sizeof (aggnode));
1003 	dt_node_type_assign(&aggnode, dtt.dtt_ctfp, dtt.dtt_type);
1004 
1005 	for (i = 0, j = 0; i < pfv->pfv_argc; i++, pfd = pfd->pfd_next) {
1006 		const dt_pfconv_t *pfc = pfd->pfd_conv;
1007 		const char *dyns[2];
1008 		int dync = 0;
1009 
1010 		char vname[64];
1011 		dt_node_t *vnp;
1012 
1013 		if (pfc == NULL)
1014 			continue; /* no checking if argd is just a prefix */
1015 
1016 		if (pfc->pfc_print == &pfprint_pct) {
1017 			(void) strcat(pfd->pfd_fmt, pfc->pfc_ofmt);
1018 			continue;
1019 		}
1020 
1021 		if (pfd->pfd_flags & DT_PFCONV_DYNPREC)
1022 			dyns[dync++] = ".*";
1023 		if (pfd->pfd_flags & DT_PFCONV_DYNWIDTH)
1024 			dyns[dync++] = "*";
1025 
1026 		for (; dync != 0; dync--) {
1027 			if (dnp == NULL) {
1028 				xyerror(D_PRINTF_DYN_PROTO,
1029 				    "%s( ) prototype mismatch: conversion "
1030 				    "#%d (%%%s) is missing a corresponding "
1031 				    "\"%s\" argument\n", func, i + 1,
1032 				    pfc->pfc_name, dyns[dync - 1]);
1033 			}
1034 
1035 			if (dt_node_is_integer(dnp) == 0) {
1036 				xyerror(D_PRINTF_DYN_TYPE,
1037 				    "%s( ) argument #%d is incompatible "
1038 				    "with conversion #%d prototype:\n"
1039 				    "\tconversion: %% %s %s\n"
1040 				    "\t prototype: int\n\t  argument: %s\n",
1041 				    func, j + foff + 1, i + 1,
1042 				    dyns[dync - 1], pfc->pfc_name,
1043 				    dt_node_type_name(dnp, n, sizeof (n)));
1044 			}
1045 
1046 			dnp = dnp->dn_list;
1047 			j++;
1048 		}
1049 
1050 		/*
1051 		 * If this conversion is consuming the aggregation data, set
1052 		 * the value node pointer (vnp) to a fake node based on the
1053 		 * aggregating function result type.  Otherwise assign vnp to
1054 		 * the next parse node in the argument list, if there is one.
1055 		 */
1056 		if (pfd->pfd_flags & DT_PFCONV_AGG) {
1057 			if (!(flags & DT_PRINTF_AGGREGATION)) {
1058 				xyerror(D_PRINTF_AGG_CONV,
1059 				    "%%@ conversion requires an aggregation"
1060 				    " and is not for use with %s( )\n", func);
1061 			}
1062 			(void) strlcpy(vname, "aggregating action",
1063 			    sizeof (vname));
1064 			vnp = &aggnode;
1065 		} else if (dnp == NULL) {
1066 			xyerror(D_PRINTF_ARG_PROTO,
1067 			    "%s( ) prototype mismatch: conversion #%d (%%"
1068 			    "%s) is missing a corresponding value argument\n",
1069 			    func, i + 1, pfc->pfc_name);
1070 		} else {
1071 			(void) snprintf(vname, sizeof (vname),
1072 			    "argument #%d", j + foff + 1);
1073 			vnp = dnp;
1074 			dnp = dnp->dn_list;
1075 			j++;
1076 		}
1077 
1078 		/*
1079 		 * Fill in the proposed final format string by prepending any
1080 		 * size-related prefixes to the pfconv's format string.  The
1081 		 * pfc_check() function below may optionally modify the format
1082 		 * as part of validating the type of the input argument.
1083 		 */
1084 		if (pfc->pfc_print == &pfprint_sint ||
1085 		    pfc->pfc_print == &pfprint_uint ||
1086 		    pfc->pfc_print == &pfprint_dint) {
1087 			if (dt_node_type_size(vnp) == sizeof (uint64_t))
1088 				(void) strcpy(pfd->pfd_fmt, "ll");
1089 		} else if (pfc->pfc_print == &pfprint_fp) {
1090 			if (dt_node_type_size(vnp) == sizeof (long double))
1091 				(void) strcpy(pfd->pfd_fmt, "L");
1092 		}
1093 
1094 		(void) strcat(pfd->pfd_fmt, pfc->pfc_ofmt);
1095 
1096 		/*
1097 		 * Validate the format conversion against the value node type.
1098 		 * If the conversion is good, create the descriptor format
1099 		 * string by concatenating together any required printf(3C)
1100 		 * size prefixes with the conversion's native format string.
1101 		 */
1102 		if (pfc->pfc_check(pfv, pfd, vnp) == 0) {
1103 			xyerror(D_PRINTF_ARG_TYPE,
1104 			    "%s( ) %s is incompatible with "
1105 			    "conversion #%d prototype:\n\tconversion: %%%s\n"
1106 			    "\t prototype: %s\n\t  argument: %s\n", func,
1107 			    vname, i + 1, pfc->pfc_name, pfc->pfc_tstr,
1108 			    dt_node_type_name(vnp, n, sizeof (n)));
1109 		}
1110 	}
1111 
1112 	if ((flags & DT_PRINTF_EXACTLEN) && dnp != NULL) {
1113 		xyerror(D_PRINTF_ARG_EXTRA,
1114 		    "%s( ) prototype mismatch: only %d arguments "
1115 		    "required by this format string\n", func, j);
1116 	}
1117 }
1118 
1119 void
1120 dt_printa_validate(dt_node_t *lhs, dt_node_t *rhs)
1121 {
1122 	dt_ident_t *lid, *rid;
1123 	dt_node_t *lproto, *rproto;
1124 	int largc, rargc, argn;
1125 	char n1[DT_TYPE_NAMELEN];
1126 	char n2[DT_TYPE_NAMELEN];
1127 
1128 	assert(lhs->dn_kind == DT_NODE_AGG);
1129 	assert(rhs->dn_kind == DT_NODE_AGG);
1130 
1131 	lid = lhs->dn_ident;
1132 	rid = rhs->dn_ident;
1133 
1134 	lproto = ((dt_idsig_t *)lid->di_data)->dis_args;
1135 	rproto = ((dt_idsig_t *)rid->di_data)->dis_args;
1136 
1137 	/*
1138 	 * First, get an argument count on each side.  These must match.
1139 	 */
1140 	for (largc = 0; lproto != NULL; lproto = lproto->dn_list)
1141 		largc++;
1142 
1143 	for (rargc = 0; rproto != NULL; rproto = rproto->dn_list)
1144 		rargc++;
1145 
1146 	if (largc != rargc) {
1147 		xyerror(D_PRINTA_AGGKEY, "printa( ): @%s and @%s do not have "
1148 		    "matching key signatures: @%s has %d key%s, @%s has %d "
1149 		    "key%s", lid->di_name, rid->di_name,
1150 		    lid->di_name, largc, largc == 1 ? "" : "s",
1151 		    rid->di_name, rargc, rargc == 1 ? "" : "s");
1152 	}
1153 
1154 	/*
1155 	 * Now iterate over the keys to verify that each type matches.
1156 	 */
1157 	lproto = ((dt_idsig_t *)lid->di_data)->dis_args;
1158 	rproto = ((dt_idsig_t *)rid->di_data)->dis_args;
1159 
1160 	for (argn = 1; lproto != NULL; argn++, lproto = lproto->dn_list,
1161 	    rproto = rproto->dn_list) {
1162 		assert(rproto != NULL);
1163 
1164 		if (dt_node_is_argcompat(lproto, rproto))
1165 			continue;
1166 
1167 		xyerror(D_PRINTA_AGGPROTO, "printa( ): @%s[ ] key #%d is "
1168 		    "incompatible with @%s:\n%9s key #%d: %s\n"
1169 		    "%9s key #%d: %s\n",
1170 		    rid->di_name, argn, lid->di_name, lid->di_name, argn,
1171 		    dt_node_type_name(lproto, n1, sizeof (n1)), rid->di_name,
1172 		    argn, dt_node_type_name(rproto, n2, sizeof (n2)));
1173 	}
1174 }
1175 
1176 static int
1177 dt_printf_getint(dtrace_hdl_t *dtp, const dtrace_recdesc_t *recp,
1178     uint_t nrecs, const void *buf, size_t len, int *ip)
1179 {
1180 	uintptr_t addr;
1181 
1182 	if (nrecs == 0)
1183 		return (dt_set_errno(dtp, EDT_DMISMATCH));
1184 
1185 	addr = (uintptr_t)buf + recp->dtrd_offset;
1186 
1187 	if (addr + sizeof (int) > (uintptr_t)buf + len)
1188 		return (dt_set_errno(dtp, EDT_DOFFSET));
1189 
1190 	if (addr & (recp->dtrd_alignment - 1))
1191 		return (dt_set_errno(dtp, EDT_DALIGN));
1192 
1193 	switch (recp->dtrd_size) {
1194 	case sizeof (int8_t):
1195 		*ip = (int)*((int8_t *)addr);
1196 		break;
1197 	case sizeof (int16_t):
1198 		*ip = (int)*((int16_t *)addr);
1199 		break;
1200 	case sizeof (int32_t):
1201 		*ip = (int)*((int32_t *)addr);
1202 		break;
1203 	case sizeof (int64_t):
1204 		*ip = (int)*((int64_t *)addr);
1205 		break;
1206 	default:
1207 		return (dt_set_errno(dtp, EDT_DMISMATCH));
1208 	}
1209 
1210 	return (0);
1211 }
1212 
1213 /*ARGSUSED*/
1214 static int
1215 pfprint_average(dtrace_hdl_t *dtp, FILE *fp, const char *format,
1216     const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
1217 {
1218 	const uint64_t *data = addr;
1219 
1220 	if (size != sizeof (uint64_t) * 2)
1221 		return (dt_set_errno(dtp, EDT_DMISMATCH));
1222 
1223 	return (dt_printf(dtp, fp, format,
1224 	    data[0] ? data[1] / normal / data[0] : 0));
1225 }
1226 
1227 /*ARGSUSED*/
1228 static int
1229 pfprint_quantize(dtrace_hdl_t *dtp, FILE *fp, const char *format,
1230     const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
1231 {
1232 	return (dt_print_quantize(dtp, fp, addr, size, normal));
1233 }
1234 
1235 /*ARGSUSED*/
1236 static int
1237 pfprint_lquantize(dtrace_hdl_t *dtp, FILE *fp, const char *format,
1238     const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
1239 {
1240 	return (dt_print_lquantize(dtp, fp, addr, size, normal));
1241 }
1242 
1243 static int
1244 dt_printf_format(dtrace_hdl_t *dtp, FILE *fp, const dt_pfargv_t *pfv,
1245     const dtrace_recdesc_t *recs, uint_t nrecs, const void *buf,
1246     size_t len, const dtrace_aggdata_t **aggsdata, int naggvars)
1247 {
1248 	dt_pfargd_t *pfd = pfv->pfv_argv;
1249 	const dtrace_recdesc_t *recp = recs;
1250 	const dtrace_aggdata_t *aggdata;
1251 	dtrace_aggdesc_t *agg;
1252 	caddr_t lim = (caddr_t)buf + len, limit;
1253 	char format[64] = "%";
1254 	int i, aggrec, curagg = -1;
1255 	uint64_t normal;
1256 
1257 	/*
1258 	 * If we are formatting an aggregation, set 'aggrec' to the index of
1259 	 * the final record description (the aggregation result) so we can use
1260 	 * this record index with any conversion where DT_PFCONV_AGG is set.
1261 	 * (The actual aggregation used will vary as we increment through the
1262 	 * aggregation variables that we have been passed.)  Finally, we
1263 	 * decrement nrecs to prevent this record from being used with any
1264 	 * other conversion.
1265 	 */
1266 	if (pfv->pfv_flags & DT_PRINTF_AGGREGATION) {
1267 		assert(aggsdata != NULL);
1268 		assert(naggvars > 0);
1269 
1270 		if (nrecs == 0)
1271 			return (dt_set_errno(dtp, EDT_DMISMATCH));
1272 
1273 		curagg = naggvars > 1 ? 1 : 0;
1274 		aggdata = aggsdata[0];
1275 		aggrec = aggdata->dtada_desc->dtagd_nrecs - 1;
1276 		nrecs--;
1277 	}
1278 
1279 	for (i = 0; i < pfv->pfv_argc; i++, pfd = pfd->pfd_next) {
1280 		const dt_pfconv_t *pfc = pfd->pfd_conv;
1281 		int width = pfd->pfd_width;
1282 		int prec = pfd->pfd_prec;
1283 		int rval;
1284 
1285 		char *f = format + 1; /* skip initial '%' */
1286 		const dtrace_recdesc_t *rec;
1287 		dt_pfprint_f *func;
1288 		caddr_t addr;
1289 		size_t size;
1290 		uint32_t flags;
1291 
1292 		if (pfd->pfd_preflen != 0) {
1293 			char *tmp = alloca(pfd->pfd_preflen + 1);
1294 
1295 			bcopy(pfd->pfd_prefix, tmp, pfd->pfd_preflen);
1296 			tmp[pfd->pfd_preflen] = '\0';
1297 
1298 			if ((rval = dt_printf(dtp, fp, tmp)) < 0)
1299 				return (rval);
1300 
1301 			if (pfv->pfv_flags & DT_PRINTF_AGGREGATION) {
1302 				/*
1303 				 * For printa(), we flush the buffer after each
1304 				 * prefix, setting the flags to indicate that
1305 				 * this is part of the printa() format string.
1306 				 */
1307 				flags = DTRACE_BUFDATA_AGGFORMAT;
1308 
1309 				if (pfc == NULL && i == pfv->pfv_argc - 1)
1310 					flags |= DTRACE_BUFDATA_AGGLAST;
1311 
1312 				if (dt_buffered_flush(dtp, NULL, NULL,
1313 				    aggdata, flags) < 0)
1314 					return (-1);
1315 			}
1316 		}
1317 
1318 		if (pfc == NULL) {
1319 			if (pfv->pfv_argc == 1)
1320 				return (nrecs != 0);
1321 			continue;
1322 		}
1323 
1324 		/*
1325 		 * If the conversion is %%, just invoke the print callback
1326 		 * with no data record and continue; it consumes no record.
1327 		 */
1328 		if (pfc->pfc_print == &pfprint_pct) {
1329 			if (pfc->pfc_print(dtp, fp, NULL, pfd, NULL, 0, 1) >= 0)
1330 				continue;
1331 			return (-1); /* errno is set for us */
1332 		}
1333 
1334 		if (pfd->pfd_flags & DT_PFCONV_DYNWIDTH) {
1335 			if (dt_printf_getint(dtp, recp++, nrecs--, buf,
1336 			    len, &width) == -1)
1337 				return (-1); /* errno is set for us */
1338 			pfd->pfd_dynwidth = width;
1339 		} else {
1340 			pfd->pfd_dynwidth = 0;
1341 		}
1342 
1343 		if ((pfd->pfd_flags & DT_PFCONV_DYNPREC) && dt_printf_getint(
1344 		    dtp, recp++, nrecs--, buf, len, &prec) == -1)
1345 			return (-1); /* errno is set for us */
1346 
1347 		if (pfd->pfd_flags & DT_PFCONV_AGG) {
1348 			/*
1349 			 * This should be impossible -- the compiler shouldn't
1350 			 * create a DT_PFCONV_AGG conversion without an
1351 			 * aggregation present.  Still, we'd rather fail
1352 			 * gracefully than blow up...
1353 			 */
1354 			if (aggsdata == NULL)
1355 				return (dt_set_errno(dtp, EDT_DMISMATCH));
1356 
1357 			aggdata = aggsdata[curagg];
1358 			agg = aggdata->dtada_desc;
1359 
1360 			/*
1361 			 * We increment the current aggregation variable, but
1362 			 * not beyond the number of aggregation variables that
1363 			 * we're printing. This has the (desired) effect that
1364 			 * DT_PFCONV_AGG conversions beyond the number of
1365 			 * aggregation variables (re-)convert the aggregation
1366 			 * value of the last aggregation variable.
1367 			 */
1368 			if (curagg < naggvars - 1)
1369 				curagg++;
1370 
1371 			rec = &agg->dtagd_rec[aggrec];
1372 			addr = aggdata->dtada_data + rec->dtrd_offset;
1373 			limit = addr + aggdata->dtada_size;
1374 			normal = aggdata->dtada_normal;
1375 			flags = DTRACE_BUFDATA_AGGVAL;
1376 		} else {
1377 			if (nrecs == 0)
1378 				return (dt_set_errno(dtp, EDT_DMISMATCH));
1379 
1380 			if (pfv->pfv_flags & DT_PRINTF_AGGREGATION) {
1381 				/*
1382 				 * When printing aggregation keys, we always
1383 				 * set the aggdata to be the representative
1384 				 * (zeroth) aggregation.  The aggdata isn't
1385 				 * actually used here in this case, but it is
1386 				 * passed to the buffer handler and must
1387 				 * therefore still be correct.
1388 				 */
1389 				aggdata = aggsdata[0];
1390 				flags = DTRACE_BUFDATA_AGGKEY;
1391 			}
1392 
1393 			rec = recp++;
1394 			nrecs--;
1395 			addr = (caddr_t)buf + rec->dtrd_offset;
1396 			limit = lim;
1397 			normal = 1;
1398 		}
1399 
1400 		size = rec->dtrd_size;
1401 
1402 		if (addr + size > limit) {
1403 			dt_dprintf("bad size: addr=%p size=0x%x lim=%p\n",
1404 			    (void *)addr, rec->dtrd_size, (void *)lim);
1405 			return (dt_set_errno(dtp, EDT_DOFFSET));
1406 		}
1407 
1408 		if (rec->dtrd_alignment != 0 &&
1409 		    ((uintptr_t)addr & (rec->dtrd_alignment - 1)) != 0) {
1410 			dt_dprintf("bad align: addr=%p size=0x%x align=0x%x\n",
1411 			    (void *)addr, rec->dtrd_size, rec->dtrd_alignment);
1412 			return (dt_set_errno(dtp, EDT_DALIGN));
1413 		}
1414 
1415 		switch (rec->dtrd_action) {
1416 		case DTRACEAGG_AVG:
1417 			func = pfprint_average;
1418 			break;
1419 		case DTRACEAGG_QUANTIZE:
1420 			func = pfprint_quantize;
1421 			break;
1422 		case DTRACEAGG_LQUANTIZE:
1423 			func = pfprint_lquantize;
1424 			break;
1425 		case DTRACEACT_MOD:
1426 			func = pfprint_mod;
1427 			break;
1428 		case DTRACEACT_UMOD:
1429 			func = pfprint_umod;
1430 			break;
1431 		default:
1432 			func = pfc->pfc_print;
1433 			break;
1434 		}
1435 
1436 		if (pfd->pfd_flags & DT_PFCONV_ALT)
1437 			*f++ = '#';
1438 		if (pfd->pfd_flags & DT_PFCONV_ZPAD)
1439 			*f++ = '0';
1440 		if (width < 0 || (pfd->pfd_flags & DT_PFCONV_LEFT))
1441 			*f++ = '-';
1442 		if (pfd->pfd_flags & DT_PFCONV_SPOS)
1443 			*f++ = '+';
1444 		if (pfd->pfd_flags & DT_PFCONV_GROUP)
1445 			*f++ = '\'';
1446 		if (pfd->pfd_flags & DT_PFCONV_SPACE)
1447 			*f++ = ' ';
1448 
1449 		/*
1450 		 * If we're printing a stack and DT_PFCONV_LEFT is set, we
1451 		 * don't add the width to the format string.  See the block
1452 		 * comment in pfprint_stack() for a description of the
1453 		 * behavior in this case.
1454 		 */
1455 		if (func == pfprint_stack && (pfd->pfd_flags & DT_PFCONV_LEFT))
1456 			width = 0;
1457 
1458 		if (width != 0)
1459 			f += snprintf(f, sizeof (format), "%d", ABS(width));
1460 
1461 		if (prec > 0)
1462 			f += snprintf(f, sizeof (format), ".%d", prec);
1463 
1464 		(void) strcpy(f, pfd->pfd_fmt);
1465 		pfd->pfd_rec = rec;
1466 
1467 		if (func(dtp, fp, format, pfd, addr, size, normal) < 0)
1468 			return (-1); /* errno is set for us */
1469 
1470 		if (pfv->pfv_flags & DT_PRINTF_AGGREGATION) {
1471 			/*
1472 			 * For printa(), we flush the buffer after each tuple
1473 			 * element, inidicating that this is the last record
1474 			 * as appropriate.
1475 			 */
1476 			if (i == pfv->pfv_argc - 1)
1477 				flags |= DTRACE_BUFDATA_AGGLAST;
1478 
1479 			if (dt_buffered_flush(dtp, NULL,
1480 			    rec, aggdata, flags) < 0)
1481 				return (-1);
1482 		}
1483 	}
1484 
1485 	return ((int)(recp - recs));
1486 }
1487 
1488 int
1489 dtrace_sprintf(dtrace_hdl_t *dtp, FILE *fp, void *fmtdata,
1490     const dtrace_recdesc_t *recp, uint_t nrecs, const void *buf, size_t len)
1491 {
1492 	dtrace_optval_t size;
1493 	int rval;
1494 
1495 	rval = dtrace_getopt(dtp, "strsize", &size);
1496 	assert(rval == 0);
1497 	assert(dtp->dt_sprintf_buflen == 0);
1498 
1499 	if (dtp->dt_sprintf_buf != NULL)
1500 		free(dtp->dt_sprintf_buf);
1501 
1502 	if ((dtp->dt_sprintf_buf = malloc(size)) == NULL)
1503 		return (dt_set_errno(dtp, EDT_NOMEM));
1504 
1505 	bzero(dtp->dt_sprintf_buf, size);
1506 	dtp->dt_sprintf_buflen = size;
1507 	rval = dt_printf_format(dtp, fp, fmtdata, recp, nrecs, buf, len,
1508 	    NULL, 0);
1509 	dtp->dt_sprintf_buflen = 0;
1510 
1511 	if (rval == -1)
1512 		free(dtp->dt_sprintf_buf);
1513 
1514 	return (rval);
1515 }
1516 
1517 /*ARGSUSED*/
1518 int
1519 dtrace_system(dtrace_hdl_t *dtp, FILE *fp, void *fmtdata,
1520     const dtrace_probedata_t *data, const dtrace_recdesc_t *recp,
1521     uint_t nrecs, const void *buf, size_t len)
1522 {
1523 	int rval = dtrace_sprintf(dtp, fp, fmtdata, recp, nrecs, buf, len);
1524 
1525 	if (rval == -1)
1526 		return (rval);
1527 
1528 	/*
1529 	 * Before we execute the specified command, flush fp to assure that
1530 	 * any prior dt_printf()'s appear before the output of the command
1531 	 * not after it.
1532 	 */
1533 	(void) fflush(fp);
1534 
1535 	if (system(dtp->dt_sprintf_buf) == -1)
1536 		return (dt_set_errno(dtp, errno));
1537 
1538 	return (rval);
1539 }
1540 
1541 int
1542 dtrace_freopen(dtrace_hdl_t *dtp, FILE *fp, void *fmtdata,
1543     const dtrace_probedata_t *data, const dtrace_recdesc_t *recp,
1544     uint_t nrecs, const void *buf, size_t len)
1545 {
1546 	char selfbuf[40], restorebuf[40], *filename;
1547 	FILE *nfp;
1548 	int rval, errval;
1549 	dt_pfargv_t *pfv = fmtdata;
1550 	dt_pfargd_t *pfd = pfv->pfv_argv;
1551 
1552 	rval = dtrace_sprintf(dtp, fp, fmtdata, recp, nrecs, buf, len);
1553 
1554 	if (rval == -1 || fp == NULL)
1555 		return (rval);
1556 
1557 	if (pfd->pfd_preflen != 0 &&
1558 	    strcmp(pfd->pfd_prefix, DT_FREOPEN_RESTORE) == 0) {
1559 		/*
1560 		 * The only way to have the format string set to the value
1561 		 * DT_FREOPEN_RESTORE is via the empty freopen() string --
1562 		 * denoting that we should restore the old stdout.
1563 		 */
1564 		assert(strcmp(dtp->dt_sprintf_buf, DT_FREOPEN_RESTORE) == 0);
1565 
1566 		if (dtp->dt_stdout_fd == -1) {
1567 			/*
1568 			 * We could complain here by generating an error,
1569 			 * but it seems like overkill:  it seems that calling
1570 			 * freopen() to restore stdout when freopen() has
1571 			 * never before been called should just be a no-op,
1572 			 * so we just return in this case.
1573 			 */
1574 			return (rval);
1575 		}
1576 
1577 		(void) snprintf(restorebuf, sizeof (restorebuf),
1578 		    "/dev/fd/%d", dtp->dt_stdout_fd);
1579 		filename = restorebuf;
1580 	} else {
1581 		filename = dtp->dt_sprintf_buf;
1582 	}
1583 
1584 	/*
1585 	 * freopen(3C) will always close the specified stream and underlying
1586 	 * file descriptor -- even if the specified file can't be opened.
1587 	 * Even for the semantic cesspool that is standard I/O, this is
1588 	 * surprisingly brain-dead behavior:  it means that any failure to
1589 	 * open the specified file destroys the specified stream in the
1590 	 * process -- which is particularly relevant when the specified stream
1591 	 * happens (or rather, happened) to be stdout.  This could be resolved
1592 	 * were there an "fdreopen()" equivalent of freopen() that allowed one
1593 	 * to pass a file descriptor instead of the name of a file, but there
1594 	 * is no such thing.  However, we can effect this ourselves by first
1595 	 * fopen()'ing the desired file, and then (assuming that that works),
1596 	 * freopen()'ing "/dev/fd/[fileno]", where [fileno] is the underlying
1597 	 * file descriptor for the fopen()'d file.  This way, if the fopen()
1598 	 * fails, we can fail the operation without destroying stdout.
1599 	 */
1600 	if ((nfp = fopen(filename, "aw")) == NULL) {
1601 		char *msg = strerror(errno), *faultstr;
1602 		int len = 80;
1603 
1604 		len += strlen(msg) + strlen(filename);
1605 		faultstr = alloca(len);
1606 
1607 		(void) snprintf(faultstr, len, "couldn't freopen() \"%s\": %s",
1608 		    filename, strerror(errno));
1609 
1610 		if ((errval = dt_handle_liberr(dtp, data, faultstr)) == 0)
1611 			return (rval);
1612 
1613 		return (errval);
1614 	}
1615 
1616 	(void) snprintf(selfbuf, sizeof (selfbuf), "/dev/fd/%d", fileno(nfp));
1617 
1618 	if (dtp->dt_stdout_fd == -1) {
1619 		/*
1620 		 * If this is the first time that we're calling freopen(),
1621 		 * we're going to stash away the file descriptor for stdout.
1622 		 * We don't expect the dup(2) to fail, so if it does we must
1623 		 * return failure.
1624 		 */
1625 		if ((dtp->dt_stdout_fd = dup(fileno(fp))) == -1) {
1626 			(void) fclose(nfp);
1627 			return (dt_set_errno(dtp, errno));
1628 		}
1629 	}
1630 
1631 	if (freopen(selfbuf, "aw", fp) == NULL) {
1632 		(void) fclose(nfp);
1633 		return (dt_set_errno(dtp, errno));
1634 	}
1635 
1636 	(void) fclose(nfp);
1637 
1638 	return (rval);
1639 }
1640 
1641 /*ARGSUSED*/
1642 int
1643 dtrace_fprintf(dtrace_hdl_t *dtp, FILE *fp, void *fmtdata,
1644     const dtrace_probedata_t *data, const dtrace_recdesc_t *recp,
1645     uint_t nrecs, const void *buf, size_t len)
1646 {
1647 	return (dt_printf_format(dtp, fp, fmtdata,
1648 	    recp, nrecs, buf, len, NULL, 0));
1649 }
1650 
1651 void *
1652 dtrace_printf_create(dtrace_hdl_t *dtp, const char *s)
1653 {
1654 	dt_pfargv_t *pfv = dt_printf_create(dtp, s);
1655 	dt_pfargd_t *pfd;
1656 	int i;
1657 
1658 	if (pfv == NULL)
1659 		return (NULL);		/* errno has been set for us */
1660 
1661 	pfd = pfv->pfv_argv;
1662 
1663 	for (i = 0; i < pfv->pfv_argc; i++, pfd = pfd->pfd_next) {
1664 		const dt_pfconv_t *pfc = pfd->pfd_conv;
1665 
1666 		if (pfc == NULL)
1667 			continue;
1668 
1669 		/*
1670 		 * If the output format is not %s then we assume that we have
1671 		 * been given a correctly-sized format string, so we copy the
1672 		 * true format name including the size modifier.  If the output
1673 		 * format is %s, then either the input format is %s as well or
1674 		 * it is one of our custom formats (e.g. pfprint_addr), so we
1675 		 * must set pfd_fmt to be the output format conversion "s".
1676 		 */
1677 		if (strcmp(pfc->pfc_ofmt, "s") != 0)
1678 			(void) strcat(pfd->pfd_fmt, pfc->pfc_name);
1679 		else
1680 			(void) strcat(pfd->pfd_fmt, pfc->pfc_ofmt);
1681 	}
1682 
1683 	return (pfv);
1684 }
1685 
1686 void *
1687 dtrace_printa_create(dtrace_hdl_t *dtp, const char *s)
1688 {
1689 	dt_pfargv_t *pfv = dtrace_printf_create(dtp, s);
1690 
1691 	if (pfv == NULL)
1692 		return (NULL);		/* errno has been set for us */
1693 
1694 	pfv->pfv_flags |= DT_PRINTF_AGGREGATION;
1695 
1696 	return (pfv);
1697 }
1698 
1699 /*ARGSUSED*/
1700 size_t
1701 dtrace_printf_format(dtrace_hdl_t *dtp, void *fmtdata, char *s, size_t len)
1702 {
1703 	dt_pfargv_t *pfv = fmtdata;
1704 	dt_pfargd_t *pfd = pfv->pfv_argv;
1705 
1706 	/*
1707 	 * An upper bound on the string length is the length of the original
1708 	 * format string, plus three times the number of conversions (each
1709 	 * conversion could add up an additional "ll" and/or pfd_width digit
1710 	 * in the case of converting %? to %16) plus one for a terminating \0.
1711 	 */
1712 	size_t formatlen = strlen(pfv->pfv_format) + 3 * pfv->pfv_argc + 1;
1713 	char *format = alloca(formatlen);
1714 	char *f = format;
1715 	int i, j;
1716 
1717 	for (i = 0; i < pfv->pfv_argc; i++, pfd = pfd->pfd_next) {
1718 		const dt_pfconv_t *pfc = pfd->pfd_conv;
1719 		const char *str;
1720 		int width = pfd->pfd_width;
1721 		int prec = pfd->pfd_prec;
1722 
1723 		if (pfd->pfd_preflen != 0) {
1724 			for (j = 0; j < pfd->pfd_preflen; j++)
1725 				*f++ = pfd->pfd_prefix[j];
1726 		}
1727 
1728 		if (pfc == NULL)
1729 			continue;
1730 
1731 		*f++ = '%';
1732 
1733 		if (pfd->pfd_flags & DT_PFCONV_ALT)
1734 			*f++ = '#';
1735 		if (pfd->pfd_flags & DT_PFCONV_ZPAD)
1736 			*f++ = '0';
1737 		if (pfd->pfd_flags & DT_PFCONV_LEFT)
1738 			*f++ = '-';
1739 		if (pfd->pfd_flags & DT_PFCONV_SPOS)
1740 			*f++ = '+';
1741 		if (pfd->pfd_flags & DT_PFCONV_DYNWIDTH)
1742 			*f++ = '*';
1743 		if (pfd->pfd_flags & DT_PFCONV_DYNPREC) {
1744 			*f++ = '.';
1745 			*f++ = '*';
1746 		}
1747 		if (pfd->pfd_flags & DT_PFCONV_GROUP)
1748 			*f++ = '\'';
1749 		if (pfd->pfd_flags & DT_PFCONV_SPACE)
1750 			*f++ = ' ';
1751 		if (pfd->pfd_flags & DT_PFCONV_AGG)
1752 			*f++ = '@';
1753 
1754 		if (width != 0)
1755 			f += snprintf(f, sizeof (format), "%d", width);
1756 
1757 		if (prec != 0)
1758 			f += snprintf(f, sizeof (format), ".%d", prec);
1759 
1760 		/*
1761 		 * If the output format is %s, then either %s is the underlying
1762 		 * conversion or the conversion is one of our customized ones,
1763 		 * e.g. pfprint_addr.  In these cases, put the original string
1764 		 * name of the conversion (pfc_name) into the pickled format
1765 		 * string rather than the derived conversion (pfd_fmt).
1766 		 */
1767 		if (strcmp(pfc->pfc_ofmt, "s") == 0)
1768 			str = pfc->pfc_name;
1769 		else
1770 			str = pfd->pfd_fmt;
1771 
1772 		for (j = 0; str[j] != '\0'; j++)
1773 			*f++ = str[j];
1774 	}
1775 
1776 	*f = '\0'; /* insert nul byte; do not count in return value */
1777 
1778 	assert(f < format + formatlen);
1779 	(void) strncpy(s, format, len);
1780 
1781 	return ((size_t)(f - format));
1782 }
1783 
1784 static int
1785 dt_fprinta(const dtrace_aggdata_t *adp, void *arg)
1786 {
1787 	const dtrace_aggdesc_t *agg = adp->dtada_desc;
1788 	const dtrace_recdesc_t *recp = &agg->dtagd_rec[0];
1789 	uint_t nrecs = agg->dtagd_nrecs;
1790 	dt_pfwalk_t *pfw = arg;
1791 	dtrace_hdl_t *dtp = pfw->pfw_argv->pfv_dtp;
1792 	int id;
1793 
1794 	if (dt_printf_getint(dtp, recp++, nrecs--,
1795 	    adp->dtada_data, adp->dtada_size, &id) != 0 || pfw->pfw_aid != id)
1796 		return (0); /* no aggregation id or id does not match */
1797 
1798 	if (dt_printf_format(dtp, pfw->pfw_fp, pfw->pfw_argv,
1799 	    recp, nrecs, adp->dtada_data, adp->dtada_size, &adp, 1) == -1)
1800 		return (pfw->pfw_err = dtp->dt_errno);
1801 
1802 	/*
1803 	 * Cast away the const to set the bit indicating that this aggregation
1804 	 * has been printed.
1805 	 */
1806 	((dtrace_aggdesc_t *)agg)->dtagd_flags |= DTRACE_AGD_PRINTED;
1807 
1808 	return (0);
1809 }
1810 
1811 static int
1812 dt_fprintas(const dtrace_aggdata_t **aggsdata, int naggvars, void *arg)
1813 {
1814 	const dtrace_aggdata_t *aggdata = aggsdata[0];
1815 	const dtrace_aggdesc_t *agg = aggdata->dtada_desc;
1816 	const dtrace_recdesc_t *rec = &agg->dtagd_rec[1];
1817 	uint_t nrecs = agg->dtagd_nrecs - 1;
1818 	dt_pfwalk_t *pfw = arg;
1819 	dtrace_hdl_t *dtp = pfw->pfw_argv->pfv_dtp;
1820 	int i;
1821 
1822 	if (dt_printf_format(dtp, pfw->pfw_fp, pfw->pfw_argv,
1823 	    rec, nrecs, aggdata->dtada_data, aggdata->dtada_size,
1824 	    aggsdata, naggvars) == -1)
1825 		return (pfw->pfw_err = dtp->dt_errno);
1826 
1827 	/*
1828 	 * For each aggregation, indicate that it has been printed, casting
1829 	 * away the const as necessary.
1830 	 */
1831 	for (i = 1; i < naggvars; i++) {
1832 		agg = aggsdata[i]->dtada_desc;
1833 		((dtrace_aggdesc_t *)agg)->dtagd_flags |= DTRACE_AGD_PRINTED;
1834 	}
1835 
1836 	return (0);
1837 }
1838 /*ARGSUSED*/
1839 int
1840 dtrace_fprinta(dtrace_hdl_t *dtp, FILE *fp, void *fmtdata,
1841     const dtrace_probedata_t *data, const dtrace_recdesc_t *recs,
1842     uint_t nrecs, const void *buf, size_t len)
1843 {
1844 	dt_pfwalk_t pfw;
1845 	int i, naggvars = 0;
1846 	dtrace_aggvarid_t *aggvars;
1847 
1848 	aggvars = alloca(nrecs * sizeof (dtrace_aggvarid_t));
1849 
1850 	/*
1851 	 * This might be a printa() with multiple aggregation variables.  We
1852 	 * need to scan forward through the records until we find a record from
1853 	 * a different statement.
1854 	 */
1855 	for (i = 0; i < nrecs; i++) {
1856 		const dtrace_recdesc_t *nrec = &recs[i];
1857 
1858 		if (nrec->dtrd_uarg != recs->dtrd_uarg)
1859 			break;
1860 
1861 		if (nrec->dtrd_action != recs->dtrd_action)
1862 			return (dt_set_errno(dtp, EDT_BADAGG));
1863 
1864 		aggvars[naggvars++] =
1865 		    /* LINTED - alignment */
1866 		    *((dtrace_aggvarid_t *)((caddr_t)buf + nrec->dtrd_offset));
1867 	}
1868 
1869 	if (naggvars == 0)
1870 		return (dt_set_errno(dtp, EDT_BADAGG));
1871 
1872 	pfw.pfw_argv = fmtdata;
1873 	pfw.pfw_fp = fp;
1874 	pfw.pfw_err = 0;
1875 
1876 	if (naggvars == 1) {
1877 		pfw.pfw_aid = aggvars[0];
1878 
1879 		if (dtrace_aggregate_walk_sorted(dtp,
1880 		    dt_fprinta, &pfw) == -1 || pfw.pfw_err != 0)
1881 			return (-1); /* errno is set for us */
1882 	} else {
1883 		if (dtrace_aggregate_walk_joined(dtp, aggvars, naggvars,
1884 		    dt_fprintas, &pfw) == -1 || pfw.pfw_err != 0)
1885 			return (-1); /* errno is set for us */
1886 	}
1887 
1888 	return (i);
1889 }
1890