[转帖]python 性能优化监控工具_Linux性能优化(四)——BCC性能监控工具

python,性能,优化,监控,工具,linux,bcc · 浏览次数 : 0

小编点评

# Python code for ddos defense # Import necessary modules and packages import bcc import pyroute2 import sys # Define flags for the bpf program flags = [ "-w", "-DCTXTYPE=%s" % ctxtype ] # Load the bpf program b = BPF(text=bpf_program).trace_print() # Define functions for parsing and detecting ddos def parse_ipv4(data, nh_off, data_end): struct iphdr *iph = data + nh_off nh_off += sizeof(struct iphdr_hdr) if data + nh_off > data_end: return rc def parse_ipv6(data, nh_off, data_end): struct iphdr *iph = data + nh_off nh_off += sizeof(struct iphdr_hdr) return rc # Define the ddos detection function def detect_ddos(): index = bpf_ktime_get_ns() if index < LEGAL_DIFF_TIMESTAMP_PACKETS: return True return False # Define the main function def main(): # Determine and recode by proto int xdp_prog1(struct CTXTYPE *ctx) # Load the bpf program b = BPF(text=bpf_program).trace_print() # Attach the bpf program to the device device = b.attach_xdp(device, flags) # Drop packets until the filter is removed dropcnt = b.get_table("dropcnt") prev = [0] * 256 while 1: try: for k in dropcnt.keys(): val = dropcnt.sum(k).value i = k.value if val else 0 prev[i] = val delta = val - prev[i] print("{}: {} pkt/s".format(i, delta)) time.sleep(1) except KeyboardInterrupt: print("Removing filter from device") break except KeyboardInterrupt: print("Removing filter from device") break # Remove the bpf program from the device b.remove_xdp(device, flags) # Print a message to indicate that the filter has been removed print("Printing drops per IP protocol-number, hit CTRL+C to stop") ``` # Notes * This code assumes that the bpf program is loaded into memory * The device is connected to a virtual IP address * The code drops packets on a random IP address * The code can be stopped by pressing Ctrl+C * This code is intended for educational purposes only

正文

一、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安装

yum install bcc-tools

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:输出数量

dc30c6427ede9da75e99e3839f3282d8.png

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。

bc5cd9a384bb1243fd7a0120c366eb5c.png

5、biosnoop

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

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

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

biosnoop [-hQ]

-h:查看帮助信息

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

8472b2e90dfde5642ec1c7f3c09ab3ec.png

6、cachestat

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

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

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

-h:查看帮助信息

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

interval:输出间隔,单位为秒

count:输出数量

b985937e3c4cb4102b42849a11159d5b.png

7、cachetop

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

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

cachetop [-h] [interval]

-h:查看帮助信息

interval:输出间隔

9e7fd0d50ecbc1b75745014124a90806.png

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:要追踪的目的端口列表,使用逗号分隔

61ea715cde98c2b4c182f3e0acc51580.png

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系统调用的所有调用方式

02b695cdd4db5e64d8968bc003b5d474.png

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

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

5d67183ef10ccf808ece46361be7210f.png

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

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

6ffb0e41eea00be4950f3fda2e58244d.png

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进行数据处理。

0e7f5505f2d163eb17e7bae029e64caf.png

2、BCC示例实现

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

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

hello_world.py:

#!/usr/bin/python3

from bcc import BPF

bpf_program = '''

int kprobe__sys_clone(void *ctx)

{

bpf_trace_printk("Hello, World!\\n");

return 0;

}'''

if __name__ == "__main__":

BPF(text=bpf_program).trace_print()

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

bpf_trace_printk: 输出

python3 hello_world.py

d1ab5b0e81efc8bc36c9c369e603ae38.png

3、DDOS防御示例

#!/usr/bin/python

from bcc import BPF

import pyroute2

import time

import sys

flags = 0

def usage():

print("Usage: {0} [-S] ".format(sys.argv[0]))

print(" -S: use skb mode\n")

print("e.g.: {0} eth0\n".format(sys.argv[0]))

exit(1)

if len(sys.argv) < 2 or len(sys.argv) > 3:

usage()

if len(sys.argv) == 2:

device = sys.argv[1]

if len(sys.argv) == 3:

if "-S" in sys.argv:

# XDP_FLAGS_SKB_MODE

flags |= 2 << 0

if "-S" == sys.argv[1]:

device = sys.argv[2]

else:

device = sys.argv[1]

mode = BPF.XDP

ctxtype = "xdp_md"

# load BPF program

b = BPF(text = """

#define KBUILD_MODNAME "foo"

#include

#include

#include

#include

#include

#include

#include

// how to determin ddos

#define MAX_NB_PACKETS 1000

#define LEGAL_DIFF_TIMESTAMP_PACKETS 1000000

// store data, data can be accessd in kernel and user namespace

BPF_HASH(rcv_packets);

BPF_TABLE("percpu_array", uint32_t, long, dropcnt, 256);

static inline int parse_ipv4(void *data, u64 nh_off, void *data_end) {

struct iphdr *iph = data + nh_off;

if ((void*)&iph[1] > data_end)

return 0;

return iph->protocol;

}

static inline int parse_ipv6(void *data, u64 nh_off, void *data_end) {

struct ipv6hdr *ip6h = data + nh_off;

if ((void*)&ip6h[1] > data_end)

return 0;

return ip6h->nexthdr;

}

// determine ddos

static inline int detect_ddos(){

// Used to count number of received packets

u64 rcv_packets_nb_index = 0, rcv_packets_nb_inter=1, *rcv_packets_nb_ptr;

// Used to measure elapsed time between 2 successive received packets

u64 rcv_packets_ts_index = 1, rcv_packets_ts_inter=0, *rcv_packets_ts_ptr;

int ret = 0;

rcv_packets_nb_ptr = rcv_packets.lookup(&rcv_packets_nb_index);

rcv_packets_ts_ptr = rcv_packets.lookup(&rcv_packets_ts_index);

if(rcv_packets_nb_ptr != 0 && rcv_packets_ts_ptr != 0){

rcv_packets_nb_inter = *rcv_packets_nb_ptr;

rcv_packets_ts_inter = bpf_ktime_get_ns() - *rcv_packets_ts_ptr;

if(rcv_packets_ts_inter < LEGAL_DIFF_TIMESTAMP_PACKETS){

rcv_packets_nb_inter++;

} else {

rcv_packets_nb_inter = 0;

}

if(rcv_packets_nb_inter > MAX_NB_PACKETS){

ret = 1;

}

}

rcv_packets_ts_inter = bpf_ktime_get_ns();

rcv_packets.update(&rcv_packets_nb_index, &rcv_packets_nb_inter);

rcv_packets.update(&rcv_packets_ts_index, &rcv_packets_ts_inter);

return ret;

}

// determine and recode by proto

int xdp_prog1(struct CTXTYPE *ctx) {

void* data_end = (void*)(long)ctx->data_end;

void* data = (void*)(long)ctx->data;

struct ethhdr *eth = data;

// drop packets

int rc = XDP_PASS; // let pass XDP_PASS or redirect to tx via XDP_TX

long *value;

uint16_t h_proto;

uint64_t nh_off = 0;

uint32_t index;

nh_off = sizeof(*eth);

if (data + nh_off > data_end)

return rc;

h_proto = eth->h_proto;

// parse double vlans

if (detect_ddos() == 0){

return rc;

}

rc = XDP_DROP;

#pragma unroll

for (int i=0; i<2; i++) {

if (h_proto == htons(ETH_P_8021Q) || h_proto == htons(ETH_P_8021AD)) {

struct vlan_hdr *vhdr;

vhdr = data + nh_off;

nh_off += sizeof(struct vlan_hdr);

if (data + nh_off > data_end)

return rc;

h_proto = vhdr->h_vlan_encapsulated_proto;

}

}

if (h_proto == htons(ETH_P_IP))

index = parse_ipv4(data, nh_off, data_end);

else if (h_proto == htons(ETH_P_IPV6))

index = parse_ipv6(data, nh_off, data_end);

else

index = 0;

value = dropcnt.lookup(&index);

if (value)

*value += 1;

return rc;

}

""", cflags=["-w", "-DCTXTYPE=%s" % ctxtype])

fn = b.load_func("xdp_prog1", mode)

b.attach_xdp(device, fn, flags)

dropcnt = b.get_table("dropcnt")

prev = [0] * 256

print("Printing drops per IP protocol-number, hit CTRL+C to stop")

while 1:

try:

for k in dropcnt.keys():

val = dropcnt.sum(k).value

i = k.value

if val:

delta = val - prev[i]

prev[i] = val

print("{}: {} pkt/s".format(i, delta))

time.sleep(1)

except KeyboardInterrupt:

print("Removing filter from device")

break;

b.remove_xdp(device, flags)

文章知识点与官方知识档案匹配,可进一步学习相关知识
Python入门技能树首页概览217792 人正在系统学习中

与[转帖]python 性能优化监控工具_Linux性能优化(四)——BCC性能监控工具相似的内容:

[转帖]python 性能优化监控工具_Linux性能优化(四)——BCC性能监控工具

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

[转帖]Linux性能优化(四)——BCC性能监控工具

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

【转帖】Linux性能优化(四)——BCC性能监控工具

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

[转帖]Linux性能优化(四)——BCC性能监控工具

原文 https://blog.51cto.com/9291927/2593705 1、BCC简介 一、BCC简介 BCC是一个Python库,简化了eBPF应用的开发过程,并收集了大量性能分析相关的eBPF应用。BCC为BPF开发提供了不一样的前端支持,包括Python和Lua,实现了map建立、

[转帖]一次python服务的性能优化经历

https://juejin.cn/post/7208708762265616421 问题背景: ​ 在我们的业务中,有一些推荐的场景会需要走到集团研究院的算法推荐服务,对一些用户进行个性化的课件推荐或者作者推荐,这个业务场景已经很久了,但是一直有一个很难解决的问题困扰着我们,就是我们调用研究院的接

[转帖]专注于GOLANG、PYTHON、DB、CLUSTER 记一次压测引起的nginx负载均衡性能调优

https://xiaorui.cc/archives/3495 rfyiamcool2016年6月26日 0 Comments 这边有个性能要求极高的api要上线,这个服务端是golang http模块实现的。在上线之前我们理所当然的要做压力测试。起初是 “小白同学” 起头进行压力测试,但当我看到

[转帖]宋宝华:用eBPF/bcc分析系统性能的一个简单案例

原创 宋宝华 Linux阅码场 3月8日 bcc是eBPF的一种前端,当然这个前端特别地简单好用。可以直接在python里面嵌入通过C语言写的BPF程序,并帮忙产生BPF bytecode和load进入kernel挂载kprobe、tracepoints等上面执行。之后,还可以从python取出来C

[转帖]如何提高Linux下块设备IO的整体性能?

http://www.yunweipai.com/6989.html 运维派隶属马哥教育旗下专业运维社区,是国内成立最早的IT运维技术社区,欢迎关注公众号:yunweipai领取学习更多免费Linux云计算、Python、Docker、K8s教程关注公众号:马哥linux运维 作者介绍 邹立巍 Li

[转帖]Jmeter笔记:使用Jmeter向kafka发送消息

https://www.cnblogs.com/daydayup-lin/p/14124816.html 日常工作中有时候需要向kafka中发送消息来测试功能或者性能,这时候我们怎么办呢?我之前是自己写个简单的python脚本来模拟发送消息的,其实用Jmeter来实现也比较简单方便。 1、我们必须有

[转帖]Python-Curses模块

Python-Curses 什么是Curses?Python Curses模块开始我们的编程吧(启动与结束)重要使用方法(包装用法)Windows and Pads显示文本属性和颜色用户输入 什么是Curses? curses库为基于文本的终端提供独立于终端的屏幕绘制和键盘处理设施;这些终端包括VT