一、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. SQLite3使用笔记(2)——插入

    目录 1. 论述 2. 总结 1. 论述 如同上一篇文章SQLite3使用笔记(1)--查询所述,使用SQLite进行查询操作同样有两种方式.对于比较简单的表格插入,使用sqlite3_exec()接 ...

  2. Karmada 结合 coreDNS 插件实现跨集群统一域名访问

    本文分享自华为云社区<Karmada 结合 coreDNS 插件实现跨集群统一域名访问>,作者:云容器大未来 . 在多云与混合云越来越成为企业标配的今天,服务的部署和访问往往不在一个 K8 ...

  3. Python 绑定:从 Python 调用 C 或 C++

    摘要:您是拥有想要从 Python 中使用的C或 C++ 库的 Python 开发人员吗?如果是这样,那么Python 绑定允许您调用函数并将数据从 Python 传递到C或C++,让您利用这两种语言 ...

  4. 华为云GuassDB(for Redis)发布全新版本推出:Lua脚本和SSL连接加密

    摘要:9月8日,华为云GuassDB(for Redis)正式推出全新版本.新版本内核带来性能提升.无损升级.慢日志统计等多维度产品体验,同时推出Lua脚本和SSL连接加密两大重要功能,让业务设计更加 ...

  5. AI新手语音入门:认识词错率WER与字错率CER

    摘要:本文介绍了词错率WER和字错率CER的概念,引入了编辑距离的概念与计算方法,从而推导得到词错率或字错率的计算方法. 本文分享自华为云社区<新手语音入门(一):认识词错率WER与字错率CER ...

  6. Seal梁胜:近水楼台先得月,IT人员应充分利用AI解决问题

    2023年9月2日,由平台工程技术社区与数澈软件Seal联合举办的⌈AIGC时代下的平台工程⌋--2023平台工程技术大会在北京圆满收官.吸引了近300名平台工程爱好者现场参会,超过3000名观众在线 ...

  7. 剖析字节案例,火山引擎 A/B 测试 DataTester 如何“嵌入”技术研发流程

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 日前,在 WOT 全球创新技术大会上,火山引擎 DataTester 技术负责人韩云飞做了关于字节跳动 A/B 测 ...

  8. Flutter 自定义组件实战之Cupertino(iOS)风格的复选框

    继上一篇Flutter自定义组件的视频短课(视频地址: https://www.bilibili.com/video/BV1ap4y1U7UB/ )后,我们继续来聊自定义组件.视频中我为大家详解了Cu ...

  9. CS01 BOM客制化屏幕增强

    一.BOM行项目新增定制字段 效果如下 二.前台增强实现步骤 1.行项目表新增字段 2.CMOD,增强项目PCSD0002:在行项目中增强(PCSD0003:在抬头增强) 3.双击创建定制化屏幕 4. ...

  10. 深入剖析 RSA 密钥原理及实践

    一.前言 在经历了人生的很多至暗时刻后,你读到了这篇文章,你会后悔甚至愤怒:为什么你没有早点写出这篇文章?! 你的至暗时刻包括: 1.你所在的项目需要对接银行,对方需要你提供一个加密证书.你手上只有一 ...