Skip to content

Commit

Permalink
offsetof: Support sub-field
Browse files Browse the repository at this point in the history
Since bpftrace PR [1] support offsetof() function. As previously discussed [2],
the current offsetof() does not support sub-fields, and this PR implements
offsetof(struct foo, a.b.c).

Example:

    struct Foo {
        struct e {
            int a;
            int b;
            struct c {
                int a, b, c;
            } c;
        } e;
    }

    struct offsetof {
        struct {
            int comm;
            int ustack;
        } kstack;
    }

    BEGIN {
        printf("struct Foo e.b offset %ld\n", offsetof(struct Foo, e.b));
        printf("struct Foo e.c offset %ld\n", offsetof(struct Foo, e.c));
        printf("struct Foo e.c.b offset %ld\n", offsetof(struct Foo, e.c.b));

        printf("struct offsetof kstack.ustack offset %ld\n", offsetof(struct offsetof, kstack.ustack));

        exit();
    }

Link: bpftrace#2579 [1] merged
Link: bpftrace#2216 [2]
Signed-off-by: Rong Tao <rongtao@cestc.cn>
  • Loading branch information
Rtoax committed Jan 31, 2025
1 parent 04c87f6 commit 66c6a2d
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 15 deletions.
54 changes: 54 additions & 0 deletions of.bt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!./src/bpftrace

struct Foo {
int a;
long b;
char c;
struct {
int d;
};
struct e {
int a;
int b;
struct c {
int a, b, c;
} c;
} e;
}
struct offsetof {
int bswap;
int comm;
int kstack;
int ustack;
int pid;
int ctx;
int arg;
}

union Bar {
int a;
};

BEGIN {
printf("%ld\n", offsetof(struct Foo, c));

printf("%ld\n", offsetof(struct Foo, d));
// printf("%ld\n", offsetof(struct Foo_1, d));

printf("e.a %ld\n", offsetof(struct Foo, e.a));
printf("e.b %ld\n", offsetof(struct Foo, e.b));
printf("e.c %ld\n", offsetof(struct Foo, e.c));
printf("e.c.a %ld\n", offsetof(struct Foo, e.c.a));
printf("e.c.b %ld\n", offsetof(struct Foo, e.c.b));

printf("%ld union\n", offsetof(union Bar, a));

printf("%ld\n", offsetof(struct offsetof, bswap));

printf("%ld\n", offsetof(struct task_struct, comm));
printf("%ld\n", offsetof(*curtask, comm));

printf("%ld\n", offsetof(*(struct task_struct *)0, comm));

exit();
}
17 changes: 15 additions & 2 deletions src/ast/passes/codegen_llvm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1522,8 +1522,21 @@ void CodegenLLVM::visit(Sizeof &szof)

void CodegenLLVM::visit(Offsetof &ofof)
{
auto &field = ofof.record.GetField(ofof.field);
expr_ = b_.getInt64(field.offset);
ssize_t offset = 0;
std::string token;
std::stringstream ss(ofof.field);
std::vector<std::string> tokens;

while (std::getline(ss, token, '.')) {
tokens.push_back(token);
}

const SizedType *record = &ofof.record;
for (const auto& token : tokens) {
offset += record->GetField(token).offset;
record = &record->GetField(token).type;
}
expr_ = b_.getInt64(offset);
}

void CodegenLLVM::visit(Map &map)
Expand Down
34 changes: 25 additions & 9 deletions src/ast/passes/semantic_analyser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1615,17 +1615,33 @@ void SemanticAnalyser::visit(Offsetof &ofof)
Visit(ofof.expr);
ofof.record = ofof.expr->type;
}

resolve_struct_type(ofof.record, ofof.loc);

if (!ofof.record.IsRecordTy()) {
LOG(ERROR, ofof.loc, err_)
<< "offsetof() 1st argument is not of a record type.";
} else if (!bpftrace_.structs.Has(ofof.record.GetName())) {
LOG(ERROR, ofof.loc, err_) << "'" << ofof.record << "' does not exist.";
} else if (!ofof.record.HasField(ofof.field)) {
LOG(ERROR, ofof.loc, err_) << "'" << ofof.record << "' "
<< "has no field named "
<< "'" << ofof.field << "'";
std::string token;
std::stringstream ss(ofof.field);
std::vector<std::string> tokens;
while (std::getline(ss, token, '.')) {
tokens.push_back(token);
}

// Check if all sub-fields are present.
SizedType record = ofof.record;
for (const auto& token : tokens) {
if (!record.IsRecordTy()) {
LOG(ERROR, ofof.loc, err_) << "'" << record << "' "
<< "is not of a record type.";
} else if (!bpftrace_.structs.Has(record.GetName())) {
LOG(ERROR, ofof.loc, err_) << "'" << record.GetName()
<< "' does not exist.";
} else if (!record.HasField(token)) {
LOG(ERROR, ofof.loc, err_) << "'" << record.GetName() << "' "
<< "has no field named "
<< "'" << token << "'";
} else {
// Get next sub-field
record = record.GetField(token).type;
}
}
}

Expand Down
13 changes: 9 additions & 4 deletions src/parser.yy
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ void yyerror(bpftrace::Driver &driver, const char *s);


%type <ast::Operator> unary_op compound_op
%type <std::string> attach_point_def c_definitions ident keyword external_name
%type <std::string> attach_point_def c_definitions ident keyword external_name struct_field

%type <ast::AttachPoint *> attach_point
%type <ast::AttachPointList> attach_points
Expand Down Expand Up @@ -626,10 +626,15 @@ sizeof_expr:
| SIZEOF "(" expr ")" { $$ = driver.ctx.make_node<ast::Sizeof>($3, @$); }
;

struct_field:
external_name { $$ = $1; }
| struct_field DOT external_name { $$ = $1 + "." + $3; }
;

offsetof_expr:
OFFSETOF "(" struct_type "," external_name ")" { $$ = driver.ctx.make_node<ast::Offsetof>($3, $5, @$); }
/* For example: offsetof(*curtask, comm) */
| OFFSETOF "(" expr "," external_name ")" { $$ = driver.ctx.make_node<ast::Offsetof>($3, $5, @$); }
OFFSETOF "(" struct_type "," struct_field ")" { $$ = driver.ctx.make_node<ast::Offsetof>($3, $5, @$); }
/* For example: offsetof(*curtask, comm) */
| OFFSETOF "(" expr "," struct_field ")" { $$ = driver.ctx.make_node<ast::Offsetof>($3, $5, @$); }
;

int:
Expand Down

0 comments on commit 66c6a2d

Please sign in to comment.