[DTrace-devel] [PATCH] test: Allow duplicate usym/umod/uaddr if for different pids
Eugene Loh
eugene.loh at oracle.com
Thu Feb 27 21:32:13 UTC 2025
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 <eugene.loh at oracle.com>
>>>>
>>>> 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 <eugene.loh at oracle.com>
>>> Reviewed-by: Kris Van Hees <kris.van.hees at oracle.com>
>>>
>>>> ---
>>>> 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 <<EOF
>>>> + 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 <<EOF
>>>> + 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 <<EOF
>>>> + 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
>>>>
More information about the DTrace-devel
mailing list