Skip to content

Commit

Permalink
add btf func_info and line_info support
Browse files Browse the repository at this point in the history
The libbpf API functions are used to parse/load
.BTF and .BTF.ext ELF sections, and retrieve
func_info and line_info for prog loading.

The LLVM cannot get remapped file source for
line_info. So a postprocessing after compilation
fixed the line_off (offset to the string table
for the line_info) in .BTF.ext and
adjusted string section in .BTF to accommodate
new source lines.

For users using python and C++ API, no changes
to their codes. If .BTF and .BTF.ext are supported
in both compiler and kernel, they will automatically
get benefit, e.g., verifier log annotated with source
code, bpftool showing source annotated jit codes, etc.

For example, with latest bpf-next and llvm trunk,
running `tcpconnect.py`, bpftool (from kernel) is able
to show the following:
  -bash-4.4$ sudo bpftool p d jited id 165
  int trace_connect_v6_return(struct pt_regs * ctx):
  bpf_prog_8937c3924bd93e28_trace_connect_v6_return:
  ; int trace_connect_v6_return(struct pt_regs *ctx)
     0:   push   %rbp
     1:   mov    %rsp,%rbp
     4:   sub    $0x88,%rsp
     b:   sub    $0x28,%rbp
     f:   mov    %rbx,0x0(%rbp)
    13:   mov    %r13,0x8(%rbp)
    17:   mov    %r14,0x10(%rbp)
    1b:   mov    %r15,0x18(%rbp)
    1f:   xor    %eax,%eax
    21:   mov    %rax,0x20(%rbp)
    25:   mov    %rdi,%rbx
  ; int ret = PT_REGS_RC(ctx);
    28:   mov    0x50(%rbx),%r13
  ; u32 pid = bpf_get_current_pid_tgid();
    2c:   callq  0xffffffffe0e4ebfe
  ; u32 pid = bpf_get_current_pid_tgid();
    31:   mov    %eax,-0x4(%rbp)
  ...
  -bash-4.4$ sudo bpftool p d xlated id 165
  int trace_connect_v6_return(struct pt_regs * ctx):
  ; int trace_connect_v6_return(struct pt_regs *ctx)
     0: (bf) r6 = r1
  ; int ret = PT_REGS_RC(ctx);
     1: (79) r7 = *(u64 *)(r6 +80)
  ; u32 pid = bpf_get_current_pid_tgid();
     2: (85) call bpf_get_current_pid_tgid#76208
  ; u32 pid = bpf_get_current_pid_tgid();
     3: (63) *(u32 *)(r10 -4) = r0
  ; skpp = bpf_map_lookup_elem((void *)bpf_pseudo_fd(1, 3), &pid);
     4: (18) r1 = map[id:298]
     6: (bf) r2 = r10

Signed-off-by: Yonghong Song <[email protected]>
  • Loading branch information
yonghong-song committed Feb 9, 2019
1 parent 89bf6f7 commit 48ca781
Show file tree
Hide file tree
Showing 12 changed files with 423 additions and 9 deletions.
2 changes: 1 addition & 1 deletion src/cc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ add_library(bpf-shared SHARED libbpf.c perf_reader.c ${libbpf_sources})
set_target_properties(bpf-shared PROPERTIES VERSION ${REVISION_LAST} SOVERSION 0)
set_target_properties(bpf-shared PROPERTIES OUTPUT_NAME bpf)

set(bcc_common_sources bpf_common.cc bpf_module.cc exported_files.cc)
set(bcc_common_sources bpf_common.cc bpf_module.cc bcc_btf.cc exported_files.cc)
if (${LLVM_PACKAGE_VERSION} VERSION_EQUAL 6 OR ${LLVM_PACKAGE_VERSION} VERSION_GREATER 6)
set(bcc_common_sources ${bcc_common_sources} bcc_debug.cc)
endif()
Expand Down
2 changes: 1 addition & 1 deletion src/cc/api/BPF.cc
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,7 @@ StatusTuple BPF::load_func(const std::string& func_name, bpf_prog_type type,
else if (flag_ & DEBUG_BPF)
log_level = 1;

fd = bcc_prog_load(type, func_name.c_str(),
fd = bpf_module_->bcc_func_load(type, func_name.c_str(),
reinterpret_cast<struct bpf_insn*>(func_start), func_size,
bpf_module_->license(), bpf_module_->kern_version(),
log_level, nullptr, 0);
Expand Down
202 changes: 202 additions & 0 deletions src/cc/bcc_btf.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
/*
* Copyright (c) 2019 Facebook, 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
*
* https://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 "bcc_btf.h"
#include <string.h>
#include "linux/btf.h"
#include "libbpf.h"
#include "libbpf/src/btf.h"
#include <vector>

namespace ebpf {

uint32_t BTFStringTable::addString(std::string S) {
// Check whether the string already exists.
for (auto &OffsetM : OffsetToIdMap) {
if (Table[OffsetM.second] == S)
return OffsetM.first;
}
// Not find, add to the string table.
uint32_t Offset = Size;
OffsetToIdMap[Offset] = Table.size();
Table.push_back(S);
Size += S.size() + 1;
return Offset;
}

BTF::~BTF() {
btf__free(btf_);
btf_ext__free(btf_ext_);
}

// The compiler doesn't have source code for remapped files.
// So we modify .BTF and .BTF.ext sections here to add these
// missing line source codes.
// The .BTF and .BTF.ext ELF section specification can be
// found at linux repo: linux/Documentation/bpf/btf.rst.
void BTF::adjust(uint8_t *btf_sec, uintptr_t btf_sec_size,
uint8_t *btf_ext_sec, uintptr_t btf_ext_sec_size,
std::map<std::string, std::string> &remapped_sources,
uint8_t **new_btf_sec, uintptr_t *new_btf_sec_size) {

// Line cache for remapped files
std::map<std::string, std::vector<std::string>> LineCaches;
for (auto it = remapped_sources.begin(); it != remapped_sources.end(); ++it) {
size_t FileBufSize = it->second.size();
std::vector<std::string> LineCache;

for (uint32_t start = 0, end = start; end < FileBufSize; end++) {
if (it->second[end] == '\n' || end == FileBufSize - 1 ||
(it->second[end] == '\r' && it->second[end + 1] == '\n')) {
// Not including the endline
LineCache.push_back(std::string(it->second.substr(start, end - start)));
if (it->second[end] == '\r')
end++;
start = end + 1;
}
}
LineCaches[it->first] = std::move(LineCache);
}

// Check the LineInfo table and add missing lines

struct btf_header *hdr = (struct btf_header *)btf_sec;
struct btf_ext_header *ehdr = (struct btf_ext_header *)btf_ext_sec;

char *strings = (char *)(btf_sec + hdr->hdr_len + hdr->str_off);
unsigned orig_strings_len = hdr->str_len;
unsigned *linfo_s = (unsigned *)(btf_ext_sec + ehdr->hdr_len + ehdr->line_info_off);
unsigned lrec_size = *linfo_s;
linfo_s++;
unsigned linfo_len = ehdr->line_info_len - 4;

// Go through all line info. For any line number whose line is in the LineCaches,
// Correct the line_off and record the corresponding source line in BTFStringTable,
// which later will be merged into .BTF string section.
BTFStringTable new_strings;
while (linfo_len) {
unsigned num_recs = linfo_s[1];
linfo_s += 2;
for (unsigned i = 0; i < num_recs; i++) {
struct bpf_line_info *linfo = (struct bpf_line_info *)linfo_s;
if (linfo->line_off == 0) {
for (auto it = LineCaches.begin(); it != LineCaches.end(); ++it) {
if (strcmp(strings + linfo->file_name_off, it->first.c_str()) == 0) {
unsigned line_num = BPF_LINE_INFO_LINE_NUM(linfo->line_col);
if (line_num > 0 && line_num <= it->second.size())
linfo->line_off = orig_strings_len + new_strings.addString(it->second[line_num - 1]);
}
}
}
linfo_s += lrec_size >> 2;
}
linfo_len -= 8 + num_recs * lrec_size;
}

// If any new source lines need to be recorded, do not touch the original section,
// allocate a new section. The original section is allocated through llvm infra.
if (new_strings.getSize() > 0) {
// LLVM generated .BTF layout always has type_sec followed by str_sec without holes,
// so we can just append the new strings to the end and adjust str_sec size.
unsigned tmp_sec_size = btf_sec_size + new_strings.getSize();
uint8_t *tmp_sec = new uint8_t[tmp_sec_size];
memcpy(tmp_sec, btf_sec, btf_sec_size);

struct btf_header *nhdr = (struct btf_header *)tmp_sec;
nhdr->str_len += new_strings.getSize();

// Populate new strings to the string table.
uint8_t *new_str = tmp_sec + nhdr->hdr_len + nhdr->str_off + orig_strings_len;
std::vector<std::string> &Table = new_strings.getTable();
for (unsigned i = 0; i < Table.size(); i++) {
strcpy((char *)new_str, Table[i].c_str());
new_str += Table[i].size() + 1;
}

*new_btf_sec = tmp_sec;
*new_btf_sec_size = tmp_sec_size;
}
}

int BTF::load(uint8_t *btf_sec, uintptr_t btf_sec_size,
uint8_t *btf_ext_sec, uintptr_t btf_ext_sec_size,
std::map<std::string, std::string> &remapped_sources) {
struct btf *btf;
struct btf_ext *btf_ext;
uint8_t *new_btf_sec = NULL;
uintptr_t new_btf_sec_size = 0;

adjust(btf_sec, btf_sec_size, btf_ext_sec, btf_ext_sec_size,
remapped_sources, &new_btf_sec, &new_btf_sec_size);

if (new_btf_sec) {
btf = btf__new(new_btf_sec, new_btf_sec_size);
delete new_btf_sec;
} else {
btf = btf__new(btf_sec, btf_sec_size);
}
if (!btf) {
fprintf(stderr, "Processing .BTF section failure\n");
return -1;
}

btf_ext = btf_ext__new(btf_ext_sec, btf_ext_sec_size);
if (!btf_ext) {
btf__free(btf);
fprintf(stderr, "Processing .BTF.ext section failure\n");
return -1;
}

btf_ = btf;
btf_ext_ = btf_ext;
return 0;
}

int BTF::get_fd() {
return btf__fd(btf_);
}

int BTF::get_btf_info(const char *fname,
void **func_info, unsigned *func_info_cnt,
unsigned *finfo_rec_size,
void **line_info, unsigned *line_info_cnt,
unsigned *linfo_rec_size) {
int ret;

*func_info = *line_info = NULL;
*func_info_cnt = *line_info_cnt = 0;

*finfo_rec_size = btf_ext__func_info_rec_size(btf_ext_);
*linfo_rec_size = btf_ext__line_info_rec_size(btf_ext_);

ret = btf_ext__reloc_func_info(btf_, btf_ext_, fname, 0,
func_info, func_info_cnt);
if (ret) {
fprintf(stderr, ".BTF.ext reloc func_info not successful\n");
return ret;
}

ret = btf_ext__reloc_line_info(btf_, btf_ext_, fname, 0,
line_info, line_info_cnt);
if (ret) {
fprintf(stderr, ".BTF.ext reloc line_info not successful\n");
return ret;
}

return 0;
}

} // namespace ebpf
70 changes: 70 additions & 0 deletions src/cc/bcc_btf.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright (c) 2019 Facebook, 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
*
* https://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.
*/

#ifndef BCC_BTF_H
#define BCC_BTF_H

#include <stdbool.h>
#include <stdint.h>
#include <string>
#include <map>
#include <vector>

struct btf;
struct btf_ext;

namespace ebpf {

class BTFStringTable {
private:
uint32_t Size;
std::map<uint32_t, uint32_t> OffsetToIdMap;
std::vector<std::string> Table;

public:
BTFStringTable(): Size(0) {}
uint32_t getSize() { return Size; }
std::vector<std::string> &getTable() { return Table; }
uint32_t addString(std::string Str);
};

class BTF {
public:
~BTF();
int load(uint8_t *btf_sec, uintptr_t btf_sec_size,
uint8_t *btf_ext_sec, uintptr_t btf_ext_sec_size,
std::map<std::string, std::string> &remapped_sources);
int get_fd();
int get_btf_info(const char *fname,
void **func_info, unsigned *func_info_cnt,
unsigned *finfo_rec_size,
void **line_info, unsigned *line_info_cnt,
unsigned *linfo_rec_size);

private:
void adjust(uint8_t *btf_sec, uintptr_t btf_sec_size,
uint8_t *btf_ext_sec, uintptr_t btf_ext_sec_size,
std::map<std::string, std::string> &remapped_sources,
uint8_t **new_btf_sec, uintptr_t *new_btf_sec_size);

private:
struct btf *btf_;
struct btf_ext *btf_ext_;
};

} // namespace ebpf

#endif
12 changes: 12 additions & 0 deletions src/cc/bpf_common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -234,4 +234,16 @@ int bpf_table_leaf_sscanf(void *program, size_t id, const char *buf, void *leaf)
return mod->table_leaf_scanf(id, buf, leaf);
}

int bcc_func_load(void *program, int prog_type, const char *name,
const struct bpf_insn *insns, int prog_len,
const char *license, unsigned kern_version,
int log_level, char *log_buf, unsigned log_buf_size) {
auto mod = static_cast<ebpf::BPFModule *>(program);
if (!mod) return -1;
return mod->bcc_func_load(prog_type, name, insns, prog_len,
license, kern_version, log_level,
log_buf, log_buf_size);

}

}
6 changes: 6 additions & 0 deletions src/cc/bpf_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ int bpf_table_leaf_snprintf(void *program, size_t id, char *buf, size_t buflen,
int bpf_table_key_sscanf(void *program, size_t id, const char *buf, void *key);
int bpf_table_leaf_sscanf(void *program, size_t id, const char *buf, void *leaf);

struct bpf_insn;
int bcc_func_load(void *program, int prog_type, const char *name,
const struct bpf_insn *insns, int prog_len,
const char *license, unsigned kern_version,
int log_level, char *log_buf, unsigned log_buf_size);

#ifdef __cplusplus
}
#endif
Expand Down
Loading

0 comments on commit 48ca781

Please sign in to comment.