Commit 36cdb6d6 authored by Alastair Robertson's avatar Alastair Robertson

semantic_analyser: Run more passes to resolve types

parent ad969b24
...@@ -83,7 +83,16 @@ void SemanticAnalyser::visit(Map &map) ...@@ -83,7 +83,16 @@ void SemanticAnalyser::visit(Map &map)
bpftrace_.map_args_.insert({map.ident, args}); bpftrace_.map_args_.insert({map.ident, args});
} }
type_ = bpftrace_.map_val_.find(map.ident)->second; auto search_val = bpftrace_.map_val_.find(map.ident);
if (search_val != bpftrace_.map_val_.end()) {
type_ = search_val->second;
}
else {
if (is_final_pass()) {
err_ << "Undefined map: " << map.ident << std::endl;
}
type_ = Type::none;
}
} }
void SemanticAnalyser::visit(Binop &binop) void SemanticAnalyser::visit(Binop &binop)
...@@ -94,7 +103,7 @@ void SemanticAnalyser::visit(Binop &binop) ...@@ -94,7 +103,7 @@ void SemanticAnalyser::visit(Binop &binop)
binop.right->accept(*this); binop.right->accept(*this);
rhs = type_; rhs = type_;
if (pass_ == 2 && lhs != rhs) { if (is_final_pass() && lhs != rhs) {
err_ << "Type mismatch for '" << opstr(binop) << "': "; err_ << "Type mismatch for '" << opstr(binop) << "': ";
err_ << "comparing '" << typestr(lhs) << "' "; err_ << "comparing '" << typestr(lhs) << "' ";
err_ << "with '" << typestr(rhs) << "'" << std::endl; err_ << "with '" << typestr(rhs) << "'" << std::endl;
...@@ -122,9 +131,17 @@ void SemanticAnalyser::visit(AssignMapStatement &assignment) ...@@ -122,9 +131,17 @@ void SemanticAnalyser::visit(AssignMapStatement &assignment)
std::string map_ident = assignment.map->ident; std::string map_ident = assignment.map->ident;
auto search = bpftrace_.map_val_.find(map_ident); auto search = bpftrace_.map_val_.find(map_ident);
if (search != bpftrace_.map_val_.end()) { if (search != bpftrace_.map_val_.end()) {
if (search->second != type_) { if (search->second == Type::none) {
if (is_final_pass()) {
err_ << "Undefined map: " << map_ident << std::endl;
}
else {
search->second = type_;
}
}
else if (search->second != type_) {
err_ << "Type mismatch for " << map_ident << ": "; err_ << "Type mismatch for " << map_ident << ": ";
err_ << "trying to assign variable of type '" << typestr(type_); err_ << "trying to assign value of type '" << typestr(type_);
err_ << "'\n\twhen map already contains a value of type '"; err_ << "'\n\twhen map already contains a value of type '";
err_ << typestr(search->second) << "'\n" << std::endl; err_ << typestr(search->second) << "'\n" << std::endl;
} }
...@@ -143,7 +160,15 @@ void SemanticAnalyser::visit(AssignMapCallStatement &assignment) ...@@ -143,7 +160,15 @@ void SemanticAnalyser::visit(AssignMapCallStatement &assignment)
std::string map_ident = assignment.map->ident; std::string map_ident = assignment.map->ident;
auto search = bpftrace_.map_val_.find(map_ident); auto search = bpftrace_.map_val_.find(map_ident);
if (search != bpftrace_.map_val_.end()) { if (search != bpftrace_.map_val_.end()) {
if (search->second != type_) { if (search->second == Type::none) {
if (is_final_pass()) {
err_ << "Undefined map: " << map_ident << std::endl;
}
else {
search->second = type_;
}
}
else if (search->second != type_) {
err_ << "Type mismatch for " << map_ident << ": "; err_ << "Type mismatch for " << map_ident << ": ";
err_ << "trying to assign result of '" << assignment.call->func; err_ << "trying to assign result of '" << assignment.call->func;
err_ << "()'\n\twhen map already contains a value of type '"; err_ << "()'\n\twhen map already contains a value of type '";
...@@ -170,7 +195,7 @@ void SemanticAnalyser::visit(Probe &probe) ...@@ -170,7 +195,7 @@ void SemanticAnalyser::visit(Probe &probe)
stmt->accept(*this); stmt->accept(*this);
} }
if (pass_ == 2 && bpftrace_.add_probe(probe)) { if (is_final_pass() && bpftrace_.add_probe(probe)) {
err_ << "Invalid probe type: '" << probe.type << "'" << std::endl; err_ << "Invalid probe type: '" << probe.type << "'" << std::endl;
} }
} }
...@@ -184,16 +209,14 @@ void SemanticAnalyser::visit(Program &program) ...@@ -184,16 +209,14 @@ void SemanticAnalyser::visit(Program &program)
int SemanticAnalyser::analyse() int SemanticAnalyser::analyse()
{ {
// Two pass analysis, to handle variables being used before they are defined: // Multiple passes to handle variables being used before they are defined
// - First pass checks assignments
// - Second pass checks expressions
std::string errors; std::string errors;
for (pass_ = 1; pass_ <= 2; pass_++) { for (pass_ = 1; pass_ <= num_passes_; pass_++) {
root_->accept(*this); root_->accept(*this);
errors = err_.str(); errors = err_.str();
if (!errors.empty()) { if (!errors.empty()) {
std::cerr << errors; out_ << errors;
return pass_; return pass_;
} }
} }
...@@ -201,6 +224,11 @@ int SemanticAnalyser::analyse() ...@@ -201,6 +224,11 @@ int SemanticAnalyser::analyse()
return 0; return 0;
} }
bool SemanticAnalyser::is_final_pass() const
{
return pass_ == num_passes_;
}
} // namespace ast } // namespace ast
} // namespace bpftrace } // namespace bpftrace
} // namespace ebpf } // namespace ebpf
...@@ -12,9 +12,10 @@ namespace ast { ...@@ -12,9 +12,10 @@ namespace ast {
class SemanticAnalyser : public Visitor { class SemanticAnalyser : public Visitor {
public: public:
explicit SemanticAnalyser(Node *root, BPFtrace &bpftrace) explicit SemanticAnalyser(Node *root, BPFtrace &bpftrace, std::ostream &out = std::cerr)
: root_(root), : root_(root),
bpftrace_(bpftrace) { } bpftrace_(bpftrace),
out_(out) { }
void visit(Integer &integer) override; void visit(Integer &integer) override;
void visit(Builtin &builtin) override; void visit(Builtin &builtin) override;
...@@ -34,11 +35,15 @@ public: ...@@ -34,11 +35,15 @@ public:
private: private:
Node *root_; Node *root_;
BPFtrace &bpftrace_; BPFtrace &bpftrace_;
std::ostream &out_;
std::ostringstream err_; std::ostringstream err_;
int pass_; int pass_;
const int num_passes_ = 10;
using Type = ebpf::bpftrace::Type; using Type = ebpf::bpftrace::Type;
Type type_; Type type_;
bool is_final_pass() const;
}; };
} // namespace ast } // namespace ast
......
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "bpftrace.h" #include "bpftrace.h"
#include "parser.tab.hh"
#include "semantic_analyser.h" #include "semantic_analyser.h"
namespace ebpf { namespace ebpf {
...@@ -20,6 +21,8 @@ TEST(semantic_analyser, probe_count) ...@@ -20,6 +21,8 @@ TEST(semantic_analyser, probe_count)
MockBPFtrace bpftrace; MockBPFtrace bpftrace;
EXPECT_CALL(bpftrace, add_probe(_)).Times(2); EXPECT_CALL(bpftrace, add_probe(_)).Times(2);
// kprobe:kprobe { 123; }
// kprobe:kprobe { 123; }
Integer expr(123); Integer expr(123);
ExprStatement stmt(&expr); ExprStatement stmt(&expr);
StatementList stmts = {&stmt}; StatementList stmts = {&stmt};
...@@ -28,10 +31,43 @@ TEST(semantic_analyser, probe_count) ...@@ -28,10 +31,43 @@ TEST(semantic_analyser, probe_count)
Probe p2(str, str, &stmts); Probe p2(str, str, &stmts);
ProbeList pl = {&p1, &p2}; ProbeList pl = {&p1, &p2};
Program root(&pl); Program root(&pl);
ebpf::bpftrace::ast::SemanticAnalyser semantics(&root, bpftrace); ebpf::bpftrace::ast::SemanticAnalyser semantics(&root, bpftrace);
semantics.analyse(); semantics.analyse();
} }
TEST(semantic_analyser, undefined_map)
{
BPFtrace bpftrace;
// kprobe:kprobe / @mymap1 == 123 / { 123; }
std::string str = "kprobe";
std::string mapstr1 = "mymap1";
Integer myint(123);
Map map1(mapstr1);
Binop binop(&map1, ebpf::bpftrace::Parser::token::EQ, &myint);
Predicate pred(&binop);
ExprStatement stmt(&myint);
StatementList stmts = {&stmt};
Probe p(str, str, &pred, &stmts);
ProbeList pl = {&p};
Program root(&pl);
std::ostringstream out1;
ebpf::bpftrace::ast::SemanticAnalyser semantics1(&root, bpftrace, out1);
EXPECT_EQ(semantics1.analyse(), 10);
// kprobe:kprobe / @mymap1 == 123 / { 123; @mymap1 = @mymap2; }
std::string mapstr2 = "mymap2";
Map map2(mapstr2);
AssignMapStatement assign(&map1, &map2);
stmts.push_back(&assign);
std::ostringstream out2;
ebpf::bpftrace::ast::SemanticAnalyser semantics2(&root, bpftrace, out2);
EXPECT_EQ(semantics2.analyse(), 10);
}
} // namespace ast } // namespace ast
} // namespace bpftrace } // namespace bpftrace
} // namespace ebpf } // namespace ebpf
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