Skip to content

Commit

Permalink
Merge pull request iovisor#1405 from iamkafai/kafai_dev
Browse files Browse the repository at this point in the history
Introduce Bpf Program Snapshot (bps)
  • Loading branch information
yonghong-song committed Oct 25, 2017
2 parents ae194a1 + f89cb40 commit 3d51c0f
Show file tree
Hide file tree
Showing 15 changed files with 410 additions and 19 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall ${CXX_ISYSTEM_DIRS}")

add_subdirectory(src)
add_subdirectory(introspection)
if(ENABLE_CLANG_JIT)
add_subdirectory(examples)
add_subdirectory(man)
Expand Down
1 change: 1 addition & 0 deletions SPECS/bcc+clang.spec
Original file line number Diff line number Diff line change
Expand Up @@ -96,5 +96,6 @@ Command line tools for BPF Compiler Collection (BCC)
%exclude /usr/share/bcc/examples/*/*/*.pyo

%files -n bcc-tools
/usr/share/bcc/introspection/*
/usr/share/bcc/tools/*
/usr/share/bcc/man/*
1 change: 1 addition & 0 deletions SPECS/bcc.spec
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ Command line tools for BPF Compiler Collection (BCC)
%exclude /usr/share/bcc/examples/*/*/*.pyo

%files -n bcc-tools
/usr/share/bcc/introspection/*
/usr/share/bcc/tools/*
/usr/share/bcc/man/*

Expand Down
12 changes: 12 additions & 0 deletions introspection/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright (c) Facebook, Inc.
# Licensed under the Apache License, Version 2.0 (the "License")

include_directories(${CMAKE_SOURCE_DIR}/src/cc)
include_directories(${CMAKE_SOURCE_DIR}/src/cc/api)

option(INSTALL_INTROSPECTION "Install BPF introspection tools" ON)

add_executable(bps bps.c)
target_link_libraries(bps bcc-static)

install (TARGETS bps DESTINATION share/bcc/introspection)
309 changes: 309 additions & 0 deletions introspection/bps.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,309 @@
#include <time.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <ctype.h>
#include <sysexits.h>

#include "libbpf.h"

static const char * const prog_type_strings[] = {
[BPF_PROG_TYPE_UNSPEC] = "unspec",
[BPF_PROG_TYPE_SOCKET_FILTER] = "socket filter",
[BPF_PROG_TYPE_KPROBE] = "kprobe",
[BPF_PROG_TYPE_SCHED_CLS] = "sched cls",
[BPF_PROG_TYPE_SCHED_ACT] = "sched act",
[BPF_PROG_TYPE_TRACEPOINT] = "tracepoint",
[BPF_PROG_TYPE_XDP] = "xdp",
[BPF_PROG_TYPE_PERF_EVENT] = "perf event",
[BPF_PROG_TYPE_CGROUP_SKB] = "cgroup skb",
[BPF_PROG_TYPE_CGROUP_SOCK] = "cgroup sock",
[BPF_PROG_TYPE_LWT_IN] = "lwt in",
[BPF_PROG_TYPE_LWT_OUT] = "lwt out",
[BPF_PROG_TYPE_LWT_XMIT] = "lwt xmit",
[BPF_PROG_TYPE_SOCK_OPS] = "sock ops",
[BPF_PROG_TYPE_SK_SKB] = "sk skb",
};

static const char * const map_type_strings[] = {
[BPF_MAP_TYPE_UNSPEC] = "unspec",
[BPF_MAP_TYPE_HASH] = "hash",
[BPF_MAP_TYPE_ARRAY] = "array",
[BPF_MAP_TYPE_PROG_ARRAY] = "prog array",
[BPF_MAP_TYPE_PERF_EVENT_ARRAY] = "perf-ev array",
[BPF_MAP_TYPE_PERCPU_HASH] = "percpu hash",
[BPF_MAP_TYPE_PERCPU_ARRAY] = "percpu array",
[BPF_MAP_TYPE_STACK_TRACE] = "stack trace",
[BPF_MAP_TYPE_CGROUP_ARRAY] = "cgroup array",
[BPF_MAP_TYPE_LRU_HASH] = "lru hash",
[BPF_MAP_TYPE_LRU_PERCPU_HASH] = "lru percpu hash",
[BPF_MAP_TYPE_LPM_TRIE] = "lpm trie",
[BPF_MAP_TYPE_ARRAY_OF_MAPS] = "array of maps",
[BPF_MAP_TYPE_HASH_OF_MAPS] = "hash of maps",
[BPF_MAP_TYPE_DEVMAP] = "devmap",
[BPF_MAP_TYPE_SOCKMAP] = "sockmap",
};

#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
#define LAST_KNOWN_PROG_TYPE (ARRAY_SIZE(prog_type_strings) - 1)
#define LAST_KNOWN_MAP_TYPE (ARRAY_SIZE(map_type_strings) - 1)
#define min(x, y) ((x) < (y) ? (x) : (y))

static inline uint64_t ptr_to_u64(const void *ptr)
{
return (uint64_t) (unsigned long) ptr;
}

static inline void * u64_to_ptr(uint64_t ptr)
{
return (void *) (unsigned long ) ptr;
}

static int handle_get_next_errno(int eno)
{
switch (eno) {
case ENOENT:
return 0;
case EINVAL:
fprintf(stderr, "Kernel does not support BPF introspection\n");
return EX_UNAVAILABLE;
case EPERM:
fprintf(stderr,
"Require CAP_SYS_ADMIN capability. Please retry as root\n");
return EX_NOPERM;
default:
fprintf(stderr, "%s\n", strerror(errno));
return 1;
}
}

static void print_prog_hdr(void)
{
printf("%9s %-15s %8s %6s %-12s %-15s\n",
"BID", "TYPE", "UID", "#MAPS", "LoadTime", "NAME");
}

static void print_prog_info(const struct bpf_prog_info *prog_info)
{
struct timespec real_time_ts, boot_time_ts;
time_t wallclock_load_time = 0;
char unknown_prog_type[16];
const char *prog_type;
char load_time[16];
struct tm load_tm;

if (prog_info->type > LAST_KNOWN_PROG_TYPE) {
snprintf(unknown_prog_type, sizeof(unknown_prog_type), "<%u>",
prog_info->type);
unknown_prog_type[sizeof(unknown_prog_type) - 1] = '\0';
prog_type = unknown_prog_type;
} else {
prog_type = prog_type_strings[prog_info->type];
}

if (!clock_gettime(CLOCK_REALTIME, &real_time_ts) &&
!clock_gettime(CLOCK_BOOTTIME, &boot_time_ts) &&
real_time_ts.tv_sec >= boot_time_ts.tv_sec)
wallclock_load_time =
(real_time_ts.tv_sec - boot_time_ts.tv_sec) +
prog_info->load_time / 1000000000;

if (wallclock_load_time && localtime_r(&wallclock_load_time, &load_tm))
strftime(load_time, sizeof(load_time), "%b%d/%H:%M", &load_tm);
else
snprintf(load_time, sizeof(load_time), "<%llu>",
prog_info->load_time / 1000000000);
load_time[sizeof(load_time) - 1] = '\0';

if (prog_info->jited_prog_len)
printf("%9u %-15s %8u %6u %-12s %-15s\n",
prog_info->id, prog_type, prog_info->created_by_uid,
prog_info->nr_map_ids, load_time, prog_info->name);
else
printf("%8u- %-15s %8u %6u %-12s %-15s\n",
prog_info->id, prog_type, prog_info->created_by_uid,
prog_info->nr_map_ids, load_time, prog_info->name);
}

static void print_map_hdr(void)
{
printf("%8s %-15s %-10s %8s %8s %8s %-15s\n",
"MID", "TYPE", "FLAGS", "KeySz", "ValueSz", "MaxEnts",
"NAME");
}

static void print_map_info(const struct bpf_map_info *map_info)
{
char unknown_map_type[16];
const char *map_type;

if (map_info->type > LAST_KNOWN_MAP_TYPE) {
snprintf(unknown_map_type, sizeof(unknown_map_type),
"<%u>", map_info->type);
unknown_map_type[sizeof(unknown_map_type) - 1] = '\0';
map_type = unknown_map_type;
} else {
map_type = map_type_strings[map_info->type];
}

printf("%8u %-15s 0x%-8x %8u %8u %8u %-15s\n",
map_info->id, map_type, map_info->map_flags, map_info->key_size,
map_info->value_size, map_info->max_entries,
map_info->name);
}

static int print_one_prog(uint32_t prog_id)
{
const uint32_t usual_nr_map_ids = 64;
uint32_t nr_map_ids = usual_nr_map_ids;
struct bpf_prog_info prog_info;
uint32_t *map_ids = NULL;
uint32_t info_len;
int ret = 0;
int prog_fd;
uint32_t i;

prog_fd = bpf_prog_get_fd_by_id(prog_id);
if (prog_fd == -1) {
if (errno == ENOENT) {
fprintf(stderr, "BID:%u not found\n", prog_id);
return EX_DATAERR;
} else {
return handle_get_next_errno(errno);
}
}

/* Retry at most one time for larger map_ids array */
for (i = 0; i < 2; i++) {
bzero(&prog_info, sizeof(prog_info));
prog_info.map_ids = ptr_to_u64(realloc(map_ids,
nr_map_ids * sizeof(*map_ids)));
if (!prog_info.map_ids) {
fprintf(stderr,
"Cannot allocate memory for %u map_ids for BID:%u\n",
nr_map_ids, prog_id);
close(prog_fd);
free(map_ids);
return 1;
}

map_ids = u64_to_ptr(prog_info.map_ids);
prog_info.nr_map_ids = nr_map_ids;
info_len = sizeof(prog_info);
ret = bpf_obj_get_info(prog_fd, &prog_info, &info_len);
if (ret) {
fprintf(stderr, "Cannot get info for BID:%u. %s(%d)\n",
prog_id, strerror(errno), errno);
close(prog_fd);
free(map_ids);
return ret;
}

if (prog_info.nr_map_ids <= nr_map_ids)
break;

nr_map_ids = prog_info.nr_map_ids;
}
close(prog_fd);

print_prog_hdr();
print_prog_info(&prog_info);
printf("\n");

/* Print all map_info used by the prog */
print_map_hdr();
nr_map_ids = min(prog_info.nr_map_ids, nr_map_ids);
for (i = 0; i < nr_map_ids; i++) {
struct bpf_map_info map_info = {};
uint32_t info_len = sizeof(map_info);
int map_fd;

map_fd = bpf_map_get_fd_by_id(map_ids[i]);
if (map_fd == -1) {
if (errno == -ENOENT)
continue;

fprintf(stderr,
"Cannot get fd for map:%u. %s(%d)\n",
map_ids[i], strerror(errno), errno);
ret = map_fd;
break;
}

ret = bpf_obj_get_info(map_fd, &map_info, &info_len);
close(map_fd);
if (ret) {
fprintf(stderr, "Cannot get info for map:%u. %s(%d)\n",
map_ids[i], strerror(errno), errno);
break;
}

print_map_info(&map_info);
}

free(map_ids);
return ret;
}

int print_all_progs(void)
{
uint32_t next_id = 0;

print_prog_hdr();

while (!bpf_prog_get_next_id(next_id, &next_id)) {
struct bpf_prog_info prog_info = {};
uint32_t prog_info_len = sizeof(prog_info);
int prog_fd;
int ret;

prog_fd = bpf_prog_get_fd_by_id(next_id);
if (prog_fd < 0) {
if (errno == ENOENT)
continue;
fprintf(stderr,
"Cannot get fd for BID:%u. %s(%d)\n",
next_id, strerror(errno), errno);
return 1;
}

ret = bpf_obj_get_info(prog_fd, &prog_info, &prog_info_len);
close(prog_fd);
if (ret) {
fprintf(stderr,
"Cannot get bpf_prog_info for BID:%u. %s(%d)\n",
next_id, strerror(errno), errno);
return ret;
}

print_prog_info(&prog_info);
}

return handle_get_next_errno(errno);
}

void usage(void)
{
printf("BPF Program Snapshot (bps):\n"
"List of all BPF programs loaded into the system.\n\n");
printf("Usage: bps [bpf-prog-id]\n");
printf(" [bpf-prog-id] If specified, it shows the details info of the bpf-prog\n");
printf("\n");
}

int main(int argc, char **argv)
{
if (argc > 1) {
if (!isdigit(*argv[1])) {
usage();
return EX_USAGE;
}
return print_one_prog((uint32_t)atoi(argv[1]));
}

return print_all_progs();
}
5 changes: 3 additions & 2 deletions src/cc/api/BPF.cc
Original file line number Diff line number Diff line change
Expand Up @@ -480,11 +480,12 @@ StatusTuple BPF::load_func(const std::string& func_name,
func_name.c_str());
size_t func_size = bpf_module_->function_size(func_name);

fd = bpf_prog_load(type, reinterpret_cast<struct bpf_insn*>(func_start),
fd = bpf_prog_load(type, func_name.c_str(),
reinterpret_cast<struct bpf_insn*>(func_start),
func_size, bpf_module_->license(),
bpf_module_->kern_version(), nullptr,
0 // BPFModule will handle error printing
);
);

if (fd < 0)
return StatusTuple(-1, "Failed to load %s: %d", func_name.c_str(), fd);
Expand Down
4 changes: 3 additions & 1 deletion src/cc/frontends/b/codegen_llvm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1104,7 +1104,9 @@ StatusTuple CodegenLLVM::visit_table_decl_stmt_node(TableDeclStmtNode *n) {
decl_gvar->setSection("maps");
tables_[n] = decl_gvar;

int map_fd = bpf_create_map(map_type, key->bit_width_ / 8, leaf->bit_width_ / 8, n->size_, 0);
int map_fd = bpf_create_map(map_type, n->id_->name_.c_str(),
key->bit_width_ / 8, leaf->bit_width_ / 8,
n->size_, 0);
if (map_fd >= 0)
table_fds_[n] = map_fd;
} else {
Expand Down
4 changes: 3 additions & 1 deletion src/cc/frontends/clang/b_frontend_action.cc
Original file line number Diff line number Diff line change
Expand Up @@ -724,7 +724,9 @@ bool BTypeVisitor::VisitVarDecl(VarDecl *Decl) {
}

table.type = map_type;
table.fd = bpf_create_map(map_type, table.key_size, table.leaf_size, table.max_entries, table.flags);
table.fd = bpf_create_map(map_type, table.name.c_str(),
table.key_size, table.leaf_size,
table.max_entries, table.flags);
}
if (table.fd < 0) {
error(Decl->getLocStart(), "could not open bpf map: %0\nis %1 map type enabled in your kernel?") <<
Expand Down
Loading

0 comments on commit 3d51c0f

Please sign in to comment.