Linux bpf 2.1、bcc的實現
bcc全稱為(BPF Compiler Collection),它是模仿gcc(GNU Compiler Collection)的命名風格。
BPF是執行在核心態的一種虛擬機器語言,我們在使用者態可以通過Clang+LLVM把c語言編譯成BPF目標碼,然後通過載入器loader(bcc/perf/iproute2)將BPF目標碼通過bpf()系統呼叫載入到核心當中,最後通過perf的ioctl命令PERF_EVENT_IOC_SET_BPF將載入到核心中的BPF程式和對應子模組(tracing/networking)的鉤子繫結起來。(具體參考3.2、BPF and XDP Reference Guide
bcc把上述使用者態編譯、載入、繫結的功能都集成了起來,方便使用者使用,對使用者的介面更友好。它使用了(python + lua + c++)的混合架構,底層操作封裝到c++ 庫中,lua提供一些輔助功能,對使用者的介面使用python提供,python和c++之間的呼叫使用ctypes連線。因為使用了python,所有抓回來的資料分析和資料呈現也都非常方便。
有了bcc以後使用者就不需要一步步手工的寫c程式碼、編譯、載入、繫結、資料分析、資料呈現,只要按照bcc的規則編寫一個python檔案,bcc幫你一鍵搞定。
1、背景介紹
說到bcc,就不得不提到Brendan Gregg,他是perfermance屆的大神。他開發了很多perf相關的工具和指令碼:
2、bcc安裝
首先確保你的核心版本是4.1或者以上的版本(推薦4.9以上),並且打開了以下配置:(檢視/proc/config.gz or /boot/config-)
CONFIG_BPF=y CONFIG_BPF_SYSCALL=y # [optional, for tc filters] CONFIG_NET_CLS_BPF=m # [optional, for tc actions] CONFIG_NET_ACT_BPF=m CONFIG_BPF_JIT=y CONFIG_HAVE_BPF_JIT=y # [optional, for kprobes] CONFIG_BPF_EVENTS=y
如果執行bcc網路示例需要開啟一些可選的核心選項:
CONFIG_NET_SCH_SFQ=m
CONFIG_NET_ACT_POLICE=m
CONFIG_NET_ACT_GACT=m
CONFIG_DUMMY=m
CONFIG_VXLAN=m
在ubuntu環境下,我們可以使用以下簡單的命令來安裝bcc工具:
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4052245BD4284CDD
echo "deb https://repo.iovisor.org/apt/xenial xenial main" | sudo tee /etc/apt/sources.list.d/iovisor.list
sudo apt-get update
sudo apt-get install bcc-tools libbcc-examples linux-headers-$(uname -r)
3、bcc的使用入門
安裝完bcc以後,我們可以進入"/usr/share/bcc/tools"和"/usr/share/bcc/examples/tracing"路徑下執行bcc已經提供的效能分析命令,關於命令的基本使用可以參考上面的入門指導。
/usr/share/bcc/tools$ ls
argdist doc mdflush pythonflow tcpconnlat
bashreadline execsnoop memleak pythongc tcpdrop
biolatency ext4dist mountsnoop pythonstat tcplife
biosnoop ext4slower mysqld_qslower reset-trace tcpretrans
biotop filelife nfsdist rubycalls tcpstates
bitesize fileslower nfsslower rubyflow tcpsubnet
bpflist filetop nodegc rubygc tcptop
btrfsdist funccount nodestat rubyobjnew tcptracer
btrfsslower funclatency offcputime rubystat tplist
cachestat funcslower offwaketime runqlat trace
cachetop gethostlatency old runqlen ttysnoop
capable hardirqs oomkill runqslower vfscount
cobjnew inject opensnoop slabratetop vfsstat
cpudist javacalls perlcalls softirqs wakeuptime
cpuunclaimed javaflow perlflow solisten xfsdist
criticalstat javagc perlstat sslsniff xfsslower
dbslower javaobjnew phpcalls stackcount zfsdist
dbstat javastat phpflow statsnoop zfsslower
dcsnoop javathreads phpstat syncsnoop
dcstat killsnoop pidpersec syscount
deadlock_detector lib profile tcpaccept
deadlock_detector.c llcstat pythoncalls tcpconnect
/usr/share/bcc/examples/tracing$ ls
bitehist_example.txt strlen_count.py
bitehist.py strlen_hist.py
CMakeLists.txt strlen_snoop.py
disksnoop_example.txt sync_timing.py
disksnoop.py task_switch.py
hello_fields.py tcpv4connect_example.txt
hello_perf_output.py tcpv4connect.py
kvm_hypercall.py trace_fields.py
kvm_hypercall.txt trace_perf_output.py
mallocstacks.py urandomread_example.txt
mysqld_query_example.txt urandomread-explicit.py
mysqld_query.py urandomread.py
nodejs_http_server_example.txt vfsreadlat.c
nodejs_http_server.py vfsreadlat_example.txt
stacksnoop_example.txt vfsreadlat.py
stacksnoop.py
如果bcc提供的專用命令不能滿足你,bcc還提供了幾個通用自定義命令:trace、argdist、funccount。可以詳細看一下這幾個命令的用法。
4、bcc python指令碼的編寫
如果bcc自帶指令碼不能滿足你,你可以仿照bcc的語法規則自己開發python指令碼,自定義自己要採集的資料,自定義自己的資料處理和呈現規則。因為是python介面,我們可以進行二次開發把資料進行更詳盡的分析、用圖形呈現等等。
5、bcc原始碼結構
我們也可以從github上下載bcc的原始碼進行分析和除錯。
我們在配置pycharm工程的時候需要注意:
我們檢視cc的python指令碼,核心部分是BPF python庫:
bcc/examples$ cat hello_world.py
#!/usr/bin/env python
# Copyright (c) PLUMgrid, Inc.
# Licensed under the Apache License, Version 2.0 (the "License")
# run in project examples directory with:
# sudo ./hello_world.py"
# see trace_fields.py for a longer example
from bcc import BPF
# This may not work for 4.17 on x64, you need replace kprobe__sys_clone with kprobe____x64_sys_clone
BPF(text='int kprobe__sys_clone(void *ctx) { bpf_trace_printk("Hello, World!\\n"); return 0; }').trace_print()
BPF python庫是在路徑bcc/src/python/bcc/中實現的,在libbcc.py中通過ctypes匯入了libbcc.so.0:
bcc/src/python/bcc$ cat libbcc.py
# Copyright 2015 PLUMgrid
#
# 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.
import ctypes as ct
lib = ct.CDLL("libbcc.so.0", use_errno=True)
# keep in sync with bpf_common.h
lib.bpf_module_create_b.restype = ct.c_void_p
lib.bpf_module_create_b.argtypes = [ct.c_char_p, ct.c_char_p, ct.c_uint]
lib.bpf_module_create_c.restype = ct.c_void_p
libbcc.so.0是c++的底層實現,原始碼在bcc/src/cc路徑下。
本來是想寫一篇bcc核心程式碼分析的文章,後來發現整個程式碼規模太大,還是分析個大致框架有問題再追蹤修改吧。