diff --git a/eBPF_Supermarket/eBPF_Performance_Analysis/Makefile b/eBPF_Supermarket/eBPF_Performance_Analysis/Makefile new file mode 100644 index 000000000..2c414bcc8 --- /dev/null +++ b/eBPF_Supermarket/eBPF_Performance_Analysis/Makefile @@ -0,0 +1,62 @@ +ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \ + | sed 's/arm.*/arm/' \ + | sed 's/aarch64/arm64/' \ + | sed 's/ppc64le/powerpc/' \ + | sed 's/mips.*/mips/' \ + | sed 's/riscv64/riscv/' \ + | sed 's/loongarch64/loongarch/') +APP = src/ebpf_performance + +# 编译器标志 +CFLAGS=-g -O2 -Wall +BPF_CFLAGS=-g -O2 -target bpf + +# 要链接的库 +LIBS=-lbpf -lelf -lz -lzstd + +# 默认目标 +.PHONY: default +default: bpf + +# 安装必要的依赖 +.PHONY: deps +deps: + sudo apt-get update && \ + sudo apt-get install -y clang libelf1 libelf-dev zlib1g-dev libbpf-dev \ + linux-tools-$$(uname -r) linux-cloud-tools-$$(uname -r) \ + libpcap-dev gcc-multilib build-essential lolcat + +# 头文件目录 +INCLUDE_DIRS=-I/usr/include/x86_64-linux-gnu -I. -I./include -I./include/bpf -I./include/helpers +# 生成 vmlinux.h +.PHONY: vmlinux +vmlinux: + bpftool btf dump file /sys/kernel/btf/kvm format c > ./include/vmlinux.h + +# 编译BPF程序 +$(APP).bpf.o: $(APP).bpf.c vmlinux + clang $(BPF_CFLAGS) -D__TARGET_ARCH_$(ARCH) $(INCLUDE_DIRS) -c $< -o $@ + +# 生成BPF骨架文件 +$(APP).skel.h: $(APP).bpf.o + bpftool gen skeleton $< > $@ + +# 编译用户空间应用程序 +${APP}.o: ${APP}.c + clang $(CFLAGS) $(INCLUDE_DIRS) -c $< -o $@ + +# 链接用户空间应用程序与库 +$(notdir $(APP)): ${APP}.o $(HELPERS_OBJ_FILES) + clang -Wall $(CFLAGS) ${APP}.o $(HELPERS_OBJ_FILES) $(LIBS) -o $@ + @echo "BPF program compiled successfully." + +# bpf 目标 +.PHONY: bpf +bpf: $(APP).skel.h $(APP).bpf.o ${APP}.o $(HELPERS_OBJ_FILES) $(notdir $(APP)) + + +clean: + rm -f src/*.o src/*.skel.h src/helpers/*.o + sudo rm -rf $(notdir $(APP)) include/vmlinux.h temp + + diff --git "a/eBPF_Supermarket/eBPF_Performance_Analysis/docs/eBPF\346\200\247\350\203\275\346\265\213\350\257\225\346\226\271\346\241\210.md" "b/eBPF_Supermarket/eBPF_Performance_Analysis/docs/eBPF\346\200\247\350\203\275\346\265\213\350\257\225\346\226\271\346\241\210.md" index 95e6a142d..c947c1d72 100644 --- "a/eBPF_Supermarket/eBPF_Performance_Analysis/docs/eBPF\346\200\247\350\203\275\346\265\213\350\257\225\346\226\271\346\241\210.md" +++ "b/eBPF_Supermarket/eBPF_Performance_Analysis/docs/eBPF\346\200\247\350\203\275\346\265\213\350\257\225\346\226\271\346\241\210.md" @@ -421,19 +421,65 @@ GRUB_CMDLINE_LINUX="resume=/dev/mapper/ao_anolis-swap rd.lvm.lv=ao_anolis/root r 测试用例设计: +用例1: + | 事项 | 内容 | | ---------------- | ------------------------------------------------------------ | -| 场景 | 在同一系统环境下,针对不同类型的eBPF Map(如HashMap、ArrayMap、LRUHashMap等)进行性能测试。测试内容包括增删改查(CRUD)操作的耗时情况。 | -| 测试目的 | 评估不同类型的eBPF Map在高频率的CRUD操作下的性能表现,特别是针对相同操作次数所需的时间差异。 | -| 负载压力产生方法 | 在ebpf程序中对不同类型的Map设置一个固定的操作次数并记录每次操作的开始和结束时间。确保每个Map类型在相同的条件下测试。 | -| 执行脚本 | map_difference.py | -| 执行方法 | 部署并加载eBPF程序;执行./run_ebpf_and_process.sh脚本;查看分析结果 | -| 与生产环境差异 | 测试环境为隔离的虚拟机,Map的操作次数可能高于生产环境的实际负载,以便突显性能差异。 | -| 指标要求 | 每种Map类型的CRUD操作次数必须相同;相同操作次数下,不同Map类型的耗时差异不应超过预期范围; | -| 测试结果 | 记录每种Map类型的总操作时间,并汇总到报告中,生成柱状图或折线图展示不同Map类型的性能差异。 | -| 测试结果分析 | 分析不同Map类型在CRUD操作下的性能差异,确定性能瓶颈。评估哪种Map类型在高并发场景下表现最优。 | -| 后续Action | 如果某种Map类型在测试中表现出显著劣势,建议在生产环境中慎用或优化其使用场景。 | +| 场景 | 在系统CPU高负载情况下(CPU使用率约为80%)并且CPU频率固定,针对不同类型的eBPF Map进行相同次数的增删改查操作,这里会多次变化操作次数来说明实验结果的正确性。查看每种类型的Map在CPU高负载的情况下对于不同操作次数的耗时情况。 | +| 测试目的 | 评估不同类型的eBPF Map在CPU高负载的情况下,进行相同次数的CRUD操作时的性能表现,特别是记录每种类型的Map在处理时间上的差异。 | +| 负载压力产生方法 | 使用stress-ng来对CPU进行加压,并且每次测试时,通过设置不同的操作次数来对ebpf程序进行CRUD的压力控制。 | +| 执行脚本 | map_difference_01.py | +| 执行方法 | 执行./run_ebpf_and_process.sh脚本;查看分析结果 | +| 与生产环境差异 | 测试环境为隔离的虚拟机,实际的CPU核心数要比生产环境少,并且在负载压力产生方面,也和生产环境有差异。 | +| 指标要求 | 每次测试之前,要控制好每种Map类型的CRUD操作次数必须相同;相同操作次数下,不同Map类型的耗时差异不应超过理论分析的预期范围; | +| 测试结果 | 记录每种Map类型的CRUD操作时间,并汇总到报告中,生成柱状图或折线图展示不同Map类型的性能差异。 | +| 测试结果分析 | 通过测试结果的内容,并结合python的数据分析能力,来分析出在高CPU负载的情况下,哪种Map类型更适合使用,并且验证理论分析的结果是否正确。 | +| 后续Action | 将详细的测试分析结果编写到文档中,并且给出一个不同Map类型的适用场景的有力指导。 | + +用例2: +| 事项 | 内容 | +| ---------------- | ------------------------------------------------------------ | +| 场景 | 在系统内存高负载情况下(内存使用率约为75%)并且CPU频率固定,针对不同类型的eBPF Map进行相同次数的增删改查操作,这里会多次变化操作次数来说明实验结果的正确性。查看每种类型的Map在内存高负载的情况下对于不同操作次数的耗时情况。 | +| 测试目的 | 评估不同类型的eBPF Map在内存高负载的情况下,进行相同次数的CRUD操作时的性能表现,特别是记录每种类型的Map在处理时间上的差异。 | +| 负载压力产生方法 | 使用stress-ng来对内存进行加压,并且每次测试时,通过设置不同的操作次数来对ebpf程序进行CRUD的压力控制。 | +| 执行脚本 | map_difference_02.py | +| 执行方法 | 执行./run_ebpf_and_process.sh脚本;查看分析结果 | +| 与生产环境差异 | 测试环境为隔离的虚拟机,实际的内存总大小要比生产环境小,并且在负载压力产生方面,也和生产环境有差异。 | +| 指标要求 | 每次测试之前,要控制好每种Map类型的CRUD操作次数必须相同;相同操作次数下,不同Map类型的耗时差异不应超过理论分析的预期范围; | +| 测试结果 | 记录每种Map类型的CRUD操作时间,并汇总到报告中,生成柱状图或折线图展示不同Map类型的性能差异。 | +| 测试结果分析 | 通过测试结果的内容,并结合python的数据分析能力,来分析出在内存高负载的情况下,哪种Map类型更适合使用,并且验证理论分析的结果是否正确。 | +| 后续Action | 将详细的测试分析结果编写到文档中,并且给出一个不同Map类型的适用场景的有力指导。 | + +用例3: + +| 事项 | 内容 | +| ---------------- | ------------------------------------------------------------ | +| 场景 | 在系统开启CPU的P-states和C-states时,针对不同类型的eBPF Map进行相同次数的增删改查操作,这里会多次变化操作次数来说明实验结果的正确性。查看每种类型的Map在系统开启CPU的P-states和C-states的情况下对于不同操作次数的耗时情况。 | +| 测试目的 | 评估不同类型的eBPF Map在内存高负载的情况下,进行相同次数的CRUD操作时的性能表现,特别是记录每种类型的Map在处理时间上的差异。 | +| 负载压力产生方法 | 使用stress-ng来对系统进行加压,并且每次测试时,通过设置不同的操作次数来对ebpf程序进行CRUD的压力控制。 | +| 执行脚本 | map_difference_03.py | +| 执行方法 | 执行./run_ebpf_and_process.sh脚本;查看分析结果 | +| 与生产环境差异 | 测试环境为隔离的虚拟机,实际的CPU频率变化与生产环境有一定的差异,并且在负载压力产生方面,也和生产环境有差异。 | +| 指标要求 | 每次测试之前,要控制好每种Map类型的CRUD操作次数必须相同;相同操作次数下,不同Map类型的耗时差异不应超过理论分析的预期范围; | +| 测试结果 | 记录每种Map类型的CRUD操作时间,并汇总到报告中,生成柱状图或折线图展示不同Map类型的性能差异。 | +| 测试结果分析 | 通过测试结果的内容,并结合python的数据分析能力,来分析出在内存高负载的情况下,哪种Map类型更适合使用,并且验证理论分析的结果是否正确。 | +| 后续Action | 将详细的测试分析结果编写到文档中,并且给出一个不同Map类型的适用场景的有力指导。 | + +用例4: + +| 事项 | 内容 | +| ---------------- | ------------------------------------------------------------ | +| 场景 | 在系统关闭CPU的P-states和C-states时,针对不同类型的eBPF Map进行相同次数的增删改查操作,这里会多次变化操作次数来说明实验结果的正确性。查看每种类型的Map在系统关闭CPU的P-states和C-states的情况下对于不同操作次数的耗时情况。 | +| 测试目的 | 评估不同类型的eBPF Map在内存高负载的情况下,进行相同次数的CRUD操作时的性能表现,特别是记录每种类型的Map在处理时间上的差异。 | +| 负载压力产生方法 | 使用stress-ng来对系统进行加压,并且每次测试时,通过设置不同的操作次数来对ebpf程序进行CRUD的压力控制。 | +| 执行脚本 | map_difference_04.py | +| 执行方法 | 执行./run_ebpf_and_process.sh脚本,查看分析结果。 | +| 与生产环境差异 | 测试环境为隔离的虚拟机,实际的CPU频率变化与生产环境有一定的差异,并且在负载压力产生方面,也和生产环境有差异。 | +| 指标要求 | 每次测试之前,要控制好每种Map类型的CRUD操作次数必须相同;相同操作次数下,不同Map类型的耗时差异不应超过理论分析的预期范围; | +| 测试结果 | 记录每种Map类型的CRUD操作时间,并汇总到报告中,生成柱状图或折线图展示不同Map类型的性能差异。 | +| 测试结果分析 | 通过测试结果的内容,并结合python的数据分析能力,来分析出在关闭CPU的P-states和C-states的情况下,哪种Map类型更适合使用,并且验证理论分析的结果是否正确。 | +| 后续Action | 将详细的测试分析结果编写到文档中,并且给出一个不同Map类型的适用场景的有力指导。 | #### 4.2对挂载点类型的测试方案: diff --git a/eBPF_Supermarket/eBPF_Performance_Analysis/docs/images/image-20240726190714998.png b/eBPF_Supermarket/eBPF_Performance_Analysis/docs/images/image-20240726190714998.png new file mode 100644 index 000000000..cae6c82c4 Binary files /dev/null and b/eBPF_Supermarket/eBPF_Performance_Analysis/docs/images/image-20240726190714998.png differ diff --git a/eBPF_Supermarket/eBPF_Performance_Analysis/docs/images/image-20240726193503653.png b/eBPF_Supermarket/eBPF_Performance_Analysis/docs/images/image-20240726193503653.png new file mode 100644 index 000000000..411fe5fea Binary files /dev/null and b/eBPF_Supermarket/eBPF_Performance_Analysis/docs/images/image-20240726193503653.png differ diff --git a/eBPF_Supermarket/eBPF_Performance_Analysis/include/bpf/analyze_map.h b/eBPF_Supermarket/eBPF_Performance_Analysis/include/bpf/analyze_map.h new file mode 100644 index 000000000..71ffec3fa --- /dev/null +++ b/eBPF_Supermarket/eBPF_Performance_Analysis/include/bpf/analyze_map.h @@ -0,0 +1,74 @@ +// Copyright 2024 The EBPF performance testing Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// 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. +// +// author: yys2020haha@163.com +// +// Kernel space BPF program used for eBPF performance testing. +#ifndef __ANALYZE_MAP_H +#define __ANALYZE_MAP_H + +#include "vmlinux.h" +#include +#include +#include +#include "common.h" + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1024);//12KB + __type(key, u32); + __type(value,u64); +} hash_map SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1024); + __type(key, u32); + __type(value,u64); +} array_map SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __uint(max_entries, 1024); + __type(key, u32); + __type(value,u64); +} percpu_array_map SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_PERCPU_HASH); + __uint(max_entries, 1024); + __type(key, u32); + __type(value,u64); +} percpu_hash_map SEC(".maps"); +//在内核态中将数据信息存入到相应的map中 +volatile __u64 k = 0; +#define MAX_ENTRIES 1024 +static int analyze_maps(struct trace_event_raw_sys_enter *args,void *rb, + struct common_event *e){ + u32 idx,counts; + u64 syscall_id = (u64)args->id; + // 使用原子操作递增k,并获取递增前的值 + idx = __sync_fetch_and_add(&k, 1); + // 确保k在0到MAX_ENTRIES之间循环(避免同步问题) + if (idx >= MAX_ENTRIES) { + __sync_bool_compare_and_swap(&k, idx + 1, 0); + idx = 0; + } + // 向hash、array类型的map中存入数据 + bpf_map_update_elem(&hash_map, &idx, &syscall_id, BPF_ANY); + bpf_map_update_elem(&array_map, &idx, &syscall_id, BPF_ANY); + bpf_map_update_elem(&percpu_array_map,&idx,&syscall_id,BPF_ANY); + bpf_map_update_elem(&percpu_hash_map,&idx,&syscall_id,BPF_ANY); + bpf_map_update_elem(&percpu_hash_map,&idx,&syscall_id,BPF_ANY); + RESERVE_RINGBUF_ENTRY(rb, e); + e->test_ringbuff.key = idx; + e->test_ringbuff.value = syscall_id; + bpf_ringbuf_submit(e, 0); + bpf_printk("syscall_id = %llu\n", syscall_id); + return 0; +} +#endif /* __ANALYZE_MAP_H */ diff --git a/eBPF_Supermarket/eBPF_Performance_Analysis/include/common.h b/eBPF_Supermarket/eBPF_Performance_Analysis/include/common.h new file mode 100644 index 000000000..4fec1e2da --- /dev/null +++ b/eBPF_Supermarket/eBPF_Performance_Analysis/include/common.h @@ -0,0 +1,60 @@ +// Copyright 2024 The EBPF performance testing Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// 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. +// +// author: yys2020haha@163.com +// +// Kernel space BPF program used for eBPF performance testing. +#ifndef __EBPF_PERFORMANCE_H +#define __EBPF_PERFORMANCE_H + +typedef unsigned int __u32; +typedef long long unsigned int __u64; + +#define OPTIONS_LIST "-a" +#define RING_BUFFER_TIMEOUT_MS 100 +#define OUTPUT_INTERVAL(SECONDS) sleep(SECONDS) + +#define PRINT_USAGE_ERR() \ + do { \ + fprintf(stderr, "Please specify exactly one option from %s.\n", \ + OPTIONS_LIST); \ + argp_state_help(state, stdout, ARGP_HELP_STD_HELP); \ + } while (0) + +#define SET_OPTION_AND_CHECK_USAGE(option, value) \ + do { \ + if (option == 0) { \ + value = true; \ + option = 1; \ + } else { \ + PRINT_USAGE_ERR(); \ + } \ + } while (0) +#define RESERVE_RINGBUF_ENTRY(rb, e) \ + do { \ + typeof(e) _tmp = bpf_ringbuf_reserve(rb, sizeof(*e), 0); \ + if (!_tmp) \ + return 0; \ + e = _tmp; \ + } while (0) +enum EventType { + NONE_TYPE, + EXECUTE_TEST_MAPS, +} event_type; + +struct common_event{ + union { + struct { + __u32 key; + __u64 value; + } test_ringbuff; + }; +}; +#endif \ No newline at end of file diff --git a/eBPF_Supermarket/eBPF_Performance_Analysis/py/analy.py b/eBPF_Supermarket/eBPF_Performance_Analysis/py/analy.py new file mode 100644 index 000000000..a68df95a9 --- /dev/null +++ b/eBPF_Supermarket/eBPF_Performance_Analysis/py/analy.py @@ -0,0 +1,67 @@ +import pandas as pd +import matplotlib.pyplot as plt + +# 步骤 1: 将 .txt 文件转换为 .csv 文件 +input_txt_file = './output.txt' # 你的 .txt 文件路径 +output_csv_file = './data.csv' # 输出 .csv 文件路径 + +# 读取 .txt 文件内容并写入 .csv 文件 +with open(input_txt_file, 'r') as txt_file: + lines = txt_file.readlines() + +# 写入 .csv 文件 +with open(output_csv_file, 'w') as csv_file: + for line in lines: + # 替换多余空格为逗号,准备写入到 .csv 文件 + formatted_line = ','.join(line.split()) + csv_file.write(formatted_line + '\n') + +# 步骤 2: 读取 .csv 文件并进行数据分析 +data = pd.read_csv(output_csv_file, header=None) + +# 给列命名,每三个数据为一组,分别对应 lookup、insert、delete 操作 +data.columns = ['hash_lookup', 'hash_insert', 'hash_delete', + 'array_lookup', 'array_insert', 'array_delete', + 'percpu_array_lookup', 'percpu_array_insert', 'percpu_array_delete', + 'percpu_hash_lookup', 'percpu_hash_insert', 'percpu_hash_delete'] + +# 计算每种 map 类型的平均操作时间 +avg_hash = data[['hash_lookup', 'hash_insert', 'hash_delete']].mean() +avg_array = data[['array_lookup', 'array_insert', 'array_delete']].mean() +avg_percpu_array = data[['percpu_array_lookup', 'percpu_array_insert', 'percpu_array_delete']].mean() +avg_percpu_hash = data[['percpu_hash_lookup', 'percpu_hash_insert', 'percpu_hash_delete']].mean() + +# 创建一个 DataFrame 来存储平均值 +avg_table = pd.DataFrame({ + 'Operation': ['lookup', 'insert', 'delete'], + 'Hash Map': avg_hash.values, + 'Array Map': avg_array.values, + 'Per-CPU Array': avg_percpu_array.values, + 'Per-CPU Hash': avg_percpu_hash.values +}) + +# 打印平均值表格到控制台 +print("Average Execution Time of eBPF Map Operations (in seconds):\n") +print(avg_table.to_string(index=False)) + +# 绘制平均操作时间的图表 +plt.figure(figsize=(10, 6)) + +# 绘制四种 map 类型的平均时间曲线 +operations = ['lookup', 'insert', 'delete'] + +plt.plot(operations, avg_hash, marker='o', linestyle='-', color='b', label='Hash') +plt.plot(operations, avg_array, marker='s', linestyle='-', color='r', label='Array') +plt.plot(operations, avg_percpu_array, marker='^', linestyle='-', color='g', label='Per-CPU Array') +plt.plot(operations, avg_percpu_hash, marker='d', linestyle='-', color='purple', label='Per-CPU Hash') + +# 图表设置 +plt.title('Average Execution Time of eBPF Map Operations') +plt.xlabel('Operation Type') +plt.ylabel('Time (seconds)') +plt.legend(loc='upper left') +plt.grid(True) + +# 显示图表 +plt.savefig('ebpf_map_operation_times.png') # 保存图表为文件 +plt.show() diff --git a/eBPF_Supermarket/eBPF_Performance_Analysis/run_ebpf_and_process.sh b/eBPF_Supermarket/eBPF_Performance_Analysis/run_ebpf_and_process.sh new file mode 100644 index 000000000..bbd6d41bc --- /dev/null +++ b/eBPF_Supermarket/eBPF_Performance_Analysis/run_ebpf_and_process.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +# Function to handle Ctrl+C +function ctrl_c() { + echo "Ctrl+C detected. Stopping ebpf_performance..." + pkill -f "ebpf_performance" # Use -f option to match the complete command line + + # Step 2: Add a short delay to ensure all data is written to output.txt + sleep 2 # Increase delay to 2 seconds to ensure file write completion + + # Step 3: Check if output.txt exists and is not empty + if [ -f "output.txt" ]; then + echo "Output file exists." + else + echo "Output file does not exist." + exit 1 + fi + + if [ -s "output.txt" ]; then + echo "Output file generated successfully. File size: $(du -h output.txt)" + + # Step 4: Run Python script to process the data + echo "Running Python script to process the data..." + sudo python3 ./py/analy.py + + if [ $? -eq 0 ]; then + echo "Python script executed successfully." + else + echo "Python script failed to execute." + fi + else + echo "Output file exists but is empty. File size: $(du -h output.txt)" + exit 1 + fi + + exit 0 +} + +# Trap Ctrl+C signal +trap ctrl_c INT + +# Step 1: Run eBPF program and redirect output to output.txt in a loop +echo "Starting eBPF program..." +sudo stdbuf -oL ./ebpf_performance -a > output.txt & + +# Wait for the eBPF program to be manually terminated by Ctrl+C +wait $! + +# If the script reaches here without Ctrl+C, it means ebpf_performance finished by itself +echo "eBPF program finished by itself, running Python script..." +ctrl_c diff --git a/eBPF_Supermarket/eBPF_Performance_Analysis/src/ebpf_performance.bpf.c b/eBPF_Supermarket/eBPF_Performance_Analysis/src/ebpf_performance.bpf.c new file mode 100644 index 000000000..1719e715c --- /dev/null +++ b/eBPF_Supermarket/eBPF_Performance_Analysis/src/ebpf_performance.bpf.c @@ -0,0 +1,31 @@ +// Copyright 2024 The EBPF performance testing Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// 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. +// +// author: yys2020haha@163.com +// +// Kernel space BPF program used for eBPF performance testing. +#include "analyze_map.h" +#include "vmlinux.h" +#include +#include +#include +#include "common.h" +char LICENSE[] SEC("license") = "Dual BSD/GPL"; +struct { + __uint(type,BPF_MAP_TYPE_RINGBUF); + __uint(max_entries,1024); +} rb SEC(".maps"); + +static struct common_event *e; +// 对比Map类型中的hash和array的性能 +SEC("tracepoint/raw_syscalls/sys_enter") +int tp_sys_entry(struct trace_event_raw_sys_enter *args) { + return analyze_maps(args,&rb,e); +} diff --git a/eBPF_Supermarket/eBPF_Performance_Analysis/src/ebpf_performance.c b/eBPF_Supermarket/eBPF_Performance_Analysis/src/ebpf_performance.c new file mode 100644 index 000000000..57049f120 --- /dev/null +++ b/eBPF_Supermarket/eBPF_Performance_Analysis/src/ebpf_performance.c @@ -0,0 +1,545 @@ +// Copyright 2024 The EBPF performance testing Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// 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. +// +// author: yys2020haha@163.com +// +// Kernel space BPF program used for eBPF performance testing. + +#include "common.h" +#include "ebpf_performance.skel.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +// 定义env结构体,用来存储程序中的事件信息 +static struct env { + bool execute_test_maps; + bool verbose; + enum EventType event_type; +} env = { + .execute_test_maps = false, + .verbose = false, + .event_type = NONE_TYPE, +}; + +const char *argp_program_version = "ebpf_performance 1.0"; +const char *argp_program_bug_address = ""; +const char argp_program_doc[] = + "BPF program used for eBPF performance testing\n"; +int option_selected = 0; // 功能标志变量,确保激活子功能 +// 具体解释命令行参数 +static const struct argp_option opts[] = { + {"Map test", 'a', NULL, 0, "Comparing the differences between eBPF Maps"}, + {"verbose", 'v', NULL, 0, "Verbose debug output"}, + {NULL, 'H', NULL, OPTION_HIDDEN, "Show the full help"}, + {}, +}; +// 解析命令行参数 +static error_t parse_arg(int key, char *arg, struct argp_state *state) { + switch (key) { + case 'a': + SET_OPTION_AND_CHECK_USAGE(option_selected, env.execute_test_maps); + break; + case 'H': + argp_state_help(state, stderr, ARGP_HELP_STD_HELP); + break; + case 'v': + env.verbose = true; + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} +// 定义解析参数的处理函数 +static const struct argp argp = { + .options = opts, + .parser = parse_arg, + .doc = argp_program_doc, +}; + +static int libbpf_print_fn(enum libbpf_print_level level, const char *format, + va_list args) { + if (level == LIBBPF_DEBUG && !env.verbose) + return 0; + return vfprintf(stderr, format, args); +} + +static volatile bool exiting = false; +// 设置信号来控制是否打印信息 +static void sig_handler(int sig) { exiting = true; } +//控制ringbuff次数 +static int event_count = 0; // 事件计数器 +static volatile bool stop_polling = false; // 控制轮询的标志 +#define MAX_EVENTS 1024 +// 根据 env 设置 EventType +static int determineEventType(struct env *env) { + if (!env) { + return 1; + } + if (env->execute_test_maps) { + env->event_type = EXECUTE_TEST_MAPS; + } else { + env->event_type = NONE_TYPE; // 或者根据需要设置一个默认的事件类型 + } + return 0; +} + +/*通过env->event_type属性来选择需要打印的信息表头*/ +static int print_event_head(struct env *env) { + if (!env->event_type) { + // 处理无效参数,可以选择抛出错误或返回 + return 1; + } + switch (env->event_type) { + case EXECUTE_TEST_MAPS: + printf("%-12s %-12s %-12s %-12s %-12s %-12s\n", "Map_01_Insert", + "Map_01_LookUp", "Map_01_Delete", "Map_02_Insert", + "Map_02_LookUp", "Map_02_Delete"); + break; + default: + // Handle default case or display an error message + break; + } + return 0; +} + +static void set_disable_load(struct ebpf_performance_bpf *skel) { + bpf_program__set_autoload(skel->progs.tp_sys_entry, + env.execute_test_maps ? true : false); +} +void print_map_and_check_error(int (*print_func)(struct ebpf_performance_bpf *), + struct ebpf_performance_bpf *skel, + const char *map_name, int err) { + OUTPUT_INTERVAL(10); + print_func(skel); + if (err < 0 && err != -4) { + printf("Error printing %s map: %d\n", map_name, err); + } +} + +int attach_probe(struct ebpf_performance_bpf *skel) { + return ebpf_performance_bpf__attach(skel); +} +struct timespec diff(struct timespec start, struct timespec end) { + struct timespec temp; + if ((end.tv_nsec - start.tv_nsec) < 0) { + temp.tv_sec = end.tv_sec - start.tv_sec - 1; + temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; + } else { + temp.tv_sec = end.tv_sec - start.tv_sec; + temp.tv_nsec = end.tv_nsec - start.tv_nsec; + } + return temp; +} +#define MAX_ENTRIES 1024 +#define MAX_CPUS 8 +// 信号处理函数,用来终止polling +void stop_polling_handler(int signum) { + stop_polling = true; +} +int compare_ebpf_maps(struct ebpf_performance_bpf *skel) { + int hash_fd = bpf_map__fd(skel->maps.hash_map); + int array_fd = bpf_map__fd(skel->maps.array_map); + int per_cpu_array_fd = bpf_map__fd(skel->maps.percpu_array_map); + int per_cpu_hash_fd = bpf_map__fd(skel->maps.percpu_hash_map); + if (hash_fd < 0 || array_fd < 0 || per_cpu_array_fd < 0) { + fprintf(stderr, "Failed to get map file descriptors: %d, %d\n", hash_fd, + array_fd); + return 1; + } + + struct timespec start, end, elapsed; + int key, value; + char formatted_time[20]; + srand(time(0)); // 生成随机数种子 + int random_number; + + // 查找 HashMap + clock_gettime(CLOCK_MONOTONIC, &start); + for (key = 1; key < MAX_ENTRIES; key++) { + random_number = (rand() % key); + if (bpf_map_lookup_elem(hash_fd, &random_number, &value) != 0) { + fprintf(stderr, "Failed to lookup element in hash_map: %d\n", + errno); + return 1; + } + } + clock_gettime(CLOCK_MONOTONIC, &end); + elapsed = diff(start, end); + snprintf(formatted_time, sizeof(formatted_time), "%ld.%09ld", + elapsed.tv_sec, elapsed.tv_nsec); + printf("%-13s", formatted_time); + fflush(stdout); // 刷新输出缓冲区 + + // 插入 HashMap + clock_gettime(CLOCK_MONOTONIC, &start); + for (key = 1; key < MAX_ENTRIES; key++) { + random_number = (rand() % key); + value = random_number * 2; + if (bpf_map_update_elem(hash_fd, &random_number, &value, BPF_ANY) != + 0) { + fprintf(stderr, "Failed to insert element into hash_map: %d\n", + errno); + return 1; + } + } + clock_gettime(CLOCK_MONOTONIC, &end); + elapsed = diff(start, end); + snprintf(formatted_time, sizeof(formatted_time), "%ld.%09ld", + elapsed.tv_sec, elapsed.tv_nsec); + printf("%-13s", formatted_time); + fflush(stdout); // 刷新输出缓冲区 + + // 清除 HashMap + clock_gettime(CLOCK_MONOTONIC, &start); + for (key = 0; key < MAX_ENTRIES; key++) { + if (bpf_map_delete_elem(hash_fd, &key) != 0) { + fprintf(stderr, "Failed to delete element in hash_map: %d\n", + errno); + return 1; + } + } + clock_gettime(CLOCK_MONOTONIC, &end); + elapsed = diff(start, end); + snprintf(formatted_time, sizeof(formatted_time), "%ld.%09ld", + elapsed.tv_sec, elapsed.tv_nsec); + printf("%-13s", formatted_time); + fflush(stdout); // 刷新输出缓冲区 + + // 操作 ArrayMap + + // 查找 ArrayMap + clock_gettime(CLOCK_MONOTONIC, &start); + for (key = 1; key < MAX_ENTRIES; key++) { + random_number = (rand() % key); + if (bpf_map_lookup_elem(array_fd, &random_number, &value) != 0) { + fprintf(stderr, "Failed to lookup element in array_map: %d\n", + errno); + return 1; + } + } + clock_gettime(CLOCK_MONOTONIC, &end); + elapsed = diff(start, end); + snprintf(formatted_time, sizeof(formatted_time), "%ld.%09ld", + elapsed.tv_sec, elapsed.tv_nsec); + printf("%-13s", formatted_time); + fflush(stdout); // 刷新输出缓冲区 + + // 插入 ArrayMap + clock_gettime(CLOCK_MONOTONIC, &start); + for (key = 1; key < MAX_ENTRIES; key++) { + random_number = (rand() % key); + value = key * 2; + if (bpf_map_update_elem(array_fd, &random_number, &value, BPF_ANY) != + 0) { + fprintf(stderr, "Failed to insert element in array_map: %d\n", + errno); + return 1; + } + } + clock_gettime(CLOCK_MONOTONIC, &end); + elapsed = diff(start, end); + snprintf(formatted_time, sizeof(formatted_time), "%ld.%09ld", + elapsed.tv_sec, elapsed.tv_nsec); + printf("%-13s", formatted_time); + fflush(stdout); // 刷新输出缓冲区 + + // 清除 ArrayMap + clock_gettime(CLOCK_MONOTONIC, &start); + for (key = 1; key < MAX_ENTRIES; key++) { + value = 0; + if (bpf_map_update_elem(array_fd, &key, &value, BPF_ANY) != 0) { + fprintf(stderr, "Failed to reset element in array_map: %d\n", + errno); + return 1; + } + } + clock_gettime(CLOCK_MONOTONIC, &end); + elapsed = diff(start, end); + snprintf(formatted_time, sizeof(formatted_time), "%ld.%09ld", + elapsed.tv_sec, elapsed.tv_nsec); + printf("%-13s", formatted_time); + fflush(stdout); // 刷新输出缓冲区 + + // 操作 Per_cpu ArrayMap + int num_cpus = sysconf(_SC_NPROCESSORS_ONLN); + size_t value_size = sizeof(__u64) * num_cpus; + + // 查找 Per_cpu ArrayMap + clock_gettime(CLOCK_MONOTONIC, &start); + for (key = 1; key < MAX_ENTRIES; key++) { + random_number = (rand() % key); + __u64 *values = malloc(value_size); + if (bpf_map_lookup_elem(per_cpu_array_fd, &random_number, values) != + 0) { + fprintf(stderr, + "Failed to lookup element in percpu_array_map: %d\n", + errno); + return 1; + } + + // 根据需要处理 CPU 上的值 + for (int cpu = 0; cpu < MAX_CPUS; cpu++) { + if (values[cpu]) { + } // 可根据实际需求打印或使用 + } + free(values); + } + clock_gettime(CLOCK_MONOTONIC, &end); + elapsed = diff(start, end); + snprintf(formatted_time, sizeof(formatted_time), "%ld.%09ld", + elapsed.tv_sec, elapsed.tv_nsec); + printf("%-13s", formatted_time); + fflush(stdout); // 刷新输出缓冲区 + + // 插入per_cpu_array_map + clock_gettime(CLOCK_MONOTONIC, &start); + for (key = 1; key < MAX_ENTRIES; key++) { + random_number = (rand() % key); + // 初始化每个CPU的值 + __u64 *values = malloc(value_size); + for (int cpu = 0; cpu < MAX_CPUS; cpu++) { + values[cpu] = random_number * + (cpu + 1); // 示例:每个CPU的值为随机数乘以CPU编号 + } + + if (bpf_map_update_elem(per_cpu_array_fd, &random_number, values, + BPF_ANY) != 0) { + fprintf(stderr, + "Failed to insert element into percpu_array_map: %d\n", + errno); + return 1; + } + free(values); + } + clock_gettime(CLOCK_MONOTONIC, &end); + elapsed = diff(start, end); + snprintf(formatted_time, sizeof(formatted_time), "%ld.%09ld", + elapsed.tv_sec, elapsed.tv_nsec); + printf("%-13s", formatted_time); + fflush(stdout); + + // 清除 Per_cpu ArrayMap + clock_gettime(CLOCK_MONOTONIC, &start); + for (key = 1; key < MAX_ENTRIES; key++) { + // 将每个CPU上的值重置为0 + unsigned long values[MAX_CPUS] = {0}; // 所有CPU初始化为0 + + if (bpf_map_update_elem(per_cpu_array_fd, &key, values, BPF_ANY) != 0) { + fprintf(stderr, "Failed to reset element in percpu_array_map: %d\n", + errno); + return 1; + } + } + clock_gettime(CLOCK_MONOTONIC, &end); + elapsed = diff(start, end); + snprintf(formatted_time, sizeof(formatted_time), "%ld.%09ld", + elapsed.tv_sec, elapsed.tv_nsec); + printf("%-13s", formatted_time); + fflush(stdout); + // 计算一下malloc和free的耗时 + // clock_gettime(CLOCK_MONOTONIC, &start); + // for(int i = 0 ; i < 1024 ; i++){ + // __u64 *values = malloc(value_size); + // free(values); + // } + // clock_gettime(CLOCK_MONOTONIC, &end); + // elapsed = diff(start, end); + // snprintf(formatted_time, sizeof(formatted_time), "%ld.%09ld", + // elapsed.tv_sec, elapsed.tv_nsec); printf("malloc耗时:%-13s\n", + // formatted_time); + + // 操作per_cpu_hash_map + // 查找 Per_cpu HashMap + clock_gettime(CLOCK_MONOTONIC, &start); + for (key = 1; key < MAX_ENTRIES; key++) { + random_number = (rand() % key); + __u64 *values = malloc(value_size); + if (bpf_map_lookup_elem(per_cpu_hash_fd, &random_number, values) != 0) { + fprintf(stderr, "Failed to lookup element in percpu_hash_map: %d\n", + errno); + return 1; + } + + // 根据需要处理 CPU 上的值 + for (int cpu = 0; cpu < MAX_CPUS; cpu++) { + if (values[cpu]) { + // 可以根据需要打印或处理每个 CPU 上的值 + } + } + free(values); + } + clock_gettime(CLOCK_MONOTONIC, &end); + elapsed = diff(start, end); + snprintf(formatted_time, sizeof(formatted_time), "%ld.%09ld", + elapsed.tv_sec, elapsed.tv_nsec); + printf("%-13s", formatted_time); + fflush(stdout); // 刷新输出缓冲区 + + // 插入 Per_cpu HashMap + clock_gettime(CLOCK_MONOTONIC, &start); + for (key = 1; key < MAX_ENTRIES; key++) { + random_number = (rand() % key); + // 初始化每个 CPU 的值 + __u64 *values = malloc(value_size); + for (int cpu = 0; cpu < MAX_CPUS; cpu++) { + values[cpu] = random_number * + (cpu + 1); // 示例:每个 CPU 的值为随机数乘以 CPU 编号 + } + + if (bpf_map_update_elem(per_cpu_hash_fd, &random_number, values, + BPF_ANY) != 0) { + fprintf(stderr, + "Failed to insert element into percpu_hash_map: %d\n", + errno); + return 1; + } + free(values); + } + clock_gettime(CLOCK_MONOTONIC, &end); + elapsed = diff(start, end); + snprintf(formatted_time, sizeof(formatted_time), "%ld.%09ld", + elapsed.tv_sec, elapsed.tv_nsec); + printf("%-13s", formatted_time); + fflush(stdout); // 刷新输出缓冲区 + + // 清除 Per_cpu HashMap + clock_gettime(CLOCK_MONOTONIC, &start); + for (key = 1; key < MAX_ENTRIES; key++) { + if (bpf_map_delete_elem(per_cpu_hash_fd, &key) != 0) { + fprintf(stderr, "Failed to delete element in percpu_hash_map: %d\n", + errno); + return 1; + } + } + clock_gettime(CLOCK_MONOTONIC, &end); + elapsed = diff(start, end); + snprintf(formatted_time, sizeof(formatted_time), "%ld.%09ld", + elapsed.tv_sec, elapsed.tv_nsec); + printf("%-13s", formatted_time); + fflush(stdout); // 刷新输出缓冲区 + + // 操作ringbuff + + printf("\n"); + + return 0; +} +/*环形缓冲区的处理函数,用来打印ringbuff中的数据(最后展示的数据行)*/ +static int handle_event(void *ctx, void *data, size_t data_sz) { + printf("进入打印ringbuff函数\n"); + struct common_event *e = data; + + switch(env.event_type){ + case EXECUTE_TEST_MAPS:{ + printf("打印ringbuff的数据\n"); + //printf("%-6d %-6llu\n", e->test_ringbuff.key, e->test_ringbuff.value); + int key = e->test_ringbuff.key; + unsigned long long value = e->test_ringbuff.value; + event_count++; + // 如果事件计数器达到 MAX_EVENTS,设置标志停止轮询 + if (event_count >= MAX_EVENTS) { + stop_polling = true; + } + break; + } + default: + // 处理未知事件类型 + break; + } + return 0; +} +int main(int argc, char **argv) { + struct ebpf_performance_bpf *skel; + struct ring_buffer *rb = NULL; + int err; + /*解析命令行参数*/ + err = argp_parse(&argp, argc, argv, 0, NULL, NULL); + if (err) + return err; + /*设置libbpf的错误和调试信息回调*/ + libbpf_set_print(libbpf_print_fn); + /* Cleaner handling of Ctrl-C */ + signal(SIGINT, sig_handler); + signal(SIGTERM, sig_handler); + signal(SIGALRM, sig_handler); + /* Open BPF application */ + skel = ebpf_performance_bpf__open(); + if (!skel) { + fprintf(stderr, "Failed to open BPF skeleton\n"); + return 1; + } + + /* 禁用或加载内核挂钩函数 */ + set_disable_load(skel); + + /* 加载并验证BPF程序 */ + err = ebpf_performance_bpf__load(skel); + if (err) { + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); + goto cleanup; + } + + /* 附加跟踪点处理程序 */ + err = attach_probe(skel); + if (err) { + fprintf(stderr, "Failed to attach BPF skeleton\n"); + goto cleanup; + } + // 根据 env 设置 EventType + err = determineEventType(&env); + if (err) { + fprintf(stderr, "Invalid env parm\n"); + goto cleanup; + } + /*打印信息头*/ + // err = print_event_head(&env); + if (err) { + fprintf(stderr, "Please specify an option using %s.\n", OPTIONS_LIST); + goto cleanup; + } + /* 设置环形缓冲区轮询 */ + rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), handle_event, NULL, NULL); + if (!rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer\n"); + goto cleanup; + } + //err = ring_buffer__poll(rb, RING_BUFFER_TIMEOUT_MS /* timeout, ms */); + while (!exiting) { + //err = ring_buffer__poll(rb, RING_BUFFER_TIMEOUT_MS /* timeout, ms */); + if (env.execute_test_maps) { + print_map_and_check_error(compare_ebpf_maps, skel, "maps", err); + } + /* Ctrl-C will cause -EINTR */ + if (err == -EINTR) { + err = 0; + break; + } + if (err < 0) { + printf("Error polling perf buffer: %d\n", err); + break; + } + } +cleanup: + ebpf_performance_bpf__destroy(skel); + return -err; +} \ No newline at end of file