diff --git a/Makefile.inc1 b/Makefile.inc1
--- a/Makefile.inc1
+++ b/Makefile.inc1
@@ -3055,6 +3055,7 @@
${_cddl_lib_libctf} ${_cddl_lib_libzfsbootenv} \
lib/libufs \
lib/libutil lib/libpjdlog ${_lib_libypclnt} lib/libz lib/msun \
+ lib/libxo \
${_secure_lib_libcrypto} ${_secure_lib_libssl} \
${_lib_libldns} ${_secure_lib_libssh}
@@ -3111,6 +3112,7 @@
.endfor
lib/libtacplus__L: lib/libmd__L lib/libpam/libpam__L
+lib/libxo__L: lib/libutil__L
.if ${MK_CDDL} != "no"
_cddl_lib_libumem= cddl/lib/libumem
@@ -3151,6 +3153,7 @@
_cddl_lib_libctf= cddl/lib/libctf
_cddl_lib= cddl/lib
cddl/lib/libctf__L: lib/libz__L cddl/lib/libspl__L
+cddl/lib/libdtrace__L: lib/libxo__L
.endif
# cddl/lib/libdtrace requires lib/libproc and lib/librtld_db
_prebuild_libs+= lib/libprocstat lib/libproc lib/librtld_db
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/dtrace.1 b/cddl/contrib/opensolaris/cmd/dtrace/dtrace.1
--- a/cddl/contrib/opensolaris/cmd/dtrace/dtrace.1
+++ b/cddl/contrib/opensolaris/cmd/dtrace/dtrace.1
@@ -20,7 +20,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd February 24, 2023
+.Dd September 8, 2023
.Dt DTRACE 1
.Os
.Sh NAME
@@ -29,7 +29,8 @@
.Sh SYNOPSIS
.Nm
.Op Fl 32 | Fl 64
-.Op Fl aACdeFGhHlqSvVwZ
+.Op Fl aACdeFGhHlOqSvVwZ
+.Op Fl -libxo
.Op Fl b Ar bufsz
.Op Fl c Ar cmd
.Op Fl D Ar name Op Ns = Ns value
@@ -357,6 +358,12 @@
The specified
.Ar path
is added after the default library search path.
+.
+.It Fl -libxo
+Generate output via
+.Xr libxo 3 .
+This option is the same as specifying
+.Sy oformat .
.It Fl m Oo Ar provider : Oc Ar module Oo Oo Ar predicate Oc Ar action Oc
Specify module name to trace or list
.Fl ( l
@@ -400,6 +407,14 @@
More than one
.Fl n
option can be specified on the command line at a time.
+.It Fl O
+This option causes
+.Nm
+to print all the aggregations upon exiting if
+.Sy oformat
+or
+.Fl -libxo
+are specified.
.It Fl o Ar output
Specify the
.Ar output
@@ -633,25 +648,26 @@
Number of speculations.
.It Sy nolibs
Do not load D system libraries.
-.It Sy quiet
-Set quiet mode.
-Same as the
-.Fl q
-flag.
-.It Sy specsize Ns = Ns Ar size
-Size of the speculation buffer.
-.It Sy strsize Ns = Ns Ar size
-Maximum size of strings.
-.It Sy stackframes Ns = Ns Ar scalar
-Maximum number of kernelspace stack frames to unwind when executing the
-.Fn stack
-action.
-.It Sy stackindent Ns = Ns Ar scalar
-Number of whitespace characters to use when indenting
-.Fn stack
-and
-.Fn ustack
-output.
+.It Sy oformat Ns = Ns Ar format
+Specify the format to use for output.
+Setting
+.Sy oformat
+to text makes
+.Nm
+use regular human-readable output.
+The options passed to
+.Sy oformat
+are directly forwarded to
+.Xr libxo 3 .
+Some of the supported formatters include json, xml and html.
+Note that specifying this option will cause
+.Nm
+to not produce any output unless printing functions are explicitly called
+or the
+.Fl O
+flag is specified.
+For more information see
+.Sx STRUCTURED OUTPUT .
.It Sy statusrate Ns = Ns Ar time
Rate of status checking.
.It Sy switchrate Ns = Ns Ar time
@@ -778,6 +794,343 @@
.Fl i
options) contain descriptions that do not match any known probes.
.El
+.Sh STRUCTURED OUTPUT
+.Nm
+supports structured output using
+.Xr libxo 3 .
+The structure of the output is always going to have a top-level object called
+"dtrace", followed by a list of objects "probes".
+Each of the probe objects is going to have a timestamp which is generated at
+output time rather than probe firing time, the CPU identifier which the probe
+was executed on and its full specification:
+.Bd -literal
+{
+ "dtrace": {
+ "probes": [
+ {
+ "timestamp": ...,
+ "cpu": ...,
+ "id": ...,
+ "provider": ...,
+ "module": ...,
+ "function": ...,
+ "name": ...,
+ "output": [
+ ... (script specific)
+ ]
+ }
+ ]
+ }
+}
+
+
+
+
+ ...
+ ...
+ ...
+ ...
+ ...
+ ...
+ ...
+
+
+
+.Ed
+.Pp
+It is also possible to see XML output take the following form if some
+of the fields are empty (in this example, module and function values
+are absent):
+.Bd -literal
+
+
+
+ ...
+
+
+ ...
+
+
+
+.Ed
+.Pp
+Similarly,
+.Sy oformat
+can be used to generate HTML:
+.Bd -literal
+
+
...
+
+
...
+
+
...
+
+
...
+
+
...
+
+
...
+
+
...
+
...
+
+.Ed
+.Pp
+Unlike JSON and XML, the "output" array is not present.
+Instead, data is simply formatted into a div of class "data" and a
+data-tag is associated with each of the keys.
+.Pp
+The "output" array's contents depend on the probes' actions and are
+explained below.
+The examples here are presented in JSON form as opposed to XML or HTML,
+however the conversion explained above applies for the rest of the
+output too.
+.Pp
+Any scalar output, such as output produced by the
+.Fn trace
+action is of form:
+.Bd -literal
+{
+ "value": ...
+}
+.Ed
+.Pp
+The
+.Fn printf
+action begins with an object containing the evaluated message of the
+.Fn printf
+action.
+Every consecutive object contains the value of each of the arguments to
+.Fn printf
+in its raw form as if the
+.Fn trace
+action was used instead.
+A
+.Fn printf
+statement which contains no arguments other than the message will only have
+one object following the message object and its value will always be 0.
+This is an artefact of the implementation and can safely be ignored.
+.Bd -literal
+{
+ "message": "... 2023 Sep 7 16:49:02, ..."
+},
+{
+ "value": 1694105342633402400
+},
+{
+ ...
+}
+.Ed
+.Pp
+Scalar aggregations are aggregations which produce a single value for given
+keys.
+These aggregations include
+.Fn count ,
+.Fn min ,
+.Fn max ,
+.Fn stddev
+and
+.Fn sum .
+Each one of them is represented with the key containing their name being
+present.
+For example, the output of a
+.Fn stddev
+aggregation will contain a key "stddev" inside an "aggregation-data" object:
+.Bd -literal
+{
+ "aggregation-data": [
+ {
+ "keys": [
+ ...
+ ],
+ "stddev": ...
+ }
+ ],
+ "aggregation-name": ...
+}
+.Ed
+.Pp
+Aggregations that produce histogram output have to be formatted
+slightly differently.
+The "keys" field remains consistent across all aggregations, however
+.Fn quantize ,
+.Fn lquantize
+and
+.Fn llquantize
+need to be treated differently.
+.Sy oformat
+will create a new array of objects called "buckets".
+Each of the objects contains a "value" and a "count" field which are
+the left-hand side and the right-hand side of human-readable
+.Nm
+output respectively.
+The full object has the following format:
+.Bd -literal
+{
+ "aggregation-data": [
+ ...
+ {
+ "keys": [
+ ...
+ ],
+ "buckets": [
+ {
+ "value": 32,
+ "count": 0
+ },
+ {
+ "value": 64,
+ "count": 17
+ },
+ ...
+ ],
+ },
+ ...
+ ]
+ "aggregation-name": ...
+}
+.Ed
+.Pp
+Similar to scalar aggregations, named scalar actions such as
+.Fn mod ,
+.Fn umod ,
+.Fn usym ,
+.Fn tracemem
+and
+.Fn printm
+will output an object with the key being equal to the
+name of the action.
+For example,
+.Fn printm
+output would produce the following object:
+.Bd -literal
+{
+ "printm": "0x4054171100"
+}
+.Ed
+.Pp
+.Fn sym
+is slightly different.
+While it will create a "sym" field which contains its value, in some cases
+it will also create additional fields "object", "name" and "offset":
+.Bd -literal
+{
+ "sym": "kernel`prison0",
+ "object": "kernel",
+ "name": "prison0"
+}
+
+{
+ "sym": "0xfffffe00c18d2000",
+ "offset": "0xfffffe00c18d2000"
+}
+.Ed
+.Pp
+.Fn stack
+and
+.Fn ustack
+actions unroll each of the stack frames into its own object in an array.
+The only real difference between them is that the
+.Fn stack
+action will produce a list called "stack-frames" while
+.Fn ustack
+will produce one called "ustack-frames".
+The following is an example of their
+.Sy oformat
+output:
+.Bd -literal
+{
+ "stack-frames": [
+ {
+ "symbol": "dtrace.ko`dtrace_dof_create+0x35",
+ "module": "dtrace.ko",
+ "name": "dtrace_dof_create",
+ "offset": "0x35"
+ },
+ {
+ "symbol": "dtrace.ko`dtrace_ioctl+0x81c",
+ "module": "dtrace.ko",
+ "name": "dtrace_ioctl",
+ "offset": "0x81c"
+ },
+ ...
+ ]
+}
+
+{
+ "ustack-frames": [
+ {
+ "symbol": "libc.so.7`ioctl+0xa",
+ "module": "libc.so.7",
+ "name": "ioctl",
+ "offset": "0xa"
+ },
+ {
+ "symbol": "libdtrace.so.2`dtrace_go+0xf3",
+ "module": "libdtrace.so.2",
+ "name": "dtrace_go",
+ "offset": "0xf3"
+ },
+ ...
+ ]
+}
+.Ed
+.Pp
+The
+.Fn print
+action produces a "type" list in the following form:
+.Bd -literal
+{
+ "type": [
+ {
+ "object-name": "kernel",
+ "name": "struct thread",
+ "ctfid": 2372
+ },
+ {
+ "member-name": "td_lock",
+ "name": "struct mtx *volatile",
+ "ctfid": 2035,
+ "value": "0xffffffff82158440"
+ },
+ ...
+}
+.Ed
+.Pp
+If the type is invalid, a "warning" object will be produced containing the
+diagnostic message as well as two possible optional fields: "type-identifier"
+which contains the CTF identifier of the type and "size" containing the size
+of an integer, enum or float.
+The fields generated will depend on the kind of error that was encountered
+while processing the trace data.
+.Pp
+Finally,
+.Sy oformat
+provides a special pseudo-probe to represent drops.
+As
+.Nm
+polls for various kinds of drops
+.Sy oformat
+will produce output similar to the following in order to represent drops:
+.Bd -literal
+{
+ "cpu": -1,
+ "id": -1,
+ "provider": "dtrace",
+ "module": "INTERNAL",
+ "function": "INTERNAL",
+ "name": "DROP",
+ "timestamp": ...,
+ "count": ...,
+ "total": ...,
+ "kind": 2,
+ "msg": "... dynamic variable drops\n"
+}
+.Ed
.Sh OPERANDS
You can specify zero or more additional arguments on the
.Nm
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/dtrace.c b/cddl/contrib/opensolaris/cmd/dtrace/dtrace.c
--- a/cddl/contrib/opensolaris/cmd/dtrace/dtrace.c
+++ b/cddl/contrib/opensolaris/cmd/dtrace/dtrace.c
@@ -26,6 +26,7 @@
/*
* Copyright (c) 2012 by Delphix. All rights reserved.
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2023, Domagoj Stolfa. All rights reserved.
*/
#include
@@ -55,6 +56,8 @@
#include
#endif
+#include
+
typedef struct dtrace_cmd {
void (*dc_func)(struct dtrace_cmd *); /* function to compile arg */
dtrace_probespec_t dc_spec; /* probe specifier context */
@@ -77,7 +80,7 @@
#define E_USAGE 2
static const char DTRACE_OPTSTR[] =
- "3:6:aAb:Bc:CdD:ef:FGhHi:I:lL:m:n:o:p:P:qs:SU:vVwx:X:Z";
+ "3:6:aAb:Bc:CdD:ef:FGhHi:I:lL:m:n:o:Op:P:qs:SU:vVwx:X:Z";
static char **g_argv;
static int g_argc;
@@ -167,6 +170,7 @@
"\t-m enable or list probes matching the specified module name\n"
"\t-n enable or list probes matching the specified probe name\n"
"\t-o set output file\n"
+ "\t-O print output upon exiting (specific to oformat)\n"
"\t-p grab specified process-ID and cache its symbol tables\n"
"\t-P enable or list probes matching the specified provider name\n"
"\t-q set quiet mode (only output explicitly traced data)\n"
@@ -907,7 +911,10 @@
static int
drophandler(const dtrace_dropdata_t *data, void *arg)
{
- error(data->dtdda_msg);
+ if (!dtrace_oformat(g_dtp)) {
+ error(data->dtdda_msg);
+ }
+
return (DTRACE_HANDLE_OK);
}
@@ -1126,7 +1133,9 @@
}
if (!g_flowindent) {
- if (!g_quiet) {
+ if (dtrace_oformat(g_dtp)) {
+ dtrace_oformat_probe(g_dtp, data, cpu, pd);
+ } else if (!g_quiet) {
char name[DTRACE_FUNCNAMELEN + DTRACE_NAMELEN + 2];
(void) snprintf(name, sizeof (name), "%s:%s",
@@ -1313,7 +1322,8 @@
g_ofp = stdout;
int done = 0, mode = 0;
- int err, i, c;
+ int err, i, c, new_argc, libxo_specified;
+ int print_upon_exit = 0;
char *p, **v;
struct ps_prochandle *P;
pid_t pid;
@@ -1336,6 +1346,15 @@
(g_psv = malloc(sizeof (struct ps_prochandle *) * argc)) == NULL)
fatal("failed to allocate memory for arguments");
+ new_argc = xo_parse_args(argc, argv);
+ if (new_argc < 0)
+ return (usage(stderr));
+
+ if (new_argc != argc)
+ libxo_specified = 1;
+
+ argc = new_argc;
+
g_argv[g_argc++] = argv[0]; /* propagate argv[0] to D as $0/$$0 */
argv[0] = g_pname; /* rewrite argv[0] for getopt errors */
@@ -1532,6 +1551,10 @@
} else if (g_mode == DMODE_ANON)
(void) dtrace_setopt(g_dtp, "linkmode", "primary");
+
+ if (libxo_specified)
+ dtrace_oformat_configure(g_dtp);
+
/*
* Now that we have libdtrace open, make a second pass through argv[]
* to perform any dtrace_setopt() calls and change any compiler flags.
@@ -1624,6 +1647,10 @@
dcp->dc_arg = optarg;
break;
+ case 'O':
+ print_upon_exit = 1;
+ break;
+
case 'q':
if (dtrace_setopt(g_dtp, "quiet", 0) != 0)
dfatal("failed to set -q");
@@ -1765,6 +1792,11 @@
(void) dtrace_getopt(g_dtp, "quiet", &opt);
g_quiet = opt != DTRACEOPT_UNSET;
+ if (dtrace_oformat(g_dtp)) {
+ if (dtrace_setopt(g_dtp, "quiet", 0) != 0)
+ dfatal("failed to set quiet (caused by oformat)");
+ }
+
/*
* Now make a fifth and final pass over the options that have been
* turned into programs and saved in g_cmdv[], performing any mode-
@@ -1777,6 +1809,9 @@
if (g_ofile != NULL && (g_ofp = fopen(g_ofile, "a")) == NULL)
fatal("failed to open output file '%s'", g_ofile);
+ if (dtrace_oformat(g_dtp))
+ dtrace_set_outfp(g_ofp);
+
for (i = 0; i < g_cmdc; i++)
exec_prog(&g_cmdv[i]);
@@ -1811,6 +1846,9 @@
if ((g_ofp = fopen(g_ofile, "a")) == NULL)
fatal("failed to open output file '%s'", g_ofile);
+ if (dtrace_oformat(g_dtp))
+ dtrace_set_outfp(g_ofp);
+
for (i = 0; i < g_cmdc; i++) {
anon_prog(&g_cmdv[i],
dtrace_dof_create(g_dtp, g_cmdv[i].dc_prog, 0), i);
@@ -1971,12 +2009,21 @@
g_pslive = g_psc; /* count for prochandler() */
+ dtrace_oformat_setup(g_dtp);
do {
if (!g_intr && !done)
dtrace_sleep(g_dtp);
#ifdef __FreeBSD__
- if (g_siginfo) {
+ /*
+ * XXX: Supporting SIGINFO with oformat makes little sense, as
+ * it can't really produce sensible DTrace output.
+ *
+ * If needed, we could support it by having an imaginary
+ * "SIGINFO" probe that we can construct in the output but leave
+ * it out for now.
+ */
+ if (g_siginfo && !dtrace_oformat(g_dtp)) {
(void)dtrace_aggregate_print(g_dtp, g_ofp, NULL);
g_siginfo = 0;
}
@@ -2013,14 +2060,24 @@
clearerr(g_ofp);
} while (!done);
- oprintf("\n");
+ if (!dtrace_oformat(g_dtp))
+ oprintf("\n");
- if (!g_impatient) {
+ /*
+ * Since there is no way to format a probe here and machine-readable
+ * output makes little sense without explicitly asking for it, we print
+ * nothing upon Ctrl-C if oformat is specified. If the user wishes to
+ * get output upon exit, they must write an explicit dtrace:::END probe
+ * to do so.
+ */
+ if ((!g_impatient && !dtrace_oformat(g_dtp)) ||
+ (!g_impatient && print_upon_exit)) {
if (dtrace_aggregate_print(g_dtp, g_ofp, NULL) == -1 &&
dtrace_errno(g_dtp) != EINTR)
dfatal("failed to print aggregations");
}
+ dtrace_oformat_teardown(g_dtp);
dtrace_close(g_dtp);
return (g_status);
}
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.avg.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.avg.ksh
new file mode 100644
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.avg.ksh
@@ -0,0 +1,66 @@
+#!/usr/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2023 Domagoj Stolfa
+#
+
+bname=`basename $0`
+dtraceout=/tmp/dtrace.$bname
+
+script()
+{
+ $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__
+syscall:::entry
+{
+ @[probefunc] = avg(tid);
+}
+
+tick-5s
+{
+ exit(0);
+}
+
+END
+{
+ printa(@);
+}
+__EOF__
+}
+
+if [ $# != 1 ]; then
+ echo expected one argument: '<'dtrace-path'>'
+ exit 2
+fi
+
+dtrace=$1
+
+script json
+jq . $dtraceout.json
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid JSON. see $dtraceout.json
+ exit 1
+fi
+
+script xml
+xmllint $dtraceout.xml
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid XML. see $dtraceout.xml
+ exit 1
+fi
+
+rm $dtraceout.json
+rm $dtraceout.xml
+
+exit 0
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.count.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.count.ksh
new file mode 100644
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.count.ksh
@@ -0,0 +1,66 @@
+#!/usr/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2023 Domagoj Stolfa
+#
+
+bname=`basename $0`
+dtraceout=/tmp/dtrace.$bname
+
+script()
+{
+ $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__
+syscall:::entry
+{
+ @[execname] = count();
+}
+
+tick-5s
+{
+ exit(0);
+}
+
+END
+{
+ printa(@);
+}
+__EOF__
+}
+
+if [ $# != 1 ]; then
+ echo expected one argument: '<'dtrace-path'>'
+ exit 2
+fi
+
+dtrace=$1
+
+script json
+jq . $dtraceout.json
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid JSON. see $dtraceout.json
+ exit 1
+fi
+
+script xml
+xmllint $dtraceout.xml
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid XML. see $dtraceout.xml
+ exit 1
+fi
+
+rm $dtraceout.json
+rm $dtraceout.xml
+
+exit 0
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.llquantize.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.llquantize.ksh
new file mode 100644
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.llquantize.ksh
@@ -0,0 +1,73 @@
+#!/usr/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2023 Domagoj Stolfa
+#
+
+bname=`basename $0`
+dtraceout=/tmp/dtrace.$bname
+
+script()
+{
+ $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__
+syscall:::entry
+{
+ self->ts = timestamp;
+}
+
+syscall:::return
+/self->ts/
+{
+ @[probefunc] = llquantize(timestamp - self->ts, 2, 1, 32, 32);
+ self->ts = 0;
+}
+
+tick-5s
+{
+ exit(0);
+}
+
+END
+{
+ printa(@);
+}
+__EOF__
+}
+
+if [ $# != 1 ]; then
+ echo expected one argument: '<'dtrace-path'>'
+ exit 2
+fi
+
+dtrace=$1
+
+script json
+jq . $dtraceout.json
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid JSON. see $dtraceout.json
+ exit 1
+fi
+
+script xml
+xmllint $dtraceout.xml
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid XML. see $dtraceout.xml
+ exit 1
+fi
+
+rm $dtraceout.json
+rm $dtraceout.xml
+
+exit 0
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.lquantize.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.lquantize.ksh
new file mode 100644
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.lquantize.ksh
@@ -0,0 +1,73 @@
+#!/usr/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2023 Domagoj Stolfa
+#
+
+bname=`basename $0`
+dtraceout=/tmp/dtrace.$bname
+
+script()
+{
+ $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__
+syscall:::entry
+{
+ self->ts = timestamp;
+}
+
+syscall:::return
+/self->ts/
+{
+ @[probefunc] = lquantize(timestamp - self->ts, 0, 1000000, 100);
+ self->ts = 0;
+}
+
+tick-5s
+{
+ exit(0);
+}
+
+END
+{
+ printa(@);
+}
+__EOF__
+}
+
+if [ $# != 1 ]; then
+ echo expected one argument: '<'dtrace-path'>'
+ exit 2
+fi
+
+dtrace=$1
+
+script json
+jq . $dtraceout.json
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid JSON. see $dtraceout.json
+ exit 1
+fi
+
+script xml
+xmllint $dtraceout.xml
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid XML. see $dtraceout.xml
+ exit 1
+fi
+
+rm $dtraceout.json
+rm $dtraceout.xml
+
+exit 0
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.max.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.max.ksh
new file mode 100644
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.max.ksh
@@ -0,0 +1,66 @@
+#!/usr/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2023 Domagoj Stolfa
+#
+
+bname=`basename $0`
+dtraceout=/tmp/dtrace.$bname
+
+script()
+{
+ $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__
+syscall:::entry
+{
+ @[probefunc] = max(tid);
+}
+
+tick-5s
+{
+ exit(0);
+}
+
+END
+{
+ printa(@);
+}
+__EOF__
+}
+
+if [ $# != 1 ]; then
+ echo expected one argument: '<'dtrace-path'>'
+ exit 2
+fi
+
+dtrace=$1
+
+script json
+jq . $dtraceout.json
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid JSON. see $dtraceout.json
+ exit 1
+fi
+
+script xml
+xmllint $dtraceout.xml
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid XML. see $dtraceout.xml
+ exit 1
+fi
+
+rm $dtraceout.json
+rm $dtraceout.xml
+
+exit 0
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.min.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.min.ksh
new file mode 100644
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.min.ksh
@@ -0,0 +1,66 @@
+#!/usr/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2023 Domagoj Stolfa
+#
+
+bname=`basename $0`
+dtraceout=/tmp/dtrace.$bname
+
+script()
+{
+ $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__
+syscall:::entry
+{
+ @[probefunc] = min(tid);
+}
+
+tick-5s
+{
+ exit(0);
+}
+
+END
+{
+ printa(@);
+}
+__EOF__
+}
+
+if [ $# != 1 ]; then
+ echo expected one argument: '<'dtrace-path'>'
+ exit 2
+fi
+
+dtrace=$1
+
+script json
+jq . $dtraceout.json
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid JSON. see $dtraceout.json
+ exit 1
+fi
+
+script xml
+xmllint $dtraceout.xml
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid XML. see $dtraceout.xml
+ exit 1
+fi
+
+rm $dtraceout.json
+rm $dtraceout.xml
+
+exit 0
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.quantize.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.quantize.ksh
new file mode 100644
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.quantize.ksh
@@ -0,0 +1,73 @@
+#!/usr/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2023 Domagoj Stolfa
+#
+
+bname=`basename $0`
+dtraceout=/tmp/dtrace.$bname
+
+script()
+{
+ $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__
+syscall:::entry
+{
+ self->ts = timestamp;
+}
+
+syscall:::return
+/self->ts/
+{
+ @[probefunc] = quantize(timestamp - self->ts);
+ self->ts = 0;
+}
+
+tick-5s
+{
+ exit(0);
+}
+
+END
+{
+ printa(@);
+}
+__EOF__
+}
+
+if [ $# != 1 ]; then
+ echo expected one argument: '<'dtrace-path'>'
+ exit 2
+fi
+
+dtrace=$1
+
+script json
+jq . $dtraceout.json
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid JSON. see $dtraceout.json
+ exit 1
+fi
+
+script xml
+xmllint $dtraceout.xml
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid XML. see $dtraceout.xml
+ exit 1
+fi
+
+rm $dtraceout.json
+rm $dtraceout.xml
+
+exit 0
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.stddev.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.stddev.ksh
new file mode 100644
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.stddev.ksh
@@ -0,0 +1,66 @@
+#!/usr/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2023 Domagoj Stolfa
+#
+
+bname=`basename $0`
+dtraceout=/tmp/dtrace.$bname
+
+script()
+{
+ $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__
+syscall:::entry
+{
+ @[probefunc] = stddev(tid);
+}
+
+tick-5s
+{
+ exit(0);
+}
+
+END
+{
+ printa(@);
+}
+__EOF__
+}
+
+if [ $# != 1 ]; then
+ echo expected one argument: '<'dtrace-path'>'
+ exit 2
+fi
+
+dtrace=$1
+
+script json
+jq . $dtraceout.json
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid JSON. see $dtraceout.json
+ exit 1
+fi
+
+script xml
+xmllint $dtraceout.xml
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid XML. see $dtraceout.xml
+ exit 1
+fi
+
+rm $dtraceout.json
+rm $dtraceout.xml
+
+exit 0
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.sum.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.sum.ksh
new file mode 100644
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.sum.ksh
@@ -0,0 +1,66 @@
+#!/usr/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2023 Domagoj Stolfa
+#
+
+bname=`basename $0`
+dtraceout=/tmp/dtrace.$bname
+
+script()
+{
+ $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__
+syscall:::entry
+{
+ @[probefunc] = sum(1);
+}
+
+tick-5s
+{
+ exit(0);
+}
+
+END
+{
+ printa(@);
+}
+__EOF__
+}
+
+if [ $# != 1 ]; then
+ echo expected one argument: '<'dtrace-path'>'
+ exit 2
+fi
+
+dtrace=$1
+
+script json
+jq . $dtraceout.json
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid JSON. see $dtraceout.json
+ exit 1
+fi
+
+script xml
+xmllint $dtraceout.xml
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid XML. see $dtraceout.xml
+ exit 1
+fi
+
+rm $dtraceout.json
+rm $dtraceout.xml
+
+exit 0
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggmod.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggmod.ksh
new file mode 100644
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggmod.ksh
@@ -0,0 +1,66 @@
+#!/usr/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2023 Domagoj Stolfa
+#
+
+bname=`basename $0`
+dtraceout=/tmp/dtrace.$bname
+
+script()
+{
+ $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__
+syscall:::entry
+{
+ @[mod((uintptr_t)rand())] = count();
+}
+
+tick-5s
+{
+ exit(0);
+}
+
+END
+{
+ printa(@);
+}
+__EOF__
+}
+
+if [ $# != 1 ]; then
+ echo expected one argument: '<'dtrace-path'>'
+ exit 2
+fi
+
+dtrace=$1
+
+script json
+jq . $dtraceout.json
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid JSON. see $dtraceout.json
+ exit 1
+fi
+
+script xml
+xmllint $dtraceout.xml
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid XML. see $dtraceout.xml
+ exit 1
+fi
+
+rm $dtraceout.json
+rm $dtraceout.xml
+
+exit 0
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggstack.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggstack.ksh
new file mode 100644
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggstack.ksh
@@ -0,0 +1,66 @@
+#!/usr/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2023 Domagoj Stolfa
+#
+
+bname=`basename $0`
+dtraceout=/tmp/dtrace.$bname
+
+script()
+{
+ $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__
+fbt:::entry
+{
+ @[stack()] = count();
+}
+
+tick-5s
+{
+ exit(0);
+}
+
+END
+{
+ printa(@);
+}
+__EOF__
+}
+
+if [ $# != 1 ]; then
+ echo expected one argument: '<'dtrace-path'>'
+ exit 2
+fi
+
+dtrace=$1
+
+script json
+jq . $dtraceout.json
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid JSON. see $dtraceout.json
+ exit 1
+fi
+
+script xml
+xmllint $dtraceout.xml
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid XML. see $dtraceout.xml
+ exit 1
+fi
+
+rm $dtraceout.json
+rm $dtraceout.xml
+
+exit 0
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggsym.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggsym.ksh
new file mode 100644
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggsym.ksh
@@ -0,0 +1,66 @@
+#!/usr/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2023 Domagoj Stolfa
+#
+
+bname=`basename $0`
+dtraceout=/tmp/dtrace.$bname
+
+script()
+{
+ $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__
+syscall:::entry
+{
+ @[sym((uintptr_t)rand())] = count();
+}
+
+tick-5s
+{
+ exit(0);
+}
+
+END
+{
+ printa(@);
+}
+__EOF__
+}
+
+if [ $# != 1 ]; then
+ echo expected one argument: '<'dtrace-path'>'
+ exit 2
+fi
+
+dtrace=$1
+
+script json
+jq . $dtraceout.json
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid JSON. see $dtraceout.json
+ exit 1
+fi
+
+script xml
+xmllint $dtraceout.xml
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid XML. see $dtraceout.xml
+ exit 1
+fi
+
+rm $dtraceout.json
+rm $dtraceout.xml
+
+exit 0
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggustack.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggustack.ksh
new file mode 100644
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggustack.ksh
@@ -0,0 +1,66 @@
+#!/usr/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2023 Domagoj Stolfa
+#
+
+bname=`basename $0`
+dtraceout=/tmp/dtrace.$bname
+
+script()
+{
+ $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__
+syscall:::entry
+{
+ @[ustack()] = count();
+}
+
+tick-5s
+{
+ exit(0);
+}
+
+END
+{
+ printa(@);
+}
+__EOF__
+}
+
+if [ $# != 1 ]; then
+ echo expected one argument: '<'dtrace-path'>'
+ exit 2
+fi
+
+dtrace=$1
+
+script json
+jq . $dtraceout.json
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid JSON. see $dtraceout.json
+ exit 1
+fi
+
+script xml
+xmllint $dtraceout.xml
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid XML. see $dtraceout.xml
+ exit 1
+fi
+
+rm $dtraceout.json
+rm $dtraceout.xml
+
+exit 0
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggusym.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggusym.ksh
new file mode 100644
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggusym.ksh
@@ -0,0 +1,66 @@
+#!/usr/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2023 Domagoj Stolfa
+#
+
+bname=`basename $0`
+dtraceout=/tmp/dtrace.$bname
+
+script()
+{
+ $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__
+syscall:::entry
+{
+ @[usym((uintptr_t)rand())] = count();
+}
+
+tick-5s
+{
+ exit(0);
+}
+
+END
+{
+ printa(@);
+}
+__EOF__
+}
+
+if [ $# != 1 ]; then
+ echo expected one argument: '<'dtrace-path'>'
+ exit 2
+fi
+
+dtrace=$1
+
+script json
+jq . $dtraceout.json
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid JSON. see $dtraceout.json
+ exit 1
+fi
+
+script xml
+xmllint $dtraceout.xml
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid XML. see $dtraceout.xml
+ exit 1
+fi
+
+rm $dtraceout.json
+rm $dtraceout.xml
+
+exit 0
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.drop.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.drop.ksh
new file mode 100644
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.drop.ksh
@@ -0,0 +1,61 @@
+#!/usr/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2023 Domagoj Stolfa
+#
+
+bname=`basename $0`
+dtraceout=/tmp/dtrace.$bname
+
+script()
+{
+ $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__
+fbt:::entry
+{
+ x[timestamp] = 1;
+}
+
+tick-5s
+{
+ exit(0);
+}
+__EOF__
+}
+
+if [ $# != 1 ]; then
+ echo expected one argument: '<'dtrace-path'>'
+ exit 2
+fi
+
+dtrace=$1
+
+script json
+jq . $dtraceout.json
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid JSON. see $dtraceout.json
+ exit 1
+fi
+
+script xml
+xmllint $dtraceout.xml
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid XML. see $dtraceout.xml
+ exit 1
+fi
+
+rm $dtraceout.json
+rm $dtraceout.xml
+
+exit 0
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.mod.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.mod.ksh
new file mode 100644
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.mod.ksh
@@ -0,0 +1,57 @@
+#!/usr/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2023 Domagoj Stolfa
+#
+
+bname=`basename $0`
+dtraceout=/tmp/dtrace.$bname
+
+script()
+{
+ $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__
+BEGIN
+{
+ mod(0);
+ exit(0);
+}
+__EOF__
+}
+
+if [ $# != 1 ]; then
+ echo expected one argument: '<'dtrace-path'>'
+ exit 2
+fi
+
+dtrace=$1
+
+script json
+jq . $dtraceout.json
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid JSON. see $dtraceout.json
+ exit 1
+fi
+
+script xml
+xmllint $dtraceout.xml
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid XML. see $dtraceout.xml
+ exit 1
+fi
+
+rm $dtraceout.json
+rm $dtraceout.xml
+
+exit 0
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.print.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.print.ksh
new file mode 100644
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.print.ksh
@@ -0,0 +1,60 @@
+#!/usr/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2023 Domagoj Stolfa
+#
+
+bname=`basename $0`
+dtraceout=/tmp/dtrace.$bname
+
+script()
+{
+ $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__
+BEGIN
+{
+ print(*curthread);
+ print(*curthread->td_proc);
+ print(*curthread->td_ucred);
+ print(*curthread->td_ucred->cr_prison);
+ exit(0);
+}
+__EOF__
+}
+
+if [ $# != 1 ]; then
+ echo expected one argument: '<'dtrace-path'>'
+ exit 2
+fi
+
+dtrace=$1
+
+script json
+jq . $dtraceout.json
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid JSON. see $dtraceout.json
+ exit 1
+fi
+
+script xml
+xmllint $dtraceout.xml
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid XML. see $dtraceout.xml
+ exit 1
+fi
+
+rm $dtraceout.json
+rm $dtraceout.xml
+
+exit 0
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.printf.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.printf.ksh
new file mode 100644
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.printf.ksh
@@ -0,0 +1,57 @@
+#!/usr/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2023 Domagoj Stolfa
+#
+
+bname=`basename $0`
+dtraceout=/tmp/dtrace.$bname
+
+script()
+{
+ $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__
+BEGIN
+{
+ printf("Hello World!");
+ exit(0);
+}
+__EOF__
+}
+
+if [ $# != 1 ]; then
+ echo expected one argument: '<'dtrace-path'>'
+ exit 2
+fi
+
+dtrace=$1
+
+script json
+jq . $dtraceout.json
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid JSON. see $dtraceout.json
+ exit 1
+fi
+
+script xml
+xmllint $dtraceout.xml
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid XML. see $dtraceout.xml
+ exit 1
+fi
+
+rm $dtraceout.json
+rm $dtraceout.xml
+
+exit 0
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.printm.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.printm.ksh
new file mode 100644
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.printm.ksh
@@ -0,0 +1,57 @@
+#!/usr/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2023 Domagoj Stolfa
+#
+
+bname=`basename $0`
+dtraceout=/tmp/dtrace.$bname
+
+script()
+{
+ $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__
+BEGIN
+{
+ printm(100, memref(curthread, 10));
+ exit(0);
+}
+__EOF__
+}
+
+if [ $# != 1 ]; then
+ echo expected one argument: '<'dtrace-path'>'
+ exit 2
+fi
+
+dtrace=$1
+
+script json
+jq . $dtraceout.json
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid JSON. see $dtraceout.json
+ exit 1
+fi
+
+script xml
+xmllint $dtraceout.xml
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid XML. see $dtraceout.xml
+ exit 1
+fi
+
+rm $dtraceout.json
+rm $dtraceout.xml
+
+exit 0
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.stack.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.stack.ksh
new file mode 100644
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.stack.ksh
@@ -0,0 +1,61 @@
+#!/usr/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2023 Domagoj Stolfa
+#
+
+bname=`basename $0`
+dtraceout=/tmp/dtrace.$bname
+
+script()
+{
+ $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__
+syscall:::entry
+{
+ stack();
+}
+
+tick-1s
+{
+ exit(0);
+}
+__EOF__
+}
+
+if [ $# != 1 ]; then
+ echo expected one argument: '<'dtrace-path'>'
+ exit 2
+fi
+
+dtrace=$1
+
+script json
+jq . $dtraceout.json
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid JSON. see $dtraceout.json
+ exit 1
+fi
+
+script xml
+xmllint $dtraceout.xml
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid XML. see $dtraceout.xml
+ exit 1
+fi
+
+rm $dtraceout.json
+rm $dtraceout.xml
+
+exit 0
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.sym.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.sym.ksh
new file mode 100644
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.sym.ksh
@@ -0,0 +1,57 @@
+#!/usr/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2023 Domagoj Stolfa
+#
+
+bname=`basename $0`
+dtraceout=/tmp/dtrace.$bname
+
+script()
+{
+ $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__
+BEGIN
+{
+ sym(0);
+ exit(0);
+}
+__EOF__
+}
+
+if [ $# != 1 ]; then
+ echo expected one argument: '<'dtrace-path'>'
+ exit 2
+fi
+
+dtrace=$1
+
+script json
+jq . $dtraceout.json
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid JSON. see $dtraceout.json
+ exit 1
+fi
+
+script xml
+xmllint $dtraceout.xml
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid XML. see $dtraceout.xml
+ exit 1
+fi
+
+rm $dtraceout.json
+rm $dtraceout.xml
+
+exit 0
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.trace.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.trace.ksh
new file mode 100644
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.trace.ksh
@@ -0,0 +1,66 @@
+#!/usr/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2023 Domagoj Stolfa
+#
+
+bname=`basename $0`
+dtraceout=/tmp/dtrace.$bname
+
+script()
+{
+ $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__
+BEGIN
+{
+ trace(0);
+ trace(1);
+ trace(2);
+ trace(3);
+ trace(4);
+ trace(5);
+ trace(6);
+ trace(7);
+ trace(8);
+ trace(9);
+ exit(0);
+}
+__EOF__
+}
+
+if [ $# != 1 ]; then
+ echo expected one argument: '<'dtrace-path'>'
+ exit 2
+fi
+
+dtrace=$1
+
+script json
+jq . $dtraceout.json
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid JSON. see $dtraceout.json
+ exit 1
+fi
+
+script xml
+xmllint $dtraceout.xml
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid XML. see $dtraceout.xml
+ exit 1
+fi
+
+rm $dtraceout.json
+rm $dtraceout.xml
+
+exit 0
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.tracemem.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.tracemem.ksh
new file mode 100644
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.tracemem.ksh
@@ -0,0 +1,57 @@
+#!/usr/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2023 Domagoj Stolfa
+#
+
+bname=`basename $0`
+dtraceout=/tmp/dtrace.$bname
+
+script()
+{
+ $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__
+BEGIN
+{
+ tracemem(curthread, 10);
+ exit(0);
+}
+__EOF__
+}
+
+if [ $# != 1 ]; then
+ echo expected one argument: '<'dtrace-path'>'
+ exit 2
+fi
+
+dtrace=$1
+
+script json
+jq . $dtraceout.json
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid JSON. see $dtraceout.json
+ exit 1
+fi
+
+script xml
+xmllint $dtraceout.xml
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid XML. see $dtraceout.xml
+ exit 1
+fi
+
+rm $dtraceout.json
+rm $dtraceout.xml
+
+exit 0
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.umod.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.umod.ksh
new file mode 100644
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.umod.ksh
@@ -0,0 +1,57 @@
+#!/usr/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2023 Domagoj Stolfa
+#
+
+bname=`basename $0`
+dtraceout=/tmp/dtrace.$bname
+
+script()
+{
+ $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__
+BEGIN
+{
+ umod(0);
+ exit(0);
+}
+__EOF__
+}
+
+if [ $# != 1 ]; then
+ echo expected one argument: '<'dtrace-path'>'
+ exit 2
+fi
+
+dtrace=$1
+
+script json
+jq . $dtraceout.json
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid JSON. see $dtraceout.json
+ exit 1
+fi
+
+script xml
+xmllint $dtraceout.xml
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid XML. see $dtraceout.xml
+ exit 1
+fi
+
+rm $dtraceout.json
+rm $dtraceout.xml
+
+exit 0
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.ustack.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.ustack.ksh
new file mode 100644
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.ustack.ksh
@@ -0,0 +1,61 @@
+#!/usr/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2023 Domagoj Stolfa
+#
+
+bname=`basename $0`
+dtraceout=/tmp/dtrace.$bname
+
+script()
+{
+ $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__
+syscall:::entry
+{
+ ustack();
+}
+
+tick-1s
+{
+ exit(0);
+}
+__EOF__
+}
+
+if [ $# != 1 ]; then
+ echo expected one argument: '<'dtrace-path'>'
+ exit 2
+fi
+
+dtrace=$1
+
+script json
+jq . $dtraceout.json
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid JSON. see $dtraceout.json
+ exit 1
+fi
+
+script xml
+xmllint $dtraceout.xml
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid XML. see $dtraceout.xml
+ exit 1
+fi
+
+rm $dtraceout.json
+rm $dtraceout.xml
+
+exit 0
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.usym.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.usym.ksh
new file mode 100644
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.usym.ksh
@@ -0,0 +1,57 @@
+#!/usr/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2023 Domagoj Stolfa
+#
+
+bname=`basename $0`
+dtraceout=/tmp/dtrace.$bname
+
+script()
+{
+ $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__
+BEGIN
+{
+ usym(0);
+ exit(0);
+}
+__EOF__
+}
+
+if [ $# != 1 ]; then
+ echo expected one argument: '<'dtrace-path'>'
+ exit 2
+fi
+
+dtrace=$1
+
+script json
+jq . $dtraceout.json
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid JSON. see $dtraceout.json
+ exit 1
+fi
+
+script xml
+xmllint $dtraceout.xml
+
+if [ $? != 0 ]; then
+ echo $bname: failed to produce valid XML. see $dtraceout.xml
+ exit 1
+fi
+
+rm $dtraceout.json
+rm $dtraceout.xml
+
+exit 0
diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_aggregate.c b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_aggregate.c
--- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_aggregate.c
+++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_aggregate.c
@@ -35,6 +35,7 @@
#include
#include
#include
+#include
#ifdef illumos
#include
#else
@@ -471,9 +472,14 @@
}
if (buf->dtbd_drops != 0) {
+ xo_open_instance("probes");
+ dt_oformat_drop(dtp, cpu);
if (dt_handle_cpudrop(dtp, cpu,
- DTRACEDROP_AGGREGATION, buf->dtbd_drops) == -1)
+ DTRACEDROP_AGGREGATION, buf->dtbd_drops) == -1) {
+ xo_close_instance("probes");
return (-1);
+ }
+ xo_close_instance("probes");
}
if (buf->dtbd_size == 0)
@@ -2127,8 +2133,14 @@
if (func == NULL)
func = dtrace_aggregate_walk_sorted;
- if ((*func)(dtp, dt_print_agg, &pd) == -1)
- return (dt_set_errno(dtp, dtp->dt_errno));
+ if (dtp->dt_oformat) {
+ if ((*func)(dtp, dt_format_agg, &pd) == -1)
+ return (dt_set_errno(dtp, dtp->dt_errno));
+ xo_flush();
+ } else {
+ if ((*func)(dtp, dt_print_agg, &pd) == -1)
+ return (dt_set_errno(dtp, dtp->dt_errno));
+ }
return (0);
}
diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_consume.c b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_consume.c
--- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_consume.c
+++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_consume.c
@@ -24,6 +24,7 @@
*/
/*
+ * Copyright (c) 2023, Domagoj Stolfa. All rights reserved.
* Copyright (c) 2017, Joyent, Inc. All rights reserved.
* Copyright (c) 2012 by Delphix. All rights reserved.
*/
@@ -40,12 +41,50 @@
#endif
#include
#include
+#include
#ifndef illumos
#include
#endif
#define DT_MASK_LO 0x00000000FFFFFFFFULL
+#define dt_format_sym(dtp, addr) dt_print_sym((dtp), NULL, NULL, addr)
+
+typedef struct dt_prepare_args {
+ int first_bin;
+ int last_bin;
+ union {
+ struct lquantize_args {
+#define lquantize_step u.lquantize.step
+#define lquantize_levels u.lquantize.levels
+#define lquantize_base u.lquantize.base
+ int base;
+ uint16_t step;
+ uint16_t levels;
+ } lquantize;
+ struct llquantize_args {
+#define llquantize_next u.llquantize.next
+#define llquantize_step u.llquantize.step
+#define llquantize_value u.llquantize.value
+#define llquantize_levels u.llquantize.levels
+#define llquantize_order u.llquantize.order
+#define llquantize_factor u.llquantize.factor
+#define llquantize_low u.llquantize.low
+#define llquantize_high u.llquantize.high
+#define llquantize_nsteps u.llquantize.nsteps
+ int64_t next;
+ int64_t step;
+ int64_t value;
+ int levels;
+ int order;
+ uint16_t factor;
+ uint16_t low;
+ uint16_t high;
+ uint16_t nsteps;
+ } llquantize;
+ } u;
+} dt_prepare_args_t;
+
/*
* We declare this here because (1) we need it and (2) we want to avoid a
* dependency on libm in libdtrace.
@@ -761,17 +800,17 @@
return (dt_printf(dtp, fp, "%c", ascii[(uint_t)(val + 0.5)]));
}
-int
-dt_print_quantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr,
- size_t size, uint64_t normal)
+static const int64_t *
+dt_format_quantize_prepare(dtrace_hdl_t *dtp, const void *addr, size_t size,
+ dt_prepare_args_t *args)
{
const int64_t *data = addr;
- int i, first_bin = 0, last_bin = DTRACE_QUANTIZE_NBUCKETS - 1;
- long double total = 0;
- char positives = 0, negatives = 0;
+ int first_bin = 0, last_bin = DTRACE_QUANTIZE_NBUCKETS - 1;
- if (size != DTRACE_QUANTIZE_NBUCKETS * sizeof (uint64_t))
- return (dt_set_errno(dtp, EDT_DMISMATCH));
+ if (size != DTRACE_QUANTIZE_NBUCKETS * sizeof (uint64_t)) {
+ (void) dt_set_errno(dtp, EDT_DMISMATCH);
+ return (NULL);
+ }
while (first_bin < DTRACE_QUANTIZE_NBUCKETS - 1 && data[first_bin] == 0)
first_bin++;
@@ -795,6 +834,58 @@
last_bin++;
}
+ args->first_bin = first_bin;
+ args->last_bin = last_bin;
+ return (data);
+}
+
+int
+dt_format_quantize(dtrace_hdl_t *dtp, const void *addr, size_t size,
+ uint64_t normal)
+{
+ const int64_t *data;
+ dt_prepare_args_t args = { 0 };
+ int i, first_bin = 0, last_bin = DTRACE_QUANTIZE_NBUCKETS - 1;
+
+ data = dt_format_quantize_prepare(dtp, addr, size, &args);
+ /* dt_errno is set for us */
+ if (data == NULL)
+ return (-1);
+
+ first_bin = args.first_bin;
+ last_bin = args.last_bin;
+
+ xo_open_list("buckets");
+ for (i = first_bin; i <= last_bin; i++) {
+ long long value = (long long)DTRACE_QUANTIZE_BUCKETVAL(i);
+ xo_open_instance("buckets");
+ xo_emit("{:value/%lld} {:count/%lld}", value,
+ (long long)data[i] / normal);
+ xo_close_instance("buckets");
+ }
+ xo_close_list("buckets");
+
+ return (0);
+}
+
+int
+dt_print_quantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr,
+ size_t size, uint64_t normal)
+{
+ const int64_t *data;
+ dt_prepare_args_t args = { 0 };
+ int i, first_bin = 0, last_bin = DTRACE_QUANTIZE_NBUCKETS - 1;
+ long double total = 0;
+ char positives = 0, negatives = 0;
+
+ data = dt_format_quantize_prepare(dtp, addr, size, &args);
+ /* dt_errno is set for us */
+ if (data == NULL)
+ return (-1);
+
+ first_bin = args.first_bin;
+ last_bin = args.last_bin;
+
for (i = first_bin; i <= last_bin; i++) {
positives |= (data[i] > 0);
negatives |= (data[i] < 0);
@@ -859,19 +950,19 @@
return (0);
}
-int
-dt_print_lquantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr,
- size_t size, uint64_t normal)
+static const int64_t *
+dt_format_lquantize_prepare(dtrace_hdl_t *dtp, const void *addr, size_t size,
+ dt_prepare_args_t *args)
{
const int64_t *data = addr;
- int i, first_bin, last_bin, base;
+ int first_bin = 0, last_bin = DTRACE_QUANTIZE_NBUCKETS - 1, base;
uint64_t arg;
- long double total = 0;
uint16_t step, levels;
- char positives = 0, negatives = 0;
- if (size < sizeof (uint64_t))
- return (dt_set_errno(dtp, EDT_DMISMATCH));
+ if (size < sizeof (uint64_t)) {
+ (void) dt_set_errno(dtp, EDT_DMISMATCH);
+ return (NULL);
+ }
arg = *data++;
size -= sizeof (uint64_t);
@@ -883,8 +974,10 @@
first_bin = 0;
last_bin = levels + 1;
- if (size != sizeof (uint64_t) * (levels + 2))
- return (dt_set_errno(dtp, EDT_DMISMATCH));
+ if (size != sizeof (uint64_t) * (levels + 2)) {
+ (void) dt_set_errno(dtp, EDT_DMISMATCH);
+ return (NULL);
+ }
while (first_bin <= levels + 1 && data[first_bin] == 0)
first_bin++;
@@ -903,6 +996,80 @@
last_bin++;
}
+ args->first_bin = first_bin;
+ args->last_bin = last_bin;
+ args->lquantize_base = base;
+ args->lquantize_step = step;
+ args->lquantize_levels = levels;
+ return (data);
+}
+
+int
+dt_format_lquantize(dtrace_hdl_t *dtp, const void *addr, size_t size,
+ uint64_t normal)
+{
+ const int64_t *data;
+ dt_prepare_args_t args = { 0 };
+ int i, first_bin, last_bin, base;
+ uint16_t step, levels;
+
+ data = dt_format_lquantize_prepare(dtp, addr, size, &args);
+ /* dt_errno is set for us */
+ if (data == NULL)
+ return (-1);
+
+ first_bin = args.first_bin;
+ last_bin = args.last_bin;
+ step = args.lquantize_step;
+ levels = args.lquantize_levels;
+ base = args.lquantize_base;
+
+ xo_open_list("buckets");
+ for (i = first_bin; i <= last_bin; i++) {
+ char c[32];
+ int err;
+
+ xo_open_instance("buckets");
+ if (i == 0) {
+ xo_emit("{:value/%d} {:operator/%s}", base, "<");
+ } else if (i == levels + 1) {
+ xo_emit("{:value/%d} {:operator/%s}",
+ base + (levels * step), ">=");
+ } else {
+ xo_emit("{:value/%d}", base + (i - 1) * step);
+ }
+
+ xo_emit("{:count/%lld}", (long long)data[i] / normal);
+ xo_close_instance("buckets");
+ }
+ xo_close_list("buckets");
+
+ return (0);
+}
+
+int
+dt_print_lquantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr,
+ size_t size, uint64_t normal)
+{
+ const int64_t *data;
+ dt_prepare_args_t args = { 0 };
+ int i, first_bin, last_bin, base;
+ uint64_t arg;
+ long double total = 0;
+ uint16_t step, levels;
+ char positives = 0, negatives = 0;
+
+ data = dt_format_lquantize_prepare(dtp, addr, size, &args);
+ /* dt_errno is set for us */
+ if (data == NULL)
+ return (-1);
+
+ first_bin = args.first_bin;
+ last_bin = args.last_bin;
+ step = args.lquantize_step;
+ levels = args.lquantize_levels;
+ base = args.lquantize_base;
+
for (i = first_bin; i <= last_bin; i++) {
positives |= (data[i] > 0);
negatives |= (data[i] < 0);
@@ -990,21 +1157,20 @@
return (dt_printf(dtp, fp, ": %-8s | %lld\n", c, (long long)count));
}
-int
-dt_print_llquantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr,
- size_t size, uint64_t normal)
+static const int64_t *
+dt_format_llquantize_prepare(dtrace_hdl_t *dtp, const void *addr, size_t size,
+ dt_prepare_args_t *args)
{
int i, first_bin, last_bin, bin = 1, order, levels;
uint16_t factor, low, high, nsteps;
const int64_t *data = addr;
int64_t value = 1, next, step;
- char positives = 0, negatives = 0;
- long double total = 0;
uint64_t arg;
- char c[32];
- if (size < sizeof (uint64_t))
- return (dt_set_errno(dtp, EDT_DMISMATCH));
+ if (size < sizeof(uint64_t)) {
+ (void) dt_set_errno(dtp, EDT_DMISMATCH);
+ return (NULL);
+ }
arg = *data++;
size -= sizeof (uint64_t);
@@ -1019,8 +1185,10 @@
* but sanity check them (to a degree) nonetheless.
*/
if (size > INT32_MAX || factor < 2 || low >= high ||
- nsteps == 0 || factor > nsteps)
- return (dt_set_errno(dtp, EDT_DMISMATCH));
+ nsteps == 0 || factor > nsteps) {
+ (void) dt_set_errno(dtp, EDT_DMISMATCH);
+ return (NULL);
+ }
levels = (int)size / sizeof (uint64_t);
@@ -1044,6 +1212,133 @@
last_bin++;
}
+ for (order = 0; order < low; order++)
+ value *= factor;
+
+ next = value * factor;
+ step = next > nsteps ? next / nsteps : 1;
+
+ args->first_bin = first_bin;
+ args->last_bin = last_bin;
+ args->llquantize_factor = factor;
+ args->llquantize_low = low;
+ args->llquantize_high = high;
+ args->llquantize_nsteps = nsteps;
+ args->llquantize_levels = levels;
+ args->llquantize_order = order;
+ args->llquantize_next = next;
+ args->llquantize_step = step;
+ args->llquantize_value = value;
+
+ return (data);
+}
+
+int
+dt_format_llquantize(dtrace_hdl_t *dtp, const void *addr, size_t size,
+ uint64_t normal)
+{
+ int first_bin, last_bin, bin = 1, order, levels;
+ uint16_t factor, low, high, nsteps;
+ const int64_t *data;
+ dt_prepare_args_t args = { 0 };
+ int64_t value = 1, next, step;
+ uint64_t arg;
+ char c[32];
+
+ data = dt_format_llquantize_prepare(dtp, addr, size, &args);
+ /* dt_errno is set for us */
+ if (data == NULL)
+ return (-1);
+
+ first_bin = args.first_bin;
+ last_bin = args.last_bin;
+ factor = args.llquantize_factor;
+ low = args.llquantize_low;
+ high = args.llquantize_high;
+ nsteps = args.llquantize_nsteps;
+ levels = args.llquantize_levels;
+ order = args.llquantize_order;
+ next = args.llquantize_next;
+ step = args.llquantize_step;
+ value = args.llquantize_value;
+
+ xo_open_list("buckets");
+ if (first_bin == 0) {
+ /*
+ * We have to represent < value somehow in JSON, so we bundle an
+ * optional "operator" in llquantize buckets.
+ */
+ xo_open_instance("buckets");
+ xo_emit("{:value/%lld} {:count/%lld} {:operator/%s}",
+ (long long)value, (long long)data[0] / normal, "<");
+ xo_close_instance("buckets");
+ }
+
+ while (order <= high) {
+ if (bin >= first_bin && bin <= last_bin) {
+ xo_open_instance("buckets");
+ xo_emit("{:value/%lld} {:count/%lld}", (long long)value,
+ (long long)data[bin] / normal);
+ xo_close_instance("buckets");
+ }
+
+ assert(value < next);
+ bin++;
+
+ if ((value += step) != next)
+ continue;
+
+ next = value * factor;
+ step = next > nsteps ? next / nsteps : 1;
+ order++;
+ }
+
+ if (last_bin < bin) {
+ xo_close_list("buckets");
+ return (0);
+ }
+
+ assert(last_bin == bin);
+ xo_open_instance("buckets");
+ xo_emit("{:value/%lld} {:count/%lld} {:operator/%s}", (long long)value,
+ (long long)data[bin] / normal, ">=");
+ xo_close_instance("buckets");
+
+ xo_close_list("buckets");
+ return (0);
+}
+
+int
+dt_print_llquantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr,
+ size_t size, uint64_t normal)
+{
+ int i, first_bin, last_bin, bin = 1, order, levels;
+ uint16_t factor, low, high, nsteps;
+ const int64_t *data;
+ dt_prepare_args_t args = { 0 };
+ int64_t value = 1, next, step;
+ char positives = 0, negatives = 0;
+ long double total = 0;
+ uint64_t arg;
+ char c[32];
+
+ data = dt_format_llquantize_prepare(dtp, addr, size, &args);
+ /* dt_errno is set for us */
+ if (data == NULL)
+ return (-1);
+
+ first_bin = args.first_bin;
+ last_bin = args.last_bin;
+ factor = args.llquantize_factor;
+ low = args.llquantize_low;
+ high = args.llquantize_high;
+ nsteps = args.llquantize_nsteps;
+ levels = args.llquantize_levels;
+ order = args.llquantize_order;
+ next = args.llquantize_next;
+ step = args.llquantize_step;
+ value = args.llquantize_value;
+
for (i = first_bin; i <= last_bin; i++) {
positives |= (data[i] > 0);
negatives |= (data[i] < 0);
@@ -1054,12 +1349,6 @@
"------------- Distribution -------------", "count") < 0)
return (-1);
- for (order = 0; order < low; order++)
- value *= factor;
-
- next = value * factor;
- step = next > nsteps ? next / nsteps : 1;
-
if (first_bin == 0) {
(void) snprintf(c, sizeof (c), "< %lld", (long long)value);
@@ -1105,6 +1394,16 @@
total, positives, negatives));
}
+static int
+dt_format_average(dtrace_hdl_t *dtp, caddr_t addr, size_t size, uint64_t normal)
+{
+ int64_t *data = (int64_t *)addr;
+
+ xo_emit("{:average/%lld}",
+ data[0] ? (long long)(data[1] / (int64_t)normal / data[0]) : 0);
+ return (0);
+}
+
/*ARGSUSED*/
static int
dt_print_average(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr,
@@ -1117,6 +1416,16 @@
(long long)(data[1] / (int64_t)normal / data[0]) : 0));
}
+static int
+dt_format_stddev(dtrace_hdl_t *dtp, caddr_t addr, size_t size, uint64_t normal)
+{
+ uint64_t *data = (uint64_t *)addr;
+
+ xo_emit("{:stddev/%llu}",
+ data[0] ? (unsigned long long)dt_stddev(data, normal) : 0);
+ return (0);
+}
+
/*ARGSUSED*/
static int
dt_print_stddev(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr,
@@ -1249,35 +1558,21 @@
}
int
-dt_print_stack(dtrace_hdl_t *dtp, FILE *fp, const char *format,
- caddr_t addr, int depth, int size)
+dt_format_stack(dtrace_hdl_t *dtp, caddr_t addr, int depth, int size)
{
dtrace_syminfo_t dts;
GElf_Sym sym;
- int i, indent;
- char c[PATH_MAX * 2];
+ int i;
uint64_t pc;
- if (dt_printf(dtp, fp, "\n") < 0)
- return (-1);
-
- if (format == NULL)
- format = "%s";
-
- if (dtp->dt_options[DTRACEOPT_STACKINDENT] != DTRACEOPT_UNSET)
- indent = (int)dtp->dt_options[DTRACEOPT_STACKINDENT];
- else
- indent = _dtrace_stkindent;
-
+ xo_open_list("stack-frames");
for (i = 0; i < depth; i++) {
switch (size) {
case sizeof (uint32_t):
- /* LINTED - alignment */
pc = *((uint32_t *)addr);
break;
case sizeof (uint64_t):
- /* LINTED - alignment */
pc = *((uint64_t *)addr);
break;
@@ -1290,16 +1585,19 @@
addr += size;
- if (dt_printf(dtp, fp, "%*s", indent, "") < 0)
- return (-1);
-
+ xo_open_instance("stack-frames");
if (dtrace_lookup_by_addr(dtp, pc, &sym, &dts) == 0) {
if (pc > sym.st_value) {
- (void) snprintf(c, sizeof (c), "%s`%s+0x%llx",
+ xo_emit("{:symbol/%s`%s+0x%llx} {:module/%s} "
+ "{:name/%s} {:offset/0x%llx}",
+ dts.dts_object, dts.dts_name,
+ (u_longlong_t)(pc - sym.st_value),
dts.dts_object, dts.dts_name,
(u_longlong_t)(pc - sym.st_value));
} else {
- (void) snprintf(c, sizeof (c), "%s`%s",
+ xo_emit("{:symbol/%s`%s} {:module/%s} "
+ "{:name/%s}",
+ dts.dts_object, dts.dts_name,
dts.dts_object, dts.dts_name);
}
} else {
@@ -1309,29 +1607,25 @@
* interested in the containing module.
*/
if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) {
- (void) snprintf(c, sizeof (c), "%s`0x%llx",
+ xo_emit("{:symbol/%s`0x%llx} {:module/%s} "
+ "{:offset/0x%llx}",
+ dts.dts_object, (u_longlong_t)pc,
dts.dts_object, (u_longlong_t)pc);
} else {
- (void) snprintf(c, sizeof (c), "0x%llx",
- (u_longlong_t)pc);
+ xo_emit("{:symbol/0x%llx} {:offset/0x%llx}",
+ (u_longlong_t)pc, (u_longlong_t)pc);
}
}
-
- if (dt_printf(dtp, fp, format, c) < 0)
- return (-1);
-
- if (dt_printf(dtp, fp, "\n") < 0)
- return (-1);
+ xo_close_instance("stack-frames");
}
+ xo_close_list("stack-frames");
return (0);
}
int
-dt_print_ustack(dtrace_hdl_t *dtp, FILE *fp, const char *format,
- caddr_t addr, uint64_t arg)
+dt_format_ustack(dtrace_hdl_t *dtp, caddr_t addr, uint64_t arg)
{
- /* LINTED - alignment */
uint64_t *pc = (uint64_t *)addr;
uint32_t depth = DTRACE_USTACK_NFRAMES(arg);
uint32_t strsize = DTRACE_USTACK_STRSIZE(arg);
@@ -1350,17 +1644,6 @@
pid = (pid_t)*pc++;
- if (dt_printf(dtp, fp, "\n") < 0)
- return (-1);
-
- if (format == NULL)
- format = "%s";
-
- if (dtp->dt_options[DTRACEOPT_STACKINDENT] != DTRACEOPT_UNSET)
- indent = (int)dtp->dt_options[DTRACEOPT_STACKINDENT];
- else
- indent = _dtrace_stkindent;
-
/*
* Ultimately, we need to add an entry point in the library vector for
* determining from . For now, if
@@ -1374,23 +1657,27 @@
if (P != NULL)
dt_proc_lock(dtp, P); /* lock handle while we perform lookups */
+ xo_open_list("ustack-frames");
for (i = 0; i < depth && pc[i] != 0; i++) {
const prmap_t *map;
- if ((err = dt_printf(dtp, fp, "%*s", indent, "")) < 0)
- break;
-
+ xo_open_instance("ustack-frames");
if (P != NULL && Plookup_by_addr(P, pc[i],
name, sizeof (name), &sym) == 0) {
(void) Pobjname(P, pc[i], objname, sizeof (objname));
if (pc[i] > sym.st_value) {
- (void) snprintf(c, sizeof (c),
- "%s`%s+0x%llx", dt_basename(objname), name,
+ xo_emit("{:symbol/%s`%s+0x%llx} {:module/%s} "
+ "{:name/%s} {:offset/0x%llx}",
+ dt_basename(objname), name,
+ (u_longlong_t)(pc[i] - sym.st_value),
+ dt_basename(objname), name,
(u_longlong_t)(pc[i] - sym.st_value));
} else {
- (void) snprintf(c, sizeof (c),
- "%s`%s", dt_basename(objname), name);
+ xo_emit("{:symbol/%s`%s} {:module/%s} "
+ "{:name/%s}",
+ dt_basename(objname), name,
+ dt_basename(objname), name);
}
} else if (str != NULL && str[0] != '\0' && str[0] != '@' &&
(P != NULL && ((map = Paddr_to_map(P, pc[i])) == NULL ||
@@ -1408,24 +1695,20 @@
* writable, we assume that we have fallen into this
* case and we refuse to use the string.
*/
- (void) snprintf(c, sizeof (c), "%s", str);
+ xo_emit("{:symbol/%s}", str);
} else {
if (P != NULL && Pobjname(P, pc[i], objname,
sizeof (objname)) != 0) {
- (void) snprintf(c, sizeof (c), "%s`0x%llx",
+ xo_emit("{:symbol/%s`0x%llx} {:module/%s} "
+ "{:offset/0x%llx}",
+ dt_basename(objname), (u_longlong_t)pc[i],
dt_basename(objname), (u_longlong_t)pc[i]);
} else {
- (void) snprintf(c, sizeof (c), "0x%llx",
- (u_longlong_t)pc[i]);
+ xo_emit("{:symbol/0x%llx} {:offset/0x%llx}",
+ (u_longlong_t)pc[i], (u_longlong_t)pc[i]);
}
}
- if ((err = dt_printf(dtp, fp, format, c)) < 0)
- break;
-
- if ((err = dt_printf(dtp, fp, "\n")) < 0)
- break;
-
if (str != NULL && str[0] == '@') {
/*
* If the first character of the string is an "at" sign,
@@ -1433,16 +1716,7 @@
* and it is printed out beneath the frame and offset
* with brackets.
*/
- if ((err = dt_printf(dtp, fp, "%*s", indent, "")) < 0)
- break;
-
- (void) snprintf(c, sizeof (c), " [ %s ]", &str[1]);
-
- if ((err = dt_printf(dtp, fp, format, c)) < 0)
- break;
-
- if ((err = dt_printf(dtp, fp, "\n")) < 0)
- break;
+ xo_emit("{:annotation/%s}", &str[1]);
}
if (str != NULL) {
@@ -1450,7 +1724,9 @@
if (str - strbase >= strsize)
str = NULL;
}
+ xo_close_instance("ustack-frames");
}
+ xo_close_list("ustack-frames");
if (P != NULL) {
dt_proc_unlock(dtp, P);
@@ -1460,8 +1736,255 @@
return (err);
}
-static int
-dt_print_usym(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, dtrace_actkind_t act)
+int
+dt_print_stack(dtrace_hdl_t *dtp, FILE *fp, const char *format,
+ caddr_t addr, int depth, int size)
+{
+ dtrace_syminfo_t dts;
+ GElf_Sym sym;
+ int i, indent;
+ char c[PATH_MAX * 2];
+ uint64_t pc;
+
+ if (dt_printf(dtp, fp, "\n") < 0)
+ return (-1);
+
+ if (format == NULL)
+ format = "%s";
+
+ if (dtp->dt_options[DTRACEOPT_STACKINDENT] != DTRACEOPT_UNSET)
+ indent = (int)dtp->dt_options[DTRACEOPT_STACKINDENT];
+ else
+ indent = _dtrace_stkindent;
+
+ for (i = 0; i < depth; i++) {
+ switch (size) {
+ case sizeof (uint32_t):
+ /* LINTED - alignment */
+ pc = *((uint32_t *)addr);
+ break;
+
+ case sizeof (uint64_t):
+ /* LINTED - alignment */
+ pc = *((uint64_t *)addr);
+ break;
+
+ default:
+ return (dt_set_errno(dtp, EDT_BADSTACKPC));
+ }
+
+ if (pc == 0)
+ break;
+
+ addr += size;
+
+ if (dt_printf(dtp, fp, "%*s", indent, "") < 0)
+ return (-1);
+
+ if (dtrace_lookup_by_addr(dtp, pc, &sym, &dts) == 0) {
+ if (pc > sym.st_value) {
+ (void) snprintf(c, sizeof (c), "%s`%s+0x%llx",
+ dts.dts_object, dts.dts_name,
+ (u_longlong_t)(pc - sym.st_value));
+ } else {
+ (void) snprintf(c, sizeof (c), "%s`%s",
+ dts.dts_object, dts.dts_name);
+ }
+ } else {
+ /*
+ * We'll repeat the lookup, but this time we'll specify
+ * a NULL GElf_Sym -- indicating that we're only
+ * interested in the containing module.
+ */
+ if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) {
+ (void) snprintf(c, sizeof (c), "%s`0x%llx",
+ dts.dts_object, (u_longlong_t)pc);
+ } else {
+ (void) snprintf(c, sizeof (c), "0x%llx",
+ (u_longlong_t)pc);
+ }
+ }
+
+ if (dt_printf(dtp, fp, format, c) < 0)
+ return (-1);
+
+ if (dt_printf(dtp, fp, "\n") < 0)
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+dt_print_ustack(dtrace_hdl_t *dtp, FILE *fp, const char *format,
+ caddr_t addr, uint64_t arg)
+{
+ /* LINTED - alignment */
+ uint64_t *pc = (uint64_t *)addr;
+ uint32_t depth = DTRACE_USTACK_NFRAMES(arg);
+ uint32_t strsize = DTRACE_USTACK_STRSIZE(arg);
+ const char *strbase = addr + (depth + 1) * sizeof (uint64_t);
+ const char *str = strsize ? strbase : NULL;
+ int err = 0;
+
+ char name[PATH_MAX], objname[PATH_MAX], c[PATH_MAX * 2];
+ struct ps_prochandle *P;
+ GElf_Sym sym;
+ int i, indent;
+ pid_t pid;
+
+ if (depth == 0)
+ return (0);
+
+ pid = (pid_t)*pc++;
+
+ if (dt_printf(dtp, fp, "\n") < 0)
+ return (-1);
+
+ if (format == NULL)
+ format = "%s";
+
+ if (dtp->dt_options[DTRACEOPT_STACKINDENT] != DTRACEOPT_UNSET)
+ indent = (int)dtp->dt_options[DTRACEOPT_STACKINDENT];
+ else
+ indent = _dtrace_stkindent;
+
+ /*
+ * Ultimately, we need to add an entry point in the library vector for
+ * determining from . For now, if
+ * this is a vector open, we just print the raw address or string.
+ */
+ if (dtp->dt_vector == NULL)
+ P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0);
+ else
+ P = NULL;
+
+ if (P != NULL)
+ dt_proc_lock(dtp, P); /* lock handle while we perform lookups */
+
+ for (i = 0; i < depth && pc[i] != 0; i++) {
+ const prmap_t *map;
+
+ if ((err = dt_printf(dtp, fp, "%*s", indent, "")) < 0)
+ break;
+
+ if (P != NULL && Plookup_by_addr(P, pc[i],
+ name, sizeof (name), &sym) == 0) {
+ (void) Pobjname(P, pc[i], objname, sizeof (objname));
+
+ if (pc[i] > sym.st_value) {
+ (void) snprintf(c, sizeof (c),
+ "%s`%s+0x%llx", dt_basename(objname), name,
+ (u_longlong_t)(pc[i] - sym.st_value));
+ } else {
+ (void) snprintf(c, sizeof (c),
+ "%s`%s", dt_basename(objname), name);
+ }
+ } else if (str != NULL && str[0] != '\0' && str[0] != '@' &&
+ (P != NULL && ((map = Paddr_to_map(P, pc[i])) == NULL ||
+ (map->pr_mflags & MA_WRITE)))) {
+ /*
+ * If the current string pointer in the string table
+ * does not point to an empty string _and_ the program
+ * counter falls in a writable region, we'll use the
+ * string from the string table instead of the raw
+ * address. This last condition is necessary because
+ * some (broken) ustack helpers will return a string
+ * even for a program counter that they can't
+ * identify. If we have a string for a program
+ * counter that falls in a segment that isn't
+ * writable, we assume that we have fallen into this
+ * case and we refuse to use the string.
+ */
+ (void) snprintf(c, sizeof (c), "%s", str);
+ } else {
+ if (P != NULL && Pobjname(P, pc[i], objname,
+ sizeof (objname)) != 0) {
+ (void) snprintf(c, sizeof (c), "%s`0x%llx",
+ dt_basename(objname), (u_longlong_t)pc[i]);
+ } else {
+ (void) snprintf(c, sizeof (c), "0x%llx",
+ (u_longlong_t)pc[i]);
+ }
+ }
+
+ if ((err = dt_printf(dtp, fp, format, c)) < 0)
+ break;
+
+ if ((err = dt_printf(dtp, fp, "\n")) < 0)
+ break;
+
+ if (str != NULL && str[0] == '@') {
+ /*
+ * If the first character of the string is an "at" sign,
+ * then the string is inferred to be an annotation --
+ * and it is printed out beneath the frame and offset
+ * with brackets.
+ */
+ if ((err = dt_printf(dtp, fp, "%*s", indent, "")) < 0)
+ break;
+
+ (void) snprintf(c, sizeof (c), " [ %s ]", &str[1]);
+
+ if ((err = dt_printf(dtp, fp, format, c)) < 0)
+ break;
+
+ if ((err = dt_printf(dtp, fp, "\n")) < 0)
+ break;
+ }
+
+ if (str != NULL) {
+ str += strlen(str) + 1;
+ if (str - strbase >= strsize)
+ str = NULL;
+ }
+ }
+
+ if (P != NULL) {
+ dt_proc_unlock(dtp, P);
+ dt_proc_release(dtp, P);
+ }
+
+ return (err);
+}
+
+static int
+dt_format_usym(dtrace_hdl_t *dtp, caddr_t addr, dtrace_actkind_t act)
+{
+ uint64_t pid = ((uint64_t *)addr)[0];
+ uint64_t pc = ((uint64_t *)addr)[1];
+ char *s;
+ int n, len = 256;
+
+ if (act == DTRACEACT_USYM && dtp->dt_vector == NULL) {
+ struct ps_prochandle *P;
+
+ if ((P = dt_proc_grab(dtp, pid,
+ PGRAB_RDONLY | PGRAB_FORCE, 0)) != NULL) {
+ GElf_Sym sym;
+
+ dt_proc_lock(dtp, P);
+
+ if (Plookup_by_addr(P, pc, NULL, 0, &sym) == 0)
+ pc = sym.st_value;
+
+ dt_proc_unlock(dtp, P);
+ dt_proc_release(dtp, P);
+ }
+ }
+
+ do {
+ n = len;
+ s = alloca(n);
+ } while ((len = dtrace_uaddr2str(dtp, pid, pc, s, n)) > n);
+
+ xo_emit("{:usym/%s}", s);
+ return (0);
+}
+
+
+static int
+dt_print_usym(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, dtrace_actkind_t act)
{
/* LINTED - alignment */
uint64_t pid = ((uint64_t *)addr)[0];
@@ -1496,6 +2019,42 @@
return (dt_printf(dtp, fp, format, s));
}
+int
+dt_format_umod(dtrace_hdl_t *dtp, caddr_t addr)
+{
+ uint64_t pid = ((uint64_t *)addr)[0];
+ uint64_t pc = ((uint64_t *)addr)[1];
+ int err = 0;
+
+ char objname[PATH_MAX];
+ struct ps_prochandle *P;
+
+ /*
+ * See the comment in dt_print_ustack() for the rationale for
+ * printing raw addresses in the vectored case.
+ */
+ if (dtp->dt_vector == NULL)
+ P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0);
+ else
+ P = NULL;
+
+ if (P != NULL)
+ dt_proc_lock(dtp, P); /* lock handle while we perform lookups */
+
+ if (P != NULL && Pobjname(P, pc, objname, sizeof (objname)) != 0) {
+ xo_emit("{:umod/%s}", dt_basename(objname));
+ } else {
+ xo_emit("{:umod/0x%llx}", (u_longlong_t)pc);
+ }
+
+ if (P != NULL) {
+ dt_proc_unlock(dtp, P);
+ dt_proc_release(dtp, P);
+ }
+
+ return (0);
+}
+
int
dt_print_umod(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr)
{
@@ -1552,8 +2111,13 @@
format = " %-50s";
if (dtrace_lookup_by_addr(dtp, pc, &sym, &dts) == 0) {
- (void) snprintf(c, sizeof (c), "%s`%s",
- dts.dts_object, dts.dts_name);
+ if (dtp->dt_oformat)
+ xo_emit("{:sym/%s`%s} {:object/%s} {:name/%s}",
+ dts.dts_object, dts.dts_name, dts.dts_object,
+ dts.dts_name);
+ else
+ (void) snprintf(c, sizeof (c), "%s`%s",
+ dts.dts_object, dts.dts_name);
} else {
/*
* We'll repeat the lookup, but this time we'll specify a
@@ -1561,20 +2125,46 @@
* the containing module.
*/
if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) {
- (void) snprintf(c, sizeof (c), "%s`0x%llx",
- dts.dts_object, (u_longlong_t)pc);
+ if (dtp->dt_oformat)
+ xo_emit("{:sym/%s`0x%llx} {:object/%s} "
+ "{:offset/0x%llx}",
+ dts.dts_object, (u_longlong_t)pc,
+ dts.dts_object, (u_longlong_t)pc);
+ else
+ (void) snprintf(c, sizeof (c), "%s`0x%llx",
+ dts.dts_object, (u_longlong_t)pc);
} else {
- (void) snprintf(c, sizeof (c), "0x%llx",
- (u_longlong_t)pc);
+ if (dtp->dt_oformat)
+ xo_emit("{:sym/0x%llx} {:offset/0x%llx}",
+ (u_longlong_t)pc, (u_longlong_t)pc);
+ else
+ (void) snprintf(c, sizeof (c), "0x%llx",
+ (u_longlong_t)pc);
}
}
- if (dt_printf(dtp, fp, format, c) < 0)
+ if (dtp->dt_oformat != 0 && dt_printf(dtp, fp, format, c) < 0)
return (-1);
return (0);
}
+int
+dt_format_mod(dtrace_hdl_t *dtp, caddr_t addr)
+{
+ /* LINTED - alignment */
+ uint64_t pc = *((uint64_t *)addr);
+ dtrace_syminfo_t dts;
+
+ if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) {
+ xo_emit("{:mod/%s}", dts.dts_object);
+ } else {
+ xo_emit("{:mod/0x%llx}", (u_longlong_t)pc);
+ }
+
+ return (0);
+}
+
int
dt_print_mod(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr)
{
@@ -1598,6 +2188,74 @@
return (0);
}
+static char *
+dt_format_bytes_get(dtrace_hdl_t *dtp, caddr_t addr, size_t nbytes)
+{
+ char *s = dt_alloc(dtp, nbytes * 2 + 2 + 1); /* 2 bytes per byte + 0x + '\0' */
+ char t[6];
+ char *c = (char *)addr;
+ size_t i, j;
+
+ if (s == NULL)
+ return (NULL);
+
+ /*
+ * XXX: Some duplication with dt_print_bytes().
+ */
+ for (i = 0; i < nbytes; i++) {
+ if (isprint(c[i]) || isspace(c[i]) || c[i] == '\b' || c[i] == '\a')
+ continue;
+
+ if (c[i] == '\0' && i > 0) {
+ for (j = i + 1; j < nbytes; j++) {
+ if (c[j] != '\0')
+ break;
+ }
+
+ if (j != nbytes)
+ break;
+
+ memcpy(s, c, nbytes);
+ return (s);
+ }
+
+ break;
+ }
+
+ if (i == nbytes) {
+ memcpy(s, c, nbytes);
+ s[nbytes] = '\0';
+ return (s);
+ }
+
+ s[0] = '0';
+ s[1] = 'x';
+ for (i = 0; i < nbytes; i++) {
+ snprintf(t, sizeof(t), "%02x", (uchar_t)c[i]);
+ memcpy(s + (i * 2) + 2, t, 2);
+ }
+
+ s[nbytes * 2 + 2] = 0;
+ return (s);
+}
+
+static int
+dt_format_memory(dtrace_hdl_t *dtp, caddr_t addr)
+{
+
+ size_t nbytes = *((uintptr_t *) addr);
+ char *s;
+
+ s = dt_format_bytes_get(dtp, addr + sizeof(uintptr_t), nbytes);
+ if (s == NULL)
+ return (-1);
+
+ xo_emit("{:printm/%s}", s);
+ dt_free(dtp, s);
+
+ return (0);
+}
+
static int
dt_print_memory(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr)
{
@@ -1745,60 +2403,144 @@
int64_t remaining;
int (*func)(dtrace_hdl_t *, dtrace_aggregate_f *, void *);
- /*
- * We (should) have two records: the aggregation ID followed by the
- * number of aggregation entries after which the aggregation is to be
- * truncated.
- */
- addr = base + rec->dtrd_offset;
+ /*
+ * We (should) have two records: the aggregation ID followed by the
+ * number of aggregation entries after which the aggregation is to be
+ * truncated.
+ */
+ addr = base + rec->dtrd_offset;
+
+ if (rec->dtrd_size != sizeof (dtrace_aggvarid_t))
+ return (dt_set_errno(dtp, EDT_BADTRUNC));
+
+ /* LINTED - alignment */
+ trunc.dttd_id = *((dtrace_aggvarid_t *)addr);
+ rec++;
+
+ if (rec->dtrd_action != DTRACEACT_LIBACT)
+ return (dt_set_errno(dtp, EDT_BADTRUNC));
+
+ if (rec->dtrd_arg != DT_ACT_TRUNC)
+ return (dt_set_errno(dtp, EDT_BADTRUNC));
+
+ addr = base + rec->dtrd_offset;
+
+ switch (rec->dtrd_size) {
+ case sizeof (uint64_t):
+ /* LINTED - alignment */
+ remaining = *((int64_t *)addr);
+ break;
+ case sizeof (uint32_t):
+ /* LINTED - alignment */
+ remaining = *((int32_t *)addr);
+ break;
+ case sizeof (uint16_t):
+ /* LINTED - alignment */
+ remaining = *((int16_t *)addr);
+ break;
+ case sizeof (uint8_t):
+ remaining = *((int8_t *)addr);
+ break;
+ default:
+ return (dt_set_errno(dtp, EDT_BADNORMAL));
+ }
+
+ if (remaining < 0) {
+ func = dtrace_aggregate_walk_valsorted;
+ remaining = -remaining;
+ } else {
+ func = dtrace_aggregate_walk_valrevsorted;
+ }
+
+ assert(remaining >= 0);
+ trunc.dttd_remaining = remaining;
+
+ (void) func(dtp, dt_trunc_agg, &trunc);
+
+ return (0);
+}
+
+static int
+dt_format_datum(dtrace_hdl_t *dtp, dtrace_recdesc_t *rec, caddr_t addr,
+ size_t size, const dtrace_aggdata_t *aggdata, uint64_t normal,
+ dt_print_aggdata_t *pd)
+{
+ dtrace_actkind_t act = rec->dtrd_action;
+ boolean_t packed = pd->dtpa_agghist || pd->dtpa_aggpack;
+ dtrace_aggdesc_t *agg = aggdata->dtada_desc;
+ char fmt[512];
+ char *s;
+
+ if (packed && pd->dtpa_agghisthdr != agg->dtagd_varid)
+ pd->dtpa_agghisthdr = agg->dtagd_varid;
+
+ switch (act) {
+ case DTRACEACT_STACK:
+ return (dt_format_stack(dtp, addr, rec->dtrd_arg,
+ rec->dtrd_size / rec->dtrd_arg));
+
+ case DTRACEACT_USTACK:
+ case DTRACEACT_JSTACK:
+ return (dt_format_ustack(dtp, addr, rec->dtrd_arg));
+
+ case DTRACEACT_USYM:
+ case DTRACEACT_UADDR:
+ return (dt_format_usym(dtp, addr, act));
+
+ case DTRACEACT_UMOD:
+ return (dt_format_umod(dtp, addr));
+
+ case DTRACEACT_SYM:
+ return (dt_format_sym(dtp, addr));
+ case DTRACEACT_MOD:
+ return (dt_format_mod(dtp, addr));
- if (rec->dtrd_size != sizeof (dtrace_aggvarid_t))
- return (dt_set_errno(dtp, EDT_BADTRUNC));
+ case DTRACEAGG_QUANTIZE:
+ return (dt_format_quantize(dtp, addr, size, normal));
- /* LINTED - alignment */
- trunc.dttd_id = *((dtrace_aggvarid_t *)addr);
- rec++;
+ case DTRACEAGG_LQUANTIZE:
+ return (dt_format_lquantize(dtp, addr, size, normal));
- if (rec->dtrd_action != DTRACEACT_LIBACT)
- return (dt_set_errno(dtp, EDT_BADTRUNC));
+ case DTRACEAGG_LLQUANTIZE:
+ return (dt_format_llquantize(dtp, addr, size, normal));
- if (rec->dtrd_arg != DT_ACT_TRUNC)
- return (dt_set_errno(dtp, EDT_BADTRUNC));
+ case DTRACEAGG_AVG:
+ return (dt_format_average(dtp, addr, size, normal));
- addr = base + rec->dtrd_offset;
+ case DTRACEAGG_STDDEV:
+ return (dt_format_stddev(dtp, addr, size, normal));
- switch (rec->dtrd_size) {
+ default:
+ break;
+ }
+
+ switch (size) {
case sizeof (uint64_t):
- /* LINTED - alignment */
- remaining = *((int64_t *)addr);
+ snprintf(fmt, sizeof(fmt), "{:%s/%%lld}", pd->dtpa_keyname);
+ xo_emit(fmt, (long long)*((uint64_t *)addr) / normal);
break;
case sizeof (uint32_t):
- /* LINTED - alignment */
- remaining = *((int32_t *)addr);
+ snprintf(fmt, sizeof(fmt), "{:%s/%%d}", pd->dtpa_keyname);
+ xo_emit(fmt, *((uint32_t *)addr) / (uint32_t)normal);
break;
case sizeof (uint16_t):
- /* LINTED - alignment */
- remaining = *((int16_t *)addr);
+ snprintf(fmt, sizeof(fmt), "{:%s/%%d}", pd->dtpa_keyname);
+ xo_emit(fmt, *((uint16_t *)addr) / (uint32_t)normal);
break;
case sizeof (uint8_t):
- remaining = *((int8_t *)addr);
+ snprintf(fmt, sizeof(fmt), "{:%s/%%d}", pd->dtpa_keyname);
+ xo_emit(fmt, *((uint8_t *)addr) / (uint32_t)normal);
break;
default:
- return (dt_set_errno(dtp, EDT_BADNORMAL));
- }
+ s = dt_format_bytes_get(dtp, addr, size);
+ if (s == NULL)
+ return (-1);
- if (remaining < 0) {
- func = dtrace_aggregate_walk_valsorted;
- remaining = -remaining;
- } else {
- func = dtrace_aggregate_walk_valrevsorted;
+ xo_emit("{:value/%s}", s);
+ dt_free(dtp, s);
+ break;
}
- assert(remaining >= 0);
- trunc.dttd_remaining = remaining;
-
- (void) func(dtp, dt_trunc_agg, &trunc);
-
return (0);
}
@@ -1956,6 +2698,119 @@
return (err);
}
+int
+dt_format_aggs(const dtrace_aggdata_t **aggsdata, int naggvars, void *arg)
+{
+ int i, aggact = 0;
+ dt_print_aggdata_t *pd = arg;
+ const dtrace_aggdata_t *aggdata = aggsdata[0];
+ dtrace_aggdesc_t *agg = aggdata->dtada_desc;
+ dtrace_hdl_t *dtp = pd->dtpa_dtp;
+ dtrace_recdesc_t *rec;
+ dtrace_actkind_t act;
+ caddr_t addr;
+ size_t size;
+
+ if (pd->dtpa_aggname == NULL)
+ pd->dtpa_aggname = agg->dtagd_name;
+
+ xo_open_instance("aggregation-data");
+ strcpy(pd->dtpa_keyname, "value");
+ xo_open_list("keys");
+
+ /*
+ * Iterate over each record description in the key, printing the traced
+ * data, skipping the first datum (the tuple member created by the
+ * compiler).
+ */
+ for (i = 1; i < agg->dtagd_nrecs; i++) {
+ rec = &agg->dtagd_rec[i];
+ act = rec->dtrd_action;
+ addr = aggdata->dtada_data + rec->dtrd_offset;
+ size = rec->dtrd_size;
+
+ if (DTRACEACT_ISAGG(act)) {
+ aggact = i;
+ break;
+ }
+
+ xo_open_instance("keys");
+ if (dt_format_datum(dtp, rec, addr,
+ size, aggdata, 1, pd) < 0) {
+ xo_close_instance("keys");
+ xo_close_instance("aggregation-data");
+ return (-1);
+ }
+ xo_close_instance("keys");
+
+ if (dt_buffered_flush(dtp, NULL, rec, aggdata,
+ DTRACE_BUFDATA_AGGKEY) < 0) {
+ xo_close_instance("aggregation-data");
+ return (-1);
+ }
+ }
+ xo_close_list("keys");
+
+ assert(aggact != 0);
+
+ for (i = (naggvars == 1 ? 0 : 1); i < naggvars; i++) {
+ uint64_t normal;
+
+ aggdata = aggsdata[i];
+ agg = aggdata->dtada_desc;
+ rec = &agg->dtagd_rec[aggact];
+ act = rec->dtrd_action;
+ addr = aggdata->dtada_data + rec->dtrd_offset;
+ size = rec->dtrd_size;
+
+ assert(DTRACEACT_ISAGG(act));
+
+ switch (act) {
+ case DTRACEAGG_MIN:
+ strcpy(pd->dtpa_keyname, "min");
+ break;
+ case DTRACEAGG_MAX:
+ strcpy(pd->dtpa_keyname, "max");
+ break;
+ case DTRACEAGG_COUNT:
+ strcpy(pd->dtpa_keyname, "count");
+ break;
+ case DTRACEAGG_SUM:
+ strcpy(pd->dtpa_keyname, "sum");
+ break;
+ default:
+ strcpy(pd->dtpa_keyname, "UNKNOWN");
+ break;
+ }
+
+ normal = aggdata->dtada_normal;
+
+ if (dt_format_datum(dtp, rec, addr, size,
+ aggdata, normal, pd) < 0) {
+ xo_close_instance("aggregation-data");
+ return (-1);
+ }
+
+ if (dt_buffered_flush(dtp, NULL, rec, aggdata,
+ DTRACE_BUFDATA_AGGVAL) < 0) {
+ xo_close_instance("aggregation-data");
+ return (-1);
+ }
+
+ if (!pd->dtpa_allunprint)
+ agg->dtagd_flags |= DTRACE_AGD_PRINTED;
+ }
+
+ if (dt_buffered_flush(dtp, NULL, NULL, aggdata,
+ DTRACE_BUFDATA_AGGFORMAT | DTRACE_BUFDATA_AGGLAST) < 0) {
+ xo_close_instance("aggregation-data");
+ return (-1);
+ }
+
+ xo_close_instance("aggregation-data");
+ return (0);
+}
+
int
dt_print_aggs(const dtrace_aggdata_t **aggsdata, int naggvars, void *arg)
{
@@ -2037,6 +2892,33 @@
return (0);
}
+int
+dt_format_agg(const dtrace_aggdata_t *aggdata, void *arg)
+{
+ dt_print_aggdata_t *pd = arg;
+ dtrace_aggdesc_t *agg = aggdata->dtada_desc;
+ dtrace_aggvarid_t aggvarid = pd->dtpa_id;
+
+ if (pd->dtpa_allunprint) {
+ if (agg->dtagd_flags & DTRACE_AGD_PRINTED)
+ return (0);
+ } else {
+ /*
+ * If we're not printing all unprinted aggregations, then the
+ * aggregation variable ID denotes a specific aggregation
+ * variable that we should print -- skip any other aggregations
+ * that we encounter.
+ */
+ if (agg->dtagd_nrecs == 0)
+ return (0);
+
+ if (aggvarid != agg->dtagd_varid)
+ return (0);
+ }
+
+ return (dt_format_aggs(&aggdata, 1, arg));
+}
+
int
dt_print_agg(const dtrace_aggdata_t *aggdata, void *arg)
{
@@ -2101,6 +2983,33 @@
return (rval);
}
+/*
+ * Helper functions to help maintain style(9) in dt_consume_cpu().
+ */
+static int
+dt_oformat_agg_sorted(dtrace_hdl_t *dtp, dtrace_aggregate_f *func,
+ dt_print_aggdata_t *pd)
+{
+ int r;
+
+ r = dtrace_aggregate_walk_sorted(dtp, dt_format_agg, pd);
+ if (r < 0) {
+ xo_close_list("aggregation-data");
+ xo_emit("{:aggregation-name/%s}", pd->dtpa_aggname);
+ xo_close_instance("output");
+ }
+
+ return (r);
+}
+
+static void
+dt_oformat_agg_name(dt_print_aggdata_t *pd)
+{
+
+ xo_close_list("aggregation-data");
+ xo_emit("{:aggregation-name/%s}", pd->dtpa_aggname);
+}
+
static int
dt_consume_cpu(dtrace_hdl_t *dtp, FILE *fp, int cpu,
dtrace_bufdesc_t *buf, boolean_t just_one,
@@ -2114,6 +3023,7 @@
uint64_t tracememsize = 0;
dtrace_probedata_t data;
uint64_t drops;
+ size_t skip_format;
bzero(&data, sizeof (data));
data.dtpda_handle = dtp;
@@ -2145,6 +3055,8 @@
epd = data.dtpda_edesc;
data.dtpda_data = buf->dtbd_data + offs;
+ data.dtpda_timestamp = DTRACE_RECORD_LOAD_TIMESTAMP(
+ (struct dtrace_rechdr *)data.dtpda_data);
if (data.dtpda_edesc->dtepd_uarg != DT_ECB_DEFAULT) {
rval = dt_handle(dtp, &data);
@@ -2160,6 +3072,8 @@
(void) dt_flowindent(dtp, &data, dtp->dt_last_epid,
buf, offs);
+ if (dtp->dt_oformat)
+ xo_open_instance("probes");
rval = (*efunc)(&data, arg);
if (flow) {
@@ -2176,11 +3090,17 @@
if (rval != DTRACE_CONSUME_THIS)
return (dt_set_errno(dtp, EDT_BADRVAL));
+ skip_format = 0;
+ if (dtp->dt_oformat)
+ xo_open_list("output");
for (i = 0; i < epd->dtepd_nrecs; i++) {
caddr_t addr;
dtrace_recdesc_t *rec = &epd->dtepd_rec[i];
dtrace_actkind_t act = rec->dtrd_action;
+ if (skip_format > 0)
+ skip_format--;
+
data.dtpda_data = buf->dtbd_data + offs +
rec->dtrd_offset;
addr = data.dtpda_data;
@@ -2301,54 +3221,109 @@
if (rval != DTRACE_CONSUME_THIS)
return (dt_set_errno(dtp, EDT_BADRVAL));
+ if (dtp->dt_oformat && rec->dtrd_size > 0)
+ xo_open_instance("output");
if (act == DTRACEACT_STACK) {
int depth = rec->dtrd_arg;
- if (dt_print_stack(dtp, fp, NULL, addr, depth,
- rec->dtrd_size / depth) < 0)
+ if (dtp->dt_oformat) {
+ if (dt_format_stack(dtp, addr, depth,
+ rec->dtrd_size / depth) < 0) {
+ xo_close_instance("output");
+ return (-1);
+ }
+ } else {
+ if (dt_print_stack(dtp,
+ fp, NULL, addr, depth,
+ rec->dtrd_size / depth) < 0)
return (-1);
+ }
goto nextrec;
}
if (act == DTRACEACT_USTACK ||
act == DTRACEACT_JSTACK) {
- if (dt_print_ustack(dtp, fp, NULL,
- addr, rec->dtrd_arg) < 0)
- return (-1);
+ if (dtp->dt_oformat) {
+ if (dt_format_ustack(dtp, addr,
+ rec->dtrd_arg) < 0) {
+ xo_close_instance("output");
+ return (-1);
+ }
+ } else {
+ if (dt_print_ustack(dtp, fp, NULL,
+ addr, rec->dtrd_arg) < 0)
+ return (-1);
+ }
goto nextrec;
}
if (act == DTRACEACT_SYM) {
- if (dt_print_sym(dtp, fp, NULL, addr) < 0)
- return (-1);
+ if (dtp->dt_oformat) {
+ if (dt_format_sym(dtp, addr) < 0) {
+ xo_close_instance("output");
+ return (-1);
+ }
+ } else {
+ if (dt_print_sym(dtp, fp, NULL, addr) < 0)
+ return (-1);
+ }
goto nextrec;
}
if (act == DTRACEACT_MOD) {
- if (dt_print_mod(dtp, fp, NULL, addr) < 0)
- return (-1);
+ if (dtp->dt_oformat) {
+ if (dt_format_mod(dtp, addr) < 0) {
+ xo_close_instance("output");
+ return (-1);
+ }
+ } else {
+ if (dt_print_mod(dtp, fp, NULL, addr) < 0)
+ return (-1);
+ }
goto nextrec;
}
if (act == DTRACEACT_USYM || act == DTRACEACT_UADDR) {
- if (dt_print_usym(dtp, fp, addr, act) < 0)
- return (-1);
+ if (dtp->dt_oformat) {
+ if (dt_format_usym(dtp, addr, act) < 0) {
+ xo_close_instance("output");
+ return (-1);
+ }
+ } else {
+ if (dt_print_usym(dtp, fp, addr, act) < 0)
+ return (-1);
+ }
goto nextrec;
}
if (act == DTRACEACT_UMOD) {
- if (dt_print_umod(dtp, fp, NULL, addr) < 0)
- return (-1);
+ if (dtp->dt_oformat) {
+ if (dt_format_umod(dtp, addr) < 0) {
+ xo_close_instance("output");
+ return (-1);
+ }
+ } else {
+ if (dt_print_umod(dtp, fp, NULL, addr) < 0)
+ return (-1);
+ }
goto nextrec;
}
if (act == DTRACEACT_PRINTM) {
- if (dt_print_memory(dtp, fp, addr) < 0)
- return (-1);
+ if (dtp->dt_oformat) {
+ if (dt_format_memory(dtp, addr) < 0) {
+ xo_close_instance("output");
+ return (-1);
+ }
+ } else {
+ if (dt_print_memory(dtp, fp, addr) < 0)
+ return (-1);
+ }
goto nextrec;
}
- if (DTRACEACT_ISPRINTFLIKE(act)) {
+ if (dtp->dt_oformat == DTRACE_OFORMAT_TEXT &&
+ DTRACEACT_ISPRINTFLIKE(act)) {
void *fmtdata;
int (*func)(dtrace_hdl_t *, FILE *, void *,
const dtrace_probedata_t *,
@@ -2387,6 +3362,54 @@
goto nextrec;
}
+ /*
+ * We don't care about a formatted printa, system or
+ * freopen for oformat.
+ */
+ if (dtp->dt_oformat && act == DTRACEACT_PRINTF &&
+ skip_format == 0) {
+ void *fmtdata;
+ if ((fmtdata = dt_format_lookup(dtp,
+ rec->dtrd_format)) == NULL)
+ goto nofmt;
+
+ n = dtrace_sprintf(dtp, fp, fmtdata, rec,
+ epd->dtepd_nrecs - i,
+ (uchar_t *)buf->dtbd_data + offs,
+ buf->dtbd_size - offs);
+
+ if (n < 0) {
+ xo_close_instance("output");
+ return (-1); /* errno is set for us */
+ }
+
+ xo_emit("{:message/%s}", dtp->dt_sprintf_buf);
+ skip_format += n;
+
+ /*
+ * We want the "message" object to be its own
+ * thing, but we still want to process the
+ * current DIFEXPR in case there is a value
+ * attached to it. If there is, we need to
+ * re-open a new output instance, as otherwise
+ * the message ends up bundled with the first
+ * value.
+ *
+ * XXX: There is an edge case where a
+ * printf("hello"); will produce a DIFO that
+ * returns 0 attached to it and we have no good
+ * way to determine if this 0 value is because
+ * there's no real data attached to the printf
+ * as an argument, or it's because the argument
+ * actually returns 0.
+ */
+ if (skip_format == 0)
+ goto nextrec;
+
+ xo_close_instance("output");
+ xo_open_instance("output");
+ }
+
/*
* If this is a DIF expression, and the record has a
* format set, this indicates we have a CTF type name
@@ -2397,8 +3420,14 @@
const char *strdata = dt_strdata_lookup(dtp,
rec->dtrd_format);
if (strdata != NULL) {
- n = dtrace_print(dtp, fp, strdata,
- addr, rec->dtrd_size);
+ if (dtp->dt_oformat)
+ n = dtrace_format_print(dtp, fp,
+ strdata, addr,
+ rec->dtrd_size);
+ else
+ n = dtrace_print(dtp, fp,
+ strdata, addr,
+ rec->dtrd_size);
/*
* dtrace_print() will return -1 on
@@ -2408,8 +3437,12 @@
* should fall through to the normal
* trace method.
*/
- if (n < 0)
+ if (n < 0) {
+ if (dtp->dt_oformat)
+ xo_close_instance(
+ "output");
return (-1);
+ }
if (n > 0)
goto nextrec;
@@ -2424,8 +3457,11 @@
size_t size = ((epd->dtepd_nrecs - i) *
sizeof (dtrace_aggvarid_t));
- if ((aggvars = dt_alloc(dtp, size)) == NULL)
+ if ((aggvars = dt_alloc(dtp, size)) == NULL) {
+ if (dtp->dt_oformat)
+ xo_close_instance("output");
return (-1);
+ }
/*
* This might be a printa() with multiple
@@ -2443,6 +3479,9 @@
break;
if (nrec->dtrd_action != act) {
+ if (dtp->dt_oformat)
+ xo_close_instance(
+ "output");
return (dt_set_errno(dtp,
EDT_BADAGG));
}
@@ -2462,24 +3501,50 @@
assert(naggvars >= 1);
+ if (dtp->dt_oformat)
+ xo_open_list("aggregation-data");
if (naggvars == 1) {
pd.dtpa_id = aggvars[0];
dt_free(dtp, aggvars);
- if (dt_printf(dtp, fp, "\n") < 0 ||
- dtrace_aggregate_walk_sorted(dtp,
- dt_print_agg, &pd) < 0)
- return (-1);
+ if (dtp->dt_oformat) {
+ n = dt_oformat_agg_sorted(dtp,
+ dt_format_agg, &pd);
+ if (n < 0)
+ return (-1);
+ } else {
+ if (dt_printf(dtp, fp, "\n") < 0 ||
+ dtrace_aggregate_walk_sorted(dtp,
+ dt_print_agg, &pd) < 0)
+ return (-1);
+ }
+
+ if (dtp->dt_oformat)
+ dt_oformat_agg_name(&pd);
goto nextrec;
}
- if (dt_printf(dtp, fp, "\n") < 0 ||
- dtrace_aggregate_walk_joined(dtp, aggvars,
- naggvars, dt_print_aggs, &pd) < 0) {
- dt_free(dtp, aggvars);
- return (-1);
+ if (dtp->dt_oformat) {
+ if (dtrace_aggregate_walk_joined(dtp,
+ aggvars, naggvars,
+ dt_format_aggs, &pd) < 0) {
+ dt_oformat_agg_name(&pd);
+ xo_close_instance("output");
+ dt_free(dtp, aggvars);
+ return (-1);
+ }
+ } else {
+ if (dt_printf(dtp, fp, "\n") < 0 ||
+ dtrace_aggregate_walk_joined(dtp,
+ aggvars, naggvars,
+ dt_print_aggs, &pd) < 0) {
+ dt_free(dtp, aggvars);
+ return (-1);
+ }
}
+ if (dtp->dt_oformat)
+ dt_oformat_agg_name(&pd);
dt_free(dtp, aggvars);
goto nextrec;
}
@@ -2490,8 +3555,17 @@
tracememsize = rec->dtrd_size;
}
- n = dt_print_bytes(dtp, fp, addr,
- tracememsize, -33, quiet, 1);
+ if (dtp->dt_oformat) {
+ char *s;
+
+ s = dt_format_bytes_get(dtp, addr,
+ tracememsize);
+ n = xo_emit("{:tracemem/%s}", s);
+ dt_free(dtp, s);
+ } else {
+ n = dt_print_bytes(dtp, fp, addr,
+ tracememsize, -33, quiet, 1);
+ }
tracememsize = 0;
@@ -2503,31 +3577,67 @@
switch (rec->dtrd_size) {
case sizeof (uint64_t):
- n = dt_printf(dtp, fp,
- quiet ? "%lld" : " %16lld",
- /* LINTED - alignment */
- *((unsigned long long *)addr));
+ if (dtp->dt_oformat) {
+ xo_emit("{:value/%lld}",
+ *((unsigned long long *)addr));
+ n = 0;
+ } else
+ n = dt_printf(dtp, fp,
+ quiet ? "%lld" : " %16lld",
+ /* LINTED - alignment */
+ *((unsigned long long *)addr));
break;
case sizeof (uint32_t):
- n = dt_printf(dtp, fp, quiet ? "%d" : " %8d",
- /* LINTED - alignment */
- *((uint32_t *)addr));
+ if (dtp->dt_oformat) {
+ xo_emit("{:value/%d}",
+ *((uint32_t *)addr));
+ n = 0;
+ } else
+ n = dt_printf(dtp, fp,
+ quiet ? "%d" : " %8d",
+ /* LINTED - alignment */
+ *((uint32_t *)addr));
break;
case sizeof (uint16_t):
- n = dt_printf(dtp, fp, quiet ? "%d" : " %5d",
- /* LINTED - alignment */
- *((uint16_t *)addr));
+ if (dtp->dt_oformat) {
+ xo_emit("{:value/%d}",
+ *((uint16_t *)addr));
+ n = 0;
+ } else
+ n = dt_printf(dtp, fp,
+ quiet ? "%d" : " %5d",
+ /* LINTED - alignment */
+ *((uint16_t *)addr));
break;
case sizeof (uint8_t):
- n = dt_printf(dtp, fp, quiet ? "%d" : " %3d",
- *((uint8_t *)addr));
+ if (dtp->dt_oformat) {
+ xo_emit("{:value/%d}",
+ *((uint8_t *)addr));
+ n = 0;
+ } else
+ n = dt_printf(dtp, fp,
+ quiet ? "%d" : " %3d",
+ *((uint8_t *)addr));
break;
default:
- n = dt_print_bytes(dtp, fp, addr,
- rec->dtrd_size, -33, quiet, 0);
+ if (dtp->dt_oformat && rec->dtrd_size > 0) {
+ char *s;
+
+ s = dt_format_bytes_get(dtp, addr,
+ rec->dtrd_size);
+ xo_emit("{:value/%s}", s);
+ dt_free(dtp, s);
+ n = 0;
+ } else {
+ n = dt_print_bytes(dtp, fp, addr,
+ rec->dtrd_size, -33, quiet, 0);
+ }
break;
}
+ if (dtp->dt_oformat && rec->dtrd_size > 0)
+ xo_close_instance("output");
+
if (n < 0)
return (-1); /* errno is set for us */
@@ -2544,6 +3654,12 @@
nextepid:
offs += epd->dtepd_size;
dtp->dt_last_epid = id;
+
+ if (dtp->dt_oformat) {
+ xo_close_list("output");
+ xo_close_instance("probes");
+ xo_flush();
+ }
if (just_one) {
buf->dtbd_oldest = offs;
break;
@@ -2562,7 +3678,12 @@
*/
buf->dtbd_drops = 0;
- return (dt_handle_cpudrop(dtp, cpu, DTRACEDROP_PRINCIPAL, drops));
+ xo_open_instance("probes");
+ dt_oformat_drop(dtp, cpu);
+ rval = dt_handle_cpudrop(dtp, cpu, DTRACEDROP_PRINCIPAL, drops);
+ xo_close_instance("probes");
+
+ return (rval);
}
/*
@@ -3065,8 +4186,12 @@
/* Consume drops. */
for (i = 0; i < max_ncpus; i++) {
if (drops[i] != 0) {
- int error = dt_handle_cpudrop(dtp, i,
+ int error;
+ xo_open_instance("probes");
+ dt_oformat_drop(dtp, i);
+ error = dt_handle_cpudrop(dtp, i,
DTRACEDROP_PRINCIPAL, drops[i]);
+ xo_close_instance("probes");
if (error != 0)
return (error);
}
@@ -3082,3 +4207,22 @@
return (0);
}
+
+void
+dtrace_oformat_probe(dtrace_hdl_t *dtp __unused, const dtrace_probedata_t *data,
+ processorid_t cpu, dtrace_probedesc_t *pd)
+{
+
+ xo_emit("{:timestamp/%llu} {:cpu/%d} {:id/%d} {:provider/%s} "
+ "{:module/%s} {:function/%s} {:name/%s}",
+ (unsigned long long)data->dtpda_timestamp, cpu, pd->dtpd_id,
+ pd->dtpd_provider, pd->dtpd_mod, pd->dtpd_func, pd->dtpd_name);
+}
+
+void
+dt_oformat_drop(dtrace_hdl_t *dtp, processorid_t cpu)
+{
+ xo_emit("{:cpu/%d} {:id/%d} {:provider/%s} "
+ "{:module/%s} {:function/%s} {:name/%s}",
+ cpu, -1, "dtrace", "INTERNAL", "INTERNAL", "DROP");
+}
diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_handle.c b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_handle.c
--- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_handle.c
+++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_handle.c
@@ -36,6 +36,7 @@
#endif
#include
+#include
#include
static const char _dt_errprog[] =
@@ -315,6 +316,7 @@
dtrace_dropdata_t drop;
char str[80], *s;
int size;
+ struct timeval tv;
assert(what == DTRACEDROP_PRINCIPAL || what == DTRACEDROP_AGGREGATION);
@@ -339,6 +341,15 @@
what == DTRACEDROP_PRINCIPAL ? "" : "aggregation ",
howmany > 1 ? "s" : "", cpu);
+ if (dtp->dt_oformat) {
+ (void) gettimeofday(&tv, NULL);
+ xo_emit("{:timestamp/%ld.%06ld} {:count/%ju} "
+ "{:total/%ju} {:kind/%d} {:msg/%s}",
+ tv.tv_sec, tv.tv_usec, (uintmax_t)drop.dtdda_drops,
+ (uintmax_t)drop.dtdda_total, drop.dtdda_kind,
+ drop.dtdda_msg);
+ }
+
if (dtp->dt_drophdlr == NULL)
return (dt_set_errno(dtp, EDT_DROPABORT));
@@ -396,6 +407,7 @@
char str[80], *s;
uintptr_t base = (uintptr_t)new, obase = (uintptr_t)old;
int i, size;
+ struct timeval tv;
bzero(&drop, sizeof (drop));
drop.dtdda_handle = dtp;
@@ -408,6 +420,8 @@
if (new->dtst_killed && !old->dtst_killed)
return (dt_set_errno(dtp, EDT_BRICKED));
+ (void) gettimeofday(&tv, NULL);
+
for (i = 0; _dt_droptab[i].dtdrt_str != NULL; i++) {
uintptr_t naddr = base + _dt_droptab[i].dtdrt_offset;
uintptr_t oaddr = obase + _dt_droptab[i].dtdrt_offset;
@@ -438,12 +452,31 @@
drop.dtdda_total = nval;
drop.dtdda_drops = nval - oval;
- if (dtp->dt_drophdlr == NULL)
+ if (dtp->dt_oformat) {
+ xo_open_instance("probes");
+ dt_oformat_drop(dtp, DTRACE_CPUALL);
+ xo_emit("{:timestamp/%ld.%06ld} {:count/%ju} "
+ "{:total/%ju} {:kind/%d} {:msg/%s}",
+ tv.tv_sec, tv.tv_usec, (uintmax_t)drop.dtdda_drops,
+ (uintmax_t)drop.dtdda_total, drop.dtdda_kind,
+ drop.dtdda_msg);
+ }
+
+ if (dtp->dt_drophdlr == NULL) {
+ if (dtp->dt_oformat)
+ xo_close_instance("probes");
return (dt_set_errno(dtp, EDT_DROPABORT));
+ }
if ((*dtp->dt_drophdlr)(&drop,
- dtp->dt_droparg) == DTRACE_HANDLE_ABORT)
+ dtp->dt_droparg) == DTRACE_HANDLE_ABORT) {
+ if (dtp->dt_oformat)
+ xo_close_instance("probes");
return (dt_set_errno(dtp, EDT_DROPABORT));
+ }
+
+ if (dtp->dt_oformat)
+ xo_close_instance("probes");
}
return (0);
diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_impl.h b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_impl.h
--- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_impl.h
+++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_impl.h
@@ -211,6 +211,8 @@
int dtpa_agghist; /* print aggregation as histogram */
int dtpa_agghisthdr; /* aggregation histogram hdr printed */
int dtpa_aggpack; /* pack quantized aggregations */
+ char dtpa_keyname[256]; /* key name for oformat */
+ char *dtpa_aggname; /* aggregate name for oformat */
} dt_print_aggdata_t;
typedef struct dt_dirpath {
@@ -364,6 +366,7 @@
dtrace_epid_t dt_last_epid; /* most recently consumed EPID */
uint64_t dt_last_timestamp; /* most recently consumed timestamp */
boolean_t dt_has_sugar; /* syntactic sugar used? */
+ int dt_oformat; /* output format (none, json, xml, html) */
};
/*
@@ -702,6 +705,8 @@
const void *, size_t, uint64_t);
extern int dt_print_agg(const dtrace_aggdata_t *, void *);
+extern int dt_format_agg(const dtrace_aggdata_t *, void *);
+
extern int dt_handle(dtrace_hdl_t *, dtrace_probedata_t *);
extern int dt_handle_liberr(dtrace_hdl_t *,
const dtrace_probedata_t *, const char *);
@@ -711,6 +716,8 @@
dtrace_status_t *, dtrace_status_t *);
extern int dt_handle_setopt(dtrace_hdl_t *, dtrace_setoptdata_t *);
+extern void dt_oformat_drop(dtrace_hdl_t *, processorid_t);
+
extern int dt_lib_depend_add(dtrace_hdl_t *, dt_list_t *, const char *);
extern dt_lib_depend_t *dt_lib_depend_lookup(dt_list_t *, const char *);
diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_oformat.h b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_oformat.h
new file mode 100644
--- /dev/null
+++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_oformat.h
@@ -0,0 +1,34 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Domagoj Stolfa
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _DT_OFORMAT_H_
+#define _DT_OFORMAT_H_
+
+#undef NORETURN /* needed because libxo redefines it */
+#include
+
+#endif /* _DT_OFORMAT_H_ */
diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_open.c b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_open.c
--- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_open.c
+++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_open.c
@@ -59,6 +59,7 @@
#include
#include
#include
+#include
#ifndef illumos
#include
#include
@@ -1736,6 +1737,8 @@
free(dtp->dt_kmods);
#endif
free(dtp->dt_provs);
+
+ xo_finish();
free(dtp);
}
diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_options.c b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_options.c
--- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_options.c
+++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_options.c
@@ -43,6 +43,7 @@
#include
#include
+#include
static int
dt_opt_agg(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
@@ -723,6 +724,18 @@
return (0);
}
+static int
+dt_opt_oformat(dtrace_hdl_t *dtp, const char *arg, uintptr_t option __unused)
+{
+ if (arg == NULL)
+ return (dt_set_errno(dtp, EDT_BADOPTVAL));
+
+ if (xo_set_options(NULL, arg) < 0)
+ return (dt_set_errno(dtp, EDT_BADOPTVAL));
+
+ return (dtrace_oformat_configure(dtp));
+}
+
static int
dt_opt_rate(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
{
@@ -1046,6 +1059,7 @@
{ "jstackframes", dt_opt_runtime, DTRACEOPT_JSTACKFRAMES },
{ "jstackstrsize", dt_opt_size, DTRACEOPT_JSTACKSTRSIZE },
{ "nspec", dt_opt_runtime, DTRACEOPT_NSPEC },
+ { "oformat", dt_opt_oformat, 0 },
{ "specsize", dt_opt_size, DTRACEOPT_SPECSIZE },
{ "stackframes", dt_opt_runtime, DTRACEOPT_STACKFRAMES },
{ "statusrate", dt_opt_rate, DTRACEOPT_STATUSRATE },
diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_print.c b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_print.c
--- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_print.c
+++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_print.c
@@ -27,6 +27,7 @@
*/
/*
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2023, Domagoj Stolfa. All rights reserved.
*/
/*
@@ -82,6 +83,7 @@
#include
#include
#include
+#include
/* determines whether the given integer CTF encoding is a character */
#define CTF_IS_CHAR(e) \
@@ -101,8 +103,10 @@
int pa_depth; /* member depth */
int pa_nest; /* nested array depth */
FILE *pa_file; /* output file */
+ const char *pa_object; /* object name */
} dt_printarg_t;
+static int dt_format_member(const char *, ctf_id_t, ulong_t, int, void *);
static int dt_print_member(const char *, ctf_id_t, ulong_t, int, void *);
/*
@@ -189,34 +193,32 @@
value >>= shift;
value &= mask;
- (void) fprintf(fp, "%#llx", (u_longlong_t)value);
+ xo_emit("{:value/%#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)
+dt_print_hex(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, size_t size)
{
switch (size) {
case sizeof (uint8_t):
- (void) fprintf(fp, "%#x", *(uint8_t *)addr);
+ xo_emit("{:value/%#x}", *(uint8_t *)addr);
break;
case sizeof (uint16_t):
- /* LINTED - alignment */
- (void) fprintf(fp, "%#x", *(uint16_t *)addr);
+ xo_emit("{:value/%#x}", *(uint16_t *)addr);
break;
case sizeof (uint32_t):
- /* LINTED - alignment */
- (void) fprintf(fp, "%#x", *(uint32_t *)addr);
+ xo_emit("{:value/%#x}", *(uint32_t *)addr);
break;
case sizeof (uint64_t):
- (void) fprintf(fp, "%#llx",
- /* LINTED - alignment */
+ xo_emit("{:value/%#llx}",
(unsigned long long)*(uint64_t *)addr);
break;
default:
- (void) fprintf(fp, "", (uint_t)size);
+ xo_emit("<{:warning} {:size/%u}>", "invalid size",
+ (uint_t)size);
}
}
@@ -229,12 +231,13 @@
{
FILE *fp = pap->pa_file;
ctf_file_t *ctfp = pap->pa_ctfp;
+ dtrace_hdl_t *dtp = pap->pa_dtp;
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, "");
+ xo_emit("<{:warning}>", "unknown encoding");
return;
}
@@ -243,7 +246,8 @@
* would be found.
*/
if (e.cte_format & CTF_INT_VARARGS) {
- (void) fprintf(fp, "...");
+ if (!dtp->dt_oformat)
+ (void)fprintf(fp, "...");
return;
}
@@ -263,15 +267,15 @@
if (CTF_IS_CHAR(e)) {
char c = *(char *)addr;
if (isprint(c))
- (void) fprintf(fp, "'%c'", c);
+ xo_emit("'{:value/%c}'", c);
else if (c == 0)
- (void) fprintf(fp, "'\\0'");
+ xo_emit("'{:value/\\0}'");
else
- (void) fprintf(fp, "'\\%03o'", c);
+ xo_emit("'{:value/\\%03o}'", c);
return;
}
- dt_print_hex(fp, addr, size);
+ dt_print_hex(dtp, fp, addr, size);
}
/*
@@ -285,22 +289,20 @@
ctf_file_t *ctfp = pap->pa_ctfp;
ctf_encoding_t e;
caddr_t addr = pap->pa_addr + off / NBBY;
+ dtrace_hdl_t *dtp = pap->pa_dtp;
if (ctf_type_encoding(ctfp, base, &e) == 0) {
if (e.cte_format == CTF_FP_SINGLE &&
e.cte_bits == sizeof (float) * NBBY) {
- /* LINTED - alignment */
- (void) fprintf(fp, "%+.7e", *((float *)addr));
+ xo_emit("{:value/%+.7e}", *((float *)addr));
} else if (e.cte_format == CTF_FP_DOUBLE &&
e.cte_bits == sizeof (double) * NBBY) {
- /* LINTED - alignment */
- (void) fprintf(fp, "%+.7e", *((double *)addr));
+ xo_emit("{:value/%+.7e}", *((double *)addr));
} else if (e.cte_format == CTF_FP_LDOUBLE &&
e.cte_bits == sizeof (long double) * NBBY) {
- /* LINTED - alignment */
- (void) fprintf(fp, "%+.16LE", *((long double *)addr));
+ xo_emit("{:value/%+.16LE}", *((long double *)addr));
} else {
- (void) fprintf(fp, "");
+ xo_emit("<{:warning}>", "unknown encoding");
}
}
}
@@ -322,15 +324,14 @@
GElf_Sym sym;
if (bid == CTF_ERR || ctf_type_kind(ctfp, bid) != CTF_K_FUNCTION) {
- dt_print_hex(fp, addr, size);
+ dt_print_hex(pap->pa_dtp, fp, addr, size);
} else {
/* LINTED - alignment */
pc = *((uint64_t *)addr);
if (dtrace_lookup_by_addr(pap->pa_dtp, pc, &sym, &dts) != 0) {
- dt_print_hex(fp, addr, size);
+ dt_print_hex(pap->pa_dtp, fp, addr, size);
} else {
- (void) fprintf(fp, "%s`%s", dts.dts_object,
- dts.dts_name);
+ xo_emit("{:value/%s`%s}", dts.dts_object, dts.dts_name);
}
}
}
@@ -367,6 +368,7 @@
FILE *fp = pap->pa_file;
ctf_file_t *ctfp = pap->pa_ctfp;
caddr_t addr = pap->pa_addr + off / NBBY;
+ char *str;
ctf_arinfo_t car;
ssize_t eltsize;
ctf_encoding_t e;
@@ -374,16 +376,18 @@
boolean_t isstring;
int kind;
ctf_id_t rtype;
+ dtrace_hdl_t *dtp = pap->pa_dtp;
if (ctf_array_info(ctfp, base, &car) == CTF_ERR) {
- (void) fprintf(fp, "%p", (void *)addr);
+ xo_emit("{:value/%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, "", car.ctr_contents);
+ xo_emit("<{:warning} {:type-identifier/%lu}>", "invalid type",
+ car.ctr_contents);
return;
}
@@ -411,18 +415,44 @@
*
* As D will internally represent this as a char[256] array.
*/
- if (!isstring || pap->pa_depth != 0)
- (void) fprintf(fp, "[ ");
+ if (dtp->dt_oformat) {
+ if (!isstring)
+ xo_open_list("value");
+ else {
+ str = malloc(car.ctr_nelems);
+ if (str == NULL)
+ return;
+ *str = 0;
+ }
+ } else {
+ if (!isstring || pap->pa_depth != 0)
+ (void)fprintf(fp, "[ ");
- if (isstring)
- (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')
+ if (c == '\0') {
+ if (dtp->dt_oformat)
+ str[i] = 0;
break;
- (void) fprintf(fp, "%c", c);
+ }
+
+ if (dtp->dt_oformat)
+ str[i] = c;
+ else
+ (void)fprintf(fp, "%c", c);
+ } else if (dtp->dt_oformat) {
+ 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_format_member, &pa);
} else {
/*
* Recursively invoke ctf_type_visit() on each member.
@@ -444,15 +474,24 @@
}
}
- 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, "]");
+ if (dtp->dt_oformat) {
+ if (!isstring)
+ xo_close_list("value");
+ else {
+ xo_emit("{:value/%s}", str);
+ free(str);
+ }
+ } else {
+ 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, "]");
+ }
}
}
@@ -463,7 +502,8 @@
static void
dt_print_structlike(ctf_id_t id, ulong_t off, dt_printarg_t *pap)
{
- (void) fprintf(pap->pa_file, "{");
+ if (pap->pa_dtp->dt_oformat == DTRACE_OFORMAT_TEXT)
+ (void)fprintf(pap->pa_file, "{");
}
/*
@@ -480,6 +520,7 @@
ssize_t size;
caddr_t addr = pap->pa_addr + off / NBBY;
int value = 0;
+ dtrace_hdl_t *dtp = pap->pa_dtp;
/*
* The C standard says that an enum will be at most the sizeof (int).
@@ -498,14 +539,16 @@
value = *(int32_t *)addr;
break;
default:
- (void) fprintf(fp, "", (uint_t)size);
+ xo_emit("<{:warning} {:size/%u}>", "invalid enum size",
+ (uint_t)size);
return;
}
- if ((ename = ctf_enum_name(ctfp, base, value)) != NULL)
- (void) fprintf(fp, "%s", ename);
- else
- (void) fprintf(fp, "%d", value);
+ if ((ename = ctf_enum_name(ctfp, base, value)) != NULL) {
+ xo_emit("{:value/%s}", ename);
+ } else {
+ xo_emit("{:value/%d}", value);
+ }
}
/*
@@ -516,7 +559,8 @@
static void
dt_print_tag(ctf_id_t base, ulong_t off, dt_printarg_t *pap)
{
- (void) fprintf(pap->pa_file, "");
+ if (pap->pa_dtp->dt_oformat == DTRACE_OFORMAT_TEXT)
+ (void)fprintf(pap->pa_file, "");
}
typedef void dt_printarg_f(ctf_id_t, ulong_t, dt_printarg_t *);
@@ -533,6 +577,46 @@
dt_print_tag /* CTF_K_FORWARD */
};
+static int
+dt_format_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;
+
+ 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) {
+ xo_emit("{:name/%s} <{:warning} {:type-identifier/%lu}>"
+ " {:value/0x%llx}",
+ name, "invalid type", id, pap->pa_addr);
+ return (0);
+ }
+
+ dt_print_type_name(ctfp, id, type, sizeof (type));
+ xo_open_instance("type");
+ if (pap->pa_object) {
+ xo_emit("{:object-name/%s}", pap->pa_object);
+ /* Clear the object to avoid duplication */
+ pap->pa_object = NULL;
+ }
+
+ if (*name != 0)
+ xo_emit("{:member-name/%s}", name);
+ xo_emit("{:name/%s} {:ctfid/%ld}", type, id);
+ dt_printfuncs[kind - 1](rtype, off, pap);
+
+ xo_close_instance("type");
+ return (0);
+}
+
/*
* Print one member of a structure. This callback is invoked from
* ctf_type_visit() recursively.
@@ -634,16 +718,12 @@
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)
+static ctf_id_t
+dt_print_prepare(dtrace_hdl_t *dtp, const char *typename, caddr_t addr,
+ size_t len, dt_printarg_t *pa)
{
const char *s;
char *object;
- dt_printarg_t pa;
ctf_id_t id;
dt_module_t *dmp;
ctf_file_t *ctfp;
@@ -661,20 +741,20 @@
;
if (*s != '`')
- return (0);
+ return (CTF_ERR);
object = alloca(s - typename + 1);
bcopy(typename, object, s - typename);
object[s - typename] = '\0';
dmp = dt_module_lookup_by_name(dtp, object);
if (dmp == NULL)
- return (0);
+ return (CTF_ERR);
if (dmp->dm_pid != 0) {
libid = atoi(s + 1);
s = strchr(s + 1, '`');
if (s == NULL || libid > dmp->dm_nctflibs)
- return (0);
+ return (CTF_ERR);
ctfp = dmp->dm_libctfp[libid];
} else {
ctfp = dt_module_getctf(dtp, dmp);
@@ -688,18 +768,63 @@
* work.
*/
if (ctfp == NULL || ctf_type_kind(ctfp, id) == CTF_ERR)
+ return (CTF_ERR);
+
+ pa->pa_dtp = dtp;
+ pa->pa_addr = addr;
+ pa->pa_ctfp = ctfp;
+ pa->pa_nest = 0;
+ pa->pa_depth = 0;
+ pa->pa_object = strdup(object);
+ return (id);
+}
+
+/*
+ * 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)
+{
+ dt_printarg_t pa;
+ ctf_id_t id;
+
+ id = dt_print_prepare(dtp, typename, addr, len, &pa);
+ if (id == CTF_ERR)
return (0);
- /* setup the print structure and kick off the main print routine */
- pa.pa_dtp = dtp;
- pa.pa_addr = addr;
- pa.pa_ctfp = ctfp;
- 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);
+ dt_free(dtp, (void *)pa.pa_object);
return (len);
}
+
+/*
+ * Main format function invoked by dt_consume_cpu().
+ */
+int
+dtrace_format_print(dtrace_hdl_t *dtp, FILE *fp, const char *typename,
+ caddr_t addr, size_t len)
+{
+ dt_printarg_t pa;
+ ctf_id_t id;
+ char toplevel[1024];
+
+ id = dt_print_prepare(dtp, typename, addr, len, &pa);
+ if (id == CTF_ERR)
+ return (0);
+
+ if (ctf_type_name(pa.pa_ctfp, id, toplevel, sizeof(toplevel)) < 0)
+ return (0);
+
+ xo_open_list("type");
+ (void) ctf_type_visit(pa.pa_ctfp, id, dt_format_member, &pa);
+ xo_close_list("type");
+ dt_free(dtp, (void *)pa.pa_object);
+
+ return (len);
+}
+
diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_printf.h b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_printf.h
--- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_printf.h
+++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_printf.h
@@ -21,6 +21,7 @@
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2023 Domagoj Stolfa. All rights reserved.
* Use is subject to license terms.
*/
@@ -128,6 +129,11 @@
extern int dt_print_mod(dtrace_hdl_t *, FILE *, const char *, caddr_t);
extern int dt_print_umod(dtrace_hdl_t *, FILE *, const char *, caddr_t);
+extern int dt_format_stack(dtrace_hdl_t *, caddr_t, int, int);
+extern int dt_format_ustack(dtrace_hdl_t *, caddr_t, uint64_t);
+extern int dt_format_mod(dtrace_hdl_t *, caddr_t);
+extern int dt_format_umod(dtrace_hdl_t *, caddr_t);
+
#ifdef __cplusplus
}
#endif
diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_subr.c b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_subr.c
--- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_subr.c
+++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_subr.c
@@ -50,6 +50,7 @@
#include
#include
+#include
static const struct {
size_t dtps_offset;
@@ -992,3 +993,44 @@
return (dt_string2str(c, str, nbytes));
}
+
+int
+dtrace_oformat_configure(dtrace_hdl_t *dtp)
+{
+
+ dtp->dt_oformat = xo_get_style(NULL) == XO_STYLE_TEXT ?
+ DTRACE_OFORMAT_TEXT :
+ DTRACE_OFORMAT_STRUCTURED;
+ xo_set_flags(NULL, XOF_DTRT);
+ return (0);
+}
+
+int
+dtrace_oformat(dtrace_hdl_t *dtp)
+{
+
+ return (dtp->dt_oformat != DTRACE_OFORMAT_TEXT);
+}
+
+void
+dtrace_set_outfp(const FILE *ofp)
+{
+
+ xo_set_file((FILE *)ofp);
+}
+
+void
+dtrace_oformat_setup(dtrace_hdl_t *dtp)
+{
+
+ xo_open_container("dtrace");
+ xo_open_list("probes");
+}
+
+void
+dtrace_oformat_teardown(dtrace_hdl_t *dtp)
+{
+
+ xo_close_list("probes");
+ xo_close_container("dtrace");
+}
diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dtrace.h b/cddl/contrib/opensolaris/lib/libdtrace/common/dtrace.h
--- a/cddl/contrib/opensolaris/lib/libdtrace/common/dtrace.h
+++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dtrace.h
@@ -25,6 +25,7 @@
*/
/*
+ * Copyright (c) 2023 by Domagoj Stolfa. All rights reserved.
* Copyright (c) 2014, 2016 by Delphix. All rights reserved.
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
*/
@@ -200,6 +201,7 @@
dtrace_flowkind_t dtpda_flow; /* flow kind */
const char *dtpda_prefix; /* recommended flow prefix */
int dtpda_indent; /* recommended flow indent */
+ uint64_t dtpda_timestamp; /* hrtime of snapshot */
} dtrace_probedata_t;
typedef int dtrace_consume_probe_f(const dtrace_probedata_t *, void *);
@@ -233,6 +235,10 @@
extern void *dtrace_printa_create(dtrace_hdl_t *, const char *);
extern size_t dtrace_printf_format(dtrace_hdl_t *, void *, char *, size_t);
+extern int dtrace_sprintf(dtrace_hdl_t *, FILE *, void *,
+ const dtrace_recdesc_t *, uint_t,
+ const void *, size_t);
+
extern int dtrace_fprintf(dtrace_hdl_t *, FILE *, void *,
const dtrace_probedata_t *, const dtrace_recdesc_t *, uint_t,
const void *, size_t);
@@ -260,6 +266,8 @@
*/
extern int dtrace_print(dtrace_hdl_t *, FILE *, const char *,
caddr_t, size_t);
+extern int dtrace_format_print(dtrace_hdl_t *, FILE *, const char *,
+ caddr_t, size_t);
/*
* DTrace Work Interface
@@ -616,4 +624,18 @@
#define _SC_NPROCESSORS_MAX _SC_NPROCESSORS_CONF
#endif
+/*
+ * Values for the dt_oformat property.
+ */
+#define DTRACE_OFORMAT_TEXT 0
+#define DTRACE_OFORMAT_STRUCTURED 1
+
+extern int dtrace_oformat_configure(dtrace_hdl_t *);
+extern int dtrace_oformat(dtrace_hdl_t *);
+extern void dtrace_set_outfp(const FILE *);
+extern void dtrace_oformat_setup(dtrace_hdl_t *);
+extern void dtrace_oformat_teardown(dtrace_hdl_t *);
+extern void dtrace_oformat_probe(dtrace_hdl_t *, const dtrace_probedata_t *,
+ processorid_t, dtrace_probedesc_t *);
+
#endif /* _DTRACE_H */
diff --git a/cddl/lib/libdtrace/Makefile b/cddl/lib/libdtrace/Makefile
--- a/cddl/lib/libdtrace/Makefile
+++ b/cddl/lib/libdtrace/Makefile
@@ -147,7 +147,7 @@
LDFLAGS+= -fsanitize=address -fsanitize=undefined
.endif
-LIBADD= ctf elf proc pthread rtld_db
+LIBADD= ctf elf proc pthread rtld_db xo
CLEANFILES= dt_errtags.c dt_names.c
diff --git a/cddl/usr.sbin/dtrace/Makefile b/cddl/usr.sbin/dtrace/Makefile
--- a/cddl/usr.sbin/dtrace/Makefile
+++ b/cddl/usr.sbin/dtrace/Makefile
@@ -34,7 +34,7 @@
LDFLAGS+= -fsanitize=address -fsanitize=undefined
.endif
-LIBADD= dtrace ctf elf proc spl
+LIBADD= dtrace ctf elf proc spl xo
.if ${MK_DTRACE_TESTS} != "no"
SUBDIR+= tests
diff --git a/cddl/usr.sbin/dtrace/tests/common/Makefile b/cddl/usr.sbin/dtrace/tests/common/Makefile
--- a/cddl/usr.sbin/dtrace/tests/common/Makefile
+++ b/cddl/usr.sbin/dtrace/tests/common/Makefile
@@ -39,6 +39,7 @@
misc \
multiaggs \
offsetof \
+ oformat \
operators \
pid \
plockstat \
diff --git a/cddl/usr.sbin/dtrace/tests/common/oformat/Makefile b/cddl/usr.sbin/dtrace/tests/common/oformat/Makefile
new file mode 100644
--- /dev/null
+++ b/cddl/usr.sbin/dtrace/tests/common/oformat/Makefile
@@ -0,0 +1,42 @@
+#
+# This Makefile was generated by $srcdir/cddl/usr.sbin/dtrace/tests/tools/genmakefiles.sh.
+#
+
+PACKAGE= tests
+
+${PACKAGE}FILES= \
+ tst.agg.avg.ksh \
+ tst.agg.count.ksh \
+ tst.agg.llquantize.ksh \
+ tst.agg.lquantize.ksh \
+ tst.agg.max.ksh \
+ tst.agg.min.ksh \
+ tst.agg.quantize.ksh \
+ tst.agg.stddev.ksh \
+ tst.agg.sum.ksh \
+ tst.aggmod.ksh \
+ tst.aggstack.ksh \
+ tst.aggsym.ksh \
+ tst.aggustack.ksh \
+ tst.aggusym.ksh \
+ tst.drop.ksh \
+ tst.mod.ksh \
+ tst.print.ksh \
+ tst.printf.ksh \
+ tst.printm.ksh \
+ tst.stack.ksh \
+ tst.sym.ksh \
+ tst.trace.ksh \
+ tst.tracemem.ksh \
+ tst.umod.ksh \
+ tst.ustack.ksh \
+ tst.usym.ksh \
+
+TESTEXES= \
+
+
+CFILES= \
+
+
+
+.include "../../dtrace.test.mk"
diff --git a/cddl/usr.sbin/dtrace/tests/dtrace.test.mk b/cddl/usr.sbin/dtrace/tests/dtrace.test.mk
--- a/cddl/usr.sbin/dtrace/tests/dtrace.test.mk
+++ b/cddl/usr.sbin/dtrace/tests/dtrace.test.mk
@@ -12,7 +12,7 @@
TESTWRAPPER= t_dtrace_contrib
ATF_TESTS_SH+= ${TESTWRAPPER}
-TEST_METADATA.t_dtrace_contrib+= required_files="/usr/local/bin/ksh"
+TEST_METADATA.t_dtrace_contrib+= required_files="/usr/local/bin/ksh /usr/local/bin/jq /usr/local/bin/xmllint"
TEST_METADATA.t_dtrace_contrib+= required_user="root"
GENTEST?= ${.CURDIR:H:H}/tools/gentest.sh
diff --git a/etc/mtree/BSD.tests.dist b/etc/mtree/BSD.tests.dist
--- a/etc/mtree/BSD.tests.dist
+++ b/etc/mtree/BSD.tests.dist
@@ -150,6 +150,8 @@
..
offsetof
..
+ oformat
+ ..
operators
..
pid
diff --git a/share/mk/src.libnames.mk b/share/mk/src.libnames.mk
--- a/share/mk/src.libnames.mk
+++ b/share/mk/src.libnames.mk
@@ -387,7 +387,7 @@
_DP_vmmapi= util
_DP_opencsd= cxxrt
_DP_ctf= spl z
-_DP_dtrace= ctf elf proc pthread rtld_db
+_DP_dtrace= ctf elf proc pthread rtld_db xo
_DP_xo= util
_DP_ztest= geom m nvpair umem zpool pthread avl zfs_core spl zutil zfs uutil icp
# The libc dependencies are not strictly needed but are defined to make the