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 @@ -633,6 +633,236 @@ Number of speculations. .It Sy nolibs Do not load D system libraries. +.It Sy oformat Ns = Ns Cm json Ns | Ns Cm xml Ns | Ns Cm html Ns | Ns Cm none +Specify the format to use for output. +Setting +.Sy oformat +to none makes +.Nm +use regular human-readable output. +.sp +.Sy oformat +generates its output using +.Xr libxo 3 +in the following format: +.Bd -literal +{ + "dtrace": { + "probes": [ + { + "timestamp": ..., + "cpu": ..., + "id": ..., + "provider": ..., + "module": ..., + "function": ..., + "name": ..., + "output": [ + ... (script specific) + ] + } + ] + } +} + + + + + ... + ... + ... + ... + ... + ... + ... + + ... (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 + + + + ... + + + ... + + ... (script specific) + + + +.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 trace action is of form: +.Bd -literal +{ + "value": ... +} +.Ed +.Pp +The printf action begins with an object containing the evaluated message +of the printf action. +Every consecutive object contains the value of each of the arguments to +printf in its raw form as if the trace action was used instead. +A printf statement which contains no arguments other than the message +will have only 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 count, min, max, stddev and sum. +Each one of them is represented with the key containing their name being +present. +For example, the output of a stddev aggregation will contain a key "stddev" +inside an "agg_data" object: +.Bd -literal +{ + "agg_data": [ + { + "keys": [ + ... + ], + "stddev": ... + } + ], + "agg_name": ... +} +.Ed +.Pp +Aggregations that produce histogram output have to be formatted +slightly differently. +The "keys" field remains consistent across all aggregations, however +quantize, lquantize and 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 +{ + "agg_data": [ + ... + { + "keys": [ + ... + ], + "buckets": [ + { + "value": 32, + "count": 0 + }, + { + "value": 64, + "count": 17 + }, + ... + ], + }, + ... + ] + "agg_name": ... +} +.Ed +.Pp +Similar to scalar aggregations, named scalar actions such as mod, umod, +sym, usym, tracemem and printm will output an object with the key being +equal to the name of the action. +For example, printm output would produce the following object: +.Bd -literal +{ + "printm": "0x4054171100" +} +.Ed +.Pp +Finally, stack and ustack actions unroll each of the stack frames into its +own object in an array. +The only real difference between them is that the stack action will produce +an array called "stack_frames" while ustack will produce one called +"ustack_frames". +The following is an example of their +.Sy oformat +output: +.Bd -literal +{ + "stack_frames": [ + { + "sym": "0xfffffe00c611b000" + }, + { + "sym": "dtrace.ko`dtrace_ioctl+0xdd4" + }, + { + "sym": "kernel`devfs_ioctl+0xd2" + }, + ... + ] +} + +{ + "ustack_frames": [ + { + "sym": "libc.so.7`ioctl+0xa" + }, + { + "sym": "dtrace`go+0x143" + }, + ... + ] +} +.Ed .It Sy quiet Set quiet mode. Same as the 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 @@ -112,6 +113,7 @@ static char *g_etcfile = "/etc/system"; static const char *g_etcbegin = "* vvvv Added by DTrace"; static const char *g_etcend = "* ^^^^ Added by DTrace"; +static int g_oformat = DTRACE_OFORMAT_NONE; static const char *g_etc[] = { "*", @@ -907,7 +909,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 +1131,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", @@ -1498,6 +1505,9 @@ dtrace_errmsg(NULL, err)); } + if (dtrace_oformat(g_dtp)) + dtrace_setopt(g_dtp, "quiet", 0); + #if defined(__i386__) /* XXX The 32-bit seems to need more buffer space by default -sson */ (void) dtrace_setopt(g_dtp, "bufsize", "12m"); @@ -1765,6 +1775,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 +1792,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 +1829,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 +1992,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 +2043,23 @@ 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)) { 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_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_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_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_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_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_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_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_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_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, @@ -1248,6 +1557,169 @@ return (0); } +int +dt_format_stack(dtrace_hdl_t *dtp, caddr_t addr, int depth, int size) +{ + dtrace_syminfo_t dts; + GElf_Sym sym; + int i; + uint64_t pc; + + xo_open_list("stack_frames"); + for (i = 0; i < depth; i++) { + switch (size) { + case sizeof (uint32_t): + pc = *((uint32_t *)addr); + break; + + case sizeof (uint64_t): + pc = *((uint64_t *)addr); + break; + + default: + return (dt_set_errno(dtp, EDT_BADSTACKPC)); + } + + if (pc == 0) + break; + + addr += size; + + xo_open_instance("stack_frames"); + if (dtrace_lookup_by_addr(dtp, pc, &sym, &dts) == 0) { + if (pc > sym.st_value) { + xo_emit("{:sym/%s`%s+0x%llx}", dts.dts_object, + dts.dts_name, + (u_longlong_t)(pc - sym.st_value)); + } else { + xo_emit("{:sym/%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) { + xo_emit("{:sym/%s`0x%llx}", dts.dts_object, + (u_longlong_t)pc); + } else { + xo_emit("{:sym/0x%llx}", (u_longlong_t)pc); + } + } + xo_close_instance("stack_frames"); + } + xo_close_list("stack_frames"); + + return (0); +} + +int +dt_format_ustack(dtrace_hdl_t *dtp, caddr_t addr, uint64_t arg) +{ + 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++; + + /* + * 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 */ + + xo_open_list("ustack_frames"); + for (i = 0; i < depth && pc[i] != 0; i++) { + const prmap_t *map; + + 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) { + xo_emit("{:sym/%s`%s+0x%llx}", + dt_basename(objname), name, + (u_longlong_t)(pc[i] - sym.st_value)); + } else { + xo_emit("{:sym/%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. + */ + xo_emit("{:sym/%s}", str); + } else { + if (P != NULL && Pobjname(P, pc[i], objname, + sizeof (objname)) != 0) { + xo_emit("{:sym/%s`0x%llx}", + dt_basename(objname), (u_longlong_t)pc[i]); + } else { + xo_emit("{:sym/0x%llx}", (u_longlong_t)pc[i]); + } + } + + 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. + */ + xo_emit("{:annotation/%s}", &str[1]); + } + + if (str != NULL) { + str += strlen(str) + 1; + if (str - strbase >= strsize) + str = NULL; + } + xo_close_instance("ustack_frames"); + } + xo_close_list("ustack_frames"); + + if (P != NULL) { + dt_proc_unlock(dtp, P); + dt_proc_release(dtp, P); + } + + return (err); +} + int dt_print_stack(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr, int depth, int size) @@ -1460,6 +1932,41 @@ 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) { @@ -1496,6 +2003,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 +2095,11 @@ 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}", 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 +2107,43 @@ * 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}", 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}", (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) { @@ -1592,9 +2161,77 @@ (void) snprintf(c, sizeof (c), "0x%llx", (u_longlong_t)pc); } - if (dt_printf(dtp, fp, format, c) < 0) + if (dt_printf(dtp, fp, format, c) < 0) + return (-1); + + 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++) { + sprintf(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); } @@ -1802,6 +2439,90 @@ 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)); + + case DTRACEAGG_QUANTIZE: + return (dt_format_quantize(dtp, addr, size, normal)); + + case DTRACEAGG_LQUANTIZE: + return (dt_format_lquantize(dtp, addr, size, normal)); + + case DTRACEAGG_LLQUANTIZE: + return (dt_format_llquantize(dtp, addr, size, normal)); + + case DTRACEAGG_AVG: + return (dt_format_average(dtp, addr, size, normal)); + + case DTRACEAGG_STDDEV: + return (dt_format_stddev(dtp, addr, size, normal)); + + default: + break; + } + + switch (size) { + case sizeof (uint64_t): + snprintf(fmt, sizeof(fmt), "{:%s/%%lld}", pd->dtpa_keyname); + xo_emit(fmt, (long long)*((uint64_t *)addr) / normal); + break; + case sizeof (uint32_t): + snprintf(fmt, sizeof(fmt), "{:%s/%%d}", pd->dtpa_keyname); + xo_emit(fmt, *((uint32_t *)addr) / (uint32_t)normal); + break; + case sizeof (uint16_t): + snprintf(fmt, sizeof(fmt), "{:%s/%%d}", pd->dtpa_keyname); + xo_emit(fmt, *((uint16_t *)addr) / (uint32_t)normal); + break; + case sizeof (uint8_t): + snprintf(fmt, sizeof(fmt), "{:%s/%%d}", pd->dtpa_keyname); + xo_emit(fmt, *((uint8_t *)addr) / (uint32_t)normal); + break; + default: + s = dt_format_bytes_get(dtp, addr, size); + if (s == NULL) + return (-1); + + xo_emit("{:value/%s}", s); + dt_free(dtp, s); + break; + } + + return (0); +} + static int dt_print_datum(dtrace_hdl_t *dtp, FILE *fp, dtrace_recdesc_t *rec, caddr_t addr, size_t size, const dtrace_aggdata_t *aggdata, @@ -1956,6 +2677,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("agg_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("agg_data"); + return (-1); + } + xo_close_instance("keys"); + + if (dt_buffered_flush(dtp, NULL, rec, aggdata, + DTRACE_BUFDATA_AGGKEY) < 0) { + xo_close_instance("agg_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("agg_data"); + return (-1); + } + + if (dt_buffered_flush(dtp, NULL, rec, aggdata, + DTRACE_BUFDATA_AGGVAL) < 0) { + xo_close_instance("agg_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("agg_data"); + return (-1); + } + + xo_close_instance("agg_data"); + return (0); +} + int dt_print_aggs(const dtrace_aggdata_t **aggsdata, int naggvars, void *arg) { @@ -2037,6 +2871,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) { @@ -2114,6 +2975,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 +3007,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 +3024,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 +3042,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 +3173,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_NONE && + DTRACEACT_ISPRINTFLIKE(act)) { void *fmtdata; int (*func)(dtrace_hdl_t *, FILE *, void *, const dtrace_probedata_t *, @@ -2387,6 +3314,53 @@ 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 +3371,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 +3388,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 +3408,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 +3430,9 @@ break; if (nrec->dtrd_action != act) { + if (dtp->dt_oformat) + xo_close_instance( + "output"); return (dt_set_errno(dtp, EDT_BADAGG)); } @@ -2462,24 +3452,61 @@ assert(naggvars >= 1); + xo_open_list("agg_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) { + if (dtrace_aggregate_walk_sorted( + dtp, dt_format_agg, &pd) < 0) { + xo_close_list("agg_data"); + xo_emit("{:agg_name/%s}", + pd.dtpa_aggname); + xo_close_instance( + "output"); + 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) { + xo_close_list("agg_data"); + xo_emit("{:agg_name/%s}", + pd.dtpa_aggname); + } 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) { + xo_close_list("agg_data"); + xo_emit("{:agg_name/%s}", + pd.dtpa_aggname); + 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) { + xo_close_list("agg_data"); + xo_emit("{:agg_name/%s}", pd.dtpa_aggname); + } dt_free(dtp, aggvars); goto nextrec; } @@ -2490,8 +3517,18 @@ 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); + xo_emit("{:tracemem/%s}", s); + dt_free(dtp, s); + n = 0; + } else { + n = dt_print_bytes(dtp, fp, addr, + tracememsize, -33, quiet, 1); + } tracememsize = 0; @@ -2503,31 +3540,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 +3617,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 +3641,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 +4149,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 +4170,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,12 @@ what == DTRACEDROP_PRINCIPAL ? "" : "aggregation ", howmany > 1 ? "s" : "", cpu); + (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 +404,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 +417,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 +449,26 @@ drop.dtdda_total = nval; drop.dtdda_drops = nval - oval; - if (dtp->dt_drophdlr == NULL) + 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) { + 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) { + xo_close_instance("probes"); return (dt_set_errno(dtp, EDT_DROPABORT)); + } + + 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,9 @@ free(dtp->dt_kmods); #endif free(dtp->dt_provs); + + if (dtp->dt_oformat != DTRACE_OFORMAT_NONE) + 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,48 @@ return (0); } +static int +dt_opt_oformat(dtrace_hdl_t *dtp, const char *arg, uintptr_t option __unused) +{ + dtrace_optval_t val = 0; + + if (arg == NULL) { + dtp->dt_oformat = DTRACE_OFORMAT_NONE; + } else { + if (strcmp(arg, "none") == 0) + dtp->dt_oformat = DTRACE_OFORMAT_NONE; + else if (strcmp(arg, "json") == 0) + dtp->dt_oformat = DTRACE_OFORMAT_JSON; + else if (strcmp(arg, "xml") == 0) + dtp->dt_oformat = DTRACE_OFORMAT_XML; + else if (strcmp(arg, "html") == 0) + dtp->dt_oformat = DTRACE_OFORMAT_HTML; + else + return (dt_set_errno(dtp, EDT_BADOPTVAL)); + } + + if (dtp->dt_oformat) + xo_set_flags(NULL, XOF_DTRT); + + switch (dtp->dt_oformat) { + case DTRACE_OFORMAT_JSON: + xo_set_style(NULL, XO_STYLE_JSON); + break; + case DTRACE_OFORMAT_HTML: + xo_set_style(NULL, XO_STYLE_HTML); + break; + case DTRACE_OFORMAT_XML: + xo_set_style(NULL, XO_STYLE_XML); + break; + case DTRACE_OFORMAT_NONE: + break; + default: + return (dt_set_errno(dtp, EDT_BADOPTVAL)); + } + + return (0); +} + static int dt_opt_rate(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { @@ -1046,6 +1089,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,53 @@ value >>= shift; value &= mask; - (void) fprintf(fp, "%#llx", (u_longlong_t)value); + if (pap->pa_dtp->dt_oformat) + xo_emit("{:value/%#llx}", (u_longlong_t)value); + else + (void)fprintf(fp, "%#llx", (u_longlong_t)value); } /* * Dump the contents of memory as a fixed-size integer in hex. */ static void -dt_print_hex(FILE *fp, caddr_t addr, size_t size) +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); + if (dtp->dt_oformat) + xo_emit("{:value/%#x}", *(uint8_t *)addr); + else + (void)fprintf(fp, "%#x", *(uint8_t *)addr); break; case sizeof (uint16_t): - /* LINTED - alignment */ - (void) fprintf(fp, "%#x", *(uint16_t *)addr); + if (dtp->dt_oformat) + xo_emit("{:value/%#x}", *(uint16_t *)addr); + else + /* LINTED - alignment */ + (void)fprintf(fp, "%#x", *(uint16_t *)addr); break; case sizeof (uint32_t): - /* LINTED - alignment */ - (void) fprintf(fp, "%#x", *(uint32_t *)addr); + if (dtp->dt_oformat) + xo_emit("{:value/%#x}", *(uint32_t *)addr); + else + /* LINTED - alignment */ + (void)fprintf(fp, "%#x", *(uint32_t *)addr); break; case sizeof (uint64_t): - (void) fprintf(fp, "%#llx", - /* LINTED - alignment */ - (unsigned long long)*(uint64_t *)addr); + if (dtp->dt_oformat) + xo_emit("{:value/%#llx}", + (unsigned long long)*(uint64_t *)addr); + else + (void)fprintf(fp, "%#llx", + /* LINTED - alignment */ + (unsigned long long)*(uint64_t *)addr); break; default: - (void) fprintf(fp, "", (uint_t)size); + if (dtp->dt_oformat) + xo_emit("{:value/}", (uint_t)size); + else + (void)fprintf(fp, "", (uint_t)size); } } @@ -229,12 +252,16 @@ { 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, ""); + if (dtp->dt_oformat) + xo_emit("{:value/}"); + else + (void)fprintf(fp, ""); return; } @@ -243,7 +270,8 @@ * would be found. */ if (e.cte_format & CTF_INT_VARARGS) { - (void) fprintf(fp, "..."); + if (!dtp->dt_oformat) + (void)fprintf(fp, "..."); return; } @@ -262,16 +290,25 @@ */ if (CTF_IS_CHAR(e)) { char c = *(char *)addr; - if (isprint(c)) - (void) fprintf(fp, "'%c'", c); - else if (c == 0) - (void) fprintf(fp, "'\\0'"); - else - (void) fprintf(fp, "'\\%03o'", c); + if (dtp->dt_oformat) { + if (isprint(c)) + xo_emit("{:value/'%c'}", c); + else if (c == 0) + xo_emit("{:value/'\\0'}"); + else + xo_emit("{:value/'\\%03o'}", c); + } else { + if (isprint(c)) + (void)fprintf(fp, "'%c'", c); + else if (c == 0) + (void)fprintf(fp, "'\\0'"); + else + (void)fprintf(fp, "'\\%03o'", c); + } return; } - dt_print_hex(fp, addr, size); + dt_print_hex(dtp, fp, addr, size); } /* @@ -285,22 +322,37 @@ 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)); + if (dtp->dt_oformat) + xo_emit("{:value/%+.7e}", *((float *)addr)); + else + /* LINTED - alignment */ + (void)fprintf(fp, "%+.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)); + if (dtp->dt_oformat) + xo_emit("{:value/%+.7e}", *((double *)addr)); + else + /* LINTED - alignment */ + (void)fprintf(fp, "%+.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)); + if (dtp->dt_oformat) + xo_emit("{:value/%+.16LE}", + *((long double *)addr)); + else + /* LINTED - alignment */ + (void)fprintf(fp, "%+.16LE", + *((long double *)addr)); } else { - (void) fprintf(fp, ""); + if (dtp->dt_oformat) + xo_emit("{:value/}"); + else + (void)fprintf(fp, ""); } } } @@ -322,15 +374,19 @@ 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); + if (pap->pa_dtp->dt_oformat) + xo_emit("{:value/%s`%s}", dts.dts_object, + dts.dts_name); + else + (void)fprintf(fp, "%s`%s", dts.dts_object, + dts.dts_name); } } } @@ -367,6 +423,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 +431,25 @@ 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); + if (dtp->dt_oformat) + xo_emit("{:value/%p}", (void *)addr); + else + (void)fprintf(fp, "%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); + if (dtp->dt_oformat) + xo_emit("{:value/}", + car.ctr_contents); + else + (void)fprintf(fp, "", + car.ctr_contents); return; } @@ -411,18 +477,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 +536,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 +564,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_NONE) + (void)fprintf(pap->pa_file, "{"); } /* @@ -480,6 +582,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 +601,26 @@ value = *(int32_t *)addr; break; default: - (void) fprintf(fp, "", (uint_t)size); + if (dtp->dt_oformat) + xo_emit("{:value/}", + (uint_t)size); + else + (void)fprintf(fp, "", + (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) { + if (dtp->dt_oformat) + xo_emit("{:value/%s}", ename); + else + (void)fprintf(fp, "%s", ename); + } else { + if (dtp->dt_oformat) + xo_emit("{:value/%d}", value); + else + (void)fprintf(fp, "%d", value); + } } /* @@ -516,7 +631,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_NONE) + (void)fprintf(pap->pa_file, ""); } typedef void dt_printarg_f(ctf_id_t, ulong_t, dt_printarg_t *); @@ -533,6 +649,45 @@ 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} {:type/} {:value/0x%llx}", + name, 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 +789,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 +812,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 +839,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,33 @@ return (dt_string2str(c, str, nbytes)); } + +int +dtrace_oformat(dtrace_hdl_t *dtp) +{ + + return (dtp->dt_oformat != DTRACE_OFORMAT_NONE); +} + +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,19 @@ #define _SC_NPROCESSORS_MAX _SC_NPROCESSORS_CONF #endif +/* + * Values for the dt_oformat property. + */ +#define DTRACE_OFORMAT_NONE 0 +#define DTRACE_OFORMAT_JSON 1 +#define DTRACE_OFORMAT_XML 2 +#define DTRACE_OFORMAT_HTML 3 + +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/lib/libdtrace/Makefile.depend b/cddl/lib/libdtrace/Makefile.depend --- a/cddl/lib/libdtrace/Makefile.depend +++ b/cddl/lib/libdtrace/Makefile.depend @@ -13,6 +13,7 @@ lib/libproc \ lib/librtld_db \ lib/libthr \ + lib/libxo \ usr.bin/awk.host \ usr.bin/yacc.host \ 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