一、BCC简介

1、BCC简介

BCC是一个Python库,简化了eBPF应用的开发过程,并收集了大量性能分析相关的eBPF应用。BCC为BPF开发提供了不同的前端支持,包括Python和Lua,实现了map创建、代码编译、解析、注入等操作,使开发人员只需聚焦于用C语言开发要注入的内核代码。

BCC工具集大部分工具需要Linux Kernel 4.1以上版本支持,完整工具支持需要Linux Kernel 4.15以上版本支持。

GitHub:https://github.com/iovisor/bcc

2、BCC安装


  1. yum install bcc-tools
  2. export PATH=$PATH:/usr/share/bcc/tools
  • 常用命令工具

1、opensnoop

opensnoop通过追踪open()系统调用显示企图打开文件的进程,可以用于定位配置文件或日志文件,或排除启动失败的故障应用。

opensnoop通过动态追踪sys_open()内核函数并更新函数的任何变化,opensnoop需要Linux Kernel 4.5版本支持,由于使用BPF,因此需要root权限。

opensnoop [-h] [-T] [-U] [-x] [-p PID] [-t TID] [-u UID] [-d DURATION] [-n NAME] [-e] [-f FLAG_FILTER]

-h, --help:帮助信息查看

-T, --timestamp:输出结果打印时间戳

-U, --print-uid:打印UID

-x, --failed:只显示失败open系统调用

-p PID, --pid PID:只追踪PID进程

-t TID, --tid TID:只追踪TID线程

-u UID, --uid UID:只追踪UID

-d DURATION, --duration DURATION:追踪时间,单位为秒

-n NAME, --name NAME:只打印包含name的进程

-e, --extended_fields:显示扩展字段

-f FLAG_FILTER, --flag_filter FLAG_FILTER:指定过滤字段,如O_WRONLY

2、execsnoop

execsnoop通过追踪exec系统调用追踪新进程,对于使用fork而不是exec产生的进程不会包括在显示结果中。

execsnoop需要BPF支持,因此需要root权限。

execsnoop [-h] [-T] [-t] [-x] [-q] [-n NAME] [-l LINE] [--max-args MAX_ARGS]

-h:查看帮助信息

-T:打印时间戳,格式HH:MM:SS

-t:打印时间戳

-x:包括失败exec

-n NAME:只打印正则表达式匹配name的命令行

-l LINE:只打印参数中匹配LINE的命令行

--max-args MAXARGS:解析和显示最大参数数量,默认为20个

3、biolatency

biolatency通过追踪块设备IO,记录IO延迟分布,并以直方图显示。biolatency通过动态追踪blk_族函数并记录函数的变化。

biolatency需要BPF支持,因此需要root权限。

biolatency [-h] [-F] [-T] [-Q] [-m] [-D] [interval [count]]

-h Print usage message.

-T:输出包含时间戳

-m:输出ms级直方图

-D:打印每个磁盘设备的直方图

-F:打印每个IO集的直方图

interval:输出间隔

count:输出数量

4、ext4slower

ext4slower通过跟踪ext4文件系统的read、write、open、sync操作,并测量相应操作所耗时间,打印超过阈值的详细信息。默认阈值最小值是10ms,如果阈值为0,则打印所有事件。

ext4slower需要BPF支持,因此需要root权限。

ext4slower可以通过文件系统识别独立较慢的磁盘IO。

ext4slower [-h] [-j] [-p PID] [min_ms]

-h, --help:查看帮助信息

-j, --csv:使用csv格式打印字段

-p PID, --pid PID:只追踪PID进程

min_ms:追踪IO的阈值,默认为10。

5、biosnoop

biosnoop可以追踪设备IO并为每个IO设备打印一行汇总信息。

biosnoop通过动态追踪blk_族函数并记录函数的变化。

biosnoop需要BPF支持,因此需要root权限。

biosnoop [-hQ]

-h:查看帮助信息

-Q:显示在OS队列的耗时

6、cachestat

cachestat用于统计Linux Page的命中率和缺失率,通过动态追踪内核页的cache函数,并更新cache函数的任何变化。

cachestat需要BPF支持,因此需要root权限。

cachestat [-h] [-T] [interval] [count]

-h:查看帮助信息

-T, --timestamp:输出时间戳

interval:输出间隔,单位为秒

count:输出数量

7、cachetop

cachetop用于统计每个进程的Linux Page缓存的命中率和缺失率,通过动态追踪内核页的cache函数,并更新cache函数的任何变化。

cachestat需要BPF支持,因此需要root权限。

cachetop [-h] [interval]

-h:查看帮助信息

interval:输出间隔

PID:进程ID

UID:进程用户ID

HITS:页缓存命中数量

MISSES:页缓存缺失数量

DIRTIES:增加到页缓存的脏页数量

READ_HIT%:页缓存的读命中率

WRITE_HIT%:页缓存的写命中率

BUFFERS_MB:Buffer大小,数据源/proc/meminfo

CACHED_MB:当前页的Cache大小,数据源/proc/meminfo

8、tcpconnect

tcpconnect用于追踪TCP活跃连接数量,通过动态追踪内核tcp_v4_connect和tcp_v6_connect函数,并记录函数内的任何变化。

tcpconnect需要BPF支持,因此需要root权限。

tcpconnect [-h] [-c] [-t] [-x] [-p PID] [-P PORT]

-h:查看帮助信息

-t:打印时间戳

-c:统计每个源IP和目的IP/端口的连接数

-p PID:只追踪PID进程

-P PORT:要追踪的目的端口列表,使用逗号分隔

9、trace

trace用于追踪某个函数调用并打印函数参数或返回值,需要BPF支持,因此需要root权限。

trace [-h] [-b BUFFER_PAGES] [-p PID] [-L TID] [-v] [-Z STRING_SIZE] [-S] [-s SYM_FILE_LIST] [-M MAX_EVENTS] [-t] [-u] [-T] [-C] [-K] [-U] [-a] [-I header] probe [probe ...]

-h:查看帮助信息

-p PID:只追踪PID进程

-L TID:只追踪TID线程

-v:显示生成的BPF程序,调试使用

-z STRING_SIZE:收集字符串参数的长度

-s SYM_FILE_LIST:收集栈大小

-M MAX_EVENTS:打印追踪消息的最大数量

-t:打印时间,单位为秒。

-u:打印时间戳

-T:打印时间列

-C:打印CPU ID

-K:打印每个事件的内核栈

-U:打印每个事件的用户栈

-a:打印序内核栈和用户栈的虚拟地址

-I header:增加header文件到BPF程序

probe [probe ...]:附加到函数的探针

trace '::do_sys_open "%s", arg2'

追踪open系统调用的所有调用方式

trace ':c:malloc "size = %d", arg1'

追踪malloc调用并打印申请分配内存的大小

trace 'u:pthread:pthread_create "start addr = %llx", arg3'

追踪pthread_create函数调用并打印线程启动函数地址

10、deadlock

deadlock用于查找正在运行进程潜在的死锁。deadlock通过附加uprobe事件,需要BPF支持,因此需要root权限。

deadlock [-h] [--binary BINARY] [--dump-graph DUMP_GRAPH] [--verbose] [--lock-symbols LOCK_SYMBOLS] [--unlock-symbols UNLOCK_SYMBOLS] pid

-h, --help:查看帮助信息

--binary BINARY:指定线程库,对于动态链接程序必须指定。

--dump-graph DUMP_GRAPH:导出mutex图到指定文件

--verbose:打印mutex统计信息

--lock-symbols LOCK_SYMBOLS:要追踪的锁的列表,使用逗号分隔,默认为pthread_mutex_lock。

--unlock-symbols UNLOCK_SYMBOLS:要追踪的解锁的列表,使用逗号分隔,默认为pthread_mutex_unlock。

pid:要追踪的进程ID

deadlock 181 --binary /lib/x86_64-linux-gnu/libpthread.so.0

查找进程181中的潜在死锁,如果进程被动态链接程序创建,需要使用--binary指定使用的线程库。

11、memleak

memleak用于追踪和查找内存分配和释放配对,需要Linux Kernel 4.7以上版本支持。

memleak  [-h]  [-p  PID] [-t] [-a] [-o OLDER] [-c COMMAND] [--combined-only] [-s SAMPLE_RATE] [-T TOP] [-z MIN_SIZE] [-Z MAX_SIZE] [-O OBJ] [INTERVAL] [COUNT]

-h:查看帮助信息

-p PID:指定进程PID

-t:追踪所有内存分配和释放请求和结果

-a:输出未释放内存的列表

-z MIN_SIZE:捕获分配内存的最小值

-Z MAX_SIZE:捕获分配内存的最大值

memleak -z 16 -Z 32

只捕获分析分配大小未16字节至32字节间的内存分配

三、BCC编程开发

1、BCC实现原理

BCC是eBPF的一个工具集,是对eBPF提取数据的上层封装,BCC工具编程形式是Python中嵌套BPF程序。Python代码可以为用户提供友好使用eBPF的上层接口,也可以用于数据处理。BPF程序会注入内核,提取数据。当BPF程序运行时,通过LLVM将BPF程序编译得到BPF指令集的elf文件,从elf文件中解析出可以注入内核的部分,使用bpf_load_program方法完成注入。

bpf_load_program注入程序方法加入了复杂的verifier机制,在运行注入程序前,先进行一系列的安全检查,最大限度的保证系统的安全。经过安全检查的BPF字节码使用内核JIT进行编译,生成本机汇编指令,附加到内核特定挂钩的程序。最终内核态与用户态通过高效的map机制进行通信,BCC工具在用户态使用Python进行数据处理。

2、BCC的Python部分实现

Python部分编码需要引入使用的模块和包。

BCC工具的Python部分代码中通过如下方式使用BPF C语言程序代码:

hello_world.py:


  1. #!/usr/bin/python3
  2. from bcc import BPF
  3. bpf_program = '''
  4. int kprobe__sys_clone(void *ctx)
  5. {
  6.     bpf_trace_printk("Hello, World!\\n");
  7.     return 0;
  8. }'''
  9. if __name__ == "__main__":
  10. BPF(text=bpf_program).trace_print()

kprobe__sys_clone是通过kprobes进行内核动态跟踪的快捷方式,如果C函数以开头kprobe__,则其余部分被视为要检测的内核函数名称。

bpf_trace_printk: 输出

python3 hello_world.py

4、DDOS防御示例


  1. #!/usr/bin/python
  2. from bcc import BPF
  3. import pyroute2
  4. import time
  5. import sys
  6. flags = 0
  7. def usage():
  8.     print("Usage: {0} [-S] <ifdev>".format(sys.argv[0]))
  9.     print("       -S: use skb mode\n")
  10.     print("e.g.: {0} eth0\n".format(sys.argv[0]))
  11.     exit(1)
  12. if len(sys.argv) < 2 or len(sys.argv) > 3:
  13.     usage()
  14. if len(sys.argv) == 2:
  15.     device = sys.argv[1]
  16. if len(sys.argv) == 3:
  17.     if "-S" in sys.argv:
  18.         # XDP_FLAGS_SKB_MODE
  19.         flags |= 2 << 0
  20.     if "-S" == sys.argv[1]:
  21.         device = sys.argv[2]
  22.     else:
  23.         device = sys.argv[1]
  24. mode = BPF.XDP
  25. ctxtype = "xdp_md"
  26. # load BPF program
  27. b = BPF(text = """
  28. #define KBUILD_MODNAME "foo"
  29. #include <uapi/linux/bpf.h>
  30. #include <linux/in.h>
  31. #include <linux/if_ether.h>
  32. #include <linux/if_packet.h>
  33. #include <linux/if_vlan.h>
  34. #include <linux/ip.h>
  35. #include <linux/ipv6.h>
  36. // how to determin ddos
  37. #define MAX_NB_PACKETS 1000
  38. #define LEGAL_DIFF_TIMESTAMP_PACKETS 1000000
  39. // store data, data can be accessd in kernel and user namespace
  40. BPF_HASH(rcv_packets);
  41. BPF_TABLE("percpu_array", uint32_t, long, dropcnt, 256);
  42. static inline int parse_ipv4(void *data, u64 nh_off, void *data_end) {
  43.     struct iphdr *iph = data + nh_off;
  44.     if ((void*)&iph[1] > data_end)
  45.         return 0;
  46.     return iph->protocol;
  47. }
  48. static inline int parse_ipv6(void *data, u64 nh_off, void *data_end) {
  49.     struct ipv6hdr *ip6h = data + nh_off;
  50.     if ((void*)&ip6h[1] > data_end)
  51.         return 0;
  52.     return ip6h->nexthdr;
  53. }
  54. // determine ddos
  55. static inline int detect_ddos(){
  56.     // Used to count number of received packets
  57.     u64 rcv_packets_nb_index = 0, rcv_packets_nb_inter=1, *rcv_packets_nb_ptr;
  58.     // Used to measure elapsed time between 2 successive received packets
  59.     u64 rcv_packets_ts_index = 1, rcv_packets_ts_inter=0, *rcv_packets_ts_ptr;
  60.     int ret = 0;
  61.     rcv_packets_nb_ptr = rcv_packets.lookup(&rcv_packets_nb_index);
  62.     rcv_packets_ts_ptr = rcv_packets.lookup(&rcv_packets_ts_index);
  63.     if(rcv_packets_nb_ptr != 0 && rcv_packets_ts_ptr != 0){
  64.         rcv_packets_nb_inter = *rcv_packets_nb_ptr;
  65.         rcv_packets_ts_inter = bpf_ktime_get_ns() - *rcv_packets_ts_ptr;
  66.         if(rcv_packets_ts_inter < LEGAL_DIFF_TIMESTAMP_PACKETS){
  67.             rcv_packets_nb_inter++;
  68.         } else {
  69.             rcv_packets_nb_inter = 0;
  70.         }
  71.         if(rcv_packets_nb_inter > MAX_NB_PACKETS){
  72.             ret = 1;
  73.         }
  74.     }
  75.     rcv_packets_ts_inter = bpf_ktime_get_ns();
  76.     rcv_packets.update(&rcv_packets_nb_index, &rcv_packets_nb_inter);
  77.     rcv_packets.update(&rcv_packets_ts_index, &rcv_packets_ts_inter);
  78.     return ret;
  79. }
  80. // determine and recode by proto
  81. int xdp_prog1(struct CTXTYPE *ctx) {
  82.     void* data_end = (void*)(long)ctx->data_end;
  83.     void* data = (void*)(long)ctx->data;
  84.     struct ethhdr *eth = data;
  85.     // drop packets
  86.     int rc = XDP_PASS; // let pass XDP_PASS or redirect to tx via XDP_TX
  87.     long *value;
  88.     uint16_t h_proto;
  89.     uint64_t nh_off = 0;
  90.     uint32_t index;
  91.     nh_off = sizeof(*eth);
  92.     if (data + nh_off  > data_end)
  93.         return rc;
  94.     h_proto = eth->h_proto;
  95.     // parse double vlans
  96.     if (detect_ddos() == 0){
  97.         return rc;
  98.     }
  99.     rc = XDP_DROP;
  100.     #pragma unroll
  101.     for (int i=0; i<2; i++) {
  102.         if (h_proto == htons(ETH_P_8021Q) || h_proto == htons(ETH_P_8021AD)) {
  103.             struct vlan_hdr *vhdr;
  104.             vhdr = data + nh_off;
  105.             nh_off += sizeof(struct vlan_hdr);
  106.             if (data + nh_off > data_end)
  107.                 return rc;
  108.                 h_proto = vhdr->h_vlan_encapsulated_proto;
  109.         }
  110.     }
  111.     if (h_proto == htons(ETH_P_IP))
  112.         index = parse_ipv4(data, nh_off, data_end);
  113.     else if (h_proto == htons(ETH_P_IPV6))
  114.        index = parse_ipv6(data, nh_off, data_end);
  115.     else
  116.         index = 0;
  117.     value = dropcnt.lookup(&index);
  118.     if (value)
  119.         *value += 1;
  120.     return rc;
  121. }
  122. """, cflags=["-w", "-DCTXTYPE=%s" % ctxtype])
  123. fn = b.load_func("xdp_prog1", mode)
  124. b.attach_xdp(device, fn, flags)
  125. dropcnt = b.get_table("dropcnt")
  126. prev = [0] * 256
  127. print("Printing drops per IP protocol-number, hit CTRL+C to stop")
  128. while 1:
  129.     try:
  130.         for k in dropcnt.keys():
  131.             val = dropcnt.sum(k).value
  132.             i = k.value
  133.             if val:
  134.                 delta = val - prev[i]
  135.                 prev[i] = val
  136.                 print("{}: {} pkt/s".format(i, delta))
  137.         time.sleep(1)
  138.     except KeyboardInterrupt:
  139.         print("Removing filter from device")
  140.         break;
  141. b.remove_xdp(device, flags)

【转帖】Linux性能优化(四)——BCC性能监控工具的更多相关文章

  1. MySQL性能优化(四):SQL优化

    原文:MySQL性能优化(四):SQL优化 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/ ...

  2. 微擎开启性能优化里面的性能优化memcache内存优化及数据库读写分离

    http://www.mitusky.com/forum.php?mod=viewthread&tid=3135 [微擎 安装使用] 微擎开启性能优化里面的性能优化memcache内存优化及数 ...

  3. web性能优化-网络传输性能优化

    浏览器工作原理:https://www.cnblogs.com/thonrt/p/10008220.html 浏览器渲染原理: https://www.cnblogs.com/thonrt/p/100 ...

  4. Android App性能优化笔记之一:性能优化是什么及为什么?

    By Long Luo   周星驰的电影<功夫>里面借火云邪神之口说出了一句至理名言:“天下武功,唯快不破”. 在移动互联网时代,同样如此,留给一个公司的窗口往往只有很短的时间,如何把握住 ...

  5. 性能优化——Web前端性能优化

    核心知识点: 1.排查网站性能瓶颈的手法:分析各个环节的日志,找出异常部分 2.Web前端:网站业务逻辑之前的部分(浏览器.图片服务.CDN) 3.优化手段 1)浏览器优化 (1)减少http请求 a ...

  6. Linux性能优化之内存性能统计信息

    关于内存的概念及其原理在任何一本介绍操作系统的书本中都可以查阅到. 理论放一遍,在Linux操作系统中如何查看系统内存使用情况呢?看看内存统计信息有哪些维度. 一.内存使用量 详细使用方法,man f ...

  7. 前端性能优化(四)——网页加载更快的N种方式

    网站前端的用户体验,决定了用户是否想要继续使用网站以及网站的其他功能,网站的用户体验佳,可留住更多的用户.除此之外,前端优化得好,还可以为企业节约成本.那么我们应该如何对我们前端的页面进行性能优化呢? ...

  8. Android app 性能优化的思考--性能卡顿不好的原因在哪?

    说到 Android 系统手机,大部分人的印象是用了一段时间就变得有点卡顿,有些程序在运行期间莫名其妙的出现崩溃,打开系统文件夹一看,发现多了很多文件,然后用手机管家 APP 不断地进行清理优化 ,才 ...

  9. iOS 性能优化之业务性能监控

    业务性能监控, 在人工的在业务的开始和结束处打点上报,然后后台统计达到监控目的, 是性能优化里比较重要的一个维度.具体来说就是测试方法操作执行的时间损耗,可能是同步 也可能是异步的.测试的方法大概有如 ...

  10. react第八单元(什么是纯函数-组件的性能优化-pureComponent-组件性能优化的原理)

    课程目标 理解纯函数 熟练掌握组件性能优化的几种技巧 pureComponent和Component的区别 #知识点 一个函数的返回结果只依赖于它的参数,并且在执行过程里面没有副作用,我们就把这个函数 ...

随机推荐

  1. MySQL进阶篇:详解索引使用_最左前缀法则

    MySQL进阶篇:第四章_四.一_ 索引使用_最左前缀法则 最左前缀法则 如果索引了多列(联合索引),要遵守最左前缀法则.最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列.如果跳跃某一列 ...

  2. 数仓实践丨主动预防-DWS关键工具安装确认

    摘要:gdb确认是否安装,所带来的该工具用户数据库实例触发core问题后集群状态反复异常,对此问题及时分析根因并及时进行规避. 本文分享自华为云社区<主动预防-DWS关键工具安装确认>,作 ...

  3. 华为云FusionInsight MRS:千余节点滚动升级业务无中断

    摘要:滚动升级作为大集群数据底座的必备能力,能够完美解决了传统大数据平台操作繁琐.业务停机.升级成本高等问题,实现一个架构的持续演进,业务无中断. 华为开发者大会2021(Cloud)大会期间,由华为 ...

  4. 详解openGauss多线程架构启动过程

    摘要:本文介绍openGauss数据库的启动过程,包括主线程,辅助线程及业务处理线程的启动过程. 本文分享自华为云社区<openGauss内核分析(一):openGauss 多线程架构启动过程详 ...

  5. Kubernetes(K8S) helm 安装

    Helm 是一个 Kubernetes 的包管理工具, 就像 Linux 下的包管理器, 如 yum/apt 等, 可以很方便的将之前打包好的 yaml 文件部署到 kubernetes 上. Hel ...

  6. WSL2 配置 ArchLinux 初始化环境

    这篇文章针对的是在 Win11 系统的 WSL2 下安装 ArchLinux 系统, 网上很多中文教程都是使用 LxRunOffline 去做的,但是实际上该方法已经过时了,目前有更加先进的ArchW ...

  7. 【算法学习笔记】区间DP

    基本的知识点引用自 OI wiki,感谢社区的帮助 什么是区间 DP? 区间类动态规划是线性动态规划的扩展,它在分阶段地划分问题时,与阶段中元素出现的顺序和由前一阶段的哪些元素合并而来有很大的关系.令 ...

  8. 负载均衡--rpc服务端

    1. dubbo负载均衡的作用? 其出发点,自然也就是普通的负载均衡器的出发点了. 将负载均衡功能实现在rpc客户端侧,以便能够随时适应外部的环境变化,更好地发挥硬件作用. 而且客户端的负载均衡天然地 ...

  9. VueTreeselect

    https://www.vue-treeselect.cn/ 官网简介

  10. vue 基于axios封装request接口请求——request.js文件

    https://blog.csdn.net/m0_67393593/article/details/123266577?utm_medium=distribute.pc_relevant.none-t ...