Skip to content

Commit

Permalink
add btf support for maps (#2192)
Browse files Browse the repository at this point in the history
Added bpf support for maps so map key/value types can be
retrieved by user space introspection tool to pretty
print map key/values.

To associate maps with its key/value types, the below macro
  BPF_ANNOTATE_KV_PAIR(name, type_key, type_val)
is used, similar to usage in kernel tools/testing/selftests/bpf
and tools/lib/bpf. Currently, some map types (e.g, PERF_EVENT_ARRAY)
do not support pretty print. But common [per_cpu] hash/array maps
are supported.

Currently, bcc create maps before llvm compilation.
To support pretty printing of maps, map creation
needs have key/value type id's which can only be
obtained after compilation. Therefore, during rewriting,
fake map fd is used. After compilation, btf is first
loaded and maps are created.

With latest bpf-next and latest trunk llvm,
bpftool is able to pretty print bcc tool map key/values
as below:
  ; running tcptop.py in one window
  $ tcptop.py
  ; running bpftool in another window
  $ bpftool m s
  ...
  343: hash  name ipv4_send_bytes  flags 0x0
          key 16B  value 8B  max_entries 10240  memlock 1003520B
  344: hash  name ipv4_recv_bytes  flags 0x0
          key 16B  value 8B  max_entries 10240  memlock 1003520B
  345: hash  name ipv6_send_bytes  flags 0x0
          key 64B  value 8B  max_entries 10240  memlock 1495040B
  346: hash  name ipv6_recv_bytes  flags 0x0
          key 64B  value 8B  max_entries 10240  memlock 1495040B
  $ bpftool m d id 345
  ...
    },{
        "key": {
            "pid": 5511,
            "saddr": 0x100007fffff00000000000000000000,
            "daddr": 0x100007fffff00000000000000000000,
            "lport": 2378,
            "dport": 52602
        },
        "value": 49
    },{
        "key": {
            "pid": 2823,
            "saddr": 0x4e000000cefa7bb0300000db0124,
            "daddr": 0x60000000cefa7bb0300000db0124,
            "lport": 2406,
            "dport": 49348
        },
        "value": 36
    }
   ...

Signed-off-by: Yonghong Song <yhs@fb.com>
  • Loading branch information
yonghong-song committed Feb 11, 2019
1 parent b374be8 commit 8300c7b
Show file tree
Hide file tree
Showing 13 changed files with 190 additions and 27 deletions.
8 changes: 8 additions & 0 deletions src/cc/bcc_btf.cc
Expand Up @@ -199,4 +199,12 @@ int BTF::get_btf_info(const char *fname,
return 0;
}

int BTF::get_map_tids(std::string map_name,
unsigned expected_ksize, unsigned expected_vsize,
unsigned *key_tid, unsigned *value_tid) {
return btf__get_map_kv_tids(btf_, map_name.c_str(),
expected_ksize, expected_vsize,
key_tid, value_tid);
}

} // namespace ebpf
3 changes: 3 additions & 0 deletions src/cc/bcc_btf.h
Expand Up @@ -53,6 +53,9 @@ class BTF {
unsigned *finfo_rec_size,
void **line_info, unsigned *line_info_cnt,
unsigned *linfo_rec_size);
int get_map_tids(std::string map_name,
unsigned expected_ksize, unsigned expected_vsize,
unsigned *key_tid, unsigned *value_tid);

private:
void adjust(uint8_t *btf_sec, uintptr_t btf_sec_size,
Expand Down
115 changes: 111 additions & 4 deletions src/cc/bpf_module.cc
Expand Up @@ -71,8 +71,9 @@ class MyMemoryManager : public SectionMemoryManager {
uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment,
unsigned SectionID,
StringRef SectionName) override {
uint8_t *Addr = SectionMemoryManager::allocateCodeSection(Size, Alignment, SectionID, SectionName);
//printf("allocateCodeSection: %s Addr %p Size %ld Alignment %d SectionID %d\n",
// The programs need to change from fake fd to real map fd, so not allocate ReadOnly regions.
uint8_t *Addr = SectionMemoryManager::allocateDataSection(Size, Alignment, SectionID, SectionName, false);
//printf("allocateDataSection: %s Addr %p Size %ld Alignment %d SectionID %d\n",
// SectionName.str().c_str(), (void *)Addr, Size, Alignment, SectionID);
(*sections_)[SectionName.str()] = make_tuple(Addr, Size);
return Addr;
Expand Down Expand Up @@ -157,7 +158,7 @@ int BPFModule::free_bcc_memory() {
int BPFModule::load_cfile(const string &file, bool in_memory, const char *cflags[], int ncflags) {
ClangLoader clang_loader(&*ctx_, flags_);
if (clang_loader.parse(&mod_, *ts_, file, in_memory, cflags, ncflags, id_,
*func_src_, mod_src_, maps_ns_))
*func_src_, mod_src_, maps_ns_, fake_fd_map_))
return -1;
return 0;
}
Expand All @@ -170,7 +171,7 @@ int BPFModule::load_cfile(const string &file, bool in_memory, const char *cflags
int BPFModule::load_includes(const string &text) {
ClangLoader clang_loader(&*ctx_, flags_);
if (clang_loader.parse(&mod_, *ts_, text, true, nullptr, 0, "", *func_src_,
mod_src_, ""))
mod_src_, "", fake_fd_map_))
return -1;
return 0;
}
Expand Down Expand Up @@ -266,6 +267,110 @@ void BPFModule::load_btf(std::map<std::string, std::tuple<uint8_t *, uintptr_t>>
btf_ = btf;
}

int BPFModule::load_maps(std::map<std::string, std::tuple<uint8_t *, uintptr_t>> &sections) {
// find .maps.<table_name> sections and retrieve all map key/value type id's
std::map<std::string, std::pair<int, int>> map_tids;
if (btf_) {
for (auto section : sections) {
auto sec_name = section.first;
if (strncmp(".maps.", sec_name.c_str(), 6) == 0) {
std::string map_name = sec_name.substr(6);
unsigned key_tid = 0, value_tid = 0;
unsigned expected_ksize = 0, expected_vsize = 0;

for (auto map : fake_fd_map_) {
std::string name;

name = get<1>(map.second);
if (map_name == name) {
expected_ksize = get<2>(map.second);
expected_vsize = get<3>(map.second);
break;
}
}

int ret = btf_->get_map_tids(map_name, expected_ksize,
expected_vsize, &key_tid, &value_tid);
if (ret)
continue;

map_tids[map_name] = std::make_pair(key_tid, value_tid);
}
}
}

// create maps
std::map<int, int> map_fds;
for (auto map : fake_fd_map_) {
int fd, fake_fd, map_type, key_size, value_size, max_entries, map_flags;
const char *map_name;

fake_fd = map.first;
map_type = get<0>(map.second);
map_name = get<1>(map.second).c_str();
key_size = get<2>(map.second);
value_size = get<3>(map.second);
max_entries = get<4>(map.second);
map_flags = get<5>(map.second);

struct bpf_create_map_attr attr = {};
attr.map_type = (enum bpf_map_type)map_type;
attr.name = map_name;
attr.key_size = key_size;
attr.value_size = value_size;
attr.max_entries = max_entries;
attr.map_flags = map_flags;

if (map_tids.find(map_name) != map_tids.end()) {
attr.btf_fd = btf_->get_fd();
attr.btf_key_type_id = map_tids[map_name].first;
attr.btf_value_type_id = map_tids[map_name].second;
}

fd = bcc_create_map_xattr(&attr);
if (fd < 0) {
fprintf(stderr, "could not open bpf map: %s\nis map type enabled in your kernel?\n",
map_name);
return -1;
}

map_fds[fake_fd] = fd;
}

// update map table fd's
for (auto it = ts_->begin(), up = ts_->end(); it != up; ++it) {
TableDesc &table = it->second;
if (map_fds.find(table.fake_fd) != map_fds.end()) {
table.fd = map_fds[table.fake_fd];
table.fake_fd = 0;
}
}

// update instructions
for (auto section : sections) {
auto sec_name = section.first;
if (strncmp(".bpf.fn.", sec_name.c_str(), 8) == 0) {
uint8_t *addr = get<0>(section.second);
uintptr_t size = get<1>(section.second);
struct bpf_insn *insns = (struct bpf_insn *)addr;
int i, num_insns;

num_insns = size/sizeof(struct bpf_insn);
for (i = 0; i < num_insns; i++) {
if (insns[i].code == (BPF_LD | BPF_DW | BPF_IMM)) {
// change map_fd is it is a ld_pseudo */
if (insns[i].src_reg == BPF_PSEUDO_MAP_FD &&
map_fds.find(insns[i].imm) != map_fds.end())
insns[i].imm = map_fds[insns[i].imm];
i++;
}
}
}
}

return 0;
}

int BPFModule::finalize() {
Module *mod = &*mod_;
std::map<std::string, std::tuple<uint8_t *, uintptr_t>> tmp_sections,
Expand Down Expand Up @@ -310,6 +415,8 @@ int BPFModule::finalize() {
}

load_btf(*sections_p);
if (load_maps(*sections_p))
return -1;

if (!rw_engine_enabled_) {
// Setup sections_ correctly and then free llvm internal memory
Expand Down
3 changes: 3 additions & 0 deletions src/cc/bpf_module.h
Expand Up @@ -23,6 +23,7 @@
#include <vector>

#include "bcc_exception.h"
#include "table_storage.h"

namespace llvm {
class ExecutionEngine;
Expand Down Expand Up @@ -80,6 +81,7 @@ class BPFModule {
StatusTuple snprintf(std::string fn_name, char *str, size_t sz,
const void *val);
void load_btf(std::map<std::string, std::tuple<uint8_t *, uintptr_t>> &sections);
int load_maps(std::map<std::string, std::tuple<uint8_t *, uintptr_t>> &sections);

public:
BPFModule(unsigned flags, TableStorage *ts = nullptr, bool rw_engine_enabled = true,
Expand Down Expand Up @@ -156,6 +158,7 @@ class BPFModule {
TableStorage *ts_;
std::unique_ptr<TableStorage> local_ts_;
BTF *btf_;
fake_fd_map_def fake_fd_map_;
};

} // namespace ebpf
13 changes: 12 additions & 1 deletion src/cc/export/helpers.h
Expand Up @@ -53,6 +53,16 @@ R"********(
*/
#define SEC(NAME) __attribute__((section(NAME), used))

// Associate map with its key/value types
#define BPF_ANNOTATE_KV_PAIR(name, type_key, type_val) \
struct ____btf_map_##name { \
type_key key; \
type_val value; \
}; \
struct ____btf_map_##name \
__attribute__ ((section(".maps." #name), used)) \
____btf_map_##name = { }

// Changes to the macro require changes in BFrontendAction classes
#define BPF_F_TABLE(_table_type, _key_type, _leaf_type, _name, _max_entries, _flags) \
struct _name##_table_t { \
Expand All @@ -70,7 +80,8 @@ struct _name##_table_t { \
int flags; \
}; \
__attribute__((section("maps/" _table_type))) \
struct _name##_table_t _name = { .flags = (_flags), .max_entries = (_max_entries) }
struct _name##_table_t _name = { .flags = (_flags), .max_entries = (_max_entries) }; \
BPF_ANNOTATE_KV_PAIR(_name, _key_type, _leaf_type)

#define BPF_TABLE(_table_type, _key_type, _leaf_type, _name, _max_entries) \
BPF_F_TABLE(_table_type, _key_type, _leaf_type, _name, _max_entries, 0)
Expand Down
8 changes: 6 additions & 2 deletions src/cc/file_desc.h
Expand Up @@ -49,8 +49,12 @@ class FileDesc {
FileDesc &operator=(const FileDesc &that) = delete;

FileDesc dup() const {
int dup_fd = ::dup(fd_);
return FileDesc(dup_fd);
if (fd_ >= 0) {
int dup_fd = ::dup(fd_);
return FileDesc(dup_fd);
} else {
return FileDesc(-1);
}
}

operator int() { return fd_; }
Expand Down
21 changes: 10 additions & 11 deletions src/cc/frontends/clang/b_frontend_action.cc
Expand Up @@ -766,7 +766,7 @@ bool BTypeVisitor::VisitCallExpr(CallExpr *Call) {
return false;
}
}
string fd = to_string(desc->second.fd);
string fd = to_string(desc->second.fd >= 0 ? desc->second.fd : desc->second.fake_fd);
string prefix, suffix;
string txt;
auto rewrite_start = GET_BEGINLOC(Call);
Expand Down Expand Up @@ -1230,14 +1230,10 @@ bool BTypeVisitor::VisitVarDecl(VarDecl *Decl) {
}

table.type = map_type;
table.fd = bcc_create_map(map_type, table.name.c_str(),
table.key_size, table.leaf_size,
table.max_entries, table.flags);
}
if (table.fd < 0) {
error(GET_BEGINLOC(Decl), "could not open bpf map: %0\nis %1 map type enabled in your kernel?") <<
strerror(errno) << A->getName();
return false;
table.fake_fd = fe_.get_next_fake_fd();
fe_.add_map_def(table.fake_fd, std::make_tuple((int)map_type, std::string(table.name),
(int)table.key_size, (int)table.leaf_size,
(int)table.max_entries, table.flags));
}

if (!table.is_extern)
Expand Down Expand Up @@ -1351,7 +1347,8 @@ BFrontendAction::BFrontendAction(llvm::raw_ostream &os, unsigned flags,
TableStorage &ts, const std::string &id,
const std::string &main_path,
FuncSource &func_src, std::string &mod_src,
const std::string &maps_ns)
const std::string &maps_ns,
fake_fd_map_def &fake_fd_map)
: os_(os),
flags_(flags),
ts_(ts),
Expand All @@ -1360,7 +1357,9 @@ BFrontendAction::BFrontendAction(llvm::raw_ostream &os, unsigned flags,
rewriter_(new Rewriter),
main_path_(main_path),
func_src_(func_src),
mod_src_(mod_src) {}
mod_src_(mod_src),
next_fake_fd_(-1),
fake_fd_map_(fake_fd_map) {}

bool BFrontendAction::is_rewritable_ext_func(FunctionDecl *D) {
StringRef file_name = rewriter_->getSourceMgr().getFilename(GET_BEGINLOC(D));
Expand Down
10 changes: 9 additions & 1 deletion src/cc/frontends/clang/b_frontend_action.h
Expand Up @@ -155,7 +155,8 @@ class BFrontendAction : public clang::ASTFrontendAction {
BFrontendAction(llvm::raw_ostream &os, unsigned flags, TableStorage &ts,
const std::string &id, const std::string &main_path,
FuncSource &func_src, std::string &mod_src,
const std::string &maps_ns);
const std::string &maps_ns,
fake_fd_map_def &fake_fd_map);

// Called by clang when the AST has been completed, here the output stream
// will be flushed.
Expand All @@ -170,6 +171,11 @@ class BFrontendAction : public clang::ASTFrontendAction {
std::string maps_ns() const { return maps_ns_; }
bool is_rewritable_ext_func(clang::FunctionDecl *D);
void DoMiscWorkAround();
// negative fake_fd to be different from real fd in bpf_pseudo_fd.
int get_next_fake_fd() { return next_fake_fd_--; }
void add_map_def(int fd, std::tuple<int, std::string, int, int, int, int> map_def) {
fake_fd_map_[fd] = map_def;
}

private:
llvm::raw_ostream &os_;
Expand All @@ -184,6 +190,8 @@ class BFrontendAction : public clang::ASTFrontendAction {
FuncSource &func_src_;
std::string &mod_src_;
std::set<clang::Decl *> m_;
int next_fake_fd_;
fake_fd_map_def &fake_fd_map_;
};

} // namespace visitor
13 changes: 8 additions & 5 deletions src/cc/frontends/clang/loader.cc
Expand Up @@ -108,7 +108,8 @@ 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, FuncSource &func_src,
std::string &mod_src,
const std::string &maps_ns) {
const std::string &maps_ns,
fake_fd_map_def &fake_fd_map) {
string main_path = "/virtual/main.c";
unique_ptr<llvm::MemoryBuffer> main_buf;
struct utsname un;
Expand Down Expand Up @@ -205,7 +206,7 @@ int ClangLoader::parse(unique_ptr<llvm::Module> *mod, TableStorage &ts,
#endif

if (do_compile(mod, ts, in_memory, flags_cstr, flags_cstr_rem, main_path,
main_buf, id, func_src, mod_src, true, maps_ns)) {
main_buf, id, func_src, mod_src, true, maps_ns, fake_fd_map)) {
#if BCC_BACKUP_COMPILE != 1
return -1;
#else
Expand All @@ -215,8 +216,9 @@ int ClangLoader::parse(unique_ptr<llvm::Module> *mod, TableStorage &ts,
ts.DeletePrefix(Path({id}));
func_src.clear();
mod_src.clear();
fake_fd_map.clear();
if (do_compile(mod, ts, in_memory, flags_cstr, flags_cstr_rem, main_path,
main_buf, id, func_src, mod_src, false, maps_ns))
main_buf, id, func_src, mod_src, false, maps_ns, fake_fd_map))
return -1;
#endif
}
Expand Down Expand Up @@ -263,7 +265,8 @@ int ClangLoader::do_compile(unique_ptr<llvm::Module> *mod, TableStorage &ts,
const unique_ptr<llvm::MemoryBuffer> &main_buf,
const std::string &id, FuncSource &func_src,
std::string &mod_src, bool use_internal_bpfh,
const std::string &maps_ns) {
const std::string &maps_ns,
fake_fd_map_def &fake_fd_map) {
using namespace clang;

vector<const char *> flags_cstr = flags_cstr_in;
Expand Down Expand Up @@ -377,7 +380,7 @@ int ClangLoader::do_compile(unique_ptr<llvm::Module> *mod, TableStorage &ts,
// capture the rewritten c file
string out_str1;
llvm::raw_string_ostream os1(out_str1);
BFrontendAction bact(os1, flags_, ts, id, main_path, func_src, mod_src, maps_ns);
BFrontendAction bact(os1, flags_, ts, id, main_path, func_src, mod_src, maps_ns, fake_fd_map);
if (!compiler1.ExecuteAction(bact))
return -1;
unique_ptr<llvm::MemoryBuffer> out_buf1 = llvm::MemoryBuffer::getMemBuffer(out_str1);
Expand Down
6 changes: 4 additions & 2 deletions src/cc/frontends/clang/loader.h
Expand Up @@ -54,7 +54,8 @@ class ClangLoader {
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, FuncSource &func_src,
std::string &mod_src, const std::string &maps_ns);
std::string &mod_src, const std::string &maps_ns,
fake_fd_map_def &fake_fd_map);

private:
int do_compile(std::unique_ptr<llvm::Module> *mod, TableStorage &ts,
Expand All @@ -64,7 +65,8 @@ class ClangLoader {
const std::unique_ptr<llvm::MemoryBuffer> &main_buf,
const std::string &id, FuncSource &func_src,
std::string &mod_src, bool use_internal_bpfh,
const std::string &maps_ns);
const std::string &maps_ns,
fake_fd_map_def &fake_fd_map);

private:
std::map<std::string, std::unique_ptr<llvm::MemoryBuffer>> remapped_headers_;
Expand Down

0 comments on commit 8300c7b

Please sign in to comment.