simple eBPF example(include makefile used kernel)...

This commit is contained in:
gatieme
2017-11-12 14:29:46 +08:00
parent 61b0de84a9
commit 7a60140ef7
16 changed files with 2514 additions and 0 deletions
@@ -0,0 +1,258 @@
# learn-bpf
This program has been tested with 4.12.5-200.fc25.x86_64.
memcpy_kprobe inserts kprobe at the entry of kernel memcpy() and prints bpf
kernel messages in trace buffer. memcpy_stat prepares a table in kernel
space itself for the count of memcpy() called with different sizes.
# make
# ./memcpy_kprobe
memcpy_kprobe-24908 [005] d... 151374.866218: : memcpy size 2
memcpy_kprobe-24908 [005] d... 151374.866221: : memcpy size 13
memcpy_kprobe-24908 [005] d... 151374.866224: : memcpy size 1
memcpy_kprobe-24908 [005] d... 151374.866226: : memcpy size 2
memcpy_kprobe-24908 [005] d... 151374.866229: : memcpy size 2
memcpy_kprobe-24908 [005] d... 151374.866232: : memcpy size 1
memcpy_kprobe-24908 [005] d... 151374.866234: : memcpy size 1
memcpy_kprobe-24908 [005] d... 151374.866237: : memcpy size 2
# ./memcpy_stat
Size Count
0 134
1 - 64 10444
65 - 128 23
129 - 192 16
193 - 256 240
257 - 320 0
321 - 384 0
385 - 448 7
449 - 512 7
513 - 576 0
577 - 640 0
641 - 704 0
705 - 768 0
769 - 832 0
833 - 896 0
897 - 960 0
961 - 1024* 107
* Size > 1024 have been counted in this interval
eBPF history and program description
====================================
Writing a simple eBPF application for Kernel Tracing
=====================================================
eBPF an introduction
======================
* BPF machine
In the year 1992, Steven McCanne and Van Jacobson from Lawrence Berkeley
Laboratory [1] proposed a solution to BSD Unix systems for minimising
unwanted network packet copy to user space by implementing an in-kernel
packet filter. This filter is known as Berkeley Packet Filter(BPF). It was
latter introduced in Linux Kernel version 2.1.75 in 1997.
This filter was aimed to filter all the unwanted packets as early as
possible, so the filtering mechanism had to be shifted from user space
utilities like tcpdump to the in-kernel virtual machine. A group of
assembly like instructions for filtering necessary packet are sent from
user space to kernel by a system call bpf(). Kernel statically analyzes
the programs before loading them and makes sure that they cannot hang or
harm a running system.
The BPF machine abstraction consists of [1] an accumulator, an index
register ( x ), a scratch memory store, and an implicit program counter.
It has a small set of arithmetic, logical, and jump instructions. The
accumulator is used for arithmetic operations, while the index register
provides offsets into the packet or into the scratch memory areas. Lets see
the example of a small BPF program written in BPF bytecode:
ldh [12]
jeq #ETHERTYPE_IP, l1, l2
l1: ret #TRUE
l2: ret #0
ldh instruction loads a half word (16 bit) value in accumulator from offset
12 in ethernet packet which is ethernet type field. If it is not an IP
packet then 0 will be returned and so the packet would be rejected.
* BPF JIT compiler
A just in time (JIT) compiler was introduced into kernel [2] in 2011 to
speed up BPF bytecode execution. This compiler translates BPF bytecode into
host systems assembly code. Such compiler exists for x86_64, SPARC,
PowerPC, ARM, ARM64, MIPS and s390 and can be enabled through
CONFIG_BPF_JIT.
* eBPF machine
Extended BPF (eBPF) is an enhancement over BPF (which is now called as cBPF
i.e. classical BPF) having more resources like 10 registers and 1-8 byte
load/store instructions etc [3]. While BPF was having only forward jump,
eBPF has both backward as well as forward jump, and so we can have a loop.
Of Course kernel takes care that loop still terminates properly. It also
includes global data store which is called maps, and this maps state
persist between events, therefore eBPF can be also used for aggregating
statistics of events. Further, an eBPF program can be written in C like
functions, which can be compiled using GCC/LLVM compiler. eBPF has been
designed to be JITed with one to one mapping, so a very optimized code
having performance as fast as natively compiled code can be generated.
eBPF and tracing review
==========================
* Upstream kernel development
Traditional built-in tracers in Linux are used in post-process manner,
where they would dump fixed event details and then user space tools like
perf or trace-cmd can post process to get required information e.g `perf
stat`. However, eBPF has ability to prepare user information in kernel
context, and only transfer needed information to user space. So far support
of kprobes, tracepoints and perf_events filtering using eBPF have been
implemented in upstream kernel. They have been supported with arch x86-64,
aarch64, s390x, powerpc64 and sparc64.
One can look into following Linux kernel files to get an insight of it.
- kernel/bpf/
- kernel/trace/bpf_trace.c
- kernel/events/core.c
* User space development
There have been user space tools development for in-kernel tree as well as
out of the kernel tree. Following files/directories are good to look into
upstream kernel for ebpf usage.
- tools/lib/bpf
- tools/perf/util/bpf-loader.c
- samples/bpf/
bcc( https://github.com/iovisor/bcc.git) is another out of kernel tree tool
which has very efficient kernel tracing programs for specific usage(like
`funccount` which counts functions matching a pattern).
Perf has also a bpf interface which can be used to load eBPF object
into kernel.
eBPF tracing: User space to kernel space flow
===============================================
Lets first understand couple of useful entity to interact with ebpf kernel:
* BPF system call
user can interact using bpf() system call whose prototype is int bpf(int
cmd, union bpf_attr *attr, unsigned int size);
One can see `man bpf` for detail about the different possible arguments.
Here, I am providing summary of those arguments.
*cmd* can be any of the defined enum bpf_cmd, which tells kernel
mainly about management of map area (like its creation, updating,
deleting or finding an element within it etc), attaching or detaching
a program etc.
*attr* can be one of the user defined structure which can be used
by respective command.
*size* will be the size of attr.
* BPF Maps:
eBPF tracing calculates the stats in kernel domain itself. We will need
some memory/data structure within the kernel to create such stats. Maps
are a generic data structure for storage of different types of data in the
form of key-value pair. They allow sharing of data between eBPF kernel
programs, and also between kernel and user-space applications.
Couple of important attributes for maps:
- Type (map_type)
- maximum number of elements (max_entries)
- key size in bytes (key_size)
- value size in bytes (value_size)
A map can be of different types like Hash, Array, Program array etc. We
need to choose appropriate type as per our needs. For example if key is a
string or not from an integer series then hash map can be used for faster
look up, however if key is like an index then array map will provide the
fastest look up method.
We can not have a key bigger than key_size and can not store a value bigger
than value_size. max_entries is the maximum number of key-value pair which
can be stored within map.
* Some important command:
- BPF_PROG_LOAD: Couple of important attributes for this program.
prog_type : some of the program type useful for tracing are
BPF_PROG_TYPE_KPROBE
BPF_PROG_TYPE_TRACEPOINT,
BPF_PROG_TYPE_PERF_EVENT,
insns: is pointer to “struct bpf_insn” which has bpf instruction to
be executed by in-kernel bpf VM.
insn_cnt: total number of instructions present at insns.
license:string, which must be GPL compatible to call helper functions
marked gpl_only.
kern_version: version of kernel tree
- BPF_MAP_CREATE: It accepts attributes as discussed in BPF Maps section,
creates a new map and then returns a new file descriptor that refers to the
map. Returned map_fd can be used for lookup or update map elements with
commands like BPF_MAP_LOOKUP_ELEM, BPF_MAP_UPDATE_ELEM, BPF_MAP_DELETE_ELEM
or BPF_MAP_GET_NEXT_KEY. These map manipulation command accepts an
attribute with map_fd, key and value.
Now lets lookup some code which can explain its working. See the exmaple
code here: https://github.com/pratyushanand/learn-bpf
Above code is a standalone ebpf demo code which does not need any other
ebpf library code. It has a small library to load different sections of bpf
kernel code (bpf_load.c) and then some wrapper function on top of bpf()
system call (bpf.c) to manipulate map and load kernel bpf code. When we
compile this code we get two executable, memcpy_kprobe and memcpy_stat.
Lets first see what memcpy_kprobe* files do.
For each application we have one *_kern file and another *_user file.
*_kern file has a function “int bpf_prog1(struct pt_regs *ctx)”. This
function is executed in kernel, so it can access kernel variable and
functions. memcpy_kprobe_kern.c has three section mappings for program,
license and version respectively. Data from these sections are made part of
attributes of system call bpf(BPF_PROG_LOAD,...) and then kernel executes
loaded bpf instructions as per prog_type attribute. So, bpf code in
memcpy_kprobe_kern.c will be executed when a kprobe instrumented at the
entry of kernel memcpy() is hit. When this bpf code is executed, it will
read 3rd argument of memcpy() ie size of copy and then will print one
statement for “memcpy size” in trace buffer. memcpy_kprobe_user.c loads
kernel program and keeps on reading trace buffer to show what kernel ebpf
program is writing into it.
We have another demo memcpy_stat which prepares stats of memcpy() copy size
in kernel itself. memcpy_stat_kern.c has one more section as maps.
bpf_prog1() reads memcpy() sizes and updates map table. Corresponding user
space program memcpy_stat_user.c reads map table at every 2 second and
prints stats on console.
Above two simple example can help one to understand, how a user can write
kernel ebpf code for kernel tracing and statistics preparation.
[1] http://www.tcpdump.org/papers/bpf-usenix93.pdf
[2] https://lwn.net/Articles/437884/
[3] https://www.kernel.org/doc/Documentation/networking/filter.txt
[4] http://events.linuxfoundation.org/sites/events/files/slides/Performance%20Monitoring%20and%20Analysis%20Using%20perf%20and%20BPF_1.pdf