changeset 13501:c3a7090dbc16

1694 Add type-aware print() action Reviewed by: Adam Leventhal <Adam.Leventhal@delphix.com> Reviewed by: Bryan Cantrill <bryancantrill@gmail.com> Reviewed by: Richard Lowe <richlowe@richlowe.net> Approved by: Richard Lowe <richlowe@richlowe.net>
author Eric Schrock <Eric.Schrock@delphix.com>
date Thu, 03 Nov 2011 18:34:26 -0700
parents f077aa5fa57c
children 34f6da175f72
files exception_lists/copyright usr/src/cmd/dtrace/test/tst/common/print/err.D_PRINT_DYN.bad.d usr/src/cmd/dtrace/test/tst/common/print/err.D_PRINT_VOID.bad.d usr/src/cmd/dtrace/test/tst/common/print/err.D_PROTO_LEN.bad.d usr/src/cmd/dtrace/test/tst/common/print/tst.array.d usr/src/cmd/dtrace/test/tst/common/print/tst.array.d.out usr/src/cmd/dtrace/test/tst/common/print/tst.bitfield.d usr/src/cmd/dtrace/test/tst/common/print/tst.bitfield.d.out usr/src/cmd/dtrace/test/tst/common/print/tst.primitive.d usr/src/cmd/dtrace/test/tst/common/print/tst.primitive.d.out usr/src/cmd/dtrace/test/tst/common/print/tst.struct.d usr/src/cmd/dtrace/test/tst/common/print/tst.struct.d.out usr/src/cmd/dtrace/test/tst/common/trace/err.D_TRACE_DYN.bad.d usr/src/lib/libdtrace/Makefile.com usr/src/lib/libdtrace/common/dt_cc.c usr/src/lib/libdtrace/common/dt_consume.c usr/src/lib/libdtrace/common/dt_dof.c usr/src/lib/libdtrace/common/dt_errtags.h usr/src/lib/libdtrace/common/dt_impl.h usr/src/lib/libdtrace/common/dt_map.c usr/src/lib/libdtrace/common/dt_open.c usr/src/lib/libdtrace/common/dt_print.c usr/src/lib/libdtrace/common/dt_program.c usr/src/lib/libdtrace/common/dtrace.h usr/src/pkg/manifests/system-dtrace-tests.mf usr/src/uts/common/dtrace/dtrace.c
diffstat 26 files changed, 1289 insertions(+), 73 deletions(-) [+]
line wrap: on
line diff
--- a/exception_lists/copyright	Thu Nov 03 11:18:52 2011 +0400
+++ b/exception_lists/copyright	Thu Nov 03 18:34:26 2011 -0700
@@ -20,12 +20,14 @@
 #
 # Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
 # Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+# Copyright (c) 2011 by Delphix. All rights reserved.
 #
 
 syntax: glob
 exception_lists/closed-bins
 exception_lists/cstyle
 exception_lists/hdrchk
+usr/src/cmd/dtrace/test/tst/common/*/*.out
 usr/src/cmd/krb5/kadmin/cli/kadmin_ct.c
 usr/src/cmd/krb5/kadmin/cli/kadmin.h
 usr/src/cmd/krb5/kadmin/cli/ss_wrapper.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/dtrace/test/tst/common/print/err.D_PRINT_DYN.bad.d	Thu Nov 03 18:34:26 2011 -0700
@@ -0,0 +1,29 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2011 by Delphix. All rights reserved.
+ */
+
+BEGIN
+{
+	print(*curpsinfo);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/dtrace/test/tst/common/print/err.D_PRINT_VOID.bad.d	Thu Nov 03 18:34:26 2011 -0700
@@ -0,0 +1,29 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2011 by Delphix. All rights reserved.
+ */
+
+BEGIN
+{
+	print((void)`p0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/dtrace/test/tst/common/print/err.D_PROTO_LEN.bad.d	Thu Nov 03 18:34:26 2011 -0700
@@ -0,0 +1,29 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2011 by Delphix. All rights reserved.
+ */
+
+BEGIN
+{
+	print();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/dtrace/test/tst/common/print/tst.array.d	Thu Nov 03 18:34:26 2011 -0700
@@ -0,0 +1,62 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2011 by Delphix. All rights reserved.
+ */
+
+#pragma D option quiet
+
+typedef struct bar {
+	int alpha;
+} bar_t;
+
+typedef struct foo {
+	int a[3];
+	char b[30];
+	bar_t c[2];
+	char d[3];
+} foo_t;
+
+BEGIN
+{
+	this->f = (foo_t *)alloca(sizeof (foo_t));
+
+	this->f->a[0] = 1;
+	this->f->a[1] = 2;
+	this->f->a[2] = 3;
+	this->f->b[0] = 'a';
+	this->f->b[1] = 'b';
+	this->f->b[2] = 0;
+	this->f->c[0].alpha = 5;
+	this->f->c[1].alpha = 6;
+	this->f->c[2].alpha = 7;
+	this->f->d[0] = 4;
+	this->f->d[1] = 0;
+	this->f->d[2] = 0;
+
+	print(this->f->a);
+	print(this->f->b);
+	print(this->f->c);
+	print(*this->f);
+
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/dtrace/test/tst/common/print/tst.array.d.out	Thu Nov 03 18:34:26 2011 -0700
@@ -0,0 +1,23 @@
+int [3] [ 0x1, 0x2, 0x3 ]
+char [30] "ab"
+bar_t [2] [ 
+    bar_t {
+        int alpha = 0x5
+    }, 
+    bar_t {
+        int alpha = 0x6
+    }
+]
+foo_t {
+    int [3] a = [ 0x1, 0x2, 0x3 ]
+    char [30] b = [ "ab" ]
+    bar_t [2] c = [ 
+        bar_t {
+            int alpha = 0x5
+        }, 
+        bar_t {
+            int alpha = 0x6
+        }
+    ]
+    char [3] d = [ '\004', '\0', '\0' ]
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/dtrace/test/tst/common/print/tst.bitfield.d	Thu Nov 03 18:34:26 2011 -0700
@@ -0,0 +1,49 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2011 by Delphix. All rights reserved.
+ */
+
+#pragma D option quiet
+
+typedef struct forward forward_t;
+
+typedef struct foo {
+	int a:4;
+	int b:7;
+	int c:1;
+	int d:2;
+} foo_t;
+
+BEGIN
+{
+	this->s = (foo_t *)alloca(sizeof (foo_t));
+
+	this->s->a = 1;
+	this->s->b = 5;
+	this->s->c = 0;
+	this->s->d = 2;
+
+	print(*this->s);
+
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/dtrace/test/tst/common/print/tst.bitfield.d.out	Thu Nov 03 18:34:26 2011 -0700
@@ -0,0 +1,6 @@
+foo_t {
+    int a :4 = 0x1
+    int b :7 = 0x5
+    int c :1 = 0
+    int d :2 = 0x2
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/dtrace/test/tst/common/print/tst.primitive.d	Thu Nov 03 18:34:26 2011 -0700
@@ -0,0 +1,45 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2011 by Delphix. All rights reserved.
+ */
+
+#pragma D option quiet
+
+BEGIN
+{
+	i = (int)'a';
+
+	printf("\n");
+
+	print((char)'a');
+	print((int)-1);
+	print((unsigned int)23);
+	print((short)456);
+	print((unsigned short)789);
+	print((long)1234);
+	print((ulong_t)56789);
+	print((void *)0x1234);
+	print("hello");
+
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/dtrace/test/tst/common/print/tst.primitive.d.out	Thu Nov 03 18:34:26 2011 -0700
@@ -0,0 +1,11 @@
+
+char 'a'
+int 0xffffffff
+unsigned int 0x17
+short 0x1c8
+unsigned short 0x315
+long 0x4d2
+ulong_t 0xddd5
+void * 0x1234
+string "hello"
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/dtrace/test/tst/common/print/tst.struct.d	Thu Nov 03 18:34:26 2011 -0700
@@ -0,0 +1,59 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2011 by Delphix. All rights reserved.
+ */
+
+#pragma D option quiet
+
+typedef struct forward forward_t;
+
+typedef struct foo {
+	int a;
+	void *b;
+	struct {
+		uint64_t alpha;
+		uint64_t beta;
+	} c;
+	ushort_t d;
+	int e;
+	forward_t *f;
+	void (*g)();
+} foo_t;
+
+BEGIN
+{
+	this->s = (foo_t *)alloca(sizeof (foo_t));
+
+	this->s->a = 1;
+	this->s->b = (void *)2;
+	this->s->c.alpha = 3;
+	this->s->c.beta = 4;
+	this->s->d = 5;
+	this->s->e = 6;
+	this->s->f = (void *)7;
+	this->s->g = (void *)8;
+
+	print(*this->s);
+
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/dtrace/test/tst/common/print/tst.struct.d.out	Thu Nov 03 18:34:26 2011 -0700
@@ -0,0 +1,12 @@
+foo_t {
+    int a = 0x1
+    void *b = 0x2
+    struct c = {
+        uint64_t alpha = 0x3
+        uint64_t beta = 0x4
+    }
+    ushort_t d = 0x5
+    int e = 0x6
+    forward_t *f = 0x7
+    int (*)() g = 0x8
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/dtrace/test/tst/common/trace/err.D_TRACE_DYN.bad.d	Thu Nov 03 18:34:26 2011 -0700
@@ -0,0 +1,29 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2011 by Delphix. All rights reserved.
+ */
+
+BEGIN
+{
+	trace(*curpsinfo);
+}
--- a/usr/src/lib/libdtrace/Makefile.com	Thu Nov 03 11:18:52 2011 +0400
+++ b/usr/src/lib/libdtrace/Makefile.com	Thu Nov 03 18:34:26 2011 -0700
@@ -20,7 +20,7 @@
 #
 #
 # Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
-#
+# Copyright (c) 2011 by Delphix. All rights reserved.
 #
 
 LIBRARY = libdtrace.a
@@ -53,6 +53,7 @@
 	dt_pcb.c \
 	dt_pid.c \
 	dt_pragma.c \
+	dt_print.c \
 	dt_printf.c \
 	dt_proc.c \
 	dt_provider.c \
--- a/usr/src/lib/libdtrace/common/dt_cc.c	Thu Nov 03 11:18:52 2011 +0400
+++ b/usr/src/lib/libdtrace/common/dt_cc.c	Thu Nov 03 18:34:26 2011 -0700
@@ -22,6 +22,7 @@
 /*
  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2011, Joyent Inc. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
  */
 
 /*
@@ -678,6 +679,51 @@
 	ap->dtad_kind = DTRACEACT_DIFEXPR;
 }
 
+/*
+ * The print() action behaves identically to trace(), except that it stores the
+ * CTF type of the argument (if present) within the DOF for the DIFEXPR action.
+ * To do this, we set the 'dtsd_strdata' to point to the fully-qualified CTF
+ * type ID for the result of the DIF action.  We use the ID instead of the name
+ * to handles complex types like arrays and function pointers that can't be
+ * resolved by ctf_type_lookup().  This is later processed by
+ * dtrace_dof_create() and turned into a reference into the string table so
+ * that we can get the type information when we process the data after the
+ * fact.
+ */
+static void
+dt_action_print(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp)
+{
+	dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp);
+	dt_node_t *dret;
+	size_t len;
+	dt_module_t *dmp;
+
+	if (dt_node_is_void(dnp->dn_args)) {
+		dnerror(dnp->dn_args, D_PRINT_VOID,
+		    "print( ) may not be applied to a void expression\n");
+	}
+
+	if (dt_node_is_dynamic(dnp->dn_args)) {
+		dnerror(dnp->dn_args, D_PRINT_DYN,
+		    "print( ) may not be applied to a dynamic expression\n");
+	}
+
+	dt_cg(yypcb, dnp->dn_args);
+
+	dret = yypcb->pcb_dret;
+	dmp = dt_module_lookup_by_ctf(dtp, dret->dn_ctfp);
+
+	len = snprintf(NULL, 0, "%s`%d", dmp->dm_name, dret->dn_type) + 1;
+	sdp->dtsd_strdata = dt_alloc(dtp, len);
+	if (sdp->dtsd_strdata == NULL)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM);
+	(void) snprintf(sdp->dtsd_strdata, len, "%s`%d", dmp->dm_name,
+	    dret->dn_type);
+
+	ap->dtad_difo = dt_as(yypcb);
+	ap->dtad_kind = DTRACEACT_DIFEXPR;
+}
+
 static void
 dt_action_tracemem(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp)
 {
@@ -1057,6 +1103,9 @@
 	case DT_ACT_TRACE:
 		dt_action_trace(dtp, dnp->dn_expr, sdp);
 		break;
+	case DT_ACT_PRINT:
+		dt_action_print(dtp, dnp->dn_expr, sdp);
+		break;
 	case DT_ACT_TRACEMEM:
 		dt_action_tracemem(dtp, dnp->dn_expr, sdp);
 		break;
--- a/usr/src/lib/libdtrace/common/dt_consume.c	Thu Nov 03 11:18:52 2011 +0400
+++ b/usr/src/lib/libdtrace/common/dt_consume.c	Thu Nov 03 18:34:26 2011 -0700
@@ -25,6 +25,7 @@
 
 /*
  * Copyright (c) 2011, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
  */
 
 #include <stdlib.h>
@@ -1975,6 +1976,35 @@
 				goto nextrec;
 			}
 
+			/*
+			 * If this is a DIF expression, and the record has a
+			 * format set, this indicates we have a CTF type name
+			 * associated with the data and we should try to print
+			 * it out by type.
+			 */
+			if (act == DTRACEACT_DIFEXPR) {
+				const char *strdata = dt_strdata_lookup(dtp,
+				    rec->dtrd_format);
+				if (strdata != NULL) {
+					n = dtrace_print(dtp, fp, strdata,
+					    addr, rec->dtrd_size);
+
+					/*
+					 * dtrace_print() will return -1 on
+					 * error, or return the number of bytes
+					 * consumed.  It will return 0 if the
+					 * type couldn't be determined, and we
+					 * should fall through to the normal
+					 * trace method.
+					 */
+					if (n < 0)
+						return (-1);
+
+					if (n > 0)
+						goto nextrec;
+				}
+			}
+
 nofmt:
 			if (act == DTRACEACT_PRINTA) {
 				dt_print_aggdata_t pd;
--- a/usr/src/lib/libdtrace/common/dt_dof.c	Thu Nov 03 11:18:52 2011 +0400
+++ b/usr/src/lib/libdtrace/common/dt_dof.c	Thu Nov 03 18:34:26 2011 -0700
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
  */
 
 #include <sys/types.h>
@@ -754,16 +755,23 @@
 				dofa[i].dofa_difo = DOF_SECIDX_NONE;
 
 			/*
-			 * If the first action in a statement has format data,
-			 * add the format string to the global string table.
+			 * If the first action in a statement has string data,
+			 * add the string to the global string table.  This can
+			 * be due either to a printf() format string
+			 * (dtsd_fmtdata) or a print() type string
+			 * (dtsd_strdata).
 			 */
 			if (sdp != NULL && ap == sdp->dtsd_action) {
 				if (sdp->dtsd_fmtdata != NULL) {
 					(void) dtrace_printf_format(dtp,
 					    sdp->dtsd_fmtdata, fmt, maxfmt + 1);
 					strndx = dof_add_string(ddo, fmt);
-				} else
+				} else if (sdp->dtsd_strdata != NULL) {
+					strndx = dof_add_string(ddo,
+					    sdp->dtsd_strdata);
+				} else {
 					strndx = 0; /* use dtad_arg instead */
+				}
 
 				if ((next = dt_list_next(next)) != NULL)
 					sdp = next->ds_desc;
--- a/usr/src/lib/libdtrace/common/dt_errtags.h	Thu Nov 03 11:18:52 2011 +0400
+++ b/usr/src/lib/libdtrace/common/dt_errtags.h	Thu Nov 03 18:34:26 2011 -0700
@@ -26,6 +26,7 @@
 
 /*
  * Copyright (c) 2011, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
  */
 
 #ifndef	_DT_ERRTAGS_H
@@ -189,6 +190,8 @@
 	D_PRINTA_AGGPROTO,		/* printa() aggregation mismatch */
 	D_TRACE_VOID,			/* trace() argument has void type */
 	D_TRACE_DYN,			/* trace() argument has dynamic type */
+	D_PRINT_VOID,			/* print() argument has void type */
+	D_PRINT_DYN,			/* print() argument has dynamic type */
 	D_TRACEMEM_ADDR,		/* tracemem() address bad type */
 	D_TRACEMEM_SIZE,		/* tracemem() size bad type */
 	D_TRACEMEM_ARGS,		/* tracemem() illegal number of args */
--- a/usr/src/lib/libdtrace/common/dt_impl.h	Thu Nov 03 11:18:52 2011 +0400
+++ b/usr/src/lib/libdtrace/common/dt_impl.h	Thu Nov 03 18:34:26 2011 -0700
@@ -26,6 +26,7 @@
 
 /*
  * Copyright (c) 2011, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
  */
 
 #ifndef	_DT_IMPL_H
@@ -240,6 +241,8 @@
 	dtrace_aggdesc_t **dt_aggdesc; /* aggregation descriptions */
 	int dt_maxformat;	/* max format ID */
 	void **dt_formats;	/* pointer to format array */
+	int dt_maxstrdata;	/* max strdata ID */
+	char **dt_strdata;	/* pointer to strdata array */
 	dt_aggregate_t dt_aggregate; /* aggregate */
 	dtrace_bufdesc_t dt_buf; /* staging buffer */
 	struct dt_pfdict *dt_pfdict; /* dictionary of printf conversions */
@@ -417,6 +420,7 @@
 #define	DT_ACT_UMOD		DT_ACT(26)	/* umod() action */
 #define	DT_ACT_UADDR		DT_ACT(27)	/* uaddr() action */
 #define	DT_ACT_SETOPT		DT_ACT(28)	/* setopt() action */
+#define	DT_ACT_PRINT		DT_ACT(29)	/* print() action */
 
 /*
  * Sentinel to tell freopen() to restore the saved stdout.  This must not
@@ -600,6 +604,9 @@
 extern void *dt_format_lookup(dtrace_hdl_t *, int);
 extern void dt_format_destroy(dtrace_hdl_t *);
 
+extern const char *dt_strdata_lookup(dtrace_hdl_t *, int);
+extern void dt_strdata_destroy(dtrace_hdl_t *);
+
 extern int dt_print_quantize(dtrace_hdl_t *, FILE *,
     const void *, size_t, uint64_t);
 extern int dt_print_lquantize(dtrace_hdl_t *, FILE *,
--- a/usr/src/lib/libdtrace/common/dt_map.c	Thu Nov 03 11:18:52 2011 +0400
+++ b/usr/src/lib/libdtrace/common/dt_map.c	Thu Nov 03 18:34:26 2011 -0700
@@ -23,7 +23,9 @@
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
+/*
+ * Copyright (c) 2011 by Delphix. All rights reserved.
+ */
 
 #include <stdlib.h>
 #include <strings.h>
@@ -35,10 +37,81 @@
 #include <dt_printf.h>
 
 static int
+dt_strdata_add(dtrace_hdl_t *dtp, dtrace_recdesc_t *rec, void ***data, int *max)
+{
+	int maxformat;
+	dtrace_fmtdesc_t fmt;
+	void *result;
+
+	if (rec->dtrd_format == 0)
+		return (0);
+
+	if (rec->dtrd_format <= *max &&
+	    (*data)[rec->dtrd_format - 1] != NULL) {
+		return (0);
+	}
+
+	bzero(&fmt, sizeof (fmt));
+	fmt.dtfd_format = rec->dtrd_format;
+	fmt.dtfd_string = NULL;
+	fmt.dtfd_length = 0;
+
+	if (dt_ioctl(dtp, DTRACEIOC_FORMAT, &fmt) == -1)
+		return (dt_set_errno(dtp, errno));
+
+	if ((fmt.dtfd_string = dt_alloc(dtp, fmt.dtfd_length)) == NULL)
+		return (dt_set_errno(dtp, EDT_NOMEM));
+
+	if (dt_ioctl(dtp, DTRACEIOC_FORMAT, &fmt) == -1) {
+		free(fmt.dtfd_string);
+		return (dt_set_errno(dtp, errno));
+	}
+
+	while (rec->dtrd_format > (maxformat = *max)) {
+		int new_max = maxformat ? (maxformat << 1) : 1;
+		size_t nsize = new_max * sizeof (void *);
+		size_t osize = maxformat * sizeof (void *);
+		void **new_data = dt_zalloc(dtp, nsize);
+
+		if (new_data == NULL) {
+			dt_free(dtp, fmt.dtfd_string);
+			return (dt_set_errno(dtp, EDT_NOMEM));
+		}
+
+		bcopy(*data, new_data, osize);
+		free(*data);
+
+		*data = new_data;
+		*max = new_max;
+	}
+
+	switch (rec->dtrd_action) {
+	case DTRACEACT_DIFEXPR:
+		result = fmt.dtfd_string;
+		break;
+	case DTRACEACT_PRINTA:
+		result = dtrace_printa_create(dtp, fmt.dtfd_string);
+		dt_free(dtp, fmt.dtfd_string);
+		break;
+	default:
+		result = dtrace_printf_create(dtp, fmt.dtfd_string);
+		dt_free(dtp, fmt.dtfd_string);
+		break;
+	}
+
+	if (result == NULL)
+		return (-1);
+
+	(*data)[rec->dtrd_format - 1] = result;
+
+	return (0);
+}
+
+static int
 dt_epid_add(dtrace_hdl_t *dtp, dtrace_epid_t id)
 {
 	dtrace_id_t max;
-	int rval, i, maxformat;
+	int rval, i;
 	dtrace_eprobedesc_t *enabled, *nenabled;
 	dtrace_probedesc_t *probe;
 
@@ -124,71 +197,23 @@
 	}
 
 	for (i = 0; i < enabled->dtepd_nrecs; i++) {
-		dtrace_fmtdesc_t fmt;
 		dtrace_recdesc_t *rec = &enabled->dtepd_rec[i];
 
-		if (!DTRACEACT_ISPRINTFLIKE(rec->dtrd_action))
-			continue;
-
-		if (rec->dtrd_format == 0)
-			continue;
-
-		if (rec->dtrd_format <= dtp->dt_maxformat &&
-		    dtp->dt_formats[rec->dtrd_format - 1] != NULL)
-			continue;
-
-		bzero(&fmt, sizeof (fmt));
-		fmt.dtfd_format = rec->dtrd_format;
-		fmt.dtfd_string = NULL;
-		fmt.dtfd_length = 0;
-
-		if (dt_ioctl(dtp, DTRACEIOC_FORMAT, &fmt) == -1) {
-			rval = dt_set_errno(dtp, errno);
-			goto err;
-		}
-
-		if ((fmt.dtfd_string = malloc(fmt.dtfd_length)) == NULL) {
-			rval = dt_set_errno(dtp, EDT_NOMEM);
-			goto err;
+		if (DTRACEACT_ISPRINTFLIKE(rec->dtrd_action)) {
+			if (dt_strdata_add(dtp, rec, &dtp->dt_formats,
+			    &dtp->dt_maxformat) != 0) {
+				rval = -1;
+				goto err;
+			}
+		} else if (rec->dtrd_action == DTRACEACT_DIFEXPR) {
+			if (dt_strdata_add(dtp, rec,
+			    (void ***)&dtp->dt_strdata,
+			    &dtp->dt_maxstrdata) != 0) {
+				rval = -1;
+				goto err;
+			}
 		}
 
-		if (dt_ioctl(dtp, DTRACEIOC_FORMAT, &fmt) == -1) {
-			rval = dt_set_errno(dtp, errno);
-			free(fmt.dtfd_string);
-			goto err;
-		}
-
-		while (rec->dtrd_format > (maxformat = dtp->dt_maxformat)) {
-			int new_max = maxformat ? (maxformat << 1) : 1;
-			size_t nsize = new_max * sizeof (void *);
-			size_t osize = maxformat * sizeof (void *);
-			void **new_formats = malloc(nsize);
-
-			if (new_formats == NULL) {
-				rval = dt_set_errno(dtp, EDT_NOMEM);
-				free(fmt.dtfd_string);
-				goto err;
-			}
-
-			bzero(new_formats, nsize);
-			bcopy(dtp->dt_formats, new_formats, osize);
-			free(dtp->dt_formats);
-
-			dtp->dt_formats = new_formats;
-			dtp->dt_maxformat = new_max;
-		}
-
-		dtp->dt_formats[rec->dtrd_format - 1] =
-		    rec->dtrd_action == DTRACEACT_PRINTA ?
-		    dtrace_printa_create(dtp, fmt.dtfd_string) :
-		    dtrace_printf_create(dtp, fmt.dtfd_string);
-
-		free(fmt.dtfd_string);
-
-		if (dtp->dt_formats[rec->dtrd_format - 1] == NULL) {
-			rval = -1; /* dt_errno is set for us */
-			goto err;
-		}
 	}
 
 	dtp->dt_pdesc[id] = probe;
@@ -424,3 +449,28 @@
 	dtp->dt_aggdesc = NULL;
 	dtp->dt_maxagg = 0;
 }
+
+const char *
+dt_strdata_lookup(dtrace_hdl_t *dtp, int idx)
+{
+	if (idx == 0 || idx > dtp->dt_maxstrdata)
+		return (NULL);
+
+	if (dtp->dt_strdata == NULL)
+		return (NULL);
+
+	return (dtp->dt_strdata[idx - 1]);
+}
+
+void
+dt_strdata_destroy(dtrace_hdl_t *dtp)
+{
+	int i;
+
+	for (i = 0; i < dtp->dt_maxstrdata; i++) {
+		free(dtp->dt_strdata[i]);
+	}
+
+	free(dtp->dt_strdata);
+	dtp->dt_strdata = NULL;
+}
--- a/usr/src/lib/libdtrace/common/dt_open.c	Thu Nov 03 11:18:52 2011 +0400
+++ b/usr/src/lib/libdtrace/common/dt_open.c	Thu Nov 03 18:34:26 2011 -0700
@@ -22,6 +22,7 @@
 /*
  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2011, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
  */
 
 #include <sys/types.h>
@@ -108,8 +109,9 @@
 #define	DT_VERS_1_7_1	DT_VERSION_NUMBER(1, 7, 1)
 #define	DT_VERS_1_8	DT_VERSION_NUMBER(1, 8, 0)
 #define	DT_VERS_1_8_1	DT_VERSION_NUMBER(1, 8, 1)
-#define	DT_VERS_LATEST	DT_VERS_1_8_1
-#define	DT_VERS_STRING	"Sun D 1.8.1"
+#define	DT_VERS_1_9	DT_VERSION_NUMBER(1, 9, 0)
+#define	DT_VERS_LATEST	DT_VERS_1_9
+#define	DT_VERS_STRING	"Sun D 1.9"
 
 const dt_version_t _dtrace_versions[] = {
 	DT_VERS_1_0,	/* D API 1.0.0 (PSARC 2001/466) Solaris 10 FCS */
@@ -129,6 +131,7 @@
 	DT_VERS_1_7_1,	/* D API 1.7.1 */
 	DT_VERS_1_8,	/* D API 1.8 */
 	DT_VERS_1_8_1,	/* D API 1.8.1 */
+	DT_VERS_1_9,	/* D API 1.9 */
 	0
 };
 
@@ -304,6 +307,8 @@
 	&dt_idops_type, "pid_t" },
 { "ppid", DT_IDENT_SCALAR, 0, DIF_VAR_PPID, DT_ATTR_STABCMN, DT_VERS_1_0,
 	&dt_idops_type, "pid_t" },
+{ "print", DT_IDENT_ACTFUNC, 0, DT_ACT_PRINT, DT_ATTR_STABCMN, DT_VERS_1_9,
+	&dt_idops_func, "void(@)" },
 { "printa", DT_IDENT_ACTFUNC, 0, DT_ACT_PRINTA, DT_ATTR_STABCMN, DT_VERS_1_0,
 	&dt_idops_func, "void(@, ...)" },
 { "printf", DT_IDENT_ACTFUNC, 0, DT_ACT_PRINTF, DT_ATTR_STABCMN, DT_VERS_1_0,
@@ -1357,6 +1362,7 @@
 	dt_epid_destroy(dtp);
 	dt_aggid_destroy(dtp);
 	dt_format_destroy(dtp);
+	dt_strdata_destroy(dtp);
 	dt_buffered_destroy(dtp);
 	dt_aggregate_destroy(dtp);
 	free(dtp->dt_buf.dtbd_data);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libdtrace/common/dt_print.c	Thu Nov 03 18:34:26 2011 -0700
@@ -0,0 +1,642 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * Copyright (c) 2011 by Delphix. All rights reserved.
+ */
+
+/*
+ * DTrace print() action
+ *
+ * This file contains the post-processing logic for the print() action.  The
+ * print action behaves identically to trace() in that it generates a
+ * DTRACEACT_DIFEXPR action, but the action argument field refers to a CTF type
+ * string stored in the DOF string table (similar to printf formats).  We
+ * take the result of the trace action and post-process it in the fashion of
+ * MDB's ::print dcmd.
+ *
+ * This implementation differs from MDB's in the following ways:
+ *
+ * 	- We do not expose any options or flags.  The behavior of print() is
+ *	  equivalent to "::print -tn".
+ *
+ * 	- MDB will display "holes" in structures (unused padding between
+ *	  members).
+ *
+ * 	- When printing arrays of structures, MDB will leave a trailing ','
+ *	  after the last element.
+ *
+ *	- MDB will print time_t types as date and time.
+ *
+ *	- MDB will detect when an enum is actually the OR of several flags,
+ *	  and print it out with the constituent flags separated.
+ *
+ *	- For large arrays, MDB will print the first few members and then
+ *	  print a "..." continuation line.
+ *
+ *	- MDB will break and wrap arrays at 80 columns.
+ *
+ *	- MDB prints out floats and doubles by hand, as it must run in kmdb
+ *	  context.  We're able to leverage the printf() format strings,
+ *	  but the result is a slightly different format.
+ */
+
+#include <sys/sysmacros.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <alloca.h>
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <dt_module.h>
+#include <dt_printf.h>
+#include <dt_string.h>
+#include <dt_impl.h>
+
+/* determines whether the given integer CTF encoding is a character */
+#define	CTF_IS_CHAR(e) \
+	(((e).cte_format & (CTF_INT_CHAR | CTF_INT_SIGNED)) == \
+	(CTF_INT_CHAR | CTF_INT_SIGNED) && (e).cte_bits == NBBY)
+/* determines whether the given CTF kind is a struct or union */
+#define	CTF_IS_STRUCTLIKE(k) \
+	((k) == CTF_K_STRUCT || (k) == CTF_K_UNION)
+
+/*
+ * Print structure passed down recursively through printing algorithm.
+ */
+typedef struct dt_printarg {
+	caddr_t		pa_addr;	/* base address of trace data */
+	ctf_file_t	*pa_ctfp;	/* CTF container */
+	int		pa_depth;	/* member depth */
+	int		pa_nest;	/* nested array depth */
+	FILE		*pa_file;	/* output file */
+} dt_printarg_t;
+
+static int dt_print_member(const char *, ctf_id_t, ulong_t, int, void *);
+
+/*
+ * Safe version of ctf_type_name() that will fall back to just "<ctfid>" if it
+ * can't resolve the type.
+ */
+static void
+dt_print_type_name(ctf_file_t *ctfp, ctf_id_t id, char *buf, size_t buflen)
+{
+	if (ctf_type_name(ctfp, id, buf, buflen) == NULL)
+		(void) snprintf(buf, buflen, "<%ld>", id);
+}
+
+/*
+ * Print any necessary trailing braces for structures or unions.  We don't get
+ * invoked when a struct or union ends, so we infer the need to print braces
+ * based on the depth the last time we printed something and the new depth.
+ */
+static void
+dt_print_trailing_braces(dt_printarg_t *pap, int depth)
+{
+	int d;
+
+	for (d = pap->pa_depth; d > depth; d--) {
+		(void) fprintf(pap->pa_file, "%*s}%s",
+		    (d + pap->pa_nest - 1) * 4, "",
+		    d == depth + 1 ? "" : "\n");
+	}
+}
+
+/*
+ * Print the appropriate amount of indentation given the current depth and
+ * array nesting.
+ */
+static void
+dt_print_indent(dt_printarg_t *pap)
+{
+	(void) fprintf(pap->pa_file, "%*s",
+	    (pap->pa_depth + pap->pa_nest) * 4, "");
+}
+
+/*
+ * Print a bitfield.  It's worth noting that the D compiler support for
+ * bitfields is currently broken; printing "D`user_desc_t" (pulled in by the
+ * various D provider files) will produce incorrect results compared to
+ * "genunix`user_desc_t".
+ */
+static void
+print_bitfield(dt_printarg_t *pap, ulong_t off, ctf_encoding_t *ep)
+{
+	FILE *fp = pap->pa_file;
+	caddr_t addr = pap->pa_addr + off / NBBY;
+	uint64_t mask = (1ULL << ep->cte_bits) - 1;
+	uint64_t value = 0;
+	size_t size = (ep->cte_bits + (NBBY - 1)) / NBBY;
+	uint8_t *buf = (uint8_t *)&value;
+	uint8_t shift;
+
+	/*
+	 * On big-endian machines, we need to adjust the buf pointer to refer
+	 * to the lowest 'size' bytes in 'value', and we need to shift based on
+	 * the offset from the end of the data, not the offset of the start.
+	 */
+#ifdef _BIG_ENDIAN
+	buf += sizeof (value) - size;
+	off += ep->cte_bits;
+#endif
+	bcopy(addr, buf, size);
+	shift = off % NBBY;
+
+	/*
+	 * Offsets are counted from opposite ends on little- and
+	 * big-endian machines.
+	 */
+#ifdef _BIG_ENDIAN
+	shift = NBBY - shift;
+#endif
+
+	/*
+	 * If the bits we want do not begin on a byte boundary, shift the data
+	 * right so that the value is in the lowest 'cte_bits' of 'value'.
+	 */
+	if (off % NBBY != 0)
+		value >>= shift;
+	value &= mask;
+
+	(void) fprintf(fp, "%#llx", (u_longlong_t)value);
+}
+
+/*
+ * Dump the contents of memory as a fixed-size integer in hex.
+ */
+static void
+dt_print_hex(FILE *fp, caddr_t addr, size_t size)
+{
+	switch (size) {
+	case sizeof (uint8_t):
+		(void) fprintf(fp, "%#x", *(uint8_t *)addr);
+		break;
+	case sizeof (uint16_t):
+		(void) fprintf(fp, "%#x", *(uint16_t *)addr);
+		break;
+	case sizeof (uint32_t):
+		(void) fprintf(fp, "%#x", *(uint32_t *)addr);
+		break;
+	case sizeof (uint64_t):
+		(void) fprintf(fp, "%#llx",
+		    (unsigned long long)*(uint64_t *)addr);
+		break;
+	default:
+		(void) fprintf(fp, "<invalid size %u>", (uint_t)size);
+	}
+}
+
+/*
+ * Print an integer type.  Before dumping the contents via dt_print_hex(), we
+ * first check the encoding to see if it's part of a bitfield or a character.
+ */
+static void
+dt_print_int(ctf_id_t base, ulong_t off, dt_printarg_t *pap)
+{
+	FILE *fp = pap->pa_file;
+	ctf_file_t *ctfp = pap->pa_ctfp;
+	ctf_encoding_t e;
+	size_t size;
+	caddr_t addr = pap->pa_addr + off / NBBY;
+
+	if (ctf_type_encoding(ctfp, base, &e) == CTF_ERR) {
+		(void) fprintf(fp, "<unknown encoding>");
+		return;
+	}
+
+	/*
+	 * This comes from MDB - it's not clear under what circumstances this
+	 * would be found.
+	 */
+	if (e.cte_format & CTF_INT_VARARGS) {
+		(void) fprintf(fp, "...");
+		return;
+	}
+
+	/*
+	 * We print this as a bitfield if the bit encoding indicates it's not
+	 * an even power of two byte size, or is larger than 8 bytes.
+	 */
+	size = e.cte_bits / NBBY;
+	if (size > 8 || (e.cte_bits % NBBY) != 0 || (size & (size - 1)) != 0) {
+		print_bitfield(pap, off, &e);
+		return;
+	}
+
+	/*
+	 * If this is a character, print it out as such.
+	 */
+	if (CTF_IS_CHAR(e)) {
+		char c = *(char *)addr;
+		if (isprint(c))
+			(void) fprintf(fp, "'%c'", c);
+		else if (c == 0)
+			(void) fprintf(fp, "'\\0'");
+		else
+			(void) fprintf(fp, "'\\%03o'", c);
+		return;
+	}
+
+	dt_print_hex(fp, addr, size);
+}
+
+/*
+ * Print a floating point (float, double, long double) value.
+ */
+/* ARGSUSED */
+static void
+dt_print_float(ctf_id_t base, ulong_t off, dt_printarg_t *pap)
+{
+	FILE *fp = pap->pa_file;
+	ctf_file_t *ctfp = pap->pa_ctfp;
+	ctf_encoding_t e;
+	caddr_t addr = pap->pa_addr + off / NBBY;
+
+	if (ctf_type_encoding(ctfp, base, &e) == 0) {
+		if (e.cte_format == CTF_FP_SINGLE &&
+		    e.cte_bits == sizeof (float) * NBBY) {
+			(void) fprintf(fp, "%+.7e", *((float *)addr));
+		} else if (e.cte_format == CTF_FP_DOUBLE &&
+		    e.cte_bits == sizeof (double) * NBBY) {
+			(void) fprintf(fp, "%+.7e", *((double *)addr));
+		} else if (e.cte_format == CTF_FP_LDOUBLE &&
+		    e.cte_bits == sizeof (long double) * NBBY) {
+			(void) fprintf(fp, "%+.16LE", *((long double *)addr));
+		} else {
+			(void) fprintf(fp, "<unknown encoding>");
+		}
+	}
+}
+
+/*
+ * A pointer is printed as a fixed-size integer.  This is used both for
+ * pointers and functions.
+ */
+static void
+dt_print_ptr(ctf_id_t base, ulong_t off, dt_printarg_t *pap)
+{
+	FILE *fp = pap->pa_file;
+	ctf_file_t *ctfp = pap->pa_ctfp;
+	caddr_t addr = pap->pa_addr + off / NBBY;
+	size_t size = ctf_type_size(ctfp, base);
+
+	dt_print_hex(fp, addr, size);
+}
+
+/*
+ * Print out an array.  This is somewhat complex, as we must manually visit
+ * each member, and recursively invoke ctf_type_visit() for each member.  If
+ * the members are non-structs, then we print them out directly:
+ *
+ * 	[ 0x14, 0x2e, 0 ]
+ *
+ * If they are structs, then we print out the necessary leading and trailing
+ * braces, to end up with:
+ *
+ *	[
+ *	    type {
+ *	    ...
+ *	    },
+ *	    type {
+ *	    ...
+ *	    }
+ *	]
+ *
+ * We also use a heuristic to detect whether the array looks like a character
+ * array.  If the encoding indicates it's a character, and we have all
+ * printable characters followed by a null byte, then we display it as a
+ * string:
+ *
+ *	[ "string" ]
+ */
+static void
+dt_print_array(ctf_id_t base, ulong_t off, dt_printarg_t *pap)
+{
+	FILE *fp = pap->pa_file;
+	ctf_file_t *ctfp = pap->pa_ctfp;
+	caddr_t addr = pap->pa_addr + off / NBBY;
+	ctf_arinfo_t car;
+	ssize_t eltsize;
+	ctf_encoding_t e;
+	int i;
+	boolean_t isstring;
+	int kind;
+	ctf_id_t rtype;
+
+	if (ctf_array_info(ctfp, base, &car) == CTF_ERR) {
+		(void) fprintf(fp, "0x%p", (void *)addr);
+		return;
+	}
+
+	if ((eltsize = ctf_type_size(ctfp, car.ctr_contents)) < 0 ||
+	    (rtype = ctf_type_resolve(ctfp, car.ctr_contents)) == CTF_ERR ||
+	    (kind = ctf_type_kind(ctfp, rtype)) == CTF_ERR) {
+		(void) fprintf(fp, "<invalid type %lu>", car.ctr_contents);
+		return;
+	}
+
+	/* see if this looks like a string */
+	isstring = B_FALSE;
+	if (kind == CTF_K_INTEGER &&
+	    ctf_type_encoding(ctfp, rtype, &e) != CTF_ERR && CTF_IS_CHAR(e)) {
+		char c;
+		for (i = 0; i < car.ctr_nelems; i++) {
+			c = *((char *)addr + eltsize * i);
+			if (!isprint(c) || c == '\0')
+				break;
+		}
+
+		if (i != car.ctr_nelems && c == '\0')
+			isstring = B_TRUE;
+	}
+
+	/*
+	 * As a slight aesthetic optimization, if we are a top-level type, then
+	 * don't bother printing out the brackets.  This lets print("foo") look
+	 * like:
+	 *
+	 * 	string "foo"
+	 *
+	 * As D will internally represent this as a char[256] array.
+	 */
+	if (!isstring || pap->pa_depth != 0)
+		(void) fprintf(fp, "[ ");
+
+	if (isstring)
+		(void) fprintf(fp, "\"");
+
+	for (i = 0; i < car.ctr_nelems; i++) {
+		if (isstring) {
+			char c = *((char *)addr + eltsize * i);
+			if (c == '\0')
+				break;
+			(void) fprintf(fp, "%c", c);
+		} else {
+			/*
+			 * Recursively invoke ctf_type_visit() on each member.
+			 * We setup a new printarg struct with 'pa_nest' set to
+			 * indicate that we are within a nested array.
+			 */
+			dt_printarg_t pa = *pap;
+			pa.pa_nest += pap->pa_depth + 1;
+			pa.pa_depth = 0;
+			pa.pa_addr = addr + eltsize * i;
+			(void) ctf_type_visit(ctfp, car.ctr_contents,
+			    dt_print_member, &pa);
+
+			dt_print_trailing_braces(&pa, 0);
+			if (i != car.ctr_nelems - 1)
+				(void) fprintf(fp, ", ");
+			else if (CTF_IS_STRUCTLIKE(kind))
+				(void) fprintf(fp, "\n");
+		}
+	}
+
+	if (isstring)
+		(void) fprintf(fp, "\"");
+
+	if (!isstring || pap->pa_depth != 0) {
+		if (CTF_IS_STRUCTLIKE(kind))
+			dt_print_indent(pap);
+		else
+			(void) fprintf(fp, " ");
+		(void) fprintf(fp, "]");
+	}
+}
+
+/*
+ * This isued by both structs and unions to print the leading brace.
+ */
+/* ARGSUSED */
+static void
+dt_print_structlike(ctf_id_t id, ulong_t off, dt_printarg_t *pap)
+{
+	(void) fprintf(pap->pa_file, "{");
+}
+
+/*
+ * For enums, we try to print the enum name, and fall back to the value if it
+ * can't be determined.  We do not do any fancy flag processing like mdb.
+ */
+/* ARGSUSED */
+static void
+dt_print_enum(ctf_id_t base, ulong_t off, dt_printarg_t *pap)
+{
+	FILE *fp = pap->pa_file;
+	ctf_file_t *ctfp = pap->pa_ctfp;
+	const char *ename;
+	int value = 0;
+
+	if ((ename = ctf_enum_name(ctfp, base, value)) != NULL)
+		(void) fprintf(fp, "%s", ename);
+	else
+		(void) fprintf(fp, "%d", value);
+}
+
+/*
+ * Forward declaration.  There's not much to do here without the complete
+ * type information, so just print out this fact and drive on.
+ */
+/* ARGSUSED */
+static void
+dt_print_tag(ctf_id_t base, ulong_t off, dt_printarg_t *pap)
+{
+	(void) fprintf(pap->pa_file, "<forward decl>");
+}
+
+typedef void dt_printarg_f(ctf_id_t, ulong_t, dt_printarg_t *);
+
+static dt_printarg_f *const dt_printfuncs[] = {
+	dt_print_int,		/* CTF_K_INTEGER */
+	dt_print_float,		/* CTF_K_FLOAT */
+	dt_print_ptr,		/* CTF_K_POINTER */
+	dt_print_array,		/* CTF_K_ARRAY */
+	dt_print_ptr,		/* CTF_K_FUNCTION */
+	dt_print_structlike,	/* CTF_K_STRUCT */
+	dt_print_structlike,	/* CTF_K_UNION */
+	dt_print_enum,		/* CTF_K_ENUM */
+	dt_print_tag		/* CTF_K_FORWARD */
+};
+
+/*
+ * Print one member of a structure.  This callback is invoked from
+ * ctf_type_visit() recursively.
+ */
+static int
+dt_print_member(const char *name, ctf_id_t id, ulong_t off, int depth,
+    void *data)
+{
+	char type[DT_TYPE_NAMELEN];
+	int kind;
+	dt_printarg_t *pap = data;
+	FILE *fp = pap->pa_file;
+	ctf_file_t *ctfp = pap->pa_ctfp;
+	boolean_t arraymember;
+	boolean_t brief;
+	ctf_encoding_t e;
+	ctf_id_t rtype;
+
+	dt_print_trailing_braces(pap, depth);
+	/*
+	 * dt_print_trailing_braces() doesn't include the trailing newline; add
+	 * it here if necessary.
+	 */
+	if (depth < pap->pa_depth)
+		(void) fprintf(fp, "\n");
+	pap->pa_depth = depth;
+
+	if ((rtype = ctf_type_resolve(ctfp, id)) == CTF_ERR ||
+	    (kind = ctf_type_kind(ctfp, rtype)) == CTF_ERR ||
+	    kind < CTF_K_INTEGER || kind > CTF_K_FORWARD) {
+		dt_print_indent(pap);
+		(void) fprintf(fp, "%s = <invalid type %lu>", name, id);
+		return (0);
+	}
+
+	dt_print_type_name(ctfp, id, type, sizeof (type));
+
+	arraymember = (pap->pa_nest != 0 && depth == 0);
+	brief = (arraymember && !CTF_IS_STRUCTLIKE(kind));
+
+	if (!brief) {
+		/*
+		 * If this is a direct array member and a struct (otherwise
+		 * brief would be true), then print a trailing newline, as the
+		 * array printing code doesn't include it because it might be a
+		 * simple type.
+		 */
+		if (arraymember)
+			(void) fprintf(fp, "\n");
+		dt_print_indent(pap);
+
+		/* always print the type */
+		(void) fprintf(fp, "%s", type);
+		if (name[0] != '\0') {
+			/*
+			 * For aesthetics, we don't include a space between the
+			 * type name and member name if the type is a pointer.
+			 * This will give us "void *foo =" instead of "void *
+			 * foo =".  Unions also have the odd behavior that the
+			 * type name is returned as "union ", with a trailing
+			 * space, so we also avoid printing a space if the type
+			 * name already ends with a space.
+			 */
+			if (type[strlen(type) - 1] != '*' &&
+			    type[strlen(type) -1] != ' ') {
+				(void) fprintf(fp, " ");
+			}
+			(void) fprintf(fp, "%s", name);
+
+			/*
+			 * If this looks like a bitfield, or is an integer not
+			 * aligned on a byte boundary, print the number of
+			 * bits after the name.
+			 */
+			if (kind == CTF_K_INTEGER &&
+			    ctf_type_encoding(ctfp, id, &e) == 0) {
+				ulong_t bits = e.cte_bits;
+				ulong_t size = bits / NBBY;
+
+				if (bits % NBBY != 0 ||
+				    off % NBBY != 0 ||
+				    size > 8 ||
+				    size != ctf_type_size(ctfp, id)) {
+					(void) fprintf(fp, " :%lu", bits);
+				}
+			}
+
+			(void) fprintf(fp, " =");
+		}
+		(void) fprintf(fp, " ");
+	}
+
+	dt_printfuncs[kind - 1](rtype, off, pap);
+
+	/* direct simple array members are not separated by newlines */
+	if (!brief)
+		(void) fprintf(fp, "\n");
+
+	return (0);
+}
+
+/*
+ * Main print function invoked by dt_consume_cpu().
+ */
+int
+dtrace_print(dtrace_hdl_t *dtp, FILE *fp, const char *typename,
+    caddr_t addr, size_t len)
+{
+	const char *s;
+	char *object;
+	dt_printarg_t pa;
+	ctf_id_t id;
+	dt_module_t *dmp;
+
+	/*
+	 * Split the fully-qualified type ID (module`id).  This should
+	 * always be the format, but if for some reason we don't find the
+	 * expected value, return 0 to fall back to the generic trace()
+	 * behavior.
+	 */
+	for (s = typename; *s != '\0' && *s != '`'; s++)
+		;
+
+	if (*s != '`')
+		return (0);
+
+	object = alloca(s - typename + 1);
+	bcopy(typename, object, s - typename);
+	object[s - typename] = '\0';
+	id = atoi(s + 1);
+
+	/*
+	 * Try to get the CTF kind for this id.  If something has gone horribly
+	 * wrong and we can't resolve the ID, bail out and let trace() do the
+	 * work.
+	 */
+	dmp = dt_module_lookup_by_name(dtp, object);
+	if (dmp == NULL || ctf_type_kind(dt_module_getctf(dtp, dmp),
+	    id) == CTF_ERR) {
+		return (0);
+	}
+
+	/* setup the print structure and kick off the main print routine */
+	pa.pa_addr = addr;
+	pa.pa_ctfp = dt_module_getctf(dtp, dmp);
+	pa.pa_nest = 0;
+	pa.pa_depth = 0;
+	pa.pa_file = fp;
+	(void) ctf_type_visit(pa.pa_ctfp, id, dt_print_member, &pa);
+
+	dt_print_trailing_braces(&pa, 0);
+
+	return (len);
+}
--- a/usr/src/lib/libdtrace/common/dt_program.c	Thu Nov 03 11:18:52 2011 +0400
+++ b/usr/src/lib/libdtrace/common/dt_program.c	Thu Nov 03 18:34:26 2011 -0700
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
  */
 
 #include <unistd.h>
@@ -347,6 +348,7 @@
 
 	if (sdp->dtsd_fmtdata != NULL)
 		dt_printf_destroy(sdp->dtsd_fmtdata);
+	dt_free(dtp, sdp->dtsd_strdata);
 
 	dt_ecbdesc_release(dtp, sdp->dtsd_ecbdesc);
 	dt_free(dtp, sdp);
--- a/usr/src/lib/libdtrace/common/dtrace.h	Thu Nov 03 11:18:52 2011 +0400
+++ b/usr/src/lib/libdtrace/common/dtrace.h	Thu Nov 03 18:34:26 2011 -0700
@@ -150,6 +150,7 @@
 	dtrace_actdesc_t *dtsd_action_last;	/* last action in action list */
 	void *dtsd_aggdata;			/* aggregation data */
 	void *dtsd_fmtdata;			/* type-specific output data */
+	void *dtsd_strdata;			/* type-specific string data */
 	void (*dtsd_callback)();		/* callback function for EPID */
 	void *dtsd_data;			/* callback data pointer */
 	dtrace_attribute_t dtsd_descattr;	/* probedesc attributes */
@@ -242,6 +243,18 @@
     const void *, size_t);
 
 /*
+ * Type-specific output printing
+ *
+ * The print() action will associate a string data record that is actually the
+ * fully-qualified type name of the data traced by the DIFEXPR action.  This is
+ * stored in the same 'format' record from the kernel, but we know by virtue of
+ * the fact that the action is still DIFEXPR that it is actually a reference to
+ * plain string data.
+ */
+extern int dtrace_print(dtrace_hdl_t *, FILE *, const char *,
+    caddr_t, size_t);
+
+/*
  * DTrace Work Interface
  */
 typedef enum {
--- a/usr/src/pkg/manifests/system-dtrace-tests.mf	Thu Nov 03 11:18:52 2011 +0400
+++ b/usr/src/pkg/manifests/system-dtrace-tests.mf	Thu Nov 03 18:34:26 2011 -0700
@@ -21,6 +21,7 @@
 
 #
 # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011 by Delphix. All rights reserved.
 #
 
 set name=pkg.fmri value=pkg:/system/dtrace/tests@$(PKGVERS)
@@ -83,6 +84,7 @@
 dir path=opt/SUNWdtrt/tst/common/pragma
 dir path=opt/SUNWdtrt/tst/common/predicates
 dir path=opt/SUNWdtrt/tst/common/preprocessor
+dir path=opt/SUNWdtrt/tst/common/print
 dir path=opt/SUNWdtrt/tst/common/printa
 dir path=opt/SUNWdtrt/tst/common/printf
 dir path=opt/SUNWdtrt/tst/common/privs
@@ -1220,6 +1222,17 @@
 file path=opt/SUNWdtrt/tst/common/preprocessor/tst.preexpifelse.d.out \
     mode=0444
 file path=opt/SUNWdtrt/tst/common/preprocessor/tst.withinprobe.d mode=0444
+file path=opt/SUNWdtrt/tst/common/print/err.D_PRINT_DYN.bad.d mode=0444
+file path=opt/SUNWdtrt/tst/common/print/err.D_PRINT_VOID.bad.d mode=0444
+file path=opt/SUNWdtrt/tst/common/print/err.D_PROTO_LEN.bad.d mode=0444
+file path=opt/SUNWdtrt/tst/common/print/tst.array.d mode=0444
+file path=opt/SUNWdtrt/tst/common/print/tst.array.d.out mode=0444
+file path=opt/SUNWdtrt/tst/common/print/tst.bitfield.d mode=0444
+file path=opt/SUNWdtrt/tst/common/print/tst.bitfield.d.out mode=0444
+file path=opt/SUNWdtrt/tst/common/print/tst.primitive.d mode=0444
+file path=opt/SUNWdtrt/tst/common/print/tst.primitive.d.out mode=0444
+file path=opt/SUNWdtrt/tst/common/print/tst.struct.d mode=0444
+file path=opt/SUNWdtrt/tst/common/print/tst.struct.d.out mode=0444
 file path=opt/SUNWdtrt/tst/common/printa/err.D_PRINTA_AGGARG.badagg.d \
     mode=0444
 file path=opt/SUNWdtrt/tst/common/printa/err.D_PRINTA_AGGARG.badfmt.d \
@@ -1753,6 +1766,7 @@
 file path=opt/SUNWdtrt/tst/common/tick-n/tst.tickusec.d mode=0444
 file path=opt/SUNWdtrt/tst/common/tick-n/tst.tickusec.d.out mode=0444
 file path=opt/SUNWdtrt/tst/common/trace/err.D_PROTO_LEN.bad.d mode=0444
+file path=opt/SUNWdtrt/tst/common/trace/err.D_TRACE_DYN.bad.d mode=0444
 file path=opt/SUNWdtrt/tst/common/trace/err.D_TRACE_VOID.bad.d mode=0444
 file path=opt/SUNWdtrt/tst/common/trace/tst.misc.d mode=0444
 file path=opt/SUNWdtrt/tst/common/trace/tst.qstring.d mode=0444
--- a/usr/src/uts/common/dtrace/dtrace.c	Thu Nov 03 11:18:52 2011 +0400
+++ b/usr/src/uts/common/dtrace/dtrace.c	Thu Nov 03 18:34:26 2011 -0700
@@ -9820,12 +9820,14 @@
 		case DTRACEACT_PRINTA:
 		case DTRACEACT_SYSTEM:
 		case DTRACEACT_FREOPEN:
+		case DTRACEACT_DIFEXPR:
 			/*
 			 * We know that our arg is a string -- turn it into a
 			 * format.
 			 */
 			if (arg == NULL) {
-				ASSERT(desc->dtad_kind == DTRACEACT_PRINTA);
+				ASSERT(desc->dtad_kind == DTRACEACT_PRINTA ||
+				    desc->dtad_kind == DTRACEACT_DIFEXPR);
 				format = 0;
 			} else {
 				ASSERT(arg != NULL);
@@ -9836,7 +9838,6 @@
 
 			/*FALLTHROUGH*/
 		case DTRACEACT_LIBACT:
-		case DTRACEACT_DIFEXPR:
 		case DTRACEACT_TRACEMEM:
 		case DTRACEACT_TRACEMEM_DYNSIZE:
 			if (dp == NULL)
@@ -11859,15 +11860,20 @@
 		    (uintptr_t)sec->dofs_offset + offs);
 		kind = (dtrace_actkind_t)desc->dofa_kind;
 
-		if (DTRACEACT_ISPRINTFLIKE(kind) &&
+		if ((DTRACEACT_ISPRINTFLIKE(kind) &&
 		    (kind != DTRACEACT_PRINTA ||
+		    desc->dofa_strtab != DOF_SECIDX_NONE)) ||
+		    (kind == DTRACEACT_DIFEXPR &&
 		    desc->dofa_strtab != DOF_SECIDX_NONE)) {
 			dof_sec_t *strtab;
 			char *str, *fmt;
 			uint64_t i;
 
 			/*
-			 * printf()-like actions must have a format string.
+			 * The argument to these actions is an index into the
+			 * DOF string table.  For printf()-like actions, this
+			 * is the format string.  For print(), this is the
+			 * CTF type of the expression result.
 			 */
 			if ((strtab = dtrace_dof_sect(dof,
 			    DOF_SECT_STRTAB, desc->dofa_strtab)) == NULL)