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