Commit 85b702b7 authored by 4ast's avatar 4ast Committed by GitHub

Merge pull request #849 from palmtenor/cppperfevent

Expose perf event support in C++ API
parents 1e5971d5 a4feb694
...@@ -22,3 +22,7 @@ install (TARGETS TCPSendStack DESTINATION share/bcc/examples/cpp) ...@@ -22,3 +22,7 @@ install (TARGETS TCPSendStack DESTINATION share/bcc/examples/cpp)
add_executable(RandomRead RandomRead.cc) add_executable(RandomRead RandomRead.cc)
target_link_libraries(RandomRead bcc-static) target_link_libraries(RandomRead bcc-static)
install (TARGETS RandomRead DESTINATION share/bcc/examples/cpp) install (TARGETS RandomRead DESTINATION share/bcc/examples/cpp)
add_executable(LLCStat LLCStat.cc)
target_link_libraries(LLCStat bcc-static)
install (TARGETS LLCStat DESTINATION share/bcc/examples/cpp)
/*
* LLCStat Show LLC hit ratio for each process on each CPU core.
* For Linux, uses BCC, eBPF. Embedded C.
*
* Basic example of BCC timed sampling perf event.
*
* USAGE: LLCStat [duration]
*
* Copyright (c) Facebook, Inc.
* Licensed under the Apache License, Version 2.0 (the "License")
*/
#include <linux/perf_event.h>
#include <unistd.h>
#include <iomanip>
#include <iostream>
#include <string>
#include "BPF.h"
const std::string BPF_PROGRAM = R"(
#include <linux/ptrace.h>
#include <uapi/linux/bpf_perf_event.h>
struct event_t {
int cpu;
int pid;
char name[16];
};
BPF_HASH(ref_count, struct event_t);
BPF_HASH(miss_count, struct event_t);
static inline __attribute__((always_inline)) void get_key(struct event_t* key) {
key->cpu = bpf_get_smp_processor_id();
key->pid = bpf_get_current_pid_tgid();
bpf_get_current_comm(&(key->name), sizeof(key->name));
}
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);
(*val) += ctx->sample_period;
return 0;
}
int on_cache_ref(struct bpf_perf_event_data *ctx) {
struct event_t key = {};
get_key(&key);
u64 zero = 0, *val;
val = ref_count.lookup_or_init(&key, &zero);
(*val) += ctx->sample_period;
return 0;
}
)";
struct event_t {
int cpu;
int pid;
char name[16];
};
int main(int argc, char** argv) {
ebpf::BPF bpf;
auto init_res = bpf.init(BPF_PROGRAM);
if (init_res.code() != 0) {
std::cerr << init_res.msg() << std::endl;
return 1;
}
auto attach_ref_res =
bpf.attach_perf_event(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES,
"on_cache_ref", 100, 0);
if (attach_ref_res.code() != 0) {
std::cerr << attach_ref_res.msg() << std::endl;
return 1;
}
auto attach_miss_res = bpf.attach_perf_event(
PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES, "on_cache_miss", 100, 0);
if (attach_miss_res.code() != 0) {
std::cerr << attach_miss_res.msg() << 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);
bpf.detach_perf_event(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES);
bpf.detach_perf_event(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES);
auto refs = bpf.get_hash_table<event_t, uint64_t>("ref_count");
auto misses = bpf.get_hash_table<event_t, uint64_t>("miss_count");
for (auto it : refs.get_table_offline()) {
uint64_t hit;
try {
auto miss = misses[it.first];
hit = miss <= it.second ? it.second - miss : 0;
} catch (...) {
hit = it.second;
}
double ratio = (double(hit) / double(it.second)) * 100.0;
std::cout << "PID " << std::setw(8) << std::setfill(' ') << it.first.pid;
std::cout << std::setw(20) << std::setfill(' ') << std::left
<< " (" + std::string(it.first.name) + ") " << std::right;
std::cout << "on CPU " << std::setw(2) << std::setfill(' ') << it.first.cpu;
std::cout << " Hit Rate " << std::setprecision(4) << ratio << "% ";
std::cout << "(" << hit << "/" << it.second << ")" << std::endl;
}
return 0;
}
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <sstream> #include <sstream>
#include <utility>
#include <vector> #include <vector>
#include "bcc_exception.h" #include "bcc_exception.h"
...@@ -107,11 +108,19 @@ StatusTuple BPF::detach_all() { ...@@ -107,11 +108,19 @@ StatusTuple BPF::detach_all() {
delete it.second; delete it.second;
} }
for (auto it : perf_events_) {
auto res = detach_perf_event_all_cpu(it.second);
if (res.code() != 0) {
error_msg += res.msg() + "\n";
has_error = true;
}
}
for (auto it : funcs_) { for (auto it : funcs_) {
int res = close(it.second); int res = close(it.second);
if (res != 0) { if (res != 0) {
error_msg += "Failed to unload BPF program for " + it.first + ": "; error_msg += "Failed to unload BPF program for " + it.first + ": ";
error_msg += std::string(std::strerror(res)) + "\n"; error_msg += std::string(std::strerror(errno)) + "\n";
has_error = true; has_error = true;
} }
} }
...@@ -231,6 +240,44 @@ StatusTuple BPF::attach_tracepoint(const std::string& tracepoint, ...@@ -231,6 +240,44 @@ StatusTuple BPF::attach_tracepoint(const std::string& tracepoint,
return StatusTuple(0); return StatusTuple(0);
} }
StatusTuple BPF::attach_perf_event(uint32_t ev_type, uint32_t ev_config,
const std::string& probe_func,
uint64_t sample_period, uint64_t sample_freq,
pid_t pid, int cpu, int group_fd) {
auto ev_pair = std::make_pair(ev_type, ev_config);
if (perf_events_.find(ev_pair) != perf_events_.end())
return StatusTuple(-1, "Perf event type %d config %d already attached",
ev_type, ev_config);
int probe_fd;
TRY2(load_func(probe_func, BPF_PROG_TYPE_PERF_EVENT, probe_fd));
auto fds = new std::map<int, int>();
int cpu_st = 0;
int cpu_en = sysconf(_SC_NPROCESSORS_ONLN) - 1;
if (cpu >= 0)
cpu_st = cpu_en = cpu;
for (int i = cpu_st; i <= cpu_en; i++) {
int fd = bpf_attach_perf_event(probe_fd, ev_type, ev_config, sample_period,
sample_freq, pid, i, group_fd);
if (fd < 0) {
for (auto it : *fds)
close(it.second);
delete fds;
TRY2(unload_func(probe_func));
return StatusTuple(-1, "Failed to attach perf event type %d config %d",
ev_type, ev_config);
}
fds->emplace(i, fd);
}
open_probe_t p = {};
p.func = probe_func;
p.per_cpu_fd = fds;
perf_events_[ev_pair] = std::move(p);
return StatusTuple(0);
}
StatusTuple BPF::detach_kprobe(const std::string& kernel_func, StatusTuple BPF::detach_kprobe(const std::string& kernel_func,
bpf_attach_type attach_type) { bpf_attach_type attach_type) {
std::string event = get_kprobe_event(kernel_func, attach_type); std::string event = get_kprobe_event(kernel_func, attach_type);
...@@ -274,6 +321,16 @@ StatusTuple BPF::detach_tracepoint(const std::string& tracepoint) { ...@@ -274,6 +321,16 @@ StatusTuple BPF::detach_tracepoint(const std::string& tracepoint) {
return StatusTuple(0); return StatusTuple(0);
} }
StatusTuple BPF::detach_perf_event(uint32_t ev_type, uint32_t ev_config) {
auto it = perf_events_.find(std::make_pair(ev_type, ev_config));
if (it == perf_events_.end())
return StatusTuple(-1, "Perf Event type %d config %d not attached",
ev_type, ev_config);
TRY2(detach_perf_event_all_cpu(it->second));
perf_events_.erase(it);
return StatusTuple(0);
}
StatusTuple BPF::open_perf_buffer(const std::string& name, StatusTuple BPF::open_perf_buffer(const std::string& name,
perf_reader_raw_cb cb, void* cb_cookie) { perf_reader_raw_cb cb, void* cb_cookie) {
if (perf_buffers_.find(name) == perf_buffers_.end()) if (perf_buffers_.find(name) == perf_buffers_.end())
...@@ -401,4 +458,24 @@ StatusTuple BPF::detach_tracepoint_event(const std::string& tracepoint, ...@@ -401,4 +458,24 @@ StatusTuple BPF::detach_tracepoint_event(const std::string& tracepoint,
return StatusTuple(0); return StatusTuple(0);
} }
StatusTuple BPF::detach_perf_event_all_cpu(open_probe_t& attr) {
bool has_error = false;
std::string err_msg;
for (auto it : *attr.per_cpu_fd) {
int res = close(it.second);
if (res < 0) {
has_error = true;
err_msg += "Failed to close perf event FD " + std::to_string(it.second) +
" For CPU " + std::to_string(it.first) + ": ";
err_msg += std::string(std::strerror(errno)) + "\n";
}
}
delete attr.per_cpu_fd;
TRY2(unload_func(attr.func));
if (has_error)
return StatusTuple(-1, err_msg);
return StatusTuple(0);
}
} // namespace ebpf } // namespace ebpf
...@@ -37,6 +37,7 @@ enum class bpf_attach_type { ...@@ -37,6 +37,7 @@ enum class bpf_attach_type {
struct open_probe_t { struct open_probe_t {
void* reader_ptr; void* reader_ptr;
std::string func; std::string func;
std::map<int, int>* per_cpu_fd;
}; };
class BPF { class BPF {
...@@ -77,6 +78,13 @@ public: ...@@ -77,6 +78,13 @@ public:
void* cb_cookie = nullptr); void* cb_cookie = nullptr);
StatusTuple detach_tracepoint(const std::string& tracepoint); StatusTuple detach_tracepoint(const std::string& tracepoint);
StatusTuple attach_perf_event(uint32_t ev_type, uint32_t ev_config,
const std::string& probe_func,
uint64_t sample_period, uint64_t sample_freq,
pid_t pid = -1, int cpu = -1,
int group_fd = -1);
StatusTuple detach_perf_event(uint32_t ev_type, uint32_t ev_config);
template <class KeyType, class ValueType> template <class KeyType, class ValueType>
BPFHashTable<KeyType, ValueType> get_hash_table(const std::string& name) { BPFHashTable<KeyType, ValueType> get_hash_table(const std::string& name) {
return BPFHashTable<KeyType, ValueType>(bpf_module_.get(), name); return BPFHashTable<KeyType, ValueType>(bpf_module_.get(), name);
...@@ -105,6 +113,7 @@ private: ...@@ -105,6 +113,7 @@ private:
StatusTuple detach_uprobe_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, StatusTuple detach_tracepoint_event(const std::string& tracepoint,
open_probe_t& attr); open_probe_t& attr);
StatusTuple detach_perf_event_all_cpu(open_probe_t& attr);
std::string attach_type_debug(bpf_attach_type type) { std::string attach_type_debug(bpf_attach_type type) {
switch (type) { switch (type) {
...@@ -146,6 +155,7 @@ private: ...@@ -146,6 +155,7 @@ private:
std::map<std::string, open_probe_t> uprobes_; std::map<std::string, open_probe_t> uprobes_;
std::map<std::string, open_probe_t> tracepoints_; std::map<std::string, open_probe_t> tracepoints_;
std::map<std::string, BPFPerfBuffer*> perf_buffers_; std::map<std::string, BPFPerfBuffer*> perf_buffers_;
std::map<std::pair<uint32_t, uint32_t>, open_probe_t> perf_events_;
}; };
} // namespace ebpf } // namespace ebpf
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