[DTrace-devel] [PATCH 4/6] Fix bit-field operations

Eugene Loh eugene.loh at oracle.com
Thu Apr 1 22:05:49 PDT 2021


On 3/31/21 10:50 PM, Kris Van Hees wrote:

> On Fri, Mar 19, 2021 at 01:45:32PM -0400, eugene.loh at oracle.com wrote:
>> From: Eugene Loh <eugene.loh at oracle.com>
>>
>> Fix bugs in the dt_cg_field_[set|get] functions.
>>
>> Add tests for the functionality.  Note that, due to an earlier patch
>> ("6d9c2398 Provide a more consistent implementation of the trace() action"),
>> results for signed bit fields with a nonzero sign bit differ from the
>> incorrect results of DTv1.  Current results match GCC.
> This is confusing.  I don't think we need to make any reference to DTrace v1
> (the original DTrace port to Linux that we did years ago).  The tests do not
> exist in the earlier version, so there is no need to compare.  You could
> mention that you verified the behaviour with this patch and that it matches
> the legacy implementation or something like that.

I'm confused.  The results do NOT match the legacy implementation, which 
is bad because it's missing the patch in question.  I'm not saying my 
wording is not confusing, but only that I don't understand your 
recommendation.

>> (Also note that bit fields are not packed together as efficiently as
>> possible.  Rather, following legacy DTrace behavior, each bit field is
>> aligned to the size of the next largest integer type.  Thus, simplifications
>> in BPF code generation can be imagined.  Or, bit fields should be packed more
>> tightly together, in a departure from earlier DTrace implementations but
>> bringing behavior in line with common user expectations.)
> One consideration here os whether this has an impact on accessing kernel
> structures.  If a kernel structure contains a bit field and we use that
> datatype (or the structure) to declare a D variable, can we access the
> content of the bit field in the kernel structure correctly?  And also any
> other data, e.g. other bit fields?  If GCC packs them together, but D is
> expecting the datatype to have them stored differently we will run into
> trouble.  This is something you may want to test on the legacy implementation
> because I don't think we can really access the content of kernel structures
> just yet.  But knowing whether there is an issue here would help fine tune
> the above paragraph in the commit message.

Consider the following test program.  All it does is prepare some 
"struct perf_event_attr", which has some bit fields.  Most of the struct 
is zero.  It then calls perf_event_open(), which won't really care much 
about the struct contents since pid=cpu=-1 is not legal.

         % cat perf_event_open.c
         #include <stdio.h>
         #include <string.h>
         #include <sys/syscall.h>
         #include <linux/perf_event.h>

         int main(int c, char **v) {
             struct perf_event_attr attr;

             memset(&attr, 0, sizeof(struct perf_event_attr));
             attr.type = 0xfeedface;
             attr.size = 0xdeadbeef;
             attr.config = 0x1234567887654321ll;
             attr.sample_freq = 0xcafecafecafecafell;
             attr.sample_type = 0xaabbccddeeffabcdll;
             attr.read_format = 0x9876543210fedcball;

             attr.wakeup_events = 0x11223344;
             attr.bp_type       = 0x55667788;

             /* using pid = cpu = -1 ensures this call will return an 
error */
             syscall(__NR_perf_event_open, &attr, -1, -1, -1, 0);

             return 0;
         }
         % gcc perf_event_open.c

Now we run a D script.  At perf_event_open:entry, it examines the struct 
contents.  Specifically, it dumps out the first fields as a sanity test 
and everything checks out okay:

         # /usr/sbin/dtrace -qn '
           syscall:vmlinux:perf_event_open:entry {
               p = (struct perf_event_attr *)arg0;
               printf("%x\n", p->type);
               printf("%x\n", p->size);
               printf("%x\n", p->config);
               printf("%x\n", p->sample_freq);
               printf("%x\n", p->sample_type);
               printf("%x\n", p->read_format);
            /* printf("%x\n", p->disabled); */
               exit(0);
           }' -c ./a.out
         feedface
         deadbeef
         1234567887654321
         cafecafecafecafe
         aabbccddeeffabcd
         9876543210fedcba

If we try a bit field (by uncommenting that line in the D script), we 
get failure:
         dtrace: invalid probe specifier
         syscall:vmlinux:perf_event_open:entry
         {......}: cg: bad field: off 320 type <1356> bits 18109968

which is presumably in dt_cg_field_get().  So, I start debugging... but 
I cannot reproduce the behavior!  Both the installed /usr/sbin/dtrace 
and my debugging dtrace report version 1.6.4.  My debugging build gets 
to dt_cg_node and looks at DT_TOK_DOT but decides that DT_NF_BITFIELD is 
not set and therefore does not call dt_cg_field_get().  So, it does not 
abort with the "bad field" messages.  It happily prints all the bit 
fields I want, always claiming they're 0x20, which is wrong.

So I don't quite know what's going on, but that's the status so far.  
DTv1 is not correctly reporting bit fields.

Back to the subject of the commit message, how about the following in 
place of the last paragraph?

     (Also note that bit fields are not packed together as efficiently as
     possible.  Rather, following legacy DTrace behavior, each bit field is
     aligned to the size of the next largest integer type.  This is a bug,
     preventing users from reading bit fields in kernel structs, but it is
     outside the scope of this patch.)



More information about the DTrace-devel mailing list