Commit 70655ee8 authored by 4ast's avatar 4ast Committed by GitHub

Merge pull request #781 from palmtenor/api

C++ helper class for BCC
parents 5794642f 284dd8e8
---
BasedOnStyle: Google
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
IndentCaseLabels: false
AccessModifierOffset: -2
set(EXAMPLE_PROGRAMS hello_world.py)
install(PROGRAMS ${EXAMPLE_PROGRAMS} DESTINATION share/bcc/examples)
add_subdirectory(cpp)
add_subdirectory(lua)
add_subdirectory(networking)
add_subdirectory(tracing)
add_subdirectory(lua)
\ No newline at end of file
# Copyright (c) Facebook, Inc.
# Licensed under the Apache License, Version 2.0 (the "License")
include_directories(${CMAKE_SOURCE_DIR}/src/cc)
add_executable(HelloWorld HelloWorld.cc)
target_link_libraries(HelloWorld bcc-static)
install (TARGETS HelloWorld DESTINATION share/bcc/examples/cpp)
add_executable(CPUDistribution CPUDistribution.cc)
target_link_libraries(CPUDistribution bcc-static)
install (TARGETS CPUDistribution DESTINATION share/bcc/examples/cpp)
add_executable(RecordMySQLQuery RecordMySQLQuery.cc)
target_link_libraries(RecordMySQLQuery bcc-static)
install (TARGETS RecordMySQLQuery DESTINATION share/bcc/examples/cpp)
add_executable(TCPSendStack TCPSendStack.cc)
target_link_libraries(TCPSendStack bcc-static)
install (TARGETS TCPSendStack DESTINATION share/bcc/examples/cpp)
add_executable(RandomRead RandomRead.cc)
target_link_libraries(RandomRead bcc-static)
install (TARGETS RandomRead DESTINATION share/bcc/examples/cpp)
/*
* CPUDistribution Show load distribution across CPU cores during a period of
* time. For Linux, uses BCC, eBPF. Embedded C.
*
* Basic example of BCC and kprobes.
*
* USAGE: CPUDistribution [duration]
*
* Copyright (c) Facebook, Inc.
* Licensed under the Apache License, Version 2.0 (the "License")
*/
#include <unistd.h>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <string>
#include "BPF.h"
const std::string BPF_PROGRAM = R"(
#include <linux/sched.h>
#include <uapi/linux/ptrace.h>
BPF_HASH(pid_to_cpu, pid_t, int);
BPF_HASH(pid_to_ts, pid_t, uint64_t);
BPF_HASH(cpu_time, int, uint64_t);
int task_switch_event(struct pt_regs *ctx, struct task_struct *prev) {
pid_t prev_pid = prev->pid;
int* prev_cpu = pid_to_cpu.lookup(&prev_pid);
uint64_t* prev_ts = pid_to_ts.lookup(&prev_pid);
pid_t cur_pid = bpf_get_current_pid_tgid();
int cur_cpu = bpf_get_smp_processor_id();
uint64_t cur_ts = bpf_ktime_get_ns();
uint64_t this_cpu_time = 0;
if (prev_ts) {
pid_to_ts.delete(&prev_pid);
this_cpu_time = (cur_ts - *prev_ts);
}
if (prev_cpu) {
pid_to_cpu.delete(&prev_pid);
if (this_cpu_time > 0) {
int cpu_key = *prev_cpu;
uint64_t* history_time = cpu_time.lookup(&cpu_key);
if (history_time)
this_cpu_time += *history_time;
cpu_time.update(&cpu_key, &this_cpu_time);
}
}
pid_to_cpu.update(&cur_pid, &cur_cpu);
pid_to_ts.update(&cur_pid, &cur_ts);
return 0;
}
)";
int main(int argc, char** argv) {
ebpf::BPF bpf;
auto init_res = bpf.init(BPF_PROGRAM);
if (std::get<0>(init_res) != 0) {
std::cerr << std::get<1>(init_res) << std::endl;
return 1;
}
auto attach_res =
bpf.attach_kprobe("finish_task_switch", "task_switch_event");
if (std::get<0>(attach_res) != 0) {
std::cerr << std::get<1>(attach_res) << std::endl;
return 1;
}
int probe_time = 10;
if (argc == 2) {
probe_time = atoi(argv[1]);
}
std::cout << "Probing for " << probe_time << " seconds" << std::endl;
sleep(probe_time);
auto table = bpf.get_hash_table<int, uint64_t>("cpu_time");
auto num_cores = sysconf(_SC_NPROCESSORS_ONLN);
for (int i = 0; i < num_cores; i++) {
std::cout << "CPU " << std::setw(2) << i << " worked for ";
std::cout << (table[i] / 1000000.0) << " ms." << std::endl;
}
auto detach_res = bpf.detach_kprobe("finish_task_switch");
if (std::get<0>(detach_res) != 0) {
std::cerr << std::get<1>(detach_res) << std::endl;
return 1;
}
return 0;
}
/*
* Copyright (c) Facebook, Inc.
* Licensed under the Apache License, Version 2.0 (the "License")
*/
#include <unistd.h>
#include <fstream>
#include <iostream>
#include <string>
#include "BPF.h"
const std::string BPF_PROGRAM = R"(
int on_sys_clone(void *ctx) {
bpf_trace_printk("Hello, World! Here I did a sys_clone call!\n");
return 0;
}
)";
int main() {
ebpf::BPF bpf;
auto init_res = bpf.init(BPF_PROGRAM);
if (std::get<0>(init_res) != 0) {
std::cerr << std::get<1>(init_res) << std::endl;
return 1;
}
std::ifstream pipe("/sys/kernel/debug/tracing/trace_pipe");
std::string line;
auto attach_res = bpf.attach_kprobe("sys_clone", "on_sys_clone");
if (std::get<0>(attach_res) != 0) {
std::cerr << std::get<1>(attach_res) << std::endl;
return 1;
}
while (true) {
if (std::getline(pipe, line)) {
std::cout << line << std::endl;
// Detach the probe if we got at least one line.
auto detach_res = bpf.detach_kprobe("sys_clone");
if (std::get<0>(detach_res) != 0) {
std::cerr << std::get<1>(detach_res) << std::endl;
return 1;
}
break;
} else {
std::cout << "Waiting for a sys_clone event" << std::endl;
sleep(1);
}
}
return 0;
}
/*
* RandomRead Monitor random number read events.
* For Linux, uses BCC, eBPF. Embedded C.
*
* Basic example of BCC Tracepoint and perf buffer.
*
* USAGE: RandomRead
*
* Copyright (c) Facebook, Inc.
* Licensed under the Apache License, Version 2.0 (the "License")
*/
#include <signal.h>
#include <iostream>
#include "BPF.h"
const std::string BPF_PROGRAM = R"(
#include <linux/sched.h>
#include <uapi/linux/ptrace.h>
struct urandom_read_args {
// See /sys/kernel/debug/tracing/events/random/urandom_read/format
uint64_t common__unused;
int got_bits;
int pool_left;
int input_left;
};
struct event_t {
int pid;
char comm[16];
int got_bits;
};
BPF_PERF_OUTPUT(events);
int on_urandom_read(struct urandom_read_args* attr) {
struct event_t event = {};
event.pid = bpf_get_current_pid_tgid();
bpf_get_current_comm(&event.comm, sizeof(event.comm));
event.got_bits = attr->got_bits;
events.perf_submit(attr, &event, sizeof(event));
return 0;
}
)";
// Define the same struct to use in user space.
struct event_t {
int pid;
char comm[16];
int got_bits;
};
void handle_output(void* cb_cookie, void* data, int data_size) {
auto event = static_cast<event_t*>(data);
std::cout << "PID: " << event->pid << " (" << event->comm << ") "
<< "Read " << event->got_bits << " bits" << std::endl;
}
ebpf::BPF* bpf;
void signal_handler(int s) {
std::cerr << "Terminating..." << std::endl;
delete bpf;
exit(0);
}
int main(int argc, char** argv) {
bpf = new ebpf::BPF();
auto init_res = bpf->init(BPF_PROGRAM);
if (std::get<0>(init_res) != 0) {
std::cerr << std::get<1>(init_res) << std::endl;
return 1;
}
auto attach_res =
bpf->attach_tracepoint("random:urandom_read", "on_urandom_read");
if (std::get<0>(attach_res) != 0) {
std::cerr << std::get<1>(attach_res) << std::endl;
return 1;
}
auto open_res = bpf->open_perf_buffer("events", &handle_output);
if (std::get<0>(open_res) != 0) {
std::cerr << std::get<1>(open_res) << std::endl;
return 1;
}
signal(SIGINT, signal_handler);
std::cout << "Started tracing, hit Ctrl-C to terminate." << std::endl;
while (true)
bpf->poll_perf_buffer("events");
return 0;
}
/*
* RecordMySQLQuery Record MySQL queries by probing the alloc_query() function
* in mysqld. For Linux, uses BCC, eBPF. Embedded C.
*
* Basic example of BCC and uprobes.
*
* Copyright (c) Facebook, Inc.
* Licensed under the Apache License, Version 2.0 (the "License")
*/
#include <unistd.h>
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <string>
#include "BPF.h"
const std::string BPF_PROGRAM = R"(
#include <linux/ptrace.h>
struct query_probe_t {
uint64_t ts;
pid_t pid;
char query[100];
};
BPF_HASH(queries, struct query_probe_t, int);
int probe_mysql_query(struct pt_regs *ctx, void* thd, char* query, size_t len) {
if (query) {
struct query_probe_t key = {};
key.ts = bpf_ktime_get_ns();
key.pid = bpf_get_current_pid_tgid();
bpf_probe_read(&key.query, sizeof(key.query), query);
int one = 1;
queries.update(&key, &one);
}
return 0;
}
)";
const std::string ALLOC_QUERY_FUNC = "_Z11alloc_queryP3THDPKcj";
// Define the same struct to use in user space.
struct query_probe_t {
uint64_t ts;
pid_t pid;
char query[100];
};
int main(int argc, char** argv) {
if (argc < 2) {
std::cout << "USAGE: RecordMySQLQuery PATH_TO_MYSQLD [duration]"
<< std::endl;
exit(1);
}
std::string mysql_path(argv[1]);
std::cout << "Using mysqld path: " << mysql_path << std::endl;
ebpf::BPF bpf;
auto init_res = bpf.init(BPF_PROGRAM);
if (std::get<0>(init_res) != 0) {
std::cerr << std::get<1>(init_res) << std::endl;
return 1;
}
auto attach_res =
bpf.attach_uprobe(mysql_path, ALLOC_QUERY_FUNC, "probe_mysql_query");
if (std::get<0>(attach_res) != 0) {
std::cerr << std::get<1>(attach_res) << std::endl;
return 1;
}
int probe_time = 10;
if (argc >= 3)
probe_time = atoi(argv[2]);
std::cout << "Probing for " << probe_time << " seconds" << std::endl;
sleep(probe_time);
auto table_handle = bpf.get_hash_table<query_probe_t, int>("queries");
auto table = table_handle.get_table_offline();
std::sort(table.begin(), table.end(), [](std::pair<query_probe_t, int> a,
std::pair<query_probe_t, int> b) {
return a.first.ts < b.first.ts;
});
std::cout << table.size() << " queries recorded:" << std::endl;
for (auto it : table) {
std::cout << "Time: " << it.first.ts << " PID: " << it.first.pid
<< " Query: " << it.first.query << std::endl;
}
auto detach_res = bpf.detach_uprobe(mysql_path, ALLOC_QUERY_FUNC);
if (std::get<0>(detach_res) != 0) {
std::cerr << std::get<1>(detach_res) << std::endl;
return 1;
}
return 0;
}
/*
* TCPSendStack Summarize tcp_sendmsg() calling stack traces.
* For Linux, uses BCC, eBPF. Embedded C.
*
* Basic example of BCC in-kernel stack trace dedup.
*
* USAGE: TCPSendStack [duration]
*
* Copyright (c) Facebook, Inc.
* Licensed under the Apache License, Version 2.0 (the "License")
*/
#include <unistd.h>
#include <algorithm>
#include <iostream>
#include "BPF.h"
const std::string BPF_PROGRAM = R"(
#include <linux/sched.h>
#include <uapi/linux/ptrace.h>
struct stack_key_t {
int pid;
char name[16];
int user_stack;
int kernel_stack;
};
BPF_STACK_TRACE(stack_traces, 10240)
BPF_HASH(counts, struct stack_key_t, uint64_t);
int on_tcp_send(struct pt_regs *ctx) {
struct stack_key_t key = {};
key.pid = bpf_get_current_pid_tgid();
bpf_get_current_comm(&key.name, sizeof(key.name));
key.kernel_stack = stack_traces.get_stackid(ctx, BPF_F_REUSE_STACKID);
key.user_stack = stack_traces.get_stackid(
ctx, BPF_F_REUSE_STACKID | BPF_F_USER_STACK
);
u64 zero = 0, *val;
val = counts.lookup_or_init(&key, &zero);
(*val)++;
return 0;
}
)";
// Define the same struct to use in user space.
struct stack_key_t {
int pid;
char name[16];
int user_stack;
int kernel_stack;
};
int main(int argc, char** argv) {
ebpf::BPF bpf;
auto init_res = bpf.init(BPF_PROGRAM);
if (std::get<0>(init_res) != 0) {
std::cerr << std::get<1>(init_res) << std::endl;
return 1;
}
auto attach_res = bpf.attach_kprobe("tcp_sendmsg", "on_tcp_send");
if (std::get<0>(attach_res) != 0) {
std::cerr << std::get<1>(attach_res) << std::endl;
return 1;
}
int probe_time = 10;
if (argc == 2) {
probe_time = atoi(argv[1]);
}
std::cout << "Probing for " << probe_time << " seconds" << std::endl;
sleep(probe_time);
auto table =
bpf.get_hash_table<stack_key_t, uint64_t>("counts").get_table_offline();
std::sort(table.begin(), table.end(), [](std::pair<stack_key_t, uint64_t> a,
std::pair<stack_key_t, uint64_t> b) {
return a.second < b.second;
});
auto stacks = bpf.get_stack_table("stack_traces");
for (auto it : table) {
std::cout << "PID: " << it.first.pid << " (" << it.first.name << ") "
<< "made " << it.second
<< " TCP sends on following stack: " << std::endl;
std::cout << " Kernel Stack:" << std::endl;
if (it.first.kernel_stack >= 0) {
auto syms = stacks.get_stack_symbol(it.first.kernel_stack, -1);
for (auto sym : syms)
std::cout << " " << sym << std::endl;
} else
std::cout << " " << it.first.kernel_stack << std::endl;
std::cout << " User Stack:" << std::endl;
if (it.first.user_stack >= 0) {
auto syms = stacks.get_stack_symbol(it.first.user_stack, it.first.pid);
for (auto sym : syms)
std::cout << " " << sym << std::endl;
} else
std::cout << " " << it.first.user_stack << std::endl;
}
auto detach_res = bpf.detach_kprobe("tcp_sendmsg");
if (std::get<0>(detach_res) != 0) {
std::cerr << std::get<1>(detach_res) << std::endl;
return 1;
}
return 0;
}
This diff is collapsed.
/*
* Copyright (c) 2016 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
*
* 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 <cctype>
#include <memory>
#include <string>
#include "BPFTable.h"
#include "bcc_syms.h"
#include "bpf_module.h"
#include "common.h"
#include "compat/linux/bpf.h"
#include "libbpf.h"
namespace ebpf {
enum class bpf_attach_type {
probe_entry,
probe_return
};
struct open_probe_t {
void* reader_ptr;
std::string func;
};
class BPF {
public:
static const int BPF_MAX_STACK_DEPTH = 127;
explicit BPF(unsigned int flag = 0) : bpf_module_(new BPFModule(flag)) {}
StatusTuple init(const std::string& bpf_program,
std::vector<std::string> cflags = {});
~BPF();
StatusTuple detach_all();
StatusTuple attach_kprobe(
const std::string& kernel_func, const std::string& probe_func,
bpf_attach_type attach_type = bpf_attach_type::probe_entry,
pid_t pid = -1, int cpu = 0, int group_fd = -1,
perf_reader_cb cb = nullptr, void* cb_cookie = nullptr);
StatusTuple detach_kprobe(
const std::string& kernel_func,
bpf_attach_type attach_type = bpf_attach_type::probe_entry);
StatusTuple attach_uprobe(
const std::string& binary_path, const std::string& symbol,
const std::string& probe_func, uint64_t symbol_addr = 0,
bpf_attach_type attach_type = bpf_attach_type::probe_entry,
pid_t pid = -1, int cpu = 0, int group_fd = -1,
perf_reader_cb cb = nullptr, void* cb_cookie = nullptr);
StatusTuple detach_uprobe(
const std::string& binary_path, const std::string& symbol,
uint64_t symbol_addr = 0,
bpf_attach_type attach_type = bpf_attach_type::probe_entry);
StatusTuple attach_tracepoint(const std::string& tracepoint,
const std::string& probe_func,
pid_t pid = -1, int cpu = 0, int group_fd = -1,
perf_reader_cb cb = nullptr,
void* cb_cookie = nullptr);
StatusTuple detach_tracepoint(const std::string& tracepoint);
template <class KeyType, class ValueType>
BPFHashTable<KeyType, ValueType> get_hash_table(const std::string& name) {
return BPFHashTable<KeyType, ValueType>(bpf_module_.get(), name);
}
BPFStackTable get_stack_table(const std::string& name) {
return BPFStackTable(bpf_module_.get(), name);
}
StatusTuple open_perf_buffer(const std::string& name, perf_reader_raw_cb cb,
void* cb_cookie = nullptr);
StatusTuple close_perf_buffer(const std::string& name);
void poll_perf_buffer(const std::string& name, int timeout = -1);
private:
StatusTuple load_func(const std::string& func_name, enum bpf_prog_type type,
int& fd);
StatusTuple unload_func(const std::string& func_name);
std::string get_kprobe_event(const std::string& kernel_func,
bpf_attach_type type);
std::string get_uprobe_event(const std::string& binary_path, uint64_t offset,
bpf_attach_type type);
StatusTuple detach_kprobe_event(const std::string& event, open_probe_t& attr);
StatusTuple detach_uprobe_event(const std::string& event, open_probe_t& attr);
StatusTuple detach_tracepoint_event(const std::string& tracepoint,
open_probe_t& attr);
std::string attach_type_debug(bpf_attach_type type) {
switch (type) {
case bpf_attach_type::probe_entry:
return "";
case bpf_attach_type::probe_return:
return "return ";
}
return "ERROR";
}
std::string attach_type_prefix(bpf_attach_type type) {
switch (type) {
case bpf_attach_type::probe_entry:
return "p";
case bpf_attach_type::probe_return:
return "r";
}
return "ERROR";
}
static bool kprobe_event_validator(char c) {
return (c != '+') && (c != '.');
}
static bool uprobe_path_validator(char c) {
return std::isalpha(c) || std::isdigit(c) || (c == '_');
}
StatusTuple check_binary_symbol(const std::string& binary_path,
const std::string& symbol,
uint64_t symbol_addr, bcc_symbol* output);
std::unique_ptr<BPFModule> bpf_module_;
std::map<std::string, int> funcs_;
std::map<std::string, open_probe_t> kprobes_;
std::map<std::string, open_probe_t> uprobes_;
std::map<std::string, open_probe_t> tracepoints_;
std::map<std::string, BPFPerfBuffer*> perf_buffers_;
};
} // namespace ebpf
/*
* Copyright (c) 2016 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
*
* 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 <errno.h>
#include <unistd.h>
#include <cstring>
#include <iostream>
#include "BPFTable.h"
#include "bcc_syms.h"
#include "common.h"
#include "exception.h"
#include "libbpf.h"
#include "perf_reader.h"
namespace ebpf {
BPFStackTable::~BPFStackTable() {
for (auto it : pid_sym_)
bcc_free_symcache(it.second, it.first);
}
std::vector<intptr_t> BPFStackTable::get_stack_addr(int stack_id) {
std::vector<intptr_t> res;
stacktrace_t stack;
if (!lookup(&stack_id, &stack))
return res;
for (int i = 0; (i < BPF_MAX_STACK_DEPTH) && (stack.ip[i] != 0); i++)
res.push_back(stack.ip[i]);
return res;
}
std::vector<std::string> BPFStackTable::get_stack_symbol(int stack_id,
int pid) {
auto addresses = get_stack_addr(stack_id);
std::vector<std::string> res;
res.reserve(addresses.size());
if (pid < 0)
pid = -1;
if (pid_sym_.find(pid) == pid_sym_.end())
pid_sym_[pid] = bcc_symcache_new(pid);
void* cache = pid_sym_[pid];
bcc_symbol symbol;
for (auto addr : addresses)
if (bcc_symcache_resolve(cache, addr, &symbol) != 0)
res.push_back("[UNKNOWN]");
else
res.push_back(symbol.demangle_name);
return res;
}
StatusTuple BPFPerfBuffer::open_on_cpu(perf_reader_raw_cb cb, int cpu,
void* cb_cookie) {
if (cpu_readers_.find(cpu) != cpu_readers_.end())
return mkstatus(-1, "Perf buffer already open on CPU %d", cpu);
auto reader =
static_cast<perf_reader*>(bpf_open_perf_buffer(cb, cb_cookie, -1, cpu));
if (reader == nullptr)
return mkstatus(-1, "Unable to construct perf reader");
int reader_fd = perf_reader_fd(reader);
if (!update(&cpu, &reader_fd)) {
perf_reader_free(static_cast<void*>(reader));
return mkstatus(-1, "Unable to open perf buffer on CPU %d: %s", cpu,
strerror(errno));
}
cpu_readers_[cpu] = static_cast<perf_reader*>(reader);
return mkstatus(0);
}
StatusTuple BPFPerfBuffer::open(perf_reader_raw_cb cb, void* cb_cookie) {
for (int i = 0; i < sysconf(_SC_NPROCESSORS_ONLN); i++)
TRY2(open_on_cpu(cb, i, cb_cookie));
return mkstatus(0);
}
StatusTuple BPFPerfBuffer::close_on_cpu(int cpu) {
auto it = cpu_readers_.find(cpu);
if (it == cpu_readers_.end())
return mkstatus(0);
perf_reader_free(static_cast<void*>(it->second));
if (!remove(const_cast<int*>(&(it->first))))
return mkstatus(-1, "Unable to close perf buffer on CPU %d", it->first);
cpu_readers_.erase(it);
return mkstatus(0);
}
StatusTuple BPFPerfBuffer::close() {
std::string errors;
bool has_error = false;
for (int i = 0; i < sysconf(_SC_NPROCESSORS_ONLN); i++) {
auto res = close_on_cpu(i);
if (std::get<0>(res) != 0) {
errors += "Failed to close CPU" + std::to_string(i) + " perf buffer: ";
errors += std::get<1>(res) + "\n";
has_error = true;
}
}
if (has_error)
return mkstatus(-1, errors);
return mkstatus(0);
}
void BPFPerfBuffer::poll(int timeout) {
perf_reader* readers[cpu_readers_.size()];
int i = 0;
for (auto it : cpu_readers_)
readers[i++] = it.second;
perf_reader_poll(cpu_readers_.size(), readers, timeout);
}
BPFPerfBuffer::~BPFPerfBuffer() {
auto res = close();
if (std::get<0>(res) != 0)
std::cerr << "Failed to close all perf buffer on destruction: "
<< std::get<1>(res) << std::endl;
}
} // namespace ebpf
/*
* Copyright (c) 2016 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
*
* 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 <exception>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "bpf_module.h"
#include "common.h"
#include "libbpf.h"
#include "perf_reader.h"
namespace ebpf {
template <class KeyType, class ValueType>
class BPFTableBase {
public:
size_t capacity() { return capacity_; }
protected:
BPFTableBase(BPFModule* bpf_module, const std::string& name) {
size_t id_ = bpf_module->table_id(name);
if (id_ >= bpf_module->num_tables())
throw std::invalid_argument("Table " + name + " does not exist");
fd_ = bpf_module->table_fd(id_);
capacity_ = bpf_module->table_max_entries(id_);
};
bool lookup(KeyType* key, ValueType* value) {
return bpf_lookup_elem(fd_, static_cast<void*>(key),
static_cast<void*>(value)) >= 0;
}
bool next(KeyType* key, KeyType* next_key) {
return bpf_get_next_key(fd_, static_cast<void*>(key),
static_cast<void*>(next_key)) >= 0;
}
bool update(KeyType* key, ValueType* value) {
return bpf_update_elem(fd_, static_cast<void*>(key),
static_cast<void*>(value), 0) >= 0;
}
bool remove(KeyType* key) {
return bpf_delete_elem(fd_, static_cast<void*>(key)) >= 0;
}
int fd_;
size_t capacity_;
};
template <class KeyType, class ValueType>
class BPFHashTable : protected BPFTableBase<KeyType, ValueType> {
public:
BPFHashTable(BPFModule* bpf_module, const std::string& name)
: BPFTableBase<KeyType, ValueType>(bpf_module, name) {}
ValueType get_value(const KeyType& key) {
ValueType res;
if (!this->lookup(const_cast<KeyType*>(&key), &res))
throw std::invalid_argument("Key does not exist in the table");
return res;
}
ValueType operator[](const KeyType& key) { return get_value(key); }
std::vector<std::pair<KeyType, ValueType>> get_table_offline() {
std::vector<std::pair<KeyType, ValueType>> res;
KeyType cur, nxt;
ValueType value;
while (true) {
if (!this->next(&cur, &nxt))
break;
if (!this->lookup(&nxt, &value))
break;
res.emplace_back(nxt, value);
std::swap(cur, nxt);
}
return res;
}
};
// From src/cc/export/helpers.h
static const int BPF_MAX_STACK_DEPTH = 127;
struct stacktrace_t {
intptr_t ip[BPF_MAX_STACK_DEPTH];
};
class BPFStackTable : protected BPFTableBase<int, stacktrace_t> {
public:
BPFStackTable(BPFModule* bpf_module, const std::string& name)
: BPFTableBase<int, stacktrace_t>(bpf_module, name) {}
~BPFStackTable();
std::vector<intptr_t> get_stack_addr(int stack_id);
std::vector<std::string> get_stack_symbol(int stack_id, int pid);
private:
std::map<int, void*> pid_sym_;
};
class BPFPerfBuffer : protected BPFTableBase<int, int> {
public:
BPFPerfBuffer(BPFModule* bpf_module, const std::string& name)
: BPFTableBase<int, int>(bpf_module, name) {}
~BPFPerfBuffer();
StatusTuple open(perf_reader_raw_cb cb, void* cb_cookie);
StatusTuple close();
void poll(int timeout);
private:
StatusTuple open_on_cpu(perf_reader_raw_cb cb, int cpu, void* cb_cookie);
StatusTuple close_on_cpu(int cpu);
std::map<int, perf_reader*> cpu_readers_;
};
} // namespace ebpf
......@@ -35,12 +35,12 @@ if (CMAKE_COMPILER_IS_GNUCC AND LIBCLANG_ISSTATIC)
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)
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 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 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)
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 BPF.cc BPFTable.cc)
set_target_properties(bcc-static PROPERTIES OUTPUT_NAME bcc)
set(llvm_raw_libs bitwriter bpfcodegen irreader linker
......@@ -63,7 +63,7 @@ target_link_libraries(bcc-static b_frontend clang_frontend bcc-loader-static ${c
install(TARGETS bcc-shared LIBRARY COMPONENT libbcc
DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(FILES bpf_common.h bpf_module.h bcc_syms.h libbpf.h perf_reader.h COMPONENT libbcc
install(FILES bpf_common.h bpf_module.h bcc_syms.h common.h exception.h libbpf.h perf_reader.h BPF.h BPFTable.h COMPONENT libbcc
DESTINATION include/bcc)
install(DIRECTORY compat/linux/ COMPONENT libbcc
DESTINATION include/bcc/compat/linux
......
......@@ -37,6 +37,12 @@ static inline std::tuple<int, std::string> mkstatus(int ret, const char *msg) {
return std::make_tuple(ret, std::string(msg));
}
static inline std::tuple<int, std::string> mkstatus(
int ret, const std::string& msg
) {
return std::make_tuple(ret, msg);
}
static inline std::tuple<int, std::string> mkstatus(int ret) {
return std::make_tuple(ret, std::string());
}
......
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