Commit 4f47e3b5 authored by Alexei Starovoitov's avatar Alexei Starovoitov

annotate program tag

during debug of production systems it's difficult to trace back
the kernel reported 'bpf_prog_4985bb0bd6c69631' symbols to the source code
of the program, hence teach bcc to store the main function source
in the /var/tmp/bcc/bpf_prog_4985bb0bd6c69631/ directory.

This program tag is stable. Every time the script is called the tag
will be the same unless source code of the program changes.
During active development of bcc scripts the /var/tmp/bcc/ dir can
get a bunch of stale tags. The users have to trim that dir manually.

Python scripts can be modified to use this feature too, but probably
need to be gated by the flag. For c++ api I think it makes sense
to store the source code always, since the cost is minimal and
c++ api is used by long running services.

Example:
$ ./examples/cpp/LLCStat
$ ls -l /var/tmp/bcc/bpf_prog_4985bb0bd6c69631/
total 16
-rw-r--r--. 1 root root 226 Sep  1 17:30 on_cache_miss.c
-rw-r--r--. 1 root root 487 Sep  1 17:30 on_cache_miss.rewritten.c
-rw-r--r--. 1 root root 224 Sep  1 17:30 on_cache_ref.c
-rw-r--r--. 1 root root 484 Sep  1 17:30 on_cache_ref.rewritten.c

Note that there are two .c files there, since two different
bpf programs have exactly the same bytecode hence same prog_tag.

$ cat /var/tmp/bcc/bpf_prog_4985bb0bd6c69631/on_cache_miss.c
int on_cache_miss(struct bpf_perf_event_data *ctx) {
    struct event_t key = {};
    get_key(&key);

    u64 zero = 0, *val;
    val = miss_count.lookup_or_init(&key, &zero);
...
Signed-off-by: default avatarAlexei Starovoitov <ast@fb.com>
parent b1df37c8
...@@ -57,6 +57,10 @@ if(NOT DEFINED BCC_KERNEL_MODULES_DIR) ...@@ -57,6 +57,10 @@ if(NOT DEFINED BCC_KERNEL_MODULES_DIR)
set(BCC_KERNEL_MODULES_DIR "/lib/modules") set(BCC_KERNEL_MODULES_DIR "/lib/modules")
endif() endif()
if(NOT DEFINED BCC_PROG_TAG_DIR)
set(BCC_PROG_TAG_DIR "/var/tmp/bcc")
endif()
# As reported in issue #735, GCC 6 has some behavioral problems when # As reported in issue #735, GCC 6 has some behavioral problems when
# dealing with -isystem. Hence, skip the warning optimization # dealing with -isystem. Hence, skip the warning optimization
# altogether on that compiler. # altogether on that compiler.
......
...@@ -14,7 +14,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/compat) ...@@ -14,7 +14,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/compat)
add_definitions(${LLVM_DEFINITIONS}) add_definitions(${LLVM_DEFINITIONS})
configure_file(libbcc.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libbcc.pc @ONLY) configure_file(libbcc.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libbcc.pc @ONLY)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -DBCC_PROG_TAG_DIR='\"${BCC_PROG_TAG_DIR}\"'")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
include(static_libstdc++) include(static_libstdc++)
......
...@@ -488,6 +488,10 @@ StatusTuple BPF::load_func(const std::string& func_name, ...@@ -488,6 +488,10 @@ StatusTuple BPF::load_func(const std::string& func_name,
if (fd < 0) if (fd < 0)
return StatusTuple(-1, "Failed to load %s: %d", func_name.c_str(), fd); return StatusTuple(-1, "Failed to load %s: %d", func_name.c_str(), fd);
bpf_module_->annotate_prog_tag(func_name, fd,
reinterpret_cast<struct bpf_insn*>(func_start),
func_size);
funcs_[func_name] = fd; funcs_[func_name] = fd;
return StatusTuple(0); return StatusTuple(0);
} }
......
...@@ -113,6 +113,7 @@ BPFModule::BPFModule(unsigned flags, TableStorage *ts) ...@@ -113,6 +113,7 @@ BPFModule::BPFModule(unsigned flags, TableStorage *ts)
local_ts_ = createSharedTableStorage(); local_ts_ = createSharedTableStorage();
ts_ = &*local_ts_; ts_ = &*local_ts_;
} }
func_src_ = ebpf::make_unique<FuncSource>();
} }
static StatusTuple unimplemented_sscanf(const char *, void *) { static StatusTuple unimplemented_sscanf(const char *, void *) {
...@@ -133,6 +134,7 @@ BPFModule::~BPFModule() { ...@@ -133,6 +134,7 @@ BPFModule::~BPFModule() {
engine_.reset(); engine_.reset();
rw_engine_.reset(); rw_engine_.reset();
ctx_.reset(); ctx_.reset();
func_src_.reset();
ts_->DeletePrefix(Path({id_})); ts_->DeletePrefix(Path({id_}));
} }
...@@ -456,7 +458,7 @@ unique_ptr<ExecutionEngine> BPFModule::finalize_rw(unique_ptr<Module> m) { ...@@ -456,7 +458,7 @@ unique_ptr<ExecutionEngine> BPFModule::finalize_rw(unique_ptr<Module> m) {
// load an entire c file as a module // load an entire c file as a module
int BPFModule::load_cfile(const string &file, bool in_memory, const char *cflags[], int ncflags) { int BPFModule::load_cfile(const string &file, bool in_memory, const char *cflags[], int ncflags) {
clang_loader_ = ebpf::make_unique<ClangLoader>(&*ctx_, flags_); clang_loader_ = ebpf::make_unique<ClangLoader>(&*ctx_, flags_);
if (clang_loader_->parse(&mod_, *ts_, file, in_memory, cflags, ncflags, id_)) if (clang_loader_->parse(&mod_, *ts_, file, in_memory, cflags, ncflags, id_, *func_src_))
return -1; return -1;
return 0; return 0;
} }
...@@ -468,7 +470,7 @@ int BPFModule::load_cfile(const string &file, bool in_memory, const char *cflags ...@@ -468,7 +470,7 @@ int BPFModule::load_cfile(const string &file, bool in_memory, const char *cflags
// build an ExecutionEngine. // build an ExecutionEngine.
int BPFModule::load_includes(const string &text) { int BPFModule::load_includes(const string &text) {
clang_loader_ = ebpf::make_unique<ClangLoader>(&*ctx_, flags_); clang_loader_ = ebpf::make_unique<ClangLoader>(&*ctx_, flags_);
if (clang_loader_->parse(&mod_, *ts_, text, true, nullptr, 0, "")) if (clang_loader_->parse(&mod_, *ts_, text, true, nullptr, 0, "", *func_src_))
return -1; return -1;
return 0; return 0;
} }
...@@ -632,6 +634,68 @@ uint8_t * BPFModule::function_start(const string &name) const { ...@@ -632,6 +634,68 @@ uint8_t * BPFModule::function_start(const string &name) const {
return get<0>(section->second); return get<0>(section->second);
} }
const char * BPFModule::function_source(const string &name) const {
return func_src_->src(name);
}
const char * BPFModule::function_source_rewritten(const string &name) const {
return func_src_->src_rewritten(name);
}
int BPFModule::annotate_prog_tag(const string &name, int prog_fd,
struct bpf_insn *insns, int prog_len) {
unsigned long long tag1, tag2;
int err;
err = bpf_prog_compute_tag(insns, prog_len, &tag1);
if (err)
return err;
err = bpf_prog_get_tag(prog_fd, &tag2);
if (err)
return err;
if (tag1 != tag2) {
fprintf(stderr, "prog tag mismatch %llx %llx\n", tag1, tag2);
return -1;
}
err = mkdir(BCC_PROG_TAG_DIR, 0777);
if (err && errno != EEXIST) {
fprintf(stderr, "cannot create " BCC_PROG_TAG_DIR "\n");
return -1;
}
char buf[128];
::snprintf(buf, sizeof(buf), BCC_PROG_TAG_DIR "/bpf_prog_%llx", tag1);
err = mkdir(buf, 0777);
if (err && errno != EEXIST) {
fprintf(stderr, "cannot create %s\n", buf);
return -1;
}
::snprintf(buf, sizeof(buf), BCC_PROG_TAG_DIR "/bpf_prog_%llx/%s.c",
tag1, name.data());
FileDesc fd(open(buf, O_CREAT | O_WRONLY | O_TRUNC, 0644));
if (fd < 0) {
fprintf(stderr, "cannot create %s\n", buf);
return -1;
}
const char *src = function_source(name);
write(fd, src, strlen(src));
::snprintf(buf, sizeof(buf), BCC_PROG_TAG_DIR "/bpf_prog_%llx/%s.rewritten.c",
tag1, name.data());
fd = open(buf, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd < 0) {
fprintf(stderr, "cannot create %s\n", buf);
return -1;
}
src = function_source_rewritten(name);
write(fd, src, strlen(src));
return 0;
}
size_t BPFModule::function_size(size_t id) const { size_t BPFModule::function_size(size_t id) const {
if (id >= function_names_.size()) if (id >= function_names_.size())
return 0; return 0;
......
...@@ -37,6 +37,7 @@ class TableDesc; ...@@ -37,6 +37,7 @@ class TableDesc;
class TableStorage; class TableStorage;
class BLoader; class BLoader;
class ClangLoader; class ClangLoader;
class FuncSource;
class BPFModule { class BPFModule {
private: private:
...@@ -68,6 +69,10 @@ class BPFModule { ...@@ -68,6 +69,10 @@ class BPFModule {
size_t num_functions() const; size_t num_functions() const;
uint8_t * function_start(size_t id) const; uint8_t * function_start(size_t id) const;
uint8_t * function_start(const std::string &name) const; uint8_t * function_start(const std::string &name) const;
const char * function_source(const std::string &name) const;
const char * function_source_rewritten(const std::string &name) const;
int annotate_prog_tag(const std::string &name, int fd,
struct bpf_insn *insn, int prog_len);
const char * function_name(size_t id) const; const char * function_name(size_t id) const;
size_t function_size(size_t id) const; size_t function_size(size_t id) const;
size_t function_size(const std::string &name) const; size_t function_size(const std::string &name) const;
...@@ -108,6 +113,7 @@ class BPFModule { ...@@ -108,6 +113,7 @@ class BPFModule {
std::unique_ptr<llvm::Module> mod_; std::unique_ptr<llvm::Module> mod_;
std::unique_ptr<BLoader> b_loader_; std::unique_ptr<BLoader> b_loader_;
std::unique_ptr<ClangLoader> clang_loader_; std::unique_ptr<ClangLoader> clang_loader_;
std::unique_ptr<FuncSource> func_src_;
std::map<std::string, std::tuple<uint8_t *, uintptr_t>> sections_; std::map<std::string, std::tuple<uint8_t *, uintptr_t>> sections_;
std::vector<TableDesc *> tables_; std::vector<TableDesc *> tables_;
std::map<std::string, size_t> table_names_; std::map<std::string, size_t> table_names_;
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <clang/Rewrite/Core/Rewriter.h> #include <clang/Rewrite/Core/Rewriter.h>
#include "b_frontend_action.h" #include "b_frontend_action.h"
#include "loader.h"
#include "common.h" #include "common.h"
#include "table_storage.h" #include "table_storage.h"
...@@ -222,6 +223,9 @@ bool BTypeVisitor::VisitFunctionDecl(FunctionDecl *D) { ...@@ -222,6 +223,9 @@ bool BTypeVisitor::VisitFunctionDecl(FunctionDecl *D) {
auto real_start_loc = rewriter_.getSourceMgr().getFileLoc(D->getLocStart()); auto real_start_loc = rewriter_.getSourceMgr().getFileLoc(D->getLocStart());
if (D->isExternallyVisible() && D->hasBody()) { if (D->isExternallyVisible() && D->hasBody()) {
current_fn_ = D->getName(); current_fn_ = D->getName();
string bd = rewriter_.getRewrittenText(expansionRange(D->getSourceRange()));
fe_.func_src_.set_src(current_fn_, bd);
fe_.func_range_[current_fn_] = expansionRange(D->getSourceRange());
string attr = string("__attribute__((section(\"") + BPF_FN_PREFIX + D->getName().str() + "\")))\n"; string attr = string("__attribute__((section(\"") + BPF_FN_PREFIX + D->getName().str() + "\")))\n";
rewriter_.InsertText(real_start_loc, attr); rewriter_.InsertText(real_start_loc, attr);
if (D->param_size() > MAX_CALLING_CONV_REGS + 1) { if (D->param_size() > MAX_CALLING_CONV_REGS + 1) {
...@@ -776,12 +780,18 @@ bool ProbeConsumer::HandleTopLevelDecl(DeclGroupRef Group) { ...@@ -776,12 +780,18 @@ bool ProbeConsumer::HandleTopLevelDecl(DeclGroupRef Group) {
} }
BFrontendAction::BFrontendAction(llvm::raw_ostream &os, unsigned flags, TableStorage &ts, BFrontendAction::BFrontendAction(llvm::raw_ostream &os, unsigned flags, TableStorage &ts,
const std::string &id) const std::string &id, FuncSource& func_src)
: os_(os), flags_(flags), ts_(ts), id_(id), rewriter_(new Rewriter) {} : os_(os), flags_(flags), ts_(ts), id_(id), rewriter_(new Rewriter), func_src_(func_src) {}
void BFrontendAction::EndSourceFileAction() { void BFrontendAction::EndSourceFileAction() {
if (flags_ & DEBUG_PREPROCESSOR) if (flags_ & DEBUG_PREPROCESSOR)
rewriter_->getEditBuffer(rewriter_->getSourceMgr().getMainFileID()).write(llvm::errs()); rewriter_->getEditBuffer(rewriter_->getSourceMgr().getMainFileID()).write(llvm::errs());
for (auto func : func_range_) {
auto f = func.first;
string bd = rewriter_->getRewrittenText(func_range_[f]);
func_src_.set_src_rewritten(f, bd);
}
rewriter_->getEditBuffer(rewriter_->getSourceMgr().getMainFileID()).write(os_); rewriter_->getEditBuffer(rewriter_->getSourceMgr().getMainFileID()).write(os_);
os_.flush(); os_.flush();
} }
......
...@@ -42,6 +42,7 @@ class StringRef; ...@@ -42,6 +42,7 @@ class StringRef;
namespace ebpf { namespace ebpf {
class BFrontendAction; class BFrontendAction;
class FuncSource;
// Type visitor and rewriter for B programs. // Type visitor and rewriter for B programs.
// It will look for B-specific features and rewrite them into a valid // It will look for B-specific features and rewrite them into a valid
...@@ -122,7 +123,8 @@ class BFrontendAction : public clang::ASTFrontendAction { ...@@ -122,7 +123,8 @@ class BFrontendAction : public clang::ASTFrontendAction {
public: public:
// Initialize with the output stream where the new source file contents // Initialize with the output stream where the new source file contents
// should be written. // should be written.
BFrontendAction(llvm::raw_ostream &os, unsigned flags, TableStorage &ts, const std::string &id); BFrontendAction(llvm::raw_ostream &os, unsigned flags, TableStorage &ts, const std::string &id,
FuncSource& func_src);
// Called by clang when the AST has been completed, here the output stream // Called by clang when the AST has been completed, here the output stream
// will be flushed. // will be flushed.
...@@ -141,6 +143,9 @@ class BFrontendAction : public clang::ASTFrontendAction { ...@@ -141,6 +143,9 @@ class BFrontendAction : public clang::ASTFrontendAction {
TableStorage &ts_; TableStorage &ts_;
std::string id_; std::string id_;
std::unique_ptr<clang::Rewriter> rewriter_; std::unique_ptr<clang::Rewriter> rewriter_;
friend class BTypeVisitor;
std::map<std::string, clang::SourceRange> func_range_;
FuncSource& func_src_;
}; };
} // namespace visitor } // namespace visitor
...@@ -105,7 +105,8 @@ std::pair<bool, string> get_kernel_path_info(const string kdir) ...@@ -105,7 +105,8 @@ std::pair<bool, string> get_kernel_path_info(const string kdir)
} }
int ClangLoader::parse(unique_ptr<llvm::Module> *mod, TableStorage &ts, const string &file, 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) { bool in_memory, const char *cflags[], int ncflags, const std::string &id,
FuncSource& func_src) {
using namespace clang; using namespace clang;
string main_path = "/virtual/main.c"; string main_path = "/virtual/main.c";
...@@ -276,7 +277,7 @@ int ClangLoader::parse(unique_ptr<llvm::Module> *mod, TableStorage &ts, const st ...@@ -276,7 +277,7 @@ int ClangLoader::parse(unique_ptr<llvm::Module> *mod, TableStorage &ts, const st
// capture the rewritten c file // capture the rewritten c file
string out_str1; string out_str1;
llvm::raw_string_ostream os1(out_str1); llvm::raw_string_ostream os1(out_str1);
BFrontendAction bact(os1, flags_, ts, id); BFrontendAction bact(os1, flags_, ts, id, func_src);
if (!compiler1.ExecuteAction(bact)) if (!compiler1.ExecuteAction(bact))
return -1; return -1;
unique_ptr<llvm::MemoryBuffer> out_buf1 = llvm::MemoryBuffer::getMemBuffer(out_str1); unique_ptr<llvm::MemoryBuffer> out_buf1 = llvm::MemoryBuffer::getMemBuffer(out_str1);
...@@ -312,5 +313,26 @@ int ClangLoader::parse(unique_ptr<llvm::Module> *mod, TableStorage &ts, const st ...@@ -312,5 +313,26 @@ int ClangLoader::parse(unique_ptr<llvm::Module> *mod, TableStorage &ts, const st
return 0; return 0;
} }
const char * FuncSource::src(const std::string& name) {
auto src = funcs_.find(name);
if (src == funcs_.end())
return "";
return src->second.src_.data();
}
const char * FuncSource::src_rewritten(const std::string& name) {
auto src = funcs_.find(name);
if (src == funcs_.end())
return "";
return src->second.src_rewritten_.data();
}
void FuncSource::set_src(const std::string& name, const std::string& src) {
funcs_[name].src_ = src;
}
void FuncSource::set_src_rewritten(const std::string& name, const std::string& src) {
funcs_[name].src_rewritten_ = src;
}
} // namespace ebpf } // namespace ebpf
...@@ -30,12 +30,29 @@ class MemoryBuffer; ...@@ -30,12 +30,29 @@ class MemoryBuffer;
namespace ebpf { namespace ebpf {
class FuncSource {
class SourceCode {
public:
SourceCode(const std::string& s1 = "", const std::string& s2 = ""): src_(s1), src_rewritten_(s2) {}
std::string src_;
std::string src_rewritten_;
};
std::map<std::string, SourceCode> funcs_;
public:
FuncSource() {}
const char * src(const std::string& name);
const char * src_rewritten(const std::string& name);
void set_src(const std::string& name, const std::string& src);
void set_src_rewritten(const std::string& name, const std::string& src);
};
class ClangLoader { class ClangLoader {
public: public:
explicit ClangLoader(llvm::LLVMContext *ctx, unsigned flags); explicit ClangLoader(llvm::LLVMContext *ctx, unsigned flags);
~ClangLoader(); ~ClangLoader();
int parse(std::unique_ptr<llvm::Module> *mod, TableStorage &ts, const std::string &file, 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); bool in_memory, const char *cflags[], int ncflags, const std::string &id,
FuncSource& func_src);
private: private:
static std::map<std::string, std::unique_ptr<llvm::MemoryBuffer>> remapped_files_; static std::map<std::string, std::unique_ptr<llvm::MemoryBuffer>> remapped_files_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment