From kris.van.hees at oracle.com Sat Feb 1 02:04:21 2025 From: kris.van.hees at oracle.com (Kris Van Hees) Date: Fri, 31 Jan 2025 21:04:21 -0500 Subject: [DTrace-devel] [PATCH v2 4/6] procfs: implement d_execargs() for pr_psargs translator support In-Reply-To: References: Message-ID: On Fri, Jan 31, 2025 at 03:29:50PM -0500, Eugene Loh wrote: > On 1/31/25 11:37, Kris Van Hees wrote: > > > On Thu, Jan 30, 2025 at 02:22:44PM -0500, Eugene Loh wrote: > > > For > > > test/unittest/funcs/d_execargs/tst.d_execargs.d > > > test/unittest/proc/tst.pr_psargs.d > > > how about a few more arguments?? E.g., > > > system("/bin/echo TEST abcde fghijkl"); > > > (I don't have anything particular in mind that this might catch. Just trying > > > to poke a little harder.) > > Sure, although it does not really improve the testing because we are looping > > over all characters, changing '\0' into ' ', except for the last one. If it > > works for one it works for all, but I also do not mind adding a few extra > > arguments. > > Cool.? Again, I have nothing particular in mind that this would stress -- if > it catches something, it would be something unforeseen. > > Incidentally, I hadn't run proc/tst.pr_args.d.? I just tried: > > *)? On ARM UEK6 (OL7 and OL8), I got > dtrace: error in dt_clause_1 for probe ID 98979 > (proc:vmlinux::exec-success): invalid address (0xfffff895fa9f) at BPF pc 934 > dtrace: error in dt_clause_1 for probe ID 98979 > (proc:vmlinux::exec-success): invalid address (0xffffcb24faa0) at BPF pc 934 > Maybe UEK6 is not of interest, but there you have it.? Some copyin thing?? I > don't know. Hm, yes, come to think of it - arg_start and arg_end seem to indeed point to the userspace stack so they might very well be userspace addresses. I am not sure, so I will investigate and if necessary will adjust the patch to handle that. > *)? On most platforms, I got > ???????????? 0? 1? 2? 3? 4? 5? 6? 7? 8? 9? a? b? c? d? e? f 0123456789abcdef > ???????? 0: 2f 62 69 6e 2f 65 63 68 6f 20 54 45 53 54 00 20 /bin/echo TEST. > ??????? 10: 54 45 53 54 00 00 00 00 00 00 00 00 00 00 00 00 TEST............ > ??????? 20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ > ??????? 30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ > ??????? 40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ > I do not know where those extra chars are coming from, but the test is > exposing itself to printing out garbage in any case.? Maybe use printf() > instead of trace so that we do not print out the buffer past the NUL char > (and get more compact and readable output to boot)? Aha, that is a bug. Well, the output is not (and should remain as-is precisely for catching things like this)... pr_psargs[] is defined as a char array of length 80. So, the output is accurate but we need to ensure we clear the remainder of the string data. From kris.van.hees at oracle.com Thu Feb 6 16:46:39 2025 From: kris.van.hees at oracle.com (Kris Van Hees) Date: Thu, 06 Feb 2025 11:46:39 -0500 Subject: [DTrace-devel] [PATCH] trace: fix char-array handling Message-ID: The special handling of strings in the consumer's trace implementation did not correctly account for char-arrays that do not necessarily are intended to hold string data. Thr trace action is documented to print data as an ASCII string if it can be represented as such, i.e. if it comprises only printable characters, optionally terminated by one or more 0-bytes. The implementation for trace on both producer and consumer side was using the data alignment as a marker to determine whether data should be printed as a string (alignment == 1) or not. The real alignment of the data should be used, reverting code back to the necessary heuristics in dt_print_bytes(). The dtrace_diftype struct has its dtdt_pad member renamed as dtdt_align. Added several new test cases to cover various possibilities. Signed-off-by: Kris Van Hees --- include/dtrace/dif.h | 2 +- libdtrace/dt_cg.c | 16 ++++++---- libdtrace/dt_consume.c | 20 ++----------- libdtrace/dt_parser.c | 2 +- .../actions/trace/tst.array-char-multi-nul.d | 30 +++++++++++++++++++ .../actions/trace/tst.array-char-multi-nul.r | 8 +++++ .../trace/tst.array-char-str-multi-nul.d | 30 +++++++++++++++++++ .../trace/tst.array-char-str-multi-nul.r | 5 ++++ .../actions/trace/tst.array-char-str-no-nul.d | 30 +++++++++++++++++++ .../actions/trace/tst.array-char-str-no-nul.r | 5 ++++ .../actions/trace/tst.array-char-str.d | 30 +++++++++++++++++++ .../actions/trace/tst.array-char-str.r | 5 ++++ .../trace/tst.array-char-unprintable.d | 30 +++++++++++++++++++ .../trace/tst.array-char-unprintable.r | 8 +++++ test/unittest/actions/trace/tst.array.d | 11 +++++-- test/unittest/actions/trace/tst.array.r | 2 +- test/unittest/actions/trace/tst.array.r.p | 6 ---- 17 files changed, 206 insertions(+), 34 deletions(-) create mode 100644 test/unittest/actions/trace/tst.array-char-multi-nul.d create mode 100644 test/unittest/actions/trace/tst.array-char-multi-nul.r create mode 100644 test/unittest/actions/trace/tst.array-char-str-multi-nul.d create mode 100644 test/unittest/actions/trace/tst.array-char-str-multi-nul.r create mode 100644 test/unittest/actions/trace/tst.array-char-str-no-nul.d create mode 100644 test/unittest/actions/trace/tst.array-char-str-no-nul.r create mode 100644 test/unittest/actions/trace/tst.array-char-str.d create mode 100644 test/unittest/actions/trace/tst.array-char-str.r create mode 100644 test/unittest/actions/trace/tst.array-char-unprintable.d create mode 100644 test/unittest/actions/trace/tst.array-char-unprintable.r delete mode 100755 test/unittest/actions/trace/tst.array.r.p diff --git a/include/dtrace/dif.h b/include/dtrace/dif.h index 576bda2c..70257b2f 100644 --- a/include/dtrace/dif.h +++ b/include/dtrace/dif.h @@ -33,7 +33,7 @@ typedef struct dtrace_diftype { uint8_t dtdt_kind; uint8_t dtdt_ckind; uint8_t dtdt_flags; - uint8_t dtdt_pad; + uint8_t dtdt_align; uint32_t dtdt_size; } dtrace_diftype_t; diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c index 6e74b4b0..a5c9aa09 100644 --- a/libdtrace/dt_cg.c +++ b/libdtrace/dt_cg.c @@ -1601,12 +1601,11 @@ static int dt_cg_store_val(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind, dt_pfargv_t *pfp, int arg) { - dtrace_diftype_t vtype; dtrace_hdl_t *dtp = pcb->pcb_hdl; dt_irlist_t *dlp = &pcb->pcb_ir; dt_regset_t *drp = pcb->pcb_regs; uint_t off; - size_t size; + size_t size, align; int not_null = 0; int cflags = pcb->pcb_stmt->dtsd_clauseflags; @@ -1619,10 +1618,14 @@ dt_cg_store_val(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind, longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); emit(dlp, BPF_MOV_IMM(dnp->dn_reg, dnp->dn_ident->di_id)); size = sizeof(dnp->dn_ident->di_id); + align = size; } else { + dtrace_diftype_t vtype; + dt_cg_node(dnp, dlp, drp); dt_node_diftype(dtp, dnp, &vtype); size = vtype.dtdt_size; + align = vtype.dtdt_align; /* * A DEREF of a REF node does not get resolved in dt_cg_node() @@ -1664,7 +1667,7 @@ dt_cg_store_val(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind, if (dt_node_is_scalar(dnp) || dt_node_is_float(dnp) || dnp->dn_kind == DT_NODE_AGG) { - off = dt_rec_add(dtp, dt_cg_fill_gap, kind, size, size, pfp, + off = dt_rec_add(dtp, dt_cg_fill_gap, kind, size, align, pfp, arg); emit(dlp, BPF_STOREX(size, BPF_REG_9, off, dnp->dn_reg)); @@ -1678,8 +1681,8 @@ dt_cg_store_val(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind, dt_cg_check_ptr_arg(dlp, drp, dnp, NULL); TRACE_REGSET("store_val(): Begin "); - off = dt_rec_add(dtp, dt_cg_fill_gap, kind, size + 1, - 1, pfp, arg); + off = dt_rec_add(dtp, dt_cg_fill_gap, kind, size + 1, 1, pfp, + arg); /* * Copy the string data (no more than STRSIZE + 1 bytes) to the @@ -1706,7 +1709,8 @@ dt_cg_store_val(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind, /* Handle tracing of by-ref values (arrays, struct, union). */ if ((dnp->dn_flags & DT_NF_REF) || (arg & DT_NF_REF)) { - off = dt_rec_add(dtp, dt_cg_fill_gap, kind, size, 2, pfp, arg); + off = dt_rec_add(dtp, dt_cg_fill_gap, kind, size, align, pfp, + arg); TRACE_REGSET("store_val(): Begin "); if (!not_null) diff --git a/libdtrace/dt_consume.c b/libdtrace/dt_consume.c index 74004c69..58a2ead9 100644 --- a/libdtrace/dt_consume.c +++ b/libdtrace/dt_consume.c @@ -1921,23 +1921,9 @@ dt_print_trace(dtrace_hdl_t *dtp, FILE *fp, dtrace_recdesc_t *rec, if (dtp->dt_options[DTRACEOPT_RAWBYTES] != DTRACEOPT_UNSET) return dt_print_rawbytes(dtp, fp, data, rec->dtrd_size); - /* - * String data can be recognized as a non-scalar data item with - * alignment == 1. - * Any other non-scalar data items are printed as a byte stream. - */ - if (rec->dtrd_arg == DT_NF_REF) { - char *s = (char *)data; - - if (rec->dtrd_alignment > 1) - return dt_print_rawbytes(dtp, fp, data, rec->dtrd_size); - - /* We have a string. Print it. */ - if (quiet) - return dt_printf(dtp, fp, "%s", s); - else - return dt_printf(dtp, fp, " %-33s", s); - } + /* Handle non-scalar data. */ + if (rec->dtrd_arg == DT_NF_REF) + return dt_print_bytes(dtp, fp, data, rec->dtrd_size, 33, quiet); /* * Differentiate between signed and unsigned numeric values. diff --git a/libdtrace/dt_parser.c b/libdtrace/dt_parser.c index 325ba881..eefe8341 100644 --- a/libdtrace/dt_parser.c +++ b/libdtrace/dt_parser.c @@ -4914,7 +4914,7 @@ dt_node_diftype(dtrace_hdl_t *dtp, const dt_node_t *dnp, dtrace_diftype_t *tp) } tp->dtdt_flags = (dnp->dn_flags & DT_NF_REF) ? DIF_TF_BYREF : 0; - tp->dtdt_pad = 0; + tp->dtdt_align = ctf_type_align(dnp->dn_ctfp, dnp->dn_type); tp->dtdt_size = ctf_type_size(dnp->dn_ctfp, dnp->dn_type); } diff --git a/test/unittest/actions/trace/tst.array-char-multi-nul.d b/test/unittest/actions/trace/tst.array-char-multi-nul.d new file mode 100644 index 00000000..187fb711 --- /dev/null +++ b/test/unittest/actions/trace/tst.array-char-multi-nul.d @@ -0,0 +1,30 @@ +/* + * Oracle Linux DTrace. + * Copyright (c) 2022, 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. + */ + +/* + * ASSERTION: The trace() action prints a char-array of printable characters + * with multiple 0-bytes in its content. + * + * SECTION: Actions and Subroutines/trace() + */ + +char n[9]; + +BEGIN +{ + n[0] = 'a'; + n[1] = 'A'; + n[2] = 0; + n[3] = 'B'; + n[4] = 0; + n[5] = 'C'; + n[6] = 0; + n[7] = 'D'; + n[8] = 'e'; + trace(n); + exit(0); +} diff --git a/test/unittest/actions/trace/tst.array-char-multi-nul.r b/test/unittest/actions/trace/tst.array-char-multi-nul.r new file mode 100644 index 00000000..c1361e75 --- /dev/null +++ b/test/unittest/actions/trace/tst.array-char-multi-nul.r @@ -0,0 +1,8 @@ + FUNCTION:NAME + :BEGIN + 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef + 0: 61 41 00 42 00 43 00 44 65 aA.B.C.De + + +-- @@stderr -- +dtrace: script 'test/unittest/actions/trace/tst.array-char-multi-nul.d' matched 1 probe diff --git a/test/unittest/actions/trace/tst.array-char-str-multi-nul.d b/test/unittest/actions/trace/tst.array-char-str-multi-nul.d new file mode 100644 index 00000000..75164cd3 --- /dev/null +++ b/test/unittest/actions/trace/tst.array-char-str-multi-nul.d @@ -0,0 +1,30 @@ +/* + * Oracle Linux DTrace. + * Copyright (c) 2022, 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. + */ + +/* + * ASSERTION: The trace() action prints a char-array of printable characters + * followed y by multiple 0-bytes correctly. + * + * SECTION: Actions and Subroutines/trace() + */ + +char n[9]; + +BEGIN +{ + n[0] = 'a'; + n[1] = 'A'; + n[2] = 'b'; + n[3] = 'B'; + n[4] = 'c'; + n[5] = 0; + n[6] = 0; + n[7] = 0; + n[8] = 0; + trace(n); + exit(0); +} diff --git a/test/unittest/actions/trace/tst.array-char-str-multi-nul.r b/test/unittest/actions/trace/tst.array-char-str-multi-nul.r new file mode 100644 index 00000000..1951ae69 --- /dev/null +++ b/test/unittest/actions/trace/tst.array-char-str-multi-nul.r @@ -0,0 +1,5 @@ + FUNCTION:NAME + :BEGIN aAbBc + +-- @@stderr -- +dtrace: script 'test/unittest/actions/trace/tst.array-char-str-multi-nul.d' matched 1 probe diff --git a/test/unittest/actions/trace/tst.array-char-str-no-nul.d b/test/unittest/actions/trace/tst.array-char-str-no-nul.d new file mode 100644 index 00000000..532c3519 --- /dev/null +++ b/test/unittest/actions/trace/tst.array-char-str-no-nul.d @@ -0,0 +1,30 @@ +/* + * Oracle Linux DTrace. + * Copyright (c) 2022, 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. + */ + +/* + * ASSERTION: The trace() action prints a char-aarray of printable characters + * (not terminated) correctly. + * + * SECTION: Actions and Subroutines/trace() + */ + +char n[9]; + +BEGIN +{ + n[0] = 'a'; + n[1] = 'A'; + n[2] = 'b'; + n[3] = 'B'; + n[4] = 'c'; + n[5] = 'C'; + n[6] = 'd'; + n[7] = 'D'; + n[8] = 'e'; + trace(n); + exit(0); +} diff --git a/test/unittest/actions/trace/tst.array-char-str-no-nul.r b/test/unittest/actions/trace/tst.array-char-str-no-nul.r new file mode 100644 index 00000000..3d56e520 --- /dev/null +++ b/test/unittest/actions/trace/tst.array-char-str-no-nul.r @@ -0,0 +1,5 @@ + FUNCTION:NAME + :BEGIN aAbBcCdDe + +-- @@stderr -- +dtrace: script 'test/unittest/actions/trace/tst.array-char-str-no-nul.d' matched 1 probe diff --git a/test/unittest/actions/trace/tst.array-char-str.d b/test/unittest/actions/trace/tst.array-char-str.d new file mode 100644 index 00000000..70f6371f --- /dev/null +++ b/test/unittest/actions/trace/tst.array-char-str.d @@ -0,0 +1,30 @@ +/* + * Oracle Linux DTrace. + * Copyright (c) 2022, 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. + */ + +/* + * ASSERTION: The trace() action prints a char-array of printable characters + * (terminated) correctly. + * + * SECTION: Actions and Subroutines/trace() + */ + +char n[9]; + +BEGIN +{ + n[0] = 'a'; + n[1] = 'A'; + n[2] = 'b'; + n[3] = 'B'; + n[4] = 'c'; + n[5] = 'C'; + n[6] = 'd'; + n[7] = 'D'; + n[8] = '\0'; + trace(n); + exit(0); +} diff --git a/test/unittest/actions/trace/tst.array-char-str.r b/test/unittest/actions/trace/tst.array-char-str.r new file mode 100644 index 00000000..9778ddd2 --- /dev/null +++ b/test/unittest/actions/trace/tst.array-char-str.r @@ -0,0 +1,5 @@ + FUNCTION:NAME + :BEGIN aAbBcCdD + +-- @@stderr -- +dtrace: script 'test/unittest/actions/trace/tst.array-char-str.d' matched 1 probe diff --git a/test/unittest/actions/trace/tst.array-char-unprintable.d b/test/unittest/actions/trace/tst.array-char-unprintable.d new file mode 100644 index 00000000..d88df369 --- /dev/null +++ b/test/unittest/actions/trace/tst.array-char-unprintable.d @@ -0,0 +1,30 @@ +/* + * Oracle Linux DTrace. + * Copyright (c) 2022, 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. + */ + +/* + * ASSERTION: The trace() action prints a char-array with a non-printable + * character correctly. + * + * SECTION: Actions and Subroutines/trace() + */ + +char n[9]; + +BEGIN +{ + n[0] = 'a'; + n[1] = 'A'; + n[2] = 'b'; + n[3] = 'B'; + n[4] = 'c'; + n[5] = 5; + n[6] = 'd'; + n[7] = 'D'; + n[8] = '\0'; + trace(n); + exit(0); +} diff --git a/test/unittest/actions/trace/tst.array-char-unprintable.r b/test/unittest/actions/trace/tst.array-char-unprintable.r new file mode 100644 index 00000000..1f84fcfa --- /dev/null +++ b/test/unittest/actions/trace/tst.array-char-unprintable.r @@ -0,0 +1,8 @@ + FUNCTION:NAME + :BEGIN + 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef + 0: 61 41 62 42 63 05 64 44 00 aAbBc.dD. + + +-- @@stderr -- +dtrace: script 'test/unittest/actions/trace/tst.array-char-unprintable.d' matched 1 probe diff --git a/test/unittest/actions/trace/tst.array.d b/test/unittest/actions/trace/tst.array.d index 104d42e1..0779d90f 100644 --- a/test/unittest/actions/trace/tst.array.d +++ b/test/unittest/actions/trace/tst.array.d @@ -1,6 +1,6 @@ /* * Oracle Linux DTrace. - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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. */ @@ -11,8 +11,15 @@ * SECTION: Actions and Subroutines/trace() */ +short n[5]; + BEGIN { - trace(curthread->comm); + n[0] = 0x1234; + n[1] = 0x5678; + n[2] = 0x0000; + n[3] = 0x8765; + n[4] = 0x4321; + trace(n); exit(0); } diff --git a/test/unittest/actions/trace/tst.array.r b/test/unittest/actions/trace/tst.array.r index fbb674b6..52ff28ec 100644 --- a/test/unittest/actions/trace/tst.array.r +++ b/test/unittest/actions/trace/tst.array.r @@ -1,7 +1,7 @@ FUNCTION:NAME :BEGIN 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef - 0: 64 74 72 61 63 65 00 00 00 00 00 00 00 00 00 00 dtrace.......... + 0: 34 12 78 56 00 00 65 87 21 43 4.xV..e.!C -- @@stderr -- diff --git a/test/unittest/actions/trace/tst.array.r.p b/test/unittest/actions/trace/tst.array.r.p deleted file mode 100755 index b8cc8daf..00000000 --- a/test/unittest/actions/trace/tst.array.r.p +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/gawk -f - -# Some Linux kernel versions leave garbage at the end of the string. -{ sub(/( [0-9A-F]{2}){9} /, " 00 00 00 00 00 00 00 00 00 "); } -{ sub(/ dtrace\..{9}/, " dtrace.........."); } -{ print; } -- 2.45.2 From eugene.loh at oracle.com Fri Feb 7 06:04:53 2025 From: eugene.loh at oracle.com (Eugene Loh) Date: Fri, 7 Feb 2025 01:04:53 -0500 Subject: [DTrace-devel] [PATCH] trace: fix char-array handling In-Reply-To: References: Message-ID: <5629b0db-a8ed-5b75-4bcf-e1ffc7e6daca@oracle.com> I don't understand the relevant code very well, but Reviewed-by: Eugene Loh Incidentally, it looks funny to have so many "new" files have 2022 copyrights on them, but I suppose they're inheriting a lot from pre-existing tests... so, okay. Also... On 2/6/25 11:46, Kris Van Hees wrote: > The special handling of strings in the consumer's trace implementation > did not correctly account for char-arrays that do not necessarily are s/do not necessarily are/are not necessarily/ > intended to hold string data. Thr trace action is documented to print s/Thr/The/ > data as an ASCII string if it can be represented as such, i.e. if it > comprises only printable characters, optionally terminated by one or > more 0-bytes. That doesn't read quite right to me.? E.g., 'a' 'b' 0 'c' 0 looks to me like "only printable chars, terminated by one or more 0 bytes" yet I think it's supposed to be dumped (not printed as a string). I'm not sure and maybe it doesn't matter. Another possible test is all bytes are 0, but I'm not worried about whether such a test is included. > The implementation for trace on both producer and consumer side was > using the data alignment as a marker to determine whether data should > be printed as a string (alignment == 1) or not. The real alignment > of the data should be used, reverting code back to the necessary > heuristics in dt_print_bytes(). > > The dtrace_diftype struct has its dtdt_pad member renamed as dtdt_align. > > Added several new test cases to cover various possibilities. > > Signed-off-by: Kris Van Hees > --- > include/dtrace/dif.h | 2 +- > libdtrace/dt_cg.c | 16 ++++++---- > libdtrace/dt_consume.c | 20 ++----------- > libdtrace/dt_parser.c | 2 +- > .../actions/trace/tst.array-char-multi-nul.d | 30 +++++++++++++++++++ > .../actions/trace/tst.array-char-multi-nul.r | 8 +++++ > .../trace/tst.array-char-str-multi-nul.d | 30 +++++++++++++++++++ > .../trace/tst.array-char-str-multi-nul.r | 5 ++++ > .../actions/trace/tst.array-char-str-no-nul.d | 30 +++++++++++++++++++ > .../actions/trace/tst.array-char-str-no-nul.r | 5 ++++ > .../actions/trace/tst.array-char-str.d | 30 +++++++++++++++++++ > .../actions/trace/tst.array-char-str.r | 5 ++++ > .../trace/tst.array-char-unprintable.d | 30 +++++++++++++++++++ > .../trace/tst.array-char-unprintable.r | 8 +++++ > test/unittest/actions/trace/tst.array.d | 11 +++++-- > test/unittest/actions/trace/tst.array.r | 2 +- > test/unittest/actions/trace/tst.array.r.p | 6 ---- > 17 files changed, 206 insertions(+), 34 deletions(-) > create mode 100644 test/unittest/actions/trace/tst.array-char-multi-nul.d > create mode 100644 test/unittest/actions/trace/tst.array-char-multi-nul.r > create mode 100644 test/unittest/actions/trace/tst.array-char-str-multi-nul.d > create mode 100644 test/unittest/actions/trace/tst.array-char-str-multi-nul.r > create mode 100644 test/unittest/actions/trace/tst.array-char-str-no-nul.d > create mode 100644 test/unittest/actions/trace/tst.array-char-str-no-nul.r > create mode 100644 test/unittest/actions/trace/tst.array-char-str.d > create mode 100644 test/unittest/actions/trace/tst.array-char-str.r > create mode 100644 test/unittest/actions/trace/tst.array-char-unprintable.d > create mode 100644 test/unittest/actions/trace/tst.array-char-unprintable.r > delete mode 100755 test/unittest/actions/trace/tst.array.r.p > > diff --git a/include/dtrace/dif.h b/include/dtrace/dif.h > index 576bda2c..70257b2f 100644 > --- a/include/dtrace/dif.h > +++ b/include/dtrace/dif.h > @@ -33,7 +33,7 @@ typedef struct dtrace_diftype { > uint8_t dtdt_kind; > uint8_t dtdt_ckind; > uint8_t dtdt_flags; > - uint8_t dtdt_pad; > + uint8_t dtdt_align; > uint32_t dtdt_size; > } dtrace_diftype_t; > > diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c > index 6e74b4b0..a5c9aa09 100644 > --- a/libdtrace/dt_cg.c > +++ b/libdtrace/dt_cg.c > @@ -1601,12 +1601,11 @@ static int > dt_cg_store_val(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind, > dt_pfargv_t *pfp, int arg) > { > - dtrace_diftype_t vtype; > dtrace_hdl_t *dtp = pcb->pcb_hdl; > dt_irlist_t *dlp = &pcb->pcb_ir; > dt_regset_t *drp = pcb->pcb_regs; > uint_t off; > - size_t size; > + size_t size, align; > int not_null = 0; > int cflags = pcb->pcb_stmt->dtsd_clauseflags; > > @@ -1619,10 +1618,14 @@ dt_cg_store_val(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind, > longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); > emit(dlp, BPF_MOV_IMM(dnp->dn_reg, dnp->dn_ident->di_id)); > size = sizeof(dnp->dn_ident->di_id); > + align = size; > } else { > + dtrace_diftype_t vtype; > + > dt_cg_node(dnp, dlp, drp); > dt_node_diftype(dtp, dnp, &vtype); > size = vtype.dtdt_size; > + align = vtype.dtdt_align; > > /* > * A DEREF of a REF node does not get resolved in dt_cg_node() > @@ -1664,7 +1667,7 @@ dt_cg_store_val(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind, > > if (dt_node_is_scalar(dnp) || dt_node_is_float(dnp) || > dnp->dn_kind == DT_NODE_AGG) { > - off = dt_rec_add(dtp, dt_cg_fill_gap, kind, size, size, pfp, > + off = dt_rec_add(dtp, dt_cg_fill_gap, kind, size, align, pfp, > arg); > > emit(dlp, BPF_STOREX(size, BPF_REG_9, off, dnp->dn_reg)); > @@ -1678,8 +1681,8 @@ dt_cg_store_val(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind, > dt_cg_check_ptr_arg(dlp, drp, dnp, NULL); > > TRACE_REGSET("store_val(): Begin "); > - off = dt_rec_add(dtp, dt_cg_fill_gap, kind, size + 1, > - 1, pfp, arg); > + off = dt_rec_add(dtp, dt_cg_fill_gap, kind, size + 1, 1, pfp, > + arg); > > /* > * Copy the string data (no more than STRSIZE + 1 bytes) to the > @@ -1706,7 +1709,8 @@ dt_cg_store_val(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind, > > /* Handle tracing of by-ref values (arrays, struct, union). */ > if ((dnp->dn_flags & DT_NF_REF) || (arg & DT_NF_REF)) { > - off = dt_rec_add(dtp, dt_cg_fill_gap, kind, size, 2, pfp, arg); > + off = dt_rec_add(dtp, dt_cg_fill_gap, kind, size, align, pfp, > + arg); > > TRACE_REGSET("store_val(): Begin "); > if (!not_null) > diff --git a/libdtrace/dt_consume.c b/libdtrace/dt_consume.c > index 74004c69..58a2ead9 100644 > --- a/libdtrace/dt_consume.c > +++ b/libdtrace/dt_consume.c > @@ -1921,23 +1921,9 @@ dt_print_trace(dtrace_hdl_t *dtp, FILE *fp, dtrace_recdesc_t *rec, > if (dtp->dt_options[DTRACEOPT_RAWBYTES] != DTRACEOPT_UNSET) > return dt_print_rawbytes(dtp, fp, data, rec->dtrd_size); > > - /* > - * String data can be recognized as a non-scalar data item with > - * alignment == 1. > - * Any other non-scalar data items are printed as a byte stream. > - */ > - if (rec->dtrd_arg == DT_NF_REF) { > - char *s = (char *)data; > - > - if (rec->dtrd_alignment > 1) > - return dt_print_rawbytes(dtp, fp, data, rec->dtrd_size); > - > - /* We have a string. Print it. */ > - if (quiet) > - return dt_printf(dtp, fp, "%s", s); > - else > - return dt_printf(dtp, fp, " %-33s", s); > - } > + /* Handle non-scalar data. */ > + if (rec->dtrd_arg == DT_NF_REF) > + return dt_print_bytes(dtp, fp, data, rec->dtrd_size, 33, quiet); > > /* > * Differentiate between signed and unsigned numeric values. > diff --git a/libdtrace/dt_parser.c b/libdtrace/dt_parser.c > index 325ba881..eefe8341 100644 > --- a/libdtrace/dt_parser.c > +++ b/libdtrace/dt_parser.c > @@ -4914,7 +4914,7 @@ dt_node_diftype(dtrace_hdl_t *dtp, const dt_node_t *dnp, dtrace_diftype_t *tp) > } > > tp->dtdt_flags = (dnp->dn_flags & DT_NF_REF) ? DIF_TF_BYREF : 0; > - tp->dtdt_pad = 0; > + tp->dtdt_align = ctf_type_align(dnp->dn_ctfp, dnp->dn_type); > tp->dtdt_size = ctf_type_size(dnp->dn_ctfp, dnp->dn_type); > } > > diff --git a/test/unittest/actions/trace/tst.array-char-multi-nul.d b/test/unittest/actions/trace/tst.array-char-multi-nul.d > new file mode 100644 > index 00000000..187fb711 > --- /dev/null > +++ b/test/unittest/actions/trace/tst.array-char-multi-nul.d > @@ -0,0 +1,30 @@ > +/* > + * Oracle Linux DTrace. > + * Copyright (c) 2022, 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. > + */ > + > +/* > + * ASSERTION: The trace() action prints a char-array of printable characters > + * with multiple 0-bytes in its content. > + * > + * SECTION: Actions and Subroutines/trace() > + */ > + > +char n[9]; > + > +BEGIN > +{ > + n[0] = 'a'; > + n[1] = 'A'; > + n[2] = 0; > + n[3] = 'B'; > + n[4] = 0; > + n[5] = 'C'; > + n[6] = 0; > + n[7] = 'D'; > + n[8] = 'e'; > + trace(n); > + exit(0); > +} > diff --git a/test/unittest/actions/trace/tst.array-char-multi-nul.r b/test/unittest/actions/trace/tst.array-char-multi-nul.r > new file mode 100644 > index 00000000..c1361e75 > --- /dev/null > +++ b/test/unittest/actions/trace/tst.array-char-multi-nul.r > @@ -0,0 +1,8 @@ > + FUNCTION:NAME > + :BEGIN > + 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef > + 0: 61 41 00 42 00 43 00 44 65 aA.B.C.De > + > + > +-- @@stderr -- > +dtrace: script 'test/unittest/actions/trace/tst.array-char-multi-nul.d' matched 1 probe > diff --git a/test/unittest/actions/trace/tst.array-char-str-multi-nul.d b/test/unittest/actions/trace/tst.array-char-str-multi-nul.d > new file mode 100644 > index 00000000..75164cd3 > --- /dev/null > +++ b/test/unittest/actions/trace/tst.array-char-str-multi-nul.d > @@ -0,0 +1,30 @@ > +/* > + * Oracle Linux DTrace. > + * Copyright (c) 2022, 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. > + */ > + > +/* > + * ASSERTION: The trace() action prints a char-array of printable characters > + * followed y by multiple 0-bytes correctly. > + * > + * SECTION: Actions and Subroutines/trace() > + */ > + > +char n[9]; > + > +BEGIN > +{ > + n[0] = 'a'; > + n[1] = 'A'; > + n[2] = 'b'; > + n[3] = 'B'; > + n[4] = 'c'; > + n[5] = 0; > + n[6] = 0; > + n[7] = 0; > + n[8] = 0; > + trace(n); > + exit(0); > +} > diff --git a/test/unittest/actions/trace/tst.array-char-str-multi-nul.r b/test/unittest/actions/trace/tst.array-char-str-multi-nul.r > new file mode 100644 > index 00000000..1951ae69 > --- /dev/null > +++ b/test/unittest/actions/trace/tst.array-char-str-multi-nul.r > @@ -0,0 +1,5 @@ > + FUNCTION:NAME > + :BEGIN aAbBc > + > +-- @@stderr -- > +dtrace: script 'test/unittest/actions/trace/tst.array-char-str-multi-nul.d' matched 1 probe > diff --git a/test/unittest/actions/trace/tst.array-char-str-no-nul.d b/test/unittest/actions/trace/tst.array-char-str-no-nul.d > new file mode 100644 > index 00000000..532c3519 > --- /dev/null > +++ b/test/unittest/actions/trace/tst.array-char-str-no-nul.d > @@ -0,0 +1,30 @@ > +/* > + * Oracle Linux DTrace. > + * Copyright (c) 2022, 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. > + */ > + > +/* > + * ASSERTION: The trace() action prints a char-aarray of printable characters > + * (not terminated) correctly. > + * > + * SECTION: Actions and Subroutines/trace() > + */ > + > +char n[9]; > + > +BEGIN > +{ > + n[0] = 'a'; > + n[1] = 'A'; > + n[2] = 'b'; > + n[3] = 'B'; > + n[4] = 'c'; > + n[5] = 'C'; > + n[6] = 'd'; > + n[7] = 'D'; > + n[8] = 'e'; > + trace(n); > + exit(0); > +} > diff --git a/test/unittest/actions/trace/tst.array-char-str-no-nul.r b/test/unittest/actions/trace/tst.array-char-str-no-nul.r > new file mode 100644 > index 00000000..3d56e520 > --- /dev/null > +++ b/test/unittest/actions/trace/tst.array-char-str-no-nul.r > @@ -0,0 +1,5 @@ > + FUNCTION:NAME > + :BEGIN aAbBcCdDe > + > +-- @@stderr -- > +dtrace: script 'test/unittest/actions/trace/tst.array-char-str-no-nul.d' matched 1 probe > diff --git a/test/unittest/actions/trace/tst.array-char-str.d b/test/unittest/actions/trace/tst.array-char-str.d > new file mode 100644 > index 00000000..70f6371f > --- /dev/null > +++ b/test/unittest/actions/trace/tst.array-char-str.d > @@ -0,0 +1,30 @@ > +/* > + * Oracle Linux DTrace. > + * Copyright (c) 2022, 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. > + */ > + > +/* > + * ASSERTION: The trace() action prints a char-array of printable characters > + * (terminated) correctly. > + * > + * SECTION: Actions and Subroutines/trace() > + */ > + > +char n[9]; > + > +BEGIN > +{ > + n[0] = 'a'; > + n[1] = 'A'; > + n[2] = 'b'; > + n[3] = 'B'; > + n[4] = 'c'; > + n[5] = 'C'; > + n[6] = 'd'; > + n[7] = 'D'; > + n[8] = '\0'; > + trace(n); > + exit(0); > +} > diff --git a/test/unittest/actions/trace/tst.array-char-str.r b/test/unittest/actions/trace/tst.array-char-str.r > new file mode 100644 > index 00000000..9778ddd2 > --- /dev/null > +++ b/test/unittest/actions/trace/tst.array-char-str.r > @@ -0,0 +1,5 @@ > + FUNCTION:NAME > + :BEGIN aAbBcCdD > + > +-- @@stderr -- > +dtrace: script 'test/unittest/actions/trace/tst.array-char-str.d' matched 1 probe > diff --git a/test/unittest/actions/trace/tst.array-char-unprintable.d b/test/unittest/actions/trace/tst.array-char-unprintable.d > new file mode 100644 > index 00000000..d88df369 > --- /dev/null > +++ b/test/unittest/actions/trace/tst.array-char-unprintable.d > @@ -0,0 +1,30 @@ > +/* > + * Oracle Linux DTrace. > + * Copyright (c) 2022, 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. > + */ > + > +/* > + * ASSERTION: The trace() action prints a char-array with a non-printable > + * character correctly. > + * > + * SECTION: Actions and Subroutines/trace() > + */ > + > +char n[9]; > + > +BEGIN > +{ > + n[0] = 'a'; > + n[1] = 'A'; > + n[2] = 'b'; > + n[3] = 'B'; > + n[4] = 'c'; > + n[5] = 5; > + n[6] = 'd'; > + n[7] = 'D'; > + n[8] = '\0'; > + trace(n); > + exit(0); > +} > diff --git a/test/unittest/actions/trace/tst.array-char-unprintable.r b/test/unittest/actions/trace/tst.array-char-unprintable.r > new file mode 100644 > index 00000000..1f84fcfa > --- /dev/null > +++ b/test/unittest/actions/trace/tst.array-char-unprintable.r > @@ -0,0 +1,8 @@ > + FUNCTION:NAME > + :BEGIN > + 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef > + 0: 61 41 62 42 63 05 64 44 00 aAbBc.dD. > + > + > +-- @@stderr -- > +dtrace: script 'test/unittest/actions/trace/tst.array-char-unprintable.d' matched 1 probe > diff --git a/test/unittest/actions/trace/tst.array.d b/test/unittest/actions/trace/tst.array.d > index 104d42e1..0779d90f 100644 > --- a/test/unittest/actions/trace/tst.array.d > +++ b/test/unittest/actions/trace/tst.array.d > @@ -1,6 +1,6 @@ > /* > * Oracle Linux DTrace. > - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. > + * Copyright (c) 2022, 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. > */ > @@ -11,8 +11,15 @@ > * SECTION: Actions and Subroutines/trace() > */ > > +short n[5]; > + > BEGIN > { > - trace(curthread->comm); > + n[0] = 0x1234; > + n[1] = 0x5678; > + n[2] = 0x0000; > + n[3] = 0x8765; > + n[4] = 0x4321; > + trace(n); > exit(0); > } > diff --git a/test/unittest/actions/trace/tst.array.r b/test/unittest/actions/trace/tst.array.r > index fbb674b6..52ff28ec 100644 > --- a/test/unittest/actions/trace/tst.array.r > +++ b/test/unittest/actions/trace/tst.array.r > @@ -1,7 +1,7 @@ > FUNCTION:NAME > :BEGIN > 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef > - 0: 64 74 72 61 63 65 00 00 00 00 00 00 00 00 00 00 dtrace.......... > + 0: 34 12 78 56 00 00 65 87 21 43 4.xV..e.!C > > > -- @@stderr -- > diff --git a/test/unittest/actions/trace/tst.array.r.p b/test/unittest/actions/trace/tst.array.r.p > deleted file mode 100755 > index b8cc8daf..00000000 > --- a/test/unittest/actions/trace/tst.array.r.p > +++ /dev/null > @@ -1,6 +0,0 @@ > -#!/usr/bin/gawk -f > - > -# Some Linux kernel versions leave garbage at the end of the string. > -{ sub(/( [0-9A-F]{2}){9} /, " 00 00 00 00 00 00 00 00 00 "); } > -{ sub(/ dtrace\..{9}/, " dtrace.........."); } > -{ print; } From kris.van.hees at oracle.com Fri Feb 7 06:08:42 2025 From: kris.van.hees at oracle.com (Kris Van Hees) Date: Fri, 7 Feb 2025 01:08:42 -0500 Subject: [DTrace-devel] [PATCH] trace: fix char-array handling In-Reply-To: <5629b0db-a8ed-5b75-4bcf-e1ffc7e6daca@oracle.com> References: <5629b0db-a8ed-5b75-4bcf-e1ffc7e6daca@oracle.com> Message-ID: On Fri, Feb 07, 2025 at 01:04:53AM -0500, Eugene Loh wrote: > I don't understand the relevant code very well, but > Reviewed-by: Eugene Loh > > Incidentally, it looks funny to have so many "new" files have 2022 > copyrights on them, but I suppose they're inheriting a lot from pre-existing > tests... so, okay. I'll have a look. Copyright years may need to be updated. > Also... > > On 2/6/25 11:46, Kris Van Hees wrote: > > The special handling of strings in the consumer's trace implementation > > did not correctly account for char-arrays that do not necessarily are > > s/do not necessarily are/are not necessarily/ > > > intended to hold string data. Thr trace action is documented to print > > s/Thr/The/ > > > data as an ASCII string if it can be represented as such, i.e. if it > > comprises only printable characters, optionally terminated by one or > > more 0-bytes. > > That doesn't read quite right to me.? E.g., 'a' 'b' 0 'c' 0 looks to me like > "only printable chars, terminated by one or more 0 bytes" yet I think it's > supposed to be dumped (not printed as a string). I'm not sure and maybe it > doesn't matter. 'a' 'b' 0 'c' 0 will indeed by bumped as raw bytes because it is not a string followed by one or more 0-bytes, but rather a potential string that has a non-printable character (byte) embedded in it, which causes it to not be seen as a string at all. > Another possible test is all bytes are 0, but I'm not worried about whether > such a test is included. > > > The implementation for trace on both producer and consumer side was > > using the data alignment as a marker to determine whether data should > > be printed as a string (alignment == 1) or not. The real alignment > > of the data should be used, reverting code back to the necessary > > heuristics in dt_print_bytes(). > > > > The dtrace_diftype struct has its dtdt_pad member renamed as dtdt_align. > > > > Added several new test cases to cover various possibilities. > > > > Signed-off-by: Kris Van Hees > > --- > > include/dtrace/dif.h | 2 +- > > libdtrace/dt_cg.c | 16 ++++++---- > > libdtrace/dt_consume.c | 20 ++----------- > > libdtrace/dt_parser.c | 2 +- > > .../actions/trace/tst.array-char-multi-nul.d | 30 +++++++++++++++++++ > > .../actions/trace/tst.array-char-multi-nul.r | 8 +++++ > > .../trace/tst.array-char-str-multi-nul.d | 30 +++++++++++++++++++ > > .../trace/tst.array-char-str-multi-nul.r | 5 ++++ > > .../actions/trace/tst.array-char-str-no-nul.d | 30 +++++++++++++++++++ > > .../actions/trace/tst.array-char-str-no-nul.r | 5 ++++ > > .../actions/trace/tst.array-char-str.d | 30 +++++++++++++++++++ > > .../actions/trace/tst.array-char-str.r | 5 ++++ > > .../trace/tst.array-char-unprintable.d | 30 +++++++++++++++++++ > > .../trace/tst.array-char-unprintable.r | 8 +++++ > > test/unittest/actions/trace/tst.array.d | 11 +++++-- > > test/unittest/actions/trace/tst.array.r | 2 +- > > test/unittest/actions/trace/tst.array.r.p | 6 ---- > > 17 files changed, 206 insertions(+), 34 deletions(-) > > create mode 100644 test/unittest/actions/trace/tst.array-char-multi-nul.d > > create mode 100644 test/unittest/actions/trace/tst.array-char-multi-nul.r > > create mode 100644 test/unittest/actions/trace/tst.array-char-str-multi-nul.d > > create mode 100644 test/unittest/actions/trace/tst.array-char-str-multi-nul.r > > create mode 100644 test/unittest/actions/trace/tst.array-char-str-no-nul.d > > create mode 100644 test/unittest/actions/trace/tst.array-char-str-no-nul.r > > create mode 100644 test/unittest/actions/trace/tst.array-char-str.d > > create mode 100644 test/unittest/actions/trace/tst.array-char-str.r > > create mode 100644 test/unittest/actions/trace/tst.array-char-unprintable.d > > create mode 100644 test/unittest/actions/trace/tst.array-char-unprintable.r > > delete mode 100755 test/unittest/actions/trace/tst.array.r.p > > > > diff --git a/include/dtrace/dif.h b/include/dtrace/dif.h > > index 576bda2c..70257b2f 100644 > > --- a/include/dtrace/dif.h > > +++ b/include/dtrace/dif.h > > @@ -33,7 +33,7 @@ typedef struct dtrace_diftype { > > uint8_t dtdt_kind; > > uint8_t dtdt_ckind; > > uint8_t dtdt_flags; > > - uint8_t dtdt_pad; > > + uint8_t dtdt_align; > > uint32_t dtdt_size; > > } dtrace_diftype_t; > > diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c > > index 6e74b4b0..a5c9aa09 100644 > > --- a/libdtrace/dt_cg.c > > +++ b/libdtrace/dt_cg.c > > @@ -1601,12 +1601,11 @@ static int > > dt_cg_store_val(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind, > > dt_pfargv_t *pfp, int arg) > > { > > - dtrace_diftype_t vtype; > > dtrace_hdl_t *dtp = pcb->pcb_hdl; > > dt_irlist_t *dlp = &pcb->pcb_ir; > > dt_regset_t *drp = pcb->pcb_regs; > > uint_t off; > > - size_t size; > > + size_t size, align; > > int not_null = 0; > > int cflags = pcb->pcb_stmt->dtsd_clauseflags; > > @@ -1619,10 +1618,14 @@ dt_cg_store_val(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind, > > longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); > > emit(dlp, BPF_MOV_IMM(dnp->dn_reg, dnp->dn_ident->di_id)); > > size = sizeof(dnp->dn_ident->di_id); > > + align = size; > > } else { > > + dtrace_diftype_t vtype; > > + > > dt_cg_node(dnp, dlp, drp); > > dt_node_diftype(dtp, dnp, &vtype); > > size = vtype.dtdt_size; > > + align = vtype.dtdt_align; > > /* > > * A DEREF of a REF node does not get resolved in dt_cg_node() > > @@ -1664,7 +1667,7 @@ dt_cg_store_val(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind, > > if (dt_node_is_scalar(dnp) || dt_node_is_float(dnp) || > > dnp->dn_kind == DT_NODE_AGG) { > > - off = dt_rec_add(dtp, dt_cg_fill_gap, kind, size, size, pfp, > > + off = dt_rec_add(dtp, dt_cg_fill_gap, kind, size, align, pfp, > > arg); > > emit(dlp, BPF_STOREX(size, BPF_REG_9, off, dnp->dn_reg)); > > @@ -1678,8 +1681,8 @@ dt_cg_store_val(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind, > > dt_cg_check_ptr_arg(dlp, drp, dnp, NULL); > > TRACE_REGSET("store_val(): Begin "); > > - off = dt_rec_add(dtp, dt_cg_fill_gap, kind, size + 1, > > - 1, pfp, arg); > > + off = dt_rec_add(dtp, dt_cg_fill_gap, kind, size + 1, 1, pfp, > > + arg); > > /* > > * Copy the string data (no more than STRSIZE + 1 bytes) to the > > @@ -1706,7 +1709,8 @@ dt_cg_store_val(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind, > > /* Handle tracing of by-ref values (arrays, struct, union). */ > > if ((dnp->dn_flags & DT_NF_REF) || (arg & DT_NF_REF)) { > > - off = dt_rec_add(dtp, dt_cg_fill_gap, kind, size, 2, pfp, arg); > > + off = dt_rec_add(dtp, dt_cg_fill_gap, kind, size, align, pfp, > > + arg); > > TRACE_REGSET("store_val(): Begin "); > > if (!not_null) > > diff --git a/libdtrace/dt_consume.c b/libdtrace/dt_consume.c > > index 74004c69..58a2ead9 100644 > > --- a/libdtrace/dt_consume.c > > +++ b/libdtrace/dt_consume.c > > @@ -1921,23 +1921,9 @@ dt_print_trace(dtrace_hdl_t *dtp, FILE *fp, dtrace_recdesc_t *rec, > > if (dtp->dt_options[DTRACEOPT_RAWBYTES] != DTRACEOPT_UNSET) > > return dt_print_rawbytes(dtp, fp, data, rec->dtrd_size); > > - /* > > - * String data can be recognized as a non-scalar data item with > > - * alignment == 1. > > - * Any other non-scalar data items are printed as a byte stream. > > - */ > > - if (rec->dtrd_arg == DT_NF_REF) { > > - char *s = (char *)data; > > - > > - if (rec->dtrd_alignment > 1) > > - return dt_print_rawbytes(dtp, fp, data, rec->dtrd_size); > > - > > - /* We have a string. Print it. */ > > - if (quiet) > > - return dt_printf(dtp, fp, "%s", s); > > - else > > - return dt_printf(dtp, fp, " %-33s", s); > > - } > > + /* Handle non-scalar data. */ > > + if (rec->dtrd_arg == DT_NF_REF) > > + return dt_print_bytes(dtp, fp, data, rec->dtrd_size, 33, quiet); > > /* > > * Differentiate between signed and unsigned numeric values. > > diff --git a/libdtrace/dt_parser.c b/libdtrace/dt_parser.c > > index 325ba881..eefe8341 100644 > > --- a/libdtrace/dt_parser.c > > +++ b/libdtrace/dt_parser.c > > @@ -4914,7 +4914,7 @@ dt_node_diftype(dtrace_hdl_t *dtp, const dt_node_t *dnp, dtrace_diftype_t *tp) > > } > > tp->dtdt_flags = (dnp->dn_flags & DT_NF_REF) ? DIF_TF_BYREF : 0; > > - tp->dtdt_pad = 0; > > + tp->dtdt_align = ctf_type_align(dnp->dn_ctfp, dnp->dn_type); > > tp->dtdt_size = ctf_type_size(dnp->dn_ctfp, dnp->dn_type); > > } > > diff --git a/test/unittest/actions/trace/tst.array-char-multi-nul.d b/test/unittest/actions/trace/tst.array-char-multi-nul.d > > new file mode 100644 > > index 00000000..187fb711 > > --- /dev/null > > +++ b/test/unittest/actions/trace/tst.array-char-multi-nul.d > > @@ -0,0 +1,30 @@ > > +/* > > + * Oracle Linux DTrace. > > + * Copyright (c) 2022, 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. > > + */ > > + > > +/* > > + * ASSERTION: The trace() action prints a char-array of printable characters > > + * with multiple 0-bytes in its content. > > + * > > + * SECTION: Actions and Subroutines/trace() > > + */ > > + > > +char n[9]; > > + > > +BEGIN > > +{ > > + n[0] = 'a'; > > + n[1] = 'A'; > > + n[2] = 0; > > + n[3] = 'B'; > > + n[4] = 0; > > + n[5] = 'C'; > > + n[6] = 0; > > + n[7] = 'D'; > > + n[8] = 'e'; > > + trace(n); > > + exit(0); > > +} > > diff --git a/test/unittest/actions/trace/tst.array-char-multi-nul.r b/test/unittest/actions/trace/tst.array-char-multi-nul.r > > new file mode 100644 > > index 00000000..c1361e75 > > --- /dev/null > > +++ b/test/unittest/actions/trace/tst.array-char-multi-nul.r > > @@ -0,0 +1,8 @@ > > + FUNCTION:NAME > > + :BEGIN > > + 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef > > + 0: 61 41 00 42 00 43 00 44 65 aA.B.C.De > > + > > + > > +-- @@stderr -- > > +dtrace: script 'test/unittest/actions/trace/tst.array-char-multi-nul.d' matched 1 probe > > diff --git a/test/unittest/actions/trace/tst.array-char-str-multi-nul.d b/test/unittest/actions/trace/tst.array-char-str-multi-nul.d > > new file mode 100644 > > index 00000000..75164cd3 > > --- /dev/null > > +++ b/test/unittest/actions/trace/tst.array-char-str-multi-nul.d > > @@ -0,0 +1,30 @@ > > +/* > > + * Oracle Linux DTrace. > > + * Copyright (c) 2022, 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. > > + */ > > + > > +/* > > + * ASSERTION: The trace() action prints a char-array of printable characters > > + * followed y by multiple 0-bytes correctly. > > + * > > + * SECTION: Actions and Subroutines/trace() > > + */ > > + > > +char n[9]; > > + > > +BEGIN > > +{ > > + n[0] = 'a'; > > + n[1] = 'A'; > > + n[2] = 'b'; > > + n[3] = 'B'; > > + n[4] = 'c'; > > + n[5] = 0; > > + n[6] = 0; > > + n[7] = 0; > > + n[8] = 0; > > + trace(n); > > + exit(0); > > +} > > diff --git a/test/unittest/actions/trace/tst.array-char-str-multi-nul.r b/test/unittest/actions/trace/tst.array-char-str-multi-nul.r > > new file mode 100644 > > index 00000000..1951ae69 > > --- /dev/null > > +++ b/test/unittest/actions/trace/tst.array-char-str-multi-nul.r > > @@ -0,0 +1,5 @@ > > + FUNCTION:NAME > > + :BEGIN aAbBc > > + > > +-- @@stderr -- > > +dtrace: script 'test/unittest/actions/trace/tst.array-char-str-multi-nul.d' matched 1 probe > > diff --git a/test/unittest/actions/trace/tst.array-char-str-no-nul.d b/test/unittest/actions/trace/tst.array-char-str-no-nul.d > > new file mode 100644 > > index 00000000..532c3519 > > --- /dev/null > > +++ b/test/unittest/actions/trace/tst.array-char-str-no-nul.d > > @@ -0,0 +1,30 @@ > > +/* > > + * Oracle Linux DTrace. > > + * Copyright (c) 2022, 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. > > + */ > > + > > +/* > > + * ASSERTION: The trace() action prints a char-aarray of printable characters > > + * (not terminated) correctly. > > + * > > + * SECTION: Actions and Subroutines/trace() > > + */ > > + > > +char n[9]; > > + > > +BEGIN > > +{ > > + n[0] = 'a'; > > + n[1] = 'A'; > > + n[2] = 'b'; > > + n[3] = 'B'; > > + n[4] = 'c'; > > + n[5] = 'C'; > > + n[6] = 'd'; > > + n[7] = 'D'; > > + n[8] = 'e'; > > + trace(n); > > + exit(0); > > +} > > diff --git a/test/unittest/actions/trace/tst.array-char-str-no-nul.r b/test/unittest/actions/trace/tst.array-char-str-no-nul.r > > new file mode 100644 > > index 00000000..3d56e520 > > --- /dev/null > > +++ b/test/unittest/actions/trace/tst.array-char-str-no-nul.r > > @@ -0,0 +1,5 @@ > > + FUNCTION:NAME > > + :BEGIN aAbBcCdDe > > + > > +-- @@stderr -- > > +dtrace: script 'test/unittest/actions/trace/tst.array-char-str-no-nul.d' matched 1 probe > > diff --git a/test/unittest/actions/trace/tst.array-char-str.d b/test/unittest/actions/trace/tst.array-char-str.d > > new file mode 100644 > > index 00000000..70f6371f > > --- /dev/null > > +++ b/test/unittest/actions/trace/tst.array-char-str.d > > @@ -0,0 +1,30 @@ > > +/* > > + * Oracle Linux DTrace. > > + * Copyright (c) 2022, 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. > > + */ > > + > > +/* > > + * ASSERTION: The trace() action prints a char-array of printable characters > > + * (terminated) correctly. > > + * > > + * SECTION: Actions and Subroutines/trace() > > + */ > > + > > +char n[9]; > > + > > +BEGIN > > +{ > > + n[0] = 'a'; > > + n[1] = 'A'; > > + n[2] = 'b'; > > + n[3] = 'B'; > > + n[4] = 'c'; > > + n[5] = 'C'; > > + n[6] = 'd'; > > + n[7] = 'D'; > > + n[8] = '\0'; > > + trace(n); > > + exit(0); > > +} > > diff --git a/test/unittest/actions/trace/tst.array-char-str.r b/test/unittest/actions/trace/tst.array-char-str.r > > new file mode 100644 > > index 00000000..9778ddd2 > > --- /dev/null > > +++ b/test/unittest/actions/trace/tst.array-char-str.r > > @@ -0,0 +1,5 @@ > > + FUNCTION:NAME > > + :BEGIN aAbBcCdD > > + > > +-- @@stderr -- > > +dtrace: script 'test/unittest/actions/trace/tst.array-char-str.d' matched 1 probe > > diff --git a/test/unittest/actions/trace/tst.array-char-unprintable.d b/test/unittest/actions/trace/tst.array-char-unprintable.d > > new file mode 100644 > > index 00000000..d88df369 > > --- /dev/null > > +++ b/test/unittest/actions/trace/tst.array-char-unprintable.d > > @@ -0,0 +1,30 @@ > > +/* > > + * Oracle Linux DTrace. > > + * Copyright (c) 2022, 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. > > + */ > > + > > +/* > > + * ASSERTION: The trace() action prints a char-array with a non-printable > > + * character correctly. > > + * > > + * SECTION: Actions and Subroutines/trace() > > + */ > > + > > +char n[9]; > > + > > +BEGIN > > +{ > > + n[0] = 'a'; > > + n[1] = 'A'; > > + n[2] = 'b'; > > + n[3] = 'B'; > > + n[4] = 'c'; > > + n[5] = 5; > > + n[6] = 'd'; > > + n[7] = 'D'; > > + n[8] = '\0'; > > + trace(n); > > + exit(0); > > +} > > diff --git a/test/unittest/actions/trace/tst.array-char-unprintable.r b/test/unittest/actions/trace/tst.array-char-unprintable.r > > new file mode 100644 > > index 00000000..1f84fcfa > > --- /dev/null > > +++ b/test/unittest/actions/trace/tst.array-char-unprintable.r > > @@ -0,0 +1,8 @@ > > + FUNCTION:NAME > > + :BEGIN > > + 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef > > + 0: 61 41 62 42 63 05 64 44 00 aAbBc.dD. > > + > > + > > +-- @@stderr -- > > +dtrace: script 'test/unittest/actions/trace/tst.array-char-unprintable.d' matched 1 probe > > diff --git a/test/unittest/actions/trace/tst.array.d b/test/unittest/actions/trace/tst.array.d > > index 104d42e1..0779d90f 100644 > > --- a/test/unittest/actions/trace/tst.array.d > > +++ b/test/unittest/actions/trace/tst.array.d > > @@ -1,6 +1,6 @@ > > /* > > * Oracle Linux DTrace. > > - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. > > + * Copyright (c) 2022, 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. > > */ > > @@ -11,8 +11,15 @@ > > * SECTION: Actions and Subroutines/trace() > > */ > > +short n[5]; > > + > > BEGIN > > { > > - trace(curthread->comm); > > + n[0] = 0x1234; > > + n[1] = 0x5678; > > + n[2] = 0x0000; > > + n[3] = 0x8765; > > + n[4] = 0x4321; > > + trace(n); > > exit(0); > > } > > diff --git a/test/unittest/actions/trace/tst.array.r b/test/unittest/actions/trace/tst.array.r > > index fbb674b6..52ff28ec 100644 > > --- a/test/unittest/actions/trace/tst.array.r > > +++ b/test/unittest/actions/trace/tst.array.r > > @@ -1,7 +1,7 @@ > > FUNCTION:NAME > > :BEGIN > > 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef > > - 0: 64 74 72 61 63 65 00 00 00 00 00 00 00 00 00 00 dtrace.......... > > + 0: 34 12 78 56 00 00 65 87 21 43 4.xV..e.!C > > -- @@stderr -- > > diff --git a/test/unittest/actions/trace/tst.array.r.p b/test/unittest/actions/trace/tst.array.r.p > > deleted file mode 100755 > > index b8cc8daf..00000000 > > --- a/test/unittest/actions/trace/tst.array.r.p > > +++ /dev/null > > @@ -1,6 +0,0 @@ > > -#!/usr/bin/gawk -f > > - > > -# Some Linux kernel versions leave garbage at the end of the string. > > -{ sub(/( [0-9A-F]{2}){9} /, " 00 00 00 00 00 00 00 00 00 "); } > > -{ sub(/ dtrace\..{9}/, " dtrace.........."); } > > -{ print; } From kris.van.hees at oracle.com Fri Feb 7 06:11:05 2025 From: kris.van.hees at oracle.com (Kris Van Hees) Date: Fri, 7 Feb 2025 01:11:05 -0500 Subject: [DTrace-devel] [PATCH] cg: ensure string results in a ternary are padded to strsize Message-ID: <6c5f5a2593039303a8f1f46fccb7a198.kris.van.hees@oracle.com> The result of a string ternary was copied from either left or right value using a memcpy() of strsize, but that could result in garbage being included in the result beyond the terminating 0-byte of the string value. If the result was e.g. assigned to a char-array translator member, that garbage could affect consumer output. The ternary will now copy the string using dt_cg_strcpy() which ensures that any string < strsize will be padded with 0-bytes up to strsize. Signed-off-by: Kris Van Hees --- libdtrace/dt_cg.c | 68 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 16 deletions(-) diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c index a5c9aa09..b48e27f0 100644 --- a/libdtrace/dt_cg.c +++ b/libdtrace/dt_cg.c @@ -1371,6 +1371,19 @@ dt_cg_fill_gap(dt_pcb_t *pcb, int gap) emit(dlp, BPF_STORE_IMM(BPF_W, BPF_REG_9, off, 0)); } +/* + * Store a pointer to the 'memory block of zeros' in reg. + */ +static void +dt_cg_zerosptr(int reg, dt_irlist_t *dlp, dt_regset_t *drp) +{ + dtrace_hdl_t *dtp = yypcb->pcb_hdl; + dt_ident_t *zero_off = dt_dlib_get_var(dtp, "ZERO_OFF"); + + dt_cg_access_dctx(reg, dlp, drp, DCTX_STRTAB); + emite(dlp, BPF_ALU64_IMM(BPF_ADD, reg, -1), zero_off); +} + static void dt_cg_memcpy(dt_irlist_t *dlp, dt_regset_t *drp, int dst, int src, size_t size) { @@ -1394,6 +1407,43 @@ dt_cg_memcpy(dt_irlist_t *dlp, dt_regset_t *drp, int dst, int src, size_t size) dt_regset_free(drp, BPF_REG_0); } +/* + * Copy a string from the pointer stored in the src register to the pointer + * stored in the dst register. At most strsize characters will be copied, and + * if the source string is less than strsize characters, the remainder will be + * filled with 0s. + */ +static void +dt_cg_strcpy(dt_irlist_t *dlp, dt_regset_t *drp, int dst, int src) +{ + dtrace_hdl_t *dtp = yypcb->pcb_hdl; + uint_t lbl_ok = dt_irlist_label(dlp); + size_t size = dtp->dt_options[DTRACEOPT_STRSIZE]; + + if (dt_regset_xalloc_args(drp) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + emit(dlp, BPF_MOV_REG(BPF_REG_1, dst)); + emit(dlp, BPF_MOV_IMM(BPF_REG_2, size)); + emit(dlp, BPF_MOV_REG(BPF_REG_3, src)); + dt_regset_xalloc(drp, BPF_REG_0); + emit(dlp, BPF_CALL_HELPER(dtp->dt_bpfhelper[BPF_FUNC_probe_read_kernel_str])); + + emit(dlp, BPF_BRANCH_IMM(BPF_JSGE, BPF_REG_0, 0, lbl_ok)); + dt_cg_probe_error(yypcb, DTRACEFLT_BADADDR, DT_ISREG, src); + + emitl(dlp, lbl_ok, + BPF_NOP()); + emit(dlp, BPF_MOV_REG(BPF_REG_1, dst)); + emit(dlp, BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_0)); + emit(dlp, BPF_MOV_IMM(BPF_REG_2, size)); + emit(dlp, BPF_ALU64_REG(BPF_SUB, BPF_REG_2, BPF_REG_0)); + dt_cg_zerosptr(BPF_REG_3, dlp, drp); + emit(dlp, BPF_CALL_HELPER(dtp->dt_bpfhelper[BPF_FUNC_probe_read_kernel])); + dt_regset_free(drp, BPF_REG_0); + dt_regset_free_args(drp); +} + static void dt_cg_spill_store(int reg) { @@ -3040,19 +3090,6 @@ dt_cg_pop_stack(int reg, dt_irlist_t *dlp, dt_regset_t *drp) dt_regset_free(drp, treg); } -/* - * Store a pointer to the 'memory block of zeros' in reg. - */ -static void -dt_cg_zerosptr(int reg, dt_irlist_t *dlp, dt_regset_t *drp) -{ - dtrace_hdl_t *dtp = yypcb->pcb_hdl; - dt_ident_t *zero_off = dt_dlib_get_var(dtp, "ZERO_OFF"); - - dt_cg_access_dctx(reg, dlp, drp, DCTX_STRTAB); - emite(dlp, BPF_ALU64_IMM(BPF_ADD, reg, -1), zero_off); -} - /* * Generate code to promote signed scalars (size < 64 bits) to native register * size (64 bits). @@ -4603,7 +4640,7 @@ dt_cg_ternary_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) * dn_right). */ if (dt_node_is_string(dnp)) { - uint_t lbl_null = dt_irlist_label(dlp); + uint_t lbl_null = dt_irlist_label(dlp); emit(dlp, BPF_BRANCH_IMM(BPF_JEQ, dnp->dn_reg, 0, lbl_null)); @@ -4619,8 +4656,7 @@ dt_cg_ternary_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) dt_cg_access_dctx(dnp->dn_reg, dlp, drp, DCTX_MEM); emit(dlp, BPF_ALU64_IMM(BPF_ADD, dnp->dn_reg, dnp->dn_tstring->dn_value)); - dt_cg_memcpy(dlp, drp, dnp->dn_reg, BPF_REG_0, - yypcb->pcb_hdl->dt_options[DTRACEOPT_STRSIZE]); + dt_cg_strcpy(dlp, drp, dnp->dn_reg, BPF_REG_0); emitl(dlp, lbl_null, BPF_NOP()); -- 2.45.2 From eugene.loh at oracle.com Fri Feb 7 19:17:44 2025 From: eugene.loh at oracle.com (Eugene Loh) Date: Fri, 7 Feb 2025 14:17:44 -0500 Subject: [DTrace-devel] [PATCH] cg: ensure string results in a ternary are padded to strsize In-Reply-To: <6c5f5a2593039303a8f1f46fccb7a198.kris.van.hees@oracle.com> References: <6c5f5a2593039303a8f1f46fccb7a198.kris.van.hees@oracle.com> Message-ID: <495b1606-caba-9b59-68a0-0587d0ddf094@oracle.com> Reviewed-by: Eugene Loh That said: *)? Is it possible to construct a test? *)? Has there been an audit to ferret out other instances of this problem?? Also, I'm confused what position we're taking here. We're saying that it's possible for a string (which in D has strsize) to have garbage after the NUL byte, but we'll deal with this situation only in the case that it's an argument of a ternary operation?? Why do we not say instead that a strsize string will not have garbage after the NUL byte, so that a strsize copy of a string will be safe (regardless of whether it's a ternary op)? *)? What position are we taking here on BPF register pressure? E.g., what if dst were %r0 (okay, let's hope not) or even just %r1-%r5 (even just %r4 or %r5)?? One position is that we don't do a robust job of BPF reg management anyhow and someday we're going to have to clean that up and so being sloppy now is okay or even necessary.? Another position is that we can add a little code to improve things (even if other areas of dt_cg.c vary considerably in quality).? E.g., one can fill-spill regs between the two function calls, costing extra BPF instructions only if needed. (That's already a pretty common practice in dt_cg.c.)? For extra robustness (beyond what we currently do in dt_cg.c), one can move %r1=dst and %r3=src in whatever order makes sense (more complicated and less likely to be useful).? Anyhow, fill-spill regs between function calls is a pretty common practice in dt_cg.c and I would think would make to do here. On 2/7/25 01:11, Kris Van Hees wrote: > The result of a string ternary was copied from either left or right > value using a memcpy() of strsize, but that could result in garbage > being included in the result beyond the terminating 0-byte of the > string value. If the result was e.g. assigned to a char-array > translator member, that garbage could affect consumer output. > > The ternary will now copy the string using dt_cg_strcpy() which ensures > that any string < strsize will be padded with 0-bytes up to strsize. > > Signed-off-by: Kris Van Hees > --- > libdtrace/dt_cg.c | 68 ++++++++++++++++++++++++++++++++++++----------- > 1 file changed, 52 insertions(+), 16 deletions(-) > > diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c > index a5c9aa09..b48e27f0 100644 > --- a/libdtrace/dt_cg.c > +++ b/libdtrace/dt_cg.c > @@ -1371,6 +1371,19 @@ dt_cg_fill_gap(dt_pcb_t *pcb, int gap) > emit(dlp, BPF_STORE_IMM(BPF_W, BPF_REG_9, off, 0)); > } > > +/* > + * Store a pointer to the 'memory block of zeros' in reg. > + */ > +static void > +dt_cg_zerosptr(int reg, dt_irlist_t *dlp, dt_regset_t *drp) > +{ > + dtrace_hdl_t *dtp = yypcb->pcb_hdl; > + dt_ident_t *zero_off = dt_dlib_get_var(dtp, "ZERO_OFF"); > + > + dt_cg_access_dctx(reg, dlp, drp, DCTX_STRTAB); > + emite(dlp, BPF_ALU64_IMM(BPF_ADD, reg, -1), zero_off); > +} > + > static void > dt_cg_memcpy(dt_irlist_t *dlp, dt_regset_t *drp, int dst, int src, size_t size) > { > @@ -1394,6 +1407,43 @@ dt_cg_memcpy(dt_irlist_t *dlp, dt_regset_t *drp, int dst, int src, size_t size) > dt_regset_free(drp, BPF_REG_0); > } > > +/* > + * Copy a string from the pointer stored in the src register to the pointer > + * stored in the dst register. At most strsize characters will be copied, and > + * if the source string is less than strsize characters, the remainder will be > + * filled with 0s. > + */ > +static void > +dt_cg_strcpy(dt_irlist_t *dlp, dt_regset_t *drp, int dst, int src) > +{ > + dtrace_hdl_t *dtp = yypcb->pcb_hdl; > + uint_t lbl_ok = dt_irlist_label(dlp); > + size_t size = dtp->dt_options[DTRACEOPT_STRSIZE]; > + > + if (dt_regset_xalloc_args(drp) == -1) > + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); > + > + emit(dlp, BPF_MOV_REG(BPF_REG_1, dst)); > + emit(dlp, BPF_MOV_IMM(BPF_REG_2, size)); > + emit(dlp, BPF_MOV_REG(BPF_REG_3, src)); > + dt_regset_xalloc(drp, BPF_REG_0); > + emit(dlp, BPF_CALL_HELPER(dtp->dt_bpfhelper[BPF_FUNC_probe_read_kernel_str])); > + > + emit(dlp, BPF_BRANCH_IMM(BPF_JSGE, BPF_REG_0, 0, lbl_ok)); > + dt_cg_probe_error(yypcb, DTRACEFLT_BADADDR, DT_ISREG, src); > + > + emitl(dlp, lbl_ok, > + BPF_NOP()); > + emit(dlp, BPF_MOV_REG(BPF_REG_1, dst)); > + emit(dlp, BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_0)); > + emit(dlp, BPF_MOV_IMM(BPF_REG_2, size)); > + emit(dlp, BPF_ALU64_REG(BPF_SUB, BPF_REG_2, BPF_REG_0)); > + dt_cg_zerosptr(BPF_REG_3, dlp, drp); > + emit(dlp, BPF_CALL_HELPER(dtp->dt_bpfhelper[BPF_FUNC_probe_read_kernel])); > + dt_regset_free(drp, BPF_REG_0); > + dt_regset_free_args(drp); > +} > + > static void > dt_cg_spill_store(int reg) > { > @@ -3040,19 +3090,6 @@ dt_cg_pop_stack(int reg, dt_irlist_t *dlp, dt_regset_t *drp) > dt_regset_free(drp, treg); > } > > -/* > - * Store a pointer to the 'memory block of zeros' in reg. > - */ > -static void > -dt_cg_zerosptr(int reg, dt_irlist_t *dlp, dt_regset_t *drp) > -{ > - dtrace_hdl_t *dtp = yypcb->pcb_hdl; > - dt_ident_t *zero_off = dt_dlib_get_var(dtp, "ZERO_OFF"); > - > - dt_cg_access_dctx(reg, dlp, drp, DCTX_STRTAB); > - emite(dlp, BPF_ALU64_IMM(BPF_ADD, reg, -1), zero_off); > -} > - > /* > * Generate code to promote signed scalars (size < 64 bits) to native register > * size (64 bits). > @@ -4603,7 +4640,7 @@ dt_cg_ternary_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) > * dn_right). > */ > if (dt_node_is_string(dnp)) { > - uint_t lbl_null = dt_irlist_label(dlp); > + uint_t lbl_null = dt_irlist_label(dlp); > > emit(dlp, BPF_BRANCH_IMM(BPF_JEQ, dnp->dn_reg, 0, lbl_null)); > > @@ -4619,8 +4656,7 @@ dt_cg_ternary_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) > dt_cg_access_dctx(dnp->dn_reg, dlp, drp, DCTX_MEM); > emit(dlp, BPF_ALU64_IMM(BPF_ADD, dnp->dn_reg, dnp->dn_tstring->dn_value)); > > - dt_cg_memcpy(dlp, drp, dnp->dn_reg, BPF_REG_0, > - yypcb->pcb_hdl->dt_options[DTRACEOPT_STRSIZE]); > + dt_cg_strcpy(dlp, drp, dnp->dn_reg, BPF_REG_0); > > emitl(dlp, lbl_null, > BPF_NOP()); From kris.van.hees at oracle.com Fri Feb 7 20:22:32 2025 From: kris.van.hees at oracle.com (Kris Van Hees) Date: Fri, 7 Feb 2025 15:22:32 -0500 Subject: [DTrace-devel] [PATCH] cg: ensure string results in a ternary are padded to strsize In-Reply-To: <495b1606-caba-9b59-68a0-0587d0ddf094@oracle.com> References: <6c5f5a2593039303a8f1f46fccb7a198.kris.van.hees@oracle.com> <495b1606-caba-9b59-68a0-0587d0ddf094@oracle.com> Message-ID: On Fri, Feb 07, 2025 at 02:17:44PM -0500, Eugene Loh wrote: > Reviewed-by: Eugene Loh > > That said: > > *)? Is it possible to construct a test? The execargs patch provides a test for this. The situation here is quite unusual because we end up assigning a DTrace string to a char array, which is an operation that you generally cannot do in D. > *)? Has there been an audit to ferret out other instances of this problem?? > Also, I'm confused what position we're taking here. We're saying that it's > possible for a string (which in D has strsize) to have garbage after the NUL > byte, but we'll deal with this situation only in the case that it's an > argument of a ternary operation?? Why do we not say instead that a strsize > string will not have garbage after the NUL byte, so that a strsize copy of a > string will be safe (regardless of whether it's a ternary op)? I am not sure what you mean with 'taking a position'. This is a fix for a real problem, that is unusual (see above) yet occurs and results in trace() output to be wrong. It may be possible that there are other cases where a similar problem may occur, but well, none have popped up as far as I know (partly witnessed that this is the first time it is noticed AFAIK and that is the reason I am fixing it). > *)? What position are we taking here on BPF register pressure? E.g., what if > dst were %r0 (okay, let's hope not) or even just %r1-%r5 (even just %r4 or > %r5)?? One position is that we don't do a robust job of BPF reg management > anyhow and someday we're going to have to clean that up and so being sloppy > now is okay or even necessary.? Another position is that we can add a little > code to improve things (even if other areas of dt_cg.c vary considerably in > quality).? E.g., one can fill-spill regs between the two function calls, > costing extra BPF instructions only if needed. (That's already a pretty > common practice in dt_cg.c.)? For extra robustness (beyond what we currently > do in dt_cg.c), one can move %r1=dst and %r3=src in whatever order makes > sense (more complicated and less likely to be useful).? Anyhow, fill-spill > regs between function calls is a pretty common practice in dt_cg.c and I > would think would make to do here. I'll look closer at the register use here but I don't think there is a problem actually. dt_cg_strcpy() is not meant to be a general use function at this point (perhaps some day it will be). I can also move this into the dt_op_ternary() funnction, but I felt that mighht make it at bit too complex. > On 2/7/25 01:11, Kris Van Hees wrote: > > The result of a string ternary was copied from either left or right > > value using a memcpy() of strsize, but that could result in garbage > > being included in the result beyond the terminating 0-byte of the > > string value. If the result was e.g. assigned to a char-array > > translator member, that garbage could affect consumer output. > > > > The ternary will now copy the string using dt_cg_strcpy() which ensures > > that any string < strsize will be padded with 0-bytes up to strsize. > > > > Signed-off-by: Kris Van Hees > > --- > > libdtrace/dt_cg.c | 68 ++++++++++++++++++++++++++++++++++++----------- > > 1 file changed, 52 insertions(+), 16 deletions(-) > > > > diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c > > index a5c9aa09..b48e27f0 100644 > > --- a/libdtrace/dt_cg.c > > +++ b/libdtrace/dt_cg.c > > @@ -1371,6 +1371,19 @@ dt_cg_fill_gap(dt_pcb_t *pcb, int gap) > > emit(dlp, BPF_STORE_IMM(BPF_W, BPF_REG_9, off, 0)); > > } > > +/* > > + * Store a pointer to the 'memory block of zeros' in reg. > > + */ > > +static void > > +dt_cg_zerosptr(int reg, dt_irlist_t *dlp, dt_regset_t *drp) > > +{ > > + dtrace_hdl_t *dtp = yypcb->pcb_hdl; > > + dt_ident_t *zero_off = dt_dlib_get_var(dtp, "ZERO_OFF"); > > + > > + dt_cg_access_dctx(reg, dlp, drp, DCTX_STRTAB); > > + emite(dlp, BPF_ALU64_IMM(BPF_ADD, reg, -1), zero_off); > > +} > > + > > static void > > dt_cg_memcpy(dt_irlist_t *dlp, dt_regset_t *drp, int dst, int src, size_t size) > > { > > @@ -1394,6 +1407,43 @@ dt_cg_memcpy(dt_irlist_t *dlp, dt_regset_t *drp, int dst, int src, size_t size) > > dt_regset_free(drp, BPF_REG_0); > > } > > +/* > > + * Copy a string from the pointer stored in the src register to the pointer > > + * stored in the dst register. At most strsize characters will be copied, and > > + * if the source string is less than strsize characters, the remainder will be > > + * filled with 0s. > > + */ > > +static void > > +dt_cg_strcpy(dt_irlist_t *dlp, dt_regset_t *drp, int dst, int src) > > +{ > > + dtrace_hdl_t *dtp = yypcb->pcb_hdl; > > + uint_t lbl_ok = dt_irlist_label(dlp); > > + size_t size = dtp->dt_options[DTRACEOPT_STRSIZE]; > > + > > + if (dt_regset_xalloc_args(drp) == -1) > > + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); > > + > > + emit(dlp, BPF_MOV_REG(BPF_REG_1, dst)); > > + emit(dlp, BPF_MOV_IMM(BPF_REG_2, size)); > > + emit(dlp, BPF_MOV_REG(BPF_REG_3, src)); > > + dt_regset_xalloc(drp, BPF_REG_0); > > + emit(dlp, BPF_CALL_HELPER(dtp->dt_bpfhelper[BPF_FUNC_probe_read_kernel_str])); > > + > > + emit(dlp, BPF_BRANCH_IMM(BPF_JSGE, BPF_REG_0, 0, lbl_ok)); > > + dt_cg_probe_error(yypcb, DTRACEFLT_BADADDR, DT_ISREG, src); > > + > > + emitl(dlp, lbl_ok, > > + BPF_NOP()); > > + emit(dlp, BPF_MOV_REG(BPF_REG_1, dst)); > > + emit(dlp, BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_0)); > > + emit(dlp, BPF_MOV_IMM(BPF_REG_2, size)); > > + emit(dlp, BPF_ALU64_REG(BPF_SUB, BPF_REG_2, BPF_REG_0)); > > + dt_cg_zerosptr(BPF_REG_3, dlp, drp); > > + emit(dlp, BPF_CALL_HELPER(dtp->dt_bpfhelper[BPF_FUNC_probe_read_kernel])); > > + dt_regset_free(drp, BPF_REG_0); > > + dt_regset_free_args(drp); > > +} > > + > > static void > > dt_cg_spill_store(int reg) > > { > > @@ -3040,19 +3090,6 @@ dt_cg_pop_stack(int reg, dt_irlist_t *dlp, dt_regset_t *drp) > > dt_regset_free(drp, treg); > > } > > -/* > > - * Store a pointer to the 'memory block of zeros' in reg. > > - */ > > -static void > > -dt_cg_zerosptr(int reg, dt_irlist_t *dlp, dt_regset_t *drp) > > -{ > > - dtrace_hdl_t *dtp = yypcb->pcb_hdl; > > - dt_ident_t *zero_off = dt_dlib_get_var(dtp, "ZERO_OFF"); > > - > > - dt_cg_access_dctx(reg, dlp, drp, DCTX_STRTAB); > > - emite(dlp, BPF_ALU64_IMM(BPF_ADD, reg, -1), zero_off); > > -} > > - > > /* > > * Generate code to promote signed scalars (size < 64 bits) to native register > > * size (64 bits). > > @@ -4603,7 +4640,7 @@ dt_cg_ternary_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) > > * dn_right). > > */ > > if (dt_node_is_string(dnp)) { > > - uint_t lbl_null = dt_irlist_label(dlp); > > + uint_t lbl_null = dt_irlist_label(dlp); > > emit(dlp, BPF_BRANCH_IMM(BPF_JEQ, dnp->dn_reg, 0, lbl_null)); > > @@ -4619,8 +4656,7 @@ dt_cg_ternary_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) > > dt_cg_access_dctx(dnp->dn_reg, dlp, drp, DCTX_MEM); > > emit(dlp, BPF_ALU64_IMM(BPF_ADD, dnp->dn_reg, dnp->dn_tstring->dn_value)); > > - dt_cg_memcpy(dlp, drp, dnp->dn_reg, BPF_REG_0, > > - yypcb->pcb_hdl->dt_options[DTRACEOPT_STRSIZE]); > > + dt_cg_strcpy(dlp, drp, dnp->dn_reg, BPF_REG_0); > > emitl(dlp, lbl_null, > > BPF_NOP()); From kris.van.hees at oracle.com Fri Feb 7 21:00:03 2025 From: kris.van.hees at oracle.com (Kris Van Hees) Date: Fri, 7 Feb 2025 16:00:03 -0500 Subject: [DTrace-devel] [PATCH] cg: ensure string results in a ternary are padded to strsize In-Reply-To: References: <6c5f5a2593039303a8f1f46fccb7a198.kris.van.hees@oracle.com> <495b1606-caba-9b59-68a0-0587d0ddf094@oracle.com> Message-ID: Actually, I am going to replace this patch with something totally different. While it is essentially correct, it is just not worth it given that assignment from string to char[] is not allowed in D, and somehow translators do it anyway (in a roundabout way). I prefer to just not cause this complication, and instead will be changing the member types in psinfo and lwpsinfo to not use char-arrays and instead use string (as is done in the io provider translated structs). That avoids this complication in a much more natural way. I will also introduce a patch to change the ternary for string types to use a string copy rather than using memcpy, because we really should be using the string primitives (probe_read_kerneL_str instead of probe_read_kernel, or even probe_read). That should do the correct thing anyway, and when we are using string types, we do not need to worry about training garbage because it is ignored correctly in those cases. The patch I sent to fix the consume code is still valid though because people ca nstill use char arrays for other things. On Fri, Feb 07, 2025 at 03:22:32PM -0500, Kris Van Hees wrote: > On Fri, Feb 07, 2025 at 02:17:44PM -0500, Eugene Loh wrote: > > Reviewed-by: Eugene Loh > > > > That said: > > > > *)? Is it possible to construct a test? > > The execargs patch provides a test for this. The situation here is quite > unusual because we end up assigning a DTrace string to a char array, which > is an operation that you generally cannot do in D. > > > *)? Has there been an audit to ferret out other instances of this problem?? > > Also, I'm confused what position we're taking here. We're saying that it's > > possible for a string (which in D has strsize) to have garbage after the NUL > > byte, but we'll deal with this situation only in the case that it's an > > argument of a ternary operation?? Why do we not say instead that a strsize > > string will not have garbage after the NUL byte, so that a strsize copy of a > > string will be safe (regardless of whether it's a ternary op)? > > I am not sure what you mean with 'taking a position'. This is a fix for a > real problem, that is unusual (see above) yet occurs and results in trace() > output to be wrong. It may be possible that there are other cases where a > similar problem may occur, but well, none have popped up as far as I know > (partly witnessed that this is the first time it is noticed AFAIK and that > is the reason I am fixing it). > > > *)? What position are we taking here on BPF register pressure? E.g., what if > > dst were %r0 (okay, let's hope not) or even just %r1-%r5 (even just %r4 or > > %r5)?? One position is that we don't do a robust job of BPF reg management > > anyhow and someday we're going to have to clean that up and so being sloppy > > now is okay or even necessary.? Another position is that we can add a little > > code to improve things (even if other areas of dt_cg.c vary considerably in > > quality).? E.g., one can fill-spill regs between the two function calls, > > costing extra BPF instructions only if needed. (That's already a pretty > > common practice in dt_cg.c.)? For extra robustness (beyond what we currently > > do in dt_cg.c), one can move %r1=dst and %r3=src in whatever order makes > > sense (more complicated and less likely to be useful).? Anyhow, fill-spill > > regs between function calls is a pretty common practice in dt_cg.c and I > > would think would make to do here. > > I'll look closer at the register use here but I don't think there is a > problem actually. dt_cg_strcpy() is not meant to be a general use function > at this point (perhaps some day it will be). I can also move this into > the dt_op_ternary() funnction, but I felt that mighht make it at bit too > complex. > > > On 2/7/25 01:11, Kris Van Hees wrote: > > > The result of a string ternary was copied from either left or right > > > value using a memcpy() of strsize, but that could result in garbage > > > being included in the result beyond the terminating 0-byte of the > > > string value. If the result was e.g. assigned to a char-array > > > translator member, that garbage could affect consumer output. > > > > > > The ternary will now copy the string using dt_cg_strcpy() which ensures > > > that any string < strsize will be padded with 0-bytes up to strsize. > > > > > > Signed-off-by: Kris Van Hees > > > --- > > > libdtrace/dt_cg.c | 68 ++++++++++++++++++++++++++++++++++++----------- > > > 1 file changed, 52 insertions(+), 16 deletions(-) > > > > > > diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c > > > index a5c9aa09..b48e27f0 100644 > > > --- a/libdtrace/dt_cg.c > > > +++ b/libdtrace/dt_cg.c > > > @@ -1371,6 +1371,19 @@ dt_cg_fill_gap(dt_pcb_t *pcb, int gap) > > > emit(dlp, BPF_STORE_IMM(BPF_W, BPF_REG_9, off, 0)); > > > } > > > +/* > > > + * Store a pointer to the 'memory block of zeros' in reg. > > > + */ > > > +static void > > > +dt_cg_zerosptr(int reg, dt_irlist_t *dlp, dt_regset_t *drp) > > > +{ > > > + dtrace_hdl_t *dtp = yypcb->pcb_hdl; > > > + dt_ident_t *zero_off = dt_dlib_get_var(dtp, "ZERO_OFF"); > > > + > > > + dt_cg_access_dctx(reg, dlp, drp, DCTX_STRTAB); > > > + emite(dlp, BPF_ALU64_IMM(BPF_ADD, reg, -1), zero_off); > > > +} > > > + > > > static void > > > dt_cg_memcpy(dt_irlist_t *dlp, dt_regset_t *drp, int dst, int src, size_t size) > > > { > > > @@ -1394,6 +1407,43 @@ dt_cg_memcpy(dt_irlist_t *dlp, dt_regset_t *drp, int dst, int src, size_t size) > > > dt_regset_free(drp, BPF_REG_0); > > > } > > > +/* > > > + * Copy a string from the pointer stored in the src register to the pointer > > > + * stored in the dst register. At most strsize characters will be copied, and > > > + * if the source string is less than strsize characters, the remainder will be > > > + * filled with 0s. > > > + */ > > > +static void > > > +dt_cg_strcpy(dt_irlist_t *dlp, dt_regset_t *drp, int dst, int src) > > > +{ > > > + dtrace_hdl_t *dtp = yypcb->pcb_hdl; > > > + uint_t lbl_ok = dt_irlist_label(dlp); > > > + size_t size = dtp->dt_options[DTRACEOPT_STRSIZE]; > > > + > > > + if (dt_regset_xalloc_args(drp) == -1) > > > + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); > > > + > > > + emit(dlp, BPF_MOV_REG(BPF_REG_1, dst)); > > > + emit(dlp, BPF_MOV_IMM(BPF_REG_2, size)); > > > + emit(dlp, BPF_MOV_REG(BPF_REG_3, src)); > > > + dt_regset_xalloc(drp, BPF_REG_0); > > > + emit(dlp, BPF_CALL_HELPER(dtp->dt_bpfhelper[BPF_FUNC_probe_read_kernel_str])); > > > + > > > + emit(dlp, BPF_BRANCH_IMM(BPF_JSGE, BPF_REG_0, 0, lbl_ok)); > > > + dt_cg_probe_error(yypcb, DTRACEFLT_BADADDR, DT_ISREG, src); > > > + > > > + emitl(dlp, lbl_ok, > > > + BPF_NOP()); > > > + emit(dlp, BPF_MOV_REG(BPF_REG_1, dst)); > > > + emit(dlp, BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_0)); > > > + emit(dlp, BPF_MOV_IMM(BPF_REG_2, size)); > > > + emit(dlp, BPF_ALU64_REG(BPF_SUB, BPF_REG_2, BPF_REG_0)); > > > + dt_cg_zerosptr(BPF_REG_3, dlp, drp); > > > + emit(dlp, BPF_CALL_HELPER(dtp->dt_bpfhelper[BPF_FUNC_probe_read_kernel])); > > > + dt_regset_free(drp, BPF_REG_0); > > > + dt_regset_free_args(drp); > > > +} > > > + > > > static void > > > dt_cg_spill_store(int reg) > > > { > > > @@ -3040,19 +3090,6 @@ dt_cg_pop_stack(int reg, dt_irlist_t *dlp, dt_regset_t *drp) > > > dt_regset_free(drp, treg); > > > } > > > -/* > > > - * Store a pointer to the 'memory block of zeros' in reg. > > > - */ > > > -static void > > > -dt_cg_zerosptr(int reg, dt_irlist_t *dlp, dt_regset_t *drp) > > > -{ > > > - dtrace_hdl_t *dtp = yypcb->pcb_hdl; > > > - dt_ident_t *zero_off = dt_dlib_get_var(dtp, "ZERO_OFF"); > > > - > > > - dt_cg_access_dctx(reg, dlp, drp, DCTX_STRTAB); > > > - emite(dlp, BPF_ALU64_IMM(BPF_ADD, reg, -1), zero_off); > > > -} > > > - > > > /* > > > * Generate code to promote signed scalars (size < 64 bits) to native register > > > * size (64 bits). > > > @@ -4603,7 +4640,7 @@ dt_cg_ternary_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) > > > * dn_right). > > > */ > > > if (dt_node_is_string(dnp)) { > > > - uint_t lbl_null = dt_irlist_label(dlp); > > > + uint_t lbl_null = dt_irlist_label(dlp); > > > emit(dlp, BPF_BRANCH_IMM(BPF_JEQ, dnp->dn_reg, 0, lbl_null)); > > > @@ -4619,8 +4656,7 @@ dt_cg_ternary_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) > > > dt_cg_access_dctx(dnp->dn_reg, dlp, drp, DCTX_MEM); > > > emit(dlp, BPF_ALU64_IMM(BPF_ADD, dnp->dn_reg, dnp->dn_tstring->dn_value)); > > > - dt_cg_memcpy(dlp, drp, dnp->dn_reg, BPF_REG_0, > > > - yypcb->pcb_hdl->dt_options[DTRACEOPT_STRSIZE]); > > > + dt_cg_strcpy(dlp, drp, dnp->dn_reg, BPF_REG_0); > > > emitl(dlp, lbl_null, > > > BPF_NOP()); From eugene.loh at oracle.com Sat Feb 8 19:06:19 2025 From: eugene.loh at oracle.com (eugene.loh at oracle.com) Date: Sat, 8 Feb 2025 14:06:19 -0500 Subject: [DTrace-devel] [PATCH 1/4] Rename _DTRACE_VERSION Message-ID: <20250208190622.23484-1-eugene.loh@oracle.com> From: Eugene Loh There are many DTrace version numbers (for version, API version, package version, etc.). Meanwhile, _DTRACE_VERSION is not a version number at all. It's a preprocessor macro in USDT .h header files. Prior to commit e2fb0ecd9 ("Ensure multiple passes through dtrace -G work."), it was perhaps not even set. With that commit, it was always set to 1, with the rationale: Also add an explicit define for _DTRACE__VERSION in the generated header file from 'dtrace -h' invocations. This seems silly, but it is there to give people a skeleton to work with if they want to pre-generate header files and select whether to actually compile on the probes at a later time. Rename to _DTRACE_HEADER for better clarity. Define it only once per file. Signed-off-by: Eugene Loh --- libdtrace/dt_program.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libdtrace/dt_program.c b/libdtrace/dt_program.c index 23b91fb2e..c6fdafb47 100644 --- a/libdtrace/dt_program.c +++ b/libdtrace/dt_program.c @@ -505,13 +505,12 @@ dt_header_provider(dtrace_hdl_t *dtp, dt_provider_t *pvp, FILE *out) info.dthi_pfname = alloca(strlen(pvp->desc.dtvd_name) + 1 + i); dt_header_fmt_func(info.dthi_pfname, pvp->desc.dtvd_name); - if (fprintf(out, "#define _DTRACE_VERSION 1\n\n" - "#if _DTRACE_VERSION\n\n") < 0) + if (fprintf(out, "#if _DTRACE_HEADER\n\n") < 0) return dt_set_errno(dtp, errno); if (dt_idhash_iter(pvp->pv_probes, dt_header_probe, &info) != 0) return -1; /* dt_errno is set for us */ - if (fprintf(out, "\n\n") < 0) + if (fprintf(out, "\n") < 0) return dt_set_errno(dtp, errno); if (dt_idhash_iter(pvp->pv_probes, dt_header_decl, &info) != 0) return -1; /* dt_errno is set for us */ @@ -560,6 +559,9 @@ dtrace_program_header(dtrace_hdl_t *dtp, FILE *out, const char *fname) "#endif\n\n") < 0) return -1; + if (fprintf(out, "#define _DTRACE_HEADER 1\n\n") < 0) + return -1; + while ((pvp = dt_htab_next(dtp->dt_provs, &it)) != NULL) { if (dt_header_provider(dtp, pvp, out) != 0) { dt_htab_next_destroy(it); -- 2.43.5 From eugene.loh at oracle.com Sat Feb 8 19:06:20 2025 From: eugene.loh at oracle.com (eugene.loh at oracle.com) Date: Sat, 8 Feb 2025 14:06:20 -0500 Subject: [DTrace-devel] [PATCH 2/4] Eliminate DT_VERS_LATEST In-Reply-To: <20250208190622.23484-1-eugene.loh@oracle.com> References: <20250208190622.23484-1-eugene.loh@oracle.com> Message-ID: <20250208190622.23484-2-eugene.loh@oracle.com> From: Eugene Loh Updating the DTrace version number requires too many distinct changes. Eliminate DT_VERS_LATEST, since it can be determined on the fly. Signed-off-by: Eugene Loh --- libdtrace/dt_open.c | 3 ++- libdtrace/dt_version.h | 14 +++++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/libdtrace/dt_open.c b/libdtrace/dt_open.c index a02058871..b4d160359 100644 --- a/libdtrace/dt_open.c +++ b/libdtrace/dt_open.c @@ -721,7 +721,8 @@ dt_vopen(int version, int flags, int *errp, dtp->dt_proc_fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); if (dt_aggregate_init(dtp) == -1) return set_open_errno(dtp, errp, dtrace_errno(dtp)); - dtp->dt_vmax = DT_VERS_LATEST; + for (i = 0; _dtrace_versions[i] != 0; i++) + dtp->dt_vmax = _dtrace_versions[i]; dtp->dt_cpp_path = strdup(_dtrace_defcpp); dtp->dt_cpp_argv = malloc(sizeof(char *)); dtp->dt_cpp_argc = 1; diff --git a/libdtrace/dt_version.h b/libdtrace/dt_version.h index 3fd1b3d1e..bef3243e9 100644 --- a/libdtrace/dt_version.h +++ b/libdtrace/dt_version.h @@ -38,18 +38,15 @@ extern "C" { * * These #defines are used in identifier tables to fill in the version fields * associated with each identifier. The DT_VERS_* macros declare the encoded - * integer values of all versions used so far. DT_VERS_LATEST must correspond - * to the latest version value among all versions exported by the D compiler. - * DT_VERS_STRING must be an ASCII string that contains DT_VERS_LATEST within - * it along with any suffixes (e.g. Beta). + * integer values of all versions used so far. DT_VERS_STRING must be an ASCII + * string that contains the latest version within it along with any suffixes + * (e.g. Beta). You must update DT_VERS_STRING when adding a new version, + * and then add the new version to the _dtrace_versions[] array declared in + * dt_open.c. * * Refer to the Solaris Dynamic Tracing Guide Versioning chapter for an * explanation of these DTrace features and their values. * - * You must update DT_VERS_LATEST and DT_VERS_STRING when adding a new version, - * and then add the new version to the _dtrace_versions[] array declared in - * dt_open.c.. - * * NOTE: Although the DTrace versioning scheme supports the labeling and * introduction of incompatible changes (e.g. dropping an interface in a * major release), the libdtrace code does not currently support this. @@ -85,7 +82,6 @@ extern "C" { #define DT_VERS_2_0 DT_VERSION_NUMBER(2, 0, 0) #define DT_VERS_2_0_1 DT_VERSION_NUMBER(2, 0, 1) -#define DT_VERS_LATEST DT_VERS_2_0_1 #define DT_VERS_STRING "Oracle D 2.0" #ifdef __cplusplus -- 2.43.5 From eugene.loh at oracle.com Sat Feb 8 19:06:21 2025 From: eugene.loh at oracle.com (eugene.loh at oracle.com) Date: Sat, 8 Feb 2025 14:06:21 -0500 Subject: [DTrace-devel] [PATCH 3/4] Sync up the version numbers In-Reply-To: <20250208190622.23484-1-eugene.loh@oracle.com> References: <20250208190622.23484-1-eugene.loh@oracle.com> Message-ID: <20250208190622.23484-3-eugene.loh@oracle.com> From: Eugene Loh DTrace has many version numbers -- e.g., for the release, packaging, and API. In reality, the variations in numbering have become nearly meaningless: - Packaging numbers -- like the Version in the dtrace*spec RPM spec file and the VERSION in GNUmakefile -- have basically been tracking the DTrace release since 2.0 anyway. - Stability attributes for idents are haphazard. Generally, they are 1.0 (or sometimes other 1.x), and all the BPFs are 2.0. While this is generally accurate, it is not exactly robust, and idents are sometimes introduced or modified without careful regard to the version number. Further, the stability of user D scripts is likely to depend more on kernel variations -- e.g., the contents of available_filter_functions -- than on D changes. - Version number updates are susceptible to mistakes, and so there have been version mismatches. Bring the version numbers into sync for 2.0.2. Clean up the descriptions in dt_version.h. Signed-off-by: Eugene Loh --- GNUmakefile | 5 +- dtrace.spec | 1 + libdtrace/dt_open.c | 3 ++ libdtrace/dt_version.h | 53 ++++++++++++-------- test/unittest/dtrace-util/tst.APIVersion.r | 2 +- test/unittest/dtrace-util/tst.APIVersion.r.p | 6 +++ test/unittest/options/tst.version.r | 2 +- test/unittest/options/tst.version.sh | 4 +- 8 files changed, 50 insertions(+), 26 deletions(-) create mode 100755 test/unittest/dtrace-util/tst.APIVersion.r.p diff --git a/GNUmakefile b/GNUmakefile index d1e18bb1b..ddf997878 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -3,7 +3,7 @@ # Build files in subdirectories are included by this file. # # Oracle Linux DTrace. -# Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 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. @@ -14,7 +14,8 @@ SHELL = /bin/bash PROJECT := dtrace -VERSION := 2.0.1 +# When updating version, see comments in dt_version.h. +VERSION := 2.0.2 # Verify supported hardware. diff --git a/dtrace.spec b/dtrace.spec index 902ad7d8b..46950bf5a 100644 --- a/dtrace.spec +++ b/dtrace.spec @@ -87,6 +87,7 @@ Requires: libdtrace-ctf >= 1.1.0 BuildRequires: libdtrace-ctf-devel >= 1.1.0 %endif Summary: DTrace user interface. +# When updating version, see comments in dt_version.h. Version: 2.0.2 Release: 1%{?dist} Source: dtrace-%{version}.tar.bz2 diff --git a/libdtrace/dt_open.c b/libdtrace/dt_open.c index b4d160359..b843228b3 100644 --- a/libdtrace/dt_open.c +++ b/libdtrace/dt_open.c @@ -58,6 +58,9 @@ const dt_version_t _dtrace_versions[] = { DT_VERS_1_6_3, /* D API 1.6.3 */ DT_VERS_1_6_4, /* D API 1.6.4 */ DT_VERS_2_0, /* D API 2.0 */ + DT_VERS_2_0_1, /* D API 2.0.1 */ + DT_VERS_2_0_2, /* D API 2.0.2 */ + /* When updating version, see comments in dt_version.h. */ 0 }; diff --git a/libdtrace/dt_version.h b/libdtrace/dt_version.h index bef3243e9..4e230ceae 100644 --- a/libdtrace/dt_version.h +++ b/libdtrace/dt_version.h @@ -38,32 +38,28 @@ extern "C" { * * These #defines are used in identifier tables to fill in the version fields * associated with each identifier. The DT_VERS_* macros declare the encoded - * integer values of all versions used so far. DT_VERS_STRING must be an ASCII - * string that contains the latest version within it along with any suffixes - * (e.g. Beta). You must update DT_VERS_STRING when adding a new version, - * and then add the new version to the _dtrace_versions[] array declared in - * dt_open.c. + * integer values of all versions used so far. * - * Refer to the Solaris Dynamic Tracing Guide Versioning chapter for an - * explanation of these DTrace features and their values. + * The major number should be incremented when a fundamental change has been + * made that would affect all consumers, and would reflect sweeping changes + * to DTrace or the D language. + * + * The minor number should be incremented when a change is introduced that + * could break scripts that had previously worked; for example, adding a + * new built-in variable could break a script which was already using that + * identifier. + * + * The micro number should be changed when introducing functionality changes + * or major bug fixes that do not affect backward compatibility -- this is + * merely to make capabilities easily determined from the version number. + * + * Minor bugs do not require any modification to the version number. * * NOTE: Although the DTrace versioning scheme supports the labeling and * introduction of incompatible changes (e.g. dropping an interface in a * major release), the libdtrace code does not currently support this. * All versions are assumed to strictly inherit from one another. If * we ever need to provide divergent interfaces, this will need work. - * - * The version number should be increased for every customer visible release - * of Solaris. The major number should be incremented when a fundamental - * change has been made that would affect all consumers, and would reflect - * sweeping changes to DTrace or the D language. The minor number should be - * incremented when a change is introduced that could break scripts that had - * previously worked; for example, adding a new built-in variable could break - * a script which was already using that identifier. The micro number should - * be changed when introducing functionality changes or major bug fixes that - * do not affect backward compatibility -- this is merely to make capabilities - * easily determined from the version number. Minor bugs do not require any - * modification to the version number. */ #define DT_VERS_1_0 DT_VERSION_NUMBER(1, 0, 0) #define DT_VERS_1_1 DT_VERSION_NUMBER(1, 1, 0) @@ -81,8 +77,25 @@ extern "C" { #define DT_VERS_1_6_4 DT_VERSION_NUMBER(1, 6, 4) #define DT_VERS_2_0 DT_VERSION_NUMBER(2, 0, 0) #define DT_VERS_2_0_1 DT_VERSION_NUMBER(2, 0, 1) +#define DT_VERS_2_0_2 DT_VERSION_NUMBER(2, 0, 2) + +/* + * When the version number is updated, the following must be kept in sync: + * + * libdtrace/dt_version.h DT_VERS_STRING, an ASCII string that contains + * the latest version within it along with any + * suffixes (e.g. Beta) + * + * libdtrace/dt_open.c _dtrace_versions[] + * + * dtrace.spec Version + * + * libdtrace/Build libdtrace_VERSION + * + * GNUmakefile VERSION + */ -#define DT_VERS_STRING "Oracle D 2.0" +#define DT_VERS_STRING "Oracle D 2.0.2" #ifdef __cplusplus } diff --git a/test/unittest/dtrace-util/tst.APIVersion.r b/test/unittest/dtrace-util/tst.APIVersion.r index 6bc7b9d72..02bf03150 100644 --- a/test/unittest/dtrace-util/tst.APIVersion.r +++ b/test/unittest/dtrace-util/tst.APIVersion.r @@ -1 +1 @@ -dtrace: Oracle D 2.0 +dtrace: Oracle D 2.0.x diff --git a/test/unittest/dtrace-util/tst.APIVersion.r.p b/test/unittest/dtrace-util/tst.APIVersion.r.p new file mode 100755 index 000000000..32ec94df4 --- /dev/null +++ b/test/unittest/dtrace-util/tst.APIVersion.r.p @@ -0,0 +1,6 @@ +#!/usr/bin/gawk -f + +# The test allows the version string to vary in micro number as well as +# other suffixes (like "Beta"). The .r.p and .r files still need to be +# updated for each minor number change. +{ sub("^dtrace: Oracle D 2\\.0\\..*$", "dtrace: Oracle D 2.0.x"); print } diff --git a/test/unittest/options/tst.version.r b/test/unittest/options/tst.version.r index 15010b3db..882e208ed 100644 --- a/test/unittest/options/tst.version.r +++ b/test/unittest/options/tst.version.r @@ -1,2 +1,2 @@ -version is 2.0 +version is 2.0.x diff --git a/test/unittest/options/tst.version.sh b/test/unittest/options/tst.version.sh index ffffcdd8b..684120af8 100755 --- a/test/unittest/options/tst.version.sh +++ b/test/unittest/options/tst.version.sh @@ -1,7 +1,7 @@ #!/bin/bash # # Oracle Linux DTrace. -# Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2023, 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. # @@ -9,7 +9,7 @@ dtrace=$1 myversion=`$dtrace $dt_flags -V | gawk '{ print $NF }'` -echo version is $myversion +echo version is $myversion | sed 's:2.0.[0-9]:2.0.x:' $dtrace $dt_flags -xversion=$myversion -qn 'BEGIN { exit(0) }' exit $? -- 2.43.5 From eugene.loh at oracle.com Sat Feb 8 19:06:22 2025 From: eugene.loh at oracle.com (eugene.loh at oracle.com) Date: Sat, 8 Feb 2025 14:06:22 -0500 Subject: [DTrace-devel] [PATCH 4/4] test: Add test for predefined preprocessor definitions In-Reply-To: <20250208190622.23484-1-eugene.loh@oracle.com> References: <20250208190622.23484-1-eugene.loh@oracle.com> Message-ID: <20250208190622.23484-4-eugene.loh@oracle.com> From: Eugene Loh Orabug: 28763074 Signed-off-by: Eugene Loh --- COMMANDLINE-OPTIONS | 10 +- test/unittest/preprocessor/tst.predefined.r | 1 + test/unittest/preprocessor/tst.predefined.sh | 119 +++++++++++++++++++ 3 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 test/unittest/preprocessor/tst.predefined.r create mode 100755 test/unittest/preprocessor/tst.predefined.sh diff --git a/COMMANDLINE-OPTIONS b/COMMANDLINE-OPTIONS index 40561af91..73be89b1f 100644 --- a/COMMANDLINE-OPTIONS +++ b/COMMANDLINE-OPTIONS @@ -321,12 +321,12 @@ definitions are always specified and valid in all modes: * __sparcv9 (on SPARC? systems only when 64?bit programs are compiled) * __i386 (on x86 systems only when 32?bit programs are compiled) * __amd64 (on x86 systems only when 64?bit programs are compiled) - * _`uname -s` (for example, __Linux) + * __`uname -s` (for example, __Linux) * __SUNW_D=1 - * _SUNW_D_VERSION=0x_MMmmmuuu (where MM is the Major release value - in hexadecimal, mmm is the Minor release value in hexadecimal, - and uuu is the Micro release value in hexadecimal; see Chapter - 41, Versioning for more information about DTrace versioning) + * _SUNW_D_VERSION=(MM << 24 | mmm << 12 | uuu), where + MM is the Major release value + mmm is the Minor release value + uuu is the Micro release value -Z Permit probe descriptions that match zero probes. If the -Z option is diff --git a/test/unittest/preprocessor/tst.predefined.r b/test/unittest/preprocessor/tst.predefined.r new file mode 100644 index 000000000..2e9ba477f --- /dev/null +++ b/test/unittest/preprocessor/tst.predefined.r @@ -0,0 +1 @@ +success diff --git a/test/unittest/preprocessor/tst.predefined.sh b/test/unittest/preprocessor/tst.predefined.sh new file mode 100755 index 000000000..79caf17ac --- /dev/null +++ b/test/unittest/preprocessor/tst.predefined.sh @@ -0,0 +1,119 @@ +#!/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. +# +# Confirm preprocessor pre-definitions. + +dtrace=$1 + +DIRNAME=$tmpdir/predefined.$$.$RANDOM +mkdir -p $DIRNAME +cd $DIRNAME + +# Arg 1 is macro that we check is defined. + +function check_defined() { + # Add to script: #ifdef is okay, else is ERROR. + echo '#ifdef' $1 >> D.d + echo 'printf("'$1' okay\n");' >> D.d + echo '#else' >> D.d + echo 'printf("ERROR! missing '$1'\n");' >> D.d + echo '#endif' >> D.d + + # Add to check file: expect "okay" message. + echo $1 okay >> chk.txt +} + +# Arg 1 is macro whose value we check to be arg 2. + +function check_value() { + # Add to script: print value. + echo 'printf("'$1'=%x\n", '$1');' >> D.d + + # Add to check file: expected value. + echo $1=$2 >> chk.txt +} + +# Arg 1 is macro that we check is not defined. + +function check_undef() { + # Add to script: #ifdef is ERROR, else is okay. + echo '#ifdef' $1 >> D.d + echo 'printf("ERROR! found '$1'\n");' >> D.d + echo '#else' >> D.d + echo 'printf("missing '$1' is okay\n");' >> D.d + echo '#endif' >> D.d + + # Add to check file: expect "okay" message. + echo missing $1 is okay >> chk.txt +} + +# Construct version string (major, minor, micro). + +read MM mmm uuu <<< `dtrace -vV | awk '/^This is DTrace / { gsub("\\\.", " "); print $(NF-2), $(NF-1), $NF }'` +vers=`printf "%x" $(($MM << 24 | $mmm << 12 | $uuu))` + +# Start setting up the D script. + +echo 'BEGIN {' > D.d + +# Check for the preprocessor definitions of COMMANDLINE-OPTIONS. + +check_defined __linux +check_defined __unix +check_defined __SVR4 +if [ `uname -m` == x86_64 ]; then +check_defined __amd64 +else +check_undef __amd64 +fi +check_defined __`uname -s` +check_value __SUNW_D 1 +check_value __SUNW_D_VERSION $vers + +# Confirm other preprocessor definitions. + +check_defined __SUNW_D_64 + +# Confirm that __GNUC__ is not present. + +check_undef __GNUC__ + +# Finish setting up the D script. + +echo 'exit(0); }' >> D.d +echo >> chk.txt + +# Run the D script. + +$dtrace $dt_flags -qCs D.d -o out.txt +if [ $? -ne 0 ]; then + echo ERROR: DTrace failed + echo "==== D.d" + cat D.d + echo "==== out.txt" + cat out.txt + exit 1 +fi + +# Check. + +if ! diff -q chk.txt out.txt; then + echo ERROR output disagrees + echo === expect === + cat chk.txt + echo === actual === + cat out.txt + echo === diff === + diff chk.txt out.txt + exit 1 +fi + +# Indicate success. + +echo success + +exit 0 -- 2.43.5 From eugene.loh at oracle.com Sat Feb 8 23:21:05 2025 From: eugene.loh at oracle.com (eugene.loh at oracle.com) Date: Sat, 8 Feb 2025 18:21:05 -0500 Subject: [DTrace-devel] [PATCH 1/2] test: Allow more variations in expected fbt kernel stacks Message-ID: <20250208232106.27146-1-eugene.loh@oracle.com> From: Eugene Loh This test checks the call stack upon entry to vfs_write(). Unfortunately, these checks require some maintenance since the call stack can vary -- slightly or greatly -- depending on processor or kernel. There is a competition between ease of test maintenance and strictness of correctness checks. Adapt post processing of output to allow new variations in stacks seen in UEK 8 (currently Linux 6.12). Orabug: 37459289 Signed-off-by: Eugene Loh --- test/unittest/stack/tst.stack_fbt.sh | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/test/unittest/stack/tst.stack_fbt.sh b/test/unittest/stack/tst.stack_fbt.sh index c365e14d6..8a0b7999c 100755 --- a/test/unittest/stack/tst.stack_fbt.sh +++ b/test/unittest/stack/tst.stack_fbt.sh @@ -37,9 +37,18 @@ if [ $? -ne 0 ]; then exit 1 fi -# Strip out blank lines and pointer values. - -awk 'NF != 0 { sub(/+0x[0-9a-f]*$/, "+{ptr}"); print }' dtrace.out > dtrace.post +# Strip out +# - blank lines +# - "constprop" +# - "isra" +# - "_after_hwframe" (x86 starting with UEK8) +# - pointer values + +awk 'NF != 0 { sub("\\.constprop\\.[0-9]", ""); + sub("\\.isra\\.[0-9]", ""); + sub("_after_hwframe\\+", "+"); + sub(/+0x[0-9a-f]*$/, "+{ptr}"); + print }' dtrace.out > dtrace.post if [ $? -ne 0 ]; then echo ERROR: awk failed cat dtrace.out -- 2.43.5 From eugene.loh at oracle.com Sat Feb 8 23:21:06 2025 From: eugene.loh at oracle.com (eugene.loh at oracle.com) Date: Sat, 8 Feb 2025 18:21:06 -0500 Subject: [DTrace-devel] [PATCH 2/2] test: Account for pid:::entry ustack() being correct In-Reply-To: <20250208232106.27146-1-eugene.loh@oracle.com> References: <20250208232106.27146-1-eugene.loh@oracle.com> Message-ID: <20250208232106.27146-2-eugene.loh@oracle.com> From: Eugene Loh The pid:::entry uprobe fires so early in the function preamble that the frame pointer is not yet set and the caller is not (yet) correctly identified. In Linux 6.11, x86-specific heuristics address this problem. Post process results from this test to accommodate both cases -- missing caller and not missing caller. Orabug: 37459289 Signed-off-by: Eugene Loh --- test/unittest/ustack/tst.ustack25_pid.r | 1 + test/unittest/ustack/tst.ustack25_pid.r.p | 37 +++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100755 test/unittest/ustack/tst.ustack25_pid.r.p diff --git a/test/unittest/ustack/tst.ustack25_pid.r b/test/unittest/ustack/tst.ustack25_pid.r index 89b35c028..e7732fb81 100644 --- a/test/unittest/ustack/tst.ustack25_pid.r +++ b/test/unittest/ustack/tst.ustack25_pid.r @@ -1,5 +1,6 @@ ustack-tst-basic`myfunc_z + ustack-tst-basic`myfunc_y+{ptr} ustack-tst-basic`myfunc_x+{ptr} ustack-tst-basic`myfunc_w+{ptr} ustack-tst-basic`myfunc_v+{ptr} diff --git a/test/unittest/ustack/tst.ustack25_pid.r.p b/test/unittest/ustack/tst.ustack25_pid.r.p new file mode 100755 index 000000000..6486f7ec6 --- /dev/null +++ b/test/unittest/ustack/tst.ustack25_pid.r.p @@ -0,0 +1,37 @@ +#!/bin/sh + +# A pid entry probe places a uprobe on the first instruction of a function. +# Unfortunately, this is so early in the function preamble that the function +# frame pointer has not yet been established and the actual caller of the +# traced function is missed. +# +# In Linux 6.11, x86-specific heuristics are introduced to fix this problem. +# See commit cfa7f3d +# ("perf,x86: avoid missing caller address in stack traces captured in uprobe") +# for both a description of the problem and an explanation of the heuristics. +# +# Add post processing to these test results to allow for both cases: +# caller frame is missing or not missing. + +missing_caller=1 +if [ $(uname -m) == "x86_64" ]; then + read MAJOR MINOR <<< `uname -r | grep -Eo '^[0-9]+\.[0-9]+' | tr '.' ' '` + + if [ $MAJOR -ge 6 ]; then + if [ $MAJOR -gt 6 -o $MINOR -ge 11 ]; then + missing_caller=0 + fi + fi +fi + +if [ $missing_caller -eq 1 ]; then + # Add the missing caller function after the current function. + awk '{ print } + /myfunc_z/ { print " ustack-tst-basic`myfunc_y+{ptr}" }' +else + # The .r results file has an extra frame at the end in case + # the caller frame is missing and the 25-frame limit goes + # "too far." If the caller is not missing, fake that extra frame. + awk '{ print } + /myfunc_b/ { print " ustack-tst-basic`myfunc_a+{ptr}" }' +fi -- 2.43.5 From eugene.loh at oracle.com Wed Feb 19 21:51:42 2025 From: eugene.loh at oracle.com (eugene.loh at oracle.com) Date: Wed, 19 Feb 2025 16:51:42 -0500 Subject: [DTrace-devel] [PATCH] Fix fprobe/kprobe selection Message-ID: <20250219215142.10568-1-eugene.loh@oracle.com> From: Eugene Loh In commit 2a09b3bea504 ("fbt: clean up fprobe/kprobe support"), libdtrace/dt_prov_fbt.c populate() has this change: - impl = BPF_HAS(dtp, BPF_FEAT_FENTRY) ? &dt_fbt_fprobe : &dt_fbt_kprobe; + dt_fbt = BPF_HAS(dtp, BPF_FEAT_FENTRY) ? dt_fbt_kprobe : dt_fbt_kprobe; That is, regardless of the BPF_HAS() test, dt_fbt_kprobe is chosen. Restore the choice to pick up dt_fbt_fprobe when appropriate. It is hard to devise a test for this unique problem. Signed-off-by: Eugene Loh --- libdtrace/dt_prov_fbt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libdtrace/dt_prov_fbt.c b/libdtrace/dt_prov_fbt.c index f0910c371..eef93879d 100644 --- a/libdtrace/dt_prov_fbt.c +++ b/libdtrace/dt_prov_fbt.c @@ -75,7 +75,7 @@ static int populate(dtrace_hdl_t *dtp) dtrace_syminfo_t sip; dtrace_probedesc_t pd; - dt_fbt = BPF_HAS(dtp, BPF_FEAT_FENTRY) ? dt_fbt_kprobe : dt_fbt_kprobe; + dt_fbt = BPF_HAS(dtp, BPF_FEAT_FENTRY) ? dt_fbt_fprobe : dt_fbt_kprobe; prv = dt_provider_create(dtp, prvname, &dt_fbt, &pattr, NULL); if (prv == NULL) -- 2.43.5 From kris.van.hees at oracle.com Wed Feb 19 22:13:09 2025 From: kris.van.hees at oracle.com (Kris Van Hees) Date: Wed, 19 Feb 2025 17:13:09 -0500 Subject: [DTrace-devel] [PATCH] Fix fprobe/kprobe selection In-Reply-To: <20250219215142.10568-1-eugene.loh@oracle.com> References: <20250219215142.10568-1-eugene.loh@oracle.com> Message-ID: On Wed, Feb 19, 2025 at 04:51:42PM -0500, eugene.loh--- via DTrace-devel wrote: > From: Eugene Loh > > In commit 2a09b3bea504 ("fbt: clean up fprobe/kprobe support"), > libdtrace/dt_prov_fbt.c populate() has this change: > > - impl = BPF_HAS(dtp, BPF_FEAT_FENTRY) ? &dt_fbt_fprobe : &dt_fbt_kprobe; > + dt_fbt = BPF_HAS(dtp, BPF_FEAT_FENTRY) ? dt_fbt_kprobe : dt_fbt_kprobe; > > That is, regardless of the BPF_HAS() test, dt_fbt_kprobe is chosen. > > Restore the choice to pick up dt_fbt_fprobe when appropriate. > > It is hard to devise a test for this unique problem. > > Signed-off-by: Eugene Loh Reviewed-by: Kris Van Hees and my aplogies for introducing this problem. I believe it was caused by needing to force test verification in the case that the kprobe option was selected on a system, and I clearly forgot to revert that before preparing the actual patch. Kris > --- > libdtrace/dt_prov_fbt.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/libdtrace/dt_prov_fbt.c b/libdtrace/dt_prov_fbt.c > index f0910c371..eef93879d 100644 > --- a/libdtrace/dt_prov_fbt.c > +++ b/libdtrace/dt_prov_fbt.c > @@ -75,7 +75,7 @@ static int populate(dtrace_hdl_t *dtp) > dtrace_syminfo_t sip; > dtrace_probedesc_t pd; > > - dt_fbt = BPF_HAS(dtp, BPF_FEAT_FENTRY) ? dt_fbt_kprobe : dt_fbt_kprobe; > + dt_fbt = BPF_HAS(dtp, BPF_FEAT_FENTRY) ? dt_fbt_fprobe : dt_fbt_kprobe; > > prv = dt_provider_create(dtp, prvname, &dt_fbt, &pattr, NULL); > if (prv == NULL) > -- > 2.43.5 > > > _______________________________________________ > DTrace-devel mailing list > DTrace-devel at oss.oracle.com > https://oss.oracle.com/mailman/listinfo/dtrace-devel From eugene.loh at oracle.com Thu Feb 20 04:43:49 2025 From: eugene.loh at oracle.com (eugene.loh at oracle.com) Date: Wed, 19 Feb 2025 23:43:49 -0500 Subject: [DTrace-devel] [PATCH 1/2] Clarify how the usdt_prids key is stored on the BPF stack Message-ID: <20250220044350.14953-1-eugene.loh@oracle.com> From: Eugene Loh While one can access the BPF stack relative to %r9, the whole point of DT_TRAMP_SP_SLOT(0) is to make trampoline code more readable. So use it. Signed-off-by: Eugene Loh --- libdtrace/dt_prov_uprobe.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/libdtrace/dt_prov_uprobe.c b/libdtrace/dt_prov_uprobe.c index 5d9f74244..f1323cc31 100644 --- a/libdtrace/dt_prov_uprobe.c +++ b/libdtrace/dt_prov_uprobe.c @@ -1015,22 +1015,15 @@ static int trampoline(dt_pcb_t *pcb, uint_t exitlbl) emit(dlp, BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32)); /* - * Look up in the BPF 'usdt_prids' map. Space for the look-up key - * will be used on the BPF stack: - * - * offset value - * - * -sizeof(usdt_prids_map_key_t) pid (in %r0) - * - * -sizeof(usdt_prids_map_key_t) + sizeof(pid_t) - * == - * -sizeof(dtrace_id_t) underlying-probe prid + * Look up in the BPF 'usdt_prids' map. The key should fit into + * trampoline stack slot 0. */ - emit(dlp, BPF_STORE(BPF_W, BPF_REG_9, (int)(-sizeof(usdt_prids_map_key_t)), BPF_REG_0)); - emit(dlp, BPF_STORE_IMM(BPF_W, BPF_REG_9, (int)(-sizeof(dtrace_id_t)), uprp->desc->id)); + 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) + 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_9)); - emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, (int)(-sizeof(usdt_prids_map_key_t)))); + 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))); emit(dlp, BPF_CALL_HELPER(BPF_FUNC_map_lookup_elem)); emit(dlp, BPF_BRANCH_IMM(BPF_JEQ, BPF_REG_0, 0, lbl_exit)); -- 2.43.5 From eugene.loh at oracle.com Thu Feb 20 04:43:50 2025 From: eugene.loh at oracle.com (eugene.loh at oracle.com) Date: Wed, 19 Feb 2025 23:43:50 -0500 Subject: [DTrace-devel] [PATCH 2/2] Extend the USDT bit mask to multiple words In-Reply-To: <20250220044350.14953-1-eugene.loh@oracle.com> References: <20250220044350.14953-1-eugene.loh@oracle.com> Message-ID: <20250220044350.14953-2-eugene.loh@oracle.com> From: Eugene Loh 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 extern int usdt_prids_map_val_extra_bytes; to denote how many extra bytes will be needed for the extended mask. This value is computed by usdt_prids_map_val_extra_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 --- libdtrace/dt_bpf.c | 6 +- libdtrace/dt_bpf_maps.h | 3 +- libdtrace/dt_prov_uprobe.c | 81 ++++++++--- .../usdt/tst.many_probe_descriptions.r | 1 + .../usdt/tst.many_probe_descriptions.sh | 64 +++++++++ .../usdt/tst.many_probe_descriptions2.r | 1 + .../usdt/tst.many_probe_descriptions2.sh | 127 ++++++++++++++++++ 7 files changed, 260 insertions(+), 23 deletions(-) create mode 100644 test/unittest/usdt/tst.many_probe_descriptions.r create mode 100755 test/unittest/usdt/tst.many_probe_descriptions.sh create mode 100644 test/unittest/usdt/tst.many_probe_descriptions2.r create mode 100755 test/unittest/usdt/tst.many_probe_descriptions2.sh diff --git a/libdtrace/dt_bpf.c b/libdtrace/dt_bpf.c index 662fd81a4..1ed9376ea 100644 --- a/libdtrace/dt_bpf.c +++ b/libdtrace/dt_bpf.c @@ -940,6 +940,7 @@ gmap_create_probes(dtrace_hdl_t *dtp) return 0; } +void usdt_prids_map_val_extra_bytes_init(dtrace_hdl_t *dtp); /* * Create the 'usdt_names' and 'usdt_prids' BPF maps. * @@ -965,8 +966,11 @@ gmap_create_usdt(dtrace_hdl_t *dtp) if (dtp->dt_usdt_namesmap_fd == -1) return -1; + usdt_prids_map_val_extra_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) + usdt_prids_map_val_extra_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..ba17d8942 100644 --- a/libdtrace/dt_bpf_maps.h +++ b/libdtrace/dt_bpf_maps.h @@ -48,8 +48,9 @@ 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[1]; } usdt_prids_map_val_t; +extern int usdt_prids_map_val_extra_bytes; #ifdef __cplusplus } diff --git a/libdtrace/dt_prov_uprobe.c b/libdtrace/dt_prov_uprobe.c index f1323cc31..2a5b0ce91 100644 --- a/libdtrace/dt_prov_uprobe.c +++ b/libdtrace/dt_prov_uprobe.c @@ -76,6 +76,8 @@ typedef struct list_key { usdt_prids_map_key_t key; } list_key_t; +int usdt_prids_map_val_extra_bytes; + static const dtrace_pattr_t pattr = { { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, @@ -175,7 +177,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) + usdt_prids_map_val_extra_bytes); list_key_t keys_to_delete, *elem, *elem_next; dt_probe_t *prp, *prp_next; @@ -190,7 +192,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. */ @@ -203,7 +205,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. @@ -224,7 +226,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 @@ -346,6 +348,33 @@ ignore_clause(dtrace_hdl_t *dtp, int n, const dt_probe_t *uprp) return 0; } +void usdt_prids_map_val_extra_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 extra bytes are needed. */ + if (n > 1) + usdt_prids_map_val_extra_bytes = (n - 1) * w; + else + usdt_prids_map_val_extra_bytes = 0; +} + static int add_probe_uprobe(dtrace_hdl_t *dtp, dt_probe_t *prp) { dtrace_difo_t *dp; @@ -416,6 +445,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; @@ -451,11 +481,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) + usdt_prids_map_val_extra_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; /* @@ -473,11 +503,15 @@ static int add_probe_usdt(dtrace_hdl_t *dtp, dt_probe_t *prp) 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) && @@ -492,11 +526,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; @@ -922,7 +956,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); @@ -1020,7 +1054,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) + 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))); @@ -1054,27 +1089,30 @@ 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)); /* * Hold the bit mask in %r6 between clause calls. */ - for (n = 0; n < dtp->dt_stmt_nextid; n++) { + 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)); @@ -1102,6 +1140,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.many_probe_descriptions.r b/test/unittest/usdt/tst.many_probe_descriptions.r new file mode 100644 index 000000000..2e9ba477f --- /dev/null +++ b/test/unittest/usdt/tst.many_probe_descriptions.r @@ -0,0 +1 @@ +success diff --git a/test/unittest/usdt/tst.many_probe_descriptions.sh b/test/unittest/usdt/tst.many_probe_descriptions.sh new file mode 100755 index 000000000..92a61d5b7 --- /dev/null +++ b/test/unittest/usdt/tst.many_probe_descriptions.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.many_probe_descriptions2.r b/test/unittest/usdt/tst.many_probe_descriptions2.r new file mode 100644 index 000000000..2e9ba477f --- /dev/null +++ b/test/unittest/usdt/tst.many_probe_descriptions2.r @@ -0,0 +1 @@ +success diff --git a/test/unittest/usdt/tst.many_probe_descriptions2.sh b/test/unittest/usdt/tst.many_probe_descriptions2.sh new file mode 100755 index 000000000..cc8821c6e --- /dev/null +++ b/test/unittest/usdt/tst.many_probe_descriptions2.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 +gcc $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 +gcc $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 From eugene.loh at oracle.com Thu Feb 20 23:27:30 2025 From: eugene.loh at oracle.com (eugene.loh at oracle.com) Date: Thu, 20 Feb 2025 18:27:30 -0500 Subject: [DTrace-devel] [PATCH] Fix format specifier in dtprobed.c Message-ID: <20250220232730.25029-1-eugene.loh@oracle.com> From: Eugene Loh The format specifier is %i but nprobes is size_t. Some compilers issue warnings. Change the format specifier to match the type. Signed-off-by: Eugene Loh --- dtprobed/dtprobed.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dtprobed/dtprobed.c b/dtprobed/dtprobed.c index 7857b3200..5f260f0a4 100644 --- a/dtprobed/dtprobed.c +++ b/dtprobed/dtprobed.c @@ -787,7 +787,7 @@ process_dof(pid_t pid, int out, int in, dev_t dev, ino_t inum, dev_t exec_dev, if (dof_stash_push_parsed(&accum, provider) < 0) goto oom; - fuse_log(FUSE_LOG_DEBUG, "Parser read: provider %s, %i probes\n", + fuse_log(FUSE_LOG_DEBUG, "Parser read: provider %s, %li probes\n", provider->provider.name, provider->provider.nprobes); for (i = 0; i < provider->provider.nprobes; i++) { -- 2.43.5 From eugene.loh at oracle.com Fri Feb 21 00:08:31 2025 From: eugene.loh at oracle.com (eugene.loh at oracle.com) Date: Thu, 20 Feb 2025 19:08:31 -0500 Subject: [DTrace-devel] [PATCH v3 6/6] Add support for pid function "-" with absolute offset Message-ID: <20250221000831.25523-1-eugene.loh@oracle.com> From: Eugene Loh The pid providers allow users to specify a probe function "-", meaning that the probe name gives an absolute offset. Signed-off-by: Eugene Loh --- include/dtrace/pid.h | 3 +- libdtrace/dt_pid.c | 79 ++++++++++++------ libdtrace/dt_prov_uprobe.c | 38 ++++++--- test/unittest/pid/tst.dash.r | 1 + test/unittest/pid/tst.dash.sh | 119 ++++++++++++++++++++++++++++ test/unittest/usdt/tst.pidprobes.sh | 21 ++++- 6 files changed, 224 insertions(+), 37 deletions(-) create mode 100644 test/unittest/pid/tst.dash.r create mode 100755 test/unittest/pid/tst.dash.sh diff --git a/include/dtrace/pid.h b/include/dtrace/pid.h index c53e60047..12934500a 100644 --- a/include/dtrace/pid.h +++ b/include/dtrace/pid.h @@ -2,7 +2,7 @@ * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. * - * Copyright (c) 2009, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. */ /* @@ -22,6 +22,7 @@ typedef enum pid_probetype { DTPPT_ENTRY, DTPPT_RETURN, DTPPT_OFFSETS, + DTPPT_ABSOFFSETS, DTPPT_USDT, DTPPT_IS_ENABLED } pid_probetype_t; diff --git a/libdtrace/dt_pid.c b/libdtrace/dt_pid.c index 11b964561..76608f690 100644 --- a/libdtrace/dt_pid.c +++ b/libdtrace/dt_pid.c @@ -1,6 +1,6 @@ /* * Oracle Linux DTrace. - * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 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. */ @@ -155,7 +155,6 @@ dt_pid_per_sym(dt_pid_probe_t *pp, const GElf_Sym *symp, const char *func) uint_t nmatches = 0; ulong_t sz; int glob, rc = 0; - int isdash = strcmp("-", func) == 0; pid_t pid; /* @@ -183,7 +182,46 @@ dt_pid_per_sym(dt_pid_probe_t *pp, const GElf_Sym *symp, const char *func) psp->pps_nameoff = 0; psp->pps_off = symp->st_value - pp->dpp_vaddr; - if (!isdash && gmatch("return", pp->dpp_name)) { + /* + * The special function "-" means the probe name is an absolute + * virtual address. + */ + if (strcmp("-", func) == 0) { + char *end; + GElf_Sym sym; + + off = strtoull(pp->dpp_name, &end, 16); + if (*end != '\0') { + rc = dt_pid_error(dtp, pcb, dpr, D_PROC_NAME, + "'%s' is an invalid probe name", + pp->dpp_name); + goto out; + } + + psp->pps_nameoff = off; + + if (dt_Plookup_by_addr(dtp, pid, off, (const char **)&psp->pps_fun, &sym)) { + rc = dt_pid_error(dtp, pcb, dpr, D_PROC_NAME, + "failed to lookup 0x%lx in module '%s'", off, pp->dpp_mod); + if (psp->pps_fun != func && psp->pps_fun != NULL) + free(psp->pps_fun); + goto out; + } + + psp->pps_prb = (char*)pp->dpp_name; + psp->pps_off = off - pp->dpp_vaddr; + + if (dt_pid_create_one_probe(pp->dpp_pr, dtp, psp, DTPPT_ABSOFFSETS) < 0) + rc = dt_pid_error(dtp, pcb, dpr, D_PROC_CREATEFAIL, + "failed to create probes at '%s+0x%llx': %s", + func, (unsigned long long)off, dtrace_errmsg(dtp, dtrace_errno(dtp))); + else + pp->dpp_nmatches++; + free(psp->pps_fun); + goto out; + } + + if (gmatch("return", pp->dpp_name)) { if (dt_pid_create_one_probe(pp->dpp_pr, dtp, psp, DTPPT_RETURN) < 0) { rc = dt_pid_error( dtp, pcb, dpr, D_PROC_CREATEFAIL, @@ -195,7 +233,7 @@ dt_pid_per_sym(dt_pid_probe_t *pp, const GElf_Sym *symp, const char *func) nmatches++; } - if (!isdash && gmatch("entry", pp->dpp_name)) { + if (gmatch("entry", pp->dpp_name)) { if (dt_pid_create_one_probe(pp->dpp_pr, dtp, psp, DTPPT_ENTRY) < 0) { rc = dt_pid_error( dtp, pcb, dpr, D_PROC_CREATEFAIL, @@ -240,7 +278,7 @@ dt_pid_per_sym(dt_pid_probe_t *pp, const GElf_Sym *symp, const char *func) } nmatches++; - } else if (glob && !isdash) { + } else if (glob) { #if defined(__amd64) /* * We need to step through the instructions to find their @@ -449,31 +487,25 @@ dt_pid_per_mod(void *arg, const prmap_t *pmp, const char *obj) else pp->dpp_obj++; + /* + * If it is the special function "-", cut to dt_pid_per_sym() now. + */ + if (strcmp("-", pp->dpp_func) == 0) + return dt_pid_per_sym(pp, &sym, pp->dpp_func); + /* * If pp->dpp_func contains any globbing meta-characters, we need * to iterate over the symbol table and compare each function name * against the pattern. */ if (!strisglob(pp->dpp_func)) { - /* - * If we fail to lookup the symbol, try interpreting the - * function as the special "-" function that indicates that the - * probe name should be interpreted as a absolute virtual - * address. If that fails and we were matching a specific - * function in a specific module, report the error, otherwise - * just fail silently in the hopes that some other object will - * contain the desired symbol. + /* If we are matching a specific function in a specific module, + * report the error, otherwise just fail silently in the hopes + * that some other object will contain the desired symbol. */ if (dt_Pxlookup_by_name(dtp, pid, pp->dpp_lmid, obj, pp->dpp_func, &sym, NULL) != 0) { - if (strcmp("-", pp->dpp_func) == 0) { - sym.st_name = 0; - sym.st_info = - GELF_ST_INFO(STB_LOCAL, STT_FUNC); - sym.st_other = 0; - sym.st_value = 0; - sym.st_size = Pelf64(pp->dpp_pr) ? -1ULL : -1U; - } else if (!strisglob(pp->dpp_mod)) { + if (!strisglob(pp->dpp_mod)) { return dt_pid_error( dtp, pcb, dpr, D_PROC_FUNC, "failed to lookup '%s' in module '%s'", @@ -647,9 +679,10 @@ dt_pid_create_pid_probes_proc(dtrace_probedesc_t *pdp, dtrace_hdl_t *dtp, if (strcmp(pp.dpp_func, "-") == 0) { const prmap_t *aout, *pmp; - if (pdp->mod[0] == '\0') { - pp.dpp_mod = pdp->mod; + if (strcmp(pp.dpp_mod, "*") == 0) { + /* Tolerate two glob cases: "" and "*". */ pdp->mod = "a.out"; + pp.dpp_mod = pdp->mod; } else if (strisglob(pp.dpp_mod) || (aout = dt_Pname_to_map(dtp, pid, "a.out")) == NULL || (pmp = dt_Pname_to_map(dtp, pid, pp.dpp_mod)) == NULL || diff --git a/libdtrace/dt_prov_uprobe.c b/libdtrace/dt_prov_uprobe.c index 11d595898..e96479963 100644 --- a/libdtrace/dt_prov_uprobe.c +++ b/libdtrace/dt_prov_uprobe.c @@ -1,6 +1,6 @@ /* * Oracle Linux DTrace. - * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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. * @@ -55,7 +55,8 @@ static const char prvname[] = "uprobe"; typedef struct dt_uprobe { dev_t dev; ino_t inum; - char *fn; + char *fn; /* object full file name */ + char *func; /* function */ uint64_t off; int flags; tp_probe_t *tp; @@ -128,6 +129,7 @@ static void probe_destroy_underlying(dtrace_hdl_t *dtp, void *datap) dt_tp_destroy(dtp, tpp); free_probe_list(dtp, dt_list_next(&upp->probes)); dt_free(dtp, upp->fn); + dt_free(dtp, upp->func); dt_free(dtp, upp->args); dt_free(dtp, upp->argvbuf); dt_free(dtp, upp); @@ -333,9 +335,13 @@ ignore_clause(dtrace_hdl_t *dtp, int n, const dt_probe_t *uprp) */ /* We know what function we're in. It must match the probe description (unless "-"). */ - if (strcmp(pdp->fun, "-") != 0 && - !dt_gmatch(uprp->desc->fun, pdp->fun)) - return 1; + if (strcmp(pdp->fun, "-") != 0) { + dt_uprobe_t *upp = uprp->prv_data; + + assert(upp->func); // never a return probe + if (!dt_gmatch(upp->func, pdp->fun)) + return 1; + } return 0; } @@ -623,11 +629,11 @@ static dt_probe_t *create_underlying(dtrace_hdl_t *dtp, * * The probe description for non-return probes is: * - * uprobe:_:: + * uprobe:_:: * * The probe description for return probes is: * - * uprobe:_::return + * uprobe:_::return */ snprintf(mod, sizeof(mod), "%lx_%lx", psp->pps_dev, psp->pps_inum); @@ -638,6 +644,7 @@ static dt_probe_t *create_underlying(dtrace_hdl_t *dtp, case DTPPT_IS_ENABLED: case DTPPT_ENTRY: case DTPPT_OFFSETS: + case DTPPT_ABSOFFSETS: case DTPPT_USDT: snprintf(prb, sizeof(prb), "%lx", psp->pps_off); break; @@ -649,7 +656,7 @@ static dt_probe_t *create_underlying(dtrace_hdl_t *dtp, pd.id = DTRACE_IDNONE; pd.prv = prvname; pd.mod = mod; - pd.fun = psp->pps_fun; + pd.fun = ""; pd.prb = prb; dt_dprintf("Providing underlying probe %s:%s:%s:%s @ %lx\n", psp->pps_prv, @@ -672,6 +679,7 @@ static dt_probe_t *create_underlying(dtrace_hdl_t *dtp, upp->inum = psp->pps_inum; upp->off = psp->pps_off; upp->fn = strdup(psp->pps_fn); + upp->func = NULL; upp->tp = dt_tp_alloc(dtp); if (upp->tp == NULL) goto fail; @@ -692,6 +700,17 @@ static dt_probe_t *create_underlying(dtrace_hdl_t *dtp, goto fail; } + /* + * The underlying probe should have the same function for all + * overlying probes unless it's a return probe. + */ + if (psp->pps_type != DTPPT_RETURN) { + if (upp->func == NULL) + upp->func = strdup(psp->pps_fun); + else + assert(strcmp(upp->func, psp->pps_fun) == 0); + } + if (populate_args(dtp, psp, upp) < 0) goto fail; @@ -735,7 +754,7 @@ static int provide_probe(dtrace_hdl_t *dtp, const pid_probespec_t *psp, pd.id = DTRACE_IDNONE; pd.prv = prv; pd.mod = psp->pps_mod; - pd.fun = psp->pps_fun; + pd.fun = (psp->pps_type == DTPPT_ABSOFFSETS) ? "-" : psp->pps_fun; pd.prb = prb; /* Get (or create) the provider for the PID of the probe. */ @@ -823,6 +842,7 @@ static int provide_pid_probe(dtrace_hdl_t *dtp, const pid_probespec_t *psp) strcpy(prb, "return"); break; case DTPPT_OFFSETS: + case DTPPT_ABSOFFSETS: snprintf(prb, sizeof(prb), "%lx", psp->pps_nameoff); break; default: diff --git a/test/unittest/pid/tst.dash.r b/test/unittest/pid/tst.dash.r new file mode 100644 index 000000000..2e9ba477f --- /dev/null +++ b/test/unittest/pid/tst.dash.r @@ -0,0 +1 @@ +success diff --git a/test/unittest/pid/tst.dash.sh b/test/unittest/pid/tst.dash.sh new file mode 100755 index 000000000..f364e3e3c --- /dev/null +++ b/test/unittest/pid/tst.dash.sh @@ -0,0 +1,119 @@ +#!/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. +# +# @@timeout: 80 + +dtrace=$1 + +DIRNAME=$tmpdir/pid-dash.$$.$RANDOM +mkdir $DIRNAME +cd $DIRNAME + +# Make trigger program. + +cat << EOF > main.c +int foo0(int i) { + int j, k; + + j = i ^ 1; k = j ^ 1; i = k ^ 1; + j = i ^ 1; k = j ^ 1; i = k ^ 1; + j = i ^ 1; k = j ^ 1; i = k ^ 1; + + return i; +} +int foo1(int i) { + int j, k; + + j = i ^ 1; k = j ^ 1; i = k ^ 1; + j = i ^ 1; k = j ^ 1; i = k ^ 1; + j = i ^ 1; k = j ^ 1; i = k ^ 1; + + return i; +} +int foo2(int i) { + int j, k; + + j = i ^ 1; k = j ^ 1; i = k ^ 1; + j = i ^ 1; k = j ^ 1; i = k ^ 1; + j = i ^ 1; k = j ^ 1; i = k ^ 1; + + return i; +} +int main(int c, char **v) { + int i = 0; + + i = foo0(i) ^ i; + i = foo1(i) ^ i; + i = foo2(i) ^ i; + + return i; +} +EOF + +gcc main.c +if [ $? -ne 0 ]; then + echo ERROR compile + exit 1 +fi + +# Loop over functions in the program. + +for func in foo0 foo1 foo2 main; do + # For each function, get the absolute and relative + # (to the function) address of some instruction in + # the function. + read ABS REL <<< `objdump -d a.out | awk ' + # Look for the function. + /^[0-9a-f]* <'$func'>:$/ { + + # Get the first instruction and note the base address of the function. + getline; sub(":", ""); base = strtonum("0x"$1); + + # Get the next instruction. + getline; + + # Get the next instruction. Note its PC. + getline; sub(":", ""); pc = strtonum("0x"$1); + + # Print the address, both absolute and relative. + printf("%x %x\n", pc, pc - base); + exit(0); + }'` + + # Write the expected output to the compare file. + echo got $ABS $func:$REL >> dtrace.exp + echo got $ABS "-":$ABS >> dtrace.exp + + # Write the actual dtrace output to the output file. + # Specify the pid probe with both relative and absolute + # forms. + for probe in $func:$REL "-:$ABS"; do + $dtrace -c ./a.out -o dtrace.out -qn ' + pid$target:a.out:'$probe' + { printf("got %x %s:%s", uregs[R_PC], probefunc, + probename); }' + if [ $? -ne 0 ]; then + echo ERROR: dtrace + cat dtrace.out + exit 1 + fi + done +done + +# Check results. + +if ! diff -q dtrace.exp dtrace.out; then + echo ERROR: + echo "==== expected" + cat dtrace.exp + echo "==== actual" + cat dtrace.out + exit 1 +fi + +echo success +exit 0 diff --git a/test/unittest/usdt/tst.pidprobes.sh b/test/unittest/usdt/tst.pidprobes.sh index 54444d49b..0c75d7967 100755 --- a/test/unittest/usdt/tst.pidprobes.sh +++ b/test/unittest/usdt/tst.pidprobes.sh @@ -1,7 +1,7 @@ #!/bin/bash # # Oracle Linux DTrace. -# Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2024, 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. # @@ -121,7 +121,13 @@ fi pcs=`awk '{print strtonum("0x"$1)}' disasm_foo.txt` pc0=`echo $pcs | awk '{print $1}'` -# Run dtrace. +# Construct D script: add a pid$pid::-:$absoff probe for each PC in foo. + +for pc in $pcs; do + printf 'p*d$target::-:%x,\n' $pc >> pidprobes.d +done + +# Construct D script: add a glob for all pid and USDT pyramid probes in foo. cat >> pidprobes.d <<'EOF' p*d$target::foo: @@ -130,6 +136,8 @@ p*d$target::foo: } EOF +# Construct D script: add a glob for all USDT pyramid probes, dumping args. + if [[ -n $usdt ]]; then echo 'pyramid$target::foo: {' >> pidprobes.d @@ -141,9 +149,12 @@ if [[ -n $usdt ]]; then echo '}' >> pidprobes.d fi +# Run dtrace. + $dtrace $dt_flags -q -c ./main -o dtrace.out -s pidprobes.d > main.out2 if [ $? -ne 0 ]; then echo "failed to run dtrace" >&2 + cat pidprobes.d cat main.out2 cat dtrace.out exit 1 @@ -286,14 +297,16 @@ fi # - a blank line # - pid entry # - pid return -# - pid offset +# - pid offset (relative -- that is, pid$pid:main:foo:$reloff) +# - pid offset (absolute -- that is, pid$pid:main:-:$absoff) # - two USDT probes (ignore is-enabled probes) echo > dtrace.out.expected -printf "$pid pid$pid:main:foo:entry %x\n" $pc0 >> dtrace.out.expected +printf "$pid pid$pid:main:foo:entry %x\n" $pc0 >> dtrace.out.expected echo "$pid pid$pid:main:foo:return $pc_return" >> dtrace.out.expected for pc in $pcs; do printf "$pid pid$pid:main:foo:%x %x\n" $(($pc - $pc0)) $pc >> dtrace.out.expected + printf "$pid pid$pid:main:-:%x %x\n" $pc $pc >> dtrace.out.expected done echo $usdt_pcs | awk '{printf("'$pid' pyramid'$pid':main:foo:entry %x\n", $1);}' >> dtrace.out.expected echo $usdt_pcs | awk '{printf("'$pid' pyramid'$pid':main:foo:entry %x\n", $2);}' >> dtrace.out.expected -- 2.43.5 From noreply at github.com Fri Feb 21 04:14:27 2025 From: noreply at github.com (Kris Van Hees) Date: Thu, 20 Feb 2025 20:14:27 -0800 Subject: [DTrace-devel] [oracle/dtrace-utils] 595d1f: Remove unused dpp_pc and dpp_size Message-ID: Branch: refs/heads/devel Home: https://github.com/oracle/dtrace-utils Commit: 595d1fe2ddd93b7bb851e54957f8912f26e98d12 https://github.com/oracle/dtrace-utils/commit/595d1fe2ddd93b7bb851e54957f8912f26e98d12 Author: Eugene Loh Date: 2025-01-22 (Wed, 22 Jan 2025) Changed paths: M libdtrace/dt_pid.c Log Message: ----------- Remove unused dpp_pc and dpp_size Signed-off-by: Eugene Loh Reviewed-by: Kris Van Hees Commit: a97ded8aaafdd4cb6b434ff379de88482c211b91 https://github.com/oracle/dtrace-utils/commit/a97ded8aaafdd4cb6b434ff379de88482c211b91 Author: Eugene Loh Date: 2025-01-22 (Wed, 22 Jan 2025) Changed paths: M libdtrace/dt_pid.c Log Message: ----------- Simplify references to dtp Signed-off-by: Eugene Loh Reviewed-by: Kris Van Hees Commit: 788b3a3c0f4e8c167c79d05e3dae7d25b378b0cc https://github.com/oracle/dtrace-utils/commit/788b3a3c0f4e8c167c79d05e3dae7d25b378b0cc Author: Eugene Loh Date: 2025-01-22 (Wed, 22 Jan 2025) Changed paths: M libdtrace/dt_pid.c Log Message: ----------- Remove unused function arg Signed-off-by: Eugene Loh Reviewed-by: Kris Van Hees Commit: 22fb7502459d097acda12c0717f49f52d3ccdb02 https://github.com/oracle/dtrace-utils/commit/22fb7502459d097acda12c0717f49f52d3ccdb02 Author: Eugene Loh Date: 2025-01-22 (Wed, 22 Jan 2025) Changed paths: M test/triggers/usdt-tst-multiprovider-prov.d Log Message: ----------- test: Remove unneeded skip for trigger Signed-off-by: Eugene Loh Reviewed-by: Kris Van Hees Commit: e49316248a94216d6fd06eaa0dc8f6769d40c4e7 https://github.com/oracle/dtrace-utils/commit/e49316248a94216d6fd06eaa0dc8f6769d40c4e7 Author: Eugene Loh Date: 2025-01-22 (Wed, 22 Jan 2025) Changed paths: M test/triggers/Build A test/triggers/usdt-tst-multiprov-dupprobe-prov.d A test/triggers/usdt-tst-multiprov-dupprobe.c A test/unittest/usdt/tst.multiprov-dupprobe.r A test/unittest/usdt/tst.multiprov-dupprobe.r.p A test/unittest/usdt/tst.multiprov-dupprobe.sh M test/unittest/usdt/tst.multiprovider.r M test/unittest/usdt/tst.multiprovider.r.p M test/unittest/usdt/tst.multiprovider.sh Log Message: ----------- test: Embellish the USDT multiprovider test Add a probe to two providers, each probe with the same name but different arguments. Do so in a new, separate test since the older test used to trigger some crashes, and we would like to continue to catch such problems. Add -v to these probe-list tests to check attributes and arguments. Signed-off-by: Eugene Loh Reviewed-by: Kris Van Hees Commit: 03381a3f7b64d26a5249cd589197a50a1ae43a2c https://github.com/oracle/dtrace-utils/commit/03381a3f7b64d26a5249cd589197a50a1ae43a2c Author: Eugene Loh Date: 2025-01-22 (Wed, 22 Jan 2025) Changed paths: A test/unittest/usdt/tst.multiprov-dupprobe-fire.r A test/unittest/usdt/tst.multiprov-dupprobe-fire.r.p A test/unittest/usdt/tst.multiprov-dupprobe-fire.sh A test/unittest/usdt/tst.multiprovider-fire.r A test/unittest/usdt/tst.multiprovider-fire.r.p A test/unittest/usdt/tst.multiprovider-fire.sh Log Message: ----------- test: Add USDT multiprovider tests that fire probes The multiprovider tests simply list probes. Add new tests that fire probes. Signed-off-by: Eugene Loh Reviewed-by: Kris Van Hees Commit: 71317c48c479dd6be1fc7814b601aad84d76b59a https://github.com/oracle/dtrace-utils/commit/71317c48c479dd6be1fc7814b601aad84d76b59a Author: Eugene Loh Date: 2025-01-22 (Wed, 22 Jan 2025) Changed paths: A test/unittest/usdt/tst.multiprov-dupprobe-shlibs.r A test/unittest/usdt/tst.multiprov-dupprobe-shlibs.r.p A test/unittest/usdt/tst.multiprov-dupprobe-shlibs.sh Log Message: ----------- test: USDT probes with multiple providers and shared libs A simple executable links in two shared libraries. There is a different USDT provider for the executable and for each of the two libraries. Each provider has probes named the same as for the other providers, but the probes differ in arguments. Run dtrace on the executable. The D script first dumps the target pid for later post-processing. All USDT probes are traced and their argument values tested. Signed-off-by: Eugene Loh Reviewed-by: Kris Van Hees Commit: 8bd26415bc78f1c6f8ccf6e629ad677aeba0e534 https://github.com/oracle/dtrace-utils/commit/8bd26415bc78f1c6f8ccf6e629ad677aeba0e534 Author: Kris Van Hees Date: 2025-01-31 (Fri, 31 Jan 2025) Changed paths: M bpf/get_bvar.c M libdtrace/dt_cg.c M libdtrace/dt_dis.c M test/demo/builtin/ipl.d M test/demo/builtin/vtimestamp.d M test/demo/io/iocpu.d M test/unittest/buffering/tst.cputime.sh M test/unittest/disasm/tst.ann-bvar.r M test/unittest/disasm/tst.ann-bvar.sh M test/unittest/disasm/tst.vartab-bvar.r M test/unittest/disasm/tst.vartab-bvar.sh Log Message: ----------- bpf: separate bvar implementation into separate functions The handling of builtin variables was done with a single big function that therefore got linked into every single BPF program (twice if the builtin variable might raise a fault). Providing separate functions for most builtin variables reduces BPF program size significantly. dtrace -n 'BEGIN { trace(arg0); exit(0); ' Before this change: BEGIN = 1195 BPF instructions END = 739 BPF instructions dtrace -n 'BEGIN { trace(arg0); trace(arg1); exit(0); ' Before this change: BEGIN = 1205 BPF instructions END = 748 BPF instructions dtrace -n 'BEGIN { trace(execname); exit(0); ' Before this change: BEGIN = 1209 BPF instructions END = 753 BPF instructions dtrace -n 'BEGIN { trace(probename); exit(0); ' Before this change: BEGIN = 1209 BPF instructions END = 787 BPF instructions dtrace -n 'BEGIN { trace(ppid); exit(0); ' Before this change: BEGIN = 1195 BPF instructions END = 779 BPF instructions This also reduces pressure on the BPF verifies. Signed-off-by: Kris Van Hees Reviewed-by: Eugene Loh Commit: c768cb785f8648cf41f9490c1578595e969c06d6 https://github.com/oracle/dtrace-utils/commit/c768cb785f8648cf41f9490c1578595e969c06d6 Author: Kris Van Hees Date: 2025-01-31 (Fri, 31 Jan 2025) Changed paths: M include/dtrace/faults_defines.h Log Message: ----------- include: remove unnecessary include of universal.h Signed-off-by: Kris Van Hees Reviewed-by: Eugene Loh Commit: 24fc774afb1f00b2422ee9398d328ca4c6732ad9 https://github.com/oracle/dtrace-utils/commit/24fc774afb1f00b2422ee9398d328ca4c6732ad9 Author: Kris Van Hees Date: 2025-01-31 (Fri, 31 Jan 2025) Changed paths: M include/port.h M libdtrace/dt_as.c M libdtrace/dt_cc.c M libdtrace/dt_dis.c Log Message: ----------- bpf: implement additional relocation types The relocation names we were using are not entirely correct in view of the "official" names. GCC is also generating code that uses some of the relocations that were not implemented yet. Signed-off-by: Kris Van Hees Reviewed-by: Eugene Loh Commit: 3a551bfd7d5875463553619d0fbbe6d3c91f6d8c https://github.com/oracle/dtrace-utils/commit/3a551bfd7d5875463553619d0fbbe6d3c91f6d8c Author: Kris Van Hees Date: 2025-02-07 (Fri, 07 Feb 2025) Changed paths: M include/dtrace/dif.h M libdtrace/dt_cg.c M libdtrace/dt_consume.c M libdtrace/dt_parser.c A test/unittest/actions/trace/tst.array-char-multi-nul.d A test/unittest/actions/trace/tst.array-char-multi-nul.r A test/unittest/actions/trace/tst.array-char-str-multi-nul.d A test/unittest/actions/trace/tst.array-char-str-multi-nul.r A test/unittest/actions/trace/tst.array-char-str-no-nul.d A test/unittest/actions/trace/tst.array-char-str-no-nul.r A test/unittest/actions/trace/tst.array-char-str.d A test/unittest/actions/trace/tst.array-char-str.r A test/unittest/actions/trace/tst.array-char-unprintable.d A test/unittest/actions/trace/tst.array-char-unprintable.r M test/unittest/actions/trace/tst.array.d M test/unittest/actions/trace/tst.array.r R test/unittest/actions/trace/tst.array.r.p Log Message: ----------- trace: fix char-array handling The special handling of strings in the consumer's trace implementation did not correctly account for char-arrays that do not necessarily are intended to hold string data. The trace action is documented to print data as an ASCII string if it can be represented as such, using the heuristics implemented in dt_print_bytes(). The implementation for trace on both producer and consumer side was using the data alignment as a marker to determine whether data should be printed as a string (alignment == 1) or not. The real alignment of the data should be used. The dtrace_diftype struct has its dtdt_pad member renamed as dtdt_align. Added several new test cases to cover various possibilities. Signed-off-by: Kris Van Hees Reviewed-by: Eugene Loh Commit: 40148f2055039f62448ff95e5c23619abac5e4f6 https://github.com/oracle/dtrace-utils/commit/40148f2055039f62448ff95e5c23619abac5e4f6 Author: Kris Van Hees Date: 2025-02-20 (Thu, 20 Feb 2025) Changed paths: M bpf/Build A bpf/bvar_execargs.S M dlibs/aarch64/5.11/procfs.d M dlibs/aarch64/5.12/procfs.d M dlibs/aarch64/5.14/procfs.d M dlibs/aarch64/5.16/procfs.d M dlibs/aarch64/5.2/procfs.d M dlibs/aarch64/5.6/procfs.d M dlibs/aarch64/6.1/procfs.d M dlibs/aarch64/6.10/procfs.d M dlibs/x86_64/5.11/procfs.d M dlibs/x86_64/5.12/procfs.d M dlibs/x86_64/5.14/procfs.d M dlibs/x86_64/5.16/procfs.d M dlibs/x86_64/5.2/procfs.d M dlibs/x86_64/5.6/procfs.d M dlibs/x86_64/6.1/procfs.d M dlibs/x86_64/6.10/procfs.d M include/dtrace/dif_defines.h M libdtrace/dt_bpf.h M libdtrace/dt_cc.c M libdtrace/dt_cg.c M libdtrace/dt_dlibs.c M libdtrace/dt_open.c M libdtrace/procfs.d.in M test/unittest/builtinvar/tst.psinfo-bug21974606.d M test/unittest/builtinvar/tst.psinfo-bug22561297.d M test/unittest/builtinvar/tst.psinfo.d M test/unittest/builtinvar/tst.psinfo1.d A test/unittest/proc/tst.pr_psargs.d A test/unittest/proc/tst.pr_psargs.r A test/unittest/proc/tst.pr_psargs_other_task.d A test/unittest/proc/tst.pr_psargs_other_task.r A test/unittest/variables/bvar/tst.execargs.d Log Message: ----------- procfs: implement execargs for pr_psargs translator support Implement a new execargs built-in variable to provide the ps-style argument string for the current task. It is used in the psinfo translator for the pr_psargs member. Due to BPF limitations, it is only possible to retrieve this information for the current task. The data is stored in the task's address space, and the only BPF helper that can access data in another task's address space is not allowed to be used in non-sleepable programs (such as tracing programs). Signed-off-by: Kris Van Hees Reviewed-by: Eugene Loh Compare: https://github.com/oracle/dtrace-utils/compare/94e53e27cbb4...40148f205503 To unsubscribe from these emails, change your notification settings at https://github.com/oracle/dtrace-utils/settings/notifications From noreply at github.com Fri Feb 21 05:11:23 2025 From: noreply at github.com (euloh) Date: Thu, 20 Feb 2025 21:11:23 -0800 Subject: [DTrace-devel] [oracle/dtrace-utils] a6b626: Fix fprobe/kprobe selection Message-ID: Branch: refs/heads/devel Home: https://github.com/oracle/dtrace-utils Commit: a6b626a8923616dc6581d17f4769c429f2ca0d67 https://github.com/oracle/dtrace-utils/commit/a6b626a8923616dc6581d17f4769c429f2ca0d67 Author: Eugene Loh Date: 2025-02-21 (Fri, 21 Feb 2025) Changed paths: M libdtrace/dt_prov_fbt.c Log Message: ----------- Fix fprobe/kprobe selection In commit 2a09b3bea504 ("fbt: clean up fprobe/kprobe support"), libdtrace/dt_prov_fbt.c populate() has this change: - impl = BPF_HAS(dtp, BPF_FEAT_FENTRY) ? &dt_fbt_fprobe : &dt_fbt_kprobe; + dt_fbt = BPF_HAS(dtp, BPF_FEAT_FENTRY) ? dt_fbt_kprobe : dt_fbt_kprobe; That is, regardless of the BPF_HAS() test, dt_fbt_kprobe is chosen. Restore the choice to pick up dt_fbt_fprobe when appropriate. It is hard to devise a test for this unique problem. Signed-off-by: Eugene Loh Reviewed-by: Kris Van Hees To unsubscribe from these emails, change your notification settings at https://github.com/oracle/dtrace-utils/settings/notifications From eugene.loh at oracle.com Fri Feb 21 20:09:24 2025 From: eugene.loh at oracle.com (eugene.loh at oracle.com) Date: Fri, 21 Feb 2025 15:09:24 -0500 Subject: [DTrace-devel] [PATCH] Use DT_TRAMP_SP_SLOT() for BPF stack scratch space in trampoline Message-ID: <20250221200924.4445-1-eugene.loh@oracle.com> From: Eugene Loh We might as well get this code right, even if this "fix" is arguably irrelevant for two reasons: *) The offset just so happens to be -96 before and after the change anyhow, just by coincidence. *) The fix is on a code path that is not currently in use. Signed-off-by: Eugene Loh --- libdtrace/dt_cg.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c index 846f0fe2e..cc64a3a13 100644 --- a/libdtrace/dt_cg.c +++ b/libdtrace/dt_cg.c @@ -594,11 +594,11 @@ dt_cg_tramp_copy_pc_from_regs(dt_pcb_t *pcb) /* test just a single byte */ emit(dlp, BPF_MOV_IMM(BPF_REG_2, 1)); - /* safe to write to FP+DT_STK_SP_BASE, which becomes the clause stack */ + /* write to scratch space */ emit(dlp, BPF_MOV_REG(BPF_REG_1, BPF_REG_FP)); - emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, DT_STK_SP_BASE)); + emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, DT_TRAMP_SP_SLOT(0))); - /* bpf_probe_read_kernel(%fp + DT_STK_SP, 1, PC) */ + /* bpf_probe_read_kernel(%fp + DT_TRAMP_SP_SLOT(0), 1, PC) */ dt_regset_xalloc(drp, BPF_REG_0); emit(dlp, BPF_CALL_HELPER(BPF_FUNC_probe_read_kernel)); -- 2.43.5 From kris.van.hees at oracle.com Fri Feb 21 20:20:27 2025 From: kris.van.hees at oracle.com (Kris Van Hees) Date: Fri, 21 Feb 2025 15:20:27 -0500 Subject: [DTrace-devel] [PATCH] Use DT_TRAMP_SP_SLOT() for BPF stack scratch space in trampoline In-Reply-To: <20250221200924.4445-1-eugene.loh@oracle.com> References: <20250221200924.4445-1-eugene.loh@oracle.com> Message-ID: On Fri, Feb 21, 2025 at 03:09:24PM -0500, eugene.loh--- via DTrace-devel wrote: > From: Eugene Loh > > We might as well get this code right, even if this "fix" is > arguably irrelevant for two reasons: > > *) The offset just so happens to be -96 before and after the > change anyhow, just by coincidence. > > *) The fix is on a code path that is not currently in use. > > Signed-off-by: Eugene Loh Reviewed-by: Kris Van Hees > --- > libdtrace/dt_cg.c | 6 +++--- > 1 file changed, 3 insertions(+), 3 deletions(-) > > diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c > index 846f0fe2e..cc64a3a13 100644 > --- a/libdtrace/dt_cg.c > +++ b/libdtrace/dt_cg.c > @@ -594,11 +594,11 @@ dt_cg_tramp_copy_pc_from_regs(dt_pcb_t *pcb) > /* test just a single byte */ > emit(dlp, BPF_MOV_IMM(BPF_REG_2, 1)); > > - /* safe to write to FP+DT_STK_SP_BASE, which becomes the clause stack */ > + /* write to scratch space */ > emit(dlp, BPF_MOV_REG(BPF_REG_1, BPF_REG_FP)); > - emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, DT_STK_SP_BASE)); > + emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, DT_TRAMP_SP_SLOT(0))); > > - /* bpf_probe_read_kernel(%fp + DT_STK_SP, 1, PC) */ > + /* bpf_probe_read_kernel(%fp + DT_TRAMP_SP_SLOT(0), 1, PC) */ > dt_regset_xalloc(drp, BPF_REG_0); > emit(dlp, BPF_CALL_HELPER(BPF_FUNC_probe_read_kernel)); > > -- > 2.43.5 > > > _______________________________________________ > DTrace-devel mailing list > DTrace-devel at oss.oracle.com > https://oss.oracle.com/mailman/listinfo/dtrace-devel From eugene.loh at oracle.com Mon Feb 24 20:19:22 2025 From: eugene.loh at oracle.com (eugene.loh at oracle.com) Date: Mon, 24 Feb 2025 15:19:22 -0500 Subject: [DTrace-devel] [PATCH] test: Check tid value Message-ID: <20250224201922.12992-1-eugene.loh@oracle.com> From: Eugene Loh We were checking the built-in variable tid simply by testing that we could print it and its value was not -1. Add a test that confirms the value is actually correct; compare to C output of gettid(). In line with other similar tests, also check for the profile provider. While we're at it, check the pid value and the pthread_t value returned via pthread_create(). Signed-off-by: Eugene Loh --- test/unittest/builtinvar/tst.tid_pid.r | 1 + test/unittest/builtinvar/tst.tid_pid.sh | 126 ++++++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 test/unittest/builtinvar/tst.tid_pid.r create mode 100755 test/unittest/builtinvar/tst.tid_pid.sh diff --git a/test/unittest/builtinvar/tst.tid_pid.r b/test/unittest/builtinvar/tst.tid_pid.r new file mode 100644 index 000000000..2e9ba477f --- /dev/null +++ b/test/unittest/builtinvar/tst.tid_pid.r @@ -0,0 +1 @@ +success diff --git a/test/unittest/builtinvar/tst.tid_pid.sh b/test/unittest/builtinvar/tst.tid_pid.sh new file mode 100755 index 000000000..7ff0227fe --- /dev/null +++ b/test/unittest/builtinvar/tst.tid_pid.sh @@ -0,0 +1,126 @@ +#!/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 +CC=/usr/bin/gcc + +DIRNAME="$tmpdir/builtinvar-tid_pid.$$.$RANDOM" +mkdir -p $DIRNAME +cd $DIRNAME + +# Create trigger program. + +cat << EOF > main.c +#include +#include +#include +#include + +/* Provide an implementation in case glibc is too old. */ +pid_t gettid(void) +{ + return syscall(__NR_gettid); +} + +static void * foo(void *arg) { + int i = 0; + + /* + * Each thread reports the pid and tid values it expects. + * (Expect the same values for both the pid and profile probes.) + */ + printf("pid probe expect pid %d tid %d\n", getpid(), gettid()); + printf("profile probe expect pid %d tid %d\n", getpid(), gettid()); + fflush(stdout); + + /* Wait endlessly. DTrace will kill me when it is done. */ + while (i < 2) + i ^= 1; + + return 0; +} + +int main(int c, char **v) { + pthread_t mythr; + + /* Create a thread. */ + pthread_create(&mythr, NULL, &foo, NULL); + + /* Also report the pthread_t. */ + printf("created pthread_t %lld\n\n", mythr); + fflush(stdout); + + /* Wait endlessly. DTrace will kill me when it is done. */ + pthread_join(mythr, NULL); + + return 0; +} +EOF + +# Compile the trigger program. + +$CC $test_cppflags main.c -lpthread +if [ $? -ne 0 ]; then + echo compilation failed + exit 1 +fi + +# Run DTrace. + +rm -f C.out D.out +$dtrace $dt_flags -o D.out -c ./a.out -qn ' +/* Report pid and tid from a pid-provider probe. */ +pid$target:a.out:foo:entry +{ + self->mypid = pid; + printf("pid probe expect pid %d tid %d\n", pid, tid); +} + +/* Report pid and tid from a profile-provider probe. Look for the thread we created. */ +profile:::profile-1s +/self->mypid != 0/ +{ + printf("profile probe expect pid %d tid %d\n", pid, tid); + exit(0); +} + +/* While we are at it, check the pthread_t returned via pthread_create(). */ +pid$target::pthread_create:entry +{ + self->thrid_p = (uintptr_t) arg0; +} +pid$target::pthread_create:return +{ + printf("created pthread_t %lld\n", *((long long *)copyin(self->thrid_p, sizeof(long long *)))); + self->thrid_p = 0 +}' |& sort > C.out +if [ $? -ne 0 ]; then + echo DTrace failed + echo ==== C.out + cat C.out + echo ==== D.out + cat D.out + exit 1 +fi + +# Compare the C and D output. + +sort D.out > D.out.sorted +if ! diff -q C.out D.out.sorted ; then + echo ERROR: mismatch + echo ==== C.out + cat C.out + echo ==== D.out + cat D.out.sorted + echo ==== diff + diff C.out D.out.sorted + exit 1 +fi + +echo success +exit 0 -- 2.43.5 From nick.alcock at oracle.com Tue Feb 25 18:27:41 2025 From: nick.alcock at oracle.com (Nick Alcock) Date: Tue, 25 Feb 2025 18:27:41 +0000 Subject: [DTrace-devel] USDT blocking ioctl In-Reply-To: <87r03mt010.fsf@esperi.org.uk> (Nick Alcock's message of "Tue, 25 Feb 2025 17:19:23 +0000") References: <87r03mt010.fsf@esperi.org.uk> Message-ID: <87tt8hswv6.fsf@esperi.org.uk> Another attempt at coming up with a design that lets ioctl()s from USDT processes block until DTrace is ready to handle their probes. All previous ones have been nightmarishly complex: this one is much simpler and needs nothing more difficult to implement than plain old named pipes. The constraints we're trying to satisfy here: - any dtraces running when a program containing relevant probes starts get a chance to enable those probes before the affected program continues execution - if the dtrace doesn't reply one way or the other, we give up and let it keep going, so a ctrl-z'ed dtrace won't hang arbitrary other programs Here's one possibility! DTrace, at startup, touches /run/dtrace/pids/$pid: at clean shutdown, it deletes it again. When an ioctl comes in, after the DOF stash is updated (so, right before final, unblocking fuse_reply_ioctl() in dtprobed's helper_ioctl()), dtprobed runs over all files in /run/dtrace/pids/$pid and does a kill($pid, 0) on each, deleting any that relate to no-longer-existing dtraces. If any still exist, dtprobed forks off a new "wait thread" to handle it, and does *not* immediately reply to the ioctl, but just returns so another ioctl can come in from something else. This new thread waits by reading a named pipe, /run/dtrace/responses/$pid, where $pid is the pid of the DOF-containing process. When dtraces spot the arrival of the process via inotify and have registered all probes -- or if they decide they don't care about this process -- they write their own PIDs down the relevant pipe: the PIDs, being shorter than PIPE_BUF, are atomically write()/read() so we don't need to worry about stream semantics and we can pretend the pipe is message-based :) The dtprobed ioctl-blocking thread reads these pids, and when all of them have been received, it replies to the ioctl, unblocking it, and terminates (for efficiency, we might in future turn this into a thread pool, but for now, forget it, they're only shortlived and they only exist for processes with DOF in them when dtraces are running anyway). This leaves only one problem: races where some dtrace dies, or is terminated, or suspended after an ioctl comes in and dtprobed starts waiting for it. The easiest solution there is for dtprobed to simply remember the time when it fires off each wait thread and the corresponding DOF-containing PID in a simple list (so, naturally sorted by thread start time): when a new ioctl comes in, it runs over the first entry in the list, and if it's "too old" (#define? startup option?), writes a 0 down that named pipe and removes the list entry: it keeps doing that until it hits an entry that isn't "too old", then gets on with normal ioctl processing. The wait threads respond to a zero coming down the pipe as an immediate request to unblock the ioctl and terminate, just as if all the dtraces have responded. Unanswered questions: only one. I don't know how CUSE and multiple threads interact. It's possible that we cannot return from the ioctl function and *not* reply and still expect further ioctls()s from other processes to turn up, and then later issue a reply to the first ioctl() from another thread later on. Let's hope we can first, since it makes things much simpler: I don't see anything in the cuse code preventing it at first glance. (Of course, there is no documentation whatsoever, but then what's new.) Last bit: regarding testing the inotify thing. A test that amplifies the high latencies seen in the current setup by doing the usual start/sigaction/probe/terminate dance in a program (like usdt-tst-defer.c), and then having dtrace wait for the probe firing and raise() while the process is started a few hundred times in a loop, sequentially, should suffice: in the current setup, each of these rounds will take on the order of half a second on average (making the whole thing take longer than the timeout), while in the new setup the dtrace raise() should be almost immediate and the processes will all start and finish in much less time. Something like that, anyway. From kris.van.hees at oracle.com Tue Feb 25 21:08:23 2025 From: kris.van.hees at oracle.com (Kris Van Hees) Date: Tue, 25 Feb 2025 16:08:23 -0500 Subject: [DTrace-devel] [PATCH v3] Allow arbitrary tracefs mount points In-Reply-To: References: <20241112134256.118717-1-nick.alcock@oracle.com> Message-ID: ping? On Fri, Nov 15, 2024 at 03:42:49PM -0500, Kris Van Hees wrote: > On Tue, Nov 12, 2024 at 01:42:56PM +0000, Nick Alcock wrote: > > So as of kernel 6.3 (upstream commit 2455f0e124d317dd08d337a75), the > > canonical tracefs location has moved to /sys/kernel/tracing. Unfortunately > > this requires an explicit mount, and a great many installations have not > > done this and perhaps never will. So if the debugfs is mounted on > > /sys/kernel/debug, it automatically makes /sys/kernel/debug/tracing appear > > as it used to, as another tracefs mount. And of course there's nothing > > special about the "canonical location": it's up to the admin, who might > > choose to remount the thing anywhere at all. > > > > To make this even more fun, it's quite possible to end up with the tracefs > > on /sys/kernel/debug/tracing, but an empty directory at /sys/kernel/tracing > > (I got that during testing with no effort at all). > > > > All this means that the existing DTrace hardwiring for tracefs/eventsfs > > locations isn't good enough. Instead, hunt for a suitable tracefs mount with > > getmntent(), and add a function to open files under that directory, allowing > > the path to be created using a printf-style format string (mimicking the > > things we used to do with EVENTSFS defines and the like). This is actually > > all we need; there is no need to ever return these paths at all, so there > > is no clogging up the code with free()s -- and actually there's a > > noticeable simplification in most cases. > > > > Tested with both in-practice-observed locations of debugfs, and the > > obviously crazy and bad-in-a-format-string path of "/%s/%s/%n" to make sure > > that is properly rejected. > > I think it is useful to put the testing (and results) in the commit msg for > this, especially since you do not have a testcase for this (and I can > understand why - though if we were to use chroot or something a test could > be constructed, right). And the case of tracefs *not* being in standard > location is worth pointing out to clearly show that this does work in the > most generic case. > > Or if output of testing is too much to include (I assume it shouldn't be), > at least make sure you test standard and non-standard locations, and then > confidently report here that it works for all locations that do not have > % in the path name. > > > Bug: https://github.com/oracle/dtrace-utils/issues/111 > > Signed-off-by: Nick Alcock > > --- > > include/tracefs.h | 14 ---- > > libdtrace/dt_error.c | 3 +- > > libdtrace/dt_impl.h | 3 + > > libdtrace/dt_open.c | 1 + > > libdtrace/dt_prov_dtrace.c | 27 +++---- > > libdtrace/dt_prov_fbt.c | 37 ++++----- > > libdtrace/dt_prov_rawtp.c | 9 ++- > > libdtrace/dt_prov_sdt.c | 30 ++++--- > > libdtrace/dt_prov_syscall.c | 27 ++++--- > > libdtrace/dt_prov_uprobe.c | 41 +++++----- > > libdtrace/dt_provider.h | 1 - > > libdtrace/dt_subr.c | 80 +++++++++++++++++++ > > runtest.sh | 8 ++ > > test/unittest/funcs/tst.rw_.x | 7 +- > > test/unittest/providers/tst.dtrace_cleanup.sh | 9 ++- > > test/utils/clean_probes.sh | 12 ++- > > 16 files changed, 203 insertions(+), 106 deletions(-) > > delete mode 100644 include/tracefs.h > > > > diff --git a/include/tracefs.h b/include/tracefs.h > > deleted file mode 100644 > > index d671f51adefc..000000000000 > > --- a/include/tracefs.h > > +++ /dev/null > > @@ -1,14 +0,0 @@ > > -/* > > - * Oracle Linux DTrace; simple uprobe helper functions > > - * Copyright (c) 2022, 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. > > - */ > > - > > -#ifndef _TRACEFS_H > > -#define _TRACEFS_H > > - > > -#define TRACEFS "/sys/kernel/debug/tracing/" > > -#define EVENTSFS TRACEFS "events/" > > - > > -#endif /* _TRACEFS_H */ > > diff --git a/libdtrace/dt_error.c b/libdtrace/dt_error.c > > index 213f0d9e1385..9c4a2b32888e 100644 > > --- a/libdtrace/dt_error.c > > +++ b/libdtrace/dt_error.c > > @@ -98,7 +98,8 @@ static const struct { > > { EDT_READMAXSTACK, "Cannot read kernel param perf_event_max_stack" }, > > { EDT_TRACEMEM, "Missing or corrupt tracemem() record" }, > > { EDT_PCAP, "Missing or corrupt pcap() record" }, > > - { EDT_PRINT, "Missing or corrupt print() record" } > > + { EDT_PRINT, "Missing or corrupt print() record" }, > > + { EDT_TRACEFS, "Cannot find tracefs" } > > }; > > > > static const int _dt_nerr = sizeof(_dt_errlist) / sizeof(_dt_errlist[0]); > > diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h > > index 68fb8ec53c06..950cb34819aa 100644 > > --- a/libdtrace/dt_impl.h > > +++ b/libdtrace/dt_impl.h > > @@ -354,6 +354,7 @@ struct dtrace_hdl { > > char *dt_module_path; /* pathname of kernel module root */ > > dt_version_t dt_kernver;/* kernel version, used in the libpath */ > > char *dt_dofstash_path; /* Path to the DOF stash. */ > > + char *dt_tracefs_path; /* Path to tracefs. */ > > uid_t dt_useruid; /* lowest non-system uid: set via -xuseruid */ > > char *dt_sysslice; /* the systemd system slice: set via -xsysslice */ > > uint_t dt_lazyload; /* boolean: set via -xlazyload */ > > @@ -643,6 +644,7 @@ enum { > > EDT_TRACEMEM, /* missing or corrupt tracemem() record */ > > EDT_PCAP, /* missing or corrupt pcap() record */ > > EDT_PRINT, /* missing or corrupt print() record */ > > + EDT_TRACEFS, /* cannot find tracefs */ > > }; > > > > /* > > @@ -713,6 +715,7 @@ extern void dt_conf_init(dtrace_hdl_t *); > > > > extern int dt_gmatch(const char *, const char *); > > extern char *dt_basename(char *); > > +extern int dt_tracefs_open(dtrace_hdl_t *, const char *fn, int flags, ...); > > > > extern ulong_t dt_popc(ulong_t); > > extern ulong_t dt_popcb(const ulong_t *, ulong_t); > > diff --git a/libdtrace/dt_open.c b/libdtrace/dt_open.c > > index e1972aa821e7..775830f64492 100644 > > --- a/libdtrace/dt_open.c > > +++ b/libdtrace/dt_open.c > > @@ -1317,6 +1317,7 @@ dtrace_close(dtrace_hdl_t *dtp) > > free(dtp->dt_cpp_argv); > > free(dtp->dt_cpp_path); > > free(dtp->dt_ld_path); > > + free(dtp->dt_tracefs_path); > > free(dtp->dt_sysslice); > > free(dtp->dt_dofstash_path); > > > > diff --git a/libdtrace/dt_prov_dtrace.c b/libdtrace/dt_prov_dtrace.c > > index 34b5d8e2467f..670954beb4c9 100644 > > --- a/libdtrace/dt_prov_dtrace.c > > +++ b/libdtrace/dt_prov_dtrace.c > > @@ -23,8 +23,6 @@ static const char funname[] = ""; > > > > #define PROBE_FUNC_SUFFIX "_probe" > > > > -#define UPROBE_EVENTS TRACEFS "uprobe_events" > > Since you keep KPROBE_EVENTS etc in other providers, why not here? > > > - > > static const dtrace_pattr_t pattr = { > > { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON }, > > { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, > > @@ -229,11 +227,9 @@ out: > > static int attach(dtrace_hdl_t *dtp, const dt_probe_t *prp, int bpf_fd) > > { > > if (!dt_tp_probe_has_info(prp)) { > > - char *spec; > > - char *fn; > > - FILE *f; > > - size_t len; > > - int fd, rc = -1; > > + char *spec; > > + FILE *f; > > + int fd = -1, rc = -1; > > > > /* get a uprobe specification for this probe */ > > spec = uprobe_spec(getpid(), prp->desc->prb); > > @@ -241,7 +237,8 @@ static int attach(dtrace_hdl_t *dtp, const dt_probe_t *prp, int bpf_fd) > > return -ENOENT; > > > > /* add a uprobe */ > > - fd = open(UPROBE_EVENTS, O_WRONLY | O_APPEND); > > + fd = dt_tracefs_open(dtp, "uprobe_events", O_WRONLY | O_APPEND); > > + > > if (fd != -1) { > > rc = dprintf(fd, "p:" GROUP_FMT "/%s %s\n", > > GROUP_DATA, prp->desc->prb, spec); > > @@ -252,16 +249,12 @@ static int attach(dtrace_hdl_t *dtp, const dt_probe_t *prp, int bpf_fd) > > return -ENOENT; > > > > /* open format file */ > > - len = snprintf(NULL, 0, "%s" GROUP_FMT "/%s/format", > > - EVENTSFS, GROUP_DATA, prp->desc->prb) + 1; > > - fn = dt_alloc(dtp, len); > > - if (fn == NULL) > > + fd = dt_tracefs_open(dtp, "events/" GROUP_FMT "/%s/format", > > + O_RDONLY, GROUP_DATA, prp->desc->prb); > > + if (fd < 0) > > return -ENOENT; > > > > - snprintf(fn, len, "%s" GROUP_FMT "/%s/format", > > - EVENTSFS, GROUP_DATA, prp->desc->prb); > > - f = fopen(fn, "r"); > > - dt_free(dtp, fn); > > + f = fdopen(fd, "r"); > > if (f == NULL) > > return -ENOENT; > > > > @@ -296,7 +289,7 @@ static void detach(dtrace_hdl_t *dtp, const dt_probe_t *prp) > > > > dt_tp_probe_detach(dtp, prp); > > > > - fd = open(UPROBE_EVENTS, O_WRONLY | O_APPEND); > > + fd = dt_tracefs_open(dtp, "uprobe_events", O_WRONLY | O_APPEND); > > if (fd == -1) > > return; > > > > diff --git a/libdtrace/dt_prov_fbt.c b/libdtrace/dt_prov_fbt.c > > index 21f63ddffc73..b5c1f5d22a06 100644 > > --- a/libdtrace/dt_prov_fbt.c > > +++ b/libdtrace/dt_prov_fbt.c > > @@ -43,8 +43,8 @@ > > static const char prvname[] = "fbt"; > > static const char modname[] = "vmlinux"; > > > > -#define KPROBE_EVENTS TRACEFS "kprobe_events" > > -#define PROBE_LIST TRACEFS "available_filter_functions" > > +#define KPROBE_EVENTS "kprobe_events" > > +#define PROBE_LIST "available_filter_functions" > > > > #define FBT_GROUP_FMT GROUP_FMT "_%s" > > #define FBT_GROUP_DATA GROUP_DATA, prp->desc->prb > > @@ -65,6 +65,7 @@ static int populate(dtrace_hdl_t *dtp) > > { > > dt_provider_t *prv; > > dt_provimpl_t *impl; > > + int fd; > > FILE *f; > > char *buf = NULL; > > char *p; > > @@ -79,7 +80,11 @@ static int populate(dtrace_hdl_t *dtp) > > if (prv == NULL) > > return -1; /* errno already set */ > > > > - f = fopen(PROBE_LIST, "r"); > > + fd = dt_tracefs_open(dtp, PROBE_LIST, O_RDONLY); > > + if (fd < 0) > > + return 0; > > + > > + f = fdopen(fd, "r"); > > if (f == NULL) > > return 0; > > > > @@ -363,16 +368,14 @@ static int kprobe_trampoline(dt_pcb_t *pcb, uint_t exitlbl) > > static int kprobe_attach(dtrace_hdl_t *dtp, const dt_probe_t *prp, int bpf_fd) > > { > > if (!dt_tp_probe_has_info(prp)) { > > - char *fn; > > - FILE *f; > > - size_t len; > > - int fd, rc = -1; > > + FILE *f; > > + int fd, rc = -1; > > > > /* > > * Register the kprobe with the tracing subsystem. This will > > * create a tracepoint event. > > */ > > - fd = open(KPROBE_EVENTS, O_WRONLY | O_APPEND); > > + fd = dt_tracefs_open(dtp, KPROBE_EVENTS, O_WRONLY | O_APPEND); > > if (fd == -1) > > return -ENOENT; > > > > @@ -383,19 +386,13 @@ static int kprobe_attach(dtrace_hdl_t *dtp, const dt_probe_t *prp, int bpf_fd) > > if (rc == -1) > > return -ENOENT; > > > > - /* create format file name */ > > - len = snprintf(NULL, 0, "%s" FBT_GROUP_FMT "/%s/format", > > - EVENTSFS, FBT_GROUP_DATA, prp->desc->fun) + 1; > > - fn = dt_alloc(dtp, len); > > - if (fn == NULL) > > + /* open format file */ > > + fd = dt_tracefs_open(dtp, "events/" FBT_GROUP_FMT "/%s/format", > > + O_RDONLY, FBT_GROUP_DATA, prp->desc->fun); > > + if (fd < 0) > > return -ENOENT; > > > > - snprintf(fn, len, "%s" FBT_GROUP_FMT "/%s/format", EVENTSFS, > > - FBT_GROUP_DATA, prp->desc->fun); > > - > > - /* open format file */ > > - f = fopen(fn, "r"); > > - dt_free(dtp, fn); > > + f = fdopen(fd, "r"); > > if (f == NULL) > > return -ENOENT; > > > > @@ -431,7 +428,7 @@ static void kprobe_detach(dtrace_hdl_t *dtp, const dt_probe_t *prp) > > > > dt_tp_probe_detach(dtp, prp); > > > > - fd = open(KPROBE_EVENTS, O_WRONLY | O_APPEND); > > + fd = dt_tracefs_open(dtp, KPROBE_EVENTS, O_WRONLY | O_APPEND); > > if (fd == -1) > > return; > > > > diff --git a/libdtrace/dt_prov_rawtp.c b/libdtrace/dt_prov_rawtp.c > > index 778a6f9cde90..6940edce6a6d 100644 > > --- a/libdtrace/dt_prov_rawtp.c > > +++ b/libdtrace/dt_prov_rawtp.c > > @@ -38,7 +38,7 @@ > > static const char prvname[] = "rawtp"; > > static const char modname[] = "vmlinux"; > > > > -#define PROBE_LIST TRACEFS "available_events" > > +#define PROBE_LIST "available_events" > > > > #define KPROBES "kprobes" > > #define SYSCALLS "syscalls" > > @@ -64,6 +64,7 @@ static const dtrace_pattr_t pattr = { > > static int populate(dtrace_hdl_t *dtp) > > { > > dt_provider_t *prv; > > + int fd; > > FILE *f; > > char *buf = NULL; > > char *p; > > @@ -73,7 +74,11 @@ static int populate(dtrace_hdl_t *dtp) > > if (prv == NULL) > > return -1; /* errno already set */ > > > > - f = fopen(PROBE_LIST, "r"); > > + fd = dt_tracefs_open(dtp, PROBE_LIST, O_RDONLY); > > + if (fd < 0) > > + return 0; > > + > > + f = fdopen(fd, "r"); > > if (f == NULL) > > return 0; > > > > diff --git a/libdtrace/dt_prov_sdt.c b/libdtrace/dt_prov_sdt.c > > index 675e0458ca4c..7ebe010efa79 100644 > > --- a/libdtrace/dt_prov_sdt.c > > +++ b/libdtrace/dt_prov_sdt.c > > @@ -36,7 +36,7 @@ > > static const char prvname[] = "sdt"; > > static const char modname[] = "vmlinux"; > > > > -#define PROBE_LIST TRACEFS "available_events" > > +#define PROBE_LIST "available_events" > > > > #define KPROBES "kprobes" > > #define SYSCALLS "syscalls" > > @@ -62,6 +62,7 @@ static const dtrace_pattr_t pattr = { > > static int populate(dtrace_hdl_t *dtp) > > { > > dt_provider_t *prv; > > + int fd; > > FILE *f; > > char *buf = NULL; > > char *p; > > @@ -71,7 +72,11 @@ static int populate(dtrace_hdl_t *dtp) > > if (prv == NULL) > > return -1; /* errno already set */ > > > > - f = fopen(PROBE_LIST, "r"); > > + fd = dt_tracefs_open(dtp, PROBE_LIST, O_RDONLY); > > + if (fd < 0) > > + return 0; > > + > > + f = fdopen(fd, "r"); > > if (f == NULL) > > return 0; > > > > @@ -192,16 +197,16 @@ static int trampoline(dt_pcb_t *pcb, uint_t exitlbl) > > static int probe_info_tracefs(dtrace_hdl_t *dtp, const dt_probe_t *prp, > > int *argcp, dt_argdesc_t **argvp) > > { > > + int fd; > > FILE *f; > > - char *fn; > > int rc; > > const dtrace_probedesc_t *pdp = prp->desc; > > > > - if (asprintf(&fn, EVENTSFS "%s/%s/format", pdp->mod, pdp->prb) == -1) > > - return dt_set_errno(dtp, EDT_NOMEM); > > + fd = dt_tracefs_open(dtp, "events/%s/%s/format", O_RDONLY, pdp->mod, pdp->prb); > > + if (fd < 0) > > + return -ENOENT; > > > > - f = fopen(fn, "r"); > > - free(fn); > > + f = fdopen(fd, "r"); > > if (!f) > > return -ENOENT; > > > > @@ -223,15 +228,18 @@ static int probe_info(dtrace_hdl_t *dtp, const dt_probe_t *prp, > > int argc = 0; > > dt_argdesc_t *argv = NULL; > > dtrace_typeinfo_t sym; > > + int fd; > > FILE *f; > > uint32_t id; > > > > /* Retrieve the event id. */ > > - if (asprintf(&str, EVENTSFS "%s/%s/id", prp->desc->mod, prp->desc->prb) == -1) > > - return dt_set_errno(dtp, EDT_NOMEM); > > > > - f = fopen(str, "r"); > > - free(str); > > + fd = dt_tracefs_open(dtp, "events/%s/%s/id", O_RDONLY, > > + prp->desc->mod, prp->desc->prb); > > + if (fd < 0) > > + return dt_set_errno(dtp, EDT_ENABLING_ERR); > > + > > + f = fdopen(fd, "r"); > > if (!f) > > return dt_set_errno(dtp, EDT_ENABLING_ERR); > > > > diff --git a/libdtrace/dt_prov_syscall.c b/libdtrace/dt_prov_syscall.c > > index 20843c6f538e..63ce3bc43ae1 100644 > > --- a/libdtrace/dt_prov_syscall.c > > +++ b/libdtrace/dt_prov_syscall.c > > @@ -38,7 +38,7 @@ > > static const char prvname[] = "syscall"; > > static const char modname[] = "vmlinux"; > > > > -#define SYSCALLSFS EVENTSFS "syscalls/" > > +#define SYSCALLSFS "events/syscalls/" > > > > /* > > * We need to skip over an extra field: __syscall_nr. > > @@ -61,7 +61,7 @@ struct syscall_data { > > > > #define SCD_ARG(n) offsetof(struct syscall_data, arg[n]) > > > > -#define PROBE_LIST TRACEFS "available_events" > > +#define PROBE_LIST "available_events" > > > > #define PROV_PREFIX "syscalls:" > > #define ENTRY_PREFIX "sys_enter_" > > @@ -71,6 +71,7 @@ struct syscall_data { > > static int populate(dtrace_hdl_t *dtp) > > { > > dt_provider_t *prv; > > + int fd; > > FILE *f; > > char *buf = NULL; > > size_t n; > > @@ -79,7 +80,11 @@ static int populate(dtrace_hdl_t *dtp) > > if (prv == NULL) > > return -1; /* errno already set */ > > > > - f = fopen(PROBE_LIST, "r"); > > + fd = dt_tracefs_open(dtp, PROBE_LIST, O_RDONLY); > > + if (fd < 0) > > + return 0; > > + > > + f = fdopen(fd, "r"); > > if (f == NULL) > > return 0; > > > > @@ -195,23 +200,21 @@ static int trampoline(dt_pcb_t *pcb, uint_t exitlbl) > > static int probe_info(dtrace_hdl_t *dtp, const dt_probe_t *prp, > > int *argcp, dt_argdesc_t **argvp) > > { > > + int fd; > > FILE *f; > > - char fn[256]; > > int rc; > > > > /* > > * We know that the probe name is either "entry" or "return", so we can > > * just check the first character. > > */ > > - strcpy(fn, SYSCALLSFS); > > - if (prp->desc->prb[0] == 'e') > > - strcat(fn, "sys_enter_"); > > - else > > - strcat(fn, "sys_exit_"); > > - strcat(fn, prp->desc->fun); > > - strcat(fn, "/format"); > > + fd = dt_tracefs_open(dtp, SYSCALLSFS "/sys_%s_%s/format", O_RDONLY, > > + (prp->desc->prb[0] == 'e') ? "enter" : "exit", > > + prp->desc->fun); > > + if (fd < 0) > > + return -ENOENT; > > > > - f = fopen(fn, "r"); > > + f = fdopen(fd, "r"); > > if (!f) > > return -ENOENT; > > > > diff --git a/libdtrace/dt_prov_uprobe.c b/libdtrace/dt_prov_uprobe.c > > index 205014617586..6a02243ff572 100644 > > --- a/libdtrace/dt_prov_uprobe.c > > +++ b/libdtrace/dt_prov_uprobe.c > > @@ -1116,13 +1116,13 @@ static char *uprobe_name(dev_t dev, ino_t ino, uint64_t addr, int flags) > > * uprobe may be a uretprobe. Return the probe's name as > > * a new dynamically-allocated string, or NULL on error. > > */ > > -static char *uprobe_create(dev_t dev, ino_t ino, const char *mapping_fn, > > - uint64_t addr, int flags) > > +static char *uprobe_create(dtrace_hdl_t *dtp, dev_t dev, ino_t ino, > > + const char *mapping_fn, uint64_t addr, int flags) > > { > > - int fd = -1; > > - int rc = -1; > > - char *name; > > - char *spec; > > + int fd = -1; > > + int rc = -1; > > + char *name; > > + char *spec; > > > > if (asprintf(&spec, "%s:0x%lx", mapping_fn, addr) < 0) > > return NULL; > > @@ -1132,8 +1132,8 @@ static char *uprobe_create(dev_t dev, ino_t ino, const char *mapping_fn, > > goto out; > > > > /* Add the uprobe. */ > > - fd = open(TRACEFS "uprobe_events", O_WRONLY | O_APPEND); > > - if (fd == -1) > > + fd = dt_tracefs_open(dtp, "uprobe_events", O_WRONLY | O_APPEND); > > + if (fd < 0) > > goto out; > > > > rc = dprintf(fd, "%c:%s %s\n", flags & PP_IS_RETURN ? 'r' : 'p', name, spec); > > @@ -1153,8 +1153,8 @@ static int attach(dtrace_hdl_t *dtp, const dt_probe_t *uprp, int bpf_fd) > > { > > dt_uprobe_t *upp = uprp->prv_data; > > tp_probe_t *tpp = upp->tp; > > + int fd; > > FILE *f; > > - char *fn; > > char *prb = NULL; > > int rc = -1; > > > > @@ -1163,7 +1163,7 @@ static int attach(dtrace_hdl_t *dtp, const dt_probe_t *uprp, int bpf_fd) > > > > assert(upp->fn != NULL); > > > > - prb = uprobe_create(upp->dev, upp->inum, upp->fn, upp->off, > > + prb = uprobe_create(dtp, upp->dev, upp->inum, upp->fn, upp->off, > > upp->flags); > > > > /* > > @@ -1177,12 +1177,12 @@ static int attach(dtrace_hdl_t *dtp, const dt_probe_t *uprp, int bpf_fd) > > upp->flags); > > > > /* open format file */ > > - rc = asprintf(&fn, "%s%s/format", EVENTSFS, prb); > > + fd = dt_tracefs_open(dtp, "events/%s/format", O_RDONLY, prb); > > free(prb); > > - if (rc < 0) > > + if (fd < 0) > > return -ENOENT; > > - f = fopen(fn, "r"); > > - free(fn); > > + > > + f = fdopen(fd, "r"); > > if (f == NULL) > > return -ENOENT; > > > > @@ -1251,21 +1251,20 @@ done: > > * Destroy a uprobe for a given device and address. > > */ > > static int > > -uprobe_delete(dev_t dev, ino_t ino, uint64_t addr, int flags) > > +uprobe_delete(dtrace_hdl_t *dtp, dev_t dev, ino_t ino, uint64_t addr, int flags) > > { > > - int fd = -1; > > - int rc = -1; > > - char *name; > > + int fd = -1; > > + int rc = -1; > > + char *name; > > > > name = uprobe_name(dev, ino, addr, flags); > > if (!name) > > goto out; > > > > - fd = open(TRACEFS "uprobe_events", O_WRONLY | O_APPEND); > > + fd = dt_tracefs_open(dtp, "uprobe_events", O_WRONLY | O_APPEND); > > if (fd == -1) > > goto out; > > > > - > > rc = dprintf(fd, "-:%s\n", name); > > > > out: > > @@ -1297,7 +1296,7 @@ static void detach(dtrace_hdl_t *dtp, const dt_probe_t *uprp) > > > > dt_tp_detach(dtp, tpp); > > > > - uprobe_delete(upp->dev, upp->inum, upp->off, upp->flags); > > + uprobe_delete(dtp, upp->dev, upp->inum, upp->off, upp->flags); > > } > > > > /* > > diff --git a/libdtrace/dt_provider.h b/libdtrace/dt_provider.h > > index 8f143dceaed7..4598a380b950 100644 > > --- a/libdtrace/dt_provider.h > > +++ b/libdtrace/dt_provider.h > > @@ -12,7 +12,6 @@ > > #include > > #include > > #include > > -#include > > > > #ifdef __cplusplus > > extern "C" { > > diff --git a/libdtrace/dt_subr.c b/libdtrace/dt_subr.c > > index d5dca164861e..f129e5591465 100644 > > --- a/libdtrace/dt_subr.c > > +++ b/libdtrace/dt_subr.c > > @@ -20,6 +20,7 @@ > > #include > > #include > > #include > > +#include > > #include > > > > #include > > @@ -998,3 +999,82 @@ uint32_t dt_gen_hval(const char *p, uint32_t hval, size_t len) > > > > return hval; > > } > > + > > +/* > > + * Find the tracefs and store it away in dtp. > > + */ > > +static int > > +find_tracefs_path(dtrace_hdl_t *dtp) > > +{ > > + FILE *mounts; > > + struct mntent *mnt; > > + > > + if ((mounts = setmntent("/proc/mounts", "r")) == NULL) { > > + dt_dprintf("Cannot open /proc/mounts: %s\n", strerror(errno)); > > + return dt_set_errno(dtp, EDT_TRACEFS); > > + } > > + > > + while ((mnt = getmntent(mounts)) != NULL) { > > + /* > > + * Only accept tracefs paths that do not contain percent > > + * characters in their mounted paths, since we use this > > + * to augment a format string in dt_tracefs_vfn(). > > + */ > > + if ((strcmp(mnt->mnt_type, "tracefs") == 0) && > > + (strchr(mnt->mnt_dir, '%') == NULL)) { > > + dtp->dt_tracefs_path = strdup(mnt->mnt_dir); > > + break; > > + } > > + } > > + endmntent(mounts); > > + > > + if (!dtp->dt_tracefs_path) { > > + dt_dprintf("Cannot find a suitable tracefs path.\n"); > > + return dt_set_errno(dtp, EDT_TRACEFS); > > + } > > + > > + dt_dprintf("Found tracefs at %s\n", dtp->dt_tracefs_path); > > + > > + return 0; > > +} > > + > > +static char * > > +dt_tracefs_vfn(dtrace_hdl_t *dtp, const char *fn, va_list ap) > > +{ > > + char *full_fn; > > + char *str; > > + > > + if (!dtp->dt_tracefs_path) > > + if (find_tracefs_path(dtp) < 0) > > + return NULL; /* errno is set for us. */ > > + > > + if (asprintf(&full_fn, "%s/%s", dtp->dt_tracefs_path, fn) < 0) { > > + dt_set_errno(dtp, EDT_NOMEM); > > + return NULL; > > + } > > + > > + if (vasprintf(&str, full_fn, ap) < 0) { > > + str = NULL; > > + dt_set_errno(dtp, EDT_NOMEM); > > + } > > + free(full_fn); > > + return str; > > +} > > + > > +int > > +dt_tracefs_open(dtrace_hdl_t *dtp, const char *fn, int flags, ...) > > +{ > > + va_list ap; > > + char *str; > > + int fd; > > + > > + va_start(ap, flags); > > + if ((str = dt_tracefs_vfn(dtp, fn, ap)) == NULL) { > > + va_end(ap); > > + return -1; /* errno is set for us. */ > > + } > > + > > + fd = open(str, flags, 0666); > > + free(str); > > + return fd; /* errno is set for us. */ > > +} > > diff --git a/runtest.sh b/runtest.sh > > index 46b532d7e161..fbf4e60c82a9 100755 > > --- a/runtest.sh > > +++ b/runtest.sh > > @@ -607,6 +607,14 @@ elif ! /usr/bin/cpp -x c -fno-show-column - /dev/null < /dev/null 2>&1 | \ > > export DTRACE_OPT_CPPARGS="-fno-show-column" > > fi > > > > +# Find the tracefs. > > +tracefs="$(awk '$3 == "tracefs" { print $2; exit; }' /proc/mounts)" > > +if [[ -z $tracefs ]]; then > > + echo "Cannot find any tracefs mounts in /proc/mounts. Some tests will fail." >&2 > > +fi > > + > > +export tracefs > > + > > # More than one dtrace tree -> run tests for all dtraces, and verify identical > > # intermediate code is produced by each dtrace. > > > > diff --git a/test/unittest/funcs/tst.rw_.x b/test/unittest/funcs/tst.rw_.x > > index 29c581116154..5737c7575a26 100755 > > --- a/test/unittest/funcs/tst.rw_.x > > +++ b/test/unittest/funcs/tst.rw_.x > > @@ -1,6 +1,11 @@ > > #!/bin/sh > > > > -FUNCS=/sys/kernel/debug/tracing/available_filter_functions > > +FUNCS=${tracefs}/available_filter_functions > > + > > +if [[ ! -e $FUNCS ]]; then > > + echo no tracefs found > > + exit 1 > > +fi > > > > if ! grep -qw _raw_read_lock $FUNCS; then > > echo no _raw_read_lock FBT probe due to kernel config > > diff --git a/test/unittest/providers/tst.dtrace_cleanup.sh b/test/unittest/providers/tst.dtrace_cleanup.sh > > index 4ac59ccb4315..f3e434ae76fc 100755 > > --- a/test/unittest/providers/tst.dtrace_cleanup.sh > > +++ b/test/unittest/providers/tst.dtrace_cleanup.sh > > @@ -1,7 +1,7 @@ > > #!/bin/bash > > # > > # Oracle Linux DTrace. > > -# Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. > > +# Copyright (c) 2020, 2024, 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. > > > > @@ -14,7 +14,12 @@ > > ## > > > > dtrace=$1 > > -UPROBE_EVENTS=/sys/kernel/debug/tracing/uprobe_events > > +UPROBE_EVENTS=${tracefs}/uprobe_events > > + > > +if [[ ! -e $UPROBE_EVENTS ]]; then > > + echo "no tracefs/uprobe_events" >&2 > > + exit 67 > > +fi > > > > out=/tmp/output.$$ > > $dtrace $dt_flags -n BEGIN,END &>> $out & > > diff --git a/test/utils/clean_probes.sh b/test/utils/clean_probes.sh > > index 8292b3096424..cfd100088eb7 100755 > > --- a/test/utils/clean_probes.sh > > +++ b/test/utils/clean_probes.sh > > @@ -1,9 +1,13 @@ > > #!/usr/bin/bash > > > > -TRACEFS=/sys/kernel/debug/tracing > > -EVENTS=${TRACEFS}/available_events > > -KPROBES=${TRACEFS}/kprobe_events > > -UPROBES=${TRACEFS}/uprobe_events > > +EVENTS=${tracefs}/available_events > > +KPROBES=${tracefs}/kprobe_events > > +UPROBES=${tracefs}/uprobe_events > > + > > +# We can't work without the tracefs: just give up quietly. > > +if [[ ! -e $EVENTS ]]; then > > + exit 0 > > +fi > > > > # Check permissions > > if [[ ! -r ${EVENTS} ]]; then > > -- > > 2.46.0.278.g36e3a12567 > > From kris.van.hees at oracle.com Tue Feb 25 21:09:53 2025 From: kris.van.hees at oracle.com (Kris Van Hees) Date: Tue, 25 Feb 2025 16:09:53 -0500 Subject: [DTrace-devel] [PATCH] test: Allow duplicate usym/umod/uaddr if for different pids In-Reply-To: References: <20241205191318.18587-1-eugene.loh@oracle.com> Message-ID: Eugene, Can you re-check this against the current dev and ensure the changes are correct? Kris On Mon, Jan 06, 2025 at 06:44:37PM -0500, Kris Van Hees wrote: > Withdrawing R-b for now after seeing these tests fail after this patch was > merged. Needs further investigation. > > On Mon, Jan 06, 2025 at 04:04:25PM -0500, Kris Van Hees wrote: > > On Thu, Dec 05, 2024 at 02:13:18PM -0500, eugene.loh at oracle.com wrote: > > > From: Eugene Loh > > > > > > In 83da884cbdc5 ("Preface usym/umod/uaddr with pid"), a bug was fixed > > > in which addresses in the same module (or function) might be mapped to > > > multiple agg entries. This fix helped the associated tests run much > > > more successfully. Nonetheless, tests would sometimes still fail. > > > > > > Another problem is that the tests themselves were overly narrow. It > > > is fine for a module (or function) to appear multiple times in the > > > aggregation output... if those entries correspond to different pids. > > > > > > Further, odd behaviors can result for some of the processes running on > > > a system. > > > > > > Change the tests to add a "pid" agg key. Filter on only a few, select > > > pids. Distinguish agg entries by pid. > > > > > > There are still occasional time outs observed with these tests, > > > presumably because the tick-2s probe is not firing (when profile-1234hz > > > is running). > > > > > > Signed-off-by: Eugene Loh > > > > Reviewed-by: Kris Van Hees > > > > > --- > > > test/unittest/profile-n/tst.ufunc.sh | 12 ++++++++---- > > > test/unittest/profile-n/tst.umod.sh | 11 ++++++++--- > > > test/unittest/profile-n/tst.usym.sh | 11 ++++++++--- > > > 3 files changed, 24 insertions(+), 10 deletions(-) > > > > > > diff --git a/test/unittest/profile-n/tst.ufunc.sh b/test/unittest/profile-n/tst.ufunc.sh > > > index 243822407..f5174a1e2 100755 > > > --- a/test/unittest/profile-n/tst.ufunc.sh > > > +++ b/test/unittest/profile-n/tst.ufunc.sh > > > @@ -11,10 +11,14 @@ tmpfile=$tmpdir/tst.profile_ufunc.$$ > > > script() > > > { > > > $dtrace $dt_flags -qs /dev/stdin < > > + BEGIN > > > + { > > > + printf("dtrace is %d\n", \$pid); > > > + } > > > profile-1234hz > > > /arg1 != 0/ > > > { > > > - @[ufunc(arg1)] = count(); > > > + @[ufunc(arg1), pid] = count(); > > > } > > > > > > tick-2s > > > @@ -52,9 +56,9 @@ if ! grep -q 'bash`[a-zA-Z_]' $tmpfile; then > > > status=1 > > > fi > > > > > > -# Check that functions are unique. (Exclude shared libraries and unresolved addresses.) > > > -if gawk '!/^ *(ld-linux-|lib|([^`]*`)?0x)/ {print $1}' $tmpfile | \ > > > - sort | uniq -c | grep -qv " 1 "; then > > > +# Check that functions are unique for each pid that interests us. > > > +dtpid=`awk '/^dtrace is [0-9]*$/ { print $3 }' $tmpfile` > > > +if gawk '$2 == '$child' || $2 == '$dtpid' {print $1, $2}' $tmpfile | sort | uniq -c | grep -qv " 1 "; then > > > echo ERROR: duplicate ufunc > > > status=1 > > > fi > > > diff --git a/test/unittest/profile-n/tst.umod.sh b/test/unittest/profile-n/tst.umod.sh > > > index 45d2b1e9b..7cfe2a073 100755 > > > --- a/test/unittest/profile-n/tst.umod.sh > > > +++ b/test/unittest/profile-n/tst.umod.sh > > > @@ -11,10 +11,14 @@ tmpfile=$tmpdir/tst.profile_umod.$$ > > > script() > > > { > > > $dtrace $dt_flags -qs /dev/stdin < > > + BEGIN > > > + { > > > + printf("dtrace is %d\n", \$pid); > > > + } > > > profile-1234hz > > > /arg1 != 0/ > > > { > > > - @[umod(arg1)] = count(); > > > + @[umod(arg1), pid] = count(); > > > } > > > > > > tick-2s > > > @@ -52,8 +56,9 @@ if ! grep -wq 'bash' $tmpfile; then > > > status=1 > > > fi > > > > > > -# Check that modules are unique. (Exclude shared libraries and unresolved addresses.) > > > -if gawk '!/^ *lib/ && !/^ *ld-.*\.so / && !/^ *0x/ {print $1}' $tmpfile | sort | uniq -c | grep -qv " 1 "; then > > > +# Check that modules are unique for each pid that interests us. > > > +dtpid=`awk '/^dtrace is [0-9]*$/ { print $3 }' $tmpfile` > > > +if gawk '$2 == '$child' || $2 == '$dtpid' {print $1, $2}' $tmpfile | sort | uniq -c | grep -qv " 1 "; then > > > echo ERROR: duplicate umod > > > status=1 > > > fi > > > diff --git a/test/unittest/profile-n/tst.usym.sh b/test/unittest/profile-n/tst.usym.sh > > > index 634e633b3..8e373b976 100755 > > > --- a/test/unittest/profile-n/tst.usym.sh > > > +++ b/test/unittest/profile-n/tst.usym.sh > > > @@ -11,10 +11,14 @@ tmpfile=$tmpdir/tst.profile_usym.$$ > > > script() > > > { > > > $dtrace $dt_flags -qs /dev/stdin < > > + BEGIN > > > + { > > > + printf("dtrace is %d\n", \$pid); > > > + } > > > profile-1234hz > > > /arg1 != 0/ > > > { > > > - @[usym(arg1)] = count(); > > > + @[usym(arg1), pid] = count(); > > > } > > > > > > tick-2s > > > @@ -52,8 +56,9 @@ if ! grep -q 'bash`[a-zA-Z_]' $tmpfile; then > > > status=1 > > > fi > > > > > > -# Check that symbols are unique. (Exclude shared libraries and unresolved addresses.) > > > -if gawk '!/^ *lib/ && !/^ *0x/ {print $1}' $tmpfile | sort | uniq -c | grep -qv " 1 "; then > > > +# Check that symbols are unique for each pid that interests us. > > > +dtpid=`awk '/^dtrace is [0-9]*$/ { print $3 }' $tmpfile` > > > +if gawk '$2 == '$child' || $2 == '$dtpid' {print $1, $2}' $tmpfile | sort | uniq -c | grep -qv " 1 "; then > > > echo ERROR: duplicate usym > > > status=1 > > > fi > > > -- > > > 2.43.5 > > > From kris.van.hees at oracle.com Tue Feb 25 21:52:04 2025 From: kris.van.hees at oracle.com (Kris Van Hees) Date: Tue, 25 Feb 2025 16:52:04 -0500 Subject: [DTrace-devel] [PATCH v2 5/6] test: Move disassembly and extracting PCs earlier In-Reply-To: <20241220232547.19043-2-eugene.loh@oracle.com> References: <20241220232547.19043-1-eugene.loh@oracle.com> <20241220232547.19043-2-eugene.loh@oracle.com> Message-ID: On Fri, Dec 20, 2024 at 06:25:47PM -0500, eugene.loh at oracle.com wrote: > From: Eugene Loh > > This will allow a future patch to use the PCs earlier in the test. > > Signed-off-by: Eugene Loh Reviewed-by: Kris Van Hees > --- > test/unittest/usdt/tst.pidprobes.sh | 33 +++++++++++++++++------------ > 1 file changed, 19 insertions(+), 14 deletions(-) > > diff --git a/test/unittest/usdt/tst.pidprobes.sh b/test/unittest/usdt/tst.pidprobes.sh > index 6fe6d752a..54444d49b 100755 > --- a/test/unittest/usdt/tst.pidprobes.sh > +++ b/test/unittest/usdt/tst.pidprobes.sh > @@ -102,6 +102,25 @@ if ! diff -q main.out main.out.expected > /dev/null; then > exit 1 > fi > > +# Disassemble foo(). (simplify with --disassemble=foo) > + > +objdump -d main | awk ' > +BEGIN { use = 0 } # start by not printing lines > +use == 1 && NF == 0 { exit } # if printing lines but hit a blank, then exit > +use == 1 { print } # print lines > +/:/ { use = 1 } # turn on printing when we hit ":" (without printing this line itself) > +' > disasm_foo.txt > +if [ $? -ne 0 ]; then > + echo cannot objdump main > + objdump -d main > + exit 1 > +fi > + > +# From the disassembly, get the PCs for foo()'s instructions. > + > +pcs=`awk '{print strtonum("0x"$1)}' disasm_foo.txt` > +pc0=`echo $pcs | awk '{print $1}'` > + > # Run dtrace. > > cat >> pidprobes.d <<'EOF' > @@ -153,20 +172,6 @@ if [ `awk 'NF != 0 { print $1 }' dtrace.out | uniq | wc -l` -ne 1 ]; then > fi > pid=`awk 'NF != 0 { print $1 }' dtrace.out | uniq` > > -# Disassemble foo(). > - > -objdump -d main | awk ' > -BEGIN { use = 0 } # start by not printing lines > -use == 1 && NF == 0 { exit } # if printing lines but hit a blank, then exit > -use == 1 { print } # print lines > -/:/ { use = 1 } # turn on printing when we hit ":" (without printing this line itself) > -' > disasm_foo.txt > - > -# From the disassembly, get the PCs for foo()'s instructions. > - > -pcs=`awk '{print strtonum("0x"$1)}' disasm_foo.txt` > -pc0=`echo $pcs | awk '{print $1}'` > - > # From the disassembly, get the PCs for USDT probes. > # Check libdtrace/dt_link.c's arch-dependent dt_modtext() to see > # what sequence of instructions signal a USDT probe. > -- > 2.43.5 > From kris.van.hees at oracle.com Tue Feb 25 22:27:14 2025 From: kris.van.hees at oracle.com (Kris Van Hees) Date: Tue, 25 Feb 2025 17:27:14 -0500 Subject: [DTrace-devel] [PATCH v3 6/6] Add support for pid function "-" with absolute offset In-Reply-To: <20250221000831.25523-1-eugene.loh@oracle.com> References: <20250221000831.25523-1-eugene.loh@oracle.com> Message-ID: On Thu, Feb 20, 2025 at 07:08:31PM -0500, eugene.loh at oracle.com wrote: > From: Eugene Loh > > The pid providers allow users to specify a probe function "-", > meaning that the probe name gives an absolute offset. > > Signed-off-by: Eugene Loh Reviewed-by: Kris Van Hees > --- > include/dtrace/pid.h | 3 +- > libdtrace/dt_pid.c | 79 ++++++++++++------ > libdtrace/dt_prov_uprobe.c | 38 ++++++--- > test/unittest/pid/tst.dash.r | 1 + > test/unittest/pid/tst.dash.sh | 119 ++++++++++++++++++++++++++++ > test/unittest/usdt/tst.pidprobes.sh | 21 ++++- > 6 files changed, 224 insertions(+), 37 deletions(-) > create mode 100644 test/unittest/pid/tst.dash.r > create mode 100755 test/unittest/pid/tst.dash.sh > > diff --git a/include/dtrace/pid.h b/include/dtrace/pid.h > index c53e60047..12934500a 100644 > --- a/include/dtrace/pid.h > +++ b/include/dtrace/pid.h > @@ -2,7 +2,7 @@ > * Licensed under the Universal Permissive License v 1.0 as shown at > * http://oss.oracle.com/licenses/upl. > * > - * Copyright (c) 2009, 2024, Oracle and/or its affiliates. All rights reserved. > + * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. > */ > > /* > @@ -22,6 +22,7 @@ typedef enum pid_probetype { > DTPPT_ENTRY, > DTPPT_RETURN, > DTPPT_OFFSETS, > + DTPPT_ABSOFFSETS, > DTPPT_USDT, > DTPPT_IS_ENABLED > } pid_probetype_t; > diff --git a/libdtrace/dt_pid.c b/libdtrace/dt_pid.c > index 11b964561..76608f690 100644 > --- a/libdtrace/dt_pid.c > +++ b/libdtrace/dt_pid.c > @@ -1,6 +1,6 @@ > /* > * Oracle Linux DTrace. > - * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved. > + * Copyright (c) 2010, 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. > */ > @@ -155,7 +155,6 @@ dt_pid_per_sym(dt_pid_probe_t *pp, const GElf_Sym *symp, const char *func) > uint_t nmatches = 0; > ulong_t sz; > int glob, rc = 0; > - int isdash = strcmp("-", func) == 0; > pid_t pid; > > /* > @@ -183,7 +182,46 @@ dt_pid_per_sym(dt_pid_probe_t *pp, const GElf_Sym *symp, const char *func) > psp->pps_nameoff = 0; > psp->pps_off = symp->st_value - pp->dpp_vaddr; > > - if (!isdash && gmatch("return", pp->dpp_name)) { > + /* > + * The special function "-" means the probe name is an absolute > + * virtual address. > + */ > + if (strcmp("-", func) == 0) { > + char *end; > + GElf_Sym sym; > + > + off = strtoull(pp->dpp_name, &end, 16); > + if (*end != '\0') { > + rc = dt_pid_error(dtp, pcb, dpr, D_PROC_NAME, > + "'%s' is an invalid probe name", > + pp->dpp_name); > + goto out; > + } > + > + psp->pps_nameoff = off; > + > + if (dt_Plookup_by_addr(dtp, pid, off, (const char **)&psp->pps_fun, &sym)) { > + rc = dt_pid_error(dtp, pcb, dpr, D_PROC_NAME, > + "failed to lookup 0x%lx in module '%s'", off, pp->dpp_mod); > + if (psp->pps_fun != func && psp->pps_fun != NULL) > + free(psp->pps_fun); > + goto out; > + } > + > + psp->pps_prb = (char*)pp->dpp_name; > + psp->pps_off = off - pp->dpp_vaddr; > + > + if (dt_pid_create_one_probe(pp->dpp_pr, dtp, psp, DTPPT_ABSOFFSETS) < 0) > + rc = dt_pid_error(dtp, pcb, dpr, D_PROC_CREATEFAIL, > + "failed to create probes at '%s+0x%llx': %s", > + func, (unsigned long long)off, dtrace_errmsg(dtp, dtrace_errno(dtp))); > + else > + pp->dpp_nmatches++; > + free(psp->pps_fun); > + goto out; > + } > + > + if (gmatch("return", pp->dpp_name)) { > if (dt_pid_create_one_probe(pp->dpp_pr, dtp, psp, DTPPT_RETURN) < 0) { > rc = dt_pid_error( > dtp, pcb, dpr, D_PROC_CREATEFAIL, > @@ -195,7 +233,7 @@ dt_pid_per_sym(dt_pid_probe_t *pp, const GElf_Sym *symp, const char *func) > nmatches++; > } > > - if (!isdash && gmatch("entry", pp->dpp_name)) { > + if (gmatch("entry", pp->dpp_name)) { > if (dt_pid_create_one_probe(pp->dpp_pr, dtp, psp, DTPPT_ENTRY) < 0) { > rc = dt_pid_error( > dtp, pcb, dpr, D_PROC_CREATEFAIL, > @@ -240,7 +278,7 @@ dt_pid_per_sym(dt_pid_probe_t *pp, const GElf_Sym *symp, const char *func) > } > > nmatches++; > - } else if (glob && !isdash) { > + } else if (glob) { > #if defined(__amd64) > /* > * We need to step through the instructions to find their > @@ -449,31 +487,25 @@ dt_pid_per_mod(void *arg, const prmap_t *pmp, const char *obj) > else > pp->dpp_obj++; > > + /* > + * If it is the special function "-", cut to dt_pid_per_sym() now. > + */ > + if (strcmp("-", pp->dpp_func) == 0) > + return dt_pid_per_sym(pp, &sym, pp->dpp_func); > + > /* > * If pp->dpp_func contains any globbing meta-characters, we need > * to iterate over the symbol table and compare each function name > * against the pattern. > */ > if (!strisglob(pp->dpp_func)) { > - /* > - * If we fail to lookup the symbol, try interpreting the > - * function as the special "-" function that indicates that the > - * probe name should be interpreted as a absolute virtual > - * address. If that fails and we were matching a specific > - * function in a specific module, report the error, otherwise > - * just fail silently in the hopes that some other object will > - * contain the desired symbol. > + /* If we are matching a specific function in a specific module, > + * report the error, otherwise just fail silently in the hopes > + * that some other object will contain the desired symbol. > */ > if (dt_Pxlookup_by_name(dtp, pid, pp->dpp_lmid, obj, > pp->dpp_func, &sym, NULL) != 0) { > - if (strcmp("-", pp->dpp_func) == 0) { > - sym.st_name = 0; > - sym.st_info = > - GELF_ST_INFO(STB_LOCAL, STT_FUNC); > - sym.st_other = 0; > - sym.st_value = 0; > - sym.st_size = Pelf64(pp->dpp_pr) ? -1ULL : -1U; > - } else if (!strisglob(pp->dpp_mod)) { > + if (!strisglob(pp->dpp_mod)) { > return dt_pid_error( > dtp, pcb, dpr, D_PROC_FUNC, > "failed to lookup '%s' in module '%s'", > @@ -647,9 +679,10 @@ dt_pid_create_pid_probes_proc(dtrace_probedesc_t *pdp, dtrace_hdl_t *dtp, > if (strcmp(pp.dpp_func, "-") == 0) { > const prmap_t *aout, *pmp; > > - if (pdp->mod[0] == '\0') { > - pp.dpp_mod = pdp->mod; > + if (strcmp(pp.dpp_mod, "*") == 0) { > + /* Tolerate two glob cases: "" and "*". */ > pdp->mod = "a.out"; > + pp.dpp_mod = pdp->mod; > } else if (strisglob(pp.dpp_mod) || > (aout = dt_Pname_to_map(dtp, pid, "a.out")) == NULL || > (pmp = dt_Pname_to_map(dtp, pid, pp.dpp_mod)) == NULL || > diff --git a/libdtrace/dt_prov_uprobe.c b/libdtrace/dt_prov_uprobe.c > index 11d595898..e96479963 100644 > --- a/libdtrace/dt_prov_uprobe.c > +++ b/libdtrace/dt_prov_uprobe.c > @@ -1,6 +1,6 @@ > /* > * Oracle Linux DTrace. > - * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. > + * Copyright (c) 2021, 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. > * > @@ -55,7 +55,8 @@ static const char prvname[] = "uprobe"; > typedef struct dt_uprobe { > dev_t dev; > ino_t inum; > - char *fn; > + char *fn; /* object full file name */ > + char *func; /* function */ > uint64_t off; > int flags; > tp_probe_t *tp; > @@ -128,6 +129,7 @@ static void probe_destroy_underlying(dtrace_hdl_t *dtp, void *datap) > dt_tp_destroy(dtp, tpp); > free_probe_list(dtp, dt_list_next(&upp->probes)); > dt_free(dtp, upp->fn); > + dt_free(dtp, upp->func); > dt_free(dtp, upp->args); > dt_free(dtp, upp->argvbuf); > dt_free(dtp, upp); > @@ -333,9 +335,13 @@ ignore_clause(dtrace_hdl_t *dtp, int n, const dt_probe_t *uprp) > */ > > /* We know what function we're in. It must match the probe description (unless "-"). */ > - if (strcmp(pdp->fun, "-") != 0 && > - !dt_gmatch(uprp->desc->fun, pdp->fun)) > - return 1; > + if (strcmp(pdp->fun, "-") != 0) { > + dt_uprobe_t *upp = uprp->prv_data; > + > + assert(upp->func); // never a return probe > + if (!dt_gmatch(upp->func, pdp->fun)) > + return 1; > + } > > return 0; > } > @@ -623,11 +629,11 @@ static dt_probe_t *create_underlying(dtrace_hdl_t *dtp, > * > * The probe description for non-return probes is: > * > - * uprobe:_:: > + * uprobe:_:: > * > * The probe description for return probes is: > * > - * uprobe:_::return > + * uprobe:_::return > */ > snprintf(mod, sizeof(mod), "%lx_%lx", psp->pps_dev, psp->pps_inum); > > @@ -638,6 +644,7 @@ static dt_probe_t *create_underlying(dtrace_hdl_t *dtp, > case DTPPT_IS_ENABLED: > case DTPPT_ENTRY: > case DTPPT_OFFSETS: > + case DTPPT_ABSOFFSETS: > case DTPPT_USDT: > snprintf(prb, sizeof(prb), "%lx", psp->pps_off); > break; > @@ -649,7 +656,7 @@ static dt_probe_t *create_underlying(dtrace_hdl_t *dtp, > pd.id = DTRACE_IDNONE; > pd.prv = prvname; > pd.mod = mod; > - pd.fun = psp->pps_fun; > + pd.fun = ""; > pd.prb = prb; > > dt_dprintf("Providing underlying probe %s:%s:%s:%s @ %lx\n", psp->pps_prv, > @@ -672,6 +679,7 @@ static dt_probe_t *create_underlying(dtrace_hdl_t *dtp, > upp->inum = psp->pps_inum; > upp->off = psp->pps_off; > upp->fn = strdup(psp->pps_fn); > + upp->func = NULL; > upp->tp = dt_tp_alloc(dtp); > if (upp->tp == NULL) > goto fail; > @@ -692,6 +700,17 @@ static dt_probe_t *create_underlying(dtrace_hdl_t *dtp, > goto fail; > } > > + /* > + * The underlying probe should have the same function for all > + * overlying probes unless it's a return probe. > + */ > + if (psp->pps_type != DTPPT_RETURN) { > + if (upp->func == NULL) > + upp->func = strdup(psp->pps_fun); > + else > + assert(strcmp(upp->func, psp->pps_fun) == 0); > + } > + > if (populate_args(dtp, psp, upp) < 0) > goto fail; > > @@ -735,7 +754,7 @@ static int provide_probe(dtrace_hdl_t *dtp, const pid_probespec_t *psp, > pd.id = DTRACE_IDNONE; > pd.prv = prv; > pd.mod = psp->pps_mod; > - pd.fun = psp->pps_fun; > + pd.fun = (psp->pps_type == DTPPT_ABSOFFSETS) ? "-" : psp->pps_fun; > pd.prb = prb; > > /* Get (or create) the provider for the PID of the probe. */ > @@ -823,6 +842,7 @@ static int provide_pid_probe(dtrace_hdl_t *dtp, const pid_probespec_t *psp) > strcpy(prb, "return"); > break; > case DTPPT_OFFSETS: > + case DTPPT_ABSOFFSETS: > snprintf(prb, sizeof(prb), "%lx", psp->pps_nameoff); > break; > default: > diff --git a/test/unittest/pid/tst.dash.r b/test/unittest/pid/tst.dash.r > new file mode 100644 > index 000000000..2e9ba477f > --- /dev/null > +++ b/test/unittest/pid/tst.dash.r > @@ -0,0 +1 @@ > +success > diff --git a/test/unittest/pid/tst.dash.sh b/test/unittest/pid/tst.dash.sh > new file mode 100755 > index 000000000..f364e3e3c > --- /dev/null > +++ b/test/unittest/pid/tst.dash.sh > @@ -0,0 +1,119 @@ > +#!/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. > +# > +# @@timeout: 80 > + > +dtrace=$1 > + > +DIRNAME=$tmpdir/pid-dash.$$.$RANDOM > +mkdir $DIRNAME > +cd $DIRNAME > + > +# Make trigger program. > + > +cat << EOF > main.c > +int foo0(int i) { > + int j, k; > + > + j = i ^ 1; k = j ^ 1; i = k ^ 1; > + j = i ^ 1; k = j ^ 1; i = k ^ 1; > + j = i ^ 1; k = j ^ 1; i = k ^ 1; > + > + return i; > +} > +int foo1(int i) { > + int j, k; > + > + j = i ^ 1; k = j ^ 1; i = k ^ 1; > + j = i ^ 1; k = j ^ 1; i = k ^ 1; > + j = i ^ 1; k = j ^ 1; i = k ^ 1; > + > + return i; > +} > +int foo2(int i) { > + int j, k; > + > + j = i ^ 1; k = j ^ 1; i = k ^ 1; > + j = i ^ 1; k = j ^ 1; i = k ^ 1; > + j = i ^ 1; k = j ^ 1; i = k ^ 1; > + > + return i; > +} > +int main(int c, char **v) { > + int i = 0; > + > + i = foo0(i) ^ i; > + i = foo1(i) ^ i; > + i = foo2(i) ^ i; > + > + return i; > +} > +EOF > + > +gcc main.c > +if [ $? -ne 0 ]; then > + echo ERROR compile > + exit 1 > +fi > + > +# Loop over functions in the program. > + > +for func in foo0 foo1 foo2 main; do > + # For each function, get the absolute and relative > + # (to the function) address of some instruction in > + # the function. > + read ABS REL <<< `objdump -d a.out | awk ' > + # Look for the function. > + /^[0-9a-f]* <'$func'>:$/ { > + > + # Get the first instruction and note the base address of the function. > + getline; sub(":", ""); base = strtonum("0x"$1); > + > + # Get the next instruction. > + getline; > + > + # Get the next instruction. Note its PC. > + getline; sub(":", ""); pc = strtonum("0x"$1); > + > + # Print the address, both absolute and relative. > + printf("%x %x\n", pc, pc - base); > + exit(0); > + }'` > + > + # Write the expected output to the compare file. > + echo got $ABS $func:$REL >> dtrace.exp > + echo got $ABS "-":$ABS >> dtrace.exp > + > + # Write the actual dtrace output to the output file. > + # Specify the pid probe with both relative and absolute > + # forms. > + for probe in $func:$REL "-:$ABS"; do > + $dtrace -c ./a.out -o dtrace.out -qn ' > + pid$target:a.out:'$probe' > + { printf("got %x %s:%s", uregs[R_PC], probefunc, > + probename); }' > + if [ $? -ne 0 ]; then > + echo ERROR: dtrace > + cat dtrace.out > + exit 1 > + fi > + done > +done > + > +# Check results. > + > +if ! diff -q dtrace.exp dtrace.out; then > + echo ERROR: > + echo "==== expected" > + cat dtrace.exp > + echo "==== actual" > + cat dtrace.out > + exit 1 > +fi > + > +echo success > +exit 0 > diff --git a/test/unittest/usdt/tst.pidprobes.sh b/test/unittest/usdt/tst.pidprobes.sh > index 54444d49b..0c75d7967 100755 > --- a/test/unittest/usdt/tst.pidprobes.sh > +++ b/test/unittest/usdt/tst.pidprobes.sh > @@ -1,7 +1,7 @@ > #!/bin/bash > # > # Oracle Linux DTrace. > -# Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. > +# Copyright (c) 2024, 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. > # > @@ -121,7 +121,13 @@ fi > pcs=`awk '{print strtonum("0x"$1)}' disasm_foo.txt` > pc0=`echo $pcs | awk '{print $1}'` > > -# Run dtrace. > +# Construct D script: add a pid$pid::-:$absoff probe for each PC in foo. > + > +for pc in $pcs; do > + printf 'p*d$target::-:%x,\n' $pc >> pidprobes.d > +done > + > +# Construct D script: add a glob for all pid and USDT pyramid probes in foo. > > cat >> pidprobes.d <<'EOF' > p*d$target::foo: > @@ -130,6 +136,8 @@ p*d$target::foo: > } > EOF > > +# Construct D script: add a glob for all USDT pyramid probes, dumping args. > + > if [[ -n $usdt ]]; then > echo 'pyramid$target::foo: {' >> pidprobes.d > > @@ -141,9 +149,12 @@ if [[ -n $usdt ]]; then > echo '}' >> pidprobes.d > fi > > +# Run dtrace. > + > $dtrace $dt_flags -q -c ./main -o dtrace.out -s pidprobes.d > main.out2 > if [ $? -ne 0 ]; then > echo "failed to run dtrace" >&2 > + cat pidprobes.d > cat main.out2 > cat dtrace.out > exit 1 > @@ -286,14 +297,16 @@ fi > # - a blank line > # - pid entry > # - pid return > -# - pid offset > +# - pid offset (relative -- that is, pid$pid:main:foo:$reloff) > +# - pid offset (absolute -- that is, pid$pid:main:-:$absoff) > # - two USDT probes (ignore is-enabled probes) > > echo > dtrace.out.expected > -printf "$pid pid$pid:main:foo:entry %x\n" $pc0 >> dtrace.out.expected > +printf "$pid pid$pid:main:foo:entry %x\n" $pc0 >> dtrace.out.expected > echo "$pid pid$pid:main:foo:return $pc_return" >> dtrace.out.expected > for pc in $pcs; do > printf "$pid pid$pid:main:foo:%x %x\n" $(($pc - $pc0)) $pc >> dtrace.out.expected > + printf "$pid pid$pid:main:-:%x %x\n" $pc $pc >> dtrace.out.expected > done > echo $usdt_pcs | awk '{printf("'$pid' pyramid'$pid':main:foo:entry %x\n", $1);}' >> dtrace.out.expected > echo $usdt_pcs | awk '{printf("'$pid' pyramid'$pid':main:foo:entry %x\n", $2);}' >> dtrace.out.expected > -- > 2.43.5 > From kris.van.hees at oracle.com Wed Feb 26 16:53:49 2025 From: kris.van.hees at oracle.com (Kris Van Hees) Date: Wed, 26 Feb 2025 11:53:49 -0500 Subject: [DTrace-devel] [PATCH] Move proc lock to where we actually find a USDT process In-Reply-To: <20250106155525.15499-1-eugene.loh@oracle.com> References: <20250106155525.15499-1-eugene.loh@oracle.com> Message-ID: On Mon, Jan 06, 2025 at 10:55:25AM -0500, eugene.loh--- via DTrace-devel wrote: > From: Eugene Loh > > The function dt_pid_create_usdt_probes_proc() creates USDT probes > for a specific process. It is called in one of two ways: > > dt_proc_scan(dtp, dpr) -> > dt_pid_create_probes_module(dtp, dpr) > > The process has been locked and we are updating its > USDT probes. > > dt_pid_create_usdt_probes(pdp, dtp, pcb) > > Here, we look for any pids that might have USDT probes, > calling the pid-specific function for each candidate. > > One problem is that the first code path assumes the process is locked. > This means the second code path has to lock processes even before we > know if it has any USDT probes we care about. > > Change dt_pid_create_usdt_probes_proc() so the caller can specify the > process in either one of two ways: > > by pid (implying the process has not been locked) > > by dpr (implying the process has been locked) > > In the first case, the function will lock the process, but only if > USDT probes have been found. > > Signed-off-by: Eugene Loh Reviewed-by: Kris Van Hees > --- > libdtrace/dt_pid.c | 70 +++++++++++++++++++++++++++------------------- > 1 file changed, 42 insertions(+), 28 deletions(-) > > diff --git a/libdtrace/dt_pid.c b/libdtrace/dt_pid.c > index 8110ccead..6db882059 100644 > --- a/libdtrace/dt_pid.c > +++ b/libdtrace/dt_pid.c > @@ -782,33 +782,42 @@ validate_dof_record(const char *path, const dof_parsed_t *parsed, > > > /* > - * Create underlying probes relating to the probespec passed on input. > + * Create underlying probes relating to the probe description passed on input. > + * Just set up probes relating to mappings found in this one process. > * > - * dpr must be set and locked. Just set up probes relating to mappings found > - * in this one process. > + * Either the pid must be specified or else dpr must be set and locked. > * > * Return 0 on success or -1 on error. (Failure to create specific underlying > * probes is not an error.) > */ > static int > -dt_pid_create_usdt_probes_proc(dtrace_hdl_t *dtp, dt_proc_t *dpr, > +dt_pid_create_usdt_probes_proc(dtrace_hdl_t *dtp, pid_t pid, dt_proc_t *dpr, > dtrace_probedesc_t *pdp, dt_pcb_t *pcb) > { > const dt_provider_t *pvp; > int ret = 0; > + int dpr_caller; /* dpr was set by caller */ > char *probepath = NULL; > glob_t probeglob = {0}; > > - assert(dpr != NULL && dpr->dpr_proc); > - assert(MUTEX_HELD(&dpr->dpr_lock)); > + if (dpr == NULL) { > + assert(pid != -1); > + dpr_caller = 0; > + } else { > + assert(pid == -1); > + assert(dpr->dpr_proc); > + assert(MUTEX_HELD(&dpr->dpr_lock)); > + pid = dpr->dpr_pid; > + dpr_caller = 1; > + } > > dt_dprintf("Scanning for usdt probes in %i matching %s:%s:%s\n", > - dpr->dpr_pid, pdp->mod, pdp->fun, pdp->prb); > + pid, pdp->mod, pdp->fun, pdp->prb); > > pvp = dt_provider_lookup(dtp, "usdt"); > assert(pvp != NULL); > > - if (Pstate(dpr->dpr_proc) == PS_DEAD) > + if (dpr != NULL && Pstate(dpr->dpr_proc) == PS_DEAD) > return 0; > > /* > @@ -835,7 +844,7 @@ dt_pid_create_usdt_probes_proc(dtrace_hdl_t *dtp, dt_proc_t *dpr, > assert(pvp->impl != NULL && pvp->impl->provide_probe != NULL); > > if (asprintf(&probepath, "%s/probes/%i/%s/%s/%s/%s", dtp->dt_dofstash_path, > - dpr->dpr_pid, pdp->prv[0] == '\0' ? "*" : pdp->prv, > + pid, pdp->prv[0] == '\0' ? "*" : pdp->prv, > pdp->mod[0] == '\0' ? "*" : pdp->mod, > pdp->fun[0] == '\0' ? "*" : pdp->fun, > pdp->prb[0] == '\0' ? "*" : pdp->prb) < 0) > @@ -858,6 +867,19 @@ dt_pid_create_usdt_probes_proc(dtrace_hdl_t *dtp, dt_proc_t *dpr, > return 0; > } > > + /* Set dpr and grab the process, if necessary. */ > + if (dpr_caller == 0) { > + if (dt_proc_grab_lock(dtp, pid, DTRACE_PROC_WAITING | > + DTRACE_PROC_SHORTLIVED) < 0) { > + dt_pid_error(dtp, pcb, NULL, D_PROC_GRAB, > + "failed to grab process %d", (int)pid); > + return -1; > + } > + dpr = dt_proc_lookup(dtp, pid); > + assert(dpr != NULL); > + } > + > + /* Loop over USDT probes. */ > for (size_t i = 0; i < probeglob.gl_pathc; i++) { > char *dof_buf = NULL, *p; > struct stat s; > @@ -1051,10 +1073,16 @@ dt_pid_create_usdt_probes_proc(dtrace_hdl_t *dtp, dt_proc_t *dpr, > free(path); > free(dof_buf); > globfree(&probeglob); > + if (dpr_caller == 0) > + dt_proc_release_unlock(dtp, pid); > return -1; > } > > globfree(&probeglob); > + if (dpr_caller == 0) { > + dt_pid_fix_mod(NULL, pdp, dtp, pid); > + dt_proc_release_unlock(dtp, pid); > + } > return ret; > > scan_err: > @@ -1237,7 +1265,6 @@ dt_pid_create_usdt_probes(dtrace_probedesc_t *pdp, dtrace_hdl_t *dtp, dt_pcb_t * > + strlen(dtp->dt_dofstash_path) > + strlen("/probes/"); > pid_t pid; > - dt_proc_t *dpr; > dtrace_probedesc_t pdptmp; > > /* Pull out the pid. */ > @@ -1247,28 +1274,15 @@ dt_pid_create_usdt_probes(dtrace_probedesc_t *pdp, dtrace_hdl_t *dtp, dt_pcb_t * > if (!Pexists(pid)) > continue; > > - /* Grab the process. */ > - if (dt_proc_grab_lock(dtp, pid, DTRACE_PROC_WAITING | > - DTRACE_PROC_SHORTLIVED) < 0) { > - dt_pid_error(dtp, pcb, NULL, D_PROC_GRAB, > - "failed to grab process %d", (int)pid); > - err = 1; // FIXME but do we want to set the error if we end up return 0? > - continue; > - } > - dpr = dt_proc_lookup(dtp, pid); > - assert(dpr != NULL); > - > - /* Create USDT probes for this process. */ > + /* Construct the probe descriptor. */ > pdptmp.prv = strchr(s, '/') + 1; > pdptmp.mod = pdp->mod[0] == '\0' ? "*" : pdp->mod; > pdptmp.fun = pdp->fun[0] == '\0' ? "*" : pdp->fun; > pdptmp.prb = pdp->prb[0] == '\0' ? "*" : pdp->prb; > - if (dt_pid_create_usdt_probes_proc(dtp, dpr, &pdptmp, pcb)) > - err = 1; > - > - dt_pid_fix_mod(NULL, &pdptmp, dtp, dpr->dpr_pid); > > - dt_proc_release_unlock(dtp, pid); > + /* Create USDT probes for this process. */ > + if (dt_pid_create_usdt_probes_proc(dtp, pid, NULL, &pdptmp, pcb)) > + err = 1; > } > free(globpat); > globfree(&globbuf); > @@ -1341,7 +1355,7 @@ dt_pid_create_probes_module(dtrace_hdl_t *dtp, dt_proc_t *dpr) > * a USDT provider. > */ > if (strcmp(provname, pdp->prv) != 0) { > - if (dt_pid_create_usdt_probes_proc(dtp, dpr, pdp, NULL) < 0) > + if (dt_pid_create_usdt_probes_proc(dtp, -1, dpr, pdp, NULL) < 0) > ret = 1; > else > dt_pid_fix_mod(NULL, pdp, dtp, dpr->dpr_pid); > -- > 2.43.5 > > > _______________________________________________ > DTrace-devel mailing list > DTrace-devel at oss.oracle.com > https://oss.oracle.com/mailman/listinfo/dtrace-devel From kris.van.hees at oracle.com Wed Feb 26 16:54:38 2025 From: kris.van.hees at oracle.com (Kris Van Hees) Date: Wed, 26 Feb 2025 11:54:38 -0500 Subject: [DTrace-devel] [PATCH] test: Have tst.ucaller use a predictable trigger In-Reply-To: <20250110190306.12814-1-eugene.loh@oracle.com> References: <20250110190306.12814-1-eugene.loh@oracle.com> Message-ID: On Fri, Jan 10, 2025 at 02:03:06PM -0500, eugene.loh--- via DTrace-devel wrote: > From: Eugene Loh > > Signed-off-by: Eugene Loh Reviewed-by: Kris Van Hees > --- > test/unittest/vars/tst.ucaller.d | 16 ++++++++++++ > test/unittest/vars/tst.ucaller.r | 2 +- > test/unittest/vars/tst.ucaller.sh | 42 ------------------------------- > test/unittest/vars/tst.ucaller.x | 1 - > 4 files changed, 17 insertions(+), 44 deletions(-) > create mode 100644 test/unittest/vars/tst.ucaller.d > delete mode 100755 test/unittest/vars/tst.ucaller.sh > delete mode 120000 test/unittest/vars/tst.ucaller.x > > diff --git a/test/unittest/vars/tst.ucaller.d b/test/unittest/vars/tst.ucaller.d > new file mode 100644 > index 000000000..271297fef > --- /dev/null > +++ b/test/unittest/vars/tst.ucaller.d > @@ -0,0 +1,16 @@ > +/* > + * 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. > + */ > + > +/* @@trigger: ustack-tst-basic */ > + > +#pragma D option quiet > + > +pid$target:a.out:myfunc_x:entry > +{ > + ufunc(ucaller); > + exit(0); > +} > diff --git a/test/unittest/vars/tst.ucaller.r b/test/unittest/vars/tst.ucaller.r > index 9b09daa0c..bfcc80f4d 100644 > --- a/test/unittest/vars/tst.ucaller.r > +++ b/test/unittest/vars/tst.ucaller.r > @@ -1 +1 @@ > - libc.so.6`strdup > + ustack-tst-basic`myfunc_v > diff --git a/test/unittest/vars/tst.ucaller.sh b/test/unittest/vars/tst.ucaller.sh > deleted file mode 100755 > index a6376af46..000000000 > --- a/test/unittest/vars/tst.ucaller.sh > +++ /dev/null > @@ -1,42 +0,0 @@ > -#!/bin/bash > -# > -# Oracle Linux DTrace. > -# Copyright (c) 2007, 2020, 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 is a bit naughty; it's assuming that libc.so has an implementation > -# of strup(3), and that it's implemented in terms of the libc.so > -# implementation of malloc(3). If you're reading this comment because > -# those assumptions have become false, please accept my apologies... > -# > -# @@xfail: dtv2 > - > -if [ $# != 1 ]; then > - echo expected one argument: '<'dtrace-path'>' > - exit 2 > -fi > - > -dtrace=$1 > - > -$dtrace $dt_flags -qs /dev/stdin -c "/bin/wc -l /dev/zero" < -pid\$target::strdup:entry > -{ > - self->strdup = 1; > -} > - > -pid\$target:libc.so:malloc:entry > -/self->strdup/ > -{ > - ufunc(ucaller); > - exit(0); > -} > - > -pid\$target::strdup:return > -/self->strdup/ > -{ > - self->strdup = 0; > -} > -EOF > - > -exit 0 > diff --git a/test/unittest/vars/tst.ucaller.x b/test/unittest/vars/tst.ucaller.x > deleted file mode 120000 > index 6507ccd87..000000000 > --- a/test/unittest/vars/tst.ucaller.x > +++ /dev/null > @@ -1 +0,0 @@ > -../pid/test.x > \ No newline at end of file > -- > 2.43.5 > > > _______________________________________________ > DTrace-devel mailing list > DTrace-devel at oss.oracle.com > https://oss.oracle.com/mailman/listinfo/dtrace-devel From kris.van.hees at oracle.com Wed Feb 26 16:55:18 2025 From: kris.van.hees at oracle.com (Kris Van Hees) Date: Wed, 26 Feb 2025 11:55:18 -0500 Subject: [DTrace-devel] [PATCH] Convert some indenting spaces into tabs In-Reply-To: <20250114233820.5501-1-eugene.loh@oracle.com> References: <20250114233820.5501-1-eugene.loh@oracle.com> Message-ID: On Tue, Jan 14, 2025 at 06:38:20PM -0500, eugene.loh at oracle.com wrote: > From: Eugene Loh > > Signed-off-by: Eugene Loh Reviewed-by: Kris Van Hees > --- > libdtrace/dt_prov_uprobe.c | 22 +++++++++++----------- > 1 file changed, 11 insertions(+), 11 deletions(-) > > diff --git a/libdtrace/dt_prov_uprobe.c b/libdtrace/dt_prov_uprobe.c > index e96479963..5d9f74244 100644 > --- a/libdtrace/dt_prov_uprobe.c > +++ b/libdtrace/dt_prov_uprobe.c > @@ -593,7 +593,7 @@ static int populate_args(dtrace_hdl_t *dtp, const pid_probespec_t *psp, > upp->args[i].mapping = map_arg; > upp->args[i].flags = 0; > > - if (i != map_arg) > + if (i != map_arg) > upp->flags |= PP_IS_MAPPED; > } > > @@ -716,21 +716,21 @@ static dt_probe_t *create_underlying(dtrace_hdl_t *dtp, > > switch (psp->pps_type) { > case DTPPT_RETURN: > - upp->flags |= PP_IS_RETURN; > - break; > + upp->flags |= PP_IS_RETURN; > + break; > case DTPPT_IS_ENABLED: > - upp->flags |= PP_IS_ENABLED; > - break; > + upp->flags |= PP_IS_ENABLED; > + break; > case DTPPT_USDT: > - upp->flags |= PP_IS_USDT; > - break; > + upp->flags |= PP_IS_USDT; > + break; > default: ; > - /* > - * No flags needed for other types. > - */ > + /* > + * No flags needed for other types. > + */ > } > > - return uprp; > + return uprp; > > fail: > dt_dprintf("Failed to instantiate %s:%s:%s:%s\n", psp->pps_prv, > -- > 2.43.5 > From kris.van.hees at oracle.com Wed Feb 26 16:57:36 2025 From: kris.van.hees at oracle.com (Kris Van Hees) Date: Wed, 26 Feb 2025 11:57:36 -0500 Subject: [DTrace-devel] [PATCH v2] Fix trampoline use of BPF stack for scratch In-Reply-To: <20250116231414.32553-1-eugene.loh@oracle.com> References: <20250116231414.32553-1-eugene.loh@oracle.com> Message-ID: On Thu, Jan 16, 2025 at 06:14:14PM -0500, eugene.loh at oracle.com wrote: > From: Eugene Loh > > The trampoline starts by setting %r9 = %fp + -DCTX_SIZE -- that is, > %r9 = %fp - 88. Then, it fills the BPF stack from %fp-88 to %fp with > the various dctx-> pointers. Calls to clauses will reset %fp. > > The proc provider's trampoline was using the BPF stack for scratch > space for the exit and signal-handle probes. Specifically, it used > %fp + DT_STK_SPILL(0), which overwrites a dctx-> pointer. > > Switch to DT_TRAMP_SP_SLOT(0), which is intended for this purpose. > > Signed-off-by: Eugene Loh Reviewed-by: Kris Van Hees > --- > libdtrace/dt_prov_proc.c | 14 +++++++------- > test/unittest/lquantize/tst.32bit-bug26268136.sh | 1 - > 2 files changed, 7 insertions(+), 8 deletions(-) > > diff --git a/libdtrace/dt_prov_proc.c b/libdtrace/dt_prov_proc.c > index 0223c5d18..9fc499aeb 100644 > --- a/libdtrace/dt_prov_proc.c > +++ b/libdtrace/dt_prov_proc.c > @@ -62,7 +62,7 @@ static const char modname[] = "vmlinux"; > * The dependent probe support should include a priority specification to drive > * the order in which dependent probes are added to the underlying probe. This > * is needed to enforce specific probe firing semantics (e.g. proc:::start must > - * always precede [roc:::lwp-start). > + * always precede proc:::lwp-start). > */ > > typedef struct probe_arg { > @@ -350,12 +350,12 @@ static int trampoline(dt_pcb_t *pcb, uint_t exitlbl) > * else args[0] = 2; // CLD_KILLED > */ > emit(dlp, BPF_MOV_REG(BPF_REG_1, BPF_REG_FP)); > - emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, DT_STK_SPILL(0))); > + emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, DT_TRAMP_SP_SLOT(0))); > emit(dlp, BPF_MOV_IMM(BPF_REG_2, sizeof(int))); > emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_3, BPF_REG_7, DMST_ARG(0))); > emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, ctm.ctm_offset / NBBY)); > emit(dlp, BPF_CALL_HELPER(BPF_FUNC_probe_read)); > - emit(dlp, BPF_LOAD(BPF_W, BPF_REG_1, BPF_REG_FP, DT_STK_SPILL(0))); > + emit(dlp, BPF_LOAD(BPF_W, BPF_REG_1, BPF_REG_FP, DT_TRAMP_SP_SLOT(0))); > emit(dlp, BPF_MOV_IMM(BPF_REG_0, 1)); > emit(dlp, BPF_MOV_REG(BPF_REG_2, BPF_REG_1)); > emit(dlp, BPF_ALU64_IMM(BPF_AND, BPF_REG_2, 0x7f)); > @@ -406,17 +406,17 @@ static int trampoline(dt_pcb_t *pcb, uint_t exitlbl) > emit(dlp, BPF_MOV_REG(BPF_REG_3, BPF_REG_0)); > emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, off)); > emit(dlp, BPF_MOV_REG(BPF_REG_1, BPF_REG_FP)); > - emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, DT_STK_SPILL(0))); > + emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, DT_TRAMP_SP_SLOT(0))); > emit(dlp, BPF_MOV_IMM(BPF_REG_2, sz)); > emit(dlp, BPF_CALL_HELPER(BPF_FUNC_probe_read)); > - emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_3, BPF_REG_FP, DT_STK_SPILL(0))); > + emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_3, BPF_REG_FP, DT_TRAMP_SP_SLOT(0))); > off = dt_cg_ctf_offsetof("struct signal_struct", "group_exit_code", &sz, 0); > emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, off)); > emit(dlp, BPF_MOV_REG(BPF_REG_1, BPF_REG_FP)); > - emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, DT_STK_SPILL(0))); > + emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, DT_TRAMP_SP_SLOT(0))); > emit(dlp, BPF_MOV_IMM(BPF_REG_2, sz)); > emit(dlp, BPF_CALL_HELPER(BPF_FUNC_probe_read)); > - emit(dlp, BPF_LOAD(BPF_W, BPF_REG_0, BPF_REG_FP, DT_STK_SPILL(0))); > + emit(dlp, BPF_LOAD(BPF_W, BPF_REG_0, BPF_REG_FP, DT_TRAMP_SP_SLOT(0))); > emit(dlp, BPF_BRANCH_IMM(BPF_JEQ, BPF_REG_0, 0, lbl_keep)); > emit(dlp, BPF_STORE(BPF_DW, BPF_REG_7, DMST_ARG(0), BPF_REG_0)); > > diff --git a/test/unittest/lquantize/tst.32bit-bug26268136.sh b/test/unittest/lquantize/tst.32bit-bug26268136.sh > index d5f143f58..a360fd17e 100755 > --- a/test/unittest/lquantize/tst.32bit-bug26268136.sh > +++ b/test/unittest/lquantize/tst.32bit-bug26268136.sh > @@ -5,7 +5,6 @@ > # Licensed under the Universal Permissive License v 1.0 as shown at > # http://oss.oracle.com/licenses/upl. > # > -# @@xfail: dtv2 > > if [ $# != 1 ]; then > echo expected one argument: '<'dtrace-path'>' > -- > 2.43.5 > From kris.van.hees at oracle.com Wed Feb 26 16:58:25 2025 From: kris.van.hees at oracle.com (Kris Van Hees) Date: Wed, 26 Feb 2025 11:58:25 -0500 Subject: [DTrace-devel] [PATCH] test: Improve resilience of tests to ptr widths In-Reply-To: <20250128213621.2617-1-eugene.loh@oracle.com> References: <20250128213621.2617-1-eugene.loh@oracle.com> Message-ID: On Tue, Jan 28, 2025 at 04:36:21PM -0500, eugene.loh at oracle.com wrote: > From: Eugene Loh > > When tests are run, runtest.sh runs postprocess(), which among > other things turns irreproducible hex strings (presumably offsets > and pointers) into fixed strings (like "ptr") for correctness > checking. In a few cases, however, there are trailing blanks > to justify the right margin, meaning that the width of the hex > string can cause the number of trailing blanks to vary. > > Improve the resilience of these tests by stripping out such > trailing blanks. > > Signed-off-by: Eugene Loh Reviewed-by: Kris Van Hees > --- > test/unittest/ustack/tst.uaddr-pid0.r | 2 +- > test/unittest/ustack/tst.uaddr-pid0.r.p | 4 ++++ > test/unittest/ustack/tst.uaddr.r | 2 +- > test/unittest/ustack/tst.uaddr.r.p | 4 ++++ > test/unittest/ustack/tst.ufunc-pid0.r | 2 +- > test/unittest/ustack/tst.ufunc-pid0.r.p | 4 ++++ > test/unittest/ustack/tst.usym-pid0.r | 2 +- > test/unittest/ustack/tst.usym-pid0.r.p | 4 ++++ > 8 files changed, 20 insertions(+), 4 deletions(-) > create mode 100755 test/unittest/ustack/tst.uaddr-pid0.r.p > create mode 100755 test/unittest/ustack/tst.uaddr.r.p > create mode 100755 test/unittest/ustack/tst.ufunc-pid0.r.p > create mode 100755 test/unittest/ustack/tst.usym-pid0.r.p > > diff --git a/test/unittest/ustack/tst.uaddr-pid0.r b/test/unittest/ustack/tst.uaddr-pid0.r > index a8e68f4e6..cc1cdbc3d 100644 > --- a/test/unittest/ustack/tst.uaddr-pid0.r > +++ b/test/unittest/ustack/tst.uaddr-pid0.r > @@ -1 +1 @@ > - {ptr} > + {ptr} > diff --git a/test/unittest/ustack/tst.uaddr-pid0.r.p b/test/unittest/ustack/tst.uaddr-pid0.r.p > new file mode 100755 > index 000000000..9203dc824 > --- /dev/null > +++ b/test/unittest/ustack/tst.uaddr-pid0.r.p > @@ -0,0 +1,4 @@ > +#!/usr/bin/gawk -f > + > +# remove trailing blanks > +{ sub(" *$", ""); print } > diff --git a/test/unittest/ustack/tst.uaddr.r b/test/unittest/ustack/tst.uaddr.r > index be48a12ab..cc329e213 100644 > --- a/test/unittest/ustack/tst.uaddr.r > +++ b/test/unittest/ustack/tst.uaddr.r > @@ -1 +1 @@ > - ustack-tst-basic`myfunc_y+{ptr} > + ustack-tst-basic`myfunc_y+{ptr} > diff --git a/test/unittest/ustack/tst.uaddr.r.p b/test/unittest/ustack/tst.uaddr.r.p > new file mode 100755 > index 000000000..9203dc824 > --- /dev/null > +++ b/test/unittest/ustack/tst.uaddr.r.p > @@ -0,0 +1,4 @@ > +#!/usr/bin/gawk -f > + > +# remove trailing blanks > +{ sub(" *$", ""); print } > diff --git a/test/unittest/ustack/tst.ufunc-pid0.r b/test/unittest/ustack/tst.ufunc-pid0.r > index a8e68f4e6..cc1cdbc3d 100644 > --- a/test/unittest/ustack/tst.ufunc-pid0.r > +++ b/test/unittest/ustack/tst.ufunc-pid0.r > @@ -1 +1 @@ > - {ptr} > + {ptr} > diff --git a/test/unittest/ustack/tst.ufunc-pid0.r.p b/test/unittest/ustack/tst.ufunc-pid0.r.p > new file mode 100755 > index 000000000..9203dc824 > --- /dev/null > +++ b/test/unittest/ustack/tst.ufunc-pid0.r.p > @@ -0,0 +1,4 @@ > +#!/usr/bin/gawk -f > + > +# remove trailing blanks > +{ sub(" *$", ""); print } > diff --git a/test/unittest/ustack/tst.usym-pid0.r b/test/unittest/ustack/tst.usym-pid0.r > index a8e68f4e6..cc1cdbc3d 100644 > --- a/test/unittest/ustack/tst.usym-pid0.r > +++ b/test/unittest/ustack/tst.usym-pid0.r > @@ -1 +1 @@ > - {ptr} > + {ptr} > diff --git a/test/unittest/ustack/tst.usym-pid0.r.p b/test/unittest/ustack/tst.usym-pid0.r.p > new file mode 100755 > index 000000000..9203dc824 > --- /dev/null > +++ b/test/unittest/ustack/tst.usym-pid0.r.p > @@ -0,0 +1,4 @@ > +#!/usr/bin/gawk -f > + > +# remove trailing blanks > +{ sub(" *$", ""); print } > -- > 2.43.5 > From noreply at github.com Wed Feb 26 21:54:22 2025 From: noreply at github.com (Sam James) Date: Wed, 26 Feb 2025 13:54:22 -0800 Subject: [DTrace-devel] [oracle/dtrace-utils] 91258e: test: Move disassembly and extracting PCs earlier Message-ID: Branch: refs/heads/devel Home: https://github.com/oracle/dtrace-utils Commit: 91258e673b0b6bb2da23b7cb0f3af1e742e4a411 https://github.com/oracle/dtrace-utils/commit/91258e673b0b6bb2da23b7cb0f3af1e742e4a411 Author: Eugene Loh Date: 2025-02-25 (Tue, 25 Feb 2025) Changed paths: M test/unittest/usdt/tst.pidprobes.sh Log Message: ----------- test: Move disassembly and extracting PCs earlier This will allow a future patch to use the PCs earlier in the test. Signed-off-by: Eugene Loh Reviewed-by: Kris Van Hees Commit: 35a4f05c2abe05edaf964ad29c4fccbb482f5395 https://github.com/oracle/dtrace-utils/commit/35a4f05c2abe05edaf964ad29c4fccbb482f5395 Author: Eugene Loh Date: 2025-02-25 (Tue, 25 Feb 2025) Changed paths: M include/dtrace/pid.h M libdtrace/dt_pid.c M libdtrace/dt_prov_uprobe.c A test/unittest/pid/tst.dash.r A test/unittest/pid/tst.dash.sh M test/unittest/usdt/tst.pidprobes.sh Log Message: ----------- Add support for pid function "-" with absolute offset The pid providers allow users to specify a probe function "-", meaning that the probe name gives an absolute offset. Signed-off-by: Eugene Loh Reviewed-by: Kris Van Hees Commit: daa7c8431c3516beb50b3802ea4fd4f72b1acdcd https://github.com/oracle/dtrace-utils/commit/daa7c8431c3516beb50b3802ea4fd4f72b1acdcd Author: Eugene Loh Date: 2025-02-25 (Tue, 25 Feb 2025) Changed paths: M libdtrace/dt_pid.c Log Message: ----------- Move proc lock to where we actually find a USDT process The function dt_pid_create_usdt_probes_proc() creates USDT probes for a specific process. It is called in one of two ways: dt_proc_scan(dtp, dpr) -> dt_pid_create_probes_module(dtp, dpr) The process has been locked and we are updating its USDT probes. dt_pid_create_usdt_probes(pdp, dtp, pcb) Here, we look for any pids that might have USDT probes, calling the pid-specific function for each candidate. One problem is that the first code path assumes the process is locked. This means the second code path has to lock processes even before we know if it has any USDT probes we care about. Change dt_pid_create_usdt_probes_proc() so the caller can specify the process in either one of two ways: by pid (implying the process has not been locked) by dpr (implying the process has been locked) In the first case, the function will lock the process, but only if USDT probes have been found. Signed-off-by: Eugene Loh Reviewed-by: Kris Van Hees Commit: 097907a3f94d7c04540c70463d76227d4988cbe5 https://github.com/oracle/dtrace-utils/commit/097907a3f94d7c04540c70463d76227d4988cbe5 Author: Eugene Loh Date: 2025-02-25 (Tue, 25 Feb 2025) Changed paths: A test/unittest/vars/tst.ucaller.d M test/unittest/vars/tst.ucaller.r R test/unittest/vars/tst.ucaller.sh R test/unittest/vars/tst.ucaller.x Log Message: ----------- test: Have tst.ucaller use a predictable trigger Signed-off-by: Eugene Loh Reviewed-by: Kris Van Hees Commit: ee1e1a3437a2979335bf7a146d5330becfe2349b https://github.com/oracle/dtrace-utils/commit/ee1e1a3437a2979335bf7a146d5330becfe2349b Author: Eugene Loh Date: 2025-02-25 (Tue, 25 Feb 2025) Changed paths: M libdtrace/dt_prov_uprobe.c Log Message: ----------- Convert some indenting spaces into tabs Signed-off-by: Eugene Loh Reviewed-by: Kris Van Hees Commit: 6a9cface7fabf0d9ef9f7a4fbe4ea8ef3fd435c5 https://github.com/oracle/dtrace-utils/commit/6a9cface7fabf0d9ef9f7a4fbe4ea8ef3fd435c5 Author: Eugene Loh Date: 2025-02-26 (Wed, 26 Feb 2025) Changed paths: M libdtrace/dt_prov_proc.c M test/unittest/lquantize/tst.32bit-bug26268136.sh Log Message: ----------- Fix trampoline use of BPF stack for scratch The trampoline starts by setting %r9 = %fp + -DCTX_SIZE -- that is, %r9 = %fp - 88. Then, it fills the BPF stack from %fp-88 to %fp with the various dctx-> pointers. Calls to clauses will reset %fp. The proc provider's trampoline was using the BPF stack for scratch space for the exit and signal-handle probes. Specifically, it used %fp + DT_STK_SPILL(0), which overwrites a dctx-> pointer. Switch to DT_TRAMP_SP_SLOT(0), which is intended for this purpose. Signed-off-by: Eugene Loh Reviewed-by: Kris Van Hees Commit: 72a79b7843889fcf7b57cb5d4f27d8164b9e8b80 https://github.com/oracle/dtrace-utils/commit/72a79b7843889fcf7b57cb5d4f27d8164b9e8b80 Author: Eugene Loh Date: 2025-02-26 (Wed, 26 Feb 2025) Changed paths: M test/unittest/ustack/tst.uaddr-pid0.r A test/unittest/ustack/tst.uaddr-pid0.r.p M test/unittest/ustack/tst.uaddr.r A test/unittest/ustack/tst.uaddr.r.p M test/unittest/ustack/tst.ufunc-pid0.r A test/unittest/ustack/tst.ufunc-pid0.r.p M test/unittest/ustack/tst.usym-pid0.r A test/unittest/ustack/tst.usym-pid0.r.p Log Message: ----------- test: Improve resilience of tests to ptr widths When tests are run, runtest.sh runs postprocess(), which among other things turns irreproducible hex strings (presumably offsets and pointers) into fixed strings (like "ptr") for correctness checking. In a few cases, however, there are trailing blanks to justify the right margin, meaning that the width of the hex string can cause the number of trailing blanks to vary. Improve the resilience of these tests by stripping out such trailing blanks. Signed-off-by: Eugene Loh Reviewed-by: Kris Van Hees Commit: 5c0c8ed1360a5a78a06e52666d29fa14e47eba7f https://github.com/oracle/dtrace-utils/commit/5c0c8ed1360a5a78a06e52666d29fa14e47eba7f Author: Sam James Date: 2025-02-26 (Wed, 26 Feb 2025) Changed paths: M test/unittest/io/check_io_probe_args.sh M test/unittest/io/dump_io_probe_args.d Log Message: ----------- test: fix 'harness' typo Noticed when grepping for something else. Signed-off-by: Sam James Reviewed-by: Eugene Loh Commit: d33337525534ffaa95ae6c1c049fe3744d9bdff7 https://github.com/oracle/dtrace-utils/commit/d33337525534ffaa95ae6c1c049fe3744d9bdff7 Author: Sam James Date: 2025-02-26 (Wed, 26 Feb 2025) Changed paths: M test/unittest/bitfields/tst.bitfield-offset.x Log Message: ----------- test: fix bashism in tst.bitfield-offset.x trap ERR is a bashism and won't work with e.g. dash, just change the shebang to #!/bin/bash in this case as ERR is a bit messy to handle (it'd be trivial here but there's no real reason to not use bash). Signed-off-by: Sam James Reviewed-by: Eugene Loh Commit: 39a5e0a8866b38679619fa357bb3082bc245aada https://github.com/oracle/dtrace-utils/commit/39a5e0a8866b38679619fa357bb3082bc245aada Author: Sam James Date: 2025-02-26 (Wed, 26 Feb 2025) Changed paths: M test/unittest/builtinvar/tst.id_ERROR.sh M test/unittest/error/tst.clause_scope-regular.r.p M test/unittest/variables/bvar/tst.arg3-ERROR-b.sh M test/unittest/variables/bvar/tst.arg3-ERROR.sh Log Message: ----------- test: finalize porting small awk stragglers to gawk We missed some in 8a1264bf0e818c8624be250eb5174714b62ed93c because some other test changes landed before it landed. Signed-off-by: Sam James Reviewed-by: Eugene Loh Compare: https://github.com/oracle/dtrace-utils/compare/a6b626a89236...39a5e0a8866b To unsubscribe from these emails, change your notification settings at https://github.com/oracle/dtrace-utils/settings/notifications From kris.van.hees at oracle.com Thu Feb 27 16:27:30 2025 From: kris.van.hees at oracle.com (Kris Van Hees) Date: Thu, 27 Feb 2025 11:27:30 -0500 Subject: [DTrace-devel] [PATCH 1/4] Rename _DTRACE_VERSION In-Reply-To: <20250208190622.23484-1-eugene.loh@oracle.com> References: <20250208190622.23484-1-eugene.loh@oracle.com> Message-ID: On Sat, Feb 08, 2025 at 02:06:19PM -0500, eugene.loh at oracle.com wrote: > From: Eugene Loh > > There are many DTrace version numbers (for version, API version, > package version, etc.). Meanwhile, _DTRACE_VERSION is not a > version number at all. It's a preprocessor macro in USDT .h header > files. Prior to commit e2fb0ecd9 > ("Ensure multiple passes through dtrace -G work."), it was perhaps > not even set. With that commit, it was always set to 1, with > the rationale: > > Also add an explicit define for _DTRACE__VERSION in the generated > header file from 'dtrace -h' invocations. This seems silly, but > it is there to give people a skeleton to work with if they want to > pre-generate header files and select whether to actually compile > on the probes at a later time. > > Rename to _DTRACE_HEADER for better clarity. Define it only once > per file. Based on the rationale, I would think that something like _DTRACE_USE_USDT or somethng similar would be better? Since the purpose seems to be to allow (after the header file is generated) for someone to change that 1 to 0 to disable the USDT probes to be compiled in. In fact, if we want to support this in a more developer-friendly way, we might as well change the generted line to be more like: #ifndef _DTRACE_USE_USDT # define _DTARCE_USE_USDT 1 #endif so that a package could e.g. allow a configure option or something to set it to 1 or 0, dypassing the need to manually change the file. > Signed-off-by: Eugene Loh > --- > libdtrace/dt_program.c | 8 +++++--- > 1 file changed, 5 insertions(+), 3 deletions(-) > > diff --git a/libdtrace/dt_program.c b/libdtrace/dt_program.c > index 23b91fb2e..c6fdafb47 100644 > --- a/libdtrace/dt_program.c > +++ b/libdtrace/dt_program.c > @@ -505,13 +505,12 @@ dt_header_provider(dtrace_hdl_t *dtp, dt_provider_t *pvp, FILE *out) > info.dthi_pfname = alloca(strlen(pvp->desc.dtvd_name) + 1 + i); > dt_header_fmt_func(info.dthi_pfname, pvp->desc.dtvd_name); > > - if (fprintf(out, "#define _DTRACE_VERSION 1\n\n" > - "#if _DTRACE_VERSION\n\n") < 0) > + if (fprintf(out, "#if _DTRACE_HEADER\n\n") < 0) > return dt_set_errno(dtp, errno); > > if (dt_idhash_iter(pvp->pv_probes, dt_header_probe, &info) != 0) > return -1; /* dt_errno is set for us */ > - if (fprintf(out, "\n\n") < 0) > + if (fprintf(out, "\n") < 0) > return dt_set_errno(dtp, errno); > if (dt_idhash_iter(pvp->pv_probes, dt_header_decl, &info) != 0) > return -1; /* dt_errno is set for us */ > @@ -560,6 +559,9 @@ dtrace_program_header(dtrace_hdl_t *dtp, FILE *out, const char *fname) > "#endif\n\n") < 0) > return -1; > > + if (fprintf(out, "#define _DTRACE_HEADER 1\n\n") < 0) > + return -1; > + > while ((pvp = dt_htab_next(dtp->dt_provs, &it)) != NULL) { > if (dt_header_provider(dtp, pvp, out) != 0) { > dt_htab_next_destroy(it); > -- > 2.43.5 > From kris.van.hees at oracle.com Thu Feb 27 16:40:45 2025 From: kris.van.hees at oracle.com (Kris Van Hees) Date: Thu, 27 Feb 2025 11:40:45 -0500 Subject: [DTrace-devel] [PATCH 2/4] Eliminate DT_VERS_LATEST In-Reply-To: <20250208190622.23484-2-eugene.loh@oracle.com> References: <20250208190622.23484-1-eugene.loh@oracle.com> <20250208190622.23484-2-eugene.loh@oracle.com> Message-ID: On Sat, Feb 08, 2025 at 02:06:20PM -0500, eugene.loh at oracle.com wrote: > From: Eugene Loh > > Updating the DTrace version number requires too many distinct > changes. Eliminate DT_VERS_LATEST, since it can be determined > on the fly. > > Signed-off-by: Eugene Loh > --- > libdtrace/dt_open.c | 3 ++- > libdtrace/dt_version.h | 14 +++++--------- > 2 files changed, 7 insertions(+), 10 deletions(-) > > diff --git a/libdtrace/dt_open.c b/libdtrace/dt_open.c > index a02058871..b4d160359 100644 > --- a/libdtrace/dt_open.c > +++ b/libdtrace/dt_open.c > @@ -721,7 +721,8 @@ dt_vopen(int version, int flags, int *errp, > dtp->dt_proc_fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); > if (dt_aggregate_init(dtp) == -1) > return set_open_errno(dtp, errp, dtrace_errno(dtp)); > - dtp->dt_vmax = DT_VERS_LATEST; > + for (i = 0; _dtrace_versions[i] != 0; i++) > + dtp->dt_vmax = _dtrace_versions[i]; dtp->dt_vmax = _dtrace_versions[ARRAY_SIZE(_dtrace_versions) - 2]; (-2 to account for the 0 sentinel value) But this will only be accurate if you also add 2.0.1 to the _dtrace_versions array, and then you should probably add 2.0.2 to it also for accuracy. > dtp->dt_cpp_path = strdup(_dtrace_defcpp); > dtp->dt_cpp_argv = malloc(sizeof(char *)); > dtp->dt_cpp_argc = 1; > diff --git a/libdtrace/dt_version.h b/libdtrace/dt_version.h > index 3fd1b3d1e..bef3243e9 100644 > --- a/libdtrace/dt_version.h > +++ b/libdtrace/dt_version.h > @@ -38,18 +38,15 @@ extern "C" { > * > * These #defines are used in identifier tables to fill in the version fields > * associated with each identifier. The DT_VERS_* macros declare the encoded > - * integer values of all versions used so far. DT_VERS_LATEST must correspond > - * to the latest version value among all versions exported by the D compiler. > - * DT_VERS_STRING must be an ASCII string that contains DT_VERS_LATEST within > - * it along with any suffixes (e.g. Beta). > + * integer values of all versions used so far. DT_VERS_STRING must be an ASCII > + * string that contains the latest version within it along with any suffixes > + * (e.g. Beta). You must update DT_VERS_STRING when adding a new version, > + * and then add the new version to the _dtrace_versions[] array declared in > + * dt_open.c. > * > * Refer to the Solaris Dynamic Tracing Guide Versioning chapter for an > * explanation of these DTrace features and their values. > * > - * You must update DT_VERS_LATEST and DT_VERS_STRING when adding a new version, > - * and then add the new version to the _dtrace_versions[] array declared in > - * dt_open.c.. > - * > * NOTE: Although the DTrace versioning scheme supports the labeling and > * introduction of incompatible changes (e.g. dropping an interface in a > * major release), the libdtrace code does not currently support this. > @@ -85,7 +82,6 @@ extern "C" { > #define DT_VERS_2_0 DT_VERSION_NUMBER(2, 0, 0) > #define DT_VERS_2_0_1 DT_VERSION_NUMBER(2, 0, 1) > > -#define DT_VERS_LATEST DT_VERS_2_0_1 > #define DT_VERS_STRING "Oracle D 2.0" You should add 2.0.2 and update the DT_VERS_STRING also. > > #ifdef __cplusplus > -- > 2.43.5 > From kris.van.hees at oracle.com Thu Feb 27 16:57:29 2025 From: kris.van.hees at oracle.com (Kris Van Hees) Date: Thu, 27 Feb 2025 11:57:29 -0500 Subject: [DTrace-devel] [PATCH 2/4] Eliminate DT_VERS_LATEST In-Reply-To: References: <20250208190622.23484-1-eugene.loh@oracle.com> <20250208190622.23484-2-eugene.loh@oracle.com> Message-ID: There is some overlap with the patch that follows this one, and that got me thinking a bit more... I think that we should rework all this a little bit, and have dt_version.h be the sole source of version number data. We can put macros in there that provide all the version information (strings and codes), and use that to populate everything else, I think. That way we reduce the places where changes need to be made (somewhat). Let me muse on that a little and I'll follow-up with another email soon with a example of what I am envisioing. On Thu, Feb 27, 2025 at 11:40:45AM -0500, Kris Van Hees via DTrace-devel wrote: > On Sat, Feb 08, 2025 at 02:06:20PM -0500, eugene.loh at oracle.com wrote: > > From: Eugene Loh > > > > Updating the DTrace version number requires too many distinct > > changes. Eliminate DT_VERS_LATEST, since it can be determined > > on the fly. > > > > Signed-off-by: Eugene Loh > > --- > > libdtrace/dt_open.c | 3 ++- > > libdtrace/dt_version.h | 14 +++++--------- > > 2 files changed, 7 insertions(+), 10 deletions(-) > > > > diff --git a/libdtrace/dt_open.c b/libdtrace/dt_open.c > > index a02058871..b4d160359 100644 > > --- a/libdtrace/dt_open.c > > +++ b/libdtrace/dt_open.c > > @@ -721,7 +721,8 @@ dt_vopen(int version, int flags, int *errp, > > dtp->dt_proc_fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); > > if (dt_aggregate_init(dtp) == -1) > > return set_open_errno(dtp, errp, dtrace_errno(dtp)); > > - dtp->dt_vmax = DT_VERS_LATEST; > > + for (i = 0; _dtrace_versions[i] != 0; i++) > > + dtp->dt_vmax = _dtrace_versions[i]; > > dtp->dt_vmax = _dtrace_versions[ARRAY_SIZE(_dtrace_versions) - 2]; > > (-2 to account for the 0 sentinel value) > > But this will only be accurate if you also add 2.0.1 to the _dtrace_versions > array, and then you should probably add 2.0.2 to it also for accuracy. > > > dtp->dt_cpp_path = strdup(_dtrace_defcpp); > > dtp->dt_cpp_argv = malloc(sizeof(char *)); > > dtp->dt_cpp_argc = 1; > > diff --git a/libdtrace/dt_version.h b/libdtrace/dt_version.h > > index 3fd1b3d1e..bef3243e9 100644 > > --- a/libdtrace/dt_version.h > > +++ b/libdtrace/dt_version.h > > @@ -38,18 +38,15 @@ extern "C" { > > * > > * These #defines are used in identifier tables to fill in the version fields > > * associated with each identifier. The DT_VERS_* macros declare the encoded > > - * integer values of all versions used so far. DT_VERS_LATEST must correspond > > - * to the latest version value among all versions exported by the D compiler. > > - * DT_VERS_STRING must be an ASCII string that contains DT_VERS_LATEST within > > - * it along with any suffixes (e.g. Beta). > > + * integer values of all versions used so far. DT_VERS_STRING must be an ASCII > > + * string that contains the latest version within it along with any suffixes > > + * (e.g. Beta). You must update DT_VERS_STRING when adding a new version, > > + * and then add the new version to the _dtrace_versions[] array declared in > > + * dt_open.c. > > * > > * Refer to the Solaris Dynamic Tracing Guide Versioning chapter for an > > * explanation of these DTrace features and their values. > > * > > - * You must update DT_VERS_LATEST and DT_VERS_STRING when adding a new version, > > - * and then add the new version to the _dtrace_versions[] array declared in > > - * dt_open.c.. > > - * > > * NOTE: Although the DTrace versioning scheme supports the labeling and > > * introduction of incompatible changes (e.g. dropping an interface in a > > * major release), the libdtrace code does not currently support this. > > @@ -85,7 +82,6 @@ extern "C" { > > #define DT_VERS_2_0 DT_VERSION_NUMBER(2, 0, 0) > > #define DT_VERS_2_0_1 DT_VERSION_NUMBER(2, 0, 1) > > > > -#define DT_VERS_LATEST DT_VERS_2_0_1 > > #define DT_VERS_STRING "Oracle D 2.0" > > You should add 2.0.2 and update the DT_VERS_STRING also. > > > > > #ifdef __cplusplus > > -- > > 2.43.5 > > > > _______________________________________________ > DTrace-devel mailing list > DTrace-devel at oss.oracle.com > https://oss.oracle.com/mailman/listinfo/dtrace-devel From eugene.loh at oracle.com Thu Feb 27 19:01:54 2025 From: eugene.loh at oracle.com (eugene.loh at oracle.com) Date: Thu, 27 Feb 2025 14:01:54 -0500 Subject: [DTrace-devel] [PATCH v2 1/4] Rename _DTRACE_VERSION Message-ID: <20250227190154.23241-1-eugene.loh@oracle.com> From: Eugene Loh There are many DTrace version numbers (for version, API version, package version, etc.). Meanwhile, _DTRACE_VERSION is not a version number at all. It's a preprocessor macro in USDT .h header files. Prior to commit e2fb0ecd9 ("Ensure multiple passes through dtrace -G work."), it was perhaps not even set. With that commit, it was always set to 1, with the rationale: Also add an explicit define for _DTRACE__VERSION in the generated header file from 'dtrace -h' invocations. This seems silly, but it is there to give people a skeleton to work with if they want to pre-generate header files and select whether to actually compile on the probes at a later time. Rename to _DTRACE_USE_USDT for better clarity. Define it only once per file. Place the definition inside an #ifndef test so that a developer could set the value without manually changing the file. Signed-off-by: Eugene Loh --- libdtrace/dt_program.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/libdtrace/dt_program.c b/libdtrace/dt_program.c index 23b91fb2e..cfb7a5fc3 100644 --- a/libdtrace/dt_program.c +++ b/libdtrace/dt_program.c @@ -505,13 +505,12 @@ dt_header_provider(dtrace_hdl_t *dtp, dt_provider_t *pvp, FILE *out) info.dthi_pfname = alloca(strlen(pvp->desc.dtvd_name) + 1 + i); dt_header_fmt_func(info.dthi_pfname, pvp->desc.dtvd_name); - if (fprintf(out, "#define _DTRACE_VERSION 1\n\n" - "#if _DTRACE_VERSION\n\n") < 0) + if (fprintf(out, "#if _DTRACE_USE_USDT\n\n") < 0) return dt_set_errno(dtp, errno); if (dt_idhash_iter(pvp->pv_probes, dt_header_probe, &info) != 0) return -1; /* dt_errno is set for us */ - if (fprintf(out, "\n\n") < 0) + if (fprintf(out, "\n") < 0) return dt_set_errno(dtp, errno); if (dt_idhash_iter(pvp->pv_probes, dt_header_decl, &info) != 0) return -1; /* dt_errno is set for us */ @@ -560,6 +559,11 @@ dtrace_program_header(dtrace_hdl_t *dtp, FILE *out, const char *fname) "#endif\n\n") < 0) return -1; + if (fprintf(out, "#ifndef _DTRACE_USE_USDT\n" + "# define _DTRACE_USE_USDT 1\n" + "#endif\n\n") < 0) + return -1; + while ((pvp = dt_htab_next(dtp->dt_provs, &it)) != NULL) { if (dt_header_provider(dtp, pvp, out) != 0) { dt_htab_next_destroy(it); -- 2.43.5 From eugene.loh at oracle.com Thu Feb 27 21:32:13 2025 From: eugene.loh at oracle.com (Eugene Loh) Date: Thu, 27 Feb 2025 16:32:13 -0500 Subject: [DTrace-devel] [PATCH] test: Allow duplicate usym/umod/uaddr if for different pids In-Reply-To: References: <20241205191318.18587-1-eugene.loh@oracle.com> Message-ID: On 2/25/25 16:09, Kris Van Hees wrote: > Eugene, > > Can you re-check this against the current dev and ensure the changes are > correct? This is interesting. I guess what happens is that if we hit something like usym(addr), the kernel only knows the addr;? it's the consumer's job to do the usym() translation.? E.g., when the usym(addr) is in an aggregation key, the kernel's copy of the aggregation is potentially very large (many distinct addr values) while the consumer's copy of that aggregation is much more compact (many addr values mapping to the same sym). Anyhow, dtrace loops over dtrace_work(), which calls dt_consume(), which calls dtrace_aggregate_snap().? When we "snapshot" the aggregation, we first call dtrace_aggregate_clear() and then walk the aggregations in the BPF map. The problem appears to be that the usym() mapping changes.? Why? First of all, we could imagine that the mapping might change.? E.g., a library is loaded and unloaded and then a different library is loaded.? But it's also possible (I don't know why) for the mapping to fail once (but not on subsequent attempts).? Incidentally, the rule is if the translation fails, we just keep the addr value (rather than replacing this addr with the addr of the associated symbol). So what happens is we snapshot the aggregation, and a couple of usym() translations fail.? They produce aggregation records (in the consumer's copy of the aggregation) with the raw addr values.? A later snapshot will clear the consumer's aggregation (zeroing out the values but preserving the keys).? If the translation is now successful, the "raw" address will be orphaned.? It will be reported with a count of 0.? The tests in question might (likely) fail. I'm thinking about some fixes, but don't mind some feedback. *)? One possibility is that if the usym() translation fails, we omit the data from the aggregation. *)? Another option is that, when we loop routinely over dtrace_work()=>dt_consume()=>dtrace_aggregate_snap() and snapshot the aggregations, instead of calling dtrace_aggregate_clear() each time we "truncate" the consumer's copy of the aggregations... that is, throw away old keys altogether. We're reading them back in anyhow. *)? Come to think of it, why are we snapshotting aggregations so often anyhow?? It's not as though we are unloading data from the kernel.? Why not snapshot lazily...? if needed for a printa() or END? There remain other problems -- like, what if a library is loaded, used, usym(addr) shows up in an aggregation, a different library is loaded in its place, and the aggregation is not printed until after the translation information has become stale?? But maybe we can settle for getting these tests passing for now. > On Mon, Jan 06, 2025 at 06:44:37PM -0500, Kris Van Hees wrote: >> Withdrawing R-b for now after seeing these tests fail after this patch was >> merged. Needs further investigation. >> >> On Mon, Jan 06, 2025 at 04:04:25PM -0500, Kris Van Hees wrote: >>> On Thu, Dec 05, 2024 at 02:13:18PM -0500, eugene.loh at oracle.com wrote: >>>> From: Eugene Loh >>>> >>>> In 83da884cbdc5 ("Preface usym/umod/uaddr with pid"), a bug was fixed >>>> in which addresses in the same module (or function) might be mapped to >>>> multiple agg entries. This fix helped the associated tests run much >>>> more successfully. Nonetheless, tests would sometimes still fail. >>>> >>>> Another problem is that the tests themselves were overly narrow. It >>>> is fine for a module (or function) to appear multiple times in the >>>> aggregation output... if those entries correspond to different pids. >>>> >>>> Further, odd behaviors can result for some of the processes running on >>>> a system. >>>> >>>> Change the tests to add a "pid" agg key. Filter on only a few, select >>>> pids. Distinguish agg entries by pid. >>>> >>>> There are still occasional time outs observed with these tests, >>>> presumably because the tick-2s probe is not firing (when profile-1234hz >>>> is running). >>>> >>>> Signed-off-by: Eugene Loh >>> Reviewed-by: Kris Van Hees >>> >>>> --- >>>> test/unittest/profile-n/tst.ufunc.sh | 12 ++++++++---- >>>> test/unittest/profile-n/tst.umod.sh | 11 ++++++++--- >>>> test/unittest/profile-n/tst.usym.sh | 11 ++++++++--- >>>> 3 files changed, 24 insertions(+), 10 deletions(-) >>>> >>>> diff --git a/test/unittest/profile-n/tst.ufunc.sh b/test/unittest/profile-n/tst.ufunc.sh >>>> index 243822407..f5174a1e2 100755 >>>> --- a/test/unittest/profile-n/tst.ufunc.sh >>>> +++ b/test/unittest/profile-n/tst.ufunc.sh >>>> @@ -11,10 +11,14 @@ tmpfile=$tmpdir/tst.profile_ufunc.$$ >>>> script() >>>> { >>>> $dtrace $dt_flags -qs /dev/stdin <>>> + BEGIN >>>> + { >>>> + printf("dtrace is %d\n", \$pid); >>>> + } >>>> profile-1234hz >>>> /arg1 != 0/ >>>> { >>>> - @[ufunc(arg1)] = count(); >>>> + @[ufunc(arg1), pid] = count(); >>>> } >>>> >>>> tick-2s >>>> @@ -52,9 +56,9 @@ if ! grep -q 'bash`[a-zA-Z_]' $tmpfile; then >>>> status=1 >>>> fi >>>> >>>> -# Check that functions are unique. (Exclude shared libraries and unresolved addresses.) >>>> -if gawk '!/^ *(ld-linux-|lib|([^`]*`)?0x)/ {print $1}' $tmpfile | \ >>>> - sort | uniq -c | grep -qv " 1 "; then >>>> +# Check that functions are unique for each pid that interests us. >>>> +dtpid=`awk '/^dtrace is [0-9]*$/ { print $3 }' $tmpfile` >>>> +if gawk '$2 == '$child' || $2 == '$dtpid' {print $1, $2}' $tmpfile | sort | uniq -c | grep -qv " 1 "; then >>>> echo ERROR: duplicate ufunc >>>> status=1 >>>> fi >>>> diff --git a/test/unittest/profile-n/tst.umod.sh b/test/unittest/profile-n/tst.umod.sh >>>> index 45d2b1e9b..7cfe2a073 100755 >>>> --- a/test/unittest/profile-n/tst.umod.sh >>>> +++ b/test/unittest/profile-n/tst.umod.sh >>>> @@ -11,10 +11,14 @@ tmpfile=$tmpdir/tst.profile_umod.$$ >>>> script() >>>> { >>>> $dtrace $dt_flags -qs /dev/stdin <>>> + BEGIN >>>> + { >>>> + printf("dtrace is %d\n", \$pid); >>>> + } >>>> profile-1234hz >>>> /arg1 != 0/ >>>> { >>>> - @[umod(arg1)] = count(); >>>> + @[umod(arg1), pid] = count(); >>>> } >>>> >>>> tick-2s >>>> @@ -52,8 +56,9 @@ if ! grep -wq 'bash' $tmpfile; then >>>> status=1 >>>> fi >>>> >>>> -# Check that modules are unique. (Exclude shared libraries and unresolved addresses.) >>>> -if gawk '!/^ *lib/ && !/^ *ld-.*\.so / && !/^ *0x/ {print $1}' $tmpfile | sort | uniq -c | grep -qv " 1 "; then >>>> +# Check that modules are unique for each pid that interests us. >>>> +dtpid=`awk '/^dtrace is [0-9]*$/ { print $3 }' $tmpfile` >>>> +if gawk '$2 == '$child' || $2 == '$dtpid' {print $1, $2}' $tmpfile | sort | uniq -c | grep -qv " 1 "; then >>>> echo ERROR: duplicate umod >>>> status=1 >>>> fi >>>> diff --git a/test/unittest/profile-n/tst.usym.sh b/test/unittest/profile-n/tst.usym.sh >>>> index 634e633b3..8e373b976 100755 >>>> --- a/test/unittest/profile-n/tst.usym.sh >>>> +++ b/test/unittest/profile-n/tst.usym.sh >>>> @@ -11,10 +11,14 @@ tmpfile=$tmpdir/tst.profile_usym.$$ >>>> script() >>>> { >>>> $dtrace $dt_flags -qs /dev/stdin <>>> + BEGIN >>>> + { >>>> + printf("dtrace is %d\n", \$pid); >>>> + } >>>> profile-1234hz >>>> /arg1 != 0/ >>>> { >>>> - @[usym(arg1)] = count(); >>>> + @[usym(arg1), pid] = count(); >>>> } >>>> >>>> tick-2s >>>> @@ -52,8 +56,9 @@ if ! grep -q 'bash`[a-zA-Z_]' $tmpfile; then >>>> status=1 >>>> fi >>>> >>>> -# Check that symbols are unique. (Exclude shared libraries and unresolved addresses.) >>>> -if gawk '!/^ *lib/ && !/^ *0x/ {print $1}' $tmpfile | sort | uniq -c | grep -qv " 1 "; then >>>> +# Check that symbols are unique for each pid that interests us. >>>> +dtpid=`awk '/^dtrace is [0-9]*$/ { print $3 }' $tmpfile` >>>> +if gawk '$2 == '$child' || $2 == '$dtpid' {print $1, $2}' $tmpfile | sort | uniq -c | grep -qv " 1 "; then >>>> echo ERROR: duplicate usym >>>> status=1 >>>> fi >>>> -- >>>> 2.43.5 >>>> From kris.van.hees at oracle.com Fri Feb 28 03:09:59 2025 From: kris.van.hees at oracle.com (Kris Van Hees) Date: Thu, 27 Feb 2025 22:09:59 -0500 Subject: [DTrace-devel] [PATCH] Refactor the versioning handling system Message-ID: DTrace was handlings versioning data in multiple locations, causing common mistakes in not consistently updating versions in allplaces. By consolidating all versioning data in dt_version.h a single file need to be updated (as far as the source tree is concerned) when a new version is introduced. For building, the GNUmakefile and dtrace.spec files will also need to be updated with the new version number. Signed-off-by: Kris Van Hees --- libdtrace/Build | 1 + libdtrace/dt_impl.h | 48 ++++++++----------- libdtrace/dt_open.c | 19 -------- libdtrace/dt_subr.c | 80 -------------------------------- libdtrace/dt_version.c | 93 +++++++++++++++++++++++++++++++++++++ libdtrace/dt_version.h | 102 ++++++++++++++++++++++++++++------------- 6 files changed, 181 insertions(+), 162 deletions(-) create mode 100644 libdtrace/dt_version.c diff --git a/libdtrace/Build b/libdtrace/Build index 51e0f078..57804f55 100644 --- a/libdtrace/Build +++ b/libdtrace/Build @@ -70,6 +70,7 @@ libdtrace-build_SOURCES = dt_aggregate.c \ dt_strtab.c \ dt_subr.c \ dt_symtab.c \ + dt_version.c \ dt_work.c \ dt_xlator.c diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h index 68fb8ec5..60e9b0c9 100644 --- a/libdtrace/dt_impl.h +++ b/libdtrace/dt_impl.h @@ -256,8 +256,6 @@ typedef struct dt_percpu_drops { */ #define DT_MAX_NSPECS 16 /* sanity upper bound on speculations */ -typedef uint32_t dt_version_t; /* encoded version (see below) */ - struct dtrace_hdl { const dtrace_vector_t *dt_vector; /* library vector, if vectored open */ void *dt_varg; /* vector argument, if vectored open */ @@ -645,6 +643,24 @@ enum { EDT_PRINT, /* missing or corrupt print() record */ }; +/* + * Stability definitions + * + * These #defines are used in the tables of identifiers below to fill in the + * attribute fields associated with each identifier. The DT_ATTR_* macros are + * a convenience to permit more concise declarations of common attributes such + * as Stable/Stable/Common. + * + * Refer to the Solaris Dynamic Tracing Guide Stability chapter respectively + * for an explanation of these DTrace features and their values. + */ +#define DT_ATTR_STABCMN { DTRACE_STABILITY_STABLE, \ + DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON } + +#define DT_ATTR_EVOLCMN { DTRACE_STABILITY_EVOLVING, \ + DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON \ +} + /* * Interfaces for parsing and comparing DTrace attribute tuples, which describe * stability and architectural binding information. @@ -654,31 +670,6 @@ extern dtrace_attribute_t dt_attr_max(dtrace_attribute_t, dtrace_attribute_t); extern char *dt_attr_str(dtrace_attribute_t, char *, size_t); extern int dt_attr_cmp(dtrace_attribute_t, dtrace_attribute_t); -/* - * Interfaces for parsing and handling DTrace version strings. Version binding - * is a feature of the D compiler that is handled completely independently of - * the DTrace kernel infrastructure, so the definitions are here in libdtrace. - * Version strings are compiled into an encoded uint32_t which can be compared - * using C comparison operators. Version definitions are found in dt_open.c. - */ -#define DT_VERSION_STRMAX 16 /* enough for "255.4095.4095\0" */ -#define DT_VERSION_MAJMAX 0xFF /* maximum major version number */ -#define DT_VERSION_MINMAX 0xFFF /* maximum minor version number */ -#define DT_VERSION_MICMAX 0xFFF /* maximum micro version number */ - -#define DT_VERSION_NUMBER(M, m, u) \ - ((((M) & 0xFF) << 24) | (((m) & 0xFFF) << 12) | ((u) & 0xFFF)) - -#define DT_VERSION_MAJOR(v) (((v) & 0xFF000000) >> 24) -#define DT_VERSION_MINOR(v) (((v) & 0x00FFF000) >> 12) -#define DT_VERSION_MICRO(v) ((v) & 0x00000FFF) - -extern char *dt_version_num2str(dt_version_t, char *, size_t); -extern int dt_version_str2num(const char *, dt_version_t *); -extern int dt_version_defined(dt_version_t); - -extern int dt_str2kver(const char *, dt_version_t *); - extern uint32_t dt_gen_hval(const char *, uint32_t, size_t); /* @@ -816,9 +807,6 @@ extern const dtrace_attribute_t _dtrace_typattr; /* type ref attributes */ extern const dtrace_attribute_t _dtrace_prvattr; /* provider attributes */ extern const dtrace_pattr_t _dtrace_prvdesc; /* provider attribute bundle */ -extern const dt_version_t _dtrace_versions[]; /* array of valid versions */ -extern const char *const _dtrace_version; /* current version string */ - extern int _dtrace_strbuckets; /* number of hash buckets for strings */ extern uint_t _dtrace_stkindent; /* default indent for stack/ustack */ extern uint_t _dtrace_pidbuckets; /* number of hash buckets for pids */ diff --git a/libdtrace/dt_open.c b/libdtrace/dt_open.c index 51c056b2..72c138ce 100644 --- a/libdtrace/dt_open.c +++ b/libdtrace/dt_open.c @@ -42,25 +42,6 @@ #include #include -const dt_version_t _dtrace_versions[] = { - DT_VERS_1_0, /* D API 1.0.0 (PSARC 2001/466) Solaris 10 FCS */ - DT_VERS_1_1, /* D API 1.1.0 Solaris Express 6/05 */ - DT_VERS_1_2, /* D API 1.2.0 Solaris 10 Update 1 */ - DT_VERS_1_2_1, /* D API 1.2.1 Solaris Express 4/06 */ - DT_VERS_1_2_2, /* D API 1.2.2 Solaris Express 6/06 */ - DT_VERS_1_3, /* D API 1.3 Solaris Express 10/06 */ - DT_VERS_1_4, /* D API 1.4 Solaris Express 2/07 */ - DT_VERS_1_4_1, /* D API 1.4.1 Solaris Express 4/07 */ - DT_VERS_1_5, /* D API 1.5 Solaris Express 7/07 */ - DT_VERS_1_6, /* D API 1.6 */ - DT_VERS_1_6_1, /* D API 1.6.1 */ - DT_VERS_1_6_2, /* D API 1.6.2 */ - DT_VERS_1_6_3, /* D API 1.6.3 */ - DT_VERS_1_6_4, /* D API 1.6.4 */ - DT_VERS_2_0, /* D API 2.0 */ - 0 -}; - /* * Table of global identifiers. This is used to populate the global identifier * hash when a new dtrace client open occurs. For more info see dt_ident.h. diff --git a/libdtrace/dt_subr.c b/libdtrace/dt_subr.c index d5dca164..40b66c7d 100644 --- a/libdtrace/dt_subr.c +++ b/libdtrace/dt_subr.c @@ -369,58 +369,6 @@ dt_attr_str(dtrace_attribute_t a, char *buf, size_t len) return buf; } -char * -dt_version_num2str(dt_version_t v, char *buf, size_t len) -{ - uint_t M = DT_VERSION_MAJOR(v); - uint_t m = DT_VERSION_MINOR(v); - uint_t u = DT_VERSION_MICRO(v); - - if (u == 0) - snprintf(buf, len, "%u.%u", M, m); - else - snprintf(buf, len, "%u.%u.%u", M, m, u); - - return buf; -} - -int -dt_version_str2num(const char *s, dt_version_t *vp) -{ - int i = 0, n[3] = { 0, 0, 0 }; - char c; - - while ((c = *s++) != '\0') { - if (isdigit(c)) - n[i] = n[i] * 10 + c - '0'; - else if (c != '.' || i++ >= sizeof(n) / sizeof(n[0]) - 1) - return -1; - } - - if (n[0] > DT_VERSION_MAJMAX || - n[1] > DT_VERSION_MINMAX || - n[2] > DT_VERSION_MICMAX) - return -1; - - if (vp != NULL) - *vp = DT_VERSION_NUMBER(n[0], n[1], n[2]); - - return 0; -} - -int -dt_version_defined(dt_version_t v) -{ - int i; - - for (i = 0; _dtrace_versions[i] != 0; i++) { - if (_dtrace_versions[i] == v) - return 1; - } - - return 0; -} - char * dt_cpp_add_arg(dtrace_hdl_t *dtp, const char *str) { @@ -949,34 +897,6 @@ dtrace_uaddr2str(dtrace_hdl_t *dtp, pid_t pid, uint64_t addr, char *str, return dt_string2str(c, str, nbytes); } -/* - * The function converts string representation of kernel version - * into the dt_version_t type. - */ -int -dt_str2kver(const char *kverstr, dt_version_t *vp) -{ - int kv1, kv2, kv3; - int rval; - - rval = sscanf(kverstr, "%d.%d.%d", &kv1, &kv2, &kv3); - - switch (rval) { - case 2: - kv3 = 0; - break; - case 3: - break; - default: - return -1; - } - - if (vp) - *vp = DT_VERSION_NUMBER(kv1, kv2, kv3); - - return 0; -} - /* * Compute a 32-bit hash value for a memory block of given size. */ diff --git a/libdtrace/dt_version.c b/libdtrace/dt_version.c new file mode 100644 index 00000000..e9ac88be --- /dev/null +++ b/libdtrace/dt_version.c @@ -0,0 +1,93 @@ +/* + * 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. + */ + +#include +#include + +#include + +const dt_version_t _dtrace_versions[] = DTRACE_VERSIONS; + +char * +dt_version_num2str(dt_version_t v, char *buf, size_t len) +{ + uint_t M = DT_VERSION_MAJOR(v); + uint_t m = DT_VERSION_MINOR(v); + uint_t u = DT_VERSION_MICRO(v); + + if (u == 0) + snprintf(buf, len, "%u.%u", M, m); + else + snprintf(buf, len, "%u.%u.%u", M, m, u); + + return buf; +} + +int +dt_version_str2num(const char *s, dt_version_t *vp) +{ + int i = 0, n[3] = { 0, 0, 0 }; + char c; + + while ((c = *s++) != '\0') { + if (isdigit(c)) + n[i] = n[i] * 10 + c - '0'; + else if (c != '.' || i++ >= ARRAY_SIZE(n) - 1) + return -1; + } + + if (n[0] > DT_VERSION_MAJMAX || + n[1] > DT_VERSION_MINMAX || + n[2] > DT_VERSION_MICMAX) + return -1; + + if (vp != NULL) + *vp = DT_VERSION_NUMBER(n[0], n[1], n[2]); + + return 0; +} + +int +dt_version_defined(dt_version_t v) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(_dtrace_versions); i++) { + if (_dtrace_versions[i] == v) + return 1; + } + + return 0; +} + +/* + * Convert a string representation of a kernel version string into the + * a dt_version_t value. + */ +int +dt_str2kver(const char *kverstr, dt_version_t *vp) +{ + int kv1, kv2, kv3; + int rval; + + rval = sscanf(kverstr, "%d.%d.%d", &kv1, &kv2, &kv3); + + switch (rval) { + case 2: + kv3 = 0; + break; + case 3: + break; + default: + return -1; + } + + if (vp) + *vp = DT_VERSION_NUMBER(kv1, kv2, kv3); + + return 0; +} diff --git a/libdtrace/dt_version.h b/libdtrace/dt_version.h index 3fd1b3d1..967e22cc 100644 --- a/libdtrace/dt_version.h +++ b/libdtrace/dt_version.h @@ -15,24 +15,6 @@ extern "C" { #include #include -/* - * Stability definitions - * - * These #defines are used in the tables of identifiers below to fill in the - * attribute fields associated with each identifier. The DT_ATTR_* macros are - * a convenience to permit more concise declarations of common attributes such - * as Stable/Stable/Common. - * - * Refer to the Solaris Dynamic Tracing Guide Stability chapter respectively - * for an explanation of these DTrace features and their values. - */ -#define DT_ATTR_STABCMN { DTRACE_STABILITY_STABLE, \ - DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON } - -#define DT_ATTR_EVOLCMN { DTRACE_STABILITY_EVOLVING, \ - DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON \ -} - /* * Versioning definitions * @@ -46,9 +28,11 @@ extern "C" { * Refer to the Solaris Dynamic Tracing Guide Versioning chapter for an * explanation of these DTrace features and their values. * - * You must update DT_VERS_LATEST and DT_VERS_STRING when adding a new version, - * and then add the new version to the _dtrace_versions[] array declared in - * dt_open.c.. + * When adding a new version: + * - Add a new DT_VERS_* macro + * - Add the new DT_VERS_* macro at the end of the DTRACE_VERSIONS macro + * - Set DT_VERS_LATEST to the new DT_VERS_* + * - Update DT_VERS_STRING to reflect the new version * * NOTE: Although the DTrace versioning scheme supports the labeling and * introduction of incompatible changes (e.g. dropping an interface in a @@ -57,16 +41,18 @@ extern "C" { * we ever need to provide divergent interfaces, this will need work. * * The version number should be increased for every customer visible release - * of Solaris. The major number should be incremented when a fundamental - * change has been made that would affect all consumers, and would reflect - * sweeping changes to DTrace or the D language. The minor number should be - * incremented when a change is introduced that could break scripts that had - * previously worked; for example, adding a new built-in variable could break - * a script which was already using that identifier. The micro number should - * be changed when introducing functionality changes or major bug fixes that - * do not affect backward compatibility -- this is merely to make capabilities - * easily determined from the version number. Minor bugs do not require any - * modification to the version number. + * of Linux. + * - The major number should be incremented when a fundamental change has been + * made that would affect all consumers, and would reflect sweeping changes + * to DTrace or the D language. + * - The minor number should be incremented when a change is introduced that + * could break scripts that had previously worked; for example, adding a new + * built-in variable could break a script which was already using that + * identifier. + * - The micro number should be changed when introducing functionality changes + * or major bug fixes that do not affect backward compatibility -- this is + * merely to make capabilities easily determined from the version number. + * Minor bugs do not require any modification to the version number. */ #define DT_VERS_1_0 DT_VERSION_NUMBER(1, 0, 0) #define DT_VERS_1_1 DT_VERSION_NUMBER(1, 1, 0) @@ -84,9 +70,59 @@ extern "C" { #define DT_VERS_1_6_4 DT_VERSION_NUMBER(1, 6, 4) #define DT_VERS_2_0 DT_VERSION_NUMBER(2, 0, 0) #define DT_VERS_2_0_1 DT_VERSION_NUMBER(2, 0, 1) +#define DT_VERS_2_0_2 DT_VERSION_NUMBER(2, 0, 2) + +#define DTRACE_VERSIONS { \ + DT_VERS_1_1, /* D API 1.1.0 Solaris Express 6/05 */ \ + DT_VERS_1_2, /* D API 1.2.0 Solaris 10 Update 1 */ \ + DT_VERS_1_2_1, /* D API 1.2.1 Solaris Express 4/06 */ \ + DT_VERS_1_2_2, /* D API 1.2.2 Solaris Express 6/06 */ \ + DT_VERS_1_3, /* D API 1.3 Solaris Express 10/06 */ \ + DT_VERS_1_4, /* D API 1.4 Solaris Express 2/07 */ \ + DT_VERS_1_4_1, /* D API 1.4.1 Solaris Express 4/07 */ \ + DT_VERS_1_5, /* D API 1.5 Solaris Express 7/07 */ \ + DT_VERS_1_6, /* D API 1.6 */ \ + DT_VERS_1_6_1, /* D API 1.6.1 */ \ + DT_VERS_1_6_2, /* D API 1.6.2 */ \ + DT_VERS_1_6_3, /* D API 1.6.3 */ \ + DT_VERS_1_6_4, /* D API 1.6.4 */ \ + DT_VERS_2_0, /* D API 2.0 */ \ + DT_VERS_2_0_1, /* D API 2.0.1 */ \ + DT_VERS_2_0_2, /* D API 2.0.2 */ \ +} + +#define DT_VERS_LATEST DT_VERS_2_0_2 +#define DT_VERS_STRING "Oracle D 2.0.2" + +/* + * Interfaces for parsing and handling DTrace version strings. Version binding + * is a feature of the D compiler that is handled completely independently of + * the DTrace kernel infrastructure, so the definitions are here in libdtrace. + * Version strings are compiled into an encoded uint32_t which can be compared + * using C comparison operators. + */ +#define DT_VERSION_STRMAX 16 /* enough for "255.4095.4095\0" */ +#define DT_VERSION_MAJMAX 0xFF /* maximum major version number */ +#define DT_VERSION_MINMAX 0xFFF /* maximum minor version number */ +#define DT_VERSION_MICMAX 0xFFF /* maximum micro version number */ + +#define DT_VERSION_NUMBER(M, m, u) \ + ((((M) & 0xFF) << 24) | (((m) & 0xFFF) << 12) | ((u) & 0xFFF)) + +#define DT_VERSION_MAJOR(v) (((v) & 0xFF000000) >> 24) +#define DT_VERSION_MINOR(v) (((v) & 0x00FFF000) >> 12) +#define DT_VERSION_MICRO(v) ((v) & 0x00000FFF) + +typedef uint32_t dt_version_t; + +extern const dt_version_t _dtrace_versions[]; +extern const char *const _dtrace_version; + +extern char *dt_version_num2str(dt_version_t, char *, size_t); +extern int dt_version_str2num(const char *, dt_version_t *); +extern int dt_version_defined(dt_version_t); -#define DT_VERS_LATEST DT_VERS_2_0_1 -#define DT_VERS_STRING "Oracle D 2.0" +extern int dt_str2kver(const char *, dt_version_t *); #ifdef __cplusplus } -- 2.45.2 From kris.van.hees at oracle.com Fri Feb 28 03:19:23 2025 From: kris.van.hees at oracle.com (Kris Van Hees) Date: Thu, 27 Feb 2025 22:19:23 -0500 Subject: [DTrace-devel] [PATCH 2/4] Eliminate DT_VERS_LATEST In-Reply-To: References: <20250208190622.23484-1-eugene.loh@oracle.com> <20250208190622.23484-2-eugene.loh@oracle.com> Message-ID: On Thu, Feb 27, 2025 at 11:57:29AM -0500, Kris Van Hees wrote: > There is some overlap with the patch that follows this one, and that got me > thinking a bit more... > > I think that we should rework all this a little bit, and have dt_version.h be > the sole source of version number data. We can put macros in there that > provide all the version information (strings and codes), and use that to > populate everything else, I think. That way we reduce the places where changes > need to be made (somewhat). > > Let me muse on that a little and I'll follow-up with another email soon with > a example of what I am envisioing. See the patch I just posted: [PATCH] Refactor the versioning handling system I think it might accomplish the same thing, i.e. consolidating the places where changes are needed for new versions? I was going to just do some prototype of a possible solution but that one didn't work out (it needed to expose too much of the version handling to other code), and then I came up with this idea. Open to suggestions/feedback/... > On Thu, Feb 27, 2025 at 11:40:45AM -0500, Kris Van Hees via DTrace-devel wrote: > > On Sat, Feb 08, 2025 at 02:06:20PM -0500, eugene.loh at oracle.com wrote: > > > From: Eugene Loh > > > > > > Updating the DTrace version number requires too many distinct > > > changes. Eliminate DT_VERS_LATEST, since it can be determined > > > on the fly. > > > > > > Signed-off-by: Eugene Loh > > > --- > > > libdtrace/dt_open.c | 3 ++- > > > libdtrace/dt_version.h | 14 +++++--------- > > > 2 files changed, 7 insertions(+), 10 deletions(-) > > > > > > diff --git a/libdtrace/dt_open.c b/libdtrace/dt_open.c > > > index a02058871..b4d160359 100644 > > > --- a/libdtrace/dt_open.c > > > +++ b/libdtrace/dt_open.c > > > @@ -721,7 +721,8 @@ dt_vopen(int version, int flags, int *errp, > > > dtp->dt_proc_fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); > > > if (dt_aggregate_init(dtp) == -1) > > > return set_open_errno(dtp, errp, dtrace_errno(dtp)); > > > - dtp->dt_vmax = DT_VERS_LATEST; > > > + for (i = 0; _dtrace_versions[i] != 0; i++) > > > + dtp->dt_vmax = _dtrace_versions[i]; > > > > dtp->dt_vmax = _dtrace_versions[ARRAY_SIZE(_dtrace_versions) - 2]; > > > > (-2 to account for the 0 sentinel value) > > > > But this will only be accurate if you also add 2.0.1 to the _dtrace_versions > > array, and then you should probably add 2.0.2 to it also for accuracy. > > > > > dtp->dt_cpp_path = strdup(_dtrace_defcpp); > > > dtp->dt_cpp_argv = malloc(sizeof(char *)); > > > dtp->dt_cpp_argc = 1; > > > diff --git a/libdtrace/dt_version.h b/libdtrace/dt_version.h > > > index 3fd1b3d1e..bef3243e9 100644 > > > --- a/libdtrace/dt_version.h > > > +++ b/libdtrace/dt_version.h > > > @@ -38,18 +38,15 @@ extern "C" { > > > * > > > * These #defines are used in identifier tables to fill in the version fields > > > * associated with each identifier. The DT_VERS_* macros declare the encoded > > > - * integer values of all versions used so far. DT_VERS_LATEST must correspond > > > - * to the latest version value among all versions exported by the D compiler. > > > - * DT_VERS_STRING must be an ASCII string that contains DT_VERS_LATEST within > > > - * it along with any suffixes (e.g. Beta). > > > + * integer values of all versions used so far. DT_VERS_STRING must be an ASCII > > > + * string that contains the latest version within it along with any suffixes > > > + * (e.g. Beta). You must update DT_VERS_STRING when adding a new version, > > > + * and then add the new version to the _dtrace_versions[] array declared in > > > + * dt_open.c. > > > * > > > * Refer to the Solaris Dynamic Tracing Guide Versioning chapter for an > > > * explanation of these DTrace features and their values. > > > * > > > - * You must update DT_VERS_LATEST and DT_VERS_STRING when adding a new version, > > > - * and then add the new version to the _dtrace_versions[] array declared in > > > - * dt_open.c.. > > > - * > > > * NOTE: Although the DTrace versioning scheme supports the labeling and > > > * introduction of incompatible changes (e.g. dropping an interface in a > > > * major release), the libdtrace code does not currently support this. > > > @@ -85,7 +82,6 @@ extern "C" { > > > #define DT_VERS_2_0 DT_VERSION_NUMBER(2, 0, 0) > > > #define DT_VERS_2_0_1 DT_VERSION_NUMBER(2, 0, 1) > > > > > > -#define DT_VERS_LATEST DT_VERS_2_0_1 > > > #define DT_VERS_STRING "Oracle D 2.0" > > > > You should add 2.0.2 and update the DT_VERS_STRING also. > > > > > > > > #ifdef __cplusplus > > > -- > > > 2.43.5 > > > > > > > _______________________________________________ > > DTrace-devel mailing list > > DTrace-devel at oss.oracle.com > > https://oss.oracle.com/mailman/listinfo/dtrace-devel From kris.van.hees at oracle.com Fri Feb 28 15:47:59 2025 From: kris.van.hees at oracle.com (Kris Van Hees) Date: Fri, 28 Feb 2025 10:47:59 -0500 Subject: [DTrace-devel] [PATCH 1/2] test: Allow more variations in expected fbt kernel stacks In-Reply-To: <20250208232106.27146-1-eugene.loh@oracle.com> References: <20250208232106.27146-1-eugene.loh@oracle.com> Message-ID: On Sat, Feb 08, 2025 at 06:21:05PM -0500, eugene.loh at oracle.com wrote: > From: Eugene Loh > > This test checks the call stack upon entry to vfs_write(). > Unfortunately, these checks require some maintenance since the call > stack can vary -- slightly or greatly -- depending on processor or > kernel. There is a competition between ease of test maintenance and > strictness of correctness checks. > > Adapt post processing of output to allow new variations in stacks > seen in UEK 8 (currently Linux 6.12). > > Orabug: 37459289 > Signed-off-by: Eugene Loh Reviewed-by: Kris Van Hees > --- > test/unittest/stack/tst.stack_fbt.sh | 15 ++++++++++++--- > 1 file changed, 12 insertions(+), 3 deletions(-) > > diff --git a/test/unittest/stack/tst.stack_fbt.sh b/test/unittest/stack/tst.stack_fbt.sh > index c365e14d6..8a0b7999c 100755 > --- a/test/unittest/stack/tst.stack_fbt.sh > +++ b/test/unittest/stack/tst.stack_fbt.sh > @@ -37,9 +37,18 @@ if [ $? -ne 0 ]; then > exit 1 > fi > > -# Strip out blank lines and pointer values. > - > -awk 'NF != 0 { sub(/+0x[0-9a-f]*$/, "+{ptr}"); print }' dtrace.out > dtrace.post > +# Strip out > +# - blank lines > +# - "constprop" > +# - "isra" > +# - "_after_hwframe" (x86 starting with UEK8) > +# - pointer values > + > +awk 'NF != 0 { sub("\\.constprop\\.[0-9]", ""); > + sub("\\.isra\\.[0-9]", ""); > + sub("_after_hwframe\\+", "+"); > + sub(/+0x[0-9a-f]*$/, "+{ptr}"); > + print }' dtrace.out > dtrace.post > if [ $? -ne 0 ]; then > echo ERROR: awk failed > cat dtrace.out > -- > 2.43.5 > From kris.van.hees at oracle.com Fri Feb 28 16:00:06 2025 From: kris.van.hees at oracle.com (Kris Van Hees) Date: Fri, 28 Feb 2025 11:00:06 -0500 Subject: [DTrace-devel] [PATCH 2/2] test: Account for pid:::entry ustack() being correct In-Reply-To: <20250208232106.27146-2-eugene.loh@oracle.com> References: <20250208232106.27146-1-eugene.loh@oracle.com> <20250208232106.27146-2-eugene.loh@oracle.com> Message-ID: On Sat, Feb 08, 2025 at 06:21:06PM -0500, eugene.loh at oracle.com wrote: > From: Eugene Loh > > The pid:::entry uprobe fires so early in the function preamble > that the frame pointer is not yet set and the caller is not (yet) > correctly identified. In Linux 6.11, x86-specific heuristics > address this problem. > > Post process results from this test to accommodate both cases -- > missing caller and not missing caller. > > Orabug: 37459289 > Signed-off-by: Eugene Loh Reviewed-by: Kris Van Hees > --- > test/unittest/ustack/tst.ustack25_pid.r | 1 + > test/unittest/ustack/tst.ustack25_pid.r.p | 37 +++++++++++++++++++++++ > 2 files changed, 38 insertions(+) > create mode 100755 test/unittest/ustack/tst.ustack25_pid.r.p > > diff --git a/test/unittest/ustack/tst.ustack25_pid.r b/test/unittest/ustack/tst.ustack25_pid.r > index 89b35c028..e7732fb81 100644 > --- a/test/unittest/ustack/tst.ustack25_pid.r > +++ b/test/unittest/ustack/tst.ustack25_pid.r > @@ -1,5 +1,6 @@ > > ustack-tst-basic`myfunc_z > + ustack-tst-basic`myfunc_y+{ptr} > ustack-tst-basic`myfunc_x+{ptr} > ustack-tst-basic`myfunc_w+{ptr} > ustack-tst-basic`myfunc_v+{ptr} > diff --git a/test/unittest/ustack/tst.ustack25_pid.r.p b/test/unittest/ustack/tst.ustack25_pid.r.p > new file mode 100755 > index 000000000..6486f7ec6 > --- /dev/null > +++ b/test/unittest/ustack/tst.ustack25_pid.r.p > @@ -0,0 +1,37 @@ > +#!/bin/sh > + > +# A pid entry probe places a uprobe on the first instruction of a function. > +# Unfortunately, this is so early in the function preamble that the function > +# frame pointer has not yet been established and the actual caller of the > +# traced function is missed. > +# > +# In Linux 6.11, x86-specific heuristics are introduced to fix this problem. > +# See commit cfa7f3d > +# ("perf,x86: avoid missing caller address in stack traces captured in uprobe") > +# for both a description of the problem and an explanation of the heuristics. > +# > +# Add post processing to these test results to allow for both cases: > +# caller frame is missing or not missing. > + > +missing_caller=1 > +if [ $(uname -m) == "x86_64" ]; then > + read MAJOR MINOR <<< `uname -r | grep -Eo '^[0-9]+\.[0-9]+' | tr '.' ' '` > + > + if [ $MAJOR -ge 6 ]; then > + if [ $MAJOR -gt 6 -o $MINOR -ge 11 ]; then > + missing_caller=0 > + fi > + fi > +fi > + > +if [ $missing_caller -eq 1 ]; then > + # Add the missing caller function after the current function. > + awk '{ print } > + /myfunc_z/ { print " ustack-tst-basic`myfunc_y+{ptr}" }' > +else > + # The .r results file has an extra frame at the end in case > + # the caller frame is missing and the 25-frame limit goes > + # "too far." If the caller is not missing, fake that extra frame. > + awk '{ print } > + /myfunc_b/ { print " ustack-tst-basic`myfunc_a+{ptr}" }' > +fi > -- > 2.43.5 > From eugene.loh at oracle.com Fri Feb 28 20:55:03 2025 From: eugene.loh at oracle.com (Eugene Loh) Date: Fri, 28 Feb 2025 15:55:03 -0500 Subject: [DTrace-devel] [PATCH] Refactor the versioning handling system In-Reply-To: References: Message-ID: <430ab6de-3ddf-35b2-692d-5e3a386e17c0@oracle.com> How about eliminating DT_VERS_LATEST?? It seems cumbersome and unnecessary to maintain manually. Do tests pass with this patch?? How about: ? ?? ?? test/unittest/options/tst.version.sh ??????? test/unittest/dtrace-util/tst.APIVersion.d Is the plan to update such tests with each new version of DTrace? Ideally (well, in my opinion), it'd be nice if the patch were factored into multiple patches.? The big code movement that merely moves stuff from one file to another would come first.? Then, the smaller changes that actually change how things are done can be in a smaller patch.? (Yeah, I know:? subjective and a question of whether to make things more tedious for the submitter or the reviewer of the changes.) Did it use to be the case that when you ran DTrace you would want to know both what version of the tool is this and what version of the API?? But now there is only one version number?? We've been going that way anyhow, but maybe if we're committing to that simpler versioning, we should say so?? (The whole topic confused me, in any case.) Also... On 2/27/25 22:09, Kris Van Hees via DTrace-devel wrote: > DTrace was handlings versioning data in multiple locations, causing > common mistakes in not consistently updating versions in allplaces. s/allplaces/all places/ > By consolidating all versioning data in dt_version.h a single file For me, a comma after dt_version.h would help with readability. > need to be updated (as far as the source tree is concerned) when a s/need/needs/? > new version is introduced. > > For building, the GNUmakefile and dtrace.spec files will also need > to be updated with the new version number. Maybe this comment about GNUmakefile and dtrace.spec should be in dt_version.h?? Also, what about libdtrace/Build (and libdtrace_VERSION)? > Signed-off-by: Kris Van Hees > --- > libdtrace/Build | 1 + > libdtrace/dt_impl.h | 48 ++++++++----------- > libdtrace/dt_open.c | 19 -------- > libdtrace/dt_subr.c | 80 -------------------------------- > libdtrace/dt_version.c | 93 +++++++++++++++++++++++++++++++++++++ > libdtrace/dt_version.h | 102 ++++++++++++++++++++++++++++------------- > 6 files changed, 181 insertions(+), 162 deletions(-) > create mode 100644 libdtrace/dt_version.c > > diff --git a/libdtrace/Build b/libdtrace/Build > index 51e0f078..57804f55 100644 > --- a/libdtrace/Build > +++ b/libdtrace/Build > @@ -70,6 +70,7 @@ libdtrace-build_SOURCES = dt_aggregate.c \ > dt_strtab.c \ > dt_subr.c \ > dt_symtab.c \ > + dt_version.c \ > dt_work.c \ > dt_xlator.c > > diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h > index 68fb8ec5..60e9b0c9 100644 > --- a/libdtrace/dt_impl.h > +++ b/libdtrace/dt_impl.h > @@ -256,8 +256,6 @@ typedef struct dt_percpu_drops { > */ > #define DT_MAX_NSPECS 16 /* sanity upper bound on speculations */ > > -typedef uint32_t dt_version_t; /* encoded version (see below) */ > - > struct dtrace_hdl { > const dtrace_vector_t *dt_vector; /* library vector, if vectored open */ > void *dt_varg; /* vector argument, if vectored open */ > @@ -645,6 +643,24 @@ enum { > EDT_PRINT, /* missing or corrupt print() record */ > }; > > +/* > + * Stability definitions > + * > + * These #defines are used in the tables of identifiers below to fill in the > + * attribute fields associated with each identifier. The DT_ATTR_* macros are > + * a convenience to permit more concise declarations of common attributes such > + * as Stable/Stable/Common. > + * > + * Refer to the Solaris Dynamic Tracing Guide Stability chapter respectively > + * for an explanation of these DTrace features and their values. > + */ > +#define DT_ATTR_STABCMN { DTRACE_STABILITY_STABLE, \ > + DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON } > + > +#define DT_ATTR_EVOLCMN { DTRACE_STABILITY_EVOLVING, \ > + DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON \ > +} > + > /* > * Interfaces for parsing and comparing DTrace attribute tuples, which describe > * stability and architectural binding information. > @@ -654,31 +670,6 @@ extern dtrace_attribute_t dt_attr_max(dtrace_attribute_t, dtrace_attribute_t); > extern char *dt_attr_str(dtrace_attribute_t, char *, size_t); > extern int dt_attr_cmp(dtrace_attribute_t, dtrace_attribute_t); > > -/* > - * Interfaces for parsing and handling DTrace version strings. Version binding > - * is a feature of the D compiler that is handled completely independently of > - * the DTrace kernel infrastructure, so the definitions are here in libdtrace. > - * Version strings are compiled into an encoded uint32_t which can be compared > - * using C comparison operators. Version definitions are found in dt_open.c. > - */ > -#define DT_VERSION_STRMAX 16 /* enough for "255.4095.4095\0" */ > -#define DT_VERSION_MAJMAX 0xFF /* maximum major version number */ > -#define DT_VERSION_MINMAX 0xFFF /* maximum minor version number */ > -#define DT_VERSION_MICMAX 0xFFF /* maximum micro version number */ > - > -#define DT_VERSION_NUMBER(M, m, u) \ > - ((((M) & 0xFF) << 24) | (((m) & 0xFFF) << 12) | ((u) & 0xFFF)) > - > -#define DT_VERSION_MAJOR(v) (((v) & 0xFF000000) >> 24) > -#define DT_VERSION_MINOR(v) (((v) & 0x00FFF000) >> 12) > -#define DT_VERSION_MICRO(v) ((v) & 0x00000FFF) > - > -extern char *dt_version_num2str(dt_version_t, char *, size_t); > -extern int dt_version_str2num(const char *, dt_version_t *); > -extern int dt_version_defined(dt_version_t); > - > -extern int dt_str2kver(const char *, dt_version_t *); > - > extern uint32_t dt_gen_hval(const char *, uint32_t, size_t); > > /* > @@ -816,9 +807,6 @@ extern const dtrace_attribute_t _dtrace_typattr; /* type ref attributes */ > extern const dtrace_attribute_t _dtrace_prvattr; /* provider attributes */ > extern const dtrace_pattr_t _dtrace_prvdesc; /* provider attribute bundle */ > > -extern const dt_version_t _dtrace_versions[]; /* array of valid versions */ > -extern const char *const _dtrace_version; /* current version string */ > - > extern int _dtrace_strbuckets; /* number of hash buckets for strings */ > extern uint_t _dtrace_stkindent; /* default indent for stack/ustack */ > extern uint_t _dtrace_pidbuckets; /* number of hash buckets for pids */ > diff --git a/libdtrace/dt_open.c b/libdtrace/dt_open.c > index 51c056b2..72c138ce 100644 > --- a/libdtrace/dt_open.c > +++ b/libdtrace/dt_open.c > @@ -42,25 +42,6 @@ > #include > #include > > -const dt_version_t _dtrace_versions[] = { > - DT_VERS_1_0, /* D API 1.0.0 (PSARC 2001/466) Solaris 10 FCS */ > - DT_VERS_1_1, /* D API 1.1.0 Solaris Express 6/05 */ > - DT_VERS_1_2, /* D API 1.2.0 Solaris 10 Update 1 */ > - DT_VERS_1_2_1, /* D API 1.2.1 Solaris Express 4/06 */ > - DT_VERS_1_2_2, /* D API 1.2.2 Solaris Express 6/06 */ > - DT_VERS_1_3, /* D API 1.3 Solaris Express 10/06 */ > - DT_VERS_1_4, /* D API 1.4 Solaris Express 2/07 */ > - DT_VERS_1_4_1, /* D API 1.4.1 Solaris Express 4/07 */ > - DT_VERS_1_5, /* D API 1.5 Solaris Express 7/07 */ > - DT_VERS_1_6, /* D API 1.6 */ > - DT_VERS_1_6_1, /* D API 1.6.1 */ > - DT_VERS_1_6_2, /* D API 1.6.2 */ > - DT_VERS_1_6_3, /* D API 1.6.3 */ > - DT_VERS_1_6_4, /* D API 1.6.4 */ > - DT_VERS_2_0, /* D API 2.0 */ > - 0 > -}; > - > /* > * Table of global identifiers. This is used to populate the global identifier > * hash when a new dtrace client open occurs. For more info see dt_ident.h. > diff --git a/libdtrace/dt_subr.c b/libdtrace/dt_subr.c > index d5dca164..40b66c7d 100644 > --- a/libdtrace/dt_subr.c > +++ b/libdtrace/dt_subr.c > @@ -369,58 +369,6 @@ dt_attr_str(dtrace_attribute_t a, char *buf, size_t len) > return buf; > } > > -char * > -dt_version_num2str(dt_version_t v, char *buf, size_t len) > -{ > - uint_t M = DT_VERSION_MAJOR(v); > - uint_t m = DT_VERSION_MINOR(v); > - uint_t u = DT_VERSION_MICRO(v); > - > - if (u == 0) > - snprintf(buf, len, "%u.%u", M, m); > - else > - snprintf(buf, len, "%u.%u.%u", M, m, u); > - > - return buf; > -} > - > -int > -dt_version_str2num(const char *s, dt_version_t *vp) > -{ > - int i = 0, n[3] = { 0, 0, 0 }; > - char c; > - > - while ((c = *s++) != '\0') { > - if (isdigit(c)) > - n[i] = n[i] * 10 + c - '0'; > - else if (c != '.' || i++ >= sizeof(n) / sizeof(n[0]) - 1) > - return -1; > - } > - > - if (n[0] > DT_VERSION_MAJMAX || > - n[1] > DT_VERSION_MINMAX || > - n[2] > DT_VERSION_MICMAX) > - return -1; > - > - if (vp != NULL) > - *vp = DT_VERSION_NUMBER(n[0], n[1], n[2]); > - > - return 0; > -} > - > -int > -dt_version_defined(dt_version_t v) > -{ > - int i; > - > - for (i = 0; _dtrace_versions[i] != 0; i++) { > - if (_dtrace_versions[i] == v) > - return 1; > - } > - > - return 0; > -} > - > char * > dt_cpp_add_arg(dtrace_hdl_t *dtp, const char *str) > { > @@ -949,34 +897,6 @@ dtrace_uaddr2str(dtrace_hdl_t *dtp, pid_t pid, uint64_t addr, char *str, > return dt_string2str(c, str, nbytes); > } > > -/* > - * The function converts string representation of kernel version > - * into the dt_version_t type. > - */ > -int > -dt_str2kver(const char *kverstr, dt_version_t *vp) > -{ > - int kv1, kv2, kv3; > - int rval; > - > - rval = sscanf(kverstr, "%d.%d.%d", &kv1, &kv2, &kv3); > - > - switch (rval) { > - case 2: > - kv3 = 0; > - break; > - case 3: > - break; > - default: > - return -1; > - } > - > - if (vp) > - *vp = DT_VERSION_NUMBER(kv1, kv2, kv3); > - > - return 0; > -} > - > /* > * Compute a 32-bit hash value for a memory block of given size. > */ > diff --git a/libdtrace/dt_version.c b/libdtrace/dt_version.c > new file mode 100644 > index 00000000..e9ac88be > --- /dev/null > +++ b/libdtrace/dt_version.c > @@ -0,0 +1,93 @@ > +/* > + * 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. > + */ > + > +#include > +#include > + > +#include > + > +const dt_version_t _dtrace_versions[] = DTRACE_VERSIONS; > + > +char * > +dt_version_num2str(dt_version_t v, char *buf, size_t len) > +{ > + uint_t M = DT_VERSION_MAJOR(v); > + uint_t m = DT_VERSION_MINOR(v); > + uint_t u = DT_VERSION_MICRO(v); > + > + if (u == 0) > + snprintf(buf, len, "%u.%u", M, m); > + else > + snprintf(buf, len, "%u.%u.%u", M, m, u); > + > + return buf; > +} > + > +int > +dt_version_str2num(const char *s, dt_version_t *vp) > +{ > + int i = 0, n[3] = { 0, 0, 0 }; > + char c; > + > + while ((c = *s++) != '\0') { > + if (isdigit(c)) > + n[i] = n[i] * 10 + c - '0'; > + else if (c != '.' || i++ >= ARRAY_SIZE(n) - 1) > + return -1; > + } > + > + if (n[0] > DT_VERSION_MAJMAX || > + n[1] > DT_VERSION_MINMAX || > + n[2] > DT_VERSION_MICMAX) > + return -1; > + > + if (vp != NULL) > + *vp = DT_VERSION_NUMBER(n[0], n[1], n[2]); > + > + return 0; > +} > + > +int > +dt_version_defined(dt_version_t v) > +{ > + int i; > + > + for (i = 0; i < ARRAY_SIZE(_dtrace_versions); i++) { > + if (_dtrace_versions[i] == v) > + return 1; > + } > + > + return 0; > +} > + > +/* > + * Convert a string representation of a kernel version string into the > + * a dt_version_t value. > + */ > +int > +dt_str2kver(const char *kverstr, dt_version_t *vp) > +{ > + int kv1, kv2, kv3; > + int rval; > + > + rval = sscanf(kverstr, "%d.%d.%d", &kv1, &kv2, &kv3); > + > + switch (rval) { > + case 2: > + kv3 = 0; > + break; > + case 3: > + break; > + default: > + return -1; > + } > + > + if (vp) > + *vp = DT_VERSION_NUMBER(kv1, kv2, kv3); > + > + return 0; > +} > diff --git a/libdtrace/dt_version.h b/libdtrace/dt_version.h > index 3fd1b3d1..967e22cc 100644 > --- a/libdtrace/dt_version.h > +++ b/libdtrace/dt_version.h > @@ -15,24 +15,6 @@ extern "C" { > #include > #include > > -/* > - * Stability definitions > - * > - * These #defines are used in the tables of identifiers below to fill in the > - * attribute fields associated with each identifier. The DT_ATTR_* macros are > - * a convenience to permit more concise declarations of common attributes such > - * as Stable/Stable/Common. > - * > - * Refer to the Solaris Dynamic Tracing Guide Stability chapter respectively > - * for an explanation of these DTrace features and their values. > - */ > -#define DT_ATTR_STABCMN { DTRACE_STABILITY_STABLE, \ > - DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON } > - > -#define DT_ATTR_EVOLCMN { DTRACE_STABILITY_EVOLVING, \ > - DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON \ > -} > - > /* > * Versioning definitions > * > @@ -46,9 +28,11 @@ extern "C" { > * Refer to the Solaris Dynamic Tracing Guide Versioning chapter for an > * explanation of these DTrace features and their values. > * > - * You must update DT_VERS_LATEST and DT_VERS_STRING when adding a new version, > - * and then add the new version to the _dtrace_versions[] array declared in > - * dt_open.c.. > + * When adding a new version: > + * - Add a new DT_VERS_* macro > + * - Add the new DT_VERS_* macro at the end of the DTRACE_VERSIONS macro > + * - Set DT_VERS_LATEST to the new DT_VERS_* > + * - Update DT_VERS_STRING to reflect the new version > * > * NOTE: Although the DTrace versioning scheme supports the labeling and > * introduction of incompatible changes (e.g. dropping an interface in a > @@ -57,16 +41,18 @@ extern "C" { > * we ever need to provide divergent interfaces, this will need work. > * > * The version number should be increased for every customer visible release > - * of Solaris. The major number should be incremented when a fundamental > - * change has been made that would affect all consumers, and would reflect > - * sweeping changes to DTrace or the D language. The minor number should be > - * incremented when a change is introduced that could break scripts that had > - * previously worked; for example, adding a new built-in variable could break > - * a script which was already using that identifier. The micro number should > - * be changed when introducing functionality changes or major bug fixes that > - * do not affect backward compatibility -- this is merely to make capabilities > - * easily determined from the version number. Minor bugs do not require any > - * modification to the version number. > + * of Linux. of Linux?? But we do not release with Linux versions. > + * - The major number should be incremented when a fundamental change has been > + * made that would affect all consumers, and would reflect sweeping changes > + * to DTrace or the D language. > + * - The minor number should be incremented when a change is introduced that > + * could break scripts that had previously worked; for example, adding a new > + * built-in variable could break a script which was already using that > + * identifier. > + * - The micro number should be changed when introducing functionality changes > + * or major bug fixes that do not affect backward compatibility -- this is > + * merely to make capabilities easily determined from the version number. > + * Minor bugs do not require any modification to the version number. > */ > #define DT_VERS_1_0 DT_VERSION_NUMBER(1, 0, 0) > #define DT_VERS_1_1 DT_VERSION_NUMBER(1, 1, 0) > @@ -84,9 +70,59 @@ extern "C" { > #define DT_VERS_1_6_4 DT_VERSION_NUMBER(1, 6, 4) > #define DT_VERS_2_0 DT_VERSION_NUMBER(2, 0, 0) > #define DT_VERS_2_0_1 DT_VERSION_NUMBER(2, 0, 1) > +#define DT_VERS_2_0_2 DT_VERSION_NUMBER(2, 0, 2) > + > +#define DTRACE_VERSIONS { \ Why was DT_VERS_1_0 dropped from this list? > + DT_VERS_1_1, /* D API 1.1.0 Solaris Express 6/05 */ \ > + DT_VERS_1_2, /* D API 1.2.0 Solaris 10 Update 1 */ \ > + DT_VERS_1_2_1, /* D API 1.2.1 Solaris Express 4/06 */ \ > + DT_VERS_1_2_2, /* D API 1.2.2 Solaris Express 6/06 */ \ > + DT_VERS_1_3, /* D API 1.3 Solaris Express 10/06 */ \ > + DT_VERS_1_4, /* D API 1.4 Solaris Express 2/07 */ \ > + DT_VERS_1_4_1, /* D API 1.4.1 Solaris Express 4/07 */ \ > + DT_VERS_1_5, /* D API 1.5 Solaris Express 7/07 */ \ > + DT_VERS_1_6, /* D API 1.6 */ \ > + DT_VERS_1_6_1, /* D API 1.6.1 */ \ > + DT_VERS_1_6_2, /* D API 1.6.2 */ \ > + DT_VERS_1_6_3, /* D API 1.6.3 */ \ > + DT_VERS_1_6_4, /* D API 1.6.4 */ \ > + DT_VERS_2_0, /* D API 2.0 */ \ > + DT_VERS_2_0_1, /* D API 2.0.1 */ \ > + DT_VERS_2_0_2, /* D API 2.0.2 */ \ > +} > + > +#define DT_VERS_LATEST DT_VERS_2_0_2 > +#define DT_VERS_STRING "Oracle D 2.0.2" > + > +/* > + * Interfaces for parsing and handling DTrace version strings. Version binding > + * is a feature of the D compiler that is handled completely independently of > + * the DTrace kernel infrastructure, so the definitions are here in libdtrace. > + * Version strings are compiled into an encoded uint32_t which can be compared > + * using C comparison operators. > + */ > +#define DT_VERSION_STRMAX 16 /* enough for "255.4095.4095\0" */ > +#define DT_VERSION_MAJMAX 0xFF /* maximum major version number */ > +#define DT_VERSION_MINMAX 0xFFF /* maximum minor version number */ > +#define DT_VERSION_MICMAX 0xFFF /* maximum micro version number */ > + > +#define DT_VERSION_NUMBER(M, m, u) \ > + ((((M) & 0xFF) << 24) | (((m) & 0xFFF) << 12) | ((u) & 0xFFF)) > + > +#define DT_VERSION_MAJOR(v) (((v) & 0xFF000000) >> 24) > +#define DT_VERSION_MINOR(v) (((v) & 0x00FFF000) >> 12) > +#define DT_VERSION_MICRO(v) ((v) & 0x00000FFF) > + > +typedef uint32_t dt_version_t; > + > +extern const dt_version_t _dtrace_versions[]; > +extern const char *const _dtrace_version; > + > +extern char *dt_version_num2str(dt_version_t, char *, size_t); > +extern int dt_version_str2num(const char *, dt_version_t *); > +extern int dt_version_defined(dt_version_t); > > -#define DT_VERS_LATEST DT_VERS_2_0_1 > -#define DT_VERS_STRING "Oracle D 2.0" > +extern int dt_str2kver(const char *, dt_version_t *); > > #ifdef __cplusplus > }