Commit 1e047247 authored by Alastair Robertson's avatar Alastair Robertson

Semantic analyser: casting + field access

parent 7045b1c4
......@@ -23,6 +23,8 @@ public:
Map *map = nullptr; // Only set when this expression is assigned to a map
bool is_literal = false;
bool is_variable = false;
bool is_map = false;
bool is_cast = false;
};
using ExpressionList = std::vector<Expression *>;
......@@ -62,8 +64,10 @@ public:
class Map : public Expression {
public:
explicit Map(std::string &ident) : ident(ident), vargs(nullptr) { }
Map(std::string &ident, ExpressionList *vargs) : ident(ident), vargs(vargs) { }
explicit Map(std::string &ident) : ident(ident), vargs(nullptr) { is_map = true; }
Map(std::string &ident, ExpressionList *vargs) : ident(ident), vargs(vargs) {
is_map = true;
}
std::string ident;
ExpressionList *vargs;
......@@ -107,8 +111,10 @@ public:
class Cast : public Expression {
public:
Cast(const std::string &type, Expression *expr) : type(type), expr(expr) { }
std::string type;
Cast(const std::string &type, Expression *expr) : cast_type(type), expr(expr) {
is_cast = true;
}
std::string cast_type;
Expression *expr;
void accept(Visitor &v) override;
......
......@@ -101,7 +101,7 @@ void Printer::visit(FieldAccess &acc)
void Printer::visit(Cast &cast)
{
std::string indent(depth_, ' ');
out_ << indent << "(" << cast.type << ")" << std::endl;
out_ << indent << "(" << cast.cast_type << ")" << std::endl;
++depth_;
cast.expr->accept(*this);
......
......@@ -281,12 +281,47 @@ void SemanticAnalyser::visit(Unop &unop)
void SemanticAnalyser::visit(FieldAccess &acc)
{
// TODO
acc.expr->accept(*this);
if (acc.expr->type.type != Type::cast) {
err_ << "Can not access field '" << acc.field
<< "' on expression of type '" << acc.expr->type
<< "'" << std::endl;
return;
}
std::string cast_type;
if (acc.expr->is_variable) {
auto var = static_cast<Variable*>(acc.expr);
cast_type = variable_casts_[var->ident];
}
else if (acc.expr->is_map) {
auto map = static_cast<Map*>(acc.expr);
cast_type = map_casts_[map->ident];
}
else if (acc.expr->is_cast) {
auto cast = static_cast<Cast*>(acc.expr);
cast_type = cast->cast_type;
}
else {
abort();
}
auto fields = bpftrace_.structs_[cast_type];
if (fields.count(acc.field) == 0) {
err_ << "Struct/union of type '" << cast_type << "' does not contain "
<< "a field named '" << acc.field << "'" << std::endl;
}
}
void SemanticAnalyser::visit(Cast &cast)
{
// TODO
cast.expr->accept(*this);
cast.type = SizedType(Type::cast, cast.expr->type.size);
if (bpftrace_.structs_.count(cast.cast_type) == 0) {
err_ << "Unknown struct/union: '" << cast.cast_type << "'" << std::endl;
}
}
void SemanticAnalyser::visit(ExprStatement &expr)
......@@ -322,6 +357,20 @@ void SemanticAnalyser::visit(AssignMapStatement &assignment)
// This map hasn't been seen before
map_val_.insert({map_ident, assignment.expr->type});
}
if (assignment.expr->type.type == Type::cast) {
auto cast = static_cast<Cast*>(assignment.expr);
if (map_casts_.count(map_ident) > 0 &&
map_casts_[map_ident] != cast->cast_type) {
err_ << "Type mismatch for " << map_ident << ": ";
err_ << "trying to assign value of type '" << cast->cast_type;
err_ << "'\n\twhen map already contains a value of type '";
err_ << map_casts_[map_ident] << "'\n" << std::endl;
}
else {
map_casts_[map_ident] = cast->cast_type;
}
}
}
void SemanticAnalyser::visit(AssignVarStatement &assignment)
......@@ -352,6 +401,20 @@ void SemanticAnalyser::visit(AssignVarStatement &assignment)
variable_val_.insert({var_ident, assignment.expr->type});
assignment.var->type = assignment.expr->type;
}
if (assignment.expr->type.type == Type::cast) {
auto cast = static_cast<Cast*>(assignment.expr);
if (variable_casts_.count(var_ident) > 0 &&
variable_casts_[var_ident] != cast->cast_type) {
err_ << "Type mismatch for " << var_ident << ": ";
err_ << "trying to assign value of type '" << cast->cast_type;
err_ << "'\n\twhen variable already contains a value of type '";
err_ << variable_casts_[var_ident] << "'\n" << std::endl;
}
else {
variable_casts_[var_ident] = cast->cast_type;
}
}
}
void SemanticAnalyser::visit(Predicate &pred)
......@@ -418,6 +481,7 @@ void SemanticAnalyser::visit(Probe &probe)
{
// Clear out map of variable names - variables should be probe-local
variable_val_.clear();
variable_casts_.clear();
probe_ = &probe;
for (AttachPoint *ap : *probe.attach_points) {
......
......@@ -53,6 +53,8 @@ private:
std::map<std::string, SizedType> variable_val_;
std::map<std::string, SizedType> map_val_;
std::map<std::string, MapKey> map_key_;
std::map<std::string, std::string> variable_casts_;
std::map<std::string, std::string> map_casts_;
bool needs_stackid_map_ = false;
bool has_begin_probe_ = false;
bool has_end_probe_ = false;
......
......@@ -30,6 +30,7 @@ public:
std::map<std::string, std::unique_ptr<IMap>> maps_;
std::map<std::string, std::tuple<uint8_t *, uintptr_t>> sections_;
std::map<std::string, std::map<std::string, int>> structs_;
std::vector<std::tuple<std::string, std::vector<SizedType>>> printf_args_;
std::unique_ptr<IMap> stackid_map_;
std::unique_ptr<IMap> perf_event_map_;
......
......@@ -34,6 +34,7 @@ std::string typestr(Type t)
case Type::string: return "string"; break;
case Type::sym: return "sym"; break;
case Type::usym: return "usym"; break;
case Type::cast: return "cast"; break;
default: abort();
}
}
......
......@@ -23,6 +23,7 @@ enum class Type
string,
sym,
usym,
cast,
};
std::ostream &operator<<(std::ostream &os, Type type);
......
......@@ -184,10 +184,12 @@ TEST(semantic_analyser, variable_use_before_assign)
TEST(semantic_analyser, maps_are_global)
{
test("kprobe:f { @x = 1 } kprobe:g { @y = @x }", 0);
test("kprobe:f { @x = 1 } kprobe:g { @x = \"abc\" }", 1);
}
TEST(semantic_analyser, variables_are_local)
{
test("kprobe:f { $x = 1 } kprobe:g { $x = \"abc\"; }", 0);
test("kprobe:f { $x = 1 } kprobe:g { @y = $x }", 1);
}
......@@ -298,6 +300,68 @@ TEST(semantic_analyser, profile)
test("profile { 1 }", 1);
}
TEST(semantic_analyser, variable_cast_types)
{
BPFtrace bpftrace;
bpftrace.structs_["type1"]["field"] = 0;
bpftrace.structs_["type2"]["field"] = 0;
test(bpftrace, "kprobe:f { $x = (type1)cpu; $x = (type1)cpu; }", 0);
test(bpftrace, "kprobe:f { $x = (type1)cpu; $x = (type2)cpu; }", 1);
}
TEST(semantic_analyser, map_cast_types)
{
BPFtrace bpftrace;
bpftrace.structs_["type1"]["field"] = 0;
bpftrace.structs_["type2"]["field"] = 0;
test(bpftrace, "kprobe:f { @x = (type1)cpu; @x = (type1)cpu; }", 0);
test(bpftrace, "kprobe:f { @x = (type1)cpu; @x = (type2)cpu; }", 1);
}
TEST(semantic_analyser, variable_casts_are_local)
{
BPFtrace bpftrace;
bpftrace.structs_["type1"]["field"] = 0;
bpftrace.structs_["type2"]["field"] = 0;
test(bpftrace, "kprobe:f { $x = (type1)cpu } kprobe:g { $x = (type2)cpu; }", 0);
}
TEST(semantic_analyser, map_casts_are_global)
{
BPFtrace bpftrace;
bpftrace.structs_["type1"]["field"] = 0;
bpftrace.structs_["type2"]["field"] = 0;
test(bpftrace, "kprobe:f { @x = (type1)cpu } kprobe:g { @x = (type2)cpu; }", 1);
}
TEST(semantic_analyser, cast_unknown_type)
{
test("kprobe:f { (faketype)cpu }", 1);
}
TEST(semantic_analyser, field_access)
{
BPFtrace bpftrace;
bpftrace.structs_["type1"]["field"] = 0;
test(bpftrace, "kprobe:f { ((type1)cpu).field }", 0);
test(bpftrace, "kprobe:f { $x = (type1)cpu; $x.field }", 0);
test(bpftrace, "kprobe:f { @x = (type1)cpu; @x.field }", 0);
}
TEST(semantic_analyser, field_access_wrong_field)
{
BPFtrace bpftrace;
bpftrace.structs_["type1"]["field"] = 0;
test(bpftrace, "kprobe:f { ((type1)cpu).blah }", 1);
test(bpftrace, "kprobe:f { $x = (type1)cpu; $x.blah }", 1);
test(bpftrace, "kprobe:f { @x = (type1)cpu; @x.blah }", 1);
}
TEST(semantic_analyser, field_access_wrong_expr)
{
test("kprobe:f { 1234->field }", 1);
}
} // namespace semantic_analyser
} // namespace test
} // namespace bpftrace
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