Re: [DTrace-devel] [PATCH v3 2/2] Extend the USDT bit mask to multiple words
Kris Van Hees <[email protected]>
| Newsgroups | dev.linux.lists.dtrace |
|---|---|
| Message-ID | <aRI/[email protected]> |
The pid-specific uprobes support removes the code that this patch applies to, so it is obsolete. On Tue, Aug 19, 2025 at 05:07:15PM -0400, Kris Van Hees via DTrace-devel wrote: > I would like to hold off on this patch a bit longer. I don't think it is too > likely people are running into toruble due to too many clauses, and it would > be nice to avoid adding more provider-specific logic in the core of DTrace. > > My suggestion is that we move the creation of the USDT-specific maps to the > uprobe provider. Since those maps are only ever used by the trampolines > created by this provider, the fds and other data can be stored in the > provider specific private data. Upon first call to generate a trampoline we > can check whether it has been initialized, and if not, create the maps and > populate them. > > This way all the USDT-specific handling remains within the uprobe provider, > and we actually fix the earlier inclusion of creation of USDT maps by the > generic code. > > On Tue, Jul 22, 2025 at 08:53:04PM -0400, [email protected] wrote: > > From: Eugene Loh <[email protected]> > > > > Currently, USDT is limited to 64 probe descriptions since the > > underlying probe uses a 64-bit mask to decide which probes to execute. > > > > Change to a multi-word bit mask that can be extended to however many > > probe descriptions there are. > > > > Also, change the mask words to be 32-bit rather than 64-bit. The reason > > is that, commonly, there will be fewer than 32 probe descriptions. In > > this case, we shorten the value of the "USDT prids" BPF map from 16 bytes > > uint32_t prid; > > long long mask[1]; > > down to 8 bytes > > uint32_t prid; > > uint32_t mask[1]; > > (The second member is smaller and no longer costs extra padding.) > > > > We also add an > > int dt_usdt_mask_bytes; > > to denote how many bytes will be needed for the mask. This value is > > computed by usdt_mask_bytes_init(). Currently, this function is > > awkwardly called in gmap_create_usdt(), just before the value is needed. > > Such a call to a provider-specific function is clumsy, but there are no > > other calls to the provider between compilation (where the number of > > statements is determined) and this map creation. > > > > Signed-off-by: Eugene Loh <[email protected]> > > --- > > libdtrace/dt_bpf.c | 6 +- > > libdtrace/dt_bpf_maps.h | 4 +- > > libdtrace/dt_impl.h | 1 + > > libdtrace/dt_prov_uprobe.c | 87 +++++++++--- > > .../unittest/usdt/tst.manyprobedescriptions.r | 1 + > > .../usdt/tst.manyprobedescriptions.sh | 64 +++++++++ > > .../usdt/tst.manyprobedescriptions2.r | 1 + > > .../usdt/tst.manyprobedescriptions2.sh | 127 ++++++++++++++++++ > > 8 files changed, 267 insertions(+), 24 deletions(-) > > create mode 100644 test/unittest/usdt/tst.manyprobedescriptions.r > > create mode 100755 test/unittest/usdt/tst.manyprobedescriptions.sh > > create mode 100644 test/unittest/usdt/tst.manyprobedescriptions2.r > > create mode 100755 test/unittest/usdt/tst.manyprobedescriptions2.sh > > > > diff --git a/libdtrace/dt_bpf.c b/libdtrace/dt_bpf.c > > index ddd849d0b..fcb53f044 100644 > > --- a/libdtrace/dt_bpf.c > > +++ b/libdtrace/dt_bpf.c > > @@ -967,6 +967,7 @@ gmap_create_probes(dtrace_hdl_t *dtp) > > return 0; > > } > > > > +void usdt_mask_bytes_init(dtrace_hdl_t *dtp); > > /* > > * Create the 'usdt_names' and 'usdt_prids' BPF maps. > > * > > @@ -992,8 +993,11 @@ gmap_create_usdt(dtrace_hdl_t *dtp) > > if (dtp->dt_usdt_namesmap_fd == -1) > > return -1; > > > > + usdt_mask_bytes_init(dtp); > > + > > dtp->dt_usdt_pridsmap_fd = create_gmap(dtp, "usdt_prids", BPF_MAP_TYPE_HASH, > > - sizeof(usdt_prids_map_key_t), sizeof(usdt_prids_map_val_t), nusdtprobes); > > + sizeof(usdt_prids_map_key_t), > > + sizeof(usdt_prids_map_val_t) + dtp->dt_usdt_mask_bytes, nusdtprobes); > > if (dtp->dt_usdt_pridsmap_fd == -1) > > return -1; > > > > diff --git a/libdtrace/dt_bpf_maps.h b/libdtrace/dt_bpf_maps.h > > index 884dc3983..ec5d4d7b1 100644 > > --- a/libdtrace/dt_bpf_maps.h > > +++ b/libdtrace/dt_bpf_maps.h > > @@ -1,6 +1,6 @@ > > /* > > * Oracle Linux DTrace. > > - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. > > + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. > > * Licensed under the Universal Permissive License v 1.0 as shown at > > * http://oss.oracle.com/licenses/upl. > > */ > > @@ -48,7 +48,7 @@ typedef struct usdt_prids_map_key { > > } usdt_prids_map_key_t; > > typedef struct usdt_prids_map_val { > > uint32_t prid; /* should be dtrace_id_t, sys/dtrace_types.h */ > > - long long mask; > > + uint32_t mask[]; > > } usdt_prids_map_val_t; > > > > #ifdef __cplusplus > > diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h > > index 2adc1252b..8bbc4dc1f 100644 > > --- a/libdtrace/dt_impl.h > > +++ b/libdtrace/dt_impl.h > > @@ -397,6 +397,7 @@ struct dtrace_hdl { > > int dt_cpumap_fd; /* file descriptor for the 'cpuinfo' BPF map */ > > int dt_usdt_pridsmap_fd; /* file descriptor for the 'usdt_prids' BPF map */ > > int dt_usdt_namesmap_fd; /* file descriptor for the 'usdt_names' BPF map */ > > + int dt_usdt_mask_bytes; /* size of USDT mask in bytes */ > > dtrace_handle_err_f *dt_errhdlr; /* error handler, if any */ > > void *dt_errarg; /* error handler argument */ > > dtrace_handle_drop_f *dt_drophdlr; /* drop handler, if any */ > > diff --git a/libdtrace/dt_prov_uprobe.c b/libdtrace/dt_prov_uprobe.c > > index e8f9f8c98..8b55fe319 100644 > > --- a/libdtrace/dt_prov_uprobe.c > > +++ b/libdtrace/dt_prov_uprobe.c > > @@ -403,7 +403,7 @@ clean_usdt_probes(dtrace_hdl_t *dtp) > > int fdprids = dtp->dt_usdt_pridsmap_fd; > > int fdnames = dtp->dt_usdt_namesmap_fd; > > usdt_prids_map_key_t key, nxt; > > - usdt_prids_map_val_t val; > > + usdt_prids_map_val_t *val = alloca(sizeof(usdt_prids_map_val_t) + dtp->dt_usdt_mask_bytes); > > list_key_t keys_to_delete, *elem, *elem_next; > > dt_probe_t *prp, *prp_next; > > > > @@ -418,7 +418,7 @@ clean_usdt_probes(dtrace_hdl_t *dtp) > > while (dt_bpf_map_next_key(fdprids, &key, &nxt) == 0) { > > memcpy(&key, &nxt, sizeof(usdt_prids_map_key_t)); > > > > - if (dt_bpf_map_lookup(fdprids, &key, &val) == -1) > > + if (dt_bpf_map_lookup(fdprids, &key, val) == -1) > > return dt_set_errno(dtp, EDT_BPF); > > > > /* Check if the process is still running. */ > > @@ -431,7 +431,7 @@ clean_usdt_probes(dtrace_hdl_t *dtp) > > * we might delete the same usdt_names entry > > * multiple times. That's okay. > > */ > > - dt_bpf_map_delete(fdnames, &val.prid); > > + dt_bpf_map_delete(fdnames, &val->prid); > > > > /* > > * Delete the usdt_prids entry. > > @@ -452,7 +452,7 @@ clean_usdt_probes(dtrace_hdl_t *dtp) > > * FIXME. There might be another case, where the process > > * is still running, but some of its USDT probes are gone? > > * So maybe we have to check for the existence of one of > > - * dtrace_probedesc_t *pdp = dtp->dt_probes[val.prid]->desc; > > + * dtrace_probedesc_t *pdp = dtp->dt_probes[val->prid]->desc; > > * char *prv = ...pdp->prv minus the numerial part; > > * > > * /run/dtrace/probes/$pid/$pdp->prv/$pdp->mod/$pdp->fun/$pdp->prb > > @@ -590,6 +590,31 @@ static void usdt_error(dt_pcb_t *pcb, const char *fmt, ...) > > longjmp(pcb->pcb_jmpbuf, EDT_COMPILER); > > } > > > > +void usdt_mask_bytes_init(dtrace_hdl_t *dtp) > > +{ > > + int i, n = 0, w = sizeof(((usdt_prids_map_val_t *)0)->mask[0]); > > + > > + /* Count how many statements cannot be ignored, regardless of uprp. */ > > + for (i = 0; i < dtp->dt_stmt_nextid; i++) { > > + dtrace_stmtdesc_t *stp; > > + > > + stp = dtp->dt_stmts[i]; > > + if (stp == NULL || ignore_clause(dtp, i, NULL)) > > + continue; > > + > > + n++; > > + } > > + > > + /* Determine how many bytes are needed for this many bits. */ > > + n = (n + CHAR_BIT - 1) / CHAR_BIT; > > + > > + /* Determine how many words are needed for this many bytes. */ > > + n = (n + w - 1) / w; > > + > > + /* Determine how many bytes are needed. */ > > + dtp->dt_usdt_mask_bytes = (n ? n : 1) * w; > > +} > > + > > static int add_probe_uprobe(dtrace_hdl_t *dtp, dt_probe_t *prp) > > { > > dtrace_difo_t *dp; > > @@ -651,6 +676,7 @@ static int add_probe_usdt(dtrace_hdl_t *dtp, dt_probe_t *prp) > > int fd = dtp->dt_usdt_namesmap_fd; > > pid_t pid; > > list_probe_t *pup; > > + usdt_prids_map_val_t *val; > > > > /* Add probe name elements to usdt_names map. */ > > p = probnam; > > @@ -686,11 +712,11 @@ static int add_probe_usdt(dtrace_hdl_t *dtp, dt_probe_t *prp) > > } > > > > /* Add prid and bit mask to usdt_prids map. */ > > + val = alloca(sizeof(usdt_prids_map_val_t) + dtp->dt_usdt_mask_bytes); > > for (pup = prp->prv_data; pup != NULL; pup = dt_list_next(pup)) { > > dt_probe_t *uprp = pup->probe; > > - long long mask = 0, bit = 1; > > + uint32_t iword = 0, mask = 0, bit = 1; > > usdt_prids_map_key_t key; > > - usdt_prids_map_val_t val; > > dt_uprobe_t *upp = uprp->prv_data; > > > > /* > > @@ -704,15 +730,24 @@ static int add_probe_usdt(dtrace_hdl_t *dtp, dt_probe_t *prp) > > if (uprp->prov->impl == &dt_uprobe && !(upp->flags & PP_IS_ENABLED)) { > > int n; > > > > + /* > > + * The loop over n to dtp->dt_stmt_nextid, skipping > > + * ignore_clause(), should be the same here as in > > + * the trampoline. > > + */ > > for (n = 0; n < dtp->dt_stmt_nextid; n++) { > > dtrace_stmtdesc_t *stp; > > > > stp = dtp->dt_stmts[n]; > > - if (stp == NULL) > > + if (stp == NULL || ignore_clause(dtp, n, uprp)) > > continue; > > > > - if (ignore_clause(dtp, n, uprp)) > > - continue; > > + if (bit == 0) { > > + val->mask[iword] = mask; > > + mask = 0; > > + iword++; > > + bit = 1; > > + } > > > > if (dt_gmatch(prp->desc->prv, stp->dtsd_ecbdesc->dted_probe.prv) && > > dt_gmatch(prp->desc->mod, stp->dtsd_ecbdesc->dted_probe.mod) && > > @@ -727,11 +762,11 @@ static int add_probe_usdt(dtrace_hdl_t *dtp, dt_probe_t *prp) > > key.pid = pid; > > key.uprid = uprp->desc->id; > > > > - val.prid = prp->desc->id; > > - val.mask = mask; > > + val->prid = prp->desc->id; > > + val->mask[iword] = mask; > > > > // FIXME Check return value, but how should errors be handled? > > - dt_bpf_map_update(dtp->dt_usdt_pridsmap_fd, &key, &val); > > + dt_bpf_map_update(dtp->dt_usdt_pridsmap_fd, &key, val); > > } > > > > return 0; > > @@ -1452,7 +1487,7 @@ static int trampoline(dt_pcb_t *pcb, uint_t exitlbl) > > const list_probe_t *pop; > > uint_t lbl_exit = pcb->pcb_exitlbl; > > dt_ident_t *usdt_prids = dt_dlib_get_map(dtp, "usdt_prids"); > > - int n; > > + int n, ibit, w = CHAR_BIT * sizeof(((usdt_prids_map_val_t *)0)->mask[0]); > > > > assert(usdt_prids != NULL); > > > > @@ -1539,7 +1574,8 @@ static int trampoline(dt_pcb_t *pcb, uint_t exitlbl) > > */ > > assert(sizeof(usdt_prids_map_key_t) <= DT_STK_SLOT_SZ); > > emit(dlp, BPF_STORE(BPF_W, BPF_REG_FP, DT_TRAMP_SP_SLOT(0), BPF_REG_0)); > > - emit(dlp, BPF_STORE_IMM(BPF_W, BPF_REG_FP, DT_TRAMP_SP_SLOT(0) + (int)sizeof(pid_t), uprp->desc->id)); > > + emit(dlp, BPF_STORE_IMM(BPF_W, BPF_REG_FP, > > + DT_TRAMP_SP_SLOT(0) + (int)sizeof(pid_t), uprp->desc->id)); > > dt_cg_xsetx(dlp, usdt_prids, DT_LBL_NONE, BPF_REG_1, usdt_prids->di_id); > > emit(dlp, BPF_MOV_REG(BPF_REG_2, BPF_REG_FP)); > > emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, DT_TRAMP_SP_SLOT(0))); > > @@ -1573,8 +1609,8 @@ static int trampoline(dt_pcb_t *pcb, uint_t exitlbl) > > emit(dlp, BPF_LOAD(BPF_W, BPF_REG_1, BPF_REG_0, 0)); > > emit(dlp, BPF_STORE(BPF_W, BPF_REG_7, DMST_PRID, BPF_REG_1)); > > > > - /* Read the bit mask from the table lookup in %r6. */ // FIXME someday, extend this past 64 bits > > - emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_6, BPF_REG_0, offsetof(usdt_prids_map_val_t, mask))); > > + /* Store the value key for reuse. */ > > + emit(dlp, BPF_STORE(BPF_DW, BPF_REG_FP, DT_TRAMP_SP_SLOT(0), BPF_REG_0)); > > > > /* > > * Apply arg mappings, if needed. > > @@ -1588,21 +1624,29 @@ static int trampoline(dt_pcb_t *pcb, uint_t exitlbl) > > /* > > * Hold the bit mask in %r6 between clause calls. > > */ > > - for (n = 0; n < dtp->dt_stmt_nextid; n++) { > > + /* > > + * The loop over n to dtp->dt_stmt_nextid, skipping > > + * ignore_clause(), should be the same here as in > > + * add_probe_usdt(). > > + */ > > + for (ibit = n = 0; n < dtp->dt_stmt_nextid; n++) { > > dtrace_stmtdesc_t *stp; > > dt_ident_t *idp; > > uint_t lbl_next; > > > > stp = dtp->dt_stmts[n]; > > - if (stp == NULL) > > - continue; > > - > > - if (ignore_clause(dtp, n, uprp)) > > + if (stp == NULL || ignore_clause(dtp, n, uprp)) > > continue; > > > > idp = stp->dtsd_clause; > > lbl_next = dt_irlist_label(dlp); > > > > + /* Load the next word of the bit mask into %r6. */ > > + if (ibit % w == 0) { > > + emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_0, BPF_REG_FP, DT_TRAMP_SP_SLOT(0))); > > + emit(dlp, BPF_LOAD(BPF_W, BPF_REG_6, BPF_REG_0, offsetof(usdt_prids_map_val_t, mask[ibit / w]))); > > + } > > + > > /* If the lowest %r6 bit is 0, skip over this clause. */ > > emit(dlp, BPF_MOV_REG(BPF_REG_1, BPF_REG_6)); > > emit(dlp, BPF_ALU64_IMM(BPF_AND, BPF_REG_1, 1)); > > @@ -1630,6 +1674,7 @@ static int trampoline(dt_pcb_t *pcb, uint_t exitlbl) > > > > /* Right-shift %r6. */ > > emit(dlp, BPF_ALU64_IMM(BPF_RSH, BPF_REG_6, 1)); > > + ibit++; > > } > > > > out: > > diff --git a/test/unittest/usdt/tst.manyprobedescriptions.r b/test/unittest/usdt/tst.manyprobedescriptions.r > > new file mode 100644 > > index 000000000..2e9ba477f > > --- /dev/null > > +++ b/test/unittest/usdt/tst.manyprobedescriptions.r > > @@ -0,0 +1 @@ > > +success > > diff --git a/test/unittest/usdt/tst.manyprobedescriptions.sh b/test/unittest/usdt/tst.manyprobedescriptions.sh > > new file mode 100755 > > index 000000000..92a61d5b7 > > --- /dev/null > > +++ b/test/unittest/usdt/tst.manyprobedescriptions.sh > > @@ -0,0 +1,64 @@ > > +#!/bin/bash > > +# > > +# Oracle Linux DTrace. > > +# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. > > +# Licensed under the Universal Permissive License v 1.0 as shown at > > +# http://oss.oracle.com/licenses/upl. > > + > > +dtrace=$1 > > +TRIGGER=$PWD/test/triggers/usdt-tst-args > > + > > +DIRNAME="$tmpdir/usdt-many_probe_descriptions.$$.$RANDOM" > > +mkdir -p $DIRNAME > > +cd $DIRNAME > > + > > +# Construct the D scripts and output files. > > +# We stick 80 probe descriptions in each of 3 scripts to test > > +# USDT's ability to handle hundreds of probe descriptions. > > +for d in 0 1 2; do > > +for x in 00 01 02 03 04 05 06 07 08 09 \ > > + 10 11 12 13 14 15 16 17 18 19 \ > > + 20 21 22 23 24 25 26 27 28 29 \ > > + 30 31 32 33 34 35 36 37 38 39 \ > > + 40 41 42 43 44 45 46 47 48 49 \ > > + 50 51 52 53 54 55 56 57 58 59 \ > > + 60 61 62 63 64 65 66 67 68 69 \ > > + 70 71 72 73 74 75 76 77 78 79 \ > > +; do > > + echo 'test_prov$target:::place { printf("'$d$x'\n"); }' >> D$d.d > > + echo $d$x >> expect.txt > > +done > > +done > > +echo 'test_prov$target:::place { exit(0); }' >> D$d.d > > +echo >> expect.txt > > + > > +# Run DTrace. > > + > > +$dtrace $dt_flags -c $TRIGGER -q -s D0.d -s D1.d -s D2.d >& actual.txt > > +if [ $? -eq 0 ]; then > > + if diff -q expect.txt actual.txt > /dev/null; then > > + echo success > > + exit 0 > > + else > > + echo ERROR: did not get expected results > > + echo === expect.txt > > + cat expect.txt > > + echo === actual.txt > > + cat actual.txt > > + echo === diff > > + diff expect.txt actual.txt > > + fi > > +else > > + echo ERROR: dtrace error > > + echo ==== output > > + cat actual.txt > > +fi > > + > > +echo ==== script D0.d > > +cat D0.d > > +echo ==== script D1.d > > +cat D1.d > > +echo ==== script D2.d > > +cat D2.d > > + > > +exit 1 > > diff --git a/test/unittest/usdt/tst.manyprobedescriptions2.r b/test/unittest/usdt/tst.manyprobedescriptions2.r > > new file mode 100644 > > index 000000000..2e9ba477f > > --- /dev/null > > +++ b/test/unittest/usdt/tst.manyprobedescriptions2.r > > @@ -0,0 +1 @@ > > +success > > diff --git a/test/unittest/usdt/tst.manyprobedescriptions2.sh b/test/unittest/usdt/tst.manyprobedescriptions2.sh > > new file mode 100755 > > index 000000000..8001cec0b > > --- /dev/null > > +++ b/test/unittest/usdt/tst.manyprobedescriptions2.sh > > @@ -0,0 +1,127 @@ > > +#!/bin/bash > > +# > > +# Oracle Linux DTrace. > > +# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. > > +# Licensed under the Universal Permissive License v 1.0 as shown at > > +# http://oss.oracle.com/licenses/upl. > > + > > +# This test uses many probes and probe descriptions. Therefore, the > > +# number of BPF programs to load into the kernel -- dt_bpf_load_prog() > > +# calling prp->prov->impl->load_prog(), which is dt_bpf_prog_load() -- > > +# and the duration of each load are both increasing. > > +# @@timeout: 400 > > + > > +dtrace=$1 > > + > > +DIRNAME="$tmpdir/usdt-many_probe_descriptions2.$$.$RANDOM" > > +mkdir -p $DIRNAME > > +cd $DIRNAME > > + > > +# Set the lists. > > +# - The probes will be foo$x$y. > > +# - The probe descriptions will be foo$x* and foo*$y, for each $d. > > +# So if there are nx items in xlist, ny in ylist, and nd in dlist, > > +# - there will be roughly nx*ny probes > > +# - there will be roughly (nx+ny)*nd probe descriptions > > + > > +xlist="a b c d e f g h i j k l m" > > +ylist="n o p q r s t u v w x y z" > > +dlist="0 1 2 3 4 5 6 7 8" > > + > > +# Make the trigger: Preambles. > > + > > +echo "provider testprov {" > prov.d > > + > > +echo '#include "prov.h"' > main.c > > +echo 'int main(int argc, char **argv) {' >> main.c > > + > > +# Make the trigger: Loop over the probes. > > + > > +for x in $xlist; do > > +for y in $ylist; do > > + echo "probe foo$x$y();" >> prov.d > > + echo "TESTPROV_FOO$x$y();" | awk '{ print(toupper($1)) }' >> main.c > > +done > > +done > > + > > +# Make the trigger: Epilogues. > > + > > +echo "};" >> prov.d > > +echo "return 0; }" >> main.c > > + > > +# Build the trigger. > > + > > +$dtrace $dt_flags -h -s prov.d > > +if [ $? -ne 0 ]; then > > + echo "failed to generate header file" >&2 > > + cat prov.d > > + exit 1 > > +fi > > +$CC $test_cppflags -c main.c > > +if [ $? -ne 0 ]; then > > + echo "failed to compile test" >&2 > > + cat main.c > > + exit 1 > > +fi > > +$dtrace $dt_flags -G -64 -s prov.d main.o > > +if [ $? -ne 0 ]; then > > + echo "failed to create DOF" >&2 > > + exit 1 > > +fi > > +$CC $test_ldflags -o main main.o prov.o > > +if [ $? -ne 0 ]; then > > + echo "failed to link final executable" >&2 > > + exit 1 > > +fi > > + > > +# Prepare the D script, generating the probe descriptions. > > + > > +rm -f D.d > > +for d in $dlist; do > > + for x in $xlist; do > > + echo 'testprov$target:::foo'$x'* { printf("'$d' '$x'* %s\n", probename) }' >> D.d > > + done > > + for y in $ylist; do > > + echo 'testprov$target:::foo*'$y' { printf("'$d' *'$y' %s\n", probename) }' >> D.d > > + done > > +done > > + > > +# Prepare the expected output. > > + > > +for x in $xlist; do > > +for y in $ylist; do > > +for d in $dlist; do > > + echo $d $x'*' foo$x$y >> expect.txt > > + echo $d '*'$y foo$x$y >> expect.txt > > +done > > +done > > +done > > +echo >> expect.txt > > + > > +# Run DTrace. > > + > > +$dtrace $dt_flags -c ./main -qs D.d >& actual.txt > > +if [ $? -ne 0 ]; then > > + echo ERROR: dtrace error > > + echo "==== D script" > > + cat D.d > > + echo "==== output" > > + cat actual.txt > > + exit 1 > > +fi > > + > > +# Check results. > > + > > +if diff -q expect.txt actual.txt; then > > + echo success > > + exit 0 > > +else > > + echo ERROR: unexpected results > > + echo "==== expect" > > + cat expect.txt > > + echo "==== actual" > > + cat actual.txt > > + echo "==== diff" > > + diff expect.txt actual.txt > > + exit 1 > > +fi > > -- > > 2.43.5 > > > > _______________________________________________ > DTrace-devel mailing list > [email protected] > https://oss.oracle.com/mailman/listinfo/dtrace-devel