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 <bblanco@gmail.com>
diff --git a/.clang-format b/.clang-format
index d5a3996..2a27948 100644
--- a/.clang-format
+++ b/.clang-format
@@ -3,4 +3,4 @@
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 @@
};
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<std::string> cflags = {},
std::vector<USDT> usdt = {});
@@ -90,6 +93,11 @@
template <class KeyType, class ValueType>
BPFHashTable<KeyType, ValueType> get_hash_table(const std::string& name) {
+ if (ts_) {
+ TableStorage::iterator it;
+ if (ts_->Find(Path({name}), it))
+ return BPFHashTable<KeyType, ValueType>(it->second);
+ }
return BPFHashTable<KeyType, ValueType>(bpf_module_.get(), name);
}
@@ -152,6 +160,7 @@
uint64_t symbol_addr, bcc_symbol* output);
std::unique_ptr<BPFModule> bpf_module_;
+ TableStorage* ts_;
std::map<std::string, int> 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 @@
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<void*>(key),
@@ -70,7 +75,8 @@
template <class KeyType, class ValueType>
class BPFHashTable : protected BPFTableBase<KeyType, ValueType> {
-public:
+ public:
+ explicit BPFHashTable(const TableDesc& desc) : BPFTableBase<KeyType, ValueType>(desc) {}
BPFHashTable(BPFModule* bpf_module, const std::string& name)
: BPFTableBase<KeyType, ValueType>(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 @@
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 @@
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 @@
map<string, tuple<uint8_t *, uintptr_t>> *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 @@
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<Value *> args) {
@@ -326,7 +322,8 @@
// 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<ClangLoader>(&*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 @@
// build an ExecutionEngine.
int BPFModule::load_includes(const string &text) {
clang_loader_ = make_unique<ClangLoader>(&*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 @@
auto m = make_unique<Module>("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 @@
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(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(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(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(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_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 @@
};
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_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_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_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 @@
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 @@
}
namespace ebpf {
-struct TableDesc;
+class TableDesc;
+class TableStorage;
class BLoader;
class ClangLoader;
@@ -52,7 +53,7 @@
int kbuild_flags(const char *uname_release, std::vector<std::string> *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 @@
std::unique_ptr<BLoader> b_loader_;
std::unique_ptr<ClangLoader> clang_loader_;
std::map<std::string, std::tuple<uint8_t *, uintptr_t>> sections_;
- std::unique_ptr<std::vector<TableDesc>> tables_;
+ std::vector<TableDesc *> tables_;
std::map<std::string, size_t> table_names_;
std::vector<std::string> function_names_;
std::map<llvm::Type *, llvm::Function *> readers_;
std::map<llvm::Type *, llvm::Function *> writers_;
+ TableStorage *ts_;
+ std::unique_ptr<TableStorage> 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<self_type> 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<TableStorageIteratorImpl> begin() override;
+ virtual unique_ptr<TableStorageIteratorImpl> end() override;
+ virtual unique_ptr<TableStorageIteratorImpl> lower_bound(const string &k) override;
+ virtual unique_ptr<TableStorageIteratorImpl> upper_bound(const string &k) override;
+ virtual unique_ptr<TableStorageIteratorImpl> 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<TableStorageIteratorImpl> BpfFsTableStorage::begin() { return unique_ptr<iterator>(); }
+unique_ptr<TableStorageIteratorImpl> BpfFsTableStorage::end() { return unique_ptr<iterator>(); }
+unique_ptr<TableStorageIteratorImpl> BpfFsTableStorage::lower_bound(const string &k) {
+ return unique_ptr<iterator>();
+}
+unique_ptr<TableStorageIteratorImpl> BpfFsTableStorage::upper_bound(const string &k) {
+ return unique_ptr<iterator>();
+}
+unique_ptr<TableStorageIteratorImpl> BpfFsTableStorage::erase(const TableStorageIteratorImpl &it) {
+ return unique_ptr<iterator>();
+}
+
+unique_ptr<TableStorage> createBpfFsTableStorage() {
+ auto t = make_unique<TableStorage>();
+ t->Init(make_unique<BpfFsTableStorage>());
+ 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 @@
return StatusTuple(0);
}
-StatusTuple CodegenLLVM::visit(Node* root, vector<TableDesc> &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 @@
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 @@
}
namespace ebpf {
-struct TableDesc;
+
+class TableStorage;
namespace cc {
@@ -63,7 +64,7 @@
EXPAND_NODES(VISIT)
#undef VISIT
- STATUS_RETURN visit(Node* n, std::vector<TableDesc> &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 @@
}
int BLoader::parse(llvm::Module *mod, const string &filename, const string &proto_filename,
- unique_ptr<vector<TableDesc>> *tables) {
+ TableStorage &ts, const string &id) {
int rc;
proto_parser_ = make_unique<ebpf::cc::Parser>(proto_filename);
@@ -61,10 +60,8 @@
return -1;
}
- *tables = make_unique<vector<TableDesc>>();
-
codegen_ = ebpf::make_unique<ebpf::cc::CodegenLLVM>(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 <memory>
#include <string>
+#include "table_storage.h"
+
namespace llvm {
class Module;
}
namespace ebpf {
-struct TableDesc;
-
namespace cc {
class Parser;
class CodegenLLVM;
@@ -38,7 +38,8 @@
explicit BLoader(unsigned flags);
~BLoader();
int parse(llvm::Module *mod, const std::string &filename, const std::string &proto_filename,
- std::unique_ptr<std::vector<TableDesc>> *tables);
+ TableStorage &ts, const std::string &id);
+
private:
unsigned flags_;
std::unique_ptr<cc::Parser> 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 <clang/Rewrite/Core/Rewriter.h>
#include "b_frontend_action.h"
-#include "shared_table.h"
#include "common.h"
+#include "table_storage.h"
#include "libbpf.h"
@@ -57,6 +57,7 @@
#endif
using std::map;
+using std::move;
using std::set;
using std::string;
using std::to_string;
@@ -64,69 +65,6 @@
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<RecordType>(F->getType()))
- TraverseDecl(R->getDecl());
- result_ += ", ";
- continue;
- }
- result_ += "[";
- TraverseDecl(F);
- if (const ConstantArrayType *T = dyn_cast<ConstantArrayType>(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<ProbeChecker> {
public:
explicit ProbeChecker(Expr *arg, const set<Decl *> &ptregs)
@@ -272,10 +210,8 @@
return C.getDiagnostics().Report(loc, diag_id);
}
-
-BTypeVisitor::BTypeVisitor(ASTContext &C, Rewriter &rewriter, vector<TableDesc> &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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
} 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 @@
} else if (A->getName() == "maps/stacktrace") {
map_type = BPF_MAP_TYPE_STACK_TRACE;
} else if (A->getName() == "maps/extern") {
- 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()) {
+ if (!fe_.table_storage().Find(global_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";
+ table = table_it->second.dup();
+ table.is_extern = true;
+ } else if (A->getName() == "maps/export") {
+ if (table.name.substr(0, 2) == "__")
+ table.name = table.name.substr(2);
+ 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;
}
- table_it->is_shared = true;
+ fe_.table_storage().Insert(global_path, table_it->second.dup());
return true;
}
@@ -769,7 +706,8 @@
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<PointerType>()) {
// 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 @@
return true;
}
-BTypeConsumer::BTypeConsumer(ASTContext &C, Rewriter &rewriter, vector<TableDesc> &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 @@
return true;
}
-BFrontendAction::BFrontendAction(llvm::raw_ostream &os, unsigned flags)
- : os_(os), flags_(flags), rewriter_(new Rewriter), tables_(new vector<TableDesc>) {
-}
+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 @@
rewriter_->setSourceMgr(Compiler.getSourceManager(), Compiler.getLangOpts());
vector<unique_ptr<ASTConsumer>> consumers;
consumers.push_back(unique_ptr<ASTConsumer>(new ProbeConsumer(Compiler.getASTContext(), *rewriter_)));
- consumers.push_back(unique_ptr<ASTConsumer>(new BTypeConsumer(Compiler.getASTContext(), *rewriter_, *tables_)));
- return unique_ptr<ASTConsumer>(new MultiplexConsumer(move(consumers)));
+ consumers.push_back(unique_ptr<ASTConsumer>(new BTypeConsumer(Compiler.getASTContext(), *this)));
+ return unique_ptr<ASTConsumer>(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 <clang/Frontend/FrontendAction.h>
#include <clang/Rewrite/Core/Rewriter.h>
-#include "table_desc.h"
+#include "table_storage.h"
#define DEBUG_PREPROCESSOR 0x4
@@ -41,21 +41,7 @@
namespace ebpf {
-// Helper visitor for constructing a string representation of a key/leaf decl
-class BMapDeclVisitor : public clang::RecursiveASTVisitor<BMapDeclVisitor> {
- 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 @@
// and store the open handles in a map of table-to-fd's.
class BTypeVisitor : public clang::RecursiveASTVisitor<BTypeVisitor> {
public:
- explicit BTypeVisitor(clang::ASTContext &C, clang::Rewriter &rewriter,
- std::vector<TableDesc> &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 @@
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<TableDesc> &tables_; /// store the open FDs
std::vector<clang::ParmVarDecl *> fn_args_;
std::set<clang::Expr *> visited_;
std::string current_fn_;
@@ -115,8 +100,7 @@
// 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<TableDesc> &tables);
+ explicit BTypeConsumer(clang::ASTContext &C, BFrontendAction &fe);
bool HandleTopLevelDecl(clang::DeclGroupRef Group) override;
private:
BTypeVisitor visitor_;
@@ -138,7 +122,7 @@
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 @@
std::unique_ptr<clang::ASTConsumer>
CreateASTConsumer(clang::CompilerInstance &Compiler, llvm::StringRef InFile) override;
- // take ownership of the table-to-fd mapping data structure
- std::unique_ptr<std::vector<TableDesc>> 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<clang::Rewriter> rewriter_;
- std::unique_ptr<std::vector<TableDesc>> 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 @@
}
-int ClangLoader::parse(unique_ptr<llvm::Module> *mod, unique_ptr<vector<TableDesc>> *tables,
- const string &file, bool in_memory, const char *cflags[], int ncflags) {
+int ClangLoader::parse(unique_ptr<llvm::Module> *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 @@
// capture the rewritten c file
string out_str1;
llvm::raw_string_ostream os1(out_str1);
- BFrontendAction bact(os1, flags_);
+ BFrontendAction bact(os1, flags_, ts, id);
if (!compiler1.ExecuteAction(bact))
return -1;
unique_ptr<llvm::MemoryBuffer> 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 <memory>
#include <string>
+#include "table_storage.h"
+
namespace llvm {
class Module;
class LLVMContext;
@@ -28,14 +30,13 @@
namespace ebpf {
-struct TableDesc;
-
class ClangLoader {
public:
explicit ClangLoader(llvm::LLVMContext *ctx, unsigned flags);
~ClangLoader();
- int parse(std::unique_ptr<llvm::Module> *mod, std::unique_ptr<std::vector<TableDesc>> *tables,
- const std::string &file, bool in_memory, const char *cflags[], int ncflags);
+ int parse(std::unique_ptr<llvm::Module> *mod, TableStorage &ts, const std::string &file,
+ bool in_memory, const char *cflags[], int ncflags, const std::string &id);
+
private:
static std::map<std::string, std::unique_ptr<llvm::MemoryBuffer>> 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 <memory>
+#include <string>
+
+#include <clang/AST/ASTContext.h>
+#include <clang/AST/RecursiveASTVisitor.h>
+#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<BMapDeclVisitor> {
+ 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<RecordType>(F->getType()))
+ TraverseDecl(R->getDecl());
+ result_ += ", ";
+ continue;
+ }
+ result_ += "[";
+ TraverseDecl(F);
+ if (const ConstantArrayType *T = dyn_cast<ConstantArrayType>(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<MapTypesVisitor> createJsonMapTypesVisitor() {
+ return make_unique<JsonMapTypesVisitor>();
+}
+
+} // 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 <unistd.h>
+#include <iostream>
-#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<string, TableDesc>::iterator it_;
-SharedTables * SharedTables::instance() {
- if (!instance_) {
- instance_ = new SharedTables;
- }
- return instance_;
-}
+ public:
+ explicit iterator(const std::map<string, TableDesc>::iterator &it) : it_(it) {}
+ virtual ~iterator() {}
+ virtual unique_ptr<self_type> clone() const override { return make_unique<iterator>(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<TableStorageIteratorImpl> begin() override;
+ virtual unique_ptr<TableStorageIteratorImpl> end() override;
+ virtual unique_ptr<TableStorageIteratorImpl> lower_bound(const string &k) override;
+ virtual unique_ptr<TableStorageIteratorImpl> upper_bound(const string &k) override;
+ virtual unique_ptr<TableStorageIteratorImpl> 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<string, TableDesc> 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 SharedTables::insert_fd(const string &name, int fd, int type) {
- if (tables_.find(name) != tables_.end())
+bool SharedTableStorage::Find(const string &name, TableStorage::iterator &result) const {
+ auto it = tables_.find(name);
+ if (it == tables_.end())
return false;
- tables_[name] = std::make_pair(fd, type);
+ result = TableStorage::iterator(make_unique<iterator>(it));
return true;
}
-bool SharedTables::remove_fd(const string &name) {
- auto table = tables_.find(name);
- if (table == tables_.end())
+bool SharedTableStorage::Insert(const string &name, TableDesc &&desc) {
+ auto it = tables_.find(name);
+ if (it != tables_.end())
return false;
- close(table->second.first);
- tables_.erase(table);
+ tables_[name] = std::move(desc);
return true;
}
+bool SharedTableStorage::Delete(const string &name) {
+ auto it = tables_.find(name);
+ if (it == tables_.end())
+ return false;
+ tables_.erase(it);
+ return true;
+}
+
+unique_ptr<TableStorageIteratorImpl> SharedTableStorage::begin() {
+ return make_unique<iterator>(tables_.begin());
+}
+unique_ptr<TableStorageIteratorImpl> SharedTableStorage::end() {
+ return make_unique<iterator>(tables_.end());
+}
+
+unique_ptr<TableStorageIteratorImpl> SharedTableStorage::lower_bound(const string &k) {
+ return make_unique<iterator>(tables_.lower_bound(k));
+}
+unique_ptr<TableStorageIteratorImpl> SharedTableStorage::upper_bound(const string &k) {
+ return make_unique<iterator>(tables_.upper_bound(k));
+}
+unique_ptr<TableStorageIteratorImpl> SharedTableStorage::erase(const TableStorageIteratorImpl &it) {
+ auto i = tables_.find((*it).first);
+ if (i == tables_.end())
+ return unique_ptr<iterator>();
+ return make_unique<iterator>(tables_.erase(i));
+}
+
+// All maps for this process are kept in global static storage.
+std::map<string, TableDesc> SharedTableStorage::tables_;
+
+unique_ptr<TableStorage> createSharedTableStorage() {
+ auto t = make_unique<TableStorage>();
+ t->Init(make_unique<SharedTableStorage>());
+ 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 <map>
-#include <string>
-
-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<std::string, std::pair<int, int>> 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 <unistd.h>
#include <cstdint>
+#include <memory>
#include <string>
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 @@
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<MapTypesVisitor> 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 <unistd.h>
+
+#include <clang/AST/Type.h>
+
+#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<TableStorageImpl> 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<MapTypesVisitor> 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<TableStorageIteratorImpl> 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 <cstddef>
+#include <iterator>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#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<std::string> 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<const std::string, TableDesc> 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<TableStorageIteratorImpl>);
+ ~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<TableStorageIteratorImpl> impl_;
+ };
+
+ TableStorage();
+ ~TableStorage();
+ void Init(std::unique_ptr<TableStorageImpl>);
+
+ 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<MapTypesVisitor>);
+ 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<TableStorageImpl> impl_;
+ std::vector<std::unique_ptr<MapTypesVisitor>> visitors_;
+};
+
+std::unique_ptr<TableStorage> createSharedTableStorage();
+std::unique_ptr<TableStorage> 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<const std::string, TableDesc> value_type;
+ typedef value_type *pointer;
+ typedef value_type &reference;
+ typedef TableStorageIteratorImpl self_type;
+ virtual ~TableStorageIteratorImpl() {}
+ virtual std::unique_ptr<self_type> 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<TableStorageIteratorImpl> begin() = 0;
+ virtual std::unique_ptr<TableStorageIteratorImpl> end() = 0;
+ virtual std::unique_ptr<TableStorageIteratorImpl> lower_bound(const std::string &k) = 0;
+ virtual std::unique_ptr<TableStorageIteratorImpl> upper_bound(const std::string &k) = 0;
+ virtual std::unique_ptr<TableStorageIteratorImpl> erase(const TableStorageIteratorImpl &it) = 0;
+};
+
+} // namespace ebpf