Commit 5c31a0a6 authored by Alastair Robertson's avatar Alastair Robertson

Codegen for C structs

parent 2f0c01df
...@@ -110,8 +110,10 @@ public: ...@@ -110,8 +110,10 @@ public:
class Cast : public Expression { class Cast : public Expression {
public: public:
Cast(const std::string &type, Expression *expr) : cast_type(type), expr(expr) { } Cast(const std::string &type, bool is_pointer, Expression *expr)
: cast_type(type), is_pointer(is_pointer), expr(expr) { }
std::string cast_type; std::string cast_type;
bool is_pointer;
Expression *expr; Expression *expr;
void accept(Visitor &v) override; void accept(Visitor &v) override;
......
...@@ -180,7 +180,7 @@ void CodegenLLVM::visit(Call &call) ...@@ -180,7 +180,7 @@ void CodegenLLVM::visit(Call &call)
static int printf_id = 0; static int printf_id = 0;
auto args = std::get<1>(bpftrace_.printf_args_.at(printf_id)); auto args = std::get<1>(bpftrace_.printf_args_.at(printf_id));
for (SizedType t : args) for (SizedType &t : args)
{ {
llvm::Type *ty = b_.GetType(t); llvm::Type *ty = b_.GetType(t);
elements.push_back(ty); elements.push_back(ty);
...@@ -291,32 +291,112 @@ void CodegenLLVM::visit(Binop &binop) ...@@ -291,32 +291,112 @@ void CodegenLLVM::visit(Binop &binop)
void CodegenLLVM::visit(Unop &unop) void CodegenLLVM::visit(Unop &unop)
{ {
assert(unop.expr->type.type == Type::integer);
unop.expr->accept(*this); unop.expr->accept(*this);
switch (unop.op) { SizedType &type = unop.expr->type;
case bpftrace::Parser::token::LNOT: expr_ = b_.CreateNot(expr_); break; if (type.type == Type::integer)
case bpftrace::Parser::token::BNOT: expr_ = b_.CreateNeg(expr_); break; {
case bpftrace::Parser::token::MUL: switch (unop.op) {
{ case bpftrace::Parser::token::LNOT: expr_ = b_.CreateNot(expr_); break;
AllocaInst *dst = b_.CreateAllocaBPF(unop.expr->type, "deref"); case bpftrace::Parser::token::BNOT: expr_ = b_.CreateNeg(expr_); break;
b_.CreateProbeRead(dst, unop.expr->type.size, expr_); case bpftrace::Parser::token::MUL:
expr_ = b_.CreateLoad(dst); {
b_.CreateLifetimeEnd(dst); int size = type.size;
break; if (type.is_pointer)
{
// When dereferencing a 32-bit integer, only read in 32-bits, etc.
size = type.pointee_size;
}
AllocaInst *dst = b_.CreateAllocaBPF(SizedType(type.type, size), "deref");
b_.CreateProbeRead(dst, size, expr_);
expr_ = b_.CreateLoad(dst);
b_.CreateLifetimeEnd(dst);
break;
}
default: abort();
} }
default: abort(); }
else if (type.type == Type::cast)
{
// Do nothing
}
else
{
abort();
} }
} }
void CodegenLLVM::visit(FieldAccess &acc) void CodegenLLVM::visit(FieldAccess &acc)
{ {
// TODO SizedType &type = acc.expr->type;
assert(type.type == Type::cast);
acc.expr->accept(*this);
auto &field = bpftrace_.structs_[type.cast_type].fields[acc.field];
if (type.is_internal)
{
// The struct we are reading from has already been pulled into
// BPF-memory, e.g. by being stored in a map.
// Just read from the correct offset of expr_
Value *src = b_.CreateGEP(expr_, {b_.getInt64(0), b_.getInt64(field.offset)});
if (field.type.type == Type::cast)
{
// TODO This should be do-able without allocating more memory here
AllocaInst *dst = b_.CreateAllocaBPF(field.type, "internal_" + type.cast_type + "." + acc.field);
b_.CreateMemCpy(dst, src, field.type.size, 1);
expr_ = dst;
// TODO clean up dst memory?
}
else if (field.type.type == Type::string)
{
expr_ = src;
}
else
{
expr_ = b_.CreateLoad(b_.GetType(field.type), src);
}
}
else
{
// The struct we are reading from has not been pulled into BPF-memory,
// so expr_ will contain an external pointer to the start of the struct
Value *src = b_.CreateAdd(expr_, b_.getInt64(field.offset));
if (field.type.type == Type::cast && !field.type.is_pointer)
{
// struct X
// {
// struct Y y;
// };
//
// We are trying to access an embedded struct, e.g. "x.y"
//
// Instead of copying the entire struct Y in, we'll just store it as a
// pointer internally and dereference later when necessary.
expr_ = src;
}
else if (field.type.type == Type::string)
{
AllocaInst *dst = b_.CreateAllocaBPF(field.type, type.cast_type + "." + acc.field);
b_.CreateProbeRead(dst, field.type.size, src);
expr_ = dst;
}
else
{
AllocaInst *dst = b_.CreateAllocaBPF(field.type, type.cast_type + "." + acc.field);
b_.CreateProbeRead(dst, field.type.size, src);
expr_ = b_.CreateLoad(dst);
b_.CreateLifetimeEnd(dst);
}
}
} }
void CodegenLLVM::visit(Cast &cast) void CodegenLLVM::visit(Cast &cast)
{ {
// TODO cast.expr->accept(*this);
} }
void CodegenLLVM::visit(ExprStatement &expr) void CodegenLLVM::visit(ExprStatement &expr)
...@@ -336,12 +416,32 @@ void CodegenLLVM::visit(AssignMapStatement &assignment) ...@@ -336,12 +416,32 @@ void CodegenLLVM::visit(AssignMapStatement &assignment)
Value *val, *expr; Value *val, *expr;
expr = expr_; expr = expr_;
AllocaInst *key = getMapKey(map); AllocaInst *key = getMapKey(map);
if (assignment.expr->type.type == Type::string) if (map.type.type == Type::string)
{ {
val = expr; val = expr;
} }
else if (map.type.type == Type::cast)
{
if (assignment.expr->type.is_internal)
{
val = expr;
}
else
{
// expr currently contains a pointer to the struct
// We now want to read the entire struct in so we can save it
AllocaInst *dst = b_.CreateAllocaBPF(map.type, map.ident + "_val");
b_.CreateProbeRead(dst, map.type.size, expr);
val = dst;
}
}
else else
{ {
if (map.type.type == Type::integer)
{
// Integers are always stored as 64-bit in map values
expr = b_.CreateIntCast(expr, b_.getInt64Ty(), false);
}
val = b_.CreateAllocaBPF(map.type, map.ident + "_val"); val = b_.CreateAllocaBPF(map.type, map.ident + "_val");
b_.CreateStore(expr, val); b_.CreateStore(expr, val);
} }
...@@ -425,7 +525,7 @@ AllocaInst *CodegenLLVM::getMapKey(Map &map) ...@@ -425,7 +525,7 @@ AllocaInst *CodegenLLVM::getMapKey(Map &map)
{ {
size += expr->type.size; size += expr->type.size;
} }
key = b_.CreateAllocaMapKey(size, map.ident + "_key"); key = b_.CreateAllocaBPF(size, map.ident + "_key");
int offset = 0; int offset = 0;
for (Expression *expr : *map.vargs) { for (Expression *expr : *map.vargs) {
...@@ -455,7 +555,7 @@ AllocaInst *CodegenLLVM::getQuantizeMapKey(Map &map, Value *log2) ...@@ -455,7 +555,7 @@ AllocaInst *CodegenLLVM::getQuantizeMapKey(Map &map, Value *log2)
{ {
size += expr->type.size; size += expr->type.size;
} }
key = b_.CreateAllocaMapKey(size, map.ident + "_key"); key = b_.CreateAllocaBPF(size, map.ident + "_key");
int offset = 0; int offset = 0;
for (Expression *expr : *map.vargs) { for (Expression *expr : *map.vargs) {
......
...@@ -48,7 +48,7 @@ AllocaInst *IRBuilderBPF::CreateAllocaBPF(const SizedType &stype, const std::str ...@@ -48,7 +48,7 @@ AllocaInst *IRBuilderBPF::CreateAllocaBPF(const SizedType &stype, const std::str
return CreateAllocaBPF(ty, name); return CreateAllocaBPF(ty, name);
} }
AllocaInst *IRBuilderBPF::CreateAllocaMapKey(int bytes, const std::string &name) AllocaInst *IRBuilderBPF::CreateAllocaBPF(int bytes, const std::string &name)
{ {
llvm::Type *ty = ArrayType::get(getInt8Ty(), bytes); llvm::Type *ty = ArrayType::get(getInt8Ty(), bytes);
return CreateAllocaBPF(ty, name); return CreateAllocaBPF(ty, name);
...@@ -57,7 +57,7 @@ AllocaInst *IRBuilderBPF::CreateAllocaMapKey(int bytes, const std::string &name) ...@@ -57,7 +57,7 @@ AllocaInst *IRBuilderBPF::CreateAllocaMapKey(int bytes, const std::string &name)
llvm::Type *IRBuilderBPF::GetType(const SizedType &stype) llvm::Type *IRBuilderBPF::GetType(const SizedType &stype)
{ {
llvm::Type *ty; llvm::Type *ty;
if (stype.type == Type::string || stype.type == Type::cast) if (stype.type == Type::string || (stype.type == Type::cast && !stype.is_pointer))
{ {
ty = ArrayType::get(getInt8Ty(), stype.size); ty = ArrayType::get(getInt8Ty(), stype.size);
} }
...@@ -121,22 +121,23 @@ Value *IRBuilderBPF::CreateMapLookupElem(Map &map, AllocaInst *key) ...@@ -121,22 +121,23 @@ Value *IRBuilderBPF::CreateMapLookupElem(Map &map, AllocaInst *key)
CreateCondBr(condition, lookup_success_block, lookup_failure_block); CreateCondBr(condition, lookup_success_block, lookup_failure_block);
SetInsertPoint(lookup_success_block); SetInsertPoint(lookup_success_block);
if (map.type.type == Type::string) if (map.type.type == Type::string || map.type.type == Type::cast)
CreateMemCpy(value, call, map.type.size, 1); CreateMemCpy(value, call, map.type.size, 1);
else else
CreateStore(CreateLoad(getInt64Ty(), call), value); CreateStore(CreateLoad(getInt64Ty(), call), value);
CreateBr(lookup_merge_block); CreateBr(lookup_merge_block);
SetInsertPoint(lookup_failure_block); SetInsertPoint(lookup_failure_block);
if (map.type.type == Type::string) if (map.type.type == Type::string || map.type.type == Type::cast)
CreateMemSet(value, getInt8(0), map.type.size, 1); CreateMemSet(value, getInt8(0), map.type.size, 1);
else else
CreateStore(getInt64(0), value); CreateStore(getInt64(0), value);
CreateBr(lookup_merge_block); CreateBr(lookup_merge_block);
SetInsertPoint(lookup_merge_block); SetInsertPoint(lookup_merge_block);
if (map.type.type == Type::string) if (map.type.type == Type::string || map.type.type == Type::cast)
return value; return value;
return CreateLoad(value); return CreateLoad(value);
} }
......
...@@ -20,7 +20,7 @@ public: ...@@ -20,7 +20,7 @@ public:
AllocaInst *CreateAllocaBPF(llvm::Type *ty, const std::string &name=""); AllocaInst *CreateAllocaBPF(llvm::Type *ty, const std::string &name="");
AllocaInst *CreateAllocaBPF(const SizedType &stype, const std::string &name=""); AllocaInst *CreateAllocaBPF(const SizedType &stype, const std::string &name="");
AllocaInst *CreateAllocaMapKey(int bytes, const std::string &name=""); AllocaInst *CreateAllocaBPF(int bytes, const std::string &name="");
llvm::Type *GetType(const SizedType &stype); llvm::Type *GetType(const SizedType &stype);
CallInst *CreateBpfPseudoCall(int mapfd); CallInst *CreateBpfPseudoCall(int mapfd);
CallInst *CreateBpfPseudoCall(Map &map); CallInst *CreateBpfPseudoCall(Map &map);
......
...@@ -101,7 +101,10 @@ void Printer::visit(FieldAccess &acc) ...@@ -101,7 +101,10 @@ void Printer::visit(FieldAccess &acc)
void Printer::visit(Cast &cast) void Printer::visit(Cast &cast)
{ {
std::string indent(depth_, ' '); std::string indent(depth_, ' ');
out_ << indent << "(" << cast.cast_type << ")" << std::endl; if (cast.is_pointer)
out_ << indent << "(" << cast.cast_type << "*)" << std::endl;
else
out_ << indent << "(" << cast.cast_type << ")" << std::endl;
++depth_; ++depth_;
cast.expr->accept(*this); cast.expr->accept(*this);
......
...@@ -231,22 +231,31 @@ void SemanticAnalyser::visit(Unop &unop) ...@@ -231,22 +231,31 @@ void SemanticAnalyser::visit(Unop &unop)
{ {
unop.expr->accept(*this); unop.expr->accept(*this);
SizedType &type = unop.expr->type;
if (is_final_pass() && if (is_final_pass() &&
unop.expr->type.type != Type::integer && !(type.type == Type::integer) &&
unop.expr->type.type != Type::cast) { !(type.type == Type::cast && unop.op == Parser::token::MUL)) {
err_ << "The " << opstr(unop) << " operator can not be used on expressions of type '" err_ << "The " << opstr(unop) << " operator can not be used on expressions of type '"
<< unop.expr->type << "'" << std::endl; << type << "'" << std::endl;
} }
if (unop.op == Parser::token::MUL && unop.expr->type.type == Type::cast) { if (unop.op == Parser::token::MUL) {
std::string cast_type = unop.expr->type.cast_type; if (type.type == Type::cast) {
if (cast_type.back() == '*') { if (type.is_pointer) {
cast_type.pop_back(); if (bpftrace_.structs_.count(type.cast_type) == 0) {
unop.type = SizedType(Type::cast, 8, cast_type); err_ << "Unknown struct/union: '" << type.cast_type << "'" << std::endl;
return;
}
int cast_size = bpftrace_.structs_[type.cast_type].size;
unop.type = SizedType(Type::cast, cast_size, type.cast_type);
}
else {
err_ << "Can not dereference struct/union of type '" << type.cast_type << "'. "
<< "It is not a pointer." << std::endl;
}
} }
else { else if (type.type == Type::integer) {
err_ << "Can not dereference struct/union of type '" << cast_type << "'. " unop.type = SizedType(Type::integer, type.size);
<< "It is not a pointer." << std::endl;
} }
} }
else { else {
...@@ -258,30 +267,35 @@ void SemanticAnalyser::visit(FieldAccess &acc) ...@@ -258,30 +267,35 @@ void SemanticAnalyser::visit(FieldAccess &acc)
{ {
acc.expr->accept(*this); acc.expr->accept(*this);
if (acc.expr->type.type != Type::cast) { SizedType &type = acc.expr->type;
if (type.type != Type::cast) {
if (is_final_pass()) { if (is_final_pass()) {
err_ << "Can not access field '" << acc.field err_ << "Can not access field '" << acc.field
<< "' on expression of type '" << acc.expr->type << "' on expression of type '" << type
<< "'" << std::endl; << "'" << std::endl;
} }
return; return;
} }
std::string cast_type = acc.expr->type.cast_type; if (type.is_pointer) {
if (cast_type.back() == '*') {
err_ << "Can not access field '" << acc.field << "' on type '" err_ << "Can not access field '" << acc.field << "' on type '"
<< cast_type << "'. Try dereferencing it first, or using '->'" << type.cast_type << "'. Try dereferencing it first, or using '->'"
<< std::endl; << std::endl;
return; return;
} }
if (bpftrace_.structs_.count(type.cast_type) == 0) {
err_ << "Unknown struct/union: '" << type.cast_type << "'" << std::endl;
return;
}
auto fields = bpftrace_.structs_[cast_type].fields; auto fields = bpftrace_.structs_[type.cast_type].fields;
if (fields.count(acc.field) == 0) { if (fields.count(acc.field) == 0) {
err_ << "Struct/union of type '" << cast_type << "' does not contain " err_ << "Struct/union of type '" << type.cast_type << "' does not contain "
<< "a field named '" << acc.field << "'" << std::endl; << "a field named '" << acc.field << "'" << std::endl;
} }
else { else {
acc.type = fields[acc.field].type; acc.type = fields[acc.field].type;
acc.type.is_internal = type.is_internal;
} }
} }
...@@ -289,22 +303,20 @@ void SemanticAnalyser::visit(Cast &cast) ...@@ -289,22 +303,20 @@ void SemanticAnalyser::visit(Cast &cast)
{ {
cast.expr->accept(*this); cast.expr->accept(*this);
std::string cast_type = cast.cast_type; if (bpftrace_.structs_.count(cast.cast_type) == 0) {
if (cast_type.back() == '*') err_ << "Unknown struct/union: '" << cast.cast_type << "'" << std::endl;
cast_type.pop_back();
if (bpftrace_.structs_.count(cast_type) == 0) {
err_ << "Unknown struct/union: '" << cast_type << "'" << std::endl;
return; return;
} }
int cast_size; int cast_size;
if (cast.cast_type.back() == '*') { if (cast.is_pointer) {
cast_size = sizeof(uintptr_t); cast_size = sizeof(uintptr_t);
} }
else { else {
cast_size = bpftrace_.structs_[cast.cast_type].size; cast_size = bpftrace_.structs_[cast.cast_type].size;
} }
cast.type = SizedType(Type::cast, cast_size, cast.cast_type); cast.type = SizedType(Type::cast, cast_size, cast.cast_type);
cast.type.is_pointer = cast.is_pointer;
} }
void SemanticAnalyser::visit(ExprStatement &expr) void SemanticAnalyser::visit(ExprStatement &expr)
...@@ -338,6 +350,11 @@ void SemanticAnalyser::visit(AssignMapStatement &assignment) ...@@ -338,6 +350,11 @@ void SemanticAnalyser::visit(AssignMapStatement &assignment)
else { else {
// This map hasn't been seen before // This map hasn't been seen before
map_val_.insert({map_ident, assignment.expr->type}); map_val_.insert({map_ident, assignment.expr->type});
if (map_val_[map_ident].type == Type::integer) {
// Store all integer values as 64-bit in maps, so that there will
// be space for any integer to be assigned to the map later
map_val_[map_ident].size = 8;
}
} }
if (assignment.expr->type.type == Type::cast) { if (assignment.expr->type.type == Type::cast) {
...@@ -351,6 +368,7 @@ void SemanticAnalyser::visit(AssignMapStatement &assignment) ...@@ -351,6 +368,7 @@ void SemanticAnalyser::visit(AssignMapStatement &assignment)
} }
else { else {
map_val_[map_ident].cast_type = cast_type; map_val_[map_ident].cast_type = cast_type;
map_val_[map_ident].is_internal = true;
} }
} }
} }
......
...@@ -93,7 +93,6 @@ void yyerror(bpftrace::Driver &driver, const char *s); ...@@ -93,7 +93,6 @@ void yyerror(bpftrace::Driver &driver, const char *s);
%type <ast::AttachPointList *> attach_points %type <ast::AttachPointList *> attach_points
%type <ast::AttachPoint *> attach_point %type <ast::AttachPoint *> attach_point
%type <std::string> wildcard %type <std::string> wildcard
%type <std::string> type
%type <std::string> ident %type <std::string> ident
%right ASSIGN %right ASSIGN
...@@ -190,11 +189,8 @@ expr : INT { $$ = new ast::Integer($1); } ...@@ -190,11 +189,8 @@ expr : INT { $$ = new ast::Integer($1); }
| MUL expr %prec DEREF { $$ = new ast::Unop(token::MUL, $2); } | MUL expr %prec DEREF { $$ = new ast::Unop(token::MUL, $2); }
| expr DOT ident { $$ = new ast::FieldAccess($1, $3); } | expr DOT ident { $$ = new ast::FieldAccess($1, $3); }
| expr PTR ident { $$ = new ast::FieldAccess(new ast::Unop(token::MUL, $1), $3); } | expr PTR ident { $$ = new ast::FieldAccess(new ast::Unop(token::MUL, $1), $3); }
| "(" type ")" expr %prec CAST { $$ = new ast::Cast($2, $4); } | "(" IDENT ")" expr %prec CAST { $$ = new ast::Cast($2, false, $4); }
; | "(" IDENT MUL ")" expr %prec CAST { $$ = new ast::Cast($2, true, $5); }
type : IDENT { $$ = $1; }
| IDENT MUL { $$ = $1 + "*"; }
; ;
ident : IDENT { $$ = $1; } ident : IDENT { $$ = $1; }
......
...@@ -13,6 +13,8 @@ std::ostream &operator<<(std::ostream &os, Type type) ...@@ -13,6 +13,8 @@ std::ostream &operator<<(std::ostream &os, Type type)
std::ostream &operator<<(std::ostream &os, const SizedType &type) std::ostream &operator<<(std::ostream &os, const SizedType &type)
{ {
os << type.type; os << type.type;
if (type.is_pointer)
os << "*";
return os; return os;
} }
......
...@@ -36,6 +36,9 @@ public: ...@@ -36,6 +36,9 @@ public:
Type type; Type type;
size_t size; size_t size;
std::string cast_type; std::string cast_type;
bool is_internal = false;
bool is_pointer = false;
size_t pointee_size;
bool operator==(const SizedType &t) const; bool operator==(const SizedType &t) const;
}; };
......
This diff is collapsed.
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "bpftrace.h" #include "bpftrace.h"
#include "clang_parser.h"
#include "driver.h" #include "driver.h"
#include "semantic_analyser.h" #include "semantic_analyser.h"
...@@ -19,6 +20,9 @@ void test(BPFtrace &bpftrace, Driver &driver, const std::string &input, int expe ...@@ -19,6 +20,9 @@ void test(BPFtrace &bpftrace, Driver &driver, const std::string &input, int expe
{ {
ASSERT_EQ(driver.parse_str(input), 0); ASSERT_EQ(driver.parse_str(input), 0);
ClangParser clang;
clang.parse(driver.root_, bpftrace.structs_);
std::stringstream out; std::stringstream out;
ast::SemanticAnalyser semantics(driver.root_, bpftrace, out); ast::SemanticAnalyser semantics(driver.root_, bpftrace, out);
std::stringstream msg; std::stringstream msg;
...@@ -40,21 +44,7 @@ void test(Driver &driver, const std::string &input, int expected_result=0) ...@@ -40,21 +44,7 @@ void test(Driver &driver, const std::string &input, int expected_result=0)
void test(const std::string &input, int expected_result=0) void test(const std::string &input, int expected_result=0)
{ {
Field field = { SizedType(Type::integer, 8), 0 };
Field mystr = { SizedType(Type::string, 8), 8 };
Field type2_field_ptr = { SizedType(Type::cast, 8, "type2*"), 16 };
Field type2_field = { SizedType(Type::cast, 8, "type2"), 24 };
Struct type1 = { 16, {{"field", field},
{"mystr", mystr},
{"type2ptr", type2_field_ptr},
{"type2", type2_field}} };
Struct type2 = { 8, {{"field", field}} };
BPFtrace bpftrace; BPFtrace bpftrace;
bpftrace.structs_["type1"] = type1;
bpftrace.structs_["type2"] = type2;
Driver driver; Driver driver;
test(bpftrace, driver, input, expected_result); test(bpftrace, driver, input, expected_result);
} }
...@@ -217,6 +207,34 @@ TEST(semantic_analyser, variable_type) ...@@ -217,6 +207,34 @@ TEST(semantic_analyser, variable_type)
EXPECT_EQ(st, assignment->var->type); EXPECT_EQ(st, assignment->var->type);
} }
TEST(semantic_analyser, map_integer_sizes)
{
Driver driver;
std::string structs = "struct type1 { int x; }";
test(driver, structs + "kprobe:f { $x = ((type1)0).x; @x = $x; }", 0);
auto var_assignment = static_cast<ast::AssignVarStatement*>(driver.root_->probes->at(0)->stmts->at(0));
auto map_assignment = static_cast<ast::AssignMapStatement*>(driver.root_->probes->at(0)->stmts->at(1));
EXPECT_EQ(SizedType(Type::integer, 4), var_assignment->var->type);
EXPECT_EQ(SizedType(Type::integer, 8), map_assignment->map->type);
}
TEST(semantic_analyser, unop_dereference)
{
test("kprobe:f { *0; }", 0);
test("struct X { int n; } kprobe:f { $x = (X*)0; *$x; }", 0);
test("struct X { int n; } kprobe:f { $x = (X)0; *$x; }", 1);
test("kprobe:f { *\"0\"; }", 10);
}
TEST(semantic_analyser, unop_not)
{
test("kprobe:f { ~0; }", 0);
test("struct X { int n; } kprobe:f { $x = (X*)0; ~$x; }", 10);
test("struct X { int n; } kprobe:f { $x = (X)0; ~$x; }", 10);
test("kprobe:f { ~\"0\"; }", 10);
}
TEST(semantic_analyser, printf) TEST(semantic_analyser, printf)
{ {
test("kprobe:f { printf(\"hi\") }", 0); test("kprobe:f { printf(\"hi\") }", 0);
...@@ -370,24 +388,28 @@ TEST(semantic_analyser, profile) ...@@ -370,24 +388,28 @@ TEST(semantic_analyser, profile)
TEST(semantic_analyser, variable_cast_types) TEST(semantic_analyser, variable_cast_types)
{ {
test("kprobe:f { $x = (type1)cpu; $x = (type1)cpu; }", 0); std::string structs = "struct type1 { int field; } struct type2 { int field; }";
test("kprobe:f { $x = (type1)cpu; $x = (type2)cpu; }", 1); test(structs + "kprobe:f { $x = (type1)cpu; $x = (type1)cpu; }", 0);
test(structs + "kprobe:f { $x = (type1)cpu; $x = (type2)cpu; }", 1);
} }
TEST(semantic_analyser, map_cast_types) TEST(semantic_analyser, map_cast_types)
{ {
test("kprobe:f { @x = (type1)cpu; @x = (type1)cpu; }", 0); std::string structs = "struct type1 { int field; } struct type2 { int field; }";
test("kprobe:f { @x = (type1)cpu; @x = (type2)cpu; }", 1); test(structs + "kprobe:f { @x = (type1)cpu; @x = (type1)cpu; }", 0);
test(structs + "kprobe:f { @x = (type1)cpu; @x = (type2)cpu; }", 1);
} }
TEST(semantic_analyser, variable_casts_are_local) TEST(semantic_analyser, variable_casts_are_local)
{ {
test("kprobe:f { $x = (type1)cpu } kprobe:g { $x = (type2)cpu; }", 0); std::string structs = "struct type1 { int field; } struct type2 { int field; }";
test(structs + "kprobe:f { $x = (type1)cpu } kprobe:g { $x = (type2)cpu; }", 0);
} }
TEST(semantic_analyser, map_casts_are_global) TEST(semantic_analyser, map_casts_are_global)
{ {
test("kprobe:f { @x = (type1)cpu } kprobe:g { @x = (type2)cpu; }", 1); std::string structs = "struct type1 { int field; } struct type2 { int field; }";
test(structs + "kprobe:f { @x = (type1)cpu } kprobe:g { @x = (type2)cpu; }", 1);
} }
TEST(semantic_analyser, cast_unknown_type) TEST(semantic_analyser, cast_unknown_type)
...@@ -397,50 +419,76 @@ TEST(semantic_analyser, cast_unknown_type) ...@@ -397,50 +419,76 @@ TEST(semantic_analyser, cast_unknown_type)
TEST(semantic_analyser, field_access) TEST(semantic_analyser, field_access)
{ {
test("kprobe:f { ((type1)cpu).field }", 0); std::string structs = "struct type1 { int field; }";
test("kprobe:f { $x = (type1)cpu; $x.field }", 0); test(structs + "kprobe:f { ((type1)cpu).field }", 0);
test("kprobe:f { @x = (type1)cpu; @x.field }", 0); test(structs + "kprobe:f { $x = (type1)cpu; $x.field }", 0);
test(structs + "kprobe:f { @x = (type1)cpu; @x.field }", 0);
} }
TEST(semantic_analyser, field_access_wrong_field) TEST(semantic_analyser, field_access_wrong_field)
{ {
test("kprobe:f { ((type1)cpu).blah }", 1); std::string structs = "struct type1 { int field; }";
test("kprobe:f { $x = (type1)cpu; $x.blah }", 1); test(structs + "kprobe:f { ((type1)cpu).blah }", 1);
test("kprobe:f { @x = (type1)cpu; @x.blah }", 1); test(structs + "kprobe:f { $x = (type1)cpu; $x.blah }", 1);
test(structs + "kprobe:f { @x = (type1)cpu; @x.blah }", 1);
} }
TEST(semantic_analyser, field_access_wrong_expr) TEST(semantic_analyser, field_access_wrong_expr)
{ {
test("kprobe:f { 1234->field }", 10); std::string structs = "struct type1 { int field; }";
test(structs + "kprobe:f { 1234->field }", 10);
} }
TEST(semantic_analyser, field_access_types) TEST(semantic_analyser, field_access_types)
{ {
test("kprobe:f { ((type1)0).field == 123 }", 0); std::string structs = "struct type1 { int field; char mystr[8]; }"
test("kprobe:f { ((type1)0).field == \"abc\" }", 10); "struct type2 { int field; }";
test("kprobe:f { ((type1)0).mystr == \"abc\" }", 0); test(structs + "kprobe:f { ((type1)0).field == 123 }", 0);
test("kprobe:f { ((type1)0).mystr == 123 }", 10); test(structs + "kprobe:f { ((type1)0).field == \"abc\" }", 10);
test("kprobe:f { ((type1)0).field == ((type2)0).field }", 0); test(structs + "kprobe:f { ((type1)0).mystr == \"abc\" }", 0);
test("kprobe:f { ((type1)0).mystr == ((type2)0).field }", 10); test(structs + "kprobe:f { ((type1)0).mystr == 123 }", 10);
test(structs + "kprobe:f { ((type1)0).field == ((type2)0).field }", 0);
test(structs + "kprobe:f { ((type1)0).mystr == ((type2)0).field }", 10);
} }
TEST(semantic_analyser, field_access_pointer) TEST(semantic_analyser, field_access_pointer)
{ {
test("kprobe:f { ((type1*)0)->field }", 0); std::string structs = "struct type1 { int field; }";
test("kprobe:f { ((type1*)0).field }", 1); test(structs + "kprobe:f { ((type1*)0)->field }", 0);
test("kprobe:f { *((type1*)0) }", 0); test(structs + "kprobe:f { ((type1*)0).field }", 1);
test(structs + "kprobe:f { *((type1*)0) }", 0);
} }
TEST(semantic_analyser, field_access_sub_struct) TEST(semantic_analyser, field_access_sub_struct)
{ {
test("kprobe:f { ((type1)0).type2ptr->field }", 0); std::string structs = "struct type1 { struct type2 *type2ptr; struct type2 type2; }"
test("kprobe:f { ((type1)0).type2.field }", 0); "struct type2 { int field; }";
test("kprobe:f { $x = (type2)0; $x = ((type1)0).type2 }", 0);
test("kprobe:f { $x = (type2*)0; $x = ((type1)0).type2ptr }", 0); test(structs + "kprobe:f { ((type1)0).type2ptr->field }", 0);
test("kprobe:f { $x = (type1)0; $x = ((type1)0).type2 }", 1); test(structs + "kprobe:f { ((type1)0).type2.field }", 0);
test("kprobe:f { $x = (type1*)0; $x = ((type1)0).type2ptr }", 1); test(structs + "kprobe:f { $x = (type2)0; $x = ((type1)0).type2 }", 0);
test(structs + "kprobe:f { $x = (type2*)0; $x = ((type1)0).type2ptr }", 0);
test(structs + "kprobe:f { $x = (type1)0; $x = ((type1)0).type2 }", 1);
test(structs + "kprobe:f { $x = (type1*)0; $x = ((type1)0).type2ptr }", 1);
}
TEST(semantic_analyser, field_access_is_internal)
{
Driver driver;
std::string structs = "struct type1 { int x; }";
test(driver, structs + "kprobe:f { $x = ((type1)0).x }", 0);
auto var_assignment1 = static_cast<ast::AssignVarStatement*>(driver.root_->probes->at(0)->stmts->at(0));
EXPECT_EQ(false, var_assignment1->var->type.is_internal);
test(driver, structs + "kprobe:f { @type1 = (type1)0; $x = @type1.x }", 0);
auto map_assignment = static_cast<ast::AssignMapStatement*>(driver.root_->probes->at(0)->stmts->at(0));
auto var_assignment2 = static_cast<ast::AssignVarStatement*>(driver.root_->probes->at(0)->stmts->at(1));
EXPECT_EQ(true, map_assignment->map->type.is_internal);
EXPECT_EQ(true, var_assignment2->var->type.is_internal);
} }
} // namespace semantic_analyser } // namespace semantic_analyser
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment