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