Commit 284dd8e8 authored by Teng Qin's avatar Teng Qin

Examples of using BCC C++ API

parent 335cbe4f
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;
}
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