From faea8c8469c53ecb1ed82698bc48656462b48dcd Mon Sep 17 00:00:00 2001 From: Brenden Blanco Date: Wed, 29 Mar 2017 09:58:31 -0700 Subject: [PATCH] Add TableStorage class for wrapping bpf map tracking Adds a TableStorage class for use by language frontends to store/access references to loaded bpf maps. Includes support for shared and namespaced maps, in a directory-like hierarchy. Add a FileDesc helper class to automatically wrap open file descriptors. The object prevents implicit copying of the fd (allows only rvalue/move()), and takes care of the close() call. Add a reference implementation of a TableStorageImpl that performs the current default behavior expected by BPF_TABLE_PUBLIC, which is to share maps between BPFModules in the same-process only. A stub implementation for bpffs is started. Update b/clang frontends to use this new class. Also included is a framework for extracting the type information of maps in an extensible way. Migrate BMapDeclVisitor to use this as the first consumer. Signed-off-by: Brenden Blanco --- .clang-format | 2 +- src/cc/BPF.cc | 3 +- src/cc/BPF.h | 11 +- src/cc/BPFTable.h | 8 +- src/cc/CMakeLists.txt | 6 +- src/cc/bpf_module.cc | 100 +++++++++-------- src/cc/bpf_module.h | 9 +- src/cc/bpffs_table.cc | 75 +++++++++++++ src/cc/frontends/b/codegen_llvm.cc | 24 ++--- src/cc/frontends/b/codegen_llvm.h | 5 +- src/cc/frontends/b/loader.cc | 7 +- src/cc/frontends/b/loader.h | 7 +- src/cc/frontends/clang/b_frontend_action.cc | 162 +++++++++------------------- src/cc/frontends/clang/b_frontend_action.h | 37 +++---- src/cc/frontends/clang/loader.cc | 8 +- src/cc/frontends/clang/loader.h | 9 +- src/cc/json_map_decl_visitor.cc | 123 +++++++++++++++++++++ src/cc/shared_table.cc | 103 +++++++++++++----- src/cc/shared_table.h | 24 ----- src/cc/table_desc.h | 100 ++++++++++++++++- src/cc/table_storage.cc | 100 +++++++++++++++++ src/cc/table_storage.h | 110 +++++++++++++++++++ src/cc/table_storage_impl.h | 51 +++++++++ 23 files changed, 806 insertions(+), 278 deletions(-) create mode 100644 src/cc/bpffs_table.cc create mode 100644 src/cc/json_map_decl_visitor.cc create mode 100644 src/cc/table_storage.cc create mode 100644 src/cc/table_storage.h create mode 100644 src/cc/table_storage_impl.h diff --git a/.clang-format b/.clang-format index d5a3996..2a27948 100644 --- a/.clang-format +++ b/.clang-format @@ -3,4 +3,4 @@ BasedOnStyle: Google AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false IndentCaseLabels: false -AccessModifierOffset: -2 +AccessModifierOffset: -1 diff --git a/src/cc/BPF.cc b/src/cc/BPF.cc index 809bfdb..3b0ad35 100644 --- a/src/cc/BPF.cc +++ b/src/cc/BPF.cc @@ -28,9 +28,10 @@ #include "bcc_exception.h" #include "bcc_syms.h" #include "bpf_module.h" +#include "common.h" #include "libbpf.h" #include "perf_reader.h" -#include "common.h" +#include "table_storage.h" #include "usdt.h" #include "BPF.h" diff --git a/src/cc/BPF.h b/src/cc/BPF.h index 532f052..85a7e13 100644 --- a/src/cc/BPF.h +++ b/src/cc/BPF.h @@ -26,6 +26,7 @@ #include "bpf_module.h" #include "compat/linux/bpf.h" #include "libbpf.h" +#include "table_storage.h" static const int DEFAULT_PERF_BUFFER_PAGE_CNT = 8; @@ -38,12 +39,14 @@ struct open_probe_t { }; class USDT; +class TableStorage; class BPF { public: static const int BPF_MAX_STACK_DEPTH = 127; - explicit BPF(unsigned int flag = 0) : bpf_module_(new BPFModule(flag)) {} + explicit BPF(unsigned int flag = 0, TableStorage* ts = nullptr) + : bpf_module_(new BPFModule(flag, ts)), ts_(ts) {} StatusTuple init(const std::string& bpf_program, std::vector cflags = {}, std::vector usdt = {}); @@ -90,6 +93,11 @@ public: template BPFHashTable get_hash_table(const std::string& name) { + if (ts_) { + TableStorage::iterator it; + if (ts_->Find(Path({name}), it)) + return BPFHashTable(it->second); + } return BPFHashTable(bpf_module_.get(), name); } @@ -152,6 +160,7 @@ private: uint64_t symbol_addr, bcc_symbol* output); std::unique_ptr bpf_module_; + TableStorage* ts_; std::map funcs_; diff --git a/src/cc/BPFTable.h b/src/cc/BPFTable.h index 4e2debf..a0e568a 100644 --- a/src/cc/BPFTable.h +++ b/src/cc/BPFTable.h @@ -28,6 +28,7 @@ #include "bpf_module.h" #include "libbpf.h" #include "perf_reader.h" +#include "table_desc.h" namespace ebpf { @@ -44,6 +45,10 @@ protected: fd_ = bpf_module->table_fd(id_); capacity_ = bpf_module->table_max_entries(id_); }; + explicit BPFTableBase(const TableDesc& desc) { + fd_ = desc.fd; + capacity_ = desc.max_entries; + } bool lookup(KeyType* key, ValueType* value) { return bpf_lookup_elem(fd_, static_cast(key), @@ -70,7 +75,8 @@ protected: template class BPFHashTable : protected BPFTableBase { -public: + public: + explicit BPFHashTable(const TableDesc& desc) : BPFTableBase(desc) {} BPFHashTable(BPFModule* bpf_module, const std::string& name) : BPFTableBase(bpf_module, name) {} diff --git a/src/cc/CMakeLists.txt b/src/cc/CMakeLists.txt index 5a738db..d282a7e 100644 --- a/src/cc/CMakeLists.txt +++ b/src/cc/CMakeLists.txt @@ -35,12 +35,12 @@ if (CMAKE_COMPILER_IS_GNUCC AND LIBCLANG_ISSTATIC) endif() endif() -add_library(bcc-shared SHARED bpf_common.cc bpf_module.cc libbpf.c perf_reader.c shared_table.cc exported_files.cc bcc_elf.c bcc_perf_map.c bcc_proc.c bcc_syms.cc usdt_args.cc usdt.cc common.cc BPF.cc BPFTable.cc) +add_library(bcc-shared SHARED bpf_common.cc bpf_module.cc libbpf.c perf_reader.c table_storage.cc shared_table.cc bpffs_table.cc json_map_decl_visitor.cc exported_files.cc bcc_elf.c bcc_perf_map.c bcc_proc.c bcc_syms.cc usdt_args.cc usdt.cc common.cc BPF.cc BPFTable.cc) set_target_properties(bcc-shared PROPERTIES VERSION ${REVISION_LAST} SOVERSION 0) set_target_properties(bcc-shared PROPERTIES OUTPUT_NAME bcc) add_library(bcc-loader-static STATIC libbpf.c perf_reader.c bcc_elf.c bcc_perf_map.c bcc_proc.c) -add_library(bcc-static STATIC bpf_common.cc bpf_module.cc shared_table.cc exported_files.cc bcc_syms.cc usdt_args.cc usdt.cc common.cc BPF.cc BPFTable.cc) +add_library(bcc-static STATIC bpf_common.cc bpf_module.cc shared_table.cc bpffs_table.cc json_map_decl_visitor.cc table_storage.cc exported_files.cc bcc_syms.cc usdt_args.cc usdt.cc common.cc BPF.cc BPFTable.cc) set_target_properties(bcc-static PROPERTIES OUTPUT_NAME bcc) set(llvm_raw_libs bitwriter bpfcodegen irreader linker @@ -67,7 +67,7 @@ target_link_libraries(bcc-static b_frontend clang_frontend bcc-loader-static ${c install(TARGETS bcc-shared LIBRARY COMPONENT libbcc DESTINATION ${CMAKE_INSTALL_LIBDIR}) -install(FILES bpf_common.h bpf_module.h bcc_syms.h bcc_exception.h libbpf.h perf_reader.h BPF.h BPFTable.h shared_table.h COMPONENT libbcc +install(FILES bpf_common.h bpf_module.h bcc_syms.h bcc_exception.h libbpf.h perf_reader.h BPF.h BPFTable.h shared_table.h table_desc.h COMPONENT libbcc DESTINATION include/bcc) install(DIRECTORY compat/linux/ COMPONENT libbcc DESTINATION include/bcc/compat/linux diff --git a/src/cc/bpf_module.cc b/src/cc/bpf_module.cc index 77abe72..ec47272 100644 --- a/src/cc/bpf_module.cc +++ b/src/cc/bpf_module.cc @@ -101,8 +101,8 @@ class MyMemoryManager : public SectionMemoryManager { map> *sections_; }; -BPFModule::BPFModule(unsigned flags) - : flags_(flags), ctx_(new LLVMContext) { +BPFModule::BPFModule(unsigned flags, TableStorage *ts) + : flags_(flags), ctx_(new LLVMContext), ts_(ts) { InitializeNativeTarget(); InitializeNativeTargetAsmPrinter(); LLVMInitializeBPFTarget(); @@ -110,21 +110,17 @@ BPFModule::BPFModule(unsigned flags) LLVMInitializeBPFTargetInfo(); LLVMInitializeBPFAsmPrinter(); LLVMLinkInMCJIT(); /* call empty function to force linking of MCJIT */ + if (!ts_) { + local_ts_ = createSharedTableStorage(); + ts_ = &*local_ts_; + } } BPFModule::~BPFModule() { engine_.reset(); rw_engine_.reset(); ctx_.reset(); - if (tables_) { - for (auto table : *tables_) { - if (table.is_shared) { - SharedTables::instance()->remove_fd(table.name); - } else if (!table.is_extern) { - close(table.fd); - } - } - } + ts_->DeletePrefix(Path({std::to_string((uintptr_t)this)})); } static void debug_printf(Module *mod, IRBuilder<> &B, const string &fmt, vector args) { @@ -326,7 +322,8 @@ unique_ptr BPFModule::finalize_rw(unique_ptr m) { // load an entire c file as a module int BPFModule::load_cfile(const string &file, bool in_memory, const char *cflags[], int ncflags) { clang_loader_ = make_unique(&*ctx_, flags_); - if (clang_loader_->parse(&mod_, &tables_, file, in_memory, cflags, ncflags)) + if (clang_loader_->parse(&mod_, *ts_, file, in_memory, cflags, ncflags, + std::to_string((uintptr_t)this))) return -1; return 0; } @@ -338,7 +335,7 @@ int BPFModule::load_cfile(const string &file, bool in_memory, const char *cflags // build an ExecutionEngine. int BPFModule::load_includes(const string &text) { clang_loader_ = make_unique(&*ctx_, flags_); - if (clang_loader_->parse(&mod_, &tables_, text, true, nullptr, 0)) + if (clang_loader_->parse(&mod_, *ts_, text, true, nullptr, 0, "")) return -1; return 0; } @@ -352,7 +349,10 @@ int BPFModule::annotate() { auto m = make_unique("sscanf", *ctx_); size_t id = 0; - for (auto &table : *tables_) { + Path path({std::to_string((uintptr_t)this)}); + for (auto it = ts_->lower_bound(path), up = ts_->upper_bound(path); it != up; ++it) { + TableDesc &table = it->second; + tables_.push_back(&it->second); table_names_[table.name] = id++; GlobalValue *gvar = mod_->getNamedValue(table.name); if (!gvar) continue; @@ -507,9 +507,7 @@ unsigned BPFModule::kern_version() const { return *(unsigned *)get<0>(section->second); } -size_t BPFModule::num_tables() const { - return tables_->size(); -} +size_t BPFModule::num_tables() const { return tables_.size(); } size_t BPFModule::table_id(const string &name) const { auto it = table_names_.find(name); @@ -522,8 +520,9 @@ int BPFModule::table_fd(const string &name) const { } int BPFModule::table_fd(size_t id) const { - if (id >= tables_->size()) return -1; - return (*tables_)[id].fd; + if (id >= tables_.size()) + return -1; + return tables_[id]->fd; } int BPFModule::table_type(const string &name) const { @@ -531,8 +530,9 @@ int BPFModule::table_type(const string &name) const { } int BPFModule::table_type(size_t id) const { - if (id >= tables_->size()) return -1; - return (*tables_)[id].type; + if (id >= tables_.size()) + return -1; + return tables_[id]->type; } size_t BPFModule::table_max_entries(const string &name) const { @@ -540,8 +540,9 @@ size_t BPFModule::table_max_entries(const string &name) const { } size_t BPFModule::table_max_entries(size_t id) const { - if (id >= tables_->size()) return 0; - return (*tables_)[id].max_entries; + if (id >= tables_.size()) + return 0; + return tables_[id]->max_entries; } int BPFModule::table_flags(const string &name) const { @@ -549,19 +550,22 @@ int BPFModule::table_flags(const string &name) const { } int BPFModule::table_flags(size_t id) const { - if (id >= tables_->size()) return -1; - return (*tables_)[id].flags; + if (id >= tables_.size()) + return -1; + return tables_[id]->flags; } const char * BPFModule::table_name(size_t id) const { - if (id >= tables_->size()) return nullptr; - return (*tables_)[id].name.c_str(); + if (id >= tables_.size()) + return nullptr; + return tables_[id]->name.c_str(); } const char * BPFModule::table_key_desc(size_t id) const { if (b_loader_) return nullptr; - if (id >= tables_->size()) return nullptr; - return (*tables_)[id].key_desc.c_str(); + if (id >= tables_.size()) + return nullptr; + return tables_[id]->key_desc.c_str(); } const char * BPFModule::table_key_desc(const string &name) const { @@ -570,24 +574,27 @@ const char * BPFModule::table_key_desc(const string &name) const { const char * BPFModule::table_leaf_desc(size_t id) const { if (b_loader_) return nullptr; - if (id >= tables_->size()) return nullptr; - return (*tables_)[id].leaf_desc.c_str(); + if (id >= tables_.size()) + return nullptr; + return tables_[id]->leaf_desc.c_str(); } const char * BPFModule::table_leaf_desc(const string &name) const { return table_leaf_desc(table_id(name)); } size_t BPFModule::table_key_size(size_t id) const { - if (id >= tables_->size()) return 0; - return (*tables_)[id].key_size; + if (id >= tables_.size()) + return 0; + return tables_[id]->key_size; } size_t BPFModule::table_key_size(const string &name) const { return table_key_size(table_id(name)); } size_t BPFModule::table_leaf_size(size_t id) const { - if (id >= tables_->size()) return 0; - return (*tables_)[id].leaf_size; + if (id >= tables_.size()) + return 0; + return tables_[id]->leaf_size; } size_t BPFModule::table_leaf_size(const string &name) const { return table_leaf_size(table_id(name)); @@ -603,8 +610,9 @@ struct TableIterator { }; int BPFModule::table_key_printf(size_t id, char *buf, size_t buflen, const void *key) { - if (id >= tables_->size()) return -1; - const TableDesc &desc = (*tables_)[id]; + if (id >= tables_.size()) + return -1; + const TableDesc &desc = *tables_[id]; if (!desc.key_snprintf) { fprintf(stderr, "Key snprintf not available\n"); return -1; @@ -627,8 +635,9 @@ int BPFModule::table_key_printf(size_t id, char *buf, size_t buflen, const void } int BPFModule::table_leaf_printf(size_t id, char *buf, size_t buflen, const void *leaf) { - if (id >= tables_->size()) return -1; - const TableDesc &desc = (*tables_)[id]; + if (id >= tables_.size()) + return -1; + const TableDesc &desc = *tables_[id]; if (!desc.leaf_snprintf) { fprintf(stderr, "Key snprintf not available\n"); return -1; @@ -651,8 +660,9 @@ int BPFModule::table_leaf_printf(size_t id, char *buf, size_t buflen, const void } int BPFModule::table_key_scanf(size_t id, const char *key_str, void *key) { - if (id >= tables_->size()) return -1; - const TableDesc &desc = (*tables_)[id]; + if (id >= tables_.size()) + return -1; + const TableDesc &desc = *tables_[id]; if (!desc.key_sscanf) { fprintf(stderr, "Key sscanf not available\n"); return -1; @@ -672,8 +682,9 @@ int BPFModule::table_key_scanf(size_t id, const char *key_str, void *key) { } int BPFModule::table_leaf_scanf(size_t id, const char *leaf_str, void *leaf) { - if (id >= tables_->size()) return -1; - const TableDesc &desc = (*tables_)[id]; + if (id >= tables_.size()) + return -1; + const TableDesc &desc = *tables_[id]; if (!desc.leaf_sscanf) { fprintf(stderr, "Key sscanf not available\n"); return -1; @@ -714,7 +725,8 @@ int BPFModule::load_b(const string &filename, const string &proto_filename) { return rc; b_loader_.reset(new BLoader(flags_)); - if (int rc = b_loader_->parse(&*mod_, filename, proto_filename, &tables_)) + if (int rc = + b_loader_->parse(&*mod_, filename, proto_filename, *ts_, std::to_string((uintptr_t)this))) return rc; if (int rc = annotate()) return rc; diff --git a/src/cc/bpf_module.h b/src/cc/bpf_module.h index aca8633..266e360 100644 --- a/src/cc/bpf_module.h +++ b/src/cc/bpf_module.h @@ -31,7 +31,8 @@ class Type; } namespace ebpf { -struct TableDesc; +class TableDesc; +class TableStorage; class BLoader; class ClangLoader; @@ -52,7 +53,7 @@ class BPFModule { int kbuild_flags(const char *uname_release, std::vector *cflags); int run_pass_manager(llvm::Module &mod); public: - BPFModule(unsigned flags); + BPFModule(unsigned flags, TableStorage *ts = nullptr); ~BPFModule(); int load_b(const std::string &filename, const std::string &proto_filename); int load_c(const std::string &filename, const char *cflags[], int ncflags); @@ -99,11 +100,13 @@ class BPFModule { std::unique_ptr b_loader_; std::unique_ptr clang_loader_; std::map> sections_; - std::unique_ptr> tables_; + std::vector tables_; std::map table_names_; std::vector function_names_; std::map readers_; std::map writers_; + TableStorage *ts_; + std::unique_ptr local_ts_; }; } // namespace ebpf diff --git a/src/cc/bpffs_table.cc b/src/cc/bpffs_table.cc new file mode 100644 index 0000000..6b49942 --- /dev/null +++ b/src/cc/bpffs_table.cc @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2017 VMware, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" +#include "table_storage_impl.h" + +namespace ebpf { + +using std::string; +using std::unique_ptr; + +/// A filesystem backed table storage +class BpfFsTableStorage : public TableStorageImpl { + public: + class iterator : public TableStorageIteratorImpl { + public: + virtual ~iterator() {} + virtual unique_ptr clone() const override; + virtual self_type &operator++() override; + virtual value_type &operator*() const override; + virtual pointer operator->() const override; + }; + virtual ~BpfFsTableStorage() {} + virtual bool Find(const string &name, TableStorage::iterator &result) const override; + virtual bool Insert(const string &name, TableDesc &&desc) override; + virtual bool Delete(const string &name) override; + virtual unique_ptr begin() override; + virtual unique_ptr end() override; + virtual unique_ptr lower_bound(const string &k) override; + virtual unique_ptr upper_bound(const string &k) override; + virtual unique_ptr erase(const TableStorageIteratorImpl &it) override; + + private: +}; + +bool BpfFsTableStorage::Find(const string &name, TableStorage::iterator &result) const { + return false; +} + +bool BpfFsTableStorage::Insert(const string &name, TableDesc &&desc) { return false; } + +bool BpfFsTableStorage::Delete(const string &name) { return false; } + +unique_ptr BpfFsTableStorage::begin() { return unique_ptr(); } +unique_ptr BpfFsTableStorage::end() { return unique_ptr(); } +unique_ptr BpfFsTableStorage::lower_bound(const string &k) { + return unique_ptr(); +} +unique_ptr BpfFsTableStorage::upper_bound(const string &k) { + return unique_ptr(); +} +unique_ptr BpfFsTableStorage::erase(const TableStorageIteratorImpl &it) { + return unique_ptr(); +} + +unique_ptr createBpfFsTableStorage() { + auto t = make_unique(); + t->Init(make_unique()); + return t; +} + +} // namespace ebpf diff --git a/src/cc/frontends/b/codegen_llvm.cc b/src/cc/frontends/b/codegen_llvm.cc index 62ed389..6a9a9f0 100644 --- a/src/cc/frontends/b/codegen_llvm.cc +++ b/src/cc/frontends/b/codegen_llvm.cc @@ -35,10 +35,10 @@ #include "bcc_exception.h" #include "codegen_llvm.h" #include "lexer.h" -#include "table_desc.h" -#include "type_helper.h" -#include "linux/bpf.h" #include "libbpf.h" +#include "linux/bpf.h" +#include "table_storage.h" +#include "type_helper.h" namespace ebpf { namespace cc { @@ -1220,7 +1220,7 @@ StatusTuple CodegenLLVM::visit_func_decl_stmt_node(FuncDeclStmtNode *n) { return StatusTuple(0); } -StatusTuple CodegenLLVM::visit(Node* root, vector &tables) { +StatusTuple CodegenLLVM::visit(Node *root, TableStorage &ts, const string &id) { scopes_->set_current(scopes_->top_state()); scopes_->set_current(scopes_->top_var()); @@ -1239,16 +1239,12 @@ StatusTuple CodegenLLVM::visit(Node* root, vector &tables) { map_type = BPF_MAP_TYPE_HASH; else if (table.first->type_id()->name_ == "INDEXED") map_type = BPF_MAP_TYPE_ARRAY; - tables.push_back({ - table.first->id_->name_, - table_fds_[table.first], - map_type, - table.first->key_type_->bit_width_ >> 3, - table.first->leaf_type_->bit_width_ >> 3, - table.first->size_, - 0, - "", "", - }); + ts.Insert(Path({id, table.first->id_->name_}), + { + table.first->id_->name_, table_fds_[table.first], map_type, + table.first->key_type_->bit_width_ >> 3, table.first->leaf_type_->bit_width_ >> 3, + table.first->size_, 0, + }); } return StatusTuple(0); } diff --git a/src/cc/frontends/b/codegen_llvm.h b/src/cc/frontends/b/codegen_llvm.h index 7f7cd53..59ae785 100644 --- a/src/cc/frontends/b/codegen_llvm.h +++ b/src/cc/frontends/b/codegen_llvm.h @@ -41,7 +41,8 @@ class GlobalVariable; } namespace ebpf { -struct TableDesc; + +class TableStorage; namespace cc { @@ -63,7 +64,7 @@ class CodegenLLVM : public Visitor { EXPAND_NODES(VISIT) #undef VISIT - STATUS_RETURN visit(Node* n, std::vector &tables); + STATUS_RETURN visit(Node *n, TableStorage &ts, const std::string &id); int get_table_fd(const std::string &name) const; diff --git a/src/cc/frontends/b/loader.cc b/src/cc/frontends/b/loader.cc index f60e8e3..7933224 100644 --- a/src/cc/frontends/b/loader.cc +++ b/src/cc/frontends/b/loader.cc @@ -18,7 +18,6 @@ #include "type_check.h" #include "codegen_llvm.h" #include "loader.h" -#include "table_desc.h" using std::string; using std::unique_ptr; @@ -34,7 +33,7 @@ BLoader::~BLoader() { } int BLoader::parse(llvm::Module *mod, const string &filename, const string &proto_filename, - unique_ptr> *tables) { + TableStorage &ts, const string &id) { int rc; proto_parser_ = make_unique(proto_filename); @@ -61,10 +60,8 @@ int BLoader::parse(llvm::Module *mod, const string &filename, const string &prot return -1; } - *tables = make_unique>(); - codegen_ = ebpf::make_unique(mod, parser_->scopes_.get(), proto_parser_->scopes_.get()); - ret = codegen_->visit(parser_->root_node_, **tables); + ret = codegen_->visit(parser_->root_node_, ts, id); if (ret.code() != 0 || ret.msg().size()) { fprintf(stderr, "Codegen error @line=%d: %s\n", ret.code(), ret.msg().c_str()); return ret.code(); diff --git a/src/cc/frontends/b/loader.h b/src/cc/frontends/b/loader.h index 3e3570d..93ca77d 100644 --- a/src/cc/frontends/b/loader.h +++ b/src/cc/frontends/b/loader.h @@ -20,14 +20,14 @@ #include #include +#include "table_storage.h" + namespace llvm { class Module; } namespace ebpf { -struct TableDesc; - namespace cc { class Parser; class CodegenLLVM; @@ -38,7 +38,8 @@ class BLoader { explicit BLoader(unsigned flags); ~BLoader(); int parse(llvm::Module *mod, const std::string &filename, const std::string &proto_filename, - std::unique_ptr> *tables); + TableStorage &ts, const std::string &id); + private: unsigned flags_; std::unique_ptr parser_; diff --git a/src/cc/frontends/clang/b_frontend_action.cc b/src/cc/frontends/clang/b_frontend_action.cc index f12d2b7..779b995 100644 --- a/src/cc/frontends/clang/b_frontend_action.cc +++ b/src/cc/frontends/clang/b_frontend_action.cc @@ -26,8 +26,8 @@ #include #include "b_frontend_action.h" -#include "shared_table.h" #include "common.h" +#include "table_storage.h" #include "libbpf.h" @@ -57,6 +57,7 @@ const char **calling_conv_regs = calling_conv_regs_x86; #endif using std::map; +using std::move; using std::set; using std::string; using std::to_string; @@ -64,69 +65,6 @@ using std::unique_ptr; using std::vector; using namespace clang; -// Encode the struct layout as a json description -BMapDeclVisitor::BMapDeclVisitor(ASTContext &C, string &result) - : C(C), result_(result) {} -bool BMapDeclVisitor::VisitFieldDecl(FieldDecl *D) { - result_ += "\""; - result_ += D->getName(); - result_ += "\","; - return true; -} - -bool BMapDeclVisitor::TraverseRecordDecl(RecordDecl *D) { - // skip children, handled in Visit... - if (!WalkUpFromRecordDecl(D)) - return false; - return true; -} -bool BMapDeclVisitor::VisitRecordDecl(RecordDecl *D) { - result_ += "[\""; - result_ += D->getName(); - result_ += "\", ["; - for (auto F : D->getDefinition()->fields()) { - if (F->isAnonymousStructOrUnion()) { - if (const RecordType *R = dyn_cast(F->getType())) - TraverseDecl(R->getDecl()); - result_ += ", "; - continue; - } - result_ += "["; - TraverseDecl(F); - if (const ConstantArrayType *T = dyn_cast(F->getType())) - result_ += ", [" + T->getSize().toString(10, false) + "]"; - if (F->isBitField()) - result_ += ", " + to_string(F->getBitWidthValue(C)); - result_ += "], "; - } - if (!D->getDefinition()->field_empty()) - result_.erase(result_.end() - 2); - result_ += "]"; - if (D->isUnion()) - result_ += ", \"union\""; - else if (D->isStruct()) - result_ += ", \"struct\""; - result_ += "]"; - return true; -} -// pointer to anything should be treated as terminal, don't recurse further -bool BMapDeclVisitor::VisitPointerType(const PointerType *T) { - result_ += "\"unsigned long long\""; - return false; -} -bool BMapDeclVisitor::VisitTagType(const TagType *T) { - return TraverseDecl(T->getDecl()->getDefinition()); -} -bool BMapDeclVisitor::VisitTypedefType(const TypedefType *T) { - return TraverseDecl(T->getDecl()); -} -bool BMapDeclVisitor::VisitBuiltinType(const BuiltinType *T) { - result_ += "\""; - result_ += T->getName(C.getPrintingPolicy()); - result_ += "\""; - return true; -} - class ProbeChecker : public RecursiveASTVisitor { public: explicit ProbeChecker(Expr *arg, const set &ptregs) @@ -272,10 +210,8 @@ DiagnosticBuilder ProbeVisitor::error(SourceLocation loc, const char (&fmt)[N]) return C.getDiagnostics().Report(loc, diag_id); } - -BTypeVisitor::BTypeVisitor(ASTContext &C, Rewriter &rewriter, vector &tables) - : C(C), diag_(C.getDiagnostics()), rewriter_(rewriter), out_(llvm::errs()), tables_(tables) { -} +BTypeVisitor::BTypeVisitor(ASTContext &C, BFrontendAction &fe) + : C(C), diag_(C.getDiagnostics()), fe_(fe), rewriter_(fe.rewriter()), out_(llvm::errs()) {} bool BTypeVisitor::VisitFunctionDecl(FunctionDecl *D) { // put each non-static non-inline function decl in its own section, to be @@ -360,14 +296,16 @@ bool BTypeVisitor::VisitCallExpr(CallExpr *Call) { Call->getArg(Call->getNumArgs() - 1)->getLocEnd()))); // find the table fd, which was opened at declaration time - auto table_it = tables_.begin(); - for (; table_it != tables_.end(); ++table_it) - if (table_it->name == Ref->getDecl()->getName()) break; - if (table_it == tables_.end()) { - error(Ref->getLocEnd(), "bpf_table %0 failed to open") << Ref->getDecl()->getName(); - return false; + TableStorage::iterator desc; + Path local_path({fe_.id(), Ref->getDecl()->getName()}); + Path global_path({Ref->getDecl()->getName()}); + if (!fe_.table_storage().Find(local_path, desc)) { + if (!fe_.table_storage().Find(global_path, desc)) { + error(Ref->getLocEnd(), "bpf_table %0 failed to open") << Ref->getDecl()->getName(); + return false; + } } - string fd = to_string(table_it->fd); + string fd = to_string(desc->second.fd); string prefix, suffix; string txt; auto rewrite_start = Call->getLocStart(); @@ -391,7 +329,7 @@ bool BTypeVisitor::VisitCallExpr(CallExpr *Call) { string lookup = "bpf_map_lookup_elem_(bpf_pseudo_fd(1, " + fd + ")"; string update = "bpf_map_update_elem_(bpf_pseudo_fd(1, " + fd + ")"; txt = "({ typeof(" + name + ".key) _key = " + arg0 + "; "; - if (table_it->type == BPF_MAP_TYPE_HASH) { + if (desc->second.type == BPF_MAP_TYPE_HASH) { txt += "typeof(" + name + ".leaf) _zleaf; memset(&_zleaf, 0, sizeof(_zleaf)); "; txt += update + ", &_key, &_zleaf, BPF_NOEXIST); "; } @@ -416,11 +354,12 @@ bool BTypeVisitor::VisitCallExpr(CallExpr *Call) { meta + ", " + meta_len + ");"; } else if (memb_name == "get_stackid") { - if (table_it->type == BPF_MAP_TYPE_STACK_TRACE) { - string arg0 = rewriter_.getRewrittenText(expansionRange(Call->getArg(0)->getSourceRange())); - txt = "bpf_get_stackid("; - txt += "bpf_pseudo_fd(1, " + fd + "), " + arg0; - rewrite_end = Call->getArg(0)->getLocEnd(); + if (desc->second.type == BPF_MAP_TYPE_STACK_TRACE) { + string arg0 = + rewriter_.getRewrittenText(expansionRange(Call->getArg(0)->getSourceRange())); + txt = "bpf_get_stackid("; + txt += "bpf_pseudo_fd(1, " + fd + "), " + arg0; + rewrite_end = Call->getArg(0)->getLocEnd(); } else { error(Call->getLocStart(), "get_stackid only available on stacktrace maps"); return false; @@ -433,7 +372,7 @@ bool BTypeVisitor::VisitCallExpr(CallExpr *Call) { prefix = "bpf_map_update_elem"; suffix = ", BPF_ANY)"; } else if (memb_name == "insert") { - if (table_it->type == BPF_MAP_TYPE_ARRAY) { + if (desc->second.type == BPF_MAP_TYPE_ARRAY) { warning(Call->getLocStart(), "all element of an array already exist; insert() will have no effect"); } prefix = "bpf_map_update_elem"; @@ -661,8 +600,12 @@ bool BTypeVisitor::VisitVarDecl(VarDecl *Decl) { } const RecordDecl *RD = R->getDecl()->getDefinition(); - TableDesc table = {}; + TableDesc table; + TableStorage::iterator table_it; table.name = Decl->getName(); + Path local_path({fe_.id(), table.name}); + Path global_path({table.name}); + QualType key_type, leaf_type; unsigned i = 0; for (auto F : RD->fields()) { @@ -673,16 +616,14 @@ bool BTypeVisitor::VisitVarDecl(VarDecl *Decl) { return false; } table.key_size = sz; - BMapDeclVisitor visitor(C, table.key_desc); - visitor.TraverseType(F->getType()); + key_type = F->getType(); } else if (F->getName() == "leaf") { if (sz == 0) { error(F->getLocStart(), "invalid zero-sized leaf"); return false; } table.leaf_size = sz; - BMapDeclVisitor visitor(C, table.leaf_desc); - visitor.TraverseType(F->getType()); + leaf_type = F->getType(); } else if (F->getName() == "data") { table.max_entries = sz / table.leaf_size; } else if (F->getName() == "flags") { @@ -713,13 +654,11 @@ bool BTypeVisitor::VisitVarDecl(VarDecl *Decl) { } else if (A->getName() == "maps/lpm_trie") { map_type = BPF_MAP_TYPE_LPM_TRIE; } else if (A->getName() == "maps/histogram") { - if (table.key_desc == "\"int\"") + map_type = BPF_MAP_TYPE_HASH; + if (key_type->isSpecificBuiltinType(BuiltinType::Int)) map_type = BPF_MAP_TYPE_ARRAY; - else - map_type = BPF_MAP_TYPE_HASH; - if (table.leaf_desc != "\"unsigned long long\"") { - error(Decl->getLocStart(), "histogram leaf type must be u64, got %0") << table.leaf_desc; - } + if (!leaf_type->isSpecificBuiltinType(BuiltinType::ULongLong)) + error(Decl->getLocStart(), "histogram leaf type must be u64, got %0") << leaf_type; } else if (A->getName() == "maps/prog") { map_type = BPF_MAP_TYPE_PROG_ARRAY; } else if (A->getName() == "maps/perf_output") { @@ -733,24 +672,22 @@ bool BTypeVisitor::VisitVarDecl(VarDecl *Decl) { } else if (A->getName() == "maps/stacktrace") { map_type = BPF_MAP_TYPE_STACK_TRACE; } else if (A->getName() == "maps/extern") { + if (!fe_.table_storage().Find(global_path, table_it)) { + error(Decl->getLocStart(), "reference to undefined table"); + return false; + } + table = table_it->second.dup(); table.is_extern = true; - table.fd = SharedTables::instance()->lookup_fd(table.name); - table.type = SharedTables::instance()->lookup_type(table.name); } else if (A->getName() == "maps/export") { if (table.name.substr(0, 2) == "__") table.name = table.name.substr(2); - auto table_it = tables_.begin(); - for (; table_it != tables_.end(); ++table_it) - if (table_it->name == table.name) break; - if (table_it == tables_.end()) { + Path local_path({fe_.id(), table.name}); + Path global_path({table.name}); + if (!fe_.table_storage().Find(local_path, table_it)) { error(Decl->getLocStart(), "reference to undefined table"); return false; } - if (!SharedTables::instance()->insert_fd(table.name, table_it->fd, table_it->type)) { - error(Decl->getLocStart(), "could not export bpf map %0: %1") << table.name << "already in use"; - return false; - } - table_it->is_shared = true; + fe_.table_storage().Insert(global_path, table_it->second.dup()); return true; } @@ -769,7 +706,8 @@ bool BTypeVisitor::VisitVarDecl(VarDecl *Decl) { return false; } - tables_.push_back(std::move(table)); + fe_.table_storage().VisitMapType(table, C, key_type, leaf_type); + fe_.table_storage().Insert(local_path, move(table)); } else if (const PointerType *P = Decl->getType()->getAs()) { // if var is a pointer to a packet type, clone the annotation into the var // decl so that the packet dext/dins rewriter can catch it @@ -786,9 +724,7 @@ bool BTypeVisitor::VisitVarDecl(VarDecl *Decl) { return true; } -BTypeConsumer::BTypeConsumer(ASTContext &C, Rewriter &rewriter, vector &tables) - : visitor_(C, rewriter, tables) { -} +BTypeConsumer::BTypeConsumer(ASTContext &C, BFrontendAction &fe) : visitor_(C, fe) {} bool BTypeConsumer::HandleTopLevelDecl(DeclGroupRef Group) { for (auto D : Group) @@ -814,9 +750,9 @@ bool ProbeConsumer::HandleTopLevelDecl(DeclGroupRef Group) { return true; } -BFrontendAction::BFrontendAction(llvm::raw_ostream &os, unsigned flags) - : os_(os), flags_(flags), rewriter_(new Rewriter), tables_(new vector) { -} +BFrontendAction::BFrontendAction(llvm::raw_ostream &os, unsigned flags, TableStorage &ts, + const std::string &id) + : os_(os), flags_(flags), ts_(ts), id_(id), rewriter_(new Rewriter) {} void BFrontendAction::EndSourceFileAction() { if (flags_ & DEBUG_PREPROCESSOR) @@ -829,8 +765,8 @@ unique_ptr BFrontendAction::CreateASTConsumer(CompilerInstance &Com rewriter_->setSourceMgr(Compiler.getSourceManager(), Compiler.getLangOpts()); vector> consumers; consumers.push_back(unique_ptr(new ProbeConsumer(Compiler.getASTContext(), *rewriter_))); - consumers.push_back(unique_ptr(new BTypeConsumer(Compiler.getASTContext(), *rewriter_, *tables_))); - return unique_ptr(new MultiplexConsumer(move(consumers))); + consumers.push_back(unique_ptr(new BTypeConsumer(Compiler.getASTContext(), *this))); + return unique_ptr(new MultiplexConsumer(std::move(consumers))); } } diff --git a/src/cc/frontends/clang/b_frontend_action.h b/src/cc/frontends/clang/b_frontend_action.h index b3a729c..bff1eb1 100644 --- a/src/cc/frontends/clang/b_frontend_action.h +++ b/src/cc/frontends/clang/b_frontend_action.h @@ -24,7 +24,7 @@ #include #include -#include "table_desc.h" +#include "table_storage.h" #define DEBUG_PREPROCESSOR 0x4 @@ -41,21 +41,7 @@ class StringRef; namespace ebpf { -// Helper visitor for constructing a string representation of a key/leaf decl -class BMapDeclVisitor : public clang::RecursiveASTVisitor { - public: - explicit BMapDeclVisitor(clang::ASTContext &C, std::string &result); - bool TraverseRecordDecl(clang::RecordDecl *Decl); - bool VisitRecordDecl(clang::RecordDecl *Decl); - bool VisitFieldDecl(clang::FieldDecl *Decl); - bool VisitBuiltinType(const clang::BuiltinType *T); - bool VisitTypedefType(const clang::TypedefType *T); - bool VisitTagType(const clang::TagType *T); - bool VisitPointerType(const clang::PointerType *T); - private: - clang::ASTContext &C; - std::string &result_; -}; +class BFrontendAction; // Type visitor and rewriter for B programs. // It will look for B-specific features and rewrite them into a valid @@ -63,8 +49,7 @@ class BMapDeclVisitor : public clang::RecursiveASTVisitor { // and store the open handles in a map of table-to-fd's. class BTypeVisitor : public clang::RecursiveASTVisitor { public: - explicit BTypeVisitor(clang::ASTContext &C, clang::Rewriter &rewriter, - std::vector &tables); + explicit BTypeVisitor(clang::ASTContext &C, BFrontendAction &fe); bool TraverseCallExpr(clang::CallExpr *Call); bool VisitFunctionDecl(clang::FunctionDecl *D); bool VisitCallExpr(clang::CallExpr *Call); @@ -82,9 +67,9 @@ class BTypeVisitor : public clang::RecursiveASTVisitor { clang::ASTContext &C; clang::DiagnosticsEngine &diag_; + BFrontendAction &fe_; clang::Rewriter &rewriter_; /// modifications to the source go into this class llvm::raw_ostream &out_; /// for debugging - std::vector &tables_; /// store the open FDs std::vector fn_args_; std::set visited_; std::string current_fn_; @@ -115,8 +100,7 @@ class ProbeVisitor : public clang::RecursiveASTVisitor { // A helper class to the frontend action, walks the decls class BTypeConsumer : public clang::ASTConsumer { public: - explicit BTypeConsumer(clang::ASTContext &C, clang::Rewriter &rewriter, - std::vector &tables); + explicit BTypeConsumer(clang::ASTContext &C, BFrontendAction &fe); bool HandleTopLevelDecl(clang::DeclGroupRef Group) override; private: BTypeVisitor visitor_; @@ -138,7 +122,7 @@ class BFrontendAction : public clang::ASTFrontendAction { public: // Initialize with the output stream where the new source file contents // should be written. - BFrontendAction(llvm::raw_ostream &os, unsigned flags); + BFrontendAction(llvm::raw_ostream &os, unsigned flags, TableStorage &ts, const std::string &id); // Called by clang when the AST has been completed, here the output stream // will be flushed. @@ -147,13 +131,16 @@ class BFrontendAction : public clang::ASTFrontendAction { std::unique_ptr CreateASTConsumer(clang::CompilerInstance &Compiler, llvm::StringRef InFile) override; - // take ownership of the table-to-fd mapping data structure - std::unique_ptr> take_tables() { return move(tables_); } + clang::Rewriter &rewriter() const { return *rewriter_; } + TableStorage &table_storage() const { return ts_; } + std::string id() const { return id_; } + private: llvm::raw_ostream &os_; unsigned flags_; + TableStorage &ts_; + std::string id_; std::unique_ptr rewriter_; - std::unique_ptr> tables_; }; } // namespace visitor diff --git a/src/cc/frontends/clang/loader.cc b/src/cc/frontends/clang/loader.cc index 99cd4e2..73a2f52 100644 --- a/src/cc/frontends/clang/loader.cc +++ b/src/cc/frontends/clang/loader.cc @@ -104,8 +104,8 @@ std::pair get_kernel_path_info(const string kdir) } -int ClangLoader::parse(unique_ptr *mod, unique_ptr> *tables, - const string &file, bool in_memory, const char *cflags[], int ncflags) { +int ClangLoader::parse(unique_ptr *mod, TableStorage &ts, const string &file, + bool in_memory, const char *cflags[], int ncflags, const std::string &id) { using namespace clang; string main_path = "/virtual/main.c"; @@ -266,12 +266,10 @@ int ClangLoader::parse(unique_ptr *mod, unique_ptr out_buf1 = llvm::MemoryBuffer::getMemBuffer(out_str1); - // this contains the open FDs - *tables = bact.take_tables(); // second pass, clear input and take rewrite buffer CompilerInstance compiler2; diff --git a/src/cc/frontends/clang/loader.h b/src/cc/frontends/clang/loader.h index 5e7c490..b67ac2a 100644 --- a/src/cc/frontends/clang/loader.h +++ b/src/cc/frontends/clang/loader.h @@ -20,6 +20,8 @@ #include #include +#include "table_storage.h" + namespace llvm { class Module; class LLVMContext; @@ -28,14 +30,13 @@ class MemoryBuffer; namespace ebpf { -struct TableDesc; - class ClangLoader { public: explicit ClangLoader(llvm::LLVMContext *ctx, unsigned flags); ~ClangLoader(); - int parse(std::unique_ptr *mod, std::unique_ptr> *tables, - const std::string &file, bool in_memory, const char *cflags[], int ncflags); + int parse(std::unique_ptr *mod, TableStorage &ts, const std::string &file, + bool in_memory, const char *cflags[], int ncflags, const std::string &id); + private: static std::map> remapped_files_; llvm::LLVMContext *ctx_; diff --git a/src/cc/json_map_decl_visitor.cc b/src/cc/json_map_decl_visitor.cc new file mode 100644 index 0000000..7bb0c7b --- /dev/null +++ b/src/cc/json_map_decl_visitor.cc @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2017 VMware, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include "common.h" +#include "table_desc.h" + +namespace ebpf { + +using std::string; +using std::to_string; +using std::unique_ptr; +using namespace clang; + +// Helper visitor for constructing a string representation of a key/leaf decl +class BMapDeclVisitor : public clang::RecursiveASTVisitor { + public: + explicit BMapDeclVisitor(clang::ASTContext &C, std::string &result); + bool TraverseRecordDecl(clang::RecordDecl *Decl); + bool VisitRecordDecl(clang::RecordDecl *Decl); + bool VisitFieldDecl(clang::FieldDecl *Decl); + bool VisitBuiltinType(const clang::BuiltinType *T); + bool VisitTypedefType(const clang::TypedefType *T); + bool VisitTagType(const clang::TagType *T); + bool VisitPointerType(const clang::PointerType *T); + + private: + clang::ASTContext &C; + std::string &result_; +}; + +// Encode the struct layout as a json description +BMapDeclVisitor::BMapDeclVisitor(ASTContext &C, string &result) : C(C), result_(result) {} +bool BMapDeclVisitor::VisitFieldDecl(FieldDecl *D) { + result_ += "\""; + result_ += D->getName(); + result_ += "\","; + return true; +} + +bool BMapDeclVisitor::TraverseRecordDecl(RecordDecl *D) { + // skip children, handled in Visit... + if (!WalkUpFromRecordDecl(D)) + return false; + return true; +} +bool BMapDeclVisitor::VisitRecordDecl(RecordDecl *D) { + result_ += "[\""; + result_ += D->getName(); + result_ += "\", ["; + for (auto F : D->getDefinition()->fields()) { + if (F->isAnonymousStructOrUnion()) { + if (const RecordType *R = dyn_cast(F->getType())) + TraverseDecl(R->getDecl()); + result_ += ", "; + continue; + } + result_ += "["; + TraverseDecl(F); + if (const ConstantArrayType *T = dyn_cast(F->getType())) + result_ += ", [" + T->getSize().toString(10, false) + "]"; + if (F->isBitField()) + result_ += ", " + to_string(F->getBitWidthValue(C)); + result_ += "], "; + } + if (!D->getDefinition()->field_empty()) + result_.erase(result_.end() - 2); + result_ += "]"; + if (D->isUnion()) + result_ += ", \"union\""; + else if (D->isStruct()) + result_ += ", \"struct\""; + result_ += "]"; + return true; +} +// pointer to anything should be treated as terminal, don't recurse further +bool BMapDeclVisitor::VisitPointerType(const PointerType *T) { + result_ += "\"unsigned long long\""; + return false; +} +bool BMapDeclVisitor::VisitTagType(const TagType *T) { + return TraverseDecl(T->getDecl()->getDefinition()); +} +bool BMapDeclVisitor::VisitTypedefType(const TypedefType *T) { return TraverseDecl(T->getDecl()); } +bool BMapDeclVisitor::VisitBuiltinType(const BuiltinType *T) { + result_ += "\""; + result_ += T->getName(C.getPrintingPolicy()); + result_ += "\""; + return true; +} + +class JsonMapTypesVisitor : public virtual MapTypesVisitor { + public: + virtual void Visit(TableDesc &desc, clang::ASTContext &C, clang::QualType key_type, + clang::QualType leaf_type) { + BMapDeclVisitor v1(C, desc.key_desc), v2(C, desc.leaf_desc); + v1.TraverseType(key_type); + v2.TraverseType(leaf_type); + } +}; + +unique_ptr createJsonMapTypesVisitor() { + return make_unique(); +} + +} // namespace ebpf diff --git a/src/cc/shared_table.cc b/src/cc/shared_table.cc index f389fad..29744a5 100644 --- a/src/cc/shared_table.cc +++ b/src/cc/shared_table.cc @@ -15,51 +15,100 @@ */ #include +#include -#include "shared_table.h" +#include "common.h" #include "compat/linux/bpf.h" +#include "table_storage.h" +#include "table_storage_impl.h" namespace ebpf { using std::string; +using std::unique_ptr; -SharedTables * SharedTables::instance_; +/// A process-wide singleton of shared tables +class SharedTableStorage : public TableStorageImpl { + public: + class iterator : public TableStorageIteratorImpl { + std::map::iterator it_; -SharedTables * SharedTables::instance() { - if (!instance_) { - instance_ = new SharedTables; - } - return instance_; -} + public: + explicit iterator(const std::map::iterator &it) : it_(it) {} + virtual ~iterator() {} + virtual unique_ptr clone() const override { return make_unique(it_); } + virtual self_type &operator++() override { + ++it_; + return *this; + } + virtual value_type &operator*() const override { return *it_; } + virtual pointer operator->() const override { return &*it_; } + }; + virtual ~SharedTableStorage() {} + virtual bool Find(const string &name, TableStorage::iterator &result) const override; + virtual bool Insert(const string &name, TableDesc &&desc) override; + virtual bool Delete(const string &name) override; + virtual unique_ptr begin() override; + virtual unique_ptr end() override; + virtual unique_ptr lower_bound(const string &k) override; + virtual unique_ptr upper_bound(const string &k) override; + virtual unique_ptr erase(const TableStorageIteratorImpl &it) override; -int SharedTables::lookup_fd(const string &name) const { - auto table = tables_.find(name); - if (table == tables_.end()) - return -1; - return table->second.first; -} + private: + static std::map tables_; +}; -int SharedTables::lookup_type(const string &name) const { - auto table = tables_.find(name); - if (table == tables_.end()) - return BPF_MAP_TYPE_UNSPEC; - return table->second.second; +bool SharedTableStorage::Find(const string &name, TableStorage::iterator &result) const { + auto it = tables_.find(name); + if (it == tables_.end()) + return false; + result = TableStorage::iterator(make_unique(it)); + return true; } -bool SharedTables::insert_fd(const string &name, int fd, int type) { - if (tables_.find(name) != tables_.end()) +bool SharedTableStorage::Insert(const string &name, TableDesc &&desc) { + auto it = tables_.find(name); + if (it != tables_.end()) return false; - tables_[name] = std::make_pair(fd, type); + tables_[name] = std::move(desc); return true; } -bool SharedTables::remove_fd(const string &name) { - auto table = tables_.find(name); - if (table == tables_.end()) +bool SharedTableStorage::Delete(const string &name) { + auto it = tables_.find(name); + if (it == tables_.end()) return false; - close(table->second.first); - tables_.erase(table); + tables_.erase(it); return true; } +unique_ptr SharedTableStorage::begin() { + return make_unique(tables_.begin()); +} +unique_ptr SharedTableStorage::end() { + return make_unique(tables_.end()); +} + +unique_ptr SharedTableStorage::lower_bound(const string &k) { + return make_unique(tables_.lower_bound(k)); +} +unique_ptr SharedTableStorage::upper_bound(const string &k) { + return make_unique(tables_.upper_bound(k)); +} +unique_ptr SharedTableStorage::erase(const TableStorageIteratorImpl &it) { + auto i = tables_.find((*it).first); + if (i == tables_.end()) + return unique_ptr(); + return make_unique(tables_.erase(i)); +} + +// All maps for this process are kept in global static storage. +std::map SharedTableStorage::tables_; + +unique_ptr createSharedTableStorage() { + auto t = make_unique(); + t->Init(make_unique()); + t->AddMapTypesVisitor(createJsonMapTypesVisitor()); + return t; +} } diff --git a/src/cc/shared_table.h b/src/cc/shared_table.h index 7b92914..3fca3dc 100644 --- a/src/cc/shared_table.h +++ b/src/cc/shared_table.h @@ -16,27 +16,3 @@ #pragma once -#include -#include - -namespace ebpf { - -struct TableDesc; - -class SharedTables { - public: - static SharedTables * instance(); - // add an fd to the shared table, return true if successfully inserted - bool insert_fd(const std::string &name, int fd, int type); - // lookup an fd in the shared table, or -1 if not found - int lookup_fd(const std::string &name) const; - // lookup on map type in the shared table, or BPF_MAP_TYPE_UNSPEC if not found - int lookup_type(const std::string &name) const; - // close and remove a shared fd. return true if the value was found - bool remove_fd(const std::string &name); - private: - static SharedTables *instance_; - std::map> tables_; -}; - -} diff --git a/src/cc/table_desc.h b/src/cc/table_desc.h index d299f5d..2c65a56 100644 --- a/src/cc/table_desc.h +++ b/src/cc/table_desc.h @@ -16,18 +16,101 @@ #pragma once +#include #include +#include #include namespace llvm { class Function; } +namespace clang { +class ASTContext; +class QualType; +} + namespace ebpf { -struct TableDesc { - std::string name; +class TableDesc; + +/// FileDesc is a helper class for managing open file descriptors. Copy is +/// disallowed (call dup instead), and cleanup happens automatically. +class FileDesc { + friend TableDesc; + + private: + FileDesc &operator=(const FileDesc &that) { + fd = ::dup(that.fd); + return *this; + } + FileDesc(const FileDesc &that) { *this = that; } + + public: + FileDesc(int fd = -1) : fd(fd) {} + FileDesc &operator=(FileDesc &&that) { + fd = that.fd; + that.fd = -1; + return *this; + } + FileDesc(FileDesc &&that) { *this = std::move(that); } + ~FileDesc() { + if (fd >= 0) + ::close(fd); + } + FileDesc dup() const { return FileDesc(*this); } + + operator int() { return fd; } + operator int() const { return fd; } + + private: int fd; +}; + +/// TableDesc uniquely stores all of the runtime state for an active bpf table. +/// The copy constructor/assign operator are disabled since the file handles +/// owned by this table are not implicitly copyable. One should call the dup() +/// method if an explicit new handle is required. We define the move operators +/// so that objects of this class can reside in stl containers. +class TableDesc { + private: + TableDesc(const TableDesc &) = default; + TableDesc &operator=(const TableDesc &) = default; + + public: + TableDesc() + : type(0), + key_size(0), + leaf_size(0), + max_entries(0), + flags(0), + key_sscanf(nullptr), + leaf_sscanf(nullptr), + key_snprintf(nullptr), + leaf_snprintf(nullptr), + is_shared(false), + is_extern(false) {} + TableDesc(const std::string &name, FileDesc &&fd, int type, size_t key_size, + size_t leaf_size, size_t max_entries, int flags) + : name(name), + fd(std::move(fd)), + type(type), + key_size(key_size), + leaf_size(leaf_size), + max_entries(max_entries), + flags(flags), + key_sscanf(nullptr), + leaf_sscanf(nullptr), + key_snprintf(nullptr), + leaf_snprintf(nullptr), + is_shared(false), + is_extern(false) {} + TableDesc(TableDesc &&that) = default; + TableDesc &operator=(TableDesc &&that) = default; + TableDesc dup() const { return TableDesc(*this); } + + std::string name; + FileDesc fd; int type; size_t key_size; // sizes are in bytes size_t leaf_size; @@ -43,4 +126,17 @@ struct TableDesc { bool is_extern; }; +/// MapTypesVisitor gets notified of new bpf tables, and has a chance to parse +/// the key and leaf types for their own usage. Subclass this abstract class and +/// implement the Visit method, then add an instance of this class to the +/// StorageTable instance to be notified of each new key/leaf type. +class MapTypesVisitor { + public: + virtual ~MapTypesVisitor() {} + virtual void Visit(TableDesc &desc, clang::ASTContext &C, clang::QualType key_type, + clang::QualType leaf_type) = 0; +}; + +std::unique_ptr createJsonMapTypesVisitor(); + } // namespace ebpf diff --git a/src/cc/table_storage.cc b/src/cc/table_storage.cc new file mode 100644 index 0000000..2211401 --- /dev/null +++ b/src/cc/table_storage.cc @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2017 VMware, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include "table_storage_impl.h" + +namespace ebpf { + +using std::move; +using std::string; +using std::unique_ptr; + +const string Path::DELIM = "/"; + +TableStorage::TableStorage() {} +TableStorage::~TableStorage() {} +void TableStorage::Init(unique_ptr impl) { impl_ = move(impl); } +bool TableStorage::Find(const Path &path, TableStorage::iterator &result) const { + return impl_->Find(path.to_string(), result); +} +bool TableStorage::Insert(const Path &path, TableDesc &&desc) { + return impl_->Insert(path.to_string(), move(desc)); +} +bool TableStorage::Delete(const Path &path) { return impl_->Delete(path.to_string()); } +size_t TableStorage::DeletePrefix(const Path &path) { + size_t i = 0; + auto it = lower_bound(path); + auto upper = upper_bound(path); + while (it != upper) { + it = impl_->erase(*it.impl_); + ++i; + } + return i; +} + +void TableStorage::AddMapTypesVisitor(unique_ptr visitor) { + visitors_.push_back(move(visitor)); +} +void TableStorage::VisitMapType(TableDesc &desc, clang::ASTContext &C, clang::QualType key_type, + clang::QualType leaf_type) { + for (auto &v : visitors_) + v->Visit(desc, C, key_type, leaf_type); +} + +TableStorage::iterator TableStorage::begin() { return impl_->begin(); } +TableStorage::iterator TableStorage::end() { return impl_->end(); } +TableStorage::iterator TableStorage::lower_bound(const Path &p) { + return impl_->lower_bound(p.to_string()); +} +TableStorage::iterator TableStorage::upper_bound(const Path &p) { + return impl_->upper_bound(p.to_string() + "\x7f"); +} + +/// TableStorage::iterator implementation +TableStorage::iterator::iterator() {} +TableStorage::iterator::iterator(unique_ptr impl) : impl_(move(impl)) {} +TableStorage::iterator::iterator(const iterator &that) : impl_(that.impl_->clone()) {} +TableStorage::iterator::~iterator() {} +TableStorage::iterator::iterator(iterator &&that) { *this = move(that); } +TableStorage::iterator &TableStorage::iterator::operator=(iterator &&that) { + impl_ = move(that.impl_); + return *this; +} + +TableStorage::iterator &TableStorage::iterator::operator++() { + ++*impl_; + return *this; +} +TableStorage::iterator TableStorage::iterator::operator++(int) { + iterator tmp(*this); + operator++(); + return tmp; +} +bool TableStorage::iterator::operator==(const iterator &rhs) const { + // assumes that the underlying pair is stored in only one place + return &**impl_ == &**rhs.impl_; +} +bool TableStorage::iterator::operator!=(const iterator &rhs) const { + return &**impl_ != &**rhs.impl_; +} +TableStorage::iterator::reference TableStorage::iterator::operator*() const { return **impl_; } +TableStorage::iterator::pointer TableStorage::iterator::operator->() const { return &**impl_; } + +} // namespace ebpf diff --git a/src/cc/table_storage.h b/src/cc/table_storage.h new file mode 100644 index 0000000..87aaa33 --- /dev/null +++ b/src/cc/table_storage.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2017 VMware, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "table_desc.h" + +namespace ebpf { + +class TableStorageImpl; +class TableStorageIteratorImpl; + +class Path { + public: + static const std::string DELIM; + Path() = default; + Path(const Path &other) = default; + Path &operator=(const Path &other) = default; + Path(std::initializer_list parts) { + size_t len = parts.size() * DELIM.size(); + for (const auto &s : parts) + len += s.size(); + path_.reserve(len); + for (const auto &s : parts) + path_ += DELIM + s; + } + const std::string &to_string() const { return path_; } + + private: + std::string path_; +}; + +class TableStorage { + public: + /// iterator is an abstract class for traversing the map entries in a table + /// storage object. + class iterator { + private: + friend class TableStorage; + iterator(const iterator &); + + public: + typedef std::pair value_type; + typedef std::ptrdiff_t difference_type; + typedef value_type *pointer; + typedef value_type &reference; + typedef std::forward_iterator_tag iterator_category; + typedef iterator self_type; + + iterator(); + iterator(std::unique_ptr); + ~iterator(); + iterator(iterator &&); + iterator &operator=(iterator &&); + self_type &operator++(); + self_type operator++(int); + bool operator==(const self_type &) const; + bool operator!=(const self_type &) const; + value_type &operator*() const; + pointer operator->() const; + + private: + std::unique_ptr impl_; + }; + + TableStorage(); + ~TableStorage(); + void Init(std::unique_ptr); + + bool Find(const Path &path, TableStorage::iterator &result) const; + bool Insert(const Path &path, TableDesc &&desc); + bool Delete(const Path &path); + size_t DeletePrefix(const Path &path); + + void AddMapTypesVisitor(std::unique_ptr); + void VisitMapType(TableDesc &desc, clang::ASTContext &C, clang::QualType key_type, + clang::QualType leaf_type); + iterator begin(); + iterator end(); + iterator lower_bound(const Path &p); + iterator upper_bound(const Path &p); + + private: + std::unique_ptr impl_; + std::vector> visitors_; +}; + +std::unique_ptr createSharedTableStorage(); +std::unique_ptr createBpfFsTableStorage(); +} diff --git a/src/cc/table_storage_impl.h b/src/cc/table_storage_impl.h new file mode 100644 index 0000000..401b404 --- /dev/null +++ b/src/cc/table_storage_impl.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2017 VMware, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "table_storage.h" + +namespace ebpf { + +class TableStorageIteratorImpl { + public: + typedef std::pair value_type; + typedef value_type *pointer; + typedef value_type &reference; + typedef TableStorageIteratorImpl self_type; + virtual ~TableStorageIteratorImpl() {} + virtual std::unique_ptr clone() const = 0; + virtual self_type &operator++() = 0; + virtual value_type &operator*() const = 0; + virtual pointer operator->() const = 0; + + private: +}; + +class TableStorageImpl { + public: + virtual ~TableStorageImpl(){}; + virtual bool Find(const std::string &name, TableStorage::iterator &result) const = 0; + virtual bool Insert(const std::string &name, TableDesc &&desc) = 0; + virtual bool Delete(const std::string &name) = 0; + virtual std::unique_ptr begin() = 0; + virtual std::unique_ptr end() = 0; + virtual std::unique_ptr lower_bound(const std::string &k) = 0; + virtual std::unique_ptr upper_bound(const std::string &k) = 0; + virtual std::unique_ptr erase(const TableStorageIteratorImpl &it) = 0; +}; + +} // namespace ebpf -- 2.7.4