diff --git a/.github/workflows/ebpf_cpu_watcher.yml b/.github/workflows/ebpf_cpu_watcher.yml index 93dbf8441..84848c083 100644 --- a/.github/workflows/ebpf_cpu_watcher.yml +++ b/.github/workflows/ebpf_cpu_watcher.yml @@ -23,11 +23,23 @@ jobs: - name: Install dependencies run: | sudo apt update - sudo apt install libbpf-dev clang llvm libelf-dev libpcap-dev gcc-multilib build-essential + sudo apt install -y libbpf-dev clang llvm libelf-dev libpcap-dev gcc-multilib build-essential git submodule update --init --recursive - - name: Run cpu_watcher + - name: Build cpu_watcher run: | cd eBPF_Supermarket/CPU_Subsystem/cpu_watcher/ make - sudo ./cpu_watcher + + - name: Run cpu_watcher + run: | + sudo ./eBPF_Supermarket/CPU_Subsystem/cpu_watcher/cpu_watcher + + - name: Build test_cpuwatcher + run: | + cd eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test + make + + - name: Run test_cpuwatcher + run: | + ./eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test/test_cpuwatcher diff --git a/.github/workflows/ebpf_kvm_watcher.yml b/.github/workflows/ebpf_kvm_watcher.yml index dc79c8d50..e90c618d5 100644 --- a/.github/workflows/ebpf_kvm_watcher.yml +++ b/.github/workflows/ebpf_kvm_watcher.yml @@ -19,10 +19,12 @@ jobs: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 - - - name: Test program execution + - name: Install dependencies run: | cd eBPF_Supermarket/kvm_watcher/ make deps - make - + - name: Test program execution + continue-on-error: true + run: | + cd eBPF_Supermarket/kvm_watcher/ + make \ No newline at end of file diff --git a/.github/workflows/ebpf_mem_watcher.yml b/.github/workflows/ebpf_mem_watcher.yml index a3c72420a..4ed5f0576 100644 --- a/.github/workflows/ebpf_mem_watcher.yml +++ b/.github/workflows/ebpf_mem_watcher.yml @@ -29,7 +29,8 @@ jobs: - name: Run mem_watcher continue-on-error: true run: | - cd eBPF_Supermarket/Memory_Subsystem/mem_watcher/ + cd eBPF_Supermarket/Memory_Subsystem/vmlinux/x86 bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h + cd ../../mem_watcher make sudo timeout 20 ./mem_watcher diff --git a/.github/workflows/ebpf_net_manager.yml b/.github/workflows/ebpf_net_manager.yml index 4881a21c7..4e18ac35e 100644 --- a/.github/workflows/ebpf_net_manager.yml +++ b/.github/workflows/ebpf_net_manager.yml @@ -36,10 +36,14 @@ jobs: cd eBPF_Supermarket/Network_Subsystem/net_manager/ sudo ./configure sudo make + ifconfig # run - sudo timeout -s SIGINT 5 ./xdp_loader -d ens33 -S || if [[ $? != 124 && $? != 0 ]];then exit $?;fi - sudo ./xacladm load ens33 ./conf.d/mac_load.conf - sudo xdp-loader unload ens33 --all + cd testenv + sudo ./testenv.sh setup --name veth-basic02 + cd .. + sudo timeout -s SIGINT 5 ./netmanager -d eth0 -S || if [[ $? != 124 && $? != 0 ]];then exit $?;fi + sudo ./netmanager -d eth0 -S + diff --git a/.github/workflows/ebpf_net_watcher.yml b/.github/workflows/ebpf_net_watcher.yml index ec987c429..4f3372d75 100644 --- a/.github/workflows/ebpf_net_watcher.yml +++ b/.github/workflows/ebpf_net_watcher.yml @@ -45,4 +45,11 @@ jobs: sudo timeout -s SIGINT 5 ./netwatcher -k || if [[ $? != 124 && $? != 0 ]];then exit $?;fi sudo timeout -s SIGINT 5 ./netwatcher -k -T || if [[ $? != 124 && $? != 0 ]];then exit $?;fi sudo timeout -s SIGINT 5 ./netwatcher -I || if [[ $? != 124 && $? != 0 ]];then exit $?;fi + sudo timeout -s SIGINT 5 ./netwatcher -S || if [[ $? != 124 && $? != 0 ]];then exit $?;fi + sudo timeout -s SIGINT 5 ./netwatcher -D || if [[ $? != 124 && $? != 0 ]];then exit $?;fi + sudo timeout -s SIGINT 5 ./netwatcher -M || if [[ $? != 124 && $? != 0 ]];then exit $?;fi + sudo timeout -s SIGINT 5 ./netwatcher -R || if [[ $? != 124 && $? != 0 ]];then exit $?;fi + sudo timeout -s SIGINT 5 ./netwatcher -C || if [[ $? != 124 && $? != 0 ]];then exit $?;fi + sudo timeout -s SIGINT 5 ./netwatcher -T || if [[ $? != 124 && $? != 0 ]];then exit $?;fi + sudo timeout -s SIGINT 5 ./netwatcher -U || if [[ $? != 124 && $? != 0 ]];then exit $?;fi timeout-minutes: 5 diff --git a/.github/workflows/ebpf_stack_analyser.yml b/.github/workflows/ebpf_stack_analyser.yml index b29742b99..d5a37e6a8 100644 --- a/.github/workflows/ebpf_stack_analyser.yml +++ b/.github/workflows/ebpf_stack_analyser.yml @@ -23,30 +23,31 @@ jobs: - name: Install native lib dependencies run: | - git submodule update --init --recursive + git submodule update --init --recursive eBPF_Supermarket/lib/ MagicEyes/ sudo apt install clang libelf1 libelf-dev zlib1g-dev - - name: Run app with native lib + - name: Compile test examples + run: | + cd eBPF_Supermarket/Stack_Analyser/testdir + gcc -o ./usdt_pthread -lpthread ./usdt_pthread.c + gcc -o ./uprobe_malloc ./uprobe_malloc.c + + - name: Compile and run app with native lib run: | cd eBPF_Supermarket/Stack_Analyser - make - gcc -o ./testdir/usdt_pthread ./testdir/usdt_pthread.c - sudo ./stack_analyzer on_cpu off_cpu memleak io readahead llc_stat probe u:pthread:pthread_create -c "./testdir/usdt_pthread" -t 5 - - magic-eyes-build-and-test: - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v3 + make -j$(nproc) - - name: Install native lib dependencies - run: | - git submodule update --init --recursive - sudo apt install clang libelf1 libelf-dev zlib1g-dev + sudo ./stack_analyzer on_cpu off_cpu memleak io readahead llc_stat probe "vfs_open" probe "t:sched:sched_switch" -d 5 + sudo ./stack_analyzer probe "u:pthread:pthread_create" -c "./testdir/usdt_pthread" -d 5 + sudo ./stack_analyzer probe "c:malloc" -c "./testdir/uprobe_malloc" -d 5 - - name: Run app with native lib + - name: Compile and run MagicEyes app with native lib run: | mkdir -p MagicEyes/build cd MagicEyes/build cmake -DBUILD_STACK_ANALYZER=ON .. - make - sudo ./src/backend/system_diagnosis/stack_analyzer/stack_analyzer on_cpu off_cpu memleak io readahead llc_stat probe u:pthread:pthread_create -c "../../eBPF_Supermarket/Stack_Analyser/testdir/usdt_pthread" -t 5 \ No newline at end of file + make -j$(nproc) + + sudo ./src/backend/system_diagnosis/stack_analyzer/stack_analyzer on_cpu off_cpu memleak io readahead llc_stat probe "p::vfs_open" probe "t:sched:sched_switch" -d 5 + sudo ./src/backend/system_diagnosis/stack_analyzer/stack_analyzer probe "u:pthread:pthread_create" -c "../../eBPF_Supermarket/Stack_Analyser/testdir/usdt_pthread" -d 5 + sudo ./src/backend/system_diagnosis/stack_analyzer/stack_analyzer probe "c:malloc" -c "../../eBPF_Supermarket/Stack_Analyser/testdir/uprobe_malloc" -d 5 diff --git a/.gitignore b/.gitignore index 681a24621..40e63914d 100644 --- a/.gitignore +++ b/.gitignore @@ -80,4 +80,4 @@ eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/controller # Stack_Analyser eBPF_Supermarket/Stack_Analyser/stack_analyzer eBPF_Supermarket/Stack_Analyser/exporter/exporter -eBPF_Supermarket/Stack_Analyser/bpf_skel \ No newline at end of file +eBPF_Supermarket/Stack_Analyser/bpf_skel diff --git a/.gitmodules b/.gitmodules index 675ac6e97..a2d3cd09c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -46,9 +46,6 @@ [submodule "eBPF_Supermarket/CPU_Subsystem/libbpf"] path = eBPF_Supermarket/CPU_Subsystem/libbpf url = https://github.com/libbpf/libbpf.git -[submodule "eBPF_Supermarket/Stack_Analyser/libbpf-bootstrap"] - path = eBPF_Supermarket/Stack_Analyser/libbpf-bootstrap - url = https://github.com/libbpf/libbpf-bootstrap.git [submodule "eBPF_Supermarket/Network_Subsystem/net_manager/lib/libbpf"] path = eBPF_Supermarket/Network_Subsystem/net_manager/lib/libbpf url = https://github.com/libbpf/libbpf.git @@ -73,3 +70,9 @@ [submodule "eBPF_Supermarket/Memory_Subsystem/bpftool"] path = eBPF_Supermarket/Memory_Subsystem/bpftool url = https://github.com/libbpf/bpftool.git +[submodule "eBPF_Supermarket/lib/bpftool"] + path = eBPF_Supermarket/lib/bpftool + url = https://github.com/libbpf/bpftool.git +[submodule "eBPF_Supermarket/lib/libbpf"] + path = eBPF_Supermarket/lib/libbpf + url = https://github.com/libbpf/libbpf.git diff --git a/MagicEyes/src/backend/net/net_watcher/bpf/common.bpf.h b/MagicEyes/src/backend/net/net_watcher/bpf/common.bpf.h new file mode 100644 index 000000000..de6d3e5cd --- /dev/null +++ b/MagicEyes/src/backend/net/net_watcher/bpf/common.bpf.h @@ -0,0 +1,670 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: blown.away@qq.com +// +// net_watcher libbpf 内核<->用户 传递信息相关结构体 + +#ifndef __COMMON_BPF_H +#define __COMMON_BPF_H + +#include "net_watcher.h" +#include "vmlinux.h" +#include +#include +#include +#include +#include +#include + +struct ktime_info { // us time stamp info发送数据包 + u64 qdisc_time; // tx包离开mac层时间戳 + u64 mac_time; // tx、rx包到达mac层时间戳 + u64 ip_time; // tx、rx包到达ip层时间戳 + // u64 tcp_time; // tx、rx包到达tcp层时间戳 + u64 tran_time; // tx、rx包到达传输层时间戳 + u64 app_time; // rx包离开tcp层时间戳 + void *sk; // 此包所属 socket套接字 + u8 data[MAX_HTTP_HEADER]; // 用户层数据 +}; + +struct packet_tuple { + unsigned __int128 saddr_v6; // ipv6 源地址 + unsigned __int128 daddr_v6; // ipv6 目的地址 + u32 saddr; // 源地址 + u32 daddr; // 目的地址 + u16 sport; // 源端口号 + u16 dport; // 目的端口号 + u32 seq; // seq报文序号 + u32 ack; // ack确认号 + u32 tran_flag; // 1:tcp 2:udp + u32 len; +}; + +struct tcpstate { + u32 saddr; + u32 daddr; + u16 sport; + u16 dport; + u16 family; + int oldstate; + int newstate; + u64 time; +}; + +enum { + e_ip_rcv = 0, + e_ip_local_deliver, + e_ip_local_deliver_finish, + e_ip__forward, + e_ip_local_out, + e_ip_output, + e_ip_finish_output, + e_ip_forward, + nf_max +} nf_hook; + +enum { + PROTO_TCP = 0, + PROTO_UDP, + PROTO_ICMP, + PROTO_UNKNOWN, + PROTO_MAX, +}; + +struct filtertime { + struct packet_tuple init; + struct packet_tuple done; + u64 time[nf_max]; +}; + +struct ip_packet { + unsigned int saddr; // 源地址 + unsigned int daddr; // 目的地址 +}; + +struct dns_header { + u16 id; // 事务ID + u16 flags; // 标志字段 + u16 qdcount; // 问题部分计数 + u16 ancount; // 应答记录计数 + u16 nscount; // 授权记录计数 + u16 arcount; // 附加记录计数 +}; + +struct dns_query { + struct dns_header header; // DNS头部 + char data[64]; // 可变长度数据(域名+类型+类) +}; + +struct dns { + u32 saddr; + u32 daddr; +}; + +struct query_info { + char msql[256]; + u32 size; + u64 start_time; +}; + +struct hist { + u64 slots[MAX_SLOTS]; + u64 latency; + u64 cnt; +}; + +struct trace_event_raw_tcp_send_reset { + unsigned short common_type; + unsigned char common_flags; + unsigned char common_preempt_count; + int common_pid; + const void *skbaddr; + const void *skaddr; + int state; + __u16 sport; + __u16 dport; + __u16 family; + __u8 saddr[4]; + __u8 daddr[4]; + __u8 saddr_v6[16]; + __u8 daddr_v6[16]; +}; + +struct trace_event_raw_tcp_receive_reset { + unsigned short common_type; + unsigned char common_flags; + unsigned char common_preempt_count; + int common_pid; + const void *skaddr; + __u16 sport; + __u16 dport; + __u16 family; + __u8 saddr[4]; + __u8 daddr[4]; + __u8 saddr_v6[16]; + __u8 daddr_v6[16]; + __u64 sock_cookie; +}; +#define MAX_CONN 1000 +#define MAX_SLOTS 27 +// 操作BPF映射的一个辅助函数 +static __always_inline void * //__always_inline强制内联 +bpf_map_lookup_or_try_init(void *map, const void *key, const void *init) { + void *val; + long err; + + val = bpf_map_lookup_elem(map, key); // 在BPF映射中查找具有给定键的条目 + if (val) + return val; + // 此时没有对应key的value + err = bpf_map_update_elem(map, key, init, + BPF_NOEXIST); // 向BPF映射中插入或更新一个条目 + if (err && err != -EEXIST) // 插入失败 + return 0; + + return bpf_map_lookup_elem(map, key); // 返回对应value值 +} + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; + +// 存储每个packet_tuple包所对应的ktime_info时间戳 +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __uint(max_entries, MAX_CONN *MAX_PACKET); + __type(key, struct packet_tuple); + __type(value, struct ktime_info); +} timestamps SEC(".maps"); + +// 包相关信息通过此buffer提供给userspace +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} rb SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} rtt_rb SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} udp_rb SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} netfilter_rb SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} mysql_rb SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} redis_rb SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} kfree_rb SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} icmp_rb SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} tcp_rb SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} dns_rb SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} trace_rb SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} redis_stat_rb SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} events SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} port_rb SEC(".maps"); + +// 存储每个tcp连接所对应的conn_t +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __uint(max_entries, MAX_CONN); + __type(key, struct sock *); + __type(value, struct conn_t); +} conns_info SEC(".maps"); + +// 根据ptid存储sock指针,从而在上下文无sock的内核探测点获得sock +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __uint(max_entries, MAX_CONN); + __type(key, u64); + __type(value, struct sock *); +} sock_stores SEC(".maps"); + +// 存储每个pid所对应的udp包 +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __uint(max_entries, MAX_CONN *MAX_PACKET); + __type(key, int); + __type(value, struct packet_tuple); +} pid_UDP SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __uint(max_entries, MAX_CONN *MAX_PACKET); + __type(key, struct sk_buff *); + __type(value, struct filtertime); +} netfilter_time SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __uint(max_entries, MAX_CONN *MAX_PACKET); + __type(key, int); + __type(value, struct packet_tuple); +} kfree SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __uint(max_entries, MAX_CONN *MAX_PACKET); + __type(key, struct ip_packet); + __type(value, unsigned long long); +} icmp_time SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 256 * 1024); + __type(key, struct sock *); + __type(value, __u64); +} tcp_state SEC(".maps"); + +// sql 耗时 +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 256 * 1024); + __type(key, __u32); + __type(value, __u64); +} mysql_time SEC(".maps"); + +// redis 耗时 +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 256 * 1024); + __type(key, __u32); + __type(value, struct redis_query); +} redis_time SEC(".maps"); + +// sql请求数 +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1024); + __type(key, __u32); + __type(value, __u64); +} sql_count SEC(".maps"); + +// dns计数根据每个saddr、daddr +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1024); + __type(key, struct dns); + __type(value, __u64); +} dns_request_count SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1024); + __type(key, struct dns); + __type(value, __u64); +} dns_response_count SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1024); + __type(key, __u32); + __type(value, struct query_info); +} queries SEC(".maps"); + +// 定义一个哈希映射,用于存储直方图数据 +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 256 * 1024); + __type(key, struct ip_packet); + __type(value, struct hist); +} hists SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, u32); + __type(value, u64); + __uint(max_entries, 1024); +} counters SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, MAX_COMM *MAX_PACKET); + __type(key, u32); + __type(value, struct packet_count); +} proto_stats SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, char*); // 键的最大长度,假设为 256 字节 + __type(value, u32); // 计数值 + __uint(max_entries, 1024); // 最大条目数 +} key_count SEC(".maps"); + +const volatile int filter_dport = 0; +const volatile int filter_sport = 0; +const volatile int all_conn = 0, err_packet = 0, extra_conn_info = 0, + layer_time = 0, http_info = 0, retrans_info = 0, + udp_info = 0, net_filter = 0, drop_reason = 0, icmp_info = 0, + tcp_info = 0, dns_info = 0, stack_info = 0, mysql_info = 0, + redis_info = 0, rtt_info = 0, rst_info = 0, + protocol_count = 0,redis_stat = 0;; + +/* help macro */ + +#define FILTER \ + if (filter_dport && filter_dport != pkt_tuple.dport) \ + return 0; \ + if (filter_sport && filter_sport != pkt_tuple.sport) \ + return 0; + +// 连接的目标端口是否匹配于filter_dport的值 +#define FILTER_DPORT \ + if (filter_dport) { \ + if (conn.dport != filter_dport) { \ + return 0; \ + } \ + } +// 连接的源端口是否匹配于filter_sport的值 +#define FILTER_SPORT \ + if (filter_sport) { \ + if (conn.sport != filter_sport) { \ + return 0; \ + } \ + } + +// 初始化conn_t结构 +#define CONN_INIT \ + struct conn_t conn = {0}; \ + conn.pid = ptid >> 32; \ + conn.ptid = ptid; \ + u16 protocol = BPF_CORE_READ(sk, sk_protocol); \ + if (protocol != IPPROTO_TCP) \ + return 0; \ + bpf_get_current_comm(&conn.comm, sizeof(conn.comm)); \ + conn.sock = sk; \ + u16 family = BPF_CORE_READ(sk, __sk_common.skc_family); \ + __be16 dport = BPF_CORE_READ(sk, __sk_common.skc_dport); \ + u16 sport = BPF_CORE_READ(sk, __sk_common.skc_num); \ + conn.family = family; \ + conn.sport = sport; \ + conn.dport = __bpf_ntohs(dport); \ + conn.init_timestamp = bpf_ktime_get_ns() / 1000; + +//初始化conn_t地址相关信息 +#define CONN_ADD_ADDRESS \ + if (family == AF_INET) { \ + conn.saddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr); \ + conn.daddr = BPF_CORE_READ(sk, __sk_common.skc_daddr); \ + } else if (family == AF_INET6) { \ + bpf_probe_read_kernel( \ + &conn.saddr_v6, \ + sizeof(sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32), \ + &sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); \ + bpf_probe_read_kernel( \ + &conn.daddr_v6, \ + sizeof(sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32), \ + &sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32); \ + } + +//初始化conn其余额外信息 +#define CONN_ADD_EXTRA_INFO \ + if (extra_conn_info) { \ + struct tcp_sock *tp = (struct tcp_sock *)sk; \ + conn->srtt = BPF_CORE_READ(tp, srtt_us); \ + conn->duration = bpf_ktime_get_ns() / 1000 - conn->init_timestamp; \ + conn->bytes_acked = BPF_CORE_READ(tp, bytes_acked); \ + conn->bytes_received = BPF_CORE_READ(tp, bytes_received); \ + conn->snd_cwnd = BPF_CORE_READ(tp, snd_cwnd); \ + conn->rcv_wnd = BPF_CORE_READ(tp, rcv_wnd); \ + conn->snd_ssthresh = BPF_CORE_READ(tp, snd_ssthresh); \ + conn->total_retrans = BPF_CORE_READ(tp, total_retrans); \ + conn->sndbuf = BPF_CORE_READ(sk, sk_sndbuf); \ + conn->sk_wmem_queued = BPF_CORE_READ(sk, sk_wmem_queued); \ + conn->tcp_backlog = BPF_CORE_READ(sk, sk_ack_backlog); \ + conn->max_tcp_backlog = BPF_CORE_READ(sk, sk_max_ack_backlog); \ + } + +#define CONN_INFO_TRANSFER tinfo->sk = conn->sock; // 将conn->sock赋给tinfo->sk + +#define PACKET_INIT_WITH_COMMON_INFO \ + struct pack_t *packet; \ + packet = bpf_ringbuf_reserve(&rb, sizeof(*packet), 0); \ + if (!packet) { \ + return 0; \ + } \ + packet->err = 0; \ + packet->sock = sk; \ + packet->ack = pkt_tuple.ack; \ + packet->seq = pkt_tuple.seq; + +#define READ_ONCE(x) (*(volatile typeof(x) *)&(x)) +#define WRITE_ONCE(x, val) ((*(volatile typeof(x) *)&(x)) = val) + +#define INIT_PACKET_TCP_TUPLE(sk, pkt) \ + struct packet_tuple pkt = { \ + .saddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr), \ + .daddr = BPF_CORE_READ(sk, __sk_common.skc_daddr), \ + .sport = BPF_CORE_READ(sk, __sk_common.skc_num), \ + .dport = __bpf_ntohs(BPF_CORE_READ(sk, __sk_common.skc_dport)), \ + .tran_flag = TCP} + +#define INIT_PACKET_UDP_TUPLE(sk, pkt) \ + struct packet_tuple pkt = { \ + .saddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr), \ + .daddr = BPF_CORE_READ(sk, __sk_common.skc_daddr), \ + .sport = BPF_CORE_READ(sk, __sk_common.skc_num), \ + .dport = __bpf_ntohs(BPF_CORE_READ(sk, __sk_common.skc_dport)), \ + .tran_flag = UDP} +/* help macro end */ + +/* help functions */ +// 将struct sock类型的指针转化为struct tcp_sock类型的指针 +static __always_inline struct tcp_sock *tcp_sk(const struct sock *sk) { + return (struct tcp_sock *)sk; +} +// 将struct sk_buff类型的指针转化为struct udphdr类型的指针 +static __always_inline struct udphdr *skb_to_udphdr(const struct sk_buff *skb) { + return (struct udphdr *)(( + BPF_CORE_READ(skb, head) + // 报文头部偏移 + BPF_CORE_READ(skb, transport_header))); // 传输层部分偏移 +} +// 将struct sk_buff类型的指针转化为struct tcphdr类型的指针 +static __always_inline struct tcphdr *skb_to_tcphdr(const struct sk_buff *skb) { + return (struct tcphdr *)(( + BPF_CORE_READ(skb, head) + // 报文头部偏移 + BPF_CORE_READ(skb, transport_header))); // 传输层部分偏移 +} +// 将struct sk_buff类型的指针转化为struct iphdr类型的指针 +static __always_inline struct iphdr *skb_to_iphdr(const struct sk_buff *skb) { + return (struct iphdr *)(BPF_CORE_READ(skb, head) + + BPF_CORE_READ(skb, network_header)); +} +// 将struct sk_buff类型的指针转化为struct ipv6hdr类型的指针 +static __always_inline struct ipv6hdr * +skb_to_ipv6hdr(const struct sk_buff *skb) { + return (struct ipv6hdr *)(BPF_CORE_READ(skb, head) + + BPF_CORE_READ(skb, network_header)); +} +// 初始化ip_packet +static void get_ip_pkt_tuple(struct ip_packet *ipk, struct iphdr *ip) { + ipk->saddr = BPF_CORE_READ(ip, saddr); + ipk->daddr = BPF_CORE_READ(ip, daddr); +} + +// 初始化packet_tuple结构指针pkt_tuple +static __always_inline void get_pkt_tuple(struct packet_tuple *pkt_tuple, + struct iphdr *ip, + struct tcphdr *tcp) { + pkt_tuple->saddr = BPF_CORE_READ(ip, saddr); + pkt_tuple->daddr = BPF_CORE_READ(ip, daddr); + u16 sport = BPF_CORE_READ(tcp, source); + u16 dport = BPF_CORE_READ(tcp, dest); + pkt_tuple->sport = __bpf_ntohs(sport); + //__bpf_ntohs根据字节序来转化为真实值(16位) 网络传输中为大端序(即为真实值) + pkt_tuple->dport = __bpf_ntohs(dport); + u32 seq = BPF_CORE_READ(tcp, seq); + u32 ack = BPF_CORE_READ(tcp, ack_seq); + pkt_tuple->seq = __bpf_ntohl(seq); + //__bpf_ntohls根据字节序来转化为真实值(32位) + pkt_tuple->ack = __bpf_ntohl(ack); + + pkt_tuple->tran_flag = TCP; // tcp包 + + pkt_tuple->saddr_v6 = 0; + pkt_tuple->daddr_v6 = 0; + pkt_tuple->len = 0; +} +// 初始化packet_tuple结构指针pkt_tuple +static __always_inline void get_udp_pkt_tuple(struct packet_tuple *pkt_tuple, + struct iphdr *ip, + struct udphdr *udp) { + pkt_tuple->saddr = BPF_CORE_READ(ip, saddr); + pkt_tuple->daddr = BPF_CORE_READ(ip, daddr); + u16 sport = BPF_CORE_READ(udp, source); + u16 dport = BPF_CORE_READ(udp, dest); + pkt_tuple->sport = __bpf_ntohs(sport); + //__bpf_ntohs根据字节序来转化为真实值(16位) 网络传输中为大端序(即为真实值) + pkt_tuple->dport = __bpf_ntohs(dport); + pkt_tuple->seq = 0; + pkt_tuple->ack = 0; + pkt_tuple->tran_flag = UDP; // udp包 +} + +static __always_inline void get_pkt_tuple_v6(struct packet_tuple *pkt_tuple, + struct ipv6hdr *ip6h, + struct tcphdr *tcp) { + bpf_probe_read_kernel(&pkt_tuple->saddr_v6, sizeof(pkt_tuple->saddr_v6), + &ip6h->saddr.in6_u.u6_addr32); + bpf_probe_read_kernel(&pkt_tuple->daddr_v6, sizeof(pkt_tuple->daddr_v6), + &ip6h->daddr.in6_u.u6_addr32); + u16 sport = BPF_CORE_READ(tcp, source); + u16 dport = BPF_CORE_READ(tcp, dest); + pkt_tuple->sport = __bpf_ntohs(sport); + pkt_tuple->dport = __bpf_ntohs(dport); + u32 seq = BPF_CORE_READ(tcp, seq); + u32 ack = BPF_CORE_READ(tcp, ack_seq); + pkt_tuple->seq = __bpf_ntohl(seq); + pkt_tuple->ack = __bpf_ntohl(ack); + + pkt_tuple->tran_flag = 1; // tcp包 +} +int getstack(void *ctx) { + int pid = bpf_get_current_pid_tgid() >> 32; + int cpu_id = bpf_get_smp_processor_id(); + struct stacktrace_event *event; + int cp; + + event = bpf_ringbuf_reserve(&trace_rb, sizeof(*event), 0); + if (!event) + return 1; + + event->pid = pid; + event->cpu_id = cpu_id; + + if (bpf_get_current_comm(event->comm, sizeof(event->comm))) + event->comm[0] = 0; + + event->kstack_sz = + bpf_get_stack(ctx, event->kstack, sizeof(event->kstack), 0); + bpf_ringbuf_submit(event, 0); + + return 0; +} +#if KERNEL_VERSION(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH) >= \ + KERNEL_VERSION(6, 3, 1) +#define GET_USER_DATA(msg) BPF_CORE_READ(msg, msg_iter.__iov, iov_base) +#else +#define GET_USER_DATA(msg) BPF_CORE_READ(msg, msg_iter.iov, iov_base) +#endif + +/* +例子: log2(16384) =14 +16384 2进制表示 1000000000000000 +初始值: v=16384 r=0 +1、16384 > 65535 不成立,r=0; v右移动0位 +2、16384 > 255 成立,shift = 8,v右移动8位100000000,r=0|8=8 +3、256 > 15 成立,shift = 4,v右移4位10000,r=8|4=12 +4、16 > 3 成立,shift = 2,右移2位100,r=12|2=14 +5、v=4,右移1位10,r|=2>>1=1 r=14|1=14 +*/ + +static __always_inline u64 log2(u32 v) { + u32 shift, r; + //检测v是否大于0xFFFF(65535),如果是,则将r设置为16 + r = (v > 0xFFFF) << 4; + v >>= r; //右移 + shift = (v > 0xFF) << 3; + v >>= shift; + r |= shift; + shift = (v > 0xF) << 2; + v >>= shift; + r |= shift; + shift = (v > 0x3) << 1; + v >>= shift; + r |= shift; + //右移v一位并将结果累加到r中 + r |= (v >> 1); + return r; +} +/* +例子:log2l(4294967296)=32 +4294967296 2进制表示 100000000000000000000000000000000 +1、v右移32位 1 +2、log2(1)=0 计算得0+32=32 +*/ +static __always_inline u64 log2l(u64 v) { + u32 hi = v >> 32; //取v的高32位 + // 如果高32位非0,计算高32位的对数并加32 + if (hi) + return log2(hi) + 32; + else + return log2(v); +} + +/* help functions end */ + +#endif diff --git a/MagicEyes/src/backend/net/net_watcher/bpf/drop.bpf.h b/MagicEyes/src/backend/net/net_watcher/bpf/drop.bpf.h new file mode 100644 index 000000000..56848f52f --- /dev/null +++ b/MagicEyes/src/backend/net/net_watcher/bpf/drop.bpf.h @@ -0,0 +1,48 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: blown.away@qq.com +// net_watcher libbpf 丢包 + +#include "common.bpf.h" +static __always_inline +int __tp_kfree(struct trace_event_raw_kfree_skb *ctx) +{ + if(!drop_reason) + return 0; + struct sk_buff *skb=ctx->skbaddr; + if (skb == NULL) // 判断是否为空 + return 0; + struct iphdr *ip = skb_to_iphdr(skb); + struct tcphdr *tcp = skb_to_tcphdr(skb); + struct packet_tuple pkt_tuple = {0}; + get_pkt_tuple(&pkt_tuple, ip, tcp); + + struct reasonissue *message; + message = bpf_ringbuf_reserve(&kfree_rb, sizeof(*message), 0); + if(!message){ + return 0; + } + message->saddr = pkt_tuple.saddr; + message->daddr = pkt_tuple.daddr; + message->sport = pkt_tuple.sport; + message->dport = pkt_tuple.dport; + message->protocol = ctx->protocol; + message->location = (long)ctx->location; + message->drop_reason = ctx->reason; + bpf_ringbuf_submit(message,0); + if(stack_info) + getstack(ctx); + return 0; +} \ No newline at end of file diff --git a/MagicEyes/src/backend/net/net_watcher/bpf/icmp.bpf.h b/MagicEyes/src/backend/net/net_watcher/bpf/icmp.bpf.h new file mode 100644 index 000000000..46286e5c4 --- /dev/null +++ b/MagicEyes/src/backend/net/net_watcher/bpf/icmp.bpf.h @@ -0,0 +1,90 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: blown.away@qq.com +// net_watcher libbpf icmp + +#include "common.bpf.h" + +static __always_inline +int __icmp_time(struct sk_buff *skb) +{ + if(!icmp_info||skb==NULL) + return 0; + struct iphdr *ip = skb_to_iphdr(skb); + struct ip_packet ipk = {0}; + get_ip_pkt_tuple(&ipk, ip); + unsigned long long time= bpf_ktime_get_ns() / 1000; + bpf_map_update_elem(&icmp_time, &ipk, &time, BPF_ANY); + return 0; +} + +static __always_inline +int __rcvend_icmp_time(struct sk_buff *skb) +{ + if(!icmp_info) + return 0; + if(skb==NULL) + return 0; + struct iphdr *ip = skb_to_iphdr(skb); + struct ip_packet ipk = {0}; + get_ip_pkt_tuple(&ipk, ip); + unsigned long long *pre_time = bpf_map_lookup_elem(&icmp_time, &ipk); + if(pre_time==NULL) + return 0; + + unsigned long long new_time= bpf_ktime_get_ns() / 1000; + unsigned long long time=new_time-*pre_time; + struct icmptime *message; + message = bpf_ringbuf_reserve(&icmp_rb, sizeof(*message), 0); + if(!message){ + return 0; + } + + message->saddr = ipk.saddr; + message->daddr =ipk.daddr; + message->icmp_tran_time =time; + message->flag =0; + bpf_ringbuf_submit(message,0); + return 0; +} + +static __always_inline +int __reply_icmp_time(struct sk_buff *skb) +{ + if(!icmp_info) + return 0; + if(skb==NULL) + return 0; + struct iphdr *ip = skb_to_iphdr(skb); + struct ip_packet ipk = {0}; + get_ip_pkt_tuple(&ipk, ip); + unsigned long long *pre_time = bpf_map_lookup_elem(&icmp_time, &ipk); + if(pre_time==NULL) + return 0; + unsigned long long new_time= bpf_ktime_get_ns() / 1000; + unsigned long long time=new_time-*pre_time; + struct icmptime *message; + message = bpf_ringbuf_reserve(&icmp_rb, sizeof(*message), 0); + if(!message){ + return 0; + } + + message->saddr = ipk.saddr; + message->daddr =ipk.daddr; + message->icmp_tran_time =time; + message->flag =1; + bpf_ringbuf_submit(message,0); + return 0; +} \ No newline at end of file diff --git a/MagicEyes/src/backend/net/net_watcher/bpf/mysql.bpf.h b/MagicEyes/src/backend/net/net_watcher/bpf/mysql.bpf.h new file mode 100644 index 000000000..13e802887 --- /dev/null +++ b/MagicEyes/src/backend/net/net_watcher/bpf/mysql.bpf.h @@ -0,0 +1,78 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: blown.away@qq.com +// mysql + +#include "common.bpf.h" +#include "mysql_helper.bpf.h" +static __always_inline int __handle_mysql_start(struct pt_regs *ctx) { + // dispatch_command(THD *thd, const COM_DATA *com_data, enum + enum enum_server_command command = PT_REGS_PARM3(ctx); + union COM_DATA *com_data = (union COM_DATA *)PT_REGS_PARM2(ctx); + pid_t pid = bpf_get_current_pid_tgid() >> 32; + pid_t tid = bpf_get_current_pid_tgid(); + void *thd = (void *)PT_REGS_PARM1(ctx); + struct query_info info; + u32 size = 0; + char *sql; + + if (command != COM_QUERY) { + return 0; + } + + bpf_probe_read(&info.size, sizeof(info.size), &com_data->com_query.length); + bpf_probe_read_str(&sql, sizeof(sql), &com_data->com_query.query); + bpf_probe_read_str(&info.msql, sizeof(info.msql), sql); + // bpf_printk("sql1==%s size1==%lu", info.msql,info.size); + info.start_time = bpf_ktime_get_ns() / 1000; + + bpf_map_update_elem(&queries, &tid, &info, BPF_ANY); + return 0; +} + +static __always_inline int __handle_mysql_end(struct pt_regs *ctx) { + char comm[16]; + pid_t pid = bpf_get_current_pid_tgid() >> 32; + pid_t tid = bpf_get_current_pid_tgid(); + struct query_info *info = bpf_map_lookup_elem(&queries, &tid); + if (!info) { + return 0; + } + + struct mysql_query *message = + bpf_ringbuf_reserve(&mysql_rb, sizeof(*message), 0); + if (!message) { + return 0; + } + u64 *count_ptr, count = 1; + count_ptr = bpf_map_lookup_elem(&sql_count, &tid); + if (count_ptr) { + count = *count_ptr + 1; + } + + message->count = count; + bpf_map_update_elem(&sql_count, &tid, &count, BPF_ANY); + message->duratime = bpf_ktime_get_ns() / 1000 - info->start_time; + message->pid = pid; + message->tid = tid; + bpf_get_current_comm(&message->comm, sizeof(comm)); + message->size = info->size; + bpf_probe_read_str(&message->msql, sizeof(message->msql), info->msql); + // bpf_printk("C==%d D==%lu S==%lu SQL==%s",count, + // message->duratime,message->size,message->msql); + + bpf_ringbuf_submit(message, 0); + return 0; +} diff --git a/MagicEyes/src/backend/net/net_watcher/bpf/mysql_helper.bpf.h b/MagicEyes/src/backend/net/net_watcher/bpf/mysql_helper.bpf.h new file mode 100644 index 000000000..67847d9a3 --- /dev/null +++ b/MagicEyes/src/backend/net/net_watcher/bpf/mysql_helper.bpf.h @@ -0,0 +1,171 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: blown.away@qq.com +// +// net_watcher libbpf 内核<->用户 传递信息相关结构体 + +#ifndef __MYSQL_HELPER_BPF_H +#define __MYSQL_HELPER_BPF_H + +#include "net_watcher.h" +#include "vmlinux.h" +#include +#include +#include +#include +#include +#include + +enum enum_server_command { + COM_SLEEP, + COM_QUIT, + COM_INIT_DB, + COM_QUERY, + COM_FIELD_LIST, + COM_CREATE_DB, + COM_DROP_DB, + COM_REFRESH, + COM_SHUTDOWN, + COM_STATISTICS, + COM_PROCESS_INFO, + COM_CONNECT, + COM_PROCESS_KILL, + COM_DEBUG, + COM_PING, + COM_TIME, + COM_DELAYED_INSERT, + COM_CHANGE_USER, + COM_BINLOG_DUMP, + COM_TABLE_DUMP, + COM_CONNECT_OUT, + COM_REGISTER_SLAVE, + COM_STMT_PREPARE, + COM_STMT_EXECUTE, + COM_STMT_SEND_LONG_DATA, + COM_STMT_CLOSE, + COM_STMT_RESET, + COM_SET_OPTION, + COM_STMT_FETCH, + COM_DAEMON, + COM_BINLOG_DUMP_GTID, + COM_RESET_CONNECTION, + /* don't forget to update const char *command_name[] in sql_parse.cc */ + /* Must be last */ + COM_END +}; + +typedef struct st_com_init_db_data { + const char *db_name; + unsigned long length; +} COM_INIT_DB_DATA; + +#define MYSQL_SHUTDOWN_KILLABLE_CONNECT (unsigned char)(1 << 0) +#define MYSQL_SHUTDOWN_KILLABLE_TRANS (unsigned char)(1 << 1) +#define MYSQL_SHUTDOWN_KILLABLE_LOCK_TABLE (unsigned char)(1 << 2) +#define MYSQL_SHUTDOWN_KILLABLE_UPDATE (unsigned char)(1 << 3) + +#define LOCK_MODE_MASK 0xFUL +#define LOCK_TYPE_MASK 0xF0UL + +enum mysql_enum_shutdown_level { + SHUTDOWN_DEFAULT = 0, + SHUTDOWN_WAIT_CONNECTIONS = MYSQL_SHUTDOWN_KILLABLE_CONNECT, + SHUTDOWN_WAIT_TRANSACTIONS = MYSQL_SHUTDOWN_KILLABLE_TRANS, + SHUTDOWN_WAIT_UPDATES = MYSQL_SHUTDOWN_KILLABLE_UPDATE, + SHUTDOWN_WAIT_ALL_BUFFERS = (MYSQL_SHUTDOWN_KILLABLE_UPDATE << 1), + SHUTDOWN_WAIT_CRITICAL_BUFFERS = (MYSQL_SHUTDOWN_KILLABLE_UPDATE << 1) + 1, + KILL_QUERY = 254, + KILL_CONNECTION = 255 +}; + +typedef struct st_com_refresh_data { + unsigned char options; +} COM_REFRESH_DATA; + +typedef struct st_com_shutdown_data { + enum mysql_enum_shutdown_level level; +} COM_SHUTDOWN_DATA; + +typedef struct st_com_kill_data { + unsigned long id; +} COM_KILL_DATA; + +typedef struct st_com_set_option_data { + unsigned int opt_command; +} COM_SET_OPTION_DATA; + +typedef struct st_com_stmt_execute_data { + unsigned long stmt_id; + unsigned long flags; + unsigned char *params; + unsigned long params_length; +} COM_STMT_EXECUTE_DATA; + +typedef struct st_com_stmt_fetch_data { + unsigned long stmt_id; + unsigned long num_rows; +} COM_STMT_FETCH_DATA; + +typedef struct st_com_stmt_send_long_data_data { + unsigned long stmt_id; + unsigned int param_number; + unsigned char *longdata; + unsigned long length; +} COM_STMT_SEND_LONG_DATA_DATA; + +typedef struct st_com_stmt_prepare_data { + const char *query; + unsigned int length; +} COM_STMT_PREPARE_DATA; + +typedef struct st_stmt_close_data { + unsigned int stmt_id; +} COM_STMT_CLOSE_DATA; + +typedef struct st_com_stmt_reset_data { + unsigned int stmt_id; +} COM_STMT_RESET_DATA; + +typedef struct st_com_query_data { + const char *query; + unsigned int length; +} COM_QUERY_DATA; + +typedef struct st_com_field_list_data { + unsigned char *table_name; + unsigned int table_name_length; + const unsigned char *query; + unsigned int query_length; +} COM_FIELD_LIST_DATA; + +union COM_DATA { + COM_INIT_DB_DATA com_init_db; + COM_REFRESH_DATA com_refresh; + COM_SHUTDOWN_DATA com_shutdown; + COM_KILL_DATA com_kill; + COM_SET_OPTION_DATA com_set_option; + COM_STMT_EXECUTE_DATA com_stmt_execute; + COM_STMT_FETCH_DATA com_stmt_fetch; + COM_STMT_SEND_LONG_DATA_DATA com_stmt_send_long_data; + COM_STMT_PREPARE_DATA com_stmt_prepare; + COM_STMT_CLOSE_DATA com_stmt_close; + COM_STMT_RESET_DATA com_stmt_reset; + COM_QUERY_DATA com_query; + COM_FIELD_LIST_DATA com_field_list; +}; + +/* help functions end */ + +#endif diff --git a/MagicEyes/src/backend/net/net_watcher/bpf/net_watcher.bpf.c b/MagicEyes/src/backend/net/net_watcher/bpf/net_watcher.bpf.c index 7aef79b8c..5db4a8064 100644 --- a/MagicEyes/src/backend/net/net_watcher/bpf/net_watcher.bpf.c +++ b/MagicEyes/src/backend/net/net_watcher/bpf/net_watcher.bpf.c @@ -16,509 +16,57 @@ // // tcpwatch libbpf 内核函数 -#include "net_watcher.h" -#include "vmlinux.h" -#include -#include -#include -#include -#include -#include +#include "common.bpf.h" -struct ktime_info { // us time stamp info发送数据包 - unsigned long long qdisc_time; // tx包离开mac层时间戳 - unsigned long long mac_time; // tx、rx包到达mac层时间戳 - unsigned long long ip_time; // tx、rx包到达ip层时间戳 - // unsigned long long tcp_time; // tx、rx包到达tcp层时间戳 - unsigned long long tran_time; // tx、rx包到达传输层时间戳 - unsigned long long app_time; // rx包离开tcp层时间戳 - void *sk; // 此包所属 socket套接字 - unsigned char data[MAX_HTTP_HEADER]; // 用户层数据 -}; - -struct packet_tuple { - unsigned __int128 saddr_v6; // ipv6 源地址 - unsigned __int128 daddr_v6; // ipv6 目的地址 - unsigned int saddr; // 源地址 - unsigned int daddr; // 目的地址 - unsigned short sport; // 源端口号 - unsigned short dport; // 目的端口号 - unsigned int seq; // seq报文序号 - unsigned int ack; // ack确认号 - unsigned int tran_flag; // 1:tcp 2:udp - unsigned int len; -}; -struct filtertime { - unsigned long long ip_rcv_time; - unsigned long long ip_local_deliver_time; - unsigned long long ip_local_deliver_finish_time; - unsigned long long ip__forward_time; - unsigned long long ip_local_out_time; - unsigned long long ip_output_time; - unsigned long long ip_finish_output_time; - unsigned long long ipv6_rcv_time; - -}; -// 操作BPF映射的一个辅助函数 -static __always_inline void * //__always_inline强制内联 -bpf_map_lookup_or_try_init(void *map, const void *key, const void *init) { - void *val; - long err; - - val = bpf_map_lookup_elem(map, key); // 在BPF映射中查找具有给定键的条目 - if (val) - return val; - // 此时没有对应key的value - err = bpf_map_update_elem(map, key, init, - BPF_NOEXIST); // 向BPF映射中插入或更新一个条目 - if (err && err != -EEXIST) // 插入失败 - return 0; - - return bpf_map_lookup_elem(map, key); // 返回对应value值 -} - -char LICENSE[] SEC("license") = "Dual BSD/GPL"; +#include "netfilter.bpf.h" -#define MAX_CONN 1000 +#include "icmp.bpf.h" -// 存储每个packet_tuple包所对应的ktime_info时间戳 -struct { - __uint(type, BPF_MAP_TYPE_LRU_HASH); - __uint(max_entries, MAX_CONN *MAX_PACKET); - __type(key, struct packet_tuple); - __type(value, struct ktime_info); -} timestamps SEC(".maps"); +#include "tcp.bpf.h" -// 包相关信息通过此buffer提供给userspace -struct { - __uint(type, BPF_MAP_TYPE_RINGBUF); - __uint(max_entries, 256 * 1024); -} rb SEC(".maps"); +#include "packet.bpf.h" -struct { - __uint(type, BPF_MAP_TYPE_RINGBUF); - __uint(max_entries, 256 * 1024); -} udp_rb SEC(".maps"); +#include "udp.bpf.h" -struct { - __uint(type, BPF_MAP_TYPE_RINGBUF); - __uint(max_entries, 256 * 1024); -} netfilter_rb SEC(".maps"); -// 存储每个tcp连接所对应的conn_t -struct { - __uint(type, BPF_MAP_TYPE_LRU_HASH); - __uint(max_entries, MAX_CONN); - __type(key, struct sock *); - __type(value, struct conn_t); -} conns_info SEC(".maps"); +#include "mysql.bpf.h" -// 根据ptid存储sock指针,从而在上下文无sock的内核探测点获得sock -struct { - __uint(type, BPF_MAP_TYPE_LRU_HASH); - __uint(max_entries, MAX_CONN); - __type(key, u64); - __type(value, struct sock *); -} sock_stores SEC(".maps"); - -// 存储每个pid所对应的udp包 -struct { - __uint(type, BPF_MAP_TYPE_LRU_HASH); - __uint(max_entries, MAX_CONN *MAX_PACKET); - __type(key, int); - __type(value, struct packet_tuple); -} pid_UDP SEC(".maps"); - -struct { - __uint(type, BPF_MAP_TYPE_LRU_HASH); - __uint(max_entries, MAX_CONN *MAX_PACKET); - __type(key, int); - __type(value, struct packet_tuple); -} pid_filter SEC(".maps"); - -struct { - __uint(type, BPF_MAP_TYPE_LRU_HASH); - __uint(max_entries, MAX_CONN *MAX_PACKET); - __type(key, u32); - __type(value, struct filtertime); -} netfilter_time SEC(".maps"); - -const volatile int filter_dport = 0; -const volatile int filter_sport = 0; -const volatile int all_conn = 0, err_packet = 0, extra_conn_info = 0, - layer_time = 0, http_info = 0, retrans_info = 0, udp_info =0,net_filter = 0; - -/* help macro */ - -// 连接的目标端口是否匹配于filter_dport的值 -#define FILTER_DPORT \ - if (filter_dport) { \ - if (conn.dport != filter_dport) { \ - return 0; \ - } \ - } -// 连接的源端口是否匹配于filter_sport的值 -#define FILTER_SPORT \ - if (filter_sport) { \ - if (conn.sport != filter_sport) { \ - return 0; \ - } \ - } - -// 初始化conn_t结构 -/* -#define CONN_INIT - struct conn_t conn = {0}; //声明一各conn_t结构,并初始化为0 conn.pid = ptid ->> 32; //将ptid的高32位赋给pid conn.ptid = ptid; -//初始化ptid u16 protocol = BPF_CORE_READ(sk, sk_protocol); //读取协议字段 if -(protocol != IPPROTO_TCP) //检查其协议字段是否为IPPROTO_TCP - return 0; - bpf_get_current_comm(&conn.comm, sizeof(conn.comm)); //获取当前进程名字 - conn.sock = sk; //套接字指针sk - u16 family = BPF_CORE_READ(sk, __sk_common.skc_family); //地址族字段 - __be16 dport = BPF_CORE_READ(sk, __sk_common.skc_dport); //目标端口字段 - u16 sport = BPF_CORE_READ(sk, __sk_common.skc_num); //源端口字段 - conn.family = family; - conn.sport = sport; - conn.dport = __bpf_ntohs(dport); //字节序转换 - conn.init_timestamp = bpf_ktime_get_ns() / 1000; //将当前时间戳(s) -*/ -#define CONN_INIT \ - struct conn_t conn = {0}; \ - conn.pid = ptid >> 32; \ - conn.ptid = ptid; \ - u16 protocol = BPF_CORE_READ(sk, sk_protocol); \ - if (protocol != IPPROTO_TCP) \ - return 0; \ - bpf_get_current_comm(&conn.comm, sizeof(conn.comm)); \ - conn.sock = sk; \ - u16 family = BPF_CORE_READ(sk, __sk_common.skc_family); \ - __be16 dport = BPF_CORE_READ(sk, __sk_common.skc_dport); \ - u16 sport = BPF_CORE_READ(sk, __sk_common.skc_num); \ - conn.family = family; \ - conn.sport = sport; \ - conn.dport = __bpf_ntohs(dport); \ - conn.init_timestamp = bpf_ktime_get_ns() / 1000; -/* -初始化conn_t地址相关信息 -#define CONN_ADD_ADDRESS - if (family == AF_INET) { //Internet IP -Protocol conn.saddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr);//获取源地址 - conn.daddr = BPF_CORE_READ(sk, __sk_common.skc_daddr); //获取目的地址 - } else if (family == AF_INET6) { //IP version 6 - bpf_probe_read_kernel( //从sk中读取IPv6连接的源地址 &conn.saddr_v6, -//存放位置 sizeof(sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32), //读取大小 - &sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); //读取位置 - bpf_probe_read_kernel( //从sk中读取IPv6连接的目的地址 &conn.daddr_v6, - sizeof(sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32), - &sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32); - } -*/ -#define CONN_ADD_ADDRESS \ - if (family == AF_INET) { \ - conn.saddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr); \ - conn.daddr = BPF_CORE_READ(sk, __sk_common.skc_daddr); \ - } else if (family == AF_INET6) { \ - bpf_probe_read_kernel( \ - &conn.saddr_v6, \ - sizeof(sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32), \ - &sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); \ - bpf_probe_read_kernel( \ - &conn.daddr_v6, \ - sizeof(sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32), \ - &sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32); \ - } -/* -初始化conn其余额外信息 -#define CONN_ADD_EXTRA_INFO //添加额外信息 - if (extra_conn_info) { - struct tcp_sock *tp = (struct tcp_sock *)sk; //新建tcp_sock结构体 - conn->srtt = BPF_CORE_READ(tp, srtt_us); //平滑往返时间 - conn->duration = bpf_ktime_get_ns() / 1000 - conn->init_timestamp; // -已连接建立时长 conn->bytes_acked = BPF_CORE_READ(tp, bytes_acked); -//已确认的字节数 conn->bytes_received = BPF_CORE_READ(tp, -bytes_received);//已接收的字节数 conn->snd_cwnd = BPF_CORE_READ(tp, snd_cwnd); -//拥塞窗口大小 conn->rcv_wnd = BPF_CORE_READ(tp, rcv_wnd); //接收窗口大小 - conn->snd_ssthresh = BPF_CORE_READ(tp, snd_ssthresh); //慢启动阈值 - conn->total_retrans = BPF_CORE_READ(tp, total_retrans); //重传包数 - conn->sndbuf = BPF_CORE_READ(sk, sk_sndbuf); //发送缓冲区大小(byte) - conn->sk_wmem_queued = BPF_CORE_READ(sk, -sk_wmem_queued);//已使用的发送缓冲区 conn->tcp_backlog = BPF_CORE_READ(sk, -sk_ack_backlog); //backlog传入连接请求的当前最大排队队列大小 - conn->max_tcp_backlog = BPF_CORE_READ(sk, sk_max_ack_backlog); -//max_backlog传入连接请求的最大挂起队列大小 - } - -*/ -#define CONN_ADD_EXTRA_INFO \ - if (extra_conn_info) { \ - struct tcp_sock *tp = (struct tcp_sock *)sk; \ - conn->srtt = BPF_CORE_READ(tp, srtt_us); \ - conn->duration = bpf_ktime_get_ns() / 1000 - conn->init_timestamp; \ - conn->bytes_acked = BPF_CORE_READ(tp, bytes_acked); \ - conn->bytes_received = BPF_CORE_READ(tp, bytes_received); \ - conn->snd_cwnd = BPF_CORE_READ(tp, snd_cwnd); \ - conn->rcv_wnd = BPF_CORE_READ(tp, rcv_wnd); \ - conn->snd_ssthresh = BPF_CORE_READ(tp, snd_ssthresh); \ - conn->total_retrans = BPF_CORE_READ(tp, total_retrans); \ - conn->sndbuf = BPF_CORE_READ(sk, sk_sndbuf); \ - conn->sk_wmem_queued = BPF_CORE_READ(sk, sk_wmem_queued); \ - conn->tcp_backlog = BPF_CORE_READ(sk, sk_ack_backlog); \ - conn->max_tcp_backlog = BPF_CORE_READ(sk, sk_max_ack_backlog); \ - } - -#define CONN_INFO_TRANSFER tinfo->sk = conn->sock; // 将conn->sock赋给tinfo->sk - -/* -初始化pack_t结构 -#define PACKET_INIT_WITH_COMMON_INFO - struct pack_t *packet; //创建pack_t指针 - packet = bpf_ringbuf_reserve(&rb, sizeof(*packet), 0); -//为pack_t结构体分配内存空间 if (!packet) { //分配失败 return 0; - } - packet->err = 0; //err - packet->sock = sk; //socket 指针 - packet->ack = pkt_tuple.ack; //确认号 - packet->seq = pkt_tuple.seq; //序号 -*/ -#define PACKET_INIT_WITH_COMMON_INFO \ - struct pack_t *packet; \ - packet = bpf_ringbuf_reserve(&rb, sizeof(*packet), 0); \ - if (!packet) { \ - return 0; \ - } \ - packet->err = 0; \ - packet->sock = sk; \ - packet->ack = pkt_tuple.ack; \ - packet->seq = pkt_tuple.seq; - -/* help macro end */ - -/* help functions */ -// 将struct sock类型的指针转化为struct tcp_sock类型的指针 -static struct tcp_sock *tcp_sk(const struct sock *sk) { - return (struct tcp_sock *)sk; -} -// 将struct sk_buff类型的指针转化为struct udphdr类型的指针 -static struct udphdr *skb_to_udphdr(const struct sk_buff *skb) { - return (struct udphdr *)(( - BPF_CORE_READ(skb, head) + // 报文头部偏移 - BPF_CORE_READ(skb, transport_header))); // 传输层部分偏移 -} -// 将struct sk_buff类型的指针转化为struct tcphdr类型的指针 -static struct tcphdr *skb_to_tcphdr(const struct sk_buff *skb) { - return (struct tcphdr *)(( - BPF_CORE_READ(skb, head) + // 报文头部偏移 - BPF_CORE_READ(skb, transport_header))); // 传输层部分偏移 -} -// 将struct sk_buff类型的指针转化为struct iphdr类型的指针 -static inline struct iphdr *skb_to_iphdr(const struct sk_buff *skb) { - return (struct iphdr *)(BPF_CORE_READ(skb, head) + - BPF_CORE_READ(skb, network_header)); -} -// 将struct sk_buff类型的指针转化为struct ipv6hdr类型的指针 -static inline struct ipv6hdr *skb_to_ipv6hdr(const struct sk_buff *skb) { - return (struct ipv6hdr *)(BPF_CORE_READ(skb, head) + - BPF_CORE_READ(skb, network_header)); -} +#include "redis.bpf.h" -// 初始化packet_tuple结构指针pkt_tuple -static void get_pkt_tuple(struct packet_tuple *pkt_tuple, struct iphdr *ip, - struct tcphdr *tcp) { - pkt_tuple->saddr = BPF_CORE_READ(ip, saddr); - pkt_tuple->daddr = BPF_CORE_READ(ip, daddr); - u16 sport = BPF_CORE_READ(tcp, source); - u16 dport = BPF_CORE_READ(tcp, dest); - pkt_tuple->sport = __bpf_ntohs(sport); - //__bpf_ntohs根据字节序来转化为真实值(16位) 网络传输中为大端序(即为真实值) - pkt_tuple->dport = __bpf_ntohs(dport); - u32 seq = BPF_CORE_READ(tcp, seq); - u32 ack = BPF_CORE_READ(tcp, ack_seq); - pkt_tuple->seq = __bpf_ntohl(seq); - //__bpf_ntohls根据字节序来转化为真实值(32位) - pkt_tuple->ack = __bpf_ntohl(ack); +#include "drop.bpf.h" - pkt_tuple->tran_flag = TCP; // tcp包 -} -// 初始化packet_tuple结构指针pkt_tuple -static void get_udp_pkt_tuple(struct packet_tuple *pkt_tuple, struct iphdr *ip, - struct udphdr *udp) { - pkt_tuple->saddr = BPF_CORE_READ(ip, saddr); - pkt_tuple->daddr = BPF_CORE_READ(ip, daddr); - u16 sport = BPF_CORE_READ(udp, source); - u16 dport = BPF_CORE_READ(udp, dest); - pkt_tuple->sport = __bpf_ntohs(sport); - //__bpf_ntohs根据字节序来转化为真实值(16位) 网络传输中为大端序(即为真实值) - pkt_tuple->dport = __bpf_ntohs(dport); - pkt_tuple->seq = 0; - pkt_tuple->ack = 0; - pkt_tuple->tran_flag = UDP; // udp包 -} - -static void get_pkt_tuple_v6(struct packet_tuple *pkt_tuple, - struct ipv6hdr *ip6h, struct tcphdr *tcp) { - bpf_probe_read_kernel(&pkt_tuple->saddr_v6, sizeof(pkt_tuple->saddr_v6), - &ip6h->saddr.in6_u.u6_addr32); - bpf_probe_read_kernel(&pkt_tuple->daddr_v6, sizeof(pkt_tuple->daddr_v6), - &ip6h->daddr.in6_u.u6_addr32); - u16 sport = BPF_CORE_READ(tcp, source); - u16 dport = BPF_CORE_READ(tcp, dest); - pkt_tuple->sport = __bpf_ntohs(sport); - pkt_tuple->dport = __bpf_ntohs(dport); - u32 seq = BPF_CORE_READ(tcp, seq); - u32 ack = BPF_CORE_READ(tcp, ack_seq); - pkt_tuple->seq = __bpf_ntohl(seq); - pkt_tuple->ack = __bpf_ntohl(ack); - - pkt_tuple->tran_flag = 1; // tcp包 -} -/* help functions end */ - -/** - accecpt an TCP connection -*/ +// accecpt an TCP connection SEC("kretprobe/inet_csk_accept") int BPF_KRETPROBE(inet_csk_accept_exit, // 接受tcp连接 struct sock *sk) { // this func return a newsk - // bpf_printk("inet_accept_ret\n"); - if (sk == NULL) { // newsk is null - // bpf_printk("inet_accept_ret err: newsk is null\n"); - return 0; - } - u64 ptid = bpf_get_current_pid_tgid(); // 获取当前进程pid - - CONN_INIT // 初始化conn_t结构中基本信息 - conn.is_server = 1; - - FILTER_DPORT // 过滤目标端口 - - FILTER_SPORT // 过滤源端口 - - CONN_ADD_ADDRESS // conn_t结构中增加地址信息 - - // 更新/插入conns_info中的键值对 - int err = bpf_map_update_elem(&conns_info, &sk, &conn, BPF_ANY); - if (err) { // 更新错误 - // bpf_printk("inet_accept update err.\n"); - return 0; - } - - return 0; + return __inet_csk_accept(sk); } -/** - accecpt an TCP connection end -*/ -/** - connect an TCP connection -*/ +// connect an TCP connection SEC("kprobe/tcp_v4_connect") // 进入tcp_v4_connect int BPF_KPROBE(tcp_v4_connect, const struct sock *sk) { - // bpf_printk("tcp_v4_connect\n"); - u64 ptid = bpf_get_current_pid_tgid(); // 获取当前pid - int err = bpf_map_update_elem(&sock_stores, &ptid, &sk, BPF_ANY); - // 更新/插入sock_stores中的键值对 - if (err) { - // bpf_printk("tcp_v4_connect update sock_stores err.\n"); - return 0; - } - return 0; + return __tcp_v4_connect(sk); } SEC("kretprobe/tcp_v4_connect") // 退出tcp_v4_connect int BPF_KRETPROBE(tcp_v4_connect_exit, int ret) { - u64 ptid = bpf_get_current_pid_tgid(); // 获取当前pid - struct sock **skp = bpf_map_lookup_elem(&sock_stores, &ptid); - // 获得sock_stores中ptid对应的*sk 用skp指向 - if (skp == NULL) { - return 0; - } - // bpf_printk("tcp_v4_connect_exit\n"); - if (ret != 0) { // 连接失败 - // bpf_printk("tcp_v4_connect_exit but ret %d\n", ret); - bpf_map_delete_elem(&sock_stores, &ptid); // 删除对应键值对 - return 0; - } - struct sock *sk = *skp; - CONN_INIT // 初始化conn_t结构中基本信息 - conn.is_server = 0; // 主动连接 - - FILTER_DPORT // 过滤目标端口 - - FILTER_SPORT // 过滤源端口 - - CONN_ADD_ADDRESS // conn_t结构中增加地址信息 - - long err = bpf_map_update_elem(&conns_info, &sk, &conn, BPF_ANY); - // 更新conns_info中sk对应的conn - if (err) { - return 0; - } - return 0; + return __tcp_v4_connect_exit(ret); } SEC("kprobe/tcp_v6_connect") // 进入tcp_v6_connect函数 int BPF_KPROBE(tcp_v6_connect, const struct sock *sk) { - u64 pid = bpf_get_current_pid_tgid(); // 获取pid - int err = bpf_map_update_elem(&sock_stores, &pid, &sk, BPF_ANY); - // 更新sock_stores中对应pid对应的sk - if (err) { - return 0; - } - return 0; + return __tcp_v6_connect(sk); } SEC("kretprobe/tcp_v6_connect") // 退出tcp_v6_connect函数 int BPF_KRETPROBE(tcp_v6_connect_exit, int ret) { - u64 ptid = bpf_get_current_pid_tgid(); // 获取pid - struct sock **skp = bpf_map_lookup_elem(&sock_stores, &ptid); - // 获得sock_stores中ptid对应的*sk 用skp指向 - if (skp == NULL) { - return 0; - } - if (ret != 0) { // 错误 - bpf_map_delete_elem(&sock_stores, &ptid); // 删除对应键值对 - return 0; - } - struct sock *sk = *skp; - - CONN_INIT // 初始化conn_t结构中基本信息 - conn.is_server = 0; // 主动连接 - - FILTER_DPORT // 过滤目标端口 - - FILTER_SPORT // 过滤源端口 - - CONN_ADD_ADDRESS // conn_t结构中增加地址信息 - - long err = bpf_map_update_elem(&conns_info, &sk, &conn, BPF_ANY); - // 更新conns_info中sk对应的conn - if (err) { - return 0; - } - // bpf_printk("tcp_v4_connect_exit update sk: %p.\n", sk); - return 0; + return __tcp_v6_connect_exit(ret); } -/** - connect an TCP connection end -*/ - -/* erase CLOSED TCP connection */ +// erase CLOSED TCP connection SEC("kprobe/tcp_set_state") int BPF_KPROBE(tcp_set_state, struct sock *sk, int state) { - if (all_conn) { - return 0; - } - struct conn_t *value = bpf_map_lookup_elem(&conns_info, &sk); - // 查找sk对应的conn_t - if (state == TCP_CLOSE && value != NULL) { // TCP_CLOSE置1 说明关闭连接 - // delete - bpf_map_delete_elem(&sock_stores, &value->ptid); // 删除sock_stores - bpf_map_delete_elem(&conns_info, &sk); // 删除conns_info - } - return 0; + return __tcp_set_state(sk, state); } -/* erase CLOSED TCP connection end*/ /*! in_ipv4: @@ -553,380 +101,61 @@ int BPF_KPROBE(tcp_set_state, struct sock *sk, int state) { /** in ipv4 && ipv6 */ SEC("kprobe/eth_type_trans") // 进入eth_type_trans int BPF_KPROBE(eth_type_trans, struct sk_buff *skb) { - const struct ethhdr *eth = - (struct ethhdr *)BPF_CORE_READ(skb, data); // 读取里面的报文数据 - u16 protocol = BPF_CORE_READ(eth, h_proto); // 读取包ID - // bpf_printk("protocol: %d\n", __bpf_ntohs(protocol)); - if (protocol == __bpf_htons(ETH_P_IP)) { // Protocol is IP 0x0800 - // 14 --> sizeof(struct ethhdr) / define - struct iphdr *ip = - (struct iphdr *)(BPF_CORE_READ(skb, data) + - 14); // 链路层头部长度为14 源端口6字节 - // 目的端口6字节 类型2字节 - struct tcphdr *tcp = (struct tcphdr *)(BPF_CORE_READ(skb, data) + - sizeof(struct iphdr) + 14); - struct packet_tuple pkt_tuple = {0}; // 声明packet_tuple结构pkt_tuple - get_pkt_tuple(&pkt_tuple, ip, tcp); // 初始化pkt_tuple - - struct ktime_info *tinfo, zero = {0}; // 定义ktime_info结构zero以及tinfo - - tinfo = (struct ktime_info *)bpf_map_lookup_or_try_init( - ×tamps, &pkt_tuple, &zero); - if (tinfo == NULL) { // 初始化失败 - // bpf_printk("v4 rx tinfo init fail.\n"); - return 0; - } - // 成功则获取当前内核时间并转换成毫秒 - tinfo->mac_time = bpf_ktime_get_ns() / 1000; - // bpf_printk("v4 rx init.\n"); - } else if (protocol == __bpf_htons(ETH_P_IPV6)) { // Protocol is IPV6 - struct ipv6hdr *ip6h = - (struct ipv6hdr *)(BPF_CORE_READ(skb, data) + 14); - struct tcphdr *tcp = (struct tcphdr *)(BPF_CORE_READ(skb, data) + - sizeof(struct ipv6hdr) + 14); - struct packet_tuple pkt_tuple = {0}; - get_pkt_tuple_v6(&pkt_tuple, ip6h, tcp); - - struct ktime_info *tinfo, zero = {0}; - - tinfo = (struct ktime_info *)bpf_map_lookup_or_try_init( - ×tamps, &pkt_tuple, &zero); - if (tinfo == NULL) { - // bpf_printk("v6 rx tinfo init fail.\n"); - return 0; - } - tinfo->mac_time = bpf_ktime_get_ns() / 1000; - // bpf_printk("v6 rx init.\n"); + if (protocol_count) { + return sum_protocol(skb, false); // receive + } else { + return __eth_type_trans(skb); } - return 0; } /** in only ipv4 */ SEC("kprobe/ip_rcv_core") // 跟踪记录ipv4数据包在内核中的处理时间 -int BPF_KPROBE(ip_rcv_core, struct sk_buff *skb) { - if (!layer_time) { - return 0; - } - if (skb == NULL) - return 0; - struct iphdr *ip = skb_to_iphdr(skb); // 通过skb获取ipv4包头信息 - struct tcphdr *tcp = skb_to_tcphdr(skb); // 获取tcp包头信息 - struct packet_tuple pkt_tuple = { - 0}; // 定义一个packet_tuple结构体变量pkt_tuple并初始化 - get_pkt_tuple(&pkt_tuple, ip, tcp); - struct ktime_info *tinfo; - tinfo = bpf_map_lookup_elem( - ×tamps, &pkt_tuple); // 在timestamps映射中查找元素pkt_tuple - if (tinfo == NULL) { - return 0; - } - tinfo->ip_time = bpf_ktime_get_ns() / 1000; - // bpf_printk("rx enter ipv4 layer.\n"); - return 0; -} +int BPF_KPROBE(ip_rcv_core, struct sk_buff *skb) { return __ip_rcv_core(skb); } /** in only ipv6 */ SEC("kprobe/ip6_rcv_core") int BPF_KPROBE(ip6_rcv_core, struct sk_buff *skb) { - if (!layer_time) { - return 0; - } - if (skb == NULL) - return 0; - struct ipv6hdr *ip6h = skb_to_ipv6hdr(skb); - struct tcphdr *tcp = skb_to_tcphdr(skb); - struct packet_tuple pkt_tuple = {0}; - get_pkt_tuple_v6(&pkt_tuple, ip6h, tcp); - - struct ktime_info *tinfo; - tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple); - if (tinfo == NULL) { - return 0; - } - - tinfo->ip_time = bpf_ktime_get_ns() / 1000; - // bpf_printk("rx enter ipv6 layer.\n"); - return 0; + return __ip6_rcv_core(skb); } /**in only ipv4 */ // 接收数据包 SEC("kprobe/tcp_v4_rcv") // 记录数据包在tcpv4层时间戳 -int BPF_KPROBE(tcp_v4_rcv, struct sk_buff *skb) { - if (!layer_time) { - return 0; - } - if (skb == NULL) - return 0; - struct iphdr *ip = skb_to_iphdr(skb); - struct tcphdr *tcp = skb_to_tcphdr(skb); - struct packet_tuple pkt_tuple = {0}; - get_pkt_tuple(&pkt_tuple, ip, tcp); - struct ktime_info *tinfo; - tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple); - if (tinfo == NULL) { - return 0; - } - tinfo->tran_time = bpf_ktime_get_ns() / 1000; - // bpf_printk("rx enter tcp4 layer.\n"); - return 0; -} +int BPF_KPROBE(tcp_v4_rcv, struct sk_buff *skb) { return __tcp_v4_rcv(skb); } /** in only ipv6 */ SEC("kprobe/tcp_v6_rcv") // 接收tcpv6数据包 -int BPF_KPROBE(tcp_v6_rcv, struct sk_buff *skb) { - if (!layer_time) { - return 0; - } - if (skb == NULL) - return 0; - struct ipv6hdr *ip6h = skb_to_ipv6hdr(skb); - struct tcphdr *tcp = skb_to_tcphdr(skb); - struct packet_tuple pkt_tuple = {0}; - get_pkt_tuple_v6(&pkt_tuple, ip6h, tcp); - - struct ktime_info *tinfo; - tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple); - if (tinfo == NULL) { - return 0; - } - tinfo->tran_time = bpf_ktime_get_ns() / 1000; - // bpf_printk("rx enter tcp6 layer.\n"); - return 0; -} +int BPF_KPROBE(tcp_v6_rcv, struct sk_buff *skb) { return __tcp_v6_rcv(skb); } // v4 & v6 do_rcv to get sk and other info SEC("kprobe/tcp_v4_do_rcv") int BPF_KPROBE(tcp_v4_do_rcv, struct sock *sk, struct sk_buff *skb) { - - if (sk == NULL || skb == NULL) - return 0; - struct conn_t *conn = bpf_map_lookup_elem(&conns_info, &sk); - if (conn == NULL) { - // bpf_printk("get a v4 rx pack but conn not record, its sock is: - // %p",sk); - return 0; - } - struct iphdr *ip = skb_to_iphdr(skb); - struct tcphdr *tcp = skb_to_tcphdr(skb); - struct packet_tuple pkt_tuple = {0}; - get_pkt_tuple(&pkt_tuple, ip, tcp); - - struct ktime_info *tinfo; - tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple); - if (tinfo == NULL) { - return 0; - } - - CONN_INFO_TRANSFER - - // bpf_printk("rx enter tcp4_do_rcv, sk: %p \n", sk); - - CONN_ADD_EXTRA_INFO - - return 0; + return __tcp_v4_do_rcv(sk, skb); } SEC("kprobe/tcp_v6_do_rcv") // tcp层包时间 int BPF_KPROBE(tcp_v6_do_rcv, struct sock *sk, struct sk_buff *skb) { - if (sk == NULL || skb == NULL) - return 0; - // bpf_printk("rx enter tcp6_do_rcv. \n"); - struct conn_t *conn = bpf_map_lookup_elem(&conns_info, &sk); - if (conn == NULL) { - // bpf_printk("get a v6 rx pack but conn not record, its sock is: %p", - // sk); - return 0; - } - - struct ipv6hdr *ip6h = skb_to_ipv6hdr(skb); - struct tcphdr *tcp = skb_to_tcphdr(skb); - struct packet_tuple pkt_tuple = {0}; - get_pkt_tuple_v6(&pkt_tuple, ip6h, tcp); // 使用ip和tcp信息填充pkt_tuple - - struct ktime_info *tinfo; - tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple); - if (tinfo == NULL) { - return 0; - } - - CONN_INFO_TRANSFER - - // bpf_printk("rx enter tcp6_do_rcv, sk: %p \n", sk); - - CONN_ADD_EXTRA_INFO - - return 0; + return __tcp_v6_do_rcv(sk, skb); } /** in ipv4 && ipv6 */ SEC("kprobe/skb_copy_datagram_iter") // 处理网络数据包,记录分析包在不同网络层之间的时间差,分ipv4以及ipv6 int BPF_KPROBE(skb_copy_datagram_iter, struct sk_buff *skb) { - if (skb == NULL) - return 0; - __be16 protocol = BPF_CORE_READ(skb, protocol); // 读取skb协议字段 - struct tcphdr *tcp = skb_to_tcphdr(skb); - struct packet_tuple pkt_tuple = {0}; - struct ktime_info *tinfo; - if (protocol == __bpf_htons(ETH_P_IP)) { /** ipv4 */ - - struct iphdr *ip = skb_to_iphdr(skb); - get_pkt_tuple(&pkt_tuple, ip, tcp); - tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple); - if (tinfo == NULL) { - return 0; - } - - tinfo->app_time = bpf_ktime_get_ns() / 1000; - } else if (protocol == __bpf_ntohs(ETH_P_IPV6)) { - /** ipv6 */ - struct ipv6hdr *ip6h = skb_to_ipv6hdr(skb); - get_pkt_tuple_v6(&pkt_tuple, ip6h, tcp); - - if ((tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple)) == NULL) { - return 0; - } - tinfo->app_time = bpf_ktime_get_ns() / 1000; - } else { - return 0; - } - /*----- record packet time info ------*/ - - if (tinfo == NULL) { - return 0; - } - struct sock *sk = tinfo->sk; - if (sk == NULL) { - return 0; - } - // bpf_printk("rx enter app layer.\n"); - - PACKET_INIT_WITH_COMMON_INFO - - if (layer_time) { - packet->mac_time = tinfo->ip_time - tinfo->mac_time; - // 计算MAC层和ip层之间的时间差 - packet->ip_time = tinfo->tran_time - tinfo->ip_time; - // 计算ip层和tcp层之间的时间差 - packet->tran_time = tinfo->app_time - tinfo->tran_time; - // 计算tcp层和应用层之间的时间差 - } - packet->rx = 1; // 数据包已经被接收 - - // RX HTTP INFO - if (http_info) { - int doff = - BPF_CORE_READ_BITFIELD_PROBED(tcp, doff); // 得用bitfield_probed - // 读取tcp头部中的数据偏移字段 - unsigned char *user_data = - (unsigned char *)((unsigned char *)tcp + (doff * 4)); - // 计算tcp的负载开始位置就是tcp头部之后的数据,将tcp指针指向tcp头部位置将其转换成unsigned - // char类型 - // doff * - // 4数据偏移值(tcp的头部长度20个字节)乘以4计算tcp头部实际字节长度,32位为单位就是4字节 - bpf_probe_read_str(packet->data, sizeof(packet->data), - user_data); // 将tcp负载数据读取到packet->data - } - bpf_ringbuf_submit(packet, 0); // 将packet提交到缓冲区 - return 0; + return __skb_copy_datagram_iter(skb); } -/**** end of receive path ****/ - -/**** receive error packet ****/ +// receive error packet /* TCP invalid seq error */ // 根据传入的数据包提取关键信息(如IP和TCP头部信息),并将这些信息与其他元数据(如套接字信息和错误标识)一同存储到BPF // ring buffer中 SEC("kprobe/tcp_validate_incoming") // 验证传入数据包的序列号 int BPF_KPROBE(tcp_validate_incoming, struct sock *sk, struct sk_buff *skb) { - if (!err_packet) { - return 0; - } - if (sk == NULL || skb == NULL) - return 0; - struct conn_t *conn = - bpf_map_lookup_elem(&conns_info, &sk); // BPFmap查找与套接字sk关联的信息 - if (conn == NULL) { - return 0; - } - struct tcp_skb_cb *tcb = TCP_SKB_CB(skb); // 数据包信息 - u32 start_seq = BPF_CORE_READ(tcb, seq); // 开始序列号 - u32 end_seq = BPF_CORE_READ(tcb, end_seq); // 结束序列号 - struct tcp_sock *tp = tcp_sk(sk); // 套接字信息 - u32 rcv_wup = BPF_CORE_READ( - tp, rcv_wup); // 接收方已经确认并准备接收的数据最后一个字节的序列号 - u32 rcv_nxt = - BPF_CORE_READ(tp, rcv_nxt); // 期望发送发下次发送的数据字节序列号 - u32 rcv_wnd = BPF_CORE_READ(tp, rcv_wnd); // tcp接收窗口大小 - u32 receive_window = rcv_wup + rcv_nxt - rcv_wnd; // 当前可用的接收窗口 - receive_window = 0; - - if (end_seq >= rcv_wup && rcv_nxt + receive_window >= start_seq) { - // bpf_printk("error_identify: tcp seq validated. \n"); - return 0; - // 检查数据包序列号是否在接收窗口内 - } - // bpf_printk("error_identify: tcp seq err. \n"); - // invalid seq - u16 family = BPF_CORE_READ( - sk, __sk_common.skc_family); // 获取套接字的地址族就是获得当前ip协议 - struct packet_tuple pkt_tuple = {0}; - if (family == AF_INET) { - struct iphdr *ip = skb_to_iphdr(skb); - struct tcphdr *tcp = skb_to_tcphdr(skb); - get_pkt_tuple(&pkt_tuple, ip, tcp); - } else if (family == AF_INET6) { - struct ipv6hdr *ip6h = skb_to_ipv6hdr(skb); - struct tcphdr *tcp = skb_to_tcphdr(skb); - get_pkt_tuple_v6(&pkt_tuple, ip6h, tcp); - } else { - return 0; - } - struct pack_t *packet; - packet = bpf_ringbuf_reserve(&rb, sizeof(*packet), 0); - if (!packet) { - return 0; - } - packet->err = 1; // 错误标记此数据包有问题 - packet->sock = sk; - packet->ack = pkt_tuple.ack; - packet->seq = pkt_tuple.seq; - bpf_ringbuf_submit(packet, 0); - return 0; + return __tcp_validate_incoming(sk, skb); } // 跟踪网络数据包检测tcp检验和错误 /* TCP invalid checksum error*/ SEC("kretprobe/__skb_checksum_complete") int BPF_KRETPROBE(__skb_checksum_complete_exit, int ret) { - if (!err_packet) { - return 0; - } - u64 pid = bpf_get_current_pid_tgid(); - struct sock **skp = bpf_map_lookup_elem(&sock_stores, &pid); - if (skp == NULL) { - return 0; - } - if (ret == 0) { - // bpf_printk("error_identify: tcp checksum validated. \n"); - return 0; - } - // bpf_printk("error_identify: tcp checksum error. \n"); - struct sock *sk = *skp; - struct conn_t *conn = bpf_map_lookup_elem(&conns_info, &sk); - if (conn == NULL) { - return 0; - } - struct pack_t *packet; - packet = bpf_ringbuf_reserve(&rb, sizeof(*packet), 0); - if (!packet) { - return 0; - } - packet->err = 2; // 校验和错误 - packet->sock = sk; // 存储socket信息到sock字段中 - bpf_ringbuf_submit(packet, 0); - - return 0; + return skb_checksum_complete(ret); } -/**** receive error packet end ****/ - /**** send path ****/ /*! * \brief: 获取数据包进入TCP层时刻的时间戳, 发送tcp层起始点 @@ -934,85 +163,7 @@ int BPF_KRETPROBE(__skb_checksum_complete_exit, int ret) { */ SEC("kprobe/tcp_sendmsg") // 跟踪tcp发送包信息 int BPF_KPROBE(tcp_sendmsg, struct sock *sk, struct msghdr *msg, size_t size) { - - struct conn_t *conn = bpf_map_lookup_elem(&conns_info, &sk); - if (conn == NULL) { - return 0; - } - - u16 family = BPF_CORE_READ(sk, __sk_common.skc_family); - struct ktime_info *tinfo, zero = {0}; // 存储时间 - struct packet_tuple pkt_tuple = {0}; // 存储数据包信息 - /** ipv4 */ - if (family == AF_INET) { - u16 dport = BPF_CORE_READ(sk, __sk_common.skc_dport); - pkt_tuple.saddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr); // 源ip - pkt_tuple.daddr = BPF_CORE_READ(sk, __sk_common.skc_daddr); // 目的ip - pkt_tuple.sport = BPF_CORE_READ(sk, __sk_common.skc_num); // 源端口 - pkt_tuple.dport = __bpf_ntohs(dport); // 目的端口并进行字节序转换 - - u32 snd_nxt = - BPF_CORE_READ(tcp_sk(sk), snd_nxt); // tcp要发送的下一个字节序列号 - u32 rcv_nxt = BPF_CORE_READ(tcp_sk(sk), - rcv_nxt); // tcp接收的下一个字节的期望序列号 - pkt_tuple.seq = snd_nxt; - pkt_tuple.ack = rcv_nxt; - pkt_tuple.tran_flag = TCP; - tinfo = (struct ktime_info *)bpf_map_lookup_or_try_init( - ×tamps, &pkt_tuple, - &zero); // timestamps的BPF map保存数据包与时间戳的映射 - if (tinfo == NULL) { - return 0; - } - tinfo->tran_time = bpf_ktime_get_ns() / 1000; - } else if (family == AF_INET6) { - // 读取ipv6源地址 - bpf_probe_read_kernel( - &pkt_tuple.saddr_v6, - sizeof(sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32), - &sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); - // 获取ipv6目的地址 - bpf_probe_read_kernel( - &pkt_tuple.daddr_v6, - sizeof(sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32), - &sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32); - // sk套接字中获取源端口号 - pkt_tuple.sport = BPF_CORE_READ(sk, __sk_common.skc_num); - // 获取目的端口 - u16 dport = BPF_CORE_READ(sk, __sk_common.skc_dport); - pkt_tuple.dport = __bpf_ntohs(dport); - - u32 snd_nxt = - BPF_CORE_READ(tcp_sk(sk), snd_nxt); // 发送的下一个字节序列号 - u32 rcv_nxt = - BPF_CORE_READ(tcp_sk(sk), rcv_nxt); // 期望接收的下一个字节序列号 - pkt_tuple.seq = snd_nxt; - pkt_tuple.ack = rcv_nxt; - pkt_tuple.tran_flag = TCP; - - tinfo = (struct ktime_info *)bpf_map_lookup_or_try_init( - ×tamps, &pkt_tuple, &zero); - if (tinfo == NULL) { - return 0; - } - tinfo->tran_time = bpf_ktime_get_ns() / 1000; - } - - CONN_INFO_TRANSFER - - CONN_ADD_EXTRA_INFO - - // TX HTTP info - if (http_info) { - unsigned char *user_data = BPF_CORE_READ(msg, msg_iter.iov, iov_base); - tinfo = (struct ktime_info *)bpf_map_lookup_or_try_init( - ×tamps, &pkt_tuple, &zero); - if (tinfo == NULL) { - return 0; - } - bpf_probe_read_str(tinfo->data, sizeof(tinfo->data), user_data); - } - return 0; + return __tcp_sendmsg(sk, msg, size); } /*! @@ -1022,32 +173,7 @@ tcp)获取ip段的数据 out only ipv4 */ SEC("kprobe/ip_queue_xmit") int BPF_KPROBE(ip_queue_xmit, struct sock *sk, struct sk_buff *skb) { - if (!layer_time) { - return 0; - } - u16 family = BPF_CORE_READ(sk, __sk_common.skc_family); - struct packet_tuple pkt_tuple = {0}; - struct ktime_info *tinfo; - struct tcphdr *tcp = skb_to_tcphdr(skb); - if (family == AF_INET) { - u16 dport; - u32 seq, ack; - pkt_tuple.saddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr); - pkt_tuple.daddr = BPF_CORE_READ(sk, __sk_common.skc_daddr); - pkt_tuple.sport = BPF_CORE_READ(sk, __sk_common.skc_num); - dport = BPF_CORE_READ(sk, __sk_common.skc_dport); - pkt_tuple.dport = __bpf_ntohs(dport); - seq = BPF_CORE_READ(tcp, seq); - ack = BPF_CORE_READ(tcp, ack_seq); - pkt_tuple.seq = __bpf_ntohl(seq); - pkt_tuple.ack = __bpf_ntohl(ack); - pkt_tuple.tran_flag = TCP; - if ((tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple)) == NULL) { - return 0; - } - tinfo->ip_time = bpf_ktime_get_ns() / 1000; - } - return 0; + return __ip_queue_xmit(sk, skb); }; /*! @@ -1057,41 +183,7 @@ tcp)获取ip段的数据 out only ipv6 */ SEC("kprobe/inet6_csk_xmit") int BPF_KPROBE(inet6_csk_xmit, struct sock *sk, struct sk_buff *skb) { - if (!layer_time) { - return 0; - } - u16 family = BPF_CORE_READ(sk, __sk_common.skc_family); - struct tcphdr *tcp = skb_to_tcphdr(skb); - struct packet_tuple pkt_tuple = {0}; - struct ktime_info *tinfo; - if (family == AF_INET6) { - u16 dport; - u32 seq, ack; - - bpf_probe_read_kernel( - &pkt_tuple.saddr_v6, - sizeof(sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32), - &sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); - - bpf_probe_read_kernel( - &pkt_tuple.daddr_v6, - sizeof(sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32), - &sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32); - - pkt_tuple.sport = BPF_CORE_READ(sk, __sk_common.skc_num); - dport = BPF_CORE_READ(sk, __sk_common.skc_dport); - pkt_tuple.dport = __bpf_ntohs(dport); - seq = BPF_CORE_READ(tcp, seq); - ack = BPF_CORE_READ(tcp, ack_seq); - pkt_tuple.seq = __bpf_ntohl(seq); - pkt_tuple.ack = __bpf_ntohl(ack); - pkt_tuple.tran_flag = TCP; - if ((tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple)) == NULL) { - return 0; - } - tinfo->ip_time = bpf_ktime_get_ns() / 1000; - } - return 0; + return __inet6_csk_xmit(sk, skb); }; /*! @@ -1100,40 +192,7 @@ int BPF_KPROBE(inet6_csk_xmit, struct sock *sk, struct sk_buff *skb) { */ SEC("kprobe/__dev_queue_xmit") int BPF_KPROBE(__dev_queue_xmit, struct sk_buff *skb) { - if (!layer_time) { - return 0; - } - // 从skb中读取以太网头部 - const struct ethhdr *eth = (struct ethhdr *)BPF_CORE_READ(skb, data); - u16 protocol = BPF_CORE_READ( - eth, - h_proto); // 以太网头部协议字段该字段存储的是以太网帧所封装的上层协议类型 - struct tcphdr *tcp = skb_to_tcphdr(skb); - struct packet_tuple pkt_tuple = {0}; - struct ktime_info *tinfo; - if (protocol == __bpf_ntohs(ETH_P_IP)) { - /** ipv4 */ - struct iphdr *ip = skb_to_iphdr(skb); - get_pkt_tuple(&pkt_tuple, ip, tcp); - - // FILTER_DPORT - // FILTER_SPORT - - if ((tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple)) == NULL) { - return 0; - } - tinfo->mac_time = bpf_ktime_get_ns() / 1000; - } else if (protocol == __bpf_ntohs(ETH_P_IPV6)) { - /** ipv6 */ - struct ipv6hdr *ip6h = skb_to_ipv6hdr(skb); - get_pkt_tuple_v6(&pkt_tuple, ip6h, tcp); - - if ((tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple)) == NULL) { - return 0; - } - tinfo->mac_time = bpf_ktime_get_ns() / 1000; - } - return 0; + return dev_queue_xmit(skb); }; /*! @@ -1142,83 +201,21 @@ int BPF_KPROBE(__dev_queue_xmit, struct sk_buff *skb) { */ SEC("kprobe/dev_hard_start_xmit") int BPF_KPROBE(dev_hard_start_xmit, struct sk_buff *skb) { - const struct ethhdr *eth = (struct ethhdr *)BPF_CORE_READ(skb, data); - u16 protocol = BPF_CORE_READ(eth, h_proto); - struct tcphdr *tcp = skb_to_tcphdr(skb); - struct packet_tuple pkt_tuple = {0}; - struct ktime_info *tinfo; - if (protocol == __bpf_ntohs(ETH_P_IP)) { - /** ipv4 */ - struct iphdr *ip = skb_to_iphdr(skb); - get_pkt_tuple(&pkt_tuple, ip, tcp); - - if ((tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple)) == NULL) { - return 0; - } - // 数据包在队列中等待的时间 - tinfo->qdisc_time = bpf_ktime_get_ns() / 1000; - } else if (protocol == __bpf_ntohs(ETH_P_IPV6)) { - /** ipv6 */ - struct ipv6hdr *ip6h = skb_to_ipv6hdr(skb); - get_pkt_tuple_v6(&pkt_tuple, ip6h, tcp); - - if ((tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple)) == NULL) { - return 0; - } - tinfo->qdisc_time = bpf_ktime_get_ns() / 1000; + if (protocol_count) { + return sum_protocol(skb, true); // send } else { - return 0; - } - - /*----- record packet time info ------*/ - if (tinfo == NULL) { - return 0; + return __dev_hard_start_xmit(skb); } - struct sock *sk = tinfo->sk; - if (!sk) { - return 0; - } - PACKET_INIT_WITH_COMMON_INFO - // 记录各层的时间差值 - if (layer_time) { - packet->tran_time = tinfo->ip_time - tinfo->tran_time; - packet->ip_time = tinfo->mac_time - tinfo->ip_time; - packet->mac_time =tinfo->qdisc_time -tinfo->mac_time; // 队列纪律层,处于网络协议栈最底层,负责实际数据传输与接收 - } - - packet->rx = 0; // 发送一个数据包 - - // TX HTTP Info - if (http_info) { - bpf_probe_read_str(packet->data, sizeof(packet->data), tinfo->data); - bpf_printk("%s", packet->data); - } - bpf_ringbuf_submit(packet, 0); - - return 0; }; -/**** send path end ****/ - -/**** retrans ****/ +// retrans /* 在进入快速恢复阶段时,不管是基于Reno或者SACK的快速恢复, * 还是RACK触发的快速恢复,都将使用函数tcp_enter_recovery进入 * TCP_CA_Recovery拥塞阶段。 */ SEC("kprobe/tcp_enter_recovery") // tcp连接进入恢复状态调用 int BPF_KPROBE(tcp_enter_recovery, struct sock *sk) { - if (!retrans_info) { - return 0; - } - struct conn_t *conn = bpf_map_lookup_elem(&conns_info, &sk); - if (conn == NULL) { - // bpf_printk("get a v4 rx pack but conn not record, its sock is: %p", - // sk); - return 0; - } - conn->fastRe += 1; // 统计进入tcp恢复状态的次数 - - return 0; + return __tcp_enter_recovery(sk); } /* Enter Loss state. If we detect SACK reneging, forget all SACK information @@ -1227,266 +224,137 @@ int BPF_KPROBE(tcp_enter_recovery, struct sock *sk) { * 在报文的重传定时器到期时,在tcp_retransmit_timer函数中,进入TCP_CA_Loss拥塞状态。 */ SEC("kprobe/tcp_enter_loss") -int BPF_KPROBE(tcp_enter_loss, struct sock *sk) { - if (!retrans_info) { - return 0; - } - struct conn_t *conn = bpf_map_lookup_elem(&conns_info, &sk); - if (conn == NULL) { - return 0; - } - conn->timeout += 1; - return 0; -} - -/**** retrans end ****/ - -/* new */ +int BPF_KPROBE(tcp_enter_loss, struct sock *sk) { return __tcp_enter_loss(sk); } +/* udp */ SEC("kprobe/udp_rcv") int BPF_KPROBE(udp_rcv, struct sk_buff *skb) { - if (!udp_info) - return 0; - if (skb == NULL) // 判断是否为空 - return 0; - struct iphdr *ip = skb_to_iphdr(skb); - struct udphdr *udp = skb_to_udphdr(skb); - struct packet_tuple pkt_tuple = {0}; - get_udp_pkt_tuple(&pkt_tuple, ip, udp); - struct ktime_info *tinfo, zero = {0}; - tinfo = (struct ktime_info *)bpf_map_lookup_or_try_init(×tamps, - &pkt_tuple, &zero); - if (tinfo == NULL) { + if (udp_info) + return __udp_rcv(skb); + else if (dns_info) + return __dns_rcv(skb); + else return 0; - } - tinfo->tran_time = bpf_ktime_get_ns() / 1000; - return 0; } SEC("kprobe/__udp_enqueue_schedule_skb") int BPF_KPROBE(__udp_enqueue_schedule_skb, struct sock *sk, struct sk_buff *skb) { - if (!udp_info) - return 0; - if (skb == NULL) // 判断是否为空 - return 0; - struct iphdr *ip = skb_to_iphdr(skb); - struct udphdr *udp = skb_to_udphdr(skb); - struct packet_tuple pkt_tuple = {0}; - u16 dport = BPF_CORE_READ(sk, __sk_common.skc_dport); - pkt_tuple.daddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr); - pkt_tuple.saddr = BPF_CORE_READ(sk, __sk_common.skc_daddr); - pkt_tuple.dport = BPF_CORE_READ(sk, __sk_common.skc_num); - pkt_tuple.sport = __bpf_ntohs(dport); - pkt_tuple.tran_flag = 2; - struct ktime_info *tinfo, zero = {0}; - tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple); - if (tinfo == NULL) { - return 0; - } - struct udp_message *message; - struct udp_message *udp_message = - bpf_map_lookup_elem(×tamps, &pkt_tuple); - ; - message = bpf_ringbuf_reserve(&udp_rb, sizeof(*message), 0); - if (!message) { - return 0; - } - message->tran_time = bpf_ktime_get_ns() / 1000 - tinfo->tran_time; - message->saddr = BPF_CORE_READ(sk, __sk_common.skc_daddr); - message->daddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr); - message->sport = __bpf_ntohs(BPF_CORE_READ(sk, __sk_common.skc_dport)); - message->dport = BPF_CORE_READ(sk, __sk_common.skc_num); - message->rx=0;//收包 - message->len=__bpf_ntohs(BPF_CORE_READ(udp,len)); - bpf_ringbuf_submit(message, 0); - return 0; + return udp_enqueue_schedule_skb(sk, skb); } -SEC("kprobe/udp_send_skb") -int BPF_KPROBE(udp_send_skb, struct sk_buff *skb,struct flowi4 *fl4) { - if (!udp_info) - return 0; - if (skb == NULL) // 判断是否为空 - return 0; - struct iphdr *ip = skb_to_iphdr(skb); - - struct udphdr *udp = skb_to_udphdr(skb); - struct packet_tuple pkt_tuple = {0}; - get_udp_pkt_tuple(&pkt_tuple, ip, udp); - - struct sock *sk = BPF_CORE_READ(skb, sk); - u16 dport = BPF_CORE_READ(sk, __sk_common.skc_dport); - pkt_tuple.daddr = BPF_CORE_READ(sk, __sk_common.skc_daddr); - pkt_tuple.saddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr); - pkt_tuple.dport = __bpf_ntohs(dport); - pkt_tuple.sport = BPF_CORE_READ(sk, __sk_common.skc_num); - pkt_tuple.tran_flag = UDP; - struct ktime_info *tinfo, zero = {0}; - tinfo = (struct ktime_info *)bpf_map_lookup_or_try_init(×tamps, - &pkt_tuple, &zero); - if (tinfo == NULL) { +SEC("kprobe/udp_send_skb") +int BPF_KPROBE(udp_send_skb, struct sk_buff *skb) { + if (udp_info) + return __udp_send_skb(skb); + else if (dns_info) + return __dns_send(skb); + else return 0; - } - tinfo->tran_time = bpf_ktime_get_ns() / 1000; - - unsigned int pid = bpf_get_current_pid_tgid(); - bpf_map_update_elem(&pid_UDP, &pid, &pkt_tuple, BPF_ANY); - - return 0; } SEC("kprobe/ip_send_skb") -int BPF_KPROBE(ip_send_skb, struct net *net,struct sk_buff *skb) { - if (!udp_info) - return 0; - if (skb == NULL) // 判断是否为空 - return 0; - unsigned int pid = bpf_get_current_pid_tgid(); - struct packet_tuple *pt = bpf_map_lookup_elem(&pid_UDP, &pid); - if (!pt) { - return 0; - } - - struct ktime_info *tinfo, zero = {0}; - struct udphdr *udp = skb_to_udphdr(skb); - tinfo = bpf_map_lookup_elem(×tamps, pt); - if (tinfo == NULL) { - return 0; - } - struct udp_message *message; - struct udp_message *udp_message = - bpf_map_lookup_elem(×tamps, pt); - message = bpf_ringbuf_reserve(&udp_rb, sizeof(*message), 0); - if (!message) { - return 0; - } - message->tran_time = bpf_ktime_get_ns() / 1000 - tinfo->tran_time; - message->saddr = pt->saddr; - message->daddr = pt->daddr; - message->sport = pt->sport; - message->dport = pt->dport; - message->rx=1; - message->len=__bpf_ntohs(BPF_CORE_READ(udp,len)); - bpf_ringbuf_submit(message, 0); - return 0; +int BPF_KPROBE(ip_send_skb, struct net *net, struct sk_buff *skb) { + return __ip_send_skb(skb); } -//netfilter +// netfilter SEC("kprobe/ip_rcv") -int BPF_KPROBE(ip_rcv, struct sk_buff *skb) { - if (skb == NULL) // 判断是否为空 - return 0; - struct iphdr *ip = skb_to_iphdr(skb); - struct tcphdr *tcp = skb_to_tcphdr(skb); - struct packet_tuple pkt_tuple = {0}; - get_pkt_tuple(&pkt_tuple, ip, tcp); - unsigned int pid = bpf_get_current_pid_tgid(); - struct filtertime *tinfo, zero = {0}; - tinfo = (struct filtertime *)bpf_map_lookup_or_try_init(&netfilter_time, - &pid, &zero); - bpf_map_update_elem(&pid_filter, &pid, &pkt_tuple, BPF_ANY); - if (tinfo == NULL) { - return 0; - } - tinfo->ip_rcv_time = bpf_ktime_get_ns() / 1000; - return 0; +int BPF_KPROBE(ip_rcv, struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) { + return store_nf_time(skb, e_ip_rcv); } SEC("kprobe/ip_local_deliver") -int BPF_KPROBE(ip_local_deliver) { - unsigned int pid = bpf_get_current_pid_tgid(); - struct filtertime *tinfo, zero = {0}; - tinfo = (struct filtertime *)bpf_map_lookup_or_try_init(&netfilter_time, - &pid, &zero); - if (tinfo == NULL) { - return 0; - } - tinfo->ip_local_deliver_time = bpf_ktime_get_ns() / 1000; - return 0; +int BPF_KPROBE(ip_local_deliver, struct sk_buff *skb) { + return store_nf_time(skb, e_ip_local_deliver); } SEC("kprobe/ip_local_deliver_finish") -int BPF_KPROBE(ip_local_deliver_finish) { - unsigned int pid = bpf_get_current_pid_tgid(); - struct packet_tuple *pkt_tuple = bpf_map_lookup_elem(&pid_filter, &pid); - if (!pkt_tuple) { - return 0; - } - struct filtertime *tinfo, zero = {0}; - tinfo = (struct filtertime *)bpf_map_lookup_or_try_init(&netfilter_time, - &pid, &zero); - if (tinfo == NULL) { - return 0; - } - tinfo->ip_local_deliver_finish_time = bpf_ktime_get_ns() / 1000; - - struct netfilter *message; - struct netfilter *netfilter =bpf_map_lookup_elem(&netfilter_time, pkt_tuple); - message = bpf_ringbuf_reserve(&netfilter_rb, sizeof(*message), 0); - if (!message) { - return 0; - } - message->saddr = pkt_tuple->saddr; - message->daddr =pkt_tuple->daddr; - message->sport =pkt_tuple->sport; - message->dport = pkt_tuple->dport; - message->local_input_time = tinfo->ip_local_deliver_finish_time - tinfo->ip_local_deliver_time; - message->pre_routing_time = tinfo->ip_local_deliver_time - tinfo->ip_rcv_time; - message->flag=1;//收包 - bpf_ringbuf_submit(message, 0); - return 0; +int BPF_KPROBE(ip_local_deliver_finish, struct net *net, struct sock *sk, + struct sk_buff *skb) { + return store_nf_time(skb, e_ip_local_deliver_finish); } SEC("kprobe/ip_local_out") -int BPF_KPROBE(ip_local_out, struct sk_buff *skb) { - unsigned int pid = bpf_get_current_pid_tgid(); - struct filtertime *tinfo, zero = {0}; - tinfo = (struct filtertime *)bpf_map_lookup_or_try_init(&netfilter_time, - &pid, &zero); - if (tinfo == NULL) { - return 0; - } - tinfo->ip_local_out_time = bpf_ktime_get_ns() / 1000; - return 0; +int BPF_KPROBE(ip_local_out, struct net *net, struct sock *sk, + struct sk_buff *skb) { + return store_nf_time(skb, e_ip_local_out); } SEC("kprobe/ip_output") -int BPF_KPROBE(ip_output) { - unsigned int pid = bpf_get_current_pid_tgid(); - struct filtertime *tinfo, zero = {0}; - tinfo = (struct filtertime *)bpf_map_lookup_or_try_init(&netfilter_time, - &pid, &zero); - if (tinfo == NULL) { - return 0; - } - tinfo->ip_output_time = bpf_ktime_get_ns() / 1000; - return 0; +int BPF_KPROBE(ip_output, struct net *net, struct sock *sk, + struct sk_buff *skb) { + return store_nf_time(skb, e_ip_output); } -SEC("kprobe/ip_finish_output") -int BPF_KPROBE(ip_finish_output) { - unsigned int pid = bpf_get_current_pid_tgid(); - struct packet_tuple *pkt_tuple = bpf_map_lookup_elem(&pid_filter, &pid); - if (!pkt_tuple) { - return 0; - } - struct filtertime *tinfo, zero = {0}; - tinfo = (struct filtertime *)bpf_map_lookup_or_try_init(&netfilter_time, - &pid, &zero); - if (tinfo == NULL) { - return 0; - } - tinfo->ip_finish_output_time = bpf_ktime_get_ns() / 1000; - struct netfilter *message; - struct netfilter *netfilter =bpf_map_lookup_elem(&netfilter_time, pkt_tuple); - message = bpf_ringbuf_reserve(&netfilter_rb, sizeof(*message), 0); - if(!message){ - return 0; - } - message->local_out_time=tinfo->ip_output_time-tinfo->ip_local_out_time; - message->post_routing_time=tinfo->ip_finish_output_time-tinfo->ip_output_time; - message->flag=2; - bpf_ringbuf_submit(message,0); - return 0; +SEC("kprobe/__ip_finish_output") +int BPF_KPROBE(__ip_finish_output, struct net *net, struct sock *sk, + struct sk_buff *skb) { + return store_nf_time(skb, e_ip_finish_output); +} + +SEC("kprobe/ip_forward") +int BPF_KPROBE(ip_forward, struct sk_buff *skb) { + return store_nf_time(skb, e_ip_forward); +} + +// drop +SEC("tp/skb/kfree_skb") +int tp_kfree(struct trace_event_raw_kfree_skb *ctx) { return __tp_kfree(ctx); } + +SEC("kprobe/icmp_rcv") +int BPF_KPROBE(icmp_rcv, struct sk_buff *skb) { return __icmp_time(skb); } + +SEC("kprobe/__sock_queue_rcv_skb") +int BPF_KPROBE(__sock_queue_rcv_skb, struct sock *sk, struct sk_buff *skb) { + return __rcvend_icmp_time(skb); +} + +SEC("kprobe/icmp_reply") +int BPF_KPROBE(icmp_reply, struct icmp_bxm *icmp_param, struct sk_buff *skb) { + return __reply_icmp_time(skb); +} + +// mysql +SEC("uprobe/_Z16dispatch_commandP3THDPK8COM_DATA19enum_server_command") +int BPF_KPROBE(query__start) { return __handle_mysql_start(ctx); } + +SEC("uretprobe/_Z16dispatch_commandP3THDPK8COM_DATA19enum_server_command") +int BPF_KPROBE(query__end) { return __handle_mysql_end(ctx); } + +//redis +SEC("uprobe/processCommand") +int BPF_KPROBE(redis_processCommand) { return __handle_redis_start(ctx); } + +SEC("uretprobe/call") +int BPF_KPROBE(redis_call) { return __handle_redis_end(ctx); } + +SEC("uprobe/lookupKey") +int BPF_UPROBE(redis_lookupKey) { + return __handle_redis_key(ctx); +} +SEC("uprobe/addReply") +int BPF_UPROBE(redis_addReply) { + return __handle_redis_value(ctx); +} +// rtt +SEC("kprobe/tcp_rcv_established") +int BPF_KPROBE(tcp_rcv_established, struct sock *sk, struct sk_buff *skb) { + return __tcp_rcv_established(sk, skb); +} + +// tcpstate +SEC("tracepoint/sock/inet_sock_set_state") +int handle_set_state(struct trace_event_raw_inet_sock_set_state *ctx) { + return __handle_set_state(ctx); +} +// RST +SEC("tracepoint/tcp/tcp_send_reset") +int handle_send_reset(struct trace_event_raw_tcp_send_reset *ctx) { + return __handle_send_reset(ctx); } +SEC("tracepoint/tcp/tcp_receive_reset") +int handle_receive_reset(struct trace_event_raw_tcp_receive_reset *ctx) { + return __handle_receive_reset(ctx); +} \ No newline at end of file diff --git a/MagicEyes/src/backend/net/net_watcher/bpf/netfilter.bpf.h b/MagicEyes/src/backend/net/net_watcher/bpf/netfilter.bpf.h new file mode 100644 index 000000000..f6eea5202 --- /dev/null +++ b/MagicEyes/src/backend/net/net_watcher/bpf/netfilter.bpf.h @@ -0,0 +1,132 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: blown.away@qq.com +// net_watcher libbpf netfilter + +#include "common.bpf.h" + +static __always_inline +int submit_nf_time(struct packet_tuple pkt_tuple, struct filtertime *tinfo, int rx) +{ + int time =0; + struct netfilter *message; + FILTER + message = bpf_ringbuf_reserve(&netfilter_rb, sizeof(*message), 0); + if(!message){ + return 0; + } + + message->saddr = pkt_tuple.saddr; + message->daddr =pkt_tuple.daddr; + message->sport =pkt_tuple.sport; + message->dport = pkt_tuple.dport; + message->local_input_time = 0; + message->pre_routing_time = 0; + message->local_out_time = 0; + message->post_routing_time = 0; + message->forward_time=0; + message->rx = rx; //收/发/转发方向 + + if(rx == 1){ + if(tinfo->time[e_ip_local_deliver_finish] && + tinfo->time[e_ip_local_deliver] && + tinfo->time[e_ip_rcv]) + { + message->local_input_time = tinfo->time[e_ip_local_deliver_finish] - + tinfo->time[e_ip_local_deliver]; + message->pre_routing_time = tinfo->time[e_ip_local_deliver] - + tinfo->time[e_ip_rcv]; + if((int)message->local_input_time < 0 || (int)message->pre_routing_time < 0){ + bpf_ringbuf_discard(message, 0); + return 0; + } + } + }else{ + if(tinfo->time[e_ip_local_deliver_finish] && + tinfo->time[e_ip_local_deliver] && + tinfo->time[e_ip_rcv] && + tinfo->time[e_ip_forward] && + tinfo->time[e_ip_output]) + { + message->local_input_time = tinfo->time[e_ip_local_deliver_finish] - + tinfo->time[e_ip_local_deliver]; + message->pre_routing_time = tinfo->time[e_ip_local_deliver] - + tinfo->time[e_ip_rcv]; + + u64 forward_time = tinfo->time[e_ip_output] - tinfo->time[e_ip_forward]; + + if((int)forward_time < 0){ + bpf_ringbuf_discard(message, 0); + return 0; + } + message->forward_time = forward_time; + message->rx = 2; + } + if(tinfo->time[e_ip_output] && + tinfo->time[e_ip_local_out] && + tinfo->time[e_ip_finish_output]) + { + message->local_out_time = tinfo->time[e_ip_output] - + tinfo->time[e_ip_local_out]; + message->post_routing_time = tinfo->time[e_ip_finish_output] - + tinfo->time[e_ip_output]; + if((int)message->local_out_time < 0 || (int)message->post_routing_time < 0){ + bpf_ringbuf_discard(message, 0); + return 0; + } + } + } + bpf_ringbuf_submit(message,0); + return 0; +} + +static __always_inline +int store_nf_time(struct sk_buff *skb, int hook) +{ + if(!net_filter) + return 0; + if (skb == NULL) // 判断是否为空 + return 0; + struct iphdr *ip = skb_to_iphdr(skb); + struct tcphdr *tcp = skb_to_tcphdr(skb); + + struct filtertime *tinfo, zero = {.init = {0}, .done={0}, .time={0}}; + if(hook == e_ip_rcv || hook == e_ip_local_out){ + tinfo = (struct filtertime *)bpf_map_lookup_or_try_init(&netfilter_time, + &skb, &zero); + if(tinfo == NULL) + return 0; + get_pkt_tuple(&tinfo->init, ip, tcp); + } + else{ + tinfo = (struct filtertime *)bpf_map_lookup_elem(&netfilter_time, &skb); + if (tinfo == NULL) { + return 0; + } + } + tinfo->time[hook] = bpf_ktime_get_ns() / 1000; + if(hook == e_ip_local_deliver_finish){ + submit_nf_time(tinfo->init, tinfo, 1); + bpf_map_delete_elem(&netfilter_time, &skb); + } + + if(hook == e_ip_finish_output){ + submit_nf_time(tinfo->init, tinfo, 0); + bpf_map_delete_elem(&netfilter_time, &skb); + } + + return 0; +} + diff --git a/MagicEyes/src/backend/net/net_watcher/bpf/packet.bpf.h b/MagicEyes/src/backend/net/net_watcher/bpf/packet.bpf.h new file mode 100644 index 000000000..c75dbea20 --- /dev/null +++ b/MagicEyes/src/backend/net/net_watcher/bpf/packet.bpf.h @@ -0,0 +1,638 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: blown.away@qq.com + +#include "common.bpf.h" +/* +in_ipv4: + kprobe/eth_type_trans + kprobe/ip_rcv_core.isra.0 + kprobe/tcp_v4_rcv + kprobe/tcp_v4_do_rcv + kprobe/skb_copy_datagram_iter + +in_ipv6: + kprobe/eth_type_trans + kprobe/ip6_rcv_core.isra.0 + kprobe/tcp_v6_rcv + kprobe/tcp_v6_do_rcv + kprobe/skb_copy_datagram_iter +*/ +static __always_inline struct packet_count *count_packet(u32 proto, + bool is_tx) { + struct packet_count *count; + struct packet_count initial_count = {0}; + + count = bpf_map_lookup_elem(&proto_stats, &proto); + if (!count) { + initial_count.tx_count = 0; + initial_count.rx_count = 0; + if (bpf_map_update_elem(&proto_stats, &proto, &initial_count, + BPF_ANY)) { + return NULL; + } + count = bpf_map_lookup_elem(&proto_stats, &proto); + if (!count) { + return NULL; + } + } + + if (is_tx) + __sync_fetch_and_add(&count->tx_count, 1); + else + __sync_fetch_and_add(&count->rx_count, 1); + return count; +} + +static __always_inline int sum_protocol(struct sk_buff *skb, bool is_tx) { + const struct ethhdr *eth = (struct ethhdr *)BPF_CORE_READ(skb, data); + u16 proto = BPF_CORE_READ(eth, h_proto); + + struct packet_info *pkt = bpf_ringbuf_reserve(&port_rb, sizeof(*pkt), 0); + if (!pkt) { + return 0; + } + + if (BPF_CORE_READ(eth, h_proto) != __bpf_htons(ETH_P_IP)) { + bpf_ringbuf_discard(pkt, 0); + return 0; + } + + struct iphdr *ip = (struct iphdr *)(BPF_CORE_READ(skb, data) + 14); + if (!ip) { + bpf_ringbuf_discard(pkt, 0); + return 0; + } + + pkt->saddr = BPF_CORE_READ(ip, saddr); + pkt->daddr = BPF_CORE_READ(ip, daddr); + pkt->proto = BPF_CORE_READ(ip, protocol); + + if (pkt->proto == IPPROTO_TCP) { + struct tcphdr *tcp = + (struct tcphdr *)(BPF_CORE_READ(skb, data) + sizeof(struct ethhdr) + + sizeof(struct iphdr)); + pkt->sport = BPF_CORE_READ(tcp, source); + pkt->dport = BPF_CORE_READ(tcp, dest); + pkt->proto = PROTO_TCP; + } else if (pkt->proto == IPPROTO_UDP) { + struct udphdr *udp = + (struct udphdr *)(BPF_CORE_READ(skb, data) + sizeof(struct ethhdr) + + sizeof(struct iphdr)); + pkt->sport = BPF_CORE_READ(udp, source); + pkt->dport = BPF_CORE_READ(udp, dest); + pkt->proto = PROTO_UDP; + } else if (pkt->proto == IPPROTO_ICMP) { + pkt->proto = PROTO_ICMP; + } else { + pkt->proto = PROTO_UNKNOWN; + } + struct packet_count *count = count_packet(pkt->proto, is_tx); + if (count) { + pkt->count.tx_count = count->tx_count; + pkt->count.rx_count = count->rx_count; + } else { + pkt->count.tx_count = 0; + pkt->count.rx_count = 0; + } + + // bpf_printk("pkt: saddr=%u, daddr=%u, proto=%u\n", pkt->saddr, pkt->daddr, + // pkt->proto); bpf_printk("sport=%d, dport=%d\n", pkt->sport, pkt->dport); + // bpf_printk("count_tx=%llu, count_rx=%llu\n", pkt->count.tx_count, + // pkt->count.rx_count); + bpf_ringbuf_submit(pkt, 0); + + return 0; +} +static __always_inline int __eth_type_trans(struct sk_buff *skb) { + const struct ethhdr *eth = + (struct ethhdr *)BPF_CORE_READ(skb, data); // 读取里面的报文数据 + u16 protocol = BPF_CORE_READ(eth, h_proto); // 读取包ID + // bpf_printk("protocol: %d\n", __bpf_ntohs(protocol)); + if (protocol == __bpf_htons(ETH_P_IP)) { // Protocol is IP 0x0800 + // 14 --> sizeof(struct ethhdr) / define + struct iphdr *ip = + (struct iphdr *)(BPF_CORE_READ(skb, data) + + 14); // 链路层头部长度为14 源端口6字节 + // 目的端口6字节 类型2字节 + struct tcphdr *tcp = (struct tcphdr *)(BPF_CORE_READ(skb, data) + + sizeof(struct iphdr) + 14); + struct packet_tuple pkt_tuple = {0}; // 声明packet_tuple结构pkt_tuple + get_pkt_tuple(&pkt_tuple, ip, tcp); // 初始化pkt_tuple + + struct ktime_info *tinfo, zero = {0}; // 定义ktime_info结构zero以及tinfo + + tinfo = (struct ktime_info *)bpf_map_lookup_or_try_init( + ×tamps, &pkt_tuple, &zero); + if (tinfo == NULL) { // 初始化失败 + // bpf_printk("v4 rx tinfo init fail.\n"); + return 0; + } + // 成功则获取当前内核时间并转换成毫秒 + tinfo->mac_time = bpf_ktime_get_ns() / 1000; + // bpf_printk("v4 rx init.\n"); + } else if (protocol == __bpf_htons(ETH_P_IPV6)) { // Protocol is IPV6 + struct ipv6hdr *ip6h = + (struct ipv6hdr *)(BPF_CORE_READ(skb, data) + 14); + struct tcphdr *tcp = (struct tcphdr *)(BPF_CORE_READ(skb, data) + + sizeof(struct ipv6hdr) + 14); + struct packet_tuple pkt_tuple = {0}; + get_pkt_tuple_v6(&pkt_tuple, ip6h, tcp); + + struct ktime_info *tinfo, zero = {0}; + + tinfo = (struct ktime_info *)bpf_map_lookup_or_try_init( + ×tamps, &pkt_tuple, &zero); + if (tinfo == NULL) { + // bpf_printk("v6 rx tinfo init fail.\n"); + return 0; + } + tinfo->mac_time = bpf_ktime_get_ns() / 1000; + // bpf_printk("v6 rx init.\n"); + } + return 0; +} + +static __always_inline int __ip_rcv_core(struct sk_buff *skb) { + if (!layer_time) { + return 0; + } + if (skb == NULL) + return 0; + struct iphdr *ip = skb_to_iphdr(skb); // 通过skb获取ipv4包头信息 + struct tcphdr *tcp = skb_to_tcphdr(skb); // 获取tcp包头信息 + struct packet_tuple pkt_tuple = { + 0}; // 定义一个packet_tuple结构体变量pkt_tuple并初始化 + get_pkt_tuple(&pkt_tuple, ip, tcp); + struct ktime_info *tinfo; + tinfo = bpf_map_lookup_elem( + ×tamps, &pkt_tuple); // 在timestamps映射中查找元素pkt_tuple + if (tinfo == NULL) { + return 0; + } + tinfo->ip_time = bpf_ktime_get_ns() / 1000; + // bpf_printk("rx enter ipv4 layer.\n"); + return 0; +} + +static __always_inline int __ip6_rcv_core(struct sk_buff *skb) { + if (!layer_time) { + return 0; + } + if (skb == NULL) + return 0; + struct ipv6hdr *ip6h = skb_to_ipv6hdr(skb); + struct tcphdr *tcp = skb_to_tcphdr(skb); + struct packet_tuple pkt_tuple = {0}; + get_pkt_tuple_v6(&pkt_tuple, ip6h, tcp); + + struct ktime_info *tinfo; + tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple); + if (tinfo == NULL) { + return 0; + } + + tinfo->ip_time = bpf_ktime_get_ns() / 1000; + // bpf_printk("rx enter ipv6 layer.\n"); + return 0; +} +static __always_inline int __tcp_v4_rcv(struct sk_buff *skb) { + if (!layer_time) { + return 0; + } + if (skb == NULL) + return 0; + struct iphdr *ip = skb_to_iphdr(skb); + struct tcphdr *tcp = skb_to_tcphdr(skb); + struct packet_tuple pkt_tuple = {0}; + get_pkt_tuple(&pkt_tuple, ip, tcp); + struct ktime_info *tinfo; + tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple); + if (tinfo == NULL) { + return 0; + } + tinfo->tran_time = bpf_ktime_get_ns() / 1000; + // bpf_printk("rx enter tcp4 layer.\n"); + return 0; +} +static __always_inline int __tcp_v6_rcv(struct sk_buff *skb) { + if (!layer_time) { + return 0; + } + if (skb == NULL) + return 0; + struct ipv6hdr *ip6h = skb_to_ipv6hdr(skb); + struct tcphdr *tcp = skb_to_tcphdr(skb); + struct packet_tuple pkt_tuple = {0}; + get_pkt_tuple_v6(&pkt_tuple, ip6h, tcp); + + struct ktime_info *tinfo; + tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple); + if (tinfo == NULL) { + return 0; + } + tinfo->tran_time = bpf_ktime_get_ns() / 1000; + // bpf_printk("rx enter tcp6 layer.\n"); + return 0; +} +static __always_inline int __tcp_v4_do_rcv(struct sock *sk, + struct sk_buff *skb) { + if (sk == NULL || skb == NULL) + return 0; + struct conn_t *conn = bpf_map_lookup_elem(&conns_info, &sk); + if (conn == NULL) { + // bpf_printk("get a v4 rx pack but conn not record, its sock is: + // %p",sk); + return 0; + } + struct iphdr *ip = skb_to_iphdr(skb); + struct tcphdr *tcp = skb_to_tcphdr(skb); + struct packet_tuple pkt_tuple = {0}; + get_pkt_tuple(&pkt_tuple, ip, tcp); + + struct ktime_info *tinfo; + tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple); + if (tinfo == NULL) { + return 0; + } + + CONN_INFO_TRANSFER + + // bpf_printk("rx enter tcp4_do_rcv, sk: %p \n", sk); + + CONN_ADD_EXTRA_INFO + + return 0; +} +static __always_inline int __tcp_v6_do_rcv(struct sock *sk, + struct sk_buff *skb) { + if (sk == NULL || skb == NULL) + return 0; + // bpf_printk("rx enter tcp6_do_rcv. \n"); + struct conn_t *conn = bpf_map_lookup_elem(&conns_info, &sk); + if (conn == NULL) { + // bpf_printk("get a v6 rx pack but conn not record, its sock is: %p", + // sk); + return 0; + } + + struct ipv6hdr *ip6h = skb_to_ipv6hdr(skb); + struct tcphdr *tcp = skb_to_tcphdr(skb); + struct packet_tuple pkt_tuple = {0}; + get_pkt_tuple_v6(&pkt_tuple, ip6h, tcp); // 使用ip和tcp信息填充pkt_tuple + + struct ktime_info *tinfo; + tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple); + if (tinfo == NULL) { + return 0; + } + + CONN_INFO_TRANSFER + + // bpf_printk("rx enter tcp6_do_rcv, sk: %p \n", sk); + + CONN_ADD_EXTRA_INFO + + return 0; +} +static __always_inline int __skb_copy_datagram_iter(struct sk_buff *skb) { + if (skb == NULL) + return 0; + __be16 protocol = BPF_CORE_READ(skb, protocol); // 读取skb协议字段 + struct tcphdr *tcp = skb_to_tcphdr(skb); + struct packet_tuple pkt_tuple = {0}; + struct ktime_info *tinfo; + if (protocol == __bpf_htons(ETH_P_IP)) { /** ipv4 */ + + struct iphdr *ip = skb_to_iphdr(skb); + get_pkt_tuple(&pkt_tuple, ip, tcp); + tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple); + if (tinfo == NULL) { + return 0; + } + + tinfo->app_time = bpf_ktime_get_ns() / 1000; + } else if (protocol == __bpf_ntohs(ETH_P_IPV6)) { + /** ipv6 */ + struct ipv6hdr *ip6h = skb_to_ipv6hdr(skb); + get_pkt_tuple_v6(&pkt_tuple, ip6h, tcp); + + if ((tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple)) == NULL) { + return 0; + } + tinfo->app_time = bpf_ktime_get_ns() / 1000; + } else { + return 0; + } + /*----- record packet time info ------*/ + + if (tinfo == NULL) { + return 0; + } + struct sock *sk = tinfo->sk; + if (sk == NULL) { + return 0; + } + // bpf_printk("rx enter app layer.\n"); + + PACKET_INIT_WITH_COMMON_INFO + packet->saddr = pkt_tuple.saddr; + packet->daddr = pkt_tuple.daddr; + packet->sport = pkt_tuple.sport; + packet->dport = pkt_tuple.dport; + + if (layer_time) { + packet->mac_time = tinfo->ip_time - tinfo->mac_time; + // 计算MAC层和ip层之间的时间差 + packet->ip_time = tinfo->tran_time - tinfo->ip_time; + // 计算ip层和tcp层之间的时间差 + packet->tran_time = tinfo->app_time - tinfo->tran_time; + // 计算tcp层和应用层之间的时间差 + } + packet->rx = 1; // 数据包已经被接收 + + // RX HTTP INFO + if (http_info) { + int doff = + BPF_CORE_READ_BITFIELD_PROBED(tcp, doff); // 得用bitfield_probed + // 读取tcp头部中的数据偏移字段 + u8 *user_data = (u8 *)((u8 *)tcp + (doff * 4)); + // 计算tcp的负载开始位置就是tcp头部之后的数据,将tcp指针指向tcp头部位置将其转换成unsigned + // char类型 + // doff * + // 4数据偏移值(tcp的头部长度20个字节)乘以4计算tcp头部实际字节长度,32位为单位就是4字节 + bpf_probe_read_str(packet->data, sizeof(packet->data), + user_data); // 将tcp负载数据读取到packet->data + } + bpf_ringbuf_submit(packet, 0); // 将packet提交到缓冲区 + return 0; +} +/* +out_ipv4: + kprobe/tcp_sendmsg + kprobe/ip_queue_xmit + kprobe/dev_queue_xmit + kprobe/dev_hard_start_xmit + +out_ipv6: + kprobe/tcp_sendmsg + kprobe/inet6_csk_xmit + kprobe/dev_queue_xmit + kprobe/dev_hard_start_xmit +*/ +static __always_inline int __tcp_sendmsg(struct sock *sk, struct msghdr *msg, + size_t size) { + struct conn_t *conn = bpf_map_lookup_elem(&conns_info, &sk); + if (conn == NULL) { + return 0; + } + + u16 family = BPF_CORE_READ(sk, __sk_common.skc_family); + struct ktime_info *tinfo, zero = {0}; // 存储时间 + struct packet_tuple pkt_tuple = {0}; // 存储数据包信息 + /** ipv4 */ + if (family == AF_INET) { + u16 dport = BPF_CORE_READ(sk, __sk_common.skc_dport); + pkt_tuple.saddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr); // 源ip + pkt_tuple.daddr = BPF_CORE_READ(sk, __sk_common.skc_daddr); // 目的ip + pkt_tuple.sport = BPF_CORE_READ(sk, __sk_common.skc_num); // 源端口 + pkt_tuple.dport = __bpf_ntohs(dport); // 目的端口并进行字节序转换 + + u32 snd_nxt = + BPF_CORE_READ(tcp_sk(sk), snd_nxt); // tcp要发送的下一个字节序列号 + u32 rcv_nxt = BPF_CORE_READ(tcp_sk(sk), + rcv_nxt); // tcp接收的下一个字节的期望序列号 + pkt_tuple.seq = snd_nxt; + pkt_tuple.ack = rcv_nxt; + pkt_tuple.tran_flag = TCP; + tinfo = (struct ktime_info *)bpf_map_lookup_or_try_init( + ×tamps, &pkt_tuple, + &zero); // timestamps的BPF map保存数据包与时间戳的映射 + if (tinfo == NULL) { + return 0; + } + tinfo->tran_time = bpf_ktime_get_ns() / 1000; + } else if (family == AF_INET6) { + // 读取ipv6源地址 + bpf_probe_read_kernel( + &pkt_tuple.saddr_v6, + sizeof(sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32), + &sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); + // 获取ipv6目的地址 + bpf_probe_read_kernel( + &pkt_tuple.daddr_v6, + sizeof(sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32), + &sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32); + // sk套接字中获取源端口号 + pkt_tuple.sport = BPF_CORE_READ(sk, __sk_common.skc_num); + // 获取目的端口 + u16 dport = BPF_CORE_READ(sk, __sk_common.skc_dport); + pkt_tuple.dport = __bpf_ntohs(dport); + + u32 snd_nxt = + BPF_CORE_READ(tcp_sk(sk), snd_nxt); // 发送的下一个字节序列号 + u32 rcv_nxt = + BPF_CORE_READ(tcp_sk(sk), rcv_nxt); // 期望接收的下一个字节序列号 + pkt_tuple.seq = snd_nxt; + pkt_tuple.ack = rcv_nxt; + pkt_tuple.tran_flag = TCP; + + tinfo = (struct ktime_info *)bpf_map_lookup_or_try_init( + ×tamps, &pkt_tuple, &zero); + if (tinfo == NULL) { + return 0; + } + tinfo->tran_time = bpf_ktime_get_ns() / 1000; + } + + CONN_INFO_TRANSFER + + CONN_ADD_EXTRA_INFO + + // TX HTTP info + if (http_info) { + u8 *user_data = GET_USER_DATA(msg); + tinfo = (struct ktime_info *)bpf_map_lookup_or_try_init( + ×tamps, &pkt_tuple, &zero); + if (tinfo == NULL) { + return 0; + } + bpf_probe_read_str(tinfo->data, sizeof(tinfo->data), user_data); + } + return 0; +} +static __always_inline int __ip_queue_xmit(struct sock *sk, + struct sk_buff *skb) { + if (!layer_time) { + return 0; + } + u16 family = BPF_CORE_READ(sk, __sk_common.skc_family); + struct packet_tuple pkt_tuple = {0}; + struct ktime_info *tinfo; + struct tcphdr *tcp = skb_to_tcphdr(skb); + if (family == AF_INET) { + u16 dport; + u32 seq, ack; + pkt_tuple.saddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr); + pkt_tuple.daddr = BPF_CORE_READ(sk, __sk_common.skc_daddr); + pkt_tuple.sport = BPF_CORE_READ(sk, __sk_common.skc_num); + dport = BPF_CORE_READ(sk, __sk_common.skc_dport); + pkt_tuple.dport = __bpf_ntohs(dport); + seq = BPF_CORE_READ(tcp, seq); + ack = BPF_CORE_READ(tcp, ack_seq); + pkt_tuple.seq = __bpf_ntohl(seq); + pkt_tuple.ack = __bpf_ntohl(ack); + pkt_tuple.tran_flag = TCP; + if ((tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple)) == NULL) { + return 0; + } + tinfo->ip_time = bpf_ktime_get_ns() / 1000; + } + + return 0; +} +static __always_inline int __inet6_csk_xmit(struct sock *sk, + struct sk_buff *skb) { + if (!layer_time) { + return 0; + } + u16 family = BPF_CORE_READ(sk, __sk_common.skc_family); + struct tcphdr *tcp = skb_to_tcphdr(skb); + struct packet_tuple pkt_tuple = {0}; + struct ktime_info *tinfo; + if (family == AF_INET6) { + u16 dport; + u32 seq, ack; + + bpf_probe_read_kernel( + &pkt_tuple.saddr_v6, + sizeof(sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32), + &sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); + + bpf_probe_read_kernel( + &pkt_tuple.daddr_v6, + sizeof(sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32), + &sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32); + + pkt_tuple.sport = BPF_CORE_READ(sk, __sk_common.skc_num); + dport = BPF_CORE_READ(sk, __sk_common.skc_dport); + pkt_tuple.dport = __bpf_ntohs(dport); + seq = BPF_CORE_READ(tcp, seq); + ack = BPF_CORE_READ(tcp, ack_seq); + pkt_tuple.seq = __bpf_ntohl(seq); + pkt_tuple.ack = __bpf_ntohl(ack); + pkt_tuple.tran_flag = TCP; + if ((tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple)) == NULL) { + return 0; + } + tinfo->ip_time = bpf_ktime_get_ns() / 1000; + } + return 0; +} +static __always_inline int dev_queue_xmit(struct sk_buff *skb) { + if (!layer_time) { + return 0; + } + // 从skb中读取以太网头部 + const struct ethhdr *eth = (struct ethhdr *)BPF_CORE_READ(skb, data); + u16 protocol = BPF_CORE_READ( + eth, + h_proto); // 以太网头部协议字段该字段存储的是以太网帧所封装的上层协议类型 + struct tcphdr *tcp = skb_to_tcphdr(skb); + struct packet_tuple pkt_tuple = {0}; + struct ktime_info *tinfo; + if (protocol == __bpf_ntohs(ETH_P_IP)) { + /** ipv4 */ + struct iphdr *ip = skb_to_iphdr(skb); + get_pkt_tuple(&pkt_tuple, ip, tcp); + + if ((tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple)) == NULL) { + return 0; + } + tinfo->mac_time = bpf_ktime_get_ns() / 1000; + } else if (protocol == __bpf_ntohs(ETH_P_IPV6)) { + /** ipv6 */ + struct ipv6hdr *ip6h = skb_to_ipv6hdr(skb); + get_pkt_tuple_v6(&pkt_tuple, ip6h, tcp); + + if ((tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple)) == NULL) { + return 0; + } + tinfo->mac_time = bpf_ktime_get_ns() / 1000; + } + return 0; +} +static __always_inline int __dev_hard_start_xmit(struct sk_buff *skb) { + const struct ethhdr *eth = (struct ethhdr *)BPF_CORE_READ(skb, data); + u16 protocol = BPF_CORE_READ(eth, h_proto); + struct tcphdr *tcp = skb_to_tcphdr(skb); + struct packet_tuple pkt_tuple = {0}; + struct ktime_info *tinfo; + if (protocol == __bpf_ntohs(ETH_P_IP)) { + /** ipv4 */ + struct iphdr *ip = skb_to_iphdr(skb); + get_pkt_tuple(&pkt_tuple, ip, tcp); + + if ((tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple)) == NULL) { + return 0; + } + // 数据包在队列中等待的时间 + tinfo->qdisc_time = bpf_ktime_get_ns() / 1000; + } else if (protocol == __bpf_ntohs(ETH_P_IPV6)) { + /** ipv6 */ + struct ipv6hdr *ip6h = skb_to_ipv6hdr(skb); + get_pkt_tuple_v6(&pkt_tuple, ip6h, tcp); + + if ((tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple)) == NULL) { + return 0; + } + tinfo->qdisc_time = bpf_ktime_get_ns() / 1000; + } else { + return 0; + } + + /*----- record packet time info ------*/ + if (tinfo == NULL) { + return 0; + } + struct sock *sk = tinfo->sk; + if (!sk) { + return 0; + } + PACKET_INIT_WITH_COMMON_INFO + packet->saddr = pkt_tuple.saddr; + packet->daddr = pkt_tuple.daddr; + packet->sport = pkt_tuple.sport; + packet->dport = pkt_tuple.dport; + // 记录各层的时间差值 + if (layer_time) { + packet->tran_time = tinfo->ip_time - tinfo->tran_time; + packet->ip_time = tinfo->mac_time - tinfo->ip_time; + packet->mac_time = + tinfo->qdisc_time - + tinfo + ->mac_time; // 队列纪律层,处于网络协议栈最底层,负责实际数据传输与接收 + } + packet->rx = 0; // 发送一个数据包 + + // TX HTTP Info + if (http_info) { + bpf_probe_read_str(packet->data, sizeof(packet->data), tinfo->data); + // bpf_printk("%s", packet->data); + } + bpf_ringbuf_submit(packet, 0); + + return 0; +} diff --git a/MagicEyes/src/backend/net/net_watcher/bpf/redis.bpf.h b/MagicEyes/src/backend/net/net_watcher/bpf/redis.bpf.h new file mode 100644 index 000000000..dc0aa533e --- /dev/null +++ b/MagicEyes/src/backend/net/net_watcher/bpf/redis.bpf.h @@ -0,0 +1,167 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: blown.away@qq.com +// redis + +#include "common.bpf.h" +#include "redis_helper.bpf.h" +#define MAXEPOLL 5 +static __always_inline int __handle_redis_start(struct pt_regs *ctx) { + if(!redis_info) return 0; + struct client *cli = (struct client *)PT_REGS_PARM1(ctx); + struct redis_query start={}; + void *ptr; + char name[100]=""; + int argv_len; + bpf_probe_read(&start.argc, sizeof(start.argc), &cli->argc); + robj **arg0; + robj *arg1; + bpf_probe_read(&arg0, sizeof(arg0), &cli->argv); + bpf_probe_read(&arg1, sizeof(arg1), &arg0[0]); + for(int i=0;iptr); + bpf_probe_read_str(&start.redis[i], sizeof(start.redis[i]), ptr); + } + pid_t pid = bpf_get_current_pid_tgid() >> 32; + u64 start_time = bpf_ktime_get_ns() / 1000; + start.begin_time=start_time; + bpf_map_update_elem(&redis_time, &pid, &start, BPF_ANY); + return 0; +} + +static __always_inline int __handle_redis_end(struct pt_regs *ctx) { + if(!redis_info) return 0; + pid_t pid = bpf_get_current_pid_tgid() >> 32; + struct redis_query *start; + u64 end_time = bpf_ktime_get_ns() / 1000; + start = bpf_map_lookup_elem(&redis_time, &pid); + if (!start) { + return 0; + } + struct redis_query *message = bpf_ringbuf_reserve(&redis_rb, sizeof(*message), 0); + if (!message) { + return 0; + } + message->pid = pid; + message->argc = start->argc; + bpf_get_current_comm(&message->comm, sizeof(message->comm)); + for(int i=0;iargc&&iredis[i], sizeof(message->redis[i]), start->redis[i]); + } + bpf_probe_read_str(&message->redis, sizeof(start->redis), start->redis); + message->duratime = end_time - start->begin_time; + bpf_ringbuf_submit(message, 0); + return 0; +} +static __always_inline int __handle_redis_key(struct pt_regs *ctx) { + if(!redis_stat) return 0; + robj *key_obj = (robj *)PT_REGS_PARM2(ctx); + char redis_key[256]; + u32 *count; + u32 initial_count = 1; + + if (!key_obj) + return 0; + + robj local_key_obj; + if (bpf_probe_read_user(&local_key_obj, sizeof(local_key_obj), key_obj) != 0) { + bpf_printk("Failed to read local_key_obj\n"); + return 0; + } + + if (!local_key_obj.ptr) { + bpf_printk("local_key_obj.ptr is null\n"); + return 0; + } + + int ret; + ret = bpf_probe_read_user_str(redis_key, sizeof(redis_key), local_key_obj.ptr); + if (ret <= 0) { + bpf_printk("Read string failed: %d\n", ret); + return 0; + } + + // 打印读取到的键值 + bpf_printk("Read key: %s\n", redis_key); + + // 查找或更新键的计数 + count = bpf_map_lookup_elem(&key_count, redis_key); + if (count) { + //bpf_printk("Found key, incrementing count\n"); + // 如果已经存在,增加计数值 + (*count)++; + bpf_map_update_elem(&key_count, redis_key, count, BPF_ANY); + } else { + //bpf_printk("Key not found, initializing count\n"); + // 如果不存在,初始化计数值为 1 + bpf_map_update_elem(&key_count, redis_key, &initial_count, BPF_ANY); + } + + // 打印调试信息 + struct redis_stat_query *message = bpf_ringbuf_reserve(&redis_stat_rb, sizeof(*message), 0); + if (!message) { + return 0; + } + message->pid=bpf_get_current_pid_tgid() >> 32; + bpf_get_current_comm(&message->comm, sizeof(message->comm)); + memcpy(message->key, redis_key, sizeof(message->key)); + message->key_count=count ? *count : initial_count; + message->value_type=0; + memset(message->value, 0, sizeof(message->value)); + bpf_printk("Key: %s\n", message->key); + bpf_printk("Count: %d\n", message->key_count); + bpf_ringbuf_submit(message, 0); + + return 0; +} +static __always_inline int __handle_redis_value(struct pt_regs *ctx) { + if(!redis_stat) return 0; + robj *key_obj = (robj *)PT_REGS_PARM2(ctx); + int ret; + char redis_value[64]; + if (!key_obj) + return 0; + robj local_key_obj; + if (bpf_probe_read_user(&local_key_obj, sizeof(local_key_obj), key_obj) != 0) { + bpf_printk("Failed to read local_key_obj\n"); + return 0; + } + if (!local_key_obj.ptr) { + bpf_printk("local_key_obj.ptr is null\n"); + return 0; + } + ret = bpf_probe_read_user_str(redis_value, sizeof(redis_value), local_key_obj.ptr); + if (ret <= 0) { + bpf_printk("Read string failed: %d\n", ret); + return 0; + } + struct redis_stat_query *message = bpf_ringbuf_reserve(&redis_stat_rb, sizeof(*message), 0); + if (!message) { + return 0; + } + message->pid=bpf_get_current_pid_tgid() >> 32; + bpf_get_current_comm(&message->comm, sizeof(message->comm)); + memset(message->key, 0, sizeof(message->key)); + message->key_count=0; + message->value_type=local_key_obj.type; + memcpy(message->value, redis_value, sizeof(message->value)); + bpf_printk("Value: %s\n", message->value); + bpf_printk("type: %d\n", message->value_type); + bpf_ringbuf_submit(message, 0); + return 0; +} \ No newline at end of file diff --git a/MagicEyes/src/backend/net/net_watcher/bpf/redis_helper.bpf.h b/MagicEyes/src/backend/net/net_watcher/bpf/redis_helper.bpf.h new file mode 100644 index 000000000..322fa66f4 --- /dev/null +++ b/MagicEyes/src/backend/net/net_watcher/bpf/redis_helper.bpf.h @@ -0,0 +1,55 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: blown.away@qq.com +// +// net_watcher libbpf 内核<->用户 传递信息相关结构体 + +#ifndef __REDIS_HELPER_BPF_H +#define __REDIS_HELPER_BPF_H + +#include "../include/net_watcher.h" +#include "vmlinux.h" +#include +#include +#include +#include +#include +#include + +#define LRU_BITS 24 +typedef struct redisObject { + unsigned type:4; + unsigned encoding:4; + unsigned lru:24; + int refcount; + void *ptr; +} robj; + +struct client { + u64 id; /* Client incremental unique ID. */ + u64 conn; + int resp; /* RESP protocol version. Can be 2 or 3. */ + u64 db; /* Pointer to currently SELECTed DB. */ + robj *name; /* As set by CLIENT SETNAME. */ + char* querybuf; /* Buffer we use to accumulate client queries. */ + unsigned long qb_pos; /* The position we have read in querybuf. */ + char* pending_querybuf; + unsigned long querybuf_peak; /* Recent (100ms or more) peak of querybuf size. */ + int argc; /* Num of arguments of current command. */ + robj **argv; /* Arguments of current command. */ + unsigned long argv_len_sum; /* Size of argv array (may be more than argc) */ +}; + +#endif diff --git a/MagicEyes/src/backend/net/net_watcher/bpf/tcp.bpf.h b/MagicEyes/src/backend/net/net_watcher/bpf/tcp.bpf.h new file mode 100644 index 000000000..abcc05698 --- /dev/null +++ b/MagicEyes/src/backend/net/net_watcher/bpf/tcp.bpf.h @@ -0,0 +1,444 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: blown.away@qq.com +// netwatcher libbpf tcp + +#include "common.bpf.h" + +static __always_inline int __inet_csk_accept(struct sock *sk) { + if (sk == NULL) { // newsk is null + // bpf_printk("inet_accept_ret err: newsk is null\n"); + return 0; + } + u64 ptid = bpf_get_current_pid_tgid(); // 获取当前进程pid + + CONN_INIT // 初始化conn_t结构中基本信息 + conn.is_server = 1; + + FILTER_DPORT // 过滤目标端口 + + FILTER_SPORT // 过滤源端口 + + CONN_ADD_ADDRESS // conn_t结构中增加地址信息 + + // 更新/插入conns_info中的键值对 + int err = bpf_map_update_elem(&conns_info, &sk, &conn, BPF_ANY); + if (err) { // 更新错误 + // bpf_printk("inet_accept update err.\n"); + return 0; + } + + return 0; +} + +static __always_inline int __tcp_v4_connect(const struct sock *sk) { + u64 ptid = bpf_get_current_pid_tgid(); // 获取当前pid + int err = bpf_map_update_elem(&sock_stores, &ptid, &sk, BPF_ANY); + // 更新/插入sock_stores中的键值对 + if (err) { + // bpf_printk("tcp_v4_connect update sock_stores err.\n"); + return 0; + } + return 0; +} + +static __always_inline int __tcp_v4_connect_exit(int ret) { + u64 ptid = bpf_get_current_pid_tgid(); // 获取当前pid + struct sock **skp = bpf_map_lookup_elem(&sock_stores, &ptid); + // 获得sock_stores中ptid对应的*sk 用skp指向 + if (skp == NULL) { + return 0; + } + // bpf_printk("tcp_v4_connect_exit\n"); + if (ret != 0) { // 连接失败 + // bpf_printk("tcp_v4_connect_exit but ret %d\n", ret); + bpf_map_delete_elem(&sock_stores, &ptid); // 删除对应键值对 + return 0; + } + struct sock *sk = *skp; + CONN_INIT // 初始化conn_t结构中基本信息 + conn.is_server = 0; // 主动连接 + + FILTER_DPORT // 过滤目标端口 + + FILTER_SPORT // 过滤源端口 + + CONN_ADD_ADDRESS // conn_t结构中增加地址信息 + + long err = bpf_map_update_elem(&conns_info, &sk, &conn, BPF_ANY); + // 更新conns_info中sk对应的conn + if (err) { + return 0; + } + return 0; +} + +static __always_inline int __tcp_v6_connect(const struct sock *sk) { + u64 pid = bpf_get_current_pid_tgid(); // 获取pid + int err = bpf_map_update_elem(&sock_stores, &pid, &sk, BPF_ANY); + // 更新sock_stores中对应pid对应的sk + if (err) { + return 0; + } + return 0; +} + +static __always_inline int __tcp_v6_connect_exit(int ret) { + u64 ptid = bpf_get_current_pid_tgid(); // 获取pid + struct sock **skp = bpf_map_lookup_elem(&sock_stores, &ptid); + // 获得sock_stores中ptid对应的*sk 用skp指向 + if (skp == NULL) { + return 0; + } + if (ret != 0) { // 错误 + bpf_map_delete_elem(&sock_stores, &ptid); // 删除对应键值对 + return 0; + } + struct sock *sk = *skp; + + CONN_INIT // 初始化conn_t结构中基本信息 + conn.is_server = 0; // 主动连接 + + FILTER_DPORT // 过滤目标端口 + + FILTER_SPORT // 过滤源端口 + + CONN_ADD_ADDRESS // conn_t结构中增加地址信息 + + long err = bpf_map_update_elem(&conns_info, &sk, &conn, BPF_ANY); + // 更新conns_info中sk对应的conn + if (err) { + return 0; + } + // bpf_printk("tcp_v4_connect_exit update sk: %p.\n", sk); + return 0; +} +static __always_inline int __tcp_set_state(struct sock *sk, int state) { + if (all_conn) { + return 0; + } + struct conn_t *value = bpf_map_lookup_elem(&conns_info, &sk); + // 查找sk对应的conn_t + if (state == TCP_CLOSE && value != NULL) { // TCP_CLOSE置1 说明关闭连接 + // delete + bpf_map_delete_elem(&sock_stores, &value->ptid); // 删除sock_stores + bpf_map_delete_elem(&conns_info, &sk); // 删除conns_info + } + return 0; +} + +// receive error packet +static __always_inline int __tcp_validate_incoming(struct sock *sk, + struct sk_buff *skb) { + if (!err_packet) { + return 0; + } + if (sk == NULL || skb == NULL) + return 0; + struct conn_t *conn = + bpf_map_lookup_elem(&conns_info, &sk); // BPFmap查找与套接字sk关联的信息 + if (conn == NULL) { + return 0; + } + struct tcp_skb_cb *tcb = TCP_SKB_CB(skb); // 数据包信息 + u32 start_seq = BPF_CORE_READ(tcb, seq); // 开始序列号 + u32 end_seq = BPF_CORE_READ(tcb, end_seq); // 结束序列号 + struct tcp_sock *tp = tcp_sk(sk); // 套接字信息 + u32 rcv_wup = BPF_CORE_READ( + tp, rcv_wup); // 接收方已经确认并准备接收的数据最后一个字节的序列号 + u32 rcv_nxt = + BPF_CORE_READ(tp, rcv_nxt); // 期望发送发下次发送的数据字节序列号 + u32 rcv_wnd = BPF_CORE_READ(tp, rcv_wnd); // tcp接收窗口大小 + u32 receive_window = rcv_wup + rcv_nxt - rcv_wnd; // 当前可用的接收窗口 + receive_window = 0; + + if (end_seq >= rcv_wup && rcv_nxt + receive_window >= start_seq) { + // bpf_printk("error_identify: tcp seq validated. \n"); + return 0; + // 检查数据包序列号是否在接收窗口内 + } + // bpf_printk("error_identify: tcp seq err. \n"); + // invalid seq + u16 family = BPF_CORE_READ( + sk, __sk_common.skc_family); // 获取套接字的地址族就是获得当前ip协议 + struct packet_tuple pkt_tuple = {0}; + if (family == AF_INET) { + struct iphdr *ip = skb_to_iphdr(skb); + struct tcphdr *tcp = skb_to_tcphdr(skb); + get_pkt_tuple(&pkt_tuple, ip, tcp); + } else if (family == AF_INET6) { + struct ipv6hdr *ip6h = skb_to_ipv6hdr(skb); + struct tcphdr *tcp = skb_to_tcphdr(skb); + get_pkt_tuple_v6(&pkt_tuple, ip6h, tcp); + } else { + return 0; + } + struct pack_t *packet; + packet = bpf_ringbuf_reserve(&rb, sizeof(*packet), 0); + if (!packet) { + return 0; + } + packet->err = 1; // 错误标记此数据包有问题 + packet->sock = sk; + packet->ack = pkt_tuple.ack; + packet->seq = pkt_tuple.seq; + bpf_ringbuf_submit(packet, 0); + return 0; +} +static __always_inline int skb_checksum_complete(int ret) { + if (!err_packet) { + return 0; + } + u64 pid = bpf_get_current_pid_tgid(); + struct sock **skp = bpf_map_lookup_elem(&sock_stores, &pid); + if (skp == NULL) { + return 0; + } + if (ret == 0) { + // bpf_printk("error_identify: tcp checksum validated. \n"); + return 0; + } + // bpf_printk("error_identify: tcp checksum error. \n"); + struct sock *sk = *skp; + struct conn_t *conn = bpf_map_lookup_elem(&conns_info, &sk); + if (conn == NULL) { + return 0; + } + struct pack_t *packet; + packet = bpf_ringbuf_reserve(&rb, sizeof(*packet), 0); + if (!packet) { + return 0; + } + packet->err = 2; // 校验和错误 + packet->sock = sk; // 存储socket信息到sock字段中 + bpf_ringbuf_submit(packet, 0); + + return 0; +} +////retrans packet +static __always_inline int __tcp_enter_recovery(struct sock *sk) { + if (!retrans_info) { + return 0; + } + struct conn_t *conn = bpf_map_lookup_elem(&conns_info, &sk); + if (conn == NULL) { + // bpf_printk("get a v4 rx pack but conn not record, its sock is: %p", + // sk); + return 0; + } + conn->fastRe += 1; // 统计进入tcp恢复状态的次数 + + return 0; +} +static __always_inline int __tcp_enter_loss(struct sock *sk) { + if (!retrans_info) { + return 0; + } + struct conn_t *conn = bpf_map_lookup_elem(&conns_info, &sk); + if (conn == NULL) { + return 0; + } + conn->timeout += 1; + return 0; +} +static __always_inline int +__handle_set_state(struct trace_event_raw_inet_sock_set_state *ctx) { + if (ctx->protocol != IPPROTO_TCP) + return 0; + + struct sock *sk = (struct sock *)ctx->skaddr; + __u64 *before_time, new_time, time; + + before_time = bpf_map_lookup_elem(&tcp_state, &sk); + new_time = bpf_ktime_get_ns(); + if (!before_time) + time = 0; + else + time = (new_time - *before_time) / 1000; + struct tcpstate tcpstate = {}; + tcpstate.oldstate = ctx->oldstate; + tcpstate.newstate = ctx->newstate; + tcpstate.family = ctx->family; + tcpstate.sport = ctx->sport; + tcpstate.dport = ctx->dport; + bpf_probe_read_kernel(&tcpstate.saddr, sizeof(tcpstate.saddr), + &sk->__sk_common.skc_rcv_saddr); + bpf_probe_read_kernel(&tcpstate.daddr, sizeof(tcpstate.daddr), + &sk->__sk_common.skc_daddr); + tcpstate.time = time; + if (ctx->newstate == TCP_CLOSE) + bpf_map_delete_elem(&tcp_state, &sk); + else + bpf_map_update_elem(&tcp_state, &sk, &new_time, BPF_ANY); + + struct tcp_state *message; + message = bpf_ringbuf_reserve(&tcp_rb, sizeof(*message), 0); + if (!message) { + return 0; + } + message->saddr = tcpstate.saddr; + message->daddr = tcpstate.daddr; + message->sport = tcpstate.sport; + message->dport = tcpstate.dport; + message->oldstate = tcpstate.oldstate; + message->newstate = tcpstate.newstate; + message->time = tcpstate.time; + bpf_printk("Dport:%d time:%d", tcpstate.dport, tcpstate.time); + bpf_ringbuf_submit(message, 0); + return 0; +} + +static __always_inline int __tcp_rcv_established(struct sock *sk, + struct sk_buff *skb) { + const struct inet_sock *inet = (struct inet_sock *)(sk); + struct tcp_sock *ts; + struct hist *histp; + u64 slot; + u32 srtt; + struct iphdr *ip = skb_to_iphdr(skb); + struct tcphdr *tcp = skb_to_tcphdr(skb); + struct packet_tuple pkt_tuple = {0}; + get_pkt_tuple(&pkt_tuple, ip, tcp); + // INIT_PACKET_TCP_TUPLE(sk, pkt_tuple); + struct ip_packet key = {.saddr = pkt_tuple.saddr, .daddr = pkt_tuple.daddr}; + + histp = bpf_map_lookup_elem(&hists, &key); + if (!histp) { + // 初始化值 + struct hist zero = {}; + bpf_map_update_elem(&hists, &key, &zero, BPF_ANY); + histp = bpf_map_lookup_elem(&hists, &key); + if (!histp) + return 0; // 如果仍然查找失败,则返回 + } + ts = (struct tcp_sock *)(sk); + + // 读取并处理SRTT(平滑往返时间) + srtt = BPF_CORE_READ(ts, srtt_us) >> 3; + // 计算对数值,根据得到的结果决定数据应该归入直方图的哪个槽位 + slot = log2l(srtt); + if (slot >= MAX_SLOTS) + slot = MAX_SLOTS - 1; // 确保槽位置不超过最大槽数 + + // 更新 + __sync_fetch_and_add(&histp->slots[slot], 1); + __sync_fetch_and_add(&histp->latency, srtt); + __sync_fetch_and_add(&histp->cnt, 1); + + struct RTT *message; + message = bpf_ringbuf_reserve(&rtt_rb, sizeof(*message), 0); + if (!message) { + return 0; + } + message->saddr = pkt_tuple.saddr; + message->daddr = pkt_tuple.daddr; + // bpf_printk("Saddr:%u Daddr:%u", pkt_tuple.saddr, pkt_tuple.daddr); + bpf_probe_read_kernel(message->slots, sizeof(message->slots), histp->slots); + message->latency = histp->latency; + message->cnt = histp->cnt; + // bpf_printk("Updating histogram: latency %llu, cnt %llu, slot %llu, + // slot_count %llu", histp->latency, histp->cnt, slot, histp->slots[slot]); + bpf_ringbuf_submit(message, 0); + return 0; +} + +static __always_inline int ret(void *ctx, u8 direction, u16 sport, + u16 dport) { + struct reset_event_t *message = + bpf_ringbuf_reserve(&events, sizeof(*message), 0); + if (!message) + return 0; + + message->pid = bpf_get_current_pid_tgid() >> 32; + bpf_get_current_comm(&message->comm, sizeof(message->comm)); + + struct sock *sk = (struct sock *)ctx; + message->family = BPF_CORE_READ(sk, __sk_common.skc_family); + message->timestamp = bpf_ktime_get_ns(); + + if (message->family == AF_INET) { + if (direction == 0) { // Send + message->saddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr); + message->daddr = BPF_CORE_READ(sk, __sk_common.skc_daddr); + } else { // Receive + message->saddr = BPF_CORE_READ(sk, __sk_common.skc_daddr); + message->daddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr); + } + message->saddr_v6 = 0; + message->daddr_v6 = 0; + } else if (message->family == AF_INET6) { + if (direction == 0) { // Send + bpf_probe_read_kernel( + &message->saddr_v6, sizeof(message->saddr_v6), + &sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); + bpf_probe_read_kernel( + &message->daddr_v6, sizeof(message->daddr_v6), + &sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32); + } else { // Receive + bpf_probe_read_kernel( + &message->saddr_v6, sizeof(message->saddr_v6), + &sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32); + bpf_probe_read_kernel( + &message->daddr_v6, sizeof(message->daddr_v6), + &sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); + } + + message->saddr = 0; + message->daddr = 0; + } + + if (direction == 0) { // Send + message->sport = bpf_ntohs(sport); + message->dport = bpf_ntohs(dport); + } else { // Receive + message->sport = bpf_ntohs(dport); + message->dport = bpf_ntohs(sport); + } + message->direction = direction; + + // 增加 RST 计数 + u32 pid = message->pid; + u64 *count = bpf_map_lookup_elem(&counters, &pid); + if (count) { + *count += 1; + } else { + u64 initial_count = 1; + bpf_map_update_elem(&counters, &pid, &initial_count, BPF_ANY); + count = &initial_count; + } + message->count = *count; + + bpf_ringbuf_submit(message, 0); + + return 0; +} +static __always_inline int +__handle_send_reset(struct trace_event_raw_tcp_send_reset *ctx) { + struct sock *sk = (struct sock *)ctx->skaddr; + if (!sk) + return 0; + // bpf_printk("Send reset: sport=%u, dport=%u\n", ctx->sport, ctx->dport); + return ret((void *)ctx->skaddr, 0, ctx->sport, ctx->dport); +} + +static __always_inline int +__handle_receive_reset(struct trace_event_raw_tcp_receive_reset *ctx) { + struct sock *sk = (struct sock *)ctx->skaddr; + if (!sk) + return 0; + // bpf_printk("Receive reset: sport=%u, dport=%u\n", ctx->sport, + // ctx->dport); + return ret((void *)ctx->skaddr, 1, ctx->sport, ctx->dport); +} \ No newline at end of file diff --git a/MagicEyes/src/backend/net/net_watcher/bpf/udp.bpf.h b/MagicEyes/src/backend/net/net_watcher/bpf/udp.bpf.h new file mode 100644 index 000000000..0b38ebb40 --- /dev/null +++ b/MagicEyes/src/backend/net/net_watcher/bpf/udp.bpf.h @@ -0,0 +1,206 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: blown.away@qq.com + +#include "common.bpf.h" + +static __always_inline int __udp_rcv(struct sk_buff *skb) { + if (!udp_info || skb == NULL) + return 0; + struct iphdr *ip = skb_to_iphdr(skb); + struct udphdr *udp = skb_to_udphdr(skb); + struct packet_tuple pkt_tuple = {0}; + get_udp_pkt_tuple(&pkt_tuple, ip, udp); + FILTER + struct ktime_info *tinfo, zero = {0}; + tinfo = (struct ktime_info *)bpf_map_lookup_or_try_init(×tamps, + &pkt_tuple, &zero); + if (tinfo == NULL) { + return 0; + } + tinfo->tran_time = bpf_ktime_get_ns() / 1000; + return 0; +} +static __always_inline int udp_enqueue_schedule_skb(struct sock *sk, + struct sk_buff *skb) { + if (!udp_info || skb == NULL) + return 0; + struct iphdr *ip = skb_to_iphdr(skb); + struct udphdr *udp = skb_to_udphdr(skb); + struct packet_tuple pkt_tuple = {0}; + get_udp_pkt_tuple(&pkt_tuple, ip, udp); + FILTER + struct ktime_info *tinfo, zero = {0}; + tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple); + if (tinfo == NULL) { + return 0; + } + struct udp_message *message; + struct udp_message *udp_message = + bpf_map_lookup_elem(×tamps, &pkt_tuple); + message = bpf_ringbuf_reserve(&udp_rb, sizeof(*message), 0); + if (!message) { + return 0; + } + message->saddr = pkt_tuple.saddr; + message->daddr = pkt_tuple.daddr; + message->dport = pkt_tuple.dport; + message->sport = pkt_tuple.sport; + message->tran_time = bpf_ktime_get_ns() / 1000 - tinfo->tran_time; + message->rx = 1; // 收包 + message->len = __bpf_ntohs(BPF_CORE_READ(udp, len)); + bpf_ringbuf_submit(message, 0); + return 0; +} + +static __always_inline int __udp_send_skb(struct sk_buff *skb) { + if (!udp_info || skb == NULL) + return 0; + struct packet_tuple pkt_tuple = {0}; + struct sock *sk = BPF_CORE_READ(skb, sk); + u16 dport = BPF_CORE_READ(sk, __sk_common.skc_dport); + u16 sport = BPF_CORE_READ(sk, __sk_common.skc_num); + pkt_tuple.saddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr); // 源ip + pkt_tuple.daddr = BPF_CORE_READ(sk, __sk_common.skc_daddr); // 目的ip + pkt_tuple.sport = sport; // 源端口 + pkt_tuple.dport = __bpf_ntohs(dport); // 目的端口并进行字节序转换 + pkt_tuple.tran_flag = UDP; + FILTER + struct ktime_info *tinfo, zero = {0}; + bpf_printk("udp_send_skb%d %d %d %d", pkt_tuple.saddr, pkt_tuple.daddr, + pkt_tuple.sport, pkt_tuple.dport); + tinfo = (struct ktime_info *)bpf_map_lookup_or_try_init(×tamps, + &pkt_tuple, &zero); + if (tinfo == NULL) { + return 0; + } + tinfo->tran_time = bpf_ktime_get_ns() / 1000; + return 0; +} +static __always_inline int __ip_send_skb(struct sk_buff *skb) { + if (!udp_info || skb == NULL) + return 0; + struct iphdr *ip = skb_to_iphdr(skb); + struct udphdr *udp = skb_to_udphdr(skb); + struct packet_tuple pkt_tuple = {0}; + get_udp_pkt_tuple(&pkt_tuple, ip, udp); + FILTER + struct ktime_info *tinfo, zero = {0}; + tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple); + if (tinfo == NULL) { + return 0; + } + struct udp_message *message; + struct udp_message *udp_message = + bpf_map_lookup_elem(×tamps, &pkt_tuple); + message = bpf_ringbuf_reserve(&udp_rb, sizeof(*message), 0); + if (!message) { + return 0; + } + udp = skb_to_udphdr(skb); + message->tran_time = bpf_ktime_get_ns() / 1000 - tinfo->tran_time; + message->saddr = pkt_tuple.saddr; + message->daddr = pkt_tuple.daddr; + message->sport = pkt_tuple.sport; + message->dport = pkt_tuple.dport; + message->rx = 0; // 发包 + message->len = __bpf_ntohs(BPF_CORE_READ(udp, len)); + bpf_ringbuf_submit(message, 0); + return 0; +} +static __always_inline int process_dns_packet(struct sk_buff *skb, int rx) { + if (skb == NULL) + return 0; + u16 QR_flags; + u64 *count_ptr, response_count = 0, request_count = 0; + struct sock *sk = BPF_CORE_READ(skb, sk); + INIT_PACKET_UDP_TUPLE(sk, pkt_tuple); + // 使用saddr、daddr作为key + struct dns key = {.saddr = pkt_tuple.saddr, .daddr = pkt_tuple.daddr}; + + if ((pkt_tuple.sport != 53) && (pkt_tuple.dport != 53)) + return 0; + + struct dns_information *message = + bpf_ringbuf_reserve(&dns_rb, sizeof(*message), 0); + if (!message) + return 0; + + // 计算dns数据开始偏移 传输层头部的位置加上 UDP 头部的大小 + u32 dns_offset = + BPF_CORE_READ(skb, transport_header) + sizeof(struct udphdr); + struct dns_query query; + // dns头部位置 + bpf_probe_read_kernel(&query.header, sizeof(query.header), + BPF_CORE_READ(skb, head) + dns_offset); + // dns数据部分 + bpf_probe_read_kernel(message->data, sizeof(message->data), + BPF_CORE_READ(skb, head) + dns_offset + + sizeof(struct dns_header)); + QR_flags = __bpf_ntohs(query.header.flags); + /* + 1000 0000 0000 0000 + &运算提取最高位QR, QR=1 Response QR=0 Request + */ + if (QR_flags & 0x8000) { // 响应 + count_ptr = bpf_map_lookup_elem(&dns_response_count, &key); + if (count_ptr) { + response_count = *count_ptr + 1; + } else { + response_count = 1; + } + bpf_map_update_elem(&dns_response_count, &key, &response_count, + BPF_ANY); + // 保留映射中的请求计数值 + count_ptr = bpf_map_lookup_elem(&dns_request_count, &key); + if (count_ptr) { + request_count = *count_ptr; + } + } else { // 请求 + count_ptr = bpf_map_lookup_elem(&dns_request_count, &key); + if (count_ptr) { + request_count = *count_ptr + 1; + } else { + request_count = 1; + } + bpf_map_update_elem(&dns_request_count, &key, &request_count, BPF_ANY); + // 保留映射中的响应计数值 + count_ptr = bpf_map_lookup_elem(&dns_response_count, &key); + if (count_ptr) { + response_count = *count_ptr; + } + } + message->saddr = rx ? pkt_tuple.saddr : pkt_tuple.daddr; + message->daddr = rx ? pkt_tuple.daddr : pkt_tuple.saddr; + message->id = __bpf_ntohs(query.header.id); + message->flags = __bpf_ntohs(query.header.flags); + message->qdcount = __bpf_ntohs(query.header.qdcount); + message->ancount = __bpf_ntohs(query.header.ancount); + message->nscount = __bpf_ntohs(query.header.nscount); + message->arcount = __bpf_ntohs(query.header.arcount); + message->request_count = request_count; + message->response_count = response_count; + message->rx = rx; + + bpf_ringbuf_submit(message, 0); + return 0; +} +static __always_inline int __dns_rcv(struct sk_buff *skb) { + return process_dns_packet(skb, 0); // 0 收 +} + +static __always_inline int __dns_send(struct sk_buff *skb) { + return process_dns_packet(skb, 1); // 1 发 +} \ No newline at end of file diff --git a/MagicEyes/src/backend/net/net_watcher/include/dropreason.h b/MagicEyes/src/backend/net/net_watcher/include/dropreason.h new file mode 100644 index 000000000..880fdf1b7 --- /dev/null +++ b/MagicEyes/src/backend/net/net_watcher/include/dropreason.h @@ -0,0 +1,100 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: blown.away@qq.com +// net_watcher libbpf 丢包原因 + +#ifndef __DROPREASON_H +#define __DROPREASON_H +const char *SKB_Drop_Reason_Strings[] = { + "SKB_NOT_DROPPED_YET", + "SKB_CONSUMED", + "SKB_DROP_REASON_NOT_SPECIFIED", + "SKB_DROP_REASON_NO_SOCKET", + "SKB_DROP_REASON_PKT_TOO_SMALL", + "SKB_DROP_REASON_TCP_CSUM", + "SKB_DROP_REASON_SOCKET_FILTER", + "SKB_DROP_REASON_UDP_CSUM", + "SKB_DROP_REASON_NETFILTER_DROP", + "SKB_DROP_REASON_OTHERHOST", + "SKB_DROP_REASON_IP_CSUM", + "SKB_DROP_REASON_IP_INHDR", + "SKB_DROP_REASON_IP_RPFILTER", + "SKB_DROP_REASON_UNICAST_IN_L2_MULTICAST", + "SKB_DROP_REASON_XFRM_POLICY", + "SKB_DROP_REASON_IP_NOPROTO", + "SKB_DROP_REASON_SOCKET_RCVBUFF", + "SKB_DROP_REASON_PROTO_MEM", + "SKB_DROP_REASON_TCP_MD5NOTFOUND", + "SKB_DROP_REASON_TCP_MD5UNEXPECTED", + "SKB_DROP_REASON_TCP_MD5FAILURE", + "SKB_DROP_REASON_SOCKET_BACKLOG", + "SKB_DROP_REASON_TCP_FLAGS", + "SKB_DROP_REASON_TCP_ZEROWINDOW", + "SKB_DROP_REASON_TCP_OLD_DATA", + "SKB_DROP_REASON_TCP_OVERWINDOW", + "SKB_DROP_REASON_TCP_OFOMERGE", + "SKB_DROP_REASON_TCP_RFC7323_PAWS", + "SKB_DROP_REASON_TCP_INVALID_SEQUENCE", + "SKB_DROP_REASON_TCP_RESET", + "SKB_DROP_REASON_TCP_INVALID_SYN", + "SKB_DROP_REASON_TCP_CLOSE", + "SKB_DROP_REASON_TCP_FASTOPEN", + "SKB_DROP_REASON_TCP_OLD_ACK", + "SKB_DROP_REASON_TCP_TOO_OLD_ACK", + "SKB_DROP_REASON_TCP_ACK_UNSENT_DATA", + "SKB_DROP_REASON_TCP_OFO_QUEUE_PRUNE", + "SKB_DROP_REASON_TCP_OFO_DROP", + "SKB_DROP_REASON_IP_OUTNOROUTES", + "SKB_DROP_REASON_BPF_CGROUP_EGRESS", + "SKB_DROP_REASON_IPV6DISABLED", + "SKB_DROP_REASON_NEIGH_CREATEFAIL", + "SKB_DROP_REASON_NEIGH_FAILED", + "SKB_DROP_REASON_NEIGH_QUEUEFULL", + "SKB_DROP_REASON_NEIGH_DEAD", + "SKB_DROP_REASON_TC_EGRESS", + "SKB_DROP_REASON_QDISC_DROP", + "SKB_DROP_REASON_CPU_BACKLOG", + "SKB_DROP_REASON_XDP", + "SKB_DROP_REASON_TC_INGRESS", + "SKB_DROP_REASON_UNHANDLED_PROTO", + "SKB_DROP_REASON_SKB_CSUM", + "SKB_DROP_REASON_SKB_GSO_SEG", + "SKB_DROP_REASON_SKB_UCOPY_FAULT", + "SKB_DROP_REASON_DEV_HDR", + "SKB_DROP_REASON_DEV_READY", + "SKB_DROP_REASON_FULL_RING", + "SKB_DROP_REASON_NOMEM", + "SKB_DROP_REASON_HDR_TRUNC", + "SKB_DROP_REASON_TAP_FILTER", + "SKB_DROP_REASON_TAP_TXFILTER", + "SKB_DROP_REASON_ICMP_CSUM", + "SKB_DROP_REASON_INVALID_PROTO", + "SKB_DROP_REASON_IP_INADDRERRORS", + "SKB_DROP_REASON_IP_INNOROUTES", + "SKB_DROP_REASON_PKT_TOO_BIG", + "SKB_DROP_REASON_DUP_FRAG", + "SKB_DROP_REASON_FRAG_REASM_TIMEOUT", + "SKB_DROP_REASON_FRAG_TOO_FAR", + "SKB_DROP_REASON_TCP_MINTTL", + "SKB_DROP_REASON_IPV6_BAD_EXTHDR", + "SKB_DROP_REASON_IPV6_NDISC_FRAG", + "SKB_DROP_REASON_IPV6_NDISC_HOP_LIMIT", + "SKB_DROP_REASON_IPV6_NDISC_BAD_CODE", + "SKB_DROP_REASON_IPV6_NDISC_BAD_OPTIONS", + "SKB_DROP_REASON_IPV6_NDISC_NS_OTHERHOST", + "SKB_DROP_REASON_MAX", + "SKB_DROP_REASON_SUBSYS_MASK", +}; +#endif \ No newline at end of file diff --git a/MagicEyes/src/backend/net/net_watcher/include/net_watcher.h b/MagicEyes/src/backend/net/net_watcher/include/net_watcher.h index e56b21a8c..e331c59f7 100644 --- a/MagicEyes/src/backend/net/net_watcher/include/net_watcher.h +++ b/MagicEyes/src/backend/net/net_watcher/include/net_watcher.h @@ -14,13 +14,19 @@ // // author: blown.away@qq.com // -// netwatcher libbpf 内核<->用户 传递信息相关结构体 +// net_watcher libbpf 内核<->用户 传递信息相关结构体 -#ifndef __NETWATCHER_H -#define __NETWATCHER_H +#ifndef __NET_WATCHER_H +#define __NET_WATCHER_H + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; #define ETH_P_IP 0x0800 /* Internet Protocol packet */ #define ETH_P_IPV6 0x86DD /* IPv6 over bluebook */ +#define MAX_SLOTS 27 #ifndef AF_INET #define AF_INET 2 @@ -35,76 +41,226 @@ #define MAX_COMM 16 #define TCP 1 #define UDP 2 +#define MAX_PACKET 1000 +#define MAX_HTTP_HEADER 256 +#define NUM_LAYERS 5 +#define RED_TEXT "\033[31m" +#define RESET_TEXT "\033[0m" +#define GRANULARITY 3 +#define ALPHA 0.2 // 衰减因子 +#define MAXTIME 10000 +#define SLOW_QUERY_THRESHOLD 10000 // +#define ANSI_COLOR_RED "\x1b[31m" +#define ANSI_COLOR_RESET "\x1b[0m" +#define MAX_STACK_DEPTH 128 +#define MAX_EVENTS 1024 +#define CACHEMAXSIZE 5 +typedef u64 stack_trace_t[MAX_STACK_DEPTH]; struct conn_t { - void *sock; // 此tcp连接的 socket 地址 - int pid; // pid - unsigned long long ptid; // 此tcp连接的 ptid(ebpf def) - char comm[MAX_COMM]; // 此tcp连接的 command - unsigned short family; // 10(AF_INET6):v6 or 2(AF_INET):v4 + void *sock; // 此tcp连接的 socket 地址 + int pid; // pid + u64 ptid; // 此tcp连接的 ptid(ebpf def) + char comm[MAX_COMM]; // 此tcp连接的 command + u16 family; // 10(AF_INET6):v6 or 2(AF_INET):v4 unsigned __int128 saddr_v6; unsigned __int128 daddr_v6; - unsigned int saddr; - unsigned int daddr; - unsigned short sport; - unsigned short dport; + u32 saddr; + u32 daddr; + u16 sport; + u16 dport; int is_server; // 1: 被动连接 0: 主动连接 - unsigned int tcp_backlog; // backlog - unsigned int max_tcp_backlog; // max_backlog - unsigned long long bytes_acked; // 已确认的字节数 - unsigned long long bytes_received; // 已接收的字节数 + u32 tcp_backlog; // backlog + u32 max_tcp_backlog; // max_backlog + u64 bytes_acked; // 已确认的字节数 + u64 bytes_received; // 已接收的字节数 - unsigned int snd_cwnd; // 拥塞窗口大小 - unsigned int rcv_wnd; // 接收窗口大小 - unsigned int snd_ssthresh; // 慢启动阈值 - unsigned int sndbuf; // 发送缓冲区大小(byte) - unsigned int sk_wmem_queued; // 已使用的发送缓冲区 - unsigned int total_retrans; // 重传包数 - unsigned int fastRe; // 快速重传次数 - unsigned int timeout; // 超时重传次数 + u32 snd_cwnd; // 拥塞窗口大小 + u32 rcv_wnd; // 接收窗口大小 + u32 snd_ssthresh; // 慢启动阈值 + u32 sndbuf; // 发送缓冲区大小(byte) + u32 sk_wmem_queued; // 已使用的发送缓冲区 + u32 total_retrans; // 重传包数 + u32 fastRe; // 快速重传次数 + u32 timeout; // 超时重传次数 - unsigned int srtt; // 平滑往返时间 - unsigned long long init_timestamp; // 建立连接时间戳 - unsigned long long duration; // 连接已建立时长 + u32 srtt; // 平滑往返时间 + u64 init_timestamp; // 建立连接时间戳 + u64 duration; // 连接已建立时长 }; -#define MAX_PACKET 1000 -#define MAX_HTTP_HEADER 256 - struct pack_t { - int err; // no err(0) invalid seq(1) invalid checksum(2) - unsigned long long mac_time; // mac layer 处理时间(us) - unsigned long long ip_time; // ip layer 处理时间(us) - // unsigned long long tcp_time; // tcp layer 处理时间(us) - unsigned long long tran_time; // tcp layer 处理时间(us) - unsigned int seq; // the seq num of packet - unsigned int ack; // the ack num of packet - unsigned char data[MAX_HTTP_HEADER]; // 用户层数据 - const void *sock; // 此包tcp连接的 socket 指针 - int rx; // rx packet(1) or tx packet(0) + int err; // no err(0) invalid seq(1) invalid checksum(2) + u64 mac_time; // mac layer 处理时间(us) + u64 ip_time; // ip layer 处理时间(us) + // u64 tcp_time; // tcp layer 处理时间(us) + u64 tran_time; // tcp layer 处理时间(us) + u32 seq; // the seq num of packet + u32 ack; // the ack num of packet + u8 data[MAX_HTTP_HEADER]; // 用户层数据 + const void *sock; // 此包tcp连接的 socket 指针 + int rx; // rx packet(1) or tx packet(0) + u32 saddr; + u32 daddr; + unsigned __int128 saddr_v6; + unsigned __int128 daddr_v6; + u16 sport; + u16 dport; }; struct udp_message { - unsigned int saddr; - unsigned int daddr; - unsigned short sport; - unsigned short dport; - unsigned long long tran_time; - int rx; + u32 saddr; + u32 daddr; + u16 sport; + u16 dport; + u64 tran_time; + int rx; int len; }; -struct netfilter -{ +struct netfilter { + u32 saddr; + u32 daddr; + u16 sport; + u16 dport; + u64 local_input_time; + u64 pre_routing_time; + u64 forward_time; + u64 local_out_time; + u64 post_routing_time; + u32 rx; +}; +struct reasonissue { + u32 saddr; + u32 daddr; + u16 sport; + u16 dport; + long location; + u16 protocol; + int drop_reason; +}; +struct icmptime { unsigned int saddr; unsigned int daddr; - unsigned short sport; - unsigned short dport; - unsigned long long local_input_time; - unsigned long long pre_routing_time; - unsigned long long forward_time; - unsigned long long local_out_time; - unsigned long long post_routing_time; - unsigned int flag; -}; -#endif /* __NETWATCHER_H */ \ No newline at end of file + unsigned long long icmp_tran_time; + unsigned int flag; // 0 send 1 rcv +}; + +struct tcp_state { + u32 saddr; + u32 daddr; + u16 sport; + u16 dport; + int oldstate; + int newstate; + u64 time; +}; +struct dns_information { + u32 saddr; + u32 daddr; + u16 id; + u16 flags; + u16 qdcount; + u16 ancount; + u16 nscount; + u16 arcount; + char data[64]; + int rx; + int response_count; + int request_count; +}; +struct stacktrace_event { + u32 pid; + u32 cpu_id; + char comm[16]; + signed int kstack_sz; + signed int ustack_sz; + stack_trace_t kstack; + stack_trace_t ustack; +}; +typedef struct mysql_query { + int pid; + int tid; + char comm[20]; + u32 size; + char msql[256]; + u64 duratime; + int count; +} mysql_query; +struct redis_query { + int pid; + int tid; + char comm[20]; + u32 size; + char redis[4][8]; + u64 duratime; + int count; + u64 begin_time; + int argc; +}; +struct redis_stat_query { + int pid; + char comm[20]; + char key[20]; + int key_count; + char value[64]; + int value_type; +}; + +struct RTT { + u32 saddr; + u32 daddr; + u64 slots[64]; + u64 latency; + u64 cnt; +}; +struct reset_event_t { + int pid; + char comm[16]; + u16 family; + unsigned __int128 saddr_v6; + unsigned __int128 daddr_v6; + u32 saddr; + u32 daddr; + u16 sport; + u16 dport; + u8 direction; // 0 for send, 1 for receive + u64 count; + u64 timestamp; + u8 state; +}; +struct packet_count { + u64 rx_count; + u64 tx_count; +}; +struct packet_info { + u32 saddr; + u32 daddr; + u16 sport; + u16 dport; + u16 proto; + struct packet_count count; +}; +struct SymbolEntry { + unsigned long addr; + char name[30]; +}; + +static const char *protocol[] = { + [0] = "TCP", + [1] = "UDP", + [2] = "ICMP", + [3] = "UNKNOWN", +}; +static const char *tcp_states[] = { + [1] = "ESTABLISHED", [2] = "SYN_SENT", [3] = "SYN_RECV", + [4] = "FIN_WAIT1", [5] = "FIN_WAIT2", [6] = "TIME_WAIT", + [7] = "CLOSE", [8] = "CLOSE_WAIT", [9] = "LAST_ACK", + [10] = "LISTEN", [11] = "CLOSING", [12] = "NEW_SYN_RECV", + [13] = "UNKNOWN", +}; +struct LayerDelayInfo { + float delay; // 时延数据 + int layer_index; // 层索引 +}; +#endif /* __NET_WATCHER_H */ \ No newline at end of file diff --git a/MagicEyes/src/backend/net/net_watcher/src/net_watcher.c b/MagicEyes/src/backend/net/net_watcher/src/net_watcher.c index 248599179..96e719dbc 100644 --- a/MagicEyes/src/backend/net/net_watcher/src/net_watcher.c +++ b/MagicEyes/src/backend/net/net_watcher/src/net_watcher.c @@ -16,6 +16,9 @@ // // net_watcher libbpf 用户态代码 +#include "net_watcher/include/net_watcher.h" +#include "net_watcher/include/dropreason.h" +#include "net/net_watcher/net_watcher.skel.h" #include #include #include @@ -26,25 +29,40 @@ #include #include #include +#include +#include #include -#include "net_watcher/include/net_watcher.h" - -#include "net/net_watcher/net_watcher.skel.h" - static volatile bool exiting = false; - +struct packet_count proto_stats[256] = {0}; +static u64 rst_count = 0; +static struct reset_event_t event_store[MAX_EVENTS]; +static int event_count = 0; static char connects_file_path[1024]; static char err_file_path[1024]; static char packets_file_path[1024]; static char udp_file_path[1024]; +static char binary_path[64] = ""; +int num_symbols = 0; +int cache_size = 0; + +// 用于存储从 eBPF map 读取的数据 +typedef struct { + char key[256]; + u32 value; +} kv_pair; + +static int map_fd; static int sport = 0, dport = 0; // for filter static int all_conn = 0, err_packet = 0, extra_conn_info = 0, layer_time = 0, - http_info = 0, retrans_info = 0, udp_info = 0,net_filter = 0; // flag + http_info = 0, retrans_info = 0, udp_info = 0, net_filter = 0, + drop_reason = 0, addr_to_func = 0, icmp_info = 0, tcp_info = 0, + time_load = 0, dns_info = 0, stack_info = 0, mysql_info = 0, + redis_info = 0, count_info = 0, rtt_info = 0, rst_info = 0, + protocol_count = 0,redis_stat = 0; // flag static const char argp_program_doc[] = "Watch tcp/ip in network subsystem \n"; - static const struct argp_option opts[] = { {"all", 'a', 0, 0, "set to trace CLOSED connection"}, {"err", 'e', 0, 0, "set to trace TCP error packets"}, @@ -55,7 +73,27 @@ static const struct argp_option opts[] = { {"sport", 's', "SPORT", 0, "trace this source port only"}, {"dport", 'd', "DPORT", 0, "trace this destination port only"}, {"udp", 'u', 0, 0, "trace the udp message"}, - {"net_filter",'n',0,0,"trace ipv4 packget filter "}, + {"net_filter", 'n', 0, 0, "trace ipv4 packget filter "}, + {"drop_reason", 'k', 0, 0, "trace kfree "}, + {"addr_to_func", 'F', 0, 0, "translation addr to func and offset"}, + {"icmptime", 'I', 0, 0, "set to trace layer time of icmp"}, + {"tcpstate", 'S', 0, 0, "set to trace tcpstate"}, + {"timeload", 'L', 0, 0, "analysis time load"}, + {"dns", 'D', 0, 0, + "set to trace dns information info include Id 事务ID、Flags 标志字段、Qd " + "问题部分计数、An 应答记录计数、Ns 授权记录计数、Ar 附加记录计数、Qr " + "域名、rx 收发包 、Qc请求数、Sc响应数"}, + {"stack", 'A', 0, 0, "set to trace of stack "}, + {"mysql", 'M', 0, 0, + "set to trace mysql information info include Pid 进程id、Comm " + "进程名、Size sql语句字节大小、Sql 语句"}, + {"redis", 'R', 0, 0}, + {"redis-stat", 'b', 0, 0}, + {"count", 'C', "NUMBER", 0, + "specify the time to count the number of requests"}, + {"rtt", 'T', 0, 0, "set to trace rtt"}, + {"rst_counters", 'U', 0, 0, "set to trace rst"}, + {"protocol_count", 'p', 0, 0, "set to trace protocol count"}, {}}; static error_t parse_arg(int key, char *arg, struct argp_state *state) { @@ -91,20 +129,626 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) { case 'n': net_filter = 1; break; + case 'k': + drop_reason = 1; + break; + case 'F': + addr_to_func = 1; + break; + case 'I': + icmp_info = 1; + break; + case 'S': + tcp_info = 1; + break; + case 'L': + time_load = 1; + break; + case 'D': + dns_info = 1; + break; + case 'A': + stack_info = 1; + break; + case 'M': + mysql_info = 1; + break; + case 'R': + redis_info = 1; + break; + case 'T': + rtt_info = 1; + break; + case 'U': + rst_info = 1; + break; + case 'p': + protocol_count = 1; + break; + case 'b': + redis_stat = 1; + break; + case 'C': + count_info = strtoul(arg, &end, 10); + break; default: return ARGP_ERR_UNKNOWN; } return 0; } - static const struct argp argp = { .options = opts, .parser = parse_arg, .doc = argp_program_doc, }; +enum MonitorMode { + MODE_UDP, + MODE_NET_FILTER, + MODE_DROP_REASON, + MODE_ICMP, + MODE_TCP, + MODE_DNS, + MODE_MYSQL, + MODE_REDIS, + MODE_RTT, + MODE_RST, + MODE_PROTOCOL_COUNT, + MODE_REDIS_STAT, + MODE_DEFAULT +}; +enum MonitorMode get_monitor_mode() { + if (udp_info) { + return MODE_UDP; + } else if (net_filter) { + return MODE_NET_FILTER; + } else if (drop_reason) { + return MODE_DROP_REASON; + } else if (icmp_info) { + return MODE_ICMP; + } else if (tcp_info) { + return MODE_TCP; + } else if (dns_info) { + return MODE_DNS; + } else if (mysql_info) { + return MODE_MYSQL; + } else if (redis_info) { + return MODE_REDIS; + } else if (redis_stat) { + return MODE_REDIS_STAT; + } else if (rtt_info) { + return MODE_RTT; + } else if (rst_info) { + return MODE_RST; + } else if (protocol_count) { + return MODE_PROTOCOL_COUNT; + } else { + return MODE_DEFAULT; + } +} +#define LOGO_STRING \ + " " \ + " __ __ __ " \ + " \n" \ + " /\\ \\__ /\\ \\__ /\\ \\ " \ + " \n" \ + " ___ __\\ \\ _\\ __ __ __ __ \\ \\ _\\ ___\\ \\ \\___ " \ + " __ _ __ \n" \ + "/ _ \\ / __ \\ \\ \\/ /\\ \\/\\ \\/\\ \\ / __ \\ \\ \\ \\/ / ___\\ " \ + "\\ _ \\ / __ \\/\\ __\\ \n" \ + "/\\ \\/\\ \\/\\ __/\\ \\ \\_\\ \\ \\_/ \\_/ \\/\\ \\_\\ \\_\\ \\ " \ + "\\_/\\ \\__/\\ \\ \\ \\ \\/\\ __/\\ \\ \\/ \n" \ + "\\ \\_\\ \\_\\ \\____\\ \\__\\ \\_______ / /\\ \\__/\\ \\_\\ \\__\\ " \ + "\\____/\\ \\_\\ \\_\\ \\____ \\ \\_\\ \n" \ + " \\/_/\\/_/\\/____/ \\/__/ \\/__//__ / \\/_/ \\/_/\\/__/\\/____/ " \ + "\\/_/\\/_/\\/____/ \\/_/ \n\n" -static void sig_handler(int signo) { exiting = true; } +void print_logo() { + char *logo = LOGO_STRING; + int i = 0; + FILE *lolcat_pipe = popen("/usr/games/lolcat", "w"); + if (lolcat_pipe == NULL) { + printf("Error: Unable to execute lolcat command.\n"); + return; + } + // 像lolcat管道逐个字符写入字符串 + while (logo[i] != '\0') { + fputc(logo[i], lolcat_pipe); + fflush(lolcat_pipe); // 刷新管道,确保字符被立即发送给lolcat + usleep(150); + i++; + } + + pclose(lolcat_pipe); +} +#define __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe) \ + do { \ + LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts, .func_name = #sym_name, \ + .retprobe = is_retprobe); \ + skel->links.prog_name = bpf_program__attach_uprobe_opts( \ + skel->progs.prog_name, -1, binary_path, 0, &uprobe_opts); \ + } while (false) + +#define __CHECK_PROGRAM(skel, prog_name) \ + do { \ + if (!skel->links.prog_name) { \ + perror("no program attached for " #prog_name); \ + return -errno; \ + } \ + } while (false) + +#define __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, is_retprobe) \ + do { \ + __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe); \ + __CHECK_PROGRAM(skel, prog_name); \ + } while (false) + +#define ATTACH_UPROBE(skel, sym_name, prog_name) \ + __ATTACH_UPROBE(skel, sym_name, prog_name, false) +#define ATTACH_URETPROBE(skel, sym_name, prog_name) \ + __ATTACH_UPROBE(skel, sym_name, prog_name, true) + +#define ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name) \ + __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, false) +#define ATTACH_URETPROBE_CHECKED(skel, sym_name, prog_name) \ + __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, true) + +struct SymbolEntry symbols[300000]; +struct SymbolEntry cache[CACHEMAXSIZE]; +// LRU算法查找函数 +struct SymbolEntry find_in_cache(unsigned long int addr) { + // 查找地址是否在快表中 + for (int i = 0; i < cache_size; i++) { + if (cache[i].addr == addr) { + // 更新访问时间 + struct SymbolEntry temp = cache[i]; + // 将访问的元素移动到快表的最前面,即最近使用的位置 + for (int j = i; j > 0; j--) { + cache[j] = cache[j - 1]; + } + cache[0] = temp; + return temp; + } + } + // 如果地址不在快表中,则返回空 + struct SymbolEntry empty_entry; + empty_entry.addr = 0; + return empty_entry; +} +// 将新的符号条目加入快表 +void add_to_cache(struct SymbolEntry entry) { + // 如果快表已满,则移除最久未使用的条目 + if (cache_size == CACHEMAXSIZE) { + for (int i = cache_size - 1; i > 0; i--) { + cache[i] = cache[i - 1]; + } + cache[0] = entry; + } else { + // 否则,直接加入快表 + for (int i = cache_size; i > 0; i--) { + cache[i] = cache[i - 1]; + } + cache[0] = entry; + cache_size++; + } +} +struct SymbolEntry findfunc(unsigned long int addr) { + // 先在快表中查找 + struct SymbolEntry entry = find_in_cache(addr); + if (entry.addr != 0) { + return entry; + } + unsigned long long low = 0, high = num_symbols - 1; + unsigned long long result = -1; + + while (low <= high) { + int mid = low + (high - low) / 2; + if (symbols[mid].addr < addr) { + result = mid; + low = mid + 1; + } else { + high = mid - 1; + } + } + add_to_cache(symbols[result]); + return symbols[result]; +}; +void readallsym() { + FILE *file = fopen("/proc/kallsyms", "r"); + if (!file) { + perror("Error opening file"); + exit(EXIT_FAILURE); + } + char line[256]; + while (fgets(line, sizeof(line), file)) { + unsigned long addr; + char type, name[30]; + int ret = sscanf(line, "%lx %c %s", &addr, &type, name); + if (ret == 3) { + symbols[num_symbols].addr = addr; + strncpy(symbols[num_symbols].name, name, 30); + num_symbols++; + } + } + + fclose(file); +} +/* + 指数加权移动平均算法(EWMA) + 1.使用指数加权移动平均算法(EWMA)来计算每层的指数加权移动平均值, + 公式EWMA_new = alpha * new_value + (1 - alpha) * old_ewma ,alpha + 指数加权系数,表示新数据点的权重,new_value 当前时延,old_ewma + 旧的指数加权移动平均值 + 2.根据当前时延和指数加权移动平均值*预先设定的粒度阈值(GRANULARITY)对比,来判断时延是否异常 + 3.可以快速适应数据的变化,并能够有效地检测异常时延 + +*/ +// 全局变量用于存储每层的移动平均值 +float ewma_values[NUM_LAYERS] = {0}; +int count[NUM_LAYERS] = {0}; + +// 指数加权移动平均算法 +float calculate_ewma(float new_value, float old_ewma) { + return ALPHA * new_value + (1 - ALPHA) * old_ewma; +} + +// 收集时延数据并检测异常 +int process_delay(float layer_delay, int layer_index) { + + if (layer_delay == 0) + return 0; + count[layer_index]++; + if (ewma_values[layer_index] == 0) { + ewma_values[layer_index] = layer_delay; + return 0; + } + // 计算阈值,指数加权移动平均值乘以粒度因子 + ewma_values[layer_index] = + calculate_ewma(layer_delay, ewma_values[layer_index]); + float threshold = ewma_values[layer_index] * GRANULARITY; + if (count[layer_index] > 30) { + // 判断当前时延是否超过阈值 + // printf("%d %d:%f %f + // ",layer_index,count[layer_index]++,threshold,layer_delay); + if (layer_delay > threshold) { // 异常 + return 1; + } else { + return 0; + } + } + return 0; +} +static void set_rodata_flags(struct net_watcher_bpf *skel) { + skel->rodata->filter_dport = dport; + skel->rodata->filter_sport = sport; + skel->rodata->all_conn = all_conn; + skel->rodata->err_packet = err_packet; + skel->rodata->extra_conn_info = extra_conn_info; + skel->rodata->layer_time = layer_time; + skel->rodata->http_info = http_info; + skel->rodata->retrans_info = retrans_info; + skel->rodata->udp_info = udp_info; + skel->rodata->net_filter = net_filter; + skel->rodata->drop_reason = drop_reason; + skel->rodata->tcp_info = tcp_info; + skel->rodata->icmp_info = icmp_info; + skel->rodata->dns_info = dns_info; + skel->rodata->stack_info = stack_info; + skel->rodata->mysql_info = mysql_info; + skel->rodata->redis_info = redis_info; + skel->rodata->redis_stat = redis_stat; + skel->rodata->rtt_info = rtt_info; + skel->rodata->rst_info = rst_info; + skel->rodata->protocol_count = protocol_count; +} +static void set_disable_load(struct net_watcher_bpf *skel) { + + bpf_program__set_autoload(skel->progs.inet_csk_accept_exit, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_v4_connect, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_v4_connect_exit, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_v6_connect, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_v6_connect_exit, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_set_state, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.eth_type_trans, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info || protocol_count) + ? true + : false); + bpf_program__set_autoload(skel->progs.ip_rcv_core, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.ip6_rcv_core, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_v4_rcv, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_v6_rcv, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_v4_do_rcv, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_v6_do_rcv, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.skb_copy_datagram_iter, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_validate_incoming, + err_packet ? true : false); + bpf_program__set_autoload(skel->progs.__skb_checksum_complete_exit, + err_packet ? true : false); + bpf_program__set_autoload(skel->progs.tcp_sendmsg, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.ip_queue_xmit, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.inet6_csk_xmit, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.__dev_queue_xmit, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.dev_hard_start_xmit, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info || protocol_count) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_enter_recovery, + retrans_info ? true : false); + bpf_program__set_autoload(skel->progs.tcp_enter_loss, + retrans_info ? true : false); + bpf_program__set_autoload(skel->progs.udp_rcv, + udp_info || dns_info ? true : false); + bpf_program__set_autoload(skel->progs.__udp_enqueue_schedule_skb, + udp_info || dns_info ? true : false); + bpf_program__set_autoload(skel->progs.udp_send_skb, + udp_info || dns_info ? true : false); + bpf_program__set_autoload(skel->progs.ip_send_skb, + udp_info || dns_info ? true : false); + bpf_program__set_autoload(skel->progs.ip_rcv, net_filter ? true : false); + bpf_program__set_autoload(skel->progs.ip_local_deliver, + net_filter ? true : false); + bpf_program__set_autoload(skel->progs.ip_local_deliver_finish, + net_filter ? true : false); + bpf_program__set_autoload(skel->progs.ip_local_out, + net_filter ? true : false); + bpf_program__set_autoload(skel->progs.ip_output, net_filter ? true : false); + bpf_program__set_autoload(skel->progs.__ip_finish_output, + net_filter ? true : false); + bpf_program__set_autoload(skel->progs.ip_forward, + net_filter ? true : false); + bpf_program__set_autoload(skel->progs.tp_kfree, drop_reason ? true : false); + bpf_program__set_autoload(skel->progs.icmp_rcv, icmp_info ? true : false); + bpf_program__set_autoload(skel->progs.__sock_queue_rcv_skb, + icmp_info ? true : false); + bpf_program__set_autoload(skel->progs.icmp_reply, icmp_info ? true : false); + bpf_program__set_autoload(skel->progs.handle_set_state, + tcp_info ? true : false); + bpf_program__set_autoload(skel->progs.query__start, + mysql_info ? true : false); + bpf_program__set_autoload(skel->progs.query__end, + mysql_info ? true : false); + bpf_program__set_autoload(skel->progs.redis_addReply, + redis_stat ? true : false); + bpf_program__set_autoload(skel->progs.redis_lookupKey, + redis_stat ? true : false); + bpf_program__set_autoload(skel->progs.redis_processCommand, + redis_info ? true : false); + bpf_program__set_autoload(skel->progs.redis_call, + redis_info ? true : false); + bpf_program__set_autoload(skel->progs.tcp_rcv_established, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.handle_send_reset, + rst_info ? true : false); + bpf_program__set_autoload(skel->progs.handle_receive_reset, + rst_info ? true : false); +} +static void print_header(enum MonitorMode mode) { + switch (mode) { + case MODE_UDP: + printf("===============================================================" + "UDP " + "INFORMATION====================================================" + "====\n"); + printf("%-20s %-20s %-20s %-20s %-20s %-20s %-20s\n", "Saddr", "Daddr", + "Sprot", "Dprot", "udp_time/μs", "RX/direction", "len/byte"); + break; + case MODE_NET_FILTER: + printf("===============================================================" + "===NETFILTER " + "INFORMATION====================================================" + "=======\n"); + printf("%-20s %-20s %-12s %-12s %-8s %-8s %-7s %-8s %-8s %-8s\n", + "Saddr", "Daddr", "Sprot", "Dprot", "PreRT/μs", "L_IN/μs", + "FW/μs", "PostRT/μs", "L_OUT/μs", "RX/direction"); + break; + case MODE_DROP_REASON: + printf("===============================================================" + "DROP " + "INFORMATION====================================================" + "====\n"); + printf("%-13s %-17s %-17s %-10s %-10s %-9s %-33s %-30s\n", "Time", + "Saddr", "Daddr", "Sprot", "Dprot", "prot", "addr", "reason"); + break; + case MODE_ICMP: + printf("=================================================ICMP " + "INFORMATION==============================================\n"); + printf("%-20s %-20s %-20s %-20s\n", "Saddr", "Daddr", "icmp_time/μs", + "RX/direction"); + break; + case MODE_TCP: + printf("===============================================================" + "TCP STATE " + "INFORMATION====================================================" + "====\n"); + printf("%-20s %-20s %-20s %-20s %-20s %-20s %-20s \n", "Saddr", "Daddr", + "Sport", "Dport", "oldstate", "newstate", "time/μs"); + break; + case MODE_DNS: + printf("===============================================================" + "====================DNS " + "INFORMATION====================================================" + "============================\n"); + printf("%-20s %-20s %-12s %-12s %-5s %-5s %-5s %-5s %-47s %-10s %-10s " + "%-10s \n", + "Saddr", "Daddr", "Id", "Flags", "Qd", "An", "Ns", "Ar", "Qr", + "Qc", "Sc", "RX/direction"); + break; + case MODE_MYSQL: + printf("===============================================================" + "====================MYSQL " + "INFORMATION====================================================" + "============================\n"); + printf("%-20s %-20s %-20s %-20s %-40s %-20s %-20s \n", "Pid", "Tid", + "Comm", "Size", "Sql", "Duration/μs", "Request"); + break; + case MODE_REDIS: + printf("===============================================================" + "====================REDIS " + "INFORMATION====================================================" + "============================\n"); + printf("%-20s %-20s %-20s %-20s %-20s \n", "Pid", "Comm", "Size", + "Redis", "duration/μs"); + break; + case MODE_REDIS_STAT: + printf("===============================================================" + "====================REDIS " + "INFORMATION====================================================" + "============================\n"); + printf("%-20s %-20s %-20s %-20s %-20s %-20s\n", "Pid", "Comm", "key", "Key_count","Value_Type","Value"); + break; + case MODE_RTT: + printf("===============================================================" + "====================RTT " + "INFORMATION====================================================" + "============================\n"); + break; + case MODE_RST: + printf("===============================================================" + "====================RST " + "INFORMATION====================================================" + "============================\n"); + printf("%-20s %-20s %-20s %-20s %-20s %-20s %-20s \n", "Pid", "Comm", + "Saddr", "Daddr", "Sport", "Dport", "Time"); + break; + case MODE_DEFAULT: + printf("===============================================================" + "=INFORMATION===================================================" + "======================\n"); + printf("%-22s %-20s %-8s %-20s %-8s %-15s %-15s %-15s %-15s %-15s \n", + "SOCK", "Saddr", "Sport", "Daddr", "Dport", "MAC_TIME/μs", + "IP_TIME/μs", "TRAN_TIME/μs", "RX/direction", "HTTP"); + break; + case MODE_PROTOCOL_COUNT: + printf("===============================================================" + "=MODE_PROTOCOL_COUNT===========================================" + "========" + "======================\n"); + break; + } +} +static void open_log_files() { + FILE *connect_file = fopen(connects_file_path, "w+"); + if (connect_file == NULL) { + fprintf(stderr, "Failed to open connect.log: (%s)\n", strerror(errno)); + exit(EXIT_FAILURE); + } + fclose(connect_file); + FILE *err_file = fopen(err_file_path, "w+"); + if (err_file == NULL) { + fprintf(stderr, "Failed to open err.log: (%s)\n", strerror(errno)); + exit(EXIT_FAILURE); + } + fclose(err_file); + + FILE *packet_file = fopen(packets_file_path, "w+"); + if (packet_file == NULL) { + fprintf(stderr, "Failed to open packets.log: (%s)\n", strerror(errno)); + exit(EXIT_FAILURE); + } + fclose(packet_file); + + FILE *udp_file = fopen(udp_file_path, "w+"); + if (udp_file == NULL) { + fprintf(stderr, "Failed to open udp.log: (%s)\n", strerror(errno)); + exit(EXIT_FAILURE); + } + fclose(udp_file); +} + +static void sig_handler(int signo) { exiting = true; } static void bytes_to_str(char *str, unsigned long long num) { if (num > 1e9) { sprintf(str, "%.8lfG", (double)num / 1e9); @@ -116,10 +760,9 @@ static void bytes_to_str(char *str, unsigned long long num) { sprintf(str, "%llu", num); } } - static int print_conns(struct net_watcher_bpf *skel) { - FILE *file = fopen(connects_file_path, "w+"); + FILE *file = fopen(connects_file_path, "w"); if (file == NULL) { fprintf(stderr, "Failed to open connects.log: (%s)\n", strerror(errno)); return 0; @@ -145,7 +788,9 @@ static int print_conns(struct net_watcher_bpf *skel) { char s_ip_port_str[INET6_ADDRSTRLEN + 6]; char d_ip_port_str[INET6_ADDRSTRLEN + 6]; - + if ((d.saddr & 0x0000FFFF) == 0x0000007F || + (d.daddr & 0x0000FFFF) == 0x0000007F) + return 0; if (d.family == AF_INET) { sprintf(s_ip_port_str, "%s:%d", inet_ntop(AF_INET, &d.saddr, s_str, sizeof(s_str)), @@ -199,16 +844,34 @@ static int print_conns(struct net_watcher_bpf *skel) { } else { fprintf(file, ",fast_retrans=\"-\",timeout_retrans=\"-\""); } - fprintf(file, "} 0\n"); + fprintf(file, "}\n"); } + fflush(file); fclose(file); return 0; } - static int print_packet(void *ctx, void *packet_info, size_t size) { - if (udp_info || net_filter) + if (udp_info || net_filter || drop_reason || icmp_info || tcp_info || + dns_info || mysql_info || redis_info || rtt_info || protocol_count||redis_stat) return 0; const struct pack_t *pack_info = packet_info; + if (pack_info->mac_time > MAXTIME || pack_info->ip_time > MAXTIME || + pack_info->tran_time > MAXTIME) { + return 0; + } + char d_str[INET_ADDRSTRLEN]; + char s_str[INET_ADDRSTRLEN]; + unsigned int saddr = pack_info->saddr; + unsigned int daddr = pack_info->daddr; + if ((daddr & 0x0000FFFF) == 0x0000007F || + (saddr & 0x0000FFFF) == 0x0000007F) + return 0; + if (dport) + if (pack_info->dport != dport) + return 0; + if (sport) + if (pack_info->sport != sport) + return 0; if (pack_info->err) { FILE *file = fopen(err_file_path, "a"); char reason[20]; @@ -246,38 +909,64 @@ static int print_packet(void *ctx, void *packet_info, size_t size) { sprintf(http_data, "-"); } if (layer_time) { - printf("%-22p %-10u %-10u %-10llu %-10llu %-10llu %-5d %s\n", - pack_info->sock, pack_info->seq, pack_info->ack, - pack_info->mac_time, pack_info->ip_time, + printf("%-22p %-20s %-8d %-20s %-8d %-14llu %-14llu %-14llu %-15d " + "%-16s", + pack_info->sock, + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), + pack_info->sport, + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), + pack_info->dport, pack_info->mac_time, pack_info->ip_time, pack_info->tran_time, pack_info->rx, http_data); fprintf( file, - "packet{sock=\"%p\",seq=\"%u\",ack=\"%u\"," + "packet{sock=\"%p\",saddr=\"%s\",sport=\"%d\",daddr=\"%s\"," + "dport=\"%d\",seq=\"%u\",ack=\"%u\"," "mac_time=\"%llu\",ip_time=\"%llu\",tran_time=\"%llu\",http_" "info=\"%s\",rx=\"%d\"} \n", - pack_info->sock, pack_info->seq, pack_info->ack, + pack_info->sock, + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), + pack_info->sport, + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), + pack_info->dport, pack_info->seq, pack_info->ack, pack_info->mac_time, pack_info->ip_time, pack_info->tran_time, http_data, pack_info->rx); } else { - printf("%-22p %-10u %-10u %-10d %-10d %-10d %-5d %s\n", - pack_info->sock, pack_info->seq, pack_info->ack, 0, 0, 0, - pack_info->rx, http_data); + printf("%-22p %-20s %-8d %-20s %-8d %-10d %-10d %-10d %-5d %-10s", + pack_info->sock, + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), + pack_info->sport, + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), + pack_info->dport, 0, 0, 0, pack_info->rx, http_data); fprintf(file, - "packet{sock=\"%p\",seq=\"%u\",ack=\"%u\"," + "packet{sock=\"%p\",saddr=\"%s\",sport=\"%d\",daddr=\"%s\"," + "dport=\"%d\",seq=\"%u\",ack=\"%u\"," "mac_time=\"%d\",ip_time=\"%d\",tran_time=\"%d\",http_" "info=\"%s\",rx=\"%d\"} \n", - pack_info->sock, pack_info->seq, pack_info->ack, 0, 0, 0, + pack_info->sock, + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), + pack_info->sport, + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), + pack_info->dport, pack_info->seq, pack_info->ack, 0, 0, 0, http_data, pack_info->rx); } fclose(file); } + if (time_load) { + int mac = process_delay(pack_info->mac_time, 0); + int ip = process_delay(pack_info->ip_time, 1); + int tran = process_delay(pack_info->tran_time, 2); + if (mac || ip || tran) { + printf("%-15s", "abnormal data"); + } + } + printf("\n"); return 0; } static int print_udp(void *ctx, void *packet_info, size_t size) { if (!udp_info) return 0; - FILE *file = fopen(udp_file_path, "a+");//追加 - if (file == NULL) { + FILE *file = fopen(udp_file_path, "a+"); // 追加 + if (file == NULL) { fprintf(stderr, "Failed to open udp.log: (%s)\n", strerror(errno)); return 0; } @@ -286,44 +975,546 @@ static int print_udp(void *ctx, void *packet_info, size_t size) { const struct udp_message *pack_info = packet_info; unsigned int saddr = pack_info->saddr; unsigned int daddr = pack_info->daddr; - if(udp_info) - { - printf("%-20s %-20s %-20u %-20u %-20llu %-20d %-20d\n", + if (pack_info->tran_time > MAXTIME || (daddr & 0x0000FFFF) == 0x0000007F || + (saddr & 0x0000FFFF) == 0x0000007F) + return 0; + printf("%-20s %-20s %-20u %-20u %-20llu %-20d %-20d", inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), pack_info->sport, - pack_info->dport, pack_info->tran_time,pack_info->rx,pack_info->len); - fprintf( - file, + pack_info->dport, pack_info->tran_time, pack_info->rx, + pack_info->len); + fprintf(file, "packet{saddr=\"%s\",daddr=\"%s\",sport=\"%u\"," "dport=\"%u\",udp_time=\"%llu\",rx=\"%d\",len=\"%d\"} \n", inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), pack_info->sport, - pack_info->dport, pack_info->tran_time,pack_info->rx,pack_info->len); - //fseek(file, 0, SEEK_END); //指针移动到文件头部 - } - + pack_info->dport, pack_info->tran_time, pack_info->rx, + pack_info->len); fclose(file); + if (time_load) { + int flag = process_delay(pack_info->tran_time, 3); + if (flag) + printf("%-15s", "abnormal data"); + } + printf("\n"); return 0; } static int print_netfilter(void *ctx, void *packet_info, size_t size) { - if(!net_filter) + if (!net_filter) return 0; char d_str[INET_ADDRSTRLEN]; - char s_str[INET_ADDRSTRLEN]; + char s_str[INET_ADDRSTRLEN]; const struct netfilter *pack_info = packet_info; + if (pack_info->local_input_time > MAXTIME || + pack_info->forward_time > MAXTIME || + pack_info->local_out_time > MAXTIME || + pack_info->post_routing_time > MAXTIME || + pack_info->pre_routing_time > MAXTIME) + return 0; unsigned int saddr = pack_info->saddr; unsigned int daddr = pack_info->daddr; - if(net_filter) - { - printf("%-20s %-20s %-20u %-20u %-20llu %-20llu %-20llu %-20llu %-20d\n", + // if ((daddr & 0x0000FFFF) == 0x0000007F || + // (saddr & 0x0000FFFF) == 0x0000007F) + // return 0; + printf("%-20s %-20s %-12d %-12d %-8lld %-8lld% -8lld %-8lld %-8lld %-8d", + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), pack_info->sport, + pack_info->dport, pack_info->pre_routing_time, + pack_info->local_input_time, pack_info->forward_time, + pack_info->post_routing_time, pack_info->local_out_time, + pack_info->rx); + // 定义一个数组用于存储需要检测的时延数据和对应的层索引 + struct LayerDelayInfo layer_delay_infos[] = { + {pack_info->pre_routing_time, 4}, + {pack_info->local_input_time, 5}, + {pack_info->forward_time, 6}, + {pack_info->post_routing_time, 7}, + {pack_info->local_out_time, 8}}; + if (time_load) { + // 循环遍历数组 + for (int i = 0; i < 5; i++) { + // 数组的总字节数除以第一个元素的字节数得到元素的个数 + float delay = layer_delay_infos[i].delay; + int layer_net = layer_delay_infos[i].layer_index; + int flag = process_delay(delay, layer_net); + if (flag) + printf("%-15s", "abnormal data"); + } + } + printf("\n"); + + return 0; +} +static int print_tcpstate(void *ctx, void *packet_info, size_t size) { + if (!tcp_info) + return 0; + char d_str[INET_ADDRSTRLEN]; + char s_str[INET_ADDRSTRLEN]; + const struct tcp_state *pack_info = packet_info; + unsigned int saddr = pack_info->saddr; + unsigned int daddr = pack_info->daddr; + printf("%-20s %-20s %-20d %-20d %-20s %-20s %-20lld\n", + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), pack_info->sport, + pack_info->dport, tcp_states[pack_info->oldstate], + tcp_states[pack_info->newstate], pack_info->time); + + return 0; +} +static void calculate_protocol_usage(struct packet_count proto_stats[], + int num_protocols, int interval) { + static uint64_t last_rx[256] = {0}, last_tx[256] = {0}; + uint64_t current_rx = 0, current_tx = 0; + uint64_t delta_rx[256] = {0}, delta_tx[256] = {0}; + //遍历所有的协议 + for (int i = 0; i < num_protocols; i++) { + //计算数据包增量 + if (proto_stats[i].rx_count >= last_rx[i]) { + delta_rx[i] = proto_stats[i].rx_count - last_rx[i]; + } else { + delta_rx[i] = proto_stats[i].rx_count; + } + + if (proto_stats[i].tx_count >= last_tx[i]) { + delta_tx[i] = proto_stats[i].tx_count - last_tx[i]; + } else { + delta_tx[i] = proto_stats[i].tx_count; + } + //时间段内总的接收和发送包数 + current_rx += delta_rx[i]; + current_tx += delta_tx[i]; + //更新上次统计的包数 + last_rx[i] = proto_stats[i].rx_count; + last_tx[i] = proto_stats[i].tx_count; + } + printf("Protocol Usage in Last %d Seconds:\n", interval); + printf("Total_rx_count:%ld Total_tx_count:%ld\n", current_rx, current_tx); + + if (current_rx > 0) { + printf("Receive Protocol Usage:\n"); + for (int i = 0; i < num_protocols; i++) { + if (delta_rx[i] > 0) { + double rx_percentage = (double)delta_rx[i] / current_rx * 100; + if (rx_percentage >= 80.0) { + printf(RED_TEXT + "Protocol %s: %.2f%% Rx_count:%ld\n" RESET_TEXT, + protocol[i], rx_percentage, delta_rx[i]); + } else { + printf("Protocol %s: %.2f%% Rx_count:%ld\n", protocol[i], + rx_percentage, delta_rx[i]); + } + } + } + } + if (current_tx > 0) { + printf("Transmit Protocol Usage:\n"); + for (int i = 0; i < num_protocols; i++) { + if (delta_tx[i] > 0) { + double tx_percentage = (double)delta_tx[i] / current_tx * 100; + if (tx_percentage >= 80.0) { + printf(RED_TEXT + "Protocol %s: %.2f%% Tx_count:%ld\n" RESET_TEXT, + protocol[i], tx_percentage, delta_tx[i]); + } else { + printf("Protocol %s: %.2f%% Tx_count:%ld\n", protocol[i], + tx_percentage, delta_tx[i]); + } + } + } + } + memset(proto_stats, 0, num_protocols * sizeof(struct packet_count)); +} +static int print_protocol_count(void *ctx, void *packet_info, size_t size) { + const struct packet_info *pack_protocol_info = + (const struct packet_info *)packet_info; + if (!protocol_count) { + return 0; + } + proto_stats[pack_protocol_info->proto].rx_count = + pack_protocol_info->count.rx_count; + proto_stats[pack_protocol_info->proto].tx_count = + pack_protocol_info->count.tx_count; + return 0; +} +static int print_kfree(void *ctx, void *packet_info, size_t size) { + if (!drop_reason) + return 0; + char d_str[INET_ADDRSTRLEN]; + char s_str[INET_ADDRSTRLEN]; + const struct reasonissue *pack_info = packet_info; + unsigned int saddr = pack_info->saddr; + unsigned int daddr = pack_info->daddr; + if (saddr == 0 && daddr == 0) { + return 0; + } + char prot[6]; + if (pack_info->protocol == 2048) { + strcpy(prot, "ipv4"); + } else if (pack_info->protocol == 34525) { + strcpy(prot, "ipv6"); + } else { + // 其他协议 + strcpy(prot, "other"); + } + time_t now = time(NULL); + struct tm *localTime = localtime(&now); + printf("%02d:%02d:%02d %-17s %-17s %-10u %-10u %-10s", + localTime->tm_hour, localTime->tm_min, localTime->tm_sec, + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), pack_info->sport, + pack_info->dport, prot); + if (!addr_to_func) + printf("%-34lx", pack_info->location); + else { + struct SymbolEntry data = findfunc(pack_info->location); + char result[40]; + sprintf(result, "%s+0x%lx", data.name, pack_info->location - data.addr); + printf("%-34s", result); + } + printf("%s\n", SKB_Drop_Reason_Strings[pack_info->drop_reason]); + return 0; +} +static int print_icmptime(void *ctx, void *packet_info, size_t size) { + if (!icmp_info) + return 0; + char d_str[INET_ADDRSTRLEN]; + char s_str[INET_ADDRSTRLEN]; + const struct icmptime *pack_info = packet_info; + if (pack_info->icmp_tran_time > MAXTIME) { + return 0; + } + unsigned int saddr = pack_info->saddr; + unsigned int daddr = pack_info->daddr; + printf("%-20s %-20s %-20lld %-20d", inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), - pack_info->sport,pack_info->dport,pack_info->local_input_time,pack_info->pre_routing_time,pack_info->local_out_time, - pack_info->post_routing_time,pack_info->flag); + pack_info->icmp_tran_time, pack_info->flag); + if (time_load) { + int icmp_data = process_delay(pack_info->icmp_tran_time, 9); + if (icmp_data) { + printf("%-15s\n", "abnormal data"); + } + } + printf("\n"); + return 0; +} +static int print_rst(void *ctx, void *packet_info, size_t size) { + if (!rst_info) { + return 0; + } + struct reset_event_t *event = packet_info; + + // 将事件存储到全局存储中 + if (event_count < MAX_EVENTS) { + memcpy(&event_store[event_count], event, sizeof(struct reset_event_t)); + event_count++; + } + + rst_count++; + return 0; +} +static void print_stored_events() { + char s_str[INET_ADDRSTRLEN]; + char d_str[INET_ADDRSTRLEN]; + + for (int i = 0; i < event_count; i++) { + struct reset_event_t *event = &event_store[i]; + unsigned int saddr = event->saddr; + unsigned int daddr = event->daddr; + + if (event->family == AF_INET) { + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)); + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)); + printf("%-20llu %-20s %-20s %-20s %-20u %-20u %-20llu\n", + (unsigned long long)event->pid, event->comm, s_str, d_str, + event->sport, event->dport, + (unsigned long long)event->timestamp); + } else if (event->family == AF_INET6) { + char saddr_v6[INET6_ADDRSTRLEN]; + char daddr_v6[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &event->saddr_v6, saddr_v6, sizeof(saddr_v6)); + inet_ntop(AF_INET6, &event->daddr_v6, daddr_v6, sizeof(daddr_v6)); + printf("%-10llu %-16s %-16s %-16s %-8u %-8u %-20llu\n", + (unsigned long long)event->pid, event->comm, saddr_v6, + daddr_v6, event->sport, event->dport, + (unsigned long long)event->timestamp); + } + } +} +static void print_domain_name(const unsigned char *data, char *output) { + const unsigned char *next = data; + int pos = 0, first = 1; + // 循环到尾部,标志0 + while (*next != 0) { + if (!first) { + output[pos++] = '.'; // 在每个段之前添加点号 + } else { + first = 0; // 第一个段后清除标志 + } + int len = *next++; // 下一个段长度 + + for (int i = 0; i < len; ++i) { + output[pos++] = *next++; + } + } + output[pos] = '\0'; // 确保字符串正确结束 +} +static int print_dns(void *ctx, void *packet_info, size_t size) { + if (!packet_info) + return 0; + char d_str[INET_ADDRSTRLEN]; + char s_str[INET_ADDRSTRLEN]; + const struct dns_information *pack_info = + (const struct dns_information *)packet_info; // 强制类型转换 + unsigned int saddr = pack_info->saddr; + unsigned int daddr = pack_info->daddr; + char domain_name[256]; // 用于存储输出的域名 + + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)); + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)); + + print_domain_name((const unsigned char *)pack_info->data, domain_name); + if (pack_info->daddr == 0) { + return 0; + } + printf("%-20s %-20s %-#12x %-#12x %-5x %-5x %-5x %-5x %-47s %-10d %-10d " + "%-10d \n", + s_str, d_str, pack_info->id, pack_info->flags, pack_info->qdcount, + pack_info->ancount, pack_info->nscount, pack_info->arcount, + domain_name, pack_info->request_count, pack_info->response_count, + pack_info->rx); + return 0; +} +static int print_mysql(void *ctx, void *packet_info, size_t size) { + if (!mysql_info) { + return 0; + } + + const mysql_query *pack_info = packet_info; + printf("%-20d %-20d %-20s %-20u %-41s", pack_info->pid, pack_info->tid, + pack_info->comm, pack_info->size, pack_info->msql); + if (pack_info->duratime > count_info) { + printf("%-21llu", pack_info->duratime); + } else { + printf("%-21s", ""); + } + printf("%-20d\n", pack_info->count); + return 0; +} +static int print_redis(void *ctx, void *packet_info, size_t size) { + const struct redis_query *pack_info = packet_info; + int i = 0; + char redis[64]; + for (i = 0; i < pack_info->argc; i++) { + strcat(redis, pack_info->redis[i]); + strcat(redis, " "); + } + printf("%-20d %-20s %-20d %-20s %-21llu\n", pack_info->pid, pack_info->comm, + pack_info->argc, redis, pack_info->duratime); + strcpy(redis, ""); + return 0; +} +static int process_redis_first(char flag,char *message) { + if(flag=='+') + { + strcpy(message, "Status Reply"); + } + else if (flag=='-') + { + strcpy(message, "Error Reply"); + } + else if (flag==':') + { + strcpy(message, "Integer Reply"); + } + else if (flag=='$') + { + strcpy(message, "Bulk String Reply"); + } + else if (flag=='*') + { + strcpy(message, "Array Reply"); + } + else{ + strcpy(message, "Unknown Type"); + } + return 0; +} + +static int print_redis_stat(void *ctx, void *packet_info, size_t size) { + if (!redis_stat) { + return 0; + } + char message[20]={}; + const struct redis_stat_query *pack_info = packet_info; + if(pack_info->key_count) + { + printf("%-20d %-20s %-20s %-20d %-20s %-20s\n", pack_info->pid, pack_info->comm, + pack_info->key,pack_info->key_count,"-","-"); + } + else + { + process_redis_first(pack_info->value[0],message); + printf("%-20d %-20s %-20s %-20s %-20s %-20s\n", pack_info->pid, pack_info->comm, + "-","-",message,pack_info->value); + } + + return 0; +} + +static int libbpf_print_fn(enum libbpf_print_level level, const char *format, + va_list args) { + return vfprintf(stderr, format, args); +} +static void show_stack_trace(__u64 *stack, int stack_sz, pid_t pid) { + int i; + printf("-----------------------------------\n"); + for (i = 1; i < stack_sz; i++) { + if (addr_to_func) { + struct SymbolEntry data = findfunc(stack[i]); + char result[40]; + sprintf(result, "%s+0x%llx", data.name, stack[i] - data.addr); + printf("%-10d [<%016llx>]=%s\n", i, stack[i], result); + } else { + printf("%-10d [<%016llx>]\n", i, stack[i]); + } } + printf("-----------------------------------\n"); +} +static int print_trace(void *_ctx, void *data, size_t size) { + struct stacktrace_event *event = data; + + if (event->kstack_sz <= 0 && event->ustack_sz <= 0) + return 1; + + printf("COMM: %s (pid=%d) @ CPU %d\n", event->comm, event->pid, + event->cpu_id); + + if (event->kstack_sz > 0) { + printf("Kernel:\n"); + show_stack_trace(event->kstack, event->kstack_sz / sizeof(__u64), 0); + } else { + printf("No Kernel Stack\n"); + } + printf("\n"); return 0; } +static int print_rtt(void *ctx, void *data, size_t size) { + if (!rtt_info) + return 0; + struct RTT *rtt_tuple = data; + unsigned long long total_latency = 0; + unsigned long long total_count = 0; + char d_str[INET_ADDRSTRLEN]; + char s_str[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &rtt_tuple->saddr, s_str, sizeof(s_str)); + inet_ntop(AF_INET, &rtt_tuple->daddr, d_str, sizeof(d_str)); + if ((rtt_tuple->saddr & 0x0000FFFF) == 0x0000007F || + (rtt_tuple->daddr & 0x0000FFFF) == 0x0000007F || + rtt_tuple->saddr == htonl(0xC0A83C01) || + rtt_tuple->daddr == htonl(0xC0A83C01)) { + return 0; // 如果匹配任一过滤条件,放弃处理这些数据包 + } + // 打印源地址和目的地址 + printf("Source Address: %s\n", s_str); + printf("Destination Address: %s\n", d_str); + // 更新总延迟和计数 + total_latency += rtt_tuple->latency; + total_count += rtt_tuple->cnt; + // 打印总延迟和平均RTT + double average_rtt = + (total_count > 0) ? (double)total_latency / total_count : 0; + printf("Total Latency: %llu μs\n", total_latency); + printf("Average RTT: %.2f ms\n", average_rtt / 1000.0); + + // 计算和打印RTT分布图 + printf(" usecs : count distribution\n"); + int bucket_size = 1; + for (int i = 0; i < MAX_SLOTS; i++) { + int start_range = bucket_size == 1 ? 0 : bucket_size; + int end_range = bucket_size * 2 - 1; + printf("%8d -> %-8d : %-8llu |", start_range, end_range, + rtt_tuple->slots[i]); + int bar_length = + rtt_tuple->slots[i] / + 10; //计算该延迟范围内的计数对应的直方图条形长度,每个'*' + //表示 10 个计数 + for (int j = 0; j < bar_length; j++) { + printf("*"); + } + printf("\n"); + bucket_size *= 2; //以对数方式扩展 + } + printf("===============================================================\n"); + return 0; +} +int attach_uprobe_mysql(struct net_watcher_bpf *skel) { + + ATTACH_UPROBE_CHECKED( + skel, _Z16dispatch_commandP3THDPK8COM_DATA19enum_server_command, + query__start); + ATTACH_URETPROBE_CHECKED( + skel, _Z16dispatch_commandP3THDPK8COM_DATA19enum_server_command, + query__end); + return 0; +} +int attach_uprobe_redis(struct net_watcher_bpf *skel) { + if(redis_info){ + ATTACH_UPROBE_CHECKED(skel, call, redis_call); + ATTACH_UPROBE_CHECKED(skel, processCommand, redis_processCommand); + } + if(redis_stat){ + ATTACH_UPROBE_CHECKED(skel, lookupKey, redis_lookupKey); + ATTACH_UPROBE_CHECKED(skel, addReply, redis_addReply); + } + return 0; +} + +void print_top_5_keys() { + kv_pair *pairs; + pairs = malloc(sizeof(kv_pair) * 1024); + if (!pairs) { + perror("Failed to allocate memory"); + exit(EXIT_FAILURE); + } + int index = 0; + char *key = NULL; + while (bpf_map_get_next_key(map_fd, &key, &key) == 0) { + // fprintf(stdout, "next_sk: (%p)\n", sk); + int count; + int err = bpf_map_lookup_elem(map_fd, &key, &count); + if (err) { + fprintf(stderr, "Failed to read value from the conns map: (%s)\n", + strerror(errno)); + return ; + } + memcpy(pairs[index].key, &key, 256); + pairs[index].value = count; + //printf("Key: %s, Count: %u\n", pairs[index].key, pairs[index].value); + index++; + } + // 获取所有键值对 + + // 排序前 5 个元素 + // 简单选择排序(可替换为其他高效排序算法) + for (int i = 0; i < index - 1; i++) { + for (int j = i + 1; j < index; j++) { + if (pairs[j].value > pairs[i].value) { + kv_pair temp = pairs[i]; + pairs[i] = pairs[j]; + pairs[j] = temp; + } + } + } + printf("----------------------------\n"); + // 打印前 5 个元素 + printf("Top 5 Keys:\n"); + for (int i = 0; i < 5 && i < index; i++) { + printf("Key: %s, Count: %u\n", pairs[i].key, pairs[i].value); + } + free(pairs); +} int main(int argc, char **argv) { char *last_slash = strrchr(argv[0], '/'); if (last_slash) { @@ -333,13 +1524,28 @@ int main(int argc, char **argv) { strcpy(err_file_path, argv[0]); strcpy(packets_file_path, argv[0]); strcpy(udp_file_path, argv[0]); - strcat(connects_file_path, "data/connects.log"); - strcat(err_file_path, "data/err.log"); - strcat(packets_file_path, "data/packets.log"); - strcat(udp_file_path,"data/udp.log"); + if (connects_file_path[strlen(connects_file_path) - 1] != '/') + strcat(connects_file_path, "/connects.log"); + else + strcat(connects_file_path, "connects.log"); + // strcat(connects_file_path, "./connects.log"); + strcat(err_file_path, "./err.log"); + strcat(packets_file_path, "./packets.log"); + strcat(udp_file_path, "./udp.log"); struct ring_buffer *rb = NULL; struct ring_buffer *udp_rb = NULL; struct ring_buffer *netfilter_rb = NULL; + struct ring_buffer *kfree_rb = NULL; + struct ring_buffer *icmp_rb = NULL; + struct ring_buffer *tcp_rb = NULL; + struct ring_buffer *dns_rb = NULL; + struct ring_buffer *trace_rb = NULL; + struct ring_buffer *mysql_rb = NULL; + struct ring_buffer *redis_rb = NULL; + struct ring_buffer *redis_stat_rb = NULL; + struct ring_buffer *rtt_rb = NULL; + struct ring_buffer *events = NULL; + struct ring_buffer *port_rb = NULL; struct net_watcher_bpf *skel; int err; /* Parse command line arguments */ @@ -348,97 +1554,176 @@ int main(int argc, char **argv) { if (err) return err; } - + libbpf_set_print(libbpf_print_fn); /* Cleaner handling of Ctrl-C */ signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); - /* Open load and verify BPF application */ skel = net_watcher_bpf__open(); if (!skel) { fprintf(stderr, "Failed to open BPF skeleton\n"); return 1; } - /* Parameterize BPF code */ - skel->rodata->filter_dport = dport; - skel->rodata->filter_sport = sport; - skel->rodata->all_conn = all_conn; - skel->rodata->err_packet = err_packet; - skel->rodata->extra_conn_info = extra_conn_info; - skel->rodata->layer_time = layer_time; - skel->rodata->http_info = http_info; - skel->rodata->retrans_info = retrans_info; - skel->rodata->udp_info = udp_info; - skel->rodata->net_filter = net_filter; + set_rodata_flags(skel); + set_disable_load(skel); + if (addr_to_func) + readallsym(); err = net_watcher_bpf__load(skel); if (err) { fprintf(stderr, "Failed to load and verify BPF skeleton\n"); goto cleanup; } - /* Attach tracepoint handler */ - err = net_watcher_bpf__attach(skel); - if (err) { - fprintf(stderr, "Failed to attach BPF skeleton\n"); + if (mysql_info) { + strcpy(binary_path, "/usr/sbin/mysqld"); + err = attach_uprobe_mysql(skel); + if (err) { + fprintf(stderr, "failed to attach uprobes\n"); + + goto cleanup; + } + } else if (redis_info||redis_stat) { + strcpy(binary_path, "/usr/bin/redis-server"); + err = attach_uprobe_redis(skel); + if (err) { + fprintf(stderr, "failed to attach uprobes\n"); + + goto cleanup; + } + } else { + err = net_watcher_bpf__attach(skel); + if (err) { + fprintf(stderr, "Failed to attach BPF skeleton\n"); + goto cleanup; + } + } + enum MonitorMode mode = get_monitor_mode(); + + // print_logo(); + + print_header(mode); + + udp_rb = + ring_buffer__new(bpf_map__fd(skel->maps.udp_rb), print_udp, NULL, NULL); + if (!udp_rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer(udp)\n"); goto cleanup; } - if (udp_info) { - printf("%-20s %-20s %-20s %-20s %-20s %-20s %-20s\n", "saddr", "daddr", "sprot", - "dprot", "udp_time","rx","len"); + netfilter_rb = ring_buffer__new(bpf_map__fd(skel->maps.netfilter_rb), + print_netfilter, NULL, NULL); + if (!netfilter_rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer(netfilter)\n"); + goto cleanup; } - else if(net_filter) - { - printf("%-20s %-20s %-20s %-20s %-20s %-20s %-20s %-20s %-20s\n", "saddr", "daddr","dprot", "sprot","local_input","pre_routing","local_out","post_routing","flag"); + kfree_rb = ring_buffer__new(bpf_map__fd(skel->maps.kfree_rb), print_kfree, + NULL, NULL); + if (!kfree_rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer(kfree)\n"); + goto cleanup; } - else{ - printf("%-22s %-10s %-10s %-10s %-10s %-10s %-5s %s\n", "SOCK", "SEQ", - "ACK", "MAC_TIME", "IP_TIME", "TRAN_TIME", "RX", "HTTP"); + icmp_rb = ring_buffer__new(bpf_map__fd(skel->maps.icmp_rb), print_icmptime, + NULL, NULL); + if (!icmp_rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer(icmp)\n"); + goto cleanup; } - udp_rb =ring_buffer__new(bpf_map__fd(skel->maps.udp_rb), print_udp, NULL, NULL); - if (!udp_rb) { + tcp_rb = ring_buffer__new(bpf_map__fd(skel->maps.tcp_rb), print_tcpstate, + NULL, NULL); + if (!tcp_rb) { err = -1; - fprintf(stderr, "Failed to create ring buffer\n"); + fprintf(stderr, "Failed to create ring buffer(tcp)\n"); goto cleanup; } - netfilter_rb =ring_buffer__new(bpf_map__fd(skel->maps.netfilter_rb), print_netfilter, NULL, NULL); - if (!netfilter_rb) { + dns_rb = + ring_buffer__new(bpf_map__fd(skel->maps.dns_rb), print_dns, NULL, NULL); + if (!dns_rb) { err = -1; - fprintf(stderr, "Failed to create ring buffer\n"); + fprintf(stderr, "Failed to create ring buffer(dns)\n"); goto cleanup; } - /* Set up ring buffer polling */ - rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), print_packet, NULL, NULL); - if (!rb) { + trace_rb = ring_buffer__new(bpf_map__fd(skel->maps.trace_rb), print_trace, + NULL, NULL); + if (!trace_rb) { err = -1; - fprintf(stderr, "Failed to create ring buffer\n"); + fprintf(stderr, "Failed to create ring buffer(trace)\n"); goto cleanup; } - FILE *err_file = fopen(err_file_path, "w+"); - if (err_file == NULL) { - fprintf(stderr, "Failed to open err.log: (%s)\n", strerror(errno)); - return 0; + mysql_rb = ring_buffer__new(bpf_map__fd(skel->maps.mysql_rb), print_mysql, + NULL, NULL); + if (!mysql_rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer(trace)\n"); + goto cleanup; } - fclose(err_file); - FILE *packet_file = fopen(packets_file_path, "w+"); - if (packet_file == NULL) { - fprintf(stderr, "Failed to open packets.log: (%s)\n", strerror(errno)); - return 0; + redis_rb = ring_buffer__new(bpf_map__fd(skel->maps.redis_rb), print_redis, + NULL, NULL); + if (!redis_rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer(trace)\n"); + goto cleanup; } - fclose(packet_file); - FILE *udp_file = fopen(udp_file_path, "w+"); - if (udp_file == NULL) { - fprintf(stderr, "Failed to open udp.log: (%s)\n", strerror(errno)); - return 0; + redis_stat_rb = ring_buffer__new(bpf_map__fd(skel->maps.redis_stat_rb), print_redis_stat, + NULL, NULL); + if (!redis_stat_rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer(trace)\n"); + goto cleanup; + } + rtt_rb = + ring_buffer__new(bpf_map__fd(skel->maps.rtt_rb), print_rtt, NULL, NULL); + if (!rtt_rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer(connect_rb)\n"); + goto cleanup; + } + events = + ring_buffer__new(bpf_map__fd(skel->maps.events), print_rst, NULL, NULL); + if (!events) { + err = -1; + fprintf(stderr, "Failed to create ring buffer(rst_rb)\n"); + goto cleanup; + } + + port_rb = ring_buffer__new(bpf_map__fd(skel->maps.port_rb), + print_protocol_count, NULL, NULL); + if (!port_rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer(trace)\n"); + goto cleanup; + } + /* Set up ring buffer polling */ + rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), print_packet, NULL, NULL); + if (!rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer(packet)\n"); + goto cleanup; } - fclose(udp_file); + //open_log_files(); + struct timeval start, end; + gettimeofday(&start, NULL); /* Process events */ while (!exiting) { err = ring_buffer__poll(rb, 100 /* timeout, ms */); err = ring_buffer__poll(udp_rb, 100 /* timeout, ms */); err = ring_buffer__poll(netfilter_rb, 100 /* timeout, ms */); + err = ring_buffer__poll(kfree_rb, 100 /* timeout, ms */); + err = ring_buffer__poll(icmp_rb, 100 /* timeout, ms */); + err = ring_buffer__poll(tcp_rb, 100 /* timeout, ms */); + err = ring_buffer__poll(dns_rb, 100 /* timeout, ms */); + err = ring_buffer__poll(trace_rb, 100 /* timeout, ms */); + err = ring_buffer__poll(mysql_rb, 100 /* timeout, ms */); + err = ring_buffer__poll(redis_rb, 100 /* timeout, ms */); + err = ring_buffer__poll(rtt_rb, 100 /* timeout, ms */); + err = ring_buffer__poll(events, 100 /* timeout, ms */); + err = ring_buffer__poll(port_rb, 100 /* timeout, ms */); + err = ring_buffer__poll(redis_stat_rb, 100 /* timeout, ms */); print_conns(skel); sleep(1); /* Ctrl-C will cause -EINTR */ @@ -450,8 +1735,28 @@ int main(int argc, char **argv) { printf("Error polling perf buffer: %d\n", err); break; } - } + gettimeofday(&end, NULL); + if ((end.tv_sec - start.tv_sec) >= 5) { + if (rst_info) { + print_stored_events(); + printf("Total RSTs in the last 5 seconds: %llu\n\n",rst_count); + rst_count = 0; + event_count = 0; + }else if (protocol_count) { + calculate_protocol_usage(proto_stats, 256, 5); + }else if(redis_stat) + { + map_fd = bpf_map__fd(skel->maps.key_count); + if (map_fd < 0) { + perror("Failed to get map FD"); + return 1; + } + print_top_5_keys(); + } + gettimeofday(&start, NULL); + } + } cleanup: net_watcher_bpf__destroy(skel); return err < 0 ? -err : 0; diff --git a/MagicEyes/src/backend/process/cpu_watcher/Makefile b/MagicEyes/src/backend/process/cpu_watcher/Makefile new file mode 100644 index 000000000..1e1d4eaab --- /dev/null +++ b/MagicEyes/src/backend/process/cpu_watcher/Makefile @@ -0,0 +1,171 @@ +# Copyright 2023 The LMP Authors. +# +# 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 +# +# https://github.com/linuxkerneltravel/lmp/blob/develop/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: zhangziheng0525@163.com +# +# compile the current folder code + +# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +OUTPUT := .output +CLANG ?= clang +LIBBPF_SRC := $(abspath ../libbpf/src) +BPFTOOL_SRC := $(abspath ../bpftool/src) +LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) +BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) +BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool +LIBBLAZESYM_SRC := $(abspath ../blazesym/) +LIBBLAZESYM_INC := $(abspath $(LIBBLAZESYM_SRC)/include) +LIBBLAZESYM_OBJ := $(abspath $(OUTPUT)/libblazesym.a) +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/') +VMLINUX := ../vmlinux/$(ARCH)/vmlinux.h +# Use our own libbpf API headers and Linux UAPI headers distributed with +# libbpf to avoid dependency on system-wide headers, which could be missing or +# outdated +INCLUDES := -I$(OUTPUT) -I../../../libbpf/include/uapi -I$(dir $(VMLINUX)) -I$(LIBBLAZESYM_INC) -I./include +CFLAGS := -g -Wall +ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) + +APPS =cs_delay sar sc_delay preempt schedule_delay mq_delay mutrace +TARGETS=cpu_watcher +CONTROLLER := controller + +SRC_DIR = ./include + + +# Get Clang's default includes on this system. We'll explicitly add these dirs +# to the includes list when compiling with `-target bpf` because otherwise some +# architecture-specific dirs will be "missing" on some architectures/distros - +# headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h, +# sys/cdefs.h etc. might be missing. +# +# Use '-idirafter': Don't interfere with include mechanics except where the +# build would have failed anyways. +CLANG_BPF_SYS_INCLUDES ?= $(shell $(CLANG) -v -E - &1 \ + | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') + +ifeq ($(V),1) + Q = + msg = +else + Q = @ + msg = @printf ' %-8s %s%s\n' \ + "$(1)" \ + "$(patsubst $(abspath $(OUTPUT))/%,%,$(2))" \ + "$(if $(3), $(3))"; + MAKEFLAGS += --no-print-directory +endif + +define allow-override + $(if $(or $(findstring environment,$(origin $(1))),\ + $(findstring command line,$(origin $(1)))),,\ + $(eval $(1) = $(2))) +endef + +$(call allow-override,CC,$(CROSS_COMPILE)cc) +$(call allow-override,LD,$(CROSS_COMPILE)ld) + +.PHONY: all +all: $(CONTROLLER) $(TARGETS) + +.PHONY: clean +clean: + $(call msg,CLEAN) + $(Q)rm -rf $(OUTPUT) $(TARGETS) $(CONTROLLER) + +$(OUTPUT) $(OUTPUT)/libbpf $(BPFTOOL_OUTPUT): + $(call msg,MKDIR,$@) + $(Q)mkdir -p $@ + +# Build libbpf +$(LIBBPF_OBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPUT)/libbpf + $(call msg,LIB,$@) + $(Q)$(MAKE) -C $(LIBBPF_SRC) BUILD_STATIC_ONLY=1 \ + OBJDIR=$(dir $@)/libbpf DESTDIR=$(dir $@) \ + INCLUDEDIR= LIBDIR= UAPIDIR= \ + install + +# Build bpftool +$(BPFTOOL): | $(BPFTOOL_OUTPUT) + $(call msg,BPFTOOL,$@) + $(Q)$(MAKE) ARCH= CROSS_COMPILE= OUTPUT=$(BPFTOOL_OUTPUT)/ -C $(BPFTOOL_SRC) bootstrap + + +$(LIBBLAZESYM_SRC)/target/release/libblazesym.a:: + $(Q)cd $(LIBBLAZESYM_SRC) && $(CARGO) build --release + +$(LIBBLAZESYM_OBJ): $(LIBBLAZESYM_SRC)/target/release/libblazesym.a | $(OUTPUT) + $(call msg,LIB, $@) + $(Q)cp $(LIBBLAZESYM_SRC)/target/release/libblazesym.a $@ + +# Build BPF code +$(OUTPUT)/%.bpf.o: bpf/%.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT) $(BPFTOOL) + $(call msg,BPF,$@) + $(Q)$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) \ + $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) \ + -c $(filter %.c,$^) -o $(patsubst %.bpf.o,%.tmp.bpf.o,$@) + $(Q)$(BPFTOOL) gen object $@ $(patsubst %.bpf.o,%.tmp.bpf.o,$@) + +# Generate BPF skeletons +.PHONY: $(APPS) +$(APPS): %: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL) + $(call msg,GEN-SKEL,$@) + $(Q)$(BPFTOOL) gen skeleton $< > $(OUTPUT)/$@.skel.h + +# Build user-space code +$(OUTPUT)/%.o: $(SRC_DIR)/%.c | $(OUTPUT) + $(call msg,CC,$@) + $(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ + +$(OUTPUT)/%.o: $(CONTROLLER).c | $(OUTPUT) + $(call msg,CC,$@) + $(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ + +$(OUTPUT)/$(TARGETS).o: $(TARGETS).c $(APPS) | $(OUTPUT) + $(call msg,CC,$@) + $(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $(filter %.c,$^) -o $@ + +# Build application binary +$(CONTROLLER): %: $(OUTPUT)/%.o $(COMMON_OBJ) $(LIBBPF_OBJ) | $(OUTPUT) + $(call msg,BINARY,$@) + $(Q)$(CC) $^ $(ALL_LDFLAGS) -lstdc++ -lelf -lz -o $@ + +$(TARGETS): %: $(OUTPUT)/%.o $(COMMON_OBJ) $(LIBBPF_OBJ) | $(OUTPUT) + $(call msg,BINARY,$@) + $(Q)$(CC) $(CFLAGS) $^ $(ALL_LDFLAGS) -lstdc++ -lelf -lz -o $@ + +SUCCESS_MESSAGE: + @echo "\e[38;2;255;0;0m __ __ \e[0m" + @echo "\e[38;2;255;128;0m _________ __ __ _ ______ _/ /______/ /_ ___ _____\e[0m" + @echo "\e[38;2;255;255;0m / ___/ __ \/ / / / | | /| / / __ / __/ ___/ __ \/ _ \/ ___/\e[0m" + @echo "\e[38;2;128;255;0m/ /__/ /_/ / /_/ / | |/ |/ / /_/ / /_/ /__/ / / / __/ / \e[0m" + @echo "\e[38;2;0;255;0m\___/ .___/\__,_/ |__/|__/\__,_/\__/\___/_/ /_/\___/_/ \e[0m" + @echo "\e[38;2;0;255;128m /_/ \e[0m" + @echo "\e[38;2;0;255;255mSuccessful to compile cpu_watcher tools: \e[0m" + @echo "\e[38;2;0;255;255mPlease start your use ~ \e[0m" + + +all: $(TARGETS) SUCCESS_MESSAGE + + +# delete failed targets +.DELETE_ON_ERROR: + +# keep intermediate (.skel.h, .bpf.o, etc) targets +.SECONDARY: \ No newline at end of file diff --git a/MagicEyes/src/backend/process/cpu_watcher/README.md b/MagicEyes/src/backend/process/cpu_watcher/README.md new file mode 100644 index 000000000..ce938ded0 --- /dev/null +++ b/MagicEyes/src/backend/process/cpu_watcher/README.md @@ -0,0 +1,255 @@ +# cpu_watcher:动态CPU指标实时监测 + +## 一、项目简介 + +​ `CPU_Watcher`是一项基于`eBPF(Extended Berkeley Packet Filter)`技术的项目,旨在实现对`CPU`各项指标的实时动态监测和分析,可以清晰且直观的了解CPU资源利用率以及事件的发生的速率。 + +​ 本工具使用ebpf工具进行实现,`eBPF`是`Linux`内核中的一种强大的工具,它允许在内核空间执行小型程序,用于在运行时过滤、转发和监控系统事件。 + +​ `CPU_Watcher`利用`eBPF`的这一特性,通过在内核空间执行精简的程序来捕获`CPU`相关的事件和指标,从而实现对`CPU`性能的实时监测和分析。 + +## 二、使用方法 + +### 1.使用环境 + +- OS:Ubuntu 22.04 +- kernel:Linux 6.2 + +### 2.编译运行 + +```shell +make 编译 +sudo ./cpu_watcher -[options] 运行 +make clean 清除生成文件 +``` + +## 三、功能介绍 + +​ `cpu_watcher`是一个用于监视系统 CPU 使用情况的工具,它可以帮助用户了解系统在不同负载下的性能表现,并提供详细的统计数据。该工具分为以下几个部分,通过不同的参数控制相关的`ebpf`捕获程序是否加载到内核中: + +| 参数 | 描述 | +| :----------------: | :----------------------------------------: | +| -s :SAR | 实时采集SAR的各项指标 | +| -i:interval | 修改SAR功能的输出间隔 | +| -P:percent | 按照百分比输出SAR功能的各项指标 | +| -p:preempt_time | 实时采集当前系统的每次抢占调度详细信息 | +| -d:schedule_delay | 实时采集当前系统的调度时延 | +| -S:syscall_delay | 实时采集当前系统调用时间 | +| -m:mq_delay | 实时采集当前消息队列通信时延 | +| -c:cs_delay | 实时对内核函数schedule()的执行时长进行测试 | + +### 1.SAR 统计功能: + +```shell +./cpu_watcher -s +``` + +#### 输出效果: + +``` + time proc/s cswch/s runqlen irqTime/us softirq/us idle/ms kthread/us sysc/ms utime/ms sys/ms +16:18:03 29 1216 1 1277 19394 1087 2908 662 747 665 +16:18:04 43 2036 2 1262 24823 1432 3981 72 171 76 +16:18:05 0 1371 2 4927 16949 1152 2489 538 636 541 +16:18:06 11 2569 4 10900 9085 518 2967 941 1121 944 +16:18:07 3 5166 4 9929 15864 469 10778 482 1020 493 +16:18:08 30 2426 2 2436 17877 1435 5086 90 262 96 +16:18:09 43 1257 1 351 20457 1713 3040 8 40 11 +16:18:10 0 813 1 20071 30563 1727 117472 41 0 159 +16:18:11 0 751 1 748 14532 1855 3935 16 50 20 +16:18:12 0 1118 1 1115 20750 1733 1956 1 50 3 +16:18:13 29 1083 1 286 18081 1698 3861 50 10 54 +16:18:14 43 1032 1 577 19513 1704 3919 26 10 30 +``` + +​ 使用参数i可以调整输出间隔,默认为1s,参数p可以按照cpu核数和自定义的输出间隔对数据进行归一化,并以百分比的形式输出,且大于60%的数据会标红输出: + +```shell +./cpu_watcher -s -i 2 -P +``` +#### 输出效果: + +![image13](image/image13.png) + + +对上述参数的解释: + +- `proc/s`: 每秒创建的进程数,此数值是通过fork数来统计的。 +- `cswch/s`: 每秒上下文切换数。 +- `runqlen`:此时CPU的运行队列的长度。 +- `irqTime/us`:CPU响应`irq`中断所占用的时间,是所有CPU时间的叠加。 +- `softirq/us`: CPU执行`softirq`所占用的时间,是所有CPU时间的叠加。 +- `idle/ms`: CPU处于空闲状态的时间,是所有CPU时间的叠加。 +- `kthread/us`: CPU执行内核线程所占用的时间,是所有CPU的叠加。不包括IDLE-0进程,因为此进程只执行空闲指令使CPU闲置。 +- `sysc/ms`: CPU执行用户程序系统调用(`syscall`)所占用的时间,是所有CPU的叠加。 +- ` utime/ms`:CPU执行普通用户进程时,花在用户态的时间,是所有CPU的叠加。 + +原理介绍: + +[libbpf_sar工具原理分析](docs/libbpf_sar.md) + +### **2.统计抢占调度时间:** + +​ 统计系统中发生抢占调度的情况,包括抢占进程的`pid`与进程名,以及被强占进程的`pid`,和本次抢占时间,单位纳秒。 + +#### 输出效果: + +``` +COMM prev_pid next_pid duration_ns +node 14221 2589 3014 +kworker/u256:1 15144 13516 1277 +node 14221 2589 3115 +kworker/u256:1 15144 13516 1125 +kworker/u256:1 15144 13516 974 +node 14221 2589 2560 +kworker/u256:1 15144 13516 1132 +node 14221 2589 2717 +kworker/u256:1 15144 13516 1206 +kworker/u256:1 15144 13516 1131 +node 14221 2589 3355 +``` + +原理介绍: + +[抢占调度原理分析](docs/preempt_time.md) + +### 3.**统计调度延迟:** + +​ 分析系统中进程调度的延迟情况,提供相关统计数据,输出包括当前系统的最大调度延迟、最小调度延迟、平均调度延迟,以及对应进程的名字。 + +#### 输出效果: + +``` + TIME avg_delay/μs max_delay/μs max_proc_name min_delay/μs min_proc_name +22:06:02 642.770000 60711.755000 node 5.227000 cpu_watcher +22:06:03 510.041000 60711.755000 node 5.227000 cpu_watcher +22:06:04 491.107000 60711.755000 node 5.227000 cpu_watcher +22:06:05 468.128000 60711.755000 node 5.227000 cpu_watcher +22:06:06 454.244000 60711.755000 node 5.227000 cpu_watcher +22:06:07 472.455000 61931.163000 node 5.227000 cpu_watcher +22:06:08 441.756000 61931.163000 node 3.360000 cpu_watcher +22:06:09 442.631000 61931.163000 node 3.360000 cpu_watcher +22:06:10 407.389000 61931.163000 node 2.549000 cpu_watcher +22:06:11 426.593000 62247.982000 node 2.549000 cpu_watcher +``` +原理介绍: + +[调度延迟原理分析](docs/schedule_delay.md) + +### 4.**统计系统调用响应时间:** + +​ 记录系统调用的响应时间,帮助用户评估系统对外部请求的处理效率, 其输出包括发起本次系统调用的进程的进程名、pid、系统调用号以及响应时间。 + +#### 输出效果: + +``` +Time Pid comm syscall_id delay/us +21:28:07 276073 cpu_watcher 1 21 +21:28:07 2579 node 0 7 +21:28:07 276073 cpu_watcher 4 8 +21:28:07 2579 node 232 6 +21:28:07 2579 node 0 6 +21:28:07 276073 cpu_watcher 1 22 +21:28:07 276073 cpu_watcher 4 8 +``` + +### 5.**统计消息队列延迟:** + +​ 统计进程间通过消息队列通信时,消息块从发送到接收的延迟情况,以便用户了解系统中进程间通信的效率和延迟,其输出内容包括发消息动作(mq_send)的延迟、接收消息动作(mq_receive)的延迟、消息块从发送到接收过程的延迟。 + +#### 输出效果: + +```c + Time Mqdes SND_PID RCV_PID SND_Delay/ms RCV_Delay/ms Delay/ms +21:40:36 3 281101 281167 0.02 0.02 2161.58542 +21:40:46 3 281432 281493 0.02 0.03 1373.68176 +21:40:52 3 281680 281741 0.03 0.05 1494.31408 +21:40:58 3 281909 281945 0.03 0.02 1434.06373 +21:41:01 3 282019 282088 0.03 0.02 1401.26321 +``` + +原理介绍: + +[消息队列延迟原理分析](docs/mq_delay.md) + +### 6.对内核函数schedule()的执行时长进行测试 + +​ 统计每次调度的执行时间,可以输出本次调度的时间,单位为微秒,并用直方图展示汇总结果: + +#### 输出效果: + +``` +t1:4817139183 t2:4817139248 delay:65 +t1:4817139255 t2:4817139319 delay:64 +t1:4817139454 t2:4817139505 delay:51 +t1:4817139512 t2:4817139557 delay:45 +t1:4817139675 t2:4817139735 delay:60 +t1:4817139742 t2:4817139800 delay:58 +t1:4817139936 t2:4817139998 delay:62 +t1:4817140005 t2:4817140065 delay:60 +t1:4817140488 t2:4817140552 delay:64 +t1:4817140559 t2:4817140621 delay:62 +t1:4817140816 t2:4817140878 delay:62 +t1:4817141241 t2:4817141303 delay:62 +``` + +```c +Time : 21:46:45 +cs_delay Count Distribution +0 => 1 585 | +2 => 3 856 | +4 => 7 2271 |** +8 => 15 5792 |***** +16 => 31 8641 |******** +32 => 63 9762 |********* +64 => 127 2041 |** +128 => 255 2158 |** +256 => 511 2075 |** +512 => 1023 751 | +1024 => 2047 301 | +2048 => 4095 112 | +4096 => 8191 36 | +8192 => 16383 0 | +16384 => 32767 0 | +32768 => 65535 0 | +65536 => 131071 0 | +131072 => 262143 0 | +262144 => 524287 0 | +524288 => 1048575 0 | +per_len = 1000 +``` + + + +## 四、实现方式 + +### 1.使用kprobe捕获内核函数的参数 + +​ 使用kprobe、kretprobe捕获挂载的内核函数的参数,从参数中提取有效的数据。比如从finish_task_switch.isra.0内核函数的参数中拿取关于prev进程的相关信息。 + +### 2.使用内核提供的tracepoint捕获特定时间 + +​ 使用tracepoint捕获特定状态的开始和结束,计算持续时间。比如softirq运行时间就是通过内核提供的tracepoint计算的。 + +### 3.获取内核全局变量 + +​ 获取内核全局变量,直接从内核全局变量读取信息。如proc/s就是通过直接读取total_forks内核全局变量来计算每秒产生进程数的。 + +## 五、cpu_watcher可视化 + +[cpu_watcher可视化指南](docs/cpu_watcher_vis_guide.md) + +## 六、未来展望 + +目前`cpu_watcher`工具的总体框架已经完成,工具所能满足的功能已覆盖CPU所涉及的大部分性能指标。下一阶段,本工具将从以下几个方向进行开发和优化: + +* 完善工具可视化; +* 功能模块化; +* 更细粒度的提取CPU相关指标; +* 完善工具,使其适配更多场景; + + + +如果你也对cpu_watcher或ebpf感兴趣,欢迎加入我们一起开发cpu_watcher工具,希望我们可以共同成长。 + +**cpu_watcher负责人:** albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com \ No newline at end of file diff --git a/MagicEyes/src/backend/process/cpu_watcher/bpf/cs_delay.bpf.c b/MagicEyes/src/backend/process/cpu_watcher/bpf/cs_delay.bpf.c index c94c5d1bf..caaf8c408 100644 --- a/MagicEyes/src/backend/process/cpu_watcher/bpf/cs_delay.bpf.c +++ b/MagicEyes/src/backend/process/cpu_watcher/bpf/cs_delay.bpf.c @@ -1,3 +1,19 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com + #include #include #include @@ -5,17 +21,28 @@ #include "cpu_watcher.h" char LICENSE[] SEC("license") = "Dual BSD/GPL"; - +const int ctrl_key = 0; //记录时间戳; BPF_ARRAY(start,int,u64,1); +BPF_ARRAY(cs_ctrl_map,int,struct cs_ctrl,1); struct { __uint(type, BPF_MAP_TYPE_RINGBUF); __uint(max_entries, 256 * 1024); } rb SEC(".maps"); +static inline struct cs_ctrl *get_cs_ctrl(void) { + struct cs_ctrl *cs_ctrl; + cs_ctrl = bpf_map_lookup_elem(&cs_ctrl_map, &ctrl_key); + if (!cs_ctrl || !cs_ctrl->cs_func) { + return NULL; + } + return cs_ctrl; +} + SEC("kprobe/schedule") int BPF_KPROBE(schedule) { + struct cs_ctrl *cs_ctrl = get_cs_ctrl(); u64 t1; t1 = bpf_ktime_get_ns()/1000; int key =0; @@ -26,6 +53,7 @@ int BPF_KPROBE(schedule) SEC("kretprobe/schedule") int BPF_KRETPROBE(schedule_exit) { + struct cs_ctrl *cs_ctrl = get_cs_ctrl(); u64 t2 = bpf_ktime_get_ns()/1000; u64 t1,delay; int key = 0; diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/mq_delay.bpf.c b/MagicEyes/src/backend/process/cpu_watcher/bpf/mq_delay.bpf.c similarity index 92% rename from eBPF_Supermarket/CPU_Subsystem/cpu_watcher/mq_delay.bpf.c rename to MagicEyes/src/backend/process/cpu_watcher/bpf/mq_delay.bpf.c index 92589afd6..3aa9dd92e 100644 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/mq_delay.bpf.c +++ b/MagicEyes/src/backend/process/cpu_watcher/bpf/mq_delay.bpf.c @@ -1,4 +1,4 @@ -// Copyright 2024 The LMP Authors. +// Copyright 2023 The LMP Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -// author: albert_xuu@163.com +// author: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com #include "vmlinux.h" #include //包含了BPF 辅助函数 @@ -23,15 +23,26 @@ char LICENSE[] SEC("license") = "Dual BSD/GPL"; - +const int ctrl_key = 0; BPF_HASH(send_msg1,pid_t,struct send_events,1024);//记录pid->u_msg_ptr的关系;do_mq_timedsend入参 BPF_HASH(send_msg2,u64,struct send_events,1024);//记录msg->time的关系; BPF_HASH(rcv_msg1,pid_t,struct rcv_events,1024);//记录pid->u_msg_ptr的关系;do_mq_timedsend入参 +BPF_ARRAY(mq_ctrl_map,int,struct mq_ctrl,1); struct { __uint(type, BPF_MAP_TYPE_RINGBUF); __uint(max_entries, 256 * 1024); } rb SEC(".maps"); +static inline struct mq_ctrl *get_mq_ctrl(void) { + struct mq_ctrl *mq_ctrl; + mq_ctrl = bpf_map_lookup_elem(&mq_ctrl_map, &ctrl_key); + if (!mq_ctrl || !mq_ctrl->mq_func) { + return NULL; + } + return mq_ctrl; +} + + // int print_send_info(struct send_events * mq_send_info,int flag){ // bpf_printk("---------------------test----------------------------test--------------------------test--------------------------------------------test---------------------test---------------------test\n"); // bpf_printk("send_msg_prio: %-8lu\n",mq_send_info->msg_prio); @@ -63,6 +74,7 @@ int BPF_KPROBE(mq_timedsend,mqd_t mqdes, const char *u_msg_ptr, size_t msg_len, unsigned int msg_prio, struct timespec64 *ts) { + struct mq_ctrl *mq_ctrl = get_mq_ctrl(); u64 send_enter_time = bpf_ktime_get_ns();//开始发送信息时间; int pid = bpf_get_current_pid_tgid();//发送端pid @@ -82,6 +94,7 @@ int BPF_KPROBE(mq_timedsend,mqd_t mqdes, const char *u_msg_ptr, /*仅获取mq_send_info -> src*/ SEC("kprobe/load_msg") int BPF_KPROBE(load_msg_enter,const void *src, size_t len){ + struct mq_ctrl *mq_ctrl = get_mq_ctrl(); int pid = bpf_get_current_pid_tgid();//发送端pid /*记录load入参src*/ struct send_events *mq_send_info = bpf_map_lookup_elem(&send_msg1, &pid); @@ -96,6 +109,7 @@ int BPF_KPROBE(load_msg_enter,const void *src, size_t len){ /*获取消息块作为key,并建立 message -> mq_send_info 的哈希表*/ SEC("kretprobe/load_msg") int BPF_KRETPROBE(load_msg_exit,void *ret){ + struct mq_ctrl *mq_ctrl = get_mq_ctrl(); int pid = bpf_get_current_pid_tgid();//发送端pid /*构建消息块结构体,作为key*/ struct send_events *mq_send_info = bpf_map_lookup_elem(&send_msg1, &pid); @@ -121,6 +135,7 @@ int BPF_KRETPROBE(load_msg_exit,void *ret){ SEC("kretprobe/do_mq_timedsend") int BPF_KRETPROBE(do_mq_timedsend_exit,void *ret) { + struct mq_ctrl *mq_ctrl = get_mq_ctrl(); bpf_printk("do_mq_timedsend_exit----------------------------------------------------------------\n"); u64 send_exit_time = bpf_ktime_get_ns();//开始发送信息时间; int pid = bpf_get_current_pid_tgid();//发送端pid @@ -148,6 +163,7 @@ int BPF_KPROBE(mq_timedreceive_entry,mqd_t mqdes, const char __user *u_msg_ptr, size_t msg_len, unsigned int msg_prio, struct timespec64 *ts) { + struct mq_ctrl *mq_ctrl = get_mq_ctrl(); u64 rcv_enter_time = bpf_ktime_get_ns(); int pid = bpf_get_current_pid_tgid(); @@ -165,6 +181,7 @@ int BPF_KPROBE(mq_timedreceive_entry,mqd_t mqdes, const char __user *u_msg_ptr, SEC("kprobe/store_msg") int BPF_KPROBE(store_msg,void __user *dest, struct msg_msg *msg, size_t len) { + struct mq_ctrl *mq_ctrl = get_mq_ctrl(); int pid = bpf_get_current_pid_tgid(); /*make key*/ @@ -192,6 +209,7 @@ int BPF_KPROBE(store_msg,void __user *dest, struct msg_msg *msg, size_t len) SEC("kretprobe/do_mq_timedreceive") int BPF_KRETPROBE(do_mq_timedreceive_exit,void *ret){ + struct mq_ctrl *mq_ctrl = get_mq_ctrl(); u64 rcv_exit_time = bpf_ktime_get_ns(); int pid = bpf_get_current_pid_tgid(); u64 send_enter_time,delay; diff --git a/MagicEyes/src/backend/process/cpu_watcher/bpf/mutrace.bpf.c b/MagicEyes/src/backend/process/cpu_watcher/bpf/mutrace.bpf.c new file mode 100644 index 000000000..79212119b --- /dev/null +++ b/MagicEyes/src/backend/process/cpu_watcher/bpf/mutrace.bpf.c @@ -0,0 +1,272 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com + +#include +#include +#include +#include +#include "cpu_watcher.h" + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; + +const int ctrl_key = 0; +BPF_HASH(kmutex_info_map, u64, struct mutex_info, 1024); +BPF_HASH(umutex_info_map, u64, struct mutex_info, 1024); +BPF_HASH(trylock_map, u64, struct trylock_info, 1024); +BPF_ARRAY(mu_ctrl_map, int, struct mu_ctrl, 1); +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} rb SEC(".maps"); + +static inline struct mu_ctrl *get_mu_ctrl(void) { + struct mu_ctrl *mu_ctrl; + mu_ctrl = bpf_map_lookup_elem(&mu_ctrl_map, &ctrl_key); + if (!mu_ctrl || !mu_ctrl->mu_func) { + return NULL; + } + return mu_ctrl; +} + +/*----------------------------------------------*/ +/* 内核态互斥锁 */ +/*----------------------------------------------*/ + +SEC("kprobe/mutex_lock") +int BPF_KPROBE(trace_mutex_lock, struct mutex *lock) { + u64 lock_addr = (u64)lock; // 获取锁地址 + u64 ts = bpf_ktime_get_ns(); + struct mutex_info *info = bpf_map_lookup_elem(&kmutex_info_map, &lock_addr); + if (info) { + info->acquire_time = ts; // 保存锁获取时间 + } else { + struct mutex_info new_info = { + .locked_total = 0, + .locked_max = 0, + .contended_total = 0, + .count = 0, + .last_owner = 0, + .acquire_time = ts, + .ptr = lock_addr + }; + __builtin_memset(new_info.last_name, 0, sizeof(new_info.last_name)); + bpf_map_update_elem(&kmutex_info_map, &lock_addr, &new_info, BPF_ANY); + } + return 0; +} + +SEC("kprobe/mutex_trylock") +int BPF_KPROBE(trace_mutex_trylock, struct mutex *lock) { + int ret = PT_REGS_RC(ctx); + if (ret != 0) { // 成功获取锁 + u64 lock_addr = (u64)lock; // 获取锁地址 + u64 ts = bpf_ktime_get_ns(); + struct mutex_info *info = bpf_map_lookup_elem(&kmutex_info_map, &lock_addr); + if (info) { + info->acquire_time = ts; + } else { + struct mutex_info new_info = { + .locked_total = 0, + .locked_max = 0, + .contended_total = 0, + .count = 0, + .last_owner = 0, + .acquire_time = ts, + .ptr = lock_addr + }; + __builtin_memset(new_info.last_name, 0, sizeof(new_info.last_name)); + bpf_map_update_elem(&kmutex_info_map, &lock_addr, &new_info, BPF_ANY); + } + } + return 0; +} + +SEC("kprobe/__mutex_lock_slowpath") +int BPF_KPROBE(trace_mutex_lock_slowpath, struct mutex *lock) { + struct mu_ctrl *mu_ctrl = get_mu_ctrl(); + struct mutex_contention_event *e; + struct task_struct *owner_task; + struct task_struct *contender_task; + pid_t pid = bpf_get_current_pid_tgid(); + long owner; + u64 lock_addr = (u64)lock; + u64 ts = bpf_ktime_get_ns(); + e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); + if (!e) { + return 0; + } + e->contender_pid = pid; + e->ptr = lock_addr; + bpf_get_current_comm(&e->contender_name, sizeof(e->contender_name)); + bpf_probe_read_kernel(&owner, sizeof(owner), &lock->owner); + owner_task = (struct task_struct *)(owner & ~0x1L); + contender_task = (struct task_struct *)bpf_get_current_task(); + bpf_probe_read_kernel(&e->contender_prio, sizeof(e->contender_prio), &contender_task->prio); + if (owner_task) { + bpf_probe_read_kernel(&e->owner_pid, sizeof(e->owner_pid), &owner_task->pid); + bpf_probe_read_kernel_str(&e->owner_name, sizeof(e->owner_name), owner_task->comm); + bpf_probe_read_kernel(&e->owner_prio, sizeof(e->owner_prio), &owner_task->prio); + } else { + e->owner_pid = 0; + __builtin_memset(e->owner_name, 0, sizeof(e->owner_name)); + } + struct mutex_info *info = bpf_map_lookup_elem(&kmutex_info_map, &lock_addr); + if (info) { + u64 contention_start = ts; + info->contended_total += (contention_start - info->acquire_time); // 更新争用时间 + info->count++; // 更新争用次数 + } else { + struct mutex_info new_info = { + .locked_total = 0, + .locked_max = 0, + .contended_total = 0, + .count = 1, // 初始化争用次数 + .last_owner = 0, + .acquire_time = ts, // 初始化获取时间 + .ptr = lock_addr + }; + __builtin_memset(new_info.last_name, 0, sizeof(new_info.last_name)); + bpf_map_update_elem(&kmutex_info_map, &lock_addr, &new_info, BPF_ANY); + } + bpf_ringbuf_submit(e, 0); + return 0; +} + +SEC("kprobe/mutex_unlock") +int BPF_KPROBE(trace_mutex_unlock, struct mutex *lock) { + u64 lock_addr = (u64)lock; + u64 ts = bpf_ktime_get_ns(); + pid_t pid = bpf_get_current_pid_tgid(); + struct mutex_info *info = bpf_map_lookup_elem(&kmutex_info_map, &lock_addr); + if (info) { + u64 held_time = ts - info->acquire_time; // 计算锁被持有的时间 + info->locked_total += held_time; // 更新锁被持有的总时间 + if (held_time > info->locked_max) { + info->locked_max = held_time; // 更新锁被持有的最长时间 + } + info->last_owner = pid; // 更新最后一次持有该锁的线程ID + bpf_get_current_comm(&info->last_name, sizeof(info->last_name)); // 更新最后一次持有该锁的线程名称 + } + return 0; +} + + + +/*----------------------------------------------*/ +/* 用户态互斥锁 */ +/*----------------------------------------------*/ + + + +SEC("uprobe/pthread_mutex_lock") +int BPF_KPROBE(pthread_mutex_lock, void *__mutex) { + u64 pid_tgid = bpf_get_current_pid_tgid(); + pid_t pid = pid_tgid >> 32; + u64 now = bpf_ktime_get_ns(); + + struct mutex_info *info = bpf_map_lookup_elem(&umutex_info_map, &__mutex); + if (info) { + if (info->acquire_time > 0) { + // 如果 acquire_time 已经被设置,说明锁被争用 + info->contended_total += (now - info->acquire_time); + info->count += 1; + } + info->acquire_time = now; + info->last_owner = pid; + bpf_get_current_comm(&info->last_name, sizeof(info->last_name)); + } else { + // 初始化 mutex_info + struct mutex_info new_info = { + .locked_total = 0, + .locked_max = 0, + .contended_total = 0, + .count = 0, + .last_owner = pid, + .acquire_time = now, + .ptr = (u64)__mutex, + }; + bpf_get_current_comm(&new_info.last_name, sizeof(new_info.last_name)); + bpf_map_update_elem(&umutex_info_map, &__mutex, &new_info, BPF_ANY); + } + return 0; +} + +SEC("uprobe/__pthread_mutex_trylock") +int BPF_KPROBE(__pthread_mutex_trylock, void *__mutex) { + u64 pid_tgid = bpf_get_current_pid_tgid(); + u64 now = bpf_ktime_get_ns(); + struct trylock_info info = { + .__mutex = __mutex, + .start_time = now, + }; + bpf_map_update_elem(&trylock_map, &pid_tgid, &info, BPF_ANY); + return 0; +} + +SEC("uretprobe/__pthread_mutex_trylock") +int BPF_KRETPROBE(ret_pthread_mutex_trylock, int ret) { + u64 pid_tgid = bpf_get_current_pid_tgid(); + struct trylock_info *try_info = bpf_map_lookup_elem(&trylock_map, &pid_tgid); + if (!try_info) { + return 0; + } + void *__mutex = try_info->__mutex; + u64 now = bpf_ktime_get_ns(); + if (ret == 0) { + struct mutex_info *info = bpf_map_lookup_elem(&umutex_info_map, &__mutex); + if (info) { + if (info->acquire_time > 0) { + // 如果 acquire_time 已经被设置,说明锁被争用 + info->contended_total += (now - info->acquire_time); + info->count += 1; + } + info->acquire_time = now; + info->last_owner = pid_tgid >> 32; + bpf_get_current_comm(&info->last_name, sizeof(info->last_name)); + } else { + // 初始化 mutex_info + struct mutex_info new_info = { + .locked_total = 0, + .locked_max = 0, + .contended_total = 0, + .count = 0, + .last_owner = pid_tgid >> 32, + .acquire_time = now, + .ptr = (u64)__mutex, + }; + bpf_get_current_comm(&new_info.last_name, sizeof(new_info.last_name)); + bpf_map_update_elem(&umutex_info_map, &__mutex, &new_info, BPF_ANY); + } + } + bpf_map_delete_elem(&trylock_map, &pid_tgid); + return 0; +} + +SEC("uprobe/pthread_mutex_unlock") +int BPF_KPROBE(pthread_mutex_unlock, void *__mutex){ + u64 now = bpf_ktime_get_ns(); + struct mutex_info *info = bpf_map_lookup_elem(&umutex_info_map, &__mutex); + if (info) { + u64 locked_time = now - info->acquire_time; + info->locked_total += locked_time; + if (locked_time > info->locked_max) { + info->locked_max = locked_time; + } + info->acquire_time = 0; + } + return 0; +} + diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/preempt.bpf.c b/MagicEyes/src/backend/process/cpu_watcher/bpf/preempt.bpf.c similarity index 56% rename from eBPF_Supermarket/CPU_Subsystem/cpu_watcher/preempt.bpf.c rename to MagicEyes/src/backend/process/cpu_watcher/bpf/preempt.bpf.c index 0abbbc781..bf650a6e0 100644 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/preempt.bpf.c +++ b/MagicEyes/src/backend/process/cpu_watcher/bpf/preempt.bpf.c @@ -1,3 +1,19 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com + #include #include #include @@ -7,17 +23,27 @@ char LICENSE[] SEC("license") = "Dual BSD/GPL"; #define TIF_NEED_RESCHED 3 - +const int ctrl_key = 0; // 记录时间戳 BPF_HASH(preemptTime, pid_t, u64, 4096); - +BPF_ARRAY(preempt_ctrl_map,int,struct preempt_ctrl,1); struct { __uint(type, BPF_MAP_TYPE_RINGBUF); __uint(max_entries, 256 * 1024); } rb SEC(".maps"); +static inline struct preempt_ctrl *get_preempt_ctrl(void) { + struct preempt_ctrl *preempt_ctrl; + preempt_ctrl = bpf_map_lookup_elem(&preempt_ctrl_map, &ctrl_key); + if (!preempt_ctrl || !preempt_ctrl->preempt_func) { + return NULL; + } + return preempt_ctrl; +} + SEC("tp_btf/sched_switch") int BPF_PROG(sched_switch, bool preempt, struct task_struct *prev, struct task_struct *next) { + struct preempt_ctrl *preempt_ctrl = get_preempt_ctrl(); u64 start_time = bpf_ktime_get_ns(); pid_t prev_pid = BPF_CORE_READ(prev, pid); @@ -33,8 +59,10 @@ int BPF_PROG(sched_switch, bool preempt, struct task_struct *prev, struct task_s return 0; } -SEC("kprobe/finish_task_switch") +// SEC("kprobe/finish_task_switch") +SEC("kprobe/finish_task_switch.isra.0") int BPF_KPROBE(finish_task_switch, struct task_struct *prev) { + struct preempt_ctrl *preempt_ctrl = get_preempt_ctrl(); u64 end_time = bpf_ktime_get_ns(); pid_t pid = BPF_CORE_READ(prev, pid); u64 *val; diff --git a/MagicEyes/src/backend/process/cpu_watcher/bpf/sar.bpf.c b/MagicEyes/src/backend/process/cpu_watcher/bpf/sar.bpf.c index bbbbf8254..00420b7d4 100644 --- a/MagicEyes/src/backend/process/cpu_watcher/bpf/sar.bpf.c +++ b/MagicEyes/src/backend/process/cpu_watcher/bpf/sar.bpf.c @@ -1,3 +1,19 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com + #include #include #include @@ -7,24 +23,22 @@ char LICENSE[] SEC("license") = "Dual BSD/GPL"; const volatile long long unsigned int forks_addr = 0; - +const int ctrl_key = 0; #define PF_IDLE 0x00000002 /* I am an IDLE thread */ #define PF_KTHREAD 0x00200000 /* I am a kernel thread */ // 计数表格,第0项为所统计fork数,第1项为进程切换数 BPF_ARRAY(countMap,int,u64,3); // 记录开始的时间 -BPF_ARRAY(procStartTime,pid_t,u64,4096); -// 储存运行队列rq的全局变量 -BPF_ARRAY(rq_map,u32,struct rq,1); +BPF_ARRAY(procStartTime,pid_t,u64,1024); //存储运行队列长度 -BPF_PERCPU_ARRAY(runqlen,u32,int,1); +BPF_ARRAY(runqlen,u32,int,1); //记录软中断开始时间 -BPF_HASH(softirqCpuEnterTime,u32,u64,4096); +BPF_HASH(softirqCpuEnterTime,u32,u64,1024); //记录软中断结束时间 BPF_HASH(softirqLastTime,u32,u64,1); // 记录开始的时间 -BPF_HASH(irq_cpu_enter_start,u32,u64,8192); +BPF_HASH(irq_cpu_enter_start,u32,u64,1024); //记录上次中断时间 BPF_ARRAY(irq_Last_time,u32,u64,1); // 储存cpu进入空闲的起始时间 @@ -37,11 +51,23 @@ BPF_ARRAY(kt_LastTime,u32,u64,1); BPF_ARRAY(ut_LastTime,u32,u64,1); BPF_ARRAY(tick_user,u32,u64,1); BPF_ARRAY(symAddr,u32,u64,1); +BPF_ARRAY(sar_ctrl_map,int,struct sar_ctrl,1); + +static inline struct sar_ctrl *get_sar_ctrl(void) { + struct sar_ctrl *sar_ctrl; + sar_ctrl = bpf_map_lookup_elem(&sar_ctrl_map, &ctrl_key); + if (!sar_ctrl || !sar_ctrl->sar_func) { + return NULL; + } + return sar_ctrl; +} + // 统计fork数 -//SEC("kprobe/finish_task_switch.isra.0") -SEC("kprobe/finish_task_switch") +SEC("kprobe/finish_task_switch.isra.0") +// SEC("kprobe/finish_task_switch") int kprobe__finish_task_switch(struct pt_regs *ctx) { + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); u32 key = 0; u64 val, *valp = NULL; unsigned long total_forks; @@ -59,6 +85,7 @@ int kprobe__finish_task_switch(struct pt_regs *ctx) //获取进程切换数; SEC("tracepoint/sched/sched_switch") int trace_sched_switch2(struct cswch_args *info) { + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); pid_t prev = info->prev_pid, next = info->next_pid; if (prev != next) { u32 key = 0; @@ -77,9 +104,10 @@ int trace_sched_switch2(struct cswch_args *info) { return 0; } -SEC("kprobe/finish_task_switch") -//SEC("kprobe/finish_task_switch.isra.0") +// SEC("kprobe/finish_task_switch") +SEC("kprobe/finish_task_switch.isra.0") int BPF_KPROBE(finish_task_switch,struct task_struct *prev){ + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); pid_t pid=BPF_CORE_READ(prev,pid); u64 *val, time = bpf_ktime_get_ns(); u64 delta; @@ -109,17 +137,10 @@ int BPF_KPROBE(finish_task_switch,struct task_struct *prev){ //统计运行队列长度 SEC("kprobe/update_rq_clock") -int kprobe_update_rq_clock(struct pt_regs *ctx){ +int BPF_KPROBE(update_rq_clock,struct rq *rq){ + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); u32 key = 0; - u32 rqkey = 0; - struct rq *p_rq = 0; - p_rq = (struct rq *)bpf_map_lookup_elem(&rq_map, &rqkey); - if (!p_rq) { - return 0; - } - bpf_probe_read_kernel(p_rq, sizeof(struct rq), (void *)PT_REGS_PARM1(ctx)); - //使用bpf_probe_read_kernel函数将内核空间中的数据复制到p_rq所指向的内存区域中,以便后续对该数据进行访问和操作。 - u64 val = p_rq->nr_running; + u64 val = BPF_CORE_READ(rq,nr_running); bpf_map_update_elem(&runqlen,&key,&val,BPF_ANY); return 0; } @@ -127,6 +148,7 @@ int kprobe_update_rq_clock(struct pt_regs *ctx){ //软中断 SEC("tracepoint/irq/softirq_entry") int trace_softirq_entry(struct __softirq_info *info) { + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); u32 key = info->vec; u64 val = bpf_ktime_get_ns(); bpf_map_update_elem(&softirqCpuEnterTime, &key, &val, BPF_ANY); @@ -135,6 +157,7 @@ int trace_softirq_entry(struct __softirq_info *info) { SEC("tracepoint/irq/softirq_exit") int trace_softirq_exit(struct __softirq_info *info) { + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); u32 key = info->vec; u64 now = bpf_ktime_get_ns(), *valp = 0; valp =bpf_map_lookup_elem(&softirqCpuEnterTime, &key); @@ -153,6 +176,7 @@ int trace_softirq_exit(struct __softirq_info *info) { 注意这是所有CPU时间的叠加,平均到每个CPU应该除以CPU个数。*/ SEC("tracepoint/irq/irq_handler_entry") int trace_irq_handler_entry(struct __irq_info *info) { + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); u32 key = info->irq; u64 ts = bpf_ktime_get_ns(); bpf_map_update_elem(&irq_cpu_enter_start, &key, &ts, BPF_ANY); @@ -161,6 +185,7 @@ int trace_irq_handler_entry(struct __irq_info *info) { SEC("tracepoint/irq/irq_handler_exit") int trace_irq_handler_exit(struct __irq_info *info) { + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); u32 key = info->irq; u64 now = bpf_ktime_get_ns(), *ts = 0; ts = bpf_map_lookup_elem(&irq_cpu_enter_start, &key); @@ -180,6 +205,7 @@ int trace_irq_handler_exit(struct __irq_info *info) { //tracepoint:power_cpu_idle 表征了CPU进入IDLE的状态,比较准确 SEC("tracepoint/power/cpu_idle") int trace_cpu_idle(struct idleStruct *pIDLE) { + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); u64 delta, time = bpf_ktime_get_ns(); u32 key = pIDLE->cpu_id; if (pIDLE->state == -1) { @@ -209,6 +235,7 @@ static __always_inline int user_mode(struct pt_regs *regs) // 两个CPU各自会产生一个调用,这正好方便我们使用 SEC("perf_event") int tick_update(struct pt_regs *ctx) { + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); // bpf_trace_printk("cs_rpl = %x\n", ctx->cs & 3); u32 key = 0; diff --git a/MagicEyes/src/backend/process/cpu_watcher/bpf/sc_delay.bpf.c b/MagicEyes/src/backend/process/cpu_watcher/bpf/sc_delay.bpf.c index 778cc9676..ee224283a 100644 --- a/MagicEyes/src/backend/process/cpu_watcher/bpf/sc_delay.bpf.c +++ b/MagicEyes/src/backend/process/cpu_watcher/bpf/sc_delay.bpf.c @@ -1,52 +1,89 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com + #include "vmlinux.h" -#include //包含了BPF 辅助函数 +#include #include #include "cpu_watcher.h" char LICENSE[] SEC("license") = "Dual BSD/GPL"; -// 定义数组映射 -BPF_HASH(procStartTime,pid_t,u64,4096);//记录时间戳 + +const int ctrl_key = 0; +BPF_PERCPU_HASH(SyscallEnterTime,pid_t,u64,512); +BPF_PERCPU_HASH(Events,pid_t,u64,10); +BPF_ARRAY(sc_ctrl_map,int,struct sc_ctrl,1); + struct { __uint(type, BPF_MAP_TYPE_RINGBUF); __uint(max_entries, 256 * 1024); } rb SEC(".maps");//环形缓冲区; +static inline struct sc_ctrl *get_sc_ctrl(void) { + struct sc_ctrl *sc_ctrl; + sc_ctrl = bpf_map_lookup_elem(&sc_ctrl_map, &ctrl_key); + if (!sc_ctrl || !sc_ctrl->sc_func) { + return NULL; + } + return sc_ctrl; +} -SEC("tracepoint/syscalls/sys_enter_execve")//进入系统调用 -int tracepoint__syscalls__sys_enter_execve(struct trace_event_raw_sys_enter* ctx){ - u64 start_time = bpf_ktime_get_ns()/1000;//ms - pid_t pid = bpf_get_current_pid_tgid() >> 32;//获取到当前进程的pid - bpf_map_update_elem(&procStartTime,&pid,&start_time,BPF_ANY); - return 0; +SEC("tracepoint/raw_syscalls/sys_enter") +int tracepoint__syscalls__sys_enter(struct trace_event_raw_sys_enter *args){ + struct sc_ctrl *sc_ctrl = get_sc_ctrl(); + u64 start_time = bpf_ktime_get_ns()/1000; + pid_t pid = bpf_get_current_pid_tgid(); + u64 syscall_id = (u64)args->id; + + //bpf_printk("ID:%ld\n",syscall_id); + bpf_map_update_elem(&Events,&pid,&syscall_id,BPF_ANY); + bpf_map_update_elem(&SyscallEnterTime,&pid,&start_time,BPF_ANY); + return 0; } -SEC("tracepoint/syscalls/sys_exit_execve")//退出系统调用 -int tracepoint__syscalls__sys_exit_execve(struct trace_event_raw_sys_exit* ctx){ - u64 exit_time = bpf_ktime_get_ns()/1000;//ms +SEC("tracepoint/raw_syscalls/sys_exit") +int tracepoint__syscalls__sys_exit(struct trace_event_raw_sys_exit *args){ + struct sc_ctrl *sc_ctrl = get_sc_ctrl(); + u64 exit_time = bpf_ktime_get_ns()/1000; + pid_t pid = bpf_get_current_pid_tgid() ; + u64 syscall_id; u64 start_time, delay; - pid_t pid = bpf_get_current_pid_tgid() >> 32;//获取到当前进程的pid - u64 *val = bpf_map_lookup_elem(&procStartTime, &pid); + u64 *val = bpf_map_lookup_elem(&SyscallEnterTime, &pid); if(val !=0){ start_time = *val; delay = exit_time - start_time; - bpf_map_delete_elem(&procStartTime, &pid); + bpf_map_delete_elem(&SyscallEnterTime, &pid); }else{ return 0; } - - struct event2 *e; + u64 *val2 = bpf_map_lookup_elem(&Events, &pid); + if(val2 !=0){ + syscall_id = *val2; + bpf_map_delete_elem(&SyscallEnterTime, &pid); + }else{ + return 0; + } + struct syscall_events *e; e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); - if (!e) return 0; - - e->start_time=start_time;//开始时间 - e->exit_time=exit_time;//结束时间 - e->delay=delay;//时间间隔 + if (!e) return 0; e->pid = pid; + e->delay = delay; bpf_get_current_comm(&e->comm, sizeof(e->comm)); - /* 成功地将其提交到用户空间进行后期处理 */ + e->syscall_id = syscall_id; bpf_ringbuf_submit(e, 0); - return 0; -} \ No newline at end of file +} diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/schedule_delay.bpf.c b/MagicEyes/src/backend/process/cpu_watcher/bpf/schedule_delay.bpf.c similarity index 51% rename from eBPF_Supermarket/CPU_Subsystem/cpu_watcher/schedule_delay.bpf.c rename to MagicEyes/src/backend/process/cpu_watcher/bpf/schedule_delay.bpf.c index fda48bda6..db1b2a363 100644 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/schedule_delay.bpf.c +++ b/MagicEyes/src/backend/process/cpu_watcher/bpf/schedule_delay.bpf.c @@ -1,3 +1,19 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com + #include #include #include @@ -7,14 +23,27 @@ char LICENSE[] SEC("license") = "Dual BSD/GPL"; #define TASK_RUNNING 0x0000 -BPF_HASH(has_scheduled,struct proc_id, bool, 10240); -BPF_HASH(enter_schedule,struct proc_id, struct schedule_event, 10240); -BPF_ARRAY(sys_schedule,int,struct sum_schedule,1); +const int ctrl_key = 0; +BPF_HASH(has_scheduled,struct proc_id, bool, 10240);//记录该进程是否调度过 +BPF_HASH(enter_schedule,struct proc_id, struct schedule_event, 10240);//记录该进程上运行队列的时间 +BPF_ARRAY(sys_schedule,int,struct sum_schedule,1);//记录整个系统的调度延迟 +BPF_ARRAY(threshold_schedule,int,struct proc_schedule,10240);//记录每个进程的调度延迟 +BPF_HASH(proc_histories,struct proc_id, struct proc_history, 10240);//记录每个进程运行前的两个进程 +BPF_ARRAY(schedule_ctrl_map,int,struct schedule_ctrl,1); +static inline struct schedule_ctrl *get_schedule_ctrl(void) { + struct schedule_ctrl *sched_ctrl; + sched_ctrl = bpf_map_lookup_elem(&schedule_ctrl_map, &ctrl_key); + if (!sched_ctrl || !sched_ctrl->schedule_func) { + return NULL; + } + return sched_ctrl; +}//查找控制结构体 SEC("tp_btf/sched_wakeup") int BPF_PROG(sched_wakeup, struct task_struct *p) { - pid_t pid = BPF_CORE_READ(p, pid); + struct schedule_ctrl *sched_ctrl = get_schedule_ctrl(); + pid_t pid = p->pid; int cpu = bpf_get_smp_processor_id(); struct schedule_event *schedule_event; struct proc_id id= {}; @@ -40,7 +69,11 @@ int BPF_PROG(sched_wakeup, struct task_struct *p) { SEC("tp_btf/sched_wakeup_new") int BPF_PROG(sched_wakeup_new, struct task_struct *p) { - pid_t pid = BPF_CORE_READ(p, pid); + struct schedule_ctrl *sched_ctrl = get_schedule_ctrl(); + sched_ctrl = bpf_map_lookup_elem(&schedule_ctrl_map,&ctrl_key); + if(!sched_ctrl || !sched_ctrl->schedule_func) + return 0; + pid_t pid = p->pid; int cpu = bpf_get_smp_processor_id(); struct proc_id id= {}; u64 current_time = bpf_ktime_get_ns(); @@ -60,6 +93,9 @@ int BPF_PROG(sched_wakeup_new, struct task_struct *p) { SEC("tp_btf/sched_switch") int BPF_PROG(sched_switch, bool preempt, struct task_struct *prev, struct task_struct *next) { + struct schedule_ctrl *sched_ctrl = get_schedule_ctrl(); + struct proc_history *history; + struct proc_history new_history; u64 current_time = bpf_ktime_get_ns(); pid_t prev_pid = prev->pid; unsigned int prev_state = prev->__state; @@ -70,17 +106,17 @@ int BPF_PROG(sched_switch, bool preempt, struct task_struct *prev, struct task_s struct schedule_event *schedule_event; struct sum_schedule *sum_schedule; int key = 0; - struct proc_id next_id= {}; + struct proc_id next_id = {}; u64 delay; if (prev_state == TASK_RUNNING) { - struct proc_id prev_pd= {}; + struct proc_id prev_pd = {}; prev_pd.pid = prev_pid; if (prev_pid == 0) { prev_pd.cpu_id = prev_cpu; - } + } schedule_event = bpf_map_lookup_elem(&enter_schedule, &prev_pd); if (!schedule_event) { - struct schedule_event schedule_event2 ; + struct schedule_event schedule_event2; bool issched = false; schedule_event2.pid = prev_pid; schedule_event2.count = 1; @@ -97,38 +133,71 @@ int BPF_PROG(sched_switch, bool preempt, struct task_struct *prev, struct task_s next_id.cpu_id = next_cpu; } schedule_event = bpf_map_lookup_elem(&enter_schedule, &next_id); - if (!schedule_event) return 0; + if (!schedule_event) return 0; issched = bpf_map_lookup_elem(&has_scheduled, &next_id); - if (!issched) return 0; + if (!issched) return 0; if (*issched) { schedule_event->count++; } else { *issched = true; - } + } delay = current_time - schedule_event->enter_time; + struct proc_schedule proc_schedule; + proc_schedule.delay = delay; + proc_schedule.id= next_id; + bpf_probe_read_kernel_str(&proc_schedule.proc_name, sizeof(proc_schedule.proc_name), next->comm); + bpf_map_update_elem(&threshold_schedule, &key, &proc_schedule, BPF_ANY); sum_schedule = bpf_map_lookup_elem(&sys_schedule, &key); if (!sum_schedule) { - struct sum_schedule sum_schedule= {}; + struct sum_schedule sum_schedule = {}; sum_schedule.sum_count++; sum_schedule.sum_delay += delay; - if (delay > sum_schedule.max_delay) + if (delay > sum_schedule.max_delay) { sum_schedule.max_delay = delay; - if (sum_schedule.min_delay == 0 || delay < sum_schedule.min_delay) + if (next->pid != 0) { + bpf_probe_read_kernel_str(&sum_schedule.proc_name_max, sizeof(sum_schedule.proc_name_max), next->comm); + } + } else if (sum_schedule.min_delay == 0 || delay < sum_schedule.min_delay) { sum_schedule.min_delay = delay; + if (next->pid != 0) { + bpf_probe_read_kernel_str(&sum_schedule.proc_name_min, sizeof(sum_schedule.proc_name_min), next->comm); + } + } bpf_map_update_elem(&sys_schedule, &key, &sum_schedule, BPF_ANY); } else { sum_schedule->sum_count++; sum_schedule->sum_delay += delay; - if (delay > sum_schedule->max_delay) + if (delay > sum_schedule->max_delay) { sum_schedule->max_delay = delay; - if (sum_schedule->min_delay == 0 || delay < sum_schedule->min_delay) + bpf_probe_read_kernel_str(&sum_schedule->proc_name_max, sizeof(sum_schedule->proc_name_max), next->comm); + } else if (sum_schedule->min_delay == 0 || delay < sum_schedule->min_delay) { sum_schedule->min_delay = delay; + if (next->pid != 0) { + bpf_probe_read_kernel_str(&sum_schedule->proc_name_min, sizeof(sum_schedule->proc_name_min), next->comm); + } + } + } + history = bpf_map_lookup_elem(&proc_histories, &next_id); + if (history) { + // 如果找到了,更新历史记录 + new_history.last[0] = history->last[1]; + new_history.last[1].pid = prev->pid; + bpf_probe_read_kernel_str(&new_history.last[1].comm, sizeof(new_history.last[1].comm), prev->comm); + bpf_map_update_elem(&proc_histories, &next_id, &new_history, BPF_ANY); + } else { + // 如果没有找到,初始化新的历史记录 + new_history.last[0].pid = 0; // 初始化为0,表示没有历史信息 + new_history.last[0].comm[0] = '\0'; + new_history.last[1].pid = prev->pid; + bpf_probe_read_kernel_str(&new_history.last[1].comm, sizeof(new_history.last[1].comm), prev->comm); + bpf_map_update_elem(&proc_histories, &next_id, &new_history, BPF_ANY); } return 0; } SEC("tracepoint/sched/sched_process_exit") int sched_process_exit(void *ctx) { + struct schedule_ctrl *sched_ctrl = get_schedule_ctrl(); struct task_struct *p = (struct task_struct *)bpf_get_current_task(); pid_t pid = BPF_CORE_READ(p, pid); int cpu = bpf_get_smp_processor_id(); @@ -147,4 +216,4 @@ int sched_process_exit(void *ctx) { bpf_map_delete_elem(&has_scheduled, &id); } return 0; -} +} \ No newline at end of file diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/cpu_wacther_vis_guide.md b/MagicEyes/src/backend/process/cpu_watcher/docs/cpu_wacther_vis_guide.md new file mode 100644 index 000000000..a20f821b9 --- /dev/null +++ b/MagicEyes/src/backend/process/cpu_watcher/docs/cpu_wacther_vis_guide.md @@ -0,0 +1,168 @@ +# cpu_wacther的可视化 + +## 1.配置环境 + +在使用cpu_watcher可视化之前,请先配置docker、go的环境,具体配置方法可参考: + +### 1.1 docker + +先参考该链接进行docker-desktop的安装: + +* [在 Ubuntu 上安装 Docker Desktop |Docker 文档](https://docs.docker.com/desktop/install/ubuntu/#install-docker-desktop) + +在准备启动docker-desktop时,可能遇到打不开docker-desktop的情况,如下所示: + +![image1](image/image1.png) + +这是因为虚拟机暂时不支持虚拟化,可以先关闭虚拟机,重新编辑虚拟机设置,开启虚拟化引擎的两个选项,再开机配置kvm; + +* [在 Linux 上安装 Docker Desktop |Docker 文档](https://docs.docker.com/desktop/install/linux-install/) + +![image2](image/image2.png) + +### 1.2 go环境: + +本可视化功能对go的版本有要求,请安装go1.19+版本,具体安装流程可参考如下链接: + +* [go:快速升级Go版本,我几分钟就解决了_go 升级版本-CSDN博客](https://blog.csdn.net/m0_37482190/article/details/128673828) + +## 2.使用cpuwatcher 可视化 + +* 首先先进入lmp目录下的lmp/eBPF_Supermarket/CPU_Subsystem/cpu_watcher文件夹 + + ```BASH + cd lmp/eBPF_Supermarket/CPU_Subsystem/cpu_watcher + ``` + + 在该目录下 进行make编译 + + ```bash + make -j 20 + ``` + +* 在lmp目录下的eBPF_Visualization/eBPF_prometheus文件夹下 + +* 执行`make`指令,编译可视化的go语言工具 + + 在执行make指令时,如果出现如下报错,是因为go包管理代理网址无法访问`proxy.golang.org` + + ```bash + go: golang.org/x/exp@v0.0.0-20190731235908-ec7cb31e5a56: Get "https://proxy.golang.org/golang.org/x/exp/@v/v0.0.0-20190731235908-ec7cb31e5a56.mod": dial tcp 172.217.160.113:443: i/o timeout + ``` + + 只需要换一个国内能访问的2代理地址即可 + + ```bash + go env -w GOPROXY=https://goproxy.cn + ``` + +* 执行`make start_service`指令,配置下载docker镜像并启动grafana和prometheus服务 + +* 执行如下指令开始采集数据以及相关处理: + + ```bash + ./data-visual collect /home/zhang/lmp/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/cpu_watcher -s + ``` + +* 在网页打开网址:http://192.168.159.128:8090/metrics 此处为`localhost:8090/metrics`,便可以看到暴露在http网页中的数据; + + ![image3](image/image3.png) + +* 在网页打开网址:http://192.168.159.128:3000/ 即可进入grafana服务,使用初始密码登录(user:admin pswd: admin)进入管理界面: + + - 点击【Home-Connection-Add new connection】,选择Prometheus,建立与Prometheus服务器的连接: + + ![image4](image/image4.png) + + 这个172.17.0.1表示docker0网桥的 IPv4 地址。在 Docker 中,通常会将docker0的第一个 IP 地址分配给Docker主机自身。因此,172.17.0.1是 Docker主机上Docker守护进程的 IP 地址,所以在Grafana数据源这块设置成[http://172.17.0.1:9090](http://172.17.0.1:9090/) ,然后点击下面的【Save & test】按钮 + +- 进入可视化配置界面: + + ![image4.5](image/image4.5.png) + ![image5](image/image5.png) + +- 在下方处进行如图所示的配置,点击Run queries即可以可视化的方式监控avg_delay字段的数据: + + ![image6](image/image6.png) + +## 3.cpu_watcher各子工具可视化输出 + +本次可视化输出样例,是在对比系统正常运行和高负载运行时本工具的使用情况,使用stress加压工具对cpu进行持续5min的加压 + +```bash +stress --cpu 8 --timeout 300s +``` + +### 3.1 cpu_watcher -s + +**【irq Time】可视化输出结果** + +![image7](image/image7.png) + +**【softirq Time】可视化输出结果** + +![image8](image/image8.png) +**【cswch】可视化输出结果** + +![image9](image/image9.png) +**【proc】可视化输出结果** + +![image10](image/image10.png) + +**【Kthread】可视化输出结果** + +![image11](image/image11.png) + +**【idle】可视化输出结果** + +![image12](image/image12.png) + +**【sys】可视化输出结果** + +![image-20240411132742107](image/image-20240411132742107.png) + +**【sysc】可视化输出结果** + +![image-20240411132807253](image/image-20240411132807253.png) + +**【utime】可视化输出结果** + +![image-20240411132842070](image/image-20240411132842070.png) + +**【cpu处于不同状态对比图】可视化输出结果** + +![image-20240411132914396](image/image-20240411132914396.png) + +### 3.2 cpu_watcher -c + +**【cs_delay】可视化输出结果** + +![image-20240411133505763](image/image-20240411133505763.png) + +### 3.3 cpu_watcher -d + +**【schedule_delay】可视化输出结果** + +【max_delay】 + +![image-20240411133841698](image/image-20240411133841698.png) + +【avg_delay】 + +![image-20240411135159178](image/image-20240411135159178.png) + +【min_delay】 + +![image-20240411135335523](image/image-20240411135335523.png) + +### 3.4 cpu_watcher -p + +**【preempt】可视化输出结果** + +![image-20240411142421440](image/image-20240411142421440.png) + +## 3.5 cpu_watcher -S + +**【syscall_delay】可视化输出结果** + +![image-20240411144331888](image/image-20240411144331888.png) \ No newline at end of file diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411132742107.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411132742107.png new file mode 100644 index 000000000..dcfb6ad1d Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411132742107.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411132807253.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411132807253.png new file mode 100644 index 000000000..941b9006f Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411132807253.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411132842070.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411132842070.png new file mode 100644 index 000000000..1c32506ac Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411132842070.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411132914396.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411132914396.png new file mode 100644 index 000000000..7d156c222 Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411132914396.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411133505763.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411133505763.png new file mode 100644 index 000000000..109b29e59 Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411133505763.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411133841698.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411133841698.png new file mode 100644 index 000000000..84279fb8d Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411133841698.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411135159178.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411135159178.png new file mode 100644 index 000000000..ad7c56591 Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411135159178.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411135335523.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411135335523.png new file mode 100644 index 000000000..5c46cc2f1 Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411135335523.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411142421440.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411142421440.png new file mode 100644 index 000000000..b308e3268 Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411142421440.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411144331888.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411144331888.png new file mode 100644 index 000000000..6e174c2f5 Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411144331888.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411160509242.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411160509242.png new file mode 100644 index 000000000..bc7a81d97 Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411160509242.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411170250839.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411170250839.png new file mode 100644 index 000000000..5b6bdc717 Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411170250839.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411170311182.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411170311182.png new file mode 100644 index 000000000..a900381ba Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image-20240411170311182.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image1.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image1.png new file mode 100644 index 000000000..b0c02f418 Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image1.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image10.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image10.png new file mode 100644 index 000000000..8c4a4bf93 Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image10.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image11.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image11.png new file mode 100644 index 000000000..dc04b23f1 Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image11.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image12.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image12.png new file mode 100644 index 000000000..bfb87edef Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image12.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image13.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image13.png new file mode 100644 index 000000000..566fd1c1e Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image13.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image2.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image2.png new file mode 100644 index 000000000..140a97b81 Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image2.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image3.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image3.png new file mode 100644 index 000000000..52db1de49 Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image3.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image4.5.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image4.5.png new file mode 100644 index 000000000..40aef3a11 Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image4.5.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image4.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image4.png new file mode 100644 index 000000000..d186d225c Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image4.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image5.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image5.png new file mode 100644 index 000000000..48f6a4a74 Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image5.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image6.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image6.png new file mode 100644 index 000000000..77baa7887 Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image6.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image7.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image7.png new file mode 100644 index 000000000..cbf0a5915 Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image7.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image8.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image8.png new file mode 100644 index 000000000..23c5d49fd Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image8.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image9.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image9.png new file mode 100644 index 000000000..e0b384f07 Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image9.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/image/image_mq.png b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image_mq.png new file mode 100644 index 000000000..1dae1df8f Binary files /dev/null and b/MagicEyes/src/backend/process/cpu_watcher/docs/image/image_mq.png differ diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/libbpf_sar.md b/MagicEyes/src/backend/process/cpu_watcher/docs/libbpf_sar.md new file mode 100644 index 000000000..4944fc264 --- /dev/null +++ b/MagicEyes/src/backend/process/cpu_watcher/docs/libbpf_sar.md @@ -0,0 +1,81 @@ +# libbpf_sar功能介绍: + +libbpf_sar是cpu_watcher工具中的一个子工具,通过cpu_watcher -s使用; + +## 1.应用场景及意义 + +libbpf_sar是一个基于eBPF的按照指定时间间隔(默认为1s)来统计特定事件发生次数和特定事件占用CPU时间的工具。使用它可以帮助您查看事件发生速率和CPU资源利用率,并提供CPU负载画像以剖析负载的来源及其占用比例。 + +与传统工具相比,libbpf_sar可提供更为细致的指标,如: + +* 1.可把内核态时间剖析为内核线程执行时间sar和进程系统调用时间 + +* 2.部分linux发行版可能由于内核编译选项确实而不能记录irq时间,本工具可以弥补这一缺陷,并且不需要对内核做出任何更改,可动态检测 + +3.可附加到指定进程上,对该进程占用的CPU资源进行实时监测 + +## 2.性能参数及观测意义 + +在 Linux 中,与 CPU 相关的状态可以分为很多类,如用户态、系统调用、硬中断以及软中断等,同时还有一些内核线程执行的状态,如 kthread,idle 进程。 + +同时,也有一些对 CPU 相当重要的事件,如新进程创建,进程切换计数,运行队 列长度等,对这些细分事件进行准确的监测,有利于我们剖析出 CPU 负载根源, 并定位出性能瓶颈。 + +libbpf_sar主要是对 CPU 上的关键事件提供的具体信息参数与含义如表3-6所示: + +libbpf_sar 实例采集信息及含义 + +| **性能指标** | **含义** | +| ------------ | -------------------------------- | +| Proc | 新创建的进程数 | +| Cswch | 进程切换计数 | +| runqlen | 运行队列长度 | +| irqTime | 硬中断时间 | +| Softirq | 软中断时间 | +| Idle | Idle 进程运行时间 | +| Sysc | 加上内核线程运行时间的系统态时间 | +| Utime | 用户态执行时间 | +| sys | 系统调用执行时间 | + +本实例采集到的信息将系统的 CPU 进行了精准分类,并且也统计了关键事件的触发频率,对于系统的性能分析有重要的意义 + +## 3.输出格式: + +```bash + time proc/s cswch/s irqTime/us softirq/us idle/ms kthread/us sysc/ms utime/ms sys/ms +15:55:43 48 1389 1646 8866 6811 3243 688 717 691 +15:55:44 31 1089 1587 7375 6759 1868 659 707 660 +15:55:45 47 1613 1685 8885 6792 3268 796 828 799 +15:55:46 0 2133 5938 7797 7643 8106 8 20 17 +15:55:47 1 3182 5128 14279 6644 4883 314 363 319 +15:55:48 0 1815 1773 11329 6753 4286 282 313 287 +15:55:49 31 1249 1605 9859 6752 4442 545 585 549 +15:55:50 47 1601 1712 11348 6765 6249 210 242 216 +15:55:51 0 1238 10591 12709 6802 13896 238 262 252 +15:55:52 0 1145 1658 10000 6863 4593 308 333 313 +15:55:53 0 1317 1587 9090 6798 4699 383 414 387 +15:55:54 31 1254 1531 9570 6755 4252 381 414 385 +15:55:55 47 1547 1624 10985 6769 6516 344 373 350 +15:55:56 0 1064 2187 9892 6851 4585 189 212 194 +``` + +* proc/s 列的数据表示 CPU 每秒新创建的进程数; +* cswch/s 列的数据表示 CPU 每秒进程切换的数量; +* runqlen 列的数据表示 CPU 运行队列的长度; +* irqTime/us 列的数据表示 CPU 处理硬中断的时间,以 us 计时; +* softirq/s 列的数据表示 CPU 每秒处理软中断的时间,以 us 计时; +* idle/ms 列的数据表示 系统处于空闲态的时间; +* kthread/us 列的数据表示系统中内核线程执行的时间; + +* sysc/ms 表示系统中内核线程外加系统调用处理的总时间; +* utime/ms 表示进程在用户态执行的总时间; +* sys/ms 表示系统调用执行的总时间。 + +事件的统计方式是对每个CPU分开统计然后求和,因此有的项的占用时间可能超过1s。所有事件占用时间的和应当为1s * 总CPU核数。由于技术问题,输出的第一项可能偏差较大,可以不予理会。按Ctrl+C本程序将停止统计,并输出在程序运行的时间段内各表项的平均值按照2s的采样频率显示各事件的CPU占用率。数据是带颜色的,蓝色表示CPU占比小于30%,绿色表示占比大于30%小于60%,红色表示占比大于60%。 + + + +## 4.数据可视化 + +![image-20240411160509242](image/image-20240411160509242.png) +![image-20240411170250839](image/image-20240411170250839.png) +![image-20240411170311182](image/image-20240411170311182.png) \ No newline at end of file diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/mq_delay.md b/MagicEyes/src/backend/process/cpu_watcher/docs/mq_delay.md new file mode 100644 index 000000000..cfcc1ba5b --- /dev/null +++ b/MagicEyes/src/backend/process/cpu_watcher/docs/mq_delay.md @@ -0,0 +1,92 @@ +# mq_delay + +为了对进程间通过消息队列通信时,发送消息、接手消息以及处于等待状态所用时间进行监测,cpuwatcher工具增添mq_delay工具。 + +![image_mq](image/image_mq.png) + +以上是发送进程发送,接收进程接收的具体过程。本工具通过跟踪单个的消息块(struct msg_msg结构体)来监测发送时延、接收时延以及等待时延。 + +## 跟踪消息块过程: + +发送过程 + +* 用户程序将要发送的消息通过mq_send()函数或mq_timedsend()函数发送,mq_send/mq_timedsend函数调用mq_timedsend系统调用在内核实现具体的发送实现,此时将指向用户态消息缓冲区的指针u_msg_ptr传入内核态,此处我们第一次追踪到消息块。 +* 在mq_timedsend 系统调用中,会调用do_mq_timedsend()内核函数进行发送消息的操作,此处将u_msg_ptr指针作为传参传入do_mq_timedsend()函数; +* 在do_mq_timedsend()函数中,通过load_msg()函数将消息从用户空间加载到内核中,这里将u_msg_ptr指针作为传参; +* load_msg()函数中,通过copy_from_user()函数将u_msg_ptr指针指向的用户空间信息复制到分配的内核空间msg,并返回一个指向消息块所在内核空间的指针msg_ptr,此时我们便在内核中跟踪到了具体的消息块实体,后续操作都是围绕这个消息块指针展开的,包括接收程序也是对此指针进行copy_to_user操作; + +接受过程 + +* 用户程序通过mq_receive()或mq_timedreceive()函数,从消息队列中接收消息,mq_receive()或mq_timedreceive()函数调用mq_timedreceive系统调用在内核中实现具体的接收实现,此时将指向用户态缓冲区的指针u_msg_ptr传入内核,这里是我们本次跟踪最后一次遇到消息块。 +* mq_timedreceive系统调用通过do_mq_timedreceive()函数找到要接收的消息块,并将其传入u_msg_ptr所指向的用户空间 +* do_mq_timedreceive()函数如果等到要接收的消息块,会通过store_msg()函数将消息块(发送时msg_ptr所指向的消息块)存储至u_msg_ptr所指向的用户空间。所以此时,我们在接收消息的内核处理函数中追踪到了具体的消息块。 + +此处还可拓展一些功能: + +* 对于发送消息块时,是否上等待队列,等待了多久? +* 对于接收消息块时,是否上等待队列,等待了多久? +* 对于处于非阻塞状态的进程,是否可以识别到,并及时统计出来? + +## 挂载点: + +发送过程: + +| 类型 | 名称 | +| --------- | -------------- | +| kprobe | do_mq_timesend | +| kprobe | load_msg | +| kretprobe | load_msg | +| kretprobe | do_mq_timesend | + +接收过程: + +| 类型 | 名称 | +| --------- | ----------------- | +| kprobe | do_mq_timereceive | +| kprobe | store_msg | +| kretprobe | store_msg | +| kretprobe | do_mq_timereceive | + +输出效果 + +```c +Time: 22:12:39 +----------------------------------------------------------------------------------------------------------------------- +Mqdes: 3 msg_len: 1152 msg_prio: 50 +SND_PID: 20945 SND_enter_time: 131725037824711 SND_exit_time: 131725037867085 +RCV_PID: 20984 RCV_enter_time: 131726555726321 RCV_exit_time: 131726555872719 +------------------------------------------------------------------------------- +SND_Delay/ms: 0.04 RCV_Delay/ms: 0.15 Delay/ms: 1518.04801 +----------------------------------------------------------------------------------------------------------------------- + + + +Time: 22:12:44 +----------------------------------------------------------------------------------------------------------------------- +Mqdes: 3 msg_len: 1152 msg_prio: 50 +SND_PID: 21007 SND_enter_time: 131730008219660 SND_exit_time: 131730008614901 +RCV_PID: 21035 RCV_enter_time: 131731465676396 RCV_exit_time: 131731465758821 +------------------------------------------------------------------------------- +SND_Delay/ms: 0.40 RCV_Delay/ms: 0.08 Delay/ms: 1457.53916 +----------------------------------------------------------------------------------------------------------------------- + + + +Time: 22:12:48 +----------------------------------------------------------------------------------------------------------------------- +Mqdes: 3 msg_len: 1152 msg_prio: 50 +SND_PID: 21069 SND_enter_time: 131733828139276 SND_exit_time: 131733828195905 +RCV_PID: 21098 RCV_enter_time: 131735705540405 RCV_exit_time: 131735705924036 +------------------------------------------------------------------------------- +SND_Delay/ms: 0.06 RCV_Delay/ms: 0.38 Delay/ms: 1877.78476 +----------------------------------------------------------------------------------------------------------------------- +``` + + + + + + + + + diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/preempt_time.md b/MagicEyes/src/backend/process/cpu_watcher/docs/preempt_time.md new file mode 100644 index 000000000..e35aaf291 --- /dev/null +++ b/MagicEyes/src/backend/process/cpu_watcher/docs/preempt_time.md @@ -0,0 +1,44 @@ +## preempt_time工具介绍 + +​ preempt_time,统计每次系统中抢占调度所用的时间。 + +### 原理分析 + +​ 使用 btf raw tracepoint监控内核中的每次调度事件: + +```c +SEC("tp_btf/sched_switch") +``` + +​ btf raw tracepoint 跟常规 raw tracepoint 有一个 最主要的区别是: btf 版本可以直接在 ebpf 程序中访问内核内存, 不需要像常规 raw tracepoint 一样需要借助类似 `bpf_core_read` 或 `bpf_probe_read_kernel` 这样 的辅助函数才能访问内核内存。 + +```c +int BPF_PROG(sched_switch, bool preempt, struct task_struct *prev, struct task_struct *next) +``` + +​ 该事件为我们提供了关于抢占的参数preempt,我们可以通过判断preempt的值来决定是否记录本次调度信息。 + +​ 另一挂载点为kprobe:finish_task_switch,即本次调度切换完成进行收尾工作的函数,在此时通过ebpf map与之前记录的调度信息做差,即可得到本次抢占调度的时间: + +```c +SEC("kprobe/finish_task_switch") +``` + +### 输出效果 + +可以获取到抢占进程的`pid`与进程名,以及被抢占进程的`pid`,和本次抢占时间,单位纳秒 + +``` +COMM prev_pid next_pid duration_ns +node 14221 2589 3014 +kworker/u256:1 15144 13516 1277 +node 14221 2589 3115 +kworker/u256:1 15144 13516 1125 +kworker/u256:1 15144 13516 974 +node 14221 2589 2560 +kworker/u256:1 15144 13516 1132 +node 14221 2589 2717 +kworker/u256:1 15144 13516 1206 +kworker/u256:1 15144 13516 1131 +node 14221 2589 3355 +``` diff --git a/MagicEyes/src/backend/process/cpu_watcher/docs/schedule_delay.md b/MagicEyes/src/backend/process/cpu_watcher/docs/schedule_delay.md new file mode 100644 index 000000000..563cfec6b --- /dev/null +++ b/MagicEyes/src/backend/process/cpu_watcher/docs/schedule_delay.md @@ -0,0 +1,57 @@ +## schedule_delay工具介绍 + +​ schedule_delay工具可以检测该系统当前的调度延迟。即从一个任务具备运行的条件,到真正执行(获得 CPU 的执行权)的这段时间。 + +​ 实时观测该指标可以帮助我们了解到当前操作系统的负载。 + +### 原理分析 + +​ 只需考虑,在何时一个任务会被加入运行队列等待运行。内核提供了两个函数实现这个功能: + +- 新建的进程通过调用`wake_up_new_task`,将新创建的任务加入runqueue等待调度。 +- 进程从睡眠状态被唤醒时触发,调用`ttwu_do_wakeup`函数,进入runqueue等待调度。 + +​ 关于这两个函数,内核提供了两个对应的`tracepoint`: + +| 内核函数 | 对应`tracepoint` | +| :--------------: | :--------------------: | +| wake_up_new_task | sched:sched_wakeup_new | +| ttwu_do_wakeup | sched:sched_wakeup | + +​ 在触发到这两个tracepoint的时候,记录这个进程的信息和进入运行队列的时间。 + +​ 除此之外,我们还应该考虑到,当一个进程**被迫离开cpu**时,其状态依然是`TASK_RUNNING`,所以在schedule时,我们还要做出判断,决定该进程是否要被记录。 + +| 内核函数 | 对应`tracepoint` | +| :------: | :----------------: | +| schedule | sched:sched_switch | + +​ 在触发到这个tracepoint时,记录此时即将要占用cpu的进程信息,通过ebpf map记录的进入运行队列的时间作差,即调度延迟。在这里还需要判断上一个进程是否要被记录。 + +```c + if(prev_state == TASK_RUNNING)//判断退出cpu进程的状态 +``` + +​ 最后要注意的是,为了避免map溢出,我们还需要在进程退出的时候,删除map中记录的数据。 + +| 内核函数 | 对应`tracepoint` | +| :------: | :----------------------: | +| do_exit | sched:sched_process_exit | + +### 输出效果 + +​ 我们可以检测到系统从加载ebpf程序到当前的平均、最大、最小调度时延: + +``` + TIME avg_delay/μs max_delay/μs min_delay/μs +17:31:28 35.005000 97.663000 9.399000 +17:31:29 326.518000 12618.465000 7.994000 +17:31:30 455.837000 217053.545000 6.462000 +17:31:31 422.582000 217053.545000 6.462000 +17:31:32 382.627000 217053.545000 6.462000 +17:31:33 360.499000 217053.545000 6.462000 +17:31:34 364.805000 217053.545000 6.462000 +17:31:35 362.039000 217053.545000 6.462000 +17:31:36 373.751000 217053.545000 6.462000 +``` + diff --git a/MagicEyes/src/backend/process/cpu_watcher/grafana_cpu_watcher_dashboard.json b/MagicEyes/src/backend/process/cpu_watcher/grafana_cpu_watcher_dashboard.json new file mode 100644 index 000000000..9177bf611 --- /dev/null +++ b/MagicEyes/src/backend/process/cpu_watcher/grafana_cpu_watcher_dashboard.json @@ -0,0 +1,1177 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"avg_delay/us\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "1", + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"max_delay/us\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "interval": "1", + "legendFormat": "__auto", + "range": true, + "refId": "B", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"min_delay/μs\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "interval": "1", + "legendFormat": "__auto", + "range": true, + "refId": "C", + "useBackend": false + } + ], + "title": "schedule_delay", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"irqTime/us\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "irqTime", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"duration_ns\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "preempt_delay", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "scheme", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "smooth", + "lineWidth": 3, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "right", + "showLegend": false + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"proc/s\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "1", + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "procs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 16 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"sys/ms\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "sys", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 16 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"softirq/us\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "softirqTime", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"utime/ms\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "utime", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 24 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"idle/ms\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "1", + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "idle", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 32 + }, + "id": 7, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"kthread/us\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "kthread", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 40 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"cswch/s\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "cswch", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "scheme", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "smooth", + "lineWidth": 3, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 48 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "right", + "showLegend": false + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"delay/ms\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "1", + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "sys_delay", + "type": "timeseries" + } + ], + "refresh": "", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timeRangeUpdatedDuringEditOrView": false, + "timepicker": {}, + "timezone": "browser", + "title": "cpu_watcher_vis", + "uid": "cdlnxoxcy4zr4b", + "version": 4, + "weekStart": "" +} \ No newline at end of file diff --git a/MagicEyes/src/backend/process/cpu_watcher/include/cpu_watcher.h b/MagicEyes/src/backend/process/cpu_watcher/include/cpu_watcher.h index 44b364680..7ffb2b3fa 100644 --- a/MagicEyes/src/backend/process/cpu_watcher/include/cpu_watcher.h +++ b/MagicEyes/src/backend/process/cpu_watcher/include/cpu_watcher.h @@ -12,40 +12,214 @@ // See the License for the specific language governing permissions and // limitations under the License. // -// author: zhangziheng0525@163.com -// -// eBPF map for libbpf sar +// author: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com +#ifndef CPU_WATCHER_H +#define CPU_WATCHER_H + #include #include -typedef long long unsigned int u64; +typedef unsigned long long u64; typedef unsigned int u32; -#define MAX_CPU_NR 128 +typedef __kernel_mqd_t mqd_t; +#define __user +#define MAX_CPU_NR 128 #define TASK_COMM_LEN 20 #define SYSCALL_MIN_TIME 1E7 +#define MAX_SYSCALL_COUNT 100 +#define MAX_ENTRIES 102400 // map容量 + +/*----------------------------------------------*/ +/* 一些maps结构体的宏定义 */ +/*----------------------------------------------*/ +/// @brief 创建一个指定名字和键值类型的ebpf数组 +/// @param name 新散列表的名字 +/// @param type1 键的类型 +/// @param type2 值的类型 +/// @param MAX_ENTRIES map容量 +#define BPF_ARRAY(name, type1, type2, MAX_ENTRIES) \ + struct { \ + __uint(type, BPF_MAP_TYPE_ARRAY); \ + __uint(key_size, sizeof(type1)); \ + __uint(value_size, sizeof(type2)); \ + __uint(max_entries, MAX_ENTRIES); \ + } name SEC(".maps") +/// @brief 创建一个指定名字和键值类型的ebpf散列表 +/// @param name 新散列表的名字 +/// @param type1 键的类型 +/// @param type2 值的类型 +/// @param MAX_ENTRIES 哈希map容量 +#define BPF_HASH(name, type1, type2, MAX_ENTRIES) \ + struct { \ + __uint(type, BPF_MAP_TYPE_HASH); \ + __uint(key_size, sizeof(type1)); \ + __uint(value_size, sizeof(type2)); \ + __uint(max_entries, MAX_ENTRIES); \ + } name SEC(".maps") +/// @brief 创建一个指定名字和键值类型的ebpf每CPU数组 +/// @param name 新散列表的名字 +/// @param type1 键的类型 +/// @param type2 值的类型 +/// @param MAX_ENTRIES map容量 +#define BPF_PERCPU_ARRAY(name, type1, type2, MAX_ENTRIES) \ + struct { \ + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); \ + __uint(key_size, sizeof(type1)); \ + __uint(value_size, sizeof(type2)); \ + __uint(max_entries, MAX_ENTRIES); \ + } name SEC(".maps") +/// @brief 创建一个指定名字和键值类型的ebpf每CPU散列表 +/// @param name 新散列表的名字 +/// @param type1 键的类型 +/// @param type2 值的类型 +/// @param MAX_ENTRIES map容量 +#define BPF_PERCPU_HASH(name, type1, type2, MAX_ENTRIES) \ + struct { \ + __uint(type, BPF_MAP_TYPE_PERCPU_HASH); \ + __uint(key_size, sizeof(type1)); \ + __uint(value_size, sizeof(type2)); \ + __uint(max_entries, MAX_ENTRIES); \ + } name SEC(".maps") + /*----------------------------------------------*/ /* cs_delay结构体 */ /*----------------------------------------------*/ #ifndef __CS_DELAY_H #define __CS_DELAY_H struct event { - long unsigned int t1; - long unsigned int t2; - long unsigned int delay; + u64 t1; + u64 t2; + u64 delay; }; #endif /* __CS_DELAY_H */ /*----------------------------------------------*/ /* syscall_delay结构体 */ /*----------------------------------------------*/ -struct event2 { - long unsigned int start_time; - long unsigned int exit_time; - long unsigned int delay; - int pid; +struct syscall_flags { + u64 start_time; + int syscall_id; +}; + +struct syscall_events {//每个进程一个 + int pid, count; + char comm[TASK_COMM_LEN]; + u64 delay; + u64 syscall_id; +}; +/*----------------------------------------------*/ +/* preempt_event结构体 */ +/*----------------------------------------------*/ +struct preempt_event { + pid_t prev_pid; + pid_t next_pid; + unsigned long long duration; + char comm[TASK_COMM_LEN]; +}; +/*----------------------------------------------*/ +/* schedule_delay相关结构体 */ +/*----------------------------------------------*/ +//标识不同进程 +struct proc_id { + int pid; + int cpu_id; +}; +//标识该进程的调度信息 +struct schedule_event { + int pid; + int count;//调度次数 + unsigned long long enter_time; +}; +//整个系统所有调度信息 +struct sum_schedule { + unsigned long long sum_count; + unsigned long long sum_delay; + unsigned long long max_delay; + unsigned long long min_delay; + char proc_name_max[TASK_COMM_LEN]; + char proc_name_min[TASK_COMM_LEN]; +}; + +struct proc_schedule { + struct proc_id id; + unsigned long long delay; + char proc_name[TASK_COMM_LEN]; +}; + +struct proc_info { + pid_t pid; char comm[TASK_COMM_LEN]; }; +struct proc_history { + struct proc_info last[2]; // 存储最后两个调度的进程信息 +}; + +/*----------------------------------------------*/ +/* mutrace相关结构体 */ +/*----------------------------------------------*/ +struct mutex_info { + u64 locked_total;//锁被持有的总时间 + u64 locked_max;//锁被持有的最长时间 + u64 contended_total;//锁发生竞争的总时间 + int count;//记录锁被争用的总次数 + pid_t last_owner;//最后一次持有该锁的线程 ID + char last_name[TASK_COMM_LEN]; + u64 acquire_time; // 锁每次被获取的时间戳,方便后续计算 + u64 ptr;//地址 +}; + +struct mutex_contention_event { + u64 ptr;//锁地址 + pid_t owner_pid;//持有者pid + pid_t contender_pid;//抢占者pid + char contender_name[TASK_COMM_LEN]; + char owner_name[TASK_COMM_LEN]; + int owner_prio; + int contender_prio; +}; + +struct trylock_info { + void *__mutex; + u64 start_time; +}; + +/*----------------------------------------------*/ +/* mq_delay相关结构体 */ +/*----------------------------------------------*/ +struct mq_events { + int send_pid; + int rcv_pid; + mqd_t mqdes; + size_t msg_len; + unsigned int msg_prio; + u64 send_enter_time; + u64 send_exit_time; + u64 rcv_enter_time; + u64 rcv_exit_time; +}; +struct send_events { + int send_pid; + u64 Key_msg_ptr; + mqd_t mqdes; + size_t msg_len; + unsigned int msg_prio; + const char *u_msg_ptr; + const void *src; + u64 send_enter_time; + u64 send_exit_time; +}; +struct rcv_events { + int rcv_pid; + u64 Key_msg_ptr; + mqd_t mqdes; + size_t msg_len; + unsigned int msg_prio; + const char *u_msg_ptr; + const void *dest; + u64 rcv_enter_time; + u64 rcv_exit_time; +}; /*----------------------------------------------*/ /* cswch_args结构体 */ /*----------------------------------------------*/ @@ -85,62 +259,47 @@ struct idleStruct { unsigned int cpu_id; }; - /*----------------------------------------------*/ -/* 一些maps结构体的宏定义 */ +/* 控制板块 */ /*----------------------------------------------*/ -/// @brief 创建一个指定名字和键值类型的ebpf数组 -/// @param name 新散列表的名字 -/// @param type1 键的类型 -/// @param type2 值的类型 -/// @param MAX_ENTRIES map容量 -#define BPF_ARRAY(name, type1,type2,MAX_ENTRIES ) \ - struct \ - { \ - __uint(type, BPF_MAP_TYPE_ARRAY); \ - __uint(key_size, sizeof(type1)); \ - __uint(value_size, sizeof(type2)); \ - __uint(max_entries, MAX_ENTRIES); \ - } name SEC(".maps") +struct sar_ctrl{ + bool sar_func; + bool percent; + int prev_watcher; +}; -/// @brief 创建一个指定名字和键值类型的ebpf散列表 -/// @param name 新散列表的名字 -/// @param type1 键的类型 -/// @param type2 值的类型 -/// @param MAX_ENTRIES 哈希map容量 -#define BPF_HASH(name, type1,type2,MAX_ENTRIES ) \ - struct \ - { \ - __uint(type, BPF_MAP_TYPE_HASH); \ - __uint(key_size, sizeof(type1)); \ - __uint(value_size, sizeof(type2)); \ - __uint(max_entries, MAX_ENTRIES); \ - } name SEC(".maps") +struct cs_ctrl{ + bool cs_func; + int prev_watcher; +}; -/// @brief 创建一个指定名字和键值类型的ebpf每CPU数组 -/// @param name 新散列表的名字 -/// @param type1 键的类型 -/// @param type2 值的类型 -/// @param MAX_ENTRIES map容量 -#define BPF_PERCPU_ARRAY(name, type1,type2,MAX_ENTRIES ) \ - struct \ - { \ - __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); \ - __uint(key_size, sizeof(type1)); \ - __uint(value_size, sizeof(type2)); \ - __uint(max_entries, MAX_ENTRIES); \ - } name SEC(".maps") +struct sc_ctrl{ + bool sc_func; + int prev_watcher; +}; -/// @brief 创建一个指定名字和键值类型的ebpf每CPU散列表 -/// @param name 新散列表的名字 -/// @param type1 键的类型 -/// @param type2 值的类型 -/// @param MAX_ENTRIES map容量 -#define BPF_PERCPU_HASH(name, type1,type2,MAX_ENTRIES ) \ - struct \ - { \ - __uint(type, BPF_MAP_TYPE_PERCPU_HASH); \ - __uint(key_size, sizeof(type1)); \ - __uint(value_size, sizeof(type2)); \ - __uint(max_entries, MAX_ENTRIES); \ - } name SEC(".maps") \ No newline at end of file +struct preempt_ctrl{ + bool preempt_func; + int prev_watcher; +}; + +struct schedule_ctrl{ + bool schedule_func; + bool min_us_set; + int min_us; + int prev_watcher; +}; + +struct mq_ctrl{ + bool mq_func; + int prev_watcher; +}; + +struct mu_ctrl{ + bool mu_func; + bool mutex_detail; + bool umutex; + int prev_watcher; +}; + +#endif // CPU_WATCHER_H \ No newline at end of file diff --git a/MagicEyes/src/backend/process/cpu_watcher/include/cpu_watcher_helper.h b/MagicEyes/src/backend/process/cpu_watcher/include/cpu_watcher_helper.h new file mode 100644 index 000000000..08e38f0e1 --- /dev/null +++ b/MagicEyes/src/backend/process/cpu_watcher/include/cpu_watcher_helper.h @@ -0,0 +1,353 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com +#ifndef CPU_WATCHER_HELPER_H +#define CPU_WATCHER_HELPER_H + +#include +#include "cpu_watcher.h" + +#define SAR_WACTHER 10 +#define CS_WACTHER 20 +#define SC_WACTHER 30 +#define PREEMPT_WACTHER 40 +#define SCHEDULE_WACTHER 50 +#define MQ_WACTHER 60 +#define MUTEX_WATCHER 70 +#define HASH_SIZE 1024 + +/*----------------------------------------------*/ +/* ewma算法 */ +/*----------------------------------------------*/ +//滑动窗口周期,用于计算alpha +#define CYCLE 10 +//阈值容错空间; +#define TOLERANCE 1.0 +struct ewma_info{ + double previousEWMA; + int count; + int cycle;//cycle是滑动窗口周期大小 +}; + +double calculateEWMA(double previousEWMA, double dataPoint, double alpha) { + return alpha * dataPoint + (1 - alpha) * previousEWMA;//当前时间点的ewma +} + +bool dynamic_filter(struct ewma_info *ewma_syscall_delay, double dataPoint) { + double alpha,threshold; + if(ewma_syscall_delay->cycle==0) alpha = 2.0 /(CYCLE + 1); // 计算 alpha + else alpha = 2.0 /(ewma_syscall_delay->cycle + 1); + + if(ewma_syscall_delay->previousEWMA == 0) {//初始化ewma算法,则赋值previousEWMA = dataPoint 并打印 + ewma_syscall_delay->previousEWMA = dataPoint; + return 1; + } + if(ewma_syscall_delay->count <30){ + ewma_syscall_delay->previousEWMA = calculateEWMA(ewma_syscall_delay->previousEWMA,dataPoint,alpha);//计算 + return 1; + } + else{ + ewma_syscall_delay->previousEWMA = calculateEWMA(ewma_syscall_delay->previousEWMA,dataPoint,alpha);//计算 + threshold = ewma_syscall_delay->previousEWMA * TOLERANCE; + if(dataPoint >= threshold) return 1; + } + return 0; +} + +/*----------------------------------------------*/ +/* bpf file system */ +/*----------------------------------------------*/ +const char *sar_ctrl_path = "/sys/fs/bpf/cpu_watcher_map/sar_ctrl_map"; +const char *cs_ctrl_path = "/sys/fs/bpf/cpu_watcher_map/cs_ctrl_map"; +const char *sc_ctrl_path = "/sys/fs/bpf/cpu_watcher_map/sc_ctrl_map"; +const char *preempt_ctrl_path = "/sys/fs/bpf/cpu_watcher_map/preempt_ctrl_map"; +const char *schedule_ctrl_path = "/sys/fs/bpf/cpu_watcher_map/schedule_ctrl_map"; +const char *mq_ctrl_path = "/sys/fs/bpf/cpu_watcher_map/mq_ctrl_map"; +const char *mu_ctrl_path = "/sys/fs/bpf/cpu_watcher_map/mu_ctrl_map"; + +int common_pin_map(struct bpf_map **bpf_map, const struct bpf_object *obj, const char *map_name, const char *ctrl_path) +{ + int ret; + + *bpf_map = bpf_object__find_map_by_name(obj, map_name);//查找具有指定名称的 BPF 映射 + if (!*bpf_map) { + fprintf(stderr, "Failed to find BPF map\n"); + return -1; + } + // 用于防止上次没有成功 unpin 掉这个 map + bpf_map__unpin(*bpf_map, ctrl_path); + ret = bpf_map__pin(*bpf_map, ctrl_path); + if (ret){ + fprintf(stderr, "Failed to pin BPF map\n"); + return -1; + }//找到pin上 + + return 0; +} + +int update_sar_ctrl_map(struct sar_ctrl sar_ctrl){ + int err,key = 0; + int srcmap_fd; + + srcmap_fd = bpf_obj_get(sar_ctrl_path); + if (srcmap_fd < 0) { + fprintf(stderr,"Failed to open sar_ctrl_map file\n"); + return srcmap_fd; + } + err = bpf_map_update_elem(srcmap_fd,&key,&sar_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update sar_ctrl_map elem\n"); + return err; + } + + return 0; +} + +int update_cs_ctrl_map(struct cs_ctrl cs_ctrl){ + int err,key = 0; + int srcmap_fd; + + srcmap_fd = bpf_obj_get(cs_ctrl_path); + if (srcmap_fd < 0) { + fprintf(stderr,"Failed to open cs_ctrl_map file\n"); + return srcmap_fd; + } + err = bpf_map_update_elem(srcmap_fd,&key,&cs_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update cs_ctrl_map elem\n"); + return err; + } + + return 0; +} + +int update_sc_ctrl_map(struct sc_ctrl sc_ctrl){ + int err,key = 0; + int srcmap_fd; + + srcmap_fd = bpf_obj_get(sc_ctrl_path); + if (srcmap_fd < 0) { + fprintf(stderr,"Failed to open sc_ctrl_map file\n"); + return srcmap_fd; + } + err = bpf_map_update_elem(srcmap_fd,&key,&sc_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update sc_ctrl_map elem\n"); + return err; + } + + return 0; +} + +int update_preempt_ctrl_map(struct preempt_ctrl preempt_ctrl){ + int err,key = 0; + int srcmap_fd; + + srcmap_fd = bpf_obj_get(preempt_ctrl_path); + if (srcmap_fd < 0) { + fprintf(stderr,"Failed to open preempt_ctrl_map file\n"); + return srcmap_fd; + } + err = bpf_map_update_elem(srcmap_fd,&key,&preempt_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update preempt_ctrl_map elem\n"); + return err; + } + + return 0; +} + +int update_schedule_ctrl_map(struct schedule_ctrl schedule_ctrl){ + int err,key = 0; + int srcmap_fd; + + srcmap_fd = bpf_obj_get(schedule_ctrl_path); + if (srcmap_fd < 0) { + fprintf(stderr,"Failed to open schedule_ctrl_map file\n"); + return srcmap_fd; + } + err = bpf_map_update_elem(srcmap_fd,&key,&schedule_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update schedule_ctrl_map elem\n"); + return err; + } + + return 0; +} + +int update_mq_ctrl_map(struct mq_ctrl mq_ctrl){ + int err,key = 0; + int srcmap_fd; + + srcmap_fd = bpf_obj_get(mq_ctrl_path); + if (srcmap_fd < 0) { + fprintf(stderr,"Failed to open mq_ctrl_map file\n"); + return srcmap_fd; + } + err = bpf_map_update_elem(srcmap_fd,&key,&mq_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update mq_ctrl_map elem\n"); + return err; + } + + return 0; +} + +int update_mu_ctrl_map(struct mu_ctrl mu_ctrl){ + int err,key = 0; + int srcmap_fd; + + srcmap_fd = bpf_obj_get(mu_ctrl_path); + if (srcmap_fd < 0) { + fprintf(stderr,"Failed to open mq_ctrl_map file\n"); + return srcmap_fd; + } + err = bpf_map_update_elem(srcmap_fd,&key,&mu_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update mq_ctrl_map elem\n"); + return err; + } + + return 0; +} +/*----------------------------------------------*/ +/* mutex_count */ +/*----------------------------------------------*/ + +typedef struct { + uint64_t ptr; + uint64_t count; +} lock_count_t; + +lock_count_t lock_counts[HASH_SIZE]; + +static uint64_t hash(uint64_t ptr) { + return ptr % HASH_SIZE; +} + +static void increment_lock_count(uint64_t ptr) { + uint64_t h = hash(ptr); + while (lock_counts[h].ptr != 0 && lock_counts[h].ptr != ptr) { + h = (h + 1) % HASH_SIZE; + } + if (lock_counts[h].ptr == 0) { + lock_counts[h].ptr = ptr; + lock_counts[h].count = 1; + } else { + lock_counts[h].count++; + } +} + +static uint64_t get_lock_count(uint64_t ptr) { + uint64_t h = hash(ptr); + while (lock_counts[h].ptr != 0 && lock_counts[h].ptr != ptr) { + h = (h + 1) % HASH_SIZE; + } + if (lock_counts[h].ptr == 0) { + return 0; + } else { + return lock_counts[h].count; + } +} + +/*----------------------------------------------*/ +/* hash */ +/*----------------------------------------------*/ + + +struct output_entry { + int pid; + char comm[16]; + long long delay; +}; + + +struct output_entry seen_entries[MAX_ENTRIES]; +int seen_count = 0; + + +bool entry_exists(int pid, const char *comm, long long delay) { + for (int i = 0; i < seen_count; i++) { + if (seen_entries[i].pid == pid && + strcmp(seen_entries[i].comm, comm) == 0 && + seen_entries[i].delay == delay) { + return true; + } + } + return false; +} + + +void add_entry(int pid, const char *comm, long long delay) { + if (seen_count < MAX_ENTRIES) { + seen_entries[seen_count].pid = pid; + strncpy(seen_entries[seen_count].comm, comm, sizeof(seen_entries[seen_count].comm)); + seen_entries[seen_count].delay = delay; + seen_count++; + } +} +/*----------------------------------------------*/ +/* uprobe */ +/*----------------------------------------------*/ +static const char object[] = "/usr/lib/x86_64-linux-gnu/libc.so.6"; + +#define __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe) \ + do \ + { \ + LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts, \ + .retprobe = is_retprobe, \ + .func_name = #sym_name); \ + skel->links.prog_name = bpf_program__attach_uprobe_opts( \ + skel->progs.prog_name, \ + -1, \ + object, \ + 0, \ + &uprobe_opts); \ + } while (false) + +#define __CHECK_PROGRAM(skel, prog_name) \ + do \ + { \ + if (!skel->links.prog_name) \ + { \ + fprintf(stderr, "[%s] no program attached for" #prog_name "\n", strerror(errno)); \ + return -errno; \ + } \ + } while (false) + +#define __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, is_retprobe) \ + do \ + { \ + __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe); \ + __CHECK_PROGRAM(skel, prog_name); \ + } while (false) + +#define ATTACH_UPROBE(skel, sym_name, prog_name) __ATTACH_UPROBE(skel, sym_name, prog_name, false) +#define ATTACH_URETPROBE(skel, sym_name, prog_name) __ATTACH_UPROBE(skel, sym_name, prog_name, true) + +#define ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name) __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, false) +#define ATTACH_URETPROBE_CHECKED(skel, sym_name, prog_name) __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, true) + +#define CHECK_ERR(cond, info) \ + if (cond) \ + { \ + fprintf(stderr, "[%s]" info "\n", strerror(errno)); \ + return -1; \ + } + +#define warn(...) fprintf(stderr, __VA_ARGS__) + + +#endif // CPU_WATCHER_HELPER_H \ No newline at end of file diff --git a/MagicEyes/src/backend/process/cpu_watcher/src/controller.c b/MagicEyes/src/backend/process/cpu_watcher/src/controller.c new file mode 100644 index 000000000..2c319dfe0 --- /dev/null +++ b/MagicEyes/src/backend/process/cpu_watcher/src/controller.c @@ -0,0 +1,281 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com +// +// used to control the execution of proc_image tool +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cpu_watcher_helper.h" + +static struct env { + // 1代表activate;2代表unactivate;3代表finish + int usemode; + bool SAR; + bool percent; + bool CS_DELAY; + bool SYSCALL_DELAY; + bool MIN_US_SET; + int MIN_US; + bool PREEMPT; + bool SCHEDULE_DELAY; + bool MQ_DELAY; + int freq; + bool mutrace; + bool mutex_detail; + bool umutex; +} env = { + .usemode = 0, + .SAR = false, + .percent = false, + .CS_DELAY = false, + .SYSCALL_DELAY = false, + .MIN_US_SET = false, + .MIN_US = 10000, + .PREEMPT = false, + .SCHEDULE_DELAY = false, + .MQ_DELAY = false, + .freq = 99, + .mutrace = false, + .mutex_detail = false, + .umutex = false, +}; + +const char argp_program_doc[] ="Trace process to get cpu watcher.\n"; + +static const struct argp_option opts[] = { + { "activate", 'a', NULL, 0, "Set startup policy of proc_image tool" }, + { "unactivate", 'u', NULL, 0, "Initialize to the original unactivated state" }, + { "finish", 'f', NULL, 0, "Finish to run eBPF tool" }, + {"libbpf_sar", 's', 0, 0, "Print sar_info (the data of cpu)" }, + {"percent", 'P', 0, 0, "Format data as percentages" }, + {"cs_delay", 'c', 0, 0, "Print cs_delay (the data of cpu)" }, + {"syscall_delay", 'S', 0, 0, "Print syscall_delay (the data of syscall)" }, + {"preempt_time", 'p', 0, 0, "Print preempt_time (the data of preempt_schedule)" }, + {"schedule_delay", 'd', 0, 0, "Print schedule_delay (the data of cpu)" }, + {"schedule_delay_min_us_set", 'e', "THRESHOLD", 0, "Print scheduling delays that exceed the threshold (the data of cpu)" }, + {"mq_delay", 'm', 0, 0, "Print mq_delay(the data of proc)" }, + {"mutrace", 'x', 0, 0, "Print kernel mutex contend" }, + {"mutex_detail", 'i', 0, 0, "Print kernel mutex details" }, + {"umutex", 'b', 0, 0, "Print user mutex details" }, + { 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': + env.usemode = 1; + break; + case 'u': + env.usemode = 2; + break; + case 'f': + env.usemode = 3; + break; + case 's': + env.SAR = true; + break; + case 'P': + env.percent = true; + case 'c': + env.CS_DELAY = true; + break; + case 'S': + env.SYSCALL_DELAY = true; + break; + case 'p': + env.PREEMPT = true; + break; + case 'd': + env.SCHEDULE_DELAY = true; + break; + case 'e': + env.MIN_US_SET = true; + if (arg) { + env.MIN_US = strtol(arg, NULL, 10); + if (env.MIN_US <= 0) { + fprintf(stderr, "Invalid value for min_us: %d\n", env.MIN_US); + argp_usage(state); + } + } else { + env.MIN_US = 10000; + } + break; + case 'm': + env.MQ_DELAY = true; + break; + case 'x': + env.mutrace = true; + break; + case 'i': + env.mutex_detail = true; + break; + case 'b': + env.umutex = true; + break; + case 'h': + argp_state_help(state, stderr, ARGP_HELP_STD_HELP); + break; + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +int deactivate_mode(){ + int err; + + if(env.SAR){ + struct sar_ctrl sar_ctrl = {false,false,0}; + err = update_sar_ctrl_map(sar_ctrl); + if(err < 0) return err; + } + if(env.CS_DELAY){ + struct cs_ctrl cs_ctrl = {false,0}; + err = update_cs_ctrl_map(cs_ctrl); + if(err < 0) return err; + } + if(env.SYSCALL_DELAY){ + struct sc_ctrl sc_ctrl = {false,0}; + err = update_sc_ctrl_map(sc_ctrl); + if(err < 0) return err; + } + if(env.PREEMPT){ + struct preempt_ctrl preempt_ctrl = {false,0}; + err = update_preempt_ctrl_map(preempt_ctrl); + if(err < 0) return err; + } + if(env.SCHEDULE_DELAY){ + struct schedule_ctrl schedule_ctrl = {false,false,10000,0}; + err = update_schedule_ctrl_map(schedule_ctrl); + if(err < 0) return err; + } + if(env.MQ_DELAY){ + struct mq_ctrl mq_ctrl = {false,0}; + err = update_mq_ctrl_map(mq_ctrl); + if(err < 0) return err; + } + if(env.mutrace){ + struct mu_ctrl mu_ctrl = {false,false,0}; + err = update_mu_ctrl_map(mu_ctrl); + if(err < 0) return err; + } + return 0; +} + +static void sig_handler(int signo) +{ + deactivate_mode(); +} + +int main(int argc, char **argv) +{ + int err; + static const struct argp argp = { + .options = opts, + .parser = parse_arg, + .doc = argp_program_doc, + }; + + err = argp_parse(&argp, argc, argv, 0, NULL, NULL); + if (err) + return err; + + signal(SIGALRM,sig_handler); + signal(SIGINT,sig_handler); + signal(SIGTERM,sig_handler); + + if(env.usemode == 1){ // activate mode + if(env.SAR){ + struct sar_ctrl sar_ctrl = {true,env.percent,SAR_WACTHER+env.percent}; + err = update_sar_ctrl_map(sar_ctrl); + if(err < 0) return err; + } + + if(env.CS_DELAY){ + struct cs_ctrl cs_ctrl = {true,CS_WACTHER}; + err = update_cs_ctrl_map(cs_ctrl); + if(err < 0) return err; + } + + if(env.SYSCALL_DELAY){ + struct sc_ctrl sc_ctrl = {true,SC_WACTHER}; + err = update_sc_ctrl_map(sc_ctrl); + if(err < 0) return err; + } + + if(env.PREEMPT){ + struct preempt_ctrl preempt_ctrl = {true,PREEMPT_WACTHER}; + err = update_preempt_ctrl_map(preempt_ctrl); + if(err < 0) return err; + } + + if(env.SCHEDULE_DELAY){ + /* + *1.未设置env.MIN_US_SET时, prev_watcher = SCHEDULE_WACTHER + 0;输出方式为schedule输出 + *2.已设置env.MIN_US_SET时, prev_watcher = SCHEDULE_WACTHER + 1;输出方式为-e输出 + */ + struct schedule_ctrl schedule_ctrl = {true,env.MIN_US_SET,env.MIN_US,SCHEDULE_WACTHER+env.MIN_US_SET}; + err = update_schedule_ctrl_map(schedule_ctrl); + if(err < 0) return err; + } + + if(env.MQ_DELAY){ + struct mq_ctrl mq_ctrl = {true,MQ_WACTHER}; + err = update_mq_ctrl_map(mq_ctrl); + if(err < 0) return err; + } + + if(env.mutrace){ + if (env.umutex){ + struct mu_ctrl mu_ctrl = {true,env.mutex_detail,env.umutex,MUTEX_WATCHER+2}; + err = update_mu_ctrl_map(mu_ctrl); + if(err < 0) return err; + } + else{ + struct mu_ctrl mu_ctrl = {true,env.mutex_detail,env.umutex,MUTEX_WATCHER+env.mutex_detail}; + err = update_mu_ctrl_map(mu_ctrl); + if(err < 0) return err; + } + } + }else if(env.usemode == 2){ // deactivate mode + err = deactivate_mode(); + if(err<0){ + fprintf(stderr, "Failed to deactivate\n"); + return err; + } + }else if(env.usemode == 3){ // finish mode + const char *command = "pkill cpu_watcher"; + int status = system(command); + if (status == -1) { + perror("system"); + } + }else{ + // 输出help信息 + printf("Please enter the usage mode(activate/deactivate/finish) before selecting the function\n"); + argp_help(&argp, stderr, ARGP_HELP_LONG, argv[0]); + } + + return 0; +} diff --git a/MagicEyes/src/backend/process/cpu_watcher/src/cpu_watcher.c b/MagicEyes/src/backend/process/cpu_watcher/src/cpu_watcher.c index ee21da12f..bcc8da5cb 100644 --- a/MagicEyes/src/backend/process/cpu_watcher/src/cpu_watcher.c +++ b/MagicEyes/src/backend/process/cpu_watcher/src/cpu_watcher.c @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -// author: zhangziheng0525@163.com +// author: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com #include @@ -21,87 +21,180 @@ #include #include #include +#include #include #include +#include +#include +#include +#include #include #include -#include "cpu_watcher/include/cpu_watcher.h" +#include "../include/cpu_watcher_helper.h" #include "process/cpu_watcher/sar.skel.h" #include "process/cpu_watcher/cs_delay.skel.h" #include "process/cpu_watcher/sc_delay.skel.h" +#include "process/cpu_watcher/preempt.skel.h" +#include "process/cpu_watcher/schedule_delay.skel.h" +#include "process/cpu_watcher/mq_delay.skel.h" +#include "process/cpu_watcher/mutrace.skel.h" typedef long long unsigned int u64; typedef unsigned int u32; + + + +struct list_head { + struct list_head *next; + struct list_head *prev; +}; +struct msg_msg { + struct list_head m_list; + long int m_type; + size_t m_ts; + struct msg_msgseg *next; + void *security; +}; + static struct env { - int time; - bool enable_proc; - bool SAR; - bool CS_DELAY; - bool SYSCALL_DELAY; - int freq; + int time; + int period; + bool enable_proc; + bool SAR; + bool CS_DELAY; + bool SYSCALL_DELAY; + bool PREEMPT; + bool SCHEDULE_DELAY; + bool MQ_DELAY; + int freq; + bool EWMA; + int cycle; + int MUTRACE; } env = { - .time = 0, - .enable_proc = false, - .SAR = false, - .CS_DELAY = false, - .SYSCALL_DELAY = false, - .freq = 99, + .time = 0, + .period = 1, + .enable_proc = false, + .SAR = false, + .CS_DELAY = false, + .SYSCALL_DELAY = false, + .PREEMPT = false, + .SCHEDULE_DELAY = false, + .MQ_DELAY = false, + .freq = 99, + .EWMA = false, + .cycle = 0, + .MUTRACE = false, }; + + struct cs_delay_bpf *cs_skel; struct sar_bpf *sar_skel; struct sc_delay_bpf *sc_skel; +struct preempt_bpf *preempt_skel; +struct schedule_delay_bpf *sd_skel; +struct mq_delay_bpf *mq_skel; +struct mutrace_bpf *mu_skel; + +static int csmap_fd; +static int sarmap_fd; +struct sar_ctrl sar_ctrl= {}; +static int scmap_fd; +static int preemptmap_fd; +static int schedulemap_fd; +struct schedule_ctrl sd_ctrl = {}; +static int mqmap_fd; +static int mumap_fd; +struct mu_ctrl mu_ctrl = {}; -u64 softirq = 0;//初始化softirq; -u64 irqtime = 0;//初始化irq; -u64 idle = 0;//初始化idle;s +//static int prev_watcher = 0;//上一个使用的工具,用于在切换使用功能时,打印不用功能的表头; + +u64 softirq = 0; +u64 irqtime = 0; +u64 idle = 0; u64 sched = 0; u64 proc = 0; unsigned long ktTime = 0; unsigned long utTime = 0; -u64 tick_user = 0;//初始化sys; +u64 tick_user = 0; + int sc_sum_time = 0 ; int sc_max_time = 0 ; int sc_min_time = SYSCALL_MIN_TIME ; int sys_call_count = 0; +bool ifprint = 0; +int preempt_count = 0 ; +int sum_preemptTime = 0 ; +int preempt_start_print = 0 ; + /*设置传参*/ -const char argp_program_doc[] ="cpu wacher is in use ....\n"; +const char argp_program_doc[] = "cpu watcher is in use ....\n"; static const struct argp_option opts[] = { - { "time", 't', "TIME-SEC", 0, "Max Running Time(0 for infinite)" }, - {"libbpf_sar", 's', 0,0,"print sar_info (the data of cpu)"}, - {"cs_delay", 'c', 0,0,"print cs_delay (the data of cpu)"}, - {"syscall_delay", 'y', 0,0,"print syscall_delay (the data of syscall)"}, - { NULL, 'h', NULL, OPTION_HIDDEN, "show the full help" }, - {0}, + { "time", 't', "TIME-SEC", 0, "Max Running Time(0 for infinite)" }, + { "period", 'i', "INTERVAL", 0, "Period interval in seconds" }, + {"libbpf_sar", 's', 0, 0, "Print sar_info (the data of cpu)" }, + {"cs_delay", 'c', 0, 0, "Print cs_delay (the data of cpu)" }, + {"syscall_delay", 'S', 0, 0, "Print syscall_delay (the data of syscall)" }, + {"preempt_time", 'p', 0, 0, "Print preempt_time (the data of preempt_schedule)" }, + {"schedule_delay", 'd', 0, 0, "Print schedule_delay (the data of cpu)" }, + {"mq_delay", 'm', 0, 0, "Print mq_delay(the data of proc)" }, + {"mutrace", 'x', 0, 0, "Print mutrace data(the data of cpu)" }, + {"ewma", 'E',0,0,"dynamic filte the data"}, + {"cycle", 'T',"CYCLE",0,"Periods of the ewma"}, + { NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" }, + { 0 }, }; + static error_t parse_arg(int key, char *arg, struct argp_state *state) { - switch (key) { - case 't': - env.time = strtol(arg, NULL, 10); - if(env.time) alarm(env.time); - break; - case 's': - env.SAR = true; + switch (key) { + case 't': + env.time = strtol(arg, NULL, 10); + if (env.time) alarm(env.time); + break; + case 'i': + env.period = strtol(arg, NULL, 10); + break; + case 's': + env.SAR = true; + break; + case 'c': + env.CS_DELAY = true; + break; + case 'S': + env.SYSCALL_DELAY = true; + break; + case 'p': + env.PREEMPT = true; + break; + case 'd': + env.SCHEDULE_DELAY = true; + break; + case 'm': + env.MQ_DELAY = true; + break; + case 'x': + env.MUTRACE = true; break; - case 'c': - env.CS_DELAY = true; - break; - case 'y': - env.SYSCALL_DELAY = true; - break; - case 'h': - argp_state_help(state, stderr, ARGP_HELP_STD_HELP); + case 'E': + env.EWMA = true; break; - default: - return ARGP_ERR_UNKNOWN; - } - return 0; + case 'T': + env.cycle = strtol(arg, NULL, 10); + break; + case 'h': + argp_state_help(state, stderr, ARGP_HELP_STD_HELP); + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; } + static const struct argp argp = { .options = opts, .parser = parse_arg, @@ -135,7 +228,6 @@ static int open_and_attach_perf_event(int freq, struct bpf_program *prog, .config = PERF_COUNT_SW_CPU_CLOCK, }; int i, fd; - for (i = 0; i < nr_cpus; i++) { fd = syscall(__NR_perf_event_open, &attr, -1, i, -1, 0); if (fd < 0) { @@ -155,7 +247,6 @@ static int open_and_attach_perf_event(int freq, struct bpf_program *prog, return -1; } } - return 0; } @@ -166,23 +257,42 @@ u64 find_ksym(const char* target_symbol) { perror("Failed to open /proc/kallsyms"); return 1; } - char symbol_name[99]; u64 symbol_address = 0; - while (fscanf(file, "%llx %*c %s\n", &symbol_address, symbol_name) != EOF) { if (strcmp(symbol_name, target_symbol) == 0) { break; } } - fclose(file); - return symbol_address; } static int print_all() { + int err,key=0; + err = bpf_map_lookup_elem(sarmap_fd, &key, &sar_ctrl); + if (err < 0) { + fprintf(stderr, "failed to lookup infos: %d\n", err); + return -1; + } + if(!sar_ctrl.sar_func) return 0; + if(sar_ctrl.prev_watcher == SAR_WACTHER + 1) { + printf(" time proc/s cswch/s runqlen irqTime/%% softirq/%% idle/%% kthread/%% sysc/%% utime/%% sys/%% \n"); + sar_ctrl.prev_watcher = SAR_WACTHER + 2; + err = bpf_map_update_elem(sarmap_fd, &key, &sar_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + } + }else if (sar_ctrl.prev_watcher == SAR_WACTHER){ + printf(" time proc/s cswch/s runqlen irqTime/us softirq/us idle/ms kthread/us sysc/ms utime/ms sys/ms \n"); + sar_ctrl.prev_watcher = SAR_WACTHER + 2; + err = bpf_map_update_elem(sarmap_fd, &key, &sar_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + } + } + int nprocs = get_nprocs(); /*proc:*/ int key_proc = 1; int err_proc, fd_proc = bpf_map__fd(sar_skel->maps.countMap); @@ -195,6 +305,7 @@ static int print_all() u64 __proc; __proc = total_forks - proc; proc = total_forks; + /*cswch:*/ int key_cswch = 0; int err_cswch, fd_cswch = bpf_map__fd(sar_skel->maps.countMap); @@ -208,15 +319,15 @@ static int print_all() __sched = sched_total - sched; sched = sched_total; - // /*runqlen:*/ - // int key_runqlen = 0;// 设置要查找的键值为0 - // int err_runqlen, fd_runqlen = bpf_map__fd(sar_skel->maps.runqlen);// 获取映射文件描述符 - // int runqlen;// 用于存储从映射中查找到的值 - // err_runqlen = bpf_map_lookup_elem(fd_runqlen, &key_runqlen, &runqlen); // 从映射中查找键为1的值 - // if (err_runqlen < 0) {//没找到 - // fprintf(stderr, "failed to lookup infos of runqlen: %d\n", err_runqlen); - // return -1; - // } + /*runqlen:*/ + int key_runqlen = 0; + int err_runqlen, fd_runqlen = bpf_map__fd(sar_skel->maps.runqlen); + int runqlen; + err_runqlen = bpf_map_lookup_elem(fd_runqlen, &key_runqlen, &runqlen); + if (err_runqlen < 0) { + fprintf(stderr, "failed to lookup infos of runqlen: %d\n", err_runqlen); + return -1; + } /*irqtime:*/ int key_irqtime = 0; @@ -279,7 +390,7 @@ static int print_all() unsigned long dtaUT = utTime -_utTime; /*sys*/ - int key_sys = 0,next_key; + int key_sys = 0; int err_sys, fd_sys = bpf_map__fd(sar_skel->maps.tick_user); u64 __tick_user =0 ;// 用于存储从映射中查找到的值 __tick_user = tick_user; @@ -297,15 +408,34 @@ static int print_all() if(env.enable_proc){ time_t now = time(NULL); struct tm *localTime = localtime(&now); - printf("%02d:%02d:%02d %8llu %8llu %8llu %10llu %8llu %8llu %8llu %8lu %8lu\n", + if (sar_ctrl.percent == true){ + printf("%02d:%02d:%02d %8llu %8llu %6d ",localTime->tm_hour, localTime->tm_min, localTime->tm_sec,__proc, __sched, runqlen); + // 大于百分之60的标红输出 + double values[7] = { + (double)dtairqtime / 10000000 / nprocs / env.period, + (double)dtasoftirq / 10000000 / nprocs / env.period, + (double)dtaidle / 10000000 / nprocs / env.period, + (double)dtaKT / 10000000 / nprocs / env.period, + (double)dtaSysc / 10000000 / nprocs / env.period, + (double)dtaUTRaw / 10000000 / nprocs / env.period, + (double)dtaSys / 10000000 / nprocs / env.period + }; + for (int i = 0; i < 7; i++) { + if (values[i] > 60.0) { + printf("\033[1;31m"); // 设置为红色 + } + printf("%10.2f ", values[i]); + printf("\033[0m"); // 重置为默认颜色 + } + printf("\n"); + }else{printf("%02d:%02d:%02d %8llu %8llu %6d %8llu %10llu %8llu %10lu %8llu %8llu %8llu\n", localTime->tm_hour, localTime->tm_min, localTime->tm_sec, - __proc,__sched,dtairqtime/1000,dtasoftirq/1000,dtaidle/1000000, - dtaKT/1000,dtaSysc / 1000000,dtaUTRaw/1000000,dtaSys / 1000000); + __proc,__sched,runqlen,dtairqtime/1000,dtasoftirq/1000,dtaidle/1000000, + dtaKT/1000,dtaSysc / 1000000,dtaUTRaw/1000000,dtaSys / 1000000);} } else{ env.enable_proc = true; } - return 0; } @@ -313,49 +443,48 @@ int count[25]={0};//定义一个count数组,用于汇总schedul()调度时间 static int handle_event(void *ctx, void *data,unsigned long data_sz) { const struct event *e = data; - printf("t1:%lu t2:%lu delay:%lu\n",e->t1,e->t2,e->delay); - + printf("t1:%llu t2:%llu delay:%llu\n",e->t1,e->t2,e->delay); int dly=(int)(e->delay),i=0; while (dly > 1){ dly /= 2; i ++; } - count[i]++;//记录时间间隔次数; + count[i]++; return 0; } static int print_hstgram(int i,int max,int per_len) { int cnt=count[i]; if(per_len==1){ - while(cnt>0){//打印 + while(cnt>0){ printf("*"); cnt--; } } - while(cnt-per_len>=0){//打印 + while(cnt-per_len>=0){ printf("*"); cnt-=per_len; } printf("\n"); return per_len; } -double pow(int n,int k)//实现pow函数 +double my_pow(int n,int k)//实现pow函数 { if (k > 0) - return n * pow(n, k - 1); + return n * my_pow(n, k - 1); else if (k == 0) return 1; else - return 1.0 / pow(n, -k); + return 1.0 / my_pow(n, -k); } static void histogram() { int log10[15]={0},max=0,per_len=1; - for(int i=0;i<10;i++){//log10(count[i]); + for(int i=0;i<10;i++){ int tmp=count[i],cnt=0; while (tmp >= 10){ tmp /= 10; - cnt ++;//幂次 + cnt ++; } log10[cnt]++; } @@ -365,13 +494,13 @@ static void histogram() max=i; } - while(max>0){//pow(10,max); + while(max>0){ per_len *=10 ; max--; } - time_t now = time(NULL);// 获取当前时间 - struct tm *localTime = localtime(&now);// 将时间转换为本地时间结构 + time_t now = time(NULL); + struct tm *localTime = localtime(&now); printf("\nTime : %02d:%02d:%02d \n",localTime->tm_hour, localTime->tm_min, localTime->tm_sec); printf("%-24s \t%-12s \t%-12s \n","cs_delay","Count","Distribution"); printf("%d\t=>\t%-8d \t%-12d \t|",0,1,count[0]); @@ -379,39 +508,273 @@ static void histogram() printf("%d\t=>\t%-8d \t%-12d \t|",2,3,count[1]); print_hstgram(1,max,per_len); for(int i=2;i<20;i++){ - printf("%d\t=>\t%-8d \t%-12d \t|",(int)pow(2,i),(int)pow(2,(i+1))-1,count[i]); + printf("%d\t=>\t%-8d \t%-12d \t|",(int)my_pow(2,i),(int)my_pow(2,(i+1))-1,count[i]); print_hstgram(i,max,per_len); } printf("per_len = %d\n",per_len); } -static void max_print(){ +struct ewma_info ewma_syscall_delay = {}; +static int syscall_delay_print(void *ctx, void *data,unsigned long data_sz) +{ + int err,key = 0; + struct sc_ctrl sc_ctrl ={}; + + err = bpf_map_lookup_elem(scmap_fd,&key,&sc_ctrl); + if (err < 0) { + fprintf(stderr, "failed to lookup infos: %d\n", err); + return -1; + } + if(!sc_ctrl.sc_func) return 0; + + const struct syscall_events *e = data; + if(e->delay<0||e->delay>1000000) return 0; + time_t now = time(NULL);// 获取当前时间 + struct tm *localTime = localtime(&now);// 将时间转换为本地时间结构 + + if(env.EWMA==0){ + printf("%02d:%02d:%02d %-8u %-15lld %-15lld\n", + localTime->tm_hour, localTime->tm_min, localTime->tm_sec, + e->pid,e->syscall_id,e->delay); + } + else{ + ewma_syscall_delay.cycle = env.cycle; + if(dynamic_filter(&ewma_syscall_delay,e->delay)){ + printf("%02d:%02d:%02d %-8u %-15lld %-15lld\n", + localTime->tm_hour, localTime->tm_min, localTime->tm_sec, + e->pid,e->syscall_id,e->delay); + } + } - int sc_average_time = sc_sum_time/sys_call_count; - printf("Average_Syscall_Time: %8d ms\n",sc_average_time); - printf("MAX_Syscall_Time: %8d ms\n",sc_max_time); - printf("MIN_Syscall_Time: %8d ms\n",sc_min_time); + return 0; } -static int syscall_delay_print(void *ctx, void *data,unsigned long data_sz) + + +//抢占时间输出 +static int preempt_print(void *ctx, void *data, unsigned long data_sz) +{ + const struct preempt_event *e = data; + printf("%-16s %-7d %-7d %-11llu\n", e->comm, e->prev_pid, e->next_pid, e->duration); + preempt_count++; + sum_preemptTime += e->duration; + return 0; +} + +static int attach(struct mutrace_bpf *mu_skel) { + int err; + ATTACH_UPROBE_CHECKED(mu_skel,pthread_mutex_lock,pthread_mutex_lock); + ATTACH_UPROBE_CHECKED(mu_skel,__pthread_mutex_trylock,__pthread_mutex_trylock); + ATTACH_URETPROBE_CHECKED(mu_skel,__pthread_mutex_trylock,ret_pthread_mutex_trylock); + ATTACH_UPROBE_CHECKED(mu_skel,pthread_mutex_unlock,pthread_mutex_unlock); + err = mutrace_bpf__attach(mu_skel); + CHECK_ERR(err, "Failed to attach BPF skeleton"); + return 0; - const struct event2 *e = data; - printf("|COMM: %-15s |pid: %-8lu |start_time: %-10lu |exit_time: %-10lu |delay: %-8lu|\n",e->comm,e->pid,e->start_time,e->exit_time,e->delay); - sc_sum_time += e->delay; - if(sc_max_time < e->delay){ - sc_max_time = e->delay; +} + + +//mutrace输出 +static int mutrace_print(void *ctx, void *data, unsigned long data_sz) { + int err,key = 0; + err = bpf_map_lookup_elem(mumap_fd,&key,&mu_ctrl); + if (err < 0) { + fprintf(stderr, "failed to lookup infos: %d\n", err); + return -1; } - else if(sc_min_time > e->delay){ - sc_min_time = e->delay; + if(!mu_ctrl.mu_func) return 0; + if(mu_ctrl.prev_watcher == MUTEX_WATCHER ){ + printf("%s\n"," lock_ptr owner_pid owner_comm owner_prio contender_pid contender_comm contender_prio contender_count"); + mu_ctrl.prev_watcher = MUTEX_WATCHER + 9;//打印表头功能关 + err = bpf_map_update_elem(mumap_fd, &key, &mu_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + } + }else if (mu_ctrl.prev_watcher == MUTEX_WATCHER +1) { + printf("%s\n"," lock_ptr locked_total locked_max contended_total count last_owner last_owmer_name"); + mu_ctrl.prev_watcher = MUTEX_WATCHER + 9;//打印表头功能关 + err = bpf_map_update_elem(mumap_fd, &key, &mu_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + } + }else if (mu_ctrl.prev_watcher == MUTEX_WATCHER +2) { + printf("%s\n"," lock_ptr locked_total locked_max contended_total count last_owner last_owmer_name"); + mu_ctrl.prev_watcher = MUTEX_WATCHER + 9;//打印表头功能关 + err = bpf_map_update_elem(mumap_fd, &key, &mu_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + } + } + if(!mu_ctrl.mutex_detail&& (!mu_ctrl.umutex)){ + const struct mutex_contention_event *e = data; + if (e->owner_pid == 0 || e->contender_pid == 0||e->owner_pid == 1) { + return 0; + } + // 增加锁争用次数 + increment_lock_count(e->ptr); + uint64_t contention_count = get_lock_count(e->ptr); + printf("%15llu %15d %15s %15d %15d %15s %15d %15ld\n", e->ptr, e->owner_pid, e->owner_name, e->owner_prio,e->contender_pid, e->contender_name, e->contender_prio,contention_count); + } + return 0; +} + +static int kmutex_detail() { + int fd = bpf_map__fd(mu_skel->maps.kmutex_info_map); + u64 key, next_key; + struct mutex_info info; + while (bpf_map_get_next_key(fd, &key, &next_key) == 0) { + int err = bpf_map_lookup_elem(fd, &next_key, &info); + if (err == 0 && info.contended_total != 0) { // 添加过滤条件 + printf(" %15llu %15lluns %15lluns %15lluns %15d %15d %20s\n", + next_key, info.locked_total, info.locked_max, info.contended_total, info.count, info.last_owner, info.last_name); + } + key = next_key; + } + return 0; +} + +static int umutex_detail() { + int fd = bpf_map__fd(mu_skel->maps.umutex_info_map); + u64 key, next_key; + struct mutex_info info; + while (bpf_map_get_next_key(fd, &key, &next_key) == 0) { + int err = bpf_map_lookup_elem(fd, &next_key, &info); + if (err == 0 && info.contended_total != 0) { // 添加过滤条件 + printf(" %15llu %15llums %15llums %15llums %15d %15d %20s\n", + next_key, info.locked_total/1000000, info.locked_max/1000000, info.contended_total/1000000, info.count, info.last_owner, info.last_name); + } + key = next_key; + } + return 0; +} + +static int schedule_print() +{ + int err,key = 0; + err = bpf_map_lookup_elem(schedulemap_fd,&key,&sd_ctrl); + if (err < 0) { + fprintf(stderr, "failed to lookup infos: %d\n", err); + return -1; + } + if(!sd_ctrl.schedule_func) return 0; + + if(sd_ctrl.prev_watcher == SCHEDULE_WACTHER ){ + printf("%-8s %s\n", " TIME ", "avg_delay/μs max_delay/μs max_proc_name min_delay/μs min_proc_name"); + sd_ctrl.prev_watcher = SCHEDULE_WACTHER + 9;//打印表头功能关 + err = bpf_map_update_elem(schedulemap_fd, &key, &sd_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + } } - sys_call_count ++; + else if(sd_ctrl.prev_watcher == SCHEDULE_WACTHER +1){ + // printf("sd_ctrl.prev_watcher = %d\n",sd_ctrl.prev_watcher); + printf("调度延时大于%dms的进程:\n",sd_ctrl.min_us/1000); + printf("%s\n","pid COMM schedule_delay/us"); + sd_ctrl.prev_watcher = SCHEDULE_WACTHER + 9;//打印表头功能关. + err = bpf_map_update_elem(schedulemap_fd, &key, &sd_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + } + } + + if(!sd_ctrl.min_us_set){ + struct sum_schedule info; + int err, fd = bpf_map__fd(sd_skel->maps.sys_schedule); + time_t now = time(NULL); + struct tm *localTime = localtime(&now); + int hour = localTime->tm_hour; + int min = localTime->tm_min; + int sec = localTime->tm_sec; + unsigned long long avg_delay; + err = bpf_map_lookup_elem(fd, &key, &info); + if (err < 0) { + fprintf(stderr, "failed to lookup infos: %d\n", err); + return -1; + } + avg_delay = info.sum_delay / info.sum_count; + if (!ifprint) { + ifprint=1; + }else{ + printf("%02d:%02d:%02d %-15lf %-15lf %10s %15lf %15s\n", + hour, min, sec, avg_delay / 1000.0, info.max_delay / 1000.0,info.proc_name_max,info.min_delay / 1000.0,info.proc_name_min); + } + } + else{ + struct proc_schedule info; + struct proc_id id_key; + struct proc_history prev_info; + int key = 0; + int err, fd1 = bpf_map__fd(sd_skel->maps.threshold_schedule),fd2 = bpf_map__fd(sd_skel->maps.proc_histories); + err = bpf_map_lookup_elem(fd1, &key, &info); + if (err < 0) { + fprintf(stderr, "failed to lookup infos: %d\n", err); + return -1; + } + if (info.delay / 1000 > sd_ctrl.min_us&&info.id.pid!=0) { + id_key.pid = info.id.pid; + id_key.cpu_id = info.id.cpu_id; + err = bpf_map_lookup_elem(fd2, &id_key, &prev_info); + if (err < 0) { + fprintf(stderr, "Failed to lookup proc_histories with PID %d and CPU ID %d: %d\n", id_key.pid, id_key.cpu_id, err); + return -1; + } + if (!entry_exists(info.id.pid, info.proc_name, info.delay / 1000)) { + printf("%-10d %-16s %15lld", info.id.pid, info.proc_name, info.delay / 1000); + add_entry(info.id.pid, info.proc_name, info.delay / 1000); + for (int i = 0; i < 2; i++) { + if (prev_info.last[i].pid != 0) { + printf(" Previous Process %d: PID=%-10d Name=%-16s ", i+1, prev_info.last[i].pid, prev_info.last[i].comm); + } + } + printf("\n"); + } + + } + } + + return 0; +} + + +static int mq_event(void *ctx, void *data,unsigned long data_sz) +{ + time_t now = time(NULL);// 获取当前时间 + struct tm *localTime = localtime(&now);// 将时间转换为本地时间结构 + const struct mq_events *e = data; + float send_delay,rcv_delay,delay; + if(!e->send_enter_time || !e->send_exit_time || !e->rcv_enter_time || !e->rcv_exit_time) { + printf("erro!\n"); + return 0; + } + send_delay = (e->send_exit_time - e->send_enter_time)/1000000.0; + rcv_delay = (e->rcv_exit_time - e->rcv_enter_time)/1000000.0; + if(e->send_enter_time < e->rcv_enter_time){ + delay = (e->rcv_exit_time - e->send_enter_time)/1000000.0; + }else{ + delay = (e->rcv_exit_time - e->send_enter_time)/1000000.0 + send_delay + rcv_delay; + } + printf("%02d:%02d:%02d %-8u %-8u %-8u \t%-16llu %-16llu %-16llu %-16llu\t%-15.5f %-15.5f %-15.5f\n", + localTime->tm_hour, localTime->tm_min, localTime->tm_sec, + e->mqdes,e->send_pid,e->rcv_pid, + e->send_enter_time,e->send_exit_time,e->rcv_enter_time,e->rcv_exit_time, + send_delay,rcv_delay,delay); + return 0; } + int main(int argc, char **argv) { struct ring_buffer *rb = NULL; + struct bpf_map *cs_ctrl_map = NULL; + struct bpf_map *sar_ctrl_map = NULL; + struct bpf_map *sc_ctrl_map = NULL; + struct bpf_map *preempt_ctrl_map = NULL; + struct bpf_map *schedule_ctrl_map = NULL; + struct bpf_map *mq_ctrl_map = NULL; + struct bpf_map *mu_ctrl_map = NULL; + int key = 0; int err; err = argp_parse(&argp, argc, argv, 0, NULL, NULL); if (err) @@ -453,6 +816,19 @@ int main(int argc, char **argv) fprintf(stderr, "Failed to load and verify BPF skeleton\n"); goto cs_delay_cleanup; } + + err = common_pin_map(&cs_ctrl_map,cs_skel->obj,"cs_ctrl_map",cs_ctrl_path); + if(err < 0){ + goto cs_delay_cleanup; + } + csmap_fd = bpf_map__fd(cs_ctrl_map); + struct cs_ctrl init_value = {false,CS_WACTHER}; + err = bpf_map_update_elem(csmap_fd, &key, &init_value, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + goto cs_delay_cleanup; + } + /* Attach tracepoints */ err = cs_delay_bpf__attach(cs_skel); if (err) @@ -466,6 +842,42 @@ int main(int argc, char **argv) fprintf(stderr, "Failed to create ring buffer\n"); goto cs_delay_cleanup; } + }else if (env.PREEMPT) { + preempt_skel = preempt_bpf__open(); + if (!preempt_skel) { + fprintf(stderr, "Failed to open and load BPF skeleton\n"); + return 1; + } + + err = preempt_bpf__load(preempt_skel); + if (err) { + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); + goto preempt_cleanup; + } + + err = common_pin_map(&preempt_ctrl_map,preempt_skel->obj,"preempt_ctrl_map",preempt_ctrl_path); + if(err < 0){ + goto preempt_cleanup; + } + preemptmap_fd = bpf_map__fd(preempt_ctrl_map); + struct preempt_ctrl init_value = {false,PREEMPT_WACTHER}; + err = bpf_map_update_elem(preemptmap_fd, &key, &init_value, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + goto preempt_cleanup; + } + err = preempt_bpf__attach(preempt_skel); + if (err) { + fprintf(stderr, "Failed to attach BPF skeleton\n"); + goto preempt_cleanup; + } + + rb = ring_buffer__new(bpf_map__fd(preempt_skel->maps.rb), preempt_print, NULL, NULL); + if (!rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer\n"); + goto preempt_cleanup; + } }else if (env.SYSCALL_DELAY){ /* Load and verify BPF application */ sc_skel = sc_delay_bpf__open(); @@ -481,6 +893,17 @@ int main(int argc, char **argv) fprintf(stderr, "Failed to load and verify BPF skeleton\n"); goto sc_delay_cleanup; } + err = common_pin_map(&sc_ctrl_map,sc_skel->obj,"sc_ctrl_map",sc_ctrl_path); + if(err < 0){ + goto sc_delay_cleanup; + } + scmap_fd = bpf_map__fd(sc_ctrl_map); + struct sc_ctrl init_value = {false,SC_WACTHER}; + err = bpf_map_update_elem(scmap_fd, &key, &init_value, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + goto sc_delay_cleanup; + } /* Attach tracepoints */ err = sc_delay_bpf__attach(sc_skel); if (err) @@ -488,12 +911,45 @@ int main(int argc, char **argv) fprintf(stderr, "Failed to attach BPF skeleton\n"); goto sc_delay_cleanup; } + printf("%-8s %-8s %-15s %-15s\n","Time","Pid","syscall_id","delay/ms"); rb = ring_buffer__new(bpf_map__fd(sc_skel->maps.rb), syscall_delay_print, NULL, NULL); //ring_buffer__new() API,允许在不使用额外选项数据结构下指定回调 if (!rb) { err = -1; fprintf(stderr, "Failed to create ring buffer\n"); goto sc_delay_cleanup; } + + + }else if(env.SCHEDULE_DELAY){ + + + sd_skel = schedule_delay_bpf__open(); + if (!sd_skel) { + fprintf(stderr, "Failed to open and load BPF skeleton\n"); + return 1; + } + err = schedule_delay_bpf__load(sd_skel); + if (err) { + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); + goto schedule_cleanup; + } + err = common_pin_map(&schedule_ctrl_map,sd_skel->obj,"schedule_ctrl_map",schedule_ctrl_path); + if(err < 0){ + goto schedule_cleanup; + } + schedulemap_fd = bpf_map__fd(schedule_ctrl_map); + struct schedule_ctrl init_value = {false,false,10000,SCHEDULE_WACTHER}; + + err = bpf_map_update_elem(schedulemap_fd, &key, &init_value, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + goto schedule_cleanup; + } + err = schedule_delay_bpf__attach(sd_skel); + if (err) { + fprintf(stderr, "Failed to attach BPF skeleton\n"); + goto schedule_cleanup; + } }else if (env.SAR){ /* Load and verify BPF application */ sar_skel = sar_bpf__open(); @@ -516,18 +972,115 @@ int main(int argc, char **argv) if (err) goto sar_cleanup; + err = common_pin_map(&sar_ctrl_map,sar_skel->obj,"sar_ctrl_map",sar_ctrl_path); + if(err < 0){ + goto sar_cleanup; + } + sarmap_fd = bpf_map__fd(sar_ctrl_map); + struct sar_ctrl init_value = {false,false,SAR_WACTHER}; + err = bpf_map_update_elem(sarmap_fd, &key, &init_value, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + goto sar_cleanup; + } + err = sar_bpf__attach(sar_skel); if (err) { fprintf(stderr, "Failed to attach BPF skeleton\n"); goto sar_cleanup; } - //printf(" time proc/s cswch/s runqlen irqTime/us softirq/us idle/ms kthread/us sysc/ms utime/ms sys/ms BpfCnt\n"); - printf(" time proc/s cswch/s irqTime/us softirq/us idle/ms kthread/us sysc/ms utime/ms sys/ms\n"); + }else if(env.MQ_DELAY){ + /* Load and verify BPF application */ + mq_skel = mq_delay_bpf__open(); + if (!mq_skel) + { + fprintf(stderr, "Failed to open and load BPF skeleton\n"); + return 1; + } + /* Load & verify BPF programs */ + err = mq_delay_bpf__load(mq_skel); + if (err) + { + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); + goto mq_delay_cleanup; + } + + err = common_pin_map(&mq_ctrl_map,mq_skel->obj,"mq_ctrl_map",mq_ctrl_path); + if(err < 0){ + goto mq_delay_cleanup; + } + mqmap_fd = bpf_map__fd(mq_ctrl_map); + struct mq_ctrl init_value = {false,MQ_WACTHER}; + err = bpf_map_update_elem(mqmap_fd, &key, &init_value, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + goto mq_delay_cleanup; + } + + /* Attach tracepoints */ + err = mq_delay_bpf__attach(mq_skel); + if (err) + { + fprintf(stderr, "Failed to attach BPF skeleton\n"); + goto mq_delay_cleanup; + } + printf("%-8s %-8s %-8s %-8s \t%-16s %-16s %-16s %-16s\t%-15s %-15s %-15s\n","Time","Mqdes","SND_PID","RCV_PID","SND_Enter","SND_EXit","RCV_Enter","RCV_EXit","SND_Delay/ms","RCV_Delay/ms","Delay/ms"); + rb = ring_buffer__new(bpf_map__fd(mq_skel->maps.rb), mq_event, NULL, NULL); //ring_buffer__new() API,允许在不使用额外选项数据结构下指定回调 + if (!rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer\n"); + goto mq_delay_cleanup; + } + }else if (env.MUTRACE) { + mu_skel = mutrace_bpf__open(); + if (!mu_skel) { + fprintf(stderr, "Failed to open and load BPF skeleton\n"); + return 1; + } + + err = mutrace_bpf__load(mu_skel); + if (err) { + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); + goto mutrace_cleanup; + } + err = common_pin_map(&mu_ctrl_map,mu_skel->obj,"mu_ctrl_map",mu_ctrl_path); + if(err < 0){ + goto mutrace_cleanup; + } + mumap_fd = bpf_map__fd(mu_ctrl_map); + struct mu_ctrl init_value = {false,false,false,MUTEX_WATCHER}; + + err = bpf_map_update_elem(mumap_fd, &key, &init_value, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + goto mutrace_cleanup; + } + //ctrl + if(err < 0){ + goto mutrace_cleanup; + } + //ctrl + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + goto mutrace_cleanup; + } + err = attach(mu_skel); + if (err) { + fprintf(stderr, "Failed to attach BPF skeleton\n"); + goto mutrace_cleanup; + } + + rb = ring_buffer__new(bpf_map__fd(mu_skel->maps.rb), mutrace_print, NULL, NULL); + if (!rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer\n"); + goto mutrace_cleanup; + } } while (!exiting) { if(env.SAR){ - sleep(1); + sleep(env.period); err = print_all(); if (err == -EINTR) { err = 0; @@ -562,36 +1115,123 @@ int main(int argc, char **argv) printf("Error polling perf buffer: %d\n", err); break; } - time_t now = time(NULL);// 获取当前时间 - struct tm *localTime = localtime(&now);// 将时间转换为本地时间结构 - if(!syscall_start_print){ - syscall_start_print=1; - }else{ + // time_t now = time(NULL);// 获取当前时间 + // struct tm *localTime = localtime(&now);// 将时间转换为本地时间结构 + // printf("\n\nTime: %02d:%02d:%02d\n",localTime->tm_hour, localTime->tm_min, localTime->tm_sec); + // printf("----------------------------------------------------------------------------------------------------------\n"); + // sleep(1); + } + else if (env.PREEMPT) { + err = ring_buffer__poll(rb, 100 /* timeout, ms */); + if (err == -EINTR) { + err = 0; + break; + } + if (err < 0) { + printf("Error polling perf buffer: %d\n", err); + break; + } + time_t now = time(NULL); + struct tm *localTime = localtime(&now); + if (!preempt_start_print) { + preempt_start_print = 1; + } else { printf("----------------------------------------------------------------------------------------------------------\n"); - max_print(); + printf("\nAverage_preempt_Time: %8d ns\n", sum_preemptTime / preempt_count); + } + printf("\nTime: %02d:%02d:%02d\n", localTime->tm_hour, localTime->tm_min, localTime->tm_sec); + printf("%-12s %-8s %-8s %11s\n", "COMM", "prev_pid", "next_pid", "duration_ns"); + preempt_count = 0; + sum_preemptTime = 0; + sleep(2); + } + else if (env.SCHEDULE_DELAY){ + err = schedule_print(); + if (err == -EINTR) { + err = 0; + break; + } + if (err < 0) { + break; } - printf("\n\nTime: %02d:%02d:%02d\n",localTime->tm_hour, localTime->tm_min, localTime->tm_sec); - printf("----------------------------------------------------------------------------------------------------------\n"); - sc_sum_time = 0 , sc_max_time = 0 ,sc_min_time = SYSCALL_MIN_TIME, sys_call_count = 0; - sleep(3); + if(env.SCHEDULE_DELAY&&!sd_ctrl.min_us_set){ + sleep(1); + } + } + else if(env.MQ_DELAY){ + err = ring_buffer__poll(rb, 1000 /* timeout, s */); + if (err == -EINTR) { + err = 0; + break; + } + if (err < 0) { + printf("Error polling perf buffer: %d\n", err); + break; + } + } + else if (env.MUTRACE) { + err = ring_buffer__poll(rb, 100 /* timeout, ms */); + if (err == -EINTR) { + err = 0; + break; + } + if (err < 0) { + printf("Error polling perf buffer: %d\n", err); + break; + } + if(env.MUTRACE&&mu_ctrl.mutex_detail){ + err = kmutex_detail(); + sleep(1); + printf("-------------------------------------------------------------\n"); + }else if(env.MUTRACE&&mu_ctrl.umutex){ + err = umutex_detail(); + sleep(1); + printf("-------------------------------------------------------------\n"); + } + } else { - printf("正在开发中......\n-c 打印cs_delay:\t对内核函数schedule()的执行时长进行测试;\n-s sar工具;\n-y 打印sc_delay:\t系统调用运行延迟进行检测; \n"); + printf("正在开发中......\n-c 打印cs_delay:\t对内核函数schedule()的执行时长进行测试;\n-s sar工具;\n-y 打印sc_delay:\t系统调用运行延迟进行检测; \n-p 打印preempt_time:\t对抢占调度时间输出;\n"); break; } } cs_delay_cleanup: + bpf_map__unpin(cs_ctrl_map, cs_ctrl_path); ring_buffer__free(rb); cs_delay_bpf__destroy(cs_skel); return err < 0 ? -err : 0; sar_cleanup: + bpf_map__unpin(sar_ctrl_map, sar_ctrl_path); sar_bpf__destroy(sar_skel); return err < 0 ? -err : 0; sc_delay_cleanup: + bpf_map__unpin(sc_ctrl_map, sc_ctrl_path); ring_buffer__free(rb); sc_delay_bpf__destroy(sc_skel); return err < 0 ? -err : 0; + +preempt_cleanup: + bpf_map__unpin(preempt_ctrl_map, preempt_ctrl_path); + ring_buffer__free(rb); + preempt_bpf__destroy(preempt_skel); + return err < 0 ? -err : 0; + +schedule_cleanup: + bpf_map__unpin(schedule_ctrl_map, schedule_ctrl_path); + schedule_delay_bpf__destroy(sd_skel); + return err < 0 ? -err : 0; + +mq_delay_cleanup: + bpf_map__unpin(mq_ctrl_map, mq_ctrl_path); + ring_buffer__free(rb); + mq_delay_bpf__destroy(mq_skel); + return err < 0 ? -err : 0; + +mutrace_cleanup: + ring_buffer__free(rb); + mutrace_bpf__destroy(mu_skel); + return err < 0 ? -err : 0; } diff --git a/MagicEyes/src/visualization/vscode_ext/README.md b/MagicEyes/src/visualization/vscode_ext/README.md new file mode 100644 index 000000000..cc57b0415 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/README.md @@ -0,0 +1,125 @@ +# lmp vscode 插件使用指南 + +### 1. 运行效果 + +![](./images/lmp_vscode_ext.gif) + +### 2. 安装与使用 + +#### 2.1 导入插件 + +![import_vscode_ext](./images/import_vscode_ext.png) + +安装成功如下: + +![lmp_ext_install_success](./images/lmp_ext_install_success.png) + +#### 2.2 设置 + +- 启动grafana(可以在docker中启动),启动prometheus与BPF后端采集程序可以看到数据呈现 +- 设置IP地址与端口,默认端口是`localhost:3000` +- 设置token + +![create_token](./images/create_token.png) + +> [grafana官方_创建token](https://grafana.com/docs/grafana/latest/administration/service-accounts/#create-a-service-account-in-grafana) + +![set_token](./images/set_token.png) + +- 设置可视化面板存放路径 + +> 注意:别忘记面板路径后面加 "/" + +![](./images/set_panel_addr.png) + +面板命名必须遵循如下规则: + +![](./images/panel_name.png) + +若面板不存在,或路径,或名称不对,将出现如下错误提示: + +![](./images/error_info.png) + +- 设置工具配置文件 + +请将配置文件路径输入在设置中,如`/home/fzy/lmp_tool_ext_config.json`,输入完成后,敲击enter键,程序将根据配置文件中的子系统与工具,生成左侧侧边栏的按钮。 + +配置文件写法参照 `.../MagicEyes/src/visualization/vscode_ext/tool_config_sample/lmp_tool_ext_config.json` + +#### 2.3 如何增加工具 + +配置文件如下。假如有一个工具,名为 mem_checker,输入 memory子系统,则 subsystem_list 不用修改,只需要在内存子系统处增加即可。如果有一个工具,名为 V4L2_tracer,属于 media子系统,则需要在subsystem_list中增加 media 子系统,并在 subsystem 下相应增加,不再赘述。 + +```json +{ + "name" : "lmp_tool_vscode_extension_config", + "version" : "0.0.1", + "subsystem_list" : [ + "CPU", + "memory", + "fs", + "network", + "system_diagnosis", + "hypervisor" + ], + "subsystem" : [ + { + "description" : "Linux CPU子系统观测工具集", + "tools" : [......] + }, + { + "description" : "Linux 内存子系统观测工具集", + "tools" : [ + { + "name": "mem_watcher", + "description" : "内存观测" + }, + { + "name": "mem_checker", + "description" : "内存检查" + } + ] + } +} +``` + + +### 3. 插件开发 + +#### 3.1 开发 + +安装yarn并且通过`yarn install`安装所需依赖 + +> tips: 按 F5 开启调试 + +#### 3.2 开发注意事项 + +1. yo code生成的框架,vscode最小版本是1.90,需要修改为1.74,不然我当前的版本。1.89无法运行插件 +2. tsconfig + +```json +{ + "compilerOptions": { + "module": "commonjs", // 不要用Node16,不然命令会触发失败 + "target": "ES2021", + "lib": ["ES2021"], + "sourceMap": true, + "rootDir": "src", + "strict": true /* enable all strict type-checking options */ + /* Additional Checks */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + } +} +``` + +3. 打包vsix + +```bash +# 进入插件开发文件夹 +vsce package +``` + + + diff --git a/MagicEyes/src/visualization/vscode_ext/images/create_token.png b/MagicEyes/src/visualization/vscode_ext/images/create_token.png new file mode 100644 index 000000000..382f04675 Binary files /dev/null and b/MagicEyes/src/visualization/vscode_ext/images/create_token.png differ diff --git a/MagicEyes/src/visualization/vscode_ext/images/error_info.png b/MagicEyes/src/visualization/vscode_ext/images/error_info.png new file mode 100644 index 000000000..90ef0fb71 Binary files /dev/null and b/MagicEyes/src/visualization/vscode_ext/images/error_info.png differ diff --git a/MagicEyes/src/visualization/vscode_ext/images/import_vscode_ext.png b/MagicEyes/src/visualization/vscode_ext/images/import_vscode_ext.png new file mode 100644 index 000000000..6b780114a Binary files /dev/null and b/MagicEyes/src/visualization/vscode_ext/images/import_vscode_ext.png differ diff --git a/MagicEyes/src/visualization/vscode_ext/images/lmp_ext_install_success.png b/MagicEyes/src/visualization/vscode_ext/images/lmp_ext_install_success.png new file mode 100644 index 000000000..2f5198b70 Binary files /dev/null and b/MagicEyes/src/visualization/vscode_ext/images/lmp_ext_install_success.png differ diff --git a/MagicEyes/src/visualization/vscode_ext/images/lmp_vscode_ext.gif b/MagicEyes/src/visualization/vscode_ext/images/lmp_vscode_ext.gif new file mode 100644 index 000000000..e875aa485 Binary files /dev/null and b/MagicEyes/src/visualization/vscode_ext/images/lmp_vscode_ext.gif differ diff --git a/MagicEyes/src/visualization/vscode_ext/images/panel_name.png b/MagicEyes/src/visualization/vscode_ext/images/panel_name.png new file mode 100644 index 000000000..103edbfd4 Binary files /dev/null and b/MagicEyes/src/visualization/vscode_ext/images/panel_name.png differ diff --git a/MagicEyes/src/visualization/vscode_ext/images/set_panel_addr.png b/MagicEyes/src/visualization/vscode_ext/images/set_panel_addr.png new file mode 100644 index 000000000..20b6d5e5c Binary files /dev/null and b/MagicEyes/src/visualization/vscode_ext/images/set_panel_addr.png differ diff --git a/MagicEyes/src/visualization/vscode_ext/images/set_token.png b/MagicEyes/src/visualization/vscode_ext/images/set_token.png new file mode 100644 index 000000000..06eefba18 Binary files /dev/null and b/MagicEyes/src/visualization/vscode_ext/images/set_token.png differ diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.eslintrc.json b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.eslintrc.json new file mode 100644 index 000000000..5dfecab7e --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module" + }, + "plugins": ["@typescript-eslint"], + "rules": { + "@typescript-eslint/naming-convention": "warn", + "@typescript-eslint/semi": "warn", + "curly": "warn", + "eqeqeq": "warn", + "no-throw-literal": "warn", + "semi": "off" + }, + "ignorePatterns": ["out", "dist", "**/*.d.ts"] +} diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.github/workflows/dashboards.yml b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.github/workflows/dashboards.yml new file mode 100644 index 000000000..fdcb9c114 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.github/workflows/dashboards.yml @@ -0,0 +1,39 @@ +name: Update dashboards + +on: + push: + branches: + - main + paths: + - dashboards/**.json + +jobs: + update-dashboards: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Get changed dashboards + id: changed-files + uses: tj-actions/changed-files@v41 + with: + files: dashboards/**.json + + - name: Update changed dashboards in Grafana + if: steps.changed-files.outputs.any_changed == 'true' + run: | + tmp=$(mktemp) + for file in ${{ steps.changed-files.outputs.all_changed_files }}; do + uid=$(jq -r '.uid' $file) + dashboardInfo=$(curl -H "Authorization: Bearer $GRAFANA_API_TOKEN" $GRAFANA_INSTANCE_URL/api/dashboards/uid/$uid) + currentVersion=$(echo $dashboardInfo | jq -r '.meta.version') + jq --argjson v $currentVersion '.version = $v' $file > $tmp && mv $tmp $file + dashboardJson='{"dashboard":'"$(jq -c . $file)"',"message":"'"$COMMIT_MESSAGE"'"}' + curl -X POST $GRAFANA_INSTANCE_URL/api/dashboards/db -H "Content-Type: application/json" -H "Authorization: Bearer $GRAFANA_API_TOKEN" -d "$dashboardJson" + done + env: + GRAFANA_INSTANCE_URL: ${{ secrets.GRAFANA_INSTANCE_URL }} + GRAFANA_API_TOKEN: ${{ secrets.GRAFANA_API_TOKEN }} + COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + \ No newline at end of file diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.github/workflows/publish.yml b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.github/workflows/publish.yml new file mode 100644 index 000000000..281dfad47 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.github/workflows/publish.yml @@ -0,0 +1,48 @@ +on: + push: + tags: + - "*" + +# These permissions are needed to assume roles from Github's OIDC. +permissions: + contents: write + id-token: write + +name: Publish Extension +jobs: + eslint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: npm install + - run: npm run compile + - run: npm run lint + publish: + needs: eslint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: npm install + - id: get-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main + with: + # Secrets placed in the ci/repo/grafana// path in Vault + repo_secrets: | + OPEN_VSX_TOKEN=openvsx:token + VS_MARKETPLACE_TOKEN=vscode-marketplace:token + - name: Publish to Open VSX + uses: HaaLeo/publish-vscode-extension@v0 + with: + pat: ${{ env.OPEN_VSX_TOKEN }} + registryUrl: https://open-vsx.org + - name: Publish to Visual Studio Marketplace + id: publishToMarketplace + uses: HaaLeo/publish-vscode-extension@v0 + with: + pat: ${{ env.VS_MARKETPLACE_TOKEN }} + registryUrl: https://marketplace.visualstudio.com + - uses: ncipollo/release-action@v1 + with: + allowUpdates: true + artifacts: "${{ steps.publishToMarketplace.outputs.vsixPath }}" + token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.gitignore b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.gitignore new file mode 100644 index 000000000..f27bbfb36 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.gitignore @@ -0,0 +1,5 @@ +!.vscode +.yarn +node_modules +.idea +dist \ No newline at end of file diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.prettierrc.json b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.prettierrc.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.prettierrc.json @@ -0,0 +1 @@ +{} diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscode/extensions.json b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscode/extensions.json new file mode 100644 index 000000000..57dbdae42 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": ["dbaeumer.vscode-eslint", "amodio.tsl-problem-matcher"] +} diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscode/launch.json b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscode/launch.json new file mode 100644 index 000000000..0fcf4f973 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscode/launch.json @@ -0,0 +1,35 @@ +// A launch configuration that compiles the extension and then opens it inside a new window +// Use IntelliSense to learn about possible attributes. +// Hover to view descriptions of existing attributes. +// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Run Extension", + "type": "extensionHost", + "request": "launch", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}", + "--disable-extensions" + ], + "outFiles": ["${workspaceFolder}/dist/**/*.js"], + "preLaunchTask": "${defaultBuildTask}" + }, + { + "name": "Extension Tests", + "type": "extensionHost", + "request": "launch", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}", + "--extensionTestsPath=${workspaceFolder}/out/test/suite/index", + "--disable-extensions" + ], + "outFiles": [ + "${workspaceFolder}/out/**/*.js", + "${workspaceFolder}/dist/**/*.js" + ], + "preLaunchTask": "tasks: watch-tests" + } + ] +} diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscode/settings.json b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscode/settings.json new file mode 100644 index 000000000..64ee92966 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscode/settings.json @@ -0,0 +1,13 @@ +// Place your settings in this file to overwrite default and user settings. +{ + "files.exclude": { + "out": false, // set this to true to hide the "out" folder with the compiled JS files + "dist": false // set this to true to hide the "dist" folder with the compiled JS files + }, + "search.exclude": { + "out": true, // set this to false to include "out" folder in search results + "dist": true // set this to false to include "dist" folder in search results + }, + // Turn off tsc task auto detection since we have the necessary tasks as npm scripts + "typescript.tsc.autoDetect": "off" +} diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscode/tasks.json b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscode/tasks.json new file mode 100644 index 000000000..9e3300b04 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscode/tasks.json @@ -0,0 +1,37 @@ +// See https://go.microsoft.com/fwlink/?LinkId=733558 +// for the documentation about the tasks.json format +{ + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "watch", + "problemMatcher": "$ts-webpack-watch", + "isBackground": true, + "presentation": { + "reveal": "never", + "group": "watchers" + }, + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "type": "npm", + "script": "watch-tests", + "problemMatcher": "$tsc-watch", + "isBackground": true, + "presentation": { + "reveal": "never", + "group": "watchers" + }, + "group": "build" + }, + { + "label": "tasks: watch-tests", + "dependsOn": ["npm: watch", "npm: watch-tests"], + "problemMatcher": [] + } + ] +} diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscodeignore b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscodeignore new file mode 100644 index 000000000..c6136798a --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscodeignore @@ -0,0 +1,13 @@ +.vscode/** +.vscode-test/** +out/** +node_modules/** +src/** +.gitignore +.yarnrc +webpack.config.js +vsc-extension-quickstart.md +**/tsconfig.json +**/.eslintrc.json +**/*.map +**/*.ts diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.yarnrc b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.yarnrc new file mode 100644 index 000000000..f757a6ac5 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.yarnrc @@ -0,0 +1 @@ +--ignore-engines true \ No newline at end of file diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/CHANGELOG.md b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/CHANGELOG.md new file mode 100644 index 000000000..c8a5139de --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/CHANGELOG.md @@ -0,0 +1,34 @@ +# Change Log + +All notable changes to the "grafana-vscode" extension will be documented in this file. + +## v0.0.16 +- Add additional proxy endpoints (to support more dashboards/etc) (#80) + +## v0.0.15 +- Follow redirects and don't fail on trailing slashes (#77) +- Theming (light/dark) support for Grafana (#74) + +## v0.0.14 +- Improved readme (#62) + +## v0.0.13 +- Add telemetry that will allow us to evaluate usefulness of this extension (#48) + +## v0.0.12 +- Removed Kiosk mode - this was preventing the 'add panel' option from showing (#59) + +## v0.0.11 +- Fixed usage on Windows (#57) + +## v0.0.10 +- Readme tweaks (#54) + +## v0.0.9 +- Added support for vscodium (#53) + +## v0.0.8 +- Improved readme (#52) + +## v0.0.7 +- First release to VSCode Marketplace diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/CODEOWNERS b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/CODEOWNERS new file mode 100644 index 000000000..d2c7fd858 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/CODEOWNERS @@ -0,0 +1,7 @@ +# Docs on CODEOWNERS: +# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners +# +# Later codeowner matches take precedence over earlier ones. + +# Default owner +* @grafana/platform-cat diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/LICENSE b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/LICENSE new file mode 100644 index 000000000..373dde574 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2015 Grafana Labs + + 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. diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/README.md b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/README.md new file mode 100644 index 000000000..3937cb2a0 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/README.md @@ -0,0 +1,78 @@ +# VS Code Extension for Grafana + +Grafana has an extensive UI for editing its dashboards. For many, this is sufficient for their needs. If this is you, this extension is not for you. + +However, some wish to use software development tools (e.g. git) to manage their dashboards and other observability resources. Dashboards can be exported as JSON, however this JSON is hard to understand and interpret. This extension allows you to: +- Open a Grafana dashboard JSON file +- Start a live preview of that dashboard inside VS Code, connected to live data from a Grafana instance of your choice +- Edit the dashboard in the preview, using the normal Grafana dashboard editor UI +- From the editor UI, save the updated dashboard back to the original JSON file + +Managing dashboards as code moves the single source of truth from Grafana itself to your version control system, which allows for dashboards to participate in gitops style workflows that are commonly used for much of the rest of software development. + +> **This library is experimental** +> +> The code in this repository should be considered experimental. Documentation is only +> available alongside the code. It comes without support, but we are keen to receive +> feedback on the product and suggestions on how to improve it, though we cannot commit to +> resolution of any particular issue. No SLAs are available. It is not meant to be used in +> production environments, and the risks are unknown/high. +> +> Additional information can be found in [Release life cycle for Grafana Labs](https://grafana.com/docs/release-life-cycle/). + +## Why Work With Dashboards as Code? + +- JSON dashboards can be stored in your version control system. This provides a simple solution for rollback, history, auditing, and even change control. +- If you have change-control policies for your Grafana stack, this extension allows you and other developers to test and verify dashboard changes before deploying them +- Dashboards can be generated with tools like [Grafonnet](https://grafana.github.io/grafonnet/index.html) +- Dashboards can be integrated into your IaC practices using [Terraform, Ansible, Grafana Operator, or Grizzly](https://grafana.com/blog/2022/12/06/a-complete-guide-to-managing-grafana-as-code-tools-tips-and-tricks/) + +## Features + +- Reads a dashboard JSON you have locally. +- Opens the dashboard configured in the JSON in a running Grafana instance, right inside your IDE. +- Allows you to edit the dashboard from the UI. +- Saves your changes to _your_ JSON when you hit "Save" in the preview. + +## Requirements + +- Have a dashboard JSON handy. +- Have a running instance of Grafana locally _or_ have access to a hosted Grafana instance. +- If you intend to use a dashboard across multiple Grafana instances, you will need to use datasources that have been deployed via the API, as these datasources will require consistent UIDs. + +## Usage: + +### Install from the Marketplace + +1. Select the Extensions icon (![extensions icon](./public/extensions-icon.png)) on the left bar in VSCode. +2. Enter `Grafana` into the search box. Select the option for `Grafana / Grafana Editor` and click `Install`. +3. Open the Settings tab inside the extension (CTRL+, (comma) or `cmd` + `,` on Mac) and search for `grafana`. Then select `Extensions`. + +### Configure the Extension +1. Provide the default URL for your Grafana instance in the `URL` field. If you are using a local Grafana instance, the default value is `http://localhost:3000`. +2. Create a [Service account in Grafana](https://grafana.com/docs/grafana/latest/administration/service-accounts/#create-a-service-account-in-grafana) and add a token to it. +3. In the VS Code settings, click `Set your token, securely` then paste your token into the popup. Press ENTER. + +### Using the Extension +1. Open a folder on your computer that has some dashboard JSON (if you don't have any of your own, navigate to the `dashboards` folder of [this repo](https://github.com/grafana/grafana-vs-code-extension/tree/main/dashboards)). +2. Right-click on a dashboard JSON file in the file explorer and select `Edit in Grafana`. +3. Have fun! +4. Note, clicking `save` on your dashboard will update the JSON file in your local folder. + +### Run from Repository +1. If using local Grafana, start Grafana locally or via Docker. +2. Run `yarn install` in this repo. +3. Open this repo VS Code, then press `F5` to start the extension locally. +4. Continue from step 4 above. + +### Develop +To make changes to this codebase, follow the instructions about how to run from this repository. Then, in your original VS Code window, make changes to the extension. Then, restart the extension with either CTRL+SHIFT+F5 (CMD+SHIFT+F5 on a Mac) or by clicking the green restart circle. + +To view debug logs, use CTRL+SHIFT+P (CMD+SHIFT+P on Mac) then select "Developer: Open Webview Developer Tools". + +## Extension Settings +- `grafana-vscode.URL`: Set the URL of the Grafana instance you want to open the dashboard in. Defaults to 'http://localhost:3000'. +- `grafana-vscode.service-account-token`: A Service Account token. This is stored in the operating system secret store. Defaults to an empty string. + +## Extension communication with Grafana +Details of how this extension communicates with Grafana is available [here](https://github.com/grafana/grafana-vs-code-extension/blob/main/how-it-works.md). diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/dashboards/streaming.grafana b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/dashboards/streaming.grafana new file mode 100644 index 000000000..20d0d69fe --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/dashboards/streaming.grafana @@ -0,0 +1,703 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 4194, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "channel": "plugin/testdata/random-20Hz-stream", + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "queryType": "measurements", + "refId": "A" + } + ], + "title": "Streaming", + "type": "timeseries" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "description": "Should be smaller given the longer value", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 15, + "options": { + "barRadius": 0, + "barWidth": 1, + "fullHighlight": false, + "groupWidth": 0.82, + "legend": { + "calcs": [ + "max" + ], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "none", + "text": {}, + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "csvContent": "Name,Stat1,Stat2\nStockholm, 10, 15\nNew York, 19, 5\nLondon, 10, 1\nNegative, 15, -5\nLong value, 15,10", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Auto sizing & auto show values", + "type": "barchart" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 8, + "x": 0, + "y": 10 + }, + "id": 16, + "options": { + "barRadius": 0, + "barWidth": 1, + "fullHighlight": false, + "groupWidth": 0.89, + "legend": { + "calcs": [ + "max" + ], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "none", + "text": {}, + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "csvContent": "Name,Stat1,Stat2,Stat3,Stat4,Stat5,Stat6,Stat7,Stat8,Stat9,Stat10\nA, 10, 15,8,3,4,12,14,1,5,10\nB, 19, 5,8,3,4,12,14,6,7,2\nC, 15, 5,8,3,4,10,4,6,7,2\n", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "auto show values & No room for value", + "type": "barchart" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 8, + "x": 8, + "y": 10 + }, + "id": 17, + "options": { + "barRadius": 0, + "barWidth": 1, + "fullHighlight": false, + "groupWidth": 0.89, + "legend": { + "calcs": [ + "max" + ], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "orientation": "auto", + "showValue": "always", + "stacking": "none", + "text": { + "valueSize": 100 + }, + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "csvContent": "Name,Stat1,Stat2,Stat3,Stat4,Stat5,Stat6,Stat7,Stat8,Stat9,Stat10\nA, 10, 15,8,3,4,12,14,1,5,10\nB, 19, 5,8,3,4,12,14,6,7,2\nC, 15, 5,8,3,4,10,4,6,7,2\n", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "auto show values & Always show value", + "type": "barchart" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 8, + "x": 16, + "y": 10 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 9, + "refId": "A" + } + ], + "title": "Fixed value sizing", + "type": "timeseries" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 7, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 12, + "x": 0, + "y": 21 + }, + "id": 18, + "options": { + "barRadius": 0, + "barWidth": 1, + "fullHighlight": false, + "groupWidth": 0.82, + "legend": { + "calcs": [ + "max" + ], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "orientation": "horizontal", + "showValue": "auto", + "stacking": "none", + "text": {}, + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "csvContent": "Name,Stat1,Stat2\nStockholm, 10, 15\nNew York, 19, -5\nLondon, 10, 1\nLong value, 15,10", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Auto sizing & auto show values", + "type": "barchart" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 12, + "x": 12, + "y": 21 + }, + "id": 19, + "options": { + "barRadius": 0, + "barWidth": 1, + "fullHighlight": false, + "groupWidth": 0.89, + "legend": { + "calcs": [ + "max" + ], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "orientation": "horizontal", + "showValue": "auto", + "stacking": "none", + "text": {}, + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "csvContent": "Name,Stat1,Stat2,Stat3,Stat4,Stat5,Stat6,Stat7,Stat8,Stat9,Stat10\nA, 10, 15,8,3,4,12,14,1,5,10\nB, 19, 5,8,3,4,12,14,6,7,2\nC, 15, 5,8,3,4,10,4,6,7,2\n", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "auto show values & little room", + "type": "barchart" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [ + "gdev", + "panel-tests", + "barchart" + ], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "BarChart - Panel Tests - Value sizing Copy", + "uid": "wziLqrvnz", + "version": 5, + "weekStart": "" +} \ No newline at end of file diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/dashboards/streaming.json b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/dashboards/streaming.json new file mode 100644 index 000000000..20d0d69fe --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/dashboards/streaming.json @@ -0,0 +1,703 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 4194, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "channel": "plugin/testdata/random-20Hz-stream", + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "queryType": "measurements", + "refId": "A" + } + ], + "title": "Streaming", + "type": "timeseries" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "description": "Should be smaller given the longer value", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 15, + "options": { + "barRadius": 0, + "barWidth": 1, + "fullHighlight": false, + "groupWidth": 0.82, + "legend": { + "calcs": [ + "max" + ], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "none", + "text": {}, + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "csvContent": "Name,Stat1,Stat2\nStockholm, 10, 15\nNew York, 19, 5\nLondon, 10, 1\nNegative, 15, -5\nLong value, 15,10", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Auto sizing & auto show values", + "type": "barchart" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 8, + "x": 0, + "y": 10 + }, + "id": 16, + "options": { + "barRadius": 0, + "barWidth": 1, + "fullHighlight": false, + "groupWidth": 0.89, + "legend": { + "calcs": [ + "max" + ], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "none", + "text": {}, + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "csvContent": "Name,Stat1,Stat2,Stat3,Stat4,Stat5,Stat6,Stat7,Stat8,Stat9,Stat10\nA, 10, 15,8,3,4,12,14,1,5,10\nB, 19, 5,8,3,4,12,14,6,7,2\nC, 15, 5,8,3,4,10,4,6,7,2\n", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "auto show values & No room for value", + "type": "barchart" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 8, + "x": 8, + "y": 10 + }, + "id": 17, + "options": { + "barRadius": 0, + "barWidth": 1, + "fullHighlight": false, + "groupWidth": 0.89, + "legend": { + "calcs": [ + "max" + ], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "orientation": "auto", + "showValue": "always", + "stacking": "none", + "text": { + "valueSize": 100 + }, + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "csvContent": "Name,Stat1,Stat2,Stat3,Stat4,Stat5,Stat6,Stat7,Stat8,Stat9,Stat10\nA, 10, 15,8,3,4,12,14,1,5,10\nB, 19, 5,8,3,4,12,14,6,7,2\nC, 15, 5,8,3,4,10,4,6,7,2\n", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "auto show values & Always show value", + "type": "barchart" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 8, + "x": 16, + "y": 10 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 9, + "refId": "A" + } + ], + "title": "Fixed value sizing", + "type": "timeseries" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 7, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 12, + "x": 0, + "y": 21 + }, + "id": 18, + "options": { + "barRadius": 0, + "barWidth": 1, + "fullHighlight": false, + "groupWidth": 0.82, + "legend": { + "calcs": [ + "max" + ], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "orientation": "horizontal", + "showValue": "auto", + "stacking": "none", + "text": {}, + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "csvContent": "Name,Stat1,Stat2\nStockholm, 10, 15\nNew York, 19, -5\nLondon, 10, 1\nLong value, 15,10", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Auto sizing & auto show values", + "type": "barchart" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 12, + "x": 12, + "y": 21 + }, + "id": 19, + "options": { + "barRadius": 0, + "barWidth": 1, + "fullHighlight": false, + "groupWidth": 0.89, + "legend": { + "calcs": [ + "max" + ], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "orientation": "horizontal", + "showValue": "auto", + "stacking": "none", + "text": {}, + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "csvContent": "Name,Stat1,Stat2,Stat3,Stat4,Stat5,Stat6,Stat7,Stat8,Stat9,Stat10\nA, 10, 15,8,3,4,12,14,1,5,10\nB, 19, 5,8,3,4,12,14,6,7,2\nC, 15, 5,8,3,4,10,4,6,7,2\n", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "auto show values & little room", + "type": "barchart" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [ + "gdev", + "panel-tests", + "barchart" + ], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "BarChart - Panel Tests - Value sizing Copy", + "uid": "wziLqrvnz", + "version": 5, + "weekStart": "" +} \ No newline at end of file diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/dashboards/test-dashboard.grafana b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/dashboards/test-dashboard.grafana new file mode 100644 index 000000000..416609ee5 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/dashboards/test-dashboard.grafana @@ -0,0 +1,227 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 4278, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "barRadius": 0, + "barWidth": 0.97, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "none", + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "channel": "plugin/testdata/random-2s-stream", + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "filter": { + "fields": [ + "Time", + "Min" + ] + }, + "queryType": "randomWalk", + "refId": "A" + } + ], + "title": "Panel Title", + "type": "barchart" + }, + { + "datasource": { + "type": "grafana", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "queryType": "randomWalk", + "refId": "A" + } + ], + "title": "Panel Title", + "type": "timeseries" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Test dashboard", + "uid": "e9abc1a5-1b8f-4327-83e4-0b3c2b3722a9", + "version": 6, + "weekStart": "" +} \ No newline at end of file diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/dashboards/test-dashboard.json b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/dashboards/test-dashboard.json new file mode 100644 index 000000000..416609ee5 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/dashboards/test-dashboard.json @@ -0,0 +1,227 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 4278, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "barRadius": 0, + "barWidth": 0.97, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "none", + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "channel": "plugin/testdata/random-2s-stream", + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "filter": { + "fields": [ + "Time", + "Min" + ] + }, + "queryType": "randomWalk", + "refId": "A" + } + ], + "title": "Panel Title", + "type": "barchart" + }, + { + "datasource": { + "type": "grafana", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "queryType": "randomWalk", + "refId": "A" + } + ], + "title": "Panel Title", + "type": "timeseries" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Test dashboard", + "uid": "e9abc1a5-1b8f-4327-83e4-0b3c2b3722a9", + "version": 6, + "weekStart": "" +} \ No newline at end of file diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/how-it-works.md b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/how-it-works.md new file mode 100644 index 000000000..06432bd5d --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/how-it-works.md @@ -0,0 +1,24 @@ +# Extension communication with Grafana + +The below diagram explains how this extension communicates with Grafana. + +For best viewing, view this page on GitHub. + +```mermaid +sequenceDiagram + participant Webview as Webview
(inside the VS Code Extension) + participant Iframe as Iframe (Grafana)
(rendered inside the extension's webview) + participant ProxyServer as Proxy server
(running inside the extension) + participant Grafana as Grafana Instance
(running outside the extension) + participant FileSystem as File system + + Note over ProxyServer: Starts on random port + Webview->>Iframe: Render an iframe for Grafana. Callback URL to the proxy is an iframe src URL param + Iframe->>ProxyServer: Requests HTML dashboard page/etc + ProxyServer->>Grafana: Requests HTML dashboards page/etc + Iframe->>ProxyServer: Request to retrieve the JSON for opened dashboard + FileSystem->>ProxyServer: Retrieve JSON + ProxyServer-->>Iframe: JSON for opened dashboard + Iframe->>ProxyServer: Edited dashboard JSON on save + ProxyServer->>FileSystem: Edited dashboard JSON +``` diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/media/StartTV.svg b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/media/StartTV.svg new file mode 100644 index 000000000..af08a09f7 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/media/StartTV.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/package.json b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/package.json new file mode 100644 index 000000000..7e32e4da5 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/package.json @@ -0,0 +1,191 @@ +{ + "name": "grafana-vscode", + "author": "Grafana Labs", + "displayName": "lmp_Grafana", + "description": "Grafana Editor for lmp", + "icon": "public/grafana_icon.png", + "version": "0.0.16", + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/grafana/grafana-vs-code-extension" + }, + "engines": { + "vscode": "^1.76.0" + }, + "publisher": "Grafana", + "categories": [ + "Visualization" + ], + "keywords": [ + "Grafana", + "dashboards" + ], + "activationEvents": [ + "onStartupFinished" + ], + "main": "./dist/extension.js", + "contributes": { + "customEditors": [ + { + "viewType": "grafana.dashboard", + "displayName": "Grafana", + "selector": [ + { + "filenamePattern": "*.grafana" + } + ] + } + ], + "commands": [ + { + "command": "grafana-vscode.openUrl", + "title": "Edit in Grafana" + } + ], + "menus": { + "commandPalette": [ + { + "command": "grafana-vscode.openUrl", + "when": "false" + } + ], + "explorer/context": [ + { + "command": "grafana-vscode.openUrl", + "when": "resourceExtname == .json" + } + ] + }, + "iconThemes": [ + { + "id": "grafana", + "label": "Grafana", + "path": "./public/icon-theme.json" + } + ], + "configuration": { + "title": "Grafana", + "properties": { + "grafana-vscode.URL": { + "type": "string", + "default": "http://localhost:3000", + "description": "Grafana instance URL", + "order": 1 + }, + "grafana-vscode.service-account-token": { + "type": "boolean", + "default": true, + "markdownDescription": "A service account token for your Grafana instance. Click the link below to add this to secure storage.\n\n[Set your token, securely](command:grafana-vscode.setPassword)", + "order": 2 + }, + "grafana-vscode.theme": { + "type": "string", + "default": "inherit", + "enum": ["inherit", "fixed", "dark", "light"], + "enumDescriptions": [ + "Inherit Grafana theme from VSCode", + "Use Grafana's own default theme", + "Use dark Grafana theme", + "Use light Grafana theme" + ] + }, + "grafana-vscode.telemetry": { + "type": "boolean", + "default": true, + "markdownDescription": "Enable basic telemetry. All data is anonymous and only used to help with feature prioritization/gloating/etc.", + "order": 3 + }, + "grafana-vscode.default_panel_path": { + "type": "string", + "default": "/home/fzy/Desktop/panels/", + "description": "the default panels search path", + "order": 4 + }, + "grafana-vscode.default_tool_config_file": { + "type": "string", + "default": "/home/fzy/lmp_tool_ext_config.json", + "description": "the default tool config file", + "order": 5 + } + } + }, + "viewsContainers": { + "activitybar": [ + { + "id": "lmp_visualization", + "title": "lmp_visualization", + "icon": "media/StartTV.svg" + } + ] + }, + "views": { + "lmp_visualization": [ + { + "id": "lmp_visualization.panel", + "name": "Panel" + }, + { + "id": "lmp_visualization.about", + "name": "about" + } + ] + }, + "viewsWelcome": [ + { + "view": "lmp_visualization.panel", + "contents": "this is welcome content", + "when": "true" + }, + { + "view": "lmp_visualization.about", + "contents": "基于grafana与prometheus的vscode可视化插件, 隶属于智能车载OS诊断与优化专家系统", + "when": "true" + } + ] + }, + "scripts": { + "vscode:prepublish": "yarn run package", + "compile": "webpack", + "watch": "webpack --watch", + "package": "webpack --mode production --devtool hidden-source-map", + "compile-tests": "tsc -p . --outDir out", + "watch-tests": "tsc -p . -w --outDir out", + "pretest": "yarn run compile-tests && yarn run compile && yarn run lint", + "lint": "eslint src --ext ts", + "test": "node ./out/test/runTest.js" + }, + "devDependencies": { + "@types/cors": "^2.8.13", + "@types/express": "^4.17.17", + "@types/glob": "^8.1.0", + "@types/http-proxy": "^1.17.10", + "@types/mocha": "^10.0.1", + "@types/node": "16.x", + "@types/source-map-support": "^0.5.8", + "@types/vscode": "^1.76.0", + "@typescript-eslint/eslint-plugin": "^5.53.0", + "@typescript-eslint/parser": "^5.53.0", + "@vscode/test-electron": "^2.2.3", + "eslint": "^8.34.0", + "glob": "^8.1.0", + "mocha": "^10.2.0", + "prettier": "3.0.2", + "ts-loader": "^9.4.2", + "typescript": "^4.9.5", + "webpack": "^5.94.0", + "webpack-cli": "^5.0.1" + }, + "dependencies": { + "@types/uuid": "^9.0.6", + "axios": "^1.7.4", + "cors": "^2.8.5", + "express": "^4.20.0", + "http-proxy": "^1.18.1", + "http-proxy-middleware": "^2.0.6", + "open": "^8.4.2", + "source-map-support": "^0.5.21", + "transformer-proxy": "^0.3.5", + "uuid": "^9.0.1" + } +} diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/error.html b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/error.html new file mode 100644 index 000000000..2a6c37c1b --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/error.html @@ -0,0 +1,25 @@ + +

A problem occurred connecting to Grafana

${error} +

+ diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/extensions-icon.png b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/extensions-icon.png new file mode 100644 index 000000000..e461ccdba Binary files /dev/null and b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/extensions-icon.png differ diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/grafana_icon.png b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/grafana_icon.png new file mode 100644 index 000000000..4332a33f2 Binary files /dev/null and b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/grafana_icon.png differ diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/icon-theme.json b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/icon-theme.json new file mode 100644 index 000000000..c24a46150 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/icon-theme.json @@ -0,0 +1,10 @@ +{ + "iconDefinitions": { + "grafana": { + "iconPath": "./grafana_icon.png" + } + }, + "fileExtensions": { + "grafana": "grafana" + } +} diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/iframe.css b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/iframe.css new file mode 100644 index 000000000..23551dc65 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/iframe.css @@ -0,0 +1,31 @@ + +.sidemenu { + display: none !important; +} + +[aria-label="Share dashboard or panel"] { + display: none !important; +} + +[aria-label="Search links"] { + pointer-events: none; + cursor: default; +} + +[aria-label="Breadcrumbs"] { + pointer-events: none; + cursor: default; +} + +[aria-label="Toggle menu"] { + pointer-events: none; + cursor: default; +} + +[title="Toggle top search bar"] { + display: none !important; +} + +.main-view > div:first-of-type > div:first-of-type { + display: none !important; +} \ No newline at end of file diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/webview.html b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/webview.html new file mode 100644 index 000000000..482fc12a6 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/webview.html @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/editor.ts b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/editor.ts new file mode 100644 index 000000000..7f0ab80ce --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/editor.ts @@ -0,0 +1,84 @@ +import * as vscode from "vscode"; +import * as fs from "fs"; +import { port } from "./server"; + +export class GrafanaEditorProvider implements vscode.CustomTextEditorProvider { + static webviewContent = ""; + static webviewErrorContent = ""; + + static readonly viewType = "grafana.dashboard"; + + public static register(context: vscode.ExtensionContext): vscode.Disposable { + const provider = new GrafanaEditorProvider(context); + const providerRegistration = vscode.window.registerCustomEditorProvider( + GrafanaEditorProvider.viewType, + provider, + { + webviewOptions: { + retainContextWhenHidden: true, + }, + }, + ); + this.webviewContent = fs.readFileSync( + context.asAbsolutePath("public/webview.html"), + "utf-8", + ); + this.webviewContent = this.webviewContent.replaceAll("${editor}", "VSCode"); + + return providerRegistration; + } + + constructor(private readonly context: vscode.ExtensionContext) {} + + /** + * Called when our custom editor is opened. + */ + public async resolveCustomTextEditor( + document: vscode.TextDocument, + webviewPanel: vscode.WebviewPanel, + _token: vscode.CancellationToken, + ) { + webviewPanel.webview.options = { + enableScripts: true, + }; + + webviewPanel.webview.html = this.getHtmlForWebview(document); + } + + private getTheme(): string { + + const settings = vscode.workspace.getConfiguration("grafana-vscode"); + const theme = settings.get("theme"); + if (theme === "dark" || theme === "light") { + return `theme=${theme}&`; + } + if (theme === "fixed") { + return ""; + } + + const kind = vscode.window.activeColorTheme.kind; + if (kind === vscode.ColorThemeKind.Light || kind === vscode.ColorThemeKind.HighContrastLight) { + return "theme=light&"; + } else { + return "theme=dark&"; + } + } + + /** + * Get the static html used for the editor webviews. + */ + private getHtmlForWebview(document: vscode.TextDocument): string { + const dash = JSON.parse(document.getText()); + const uid: string = dash.uid; + const theme = this.getTheme(); + + let view = GrafanaEditorProvider.webviewContent.replaceAll( + "${filename}", + document.uri.fsPath, + ); + view = view.replaceAll("${port}", port.toString()); + view = view.replaceAll("${uid}", uid); + view = view.replaceAll("${theme}", theme); + return view; + } +} diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/extension.ts b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/extension.ts new file mode 100644 index 000000000..152b3c7b2 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/extension.ts @@ -0,0 +1,473 @@ +// The module 'vscode' contains the VS Code extensibility API +// Import the module and reference it with the alias vscode in your code below +import * as vscode from "vscode"; +import { startServer, restartServer, stopServer, TOKEN_SECRET } from "./server"; +import { GrafanaEditorProvider } from "./editor"; +import { install as installSourceMapSupport } from 'source-map-support'; +import { sendTelemetry } from "./telemetry"; +import { setVersion } from "./util"; + +import * as fs from 'fs' // fzy: 为了检查面板文件是否存在 + + +let default_panel_path = "/home/fzy/Desktop/panels/"; // fzy: 为了检查面板文件是否存在 +let default_tool_config_file = "/home/fzy/lmp_tool_ext_config.json"; +let sub_key = 0; // 子系统计数,用于与element.label进行匹配 + + +// This method is called when your extension is activated +// Your extension is activated the very first time the command is executed +export async function activate(ctx: vscode.ExtensionContext) { + + setVersion(ctx.extension.packageJSON.version); + startServer(ctx.secrets, ctx.extensionPath); + + ctx.subscriptions.push(GrafanaEditorProvider.register(ctx)); + + ctx.subscriptions.push( + vscode.commands.registerCommand( + "grafana-vscode.openUrl", + (uri: vscode.Uri) => { + sendTelemetry(ctx); + vscode.commands.executeCommand( + "vscode.openWith", + uri, + GrafanaEditorProvider.viewType, + ); + }), + ); + // fzy + // ------------------------------------------------------------------------------------- + TreeViewProvider.initTreeViewItem(); + ctx.subscriptions.push(vscode.commands.registerCommand('itemClick', (label) => { + let file_path = default_panel_path + label + '.json'; + //console.log('file_path = ', file_path); + if (fs.existsSync(file_path)) { + let uri = vscode.Uri.file(file_path); + // 获取label + // 根据label确定uri + //console.log('uri = ', uri); + //console.log('lable = ', label); + sendTelemetry(ctx); + vscode.commands.executeCommand( + "vscode.openWith", + uri, + GrafanaEditorProvider.viewType, + ); + } + else { + let panel_search_info = label + " 可视化面板文件不存在,请检查!" + vscode.window.showErrorMessage(panel_search_info); + } + + })); + // ------------------------------------------------------------------------------------- + + vscode.workspace.onDidChangeConfiguration(async(event) => { + if (event.affectsConfiguration("grafana-vscode.URL")) { + restartServer(ctx.secrets, ctx.extensionPath); + } + // fzy, 让用户可以在设置中修改面板放置路径 + if (event.affectsConfiguration("grafana-vscode.default_panel_path")) { + const settings = vscode.workspace.getConfiguration("grafana-vscode"); + + default_panel_path = String(settings.get("default_panel_path")); + //console.log("path = ", default_panel_path); + } + // fzy, 让用户可以在设置中修改json配置文件放置路径及配置文件名字 + if (event.affectsConfiguration("grafana-vscode.default_tool_config_file")) { + const settings = vscode.workspace.getConfiguration("grafana-vscode"); + default_tool_config_file = String(settings.get("default_tool_config_file")); + //console.log("file = ", default_tool_config_file); + sub_key = 0; // 全局变量先清零,不然 subsystem无法匹配 + TreeViewProvider.initTreeViewItem(); + } + }); + + vscode.commands.registerCommand('grafana-vscode.setPassword', async () => { + const passwordInput = await vscode.window.showInputBox({ + password: true, + placeHolder: "My Grafana service account token", + title: "Enter the service account token for your Grafana instance. This value will be stored securely in your operating system's secure key store." + }) ?? ''; + await ctx.secrets.store(TOKEN_SECRET, passwordInput); + restartServer(ctx.secrets, ctx.extensionPath); + }); + + installSourceMapSupport(); +} + +// This method is called when your extension is deactivated +export function deactivate() { + stopServer(); +} + + + +// --------------------------------------------------------------------------------- +// fzy +import { CancellationToken, Event, ProviderResult, TreeDataProvider, TreeItem, TreeItemCollapsibleState, window} from "vscode"; +import { json } from "stream/consumers"; + +// 扩展 TreeItem +/* +export class TreeItemNode extends TreeItem { + constructor( + public readonly label: string = '', + public readonly collapsibleState: TreeItemCollapsibleState, + ){ + super(label, collapsibleState); + } + + command = { + title: this.label, + command: 'itemClick', + tooltip: this.label, + arguments: [ + this.label, + ] + }; + // 获取json文件路径 + // path = TreeItemNode.getPanelUrl(this.label); + + //static getPanelUrl(label: string):Uri { + // return Uri.file(join(__filename)); + //} +} +*/ + +export class TreeViewProvider implements TreeDataProvider { + + onDidChangeTreeData?: Event | undefined; + + getTreeItem(element: TreeItem): TreeItem | Thenable { + return element; + } + getChildren(element?: TreeItem | undefined): ProviderResult { + let jsonData: any; // 保存 json 数据 + jsonData = readLmpConfig(); // 读取json配置文件信息 + + + let arr: TreeItem[] = new Array(); + // treeview 根节点 + if (element == undefined) { + for (const key in jsonData.subsystem_list) { + let item: TreeItem = new TreeItem(jsonData.subsystem_list[key], TreeItemCollapsibleState.Expanded); + item.description = jsonData.subsystem[key].description; + arr.push(item); + } + /* + let item1: TreeItem = new TreeItem("CPU", TreeItemCollapsibleState.Expanded); + item1.description = "Linux CPU子系统观测工具集"; + arr.push(item1); + + let item2: TreeItem = new TreeItem("network", TreeItemCollapsibleState.Expanded); + item2.description = "Linux 网络子系统观测工具集"; + arr.push(item2); + + let item3: TreeItem = new TreeItem("memory", TreeItemCollapsibleState.Expanded); + item3.description = "Linux 内存子系统观测工具集"; + arr.push(item3); + + let item4: TreeItem = new TreeItem("system_diagnosis", TreeItemCollapsibleState.Expanded); + item4.description = "Linux 系统诊断工具集"; + arr.push(item4); + + let item5: TreeItem = new TreeItem("hypervisior", TreeItemCollapsibleState.Expanded); + item5.description = "Linux 虚拟化子系统工具集"; + arr.push(item5); + */ + + return arr; + } + // treeview 子节点 + else { + //for (let sub_key = 0; sub_key <= 5; sub_key++) { + if (element.label == jsonData.subsystem_list[sub_key]) { // 遍历所有子系统 + //console.log("jsonData.subsystem_list[key] = ", jsonData.subsystem_list[sub_key]); + for (const tool_num in jsonData.subsystem[sub_key].tools) { // 遍历子系统下的所有工具 + let tool_name = jsonData.subsystem[sub_key].tools[tool_num].name; + let tool_description = jsonData.subsystem[sub_key].tools[tool_num].description; + let item1:TreeItem = new TreeItem(tool_name, TreeItemCollapsibleState.None); + item1.description = tool_description; + let tool_command = { + title: tool_name, + command: 'itemClick', + tooltip: "点击将呈现工具的grafana可视化面板", + arguments: [ + tool_name + ] + } + item1.command = tool_command; + arr.push(item1); + } + sub_key++; // key + 1, 匹配下一个子系统 + //console.log("sub_key = ", sub_key); + return arr; + } + else { + return null; + } + } + } + /* + if (element.label == 'CPU') { + // ***************************************************************************** + //let item1: TreeItem = new TreeItem("cpu_watcher", TreeItemCollapsibleState.None); + let cpu_watcher_label = "cpu_watcher"; + let item1:TreeItem = new TreeItem(cpu_watcher_label, TreeItemCollapsibleState.None); + item1.description = "cpu观测"; + let command_cpu_watcher = { + title: cpu_watcher_label, + command: 'itemClick', + tooltip: "点击将呈现cpu_watcher的grafana的可视化面板", + arguments: [ + cpu_watcher_label + ] + } + item1.command = command_cpu_watcher; + arr.push(item1); + // ***************************************************************************** + let proc_iamge_label = "proc_image"; + let item2: TreeItem = new TreeItem(proc_iamge_label, TreeItemCollapsibleState.None); + item2.description = "进程画像"; + let command_proc_image = { + title: proc_iamge_label, + command: 'itemClick', + tooltip: "点击将呈现proc_image的grafana的可视化面板", + arguments: [ + proc_iamge_label + ] + } + item2.command = command_proc_image; + arr.push(item2); + // ***************************************************************************** + return arr; + } + else if (element.label == 'network') { + // ***************************************************************************** + let net_watcher_label = "net_watcher"; + let network_item1: TreeItem = new TreeItem(net_watcher_label, TreeItemCollapsibleState.None); + network_item1.description = "网络观测"; + let command_net_watcher = { + title: net_watcher_label, + command: 'itemClick', + tooltip: "点击将呈现net_watcher的grafana的可视化面板", + arguments: [ + net_watcher_label + ] + } + network_item1.command = command_net_watcher; + arr.push(network_item1); + // ***************************************************************************** + let net_manager_label = "net_manager"; + let network_item2: TreeItem = new TreeItem(net_manager_label, TreeItemCollapsibleState.None); + network_item2.description = "网络管理"; + let command_net_manager = { + title: net_manager_label, + command: 'itemClick', + tooltip: "点击将呈现net_manager的grafana的可视化面板", + arguments: [ + net_manager_label + ] + } + network_item2.command = command_net_manager; + arr.push(network_item2); + // ***************************************************************************** + return arr; + } + else if (element.label == 'memory') { + let mem_watcher_label = "mem_watcher"; + let memory_item1: TreeItem = new TreeItem(mem_watcher_label, TreeItemCollapsibleState.None); + memory_item1.description = "内存观测"; + let command_mem_watcher = { + title: mem_watcher_label, + command: 'itemClick', + tooltip: "点击将呈现mem_watcher的grafana的可视化面板", + arguments: [ + mem_watcher_label + ] + } + memory_item1.command = command_mem_watcher; + arr.push(memory_item1); + + return arr; + } + else if (element.label == 'system_diagnosis') { + let stack_analyzer_label = "stack_analyzer"; + let system_diagnosis_item1: TreeItem = new TreeItem(stack_analyzer_label, TreeItemCollapsibleState.None); + system_diagnosis_item1.description = "栈调用分析器"; + let command_stack_analyzer = { + title: stack_analyzer_label, + command: 'itemClick', + tooltip: "点击将呈现stack_analyzer的grafana的可视化面板", + arguments: [ + stack_analyzer_label + ] + } + system_diagnosis_item1.command = command_stack_analyzer; + arr.push(system_diagnosis_item1); + + return arr; + } + else if (element.label == 'hypervisior') { + let kvm_watcher_label = "kvm_watcher"; + let hypervisior_item1: TreeItem = new TreeItem(kvm_watcher_label, TreeItemCollapsibleState.None); + hypervisior_item1.description = "kvm虚拟化"; + let command_kvm_watcher = { + title: kvm_watcher_label, + command: 'itemClick', + tooltip: "点击将呈现kvm_watcher的grafana的可视化面板", + arguments: [ + kvm_watcher_label + ] + } + hypervisior_item1.command = command_kvm_watcher; + arr.push(hypervisior_item1); + + return arr; + } + else { + return null; + } + + } + } + */ + public static initTreeViewItem(){ + const treeViewProvider = new TreeViewProvider(); + window.registerTreeDataProvider('lmp_visualization.panel',treeViewProvider); + } + +} + + +export function readLmpConfig() { + let data:any; + if (fs.existsSync(default_tool_config_file)) //判断是否存在此文件 + { + try { + //读取文件内容,并转化为Json对象 + + data = JSON.parse(fs.readFileSync(default_tool_config_file, "utf8")); + // console.log(jsonData); + //获取Json里key为data的数据 + //const data = userBugsJson['data']; + } + catch (error){ + console.error('Error parsing JSON:', error); + } + } + else { + console.error("no config file"); + let config_search_info =" json配置文件不存在,请检查!" + vscode.window.showErrorMessage(config_search_info); + } + return data; +} + +// fzy end +// --------------------------------------------------------------------------------- + +// backup +/** + export class TreeViewProvider implements TreeDataProvider { + onDidChangeTreeData?: Event | undefined; + + getTreeItem(element: TreeItemNode): TreeItem | Thenable { + return element; + } + getChildren(element?: TreeItem | undefined): ProviderResult { + let arr: TreeItem[] = new Array(); + // treeview 根节点 + if (element == undefined) { + let item1: TreeItem = new TreeItem("CPU", TreeItemCollapsibleState.Expanded); + item1.description = "Linux CPU子系统观测工具集"; + arr.push(item1); + + let item2: TreeItem = new TreeItem("network", TreeItemCollapsibleState.Expanded); + item2.description = "Linux 网络子系统观测工具集"; + arr.push(item2); + + let item3: TreeItem = new TreeItem("memory", TreeItemCollapsibleState.Expanded); + item3.description = "Linux 内存子系统观测工具集"; + arr.push(item3); + + let item4: TreeItem = new TreeItem("system_diagnosis", TreeItemCollapsibleState.Expanded); + item4.description = "Linux 系统诊断工具集"; + arr.push(item4); + + let item5: TreeItem = new TreeItem("hypervisior", TreeItemCollapsibleState.Expanded); + item5.description = "Linux 虚拟化子系统工具集"; + arr.push(item5); + + return arr; + } + // treeview 子节点 + else { + if (element.label == 'CPU') { + //let item1: TreeItem = new TreeItem("cpu_watcher", TreeItemCollapsibleState.None); + let item1:TreeItemNode = new TreeItemNode("cpu_watcher", TreeItemCollapsibleState.None); + item1.description = "cpu观测"; + arr.push(item1); + + let item2: TreeItemNode = new TreeItemNode("proc_image", TreeItemCollapsibleState.None); + item2.description = "进程画像"; + arr.push(item2); + + return arr; + } + else if (element.label == 'network') { + let network_item1: TreeItemNode = new TreeItemNode("net_watcher", TreeItemCollapsibleState.None); + network_item1.description = "网络观测"; + + //let command = { + //title: 'net_watcher', + //command: 'itemClick', + //tooltip: "点击将呈现net_watcher的grafana的可视化面板", + //arguments: [ + //] + //} + //network_item1.command = command; + + arr.push(network_item1); + + let network_item2: TreeItemNode = new TreeItemNode("net_manager", TreeItemCollapsibleState.None); + network_item2.description = "网络管理"; + arr.push(network_item2); + + return arr; + } + else if (element.label == 'memory') { + let memory_item1: TreeItemNode = new TreeItemNode("mem_watcher", TreeItemCollapsibleState.None); + memory_item1.description = "内存观测"; + arr.push(memory_item1); + + return arr; + } + else if (element.label == 'system_diagnosis') { + let system_diagnosis_item1: TreeItemNode = new TreeItemNode("stack_analyzer", TreeItemCollapsibleState.None); + system_diagnosis_item1.description = "栈调用分析器"; + arr.push(system_diagnosis_item1); + + return arr; + } + else if (element.label == 'hypervisior') { + let hypervisior_item1: TreeItemNode = new TreeItemNode("kvm_watcher", TreeItemCollapsibleState.None); + hypervisior_item1.description = "kvm虚拟化"; + arr.push(hypervisior_item1); + + return arr; + } + else { + return null; + } + } + } + public static initTreeViewItem(){ + const treeViewProvider = new TreeViewProvider(); + window.registerTreeDataProvider('lmp_visualization.panel',treeViewProvider); + } + +} + */ diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/middleware.ts b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/middleware.ts new file mode 100644 index 000000000..43b5aa00a --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/middleware.ts @@ -0,0 +1,16 @@ +import { Response, Request, NextFunction } from "express"; + +export function detectRequestSource( + req: Request, + res: Response, + next: NextFunction, +) { + const userAgent = req.headers["user-agent"]; + + if ((userAgent?.includes("Code") || userAgent?.includes("code")) + && userAgent?.includes("Electron")) { + next(); + } else { + res.status(403).send("Access Denied"); + } +} diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/server.ts b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/server.ts new file mode 100644 index 000000000..9f618c3ee --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/server.ts @@ -0,0 +1,261 @@ +import * as express from "express"; +import { Server, createServer } from "http"; +import { createProxyServer } from "http-proxy"; +import * as fs from "fs"; +import * as vscode from "vscode"; +import * as cors from "cors"; +import { detectRequestSource } from "./middleware"; +import axios from "axios"; +import * as path from "path"; +import * as util from "./util"; + +export let port = 0; + +let server: Server; + +export const TOKEN_SECRET = "grafana-vscode.token"; + +export async function startServer(secrets: vscode.SecretStorage, extensionPath: string) { + const settings = vscode.workspace.getConfiguration("grafana-vscode"); + const token = await secrets.get(TOKEN_SECRET); + let URL = String(settings.get("URL")); + if (URL.slice(-1) === "/") { + URL = URL.slice(0, -1); + } + + const corsOptions = { + origin: `http://localhost:${port}`, + optionsSuccessStatus: 200, + }; + + const app = express(); + app.use(detectRequestSource); + server = createServer(app); + + const proxy = createProxyServer({ + target: URL, + changeOrigin: !URL.includes("localhost"), + ws: true, + headers: { + // eslint-disable-next-line @typescript-eslint/naming-convention + Authorization: `Bearer ${token}`, + // eslint-disable-next-line @typescript-eslint/naming-convention + 'User-Agent': util.getUserAgent(), + }, + }); + + server.on("upgrade", function (req, socket, head) { + proxy.ws(req, socket, head, {}); + }); + + const sendErrorPage = (res: express.Response, message: string) => { + const errorFile = path.join(extensionPath, "public/error.html"); + let content = fs.readFileSync(errorFile, "utf-8"); + content = content.replaceAll("${error}", message); + res.write(content); + }; + + /* + * Note, this method avoids using `proxy.web`, implementing its own proxy + * event using Axios. This is because Grafana returns `X-Frame-Options: deny` + * which breaks our ability to place Grafana inside an iframe. `http-proxy` + * will not remove that header once it is added. Therefore we need a different + * form of proxy. + * + * This security protection does not apply to this situation - given we own + * both the connection to the backend as well as the webview. Therefore + * it is reasonable remove this header in this context. + * + * This method also doubles as connection verification. If an issue is + * encountered connecting to Grafana, rather than reporting an HTTP error, + * it returns an alternate HTML page to the user explaining the error, and + * offering a "refresh" option. + */ + app.get("/d/:uid/:slug", async function (req, res) { + try { + const resp = await axios.get(URL + req.url, { + maxRedirects: 5, + headers: { + // eslint-disable-next-line @typescript-eslint/naming-convention + Authorization: `Bearer ${token}`, + // eslint-disable-next-line @typescript-eslint/naming-convention + 'User-Agent': util.getUserAgent(), + }, + }); + res.write(resp.data); + } catch (e) { + let msg = ""; + if (URL === "") { + msg += "

Error: URL is not defined

"; + } + if (token === "") { + msg += "

Warning: No service account token specified.

"; + } + if (axios.isAxiosError(e)) { + if (e.response?.status === 302) { + sendErrorPage(res, msg+ "

Authentication error

"); + } else { + sendErrorPage(res, msg + `

${e.message}

`); + } + } else if (e instanceof Error) { + sendErrorPage(res, msg + `

${e.message}

`); + } else { + sendErrorPage(res, msg + "

" + String(e) + "

"); + } + } + }); + + app.get( + "/api/dashboards/uid/:uid", + express.json(), + cors(corsOptions), + (req, res) => { + const refererParams = new URLSearchParams(req.headers.referer); + const filename = refererParams.get("filename"); + if (filename === null) { + console.log("Filename not specified in referer"); + res.sendStatus(500); + return; + } + fs.readFile(filename, "utf-8", (err, data) => { + if (err) { + console.error("Error reading file:", err); + res.sendStatus(500); + return; + } + const dash: any = JSON.parse(data); + const wrapper = { + dashboard: dash, + meta: { + isStarred: false, + folderId: 0, + folderUid: "", + url: `/d/${dash.uid}/slug`, + }, + }; + + res.send(wrapper); + }); + }, + ); + + app.post( + "/api/dashboards/db/", + express.json(), + cors(corsOptions), + (req, res) => { + const refererParams = new URLSearchParams(req.headers.referer); + const filename = refererParams.get("filename"); + if (!filename) { + res.send(500); + return; + } + const uid = req.headers.referer?.split("/")[4]; + const jsonData = JSON.stringify(req.body.dashboard, null, 2); + + fs.writeFile(filename, jsonData, "utf-8", (err) => { + if (err) { + console.error("Error writing file:", err); + res.sendStatus(500); + } else { + res.send({ + id: 1, + slug: "slug", + status: "success", + uid: uid, + url: `/d/${uid}/slug`, + version: 1, + }); + } + }); + }, + ); + + app.get( + "/api/access-control/user/actions", + express.json(), + cors(corsOptions), + (req, res) => { + res.send({ + /* eslint-disable-next-line @typescript-eslint/naming-convention */ + "dashboards:write": true, + }); + return; + }, + ); + + const mustProxyGET = [ + "/public/*", + "/api/datasources/proxy/*", + "/api/datasources/*", + "/api/plugins/*", + ]; + for (const path of mustProxyGET) { + app.get(path, function (req, res) { + proxy.web(req, res, {}); + }); + } + + const mustProxyPOST = [ + "/api/ds/query", + "/api/datasources/proxy/*", + ]; + for (const path of mustProxyPOST) { + app.post(path, function (req, res) { + proxy.web(req, res, {}); + }); + } + + const blockJSONget: { [name: string]: any } = { + /* eslint-disable @typescript-eslint/naming-convention */ + "/api/ma/events": [], + "/api/live/publish": [], + "/api/live/list": [], + "/api/user/orgs": [], + "/api/annotations": [], + "/api/search": [], + "/api/usage/*": [], + "/api/prometheus/grafana/api/v1/rules": { + status: "success", + data: { groups: [] }, + }, + "/avatar/*": "", + "/api/folders": [], + /* eslint-enable @typescript-eslint/naming-convention */ + }; + for (const path in blockJSONget) { + app.get(path, function (req, res) { + res.send(blockJSONget[path]); + }); + } + + const blockJSONpost: { [name: string]: any } = { + /* eslint-disable @typescript-eslint/naming-convention */ + "/api/frontend-metrics": [], + "/api/search-v2": [], + "/api/live/publish": {}, + /* eslint-enable @typescript-eslint/naming-convention */ + }; + for (const path in blockJSONpost) { + app.post(path, function (req, res) { + res.send(blockJSONpost[path]); + }); + } + + server.listen(port, () => { + //@ts-expect-error + port = server?.address()?.port; + console.log("Server started"); + }); +} + +export function restartServer(secrets: vscode.SecretStorage, extensionPath: string) { + console.log("Restarting server"); + stopServer(); + startServer(secrets, extensionPath); +} +export function stopServer() { + if (server) { + server.close(); + } +} diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/telemetry.ts b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/telemetry.ts new file mode 100644 index 000000000..b6bcc7854 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/telemetry.ts @@ -0,0 +1,88 @@ +import * as vscode from "vscode"; +import axios from "axios"; +import {v4 as uuidv4} from 'uuid'; +import * as util from "./util"; + +const LAST_UPDATED_DATE = "lastUpdatedDate"; +const INSTALLATION_DATE = "installDate"; +const INSTALLATION_UUID = "installUUID"; +const RECENT_VIEWS = "recentViews"; + +const URL = "https://stats.grafana.org/vscode-usage-report"; + +/* + * Sends a single anonymous telemetry call once per day, allowing tracking of + * usage - reports on first opening of a dashboard each day. + */ +export async function sendTelemetry(ctx: vscode.ExtensionContext) { + + const settings = vscode.workspace.getConfiguration("grafana-vscode"); + const enableTelemetry = settings.get("telemetry"); + if (!enableTelemetry) { + return; + } + const lastUpdatedDate = ctx.globalState.get(LAST_UPDATED_DATE); + const today = new Date(); + + if (lastUpdatedDate === undefined) { + const uuid = uuidv4(); + await sendEvent("first", uuid, today.toISOString(), 1); + ctx.globalState.update(LAST_UPDATED_DATE, today); + ctx.globalState.update(INSTALLATION_UUID, uuid); + ctx.globalState.update(INSTALLATION_DATE, today); + ctx.globalState.update(RECENT_VIEWS, 0); + } else { + let recentViews = ctx.globalState.get(RECENT_VIEWS); + recentViews = (recentViews === undefined) ? 1 : recentViews+1; + + if (differentDay(new Date(lastUpdatedDate), today)) { + let uuid = ctx.globalState.get(INSTALLATION_UUID); + let installDate = ctx.globalState.get(INSTALLATION_DATE); + if (uuid === undefined) { + console.log("UUID undefined. Shouldn't happen."); + uuid = uuidv4(); + ctx.globalState.update(INSTALLATION_UUID, uuid); + } + if (installDate === undefined) { + console.log("Install date undefined. Shouldn't happen."); + installDate = (new Date(lastUpdatedDate)).toISOString(); + ctx.globalState.update(INSTALLATION_DATE, installDate); + } + await sendEvent("subsequent", uuid as string, installDate as string, recentViews); + ctx.globalState.update(LAST_UPDATED_DATE, today); + recentViews = 0; + } + ctx.globalState.update(RECENT_VIEWS, recentViews); + } +} + +function differentDay(d1: Date, d2: Date) { + return d1.getDate() !== d2.getDate() || + d1.getMonth() !== d2.getMonth() || + d1.getFullYear() !== d2.getFullYear(); +} + +async function sendEvent(eventType: string, uuid: string, installDate: string, views: number | undefined) { + try { + const data = { + uuid: uuid, + eventType: eventType, + timestamp: Date(), + createdAt: installDate, + os: process.platform, + arch: process.arch, + packaging: "unknown", + views: views, + version: util.getVersion(), + }; + + await axios.post(URL, data, { + headers: { + // eslint-disable-next-line @typescript-eslint/naming-convention + 'User-Agent': util.getUserAgent(), + }, + }); + } catch(e) { + console.log("Telemetry error", e, "for event", eventType); + } +} \ No newline at end of file diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/test/runTest.ts b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/test/runTest.ts new file mode 100644 index 000000000..8a6ab0e15 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/test/runTest.ts @@ -0,0 +1,23 @@ +import * as path from "path"; + +import { runTests } from "@vscode/test-electron"; + +async function main() { + try { + // The folder containing the Extension Manifest package.json + // Passed to `--extensionDevelopmentPath` + const extensionDevelopmentPath = path.resolve(__dirname, "../../"); + + // The path to test runner + // Passed to --extensionTestsPath + const extensionTestsPath = path.resolve(__dirname, "./suite/index"); + + // Download VS Code, unzip it and run the integration test + await runTests({ extensionDevelopmentPath, extensionTestsPath }); + } catch (err) { + console.error("Failed to run tests", err); + process.exit(1); + } +} + +main(); diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/test/suite/extension.test.ts b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/test/suite/extension.test.ts new file mode 100644 index 000000000..2f671d3c7 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/test/suite/extension.test.ts @@ -0,0 +1,15 @@ +import * as assert from "assert"; + +// You can import and use all API from the 'vscode' module +// as well as import your extension to test it +import * as vscode from "vscode"; +// import * as myExtension from '../../extension'; + +suite("Extension Test Suite", () => { + vscode.window.showInformationMessage("Start all tests."); + + test("Sample test", () => { + assert.strictEqual(-1, [1, 2, 3].indexOf(5)); + assert.strictEqual(-1, [1, 2, 3].indexOf(0)); + }); +}); diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/test/suite/index.ts b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/test/suite/index.ts new file mode 100644 index 000000000..2cb7d7d8b --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/test/suite/index.ts @@ -0,0 +1,38 @@ +import * as path from "path"; +import * as Mocha from "mocha"; +import * as glob from "glob"; + +export function run(): Promise { + // Create the mocha test + const mocha = new Mocha({ + ui: "tdd", + color: true, + }); + + const testsRoot = path.resolve(__dirname, ".."); + + return new Promise((c, e) => { + glob("**/**.test.js", { cwd: testsRoot }, (err, files) => { + if (err) { + return e(err); + } + + // Add files to the test suite + files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f))); + + try { + // Run the mocha test + mocha.run((failures) => { + if (failures > 0) { + e(new Error(`${failures} tests failed.`)); + } else { + c(); + } + }); + } catch (err) { + console.error(err); + e(err); + } + }); + }); +} diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/util.ts b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/util.ts new file mode 100644 index 000000000..55ef02df8 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/util.ts @@ -0,0 +1,15 @@ +let userAgent: string; +let version: string; + +export function setVersion(v: string) { + version = v; + userAgent = `Grafana VSCode Extension/v${version}`; +} + +export function getVersion(): string { + return version; +} + +export function getUserAgent(): string { + return userAgent; +} \ No newline at end of file diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/tsconfig.json b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/tsconfig.json new file mode 100644 index 000000000..708b31212 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "ES2021", + "lib": ["ES2021"], + "sourceMap": true, + "rootDir": "src", + "strict": true /* enable all strict type-checking options */ + /* Additional Checks */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + } +} diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/webpack.config.js b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/webpack.config.js new file mode 100644 index 000000000..e6ed48c6e --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/webpack.config.js @@ -0,0 +1,48 @@ +//@ts-check + +"use strict"; + +const path = require("path"); + +//@ts-check +/** @typedef {import('webpack').Configuration} WebpackConfig **/ + +/** @type WebpackConfig */ +const extensionConfig = { + target: "node", // VS Code extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/ + mode: "none", // this leaves the source code as close as possible to the original (when packaging we set this to 'production') + + entry: "./src/extension.ts", // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ + output: { + // the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/ + path: path.resolve(__dirname, "dist"), + filename: "extension.js", + libraryTarget: "commonjs2", + }, + externals: { + vscode: "commonjs vscode", // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/ + // modules added here also need to be added in the .vscodeignore file + }, + resolve: { + // support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader + extensions: [".ts", ".js"], + }, + module: { + rules: [ + { + test: /\.ts$/, + exclude: /node_modules/, + use: [ + { + loader: "ts-loader", + }, + ], + }, + ], + }, + devtool: "nosources-source-map", + infrastructureLogging: { + level: "log", // enables logging required for problem matchers + }, +}; +module.exports = [extensionConfig]; diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/yarn.lock b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/yarn.lock new file mode 100644 index 000000000..0d87b08ab --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/yarn.lock @@ -0,0 +1,2811 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + +"@discoveryjs/json-ext@^0.5.0": + version "0.5.7" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" + integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== + +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": + version "4.10.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" + integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== + +"@eslint/eslintrc@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.2.tgz#c6936b4b328c64496692f76944e755738be62396" + integrity sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.52.0": + version "8.52.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.52.0.tgz#78fe5f117840f69dc4a353adf9b9cd926353378c" + integrity sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA== + +"@humanwhocodes/config-array@^0.11.13": + version "0.11.13" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.13.tgz#075dc9684f40a531d9b26b0822153c1e832ee297" + integrity sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ== + dependencies: + "@humanwhocodes/object-schema" "^2.0.1" + debug "^4.1.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz#e5211452df060fa8522b55c7b3c0c4d1981cb044" + integrity sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw== + +"@jridgewell/gen-mapping@^0.3.0": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/source-map@^0.3.3": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91" + integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.20": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@jridgewell/trace-mapping@^0.3.9": + version "0.3.20" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz#72e45707cf240fa6b081d0366f8265b0cd10197f" + integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== + +"@types/body-parser@*": + version "1.19.4" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.4.tgz#78ad68f1f79eb851aa3634db0c7f57f6f601b462" + integrity sha512-N7UDG0/xiPQa2D/XrVJXjkWbpqHCd2sBaB32ggRF2l83RhPfamgKGF8gwwqyksS95qUS5ZYF9aF+lLPRlwI2UA== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.37" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.37.tgz#c66a96689fd3127c8772eb3e9e5c6028ec1a9af5" + integrity sha512-zBUSRqkfZ59OcwXon4HVxhx5oWCJmc0OtBTK05M+p0dYjgN6iTwIL2T/WbsQZrEsdnwaF9cWQ+azOnpPvIqY3Q== + dependencies: + "@types/node" "*" + +"@types/cors@^2.8.13": + version "2.8.15" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.15.tgz#eb143aa2f8807ddd78e83cbff141bbedd91b60ee" + integrity sha512-n91JxbNLD8eQIuXDIChAN1tCKNWCEgpceU9b7ZMbFA+P+Q4yIeh80jizFLEvolRPc1ES0VdwFlGv+kJTSirogw== + dependencies: + "@types/node" "*" + +"@types/estree@^1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" + integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== + +"@types/express-serve-static-core@^4.17.33": + version "4.17.39" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.39.tgz#2107afc0a4b035e6cb00accac3bdf2d76ae408c8" + integrity sha512-BiEUfAiGCOllomsRAZOiMFP7LAnrifHpt56pc4Z7l9K6ACyN06Ns1JLMBxwkfLOjJRlSf06NwWsT7yzfpaVpyQ== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express@^4.17.17": + version "4.17.20" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.20.tgz#e7c9b40276d29e38a4e3564d7a3d65911e2aa433" + integrity sha512-rOaqlkgEvOW495xErXMsmyX3WKBInbhG5eqojXYi3cGUaLoRDlXa5d52fkfWZT963AZ3v2eZ4MbKE6WpDAGVsw== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/glob@^8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-8.1.0.tgz#b63e70155391b0584dce44e7ea25190bbc38f2fc" + integrity sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w== + dependencies: + "@types/minimatch" "^5.1.2" + "@types/node" "*" + +"@types/http-errors@*": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.3.tgz#c54e61f79b3947d040f150abd58f71efb422ff62" + integrity sha512-pP0P/9BnCj1OVvQR2lF41EkDG/lWWnDyA203b/4Fmi2eTyORnBtcDoKDwjWQthELrBvWkMOrvSOnZ8OVlW6tXA== + +"@types/http-proxy@^1.17.10", "@types/http-proxy@^1.17.8": + version "1.17.13" + resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.13.tgz#dd3a4da550580eb0557d4c7128a2ff1d1a38d465" + integrity sha512-GkhdWcMNiR5QSQRYnJ+/oXzu0+7JJEPC8vkWXK351BkhjraZF+1W13CUYARUvX9+NqIU2n6YHA4iwywsc/M6Sw== + dependencies: + "@types/node" "*" + +"@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": + version "7.0.14" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.14.tgz#74a97a5573980802f32c8e47b663530ab3b6b7d1" + integrity sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw== + +"@types/mime@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.3.tgz#886674659ce55fe7c6c06ec5ca7c0eb276a08f91" + integrity sha512-i8MBln35l856k5iOhKk2XJ4SeAWg75mLIpZB4v6imOagKL6twsukBZGDMNhdOVk7yRFTMPpfILocMos59Q1otQ== + +"@types/mime@^1": + version "1.3.4" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.4.tgz#a4ed836e069491414bab92c31fdea9e557aca0d9" + integrity sha512-1Gjee59G25MrQGk8bsNvC6fxNiRgUlGn2wlhGf95a59DrprnnHk80FIMMFG9XHMdrfsuA119ht06QPDXA1Z7tw== + +"@types/minimatch@^5.1.2": + version "5.1.2" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" + integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== + +"@types/mocha@^10.0.1": + version "10.0.3" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.3.tgz#4804fe9cd39da26eb62fa65c15ea77615a187812" + integrity sha512-RsOPImTriV/OE4A9qKjMtk2MnXiuLLbcO3nCXK+kvq4nr0iMfFgpjaX3MPLb6f7+EL1FGSelYvuJMV6REH+ZPQ== + +"@types/node@*": + version "20.8.10" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.8.10.tgz#a5448b895c753ae929c26ce85cab557c6d4a365e" + integrity sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w== + dependencies: + undici-types "~5.26.4" + +"@types/node@16.x": + version "16.18.60" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.60.tgz#0b0f4316906f1bd0e03b640363f67bd4e86958bd" + integrity sha512-ZUGPWx5vKfN+G2/yN7pcSNLkIkXEvlwNaJEd4e0ppX7W2S8XAkdc/37hM4OUNJB9sa0p12AOvGvxL4JCPiz9DA== + +"@types/qs@*": + version "6.9.9" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.9.tgz#66f7b26288f6799d279edf13da7ccd40d2fa9197" + integrity sha512-wYLxw35euwqGvTDx6zfY1vokBFnsK0HNrzc6xNHchxfO2hpuRg74GbkEW7e3sSmPvj0TjCDT1VCa6OtHXnubsg== + +"@types/range-parser@*": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.6.tgz#7cb33992049fd7340d5b10c0098e104184dfcd2a" + integrity sha512-+0autS93xyXizIYiyL02FCY8N+KkKPhILhcUSA276HxzreZ16kl+cmwvV2qAM/PuCCwPXzOXOWhiPcw20uSFcA== + +"@types/semver@^7.3.12": + version "7.5.4" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.4.tgz#0a41252ad431c473158b22f9bfb9a63df7541cff" + integrity sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ== + +"@types/send@*": + version "0.17.3" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.3.tgz#81b2ea5a3a18aad357405af2d643ccbe5a09020b" + integrity sha512-/7fKxvKUoETxjFUsuFlPB9YndePpxxRAOfGC/yJdc9kTjTeP5kRCTzfnE8kPUKCeyiyIZu0YQ76s50hCedI1ug== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/serve-static@*": + version "1.15.4" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.4.tgz#44b5895a68ca637f06c229119e1c774ca88f81b2" + integrity sha512-aqqNfs1XTF0HDrFdlY//+SGUxmdSUbjeRXb5iaZc3x0/vMbYmdw9qvOgHWOyyLFxSSRnUuP5+724zBgfw8/WAw== + dependencies: + "@types/http-errors" "*" + "@types/mime" "*" + "@types/node" "*" + +"@types/source-map-support@^0.5.8": + version "0.5.9" + resolved "https://registry.yarnpkg.com/@types/source-map-support/-/source-map-support-0.5.9.tgz#750860ba0dc3eb2929a940ac630e6ef60407f6b6" + integrity sha512-91Jf4LyPAObBTFbpW3bSDK1ncdwXohvlBmzffSj7/44SY+1mD/HhesdfspCMxPIJwllgN2G4eVFatGs4Zw/lnw== + dependencies: + source-map "^0.6.0" + +"@types/uuid@^9.0.6": + version "9.0.6" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.6.tgz#c91ae743d8344a54b2b0c691195f5ff5265f6dfb" + integrity sha512-BT2Krtx4xaO6iwzwMFUYvWBWkV2pr37zD68Vmp1CDV196MzczBRxuEpD6Pr395HAgebC/co7hOphs53r8V7jew== + +"@types/vscode@^1.76.0": + version "1.83.1" + resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.83.1.tgz#fe51b3d913a5c5b265a622179ae4ab6c0af7d54e" + integrity sha512-BHu51NaNKOtDf3BOonY3sKFFmZKEpRkzqkZVpSYxowLbs5JqjOQemYFob7Gs5rpxE5tiGhfpnMpcdF/oKrLg4w== + +"@typescript-eslint/eslint-plugin@^5.53.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" + integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== + dependencies: + "@eslint-community/regexpp" "^4.4.0" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/type-utils" "5.62.0" + "@typescript-eslint/utils" "5.62.0" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.0" + natural-compare-lite "^1.4.0" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/parser@^5.53.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7" + integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== + dependencies: + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" + integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + +"@typescript-eslint/type-utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" + integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew== + dependencies: + "@typescript-eslint/typescript-estree" "5.62.0" + "@typescript-eslint/utils" "5.62.0" + debug "^4.3.4" + tsutils "^3.21.0" + +"@typescript-eslint/types@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" + integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== + +"@typescript-eslint/typescript-estree@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" + integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" + integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" + eslint-scope "^5.1.1" + semver "^7.3.7" + +"@typescript-eslint/visitor-keys@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" + integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== + dependencies: + "@typescript-eslint/types" "5.62.0" + eslint-visitor-keys "^3.3.0" + +"@ungap/structured-clone@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + +"@vscode/test-electron@^2.2.3": + version "2.3.6" + resolved "https://registry.yarnpkg.com/@vscode/test-electron/-/test-electron-2.3.6.tgz#61a6ec1b4bdc9a2a694a9d7d86b9cb8b82c5a116" + integrity sha512-M31xGH0RgqNU6CZ4/9g39oUMJ99nLzfjA+4UbtIQ6TcXQ6+2qkjOOxedmPBDDCg26/3Al5ubjY80hIoaMwKYSw== + dependencies: + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + jszip "^3.10.1" + semver "^7.5.2" + +"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.12.1.tgz#bb16a0e8b1914f979f45864c23819cc3e3f0d4bb" + integrity sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + +"@webassemblyjs/floating-point-hex-parser@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz#dacbcb95aff135c8260f77fa3b4c5fea600a6431" + integrity sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw== + +"@webassemblyjs/helper-api-error@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768" + integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== + +"@webassemblyjs/helper-buffer@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz#6df20d272ea5439bf20ab3492b7fb70e9bfcb3f6" + integrity sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw== + +"@webassemblyjs/helper-numbers@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz#cbce5e7e0c1bd32cf4905ae444ef64cea919f1b5" + integrity sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9" + integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== + +"@webassemblyjs/helper-wasm-section@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz#3da623233ae1a60409b509a52ade9bc22a37f7bf" + integrity sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/wasm-gen" "1.12.1" + +"@webassemblyjs/ieee754@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz#bb665c91d0b14fffceb0e38298c329af043c6e3a" + integrity sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.6.tgz#70e60e5e82f9ac81118bc25381a0b283893240d7" + integrity sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" + integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== + +"@webassemblyjs/wasm-edit@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz#9f9f3ff52a14c980939be0ef9d5df9ebc678ae3b" + integrity sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/helper-wasm-section" "1.12.1" + "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/wasm-opt" "1.12.1" + "@webassemblyjs/wasm-parser" "1.12.1" + "@webassemblyjs/wast-printer" "1.12.1" + +"@webassemblyjs/wasm-gen@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz#a6520601da1b5700448273666a71ad0a45d78547" + integrity sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wasm-opt@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz#9e6e81475dfcfb62dab574ac2dda38226c232bc5" + integrity sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/wasm-parser" "1.12.1" + +"@webassemblyjs/wasm-parser@1.12.1", "@webassemblyjs/wasm-parser@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz#c47acb90e6f083391e3fa61d113650eea1e95937" + integrity sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-api-error" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wast-printer@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz#bcecf661d7d1abdaf989d8341a4833e33e2b31ac" + integrity sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@xtuc/long" "4.2.2" + +"@webpack-cli/configtest@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-2.1.1.tgz#3b2f852e91dac6e3b85fb2a314fb8bef46d94646" + integrity sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw== + +"@webpack-cli/info@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-2.0.2.tgz#cc3fbf22efeb88ff62310cf885c5b09f44ae0fdd" + integrity sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A== + +"@webpack-cli/serve@^2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e" + integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-import-attributes@^1.9.5: + version "1.9.5" + resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef" + integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: + version "8.11.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.2.tgz#ca0d78b51895be5390a5903c5b3bdcdaf78ae40b" + integrity sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w== + +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv@^6.12.4, ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-colors@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +asap@~2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +axios@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.4.tgz#4c8ded1b43683c8dd362973c393f3ede24052aa2" + integrity sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +body-parser@1.20.3: + version "1.20.3" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6" + integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== + dependencies: + bytes "3.1.2" + content-type "~1.0.5" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.13.0" + raw-body "2.5.2" + type-is "~1.6.18" + unpipe "1.0.0" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.3, braces@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +browserslist@^4.21.10: + version "4.23.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.3.tgz#debb029d3c93ebc97ffbc8d9cbb03403e227c800" + integrity sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA== + dependencies: + caniuse-lite "^1.0.30001646" + electron-to-chromium "^1.5.4" + node-releases "^2.0.18" + update-browserslist-db "^1.1.0" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +call-bind@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" + integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== + dependencies: + function-bind "^1.1.2" + get-intrinsic "^1.2.1" + set-function-length "^1.1.1" + +call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase@^6.0.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +caniuse-lite@^1.0.30001646: + version "1.0.30001655" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001655.tgz#0ce881f5a19a2dcfda2ecd927df4d5c1684b982f" + integrity sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg== + +chalk@^4.0.0, chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chokidar@3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chrome-trace-event@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" + integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colorette@^2.0.14: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@~1.0.4, content-type@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" + integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cors@^2.8.5: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@4, debug@4.3.4, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +decamelize@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +define-data-property@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" + integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== + dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +diff@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" + integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +electron-to-chromium@^1.5.4: + version "1.5.13" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz#1abf0410c5344b2b829b7247e031f02810d442e6" + integrity sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q== + +emitter-component@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/emitter-component/-/emitter-component-1.1.1.tgz#065e2dbed6959bf470679edabeaf7981d1003ab6" + integrity sha512-G+mpdiAySMuB7kesVRLuyvYRqDmshB7ReKEVuyBPkzQlmiDiLrt7hHHIy4Aff552bgknVN7B2/d3lzhGO5dvpQ== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +encodeurl@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + +enhanced-resolve@^5.0.0, enhanced-resolve@^5.17.1: + version "5.17.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" + integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +envinfo@^7.7.3: + version "7.11.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.11.0.tgz#c3793f44284a55ff8c82faf1ffd91bc6478ea01f" + integrity sha512-G9/6xF1FPbIw0TtalAMaVPpiq2aDEuKLXM314jPVAO9r2fo2a4BLqMNkmRS7O/xPPZ+COAhGIz3ETvHEV3eUcg== + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-module-lexer@^1.2.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.3.1.tgz#c1b0dd5ada807a3b3155315911f364dc4e909db1" + integrity sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q== + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escalade@^3.1.2: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-scope@5.1.1, eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint@^8.34.0: + version "8.52.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.52.0.tgz#d0cd4a1fac06427a61ef9242b9353f36ea7062fc" + integrity sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.2" + "@eslint/js" "8.52.0" + "@humanwhocodes/config-array" "^0.11.13" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +events@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +express@^4.20.0: + version "4.20.0" + resolved "https://registry.yarnpkg.com/express/-/express-4.20.0.tgz#f1d08e591fcec770c07be4767af8eb9bcfd67c48" + integrity sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.3" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.6.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~2.0.0" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.3" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.10" + proxy-addr "~2.0.7" + qs "6.11.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.19.0" + serve-static "1.16.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.2.9: + version "3.3.1" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4" + integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastest-levenshtein@^1.0.12: + version "1.0.16" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + +fastq@^1.6.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== + dependencies: + reusify "^1.0.4" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + +find-up@5.0.0, find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +flat-cache@^3.0.4: + version "3.1.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.1.1.tgz#a02a15fdec25a8f844ff7cc658f03dd99eb4609b" + integrity sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q== + dependencies: + flatted "^3.2.9" + keyv "^4.5.3" + rimraf "^3.0.2" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +flatted@^3.2.9: + version "3.2.9" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" + integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== + +follow-redirects@^1.0.0, follow-redirects@^1.15.6: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" + integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== + dependencies: + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + +globals@^13.19.0: + version "13.23.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.23.0.tgz#ef31673c926a0976e1f61dab4dca57e0c0a8af02" + integrity sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA== + dependencies: + type-fest "^0.20.2" + +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +graceful-fs@^4.1.2, graceful-fs@^4.2.11, graceful-fs@^4.2.4: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340" + integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== + dependencies: + get-intrinsic "^1.2.2" + +has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== + dependencies: + function-bind "^1.1.2" + +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + +http-proxy-middleware@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f" + integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw== + dependencies: + "@types/http-proxy" "^1.17.8" + http-proxy "^1.18.1" + is-glob "^4.0.1" + is-plain-obj "^3.0.0" + micromatch "^4.0.2" + +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ignore@^5.2.0: + version "5.2.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== + +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== + +import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-local@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== + +interpret@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" + integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-core-module@^2.13.0: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== + dependencies: + hasown "^2.0.0" + +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-plain-obj@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + +is-plain-obj@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" + integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== + +is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +js-yaml@4.1.0, js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +jszip@^3.10.1: + version "3.10.1" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2" + integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g== + dependencies: + lie "~3.3.0" + pako "~1.0.2" + readable-stream "~2.3.6" + setimmediate "^1.0.5" + +keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +lie@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" + integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== + dependencies: + immediate "~3.0.5" + +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +log-symbols@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +merge-descriptors@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" + integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +minimatch@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +mocha@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.2.0.tgz#1fd4a7c32ba5ac372e03a17eef435bd00e5c68b8" + integrity sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg== + dependencies: + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.5.3" + debug "4.3.4" + diff "5.0.0" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "7.2.0" + he "1.2.0" + js-yaml "4.1.0" + log-symbols "4.1.0" + minimatch "5.0.1" + ms "2.1.3" + nanoid "3.3.3" + serialize-javascript "6.0.0" + strip-json-comments "3.1.1" + supports-color "8.1.1" + workerpool "6.2.1" + yargs "16.2.0" + yargs-parser "20.2.4" + yargs-unparser "2.0.0" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +nanoid@3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" + integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== + +natural-compare-lite@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" + integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +node-releases@^2.0.18: + version "2.0.18" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" + integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +object-assign@^4: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.13.1: + version "1.13.2" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" + integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== + +object-inspect@^1.9.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +open@^8.4.2: + version "8.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" + integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== + dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +pako@~1.0.2: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-to-regexp@0.1.10: + version "0.1.10" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b" + integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w== + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +picocolors@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" + integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prettier@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.0.2.tgz#78fcecd6d870551aa5547437cdae39d4701dca5b" + integrity sha512-o2YR9qtniXvwEZlOKbveKfDQVyqxbEIWn48Z8m3ZJjBjcCmUy3xZGIv+7AkaeuaTr6yPXJjwv07ZWlsWbEy1rQ== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +promise@^7.0.4: + version "7.3.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== + dependencies: + asap "~2.0.3" + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +qs@6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + +qs@6.13.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" + integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== + dependencies: + side-channel "^1.0.6" + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +readable-stream@~2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +rechoir@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" + integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== + dependencies: + resolve "^1.20.0" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve@^1.20.0: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-buffer@5.2.1, safe-buffer@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +schema-utils@^3.1.1, schema-utils@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +semver@^7.3.4, semver@^7.3.7, semver@^7.5.2: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +send@0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + +send@0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" + integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + +serialize-javascript@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== + dependencies: + randombytes "^2.1.0" + +serialize-javascript@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" + integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== + dependencies: + randombytes "^2.1.0" + +serve-static@1.16.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.0.tgz#2bf4ed49f8af311b519c46f272bf6ac3baf38a92" + integrity sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.18.0" + +set-function-length@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed" + integrity sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ== + dependencies: + define-data-property "^1.1.1" + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +side-channel@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +source-map-support@^0.5.21, source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" + integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +stream@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/stream/-/stream-0.0.2.tgz#7f5363f057f6592c5595f00bc80a27f5cec1f0ef" + integrity sha512-gCq3NDI2P35B2n6t76YJuOp7d6cN/C7Rt0577l91wllh0sY9ZBuw9KaSGqH/b0hzn3CWWJbpbW0W0WvQ1H/Q7g== + dependencies: + emitter-component "^1.1.1" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-json-comments@3.1.1, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@8.1.1, supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +tapable@^2.1.1, tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +terser-webpack-plugin@^5.3.10: + version "5.3.10" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" + integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== + dependencies: + "@jridgewell/trace-mapping" "^0.3.20" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.1" + terser "^5.26.0" + +terser@^5.26.0: + version "5.31.6" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.31.6.tgz#c63858a0f0703988d0266a82fcbf2d7ba76422b1" + integrity sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +transformer-proxy@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/transformer-proxy/-/transformer-proxy-0.3.5.tgz#40ecf87f846e8b1603a6d22b49d60d81f57ce512" + integrity sha512-mBbKwpGq45DAa4AJQU8osDL7rV065VKCEhVH7g7EwVT3oYogABso+ZwFz5XuvNVI9+tfgwk9NMKE+JL+bue/mw== + dependencies: + promise "^7.0.4" + stream "0.0.2" + util "^0.10.3" + +ts-loader@^9.4.2: + version "9.5.0" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.5.0.tgz#f0a51dda37cc4d8e43e6cb14edebbc599b0c3aa2" + integrity sha512-LLlB/pkB4q9mW2yLdFMnK3dEHbrBjeZTYguaaIfusyojBgAGf5kF+O6KcWqiGzWqHk0LBsoolrp4VftEURhybg== + dependencies: + chalk "^4.1.0" + enhanced-resolve "^5.0.0" + micromatch "^4.0.0" + semver "^7.3.4" + source-map "^0.7.4" + +tslib@^1.8.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== + dependencies: + tslib "^1.8.1" + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typescript@^4.9.5: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +update-browserslist-db@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz#7ca61c0d8650766090728046e416a8cde682859e" + integrity sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ== + dependencies: + escalade "^3.1.2" + picocolors "^1.0.1" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +util@^0.10.3: + version "0.10.4" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" + integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== + dependencies: + inherits "2.0.3" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +uuid@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +watchpack@^2.4.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.2.tgz#2feeaed67412e7c33184e5a79ca738fbd38564da" + integrity sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +webpack-cli@^5.0.1: + version "5.1.4" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.1.4.tgz#c8e046ba7eaae4911d7e71e2b25b776fcc35759b" + integrity sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg== + dependencies: + "@discoveryjs/json-ext" "^0.5.0" + "@webpack-cli/configtest" "^2.1.1" + "@webpack-cli/info" "^2.0.2" + "@webpack-cli/serve" "^2.0.5" + colorette "^2.0.14" + commander "^10.0.1" + cross-spawn "^7.0.3" + envinfo "^7.7.3" + fastest-levenshtein "^1.0.12" + import-local "^3.0.2" + interpret "^3.1.1" + rechoir "^0.8.0" + webpack-merge "^5.7.3" + +webpack-merge@^5.7.3: + version "5.10.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177" + integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== + dependencies: + clone-deep "^4.0.1" + flat "^5.0.2" + wildcard "^2.0.0" + +webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack@^5.94.0: + version "5.94.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.94.0.tgz#77a6089c716e7ab90c1c67574a28da518a20970f" + integrity sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg== + dependencies: + "@types/estree" "^1.0.5" + "@webassemblyjs/ast" "^1.12.1" + "@webassemblyjs/wasm-edit" "^1.12.1" + "@webassemblyjs/wasm-parser" "^1.12.1" + acorn "^8.7.1" + acorn-import-attributes "^1.9.5" + browserslist "^4.21.10" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.17.1" + es-module-lexer "^1.2.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.11" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.2.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.10" + watchpack "^2.4.1" + webpack-sources "^3.2.3" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wildcard@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== + +workerpool@6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" + integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yargs-parser@20.2.4: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== + +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-unparser@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== + dependencies: + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" + +yargs@16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_vscode_ext_0.01.vsix b/MagicEyes/src/visualization/vscode_ext/lmp_vscode_ext_0.01.vsix new file mode 100644 index 000000000..9aea7d416 Binary files /dev/null and b/MagicEyes/src/visualization/vscode_ext/lmp_vscode_ext_0.01.vsix differ diff --git a/MagicEyes/src/visualization/vscode_ext/tool_config_sample/lmp_tool_ext_config.json b/MagicEyes/src/visualization/vscode_ext/tool_config_sample/lmp_tool_ext_config.json new file mode 100644 index 000000000..0102d7e69 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/tool_config_sample/lmp_tool_ext_config.json @@ -0,0 +1,80 @@ +{ + "name" : "lmp_tool_vscode_extension_config", + "version" : "0.0.1", + "subsystem_list" : [ + "CPU", + "memory", + "fs", + "network", + "system_diagnosis", + "hypervisor" + ], + "subsystem" : [ + { + "description" : "Linux CPU子系统观测工具集", + "tools" : [ + { + "name": "cpu_watcher", + "description" : "cpu观测" + }, + { + "name": "proc_image", + "description" : "进程画像" + } + ] + }, + { + "description" : "Linux 内存子系统观测工具集", + "tools" : [ + { + "name": "mem_watcher", + "description" : "内存观测" + } + ] + }, + { + "description" : "Linux 文件子系统观测工具集", + "tools" : [ + { + "name": "fast_fuse", + "description" : "FUSE 性能优化" + } + ] + }, + { + "description" : "Linux 网络子系统观测工具集", + "tools" : [ + { + "name": "net_watcher", + "description" : "网络观测" + }, + { + "name": "net_manager", + "description" : "网络优化与加速" + } + ] + }, + { + "description" : "Linux 系统诊断子系统观测工具集", + "tools" : [ + { + "name": "stack_analyzer", + "description" : "栈调用分析器" + } + ] + }, + { + "description" : "Linux 虚拟化子系统观测工具集", + "tools" : [ + { + "name": "kvm_watcher", + "description" : "kvm状态分析" + } + ] + } + ] +} + + + + diff --git a/eBPF_Supermarket/Auto_Cluster_Deployer/README.md b/eBPF_Supermarket/Auto_Cluster_Deployer/README.md new file mode 100644 index 000000000..a7ad301be --- /dev/null +++ b/eBPF_Supermarket/Auto_Cluster_Deployer/README.md @@ -0,0 +1,312 @@ +# Linux服务器集群的应用软件自动化部署管理工具 + +## 一、项目背景 + +在企业级 IT 环境中,随着业务需求的不断变化和扩展,服务器集群的管理和运维变得越来越复杂和繁重。特别是在 Linux 服务器集群中,面对大量分布式节点和多种应用软件的管理需求,如何高效地进行应用软件的安装、更新、卸载和同步操作,成为了一项重要的挑战。 + +本项目旨在设计和开发一个基于 Rust 语言的自动化应用软件部署与管理工具,以满足企业在 Linux 服务器集群中自动化运维的需求。该工具致力于提高系统运维效率、降低运维人员的工作强度,并实现以下几个目标: + +1. **同步更新与自动部署**:自动检测服务器集群中不同节点的状态,根据预设的部署策略和更新计划,执行应用软件的安装、更新或卸载操作。确保应用版本的一致性和更新的及时性,减少人为干预和操作风险。 + +2. **支持多种应用场景**:该工具适用于多种业务场景,包括但不限于 Web 服务、数据库服务、数据分析服务、容器管理平台等。根据服务器类别和业务需求,工具能够自动判断和执行相应的软件管理操作。 + +3. **适配国产操作系统**:为满足国产化、自主可控的需求,本工具特别设计成兼容主流国产操作系统,如麒麟、统信 UOS 等,确保在这些平台上能够正常运行并实现自动化运维目标。 + +4. **扩展性和可维护性**:工具采用模块化设计,支持功能扩展和定制化开发。通过基于 Rust 语言的安全性、高效性和并发性优势,提供良好的性能表现和较低的运行开销。同时,提供详细的日志和监控功能,便于问题排查和性能优化。 + +## 二、使用的技术 + +本项目使用了多种关键技术和工具来实现自动化应用软件部署与管理: + +1. **Rust编程语言**:作为项目的核心开发语言,Rust 提供了高效的系统级编程能力和内存安全性。它的“零成本抽象”特性确保了高性能,并且所有权机制有效防止了数据竞争、空指针引用等常见的编程错误,为开发高并发、低延迟的服务提供了坚实基础。 + +2. **gRPC**:用于实现客户端与服务器之间的远程过程调用(RPC)。gRPC 基于 HTTP/2 协议,支持多路复用、流式传输和双向通信等特性,确保高效、可靠的数据传输,适用于低延迟和高吞吐量的场景。 + + ![grpc](img/grpc.jpg) + +3. **Tonic**:Rust 生态中用于实现 gRPC 服务的库,简化了 gRPC 服务端和客户端的开发。Tonic 提供了符合 Rust 语言特性的异步接口,与 tokio 等异步运行时库紧密集成,为构建高效的异步网络服务提供了便利。 + +4. **tonic-web**:用于启用 gRPC-Web 支持。`tonic_web::enable` 允许 `tonic` 生成的 gRPC 服务可以通过 gRPC-Web 与客户端通信。由于 gRPC-Web 是基于 HTTP/1.x,因此需要显式启用 `accept_http1`。 + +5. **Protocol Buffers (Proto)**:作为 gRPC 的接口描述语言(IDL),Proto 用于定义服务端和客户端之间通信的接口和消息格式。它支持跨语言的序列化和反序列化,具有高效的二进制编码方式,能够显著减少数据传输的大小和开销。 + +6. **tokio**:Rust 的异步编程库,提供了多线程的异步运行时,能够处理大量的并发 IO 操作。tokio 在性能和可扩展性上表现出色,使得应用程序能够在不增加线程开销的情况下处理更多的连接和请求,特别适合高并发的网络服务场景。 + +7. **Rocket**:Rust 的一个 Web 框架,用于构建快速、安全的 web 应用。Rocket 提供了简洁的 API 和强大的功能,支持高级路由、请求处理和响应构建,非常适合需要高性能和高安全性的 Web 应用开发。 + +8. **PostgreSQL**:一个强大的开源关系数据库管理系统,提供丰富的功能和高度的可扩展性。PostgreSQL 支持复杂查询、大数据集、事务处理以及多种数据类型,适用于需要高性能和可靠数据存储的应用场景。 + +9. **Vue**:一个现代化的前端 JavaScript 框架,用于构建用户界面和单页应用。Vue 提供了响应式的数据绑定和组件化的开发方式,使得构建复杂的用户界面变得更加简单和高效。 + +这些技术的结合,使得项目能够在保证高性能和可靠性的前提下,实现复杂的部署和管理功能,满足企业级Linux服务器集群的运维需求。 + +## 三、系统实现 + +本项目使用 Rust 语言编写,采用 Tonic 库提供 gRPC 服务来实现客户端与服务器端的通信和数据交换。系统分为两大部分:客户端 (`client.rs`) 和服务器端 (`server.rs`)。通过 gRPC,客户端和服务器端能够进行高效的远程过程调用,实现软件包的部署和管理功能。 + +### 1. 客户端与服务器端的互通 + +- 在客户端部分,首先创建了一个 HTTP 客户端,它使用 `hyper` 库提供的功能来处理 HTTP 请求。为了与 gRPC-Web 兼容,客户端配置了一个中间件层 (`GrpcWebClientLayer`),这使得 HTTP 客户端能够处理 gRPC-Web 的请求和响应。客户端通过指定服务器的地址(如 `http://192.168.31.145:3000`)创建了一个 `SaControlClient` 实例,并将其封装在一个 `Arc` 中。这种封装方式使得客户端能够安全地在多线程环境中使用。 +- 服务器端部分使用 `tonic` 库创建了一个 gRPC 服务,并且通过 `tonic_web` 启用了 gRPC-Web 支持。服务器的地址和端口被设置为 `192.168.31.145:3000`。服务器通过 `Server::builder` 配置了 HTTP/1.x 支持,以便与 gRPC-Web 兼容。服务器端初始化并启动了 `SaControlServer`,并开始监听指定的地址。 + +### 2. 通信协议定义 + +本项目使用 Protocol Buffers(简称 Proto)来定义 gRPC 的通信协议,版本使用 `proto3` 语法。 + +下图1是服务的通信协议图示,展示了客户端与服务器之间的交互流程以及消息定义。 + +![通信协议图](img/通信图.jpg) + +#### (1)服务和消息定义 + +- **服务定义**: + + SAControl服务包括以下 RPC 方法: + + - `DeployPackages(Empty) returns (Ack)`:用于在服务器端执行部署软件包的操作。客户端调用该方法后,服务器端将根据预定义的规则进行软件包的自动部署。 + - `SARegist(SAInfo) returns (Ack)`:用于在服务器端注册服务器信息。客户端发送服务器的 IP 地址和类别信息,服务器端接收后确认注册。 + - `GetPackageInfoByIP(IpRequest) returns (PackageInfoResponse)`:根据 IP 地址请求获取软件包信息。客户端发送 IP 地址请求,服务器端返回该 IP 地址对应的包信息(如 ID、版本、软件名称和描述)。 + - `SendPackageFile(PackageRequest) returns (stream FileChunk)`:客户端请求软件包文件传输,服务器端通过流式数据传输的方式发送文件块。 + +- **消息定义**: + + - `Empty`:一个空消息,用于无参数请求。 + - `IpRequest`:包含 IP 地址的请求消息。 + - `SAInfo`:包含服务器 IP 地址和类别的注册信息。 + - `Ack`:一个确认消息,包含布尔类型的成功标志。 + - `ProgramDirectory`:包含程序目录信息,使用名称和子目录列表。 + - `TasksList`:任务列表,包含任务名称的重复字符串。 + - `TaskRequest`:任务请求,包含任务名称列表。 + - `Data`:包含字符串数据的消息。 + - `VersionInfo`:包含版本信息的消息。 + - `PackageRequest`:包含类别信息的请求消息。 + - `FileChunk`:用于文件传输的消息,包含文件内容(字节)和文件类型(字符串)。 + - `PackageInfoResponse`:包含包的 ID、版本、软件名称和描述的响应消息。 + +#### (2)客户端与服务器交互流程 + +1. **注册阶段**:客户端通过 `SARegist` RPC 方法向服务器注册自身信息,包括服务器 IP 和类别信息。服务器在接收到此请求后,确认并记录该信息。 +2. **部署请求阶段**:客户端调用 `DeployPackages` 方法,向服务器发送部署请求。服务器根据请求指示,执行相应的自动化部署任务。 +3. **获取软件包信息**:客户端通过 `GetPackageInfoByIP` 方法,按需向服务器请求特定 IP 地址的相关软件包信息,服务器将返回相应的版本、软件名称等详细信息。 +4. **文件传输**:当需要更新或安装新的软件包时,客户端使用 `SendPackageFile` 方法请求文件传输。服务器端通过流的形式逐块发送文件内容(`FileChunk`),确保大文件的传输可靠性和稳定性。 + +### 3. 客户端设计 + +1. **异步获取本地 IP 地址** + + 客户端需要获取本机的 IP 地址,以便与服务器进行通信。通过异步命令执行,客户端可以动态获取和确认其 IP 地址,以保证更新任务的精确性。 + +2. **解析配置文件和 DEB 包的版本号** + + 客户端检查本地的配置文件和 DEB 软件包以确定当前版本号。 + + - 如果配置文件不可用,客户端会回退到默认版本号。 + - 使用外部工具来解析 DEB 包中的版本号信息,以确保获取的版本号准确无误。 + 这一过程有助于减少不必要的更新操作,并保持软件版本的一致性。 + +3. **gRPC 服务客户端的初始化** + + 客户端通过 gRPC 接口与服务器通信。在初始化过程中,创建了一个基于 HTTP 的 gRPC 客户端,以支持不同服务之间的高效通信。 + 为确保线程安全,客户端对象被包装在并发控制结构中,使其能够在异步操作和多线程环境下正确工作。 + +4. **循环检查版本并触发更新** + + 客户端采用一个循环结构来不断检查软件包的版本信息。具体流程如下: + + - 向服务器发送部署请求,并等待服务器的响应。 + - 根据本地 IP 地址查询服务器上对应的软件包信息。 + - 如果从服务器获取的软件包信息表明不需要更新,客户端将继续进入下一轮循环。 + +5. **注册代理信息** + + 客户端根据获取到的本地 IP 地址和服务器返回的信息,向服务器进行代理注册。这一步骤确保服务器能够识别每个客户端,并准确分配相应的更新任务。 + +6. **比较本地和服务器的版本号** + + 客户端比较本地版本号与服务器上的最新版本号: + + - 如果本地版本落后,客户端将进一步检查传输的 DEB 包版本,以确认是否需要更新。 + - 如果传输的版本也低于服务器版本,客户端会请求获取新的更新包。 + +7. **请求和保存更新包** + + 如果确认需要更新,客户端向服务器请求更新包,并将接收到的包分块保存到本地文件系统中。 + 根据文件类型(如 DEB 包或安装脚本),客户端将内容保存到相应的文件中,确保更新操作的顺利进行。 + +8. **执行更新操作** + + 在接收到完整的更新包后,客户端准备执行更新操作。由于之前版本的软件可能用户还正在使用中,所以需要让用户自行决定要不要执行脚本来部署新版本的软件包。 + + - 执行脚本 + + 用户在终端输入以下命令来执行脚本: + + ```bash + bash install.sh + ``` + +9. **定时检查机制** + + 客户端设置了一个定时机制,在每轮更新检查完成后,等待一段时间再重新开始版本检查。这种设计不仅减少了对服务器的频繁请求,还提高了客户端的性能和稳定性。 + +### 4. 服务器端设计 + +服务器采用了 Rust 编程语言,使用了 `tonic` 和 `tokio` 等异步编程库,同时集成了 PostgreSQL 数据库进行数据管理。 + +1. **项目结构** + + 服务器端的核心逻辑通过实现 `SaControl` 服务接口来完成,该接口包含了多个方法来处理各种任务,如部署软件包、获取软件包信息、注册传感器代理等。程序模块包括以下内容: + + - **gRPC 服务定义**: `SaControl` 服务接口的定义,包含多个远程过程调用 (RPC) 方法。 + - **配置管理**: 使用结构体和序列化库来管理服务器的配置文件,包括程序目录、任务配置、代理信息等。 + - **数据库连接**: 通过 `tokio_postgres` 库实现与 PostgreSQL 数据库的连接与操作。 + - **文件传输**: 使用异步文件读取和流处理来实现软件包文件的分块传输。 + +2. **部署包打包** + + 为了能够在 Linux 系统上自动化部署软件,需要将应用程序打包成 `.deb` 或 `.rpm` 格式的部署包。这些格式的包通常用于在 Debian/Ubuntu 或 Red Hat/CentOS 系统上进行软件安装和管理。 + + - **打包成 `.deb` 文件** + + - **目录结构**: + + ```lua + package/ + ├── DEBIAN + │ └── control + └── usr + └── local + └── bin + ├── config.toml + └── application + ``` + + - **`DEBIAN/control` 文件**:包含包的元数据,如包名、版本、依赖关系等: + + ```less + Package: package + Version: 1.0.0 + Section: base + Priority: optional + Architecture: all + Maintainer: Your Name + Description: A brief description of your package. + ``` + + - **打包命令**: + + ```bash + dpkg-deb --build package + ``` + + - **打包成 `.rpm` 文件** + + - **目录结构**: + + ```plaintext + package-name/ + ├── RPM/ + │ ├── BUILD/ + │ ├── RPMS/ + │ ├── SOURCES/ + │ ├── SPECS/ + │ └── SRPMS/ + └── usr/ + └── local/ + └── bin/ + ├── config.toml + └── application + ``` + + - **`SPECS/your-package.spec` 文件**:此文件包含包的构建说明和元数据。 + + ```spec + Name: package + Version: 1.0.0 + Release: 1%{?dist} + Summary: A brief description of your package. + + License: GPL + URL: http://your.url + Source0: %{name}-%{version}.tar.gz + + %description + A detailed description of your package. + + %prep + %setup -q + + %build + # 在此处添加编译构建的命令 + + %install + # 安装命令,用于将文件复制到 RPM 构建目录 + install -D -m 0755 application %{buildroot}/usr/local/bin/application + install -D -m 0644 config.toml %{buildroot}/usr/local/bin/config.toml + + %files + /usr/local/bin/application + /usr/local/bin/config.toml + ``` + + - **打包命令**: + + ```bash + rpmbuild -ba SPECS/package.spec + ``` + +3. **服务接口实现** + + - **部署软件包 (`deploy_packages`)** + + 该方法用于根据数据库中的任务列表部署软件包。它从 `deployment_tasks` 表中获取未部署的任务,并根据任务的目标类型执行以下操作: + + - **所有目标**: 如果目标类型为“所有”,则查询所有服务器的 IP 地址并将信息插入到 `package_deployment` 表中。 + - **服务器组**: 对于指定的服务器组,查询组成员的服务器 IP 地址并进行相应的插入操作。 + - **单台服务器**: 针对特定服务器的目标类型,直接查询其 IP 地址并将信息存储在 `package_deployment` 表中。 + + 部署完成后,更新任务状态为已部署。 + + - **获取软件包信息 (`get_package_info_by_ip`)** + + 该方法根据客户端提供的 IP 地址,查询指定 IP 所关联的软件包信息。步骤如下: + + 1. 从 `package_deployment` 表中查找对应的包 ID。 + 2. 根据包 ID 从 `deployment_packages` 表中获取详细信息,如版本、软件名称和描述。 + 3. 删除 `package_deployment` 表中与该 IP 相关的记录。 + 4. 返回查询到的软件包信息。 + + 如果查询超时,将返回一个默认的响应,表示查询失败。 + + - **传感器代理注册 (`sa_regist`)** + + 用于接收来自传感器代理的注册信息,记录并确认接收到的注册信息,包括服务器 IP 和软件包 ID。响应一个成功的确认信息。 + + - **发送软件包文件 (`send_package_file`)** + + 此方法用于向客户端传输软件包文件。步骤如下: + + 1. 根据请求中的包 ID 从数据库中查询软件包的存储路径。 + 2. 准备一个文件列表,包括软件包和安装脚本。 + 3. 异步读取文件内容,并将其分块发送给客户端。 + 4. 返回包含数据流的响应。 + +4. **数据库操作** + + 程序通过 `tokio_postgres` 库与 PostgreSQL 数据库进行交互,包括以下主要操作: + + - **连接和错误处理**: 使用异步任务处理数据库连接,并在发生错误时打印相应的错误信息。 + - **查询操作**: 从 `deployment_tasks`、`servers`、`server_group_members`、`package_deployment` 和 `deployment_packages` 表中查询信息。 + - **插入和更新操作**: 向 `package_deployment` 表中插入新的记录,更新 `deployment_tasks` 表的状态。 + - **删除操作**: 删除与特定 IP 地址相关的包部署记录。 + +5. **异步任务和错误处理** + + 服务器使用 `tokio` 提供的异步功能来处理多任务并发。针对每个数据库操作和文件传输,都进行了详细的错误处理和日志记录,以确保系统的可靠性和可维护性。 + +6. **启动和配置** + + 服务器程序的启动由 `main` 函数负责,其中包括设置服务器的监听地址、初始化日志系统、以及配置 gRPC 服务和 HTTP/1 支持。 + + 服务器将在指定的 IP 地址和端口上监听客户端请求,并根据定义的服务接口处理请求。 + diff --git a/eBPF_Supermarket/Auto_Cluster_Deployer/img/grpc.jpg b/eBPF_Supermarket/Auto_Cluster_Deployer/img/grpc.jpg new file mode 100644 index 000000000..d6cdb6cc6 Binary files /dev/null and b/eBPF_Supermarket/Auto_Cluster_Deployer/img/grpc.jpg differ diff --git "a/eBPF_Supermarket/Auto_Cluster_Deployer/img/\351\200\232\344\277\241\345\233\276.jpg" "b/eBPF_Supermarket/Auto_Cluster_Deployer/img/\351\200\232\344\277\241\345\233\276.jpg" new file mode 100644 index 000000000..29cf8714c Binary files /dev/null and "b/eBPF_Supermarket/Auto_Cluster_Deployer/img/\351\200\232\344\277\241\345\233\276.jpg" differ diff --git a/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/.env b/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/.env new file mode 100644 index 000000000..4b2fe1d04 --- /dev/null +++ b/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/.env @@ -0,0 +1 @@ +DATABASE_URL=postgres://zxy:123456@192.168.1.158/db diff --git a/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/Cargo.lock b/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/Cargo.lock new file mode 100644 index 000000000..8b8dbac4a --- /dev/null +++ b/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/Cargo.lock @@ -0,0 +1,4214 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", + "version_check 0.9.4", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7ebdfa2ebdab6b1760375fa7d6f382b9f486eac35fc994625a00e89280bdbb7" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand 2.1.0", + "futures-lite 2.3.0", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +dependencies = [ + "async-channel 2.3.1", + "async-executor", + "async-io 2.3.3", + "async-lock 3.4.0", + "blocking", + "futures-lite 2.3.0", + "once_cell", +] + +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock 2.8.0", + "autocfg", + "cfg-if 1.0.0", + "concurrent-queue", + "futures-lite 1.13.0", + "log 0.4.21", + "parking", + "polling 2.8.0", + "rustix 0.37.27", + "slab", + "socket2 0.4.10", + "waker-fn", +] + +[[package]] +name = "async-io" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" +dependencies = [ + "async-lock 3.4.0", + "cfg-if 1.0.0", + "concurrent-queue", + "futures-io", + "futures-lite 2.3.0", + "parking", + "polling 3.7.2", + "rustix 0.38.34", + "slab", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener 2.5.3", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener 5.3.1", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-std" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +dependencies = [ + "async-channel 1.9.0", + "async-global-executor", + "async-io 1.13.0", + "async-lock 2.8.0", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite 1.13.0", + "gloo-timers", + "kv-log-macro", + "log 0.4.21", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", +] + +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + +[[package]] +name = "atomic" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" + +[[package]] +name = "atomic" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" +dependencies = [ + "byteorder", + "safemem", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bcrypt" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f691e63585950d8c1c43644d11bab9073e40f5060dd2822734ae7c3dc69a3a80" +dependencies = [ + "base64 0.13.1", + "blowfish", + "getrandom", +] + +[[package]] +name = "binascii" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +dependencies = [ + "serde", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel 2.3.1", + "async-task", + "futures-io", + "futures-lite 2.3.0", + "piper", +] + +[[package]] +name = "blowfish" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe3ff3fc1de48c1ac2e3341c4df38b0d1bfb8fdf04632a187c8b75aaa319a7ab" +dependencies = [ + "byteorder", + "cipher", + "opaque-debug", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "bytemuck" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "cc" +version = "1.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets 0.52.5", +] + +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "cookie" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be2018768ed1d848cc4d347d551546474025ba820e5db70e4c9aaa349f678bd7" +dependencies = [ + "percent-encoding 2.3.1", + "time 0.1.45", +] + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding 2.3.1", + "time 0.3.36", + "version_check 0.9.4", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "darling" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2 1.0.85", + "quote 1.0.36", + "strsim", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" +dependencies = [ + "darling_core", + "quote 1.0.36", + "syn 1.0.109", +] + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "devise" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd716c4a507adc5a2aa7c2a372d06c7497727e0892b243d3036bc7478a13e526" +dependencies = [ + "devise_codegen 0.2.1", + "devise_core 0.2.1", +] + +[[package]] +name = "devise" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6eacefd3f541c66fc61433d65e54e0e46e0a029a819a7dbbc7a7b489e8a85f8" +dependencies = [ + "devise_codegen 0.4.1", + "devise_core 0.4.1", +] + +[[package]] +name = "devise_codegen" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea7b8290d118127c08e3669da20b331bed56b09f20be5945b7da6c116d8fab53" +dependencies = [ + "devise_core 0.2.1", + "quote 0.6.13", +] + +[[package]] +name = "devise_codegen" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8cf4b8dd484ede80fd5c547592c46c3745a617c8af278e2b72bea86b2dfed6" +dependencies = [ + "devise_core 0.4.1", + "quote 1.0.36", +] + +[[package]] +name = "devise_core" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1053e9d5d5aade9bcedb5ab53b78df2b56ff9408a3138ce77eaaef87f932373" +dependencies = [ + "bitflags 1.3.2", + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.15.44", +] + +[[package]] +name = "devise_core" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35b50dba0afdca80b187392b24f2499a88c336d5a8493e4b4ccfb608708be56a" +dependencies = [ + "bitflags 2.5.0", + "proc-macro2 1.0.85", + "proc-macro2-diagnostics", + "quote 1.0.36", + "syn 2.0.66", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", +] + +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "either" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +dependencies = [ + "serde", +] + +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if 1.0.0", + "home", + "windows-sys 0.48.0", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener 5.3.1", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[package]] +name = "figment" +version = "0.10.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" +dependencies = [ + "atomic 0.6.0", + "pear 0.2.9", + "serde", + "toml 0.8.14", + "uncased", + "version_check 0.9.4", +] + +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall 0.4.1", + "windows-sys 0.52.0", +] + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "futures-core", + "futures-sink", + "spin 0.9.8", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding 2.3.1", +] + +[[package]] +name = "fsevent" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6" +dependencies = [ + "bitflags 1.3.2", + "fsevent-sys", +] + +[[package]] +name = "fsevent-sys" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f41b048a94555da0f42f1d632e2e19510084fb8e303b0daa2816e733fb3644a0" +dependencies = [ + "libc", +] + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags 1.3.2", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-lite" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "fastrand 2.1.0", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generator" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" +dependencies = [ + "cc", + "libc", + "log 0.4.21", + "rustversion", + "windows", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check 0.9.4", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap 2.2.6", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown 0.14.5", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.10.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a0652d9a2609a968c14be1a9ea00bf4b1d64e2e1f53a1b51b6fff3a6e829273" +dependencies = [ + "base64 0.9.3", + "httparse", + "language-tags", + "log 0.3.9", + "mime 0.2.6", + "num_cpus", + "time 0.1.45", + "traitobject", + "typeable", + "unicase 1.4.2", + "url 1.7.2", +] + +[[package]] +name = "hyper" +version = "0.14.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.7", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4716a3a0933a1d01c2f72450e89596eb51dd34ef3c211ccd875acdf1f8fe47ed" +dependencies = [ + "icu_normalizer", + "icu_properties", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown 0.14.5", + "serde", +] + +[[package]] +name = "inlinable_string" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" + +[[package]] +name = "inotify" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4816c66d2c8ae673df83366c18341538f234a26d65a9ecea5c348b453ac1d02f" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log 0.4.21", +] + +[[package]] +name = "language-tags" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin 0.5.2", +] + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" +dependencies = [ + "log 0.4.21", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +dependencies = [ + "value-bag", +] + +[[package]] +name = "loom" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" +dependencies = [ + "cfg-if 1.0.0", + "generator", + "scoped-tls", + "serde", + "serde_json", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if 1.0.0", + "digest", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" +dependencies = [ + "log 0.3.9", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.6.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" +dependencies = [ + "cfg-if 0.1.10", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log 0.4.21", + "miow", + "net2", + "slab", + "winapi 0.2.8", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", +] + +[[package]] +name = "mio-extras" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" +dependencies = [ + "lazycell", + "log 0.4.21", + "mio 0.6.23", + "slab", +] + +[[package]] +name = "miow" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "multer" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http 1.1.0", + "httparse", + "memchr", + "mime 0.3.17", + "spin 0.9.8", + "tokio", + "tokio-util", + "version_check 0.9.4", +] + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log 0.4.21", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "net2" +version = "0.2.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b13b648036a2339d06de780866fbdfda0dde886de7b3af2ddeba8b14f4ee34ac" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "notify" +version = "4.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b72dd35279a5dc895a30965e247b0961ba36c233dc48454a2de8ccd459f1afd3" +dependencies = [ + "bitflags 1.3.2", + "filetime", + "fsevent", + "fsevent-sys", + "inotify", + "libc", + "mio 0.6.23", + "mio-extras", + "walkdir", + "winapi 0.3.9", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi 0.3.9", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.9", + "libc", +] + +[[package]] +name = "object" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" +dependencies = [ + "memchr", +] + +[[package]] +name = "okapi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e969ac53c86c158761836e746be203f4cfd774445bd8aff4bfdcf8e42dd93891" +dependencies = [ + "schemars", + "serde", + "serde_json", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "openssl" +version = "0.10.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +dependencies = [ + "bitflags 2.5.0", + "cfg-if 1.0.0", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall 0.5.2", + "smallvec", + "windows-targets 0.52.5", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pear" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32dfa7458144c6af7f9ce6a137ef975466aa68ffa44d4d816ee5934018ba960a" +dependencies = [ + "pear_codegen 0.1.5", +] + +[[package]] +name = "pear" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" +dependencies = [ + "inlinable_string", + "pear_codegen 0.2.9", + "yansi 1.0.1", +] + +[[package]] +name = "pear_codegen" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0288ba5d581afbc93e2bbd931c1013584c15ecf46b1cdb927edc7abddbc8ca6" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.15.44", + "version_check 0.9.4", + "yansi 0.5.1", +] + +[[package]] +name = "pear_codegen" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" +dependencies = [ + "proc-macro2 1.0.85", + "proc-macro2-diagnostics", + "quote 1.0.36", + "syn 2.0.66", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand 2.1.0", + "futures-io", +] + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if 1.0.0", + "concurrent-queue", + "libc", + "log 0.4.21", + "pin-project-lite", + "windows-sys 0.48.0", +] + +[[package]] +name = "polling" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b" +dependencies = [ + "cfg-if 1.0.0", + "concurrent-queue", + "hermit-abi 0.4.0", + "pin-project-lite", + "rustix 0.38.34", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +dependencies = [ + "unicode-xid 0.1.0", +] + +[[package]] +name = "proc-macro2" +version = "1.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", + "version_check 0.9.4", + "yansi 1.0.1", +] + +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +dependencies = [ + "proc-macro2 0.4.30", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2 1.0.85", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +dependencies = [ + "bitflags 2.5.0", +] + +[[package]] +name = "ref-cast" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", +] + +[[package]] +name = "regex" +version = "1.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "restful_api" +version = "0.1.0" +dependencies = [ + "async-std", + "bcrypt", + "chrono", + "dotenv", + "rocket 0.5.1", + "rocket_cors", + "rocket_okapi", + "serde", + "serde_json", + "sqlx", + "syn 1.0.109", + "time 0.3.36", + "tokio", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if 1.0.0", + "getrandom", + "libc", + "spin 0.9.8", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rocket" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83b9d9dc08c5dcc1d8126a9dd615545e6a358f8c13c883c8dfed8c0376fa355e" +dependencies = [ + "atty", + "base64 0.13.1", + "log 0.4.21", + "memchr", + "num_cpus", + "pear 0.1.5", + "rocket_codegen 0.4.11", + "rocket_http 0.4.11", + "state 0.4.2", + "time 0.1.45", + "toml 0.4.10", + "version_check 0.9.4", + "yansi 0.5.1", +] + +[[package]] +name = "rocket" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a516907296a31df7dc04310e7043b61d71954d703b603cc6867a026d7e72d73f" +dependencies = [ + "async-stream", + "async-trait", + "atomic 0.5.3", + "binascii", + "bytes", + "either", + "figment", + "futures", + "indexmap 2.2.6", + "log 0.4.21", + "memchr", + "multer", + "num_cpus", + "parking_lot", + "pin-project-lite", + "rand", + "ref-cast", + "rocket_codegen 0.5.1", + "rocket_http 0.5.1", + "serde", + "serde_json", + "state 0.6.0", + "tempfile", + "time 0.3.36", + "tokio", + "tokio-stream", + "tokio-util", + "ubyte", + "version_check 0.9.4", + "yansi 1.0.1", +] + +[[package]] +name = "rocket_codegen" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2810037b5820098af97bd4fdd309e76a8101ceb178147de775c835a2537284fe" +dependencies = [ + "devise 0.2.1", + "glob", + "indexmap 1.9.3", + "quote 0.6.13", + "rocket_http 0.4.11", + "version_check 0.9.4", + "yansi 0.5.1", +] + +[[package]] +name = "rocket_codegen" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "575d32d7ec1a9770108c879fc7c47815a80073f96ca07ff9525a94fcede1dd46" +dependencies = [ + "devise 0.4.1", + "glob", + "indexmap 2.2.6", + "proc-macro2 1.0.85", + "quote 1.0.36", + "rocket_http 0.5.1", + "syn 2.0.66", + "unicode-xid 0.2.4", + "version_check 0.9.4", +] + +[[package]] +name = "rocket_contrib" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e20efbc6a211cb3df5375accf532d4186f224b623f39eca650b19b96240c596b" +dependencies = [ + "log 0.4.21", + "notify", + "rocket 0.4.11", + "serde", + "serde_json", +] + +[[package]] +name = "rocket_cors" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea20696dc46308d0ca06222905fe38e02b8e46c087af9c82ea85cdc386271076" +dependencies = [ + "log 0.4.21", + "regex", + "rocket 0.4.11", + "serde", + "serde_derive", + "unicase 2.7.0", + "unicase_serde", + "url 2.5.1", +] + +[[package]] +name = "rocket_http" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf9cbd128e1f321a2d0bebd2b7cf0aafd89ca43edf69e49b56a5c46e48eb19f" +dependencies = [ + "cookie 0.11.5", + "hyper 0.10.16", + "indexmap 1.9.3", + "pear 0.1.5", + "percent-encoding 1.0.1", + "smallvec", + "state 0.4.2", + "time 0.1.45", + "unicode-xid 0.1.0", +] + +[[package]] +name = "rocket_http" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e274915a20ee3065f611c044bd63c40757396b6dbc057d6046aec27f14f882b9" +dependencies = [ + "cookie 0.18.1", + "either", + "futures", + "http 0.2.12", + "hyper 0.14.29", + "indexmap 2.2.6", + "log 0.4.21", + "memchr", + "pear 0.2.9", + "percent-encoding 2.3.1", + "pin-project-lite", + "ref-cast", + "rustls", + "rustls-pemfile", + "serde", + "smallvec", + "stable-pattern", + "state 0.6.0", + "time 0.3.36", + "tokio", + "tokio-rustls", + "uncased", +] + +[[package]] +name = "rocket_okapi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf633b45fd1c03ed4e4da824d9fafbecab8af298e46716ab0cf446fdf36c58b2" +dependencies = [ + "okapi", + "rocket 0.4.11", + "rocket_contrib", + "rocket_okapi_codegen", + "schemars", + "serde", + "serde_json", +] + +[[package]] +name = "rocket_okapi_codegen" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b630c92ffa097a6425262547732e6bc19d80a24628d209126bd52c65acd96490" +dependencies = [ + "darling", + "proc-macro2 1.0.85", + "quote 1.0.36", + "rocket_http 0.4.11", + "syn 1.0.109", +] + +[[package]] +name = "rsa" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "0.37.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys 0.4.14", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log 0.4.21", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "schemars" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be77ed66abed6954aabf6a3e31a84706bedbf93750d267e92ef4a6d90bbd6a61" +dependencies = [ + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11af7a475c9ee266cfaa9e303a47c830ebe072bf3101ab907a7b7b9d816fa01d" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "serde_derive_internals", + "syn 1.0.109", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "security-framework" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +dependencies = [ + "bitflags 2.5.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", +] + +[[package]] +name = "serde_derive_internals" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dbab34ca63057a1f15280bdf3c39f2b1eb1b54c17e98360e511637aef7418c6" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 1.0.109", +] + +[[package]] +name = "serde_json" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +dependencies = [ + "serde", +] + +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlformat" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f895e3734318cc55f1fe66258926c9b910c124d47520339efecbb6c59cec7c1f" +dependencies = [ + "nom", + "unicode_categories", +] + +[[package]] +name = "sqlx" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcfa89bea9500db4a0d038513d7a060566bfc51d46d1c014847049a45cce85e8" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d06e2f2bd861719b1f3f0c7dbe1d80c30bf59e76cf019f07d9014ed7eefb8e08" +dependencies = [ + "atoi", + "byteorder", + "bytes", + "chrono", + "crc", + "crossbeam-queue", + "either", + "event-listener 5.3.1", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown 0.14.5", + "hashlink", + "hex", + "indexmap 2.2.6", + "log 0.4.21", + "memchr", + "native-tls", + "once_cell", + "paste", + "percent-encoding 2.3.1", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlformat", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "url 2.5.1", +] + +[[package]] +name = "sqlx-macros" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f998a9defdbd48ed005a89362bd40dd2117502f15294f61c8d47034107dbbdc" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "sqlx-core", + "sqlx-macros-core", + "syn 2.0.66", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d100558134176a2629d46cec0c8891ba0be8910f7896abfdb75ef4ab6f4e7ce" +dependencies = [ + "dotenvy", + "either", + "heck", + "hex", + "once_cell", + "proc-macro2 1.0.85", + "quote 1.0.36", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn 2.0.66", + "tempfile", + "tokio", + "url 2.5.1", +] + +[[package]] +name = "sqlx-mysql" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cac0ab331b14cb3921c62156d913e4c15b74fb6ec0f3146bd4ef6e4fb3c12" +dependencies = [ + "atoi", + "base64 0.22.1", + "bitflags 2.5.0", + "byteorder", + "bytes", + "chrono", + "crc", + "digest", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", + "itoa", + "log 0.4.21", + "md-5", + "memchr", + "once_cell", + "percent-encoding 2.3.1", + "rand", + "rsa", + "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9734dbce698c67ecf67c442f768a5e90a49b2a4d61a9f1d59f73874bd4cf0710" +dependencies = [ + "atoi", + "base64 0.22.1", + "bitflags 2.5.0", + "byteorder", + "chrono", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log 0.4.21", + "md-5", + "memchr", + "once_cell", + "rand", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75b419c3c1b1697833dd927bdc4c6545a620bc1bbafabd44e1efbe9afcd337e" +dependencies = [ + "atoi", + "chrono", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log 0.4.21", + "percent-encoding 2.3.1", + "serde", + "serde_urlencoded", + "sqlx-core", + "tracing", + "url 2.5.1", +] + +[[package]] +name = "stable-pattern" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4564168c00635f88eaed410d5efa8131afa8d8699a612c80c455a0ba05c21045" +dependencies = [ + "memchr", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "state" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3015a7d0a5fd5105c91c3710d42f9ccf0abfb287d62206484dcc67f9569a6483" + +[[package]] +name = "state" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8" +dependencies = [ + "loom", +] + +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + +[[package]] +name = "strsim" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "0.15.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "unicode-xid 0.1.0", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", +] + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if 1.0.0", + "fastrand 2.1.0", + "rustix 0.38.34", + "windows-sys 0.52.0", +] + +[[package]] +name = "thiserror" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", +] + +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi 0.3.9", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio 0.8.11", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.5.7", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" +dependencies = [ + "serde", +] + +[[package]] +name = "toml" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" +dependencies = [ + "indexmap 2.2.6", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log 0.4.21", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log 0.4.21", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "traitobject" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typeable" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ubyte" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f720def6ce1ee2fc44d40ac9ed6d3a59c361c80a75a7aa8e75bb9baed31cf2ea" +dependencies = [ + "serde", +] + +[[package]] +name = "uncased" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" +dependencies = [ + "serde", + "version_check 0.9.4", +] + +[[package]] +name = "unicase" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" +dependencies = [ + "version_check 0.1.5", +] + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check 0.9.4", +] + +[[package]] +name = "unicase_serde" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ef53697679d874d69f3160af80bc28de12730a985d57bdf2b47456ccb8b11f1" +dependencies = [ + "serde", + "unicase 2.7.0", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-properties" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291" + +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" +dependencies = [ + "idna 0.1.5", + "matches", + "percent-encoding 1.0.1", +] + +[[package]] +name = "url" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c25da092f0a868cdf09e8674cd3b7ef3a7d92a24253e663a2fb85e2496de56" +dependencies = [ + "form_urlencoded", + "idna 1.0.0", + "percent-encoding 2.3.1", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "value-bag" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "waker-fn" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log 0.4.21", + "once_cell", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote 1.0.36", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "whoami" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" +dependencies = [ + "redox_syscall 0.4.1", + "wasite", +] + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "winnow" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" +dependencies = [ + "memchr", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" +dependencies = [ + "is-terminal", +] + +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", +] + +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", +] diff --git a/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/Cargo.toml b/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/Cargo.toml new file mode 100644 index 000000000..dc120537f --- /dev/null +++ b/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "restful_api" +version = "0.1.0" +authors = ["zxy "] +edition = "2018" + +[dependencies] +rocket = { version = "0.5.0-rc.4", features = ["json", "tls"] } +rocket_cors = "0.5.2" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +sqlx = { version = "0.8", features = ["postgres", "runtime-tokio-native-tls", "macros", "chrono"] } +chrono = { version = "0.4", features = ["serde"] } +dotenv = "0.15.0" +tokio = { version = "1", features = ["full"] } +time = "0.3" +syn = { version = "1.0", features = ["derive", "parsing"] } +bcrypt = "0.10" +async-std = "1.10" +rocket_okapi = "0.5" + + + + + diff --git a/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/src/auth.rs b/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/src/auth.rs new file mode 100644 index 000000000..9c4d39648 --- /dev/null +++ b/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/src/auth.rs @@ -0,0 +1,135 @@ +use rocket::{post, serde::json::Json, State}; +use crate::model::{User, NewUser, ApiResponse}; +use sqlx::query_as; +use bcrypt::{hash, verify}; +use chrono::Utc; +use rocket::http::Status; +use sqlx::PgPool; +use crate::model::ChangePasswordRequest; +use sqlx::query; + +#[post("/register", data = "")] +pub async fn register(db: &State, new_user: Json) -> Result>, Status> { + let password_hash = hash(&new_user.password, 4).map_err(|_| Status::InternalServerError)?; + + let mut conn = db.acquire().await.map_err(|_| Status::InternalServerError)?; + + let result = query_as::<_, User>( + r#" + INSERT INTO users (username, password_hash, created_at, updated_at) + VALUES ($1, $2, $3, $4) + RETURNING * + "# + ) + .bind(&new_user.username) + .bind(&password_hash) + .bind(Utc::now()) + .bind(Utc::now()) + .fetch_one(&mut conn) + .await; + + match result { + Ok(user) => Ok(Json(ApiResponse { + code: 200, + status: "success".to_string(), + message: "User registered successfully".to_string(), + data: Some(user), + })), + Err(sqlx::Error::Database(db_err)) if db_err.constraint() == Some("unique_username") => { + // 处理唯一约束冲突错误 + Err(Status::Conflict) // 返回 409 冲突状态 + }, + Err(_) => Err(Status::UnprocessableEntity), + } +} + + +#[post("/login", data = "")] +pub async fn login(db: &State, login_data: Json) -> Result>, Status> { + let mut conn = db.acquire().await.map_err(|_| Status::InternalServerError)?; + + let user = query_as::<_, User>( + r#" + SELECT id, username, password_hash, created_at, updated_at + FROM users + WHERE username = $1 + "# + ) + .bind(&login_data.username) + .fetch_optional(&mut conn) // 使用获取的连接 + .await + .map_err(|_| Status::InternalServerError)? + .ok_or_else(|| Status::Unauthorized)?; + + let valid = verify(&login_data.password, &user.password_hash).map_err(|_| Status::InternalServerError)?; + + if valid { + Ok(Json(ApiResponse { + code: 200, + status: "success".to_string(), + message: "Login successful".to_string(), + data: Some(user), + })) + } else { + Err(Status::Unauthorized) + } +} + +// 修改密码接口 +#[post("/change_password", data = "")] +pub async fn change_password( + db: &State, + change_password_request: Json, +) -> Result>, Status> { + let mut conn = db.acquire().await.map_err(|_| Status::InternalServerError)?; + + // 获取用户信息 + let user = query_as::<_, User>( + r#" + SELECT id, username, password_hash, created_at, updated_at + FROM users + WHERE username = $1 + "# + ) + .bind(&change_password_request.username) + .fetch_optional(&mut conn) + .await + .map_err(|_| Status::InternalServerError)? + .ok_or_else(|| Status::Unauthorized)?; + + // 验证旧密码 + let is_valid = verify(&change_password_request.old_password, &user.password_hash) + .map_err(|_| Status::InternalServerError)?; + + if !is_valid { + return Err(Status::Unauthorized); // 如果旧密码不正确,返回未授权状态 + } + + // 哈希新密码 + let new_password_hash = hash(&change_password_request.new_password, 4) + .map_err(|_| Status::InternalServerError)?; + + // 更新数据库中的密码哈希 + let update_result = query( + r#" + UPDATE users + SET password_hash = $1, updated_at = $2 + WHERE id = $3 + "# + ) + .bind(&new_password_hash) + .bind(chrono::Utc::now()) + .bind(user.id) + .execute(&mut conn) + .await; + + match update_result { + Ok(_) => Ok(Json(ApiResponse { + code: 200, + status: "success".to_string(), + message: "Password updated successfully".to_string(), + data: None, + })), + Err(_) => Err(Status::InternalServerError), + } +} diff --git a/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/src/deployment_packages.rs b/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/src/deployment_packages.rs new file mode 100644 index 000000000..68021f2dd --- /dev/null +++ b/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/src/deployment_packages.rs @@ -0,0 +1,132 @@ +use rocket::{get, post, put, delete, serde::json::Json, State}; +use crate::model::{DeploymentPackage, NewDeploymentPackage, ApiResponse}; +use sqlx::{PgPool, query_as, query}; +use rocket::http::Status; + +// 增加部署包 +#[post("/deployment_packages", data = "")] +pub async fn add_deployment_package( + db: &State, + new_package: Json +) -> Result>, Status> { + let result = query_as::<_, DeploymentPackage>( + "INSERT INTO deployment_packages (version, software_name, description, path) + VALUES ($1, $2, $3, $4) RETURNING *" + ) + .bind(&new_package.version) + .bind(&new_package.software_name) + .bind(&new_package.description) + .bind(&new_package.path) + .fetch_one(&**db) + .await; + + match result { + Ok(package) => Ok(Json(ApiResponse { + code: 200, + status: "success".to_string(), + message: "Deployment package added successfully".to_string(), + data: Some(package), + })), + Err(_) => Err(Status::InternalServerError), + } +} + +// 查询部署包(根据软件名) +#[get("/deployment_packages/")] +pub async fn get_deployment_packages( + db: &State, + software_name: String +) -> Result>>, Status> { + let result = query_as::<_, DeploymentPackage>( + "SELECT * FROM deployment_packages WHERE software_name = $1" + ) + .bind(&software_name) + .fetch_all(&**db) + .await; + + match result { + Ok(packages) => Ok(Json(ApiResponse { + code: 200, + status: "success".to_string(), + message: "Deployment packages retrieved successfully".to_string(), + data: Some(packages), + })), + Err(_) => Err(Status::InternalServerError), + } +} + +// 获取全部部署包 +#[get("/deployment_packages")] +pub async fn get_all_deployment_packages( + db: &State +) -> Result>>, Status> { + let result = query_as::<_, DeploymentPackage>( + "SELECT * FROM deployment_packages" + ) + .fetch_all(&**db) + .await; + + match result { + Ok(packages) => Ok(Json(ApiResponse { + code: 200, + status: "success".to_string(), + message: "All deployment packages retrieved successfully".to_string(), + data: Some(packages), + })), + Err(_) => Err(Status::InternalServerError), + } +} + +// 修改部署包(根据软件名) +#[put("/deployment_packages/", data = "")] +pub async fn update_deployment_package( + db: &State, + software_name: String, + updated_package: Json +) -> Result>, Status> { + let result = query_as::<_, DeploymentPackage>( + "UPDATE deployment_packages + SET version = $1, description = $2, path = $3 + WHERE software_name = $4 + RETURNING *" + ) + .bind(&updated_package.version) + .bind(&updated_package.description) + .bind(&updated_package.path) + .bind(&software_name) + .fetch_one(&**db) + .await; + + match result { + Ok(package) => Ok(Json(ApiResponse { + code: 200, + status: "success".to_string(), + message: "Deployment package updated successfully".to_string(), + data: Some(package), + })), + Err(_) => Err(Status::InternalServerError), + } +} + +// 删除部署包(根据软件名) +#[delete("/deployment_packages/")] +pub async fn delete_deployment_package( + db: &State, + software_name: String +) -> Result>, Status> { + let result = query("DELETE FROM deployment_packages WHERE software_name = $1") + .bind(&software_name) + .execute(&**db) + .await; + + match result { + Ok(_) => Ok(Json(ApiResponse { + code: 200, + status: "success".to_string(), + message: "Deployment package deleted successfully".to_string(), + data: None, + })), + Err(_) => Err(Status::InternalServerError), + } +} + diff --git a/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/src/deployment_tasks.rs b/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/src/deployment_tasks.rs new file mode 100644 index 000000000..b27980cf3 --- /dev/null +++ b/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/src/deployment_tasks.rs @@ -0,0 +1,170 @@ +use crate::model::{DeploymentTask, NewDeploymentTask, ApiResponse, TargetType}; +use sqlx::{PgPool, query_as, query}; +use rocket::{get, post, put, delete, serde::json::Json, State}; +use rocket::http::Status; + +#[post("/deployment_tasks", data = "")] +pub async fn add_deployment_task( + db: &State, + new_task: Json +) -> Result>, Status> { + let target_type = TargetType::from_str(&new_task.target_type) + .ok_or(Status::BadRequest)?; + + match target_type { + TargetType::SingleServer => { + let server_exists = query("SELECT 1 FROM servers WHERE id = $1") + .bind(new_task.target_id) + .fetch_optional(&**db) + .await + .map_err(|e| { + eprintln!("Database error: {}", e); + Status::InternalServerError + })?; + + if server_exists.is_none() { + return Err(Status::BadRequest); + } + }, + TargetType::ServerGroup => { + let group_exists = query("SELECT 1 FROM server_groups WHERE id = $1") + .bind(new_task.target_id) + .fetch_optional(&**db) + .await + .map_err(|e| { + eprintln!("Database error: {}", e); + Status::InternalServerError + })?; + + if group_exists.is_none() { + return Err(Status::BadRequest); + } + }, + TargetType::All => { + if new_task.target_id.is_some() { + return Err(Status::BadRequest); + } + }, + } + + let result = query_as::<_, DeploymentTask>( + "INSERT INTO deployment_tasks (package_id, target_type, target_id, is_deployed) + VALUES ($1, $2, $3, FALSE) RETURNING *" + ) + .bind(new_task.package_id) + .bind(new_task.target_type.clone()) + .bind(new_task.target_id) + .fetch_one(&**db) + .await; + + match result { + Ok(task) => Ok(Json(ApiResponse { + code: 200, + status: "success".to_string(), + message: "Deployment task created successfully".to_string(), + data: Some(task), + })), + Err(sqlx::Error::Database(db_err)) if db_err.constraint() == Some("deployment_tasks_package_id_target_type_target_id_key") => { + Err(Status::Conflict) + }, + Err(e) => { + eprintln!("Database error: {}", e); + Err(Status::InternalServerError) + }, + } +} + +// 查询所有部署任务 +#[get("/deployment_tasks")] +pub async fn get_all_deployment_tasks( + db: &State +) -> Result>>, Status> { + let result = query_as::<_, DeploymentTask>("SELECT * FROM deployment_tasks") + .fetch_all(&**db) + .await; + + match result { + Ok(tasks) => Ok(Json(ApiResponse { + code: 200, + status: "success".to_string(), + message: "Deployment tasks retrieved successfully".to_string(), + data: Some(tasks), + })), + Err(_) => Err(Status::InternalServerError), + } +} + +// 根据 package_id 查询部署任务 +#[get("/deployment_tasks/package/")] +pub async fn get_deployment_tasks_by_package( + db: &State, + package_id: i32 +) -> Result>>, Status> { + let result = query_as::<_, DeploymentTask>("SELECT * FROM deployment_tasks WHERE package_id = $1") + .bind(package_id) + .fetch_all(&**db) + .await; + + match result { + Ok(tasks) => Ok(Json(ApiResponse { + code: 200, + status: "success".to_string(), + message: "Deployment tasks retrieved successfully".to_string(), + data: Some(tasks), + })), + Err(_) => Err(Status::InternalServerError), + } +} + +// 更新部署任务 +#[put("/deployment_tasks/", data = "")] +pub async fn update_deployment_task( + db: &State, + id: i32, + updated_task: Json +) -> Result>, Status> { + let result = query_as::<_, DeploymentTask>( + "UPDATE deployment_tasks + SET package_id = $1, target_type = $2, target_id = $3 + WHERE id = $4 + RETURNING *" + ) + .bind(updated_task.package_id) + .bind(updated_task.target_type.to_string()) + .bind(updated_task.target_id) + .bind(id) + .fetch_one(&**db) + .await; + + match result { + Ok(task) => Ok(Json(ApiResponse { + code: 200, + status: "success".to_string(), + message: "Deployment task created successfully".to_string(), + data: Some(task), + })), + Err(_) => Err(Status::InternalServerError), + } +} + +// 删除部署任务 +#[delete("/deployment_tasks/")] +pub async fn delete_deployment_task( + db: &State, + id: i32 +) -> Result>, Status> { + let result = query("DELETE FROM deployment_tasks WHERE id = $1") + .bind(id) + .execute(&**db) + .await; + + match result { + Ok(_) => Ok(Json(ApiResponse { + code: 200, + status: "success".to_string(), + message: "Deployment task deleted successfully".to_string(), + data: None, + })), + Err(_) => Err(Status::InternalServerError), + } +} diff --git a/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/src/main.rs b/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/src/main.rs new file mode 100644 index 000000000..e8ec93c6f --- /dev/null +++ b/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/src/main.rs @@ -0,0 +1,122 @@ +mod auth; +mod model; +mod servers; +mod server_groups; +mod server_group_members; +mod deployment_packages; +mod deployment_tasks; + +use rocket::routes; +use sqlx::postgres::PgPoolOptions; +use sqlx::PgPool; +use rocket::{Request, Response}; +use rocket::fairing::{Fairing, Info, Kind}; +use rocket::http::Header; +use rocket::config::Config; + + +type Db = PgPool; + +// 定义自定义 CORS Fairing +pub struct Cors { + allowed_origins: Vec, +} + +#[rocket::async_trait] +impl Fairing for Cors { + fn info(&self) -> Info { + Info { + name: "CORS Fairing", + kind: Kind::Response | Kind::Request, + } + } + + async fn on_request(&self, _request: &mut Request<'_>, _data: &mut rocket::Data<'_>) { + } + + async fn on_response<'r>(&self, request: &'r Request<'_>, response: &mut Response<'r>) { + if request.method() == rocket::http::Method::Options { + response.set_status(rocket::http::Status::Ok); + } + + response.set_header(Header::new("Access-Control-Allow-Origin", self.get_origin(request))); + response.set_header(Header::new("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")); + response.set_header(Header::new("Access-Control-Allow-Headers", "Authorization, Accept, Content-Type")); + } +} + +impl Cors { + fn new(allowed_origins: Vec<&str>) -> Self { + Cors { + allowed_origins: allowed_origins.into_iter().map(String::from).collect(), + } + } + + fn get_origin(&self, request: &Request<'_>) -> String { + if let Some(origin) = request.headers().get_one("Origin") { + if self.allowed_origins.contains(&origin.to_string()) { + return origin.to_string(); + } + } + "*".to_string() // 默认情况下允许所有来源 + } +} + +#[rocket::options("/")] +fn options_route() -> rocket::http::Status { + rocket::http::Status::Ok +} + +// 配置并启动 Rocket +fn rocket(pool: Db) -> rocket::Rocket { + let allowed_origins = vec![ + "http://localhost:8080", + "http://192.168.31.145:8080", + ]; + + rocket::custom( + Config::figment() + .merge(("port", 8080)) + .merge(("address", "0.0.0.0")) + ) + .manage(pool) + .attach(Cors::new(allowed_origins)) + .mount("/", routes![ + options_route, + auth::register, + auth::login, + auth::change_password, + servers::add_server, + servers::get_servers, + servers::delete_server, + server_groups::add_server_group, + server_groups::get_server_groups, + server_groups::delete_server_group, + server_group_members::add_servers_to_group, + server_group_members::get_servers_in_group, + server_group_members::remove_servers_from_group, + deployment_packages::add_deployment_package, + deployment_packages::get_deployment_packages, + deployment_packages::get_all_deployment_packages, + deployment_packages::update_deployment_package, + deployment_packages::delete_deployment_package, + deployment_tasks::add_deployment_task, + deployment_tasks::get_all_deployment_tasks, + deployment_tasks::get_deployment_tasks_by_package, + deployment_tasks::update_deployment_task, + deployment_tasks::delete_deployment_task, + ]) +} + +#[rocket::main] +async fn main() -> Result<(), rocket::Error> { + let pool = PgPoolOptions::new() + .max_connections(5) + .connect("postgres://zxy:123456@localhost/sensordb") + .await + .expect("Failed to create pool."); + + rocket(pool).launch().await?; + + Ok(()) +} diff --git a/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/src/model.rs b/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/src/model.rs new file mode 100644 index 000000000..180b0f7dd --- /dev/null +++ b/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/src/model.rs @@ -0,0 +1,128 @@ +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use sqlx::FromRow; + + +#[derive(Debug, Serialize, Deserialize, FromRow)] +pub struct User { + pub id: i32, + pub username: String, + pub password_hash: String, + pub created_at: DateTime, + pub updated_at: DateTime, +} + +#[derive(Deserialize)] +pub struct NewUser { + pub username: String, + pub password: String, +} + + +#[derive(Debug, Deserialize)] +pub struct ChangePasswordRequest { + pub username: String, + pub old_password: String, + pub new_password: String, +} + +#[derive(Debug, Serialize, Deserialize, FromRow)] +pub struct Server { + pub id: i32, + pub ip_address: String, +} + +#[derive(Debug, Deserialize)] +pub struct NewServer { + pub ip_address: String, +} + +#[derive(Debug, Serialize, Deserialize, FromRow)] +pub struct ServerGroup { + pub id: i32, + pub description: String, +} + +#[derive(Debug, Deserialize)] +pub struct NewServerGroup { + pub description: String, +} + +#[derive(Debug, Serialize, Deserialize, FromRow)] +pub struct ServerGroupMember { + pub id: i32, + pub server_id: i32, + pub group_id: i32, +} + +#[derive(Debug, Deserialize)] +pub struct BatchAddServersRequest { + pub ip_addresses: Vec, + pub group_description: String, +} + + +#[derive(Debug, Deserialize)] +pub struct BatchRemoveServersRequest { + pub ip_addresses: Vec, + pub group_description: String, +} + +#[derive(Debug, Serialize, Deserialize, FromRow)] +pub struct DeploymentPackage { + pub id: i32, + pub version: String, + pub software_name: String, + pub description: Option, + pub path: String, +} + +#[derive(Debug, Deserialize)] +pub struct NewDeploymentPackage { + pub version: String, + pub software_name: String, + pub description: Option, + pub path: String, +} + +#[derive(Debug, Serialize, Deserialize, FromRow)] +pub struct DeploymentTask { + pub id: i32, + pub package_id: i32, + pub target_type: String, + pub target_id: Option, +} + +#[derive(Debug, Deserialize)] +pub struct NewDeploymentTask { + pub package_id: i32, + pub target_type: String, + pub target_id: Option, +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum TargetType { + SingleServer, + ServerGroup, + All, +} + +impl TargetType { + pub fn from_str(s: &str) -> Option { + match s { + "单台服务器" => Some(TargetType::SingleServer), + "服务器组" => Some(TargetType::ServerGroup), + "所有" => Some(TargetType::All), + _ => None, + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ApiResponse { + pub code: u16, + pub status: String, + pub message: String, + pub data: Option, +} diff --git a/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/src/server_group_members.rs b/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/src/server_group_members.rs new file mode 100644 index 000000000..99697fe13 --- /dev/null +++ b/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/src/server_group_members.rs @@ -0,0 +1,142 @@ +// server_group_members.rs + +use rocket::{get, post, delete, serde::json::Json, State}; +use crate::model::{ServerGroupMember, BatchAddServersRequest,BatchRemoveServersRequest, ApiResponse, Server, ServerGroup}; +use sqlx::{PgPool, query_as, query}; +use rocket::http::Status; + +// 通用接口:添加一个或多个服务器到服务器组 +#[post("/add_servers_to_group", data = "")] +pub async fn add_servers_to_group( + db: &State, + request: Json +) -> Result>>, Status> { + // 查找目标服务器组 + let group = query_as::<_, ServerGroup>("SELECT * FROM server_groups WHERE description = $1") + .bind(&request.group_description) + .fetch_one(&**db) + .await + .map_err(|_| Status::NotFound)?; + + let mut added_members = vec![]; // 存储成功添加的成员记录 + + // 逐个查找服务器并添加到组 + for ip in &request.ip_addresses { + // 查找服务器 + let server = query_as::<_, Server>("SELECT * FROM servers WHERE ip_address = $1") + .bind(ip) + .fetch_one(&**db) + .await + .map_err(|_| Status::NotFound)?; + + // 插入关联关系 + match query_as::<_, ServerGroupMember>( + "INSERT INTO server_group_members (server_id, group_id) VALUES ($1, $2) RETURNING *" + ) + .bind(server.id) + .bind(group.id) + .fetch_one(&**db) + .await { + Ok(member) => added_members.push(member), // 成功时将成员添加到结果集中 + Err(_) => continue, // 如果插入失败,继续处理下一个 + } + } + + // 如果没有成功添加的成员,返回错误 + if added_members.is_empty() { + return Err(Status::UnprocessableEntity); + } + + // 返回成功添加的成员记录 + Ok(Json(ApiResponse { + code: 200, + status: "success".to_string(), + message: format!("Successfully added {} servers to group.", added_members.len()), + data: Some(added_members), + })) +} + +// 查看服务器组中的所有服务器 +#[get("/server_group_members/")] +pub async fn get_servers_in_group( + db: &State, + description: String +) -> Result>>, Status> { + // 查询服务器组 + let group = query_as::<_, ServerGroup>("SELECT * FROM server_groups WHERE description = $1") + .bind(&description) + .fetch_one(&**db) + .await + .map_err(|_| Status::NotFound)?; + + // 查询该组下的所有服务器 + let result = query_as::<_, Server>( + "SELECT s.* FROM servers s + JOIN server_group_members m ON s.id = m.server_id + WHERE m.group_id = $1" + ) + .bind(group.id) + .fetch_all(&**db) + .await; + + match result { + Ok(servers) => Ok(Json(ApiResponse { + code: 200, + status: "success".to_string(), + message: "Servers retrieved successfully".to_string(), + data: Some(servers), + })), + Err(_) => Err(Status::InternalServerError), + } +} + +// 批量从服务器组中移除服务器 +#[delete("/remove_servers_from_group", data = "")] +pub async fn remove_servers_from_group( + db: &State, + request: Json +) -> Result>, Status> { + // 查找目标服务器组 + let group = query_as::<_, ServerGroup>("SELECT * FROM server_groups WHERE description = $1") + .bind(&request.group_description) + .fetch_one(&**db) + .await + .map_err(|_| Status::NotFound)?; + + let mut removed_count = 0; // 记录成功移除的服务器数量 + + // 逐个查找服务器并移除其与服务器组的关系 + for ip in &request.ip_addresses { + // 查找服务器 + let server = query_as::<_, Server>("SELECT * FROM servers WHERE ip_address = $1") + .bind(ip) + .fetch_one(&**db) + .await + .map_err(|_| Status::NotFound)?; + + // 删除关联关系 + match query( + "DELETE FROM server_group_members WHERE server_id = $1 AND group_id = $2" + ) + .bind(server.id) + .bind(group.id) + .execute(&**db) + .await { + Ok(_) => removed_count += 1, // 成功时增加计数 + Err(_) => continue, // 如果删除失败,继续处理下一个 + } + } + + // 如果没有成功移除的服务器,返回错误 + if removed_count == 0 { + return Err(Status::UnprocessableEntity); + } + + // 返回成功移除的结果 + Ok(Json(ApiResponse { + code: 200, + status: "success".to_string(), + message: format!("Successfully removed {} servers from group.", removed_count), + data: None, + })) +} \ No newline at end of file diff --git a/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/src/server_groups.rs b/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/src/server_groups.rs new file mode 100644 index 000000000..eb143da34 --- /dev/null +++ b/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/src/server_groups.rs @@ -0,0 +1,64 @@ + +use rocket::{get, post, delete, serde::json::Json, State}; +use crate::model::{ServerGroup, NewServerGroup, ApiResponse}; +use sqlx::{PgPool, query_as, query}; +use rocket::http::Status; + +// 增加服务器组 +#[post("/server_groups", data = "")] +pub async fn add_server_group(db: &State, new_group: Json) -> Result>, Status> { + let result = query_as::<_, ServerGroup>( + "INSERT INTO server_groups (description) VALUES ($1) RETURNING *" + ) + .bind(&new_group.description) + .fetch_one(&**db) + .await; + + match result { + Ok(group) => Ok(Json(ApiResponse { + code: 200, + status: "success".to_string(), + message: "Server group added successfully".to_string(), + data: Some(group), + })), + Err(_) => Err(Status::InternalServerError), + } +} + +// 查询所有服务器组 +#[get("/server_groups")] +pub async fn get_server_groups(db: &State) -> Result>>, Status> { + let result = query_as::<_, ServerGroup>("SELECT * FROM server_groups") + .fetch_all(&**db) + .await; + + match result { + Ok(groups) => Ok(Json(ApiResponse { + code: 200, + status: "success".to_string(), + message: "Server groups retrieved successfully".to_string(), + data: Some(groups), + })), + Err(_) => Err(Status::InternalServerError), + } +} + + +// 删除服务器组 +#[delete("/server_groups/")] +pub async fn delete_server_group(db: &State, id: i32) -> Result>, Status> { + let result = query("DELETE FROM server_groups WHERE id = $1") + .bind(id) + .execute(&**db) + .await; + + match result { + Ok(_) => Ok(Json(ApiResponse { + code: 200, + status: "success".to_string(), + message: "Server group deleted successfully".to_string(), + data: None, + })), + Err(_) => Err(Status::InternalServerError), + } +} diff --git a/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/src/servers.rs b/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/src/servers.rs new file mode 100644 index 000000000..7012ba78c --- /dev/null +++ b/eBPF_Supermarket/Auto_Cluster_Deployer/restful_api/src/servers.rs @@ -0,0 +1,63 @@ +use rocket::{get, post,delete, serde::json::Json, State}; +use crate::model::{Server, NewServer, ApiResponse}; +use sqlx::{PgPool, query_as, query}; +use rocket::http::Status; + +// 增加服务器 +#[post("/servers", data = "")] +pub async fn add_server(db: &State, new_server: Json) -> Result>, Status> { + let result = query_as::<_, Server>( + "INSERT INTO servers (ip_address) VALUES ($1) RETURNING *" + ) + .bind(&new_server.ip_address) + .fetch_one(&**db) + .await; + + match result { + Ok(server) => Ok(Json(ApiResponse { + code: 200, + status: "success".to_string(), + message: "Server added successfully".to_string(), + data: Some(server), + })), + Err(_) => Err(Status::InternalServerError), + } +} + +// 查询所有服务器 +#[get("/servers")] +pub async fn get_servers(db: &State) -> Result>>, Status> { + let result = query_as::<_, Server>("SELECT * FROM servers") + .fetch_all(&**db) + .await; + + match result { + Ok(servers) => Ok(Json(ApiResponse { + code: 200, + status: "success".to_string(), + message: "Servers retrieved successfully".to_string(), + data: Some(servers), + })), + Err(_) => Err(Status::InternalServerError), + } +} + + +#[delete("/servers/")] +pub async fn delete_server(db: &State, ip_address: &str) -> Result>, Status> { + let result = query("DELETE FROM servers WHERE ip_address = $1") + .bind(ip_address) + .execute(&**db) + .await; + + match result { + Ok(_) => Ok(Json(ApiResponse { + code: 200, + status: "success".to_string(), + message: "Server deleted successfully".to_string(), + data: None, + })), + Err(_) => Err(Status::InternalServerError), + } +} + diff --git a/eBPF_Supermarket/Auto_Cluster_Deployer/tonic/server.rs b/eBPF_Supermarket/Auto_Cluster_Deployer/tonic/server.rs new file mode 100644 index 000000000..c0b54e2ea --- /dev/null +++ b/eBPF_Supermarket/Auto_Cluster_Deployer/tonic/server.rs @@ -0,0 +1,357 @@ +use sacontrol::{Empty, SaInfo, Ack, FileChunk, PackageRequest, IpRequest, PackageInfoResponse}; +use sacontrol::sa_control_server::{SaControl, SaControlServer}; +use tonic::{Request, Response, Status}; +use tonic::transport::Server; +use tokio_stream::wrappers::ReceiverStream; +use tokio::sync::mpsc; +use tokio::io::{AsyncReadExt, BufReader}; +use tokio_postgres::NoTls; +use serde::{Deserialize, Serialize}; +use tokio::fs::File as TokioFile; + +pub mod sacontrol { + tonic::include_proto!("sacontrol"); +} + +#[derive(Debug, Default)] +pub struct MySAControl; + +#[derive(Debug, Deserialize, Serialize)] +struct Config { + program_directory: Vec, +} + +#[derive(Debug, Deserialize, Serialize)] +struct ProgramDirectory { + name: String, + subdirectories: Vec, + paths: Vec, +} + +#[derive(Debug, Deserialize, Serialize)] +struct TasksConfig { + task_list: Vec, +} + +#[derive(Debug, Deserialize, Serialize)] +struct TaskOutput { + task: String, + output: String, + success: bool, +} + +#[derive(Debug, Deserialize, Serialize)] +struct Agent { + id: String, + name: String, +} + +#[tonic::async_trait] +impl SaControl for MySAControl { + async fn deploy_packages( + &self, + _request: Request, + ) -> Result, Status> { + // 连接到 PostgreSQL 数据库 + let (client, connection) = + tokio_postgres::connect("host=localhost port=5432 dbname=db user=zxy password=123456", NoTls) + .await + .map_err(|e| Status::internal(format!("数据库连接失败: {}", e)))?; + + // 启动异步任务处理数据库连接 + tokio::spawn(async move { + if let Err(e) = connection.await { + eprintln!("数据库连接错误: {}", e); + } + }); + + // 查询 deployment_tasks 表 + let query_tasks = "SELECT id, package_id, target_type, target_id FROM deployment_tasks WHERE is_deployed != 't'"; + let rows = client.query(query_tasks, &[]) + .await + .map_err(|e| Status::internal(format!("数据库查询失败: {}", e)))?; + + for row in rows { + let id: i32 = row.get("id"); + let package_id: i32 = row.get("package_id"); + let target_type: String = row.get("target_type"); + let target_id: Option = row.get("target_id"); + + if target_type == "所有" { + // 对于 "所有",获取所有服务器的 IP 地址并插入到 package_deployment 表 + let query_servers = "SELECT ip_address FROM servers"; + let server_rows = client.query(query_servers, &[]) + .await + .map_err(|e| Status::internal(format!("查询 servers 失败: {}", e)))?; + + for server_row in server_rows { + let ip_address: String = server_row.get("ip_address"); + + // 插入到 package_deployment 表 + client.execute( + "INSERT INTO package_deployment (package_id, ip_address) VALUES ($1, $2) + ON CONFLICT (ip_address) DO UPDATE SET package_id = EXCLUDED.package_id", + &[&package_id, &ip_address], + ).await.map_err(|e| Status::internal(format!("插入 package_deployment 失败: {}", e)))?; + } + } else if target_type == "服务器组" { + // 查找 server_group_members 中的 server_id + if let Some(target_id) = target_id { + let query_group_members = "SELECT server_id FROM server_group_members WHERE group_id = $1"; + let group_rows = client.query(query_group_members, &[&target_id]) + .await + .map_err(|e| Status::internal(format!("查询 server_group_members 失败: {}", e)))?; + + for group_row in group_rows { + let server_id: i32 = group_row.get("server_id"); + + // 查找 servers 中的 ip_address + let query_servers = "SELECT ip_address FROM servers WHERE id = $1"; + let server_rows = client.query(query_servers, &[&server_id]) + .await + .map_err(|e| Status::internal(format!("查询 servers 失败: {}", e)))?; + + if let Some(server_row) = server_rows.get(0) { + let ip_address: String = server_row.get("ip_address"); + + // 将结果插入到 package_deployment 表中 + client.execute( + "INSERT INTO package_deployment (package_id, ip_address) VALUES ($1, $2) + ON CONFLICT (ip_address) DO UPDATE SET package_id = EXCLUDED.package_id", + &[&package_id, &ip_address], + ).await.map_err(|e| Status::internal(format!("插入 package_deployment 失败: {}", e)))?; + } + } + } + } else if target_type == "单台服务器" { + // 查找 servers 中的 ip_address + if let Some(target_id) = target_id { + let query_servers = "SELECT ip_address FROM servers WHERE id = $1"; + let server_rows = client.query(query_servers, &[&target_id]) + .await + .map_err(|e| Status::internal(format!("查询 servers 失败: {}", e)))?; + + if let Some(server_row) = server_rows.get(0) { + let ip_address: String = server_row.get("ip_address"); + + // 将结果插入到 package_deployment 表中 + client.execute( + "INSERT INTO package_deployment (package_id, ip_address) VALUES ($1, $2) + ON CONFLICT (ip_address) DO UPDATE SET package_id = EXCLUDED.package_id", + &[&package_id, &ip_address], + ).await.map_err(|e| Status::internal(format!("插入 package_deployment 失败: {}", e)))?; + } + } + } + + // 更新 deployment_tasks 表中的 is_deployed 为 true + client.execute( + "UPDATE deployment_tasks SET is_deployed = 't' WHERE id = $1", + &[&id], + ).await.map_err(|e| Status::internal(format!("更新 deployment_tasks 失败: {}", e)))?; + } + + // 返回成功的 Ack 响应 + Ok(Response::new(Ack { success: true })) + } + + async fn get_package_info_by_ip( + &self, + request: Request, + ) -> Result, Status> { + let ip_address = request.into_inner().ip_address; + + // 连接到 PostgreSQL 数据库 + let (client, connection) = + tokio_postgres::connect("host=localhost port=5432 dbname=db user=zxy password=123456", NoTls) + .await + .map_err(|e| Status::internal(format!("数据库连接失败: {}", e)))?; + + // 启动异步任务处理数据库连接 + tokio::spawn(async move { + if let Err(e) = connection.await { + eprintln!("数据库连接错误: {}", e); + } + }); + + // 创建一个用于超时的计时器 + let timeout = tokio::time::sleep(tokio::time::Duration::from_secs(5)); + + tokio::select! { + _ = timeout => { + // 超时后返回 ID 为 -1 的响应 + Ok(Response::new(PackageInfoResponse { + id: -1, + version: "".to_string(), + software_name: "".to_string(), + description: "".to_string(), + })) + } + result = async { + loop { + // 查询 package_deployment 表 + let query_package_deployment = "SELECT package_id FROM package_deployment WHERE ip_address = $1"; + let rows = client.query(query_package_deployment, &[&ip_address]) + .await + .map_err(|e| Status::internal(format!("查询 package_deployment 失败: {}", e)))?; + + if let Some(row) = rows.get(0) { + let package_id: i32 = row.get(0); + + // 查询 deployment_packages 表,获取 id、version、software_name 和 description + let query_deployment_packages = "SELECT id, version, software_name, description FROM deployment_packages WHERE id = $1"; + let rows = client.query(query_deployment_packages, &[&package_id]) + .await + .map_err(|e| Status::internal(format!("查询 deployment_packages 失败: {}", e)))?; + + // 处理查询结果 + if let Some(row) = rows.get(0) { + let id: i32 = row.get("id"); // 获取 id + let version: String = row.get("version"); + let software_name: String = row.get("software_name"); + let description: String = row.get("description"); + + // 删除 package_deployment 表中与该 IP 地址相关的记录 + client.execute( + "DELETE FROM package_deployment WHERE ip_address = $1", + &[&ip_address], + ) + .await + .map_err(|e| Status::internal(format!("删除 package_deployment 记录失败: {}", e)))?; + + // 返回查询结果 + return Ok(Response::new(PackageInfoResponse { + id, + version, + software_name, + description, + })); + } else { + // 没有找到包信息,继续循环 + tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + } + } else { + // 没有找到相关的包部署记录,继续循环 + tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + } + } + } => result, + } + } + + async fn sa_regist( + &self, + request: Request, + ) -> Result, Status> { + let sa_info = request.into_inner(); + println!("Received registration from Sensor Agent: server_ip={}, package_id={}", sa_info.server_ip, sa_info.category); + Ok(Response::new(Ack { success: true })) + } + + type SendPackageFileStream = ReceiverStream>; + + async fn send_package_file( + &self, + request: Request, + ) -> Result, Status> { + let package_id = request.into_inner().category; // 获取包的 ID + + // 连接到 PostgreSQL 数据库 + let (client, connection) = + tokio_postgres::connect("host=localhost port=5432 dbname=db user=zxy password=123456", NoTls) + .await + .map_err(|e| Status::internal(format!("数据库连接失败: {}", e)))?; + + // 启动异步任务处理数据库连接 + tokio::spawn(async move { + if let Err(e) = connection.await { + eprintln!("数据库连接错误: {}", e); + } + }); + + // 查询数据库以获取包的信息 + let row = client.query_one( + "SELECT path FROM deployment_packages WHERE id = $1", + &[&package_id], + ) + .await + .map_err(|e| Status::internal(format!("数据库查询失败: {}", e)))?; + + let package_path: String = row.get("path"); + let install_script_path = "install.sh".to_string(); + + // 准备发送的文件列表 + let files_to_send = vec![ + (package_path, "deb"), + (install_script_path, "sh"), + ]; + + let (tx, rx) = mpsc::channel(4); + + // 启动任务来读取每个文件并发送数据块 + tokio::spawn(async move { + for (file_path, file_type) in files_to_send { + // 打开文件 + match TokioFile::open(&file_path).await { + Ok(file) => { + let mut reader = BufReader::new(file); + let mut buffer = [0u8; 1024]; + + // 读取文件并发送块 + loop { + match reader.read(&mut buffer).await { + Ok(0) => break, // 文件结束 + Ok(n) => { + // 发送文件块 + let chunk = FileChunk { + content: buffer[..n].to_vec(), + file_type: file_type.to_string(), + }; + if tx.send(Ok(chunk)).await.is_err() { + eprintln!("发送文件块失败: {}", file_path); + break; + } + } + Err(e) => { + eprintln!("读取文件时出错: {} - {}", file_path, e); + break; + } + } + } + } + Err(e) => { + eprintln!("无法打开文件: {} - {}", file_path, e); + } + } + } + }); + + // 从通道接收器创建 gRPC 流 + let stream = ReceiverStream::new(rx); + + // 返回带有流的响应 + Ok(Response::new(stream)) + } +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + tracing_subscriber::fmt::init(); + + // 将服务器地址改为实际的服务器内网 IP 地址和端口 + let addr = "192.168.31.145:3000".parse().unwrap(); + + let sa_control = MySAControl::default(); + let sa_control = SaControlServer::new(sa_control); + + println!("SaControlServer listening on {}", addr); + + Server::builder() + // GrpcWeb is over http1 so we must enable it. + .accept_http1(true) + .add_service(tonic_web::enable(sa_control)) + .serve(addr) + .await?; + + Ok(()) +} diff --git a/eBPF_Supermarket/CPU_Subsystem/blazesym b/eBPF_Supermarket/CPU_Subsystem/blazesym index 90eb4e08a..dc343b7e9 160000 --- a/eBPF_Supermarket/CPU_Subsystem/blazesym +++ b/eBPF_Supermarket/CPU_Subsystem/blazesym @@ -1 +1 @@ -Subproject commit 90eb4e08a25d702776f35b6b7fea97900213e3d5 +Subproject commit dc343b7e99f7194f41160a8fc9f6f4a1cf6d4485 diff --git a/eBPF_Supermarket/CPU_Subsystem/bpftool b/eBPF_Supermarket/CPU_Subsystem/bpftool index 06c61eccd..8485b9fba 160000 --- a/eBPF_Supermarket/CPU_Subsystem/bpftool +++ b/eBPF_Supermarket/CPU_Subsystem/bpftool @@ -1 +1 @@ -Subproject commit 06c61eccd3b8a6ff3df3e451a2a93058913124fc +Subproject commit 8485b9fba9b3bb3bd311b00632d2d22c0eee2e13 diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/Makefile b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/Makefile index efe1a9de9..1e1d4eaab 100644 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/Makefile +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/Makefile @@ -38,12 +38,15 @@ VMLINUX := ../vmlinux/$(ARCH)/vmlinux.h # Use our own libbpf API headers and Linux UAPI headers distributed with # libbpf to avoid dependency on system-wide headers, which could be missing or # outdated -INCLUDES := -I$(OUTPUT) -I../../../libbpf/include/uapi -I$(dir $(VMLINUX)) -I$(LIBBLAZESYM_INC) -I./ +INCLUDES := -I$(OUTPUT) -I../../../libbpf/include/uapi -I$(dir $(VMLINUX)) -I$(LIBBLAZESYM_INC) -I./include CFLAGS := -g -Wall ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) -APPS =cs_delay sar sc_delay preempt schedule_delay mq_delay +APPS =cs_delay sar sc_delay preempt schedule_delay mq_delay mutrace TARGETS=cpu_watcher +CONTROLLER := controller + +SRC_DIR = ./include # Get Clang's default includes on this system. We'll explicitly add these dirs @@ -79,12 +82,12 @@ $(call allow-override,CC,$(CROSS_COMPILE)cc) $(call allow-override,LD,$(CROSS_COMPILE)ld) .PHONY: all -all: $(TARGETS) +all: $(CONTROLLER) $(TARGETS) .PHONY: clean clean: $(call msg,CLEAN) - $(Q)rm -rf $(OUTPUT) $(TARGETS) + $(Q)rm -rf $(OUTPUT) $(TARGETS) $(CONTROLLER) $(OUTPUT) $(OUTPUT)/libbpf $(BPFTOOL_OUTPUT): $(call msg,MKDIR,$@) @@ -112,7 +115,7 @@ $(LIBBLAZESYM_OBJ): $(LIBBLAZESYM_SRC)/target/release/libblazesym.a | $(OUTPUT) $(Q)cp $(LIBBLAZESYM_SRC)/target/release/libblazesym.a $@ # Build BPF code -$(OUTPUT)/%.bpf.o: %.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT) $(BPFTOOL) +$(OUTPUT)/%.bpf.o: bpf/%.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT) $(BPFTOOL) $(call msg,BPF,$@) $(Q)$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) \ $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) \ @@ -126,14 +129,40 @@ $(APPS): %: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL) $(Q)$(BPFTOOL) gen skeleton $< > $(OUTPUT)/$@.skel.h # Build user-space code +$(OUTPUT)/%.o: $(SRC_DIR)/%.c | $(OUTPUT) + $(call msg,CC,$@) + $(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ + +$(OUTPUT)/%.o: $(CONTROLLER).c | $(OUTPUT) + $(call msg,CC,$@) + $(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ + $(OUTPUT)/$(TARGETS).o: $(TARGETS).c $(APPS) | $(OUTPUT) $(call msg,CC,$@) $(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $(filter %.c,$^) -o $@ # Build application binary +$(CONTROLLER): %: $(OUTPUT)/%.o $(COMMON_OBJ) $(LIBBPF_OBJ) | $(OUTPUT) + $(call msg,BINARY,$@) + $(Q)$(CC) $^ $(ALL_LDFLAGS) -lstdc++ -lelf -lz -o $@ + $(TARGETS): %: $(OUTPUT)/%.o $(COMMON_OBJ) $(LIBBPF_OBJ) | $(OUTPUT) $(call msg,BINARY,$@) - $(Q)$(CC) $(CFLAGS) $^ $(ALL_LDFLAGS) -lelf -lz -o $@ + $(Q)$(CC) $(CFLAGS) $^ $(ALL_LDFLAGS) -lstdc++ -lelf -lz -o $@ + +SUCCESS_MESSAGE: + @echo "\e[38;2;255;0;0m __ __ \e[0m" + @echo "\e[38;2;255;128;0m _________ __ __ _ ______ _/ /______/ /_ ___ _____\e[0m" + @echo "\e[38;2;255;255;0m / ___/ __ \/ / / / | | /| / / __ / __/ ___/ __ \/ _ \/ ___/\e[0m" + @echo "\e[38;2;128;255;0m/ /__/ /_/ / /_/ / | |/ |/ / /_/ / /_/ /__/ / / / __/ / \e[0m" + @echo "\e[38;2;0;255;0m\___/ .___/\__,_/ |__/|__/\__,_/\__/\___/_/ /_/\___/_/ \e[0m" + @echo "\e[38;2;0;255;128m /_/ \e[0m" + @echo "\e[38;2;0;255;255mSuccessful to compile cpu_watcher tools: \e[0m" + @echo "\e[38;2;0;255;255mPlease start your use ~ \e[0m" + + +all: $(TARGETS) SUCCESS_MESSAGE + # delete failed targets .DELETE_ON_ERROR: diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/README.md b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/README.md index f64e4a8a8..ce938ded0 100644 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/README.md +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/README.md @@ -29,14 +29,20 @@ make clean 清除生成文件 | 参数 | 描述 | | :----------------: | :----------------------------------------: | -| -s :SAR | 实时采集SAR的各项指标,每秒输出一次 | +| -s :SAR | 实时采集SAR的各项指标 | +| -i:interval | 修改SAR功能的输出间隔 | +| -P:percent | 按照百分比输出SAR功能的各项指标 | | -p:preempt_time | 实时采集当前系统的每次抢占调度详细信息 | | -d:schedule_delay | 实时采集当前系统的调度时延 | | -S:syscall_delay | 实时采集当前系统调用时间 | | -m:mq_delay | 实时采集当前消息队列通信时延 | | -c:cs_delay | 实时对内核函数schedule()的执行时长进行测试 | -### 1.SAR 统计功能(每秒输出一次): +### 1.SAR 统计功能: + +```shell +./cpu_watcher -s +``` #### 输出效果: @@ -56,6 +62,16 @@ make clean 清除生成文件 16:18:14 43 1032 1 577 19513 1704 3919 26 10 30 ``` +​ 使用参数i可以调整输出间隔,默认为1s,参数p可以按照cpu核数和自定义的输出间隔对数据进行归一化,并以百分比的形式输出,且大于60%的数据会标红输出: + +```shell +./cpu_watcher -s -i 2 -P +``` +#### 输出效果: + +![image13](image/image13.png) + + 对上述参数的解释: - `proc/s`: 每秒创建的进程数,此数值是通过fork数来统计的。 @@ -68,6 +84,10 @@ make clean 清除生成文件 - `sysc/ms`: CPU执行用户程序系统调用(`syscall`)所占用的时间,是所有CPU的叠加。 - ` utime/ms`:CPU执行普通用户进程时,花在用户态的时间,是所有CPU的叠加。 +原理介绍: + +[libbpf_sar工具原理分析](docs/libbpf_sar.md) + ### **2.统计抢占调度时间:** ​ 统计系统中发生抢占调度的情况,包括抢占进程的`pid`与进程名,以及被强占进程的`pid`,和本次抢占时间,单位纳秒。 @@ -95,21 +115,22 @@ node 14221 2589 3355 ### 3.**统计调度延迟:** -​ 分析系统中进程调度的延迟情况,提供相关统计数据,输出包括当前系统的最大调度延迟、最小调度延迟、平均调度延迟。 +​ 分析系统中进程调度的延迟情况,提供相关统计数据,输出包括当前系统的最大调度延迟、最小调度延迟、平均调度延迟,以及对应进程的名字。 #### 输出效果: ``` - TIME avg_delay/μs max_delay/μs min_delay/μs -17:31:28 35.005000 97.663000 9.399000 -17:31:29 326.518000 12618.465000 7.994000 -17:31:30 455.837000 217053.545000 6.462000 -17:31:31 422.582000 217053.545000 6.462000 -17:31:32 382.627000 217053.545000 6.462000 -17:31:33 360.499000 217053.545000 6.462000 -17:31:34 364.805000 217053.545000 6.462000 -17:31:35 362.039000 217053.545000 6.462000 -17:31:36 373.751000 217053.545000 6.462000 + TIME avg_delay/μs max_delay/μs max_proc_name min_delay/μs min_proc_name +22:06:02 642.770000 60711.755000 node 5.227000 cpu_watcher +22:06:03 510.041000 60711.755000 node 5.227000 cpu_watcher +22:06:04 491.107000 60711.755000 node 5.227000 cpu_watcher +22:06:05 468.128000 60711.755000 node 5.227000 cpu_watcher +22:06:06 454.244000 60711.755000 node 5.227000 cpu_watcher +22:06:07 472.455000 61931.163000 node 5.227000 cpu_watcher +22:06:08 441.756000 61931.163000 node 3.360000 cpu_watcher +22:06:09 442.631000 61931.163000 node 3.360000 cpu_watcher +22:06:10 407.389000 61931.163000 node 2.549000 cpu_watcher +22:06:11 426.593000 62247.982000 node 2.549000 cpu_watcher ``` 原理介绍: @@ -214,9 +235,11 @@ per_len = 1000 ​ 获取内核全局变量,直接从内核全局变量读取信息。如proc/s就是通过直接读取total_forks内核全局变量来计算每秒产生进程数的。 +## 五、cpu_watcher可视化 +[cpu_watcher可视化指南](docs/cpu_watcher_vis_guide.md) -## 五、未来展望 +## 六、未来展望 目前`cpu_watcher`工具的总体框架已经完成,工具所能满足的功能已覆盖CPU所涉及的大部分性能指标。下一阶段,本工具将从以下几个方向进行开发和优化: @@ -229,4 +252,4 @@ per_len = 1000 如果你也对cpu_watcher或ebpf感兴趣,欢迎加入我们一起开发cpu_watcher工具,希望我们可以共同成长。 -**cpu_watcher负责人:** albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com +**cpu_watcher负责人:** albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com \ No newline at end of file diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/cs_delay.bpf.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/cs_delay.bpf.c new file mode 100644 index 000000000..9cd31fc3d --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/cs_delay.bpf.c @@ -0,0 +1,84 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com + +#include +#include +#include +#include +#include "cpu_watcher.h" + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; +const int ctrl_key = 0; +//记录时间戳; +BPF_ARRAY(start,int,u64,1); +BPF_ARRAY(cs_ctrl_map,int,struct cs_ctrl,1); +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} rb SEC(".maps"); + +static inline struct cs_ctrl *get_cs_ctrl(void) { + struct cs_ctrl *cs_ctrl; + cs_ctrl = bpf_map_lookup_elem(&cs_ctrl_map, &ctrl_key); + if (!cs_ctrl || !cs_ctrl->cs_func) { + return NULL; + } + return cs_ctrl; +} + +SEC("kprobe/schedule") +int BPF_KPROBE(schedule) +{ + struct cs_ctrl *cs_ctrl = get_cs_ctrl(); + if (!cs_ctrl) { + return 0; + } + u64 t1; + t1 = bpf_ktime_get_ns()/1000; + int key =0; + bpf_map_update_elem(&start,&key,&t1,BPF_ANY); + return 0; +} + +SEC("kretprobe/schedule") +int BPF_KRETPROBE(schedule_exit) +{ + struct cs_ctrl *cs_ctrl = get_cs_ctrl(); + if (!cs_ctrl) { + return 0; + } + u64 t2 = bpf_ktime_get_ns()/1000; + u64 t1,delay; + int key = 0; + u64 *val = bpf_map_lookup_elem(&start,&key); + if (val != 0) + { + t1 = *val; + delay = t2 - t1; + bpf_map_delete_elem(&start, &key); + }else{ + return 0; + } + struct event *e; + e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); + if (!e) return 0; + e->t1=t1; + e->t2=t2; + e->delay=delay; + bpf_ringbuf_submit(e, 0); + return 0; +} + diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/mq_delay.bpf.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/mq_delay.bpf.c new file mode 100644 index 000000000..d9e94da36 --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/mq_delay.bpf.c @@ -0,0 +1,269 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com + +#include "vmlinux.h" +#include //包含了BPF 辅助函数 +#include +#include + +#include "cpu_watcher.h" + + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; +const int ctrl_key = 0; +BPF_HASH(send_msg1,pid_t,struct send_events,1024);//记录pid->u_msg_ptr的关系;do_mq_timedsend入参 +BPF_HASH(send_msg2,u64,struct send_events,1024);//记录msg->time的关系; +BPF_HASH(rcv_msg1,pid_t,struct rcv_events,1024);//记录pid->u_msg_ptr的关系;do_mq_timedsend入参 +BPF_ARRAY(mq_ctrl_map,int,struct mq_ctrl,1); +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} rb SEC(".maps"); + +static inline struct mq_ctrl *get_mq_ctrl(void) { + struct mq_ctrl *mq_ctrl; + mq_ctrl = bpf_map_lookup_elem(&mq_ctrl_map, &ctrl_key); + if (!mq_ctrl || !mq_ctrl->mq_func) { + return NULL; + } + return mq_ctrl; +} + + +// int print_send_info(struct send_events * mq_send_info,int flag){ +// bpf_printk("---------------------test----------------------------test--------------------------test--------------------------------------------test---------------------test---------------------test\n"); +// bpf_printk("send_msg_prio: %-8lu\n",mq_send_info->msg_prio); +// bpf_printk("mqdes: %-08lu send_pid: %-08lu send_enter_time: %-16lu\n",mq_send_info->mqdes,mq_send_info->send_pid,mq_send_info->send_enter_time); +// if(flag > 0){ +// bpf_printk("u_msg_ptr: 0x%08lx src: 0x%08lx\n",mq_send_info->u_msg_ptr,mq_send_info->src); +// if(flag==2) bpf_printk("Key_msg_ptr: 0x%08lx \n",mq_send_info->Key_msg_ptr); +// } +// bpf_printk("---------------------test----------------------------test--------------------------test--------------------------------------------test---------------------test---------------------test\n"); +// return 0; +// } + +// int print_rcv_info(struct rcv_events * mq_rcv_info,int flag){ +// bpf_printk("---------------------test----------------------------test--------------------------test--------------------------------------------test---------------------test---------------------test\n"); +// bpf_printk("rcv_msg_prio: %-8lu\n",mq_rcv_info->msg_prio); +// bpf_printk("mqdes: %-08lu rcv_pid: %-08lu rcv_enter_time: %-16lu\n",mq_rcv_info->mqdes,mq_rcv_info->rcv_pid,mq_rcv_info->rcv_enter_time); +// if(flag > 0){ +// bpf_printk("u_msg_ptr: 0x%08lx dest: 0x%08lx\n",mq_rcv_info->u_msg_ptr,mq_rcv_info->dest); +// if(flag==2) bpf_printk("Key_msg_ptr: 0x%08lx \n",mq_rcv_info->Key_msg_ptr); +// } +// bpf_printk("---------------------test----------------------------test--------------------------test--------------------------------------------test---------------------test---------------------test\n"); +// return 0; +// } + + +/*获取 mq_send_info -> send_time send_pid mdqes u_msg_ptr msg_len msg_prio*/ +SEC("kprobe/do_mq_timedsend") +int BPF_KPROBE(mq_timedsend,mqd_t mqdes, const char *u_msg_ptr, + size_t msg_len, unsigned int msg_prio, + struct timespec64 *ts) +{ + struct mq_ctrl *mq_ctrl = get_mq_ctrl(); + if (!mq_ctrl) { + return 0; + } + u64 send_enter_time = bpf_ktime_get_ns();//开始发送信息时间; + int pid = bpf_get_current_pid_tgid();//发送端pid + + /*将消息暂存至send_events结构体中*/ + struct send_events mq_send_info ={}; + mq_send_info.send_pid= pid; + mq_send_info.send_enter_time = send_enter_time; + mq_send_info.mqdes= mqdes; + mq_send_info.msg_len = msg_len; + mq_send_info.msg_prio = msg_prio; + mq_send_info.u_msg_ptr = u_msg_ptr; + + bpf_map_update_elem(&send_msg1, &pid, &mq_send_info, BPF_ANY);//pid->u_msg_ptr + return 0; +} + +/*仅获取mq_send_info -> src*/ +SEC("kprobe/load_msg") +int BPF_KPROBE(load_msg_enter,const void *src, size_t len){ + struct mq_ctrl *mq_ctrl = get_mq_ctrl(); + if (!mq_ctrl) { + return 0; + } + int pid = bpf_get_current_pid_tgid();//发送端pid + /*记录load入参src*/ + struct send_events *mq_send_info = bpf_map_lookup_elem(&send_msg1, &pid); + if(!mq_send_info){ + return 0; + }else{ + mq_send_info->src = src; + } + return 0; +} + +/*获取消息块作为key,并建立 message -> mq_send_info 的哈希表*/ +SEC("kretprobe/load_msg") +int BPF_KRETPROBE(load_msg_exit,void *ret){ + struct mq_ctrl *mq_ctrl = get_mq_ctrl(); + if (!mq_ctrl) { + return 0; + } + int pid = bpf_get_current_pid_tgid();//发送端pid + /*构建消息块结构体,作为key*/ + struct send_events *mq_send_info = bpf_map_lookup_elem(&send_msg1, &pid); + if(!mq_send_info){ + return 0; + } + + /*make key*/ + u64 Key_msg_ptr; + if(mq_send_info->u_msg_ptr == mq_send_info->src && pid == mq_send_info->send_pid){ + /*该load_msg为do_mq_timedsend调用*/ + Key_msg_ptr =(u64)ret; + mq_send_info->Key_msg_ptr = Key_msg_ptr; + } + else { + return 0; + } + /*已经获得key*/ + bpf_map_update_elem(&send_msg2, &Key_msg_ptr, mq_send_info, BPF_ANY);//key_messege->mq_send_info; + return 0; +} + +SEC("kretprobe/do_mq_timedsend") +int BPF_KRETPROBE(do_mq_timedsend_exit,void *ret) +{ + struct mq_ctrl *mq_ctrl = get_mq_ctrl(); + if (!mq_ctrl) { + return 0; + } + bpf_printk("do_mq_timedsend_exit----------------------------------------------------------------\n"); + u64 send_exit_time = bpf_ktime_get_ns();//开始发送信息时间; + int pid = bpf_get_current_pid_tgid();//发送端pid + u64 Key; + + struct send_events *mq_send_info1 = bpf_map_lookup_elem(&send_msg1, &pid); + if(!mq_send_info1){ + return 0; + } + Key = mq_send_info1->Key_msg_ptr; + struct send_events *mq_send_info2 = bpf_map_lookup_elem(&send_msg2, &Key); + if(!mq_send_info2){ + return 0; + } + mq_send_info2->send_exit_time = send_exit_time; + bpf_map_delete_elem(&send_msg1,&pid); + return 0; +} +/*-----------------------------------------------------------------------------发送端--------------------------------------------------------------------------------------------------------*/ +/* 分界 */ +/*-----------------------------------------------------------------------------接收端--------------------------------------------------------------------------------------------------------*/ +/*接收端*/ +SEC("kprobe/do_mq_timedreceive") +int BPF_KPROBE(mq_timedreceive_entry,mqd_t mqdes, const char __user *u_msg_ptr, + size_t msg_len, unsigned int msg_prio, + struct timespec64 *ts) +{ + struct mq_ctrl *mq_ctrl = get_mq_ctrl(); + if (!mq_ctrl) { + return 0; + } + u64 rcv_enter_time = bpf_ktime_get_ns(); + int pid = bpf_get_current_pid_tgid(); + + /*赋值*/ + struct rcv_events mq_rcv_info ={}; + mq_rcv_info.rcv_pid= pid; + mq_rcv_info.rcv_enter_time = rcv_enter_time; + mq_rcv_info.mqdes= mqdes; + mq_rcv_info.u_msg_ptr = u_msg_ptr; + bpf_map_update_elem(&rcv_msg1, &pid, &mq_rcv_info, BPF_ANY);//pid->u_msg_ptr + + return 0; +} + +SEC("kprobe/store_msg") +int BPF_KPROBE(store_msg,void __user *dest, struct msg_msg *msg, size_t len) +{ + struct mq_ctrl *mq_ctrl = get_mq_ctrl(); + if (!mq_ctrl) { + return 0; + } + int pid = bpf_get_current_pid_tgid(); + + /*make key*/ + u64 Key_msg_ptr = (u64)msg; + struct send_events *mq_send_info = bpf_map_lookup_elem(&send_msg2, &Key_msg_ptr); + if(!mq_send_info){ + return 0; + } + + struct rcv_events *mq_rcv_info = bpf_map_lookup_elem(&rcv_msg1, &pid); + if(!mq_rcv_info){ + return 0; + } + /*拿到mq_rcv_info*/ + if(dest == mq_rcv_info->u_msg_ptr && pid == mq_rcv_info->rcv_pid){ + mq_rcv_info->Key_msg_ptr = Key_msg_ptr; + mq_rcv_info->dest = dest; + mq_rcv_info->msg_prio = BPF_CORE_READ(msg,m_type); + mq_rcv_info->msg_len = BPF_CORE_READ(msg,m_ts); + }else{ + return 0; + } + return 0; +} + +SEC("kretprobe/do_mq_timedreceive") +int BPF_KRETPROBE(do_mq_timedreceive_exit,void *ret){ + struct mq_ctrl *mq_ctrl = get_mq_ctrl(); + if (!mq_ctrl) { + return 0; + } + u64 rcv_exit_time = bpf_ktime_get_ns(); + int pid = bpf_get_current_pid_tgid(); + u64 send_enter_time,delay; + u64 Key; + + /*获取发送端、接收端信息*/ + struct rcv_events *mq_rcv_info = bpf_map_lookup_elem(&rcv_msg1, &pid); + if(!mq_rcv_info){ + return 0; + } + Key = mq_rcv_info->Key_msg_ptr; + struct send_events *mq_send_info = bpf_map_lookup_elem(&send_msg2,&Key); + if(!mq_send_info){ + return 0; + } + + /*ringbuffer传值*/ + struct mq_events *e; + e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); + if (!e) return 0; + e->send_pid = mq_send_info->send_pid; + e->rcv_pid = pid; + e->mqdes = mq_send_info->mqdes; + e->msg_len = mq_send_info->msg_len; + e->msg_prio = mq_send_info->msg_prio; + + e->send_enter_time = mq_send_info->send_enter_time; + e->send_exit_time = mq_send_info->send_exit_time; + e->rcv_enter_time = mq_rcv_info->rcv_enter_time; + e->rcv_exit_time = rcv_exit_time; + bpf_ringbuf_submit(e, 0); + bpf_map_delete_elem(&send_msg2, &Key);//暂时性删除 + bpf_map_delete_elem(&rcv_msg1,&pid);//删除rcv_msg1 map; + return 0; + +} diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/mutrace.bpf.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/mutrace.bpf.c new file mode 100644 index 000000000..7b10a6ff7 --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/mutrace.bpf.c @@ -0,0 +1,260 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com + +#include +#include +#include +#include +#include "cpu_watcher.h" + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; + +const int ctrl_key = 0; +BPF_HASH(kmutex_info_map, u64, struct mutex_info, 1024); +BPF_HASH(umutex_info_map, u64, struct mutex_info, 1024); +BPF_HASH(trylock_map, u64, struct trylock_info, 1024); +BPF_ARRAY(mu_ctrl_map, int, struct mu_ctrl, 1); +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} rb SEC(".maps"); + +static inline struct mu_ctrl *get_mu_ctrl(void) { + struct mu_ctrl *mu_ctrl = bpf_map_lookup_elem(&mu_ctrl_map, &ctrl_key); + return (mu_ctrl && mu_ctrl->mu_func) ? mu_ctrl : NULL; +} + +static inline void update_mutex_info(struct mutex_info *info, u64 ts, pid_t pid) { + info->acquire_time = ts; + info->last_owner = pid; + bpf_get_current_comm(&info->last_name, sizeof(info->last_name)); +} + +static inline void init_mutex_info(struct mutex_info *info, u64 lock_addr, u64 ts, pid_t pid) { + info->locked_total = 0; + info->locked_max = 0; + info->contended_total = 0; + info->count = 0; + info->last_owner = pid; + info->acquire_time = ts; + info->ptr = lock_addr; + __builtin_memset(info->last_name, 0, sizeof(info->last_name)); + bpf_get_current_comm(&info->last_name, sizeof(info->last_name)); +} + +SEC("kprobe/mutex_lock") +int BPF_KPROBE(trace_mutex_lock, struct mutex *lock) { + struct mu_ctrl *mu_ctrl = get_mu_ctrl(); + if (!mu_ctrl) { + return 0; + } + u64 lock_addr = (u64)lock; + u64 ts = bpf_ktime_get_ns(); + struct mutex_info *info = bpf_map_lookup_elem(&kmutex_info_map, &lock_addr); + + if (info) { + info->acquire_time = ts; + } else { + struct mutex_info new_info; + init_mutex_info(&new_info, lock_addr, ts, 0); + bpf_map_update_elem(&kmutex_info_map, &lock_addr, &new_info, BPF_ANY); + } + + return 0; +} + +SEC("kprobe/mutex_trylock") +int BPF_KPROBE(trace_mutex_trylock, struct mutex *lock) { + struct mu_ctrl *mu_ctrl = get_mu_ctrl(); + if (!mu_ctrl) { + return 0; + } + int ret = PT_REGS_RC(ctx); + if (ret != 0) { + u64 lock_addr = (u64)lock; + u64 ts = bpf_ktime_get_ns(); + struct mutex_info *info = bpf_map_lookup_elem(&kmutex_info_map, &lock_addr); + + if (info) { + info->acquire_time = ts; + } else { + struct mutex_info new_info; + init_mutex_info(&new_info, lock_addr, ts, 0); + bpf_map_update_elem(&kmutex_info_map, &lock_addr, &new_info, BPF_ANY); + } + } + + return 0; +} + +SEC("kprobe/__mutex_lock_slowpath") +int BPF_KPROBE(trace_mutex_lock_slowpath, struct mutex *lock) { + struct mu_ctrl *mu_ctrl = get_mu_ctrl(); + if (!mu_ctrl) { + return 0; + } + if (!mu_ctrl) return 0; + + u64 lock_addr = (u64)lock; + u64 ts = bpf_ktime_get_ns(); + struct mutex_contention_event *e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); + if (!e) return 0; + + pid_t pid = bpf_get_current_pid_tgid(); + struct task_struct *owner_task, *contender_task; + long owner; + + e->contender_pid = pid; + e->ptr = lock_addr; + bpf_get_current_comm(&e->contender_name, sizeof(e->contender_name)); + bpf_probe_read_kernel(&owner, sizeof(owner), &lock->owner); + owner_task = (struct task_struct *)(owner & ~0x1L); + contender_task = (struct task_struct *)bpf_get_current_task(); + bpf_probe_read_kernel(&e->contender_prio, sizeof(e->contender_prio), &contender_task->prio); + + if (owner_task) { + bpf_probe_read_kernel(&e->owner_pid, sizeof(e->owner_pid), &owner_task->pid); + bpf_probe_read_kernel_str(&e->owner_name, sizeof(e->owner_name), owner_task->comm); + bpf_probe_read_kernel(&e->owner_prio, sizeof(e->owner_prio), &owner_task->prio); + } else { + e->owner_pid = 0; + __builtin_memset(e->owner_name, 0, sizeof(e->owner_name)); + } + + struct mutex_info *info = bpf_map_lookup_elem(&kmutex_info_map, &lock_addr); + if (info) { + u64 contention_start = ts; + info->contended_total += (contention_start - info->acquire_time); + info->count++; + } else { + struct mutex_info new_info; + init_mutex_info(&new_info, lock_addr, ts, 0); + new_info.count = 1; + bpf_map_update_elem(&kmutex_info_map, &lock_addr, &new_info, BPF_ANY); + } + + bpf_ringbuf_submit(e, 0); + return 0; +} + +SEC("kprobe/mutex_unlock") +int BPF_KPROBE(trace_mutex_unlock, struct mutex *lock) { + struct mu_ctrl *mu_ctrl = get_mu_ctrl(); + if (!mu_ctrl) { + return 0; + } + u64 lock_addr = (u64)lock; + u64 ts = bpf_ktime_get_ns(); + pid_t pid = bpf_get_current_pid_tgid(); + struct mutex_info *info = bpf_map_lookup_elem(&kmutex_info_map, &lock_addr); + + if (info) { + u64 held_time = ts - info->acquire_time; + info->locked_total += held_time; + if (held_time > info->locked_max) { + info->locked_max = held_time; + } + info->last_owner = pid; + bpf_get_current_comm(&info->last_name, sizeof(info->last_name)); + } + + return 0; +} + +/*----------------------------------------------*/ +/* 用户态互斥锁 */ +/*----------------------------------------------*/ + +static inline void handle_user_mutex_lock(void *__mutex, u64 now, pid_t pid) { + struct mutex_info *info = bpf_map_lookup_elem(&umutex_info_map, &__mutex); + if (info) { + if (info->acquire_time > 0) { + info->contended_total += (now - info->acquire_time); + info->count++; + } + update_mutex_info(info, now, pid); + } else { + struct mutex_info new_info; + init_mutex_info(&new_info, (u64)__mutex, now, pid); + bpf_map_update_elem(&umutex_info_map, &__mutex, &new_info, BPF_ANY); + } +} + +SEC("uprobe/pthread_mutex_lock") +int BPF_KPROBE(pthread_mutex_lock, void *__mutex) { + struct mu_ctrl *mu_ctrl = get_mu_ctrl(); + if (!mu_ctrl) { + return 0; + } + u64 now = bpf_ktime_get_ns(); + pid_t pid = bpf_get_current_pid_tgid() >> 32; + handle_user_mutex_lock(__mutex, now, pid); + return 0; +} + +SEC("uprobe/__pthread_mutex_trylock") +int BPF_KPROBE(__pthread_mutex_trylock, void *__mutex) { + struct mu_ctrl *mu_ctrl = get_mu_ctrl(); + if (!mu_ctrl) { + return 0; + } + u64 pid_tgid = bpf_get_current_pid_tgid(); + u64 now = bpf_ktime_get_ns(); + struct trylock_info info = { + .__mutex = __mutex, + .start_time = now, + }; + bpf_map_update_elem(&trylock_map, &pid_tgid, &info, BPF_ANY); + return 0; +} + +SEC("uretprobe/__pthread_mutex_trylock") +int BPF_KRETPROBE(ret_pthread_mutex_trylock, int ret) { + struct mu_ctrl *mu_ctrl = get_mu_ctrl(); + if (!mu_ctrl) { + return 0; + } + u64 pid_tgid = bpf_get_current_pid_tgid(); + struct trylock_info *try_info = bpf_map_lookup_elem(&trylock_map, &pid_tgid); + if (!try_info) return 0; + + if (ret == 0) { + handle_user_mutex_lock(try_info->__mutex, try_info->start_time, pid_tgid >> 32); + } + bpf_map_delete_elem(&trylock_map, &pid_tgid); + return 0; +} + +SEC("uprobe/pthread_mutex_unlock") +int BPF_KPROBE(pthread_mutex_unlock, void *__mutex) { + struct mu_ctrl *mu_ctrl = get_mu_ctrl(); + if (!mu_ctrl) { + return 0; + } + u64 now = bpf_ktime_get_ns(); + pid_t pid = bpf_get_current_pid_tgid() >> 32; + struct mutex_info *info = bpf_map_lookup_elem(&umutex_info_map, &__mutex); + if (info) { + u64 held_time = now - info->acquire_time; + info->locked_total += held_time; + if (held_time > info->locked_max) { + info->locked_max = held_time; + } + info->last_owner = pid; + bpf_get_current_comm(&info->last_name, sizeof(info->last_name)); + } + return 0; +} diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/preempt.bpf.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/preempt.bpf.c new file mode 100644 index 000000000..167f44f1a --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/preempt.bpf.c @@ -0,0 +1,91 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com + +#include +#include +#include +#include +#include "cpu_watcher.h" + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; + +#define TIF_NEED_RESCHED 3 +const int ctrl_key = 0; +// 记录时间戳 +BPF_HASH(preemptTime, pid_t, u64, 4096); +BPF_ARRAY(preempt_ctrl_map,int,struct preempt_ctrl,1); +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} rb SEC(".maps"); + +static inline struct preempt_ctrl *get_preempt_ctrl(void) { + struct preempt_ctrl *preempt_ctrl; + preempt_ctrl = bpf_map_lookup_elem(&preempt_ctrl_map, &ctrl_key); + if (!preempt_ctrl || !preempt_ctrl->preempt_func) { + return NULL; + } + return preempt_ctrl; +} + +SEC("tp_btf/sched_switch") +int BPF_PROG(sched_switch, bool preempt, struct task_struct *prev, struct task_struct *next) { + struct preempt_ctrl *preempt_ctrl = get_preempt_ctrl(); + if (!preempt_ctrl) { + return 0; + } + u64 start_time = bpf_ktime_get_ns(); + pid_t prev_pid = BPF_CORE_READ(prev, pid); + + if (preempt) { + bpf_map_update_elem(&preemptTime, &prev_pid, &start_time, BPF_ANY); + } + + // 下面的代码被注释掉,因为我们使用`preempt`参数判断是否需要记录时间戳 + // if (prev->thread_info.flags & TIF_NEED_RESCHED) { + // bpf_map_update_elem(&preemptTime, &prev_pid, &start_time, BPF_ANY); + // } + + return 0; +} + +// SEC("kprobe/finish_task_switch") +SEC("kprobe/finish_task_switch.isra.0") +int BPF_KPROBE(finish_task_switch, struct task_struct *prev) { + struct preempt_ctrl *preempt_ctrl = get_preempt_ctrl(); + if (!preempt_ctrl) { + return 0; + } + u64 end_time = bpf_ktime_get_ns(); + pid_t pid = BPF_CORE_READ(prev, pid); + u64 *val; + val = bpf_map_lookup_elem(&preemptTime, &pid); + if (val) { + u64 delta = end_time - *val; + struct preempt_event *e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); + if (!e) { + return 0; + } + e->prev_pid = pid; + e->next_pid = bpf_get_current_pid_tgid() >> 32; + e->duration = delta; + bpf_get_current_comm(&e->comm, sizeof(e->comm)); + bpf_ringbuf_submit(e, 0); + bpf_map_delete_elem(&preemptTime, &pid); + } + + return 0; +} diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/sar.bpf.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/sar.bpf.c similarity index 76% rename from eBPF_Supermarket/CPU_Subsystem/cpu_watcher/sar.bpf.c rename to eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/sar.bpf.c index 4e48d49c7..b2f1a15a9 100644 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/sar.bpf.c +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/sar.bpf.c @@ -1,3 +1,19 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com + #include #include #include @@ -7,7 +23,7 @@ char LICENSE[] SEC("license") = "Dual BSD/GPL"; const volatile long long unsigned int forks_addr = 0; - +const int ctrl_key = 0; #define PF_IDLE 0x00000002 /* I am an IDLE thread */ #define PF_KTHREAD 0x00200000 /* I am a kernel thread */ @@ -35,11 +51,26 @@ BPF_ARRAY(kt_LastTime,u32,u64,1); BPF_ARRAY(ut_LastTime,u32,u64,1); BPF_ARRAY(tick_user,u32,u64,1); BPF_ARRAY(symAddr,u32,u64,1); +BPF_ARRAY(sar_ctrl_map,int,struct sar_ctrl,1); + +static inline struct sar_ctrl *get_sar_ctrl(void) { + struct sar_ctrl *sar_ctrl; + sar_ctrl = bpf_map_lookup_elem(&sar_ctrl_map, &ctrl_key); + if (!sar_ctrl || !sar_ctrl->sar_func) { + return NULL; + } + return sar_ctrl; +} + // 统计fork数 -//SEC("kprobe/finish_task_switch.isra.0") -SEC("kprobe/finish_task_switch") +SEC("kprobe/finish_task_switch.isra.0") +// SEC("kprobe/finish_task_switch") int kprobe__finish_task_switch(struct pt_regs *ctx) { + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); + if (!sar_ctrl) { + return 0; + } u32 key = 0; u64 val, *valp = NULL; unsigned long total_forks; @@ -57,6 +88,10 @@ int kprobe__finish_task_switch(struct pt_regs *ctx) //获取进程切换数; SEC("tracepoint/sched/sched_switch") int trace_sched_switch2(struct cswch_args *info) { + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); + if (!sar_ctrl) { + return 0; + } pid_t prev = info->prev_pid, next = info->next_pid; if (prev != next) { u32 key = 0; @@ -75,9 +110,13 @@ int trace_sched_switch2(struct cswch_args *info) { return 0; } -SEC("kprobe/finish_task_switch") -//SEC("kprobe/finish_task_switch.isra.0") +// SEC("kprobe/finish_task_switch") +SEC("kprobe/finish_task_switch.isra.0") int BPF_KPROBE(finish_task_switch,struct task_struct *prev){ + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); + if (!sar_ctrl) { + return 0; + } pid_t pid=BPF_CORE_READ(prev,pid); u64 *val, time = bpf_ktime_get_ns(); u64 delta; @@ -108,6 +147,10 @@ int BPF_KPROBE(finish_task_switch,struct task_struct *prev){ //统计运行队列长度 SEC("kprobe/update_rq_clock") int BPF_KPROBE(update_rq_clock,struct rq *rq){ + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); + if (!sar_ctrl) { + return 0; + } u32 key = 0; u64 val = BPF_CORE_READ(rq,nr_running); bpf_map_update_elem(&runqlen,&key,&val,BPF_ANY); @@ -117,6 +160,10 @@ int BPF_KPROBE(update_rq_clock,struct rq *rq){ //软中断 SEC("tracepoint/irq/softirq_entry") int trace_softirq_entry(struct __softirq_info *info) { + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); + if (!sar_ctrl) { + return 0; + } u32 key = info->vec; u64 val = bpf_ktime_get_ns(); bpf_map_update_elem(&softirqCpuEnterTime, &key, &val, BPF_ANY); @@ -125,6 +172,10 @@ int trace_softirq_entry(struct __softirq_info *info) { SEC("tracepoint/irq/softirq_exit") int trace_softirq_exit(struct __softirq_info *info) { + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); + if (!sar_ctrl) { + return 0; + } u32 key = info->vec; u64 now = bpf_ktime_get_ns(), *valp = 0; valp =bpf_map_lookup_elem(&softirqCpuEnterTime, &key); @@ -143,6 +194,10 @@ int trace_softirq_exit(struct __softirq_info *info) { 注意这是所有CPU时间的叠加,平均到每个CPU应该除以CPU个数。*/ SEC("tracepoint/irq/irq_handler_entry") int trace_irq_handler_entry(struct __irq_info *info) { + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); + if (!sar_ctrl) { + return 0; + } u32 key = info->irq; u64 ts = bpf_ktime_get_ns(); bpf_map_update_elem(&irq_cpu_enter_start, &key, &ts, BPF_ANY); @@ -151,6 +206,10 @@ int trace_irq_handler_entry(struct __irq_info *info) { SEC("tracepoint/irq/irq_handler_exit") int trace_irq_handler_exit(struct __irq_info *info) { + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); + if (!sar_ctrl) { + return 0; + } u32 key = info->irq; u64 now = bpf_ktime_get_ns(), *ts = 0; ts = bpf_map_lookup_elem(&irq_cpu_enter_start, &key); @@ -170,6 +229,10 @@ int trace_irq_handler_exit(struct __irq_info *info) { //tracepoint:power_cpu_idle 表征了CPU进入IDLE的状态,比较准确 SEC("tracepoint/power/cpu_idle") int trace_cpu_idle(struct idleStruct *pIDLE) { + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); + if (!sar_ctrl) { + return 0; + } u64 delta, time = bpf_ktime_get_ns(); u32 key = pIDLE->cpu_id; if (pIDLE->state == -1) { @@ -199,6 +262,10 @@ static __always_inline int user_mode(struct pt_regs *regs) // 两个CPU各自会产生一个调用,这正好方便我们使用 SEC("perf_event") int tick_update(struct pt_regs *ctx) { + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); + if (!sar_ctrl) { + return 0; + } // bpf_trace_printk("cs_rpl = %x\n", ctx->cs & 3); u32 key = 0; diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/sc_delay.bpf.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/sc_delay.bpf.c new file mode 100644 index 000000000..91c4537d8 --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/sc_delay.bpf.c @@ -0,0 +1,95 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com + +#include "vmlinux.h" +#include +#include +#include "cpu_watcher.h" + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; + + +const int ctrl_key = 0; +BPF_PERCPU_HASH(SyscallEnterTime,pid_t,u64,512); +BPF_PERCPU_HASH(Events,pid_t,u64,10); +BPF_ARRAY(sc_ctrl_map,int,struct sc_ctrl,1); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} rb SEC(".maps");//环形缓冲区; + +static inline struct sc_ctrl *get_sc_ctrl(void) { + struct sc_ctrl *sc_ctrl; + sc_ctrl = bpf_map_lookup_elem(&sc_ctrl_map, &ctrl_key); + if (!sc_ctrl || !sc_ctrl->sc_func) { + return NULL; + } + return sc_ctrl; +} + + +SEC("tracepoint/raw_syscalls/sys_enter") +int tracepoint__syscalls__sys_enter(struct trace_event_raw_sys_enter *args){ + struct sc_ctrl *sc_ctrl = get_sc_ctrl(); + if (!sc_ctrl) { + return 0; + } + u64 start_time = bpf_ktime_get_ns()/1000; + pid_t pid = bpf_get_current_pid_tgid(); + u64 syscall_id = (u64)args->id; + + //bpf_printk("ID:%ld\n",syscall_id); + bpf_map_update_elem(&Events,&pid,&syscall_id,BPF_ANY); + bpf_map_update_elem(&SyscallEnterTime,&pid,&start_time,BPF_ANY); + return 0; +} + +SEC("tracepoint/raw_syscalls/sys_exit") +int tracepoint__syscalls__sys_exit(struct trace_event_raw_sys_exit *args){ + struct sc_ctrl *sc_ctrl = get_sc_ctrl(); + if (!sc_ctrl) { + return 0; + } + u64 exit_time = bpf_ktime_get_ns()/1000; + pid_t pid = bpf_get_current_pid_tgid() ; + u64 syscall_id; + u64 start_time, delay; + u64 *val = bpf_map_lookup_elem(&SyscallEnterTime, &pid); + if(val !=0){ + start_time = *val; + delay = exit_time - start_time; + bpf_map_delete_elem(&SyscallEnterTime, &pid); + }else{ + return 0; + } + u64 *val2 = bpf_map_lookup_elem(&Events, &pid); + if(val2 !=0){ + syscall_id = *val2; + bpf_map_delete_elem(&SyscallEnterTime, &pid); + }else{ + return 0; + } + struct syscall_events *e; + e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); + if (!e) return 0; + e->pid = pid; + e->delay = delay; + bpf_get_current_comm(&e->comm, sizeof(e->comm)); + e->syscall_id = syscall_id; + bpf_ringbuf_submit(e, 0); + return 0; +} diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/schedule_delay.bpf.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/schedule_delay.bpf.c new file mode 100644 index 000000000..459eb042a --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/schedule_delay.bpf.c @@ -0,0 +1,231 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com + +#include +#include +#include +#include +#include "cpu_watcher.h" + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; +#define TASK_RUNNING 0x0000 + +const int ctrl_key = 0; +BPF_HASH(has_scheduled,struct proc_id, bool, 10240);//记录该进程是否调度过 +BPF_HASH(enter_schedule,struct proc_id, struct schedule_event, 10240);//记录该进程上运行队列的时间 +BPF_ARRAY(sys_schedule,int,struct sum_schedule,1);//记录整个系统的调度延迟 +BPF_ARRAY(threshold_schedule,int,struct proc_schedule,10240);//记录每个进程的调度延迟 +BPF_HASH(proc_histories,struct proc_id, struct proc_history, 10240);//记录每个进程运行前的两个进程 +BPF_ARRAY(schedule_ctrl_map,int,struct schedule_ctrl,1); + +static inline struct schedule_ctrl *get_schedule_ctrl(void) { + struct schedule_ctrl *sched_ctrl; + sched_ctrl = bpf_map_lookup_elem(&schedule_ctrl_map, &ctrl_key); + if (!sched_ctrl || !sched_ctrl->schedule_func) { + return NULL; + } + return sched_ctrl; +}//查找控制结构体 + +SEC("tp_btf/sched_wakeup") +int BPF_PROG(sched_wakeup, struct task_struct *p) { + struct schedule_ctrl *sched_ctrl = get_schedule_ctrl(); + if (!sched_ctrl) { + return 0; + } + pid_t pid = p->pid; + int cpu = bpf_get_smp_processor_id(); + struct schedule_event *schedule_event; + struct proc_id id= {}; + u64 current_time = bpf_ktime_get_ns(); + id.pid = pid; + if (pid == 0) { + id.cpu_id = cpu; + } + schedule_event = bpf_map_lookup_elem(&enter_schedule, &id); + if (!schedule_event) { + struct schedule_event schedule_event1; + bool issched = false; + schedule_event1.pid = pid; + schedule_event1.count = 1; + schedule_event1.enter_time = current_time; + bpf_map_update_elem(&has_scheduled, &id, &issched, BPF_ANY); + bpf_map_update_elem(&enter_schedule, &id, &schedule_event1, BPF_ANY); + } else { + schedule_event->enter_time = current_time; + } + return 0; +} + +SEC("tp_btf/sched_wakeup_new") +int BPF_PROG(sched_wakeup_new, struct task_struct *p) { + struct schedule_ctrl *sched_ctrl = get_schedule_ctrl(); + if (!sched_ctrl) { + return 0; + } + sched_ctrl = bpf_map_lookup_elem(&schedule_ctrl_map,&ctrl_key); + if(!sched_ctrl || !sched_ctrl->schedule_func) + return 0; + pid_t pid = p->pid; + int cpu = bpf_get_smp_processor_id(); + struct proc_id id= {}; + u64 current_time = bpf_ktime_get_ns(); + id.pid = pid; + if (pid == 0) { + id.cpu_id = cpu; + } + struct schedule_event schedule_event; + bool issched = false; + schedule_event.pid = pid; + schedule_event.count = 1; + schedule_event.enter_time = current_time; + bpf_map_update_elem(&has_scheduled, &id, &issched, BPF_ANY); + bpf_map_update_elem(&enter_schedule, &id, &schedule_event, BPF_ANY); + return 0; +} + +SEC("tp_btf/sched_switch") +int BPF_PROG(sched_switch, bool preempt, struct task_struct *prev, struct task_struct *next) { + struct schedule_ctrl *sched_ctrl = get_schedule_ctrl(); + if (!sched_ctrl) { + return 0; + } + struct proc_history *history; + struct proc_history new_history; + u64 current_time = bpf_ktime_get_ns(); + pid_t prev_pid = prev->pid; + unsigned int prev_state = prev->__state; + int prev_cpu = bpf_get_smp_processor_id(); + pid_t next_pid = next->pid; + int next_cpu = bpf_get_smp_processor_id(); + bool *issched; + struct schedule_event *schedule_event; + struct sum_schedule *sum_schedule; + int key = 0; + struct proc_id next_id = {}; + u64 delay; + if (prev_state == TASK_RUNNING) { + struct proc_id prev_pd = {}; + prev_pd.pid = prev_pid; + if (prev_pid == 0) { + prev_pd.cpu_id = prev_cpu; + } + schedule_event = bpf_map_lookup_elem(&enter_schedule, &prev_pd); + if (!schedule_event) { + struct schedule_event schedule_event2; + bool issched = false; + schedule_event2.pid = prev_pid; + schedule_event2.count = 1; + schedule_event2.enter_time = current_time; + bpf_map_update_elem(&has_scheduled, &prev_pd, &issched, BPF_ANY); + bpf_map_update_elem(&enter_schedule, &prev_pd, &schedule_event2, BPF_ANY); + } else { + schedule_event->enter_time = current_time; + } + } + + next_id.pid = next_pid; + if (next_pid == 0) { + next_id.cpu_id = next_cpu; + } + schedule_event = bpf_map_lookup_elem(&enter_schedule, &next_id); + if (!schedule_event) return 0; + issched = bpf_map_lookup_elem(&has_scheduled, &next_id); + if (!issched) return 0; + if (*issched) { + schedule_event->count++; + } else { + *issched = true; + } + delay = current_time - schedule_event->enter_time; + struct proc_schedule proc_schedule; + proc_schedule.delay = delay; + proc_schedule.id= next_id; + bpf_probe_read_kernel_str(&proc_schedule.proc_name, sizeof(proc_schedule.proc_name), next->comm); + bpf_map_update_elem(&threshold_schedule, &key, &proc_schedule, BPF_ANY); + sum_schedule = bpf_map_lookup_elem(&sys_schedule, &key); + if (!sum_schedule) { + struct sum_schedule sum_schedule = {}; + sum_schedule.sum_count++; + sum_schedule.sum_delay += delay; + if (delay > sum_schedule.max_delay) { + sum_schedule.max_delay = delay; + if (next->pid != 0) { + bpf_probe_read_kernel_str(&sum_schedule.proc_name_max, sizeof(sum_schedule.proc_name_max), next->comm); + } + } else if (sum_schedule.min_delay == 0 || delay < sum_schedule.min_delay) { + sum_schedule.min_delay = delay; + if (next->pid != 0) { + bpf_probe_read_kernel_str(&sum_schedule.proc_name_min, sizeof(sum_schedule.proc_name_min), next->comm); + } + } + bpf_map_update_elem(&sys_schedule, &key, &sum_schedule, BPF_ANY); + } else { + sum_schedule->sum_count++; + sum_schedule->sum_delay += delay; + if (delay > sum_schedule->max_delay) { + sum_schedule->max_delay = delay; + bpf_probe_read_kernel_str(&sum_schedule->proc_name_max, sizeof(sum_schedule->proc_name_max), next->comm); + } else if (sum_schedule->min_delay == 0 || delay < sum_schedule->min_delay) { + sum_schedule->min_delay = delay; + if (next->pid != 0) { + bpf_probe_read_kernel_str(&sum_schedule->proc_name_min, sizeof(sum_schedule->proc_name_min), next->comm); + } + } + } + history = bpf_map_lookup_elem(&proc_histories, &next_id); + if (history) { + // 如果找到了,更新历史记录 + new_history.last[0] = history->last[1]; + new_history.last[1].pid = prev->pid; + bpf_probe_read_kernel_str(&new_history.last[1].comm, sizeof(new_history.last[1].comm), prev->comm); + bpf_map_update_elem(&proc_histories, &next_id, &new_history, BPF_ANY); + } else { + // 如果没有找到,初始化新的历史记录 + new_history.last[0].pid = 0; // 初始化为0,表示没有历史信息 + new_history.last[0].comm[0] = '\0'; + new_history.last[1].pid = prev->pid; + bpf_probe_read_kernel_str(&new_history.last[1].comm, sizeof(new_history.last[1].comm), prev->comm); + bpf_map_update_elem(&proc_histories, &next_id, &new_history, BPF_ANY); + } + return 0; +} + +SEC("tracepoint/sched/sched_process_exit") +int sched_process_exit(void *ctx) { + struct schedule_ctrl *sched_ctrl = get_schedule_ctrl(); + if (!sched_ctrl) { + return 0; + } + struct task_struct *p = (struct task_struct *)bpf_get_current_task(); + pid_t pid = BPF_CORE_READ(p, pid); + int cpu = bpf_get_smp_processor_id(); + struct proc_id id= {}; + struct schedule_event *schedule_event; + bool *issched; + int key = 0; + id.pid = pid; + if (pid == 0) id.cpu_id = cpu; + schedule_event = bpf_map_lookup_elem(&enter_schedule, &id); + if (schedule_event) { + bpf_map_delete_elem(&enter_schedule, &id); + } + issched = bpf_map_lookup_elem(&has_scheduled, &id); + if (issched) { + bpf_map_delete_elem(&has_scheduled, &id); + } + return 0; +} \ No newline at end of file diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/controller.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/controller.c new file mode 100644 index 000000000..deaaad387 --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/controller.c @@ -0,0 +1,280 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cpu_watcher_helper.h" + +static struct env { + // 1代表activate;2代表unactivate;3代表finish + int usemode; + bool SAR; + bool percent; + bool CS_DELAY; + bool SYSCALL_DELAY; + bool MIN_US_SET; + int MIN_US; + bool PREEMPT; + bool SCHEDULE_DELAY; + bool MQ_DELAY; + int freq; + bool mutrace; + bool mutex_detail; + bool umutex; +} env = { + .usemode = 0, + .SAR = false, + .percent = false, + .CS_DELAY = false, + .SYSCALL_DELAY = false, + .MIN_US_SET = false, + .MIN_US = 10000, + .PREEMPT = false, + .SCHEDULE_DELAY = false, + .MQ_DELAY = false, + .freq = 99, + .mutrace = false, + .mutex_detail = false, + .umutex = false, +}; + +const char argp_program_doc[] ="Trace process to get cpu watcher.\n"; + +static const struct argp_option opts[] = { + { "activate", 'a', NULL, 0, "Set startup policy of proc_image tool" }, + { "unactivate", 'u', NULL, 0, "Initialize to the original unactivated state" }, + { "finish", 'f', NULL, 0, "Finish to run eBPF tool" }, + {"libbpf_sar", 's', 0, 0, "Print sar_info (the data of cpu)" }, + {"percent", 'P', 0, 0, "Format data as percentages" }, + {"cs_delay", 'c', 0, 0, "Print cs_delay (the data of cpu)" }, + {"syscall_delay", 'S', 0, 0, "Print syscall_delay (the data of syscall)" }, + {"preempt_time", 'p', 0, 0, "Print preempt_time (the data of preempt_schedule)" }, + {"schedule_delay", 'd', 0, 0, "Print schedule_delay (the data of cpu)" }, + {"schedule_delay_min_us_set", 'e', "THRESHOLD", 0, "Print scheduling delays that exceed the threshold (the data of cpu)" }, + {"mq_delay", 'm', 0, 0, "Print mq_delay(the data of proc)" }, + {"mutrace", 'x', 0, 0, "Print kernel mutex contend" }, + {"mutex_detail", 'i', 0, 0, "Print kernel mutex details" }, + {"umutex", 'b', 0, 0, "Print user mutex details" }, + { 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': + env.usemode = 1; + break; + case 'u': + env.usemode = 2; + break; + case 'f': + env.usemode = 3; + break; + case 's': + env.SAR = true; + break; + case 'P': + env.percent = true; + case 'c': + env.CS_DELAY = true; + break; + case 'S': + env.SYSCALL_DELAY = true; + break; + case 'p': + env.PREEMPT = true; + break; + case 'd': + env.SCHEDULE_DELAY = true; + break; + case 'e': + env.MIN_US_SET = true; + if (arg) { + env.MIN_US = strtol(arg, NULL, 10); + if (env.MIN_US <= 0) { + fprintf(stderr, "Invalid value for min_us: %d\n", env.MIN_US); + argp_usage(state); + } + } else { + env.MIN_US = 10000; + } + break; + case 'm': + env.MQ_DELAY = true; + break; + case 'x': + env.mutrace = true; + break; + case 'i': + env.mutex_detail = true; + break; + case 'b': + env.umutex = true; + break; + case 'h': + argp_state_help(state, stderr, ARGP_HELP_STD_HELP); + break; + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +int deactivate_mode(){ + int err; + + if(env.SAR){ + struct sar_ctrl sar_ctrl = {false,false,0}; + err = update_sar_ctrl_map(sar_ctrl); + if(err < 0) return err; + } + if(env.CS_DELAY){ + struct cs_ctrl cs_ctrl = {false,0}; + err = update_cs_ctrl_map(cs_ctrl); + if(err < 0) return err; + } + if(env.SYSCALL_DELAY){ + struct sc_ctrl sc_ctrl = {false,0}; + err = update_sc_ctrl_map(sc_ctrl); + if(err < 0) return err; + } + if(env.PREEMPT){ + struct preempt_ctrl preempt_ctrl = {false,0}; + err = update_preempt_ctrl_map(preempt_ctrl); + if(err < 0) return err; + } + if(env.SCHEDULE_DELAY){ + struct schedule_ctrl schedule_ctrl = {false,false,10000,0}; + err = update_schedule_ctrl_map(schedule_ctrl); + if(err < 0) return err; + } + if(env.MQ_DELAY){ + struct mq_ctrl mq_ctrl = {false,0}; + err = update_mq_ctrl_map(mq_ctrl); + if(err < 0) return err; + } + if(env.mutrace){ + struct mu_ctrl mu_ctrl = {false,false,0}; + err = update_mu_ctrl_map(mu_ctrl); + if(err < 0) return err; + } + return 0; +} + +static void sig_handler(int signo) +{ + deactivate_mode(); +} + +int main(int argc, char **argv) +{ + int err; + static const struct argp argp = { + .options = opts, + .parser = parse_arg, + .doc = argp_program_doc, + }; + + err = argp_parse(&argp, argc, argv, 0, NULL, NULL); + if (err) + return err; + + signal(SIGALRM,sig_handler); + signal(SIGINT,sig_handler); + signal(SIGTERM,sig_handler); + + if(env.usemode == 1){ // activate mode + if(env.SAR){ + struct sar_ctrl sar_ctrl = {true,env.percent,SAR_WACTHER+env.percent}; + err = update_sar_ctrl_map(sar_ctrl); + if(err < 0) return err; + } + + if(env.CS_DELAY){ + struct cs_ctrl cs_ctrl = {true,CS_WACTHER}; + err = update_cs_ctrl_map(cs_ctrl); + if(err < 0) return err; + } + + if(env.SYSCALL_DELAY){ + struct sc_ctrl sc_ctrl = {true,SC_WACTHER}; + err = update_sc_ctrl_map(sc_ctrl); + if(err < 0) return err; + } + + if(env.PREEMPT){ + struct preempt_ctrl preempt_ctrl = {true,PREEMPT_WACTHER}; + err = update_preempt_ctrl_map(preempt_ctrl); + if(err < 0) return err; + } + + if(env.SCHEDULE_DELAY){ + /* + *1.未设置env.MIN_US_SET时, prev_watcher = SCHEDULE_WACTHER + 0;输出方式为schedule输出 + *2.已设置env.MIN_US_SET时, prev_watcher = SCHEDULE_WACTHER + 1;输出方式为-e输出 + */ + struct schedule_ctrl schedule_ctrl = {true,env.MIN_US_SET,env.MIN_US,SCHEDULE_WACTHER+env.MIN_US_SET}; + err = update_schedule_ctrl_map(schedule_ctrl); + if(err < 0) return err; + } + + if(env.MQ_DELAY){ + struct mq_ctrl mq_ctrl = {true,MQ_WACTHER}; + err = update_mq_ctrl_map(mq_ctrl); + if(err < 0) return err; + } + + if(env.mutrace){ + if (env.umutex){ + struct mu_ctrl mu_ctrl = {true,env.mutex_detail,env.umutex,MUTEX_WATCHER+2}; + err = update_mu_ctrl_map(mu_ctrl); + if(err < 0) return err; + } + else{ + struct mu_ctrl mu_ctrl = {true,env.mutex_detail,env.umutex,MUTEX_WATCHER+env.mutex_detail}; + err = update_mu_ctrl_map(mu_ctrl); + if(err < 0) return err; + } + } + }else if(env.usemode == 2){ // deactivate mode + err = deactivate_mode(); + if(err<0){ + fprintf(stderr, "Failed to deactivate\n"); + return err; + } + }else if(env.usemode == 3){ // finish mode + const char *command = "pkill cpu_watcher"; + int status = system(command); + if (status == -1) { + perror("system"); + } + }else{ + // 输出help信息 + printf("Please enter the usage mode(activate/deactivate/finish) before selecting the function\n"); + argp_help(&argp, stderr, ARGP_HELP_LONG, argv[0]); + } + + return 0; +} diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/cpu_watcher.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/cpu_watcher.c index 709b29c74..e50e23ab2 100644 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/cpu_watcher.c +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/cpu_watcher.c @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -// author: zhangziheng0525@163.com +// author: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com #include @@ -21,21 +21,29 @@ #include #include #include +#include #include #include +#include +#include +#include +#include #include #include -#include "cpu_watcher.h" +#include "cpu_watcher_helper.h" #include "sar.skel.h" #include "cs_delay.skel.h" #include "sc_delay.skel.h" #include "preempt.skel.h" #include "schedule_delay.skel.h" #include "mq_delay.skel.h" +#include "mutrace.skel.h" typedef long long unsigned int u64; typedef unsigned int u32; + + struct list_head { struct list_head *next; struct list_head *prev; @@ -50,47 +58,72 @@ struct msg_msg { static struct env { int time; + int period; bool enable_proc; bool SAR; bool CS_DELAY; bool SYSCALL_DELAY; bool PREEMPT; bool SCHEDULE_DELAY; - bool MQ_DELAY; + bool MQ_DELAY; int freq; + bool EWMA; + int cycle; + int MUTRACE; } env = { .time = 0, + .period = 1, .enable_proc = false, .SAR = false, .CS_DELAY = false, .SYSCALL_DELAY = false, .PREEMPT = false, .SCHEDULE_DELAY = false, - .MQ_DELAY = false, - .freq = 99 + .MQ_DELAY = false, + .freq = 99, + .EWMA = false, + .cycle = 0, + .MUTRACE = false, }; + struct cs_delay_bpf *cs_skel; struct sar_bpf *sar_skel; struct sc_delay_bpf *sc_skel; struct preempt_bpf *preempt_skel; struct schedule_delay_bpf *sd_skel; struct mq_delay_bpf *mq_skel; - -u64 softirq = 0;//初始化softirq; -u64 irqtime = 0;//初始化irq; -u64 idle = 0;//初始化idle;s +struct mutrace_bpf *mu_skel; + +static int csmap_fd; +static int sarmap_fd; +struct sar_ctrl sar_ctrl= {}; +static int scmap_fd; +static int preemptmap_fd; +static int schedulemap_fd; +struct schedule_ctrl sd_ctrl = {}; +static int mqmap_fd; +static int mumap_fd; +struct mu_ctrl mu_ctrl = {}; + +//static int prev_watcher = 0;//上一个使用的工具,用于在切换使用功能时,打印不用功能的表头; + +u64 softirq = 0; +u64 irqtime = 0; +u64 idle = 0; u64 sched = 0; u64 proc = 0; unsigned long ktTime = 0; unsigned long utTime = 0; -u64 tick_user = 0;//初始化sys; +u64 tick_user = 0; + int sc_sum_time = 0 ; int sc_max_time = 0 ; int sc_min_time = SYSCALL_MIN_TIME ; int sys_call_count = 0; +bool ifprint = 0; int preempt_count = 0 ; @@ -98,51 +131,69 @@ int sum_preemptTime = 0 ; int preempt_start_print = 0 ; /*设置传参*/ -const char argp_program_doc[] ="cpu wacher is in use ....\n"; +const char argp_program_doc[] = "cpu watcher is in use ....\n"; static const struct argp_option opts[] = { - { "time", 't', "TIME-SEC", 0, "Max Running Time(0 for infinite)" }, - {"libbpf_sar", 's', 0,0,"print sar_info (the data of cpu)"}, - {"cs_delay", 'c', 0,0,"print cs_delay (the data of cpu)"}, - {"syscall_delay", 'S', 0,0,"print syscall_delay (the data of syscall)"}, - {"preempt_time", 'p', 0,0,"print preempt_time (the data of preempt_schedule)"}, - {"schedule_delay", 'd', 0,0,"print schedule_delay (the data of cpu)"}, - {"mq_delay", 'm', 0,0,"print mq_delay"}, - { NULL, 'h', NULL, OPTION_HIDDEN, "show the full help" }, - {0}, + { "time", 't', "TIME-SEC", 0, "Max Running Time(0 for infinite)" }, + { "period", 'i', "INTERVAL", 0, "Period interval in seconds" }, + {"libbpf_sar", 's', 0, 0, "Print sar_info (the data of cpu)" }, + {"cs_delay", 'c', 0, 0, "Print cs_delay (the data of cpu)" }, + {"syscall_delay", 'S', 0, 0, "Print syscall_delay (the data of syscall)" }, + {"preempt_time", 'p', 0, 0, "Print preempt_time (the data of preempt_schedule)" }, + {"schedule_delay", 'd', 0, 0, "Print schedule_delay (the data of cpu)" }, + {"mq_delay", 'm', 0, 0, "Print mq_delay(the data of proc)" }, + {"mutrace", 'x', 0, 0, "Print mutrace data(the data of cpu)" }, + {"ewma", 'E',0,0,"dynamic filte the data"}, + {"cycle", 'T',"CYCLE",0,"Periods of the ewma"}, + { NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" }, + { 0 }, }; + static error_t parse_arg(int key, char *arg, struct argp_state *state) { - switch (key) { - case 't': - env.time = strtol(arg, NULL, 10); - if(env.time) alarm(env.time); - break; - case 's': - env.SAR = true; - break; - case 'c': - env.CS_DELAY = true; - break; - case 'S': - env.SYSCALL_DELAY = true; - break; - case 'p': - env.PREEMPT = true; - break; - case 'd': - env.SCHEDULE_DELAY = true; - break; - case 'm': - env.MQ_DELAY = true; + switch (key) { + case 't': + env.time = strtol(arg, NULL, 10); + if (env.time) alarm(env.time); + break; + case 'i': + env.period = strtol(arg, NULL, 10); + break; + case 's': + env.SAR = true; + break; + case 'c': + env.CS_DELAY = true; + break; + case 'S': + env.SYSCALL_DELAY = true; + break; + case 'p': + env.PREEMPT = true; + break; + case 'd': + env.SCHEDULE_DELAY = true; + break; + case 'm': + env.MQ_DELAY = true; + break; + case 'x': + env.MUTRACE = true; break; - case 'h': - argp_state_help(state, stderr, ARGP_HELP_STD_HELP); + case 'E': + env.EWMA = true; break; - default: - return ARGP_ERR_UNKNOWN; - } - return 0; + case 'T': + env.cycle = strtol(arg, NULL, 10); + break; + case 'h': + argp_state_help(state, stderr, ARGP_HELP_STD_HELP); + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; } + static const struct argp argp = { .options = opts, .parser = parse_arg, @@ -176,7 +227,6 @@ static int open_and_attach_perf_event(int freq, struct bpf_program *prog, .config = PERF_COUNT_SW_CPU_CLOCK, }; int i, fd; - for (i = 0; i < nr_cpus; i++) { fd = syscall(__NR_perf_event_open, &attr, -1, i, -1, 0); if (fd < 0) { @@ -196,7 +246,6 @@ static int open_and_attach_perf_event(int freq, struct bpf_program *prog, return -1; } } - return 0; } @@ -207,23 +256,42 @@ u64 find_ksym(const char* target_symbol) { perror("Failed to open /proc/kallsyms"); return 1; } - char symbol_name[99]; u64 symbol_address = 0; - while (fscanf(file, "%llx %*c %s\n", &symbol_address, symbol_name) != EOF) { if (strcmp(symbol_name, target_symbol) == 0) { break; } } - fclose(file); - return symbol_address; } static int print_all() { + int err,key=0; + err = bpf_map_lookup_elem(sarmap_fd, &key, &sar_ctrl); + if (err < 0) { + fprintf(stderr, "failed to lookup infos: %d\n", err); + return -1; + } + if(!sar_ctrl.sar_func) return 0; + if(sar_ctrl.prev_watcher == SAR_WACTHER + 1) { + printf(" time proc/s cswch/s runqlen irqTime/%% softirq/%% idle/%% kthread/%% sysc/%% utime/%% sys/%% \n"); + sar_ctrl.prev_watcher = SAR_WACTHER + 2; + err = bpf_map_update_elem(sarmap_fd, &key, &sar_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + } + }else if (sar_ctrl.prev_watcher == SAR_WACTHER){ + printf(" time proc/s cswch/s runqlen irqTime/us softirq/us idle/ms kthread/us sysc/ms utime/ms sys/ms \n"); + sar_ctrl.prev_watcher = SAR_WACTHER + 2; + err = bpf_map_update_elem(sarmap_fd, &key, &sar_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + } + } + int nprocs = get_nprocs(); /*proc:*/ int key_proc = 1; int err_proc, fd_proc = bpf_map__fd(sar_skel->maps.countMap); @@ -236,6 +304,7 @@ static int print_all() u64 __proc; __proc = total_forks - proc; proc = total_forks; + /*cswch:*/ int key_cswch = 0; int err_cswch, fd_cswch = bpf_map__fd(sar_skel->maps.countMap); @@ -320,7 +389,7 @@ static int print_all() unsigned long dtaUT = utTime -_utTime; /*sys*/ - int key_sys = 0,next_key; + int key_sys = 0; int err_sys, fd_sys = bpf_map__fd(sar_skel->maps.tick_user); u64 __tick_user =0 ;// 用于存储从映射中查找到的值 __tick_user = tick_user; @@ -338,15 +407,34 @@ static int print_all() if(env.enable_proc){ time_t now = time(NULL); struct tm *localTime = localtime(&now); - printf("%02d:%02d:%02d %8llu %8llu %6d %8llu %10llu %8llu %10llu %8llu %8lu %8lu\n", + if (sar_ctrl.percent == true){ + printf("%02d:%02d:%02d %8llu %8llu %6d ",localTime->tm_hour, localTime->tm_min, localTime->tm_sec,__proc, __sched, runqlen); + // 大于百分之60的标红输出 + double values[7] = { + (double)dtairqtime / 10000000 / nprocs / env.period, + (double)dtasoftirq / 10000000 / nprocs / env.period, + (double)dtaidle / 10000000 / nprocs / env.period, + (double)dtaKT / 10000000 / nprocs / env.period, + (double)dtaSysc / 10000000 / nprocs / env.period, + (double)dtaUTRaw / 10000000 / nprocs / env.period, + (double)dtaSys / 10000000 / nprocs / env.period + }; + for (int i = 0; i < 7; i++) { + if (values[i] > 60.0) { + printf("\033[1;31m"); // 设置为红色 + } + printf("%10.2f ", values[i]); + printf("\033[0m"); // 重置为默认颜色 + } + printf("\n"); + }else{printf("%02d:%02d:%02d %8llu %8llu %6d %8llu %10llu %8llu %10lu %8llu %8llu %8llu\n", localTime->tm_hour, localTime->tm_min, localTime->tm_sec, __proc,__sched,runqlen,dtairqtime/1000,dtasoftirq/1000,dtaidle/1000000, - dtaKT/1000,dtaSysc / 1000000,dtaUTRaw/1000000,dtaSys / 1000000); + dtaKT/1000,dtaSysc / 1000000,dtaUTRaw/1000000,dtaSys / 1000000);} } else{ env.enable_proc = true; } - return 0; } @@ -354,49 +442,48 @@ int count[25]={0};//定义一个count数组,用于汇总schedul()调度时间 static int handle_event(void *ctx, void *data,unsigned long data_sz) { const struct event *e = data; - printf("t1:%lu t2:%lu delay:%lu\n",e->t1,e->t2,e->delay); - + printf("t1:%llu t2:%llu delay:%llu\n",e->t1,e->t2,e->delay); int dly=(int)(e->delay),i=0; while (dly > 1){ dly /= 2; i ++; } - count[i]++;//记录时间间隔次数; + count[i]++; return 0; } static int print_hstgram(int i,int max,int per_len) { int cnt=count[i]; if(per_len==1){ - while(cnt>0){//打印 + while(cnt>0){ printf("*"); cnt--; } } - while(cnt-per_len>=0){//打印 + while(cnt-per_len>=0){ printf("*"); cnt-=per_len; } printf("\n"); return per_len; } -double pow(int n,int k)//实现pow函数 +double my_pow(int n,int k)//实现pow函数 { if (k > 0) - return n * pow(n, k - 1); + return n * my_pow(n, k - 1); else if (k == 0) return 1; else - return 1.0 / pow(n, -k); + return 1.0 / my_pow(n, -k); } static void histogram() { int log10[15]={0},max=0,per_len=1; - for(int i=0;i<10;i++){//log10(count[i]); + for(int i=0;i<10;i++){ int tmp=count[i],cnt=0; while (tmp >= 10){ tmp /= 10; - cnt ++;//幂次 + cnt ++; } log10[cnt]++; } @@ -406,13 +493,13 @@ static void histogram() max=i; } - while(max>0){//pow(10,max); + while(max>0){ per_len *=10 ; max--; } - time_t now = time(NULL);// 获取当前时间 - struct tm *localTime = localtime(&now);// 将时间转换为本地时间结构 + time_t now = time(NULL); + struct tm *localTime = localtime(&now); printf("\nTime : %02d:%02d:%02d \n",localTime->tm_hour, localTime->tm_min, localTime->tm_sec); printf("%-24s \t%-12s \t%-12s \n","cs_delay","Count","Distribution"); printf("%d\t=>\t%-8d \t%-12d \t|",0,1,count[0]); @@ -420,28 +507,49 @@ static void histogram() printf("%d\t=>\t%-8d \t%-12d \t|",2,3,count[1]); print_hstgram(1,max,per_len); for(int i=2;i<20;i++){ - printf("%d\t=>\t%-8d \t%-12d \t|",(int)pow(2,i),(int)pow(2,(i+1))-1,count[i]); + printf("%d\t=>\t%-8d \t%-12d \t|",(int)my_pow(2,i),(int)my_pow(2,(i+1))-1,count[i]); print_hstgram(i,max,per_len); } printf("per_len = %d\n",per_len); } -// static void max_print(){ - -// int sc_average_time = sc_sum_time/sys_call_count; -// printf("Average_Syscall_Time: %8d ms\n",sc_average_time); -// printf("MAX_Syscall_Time: %8d ms\n",sc_max_time); -// printf("MIN_Syscall_Time: %8d ms\n",sc_min_time); -// } +struct ewma_info ewma_syscall_delay = {}; static int syscall_delay_print(void *ctx, void *data,unsigned long data_sz) { + int err,key = 0; + struct sc_ctrl sc_ctrl ={}; + + err = bpf_map_lookup_elem(scmap_fd,&key,&sc_ctrl); + if (err < 0) { + fprintf(stderr, "failed to lookup infos: %d\n", err); + return -1; + } + if(!sc_ctrl.sc_func) return 0; const struct syscall_events *e = data; - printf("pid: %-8llu comm: %-10s syscall_id: %-8ld delay: %-8llu\n", - e->pid,e->comm,e->syscall_id,e->delay); + if(e->delay<0||e->delay>1000000) return 0; + time_t now = time(NULL);// 获取当前时间 + struct tm *localTime = localtime(&now);// 将时间转换为本地时间结构 + + if(env.EWMA==0){ + printf("%02d:%02d:%02d %-8u %-15lld %-15lld\n", + localTime->tm_hour, localTime->tm_min, localTime->tm_sec, + e->pid,e->syscall_id,e->delay); + } + else{ + ewma_syscall_delay.cycle = env.cycle; + if(dynamic_filter(&ewma_syscall_delay,e->delay)){ + printf("%02d:%02d:%02d %-8u %-15lld %-15lld\n", + localTime->tm_hour, localTime->tm_min, localTime->tm_sec, + e->pid,e->syscall_id,e->delay); + } + } + return 0; } + + //抢占时间输出 static int preempt_print(void *ctx, void *data, unsigned long data_sz) { @@ -452,49 +560,204 @@ static int preempt_print(void *ctx, void *data, unsigned long data_sz) return 0; } -static int schedule_print(struct bpf_map *sys_fd) +static int attach(struct mutrace_bpf *mu_skel) { - int key = 0; - struct sum_schedule info; - int err, fd = bpf_map__fd(sys_fd); - time_t now = time(NULL); - struct tm *localTime = localtime(&now); - int hour = localTime->tm_hour; - int min = localTime->tm_min; - int sec = localTime->tm_sec; - unsigned long long avg_delay; - - err = bpf_map_lookup_elem(fd, &key, &info); - if (err < 0) { - fprintf(stderr, "failed to lookup infos: %d\n", err); - return -1; + int err; + ATTACH_UPROBE_CHECKED(mu_skel,pthread_mutex_lock,pthread_mutex_lock); + ATTACH_UPROBE_CHECKED(mu_skel,__pthread_mutex_trylock,__pthread_mutex_trylock); + ATTACH_URETPROBE_CHECKED(mu_skel,__pthread_mutex_trylock,ret_pthread_mutex_trylock); + ATTACH_UPROBE_CHECKED(mu_skel,pthread_mutex_unlock,pthread_mutex_unlock); + err = mutrace_bpf__attach(mu_skel); + CHECK_ERR(err, "Failed to attach BPF skeleton"); + return 0; + +} + + +//mutrace输出 +static int mutrace_print(void *ctx, void *data, unsigned long data_sz) { + int err,key = 0; + err = bpf_map_lookup_elem(mumap_fd,&key,&mu_ctrl); + if (err < 0) { + fprintf(stderr, "failed to lookup infos: %d\n", err); + return -1; + } + if(!mu_ctrl.mu_func) return 0; + if(mu_ctrl.prev_watcher == MUTEX_WATCHER ){ + printf("%s\n"," lock_ptr owner_pid owner_comm owner_prio contender_pid contender_comm contender_prio contender_count"); + mu_ctrl.prev_watcher = MUTEX_WATCHER + 9;//打印表头功能关 + err = bpf_map_update_elem(mumap_fd, &key, &mu_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + } + }else if (mu_ctrl.prev_watcher == MUTEX_WATCHER +1) { + printf("%s\n"," lock_ptr locked_total locked_max contended_total count last_owner last_owmer_name"); + mu_ctrl.prev_watcher = MUTEX_WATCHER + 9;//打印表头功能关 + err = bpf_map_update_elem(mumap_fd, &key, &mu_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + } + }else if (mu_ctrl.prev_watcher == MUTEX_WATCHER +2) { + printf("%s\n"," lock_ptr locked_total locked_max contended_total count last_owner last_owmer_name"); + mu_ctrl.prev_watcher = MUTEX_WATCHER + 9;//打印表头功能关 + err = bpf_map_update_elem(mumap_fd, &key, &mu_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + } + } + if(!mu_ctrl.mutex_detail&& (!mu_ctrl.umutex)){ + const struct mutex_contention_event *e = data; + if (e->owner_pid == 0 || e->contender_pid == 0||e->owner_pid == 1) { + return 0; + } + // 增加锁争用次数 + increment_lock_count(e->ptr); + uint64_t contention_count = get_lock_count(e->ptr); + printf("%15llu %15d %15s %15d %15d %15s %15d %15ld\n", e->ptr, e->owner_pid, e->owner_name, e->owner_prio,e->contender_pid, e->contender_name, e->contender_prio,contention_count); + } + return 0; +} + +static int kmutex_detail() { + int fd = bpf_map__fd(mu_skel->maps.kmutex_info_map); + u64 key, next_key; + struct mutex_info info; + while (bpf_map_get_next_key(fd, &key, &next_key) == 0) { + int err = bpf_map_lookup_elem(fd, &next_key, &info); + if (err == 0 && info.contended_total != 0) { // 添加过滤条件 + printf(" %15llu %15lluns %15lluns %15lluns %15d %15d %20s\n", + next_key, info.locked_total, info.locked_max, info.contended_total, info.count, info.last_owner, info.last_name); + } + key = next_key; + } + return 0; +} + +static int umutex_detail() { + int fd = bpf_map__fd(mu_skel->maps.umutex_info_map); + u64 key, next_key; + struct mutex_info info; + while (bpf_map_get_next_key(fd, &key, &next_key) == 0) { + int err = bpf_map_lookup_elem(fd, &next_key, &info); + if (err == 0 && info.contended_total != 0) { // 添加过滤条件 + printf(" %15llu %15llums %15llums %15llums %15d %15d %20s\n", + next_key, info.locked_total/1000000, info.locked_max/1000000, info.contended_total/1000000, info.count, info.last_owner, info.last_name); + } + key = next_key; } - avg_delay = info.sum_delay / info.sum_count; - printf("%02d:%02d:%02d | %-15lf %-15lf %-15lf |\n", - hour, min, sec, avg_delay / 1000.0, info.max_delay / 1000.0, info.min_delay / 1000.0); return 0; } +static int schedule_print() +{ + int err,key = 0; + err = bpf_map_lookup_elem(schedulemap_fd,&key,&sd_ctrl); + if (err < 0) { + fprintf(stderr, "failed to lookup infos: %d\n", err); + return -1; + } + if(!sd_ctrl.schedule_func) return 0; + + if(sd_ctrl.prev_watcher == SCHEDULE_WACTHER ){ + printf("%-8s %s\n", " TIME ", "avg_delay/μs max_delay/μs max_proc_name min_delay/μs min_proc_name"); + sd_ctrl.prev_watcher = SCHEDULE_WACTHER + 9;//打印表头功能关 + err = bpf_map_update_elem(schedulemap_fd, &key, &sd_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + } + } + else if(sd_ctrl.prev_watcher == SCHEDULE_WACTHER +1){ + // printf("sd_ctrl.prev_watcher = %d\n",sd_ctrl.prev_watcher); + printf("调度延时大于%dms的进程:\n",sd_ctrl.min_us/1000); + printf("%s\n","pid COMM schedule_delay/us"); + sd_ctrl.prev_watcher = SCHEDULE_WACTHER + 9;//打印表头功能关. + err = bpf_map_update_elem(schedulemap_fd, &key, &sd_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + } + } + + if(!sd_ctrl.min_us_set){ + struct sum_schedule info; + int err, fd = bpf_map__fd(sd_skel->maps.sys_schedule); + time_t now = time(NULL); + struct tm *localTime = localtime(&now); + int hour = localTime->tm_hour; + int min = localTime->tm_min; + int sec = localTime->tm_sec; + unsigned long long avg_delay; + err = bpf_map_lookup_elem(fd, &key, &info); + if (err < 0) { + fprintf(stderr, "failed to lookup infos: %d\n", err); + return -1; + } + avg_delay = info.sum_delay / info.sum_count; + if (!ifprint) { + ifprint=1; + }else{ + printf("%02d:%02d:%02d %-15lf %-15lf %10s %15lf %15s\n", + hour, min, sec, avg_delay / 1000.0, info.max_delay / 1000.0,info.proc_name_max,info.min_delay / 1000.0,info.proc_name_min); + } + } + else{ + struct proc_schedule info; + struct proc_id id_key; + struct proc_history prev_info; + int key = 0; + int err, fd1 = bpf_map__fd(sd_skel->maps.threshold_schedule),fd2 = bpf_map__fd(sd_skel->maps.proc_histories); + err = bpf_map_lookup_elem(fd1, &key, &info); + if (err < 0) { + fprintf(stderr, "failed to lookup infos: %d\n", err); + return -1; + } + if (info.delay / 1000 > sd_ctrl.min_us&&info.id.pid!=0) { + id_key.pid = info.id.pid; + id_key.cpu_id = info.id.cpu_id; + err = bpf_map_lookup_elem(fd2, &id_key, &prev_info); + if (err < 0) { + fprintf(stderr, "Failed to lookup proc_histories with PID %d and CPU ID %d: %d\n", id_key.pid, id_key.cpu_id, err); + return -1; + } + if (!entry_exists(info.id.pid, info.proc_name, info.delay / 1000)) { + printf("%-10d %-16s %15lld", info.id.pid, info.proc_name, info.delay / 1000); + add_entry(info.id.pid, info.proc_name, info.delay / 1000); + for (int i = 0; i < 2; i++) { + if (prev_info.last[i].pid != 0) { + printf(" Previous Process %d: PID=%-10d Name=%-16s ", i+1, prev_info.last[i].pid, prev_info.last[i].comm); + } + } + printf("\n"); + } + + } + } + + return 0; +} + + static int mq_event(void *ctx, void *data,unsigned long data_sz) { time_t now = time(NULL);// 获取当前时间 struct tm *localTime = localtime(&now);// 将时间转换为本地时间结构 - printf("\n\nTime: %02d:%02d:%02d\n",localTime->tm_hour, localTime->tm_min, localTime->tm_sec); - printf("-----------------------------------------------------------------------------------------------------------------------\n"); const struct mq_events *e = data; - - printf("Mqdes: %-8llu msg_len: %-8llu msg_prio: %-8llu\n",e->mqdes,e->msg_len,e->msg_prio); - printf("SND_PID: %-8lu SND_enter_time: %-16llu SND_exit_time: %-16llu\n", - e->send_pid,e->send_enter_time,e->send_exit_time); - printf("RCV_PID: %-8lu RCV_enter_time: %-16llu RCV_exit_time: %-16llu\n", - e->rcv_pid,e->rcv_enter_time,e->rcv_exit_time); - printf("-------------------------------------------------------------------------------\n"); - - printf("SND_Delay/ms: %-8.2f RCV_Delay/ms: %-8.2f Delay/ms: %-8.5f\n", - (e->send_exit_time - e->send_enter_time)/1000000.0, - (e->rcv_exit_time - e->rcv_enter_time)/1000000.0, - (e->rcv_exit_time - e->send_enter_time)/1000000.0); - printf("-----------------------------------------------------------------------------------------------------------------------\n\n"); + float send_delay,rcv_delay,delay; + if(!e->send_enter_time || !e->send_exit_time || !e->rcv_enter_time || !e->rcv_exit_time) { + printf("erro!\n"); + return 0; + } + send_delay = (e->send_exit_time - e->send_enter_time)/1000000.0; + rcv_delay = (e->rcv_exit_time - e->rcv_enter_time)/1000000.0; + if(e->send_enter_time < e->rcv_enter_time){ + delay = (e->rcv_exit_time - e->send_enter_time)/1000000.0; + }else{ + delay = (e->rcv_exit_time - e->send_enter_time)/1000000.0 + send_delay + rcv_delay; + } + printf("%02d:%02d:%02d %-8u %-8u %-8u \t%-16llu %-16llu %-16llu %-16llu\t%-15.5f %-15.5f %-15.5f\n", + localTime->tm_hour, localTime->tm_min, localTime->tm_sec, + e->mqdes,e->send_pid,e->rcv_pid, + e->send_enter_time,e->send_exit_time,e->rcv_enter_time,e->rcv_exit_time, + send_delay,rcv_delay,delay); return 0; } @@ -503,6 +766,14 @@ static int mq_event(void *ctx, void *data,unsigned long data_sz) int main(int argc, char **argv) { struct ring_buffer *rb = NULL; + struct bpf_map *cs_ctrl_map = NULL; + struct bpf_map *sar_ctrl_map = NULL; + struct bpf_map *sc_ctrl_map = NULL; + struct bpf_map *preempt_ctrl_map = NULL; + struct bpf_map *schedule_ctrl_map = NULL; + struct bpf_map *mq_ctrl_map = NULL; + struct bpf_map *mu_ctrl_map = NULL; + int key = 0; int err; err = argp_parse(&argp, argc, argv, 0, NULL, NULL); if (err) @@ -544,6 +815,19 @@ int main(int argc, char **argv) fprintf(stderr, "Failed to load and verify BPF skeleton\n"); goto cs_delay_cleanup; } + + err = common_pin_map(&cs_ctrl_map,cs_skel->obj,"cs_ctrl_map",cs_ctrl_path); + if(err < 0){ + goto cs_delay_cleanup; + } + csmap_fd = bpf_map__fd(cs_ctrl_map); + struct cs_ctrl init_value = {false,CS_WACTHER}; + err = bpf_map_update_elem(csmap_fd, &key, &init_value, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + goto cs_delay_cleanup; + } + /* Attach tracepoints */ err = cs_delay_bpf__attach(cs_skel); if (err) @@ -570,6 +854,17 @@ int main(int argc, char **argv) goto preempt_cleanup; } + err = common_pin_map(&preempt_ctrl_map,preempt_skel->obj,"preempt_ctrl_map",preempt_ctrl_path); + if(err < 0){ + goto preempt_cleanup; + } + preemptmap_fd = bpf_map__fd(preempt_ctrl_map); + struct preempt_ctrl init_value = {false,PREEMPT_WACTHER}; + err = bpf_map_update_elem(preemptmap_fd, &key, &init_value, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + goto preempt_cleanup; + } err = preempt_bpf__attach(preempt_skel); if (err) { fprintf(stderr, "Failed to attach BPF skeleton\n"); @@ -597,6 +892,17 @@ int main(int argc, char **argv) fprintf(stderr, "Failed to load and verify BPF skeleton\n"); goto sc_delay_cleanup; } + err = common_pin_map(&sc_ctrl_map,sc_skel->obj,"sc_ctrl_map",sc_ctrl_path); + if(err < 0){ + goto sc_delay_cleanup; + } + scmap_fd = bpf_map__fd(sc_ctrl_map); + struct sc_ctrl init_value = {false,SC_WACTHER}; + err = bpf_map_update_elem(scmap_fd, &key, &init_value, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + goto sc_delay_cleanup; + } /* Attach tracepoints */ err = sc_delay_bpf__attach(sc_skel); if (err) @@ -604,13 +910,18 @@ int main(int argc, char **argv) fprintf(stderr, "Failed to attach BPF skeleton\n"); goto sc_delay_cleanup; } + printf("%-8s %-8s %-15s %-15s\n","Time","Pid","syscall_id","delay/ms"); rb = ring_buffer__new(bpf_map__fd(sc_skel->maps.rb), syscall_delay_print, NULL, NULL); //ring_buffer__new() API,允许在不使用额外选项数据结构下指定回调 if (!rb) { err = -1; fprintf(stderr, "Failed to create ring buffer\n"); goto sc_delay_cleanup; } + + }else if(env.SCHEDULE_DELAY){ + + sd_skel = schedule_delay_bpf__open(); if (!sd_skel) { fprintf(stderr, "Failed to open and load BPF skeleton\n"); @@ -621,12 +932,23 @@ int main(int argc, char **argv) fprintf(stderr, "Failed to load and verify BPF skeleton\n"); goto schedule_cleanup; } + err = common_pin_map(&schedule_ctrl_map,sd_skel->obj,"schedule_ctrl_map",schedule_ctrl_path); + if(err < 0){ + goto schedule_cleanup; + } + schedulemap_fd = bpf_map__fd(schedule_ctrl_map); + struct schedule_ctrl init_value = {false,false,10000,SCHEDULE_WACTHER}; + + err = bpf_map_update_elem(schedulemap_fd, &key, &init_value, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + goto schedule_cleanup; + } err = schedule_delay_bpf__attach(sd_skel); if (err) { fprintf(stderr, "Failed to attach BPF skeleton\n"); goto schedule_cleanup; } - printf("%-8s %s\n", " TIME ", "avg_delay/μs max_delay/μs min_delay/μs"); }else if (env.SAR){ /* Load and verify BPF application */ sar_skel = sar_bpf__open(); @@ -649,13 +971,24 @@ int main(int argc, char **argv) if (err) goto sar_cleanup; + err = common_pin_map(&sar_ctrl_map,sar_skel->obj,"sar_ctrl_map",sar_ctrl_path); + if(err < 0){ + goto sar_cleanup; + } + sarmap_fd = bpf_map__fd(sar_ctrl_map); + struct sar_ctrl init_value = {false,false,SAR_WACTHER}; + err = bpf_map_update_elem(sarmap_fd, &key, &init_value, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + goto sar_cleanup; + } + err = sar_bpf__attach(sar_skel); if (err) { fprintf(stderr, "Failed to attach BPF skeleton\n"); goto sar_cleanup; } - printf(" time proc/s cswch/s runqlen irqTime/us softirq/us idle/ms kthread/us sysc/ms utime/ms sys/ms \n"); }else if(env.MQ_DELAY){ /* Load and verify BPF application */ mq_skel = mq_delay_bpf__open(); @@ -671,6 +1004,19 @@ int main(int argc, char **argv) fprintf(stderr, "Failed to load and verify BPF skeleton\n"); goto mq_delay_cleanup; } + + err = common_pin_map(&mq_ctrl_map,mq_skel->obj,"mq_ctrl_map",mq_ctrl_path); + if(err < 0){ + goto mq_delay_cleanup; + } + mqmap_fd = bpf_map__fd(mq_ctrl_map); + struct mq_ctrl init_value = {false,MQ_WACTHER}; + err = bpf_map_update_elem(mqmap_fd, &key, &init_value, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + goto mq_delay_cleanup; + } + /* Attach tracepoints */ err = mq_delay_bpf__attach(mq_skel); if (err) @@ -678,16 +1024,62 @@ int main(int argc, char **argv) fprintf(stderr, "Failed to attach BPF skeleton\n"); goto mq_delay_cleanup; } + printf("%-8s %-8s %-8s %-8s \t%-16s %-16s %-16s %-16s\t%-15s %-15s %-15s\n","Time","Mqdes","SND_PID","RCV_PID","SND_Enter","SND_EXit","RCV_Enter","RCV_EXit","SND_Delay/ms","RCV_Delay/ms","Delay/ms"); rb = ring_buffer__new(bpf_map__fd(mq_skel->maps.rb), mq_event, NULL, NULL); //ring_buffer__new() API,允许在不使用额外选项数据结构下指定回调 if (!rb) { err = -1; fprintf(stderr, "Failed to create ring buffer\n"); goto mq_delay_cleanup; } + }else if (env.MUTRACE) { + mu_skel = mutrace_bpf__open(); + if (!mu_skel) { + fprintf(stderr, "Failed to open and load BPF skeleton\n"); + return 1; + } + + err = mutrace_bpf__load(mu_skel); + if (err) { + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); + goto mutrace_cleanup; + } + err = common_pin_map(&mu_ctrl_map,mu_skel->obj,"mu_ctrl_map",mu_ctrl_path); + if(err < 0){ + goto mutrace_cleanup; + } + mumap_fd = bpf_map__fd(mu_ctrl_map); + struct mu_ctrl init_value = {false,false,false,MUTEX_WATCHER}; + + err = bpf_map_update_elem(mumap_fd, &key, &init_value, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + goto mutrace_cleanup; + } + //ctrl + if(err < 0){ + goto mutrace_cleanup; + } + //ctrl + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + goto mutrace_cleanup; + } + err = attach(mu_skel); + if (err) { + fprintf(stderr, "Failed to attach BPF skeleton\n"); + goto mutrace_cleanup; + } + + rb = ring_buffer__new(bpf_map__fd(mu_skel->maps.rb), mutrace_print, NULL, NULL); + if (!rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer\n"); + goto mutrace_cleanup; + } } while (!exiting) { if(env.SAR){ - sleep(1); + sleep(env.period); err = print_all(); if (err == -EINTR) { err = 0; @@ -722,11 +1114,11 @@ int main(int argc, char **argv) printf("Error polling perf buffer: %d\n", err); break; } - time_t now = time(NULL);// 获取当前时间 - struct tm *localTime = localtime(&now);// 将时间转换为本地时间结构 - printf("\n\nTime: %02d:%02d:%02d\n",localTime->tm_hour, localTime->tm_min, localTime->tm_sec); - printf("----------------------------------------------------------------------------------------------------------\n"); - sleep(1); + // time_t now = time(NULL);// 获取当前时间 + // struct tm *localTime = localtime(&now);// 将时间转换为本地时间结构 + // printf("\n\nTime: %02d:%02d:%02d\n",localTime->tm_hour, localTime->tm_min, localTime->tm_sec); + // printf("----------------------------------------------------------------------------------------------------------\n"); + // sleep(1); } else if (env.PREEMPT) { err = ring_buffer__poll(rb, 100 /* timeout, ms */); @@ -753,7 +1145,7 @@ int main(int argc, char **argv) sleep(2); } else if (env.SCHEDULE_DELAY){ - err = schedule_print(sd_skel->maps.sys_schedule); + err = schedule_print(); if (err == -EINTR) { err = 0; break; @@ -761,7 +1153,9 @@ int main(int argc, char **argv) if (err < 0) { break; } - sleep(1); + if(env.SCHEDULE_DELAY&&!sd_ctrl.min_us_set){ + sleep(1); + } } else if(env.MQ_DELAY){ err = ring_buffer__poll(rb, 1000 /* timeout, s */); @@ -774,6 +1168,27 @@ int main(int argc, char **argv) break; } } + else if (env.MUTRACE) { + err = ring_buffer__poll(rb, 100 /* timeout, ms */); + if (err == -EINTR) { + err = 0; + break; + } + if (err < 0) { + printf("Error polling perf buffer: %d\n", err); + break; + } + if(env.MUTRACE&&mu_ctrl.mutex_detail){ + err = kmutex_detail(); + sleep(1); + printf("-------------------------------------------------------------\n"); + }else if(env.MUTRACE&&mu_ctrl.umutex){ + err = umutex_detail(); + sleep(1); + printf("-------------------------------------------------------------\n"); + } + + } else { printf("正在开发中......\n-c 打印cs_delay:\t对内核函数schedule()的执行时长进行测试;\n-s sar工具;\n-y 打印sc_delay:\t系统调用运行延迟进行检测; \n-p 打印preempt_time:\t对抢占调度时间输出;\n"); break; @@ -781,30 +1196,41 @@ int main(int argc, char **argv) } cs_delay_cleanup: + bpf_map__unpin(cs_ctrl_map, cs_ctrl_path); ring_buffer__free(rb); cs_delay_bpf__destroy(cs_skel); return err < 0 ? -err : 0; sar_cleanup: + bpf_map__unpin(sar_ctrl_map, sar_ctrl_path); sar_bpf__destroy(sar_skel); return err < 0 ? -err : 0; sc_delay_cleanup: + bpf_map__unpin(sc_ctrl_map, sc_ctrl_path); ring_buffer__free(rb); sc_delay_bpf__destroy(sc_skel); return err < 0 ? -err : 0; preempt_cleanup: + bpf_map__unpin(preempt_ctrl_map, preempt_ctrl_path); ring_buffer__free(rb); preempt_bpf__destroy(preempt_skel); return err < 0 ? -err : 0; schedule_cleanup: + bpf_map__unpin(schedule_ctrl_map, schedule_ctrl_path); schedule_delay_bpf__destroy(sd_skel); return err < 0 ? -err : 0; mq_delay_cleanup: + bpf_map__unpin(mq_ctrl_map, mq_ctrl_path); ring_buffer__free(rb); mq_delay_bpf__destroy(mq_skel); return err < 0 ? -err : 0; + +mutrace_cleanup: + ring_buffer__free(rb); + mutrace_bpf__destroy(mu_skel); + return err < 0 ? -err : 0; } diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/cs_delay.bpf.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/cs_delay.bpf.c deleted file mode 100644 index c94c5d1bf..000000000 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/cs_delay.bpf.c +++ /dev/null @@ -1,50 +0,0 @@ -#include -#include -#include -#include -#include "cpu_watcher.h" - -char LICENSE[] SEC("license") = "Dual BSD/GPL"; - -//记录时间戳; -BPF_ARRAY(start,int,u64,1); -struct { - __uint(type, BPF_MAP_TYPE_RINGBUF); - __uint(max_entries, 256 * 1024); -} rb SEC(".maps"); - -SEC("kprobe/schedule") -int BPF_KPROBE(schedule) -{ - u64 t1; - t1 = bpf_ktime_get_ns()/1000; - int key =0; - bpf_map_update_elem(&start,&key,&t1,BPF_ANY); - return 0; -} - -SEC("kretprobe/schedule") -int BPF_KRETPROBE(schedule_exit) -{ - u64 t2 = bpf_ktime_get_ns()/1000; - u64 t1,delay; - int key = 0; - u64 *val = bpf_map_lookup_elem(&start,&key); - if (val != 0) - { - t1 = *val; - delay = t2 - t1; - bpf_map_delete_elem(&start, &key); - }else{ - return 0; - } - struct event *e; - e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); - if (!e) return 0; - e->t1=t1; - e->t2=t2; - e->delay=delay; - bpf_ringbuf_submit(e, 0); - return 0; -} - diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/docs/image/image13.png b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/docs/image/image13.png new file mode 100644 index 000000000..566fd1c1e Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/docs/image/image13.png differ diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/grafana_cpu_watcher_dashboard.json b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/grafana_cpu_watcher_dashboard.json new file mode 100644 index 000000000..9177bf611 --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/grafana_cpu_watcher_dashboard.json @@ -0,0 +1,1177 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"avg_delay/us\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "1", + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"max_delay/us\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "interval": "1", + "legendFormat": "__auto", + "range": true, + "refId": "B", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"min_delay/μs\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "interval": "1", + "legendFormat": "__auto", + "range": true, + "refId": "C", + "useBackend": false + } + ], + "title": "schedule_delay", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"irqTime/us\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "irqTime", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"duration_ns\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "preempt_delay", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "scheme", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "smooth", + "lineWidth": 3, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "right", + "showLegend": false + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"proc/s\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "1", + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "procs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 16 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"sys/ms\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "sys", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 16 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"softirq/us\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "softirqTime", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"utime/ms\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "utime", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 24 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"idle/ms\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "1", + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "idle", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 32 + }, + "id": 7, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"kthread/us\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "kthread", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 40 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"cswch/s\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "cswch", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "scheme", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "smooth", + "lineWidth": 3, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 48 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "right", + "showLegend": false + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"delay/ms\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "1", + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "sys_delay", + "type": "timeseries" + } + ], + "refresh": "", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timeRangeUpdatedDuringEditOrView": false, + "timepicker": {}, + "timezone": "browser", + "title": "cpu_watcher_vis", + "uid": "cdlnxoxcy4zr4b", + "version": 4, + "weekStart": "" +} \ No newline at end of file diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/cpu_watcher.h b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/include/cpu_watcher.h similarity index 54% rename from eBPF_Supermarket/CPU_Subsystem/cpu_watcher/cpu_watcher.h rename to eBPF_Supermarket/CPU_Subsystem/cpu_watcher/include/cpu_watcher.h index 6812096ce..7ffb2b3fa 100644 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/cpu_watcher.h +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/include/cpu_watcher.h @@ -12,17 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. // -// author: zhangziheng0525@163.com -// -// eBPF map for libbpf sar +// author: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com +#ifndef CPU_WATCHER_H +#define CPU_WATCHER_H + #include #include -typedef long long unsigned int u64; +typedef unsigned long long u64; typedef unsigned int u32; -typedef __kernel_mqd_t mqd_t; +typedef __kernel_mqd_t mqd_t; #define __user -#define MAX_CPU_NR 128 +#define MAX_CPU_NR 128 #define TASK_COMM_LEN 20 #define SYSCALL_MIN_TIME 1E7 #define MAX_SYSCALL_COUNT 100 @@ -36,56 +37,49 @@ typedef __kernel_mqd_t mqd_t; /// @param type1 键的类型 /// @param type2 值的类型 /// @param MAX_ENTRIES map容量 -#define BPF_ARRAY(name, type1,type2,MAX_ENTRIES ) \ - struct \ - { \ - __uint(type, BPF_MAP_TYPE_ARRAY); \ - __uint(key_size, sizeof(type1)); \ - __uint(value_size, sizeof(type2)); \ - __uint(max_entries, MAX_ENTRIES); \ +#define BPF_ARRAY(name, type1, type2, MAX_ENTRIES) \ + struct { \ + __uint(type, BPF_MAP_TYPE_ARRAY); \ + __uint(key_size, sizeof(type1)); \ + __uint(value_size, sizeof(type2)); \ + __uint(max_entries, MAX_ENTRIES); \ } name SEC(".maps") - /// @brief 创建一个指定名字和键值类型的ebpf散列表 /// @param name 新散列表的名字 /// @param type1 键的类型 /// @param type2 值的类型 /// @param MAX_ENTRIES 哈希map容量 -#define BPF_HASH(name, type1,type2,MAX_ENTRIES ) \ - struct \ - { \ - __uint(type, BPF_MAP_TYPE_HASH); \ - __uint(key_size, sizeof(type1)); \ - __uint(value_size, sizeof(type2)); \ - __uint(max_entries, MAX_ENTRIES); \ - } name SEC(".maps") - +#define BPF_HASH(name, type1, type2, MAX_ENTRIES) \ + struct { \ + __uint(type, BPF_MAP_TYPE_HASH); \ + __uint(key_size, sizeof(type1)); \ + __uint(value_size, sizeof(type2)); \ + __uint(max_entries, MAX_ENTRIES); \ + } name SEC(".maps") /// @brief 创建一个指定名字和键值类型的ebpf每CPU数组 /// @param name 新散列表的名字 /// @param type1 键的类型 /// @param type2 值的类型 /// @param MAX_ENTRIES map容量 -#define BPF_PERCPU_ARRAY(name, type1,type2,MAX_ENTRIES ) \ - struct \ - { \ - __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); \ - __uint(key_size, sizeof(type1)); \ - __uint(value_size, sizeof(type2)); \ - __uint(max_entries, MAX_ENTRIES); \ - } name SEC(".maps") - +#define BPF_PERCPU_ARRAY(name, type1, type2, MAX_ENTRIES) \ + struct { \ + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); \ + __uint(key_size, sizeof(type1)); \ + __uint(value_size, sizeof(type2)); \ + __uint(max_entries, MAX_ENTRIES); \ + } name SEC(".maps") /// @brief 创建一个指定名字和键值类型的ebpf每CPU散列表 /// @param name 新散列表的名字 /// @param type1 键的类型 /// @param type2 值的类型 /// @param MAX_ENTRIES map容量 -#define BPF_PERCPU_HASH(name, type1,type2,MAX_ENTRIES ) \ - struct \ - { \ - __uint(type, BPF_MAP_TYPE_PERCPU_HASH); \ - __uint(key_size, sizeof(type1)); \ - __uint(value_size, sizeof(type2)); \ - __uint(max_entries, MAX_ENTRIES); \ - } name SEC(".maps") +#define BPF_PERCPU_HASH(name, type1, type2, MAX_ENTRIES) \ + struct { \ + __uint(type, BPF_MAP_TYPE_PERCPU_HASH); \ + __uint(key_size, sizeof(type1)); \ + __uint(value_size, sizeof(type2)); \ + __uint(max_entries, MAX_ENTRIES); \ + } name SEC(".maps") /*----------------------------------------------*/ /* cs_delay结构体 */ @@ -93,22 +87,22 @@ typedef __kernel_mqd_t mqd_t; #ifndef __CS_DELAY_H #define __CS_DELAY_H struct event { - long unsigned int t1; - long unsigned int t2; - long unsigned int delay; + u64 t1; + u64 t2; + u64 delay; }; #endif /* __CS_DELAY_H */ /*----------------------------------------------*/ /* syscall_delay结构体 */ /*----------------------------------------------*/ -struct syscall_flags{ - long unsigned int start_time; +struct syscall_flags { + u64 start_time; int syscall_id; }; struct syscall_events {//每个进程一个 - int pid,count; + int pid, count; char comm[TASK_COMM_LEN]; u64 delay; u64 syscall_id; @@ -116,7 +110,7 @@ struct syscall_events {//每个进程一个 /*----------------------------------------------*/ /* preempt_event结构体 */ /*----------------------------------------------*/ -struct preempt_event{ +struct preempt_event { pid_t prev_pid; pid_t next_pid; unsigned long long duration; @@ -126,22 +120,68 @@ struct preempt_event{ /* schedule_delay相关结构体 */ /*----------------------------------------------*/ //标识不同进程 -struct proc_id{ +struct proc_id { int pid; int cpu_id; }; //标识该进程的调度信息 -struct schedule_event{ +struct schedule_event { int pid; int count;//调度次数 unsigned long long enter_time; }; //整个系统所有调度信息 -struct sum_schedule{ +struct sum_schedule { unsigned long long sum_count; unsigned long long sum_delay; unsigned long long max_delay; unsigned long long min_delay; + char proc_name_max[TASK_COMM_LEN]; + char proc_name_min[TASK_COMM_LEN]; +}; + +struct proc_schedule { + struct proc_id id; + unsigned long long delay; + char proc_name[TASK_COMM_LEN]; +}; + +struct proc_info { + pid_t pid; + char comm[TASK_COMM_LEN]; +}; + +struct proc_history { + struct proc_info last[2]; // 存储最后两个调度的进程信息 +}; + +/*----------------------------------------------*/ +/* mutrace相关结构体 */ +/*----------------------------------------------*/ +struct mutex_info { + u64 locked_total;//锁被持有的总时间 + u64 locked_max;//锁被持有的最长时间 + u64 contended_total;//锁发生竞争的总时间 + int count;//记录锁被争用的总次数 + pid_t last_owner;//最后一次持有该锁的线程 ID + char last_name[TASK_COMM_LEN]; + u64 acquire_time; // 锁每次被获取的时间戳,方便后续计算 + u64 ptr;//地址 +}; + +struct mutex_contention_event { + u64 ptr;//锁地址 + pid_t owner_pid;//持有者pid + pid_t contender_pid;//抢占者pid + char contender_name[TASK_COMM_LEN]; + char owner_name[TASK_COMM_LEN]; + int owner_prio; + int contender_prio; +}; + +struct trylock_info { + void *__mutex; + u64 start_time; }; /*----------------------------------------------*/ @@ -151,36 +191,34 @@ struct mq_events { int send_pid; int rcv_pid; mqd_t mqdes; - size_t msg_len; - unsigned int msg_prio; - - u64 send_enter_time; - u64 send_exit_time; - u64 rcv_enter_time; - u64 rcv_exit_time; + size_t msg_len; + unsigned int msg_prio; + u64 send_enter_time; + u64 send_exit_time; + u64 rcv_enter_time; + u64 rcv_exit_time; }; struct send_events { int send_pid; u64 Key_msg_ptr; - mqd_t mqdes; - size_t msg_len; - unsigned int msg_prio; - const char *u_msg_ptr; - const void *src; - u64 send_enter_time; - u64 send_exit_time; + size_t msg_len; + unsigned int msg_prio; + const char *u_msg_ptr; + const void *src; + u64 send_enter_time; + u64 send_exit_time; }; struct rcv_events { int rcv_pid; u64 Key_msg_ptr; mqd_t mqdes; - size_t msg_len; - unsigned int msg_prio; - const char *u_msg_ptr; - const void *dest; - u64 rcv_enter_time; - u64 rcv_exit_time; + size_t msg_len; + unsigned int msg_prio; + const char *u_msg_ptr; + const void *dest; + u64 rcv_enter_time; + u64 rcv_exit_time; }; /*----------------------------------------------*/ /* cswch_args结构体 */ @@ -219,4 +257,49 @@ struct idleStruct { u64 pad; unsigned int state; unsigned int cpu_id; -}; \ No newline at end of file +}; + +/*----------------------------------------------*/ +/* 控制板块 */ +/*----------------------------------------------*/ +struct sar_ctrl{ + bool sar_func; + bool percent; + int prev_watcher; +}; + +struct cs_ctrl{ + bool cs_func; + int prev_watcher; +}; + +struct sc_ctrl{ + bool sc_func; + int prev_watcher; +}; + +struct preempt_ctrl{ + bool preempt_func; + int prev_watcher; +}; + +struct schedule_ctrl{ + bool schedule_func; + bool min_us_set; + int min_us; + int prev_watcher; +}; + +struct mq_ctrl{ + bool mq_func; + int prev_watcher; +}; + +struct mu_ctrl{ + bool mu_func; + bool mutex_detail; + bool umutex; + int prev_watcher; +}; + +#endif // CPU_WATCHER_H \ No newline at end of file diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/include/cpu_watcher_helper.h b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/include/cpu_watcher_helper.h new file mode 100644 index 000000000..08e38f0e1 --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/include/cpu_watcher_helper.h @@ -0,0 +1,353 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com +#ifndef CPU_WATCHER_HELPER_H +#define CPU_WATCHER_HELPER_H + +#include +#include "cpu_watcher.h" + +#define SAR_WACTHER 10 +#define CS_WACTHER 20 +#define SC_WACTHER 30 +#define PREEMPT_WACTHER 40 +#define SCHEDULE_WACTHER 50 +#define MQ_WACTHER 60 +#define MUTEX_WATCHER 70 +#define HASH_SIZE 1024 + +/*----------------------------------------------*/ +/* ewma算法 */ +/*----------------------------------------------*/ +//滑动窗口周期,用于计算alpha +#define CYCLE 10 +//阈值容错空间; +#define TOLERANCE 1.0 +struct ewma_info{ + double previousEWMA; + int count; + int cycle;//cycle是滑动窗口周期大小 +}; + +double calculateEWMA(double previousEWMA, double dataPoint, double alpha) { + return alpha * dataPoint + (1 - alpha) * previousEWMA;//当前时间点的ewma +} + +bool dynamic_filter(struct ewma_info *ewma_syscall_delay, double dataPoint) { + double alpha,threshold; + if(ewma_syscall_delay->cycle==0) alpha = 2.0 /(CYCLE + 1); // 计算 alpha + else alpha = 2.0 /(ewma_syscall_delay->cycle + 1); + + if(ewma_syscall_delay->previousEWMA == 0) {//初始化ewma算法,则赋值previousEWMA = dataPoint 并打印 + ewma_syscall_delay->previousEWMA = dataPoint; + return 1; + } + if(ewma_syscall_delay->count <30){ + ewma_syscall_delay->previousEWMA = calculateEWMA(ewma_syscall_delay->previousEWMA,dataPoint,alpha);//计算 + return 1; + } + else{ + ewma_syscall_delay->previousEWMA = calculateEWMA(ewma_syscall_delay->previousEWMA,dataPoint,alpha);//计算 + threshold = ewma_syscall_delay->previousEWMA * TOLERANCE; + if(dataPoint >= threshold) return 1; + } + return 0; +} + +/*----------------------------------------------*/ +/* bpf file system */ +/*----------------------------------------------*/ +const char *sar_ctrl_path = "/sys/fs/bpf/cpu_watcher_map/sar_ctrl_map"; +const char *cs_ctrl_path = "/sys/fs/bpf/cpu_watcher_map/cs_ctrl_map"; +const char *sc_ctrl_path = "/sys/fs/bpf/cpu_watcher_map/sc_ctrl_map"; +const char *preempt_ctrl_path = "/sys/fs/bpf/cpu_watcher_map/preempt_ctrl_map"; +const char *schedule_ctrl_path = "/sys/fs/bpf/cpu_watcher_map/schedule_ctrl_map"; +const char *mq_ctrl_path = "/sys/fs/bpf/cpu_watcher_map/mq_ctrl_map"; +const char *mu_ctrl_path = "/sys/fs/bpf/cpu_watcher_map/mu_ctrl_map"; + +int common_pin_map(struct bpf_map **bpf_map, const struct bpf_object *obj, const char *map_name, const char *ctrl_path) +{ + int ret; + + *bpf_map = bpf_object__find_map_by_name(obj, map_name);//查找具有指定名称的 BPF 映射 + if (!*bpf_map) { + fprintf(stderr, "Failed to find BPF map\n"); + return -1; + } + // 用于防止上次没有成功 unpin 掉这个 map + bpf_map__unpin(*bpf_map, ctrl_path); + ret = bpf_map__pin(*bpf_map, ctrl_path); + if (ret){ + fprintf(stderr, "Failed to pin BPF map\n"); + return -1; + }//找到pin上 + + return 0; +} + +int update_sar_ctrl_map(struct sar_ctrl sar_ctrl){ + int err,key = 0; + int srcmap_fd; + + srcmap_fd = bpf_obj_get(sar_ctrl_path); + if (srcmap_fd < 0) { + fprintf(stderr,"Failed to open sar_ctrl_map file\n"); + return srcmap_fd; + } + err = bpf_map_update_elem(srcmap_fd,&key,&sar_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update sar_ctrl_map elem\n"); + return err; + } + + return 0; +} + +int update_cs_ctrl_map(struct cs_ctrl cs_ctrl){ + int err,key = 0; + int srcmap_fd; + + srcmap_fd = bpf_obj_get(cs_ctrl_path); + if (srcmap_fd < 0) { + fprintf(stderr,"Failed to open cs_ctrl_map file\n"); + return srcmap_fd; + } + err = bpf_map_update_elem(srcmap_fd,&key,&cs_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update cs_ctrl_map elem\n"); + return err; + } + + return 0; +} + +int update_sc_ctrl_map(struct sc_ctrl sc_ctrl){ + int err,key = 0; + int srcmap_fd; + + srcmap_fd = bpf_obj_get(sc_ctrl_path); + if (srcmap_fd < 0) { + fprintf(stderr,"Failed to open sc_ctrl_map file\n"); + return srcmap_fd; + } + err = bpf_map_update_elem(srcmap_fd,&key,&sc_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update sc_ctrl_map elem\n"); + return err; + } + + return 0; +} + +int update_preempt_ctrl_map(struct preempt_ctrl preempt_ctrl){ + int err,key = 0; + int srcmap_fd; + + srcmap_fd = bpf_obj_get(preempt_ctrl_path); + if (srcmap_fd < 0) { + fprintf(stderr,"Failed to open preempt_ctrl_map file\n"); + return srcmap_fd; + } + err = bpf_map_update_elem(srcmap_fd,&key,&preempt_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update preempt_ctrl_map elem\n"); + return err; + } + + return 0; +} + +int update_schedule_ctrl_map(struct schedule_ctrl schedule_ctrl){ + int err,key = 0; + int srcmap_fd; + + srcmap_fd = bpf_obj_get(schedule_ctrl_path); + if (srcmap_fd < 0) { + fprintf(stderr,"Failed to open schedule_ctrl_map file\n"); + return srcmap_fd; + } + err = bpf_map_update_elem(srcmap_fd,&key,&schedule_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update schedule_ctrl_map elem\n"); + return err; + } + + return 0; +} + +int update_mq_ctrl_map(struct mq_ctrl mq_ctrl){ + int err,key = 0; + int srcmap_fd; + + srcmap_fd = bpf_obj_get(mq_ctrl_path); + if (srcmap_fd < 0) { + fprintf(stderr,"Failed to open mq_ctrl_map file\n"); + return srcmap_fd; + } + err = bpf_map_update_elem(srcmap_fd,&key,&mq_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update mq_ctrl_map elem\n"); + return err; + } + + return 0; +} + +int update_mu_ctrl_map(struct mu_ctrl mu_ctrl){ + int err,key = 0; + int srcmap_fd; + + srcmap_fd = bpf_obj_get(mu_ctrl_path); + if (srcmap_fd < 0) { + fprintf(stderr,"Failed to open mq_ctrl_map file\n"); + return srcmap_fd; + } + err = bpf_map_update_elem(srcmap_fd,&key,&mu_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update mq_ctrl_map elem\n"); + return err; + } + + return 0; +} +/*----------------------------------------------*/ +/* mutex_count */ +/*----------------------------------------------*/ + +typedef struct { + uint64_t ptr; + uint64_t count; +} lock_count_t; + +lock_count_t lock_counts[HASH_SIZE]; + +static uint64_t hash(uint64_t ptr) { + return ptr % HASH_SIZE; +} + +static void increment_lock_count(uint64_t ptr) { + uint64_t h = hash(ptr); + while (lock_counts[h].ptr != 0 && lock_counts[h].ptr != ptr) { + h = (h + 1) % HASH_SIZE; + } + if (lock_counts[h].ptr == 0) { + lock_counts[h].ptr = ptr; + lock_counts[h].count = 1; + } else { + lock_counts[h].count++; + } +} + +static uint64_t get_lock_count(uint64_t ptr) { + uint64_t h = hash(ptr); + while (lock_counts[h].ptr != 0 && lock_counts[h].ptr != ptr) { + h = (h + 1) % HASH_SIZE; + } + if (lock_counts[h].ptr == 0) { + return 0; + } else { + return lock_counts[h].count; + } +} + +/*----------------------------------------------*/ +/* hash */ +/*----------------------------------------------*/ + + +struct output_entry { + int pid; + char comm[16]; + long long delay; +}; + + +struct output_entry seen_entries[MAX_ENTRIES]; +int seen_count = 0; + + +bool entry_exists(int pid, const char *comm, long long delay) { + for (int i = 0; i < seen_count; i++) { + if (seen_entries[i].pid == pid && + strcmp(seen_entries[i].comm, comm) == 0 && + seen_entries[i].delay == delay) { + return true; + } + } + return false; +} + + +void add_entry(int pid, const char *comm, long long delay) { + if (seen_count < MAX_ENTRIES) { + seen_entries[seen_count].pid = pid; + strncpy(seen_entries[seen_count].comm, comm, sizeof(seen_entries[seen_count].comm)); + seen_entries[seen_count].delay = delay; + seen_count++; + } +} +/*----------------------------------------------*/ +/* uprobe */ +/*----------------------------------------------*/ +static const char object[] = "/usr/lib/x86_64-linux-gnu/libc.so.6"; + +#define __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe) \ + do \ + { \ + LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts, \ + .retprobe = is_retprobe, \ + .func_name = #sym_name); \ + skel->links.prog_name = bpf_program__attach_uprobe_opts( \ + skel->progs.prog_name, \ + -1, \ + object, \ + 0, \ + &uprobe_opts); \ + } while (false) + +#define __CHECK_PROGRAM(skel, prog_name) \ + do \ + { \ + if (!skel->links.prog_name) \ + { \ + fprintf(stderr, "[%s] no program attached for" #prog_name "\n", strerror(errno)); \ + return -errno; \ + } \ + } while (false) + +#define __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, is_retprobe) \ + do \ + { \ + __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe); \ + __CHECK_PROGRAM(skel, prog_name); \ + } while (false) + +#define ATTACH_UPROBE(skel, sym_name, prog_name) __ATTACH_UPROBE(skel, sym_name, prog_name, false) +#define ATTACH_URETPROBE(skel, sym_name, prog_name) __ATTACH_UPROBE(skel, sym_name, prog_name, true) + +#define ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name) __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, false) +#define ATTACH_URETPROBE_CHECKED(skel, sym_name, prog_name) __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, true) + +#define CHECK_ERR(cond, info) \ + if (cond) \ + { \ + fprintf(stderr, "[%s]" info "\n", strerror(errno)); \ + return -1; \ + } + +#define warn(...) fprintf(stderr, __VA_ARGS__) + + +#endif // CPU_WATCHER_HELPER_H \ No newline at end of file diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/sc_delay.bpf.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/sc_delay.bpf.c deleted file mode 100644 index ec642bf17..000000000 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/sc_delay.bpf.c +++ /dev/null @@ -1,69 +0,0 @@ -#include "vmlinux.h" -#include //包含了BPF 辅助函数 -#include -#include "cpu_watcher.h" - -char LICENSE[] SEC("license") = "Dual BSD/GPL"; - -// 定义数组映射 -//BPF_PERCPU_HASH(SyscallEnterTime,pid_t,struct syscall_flags,512);//记录时间戳 -BPF_PERCPU_HASH(SyscallEnterTime,pid_t,u64,512);//记录时间戳 -BPF_PERCPU_HASH(Events,pid_t,u64,10); - -struct { - __uint(type, BPF_MAP_TYPE_RINGBUF); - __uint(max_entries, 256 * 1024); -} rb SEC(".maps");//环形缓冲区; - - -SEC("tracepoint/raw_syscalls/sys_enter")//进入系统调用 -int tracepoint__syscalls__sys_enter(struct trace_event_raw_sys_enter *args){ - u64 start_time = bpf_ktime_get_ns()/1000;//ms - pid_t pid = bpf_get_current_pid_tgid();//获取到当前进程的pid - u64 syscall_id = (u64)args->id; - - //bpf_printk("ID:%ld\n",syscall_id); - bpf_map_update_elem(&Events,&pid,&syscall_id,BPF_ANY); - bpf_map_update_elem(&SyscallEnterTime,&pid,&start_time,BPF_ANY); - return 0; -} - -SEC("tracepoint/raw_syscalls/sys_exit")//退出系统调用 -int tracepoint__syscalls__sys_exit(struct trace_event_raw_sys_exit *args){ - u64 exit_time = bpf_ktime_get_ns()/1000;//ms - pid_t pid = bpf_get_current_pid_tgid() ;//获取到当前进程的pid - u64 syscall_id; - u64 start_time, delay; - - u64 *val = bpf_map_lookup_elem(&SyscallEnterTime, &pid); - if(val !=0){ - start_time = *val; - delay = exit_time - start_time; - bpf_map_delete_elem(&SyscallEnterTime, &pid); - }else{ - return 0; - } - - u64 *val2 = bpf_map_lookup_elem(&Events, &pid); - if(val2 !=0){ - syscall_id = *val2; - bpf_map_delete_elem(&SyscallEnterTime, &pid); - }else{ - return 0; - } - - - struct syscall_events *e; - e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); - if (!e) return 0; - - e->pid = pid; - e->delay = delay; - bpf_get_current_comm(&e->comm, sizeof(e->comm)); - e->syscall_id = syscall_id; - - bpf_ringbuf_submit(e, 0); - - - return 0; -} diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test/Makefile b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test/Makefile new file mode 100644 index 000000000..8e5da9b71 --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test/Makefile @@ -0,0 +1,21 @@ +CC = gcc +CFLAGS = -Wall -Wextra +LDFLAGS = -lrt + +.PHONY: all clean + +all: test_cpuwatcher sender receiver + +sender: mq_test_sender.c + $(CC) $(CFLAGS) -o sender mq_test_sender.c $(LDFLAGS) + +receiver: mq_test_receiver.c + $(CC) $(CFLAGS) -o receiver mq_test_receiver.c $(LDFLAGS) + +test_cpuwatcher: test_cpuwatcher.c + $(CC) $(CFLAGS) -o test_cpuwatcher test_cpuwatcher.c + +clean: + rm -f test_cpuwatcher sender receiver + # 清除 stress-ng 生成的临时文件 + rm -rf /tmp-stress-ng* diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test/mq_test_receiver.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test/mq_test_receiver.c new file mode 100644 index 000000000..240202514 --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test/mq_test_receiver.c @@ -0,0 +1,37 @@ +#include +#include +#include +#include +#include +#include +#include + +#define QUEUE_NAME "/test_queue" +#define MSG_SIZE 50 + +int main() { + mqd_t mq; + char msg_buffer[MSG_SIZE]; + unsigned int priority; + + // 打开消息队列 + mq = mq_open(QUEUE_NAME, O_RDONLY); + if (mq == (mqd_t)-1) { + perror("mq_open"); + exit(1); + } + + // 接收消息 + while (1) { + if (mq_receive(mq, msg_buffer, MSG_SIZE, &priority) == -1) { + perror("mq_receive"); + break; + } + printf("Received: %s\n", msg_buffer); + } + + // 关闭消息队列 + mq_close(mq); + + return 0; +} diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test/mq_test_sender.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test/mq_test_sender.c new file mode 100644 index 000000000..43c66e55b --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test/mq_test_sender.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include +#include +#include +#include + +#define QUEUE_NAME "/test_queue" +#define MSG_SIZE 50 +#define MAX_MSGS 10 + +int main() { + mqd_t mq; + struct mq_attr attr; + char msg_buffer[MSG_SIZE]; + unsigned int priority = 1; + int i; + + // 设置消息队列属性 + attr.mq_flags = 0; + attr.mq_maxmsg = MAX_MSGS; + attr.mq_msgsize = MSG_SIZE; + attr.mq_curmsgs = 0; + + // 创建或打开消息队列 + mq = mq_open(QUEUE_NAME, O_CREAT | O_WRONLY, 0644, &attr); + if (mq == (mqd_t)-1) { + perror("mq_open"); + exit(1); + } + + // 发送消息 + for (i = 0;i<60 ; i++) { + sprintf(msg_buffer, "Message %d", i); + if (mq_send(mq, msg_buffer, strlen(msg_buffer) + 1, priority) == -1) { + perror("mq_send"); + break; + } + printf("Sent: %s\n", msg_buffer); + sleep(1); + } + + // 关闭消息队列 + mq_close(mq); + mq_unlink(QUEUE_NAME); + + return 0; +} diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test/test_cpuwatcher.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test/test_cpuwatcher.c new file mode 100644 index 000000000..806fb55e2 --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test/test_cpuwatcher.c @@ -0,0 +1,266 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define gettid() syscall(__NR_gettid) + +static struct env { + bool sar_test; + bool cs_delay_test; + bool sc_delay_test; + bool mq_delay_test; + bool preempt_test; + bool schedule_test; + bool mutrace_test1; + bool mutrace_test2; +} env = { + .sar_test = false, + .cs_delay_test = false, + .sc_delay_test = false, + .mq_delay_test = false, + .preempt_test = false, + .schedule_test = false, + .mutrace_test1 = false, + .mutrace_test2 = false, +}; + +const char argp_program_doc[] ="To test cpu_watcher.\n"; + +static const struct argp_option opts[] = { + { "sar", 's', NULL, 0, "To test sar", 0 }, + { "cs_delay", 'c', NULL, 0, "To test cs_delay", 0 }, + { "sc_delay", 'S', NULL, 0, "To test sc_delay", 0 }, + { "mq_delay", 'm', NULL, 0, "To test mq_delay", 0 }, + { "preempt_delay", 'p', NULL, 0, "To test preempt_delay", 0 }, + { "schedule_delay", 'd', NULL, 0, "To test schedule_delay", 0 }, + { "mu_trace_kernel", 'x', NULL, 0, "To test kernel mutrace", 0 }, + { "mu_trace_user", 'u', NULL, 0, "To test user mutrace", 0 }, + { "all", 'a', NULL, 0, "To test all", 0 }, + { NULL, 'h', NULL, OPTION_HIDDEN, "show the full help", 0 }, + {}, +}; + +static error_t parse_arg(int key, char *arg, struct argp_state *state) +{ + (void)arg; + switch (key) { + case 'a': + env.sar_test = true; + env.cs_delay_test = true; + env.mq_delay_test = true; + env.preempt_test = true; + env.sc_delay_test = true; + env.schedule_test = true; + break; + case 's': + env.sar_test = true; + break; + case 'c': + env.cs_delay_test = true; + break; + case 'S': + env.sc_delay_test = true; + break; + case 'm': + env.mq_delay_test = true; + break; + case 'p': + env.preempt_test = true; + break; + case 'd': + env.schedule_test = true; + break; + case 'x': + env.mutrace_test1 = true; + break; + case 'u': + env.mutrace_test2 = true; + break; + case 'h': + argp_state_help(state, stderr, ARGP_HELP_STD_HELP); + break; + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +void *schedule_stress_test(void *arg) { + (void)arg; + while (1) { + sched_yield(); // 调度函数 + } + return NULL; +} + +void start_schedule_stress_test(int num_threads) { + pthread_t *threads = malloc(num_threads * sizeof(pthread_t)); + for (int i = 0; i < num_threads; i++) { + pthread_create(&threads[i], NULL, schedule_stress_test, NULL); + } + for (int i = 0; i < num_threads; i++) { + pthread_join(threads[i], NULL); + } + free(threads); +} + +void *func(void *arg) +{ + (void)arg; + int tpid; + tpid = gettid(); + printf("新线程pid:%d,睡眠3s后退出\n",tpid); + sleep(3); + printf("新线程退出\n"); + return NULL; +} + +void input_pid() { + int stop; + int pid = getpid(); + printf("test_proc进程的PID:【%d】\n", pid); + printf("输入任意数字继续程序的运行:"); + scanf("%d", &stop); // 使用时将其取消注释 + printf("程序开始执行...\n"); + printf("\n"); +} + +void *mutex_test_thread(void *arg) { + pthread_mutex_t *mutex = (pthread_mutex_t *)arg; + uintptr_t mutex_addr = (uintptr_t)mutex; // 获取互斥锁的地址 + + for (int i = 0; i < 10; i++) { + pthread_mutex_lock(mutex); + printf("Thread %ld (mutex address: %lu) acquired the mutex\n", + pthread_self(), (unsigned long)mutex_addr); + usleep(rand() % 1000); + pthread_mutex_unlock(mutex); + printf("Thread %ld (mutex address: %lu) released the mutex\n", + pthread_self(), (unsigned long)mutex_addr); + usleep(rand() % 1000); + } + + return NULL; +} + +void start_mutex_test(int num_threads) { + pthread_mutex_t mutex; + pthread_t *threads = malloc(num_threads * sizeof(pthread_t)); + + pthread_mutex_init(&mutex, NULL); + + for (int i = 0; i < num_threads; i++) { + pthread_create(&threads[i], NULL, mutex_test_thread, &mutex); + } + + for (int i = 0; i < num_threads; i++) { + pthread_join(threads[i], NULL); + } + + pthread_mutex_destroy(&mutex); + free(threads); +} + + +int main(int argc, char **argv){ + int err; + static const struct argp argp = { + .options = opts, + .parser = parse_arg, + .doc = argp_program_doc, + }; + + err = argp_parse(&argp, argc, argv, 0, NULL, NULL); + if (err) + return err; + + if(env.sar_test){ + printf("SAR_TEST----------------------------------------------\n"); + //SAR功能测试逻辑:系统上执行混合压力测试,包括4个顺序读写硬盘线程、4个IO操作线程,持续15秒,观察加压前后的变化。 + char *argvv[] = { "/usr/bin/stress-ng", "--hdd", "4", "--hdd-opts", "wr-seq,rd-seq", "--io", "4", "--timeout", "15s", "--metrics-brief", NULL }; + char *envp[] = { "PATH=/bin", NULL }; + printf("SAR功能测试逻辑:系统上执行混合压力测试,包括4个顺序读写硬盘线程、4个IO操作线程和4个UDP网络操作线程,持续15秒,观察加压前后的变化\n"); + printf("执行指令 stress-ng --hdd 4 --hdd-opts wr-seq,rd-seq --io 4 --udp 4 --timeout 15s --metrics-brief\n"); + execve("/usr/bin/stress-ng", argvv, envp); + perror("execve"); + printf("\n"); + } + + if(env.cs_delay_test){ + printf("CS_DELAY_TEST----------------------------------------------\n"); + //CS_DELAY功能测试逻辑:无限循环的线程函数,不断调用 sched_yield() 来放弃 CPU 使用权,模拟高调度负载。 + start_schedule_stress_test(10); // 创建10个线程进行调度压力测试 + } + + if(env.sc_delay_test){ + printf("SC_DELAY_TEST----------------------------------------------\n"); + //SC_DELAY功能测试逻辑:创建多个系统调用,观察其变化 + const int num_iterations = 1000000; // 系统调用的迭代次数 + for (int i = 0; i < num_iterations; i++) { + getpid(); // 获取进程ID + getppid(); // 获取父进程ID + time(NULL); // 获取当前时间 + syscall(SYS_gettid); // 获取线程ID + } + printf("系统调用压力测试完成。\n"); + } + + if(env.mq_delay_test){ + /*mq_delay的测试代码*/ + input_pid(); // 在mq_delay_test中调用 + system("./sender & ./receiver"); + sleep(60); + system("^Z"); + } + + if(env.preempt_test){ + printf("PREEMPT_TEST----------------------------------------------\n"); + //PREEMPT功能测试逻辑:无限循环的线程函数,不断调用 sched_yield() 来放弃 CPU 使用权,模拟高调度负载。 + start_schedule_stress_test(10); // 创建10个线程进行调度压力测试 + } + + if(env.schedule_test){ + printf("SCHEDULE_TEST----------------------------------------------\n"); + // 调度延迟测试逻辑:创建线程执行 sysbench --threads=32 --time=10 cpu run,观察加压前后的变化 + char *argvv[] = { "/usr/bin/sysbench", "--threads=32", "--time=10", "cpu", "run", NULL }; + char *envp[] = { "PATH=/bin", NULL }; + printf("调度延迟测试逻辑:\n"); + printf("执行指令 sysbench --threads=32 --time=10 cpu run\n"); + execve("/usr/bin/sysbench", argvv, envp); + perror("execve"); + printf("\n"); + } + + if(env.mutrace_test1){ + printf("MUTRACE_KERNEL_TEST----------------------------------------------\n"); + //内核态互斥锁功能测试逻辑:系统上执行混合压力测试,包括4个顺序读写硬盘线程、4个IO操作线程,持续15秒,观察加压前后的变化。 + char *argvv[] = { "/usr/bin/stress-ng", "--hdd", "4", "--hdd-opts", "wr-seq,rd-seq", "--io", "4", "--timeout", "15s", "--metrics-brief", NULL }; + char *envp[] = { "PATH=/bin", NULL }; + printf("MUTRACE功能测试逻辑:系统上执行混合压力测试,包括4个顺序读写硬盘线程、4个IO操作线程和4个UDP网络操作线程,持续15秒,观察加压前后的变化\n"); + printf("执行指令 stress-ng --hdd 4 --hdd-opts wr-seq,rd-seq --io 4 --udp 4 --timeout 15s --metrics-brief\n"); + execve("/usr/bin/stress-ng", argvv, envp); + perror("execve"); + printf("\n"); + } + + + if(env.mutrace_test2){ + printf("MUTRACE_USER_TEST----------------------------------------------\n"); + printf("测试场景: 创建多个线程,每个线程反复加锁和解锁同一个互斥锁,观察互斥锁的争用情况\n"); + start_mutex_test(10); // 创建10个线程进行互斥锁测试 + printf("测试结束\n"); + printf("\n"); + + } + return 0; +} diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/Makefile b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/Makefile index 2c7d12a2a..dab1bd42d 100644 --- a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/Makefile +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/Makefile @@ -41,7 +41,7 @@ INCLUDES := -I$(OUTPUT) -I../libbpf/include/uapi -I$(LIBBLAZESYM_INC) -I./includ CFLAGS := -g -Wall ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) -APPS = resource_image lock_image syscall_image keytime_image schedule_image +APPS = resource_image lock_image syscall_image keytime_image schedule_image mfutex WORKTOOL := proc_image CONTROLLER := controller diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/README.md b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/README.md index 4626ce028..5ec41e983 100644 --- a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/README.md +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/README.md @@ -69,6 +69,14 @@ tools文件夹中的eBPF程序是按照进程生命周期中数据的类型分 | syscall_image | 对进程的系统调用序列进行画像 | | schedule_image | 对进程的调度信息进行画像 | +### 4.1 syscall_image使用说明 +[syscall_image使用说明](docs/Syscall_image工具使用说明.md) +
+ +### 4.2 keytime_image使用说明 +[keytime_image使用说明](docs/keytime_image工具使用说明.md) +
+ ## 五、基于 Prometheus 和 Grafana 的可视化平台 基于 Prometheus 和 Grafana 的可视化平台框架图: diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/bpf/mfutex.bpf.c b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/bpf/mfutex.bpf.c new file mode 100644 index 000000000..d65926b8e --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/bpf/mfutex.bpf.c @@ -0,0 +1,294 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: zhangziheng0525@163.com +// +// eBPF kernel-mode code that collects holding lock information of processes + +#include +#include +#include +#include +#include "proc_image.h" + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; + +const volatile pid_t ignore_tgid = -1; + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, int); + __type(value, struct mfutex_ctrl); +} mfutex_ctrl_map SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 10240); + __type(key, struct proc_flag); + __type(value, u64); +} proc_lock SEC(".maps");//记录哪些进程上锁了,便于在lock_exit时找到锁地址 + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 10240); + __type(key, struct proc_flag); + __type(value, u64); +} proc_unlock SEC(".maps");//记录哪些进程解锁了,便于在unlock_exit时找到锁地址 + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 10240); + __type(key, struct record_lock_key); + __type(value, struct per_request); +} record_lock SEC(".maps");//记录争用锁的全部进程 + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 10240); + __type(key, struct lock_record_key); + __type(value, int); +} lock_record SEC(".maps");//为了在线程被唤醒时,通过lock_addr, pid 找到cnt ,再通过cnt对record_lock中对应的pid进行操作; + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 10240); + __type(key, u64); + __type(value, struct per_lock_event); +} per_lock_info SEC(".maps");//每个锁的信息事件 + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries,528 * 10240); +} mfutex_rb SEC(".maps"); + +// struct { +// __uint(type, BPF_MAP_TYPE_HASH); +// __uint(max_entries, 10240); +// __type(key, struct record_lock_key); +// __type(value, struct per_request); +// } futex_wait_queue SEC(".maps");//记录futex陷入内核 + + +#define MUTEX_FLAG 1 +#define RWLOCK_FLAG 2 +#define SPIN_FLAG 3 +#define RCU_FLAG 4 +#define FUTEX_FLAG 5 +const int ctrl_key = 0; +static inline struct mfutex_ctrl *get_mfutex_ctrl(void) { + struct mfutex_ctrl *mfutex_ctrl; + mfutex_ctrl = bpf_map_lookup_elem(&mfutex_ctrl_map, &ctrl_key); + if (!mfutex_ctrl || !mfutex_ctrl->lock_func) { + return NULL; + } + return mfutex_ctrl; +} + +/*1.尝试申请锁 + *1.1:将申请锁的进程加入等待队列,并标记相关信息; +*/ +SEC("uprobe/pthread_mutex_lock") +int BPF_KPROBE(pthread_mutex_lock_enter, void *__mutex)//存申请锁的地址 +{ + struct mfutex_ctrl *mfutex_ctrl = get_mfutex_ctrl(); + if(!mfutex_ctrl) + return 0; + pid_t pid = bpf_get_current_pid_tgid(); + int tgid = bpf_get_current_pid_tgid() >> 32; + + if(mfutex_ctrl->target_pid == -1 && mfutex_ctrl->target_tgid == -1)//未指定目标进程或线程 + return 0; + if((mfutex_ctrl->target_pid != -1 && pid!=mfutex_ctrl->target_pid)|| + (mfutex_ctrl->target_tgid != -1 && tgid != mfutex_ctrl->target_tgid))//当前进程或线程非目标进程或线程 + return 0; + + u64 lock_ptr = (u64)__mutex; + int cnt; + /*1.将锁地址信息与进程pid记录在proc_lock map中*/ + struct proc_flag proc_flag = {}; + proc_flag.pid = pid; + proc_flag.flag = MUTEX_FLAG; + bpf_map_update_elem(&proc_lock, &proc_flag, &lock_ptr, BPF_ANY); + + /*2.对per_lock_info map中的信息进行读取更新或增加,包括cnt*/ + struct per_lock_event * per_lock_event = bpf_map_lookup_elem(&per_lock_info, &lock_ptr); + if(per_lock_event){ + per_lock_event->cnt++; + cnt = per_lock_event->cnt; + }else{ + struct per_lock_event new_per_lock = {}; + new_per_lock.lock_ptr = lock_ptr; + new_per_lock.type = MUTEX_FLAG; + new_per_lock.cnt = 1; + cnt = 1; + bpf_map_update_elem(&per_lock_info, &lock_ptr, &new_per_lock, BPF_ANY); + } + /*3.将 单个线程请求信息块 放入 请求等待队列record_lock 以及lock_record map */ + struct per_request per_request = {}; + per_request.pid = pid; + per_request.start_request_time = bpf_ktime_get_ns(); + struct record_lock_key key = {}; + key.lock_ptr = lock_ptr; + key.cnt = cnt; + bpf_map_update_elem(&record_lock, &key, &per_request, BPF_ANY); + bpf_printk("Push_info:pid:%d ,lock_ptr:%lu, cnt:%d\n",per_request.pid,key.lock_ptr,key.cnt); + //用于通过lock_ptr 和pid 找到cnt + struct lock_record_key key2 ={}; + key2.lock_ptr = lock_ptr; + key2.pid = pid; + bpf_map_update_elem(&lock_record, &key2, &cnt, BPF_ANY); + return 0; +} + +/*2.申请锁成功 + *2.1:更新持有锁信息,将等待对列中对应的进程标记为持有; +*/ +SEC("uretprobe/pthread_mutex_lock") +int BPF_KRETPROBE(pthread_mutex_lock_exit,int ret) +{ + struct mfutex_ctrl *mfutex_ctrl = get_mfutex_ctrl(); + if(!mfutex_ctrl) return 0; + + pid_t pid = bpf_get_current_pid_tgid(); + int tgid = bpf_get_current_pid_tgid() >> 32; + if(ret) + return 0; + if(mfutex_ctrl->target_pid == -1 && mfutex_ctrl->target_tgid == -1)//未指定目标进程或线程 + return 0; + if((mfutex_ctrl->target_pid != -1 && pid!=mfutex_ctrl->target_pid)|| + (mfutex_ctrl->target_tgid != -1 && tgid != mfutex_ctrl->target_tgid))//当前进程或线程非目标进程或线程 + return 0; + + u64 *lock_ptr; + u64 temp_lock_ptr; + u64 ts = bpf_ktime_get_ns(); + /*1.找到锁的地址*/ + struct proc_flag proc_flag = {}; + proc_flag.pid = pid; + proc_flag.flag = MUTEX_FLAG; + lock_ptr = bpf_map_lookup_elem(&proc_lock, &proc_flag); + if(!lock_ptr) return 0; + temp_lock_ptr = *lock_ptr; + + /*2.找到当前线程在record_lock map中的位置,即cnt*/ + int *cnt; + struct lock_record_key key1 = {}; + key1.lock_ptr = temp_lock_ptr; + key1.pid = pid; + cnt = bpf_map_lookup_elem(&lock_record, &key1);//找到cnt + if(!cnt) return 0; + + /*3.通过 cnt和lock_ptr 在record_lock map中找到对应的线程信息块,并更新*/ + struct record_lock_key key2 = {}; + key2.lock_ptr = temp_lock_ptr; + key2.cnt = *cnt; + struct per_request *per_request = bpf_map_lookup_elem(&record_lock, &key2); + if(!per_request) return 0; + bpf_map_delete_elem(&record_lock, &key2); + key2.cnt = 1; + per_request->start_hold_time = ts;//标志着开始占有锁 + per_request->wait_delay = ts - per_request->start_request_time; + bpf_map_update_elem(&record_lock, &key2, per_request, BPF_ANY); + + /*4.更新per_lock_info map中的信息,并传送到ringbuf中*/ + struct per_lock_event *per_lock_event = bpf_map_lookup_elem(&per_lock_info, &temp_lock_ptr); + if(!per_lock_event) return 0; + per_lock_event->owner = pid; + per_lock_event->start_hold_time = ts; + bpf_map_update_elem(&per_lock_info, &temp_lock_ptr, per_lock_event, BPF_ANY); + + //数据传送到ringbuf; + struct per_lock_event *e; + e = bpf_ringbuf_reserve(&mfutex_rb, sizeof(*e), 0); + if(!e) + return 0; + e->lock_ptr = per_lock_event->lock_ptr; + e->owner = per_lock_event->owner; + e->last_owner = per_lock_event->last_owner; + e->start_hold_time = per_lock_event->start_hold_time; + e->type = per_lock_event->type; + e->cnt = per_lock_event->cnt; + e->last_hold_delay = per_lock_event->last_hold_delay; + bpf_printk("In Push_to_rb lock_ptr %llu,per_cnt:%d,owner:%d,last_owner:%d,last_hold_time:%lu\n",e->lock_ptr,e->cnt,e->owner, + e->last_owner,e->last_hold_delay); + bpf_ringbuf_submit(e, 0); + return 0; +} + +/*尝试解锁 + *1.将等待对列中对应的解锁进程删除,并更新该进程持有锁的时间; +*/ +SEC("uprobe/pthread_mutex_unlock") +int BPF_KPROBE(pthread_mutex_unlock_enter, void *__mutex) +{ + struct mfutex_ctrl *mfutex_ctrl = get_mfutex_ctrl(); + if(!mfutex_ctrl) return 0; + + pid_t pid = bpf_get_current_pid_tgid(); + int tgid = bpf_get_current_pid_tgid() >> 32; + + if(mfutex_ctrl->target_pid == -1 && mfutex_ctrl->target_tgid == -1)//未指定目标进程或线程 + return 0; + if((mfutex_ctrl->target_pid != -1 && pid!=mfutex_ctrl->target_pid)|| + (mfutex_ctrl->target_tgid != -1 && tgid != mfutex_ctrl->target_tgid))//当前进程或线程非目标进程或线程 + return 0; + + u64 lock_ptr = (u64)__mutex; + u64 ts = bpf_ktime_get_ns(); + + /*1.通过 cnt和lock_ptr 在record_lock map中找到对应的线程信息块,并删除在队列中的记录*/ + struct record_lock_key key2 = {}; + key2.lock_ptr = lock_ptr; + key2.cnt = 1; + struct per_request *per_request = bpf_map_lookup_elem(&record_lock, &key2); + if(!per_request) return 0; + bpf_map_delete_elem(&record_lock, &key2); + + /*2.将该线程持有锁的信息同步在per_lock map 中*/ + struct per_lock_event *per_lock_event = bpf_map_lookup_elem(&per_lock_info, &lock_ptr); + if(!per_lock_event) return 0; + per_lock_event->last_owner = pid; + // per_lock_event->last_start_hold_time = ts; + per_lock_event->last_hold_delay = ts - per_request->start_hold_time;//持有锁的时间 + bpf_map_update_elem(&per_lock_info, &lock_ptr, per_lock_event, BPF_ANY); + return 0; +} + +// SEC("uprobe/__pthread_mutex_trylock") +// int BPF_KPROBE(__pthread_mutex_trylock_enter, void *__mutex) +// { +// record_lock_enter(ignore_tgid,1,1,__mutex,&mfutex_rb,&proc_lock,&lock_ctrl_map); +// return 0; +// } + +// SEC("uretprobe/__pthread_mutex_trylock") +// int BPF_KRETPROBE(__pthread_mutex_trylock_exit,int ret) +// { +// record_lock_exit(ignore_tgid,2,1,ret,&mfutex_rb,&proc_lock,&locktype,&lock_ctrl_map); +// return 0; +// } + + +// /*获取到要加入等待队列的线程,拿到时间*/ +// SEC("kprobe/futex_wait") +// int BPF_KPROBE(trace_futex_wait, +// u32 __user *uaddr, unsigned int flags, +// u32 val, ktime_t *abs_time, u32 bitset) +// { + + + + +// } diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/bpf/syscall_image.bpf.c b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/bpf/syscall_image.bpf.c index 68d8f29d5..b608d77be 100644 --- a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/bpf/syscall_image.bpf.c +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/bpf/syscall_image.bpf.c @@ -23,9 +23,12 @@ #include "proc_image.h" char LICENSE[] SEC("license") = "Dual BSD/GPL"; - +#define MAX_NODENAME_LEN 64 const volatile pid_t ignore_tgid = -1; +const volatile char hostname[MAX_NODENAME_LEN] = ""; const int key = 0; +pid_t pre_target_pid = -1;//上一个监测的进程; +int pre_target_tgid = -1;//上一个监测的进程组; struct { __uint(type, BPF_MAP_TYPE_ARRAY); @@ -46,14 +49,69 @@ struct { __uint(max_entries,256 * 10240); } syscall_rb SEC(".maps"); +struct { + __uint(type,BPF_MAP_TYPE_HASH); + __uint(max_entries, 8192); + __type(key, pid_t); + __type(value,struct container_id); +}container_id_map SEC(".maps"); + +struct container_id{ + char container_id[20]; +}; + +struct data_t { + char nodename[MAX_NODENAME_LEN]; +}; + +static bool is_container_task(const volatile char hostname[MAX_NODENAME_LEN]){ + struct task_struct *task; + struct nsproxy *ns; + struct uts_namespace *uts; + struct data_t data = {}; + // 获取当前任务的 task_struct + task = (struct task_struct *)bpf_get_current_task(); + // 获取 nsproxy + bpf_probe_read_kernel(&ns, sizeof(ns), &task->nsproxy); + if (!ns) { + return false; + } + // 获取 uts_namespace + bpf_probe_read_kernel(&uts, sizeof(uts), &ns->uts_ns); + if (!uts) { + return false; + } + // 读取主机名 + bpf_probe_read_kernel_str(&data.nodename, sizeof(data.nodename), uts->name.nodename); + // 打印主机名 + bool is_equal = true; + for(int i = 0;isc_func) + if(!sc_ctrl || !sc_ctrl->sc_func) return 0; - + if(sc_ctrl->is_container) + if(!is_container_task(hostname)) + return 0; pid_t pid = bpf_get_current_pid_tgid(); int tgid = bpf_get_current_pid_tgid() >> 32; @@ -80,14 +138,14 @@ int sys_enter(struct trace_event_raw_sys_enter *args) if((sc_ctrl->target_tgid==-1 && (sc_ctrl->target_pid==-1 || pid==sc_ctrl->target_pid)) || (sc_ctrl->target_tgid!=-1 && tgid == sc_ctrl->target_tgid)){ syscall_seq->record_syscall[syscall_seq->count] = (int)args->id; } - syscall_seq->count ++; + syscall_seq->count++; }else if (syscall_seq->count <= MAX_SYSCALL_COUNT-1 && syscall_seq->count > 0 && syscall_seq->record_syscall+syscall_seq->count <= syscall_seq->record_syscall+(MAX_SYSCALL_COUNT-1)){ if((sc_ctrl->target_tgid==-1 && (sc_ctrl->target_pid==-1 || pid==sc_ctrl->target_pid)) || (sc_ctrl->target_tgid!=-1 && tgid == sc_ctrl->target_tgid)){ syscall_seq->record_syscall[syscall_seq->count] = (int)args->id; } - syscall_seq->count ++; + syscall_seq->count++; } } } @@ -102,7 +160,9 @@ int sys_exit(struct trace_event_raw_sys_exit *args) sc_ctrl = bpf_map_lookup_elem(&sc_ctrl_map,&key); if(!sc_ctrl || !sc_ctrl->sc_func) return 0; - + if(sc_ctrl->is_container) + if(!is_container_task(hostname)) + return 0; pid_t pid = bpf_get_current_pid_tgid(); int tgid = bpf_get_current_pid_tgid() >> 32; @@ -132,6 +192,20 @@ int sys_exit(struct trace_event_raw_sys_exit *args) syscall_seq->max_delay = this_delay; if(syscall_seq->min_delay==0 || this_delaymin_delay) syscall_seq->min_delay = this_delay; + //策略切换,首次数据不记录; + if(sc_ctrl->target_tgid ==-1 && sc_ctrl->target_pid ==pid && sc_ctrl->target_pid != pre_target_pid){ + syscall_seq->sum_delay = 0; + syscall_seq->count = 0; + pre_target_pid = sc_ctrl->target_pid;//更改pre_target_pid; + return 0; + } + if(sc_ctrl->target_tgid !=-1 && sc_ctrl->target_tgid ==tgid && sc_ctrl->target_tgid != pre_target_tgid){ + syscall_seq->sum_delay = 0; + syscall_seq->count = 0; + pre_target_tgid = sc_ctrl->target_tgid;//更改pre_target_pid; + return 0; + } + if((sc_ctrl->target_tgid==-1 && (sc_ctrl->target_pid==-1 || pid==sc_ctrl->target_pid)) || (sc_ctrl->target_tgid!=-1 && tgid == sc_ctrl->target_tgid)){ syscall_seq->proc_count += syscall_seq->count; diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/controller.c b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/controller.c index 121a8fdb1..68631c2ad 100644 --- a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/controller.c +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/controller.c @@ -49,6 +49,8 @@ static struct env { bool enable_lock; bool enable_syscall; bool enable_schedule; + bool enable_mfutex; + bool is_container; } env = { .usemode = 0, .pid = -1, @@ -68,6 +70,8 @@ static struct env { .enable_lock = false, .enable_syscall = false, .enable_schedule = false, + .is_container = false, + .enable_mfutex = false, }; const char argp_program_doc[] ="Trace process to get process image.\n"; @@ -78,6 +82,7 @@ static const struct argp_option opts[] = { { "finish", 'f', NULL, 0, "Finish to run eBPF tool" }, { "pid", 'p', "PID", 0, "Process ID to trace" }, { "tgid", 'P', "TGID", 0, "Thread group to trace" }, + { "containerproc", 'o', NULL, 0, "Thread of containerproc to trace" }, { "cpuid", 'c', "CPUID", 0, "Set For Tracing per-CPU Process(other processes don't need to set this parameter)" }, { "time", 't', "TIME-SEC", 0, "Max Running Time(0 for infinite)" }, { "myproc", 'm', NULL, 0, "Trace the process of the tool itself (not tracked by default)" }, @@ -86,6 +91,7 @@ static const struct argp_option opts[] = { { "lock", 'l', NULL, 0, "Collects lock information about processes" }, { "syscall", 's', "SYSCALLS", 0, "Collects syscall sequence (1~50) information about processes(any 1~50 when deactivated)" }, { "schedule", 'S', NULL, 0, "Collects schedule information about processes (trace tool process)" }, + { "mfutex", 'M', NULL, 0, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }, { NULL, 'h', NULL, OPTION_HIDDEN, "show the full help" }, {}, }; @@ -143,6 +149,9 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) case 'm': env.enable_myproc = true; break; + case 'o': + env.is_container = true; + break; case 'r': env.enable_resource = true; break; @@ -169,6 +178,9 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) case 'S': env.enable_schedule = true; break; + case 'M': + env.enable_mfutex = true; + break; case 'h': argp_state_help(state, stderr, ARGP_HELP_STD_HELP); break; @@ -201,7 +213,7 @@ int deactivate_mode(){ } if(env.enable_syscall){ - struct sc_ctrl sc_ctrl = {false,false,-1,-1,0}; + struct sc_ctrl sc_ctrl = {false,false,false,-1,-1,0}; err = update_sc_ctrl_map(sc_ctrl); if(err < 0) return err; } @@ -212,6 +224,12 @@ int deactivate_mode(){ if(err < 0) return err; } + if(env.enable_mfutex){ + struct mfutex_ctrl mfutex_ctrl = {false,false,-1,-1}; + err = update_mfutex_ctrl_map(mfutex_ctrl); + if(err < 0) return err; + } + return 0; } @@ -256,8 +274,14 @@ int main(int argc, char **argv) if(err < 0) return err; } + if(env.enable_mfutex){ + struct mfutex_ctrl mfutex_ctrl = {true,env.enable_myproc,env.pid,env.tgid}; + err = update_mfutex_ctrl_map(mfutex_ctrl); + if(err < 0) return err; + } + if(env.enable_syscall){ - struct sc_ctrl sc_ctrl = {true,env.enable_myproc,env.pid,env.tgid,env.syscalls}; + struct sc_ctrl sc_ctrl = {true,env.enable_myproc, env.is_container,env.pid,env.tgid,env.syscalls}; err = update_sc_ctrl_map(sc_ctrl); if(err < 0) return err; } diff --git "a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/Syscall_image\345\267\245\345\205\267\344\275\277\347\224\250\350\257\264\346\230\216.md" "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/Syscall_image\345\267\245\345\205\267\344\275\277\347\224\250\350\257\264\346\230\216.md" new file mode 100644 index 000000000..6eb296a60 --- /dev/null +++ "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/Syscall_image\345\267\245\345\205\267\344\275\277\347\224\250\350\257\264\346\230\216.md" @@ -0,0 +1,162 @@ +# Syscall_image工具使用说明: + +syscall_image工具是用于监测系统中系统调用时延的工具, 该工具可以监测特定线程或线程组调用系统调用情况,统计输出该线程系统调用时延(系统调用平均时延、系统调用最大时延、系统调用最大时延),并将系统调用序列号顺序输出,可用于解决进程出现异常后的问题排查; + +## 1.代码逻辑图: + +![](images/syscall_image.jpg) + +## 2.使用方法: + +### 2.1.编译 + +首先在`lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image`目录下进行编译操作; + +```shell +sudo make +``` + +编译成功后,会生成两个可执行文件`proc_image`,`controller`, 后面的数据监测均围绕这两个可执行文件; + +### 2.2.挂载 + +在`lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image`目录下运行 `proc_image`可执行文件, + +通过`proc_image -h`命令可查看进程画像的使用方法: + +```shell +xhb@1:~/lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image$ sudo ./proc_image -h +Usage: proc_image [OPTION...] +Trace process to get process image. + + -a, --all Attach all eBPF functions(but do not start) + -k, --keytime Attach eBPF functions about keytime(but do not + start) + -l, --lock Attach eBPF functions about lock(but do not start) + + -r, --resource Attach eBPF functions about resource usage(but do + not start) + -s, --syscall Attach eBPF functions about syscall sequence(but + do not start) + -S, --schedule Attach eBPF functions about schedule (but do not + start) + -?, --help Give this help list + --usage Give a short usage message +``` + +挂载syscall_image工具相关挂载点; + +```shell +sudo ./proc_image -s +``` + +```shell +xhb@1:~/lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image$ sudo ./proc_image -s +libbpf: loading object 'syscall_image_bpf' from buffer +libbpf: elf: section(2) .symtab, size 1128, link 1, flags 0, type=2 +libbpf: elf: section(3) tracepoint/raw_syscalls/sys_enter, size 1064, link 0, flags 6, type=1 +libbpf: sec 'tracepoint/raw_syscalls/sys_enter': found program 'sys_enter' at insn offset 0 (0 bytes), code size 133 insns (1064 bytes) +libbpf: elf: section(4) tracepoint/raw_syscalls/sys_exit, size 1216, link 0, flags 6, type=1 +.... +libbpf: prog 'sched_process_exit': relo #0: [51] struct task_struct.pid (0:85 @ offset 2456) +libbpf: prog 'sched_process_exit': relo #0: matching candidate #0 [80] struct task_struct.pid (0:85 @ offset 2456) +libbpf: prog 'sched_process_exit': relo #0: patched insn #9 (ALU/ALU64) imm 2456 -> 2456 +libbpf: unpinned map 'sc_ctrl_map' from '/sys/fs/bpf/proc_image_map/sc_ctrl_map' +libbpf: pinned map '/sys/fs/bpf/proc_image_map/sc_ctrl_map' + + +``` + +### 2.3.控制策略: + +可使用`controller`工具进行策略控制,策略切换。 + +重启一个终端, 在`lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image`目录下运行 `controller`可执行文件。 + +通过`controller -h`命令可查看进程画像策略切换方法: + +```shell +xhb@1:~/lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image$ sudo ./controller -h +Usage: controller [OPTION...] +Trace process to get process image. + + -a, --activate Set startup policy of proc_image tool + -c, --cpuid=CPUID Set For Tracing per-CPU Process(other processes + don't need to set this parameter) + -d, --deactivate Initialize to the original deactivated state + -f, --finish Finish to run eBPF tool + -k, --keytime=KEYTIME Collects keytime information about + processes(0:except CPU kt_info,1:all kt_info,any 0 + or 1 when deactivated) + -l, --lock Collects lock information about processes + -m, --myproc Trace the process of the tool itself (not tracked + by default) + -p, --pid=PID Process ID to trace + -P, --tgid=TGID Thread group to trace + -r, --resource Collects resource usage information about + processes + -s, --syscall=SYSCALLS Collects syscall sequence (1~50) information about + processes(any 1~50 when deactivated) + -S, --schedule Collects schedule information about processes + (trace tool process) + -t, --time=TIME-SEC Max Running Time(0 for infinite) + -?, --help Give this help list + --usage Give a short usage message + +Mandatory or optional arguments to long options are also mandatory or optional +for any corresponding short options. +``` + +使用syscall_image工具的不同参数,控制该工具的使用策略: + +| 参数 | | +| ---- | ------------------------------------------------------------ | +| -s | syscall_image工具 后加参数(syscalls)用于控制每次输出系统调用次数;例:-s 10; | +| -a | 激活 syscall_image工具; | +| -p | 指定目标线程; | +| -P | 指定目标线程组; | +| -c | 指定检测cpu; | +| -t | 指定检测时间; | + +通过以下指令更改控制策略: + +* 激活对线程1111的系统调用进行监测; + + ```shell + sudo ./controller -s 10 -p 1111 -c 0 -a + ``` + +* 激活对线程组1111的系统调用进行监测; + + ```shell + sudo ./controller -s 10 -P 1111 -c 0 -a + ``` + +* 关闭对线程1111的系统调用进行监测; + + ```shell + sudo ./controller -s 10 -p 1111 -c 0 -d + ``` + +* 关闭对线程1111的系统调用进行监测; + + ```shell + sudo ./controller -s 10 -P 1111 -c 0 -d + ``` + +* 关闭进程画像: + + ```shell + sudo ./controller -f + ``` + +### 2.3.数据监测: + +当更改了使用策略后,将对数据进行检测: + +![](images/syscall_image数据监测.png) + + + + + diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720660193068.png b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720660193068.png new file mode 100644 index 000000000..0544a55d5 Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720660193068.png differ diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720683501899.png b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720683501899.png new file mode 100644 index 000000000..fb1a5f91a Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720683501899.png differ diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720692895341.png b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720692895341.png new file mode 100644 index 000000000..28d36bb4c Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720692895341.png differ diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720693018279.png b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720693018279.png new file mode 100644 index 000000000..fd1e31a5f Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720693018279.png differ diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720693526887.png b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720693526887.png new file mode 100644 index 000000000..219da5fcb Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720693526887.png differ diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720693579439.png b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720693579439.png new file mode 100644 index 000000000..d22bb6259 Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720693579439.png differ diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720693616700.png b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720693616700.png new file mode 100644 index 000000000..787086983 Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720693616700.png differ diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720694420680.png b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720694420680.png new file mode 100644 index 000000000..741003f63 Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720694420680.png differ diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720696786685.png b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720696786685.png new file mode 100644 index 000000000..7afa54628 Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720696786685.png differ diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/keytime2.png b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/keytime2.png new file mode 100644 index 000000000..f2a286dc3 Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/keytime2.png differ diff --git "a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/keytime_image\345\217\257\350\247\206\345\214\226.png" "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/keytime_image\345\217\257\350\247\206\345\214\226.png" new file mode 100644 index 000000000..9e88e6db5 Binary files /dev/null and "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/keytime_image\345\217\257\350\247\206\345\214\226.png" differ diff --git "a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/keytime_image\346\225\260\346\215\256\347\233\221\346\265\213.png" "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/keytime_image\346\225\260\346\215\256\347\233\221\346\265\213.png" new file mode 100644 index 000000000..a5a2be566 Binary files /dev/null and "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/keytime_image\346\225\260\346\215\256\347\233\221\346\265\213.png" differ diff --git "a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/lock_image\345\217\257\350\247\206\345\214\226.png" "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/lock_image\345\217\257\350\247\206\345\214\226.png" new file mode 100644 index 000000000..89f8ddbad Binary files /dev/null and "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/lock_image\345\217\257\350\247\206\345\214\226.png" differ diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/schedule_delay_output.png b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/schedule_delay_output.png new file mode 100644 index 000000000..1cfd38889 Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/schedule_delay_output.png differ diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/schedule_delay_process.png b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/schedule_delay_process.png new file mode 100644 index 000000000..9d989c8ea Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/schedule_delay_process.png differ diff --git "a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/schedule_image\345\217\257\350\247\206\345\214\226.png" "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/schedule_image\345\217\257\350\247\206\345\214\226.png" new file mode 100644 index 000000000..6bdfa6564 Binary files /dev/null and "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/schedule_image\345\217\257\350\247\206\345\214\226.png" differ diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/syscall_image.jpg b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/syscall_image.jpg new file mode 100644 index 000000000..3b4ed2082 Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/syscall_image.jpg differ diff --git "a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/syscall_image\345\217\257\350\247\206\345\214\226.png" "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/syscall_image\345\217\257\350\247\206\345\214\226.png" new file mode 100644 index 000000000..a049df1db Binary files /dev/null and "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/syscall_image\345\217\257\350\247\206\345\214\226.png" differ diff --git "a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/syscall_image\346\225\260\346\215\256\345\257\271\346\257\224\345\233\2761.png" "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/syscall_image\346\225\260\346\215\256\345\257\271\346\257\224\345\233\2761.png" new file mode 100644 index 000000000..20490c463 Binary files /dev/null and "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/syscall_image\346\225\260\346\215\256\345\257\271\346\257\224\345\233\2761.png" differ diff --git "a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/syscall_image\346\225\260\346\215\256\345\257\271\346\257\224\345\233\2762.png" "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/syscall_image\346\225\260\346\215\256\345\257\271\346\257\224\345\233\2762.png" new file mode 100644 index 000000000..c56c0de62 Binary files /dev/null and "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/syscall_image\346\225\260\346\215\256\345\257\271\346\257\224\345\233\2762.png" differ diff --git "a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/syscall_image\346\225\260\346\215\256\345\257\271\346\257\224\345\233\2763.png" "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/syscall_image\346\225\260\346\215\256\345\257\271\346\257\224\345\233\2763.png" new file mode 100644 index 000000000..f103b3447 Binary files /dev/null and "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/syscall_image\346\225\260\346\215\256\345\257\271\346\257\224\345\233\2763.png" differ diff --git "a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/syscall_image\346\225\260\346\215\256\347\233\221\346\265\213.png" "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/syscall_image\346\225\260\346\215\256\347\233\221\346\265\213.png" new file mode 100644 index 000000000..e80dba39a Binary files /dev/null and "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/syscall_image\346\225\260\346\215\256\347\233\221\346\265\213.png" differ diff --git "a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/keytime_image\345\267\245\345\205\267\344\275\277\347\224\250\350\257\264\346\230\216.md" "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/keytime_image\345\267\245\345\205\267\344\275\277\347\224\250\350\257\264\346\230\216.md" new file mode 100644 index 000000000..5b49b20ef --- /dev/null +++ "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/keytime_image\345\267\245\345\205\267\344\275\277\347\224\250\350\257\264\346\230\216.md" @@ -0,0 +1,185 @@ +# keytime_image工具使用说明: + +keytime_image主要是用于采集进程创建与退出、进程以什么方式创建(fork、vfork、clone)、进程何时上cpu何时下cpu等信息。本工具可用于监测进程全生命周期,并获取对应的数据; + +## 1.代码逻辑图: + +![](images/keytime2.png) + +## 2.使用方法: + +### 2.1.编译 + +首先在`lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image`目录下进行编译操作; + +```shell +sudo make +``` + +编译成功后,会生成两个可执行文件`proc_image`,`controller`, 后面的数据监测均围绕这两个可执行文件; + +### 2.2.挂载 + +在`lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image`目录下运行 `proc_image`可执行文件, + +通过`proc_image -h`命令可查看进程画像的使用方法: + +```shell +xhb@1:~/lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image$ sudo ./proc_image -h +Usage: proc_image [OPTION...] +Trace process to get process image. + + -a, --all Attach all eBPF functions(but do not start) + -k, --keytime Attach eBPF functions about keytime(but do not + start) + -l, --lock Attach eBPF functions about lock(but do not start) + + -r, --resource Attach eBPF functions about resource usage(but do + not start) + -s, --syscall Attach eBPF functions about syscall sequence(but + do not start) + -S, --schedule Attach eBPF functions about schedule (but do not + start) + -?, --help Give this help list + --usage Give a short usage message +``` + +挂载syscall_image工具相关挂载点; + +```shell +sudo ./proc_image -k +``` + +```shell +xhb@1:~/lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image$ sudo ./proc_image -k +libbpf: loading object 'keytime_image_bpf' from buffer +libbpf: elf: section(2) .symtab, size 2736, link 1, flags 0, type=2 +libbpf: elf: section(3) tracepoint/syscalls/sys_enter_execve, size 17888, link 0, flags 6, type=1 +libbpf: sec 'tracepoint/syscalls/sys_enter_execve': found program 'tracepoint__syscalls__sys_enter_execve' at insn offset 0 (0 bytes), code size 2236 insns (17888 bytes) +libbpf: elf: section(4) tracepoint/syscalls/sys_exit_execve, size 496, link 0, flags 6, type=1 +libbpf: sec 'tracepoint/syscalls/sys_exit_execve': found program 'tracepoint__syscalls__sys_exit_execve' at insn offset 0 (0 bytes), code size 62 insns (496 bytes) +... +libbpf: prog 'sched_switch': relo #3: patched insn #37 (ALU/ALU64) imm 2460 -> 2460 +libbpf: unpinned map 'kt_ctrl_map' from '/sys/fs/bpf/proc_image_map/kt_ctrl_map' +libbpf: pinned map '/sys/fs/bpf/proc_image_map/kt_ctrl_map' +libbpf: elf: symbol address match for 'fork' in '/usr/lib/x86_64-linux-gnu/libc.so.6': 0xefac0 + + +``` + +### 2.3.控制策略: + +可使用`controller`工具进行策略控制,策略切换。 + +重启一个终端, 在`lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image`目录下运行 `controller`可执行文件。 + +通过`controller -h`命令可查看进程画像策略切换方法: + +```shell +xhb@1:~/lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image$ sudo ./controller -h +Usage: controller [OPTION...] +Trace process to get process image. + + -a, --activate Set startup policy of proc_image tool + -c, --cpuid=CPUID Set For Tracing per-CPU Process(other processes + don't need to set this parameter) + -d, --deactivate Initialize to the original deactivated state + -f, --finish Finish to run eBPF tool + -k, --keytime=KEYTIME Collects keytime information about + processes(0:except CPU kt_info,1:all kt_info,any 0 + or 1 when deactivated) + -l, --lock Collects lock information about processes + -m, --myproc Trace the process of the tool itself (not tracked + by default) + -p, --pid=PID Process ID to trace + -P, --tgid=TGID Thread group to trace + -r, --resource Collects resource usage information about + processes + -s, --syscall=SYSCALLS Collects syscall sequence (1~50) information about + processes(any 1~50 when deactivated) + -S, --schedule Collects schedule information about processes + (trace tool process) + -t, --time=TIME-SEC Max Running Time(0 for infinite) + -?, --help Give this help list + --usage Give a short usage message + +Mandatory or optional arguments to long options are also mandatory or optional +for any corresponding short options. +``` + +使用keytime_image工具的不同参数,控制该工具的使用策略: + +| 参数 | | +| ---- | ------------------------------------------------------------ | +| -k | keytime_image工具 后加参数(0或1)用于控制是否监测进程上下CPU的时间点;例:-k 0; | +| -a | 激活 keytime_image工具; | +| -p | 指定目标线程; | +| -P | 指定目标线程组; | +| -t | 指定检测时间; | + +通过以下指令更改控制策略: + +- 激活对线程3184关键时间点的数据收集; + + ```shell + sudo ./controller -k 0 -p 3184 -a + KEYTIME ------------------------------------------- + TIME PID EVENT ARGS/RET/OTHERS + 10:02:44 3184 forkP_enter child_pid:208024 + 10:02:44 3184 vforkP_enter child_pid:208024 + 10:02:45 3184 vforkP_exit child_pid:208024 + 10:02:45 3184 forkP_enter child_pid:208026 + 10:02:45 3184 vforkP_enter child_pid:208026 + 10:02:45 3184 vforkP_exit child_pid:208026 + 10:02:45 3184 forkP_enter child_pid:208028 + ``` + +- 激活对线程组3184关键时间点的数据收集; + + ```shell + sudo ./controller -k 1 -P 3184 -a + KEYTIME ----------------------------------------------------------------- + TIME TGID PID EVENT ARGS/RET/OTHERS + 10:23:39 3184 3184 offCPU offcpu_time:253480629547342 + 10:23:39 3184 3184 onCPU oncpu_time:253480629595213 + 10:23:39 3184 3188 onCPU oncpu_time:253480630262378 + 10:23:39 3184 3188 offCPU offcpu_time:253480630274211 + 10:23:39 3184 3186 onCPU oncpu_time:253480630292716 + 10:23:39 3184 3188 onCPU oncpu_time:253480630301954 + 10:23:39 3184 3186 offCPU offcpu_time:253480630302214 + 10:23:39 3184 3188 offCPU offcpu_time:253480630307033 + 10:23:39 3184 3187 onCPU oncpu_time:253480630342231 + 10:23:39 3184 3186 onCPU oncpu_time:253480630350606 + 10:23:39 3184 3186 offCPU offcpu_time:253480630357990 + 10:23:39 3184 3188 onCPU oncpu_time:253480630397210 + + ``` + +- 关闭对线程3184关键时间点的数据收集; + + ```shell + sudo ./controller -k 0 -p 3184 -d + ``` + +- 关闭对线程组3184关键时间点的数据收集; + + ```shell + sudo ./controller -k 1 -P 3184 -d + ``` + +- 关闭进程画像: + + ```shell + sudo ./controller -f + ``` + +### 2.3.数据监测: + +当更改了使用策略后,将对数据进行检测: + +![](images/keytime_image数据监测.png) + + + + + diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/proc_image_vis_guide.md b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/proc_image_vis_guide.md index b43b75b16..2ed10bd7e 100644 --- a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/proc_image_vis_guide.md +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/proc_image_vis_guide.md @@ -105,7 +105,10 @@ TIME READ/s WRITE/s FSYNC/s OPEN/s CREATE/s - 执行如下指令开始采集数据以及相关处理: ``` - ./data-visual collect /home/zhang/lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/proc_image -r -p 16279 + // 终端1:挂载eBPF内核态函数,但是处于失活状态(不进行数据的采集) + ./data-visual collect /home/zhang/lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/proc_image -r + // 终端2:激活eBPF内核态函数 + ./controller -a -r -p 16279 ``` - 个人喜欢在主机上进行可视化设置,这样方便快捷,所以在主机上打开网址http://192.168.109.150:8090/metrics (其中192.168.109.150是虚拟机网络接口的IPv4地址),可以看到暴露在http网页中的数据: diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/schedule_delay_usemethod.md b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/schedule_delay_usemethod.md new file mode 100644 index 000000000..84cee2c22 --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/schedule_delay_usemethod.md @@ -0,0 +1,152 @@ +# Schedule_delay工具使用说明 + +​ Schedule_delay工具是用来检测系统中调度延时的工具,该工具可以监测特定线程或线程组、以及整个系统的调度延迟(最大、最小、平均)。即从一个任务具备运行的条件,到真正执行(获得 CPU 的执行权)的这段时间。实时观测该指标可以帮助我们了解到当前操作系统的负载。 + +## 原理讲解 + +​ 该工具使用六个map来控制工具的行为和存储数据与用户态交互。分别为: + +- **ARRAY MAP**`sched_ctrl_map`:作为调度控制信息的存储,策略上将该map pin到bpf文件系统,即可在用户态更新该map,与内核态交互。其value为`struct sched_ctrl`: + +```c +struct sched_ctrl { + bool sched_func; // 控制是否记录调度相关事件 + pid_t target_pid; // 目标进程的 PID + int target_cpu_id; // 目标 CPU 的 ID + pid_t target_tgid; // 目标线程组的 TGID +}; +``` + +​ 通过这些控制信息,可以控制该工具在内核态的行为。 + +- **HASH MAP**`enable add`:用来记录进程是否被调度过,以避免重复计数或错误计数。 + +- **HASH MAP**`proc_schedule`:用于存储和跟踪每个进程的调度事件信息,key为进程的id和cpu_id,value为` struct schedule_event`: + +```c +struct schedule_event { + pid_t pid; // 进程ID + int tgid; // 线程组ID + int prio; // 进程优先级 + u32 count; // 调度次数 + u64 enter_time; // 进入调度的时间 + u64 sum_delay; // 累计调度延迟 + u64 max_delay; // 最大调度延迟 + u64 min_delay; // 最小调度延迟 +}; +``` + +- **HASH MAP**`target_schedule`:用于存储指定**线程**的调度信息,value为` struct schedule_event`,在用户态通过更新`sched_ctrl_map`指定要记录的线程。 +- **HASH MAP**`tg_schedule`:用于存储指定**线程组**的调度信息,value为` struct schedule_event`,在用户态通过更新`sched_ctrl_map`指定要记录的线程组。 + +- **ARRAY MAP**`sys_schedule`:用于记录整个系调度延时,value为`struct sum_schedule:` + +```c +struct sum_schedule { + long long unsigned int sum_count; // 总调度次数 + long long unsigned int sum_delay; // 总调度延迟 + long long unsigned int max_delay; // 最大调度延迟 + long long unsigned int min_delay; // 最小调度延迟 +}; +``` + +**代码逻辑图如下:** + +![](images/schedule_delay_process.png) + +## 2.使用方法 + +### 2.1编译 + +​ 首先在`lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image`目录下进行编译操作。 + +```shell +make +``` + +​ 编译成功后,会生成两个可执行文件`proc_image`,`controller`。 + +### 2.2挂载 + +​ 在`lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image`目录下运行 `proc_image`可执行文件。 + +​ 使用-S选项挂载检测调度延迟的几个挂载点: + +```shell +sudo ./proc_image -S +``` + +``` +sudo ./proc_image -S +...... +libbpf: map 'sched_ctrl_map': created successfully, fd=3 +libbpf: map 'proc_schedule': created successfully, fd=4 +libbpf: map 'enable_add': created successfully, fd=5 +libbpf: map 'target_schedule': created successfully, fd=6 +libbpf: map 'tg_schedule': created successfully, fd=7 +libbpf: map 'sys_schedule': created successfully, fd=8 +libbpf: map 'schedule.rodata': created successfully, fd=9 +libbpf: pinned map '/sys/fs/bpf/proc_image_map/sched_ctrl_map' +``` + +​ 此时用户态内核态交互的**sched_ctrl_map**已经pin上,现在我们可以通过controller执行文件来更改输出策略。 + +### 2.3用户态控制交互 + +​ 可使用`controller`工具和内核态进行交互,控制输出。 + +​ 重启一个终端, 在`lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image`目录下运行 `controller`可执行文件。 + +​ 使用schedule_image工具的不同参数,控制该工具的使用策略: + +| 参数 | | +| :--: | ---------------------- | +| -S | 使用schedule_image工具 | +| -a | 激活schedule_image工具 | +| -p | 指定目标线程 | +| -P | 指定目标线程组 | +| -c | 指定检测CPU | +| -t | 指定检测时间 | + +通过以下指令更改控制策略: + +- 激活对线程21416的调度延迟进行监测: + + ``` + sudo ./controller -S -a -p 21416 + ``` + +- 激活对线程组21416的调度延迟进行监测: + + ``` + sudo ./controller -s -a -p 21416 + ``` + +- 关闭对线程21416的调度延迟进行监测: + + ``` + sudo ./controller -S -d -p 21416 + ``` + +- 关闭对线程组21416的调度延迟进行监测: + + ``` + sudo ./controller -S -d -P 21416 + ``` + +- 检测整个系统的调度延迟: + + ``` + sudo ./controller -S -a + ``` + +- 关闭进程画像工具: + + ``` + sudo ./controller -f + ``` + +#### 2.4 输出 + +​ 对线程21416的调度延迟进行监测,可以看到指定进程的调度延迟信息,包括该线程最大、最小、平均的调度延迟(P_xxx_DELAY),以及整个系统的最大、最小、平均的调度延迟(S_xxx_DELAY)。 +![](images/schedule_delay_output.png) \ No newline at end of file diff --git "a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/\346\265\213\350\257\225\344\270\216\346\225\260\346\215\256\345\205\263\350\201\224.md" "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/\346\265\213\350\257\225\344\270\216\346\225\260\346\215\256\345\205\263\350\201\224.md" new file mode 100644 index 000000000..6f4cf945f --- /dev/null +++ "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/\346\265\213\350\257\225\344\270\216\346\225\260\346\215\256\345\205\263\350\201\224.md" @@ -0,0 +1,991 @@ +# 数据真实性测试 + +本次测试工作主要包括: + +* 针对进程画像的每个功能,涉及测试用例,在逻辑上验证ebpf程序的正确性; +* 学习使用LTTng工具,通过该工具实现相同或相似功能,将测试用例在使用在LTTng工具上,验证进程画像相应工具数据的正确性; +* 学习使用trace compass可视化性能分析工具,将LTTng监测出的数据放在trace compass上查看进程流程,并根据LTTng工具提供的技术文档来梳理不同数据见的关系; +* 输出数据关联性文档; + +## 0.准备工作: + +学习使用LTTng 来监测数据; + +学习将LTTng生成的文件在compass上跑出来,并分析; + +### 0.1.LTTng学习使用: + +[linux下 LTTng使用详细说明_no package 'liburcu' found-CSDN博客](https://blog.csdn.net/mao_hui_fei/article/details/120654095) + +```c +lttng create my-session +lttng enable-event --kernel sched_switch +lttng add-context --kernel --type pid --type tid +lttng track --kernel --pid +lttng track --kernel --tid +lttng start +# 运行你想监控的进程或线程 +lttng stop +lttng view +lttng destroy +``` + +### 0.2.trace compass + +[Trace Compass User Guide - LTTng Kernel Analysis (eclipse.org)](https://archive.eclipse.org/tracecompass/doc/stable/org.eclipse.tracecompass.doc.user/LTTng-Kernel-Analysis.html#Control_Flow_View) + +![1720660193068](./images/1720660193068.png) + +## 1.keytime_image + +keytime_image 是一个用于捕获进程关键时间点的工具,聚焦于进程何时创建(fork,vfork, pthread_create),何时上CPU,何时下CPU,何时执行execve等。通过ebpf技术将其挂载固定的挂载点上,并在进程发生以上几种行为时进行数据收集,并对数据进行处理,可以通过数据描绘出进程在关键时间点上的行为; + +本次测试以及数据关联工作分为三部分: + +* keytime_image工具逻辑正确性的测试; +* keytime_image工具数据正确性的测试; +* keytime_image工具数据的关联关系,以及原理梳理; + +### 1.1 测试用例 test_keytime + +为了完成以上三步测试与数据关联工作,需要涉及一个测试用例,使用该测试用例可以测试keytime_image工具的逻辑正确性,并通过和lttng工具测试的结果进行对比,判断keytime_image数据正确性; + +#### 1.1.1 测试用例设计思路 + +测试用例主要目的是为了模拟一个进程的一组行为,包括keytime_image所检测的关键时间点,从而查看keytime_image在进程行为观测上是否正确无误; + +测试用例主要思路 + +* 对测试用例进程进行cpu绑核,提高优先级,保证当前cpu仅运行一个或一组进程; +* 分别通过fork,vfork创建新进程; +* 通过pthread_create创建3个不同的线程,并将三个线程绑核,保证整个线程组在同一个cpu上运行; + * 线程1:执行cpu密集型任务; + * 线程2:执行mem密集型任务; + * 线程3:执行io密集型任务; + * 注意:这三个线程会存在抢占cpu现象,这是由于三者的优先级一致; +* 通过sleep(3),让当前进程从绑定的cpu上下来,当睡眠结束,进程则会上cpu; +* 通过execve();,让测试程序执行指定的操作,方便测试keytime_image能否监测到进程执行execve; +* 如果execve()执行失败,则执行exit来终止进程; + +### 1.1.2 测试脚本: + +在通过测试用例对keytime_image工具进行逻辑正确性的验证之后,需要验证keytime_image工具采集到的数据是否是可靠的; + +这里通过一个测试脚本实现keytime_image工具和lttng同时监测测试用例的数据,并将数据导出; + +### 1.2 结果分析: + +通过测试用例和测试脚本,对keytime_image工具的逻辑正确性和数据正确性进行了评估,本小节将针对输出的数据进行正确性检测; + +#### 1.2.1.逻辑正确性: + +通过脚本,可以获取到keytime_image监测测试用例进程的全过程;以下是输出的数据: + +```shell +KEYTIME ------------------------------------------------------------------------------------------------- +TIME PID EVENT ARGS/RET/OTHERS +14:46:20 125532 onCPU oncpu_time:50032701454776 +14:46:20 125532 offCPU offcpu_time:50032701499005 +14:46:22 125532 onCPU oncpu_time:50035264147470 +14:46:22 125532 forkP_enter child_pid:127606 +14:46:22 125532 offCPU offcpu_time:50035264461066 +14:46:24 125532 forkP_exit child_pid:127606 +14:46:24 125532 onCPU oncpu_time:50037265018542 +14:46:24 125532 offCPU offcpu_time:50037265183095 +14:46:24 125532 vforkP_enter child_pid:128805 +14:46:26 125532 vforkP_exit child_pid:128805 +14:46:26 125532 onCPU oncpu_time:50039273327870 +14:46:26 125532 createT_enter child_pid:129392 +14:46:26 125532 createT_enter child_pid:129393 +14:46:26 125532 createT_enter child_pid:129394 +14:46:26 125532 offCPU offcpu_time:50039273813650 +14:46:29 125532 createT_exit child_pid:129393 +14:46:29 125532 createT_exit child_pid:129392 +14:46:29 125532 onCPU oncpu_time:50042307545368 +14:46:29 125532 offCPU offcpu_time:50042307567649 +14:46:29 125532 onCPU oncpu_time:50042411845305 +14:46:29 125532 offCPU offcpu_time:50042411869110 +14:46:29 125532 createT_exit child_pid:129394 +14:46:29 125532 onCPU oncpu_time:50042412175342 +14:46:29 125532 offCPU offcpu_time:50042412370717 +14:46:32 125532 onCPU oncpu_time:50045412482772 +14:46:32 125532 exec_enter /bin/ls -l +14:46:32 125532 offCPU offcpu_time:50045412900824 +14:46:32 125532 onCPU oncpu_time:50045413164136 +14:46:32 125532 offCPU offcpu_time:50045413644970 +14:46:32 125532 onCPU oncpu_time:50045414001685 +14:46:32 125532 exec_exit 0 +14:46:32 125532 exit 0 +14:46:32 125532 offCPU offcpu_time:50045415933416 + +``` + +结合测试用例在执行特定操作时的时间节点输出,可以验证在特定时间点,keytime_image是否正确捕获测试进程的该行为: + +```shell +#PID125532绑定CPU4:√ +#PID125532 被绑定在以下cpu上: 4 +test_proc进程的TGID:125532 PID:125532 CPU_id:4 +输入任意数字继续程序的运行: +程序开始执行... + +KEYTIME_TEST----------------------------------------------- + +fork逻辑------------------------: +fork开始时间2024-07-11 14:46:22 +(fork)子进程pid:127606,睡眠2s后退出 +fork结束时间2024-07-11 14:46:24 +(fork)pid为127606的子进程退出 + +输入任意数字开始测试vfork:vfork逻辑------------------------: +vfork开始时间2024-07-11 14:46:24 +(vfork)子进程pid:128805,睡眠2s后退出 +vfork退出时间2024-07-11 14:46:26 +(vfork)pid为128805的子进程退出 + +输入任意数字开始测试 pthread_create:pthread_create逻辑------------------------: +pthread_create开始时间2024-07-11 14:46:26 + + #3.PID:129394 TGID:125532 + #PID129394绑定CPU4:√ + #PID129394 被绑定在以下cpu上: 4 + #IO密集型 + + #2.PID:129393 TGID:125532 + #PID129393绑定CPU4:√ + #PID129393 被绑定在以下cpu上: 4 + #MEM 密集型 + + #1.PID:129392 TGID:125532 + #PID129392绑定CPU4:√ + #PID129392 被绑定在以下cpu上: 4 + #CPU密集型 + #129393退出 + #129392退出 + #129394退出 +pthread_create结束时间2024-07-11 14:46:29 + +输入任意数字开始测试 上下cpu:进程上下CPU逻辑------------------------: +CPU sleep 开始时间2024-07-11 14:46:29 +CPU sleep 结束时间2024-07-11 14:46:32 +程序睡眠3s! + +输入任意数字开始测试 execve:execve开始时间2024-07-11 14:46:32 +execve逻辑------------------------: +total 60 +-rw-rw-r-- 1 xhb xhb 194 Jul 9 10:51 Makefile +drwxrwxr-x 5 xhb xhb 4096 Jul 11 14:46 ebpf +-rwxr-xr-x 1 xhb xhb 995 Jul 10 23:10 ebpf_test.sh +drwxrwx--- 18 xhb xhb 4096 Jul 11 14:46 lttng-traces +-rwxr-xr-x 1 xhb xhb 1913 Jul 10 23:02 test_keytime.sh +-rwxr-xr-x 1 root root 26256 Jul 11 04:43 test_proc_image +-rw-rw-r-- 1 xhb xhb 9493 Jul 11 04:45 test_proc_image.c +``` + +![1720683501899](./images/1720683501899.png) + +通过对比测试用例关键点的时间和行为,与keytime_image关键点的时间完全一致,且keytime_image可以观测到进程执行cpu、mem、io等行为;至此我们可以得出结论,keytime_image逻辑正确,功能可用; + +#### 1.2.2 数据正确性 + +通过将keytime_image获取的数据同lttng作对比,便可得到 进程的整体行为是否正确,数据获取是否正确,描述该行为需要哪些数据进行关联; + +这里通过多次测试(包括但不限于指定某一进程进行数据采集,指定某一个线程组进行数据采集,不指定固定进程或线程采集全部数据信息),均证明可以证明keytime_image获取到的数据是真实可靠且可以描绘进程关键时间点行为的。原始数据在这里: + +keytime_image测出的数据:[keytime_image测试数据详细](../data/output.log) + +lttng 测出的数据: +- [lttng 测试数据表](../data/keytime_test_data.csv) +- [lttng 测试数据详细](../data/trace_data.txt) + +### 1.3.关键数据点的关联关系 + +通过lttng检测到的数据以及在trace compass上可以看到进程的行为跟踪图,我们这里可以根据lttng所提供的关键数据点指导进程行为分析,从而将这些数据关联到一起。本小节将找到这些关键数据点,并依据内核中的香相关原理来指导进程行为分析。 + +keytime_image关注点在一个进程或线程,我们以125532进程为例,分析keytime_image观测到该进程的具体行为,该进程的行为众多,这里值分析典型行为:创建进程,上下cpu,执行execve,以及进程退出: + +#### 1.3.1 创建进程: + +由于lttng关于进程创建的跟踪点为 sched_process_fork 只能识别进程创建了子进程,不能识别到通过什么方式创建的,而keytime_image可以通过uprobe和uretpobe识别到时是fork、vfork还是pythreat_create创建的子进程或线程; + +##### 1.3.1.1 fork + +**【22:811111842】** + +- 行为:主体进程125532,fork出子进程127606 + +- 数据点:父进程的信息,子进程的信息; + + * parent_comm = "test_proc_image", parent_tid = 125532, parent_pid = 125532; + + * child_comm = "test_proc_image", child_tid = 127606,child_pid = 127606; + * cpu_id = 4; + +- 原理:lttng的挂载点在sched_process_fork上,记录了父进程的信息,子进程的信息,以及在哪个cpu上进行的fork;而我们的keytime_image则将挂载点放在了用户态fork上,可以看到父进程fork开始和fork结束的时间;这一点相较于lttng关注到了用户态,更细节; + +![1720693018279](./images/1720693018279.png) + +##### 1.3.1.2 vfork + +**【24:811853001】** + +- 行为:主体进程125532 vfork 出子进程128805; + +- 数据点:父进程的信息,子进程的信息; + + - prev_comm = "test_proc_image", prev_tid = 125532,prev_prio = 20, prev_state = 1; + + - next_comm = "test_proc_image", next_tid = 127606, next_prio = 20; + +- 原理:lttng的挂载点在sched_process_fork上,记录了父进程的信息,子进程的信息,以及在哪个cpu上进行的fork;而我们的keytime_image则将挂载点放在了用户态vfork上,可以看到父进程vfork开始和fork结束的时间;并且可以知道父进程是如何创建了子进程,这一点相较于lttng关注到了用户态,更细节; + +![1720692895341](./images/1720692895341.png) + +##### 1.3.1.3 pthread_create + +**【26:820321972】** + +- 行为:主体进程125532创建线程129392, +- 数据点:父进程的信息,子进程的信息; + * parent_comm = "test_proc_image", parent_tid = 125532, parent_pid = 125532; + * child_comm = "test_proc_image", child_tid = 129392,child_pid = 125532 +- 原理:lttng的挂载点在sched_process_fork上,记录了父进程的信息,子进程的信息,以及在哪个cpu上进行的fork;而我们的keytime_image则将挂载点放在了用户态上pthread_create,可以看到父进程pthread_create开始和fork结束的时间;并且可以知道父进程是如何创建了子进程,这一点相较于lttng关注到了用户态,更细节; + +![1720693526887](./images/1720693526887.png) + +![1720693579439](./images/1720693579439.png) + +![1720693616700](./images/1720693616700.png) + + + +#### 1.3.2 上下CPU: + +上下CPU是CPU调度时产生的进程行为,由于不同的调度策略会使不同的进程上下CPU,一般进程上下CPU是在linux内核函数中的`__schedule()`中实现的,具体是通过`__schedule()–>pick_next_task()`选择下一个要上CPU的进程,`__schedule()–>context_switch()`进行上下文切换,完成进程上下CPU操作; + +keytime_image和lttng都关注到`__schedule()`中的一个静态挂载点:`sched_switch`,此时cpu知道了哪个进程下cpu,哪个进程上cpu,故而该关键点处的数据便可以描绘出进程上下cpu的行为; + +![1720694420680](./images/1720694420680.png) + +测试用例中涉及到了很多上下CPU的情况,此处将分析主体进程创建的三个线程129392,129393,129394三者之间在CPU4上的切换情况。 + +![1720696786685](./images/1720696786685.png) + +**【26:820503574】** + +- 行为:主体进程125532让出cpu,子进程 129394上CPU; +- 数据点:上CPU进程信息,下CPU进程信息; + - prev_comm = "test_proc_image", prev_tid = 125532, prev_prio = 20, prev_state = 1,; + - next_comm = "test_proc_image", next_tid = 129394, next_prio = 20; +- 原理:挂载点在sched_switch上,记录了进程在cpu上切换的数据,哪个进程下cpu,哪个进程上cpu; + +**【26:823397729】** + +- 行为:线程129394让出cpu,子进程 129393上CPU; +- 数据点:上CPU进程信息,下CPU进程信息; + - prev_comm = "test_proc_image", prev_tid = 129394, prev_prio = 20, prev_state = 2; + - next_comm = "test_proc_image", next_tid = 129393, next_prio = 20; +- 原理:挂载点在sched_switch上,记录了进程在cpu上切换的数据,哪个进程下cpu,哪个进程上cpu; + +**【26:826032494】** + +- 行为:线程129393让出cpu,子进程 129394上CPU; +- 数据点:上CPU进程信息,下CPU进程信息; + - prev_comm = "test_proc_image", prev_tid = 129393, prev_prio = 20, prev_state = 1; + - next_comm = "test_proc_image", next_tid = 129394, next_prio = 20 ; +- 原理:挂载点在sched_switch上,记录了进程在cpu上切换的数据,哪个进程下cpu,哪个进程上cpu; + +**【26:826933280】** + +- 行为:线程129394让出cpu,线程 111470上CPU; +- 数据点:上CPU进程信息,下CPU进程信息; + - prev_comm = "test_proc_image", prev_tid = 129394, prev_prio = 20, prev_state = 256; + - next_comm = "kworker/4:1", next_tid = 111470, next_prio = 20; +- 原理:挂载点在sched_switch上,记录了进程在cpu上切换的数据,哪个进程下cpu,哪个进程上cpu; + +**【26:835640901】** + +- 行为:线程129394让出cpu,线程129392上CPU; +- 数据点:上CPU进程信息,下CPU进程信息; + - prev_comm = "test_proc_image", prev_tid = 129394, prev_prio = 20, prev_state = 2; + - next_comm = "test_proc_image", next_tid = 129392, next_prio = 20 +- 原理:挂载点在sched_switch上,记录了进程在cpu上切换的数据,哪个进程下cpu,哪个进程上cpu; + +通过以上数据就可将keytime_image工具所监测到的数据所描述的进程行为描述出来,做到上下CPU这个行为的数据关联; + + + +#### 1.3.3 execve执行: + +execve是进程所执行的一个程序,它是进程的一种行为,会替换当前进程的地址空间,并加载并执行一个新的程序,这里`execve` 不创建新进程,而是替换当前进程。 + +keytime_image工具通过挂载点sys_enter_execve、sys_exit_execve对进程execve行为进行监测,而lttng通过sched_process_exec实现相同的数据监测。 + +**【32:960747436】** + +- 行为:线程 125532,执性程序/bin/ls; +- 数据点:哪个进程/线程在执行,执行的命令,下CPU进程信息; + - filename = "/bin/ls", tid = 125532, old_tid = 125532; + +* 原理:挂载点在sched_process_exec,或sys_enter_execve、sys_exit_execve上,用户态进程执行execve时会通过系统调用在内核中进行相关操作,通过sys_enter_execve、sys_exit_execve便可以获取到用户态进程执行的execve信息; + +#### 1.3.4 exit 执行: + +exit可以用于终止进程, 会导致当前进程的执行终止,并返回一个退出状态码给操作系统;同execve一样,exit会通过系统调用到内核中实现终止进程的操作;所以我们在sys_enter_exit、sys_enter_exit_group两处进行插装,获取到关于进程退出时的相关数据,这里主要是进程退出时间的统计; + +**【32:962637158】** + +- 行为:进程 125532, 退出; +- 数据点:哪个进程/线程,在什么时间点退出; + - pid : 125532 exit ret :0; + +- 原理:挂载点在sys_enter_exit、sys_enter_exit_group上,用户态进程执行exit时会通过系统调用在内核中进行相关操作,通过sys_enter_exit、sys_enter_exit_group便可以获取到用户态进程终止时的信息; + + + +## 2.lock_image + +lock_image 是一个用于捕获进程持有用户态锁的工具,聚焦于进程持有的所有用户态锁(互斥锁、自旋锁、读写锁)。通过ebpf技术将其挂载固定的挂载点上,并在进程持有以上几种锁时进行数据收集,并对数据进行处理; + +本次测试以及数据关联工作分为三部分: + +- lock_image工具逻辑正确性的测试; +- lock_image工具数据正确性的测试; +- lock_image工具数据的关联关系,以及原理梳理; + +### 2.1 测试用例 test_lock + +为了完成以上三步测试与数据关联工作,需要设计一个测试用例,使用该测试用例可以测试lock_image工具的逻辑正确性,并通过和lttng工具测试的结果进行对比,判断lock_image数据正确性; + +lock_image工具关注的是进程持有用户态锁的情况,故需要用到lttng-ust,我们需要在lttng中自定义跟踪函数用以跟踪测试用例中涉及到的进程对锁的相关行为; + +#### 2.1.1 测试用例设计思路 + +测试用例主要目的是为了模拟进程或线程成功持有锁、未成功持有锁以及争用一个锁的情景。 + +测试用例主要思路 + +- 对测试用例进程进行cpu绑核,提高优先级,保证当前cpu仅运行一个或一组进程; +- 分别定义一个自旋锁、互斥锁以及读写锁并初始化,用于线程争用持有这些锁,来查看lock_image是否能准确的获取到线程持有锁的状态以及锁的信息; +- 创建两个线程分别去申请持有提前定义好的锁,并在特殊点插入lttng-ust跟踪函数,用来和lock_image进行数据对比; + - 线程1分别去持有互斥锁、解锁互斥锁,持有读写锁-写锁、释放读写锁,持有自旋锁、释放自旋锁; + - 考虑到创建线程2的时间稍晚于线程1,故通过pthread_mutex_trylock等函数去尝试持有互斥锁1(这里涉及到pthread_mutex_lock,pthread_mutex_trylock实现方式差别,后面会提到),若尝试失败,则通过pthread_mutex_trylock去持有互斥锁2;读写锁和自旋锁同理; + +#### 2.1.2 lttng-ust跟踪用户应用事件 + +由于lttng不能像ebpf一样方便的使用uprobe和uretprobe来跟踪用户态的函数,所以我们要自己定义适合本次测试用例的跟踪点定义文件,并将`lttng_ust_tracepoint`加在测试用例相应的跟踪位置; + +我们以mutex互斥锁为例,展示线程触发互斥锁时相关跟踪点的定义,其余锁类似: + +- 请求互斥锁时的跟踪点; + +```c +TRACEPOINT_EVENT( + lock_monitor, + mutex_lock_start, + TP_ARGS(int, thread_id, void*, lock_ptr, long long unsigned int, time), + TP_FIELDS( + ctf_integer(int, thread_id, thread_id) + ctf_integer_dec(void*, lock_ptr, lock_ptr) + ctf_integer(long long unsigned int, time, time) + ) +) +``` + +- 获取互斥锁成功的跟踪点: + +```c +TRACEPOINT_EVENT( + lock_monitor, + mutex_lock_acquired, + TP_ARGS(int, thread_id, void*, lock_ptr, long long unsigned int, time), + TP_FIELDS( + ctf_integer(int, thread_id, thread_id) + ctf_integer_dec(void*, lock_ptr, lock_ptr) + ctf_integer(long long unsigned int, time, time) + ) +) +``` + +- 互斥锁解锁成功的跟踪点: + +```c +TRACEPOINT_EVENT( + lock_monitor, + mutex_lock_released, + TP_ARGS(int, thread_id, void*, lock_ptr, long long unsigned int, time), + TP_FIELDS( + ctf_integer(int, thread_id, thread_id) + ctf_integer_dec(void*, lock_ptr, lock_ptr) + ctf_integer(long long unsigned int, time, time) + ) +) +``` + +- 尝试申请互斥锁的跟踪点,对应`pthread_mutex_trylock`函数 + +```c +TRACEPOINT_EVENT( + lock_monitor, + mutex_trylock_start, + TP_ARGS(int, thread_id, void*, lock_ptr, long long unsigned int, time), + TP_FIELDS( + ctf_integer(int, thread_id, thread_id) + ctf_integer_dec(void*, lock_ptr, lock_ptr) + ctf_integer(long long unsigned int, time, time) + ) +) +``` + +通过以下指令,将跟踪点定义文件生成跟踪点头文件和实现文件: + +```shell +lttng-gen-tp lock_tracepoint.tp +``` + +这将生成 `lock_tracepoint.h` 和 `lock_tracepoint.c` 文件,便可以将我们定义的跟踪点用在测试用例中,以下是一个示例: + +```c +#include +#include "lock_tracepoint.h" +... +void *thread_lock_func1(void *arg) { + ... + /*准备开始申请互斥锁*/ + tracepoint(lock_monitor, mutex_lock_start, tid,&mutex1,(long long unsigned int)(ts.tv_sec*1000000000+ts.tv_nsec)); + pthread_mutex_lock(&mutex1); + /*锁申请成功*/ + tracepoint(lock_monitor, mutex_lock_acquired, tid,&mutex1,(long long unsigned int)(ts.tv_sec*1000000000+ts.tv_nsec)); + ... +} +``` + +编译时将我们定义好的lttng跟踪点头文件包含在测试用例中,并进行编译和链接: + +```shell +gcc -o test_lock lock_monitor.c lock_tracepoint.c -lpthread -llttng-ust +``` + +#### 2.1.3 测试脚本: + +在通过测试用例对lock_image工具进行逻辑正确性的验证之后,需要验证lock_image工具采集到的数据是否是可靠的; + +这里通过一个测试脚本实现lock_image工具和lttng同时监测测试用例的数据,并将数据导出; + +```shell +#!/bin/bash + +# 获取脚本所在目录的绝对路径 +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) + +# 指定输出目录 +OUTPUT_DIR="$SCRIPT_DIR/lttng-traces/lock_test_$(date +'%Y%m%d_%H:%M:%S')" +CSV_FILE="$OUTPUT_DIR/lock_test_data.csv" +mkdir -p "$SCRIPT_DIR/ebpf/ebpf_output_$(date +'%Y%m%d_%H:%M:%S')" +EBPF_OUTPUT_DIR="$SCRIPT_DIR/ebpf/ebpf_output_$(date +'%Y%m%d_%H:%M:%S')" + +# 获取目标进程的 PID +TARGET_PID=$(pidof test_proc_image) + +if [ -z "$TARGET_PID" ]; then + echo "目标进程未运行,请先启动目标进程。" + exit 1 +fi +echo "测试程序 PID: $TARGET_PID" + +# 创建会话并指定输出目录 +sudo lttng create xhb_lock --output=$OUTPUT_DIR + +# 启用内核事件,仅针对特定 PID +sudo lttng enable-event -u 'lock_monitor:*' + +# 添加上下文信息 +sudo lttng add-context --kernel --type pid + +sudo lttng track --kernel --pid $TARGET_PID + +# 运行proc_image监测工具 +cd /home/xhb/lmp2/lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/ +sudo ./proc_image -l > $EBPF_OUTPUT_DIR/lock_output.log & +PROC_IMAGE_PID=$! +sleep 1 +sudo ./controller -l -P $TARGET_PID -a +# 启动会话 +sudo lttng start +read + +sudo lttng stop +sudo lttng view +sudo lttng destroy +echo "追踪数据已保存到 $OUTPUT_DIR 目录中" +# 将 LTTng 跟踪数据转换为文本格式 +babeltrace2 $OUTPUT_DIR > $OUTPUT_DIR/trace_data.txt + +echo "lttng、eBPF 程序数据收集完毕,请 Ctrl+C 结束" +read +echo "数据已导出到 $CSV_FILE" +``` + +### 2 .2 结果分析: + +通过测试用例和测试脚本,对lock_image工具的逻辑正确性和数据正确性进行了评估,本小节将针对输出的数据进行正确性检测; + +#### 1.2.1.逻辑正确性: + +通过脚本,可以获取到lock_image监测测试用例进程的全过程;以下是输出的数据: + +``` + #PID24685绑定CPU4:√ + #PID24685 被绑定在以下cpu上: 4 +test_proc进程的TGID:24685 PID:24685 CPU_id:4 +输入任意数字继续程序的运行:1 +程序开始执行... + +LOCK_TEST-------------------------------------------------- +用户态自旋锁地址:94014792843640 +用户态互斥锁地址:94014792843456 , 94014792843520 +用户态读写锁地址:94014792843584 +线程1 tid:24994 ... +tid :24994 申请互斥锁成功,并将持有1s +线程2 tid:24995 ... +tid :24995 申请互斥锁1失败 +tid :24995 尝试申请读写锁2... +tid :24995 申请互斥锁2成功,并将持有1s +tid :24994 互斥锁解锁成功 +tid:24994 尝试申请读写锁... +tid :24994 申请读写锁-读锁成功,并将持有1s +tid :24995 互斥锁解锁2成功 +tid:24995 尝试申请读写锁... +tid :24995 申请读写锁-写锁失败 +tid:24995 尝试申请自旋锁... +tid :24995 申请自旋锁成功,并将持有1s +tid :24994 读写锁-读锁解锁成功 +tid:24994 尝试申请自旋锁... +tid :24995 自旋锁解锁成功 +tid :24994 申请自旋锁成功,并将持有1s +tid :24994 自旋锁解锁成功 +``` + +**【测试用例逻辑分析】**:我们可以根据测试用例输出的信息,对该进程争用锁的整体画像进行梳理,以此为基准,对照lock_image工具查看其逻辑是否正确; + +- 目标锁:【用户态自旋锁: 94014792843640】、【用户态互斥锁1:94014792843456]】,【用户态互斥锁2: 94014792843520】、【用户态读写锁:94014792843584】。 +- 目标线程:线程1:24994 ;线程2:24995; +- 主要行为: + - 1.主体进程24685 初始化所有锁; + - 2.线程1被创建后,立即申请【用户态互斥锁1:94014792843456】,并将持有该锁1s; + - 3.线程2被创建后,立即尝试申请【用户态互斥锁1:94014792843456】,由于该锁被线程1持有,线程2申请失败,并尝试申请【用户态互斥锁2: 94014792843520】,申请成功并持有该锁1s; + - 4.线程1持有【用户态互斥锁1:94014792843456】1s后将其释放,解锁成功; + - 5.线程1申请持有【用户态读写锁:94014792843584】,并将持有1s; + - 6.线程2解锁【用户态互斥锁2: 94014792843520】成功; + - 7.线程2尝试持有【用户态读写锁:94014792843584】,由于该锁被线程1持有,线程2申请失败,则放弃申请该锁; + - 8.线程2尝试持有【用户态自旋锁: 94014792843640】,由于该锁未被持有,故线程2申请成功,并将持有1s + - 9.线程1持有【用户态读写锁:94014792843584】1s后解锁成功; + - 10.线程1申请持有【用户态自旋锁: 94014792843640】,由于该锁被线程2持有,故线程1等待该锁被释放后再申请持有; + - 11.线程2持有【用户态自旋锁: 94014792843640】1s并解锁成功; + - 12.线程1申请持有【用户态自旋锁: 94014792843640】并将持有1s; + - 13.线程1解锁【用户态自旋锁: 94014792843640】; + +**【lock_image跟踪结果】** + +lock_image工具跟踪的是整个进程组的数据,故会有父进程24685关于非指定锁的一些使用情况,这里选择性的删除,仅关注我们提前定义好可以跟踪的锁。 + +lock_image跟踪到的数据: + +```shell +USERLOCK ---------------------------------------------------------- +TIME TGID PID LockAddr LockStatus +10139926674250 24685 24685 94014792843640 spinlock_unlock +10139926898189 24685 24994 94014792843456 mutex_req +10139926902097 24685 24994 94014792843456 mutex_lock-0 +10140927410524 24685 24995 94014792843456 mutex_req +10140927416075 24685 24995 94014792843456 mutex_lock-16 +10140927418850 24685 24995 94014792843520 mutex_req +10140927423449 24685 24995 94014792843520 mutex_lock-0 +10140927462995 24685 24994 94014792843456 mutex_unlock +10140927489736 24685 24994 94014792843584 rdlock_req +10140927496720 24685 24994 94014792843584 rdlock_lock-0 +10141927546414 24685 24995 94014792843520 mutex_unlock +10141927603794 24685 24995 94014792843584 wrlock_req +10141927610998 24685 24995 94014792843584 wrlock_lock-16 +10141927621508 24685 24995 94014792843640 spinlock_req +10141927627018 24685 24995 94014792843640 spinlock_lock-0 +10141927646065 24685 24994 94014792843584 rdlock_unlock +10141927658509 24685 24994 94014792843640 spinlock_req +10142927736555 24685 24995 94014792843640 spinlock_unlock +10142927920498 24685 24994 94014792843640 spinlock_lock-0 +10143928473997 24685 24994 94014792843640 spinlock_unlock +``` + +针对lock_image跟踪到的数据,对其进行逻辑行为分析: + +- 目标锁:【用户态自旋锁: 94014792843640】、【用户态互斥锁1:94014792843456]】,【用户态互斥锁2: 94014792843520】、【用户态读写锁:94014792843584】。 + +- 目标线程:线程1:24994 ;线程2:24995 + +- 行为分析: + + - **[时间:10139926674250 ]**:由于`pthread_spin_init()`会调用`pthread_spin_unlock()`故主体进程触发pthread_spin_unlock()被检测到,`spinlock_unlock`; + - **[时间:10139926898189]~[时间:10139926902097 ]**: 线程1发出申请【互斥锁1:94014792843456】请求,并申请成功,`mutex_lock-0`; + - **[时间:10140927410524]~[时间:10140927416075]**: 线程2发出申请【互斥锁1:94014792843456】请求,申请失败,返回值-16,`mutex_lock-16`; + - **[时间:10140927418850]~[时间:10140927423449]**:线程2尝试申请用户态互斥锁2: 94014792843520】,申请成功,`mutex_lock-0`; + - **[时间:10140927462995]**:线程1持有锁1s(10140927462995-10139926902097 =1s),并解锁成功`mutex_unlock`; + - **[时间:10140927489736]~[时间:10140927496720]**:线程1申请持有【用户态读写锁:94014792843584】,申请成功,`rdlock_lock-0`; + - **[时间:10141927546414]** :线程2持有锁1s (10140927423449-10141927546414=1s)并解锁成功`mutex_unlock`; + - **[时间:10141927603794]~[时间:10141927610998]**:线程2尝试申请【用户态读写锁:94014792843584】写锁,失败,返回值-16,`wrlock_lock-16`; + - **[时间:10141927621508]~[时间:10141927627018]**:线程2尝试申请自旋锁【用户态自旋锁: 94014792843640】,申请成功,`spinlock_lock-0`; + - **[时间:10141927646065 ]**:线程1持有【用户态读写锁:94014792843584】1s(10140927496720-10141927646065=1s),并解锁成功,`rdlock_unlock`; + - **[时间:10141927658509 ]**:线程1尝试申请【用户态自旋锁: 94014792843640】,`spinlock_req` + - **[时间:10142927736555]**:线程2持有【用户态自旋锁: 94014792843640】1s,并释放成功,`spinlock_unlock`; + - **[时间:10142927920498]~[时间:10143928473997]**:线程1持有【用户态自旋锁: 94014792843640】,从发出持有申请到实际持有,共等待了10142927920498-10141927658509 =1.00s,并在持有1s后解锁; + + 通过将lock_image对进程关于锁的行为分析的逻辑同测试用例的逻辑对比,发现lock_image可以完美的观测到测试用的全部行为,包括一些细微的行为; + + + +#### 2.2.2 数据正确 + +通过将lock_image获取的数据同lttng作对比,便可得到 进程的整体行为是否正确,数据获取是否正确,描述该行为需要哪些数据进行关联; + +由于lttng-ust仅仅是将自定义的跟踪点挂到了测试用例上,而不是像uprobe一样挂载到libc用户态函数中,在时间上会存在一定的误差,从实现原理上讲,我们的lock_image所采集到的时间点更贴近真实时间; + +### 2.3 关键数据点的联系: + +时间点,进程或线程号,锁的地址,对锁的相关操作的结果这些数据点可以描绘出一个进程的行为:哪个线程在具体的时间点对具体哪个锁进行了什么操作?结果如何? + +缺乏功能:线程申请锁失败后,不能找到哪个线程持有该锁; + +## 3.schedule_image + +为了完成以上三步测试与数据关联工作,需要设计一个测试用例,使用该测试用例可以测试schedule_image工具的逻辑正确性,并通过和lttng工具测试的结果进行对比,判断schedule_image数据正确性; + +schedule_image工具关注的是进程被调度时的延迟,具体点就是进程从被唤醒到上cpu这段时间的延迟,为了比较数据的正确性,我们采用lttng,将lttng跟踪点放在`sched_switch`、`sched_wakeup`、`sched_wakeup_new`。 + +### 3.1 测试用例test_sched + +#### 3.1.1 测试用例设计思路: + +测试用例主要思路是:创建多个线程去执行CPU密集型任务,查看这些线程在被调度时的延迟情况,与此同时采用sysbench工具产生高负载环境,产生大量调度事件,增加系统整体调度频率。 + +测试用例主要思路 + +- 对测试进程进行绑核,绑定在cpu4上; +- 创建16个线程去执行CPU密集型任务,营造出调度的环境; +- 线程执行完任务后退出; + +在执行测试用例前需要用sysbench创建36个进程执行大量的CPU密集型任务,去提高系统负载,此时再通过`schedule_image()`监测该环境下的线程组的行为。 + +#### 3.1.2 测试脚本: + +在通过测试用例对schedule_image工具进行逻辑正确性的验证之后,需要验证schedule_image工具采集到的数据是否是可靠的; + +这里通过一个测试脚本实现schedule_image工具和lttng同时监测测试用例的数据,并将数据导出; + +由于lttng输出的结果是详细的单个线程每次发生调度时的数据,为了符合schedule_image的输出结果,设计了python脚本对lttng原始数据进行计算处理; + +### 3.2 结果分析: + +通过测试用例和测试脚本,对schedule_image工具的逻辑正确性和数据正确性进行了评估,本小节将针对输出的数据进行正确性检测; + +#### 3.2.1.逻辑正确性 + +通过脚本,可以获取到schedule_image监测测试用例进程的在被调度时的延迟情况;由于本次测试仅关注单个进程或线程组的情况,忽略对整个系统当前的调度延迟情况的验证; + +下面是测试用例在执行了相关操作后的输出,可以看出测试用例创建了30255~30270共计16个线程来执行cpu密集型任务,在schedule_image的输出中信息中也可以查看到相关线程的调度延迟情况,证明了工具可以监测线程调度延迟; + +``` + #PID29705绑定CPU4:√ + #PID29705 被绑定在以下cpu上: 4 +test_proc进程的TGID:29705 PID:29705 CPU_id:4 +输入任意数字继续程序的运行:1 +程序开始执行... + #1.PID:30255 TGID:29705 + #CPU密集型 + + #1.PID:30256 TGID:29705 + #CPU密集型 + + #1.PID:30257 TGID:29705 + #CPU密集型 + + #1.PID:30258 TGID:29705 + #CPU密集型 + + #1.PID:30259 TGID:29705 + #CPU密集型 + + #1.PID:30260 TGID:29705 + #CPU密集型 + + #1.PID:30261 TGID:29705 + #CPU密集型 + + #1.PID:30262 TGID:29705 + #CPU密集型 + + #1.PID:30263 TGID:29705 + #CPU密集型 + + #1.PID:30264 TGID:29705 + #CPU密集型 + + #1.PID:30270 TGID:29705 + #CPU密集型 + + #1.PID:30269 TGID:29705 + #CPU密集型 + + #1.PID:30268 TGID:29705 + #CPU密集型 + + #1.PID:30267 TGID:29705 + #CPU密集型 + + #1.PID:30266 TGID:29705 + #CPU密集型 + + #1.PID:30265 TGID:29705 + #CPU密集型 + #30263退出 + #30260退出 + #30256退出 + #30269退出 + #30261退出 + #30262退出 + #30255退出 + #30264退出 + #30257退出 + #30270退出 + #30266退出 + #30258退出 + #30259退出 + #30268退出 + #30267退出 +``` + +schedule_image工具监测到的结果: + +``` +SCHEDULE ---------------------------------------------------------------------------------------------------------------------- +TIME TGID PID PRIO | P_AVG_DELAY(ms) S_AVG_DELAY(ms) | P_MAX_DELAY(ms) S_MAX_DELAY(ms) | P_MIN_DELAY(ms) S_MIN_DELAY(ms) | +21:55:14 29705 30263 120 | 59.439293 4.486027 | 72.030970 4629.971274 | 37.406719 0.000882 | +21:55:14 29705 30265 120 | 59.277075 4.486027 | 74.783045 4629.971274 | 14.999412 0.000882 | +21:55:14 29705 30267 120 | 59.200207 4.486027 | 71.837144 4629.971274 | 28.286873 0.000882 | +21:55:14 29705 29705 120 | 0.004809 4.486027 | 0.004809 4629.971274 | 0.004809 0.000882 | +21:55:14 29705 30256 120 | 60.343717 4.486027 | 80.016246 4629.971274 | 48.131959 0.000882 | +21:55:14 29705 30269 120 | 60.828869 4.486027 | 104.015723 4629.971274 | 44.202620 0.000882 | +21:55:14 29705 30264 120 | 62.809482 4.486027 | 202.431816 4629.971274 | 41.031998 0.000882 | +21:55:14 29705 30257 120 | 60.246384 4.486027 | 96.055618 4629.971274 | 47.910671 0.000882 | +21:55:14 29705 30260 120 | 59.768547 4.486027 | 92.002259 4629.971274 | 25.142902 0.000882 | +21:55:14 29705 30259 120 | 59.845333 4.486027 | 108.348840 4629.971274 | 19.561148 0.000882 | +21:55:14 29705 30258 120 | 60.156634 4.486027 | 122.003156 4629.971274 | 13.581161 0.000882 | +21:55:14 29705 30262 120 | 58.650610 4.486027 | 104.011502 4629.971274 | 0.019427 0.000882 | +21:55:14 29705 30270 120 | 60.002680 4.486027 | 72.012554 4629.971274 | 44.008079 0.000882 | +21:55:14 29705 30261 120 | 59.596674 4.486027 | 72.089872 4629.971274 | 29.450209 0.000882 | +21:55:14 29705 30268 120 | 60.397836 4.486027 | 82.019640 4629.971274 | 36.338791 0.000882 | +21:55:14 29705 30255 120 | 60.714674 4.486027 | 77.784962 4629.971274 | 48.009419 0.000882 | +21:55:14 29705 30266 120 | 59.679530 4.486027 | 76.011399 4629.971274 | 39.815206 0.000882 | + +``` + + + +#### 3.2.2 数据正确性 + +通过将schedule_image获取的数据同lttng作对比,便可得到 schedule_image工具获取到的调度延迟数据是否正确,同时也验证了schedule_image工具在逻辑上的正确性。以下是schedule_image和lttng获取到的数据进行比对: + +在时间【21:55:14】时,schedule_image以及lttng产生了如下数据: + +- **线程30259**: + - 平均时延 + - schedule_image:59.845333 ms + - lttng:60.044201ms + - 二者相差误差在0.198868ms + - 最大时延 + - schedule_image:108.348840 ms + - lttng:108.34258ms + - 二者相差误差在0.00626ms + - 最小时延 + - schedule_image:19.561148 ms + - lttng:19.57056ms + - 二者相差误差在0.009412ms +- **线程3025966**: + +- 平均时延 + - schedule_image:59.679530 ms + - lttng:59.530130ms + - 二者相差误差在0.1494ms +- 最大时延 + - schedule_image:76.011399 ms + - lttng:76.00721ms + - 二者相差误差在0.004189ms +- 最小时延 + - schedule_image:39.815206 ms + - lttng:39.82198ms + - 二者相差误差在0.006774ms +- **线程3025969**: +- 平均时延 + - schedule_image:60.736575 ms + - lttng:60.496016 ms + - 二者相差误差在0.2405598ms +- 最大时延 + - schedule_image:104.015723 ms + - lttng:104.01492 ms + - 二者相差误差在0.000803ms +- 最小时延 + - schedule_image:44.202620 ms + - lttng:44.19760ms + - 二者相差误差在0.00502ms + +由于数据量过于庞大,仅从中抽取以上三条数据作为对比,两种监测数据之差可以看出,schedule_image采集到的数据和lttng官方工具采集到的数据是完全一致的(误差小于0.01%),故得到结论schedule_image工具的逻辑正确性与数据正确性完全成立; + + + +### 3.3 关键数据点的关联关系 + +由于每秒钟产生调度事件十分繁多,schedule_image工具关注于每秒钟线程调度延迟的最大、最小以及平均值。可以将这三个指标作为测评单个进程调度延迟性能的指标,而不关注进程单次调度行为而产生的延迟(这一部分可以通过keytime_image中的上下CPU功能来监测其具体行为)。 + +schedule_image采集到的这个调度延迟数据指标可以和keytime_image相关联,用于研究当线程发生上cpu行为时,该线程此次调度所等待的时间。 + + + +## 4.syscall_image + +syscall_image 是一个用于捕获进程发生系统调用的时工具,他致力于发现系统中每一个线程/进程的系统调用行为,他通过将挂载点放在sys_enter,sys_exit来发现系统中产生的系统调用,并计算没每次系统调用的延迟时间,做到在宏观上对系统中产生系统调用进行分析; + +本次测试以及数据关联工作分为三部分: + +- syscall_image工具逻辑正确性的测试; +- syscall_image工具数据正确性的测试; +- syscall_image工具数据的关联关系,以及原理梳理; + +### 4.1 测试用例test_sys + +为了完成以上三步测试与数据关联工作,需要设计一个测试用例,使用该测试用例可以测试syscall_image工具的逻辑正确性,并通过和lttng工具测试的结果进行对比,判断syscall_image数据正确性; + +syscall_image工具关注的是单个进程发生系统调用时的延迟以及其系统调用序列,为了比较数据的正确性,我们采用lttng,由于lttng没有像sys_enter,sys_exit对应的跟踪点,所以我们将和系统调用有关的全部跟踪点打开,便于捕获所有的系统调用; + +#### 4.1.1 测试用例设计思路 + +测试用例主要思路是:参考 UnixBench 中的 syscall 测试,我们通过syscall(SYS_getpid);来不停地直接调用getpid系统调用,在经过多次调用后,便可以通过查看syscall_image工具监测的结果来验证其正确性。 + +测试用例主要思路 + +- 对测试进程进行绑核,绑定在cpu4上; +- 连续执行10次系统调用`syscall(SYS_getpid);`每执行一次休息1s + +```c + if(env.syscall_test){ + printf("SYSCALL_TEST----------------------------------------------\n"); + + // 系统调用序列逻辑(参考 UnixBench 中的 syscall 测试) + printf("系统调用序列逻辑:\n"); + printf("每调用一次 SYS_getpid 系统调用睡眠 1 s,循环 10 次\n"); + int count = 10; + while(count){ + syscall(SYS_getpid); + count--; + sleep(1); + } + } +``` + +在执行我们指定的系统调用SYS_getpid的同时,一定会伴随着其他系统调用的使用,需要用syscall-image工具对结果进行进一步的分析; + +#### 4.1.2 测试脚本: + +在通过测试用例对syscall_image工具进行逻辑正确性的验证之后,需要验证syscall_image工具采集到的数据是否是可靠的; + +这里通过一个测试脚本实现syscall_image工具和lttng同时监测测试用例的数据,并将数据导出;测试脚本主要完成的工作: + +- 再打开测试用例之后,使用syscalll_image和lttng两种放法对其进行数据监测; +- 将采集到的数据输入进指定的文件中; +- 为了方便数据分析和结果对比,需要将syscall_image工具拿到的由系统调用号组成的系统调用序列转换为由系统调用名组成的序列; +- 将lttng采集到的数据进行逻辑计算,计算出每个系统调用的时延并作统计; + +### 4.2 结果分析: + +通过测试用例和测试脚本,已经将采集到的数据处理结束,接下来就需要对syscall_image工具进行逻辑正确性以及数据正确性的验证; + +#### 4.2.1.逻辑正确性 + +由于syscall_image工具主要检测的是进程所产生的系统调用情况,所以在逻辑上我们需要进行一下对比: + +- 测试用例所用到的系统调用,syscall_image工具是否全部监测到; +- syscall_image工具检测到的系统调用是否和ttng所检测到的一致; + +通过脚本,可以获取到syscall_image监测测试用例进程所用到的系统调用,以及lttng所检测到的系统调用序列,由于本次测试仅关注单个进程或线程组的情况,忽略对整个系统当前的系统调用情况的验证; + +**【syscall_image 对比 测试用例】** + +测试用例会连续10次调用SYS_getpid,每调用完一次就会睡眠1s; + +我们可以在syscall_image工具的输出数据中看到如下信息: + +![](./images/syscall_image数据对比图1.png) + +正好是十次连续的系统调用,证明我们的工具是逻辑正确的; + +**【syscall_image 对比 lttng结果】** + +lttng监测到的结果: + +![](./images/syscall_image数据对比图2.png) + +通过lttng以及syscall_image的对比,可以证明我们的syscall_image工具在逻辑上是正确无误的; + +其中还有很多细节没有展示,可以在以下几个原始数据中进行对比: + +- +- + +#### 4.2.2 数据正确性 + +前面我们已经对syscall-image工具进行了逻辑真确性验证,接下来我们通过对比一些数据来验证数据正确性,这些数据包括: + +- 最大系统调用延迟 +- 最小系统调用延迟 +- 平均系统调用延迟; +- 被调用最多的系统调用; + +通过下图我们可以看到lttng采集到的数据中,调用次数最多的前三个系统调用分别是:rt_sigprocmask、close 、 munmap,而这与我们在syscall-image中统计得到的数据一致; + +```shell +SYSCALL --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +TIME TGID PID 1st/num 2nd/num 3nd/num | P_AVG_DELAY(ns) S_AVG_DELAY(ns) | P_MAX_DELAY(ns) S_MAX_DELAY(ns) | P_MIN_DELAY(ns) S_MIN_DELAY(ns) | SYSCALLS +... +16:08:44 92328 92328 14 /532 3 /131 11 /129 | 12054909 13682568 | 1000994442 10006484305 | 651 281 | 14,14,3,14,14 +``` + +![](./images/syscall_image数据对比图3.png) + +- 最大系统调用延迟: + - syscall_image:1000994442ns + - lttng:1000996999.999999ns + - 差值:2558ns +- 最小系统调用延迟: + - syscall_image:651ns + - lttng:1000ns + - 差值:349ns +- 平均系统调用延迟: + - syscall_image:11982729ns + - lttng:11434793.1428ns + - 差值:547935ns + +通过以上数据的对比,我们可以得出结论,syscall_image在数据层面是真实可靠的,他与lttng之间存在0.0000255%的误差,是可以忽略的。 + +详细的原始数据可在这里查看: + +- +- + +### 4.3 关键数据点的关联关系 + +由于每秒钟产生系统调用十分繁多,我们可以将其作为分析进程行为非常细粒度的指标,将最大系统调用延迟、最小系统调用延迟以及平均系统调用延迟作为宏观分析指标; + +数据关联关系: + +- 进程的所有的系统调用信息可以通过以下的数据点进行关联: + - 线程号/进程号;系统调用号;系统调用时延;进入系统调用时间点; +- 系统调用的总体统计数据可以在宏观层面对进程的行为进行指导判断: + - 系统调用次数排名; + - 最大系统调用延迟、平均系统调用延迟、最小系统调用延迟; diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/include/helpers.h b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/include/helpers.h index 617b314cb..ab243a79a 100644 --- a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/include/helpers.h +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/include/helpers.h @@ -62,8 +62,6 @@ #define warn(...) fprintf(stderr, __VA_ARGS__) -typedef long long unsigned int u64; -typedef unsigned int u32; #define NR_syscalls 500 @@ -78,6 +76,7 @@ const char *kt_ctrl_path = "/sys/fs/bpf/proc_image_map/kt_ctrl_map"; const char *lock_ctrl_path = "/sys/fs/bpf/proc_image_map/lock_ctrl_map"; const char *sched_ctrl_path = "/sys/fs/bpf/proc_image_map/sched_ctrl_map"; const char *sc_ctrl_path = "/sys/fs/bpf/proc_image_map/sc_ctrl_map"; +const char *mfutex_ctrl_path = "/sys/fs/bpf/proc_image_map/mfutex_ctrl_map"; struct proc_syscall_info { int first_syscall; @@ -220,6 +219,23 @@ int update_lock_ctrl_map(struct lock_ctrl lock_ctrl){ return 0; } +int update_mfutex_ctrl_map(struct mfutex_ctrl mfutex_ctrl){ + int err,key = 0; + int mfutexmap_fd; + + mfutexmap_fd = bpf_obj_get(mfutex_ctrl_path); + if (mfutexmap_fd < 0) { + fprintf(stderr,"Failed to open mfutex_ctrl_map file\n"); + return mfutexmap_fd; + } + err = bpf_map_update_elem(mfutexmap_fd,&key,&mfutex_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update mfutex_ctrl_map elem\n"); + return err; + } + return 0; +} + int update_sc_ctrl_map(struct sc_ctrl sc_ctrl){ int err,key = 0; int scmap_fd; diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/include/proc_image.h b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/include/proc_image.h index ecfc1905f..101146dbe 100644 --- a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/include/proc_image.h +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/include/proc_image.h @@ -27,7 +27,13 @@ #define LAST_ARG (FULL_MAX_ARGS_ARR - ARGSIZE) #define TASK_RUNNING 0x00000000 - +// #define MUTEX_FLAG 1 +// #define RWLOCK_FLAG 2 +// #define SPIN_FLAG 3 +// #define RCU_FLAG 4 +// #define FUTEX_FLAG 5 +typedef long long unsigned int u64; +typedef unsigned int u32; #define MAX_STACK_DEPTH 128 typedef __u64 stack_trace_t[MAX_STACK_DEPTH]; @@ -65,6 +71,7 @@ struct total_rsc{ struct sc_ctrl { bool sc_func; bool enable_myproc; + bool is_container; pid_t target_pid; pid_t target_tgid; int syscalls; @@ -114,6 +121,38 @@ struct lock_event{ long long unsigned int time; }; +//mfutex +struct mfutex_ctrl{ + bool lock_func; + bool enable_myproc; + pid_t target_pid; + pid_t target_tgid; +}; + +struct per_request{//每条锁请求信息 + int pid; + u64 start_request_time; + u64 start_hold_time; + u64 wait_delay; +}; + +struct lock_record_key{ + u64 lock_ptr;//哪个锁 + int pid;//进程号 +}; + +struct record_lock_key{ + u64 lock_ptr;//哪个锁 + int cnt;//第几次申请该锁 +}; +struct per_lock_event{ + u64 lock_ptr; + int type;//1:互斥锁;2:读写锁读锁;3:读写锁写锁;4:自旋锁 + int owner,last_owner;//目前谁持有锁; + u64 start_hold_time,last_hold_delay;//持有锁的时间; + int cnt;//等待锁+持有锁的数量; +}; + // keytime_image struct kt_ctrl{ bool kt_func; diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/proc_image.c b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/proc_image.c index b52a77712..b185d67b2 100644 --- a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/proc_image.c +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/proc_image.c @@ -34,6 +34,7 @@ #include "lock_image.skel.h" #include "keytime_image.skel.h" #include "schedule_image.skel.h" +#include "mfutex.skel.h" #include "hashmap.h" #include "helpers.h" #include "trace_helpers.h" @@ -65,6 +66,8 @@ static struct env { int lock_prev_tgid; int sched_prev_tgid; int sc_prev_tgid; + char hostname[64]; + bool enable_mfutex; } env = { .output_resourse = false, .output_schedule = false, @@ -88,15 +91,25 @@ static struct env { .lock_prev_tgid = 0, .sched_prev_tgid = 0, .sc_prev_tgid = 0, + .hostname = "", + .enable_mfutex = false, }; struct hashmap *map = NULL; +struct resource_image_bpf *resource_skel = NULL; +struct syscall_image_bpf *syscall_skel = NULL; +struct lock_image_bpf *lock_skel = NULL; +struct keytime_image_bpf *keytime_skel = NULL; +struct schedule_image_bpf *schedule_skel = NULL; +struct mfutex_bpf *mfutex_skel = NULL; + static int scmap_fd; static int rscmap_fd; static int lockmap_fd; static int ktmap_fd; static int schedmap_fd; +static int mfutexmap_fd; static struct timespec prevtime; static struct timespec currentime; @@ -105,6 +118,7 @@ char *lock_status[] = {"", "mutex_req", "mutex_lock", "mutex_unlock", "rdlock_req", "rdlock_lock", "rdlock_unlock", "wrlock_req", "wrlock_lock", "wrlock_unlock", "spinlock_req", "spinlock_lock", "spinlock_unlock"}; +char *mfutex_type[] = {"","MUTEX","RW_LOCK","SPIN_LOCK","RCU_LOCK","FUTEX_LOCK"}; char *keytime_type[] = {"", "exec_enter", "exec_exit", "exit", @@ -131,6 +145,7 @@ static const struct argp_option opts[] = { { "lock", 'l', NULL, 0, "Attach eBPF functions about lock(but do not start)" }, { "keytime", 'k', NULL, 0, "Attach eBPF functions about keytime(but do not start)" }, { "schedule", 'S', NULL, 0, "Attach eBPF functions about schedule (but do not start)" }, + { "mfutex", 'm', NULL, 0, "Attach eBPF functions about mfutex (but do not start)" }, { NULL, 'h', NULL, OPTION_HIDDEN, "show the full help" }, {}, }; @@ -144,6 +159,7 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) env.enable_lock = true; env.enable_keytime = true; env.enable_schedule = true; + env.enable_mfutex = true; break; case 'r': env.enable_resource = true; @@ -160,6 +176,9 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) case 'S': env.enable_schedule = true; break; + case 'm': + env.enable_mfutex = true; + break; case 'h': argp_state_help(state, stderr, ARGP_HELP_STD_HELP); break; @@ -509,6 +528,57 @@ static int print_lock(void *ctx, void *data,unsigned long data_sz) return 0; } +static int print_mfutex(void *ctx, void *data,unsigned long data_sz) +{ + const struct per_lock_event *e = data; + time_t now = time(NULL);// 获取当前时间 + struct tm *localTime = localtime(&now);// 将时间转换为本地时间结构 + printf("%02d:%02d:%02d ",localTime->tm_hour, localTime->tm_min, localTime->tm_sec); + printf("%-12s 0x%x %-10d %-16llu %-11llu %-11lu | ",mfutex_type[e->type],e->lock_ptr,e->owner,e->start_hold_time,e->last_owner,e->last_hold_delay); + + int err,record_lock_fd =bpf_map__fd(mfutex_skel->maps.record_lock); + + struct record_lock_key lookup_key = {-1,-1}, next_key; + while(!bpf_map_get_next_key(record_lock_fd, &lookup_key, &next_key)){ + // printf("next_key:%x target_ptr:%x\n",next_key,e->lock_ptr); + if(next_key.lock_ptr != e->lock_ptr) { + lookup_key = next_key; + continue;//不是目标锁,跳过 + } + struct per_request per_request = {}; + err = bpf_map_lookup_elem(record_lock_fd,&next_key,&per_request); + if (err < 0) { + // printf(stderr, "failed to lookup info: %d\n", err); + lookup_key = next_key; + continue;//不是目标锁,跳过 + } + float wait_delay = (e->start_hold_time-per_request.start_request_time)/1000000000.0; + if(wait_delay<=0||wait_delay>=10000) + printf("%d(NULL) | ",per_request.pid); + else printf("%d(%ds) | ",per_request.pid,((e->start_hold_time-per_request.start_request_time)/1000000000)); + lookup_key = next_key; + } + printf("\n"); + + // struct record_lock_key key; + // key.lock_ptr = e->lock_ptr; + // key.cnt = 1; + // while(key.cnt<=e->cnt){ + // struct per_request per_request; + // err = bpf_map_lookup_elem(record_lock_fd,&key,&per_request); + // if (err < 0) { + // // fprintf(stderr, "failed to lookup info: %d\n", err); + // printf("(%llu|%d) ",key.lock_ptr,key.cnt); + // key.cnt++; + // continue;//没找到,就去找下一个 + // } + // printf("%d(%lu)| ",per_request.pid,per_request.wait_delay); + // key.cnt++; + // } + // printf("\n"); + return 0; +} + static void inline quoted_symbol(char c) { switch(c) { case '"': @@ -691,6 +761,24 @@ static int lock_attach(struct lock_image_bpf *skel) return 0; } +static int mfutex_attach(struct mfutex_bpf *skel) +{ + int err; + + ATTACH_UPROBE_CHECKED(skel,pthread_mutex_lock,pthread_mutex_lock_enter); + ATTACH_URETPROBE_CHECKED(skel,pthread_mutex_lock,pthread_mutex_lock_exit); + // ATTACH_UPROBE_CHECKED(skel,__pthread_mutex_trylock,__pthread_mutex_trylock_enter); + // ATTACH_URETPROBE_CHECKED(skel,__pthread_mutex_trylock,__pthread_mutex_trylock_exit); + ATTACH_UPROBE_CHECKED(skel,pthread_mutex_unlock,pthread_mutex_unlock_enter); + + // ATTACH_URETPROBE_CHECKED(skel,pthread_mutex_unlock,pthread_mutex_unlock_exit); + + + err = mfutex_bpf__attach(skel); + CHECK_ERR(err, "Failed to attach BPF mfutex skeleton"); + + return 0; +} static int keytime_attach(struct keytime_image_bpf *skel) { @@ -723,21 +811,28 @@ static void sig_handler(int signo) exiting = true; } +void get_hostname() { + char hostname[64]; + int result = gethostname(hostname, sizeof(hostname)); + if (result == 0) { + strcpy(env.hostname,hostname); + } else { + perror("gethostname"); + } +} + int main(int argc, char **argv) { - struct resource_image_bpf *resource_skel = NULL; struct bpf_map *rsc_ctrl_map = NULL; - struct syscall_image_bpf *syscall_skel = NULL; struct ring_buffer *syscall_rb = NULL; struct bpf_map *sc_ctrl_map = NULL; - struct lock_image_bpf *lock_skel = NULL; struct ring_buffer *lock_rb = NULL; struct bpf_map *lock_ctrl_map = NULL; - struct keytime_image_bpf *keytime_skel = NULL; struct ring_buffer *keytime_rb = NULL; struct bpf_map *kt_ctrl_map = NULL; - struct schedule_image_bpf *schedule_skel = NULL; struct bpf_map *sched_ctrl_map = NULL; + struct ring_buffer *mfutex_rb = NULL; + struct bpf_map *mfutex_ctrl_map = NULL; pthread_t thread_enable; int key = 0; int err; @@ -802,7 +897,9 @@ int main(int argc, char **argv) } syscall_skel->rodata->ignore_tgid = env.ignore_tgid; - + get_hostname(); + strcpy(syscall_skel->rodata->hostname,env.hostname); + err = syscall_image_bpf__load(syscall_skel); if (err) { fprintf(stderr, "Failed to load and verify BPF syscall skeleton\n"); @@ -881,6 +978,51 @@ int main(int argc, char **argv) } } + if(env.enable_mfutex){ + mfutex_skel = mfutex_bpf__open(); + if (!mfutex_skel) { + fprintf(stderr, "Failed to open BPF mfutex skeleton\n"); + return 1; + } + + mfutex_skel->rodata->ignore_tgid = env.ignore_tgid; + + err = mfutex_bpf__load(mfutex_skel); + if (err) { + fprintf(stderr, "Failed to load and verify BPF mfutex skeleton\n"); + goto cleanup; + } + + err = common_pin_map(&mfutex_ctrl_map,mfutex_skel->obj,"mfutex_ctrl_map",mfutex_ctrl_path); + if(err < 0){ + goto cleanup; + } + mfutexmap_fd = bpf_map__fd(mfutex_ctrl_map); + struct mfutex_ctrl init_value = {false,false,-1,-1}; + err = bpf_map_update_elem(mfutexmap_fd, &key, &init_value, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + goto cleanup; + } + + /* 附加跟踪点处理程序 */ + err = mfutex_attach(mfutex_skel); + if (err) { + fprintf(stderr, "Failed to attach BPF mfutex skeleton\n"); + goto cleanup; + } + + /* 设置环形缓冲区轮询 */ + //ring_buffer__new() API,允许在不使用额外选项数据结构下指定回调 + mfutex_rb = ring_buffer__new(bpf_map__fd(mfutex_skel->maps.mfutex_rb), print_mfutex, NULL, NULL); + if (!mfutex_rb) { + err = -1; + fprintf(stderr, "Failed to create mfutex ring buffer\n"); + goto cleanup; + } + printf("%-8s %-12s %-10s %-10s %-16s %-11s %-11s | %10s\n","TIME","LOCK_TYPE","LOCK_Addr","Holder","HoldTime","Last_Holder","Last_Delay","Wait_Proc"); + } + if(env.enable_keytime){ keytime_skel = keytime_image_bpf__open(); if (!keytime_skel) { @@ -1022,6 +1164,19 @@ int main(int argc, char **argv) } } + if(env.enable_mfutex){ + err = ring_buffer__poll(mfutex_rb, 2000); + /* Ctrl-C will cause -EINTR */ + if (err == -EINTR) { + err = 0; + break; + } + if (err < 0) { + printf("Error polling mfutex ring buffer: %d\n", err); + break; + } + } + if(env.enable_keytime){ err = ring_buffer__poll(keytime_rb, 0); /* Ctrl-C will cause -EINTR */ @@ -1066,6 +1221,11 @@ int main(int argc, char **argv) ring_buffer__free(lock_rb); lock_image_bpf__destroy(lock_skel); } + if(env.enable_mfutex){ + bpf_map__unpin(mfutex_ctrl_map, mfutex_ctrl_path); + ring_buffer__free(mfutex_rb); + mfutex_bpf__destroy(mfutex_skel); + } if(env.enable_keytime){ bpf_map__unpin(kt_ctrl_map, kt_ctrl_path); ksyms__free(ksyms); diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/test/test_mfutex.c b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/test/test_mfutex.c new file mode 100644 index 000000000..1c2476394 --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/test/test_mfutex.c @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include + +// 定义一个互斥锁 +pthread_mutex_t mutex; + +// 线程的执行函数 +void *thread_func(void *arg) { + int thread_id = *(int *)arg; + + printf("Thread %d: Trying to acquire the mutex\n", thread_id); + + // 尝试获取互斥锁 + pthread_mutex_lock(&mutex); + printf("Thread %d: Mutex acquired\n", thread_id); + + // 模拟线程持有互斥锁的操作 + sleep(3); + + // 释放互斥锁 + printf("Thread %d: Releasing the mutex\n", thread_id); + pthread_mutex_unlock(&mutex); + + return NULL; +} +pid_t get_tgid() { + pid_t pid = getpid(); + char path[256]; + snprintf(path, sizeof(path), "/proc/%d/status", pid); + + FILE *fp = fopen(path, "r"); + if (!fp) { + perror("打开 proc/pid/status 文件失败"); + return -1; + } + + char line[256]; + int tgid = -1,cpuid = -1; + + while (fgets(line, sizeof(line), fp)) { + if (strncmp(line, "Tgid:", 5) == 0) { + sscanf(line, "Tgid: %d", &tgid); + break; + } + } + + fclose(fp); + return tgid; +} +int main() { + pthread_t threads[10]; + int thread_ids[10]; + int i; + pid_t tgid = get_tgid(); + printf("TGID:%d\n",tgid); + int tmp; + scanf("%d",&tmp); + // 初始化互斥锁 + if (pthread_mutex_init(&mutex, NULL) != 0) { + fprintf(stderr, "Failed to initialize mutex\n"); + return EXIT_FAILURE; + } + + // 创建5个线程 + for (i = 0; i < 10; i++) { + thread_ids[i] = i + 1; + if (pthread_create(&threads[i], NULL, thread_func, &thread_ids[i]) != 0) { + fprintf(stderr, "Failed to create thread %d\n", i + 1); + return EXIT_FAILURE; + } + } + + // 等待所有线程结束 + for (i = 0; i < 10; i++) { + if (pthread_join(threads[i], NULL) != 0) { + fprintf(stderr, "Failed to join thread %d\n", i + 1); + return EXIT_FAILURE; + } + } + + // 销毁互斥锁 + pthread_mutex_destroy(&mutex); + + return EXIT_SUCCESS; +} diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/tools/Makefile b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/tools/Makefile index ae93b43c6..af43eaec8 100644 --- a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/tools/Makefile +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/tools/Makefile @@ -42,7 +42,7 @@ INCLUDES := -I$(OUTPUT) -I../../../libbpf/include/uapi -I$(dir $(VMLINUX)) -I$(L CFLAGS := -g -Wall ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) -APPS = lifecycle_image lock_image newlife_image keytime_image +APPS = lifecycle_image lock_image newlife_image keytime_image migrate_image CARGO ?= $(shell which cargo) ifeq ($(strip $(CARGO)),) diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/tools/migrate_image.bpf.c b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/tools/migrate_image.bpf.c new file mode 100644 index 000000000..bff87d0ac --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/tools/migrate_image.bpf.c @@ -0,0 +1,115 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: albert_xuu@163.com + +#include "vmlinux.h" +#include //包含了BPF 辅助函数 +#include +#include +#include "migrate_image.h" + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(key_size, sizeof(pid_t)); + __uint(value_size, sizeof(struct migrate_event)); + __uint(max_entries, 1024); +} migrate SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(key_size, sizeof(struct minfo_key)); + __uint(value_size, sizeof(struct per_migrate)); + __uint(max_entries, 1024); +} migrate_info SEC(".maps"); + +SEC("tracepoint/sched/sched_migrate_task") +int tracepoint_sched_migrate_task(struct trace_event_raw_sched_migrate_task *args){ + u64 time = bpf_ktime_get_ns();//当前转移时间点; + pid_t pid = args->pid; + struct migrate_event *migrate_event; + struct task_struct *task = (struct task_struct *)bpf_get_current_task(); + struct rq *orig_rq = BPF_CORE_READ(task,se.cfs_rq,rq); + struct cfs_rq *orig_cfs = BPF_CORE_READ(task,se.cfs_rq); + + bpf_printk("[se]:Pload_avg:%llu\tPutil_avg:%llu\n",BPF_CORE_READ(task,se.avg.load_avg),BPF_CORE_READ(task,se.avg.util_avg)); + bpf_printk("[rq]: nr_running :%d cpu_capacity : %ld cpu_capacity_orig : %ld\n", + BPF_CORE_READ(orig_rq,cpu),BPF_CORE_READ(orig_rq,nr_running), + BPF_CORE_READ(orig_rq,cpu_capacity),BPF_CORE_READ(orig_rq,cpu_capacity_orig)); + bpf_printk("Cload_avg:%ld\n",BPF_CORE_READ(orig_cfs,avg.runnable_avg)); + migrate_event = bpf_map_lookup_elem(&migrate,&pid); + if(!migrate_event){ + struct migrate_event migrate_event = {}; + struct per_migrate per_migrate = {}; + struct minfo_key mkey = {}; + mkey.pid = pid; + mkey.count = 1; + migrate_event.pid = pid; + migrate_event.prio = args->prio; + migrate_event.count = 1; + migrate_event.rear = 1; + per_migrate.time = time; + per_migrate.orig_cpu = args->orig_cpu; + per_migrate.dest_cpu = args->dest_cpu; + + per_migrate.cpu_capacity = BPF_CORE_READ(orig_rq,cpu_capacity); + per_migrate.cpu_capacity_orig = BPF_CORE_READ(orig_rq,cpu_capacity_orig); + per_migrate.cpu_load_avg = BPF_CORE_READ(orig_cfs,avg.runnable_avg); + + + per_migrate.pload_avg = BPF_CORE_READ(task,se.avg.load_avg);//进程的量化负载; + per_migrate.putil_avg = BPF_CORE_READ(task,se.avg.util_avg);//进程的实际算力; + per_migrate.mem_usage = BPF_CORE_READ(task,mm,total_vm) << PAGE_SHIFT; + + + per_migrate.read_bytes = BPF_CORE_READ(task,ioac.read_bytes); + per_migrate.write_bytes = BPF_CORE_READ(task,ioac.write_bytes); + + per_migrate.context_switches = BPF_CORE_READ(task,nvcsw) + BPF_CORE_READ(task,nivcsw); + // per_migrate.runtime = BPF_CORE_READ(task,se.sum_exec_runtime); + bpf_map_update_elem(&migrate_info, &mkey, &per_migrate, BPF_ANY); + bpf_map_update_elem(&migrate, &pid, &migrate_event, BPF_ANY); + } + /*&& (migrate_event->migrate_info + migrate_event->count) < (migrate_event->migrate_info + MAX_MIGRATE)*/ + else if(migrate_event->count>0 && migrate_event->countcount++; + mkey.pid = pid; + mkey.count = migrate_event->count; + per_migrate.time = time; + per_migrate.orig_cpu = args->orig_cpu; + per_migrate.dest_cpu = args->dest_cpu; + + per_migrate.cpu_capacity = BPF_CORE_READ(orig_rq,cpu_capacity); + per_migrate.cpu_capacity_orig = BPF_CORE_READ(orig_rq,cpu_capacity_orig); + per_migrate.cpu_load_avg = BPF_CORE_READ(orig_cfs,avg.runnable_avg); + + per_migrate.pload_avg = BPF_CORE_READ(task,se.avg.load_avg);//进程的量化负载; + per_migrate.putil_avg = BPF_CORE_READ(task,se.avg.util_avg);//进程的实际算力; + per_migrate.mem_usage = BPF_CORE_READ(task,mm,total_vm) << PAGE_SHIFT; + + + per_migrate.read_bytes = BPF_CORE_READ(task,ioac.read_bytes); + per_migrate.write_bytes = BPF_CORE_READ(task,ioac.write_bytes); + + per_migrate.context_switches = BPF_CORE_READ(task,nvcsw) + BPF_CORE_READ(task,nivcsw); + // per_migrate.runtime = BPF_CORE_READ(task,se.sum_exec_runtime); + + bpf_map_update_elem(&migrate_info, &mkey, &per_migrate, BPF_ANY); + } + return 0; +} \ No newline at end of file diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/tools/migrate_image.c b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/tools/migrate_image.c new file mode 100644 index 000000000..af2958210 --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/tools/migrate_image.c @@ -0,0 +1,256 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: albert_xuu@163.com + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "migrate_image.skel.h" +#include "migrate_image.h" +#include "trace_helpers.h" + +#define warn(...) fprintf(stderr, __VA_ARGS__) + +static volatile bool exiting = false; +struct migrate_image_bpf *skel; +// static struct env { +// int pid; +// int time; +// int cpu_id; +// int stack_count; +// bool set_stack; +// bool enable_cs; +// } env = { +// .pid = 0, +// .time = 0, +// .cpu_id = 0, +// .stack_count = 0, +// .set_stack = false, +// .enable_cs = false, +// }; + +static struct ksyms *ksyms = NULL; + +const char argp_program_doc[] ="Trace process to get process life cycle image.\n"; + +// static const struct argp_option opts[] = { +// { "pid", 'p', "PID", 0, "Process ID to trace" }, +// { "cpuid", 'C', "CPUID", 0, "Set For Tracing Process 0(other processes don't need to set this parameter)" }, +// { "time", 't', "TIME-SEC", 0, "Max Running Time(0 for infinite)" }, +// { "cs-reason", 'r', NULL, 0, "Process context switch reasons annotation" }, +// { "stack", 's', "STACK-COUNT", 0, "The number of kernel stacks printed when the process is under the CPU" }, +// { NULL, 'h', NULL, OPTION_HIDDEN, "show the full help" }, +// {}, +// }; + +// static error_t parse_arg(int key, char *arg, struct argp_state *state) +// { +// long pid; +// long cpu_id; +// long stack; +// switch (key) { +// case 'p': +// errno = 0; +// pid = strtol(arg, NULL, 10); +// if (errno || pid < 0) { +// warn("Invalid PID: %s\n", arg); +// // 调用argp_usage函数,用于打印用法信息并退出程序 +// argp_usage(state); +// } +// env.pid = pid; +// break; +// case 'C': +// cpu_id = strtol(arg, NULL, 10); +// if(cpu_id < 0){ +// warn("Invalid CPUID: %s\n", arg); +// argp_usage(state); +// } +// env.cpu_id = cpu_id; +// break; +// case 't': +// env.time = strtol(arg, NULL, 10); +// if(env.time) alarm(env.time); +// break; +// case 'r': +// env.enable_cs = true; +// break; +// case 's': +// stack = strtol(arg, NULL, 10); +// if (stack < 0) { +// warn("Invalid STACK-COUNT: %s\n", arg); +// // 调用argp_usage函数,用于打印用法信息并退出程序 +// argp_usage(state); +// } +// env.stack_count = stack; +// env.set_stack = true; +// break; +// case 'h': +// argp_state_help(state, stderr, ARGP_HELP_STD_HELP); +// break; +// default: +// return ARGP_ERR_UNKNOWN; +// } + +// return 0; +// } + +static void sig_handler(int sig) +{ + exiting = true; +} + + +static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) +{ + return vfprintf(stderr, format, args); +} + +static int migrate_print(){ + time_t now = time(NULL);// 获取当前时间 + struct tm *localTime = localtime(&now);// 将时间转换为本地时间结构 + printf("\nTime: %02d:%02d:%02d\n",localTime->tm_hour, localTime->tm_min, localTime->tm_sec); + printf("---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n"); + int err,migrate_fd =bpf_map__fd(skel->maps.migrate),migrate_info_fd =bpf_map__fd(skel->maps.migrate_info); + + pid_t lookup_key = -1 ,next_key; + struct migrate_event migrate_event; + while(!bpf_map_get_next_key(migrate_fd, &lookup_key, &next_key)){//遍历打印hash map + err = bpf_map_lookup_elem(migrate_fd,&next_key,&migrate_event); + if (err < 0) { + fprintf(stderr, "failed to lookup infos2: %d\n", err); + return -1; + } + if(migrate_event.count <= migrate_event.rear) { + lookup_key = next_key; + continue; + } + u64 last_time_stamp = 0; + printf("\npid:%d\tprio:%d\tcount:%d\trear:%d\n",migrate_event.pid,migrate_event.prio,migrate_event.count,migrate_event.rear); + printf("---------------------------------------------------------------------------------\n"); + for(int i=migrate_event.rear;i<=migrate_event.count;i++){ + struct per_migrate migrate_info; + struct minfo_key mkey; + mkey.pid = migrate_event.pid; + mkey.count = i; + err = bpf_map_lookup_elem(migrate_info_fd,&mkey,&migrate_info); + if (err < 0) { + fprintf(stderr, "failed to lookup infos err %d mkey_pid: %d mkey_count: %d\n", err,mkey.pid,i); + continue; + } + printf("time_stamp:%llu\t%d->%d \t PROC_LOAD:%llu \t PROC_UTIL:%llu\t", + migrate_info.time,migrate_info.orig_cpu,migrate_info.dest_cpu,migrate_info.pload_avg,migrate_info.putil_avg); + printf("CPU_LOAD: %ld \t Cpu_Capacity:[%ld:%ld] \t ",migrate_info.cpu_load_avg,migrate_info.cpu_capacity,migrate_info.cpu_capacity_orig); + printf("mmem_usage:%llu kb \t\t read:%llu kb \t\t wite:%llu kb \t\t context_switch:%llu\t", + migrate_info.mem_usage/1024,migrate_info.read_bytes/1024,migrate_info.write_bytes/1024, + migrate_info.context_switches); + + if(i==migrate_event.rear && last_time_stamp == 0) { + last_time_stamp = migrate_info.time; + printf("delay: /\n"); + }else{ + printf("delay: %d us\n",(migrate_info.time - last_time_stamp)/1000); + last_time_stamp = migrate_info.time; + } + bpf_map_delete_elem(migrate_info_fd,&mkey);//删除已经打印了的数据 + + } + migrate_event.rear = migrate_event.count + 1; + bpf_map_update_elem(migrate_fd,&next_key,&migrate_event,BPF_ANY); + lookup_key = next_key; + } + printf("---------------------------------------------------------------------------------\n\n"); + return 0; +} + +int main(int argc, char **argv) +{ + struct ring_buffer *rb = NULL; + int err; + // static const struct argp argp = { + // .options = opts, + // .parser = parse_arg, + // .doc = argp_program_doc, + // }; + + // err = argp_parse(&argp, argc, argv, 0, NULL, NULL); + // if (err) + // return err; + + + libbpf_set_strict_mode(LIBBPF_STRICT_ALL); + /* 设置libbpf错误和调试信息回调 */ + libbpf_set_print(libbpf_print_fn); + + /* 更干净地处理Ctrl-C + SIGINT:由Interrupt Key产生,通常是CTRL+C或者DELETE。发送给所有ForeGround Group的进程 + SIGTERM:请求中止进程,kill命令发送 + */ + signal(SIGINT, sig_handler); + signal(SIGTERM, sig_handler); + signal(SIGALRM,sig_handler); + + /* 打开BPF应用程序 */ + skel = migrate_image_bpf__open(); + if (!skel) { + fprintf(stderr, "Failed to open BPF skeleton\n"); + return 1; + } + + /* 加载并验证BPF程序 */ + err = migrate_image_bpf__load(skel); + if (err) { + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); + goto cleanup; + } + + ksyms = ksyms__load(); + if (!ksyms) { + fprintf(stderr, "failed to load kallsyms\n"); + goto cleanup; + } + + /* 附加跟踪点处理程序 */ + err = migrate_image_bpf__attach(skel); + if (err) { + fprintf(stderr, "Failed to attach BPF skeleton\n"); + goto cleanup; + } + + /* 处理事件 */ + while (!exiting) { + sleep(1); + err = migrate_print(); + if (err == -EINTR) { + err = 0; + break; + } + if (err < 0) { + printf("Error: %d\n", err); + break; + } + } + +/* 卸载BPF程序 */ +cleanup: + // ring_buffer__free(rb); + migrate_image_bpf__destroy(skel); + return err < 0 ? -err : 0; +} diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/tools/migrate_image.h b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/tools/migrate_image.h new file mode 100644 index 000000000..685cf0374 --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/tools/migrate_image.h @@ -0,0 +1,45 @@ +#include +#include + +typedef unsigned long long u64; +typedef unsigned int u32; +/*----------------------------------------------*/ +/* migrate_event结构体 */ +/*----------------------------------------------*/ +#define MAX_MIGRATE 1024 +#define PAGE_SHIFT 13 +// #define ARRY_OVERFLOW -1 +struct minfo_key{ + pid_t pid; + int count; +}; +struct per_migrate{//每次迁移,记录该次迁移信息; + u64 time; + u32 orig_cpu; + u32 dest_cpu; + + u64 orig_cpu_load;//cfs->avg.runnale_avg 就绪队列所有调度实体;量化负载总和 + u64 dest_cpu_load; + int cpu_capacity;//计算机算力 + int cpu_capacity_orig;//额定算力 + u64 cpu_load_avg; + u64 pload_avg; + u64 putil_avg; + + int on_cpu; + u64 mem_usage; + u64 read_bytes; + u64 write_bytes; + // u64 syscr; + // u64 syscw; + u64 context_switches; + u64 runtime; +}; +//每个进程的迁移信息; +struct migrate_event{ + int erro; + pid_t pid; + int prio; + int count,rear;//迁移频率 + //struct per_migrate *migrate_info;//该进程每次迁移信息; +}; diff --git a/eBPF_Supermarket/Filesystem_Subsystem/fast_fuse/difuse/README.md b/eBPF_Supermarket/Filesystem_Subsystem/fast_fuse/difuse/README.md new file mode 100644 index 000000000..1e0eb6f70 --- /dev/null +++ b/eBPF_Supermarket/Filesystem_Subsystem/fast_fuse/difuse/README.md @@ -0,0 +1,32 @@ +# DIFUSE + +## 代码结构 + +``` +. +├── README.md +├── src +│ ├── difuse.c 源码 +│ ├── Makefile +│ └── mountpoints 挂载目录 +└── test + └── test.sh 测试脚本 +``` + + + +## Build + +在`src`目录下执行`make` + +```bash +cd src +make +``` + +在`test`目录下执行`test`脚本 + +```bash +cd test +./test.sh +``` \ No newline at end of file diff --git a/eBPF_Supermarket/Filesystem_Subsystem/fast_fuse/difuse/src/Makefile b/eBPF_Supermarket/Filesystem_Subsystem/fast_fuse/difuse/src/Makefile new file mode 100644 index 000000000..aa3c52834 --- /dev/null +++ b/eBPF_Supermarket/Filesystem_Subsystem/fast_fuse/difuse/src/Makefile @@ -0,0 +1,29 @@ +# Makefile for FUSE difuse example + +# Compiler +CC = gcc + +# Compiler flags +CFLAGS = -Wall `pkg-config fuse3 --cflags` + +# Linker flags +LDFLAGS = `pkg-config fuse3 --libs` + +# Source files +SRCS = difuse.c + +# Output executable +TARGET = difuse + +# Build target +all: $(TARGET) + +# Rule to build the target +$(TARGET): $(SRCS) + $(CC) $(CFLAGS) -o $(TARGET) $(SRCS) $(LDFLAGS) + +# Clean rule +clean: + rm -f $(TARGET) + +.PHONY: all clean diff --git a/eBPF_Supermarket/Filesystem_Subsystem/fast_fuse/difuse/src/difuse.c b/eBPF_Supermarket/Filesystem_Subsystem/fast_fuse/difuse/src/difuse.c new file mode 100644 index 000000000..f1a6d29a4 --- /dev/null +++ b/eBPF_Supermarket/Filesystem_Subsystem/fast_fuse/difuse/src/difuse.c @@ -0,0 +1,548 @@ +#define FUSE_USE_VERSION 31 + +#include +#include +#include +#include +#include +#include +#include +#include + +/*相关数据结构*/ + +#define FILE_TYPE 1 +#define DIRECTORY_TYPE 2 +#define MAX_INODES 1000 //最大 inode 数量 +#define HASH_SIZE 1024 +#define CHUNK_SIZE 4096 // 数据块的大小 + +uint32_t next_ino = 1; + +struct dfs_data +{ + char *data; + size_t size; + struct dfs_data *next; +}; + +static struct dfs_data *allocate_data_block() +{ + struct dfs_data *new_data = (struct dfs_data *)malloc(sizeof(struct dfs_data)); + new_data->data = (char *)malloc(CHUNK_SIZE); + new_data->next = NULL; + return new_data; +} + +struct dfs_inode +{ + uint32_t ino; // inode编号 + int size; // 文件大小 + int dir_cnt; // 目录项数量 + struct dfs_data *data_pointer; // 数据块指针 + time_t atime; // 最后访问时间 + time_t mtime; // 最后修改时间 + struct dfs_inode *prev; + struct dfs_inode *next; +}; + + +struct dfs_dentry +{ + char fname[255]; + int ftype; + struct dfs_dentry *parent; + struct dfs_dentry *brother; + struct dfs_dentry *child; + struct dfs_inode *inode; //指向对应的inode + struct dfs_dentry *prev; //LRU 链表前驱指针 + struct dfs_dentry *next; //LRU 链表后继指针 +}; + +struct dfs_dentry *root; //根节点 +struct dfs_dentry *lru_head = NULL; //LRU 链表头 +struct dfs_dentry *lru_tail = NULL; //LRU 链表尾 +struct dfs_dentry *hash_table[HASH_SIZE]; //哈希表 + +/*缓存管理*/ + +static unsigned int hash(const char *path) +{ + unsigned int hash = 0; + while (*path) + { + hash = (hash << 5) + *path++; + } + return hash % HASH_SIZE; +} + +static void lru_remove(struct dfs_dentry *dentry) +{ + if (dentry->prev) + { + dentry->prev->next = dentry->next; + } + else + { + lru_head = dentry->next; + } + if (dentry->next) + { + dentry->next->prev = dentry->prev; + } + else + { + lru_tail = dentry->prev; + } +} + +static void lru_insert(struct dfs_dentry *dentry) +{ + dentry->next = lru_head; + dentry->prev = NULL; + if (lru_head) + { + lru_head->prev = dentry; + } + lru_head = dentry; + if (!lru_tail) + { + lru_tail = dentry; + } +} + +static void lru_access(struct dfs_dentry *dentry) +{ + lru_remove(dentry); + lru_insert(dentry); +} + +static void lru_evict() +{ + if (lru_tail) + { + struct dfs_dentry *evict = lru_tail; + lru_remove(evict); + unsigned int index = hash(evict->fname); + hash_table[index] = NULL; + free(evict->inode); + free(evict); + } +} + +/*过程函数*/ +static struct dfs_inode *new_inode(int size, int dir_cnt) +{ + struct dfs_inode *inode = (struct dfs_inode *)malloc(sizeof(struct dfs_inode)); + inode->ino = next_ino++; + inode->size = size; + inode->dir_cnt = dir_cnt; + inode->data_pointer = NULL; + inode->prev = NULL; + inode->next = NULL; + return inode; +} + +static struct dfs_dentry *new_dentry(char *fname, int ftype, struct dfs_dentry *parent, struct dfs_inode *inode) +{ + struct dfs_dentry *dentry = (struct dfs_dentry *)malloc(sizeof(struct dfs_dentry)); + strcpy(dentry->fname, fname); + dentry->inode = inode; + dentry->brother = NULL; + dentry->parent = parent; + dentry->child = NULL; + dentry->ftype = ftype; + dentry->prev = NULL; + dentry->next = NULL; + return dentry; +} + +void add_child_dentry(struct dfs_dentry *parent, struct dfs_dentry *child) +{ + child->brother = parent->child; + parent->child = child; +} + +static int remove_child_dentry(struct dfs_dentry *parent, struct dfs_dentry *child) +{ + struct dfs_dentry *prev_child = NULL; + struct dfs_dentry *cur_child = parent->child; + + while (cur_child != NULL && cur_child != child) + { + prev_child = cur_child; + cur_child = cur_child->brother; + } + if (cur_child == NULL) + return 0; + + if (prev_child == NULL) + parent->child = cur_child->brother; + else prev_child->brother = cur_child->brother; + return 1; +} + +struct dfs_dentry *traverse_path(struct dfs_dentry *start_dentry, const char *path, int ftype, int create) +{ + struct dfs_dentry *dentry = start_dentry; + char *path_copy = strdup(path); + char *token = strtok(path_copy, "/"); + + while (token != NULL) + { + struct dfs_dentry *child = dentry->child; + while (child != NULL && strcmp(child->fname, token) != 0) + { + child = child->brother; + } + + if (child == NULL) + { + if (create) + { + struct dfs_inode *new_inodes = new_inode(0, 0); // 创建新的 inode + child = new_dentry(token, ftype, dentry, new_inodes); // 创建新的目录项 + add_child_dentry(dentry, child); // 将新目录项添加到父目录项的子目录列表中 + } + else + { + free(path_copy); + return NULL; + } + } + + dentry = child; + token = strtok(NULL, "/"); + } + + free(path_copy); + return dentry; +} + +struct dfs_dentry *look_up(struct dfs_dentry *dentrys, const char *path) +{ + return traverse_path(dentrys, path, 0, 0); +} + +struct dfs_dentry *lookup_or_create_dentry(const char *path, struct dfs_dentry *start_dentry, int ftype) +{ + unsigned int index = hash(path); + struct dfs_dentry *dentry = hash_table[index]; + + if (dentry) + { + lru_access(dentry); + return dentry; + } + + dentry = traverse_path(start_dentry, path, ftype, 1); + if (dentry) + { + lru_insert(dentry); + hash_table[index] = dentry; + if (next_ino > MAX_INODES) + { + lru_evict(); + } + } + + return dentry; +} + + +/*功能函数*/ +static int di_unlink(const char *path) +{ + struct dfs_dentry *dentry = look_up(root, path); + + if (dentry == NULL) + return -ENOENT; + if (dentry->ftype != FILE_TYPE) + return -EISDIR; + + if (remove_child_dentry(dentry->parent, dentry)) + { + lru_remove(dentry); + unsigned int index = hash(dentry->fname); + hash_table[index] = NULL; + free(dentry->inode); + free(dentry); + return 0; + } + return -ENOENT; +} + +static int di_rmdir(const char *path) +{ + struct dfs_dentry *dentry = look_up(root, path); + + if (dentry == NULL) + return -ENOENT; + if (dentry->ftype != DIRECTORY_TYPE) + return -ENOTDIR; + if (dentry->child != NULL) + return -ENOTEMPTY; + + // 移除子目录项 + if (remove_child_dentry(dentry->parent, dentry)) + { + lru_remove(dentry); + unsigned int index = hash(dentry->fname); + hash_table[index] = NULL; + free(dentry->inode); + free(dentry); + return 0; + } + return -ENOENT; +} + +static int di_utimens(const char *path, const struct timespec ts[2], struct fuse_file_info *fi) +{ + (void)fi; + struct dfs_dentry *dentry = look_up(root, path); + if (dentry == NULL) + { + return -ENOENT; + } + + // 设置文件的时间戳 + dentry->inode->atime = ts[0].tv_sec; // 访问时间 + dentry->inode->mtime = ts[1].tv_sec; // 修改时间 + + return 0; +} + + +static int di_mkdir(const char *path, mode_t mode) +{ + (void)mode; + struct dfs_dentry *dentry = lookup_or_create_dentry(path, root, DIRECTORY_TYPE); + if (dentry == NULL) + { + return -ENOENT; + } + + return 0; +} + +static int dfs_create(const char *path, mode_t mode, struct fuse_file_info *fi) +{ + (void)mode; + (void)fi; + struct dfs_dentry *existing = look_up(root, path); + if (existing != NULL) + { + return -EEXIST; // 文件已存在,返回错误 + } + struct dfs_dentry *dentry = lookup_or_create_dentry(path, root, FILE_TYPE); + if (dentry == NULL) + { + return -ENOENT; + } + + return 0; +} + +static int di_getattr(const char *path, struct stat *di_stat, struct fuse_file_info *fi) +{ + (void)fi; + memset(di_stat, 0, sizeof(struct stat)); + + struct dfs_dentry *dentry = look_up(root, path); + if (dentry == NULL) + return -ENOENT; + + if (dentry->ftype == DIRECTORY_TYPE) + { + di_stat->st_mode = S_IFDIR | 0755; + di_stat->st_nlink = 2; + } + else if (dentry->ftype == FILE_TYPE) + { + di_stat->st_mode = S_IFREG | 0644; + di_stat->st_nlink = 1; + di_stat->st_size = dentry->inode->size; + } + + di_stat->st_atime = dentry->inode->atime; // 最后访问时间 + di_stat->st_mtime = dentry->inode->mtime; // 最后修改时间 + + return 0; +} + + +/*遍历目录项*/ +static int di_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags flags) +{ + (void)fi; + (void)offset; + (void)flags; + struct dfs_dentry *dentry = look_up(root, path); + + if (dentry == NULL) + return -ENOENT; + + if (dentry->ftype != DIRECTORY_TYPE) + return -ENOTDIR; + + filler(buf, ".", NULL, 0, 0); + filler(buf, "..", NULL, 0, 0); + + struct dfs_dentry *child = dentry->child; + while (child != NULL) + { + filler(buf, child->fname, NULL, 0, 0); + child = child->brother; + } + + return 0; +} + +static int di_open(const char *path, struct fuse_file_info *fi) +{ + struct dfs_dentry *dentry = look_up(root, path); + + if (dentry == NULL) + return -ENOENT; + + if (dentry->ftype != FILE_TYPE) + return -EISDIR; + + return 0; +} + +static int di_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) +{ + struct dfs_dentry *dentry = look_up(root, path); + + if (dentry == NULL) + return -ENOENT; + + if (dentry->ftype != FILE_TYPE) + return -EISDIR; + + struct dfs_inode *inode = dentry->inode; + size_t file_size = inode->size; + + if (offset >= file_size) + return 0; + + if (offset + size > file_size) + size = file_size - offset; + + size_t bytes_read = 0; + struct dfs_data *data_block = inode->data_pointer; + + // 遍历数据块,处理偏移和读取 + while (data_block != NULL && bytes_read < size) + { + if (offset >= CHUNK_SIZE) + { + offset -= CHUNK_SIZE; + data_block = data_block->next; + continue; + } + + size_t to_read = CHUNK_SIZE - offset; + if (to_read > size - bytes_read) + to_read = size - bytes_read; + + memcpy(buf + bytes_read, data_block->data + offset, to_read); + bytes_read += to_read; + offset = 0; // 只有第一个块需要处理 offset,之后的块直接从头开始 + + data_block = data_block->next; + } + + return bytes_read; +} + + +static int di_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) +{ + (void)fi; + struct dfs_dentry *dentry = look_up(root, path); + + if (dentry == NULL) return -ENOENT; + if (dentry->ftype != FILE_TYPE) return -EISDIR; + + struct dfs_inode *inode = dentry->inode; + struct dfs_data *data_block = inode->data_pointer; + + if (data_block == NULL) + { + data_block = allocate_data_block(); + inode->data_pointer = data_block; + } + + size_t bytes_written = 0; + size_t total_offset = offset; + + while (data_block != NULL && total_offset >= CHUNK_SIZE) + { + total_offset -= CHUNK_SIZE; + if (data_block->next == NULL) + { + data_block->next = allocate_data_block(); + } + data_block = data_block->next; + } + + while (bytes_written < size) + { + size_t space_in_block = CHUNK_SIZE - total_offset; + size_t to_write = size - bytes_written; + + if (to_write > space_in_block) to_write = space_in_block; + + memcpy(data_block->data + total_offset, buf + bytes_written, to_write); + + total_offset = 0; + bytes_written += to_write; + data_block->size += to_write; + + if (bytes_written < size && data_block->next == NULL) + { + data_block->next = allocate_data_block(); + } + data_block = data_block->next; + } + + if (offset + bytes_written > inode->size) + { + inode->size = offset + bytes_written; + } + + return bytes_written; +} + + +static void *di_init(struct fuse_conn_info *conn, struct fuse_config *cfg) +{ + (void)conn; + + // 创建并初始化根目录的 inode 和 dentry + struct dfs_inode *root_inode = new_inode(0, 0); + root = new_dentry("/", DIRECTORY_TYPE, NULL, root_inode); + + return 0; +} + +static struct fuse_operations difs_ops = { + .init = di_init, + .readdir = di_readdir, + .getattr = di_getattr, + .open = di_open, + .read = di_read, + .write = di_write, + .mkdir = di_mkdir, + .create = dfs_create, + .utimens = di_utimens, + .unlink = di_unlink, + .rmdir = di_rmdir, +}; + +int main(int argc, char *argv[]) +{ + return fuse_main(argc, argv, &difs_ops, NULL); +} \ No newline at end of file diff --git a/eBPF_Supermarket/Filesystem_Subsystem/fast_fuse/difuse/test/test.sh b/eBPF_Supermarket/Filesystem_Subsystem/fast_fuse/difuse/test/test.sh new file mode 100755 index 000000000..f833d0764 --- /dev/null +++ b/eBPF_Supermarket/Filesystem_Subsystem/fast_fuse/difuse/test/test.sh @@ -0,0 +1,87 @@ +#!/bin/bash + +# 挂载点目录 +MOUNT_POINT="../src/mountpoints" +SRC_DIR="../src" + +# FUSE 可执行文件 +FUSE_EXEC="../src/difuse" + +echo "Compiling FUSE filesystem..." +make -C "$SRC_DIR" clean +make -C "$SRC_DIR" all + +# 检查编译是否成功 +if [ ! -f "$FUSE_EXEC" ]; then + echo "Compilation failed. Exiting." + exit 1 +fi + +# 创建挂载点目录(如果不存在) +if [ ! -d "$MOUNT_POINT" ]; then + mkdir -p "$MOUNT_POINT" +fi + +# 挂载 FUSE 文件系统(前台运行并显示调试信息) +echo "Mounting FUSE filesystem..." +$FUSE_EXEC -f -d "$MOUNT_POINT" & +FUSE_PID=$! +sleep 2 # 等待文件系统完全挂载 + +# 确保脚本退出时卸载文件系统 +trap "fusermount -u $MOUNT_POINT" EXIT + +# 创建目录 +mkdir $MOUNT_POINT/dir1 +mkdir $MOUNT_POINT/dir2 + +# 创建文件 +touch $MOUNT_POINT/dir1/file1 +touch $MOUNT_POINT/dir1/file2 +touch $MOUNT_POINT/dir2/file3 + +# 验证结构 +echo "创建的目录和文件结构:" +ls -l $MOUNT_POINT + +# 进行写入测试 +echo "测试文件内容写入..." +echo "Hello, this is a write test!" > $MOUNT_POINT/dir1/file1 + +# 进行读取测试 +echo "读取文件内容:" +cat $MOUNT_POINT/dir1/file1 + +# 验证写入是否正确 +FILE_CONTENT=$(cat $MOUNT_POINT/dir1/file1) +if [ "$FILE_CONTENT" == "Hello, this is a write test!" ]; then + echo "文件写入和读取成功。" +else + echo "文件写入或读取失败!" +fi + +# 删除文件 +echo "删除文件 $MOUNT_POINT/dir1/file1 和 $MOUNT_POINT/dir2/file3..." +rm $MOUNT_POINT/dir1/file1 +rm $MOUNT_POINT/dir2/file3 + +# 验证文件删除 +echo "验证文件删除后的结构:" +ls -l $MOUNT_POINT/dir1 +ls -l $MOUNT_POINT/dir2 + +# 尝试删除非空目录 +echo "尝试删除非空目录 $MOUNT_POINT/dir1 (应失败)..." +rmdir $MOUNT_POINT/dir1 || echo "无法删除非空目录 $MOUNT_POINT/dir1, 操作成功。" + +# 删除剩余文件 +rm $MOUNT_POINT/dir1/file2 + +# 删除空目录 +echo "删除空目录 $MOUNT_POINT/dir1 和 $MOUNT_POINT/dir2..." +rmdir $MOUNT_POINT/dir1 +rmdir $MOUNT_POINT/dir2 + +# 验证目录删除 +echo "验证目录删除后的结构:" +ls -l $MOUNT_POINT diff --git a/eBPF_Supermarket/Filesystem_Subsystem/fs_watcher/include/fs_watcher.h b/eBPF_Supermarket/Filesystem_Subsystem/fs_watcher/include/fs_watcher.h index b52ab6bbb..194e1ceb8 100644 --- a/eBPF_Supermarket/Filesystem_Subsystem/fs_watcher/include/fs_watcher.h +++ b/eBPF_Supermarket/Filesystem_Subsystem/fs_watcher/include/fs_watcher.h @@ -1,11 +1,10 @@ #ifndef __FS_WATCHER_H #define __FS_WATCHER_H - +/*open*/ #define path_size 256 +#define TASK_COMM_LEN 16 -#define TASK_COMM_LEN 256 -/*open*/ struct event_open { int pid_; char path_name_[path_size]; diff --git a/eBPF_Supermarket/Memory_Subsystem/README.md b/eBPF_Supermarket/Memory_Subsystem/README.md deleted file mode 100644 index 2175886a4..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/README.md +++ /dev/null @@ -1,442 +0,0 @@ -# mem_watcher - -## mem_watcher介绍 - -memwatcher是一款基于eBPF的内存监测工具,其设计的目的就是为了可以让用户能够在主机环境上可以快捷的检测到Linux内存的详细信息。 -通过高效的数据收集和精准的监控能力,帮助用户可以有效的监控主机内存情况。 -使用了eBPF(Extended Berkeley Packet Filter)来监控内核中的几个关键事件,主要涉及到内存管理方面的几个功能: -第一:`get_page_from_freelist:` - -- 监控页面分配过程中的某些关键参数。 -- 捕获了页面分配时的一些重要信息,比如所用的gfp_mask、order、alloc_flags等。 - -第二:`shrink_page_list:` - -- 监控页面收缩(shrink)过程中的一些参数。 -- 捕获了页面收缩时的关键参数,如nr_to_reclaim、nr_reclaimed等。 - -第三:`finish_task_switch:` - -- 监控进程切换完成时的内存相关参数。 -- 捕获了进程切换时的一些内存使用情况,如pid、vsize、rss等。 - -第四:`get_page_from_freelist:` - -- 监控页面分配的另一方面,可能是为了提供更全面的内存分配情况。 -- 捕获了更多与页面分配相关的内存统计信息,如anon_inactive、file_inactive等。 - -通过收集这些信息,可以用于监控系统内存的使用情况、诊断内存相关的性能问题以及进行性能优化。 - -第五: - -内存泄露是指程序在申请内存后,无法释放或未能及时释放,从而导致系统内存的不断消耗,最终导致程序的崩溃或性能的下降。这种现象一般发生在程序中有大量的动态内存分配和释放操作,如果程序员忘记或者疏忽了释放内存,就有可能导致内存泄露。 - -eBPF 提供了一种高效的机制来监控和追踪系统级别的事件,包括内存的分配和释放。通过 eBPF,可以跟踪内存分配和释放的请求,并收集每次分配的调用堆栈。然后,分析这些信息,找出执行了内存分配但未执行释放操作的调用堆栈,这有助于程序员找出导致内存泄漏的源头。 - ------- -## 背景意义 - -内存子系统是Linux内核中是一个相对复杂的模块,内核中几乎所有的数据、缓存、程序指令都有内存模块参与管理。在内存不足的情况下,这些数据就会被存储在磁盘的交换空间中,但是磁盘的处理速度相对与内存非常慢,当内存和磁盘频繁进行数据交换时,缓慢的磁盘读写速度非常影响系统性能。系统可能因内存不足从而终止那些占用内存较大的进程,导致程序运行故障。因此准确的监控分析内存性能状况就变得非常重要。 - -目前,传统的内存性能分析工具通过读取proc文件系统下的数据,经过简单的处理后呈现给用户,方便管理人员随时了解系统状况。然而这些工具的灵活性非常差,单个工具输出的信息有限。系统维护人员在分析性能问题时常常需要借助多个工具才能进行。步骤繁琐且工具本身对系统性能也有一定影响。随着ebpf技术在系统可观测上的发展,利于ebpf非侵入式的数据获取方式已被大多数企业、高校认可并取得了一定的研究成果。ebpf的可编程性可以让管理人员灵活的获取系统的运行数据,而且在数据的提取粒度上有着传统工具无法比拟的优势。现在,ebpf作为Linux内核顶级子系统,已经成为实现Linux内核可观测性、网络和内核安全的理想技术。 - ------- - -# mem_watcher据体代码的分析。 - -## procstat - -### 采集信息: - -| 参数 | 含义 | -| -------- | ------------------------ | -| vsize | 进程使用的虚拟内存 | -| size | 进程使用的最大物理内存 | -| rssanon | 进程使用的匿名页面 | -| rssfile | 进程使用的文件映射页面 | -| rssshmem | 进程使用的共享内存页面 | -| vswap | 进程使用的交换分区大小 | -| vdata | 进程使用的私有数据段大小 | -| vpte | 进程页表大小 | -| vstk | 进程用户栈大小 | -### 功能 - -主要是用于跟踪用户空间进程的内存使用情况。具体功能是在用户空间进程切换时,记录切换前进程的内存信息,并将这些信息写入环形缓冲区中。 - -### 分析 - -`BPF_KPROBE`标记了一个内核探针函数,挂载在`finish_task_switch`结束时,这个函数用于捕获进程切换事件。 -内核探针函数首先通过`bpf_get_current_pid_tgid()`获取当前进程的PID,然后通过prev参数获取切换前的进程结构体指针。 -然后判断当前进程的PID是否是要跟踪的用户进程的PID,如果是则直接返回,不做任何处理。 -接着获取切换前进程的PID,并判断是否是要跟踪的用户进程的PID,如果是则直接返回。 -如果不是要跟踪的进程,则从`last_val`哈希表中查找上一次记录的内存状态。 -如果没有找到,则更新`last_val`哈希表,将该进程的PID作为键,将值设置为1。 -如果找到了上一次的记录,并且上一次的值与当前值相同,则说明内存状态没有变化,直接返回。 -如果上一次的记录与当前值不同,则说明内存状态发生了变化,需要记录内存信息。 -通过`bpf_ringbuf_reserve()`函数在环形缓冲区中分配空间,并填充内存事件信息。 -最后通过`bpf_ringbuf_submit()`函数将填充好的内存事件信息提交到环形缓冲区中。 -### 载点及挂载原因 - -挂载点:finish_task_switch - -挂载原因: - -首先,获取进程级别内存使用信息首先需要获取到进程的task_struct结构体,其中在mm_struct成员中存在一个保存进程当前内存使用状态的数组结构,因此有关进程的大部分内存使用信息都可以通过这个数组获得。其次,需要注意函数的插入点,插入点的选取关系到数据准确性是否得到保证,而在进程的内存申请,释放,规整等代码路径上都存在页面状态改变,但是数量信息还没有更新的相关结构中的情况,如果插入点这两者中间,数据就会和实际情况存在差异,所有在确保可以获取到进程PCB的前提下,选择在进程调度代码路径上考虑。而finish_task_switch函数是新一个进程第一个执行的函数,做的事却是给上一个被调度出去的进程做收尾工作,所有这个函数的参数是上一个进程的PCB,从这块获得上一个进程的内存信息就可以确保在它没有再次被调度上CPU执行的这段时间内的内存数据稳定性。因此最后选择将程序挂载到finish_task_switch函数上。以下是调度程序处理过程: - -![](./image/6.png) - -数据来源有两部分,一个是mm_struc结构本身存在的状态信息,另一个是在mm_rss_stat结构中,它总共统计四部分信息,内核定义如下: - -![](./image/7.png) - -## sysstat - -### 采集信息: - -| 参数 | 含义 | -| -------------- | -------------------------------- | -| active | LRU活跃内存大小 | -| inactive | LRU不活跃内存大小 | -| anon_active | 活跃匿名内存大小 | -| anon_inactive | 不活跃匿名内存大小 | -| file_active | 活跃文件映射内存大小 | -| file_inactive | 不活跃文件映射内存大小 | -| unevictable | 不可回收内存大小 | -| dirty | 脏页大小 | -| writeback | 正在回写的内存大小 | -| anonpages | RMAP页面 | -| mapped | 所有映射到用户地址空间的内存大小 | -| shmem | 共享内存 | -| kreclaimable | 内核可回收内存 | -| slab | 用于slab的内存大小 | -| sreclaimable | 可回收slab内存 | -| sunreclaim | 不可回收slab内存 | -| NFS_unstable | NFS中还没写到磁盘中的内存 | -| writebacktmp | 回写所使用的临时缓存大小 | -| anonhugepages | 透明巨页大小 | -| shmemhugepages | shmem或tmpfs使用的透明巨页 | -### 功能 - -提取各种类型内存的活动和非活动页面数量,以及其他内存回收相关的统计数据,除了常规的事件信息外,程序还输出了与内存管理相关的详细信息,包括了不同类型内存的活动(active)和非活动(inactive)页面,未被驱逐(unevictable)页面,脏(dirty)页面,写回(writeback)页面,映射(mapped)页面,以及各种类型的内存回收相关统计数据。 - -### 分析 - -分别用last_val1、last_val2、last_val3 三个哈希表记录上次统计的值,这里的键是不同类型页面的数量。 -`BPF_KPROBE(get_page_from_freelist_second, ...)` 是一个 Kprobe 函数,用于跟踪从空闲页列表获取页面的情况。 -在函数内部,首先获取当前进程的 PID,并与 `user_pid` 进行比较,如果相同则直接返回。 -通过 `BPF_CORE_READ` 读取内存分配相关的统计信息,包括匿名页面的活动状态和非活动状态下的数量、文件页面的活动状态和非活动状态下的数量、不可驱逐页面的数量等。 -通过三个哈希表 last_val1、last_val2、last_val3 查找上次统计的值,如果不存在则将新的统计值更新到哈希表中,如果存在且与当前值相同,则表示重复统计,直接返回。 -在环形缓冲区中预留空间,用于记录系统统计事件,并将获取的内存分配相关的统计信息填充到事件结构体中。 -最后,提交系统统计事件到环形缓冲区中。 -这个代码的挂载函数和paf代码相同在这里不再进行二次分析。 -### 挂载点及挂载原因 - -挂载点:get_page_from_freelist - -原因: - -首先,内存状态数据的提取需要获取到内存节点pglist_data数据结构,这个结构是对内存的总体抽象。pglist_data数据结构末尾有个vm_stat的数组,里面包含了当前内存节点所有的状态信息。所有只需要获取到pglist_data结构就能拿到当前的内存状态信息。但是物理内存分配在选择内存节点是通过mempolicy结构获取,无法获得具体的节点结构。选择内存节点的函数处理流程如下: - -```c -struct mempolicy *get_task_policy(struct task_struct *p) -{ - struct mempolicy *pol = p->mempolicy;//根据当前task_struct取得 - int node; - - if (pol) - return pol; - - node = numa_node_id(); - if (node != NUMA_NO_NODE) {//存在其他节点 - pol = &preferred_node_policy[node]; - /* preferred_node_policy is not initialised early in boot */ - if (pol->mode) - return pol; - } - - return &default_policy;//不存在其他节点返回本地节点 -} -``` - -经过对内存申请的内部结构alloc_context分析(这是内存申请过程中临时保存相关参数的结构),当前内存节点是可以通过:alloc_context——>zoneref——>zone——>pglist_data的路径访问到。 - -其次,因为函数执行申请内存的过程对获取内存节点数据的影响不大,所以只要可以获得alloc_context数据结构,在整个申请路径上挂载函数都是可以的。sysstat工具选择的挂载点是get_page_from_freelist函数。这个函数是快速物理内存分配的入口函数。因为内核在进行物理内存分配时,都会进入快速路径分配,只有当失败时才会进入慢速路径,所以get_page_from_freelist函数是必经函数。整个处理过程以及函数关系如下: - -![](./image/1.png) - -但是,经过对proc文件系统的打印函数meminfo_proc_show函数的分析得知,影响内存性能的参数在vm_stat中无法全部获得。一部分数据需要遍历当前内存节点包含的所有内存管理区zone结构中vm_stat数组获得,一部分需要读取全局变量vm_node_stat获得。但是内核的全局变量不会作为函数参数参与数据处理,目前还没具体方法获得这部分数据。 - -### 存在问题 - -■ 部分性能数据存在于内核全局变量中,而这些数据不会作为函数参数存储在栈中,因此这些数据目前还没实现统计 - -■ 因为内核对内存管理不会是物理内存的全部容量,而且最大管理内存的数据结构是内存结点,所以以上统计数据是以当前内存结点实际管理的内存容量为基准。 - -■ 当前剩余内存总量的统计需要遍历所有内存管理区来统计,但是由于内存管理区的空闲页面信息存储在数组第一个位置,使用指针指向时,统计到的数据不准确,使用变量统计会出现数据类型错误的报告。 - -## paf - -### 采集信息 - -| 参数 | 含义 | -| ------- | ------------------------------------ | -| min | 内存管理区处于最低警戒水位的页面数量 | -| low | 内存管理区处于低水位的页面数量 | -| high | 内存管理区处于高水位的页面数量 | -| present | 内存管理区实际管理的页面数量 | -| flag | 申请页面时的权限(标志) | -### 功能 - -主要是监控内核中的`get_page_from_freelist`函数。这个函数在内核中用于从内存空闲页列表中获取一个页面。 -程序主要是输出present(当前内存中可用的页面数量),min(在这个阈值下,系统可能会触发内存压缩),low(在这个阈值下,系统进行内存回收),high(在这个阈值上,认为内存资源充足),flag(用于内存分配的状态)。 - -### 分析 - -该程序挂载在内核函数`get_page_from_freelist`的前面。 -首先,获取当前进程的PID,并检查是否等于`user_pid`,如果相等,则直接返回,表示不监控当前进程。 -然后,通过`bpf_map_lookup_elem()`查找last_val哈希表中是否已经存在当前`gfp_mask`的值,如果不存在,则将当前`gfp_mask`的值插入哈希表中。 -如果已经存在,并且与上一次观察到的值相同,则直接返回,表示不需要重复记录相同的`gfp_mask`值。 -接着,通过`bpf_ringbuf_reserve()`尝试在环形缓冲区中预留一段空间,用于存储事件数据。如果预留失败,则直接返回。 -之后,通过`BPF_CORE_READ()`读取给定的内核数据结构`alloc_context`中的一些字段的值,并将这些值存储在定义好的事件结构体e中。 -最后,通过`bpf_ringbuf_submit()`将填充好的事件结构体提交到环形缓冲区中。 - -内存申请失败一般集中在申请权限不够或者是权限冲突导致,申请权限不够是当内核申请优先级较低的页面时,虽然内存管理区有足够的页面满足这次申请数量,但是当前剩余空闲页面少于最低警戒水位,因此导致内核无法成功分配页面的情况。权限冲突,例如内核在开启CMA机制下导致的页面页面申请失败的情况,这种情况下管理区空闲页面需要减去CMA机制占用内存才是当前可分配内存。相关权限判断代码如下: - -添加CMA权限代码路径mm/page_alloc.c - -```c -static inline unsigned int -gfp_to_alloc_flags(gfp_t gfp_mask) -{ - unsigned int alloc_flags = ALLOC_WMARK_MIN | ALLOC_CPUSET; - ... - alloc_flags |= (__force int) (gfp_mask & __GFP_HIGH); - ... - if (gfp_mask & __GFP_KSWAPD_RECLAIM) - alloc_flags |= ALLOC_KSWAPD; - -#ifdef CONFIG_CMA - if (gfpflags_to_migratetype(gfp_mask) == MIGRATE_MOVABLE) - alloc_flags |= ALLOC_CMA; -#endif - return alloc_flags; -} -``` - -CMA机制内存处理代码: - -```c -bool __zone_watermark_ok(struct zone *z, unsigned int order, unsigned long mark, - int classzone_idx, unsigned int alloc_flags, long free_pages) -{ - ... -#ifdef CONFIG_CMA - if (!(alloc_flags & ALLOC_CMA)) - free_pages -= zone_page_state(z, NR_FREE_CMA_PAGES); -#endif - - if (free_pages <= min + z->lowmem_reserve[classzone_idx]) - return false; - ... -#ifdef CONFIG_CMA - if ((alloc_flags & ALLOC_CMA) && - !list_empty(&area->free_list[MIGRATE_CMA])) { - return true; - } -#endif -} -``` -挂载点:get_page_from_freelist - -原因: - -经过对内核源码的分析,页面申请失败分析工具的理想挂载点应该是慢速路径的入口函数(__alloc_pages_slowpath)。但是这个函数不允许ebpf程序挂载,而且这个函数内部也不存在合理的挂载点,所有将函数挂载点选在快速路径的入口函数get_page_from_freelist上。因为页面申请的控制结构体ac在这两个函数之间不存在信息更改,所以可以确保这两个函数传递的ac结构体是相同的,不会对提取出来的数据产生影响。为了确保数据确实是在页面申请失败的情况下才会打印数据,需要对alloc_pages_nodemask函数的返回值进行挂载,当alloc_pages_nodemask函数没有返回页面结构体page时,也就是页面申请失败的情况下单元提取的数据。 - -### 存在问题 - -■ 打印出来的内存申请标志与申请内存传递进去的标志不符,分析原因可能内核在进行alloc_pages函数之前有对标志位进行处理。 - -■ 因为内存管理区的剩余内存空间处在vm_stat数组第一位,经过分析,使用指针提取的数组第一个数据总是存在差异,需要调整。 - -■ 对打印的标志位需要进一步解析,方便快速确认当前申请页面类型。 - - -## pr - -### 采集信息 - -| 参数 | 含义 | -| ------------- | -------------------------------------------- | -| reclaim | 要回收的页面数量 | -| reclaimed | 已经回收的页面数量 | -| unqueue_dirty | 还没开始回写和还没在队列等待的脏页 | -| congested | 正在块设备上回写的页面,含写入交换空间的页面 | -| writeback | 正在回写的页面 | -### 功能 - -主要用于监控内核中的`shrink_page_list`函数。 -整个BPF程序的功能是监控`shrink_page_list`函数的调用,当函数被调用时,记录特定的内核数据(包括`nr_reclaimed`等值),并将这些数据存储在环形缓冲区中,以供用户空间程序使用。 -跟踪内核中页面的回收行为,记录回收的各个阶段,例如要回收的页面,以回收的页面,等待回收的脏页数,要写回的页数(包括交换空间中的页数)以及当前正在写回的页数。 - -### 分析 - -在这个函数内部,首先通过`bpf_get_current_pid_tgid()`获取当前进程的PID,并与`user_pid`比较,如果相等,则直接返回,表示不监控当前进程。 -通过`BPF_CORE_READ()`读取给定的内核数据结构`scan_control`中的一些字段的值,并将这些值存储在定义好的事件结构体中。 -通过`bpf_ringbuf_reserve()`尝试在环形缓冲区中预留一段空间,用于存储事件数据。如果预留失败,则直接返回。 -通过bpf_ringbuf_submit()将填充好的事件结构体提交到环形缓冲区中。 -### 挂载点与挂载原因 - -挂载点 - -shrink_page_list - -挂载原因 - -shrink_page_list函数是页面回收后期指向函数,主要操作是遍历链表中每一个页面,根据页面的属性确定将页面添加到回收队列、活跃链表还是不活跃链表.这块遍历的链表是在上一级函数 shrink_inactive_list中定义的临时链表,因为一次最多扫描32个页面,所有链表最多含有32个页面。在shrink_page_list这个函数中还有一个重要操作是统计不同状态的页面数量并保存在scan_control结构体中。而工具数据提取的位置就是找到这个结构体并获取有关性能指标。因为这个提取的数据都是内核函数实时更改的,所有具有较高准确性。 - -scan_control结构体是每次进行内存回收时都会被回收进程重新定义,所有会看到数据是一个增长状态,之后有回归0,这和挂载点也有一定关系。 -## memleak - -### 功能 - -代码主要用于跟踪内核内存分配和释放的情况,并记录相关的统计信息。 - -### 分析 - -首先定义了几个 BPF 映射: -`sizes`:用于存储与每个进程 ID(PID)关联的内存分配大小。 -`allocs`:用于存储与内存分配相关信息,以分配的返回地址为索引。 -`combined_allocs`:另一个哈希映射,用于根据堆栈跟踪 ID 聚合内存分配信息。 -`stack_traces`:堆栈跟踪映射,用于将堆栈跟踪转换为堆栈跟踪 ID。 - -定义了两个 `uprobes`(用户空间探测点): -`malloc_enter`:此探测点附加到 `malloc` 函数的入口点。它将当前进程 ID(pid)的内存分配大小记录到 `sizes` 映射中。 -`malloc_exit`:此探测点附加到 `malloc` 函数的退出点。它从 `sizes` 映射中检索先前记录的大小,将其与分配的返回地址关联,并使用此信息更新 `allocs` 映射。此外,它根据堆栈跟踪更新 `combined_allocs` 映射以聚合分配信息。 - -定义了一个 `uretprobe`(用户空间返回探测点): -`malloc_exit`:此探测点附加到 `malloc` 函数的退出点。它从 sizes 映射中检索先前记录的大小,将其与分配的返回地址关联,并使用此信息更新 `allocs` 映射。此外,它根据堆栈跟踪更新 `combined_allocs` 映射以聚合分配信息。 - -定义了一个 `uprobe`(用户空间探测点): -`free_ente`r:此探测点附加到 `free` 函数的入口点。它从 allocs 映射中检索与正在释放的地址相关的分配信息。然后,它更新 `combined_allocs` 映射以反映释放。 - -# 工具的使用方法说明 - -## 功能介绍 - -mem_watcher工具可以通过一系列的命令控制参数来控制其具体的检测行为:我们可以通过sudo ./mem_watcher -h来查看工具支持的功能 - -``` - select function: - -a, --paf print paf (内存页面状态报告) - -p, --pr print pr (页面回收状态报告) - -r, --procstat print procstat (进程内存状态报告) - -s, --sysstat print sysstat (系统内存状态报告) - -l, --memleak=PID print memleak (内存泄漏检测) -``` - -- -a 输出的信息包括时间戳、进程ID、虚拟内存大小、物理内存等。输出的内容根据用户的选择(特定PID、是否显示RSS等)而变化。除了常规的事件信息外,程序还输出了与内存管理相关的详细信息,主要是present(当前内存中可用的页面数量),min(在这个阈值下,系统可能会触发内存压缩),low(在这个阈值下,系统进行内存回收),high(在这个阈值上,认为内存资源充足),flag(用于内存分配的状态)。 -- -p 跟踪内核中页面的回收行为,记录回收的各个阶段,例如要回收的页面,以回收的页面,等待回收的脏页数,要写回的页数(包括交换空间中的页数)以及当前正在写回的页数。 -- -r 主要是用于跟踪用户空间进程的内存使用情况。具体功能是在用户空间进程切换时,记录切换前进程的内存信息。 -- -s 提取各种类型内存的活动和非活动页面数量,以及其他内存回收相关的统计数据,除了常规的事件信息外,程序还输出了与内存管理相关的详细信息,包括了不同类型内存的活动(active)和非活动(inactive)页面,未被驱逐(unevictable)页面,脏(dirty)页面,写回(writeback)页面,映射(mapped)页面,以及各种类型的内存回收相关统计数据。 -- -l 输出了用户态造成内存泄漏的位置,包括内存泄漏指令地址对应符号名、文件名、行号,程序中尚未被释放的内存总量,未被释放的分配次数。 - -## 使用方法和结果展示 - -## paf - -```c -sudo ./mem_watcher -a -MIN LOW HIGH PRESENT FLAG -262144 5100 6120 262144 1100dca -262144 5100 6120 262144 2800 -262144 5100 6120 262144 cc0 -262144 5100 6120 262144 d00 -262144 5100 6120 262144 2dc2 -...... -``` - -## pr - -```c -sudo ./mem_watcher -p -RECLAIM RECLAIMED UNQUEUE CONGESTED WRITEBACK -16893 0 0 0 0 -16893 24 0 0 0 -16893 24 0 0 0 -16893 40 0 0 0 -16893 64 0 0 0 -16893 64 0 0 0 -16893 66 0 0 0 -...... -``` - -## procstat - -```c -sudo ./mem_watcher -r -...... -01:08:50 334 0 0 0 0 -01:08:50 2984 13194 10242 2952 0 -01:08:50 0 0 0 0 0 -01:08:50 334 0 0 0 0 -01:08:50 5427 0 0 0 0 -01:08:50 0 0 0 0 0 -01:08:50 5427 0 0 0 0 -01:08:50 0 0 0 0 0 -01:08:50 0 0 0 0 0 -01:08:50 5427 0 0 0 0 -01:08:50 0 0 0 0 0 -01:08:50 5427 0 0 0 0 -01:08:50 2984 13194 10242 2952 0 -01:08:50 0 0 0 0 0 -01:08:50 5427 0 0 0 0 -01:08:50 334 0 0 0 0 -01:08:50 5427 0 0 0 0 -01:08:50 0 0 0 0 0 -01:08:50 0 0 0 0 0 -01:08:50 5427 0 0 0 0 -01:08:50 0 0 0 0 0 -...... -``` - -## sysstat - -```c -sudo ./mem_watcher -s -...... -ACTIVE INACTVE ANON_ACT ANON_INA FILE_ACT FILE_INA UNEVICT DIRTY WRITEBK ANONPAG MAP SHMEM -327644 2747936 1988 2278752 325656 469184 0 216 0 563728 249116 7832 -327652 2747616 1996 2278432 325656 469184 0 240 0 563844 249164 7832 -327652 2747616 1996 2278432 325656 469184 0 240 0 563864 249164 7832 -327652 2747844 1996 2278656 325656 469188 0 252 0 563864 249164 7832 -327652 2747844 1996 2278656 325656 469188 0 252 0 563884 249164 7832 -...... -``` - -## memleak - -``` -sudo ./mem_watcher -l 2429 -...... -stack_id=0x3c14 with outstanding allocations: total_size=4 nr_allocs=1 -000055e032027205: alloc_v3 @ 0x11e9+0x1c /test_leak.c:11 -000055e032027228: alloc_v2 @ 0x120f+0x19 /test_leak.c:17 -000055e03202724b: alloc_v1 @ 0x1232+0x19 /test_leak.c:23 -000055e032027287: memory_leak @ 0x1255+0x32 /test_leak.c:35 -00007f1ca1d66609: start_thread @ 0x8530+0xd9 -stack_id=0x3c14 with outstanding allocations: total_size=8 nr_allocs=2 -000055e032027205: alloc_v3 @ 0x11e9+0x1c /test_leak.c:11 -000055e032027228: alloc_v2 @ 0x120f+0x19 /test_leak.c:17 -000055e03202724b: alloc_v1 @ 0x1232+0x19 /test_leak.c:23 -000055e032027287: memory_leak @ 0x1255+0x32 /test_leak.c:35 -00007f1ca1d66609: start_thread @ 0x8530+0xd9 -...... -``` - ------- -## 测试环境 - -deepin20.6,Linux-5.17; - -libbpf:[libbpf-bootstrap](https://github.com/libbpf/libbpf-bootstrap) - diff --git a/eBPF_Supermarket/Memory_Subsystem/bpftool b/eBPF_Supermarket/Memory_Subsystem/bpftool deleted file mode 160000 index 06c61eccd..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/bpftool +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 06c61eccd3b8a6ff3df3e451a2a93058913124fc diff --git a/eBPF_Supermarket/Memory_Subsystem/docs/image/10.png b/eBPF_Supermarket/Memory_Subsystem/docs/image/10.png new file mode 100644 index 000000000..7ca4bdf64 Binary files /dev/null and b/eBPF_Supermarket/Memory_Subsystem/docs/image/10.png differ diff --git a/eBPF_Supermarket/Memory_Subsystem/docs/image/11.png b/eBPF_Supermarket/Memory_Subsystem/docs/image/11.png new file mode 100644 index 000000000..e35dd9d0c Binary files /dev/null and b/eBPF_Supermarket/Memory_Subsystem/docs/image/11.png differ diff --git a/eBPF_Supermarket/Memory_Subsystem/docs/image/12.png b/eBPF_Supermarket/Memory_Subsystem/docs/image/12.png new file mode 100644 index 000000000..dd79f597f Binary files /dev/null and b/eBPF_Supermarket/Memory_Subsystem/docs/image/12.png differ diff --git a/eBPF_Supermarket/Memory_Subsystem/docs/image/13.png b/eBPF_Supermarket/Memory_Subsystem/docs/image/13.png new file mode 100644 index 000000000..6f9423fee Binary files /dev/null and b/eBPF_Supermarket/Memory_Subsystem/docs/image/13.png differ diff --git a/eBPF_Supermarket/Memory_Subsystem/docs/image/14.png b/eBPF_Supermarket/Memory_Subsystem/docs/image/14.png new file mode 100644 index 000000000..5012e8e47 Binary files /dev/null and b/eBPF_Supermarket/Memory_Subsystem/docs/image/14.png differ diff --git a/eBPF_Supermarket/Memory_Subsystem/docs/image/15.png b/eBPF_Supermarket/Memory_Subsystem/docs/image/15.png new file mode 100644 index 000000000..901829999 Binary files /dev/null and b/eBPF_Supermarket/Memory_Subsystem/docs/image/15.png differ diff --git a/eBPF_Supermarket/Memory_Subsystem/docs/image/16.png b/eBPF_Supermarket/Memory_Subsystem/docs/image/16.png new file mode 100644 index 000000000..349787e9a Binary files /dev/null and b/eBPF_Supermarket/Memory_Subsystem/docs/image/16.png differ diff --git a/eBPF_Supermarket/Memory_Subsystem/docs/image/17.png b/eBPF_Supermarket/Memory_Subsystem/docs/image/17.png new file mode 100644 index 000000000..92ead4de5 Binary files /dev/null and b/eBPF_Supermarket/Memory_Subsystem/docs/image/17.png differ diff --git a/eBPF_Supermarket/Memory_Subsystem/docs/image/18.png b/eBPF_Supermarket/Memory_Subsystem/docs/image/18.png new file mode 100644 index 000000000..97aa1aed9 Binary files /dev/null and b/eBPF_Supermarket/Memory_Subsystem/docs/image/18.png differ diff --git a/eBPF_Supermarket/Memory_Subsystem/docs/image/9.png b/eBPF_Supermarket/Memory_Subsystem/docs/image/9.png new file mode 100644 index 000000000..3cb9da295 Binary files /dev/null and b/eBPF_Supermarket/Memory_Subsystem/docs/image/9.png differ diff --git a/eBPF_Supermarket/Memory_Subsystem/docs/mem_watcher_vision.md b/eBPF_Supermarket/Memory_Subsystem/docs/mem_watcher_vision.md new file mode 100644 index 000000000..b64ce6a24 --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/docs/mem_watcher_vision.md @@ -0,0 +1,43 @@ +# mem_watcher可视化 +## 配置环境 +在使用mem_watcher可视化之前,请先配置docker、go的环境,具体配置方法可参考: +### 配置docker +[在 Ubuntu 上安装 Docker Desktop |Docker 文档](https://docs.docker.com/desktop/install/ubuntu/#install-docker-desktop) +[在 Linux 上安装 Docker Desktop |Docker 文档](https://docs.docker.com/desktop/install/linux-install/) +### go环境 +配置好dacker环境后接着配置go的环境 +我们首先对我们现有的工具进行编译 +- 首先先进入lmp目录下的`lmp/eBPF_Supermarket/Memory_Subsystem/mem_watcher`文件夹 +然后进行编译,其实这一步也可以忽略掉 +```c + make -j 20 +``` +- 在lmp目录下的`eBPF_Visualization/eBPF_prometheus`文件夹下 +- 执行make指令,编译可视化的go语言工具 +- 执行`make start_service`指令,配置下载docker镜像并启动grafana和prometheus服务 +- 执行如下指令开始采集数据以及相关处理: +```c + ./data-visual collect lmp/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher -p +``` +切记根据自己的文件所在目录进行修改,如果不知道或者不确定的可以在自己的程序文件下输入`pwd`命令进行查看,如果目录出现错误会失败。 + +在网页打开网址:http://81.70.204.7:8090/metrics 此处为localhost:8090/metrics,便可以看到http网页中的数据; +这个网址需要根据你自己的虚拟机的ip进行修改。 +![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/a2aab03835bf44ca8a27023c8c445bad.png) +- 在网页打开网址:http://81.70.204.7:3000/ 即可进入grafana服务,使用初始密码登录(user:admin pswd: admin)进入管理界面: +- 点击【Home-Connection-Add new connection】,选择Prometheus,建立与Prometheus服务器的连接: +![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/1e41ec78f5fa4923a5a1e18cab233c62.png) + +这个172.17.0.1表示docker0网桥的 IPv4 地址。在 Docker 中,通常会将docker0的第一个 IP 地址分配给Docker主机自身。因此,172.17.0.1是 Docker主机上Docker守护进程的 IP 地址,所以在Grafana数据源这块设置成http://172.17.0.1:9090 ,然后点击下面的【Save & test】按钮 +- 进入可视化配置界面: +![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/6e37ecb4c62245b2a68a80dc07643ac9.png) +![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/fbdb65ef1cc54c128594a96cad5e2386.png) +![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/4f3a33b65929429b96b683e573593938.png) +## mem_watcher子工具可视化输出(样例) +### mem_watcher -p +- reclaimed +![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/eed35e475ba6422ca176f966b011902c.png) +可以进行压力测试 +首先使用`sudo apt-get install stress`安装压力测试工具。 +使用下面的命令对内存进行施压 `stress -m 3 --vm-bytes 300M` +会生成3个进程,每个进程占用300M内存。 diff --git a/eBPF_Supermarket/Memory_Subsystem/libbpf b/eBPF_Supermarket/Memory_Subsystem/libbpf deleted file mode 160000 index 46eafba62..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/libbpf +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 46eafba62ee380a1f8230e2d5e81f65e0e030f15 diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/Makefile b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/Makefile index 5847750c7..1c22cb996 100644 --- a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/Makefile +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/Makefile @@ -1,13 +1,13 @@ # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) OUTPUT := .output CLANG ?= clang -LIBBPF_SRC := $(abspath ../../libbpf/src) -BPFTOOL_SRC := $(abspath ../../bpftool/src) +LIBBPF_SRC := $(abspath ../../lib/libbpf/src) +BPFTOOL_SRC := $(abspath ../../lib/bpftool/src) LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool -LIBBLAZESYM_SRC := $(abspath ../../blazesym/) -LIBBLAZESYM_INC := $(abspath $(LIBBLAZESYM_SRC)/capi/include) +LIBBLAZESYM_SRC := $(abspath ../blazesym/) +LIBBLAZESYM_INC := $(abspath ../blazesym/capi/include) LIBBLAZESYM_OBJ := $(abspath $(OUTPUT)/libblazesym_c.a) ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \ | sed 's/arm.*/arm/' \ @@ -16,22 +16,24 @@ ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \ | sed 's/mips.*/mips/' \ | sed 's/riscv64/riscv/' \ | sed 's/loongarch64/loongarch/') -VMLINUX := ../../vmlinux/$(ARCH)/vmlinux.h +VMLINUX := ../vmlinux/$(ARCH)/vmlinux.h # Use our own libbpf API headers and Linux UAPI headers distributed with # libbpf to avoid dependency on system-wide headers, which could be missing or # outdated -INCLUDES := -I$(OUTPUT) -I../../libbpf/include/uapi -I$(dir $(VMLINUX)) -I$(LIBBLAZESYM_INC) +INCLUDES := -I$(OUTPUT) -I../../libbpf/include/uapi -I$(dir $(VMLINUX)) -I$(LIBBLAZESYM_INC) -I./include CFLAGS := -g -Wall ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) -APPS = paf pr procstat sysstat memleak +APPS = paf pr procstat sysstat memleak fraginfo vmasnap drsnoop oomkiller +TARGETS= mem_watcher CARGO ?= $(shell which cargo) ifeq ($(strip $(CARGO)),) BZS_APPS := else -BZS_APPS := mem_watcher -APPS += $(BZS_APPS) +BZS_APPS := + + # Required by libblazesym ALL_LDFLAGS += -lrt -ldl -lpthread -lm endif @@ -69,12 +71,12 @@ $(call allow-override,CC,$(CROSS_COMPILE)cc) $(call allow-override,LD,$(CROSS_COMPILE)ld) .PHONY: all -all: $(APPS) +all: $(TARGETS) .PHONY: clean clean: $(call msg,CLEAN) - $(Q)rm -rf $(OUTPUT) $(APPS) + $(Q)rm -rf $(OUTPUT) $(TARGETS) $(OUTPUT) $(OUTPUT)/libbpf $(BPFTOOL_OUTPUT): $(call msg,MKDIR,$@) @@ -102,7 +104,7 @@ $(LIBBLAZESYM_OBJ): $(LIBBLAZESYM_SRC)/target/release/libblazesym_c.a | $(OUTPUT $(Q)cp $(LIBBLAZESYM_SRC)/target/release/libblazesym_c.a $@ # Build BPF code -$(OUTPUT)/%.bpf.o: %.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT) $(BPFTOOL) +$(OUTPUT)/%.bpf.o: bpf/%.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT) $(BPFTOOL) $(call msg,BPF,$@) $(Q)$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) \ $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) \ @@ -110,25 +112,39 @@ $(OUTPUT)/%.bpf.o: %.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT) $(Q)$(BPFTOOL) gen object $@ $(patsubst %.bpf.o,%.tmp.bpf.o,$@) # Generate BPF skeletons -$(OUTPUT)/%.skel.h: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL) +.PHONY: $(APPS) +$(APPS): %: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL) $(call msg,GEN-SKEL,$@) - $(Q)$(BPFTOOL) gen skeleton $< > $@ + $(Q)$(BPFTOOL) gen skeleton $< > $(OUTPUT)/$@.skel.h # Build user-space code -$(patsubst %,$(OUTPUT)/%.o,$(APPS)): %.o: %.skel.h - $(OUTPUT)/%.o: %.c $(wildcard %.h) | $(OUTPUT) + $(call msg,CC,$@) + $(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ + +$(OUTPUT)/$(TARGETS).o: $(TARGETS).c $(APPS) | $(OUTPUT) $(call msg,CC,$@) $(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $(filter %.c,$^) -o $@ -$(patsubst %,$(OUTPUT)/%.o,$(BZS_APPS)): $(LIBBLAZESYM_OBJ) +$(patsubst %,$(OUTPUT)/%.o,$(TARGETS)): $(LIBBLAZESYM_OBJ) -$(BZS_APPS): $(LIBBLAZESYM_OBJ) +$(TARGETS): $(LIBBLAZESYM_OBJ) # Build application binary -$(APPS): %: $(OUTPUT)/%.o $(LIBBPF_OBJ) | $(OUTPUT) +$(TARGETS): %: $(OUTPUT)/%.o $(COMMON_OBJ) $(LIBBPF_OBJ) | $(OUTPUT) $(call msg,BINARY,$@) - $(Q)$(CC) $(CFLAGS) $^ $(ALL_LDFLAGS) -lelf -lz -o $@ + $(Q)$(CC) $(CFLAGS) $^ $(ALL_LDFLAGS) -lstdc++ -lelf -lz -o $@ + +SUCCESS_MESSAGE: + @echo "\e[38;2;255;102;204m _ _ \e[0m" + @echo "\e[38;2;255;153;204m _ __ ___ ___ _ __ ___ __ ____ _| |_ ___| |__ ___ _ __ \e[0m" + @echo "\e[38;2;255;204;204m | '_ \` _ \ / _ \ '_ \` _ \ \ \ /\ / / _\` | __/ __| '_ \ / _ \ '__|\e[0m" + @echo "\e[38;2;204;229;255m | | | | | | __/ | | | | | \ V V / (_| | || (__| | | | __/ | \e[0m" + @echo "\e[38;2;153;204;255m |_| |_| |_|\___|_| |_| |_| \_/\_/ \__ _|\__\___|_| |_|\___|_| \e[0m" + + @echo "\e[38;2;0;255;255mSuccessful to compile mem_watcher tools: \e[0m" + @echo "\e[38;2;0;255;255mPlease start your use ~ \e[0m" +all: $(TARGETS) SUCCESS_MESSAGE # delete failed targets .DELETE_ON_ERROR: diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/README.md b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/README.md new file mode 100644 index 000000000..473eaf650 --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/README.md @@ -0,0 +1,540 @@ +# mem\_watcher + +## mem\_watcher介绍 + +**memwatcher是一款基于eBPF的内存监测工具,其设计的目的就是为了可以让用户能够在主机环境上可以快捷的检测到Linux内存的详细信息。** **通过高效的数据收集和精准的监控能力,帮助用户可以有效的监控主机内存情况。** **使用了eBPF(Extended Berkeley Packet Filter)来监控内核中的几个关键事件,主要涉及到内存管理方面的几个功能:** + +**本项目是内存性能分析工具集合,采用libbpf编写。现有工具procstat(进程内存状态报告),sysstat(系统内存状态报告),paf(内存页面状态报告),pr(内存回收状态报告)和memleak(内存泄漏检测)。** + +**eBPF 提供了一种高效的机制来监控和追踪系统级别的事件,包括内存的分配和释放。通过 eBPF,可以跟踪内存分配和释放的请求,并收集每次分配的调用堆栈。然后,分析这些信息,找出执行了内存分配但未执行释放操作的调用堆栈,这有助于程序员找出导致内存泄漏的源头。** + +--- + +## 背景意义 + +**内存子系统是Linux内核中是一个相对复杂的模块,内核中几乎所有的数据、缓存、程序指令都有内存模块参与管理。在内存不足的情况下,这些数据就会被存储在磁盘的交换空间中,但是磁盘的处理速度相对与内存非常慢,当内存和磁盘频繁进行数据交换时,缓慢的磁盘读写速度非常影响系统性能。系统可能因内存不足从而终止那些占用内存较大的进程,导致程序运行故障。因此准确的监控分析内存性能状况就变得非常重要。** + +**目前,传统的内存性能分析工具通过读取proc文件系统下的数据,经过简单的处理后呈现给用户,方便管理人员随时了解系统状况。然而这些工具的灵活性非常差,单个工具输出的信息有限。系统维护人员在分析性能问题时常常需要借助多个工具才能进行。步骤繁琐且工具本身对系统性能也有一定影响。随着ebpf技术在系统可观测上的发展,利于ebpf非侵入式的数据获取方式已被大多数企业、高校认可并取得了一定的研究成果。ebpf的可编程性可以让管理人员灵活的获取系统的运行数据,而且在数据的提取粒度上有着传统工具无法比拟的优势。现在,ebpf作为Linux内核顶级子系统,已经成为实现Linux内核可观测性、网络和内核安全的理想技术。** + +## 准备工作 + +**环境:Ubuntu 22.04, 内核版本 5.15.0-107-generic及以上** + +**注:由于 eBPF 的 kprobe 逻辑与内核数据结构定义高度相关,而现在 BTF 的应用(可消除不同内核版本间数据结构的不兼容)还不是很成熟,因此在使用此例程前,需首先适配内核版本。** + +**软件:** + +* **go SDK(安装cilium库)** +* **llvm,clang,rust** +* **bpftrace** + +## 环境搭建 + +1. **rust 语言编译环境安装** + **`blazesym` 使用 `rust` 语言编写,使用前需要安装 `rust` 语言的编译环境** + ``` + # 安装前先配置国内镜像源,可以加速下载 + # 设置环境变量 RUSTUP_DIST_SERVER (用于更新 toolchain): + export RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static + # RUSTUP_UPDATE_ROOT (用于更新 rustup): + export RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup + + # 安装 https://www.rust-lang.org/tools/install + # 请不要使用Ubuntu的安装命令: sudo apt install cargo,否则可能会出现莫名其妙的问题 + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + + # 修改 ~/.cargo/config 文件,配置 rust 使用的国内镜像源 + [source.crates-io] + registry = "https://github.com/rust-lang/crates.io-index" + replace-with = 'ustc' + + [source.ustc] + registry = "git://mirrors.ustc.edu.cn/crates.io-index" + ``` + +### 环境搭建的问题记录 + ``` + cd lmp/eBPF_Supermarket/Memory_Subsystem/mem_watcher + make + ``` + make后没有编译生成任何的二进制文件,只打印了logo,效果如下: + ![alt text](/docs/image/15.png) + +打开makefile,检查makefile逻辑,代码如下: +``` +CARGO ?= $(shell which cargo) +ifeq ($(strip $(CARGO)),) +BZS_APPS := +else +BZS_APPS := +TARGETS= mem_watcher +``` +这段代码是检查`CARGO`变量是否为空。如果为空(即`cargo`命令不存在),则`BZS_APPS`变量将被设置为空。否则,`BZS_APPS`变量也将被设置为空,但同时定义了一个名为`TARGETS`的变量,其值为`mem_watcher`。 + +**修改makefile** + +``` +TARGETS= mem_watcher +CARGO ?= $(shell which cargo) +ifeq ($(strip $(CARGO)),) +BZS_APPS := +else +BZS_APPS := +``` +再次执行make,发现报错为 "vmlinux.h file not find",如下: + ![alt text](/docs/image/16.png) + +执行以下命令,生成vmlinux.h文件 +``` +cd /lmp/eBPF_Supermarket/Memory_Subsystem +bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h +``` + +**安装cargo** +``` + $ curl -sSf https://static.rust-lang.org/rustup.sh | sh +``` +这里参考 [Blog](https://blog.csdn.net/somken/article/details/129145764)换源 +``` +# 放到 `$HOME/.cargo/config` 文件中 +[source.crates-io] +registry = "https://github.com/rust-lang/crates.io-index" + +# 替换成你偏好的镜像源 +replace-with = 'tuna' +#replace-with = 'ustc' +#replace-with = 'zju' + +[source.tuna] +registry = "https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git" + +[source.ustc] +registry = "git://mirrors.ustc.edu.cn/crates.io-index" + +[source.zju] +registry = "https://mirrors.zju.edu.cn/git/crates.io-index.git" + + +[source.sjtu] +registry = "https://mirrors.sjtug.sjtu.edu.cn/git/crates.io-index" + +# rustcc社区 +[source.rustcc] +registry = "git://crates.rustcc.cn/crates.io-index" + +``` + 重新安装还是会报错: + + ![alt text](/docs/image/17.png) + + 在 `~/.cargo/config` 文件中添加以下内容,即可解决: + ``` + [net] +git-fetch-with-cli = true +``` +再次make编译完成,生成二进制文件 mem_watcher,并能正常运行。 + ![alt text](/docs/image/18.png) + +# 工具的使用方法说明 + +## 功能介绍 + +**mem\_watcher工具可以通过一系列的命令控制参数来控制其具体的检测行为:我们可以通过sudo ./mem\_watcher -h来查看工具支持的功能** + +``` +mem_watcher is in use .... + + Select function: + + Memory Page Reports: + -a, --paf                 Print paf (内存页面状态报告) + + Page Reclaim Reports: + -p, --pr                   Print pr (页面回收状态报告) + + Process Memory Reports: + -P, --choose_pid=PID       选择进程号打印 + -r, --procstat             Print procstat (进程内存状态报告) + -R, --Rss                 打印进程页面 + + System Memory Reports: + -s, --sysstat             Print sysstat (系统内存状态报告) + -n, --part2               System Memory Reports (Part 2) + + Memory Leak Detection: + -l, --memleak             Print memleak (内核态内存泄漏检测) +     --choose_pid=PID       选择进程号打印, Print memleak (用户态内存泄漏检测) +``` + +* **-a 输出的信息跟踪内核中页面的回收行为。包括present(当前内存中可用的页面数量),min(在这个阈值下,系统可能会触发内存压缩),low(在这个阈值下,系统进行内存回收),high(在这个阈值上,认为内存资源充足),flag(用于内存分配的状态)。** +* **-p 跟踪内核中页面的回收行为,记录回收的各个阶段,例如要回收的页面,以回收的页面,等待回收的脏页数,要写回的页数(包括交换空间中的页数)以及当前正在写回的页数。** +* **-r 主要是用于跟踪用户空间进程的内存使用情况。具体功能是在用户空间进程切换时,记录切换前进程的内存信息。如果要选择进程进行跟踪,则输入进程的pid;输入参数R,显示进程使用的页面数量。** +* **-s 提取各种类型内存的活动和非活动页面数量,以及其他内存回收相关的统计数据,除了常规的事件信息外,程序还输出了与内存管理相关的详细信息,包括了不同类型内存的活动(active)和非活动(inactive)页面,未被驱逐(unevictable)页面,脏(dirty)页面,写回(writeback)页面,映射(mapped)页面,以及各种类型的内存回收相关统计数据。该功能分为了两部分输出,输入参数“n”,输出第二部分性能指标。** +* **-l 输出了用户态造成内存泄漏的位置,包括内存泄漏指令地址对应符号名,程序中尚未被释放的内存总量,未被释放的分配次数。也可以输出内核态的内存泄漏。如跟踪内核态内存泄漏,只输入参数-l;如跟踪用户态内存泄漏,则在获取用户态进程的pid后,再输入要跟踪进程的pid。** + +--- + +# mem\_watcher具体功能分析 + +## procstat + +### 采集信息: + + +| **参数** | **含义** | +| ------------ | ---------------------------- | +| **vsize** | **进程使用的虚拟内存** | +| **size** | **进程使用的最大物理内存** | +| **rssanon** | **进程使用的匿名页面** | +| **rssfile** | **进程使用的文件映射页面** | +| **rssshmem** | **进程使用的共享内存页面** | +| **vswap** | **进程使用的交换分区大小** | +| **vdata** | **进程使用的私有数据段大小** | +| **vpte** | **进程页表大小** | +| **vstk** | **进程用户栈大小** | + +### 载点及挂载原因 + +**挂载点:finish\_task\_switch** + +**挂载原因:** + +**首先,获取进程级别内存使用信息首先需要获取到进程的task\_struct结构体,其中在mm\_struct成员中存在一个保存进程当前内存使用状态的数组结构,因此有关进程的大部分内存使用信息都可以通过这个数组获得。其次,需要注意函数的插入点,插入点的选取关系到数据准确性是否得到保证,而在进程的内存申请,释放,规整等代码路径上都存在页面状态改变,但是数量信息还没有更新的相关结构中的情况,如果插入点这两者中间,数据就会和实际情况存在差异,所有在确保可以获取到进程PCB的前提下,选择在进程调度代码路径上考虑。而finish\_task\_switch函数是新一个进程第一个执行的函数,做的事却是给上一个被调度出去的进程做收尾工作,所有这个函数的参数是上一个进程的PCB,从这块获得上一个进程的内存信息就可以确保在它没有再次被调度上CPU执行的这段时间内的内存数据稳定性。因此最后选择将程序挂载到finish\_task\_switch函数上。** + +### 可以解决的问题 + +**首先,通过监测这些指标,可以及时发现内存使用异常或泄漏问题,针对性地进行性能优化和内存管理,确保系统运行的高效性和稳定性。其次,了解进程的内存占用情况有助于有效管理系统资源,避免资源浪费和冲突,提高整体资源利用率。此外,当系统出现内存相关的故障或异常时,通过分析这些指标可以快速定位问题所在,有针对性地进行故障排除和修复,缩短系统恢复时间。结合这些指标可以进行性能调优,优化内存分配和释放策略,提升系统的响应速度和整体性能表现。** + +### 使用方法和结果展示 + +``` +sudo ./mem_watcher -r +...... +01:08:50 334      0        0        0        0   +01:08:50 2984     13194    10242    2952     0   +01:08:50 0        0        0        0        0   +01:08:50 334      0        0        0        0   +01:08:50 5427     0        0        0        0   +01:08:50 0        0        0        0        0   +01:08:50 5427     0        0        0        0   +01:08:50 0        0        0        0        0   +01:08:50 0        0        0        0        0   +01:08:50 5427     0        0        0        0   +01:08:50 0        0        0        0        0   +01:08:50 5427     0        0        0        0   +01:08:50 2984     13194    10242    2952     0   +01:08:50 0        0        0        0        0   +01:08:50 5427     0        0        0        0   +01:08:50 334      0        0        0        0   +01:08:50 5427     0        0        0        0   +01:08:50 0        0        0        0        0   +01:08:50 0        0        0        0        0   +01:08:50 5427     0        0        0        0   +01:08:50 0        0        0        0        0   +...... +``` + +## sysstat + +### 采集信息: + + +| **参数** | **含义** | +| ------------------ | ------------------------------------ | +| **active** | **LRU活跃内存大小** | +| **inactive** | **LRU不活跃内存大小** | +| **anon\_active** | **活跃匿名内存大小** | +| **anon\_inactive** | **不活跃匿名内存大小** | +| **file\_active** | **活跃文件映射内存大小** | +| **file\_inactive** | **不活跃文件映射内存大小** | +| **unevictable** | **不可回收内存大小** | +| **dirty** | **脏页大小** | +| **writeback** | **正在回写的内存大小** | +| **anonpages** | **RMAP页面** | +| **mapped** | **所有映射到用户地址空间的内存大小** | +| **shmem** | **共享内存** | +| **kreclaimable** | **内核可回收内存** | +| **slab** | **用于slab的内存大小** | +| **sreclaimable** | **可回收slab内存** | +| **sunreclaim** | **不可回收slab内存** | +| **NFS\_unstable** | **NFS中还没写到磁盘中的内存** | +| **writebacktmp** | **回写所使用的临时缓存大小** | +| **anonhugepages** | **透明巨页大小** | +| **shmemhugepages** | **shmem或tmpfs使用的透明巨页** | + +### 功能 + +**提取各种类型内存的活动和非活动页面数量,以及其他内存回收相关的统计数据,除了常规的事件信息外,程序还输出了与内存管理相关的详细信息,包括了不同类型内存的活动(active)和非活动(inactive)页面,未被驱逐(unevictable)页面,脏(dirty)页面,写回(writeback)页面,映射(mapped)页面,以及各种类型的内存回收相关统计数据。** + +### 挂载点及挂载原因 + +**挂载点:get\_page\_from\_freelist** + +**原因:** + +**首先,内存状态数据的提取需要获取到内存节点pglist\_data数据结构,这个结构是对内存的总体抽象。pglist\_data数据结构末尾有个vm\_stat的数组,里面包含了当前内存节点所有的状态信息。所有只需要获取到pglist\_data结构就能拿到当前的内存状态信息。但是物理内存分配在选择内存节点是通过mempolicy结构获取,无法获得具体的节点结构。选择内存节点的函数处理流程如下:** + +``` +struct mempolicy *get_task_policy(struct task_struct *p) +{ +        struct mempolicy *pol = p->mempolicy;//根据当前task_struct取得 +        int node; + +        if (pol) +                return pol; + +        node = numa_node_id(); +        if (node != NUMA_NO_NODE) {//存在其他节点 +                pol = &preferred_node_policy[node]; +                /* preferred_node_policy is not initialised early in boot */ +                if (pol->mode) +                        return pol; +       }   + +        return &default_policy;//不存在其他节点返回本地节点 +} +``` + +**经过对内存申请的内部结构alloc\_context分析(这是内存申请过程中临时保存相关参数的结构),当前内存节点是可以通过:alloc\_context——>zoneref——>zone——>pglist\_data的路径访问到。** + +**其次,因为函数执行申请内存的过程对获取内存节点数据的影响不大,所以只要可以获得alloc\_context数据结构,在整个申请路径上挂载函数都是可以的。sysstat工具选择的挂载点是get\_page\_from\_freelist函数。这个函数是快速物理内存分配的入口函数。因为内核在进行物理内存分配时,都会进入快速路径分配,只有当失败时才会进入慢速路径,所以get\_page\_from\_freelist函数是必经函数。** + +**但是,经过对proc文件系统的打印函数meminfo\_proc\_show函数的分析得知,影响内存性能的参数在vm\_stat中无法全部获得。一部分数据需要遍历当前内存节点包含的所有内存管理区zone结构中vm\_stat数组获得,一部分需要读取全局变量vm\_node\_stat获得。但是内核的全局变量不会作为函数参数参与数据处理,目前还没具体方法获得这部分数据。** + +### 存在问题 + +**■ 部分性能数据存在于内核全局变量中,而这些数据不会作为函数参数存储在栈中,因此这些数据目前还没实现统计** + +**■ 因为内核对内存管理不会是物理内存的全部容量,而且最大管理内存的数据结构是内存结点,所以以上统计数据是以当前内存结点实际管理的内存容量为基准。** + +**■ 当前剩余内存总量的统计需要遍历所有内存管理区来统计,但是由于内存管理区的空闲页面信息存储在数组第一个位置,使用指针指向时,统计到的数据不准确,使用变量统计会出现数据类型错误的报告。** + +### 可以解决的问题 + +**通过提取内存指标,可以及时发现潜在问题,如内存泄漏,从而采取相应措施。此外,了解各种内存类型的使用情况有助于合理分配资源,提高系统效率,并确保数据一致性。** + +### 使用方法和结果展示 + +``` +sudo ./mem_watcher -s +...... +ACTIVE   INACTVE  ANON_ACT ANON_INA FILE_ACT FILE_INA UNEVICT  DIRTY    WRITEBK  ANONPAG  MAP      SHMEM   +327644   2747936  1988     2278752  325656   469184   0        216      0        563728   249116   7832   +327652   2747616  1996     2278432  325656   469184   0        240      0        563844   249164   7832   +327652   2747616  1996     2278432  325656   469184   0        240      0        563864   249164   7832   +327652   2747844  1996     2278656  325656   469188   0        252      0        563864   249164   7832   +327652   2747844  1996     2278656  325656   469188   0        252      0        563884   249164   7832 +...... +``` + +## paf + +### 采集信息 + + +| **参数** | **含义** | +| ----------- | ---------------------------------------- | +| **min** | **内存管理区处于最低警戒水位的页面数量** | +| **low** | **内存管理区处于低水位的页面数量** | +| **high** | **内存管理区处于高水位的页面数量** | +| **present** | **内存管理区实际管理的页面数量** | +| **flag** | **申请页面时的权限(标志)** | + +### 功能 + +**主要是监控内核中的 **`get_page_from_freelist`函数。这个函数在内核中用于从内存空闲页列表中获取一个页面。** **程序主要是输出present(当前内存中可用的页面数量),min(在这个阈值下,系统可能会触发内存压缩),low(在这个阈值下,系统进行内存回收),high(在这个阈值上,认为内存资源充足),flag(用于内存分配的状态)。 + +### 注: + +**内存申请失败一般集中在申请权限不够或者是权限冲突导致,申请权限不够是当内核申请优先级较低的页面时,虽然内存管理区有足够的页面满足这次申请数量,但是当前剩余空闲页面少于最低警戒水位,因此导致内核无法成功分配页面的情况。权限冲突,例如内核在开启CMA机制下导致的页面页面申请失败的情况,这种情况下管理区空闲页面需要减去CMA机制占用内存才是当前可分配内存。相关权限判断代码如下:** + +**添加CMA权限代码路径mm/page\_alloc.c** + +``` +static inline unsigned int +gfp_to_alloc_flags(gfp_t gfp_mask) +{ + unsigned int alloc_flags = ALLOC_WMARK_MIN | ALLOC_CPUSET; +   ... + alloc_flags |= (__force int) (gfp_mask & __GFP_HIGH); +   ... + if (gfp_mask & __GFP_KSWAPD_RECLAIM) + alloc_flags |= ALLOC_KSWAPD; +   +#ifdef CONFIG_CMA + if (gfpflags_to_migratetype(gfp_mask) == MIGRATE_MOVABLE) + alloc_flags |= ALLOC_CMA; +#endif + return alloc_flags; +} +``` + +**CMA机制内存处理代码:** + +``` +bool __zone_watermark_ok(struct zone *z, unsigned int order, unsigned long mark, +                         int classzone_idx, unsigned int alloc_flags, long free_pages) +{ +   ... +#ifdef CONFIG_CMA +        if (!(alloc_flags & ALLOC_CMA)) +                free_pages -= zone_page_state(z, NR_FREE_CMA_PAGES); +#endif   +   +        if (free_pages <= min + z->lowmem_reserve[classzone_idx]) +                return false; + ... +#ifdef CONFIG_CMA +       if ((alloc_flags & ALLOC_CMA) && +                !list_empty(&area->free_list[MIGRATE_CMA])) { +                        return true; +               } +#endif +} +``` + +**挂载点:get\_page\_from\_freelist** + +**原因:** + +**经过对内核源码的分析,页面申请失败分析工具的理想挂载点应该是慢速路径的入口函数(\_\_alloc\_pages\_slowpath)。但是这个函数不允许ebpf程序挂载,而且这个函数内部也不存在合理的挂载点,所有将函数挂载点选在快速路径的入口函数get\_page\_from\_freelist上。因为页面申请的控制结构体ac在这两个函数之间不存在信息更改,所以可以确保这两个函数传递的ac结构体是相同的,不会对提取出来的数据产生影响。为了确保数据确实是在页面申请失败的情况下才会打印数据,需要对alloc\_pages\_nodemask函数的返回值进行挂载,当alloc\_pages\_nodemask函数没有返回页面结构体page时,也就是页面申请失败的情况下单元提取的数据。** + +### 存在问题 + +**■ 打印出来的内存申请标志与申请内存传递进去的标志不符,分析原因可能内核在进行alloc\_pages函数之前有对标志位进行处理。** + +**■ 因为内存管理区的剩余内存空间处在vm\_stat数组第一位,经过分析,使用指针提取的数组第一个数据总是存在差异,需要调整。** + +**■ 对打印的标志位需要进一步解析,方便快速确认当前申请页面类型。** + +### 可以解决的问题 + +**可以帮助监控系统内存的使用情况,预测内存不足的可能性,以及在必要时触发相应的内存回收机制来保证系统的稳定性和性能。** + +### 使用方法和结果展示 + +``` +sudo ./mem_watcher -a +MIN      LOW       HIGH     PRESENT  FLAG   +262144   5100      6120     262144   1100dca +262144   5100      6120     262144   2800   +262144   5100      6120     262144   cc0   +262144   5100      6120     262144   d00   +262144   5100      6120     262144   2dc2   +...... +``` + +## pr + +### 采集信息 + + +| **参数** | **含义** | +| ------------------ | ------------------------------------------------ | +| **reclaim** | **要回收的页面数量** | +| **reclaimed** | **已经回收的页面数量** | +| **unqueue\_dirty** | **还没开始回写和还没在队列等待的脏页** | +| **congested** | **正在块设备上回写的页面,含写入交换空间的页面** | +| **writeback** | **正在回写的页面** | + +### 功能 + +**主要用于监控内核中的 **`shrink_page_list`函数。** **整个BPF程序的功能是监控 `shrink_page_list`函数的调用,当函数被调用时,记录特定的内核数据(包括 `nr_reclaimed`等值),并将这些数据存储在环形缓冲区中,以供用户空间程序使用。** **跟踪内核中页面的回收行为,记录回收的各个阶段,例如要回收的页面,以回收的页面,等待回收的脏页数,要写回的页数(包括交换空间中的页数)以及当前正在写回的页数。 + +### 挂载点与挂载原因 + +**挂载点** + +**shrink\_page\_list** + +**挂载原因** + +**shrink\_page\_list函数是页面回收后期指向函数,主要操作是遍历链表中每一个页面,根据页面的属性确定将页面添加到回收队列、活跃链表还是不活跃链表.这块遍历的链表是在上一级函数 shrink\_inactive\_list中定义的临时链表,因为一次最多扫描32个页面,所有链表最多含有32个页面。在shrink\_page\_list这个函数中还有一个重要操作是统计不同状态的页面数量并保存在scan\_control结构体中。而工具数据提取的位置就是找到这个结构体并获取有关性能指标。因为这个提取的数据都是内核函数实时更改的,所有具有较高准确性。** **scan\_control结构体是每次进行内存回收时都会被回收进程重新定义,所有会看到数据是一个增长状态,之后有回归0,这和挂载点也有一定关系。** + +### 可以解决的问题 + +**监测系统的页面回收和写回情况,对系统内存使用和性能优化非常重要。整体来说,这些参数可以帮助系统管理员或开发人员了解系统内存管理的情况,包括页面回收的效率、脏页处理情况以及写回操作的进度。** + +### 使用方法和结果展示 + +``` +sudo ./mem_watcher -p +RECLAIM RECLAIMED UNQUEUE CONGESTED WRITEBACK +16893 0 0 0 0 +16893 24 0 0 0 +16893 24 0 0 0 +16893 40 0 0 0 +16893 64 0 0 0 +16893 64 0 0 0 +16893 66 0 0 0 +...... +``` + +## memleak + +### 计算方式 + +1. **当调用内存分配相关的函数(如malloc、calloc等)时,程序记录分配的大小并跟踪分配返回的地址。这涉及更新两个BPF映射,一个记录PID与分配大小的对应关系,另一个记录分配地址与分配详情的对应关系。** +2. **当调用free或相关函数时,程序查找之前记录的分配地址,若找到,则更新或删除对应映射中的记录。** +3. **对于每次分配,程序尝试获取调用堆栈的ID,这通过bpf\_get\_stackid()实现。堆栈ID用于识别特定的调用序列,帮助理解分配发生的上下文。** +4. **使用堆栈ID将相同堆栈上的分配合并,记录总分配大小和次数。这涉及到在BPF映射中累加新的分配或减去释放的分配。程序使用****sync\_fetch\_and\_add和**sync\_fetch\_and\_sub等原子操作来更新共享数据。这确保即使在高并发的环境下,数据更新也是安全的。 + +### 采集信息 + + +| **参数** | **含义** | +| --------------- | ---------------------------------- | +| **stack\_id** | **触发分配的的调用堆栈ID** | +| **total\_size** | **表示程序中尚未被释放的内存总量** | +| **nr\_allocs** | **所有未释放分配的总次数** | +| **input\_addr** | **堆栈中的指令地址** | +| **name** | **符号名** | + +### 功能 + +**代码主要用于跟踪内核内存分配和释放的情况,并记录相关的统计信息。** + +### 可以解决的问题 + +**可以帮助用户准确的找出内存泄露的位置,更好的排查出所存在的问题。** + +### 使用方法和结果展示 + +**用户态内存泄漏** + +``` +sudo ./mem_watcher -l -P 2429 +...... +stack_id=0x3c14 with outstanding allocations: total_size=4 nr_allocs=1 +000055e032027205: alloc_v3 @ 0x11e9+0x1c /test_leak.c:11 +000055e032027228: alloc_v2 @ 0x120f+0x19 /test_leak.c:17 +000055e03202724b: alloc_v1 @ 0x1232+0x19 /test_leak.c:23 +000055e032027287: memory_leak @ 0x1255+0x32 /test_leak.c:35 +00007f1ca1d66609: start_thread @ 0x8530+0xd9 +stack_id=0x3c14 with outstanding allocations: total_size=8 nr_allocs=2 +000055e032027205: alloc_v3 @ 0x11e9+0x1c /test_leak.c:11 +000055e032027228: alloc_v2 @ 0x120f+0x19 /test_leak.c:17 +000055e03202724b: alloc_v1 @ 0x1232+0x19 /test_leak.c:23 +000055e032027287: memory_leak @ 0x1255+0x32 /test_leak.c:35 +00007f1ca1d66609: start_thread @ 0x8530+0xd9 +...... +``` + +**内核态内存泄漏** + +``` +sudo ./mem_watcher -l +...... +[19:49:22] Top 10 stacks with outstanding allocations: +ffffffff95127b02: __alloc_pages @ 0xffffffff951278a0+0x262 +ffffffff95127b02: __alloc_pages @ 0xffffffff951278a0+0x262 +ffffffff95147bb0: alloc_pages @ 0xffffffff95147b20+0x90 +ffffffff950b40e7: __page_cache_alloc @ 0xffffffff950b4060+0x87 +ffffffff950b75f0: pagecache_get_page @ 0xffffffff950b74a0+0x150 +ffffffff951e1f92: __getblk_gfp @ 0xffffffff951e1ea0+0xf2 +ffffffff952d143c: jbd2_journal_get_descriptor_buffer @ 0xffffffff952d13e0+0x5c +ffffffff952c724b: journal_submit_commit_record.part.0 @ 0xffffffff952c7210+0x3b +ffffffff952c8839: jbd2_journal_commit_transaction @ 0xffffffff952c7540+0x12f9 +ffffffff952ce166: kjournald2 @ 0xffffffff952ce0b0+0xb6 +...... +``` diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/drsnoop.bpf.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/drsnoop.bpf.c new file mode 100644 index 000000000..7fb09adc5 --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/drsnoop.bpf.c @@ -0,0 +1,90 @@ +#include "vmlinux.h" +#include +#include +#include +#include "mem_watcher.h" + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; + +// Define BPF maps +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 256 * 1024); + __type(key, u64); + __type(value, struct val_t); +} start SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} rb SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, u32); + __type(value, u64); +} vm_stat_map SEC(".maps"); + +struct trace_event_raw_mm_vmscan_direct_reclaim_end_template___x { + long unsigned int nr_reclaimed; +} __attribute__((preserve_access_index)); + +SEC("tracepoint/vmscan/mm_vmscan_direct_reclaim_begin") +int trace_mm_vmscan_direct_reclaim_begin(void *ctx) { + struct val_t val = {}; + u64 id = bpf_get_current_pid_tgid(); + u64 *vm_stat_addr; + __u32 key = 0; // 使用与用户态相同的键值 + + // Capture start timestamp and process information + if (bpf_get_current_comm(&val.name, sizeof(val.name)) == 0) { + val.id = id; + val.ts = bpf_ktime_get_ns(); + // Retrieve the vm_stat address from the map + vm_stat_addr = bpf_map_lookup_elem(&vm_stat_map, &key); + if (vm_stat_addr) { + bpf_probe_read_kernel(&val.vm_stat, sizeof(val.vm_stat), (const void *)*vm_stat_addr); + } + else { + bpf_printk("vm_stat address not found in map\n"); + } + bpf_map_update_elem(&start, &id, &val, BPF_ANY); + } + + return 0; +} + +SEC("tracepoint/vmscan/mm_vmscan_direct_reclaim_end") +int trace_mm_vmscan_direct_reclaim_end(void *ctx) { + struct trace_event_raw_mm_vmscan_direct_reclaim_end_template___x *args = ctx; + + u64 id = bpf_get_current_pid_tgid(); + struct val_t *valp; + struct data_t *data; + u64 ts = bpf_ktime_get_ns(); + + valp = bpf_map_lookup_elem(&start, &id); + if (!valp) { + bpf_printk("No start record found for PID %llu\n", id >> 32); + return 0; + } + + data = bpf_ringbuf_reserve(&rb, sizeof(*data), 0); + if (!data) { + bpf_printk("Failed to reserve space in ringbuf\n"); + return 0; + } + + data->id = valp->id; + data->delta = ts - valp->ts; + data->ts = ts / 1000; + bpf_probe_read_kernel(&data->name, sizeof(data->name), valp->name); + bpf_probe_read_kernel(&data->vm_stat, sizeof(data->vm_stat), valp->vm_stat); + data->nr_reclaimed = BPF_CORE_READ(args, nr_reclaimed); + + bpf_ringbuf_submit(data, 0); + bpf_map_delete_elem(&start, &id); + + return 0; +} diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/fraginfo.bpf.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/fraginfo.bpf.c new file mode 100644 index 000000000..9d199da7b --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/fraginfo.bpf.c @@ -0,0 +1,96 @@ +#include "vmlinux.h" +#include +#include +#include +#include "fraginfo.h" + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 102400); + __type(key, u64); + __type(value, struct zone_info); +} zones SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 102400); + __type(key, u64); + __type(value, struct pgdat_info); +} nodes SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 102400); + __type(key,struct order_zone); + __type(value, struct ctg_info); +} orders SEC(".maps"); + +static void fill_contig_page_info(struct zone *zone, unsigned int suitable_order, + struct contig_page_info *info) +{ + unsigned int order; + info->free_pages = 0; + info->free_blocks_total = 0; + info->free_blocks_suitable = 0; + for (order = 0; order <= MAX_ORDER; order++) { + unsigned long blocks; + unsigned long nr_free; + nr_free = BPF_CORE_READ(&zone->free_area[order], nr_free); + blocks = nr_free; + info->free_blocks_total += blocks; + info->free_pages += blocks << order; + if (order >= suitable_order) + info->free_blocks_suitable += blocks << (order - suitable_order); + } +} + +SEC("kprobe/get_page_from_freelist") +int BPF_KPROBE(get_page_from_freelist, gfp_t gfp_mask, unsigned int order, int alloc_flags, + const struct alloc_context *ac) +{ + // bpf_printk("1111"); + struct pgdat_info node_info = {}; + struct zone_info zone_data = {}; + + struct pglist_data *pgdat; + struct zoneref *zref; + struct zone *z; + int i; + unsigned int a_order; + + pgdat = BPF_CORE_READ(ac, preferred_zoneref, zone, zone_pgdat); + node_info.node_id = BPF_CORE_READ(pgdat, node_id); + node_info.nr_zones = BPF_CORE_READ(pgdat, nr_zones); + node_info.pgdat_ptr = (u64)pgdat; + u64 key = (u64)pgdat; + + bpf_map_update_elem(&nodes, &key, &node_info, BPF_ANY); + + for (i = 0; i < __MAX_NR_ZONES; i++) { + zref = &pgdat->node_zonelists[0]._zonerefs[i]; + z = BPF_CORE_READ(zref, zone); + if ((u64)z == 0) break; + zone_data.zone_ptr = (u64)z; + u64 zone_key = (u64)z; + zone_data.zone_start_pfn = BPF_CORE_READ(z, zone_start_pfn); + zone_data.spanned_pages = BPF_CORE_READ(z, spanned_pages); + zone_data.present_pages = BPF_CORE_READ(z, present_pages); + bpf_probe_read_kernel_str(zone_data.comm, sizeof(zone_data.comm), BPF_CORE_READ(z, name)); + for (a_order = 0; a_order <= MAX_ORDER; ++a_order) { + zone_data.order = a_order; + struct order_zone order_key = {}; + order_key.order = a_order; + if ((u64)z == 0) break; + order_key.zone_ptr = (u64)z; + struct contig_page_info ctg_info = {}; + fill_contig_page_info(z, a_order, &ctg_info); + bpf_map_update_elem(&orders,&order_key,&ctg_info,BPF_ANY); + } + + bpf_map_update_elem(&zones, &zone_key, &zone_data, BPF_ANY); + } + + return 0; +} diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/memleak.bpf.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/memleak.bpf.c similarity index 55% rename from eBPF_Supermarket/Memory_Subsystem/mem_watcher/memleak.bpf.c rename to eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/memleak.bpf.c index d8b5d0d31..00ef2efe1 100644 --- a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/memleak.bpf.c +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/memleak.bpf.c @@ -19,10 +19,10 @@ #include "vmlinux.h" #include #include +#include #include "mem_watcher.h" - -#define KERN_STACKID_FLAGS (0 | BPF_F_FAST_STACK_CMP) -#define USER_STACKID_FLAGS (0 | BPF_F_FAST_STACK_CMP | BPF_F_USER_STACK) + +const volatile __u64 stack_flags = 0; struct { __uint(type, BPF_MAP_TYPE_HASH); @@ -62,6 +62,20 @@ struct { __type(value, u64); // 用户态指针变量 memptr } memptrs SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, u64); /* alloc return address */ + __type(value, u64); /* timestamp */ + __uint(max_entries, 10240); +} addr_times SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, u64); /* alloc return address */ + __type(value, u64); /* timestamp */ + __uint(max_entries, 10240); +} first_time SEC(".maps"); + char LICENSE[] SEC("license") = "Dual BSD/GPL"; static int gen_alloc_enter(size_t size) { @@ -88,10 +102,14 @@ static int gen_alloc_exit2(void *ctx, u64 address) { bpf_map_delete_elem(&sizes, &pid); if (0 != address) { - info.stack_id = bpf_get_stackid(ctx, &stack_traces, USER_STACKID_FLAGS); + info.stack_id = bpf_get_stackid(ctx, &stack_traces, stack_flags); bpf_map_update_elem(&allocs, &addr, &info, BPF_ANY); + // Initialize the addr_times map to 0 + __u64 zero_ts = 0; + bpf_map_update_elem(&addr_times, &addr, &zero_ts, BPF_ANY); + union combined_alloc_info add_cinfo = { .total_size = info.size, .number_of_allocs = 1 @@ -135,6 +153,10 @@ static int gen_free_enter(const void *address) { bpf_map_delete_elem(&allocs, &addr); + // Initialize the addr_times map to 0 + __u64 zero_ts = 0; + bpf_map_update_elem(&addr_times, &addr, &zero_ts, BPF_ANY); + return 0; } @@ -258,4 +280,191 @@ int BPF_KPROBE(pvalloc_enter, size_t size) { SEC("uretprobe") int BPF_KRETPROBE(pvalloc_exit) { return gen_alloc_exit(ctx); +} + +struct trace_event_raw_kmem_alloc_node___x { + const void *ptr; + size_t bytes_alloc; +} __attribute__((preserve_access_index)); + +static __always_inline bool has_kmem_alloc_node(void) { + if (bpf_core_type_exists(struct trace_event_raw_kmem_alloc_node___x)) + return true; + return false; +} + +struct trace_event_raw_kmem_alloc___x { + const void *ptr; + size_t bytes_alloc; +} __attribute__((preserve_access_index)); + +struct trace_event_raw_kmalloc___x { + const void *ptr; + size_t bytes_alloc; +} __attribute__((preserve_access_index)); + +struct trace_event_raw_kmem_cache_alloc___x { + const void *ptr; + size_t bytes_alloc; +} __attribute__((preserve_access_index)); + +static __always_inline bool has_kmem_alloc(void) +{ + if (bpf_core_type_exists(struct trace_event_raw_kmem_alloc___x)) + return true; + return false; +} + +SEC("tracepoint/kmem/kmalloc") +int memleak__kmalloc(void *ctx) +{ + const void *ptr; + size_t bytes_alloc; + + if (has_kmem_alloc()) { + struct trace_event_raw_kmem_alloc___x *args = ctx; + ptr = BPF_CORE_READ(args, ptr); + bytes_alloc = BPF_CORE_READ(args, bytes_alloc); + } else { + struct trace_event_raw_kmalloc___x *args = ctx; + ptr = BPF_CORE_READ(args, ptr); + bytes_alloc = BPF_CORE_READ(args, bytes_alloc); + } + + gen_alloc_enter(bytes_alloc); + + return gen_alloc_exit2(ctx, (u64)ptr); +} + +SEC("tracepoint/kmem/kmalloc_node") +int memleak__kmalloc_node(void *ctx) +{ + const void *ptr; + size_t bytes_alloc; + + if (has_kmem_alloc_node()) { + struct trace_event_raw_kmem_alloc_node___x *args = ctx; + ptr = BPF_CORE_READ(args, ptr); + bytes_alloc = BPF_CORE_READ(args, bytes_alloc); + + gen_alloc_enter( bytes_alloc); + + return gen_alloc_exit2(ctx, (u64)ptr); + } else { + /* tracepoint is disabled if not exist, avoid compile warning */ + return 0; + } +} + +struct trace_event_raw_kmem_free___x { + const void *ptr; +} __attribute__((preserve_access_index)); + +struct trace_event_raw_kfree___x { + const void *ptr; +} __attribute__((preserve_access_index)); + +struct trace_event_raw_kmem_cache_free___x { + const void *ptr; +} __attribute__((preserve_access_index)); + +static __always_inline bool has_kfree() +{ + if (bpf_core_type_exists(struct trace_event_raw_kfree___x)) + return true; + return false; +} + +static __always_inline bool has_kmem_cache_free() +{ + if (bpf_core_type_exists(struct trace_event_raw_kmem_cache_free___x)) + return true; + return false; +} + +SEC("tracepoint/kmem/kfree") +int memleak__kfree(void *ctx) +{ + const void *ptr; + + if (has_kfree()) { + struct trace_event_raw_kfree___x *args = ctx; + ptr = BPF_CORE_READ(args, ptr); + } else { + struct trace_event_raw_kmem_free___x *args = ctx; + ptr = BPF_CORE_READ(args, ptr); + } + + return gen_free_enter(ptr); +} + +SEC("tracepoint/kmem/kmem_cache_alloc") +int memleak__kmem_cache_alloc(void *ctx) +{ + const void *ptr; + size_t bytes_alloc; + + if (has_kmem_alloc()) { + struct trace_event_raw_kmem_alloc___x *args = ctx; + ptr = BPF_CORE_READ(args, ptr); + bytes_alloc = BPF_CORE_READ(args, bytes_alloc); + } else { + struct trace_event_raw_kmem_cache_alloc___x *args = ctx; + ptr = BPF_CORE_READ(args, ptr); + bytes_alloc = BPF_CORE_READ(args, bytes_alloc); + } + + gen_alloc_enter(bytes_alloc); + + return gen_alloc_exit2(ctx, (u64)ptr); +} + +SEC("tracepoint/kmem/kmem_cache_alloc_node") +int memleak__kmem_cache_alloc_node(void *ctx) +{ + const void *ptr; + size_t bytes_alloc; + + if (has_kmem_alloc_node()) { + struct trace_event_raw_kmem_alloc_node___x *args = ctx; + ptr = BPF_CORE_READ(args, ptr); + bytes_alloc = BPF_CORE_READ(args, bytes_alloc); + + gen_alloc_enter(bytes_alloc); + + return gen_alloc_exit2(ctx, (u64)ptr); + } else { + /* tracepoint is disabled if not exist, avoid compile warning */ + return 0; + } +} + +SEC("tracepoint/kmem/kmem_cache_free") +int memleak__kmem_cache_free(void *ctx) +{ + const void *ptr; + + if (has_kmem_cache_free()) { + struct trace_event_raw_kmem_cache_free___x *args = ctx; + ptr = BPF_CORE_READ(args, ptr); + } else { + struct trace_event_raw_kmem_free___x *args = ctx; + ptr = BPF_CORE_READ(args, ptr); + } + + return gen_free_enter(ptr); +} + +SEC("tracepoint/kmem/mm_page_alloc") +int memleak__mm_page_alloc(struct trace_event_raw_mm_page_alloc *ctx) +{ + gen_alloc_enter(4096 << ctx->order); + + return gen_alloc_exit2(ctx, ctx->pfn); +} + +SEC("tracepoint/kmem/mm_page_free") +int memleak__mm_page_free(struct trace_event_raw_mm_page_free *ctx) +{ + return gen_free_enter((void *)ctx->pfn); } \ No newline at end of file diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/oomkiller.bpf.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/oomkiller.bpf.c new file mode 100644 index 000000000..68acdf317 --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/oomkiller.bpf.c @@ -0,0 +1,51 @@ +#include "vmlinux.h" +#include +#include +#include +#include "mem_watcher.h" + +char __license[] SEC("license") = "Dual MIT/GPL"; + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 1 << 24); +} events SEC(".maps"); + +SEC("kprobe/oom_kill_process") +int BPF_KPROBE(oom_kill_process, struct oom_control *oc, const char *message) { + // 打印 OOM 触发信息 + bpf_printk("oom_kill_process triggered\n"); + + // 准备存储事件数据 + struct event *task_info; + task_info = bpf_ringbuf_reserve(&events, sizeof(struct event), 0); + if (!task_info) { + return 0; + } + + // 获取被 OOM 杀死的进程 + struct task_struct *p; + bpf_probe_read(&p, sizeof(p), &oc->chosen); + bpf_probe_read(&task_info->oomkill_pid, sizeof(task_info->oomkill_pid), &p->pid); + + // 获取被杀进程的命令名 (comm) + bpf_probe_read(&task_info->comm, sizeof(task_info->comm), &p->comm); + + // 获取触发 OOM 的进程信息 + struct task_struct *trigger_task = (struct task_struct *)bpf_get_current_task(); + task_info->triggered_pid = BPF_CORE_READ(trigger_task, pid); + + // 获取未被杀掉的进程的内存页信息 + struct mm_struct *mm; + mm = BPF_CORE_READ(trigger_task, mm); + if (mm) { + task_info->mem_pages = BPF_CORE_READ(mm, total_vm); // 获取进程的虚拟内存页面总数 + } else { + task_info->mem_pages = 0; // 如果进程没有分配内存 + } + + // 提交事件 + bpf_ringbuf_submit(task_info, 0); + + return 0; +} diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/paf.bpf.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/paf.bpf.c similarity index 100% rename from eBPF_Supermarket/Memory_Subsystem/mem_watcher/paf.bpf.c rename to eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/paf.bpf.c diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/pr.bpf.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/pr.bpf.c similarity index 100% rename from eBPF_Supermarket/Memory_Subsystem/mem_watcher/pr.bpf.c rename to eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/pr.bpf.c diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/procstat.bpf.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/procstat.bpf.c similarity index 100% rename from eBPF_Supermarket/Memory_Subsystem/mem_watcher/procstat.bpf.c rename to eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/procstat.bpf.c diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/sysstat.bpf.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/sysstat.bpf.c similarity index 100% rename from eBPF_Supermarket/Memory_Subsystem/mem_watcher/sysstat.bpf.c rename to eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/sysstat.bpf.c diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/vmasnap.bpf.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/vmasnap.bpf.c new file mode 100644 index 000000000..550aa2fe0 --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/vmasnap.bpf.c @@ -0,0 +1,247 @@ +#include "vmlinux.h" +#include +#include +#include +#include "mem_watcher.h" + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; + +// BPF Map 用于存储插入操作的事件数据 +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 256 * 1024); + __type(key, u64); + __type(value, struct insert_event_t); +} insert_events SEC(".maps"); + +// BPF Map 用于存储查找操作的事件数据 +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 256 * 1024); + __type(key, u64); + __type(value, struct find_event_t); +} find_events SEC(".maps"); + +// =============================== +// 查找操作的 Trace 函数 +// =============================== + +// Trace function for vmacache_find +SEC("kprobe/vmacache_find") +int BPF_KPROBE(trace_vmacache_find, struct mm_struct *mm, unsigned long addr) { + u64 pid = bpf_get_current_pid_tgid() >> 32; + struct find_event_t event = {}; + event.timestamp = bpf_ktime_get_ns(); + event.addr = addr; + + // Initialize event with default values + event.vmacache_hit = 0; + + // Update map with initial event details + bpf_map_update_elem(&find_events, &pid, &event, BPF_ANY); + return 0; +} + +SEC("kretprobe/vmacache_find") +int BPF_KRETPROBE(trace_vmacache_find_ret) { + u64 pid = bpf_get_current_pid_tgid() >> 32; + struct find_event_t *event; + struct vm_area_struct *vma; + + // Look up the event for the current PID + event = bpf_map_lookup_elem(&find_events, &pid); + if (event) { + // Check the return value of vmacache_find + vma = (struct vm_area_struct *)PT_REGS_RC(ctx); + if (vma) { + // If vma is not NULL, set vmacache_hit to 1 + event->vmacache_hit = 1; + } else { + // If vma is NULL, set vmacache_hit to 0 + event->vmacache_hit = 0; + } + + // Update the event in the map + bpf_map_update_elem(&find_events, &pid, event, BPF_ANY); + } + return 0; +} + +// Trace function for find_vma +SEC("kprobe/find_vma") +int BPF_KPROBE(trace_find_vma, struct mm_struct *mm, unsigned long addr) { + u64 pid = bpf_get_current_pid_tgid() >> 32; + struct find_event_t event = {}; + event.timestamp = bpf_ktime_get_ns(); + event.addr = addr; + + // Initialize event with default values + event.vmacache_hit = 0; + + // Update map with initial event details + bpf_map_update_elem(&find_events, &pid, &event, BPF_ANY); + return 0; +} + +SEC("kretprobe/find_vma") +int BPF_KRETPROBE(trace_find_vma_ret) { + u64 pid = bpf_get_current_pid_tgid() >> 32; + struct find_event_t *event; + struct vm_area_struct *vma; + + event = bpf_map_lookup_elem(&find_events, &pid); + if (event) { + event->duration = bpf_ktime_get_ns() - event->timestamp; + + // Retrieve vma and rb_subtree_last + vma = (struct vm_area_struct *)PT_REGS_RC(ctx); + if (vma) { + u64 rb_subtree_last = BPF_CORE_READ(vma, shared.rb_subtree_last); + u64 vm_start = BPF_CORE_READ(vma, vm_start); + u64 vm_end = BPF_CORE_READ(vma, vm_end); + + event->rb_subtree_last = rb_subtree_last; + event->vm_start = vm_start; + event->vm_end = vm_end; + bpf_printk("pid=%llu\n", pid); + bpf_printk("find_vma addr=%lu, duration=%llu ns, rb_subtree_last=%llu\n", + event->addr, event->duration, event->rb_subtree_last); + bpf_printk("vm_start=%llu, vm_end=%llu\n", + vm_start, vm_end); + bpf_printk("vmacache_hit=%d\n", event->vmacache_hit); + } + else { + bpf_printk("find_vma addr=%lu, duration=%llu ns, rb_subtree_last=not_available, vmacache_hit=%d\n", + event->addr, event->duration, event->vmacache_hit); + } + + // bpf_map_delete_elem(&find_events, &pid); + } + return 0; +} + +// =============================== +// 插入操作的 Trace 函数 +// =============================== + +// Trace function for insert_vm_struct +SEC("kprobe/insert_vm_struct") +int BPF_KPROBE(trace_insert_vm_struct, struct mm_struct *mm, struct vm_area_struct *vma) { + u64 pid = bpf_get_current_pid_tgid() >> 32; + struct insert_event_t event = {}; + event.timestamp = bpf_ktime_get_ns(); + + // Initialize event with default values + event.inserted_to_list = 0; + event.inserted_to_rb = 0; + event.inserted_to_interval_tree = 0; + event.link_list_duration = 0; + event.link_rb_duration = 0; + event.interval_tree_duration = 0; + + // Update map with initial event details + bpf_map_update_elem(&insert_events, &pid, &event, BPF_ANY); + + return 0; +} + +// Kprobe for __vma_link_list +SEC("kprobe/__vma_link_list") +int BPF_KPROBE(trace_vma_link_list, struct mm_struct *mm, struct vm_area_struct *vma, struct vm_area_struct *prev) { + u64 pid = bpf_get_current_pid_tgid() >> 32; + u64 timestamp = bpf_ktime_get_ns(); + struct insert_event_t *event = bpf_map_lookup_elem(&insert_events, &pid); + if (event) { + event->inserted_to_list = 1; + event->link_list_start_time = timestamp; + bpf_map_update_elem(&insert_events, &pid, event, BPF_ANY); + } + return 0; +} + +// Kprobe for __vma_link_rb +SEC("kprobe/__vma_link_rb") +int BPF_KPROBE(trace_vma_link_rb, struct mm_struct *mm, struct vm_area_struct *vma, struct vm_area_struct *prev, struct rb_node **rb_link, struct rb_node *rb_parent) { + u64 pid = bpf_get_current_pid_tgid() >> 32; + u64 timestamp = bpf_ktime_get_ns(); + struct insert_event_t *event = bpf_map_lookup_elem(&insert_events, &pid); + if (event) { + event->inserted_to_rb = 1; + event->link_rb_start_time = timestamp; + bpf_map_update_elem(&insert_events, &pid, event, BPF_ANY); + } + return 0; +} + +// Kprobe for vma_interval_tree_insert +SEC("kprobe/vma_interval_tree_insert") +int BPF_KPROBE(trace_vma_interval_tree_insert, struct vm_area_struct *vma, struct interval_tree_node *node) { + u64 pid = bpf_get_current_pid_tgid() >> 32; + u64 timestamp = bpf_ktime_get_ns(); + struct insert_event_t *event = bpf_map_lookup_elem(&insert_events, &pid); + if (event) { + event->inserted_to_interval_tree = 1; + event->interval_tree_start_time = timestamp; + bpf_map_update_elem(&insert_events, &pid, event, BPF_ANY); + } + return 0; +} + +// Return probe for insert_vm_struct +SEC("kretprobe/insert_vm_struct") +int BPF_KRETPROBE(trace_insert_vm_struct_ret) { + u64 pid = bpf_get_current_pid_tgid() >> 32; + struct insert_event_t *event = bpf_map_lookup_elem(&insert_events, &pid); + if (event) { + event->duration = bpf_ktime_get_ns() - event->timestamp; + + // Output the result + bpf_printk("insert_vm_struct duration=%llu ns, list=%d, rb=%d\n", + event->duration, event->inserted_to_list, + event->inserted_to_rb); + bpf_printk("interval_tree=%d\n", event->inserted_to_interval_tree); + + // Remove the event after processing + // bpf_map_delete_elem(&insert_events, &pid); + } + return 0; +} + +// Return probe for __vma_link_list +SEC("kretprobe/__vma_link_list") +int BPF_KRETPROBE(trace_vma_link_list_ret) { + u64 pid = bpf_get_current_pid_tgid() >> 32; + struct insert_event_t *event = bpf_map_lookup_elem(&insert_events, &pid); + if (event) { + u64 end_time = bpf_ktime_get_ns(); + event->link_list_duration = end_time - event->link_list_start_time; + bpf_printk("__vma_link_list duration=%llu ns\n", event->link_list_duration); + } + return 0; +} + +// Return probe for __vma_link_rb +SEC("kretprobe/__vma_link_rb") +int BPF_KRETPROBE(trace_vma_link_rb_ret) { + u64 pid = bpf_get_current_pid_tgid() >> 32; + struct insert_event_t *event = bpf_map_lookup_elem(&insert_events, &pid); + if (event) { + u64 end_time = bpf_ktime_get_ns(); + event->link_rb_duration = end_time - event->link_rb_start_time; + bpf_printk("__vma_link_rb duration=%llu ns\n", event->link_rb_duration); + } + return 0; +} + +// Return probe for vma_interval_tree_insert +SEC("kretprobe/vma_interval_tree_insert") +int BPF_KRETPROBE(trace_vma_interval_tree_insert_ret) { + u64 pid = bpf_get_current_pid_tgid() >> 32; + struct insert_event_t *event = bpf_map_lookup_elem(&insert_events, &pid); + if (event) { + u64 end_time = bpf_ktime_get_ns(); + event->interval_tree_duration = end_time - event->interval_tree_start_time; + bpf_printk("vma_interval_tree_insert duration=%llu ns\n", event->interval_tree_duration); + } + return 0; +} \ No newline at end of file diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/include/fraginfo.h b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/include/fraginfo.h new file mode 100644 index 000000000..530e62065 --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/include/fraginfo.h @@ -0,0 +1,35 @@ +#ifndef FRAGINFO_H +#define FRAGINFO_H + +#define MAX_ORDER 10 +typedef __u64 u64; +struct order_zone{ + unsigned int order; + u64 zone_ptr; +}; +struct ctg_info { + long unsigned int free_pages; + long unsigned int free_blocks_total; + long unsigned int free_blocks_suitable; +}; + +struct zone_info +{ + u64 zone_ptr; + u64 zone_start_pfn; + //spanned_pages: 代表的是这个zone中所有的页,包含空洞,计算公式是: zone_end_pfn - zone_start_pfn + //present_pages: 代表的是这个zone中可用的所有物理页,计算公式是:spanned_pages-hole_pages + u64 spanned_pages; + u64 present_pages; + char comm[32]; + unsigned int order; +}; + +struct pgdat_info +{ + u64 pgdat_ptr; + int nr_zones; + int node_id; +}; + +#endif diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/include/mem_watcher.h b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/include/mem_watcher.h new file mode 100644 index 000000000..b05ef1200 --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/include/mem_watcher.h @@ -0,0 +1,203 @@ +#ifndef __MEM_WATCHER_H +#define __MEM_WATCHER_H + +#define TASK_COMM_LEN 16 +#define MAX_FILENAME_LEN 127 + +#define ___GFP_DMA 0x01u +#define ___GFP_HIGHMEM 0x02u +#define ___GFP_DMA32 0x04u +#define ___GFP_MOVABLE 0x08u +#define ___GFP_RECLAIMABLE 0x10u +#define ___GFP_HIGH 0x20u +#define ___GFP_IO 0x40u +#define ___GFP_FS 0x80u +#define ___GFP_ZERO 0x100u +#define ___GFP_ATOMIC 0x200u +#define ___GFP_DIRECT_RECLAIM 0x400u +#define ___GFP_KSWAPD_RECLAIM 0x800u +#define ___GFP_WRITE 0x1000u +#define ___GFP_NOWARN 0x2000u +#define ___GFP_RETRY_MAYFAIL 0x4000u +#define ___GFP_NOFAIL 0x8000u +#define ___GFP_NORETRY 0x10000u +#define ___GFP_MEMALLOC 0x20000u +#define ___GFP_COMP 0x40000u +#define ___GFP_NOMEMALLOC 0x80000u +#define ___GFP_HARDWALL 0x100000u +#define ___GFP_THISNODE 0x200000u +#define ___GFP_ACCOUNT 0x400000u +#define ___GFP_ZEROTAGS 0x800000u +#define ___GFP_SKIP_KASAN_POISON 0x1000000u + +#define GFP_ATOMIC (___GFP_HIGH|___GFP_ATOMIC|___GFP_KSWAPD_RECLAIM) +#define GFP_KERNEL (___GFP_RECLAIMABLE | ___GFP_IO | ___GFP_FS) +#define GFP_KERNEL_ACCOUNT (GFP_KERNEL | ___GFP_ACCOUNT) +#define GFP_NOWAIT (___GFP_KSWAPD_RECLAIM) +#define GFP_NOIO (___GFP_RECLAIMABLE) +#define GFP_NOFS (___GFP_RECLAIMABLE | ___GFP_IO) +#define GFP_USER (___GFP_RECLAIMABLE | ___GFP_IO | ___GFP_FS | ___GFP_HARDWALL) +#define GFP_DMA ___GFP_DMA +#define GFP_DMA32 ___GFP_DMA32 +#define GFP_HIGHUSER (GFP_USER | ___GFP_HIGHMEM) +#define GFP_HIGHUSER_MOVABLE (GFP_HIGHUSER | ___GFP_MOVABLE | ___GFP_SKIP_KASAN_POISON) +#define GFP_TRANSHUGE_LIGHT ((GFP_HIGHUSER_MOVABLE | ___GFP_COMP | ___GFP_NOMEMALLOC | ___GFP_NOWARN) & ~___GFP_RECLAIMABLE) +#define GFP_TRANSHUGE (GFP_TRANSHUGE_LIGHT | ___GFP_DIRECT_RECLAIM) + +struct paf_event { + unsigned long min; + unsigned long low; + unsigned long high; + unsigned long present; + unsigned long protection; + int flag; +}; + +struct pr_event { + unsigned long reclaim; + unsigned long reclaimed; + unsigned int unqueued_dirty; + unsigned int congested; + unsigned int writeback; +}; + +struct procstat_event { + /*进程内存状态报告*/ + pid_t pid; + long nvcsw; + long nivcsw; + long vsize; //虚拟内存 + long size; //物理内存 + long long rssanon; //匿名页面 + long long rssfile; //文件页面 + long long rssshmem; //共享页面 + long long vswap; //交换页面 + long long Hpages; //hugetlbPages + long Vdata; //Private data segments + long Vstk; //User stack + long long VPTE; +}; + +struct sysstat_event { + /*系统内存状态报告*/ + unsigned long present; + unsigned long anon_inactive; // 0 + unsigned long anon_active; // 1 + unsigned long file_inactive; // 2 + unsigned long file_active; // 3 + unsigned long unevictable; // 不可回收页面 + unsigned long slab_reclaimable; + unsigned long slab_unreclaimable; + unsigned long anon_isolated; // 匿名隔离页面 + unsigned long file_isolated; // 文件隔离页面 + + unsigned long working_nodes; // 12 + unsigned long working_refault; + unsigned long working_activate; + unsigned long working_restore; + unsigned long working_nodereclaim; + + unsigned long anon_mapped; // 17 + unsigned long file_mapped; + + unsigned long file_pages; // 19 + unsigned long file_dirty; + unsigned long writeback; + unsigned long writeback_temp; + + unsigned long shmem; // 共享内存23 + unsigned long shmem_thps; + unsigned long pmdmapped; + unsigned long anon_thps; + unsigned long unstable_nfs; + unsigned long vmscan_write; + unsigned long vmscan_immediate; + + unsigned long diried; + unsigned long written; + unsigned long kernel_misc_reclaimable; +}; + +/*memleak.h*/ +#define ALLOCS_MAX_ENTRIES 1000000 +#define COMBINED_ALLOCS_MAX_ENTRIES 10240 + +struct alloc_info { + __u64 size; + int stack_id; +}; + +union combined_alloc_info { + struct { + __u64 total_size : 40; + __u64 number_of_allocs : 24; + }; + __u64 bits; +}; + +/* vmasnap.h */ +// 记录插入操作的事件数据 +struct insert_event_t { + unsigned long long timestamp; + unsigned long long duration; + int inserted_to_list; + int inserted_to_rb; + int inserted_to_interval_tree; + unsigned long long link_list_start_time; + unsigned long long link_rb_start_time; + unsigned long long interval_tree_start_time; + unsigned long long link_list_duration; + unsigned long long link_rb_duration; + unsigned long long interval_tree_duration; +}; + +// 记录查找操作的事件数据 +struct find_event_t { + unsigned long long timestamp; + unsigned long long duration; + unsigned long addr; + int vmacache_hit; + unsigned long long rb_subtree_last; + unsigned long long vm_start; + unsigned long long vm_end; +}; + +/* drsnoop.h */ +#define KALLSYMS_PATH "/proc/kallsyms" +#define VM_STAT_SYMBOL "vm_stat" +#define VM_ZONE_STAT_SYMBOL "vm_zone_stat" + +#define NR_VM_ZONE_STAT_ITEMS 5 +#define TASK_COMM_LEN 16 +#define NR_FREE_PAGES 0 + +#define PAGE_SHIFT 12 +#define K(x) ((x) << (PAGE_SHIFT - 10)) + +// Define structures used in maps and tracepoints +struct val_t { + unsigned long long id; + unsigned long long ts; // start time + char name[TASK_COMM_LEN]; + unsigned long long vm_stat[NR_VM_ZONE_STAT_ITEMS]; +}; + +struct data_t { + unsigned long long id; + unsigned long uid; + unsigned long long nr_reclaimed; + unsigned long long delta; + unsigned long long ts; // end time + char name[TASK_COMM_LEN]; + unsigned long long vm_stat[NR_VM_ZONE_STAT_ITEMS]; +}; + +/* OOM Killer Event */ +struct event { + uint32_t triggered_pid; // 触发 OOM 的进程 PID + uint32_t oomkill_pid; // 被 OOM 杀死的进程 PID + uint32_t mem_pages; // 没有被杀掉的进程所使用的内存页数 + char comm[TASK_COMM_LEN]; // 被杀死进程的命令名 +}; + +#endif /* __MEM_WATCHER_H */ diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.bpf.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.bpf.c deleted file mode 100644 index 2872e867e..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.bpf.c +++ /dev/null @@ -1,14 +0,0 @@ - -/* 请一定注意vmlinux.h头文件是依赖于特定架构的,本机编译的时候需要自行生成, -生成方法: -1、切换至本代码../../vmlinux/你的架构目录下; -2、安装Linux开发工具包:sudo apt install linux-tools-$(uname -r) -3、删除那个vmlinux_数字.h文件(记住它的名字); -4、生成vmlinux.h文件:bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h -5、将生成的vmlinux.h文件名字改成刚刚删除的vmlinux_数字.h -如果编译不通过,提示找不到vmlinux.h文件,那么请在本代码同级目录下运行生成vmlinux.h命令 */ -#include "vmlinux.h" -#include -#include -#include -#include "mem_watcher.h" \ No newline at end of file diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.c index 8c5eb3dc6..7337615a6 100644 --- a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.c +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.c @@ -21,114 +21,331 @@ #include #include #include +#include #include #include #include +#include #include #include #include "paf.skel.h" #include "pr.skel.h" #include "procstat.skel.h" #include "sysstat.skel.h" +#include "fraginfo.skel.h" #include "memleak.skel.h" -#include "mem_watcher.h" +#include "vmasnap.skel.h" +#include "drsnoop.skel.h" +#include "mem_watcher.h" +#include "fraginfo.h" +#include "oomkiller.skel.h" #include "blazesym.h" -static const int perf_max_stack_depth = 127; //stack id 对应的堆栈的深度 -static const int stack_map_max_entries = 10240; //最大允许存储多少个stack_id +// 定义标志结构体 +typedef struct +{ + int flag; + const char *name; +} Flag; + +// 定义所有组合修饰符和单独标志位 +Flag gfp_combined_list[] = { + {GFP_ATOMIC, "GFP_ATOMIC"}, + {GFP_KERNEL, "GFP_KERNEL"}, + {GFP_KERNEL_ACCOUNT, "GFP_KERNEL_ACCOUNT"}, + {GFP_NOWAIT, "GFP_NOWAIT"}, + {GFP_NOIO, "GFP_NOIO"}, + {GFP_NOFS, "GFP_NOFS"}, + {GFP_USER, "GFP_USER"}, + {GFP_DMA, "GFP_DMA"}, + {GFP_DMA32, "GFP_DMA32"}, + {GFP_HIGHUSER, "GFP_HIGHUSER"}, + {GFP_HIGHUSER_MOVABLE, "GFP_HIGHUSER_MOVABLE"}, + {GFP_TRANSHUGE_LIGHT, "GFP_TRANSHUGE_LIGHT"}, + {GFP_TRANSHUGE, "GFP_TRANSHUGE"}, +}; + +Flag gfp_separate_list[] = { + {___GFP_DMA, "___GFP_DMA"}, + {___GFP_HIGHMEM, "___GFP_HIGHMEM"}, + {___GFP_DMA32, "___GFP_DMA32"}, + {___GFP_MOVABLE, "___GFP_MOVABLE"}, + {___GFP_RECLAIMABLE, "___GFP_RECLAIMABLE"}, + {___GFP_HIGH, "___GFP_HIGH"}, + {___GFP_IO, "___GFP_IO"}, + {___GFP_FS, "___GFP_FS"}, + {___GFP_ZERO, "___GFP_ZERO"}, + {___GFP_ATOMIC, "___GFP_ATOMIC"}, + {___GFP_DIRECT_RECLAIM, "___GFP_DIRECT_RECLAIM"}, + {___GFP_KSWAPD_RECLAIM, "___GFP_KSWAPD_RECLAIM"}, + {___GFP_WRITE, "___GFP_WRITE"}, + {___GFP_NOWARN, "___GFP_NOWARN"}, + {___GFP_RETRY_MAYFAIL, "___GFP_RETRY_MAYFAIL"}, + {___GFP_NOFAIL, "___GFP_NOFAIL"}, + {___GFP_NORETRY, "___GFP_NORETRY"}, + {___GFP_MEMALLOC, "___GFP_MEMALLOC"}, + {___GFP_COMP, "___GFP_COMP"}, + {___GFP_NOMEMALLOC, "___GFP_NOMEMALLOC"}, + {___GFP_HARDWALL, "___GFP_HARDWALL"}, + {___GFP_THISNODE, "___GFP_THISNODE"}, + {___GFP_ACCOUNT, "___GFP_ACCOUNT"}, + {___GFP_ZEROTAGS, "___GFP_ZEROTAGS"}, + {___GFP_SKIP_KASAN_POISON, "___GFP_SKIP_KASAN_POISON"}, +}; + +static const int perf_max_stack_depth = 127; // stack id 对应的堆栈的深度 +static const int stack_map_max_entries = 10240; // 最大允许存储多少个stack_id static __u64 *g_stacks = NULL; static size_t g_stacks_size = 0; +#define KERN_STACKID_FLAGS (0 | BPF_F_FAST_STACK_CMP) +#define USER_STACKID_FLAGS (0 | BPF_F_FAST_STACK_CMP | BPF_F_USER_STACK) + static struct blaze_symbolizer *symbolizer; static int attach_pid; +pid_t own_pid; static char binary_path[128] = {0}; - -#define __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe) \ - do { \ - LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts, \ - .func_name = #sym_name, \ - .retprobe = is_retprobe); \ - skel->links.prog_name = bpf_program__attach_uprobe_opts( \ - skel->progs.prog_name, \ - attach_pid, \ - binary_path, \ - 0, \ - &uprobe_opts); \ - } while (false) - -#define __CHECK_PROGRAM(skel, prog_name) \ - do { \ - if (!skel->links.prog_name) { \ - perror("no program attached for " #prog_name); \ - return -errno; \ - } \ - } while (false) - + +struct allocation +{ + int stack_id; + __u64 size; + size_t count; +}; +// ============================= fraginfo==================================== +struct order_entry +{ + struct order_zone okey; + struct ctg_info oinfo; +}; + +int compare_entries(const void *a, const void *b) +{ + struct order_entry *entryA = (struct order_entry *)a; + struct order_entry *entryB = (struct order_entry *)b; + + if (entryA->okey.zone_ptr != entryB->okey.zone_ptr) + { + return (entryA->okey.zone_ptr < entryB->okey.zone_ptr) ? -1 : 1; + } + else + { + return (entryA->okey.order < entryB->okey.order) ? -1 : 1; + } +} + +// ============================= fraginfo==================================== +static struct allocation *allocs; + +static volatile bool exiting = false; + +#define __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe) \ + do \ + { \ + LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts, \ + .func_name = #sym_name, \ + .retprobe = is_retprobe); \ + skel->links.prog_name = bpf_program__attach_uprobe_opts( \ + skel->progs.prog_name, \ + attach_pid, \ + binary_path, \ + 0, \ + &uprobe_opts); \ + } while (false) + +#define __CHECK_PROGRAM(skel, prog_name) \ + do \ + { \ + if (!skel->links.prog_name) \ + { \ + perror("no program attached for " #prog_name); \ + return -errno; \ + } \ + } while (false) + #define __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, is_retprobe) \ - do { \ - __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe); \ - __CHECK_PROGRAM(skel, prog_name); \ - } while (false) + do \ + { \ + __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe); \ + __CHECK_PROGRAM(skel, prog_name); \ + } while (false) #define ATTACH_UPROBE(skel, sym_name, prog_name) __ATTACH_UPROBE(skel, sym_name, prog_name, false) #define ATTACH_URETPROBE(skel, sym_name, prog_name) __ATTACH_UPROBE(skel, sym_name, prog_name, true) - + #define ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name) __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, false) #define ATTACH_URETPROBE_CHECKED(skel, sym_name, prog_name) __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, true) +#define PROCESS_SKEL(skel, func) \ + skel = func##_bpf__open(); \ + if (!skel) \ + { \ + fprintf(stderr, "Failed to open and load BPF skeleton\n"); \ + return 1; \ + } \ + process_##func(skel) + +#define POLL_RING_BUFFER(rb, timeout, err) \ + while (!exiting) \ + { \ + err = ring_buffer__poll(rb, timeout); \ + if (err == -EINTR) \ + { \ + err = 0; \ + break; \ + } \ + if (err < 0) \ + { \ + printf("Error polling perf buffer: %d\n", err); \ + break; \ + } \ + } -static struct env { - int time; - bool paf; - bool pr; - bool procstat; - bool sysstat; - bool memleak; - - bool part2; - - long choose_pid; - bool rss; +// 为 oomkiller 使用的宏,指定 map_name +#define LOAD_AND_ATTACH_SKELETON_WITH_MAP(skel, event, map_name) \ + do \ + { \ + err = event##_bpf__load(skel); \ + if (err) \ + { \ + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); \ + goto event##_cleanup; \ + } \ + \ + err = event##_bpf__attach(skel); \ + if (err) \ + { \ + fprintf(stderr, "Failed to attach BPF skeleton\n"); \ + goto event##_cleanup; \ + } \ + \ + rb = ring_buffer__new(bpf_map__fd(skel->maps.map_name), handle_event_##event, NULL, NULL); \ + if (!rb) \ + { \ + fprintf(stderr, "Failed to create ring buffer\n"); \ + goto event##_cleanup; \ + } \ + } while (0) + +// 保留原有逻辑的宏 +#define LOAD_AND_ATTACH_SKELETON(skel, event) \ + do \ + { \ + err = event##_bpf__load(skel); \ + if (err) \ + { \ + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); \ + goto event##_cleanup; \ + } \ + \ + err = event##_bpf__attach(skel); \ + if (err) \ + { \ + fprintf(stderr, "Failed to attach BPF skeleton\n"); \ + goto event##_cleanup; \ + } \ + \ + rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), handle_event_##event, NULL, NULL); \ + if (!rb) \ + { \ + fprintf(stderr, "Failed to create ring buffer\n"); \ + goto event##_cleanup; \ + } \ + } while (0) + +static struct env +{ + int time; // 最大运行时间,单位为秒 + bool paf; // 是否启用内存页面状态报告 + bool pr; // 是否启用页面回收状态报告 + bool procstat; // 是否启用进程内存状态报告 + bool sysstat; // 是否启用系统内存状态报告 + bool memleak; // 是否启用内核态/用户态内存泄漏检测 + bool fraginfo; // 是否启用内存碎片信息 + bool vmasnap; // 是否启用虚拟内存区域信息 + bool drsnoop; + bool kernel_trace; // 是否启用内核态跟踪 + bool print_time; // 是否打印地址申请时间 + int interval; // 打印间隔,单位为秒 + int duration; // 运行时长,单位为秒 + bool part2; // 是否启用系统内存状态报告的扩展部分 + bool oomkiller; // 是否启用oomkiller事件处理 + + long choose_pid; // 选择的进程号 + bool rss; // 是否打印进程页面信息 } env = { - .time = 0, - .paf = false, - .pr = false, - .procstat = false, - .sysstat = false, - .memleak = false, - .rss = false, - .part2 = false, + .time = 0, // 0 表示无限运行 + .paf = false, // 默认关闭内存页面状态报告 + .pr = false, // 默认关闭页面回收状态报告 + .procstat = false, // 默认关闭进程内存状态报告 + .sysstat = false, // 默认关闭系统内存状态报告 + .memleak = false, // 默认关闭内存泄漏检测 + .fraginfo = false, // 默认关闭内存碎片信息 + .vmasnap = false, // 默认关闭虚拟内存区域信息 + .drsnoop = false, + .kernel_trace = true, // 默认启用内核态跟踪 + .print_time = false, // 默认不打印地址申请时间 + .rss = false, // 默认不打印进程页面信息 + .part2 = false, // 默认关闭系统内存状态报告的扩展部分 + .oomkiller = false, // 默认关闭oomkiller事件处理 + .choose_pid = 0, // 默认不选择特定进程 + .interval = 1, // 默认打印间隔为1秒 + .duration = 10, // 默认持续运行10秒 }; + const char argp_program_doc[] = "mem_watcher is in use ....\n"; static const struct argp_option opts[] = { {0, 0, 0, 0, "select function:", 1}, - {"paf", 'a', 0, 0, "print paf (内存页面状态报告)", 2}, + {0, 0, 0, 0, "par:", 2}, + {"paf", 'a', 0, 0, "print paf (内存页面状态报告)"}, - {"pr", 'p', 0, 0, "print pr (页面回收状态报告)", 3}, + {0, 0, 0, 0, "pr:", 3}, + {"pr", 'p', 0, 0, "print pr (页面回收状态报告)"}, - {"procstat", 'r', 0, 0, "print procstat (进程内存状态报告)", 4}, - {0, 0, 0, 0, "procstat additional function:"}, + {0, 0, 0, 0, "procstat:", 4}, + {"procstat", 'r', 0, 0, "print procstat (进程内存状态报告)"}, {"choose_pid", 'P', "PID", 0, "选择进程号打印"}, {"Rss", 'R', NULL, 0, "打印进程页面", 5}, - {"sysstat", 's', 0, 0, "print sysstat (系统内存状态报告)", 6}, - {0, 0, 0, 0, "sysstat additional function:"}, + {0, 0, 0, 0, "sysstat:", 6}, + {"sysstat", 's', 0, 0, "print sysstat (系统内存状态报告)"}, + {"part2", 'n', NULL, 0, "系统内存状态报告2", 7}, - {"memleak", 'l', "PID", 0, "print memleak (内存泄漏检测)", 8}, + {0, 0, 0, 0, "memleak:", 8}, + {"memleak", 'l', 0, 0, "print memleak (内核态内存泄漏检测)", 8}, + {"choose_pid", 'P', "PID", 0, "选择进程号打印, print memleak (用户态内存泄漏检测)", 9}, + {"print_time", 'm', 0, 0, "打印申请地址时间 (用户态)", 10}, + {"print_time", 'f', 0, 0, "打印申请地址时间 (用户态)", 10}, + {"time", 't', "TIME-SEC", 0, "Max Running Time(0 for infinite)", 11}, + + {0, 0, 0, 0, "fraginfo:", 12}, + {"fraginfo", 'f', 0, 0, "print fraginfo", 12}, + {"interval", 'i', "INTERVAL", 0, "Print interval in seconds (default 1)"}, + {"duration", 'd', "DURATION", 0, "Total duration in seconds to run (default 10)"}, + + {0, 0, 0, 0, "vmasnap:", 13}, + {"vmasnap", 'v', 0, 0, "print vmasnap (虚拟内存区域信息)"}, + + {0, 0, 0, 0, "drsnoop:", 14}, + {"drsnoop", 'b', 0, 0, "print drsnoop (直接回收追踪信息)"}, + + {0, 0, 0, 0, "oomkiller:", 15}, // 新增的 oomkiller 选项 + {"oomkiller", 'o', 0, 0, "print oomkiller (内存不足时被杀死的进程信息)"}, - {"time", 't', "TIME-SEC", 0, "Max Running Time(0 for infinite)", 9}, {NULL, 'h', NULL, OPTION_HIDDEN, "show the full help"}, {0}, }; -static error_t parse_arg(int key, char *arg, struct argp_state *state) { - switch (key) { +static error_t parse_arg(int key, char *arg, struct argp_state *state) +{ + switch (key) + { case 't': env.time = strtol(arg, NULL, 10); if (env.time) @@ -143,15 +360,18 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) { case 'r': env.procstat = true; break; + case 'f': + env.fraginfo = true; + break; + case 'v': + env.vmasnap = true; + break; case 's': env.sysstat = true; break; case 'n': env.part2 = true; break; - case 'h': - argp_state_help(state, stderr, ARGP_HELP_STD_HELP); - break; case 'P': env.choose_pid = strtol(arg, NULL, 10); break; @@ -161,6 +381,24 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) { case 'l': env.memleak = true; break; + case 'b': + env.drsnoop = true; + break; + case 'm': + env.print_time = true; + break; + case 'o': // 处理 oomkiller 选项 + env.oomkiller = true; + break; + case 'h': + argp_state_help(state, stderr, ARGP_HELP_STD_HELP); + break; + case 'i': + env.interval = atoi(arg); + break; + case 'd': + env.duration = atoi(arg); + break; default: return ARGP_ERR_UNKNOWN; } @@ -173,35 +411,178 @@ static const struct argp argp = { .doc = argp_program_doc, }; -static void print_frame(const char *name, uintptr_t input_addr, uintptr_t addr, uint64_t offset, const blaze_symbolize_code_info *code_info) { + +// Function prototypes +// static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args); +static void sig_handler(int sig); +static void setup_signals(void); +static void disable_kernel_tracepoints(struct memleak_bpf *skel); +static void print_frame(const char *name, uintptr_t input_addr, uintptr_t addr, uint64_t offset, const blaze_symbolize_code_info *code_info); +static void show_stack_trace(__u64 *stack, int stack_sz, pid_t pid); +static int print_outstanding_allocs(struct memleak_bpf *skel); +static int print_outstanding_combined_allocs(struct memleak_bpf *skel, pid_t pid); +static int get_vm_stat_addr(__u64 *addr); +static int handle_event_paf(void *ctx, void *data, size_t data_sz); +static int handle_event_pr(void *ctx, void *data, size_t data_sz); +static int handle_event_procstat(void *ctx, void *data, size_t data_sz); +static int handle_event_sysstat(void *ctx, void *data, size_t data_sz); +static int handle_event_drsnoop(void *ctx, void *data, size_t data_sz); +static int attach_uprobes(struct memleak_bpf *skel); +static void print_flag_modifiers(int flag); +static int process_paf(struct paf_bpf *skel_paf); +static int process_pr(struct pr_bpf *skel_pr); +static int process_procstat(struct procstat_bpf *skel_procstat); +static int process_sysstat(struct sysstat_bpf *skel_sysstat); +static int process_memleak(struct memleak_bpf *skel_memleak, struct env); +static int process_fraginfo(struct fraginfo_bpf *skel_fraginfo); +static int process_vmasnap(struct vmasnap_bpf *skel_vmasnap); +static int process_drsnoop(struct drsnoop_bpf *skel_drsnoop); +static int process_oomkiller(struct oomkiller_bpf *skel_oomkiller); // 新增的oomkiller处理函数原型 +static int handle_event_oomkiller(void *ctx, void *data, size_t data_sz); // 新增的oomkiller事件处理函数 +static __u64 adjust_time_to_program_start_time(__u64 first_query_time); +static int update_addr_times(struct memleak_bpf *skel_memleak); +static int print_time(struct memleak_bpf *skel_memleak); +static void print_find_event_data(int map_fd); +static void print_insert_event_data(int map_fd); + +// Main function +int main(int argc, char **argv) +{ + int err; + struct paf_bpf *skel_paf; + struct pr_bpf *skel_pr; + struct procstat_bpf *skel_procstat; + struct sysstat_bpf *skel_sysstat; + struct memleak_bpf *skel_memleak; + struct fraginfo_bpf *skel_fraginfo; + struct vmasnap_bpf *skel_vmasnap; + struct oomkiller_bpf *skel_oomkiller; + struct drsnoop_bpf *skel_drsnoop; + + err = argp_parse(&argp, argc, argv, 0, NULL, NULL); + if (err) + return err; + + own_pid = getpid(); + libbpf_set_strict_mode(LIBBPF_STRICT_ALL); + + setup_signals(); + + if (env.paf) + { + PROCESS_SKEL(skel_paf, paf); + } + else if (env.pr) + { + PROCESS_SKEL(skel_pr, pr); + } + else if (env.procstat) + { + PROCESS_SKEL(skel_procstat, procstat); + } + else if (env.fraginfo) + { + PROCESS_SKEL(skel_fraginfo, fraginfo); + } + else if (env.vmasnap) + { + PROCESS_SKEL(skel_vmasnap, vmasnap); + } + else if (env.sysstat) + { + PROCESS_SKEL(skel_sysstat, sysstat); + } + else if (env.memleak) + { + if (env.choose_pid != 0) + { + printf("用户态内存泄漏\n"); + env.kernel_trace = false; + attach_pid = env.choose_pid; + } + else + attach_pid = 0; + + strcpy(binary_path, "/lib/x86_64-linux-gnu/libc.so.6"); + + allocs = calloc(ALLOCS_MAX_ENTRIES, sizeof(*allocs)); + + skel_memleak = memleak_bpf__open(); + if (!skel_memleak) + { + fprintf(stderr, "Failed to open BPF skeleton\n"); + return 1; + } + process_memleak(skel_memleak, env); + } + else if (env.oomkiller) // 处理 oomkiller + { + PROCESS_SKEL(skel_oomkiller, oomkiller); // 使用处理 oomkiller 的函数 + } + else if (env.drsnoop) + { + PROCESS_SKEL(skel_drsnoop, drsnoop); + } + + return 0; +} + + +int alloc_size_compare(const void *a, const void *b) +{ + const struct allocation *x = (struct allocation *)a; + const struct allocation *y = (struct allocation *)b; + + // descending order + + if (x->size > y->size) + return -1; + + if (x->size < y->size) + return 1; + + return 0; +} + +static void print_frame(const char *name, uintptr_t input_addr, uintptr_t addr, uint64_t offset, const blaze_symbolize_code_info *code_info) +{ // If we have an input address we have a new symbol. - if (input_addr != 0) { + if (input_addr != 0) + { printf("%016lx: %s @ 0x%lx+0x%lx", input_addr, name, addr, offset); - if (code_info != NULL && code_info->dir != NULL && code_info->file != NULL) { + if (code_info != NULL && code_info->dir != NULL && code_info->file != NULL) + { printf(" %s/%s:%u\n", code_info->dir, code_info->file, code_info->line); } - else if (code_info != NULL && code_info->file != NULL) { + else if (code_info != NULL && code_info->file != NULL) + { printf(" %s:%u\n", code_info->file, code_info->line); } - else { + else + { printf("\n"); } } - else { + else + { printf("%16s %s", "", name); - if (code_info != NULL && code_info->dir != NULL && code_info->file != NULL) { + if (code_info != NULL && code_info->dir != NULL && code_info->file != NULL) + { printf("@ %s/%s:%u [inlined]\n", code_info->dir, code_info->file, code_info->line); } - else if (code_info != NULL && code_info->file != NULL) { + else if (code_info != NULL && code_info->file != NULL) + { printf("@ %s:%u [inlined]\n", code_info->file, code_info->line); } - else { + else + { printf("[inlined]\n"); } } } -static void show_stack_trace(__u64 *stack, int stack_sz, pid_t pid) { +static void show_stack_trace(__u64 *stack, int stack_sz, pid_t pid) +{ const struct blaze_symbolize_inlined_fn *inlined; const struct blaze_result *result; const struct blaze_sym *sym; @@ -209,23 +590,26 @@ static void show_stack_trace(__u64 *stack, int stack_sz, pid_t pid) { assert(sizeof(uintptr_t) == sizeof(uint64_t)); - if (pid) { + if (pid) + { struct blaze_symbolize_src_process src = { .type_size = sizeof(src), .pid = pid, }; result = blaze_symbolize_process_abs_addrs(symbolizer, &src, (const uintptr_t *)stack, stack_sz); } - else { + else + { struct blaze_symbolize_src_kernel src = { .type_size = sizeof(src), }; result = blaze_symbolize_kernel_abs_addrs(symbolizer, &src, (const uintptr_t *)stack, stack_sz); } - - for (i = 0; i < stack_sz; i++) { - if (!result || result->cnt <= i || result->syms[i].name == NULL) { + for (i = 0; i < stack_sz; i++) + { + if (!result || result->cnt <= i || result->syms[i].name == NULL) + { printf("%016llx: \n", stack[i]); continue; } @@ -233,7 +617,8 @@ static void show_stack_trace(__u64 *stack, int stack_sz, pid_t pid) { sym = &result->syms[i]; print_frame(sym->name, stack[i], sym->addr, sym->offset, &sym->code_info); - for (j = 0; j < sym->inlined_cnt; j++) { + for (j = 0; j < sym->inlined_cnt; j++) + { inlined = &sym->inlined[j]; print_frame(sym->name, 0, 0, 0, &inlined->code_info); } @@ -242,102 +627,367 @@ static void show_stack_trace(__u64 *stack, int stack_sz, pid_t pid) { blaze_result_free(result); } -int print_outstanding_combined_allocs(struct memleak_bpf *skel, pid_t pid) { - const size_t combined_allocs_key_size = bpf_map__key_size(skel->maps.combined_allocs); - const size_t stack_traces_key_size = bpf_map__key_size(skel->maps.stack_traces); +int print_outstanding_allocs(struct memleak_bpf *skel) +{ + const size_t allocs_key_size = bpf_map__key_size(skel->maps.allocs); + + time_t t = time(NULL); + struct tm *tm = localtime(&t); - for (__u64 prev_key = 0, curr_key = 0; ; prev_key = curr_key) { + size_t nr_allocs = 0; - if (bpf_map__get_next_key(skel->maps.combined_allocs, - &prev_key, &curr_key, combined_allocs_key_size)) { - if (errno == ENOENT) { - break; //no more keys, done! - } - perror("map get next key failed!"); + // for each struct alloc_info "alloc_info" in the bpf map "allocs" + for (__u64 prev_key = 0, curr_key = 0;; prev_key = curr_key) + { + struct alloc_info alloc_info = {}; + memset(&alloc_info, 0, sizeof(alloc_info)); - return -errno; - } + if (bpf_map__get_next_key(skel->maps.allocs, &prev_key, &curr_key, allocs_key_size)) + { + if (errno == ENOENT) + { + break; // no more keys, done + } - // stack_id = curr_key - union combined_alloc_info cinfo; - memset(&cinfo, 0, sizeof(cinfo)); + perror("map get next key error"); - if (bpf_map__lookup_elem(skel->maps.combined_allocs, - &curr_key, combined_allocs_key_size, &cinfo, sizeof(cinfo), 0)) { - if (errno == ENOENT) { - continue; - } + return -errno; + } - perror("map lookup failed!"); - return -errno; - } + if (bpf_map__lookup_elem(skel->maps.allocs, &curr_key, allocs_key_size, &alloc_info, sizeof(alloc_info), 0)) + { + if (errno == ENOENT) + continue; - if (bpf_map__lookup_elem(skel->maps.stack_traces, - &curr_key, stack_traces_key_size, g_stacks, g_stacks_size, 0)) { - perror("failed to lookup stack traces!"); - return -errno; - } + perror("map lookup error"); - printf("stack_id=0x%llx with outstanding allocations: total_size=%llu nr_allocs=%llu\n", - curr_key, (__u64)cinfo.total_size, (__u64)cinfo.number_of_allocs); + return -errno; + } - int stack_sz = 0; - for (int i = 0; i < perf_max_stack_depth; i++) { - if (g_stacks[i] == 0) { - break; + // filter invalid stacks + if (alloc_info.stack_id < 0) + { + continue; + } + + // when the stack_id exists in the allocs array, + // increment size with alloc_info.size + bool stack_exists = false; + + for (size_t i = 0; !stack_exists && i < nr_allocs; ++i) + { + struct allocation *alloc = &allocs[i]; + + if (alloc->stack_id == alloc_info.stack_id) + { + alloc->size += alloc_info.size; + alloc->count++; + + stack_exists = true; + break; + } + } + + if (stack_exists) + continue; + + // when the stack_id does not exist in the allocs array, + // create a new entry in the array + struct allocation alloc = { + .stack_id = alloc_info.stack_id, + .size = alloc_info.size, + .count = 1, + }; + + memcpy(&allocs[nr_allocs], &alloc, sizeof(alloc)); + nr_allocs++; + } + + // sort the allocs array in descending order + qsort(allocs, nr_allocs, sizeof(allocs[0]), alloc_size_compare); + + // get min of allocs we stored vs the top N requested stacks + size_t nr_allocs_to_show = nr_allocs < 10 ? nr_allocs : 10; + + printf("[%d:%d:%d] Top %zu stacks with outstanding allocations:\n", + tm->tm_hour, tm->tm_min, tm->tm_sec, nr_allocs_to_show); + + for (size_t i = 0; i < nr_allocs_to_show; i++) + { + if (bpf_map__lookup_elem(skel->maps.stack_traces, + &allocs[i].stack_id, sizeof(allocs[i].stack_id), g_stacks, g_stacks_size, 0)) + { + perror("failed to lookup stack traces!"); + return -errno; + } + } + + show_stack_trace(g_stacks, nr_allocs_to_show, 0); + + return 0; +} + +int print_outstanding_combined_allocs(struct memleak_bpf *skel, pid_t pid) +{ + const size_t combined_allocs_key_size = bpf_map__key_size(skel->maps.combined_allocs); + const size_t stack_traces_key_size = bpf_map__key_size(skel->maps.stack_traces); + + for (__u64 prev_key = 0, curr_key = 0;; prev_key = curr_key) + { + + if (bpf_map__get_next_key(skel->maps.combined_allocs, + &prev_key, &curr_key, combined_allocs_key_size)) + { + if (errno == ENOENT) + { + break; // no more keys, done! + } + perror("map get next key failed!"); + + return -errno; + } + + // stack_id = curr_key + union combined_alloc_info cinfo; + memset(&cinfo, 0, sizeof(cinfo)); + + if (bpf_map__lookup_elem(skel->maps.combined_allocs, + &curr_key, combined_allocs_key_size, &cinfo, sizeof(cinfo), 0)) + { + if (errno == ENOENT) + { + continue; + } + + perror("map lookup failed!"); + return -errno; + } + + if (bpf_map__lookup_elem(skel->maps.stack_traces, + &curr_key, stack_traces_key_size, g_stacks, g_stacks_size, 0)) + { + perror("failed to lookup stack traces!"); + return -errno; + } + + printf("stack_id=0x%llx with outstanding allocations: total_size=%llu nr_allocs=%llu\n", + curr_key, (__u64)cinfo.total_size, (__u64)cinfo.number_of_allocs); + + int stack_sz = 0; + for (int i = 0; i < perf_max_stack_depth; i++) + { + if (g_stacks[i] == 0) + { + break; + } + stack_sz++; + // printf("[%3d] 0x%llx\n", i, g_stacks[i]); + } + + show_stack_trace(g_stacks, stack_sz, pid); + } + + return 0; +} + +// 在更新时间之前获取当前时间并调整为相对于程序启动时的时间 +static __u64 adjust_time_to_program_start_time(__u64 first_query_time) +{ + struct timespec current_time; + clock_gettime(CLOCK_MONOTONIC, ¤t_time); + // printf("current_time: %ld\n", current_time.tv_sec); + __u64 adjusted_time; + adjusted_time = current_time.tv_sec - first_query_time; + + // printf("adjusted_time: %lld\n", adjusted_time); + return adjusted_time; +} + +// 在更新时间时,先将时间调整为相对于程序启动的时间 +static int update_addr_times(struct memleak_bpf *skel) +{ + const size_t addr_times_key_size = bpf_map__key_size(skel->maps.addr_times); + const size_t first_time_key_size = bpf_map__key_size(skel->maps.first_time); + for (__u64 prev_key = 0, curr_key = 0;; prev_key = curr_key) + { + if (bpf_map__get_next_key(skel->maps.addr_times, &prev_key, &curr_key, addr_times_key_size)) + { + if (errno == ENOENT) + { + break; // no more keys, done! + } + + perror("map get next key failed!"); + return -errno; + } + + // Check if the address exists in the first_time map + __u64 first_query_time; + if (bpf_map__lookup_elem(skel->maps.first_time, &curr_key, first_time_key_size, &first_query_time, sizeof(first_query_time), 0)) + { + // Address doesn't exist in the first_time map, add it with the current time + struct timespec first_time_alloc; + clock_gettime(CLOCK_MONOTONIC, &first_time_alloc); + if (bpf_map__update_elem(skel->maps.first_time, &curr_key, first_time_key_size, &first_time_alloc.tv_sec, sizeof(first_time_alloc.tv_sec), 0)) + { + perror("map update failed!"); + return -errno; + } + } + else + { + // Address exists in the first_time map + // This is the first time updating timestamp + __u64 adjusted_time = adjust_time_to_program_start_time(first_query_time); + // printf("update_addr_times adjusted_time: %lld\n", adjusted_time); + + // Save the adjusted time to addr_times map + __u64 timestamp = adjusted_time; + + // write the updated timestamp back to the map + if (bpf_map__update_elem(skel->maps.addr_times, &curr_key, addr_times_key_size, ×tamp, sizeof(timestamp), 0)) + { + perror("map update failed!"); + return -errno; + } + } + } + return 0; +} + +// 在打印时间时,先将时间调整为相对于程序启动的时间 +int print_time(struct memleak_bpf *skel) +{ + const size_t addr_times_key_size = bpf_map__key_size(skel->maps.addr_times); + + printf("%-16s %12s\n", "AL_ADDR", "AL_Time(s)"); + + // Iterate over the addr_times map to print address and time + for (__u64 prev_key = 0, curr_key = 0;; prev_key = curr_key) + { + if (bpf_map__get_next_key(skel->maps.addr_times, &prev_key, &curr_key, addr_times_key_size)) + { + if (errno == ENOENT) + { + break; // no more keys, done! + } + perror("map get next key failed!"); + return -errno; + } + + // Read the timestamp for the current address + __u64 timestamp; + if (bpf_map__lookup_elem(skel->maps.addr_times, &curr_key, addr_times_key_size, ×tamp, sizeof(timestamp), 0) == 0) + { + printf("0x%-16llx %lld\n", curr_key, timestamp); + } + else + { + perror("map lookup failed!"); + return -errno; + } + } + return 0; +} + +void disable_kernel_tracepoints(struct memleak_bpf *skel) +{ + bpf_program__set_autoload(skel->progs.memleak__kmalloc, false); + bpf_program__set_autoload(skel->progs.memleak__kmalloc_node, false); + bpf_program__set_autoload(skel->progs.memleak__kfree, false); + bpf_program__set_autoload(skel->progs.memleak__kmem_cache_alloc, false); + bpf_program__set_autoload(skel->progs.memleak__kmem_cache_alloc_node, false); + bpf_program__set_autoload(skel->progs.memleak__kmem_cache_free, false); + bpf_program__set_autoload(skel->progs.memleak__mm_page_alloc, false); + bpf_program__set_autoload(skel->progs.memleak__mm_page_free, false); +} + +static int get_vm_stat_addr(__u64 *addr) +{ + FILE *file = fopen(KALLSYMS_PATH, "r"); + if (!file) { + perror("fopen"); + return -1; + } + + char line[256]; + while (fgets(line, sizeof(line), file)) { + unsigned long address; + char symbol[256]; + if (sscanf(line, "%lx %*s %s", &address, symbol) == 2) { + if (strcmp(symbol, VM_STAT_SYMBOL) == 0 || strcmp(symbol, VM_ZONE_STAT_SYMBOL) == 0) { + *addr = address; + fclose(file); + return 0; } - stack_sz++; - //printf("[%3d] 0x%llx\n", i, g_stacks[i]); } - - show_stack_trace(g_stacks, stack_sz, pid); } - return 0; + fclose(file); + return -1; // Symbol not found } -static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) { - return vfprintf(stderr, format, args); +// static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) +// { +// return vfprintf(stderr, format, args); +// } + +static void sig_handler(int sig) +{ + exiting = true; + exit(EXIT_SUCCESS); } -static volatile bool exiting = false; +static void setup_signals(void) +{ + signal(SIGINT, sig_handler); + signal(SIGTERM, sig_handler); + signal(SIGALRM, sig_handler); +} -static void sig_handler(int sig) { - exiting = true; - exit(EXIT_SUCCESS); -} - -/* -static char* flags(int flag) -{ - if(flag & GFP_ATOMIC) - return "GFP_ATOMIC"; - if(flag & GFP_KERNEL) - return "GFP_KERNEL"; - if(flag & GFP_KERNEL_ACCOUNT) - return "GFP_KERNEL_ACCOUNT"; - if(flag & GFP_NOWAIT) - return "GFP_NOWAIT"; - if(flag & GFP_NOIO ) - return "GFP_NOIO "; - if(flag & GFP_NOFS) - return "GFP_NOFS"; - if(flag & GFP_USER) - return "GFP_USER"; - if(flag & GFP_DMA) - return "GFP_DMA"; - if(flag & GFP_DMA32) - return "GFP_DMA32"; - if(flag & GFP_HIGHUSER) - return "GFP_HIGHUSER"; - if(flag & GFP_HIGHUSER_MOVABLE) - return "GFP_HIGHUSER_MOVABLE"; - if(flag & GFP_TRANSHUGE_LIGHT) - return "GFP_TRANSHUGE_LIGHT"; - return; -} -*/ -static int handle_event_paf(void *ctx, void *data, size_t data_sz) { +static void print_flag_modifiers(int flag) +{ + char combined[512] = {0}; // 用于保存组合修饰符 + char separate[512] = {0}; // 用于保存单独标志位 + + // 检查组合修饰符 + for (int i = 0; i < sizeof(gfp_combined_list) / sizeof(gfp_combined_list[0]); ++i) + { + if ((flag & gfp_combined_list[i].flag) == gfp_combined_list[i].flag) + { + strcat(combined, gfp_combined_list[i].name); + strcat(combined, " | "); + } + } + + // 移除最后一个 " | " 字符串的末尾 + if (strlen(combined) > 3) + { + combined[strlen(combined) - 3] = '\0'; + } + + // 检查单独标志位 + for (int i = 0; i < sizeof(gfp_separate_list) / sizeof(gfp_separate_list[0]); ++i) + { + if (flag & gfp_separate_list[i].flag) + { + strcat(separate, gfp_separate_list[i].name); + strcat(separate, " | "); + } + } + + // 移除最后一个 " | " 字符串的末尾 + if (strlen(separate) > 3) + { + separate[strlen(separate) - 3] = '\0'; + } + + // 打印组合修饰符和单独标志位 + printf("%-50s %-100s\n", combined, separate); +} + +static int handle_event_paf(void *ctx, void *data, size_t data_sz) +{ const struct paf_event *e = data; struct tm *tm; char ts[32]; @@ -347,13 +997,16 @@ static int handle_event_paf(void *ctx, void *data, size_t data_sz) { tm = localtime(&t); strftime(ts, sizeof(ts), "%H:%M:%S", tm); - printf("%-8lu %-8lu %-8lu %-8lu %-8x\n", - e->min, e->low, e->high, e->present, e->flag); + printf("%-8lu %-8lu %-8lu %-8lu %-8x ", + e->min, e->low, e->high, e->present, e->flag); + print_flag_modifiers(e->flag); + printf("\n"); return 0; } -static int handle_event_pr(void *ctx, void *data, size_t data_sz) { +static int handle_event_pr(void *ctx, void *data, size_t data_sz) +{ const struct pr_event *e = data; struct tm *tm; char ts[32]; @@ -364,12 +1017,13 @@ static int handle_event_pr(void *ctx, void *data, size_t data_sz) { strftime(ts, sizeof(ts), "%H:%M:%S", tm); printf("%-8lu %-8lu %-8u %-8u %-8u\n", - e->reclaim, e->reclaimed, e->unqueued_dirty, e->congested, e->writeback); + e->reclaim, e->reclaimed, e->unqueued_dirty, e->congested, e->writeback); return 0; } -static int handle_event_procstat(void *ctx, void *data, size_t data_sz) { +static int handle_event_procstat(void *ctx, void *data, size_t data_sz) +{ const struct procstat_event *e = data; struct tm *tm; char ts[32]; @@ -378,15 +1032,18 @@ static int handle_event_procstat(void *ctx, void *data, size_t data_sz) { time(&t); tm = localtime(&t); strftime(ts, sizeof(ts), "%H:%M:%S", tm); - if (env.choose_pid) { - if (e->pid == env.choose_pid) { + if (env.choose_pid) + { + if (e->pid == env.choose_pid) + { if (env.rss == true) printf("%-8s %-8d %-8ld %-8ld %-8ld %-8lld %-8lld\n", ts, e->pid, e->vsize, e->Vdata, e->Vstk, e->VPTE, e->vswap); else printf("%-8s %-8d %-8ld %-8lld %-8lld %-8lld\n", ts, e->pid, e->size, e->rssanon, e->rssfile, e->rssshmem); } } - else { + else + { if (env.rss == true) printf("%-8s %-8d %-8ld %-8ld %-8ld %-8lld %-8lld\n", ts, e->pid, e->vsize, e->Vdata, e->Vstk, e->VPTE, e->vswap); else @@ -396,7 +1053,8 @@ static int handle_event_procstat(void *ctx, void *data, size_t data_sz) { return 0; } -static int handle_event_sysstat(void *ctx, void *data, size_t data_sz) { +static int handle_event_sysstat(void *ctx, void *data, size_t data_sz) +{ const struct sysstat_event *e = data; struct tm *tm; char ts[32]; @@ -414,342 +1072,588 @@ static int handle_event_sysstat(void *ctx, void *data, size_t data_sz) { return 0; } -pid_t own_pid; +static int handle_event_drsnoop(void *ctx, void *data, size_t data_sz) +{ + const struct data_t *e = data; + struct tm *tm; + char ts[32]; + time_t t; + + time(&t); + tm = localtime(&t); + strftime(ts, sizeof(ts), "%H:%M:%S", tm); + + __u64 delta_us = e->delta / 1000; + __u64 delta_ms = delta_us / 1000; + __u64 fractional_us = delta_us % 1000; + + printf("%-8s %-16s %-7llu %-9llu %llu.%02llu\n", ts, e->name, e->id >> 32, K(e->vm_stat[NR_FREE_PAGES]), delta_ms, fractional_us); + + return 0; +} + +static int handle_event_oomkiller(void *ctx, void *data, size_t data_sz) +{ + const struct event *e = data; // 假设事件结构为 struct event + static int header_printed = 0; // 标记是否已经打印表头 + struct tm *tm; + char ts[32]; + time_t t; + + // 获取当前时间戳 + time(&t); + tm = localtime(&t); + strftime(ts, sizeof(ts), "%H:%M:%S", tm); + + // 打印表头,说明输出内容 (只打印一次) + if (!header_printed) { + printf("%-20s %-20s %-20s %-20s\n", + "触发 OOM 的进程 (PID)", "被杀进程 (PID)", "内存页数", "被杀进程的命令名"); + printf("----------------------------------------------------------------------------------------\n"); + header_printed = 1; + } + + // 打印事件数据,包含 OOM 事件的关键信息 + printf("%-20d %-20d %-20u %-20s\n", + e->triggered_pid, e->oomkill_pid, e->mem_pages, e->comm); + + return 0; +} -int attach_uprobes(struct memleak_bpf *skel) { - ATTACH_UPROBE_CHECKED(skel, malloc, malloc_enter); - ATTACH_URETPROBE_CHECKED(skel, malloc, malloc_exit); - ATTACH_UPROBE_CHECKED(skel, free, free_enter); +int attach_uprobes(struct memleak_bpf *skel) +{ + ATTACH_UPROBE_CHECKED(skel, malloc, malloc_enter); + ATTACH_URETPROBE_CHECKED(skel, malloc, malloc_exit); + ATTACH_UPROBE_CHECKED(skel, free, free_enter); - ATTACH_UPROBE_CHECKED(skel, posix_memalign, posix_memalign_enter); - ATTACH_URETPROBE_CHECKED(skel, posix_memalign, posix_memalign_exit); + ATTACH_UPROBE_CHECKED(skel, posix_memalign, posix_memalign_enter); + ATTACH_URETPROBE_CHECKED(skel, posix_memalign, posix_memalign_exit); - ATTACH_UPROBE_CHECKED(skel, calloc, calloc_enter); - ATTACH_URETPROBE_CHECKED(skel, calloc, calloc_exit); + ATTACH_UPROBE_CHECKED(skel, calloc, calloc_enter); + ATTACH_URETPROBE_CHECKED(skel, calloc, calloc_exit); - ATTACH_UPROBE_CHECKED(skel, realloc, realloc_enter); - ATTACH_URETPROBE_CHECKED(skel, realloc, realloc_exit); + ATTACH_UPROBE_CHECKED(skel, realloc, realloc_enter); + ATTACH_URETPROBE_CHECKED(skel, realloc, realloc_exit); - ATTACH_UPROBE_CHECKED(skel, mmap, mmap_enter); - ATTACH_URETPROBE_CHECKED(skel, mmap, mmap_exit); + ATTACH_UPROBE_CHECKED(skel, mmap, mmap_enter); + ATTACH_URETPROBE_CHECKED(skel, mmap, mmap_exit); - ATTACH_UPROBE_CHECKED(skel, memalign, memalign_enter); - ATTACH_URETPROBE_CHECKED(skel, memalign, memalign_exit); + ATTACH_UPROBE_CHECKED(skel, memalign, memalign_enter); + ATTACH_URETPROBE_CHECKED(skel, memalign, memalign_exit); - ATTACH_UPROBE_CHECKED(skel, free, free_enter); - ATTACH_UPROBE_CHECKED(skel, munmap, munmap_enter); + ATTACH_UPROBE_CHECKED(skel, free, free_enter); + ATTACH_UPROBE_CHECKED(skel, munmap, munmap_enter); - // the following probes are intentinally allowed to fail attachment + // the following probes are intentinally allowed to fail attachment - // deprecated in libc.so bionic - ATTACH_UPROBE(skel, valloc, valloc_enter); - ATTACH_URETPROBE(skel, valloc, valloc_exit); + // deprecated in libc.so bionic + ATTACH_UPROBE(skel, valloc, valloc_enter); + ATTACH_URETPROBE(skel, valloc, valloc_exit); - // deprecated in libc.so bionic - ATTACH_UPROBE(skel, pvalloc, pvalloc_enter); - ATTACH_URETPROBE(skel, pvalloc, pvalloc_exit); + // deprecated in libc.so bionic + ATTACH_UPROBE(skel, pvalloc, pvalloc_enter); + ATTACH_URETPROBE(skel, pvalloc, pvalloc_exit); - // added in C11 - ATTACH_UPROBE(skel, aligned_alloc, aligned_alloc_enter); - ATTACH_URETPROBE(skel, aligned_alloc, aligned_alloc_exit); + // added in C11 + ATTACH_UPROBE(skel, aligned_alloc, aligned_alloc_enter); + ATTACH_URETPROBE(skel, aligned_alloc, aligned_alloc_exit); - return 0; + return 0; } +// Functions to process different BPF programs +static int process_paf(struct paf_bpf *skel_paf) +{ + int err; + struct ring_buffer *rb; -int main(int argc, char **argv) { - struct ring_buffer *rb = NULL; - struct paf_bpf *skel_paf; - struct pr_bpf *skel_pr; - struct procstat_bpf *skel_procstat; - struct sysstat_bpf *skel_sysstat; - struct memleak_bpf *skel; + LOAD_AND_ATTACH_SKELETON(skel_paf, paf); - int err, i; - LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts); + printf("%-8s %-8s %-8s %-8s %-8s\n", "MIN", "LOW", "HIGH", "PRESENT", "FLAG"); - err = argp_parse(&argp, argc, argv, 0, NULL, NULL); - if (err) - return err; - own_pid = getpid(); - libbpf_set_strict_mode(LIBBPF_STRICT_ALL); + POLL_RING_BUFFER(rb, 1000, err); - /* Set up libbpf errors and debug info callback */ - libbpf_set_print(libbpf_print_fn); +paf_cleanup: + ring_buffer__free(rb); + paf_bpf__destroy(skel_paf); + return err; +} - /* Cleaner handling of Ctrl-C */ - signal(SIGINT, sig_handler); - signal(SIGTERM, sig_handler); - signal(SIGALRM, sig_handler); +static int process_pr(struct pr_bpf *skel_pr) +{ + int err; + struct ring_buffer *rb; - if (env.paf) { - /* Load and verify BPF application */ - skel_paf = paf_bpf__open(); - if (!skel_paf) { - fprintf(stderr, "Failed to open and load BPF skeleton\n"); - return 1; - } + LOAD_AND_ATTACH_SKELETON(skel_pr, pr); - skel_paf->bss->user_pid = own_pid; + printf("%-8s %-8s %-8s %-8s %-8s\n", "RECLAIM", "RECLAIMED", "UNQUEUE", "CONGESTED", "WRITEBACK"); - /* Load & verify BPF programs */ - err = paf_bpf__load(skel_paf); - if (err) { - fprintf(stderr, "Failed to load and verify BPF skeleton\n"); - goto paf_cleanup; - } + POLL_RING_BUFFER(rb, 1000, err); - /* Attach tracepoints */ - err = paf_bpf__attach(skel_paf); - if (err) { - fprintf(stderr, "Failed to attach BPF skeleton\n"); - goto paf_cleanup; - } +pr_cleanup: + ring_buffer__free(rb); + pr_bpf__destroy(skel_pr); + return err; +} - /* Set up ring buffer polling */ - rb = ring_buffer__new(bpf_map__fd(skel_paf->maps.rb), handle_event_paf, NULL, NULL); - if (!rb) { - err = -1; - fprintf(stderr, "Failed to create ring buffer\n"); - goto paf_cleanup; - } +static int process_procstat(struct procstat_bpf *skel_procstat) +{ + int err; + struct ring_buffer *rb; - /* Process events */ - printf("%-8s %-8s %-8s %-8s %-8s\n", "MIN", "LOW", "HIGH", "PRESENT", "FLAG"); + LOAD_AND_ATTACH_SKELETON(skel_procstat, procstat); + + if (env.rss) + { + printf("%-8s %-8s %-8s %-8s %-8s %-8s %-8s\n", "TIME", "PID", "VMSIZE", "VMDATA", "VMSTK", "VMPTE", "VMSWAP"); + } + else + { + printf("%-8s %-8s %-8s %-8s %-8s %-8s\n", "TIME", "PID", "SIZE", "RSSANON", "RSSFILE", "RSSSHMEM"); } - else if (env.pr) { - /* Load and verify BPF application */ - skel_pr = pr_bpf__open(); - if (!skel_pr) { - fprintf(stderr, "Failed to open and load BPF skeleton\n"); - return 1; - } - skel_pr->bss->user_pid = own_pid; + POLL_RING_BUFFER(rb, 1000, err); - /* Load & verify BPF programs */ - err = pr_bpf__load(skel_pr); - if (err) { - fprintf(stderr, "Failed to load and verify BPF skeleton\n"); - goto pr_cleanup; - } +procstat_cleanup: + ring_buffer__free(rb); + procstat_bpf__destroy(skel_procstat); + return err; +} - /* Attach tracepoints */ - err = pr_bpf__attach(skel_pr); - if (err) { - fprintf(stderr, "Failed to attach BPF skeleton\n"); - goto pr_cleanup; - } +static int process_sysstat(struct sysstat_bpf *skel_sysstat) +{ + int err; + struct ring_buffer *rb; - /* Set up ring buffer polling */ - rb = ring_buffer__new(bpf_map__fd(skel_pr->maps.rb), handle_event_pr, NULL, NULL); - if (!rb) { - err = -1; - fprintf(stderr, "Failed to create ring buffer\n"); - goto pr_cleanup; - } + LOAD_AND_ATTACH_SKELETON(skel_sysstat, sysstat); - /* Process events */ - printf("%-8s %-8s %-8s %-8s %-8s\n", "RECLAIM", "RECLAIMED", "UNQUEUE", "CONGESTED", "WRITEBACK"); + if (env.part2) + { + printf("%-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s\n", "KRECLM", "SLAB", "SRECLM", "SUNRECL", "NFSUNSTB", "WRITEBACKTMP", "KMAP", "UNMAP", "PAGE"); } + else + { + printf("%-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s\n", "TIME", "PID", "CPU", "MEM", "READ", "WRITE", "IOWAIT", "SWAP"); + } + POLL_RING_BUFFER(rb, 1000, err); - else if (env.procstat) { - /* Load and verify BPF application */ - skel_procstat = procstat_bpf__open(); - if (!skel_procstat) { - fprintf(stderr, "Failed to open and load BPF skeleton\n"); - return 1; - } - - skel_procstat->bss->user_pid = own_pid; +sysstat_cleanup: + ring_buffer__free(rb); + sysstat_bpf__destroy(skel_sysstat); + return err; +} - /* Load & verify BPF programs */ - err = procstat_bpf__load(skel_procstat); - if (err) { - fprintf(stderr, "Failed to load and verify BPF skeleton\n"); - goto procstat_cleanup; - } +static int process_memleak(struct memleak_bpf *skel_memleak, struct env env) +{ + skel_memleak->rodata->stack_flags = env.kernel_trace ? KERN_STACKID_FLAGS : USER_STACKID_FLAGS; - /* Attach tracepoints */ - err = procstat_bpf__attach(skel_procstat); - if (err) { - fprintf(stderr, "Failed to attach BPF skeleton\n"); - goto procstat_cleanup; - } + bpf_map__set_value_size(skel_memleak->maps.stack_traces, perf_max_stack_depth * sizeof(__u64)); + bpf_map__set_max_entries(skel_memleak->maps.stack_traces, stack_map_max_entries); - /* Set up ring buffer polling */ - rb = ring_buffer__new(bpf_map__fd(skel_procstat->maps.rb), handle_event_procstat, NULL, NULL); - if (!rb) { - err = -1; - fprintf(stderr, "Failed to create ring buffer\n"); - goto procstat_cleanup; - } + if (!env.kernel_trace) + disable_kernel_tracepoints(skel_memleak); - /* Process events */ - if (env.rss == true) { - printf("%-8s %-8s %-8s %-8s %-8s %-8s %-8s\n", "TIME", "PID", "VMSIZE", "VMDATA", "VMSTK", "VMPTE", "VMSWAP"); - } - else { - printf("%-8s %-8s %-8s %-8s %-8s %-8s\n", "TIME", "PID", "SIZE", "RSSANON", "RSSFILE", "RSSSHMEM"); - } + int err = memleak_bpf__load(skel_memleak); + if (err) + { + fprintf(stderr, "Failed to load BPF skeleton\n"); + goto memleak_cleanup; } - else if (env.sysstat) { - /* Load and verify BPF application */ - skel_sysstat = sysstat_bpf__open(); - if (!skel_sysstat) { - fprintf(stderr, "Failed to open and load BPF skeleton\n"); - return 1; + if (!env.kernel_trace) + { + err = attach_uprobes(skel_memleak); + if (err) + { + fprintf(stderr, "Failed to attach uprobes\n"); + goto memleak_cleanup; } + } - skel_sysstat->bss->user_pid = own_pid; + err = memleak_bpf__attach(skel_memleak); + if (err) + { + fprintf(stderr, "Failed to auto-attach BPF skeleton: %d\n", err); + goto memleak_cleanup; + } - /* Load & verify BPF programs */ - err = sysstat_bpf__load(skel_sysstat); - if (err) { - fprintf(stderr, "Failed to load and verify BPF skeleton\n"); - goto sysstat_cleanup; - } + g_stacks_size = perf_max_stack_depth * sizeof(*g_stacks); + g_stacks = (__u64 *)malloc(g_stacks_size); + if (!g_stacks) + { + fprintf(stderr, "Failed to allocate memory\n"); + err = -1; + goto memleak_cleanup; + } + memset(g_stacks, 0, g_stacks_size); + + symbolizer = blaze_symbolizer_new(); + if (!symbolizer) + { + fprintf(stderr, "Fail to create a symbolizer\n"); + err = -1; + goto memleak_cleanup; + } - /* Attach tracepoints */ - err = sysstat_bpf__attach(skel_sysstat); - if (err) { - fprintf(stderr, "Failed to attach BPF skeleton\n"); - goto sysstat_cleanup; - } + for (;;) + { + if (!env.kernel_trace) + if (env.print_time) + { + system("clear"); + update_addr_times(skel_memleak); + print_time(skel_memleak); + } + else + print_outstanding_combined_allocs(skel_memleak, attach_pid); + else + print_outstanding_allocs(skel_memleak); - /* Set up ring buffer polling */ - rb = ring_buffer__new(bpf_map__fd(skel_sysstat->maps.rb), handle_event_sysstat, NULL, NULL); - if (!rb) { - err = -1; - fprintf(stderr, "Failed to create ring buffer\n"); - goto sysstat_cleanup; - } + sleep(1); + } - /* Process events */ - if (env.part2 == true) { - printf("%-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s\n", "KRECLM", "SLAB", "SRECLM", "SUNRECLMA", "UNSTABLE", "WRITEBK_T", "ANONHUGE", "SHMEMHUGE", "PMDMAPP"); + while (!exiting) + { + /* Ctrl-C will cause -EINTR */ + if (err == -EINTR) + { + err = 0; + break; } - else { - printf("%-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s\n", "ACTIVE", "INACTVE", "ANON_ACT", "ANON_INA", "FILE_ACT", "FILE_INA", "UNEVICT", "DIRTY", "WRITEBK", "ANONPAG", "MAP", "SHMEM"); + if (err < 0) + { + printf("Error polling perf buffer: %d\n", err); + break; } } - else if (env.memleak) { - if (argc != 3) { - printf("usage:%s attach_pid\n", argv[0]); - return -1; - } +memleak_cleanup: + memleak_bpf__destroy(skel_memleak); + if (symbolizer) + blaze_symbolizer_free(symbolizer); + if (g_stacks) + free(g_stacks); + if (allocs) + free(allocs); + return err; +} - attach_pid = atoi(argv[2]); +static int process_oomkiller(struct oomkiller_bpf *skel_oomkiller) +{ + int err; + struct ring_buffer *rb; - strcpy(binary_path, "/lib/x86_64-linux-gnu/libc.so.6"); + // 使用指定 map_name 的宏 + LOAD_AND_ATTACH_SKELETON_WITH_MAP(skel_oomkiller, oomkiller, events); - /* Set up libbpf errors and debug info callback */ - libbpf_set_print(libbpf_print_fn); + printf("Waiting for OOM events...\n"); - /* Load and verify BPF application */ - skel = memleak_bpf__open(); - if (!skel) { - fprintf(stderr, "Failed to open BPF skeleton\n"); - return 1; - } + POLL_RING_BUFFER(rb, 1000, err); - bpf_map__set_value_size(skel->maps.stack_traces, perf_max_stack_depth * sizeof(__u64)); - bpf_map__set_max_entries(skel->maps.stack_traces, stack_map_max_entries); +oomkiller_cleanup: + ring_buffer__free(rb); + oomkiller_bpf__destroy(skel_oomkiller); + return err; +} - /* Load & verify BPF programs */ - err = memleak_bpf__load(skel); - if (err) { - fprintf(stderr, "Failed to load BPF skeleton\n"); - goto memleak_cleanup; - } - err = attach_uprobes(skel); - if (err) { - fprintf(stderr, "failed to attach uprobes\n"); - goto memleak_cleanup; +// ================================================== fraginfo==================================================================== +// compute order +static int __fragmentation_index(unsigned int order, long unsigned int total, long unsigned int suitable, long unsigned int free) +{ + unsigned long requested = 1UL << order; + // 无可用内存返回0 + if (order > MAX_ORDER) + return 0; + if (!total) + return 0; + // 有可用内存返回-1000 + if (suitable) + return -1000; + double res1, res2; + res1 = (double)(free * 1000ULL) / requested; + // res1 +=1000; + res2 = (double)res1 / total; + return res2; +} +static int unusable_free_index(unsigned int order, long unsigned int total, long unsigned int suitable, long unsigned int free) +{ + /* No free memory is interpreted as all free memory is unusable */ + if (free == 0) + return 1000; + + /* + * Index should be a value between 0 and 1. Return a value to 3 + * decimal places. + * + * 0 => no fragmentation + * 1 => high fragmentation + */ + long unsigned int res1 = free - (suitable << order); + double res = (res1 * 1000ULL) / free; + return res; +} +void print_zones(int fd) +{ + struct zone_info zinfo; + __u64 key = 0, next_key; + printf("%-20s %-20s %-25s %-20s %-20s", " COMM", "ZONE_PTR", "ZONE_PFN ", " SUM_PAGES", "FACT_PAGES "); + printf("\n"); + while (bpf_map_get_next_key(fd, &key, &next_key) == 0) + { + bpf_map_lookup_elem(fd, &next_key, &zinfo); + printf(" %-15s 0x%-25llx %-25llu %-20llu %-15llu\n", zinfo.comm, zinfo.zone_ptr, zinfo.zone_start_pfn, zinfo.spanned_pages, zinfo.present_pages); + key = next_key; + } +} +void print_nodes(int fd) +{ + struct pgdat_info pinfo; + __u64 key = 0, next_key; + printf(" Node ID PGDAT_PTR NR_ZONES \n"); + while (bpf_map_get_next_key(fd, &key, &next_key) == 0) + { + bpf_map_lookup_elem(fd, &next_key, &pinfo); + printf(" %5d 0x%llx %5d\n", + pinfo.node_id, pinfo.pgdat_ptr, pinfo.nr_zones); + key = next_key; + } +} +void print_orders(int fd) +{ + struct order_zone okey = {}; + struct ctg_info oinfo; + struct order_entry entries[256]; + int entry_count = 0; + + while (bpf_map_get_next_key(fd, &okey, &okey) == 0) + { + if (bpf_map_lookup_elem(fd, &okey, &oinfo) == 0) + { + entries[entry_count].okey = okey; + entries[entry_count].oinfo = oinfo; + entry_count++; } + } - /* Let libbpf perform auto-attach for uprobe_sub/uretprobe_sub - * NOTICE: we provide path and symbol info in SEC for BPF programs - */ - err = memleak_bpf__attach(skel); - if (err) { - fprintf(stderr, "Failed to auto-attach BPF skeleton: %d\n", err); - goto memleak_cleanup; - } + // 排序 + qsort(entries, entry_count, sizeof(struct order_entry), compare_entries); + + // 打印排序后的 + printf(" Order Zone_PTR Free Pages Free Blocks Total Free Blocks Suitable SCOREA SCOREB\n"); + for (int i = 0; i < entry_count; i++) + { + int res = __fragmentation_index(entries[i].okey.order, entries[i].oinfo.free_blocks_total, entries[i].oinfo.free_blocks_suitable, entries[i].oinfo.free_pages); + int tmp = unusable_free_index(entries[i].okey.order, entries[i].oinfo.free_blocks_total, entries[i].oinfo.free_blocks_suitable, entries[i].oinfo.free_pages); + // int part1 = res / 1000; + // int dec1 = res % 1000; + int part2 = tmp / 1000; + int dec2 = tmp % 1000; + printf(" %-8u 0x%-25llx %-20lu %-20lu %-20lu %d %d.%03d\n", + entries[i].okey.order, entries[i].okey.zone_ptr, entries[i].oinfo.free_pages, + entries[i].oinfo.free_blocks_total, entries[i].oinfo.free_blocks_suitable, res, part2, dec2); + } +} - g_stacks_size = perf_max_stack_depth * sizeof(*g_stacks); - g_stacks = (__u64 *)malloc(g_stacks_size); - memset(g_stacks, 0, g_stacks_size); +static int process_fraginfo(struct fraginfo_bpf *skel_fraginfo) +{ - symbolizer = blaze_symbolizer_new(); - if (!symbolizer) { - fprintf(stderr, "Fail to create a symbolizer\n"); - err = -1; - goto memleak_cleanup; - } + int err = fraginfo_bpf__load(skel_fraginfo); + if (err) + { + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); + goto fraginfo_cleanup; + } - for (i = 0;; i++) { - /* trigger our BPF programs */ - print_outstanding_combined_allocs(skel, attach_pid); - sleep(1); - } + err = fraginfo_bpf__attach(skel_fraginfo); + if (err) + { + fprintf(stderr, "Failed to attach BPF skeleton\n"); + goto fraginfo_cleanup; + } + while (1) + { + sleep(env.interval); + print_nodes(bpf_map__fd(skel_fraginfo->maps.nodes)); + printf("\n"); + print_zones(bpf_map__fd(skel_fraginfo->maps.zones)); + printf("\n"); + print_orders(bpf_map__fd(skel_fraginfo->maps.orders)); + printf("\n"); } - while (!exiting) { - if (env.paf || env.pr || env.procstat || env.sysstat) { - err = ring_buffer__poll(rb, 1000 /* timeout, ms */); - /* Ctrl-C will cause -EINTR */ - if (err == -EINTR) { - err = 0; - break; - } - if (err < 0) { - printf("Error polling perf buffer: %d\n", err); - break; +fraginfo_cleanup: + fraginfo_bpf__destroy(skel_fraginfo); + return -err; +} + +// ================================================== vmasnap ==================================================================== +static void print_find_event_data(int map_fd) +{ + __aligned_u64 key = 0; + __aligned_u64 next_key; + struct find_event_t event; + + printf("Reading find events...\n"); + + // Print header + printf("%-10s %-20s %-15s %-20s %-20s %-20s %-20s\n", + "PID", "Address", "Duration", "VMACache Hit", "RB Subtree Last", "VM Start", "VM End"); + + while (bpf_map_get_next_key(map_fd, &key, &next_key) == 0) + { + if (bpf_map_lookup_elem(map_fd, &next_key, &event) == 0) + { + printf("%-10llu %-20lu %-15llu %-20d %-20llu %-20llu %-20llu\n", + next_key, event.addr, event.duration, event.vmacache_hit, + event.rb_subtree_last, event.vm_start, event.vm_end); + + // Delete the element from the map after printing + if (bpf_map_delete_elem(map_fd, &next_key) != 0) + { + perror("Failed to delete element from map"); } } - else if (env.memleak) { - /* Ctrl-C will cause -EINTR */ - if (err == -EINTR) { - err = 0; - break; - } - if (err < 0) { - printf("Error polling perf buffer: %d\n", err); - break; + + // Use a temporary variable to handle key update + __aligned_u64 temp_key = next_key; + key = temp_key; + } +} + +static void print_insert_event_data(int map_fd) +{ + __aligned_u64 key = 0; + __aligned_u64 next_key; + struct insert_event_t event; + + printf("Reading insert events...\n"); + + // Print header + printf("%-10s %-15s %-15s %-20s %-20s %-20s %-20s %-20s\n", + "PID", "Duration", "List", "RB", "Interval Tree", "List Time", "RB Time", "Interval Tree Time"); + + while (bpf_map_get_next_key(map_fd, &key, &next_key) == 0) + { + if (bpf_map_lookup_elem(map_fd, &next_key, &event) == 0) + { + printf("%-10llu %-15llu %-15d %-20d %-20d %-20llu %-20llu %-20llu\n", + next_key, event.duration, event.inserted_to_list, event.inserted_to_rb, + event.inserted_to_interval_tree, event.link_list_duration, + event.link_rb_duration, event.interval_tree_duration); + + // Delete the element from the map after printing + if (bpf_map_delete_elem(map_fd, &next_key) != 0) + { + perror("Failed to delete element from map"); } } - else { - printf("请输入要使用的功能...\n"); - break; - } + + // Use a temporary variable to handle key update + __aligned_u64 temp_key = next_key; + key = temp_key; } +} -paf_cleanup: - ring_buffer__free(rb); - paf_bpf__destroy(skel_paf); - return err < 0 ? -err : 0; +static int process_vmasnap(struct vmasnap_bpf *skel_vmasnap) +{ + int err; + + // Load and verify BPF application + skel_vmasnap = vmasnap_bpf__open(); + if (!skel_vmasnap) + { + fprintf(stderr, "Failed to open BPF skeleton\n"); + return 1; + } -pr_cleanup: - ring_buffer__free(rb); - pr_bpf__destroy(skel_pr); - return err < 0 ? -err : 0; + // Load & verify BPF programs + err = vmasnap_bpf__load(skel_vmasnap); + if (err) + { + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); + goto vmasnap_cleanup; + } -procstat_cleanup: - ring_buffer__free(rb); - procstat_bpf__destroy(skel_procstat); - return err < 0 ? -err : 0; + // Attach tracepoints + err = vmasnap_bpf__attach(skel_vmasnap); + if (err) + { + fprintf(stderr, "Failed to attach BPF skeleton\n"); + goto vmasnap_cleanup; + } -sysstat_cleanup: - ring_buffer__free(rb); - sysstat_bpf__destroy(skel_sysstat); - return err < 0 ? -err : 0; + printf("Successfully started! Press Ctrl-C to exit.\n"); -memleak_cleanup: - memleak_bpf__destroy(skel); - blaze_symbolizer_free(symbolizer); - free(g_stacks); - return err < 0 ? -err : 0; + // Get the file descriptors for the maps + int find_map_fd = bpf_map__fd(skel_vmasnap->maps.find_events); + int insert_map_fd = bpf_map__fd(skel_vmasnap->maps.insert_events); + + if (find_map_fd < 0 || insert_map_fd < 0) + { + fprintf(stderr, "Failed to get file descriptor for maps\n"); + goto vmasnap_cleanup; + } + + // Main loop + while (!exiting) + { + // Print events data every second + print_find_event_data(find_map_fd); + print_insert_event_data(insert_map_fd); + sleep(1); + } + +vmasnap_cleanup: + vmasnap_bpf__destroy(skel_vmasnap); + return 0; +} + +static int process_drsnoop(struct drsnoop_bpf *skel_drsnoop) { + int err; + struct ring_buffer *rb; + + __u64 vm_stat_addr; + __u32 key = 0; // Key for the vm_stat_map + + if (get_vm_stat_addr(&vm_stat_addr) != 0) { + fprintf(stderr, "Failed to get vm_stat or vm_zone_stat address\n"); + return 1; + } + + err = drsnoop_bpf__load(skel_drsnoop); + if (err) { + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); + return 1; + } + + // Update BPF map with the address of vm_stat + err = bpf_map_update_elem(bpf_map__fd(skel_drsnoop->maps.vm_stat_map), &key, &vm_stat_addr, BPF_ANY); + if (err) { + fprintf(stderr, "Failed to update BPF map: %s\n", strerror(errno)); + goto drsnoop_cleanup; + } + + err = drsnoop_bpf__attach(skel_drsnoop); + if (err) { + fprintf(stderr, "Failed to attach BPF skeleton\n"); + goto drsnoop_cleanup; + } + + rb = ring_buffer__new(bpf_map__fd(skel_drsnoop->maps.rb), handle_event_drsnoop, NULL, NULL); + if (!rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer\n"); + goto drsnoop_cleanup; + } + + printf("%-8s %-16s %-7s %-9s %-7s\n", "TIME", "COMM", "PID", "FREE(KB)", "LAT(ms)"); + + POLL_RING_BUFFER(rb, 1000, err);\ + +drsnoop_cleanup: + /* 清理 */ + ring_buffer__free(rb); + drsnoop_bpf__destroy(skel_drsnoop); + + return err < 0 ? -err : 0; } \ No newline at end of file diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.h b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.h deleted file mode 100644 index 3b65256eb..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.h +++ /dev/null @@ -1,137 +0,0 @@ -#ifndef __MEM_WATCHER_H -#define __MEM_WATCHER_H - -#define TASK_COMM_LEN 16 -#define MAX_FILENAME_LEN 127 - -#define ___GFP_DMA 0x01u -#define ___GFP_HIGHMEM 0x02u -#define ___GFP_DMA32 0x04u -#define ___GFP_MOVABLE 0x08u -#define ___GFP_RECLAIMABLE 0x10u -#define ___GFP_HIGH 0x20u -#define ___GFP_IO 0x40u -#define ___GFP_FS 0x80u -#define ___GFP_WRITE 0x100u -#define ___GFP_NOWARN 0x200u -#define ___GFP_RETRY_MAYFAIL 0x400u -#define ___GFP_NOFAIL 0x800u -#define ___GFP_NORETRY 0x1000u -#define ___GFP_MEMALLOC 0x2000u -#define ___GFP_COMP 0x4000u -#define ___GFP_ZERO 0x8000u -#define ___GFP_NOMEMALLOC 0x10000u -#define ___GFP_HARDWALL 0x20000u -#define ___GFP_THISNODE 0x40000u -#define ___GFP_ATOMIC 0x80000u -#define ___GFP_ACCOUNT 0x100000u -#define ___GFP_DIRECT_RECLAIM 0x200000u -#define ___GFP_KSWAPD_RECLAIM 0x400000u - -#define GFP_ATOMIC (__GFP_HIGH|__GFP_ATOMIC|__GFP_KSWAPD_RECLAIM) -#define GFP_KERNEL (__GFP_RECLAIM | __GFP_IO | __GFP_FS) -#define GFP_KERNEL_ACCOUNT (GFP_KERNEL | __GFP_ACCOUNT) -#define GFP_NOWAIT (__GFP_KSWAPD_RECLAIM) -#define GFP_NOIO (__GFP_RECLAIM) -#define GFP_NOFS (__GFP_RECLAIM | __GFP_IO) -#define GFP_USER (__GFP_RECLAIM | __GFP_IO | __GFP_FS | __GFP_HARDWALL) -#define GFP_DMA __GFP_DMA -#define GFP_DMA32 __GFP_DMA32 -#define GFP_HIGHUSER (GFP_USER | __GFP_HIGHMEM) -#define GFP_HIGHUSER_MOVABLE (GFP_HIGHUSER | __GFP_MOVABLE) -#define GFP_TRANSHUGE_LIGHT ((GFP_HIGHUSER_MOVABLE | __GFP_COMP | \ - __GFP_NOMEMALLOC | __GFP_NOWARN) & ~__GFP_RECLAIM) -#define GFP_TRANSHUGE (GFP_TRANSHUGE_LIGHT | __GFP_DIRECT_RECLAIM) - -struct paf_event { - unsigned long min; - unsigned long low; - unsigned long high; - unsigned long present; - unsigned long protection; - int flag; -}; - -struct pr_event { - unsigned long reclaim; - unsigned long reclaimed; - unsigned int unqueued_dirty; - unsigned int congested; - unsigned int writeback; -}; - -struct procstat_event { - /*进程内存状态报告*/ - pid_t pid; - long nvcsw; - long nivcsw; - long vsize; //虚拟内存 - long size; //物理内存 - long long rssanon; //匿名页面 - long long rssfile; //文件页面 - long long rssshmem; //共享页面 - long long vswap; //交换页面 - long long Hpages; //hugetlbPages - long Vdata; //Private data segments - long Vstk; //User stack - long long VPTE; -}; - -struct sysstat_event { - /*系统内存状态报告*/ - unsigned long present; - unsigned long anon_inactive; // 0 - unsigned long anon_active; // 1 - unsigned long file_inactive; // 2 - unsigned long file_active; // 3 - unsigned long unevictable; // 不可回收页面 - unsigned long slab_reclaimable; - unsigned long slab_unreclaimable; - unsigned long anon_isolated; // 匿名隔离页面 - unsigned long file_isolated; // 文件隔离页面 - - unsigned long working_nodes; // 12 - unsigned long working_refault; - unsigned long working_activate; - unsigned long working_restore; - unsigned long working_nodereclaim; - - unsigned long anon_mapped; // 17 - unsigned long file_mapped; - - unsigned long file_pages; // 19 - unsigned long file_dirty; - unsigned long writeback; - unsigned long writeback_temp; - - unsigned long shmem; // 共享内存23 - unsigned long shmem_thps; - unsigned long pmdmapped; - unsigned long anon_thps; - unsigned long unstable_nfs; - unsigned long vmscan_write; - unsigned long vmscan_immediate; - - unsigned long diried; - unsigned long written; - unsigned long kernel_misc_reclaimable; -}; - -/*memleak.h*/ -#define ALLOCS_MAX_ENTRIES 1000000 -#define COMBINED_ALLOCS_MAX_ENTRIES 10240 - -struct alloc_info { - __u64 size; - int stack_id; -}; - -union combined_alloc_info { - struct { - __u64 total_size : 40; - __u64 number_of_allocs : 24; - }; - __u64 bits; -}; - -#endif /* __MEM_WATCHER_H */ \ No newline at end of file diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/memleak.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/memleak.c deleted file mode 100644 index fa926527c..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/memleak.c +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2023 The LMP Authors. -// -// 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 -// -// https://github.com/linuxkerneltravel/lmp/blob/develop/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: zai953879556@163.com -// -// mem_watcher libbpf user mode code - -#include -#include -#include -#include -#include -#include -#include -#include "memleak.skel.h" -#include "mem_watcher.h" - -#include "blazesym.h" - -int main(int argc, char **argv) { - -} diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/paf.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/paf.c deleted file mode 100644 index 3057fae61..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/paf.c +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) -/* Copyright (c) 2020 Facebook */ -#include -#include -#include -#include -#include -#include -#include "mem_watcher.h" -#include "paf.skel.h" -#include -#include - -int main(int argc, char **argv) { - -} diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/pr.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/pr.c deleted file mode 100644 index c4dcd6f3f..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/pr.c +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) -/* Copyright (c) 2020 Facebook */ -#include -#include -#include -#include -#include -#include -#include "mem_watcher.h" -#include "pr.skel.h" -#include -#include - -int main(int argc, char **argv){ - -} diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/procstat.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/procstat.c deleted file mode 100644 index 90876c125..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/procstat.c +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) -/* Copyright (c) 2020 Facebook */ -#include -#include -#include -#include -#include -#include -#include "mem_watcher.h" -#include "procstat.skel.h" -#include -#include - -int main(int argc, char **argv) { - -} \ No newline at end of file diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/sysstat.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/sysstat.c deleted file mode 100644 index 448210d90..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/sysstat.c +++ /dev/null @@ -1,14 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "mem_watcher.h" -#include "sysstat.skel.h" -#include -#include - -int main(int argc, char **argv) { - -} \ No newline at end of file diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/Makefile b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/Makefile new file mode 100644 index 000000000..cbe5710b9 --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/Makefile @@ -0,0 +1,12 @@ +CC = gcc +CFLAGS = -g + +.PHONY: all clean + +all: test_mem + +test_mem: test_mem.c + $(CC) $(CFLAGS) -o test_mem test_mem.c + +clean: + rm -f test_mem diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/pr_test/Makefile b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/pr_test/Makefile new file mode 100644 index 000000000..55d78fca0 --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/pr_test/Makefile @@ -0,0 +1,22 @@ +# Compiler and flags +CC = gcc +CFLAGS = -Wall + +# Source files +C_SRC = pr_test.c + +# Output binary +C_OUT = pr_test + +.PHONY: all clean run + +all: $(C_OUT) + +$(C_OUT): $(C_SRC) + $(CC) $(CFLAGS) $< -o $@ + +clean: + rm -f $(C_OUT) + +run: $(C_OUT) + sudo ./$(C_OUT) diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/pr_test/pr_test.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/pr_test/pr_test.c new file mode 100644 index 000000000..e10e25bf5 --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/pr_test/pr_test.c @@ -0,0 +1,36 @@ +#include +#include +#include + +#define ALLOC_SIZE 1024*1024*1024 // 分配 512 MB 内存 + +int main() { + void *memory; + printf("Allocating memory...\n"); + memory = malloc(ALLOC_SIZE); + if (!memory) { + perror("Failed to allocate memory"); + return -1; + } + + // 填充内存以确保页面被分配 + printf("Filling memory...\n"); + for (size_t i = 0; i < ALLOC_SIZE; ++i) { + ((char*)memory)[i] = (char)i; + } + + printf("Freeing memory...\n"); + free(memory); + + // 给内核更多时间处理回收 + printf("Sleeping for 10 seconds...\n"); + sleep(10); // 增加等待时间到 10 秒 + + printf("Memory management demo finished.\n"); + return 0; +} + +// 分配大量内存。 +//填充内存以确保页面分配。 +//释放内存。 +//等待一段时间以允许内核处理任何可能的内存回收。 \ No newline at end of file diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/sysstat_test/Makefile b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/sysstat_test/Makefile new file mode 100644 index 000000000..9e3fbbded --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/sysstat_test/Makefile @@ -0,0 +1,11 @@ +CC = gcc +CFLAGS = -Wall -O2 +TARGET = sysstat_test + +all: $(TARGET) + +$(TARGET): sysstat_test.c + $(CC) $(CFLAGS) -o $(TARGET) sysstat_test.c + +clean: + rm -f $(TARGET) diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/sysstat_test/sysstat_test.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/sysstat_test/sysstat_test.c new file mode 100644 index 000000000..c0e7e64b5 --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/sysstat_test/sysstat_test.c @@ -0,0 +1,49 @@ +#include +#include +#include + +#define ALLOC_SIZE_SMALL 512*1024*1024 // 分配 512 MB 内存 +#define ALLOC_SIZE_LARGE 1024*1024*1024 // 分配 1 GB 内存 + +void allocate_memory(size_t size) { + void *memory; + + printf("Allocating %lu MB memory...\n", size / (1024*1024)); + memory = malloc(size); + if (!memory) { + perror("Failed to allocate memory"); + return; + } + + // 填充内存以确保页面被分配 + printf("Filling memory...\n"); + for (size_t i = 0; i < size; ++i) { + ((char*)memory)[i] = (char)i; + } + + printf("Freeing memory...\n"); + free(memory); + + // 给内核更多时间处理回收 + printf("Sleeping for 10 seconds...\n"); + sleep(10); // 增加等待时间到 10 秒 + + printf("Memory management operation finished.\n"); +} + +int main() { + printf("Starting memory management demo...\n"); + + // 分配和释放多次小块内存 + for (int i = 0; i < 5; ++i) { + allocate_memory(ALLOC_SIZE_SMALL); + } + + // 分配和释放多次大块内存 + for (int i = 0; i < 5; ++i) { + allocate_memory(ALLOC_SIZE_LARGE); + } + + printf("Memory management demo finished.\n"); + return 0; +} diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/test_mem.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/test_mem.c new file mode 100644 index 000000000..9396ace1f --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/test_mem.c @@ -0,0 +1,164 @@ +#include +#include +#include +#include +#include +#include +#include + +#define ALLOC_SIZE_SMALL 4 +#define ALLOC_SIZE_MEDIUM 64 +#define ALLOC_SIZE_LARGE 1024 + +static struct env { + bool overall_leak_test; + bool mem_leak; + bool mem_unleak; +} env = { + .overall_leak_test = false, + .mem_leak = false, + .mem_unleak = false, +}; + +const char argp_program_doc[] ="mem_watcher test.\n"; + +static const struct argp_option opts[] = { + { NULL, 0, NULL, 0, "Memory Management Options:", 1 }, + { "overall-test", 'o', NULL, 0, "Perform overall memory test", 2 }, + { "detect-leak", 'l', NULL, 0, "Detect memory leaks", 3 }, + { "no-leak", 'n', NULL, 0, "No memory leaks expected", 3 }, + { NULL, 'h', NULL, OPTION_HIDDEN, "show the full help", 0 }, + { NULL, 0, NULL, 0, NULL, 0 } +}; + +static error_t parse_arg(int key, char *arg, struct argp_state *state) +{ + (void)arg; + switch (key) { + case 'o': + env.overall_leak_test = true; + break; + case 'l': + env.mem_leak = true; + break; + case 'n': + env.mem_unleak = true; + break; + case 'h': + argp_state_help(state, stderr, ARGP_HELP_STD_HELP); + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +// 模拟一些处理,通过写入分配的内存 +static void process_data(void *ptr, int size) { + memset(ptr, 0, size); +} + +// 分配内存并处理数据 +static void * alloc_v3(int alloc_size) { + void *ptr = malloc(alloc_size); + if (ptr) { + process_data(ptr, alloc_size / 3); + } + return ptr; +} + +// 分配内存并处理数据 +static void * alloc_v2(int alloc_size) { + void *ptr = alloc_v3(alloc_size); + if (ptr) { + process_data(ptr, alloc_size / 4); + } + return ptr; +} + +// 分配内存并处理数据 +static void * alloc_v1(int alloc_size) { + void *ptr = alloc_v2(alloc_size); + if (ptr) { + process_data(ptr, alloc_size / 5); + } + return ptr; +} + +// 演示内存泄漏 +static void leak_memory() { + void *ptr = malloc(ALLOC_SIZE_LARGE); + // 故意不释放 ptr 以制造内存泄漏 + process_data(ptr, ALLOC_SIZE_LARGE); +} + +static void mem_leak_process() { + // 引入一些间歇性的内存泄漏 + void *ptr = NULL; + int i = 0; + for (i = 0; ; i++) { + if (i % 5 == 0) { + leak_memory(); + } + sleep(1); + } +} + +static void mem_unleak_process(){ + void *ptr = NULL; + int i = 0; + + for (i = 0; ; i++) { + int alloc_size = (i % 3 == 0) ? ALLOC_SIZE_SMALL : (i % 3 == 1) ? ALLOC_SIZE_MEDIUM : ALLOC_SIZE_LARGE; + + ptr = alloc_v1(alloc_size); + if (!ptr) { + perror("alloc_v1 失败"); + exit(EXIT_FAILURE); + } + + void *ptr2 = malloc(alloc_size); + if (!ptr2) { + perror("malloc 失败"); + free(ptr); + exit(EXIT_FAILURE); + } + + process_data(ptr2, alloc_size); + + sleep(1); + free(ptr); + + sleep(2); + free(ptr2); + } +} + +int main(int argc, char **argv){ + int err; + static const struct argp argp = { + .options = opts, + .parser = parse_arg, + .doc = argp_program_doc, + }; + + err = argp_parse(&argp, argc, argv, 0, NULL, NULL); + if (err) + return err; + + if (env.overall_leak_test) { + // 打印当前进程的进程号(PID) + pid_t pid = getpid(); + printf("当前进程的进程号(PID): %d\n", pid); + if (env.mem_leak) { + printf("正在进行内存泄漏检测...\n"); + mem_leak_process(); + } + if (env.mem_unleak) { + printf("正在进行无内存泄漏测试...\n"); + mem_unleak_process(); + } + } + + return 0; +} diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/cma/Makefile b/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/cma/Makefile index c3a2cd4fc..4bee190f7 100644 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/cma/Makefile +++ b/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/cma/Makefile @@ -1,8 +1,8 @@ # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) OUTPUT := .output CLANG ?= clang -LIBBPF_SRC := $(abspath ../libbpf-bootstrap/libbpf/src) -BPFTOOL_SRC := $(abspath ../libbpf-bootstrap/bpftool/src) +LIBBPF_SRC := $(abspath ../../../../lib/libbpf/src) +BPFTOOL_SRC := $(abspath ../../../../lib/bpftool/src) LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/ion/Makefile b/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/ion/Makefile index e9e8ef726..154ab723b 100644 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/ion/Makefile +++ b/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/ion/Makefile @@ -1,8 +1,8 @@ # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) OUTPUT := .output CLANG ?= clang -LIBBPF_SRC := $(abspath ../libbpf-bootstrap/libbpf/src) -BPFTOOL_SRC := $(abspath ../libbpf-bootstrap/bpftool/src) +LIBBPF_SRC := $(abspath ../../../../lib/libbpf/src) +BPFTOOL_SRC := $(abspath ../../../../lib/bpftool/src) LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/Makefile b/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/Makefile index 041fcb3ba..9e45bb23a 100644 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/Makefile +++ b/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/Makefile @@ -2,8 +2,8 @@ OUTPUT := .output CLANG ?= clang LLVM_STRIP ?= llvm-strip -LIBBPF_SRC := $(abspath ../../libbpf/src) -BPFTOOL_SRC := $(abspath ../../bpftool/src) +LIBBPF_SRC := $(abspath ../../../../lib/libbpf/src) +BPFTOOL_SRC := $(abspath ../../../../lib/bpftool/src) LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/cma/cma_monitor.bpf.c b/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/cma/cma_monitor.bpf.c deleted file mode 100644 index dd18dd50c..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/cma/cma_monitor.bpf.c +++ /dev/null @@ -1,62 +0,0 @@ -#include "vmlinux.h" -#include -#include -#include -#include "cma_monitor.h" - -#define INTERVAL_MAX 6U -char LICENSE[] SEC("license") = "Dual BSD/GPL"; -struct { - __uint(type, BPF_MAP_TYPE_HASH); - __uint(max_entries, 8192); - __type(key, unsigned); - __type(value, u64); -} count_map SEC(".maps"); - -struct { - __uint(type, BPF_MAP_TYPE_HASH); - __uint(max_entries, 8192); - __type(key, u32); - __type(value, u64); -} time_map SEC(".maps"); - - -SEC("kretprobe/cma_alloc") -int BPF_KRETPROBE(cma_alloc) -{ - u32 pid = bpf_get_current_pid_tgid(); - u64 ts = bpf_ktime_get_ns(); - - bpf_map_update_elem(&time_map, &pid, &ts, BPF_ANY); - - return 0; -} - -SEC("kprobe/alloc_contig_range") -int BPF_KRETPROBE(alloc_contig_range) -{ - u32 pid = bpf_get_current_pid_tgid(); - u64 tm = bpf_ktime_get_ns(); - u64 *tsp = bpf_map_lookup_elem(&time_map, &pid); - - if (tsp) - tm -= *tsp; - else - return 1; - - unsigned key = tm / 10000000; - if (key > INTERVAL_MAX - 1) - key = INTERVAL_MAX - 1; - - u64 *value = bpf_map_lookup_elem(&count_map, &key); - if (value) - *value += 1; - else { - u64 init_value = 1; - bpf_map_update_elem(&count_map, &key, &init_value, BPF_ANY); - } - - bpf_map_delete_elem(&time_map, &pid); - - return 0; -} diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/cma/cma_monitor.c b/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/cma/cma_monitor.c deleted file mode 100644 index cecf5bc82..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/cma/cma_monitor.c +++ /dev/null @@ -1,51 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -//#include -#include "cma_monitor.h" -#include "cma_monitor.skel.h" - -#define INTERVAL_MAX 6U - -int main(int argc, char **argv) -{ - /* - char file_name[200]; - - snprintf(file_name, sizeof(file_name), "%s_kern.o", argv[0]); - if (load_bpf_file(file_name)) { - printf("%s", bpf_log_buf); - - return 1; - }*/ - struct cma_monitor_bpf *skel = cma_monitor_bpf__open_and_load(); - if (!skel) { - fprintf(stderr, "Failed to open BPF skeleton\n"); - return 1; - } - int fd = bpf_map__fd(skel->maps.time_map); - int key; - - for (;;) { - sleep(5); - - for (key = 0; key < INTERVAL_MAX; key++) { - unsigned long long value = 0; - bpf_map_lookup_elem(fd, &key, &value); - - if (key < INTERVAL_MAX - 1) - printf("Range %dms - %dms\tCount:%llu\n", - key * 10, (key + 1) * 10, value); - else - printf("Over 50ms\t\tCount:%llu\n", value); - } - - printf("=========================================\n"); - } - - return 0; -} diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/cma/cma_monitor.h b/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/cma/cma_monitor.h deleted file mode 100644 index b9b3eddb1..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/cma/cma_monitor.h +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause -/* Copyright (c) 2022 Jacky Yin */ -#ifndef __CMA_MONITOR_H -#define __CMA_MONTOR_H - - - - - -#endif /* __CMA_MONTOR_H */ diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/ion/ion_monitor.bpf.c b/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/ion/ion_monitor.bpf.c deleted file mode 100644 index e91936618..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/ion/ion_monitor.bpf.c +++ /dev/null @@ -1,63 +0,0 @@ -#include "vmlinux.h" -#include -#include -#include -#include "ion_monitor.h" - -#define INTERVAL_MAX 6U - -char LICENSE[] SEC("license") = "Dual BSD/GPL"; -struct { - __uint(type, BPF_MAP_TYPE_HASH); - __uint(max_entries, 8192); - __type(key, unsigned); - __type(value, u64); -} count_map SEC(".maps"); - -struct { - __uint(type, BPF_MAP_TYPE_HASH); - __uint(max_entries, 8192); - __type(key, u32); - __type(value, u64); -} time_map SEC(".maps"); - -SEC("kprobe/ion_alloc") -int bpf_prog1(void *ctx) -{ - u32 pid = bpf_get_current_pid_tgid() >> 32; - u64 time = bpf_ktime_get_ns(); - u64 ts = bpf_ktime_get_ns(); - bpf_map_update_elem(&time_map, &pid, &ts, BPF_ANY); - - return 0; -} - -SEC("kprobe/ion_ioctl") -int bpf_prog2(void *ctx) -{ - u32 pid = bpf_get_current_pid_tgid() >> 32; - u64 tm = bpf_ktime_get_ns(); - - u64 *tsp = bpf_map_lookup_elem(&time_map, &pid); - if (tsp) - tm -= *tsp; - else - return -1; - - unsigned key = tm / 10000000;//10ms为区间单位 - if (key > INTERVAL_MAX - 1) - key = INTERVAL_MAX - 1; - u64 *value = bpf_map_lookup_elem(&count_map,&key); - if (value) { - *value += 1; - } else { - u64 init_value = 1; - bpf_map_update_elem(&count_map, &key, &init_value, BPF_ANY); - } - - bpf_map_delete_elem(&time_map, &pid); - - return 0; -} - -char _license[] SEC("license") = "GPL"; diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/ion/ion_monitor.c b/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/ion/ion_monitor.c deleted file mode 100644 index d22a7e004..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/ion/ion_monitor.c +++ /dev/null @@ -1,57 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -//#include -#include "ion_monitor.h" -#include "ion_monitor.skel.h" -#include - - - -#define INTERVAL_MAX 6U -int main(int argc, char **argv) -{ - /* - char file_name[200]; - - snprintf(file_name, sizeof(file_name), "%s_kern.o", argv[0]); - if (load_bpf_file(file_name)) { - printf("%s", bpf_log_buf); - - return 1; - }*/ - struct ion_monitor_bpf *skel = ion_monitor_bpf__open_and_load(); - if (!skel) { - fprintf(stderr, "Failed to open BPF skeleton\n"); - return 1; - } - - int fd = bpf_map__fd(skel->maps.time_map); - int key; - - for(;;) { - sleep(10); - - for (key = 0; key < INTERVAL_MAX; key++) { - unsigned long long value = 0; - bpf_map_lookup_elem(fd, &key, &value); - if (key < INTERVAL_MAX - 1) - printf("Range %dms - %dms\tCount:%llu\n", - key * 10, (key + 1) * 10, value); - else - printf("Over 50ms\t\tCount:%llu\n", value); - } - - printf("==========================================\n"); - } - - return 0; -} - - - - diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/ion/ion_monitor.h b/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/ion/ion_monitor.h deleted file mode 100644 index e6712713e..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/ion/ion_monitor.h +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause -/* Copyright (c) 2022 Jacky Yin */ -#ifndef __ION_MONITOR_H -#define __ION_MONITOR_H - - - - - -#endif /* __ION_MONTOR_H */ diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/paf/Makefile b/eBPF_Supermarket/Memory_Subsystem/old_project/paf/Makefile index 04f7a181a..75cfbb57c 100644 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/paf/Makefile +++ b/eBPF_Supermarket/Memory_Subsystem/old_project/paf/Makefile @@ -1,8 +1,8 @@ # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) OUTPUT := .output CLANG ?= clang -LIBBPF_SRC := $(abspath ../../libbpf/src) -BPFTOOL_SRC := $(abspath ../../bpftool/src) +LIBBPF_SRC := $(abspath ../../../lib/libbpf/src) +BPFTOOL_SRC := $(abspath ../../../lib/bpftool/src) LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/ion/Makefile b/eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/Makefile similarity index 93% rename from eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/ion/Makefile rename to eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/Makefile index e9e8ef726..6fc92d3a9 100644 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/ion/Makefile +++ b/eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/Makefile @@ -1,10 +1,10 @@ # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) OUTPUT := .output CLANG ?= clang -LIBBPF_SRC := $(abspath ../libbpf-bootstrap/libbpf/src) -BPFTOOL_SRC := $(abspath ../libbpf-bootstrap/bpftool/src) +LIBBPF_SRC := $(abspath ../../../lib/libbpf/src) +BPFTOOL_SRC := $(abspath ../../../lib/bpftool/src) LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) -BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) +BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/../bpftool) BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \ @@ -14,7 +14,7 @@ ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \ | sed 's/mips.*/mips/' \ | sed 's/riscv64/riscv/' \ | sed 's/loongarch64/loongarch/') -VMLINUX := ../libbpf-bootstrap/vmlinux/$(ARCH)/vmlinux.h +VMLINUX := ../../vmlinux/$(ARCH)/vmlinux.h # Use our own libbpf API headers and Linux UAPI headers distributed with # libbpf to avoid dependency on system-wide headers, which could be missing or # outdated @@ -22,7 +22,7 @@ INCLUDES := -I$(OUTPUT) -I../../libbpf/include/uapi -I$(dir $(VMLINUX)) CFLAGS := -g -Wall ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) -APPS = ion_monitor +APPS = time # Get Clang's default includes on this system. We'll explicitly add these dirs # to the includes list when compiling with `-target bpf` because otherwise some diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/page_fault.bpf.c b/eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/page_fault.bpf.c new file mode 100644 index 000000000..073dd01b4 --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/page_fault.bpf.c @@ -0,0 +1,67 @@ +#include "vmlinux.h" +#include +#include +#include +#include "time.h" + + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 8192); + __type(key, pid_t); + __type(value, u64); +} exec_start SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} rb SEC(".maps"); + +//动态挂载 +SEC("kprobe/handle_mm_fault") +int BPF_KPROBE(handle_mm_fault_enter) +{ + unsigned int pid; + u64 current_time; + pid=bpf_get_current_pid_tgid();//pid + + current_time= bpf_ktime_get_ns()/1000;//挂载前进程时间 + bpf_map_update_elem(&exec_start, &pid, ¤t_time, BPF_ANY);//更新map元素 + + //bpf_printk("KPROBE ENTRY pid=%d,comm=%s,state=%d,current_time=%d\n",pid,comm,state,current_time); + return 0; +} + +SEC("kretprobe/handle_mm_fault") +int BPF_KRETPROBE(handle_mm_fault_exit) +{ + struct task_struct *t = (struct task_struct *)bpf_get_current_task();//获取task_struct结构体数据 + struct event *e; + unsigned int pid; + u64 *start_ts; + u64 duration_ns = 0; + char comm[20]; + + /* get PID and TID of exiting thread/process */ + + pid = bpf_get_current_pid_tgid(); + + start_ts=bpf_map_lookup_elem(&exec_start,&pid); + if (!start_ts) + return 0; + duration_ns = bpf_ktime_get_ns()/1000 - *start_ts;//获取系统进程执行时间 + bpf_map_delete_elem(&exec_start,&pid);//删除当前进程pid对应键值对 + //ringbuf提交 + e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);//malloc + if (!e) + return 0; + + e->duration_ns=duration_ns; + e->pid=pid; + bpf_get_current_comm(&e->comm,sizeof(e->comm)); + e->state=BPF_CORE_READ(t,__state); + bpf_ringbuf_submit(e, 0); + return 0; +} + diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/page_fault.c b/eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/page_fault.c new file mode 100644 index 000000000..570ce5e7e --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/page_fault.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +/* Copyright (c) 2020 Facebook */ +#include +#include +#include +#include +#include +#include +#include "time.h" +#include "time.skel.h" + + +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; +} + +static int handle_event(void *ctx, void *data, size_t data_sz) +{ + const struct event *e = data; + printf("%-8d %-5d %-16lld %-7s\n", e->pid, e->state, e->duration_ns,e->comm); + return 0; +} + +int main(int argc, char **argv) +{ + struct ring_buffer *rb = NULL; + struct time_bpf *skel; + int err; + + + + /* Set up libbpf errors and debug info callback */ + libbpf_set_print(libbpf_print_fn); + + /* Cleaner handling of Ctrl-C */ + signal(SIGINT, sig_handler); + signal(SIGTERM, sig_handler); + + /* Load and verify BPF application */ + skel = time_bpf__open(); + if (!skel) { + fprintf(stderr, "Failed to open and load BPF skeleton\n"); + return 1; + } + + /* Load & verify BPF programs */ + err = time_bpf__load(skel); + if (err) { + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); + goto cleanup; + } + + /* Attach tracepoints */ + err = time_bpf__attach(skel); + if (err) { + fprintf(stderr, "Failed to attach BPF skeleton\n"); + goto cleanup; + } + + /* Set up ring buffer polling */ + 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; + } + + /* Process events */ + + while (!exiting) { + err = ring_buffer__poll(rb, 100 /* timeout, ms */); + /* Ctrl-C will cause -EINTR */ + if (err == -EINTR) { + err = 0; + break; + } + if (err < 0) { + printf("Error polling perf buffer: %d\n", err); + break; + } + } + +cleanup: + /* Clean up */ + ring_buffer__free(rb); + time_bpf__destroy(skel); + + return err < 0 ? -err : 0; +} + + + diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/page_fault.h b/eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/page_fault.h new file mode 100644 index 000000000..bf9de6845 --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/page_fault.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +/* Copyright (c) 2020 Facebook */ +#ifndef __BOOTSTRAP_H +#define __BOOTSTRAP_H + +#define TASK_COMM_LEN 16 +#define MAX_FILENAME_LEN 127 + +struct event { + int pid; + unsigned int state; + unsigned long long duration_ns; + bool exit_event; + char comm[TASK_COMM_LEN]; +}; + +#endif /* __BOOTSTRAP_H */ diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/pr/Makefile b/eBPF_Supermarket/Memory_Subsystem/old_project/pr/Makefile index 5b76fdb3e..fe15282f5 100644 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/pr/Makefile +++ b/eBPF_Supermarket/Memory_Subsystem/old_project/pr/Makefile @@ -1,8 +1,8 @@ # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) OUTPUT := .output CLANG ?= clang -LIBBPF_SRC := $(abspath ../../libbpf/src) -BPFTOOL_SRC := $(abspath ../../bpftool/src) +LIBBPF_SRC := $(abspath ../../../lib/libbpf/src) +BPFTOOL_SRC := $(abspath ../../../lib/bpftool/src) LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/procstat/Makefile b/eBPF_Supermarket/Memory_Subsystem/old_project/procstat/Makefile index 28dcdc12c..a28526400 100644 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/procstat/Makefile +++ b/eBPF_Supermarket/Memory_Subsystem/old_project/procstat/Makefile @@ -1,8 +1,8 @@ # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) OUTPUT := .output CLANG ?= clang -LIBBPF_SRC := $(abspath ../../libbpf/src) -BPFTOOL_SRC := $(abspath ../../bpftool/src) +LIBBPF_SRC := $(abspath ../../../lib/libbpf/src) +BPFTOOL_SRC := $(abspath ../../../lib/bpftool/src) LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/sysstat/Makefile b/eBPF_Supermarket/Memory_Subsystem/old_project/sysstat/Makefile index 422ff2fc3..5f33e0a2a 100644 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/sysstat/Makefile +++ b/eBPF_Supermarket/Memory_Subsystem/old_project/sysstat/Makefile @@ -1,8 +1,8 @@ # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) OUTPUT := .output CLANG ?= clang -LIBBPF_SRC := $(abspath ../../libbpf/src) -BPFTOOL_SRC := $(abspath ../../bpftool/src) +LIBBPF_SRC := $(abspath ../../../lib/libbpf/src) +BPFTOOL_SRC := $(abspath ../../../lib/bpftool/src) LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool diff --git a/eBPF_Supermarket/Network_Subsystem/TrafficManager/go.mod b/eBPF_Supermarket/Network_Subsystem/TrafficManager/go.mod index 2c47343f5..6fba00706 100644 --- a/eBPF_Supermarket/Network_Subsystem/TrafficManager/go.mod +++ b/eBPF_Supermarket/Network_Subsystem/TrafficManager/go.mod @@ -3,7 +3,7 @@ module lmp/eTrafficManager go 1.20 require ( - github.com/cilium/cilium v1.14.8 + github.com/cilium/cilium v1.14.14 github.com/cilium/ebpf v0.11.0 github.com/prometheus/client_golang v1.16.0 github.com/prometheus/common v0.42.0 @@ -18,7 +18,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/emicklei/go-restful/v3 v3.10.2 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/logr v1.4.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-openapi/analysis v0.21.4 // indirect github.com/go-openapi/errors v0.20.4 // indirect @@ -30,11 +30,11 @@ require ( github.com/go-openapi/swag v0.22.4 // indirect github.com/go-openapi/validate v0.22.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/uuid v1.3.1 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -54,7 +54,7 @@ require ( github.com/sasha-s/go-deadlock v0.3.1 // indirect github.com/shirou/gopsutil/v3 v3.23.5 // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/spf13/afero v1.9.5 // indirect + github.com/spf13/afero v1.10.0 // indirect github.com/spf13/cast v1.5.1 // indirect github.com/spf13/cobra v1.7.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -68,22 +68,22 @@ require ( github.com/yusufpapurcu/wmi v1.2.3 // indirect go.mongodb.org/mongo-driver v1.11.3 // indirect golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect - golang.org/x/net v0.23.0 // indirect - golang.org/x/oauth2 v0.11.0 // indirect - golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/term v0.18.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/net v0.27.0 // indirect + golang.org/x/oauth2 v0.18.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/term v0.22.0 // indirect + golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.3.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.33.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.100.1 // indirect k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect - k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect + k8s.io/utils v0.0.0-20240310230437-4693a0247e57 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect diff --git a/eBPF_Supermarket/Network_Subsystem/TrafficManager/go.sum b/eBPF_Supermarket/Network_Subsystem/TrafficManager/go.sum index 101946f56..073b75e00 100644 --- a/eBPF_Supermarket/Network_Subsystem/TrafficManager/go.sum +++ b/eBPF_Supermarket/Network_Subsystem/TrafficManager/go.sum @@ -52,8 +52,8 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/checkmate v1.0.3 h1:CQC5eOmlAZeEjPrVZY3ZwEBH64lHlx9mXYdUehEwI5w= -github.com/cilium/cilium v1.14.8 h1:Jm54iA7XxWJn+GdZrfzcEyeoNUb58w8y0g8ZB2lVcJw= -github.com/cilium/cilium v1.14.8/go.mod h1:7cWSSEl+dU9Q5CqnEWT//c0w8yLSazShw287Uop6xVs= +github.com/cilium/cilium v1.14.14 h1:KYxWuIcibhR2ad1IjAoUT1ofQt93vtSEQwkYRujjgus= +github.com/cilium/cilium v1.14.14/go.mod h1:Av3AtSEaR3A93p4saMzhNBCdCnmyvUGQYu8oueKmKIA= github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y= github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= @@ -81,8 +81,8 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= @@ -176,8 +176,8 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -194,8 +194,9 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -216,8 +217,8 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJY github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= @@ -323,8 +324,8 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= -github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= +github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= @@ -352,7 +353,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= @@ -376,6 +377,7 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= @@ -398,6 +400,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -435,6 +438,7 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -468,8 +472,9 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -479,8 +484,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= -golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= +golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= +golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -493,8 +498,9 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -539,16 +545,19 @@ golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.9.1-0.20230616193735-e0c3b6e6ae3b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -558,8 +567,9 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -618,7 +628,8 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -648,8 +659,9 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -715,8 +727,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -756,8 +768,8 @@ k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= -k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY= -k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20240310230437-4693a0247e57 h1:gbqbevonBh57eILzModw6mrkbwM0gQBEuevE/AaBsHY= +k8s.io/utils v0.0.0-20240310230437-4693a0247e57/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/Makefile b/eBPF_Supermarket/Network_Subsystem/net_manager/Makefile index 7bbe2ad6b..f1bf6eb80 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_manager/Makefile +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/Makefile @@ -1,36 +1,159 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Compiler and Tools +LLC ?= llc +CLANG ?= clang +CC ?= gcc + +# Targets +XDP_TARGETS := netmanager_kern +USER_TARGETS := netmanager + +XDP_C = ${XDP_TARGETS:=.c} +XDP_OBJ = ${XDP_C:.c=.o} +USER_C := ${USER_TARGETS:=.c} +USER_OBJ := ${USER_C:.c=.o} + +# Directories +COMMON_DIR = ./common +LIB_DIR ?= ./lib +LOADER_DIR ?= $(LIB_DIR)/xdp-tools/xdp-loader +STATS_DIR ?= $(COMMON_DIR)/../basic-solutions + +# Common Objects and Dependencies +COMMON_OBJS += $(COMMON_DIR)/common_user_bpf_xdp.o $(COMMON_DIR)/common_params.o +EXTRA_DEPS := $(COMMON_DIR)/parsing_helpers.h +COMMON_H := ${COMMON_OBJS:.o=.h} + +include $(LIB_DIR)/defines.mk +KERN_USER_H ?= $(wildcard common_kern_user.h) +COMMON_MK = $(COMMON_DIR)/common.mk + +# Extra Linker/Compiler Flags +CFLAGS += -I$(LIB_DIR)/install/include $(EXTRA_CFLAGS) -g +BPF_CFLAGS += -I$(LIB_DIR)/install/include $(EXTRA_CFLAGS) -g +LDFLAGS += -L$(LIB_DIR)/install/lib + +# Verbosity Control ifeq ("$(origin V)", "command line") -VERBOSE = $(V) + VERBOSE = $(V) endif ifndef VERBOSE -VERBOSE = 0 + VERBOSE = 0 endif ifeq ($(VERBOSE),0) -MAKEFLAGS += --no-print-directory -s -Q = @ + MAKEFLAGS += --no-print-directory -s + Q = @ endif +# Projects and Cleaning PROJ := xacl_ip router xacl_mac xstate - - - PROJ_CLEAN = $(addsuffix _clean,$(PROJ)) -.PHONY: clean clobber distclean $(PROJ) $(PROJ_CLEAN) +.PHONY: clean clobber distclean $(PROJ) $(PROJ_CLEAN) $(CLANG) $(LLC) + +# Default Target +# all: lib $(PROJ) net_manager +all: lib $(PROJ) netmanager_kern.o netmanager_kern.skel.h net_manager -all: lib $(PROJ) +# Clean Target clean: $(PROJ_CLEAN) @echo; echo common; $(MAKE) -C common clean @echo; echo lib; $(MAKE) -C lib clean - + $(Q)rm -f $(USER_TARGETS) $(XDP_OBJ) $(USER_OBJ) $(COPY_LOADER) $(COPY_STATS) *.ll $(XLB_OBJS) netmanager_kern.skel.h + +# Build Library lib: config.mk check_submodule @echo; echo $@; $(MAKE) -C $@ +# Project Build Targets $(PROJ): @echo; echo $@; $(MAKE) -C $@ +# Main Target +net_manager: llvm-check $(USER_TARGETS) $(XDP_OBJ) $(COPY_LOADER) $(COPY_STATS) + +netmanager_kern.skel.h: netmanager_kern.o + sudo bpftool gen skeleton netmanager_kern.o > netmanager_kern.skel.h + +# Copy Loader +ifdef COPY_LOADER +$(LOADER_DIR)/$(COPY_LOADER): + $(Q)make -C $(LOADER_DIR) + +$(COPY_LOADER): $(LOADER_DIR)/$(COPY_LOADER) + $(QUIET_COPY)cp $(LOADER_DIR)/$(COPY_LOADER) $(COPY_LOADER) +endif + +# Copy Stats +ifdef COPY_STATS +$(STATS_DIR)/$(COPY_STATS): $(STATS_DIR)/${COPY_STATS:=.c} $(COMMON_H) + $(Q)make -C $(STATS_DIR) $(COPY_STATS) + +$(COPY_STATS): $(STATS_DIR)/$(COPY_STATS) + $(QUIET_COPY)cp $(STATS_DIR)/$(COPY_STATS) $(COPY_STATS) +EXTRA_DEPS += $(COMMON_DIR)/xdp_stats_kern.h $(COMMON_DIR)/xdp_stats_kern_user.h +endif + +# LLVM Tool Check +llvm-check: $(CLANG) $(LLC) + @for TOOL in $^ ; do \ + if [ ! $$(command -v $${TOOL} 2>/dev/null) ]; then \ + echo "*** ERROR: Cannot find tool $${TOOL}" ;\ + exit 1; \ + else true; fi; \ + done + +$(OBJECT_LIBBPF): + @if [ ! -d $(LIBBPF_DIR) ]; then \ + echo "Error: Need libbpf submodule" $(LIBBPF_DIR); \ + echo "May need to run git submodule update --init"; \ + exit 1; \ + else \ + cd $(LIBBPF_DIR) && $(MAKE) all OBJDIR=.; \ + mkdir -p build; $(MAKE) install_headers DESTDIR=build OBJDIR=.; \ + fi + +$(OBJECT_LIBXDP): + @if [ ! -d $(LIBXDP_DIR) ]; then \ + echo "Error: Need libxdp submodule" $(LIBXDP_DIR); \ + echo "May need to run git submodule update --init"; \ + exit 1; \ + else \ + cd $(LIBXDP_DIR) && $(MAKE) all OBJDIR=.; \ + fi + +# Create dependency: detect if C-file change and touch H-file, to trigger +# target $(COMMON_OBJS) +$(COMMON_H): %.h: %.c + touch $@ + +# Detect if any of common obj changed and create dependency on .h-files +$(COMMON_OBJS): %.o: %.h + $(Q)$(MAKE) -C $(COMMON_DIR) + +# $(USER_TARGETS): %: %.c $(OBJECT_LIBBPF) $(OBJECT_LIBXDP) Makefile $(COMMON_MK) $(COMMON_OBJS) $(KERN_USER_H) $(EXTRA_DEPS) $(XLB_OBJS) +# $(QUIET_CC)$(CC) -Wall $(CFLAGS) $(LDFLAGS) -o $@ $(COMMON_OBJS) $(XLB_OBJS) $(LIB_OBJS) \ +# $< $(LDLIBS) +$(USER_TARGETS): %: %.c netmanager_kern.skel.h $(OBJECT_LIBBPF) $(OBJECT_LIBXDP) Makefile $(COMMON_MK) $(COMMON_OBJS) $(KERN_USER_H) $(EXTRA_DEPS) $(XLB_OBJS) + $(QUIET_CC)$(CC) -Wall $(CFLAGS) $(LDFLAGS) -o $@ $(COMMON_OBJS) $(XLB_OBJS) $(LIB_OBJS) \ + $< $(LDLIBS) + +$(XDP_OBJ): %.o: %.c Makefile $(COMMON_MK) $(KERN_USER_H) $(EXTRA_DEPS) $(OBJECT_LIBBPF) + $(QUIET_CLANG)$(CLANG) -S \ + -target bpf \ + -D __BPF_TRACING__ \ + $(BPF_CFLAGS) \ + -Wall \ + -Wno-unused-value \ + -Wno-pointer-sign \ + -Wno-compare-distinct-pointer-types \ + -Werror \ + -O2 -emit-llvm -c -g -o ${@:.o=.ll} $< + $(QUIET_LLC)$(LLC) -march=bpf -filetype=obj -o $@ ${@:.o=.ll} + + $(PROJ_CLEAN): @echo; echo $@; $(MAKE) -C $(subst _clean,,$@) clean @@ -51,4 +174,3 @@ check_submodule: echo " consider running: git submodule update" ;\ echo "" ;\ fi\ - diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/bpf_use_errno_test.o b/eBPF_Supermarket/Network_Subsystem/net_manager/bpf_use_errno_test.o deleted file mode 100644 index 77fad42d6..000000000 Binary files a/eBPF_Supermarket/Network_Subsystem/net_manager/bpf_use_errno_test.o and /dev/null differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/common/common_defines.h b/eBPF_Supermarket/Network_Subsystem/net_manager/common/common_defines.h index 167822a1f..d02e44b2f 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_manager/common/common_defines.h +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/common/common_defines.h @@ -1,11 +1,12 @@ #ifndef __COMMON_DEFINES_H #define __COMMON_DEFINES_H -#include -#include -#include #include + +#define FILE_MAXSIZE 128 +#define IF_NAMESIZE 16 + struct config { enum xdp_attach_mode attach_mode; __u32 xdp_flags; @@ -27,6 +28,20 @@ struct config { int xsk_if_queue; bool xsk_poll_mode; bool unload_all; + bool show_stats; // 数据统计 + bool ip_filter; //ip过滤 + bool mac_filter; //mac过滤 + bool router; //路由 + bool state; //会话保持 + bool clear; //清理 + char *ip_filter_file; + char ip_filter_file_buf[FILE_MAXSIZE]; + char *mac_filter_file; + char mac_filter_file_buf[FILE_MAXSIZE]; + char *router_file; + char router_file_buf[FILE_MAXSIZE]; + bool print_info; + bool socketmap_flag; }; /* Defined in common_params.o */ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/common/common_params.c b/eBPF_Supermarket/Network_Subsystem/net_manager/common/common_params.c index 44d07f39e..6329a9540 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_manager/common/common_params.c +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/common/common_params.c @@ -16,66 +16,124 @@ int verbose = 1; #define BUFSIZE 30 +/** + * @brief 打印选项的帮助信息 + * + * @param long_options 包含所有长选项的结构体数组 + * @param required 标志位,用于指示是否打印必需的选项 + */ void _print_options(const struct option_wrapper *long_options, bool required) { - int i, pos; - char buf[BUFSIZE]; - - for (i = 0; long_options[i].option.name != 0; i++) { - if (long_options[i].required != required) - continue; - - if (long_options[i].option.val > 64) /* ord('A') = 65 */ - printf(" -%c,", long_options[i].option.val); - else - printf(" "); - pos = snprintf(buf, BUFSIZE, " --%s", long_options[i].option.name); - if (long_options[i].metavar) - snprintf(&buf[pos], BUFSIZE-pos, " %s", long_options[i].metavar); - printf("%-22s", buf); - printf(" %s", long_options[i].help); - printf("\n"); - } + int i, pos; + char buf[BUFSIZE]; + + // 遍历所有的长选项 + for (i = 0; long_options[i].option.name != 0; i++) { + // 如果选项的必需性与参数不符,则跳过 + if (long_options[i].required != required) + continue; + + // 如果选项的短名称为大写字母,打印其短名称 + if (long_options[i].option.val > 64) /* ord('A') = 65 */ + printf(" -%c,", long_options[i].option.val); + else + // 否则不打印短名称,保留空白对齐 + printf(" "); + + // 将选项的长名称及其元变量(如果有)格式化到 buf 中 + pos = snprintf(buf, BUFSIZE, " --%s", long_options[i].option.name); + if (long_options[i].metavar) + snprintf(&buf[pos], BUFSIZE-pos, " %s", long_options[i].metavar); + + // 打印格式化后的选项名称和帮助信息 + printf("%-22s", buf); + printf(" %s", long_options[i].help); + printf("\n"); + } } + +/** + * @brief 打印程序的用法信息 + * + * @param prog_name 程序名 + * @param doc 程序的文档说明 + * @param long_options 包含所有长选项的结构体数组 + * @param full 标志位,是否打印完整的帮助信息 + */ void usage(const char *prog_name, const char *doc, const struct option_wrapper *long_options, bool full) { - printf("Usage: %s [options]\n", prog_name); + // 打印用法信息的基本格式 + printf("Usage: %s [options]\n", prog_name); - if (!full) { - printf("Use --help (or -h) to see full option list.\n"); - return; - } + // 如果不需要打印完整的帮助信息 + if (!full) { + // 提示用户使用 --help 或 -h 查看完整的选项列表 + printf("Use --help (or -h) to see full option list.\n"); + return; + } - printf("\nDOCUMENTATION:\n %s\n", doc); - printf("Required options:\n"); - _print_options(long_options, true); - printf("\n"); - printf("Other options:\n"); - _print_options(long_options, false); - printf("\n"); + // 打印文档说明 + printf("\nDOCUMENTATION:\n %s\n", doc); + + // 打印必需选项 + printf("Required options:\n"); + _print_options(long_options, true); + printf("\n"); + + // 打印其他选项 + printf("Other options:\n"); + _print_options(long_options, false); + printf("\n"); } + +/** + * @brief 将 option_wrapper 结构体数组转换为标准的 option 结构体数组 + * + * @param wrapper 包含所有长选项的结构体数组 + * @param options 输出参数,用于存储转换后的 option 结构体数组 + * @return int 成功返回0,失败返回-1 + */ int option_wrappers_to_options(const struct option_wrapper *wrapper, struct option **options) { int i, num; struct option *new_options; + + // 计算 wrapper 数组中的选项数量 for (i = 0; wrapper[i].option.name != 0; i++) {} num = i; + // 分配新的 option 数组内存 new_options = malloc(sizeof(struct option) * num); if (!new_options) + // 如果内存分配失败,返回 -1 return -1; + + // 将 wrapper 数组中的每个 option 复制到新的 option 数组中 for (i = 0; i < num; i++) { memcpy(&new_options[i], &wrapper[i], sizeof(struct option)); } + // 将新分配并填充的 option 数组赋值给输出参数 *options *options = new_options; + + // 成功返回 0 return 0; } + +/** + * @brief 解析命令行参数 + * + * @param argc 参数个数 + * @param argv 参数值数组 + * @param options_wrapper 包含所有长选项的结构体数组 + * @param cfg 配置结构体,用于存储解析结果 + * @param doc 程序的文档说明 + */ void parse_cmdline_args(int argc, char **argv, const struct option_wrapper *options_wrapper, struct config *cfg, const char *doc) @@ -86,22 +144,26 @@ void parse_cmdline_args(int argc, char **argv, char *dest; int opt; + // 将 option_wrapper 结构体数组转换为标准的 option 结构体数组 if (option_wrappers_to_options(options_wrapper, &long_options)) { fprintf(stderr, "Unable to malloc()\n"); exit(EXIT_FAIL_OPTION); } - /* Parse commands line args */ - while ((opt = getopt_long(argc, argv, "hd:r:L:R:ASNFU:MQ:czpq", + /* 解析命令行参数 */ + while ((opt = getopt_long(argc, argv, "hd:r:L:R:ASNFUMQ:czpq:i:m:k:g:n:tTf", long_options, &longindex)) != -1) { switch (opt) { case 'd': + // 检查设备名称长度是否超出限制 if (strlen(optarg) >= IF_NAMESIZE) { fprintf(stderr, "ERR: --dev name too long\n"); goto error; } + // 设置设备名称 cfg->ifname = (char *)&cfg->ifname_buf; strncpy(cfg->ifname, optarg, IF_NAMESIZE); + // 获取设备索引 cfg->ifindex = if_nametoindex(cfg->ifname); if (cfg->ifindex == 0) { fprintf(stderr, @@ -111,12 +173,15 @@ void parse_cmdline_args(int argc, char **argv, } break; case 'r': + // 检查重定向设备名称长度是否超出限制 if (strlen(optarg) >= IF_NAMESIZE) { fprintf(stderr, "ERR: --redirect-dev name too long\n"); goto error; } + // 设置重定向设备名称 cfg->redirect_ifname = (char *)&cfg->redirect_ifname_buf; strncpy(cfg->redirect_ifname, optarg, IF_NAMESIZE); + // 获取重定向设备索引 cfg->redirect_ifindex = if_nametoindex(cfg->redirect_ifname); if (cfg->redirect_ifindex == 0) { fprintf(stderr, @@ -125,73 +190,144 @@ void parse_cmdline_args(int argc, char **argv, goto error; } break; + case 't': + cfg->show_stats = true; + break; + case 'i': + cfg->ip_filter = true; + // 检查文件路径长度是否超出限制 + if (strlen(optarg) >= FILE_MAXSIZE) { + fprintf(stderr, "ERR: --ip_filter_file name too long\n"); + goto error; + } + // 设置文件路径 + cfg->ip_filter_file = (char *)&cfg->ip_filter_file_buf; //初始化ip_filter_file + strncpy(cfg->ip_filter_file, optarg, FILE_MAXSIZE); + //printf("%s %s\n",optarg,cfg->ip_filter_file); + break; + case 'm': + cfg->mac_filter = true; + // 检查文件路径长度是否超出限制 + if (strlen(optarg) >= FILE_MAXSIZE) { + fprintf(stderr, "ERR: --mac_filter_file name too long\n"); + goto error; + } + // 设置文件路径 + cfg->mac_filter_file = (char *)&cfg->mac_filter_file_buf; //初始化mac_filter_file + strncpy(cfg->mac_filter_file, optarg, FILE_MAXSIZE); + break; + case 'k': + cfg->router = true; + // 检查文件路径长度是否超出限制 + if (strlen(optarg) >= FILE_MAXSIZE) { + fprintf(stderr, "ERR: --router_file name too long\n"); + goto error; + } + // 设置文件路径 + cfg->router_file = (char *)&cfg->router_file_buf; //初始化router_file + strncpy(cfg->router_file, optarg, FILE_MAXSIZE); + break; + case 'g': + cfg->state = true; + break; + case 'n': + cfg->clear = true; + break; case 'A': + // 设置附加模式为未指定模式 cfg->attach_mode = XDP_MODE_UNSPEC; break; case 'S': + // 设置附加模式为 SKB 模式 cfg->attach_mode = XDP_MODE_SKB; cfg->xsk_bind_flags &= ~XDP_ZEROCOPY; cfg->xsk_bind_flags |= XDP_COPY; break; case 'N': + // 设置附加模式为原生模式 cfg->attach_mode = XDP_MODE_NATIVE; break; case 3: /* --offload-mode */ + // 设置附加模式为硬件模式 cfg->attach_mode = XDP_MODE_HW; break; case 'M': + // 启用重用地图 cfg->reuse_maps = true; break; case 'U': + // 设置卸载标志 cfg->do_unload = true; cfg->unload_all = true; - //cfg->prog_id = atoi(optarg); + // cfg->prog_id = atoi(optarg); break; case 'p': + // 启用轮询模式 cfg->xsk_poll_mode = true; break; case 'q': + // 设置为非详细模式 verbose = false; break; case 'Q': + // 设置接口队列 cfg->xsk_if_queue = atoi(optarg); break; case 1: /* --filename */ + // 设置文件名 dest = (char *)&cfg->filename; strncpy(dest, optarg, sizeof(cfg->filename)); break; case 2: /* --progname */ + // 设置程序名称 dest = (char *)&cfg->progname; strncpy(dest, optarg, sizeof(cfg->progname)); break; case 'L': /* --src-mac */ + // 设置源 MAC 地址 dest = (char *)&cfg->src_mac; strncpy(dest, optarg, sizeof(cfg->src_mac)); break; case 'R': /* --dest-mac */ + // 设置目的 MAC 地址 dest = (char *)&cfg->dest_mac; strncpy(dest, optarg, sizeof(cfg->dest_mac)); break; case 'c': + // 设置绑定标志为复制模式 cfg->xsk_bind_flags &= ~XDP_ZEROCOPY; cfg->xsk_bind_flags |= XDP_COPY; break; case 'z': + // 设置绑定标志为零拷贝模式 cfg->xsk_bind_flags &= ~XDP_COPY; cfg->xsk_bind_flags |= XDP_ZEROCOPY; break; case 4: /* --unload-all */ + // 设置卸载所有标志 cfg->unload_all = true; break; case 'h': + // 设置显示完整帮助信息的标志 full_help = true; /* fall-through */ + break; + case 'T': + // 设置打印的标志 + cfg->print_info = true; + break; + case 'f': + // 设置打印的标志 + cfg->socketmap_flag = true; + break; error: default: + // 打印使用信息并退出 usage(argv[0], doc, options_wrapper, full_help); free(long_options); exit(EXIT_FAIL_OPTION); } } + // 释放分配的内存 free(long_options); } diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/common_kern_user.h b/eBPF_Supermarket/Network_Subsystem/net_manager/common_kern_user.h new file mode 100644 index 000000000..11f3173b5 --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/common_kern_user.h @@ -0,0 +1,105 @@ +/* This common_kern_user.h is used by kernel side BPF-progs and + * userspace programs, for sharing common struct's and DEFINEs. + */ +#ifndef __COMMON_KERN_USER_H +#define __COMMON_KERN_USER_H + +#include + +typedef __u32 xdp_act; +#define ETH_ALEN 6 + +#define ALERT_ERR_STR "[XACL] ERROR:" + + +#define MAX_RULES 256 + + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + + +struct datarec { + __u64 rx_packets; + __u64 rx_bytes; +}; + +struct conn_ipv4 { + __u32 saddr; + __u32 daddr; + __u16 sport; + __u16 dport; + __u16 ip_proto; +}; + +struct rules_ipv4 { + __u32 saddr; + __u32 daddr; + __u8 saddr_mask; + __u8 daddr_mask; + __u16 sport; + __u16 dport; + __u16 ip_proto; + __u16 action; + __u16 prev_rule; + __u16 next_rule; +}; + +struct conn_mac { + unsigned char dest[ETH_ALEN]; + unsigned char source[ETH_ALEN]; +}; + +struct rules_mac { + unsigned char dest[ETH_ALEN]; + unsigned char source[ETH_ALEN]; + __u16 action; + __u16 prev_rule; + __u16 next_rule; +}; +// 转发表项 +struct rt_item { + __u32 saddr; + __u8 eth_source[ETH_ALEN]; // 封装帧的源MAC地址。 + __u8 eth_dest[ETH_ALEN]; // 封装帧的目标MAC地址。 +}; + +// mac 过滤 +struct mac_addr { + __u8 addr[ETH_ALEN]; +}; + + +// 会话保持 +struct conn_ipv4_key { + __u32 saddr; + __u32 daddr; + __u16 sport; + __u16 dport; + __u16 proto; +}; + +struct conn_ipv4_val { + __u32 tcp_state; + __u32 rid; +}; + +enum { + TCP_S_NONE = 0U, + TCP_S_ESTABLISHED, + TCP_S_SYN_SENT, + TCP_S_SYN_RECV, + TCP_S_FIN_WAIT1, + TCP_S_FIN_WAIT2, + TCP_S_CLOSE_WAIT, + TCP_S_CLOSE, +}; + + + +#ifndef XDP_ACTION_MAX +#define XDP_ACTION_MAX (XDP_REDIRECT + 1) +#endif + +#endif /* __COMMON_KERN_USER_H */ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/conf.d/black_ipv4.conf b/eBPF_Supermarket/Network_Subsystem/net_manager/conf.d/black_ipv4.conf new file mode 100644 index 000000000..ea5f7ac8c --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/conf.d/black_ipv4.conf @@ -0,0 +1,2 @@ +0.0.0.0/0 192.168.239.132/32 0 0 ICMP DENY +0.0.0.0/0 192.168.239.132/32 80 0 TCP DENY \ No newline at end of file diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/conf.d/mac_load.conf b/eBPF_Supermarket/Network_Subsystem/net_manager/conf.d/mac_load.conf new file mode 100644 index 000000000..b4a92a3d9 --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/conf.d/mac_load.conf @@ -0,0 +1,3 @@ +00:0c:29:57:00:4d 00:00:00:00:00:00 ALLOW +00:0c:29:00:00:00 00:00:00:00:00:00 DENY +00:00:00:00:00:00 00:00:00:00:00:00 ALLOW \ No newline at end of file diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/conf.d/router_load.conf b/eBPF_Supermarket/Network_Subsystem/net_manager/conf.d/router_load.conf new file mode 100644 index 000000000..ac552338c --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/conf.d/router_load.conf @@ -0,0 +1,2 @@ +0.0.0.0 00:0c:29:7b:a6:d9 00:0c:29:fd:69:58 +1.2.3.4 00:0c:29:7b:a6:d9 00:0c:29:dd:17:2c \ No newline at end of file diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/ip_filter1.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/ip_filter1.png new file mode 100644 index 000000000..5cccd9127 Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/ip_filter1.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/ip_filter2.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/ip_filter2.png new file mode 100644 index 000000000..084a5e233 Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/ip_filter2.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/ip_filter3.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/ip_filter3.png new file mode 100644 index 000000000..cb4331a8e Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/ip_filter3.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/ip_filter4.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/ip_filter4.png new file mode 100644 index 000000000..1dea44779 Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/ip_filter4.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/mac_filter1.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/mac_filter1.png new file mode 100644 index 000000000..db204dbe4 Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/mac_filter1.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/mac_filter2.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/mac_filter2.png new file mode 100644 index 000000000..6d7d20631 Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/mac_filter2.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/mac_filter3.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/mac_filter3.png new file mode 100644 index 000000000..a875fa4d2 Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/mac_filter3.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/mac_filter4.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/mac_filter4.png new file mode 100644 index 000000000..28ca9a380 Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/mac_filter4.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/net_manager1.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/net_manager1.png new file mode 100644 index 000000000..0051b6295 Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/net_manager1.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/net_manager2.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/net_manager2.png new file mode 100644 index 000000000..eed5d0fc1 Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/net_manager2.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/net_manager3.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/net_manager3.png new file mode 100644 index 000000000..2655c5f4e Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/net_manager3.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/net_manager4.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/net_manager4.png new file mode 100644 index 000000000..e749ff86d Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/net_manager4.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/net_manager5.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/net_manager5.png new file mode 100644 index 000000000..e56504f20 Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/net_manager5.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/net_manager6.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/net_manager6.png new file mode 100644 index 000000000..75a714ac8 Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/net_manager6.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/net_manager7.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/net_manager7.png new file mode 100644 index 000000000..8cec2c143 Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/net_manager7.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/net_manager8.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/net_manager8.png new file mode 100644 index 000000000..902fb1981 Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/net_manager8.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/session_keep1.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/session_keep1.png new file mode 100644 index 000000000..31e1e5965 Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/session_keep1.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/session_keep2.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/session_keep2.png new file mode 100644 index 000000000..e392bc825 Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/image/session_keep2.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/ip_filter.md b/eBPF_Supermarket/Network_Subsystem/net_manager/document/ip_filter.md new file mode 100644 index 000000000..63d339e63 --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/document/ip_filter.md @@ -0,0 +1,149 @@ +## 黑白名单 + +### 概述 + +​ 本工具通过XDP技术,在内核层面实现高效的网络流量监控和过滤。通过配置规则文件,可以灵活地定义黑白名单策略,对特定IP、端口和协议的报文进行过滤。结合实际网络环境,这种工具能够有效提升网络安全性,减少恶意流量,优化网络性能,特别适用于需要高性能数据包处理的场景,如大型数据中心和云计算平台。 + +### 实现 + +![image-20240726104526418](./image/ip_filter1.png) + +#### 输入参数优化 + +在原先的黑白名单中,名单的路径参数十分固定 + +```c + if (cfg.ip_filter) { + load_bpf_map(); + err = load_handler_ipv4(argc - 3, argv + 6); + if (err) { + fprintf(stderr, "ERR: loading IP filter config file\n"); + return err; + } +``` + +因此其使用的方法十分固定,要求路径必须在相应的位置,如: + +``` +sudo ./netmanager -d ens33 -S --progname=xdp_entry_ipv4 -i conf.d/black_ipv4.conf -t +``` + +将其绑定到-i参数上 + +```c +cfg->ip_filter_file = (char *)&cfg->ip_filter_file_buf; //初始化ip_filter_file +strncpy(cfg->ip_filter_file, optarg, FILE_MAXSIZE); +``` + +通过先将 `cfg->ifname` 指向 `cfg->ifname_buf`,确保了 `cfg->ifname` 指向的是一个有效的缓冲区,然后再使用 `strncpy` 将字符串复制到该缓冲区中,从而避免了潜在的内存访问错误。 + +去掉这种限制 + +``` +sudo ./netmanager -d ens33 -S --progname=xdp_entry_ipv4 -i conf.d/black_ipv4.conf -t +``` + +#### 增添输出格式 + +原先的程序中,除了对所有策略的统计输出,并无其余输出,对可读性有一定的限制,现在通过增添filter_info将数据打印在日志中,使用户可以清晰看到相应规则所匹配到的条目信息。 + +### 使用方法 + +本功能的使用命令为 + +``` +sudo ./netmanager -d ens33 -S --progname=xdp_entry_ipv4 -i conf.d/black_ipv4.conf +``` + +也可以在其中加入-t/T选项,t参数会定时统计所有策略对应的报文数,T参数会输出所有匹配条目与策略 + +之后可以使用xdp-loader查看挂载程序及卸载 + +```shell +sudo xdp-loader status +``` + +![image-20240726114124092](./image/ip_filter2.png) + +其中可以看到对应网卡上挂载的XDP程序 + +当不使用时进行卸载 + +```shell +sudo xdp-loader unload ens33 --all +``` + +在 ./conf.d 目录里有样例规则文件 black_ipv4.conf 别代表条目名单。程序会按顺序逐行加载进BPF Map,同样,XDP程序执行时也会逐行匹配规则,所以写在前面的规则具有更高的优先级。每行规则的格式为: + +``` +[SIP/MASK] [DIP/MASK] [SPORT] [DPORT] [PROTO] [ALLOW/DENY] +``` + +其中分别为源地址/源码、目的地址/源码、源端口、目的端口、协议类型、条目策略。 + +需要注意,**XDP只对收包路径上的数据有效,因此此处的源地址/端口为另一端,而目的地址/端口为本机**。 + +**当某段字段为0时,代表不进行此处的过滤,为全部匹配**。如需要匹配所有的ICMP报文,则为 + +``` +0.0.0.0/0 0.0.0.0/0 0 0 ICMP DENY +``` + +若要实现黑名单,根据匹配的优先级顺序,则需要在规则的最后⼀条写上(也可不加),默认为ALLOW,当匹配不到其余规则时会默认进行PASS策略(但仍建议增添) + +```c +0.0.0.0/0 0.0.0.0/0 0 0 0 ALLOW +``` + +若要实现白名单,需要将最后⼀条规则写为(必须增添,否则没有实际效果) + +``` +0.0.0.0/0 0.0.0.0/0 0 0 0 DENY +``` + +如需禁止源IP为 172.17.0.0/24 ,目的IP为任意 0.0.0.0/0 ,源端⼝号为任意 0 ,⽬的端⼝号为 80 的 TCP 协议 + +``` +172.17.0.2/32 0.0.0.0/0 0 80 TCP DENY +``` + +对于掩码,本功能用以过滤同一IP组的报文,如 + +``` +192.168.239.0/24 192.168.239.132/32 0 0 ICMP DENY +``` + +此处的192.168.239.0/24表示掩码为24位,及仅留取前24位的IP用以匹配,同样的,**收到的实际报文也会按照此掩码屏蔽掉相应字节**, 所以此句将会屏蔽192.168.239.*地址发来的ICMP报文,所以此处192.168.239.0/24 中的0并无实际意义,其可以写为任意实际ip,但需注意掩码位数,尤其掩码不为8的整数倍时,但是本功能**建议屏蔽掉的相应字节归零**,便于阅读,如192.168.239.194/26应写为192.168.239.192/26,此处的192为194屏蔽掉后后6位的实际值。 + +最终给出示例,我们在配置文件中写入,用以配置规则,其实现类似黑名单的功能,及禁止所有目的地地址为本机的ICMP及TCP报文 + +```c +0.0.0.0/0 192.168.239.132/32 0 0 ICMP DENY +0.0.0.0/0 192.168.239.132/32 80 0 TCP DENY +0.0.0.0/0 0.0.0.0/0 0 0 0 ALLOW //此条目可省略 +``` + +之后加载到程序中 + +```bash +sudo ./xdp_loader -d ens33 -S --progname=xdp_entry_ipv4 -i conf.d/black_ipv4.conf -t +``` + +可以发现已经drop了所有报文 + +![image-20240726110021447](./image/ip_filter3.png) + + +### 输出分析 + +本功能将输出所有匹配到的报文,此为无其余输出,由于用户态程序只用于激活,不作为循环监听的载体,故在内核态进行输出,当XDP程序挂载到相应网卡并且与相应条目匹配后输出。 + +需要使用相关命令查看 + +``` +sudo cat /sys/kernel/debug/tracing/trace_pipe +``` + +![image-20240726153912838](./image/ip_filter4.png) + +其中可以看到符合匹配的相关报文信息,其中包括四元组、协议类型、XDP策略行为以及匹配条目的序号。 \ No newline at end of file diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/mac_filter.md b/eBPF_Supermarket/Network_Subsystem/net_manager/document/mac_filter.md new file mode 100644 index 000000000..ec11fc7ec --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/document/mac_filter.md @@ -0,0 +1,116 @@ +## MAC过滤 + +### 概述 + +​ 本工具通过XDP技术,在内核层实现了高效的MAC地址过滤,专注于基于设备物理地址的流量控制。MAC地址过滤能够在网络层面上直接识别和控制特定设备的访问权限,无需依赖上层协议的验证机制。通过配置特定设备的MAC地址黑白名单,能够有效防止未经授权的设备接入网络,确保网络安全性。 + +其主要应用在于 + +1. **硬件级别过滤**: MAC地址是网络接口卡的唯一标识,不会像IP地址那样频繁变化,因此在底层网络设备上做过滤更有效。 +2. **物理位置绑定**: 在局域网中,MAC地址通常和设备物理位置绑定,有助于对物理设备进行精确控制。 +3. **隔离内外网**:通过限制外部设备基于MAC地址接入本地网络,可以强化内外网隔离的策略,从而间接提高内部网络的安全性。 + +MAC地址仅在局域网中有效,跨路由器的网络(如广域网)无法通过MAC地址进行过滤 + +### 实现 + +总体框架流程如下: + +![image-20240825133247633](./image/mac_filter1.png) + +具体匹配策略如下: + +```c +int mac_match(__u8 *conn_mac, __u8 *rule_mac) { + __u8 zero_mac[ETH_ALEN] = {0}; // 全零的MAC地址 + + // 如果rule_mac全为零,匹配所有MAC地址 + if (bpf_memcmp(rule_mac, zero_mac, ETH_ALEN) == 0) { + return 1; + } + + // 如果rule_mac的后三个字节为零,且前三个字节与conn_mac相同 + if (bpf_memcmp(&rule_mac[3], zero_mac, 3) == 0) { + if (bpf_memcmp(conn_mac, rule_mac, 3) == 0) { + return 1; // 匹配前三字节 + } + } + + // 检查规则MAC与连接MAC是否完全匹配 + if (bpf_memcmp(rule_mac, conn_mac, ETH_ALEN) == 0) { + return 1; // 完全匹配 + } + + return 0; // 不匹配 +} +``` + +### 使用方法 + +本功能的使用命令为 + +```c +sudo ./netmanager -d ens33 -S --progname=xdp_entry_mac -m conf.d/mac_load.conf -t +``` + +之后可以使用xdp-loader查看挂载程序及卸载 + +在 ./conf.d 目录里有样例规则文件 mac_load.conf 代表条目名单。程序会按顺序逐行加载进BPF Map,同样,XDP程序执行时也会逐行匹配规则,所以写在前面的规则具有更高的优先级。每行规则的格式为: + +``` +[SOURCE_MAC] [DEST_MAC] [ALLOW/DENY] +``` + +其中分别为源MAC地址、目的MAC地址及条目策略。 + +需要注意,**XDP只对收包路径上的数据有效,因此此处的源为另一端,而目的为本机**。 + +**当某段字段为0时,代表不进行此处的过滤,为全部匹配**。 + +若要实现黑名单,根据匹配的优先级顺序,则需要在规则的最后⼀条写上(也可不加),默认为ALLOW,当匹配不到其余规则时会默认进行PASS策略(但仍建议增添) + +```c +00:00:00:00:00:00 00:00:00:00:00:00 ALLOW +``` + +若要实现白名单,需要将最后⼀条规则写为(必须增添,否则没有实际效果) + +```c +00:00:00:00:00:00 00:00:00:00:00:00 DENY +``` + +我们还对某一厂商的MAC地址进行泛化匹配,当前三字节不为0(固定厂商)且后三字节为0时,可以对其进行泛化,匹配到所有改厂商的MAC地址,如 + +``` +00:0c:29:00:00:00 00:00:00:00:00:00 ALLOW +``` + +最终给出实例,我们在规则配置文件中写入 + +```c +00:0c:29:57:00:4d 00:00:00:00:00:00 ALLOW +00:0c:29:00:00:00 00:00:00:00:00:00 DENY +00:00:00:00:00:00 00:00:00:00:00:00 ALLOW +``` + +其中,00:0c:29开头的MAC地址是VMware虚拟网卡固定分配的前缀 + +之后加载到程序中 + +```shell +sudo ./netmanager -d ens33 -S --progname=xdp_entry_mac -m conf.d/mac_load.conf -t +``` + +之后通过不同虚拟机使用ping/curl来连接该主机 + +当MAC地址为00:0c:29:57:00:4d(特定主机),其可以正常连接 + +![image-20240825132436960](./image/mac_filter2.png) + +而其余虚拟机进行访问时会被拒绝 + +![image-20240825132526078](./image/mac_filter3.png) + +可以看到,相应报文已经被DROP + +![image-20240825132551308](./image/mac_filter4.png) \ No newline at end of file diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/net_manager.md b/eBPF_Supermarket/Network_Subsystem/net_manager/document/net_manager.md new file mode 100644 index 000000000..969c6c49a --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/document/net_manager.md @@ -0,0 +1,209 @@ +## netmanager + +netmanager是一款基于 eBPF 技术的高效网络管理工具,核心技术是 eBPF 的 XDP模块。eBPF 是一种强大的内核技术,允许在不修改内核源代码的情况下执行自定义代码,而 XDP 则是 eBPF 的一个子集,专门用于高速数据包处理。NetManager 利用 XDP 的高速数据包处理能力,实现了过滤、转发、统计信息和会话保持等主要功能。这款工具通过在网络数据包进入内核堆栈之前处理它们,显著提升了数据包处理的性能和效率。在高流量环境下,NetManager 能够保持卓越的性能和低延迟,帮助用户优化网络管理,提升系统效率。 + +## 框架 + +目前netmanager的整体框架为 + +![image-20240711091259144](./image/net_manager1.png) + + + +netmanager 的整体框架从主程序入口(main)开始,首先进行启动程序并解析命令行参数(parse_cmdline_args)。解析得到的参数会被解译,并用于挂载相应的处理函数。接下来,程序会根据参数进行进一步操作,如加载对应的配置文件,初始化处理程序,包括加载IPv4、MAC地址和路由器的处理函数(load_handler_ipv4, load_handler_mac, load_handler_router)。 + +在用户态与内核态的交互中,用户态通过map与内核态进行数据交换。内核态的默认处理入口包括多个XDP处理函数,如xdp_entry_state、xdp_entry_ipv4、xdp_entry_router和xdp_entry_mac,这些函数分别负责不同类型数据包的解析和处理。xdp_entry_state负责筛选TCP报文,并根据新的报文标志位修改连接状态;xdp_entry_ipv4处理逐层解析报文,并匹配相应的IPv4过滤规则;xdp_entry_router则解析报文并进行标准匹配,通过bpf_fib_lookup实现路由转发;xdp_entry_mac则负责MAC报文的解析和过滤。 + +最终,所有的处理结果都会通过xdp_stats_record_action进行统计和记录,处理后的报文和字节数也会被记录下来。这一系列操作确保了NetManager能够高效地执行过滤、转发、统计信息和会话保持等主要功能,为用户提供了强大的网络管理能力。 + +## 功能 + +netmanager目前实现了过滤、转发、统计信息和会话保持四大功能 + +**xdp_entry_state** + +通过解析进入网络设备的数据包,跟踪和记录 IPv4 连接的状态变化,特别是 TCP 连接的状态。 + +1. **数据包解析**: + - 通过 `parse_ethhdr` 函数解析以太网头部,检查是否为 IPv4 数据包。 + - 如果是 IPv4 数据包,则继续解析 IP 头部,获取源地址(saddr)、目的地址(daddr)和协议(proto)。 +2. **TCP 连接处理**: + - 如果协议是 TCP,解析 TCP 头部,获取源端口(sport)和目的端口(dport)。 + - 创建或查找一个包含源地址、目的地址、源端口、目的端口和协议的连接键(conn_k)。 + - 在 `conn_ipv4_map` 哈希表中查找该连接键对应的连接值(p_conn_v)。如果找不到,交换源和目的地址端口后再次查找。 + - 如果依然找不到,并且是 TCP SYN 包(表示新连接请求),则创建一个新的连接项并插入到哈希表中,同时记录日志。 + - 如果找到现有连接项,处理 TCP 标志位以更新连接状态(如 SYN_SENT, SYN_RECV, ESTABLISHED, FIN_WAIT1, CLOSE 等)。 + - 根据不同的连接状态,更新哈希表或删除连接项,并记录日志。 +3. **UDP 连接处理**: + - 如果协议是 UDP,解析 UDP 头部,获取源端口和目的端口。 + - 打印除 SSH(端口 22)协议以外的所有连接信息(在调试模式下)。 +4. **日志记录**: + - 对新创建的连接项和状态变化的连接项记录日志,便于后续分析和调试。 +5. **状态记录**: + - 使用 `xdp_stats_record_action` 函数记录操作状态。 + +**xdp_entry_mac** + +其功能是根据源 MAC 地址查找映射表,决定是否放行数据包。如果源 MAC 地址在映射表中存在,则根据映射表中的值设置相应的操作(例如,放行、丢弃或重定向);如果不存在,则默认放行数据包。 + +**xdp_entry_router** + +功能是根据数据包的目标地址进行查找和转发处理,支持 IPv4 和 IPv6 两种协议。通过查找转发表和最长前缀匹配表,决定如何处理数据包(例如,放行、丢弃或重定向)。 + +​ 其核心功能是对路由进行匹配:解析 IP 头部,获取源地址 `ip4_saddr`。检查 IP 头部的 TTL创建并初始化路由表项 `nitem`,将源地址赋值给 `nitem.saddr`。调用 `match_rules` 函数进行精确查找转发表。 + +​ 如果找到匹配项,进行数据包的转发处理:减少 IP 头部的 TTL。更新以太网头部的目的地址和源地址。用 `bpf_redirect_map` 函数进行数据包重定向。 + +如果精确查找未找到匹配项,执行最长前缀匹配查找:初始化 `bpf_fib_lookup` 结构体 `ifib`,设置相关字段(如 IP 头部信息、接口索引等)。调用 bpf_fib_lookup函数进行查找,检查返回值并根据结果进行处理: + +- `BPF_FIB_LKUP_RET_SUCCESS`:查找成功,减少 IP 头部的 TTL,更新以太网头部的目的地址和源地址,调用 `bpf_redirect` 函数进行数据包重定向。 +- `BPF_FIB_LKUP_RET_BLACKHOLE`、`BPF_FIB_LKUP_RET_UNREACHABLE`、`BPF_FIB_LKUP_RET_PROHIBIT`:目标地址不可达,丢弃数据包。 +- 其他返回值:默认放行数据包。 + +**xdp_entry_ipv4** + +其功能是解析 IPv4 数据包的头部信息,记录连接信息,并根据预定义的规则对数据包进行处理。代码支持对 TCP 和 UDP 数据包的解析和处理,通过匹配规则函数 `match_rules_ipv4` 来决定数据包的最终处理方式。 + +## 代码运行实例 + +目前功能的具体使用操作如下, + +```c +黑白名单:加载到本地链路 ens33 上 + sudo ./netmanager -d ens33 --progname=xdp_entry_state -S +会话保持: + sudo ./netmanager -d ens33 -S --progname=xdp_entry_ipv4 -i conf.d/black_ipv4.conf + +``` + +**以会话保持为例** + +![image-20240711110643737](./image/net_manager2.png) + +可以借助xdp-loader工具进行操作 + +![image-20240711110713174](./image/net_manager3.png) + +可以看到我们的程序被挂载到相应的端口上 + +![image-20240711110826395](./image/net_manager4.png) + +可以看到其能抓到本机网络通信的各个报文,并获取其中的基本信息和连接状态。 + +#### 黑白名单 + +在 ./conf.d ⽬录里有样例规则⽂件 black_ipv4.conf 别代表黑白名单。程序会按顺序逐行加载进BPF Map,同样,XDP程序执行时也会逐⾏匹配规则,所以写在前面的规则具有更高的优先级。每行规则的格式为: + +``` +[SIP/MASK] [DIP/MASK] [SPORT] [DPORT] [PROTO] [ALLOW/DENY] +``` + +若要实现⿊名单,根据匹配的优先级顺序,则需要在规则的最后⼀条写上 + +```c +0.0.0.0/0 0.0.0.0/0 0 0 0 ALLOW +``` + +同理,若要实现⽩名单,需要将最后⼀条规则写为 + +``` +0.0.0.0/0 0.0.0.0/0 0 0 0 DENY +``` + +如需禁⽌源IP为 172.17.0.0/24 ,⽬的IP为任意 0.0.0.0/0 ,源端⼝号为任意 0 ,⽬的端⼝号为 80 的 TCP 协议 + +``` +172.17.0.2/32 0.0.0.0/0 0 80 TCP DENY +``` + +如需禁⽌任意来源的PING请求,可以写为: + +``` +0.0.0.0/0 0.0.0.0/0 0 0 ICMP DENY +``` + +我们在配置文件中写入,用以配置规则 + +```c +192.168.239.132/24 0.0.0.0/0 0 0 ICMP DENY +192.168.239.132/24 0.0.0.0/0 0 80 TCP DENY +0.0.0.0/0 0.0.0.0/0 0 0 0 ALLOW +``` + +之后加载到程序中 + +```bash +sudo ./netmanager -d ens33 -S --progname=xdp_entry_ipv4 -i conf.d/black_ipv4.conf -t +``` + +之后加载到程序中 + +```bash +sudo ./netmanager -d ens33 -S --progname=xdp_entry_ipv4 -i conf.d/black_ipv4.conf -t +``` + +统计信息如下 + +![image-20240715202110387](./image/net_manager5.png) + +可以发现已经drop了所有icmp报文 + +卸载命令 + +```bash +sudo xdp-loader unload ens33 --all +``` + +## ## 代码分析 + +现阶段的目录结构如下 + +```c +. +├── bpf_use_errno_test.o +├── common +│ ├── ... +├── config.mk +├── configure +├── lib +│ ├── ... +├── Makefile +├── net_manager +│ ├── common_kern_user.h +│ ├── conf.d +│ │ ├── black_ipv4.conf +│ │ ├── mac_load.conf +│ │ └── router_load.conf +│ ├── Makefile +│ ├── xdp_loader +│ ├── xdp_loader.c +│ ├── xdp_prog_kern.c +│ ├── xdp_prog_kern.ll +│ └── xdp_prog_kern.o +├── README.md +//以下为子项目 +├── router +│ ├── ... +├── testenv +│ ├── ... +├── xacl_ip +│ ├── ... +├── xacl_mac +│ ├── ... +``` + +### Makefile结构 + +![image-20240711102520620](./image/net_manager6.png) + +为递归make,尝试修改,较为困难 + +### ./net_manager/xdp_loader.c + +主用户态代码 + +![image-20240711105855149](./image/net_manager7.png) + +### ./net_manager/xdp_prog_kern.c + +![image-20240711110039806](./image/net_manager8.png) \ No newline at end of file diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/session_keep.md b/eBPF_Supermarket/Network_Subsystem/net_manager/document/session_keep.md new file mode 100644 index 000000000..1cea057c2 --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/document/session_keep.md @@ -0,0 +1,72 @@ +## 会话保持 + +### 概述 + +​ 工具能够检测多种类型的网络报文,包括TCP、UDP和ICMP。通过对TCP连接状态的监控,了解连接的建立和关闭过程;UDP数据包的监测提供了数据传输的详细信息,如源和目标端口以及数据包大小;而ICMP报文则用于网络诊断,帮助确认主机在线状态和测量网络延迟。可以帮助全面掌握网络流量状况和性能。 + +### 实现 + +在先前的功能中,仅能实现对lo端口的监测,不能实现对通用网卡的监测,且ip输出格式不易懂 + +其在lo网卡上监测的信息 + +![image-20240719173203213](./image/session_keep1.png) + +增加对通用网卡的监测,UDP、ICMP的监测,输出格式的转化,但由于XDP仅在收包路径,所以发送报文/相关状态获取不到 + +### 使用方法 + +使用命令将程序挂载到相应网卡 + +```c +sudo ./netmanager -d ens33 --progname=xdp_entry_state -S +``` + +查看挂载程序 + +``` +sudo xdp-loader status +``` + +查看输出 + +```c +sudo cat /sys/kernel/debug/tracing/trace_pipe +``` + +实例截图 + +![image-20240719173840908](./image/session_keep2.png) + +进行卸载 + +```c +sudo xdp-loader unload ens33 --all +``` + +### 输出分析 + +**tcp连接** + +​ 程序可以监测源IP地址、目标IP地址、端口号、连接状态以及连接的角色(C/S),通过分析和统计其连接状态,可以获取到其连接的相应信息 + +```c +tcp(96.91.189.91:80->132.239.168.192:36676),state:ESTABLISHED,client +``` + +**udp** + +​ 程序可以监测源IP地址、目标IP地址、端口号、数据包长度等信息。通过分析此日志,可以了解网络中的DNS查询活动,并监控数据传输的细节,故障排除以及检测异常流量。 + +```c +udp(192.168.239.2:53->192.168.239.132:36874),len=247 +``` + +**ICMP** + +​ 程序可以监测源IP地址、目标IP地址、icmp报文的类型和代码等信息,用于确认目标主机是否在线或检查网络连接的延迟,有助于检查网络连接的正常性和响应时间。 + +```c +icmp(1.1.1.1->192.168.239.132),type=0,code=0 +``` + diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/netmanager.c b/eBPF_Supermarket/Network_Subsystem/net_manager/netmanager.c new file mode 100644 index 000000000..32e31a9a1 --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/netmanager.c @@ -0,0 +1,934 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +static const char *__doc__ = "XDP loader\n" + " - Allows selecting BPF program --progname name to XDP-attach to --dev\n" + " - Collects and displays stats from XDP program\n"; + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include /* depend on kernel-headers installed */ +#include +#include +#include "./common/common_params.h" +#include "./common/common_user_bpf_xdp.h" +#include "./common/common_libbpf.h" +#include "common_kern_user.h" +#include "netmanager_kern.skel.h" +static const char *default_filename = "netmanager_kern.o"; +static const char *default_progname = "xdp_entry_state"; + +static const struct option_wrapper long_options[] = { + + {{"help", no_argument, NULL, 'h' }, + "Show help", false}, + + {{"dev", required_argument, NULL, 'd' }, + "Operate on device ", "", true}, + + {{"skb-mode", no_argument, NULL, 'S' }, + "Install XDP program in SKB (AKA generic) mode"}, + + {{"native-mode", no_argument, NULL, 'N' }, + "Install XDP program in native mode"}, + + {{"auto-mode", no_argument, NULL, 'A' }, + "Auto-detect SKB or native mode"}, + + {{"force", no_argument, NULL, 'F' }, + "Force install, replacing existing program on interface"}, + + {{"unload", no_argument, NULL, 'U' }, + "Unload XDP program instead of loading"}, + + {{"quiet", no_argument, NULL, 'q' }, + "Quiet mode (no output)"}, + + {{"progname", required_argument, NULL, 2 }, + "Load program from function in the ELF file", ""}, + + {{"stats", no_argument, NULL, 't' }, + "Show XDP stats"}, + + {{"ip-filter", required_argument, NULL, 'i' }, + "ip_filter"}, + + {{"mac_filter", required_argument, NULL, 'm' }, + "mac_filter"}, + + {{"router", required_argument, NULL, 'k' }, + "package_router"}, + + {{"clear", no_argument, NULL, 'n' }, + "clear_map"}, + + {{"config", no_argument, NULL, 'T' }, + "config from user to kernel"}, + + {{"socketmap_flag", no_argument, NULL, 'f' }, + "socketmap_flag"}, + {{0, 0, NULL, 0 }, NULL, false} +}; + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +const char *pin_basedir = "/sys/fs/bpf"; +const char *map_name = "xdp_stats_map"; +char pin_dir[PATH_MAX]; +char map_filename[PATH_MAX]; + + +static void list_avail_progs(struct bpf_object *obj) +{ + struct bpf_program *pos; + + printf("BPF object (%s) listing available XDP functions\n", + bpf_object__name(obj)); + + bpf_object__for_each_program(pos, obj) { + if (bpf_program__type(pos) == BPF_PROG_TYPE_XDP) + printf(" %s\n", bpf_program__name(pos)); + } +} + + +#define NANOSEC_PER_SEC 1000000000 /* 10^9 */ +static __u64 gettime(void) +{ + struct timespec t; + int res; + + res = clock_gettime(CLOCK_MONOTONIC, &t); + if (res < 0) { + fprintf(stderr, "Error with gettimeofday! (%i)\n", res); + exit(EXIT_FAIL); + } + return (__u64) t.tv_sec * NANOSEC_PER_SEC + t.tv_nsec; +} + +struct record { + __u64 timestamp; + struct datarec total; /* defined in common_kern_user.h */ +}; + +struct stats_record { + struct record stats[XDP_ACTION_MAX]; +}; + +static double calc_period(struct record *r, struct record *p) +{ + double period_ = 0; + __u64 period = 0; + + period = r->timestamp - p->timestamp; + if (period > 0) + period_ = ((double) period / NANOSEC_PER_SEC); + + return period_; +} + +static void stats_print_header() +{ + /* Print stats "header" */ + printf("%-12s\n", "XDP-action"); +} + +static void stats_print(struct stats_record *stats_rec, + struct stats_record *stats_prev) +{ + struct record *rec, *prev; + __u64 packets, bytes; + double period; + double pps; /* packets per sec */ + double bps; /* bits per sec */ + int i; + + stats_print_header(); /* Print stats "header" */ + + /* Print for each XDP actions stats */ + for (i = 0; i < XDP_ACTION_MAX; i++) + { + char *fmt = "%-12s %'11lld pkts (%'10.0f pps)" + " %'11lld Kbytes (%'6.0f Mbits/s)" + " period:%f\n"; + const char *action = action2str(i); + + rec = &stats_rec->stats[i]; + prev = &stats_prev->stats[i]; + + period = calc_period(rec, prev); + if (period == 0) + return; + + packets = rec->total.rx_packets - prev->total.rx_packets; + pps = packets / period; + + bytes = rec->total.rx_bytes - prev->total.rx_bytes; + bps = (bytes * 8)/ period / 1000000; + + printf(fmt, action, rec->total.rx_packets, pps, + rec->total.rx_bytes / 1000 , bps, + period); + } + printf("\n"); +} + + +/* BPF_MAP_TYPE_ARRAY */ +void map_get_value_array(int fd, __u32 key, struct datarec *value) +{ + if ((bpf_map_lookup_elem(fd, &key, value)) != 0) { + fprintf(stderr, + "ERR: bpf_map_lookup_elem failed key:0x%X\n", key); + } +} + +/* BPF_MAP_TYPE_PERCPU_ARRAY */ +void map_get_value_percpu_array(int fd, __u32 key, struct datarec *value) +{ + /* For percpu maps, userspace gets a value per possible CPU */ + unsigned int nr_cpus = libbpf_num_possible_cpus(); + struct datarec values[nr_cpus]; + __u64 sum_bytes = 0; + __u64 sum_pkts = 0; + int i; + + if ((bpf_map_lookup_elem(fd, &key, values)) != 0) { + fprintf(stderr, + "ERR: bpf_map_lookup_elem failed key:0x%X\n", key); + return; + } + + /* Sum values from each CPU */ + for (i = 0; i < nr_cpus; i++) { + sum_pkts += values[i].rx_packets; + sum_bytes += values[i].rx_bytes; + } + value->rx_packets = sum_pkts; + value->rx_bytes = sum_bytes; +} + +static bool map_collect(int fd, __u32 map_type, __u32 key, struct record *rec) +{ + struct datarec value; + + /* Get time as close as possible to reading map contents */ + rec->timestamp = gettime(); + + switch (map_type) { + case BPF_MAP_TYPE_ARRAY: + map_get_value_array(fd, key, &value); + break; + case BPF_MAP_TYPE_PERCPU_ARRAY: + map_get_value_percpu_array(fd, key, &value); + break; + default: + fprintf(stderr, "ERR: Unknown map_type(%u) cannot handle\n", + map_type); + return false; + break; + } + + rec->total.rx_packets = value.rx_packets; + rec->total.rx_bytes = value.rx_bytes; + return true; +} + +static void stats_collect(int map_fd, __u32 map_type, + struct stats_record *stats_rec) +{ + /* Collect all XDP actions stats */ + __u32 key; + + for (key = 0; key < XDP_ACTION_MAX; key++) { + map_collect(map_fd, map_type, key, &stats_rec->stats[key]); + } +} + +static void stats_poll(int map_fd, __u32 map_type, int interval) +{ + struct stats_record prev, record = { 0 }; + + /* Trick to pretty printf with thousands separators use %' */ + setlocale(LC_NUMERIC, "en_US"); + + /* Get initial reading quickly */ + stats_collect(map_fd, map_type, &record); + usleep(1000000/4); + + while (1) { + prev = record; /* struct copy */ + stats_collect(map_fd, map_type, &record); + stats_print(&record, &prev); + sleep(interval); + } +} + + + +int unpin_maps(struct bpf_object *bpf_obj) +{ + int err; + /* Existing/previous XDP prog might not have cleaned up */ + if (access(map_filename, F_OK ) != -1 ) { + if (verbose) + printf(" - Unpinning (remove) prev maps in %s/\n", + pin_dir); + + /* Basically calls unlink(3) on map_filename */ + err = bpf_object__unpin_maps(bpf_obj, pin_dir); + if (err) { + fprintf(stderr, "ERR: UNpinning maps in %s\n", pin_dir); + return EXIT_FAIL_BPF; + } + } + return 0; +} + +/* Pinning maps under /sys/fs/bpf in subdir */ +int pin_maps_in_bpf_object(struct bpf_object *bpf_obj) +{ + int err; + unpin_maps(bpf_obj); + if (verbose) + printf(" - Pinning maps in %s/\n", pin_dir); + + /* This will pin all maps in our bpf_object */ + err = bpf_object__pin_maps(bpf_obj, pin_dir); + if (err) + return EXIT_FAIL_BPF; + + return 0; +} + + +char *ifname; +int rules_ipv4_map; +int rtcache_map4; +int rules_mac_map; + +int print_usage(int id){ + switch(id){ + case 0: + fprintf(stderr, "Usage: \n"); + break; + default: + break; + }; + + return 0; +} + + +int open_map(const char *ifname, const char *map_name){ + int len; + char pin_dir[PATH_MAX]; + const char *pin_basedir = "/sys/fs/bpf"; + struct bpf_map_info info = { 0 }; + + /* Use the --dev name as subdir for finding pinned maps */ + len = snprintf(pin_dir, PATH_MAX, "%s/%s", pin_basedir, ifname); + if (len < 0) { + fprintf(stderr, "ERR: creating pin dirname\n"); + return -1; + } + + int fd = open_bpf_map_file(pin_dir, map_name, &info); + if (fd < 0) { + fprintf(stderr, "ERR: Failed to open map file: %s\n", map_name); + return -1; + } + printf("Opened BPF map\n"); + printf(" - BPF map (bpf_map_type:%d) fd: %d id:%d name:%s" + " key_size:%d value_size:%d max_entries:%d\n", + info.type, fd, info.id, info.name, + info.key_size, info.value_size, info.max_entries); + + return fd; +} + + +int load_bpf_map(){ + rules_ipv4_map = open_map(ifname, "rules_ipv4_map"); + rtcache_map4 = open_map(ifname, "rtcache_map4"); + rules_mac_map = open_map(ifname, "rules_mac_map"); + // Check if any map failed to open + if (rules_ipv4_map < 0) { + fprintf(stderr, "Failed to open rules_ipv4_map\n"); + } + if (rtcache_map4 < 0) { + fprintf(stderr, "Failed to open rtcache_map4\n"); + } + if (rules_mac_map < 0) { + fprintf(stderr, "Failed to open rules_mac_map\n"); + } + + if (rules_ipv4_map < 0 || rtcache_map4 < 0 || rules_mac_map < 0) { + fprintf(stderr, "load bpf map error, check device name\n"); + return -1; + } + + return 0; +} + + +static __u32 ip_to_u32(__u8 *ip_u8) { + __u32 ip_u32 = 0; + ip_u32 = (ip_u8[0]<<24) | (ip_u8[1]<<16) | (ip_u8[2]<<8) | (ip_u8[3]); + //printf("%hhu.%hhu.%hhu.%hhu,%u\n",ip_u8[0],ip_u8[1],ip_u8[2],ip_u8[3],ip_u32); + return ip_u32; +} + +int clear_map(){ + __u16 keys[MAX_RULES]; + for(int i=0; iprogs.bpf_sockmap); + if (sock_ops_prog_fd < 0) { + fprintf(stderr, "Invalid sock_ops program fd: %d\n", sock_ops_prog_fd); + return -1; + } + + int msg_verdict_prog_fd = bpf_program__fd(skel->progs.bpf_redir); + if (msg_verdict_prog_fd < 0) { + fprintf(stderr, "Invalid msg_verdict program fd: %d\n", msg_verdict_prog_fd); + return -1; + } + + int sock_ops_map_fd = bpf_map__fd(skel->maps.sock_ops_map); + if (sock_ops_map_fd < 0) { + fprintf(stderr, "Invalid sock_ops map fd: %d\n", sock_ops_map_fd); + return -1; + } + + err = bpf_prog_attach(sock_ops_prog_fd, cgroup_fd, BPF_CGROUP_SOCK_OPS, 0); + if (err) { + fprintf(stderr, "Failed to attach bpf_sockmap program: %d\n", err); + return 1; + } + + //加载并附加msg_verdict程序 + err = bpf_prog_attach(msg_verdict_prog_fd, sock_ops_map_fd, BPF_SK_MSG_VERDICT, 0); + if (err) { + fprintf(stderr, "Failed to attach bpf_redir program: %d\n", err); + return 1; + } + while (!exiting) { + sleep(1); + printf("----1-----\n"); + /* Ctrl-C will cause -EINTR */ + if (err == -EINTR) { + err = 0; + break; + } + if (err < 0) { + printf("Error polling perf buffer: %d\n", err); + break; + } + } + cleanup: + netmanager_kern__destroy(skel); + return err < 0 ? -err : 0; + } + /* Required option */ + // 检查是否提供了必需的选项 + if (cfg.ifindex == -1) { + fprintf(stderr, "ERR: required option --dev missing\n\n"); + usage(argv[0], __doc__, long_options, (argc == 1)); + return EXIT_FAIL_OPTION; + } + /* Generate pin_dir & map_filename string */ + // 生成pin目录和映射文件名字符串 + len = snprintf(pin_dir, PATH_MAX, "%s/%s", pin_basedir, cfg.ifname); + if (len < 0) { + fprintf(stderr, "ERR: creating pin dirname\n"); + return EXIT_FAIL_OPTION; + } + len = snprintf(map_filename, PATH_MAX, "%s/%s/%s", + pin_basedir, cfg.ifname, map_name); + if (len < 0) { + fprintf(stderr, "ERR: creating map_name\n"); + return EXIT_FAIL_OPTION; + } + + // 加载BPF程序并将其附加到XDP + DECLARE_LIBBPF_OPTS(bpf_object_open_opts, bpf_opts); + obj = bpf_object__open_file(cfg.filename, &bpf_opts); + err = libbpf_get_error(obj); + if (err) { + libxdp_strerror(err, errmsg, sizeof(errmsg)); + fprintf(stderr, "Couldn't open BPF object file %s: %s\n", + cfg.filename, errmsg); + return err; + } + + + if (verbose) + list_avail_progs(obj); + + DECLARE_LIBXDP_OPTS(xdp_program_opts, xdp_opts, + .obj = obj, + .prog_name = cfg.progname); + program = xdp_program__create(&xdp_opts); + err = libxdp_get_error(program); + if (err) { + libxdp_strerror(err, errmsg, sizeof(errmsg)); + fprintf(stderr, "ERR: loading program %s: %s\n", cfg.progname, errmsg); + exit(EXIT_FAIL_BPF); + } + + err = xdp_program__attach(program, cfg.ifindex, cfg.attach_mode, 0); + if (err) { + perror("xdp_program__attach"); + exit(err); + } + + /* do unload */ + // 如果指定了卸载选项,则执行卸载操作 + if (cfg.do_unload) { + unpin_maps(xdp_program__bpf_obj(program)); // 解除BPF程序固定的映射 + err = do_unload(&cfg); + if (err) { + libxdp_strerror(err, errmsg, sizeof(errmsg)); + fprintf(stderr, "Couldn't unload XDP program %s: %s\n", + cfg.progname, errmsg); // 打印卸载错误消息 + return err; + } + + printf("Success: Unloading XDP prog name: %s\n", cfg.progname); + return EXIT_OK;; + } + + // 如果启用了详细模式,则打印加载的BPF对象文件和程序名称,以及附加的XDP程序的设备信息 + if (verbose) { + printf("Success: Loaded BPF-object(%s) and used program(%s)\n", + cfg.filename, cfg.progname); + printf(" - XDP prog attached on device:%s(ifindex:%d)\n", + cfg.ifname, cfg.ifindex); + } + + /* Use the --dev name as subdir for exporting/pinning maps */ + // 使用--dev名称作为子目录来导出/固定映射 + err = pin_maps_in_bpf_object(xdp_program__bpf_obj(program)); + if (err) { + fprintf(stderr, "ERR: pinning maps\n"); + return err; + } + ifname = cfg.ifname; + + + map_fd = open_bpf_map_file(pin_dir, "print_info_map", NULL); + if (map_fd < 0) { + return EXIT_FAIL_BPF; + } + i = 0; + bpf_map_update_elem(map_fd, &i, &cfg.print_info, 0); + + + // 根据不同的选项加载不同的配置文件 + if (cfg.ip_filter) { + load_bpf_map(); + err = load_handler_ipv4(cfg.ip_filter_file); + if (err) { + fprintf(stderr, "ERR: loading IP filter config file\n"); + return err; + } + } else if (cfg.mac_filter) { + load_bpf_map(); + err = load_handler_mac(cfg.mac_filter_file); + if (err) { + fprintf(stderr, "ERR: loading MAC filter config file\n"); + return err; + } + } else if (cfg.router) { + load_bpf_map(); + err = load_handler_router(cfg.router_file); + if (err) { + fprintf(stderr, "ERR: loading router config file\n"); + return err; + } + } else if (cfg.clear) { + load_bpf_map(); + err = clear_handler(argc - 3, argv + 6); + if (err) { + fprintf(stderr, "ERR: clearing maps\n"); + return err; + } + } + + + + map_fd = open_bpf_map_file(pin_dir, "tx_port", NULL); + if (map_fd < 0) { + return EXIT_FAIL_BPF; + } + + i = 0; + bpf_map_update_elem(map_fd, &i, &cfg.ifindex, 0); + printf("redirect from ifnum=%d to ifnum=%d\n", cfg.ifindex, cfg.ifindex); + + //打印统计信息 + if (cfg.show_stats) { + /* Use the --dev name as subdir for finding pinned maps */ + len = snprintf(pin_dir, PATH_MAX, "%s/%s", pin_basedir, cfg.ifname); + if (len < 0) { + fprintf(stderr, "ERR: creating pin dirname\n"); + return EXIT_FAIL_OPTION; + } + + stats_map_fd = open_bpf_map_file(pin_dir, "xdp_stats_map", &info); + if (stats_map_fd < 0) { + return EXIT_FAIL_BPF; + } + + /* check map info, e.g. datarec is expected size */ + map_expect.key_size = sizeof(__u32); + map_expect.value_size = sizeof(struct datarec); + map_expect.max_entries = XDP_ACTION_MAX; + err = check_map_fd_info(&info, &map_expect); + if (err) { + fprintf(stderr, "ERR: map via FD not compatible\n"); + return err; + } + if (verbose) { + printf("\nCollecting stats from BPF map\n"); + printf(" - BPF map (bpf_map_type:%d) id:%d name:%s" + " key_size:%d value_size:%d max_entries:%d\n", + info.type, info.id, info.name, + info.key_size, info.value_size, info.max_entries + ); + } + + stats_poll(stats_map_fd, info.type, interval); + return EXIT_OK; + } + +} \ No newline at end of file diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/netmanager_kern.c b/eBPF_Supermarket/Network_Subsystem/net_manager/netmanager_kern.c new file mode 100644 index 000000000..614594225 --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/netmanager_kern.c @@ -0,0 +1,980 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include +#include +#include +#include + +#include "common_kern_user.h" +#include "./common/parsing_helpers.h" + +#ifndef memcpy +#define memcpy(dest, src, n) __builtin_memcpy((dest), (src), (n)) +#endif + +//重定义 +#undef AF_INET +#define AF_INET 2 +#undef AF_INET6 +#define AF_INET6 10 +#define IPV6_FLOWINFO_MASK bpf_htonl(0x0FFFFFFF) + +// config map +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, __u16); + __type(value, __u16); + __uint(max_entries, 1); +} print_info_map SEC(".maps"); + +// 数据包统计 +struct { + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __type(key, __u32); + __type(value, struct datarec); + __uint(max_entries, XDP_ACTION_MAX); +} xdp_stats_map SEC(".maps"); + +// ipv4—filter +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, __u16); + __type(value, struct rules_ipv4); + __uint(max_entries, MAX_RULES); +} rules_ipv4_map SEC(".maps"); + +// mac—filter +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, __u16); + __type(value, struct rules_mac); + __uint(max_entries, MAX_RULES); +} rules_mac_map SEC(".maps"); + +// router +struct { + __uint(type, BPF_MAP_TYPE_DEVMAP); + __type(key, int); + __type(value, int); + __uint(max_entries, 256); +} tx_port SEC(".maps"); + +// 路由转发表缓存 +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, __u32); + __type(value, struct rt_item); + __uint(max_entries, MAX_RULES); +} rtcache_map4 SEC(".maps"); + +// 会话保持 +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, struct conn_ipv4_key); + __type(value, struct conn_ipv4_val); + __uint(max_entries, MAX_RULES); +} conn_ipv4_map SEC(".maps"); + +struct sock_key { + __u32 sip4; // 源 IP + __u32 dip4; // 目的 IP + __u8 family; // 协议类型 + __u8 pad1; // this padding required for 64bit alignment + __u16 pad2; // else ebpf kernel verifier rejects loading of the program + __u32 pad3; + __u32 sport; // 源端口 + __u32 dport; // 目的端口 +} __attribute__((packed)); + +struct{ + __uint(type, BPF_MAP_TYPE_SOCKHASH); + __type(key,struct sock_key); + __type(value, int); + __uint(max_entries, 65535); +}sock_ops_map SEC(".maps"); + +#define FORCE_READ(x) (*(volatile typeof(x) *)&(x)) + +static __always_inline +__u32 xdp_stats_record_action(struct xdp_md *ctx, __u32 action) +{ + void *data_end = (void *)(long)ctx->data_end; + void *data = (void *)(long)ctx->data; + + if (action >= XDP_ACTION_MAX) + return XDP_ABORTED; + + /* Lookup in kernel BPF-side return pointer to actual data record */ + struct datarec *rec = bpf_map_lookup_elem(&xdp_stats_map, &action); + if (!rec) + return XDP_ABORTED; + + /* Calculate packet length */ + __u64 bytes = data_end - data; + + /* BPF_MAP_TYPE_PERCPU_ARRAY returns a data record specific to current + * CPU and XDP hooks runs under Softirq, which makes it safe to update + * without atomic operations. + */ + rec->rx_packets++; + rec->rx_bytes += bytes; + + return action; +} + +/*路由功能*/ + +/* from include/net/ip.h */ +static __always_inline int ip_decrease_ttl(struct iphdr *iph) +{ + __u32 check = iph->check; + check += bpf_htons(0x0100); + iph->check = (__u16)(check + (check >= 0xFFFF)); + return --iph->ttl; +} + +static __always_inline +int mac_zero(const __u8 *mac_addr) { + // 检查MAC地址是否不全为零 + for (int i = 0; i < ETH_ALEN; i++) { + if (mac_addr[i] != 0) + return 1; // 如果有一个字节不为零,返回1表示不为零 + } + return 0; // 如果所有字节都为零,返回0表示全为零 +} + + +static __always_inline +int ipv4_match(__u32 conn_addr, __u32 rule_addr) { + // 直接比较IPv4地址和网络地址 + if( (!rule_addr) || (conn_addr == rule_addr) ) //0 , match all + return 1; + return 0; +} + +static int match_rules_loop(__u32 index, void *ctx) +{ + struct rt_item *p_ctx = (struct rt_item *)ctx; + + + struct rt_item *p_r = bpf_map_lookup_elem(&rtcache_map4, &index); + if(!p_r){ + return 1; //out of range + } + + + if( ipv4_match(p_ctx->saddr, p_r->saddr) ) { + + memcpy(p_ctx->eth_source, p_r->eth_source, ETH_ALEN); + memcpy(p_ctx->eth_dest, p_r->eth_dest, ETH_ALEN); + + + return 1; + } + + + return 1; +} + +static __always_inline +int match_rules(struct rt_item *conn) +{ + struct rt_item *ctx = conn; + + bpf_loop(MAX_RULES, match_rules_loop, ctx, 0); + + return 1; +} + +/*使用 IP 进行过滤*/ + +struct match_rules_loop_ctx{ + __u16 action; + __u16 next_rule; + struct conn_ipv4 *conn; +}; + +static __always_inline +int ipv4_cidr_match(__u32 ip_addr, __u32 network_addr, __u8 cidr) { + if(network_addr == 0 && cidr == 0) + return 1; + + __u32 subnet_mask = (0xFFFFFFFFU << (32 - cidr)) & 0xFFFFFFFFU; + + __u32 masked_ip = ip_addr & subnet_mask; + __u32 masked_network = network_addr & subnet_mask; + return masked_ip == masked_network; +} + +static __always_inline +int port_match(__u16 conn_port, __u16 rule_port){ + if( (!rule_port) || (rule_port == conn_port) ) //0 , match all + return 1; + return 0; +} + +static int match_rules_ipv4_loop(__u32 index, void *ctx) +{ + int i = 0; + unsigned char *saddr; + unsigned char *daddr; + struct match_rules_loop_ctx *p_ctx = (struct match_rules_loop_ctx *)ctx; + if(index != p_ctx->next_rule) + return 0; + + struct rules_ipv4 *p_r = bpf_map_lookup_elem(&rules_ipv4_map, &index); + if(!p_r){ + return 1; //out of range + } + + p_ctx->next_rule = p_r->next_rule; + + if(index == 0) + goto out_match_rules_ipv4_loop; + bpf_printk("match_rules_ipv4_loop %d",index); + if( ipv4_cidr_match(p_ctx->conn->saddr, p_r->saddr, p_r->saddr_mask) && + ipv4_cidr_match(p_ctx->conn->daddr, p_r->daddr, p_r->daddr_mask) && + port_match(p_ctx->conn->sport, p_r->sport) && + port_match(p_ctx->conn->dport, p_r->dport) && + port_match(p_ctx->conn->ip_proto, p_r->ip_proto) ) + { + p_ctx->action = p_r->action; + __u8 *print_info=(__u8*)bpf_map_lookup_elem(&print_info_map,&i); + if(!print_info) return 1; + if(print_info){ + saddr = (unsigned char *)&p_ctx->conn->saddr; + daddr = (unsigned char *)&p_ctx->conn->daddr; + //bpf_printk("%s ,%s",saddr,daddr); + bpf_printk("src: %lu.%lu.%lu.%lu:%d" ,(unsigned long)saddr[3], (unsigned long)saddr[2], (unsigned long)saddr[1], (unsigned long)saddr[0],p_ctx->conn->sport); + bpf_printk("dst: %lu.%lu.%lu.%lu:%d" ,(unsigned long)daddr[3], (unsigned long)daddr[2], (unsigned long)daddr[1], (unsigned long)daddr[0],p_ctx->conn->dport); + bpf_printk("prot:%d ,action:%d ,index:%d" ,p_ctx->conn->ip_proto,p_ctx->action, index); + bpf_printk("-----------------------------------"); + } + + return 1; + } + +out_match_rules_ipv4_loop: + if(p_r->next_rule == 0) + return 1; //go out loop + + return 0; +} + +static __always_inline +xdp_act match_rules_ipv4(struct conn_ipv4 *conn) +{ + struct match_rules_loop_ctx ctx = {.action = XDP_PASS, .conn = conn, .next_rule = 0}; + + + for(int i=0; idata_end; + void *data = (void *)(long)ctx->data; + struct hdr_cursor nh; + int nh_type; //next header type + struct ethhdr *eth; + struct iphdr *iph; + struct tcphdr *tcph; + struct udphdr *udph; + struct conn_ipv4 conn = {.saddr = 0, .daddr = 0, .sport = 0, .dport = 0, .ip_proto = 0}; + + //bpf_printk("xdp_entry_ipv4"); + nh.pos = data; + + nh_type = parse_ethhdr(&nh, data_end, ð); + + if(nh_type < 0) + goto out; + + if (nh_type == bpf_htons(ETH_P_IP)) { + + nh_type = parse_iphdr(&nh, data_end, &iph); + + if(nh_type < 0) + goto out; + + if (nh_type == IPPROTO_TCP) { + if(parse_tcphdr(&nh, data_end, &tcph) < 0) + goto out; + + conn.sport = bpf_ntohs(tcph -> source); + conn.dport = bpf_ntohs(tcph -> dest); + + } + else if(nh_type == IPPROTO_UDP){ + if(parse_udphdr(&nh, data_end, &udph) < 0){ + goto out; + } + conn.sport = bpf_ntohs(udph -> source); + conn.dport = bpf_ntohs(udph -> dest); + } + else if(nh_type == IPPROTO_ICMP){ + conn.sport = 0; + conn.dport = 0; + } + conn.saddr = bpf_ntohl(iph -> saddr); + conn.daddr = bpf_ntohl(iph -> daddr); + conn.ip_proto = nh_type; + + // #ifdef DEBUG_PRINT_EVERY + // if(conn.dport != 22) + // bpf_printk("conn(%u:%u to %u:%u)", conn.saddr, conn.sport, conn.daddr, conn.dport); + // #endif + + action = match_rules_ipv4(&conn); + + } + + +out: + return xdp_stats_record_action(ctx, action); +} + + + + +/* Solution to packet03/assignment-4 */ +SEC("xdp") +int xdp_entry_router1(struct xdp_md *ctx) +{ + xdp_act action = XDP_PASS; + void *data_end = (void *)(long)ctx->data_end; + void *data = (void *)(long)ctx->data; + struct bpf_fib_lookup ifib = {}; + struct hdr_cursor nh; + int nh_type; //next header type + struct ethhdr *eth = data; + struct ipv6hdr *ip6h; + struct iphdr *iph; + unsigned int ip4_saddr = 0; + //unsigned ifindex = 2; + int rc; + struct rt_item nitem = {.saddr = 0, .eth_source = {0}, .eth_dest = {0}}; + + + nh.pos = data; + + nh_type = parse_ethhdr(&nh, data_end, ð); + + if(nh_type < 0) + goto out; + + if (nh_type == bpf_htons(ETH_P_IP)) { + nh_type = parse_iphdr(&nh, data_end, &iph); + + if(nh_type < 0) + goto out; + + + if (iph->ttl <= 1) + goto out; + + + ip4_saddr = iph->saddr; + + nitem.saddr = ip4_saddr; + + // 首先精确查找转发表,如果找到就直接转发,不必再经历最长前缀匹配的慢速通配查找 + match_rules(&nitem);//rtcache_map4 + + + + if (mac_zero(nitem.eth_dest)) { + ip_decrease_ttl(iph); + memcpy(eth->h_dest, nitem.eth_dest, ETH_ALEN); + memcpy(eth->h_source, nitem.eth_source, ETH_ALEN); + action = bpf_redirect_map(&tx_port, 0, 0);//这里可能需要改 + + goto out; + } + + // 否则执行最长前缀匹配了 + ifib.family = AF_INET; + ifib.tos = iph->tos; + ifib.l4_protocol = iph->protocol; + ifib.sport = 0; + ifib.dport = 0; + ifib.tot_len = bpf_ntohs(iph->tot_len); + ifib.ipv4_src = iph->saddr; + ifib.ipv4_dst = iph->daddr; + ifib.ifindex = ctx->ingress_ifindex; + + + rc = bpf_fib_lookup(ctx, &ifib, sizeof(ifib), 0); + switch (rc) { + case BPF_FIB_LKUP_RET_SUCCESS: /* lookup successful */ + ip_decrease_ttl(iph); + + memcpy(eth->h_dest, ifib.dmac, ETH_ALEN); + memcpy(eth->h_source, ifib.smac, ETH_ALEN); + action = bpf_redirect(ifib.ifindex, 0); + goto out; + break; + case BPF_FIB_LKUP_RET_BLACKHOLE: /* dest is blackholed; can be dropped */ + case BPF_FIB_LKUP_RET_UNREACHABLE: /* dest is unreachable; can be dropped */ + case BPF_FIB_LKUP_RET_PROHIBIT: /* dest not allowed; can be dropped */ + action = XDP_DROP; + goto out; + break; + case BPF_FIB_LKUP_RET_NOT_FWDED: /* packet is not forwarded */ + case BPF_FIB_LKUP_RET_FWD_DISABLED: /* fwding is not enabled on ingress */ + case BPF_FIB_LKUP_RET_UNSUPP_LWT: /* fwd requires encapsulation */ + case BPF_FIB_LKUP_RET_NO_NEIGH: /* no neighbor entry for nh */ + case BPF_FIB_LKUP_RET_FRAG_NEEDED: /* fragmentation required to fwd */ + /* PASS */ + goto out; + break; + } + + } else if (nh_type == bpf_htons(ETH_P_IPV6)) { + nh_type = parse_ip6hdr(&nh, data_end, &ip6h); + + struct in6_addr *src = (struct in6_addr *) ifib.ipv6_src; + struct in6_addr *dst = (struct in6_addr *) ifib.ipv6_dst; + + if(nh_type < 0) + goto out; + + if (ip6h->hop_limit <= 1) + goto out; + + ifib.family = AF_INET6; + ifib.flowinfo = *(__be32 *) ip6h & IPV6_FLOWINFO_MASK; + ifib.l4_protocol = ip6h->nexthdr; + ifib.sport = 0; + ifib.dport = 0; + ifib.tot_len = bpf_ntohs(ip6h->payload_len); + *src = ip6h->saddr; + *dst = ip6h->daddr; + ifib.ifindex = ctx->ingress_ifindex; + + rc = bpf_fib_lookup(ctx, &ifib, sizeof(ifib), 0); + switch (rc) { + case BPF_FIB_LKUP_RET_SUCCESS: /* lookup successful */ + ip6h->hop_limit--; + + memcpy(eth->h_dest, ifib.dmac, ETH_ALEN); + memcpy(eth->h_source, ifib.smac, ETH_ALEN); + action = bpf_redirect(ifib.ifindex, 0); + goto out; + break; + case BPF_FIB_LKUP_RET_BLACKHOLE: /* dest is blackholed; can be dropped */ + case BPF_FIB_LKUP_RET_UNREACHABLE: /* dest is unreachable; can be dropped */ + case BPF_FIB_LKUP_RET_PROHIBIT: /* dest not allowed; can be dropped */ + action = XDP_DROP; + break; + case BPF_FIB_LKUP_RET_NOT_FWDED: /* packet is not forwarded */ + case BPF_FIB_LKUP_RET_FWD_DISABLED: /* fwding is not enabled on ingress */ + case BPF_FIB_LKUP_RET_UNSUPP_LWT: /* fwd requires encapsulation */ + case BPF_FIB_LKUP_RET_NO_NEIGH: /* no neighbor entry for nh */ + case BPF_FIB_LKUP_RET_FRAG_NEEDED: /* fragmentation required to fwd */ + /* PASS */ + break; + } + + } + else { + goto out; + } + + + +out: + return xdp_stats_record_action(ctx, action); +} + +struct match_rules_loop_mac_ctx{ + __u16 action; + __u16 next_rule; + struct conn_mac *conn; +}; + +static inline int bpf_memcmp(const void *a, const void *b, size_t len) { + const unsigned char *p1 = a; + const unsigned char *p2 = b; + size_t i; + + for (i = 0; i < len; i++) { + if (p1[i] != p2[i]) { + return p1[i] - p2[i]; + } + } + return 0; +} +static __always_inline +int mac_match(__u8 *conn_mac, __u8 *rule_mac) { + __u8 zero_mac[ETH_ALEN] = {0}; // 全零的MAC地址 + + // 如果rule_mac全为零,匹配所有MAC地址 + if (bpf_memcmp(rule_mac, zero_mac, ETH_ALEN) == 0) { + return 1; + } + + // 如果rule_mac的后三个字节为零,且前三个字节与conn_mac相同 + if (bpf_memcmp(&rule_mac[3], zero_mac, 3) == 0) { + if (bpf_memcmp(conn_mac, rule_mac, 3) == 0) { + return 1; // 匹配前三字节 + } + } + + // 检查规则MAC与连接MAC是否完全匹配 + if (bpf_memcmp(rule_mac, conn_mac, ETH_ALEN) == 0) { + return 1; // 完全匹配 + } + + return 0; // 不匹配 +} + + +static int match_rules_mac_loop(__u32 index, void *ctx) +{ + struct match_rules_loop_mac_ctx *p_ctx = (struct match_rules_loop_mac_ctx *)ctx; + if(index != p_ctx->next_rule) + return 0; + struct rules_mac *p_r = bpf_map_lookup_elem(&rules_mac_map, &index); + if(!p_r){ + return 1; //out of range + } + p_ctx->next_rule = p_r->next_rule; + + if(index == 0) + goto out_match_rules_mac_loop; + //bpf_printk("match_rules_ipv4_loop %d",index); + // bpf_printk("MAC_SRC: %02x:%02x:%02x:%02x:%02x:%02x\n",p_r->source[0], p_r->source[1], p_r->source[2],p_r->source[3], p_r->source[4], p_r->source[5]); + // bpf_printk("MAC_DEST: %02x:%02x:%02x:%02x:%02x:%02x ,Action: %d\n",p_r->dest[0], p_r->dest[1], p_r->dest[2],p_r->dest[3], p_r->dest[4], p_r->dest[5], + // p_r->action); + if(mac_match(p_ctx->conn->dest, p_r->dest)&&mac_match(p_ctx->conn->source, p_r->source)) + { + p_ctx->action = p_r->action; + return 1; + } + +out_match_rules_mac_loop: + if(p_r->next_rule == 0) + return 1; //go out loop + + return 0; +} + +static __always_inline +xdp_act match_rules_mac(struct conn_mac *conn) +{ + struct match_rules_loop_mac_ctx ctx = {.action = XDP_PASS, .conn = conn, .next_rule = 0}; + + + for(int i=0; idata_end; + void *data = (void *)(long)ctx->data; + struct hdr_cursor nh; + int nh_type; //next header type + struct ethhdr *eth; + struct conn_mac conn = { + .dest = {0}, // 初始化 dest 成员为全零 + .source = {0} // 初始化 source 成员为全零 + }; + nh.pos = data; + + nh_type = parse_ethhdr(&nh, data_end, ð); + + if(nh_type < 0) + goto out; + + //action = match_rules_ipv4(ð->h_source); + + // /* check if src mac is in src_macs map */ + // value = bpf_map_lookup_elem(&src_macs, eth->h_source); + // if (value) { + // action = *value; + // goto out; + // } + for (int i = 0; i < ETH_ALEN; i++) { + conn.source[i] = eth->h_source[i]; + conn.dest[i] = eth->h_dest[i]; + } + action = match_rules_mac(&conn); + +out: + return xdp_stats_record_action(ctx, action); +} + +SEC("xdp") +int xdp_entry_state(struct xdp_md *ctx) +{ + __u32 action = XDP_PASS; + void *data_end = (void *)(long)ctx->data_end; + void *data = (void *)(long)ctx->data; + struct hdr_cursor nh; + int nh_type; //next header type + struct ethhdr *eth; + struct iphdr *iph; + struct tcphdr *tcph; + struct udphdr *udph; + struct icmphdr *icmph; + unsigned char *saddr; + unsigned char *daddr; + + // 定义IPv4连接关键信息 + struct conn_ipv4_key conn_k = {.saddr = 0, .daddr = 0, .sport = 0, .dport = 0, .proto = 0}; + nh.pos = data; + + // 如果下一个头部类型为IPv4 + nh_type = parse_ethhdr(&nh, data_end, ð); + + if(nh_type < 0) + goto out; + + if (nh_type == bpf_htons(ETH_P_IP)) { + + nh_type = parse_iphdr(&nh, data_end, &iph); + + if(nh_type < 0) + goto out; + + conn_k.saddr = bpf_ntohl(iph -> saddr); + conn_k.daddr = bpf_ntohl(iph -> daddr); + + conn_k.proto = nh_type; + + saddr = (unsigned char *)&conn_k.saddr; + daddr = (unsigned char *)&conn_k.daddr; + + // 如果下一个头部类型为TCP + if (nh_type == IPPROTO_TCP) { + + if(parse_tcphdr(&nh, data_end, &tcph) < 0) + goto out; + // 获取TCP连接信息 + conn_k.sport = bpf_ntohs(tcph -> source); + conn_k.dport = bpf_ntohs(tcph -> dest); + // bpf_printk("conn(%lu.%lu.%lu.%lu:%u->%lu.%lu.%lu.%lu:%u)", + // (unsigned long)saddr[0], (unsigned long)saddr[1], (unsigned long)saddr[2], (unsigned long)saddr[3],conn_k.sport, + // (unsigned long)daddr[0], (unsigned long)daddr[1], (unsigned long)daddr[2], (unsigned long)daddr[3],conn_k.dport); + // 查找IPv4连接映射表中的值 + // 如果找到,就说明该连接已经存在,可以在原有连接信息的基础上进行处理。 + // 如果没有找到,可能是首次遇到这个连接,可以进行一些初始化操作,例如创建新的连接信息并添加到哈希表中。 + struct conn_ipv4_val *p_conn_v = bpf_map_lookup_elem(&conn_ipv4_map, &conn_k); + if(!p_conn_v){ + if(tcph->syn && tcph->ack){ //客户端 + struct conn_ipv4_val conn_v = {.tcp_state = TCP_S_ESTABLISHED,.rid=1}; + // 将新的连接项插入到 IPv4 连接映射中 + bpf_map_update_elem(&conn_ipv4_map, &conn_k, &conn_v, BPF_ANY); + // 输出日志信息,表示创建了一个新的连接项 + bpf_printk("tcp(%lu.%lu.%lu.%lu:%u->%lu.%lu.%lu.%lu:%u),state:%s,%s", + (unsigned long)saddr[0], (unsigned long)saddr[1], (unsigned long)saddr[2], (unsigned long)saddr[3],conn_k.sport, + (unsigned long)daddr[0], (unsigned long)daddr[1], (unsigned long)daddr[2], (unsigned long)daddr[3],conn_k.dport, + "ESTABLISHED",conn_v.rid?"client":"service"); + } + else if(tcph->syn){ //客户端 + struct conn_ipv4_val conn_v = {.tcp_state = TCP_S_SYN_RECV,.rid=0}; + // 将新的连接项插入到 IPv4 连接映射中 + bpf_map_update_elem(&conn_ipv4_map, &conn_k, &conn_v, BPF_ANY); + // 输出日志信息,表示创建了一个新的连接项 + bpf_printk("tcp(%lu.%lu.%lu.%lu:%u->%lu.%lu.%lu.%lu:%u),state:%s,%s", + (unsigned long)saddr[0], (unsigned long)saddr[1], (unsigned long)saddr[2], (unsigned long)saddr[3],conn_k.sport, + (unsigned long)daddr[0], (unsigned long)daddr[1], (unsigned long)daddr[2], (unsigned long)daddr[3],conn_k.dport, + "SYN-RECV",conn_v.rid?"client":"service"); + } + goto out; + } + // 如果查找成功,继续处理连接项 + // 如果TCP报文的标志位包含RST(复位),则删除连接项并输出相应的日志信息 + if(tcph->rst){ + bpf_map_delete_elem(&conn_ipv4_map, &conn_k); + bpf_printk("tcp(%lu.%lu.%lu.%lu:%u->%lu.%lu.%lu.%lu:%u),state:%s,%s", + (unsigned long)saddr[0], (unsigned long)saddr[1], (unsigned long)saddr[2], (unsigned long)saddr[3],conn_k.sport, + (unsigned long)daddr[0], (unsigned long)daddr[1], (unsigned long)daddr[2], (unsigned long)daddr[3],conn_k.dport, + "RST",p_conn_v->rid?"client":"service"); + goto out; + } + if(p_conn_v->rid) //客户端 + { + // 如果连接项的TCP状态为ESTABLISHED并且收到了ack,将TCP状态更新为FIN_WAIT2 + if(p_conn_v->tcp_state == TCP_S_ESTABLISHED && tcph->ack){ + p_conn_v->tcp_state = TCP_S_FIN_WAIT2; + goto out_tcp_conn; + } + } + if(!p_conn_v->rid)//服务端 + { + // 如果连接项的TCP状态为SYN_RECV并且收到了ACK,将TCP状态更新为ESTABLISHED + if(p_conn_v->tcp_state == TCP_S_SYN_RECV && tcph->ack){ + p_conn_v->tcp_state = TCP_S_ESTABLISHED; + goto out_tcp_conn; + } + // 如果连接项的TCP状态为ESTABLISHED并且收到了FIN,将TCP状态更新为FIN_WAIT1 + if(p_conn_v->tcp_state == TCP_S_ESTABLISHED && tcph->fin){ + p_conn_v->tcp_state = TCP_S_CLOSE_WAIT; + goto out_tcp_conn; + } + // 如果连接项的TCP状态为CLOSE_WAIT且收到了FIN和ACK,将TCP状态更新为FIN_WAIT2 + if(p_conn_v->tcp_state == TCP_S_CLOSE_WAIT && tcph->ack){ + p_conn_v->tcp_state = TCP_S_CLOSE; + goto out_tcp_conn; + } + } + const char *tcp_state_str; + + // 根据连接状态设置对应的字符串 + out_tcp_conn: + if(p_conn_v->tcp_state == TCP_S_CLOSE||p_conn_v->tcp_state == TCP_S_FIN_WAIT2){ + // 如果是CLOSE状态,从映射表中删除连接信息 + bpf_map_delete_elem(&conn_ipv4_map, &conn_k); + }else{ + // 否则更新映射表中的连接信息 + bpf_map_update_elem(&conn_ipv4_map, &conn_k, p_conn_v, BPF_EXIST); + } + // 根据连接状态打印日志 + switch(p_conn_v->tcp_state) { + case TCP_S_SYN_SENT: + tcp_state_str = "SYN_SENT"; + break; + case TCP_S_SYN_RECV: + tcp_state_str = "SYN_RECV"; + break; + case TCP_S_ESTABLISHED: + tcp_state_str = "ESTABLISHED"; + break; + case TCP_S_FIN_WAIT1: + tcp_state_str = "FIN_WAIT1"; + break; + case TCP_S_FIN_WAIT2: + tcp_state_str = "FIN_WAIT2"; + break; + case TCP_S_CLOSE_WAIT: + tcp_state_str = "CLOSE_WAIT"; + break; + case TCP_S_CLOSE: + tcp_state_str = "CLOSE"; + break; + default: + tcp_state_str = ""; + } + bpf_printk("tcp(%lu.%lu.%lu.%lu:%u->%lu.%lu.%lu.%lu:%u),state:%s,%s", + (unsigned long)saddr[0], (unsigned long)saddr[1], (unsigned long)saddr[2], (unsigned long)saddr[3],conn_k.sport, + (unsigned long)daddr[0], (unsigned long)daddr[1], (unsigned long)daddr[2], (unsigned long)daddr[3],conn_k.dport, + tcp_state_str,p_conn_v->rid?"client":"service"); + goto out; + } + else if(nh_type == IPPROTO_UDP){ + // 如果是UDP包,解析UDP头部并获取端口信息 + if(parse_udphdr(&nh, data_end, &udph) < 0){ + goto out; + } + conn_k.sport = bpf_ntohs(udph -> source); + conn_k.dport = bpf_ntohs(udph -> dest); + bpf_printk("udp(%lu.%lu.%lu.%lu:%u->%lu.%lu.%lu.%lu:%u),len=%lu", + (unsigned long)saddr[3], (unsigned long)saddr[2], (unsigned long)saddr[1], (unsigned long)saddr[0],conn_k.sport, + (unsigned long)daddr[3], (unsigned long)daddr[2], (unsigned long)daddr[1], (unsigned long)daddr[0],conn_k.dport, + __bpf_ntohs(udph -> len)); + } + else if(nh_type == IPPROTO_ICMP){ + // 如果是ICMP + if(parse_icmphdr(&nh, data_end, &icmph) < 0){ + goto out; + } + bpf_printk("icmp(%lu.%lu.%lu.%lu->%lu.%lu.%lu.%lu),type=%u,code=%u", + (unsigned long)saddr[3], (unsigned long)saddr[2], (unsigned long)saddr[1], (unsigned long)saddr[0], + (unsigned long)daddr[3], (unsigned long)daddr[2], (unsigned long)daddr[1], (unsigned long)daddr[0], + icmph->type,icmph->code); + } + #ifdef DEBUG_PRINT_EVERY + // 打印除SSH协议以外的所有连接信息 + if(conn.dport != 22) + bpf_printk("icmp(%u:%u to %u:%u)", conn.saddr, conn.sport, conn.daddr, conn.dport); + #endif + + } + + +out: + return xdp_stats_record_action(ctx, action); + +} + +// 最简单的一个转发表项 +struct rt_item_tab{ + int ifindex; // 转发出去的接口 + char eth_source[ETH_ALEN]; // 封装帧的源MAC地址。 + char eth_dest[ETH_ALEN]; // 封装帧的目标MAC地址。 +}; + +// 路由转发表缓存 +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __type(key, __u32); + __type(value, struct rt_item_tab); + __uint(max_entries, MAX_RULES); +} rtcache_map SEC(".maps"); +// 递减TTL还是要的 +static __always_inline int __ip_decrease_ttl(struct iphdr *iph) +{ + __u32 check = iph->check; + check += bpf_htons(0x0100); + iph->check = (__u16)(check + (check >= 0xFFFF)); + return --iph->ttl; +} +// 字节码的C程序本身 +SEC("xdp") +int xdp_entry_router(struct xdp_md *ctx) +{ + void *data_end = (void *)(long)ctx->data_end; + void *data = (void *)(long)ctx->data; + struct bpf_fib_lookup ifib; + struct ethhdr *eth = data; + struct iphdr *iph; + struct rt_item_tab *pitem = NULL; + unsigned int daddr = 0; + __u16 h_proto; + __u64 nh_off; + char fast_info[] = "Fast path to [%d]\n"; + char slow_info[] = "Slow path to [%d]\n"; + int action = XDP_DROP; + nh_off = sizeof(*eth); + if (data + nh_off > data_end) { + return XDP_DROP; + } + + __builtin_memset(&ifib, 0, sizeof(ifib)); + h_proto = eth->h_proto; + if (h_proto != bpf_htons(ETH_P_IP)) { + return XDP_PASS; + } + + iph = data + nh_off; + + if (iph + 1 > data_end) { + return XDP_DROP; + } + + daddr = iph->daddr; + + pitem = bpf_map_lookup_elem(&rtcache_map, &daddr); + // 首先精确查找转发表,如果找到就直接转发,不必再经历最长前缀匹配的慢速通配查找 + // 这个动作是可以offload到硬件中的。 + if (pitem) { + __ip_decrease_ttl(iph); + memcpy(eth->h_dest, pitem->eth_dest, ETH_ALEN); + memcpy(eth->h_source, pitem->eth_source, ETH_ALEN); + bpf_printk("%s----daddr : %d prot:%d",fast_info,daddr,pitem->ifindex); + //bpf_trace_printk(fast_info, sizeof(fast_info), pitem->ifindex); + action = bpf_redirect(pitem->ifindex, 0); + goto out; + } + + // 否则只能执行最长前缀匹配了 + ifib.family = AF_INET; + ifib.tos = iph->tos; + ifib.l4_protocol = iph->protocol; + ifib.tot_len = bpf_ntohs(iph->tot_len); + ifib.ipv4_src = iph->saddr; + ifib.ipv4_dst = iph->daddr; + ifib.ifindex = ctx->ingress_ifindex; + + // 调用eBPF封装的路由查找函数,虽然所谓慢速查找,也依然不会进入协议栈的。 + if (bpf_fib_lookup(ctx, &ifib, sizeof(ifib), 0) == 0) { + struct rt_item_tab nitem; + + __builtin_memset(&nitem, 0, sizeof(nitem)); + memcpy(&nitem.eth_dest, ifib.dmac, ETH_ALEN); + memcpy(&nitem.eth_source, ifib.smac, ETH_ALEN); + nitem.ifindex = ifib.ifindex; + // 插入新的表项 + bpf_map_update_elem(&rtcache_map, &daddr, &nitem, BPF_ANY); + __ip_decrease_ttl(iph); + memcpy(eth->h_dest, ifib.dmac, ETH_ALEN); + memcpy(eth->h_source, ifib.smac, ETH_ALEN); + bpf_printk("%s----daddr : %d prot:%d",slow_info,daddr,nitem.ifindex); + //bpf_trace_printk(slow_info, sizeof(slow_info), ifib.ifindex); + action = bpf_redirect(ifib.ifindex, 0); + goto out; + } + action = XDP_PASS; +out: + return xdp_stats_record_action(ctx, action); +} + +static inline +void extract_key4_from_msg(struct sk_msg_md *msg, struct sock_key *key) +{ + key->sip4 = msg->remote_ip4; + key->dip4 = msg->local_ip4; + key->family = 1; + + key->dport = (bpf_htonl(msg->local_port) >> 16); + key->sport = FORCE_READ(msg->remote_port) >> 16; +} +static inline +void extract_key4_from_ops(struct bpf_sock_ops *ops, struct sock_key *key) +{ + // keep ip and port in network byte order + key->dip4 = ops->remote_ip4; + key->sip4 = ops->local_ip4; + key->family = 1; + + // local_port is in host byte order, and remote_port is in network byte order + key->sport = (bpf_htonl(ops->local_port) >> 16); + key->dport = FORCE_READ(ops->remote_port) >> 16; +} +static inline +void bpf_sock_ops_ipv4(struct bpf_sock_ops *skops) +{ + struct sock_key key = {}; + int ret; + + extract_key4_from_ops(skops, &key); + ret = bpf_sock_hash_update(skops, &sock_ops_map, &key, BPF_NOEXIST); + //ret = sock_hash_update(skops, &sock_ops_map, &key, BPF_NOEXIST); + if (ret != 0) { + bpf_printk("sock_hash_update() failed, ret: %d\n", ret); + } + + bpf_printk("sockmap: op %d, port %d --> %d\n", skops->op, skops->local_port, bpf_ntohl(skops->remote_port)); +} + +SEC("sockops") // 加载到 ELF 中的 `sockops` 区域,有 socket operations 时触发执行 +int bpf_sockmap(struct bpf_sock_ops *skops) +{ + switch (skops->op) { + case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB: // 被动建连 + case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB: // 主动建连 + if (skops->family == 2) { // AF_INET + bpf_sock_ops_ipv4(skops); // 将 socket 信息记录到到 sockmap + } + break; + default: + break; + } + return 0; +} +SEC("sk_msg") // 加载目标文件(ELF )中的 `sk_msg` section,`sendmsg` 系统调用时触发执行 +int bpf_redir(struct sk_msg_md *msg) +{ + struct sock_key key = {}; + extract_key4_from_msg(msg, &key); + bpf_msg_redirect_hash(msg, &sock_ops_map, &key, BPF_F_INGRESS); + bpf_printk("bpf_msg_redirect_hash successful!"); + return SK_PASS; +} +char _license[] SEC("license") = "GPL"; \ No newline at end of file diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/testenv/README.org b/eBPF_Supermarket/Network_Subsystem/net_manager/testenv/README.org new file mode 100644 index 000000000..7bae95ebc --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/testenv/README.org @@ -0,0 +1,79 @@ +# -*- fill-column: 76; -*- +#+TITLE: Test environment script +#+OPTIONS: ^:nil + +This directory contains a setup script that you can use to create test +environments for testing your XDP programs. It works by creating virtual +ethernet (veth) interface pairs and moving one end of each pair to another +network namespace. You can load the XDP program in the other namespace and +send traffic to it through the interface that is visible in the root +namespace. + +Run =./testenv.sh= with no parameter to get a list of available commands, or +run =./testenv.sh --help= to get the full help listing with all options. The +script can maintain several environments active at the same time, and you +can switch between them using the =--name= option. + +If you don't specify a name, the most recently used environment will be +used. If you don't specify a name when setting up a new environment, a +random name will be generated for you. + +Examples: + +Setup new environment named "test": +=./testenv.sh setup --name=test= + +Create a shell alias for easy use of script from anywhere: +=eval $(./testenv.sh alias)= + +See the currently active environment, and a list of all active environment +names (with alias defined as above): +=t status= + +Enter the currently active environment: +=t enter= + +Execute a command inside the environment: +=t exec -- ip a= + +Teardown the environment: +=t teardown= + +* Understanding the network topology + +When setting up a test environment, there will be a virtual link between the +environment inside the new namespace, and the interface visible from the +host system root namespace. The new namespace will be named after the +environment name passed to the script, as will the interface visible in the +outer namespace. The interface *inside* the namespace will always be named +'veth0'. + +To illustrate this, creating a test environment with the name 'test01' (with +=t setup --name test01= will result in the following environment being set +up: + +#+begin_example ++-----------------------------+ +-----------------------------+ +| Root namespace | | Testenv namespace 'test01' | +| | From 'test01' | | +| +--------+ TX-> RX-> +--------+ | +| | test01 +--------------------------+ veth0 | | +| +--------+ <-RX <-TX +--------+ | +| | From 'veth0' | | ++-----------------------------+ +-----------------------------+ +#+end_example + +The 'test01' interface visible in the root namespace is the one we will be +installing XDP programs on in the tutorial lessons. The XDP program will see +packets being *received* on this interface; as you can see from the diagram, +this means all packets being transmitted from inside the new namespace. + +The setup is created this way to simulate the case where the host machine +have physical interfaces; but instead of the traffic arriving from outside +hosts on physical interfaces, they will arrive from inside the namespace on +the virtual interface. This also means that when you generate traffic to +test your XDP programs, you need to generate it from *inside* the test +environment. The =t ping= command will start the ping inside the test +environment by default, and you can run arbitrary programs inside the +environment by using =t exec -- =, or simply spawning a shell with +=t enter=. diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/testenv/config.sh b/eBPF_Supermarket/Network_Subsystem/net_manager/testenv/config.sh new file mode 100644 index 000000000..4b1853b76 --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/testenv/config.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# These are the config options for the testlab + + +SETUP_SCRIPT="$(dirname "$0")/setup-env.sh" +STATEDIR="${TMPDIR:-/tmp}/xdp-tutorial-testlab" +IP6_SUBNET=fc00:dead:cafe # must have exactly three :-separated elements +IP6_PREFIX_SIZE=64 # Size of assigned prefixes +IP6_FULL_PREFIX_SIZE=48 # Size of IP6_SUBNET +IP4_SUBNET=10.11 +IP4_PREFIX_SIZE=24 # Size of assigned prefixes +IP4_FULL_PREFIX_SIZE=16 # Size of IP4_SUBNET +VLAN_IDS=(1 2) +GENERATED_NAME_PREFIX="xdptut" diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/testenv/setup-env.sh b/eBPF_Supermarket/Network_Subsystem/net_manager/testenv/setup-env.sh new file mode 100755 index 000000000..b3db42537 --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/testenv/setup-env.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Script to setup things inside a test environment, used by testenv.sh for +# executing commands. +# +# Author: Toke Høiland-Jørgensen (toke@redhat.com) +# Date: 7 March 2019 +# Copyright (c) 2019 Red Hat + + +die() +{ + echo "$1" >&2 + exit 1 +} + +[ -n "$TESTENV_NAME" ] || die "TESTENV_NAME missing from environment" +[ -n "$1" ] || die "Usage: $0 " + +set -o nounset + +mount -t bpf bpf /sys/fs/bpf/ || die "Unable to mount /sys/fs/bpf inside test environment" + +exec "$@" diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/testenv/testenv.sh b/eBPF_Supermarket/Network_Subsystem/net_manager/testenv/testenv.sh new file mode 100755 index 000000000..34016b744 --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/testenv/testenv.sh @@ -0,0 +1,619 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Script to setup and manage test environment for the XDP tutorial. +# See README.org for instructions on how to use. +# +# Author: Toke Høiland-Jørgensen (toke@redhat.com) +# Date: 6 March 2019 +# Copyright (c) 2019 Red Hat + +set -o errexit +set -o nounset +umask 077 + +source "$(dirname "$0")/config.sh" + +NEEDED_TOOLS="ethtool ip tc ping" +MAX_NAMELEN=15 + +# Global state variables that will be set by options etc below +GENERATE_NEW=0 +CLEANUP_FUNC= +STATEFILE= +CMD= +NS= +XDP_LOADER=./xdp_loader +XDP_STATS=./xdp_stats +LEGACY_IP=0 +USE_VLAN=0 +RUN_ON_INNER=0 + +# State variables that are written to and read from statefile +STATEVARS=(IP6_PREFIX IP4_PREFIX + INSIDE_IP6 INSIDE_IP4 INSIDE_MAC + OUTSIDE_IP6 OUTSIDE_IP4 OUTSIDE_MAC + ENABLE_IPV4 ENABLE_VLAN) +IP6_PREFIX= +IP4_PREFIX= +INSIDE_IP6= +INSIDE_IP4= +INSIDE_MAC= +OUTSIDE_IP6= +OUTSIDE_IP4= +OUTSIDE_MAC= +ENABLE_IPV4=0 +ENABLE_VLAN=0 + +die() +{ + echo "$1" >&2 + exit 1 +} + +check_prereq() +{ + local max_locked_mem=$(ulimit -l) + + for t in $NEEDED_TOOLS; do + which "$t" > /dev/null || die "Missing required tools: $t" + done + + if [ "$EUID" -ne "0" ]; then + die "This script needs root permissions to run." + fi + + [ -d "$STATEDIR" ] || mkdir -p "$STATEDIR" || die "Unable to create state dir $STATEDIR" + + if [ "$max_locked_mem" != "unlimited" ]; then + ulimit -l unlimited || die "Unable to set ulimit" + fi +} + +get_nsname() +{ + local GENERATE=${1:-0} + + if [ -z "$NS" ]; then + [ -f "$STATEDIR/current" ] && NS=$(< "$STATEDIR/current") + + if [ "$GENERATE" -eq "1" ] && [ -z "$NS" -o "$GENERATE_NEW" -eq "1" ]; then + NS=$(printf "%s-%04x" "$GENERATED_NAME_PREFIX" $RANDOM) + fi + fi + + if [ "${#NS}" -gt "$MAX_NAMELEN" ]; then + die "Environment name '$NS' is too long (max $MAX_NAMELEN)" + fi + + STATEFILE="$STATEDIR/${NS}.state" +} + +ensure_nsname() +{ + [ -z "$NS" ] && die "No environment selected; use --name to select one or 'setup' to create one" + [ -e "$STATEFILE" ] || die "Environment for $NS doesn't seem to exist" + + echo "$NS" > "$STATEDIR/current" + + read_statefile +} + +get_num() +{ + local num=1 + if [ -f "$STATEDIR/highest_num" ]; then + num=$(( 1 + $(< "$STATEDIR/highest_num" ))) + fi + + echo $num > "$STATEDIR/highest_num" + printf "%x" $num +} + +write_statefile() +{ + [ -z "$STATEFILE" ] && return 1 + echo > "$STATEFILE" + for var in "${STATEVARS[@]}"; do + echo "${var}='$(eval echo '$'$var)'" >> "$STATEFILE" + done +} + +read_statefile() +{ + local value + for var in "${STATEVARS[@]}"; do + value=$(source "$STATEFILE"; eval echo '$'$var) + eval "$var=\"$value\"" + done +} + +cleanup_setup() +{ + echo "Error during setup, removing partially-configured environment '$NS'" >&2 + set +o errexit + ip netns del "$NS" 2>/dev/null + ip link del dev "$NS" 2>/dev/null + rm -f "$STATEFILE" +} + +cleanup_teardown() +{ + echo "Warning: Errors during teardown, partial environment may be left" >&2 +} + + +cleanup() +{ + [ -n "$CLEANUP_FUNC" ] && $CLEANUP_FUNC + + [ -d "$STATEDIR" ] || return 0 + + local statefiles=("$STATEDIR"/*.state) + + if [ "${#statefiles[*]}" -eq 1 ] && [ ! -e "${statefiles[0]}" ]; then + rm -f "${STATEDIR}/highest_num" "${STATEDIR}/current" + rmdir "$STATEDIR" + fi +} + +iface_macaddr() +{ + local iface="$1" + local ns="${2:-}" + local output + + if [ -n "$ns" ]; then + output=$(ip -br -n "$ns" link show dev "$iface") + else + output=$(ip -br link show dev "$iface") + fi + echo "$output" | awk '{print $3}' +} + +set_sysctls() +{ + local iface="$1" + local in_ns="${2:-}" + local nscmd= + + [ -n "$in_ns" ] && nscmd="ip netns exec $in_ns" + local sysctls=(accept_dad + accept_ra + mldv1_unsolicited_report_interval + mldv2_unsolicited_report_interval) + + for s in ${sysctls[*]}; do + $nscmd sysctl -w net.ipv6.conf.$iface.${s}=0 >/dev/null + done +} + +wait_for_dev() +{ + local iface="$1" + local in_ns="${2:-}" + local retries=5 # max retries + local nscmd= + + [ -n "$in_ns" ] && nscmd="ip netns exec $in_ns" + while [ "$retries" -gt "0" ]; do + if ! $nscmd ip addr show dev $iface | grep -q tentative; then return 0; fi + sleep 0.5 + retries=$((retries -1)) + done +} + +get_vlan_prefix() +{ + # Split the IPv6 prefix, and add the VLAN ID to the upper byte of the fourth + # element in the prefix. This will break if the global prefix config doesn't + # have exactly three elements in it. + local prefix="$1" + local vid="$2" + (IFS=:; set -- $prefix; printf "%s:%s:%s:%x::" "$1" "$2" "$3" $(($4 + $vid * 4096))) +} + +setup() +{ + get_nsname 1 + + echo "Setting up new environment '$NS'" + + [ -e "$STATEFILE" ] && die "Environment for '$NS' already exists" + + local NUM=$(get_num "$NS") + local PEERNAME="testl-ve-$NUM" + [ -z "$IP6_PREFIX" ] && IP6_PREFIX="${IP6_SUBNET}:${NUM}::" + [ -z "$IP4_PREFIX" ] && IP4_PREFIX="${IP4_SUBNET}.$((0x$NUM))." + + INSIDE_IP6="${IP6_PREFIX}2" + INSIDE_IP4="${IP4_PREFIX}2" + OUTSIDE_IP6="${IP6_PREFIX}1" + OUTSIDE_IP4="${IP4_PREFIX}1" + + CLEANUP_FUNC=cleanup_setup + + if ! mount | grep -q /sys/fs/bpf; then + mount -t bpf bpf /sys/fs/bpf/ + fi + + ip netns add "$NS" + ip link add dev "$NS" type veth peer name veth0 netns "$NS" + + set_sysctls $NS + ip link set dev "$NS" up + ip addr add dev "$NS" "${OUTSIDE_IP6}/${IP6_PREFIX_SIZE}" + ethtool -K "$NS" rxvlan off txvlan off + # Prevent neighbour queries on the link + INSIDE_MAC=$(iface_macaddr veth0 "$NS") + ip neigh add "$INSIDE_IP6" lladdr "$INSIDE_MAC" dev "$NS" nud permanent + + set_sysctls veth0 "$NS" + ip -n "$NS" link set dev lo up + ip -n "$NS" link set dev veth0 up + ip -n "$NS" addr add dev veth0 "${INSIDE_IP6}/${IP6_PREFIX_SIZE}" + ip netns exec "$NS" ethtool -K veth0 rxvlan off txvlan off + # Prevent neighbour queries on the link + OUTSIDE_MAC=$(iface_macaddr "$NS") + ip -n "$NS" neigh add "$OUTSIDE_IP6" lladdr "$OUTSIDE_MAC" dev veth0 nud permanent + # Add route for whole test subnet, to make it easier to communicate between + # namespaces + ip -n "$NS" route add "${IP6_SUBNET}::/$IP6_FULL_PREFIX_SIZE" via "$OUTSIDE_IP6" dev veth0 + + if [ "$LEGACY_IP" -eq "1" ]; then + ip addr add dev "$NS" "${OUTSIDE_IP4}/${IP4_PREFIX_SIZE}" + ip -n "$NS" addr add dev veth0 "${INSIDE_IP4}/${IP4_PREFIX_SIZE}" + ip neigh add "$INSIDE_IP4" lladdr "$INSIDE_MAC" dev "$NS" nud permanent + ip -n "$NS" neigh add "$OUTSIDE_IP4" lladdr "$OUTSIDE_MAC" dev veth0 nud permanent + ip -n "$NS" route add "${IP4_SUBNET}/${IP4_FULL_PREFIX_SIZE}" via "$OUTSIDE_IP4" dev veth0 + ENABLE_IPV4=1 + else + ENABLE_IPV4=0 + fi + + if [ "$USE_VLAN" -eq "1" ]; then + ENABLE_VLAN=1 + for vid in "${VLAN_IDS[@]}"; do + local vlpx="$(get_vlan_prefix "$IP6_PREFIX" "$vid")" + local inside_ip="${vlpx}2" + local outside_ip="${vlpx}1" + ip link add dev "${NS}.$vid" link "$NS" type vlan id "$vid" + ip link set dev "${NS}.$vid" up + ip addr add dev "${NS}.$vid" "${outside_ip}/${IP6_PREFIX_SIZE}" + ip neigh add "$inside_ip" lladdr "$INSIDE_MAC" dev "${NS}.$vid" nud permanent + set_sysctls "${NS}/$vid" + + ip -n "$NS" link add dev "veth0.$vid" link "veth0" type vlan id "$vid" + ip -n "$NS" link set dev "veth0.$vid" up + ip -n "$NS" addr add dev "veth0.$vid" "${inside_ip}/${IP6_PREFIX_SIZE}" + ip -n "$NS" neigh add "$outside_ip" lladdr "$OUTSIDE_MAC" dev "veth0.$vid" nud permanent + set_sysctls "veth0/$vid" "$NS" + done + else + ENABLE_VLAN=0 + fi + + write_statefile + + CLEANUP_FUNC= + + echo -n "Setup environment '$NS' with peer ip ${INSIDE_IP6}" + [ "$ENABLE_IPV4" -eq "1" ] && echo " and ${INSIDE_IP4}." || echo "." + echo "Waiting for interface configuration to settle..." + echo "" + wait_for_dev "$NS" && wait_for_dev veth0 "$NS" + + LEGACY_IP=0 USE_VLAN=0 run_ping -c 1 + + echo "$NS" > "$STATEDIR/current" +} + +teardown() +{ + get_nsname && ensure_nsname "$NS" + + echo "Tearing down environment '$NS'" + + CLEANUP_FUNC=cleanup_teardown + + ip link del dev "$NS" + ip netns del "$NS" + rm -f "$STATEFILE" + [ -d "/sys/fs/bpf/$NS" ] && rmdir "/sys/fs/bpf/$NS" || true + + if [ -f "$STATEDIR/current" ]; then + local CUR=$(< "$STATEDIR/current" ) + [[ "$CUR" == "$NS" ]] && rm -f "$STATEDIR/current" + fi + + CLEANUP_FUNC= +} + +reset() +{ + teardown && setup +} + +ns_exec() +{ + get_nsname && ensure_nsname "$NS" + + ip netns exec "$NS" env TESTENV_NAME="$NS" "$SETUP_SCRIPT" "$@" +} + +enter() +{ + ns_exec "${SHELL:-bash}" +} + +run_ping() +{ + local PING + local IP + + get_nsname && ensure_nsname "$NS" + + echo "Running ping from inside test environment:" + echo "" + + if [ "$LEGACY_IP" -eq "1" ]; then + PING=$(which ping) + IP="${OUTSIDE_IP4}" + [ "$USE_VLAN" -eq "0" ] || die "Can't use --legacy-ip and --vlan at the same time." + [ "$ENABLE_IPV4" -eq "1" ] || die "No legacy IP addresses configured in environment." + else + PING=$(which ping6 2>/dev/null || which ping) + if [ "$USE_VLAN" -eq "0" ]; then + IP="${OUTSIDE_IP6}" + else + [ "$ENABLE_VLAN" -eq "1" ] || die "No VLANs configured in environment." + IP="$(get_vlan_prefix "$IP6_PREFIX" "${VLAN_IDS[0]}")1" + fi + fi + + ns_exec "$PING" "$IP" "$@" +} + +run_tcpdump() +{ + get_nsname && ensure_nsname "$NS" + + if [ "$RUN_ON_INNER" -eq "1" ]; then + ns_exec tcpdump -nei veth0 "$@" + else + tcpdump -nei "$NS" "$@" + fi +} + +status() +{ + get_nsname + + echo "Currently selected environment: ${NS:-None}" + if [ -n "$NS" ] && [ -e "$STATEFILE" ]; then + read_statefile + echo -n " Namespace: "; ip netns | grep "^$NS" + echo " Prefix: ${IP6_PREFIX}/${IP6_PREFIX_SIZE}" + [ "$ENABLE_IPV4" -eq "1" ] && echo " Legacy prefix: ${IP4_PREFIX}0/${IP4_PREFIX_SIZE}" + echo -n " Iface: "; ip -br a show dev "$NS" | sed 's/\s\+/ /g' + fi + echo "" + + echo "All existing environments:" + for f in "$STATEDIR"/*.state; do + if [ ! -e "$f" ]; then + echo " No environments exist" + break + fi + NAME=$(basename "$f" .state) + echo " $NAME" + done +} + +print_alias() +{ + local scriptname="$(readlink -e "$0")" + local sudo= + + [ -t 1 ] && echo "Eval this with \`eval \$($0 alias)\` to create shell alias" >&2 + + if [ "$EUID" -ne "0" ]; then + sudo="sudo " + echo "WARNING: Creating sudo alias; be careful, this script WILL execute arbitrary programs" >&2 + fi + + echo "" >&2 + + + echo "alias t='$sudo$scriptname'" +} + +# +# This command can be used to populate maps for the assignment 3 of the +# packet03-redirecting lesson. It takes two arguments: the source and the +# destination environment names. +# +populate_redirect_map() +{ + local src="$1" + local dest="$2" + local src_mac=$(ip netns exec $src cat /sys/class/net/veth0/address) + local dest_mac=$(ip netns exec $dest cat /sys/class/net/veth0/address) + + # set bidirectional forwarding + ./xdp_prog_user -d $src -r $dest --src-mac $src_mac --dest-mac $dest_mac + ./xdp_prog_user -d $dest -r $src --src-mac $dest_mac --dest-mac $src_mac +} + +xdp_load() +{ + get_nsname && ensure_nsname + + [ -x "$XDP_LOADER" ] || die "Loader '$XDP_LOADER' is not executable" + $XDP_LOADER --dev "$NS" "$@" +} + +xdp_unload() +{ + get_nsname && ensure_nsname + + [ -x "$XDP_LOADER" ] || die "Loader '$XDP_LOADER' is not executable" + $XDP_LOADER --dev "$NS" --unload "$@" +} + +xdp_stats() +{ + get_nsname && ensure_nsname + + [ -x "$XDP_STATS" ] || die "Stats tool '$XDP_STATS' is not executable" + $XDP_STATS --dev "$NS" "$@" +} + +usage() +{ + local FULL=${1:-} + + echo "Usage: $0 [options] [param]" + echo "" + echo "Commands:" + echo "setup Setup and initialise new environment" + echo "teardown Tear down existing environment" + echo "reset Reset environment to original state" + echo "exec Exec inside test environment" + echo "enter Execute shell inside test environment" + echo "ping Run ping inside test environment" + echo "alias Print shell alias for easy access to this script" + echo "status (or st) Show status of test environment" + echo "load Load XDP program on outer interface" + echo "unload Unload XDP program on outer interface" + echo "tcpdump Run on outer interface (or inner with --inner)" + echo "stats Run the XDP statistics program" + echo "redirect Setup redirects for packet03 lessons" + echo "" + + if [ -z "$FULL" ] ; then + echo "Use --help to see the list of options." + exit 1 + fi + + echo "Options:" + echo "-h, --help Show this usage text" + echo "" + echo "-n, --name Set name of test environment. If not set, the last used" + echo " name will be used, or a new one generated." + echo "" + echo "-g, --gen-new Generate a new test environment name even though an existing" + echo " environment is selected as the current one." + echo "" + echo "-l, --loader Specify program to use for loading XDP programs." + echo " Device name will be passed to it, along with any additional" + echo " command line options passed after --." + echo " Default: '$XDP_LOADER'" + echo "" + echo "-s, --stats Specify program to use for getting statistics ('stats' command)." + echo " Device name will be passed to it, along with any additional" + echo " command line options passed after --." + echo " Default: '$XDP_STATS'" + echo "" + echo " --legacy-ip Enable legacy IP (IPv4) support." + echo " For setup and reset commands this enables configuration of legacy" + echo " IP addresses on the interface, for the ping command it switches to" + echo " legacy ping." + echo "" + echo " --vlan Enable VLAN support." + echo " When used with the setup and reset commands, these VLAN IDs will" + echo " be configured: ${VLAN_IDS[*]}. The VLAN interfaces are named as" + echo " .." + echo " When used with the ping command, the pings will be sent on the" + echo " first VLAN ID (${VLAN_IDS[0]})." + echo "" + echo " --inner Use with tcpdump command to run on inner interface." + echo "" + exit 1 +} + + +OPTS="hn:gl:s:" +LONGOPTS="help,name:,gen-new,loader:,stats:,legacy-ip,vlan,inner" + +OPTIONS=$(getopt -o "$OPTS" --long "$LONGOPTS" -- "$@") +[ "$?" -ne "0" ] && usage >&2 || true + +eval set -- "$OPTIONS" + + +while true; do + arg="$1" + shift + + case "$arg" in + -h | --help) + usage full >&2 + ;; + -n | --name) + NS="$1" + shift + ;; + -l | --loader) + XDP_LOADER="$1" + shift + ;; + -s | --stats) + XDP_STATS="$1" + shift + ;; + -g | --gen-new) + GENERATE_NEW=1 + ;; + --legacy-ip) + LEGACY_IP=1 + ;; + --vlan) + USE_VLAN=1 + ;; + --inner) + RUN_ON_INNER=1 + ;; + -- ) + break + ;; + esac +done + +[ "$#" -eq 0 ] && usage >&2 + +case "$1" in + st|sta|status) + CMD=status + ;; + setup|teardown|reset|enter) + CMD="$1" + ;; + load|unload|stats) + CMD="xdp_$1" + ;; + "exec") + CMD=ns_exec + ;; + ping|tcpdump) + CMD="run_$1" + ;; + redirect) + CMD=populate_redirect_map + ;; + "alias") + print_alias + exit 0 + ;; + "help") + usage full >&2 + ;; + *) + usage >&2 + ;; +esac + +shift +trap cleanup EXIT +check_prereq +$CMD "$@" diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/Makefile b/eBPF_Supermarket/Network_Subsystem/net_watcher/Makefile index 38ff8307f..c8c6e30fe 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/Makefile +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/Makefile @@ -1,12 +1,19 @@ # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) OUTPUT := .output CLANG ?= clang -LIBBPF_SRC := $(abspath ../libbpf-bootstrap/libbpf/src) -BPFTOOL_SRC := $(abspath ../libbpf-bootstrap/bpftool/src) +LIBBPF_SRC := $(abspath ../../lib/libbpf/src) +BPFTOOL_SRC := $(abspath ../../lib/bpftool/src) LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool - +VERSION_INFO := $(shell uname -r | cut -d'-' -f1) +VERSION_MAJOR := $(shell echo $(VERSION_INFO) | cut -d'.' -f1) +VERSION_MINOR := $(shell echo $(VERSION_INFO) | cut -d'.' -f2) +VERSION_PATCH := $(shell echo $(VERSION_INFO) | cut -d'.' -f3) +export VERSION_INFO +export VERSION_MAJOR +export VERSION_MINOR +export VERSION_PATCH ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \ | sed 's/arm.*/arm/' \ | sed 's/aarch64/arm64/' \ @@ -14,11 +21,14 @@ ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \ | sed 's/mips.*/mips/' \ | sed 's/riscv64/riscv/' \ | sed 's/loongarch64/loongarch/') -VMLINUX := ../libbpf-bootstrap/vmlinux/$(ARCH)/vmlinux.h +LOGSRC := ./data/doc +VMDIR := ../../lib/vmlinux +VMLINUXSRC := $(VMDIR)/$(ARCH) +VMLINUX := $(VMLINUXSRC)/vmlinux.h # Use our own libbpf API headers and Linux UAPI headers distributed with # libbpf to avoid dependency on system-wide headers, which could be missing or # outdated -INCLUDES := -I$(OUTPUT) -I../../libbpf/include/uapi -I$(dir $(VMLINUX)) +INCLUDES := -I$(OUTPUT) -I ../../lib/libbpf/include/uapi -I$(dir $(VMLINUX)) CFLAGS := -g -Wall ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) @@ -57,13 +67,62 @@ $(call allow-override,CC,$(CROSS_COMPILE)cc) $(call allow-override,LD,$(CROSS_COMPILE)ld) .PHONY: all -all: $(APPS) +all: deps $(APPS) +#更新vmlinux.h文件 +.PHONY: deps +deps:check_bpftool check_clang check_multilib + @echo "Kernel version is $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH)" + mkdir -p $(VMLINUXSRC) + bpftool btf dump file /sys/kernel/btf/vmlinux format c > $(VMLINUX) + +.PHONY: check_bpftool +check_bpftool: + @if ! command -v bpftool &> /dev/null; then \ + echo "bpftool 未安装,正在安装..."; \ + sudo apt-get update; \ + sudo apt-get install -y linux-tools-$(shell uname -r); \ + else \ + echo "bpftool 已经安装"; \ + fi +.PHONY: check_clang + +check_clang: + @if ! command -v clang &> /dev/null; then \ + echo "clang 未安装,正在安装..."; \ + sudo apt-get update; \ + sudo apt-get install -y clang; \ + else \ + echo "clang 已经安装"; \ + fi + +# 检查 multilib 是否安装,如果没有则安装 +.PHONY: check_multilib +check_multilib: + @if [ ! -f /usr/include/x86_64-linux-gnu/gnu/stubs-32.h ]; then \ + echo "multilib 未安装,正在安装..."; \ + sudo apt-get update; \ + sudo apt-get install -y gcc-multilib g++-multilib; \ + else \ + echo "multilib 已经安装"; \ + fi .PHONY: clean clean: $(call msg,CLEAN) $(Q)rm -rf $(OUTPUT) $(APPS) - + + rm -rf $(LOGSRC) + rm -rf $(VMDIR) + +.PHONY: clean2 +clean2: + $(call msg,CLEAN) + rm -f $(APPS) + rm -f $(OUTPUT)/*.skel.h + rm -f $(OUTPUT)/*.o + rm -rf $(LOGSRC) + rm -rf $(VMDIR) + $(OUTPUT) $(OUTPUT)/libbpf $(BPFTOOL_OUTPUT): $(call msg,MKDIR,$@) $(Q)mkdir -p $@ @@ -72,7 +131,7 @@ $(OUTPUT) $(OUTPUT)/libbpf $(BPFTOOL_OUTPUT): $(LIBBPF_OBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPUT)/libbpf $(call msg,LIB,$@) $(Q)$(MAKE) -C $(LIBBPF_SRC) BUILD_STATIC_ONLY=1 \ - OBJDIR=$(dir $@)/libbpf DESTDIR=$(dir $@) \ + OBJDIR=$(dir $@)libbpf DESTDIR=$(dir $@) \ INCLUDEDIR= LIBDIR= UAPIDIR= \ install @@ -82,9 +141,13 @@ $(BPFTOOL): | $(BPFTOOL_OUTPUT) $(Q)$(MAKE) ARCH= CROSS_COMPILE= OUTPUT=$(BPFTOOL_OUTPUT)/ -C $(BPFTOOL_SRC) bootstrap # Build BPF code -$(OUTPUT)/%.bpf.o: %.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT) $(BPFTOOL) +$(OUTPUT)/%.bpf.o: %.bpf.c $(LIBBPF_OBJ) $(wildcard *.h) $(VMLINUX) | $(OUTPUT) $(BPFTOOL) $(call msg,BPF,$@) $(Q)$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) \ + -D__TARGET_ARCH_$(ARCH) \ + -DVERSION_MAJOR=$(VERSION_MAJOR) \ + -DVERSION_MINOR=$(VERSION_MINOR) \ + -DVERSION_PATCH=$(VERSION_PATCH) \ $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) \ -c $(filter %.c,$^) -o $(patsubst %.bpf.o,%.tmp.bpf.o,$@) $(Q)$(BPFTOOL) gen object $@ $(patsubst %.bpf.o,%.tmp.bpf.o,$@) @@ -97,7 +160,7 @@ $(OUTPUT)/%.skel.h: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL) # Build user-space code $(patsubst %,$(OUTPUT)/%.o,$(APPS)): %.o: %.skel.h -$(OUTPUT)/%.o: %.c $(wildcard %.h) | $(OUTPUT) +$(OUTPUT)/%.o: %.c $(wildcard %.h) | $(OUTPUT) $(wildcard %.h) $(call msg,CC,$@) $(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $(filter %.c,$^) -o $@ diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/README.md b/eBPF_Supermarket/Network_Subsystem/net_watcher/README.md index 7cf63cb0f..355cf2f18 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/README.md +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/README.md @@ -1,6 +1,7 @@ # netwatcher - 网络检测工具 ## 一、工具介绍 ### 1.1 背景 + lmp现有许多用于监控linux网络协议栈相关信息的小工具,但这些工具功能零散并且冗余: | 工具目录 | 功能 | 监控信息 | | --- | --- | --- | @@ -18,18 +19,26 @@ lmp现有许多用于监控linux网络协议栈相关信息的小工具,但这 ### 1.2 功能介绍 -`netwatcher`是一款基于eBPF的网络检测工具,其旨在使用户方便快捷的获取主机环境下linux网络协议栈的各种信息。 +`netwatcher`是一款基于eBPF的高效网络检测工具,其目的是为了让用户能够轻松快捷地获取到网络协议栈的详细信息,通过高效的数据采集和精准的监控能力,帮助用户深入了解网络行为,确保网络安全和性能的优化。其在车辆智能导航、自动驾驶等关键领扮演着重要角色,面对网络异常或延迟时,`netwatcher`能够提供强大的网络监测和优化支持,帮助企业及时诊断并解决网络障碍,提升系统稳定性和服务可靠性。 + +netwatcher作为一款基于eBPF的网络检测工具,其设计初衷是帮助用户能够在主机环境中,轻松快捷地获取到 Linux 网络协议栈的详细信息,通过高效的数据收集和精准的监控能力,深入了解网络行为,确保网络安全和性能的优化,其应用范围涉及较广。 + +netwatcher能够追踪TCP、UDP、ICMP协议数据包从应用程序发出开始,经过内核协议栈到驱动、最终发出过程(发包路径)的时延数据和流量信息,以及数据包从驱动到内核协议栈到用户态程序的过程(收包路径)中的时延数据。对获取到的时延数据采用算法进行阈值比较捕获异常时延数据并给予警告信息。同时监测TCP连接的状态信息(seq、ack、连接状态、重传信息、错误信息、rwnd、cwnd、sndbuf等关键指标),并且可以监控丢包事件(包括skb_drop_reason中定义的77种原因)。 + +其有助于及时解决各种网络问题,提高系统稳定性和服务可靠性。无论是云计算所需要的确保其基础设施的稳定性和安全性,还是金融机构需要保障交易的及时性和安全性,亦或是电子商务企业追求用户体验和网站性能的优化,netwatcher都提供了强大的网络监测和优化能力,满足不同行业的需求,助力企业顺利应对数字化时代的挑战。 目前,其实现的功能包括: + - TCP相关的信息监测:主机环境下对tcp/ip协议的分析,可以统计流量,延时,错误,链接信息等主要信息 - HTTP1/1.1相关信息检测:通过截取相应TCP包的HTTP头实现主机环境下对用户态http1的分析 +- TCP、UDP、ICMP相关信息监测:追踪TCP、UDP、ICMP协议数据包,并实现对主机接收和发送的所有相关数据包的时延数据和流量信息 +- 监测TCP连接状态信息:包括三次握手以及四次挥手的状态转变和时延数据 +- 丢包事件的监控:分析导致丢包的地址以及丢包原因 +- DNS协议相关信息监控:通过截取UDP包,对DNS协议包进行解析,获取事务ID、标志字段、问题部分计数、应答记录计数、授权记录计数、附加记录计数、域名等信息 +- 主机环境下对用户态mysql的分析:uprobe实现对mysql的监测,其监测内容有进程pid、进程名、sql语句、sql语句执行时间。 #### TODO -- [ ] ICMP数据包信息的监控 - - 实现对ICMP协议报文的监控,输出到达的ICMP数据包的消息类型 -- [ ] UDP数据包信息的监控 - - 实现对主机接收和发送的所有UDP数据包的监控,输出各个UDP数据包的地址-端口4元组 -- [ ] 应用层协议的支持 +- [ ] 应用层协议的支持 - 在各个底层协议之上,提供对以下应用层信息的监控 - [ ] HTTP协议相关 - [ ] HTTP1 @@ -47,19 +56,33 @@ lmp现有许多用于监控linux网络协议栈相关信息的小工具,但这 ### 1.3 组织结构 -- netwatcher.bpf.c:在各个内核探针点对TCP包信息、TCP连接信息以及各个包的HTTP1/1.1信息进行记录 -- netwatcher.c: 对bpf.c文件中记录的信息进行输出 -- netwatcher.h: 定义内核态与用户态程序共用的结构体 - doc/: - implement.md:详细描述本项目的实现细节 -- data/: +- data/:文件夹存放打印的日志信息 - connects.log:符合Prometheus格式的连接信息 - err.log:符合Prometheus格式的错误包信息 - packets.log:符合Prometheus格式的包信息 +- udp.loh:符合Prometheus格式的udp包信息 - visual.py:暴露metrics接口给Prometheus,输出data文件夹下的所有信息 +- netwatcher.c :对bpf.c文件中记录的信息进行输出 +- netwatcher.bpf.c:封装内核探针点。 +- tcp.bpf.h:网络数据包处理以及tcp连接状态等信息具体实现细节。 +- udp.bpf.h :udp数据包时延、流量的具体处理逻辑。 +- packet.bpf.h :网络数据包的处理、时间戳的记录、数据包信息的提取等指标具体处理逻辑。 +- netfilter.bpf.h:处理netfilter时延的具体逻辑,`submit_nf_time`函数将时延信息提交到用户态,`store_nf_time`函数存储经过每个`HOOK`点的时延。 +- drop.bpf.h :数据包丢弃原因的具体处理逻辑。 +- dropreason.h :skb_drop_reason定义77种丢包原因。 +- icmp.bpf.h: icmp时延具体实现细节。 +- comm.bpf.h :辅助函数、宏、BPF映射、以及内核中使用到的结构体。 +- mysql,bpf.h : 处理mysql的具体实现逻辑。 +- mysql_helper.bpf : mysql相关数据结构。 + ## 二、快速开始 ### 2.1 安装依赖 OS: Ubuntu 22.04LTS + +Kernel:Linux 6.2 + ```bash sudo apt update sudo apt install libbpf-dev clang llvm libelf-dev libpcap-dev gcc-multilib build-essential @@ -78,22 +101,41 @@ sudo make test # 测试 ```bash Usage: netwatcher [OPTION...] +Watch tcp/ip in network subsystem +Usage: netwatcher [OPTION...] Watch tcp/ip in network subsystem -a, --all set to trace CLOSED connection + -A, --stack set to trace of stack -d, --dport=DPORT trace this destination port only + -D, --dns set to trace dns information info include Id + 事务ID、Flags 标志字段、Qd + 问题部分计数、An 应答记录计数、Ns + 授权记录计数、Ar 附加记录计数、Qr + 域名、rx 收发包 -e, --err set to trace TCP error packets -i, --http set to trace http info + -I, --icmptime set to trace layer time of icmp + -k, --drop_reason trace kfree + -L, --timeload analysis time load + -M, --mysql set to trace mysql information info include Pid + 进程id、Comm 进程名、Size + sql语句字节大小、Sql 语句 + -n, --net_filter trace ipv4 packget filter -r, --retrans set to trace extra retrans info -s, --sport=SPORT trace this source port only + -S, --tcpstate set to trace tcpstate -t, --time set to trace layer time of each packet + -T, --addr_to_func translation addr to func and offset + -u, --udp trace the udp message -x, --extra set to trace extra conn info -?, --help Give this help list - --usage Give a short usage messages. + --usage Give a short usage message + ``` - 参数`-d`,`-s`用于指定监控某个源端口/目的端口 - 指定参数`-a`会保留已CLOSED的TCP连接信息 -- 指定`-e`参数会记录SEQ错误或Checksum错误的TCP包 +- 指定`-e`参数会记录SEQ错误或Checksum错误的TCP包并输出到标准输出以及data/err.log中。 - 默认情况下,监控以下连接信息与包信息: - TCP连接pid - TCP连接\[源地址:端口,目的地址:端口\] @@ -115,12 +157,23 @@ Watch tcp/ip in network subsystem - 已使用的发送缓冲区 - 平滑往返时间 - 连接已建立时长 - - 连接总重传次数 + - 连接总重传次数。 +- 指定 `-i` 参数监控HTTP信息。从携带HTTP请求头的TCP包中将HTTP请求头提取并输出,记录HTTP的状态码等信息。 +- 指定 `-u` 参数监控UDP数据包信息。统计UDP协议的数据包流量、以us为单位的时延等信息。 +- 指定 `-n` 参数监控ipv4网络层的Netfilter延迟。监测ipv4网络层数据包经过Netfilter各个HOOK点的处理时延。 +- 指定 `-k` 参数监测系统中的各类丢包并分析丢包原因,详细定位协议丢包的指令地址。 +- 指定 `-S` 参数监控TCP的连接状态以及状态转换的时延。 +- 指定 `-I` 参数监控ICMP协议数据包收发过程中的时延。 +- 指定 `-T` 参数将捕获到的丢包事件虚拟地址转换成函数名+偏移量形式。 +- 指定 `-L` 参数监测网络协议栈数据包经过各层的时延,采用指数加权移动法对异常的时延数据进行监控并发出警告信息。 +- 指定 `-D` 参数监测DNS协议包信息。截取UDP包,对DNS协议包进行解析,获取其基本指标,包含事务ID、标志字段、问题部分计数、应答记录计数、域名等相关信息。 +- 指定 `-M` 参数监测Mysql信息。实现用户态下mysql监控,获取其sql语句及sql执行耗时,单位μs。 + ### 3.1 监控连接信息 `netwatcher`会将保存在内存中的连接相关信息实时地在`data/connects.log`中更新。默认情况下,为节省资源消耗,`netwatcher`会实时删除已CLOSED的TCP连接相关信息,并只会保存每个TCP连接的基本信息。 -``` +```c // data/connects.log -connection{pid="44793",sock="0xffff9d1ecb3ba300",src="10.0.2.15:46348",dst="103.235.46.40:80",is_server="0",backlog="-",maxbacklog="-",cwnd="-",ssthresh="-",sndbuf="-",wmem_queued="-",rx_bytes="-",tx_bytes="-",srtt="-",duration="-",total_retrans="-",fast_retrans="-",timeout_retrans="-"} 0 +connection{pid="44793",sock="0xffff9d1ecb3ba300",src="10.0.2.15:46348",dst="103.235.46.40:80",is_server="0",backlog="-",maxbacklog="-",cwnd="-",ssthresh="-",sndbuf="-",wmem_queued="-",rx_bytes="-",tx_bytes="-",srtt="-",duration="-",total_retrans="-",fast_retrans="-",timeout_retrans="-"} ``` #### 3.1.1 保留所有连接信息 对于想要保留所有连接信息的用户,需要指定`-a`参数。 @@ -128,58 +181,189 @@ connection{pid="44793",sock="0xffff9d1ecb3ba300",src="10.0.2.15:46348",dst="103. sudo ./netwatcher -a ``` #### 3.1.2 监控额外连接信息 -`netwatcher`默认只监控基本连接信息,额外连接信息输出为`-`;对于想要监控额外连接信息的用户,需要指定`-x`参数。 -``` -sudo ./netwatcher -x +`netwatcher`指定`-x`参数输出额外信息 ,额外信息实时更新于data/connects.log日志中。记录接收窗口大小rwnd、拥塞窗口大小cwnd、慢启动阈值ssthresh、发送缓冲区大小sndbuf、已使用的发送缓冲区wmem_queued、已接收字节数rx_bytes、已确认字节数tx_bytes、平滑往返时间srtt、连接建立时延duration等关键信息。 + +```c +sudo ./netwatcher -x +connection{pid="45395",sock="0xffff9780c13f7500",src="192.168.60.136:42938",dst="171.214.23.48:443",is_server="0",backlog="0",maxbacklog="0",rwnd="63986",cwnd="10",ssthresh="2147483647",sndbuf="87040",wmem_queued="0",rx_bytes="603",tx_bytes="1.563K",srtt="68166",duration="63879",total_retrans="0",fast_retrans="-",timeout_retrans="-"} ``` #### 3.1.3 监控重传信息 -`netwatcher`监控的额外连接信息并不提供对超时重传次数与快速重传次数的细化;对于想要获取此信息的用户,需要指定`-r`参数。 -``` +`netwatcher`监控超时重传次数、快速重传次数和连接总重传次数,指定`-r`参数。 + +```c sudo ./netwatcher -r +connection{pid="45395",sock="0xffff9780c13f6c00",src="192.168.60.136:60210",dst="124.237.208.55:443",is_server="0",backlog="0",maxbacklog="0",rwnd="63784",cwnd="10",ssthresh="2147483647",sndbuf="87040",wmem_queued="0",rx_bytes="568",tx_bytes="8.489K",srtt="65211",duration="319696",total_retrans="0",fast_retrans="-",timeout_retrans="2"} ``` ### 3.2 监控包信息 `netwatcher`会将各个TCP包的相关信息实时地输出在标准输出以及`data/packets.log`中。默认情况下,为节省资源消耗,`netwatcher`只会监控各个包的基本信息并输出。 -``` + +```c sudo ./netwatcher -SOCK SEQ ACK MAC_TIME IP_TIME TCP_TIME RX HTTP +SOCK SEQ ACK MAC_TIME IP_TIME TCP_TIME RX HTTP 0xffff9d1ecb3ba300 629372796 279168002 - - - 0 - 0xffff9d1ecb3ba300 279168002 629372873 - - - 1 - ``` -``` +```c // data/packets.log -packet{sock="0xffff9d1ecb3ba300",seq="629372796",ack="279168002",mac_time="-",ip_time="-",tcp_time="-",http_info="-",rx="0"} 0 -packet{sock="0xffff9d1ecb3ba300",seq="279168002",ack="629372873",mac_time="-",ip_time="-",tcp_time="-",http_info="-",rx="1"} 0 +packet{sock="0xffff9d1ecb3ba300",seq="629372796",ack="279168002",mac_time="-",ip_time="-",tcp_time="-",http_info="-",rx="0"} +packet{sock="0xffff9d1ecb3ba300",seq="279168002",ack="629372873",mac_time="-",ip_time="-",tcp_time="-",http_info="-",rx="1"} +``` +#### 3.2.1监控数据包在各层的处理时延 +`netwatcher`监控各个数据包在各层的处理时间,指定`-t`参数,`netwatcher`会输出以`us`为单位的各层处理时间,支持ipv4、ipv6协议。 +```c +sudo ./netwatcher -t +SOCK Saddr Sport Daddr Dport MAC_TIME/μs IP_TIME/μs TRAN_TIME/μs RX/direction HTTP +0xffff939118a9ad00 192.168.60.136 36236 1.1.1.1 80 2 5 11 0 - +0xffff939118a9ad00 1.1.1.1 80 192.168.60.136 36236 6 10 111 1 - +0xffff93911049b600 192.168.60.136 36244 1.1.1.1 80 6 18 38 0 - +0xffff93911049b600 1.1.1.1 80 192.168.60.136 36244 14 17 263 1 - +0xffff93911049ad00 192.168.60.136 36246 1.1.1.1 80 2 4 16 0 - ``` -#### 3.2.1 各层处理时间 -`netwatcher`提供监控各个数据包在各层的处理时间的支持。为了获得这部分信息,用户需要指定`-t`参数,`netwatcher`会输出以`us`为单位的各层处理时间。 + +指定参数`-u`,查看UDP数据包处理时延并记录于`data/udp.log`中,单位为微妙。 + +```c +sudo ./netwatcher -u +Saddr Daddr Sprot Dprot udp_time/μs RX/direction len/byte +192.168.60.136 192.168.60.2 53643 53 2 0 39 +192.168.60.136 192.168.60.2 34272 53 3 0 42 +192.168.60.2 192.168.60.136 53 53643 12 1 230 +192.168.60.136 192.168.60.2 34442 53 1 0 40 +192.168.60.2 192.168.60.136 53 59996 2 1 190 ``` -sudo ./netwatcher -t -SOCK SEQ ACK MAC_TIME IP_TIME TCP_TIME RX HTTP -0xffff9d1ec43fd780 2018346083 420544002 1 3 9 0 - -0xffff9d1ec43fd780 420544002 2018346160 67 12 494 1 - -0xffff9d1ec43fd780 420545414 2018346160 40 5 258 1 - + +指定参数`-I`,查看ICMP数据包处理时延,单位为微妙。 + +```c +sudo ./netwatcher -I +Saddr Daddr icmp_time/μs RX/direction +192.168.60.136 192.168.60.136 11 1 +192.168.60.136 192.168.60.136 5 0 +192.168.60.136 192.168.60.136 80 1 +``` + +指定参数`-n`,查看数据包在Netfilter框架(包括PRE_ROUTING、LOCAL_IN、FORWARD、LOCAL_OUT、POST_ROUTING链中处理的时间),单位为微妙,其网络数据包路径为: + +- 发往本地:**NF_INET_PRE_ROUTING**-->**NF_INET_LOCAL_IN** +- 转发:**NF_INET_PRE_ROUTING**-->**NF_INET_FORWARD**-->**NF_INET_POST_ROUTING** +- 本地发出:**NF_INET_LOCAL_OUT**-->**NF_INET_POST_ROUTING** + +```c +sudo ./netwatcher -n +Saddr Daddr Sprot Dprot PreRT/μs L_IN/μs FW/μs PostRT/μs L_OUT/μs RX/direction +127.0.0.53 127.0.0.1 53 60590 3 2 0 0 0 1 +127.0.0.1 127.0.0.1 55858 40327 0 0 0 2 4 0 +127.0.0.1 127.0.0.1 55858 40327 1 1 0 0 0 1 +127.0.0.1 127.0.0.1 40327 55858 0 0 0 1 5 0 ``` #### 3.2.2 监控错误数据包 + `netwatcher`提供对TCP错误的监控支持,用户只需指定`-e`参数,`netwatcher`会记录SEQ错误或Checksum错误的TCP包并输出到标准输出以及`data/err.log`中。 -``` -sudo ./netwatcher -e + +```c +sudo ./netwatcher -e +packet{sock="0xffff13235ac8ac8e",seq="1318124482",ack="2468218244",reason="Invalid SEQ"} ``` #### 3.2.3 HTTP1/1.1 -`netwatcher`提供对HTTP1/1.1信息的监控,在指定`-i`参数后,`netwatcher`会从携带HTTP请求头的TCP包中将HTTP请求头提取并输出。 -``` +`netwatcher`提供对HTTP1/1.1信息的监控,在指定`-i`参数后,`netwatcher`会从携带HTTP请求头的TCP包中将HTTP请求头提取并输出,提取信息包括包含请求资源方式、状态码、状态文本等。 + +```c sudo ./netwatcher -i -SOCK SEQ ACK MAC_TIME IP_TIME TCP_TIME RX HTTP -0xffff9d1ecb3b9180 3705894662 522176002 - - - 0 GET / HTTP/1.1 -0xffff9d1ecb3b9180 522176002 3705894739 - - - 1 HTTP/1.1 200 OK +SOCK Saddr Sport Daddr Dport MAC_TIME/μs IP_TIME/μs TRAN_TIME/μs RX/direction HTTP +0xffff93911049d100 192.168.60.136 44152 1.1.1.1 80 0 0 0 0 0 - +0xffff93911049d100 1.1.1.1 80 192.168.60.136 44152 0 0 0 1 1 HTTP/1.1 301 Moved Permanently +0xffff9391601a6c00 192.168.60.136 44154 1.1.1.1 80 0 0 0 0 0 - +0xffff9391601a6c00 1.1.1.1 80 192.168.60.136 44154 0 0 0 1 1 HTTP/1.1 301 Moved Permanently ``` -### 3.3 与Prometheus连接进行可视化 -可以注意到,`data`中的所有文件都满足Prometheus要求的时序数据库格式。`netwatcher`使用`visual.py`在端口41420暴露`metrics`API为Prometheus提供可视化支持,当Prometheus请求此API时,会获得当前时刻下三个log文件的所有内容。由于三个log文件被eBPF程序实时更新,因此满足时序性。 +#### 3.2.3 过滤指定目的端口、源端口 + +```c +sudo ./netwatcher -d 80 或者 sudo ./netwatcher -s 80 +``` + +#### 3.2.4 TCP协议数据包连接状态 + +指定参数`-S`,对TCP数据包连接状态的监控。netwatcher会跟踪TCP连接状态的转换,其中可以对各个状态所持续的时间进行监测。 + +```C +sudo ./netwatcher -S +Saddr Daddr Sport Dport oldstate newstate time/μs +192.168.60.136 1.1.1.1 0 80 CLOSE SYN_SENT 0 +192.168.60.136 1.1.1.1 41312 80 SYN_SENT ESTABLISHED 181270 +192.168.60.136 1.1.1.1 41312 80 ESTABLISHED FIN_WAIT1 183729 +``` + +#### 3.2.5 捕捉丢包及原因 + +指定参数`-k`,捕捉丢包信息并获取其导致丢包的函数地址、丢包原因。 + +```c +sudo ./netwatcher -k +Time Saddr Daddr Sprot Dprot prot addr reason +13:44:03 1.1.1.1 192.168.60.136 80 49668 ipv4 ffffffff9c914464 SKB_DROP_REASON_NOT_SPECIFIED +13:44:03 1.1.1.1 192.168.60.136 80 49680 ipv4 ffffffff9c914464 SKB_DROP_REASON_NOT_SPECIFIED +``` + +指定参数`-k -T`,可以将虚拟地址转换成函数名+偏移的形式,在此处可以捕捉发生丢包的内核函数名称。 + +```C +sudo ./netwatcher -k -T +Time Saddr Daddr Sprot Dprot prot addr reason +13:44:22 1.1.1.1 192.168.60.136 80 37078 ipv4 tcp_v4_rcv+0x84 SKB_DROP_REASON_NOT_SPECIFIED +13:44:22 1.1.1.1 192.168.60.136 80 37092 ipv4 tcp_v4_rcv+0x84 SKB_DROP_REASON_NOT_SPECIFIED +13:44:24 1.1.1.1 192.168.60.136 80 37104 ipv4 tcp_v4_rcv+0x84 SKB_DROP_REASON_NOT_SPECIFIED +13:44:24 1.1.1.1 192.168.60.136 80 37118 ipv4 tcp_v4_rcv+0x84 SKB_DROP_REASON_NOT_SPECIFIED ``` + +#### 3.2.7 异常时延监控 + +对获取到的各层时延加上-L参数进行监控,捕获监测到的异常数据并给予警告信息。 + +```C +sudo ./netwatcher -t -L +SOCK Saddr Sport Daddr Dport MAC_TIME/μs IP_TIME/μs TRAN_TIME/μs RX/direction HTTP +0xffff939118a9c800 192.168.60.136 53788 1.1.1.1 80 2 4 20 0 - +0xffff939118a9c800 1.1.1.1 80 192.168.60.136 53788 14 22 345 1 - +0xffff9391601a7500 192.168.60.136 53790 1.1.1.1 80 3 11 31 0 - +0xffff9391601a7500 1.1.1.1 80 192.168.60.136 53790 22 37 170 1 - +0xffff9391107dec00 113.137.56.223 443 192.168.60.136 34104 11 10 1442 1 abnormal data +``` + +#### 3.2.8 DNS协议包监控 + +选择`udp_rcv`和`udp_send_skb`挂载捕获DNS收包和发包相关信息,从UDP头部开始分析并定位DNS数据部分获取其信息,头部信息存储在`query.header`,数据部分读取存储于`data`,可以获取到DNS协议包的事务ID、标志字段、问题部分计数、应答记录计数、授权记录计数、附加记录计数、域名等信息。 + +```C +sudo ./netwatcher -D +Saddr Daddr Id Flags Qd An Ns Ar Qr RX/direction +192.168.60.2 192.168.60.136 0x7894 0x8180 1 2 0 0 baidu.com 0 +127.0.0.53 0.0.0.0 0xc247 0x8180 1 2 0 1 baidu.com 1 +127.0.0.1 127.0.0.53 0x7637 0x120 1 0 0 1 contile.services.mozilla.com 1 +192.168.60.136 192.168.60.2 0x2c35 0x100 1 0 0 0 contile.services.mozilla.com 1 +``` + +#### 3.2.9 Mysql监控 + +利用uprobe和uretprobe挂载mysql-server层的命令分发处理函数`dispatch_command`,探测该函数获取进程pid、进程名comm、sql语句、sql执行耗时(μs)。 + +```C +sudo ./netwatcher -M +Pid Comm Size Sql duration/μs +1121 connection 32 select @@version_comment limit 1 295 +1121 connection 17 SELECT DATABASE() 277 +1121 connection 14 show databases 1361 +1121 connection 11 show tables 1080 +``` + +### 3.3 与Prometheus连接进行可视化 + +`data`目录下的所有文件都满足Prometheus要求的时序数据库格式。`netwatcher`使用`visual.py`在端口41420暴露`metrics`API为Prometheus提供可视化支持,当Prometheus请求此API时,会获得当前时刻所有log文件的全部内容。由于log文件被eBPF程序实时更新,因此满足时序性。 +```c python visual.py ``` ## 四、代码实现细节 -- 见`doc/implement.md` +- 详细实现细节见`doc/implement.md` diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/common.bpf.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/common.bpf.h index 8ea57deb1..73aa55f13 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/common.bpf.h +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/common.bpf.h @@ -28,43 +28,42 @@ #include #include -struct ktime_info { // us time stamp info发送数据包 +struct ktime_info { // us time stamp info发送数据包 u64 qdisc_time; // tx包离开mac层时间戳 u64 mac_time; // tx、rx包到达mac层时间戳 u64 ip_time; // tx、rx包到达ip层时间戳 // u64 tcp_time; // tx、rx包到达tcp层时间戳 - u64 tran_time; // tx、rx包到达传输层时间戳 - u64 app_time; // rx包离开tcp层时间戳 - void *sk; // 此包所属 socket套接字 + u64 tran_time; // tx、rx包到达传输层时间戳 + u64 app_time; // rx包离开tcp层时间戳 + void *sk; // 此包所属 socket套接字 u8 data[MAX_HTTP_HEADER]; // 用户层数据 }; struct packet_tuple { unsigned __int128 saddr_v6; // ipv6 源地址 unsigned __int128 daddr_v6; // ipv6 目的地址 - u32 saddr; // 源地址 - u32 daddr; // 目的地址 - u16 sport; // 源端口号 - u16 dport; // 目的端口号 - u32 seq; // seq报文序号 - u32 ack; // ack确认号 - u32 tran_flag; // 1:tcp 2:udp + u32 saddr; // 源地址 + u32 daddr; // 目的地址 + u16 sport; // 源端口号 + u16 dport; // 目的端口号 + u32 seq; // seq报文序号 + u32 ack; // ack确认号 + u32 tran_flag; // 1:tcp 2:udp u32 len; }; struct tcpstate { - u32 saddr; - u32 daddr; + u32 saddr; + u32 daddr; u16 sport; - u16 dport; + u16 dport; u16 family; - int oldstate; - int newstate; + int oldstate; + int newstate; u64 time; }; -enum -{ +enum { e_ip_rcv = 0, e_ip_local_deliver, e_ip_local_deliver_finish, @@ -74,18 +73,92 @@ enum e_ip_finish_output, e_ip_forward, nf_max -}nf_hook; +} nf_hook; + +enum { + PROTO_TCP = 0, + PROTO_UDP, + PROTO_ICMP, + PROTO_UNKNOWN, + PROTO_MAX, +}; -struct filtertime { +struct filtertime { struct packet_tuple init; struct packet_tuple done; u64 time[nf_max]; }; -struct ip_packet -{ - unsigned int saddr; // 源地址 - unsigned int daddr; // 目的地址 + +struct ip_packet { + unsigned int saddr; // 源地址 + unsigned int daddr; // 目的地址 +}; + +struct dns_header { + u16 id; // 事务ID + u16 flags; // 标志字段 + u16 qdcount; // 问题部分计数 + u16 ancount; // 应答记录计数 + u16 nscount; // 授权记录计数 + u16 arcount; // 附加记录计数 +}; + +struct dns_query { + struct dns_header header; // DNS头部 + char data[64]; // 可变长度数据(域名+类型+类) +}; + +struct dns { + u32 saddr; + u32 daddr; +}; + +struct query_info { + char msql[256]; + u32 size; + u64 start_time; +}; + +struct hist { + u64 slots[MAX_SLOTS]; + u64 latency; + u64 cnt; }; + +struct trace_event_raw_tcp_send_reset { + unsigned short common_type; + unsigned char common_flags; + unsigned char common_preempt_count; + int common_pid; + const void *skbaddr; + const void *skaddr; + int state; + __u16 sport; + __u16 dport; + __u16 family; + __u8 saddr[4]; + __u8 daddr[4]; + __u8 saddr_v6[16]; + __u8 daddr_v6[16]; +}; + +struct trace_event_raw_tcp_receive_reset { + unsigned short common_type; + unsigned char common_flags; + unsigned char common_preempt_count; + int common_pid; + const void *skaddr; + __u16 sport; + __u16 dport; + __u16 family; + __u8 saddr[4]; + __u8 daddr[4]; + __u8 saddr_v6[16]; + __u8 daddr_v6[16]; + __u64 sock_cookie; +}; +#define MAX_CONN 1000 +#define MAX_SLOTS 27 // 操作BPF映射的一个辅助函数 static __always_inline void * //__always_inline强制内联 bpf_map_lookup_or_try_init(void *map, const void *key, const void *init) { @@ -106,8 +179,6 @@ bpf_map_lookup_or_try_init(void *map, const void *key, const void *init) { char LICENSE[] SEC("license") = "Dual BSD/GPL"; -#define MAX_CONN 1000 - // 存储每个packet_tuple包所对应的ktime_info时间戳 struct { __uint(type, BPF_MAP_TYPE_LRU_HASH); @@ -122,6 +193,11 @@ struct { __uint(max_entries, 256 * 1024); } rb SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} rtt_rb SEC(".maps"); + struct { __uint(type, BPF_MAP_TYPE_RINGBUF); __uint(max_entries, 256 * 1024); @@ -132,6 +208,16 @@ struct { __uint(max_entries, 256 * 1024); } netfilter_rb SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} mysql_rb SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} redis_rb SEC(".maps"); + struct { __uint(type, BPF_MAP_TYPE_RINGBUF); __uint(max_entries, 256 * 1024); @@ -147,6 +233,31 @@ struct { __uint(max_entries, 256 * 1024); } tcp_rb SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} dns_rb SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} trace_rb SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} redis_stat_rb SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} events SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} port_rb SEC(".maps"); + // 存储每个tcp连接所对应的conn_t struct { __uint(type, BPF_MAP_TYPE_LRU_HASH); @@ -183,29 +294,113 @@ struct { __uint(max_entries, MAX_CONN *MAX_PACKET); __type(key, int); __type(value, struct packet_tuple); -} kfree SEC(".maps"); +} kfree SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_LRU_HASH); - __uint(max_entries, MAX_CONN * MAX_PACKET); + __uint(max_entries, MAX_CONN *MAX_PACKET); __type(key, struct ip_packet); - __type(value,unsigned long long); + __type(value, unsigned long long); } icmp_time SEC(".maps"); struct { - __uint(type, BPF_MAP_TYPE_HASH); - __uint(max_entries, 256*1024); - __type(key, struct sock *); - __type(value, __u64); + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 256 * 1024); + __type(key, struct sock *); + __type(value, __u64); } tcp_state SEC(".maps"); +// sql 耗时 +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 256 * 1024); + __type(key, __u32); + __type(value, __u64); +} mysql_time SEC(".maps"); + +// redis 耗时 +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 256 * 1024); + __type(key, __u32); + __type(value, struct redis_query); +} redis_time SEC(".maps"); + +// sql请求数 +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1024); + __type(key, __u32); + __type(value, __u64); +} sql_count SEC(".maps"); + +// dns计数根据每个saddr、daddr +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1024); + __type(key, struct dns); + __type(value, __u64); +} dns_request_count SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1024); + __type(key, struct dns); + __type(value, __u64); +} dns_response_count SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1024); + __type(key, __u32); + __type(value, struct query_info); +} queries SEC(".maps"); + +// 定义一个哈希映射,用于存储直方图数据 +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 256 * 1024); + __type(key, struct ip_packet); + __type(value, struct hist); +} hists SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, u32); + __type(value, u64); + __uint(max_entries, 1024); +} counters SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, MAX_COMM *MAX_PACKET); + __type(key, u32); + __type(value, struct packet_count); +} proto_stats SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, char*); // 键的最大长度,假设为 256 字节 + __type(value, u32); // 计数值 + __uint(max_entries, 1024); // 最大条目数 +} key_count SEC(".maps"); + const volatile int filter_dport = 0; const volatile int filter_sport = 0; const volatile int all_conn = 0, err_packet = 0, extra_conn_info = 0, - layer_time = 0, http_info = 0, retrans_info = 0, udp_info =0,net_filter = 0,kfree_info = 0,icmp_info = 0 ,tcp_info = 0; + layer_time = 0, http_info = 0, retrans_info = 0, + udp_info = 0, net_filter = 0, drop_reason = 0, icmp_info = 0, + tcp_info = 0, dns_info = 0, stack_info = 0, mysql_info = 0, + redis_info = 0, rtt_info = 0, rst_info = 0, + protocol_count = 0,redis_stat = 0;; /* help macro */ +#define FILTER \ + if (filter_dport && filter_dport != pkt_tuple.dport) \ + return 0; \ + if (filter_sport && filter_sport != pkt_tuple.sport) \ + return 0; + // 连接的目标端口是否匹配于filter_dport的值 #define FILTER_DPORT \ if (filter_dport) { \ @@ -275,7 +470,6 @@ const volatile int all_conn = 0, err_packet = 0, extra_conn_info = 0, #define CONN_INFO_TRANSFER tinfo->sk = conn->sock; // 将conn->sock赋给tinfo->sk - #define PACKET_INIT_WITH_COMMON_INFO \ struct pack_t *packet; \ packet = bpf_ringbuf_reserve(&rb, sizeof(*packet), 0); \ @@ -287,37 +481,51 @@ const volatile int all_conn = 0, err_packet = 0, extra_conn_info = 0, packet->ack = pkt_tuple.ack; \ packet->seq = pkt_tuple.seq; +#define READ_ONCE(x) (*(volatile typeof(x) *)&(x)) +#define WRITE_ONCE(x, val) ((*(volatile typeof(x) *)&(x)) = val) + +#define INIT_PACKET_TCP_TUPLE(sk, pkt) \ + struct packet_tuple pkt = { \ + .saddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr), \ + .daddr = BPF_CORE_READ(sk, __sk_common.skc_daddr), \ + .sport = BPF_CORE_READ(sk, __sk_common.skc_num), \ + .dport = __bpf_ntohs(BPF_CORE_READ(sk, __sk_common.skc_dport)), \ + .tran_flag = TCP} + +#define INIT_PACKET_UDP_TUPLE(sk, pkt) \ + struct packet_tuple pkt = { \ + .saddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr), \ + .daddr = BPF_CORE_READ(sk, __sk_common.skc_daddr), \ + .sport = BPF_CORE_READ(sk, __sk_common.skc_num), \ + .dport = __bpf_ntohs(BPF_CORE_READ(sk, __sk_common.skc_dport)), \ + .tran_flag = UDP} /* help macro end */ /* help functions */ // 将struct sock类型的指针转化为struct tcp_sock类型的指针 -static __always_inline -struct tcp_sock *tcp_sk(const struct sock *sk) { +static __always_inline struct tcp_sock *tcp_sk(const struct sock *sk) { return (struct tcp_sock *)sk; } // 将struct sk_buff类型的指针转化为struct udphdr类型的指针 -static __always_inline -struct udphdr *skb_to_udphdr(const struct sk_buff *skb) { +static __always_inline struct udphdr *skb_to_udphdr(const struct sk_buff *skb) { return (struct udphdr *)(( BPF_CORE_READ(skb, head) + // 报文头部偏移 BPF_CORE_READ(skb, transport_header))); // 传输层部分偏移 } // 将struct sk_buff类型的指针转化为struct tcphdr类型的指针 -static __always_inline -struct tcphdr *skb_to_tcphdr(const struct sk_buff *skb) { +static __always_inline struct tcphdr *skb_to_tcphdr(const struct sk_buff *skb) { return (struct tcphdr *)(( BPF_CORE_READ(skb, head) + // 报文头部偏移 BPF_CORE_READ(skb, transport_header))); // 传输层部分偏移 } // 将struct sk_buff类型的指针转化为struct iphdr类型的指针 -static __always_inline -struct iphdr *skb_to_iphdr(const struct sk_buff *skb) { +static __always_inline struct iphdr *skb_to_iphdr(const struct sk_buff *skb) { return (struct iphdr *)(BPF_CORE_READ(skb, head) + BPF_CORE_READ(skb, network_header)); } // 将struct sk_buff类型的指针转化为struct ipv6hdr类型的指针 -static __always_inline -struct ipv6hdr *skb_to_ipv6hdr(const struct sk_buff *skb) { +static __always_inline struct ipv6hdr * +skb_to_ipv6hdr(const struct sk_buff *skb) { return (struct ipv6hdr *)(BPF_CORE_READ(skb, head) + BPF_CORE_READ(skb, network_header)); } @@ -328,9 +536,9 @@ static void get_ip_pkt_tuple(struct ip_packet *ipk, struct iphdr *ip) { } // 初始化packet_tuple结构指针pkt_tuple -static __always_inline -void get_pkt_tuple(struct packet_tuple *pkt_tuple, struct iphdr *ip, - struct tcphdr *tcp) { +static __always_inline void get_pkt_tuple(struct packet_tuple *pkt_tuple, + struct iphdr *ip, + struct tcphdr *tcp) { pkt_tuple->saddr = BPF_CORE_READ(ip, saddr); pkt_tuple->daddr = BPF_CORE_READ(ip, daddr); u16 sport = BPF_CORE_READ(tcp, source); @@ -351,9 +559,9 @@ void get_pkt_tuple(struct packet_tuple *pkt_tuple, struct iphdr *ip, pkt_tuple->len = 0; } // 初始化packet_tuple结构指针pkt_tuple -static __always_inline -void get_udp_pkt_tuple(struct packet_tuple *pkt_tuple, struct iphdr *ip, - struct udphdr *udp) { +static __always_inline void get_udp_pkt_tuple(struct packet_tuple *pkt_tuple, + struct iphdr *ip, + struct udphdr *udp) { pkt_tuple->saddr = BPF_CORE_READ(ip, saddr); pkt_tuple->daddr = BPF_CORE_READ(ip, daddr); u16 sport = BPF_CORE_READ(udp, source); @@ -366,9 +574,9 @@ void get_udp_pkt_tuple(struct packet_tuple *pkt_tuple, struct iphdr *ip, pkt_tuple->tran_flag = UDP; // udp包 } -static __always_inline -void get_pkt_tuple_v6(struct packet_tuple *pkt_tuple, - struct ipv6hdr *ip6h, struct tcphdr *tcp) { +static __always_inline void get_pkt_tuple_v6(struct packet_tuple *pkt_tuple, + struct ipv6hdr *ip6h, + struct tcphdr *tcp) { bpf_probe_read_kernel(&pkt_tuple->saddr_v6, sizeof(pkt_tuple->saddr_v6), &ip6h->saddr.in6_u.u6_addr32); bpf_probe_read_kernel(&pkt_tuple->daddr_v6, sizeof(pkt_tuple->daddr_v6), @@ -384,8 +592,79 @@ void get_pkt_tuple_v6(struct packet_tuple *pkt_tuple, pkt_tuple->tran_flag = 1; // tcp包 } -/* help functions end */ +int getstack(void *ctx) { + int pid = bpf_get_current_pid_tgid() >> 32; + int cpu_id = bpf_get_smp_processor_id(); + struct stacktrace_event *event; + int cp; + + event = bpf_ringbuf_reserve(&trace_rb, sizeof(*event), 0); + if (!event) + return 1; + + event->pid = pid; + event->cpu_id = cpu_id; + + if (bpf_get_current_comm(event->comm, sizeof(event->comm))) + event->comm[0] = 0; + event->kstack_sz = + bpf_get_stack(ctx, event->kstack, sizeof(event->kstack), 0); + bpf_ringbuf_submit(event, 0); + return 0; +} +#if KERNEL_VERSION(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH) >= \ + KERNEL_VERSION(6, 3, 1) +#define GET_USER_DATA(msg) BPF_CORE_READ(msg, msg_iter.__iov, iov_base) +#else +#define GET_USER_DATA(msg) BPF_CORE_READ(msg, msg_iter.iov, iov_base) +#endif + +/* +例子: log2(16384) =14 +16384 2进制表示 1000000000000000 +初始值: v=16384 r=0 +1、16384 > 65535 不成立,r=0; v右移动0位 +2、16384 > 255 成立,shift = 8,v右移动8位100000000,r=0|8=8 +3、256 > 15 成立,shift = 4,v右移4位10000,r=8|4=12 +4、16 > 3 成立,shift = 2,右移2位100,r=12|2=14 +5、v=4,右移1位10,r|=2>>1=1 r=14|1=14 +*/ + +static __always_inline u64 log2(u32 v) { + u32 shift, r; + //检测v是否大于0xFFFF(65535),如果是,则将r设置为16 + r = (v > 0xFFFF) << 4; + v >>= r; //右移 + shift = (v > 0xFF) << 3; + v >>= shift; + r |= shift; + shift = (v > 0xF) << 2; + v >>= shift; + r |= shift; + shift = (v > 0x3) << 1; + v >>= shift; + r |= shift; + //右移v一位并将结果累加到r中 + r |= (v >> 1); + return r; +} +/* +例子:log2l(4294967296)=32 +4294967296 2进制表示 100000000000000000000000000000000 +1、v右移32位 1 +2、log2(1)=0 计算得0+32=32 +*/ +static __always_inline u64 log2l(u64 v) { + u32 hi = v >> 32; //取v的高32位 + // 如果高32位非0,计算高32位的对数并加32 + if (hi) + return log2(hi) + 32; + else + return log2(v); +} + +/* help functions end */ -#endif \ No newline at end of file +#endif diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/data/netfilter.log b/eBPF_Supermarket/Network_Subsystem/net_watcher/data/netfilter.log new file mode 100644 index 000000000..e69de29bb diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/doc/image/process.png b/eBPF_Supermarket/Network_Subsystem/net_watcher/doc/image/process.png index 831a6373c..459a2e15a 100644 Binary files a/eBPF_Supermarket/Network_Subsystem/net_watcher/doc/image/process.png and b/eBPF_Supermarket/Network_Subsystem/net_watcher/doc/image/process.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/doc/implement.md b/eBPF_Supermarket/Network_Subsystem/net_watcher/doc/implement.md index 24785b5f0..b740c065b 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/doc/implement.md +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/doc/implement.md @@ -153,3 +153,4 @@ printf("[X] invalid checksum: sock = %p\n", pack_info->sock); fprintf(file, "error{sock=\"%p\",seq=\"%u\",ack=\"%u\",reason=\"%s\"} 0\n", pack_info->sock, pack_info->seq, pack_info->ack, reason); ``` +fprintf(file, "connection{pid=\"%d\",sock=\"%p\",src=\"%s\",dst=\"%s\",is_server=\"%d\"", \ No newline at end of file diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/drop.bpf.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/drop.bpf.h index 1eb219c06..8c7883c17 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/drop.bpf.h +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/drop.bpf.h @@ -13,12 +13,13 @@ // limitations under the License. // // author: blown.away@qq.com +// netwatcher libbpf 丢包 #include "common.bpf.h" static __always_inline int __tp_kfree(struct trace_event_raw_kfree_skb *ctx) { - if(!kfree_info) + if(!drop_reason) return 0; struct sk_buff *skb=ctx->skbaddr; if (skb == NULL) // 判断是否为空 @@ -41,5 +42,7 @@ int __tp_kfree(struct trace_event_raw_kfree_skb *ctx) message->location = (long)ctx->location; message->drop_reason = ctx->reason; bpf_ringbuf_submit(message,0); + if(stack_info) + getstack(ctx); return 0; -} \ No newline at end of file +} \ No newline at end of file diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/dropreason.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/dropreason.h index 68ff87eaa..3c172c63f 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/dropreason.h +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/dropreason.h @@ -1,3 +1,20 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: blown.away@qq.com +// netwatcher libbpf 丢包原因 + #ifndef __DROPREASON_H #define __DROPREASON_H const char *SKB_Drop_Reason_Strings[] = { diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/icmp.bpf.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/icmp.bpf.h index 96c8a37ed..8ba117c49 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/icmp.bpf.h +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/icmp.bpf.h @@ -13,6 +13,7 @@ // limitations under the License. // // author: blown.away@qq.com +// netwatcher libbpf icmp #include "common.bpf.h" @@ -26,7 +27,6 @@ int __icmp_time(struct sk_buff *skb) get_ip_pkt_tuple(&ipk, ip); unsigned long long time= bpf_ktime_get_ns() / 1000; bpf_map_update_elem(&icmp_time, &ipk, &time, BPF_ANY); - return 0; } @@ -46,7 +46,6 @@ int __rcvend_icmp_time(struct sk_buff *skb) unsigned long long new_time= bpf_ktime_get_ns() / 1000; unsigned long long time=new_time-*pre_time; - //bpf_printk("%d %d %d",ip->saddr,ip->daddr,time); struct icmptime *message; message = bpf_ringbuf_reserve(&icmp_rb, sizeof(*message), 0); if(!message){ @@ -76,7 +75,6 @@ int __reply_icmp_time(struct sk_buff *skb) return 0; unsigned long long new_time= bpf_ktime_get_ns() / 1000; unsigned long long time=new_time-*pre_time; - //bpf_printk("%d %d %d",ip->saddr,ip->daddr,time); struct icmptime *message; message = bpf_ringbuf_reserve(&icmp_rb, sizeof(*message), 0); if(!message){ diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/mysql.bpf.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/mysql.bpf.h new file mode 100644 index 000000000..13e802887 --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/mysql.bpf.h @@ -0,0 +1,78 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: blown.away@qq.com +// mysql + +#include "common.bpf.h" +#include "mysql_helper.bpf.h" +static __always_inline int __handle_mysql_start(struct pt_regs *ctx) { + // dispatch_command(THD *thd, const COM_DATA *com_data, enum + enum enum_server_command command = PT_REGS_PARM3(ctx); + union COM_DATA *com_data = (union COM_DATA *)PT_REGS_PARM2(ctx); + pid_t pid = bpf_get_current_pid_tgid() >> 32; + pid_t tid = bpf_get_current_pid_tgid(); + void *thd = (void *)PT_REGS_PARM1(ctx); + struct query_info info; + u32 size = 0; + char *sql; + + if (command != COM_QUERY) { + return 0; + } + + bpf_probe_read(&info.size, sizeof(info.size), &com_data->com_query.length); + bpf_probe_read_str(&sql, sizeof(sql), &com_data->com_query.query); + bpf_probe_read_str(&info.msql, sizeof(info.msql), sql); + // bpf_printk("sql1==%s size1==%lu", info.msql,info.size); + info.start_time = bpf_ktime_get_ns() / 1000; + + bpf_map_update_elem(&queries, &tid, &info, BPF_ANY); + return 0; +} + +static __always_inline int __handle_mysql_end(struct pt_regs *ctx) { + char comm[16]; + pid_t pid = bpf_get_current_pid_tgid() >> 32; + pid_t tid = bpf_get_current_pid_tgid(); + struct query_info *info = bpf_map_lookup_elem(&queries, &tid); + if (!info) { + return 0; + } + + struct mysql_query *message = + bpf_ringbuf_reserve(&mysql_rb, sizeof(*message), 0); + if (!message) { + return 0; + } + u64 *count_ptr, count = 1; + count_ptr = bpf_map_lookup_elem(&sql_count, &tid); + if (count_ptr) { + count = *count_ptr + 1; + } + + message->count = count; + bpf_map_update_elem(&sql_count, &tid, &count, BPF_ANY); + message->duratime = bpf_ktime_get_ns() / 1000 - info->start_time; + message->pid = pid; + message->tid = tid; + bpf_get_current_comm(&message->comm, sizeof(comm)); + message->size = info->size; + bpf_probe_read_str(&message->msql, sizeof(message->msql), info->msql); + // bpf_printk("C==%d D==%lu S==%lu SQL==%s",count, + // message->duratime,message->size,message->msql); + + bpf_ringbuf_submit(message, 0); + return 0; +} diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/mysql_helper.bpf.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/mysql_helper.bpf.h new file mode 100644 index 000000000..222764e1c --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/mysql_helper.bpf.h @@ -0,0 +1,171 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: blown.away@qq.com +// +// netwatcher libbpf 内核<->用户 传递信息相关结构体 + +#ifndef __MYSQL_HELPER_BPF_H +#define __MYSQL_HELPER_BPF_H + +#include "netwatcher.h" +#include "vmlinux.h" +#include +#include +#include +#include +#include +#include + +enum enum_server_command { + COM_SLEEP, + COM_QUIT, + COM_INIT_DB, + COM_QUERY, + COM_FIELD_LIST, + COM_CREATE_DB, + COM_DROP_DB, + COM_REFRESH, + COM_SHUTDOWN, + COM_STATISTICS, + COM_PROCESS_INFO, + COM_CONNECT, + COM_PROCESS_KILL, + COM_DEBUG, + COM_PING, + COM_TIME, + COM_DELAYED_INSERT, + COM_CHANGE_USER, + COM_BINLOG_DUMP, + COM_TABLE_DUMP, + COM_CONNECT_OUT, + COM_REGISTER_SLAVE, + COM_STMT_PREPARE, + COM_STMT_EXECUTE, + COM_STMT_SEND_LONG_DATA, + COM_STMT_CLOSE, + COM_STMT_RESET, + COM_SET_OPTION, + COM_STMT_FETCH, + COM_DAEMON, + COM_BINLOG_DUMP_GTID, + COM_RESET_CONNECTION, + /* don't forget to update const char *command_name[] in sql_parse.cc */ + /* Must be last */ + COM_END +}; + +typedef struct st_com_init_db_data { + const char *db_name; + unsigned long length; +} COM_INIT_DB_DATA; + +#define MYSQL_SHUTDOWN_KILLABLE_CONNECT (unsigned char)(1 << 0) +#define MYSQL_SHUTDOWN_KILLABLE_TRANS (unsigned char)(1 << 1) +#define MYSQL_SHUTDOWN_KILLABLE_LOCK_TABLE (unsigned char)(1 << 2) +#define MYSQL_SHUTDOWN_KILLABLE_UPDATE (unsigned char)(1 << 3) + +#define LOCK_MODE_MASK 0xFUL +#define LOCK_TYPE_MASK 0xF0UL + +enum mysql_enum_shutdown_level { + SHUTDOWN_DEFAULT = 0, + SHUTDOWN_WAIT_CONNECTIONS = MYSQL_SHUTDOWN_KILLABLE_CONNECT, + SHUTDOWN_WAIT_TRANSACTIONS = MYSQL_SHUTDOWN_KILLABLE_TRANS, + SHUTDOWN_WAIT_UPDATES = MYSQL_SHUTDOWN_KILLABLE_UPDATE, + SHUTDOWN_WAIT_ALL_BUFFERS = (MYSQL_SHUTDOWN_KILLABLE_UPDATE << 1), + SHUTDOWN_WAIT_CRITICAL_BUFFERS = (MYSQL_SHUTDOWN_KILLABLE_UPDATE << 1) + 1, + KILL_QUERY = 254, + KILL_CONNECTION = 255 +}; + +typedef struct st_com_refresh_data { + unsigned char options; +} COM_REFRESH_DATA; + +typedef struct st_com_shutdown_data { + enum mysql_enum_shutdown_level level; +} COM_SHUTDOWN_DATA; + +typedef struct st_com_kill_data { + unsigned long id; +} COM_KILL_DATA; + +typedef struct st_com_set_option_data { + unsigned int opt_command; +} COM_SET_OPTION_DATA; + +typedef struct st_com_stmt_execute_data { + unsigned long stmt_id; + unsigned long flags; + unsigned char *params; + unsigned long params_length; +} COM_STMT_EXECUTE_DATA; + +typedef struct st_com_stmt_fetch_data { + unsigned long stmt_id; + unsigned long num_rows; +} COM_STMT_FETCH_DATA; + +typedef struct st_com_stmt_send_long_data_data { + unsigned long stmt_id; + unsigned int param_number; + unsigned char *longdata; + unsigned long length; +} COM_STMT_SEND_LONG_DATA_DATA; + +typedef struct st_com_stmt_prepare_data { + const char *query; + unsigned int length; +} COM_STMT_PREPARE_DATA; + +typedef struct st_stmt_close_data { + unsigned int stmt_id; +} COM_STMT_CLOSE_DATA; + +typedef struct st_com_stmt_reset_data { + unsigned int stmt_id; +} COM_STMT_RESET_DATA; + +typedef struct st_com_query_data { + const char *query; + unsigned int length; +} COM_QUERY_DATA; + +typedef struct st_com_field_list_data { + unsigned char *table_name; + unsigned int table_name_length; + const unsigned char *query; + unsigned int query_length; +} COM_FIELD_LIST_DATA; + +union COM_DATA { + COM_INIT_DB_DATA com_init_db; + COM_REFRESH_DATA com_refresh; + COM_SHUTDOWN_DATA com_shutdown; + COM_KILL_DATA com_kill; + COM_SET_OPTION_DATA com_set_option; + COM_STMT_EXECUTE_DATA com_stmt_execute; + COM_STMT_FETCH_DATA com_stmt_fetch; + COM_STMT_SEND_LONG_DATA_DATA com_stmt_send_long_data; + COM_STMT_PREPARE_DATA com_stmt_prepare; + COM_STMT_CLOSE_DATA com_stmt_close; + COM_STMT_RESET_DATA com_stmt_reset; + COM_QUERY_DATA com_query; + COM_FIELD_LIST_DATA com_field_list; +}; + +/* help functions end */ + +#endif diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/netfilter.bpf.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/netfilter.bpf.h index ad0c8cd21..5e92a025c 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/netfilter.bpf.h +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/netfilter.bpf.h @@ -13,14 +13,16 @@ // limitations under the License. // // author: blown.away@qq.com +// netwatcher libbpf netfilter #include "common.bpf.h" static __always_inline int submit_nf_time(struct packet_tuple pkt_tuple, struct filtertime *tinfo, int rx) { + int time =0; struct netfilter *message; - + FILTER message = bpf_ringbuf_reserve(&netfilter_rb, sizeof(*message), 0); if(!message){ return 0; @@ -34,6 +36,7 @@ int submit_nf_time(struct packet_tuple pkt_tuple, struct filtertime *tinfo, int message->pre_routing_time = 0; message->local_out_time = 0; message->post_routing_time = 0; + message->forward_time=0; message->rx = rx; //收/发/转发方向 if(rx == 1){ @@ -44,20 +47,31 @@ int submit_nf_time(struct packet_tuple pkt_tuple, struct filtertime *tinfo, int message->local_input_time = tinfo->time[e_ip_local_deliver_finish] - tinfo->time[e_ip_local_deliver]; message->pre_routing_time = tinfo->time[e_ip_local_deliver] - - tinfo->time[e_ip_rcv]; + tinfo->time[e_ip_rcv]; if((int)message->local_input_time < 0 || (int)message->pre_routing_time < 0){ bpf_ringbuf_discard(message, 0); return 0; } } }else{ - if(tinfo->time[e_ip_forward] && tinfo->time[e_ip_output]) + if(tinfo->time[e_ip_local_deliver_finish] && + tinfo->time[e_ip_local_deliver] && + tinfo->time[e_ip_rcv] && + tinfo->time[e_ip_forward] && + tinfo->time[e_ip_output]) { - message->forward_time = tinfo->time[e_ip_output] - tinfo->time[e_ip_forward]; - if((int)message->forward_time < 0){ + message->local_input_time = tinfo->time[e_ip_local_deliver_finish] - + tinfo->time[e_ip_local_deliver]; + message->pre_routing_time = tinfo->time[e_ip_local_deliver] - + tinfo->time[e_ip_rcv]; + + u64 forward_time = tinfo->time[e_ip_output] - tinfo->time[e_ip_forward]; + + if((int)forward_time < 0){ bpf_ringbuf_discard(message, 0); return 0; } + message->forward_time = forward_time; message->rx = 2; } if(tinfo->time[e_ip_output] && @@ -102,9 +116,7 @@ int store_nf_time(struct sk_buff *skb, int hook) return 0; } } - tinfo->time[hook] = bpf_ktime_get_ns() / 1000; - if(hook == e_ip_local_deliver_finish){ submit_nf_time(tinfo->init, tinfo, 1); bpf_map_delete_elem(&netfilter_time, &skb); diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.bpf.c b/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.bpf.c index a41f72e13..5db4a8064 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.bpf.c +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.bpf.c @@ -22,23 +22,26 @@ #include "icmp.bpf.h" - #include "tcp.bpf.h" #include "packet.bpf.h" #include "udp.bpf.h" +#include "mysql.bpf.h" + +#include "redis.bpf.h" + #include "drop.bpf.h" -//accecpt an TCP connection +// accecpt an TCP connection SEC("kretprobe/inet_csk_accept") int BPF_KRETPROBE(inet_csk_accept_exit, // 接受tcp连接 struct sock *sk) { // this func return a newsk return __inet_csk_accept(sk); } -//connect an TCP connection +// connect an TCP connection SEC("kprobe/tcp_v4_connect") // 进入tcp_v4_connect int BPF_KPROBE(tcp_v4_connect, const struct sock *sk) { return __tcp_v4_connect(sk); @@ -59,10 +62,10 @@ int BPF_KRETPROBE(tcp_v6_connect_exit, int ret) { return __tcp_v6_connect_exit(ret); } -// erase CLOSED TCP connection +// erase CLOSED TCP connection SEC("kprobe/tcp_set_state") int BPF_KPROBE(tcp_set_state, struct sock *sk, int state) { - return __tcp_set_state(sk,state); + return __tcp_set_state(sk, state); } /*! @@ -98,14 +101,16 @@ int BPF_KPROBE(tcp_set_state, struct sock *sk, int state) { /** in ipv4 && ipv6 */ SEC("kprobe/eth_type_trans") // 进入eth_type_trans int BPF_KPROBE(eth_type_trans, struct sk_buff *skb) { - return __eth_type_trans(skb); + if (protocol_count) { + return sum_protocol(skb, false); // receive + } else { + return __eth_type_trans(skb); + } } /** in only ipv4 */ SEC("kprobe/ip_rcv_core") // 跟踪记录ipv4数据包在内核中的处理时间 -int BPF_KPROBE(ip_rcv_core, struct sk_buff *skb) { - return __ip_rcv_core(skb); -} +int BPF_KPROBE(ip_rcv_core, struct sk_buff *skb) { return __ip_rcv_core(skb); } /** in only ipv6 */ SEC("kprobe/ip6_rcv_core") int BPF_KPROBE(ip6_rcv_core, struct sk_buff *skb) { @@ -114,24 +119,20 @@ int BPF_KPROBE(ip6_rcv_core, struct sk_buff *skb) { /**in only ipv4 */ // 接收数据包 SEC("kprobe/tcp_v4_rcv") // 记录数据包在tcpv4层时间戳 -int BPF_KPROBE(tcp_v4_rcv, struct sk_buff *skb) { - return __tcp_v4_rcv(skb); -} +int BPF_KPROBE(tcp_v4_rcv, struct sk_buff *skb) { return __tcp_v4_rcv(skb); } /** in only ipv6 */ SEC("kprobe/tcp_v6_rcv") // 接收tcpv6数据包 -int BPF_KPROBE(tcp_v6_rcv, struct sk_buff *skb) { - return __tcp_v6_rcv(skb); -} +int BPF_KPROBE(tcp_v6_rcv, struct sk_buff *skb) { return __tcp_v6_rcv(skb); } // v4 & v6 do_rcv to get sk and other info SEC("kprobe/tcp_v4_do_rcv") int BPF_KPROBE(tcp_v4_do_rcv, struct sock *sk, struct sk_buff *skb) { - return __tcp_v4_do_rcv(sk,skb); + return __tcp_v4_do_rcv(sk, skb); } SEC("kprobe/tcp_v6_do_rcv") // tcp层包时间 int BPF_KPROBE(tcp_v6_do_rcv, struct sock *sk, struct sk_buff *skb) { - return __tcp_v6_do_rcv(sk,skb); + return __tcp_v6_do_rcv(sk, skb); } /** in ipv4 && ipv6 */ @@ -140,23 +141,21 @@ int BPF_KPROBE(skb_copy_datagram_iter, struct sk_buff *skb) { return __skb_copy_datagram_iter(skb); } - // receive error packet /* TCP invalid seq error */ // 根据传入的数据包提取关键信息(如IP和TCP头部信息),并将这些信息与其他元数据(如套接字信息和错误标识)一同存储到BPF // ring buffer中 SEC("kprobe/tcp_validate_incoming") // 验证传入数据包的序列号 int BPF_KPROBE(tcp_validate_incoming, struct sock *sk, struct sk_buff *skb) { - return __tcp_validate_incoming(sk,skb); + return __tcp_validate_incoming(sk, skb); } // 跟踪网络数据包检测tcp检验和错误 /* TCP invalid checksum error*/ SEC("kretprobe/__skb_checksum_complete") int BPF_KRETPROBE(__skb_checksum_complete_exit, int ret) { - return skb_checksum_complete(ret); + return skb_checksum_complete(ret); } - /**** send path ****/ /*! * \brief: 获取数据包进入TCP层时刻的时间戳, 发送tcp层起始点 @@ -164,7 +163,7 @@ int BPF_KRETPROBE(__skb_checksum_complete_exit, int ret) { */ SEC("kprobe/tcp_sendmsg") // 跟踪tcp发送包信息 int BPF_KPROBE(tcp_sendmsg, struct sock *sk, struct msghdr *msg, size_t size) { - return __tcp_sendmsg(sk,msg,size); + return __tcp_sendmsg(sk, msg, size); } /*! @@ -174,7 +173,7 @@ tcp)获取ip段的数据 out only ipv4 */ SEC("kprobe/ip_queue_xmit") int BPF_KPROBE(ip_queue_xmit, struct sock *sk, struct sk_buff *skb) { - return __ip_queue_xmit(sk,skb); + return __ip_queue_xmit(sk, skb); }; /*! @@ -184,7 +183,7 @@ tcp)获取ip段的数据 out only ipv6 */ SEC("kprobe/inet6_csk_xmit") int BPF_KPROBE(inet6_csk_xmit, struct sock *sk, struct sk_buff *skb) { - return __inet6_csk_xmit(sk,skb); + return __inet6_csk_xmit(sk, skb); }; /*! @@ -202,11 +201,14 @@ int BPF_KPROBE(__dev_queue_xmit, struct sk_buff *skb) { */ SEC("kprobe/dev_hard_start_xmit") int BPF_KPROBE(dev_hard_start_xmit, struct sk_buff *skb) { - return __dev_hard_start_xmit(skb); + if (protocol_count) { + return sum_protocol(skb, true); // send + } else { + return __dev_hard_start_xmit(skb); + } }; - -//retrans +// retrans /* 在进入快速恢复阶段时,不管是基于Reno或者SACK的快速恢复, * 还是RACK触发的快速恢复,都将使用函数tcp_enter_recovery进入 * TCP_CA_Recovery拥塞阶段。 @@ -222,92 +224,137 @@ int BPF_KPROBE(tcp_enter_recovery, struct sock *sk) { * 在报文的重传定时器到期时,在tcp_retransmit_timer函数中,进入TCP_CA_Loss拥塞状态。 */ SEC("kprobe/tcp_enter_loss") -int BPF_KPROBE(tcp_enter_loss, struct sock *sk) { - return __tcp_enter_loss(sk); -} +int BPF_KPROBE(tcp_enter_loss, struct sock *sk) { return __tcp_enter_loss(sk); } /* udp */ SEC("kprobe/udp_rcv") int BPF_KPROBE(udp_rcv, struct sk_buff *skb) { - return __udp_rcv(skb); + if (udp_info) + return __udp_rcv(skb); + else if (dns_info) + return __dns_rcv(skb); + else + return 0; } SEC("kprobe/__udp_enqueue_schedule_skb") -int BPF_KPROBE(__udp_enqueue_schedule_skb, struct sock *sk,struct sk_buff *skb) { - return udp_enqueue_schedule_skb(sk,skb); +int BPF_KPROBE(__udp_enqueue_schedule_skb, struct sock *sk, + struct sk_buff *skb) { + return udp_enqueue_schedule_skb(sk, skb); } SEC("kprobe/udp_send_skb") int BPF_KPROBE(udp_send_skb, struct sk_buff *skb) { - return __udp_send_skb(skb); + if (udp_info) + return __udp_send_skb(skb); + else if (dns_info) + return __dns_send(skb); + else + return 0; } SEC("kprobe/ip_send_skb") -int BPF_KPROBE(ip_send_skb,struct net *net, struct sk_buff *skb) { +int BPF_KPROBE(ip_send_skb, struct net *net, struct sk_buff *skb) { return __ip_send_skb(skb); } -//netfilter +// netfilter SEC("kprobe/ip_rcv") -int BPF_KPROBE(ip_rcv, struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, - struct net_device *orig_dev) { +int BPF_KPROBE(ip_rcv, struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) { return store_nf_time(skb, e_ip_rcv); } SEC("kprobe/ip_local_deliver") -int BPF_KPROBE(ip_local_deliver,struct sk_buff *skb) { +int BPF_KPROBE(ip_local_deliver, struct sk_buff *skb) { return store_nf_time(skb, e_ip_local_deliver); } SEC("kprobe/ip_local_deliver_finish") -int BPF_KPROBE(ip_local_deliver_finish,struct net *net, struct sock *sk, struct sk_buff *skb) { +int BPF_KPROBE(ip_local_deliver_finish, struct net *net, struct sock *sk, + struct sk_buff *skb) { return store_nf_time(skb, e_ip_local_deliver_finish); } SEC("kprobe/ip_local_out") -int BPF_KPROBE(ip_local_out, struct net *net, struct sock *sk, struct sk_buff *skb) { +int BPF_KPROBE(ip_local_out, struct net *net, struct sock *sk, + struct sk_buff *skb) { return store_nf_time(skb, e_ip_local_out); } SEC("kprobe/ip_output") -int BPF_KPROBE(ip_output,struct net *net, struct sock *sk, struct sk_buff *skb) { +int BPF_KPROBE(ip_output, struct net *net, struct sock *sk, + struct sk_buff *skb) { return store_nf_time(skb, e_ip_output); } SEC("kprobe/__ip_finish_output") -int BPF_KPROBE(__ip_finish_output,struct net *net, struct sock *sk, struct sk_buff *skb) { +int BPF_KPROBE(__ip_finish_output, struct net *net, struct sock *sk, + struct sk_buff *skb) { return store_nf_time(skb, e_ip_finish_output); } SEC("kprobe/ip_forward") -int BPF_KPROBE(ip_forward,struct sk_buff *skb) { +int BPF_KPROBE(ip_forward, struct sk_buff *skb) { return store_nf_time(skb, e_ip_forward); } -//drop +// drop SEC("tp/skb/kfree_skb") -int tp_kfree(struct trace_event_raw_kfree_skb *ctx) { - return __tp_kfree(ctx); -} +int tp_kfree(struct trace_event_raw_kfree_skb *ctx) { return __tp_kfree(ctx); } SEC("kprobe/icmp_rcv") -int BPF_KPROBE(icmp_rcv,struct sk_buff *skb) { - return __icmp_time(skb); -} +int BPF_KPROBE(icmp_rcv, struct sk_buff *skb) { return __icmp_time(skb); } SEC("kprobe/__sock_queue_rcv_skb") -int BPF_KPROBE(__sock_queue_rcv_skb,struct sock *sk, struct sk_buff *skb) { +int BPF_KPROBE(__sock_queue_rcv_skb, struct sock *sk, struct sk_buff *skb) { return __rcvend_icmp_time(skb); } SEC("kprobe/icmp_reply") -int BPF_KPROBE(icmp_reply,struct icmp_bxm *icmp_param, struct sk_buff *skb) { +int BPF_KPROBE(icmp_reply, struct icmp_bxm *icmp_param, struct sk_buff *skb) { return __reply_icmp_time(skb); } -//tcpstate -SEC("tracepoint/sock/inet_sock_set_state") -int handle_set_state(struct trace_event_raw_inet_sock_set_state *ctx) -{ +// mysql +SEC("uprobe/_Z16dispatch_commandP3THDPK8COM_DATA19enum_server_command") +int BPF_KPROBE(query__start) { return __handle_mysql_start(ctx); } + +SEC("uretprobe/_Z16dispatch_commandP3THDPK8COM_DATA19enum_server_command") +int BPF_KPROBE(query__end) { return __handle_mysql_end(ctx); } + +//redis +SEC("uprobe/processCommand") +int BPF_KPROBE(redis_processCommand) { return __handle_redis_start(ctx); } +SEC("uretprobe/call") +int BPF_KPROBE(redis_call) { return __handle_redis_end(ctx); } + +SEC("uprobe/lookupKey") +int BPF_UPROBE(redis_lookupKey) { + return __handle_redis_key(ctx); +} +SEC("uprobe/addReply") +int BPF_UPROBE(redis_addReply) { + return __handle_redis_value(ctx); +} +// rtt +SEC("kprobe/tcp_rcv_established") +int BPF_KPROBE(tcp_rcv_established, struct sock *sk, struct sk_buff *skb) { + return __tcp_rcv_established(sk, skb); +} + +// tcpstate +SEC("tracepoint/sock/inet_sock_set_state") +int handle_set_state(struct trace_event_raw_inet_sock_set_state *ctx) { return __handle_set_state(ctx); } +// RST +SEC("tracepoint/tcp/tcp_send_reset") +int handle_send_reset(struct trace_event_raw_tcp_send_reset *ctx) { + return __handle_send_reset(ctx); +} + +SEC("tracepoint/tcp/tcp_receive_reset") +int handle_receive_reset(struct trace_event_raw_tcp_receive_reset *ctx) { + return __handle_receive_reset(ctx); +} \ No newline at end of file diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.c b/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.c index c5fca5b48..393fca711 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.c +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.c @@ -17,6 +17,7 @@ // netwatcher libbpf 用户态代码 #include "netwatcher.h" +#include "dropreason.h" #include "netwatcher.skel.h" #include #include @@ -28,30 +29,40 @@ #include #include #include +#include +#include #include -#include "dropreason.h" static volatile bool exiting = false; - +struct packet_count proto_stats[256] = {0}; +static u64 rst_count = 0; +static struct reset_event_t event_store[MAX_EVENTS]; +static int event_count = 0; static char connects_file_path[1024]; static char err_file_path[1024]; static char packets_file_path[1024]; static char udp_file_path[1024]; +static char binary_path[64] = ""; +int num_symbols = 0; +int cache_size = 0; + +// 用于存储从 eBPF map 读取的数据 +typedef struct { + char key[256]; + u32 value; +} kv_pair; + +static int map_fd; static int sport = 0, dport = 0; // for filter static int all_conn = 0, err_packet = 0, extra_conn_info = 0, layer_time = 0, - http_info = 0, retrans_info = 0, udp_info = 0,net_filter = 0,kfree_info = 0,addr_to_func=0 ,icmp_info = 0 , tcp_info = 0; // flag - -static const char* tcp_states[] = { - [1] = "ESTABLISHED", [2] = "SYN_SENT", [3] = "SYN_RECV", - [4] = "FIN_WAIT1", [5] = "FIN_WAIT2", [6] = "TIME_WAIT", - [7] = "CLOSE", [8] = "CLOSE_WAIT", [9] = "LAST_ACK", - [10] = "LISTEN", [11] = "CLOSING", [12] = "NEW_SYN_RECV", - [13] = "UNKNOWN", -}; + http_info = 0, retrans_info = 0, udp_info = 0, net_filter = 0, + drop_reason = 0, addr_to_func = 0, icmp_info = 0, tcp_info = 0, + time_load = 0, dns_info = 0, stack_info = 0, mysql_info = 0, + redis_info = 0, count_info = 0, rtt_info = 0, rst_info = 0, + protocol_count = 0,redis_stat = 0; // flag static const char argp_program_doc[] = "Watch tcp/ip in network subsystem \n"; - static const struct argp_option opts[] = { {"all", 'a', 0, 0, "set to trace CLOSED connection"}, {"err", 'e', 0, 0, "set to trace TCP error packets"}, @@ -62,11 +73,27 @@ static const struct argp_option opts[] = { {"sport", 's', "SPORT", 0, "trace this source port only"}, {"dport", 'd', "DPORT", 0, "trace this destination port only"}, {"udp", 'u', 0, 0, "trace the udp message"}, - {"net_filter",'n',0,0,"trace ipv4 packget filter "}, - {"kfree_info",'k',0,0,"trace kfree "}, - {"addr_to_func",'T',0,0,"translation addr to func and offset"}, + {"net_filter", 'n', 0, 0, "trace ipv4 packget filter "}, + {"drop_reason", 'k', 0, 0, "trace kfree "}, + {"addr_to_func", 'F', 0, 0, "translation addr to func and offset"}, {"icmptime", 'I', 0, 0, "set to trace layer time of icmp"}, {"tcpstate", 'S', 0, 0, "set to trace tcpstate"}, + {"timeload", 'L', 0, 0, "analysis time load"}, + {"dns", 'D', 0, 0, + "set to trace dns information info include Id 事务ID、Flags 标志字段、Qd " + "问题部分计数、An 应答记录计数、Ns 授权记录计数、Ar 附加记录计数、Qr " + "域名、rx 收发包 、Qc请求数、Sc响应数"}, + {"stack", 'A', 0, 0, "set to trace of stack "}, + {"mysql", 'M', 0, 0, + "set to trace mysql information info include Pid 进程id、Comm " + "进程名、Size sql语句字节大小、Sql 语句"}, + {"redis", 'R', 0, 0}, + {"redis-stat", 'b', 0, 0}, + {"count", 'C', "NUMBER", 0, + "specify the time to count the number of requests"}, + {"rtt", 'T', 0, 0, "set to trace rtt"}, + {"rst_counters", 'U', 0, 0, "set to trace rst"}, + {"protocol_count", 'p', 0, 0, "set to trace protocol count"}, {}}; static error_t parse_arg(int key, char *arg, struct argp_state *state) { @@ -103,9 +130,9 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) { net_filter = 1; break; case 'k': - kfree_info = 1; + drop_reason = 1; break; - case 'T': + case 'F': addr_to_func = 1; break; case 'I': @@ -114,28 +141,204 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) { case 'S': tcp_info = 1; break; + case 'L': + time_load = 1; + break; + case 'D': + dns_info = 1; + break; + case 'A': + stack_info = 1; + break; + case 'M': + mysql_info = 1; + break; + case 'R': + redis_info = 1; + break; + case 'T': + rtt_info = 1; + break; + case 'U': + rst_info = 1; + break; + case 'p': + protocol_count = 1; + break; + case 'b': + redis_stat = 1; + break; + case 'C': + count_info = strtoul(arg, &end, 10); + break; default: return ARGP_ERR_UNKNOWN; } return 0; } - static const struct argp argp = { .options = opts, .parser = parse_arg, .doc = argp_program_doc, }; - -struct SymbolEntry{ - unsigned long addr; - char name[30]; +enum MonitorMode { + MODE_UDP, + MODE_NET_FILTER, + MODE_DROP_REASON, + MODE_ICMP, + MODE_TCP, + MODE_DNS, + MODE_MYSQL, + MODE_REDIS, + MODE_RTT, + MODE_RST, + MODE_PROTOCOL_COUNT, + MODE_REDIS_STAT, + MODE_DEFAULT }; +enum MonitorMode get_monitor_mode() { + if (udp_info) { + return MODE_UDP; + } else if (net_filter) { + return MODE_NET_FILTER; + } else if (drop_reason) { + return MODE_DROP_REASON; + } else if (icmp_info) { + return MODE_ICMP; + } else if (tcp_info) { + return MODE_TCP; + } else if (dns_info) { + return MODE_DNS; + } else if (mysql_info) { + return MODE_MYSQL; + } else if (redis_info) { + return MODE_REDIS; + } else if (redis_stat) { + return MODE_REDIS_STAT; + } else if (rtt_info) { + return MODE_RTT; + } else if (rst_info) { + return MODE_RST; + } else if (protocol_count) { + return MODE_PROTOCOL_COUNT; + } else { + return MODE_DEFAULT; + } +} +#define LOGO_STRING \ + " " \ + " __ __ __ " \ + " \n" \ + " /\\ \\__ /\\ \\__ /\\ \\ " \ + " \n" \ + " ___ __\\ \\ _\\ __ __ __ __ \\ \\ _\\ ___\\ \\ \\___ " \ + " __ _ __ \n" \ + "/ _ \\ / __ \\ \\ \\/ /\\ \\/\\ \\/\\ \\ / __ \\ \\ \\ \\/ / ___\\ " \ + "\\ _ \\ / __ \\/\\ __\\ \n" \ + "/\\ \\/\\ \\/\\ __/\\ \\ \\_\\ \\ \\_/ \\_/ \\/\\ \\_\\ \\_\\ \\ " \ + "\\_/\\ \\__/\\ \\ \\ \\ \\/\\ __/\\ \\ \\/ \n" \ + "\\ \\_\\ \\_\\ \\____\\ \\__\\ \\_______ / /\\ \\__/\\ \\_\\ \\__\\ " \ + "\\____/\\ \\_\\ \\_\\ \\____ \\ \\_\\ \n" \ + " \\/_/\\/_/\\/____/ \\/__/ \\/__//__ / \\/_/ \\/_/\\/__/\\/____/ " \ + "\\/_/\\/_/\\/____/ \\/_/ \n\n" + +void print_logo() { + char *logo = LOGO_STRING; + int i = 0; + FILE *lolcat_pipe = popen("/usr/games/lolcat", "w"); + if (lolcat_pipe == NULL) { + printf("Error: Unable to execute lolcat command.\n"); + return; + } + // 像lolcat管道逐个字符写入字符串 + while (logo[i] != '\0') { + fputc(logo[i], lolcat_pipe); + fflush(lolcat_pipe); // 刷新管道,确保字符被立即发送给lolcat + usleep(150); + i++; + } + + pclose(lolcat_pipe); +} +#define __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe) \ + do { \ + LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts, .func_name = #sym_name, \ + .retprobe = is_retprobe); \ + skel->links.prog_name = bpf_program__attach_uprobe_opts( \ + skel->progs.prog_name, -1, binary_path, 0, &uprobe_opts); \ + } while (false) + +#define __CHECK_PROGRAM(skel, prog_name) \ + do { \ + if (!skel->links.prog_name) { \ + perror("no program attached for " #prog_name); \ + return -errno; \ + } \ + } while (false) + +#define __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, is_retprobe) \ + do { \ + __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe); \ + __CHECK_PROGRAM(skel, prog_name); \ + } while (false) + +#define ATTACH_UPROBE(skel, sym_name, prog_name) \ + __ATTACH_UPROBE(skel, sym_name, prog_name, false) +#define ATTACH_URETPROBE(skel, sym_name, prog_name) \ + __ATTACH_UPROBE(skel, sym_name, prog_name, true) + +#define ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name) \ + __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, false) +#define ATTACH_URETPROBE_CHECKED(skel, sym_name, prog_name) \ + __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, true) + struct SymbolEntry symbols[300000]; -int num_symbols = 0; -struct SymbolEntry findfunc(unsigned long int addr) -{ - int low = 0, high = num_symbols - 1; - int result = -1; +struct SymbolEntry cache[CACHEMAXSIZE]; +// LRU算法查找函数 +struct SymbolEntry find_in_cache(unsigned long int addr) { + // 查找地址是否在快表中 + for (int i = 0; i < cache_size; i++) { + if (cache[i].addr == addr) { + // 更新访问时间 + struct SymbolEntry temp = cache[i]; + // 将访问的元素移动到快表的最前面,即最近使用的位置 + for (int j = i; j > 0; j--) { + cache[j] = cache[j - 1]; + } + cache[0] = temp; + return temp; + } + } + // 如果地址不在快表中,则返回空 + struct SymbolEntry empty_entry; + empty_entry.addr = 0; + return empty_entry; +} +// 将新的符号条目加入快表 +void add_to_cache(struct SymbolEntry entry) { + // 如果快表已满,则移除最久未使用的条目 + if (cache_size == CACHEMAXSIZE) { + for (int i = cache_size - 1; i > 0; i--) { + cache[i] = cache[i - 1]; + } + cache[0] = entry; + } else { + // 否则,直接加入快表 + for (int i = cache_size; i > 0; i--) { + cache[i] = cache[i - 1]; + } + cache[0] = entry; + cache_size++; + } +} +struct SymbolEntry findfunc(unsigned long int addr) { + // 先在快表中查找 + struct SymbolEntry entry = find_in_cache(addr); + if (entry.addr != 0) { + return entry; + } + unsigned long long low = 0, high = num_symbols - 1; + unsigned long long result = -1; while (low <= high) { int mid = low + (high - low) / 2; @@ -146,11 +349,10 @@ struct SymbolEntry findfunc(unsigned long int addr) high = mid - 1; } } - + add_to_cache(symbols[result]); return symbols[result]; }; -void readallsym() -{ +void readallsym() { FILE *file = fopen("/proc/kallsyms", "r"); if (!file) { perror("Error opening file"); @@ -161,7 +363,7 @@ void readallsym() unsigned long addr; char type, name[30]; int ret = sscanf(line, "%lx %c %s", &addr, &type, name); - if (ret == 3) { + if (ret == 3) { symbols[num_symbols].addr = addr; strncpy(symbols[num_symbols].name, name, 30); num_symbols++; @@ -170,8 +372,382 @@ void readallsym() fclose(file); } -static void sig_handler(int signo) { exiting = true; } +/* + 指数加权移动平均算法(EWMA) + 1.使用指数加权移动平均算法(EWMA)来计算每层的指数加权移动平均值, + 公式EWMA_new = alpha * new_value + (1 - alpha) * old_ewma ,alpha + 指数加权系数,表示新数据点的权重,new_value 当前时延,old_ewma + 旧的指数加权移动平均值 + 2.根据当前时延和指数加权移动平均值*预先设定的粒度阈值(GRANULARITY)对比,来判断时延是否异常 + 3.可以快速适应数据的变化,并能够有效地检测异常时延 +*/ +// 全局变量用于存储每层的移动平均值 +float ewma_values[NUM_LAYERS] = {0}; +int count[NUM_LAYERS] = {0}; + +// 指数加权移动平均算法 +float calculate_ewma(float new_value, float old_ewma) { + return ALPHA * new_value + (1 - ALPHA) * old_ewma; +} + +// 收集时延数据并检测异常 +int process_delay(float layer_delay, int layer_index) { + + if (layer_delay == 0) + return 0; + count[layer_index]++; + if (ewma_values[layer_index] == 0) { + ewma_values[layer_index] = layer_delay; + return 0; + } + // 计算阈值,指数加权移动平均值乘以粒度因子 + ewma_values[layer_index] = + calculate_ewma(layer_delay, ewma_values[layer_index]); + float threshold = ewma_values[layer_index] * GRANULARITY; + if (count[layer_index] > 30) { + // 判断当前时延是否超过阈值 + // printf("%d %d:%f %f + // ",layer_index,count[layer_index]++,threshold,layer_delay); + if (layer_delay > threshold) { // 异常 + return 1; + } else { + return 0; + } + } + return 0; +} +static void set_rodata_flags(struct netwatcher_bpf *skel) { + skel->rodata->filter_dport = dport; + skel->rodata->filter_sport = sport; + skel->rodata->all_conn = all_conn; + skel->rodata->err_packet = err_packet; + skel->rodata->extra_conn_info = extra_conn_info; + skel->rodata->layer_time = layer_time; + skel->rodata->http_info = http_info; + skel->rodata->retrans_info = retrans_info; + skel->rodata->udp_info = udp_info; + skel->rodata->net_filter = net_filter; + skel->rodata->drop_reason = drop_reason; + skel->rodata->tcp_info = tcp_info; + skel->rodata->icmp_info = icmp_info; + skel->rodata->dns_info = dns_info; + skel->rodata->stack_info = stack_info; + skel->rodata->mysql_info = mysql_info; + skel->rodata->redis_info = redis_info; + skel->rodata->redis_stat = redis_stat; + skel->rodata->rtt_info = rtt_info; + skel->rodata->rst_info = rst_info; + skel->rodata->protocol_count = protocol_count; +} +static void set_disable_load(struct netwatcher_bpf *skel) { + + bpf_program__set_autoload(skel->progs.inet_csk_accept_exit, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_v4_connect, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_v4_connect_exit, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_v6_connect, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_v6_connect_exit, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_set_state, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.eth_type_trans, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info || protocol_count) + ? true + : false); + bpf_program__set_autoload(skel->progs.ip_rcv_core, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.ip6_rcv_core, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_v4_rcv, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_v6_rcv, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_v4_do_rcv, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_v6_do_rcv, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.skb_copy_datagram_iter, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_validate_incoming, + err_packet ? true : false); + bpf_program__set_autoload(skel->progs.__skb_checksum_complete_exit, + err_packet ? true : false); + bpf_program__set_autoload(skel->progs.tcp_sendmsg, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.ip_queue_xmit, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.inet6_csk_xmit, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.__dev_queue_xmit, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.dev_hard_start_xmit, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info || protocol_count) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_enter_recovery, + retrans_info ? true : false); + bpf_program__set_autoload(skel->progs.tcp_enter_loss, + retrans_info ? true : false); + bpf_program__set_autoload(skel->progs.udp_rcv, + udp_info || dns_info ? true : false); + bpf_program__set_autoload(skel->progs.__udp_enqueue_schedule_skb, + udp_info || dns_info ? true : false); + bpf_program__set_autoload(skel->progs.udp_send_skb, + udp_info || dns_info ? true : false); + bpf_program__set_autoload(skel->progs.ip_send_skb, + udp_info || dns_info ? true : false); + bpf_program__set_autoload(skel->progs.ip_rcv, net_filter ? true : false); + bpf_program__set_autoload(skel->progs.ip_local_deliver, + net_filter ? true : false); + bpf_program__set_autoload(skel->progs.ip_local_deliver_finish, + net_filter ? true : false); + bpf_program__set_autoload(skel->progs.ip_local_out, + net_filter ? true : false); + bpf_program__set_autoload(skel->progs.ip_output, net_filter ? true : false); + bpf_program__set_autoload(skel->progs.__ip_finish_output, + net_filter ? true : false); + bpf_program__set_autoload(skel->progs.ip_forward, + net_filter ? true : false); + bpf_program__set_autoload(skel->progs.tp_kfree, drop_reason ? true : false); + bpf_program__set_autoload(skel->progs.icmp_rcv, icmp_info ? true : false); + bpf_program__set_autoload(skel->progs.__sock_queue_rcv_skb, + icmp_info ? true : false); + bpf_program__set_autoload(skel->progs.icmp_reply, icmp_info ? true : false); + bpf_program__set_autoload(skel->progs.handle_set_state, + tcp_info ? true : false); + bpf_program__set_autoload(skel->progs.query__start, + mysql_info ? true : false); + bpf_program__set_autoload(skel->progs.query__end, + mysql_info ? true : false); + bpf_program__set_autoload(skel->progs.redis_addReply, + redis_stat ? true : false); + bpf_program__set_autoload(skel->progs.redis_lookupKey, + redis_stat ? true : false); + bpf_program__set_autoload(skel->progs.redis_processCommand, + redis_info ? true : false); + bpf_program__set_autoload(skel->progs.redis_call, + redis_info ? true : false); + bpf_program__set_autoload(skel->progs.tcp_rcv_established, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.handle_send_reset, + rst_info ? true : false); + bpf_program__set_autoload(skel->progs.handle_receive_reset, + rst_info ? true : false); +} +static void print_header(enum MonitorMode mode) { + switch (mode) { + case MODE_UDP: + printf("===============================================================" + "UDP " + "INFORMATION====================================================" + "====\n"); + printf("%-20s %-20s %-20s %-20s %-20s %-20s %-20s\n", "Saddr", "Daddr", + "Sprot", "Dprot", "udp_time/μs", "RX/direction", "len/byte"); + break; + case MODE_NET_FILTER: + printf("===============================================================" + "===NETFILTER " + "INFORMATION====================================================" + "=======\n"); + printf("%-20s %-20s %-12s %-12s %-8s %-8s %-7s %-8s %-8s %-8s\n", + "Saddr", "Daddr", "Sprot", "Dprot", "PreRT/μs", "L_IN/μs", + "FW/μs", "PostRT/μs", "L_OUT/μs", "RX/direction"); + break; + case MODE_DROP_REASON: + printf("===============================================================" + "DROP " + "INFORMATION====================================================" + "====\n"); + printf("%-13s %-17s %-17s %-10s %-10s %-9s %-33s %-30s\n", "Time", + "Saddr", "Daddr", "Sprot", "Dprot", "prot", "addr", "reason"); + break; + case MODE_ICMP: + printf("=================================================ICMP " + "INFORMATION==============================================\n"); + printf("%-20s %-20s %-20s %-20s\n", "Saddr", "Daddr", "icmp_time/μs", + "RX/direction"); + break; + case MODE_TCP: + printf("===============================================================" + "TCP STATE " + "INFORMATION====================================================" + "====\n"); + printf("%-20s %-20s %-20s %-20s %-20s %-20s %-20s \n", "Saddr", "Daddr", + "Sport", "Dport", "oldstate", "newstate", "time/μs"); + break; + case MODE_DNS: + printf("===============================================================" + "====================DNS " + "INFORMATION====================================================" + "============================\n"); + printf("%-20s %-20s %-12s %-12s %-5s %-5s %-5s %-5s %-47s %-10s %-10s " + "%-10s \n", + "Saddr", "Daddr", "Id", "Flags", "Qd", "An", "Ns", "Ar", "Qr", + "Qc", "Sc", "RX/direction"); + break; + case MODE_MYSQL: + printf("===============================================================" + "====================MYSQL " + "INFORMATION====================================================" + "============================\n"); + printf("%-20s %-20s %-20s %-20s %-40s %-20s %-20s \n", "Pid", "Tid", + "Comm", "Size", "Sql", "Duration/μs", "Request"); + break; + case MODE_REDIS: + printf("===============================================================" + "====================REDIS " + "INFORMATION====================================================" + "============================\n"); + printf("%-20s %-20s %-20s %-20s %-20s \n", "Pid", "Comm", "Size", + "Redis", "duration/μs"); + break; + case MODE_REDIS_STAT: + printf("===============================================================" + "====================REDIS " + "INFORMATION====================================================" + "============================\n"); + printf("%-20s %-20s %-20s %-20s %-20s %-20s\n", "Pid", "Comm", "key", "Key_count","Value_Type","Value"); + break; + case MODE_RTT: + printf("===============================================================" + "====================RTT " + "INFORMATION====================================================" + "============================\n"); + break; + case MODE_RST: + printf("===============================================================" + "====================RST " + "INFORMATION====================================================" + "============================\n"); + printf("%-20s %-20s %-20s %-20s %-20s %-20s %-20s \n", "Pid", "Comm", + "Saddr", "Daddr", "Sport", "Dport", "Time"); + break; + case MODE_DEFAULT: + printf("===============================================================" + "=INFORMATION===================================================" + "======================\n"); + printf("%-22s %-20s %-8s %-20s %-8s %-15s %-15s %-15s %-15s %-15s \n", + "SOCK", "Saddr", "Sport", "Daddr", "Dport", "MAC_TIME/μs", + "IP_TIME/μs", "TRAN_TIME/μs", "RX/direction", "HTTP"); + break; + case MODE_PROTOCOL_COUNT: + printf("===============================================================" + "=MODE_PROTOCOL_COUNT===========================================" + "========" + "======================\n"); + break; + } +} +static void open_log_files() { + FILE *connect_file = fopen(connects_file_path, "w+"); + if (connect_file == NULL) { + fprintf(stderr, "Failed to open connect.log: (%s)\n", strerror(errno)); + exit(EXIT_FAILURE); + } + fclose(connect_file); + + FILE *err_file = fopen(err_file_path, "w+"); + if (err_file == NULL) { + fprintf(stderr, "Failed to open err.log: (%s)\n", strerror(errno)); + exit(EXIT_FAILURE); + } + fclose(err_file); + + FILE *packet_file = fopen(packets_file_path, "w+"); + if (packet_file == NULL) { + fprintf(stderr, "Failed to open packets.log: (%s)\n", strerror(errno)); + exit(EXIT_FAILURE); + } + fclose(packet_file); + + FILE *udp_file = fopen(udp_file_path, "w+"); + if (udp_file == NULL) { + fprintf(stderr, "Failed to open udp.log: (%s)\n", strerror(errno)); + exit(EXIT_FAILURE); + } + fclose(udp_file); +} +static void sig_handler(int signo) { exiting = true; } static void bytes_to_str(char *str, unsigned long long num) { if (num > 1e9) { sprintf(str, "%.8lfG", (double)num / 1e9); @@ -183,7 +759,6 @@ static void bytes_to_str(char *str, unsigned long long num) { sprintf(str, "%llu", num); } } - static int print_conns(struct netwatcher_bpf *skel) { FILE *file = fopen(connects_file_path, "w"); @@ -212,7 +787,9 @@ static int print_conns(struct netwatcher_bpf *skel) { char s_ip_port_str[INET6_ADDRSTRLEN + 6]; char d_ip_port_str[INET6_ADDRSTRLEN + 6]; - + if ((d.saddr & 0x0000FFFF) == 0x0000007F || + (d.daddr & 0x0000FFFF) == 0x0000007F) + return 0; if (d.family == AF_INET) { sprintf(s_ip_port_str, "%s:%d", inet_ntop(AF_INET, &d.saddr, s_str, sizeof(s_str)), @@ -272,11 +849,28 @@ static int print_conns(struct netwatcher_bpf *skel) { fclose(file); return 0; } - static int print_packet(void *ctx, void *packet_info, size_t size) { - if (udp_info || net_filter || kfree_info || icmp_info || tcp_info) + if (udp_info || net_filter || drop_reason || icmp_info || tcp_info || + dns_info || mysql_info || redis_info || rtt_info || protocol_count||redis_stat) return 0; const struct pack_t *pack_info = packet_info; + if (pack_info->mac_time > MAXTIME || pack_info->ip_time > MAXTIME || + pack_info->tran_time > MAXTIME) { + return 0; + } + char d_str[INET_ADDRSTRLEN]; + char s_str[INET_ADDRSTRLEN]; + unsigned int saddr = pack_info->saddr; + unsigned int daddr = pack_info->daddr; + if ((daddr & 0x0000FFFF) == 0x0000007F || + (saddr & 0x0000FFFF) == 0x0000007F) + return 0; + if (dport) + if (pack_info->dport != dport) + return 0; + if (sport) + if (pack_info->sport != sport) + return 0; if (pack_info->err) { FILE *file = fopen(err_file_path, "a"); char reason[20]; @@ -314,38 +908,64 @@ static int print_packet(void *ctx, void *packet_info, size_t size) { sprintf(http_data, "-"); } if (layer_time) { - printf("%-22p %-10u %-10u %-10llu %-10llu %-10llu %-5d %s\n", - pack_info->sock, pack_info->seq, pack_info->ack, - pack_info->mac_time, pack_info->ip_time, + printf("%-22p %-20s %-8d %-20s %-8d %-14llu %-14llu %-14llu %-15d " + "%-16s", + pack_info->sock, + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), + pack_info->sport, + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), + pack_info->dport, pack_info->mac_time, pack_info->ip_time, pack_info->tran_time, pack_info->rx, http_data); fprintf( file, - "packet{sock=\"%p\",seq=\"%u\",ack=\"%u\"," + "packet{sock=\"%p\",saddr=\"%s\",sport=\"%d\",daddr=\"%s\"," + "dport=\"%d\",seq=\"%u\",ack=\"%u\"," "mac_time=\"%llu\",ip_time=\"%llu\",tran_time=\"%llu\",http_" "info=\"%s\",rx=\"%d\"} \n", - pack_info->sock, pack_info->seq, pack_info->ack, + pack_info->sock, + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), + pack_info->sport, + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), + pack_info->dport, pack_info->seq, pack_info->ack, pack_info->mac_time, pack_info->ip_time, pack_info->tran_time, http_data, pack_info->rx); } else { - printf("%-22p %-10u %-10u %-10d %-10d %-10d %-5d %s\n", - pack_info->sock, pack_info->seq, pack_info->ack, 0, 0, 0, - pack_info->rx, http_data); + printf("%-22p %-20s %-8d %-20s %-8d %-10d %-10d %-10d %-5d %-10s", + pack_info->sock, + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), + pack_info->sport, + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), + pack_info->dport, 0, 0, 0, pack_info->rx, http_data); fprintf(file, - "packet{sock=\"%p\",seq=\"%u\",ack=\"%u\"," + "packet{sock=\"%p\",saddr=\"%s\",sport=\"%d\",daddr=\"%s\"," + "dport=\"%d\",seq=\"%u\",ack=\"%u\"," "mac_time=\"%d\",ip_time=\"%d\",tran_time=\"%d\",http_" "info=\"%s\",rx=\"%d\"} \n", - pack_info->sock, pack_info->seq, pack_info->ack, 0, 0, 0, + pack_info->sock, + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), + pack_info->sport, + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), + pack_info->dport, pack_info->seq, pack_info->ack, 0, 0, 0, http_data, pack_info->rx); } fclose(file); } + if (time_load) { + int mac = process_delay(pack_info->mac_time, 0); + int ip = process_delay(pack_info->ip_time, 1); + int tran = process_delay(pack_info->tran_time, 2); + if (mac || ip || tran) { + printf("%-15s", "abnormal data"); + } + } + printf("\n"); return 0; } static int print_udp(void *ctx, void *packet_info, size_t size) { if (!udp_info) return 0; - FILE *file = fopen(udp_file_path, "a+");//追加 - if (file == NULL) { + FILE *file = fopen(udp_file_path, "a+"); // 追加 + if (file == NULL) { fprintf(stderr, "Failed to open udp.log: (%s)\n", strerror(errno)); return 0; } @@ -354,114 +974,546 @@ static int print_udp(void *ctx, void *packet_info, size_t size) { const struct udp_message *pack_info = packet_info; unsigned int saddr = pack_info->saddr; unsigned int daddr = pack_info->daddr; - printf("%-20s %-20s %-20u %-20u %-20llu %-20d %-20d\n", + if (pack_info->tran_time > MAXTIME || (daddr & 0x0000FFFF) == 0x0000007F || + (saddr & 0x0000FFFF) == 0x0000007F) + return 0; + printf("%-20s %-20s %-20u %-20u %-20llu %-20d %-20d", inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), pack_info->sport, - pack_info->dport, pack_info->tran_time,pack_info->rx,pack_info->len); - fprintf( - file, + pack_info->dport, pack_info->tran_time, pack_info->rx, + pack_info->len); + fprintf(file, "packet{saddr=\"%s\",daddr=\"%s\",sport=\"%u\"," "dport=\"%u\",udp_time=\"%llu\",rx=\"%d\",len=\"%d\"} \n", inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), pack_info->sport, - pack_info->dport, pack_info->tran_time,pack_info->rx,pack_info->len); - + pack_info->dport, pack_info->tran_time, pack_info->rx, + pack_info->len); fclose(file); + if (time_load) { + int flag = process_delay(pack_info->tran_time, 3); + if (flag) + printf("%-15s", "abnormal data"); + } + printf("\n"); return 0; } - static int print_netfilter(void *ctx, void *packet_info, size_t size) { - if(!net_filter) + if (!net_filter) return 0; char d_str[INET_ADDRSTRLEN]; - char s_str[INET_ADDRSTRLEN]; + char s_str[INET_ADDRSTRLEN]; const struct netfilter *pack_info = packet_info; + if (pack_info->local_input_time > MAXTIME || + pack_info->forward_time > MAXTIME || + pack_info->local_out_time > MAXTIME || + pack_info->post_routing_time > MAXTIME || + pack_info->pre_routing_time > MAXTIME) + return 0; unsigned int saddr = pack_info->saddr; unsigned int daddr = pack_info->daddr; - printf("%-20s %-20s %-20d %-20d %-20lld %-20lld %-20lld %-20lld %-20lld %-20d\n", - inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), - inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), - pack_info->sport,pack_info->dport, - pack_info->pre_routing_time, - pack_info->local_input_time, - pack_info->forward_time, - pack_info->post_routing_time, - pack_info->local_out_time, - pack_info->rx); - + // if ((daddr & 0x0000FFFF) == 0x0000007F || + // (saddr & 0x0000FFFF) == 0x0000007F) + // return 0; + printf("%-20s %-20s %-12d %-12d %-8lld %-8lld% -8lld %-8lld %-8lld %-8d", + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), pack_info->sport, + pack_info->dport, pack_info->pre_routing_time, + pack_info->local_input_time, pack_info->forward_time, + pack_info->post_routing_time, pack_info->local_out_time, + pack_info->rx); + // 定义一个数组用于存储需要检测的时延数据和对应的层索引 + struct LayerDelayInfo layer_delay_infos[] = { + {pack_info->pre_routing_time, 4}, + {pack_info->local_input_time, 5}, + {pack_info->forward_time, 6}, + {pack_info->post_routing_time, 7}, + {pack_info->local_out_time, 8}}; + if (time_load) { + // 循环遍历数组 + for (int i = 0; i < 5; i++) { + // 数组的总字节数除以第一个元素的字节数得到元素的个数 + float delay = layer_delay_infos[i].delay; + int layer_net = layer_delay_infos[i].layer_index; + int flag = process_delay(delay, layer_net); + if (flag) + printf("%-15s", "abnormal data"); + } + } + printf("\n"); + return 0; } - - static int print_tcpstate(void *ctx, void *packet_info, size_t size) { - if(!tcp_info) + if (!tcp_info) return 0; char d_str[INET_ADDRSTRLEN]; - char s_str[INET_ADDRSTRLEN]; + char s_str[INET_ADDRSTRLEN]; const struct tcp_state *pack_info = packet_info; unsigned int saddr = pack_info->saddr; unsigned int daddr = pack_info->daddr; printf("%-20s %-20s %-20d %-20d %-20s %-20s %-20lld\n", - inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), - inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), - pack_info->sport,pack_info->dport,tcp_states[pack_info->oldstate],tcp_states[pack_info->newstate],pack_info->time); + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), pack_info->sport, + pack_info->dport, tcp_states[pack_info->oldstate], + tcp_states[pack_info->newstate], pack_info->time); return 0; } +static void calculate_protocol_usage(struct packet_count proto_stats[], + int num_protocols, int interval) { + static uint64_t last_rx[256] = {0}, last_tx[256] = {0}; + uint64_t current_rx = 0, current_tx = 0; + uint64_t delta_rx[256] = {0}, delta_tx[256] = {0}; + //遍历所有的协议 + for (int i = 0; i < num_protocols; i++) { + //计算数据包增量 + if (proto_stats[i].rx_count >= last_rx[i]) { + delta_rx[i] = proto_stats[i].rx_count - last_rx[i]; + } else { + delta_rx[i] = proto_stats[i].rx_count; + } + + if (proto_stats[i].tx_count >= last_tx[i]) { + delta_tx[i] = proto_stats[i].tx_count - last_tx[i]; + } else { + delta_tx[i] = proto_stats[i].tx_count; + } + //时间段内总的接收和发送包数 + current_rx += delta_rx[i]; + current_tx += delta_tx[i]; + //更新上次统计的包数 + last_rx[i] = proto_stats[i].rx_count; + last_tx[i] = proto_stats[i].tx_count; + } + printf("Protocol Usage in Last %d Seconds:\n", interval); + printf("Total_rx_count:%ld Total_tx_count:%ld\n", current_rx, current_tx); + if (current_rx > 0) { + printf("Receive Protocol Usage:\n"); + for (int i = 0; i < num_protocols; i++) { + if (delta_rx[i] > 0) { + double rx_percentage = (double)delta_rx[i] / current_rx * 100; + if (rx_percentage >= 80.0) { + printf(RED_TEXT + "Protocol %s: %.2f%% Rx_count:%ld\n" RESET_TEXT, + protocol[i], rx_percentage, delta_rx[i]); + } else { + printf("Protocol %s: %.2f%% Rx_count:%ld\n", protocol[i], + rx_percentage, delta_rx[i]); + } + } + } + } + if (current_tx > 0) { + printf("Transmit Protocol Usage:\n"); + for (int i = 0; i < num_protocols; i++) { + if (delta_tx[i] > 0) { + double tx_percentage = (double)delta_tx[i] / current_tx * 100; + if (tx_percentage >= 80.0) { + printf(RED_TEXT + "Protocol %s: %.2f%% Tx_count:%ld\n" RESET_TEXT, + protocol[i], tx_percentage, delta_tx[i]); + } else { + printf("Protocol %s: %.2f%% Tx_count:%ld\n", protocol[i], + tx_percentage, delta_tx[i]); + } + } + } + } + memset(proto_stats, 0, num_protocols * sizeof(struct packet_count)); +} +static int print_protocol_count(void *ctx, void *packet_info, size_t size) { + const struct packet_info *pack_protocol_info = + (const struct packet_info *)packet_info; + if (!protocol_count) { + return 0; + } + proto_stats[pack_protocol_info->proto].rx_count = + pack_protocol_info->count.rx_count; + proto_stats[pack_protocol_info->proto].tx_count = + pack_protocol_info->count.tx_count; + return 0; +} static int print_kfree(void *ctx, void *packet_info, size_t size) { - if(!kfree_info) + if (!drop_reason) return 0; char d_str[INET_ADDRSTRLEN]; - char s_str[INET_ADDRSTRLEN]; + char s_str[INET_ADDRSTRLEN]; const struct reasonissue *pack_info = packet_info; unsigned int saddr = pack_info->saddr; unsigned int daddr = pack_info->daddr; - if(saddr == 0 && daddr ==0 ) - { + if (saddr == 0 && daddr == 0) { return 0; } char prot[6]; - if(pack_info->protocol==2048) - { + if (pack_info->protocol == 2048) { strcpy(prot, "ipv4"); - } - else if(pack_info->protocol==34525) - { + } else if (pack_info->protocol == 34525) { strcpy(prot, "ipv6"); - } - else { + } else { // 其他协议 strcpy(prot, "other"); } - printf("%-20s %-20s %-10u %-10u %-10s", - inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), - inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), pack_info->sport,pack_info->dport,prot); - if(!addr_to_func) - printf("%-20lx",pack_info->location); + time_t now = time(NULL); + struct tm *localTime = localtime(&now); + printf("%02d:%02d:%02d %-17s %-17s %-10u %-10u %-10s", + localTime->tm_hour, localTime->tm_min, localTime->tm_sec, + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), pack_info->sport, + pack_info->dport, prot); + if (!addr_to_func) + printf("%-34lx", pack_info->location); else { - struct SymbolEntry data= findfunc(pack_info->location); - printf("%s+0x%-10lx",data.name,pack_info->location-data.addr); + struct SymbolEntry data = findfunc(pack_info->location); + char result[40]; + sprintf(result, "%s+0x%lx", data.name, pack_info->location - data.addr); + printf("%-34s", result); } printf("%s\n", SKB_Drop_Reason_Strings[pack_info->drop_reason]); return 0; } - static int print_icmptime(void *ctx, void *packet_info, size_t size) { - if(!icmp_info) + if (!icmp_info) return 0; char d_str[INET_ADDRSTRLEN]; - char s_str[INET_ADDRSTRLEN]; + char s_str[INET_ADDRSTRLEN]; const struct icmptime *pack_info = packet_info; + if (pack_info->icmp_tran_time > MAXTIME) { + return 0; + } unsigned int saddr = pack_info->saddr; unsigned int daddr = pack_info->daddr; - printf("%-20s %-20s %-10lld %-10d\n", - inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), - inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), - pack_info->icmp_tran_time, - pack_info->flag); + printf("%-20s %-20s %-20lld %-20d", + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), + pack_info->icmp_tran_time, pack_info->flag); + if (time_load) { + int icmp_data = process_delay(pack_info->icmp_tran_time, 9); + if (icmp_data) { + printf("%-15s\n", "abnormal data"); + } + } + printf("\n"); + return 0; +} +static int print_rst(void *ctx, void *packet_info, size_t size) { + if (!rst_info) { + return 0; + } + struct reset_event_t *event = packet_info; + + // 将事件存储到全局存储中 + if (event_count < MAX_EVENTS) { + memcpy(&event_store[event_count], event, sizeof(struct reset_event_t)); + event_count++; + } + + rst_count++; + return 0; +} +static void print_stored_events() { + char s_str[INET_ADDRSTRLEN]; + char d_str[INET_ADDRSTRLEN]; + + for (int i = 0; i < event_count; i++) { + struct reset_event_t *event = &event_store[i]; + unsigned int saddr = event->saddr; + unsigned int daddr = event->daddr; + + if (event->family == AF_INET) { + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)); + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)); + printf("%-20llu %-20s %-20s %-20s %-20u %-20u %-20llu\n", + (unsigned long long)event->pid, event->comm, s_str, d_str, + event->sport, event->dport, + (unsigned long long)event->timestamp); + } else if (event->family == AF_INET6) { + char saddr_v6[INET6_ADDRSTRLEN]; + char daddr_v6[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &event->saddr_v6, saddr_v6, sizeof(saddr_v6)); + inet_ntop(AF_INET6, &event->daddr_v6, daddr_v6, sizeof(daddr_v6)); + printf("%-10llu %-16s %-16s %-16s %-8u %-8u %-20llu\n", + (unsigned long long)event->pid, event->comm, saddr_v6, + daddr_v6, event->sport, event->dport, + (unsigned long long)event->timestamp); + } + } +} +static void print_domain_name(const unsigned char *data, char *output) { + const unsigned char *next = data; + int pos = 0, first = 1; + // 循环到尾部,标志0 + while (*next != 0) { + if (!first) { + output[pos++] = '.'; // 在每个段之前添加点号 + } else { + first = 0; // 第一个段后清除标志 + } + int len = *next++; // 下一个段长度 + + for (int i = 0; i < len; ++i) { + output[pos++] = *next++; + } + } + output[pos] = '\0'; // 确保字符串正确结束 +} +static int print_dns(void *ctx, void *packet_info, size_t size) { + if (!packet_info) + return 0; + char d_str[INET_ADDRSTRLEN]; + char s_str[INET_ADDRSTRLEN]; + const struct dns_information *pack_info = + (const struct dns_information *)packet_info; // 强制类型转换 + unsigned int saddr = pack_info->saddr; + unsigned int daddr = pack_info->daddr; + char domain_name[256]; // 用于存储输出的域名 + + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)); + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)); + + print_domain_name((const unsigned char *)pack_info->data, domain_name); + if (pack_info->daddr == 0) { + return 0; + } + printf("%-20s %-20s %-#12x %-#12x %-5x %-5x %-5x %-5x %-47s %-10d %-10d " + "%-10d \n", + s_str, d_str, pack_info->id, pack_info->flags, pack_info->qdcount, + pack_info->ancount, pack_info->nscount, pack_info->arcount, + domain_name, pack_info->request_count, pack_info->response_count, + pack_info->rx); + return 0; +} +static int print_mysql(void *ctx, void *packet_info, size_t size) { + if (!mysql_info) { + return 0; + } + + const mysql_query *pack_info = packet_info; + printf("%-20d %-20d %-20s %-20u %-41s", pack_info->pid, pack_info->tid, + pack_info->comm, pack_info->size, pack_info->msql); + if (pack_info->duratime > count_info) { + printf("%-21llu", pack_info->duratime); + } else { + printf("%-21s", ""); + } + printf("%-20d\n", pack_info->count); + return 0; +} +static int print_redis(void *ctx, void *packet_info, size_t size) { + const struct redis_query *pack_info = packet_info; + int i = 0; + char redis[64]; + for (i = 0; i < pack_info->argc; i++) { + strcat(redis, pack_info->redis[i]); + strcat(redis, " "); + } + printf("%-20d %-20s %-20d %-20s %-21llu\n", pack_info->pid, pack_info->comm, + pack_info->argc, redis, pack_info->duratime); + strcpy(redis, ""); + return 0; +} +static int process_redis_first(char flag,char *message) { + if(flag=='+') + { + strcpy(message, "Status Reply"); + } + else if (flag=='-') + { + strcpy(message, "Error Reply"); + } + else if (flag==':') + { + strcpy(message, "Integer Reply"); + } + else if (flag=='$') + { + strcpy(message, "Bulk String Reply"); + } + else if (flag=='*') + { + strcpy(message, "Array Reply"); + } + else{ + strcpy(message, "Unknown Type"); + } return 0; } + +static int print_redis_stat(void *ctx, void *packet_info, size_t size) { + if (!redis_stat) { + return 0; + } + char message[20]={}; + const struct redis_stat_query *pack_info = packet_info; + if(pack_info->key_count) + { + printf("%-20d %-20s %-20s %-20d %-20s %-20s\n", pack_info->pid, pack_info->comm, + pack_info->key,pack_info->key_count,"-","-"); + } + else + { + process_redis_first(pack_info->value[0],message); + printf("%-20d %-20s %-20s %-20s %-20s %-20s\n", pack_info->pid, pack_info->comm, + "-","-",message,pack_info->value); + } + + return 0; +} + +static int libbpf_print_fn(enum libbpf_print_level level, const char *format, + va_list args) { + return vfprintf(stderr, format, args); +} +static void show_stack_trace(__u64 *stack, int stack_sz, pid_t pid) { + int i; + printf("-----------------------------------\n"); + for (i = 1; i < stack_sz; i++) { + if (addr_to_func) { + struct SymbolEntry data = findfunc(stack[i]); + char result[40]; + sprintf(result, "%s+0x%llx", data.name, stack[i] - data.addr); + printf("%-10d [<%016llx>]=%s\n", i, stack[i], result); + } else { + printf("%-10d [<%016llx>]\n", i, stack[i]); + } + } + printf("-----------------------------------\n"); +} +static int print_trace(void *_ctx, void *data, size_t size) { + struct stacktrace_event *event = data; + + if (event->kstack_sz <= 0 && event->ustack_sz <= 0) + return 1; + + printf("COMM: %s (pid=%d) @ CPU %d\n", event->comm, event->pid, + event->cpu_id); + + if (event->kstack_sz > 0) { + printf("Kernel:\n"); + show_stack_trace(event->kstack, event->kstack_sz / sizeof(__u64), 0); + } else { + printf("No Kernel Stack\n"); + } + printf("\n"); + return 0; +} +static int print_rtt(void *ctx, void *data, size_t size) { + if (!rtt_info) + return 0; + struct RTT *rtt_tuple = data; + unsigned long long total_latency = 0; + unsigned long long total_count = 0; + char d_str[INET_ADDRSTRLEN]; + char s_str[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &rtt_tuple->saddr, s_str, sizeof(s_str)); + inet_ntop(AF_INET, &rtt_tuple->daddr, d_str, sizeof(d_str)); + if ((rtt_tuple->saddr & 0x0000FFFF) == 0x0000007F || + (rtt_tuple->daddr & 0x0000FFFF) == 0x0000007F || + rtt_tuple->saddr == htonl(0xC0A83C01) || + rtt_tuple->daddr == htonl(0xC0A83C01)) { + return 0; // 如果匹配任一过滤条件,放弃处理这些数据包 + } + // 打印源地址和目的地址 + printf("Source Address: %s\n", s_str); + printf("Destination Address: %s\n", d_str); + // 更新总延迟和计数 + total_latency += rtt_tuple->latency; + total_count += rtt_tuple->cnt; + + // 打印总延迟和平均RTT + double average_rtt = + (total_count > 0) ? (double)total_latency / total_count : 0; + printf("Total Latency: %llu μs\n", total_latency); + printf("Average RTT: %.2f ms\n", average_rtt / 1000.0); + + // 计算和打印RTT分布图 + printf(" usecs : count distribution\n"); + int bucket_size = 1; + for (int i = 0; i < MAX_SLOTS; i++) { + int start_range = bucket_size == 1 ? 0 : bucket_size; + int end_range = bucket_size * 2 - 1; + printf("%8d -> %-8d : %-8llu |", start_range, end_range, + rtt_tuple->slots[i]); + int bar_length = + rtt_tuple->slots[i] / + 10; //计算该延迟范围内的计数对应的直方图条形长度,每个'*' + //表示 10 个计数 + for (int j = 0; j < bar_length; j++) { + printf("*"); + } + printf("\n"); + bucket_size *= 2; //以对数方式扩展 + } + printf("===============================================================\n"); + return 0; +} +int attach_uprobe_mysql(struct netwatcher_bpf *skel) { + + ATTACH_UPROBE_CHECKED( + skel, _Z16dispatch_commandP3THDPK8COM_DATA19enum_server_command, + query__start); + ATTACH_URETPROBE_CHECKED( + skel, _Z16dispatch_commandP3THDPK8COM_DATA19enum_server_command, + query__end); + return 0; +} +int attach_uprobe_redis(struct netwatcher_bpf *skel) { + if(redis_info){ + ATTACH_UPROBE_CHECKED(skel, call, redis_call); + ATTACH_UPROBE_CHECKED(skel, processCommand, redis_processCommand); + } + if(redis_stat){ + ATTACH_UPROBE_CHECKED(skel, lookupKey, redis_lookupKey); + ATTACH_UPROBE_CHECKED(skel, addReply, redis_addReply); + } + return 0; +} + +void print_top_5_keys() { + kv_pair *pairs; + pairs = malloc(sizeof(kv_pair) * 1024); + if (!pairs) { + perror("Failed to allocate memory"); + exit(EXIT_FAILURE); + } + int index = 0; + char *key = NULL; + while (bpf_map_get_next_key(map_fd, &key, &key) == 0) { + // fprintf(stdout, "next_sk: (%p)\n", sk); + int count; + int err = bpf_map_lookup_elem(map_fd, &key, &count); + if (err) { + fprintf(stderr, "Failed to read value from the conns map: (%s)\n", + strerror(errno)); + return ; + } + memcpy(pairs[index].key, &key, 256); + pairs[index].value = count; + //printf("Key: %s, Count: %u\n", pairs[index].key, pairs[index].value); + index++; + } + // 获取所有键值对 + + // 排序前 5 个元素 + // 简单选择排序(可替换为其他高效排序算法) + for (int i = 0; i < index - 1; i++) { + for (int j = i + 1; j < index; j++) { + if (pairs[j].value > pairs[i].value) { + kv_pair temp = pairs[i]; + pairs[i] = pairs[j]; + pairs[j] = temp; + } + } + } + printf("----------------------------\n"); + // 打印前 5 个元素 + printf("Top 5 Keys:\n"); + for (int i = 0; i < 5 && i < index; i++) { + printf("Key: %s, Count: %u\n", pairs[i].key, pairs[i].value); + } + free(pairs); +} int main(int argc, char **argv) { char *last_slash = strrchr(argv[0], '/'); if (last_slash) { @@ -474,13 +1526,21 @@ int main(int argc, char **argv) { strcat(connects_file_path, "data/connects.log"); strcat(err_file_path, "data/err.log"); strcat(packets_file_path, "data/packets.log"); - strcat(udp_file_path,"data/udp.log"); + strcat(udp_file_path, "data/udp.log"); struct ring_buffer *rb = NULL; struct ring_buffer *udp_rb = NULL; struct ring_buffer *netfilter_rb = NULL; struct ring_buffer *kfree_rb = NULL; struct ring_buffer *icmp_rb = NULL; struct ring_buffer *tcp_rb = NULL; + struct ring_buffer *dns_rb = NULL; + struct ring_buffer *trace_rb = NULL; + struct ring_buffer *mysql_rb = NULL; + struct ring_buffer *redis_rb = NULL; + struct ring_buffer *redis_stat_rb = NULL; + struct ring_buffer *rtt_rb = NULL; + struct ring_buffer *events = NULL; + struct ring_buffer *port_rb = NULL; struct netwatcher_bpf *skel; int err; /* Parse command line arguments */ @@ -489,107 +1549,147 @@ int main(int argc, char **argv) { if (err) return err; } - + libbpf_set_print(libbpf_print_fn); /* Cleaner handling of Ctrl-C */ signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); - /* Open load and verify BPF application */ skel = netwatcher_bpf__open(); if (!skel) { fprintf(stderr, "Failed to open BPF skeleton\n"); return 1; } - /* Parameterize BPF code */ - skel->rodata->filter_dport = dport; - skel->rodata->filter_sport = sport; - skel->rodata->all_conn = all_conn; - skel->rodata->err_packet = err_packet; - skel->rodata->extra_conn_info = extra_conn_info; - skel->rodata->layer_time = layer_time; - skel->rodata->http_info = http_info; - skel->rodata->retrans_info = retrans_info; - skel->rodata->udp_info = udp_info; - skel->rodata->net_filter = net_filter; - skel->rodata->kfree_info = kfree_info; - skel->rodata->tcp_info = tcp_info; - skel->rodata->icmp_info = icmp_info; + set_rodata_flags(skel); + set_disable_load(skel); - if(addr_to_func) + if (addr_to_func) readallsym(); - err = netwatcher_bpf__load(skel); if (err) { fprintf(stderr, "Failed to load and verify BPF skeleton\n"); goto cleanup; } - /* Attach tracepoint handler */ - err = netwatcher_bpf__attach(skel); - if (err) { - fprintf(stderr, "Failed to attach BPF skeleton\n"); - goto cleanup; - } - if (udp_info) { - printf("%-20s %-20s %-20s %-20s %-20s %-20s %-20s\n", "saddr", "daddr", "sprot", - "dprot", "udp_time","rx","len"); - } - else if(net_filter) - { - printf("%-20s %-20s %-20s %-20s %-20s %-20s %-20s %-20s %-20s %-20s\n", "saddr", "daddr","dprot", "sprot", - "PreRT","L_IN","FW","PostRT","L_OUT","rx"); - } - else if(kfree_info) - { - printf("%-20s %-20s %-10s %-10s %-9s %-24s %-25s\n", "saddr", "daddr","sprot", "dprot","prot","addr","reason"); - } - else if(icmp_info) - { - printf("%-20s %-20s %-10s %-10s\n", "saddr", "daddr","time","flag"); - } - else if(tcp_info) - { - printf("%-20s %-20s %-20s %-20s %-20s %-20s %-20s \n", "saddr", "daddr","sport","dport","oldstate","newstate","time"); - } - else{ - printf("%-22s %-10s %-10s %-10s %-10s %-10s %-5s %s\n", "SOCK", "SEQ", - "ACK", "MAC_TIME", "IP_TIME", "TRAN_TIME", "RX", "HTTP"); + if (mysql_info) { + strcpy(binary_path, "/usr/sbin/mysqld"); + err = attach_uprobe_mysql(skel); + if (err) { + fprintf(stderr, "failed to attach uprobes\n"); + + goto cleanup; + } + } else if (redis_info||redis_stat) { + strcpy(binary_path, "/usr/bin/redis-server"); + err = attach_uprobe_redis(skel); + if (err) { + fprintf(stderr, "failed to attach uprobes\n"); + + goto cleanup; + } + } else { + err = netwatcher_bpf__attach(skel); + if (err) { + fprintf(stderr, "Failed to attach BPF skeleton\n"); + goto cleanup; + } } - udp_rb =ring_buffer__new(bpf_map__fd(skel->maps.udp_rb), print_udp, NULL, NULL); + enum MonitorMode mode = get_monitor_mode(); + + // print_logo(); + + print_header(mode); + + udp_rb = + ring_buffer__new(bpf_map__fd(skel->maps.udp_rb), print_udp, NULL, NULL); if (!udp_rb) { err = -1; fprintf(stderr, "Failed to create ring buffer(udp)\n"); goto cleanup; } - netfilter_rb =ring_buffer__new(bpf_map__fd(skel->maps.netfilter_rb), print_netfilter, NULL, NULL); + netfilter_rb = ring_buffer__new(bpf_map__fd(skel->maps.netfilter_rb), + print_netfilter, NULL, NULL); if (!netfilter_rb) { err = -1; fprintf(stderr, "Failed to create ring buffer(netfilter)\n"); goto cleanup; } - kfree_rb =ring_buffer__new(bpf_map__fd(skel->maps.kfree_rb), print_kfree, NULL, NULL); + kfree_rb = ring_buffer__new(bpf_map__fd(skel->maps.kfree_rb), print_kfree, + NULL, NULL); if (!kfree_rb) { err = -1; fprintf(stderr, "Failed to create ring buffer(kfree)\n"); goto cleanup; } - icmp_rb =ring_buffer__new(bpf_map__fd(skel->maps.icmp_rb), print_icmptime, NULL, NULL); + icmp_rb = ring_buffer__new(bpf_map__fd(skel->maps.icmp_rb), print_icmptime, + NULL, NULL); if (!icmp_rb) { err = -1; fprintf(stderr, "Failed to create ring buffer(icmp)\n"); goto cleanup; } - tcp_rb =ring_buffer__new(bpf_map__fd(skel->maps.tcp_rb), print_tcpstate, NULL, NULL); + tcp_rb = ring_buffer__new(bpf_map__fd(skel->maps.tcp_rb), print_tcpstate, + NULL, NULL); if (!tcp_rb) { err = -1; fprintf(stderr, "Failed to create ring buffer(tcp)\n"); goto cleanup; } - icmp_rb =ring_buffer__new(bpf_map__fd(skel->maps.icmp_rb), print_icmptime, NULL, NULL); - if (!icmp_rb) { + dns_rb = + ring_buffer__new(bpf_map__fd(skel->maps.dns_rb), print_dns, NULL, NULL); + if (!dns_rb) { err = -1; - fprintf(stderr, "Failed to create ring buffer(icmp)\n"); + fprintf(stderr, "Failed to create ring buffer(dns)\n"); + goto cleanup; + } + trace_rb = ring_buffer__new(bpf_map__fd(skel->maps.trace_rb), print_trace, + NULL, NULL); + if (!trace_rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer(trace)\n"); + goto cleanup; + } + mysql_rb = ring_buffer__new(bpf_map__fd(skel->maps.mysql_rb), print_mysql, + NULL, NULL); + if (!mysql_rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer(trace)\n"); + goto cleanup; + } + redis_rb = ring_buffer__new(bpf_map__fd(skel->maps.redis_rb), print_redis, + NULL, NULL); + if (!redis_rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer(trace)\n"); + goto cleanup; + } + redis_stat_rb = ring_buffer__new(bpf_map__fd(skel->maps.redis_stat_rb), print_redis_stat, + NULL, NULL); + if (!redis_stat_rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer(trace)\n"); + goto cleanup; + } + rtt_rb = + ring_buffer__new(bpf_map__fd(skel->maps.rtt_rb), print_rtt, NULL, NULL); + if (!rtt_rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer(connect_rb)\n"); + goto cleanup; + } + events = + ring_buffer__new(bpf_map__fd(skel->maps.events), print_rst, NULL, NULL); + if (!events) { + err = -1; + fprintf(stderr, "Failed to create ring buffer(rst_rb)\n"); + goto cleanup; + } + + port_rb = ring_buffer__new(bpf_map__fd(skel->maps.port_rb), + print_protocol_count, NULL, NULL); + if (!port_rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer(trace)\n"); goto cleanup; } /* Set up ring buffer polling */ @@ -599,25 +1699,10 @@ int main(int argc, char **argv) { fprintf(stderr, "Failed to create ring buffer(packet)\n"); goto cleanup; } - FILE *err_file = fopen(err_file_path, "w+"); - if (err_file == NULL) { - fprintf(stderr, "Failed to open err.log: (%s)\n", strerror(errno)); - return 0; - } - fclose(err_file); - FILE *packet_file = fopen(packets_file_path, "w+"); - if (packet_file == NULL) { - fprintf(stderr, "Failed to open packets.log: (%s)\n", strerror(errno)); - return 0; - } - fclose(packet_file); - FILE *udp_file = fopen(udp_file_path, "w+"); - if (udp_file == NULL) { - fprintf(stderr, "Failed to open udp.log: (%s)\n", strerror(errno)); - return 0; - } - fclose(udp_file); + open_log_files(); + struct timeval start, end; + gettimeofday(&start, NULL); /* Process events */ while (!exiting) { err = ring_buffer__poll(rb, 100 /* timeout, ms */); @@ -626,6 +1711,14 @@ int main(int argc, char **argv) { err = ring_buffer__poll(kfree_rb, 100 /* timeout, ms */); err = ring_buffer__poll(icmp_rb, 100 /* timeout, ms */); err = ring_buffer__poll(tcp_rb, 100 /* timeout, ms */); + err = ring_buffer__poll(dns_rb, 100 /* timeout, ms */); + err = ring_buffer__poll(trace_rb, 100 /* timeout, ms */); + err = ring_buffer__poll(mysql_rb, 100 /* timeout, ms */); + err = ring_buffer__poll(redis_rb, 100 /* timeout, ms */); + err = ring_buffer__poll(rtt_rb, 100 /* timeout, ms */); + err = ring_buffer__poll(events, 100 /* timeout, ms */); + err = ring_buffer__poll(port_rb, 100 /* timeout, ms */); + err = ring_buffer__poll(redis_stat_rb, 100 /* timeout, ms */); print_conns(skel); sleep(1); /* Ctrl-C will cause -EINTR */ @@ -637,9 +1730,29 @@ int main(int argc, char **argv) { printf("Error polling perf buffer: %d\n", err); break; } - } + gettimeofday(&end, NULL); + if ((end.tv_sec - start.tv_sec) >= 5) { + if (rst_info) { + print_stored_events(); + printf("Total RSTs in the last 5 seconds: %llu\n\n",rst_count); + rst_count = 0; + event_count = 0; + }else if (protocol_count) { + calculate_protocol_usage(proto_stats, 256, 5); + }else if(redis_stat) + { + map_fd = bpf_map__fd(skel->maps.key_count); + if (map_fd < 0) { + perror("Failed to get map FD"); + return 1; + } + print_top_5_keys(); + } + gettimeofday(&start, NULL); + } + } cleanup: netwatcher_bpf__destroy(skel); return err < 0 ? -err : 0; -} \ No newline at end of file +} diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.h index c0955a298..359fa1c98 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.h +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.h @@ -26,6 +26,7 @@ typedef unsigned long long u64; #define ETH_P_IP 0x0800 /* Internet Protocol packet */ #define ETH_P_IPV6 0x86DD /* IPv6 over bluebook */ +#define MAX_SLOTS 27 #ifndef AF_INET #define AF_INET 2 @@ -40,13 +41,28 @@ typedef unsigned long long u64; #define MAX_COMM 16 #define TCP 1 #define UDP 2 +#define MAX_PACKET 1000 +#define MAX_HTTP_HEADER 256 +#define NUM_LAYERS 5 +#define RED_TEXT "\033[31m" +#define RESET_TEXT "\033[0m" +#define GRANULARITY 3 +#define ALPHA 0.2 // 衰减因子 +#define MAXTIME 10000 +#define SLOW_QUERY_THRESHOLD 10000 // +#define ANSI_COLOR_RED "\x1b[31m" +#define ANSI_COLOR_RESET "\x1b[0m" +#define MAX_STACK_DEPTH 128 +#define MAX_EVENTS 1024 +#define CACHEMAXSIZE 5 +typedef u64 stack_trace_t[MAX_STACK_DEPTH]; struct conn_t { - void *sock; // 此tcp连接的 socket 地址 - int pid; // pid - u64 ptid; // 此tcp连接的 ptid(ebpf def) - char comm[MAX_COMM]; // 此tcp连接的 command - u16 family; // 10(AF_INET6):v6 or 2(AF_INET):v4 + void *sock; // 此tcp连接的 socket 地址 + int pid; // pid + u64 ptid; // 此tcp连接的 ptid(ebpf def) + char comm[MAX_COMM]; // 此tcp连接的 command + u16 family; // 10(AF_INET6):v6 or 2(AF_INET):v4 unsigned __int128 saddr_v6; unsigned __int128 daddr_v6; u32 saddr; @@ -55,10 +71,10 @@ struct conn_t { u16 dport; int is_server; // 1: 被动连接 0: 主动连接 - u32 tcp_backlog; // backlog - u32 max_tcp_backlog; // max_backlog - u64 bytes_acked; // 已确认的字节数 - u64 bytes_received; // 已接收的字节数 + u32 tcp_backlog; // backlog + u32 max_tcp_backlog; // max_backlog + u64 bytes_acked; // 已确认的字节数 + u64 bytes_received; // 已接收的字节数 u32 snd_cwnd; // 拥塞窗口大小 u32 rcv_wnd; // 接收窗口大小 @@ -69,25 +85,28 @@ struct conn_t { u32 fastRe; // 快速重传次数 u32 timeout; // 超时重传次数 - u32 srtt; // 平滑往返时间 + u32 srtt; // 平滑往返时间 u64 init_timestamp; // 建立连接时间戳 u64 duration; // 连接已建立时长 }; -#define MAX_PACKET 1000 -#define MAX_HTTP_HEADER 256 - struct pack_t { - int err; // no err(0) invalid seq(1) invalid checksum(2) + int err; // no err(0) invalid seq(1) invalid checksum(2) u64 mac_time; // mac layer 处理时间(us) u64 ip_time; // ip layer 处理时间(us) // u64 tcp_time; // tcp layer 处理时间(us) - u64 tran_time; // tcp layer 处理时间(us) - u32 seq; // the seq num of packet - u32 ack; // the ack num of packet + u64 tran_time; // tcp layer 处理时间(us) + u32 seq; // the seq num of packet + u32 ack; // the ack num of packet u8 data[MAX_HTTP_HEADER]; // 用户层数据 - const void *sock; // 此包tcp连接的 socket 指针 - int rx; // rx packet(1) or tx packet(0) + const void *sock; // 此包tcp连接的 socket 指针 + int rx; // rx packet(1) or tx packet(0) + u32 saddr; + u32 daddr; + unsigned __int128 saddr_v6; + unsigned __int128 daddr_v6; + u16 sport; + u16 dport; }; struct udp_message { @@ -96,11 +115,10 @@ struct udp_message { u16 sport; u16 dport; u64 tran_time; - int rx; + int rx; int len; }; -struct netfilter -{ +struct netfilter { u32 saddr; u32 daddr; u16 sport; @@ -112,31 +130,137 @@ struct netfilter u64 post_routing_time; u32 rx; }; -struct reasonissue -{ +struct reasonissue { u32 saddr; u32 daddr; u16 sport; u16 dport; long location; u16 protocol; - int drop_reason; + int drop_reason; }; -struct icmptime{ +struct icmptime { unsigned int saddr; unsigned int daddr; unsigned long long icmp_tran_time; - unsigned int flag; //0 send 1 rcv + unsigned int flag; // 0 send 1 rcv }; struct tcp_state { - u32 saddr; - u32 daddr; + u32 saddr; + u32 daddr; u16 sport; - u16 dport; - int oldstate; - int newstate; + u16 dport; + int oldstate; + int newstate; u64 time; }; +struct dns_information { + u32 saddr; + u32 daddr; + u16 id; + u16 flags; + u16 qdcount; + u16 ancount; + u16 nscount; + u16 arcount; + char data[64]; + int rx; + int response_count; + int request_count; +}; +struct stacktrace_event { + u32 pid; + u32 cpu_id; + char comm[16]; + signed int kstack_sz; + signed int ustack_sz; + stack_trace_t kstack; + stack_trace_t ustack; +}; +typedef struct mysql_query { + int pid; + int tid; + char comm[20]; + u32 size; + char msql[256]; + u64 duratime; + int count; +} mysql_query; +struct redis_query { + int pid; + int tid; + char comm[20]; + u32 size; + char redis[4][8]; + u64 duratime; + int count; + u64 begin_time; + int argc; +}; +struct redis_stat_query { + int pid; + char comm[20]; + char key[20]; + int key_count; + char value[64]; + int value_type; +}; +struct RTT { + u32 saddr; + u32 daddr; + u64 slots[64]; + u64 latency; + u64 cnt; +}; +struct reset_event_t { + int pid; + char comm[16]; + u16 family; + unsigned __int128 saddr_v6; + unsigned __int128 daddr_v6; + u32 saddr; + u32 daddr; + u16 sport; + u16 dport; + u8 direction; // 0 for send, 1 for receive + u64 count; + u64 timestamp; + u8 state; +}; +struct packet_count { + u64 rx_count; + u64 tx_count; +}; +struct packet_info { + u32 saddr; + u32 daddr; + u16 sport; + u16 dport; + u16 proto; + struct packet_count count; +}; +struct SymbolEntry { + unsigned long addr; + char name[30]; +}; + +static const char *protocol[] = { + [0] = "TCP", + [1] = "UDP", + [2] = "ICMP", + [3] = "UNKNOWN", +}; +static const char *tcp_states[] = { + [1] = "ESTABLISHED", [2] = "SYN_SENT", [3] = "SYN_RECV", + [4] = "FIN_WAIT1", [5] = "FIN_WAIT2", [6] = "TIME_WAIT", + [7] = "CLOSE", [8] = "CLOSE_WAIT", [9] = "LAST_ACK", + [10] = "LISTEN", [11] = "CLOSING", [12] = "NEW_SYN_RECV", + [13] = "UNKNOWN", +}; +struct LayerDelayInfo { + float delay; // 时延数据 + int layer_index; // 层索引 +}; #endif /* __NETWATCHER_H */ \ No newline at end of file diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/packet.bpf.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/packet.bpf.h index e3adf22a4..c75dbea20 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/packet.bpf.h +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/packet.bpf.h @@ -30,9 +30,93 @@ kprobe/tcp_v6_do_rcv kprobe/skb_copy_datagram_iter */ -static __always_inline -int __eth_type_trans(struct sk_buff *skb) -{ +static __always_inline struct packet_count *count_packet(u32 proto, + bool is_tx) { + struct packet_count *count; + struct packet_count initial_count = {0}; + + count = bpf_map_lookup_elem(&proto_stats, &proto); + if (!count) { + initial_count.tx_count = 0; + initial_count.rx_count = 0; + if (bpf_map_update_elem(&proto_stats, &proto, &initial_count, + BPF_ANY)) { + return NULL; + } + count = bpf_map_lookup_elem(&proto_stats, &proto); + if (!count) { + return NULL; + } + } + + if (is_tx) + __sync_fetch_and_add(&count->tx_count, 1); + else + __sync_fetch_and_add(&count->rx_count, 1); + return count; +} + +static __always_inline int sum_protocol(struct sk_buff *skb, bool is_tx) { + const struct ethhdr *eth = (struct ethhdr *)BPF_CORE_READ(skb, data); + u16 proto = BPF_CORE_READ(eth, h_proto); + + struct packet_info *pkt = bpf_ringbuf_reserve(&port_rb, sizeof(*pkt), 0); + if (!pkt) { + return 0; + } + + if (BPF_CORE_READ(eth, h_proto) != __bpf_htons(ETH_P_IP)) { + bpf_ringbuf_discard(pkt, 0); + return 0; + } + + struct iphdr *ip = (struct iphdr *)(BPF_CORE_READ(skb, data) + 14); + if (!ip) { + bpf_ringbuf_discard(pkt, 0); + return 0; + } + + pkt->saddr = BPF_CORE_READ(ip, saddr); + pkt->daddr = BPF_CORE_READ(ip, daddr); + pkt->proto = BPF_CORE_READ(ip, protocol); + + if (pkt->proto == IPPROTO_TCP) { + struct tcphdr *tcp = + (struct tcphdr *)(BPF_CORE_READ(skb, data) + sizeof(struct ethhdr) + + sizeof(struct iphdr)); + pkt->sport = BPF_CORE_READ(tcp, source); + pkt->dport = BPF_CORE_READ(tcp, dest); + pkt->proto = PROTO_TCP; + } else if (pkt->proto == IPPROTO_UDP) { + struct udphdr *udp = + (struct udphdr *)(BPF_CORE_READ(skb, data) + sizeof(struct ethhdr) + + sizeof(struct iphdr)); + pkt->sport = BPF_CORE_READ(udp, source); + pkt->dport = BPF_CORE_READ(udp, dest); + pkt->proto = PROTO_UDP; + } else if (pkt->proto == IPPROTO_ICMP) { + pkt->proto = PROTO_ICMP; + } else { + pkt->proto = PROTO_UNKNOWN; + } + struct packet_count *count = count_packet(pkt->proto, is_tx); + if (count) { + pkt->count.tx_count = count->tx_count; + pkt->count.rx_count = count->rx_count; + } else { + pkt->count.tx_count = 0; + pkt->count.rx_count = 0; + } + + // bpf_printk("pkt: saddr=%u, daddr=%u, proto=%u\n", pkt->saddr, pkt->daddr, + // pkt->proto); bpf_printk("sport=%d, dport=%d\n", pkt->sport, pkt->dport); + // bpf_printk("count_tx=%llu, count_rx=%llu\n", pkt->count.tx_count, + // pkt->count.rx_count); + bpf_ringbuf_submit(pkt, 0); + + return 0; +} +static __always_inline int __eth_type_trans(struct sk_buff *skb) { const struct ethhdr *eth = (struct ethhdr *)BPF_CORE_READ(skb, data); // 读取里面的报文数据 u16 protocol = BPF_CORE_READ(eth, h_proto); // 读取包ID @@ -81,9 +165,7 @@ int __eth_type_trans(struct sk_buff *skb) return 0; } -static __always_inline -int __ip_rcv_core(struct sk_buff *skb) -{ +static __always_inline int __ip_rcv_core(struct sk_buff *skb) { if (!layer_time) { return 0; } @@ -105,9 +187,7 @@ int __ip_rcv_core(struct sk_buff *skb) return 0; } -static __always_inline -int __ip6_rcv_core( struct sk_buff *skb) -{ +static __always_inline int __ip6_rcv_core(struct sk_buff *skb) { if (!layer_time) { return 0; } @@ -128,9 +208,7 @@ int __ip6_rcv_core( struct sk_buff *skb) // bpf_printk("rx enter ipv6 layer.\n"); return 0; } -static __always_inline -int __tcp_v4_rcv(struct sk_buff *skb) -{ +static __always_inline int __tcp_v4_rcv(struct sk_buff *skb) { if (!layer_time) { return 0; } @@ -149,9 +227,7 @@ int __tcp_v4_rcv(struct sk_buff *skb) // bpf_printk("rx enter tcp4 layer.\n"); return 0; } -static __always_inline -int __tcp_v6_rcv(struct sk_buff *skb) -{ +static __always_inline int __tcp_v6_rcv(struct sk_buff *skb) { if (!layer_time) { return 0; } @@ -171,9 +247,8 @@ int __tcp_v6_rcv(struct sk_buff *skb) // bpf_printk("rx enter tcp6 layer.\n"); return 0; } -static __always_inline -int __tcp_v4_do_rcv(struct sock *sk,struct sk_buff *skb) -{ +static __always_inline int __tcp_v4_do_rcv(struct sock *sk, + struct sk_buff *skb) { if (sk == NULL || skb == NULL) return 0; struct conn_t *conn = bpf_map_lookup_elem(&conns_info, &sk); @@ -201,9 +276,8 @@ int __tcp_v4_do_rcv(struct sock *sk,struct sk_buff *skb) return 0; } -static __always_inline -int __tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) -{ +static __always_inline int __tcp_v6_do_rcv(struct sock *sk, + struct sk_buff *skb) { if (sk == NULL || skb == NULL) return 0; // bpf_printk("rx enter tcp6_do_rcv. \n"); @@ -233,9 +307,7 @@ int __tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) return 0; } -static __always_inline -int __skb_copy_datagram_iter(struct sk_buff *skb) -{ +static __always_inline int __skb_copy_datagram_iter(struct sk_buff *skb) { if (skb == NULL) return 0; __be16 protocol = BPF_CORE_READ(skb, protocol); // 读取skb协议字段 @@ -276,6 +348,10 @@ int __skb_copy_datagram_iter(struct sk_buff *skb) // bpf_printk("rx enter app layer.\n"); PACKET_INIT_WITH_COMMON_INFO + packet->saddr = pkt_tuple.saddr; + packet->daddr = pkt_tuple.daddr; + packet->sport = pkt_tuple.sport; + packet->dport = pkt_tuple.dport; if (layer_time) { packet->mac_time = tinfo->ip_time - tinfo->mac_time; @@ -292,8 +368,7 @@ int __skb_copy_datagram_iter(struct sk_buff *skb) int doff = BPF_CORE_READ_BITFIELD_PROBED(tcp, doff); // 得用bitfield_probed // 读取tcp头部中的数据偏移字段 - u8 *user_data = - (u8 *)((u8 *)tcp + (doff * 4)); + u8 *user_data = (u8 *)((u8 *)tcp + (doff * 4)); // 计算tcp的负载开始位置就是tcp头部之后的数据,将tcp指针指向tcp头部位置将其转换成unsigned // char类型 // doff * @@ -317,9 +392,8 @@ int __skb_copy_datagram_iter(struct sk_buff *skb) kprobe/dev_queue_xmit kprobe/dev_hard_start_xmit */ -static __always_inline -int __tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) -{ +static __always_inline int __tcp_sendmsg(struct sock *sk, struct msghdr *msg, + size_t size) { struct conn_t *conn = bpf_map_lookup_elem(&conns_info, &sk); if (conn == NULL) { return 0; @@ -389,7 +463,7 @@ int __tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) // TX HTTP info if (http_info) { - u8 *user_data = BPF_CORE_READ(msg, msg_iter.iov, iov_base); + u8 *user_data = GET_USER_DATA(msg); tinfo = (struct ktime_info *)bpf_map_lookup_or_try_init( ×tamps, &pkt_tuple, &zero); if (tinfo == NULL) { @@ -399,9 +473,8 @@ int __tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) } return 0; } -static __always_inline -int __ip_queue_xmit(struct sock *sk, struct sk_buff *skb) -{ +static __always_inline int __ip_queue_xmit(struct sock *sk, + struct sk_buff *skb) { if (!layer_time) { return 0; } @@ -427,11 +500,11 @@ int __ip_queue_xmit(struct sock *sk, struct sk_buff *skb) } tinfo->ip_time = bpf_ktime_get_ns() / 1000; } + return 0; } -static __always_inline -int __inet6_csk_xmit(struct sock *sk, struct sk_buff *skb) -{ +static __always_inline int __inet6_csk_xmit(struct sock *sk, + struct sk_buff *skb) { if (!layer_time) { return 0; } @@ -468,9 +541,7 @@ int __inet6_csk_xmit(struct sock *sk, struct sk_buff *skb) } return 0; } -static __always_inline -int dev_queue_xmit(struct sk_buff *skb) -{ +static __always_inline int dev_queue_xmit(struct sk_buff *skb) { if (!layer_time) { return 0; } @@ -487,9 +558,6 @@ int dev_queue_xmit(struct sk_buff *skb) struct iphdr *ip = skb_to_iphdr(skb); get_pkt_tuple(&pkt_tuple, ip, tcp); - // FILTER_DPORT - // FILTER_SPORT - if ((tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple)) == NULL) { return 0; } @@ -506,9 +574,7 @@ int dev_queue_xmit(struct sk_buff *skb) } return 0; } -static __always_inline -int __dev_hard_start_xmit(struct sk_buff *skb) -{ +static __always_inline int __dev_hard_start_xmit(struct sk_buff *skb) { const struct ethhdr *eth = (struct ethhdr *)BPF_CORE_READ(skb, data); u16 protocol = BPF_CORE_READ(eth, h_proto); struct tcphdr *tcp = skb_to_tcphdr(skb); @@ -546,22 +612,27 @@ int __dev_hard_start_xmit(struct sk_buff *skb) return 0; } PACKET_INIT_WITH_COMMON_INFO + packet->saddr = pkt_tuple.saddr; + packet->daddr = pkt_tuple.daddr; + packet->sport = pkt_tuple.sport; + packet->dport = pkt_tuple.dport; // 记录各层的时间差值 if (layer_time) { packet->tran_time = tinfo->ip_time - tinfo->tran_time; packet->ip_time = tinfo->mac_time - tinfo->ip_time; - packet->mac_time =tinfo->qdisc_time -tinfo->mac_time; // 队列纪律层,处于网络协议栈最底层,负责实际数据传输与接收 + packet->mac_time = + tinfo->qdisc_time - + tinfo + ->mac_time; // 队列纪律层,处于网络协议栈最底层,负责实际数据传输与接收 } - packet->rx = 0; // 发送一个数据包 // TX HTTP Info if (http_info) { bpf_probe_read_str(packet->data, sizeof(packet->data), tinfo->data); - bpf_printk("%s", packet->data); + // bpf_printk("%s", packet->data); } bpf_ringbuf_submit(packet, 0); return 0; } - diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/redis.bpf.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/redis.bpf.h new file mode 100644 index 000000000..dc0aa533e --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/redis.bpf.h @@ -0,0 +1,167 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: blown.away@qq.com +// redis + +#include "common.bpf.h" +#include "redis_helper.bpf.h" +#define MAXEPOLL 5 +static __always_inline int __handle_redis_start(struct pt_regs *ctx) { + if(!redis_info) return 0; + struct client *cli = (struct client *)PT_REGS_PARM1(ctx); + struct redis_query start={}; + void *ptr; + char name[100]=""; + int argv_len; + bpf_probe_read(&start.argc, sizeof(start.argc), &cli->argc); + robj **arg0; + robj *arg1; + bpf_probe_read(&arg0, sizeof(arg0), &cli->argv); + bpf_probe_read(&arg1, sizeof(arg1), &arg0[0]); + for(int i=0;iptr); + bpf_probe_read_str(&start.redis[i], sizeof(start.redis[i]), ptr); + } + pid_t pid = bpf_get_current_pid_tgid() >> 32; + u64 start_time = bpf_ktime_get_ns() / 1000; + start.begin_time=start_time; + bpf_map_update_elem(&redis_time, &pid, &start, BPF_ANY); + return 0; +} + +static __always_inline int __handle_redis_end(struct pt_regs *ctx) { + if(!redis_info) return 0; + pid_t pid = bpf_get_current_pid_tgid() >> 32; + struct redis_query *start; + u64 end_time = bpf_ktime_get_ns() / 1000; + start = bpf_map_lookup_elem(&redis_time, &pid); + if (!start) { + return 0; + } + struct redis_query *message = bpf_ringbuf_reserve(&redis_rb, sizeof(*message), 0); + if (!message) { + return 0; + } + message->pid = pid; + message->argc = start->argc; + bpf_get_current_comm(&message->comm, sizeof(message->comm)); + for(int i=0;iargc&&iredis[i], sizeof(message->redis[i]), start->redis[i]); + } + bpf_probe_read_str(&message->redis, sizeof(start->redis), start->redis); + message->duratime = end_time - start->begin_time; + bpf_ringbuf_submit(message, 0); + return 0; +} +static __always_inline int __handle_redis_key(struct pt_regs *ctx) { + if(!redis_stat) return 0; + robj *key_obj = (robj *)PT_REGS_PARM2(ctx); + char redis_key[256]; + u32 *count; + u32 initial_count = 1; + + if (!key_obj) + return 0; + + robj local_key_obj; + if (bpf_probe_read_user(&local_key_obj, sizeof(local_key_obj), key_obj) != 0) { + bpf_printk("Failed to read local_key_obj\n"); + return 0; + } + + if (!local_key_obj.ptr) { + bpf_printk("local_key_obj.ptr is null\n"); + return 0; + } + + int ret; + ret = bpf_probe_read_user_str(redis_key, sizeof(redis_key), local_key_obj.ptr); + if (ret <= 0) { + bpf_printk("Read string failed: %d\n", ret); + return 0; + } + + // 打印读取到的键值 + bpf_printk("Read key: %s\n", redis_key); + + // 查找或更新键的计数 + count = bpf_map_lookup_elem(&key_count, redis_key); + if (count) { + //bpf_printk("Found key, incrementing count\n"); + // 如果已经存在,增加计数值 + (*count)++; + bpf_map_update_elem(&key_count, redis_key, count, BPF_ANY); + } else { + //bpf_printk("Key not found, initializing count\n"); + // 如果不存在,初始化计数值为 1 + bpf_map_update_elem(&key_count, redis_key, &initial_count, BPF_ANY); + } + + // 打印调试信息 + struct redis_stat_query *message = bpf_ringbuf_reserve(&redis_stat_rb, sizeof(*message), 0); + if (!message) { + return 0; + } + message->pid=bpf_get_current_pid_tgid() >> 32; + bpf_get_current_comm(&message->comm, sizeof(message->comm)); + memcpy(message->key, redis_key, sizeof(message->key)); + message->key_count=count ? *count : initial_count; + message->value_type=0; + memset(message->value, 0, sizeof(message->value)); + bpf_printk("Key: %s\n", message->key); + bpf_printk("Count: %d\n", message->key_count); + bpf_ringbuf_submit(message, 0); + + return 0; +} +static __always_inline int __handle_redis_value(struct pt_regs *ctx) { + if(!redis_stat) return 0; + robj *key_obj = (robj *)PT_REGS_PARM2(ctx); + int ret; + char redis_value[64]; + if (!key_obj) + return 0; + robj local_key_obj; + if (bpf_probe_read_user(&local_key_obj, sizeof(local_key_obj), key_obj) != 0) { + bpf_printk("Failed to read local_key_obj\n"); + return 0; + } + if (!local_key_obj.ptr) { + bpf_printk("local_key_obj.ptr is null\n"); + return 0; + } + ret = bpf_probe_read_user_str(redis_value, sizeof(redis_value), local_key_obj.ptr); + if (ret <= 0) { + bpf_printk("Read string failed: %d\n", ret); + return 0; + } + struct redis_stat_query *message = bpf_ringbuf_reserve(&redis_stat_rb, sizeof(*message), 0); + if (!message) { + return 0; + } + message->pid=bpf_get_current_pid_tgid() >> 32; + bpf_get_current_comm(&message->comm, sizeof(message->comm)); + memset(message->key, 0, sizeof(message->key)); + message->key_count=0; + message->value_type=local_key_obj.type; + memcpy(message->value, redis_value, sizeof(message->value)); + bpf_printk("Value: %s\n", message->value); + bpf_printk("type: %d\n", message->value_type); + bpf_ringbuf_submit(message, 0); + return 0; +} \ No newline at end of file diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/redis_helper.bpf.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/redis_helper.bpf.h new file mode 100644 index 000000000..21f2031f7 --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/redis_helper.bpf.h @@ -0,0 +1,55 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: blown.away@qq.com +// +// netwatcher libbpf 内核<->用户 传递信息相关结构体 + +#ifndef __REDIS_HELPER_BPF_H +#define __REDIS_HELPER_BPF_H + +#include "netwatcher.h" +#include "vmlinux.h" +#include +#include +#include +#include +#include +#include + +#define LRU_BITS 24 +typedef struct redisObject { + unsigned type:4; + unsigned encoding:4; + unsigned lru:24; + int refcount; + void *ptr; +} robj; + +struct client { + u64 id; /* Client incremental unique ID. */ + u64 conn; + int resp; /* RESP protocol version. Can be 2 or 3. */ + u64 db; /* Pointer to currently SELECTed DB. */ + robj *name; /* As set by CLIENT SETNAME. */ + char* querybuf; /* Buffer we use to accumulate client queries. */ + unsigned long qb_pos; /* The position we have read in querybuf. */ + char* pending_querybuf; + unsigned long querybuf_peak; /* Recent (100ms or more) peak of querybuf size. */ + int argc; /* Num of arguments of current command. */ + robj **argv; /* Arguments of current command. */ + unsigned long argv_len_sum; /* Size of argv array (may be more than argc) */ +}; + +#endif diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/tcp.bpf.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/tcp.bpf.h index 0d0bb5f70..abcc05698 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/tcp.bpf.h +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/tcp.bpf.h @@ -13,12 +13,11 @@ // limitations under the License. // // author: blown.away@qq.com +// netwatcher libbpf tcp #include "common.bpf.h" -static __always_inline -int __inet_csk_accept(struct sock *sk) -{ +static __always_inline int __inet_csk_accept(struct sock *sk) { if (sk == NULL) { // newsk is null // bpf_printk("inet_accept_ret err: newsk is null\n"); return 0; @@ -28,11 +27,11 @@ int __inet_csk_accept(struct sock *sk) CONN_INIT // 初始化conn_t结构中基本信息 conn.is_server = 1; - FILTER_DPORT // 过滤目标端口 + FILTER_DPORT // 过滤目标端口 - FILTER_SPORT // 过滤源端口 - - CONN_ADD_ADDRESS // conn_t结构中增加地址信息 + FILTER_SPORT // 过滤源端口 + + CONN_ADD_ADDRESS // conn_t结构中增加地址信息 // 更新/插入conns_info中的键值对 int err = bpf_map_update_elem(&conns_info, &sk, &conn, BPF_ANY); @@ -44,9 +43,7 @@ int __inet_csk_accept(struct sock *sk) return 0; } -static __always_inline -int __tcp_v4_connect(const struct sock *sk) -{ +static __always_inline int __tcp_v4_connect(const struct sock *sk) { u64 ptid = bpf_get_current_pid_tgid(); // 获取当前pid int err = bpf_map_update_elem(&sock_stores, &ptid, &sk, BPF_ANY); // 更新/插入sock_stores中的键值对 @@ -57,9 +54,7 @@ int __tcp_v4_connect(const struct sock *sk) return 0; } -static __always_inline -int __tcp_v4_connect_exit(int ret) -{ +static __always_inline int __tcp_v4_connect_exit(int ret) { u64 ptid = bpf_get_current_pid_tgid(); // 获取当前pid struct sock **skp = bpf_map_lookup_elem(&sock_stores, &ptid); // 获得sock_stores中ptid对应的*sk 用skp指向 @@ -74,13 +69,13 @@ int __tcp_v4_connect_exit(int ret) } struct sock *sk = *skp; CONN_INIT // 初始化conn_t结构中基本信息 - conn.is_server = 0; // 主动连接 + conn.is_server = 0; // 主动连接 - FILTER_DPORT // 过滤目标端口 + FILTER_DPORT // 过滤目标端口 - FILTER_SPORT // 过滤源端口 + FILTER_SPORT // 过滤源端口 - CONN_ADD_ADDRESS // conn_t结构中增加地址信息 + CONN_ADD_ADDRESS // conn_t结构中增加地址信息 long err = bpf_map_update_elem(&conns_info, &sk, &conn, BPF_ANY); // 更新conns_info中sk对应的conn @@ -90,9 +85,7 @@ int __tcp_v4_connect_exit(int ret) return 0; } -static __always_inline -int __tcp_v6_connect(const struct sock *sk) -{ +static __always_inline int __tcp_v6_connect(const struct sock *sk) { u64 pid = bpf_get_current_pid_tgid(); // 获取pid int err = bpf_map_update_elem(&sock_stores, &pid, &sk, BPF_ANY); // 更新sock_stores中对应pid对应的sk @@ -102,9 +95,7 @@ int __tcp_v6_connect(const struct sock *sk) return 0; } -static __always_inline -int __tcp_v6_connect_exit(int ret) -{ +static __always_inline int __tcp_v6_connect_exit(int ret) { u64 ptid = bpf_get_current_pid_tgid(); // 获取pid struct sock **skp = bpf_map_lookup_elem(&sock_stores, &ptid); // 获得sock_stores中ptid对应的*sk 用skp指向 @@ -118,15 +109,15 @@ int __tcp_v6_connect_exit(int ret) struct sock *sk = *skp; CONN_INIT // 初始化conn_t结构中基本信息 - conn.is_server = 0; // 主动连接 + conn.is_server = 0; // 主动连接 - FILTER_DPORT // 过滤目标端口 + FILTER_DPORT // 过滤目标端口 - FILTER_SPORT // 过滤源端口 + FILTER_SPORT // 过滤源端口 - CONN_ADD_ADDRESS // conn_t结构中增加地址信息 + CONN_ADD_ADDRESS // conn_t结构中增加地址信息 - long err = bpf_map_update_elem(&conns_info, &sk, &conn, BPF_ANY); + long err = bpf_map_update_elem(&conns_info, &sk, &conn, BPF_ANY); // 更新conns_info中sk对应的conn if (err) { return 0; @@ -134,9 +125,7 @@ int __tcp_v6_connect_exit(int ret) // bpf_printk("tcp_v4_connect_exit update sk: %p.\n", sk); return 0; } -static __always_inline -int __tcp_set_state( struct sock *sk, int state) -{ +static __always_inline int __tcp_set_state(struct sock *sk, int state) { if (all_conn) { return 0; } @@ -151,9 +140,8 @@ int __tcp_set_state( struct sock *sk, int state) } // receive error packet -static __always_inline -int __tcp_validate_incoming(struct sock *sk, struct sk_buff *skb) -{ +static __always_inline int __tcp_validate_incoming(struct sock *sk, + struct sk_buff *skb) { if (!err_packet) { return 0; } @@ -209,9 +197,7 @@ int __tcp_validate_incoming(struct sock *sk, struct sk_buff *skb) bpf_ringbuf_submit(packet, 0); return 0; } -static __always_inline -int skb_checksum_complete(int ret) -{ +static __always_inline int skb_checksum_complete(int ret) { if (!err_packet) { return 0; } @@ -242,9 +228,7 @@ int skb_checksum_complete(int ret) return 0; } ////retrans packet -static __always_inline -int __tcp_enter_recovery(struct sock *sk) -{ +static __always_inline int __tcp_enter_recovery(struct sock *sk) { if (!retrans_info) { return 0; } @@ -258,9 +242,7 @@ int __tcp_enter_recovery(struct sock *sk) return 0; } -static __always_inline -int __tcp_enter_loss(struct sock *sk) -{ +static __always_inline int __tcp_enter_loss(struct sock *sk) { if (!retrans_info) { return 0; } @@ -271,9 +253,8 @@ int __tcp_enter_loss(struct sock *sk) conn->timeout += 1; return 0; } -static __always_inline -int __handle_set_state(struct trace_event_raw_inet_sock_set_state *ctx) -{ +static __always_inline int +__handle_set_state(struct trace_event_raw_inet_sock_set_state *ctx) { if (ctx->protocol != IPPROTO_TCP) return 0; @@ -281,31 +262,30 @@ int __handle_set_state(struct trace_event_raw_inet_sock_set_state *ctx) __u64 *before_time, new_time, time; before_time = bpf_map_lookup_elem(&tcp_state, &sk); - new_time= bpf_ktime_get_ns(); + new_time = bpf_ktime_get_ns(); if (!before_time) time = 0; else time = (new_time - *before_time) / 1000; - struct tcpstate tcpstate = {}; tcpstate.oldstate = ctx->oldstate; tcpstate.newstate = ctx->newstate; tcpstate.family = ctx->family; tcpstate.sport = ctx->sport; tcpstate.dport = ctx->dport; - bpf_probe_read_kernel(&tcpstate.saddr, sizeof(tcpstate.saddr), &sk->__sk_common.skc_rcv_saddr); - bpf_probe_read_kernel(&tcpstate.daddr, sizeof(tcpstate.daddr), &sk->__sk_common.skc_daddr); + bpf_probe_read_kernel(&tcpstate.saddr, sizeof(tcpstate.saddr), + &sk->__sk_common.skc_rcv_saddr); + bpf_probe_read_kernel(&tcpstate.daddr, sizeof(tcpstate.daddr), + &sk->__sk_common.skc_daddr); tcpstate.time = time; - //bpf_printk(" %d %d %d %d %d %d ",tcpstate.saddr,tcpstate.sport,tcpstate.daddr,tcpstate.dport, - // tcpstate.oldstate,tcpstate.newstate,tcpstate.time); if (ctx->newstate == TCP_CLOSE) bpf_map_delete_elem(&tcp_state, &sk); else bpf_map_update_elem(&tcp_state, &sk, &new_time, BPF_ANY); - struct tcp_state *message; + struct tcp_state *message; message = bpf_ringbuf_reserve(&tcp_rb, sizeof(*message), 0); - if(!message){ + if (!message) { return 0; } message->saddr = tcpstate.saddr; @@ -314,8 +294,151 @@ int __handle_set_state(struct trace_event_raw_inet_sock_set_state *ctx) message->dport = tcpstate.dport; message->oldstate = tcpstate.oldstate; message->newstate = tcpstate.newstate; - message->time = tcpstate.time; + message->time = tcpstate.time; + bpf_printk("Dport:%d time:%d", tcpstate.dport, tcpstate.time); bpf_ringbuf_submit(message, 0); return 0; +} + +static __always_inline int __tcp_rcv_established(struct sock *sk, + struct sk_buff *skb) { + const struct inet_sock *inet = (struct inet_sock *)(sk); + struct tcp_sock *ts; + struct hist *histp; + u64 slot; + u32 srtt; + struct iphdr *ip = skb_to_iphdr(skb); + struct tcphdr *tcp = skb_to_tcphdr(skb); + struct packet_tuple pkt_tuple = {0}; + get_pkt_tuple(&pkt_tuple, ip, tcp); + // INIT_PACKET_TCP_TUPLE(sk, pkt_tuple); + struct ip_packet key = {.saddr = pkt_tuple.saddr, .daddr = pkt_tuple.daddr}; + + histp = bpf_map_lookup_elem(&hists, &key); + if (!histp) { + // 初始化值 + struct hist zero = {}; + bpf_map_update_elem(&hists, &key, &zero, BPF_ANY); + histp = bpf_map_lookup_elem(&hists, &key); + if (!histp) + return 0; // 如果仍然查找失败,则返回 + } + ts = (struct tcp_sock *)(sk); + + // 读取并处理SRTT(平滑往返时间) + srtt = BPF_CORE_READ(ts, srtt_us) >> 3; + // 计算对数值,根据得到的结果决定数据应该归入直方图的哪个槽位 + slot = log2l(srtt); + if (slot >= MAX_SLOTS) + slot = MAX_SLOTS - 1; // 确保槽位置不超过最大槽数 + + // 更新 + __sync_fetch_and_add(&histp->slots[slot], 1); + __sync_fetch_and_add(&histp->latency, srtt); + __sync_fetch_and_add(&histp->cnt, 1); + + struct RTT *message; + message = bpf_ringbuf_reserve(&rtt_rb, sizeof(*message), 0); + if (!message) { + return 0; + } + message->saddr = pkt_tuple.saddr; + message->daddr = pkt_tuple.daddr; + // bpf_printk("Saddr:%u Daddr:%u", pkt_tuple.saddr, pkt_tuple.daddr); + bpf_probe_read_kernel(message->slots, sizeof(message->slots), histp->slots); + message->latency = histp->latency; + message->cnt = histp->cnt; + // bpf_printk("Updating histogram: latency %llu, cnt %llu, slot %llu, + // slot_count %llu", histp->latency, histp->cnt, slot, histp->slots[slot]); + bpf_ringbuf_submit(message, 0); + return 0; +} + +static __always_inline int ret(void *ctx, u8 direction, u16 sport, + u16 dport) { + struct reset_event_t *message = + bpf_ringbuf_reserve(&events, sizeof(*message), 0); + if (!message) + return 0; + + message->pid = bpf_get_current_pid_tgid() >> 32; + bpf_get_current_comm(&message->comm, sizeof(message->comm)); + + struct sock *sk = (struct sock *)ctx; + message->family = BPF_CORE_READ(sk, __sk_common.skc_family); + message->timestamp = bpf_ktime_get_ns(); + if (message->family == AF_INET) { + if (direction == 0) { // Send + message->saddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr); + message->daddr = BPF_CORE_READ(sk, __sk_common.skc_daddr); + } else { // Receive + message->saddr = BPF_CORE_READ(sk, __sk_common.skc_daddr); + message->daddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr); + } + message->saddr_v6 = 0; + message->daddr_v6 = 0; + } else if (message->family == AF_INET6) { + if (direction == 0) { // Send + bpf_probe_read_kernel( + &message->saddr_v6, sizeof(message->saddr_v6), + &sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); + bpf_probe_read_kernel( + &message->daddr_v6, sizeof(message->daddr_v6), + &sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32); + } else { // Receive + bpf_probe_read_kernel( + &message->saddr_v6, sizeof(message->saddr_v6), + &sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32); + bpf_probe_read_kernel( + &message->daddr_v6, sizeof(message->daddr_v6), + &sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); + } + + message->saddr = 0; + message->daddr = 0; + } + + if (direction == 0) { // Send + message->sport = bpf_ntohs(sport); + message->dport = bpf_ntohs(dport); + } else { // Receive + message->sport = bpf_ntohs(dport); + message->dport = bpf_ntohs(sport); + } + message->direction = direction; + + // 增加 RST 计数 + u32 pid = message->pid; + u64 *count = bpf_map_lookup_elem(&counters, &pid); + if (count) { + *count += 1; + } else { + u64 initial_count = 1; + bpf_map_update_elem(&counters, &pid, &initial_count, BPF_ANY); + count = &initial_count; + } + message->count = *count; + + bpf_ringbuf_submit(message, 0); + + return 0; +} +static __always_inline int +__handle_send_reset(struct trace_event_raw_tcp_send_reset *ctx) { + struct sock *sk = (struct sock *)ctx->skaddr; + if (!sk) + return 0; + // bpf_printk("Send reset: sport=%u, dport=%u\n", ctx->sport, ctx->dport); + return ret((void *)ctx->skaddr, 0, ctx->sport, ctx->dport); +} + +static __always_inline int +__handle_receive_reset(struct trace_event_raw_tcp_receive_reset *ctx) { + struct sock *sk = (struct sock *)ctx->skaddr; + if (!sk) + return 0; + // bpf_printk("Receive reset: sport=%u, dport=%u\n", ctx->sport, + // ctx->dport); + return ret((void *)ctx->skaddr, 1, ctx->sport, ctx->dport); } \ No newline at end of file diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/udp.bpf.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/udp.bpf.h index f64cc7023..0b38ebb40 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/udp.bpf.h +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/udp.bpf.h @@ -16,15 +16,14 @@ #include "common.bpf.h" -static __always_inline -int __udp_rcv(struct sk_buff *skb) -{ - if (!udp_info||skb == NULL) +static __always_inline int __udp_rcv(struct sk_buff *skb) { + if (!udp_info || skb == NULL) return 0; struct iphdr *ip = skb_to_iphdr(skb); struct udphdr *udp = skb_to_udphdr(skb); struct packet_tuple pkt_tuple = {0}; get_udp_pkt_tuple(&pkt_tuple, ip, udp); + FILTER struct ktime_info *tinfo, zero = {0}; tinfo = (struct ktime_info *)bpf_map_lookup_or_try_init(×tamps, &pkt_tuple, &zero); @@ -34,19 +33,15 @@ int __udp_rcv(struct sk_buff *skb) tinfo->tran_time = bpf_ktime_get_ns() / 1000; return 0; } -static __always_inline -int udp_enqueue_schedule_skb(struct sock *sk,struct sk_buff *skb) -{ - if (!udp_info||skb == NULL) +static __always_inline int udp_enqueue_schedule_skb(struct sock *sk, + struct sk_buff *skb) { + if (!udp_info || skb == NULL) return 0; struct iphdr *ip = skb_to_iphdr(skb); struct udphdr *udp = skb_to_udphdr(skb); struct packet_tuple pkt_tuple = {0}; - pkt_tuple.daddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr); - pkt_tuple.saddr = BPF_CORE_READ(sk, __sk_common.skc_daddr); - pkt_tuple.dport = BPF_CORE_READ(sk, __sk_common.skc_num); - pkt_tuple.sport = __bpf_ntohs(BPF_CORE_READ(sk, __sk_common.skc_dport)); - pkt_tuple.tran_flag = UDP ; + get_udp_pkt_tuple(&pkt_tuple, ip, udp); + FILTER struct ktime_info *tinfo, zero = {0}; tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple); if (tinfo == NULL) { @@ -59,71 +54,153 @@ int udp_enqueue_schedule_skb(struct sock *sk,struct sk_buff *skb) if (!message) { return 0; } - message->saddr = pkt_tuple.saddr; - message->daddr = pkt_tuple.daddr; - message->dport = pkt_tuple.sport; - message->sport = pkt_tuple.dport; + message->saddr = pkt_tuple.saddr; + message->daddr = pkt_tuple.daddr; + message->dport = pkt_tuple.dport; + message->sport = pkt_tuple.sport; message->tran_time = bpf_ktime_get_ns() / 1000 - tinfo->tran_time; - message->rx=1;//收包 - message->len=__bpf_ntohs(BPF_CORE_READ(udp,len)); + message->rx = 1; // 收包 + message->len = __bpf_ntohs(BPF_CORE_READ(udp, len)); bpf_ringbuf_submit(message, 0); return 0; } - -static __always_inline -int __udp_send_skb(struct sk_buff *skb) -{ - if (!udp_info||skb==NULL) +static __always_inline int __udp_send_skb(struct sk_buff *skb) { + if (!udp_info || skb == NULL) return 0; - struct iphdr *ip = skb_to_iphdr(skb); - struct udphdr *udp = skb_to_udphdr(skb); struct packet_tuple pkt_tuple = {0}; - get_udp_pkt_tuple(&pkt_tuple, ip, udp); + struct sock *sk = BPF_CORE_READ(skb, sk); + u16 dport = BPF_CORE_READ(sk, __sk_common.skc_dport); + u16 sport = BPF_CORE_READ(sk, __sk_common.skc_num); + pkt_tuple.saddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr); // 源ip + pkt_tuple.daddr = BPF_CORE_READ(sk, __sk_common.skc_daddr); // 目的ip + pkt_tuple.sport = sport; // 源端口 + pkt_tuple.dport = __bpf_ntohs(dport); // 目的端口并进行字节序转换 + pkt_tuple.tran_flag = UDP; + FILTER struct ktime_info *tinfo, zero = {0}; + bpf_printk("udp_send_skb%d %d %d %d", pkt_tuple.saddr, pkt_tuple.daddr, + pkt_tuple.sport, pkt_tuple.dport); tinfo = (struct ktime_info *)bpf_map_lookup_or_try_init(×tamps, - &pkt_tuple, &zero); + &pkt_tuple, &zero); if (tinfo == NULL) { return 0; } tinfo->tran_time = bpf_ktime_get_ns() / 1000; - return 0; } -static __always_inline -int __ip_send_skb(struct sk_buff *skb) -{ - if (!udp_info||skb == NULL) +static __always_inline int __ip_send_skb(struct sk_buff *skb) { + if (!udp_info || skb == NULL) return 0; struct iphdr *ip = skb_to_iphdr(skb); struct udphdr *udp = skb_to_udphdr(skb); struct packet_tuple pkt_tuple = {0}; get_udp_pkt_tuple(&pkt_tuple, ip, udp); - // bpf_printk("%d %d",pkt_tuple.saddr,pkt_tuple.daddr); - struct sock *sk = BPF_CORE_READ(skb, sk); - pkt_tuple.saddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr); - pkt_tuple.daddr = BPF_CORE_READ(sk, __sk_common.skc_daddr); - pkt_tuple.sport = BPF_CORE_READ(sk, __sk_common.skc_num); - pkt_tuple.dport = __bpf_ntohs(BPF_CORE_READ(sk, __sk_common.skc_dport)); - pkt_tuple.tran_flag = UDP; + FILTER struct ktime_info *tinfo, zero = {0}; tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple); if (tinfo == NULL) { return 0; } struct udp_message *message; - struct udp_message *udp_message =bpf_map_lookup_elem(×tamps,&pkt_tuple); + struct udp_message *udp_message = + bpf_map_lookup_elem(×tamps, &pkt_tuple); message = bpf_ringbuf_reserve(&udp_rb, sizeof(*message), 0); if (!message) { return 0; } + udp = skb_to_udphdr(skb); message->tran_time = bpf_ktime_get_ns() / 1000 - tinfo->tran_time; - message->saddr = pkt_tuple.saddr; - message->daddr = pkt_tuple.daddr; - message->sport = pkt_tuple.sport; - message->dport = pkt_tuple.dport; - message->rx=0;//发包 - message->len=__bpf_ntohs(BPF_CORE_READ(udp,len)); + message->saddr = pkt_tuple.saddr; + message->daddr = pkt_tuple.daddr; + message->sport = pkt_tuple.sport; + message->dport = pkt_tuple.dport; + message->rx = 0; // 发包 + message->len = __bpf_ntohs(BPF_CORE_READ(udp, len)); + bpf_ringbuf_submit(message, 0); + return 0; +} +static __always_inline int process_dns_packet(struct sk_buff *skb, int rx) { + if (skb == NULL) + return 0; + u16 QR_flags; + u64 *count_ptr, response_count = 0, request_count = 0; + struct sock *sk = BPF_CORE_READ(skb, sk); + INIT_PACKET_UDP_TUPLE(sk, pkt_tuple); + // 使用saddr、daddr作为key + struct dns key = {.saddr = pkt_tuple.saddr, .daddr = pkt_tuple.daddr}; + + if ((pkt_tuple.sport != 53) && (pkt_tuple.dport != 53)) + return 0; + + struct dns_information *message = + bpf_ringbuf_reserve(&dns_rb, sizeof(*message), 0); + if (!message) + return 0; + + // 计算dns数据开始偏移 传输层头部的位置加上 UDP 头部的大小 + u32 dns_offset = + BPF_CORE_READ(skb, transport_header) + sizeof(struct udphdr); + struct dns_query query; + // dns头部位置 + bpf_probe_read_kernel(&query.header, sizeof(query.header), + BPF_CORE_READ(skb, head) + dns_offset); + // dns数据部分 + bpf_probe_read_kernel(message->data, sizeof(message->data), + BPF_CORE_READ(skb, head) + dns_offset + + sizeof(struct dns_header)); + QR_flags = __bpf_ntohs(query.header.flags); + /* + 1000 0000 0000 0000 + &运算提取最高位QR, QR=1 Response QR=0 Request + */ + if (QR_flags & 0x8000) { // 响应 + count_ptr = bpf_map_lookup_elem(&dns_response_count, &key); + if (count_ptr) { + response_count = *count_ptr + 1; + } else { + response_count = 1; + } + bpf_map_update_elem(&dns_response_count, &key, &response_count, + BPF_ANY); + // 保留映射中的请求计数值 + count_ptr = bpf_map_lookup_elem(&dns_request_count, &key); + if (count_ptr) { + request_count = *count_ptr; + } + } else { // 请求 + count_ptr = bpf_map_lookup_elem(&dns_request_count, &key); + if (count_ptr) { + request_count = *count_ptr + 1; + } else { + request_count = 1; + } + bpf_map_update_elem(&dns_request_count, &key, &request_count, BPF_ANY); + // 保留映射中的响应计数值 + count_ptr = bpf_map_lookup_elem(&dns_response_count, &key); + if (count_ptr) { + response_count = *count_ptr; + } + } + message->saddr = rx ? pkt_tuple.saddr : pkt_tuple.daddr; + message->daddr = rx ? pkt_tuple.daddr : pkt_tuple.saddr; + message->id = __bpf_ntohs(query.header.id); + message->flags = __bpf_ntohs(query.header.flags); + message->qdcount = __bpf_ntohs(query.header.qdcount); + message->ancount = __bpf_ntohs(query.header.ancount); + message->nscount = __bpf_ntohs(query.header.nscount); + message->arcount = __bpf_ntohs(query.header.arcount); + message->request_count = request_count; + message->response_count = response_count; + message->rx = rx; + bpf_ringbuf_submit(message, 0); return 0; } +static __always_inline int __dns_rcv(struct sk_buff *skb) { + return process_dns_packet(skb, 0); // 0 收 +} + +static __always_inline int __dns_send(struct sk_buff *skb) { + return process_dns_packet(skb, 1); // 1 发 +} \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/Makefile b/eBPF_Supermarket/Stack_Analyser/Makefile index 132bb1c85..4c357c824 100644 --- a/eBPF_Supermarket/Stack_Analyser/Makefile +++ b/eBPF_Supermarket/Stack_Analyser/Makefile @@ -19,8 +19,11 @@ OUTPUT := .output BPF_SKEL := bpf_skel CLANG ?= clang -LIBBPF_SRC := $(abspath libbpf-bootstrap/libbpf/src) -BPFTOOL_SRC := $(abspath libbpf-bootstrap/bpftool/src) +CXX := clang +LIB := ../lib +LIBBPF_ROOT := $(abspath $(LIB)/libbpf) +LIBBPF_SRC := $(LIBBPF_ROOT)/src +BPFTOOL_SRC := $(abspath $(LIB)/bpftool/src) LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool @@ -31,16 +34,21 @@ ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \ | sed 's/mips.*/mips/' \ | sed 's/riscv64/riscv/' \ | sed 's/loongarch64/loongarch/') -VMLINUX := libbpf-bootstrap/vmlinux/$(ARCH)/vmlinux.h + +VMLINUX := $(LIB)/vmlinux.h # Use our own libbpf API headers and Linux UAPI headers distributed with # libbpf to avoid dependency on system-wide headers, which could be missing or # outdated -INCLUDES := -I./include -I./$(OUTPUT) -I./$(BPF_SKEL) -I./libbpf-bootstrap/libbpf/include/uapi -I$(dir $(VMLINUX)) +INCLUDES := -I./include -I./$(OUTPUT) -I./$(BPF_SKEL) -I$(LIBBPF_ROOT)/include/uapi -I$(dir $(VMLINUX)) CFLAGS := -Og -Wall ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) BIN = $(patsubst src/%.cpp, %, ${wildcard src/*.cpp}) BPF = $(patsubst bpf/%.bpf.c, %, ${wildcard bpf/*.bpf.c}) +BPF_OBJ = $(patsubst %,$(OUTPUT)/%.bpf.o,$(BPF)) +BPF_SKEL_H = $(patsubst %,$(BPF_SKEL)/%.skel.h,$(BPF)) +BPF_WAPPER = $(patsubst %,$(OUTPUT)/%.o,$(BPF)) +BIN_OBJ = $(patsubst %,$(OUTPUT)/%.o,$(BIN)) TARGETS = stack_analyzer @@ -85,12 +93,22 @@ clean: $(call msg,CLEAN) $(Q)rm -rf $(OUTPUT) $(TARGETS) $(BPF_SKEL) +init: + $(call msg,INIT,$(LIB)) + $(Q)git submodule update --init --recursive ../lib/ + +$(LIBBPF_SRC) $(BPFTOOL_SRC): init + +$(VMLINUX): + $(call msg,BTFDUMP,$@) + $(Q)bpftool btf dump file /sys/kernel/btf/vmlinux format c > $@ + $(OUTPUT) $(OUTPUT)/libbpf $(BPFTOOL_OUTPUT) $(BPF_SKEL): $(call msg,MKDIR,$@) $(Q)mkdir -p $@ # Build libbpf -$(LIBBPF_OBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPUT)/libbpf +$(LIBBPF_OBJ): $(LIBBPF_SRC) $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPUT)/libbpf $(call msg,LIB,$@) $(Q)$(MAKE) -C $(LIBBPF_SRC) BUILD_STATIC_ONLY=1 \ OBJDIR=$(dir $@)/libbpf DESTDIR=$(dir $@) \ @@ -98,12 +116,12 @@ $(LIBBPF_OBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPU install # Build bpftool -$(BPFTOOL): | $(BPFTOOL_OUTPUT) +$(BPFTOOL): $(BPFTOOL_SRC) | $(BPFTOOL_OUTPUT) $(call msg,BPFTOOL,$@) $(Q)$(MAKE) ARCH= CROSS_COMPILE= OUTPUT=$(BPFTOOL_OUTPUT)/ -C $(BPFTOOL_SRC) bootstrap # Build BPF code -$(OUTPUT)/%.bpf.o: bpf/%.bpf.c include/sa_ebpf.h $(LIBBPF_OBJ) $(VMLINUX) | $(OUTPUT) $(BPFTOOL) +$(BPF_OBJ): $(OUTPUT)/%.bpf.o: bpf/%.bpf.c include/ebpf.h $(LIBBPF_OBJ) $(VMLINUX) | $(OUTPUT) $(BPFTOOL) $(call msg,BPF,$@) $(Q)$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) \ $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) \ @@ -111,27 +129,25 @@ $(OUTPUT)/%.bpf.o: bpf/%.bpf.c include/sa_ebpf.h $(LIBBPF_OBJ) $(VMLINUX) | $(OU $(Q)$(BPFTOOL) gen object $@ $(patsubst %.bpf.o,%.tmp.bpf.o,$@) # Generate BPF skeletons -$(BPF_SKEL)/%.skel.h: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL) $(BPF_SKEL) +$(BPF_SKEL_H): $(BPF_SKEL)/%.skel.h: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL) $(BPF_SKEL) $(call msg,GEN-SKEL,$@) $(Q)$(BPFTOOL) gen skeleton $< > $@ -$(patsubst %,include/bpf_wapper/%.h,$(BPF)): include/bpf_wapper/%.h: $(BPF_SKEL)/%.skel.h - -$(patsubst %,$(OUTPUT)/%.o,$(BPF)): $(OUTPUT)/%.o: src/bpf_wapper/%.cpp include/bpf_wapper/%.h $(OUTPUT)/eBPFStackCollector.o +$(BPF_WAPPER): $(OUTPUT)/%.o: src/bpf_wapper/%.cpp include/bpf_wapper/%.h $(BPF_SKEL)/%.skel.h $(OUTPUT)/eBPFStackCollector.o $(call msg,CXX,$@) $(Q)$(CXX) $(CFLAGS) $(INCLUDES) -c $< -o $@ # Build depending library -$(patsubst %,$(OUTPUT)/%.o,$(BIN)): $(OUTPUT)/%.o: src/%.cpp $(patsubst %,include/bpf_wapper/%.h,$(BPF)) +$(BIN_OBJ): $(OUTPUT)/%.o: src/%.cpp $(BPF_SKEL_H) $(call msg,CXX,$@) $(Q)$(CXX) $(CFLAGS) $(INCLUDES) -c $< -o $@ -$(OUTPUT)/eBPFStackCollector.o: src/bpf_wapper/eBPFStackCollector.cpp | $(LIBBPF_OBJ) +$(OUTPUT)/eBPFStackCollector.o: src/bpf_wapper/eBPFStackCollector.cpp include/bpf_wapper/eBPFStackCollector.h | $(LIBBPF_OBJ) $(call msg,CXX,$@) $(Q)$(CXX) $(CFLAGS) $(INCLUDES) -c $< -o $@ # Build application binary -$(TARGETS): $(OUTPUT)/eBPFStackCollector.o $(patsubst %,$(OUTPUT)/%.o,$(BIN)) $(patsubst %,$(OUTPUT)/%.o,$(BPF)) $(LIBBPF_OBJ) +$(TARGETS): $(OUTPUT)/eBPFStackCollector.o $(BIN_OBJ) $(BPF_WAPPER) $(LIBBPF_OBJ) $(call msg,BINARY,$@) $(Q)$(CXX) $^ $(ALL_LDFLAGS) -lstdc++ -lelf -lz -o $@ diff --git a/eBPF_Supermarket/Stack_Analyser/README.md b/eBPF_Supermarket/Stack_Analyser/README.md index 6ba720da4..2df17eb1b 100644 --- a/eBPF_Supermarket/Stack_Analyser/README.md +++ b/eBPF_Supermarket/Stack_Analyser/README.md @@ -61,21 +61,34 @@ Stack_Analyzer是一个基于eBPF的按照指定时间间隔(默认为5s)来 ## 编译要求 -Ubuntu下需要安装一下依赖,其他发行版类似 +初始化并更新libbpf和bpftool的代码仓库: ```shell -$ git submodule update --init --recursive -$ apt install clang libelf1 libelf-dev zlib1g-dev +git submodule update --init --recursive ../lib/* ``` +需要安装一下依赖: -g++-10以上,clang-12以上 +Ubuntu下 + +```shell +sudo apt update +sudo apt install -y clang libelf1 libelf-dev zlib1g-dev bpftool +``` + +CentOS下 + +```shell +sudo dnf install clang elfutils-libelf elfutils-libelf-devel zlib-devel bpftool +``` + +clang-12以上 # 使用方法 ## 工具编译 ```shell -$ make +$ make -j$(nproc) ``` ## 命令使用方法 @@ -87,20 +100,27 @@ $ ./stack_analyzer -h ## 输出格式 ```shell -Type:OnCPUTime Unit:nanoseconds Period:20408163 -time:20240309_06_23_32 +time:20240516_17_37_41 counts: -pid usid ksid count -23640 126625 88396 1 +pid usid ksid OnCPUTime/20408163nanoseconds +720 9226 49641 1 +3149 35172 -14 1 +91036 70751 78610 1 +91045 33012 -14 1 traces: sid trace -88396 entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_clone;__do_sys_clone;kernel_clone;copy_process;dup_mm.constprop.0;dup_mmap;copy_page_range;copy_p4d_range;copy_pte_range;__pte_alloc;pte_alloc_one;alloc_pages;__alloc_pages;get_page_from_freelist;clear_page_orig; -126625 _Fork+0x7ff997200027; -groups: -pid tgid -23640 23640 -commands: -23640 node +9226 GlobalConfig_DownloadConfig+0xffffa9f3f4439631;__socket+0xb; +33012 0x40;0x7f2e8c34183c;0x7f2e8c344845; +35172 runtime.goexit.abi0+0x1;github.com/prometheus/node_exporter/collector.NodeCollector.Collect.gowrap1+0x30;github.com/prometheus/node_exporter/collector.NodeCollector.Collect.func1+0x37;github.com/prometheus/node_exporter/collector.execute+0x90;github.com/prometheus/node_exporter/collector.(*netClassCollector).Update+0x2a;github.com/prometheus/node_exporter/collector.(*netClassCollector).netClassSysfsUpdate+0x32;github.com/prometheus/node_exporter/collector.(*netClassCollector).getNetClassInfo+0xf9;github.com/prometheus/procfs/sysfs.FS.NetClassByIface+0x105;github.com/prometheus/procfs/sysfs.parseNetClassIface+0xc5;github.com/prometheus/procfs/sysfs.ParseNetClassAttribute+0x94;github.com/prometheus/procfs/internal/util.SysReadFile+0x4d;os.OpenFile+0x3e;os.openFileNolog+0x92;os.open+0x2b;syscall.openat+0x90;syscall.Syscall6+0x6a;runtime/internal/syscall.Syscall6+0xe; +49641 entry_SYSCALL_64_after_hwframe+0x67;do_syscall_64+0x56;x64_sys_call+0x1ec2;__x64_sys_socket+0x17;__sys_socket+0x5d;__sock_create+0x133;unix_create+0x43;unix_create1+0x67;sk_alloc+0x31;sk_prot_alloc+0x8b; +70751 0x7f19ac2e37e2; +78610 entry_SYSCALL_64_after_hwframe+0x67;do_syscall_64+0x56;x64_sys_call+0x1dba;__x64_sys_read+0x19;ksys_read+0x67;vfs_read+0x9c;seq_read+0xf2;seq_read_iter+0x121;proc_single_show+0x4f;proc_pid_status+0x40c;cpuset_task_status_allowed+0x3a;seq_printf+0x91;vsnprintf+0x1e3;pointer+0x2a5;bitmap_string.constprop.0+0xe7; +info: +pid NSpid comm tgid cgroup +720 0 vmtoolsd 720 open-vm-tools.service +3149 0 node_exporter 3046 docker-bb4adf6694d50bea52f9e57038ab2870cd070fac4c8fe241f3d34dd1791a9a32.scope +91036 0 ps 91036 session-4.scope +91045 0 sed 91045 session-4.scope OK ``` @@ -108,6 +128,24 @@ OK 请阅读[`exporter/README.md`](exporter/README.md)。 +## 将Pyroscope集成到Grafana + +1. 选择pyroscope数据源 + ![选择数据源](asset/image.png) +2. 设置pyroscope服务器地址并测试保存 + ![配置](asset/image-1.png) +3. 创建仪表盘并添加可视化面板,将数据源设为刚刚创建的pyroscope数据源,调整参数,其中,metrics请求可以获取调用栈对应指标总体走势,profile请求可以获取火焰图绘制数据,展示时间区间内的火焰图。 + ![metrics](asset/image-2.png) + ![profile](asset/image-3.png) +4. 总体效果展示 + ![stack analyzer dashboard](asset/image-4.png) + +## grafana dashboard 导出及导入 + +本仓库提供了上节效果展示所使用的dashboard,即`./grafana_stack_analyzer_dashboard.json` +其导出方法为:在仪表盘界面点击[分享]按钮,导出,导出为文件,即可。 +导入则需要在创建仪表盘菜单选择[导入仪表盘],通过仪表板 JSON 模型导入,选择需要的仪表盘文件,即可。 + # 目录描述 - include:各种定义。 @@ -117,4 +155,12 @@ OK - exporter:使用Golang开发的数据推送程序,将采集到的调用栈数据推送到Pyroscope服务器,获取更强的数据存储和可视化性能。 - main.cpp:负责参数解析、配置、调用栈数据收集器管理和子进程管理。 - libbpf-bootstrap: 项目依赖的libbpf及相关工具源代码,方便移植。 -- new_bpf.sh:初始化新的采集能力的脚本,详情请参考`框架使用说明.md`。 \ No newline at end of file +- new_bpf.sh:初始化新的采集能力的脚本,详情请参考`框架使用说明.md`。 + +# 问题描述 + +- [x] 符号解析库没有访问容器内可执行文件的能力 +- [ ] memleak挂载在uprobe上时无数据 +- [ ] memleak在定频时依旧会采集大量数据 +- [ ] golang语言调用栈解析效果差 +- [ ] readahead采集结果中存在指标值为0的项 \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/asset/image-1.png b/eBPF_Supermarket/Stack_Analyser/asset/image-1.png new file mode 100644 index 000000000..877526a07 Binary files /dev/null and b/eBPF_Supermarket/Stack_Analyser/asset/image-1.png differ diff --git a/eBPF_Supermarket/Stack_Analyser/asset/image-2.png b/eBPF_Supermarket/Stack_Analyser/asset/image-2.png new file mode 100644 index 000000000..bd6e27996 Binary files /dev/null and b/eBPF_Supermarket/Stack_Analyser/asset/image-2.png differ diff --git a/eBPF_Supermarket/Stack_Analyser/asset/image-3.png b/eBPF_Supermarket/Stack_Analyser/asset/image-3.png new file mode 100644 index 000000000..26c23b9b6 Binary files /dev/null and b/eBPF_Supermarket/Stack_Analyser/asset/image-3.png differ diff --git a/eBPF_Supermarket/Stack_Analyser/asset/image-4.png b/eBPF_Supermarket/Stack_Analyser/asset/image-4.png new file mode 100644 index 000000000..9eb534213 Binary files /dev/null and b/eBPF_Supermarket/Stack_Analyser/asset/image-4.png differ diff --git a/eBPF_Supermarket/Stack_Analyser/asset/image.png b/eBPF_Supermarket/Stack_Analyser/asset/image.png new file mode 100644 index 000000000..60d4a6679 Binary files /dev/null and b/eBPF_Supermarket/Stack_Analyser/asset/image.png differ diff --git a/eBPF_Supermarket/Stack_Analyser/bpf/io.bpf.c b/eBPF_Supermarket/Stack_Analyser/bpf/io.bpf.c index 65a9a60a7..2469004fe 100644 --- a/eBPF_Supermarket/Stack_Analyser/bpf/io.bpf.c +++ b/eBPF_Supermarket/Stack_Analyser/bpf/io.bpf.c @@ -21,32 +21,31 @@ #include #include -#include "sa_ebpf.h" +#include "ebpf.h" #include "bpf_wapper/io.h" #include "task.h" COMMON_MAPS(io_tuple); COMMON_VALS; -const volatile int target_pid = 0; const char LICENSE[] SEC("license") = "GPL"; static int do_stack(struct trace_event_raw_sys_enter *ctx) { CHECK_ACTIVE; - struct task_struct *curr = (struct task_struct *)bpf_get_current_task(); // 利用bpf_get_current_task()获得当前的进程tsk - RET_IF_KERN(curr); - u32 pid = BPF_CORE_READ(curr, pid); // 利用帮助函数获得当前进程的pid - if ((target_pid >= 0 && pid != target_pid) || !pid || pid == self_pid) - return 0; + CHECK_FREQ(TS); + struct task_struct *curr = GET_CURR; + CHECK_KTHREAD(curr); + u32 tgid = BPF_CORE_READ(curr, tgid); + CHECK_TGID(tgid); + struct kernfs_node *knode = GET_KNODE(curr); + CHECK_CGID(knode); - SAVE_TASK_INFO(pid, curr); - - // record time delta - psid apsid = GET_COUNT_KEY(pid, ctx); + u32 pid = BPF_CORE_READ(curr, pid); + TRY_SAVE_INFO(curr, pid, tgid, knode); + psid apsid = TRACE_AND_GET_COUNT_KEY(pid, ctx); io_tuple *d = bpf_map_lookup_elem(&psid_count_map, &apsid); // count指向psid_count表当中的apsid表项,即size u64 len = BPF_CORE_READ(ctx, args[2]); // 读取系统调用的第三个参数 - if (!d) { io_tuple tmp = {.count = 1, .size = len}; diff --git a/eBPF_Supermarket/Stack_Analyser/bpf/llc_stat.bpf.c b/eBPF_Supermarket/Stack_Analyser/bpf/llc_stat.bpf.c index 89c444690..b48d433d0 100644 --- a/eBPF_Supermarket/Stack_Analyser/bpf/llc_stat.bpf.c +++ b/eBPF_Supermarket/Stack_Analyser/bpf/llc_stat.bpf.c @@ -21,7 +21,7 @@ #include #include -#include "sa_ebpf.h" +#include "ebpf.h" #include "bpf_wapper/llc_stat.h" #include "task.h" @@ -31,13 +31,17 @@ COMMON_VALS; static __always_inline int trace_event(__u64 sample_period, bool miss, struct bpf_perf_event_data *ctx) { CHECK_ACTIVE; - struct task_struct *curr = (struct task_struct *)bpf_get_current_task(); - RET_IF_KERN(curr); - u32 pid = BPF_CORE_READ(curr, pid); // 利用帮助函数获得当前进程的pid - if (!pid || pid == self_pid) - return 0; - SAVE_TASK_INFO(pid, curr); - psid apsid = GET_COUNT_KEY(pid, ctx); + CHECK_FREQ(TS); + struct task_struct *curr = GET_CURR; + CHECK_KTHREAD(curr); + // perf 中已设置目标tgid,这里无需再次过滤tgid + struct kernfs_node *knode = GET_KNODE(curr); + CHECK_CGID(knode); + + u32 pid = BPF_CORE_READ(curr, pid); + u32 tgid = BPF_CORE_READ(curr, tgid); + TRY_SAVE_INFO(curr, pid, tgid, knode); + psid apsid = TRACE_AND_GET_COUNT_KEY(pid, ctx); llc_stat *infop = bpf_map_lookup_elem(&psid_count_map, &apsid); if (!infop) { diff --git a/eBPF_Supermarket/Stack_Analyser/bpf/memleak.bpf.c b/eBPF_Supermarket/Stack_Analyser/bpf/memleak.bpf.c index 239a7edba..d94b29d97 100644 --- a/eBPF_Supermarket/Stack_Analyser/bpf/memleak.bpf.c +++ b/eBPF_Supermarket/Stack_Analyser/bpf/memleak.bpf.c @@ -21,41 +21,34 @@ #include #include -#include "sa_ebpf.h" +#include "ebpf.h" #include "task.h" #include "bpf_wapper/memleak.h" COMMON_MAPS(union combined_alloc_info); COMMON_VALS; -const volatile __u64 sample_rate = 1; const volatile bool wa_missing_free = false; const volatile size_t page_size = 4096; const volatile bool trace_all = false; -BPF_HASH(pid_size_map, u32, u64); // 记录了对应进程使用malloc,calloc等函数申请内存的大小 -BPF_HASH(piddr_meminfo_map, piddr, mem_info); // 记录了每次申请的内存空间的起始地址等信息 -BPF_HASH(memptrs_map, u32, u64); +BPF_HASH(pid_size_map, u32, u64, MAX_ENTRIES); // 记录了对应进程使用malloc,calloc等函数申请内存的大小 +BPF_HASH(piddr_meminfo_map, piddr, mem_info, MAX_ENTRIES); // 记录了每次申请的内存空间的起始地址等信息 +BPF_HASH(memptrs_map, u32, u64, MAX_ENTRIES); const char LICENSE[] SEC("license") = "GPL"; static int gen_alloc_enter(size_t size) { CHECK_ACTIVE; - if (!size) - return 0; - if (sample_rate > 1) - { - if (bpf_ktime_get_ns() % sample_rate != 0) - return 0; - } - struct task_struct *curr = (struct task_struct *)bpf_get_current_task(); - RET_IF_KERN(curr); - // update group - // group share memory + CHECK_FREQ(TS); + struct task_struct *curr = GET_CURR; + CHECK_KTHREAD(curr); + // attach 时已设置目标tgid,这里无需再次过滤tgid + struct kernfs_node *knode = GET_KNODE(curr); + CHECK_CGID(knode); + u32 tgid = BPF_CORE_READ(curr, tgid); - if (tgid == self_pid) - return 0; - SAVE_TASK_INFO(tgid, curr); + TRY_SAVE_INFO(curr, tgid, tgid, knode); if (trace_all) bpf_printk("alloc entered, size = %lu\n", size); // record size @@ -72,7 +65,7 @@ static int gen_alloc_exit2(void *ctx, u64 addr) if (!size) return 0; // record counts - psid apsid = GET_COUNT_KEY(tgid, ctx); + psid apsid = TRACE_AND_GET_COUNT_KEY(tgid, ctx); union combined_alloc_info *count = bpf_map_lookup_elem(&psid_count_map, &apsid); union combined_alloc_info cur = { .number_of_allocs = 1, @@ -100,7 +93,6 @@ static int gen_alloc_exit2(void *ctx, u64 addr) static int gen_alloc_exit(struct pt_regs *ctx) { - CHECK_ACTIVE; return gen_alloc_exit2(ctx, PT_REGS_RC(ctx)); } @@ -111,7 +103,7 @@ static int gen_free_enter(const void *addr) piddr a = {.addr = (u64)addr, .pid = tgid, ._pad = 0}; mem_info *info = bpf_map_lookup_elem(&piddr_meminfo_map, &a); if (!info) - return -1; + return 0; // get allocated size psid apsid = { @@ -492,7 +484,7 @@ int memleak__mm_page_free(struct trace_event_raw_mm_page_free *ctx) SEC("tracepoint/percpu/percpu_alloc_percpu") int memleak__percpu_alloc_percpu(struct trace_event_raw_percpu_alloc_percpu *ctx) { - gen_alloc_enter(ctx->bytes_alloc); + gen_alloc_enter(ctx->size); return gen_alloc_exit2(ctx, (u64)(ctx->ptr)); } diff --git a/eBPF_Supermarket/Stack_Analyser/bpf/off_cpu.bpf.c b/eBPF_Supermarket/Stack_Analyser/bpf/off_cpu.bpf.c index 10445e9db..80877b067 100644 --- a/eBPF_Supermarket/Stack_Analyser/bpf/off_cpu.bpf.c +++ b/eBPF_Supermarket/Stack_Analyser/bpf/off_cpu.bpf.c @@ -21,50 +21,67 @@ #include #include -#include "sa_ebpf.h" +#include "ebpf.h" #include "task.h" COMMON_MAPS(u32); COMMON_VALS; - -const volatile int target_pid = 0; -BPF_HASH(pid_offTs_map, u32, u64); // 记录进程运行的起始时间 +// 记录进程运行的起始时间 +BPF_HASH(pid_offTs_map, u32, u64, MAX_ENTRIES/10); const char LICENSE[] SEC("license") = "GPL"; -SEC("kprobe/finish_task_switch") // 动态挂载点finish_task_switch.isra.0 -int BPF_KPROBE(do_stack, struct task_struct *curr) +static int prev_part(struct task_struct *prev) { - CHECK_ACTIVE; - u32 pid = BPF_CORE_READ(curr, pid); // 利用帮助函数获取换出进程tsk的pid - RET_IF_KERN(curr); - if ((target_pid >= 0 && pid == target_pid) || (target_pid < 0 && pid && pid != self_pid)) - { - // record curr block time - u64 ts = bpf_ktime_get_ns(); // ts=当前的时间戳(ns) - bpf_map_update_elem(&pid_offTs_map, &pid, &ts, BPF_NOEXIST); // 如果start表中不存在pid对应的时间,则就创建pid-->ts - } + u64 ts = bpf_ktime_get_ns(); + CHECK_FREQ(ts); + CHECK_KTHREAD(prev); + u32 tgid = BPF_CORE_READ(prev, tgid); + CHECK_TGID(tgid); + struct kernfs_node *knode = GET_KNODE(prev); + CHECK_CGID(knode); + u32 pid = BPF_CORE_READ(prev, pid); + bpf_map_update_elem(&pid_offTs_map, &pid, &ts, BPF_ANY); + return 0; +} - // calculate time delta, next ready to run - struct task_struct *next = (struct task_struct *)bpf_get_current_task(); // next指向换入进程结构体 - pid = BPF_CORE_READ(next, pid); // 利用帮助函数获取next指向的tsk的pid - u64 *tsp = bpf_map_lookup_elem(&pid_offTs_map, &pid); // tsp指向start表中的pid的值 +static int next_part(struct task_struct *next, void *ctx) +{ + // 利用帮助函数获取next指向的tsk的pid + u32 pid = BPF_CORE_READ(next, pid); + // tsp指向start表中的pid的值 + u64 *tsp = bpf_map_lookup_elem(&pid_offTs_map, &pid); if (!tsp) return 0; - bpf_map_delete_elem(&pid_offTs_map, &pid); // 存在tsp,则删除pid对应的值 - u32 delta = (bpf_ktime_get_ns() - *tsp) >> 20; // delta为当前时间戳 - 原先tsp指向start表中的pid的值.代表运行时间 + // delta为当前时间戳 - 原先tsp指向start表中的pid的值.代表运行时间 + u32 delta = (bpf_ktime_get_ns() - *tsp) >> 20; if (!delta) return 0; // record data - SAVE_TASK_INFO(pid, next); - psid apsid = GET_COUNT_KEY(pid, ctx); + struct kernfs_node *knode = GET_KNODE(next); + TRY_SAVE_INFO(next, pid, BPF_CORE_READ(next, tgid), knode); + psid apsid = TRACE_AND_GET_COUNT_KEY(pid, ctx); // record time delta - u32 *count = bpf_map_lookup_elem(&psid_count_map, &apsid); // count指向psid_count中的apsid对应的值 + // count指向psid_count中的apsid对应的值 + u32 *count = bpf_map_lookup_elem(&psid_count_map, &apsid); if (count) - (*count) += delta; // 如果count存在,则psid_count中的apsid对应的值+=时间戳 + // 如果count存在,则psid_count中的apsid对应的值+=时间戳 + (*count) += delta; else - bpf_map_update_elem(&psid_count_map, &apsid, &delta, BPF_NOEXIST); // 如果不存在,则将psid_count表中的apsid设置为delta + // 如果不存在,则将psid_count表中的apsid设置为delta + bpf_map_update_elem(&psid_count_map, &apsid, &delta, BPF_NOEXIST); + return 0; +} + +// 动态挂载点finish_task_switch.isra.0 +SEC("kprobe/finish_task_switch") +int BPF_KPROBE(do_stack, struct task_struct *prev) +{ + CHECK_ACTIVE; + prev_part(prev); + // calculate time delta, next ready to run + next_part(GET_CURR, ctx); return 0; } \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/bpf/on_cpu.bpf.c b/eBPF_Supermarket/Stack_Analyser/bpf/on_cpu.bpf.c index 7830850ad..c1b103f19 100644 --- a/eBPF_Supermarket/Stack_Analyser/bpf/on_cpu.bpf.c +++ b/eBPF_Supermarket/Stack_Analyser/bpf/on_cpu.bpf.c @@ -21,7 +21,7 @@ #include #include -#include "sa_ebpf.h" +#include "ebpf.h" #include "task.h" const char LICENSE[] SEC("license") = "GPL"; @@ -33,15 +33,15 @@ SEC("perf_event") // 挂载点为perf_event int do_stack(void *ctx) { CHECK_ACTIVE; - struct task_struct *curr = (void *)bpf_get_current_task(); // curr指向当前进程的tsk - RET_IF_KERN(curr); // 忽略内核线程 - u32 pid = BPF_CORE_READ(curr, pid); // pid保存当前进程的pid,是cgroup pid 对应的level 0 pid - if (!pid || pid == self_pid) - return 0; - SAVE_TASK_INFO(pid, curr); - psid apsid = GET_COUNT_KEY(pid, ctx); + struct task_struct *curr = GET_CURR; // curr指向当前进程的tsk + CHECK_KTHREAD(curr); + // perf 中已设置目标tgid,这里无需再次过滤tgid + struct kernfs_node *knode = GET_KNODE(curr); + CHECK_CGID(knode); - // add cosunt + u32 pid = BPF_CORE_READ(curr, pid); + TRY_SAVE_INFO(curr, pid, BPF_CORE_READ(curr, tgid), knode); + psid apsid = TRACE_AND_GET_COUNT_KEY(pid, ctx); u32 *count = bpf_map_lookup_elem(&psid_count_map, &apsid); // count指向psid_count对应的apsid的值 if (count) (*count)++; // count不为空,则psid_count对应的apsid的值+1 diff --git a/eBPF_Supermarket/Stack_Analyser/bpf/probe.bpf.c b/eBPF_Supermarket/Stack_Analyser/bpf/probe.bpf.c index 65706ad64..623bb5ae0 100644 --- a/eBPF_Supermarket/Stack_Analyser/bpf/probe.bpf.c +++ b/eBPF_Supermarket/Stack_Analyser/bpf/probe.bpf.c @@ -12,9 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. // -// author: GaoYixiang +// author: luiyanbing@foxmail.com // -// 内核态eBPF的通用的调用栈计数代码 +// probe功能的内核态bpf程序代码 #include "vmlinux.h" #include @@ -22,54 +22,122 @@ #include #include -#include "sa_ebpf.h" +#include "ebpf.h" +#include "bpf_wapper/probe.h" #include "task.h" -COMMON_MAPS(u32); +COMMON_MAPS(time_tuple); COMMON_VALS; -const volatile int target_pid = 0; +BPF_HASH(starts, u32, u64, MAX_ENTRIES/10); -const char LICENSE[] SEC("license") = "GPL"; - -static int handle_func(void *ctx) +static int entry(void *ctx) { CHECK_ACTIVE; - struct task_struct *curr = (struct task_struct *)bpf_get_current_task(); // 利用bpf_get_current_task()获得当前的进程tsk - RET_IF_KERN(curr); + u64 ts = bpf_ktime_get_ns(); + CHECK_FREQ(ts); + struct task_struct *curr = GET_CURR; + CHECK_KTHREAD(curr); + u32 tgid = BPF_CORE_READ(curr, tgid); + CHECK_TGID(tgid); + struct kernfs_node *knode = GET_KNODE(curr); + CHECK_CGID(knode); - u32 pid = get_task_ns_pid(curr); // 利用帮助函数获得当前进程的pid - if ((target_pid >= 0 && pid != target_pid) || !pid || pid == self_pid) - return 0; + u32 pid = BPF_CORE_READ(curr, pid); + TRY_SAVE_INFO(curr, pid, tgid, knode); + bpf_map_update_elem(&starts, &pid, &ts, BPF_ANY); + return 0; +} - SAVE_TASK_INFO(pid, curr); +static int exit(void *ctx) +{ + CHECK_ACTIVE; + u32 pid = bpf_get_current_pid_tgid() >> 32; + u64 *start = bpf_map_lookup_elem(&starts, &pid); + if (!start) + return 0; + u64 delta = TS - *start; - psid a_psid = GET_COUNT_KEY(pid, ctx); - u32 *cnt = bpf_map_lookup_elem(&psid_count_map, &a_psid); - if (!cnt) + psid a_psid = TRACE_AND_GET_COUNT_KEY(pid, ctx); + time_tuple *d = bpf_map_lookup_elem(&psid_count_map, &a_psid); + if (!d) { - u32 ONE = 1; - bpf_map_update_elem(&psid_count_map, &a_psid, &ONE, BPF_NOEXIST); + time_tuple tmp = {.lat = delta, .count = 1}; + bpf_map_update_elem(&psid_count_map, &a_psid, &tmp, BPF_NOEXIST); } else - (*cnt)++; - + { + d->lat += delta; + d->count++; + } return 0; } + SEC("kprobe/dummy_kprobe") -int BPF_KPROBE(handle) +int BPF_KPROBE(dummy_kprobe) { - handle_func(ctx); + entry(ctx); return 0; } + +SEC("kretprobe/dummy_kretprobe") +int BPF_KRETPROBE(dummy_kretprobe) +{ + exit(ctx); + return 0; +} + +SEC("fentry/dummy_fentry") +int BPF_PROG(dummy_fentry) +{ + entry(ctx); + return 0; +} + +SEC("fexit/dummy_fexit") +int BPF_PROG(dummy_fexit) +{ + exit(ctx); + return 0; +} + +static int static_tracing_handler(void *ctx) +{ + CHECK_ACTIVE; + CHECK_FREQ(TS); + struct task_struct *curr = GET_CURR; + CHECK_KTHREAD(curr); + u32 tgid = BPF_CORE_READ(curr, tgid); + CHECK_TGID(tgid); + struct kernfs_node *knode = GET_KNODE(curr); + CHECK_CGID(knode); + + u32 pid = BPF_CORE_READ(curr, pid); + TRY_SAVE_INFO(curr, pid, tgid, knode); + psid a_psid = TRACE_AND_GET_COUNT_KEY(pid, ctx); + time_tuple *d = bpf_map_lookup_elem(&psid_count_map, &a_psid); + if (!d) + { + time_tuple tmp = {.lat = 0, .count = 1}; + bpf_map_update_elem(&psid_count_map, &a_psid, &tmp, BPF_NOEXIST); + } + else + d->count++; + return 0; +} + SEC("tp/sched/dummy_tp") -int handle_tp(void *ctx) +int tp_exit(void *ctx) { - handle_func(ctx); + static_tracing_handler(ctx); return 0; } + SEC("usdt") -int handle_usdt(void *ctx) +int usdt_exit(void *ctx) { - return handle_func(ctx); -} \ No newline at end of file + static_tracing_handler(ctx); + return 0; +} + +const char LICENSE[] SEC("license") = "GPL"; \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/bpf/readahead.bpf.c b/eBPF_Supermarket/Stack_Analyser/bpf/readahead.bpf.c index 59c390076..69925f805 100644 --- a/eBPF_Supermarket/Stack_Analyser/bpf/readahead.bpf.c +++ b/eBPF_Supermarket/Stack_Analyser/bpf/readahead.bpf.c @@ -21,7 +21,7 @@ #include #include -#include "sa_ebpf.h" +#include "ebpf.h" #include "task.h" #include "bpf_wapper/readahead.h" @@ -31,24 +31,24 @@ COMMON_MAPS(ra_tuple); COMMON_VALS; -int target_pid = 0; -BPF_HASH(in_ra_map, u32, psid); -BPF_HASH(page_psid_map, struct page *, psid); +BPF_HASH(in_ra_map, u32, psid, MAX_ENTRIES/10); +BPF_HASH(page_psid_map, struct page *, psid, MAX_ENTRIES); SEC("fentry/page_cache_ra_unbounded") // fentry在内核函数page_cache_ra_unbounded进入时触发的挂载点 int BPF_PROG(page_cache_ra_unbounded) { CHECK_ACTIVE; - struct task_struct *curr = (struct task_struct *)bpf_get_current_task(); - RET_IF_KERN(curr); - u32 pid = get_task_ns_pid(curr); // 获取当前进程tgid,用户空间的pid即是tgid - - if ((target_pid >= 0 && pid != target_pid) || !pid || pid == self_pid) - return 0; - - SAVE_TASK_INFO(pid, curr); - psid apsid = GET_COUNT_KEY(pid, ctx); - + CHECK_FREQ(TS); + struct task_struct *curr = GET_CURR; + CHECK_KTHREAD(curr); + u32 tgid = BPF_CORE_READ(curr, tgid); + CHECK_TGID(tgid); + struct kernfs_node *knode = GET_KNODE(curr); + CHECK_CGID(knode); + + u32 pid = BPF_CORE_READ(curr, pid); + TRY_SAVE_INFO(curr, pid, tgid, knode); + psid apsid = TRACE_AND_GET_COUNT_KEY(pid, ctx); ra_tuple *d = bpf_map_lookup_elem(&psid_count_map, &apsid); // d指向psid_count表中的apsid对应的类型为tuple的值 if (!d) { @@ -63,11 +63,7 @@ SEC("fexit/alloc_pages") // fexit在内核函数alloc_pages退出时触发,挂 int BPF_PROG(filemap_alloc_folio_ret, gfp_t gfp, unsigned int order, u64 ret) { CHECK_ACTIVE; - u32 pid = bpf_get_current_pid_tgid() >> 32; // pid为当前进程的pid - - if ((target_pid >= 0 && pid != target_pid) || !pid) - return 0; - + u32 pid = bpf_get_current_pid_tgid() >> 32; // pid为当前进程的pid struct psid *apsid = bpf_map_lookup_elem(&in_ra_map, &pid); // apsid指向了当前in_ra中pid的表项内容 if (!apsid) return 0; @@ -91,11 +87,7 @@ int BPF_PROG(page_cache_ra_unbounded_ret) // fexit在内核函数page_cache_ra_u { CHECK_ACTIVE; u32 pid = bpf_get_current_pid_tgid() >> 32; // 获取当前进程的pid - - if ((target_pid >= 0 && pid != target_pid) || !pid) - return 0; - - bpf_map_delete_elem(&in_ra_map, &pid); // 删除了in_ra对应的pid的表项,即删除对应的栈计数信息 + bpf_map_delete_elem(&in_ra_map, &pid); // 删除了in_ra对应的pid的表项,即删除对应的栈计数信息 return 0; } @@ -104,9 +96,6 @@ int BPF_PROG(mark_page_accessed, u64 page) { CHECK_ACTIVE; u32 pid = bpf_get_current_pid_tgid() >> 32; // 获取当前进程的pid - - if ((target_pid >= 0 && pid != target_pid) || !pid) - return 0; psid *apsid; apsid = bpf_map_lookup_elem(&page_psid_map, &page); // 查看page_psid对应的 地址page 对应类型为psid的值,并保存在apsid if (!apsid) diff --git a/eBPF_Supermarket/Stack_Analyser/bpf/template.bpf.c b/eBPF_Supermarket/Stack_Analyser/bpf/template.bpf.c index 8f741746f..108412609 100644 --- a/eBPF_Supermarket/Stack_Analyser/bpf/template.bpf.c +++ b/eBPF_Supermarket/Stack_Analyser/bpf/template.bpf.c @@ -21,7 +21,7 @@ #include #include -#include "sa_ebpf.h" +#include "ebpf.h" #include "bpf_wapper/template.h" #include "task.h" diff --git a/eBPF_Supermarket/Stack_Analyser/exporter/README.md b/eBPF_Supermarket/Stack_Analyser/exporter/README.md index 911164863..90fdde8a7 100644 --- a/eBPF_Supermarket/Stack_Analyser/exporter/README.md +++ b/eBPF_Supermarket/Stack_Analyser/exporter/README.md @@ -1,6 +1,6 @@ # 功能描述 -适用于Pyroscope服务器的数据发送程序,程序通过监听标准输入来获取调用栈数据,输入格式与stack_analyzer工具输出格式一致,可通过管道配合stack_analyer使用,将stack_analyzer的数据发送到Pyroscope服务器获取更强的数据存储和可视化能力。 +适用于Pyroscope调用栈可视化服务器的数据发送程序,程序通过监听标准输入来获取调用栈数据,输入格式与stack_analyzer工具输出格式一致,可通过管道配合stack_analyer使用,将stack_analyzer的数据发送到Pyroscope服务器获取更强的数据存储和可视化能力。Pyroscope安装方法可参考[这里](https://grafana.com/docs/pyroscope/latest/get-started/)。 # 使用方法 diff --git a/eBPF_Supermarket/Stack_Analyser/exporter/main.go b/eBPF_Supermarket/Stack_Analyser/exporter/main.go index 66a074f54..f81dbb098 100644 --- a/eBPF_Supermarket/Stack_Analyser/exporter/main.go +++ b/eBPF_Supermarket/Stack_Analyser/exporter/main.go @@ -292,6 +292,7 @@ func CollectProfiles(cb CollectProfilesCallback) error { for i, s := range scales { target := sd.NewTarget("", k.pid, sd.DiscoveryTarget{ "__container_id__": info[k.pid].cid, + "service_name": "Stack_Analyzer", labels.MetricName: s.Type, }) cb(target, group_trace, v[i], s, true) diff --git "a/eBPF_Supermarket/Stack_Analyser/\346\241\206\346\236\266\344\275\277\347\224\250\346\226\271\346\263\225.md" b/eBPF_Supermarket/Stack_Analyser/frame-use-guide.md similarity index 87% rename from "eBPF_Supermarket/Stack_Analyser/\346\241\206\346\236\266\344\275\277\347\224\250\346\226\271\346\263\225.md" rename to eBPF_Supermarket/Stack_Analyser/frame-use-guide.md index 2f393cbd5..8d829a678 100644 --- "a/eBPF_Supermarket/Stack_Analyser/\346\241\206\346\236\266\344\275\277\347\224\250\346\226\271\346\263\225.md" +++ b/eBPF_Supermarket/Stack_Analyser/frame-use-guide.md @@ -32,18 +32,18 @@ new_ebpf.sh 1. 实现包装类初始化函数,若数据标度`scale`的值可以确定,请在此初始化 2. 若标度无法确定,则实现`setScale`,对标度及包装类参数进行设置 -3. 实现一系列虚函数:`count_value, load, attach, detach, unload` +3. 实现一系列虚函数:`count_values, load, attach, detach, unload` 4. 可实现一些辅助函数 ## src/bpf/.bpf.c -1. 通过修改`DeclareCommonMaps(__u32)`中的`__u32`设置计数变量类型 +1. 通过修改`COMMON_MAPS(__u32)`中的`__u32`设置计数变量类型 2. 可声明额外的map和全局变量在eBPF程序内部使用,但不会被框架输出。 3. 实现eBPF程序,请使用通用的`eBPF map`进行数据存储,使用通用的全局变量进行进程和数据过滤,否则无法正确输出数据 通用的map分别为: - 1. psid_count:键为psid类型,值为 1. 中设置的计数变量类型 - 2. stack_trace:键为uint32类型,标识唯一的栈嗲用路径,值为void*[]类型,存储栈上的调用地址 + 1. psid_count_map:键为psid类型,值为 1. 中设置的计数变量类型 + 2. sid_trace_map:键为uint32类型,标识唯一的栈嗲用路径,值为void*[]类型,存储栈上的调用地址 3. pid_tgid:键为uint32类型,表示pid,值为uint32类型,表示tgid 4. pid_comm:键为uint32类型,表示pid,值为comm类型,表示进程名 diff --git a/eBPF_Supermarket/Stack_Analyser/grafana_stack_analyzer_dashboard.json b/eBPF_Supermarket/Stack_Analyser/grafana_stack_analyzer_dashboard.json new file mode 100644 index 000000000..dfe7e0cab --- /dev/null +++ b/eBPF_Supermarket/Stack_Analyser/grafana_stack_analyzer_dashboard.json @@ -0,0 +1,1362 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 6, + "panels": [], + "title": "cpu", + "type": "row" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 1 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "OnCPUTime:OnCPUTime:nanoseconds::", + "queryType": "metrics", + "refId": "A", + "spanSelector": [] + } + ], + "title": "cpu metrics", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 1 + }, + "id": 2, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "OnCPUTime:OnCPUTime:nanoseconds::", + "queryType": "profile", + "refId": "A", + "spanSelector": [] + } + ], + "title": "cpu profile", + "type": "flamegraph" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 9 + }, + "id": 5, + "panels": [], + "title": "block", + "type": "row" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 10 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "OffCPUTime:OffCPUTime:nanoseconds::", + "queryType": "metrics", + "refId": "A", + "spanSelector": [] + } + ], + "title": "block time metrics", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 10 + }, + "id": 4, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "OffCPUTime:OffCPUTime:nanoseconds::", + "queryType": "profile", + "refId": "A", + "spanSelector": [] + } + ], + "title": "block time profile", + "type": "flamegraph" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 18 + }, + "id": 7, + "panels": [], + "title": "memory inuse", + "type": "row" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 19 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "LeakedSize:LeakedSize:bytes::", + "queryType": "metrics", + "refId": "A", + "spanSelector": [] + } + ], + "title": "memory-inuse_space metrics", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 19 + }, + "id": 9, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "LeakedSize:LeakedSize:bytes::", + "queryType": "profile", + "refId": "A", + "spanSelector": [] + } + ], + "title": "memory-inuse_space profile", + "type": "flamegraph" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 27 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "LeakedCount:LeakedCount:counts::", + "queryType": "metrics", + "refId": "A", + "spanSelector": [] + } + ], + "title": "memory-inuse_alloc metrics", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 27 + }, + "id": 12, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "LeakedCount:LeakedCount:counts::", + "queryType": "profile", + "refId": "A", + "spanSelector": [] + } + ], + "title": "memory-inuse_alloc profile", + "type": "flamegraph" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 35 + }, + "id": 13, + "panels": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 12 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "IOSize:IOSize:bytes::", + "queryType": "metrics", + "refId": "A", + "spanSelector": [] + } + ], + "title": "IO size metrics", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 12 + }, + "id": 20, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "IOSize:IOSize:bytes::", + "queryType": "profile", + "refId": "A", + "spanSelector": [] + } + ], + "title": "io size profile", + "type": "flamegraph" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 20 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "IOCount:IOCount:counts::", + "queryType": "metrics", + "refId": "A", + "spanSelector": [] + } + ], + "title": "IO count metrics", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 20 + }, + "id": 17, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "IOCount:IOCount:counts::", + "queryType": "profile", + "refId": "A", + "spanSelector": [] + } + ], + "title": "io count profile", + "type": "flamegraph" + } + ], + "title": "IO", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 36 + }, + "id": 18, + "panels": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 14 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "UnusedReadaheadPages:UnusedReadaheadPages:pages::", + "queryType": "metrics", + "refId": "A", + "spanSelector": [] + } + ], + "title": "readahead unused metrics", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 14 + }, + "id": 25, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "UnusedReadaheadPages:UnusedReadaheadPages:pages::", + "queryType": "profile", + "refId": "A", + "spanSelector": [] + } + ], + "title": "readahead unused profile", + "type": "flamegraph" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 22 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "UsedReadaheadPages:UsedReadaheadPages:pages::", + "queryType": "metrics", + "refId": "A", + "spanSelector": [] + } + ], + "title": "readahead used metrics", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 22 + }, + "id": 22, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "UsedReadaheadPages:UsedReadaheadPages:pages::", + "queryType": "profile", + "refId": "A", + "spanSelector": [] + } + ], + "title": "readahead used profile", + "type": "flamegraph" + } + ], + "title": "readahead", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 37 + }, + "id": 23, + "panels": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 1 + }, + "id": 24, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "vfs_openCount:vfs_openCount:counts::", + "queryType": "metrics", + "refId": "A", + "spanSelector": [] + } + ], + "title": "vfs_open count metrics", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 1 + }, + "id": 15, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "vfs_openCount:vfs_openCount:counts::", + "queryType": "profile", + "refId": "A", + "spanSelector": [] + } + ], + "title": "vfs_open count profile", + "type": "flamegraph" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 26, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "vfs_openTime:vfs_openTime:nanoseconds::", + "queryType": "metrics", + "refId": "A", + "spanSelector": [] + } + ], + "title": "vfs_open time metrics", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 9 + }, + "id": 27, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "vfs_openTime:vfs_openTime:nanoseconds::", + "queryType": "profile", + "refId": "A", + "spanSelector": [] + } + ], + "title": "vfs_open time profile", + "type": "flamegraph" + } + ], + "title": "vfs_open", + "type": "row" + } + ], + "refresh": "", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-30m", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "stack_analyzer_dashboard", + "uid": "fdlqh6mke9hq8e", + "version": 6, + "weekStart": "" +} \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/eBPFStackCollector.h b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/eBPFStackCollector.h index 08bf2cb04..ff07187c6 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/eBPFStackCollector.h +++ b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/eBPFStackCollector.h @@ -23,7 +23,7 @@ #include #include #include -#include "sa_user.h" +#include "user.h" struct Scale { @@ -48,7 +48,7 @@ struct CountItem class StackCollector { protected: - int self_pid = -1; + int self_tgid = -1; struct bpf_object *obj = NULL; // 默认显示计数的变化情况,即每次输出数据后清除计数 @@ -58,8 +58,11 @@ class StackCollector public: Scale *scales; - int pid = -1; // 用于设置ebpf程序跟踪的pid - int err = 0; // 用于保存错误代码 + uint32_t top = 10; + uint32_t freq = 49; + uint64_t cgroup = 0; + uint32_t tgid = 0; + int err = 0; // 用于保存错误代码 bool ustack = false; // 是否跟踪用户栈 bool kstack = false; // 是否跟踪内核栈 @@ -76,23 +79,8 @@ class StackCollector StackCollector(); operator std::string(); - /// @brief 负责ebpf程序的加载、参数设置和打开操作 - /// @param 无 - /// @return 成功则返回0,否则返回负数 - virtual int load(void) = 0; - - /// @brief 将ebpf程序挂载到跟踪点上 - /// @param 无 - /// @return 成功则返回0,否则返回负数 - virtual int attach(void) = 0; - - /// @brief 断开ebpf的跟踪点和处理函数间的连接 - /// @param 无 - virtual void detach(void) = 0; - - /// @brief 卸载ebpf程序 - /// @param 无 - virtual void unload(void) = 0; + virtual int ready(void) = 0; + virtual void finish(void) = 0; /// @brief 激活eBPF程序 /// @param 无 @@ -106,23 +94,26 @@ class StackCollector /// @brief 加载、初始化参数并打开指定类型的ebpf程序 /// @param ... 一些ebpf程序全局变量初始化语句 /// @note 失败会使上层函数返回-1 -#define EBPF_LOAD_OPEN_INIT(...) \ - { \ - skel = skel->open(NULL); \ - CHECK_ERR(!skel, "Fail to open BPF skeleton"); \ - __VA_ARGS__; \ - skel->rodata->trace_user = ustack; \ - skel->rodata->trace_kernel = kstack; \ - skel->rodata->self_pid = self_pid; \ - err = skel->load(skel); \ - CHECK_ERR(err, "Fail to load BPF skeleton"); \ - obj = skel->obj; \ +#define EBPF_LOAD_OPEN_INIT(...) \ + { \ + skel = skel->open(NULL); \ + CHECK_ERR_RN1(!skel, "Fail to open BPF skeleton"); \ + __VA_ARGS__; \ + skel->rodata->trace_user = ustack; \ + skel->rodata->trace_kernel = kstack; \ + skel->rodata->self_tgid = self_tgid; \ + skel->rodata->target_tgid = tgid; \ + skel->rodata->target_cgroupid = cgroup; \ + skel->rodata->freq = freq; \ + err = skel->load(skel); \ + CHECK_ERR_RN1(err, "Fail to load BPF skeleton"); \ + obj = skel->obj; \ } -#define ATTACH_PROTO \ - { \ - err = skel->attach(skel); \ - CHECK_ERR(err, "Failed to attach BPF skeleton"); \ +#define ATTACH_PROTO \ + { \ + err = skel->attach(skel); \ + CHECK_ERR_RN1(err, "Failed to attach BPF skeleton"); \ } #define DETACH_PROTO \ diff --git a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/io.h b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/io.h index 8cef7ece0..227b1bcf7 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/io.h +++ b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/io.h @@ -40,10 +40,8 @@ class IOStackCollector : public StackCollector public: IOStackCollector(); - virtual int load(void); - virtual int attach(void); - virtual void detach(void); - virtual void unload(void); + virtual int ready(void); + virtual void finish(void); virtual void activate(bool tf); virtual const char *getName(void); }; diff --git a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/llc_stat.h b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/llc_stat.h index e801e4ea5..2a365255e 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/llc_stat.h +++ b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/llc_stat.h @@ -51,10 +51,8 @@ class LlcStatStackCollector : public StackCollector public: LlcStatStackCollector(); - virtual int load(void); - virtual int attach(void); - virtual void detach(void); - virtual void unload(void); + virtual int ready(void); + virtual void finish(void); virtual void activate(bool tf); virtual const char *getName(void); void setScale(uint64_t period); diff --git a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/memleak.h b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/memleak.h index 08afd79fb..db7a5f774 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/memleak.h +++ b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/memleak.h @@ -60,7 +60,6 @@ class MemleakStackCollector : public StackCollector public: char *object = (char *)"libc.so.6"; bool percpu = false; - __u64 sample_rate = 1; bool wa_missing_free = false; protected: @@ -70,10 +69,8 @@ class MemleakStackCollector : public StackCollector public: MemleakStackCollector(); - virtual int load(void); - virtual int attach(void); - virtual void detach(void); - virtual void unload(void); + virtual int ready(void); + virtual void finish(void); virtual void activate(bool tf); virtual const char *getName(void); @@ -91,7 +88,7 @@ class MemleakStackCollector : public StackCollector skel->links.prog_name = \ bpf_program__attach_uprobe_opts( \ skel->progs.prog_name, \ - pid, \ + tgid, \ object, \ 0, \ &uprobe_opts); \ @@ -107,7 +104,7 @@ class MemleakStackCollector : public StackCollector do \ { \ __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe); \ - CHECK_ERR(!skel->links.prog_name, "no program attached for " #prog_name "\n") \ + CHECK_ERR_RN1(!skel->links.prog_name, "no program attached for " #prog_name "\n") \ } while (false) #define ATTACH_UPROBE(skel, sym_name, prog_name) __ATTACH_UPROBE(skel, sym_name, prog_name, false) diff --git a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/off_cpu.h b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/off_cpu.h index df76f5789..d5bcc0aac 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/off_cpu.h +++ b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/off_cpu.h @@ -32,10 +32,8 @@ class OffCPUStackCollector : public StackCollector public: OffCPUStackCollector(); - virtual int load(void); - virtual int attach(void); - virtual void detach(void); - virtual void unload(void); + virtual int ready(void); + virtual void finish(void); virtual void activate(bool tf); virtual const char *getName(void); }; diff --git a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/on_cpu.h b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/on_cpu.h index cd79dfccc..26d11ed28 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/on_cpu.h +++ b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/on_cpu.h @@ -31,7 +31,6 @@ class OnCPUStackCollector : public StackCollector int *pefds = NULL; int num_cpus = 0; struct bpf_link **links = NULL; - unsigned long long freq = 49; protected: virtual uint64_t *count_values(void *); @@ -39,10 +38,8 @@ class OnCPUStackCollector : public StackCollector public: void setScale(uint64_t freq); OnCPUStackCollector(); - virtual int load(void); - virtual int attach(void); - virtual void detach(void); - virtual void unload(void); + virtual int ready(void); + virtual void finish(void); virtual void activate(bool tf); virtual const char *getName(void); }; diff --git a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/probe.h b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/probe.h index 86e0b6182..39dd4a7bb 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/probe.h +++ b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/probe.h @@ -14,16 +14,29 @@ // // author: luiyanbing@foxmail.com // -// probe ebpf程序的包装类,声明接口和一些自定义方法 +// ebpf程序的包装类的模板,声明接口和一些自定义方法,以及辅助结构 -#include "bpf_wapper/eBPFStackCollector.h" +#ifndef _SA_PROBE_H__ +#define _SA_PROBE_H__ + +// ========== C code part ========== +#include +typedef struct +{ + __u64 lat; + __u64 count; +} time_tuple; +// ========== C code end ========== + +#ifdef __cplusplus +// ========== C++ code part ========== #include "probe.skel.h" +#include "bpf_wapper/eBPFStackCollector.h" class ProbeStackCollector : public StackCollector { private: - struct probe_bpf *skel = __null; - + DECL_SKEL(probe); public: std::string probe; @@ -33,10 +46,12 @@ class ProbeStackCollector : public StackCollector public: void setScale(std::string probe); ProbeStackCollector(); - virtual int load(void); - virtual int attach(void); - virtual void detach(void); - virtual void unload(void); + virtual int ready(void); + virtual void finish(void); virtual void activate(bool tf); virtual const char *getName(void); }; +// ========== C++ code end ========== +#endif + +#endif \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/readahead.h b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/readahead.h index d3cd94850..41e48947e 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/readahead.h +++ b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/readahead.h @@ -40,10 +40,8 @@ class ReadaheadStackCollector : public StackCollector public: ReadaheadStackCollector(); - virtual int load(void); - virtual int attach(void); - virtual void detach(void); - virtual void unload(void); + virtual int ready(void); + virtual void finish(void); virtual void activate(bool tf); virtual const char *getName(void); }; diff --git a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/template.h b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/template.h index f2b9aa53d..fe728eb4d 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/template.h +++ b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/template.h @@ -38,10 +38,8 @@ class TemplateClass : public StackCollector public: TemplateClass(); - virtual int load(void); - virtual int attach(void); - virtual void detach(void); - virtual void unload(void); + virtual int ready(void); + virtual void finish(void); virtual void activate(bool tf); virtual const char *getName(void); }; diff --git a/eBPF_Supermarket/Stack_Analyser/include/cgroup.h b/eBPF_Supermarket/Stack_Analyser/include/cgroup.h new file mode 100644 index 000000000..a9157a603 --- /dev/null +++ b/eBPF_Supermarket/Stack_Analyser/include/cgroup.h @@ -0,0 +1,9 @@ +#include +struct cgid_file_handle +{ + // struct file_handle handle; + unsigned int handle_bytes; + int handle_type; + uint64_t cgid; +}; +uint64_t get_cgroupid(const char *pathname); \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/include/sa_common.h b/eBPF_Supermarket/Stack_Analyser/include/common.h similarity index 81% rename from eBPF_Supermarket/Stack_Analyser/include/sa_common.h rename to eBPF_Supermarket/Stack_Analyser/include/common.h index b5707d644..ad77f45e3 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/sa_common.h +++ b/eBPF_Supermarket/Stack_Analyser/include/common.h @@ -27,23 +27,17 @@ #define CONTAINER_ID_LEN (128) /// @brief 栈计数的键,可以唯一标识一个用户内核栈 -typedef struct { +typedef struct +{ __u32 pid; __s32 ksid, usid; } psid; -typedef struct { +typedef struct +{ __u32 pid; __u32 tgid; - char cid[CONTAINER_ID_LEN]; char comm[COMM_LEN]; } task_info; -#define _COL_PREFIX "\033[" -#define _BLUE _COL_PREFIX "1;34m" -#define _GREEN _COL_PREFIX "1;32m" -#define _RED _COL_PREFIX "1;35m" -#define _ERED _COL_PREFIX "1;31m" -#define _RE _COL_PREFIX "0m" - #endif diff --git a/eBPF_Supermarket/Stack_Analyser/include/dt_elf.h b/eBPF_Supermarket/Stack_Analyser/include/dt_elf.h deleted file mode 100644 index a6174a376..000000000 --- a/eBPF_Supermarket/Stack_Analyser/include/dt_elf.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Linux内核诊断工具--elf相关函数头文件 - * - * Copyright (C) 2020 Alibaba Ltd. - * - * License terms: GNU General Public License (GPL) version 3 - * - */ - -#ifndef _PERF_ELF_H__ -#define _PERF_ELF_H__ - -#include -#include - -#include "dt_symbol.h" - -#define BUILD_ID_SIZE 40 -bool save_symbol_cache(std::set &ss, const char *path); -bool load_symbol_cache(std::set &ss, const char *path, const char *filename); - -bool get_symbol_from_elf(std::set &ss, const char *path); -bool search_symbol(const std::set &ss, symbol &sym); -int filename__read_build_id(int pid, const char *mnt_ns_name, const char *filename, char *bf, size_t size); -#endif diff --git a/eBPF_Supermarket/Stack_Analyser/include/dt_symbol.h b/eBPF_Supermarket/Stack_Analyser/include/dt_symbol.h deleted file mode 100644 index 3f2c3ffe9..000000000 --- a/eBPF_Supermarket/Stack_Analyser/include/dt_symbol.h +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Linux内核诊断工具--用户态符号表解析 - * - * Copyright (C) 2020 Alibaba Ltd. - * - * License terms: GNU General Public License (GPL) version 3 - * - */ - -#ifndef __PERF_SYMBOL_H__ -#define __PERF_SYMBOL_H__ - -#include -#include -#include - -//#include - -#define INVALID_ADDR ((size_t)(-1)) -enum { - NATIVE_TYPE = 0, - JIT_TYPE = 1, - UNKNOWN = 2, -}; - -struct elf_file { - unsigned char elf_read_error; - size_t eh_frame_hdr_offset; - size_t fde_count; - size_t table_data; - std::string filename; - int type; - - // TODO get builid from elf header or build hash for elf - elf_file(const std::string &name) : filename(name), type(NATIVE_TYPE) { - elf_read_error = 0; - eh_frame_hdr_offset = 0; - fde_count = 0; - table_data = 0; - } - - elf_file() :type(NATIVE_TYPE) {} - - // TODO get builid from elf header or build hash for elf - void reset(const std::string &name) { - filename = name; - elf_read_error = 0; - eh_frame_hdr_offset = 0; - fde_count = 0; - table_data = 0; - } - - bool operator< (const elf_file &rhs) const { - return filename < rhs.filename; - } -}; - -struct symbol { - size_t start; - size_t end; - size_t ip; - std::string name; - - symbol() :start(0), end(0), ip(0) {} - symbol(size_t pc) :start(0), end(0), ip(pc) {} - - void reset(size_t va) { start = end = 0; ip = va; } - bool operator< (const symbol &sym) const { - return sym.ip < start; - } - - bool operator> (const symbol &sym) const { - return sym.ip > end; - } -}; - -struct vma { - size_t start; - size_t end; - size_t offset; - size_t pc; - int type; - std::string name; - struct { - unsigned char elf_read_error; - size_t eh_frame_hdr_offset; - size_t fde_count; - size_t table_data; - }; - - size_t map(size_t pc) { - return pc - start + offset; - } - - void set_type(int t) { type = t; } - - vma(size_t s, size_t e, size_t o, const std::string &n) - :start(s), end(e), offset(o), pc(0), type(NATIVE_TYPE), name(n) {} - - vma() : start(0), end(0), offset(0), pc(0), type(NATIVE_TYPE) {} - - vma(size_t addr) : start(0), end(0), offset(0), pc(addr), type(NATIVE_TYPE) {} - - bool operator<(const vma &vm) { - return vm.start < vm.pc; - } - - vma &operator=(const vma &vm) { - if (this == &vm) { - return *this; - } - start = vm.start; - end = vm.end; - offset = vm.offset; - name = vm.name; - return *this; - } -}; - -static inline bool operator==(const vma &lhs, const vma &rhs) { - return lhs.start == rhs.start && lhs.end == rhs.end && lhs.name == rhs.name; -} - -class symbol_parser { -private: - typedef std::map proc_vma; - - std::map > file_symbols; - std::map > java_symbols; - std::set kernel_symbols; - std::map machine_vma; - std::set java_procs; - std::map > symbols_cache; -public: - bool load_kernel(); - std::set& get_java_procs() { return java_procs; } - - bool find_kernel_symbol(symbol &sym); - bool complete_kernel_symbol(symbol &sym); - - /// @brief 从elf file中查找sym中地址对应的符号名存入sym - /// @param sym 符号对象 - /// @param file 进程对应的elf file - /// @param pid 进程 - /// @param pid_ns 进程的命名空间? - /// @return 查找成功返回true,否则返回false - bool find_elf_symbol(symbol &sym, const elf_file &file, int pid, int pid_ns); - bool find_java_symbol(symbol &sym, int pid, int pid_ns); - - bool get_symbol_info(int pid, symbol &sym, elf_file &file); - - bool find_vma(pid_t pid, vma &vm); - vma* find_vma(pid_t pid, size_t pc); - void clear_symbol_info(int); - bool add_pid_maps(int pid, size_t start, size_t end, size_t offset, const char *name); - - bool find_symbol_in_cache(int tgid, unsigned long addr, std::string &symbol); - bool putin_symbol_cache(int tgid, unsigned long addr, std::string &symbol); - - void dump(void); -private: - bool load_pid_maps(int pid); - /// @brief 对elf_file对应的符号表进行缓存 -/// @param pid 未使用 -/// @param file elf file -/// @return 缓存成功返回true,否则返回false - bool load_elf(pid_t pid, const elf_file& file); - bool load_perf_map(int pid, int pid_ns); -public: - int java_only; - int user_symbol; -}; - -extern symbol_parser g_symbol_parser; - -std::string demangleCppSym(std::string symbol); -void clearSpace(std::string &sym); - -#endif diff --git a/eBPF_Supermarket/Stack_Analyser/include/ebpf.h b/eBPF_Supermarket/Stack_Analyser/include/ebpf.h new file mode 100644 index 000000000..84ee017f6 --- /dev/null +++ b/eBPF_Supermarket/Stack_Analyser/include/ebpf.h @@ -0,0 +1,169 @@ +// Copyright 2024 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: luiyanbing@foxmail.com +// +// 用于eBPF程序的宏 + +#ifndef STACK_ANALYZER_EBPF +#define STACK_ANALYZER_EBPF + +#include "common.h" +#include + +#define PF_KTHREAD 0x00200000 + +/// @brief 创建一个指定名字的ebpf调用栈表 +/// @param 新栈表的名字 +#define BPF_STACK_TRACE(name) \ + struct \ + { \ + __uint(type, BPF_MAP_TYPE_STACK_TRACE); \ + __uint(key_size, sizeof(__u32)); \ + __uint(value_size, MAX_STACKS * sizeof(__u64)); \ + __uint(max_entries, MAX_ENTRIES); \ + } name SEC(".maps") + +/// @brief 创建一个指定名字和键值类型的ebpf散列表 +/// @param name 新散列表的名字 +/// @param type1 键的类型 +/// @param type2 值的类型 +#define BPF_HASH(name, _kt, _vt, _cap) \ + struct \ + { \ + __uint(type, BPF_MAP_TYPE_HASH); \ + __type(key, _kt); \ + __type(value, _vt); \ + __uint(max_entries, _cap); \ + } name SEC(".maps") + +/** + * 用于在eBPF代码中声明通用的maps,其中 + * psid_count_map 存储 键值对,记录了id(由pid、ksid和usid(内核、用户栈id))及相应的值 + * sid_trace_map 存储 键值对,记录了栈id(ksid或usid)及相应的栈 + * pid_tgid 存储 键值对,记录pid以及对应的tgid + * pid_comm 存储 键值对,记录pid以及对应的命令名 + * type:指定count值的类型 + */ +#define COMMON_MAPS(count_type) \ + BPF_HASH(psid_count_map, psid, count_type, MAX_ENTRIES); \ + BPF_STACK_TRACE(sid_trace_map); \ + BPF_HASH(tgid_cgroup_map, __u32, \ + char[CONTAINER_ID_LEN], MAX_ENTRIES / 100); \ + BPF_HASH(pid_info_map, u32, task_info, MAX_ENTRIES / 10); + +#define COMMON_VALS \ + const volatile bool trace_user = false; \ + const volatile bool trace_kernel = false; \ + const volatile __u64 target_cgroupid = 0; \ + const volatile __u32 target_tgid = 0; \ + const volatile __u32 self_tgid = 0; \ + const volatile __u32 freq = 0; \ + bool __active = false; \ + __u64 __last_n = 0; \ + __u64 __next_n = 0; + +#define CHECK_ACTIVE \ + if (!__active) \ + return 0; + +#define TS bpf_ktime_get_ns() + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 5, 0) +#define CHECK_FREQ(_ts) +#else +/* + 内置函数: + bool __atomic_compare_exchange_n ( + type *ptr, type *expected, type desired, bool weak, + int success_memorder, int failure_memorder + ) + + * 内置函数__atomic_compare_exchange_n实现了原子性的比较和交换操作。 + * 该函数用于比较指针ptr所指向位置的内容与expected所指向位置的内容。 + * 如果相等,则进行读-修改-写操作,将desired数据写入ptr; + * 如果不相等,则进行读操作,将当前ptr的内容写入expected。 + * 参数weak表示使用弱类型compare_exchange(true) + * 或强类型compare_exchange(false), + * 其中弱类型可能会失败而强类型永远不会失败。 + * 在许多情况下,目标只提供了强变体并忽略了该参数。 + * 当存在疑虑时,请使用强变体。 + * + * 如果desired成功写入ptr,则返回true, + * 并根据success_memorder指定的内存顺序影响内存; + * 对可用的内存顺序没有限制。 + * + * 否则,返回false,并根据failure_memorder影响内存; + * 该内存顺序不能是__ATOMIC_RELEASE或__ATOMIC_ACQ_REL, + * 并且不能比success_memorder更严格。 + */ +#define CHECK_FREQ(_ts) \ + if (freq) \ + { \ + __next_n = (_ts * freq) >> 30; \ + if (__atomic_compare_exchange_n( \ + &__next_n, &__last_n, __next_n, true, \ + __ATOMIC_RELAXED, __ATOMIC_RELAXED)) \ + return 0; \ + } +#endif + +#define GET_CURR \ + (struct task_struct *)bpf_get_current_task() + +// 如果没有设置目标进程,则检查被采集进程是否为内核线程,是则退出采集 +#define CHECK_KTHREAD(_task) \ + if (!target_tgid && BPF_CORE_READ(_task, flags) & PF_KTHREAD) \ + return 0; + +#define CHECK_TGID(_tgid) \ + if ((!_tgid) || (_tgid == self_tgid) || (target_tgid > 0 && _tgid != target_tgid)) \ + return 0; + +#define GET_KNODE(_task) \ + BPF_CORE_READ(_task, cgroups, dfl_cgrp, kn) + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 5, 0) +#define CHECK_CGID(_knode) \ + if (target_cgroupid > 0 && BPF_CORE_READ(_knode, id.id) != target_cgroupid) \ + return 0; +#else +#define CHECK_CGID(_knode) \ + if (target_cgroupid > 0 && BPF_CORE_READ(_knode, id) != target_cgroupid) \ + return 0; +#endif + +#define TRY_SAVE_INFO(_task, _pid, _tgid, _knode) \ + if (!bpf_map_lookup_elem(&pid_info_map, &_pid)) \ + { \ + task_info info = {0}; \ + bpf_core_read_str(info.comm, COMM_LEN, &_task->comm); \ + info.tgid = _tgid; \ + bpf_map_update_elem(&pid_info_map, &_pid, &info, BPF_NOEXIST); \ + if (!bpf_map_lookup_elem(&tgid_cgroup_map, &(info.tgid))) \ + { \ + char cgroup_name[CONTAINER_ID_LEN] = {0}; \ + bpf_probe_read_kernel_str(cgroup_name, CONTAINER_ID_LEN, BPF_CORE_READ(_knode, name)); \ + bpf_map_update_elem(&tgid_cgroup_map, &(info.tgid), &cgroup_name, BPF_NOEXIST); \ + } \ + } + +#define TRACE_AND_GET_COUNT_KEY(_pid, _ctx) \ + { \ + .pid = _pid, \ + .usid = trace_user ? bpf_get_stackid(_ctx, &sid_trace_map, BPF_F_FAST_STACK_CMP | BPF_F_USER_STACK) : -1, \ + .ksid = trace_kernel ? bpf_get_stackid(_ctx, &sid_trace_map, BPF_F_FAST_STACK_CMP) : -1, \ + } + +#endif \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/include/sa_ebpf.h b/eBPF_Supermarket/Stack_Analyser/include/sa_ebpf.h deleted file mode 100644 index 433068353..000000000 --- a/eBPF_Supermarket/Stack_Analyser/include/sa_ebpf.h +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2024 The LMP Authors. -// -// 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 -// -// https://github.com/linuxkerneltravel/lmp/blob/develop/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: luiyanbing@foxmail.com -// -// 用于eBPF程序的宏 - -#ifndef STACK_ANALYZER_EBPF -#define STACK_ANALYZER_EBPF - -#include "sa_common.h" - -#define PF_KTHREAD 0x00200000 -#define RET_IF_KERN(task) \ - do \ - { \ - int flags = BPF_CORE_READ(task, flags); \ - if (flags & PF_KTHREAD) \ - return 0; \ - } while (false) - -/// @brief 创建一个指定名字的ebpf调用栈表 -/// @param 新栈表的名字 -#define BPF_STACK_TRACE(name) \ - struct \ - { \ - __uint(type, BPF_MAP_TYPE_STACK_TRACE); \ - __uint(key_size, sizeof(__u32)); \ - __uint(value_size, MAX_STACKS * sizeof(__u64)); \ - __uint(max_entries, MAX_ENTRIES); \ - } name SEC(".maps") - -/// @brief 创建一个指定名字和键值类型的ebpf散列表 -/// @param name 新散列表的名字 -/// @param type1 键的类型 -/// @param type2 值的类型 -#define BPF_HASH(name, type1, type2) \ - struct \ - { \ - __uint(type, BPF_MAP_TYPE_HASH); \ - __uint(key_size, sizeof(type1)); \ - __uint(value_size, sizeof(type2)); \ - __uint(max_entries, MAX_ENTRIES); \ - } name SEC(".maps") - -/** - * 用于在eBPF代码中声明通用的maps,其中 - * psid_count_map 存储 键值对,记录了id(由pid、ksid和usid(内核、用户栈id))及相应的值 - * sid_trace_map 存储 键值对,记录了栈id(ksid或usid)及相应的栈 - * pid_tgid 存储 键值对,记录pid以及对应的tgid - * pid_comm 存储 键值对,记录pid以及对应的命令名 - * type:指定count值的类型 - */ -#define COMMON_MAPS(count_type) \ - BPF_HASH(psid_count_map, psid, count_type); \ - BPF_STACK_TRACE(sid_trace_map); \ - BPF_HASH(pid_info_map, u32, task_info); - -#define COMMON_VALS \ - const volatile bool trace_user = false; \ - const volatile bool trace_kernel = false; \ - const volatile int self_pid = 0; \ - bool __active = false; - -#define CHECK_ACTIVE if(!__active) return 0; - -#define SAVE_TASK_INFO(_pid, _task) \ - if (!bpf_map_lookup_elem(&pid_info_map, &_pid)) \ - { \ - task_info info; \ - info.pid = get_task_ns_pid(_task); \ - bpf_get_current_comm(info.comm, COMM_LEN); \ - info.tgid = get_task_ns_tgid(_task); \ - fill_container_id(_task, info.cid); \ - bpf_map_update_elem(&pid_info_map, &_pid, &info, BPF_NOEXIST); \ - } - -#define GET_COUNT_KEY(_pid, _ctx) \ - ((psid){ \ - .pid = _pid, \ - .usid = trace_user ? bpf_get_stackid(_ctx, &sid_trace_map, BPF_F_FAST_STACK_CMP | BPF_F_USER_STACK) : -1, \ - .ksid = trace_kernel ? bpf_get_stackid(_ctx, &sid_trace_map, BPF_F_FAST_STACK_CMP) : -1, \ - }) - -#endif \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/include/sa_user.h b/eBPF_Supermarket/Stack_Analyser/include/sa_user.h deleted file mode 100644 index ae05013f2..000000000 --- a/eBPF_Supermarket/Stack_Analyser/include/sa_user.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2024 The LMP Authors. -// -// 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 -// -// https://github.com/linuxkerneltravel/lmp/blob/develop/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: luiyanbing@foxmail.com -// -// 用户态使用的宏 - -#ifndef STACK_ANALYZER_USER -#define STACK_ANALYZER_USER - -#include -#include -#include -#include -#include -#include - -#include "sa_common.h" - -/// @brief 检查错误,若错误成立则打印带原因的错误信息并使上层函数返回-1 -/// @param cond 被检查的条件表达式 -/// @param info 要打印的错误信息 -#define CHECK_ERR(cond, ...) \ - if (cond) \ - { \ - fprintf(stderr, _ERED __VA_ARGS__); \ - fprintf(stderr, " [%s]\n" _RE, strerror(errno)); \ - return -1; \ - } - -#include -/// @brief 检查错误,若错误成立则打印带原因的错误信息并退出 -/// @param cond 被检查的条件表达式 -/// @param info 要打印的错误信息 -#define CHECK_ERR_EXIT(cond, ...) \ - if (cond) \ - { \ - fprintf(stderr, __VA_ARGS__); \ - fprintf(stderr, " [%s]\n", strerror(errno)); \ - exit(EXIT_FAILURE); \ - } - -#define BANNER \ - "\033[38;2;214;165;4m \033[39m\033[38;2;219;159;5m \033[39m\033[38;2;223;152;7m \033[39m\033[38;2;227;146;9m_\033[39m\033[38;2;231;140;12m_\033[39m\033[38;2;234;133;15m_\033[39m\033[38;2;238;127;18m_\033[39m\033[38;2;241;121;21m_\033[39m\033[38;2;243;114;25m \033[39m\033[38;2;246;108;28m_\033[39m\033[38;2;248;102;33m_\033[39m\033[38;2;250;96;37m \033[39m\033[38;2;251;90;41m \033[39m\033[38;2;253;84;46m \033[39m\033[38;2;254;78;51m \033[39m\033[38;2;254;72;56m \033[39m\033[38;2;254;66;62m \033[39m\033[38;2;254;61;67m \033[39m\033[38;2;254;55;73m \033[39m\033[38;2;253;50;79m \033[39m\033[38;2;252;45;85m \033[39m\033[38;2;251;41;91m \033[39m\033[38;2;250;36;97m \033[39m\033[38;2;248;32;103m \033[39m\033[38;2;245;28;109m_\033[39m\033[38;2;243;24;116m_\033[39m\033[38;2;240;20;122m \033[39m\033[38;2;237;17;128m \033[39m\033[38;2;234;14;135m \033[39m\033[38;2;230;11;141m_\033[39m\033[38;2;226;9;147m_\033[39m\033[38;2;222;7;153m_\033[39m\033[38;2;218;5;160m \033[39m\033[38;2;213;4;166m \033[39m\033[38;2;208;2;172m \033[39m\033[38;2;204;1;178m \033[39m\033[38;2;198;1;183m \033[39m\033[38;2;193;1;189m \033[39m\033[38;2;187;1;194m \033[39m\033[38;2;182;1;200m \033[39m\033[38;2;176;2;205m \033[39m\033[38;2;170;3;210m \033[39m\033[38;2;164;4;215m \033[39m\033[38;2;158;6;219m \033[39m\033[38;2;152;7;223m \033[39m\033[38;2;145;10;227m \033[39m\033[38;2;139;12;231m \033[39m\033[38;2;133;15;235m \033[39m\033[38;2;126;18;238m_\033[39m\033[38;2;120;21;241m_\033[39m\033[38;2;114;25;244m \033[39m\033[38;2;107;29;246m \033[39m\033[38;2;101;33;248m \033[39m\033[38;2;95;37;250m \033[39m\033[38;2;89;42;252m \033[39m\033[38;2;83;47;253m \033[39m\033[38;2;77;52;254m \033[39m\033[38;2;71;57;254m \033[39m\033[38;2;66;62;254m \033[39m\033[38;2;60;68;254m \033[39m\033[38;2;55;74;254m \033[39m\033[38;2;50;79;253m \033[39m\033[38;2;45;85;252m \033[39m\033[38;2;40;91;251m \033[39m\033[38;2;36;97;249m \033[39m\033[38;2;31;104;247m \033[39m\033[38;2;27;110;245m \033[39m\033[38;2;24;116;243m \033[39m\033[38;2;20;123;240m \033[39m\033[38;2;17;129;237m \033[39m\033[38;2;14;135;233m \033[39m\033[38;2;11;142;230m\033[39m\n" \ - "\033[38;2;215;164;4m \033[39m\033[38;2;219;158;5m \033[39m\033[38;2;223;152;7m/\033[39m\033[38;2;227;145;10m \033[39m\033[38;2;231;139;12m_\033[39m\033[38;2;235;133;15m_\033[39m\033[38;2;238;127;18m_\033[39m\033[38;2;241;120;21m/\033[39m\033[38;2;244;114;25m/\033[39m\033[38;2;246;108;29m \033[39m\033[38;2;248;101;33m/\033[39m\033[38;2;250;95;37m_\033[39m\033[38;2;252;89;42m_\033[39m\033[38;2;253;83;47m_\033[39m\033[38;2;254;77;52m_\033[39m\033[38;2;254;71;57m_\033[39m\033[38;2;254;66;62m \033[39m\033[38;2;254;60;68m_\033[39m\033[38;2;254;55;73m_\033[39m\033[38;2;253;50;79m_\033[39m\033[38;2;252;45;85m_\033[39m\033[38;2;251;40;91m_\033[39m\033[38;2;249;36;97m_\033[39m\033[38;2;247;31;104m/\033[39m\033[38;2;245;27;110m \033[39m\033[38;2;243;24;116m/\033[39m\033[38;2;240;20;122m_\033[39m\033[38;2;237;17;129m_\033[39m\033[38;2;233;14;135m/\033[39m\033[38;2;230;11;142m \033[39m\033[38;2;226;9;148m \033[39m\033[38;2;222;7;154m \033[39m\033[38;2;217;5;160m|\033[39m\033[38;2;213;3;166m \033[39m\033[38;2;208;2;172m \033[39m\033[38;2;203;1;178m_\033[39m\033[38;2;198;1;184m_\033[39m\033[38;2;192;1;190m_\033[39m\033[38;2;187;1;195m_\033[39m\033[38;2;181;1;200m \033[39m\033[38;2;175;2;205m \033[39m\033[38;2;169;3;210m_\033[39m\033[38;2;163;4;215m_\033[39m\033[38;2;157;6;220m_\033[39m\033[38;2;151;8;224m_\033[39m\033[38;2;145;10;228m \033[39m\033[38;2;138;12;232m_\033[39m\033[38;2;132;15;235m/\033[39m\033[38;2;126;18;238m \033[39m\033[38;2;119;22;241m/\033[39m\033[38;2;113;25;244m_\033[39m\033[38;2;107;29;246m \033[39m\033[38;2;101;33;248m \033[39m\033[38;2;94;38;250m_\033[39m\033[38;2;88;42;252m_\033[39m\033[38;2;82;47;253m_\033[39m\033[38;2;76;52;254m_\033[39m\033[38;2;71;57;254m_\033[39m\033[38;2;65;63;254m_\033[39m\033[38;2;60;68;254m \033[39m\033[38;2;54;74;254m \033[39m\033[38;2;49;80;253m_\033[39m\033[38;2;44;86;252m_\033[39m\033[38;2;40;92;251m_\033[39m\033[38;2;35;98;249m \033[39m\033[38;2;31;104;247m \033[39m\033[38;2;27;111;245m_\033[39m\033[38;2;23;117;242m_\033[39m\033[38;2;20;123;240m_\033[39m\033[38;2;16;130;236m_\033[39m\033[38;2;14;136;233m_\033[39m\033[38;2;11;142;229m\033[39m\n" \ - "\033[38;2;215;163;4m \033[39m\033[38;2;219;157;6m \033[39m\033[38;2;224;151;8m\\\033[39m\033[38;2;228;145;10m_\033[39m\033[38;2;232;139;12m_\033[39m\033[38;2;235;132;15m \033[39m\033[38;2;238;126;18m\\\033[39m\033[38;2;241;120;22m/\033[39m\033[38;2;244;113;25m \033[39m\033[38;2;246;107;29m_\033[39m\033[38;2;248;101;33m_\033[39m\033[38;2;250;95;38m/\033[39m\033[38;2;252;88;42m \033[39m\033[38;2;253;82;47m_\033[39m\033[38;2;254;77;52m_\033[39m\033[38;2;254;71;57m \033[39m\033[38;2;254;65;63m`\033[39m\033[38;2;254;60;68m/\033[39m\033[38;2;254;54;74m \033[39m\033[38;2;253;49;80m_\033[39m\033[38;2;252;44;86m_\033[39m\033[38;2;251;40;92m_\033[39m\033[38;2;249;35;98m/\033[39m\033[38;2;247;31;104m \033[39m\033[38;2;245;27;110m/\033[39m\033[38;2;242;23;117m/\033[39m\033[38;2;240;20;123m_\033[39m\033[38;2;236;17;129m/\033[39m\033[38;2;233;14;136m \033[39m\033[38;2;229;11;142m/\033[39m\033[38;2;226;9;148m|\033[39m\033[38;2;221;6;155m \033[39m\033[38;2;217;5;161m|\033[39m\033[38;2;212;3;167m \033[39m\033[38;2;208;2;173m/\033[39m\033[38;2;202;1;179m \033[39m\033[38;2;197;1;185m_\033[39m\033[38;2;192;1;190m_\033[39m\033[38;2;186;1;196m \033[39m\033[38;2;181;1;201m\\\033[39m\033[38;2;175;2;206m/\033[39m\033[38;2;169;3;211m \033[39m\033[38;2;163;4;216m_\033[39m\033[38;2;157;6;220m_\033[39m\033[38;2;150;8;224m \033[39m\033[38;2;144;10;228m`\033[39m\033[38;2;138;13;232m/\033[39m\033[38;2;132;16;235m \033[39m\033[38;2;125;19;239m/\033[39m\033[38;2;119;22;242m \033[39m\033[38;2;112;26;244m/\033[39m\033[38;2;106;30;247m \033[39m\033[38;2;100;34;249m/\033[39m\033[38;2;94;38;250m \033[39m\033[38;2;88;43;252m/\033[39m\033[38;2;82;48;253m_\033[39m\033[38;2;76;53;254m \033[39m\033[38;2;70;58;254m \033[39m\033[38;2;65;63;254m/\033[39m\033[38;2;59;69;254m \033[39m\033[38;2;54;75;254m/\033[39m\033[38;2;49;81;253m \033[39m\033[38;2;44;86;252m_\033[39m\033[38;2;39;93;251m \033[39m\033[38;2;35;99;249m\\\033[39m\033[38;2;31;105;247m/\033[39m\033[38;2;27;111;245m \033[39m\033[38;2;23;118;242m_\033[39m\033[38;2;19;124;239m_\033[39m\033[38;2;16;130;236m_\033[39m\033[38;2;13;137;233m/\033[39m\033[38;2;11;143;229m\033[39m\n" \ - "\033[38;2;215;163;4m \033[39m\033[38;2;220;157;6m_\033[39m\033[38;2;224;151;8m_\033[39m\033[38;2;228;144;10m_\033[39m\033[38;2;232;138;13m/\033[39m\033[38;2;235;132;16m \033[39m\033[38;2;239;125;19m/\033[39m\033[38;2;242;119;22m \033[39m\033[38;2;244;113;26m/\033[39m\033[38;2;247;106;30m_\033[39m\033[38;2;249;100;34m/\033[39m\033[38;2;250;94;38m \033[39m\033[38;2;252;88;43m/\033[39m\033[38;2;253;82;48m_\033[39m\033[38;2;254;76;53m/\033[39m\033[38;2;254;70;58m \033[39m\033[38;2;254;65;63m/\033[39m\033[38;2;254;59;69m \033[39m\033[38;2;254;54;75m/\033[39m\033[38;2;253;49;80m_\033[39m\033[38;2;252;44;86m_\033[39m\033[38;2;251;39;92m/\033[39m\033[38;2;249;35;99m \033[39m\033[38;2;247;31;105m,\033[39m\033[38;2;245;27;111m<\033[39m\033[38;2;242;23;117m \033[39m\033[38;2;239;19;124m/\033[39m\033[38;2;236;16;130m \033[39m\033[38;2;233;13;136m_\033[39m\033[38;2;229;11;143m_\033[39m\033[38;2;225;8;149m_\033[39m\033[38;2;221;6;155m \033[39m\033[38;2;217;5;161m|\033[39m\033[38;2;212;3;168m/\033[39m\033[38;2;207;2;173m \033[39m\033[38;2;202;1;179m/\033[39m\033[38;2;197;1;185m \033[39m\033[38;2;191;1;191m/\033[39m\033[38;2;186;1;196m \033[39m\033[38;2;180;1;201m/\033[39m\033[38;2;174;2;206m \033[39m\033[38;2;168;3;211m/\033[39m\033[38;2;162;4;216m_\033[39m\033[38;2;156;6;220m/\033[39m\033[38;2;150;8;225m \033[39m\033[38;2;144;10;229m/\033[39m\033[38;2;137;13;232m \033[39m\033[38;2;131;16;236m/\033[39m\033[38;2;125;19;239m \033[39m\033[38;2;118;22;242m/\033[39m\033[38;2;112;26;244m_\033[39m\033[38;2;106;30;247m/\033[39m\033[38;2;99;34;249m \033[39m\033[38;2;93;39;251m/\033[39m\033[38;2;87;43;252m \033[39m\033[38;2;81;48;253m/\033[39m\033[38;2;75;53;254m \033[39m\033[38;2;70;59;254m/\033[39m\033[38;2;64;64;254m_\033[39m\033[38;2;59;70;254m/\033[39m\033[38;2;53;75;254m \033[39m\033[38;2;48;81;253m \033[39m\033[38;2;43;87;252m_\033[39m\033[38;2;39;93;251m_\033[39m\033[38;2;34;99;249m/\033[39m\033[38;2;30;106;247m \033[39m\033[38;2;26;112;244m/\033[39m\033[38;2;22;118;242m \033[39m\033[38;2;19;124;239m \033[39m\033[38;2;16;131;236m \033[39m\033[38;2;13;137;232m \033[39m\033[38;2;10;143;229m\033[39m\n" \ - "\033[38;2;216;162;4m/\033[39m\033[38;2;220;156;6m_\033[39m\033[38;2;225;150;8m_\033[39m\033[38;2;229;144;10m_\033[39m\033[38;2;232;137;13m_\033[39m\033[38;2;236;131;16m/\033[39m\033[38;2;239;125;19m\\\033[39m\033[38;2;242;118;22m_\033[39m\033[38;2;244;112;26m_\033[39m\033[38;2;247;106;30m/\033[39m\033[38;2;249;99;34m\\\033[39m\033[38;2;251;93;39m_\033[39m\033[38;2;252;87;43m_\033[39m\033[38;2;253;81;48m,\033[39m\033[38;2;254;75;53m_\033[39m\033[38;2;254;70;58m/\033[39m\033[38;2;254;64;64m\\\033[39m\033[38;2;254;59;69m_\033[39m\033[38;2;254;53;75m_\033[39m\033[38;2;253;48;81m_\033[39m\033[38;2;252;43;87m/\033[39m\033[38;2;251;39;93m_\033[39m\033[38;2;249;34;99m/\033[39m\033[38;2;247;30;105m|\033[39m\033[38;2;245;26;112m_\033[39m\033[38;2;242;23;118m/\033[39m\033[38;2;239;19;124m_\033[39m\033[38;2;236;16;131m/\033[39m\033[38;2;232;13;137m \033[39m\033[38;2;229;10;143m \033[39m\033[38;2;225;8;150m|\033[39m\033[38;2;220;6;156m_\033[39m\033[38;2;216;4;162m/\033[39m\033[38;2;211;3;168m_\033[39m\033[38;2;207;2;174m/\033[39m\033[38;2;201;1;180m \033[39m\033[38;2;196;1;186m/\033[39m\033[38;2;191;1;191m_\033[39m\033[38;2;185;1;197m/\033[39m\033[38;2;179;1;202m\\\033[39m\033[38;2;174;2;207m_\033[39m\033[38;2;168;3;212m_\033[39m\033[38;2;162;5;216m,\033[39m\033[38;2;155;6;221m_\033[39m\033[38;2;149;8;225m/\033[39m\033[38;2;143;11;229m_\033[39m\033[38;2;137;13;233m/\033[39m\033[38;2;130;16;236m\\\033[39m\033[38;2;124;19;239m_\033[39m\033[38;2;118;23;242m_\033[39m\033[38;2;111;27;245m,\033[39m\033[38;2;105;31;247m \033[39m\033[38;2;99;35;249m/\033[39m\033[38;2;93;39;251m \033[39m\033[38;2;87;44;252m/\033[39m\033[38;2;81;49;253m_\033[39m\033[38;2;75;54;254m_\033[39m\033[38;2;69;59;254m_\033[39m\033[38;2;63;65;254m/\033[39m\033[38;2;58;70;254m\\\033[39m\033[38;2;53;76;254m_\033[39m\033[38;2;48;82;253m_\033[39m\033[38;2;43;88;252m_\033[39m\033[38;2;38;94;250m/\033[39m\033[38;2;34;100;249m_\033[39m\033[38;2;30;106;247m/\033[39m\033[38;2;26;112;244m \033[39m\033[38;2;22;119;242m \033[39m\033[38;2;19;125;239m \033[39m\033[38;2;16;131;235m \033[39m\033[38;2;13;138;232m \033[39m\033[38;2;10;144;228m\033[39m\n" \ - "\033[38;2;216;162;4m \033[39m\033[38;2;221;155;6m \033[39m\033[38;2;225;149;8m \033[39m\033[38;2;229;143;11m \033[39m\033[38;2;233;137;13m \033[39m\033[38;2;236;130;16m \033[39m\033[38;2;239;124;19m \033[39m\033[38;2;242;118;23m \033[39m\033[38;2;245;111;27m \033[39m\033[38;2;247;105;30m \033[39m\033[38;2;249;99;35m \033[39m\033[38;2;251;93;39m \033[39m\033[38;2;252;87;44m \033[39m\033[38;2;253;81;49m \033[39m\033[38;2;254;75;54m \033[39m\033[38;2;254;69;59m \033[39m\033[38;2;254;64;64m \033[39m\033[38;2;254;58;70m \033[39m\033[38;2;254;53;76m \033[39m\033[38;2;253;48;82m \033[39m\033[38;2;252;43;88m \033[39m\033[38;2;250;38;94m \033[39m\033[38;2;249;34;100m \033[39m\033[38;2;247;30;106m \033[39m\033[38;2;244;26;112m \033[39m\033[38;2;242;22;119m \033[39m\033[38;2;239;19;125m \033[39m\033[38;2;235;16;131m \033[39m\033[38;2;232;13;138m \033[39m\033[38;2;228;10;144m \033[39m\033[38;2;224;8;150m \033[39m\033[38;2;220;6;157m \033[39m\033[38;2;216;4;163m \033[39m\033[38;2;211;3;169m \033[39m\033[38;2;206;2;175m \033[39m\033[38;2;201;1;181m \033[39m\033[38;2;196;1;186m \033[39m\033[38;2;190;1;192m \033[39m\033[38;2;185;1;197m \033[39m\033[38;2;179;1;202m \033[39m\033[38;2;173;2;207m \033[39m\033[38;2;167;3;212m \033[39m\033[38;2;161;5;217m \033[39m\033[38;2;155;6;221m \033[39m\033[38;2;149;9;225m \033[39m\033[38;2;142;11;229m \033[39m\033[38;2;136;14;233m/\033[39m\033[38;2;130;16;236m_\033[39m\033[38;2;123;20;240m_\033[39m\033[38;2;117;23;242m_\033[39m\033[38;2;111;27;245m_\033[39m\033[38;2;104;31;247m/\033[39m\033[38;2;98;35;249m \033[39m\033[38;2;92;40;251m \033[39m\033[38;2;86;44;252m \033[39m\033[38;2;80;49;253m \033[39m\033[38;2;74;54;254m \033[39m\033[38;2;68;60;254m \033[39m\033[38;2;63;65;254m \033[39m\033[38;2;58;71;254m \033[39m\033[38;2;52;76;254m \033[39m\033[38;2;47;82;253m \033[39m\033[38;2;42;88;252m \033[39m\033[38;2;38;94;250m \033[39m\033[38;2;33;101;248m \033[39m\033[38;2;29;107;246m \033[39m\033[38;2;25;113;244m \033[39m\033[38;2;22;119;241m \033[39m\033[38;2;18;126;238m \033[39m\033[38;2;15;132;235m \033[39m\033[38;2;12;138;232m \033[39m\033[38;2;10;145;228m\033[39m" - -#define COLLECTOR_INFO(_name) _BLUE "\b\b\b\bCollector for " _name " trace" _RE - -#endif diff --git a/eBPF_Supermarket/Stack_Analyser/include/task.h b/eBPF_Supermarket/Stack_Analyser/include/task.h index 79f532543..db2e9b388 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/task.h +++ b/eBPF_Supermarket/Stack_Analyser/include/task.h @@ -71,13 +71,12 @@ statfunc u32 get_task_ns_ppid(struct task_struct *task) return get_task_pid_vnr(real_parent); } -static void fill_container_id(struct task_struct *task, char *container_id) +static void fill_container_id(struct kernfs_node *knode, char *container_id) { - struct kernfs_node *knode = BPF_CORE_READ(task, cgroups, subsys[0], cgroup, kn); if (BPF_CORE_READ(knode, parent) != NULL) { char *aus; - bpf_probe_read(&aus, sizeof(void *), &knode->name); + bpf_probe_read(&aus, sizeof(void *), &(knode->name)); bpf_probe_read_str(container_id, CONTAINER_ID_LEN, aus); } } diff --git a/eBPF_Supermarket/Stack_Analyser/include/trace.h b/eBPF_Supermarket/Stack_Analyser/include/trace.h new file mode 100644 index 000000000..777248f94 --- /dev/null +++ b/eBPF_Supermarket/Stack_Analyser/include/trace.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +#ifndef __TRACE_HELPERS_H +#define __TRACE_HELPERS_H + +#include + +#define NSEC_PER_SEC 1000000000ULL + +struct ksym +{ + const char *name; + unsigned long addr; +}; + +struct ksyms; + +struct ksyms *ksyms__load(void); +void ksyms__free(struct ksyms *ksyms); +const struct ksym *ksyms__map_addr(const struct ksyms *ksyms, + unsigned long addr); +const struct ksym *ksyms__get_symbol(const struct ksyms *ksyms, + const char *name); + +struct sym +{ + const char *name; + unsigned long start; + unsigned long size; + unsigned long offset; +}; + +struct syms; + +struct syms *syms__load_pid(int tgid); +struct syms *syms__load_file(const char *fname, int tgid); +void syms__free(struct syms *syms); +struct sym *syms__map_addr(const struct syms *syms, unsigned long addr); +const struct sym *syms__map_addr_dso(const struct syms *syms, unsigned long addr, + char **dso_name, unsigned long *dso_offset); + +struct syms_cache; + +struct syms_cache *syms_cache__new(int nr); +struct syms *syms_cache__get_syms(struct syms_cache *syms_cache, int tgid); +void syms_cache__free(struct syms_cache *syms_cache); + +struct partition +{ + char *name; + unsigned int dev; +}; + +struct partitions; + +struct partitions *partitions__load(void); +void partitions__free(struct partitions *partitions); +const struct partition * +partitions__get_by_dev(const struct partitions *partitions, unsigned int dev); +const struct partition * +partitions__get_by_name(const struct partitions *partitions, const char *name); + +void print_log2_hist(unsigned int *vals, int vals_size, const char *val_type); +void print_linear_hist(unsigned int *vals, int vals_size, unsigned int base, + unsigned int step, const char *val_type); + +unsigned long long get_ktime_ns(void); + +bool is_kernel_module(const char *name); + +/* + * When attempting to use kprobe/kretprobe, please check out new fentry/fexit + * probes, as they provide better performance and usability. But in some + * situations we have to fallback to kprobe/kretprobe probes. This helper + * is used to detect fentry/fexit support for the specified kernel function. + * + * 1. A gap between kernel versions, kernel BTF is exposed + * starting from 5.4 kernel. but fentry/fexit is actually + * supported starting from 5.5. + * 2. Whether kernel supports module BTF or not + * + * *name* is the name of a kernel function to be attached to, which can be + * from vmlinux or a kernel module. + * *mod* is a hint that indicates the *name* may reside in module BTF, + * if NULL, it means *name* belongs to vmlinux. + */ +bool fentry_can_attach(const char *name, const char *mod); + +/* + * The name of a kernel function to be attached to may be changed between + * kernel releases. This helper is used to confirm whether the target kernel + * uses a certain function name before attaching. + * + * It is achieved by scaning + * /sys/kernel/debug/tracing/available_filter_functions + * If this file does not exist, it fallbacks to parse /proc/kallsyms, + * which is slower. + */ +bool kprobe_exists(const char *name); +bool tracepoint_exists(const char *category, const char *event); + +bool vmlinux_btf_exists(void); +bool module_btf_exists(const char *mod); + +bool probe_tp_btf(const char *name); +bool probe_ringbuf(); +const struct ksym *ksyms__find_symbol(const struct ksyms *ksyms, + const char *name); + +extern struct ksyms *ksyms; +extern struct syms_cache *syms_cache; +extern struct syms *syms; + +#endif /* __TRACE_HELPERS_H */ diff --git a/eBPF_Supermarket/Stack_Analyser/include/uprobe_helpers.h b/eBPF_Supermarket/Stack_Analyser/include/uprobe.h similarity index 100% rename from eBPF_Supermarket/Stack_Analyser/include/uprobe_helpers.h rename to eBPF_Supermarket/Stack_Analyser/include/uprobe.h diff --git a/eBPF_Supermarket/Stack_Analyser/include/user.h b/eBPF_Supermarket/Stack_Analyser/include/user.h new file mode 100644 index 000000000..b46c53a76 --- /dev/null +++ b/eBPF_Supermarket/Stack_Analyser/include/user.h @@ -0,0 +1,90 @@ +// Copyright 2024 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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: luiyanbing@foxmail.com +// +// 用户态使用的宏 + +#ifndef STACK_ANALYZER_USER +#define STACK_ANALYZER_USER + +#include +#include +#include +#include +#include +#include + +#include "common.h" + +#define _COL_PREFIX "\033[" +#define _BLUE _COL_PREFIX "1;34m" +#define _GREEN _COL_PREFIX "1;32m" +#define _RED _COL_PREFIX "1;35m" +#define _ERED _COL_PREFIX "1;31m" +#define _RE _COL_PREFIX "0m" + +#define PUT_ERR_REASON \ + fprintf(stderr, _ERED " [%s]\n" _RE, strerror(errno)); + +#define HINT_ERR(...) \ + { \ + fprintf(stderr, _ERED __VA_ARGS__); \ + PUT_ERR_REASON; \ + } + +#define DEAL_ERR(action, ...) \ + { \ + HINT_ERR(__VA_ARGS__); \ + action; \ + } + +/// @brief 检查错误,若错误成立则打印带原因的错误信息,并执行action +/// @param action cond成立执行的语句 +/// @param cond 被检查的条件表达式 +/// @param info 要打印的错误信息 +#define CHECK_ERR(action, cond, ...) \ + if (cond) \ + DEAL_ERR(action, __VA_ARGS__) + +/// @brief 检查错误,若错误成立则打印带原因的错误信息并使上层函数返回-1 +/// @param cond 被检查的条件表达式 +/// @param info 要打印的错误信息 +#define CHECK_ERR_RN1(cond, ...) \ + CHECK_ERR(return -1, cond, __VA_ARGS__) + +#define BANNER \ + "\033[38;2;214;165;4m \033[39m\033[38;2;219;159;5m \033[39m\033[38;2;223;152;7m \033[39m\033[38;2;227;146;9m_\033[39m\033[38;2;231;140;12m_\033[39m\033[38;2;234;133;15m_\033[39m\033[38;2;238;127;18m_\033[39m\033[38;2;241;121;21m_\033[39m\033[38;2;243;114;25m \033[39m\033[38;2;246;108;28m_\033[39m\033[38;2;248;102;33m_\033[39m\033[38;2;250;96;37m \033[39m\033[38;2;251;90;41m \033[39m\033[38;2;253;84;46m \033[39m\033[38;2;254;78;51m \033[39m\033[38;2;254;72;56m \033[39m\033[38;2;254;66;62m \033[39m\033[38;2;254;61;67m \033[39m\033[38;2;254;55;73m \033[39m\033[38;2;253;50;79m \033[39m\033[38;2;252;45;85m \033[39m\033[38;2;251;41;91m \033[39m\033[38;2;250;36;97m \033[39m\033[38;2;248;32;103m \033[39m\033[38;2;245;28;109m_\033[39m\033[38;2;243;24;116m_\033[39m\033[38;2;240;20;122m \033[39m\033[38;2;237;17;128m \033[39m\033[38;2;234;14;135m \033[39m\033[38;2;230;11;141m_\033[39m\033[38;2;226;9;147m_\033[39m\033[38;2;222;7;153m_\033[39m\033[38;2;218;5;160m \033[39m\033[38;2;213;4;166m \033[39m\033[38;2;208;2;172m \033[39m\033[38;2;204;1;178m \033[39m\033[38;2;198;1;183m \033[39m\033[38;2;193;1;189m \033[39m\033[38;2;187;1;194m \033[39m\033[38;2;182;1;200m \033[39m\033[38;2;176;2;205m \033[39m\033[38;2;170;3;210m \033[39m\033[38;2;164;4;215m \033[39m\033[38;2;158;6;219m \033[39m\033[38;2;152;7;223m \033[39m\033[38;2;145;10;227m \033[39m\033[38;2;139;12;231m \033[39m\033[38;2;133;15;235m \033[39m\033[38;2;126;18;238m_\033[39m\033[38;2;120;21;241m_\033[39m\033[38;2;114;25;244m \033[39m\033[38;2;107;29;246m \033[39m\033[38;2;101;33;248m \033[39m\033[38;2;95;37;250m \033[39m\033[38;2;89;42;252m \033[39m\033[38;2;83;47;253m \033[39m\033[38;2;77;52;254m \033[39m\033[38;2;71;57;254m \033[39m\033[38;2;66;62;254m \033[39m\033[38;2;60;68;254m \033[39m\033[38;2;55;74;254m \033[39m\033[38;2;50;79;253m \033[39m\033[38;2;45;85;252m \033[39m\033[38;2;40;91;251m \033[39m\033[38;2;36;97;249m \033[39m\033[38;2;31;104;247m \033[39m\033[38;2;27;110;245m \033[39m\033[38;2;24;116;243m \033[39m\033[38;2;20;123;240m \033[39m\033[38;2;17;129;237m \033[39m\033[38;2;14;135;233m \033[39m\033[38;2;11;142;230m\033[39m\n" \ + "\033[38;2;215;164;4m \033[39m\033[38;2;219;158;5m \033[39m\033[38;2;223;152;7m/\033[39m\033[38;2;227;145;10m \033[39m\033[38;2;231;139;12m_\033[39m\033[38;2;235;133;15m_\033[39m\033[38;2;238;127;18m_\033[39m\033[38;2;241;120;21m/\033[39m\033[38;2;244;114;25m/\033[39m\033[38;2;246;108;29m \033[39m\033[38;2;248;101;33m/\033[39m\033[38;2;250;95;37m_\033[39m\033[38;2;252;89;42m_\033[39m\033[38;2;253;83;47m_\033[39m\033[38;2;254;77;52m_\033[39m\033[38;2;254;71;57m_\033[39m\033[38;2;254;66;62m \033[39m\033[38;2;254;60;68m_\033[39m\033[38;2;254;55;73m_\033[39m\033[38;2;253;50;79m_\033[39m\033[38;2;252;45;85m_\033[39m\033[38;2;251;40;91m_\033[39m\033[38;2;249;36;97m_\033[39m\033[38;2;247;31;104m/\033[39m\033[38;2;245;27;110m \033[39m\033[38;2;243;24;116m/\033[39m\033[38;2;240;20;122m_\033[39m\033[38;2;237;17;129m_\033[39m\033[38;2;233;14;135m/\033[39m\033[38;2;230;11;142m \033[39m\033[38;2;226;9;148m \033[39m\033[38;2;222;7;154m \033[39m\033[38;2;217;5;160m|\033[39m\033[38;2;213;3;166m \033[39m\033[38;2;208;2;172m \033[39m\033[38;2;203;1;178m_\033[39m\033[38;2;198;1;184m_\033[39m\033[38;2;192;1;190m_\033[39m\033[38;2;187;1;195m_\033[39m\033[38;2;181;1;200m \033[39m\033[38;2;175;2;205m \033[39m\033[38;2;169;3;210m_\033[39m\033[38;2;163;4;215m_\033[39m\033[38;2;157;6;220m_\033[39m\033[38;2;151;8;224m_\033[39m\033[38;2;145;10;228m \033[39m\033[38;2;138;12;232m_\033[39m\033[38;2;132;15;235m/\033[39m\033[38;2;126;18;238m \033[39m\033[38;2;119;22;241m/\033[39m\033[38;2;113;25;244m_\033[39m\033[38;2;107;29;246m \033[39m\033[38;2;101;33;248m \033[39m\033[38;2;94;38;250m_\033[39m\033[38;2;88;42;252m_\033[39m\033[38;2;82;47;253m_\033[39m\033[38;2;76;52;254m_\033[39m\033[38;2;71;57;254m_\033[39m\033[38;2;65;63;254m_\033[39m\033[38;2;60;68;254m \033[39m\033[38;2;54;74;254m \033[39m\033[38;2;49;80;253m_\033[39m\033[38;2;44;86;252m_\033[39m\033[38;2;40;92;251m_\033[39m\033[38;2;35;98;249m \033[39m\033[38;2;31;104;247m \033[39m\033[38;2;27;111;245m_\033[39m\033[38;2;23;117;242m_\033[39m\033[38;2;20;123;240m_\033[39m\033[38;2;16;130;236m_\033[39m\033[38;2;14;136;233m_\033[39m\033[38;2;11;142;229m\033[39m\n" \ + "\033[38;2;215;163;4m \033[39m\033[38;2;219;157;6m \033[39m\033[38;2;224;151;8m\\\033[39m\033[38;2;228;145;10m_\033[39m\033[38;2;232;139;12m_\033[39m\033[38;2;235;132;15m \033[39m\033[38;2;238;126;18m\\\033[39m\033[38;2;241;120;22m/\033[39m\033[38;2;244;113;25m \033[39m\033[38;2;246;107;29m_\033[39m\033[38;2;248;101;33m_\033[39m\033[38;2;250;95;38m/\033[39m\033[38;2;252;88;42m \033[39m\033[38;2;253;82;47m_\033[39m\033[38;2;254;77;52m_\033[39m\033[38;2;254;71;57m \033[39m\033[38;2;254;65;63m`\033[39m\033[38;2;254;60;68m/\033[39m\033[38;2;254;54;74m \033[39m\033[38;2;253;49;80m_\033[39m\033[38;2;252;44;86m_\033[39m\033[38;2;251;40;92m_\033[39m\033[38;2;249;35;98m/\033[39m\033[38;2;247;31;104m \033[39m\033[38;2;245;27;110m/\033[39m\033[38;2;242;23;117m/\033[39m\033[38;2;240;20;123m_\033[39m\033[38;2;236;17;129m/\033[39m\033[38;2;233;14;136m \033[39m\033[38;2;229;11;142m/\033[39m\033[38;2;226;9;148m|\033[39m\033[38;2;221;6;155m \033[39m\033[38;2;217;5;161m|\033[39m\033[38;2;212;3;167m \033[39m\033[38;2;208;2;173m/\033[39m\033[38;2;202;1;179m \033[39m\033[38;2;197;1;185m_\033[39m\033[38;2;192;1;190m_\033[39m\033[38;2;186;1;196m \033[39m\033[38;2;181;1;201m\\\033[39m\033[38;2;175;2;206m/\033[39m\033[38;2;169;3;211m \033[39m\033[38;2;163;4;216m_\033[39m\033[38;2;157;6;220m_\033[39m\033[38;2;150;8;224m \033[39m\033[38;2;144;10;228m`\033[39m\033[38;2;138;13;232m/\033[39m\033[38;2;132;16;235m \033[39m\033[38;2;125;19;239m/\033[39m\033[38;2;119;22;242m \033[39m\033[38;2;112;26;244m/\033[39m\033[38;2;106;30;247m \033[39m\033[38;2;100;34;249m/\033[39m\033[38;2;94;38;250m \033[39m\033[38;2;88;43;252m/\033[39m\033[38;2;82;48;253m_\033[39m\033[38;2;76;53;254m \033[39m\033[38;2;70;58;254m \033[39m\033[38;2;65;63;254m/\033[39m\033[38;2;59;69;254m \033[39m\033[38;2;54;75;254m/\033[39m\033[38;2;49;81;253m \033[39m\033[38;2;44;86;252m_\033[39m\033[38;2;39;93;251m \033[39m\033[38;2;35;99;249m\\\033[39m\033[38;2;31;105;247m/\033[39m\033[38;2;27;111;245m \033[39m\033[38;2;23;118;242m_\033[39m\033[38;2;19;124;239m_\033[39m\033[38;2;16;130;236m_\033[39m\033[38;2;13;137;233m/\033[39m\033[38;2;11;143;229m\033[39m\n" \ + "\033[38;2;215;163;4m \033[39m\033[38;2;220;157;6m_\033[39m\033[38;2;224;151;8m_\033[39m\033[38;2;228;144;10m_\033[39m\033[38;2;232;138;13m/\033[39m\033[38;2;235;132;16m \033[39m\033[38;2;239;125;19m/\033[39m\033[38;2;242;119;22m \033[39m\033[38;2;244;113;26m/\033[39m\033[38;2;247;106;30m_\033[39m\033[38;2;249;100;34m/\033[39m\033[38;2;250;94;38m \033[39m\033[38;2;252;88;43m/\033[39m\033[38;2;253;82;48m_\033[39m\033[38;2;254;76;53m/\033[39m\033[38;2;254;70;58m \033[39m\033[38;2;254;65;63m/\033[39m\033[38;2;254;59;69m \033[39m\033[38;2;254;54;75m/\033[39m\033[38;2;253;49;80m_\033[39m\033[38;2;252;44;86m_\033[39m\033[38;2;251;39;92m/\033[39m\033[38;2;249;35;99m \033[39m\033[38;2;247;31;105m,\033[39m\033[38;2;245;27;111m<\033[39m\033[38;2;242;23;117m \033[39m\033[38;2;239;19;124m/\033[39m\033[38;2;236;16;130m \033[39m\033[38;2;233;13;136m_\033[39m\033[38;2;229;11;143m_\033[39m\033[38;2;225;8;149m_\033[39m\033[38;2;221;6;155m \033[39m\033[38;2;217;5;161m|\033[39m\033[38;2;212;3;168m/\033[39m\033[38;2;207;2;173m \033[39m\033[38;2;202;1;179m/\033[39m\033[38;2;197;1;185m \033[39m\033[38;2;191;1;191m/\033[39m\033[38;2;186;1;196m \033[39m\033[38;2;180;1;201m/\033[39m\033[38;2;174;2;206m \033[39m\033[38;2;168;3;211m/\033[39m\033[38;2;162;4;216m_\033[39m\033[38;2;156;6;220m/\033[39m\033[38;2;150;8;225m \033[39m\033[38;2;144;10;229m/\033[39m\033[38;2;137;13;232m \033[39m\033[38;2;131;16;236m/\033[39m\033[38;2;125;19;239m \033[39m\033[38;2;118;22;242m/\033[39m\033[38;2;112;26;244m_\033[39m\033[38;2;106;30;247m/\033[39m\033[38;2;99;34;249m \033[39m\033[38;2;93;39;251m/\033[39m\033[38;2;87;43;252m \033[39m\033[38;2;81;48;253m/\033[39m\033[38;2;75;53;254m \033[39m\033[38;2;70;59;254m/\033[39m\033[38;2;64;64;254m_\033[39m\033[38;2;59;70;254m/\033[39m\033[38;2;53;75;254m \033[39m\033[38;2;48;81;253m \033[39m\033[38;2;43;87;252m_\033[39m\033[38;2;39;93;251m_\033[39m\033[38;2;34;99;249m/\033[39m\033[38;2;30;106;247m \033[39m\033[38;2;26;112;244m/\033[39m\033[38;2;22;118;242m \033[39m\033[38;2;19;124;239m \033[39m\033[38;2;16;131;236m \033[39m\033[38;2;13;137;232m \033[39m\033[38;2;10;143;229m\033[39m\n" \ + "\033[38;2;216;162;4m/\033[39m\033[38;2;220;156;6m_\033[39m\033[38;2;225;150;8m_\033[39m\033[38;2;229;144;10m_\033[39m\033[38;2;232;137;13m_\033[39m\033[38;2;236;131;16m/\033[39m\033[38;2;239;125;19m\\\033[39m\033[38;2;242;118;22m_\033[39m\033[38;2;244;112;26m_\033[39m\033[38;2;247;106;30m/\033[39m\033[38;2;249;99;34m\\\033[39m\033[38;2;251;93;39m_\033[39m\033[38;2;252;87;43m_\033[39m\033[38;2;253;81;48m,\033[39m\033[38;2;254;75;53m_\033[39m\033[38;2;254;70;58m/\033[39m\033[38;2;254;64;64m\\\033[39m\033[38;2;254;59;69m_\033[39m\033[38;2;254;53;75m_\033[39m\033[38;2;253;48;81m_\033[39m\033[38;2;252;43;87m/\033[39m\033[38;2;251;39;93m_\033[39m\033[38;2;249;34;99m/\033[39m\033[38;2;247;30;105m|\033[39m\033[38;2;245;26;112m_\033[39m\033[38;2;242;23;118m/\033[39m\033[38;2;239;19;124m_\033[39m\033[38;2;236;16;131m/\033[39m\033[38;2;232;13;137m \033[39m\033[38;2;229;10;143m \033[39m\033[38;2;225;8;150m|\033[39m\033[38;2;220;6;156m_\033[39m\033[38;2;216;4;162m/\033[39m\033[38;2;211;3;168m_\033[39m\033[38;2;207;2;174m/\033[39m\033[38;2;201;1;180m \033[39m\033[38;2;196;1;186m/\033[39m\033[38;2;191;1;191m_\033[39m\033[38;2;185;1;197m/\033[39m\033[38;2;179;1;202m\\\033[39m\033[38;2;174;2;207m_\033[39m\033[38;2;168;3;212m_\033[39m\033[38;2;162;5;216m,\033[39m\033[38;2;155;6;221m_\033[39m\033[38;2;149;8;225m/\033[39m\033[38;2;143;11;229m_\033[39m\033[38;2;137;13;233m/\033[39m\033[38;2;130;16;236m\\\033[39m\033[38;2;124;19;239m_\033[39m\033[38;2;118;23;242m_\033[39m\033[38;2;111;27;245m,\033[39m\033[38;2;105;31;247m \033[39m\033[38;2;99;35;249m/\033[39m\033[38;2;93;39;251m \033[39m\033[38;2;87;44;252m/\033[39m\033[38;2;81;49;253m_\033[39m\033[38;2;75;54;254m_\033[39m\033[38;2;69;59;254m_\033[39m\033[38;2;63;65;254m/\033[39m\033[38;2;58;70;254m\\\033[39m\033[38;2;53;76;254m_\033[39m\033[38;2;48;82;253m_\033[39m\033[38;2;43;88;252m_\033[39m\033[38;2;38;94;250m/\033[39m\033[38;2;34;100;249m_\033[39m\033[38;2;30;106;247m/\033[39m\033[38;2;26;112;244m \033[39m\033[38;2;22;119;242m \033[39m\033[38;2;19;125;239m \033[39m\033[38;2;16;131;235m \033[39m\033[38;2;13;138;232m \033[39m\033[38;2;10;144;228m\033[39m\n" \ + "\033[38;2;216;162;4m \033[39m\033[38;2;221;155;6m \033[39m\033[38;2;225;149;8m \033[39m\033[38;2;229;143;11m \033[39m\033[38;2;233;137;13m \033[39m\033[38;2;236;130;16m \033[39m\033[38;2;239;124;19m \033[39m\033[38;2;242;118;23m \033[39m\033[38;2;245;111;27m \033[39m\033[38;2;247;105;30m \033[39m\033[38;2;249;99;35m \033[39m\033[38;2;251;93;39m \033[39m\033[38;2;252;87;44m \033[39m\033[38;2;253;81;49m \033[39m\033[38;2;254;75;54m \033[39m\033[38;2;254;69;59m \033[39m\033[38;2;254;64;64m \033[39m\033[38;2;254;58;70m \033[39m\033[38;2;254;53;76m \033[39m\033[38;2;253;48;82m \033[39m\033[38;2;252;43;88m \033[39m\033[38;2;250;38;94m \033[39m\033[38;2;249;34;100m \033[39m\033[38;2;247;30;106m \033[39m\033[38;2;244;26;112m \033[39m\033[38;2;242;22;119m \033[39m\033[38;2;239;19;125m \033[39m\033[38;2;235;16;131m \033[39m\033[38;2;232;13;138m \033[39m\033[38;2;228;10;144m \033[39m\033[38;2;224;8;150m \033[39m\033[38;2;220;6;157m \033[39m\033[38;2;216;4;163m \033[39m\033[38;2;211;3;169m \033[39m\033[38;2;206;2;175m \033[39m\033[38;2;201;1;181m \033[39m\033[38;2;196;1;186m \033[39m\033[38;2;190;1;192m \033[39m\033[38;2;185;1;197m \033[39m\033[38;2;179;1;202m \033[39m\033[38;2;173;2;207m \033[39m\033[38;2;167;3;212m \033[39m\033[38;2;161;5;217m \033[39m\033[38;2;155;6;221m \033[39m\033[38;2;149;9;225m \033[39m\033[38;2;142;11;229m \033[39m\033[38;2;136;14;233m/\033[39m\033[38;2;130;16;236m_\033[39m\033[38;2;123;20;240m_\033[39m\033[38;2;117;23;242m_\033[39m\033[38;2;111;27;245m_\033[39m\033[38;2;104;31;247m/\033[39m\033[38;2;98;35;249m \033[39m\033[38;2;92;40;251m \033[39m\033[38;2;86;44;252m \033[39m\033[38;2;80;49;253m \033[39m\033[38;2;74;54;254m \033[39m\033[38;2;68;60;254m \033[39m\033[38;2;63;65;254m \033[39m\033[38;2;58;71;254m \033[39m\033[38;2;52;76;254m \033[39m\033[38;2;47;82;253m \033[39m\033[38;2;42;88;252m \033[39m\033[38;2;38;94;250m \033[39m\033[38;2;33;101;248m \033[39m\033[38;2;29;107;246m \033[39m\033[38;2;25;113;244m \033[39m\033[38;2;22;119;241m \033[39m\033[38;2;18;126;238m \033[39m\033[38;2;15;132;235m \033[39m\033[38;2;12;138;232m \033[39m\033[38;2;10;145;228m\033[39m" + +#define COLLECTOR_INFO(_name) _BLUE "\b\b\b\bCollector for " _name " trace" _RE + +inline void clearSpace(char *sym) +{ + for (char *p = sym; *p; p++) + { + if (*p != ' ') + { + *sym = *p; + sym++; + } + } + *sym = '\0'; +} + +#endif diff --git a/eBPF_Supermarket/Stack_Analyser/libbpf-bootstrap b/eBPF_Supermarket/Stack_Analyser/libbpf-bootstrap deleted file mode 160000 index b0c8234df..000000000 --- a/eBPF_Supermarket/Stack_Analyser/libbpf-bootstrap +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b0c8234dfb8f31eb12c99b26bb2bec96eb76aff3 diff --git a/eBPF_Supermarket/Stack_Analyser/new_bpf.sh b/eBPF_Supermarket/Stack_Analyser/new_bpf.sh index 8ed45cdb9..8aa1507a2 100755 --- a/eBPF_Supermarket/Stack_Analyser/new_bpf.sh +++ b/eBPF_Supermarket/Stack_Analyser/new_bpf.sh @@ -27,6 +27,6 @@ sed -i 's/template/'$origin_name'/g' bpf/$origin_name.bpf.c sed -i '/#include "bpf_wapper\/on_cpu.h"/a#include "bpf_wapper\/'$origin_name'.h"' src/main.cpp -sed -i '/auto MainOption = _GREEN "Some overall options" _RE %/iauto '$name'Option = clipp::option("'$origin_name'").call([]{ StackCollectorList.push_back(new '$class_name'()); }) % COLLECTOR_INFO("'$origin_name'") & (TraceOption);' src/main.cpp +sed -i '/auto MainOption = _GREEN "Some overall options" _RE %/iauto '$name'Option = clipp::option("'$origin_name'").call([]{ StackCollectorList.push_back(new '$class_name'()); }) % COLLECTOR_INFO("'$origin_name'");' src/main.cpp sed -i '/MainOption,/i'$name'Option,' src/main.cpp \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/eBPFStackCollector.cpp b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/eBPFStackCollector.cpp index f69a509f1..2f99dcc35 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/eBPFStackCollector.cpp +++ b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/eBPFStackCollector.cpp @@ -17,13 +17,15 @@ // 包装用于采集调用栈数据的eBPF程序,规定一些抽象接口和通用变量 #include "bpf_wapper/eBPFStackCollector.h" -#include "sa_user.h" -#include "dt_symbol.h" +#include "user.h" +#include "trace.h" #include #include #include #include +#include +#include std::string getLocalDateTime(void) { @@ -44,7 +46,7 @@ bool operator<(const CountItem a, const CountItem b) StackCollector::StackCollector() { - self_pid = getpid(); + self_tgid = getpid(); }; std::vector *StackCollector::sortedCountList(void) @@ -53,25 +55,44 @@ std::vector *StackCollector::sortedCountList(void) auto val_size = bpf_map__value_size(psid_count_map); auto value_fd = bpf_object__find_map_fd_by_name(obj, "psid_count_map"); + auto D = new std::vector(); +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 5, 0) + for (psid prev_key = {0}, curr_key = {0};; prev_key = curr_key) + { + if (bpf_map_get_next_key(value_fd, &prev_key, &curr_key)) + { + if (errno != ENOENT) + perror("map get next key error"); + break; // no more keys, done + } + if (showDelta) + bpf_map_delete_elem(value_fd, &prev_key); + char val[val_size]; + memset(val, 0, val_size); + if (bpf_map_lookup_elem(value_fd, &curr_key, &val)) + { + if (errno != ENOENT) + { + perror("map lookup error"); + break; + } + continue; + } + CountItem d(curr_key, count_values(val)); + D->insert(std::lower_bound(D->begin(), D->end(), d), d); + } +#else auto keys = new psid[MAX_ENTRIES]; auto vals = new char[MAX_ENTRIES * val_size]; uint32_t count = MAX_ENTRIES; psid next_key; int err; if (showDelta) - { err = bpf_map_lookup_and_delete_batch(value_fd, NULL, &next_key, keys, vals, &count, NULL); - } else - { err = bpf_map_lookup_batch(value_fd, NULL, &next_key, keys, vals, &count, NULL); - } if (err == EFAULT) - { return NULL; - } - - auto D = new std::vector(); for (uint32_t i = 0; i < count; i++) { CountItem d(keys[i], count_values(vals + val_size * i)); @@ -79,6 +100,7 @@ std::vector *StackCollector::sortedCountList(void) } delete[] keys; delete[] vals; +#endif return D; }; @@ -87,12 +109,21 @@ StackCollector::operator std::string() std::ostringstream oss; oss << _RED "time:" << getLocalDateTime() << _RE "\n"; std::map> traces; + std::map infos; oss << _BLUE "counts:" _RE "\n"; { auto D = sortedCountList(); if (!D) return oss.str(); + if ((*D).size() > top) + { + auto end = (*D).end(); + auto begin = end - top; + for (auto i = (*D).begin(); i < begin; i++) + delete i->v; + (*D).assign(begin, end); + } oss << _GREEN "pid\tusid\tksid"; for (int i = 0; i < scale_num; i++) oss << '\t' << scales[i].Type << "/" << scales[i].Period << scales[i].Unit; @@ -112,67 +143,55 @@ StackCollector::operator std::string() auto trace_fd = bpf_object__find_map_fd_by_name(obj, "sid_trace_map"); if (id.usid > 0 && traces.find(id.usid) == traces.end()) { - std::vector sym_trace; - bpf_map_lookup_elem(trace_fd, &id.usid, trace); - for (p = trace + MAX_STACKS - 1; !*p; p--) - ; - for (; p >= trace; p--) + syms = syms_cache__get_syms(syms_cache, id.pid); + if (!syms) + fprintf(stderr, "failed to get syms\n"); + else { - uint64_t &addr = *p; - symbol sym; - sym.reset(addr); - elf_file file; - if (g_symbol_parser.find_symbol_in_cache(id.pid, addr, sym.name)) + bpf_map_lookup_elem(trace_fd, &id.usid, trace); + for (p = trace + MAX_STACKS - 1; !*p; p--) ; - else if (g_symbol_parser.get_symbol_info(id.pid, sym, file) && g_symbol_parser.find_elf_symbol(sym, file, id.pid, id.pid)) + std::vector sym_trace(p - trace + 1); + for (int i = 0; p >= trace; p--) { - if (sym.name[0] == '_' && sym.name[1] == 'Z') - // 代表是C++符号,则调用demangle解析 + struct sym *sym = syms__map_addr(syms, *p); + if (sym) { - sym.name = demangleCppSym(sym.name); + if (sym->name[0] == '_' && sym->name[1] == 'Z') + { + char *demangled = abi::__cxa_demangle(sym->name, NULL, NULL, NULL); + if (demangled) + { + clearSpace(demangled); + sym->name = demangled; + } + } + sym_trace[i++] = std::string(sym->name) + "+" + std::to_string(sym->offset); } - std::stringstream ss(""); - ss << "+0x" << std::hex << (addr - sym.start); - sym.name += ss.str(); - g_symbol_parser.putin_symbol_cache(id.pid, addr, sym.name); - } - else - { - std::stringstream ss(""); - ss << "0x" << std::hex << addr; - sym.name = ss.str(); - g_symbol_parser.putin_symbol_cache(id.pid, addr, sym.name); + else + sym_trace[i++] = "[unknown]"; } - clearSpace(sym.name); - sym_trace.push_back(sym.name); + traces[id.usid] = sym_trace; } - traces[id.usid] = sym_trace; } if (id.ksid > 0 && traces.find(id.ksid) == traces.end()) { - std::vector sym_trace; bpf_map_lookup_elem(trace_fd, &id.ksid, trace); for (p = trace + MAX_STACKS - 1; !*p; p--) ; - for (; p >= trace; p--) + std::vector sym_trace(p - trace + 1); + for (int i = 0; p >= trace; p--) { - uint64_t &addr = *p; - symbol sym; - sym.reset(addr); - if (g_symbol_parser.find_kernel_symbol(sym)) - ; - else - { - std::stringstream ss(""); - ss << "0x" << std::hex << addr; - sym.name = ss.str(); - g_symbol_parser.putin_symbol_cache(pid, addr, sym.name); - } - clearSpace(sym.name); - sym_trace.push_back(sym.name); + const struct ksym *ksym = ksyms__map_addr(ksyms, *p); + sym_trace[i++] = ksym ? std::string(ksym->name) + "+" + std::to_string(*p - ksym->addr) + : "[unknown]"; } traces[id.ksid] = sym_trace; } + auto info_fd = bpf_object__find_map_fd_by_name(obj, "pid_info_map"); + task_info info; + bpf_map_lookup_elem(info_fd, &id.pid, &info); + infos[id.pid] = info; } delete D; } @@ -191,37 +210,20 @@ StackCollector::operator std::string() oss << _BLUE "info:" _RE "\n"; { - auto info_fd = bpf_object__find_map_fd_by_name(obj, "pid_info_map"); - if (info_fd < 0) + oss << _GREEN "pid\tNSpid\tcomm\ttgid\tcgroup\t" _RE "\n"; + for (auto i : infos) { - return oss.str(); + auto cgroup_fd = bpf_object__find_map_fd_by_name(obj, "tgid_cgroup_map"); + char group[CONTAINER_ID_LEN]; + bpf_map_lookup_elem(cgroup_fd, &(i.second.tgid), &group); + oss << i.first << '\t' + << i.second.pid << '\t' + << i.second.comm << '\t' + << i.second.tgid << '\t' + << group << '\n'; } - auto keys = new uint32_t[MAX_ENTRIES]; - auto vals = new task_info[MAX_ENTRIES]; - uint32_t count = MAX_ENTRIES; - uint32_t next_key; - { - int err; - if (showDelta) - err = bpf_map_lookup_and_delete_batch(info_fd, NULL, &next_key, - keys, vals, &count, NULL); - else - err = bpf_map_lookup_batch(info_fd, NULL, &next_key, - keys, vals, &count, NULL); - if (err == EFAULT) - return oss.str(); - } - oss << _GREEN "pid\tNSpid\tcomm\ttgid\tcgroup" _RE "\n"; - for (uint32_t i = 0; i < count; i++) - oss << keys[i] << '\t' - << vals[i].pid << '\t' - << vals[i].comm << '\t' - << vals[i].tgid << '\t' - << vals[i].cid << '\n'; - delete[] keys; - delete[] vals; } oss << _BLUE "OK" _RE "\n"; return oss.str(); -} \ No newline at end of file +} diff --git a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/io.cpp b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/io.cpp index e12e8f474..910daaca6 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/io.cpp +++ b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/io.cpp @@ -36,25 +36,16 @@ IOStackCollector::IOStackCollector() }; }; -int IOStackCollector::load(void) -{ - EBPF_LOAD_OPEN_INIT(skel->rodata->target_pid = pid;); - return 0; -} - -int IOStackCollector::attach(void) +int IOStackCollector::ready(void) { + EBPF_LOAD_OPEN_INIT(); ATTACH_PROTO; return 0; } -void IOStackCollector::detach(void) +void IOStackCollector::finish(void) { DETACH_PROTO; -} - -void IOStackCollector::unload(void) -{ UNLOAD_PROTO; } @@ -63,6 +54,7 @@ void IOStackCollector::activate(bool tf) ACTIVE_SET(tf); } -const char *IOStackCollector::getName(void) { +const char *IOStackCollector::getName(void) +{ return "IOStackCollector"; } \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/llc_stat.cpp b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/llc_stat.cpp index a6bdc3986..575b14450 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/llc_stat.cpp +++ b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/llc_stat.cpp @@ -37,25 +37,19 @@ uint64_t *LlcStatStackCollector::count_values(void *data) }; }; -int LlcStatStackCollector::load(void) +int LlcStatStackCollector::ready(void) { EBPF_LOAD_OPEN_INIT(); - return 0; -}; - -int LlcStatStackCollector::attach(void) -{ - const char *online_cpus_file = "/sys/devices/system/cpu/online"; bool *online_mask; int num_online_cpus; - err = parse_cpu_mask_file(online_cpus_file, &online_mask, &num_online_cpus); - CHECK_ERR(err, "Fail to get online CPU numbers"); + err = parse_cpu_mask_file("/sys/devices/system/cpu/online", &online_mask, &num_online_cpus); + CHECK_ERR_RN1(err, "Fail to get online CPU numbers"); num_cpus = libbpf_num_possible_cpus(); - CHECK_ERR(num_cpus <= 0, "Fail to get the number of processors"); + CHECK_ERR_RN1(num_cpus <= 0, "Fail to get the number of processors"); struct perf_event_attr attr = { - .type = PERF_COUNT_HW_CPU_CYCLES, + .type = PERF_TYPE_HARDWARE, .size = sizeof(attr), .sample_period = scales->Period, .inherit = 1, @@ -78,25 +72,25 @@ int LlcStatStackCollector::attach(void) } /* Set up performance monitoring on a CPU/Core */ attr.config = PERF_COUNT_HW_CACHE_MISSES; - int pefd = syscall(SYS_perf_event_open, &attr, pid, cpu, -1, 0); - CHECK_ERR(pefd < 0, "Fail to set up performance monitor on a CPU/Core"); + int pefd = syscall(SYS_perf_event_open, &attr, tgid ? tgid : -1, cpu, -1, 0); + CHECK_ERR_RN1(pefd < 0, "Fail to set up performance monitor on a CPU/Core"); mpefds[cpu] = pefd; /* Attach a BPF program on a CPU */ mlinks[cpu] = bpf_program__attach_perf_event(skel->progs.on_cache_miss, pefd); - CHECK_ERR(!mlinks[cpu], "Fail to attach bpf program"); + CHECK_ERR_RN1(!mlinks[cpu], "Fail to attach bpf program"); attr.config = PERF_COUNT_HW_CACHE_REFERENCES; - pefd = syscall(SYS_perf_event_open, &attr, pid, cpu, -1, 0); - CHECK_ERR(pefd < 0, "Fail to set up performance monitor on a CPU/Core"); + pefd = syscall(SYS_perf_event_open, &attr, tgid ? tgid : -1, cpu, -1, 0); + CHECK_ERR_RN1(pefd < 0, "Fail to set up performance monitor on a CPU/Core"); rpefds[cpu] = pefd; /* Attach a BPF program on a CPU */ rlinks[cpu] = bpf_program__attach_perf_event(skel->progs.on_cache_ref, pefd); - CHECK_ERR(!rlinks[cpu], "Fail to attach bpf program"); + CHECK_ERR_RN1(!rlinks[cpu], "Fail to attach bpf program"); } - return 0; -}; + return 0; +} -void LlcStatStackCollector::detach(void) +void LlcStatStackCollector::finish(void) { for (int cpu = 0; cpu < num_cpus; cpu++) { @@ -111,12 +105,8 @@ void LlcStatStackCollector::detach(void) free(rpefds); mlinks = rlinks = NULL; mpefds = rpefds = NULL; -}; - -void LlcStatStackCollector::unload(void) -{ UNLOAD_PROTO; -}; +} void LlcStatStackCollector::activate(bool tf) { @@ -141,6 +131,7 @@ LlcStatStackCollector::LlcStatStackCollector() }; }; -void LlcStatStackCollector::setScale(uint64_t p) { +void LlcStatStackCollector::setScale(uint64_t p) +{ scales[0].Period = scales[1].Period = p; } \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/memleak.cpp b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/memleak.cpp index e0edb6cda..d8224de6d 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/memleak.cpp +++ b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/memleak.cpp @@ -17,7 +17,7 @@ // mem ebpf程序的包装类,实现接口和一些自定义方法 #include "bpf_wapper/memleak.h" -#include "trace_helpers.h" +#include "trace.h" #include uint64_t *MemleakStackCollector::count_values(void *d) @@ -111,7 +111,7 @@ int MemleakStackCollector::attach_uprobes(struct memleak_bpf *skel) return 0; } -int MemleakStackCollector::load(void) +int MemleakStackCollector::ready(void) { EBPF_LOAD_OPEN_INIT( if (kstack) { @@ -119,39 +119,29 @@ int MemleakStackCollector::load(void) disable_kernel_node_tracepoints(skel); if (!percpu) disable_kernel_percpu_tracepoints(skel); - } else { - disable_kernel_tracepoints(skel); - }; - skel->rodata->sample_rate = sample_rate; + } else disable_kernel_tracepoints(skel); skel->rodata->wa_missing_free = wa_missing_free; skel->rodata->page_size = sysconf(_SC_PAGE_SIZE);); - return 0; -}; - -int MemleakStackCollector::attach(void) -{ if (!kstack) - CHECK_ERR(attach_uprobes(skel), "failed to attach uprobes"); + CHECK_ERR_RN1(attach_uprobes(skel), "failed to attach uprobes"); err = skel->attach(skel); - CHECK_ERR(err, "Failed to attach BPF skeleton"); + CHECK_ERR_RN1(err, "Failed to attach BPF skeleton"); return 0; -}; +} -void MemleakStackCollector::detach(void) +void MemleakStackCollector::finish(void) { DETACH_PROTO; -}; - -void MemleakStackCollector::unload(void) -{ UNLOAD_PROTO; } + void MemleakStackCollector::activate(bool tf) { ACTIVE_SET(tf); } -const char *MemleakStackCollector::getName(void) { +const char *MemleakStackCollector::getName(void) +{ return "MemleakStackCollector"; -} \ No newline at end of file +} diff --git a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/off_cpu.cpp b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/off_cpu.cpp index 068375f34..d1cd1d9ba 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/off_cpu.cpp +++ b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/off_cpu.cpp @@ -17,7 +17,7 @@ // off cpu ebpf程序的包装类,实现接口和一些自定义方法 #include "bpf_wapper/off_cpu.h" -#include "dt_symbol.h" +#include "trace.h" OffCPUStackCollector::OffCPUStackCollector() { @@ -34,31 +34,20 @@ uint64_t *OffCPUStackCollector::count_values(void *data) }; }; -int OffCPUStackCollector::load(void) +int OffCPUStackCollector::ready(void) { - EBPF_LOAD_OPEN_INIT(skel->rodata->target_pid = pid;); - return 0; -} - -int OffCPUStackCollector::attach(void) -{ - symbol sym; - sym.name = "finish_task_switch"; - if (!g_symbol_parser.complete_kernel_symbol(sym)) - { + EBPF_LOAD_OPEN_INIT(); + const char *name = "finish_task_switch"; + const struct ksym *ksym = ksyms__find_symbol(ksyms, name); + if (!ksym) return -1; - } - skel->links.do_stack = bpf_program__attach_kprobe(skel->progs.do_stack, false, sym.name.c_str()); + skel->links.do_stack = bpf_program__attach_kprobe(skel->progs.do_stack, false, ksym->name); return 0; } -void OffCPUStackCollector::detach(void) +void OffCPUStackCollector::finish(void) { DETACH_PROTO; -} - -void OffCPUStackCollector::unload(void) -{ UNLOAD_PROTO; } @@ -67,6 +56,7 @@ void OffCPUStackCollector::activate(bool tf) ACTIVE_SET(tf); } -const char *OffCPUStackCollector::getName(void) { +const char *OffCPUStackCollector::getName(void) +{ return "OffCPUStackCollector"; } \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/on_cpu.cpp b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/on_cpu.cpp index 8e4e1cf4a..3bf90f748 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/on_cpu.cpp +++ b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/on_cpu.cpp @@ -59,26 +59,19 @@ uint64_t *OnCPUStackCollector::count_values(void *data) }; }; -int OnCPUStackCollector::load(void) +int OnCPUStackCollector::ready(void) { EBPF_LOAD_OPEN_INIT(); - - return 0; -}; - -int OnCPUStackCollector::attach(void) -{ - const char *online_cpus_file = "/sys/devices/system/cpu/online"; bool *online_mask; int num_online_cpus; - err = parse_cpu_mask_file(online_cpus_file, &online_mask, &num_online_cpus); - CHECK_ERR(err, "Fail to get online CPU numbers"); + err = parse_cpu_mask_file("/sys/devices/system/cpu/online", &online_mask, &num_online_cpus); + CHECK_ERR_RN1(err, "Fail to get online CPU numbers"); num_cpus = libbpf_num_possible_cpus(); - CHECK_ERR(num_cpus <= 0, "Fail to get the number of processors"); + CHECK_ERR_RN1(num_cpus <= 0, "Fail to get the number of processors"); struct perf_event_attr attr = { - .type = PERF_COUNT_HW_CPU_CYCLES, + .type = PERF_TYPE_HARDWARE, .size = sizeof(attr), .config = PERF_COUNT_HW_CPU_CYCLES, .sample_freq = freq, @@ -99,51 +92,48 @@ int OnCPUStackCollector::attach(void) continue; } /* Set up performance monitoring on a CPU/Core */ - int pefd = perf_event_open(&attr, pid, cpu, -1, 0); - CHECK_ERR(pefd < 0, "Fail to set up performance monitor on a CPU/Core"); + int pefd = perf_event_open(&attr, tgid ? tgid : -1, cpu, -1, 0); + if (pefd < 0) + { + if (attr.type != PERF_TYPE_SOFTWARE) + { + HINT_ERR("Hardware perf events not exist, try to attach to software event"); + attr.type = PERF_TYPE_SOFTWARE; + attr.config = PERF_COUNT_SW_CPU_CLOCK; + pefd = perf_event_open(&attr, tgid ? tgid : -1, cpu, -1, 0); + CHECK_ERR_RN1(pefd < 0, "Fail to set up performance monitor on a CPU/Core"); + } + else + DEAL_ERR(return -1, "Fail to set up performance monitor on a CPU/Core"); + } pefds[cpu] = pefd; /* Attach a BPF program on a CPU */ links[cpu] = bpf_program__attach_perf_event(skel->progs.do_stack, pefd); // 与内核bpf程序联系 - CHECK_ERR(!links[cpu], "Fail to attach bpf program"); + CHECK_ERR_RN1(!links[cpu], "Fail to attach bpf program"); } return 0; } -void OnCPUStackCollector::detach(void) +void OnCPUStackCollector::finish(void) { - if (links) - { - for (int cpu = 0; cpu < num_cpus; cpu++) - { - bpf_link__destroy(links[cpu]); - } - free(links); - links = NULL; - } - if (pefds) + for (int i = 0; i < num_cpus; i++) { - for (int i = 0; i < num_cpus; i++) - { - if (pefds[i] >= 0) - { - close(pefds[i]); - } - } - free(pefds); - pefds = NULL; + bpf_link__destroy(links[i]); + close(pefds[i]); } -}; - -void OnCPUStackCollector::unload(void) -{ + free(links); + free(pefds); + links = NULL; + pefds = NULL; UNLOAD_PROTO; -}; +} void OnCPUStackCollector::activate(bool tf) { ACTIVE_SET(tf); } -const char *OnCPUStackCollector::getName(void) { +const char *OnCPUStackCollector::getName(void) +{ return "OnCPUStackCollector"; } \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/probe.cpp b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/probe.cpp index 3c82035a2..6b720c6b8 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/probe.cpp +++ b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/probe.cpp @@ -12,29 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. // -// author: GaoYixiang +// author: luiyanbing@foxmail.com // -// probe ebpf 程序的包装类,实现接口和一些自定义方法 +// ebpf程序包装类的模板,实现接口和一些自定义方法 #include "bpf_wapper/probe.h" -#include "uprobe_helpers.h" +#include "trace.h" +#include "uprobe.h" -uint64_t *ProbeStackCollector::count_values(void *data) -{ - return new uint64_t[scale_num]{ - *(uint32_t *)data, - }; -} - -ProbeStackCollector::ProbeStackCollector() -{ - scale_num = 1; - scales = new Scale[scale_num]{ - {"", 1, "counts"}, - }; -}; - -void splitString(std::string symbol, const char split, std::vector &res) +void splitStr(const std::string &symbol, const char split, std::vector &res) { if (symbol == "") return; @@ -49,7 +35,25 @@ void splitString(std::string symbol, const char split, std::vector } } -static int get_path(char *path,int pid) +static bool try_fentry(struct probe_bpf *skel, const char *func) +{ + if (!fentry_can_attach(func, NULL) || + bpf_program__set_attach_target(skel->progs.dummy_fentry, 0, func) || + bpf_program__set_attach_target(skel->progs.dummy_fexit, 0, func)) + { + bpf_program__set_autoload(skel->progs.dummy_fentry, false); + bpf_program__set_autoload(skel->progs.dummy_fexit, false); + return false; + } + else + { + bpf_program__set_autoload(skel->progs.dummy_kprobe, false); + bpf_program__set_autoload(skel->progs.dummy_kretprobe, false); + return true; + } +}; + +static int get_binpath(char *path, int pid) { char mode[16], line[128], buf[64]; size_t seg_start, seg_end, seg_off; @@ -62,11 +66,13 @@ static int get_path(char *path,int pid) return -1; while (fscanf(f, "%zx-%zx %s %zx %*s %*d%[^\n]\n", - &seg_start, &seg_end, mode, &seg_off, line) == 5) { + &seg_start, &seg_end, mode, &seg_off, line) == 5) + { i = 0; while (isblank(line[i])) i++; - if (strstr(line + i, "libc.so.6")) { + if (strstr(line + i, "libc.so.6")) + { break; } } @@ -74,105 +80,162 @@ static int get_path(char *path,int pid) strcpy(path, line + i); fclose(f); return 0; -} -void ProbeStackCollector::setScale(std::string probe) +}; + +static int attach_kprobes(struct probe_bpf *skel, const std::string &func) { - this->probe = probe; - scales->Type = probe + "Counts"; + skel->links.dummy_kprobe = + bpf_program__attach_kprobe(skel->progs.dummy_kprobe, false, func.c_str()); + CHECK_ERR_RN1(!skel->links.dummy_kprobe, "Fail to attach kprobe"); + skel->links.dummy_kretprobe = + bpf_program__attach_kprobe(skel->progs.dummy_kretprobe, true, func.c_str()); + CHECK_ERR_RN1(!skel->links.dummy_kretprobe, "Fail to attach ketprobe"); + return 0; }; -int ProbeStackCollector::load(void) +static int attach_fentry(struct probe_bpf *skel) { - EBPF_LOAD_OPEN_INIT(skel->rodata->target_pid = pid;); + skel->links.dummy_fentry = + bpf_program__attach(skel->progs.dummy_fentry); + CHECK_ERR_RN1(!skel->links.dummy_fentry, "Fail to attach fentry"); + skel->links.dummy_fexit = + bpf_program__attach(skel->progs.dummy_fexit); + CHECK_ERR_RN1(!skel->links.dummy_fexit, "Fail to attach fexit"); return 0; }; -int ProbeStackCollector::attach(void) +static int attach_uprobes(struct probe_bpf *skel, const std::string &probe, int pid) { - std::vector strList; - splitString(probe, ':', strList); + char *binary, *function; + char bin_path[128]; + std::string func = probe; + off_t func_off; + + binary = strdup(func.c_str()); + function = strchr(binary, ':'); // 查找:首次出现的位置 + *function = '\0'; + function++; + + if (resolve_binary_path(binary, pid, bin_path, sizeof(bin_path))) + free(binary); + + func_off = get_elf_func_offset(bin_path, function); + if (func_off < 0) + free(binary); + skel->links.dummy_kprobe = + bpf_program__attach_uprobe(skel->progs.dummy_kprobe, false, pid, + bin_path, func_off); + CHECK_ERR_RN1(!skel->links.dummy_kprobe, "Fail to attach uprobe"); + skel->links.dummy_kretprobe = + bpf_program__attach_uprobe(skel->progs.dummy_kretprobe, true, pid, + bin_path, func_off); + CHECK_ERR_RN1(!skel->links.dummy_kretprobe, "Fail to attach uprobe"); + return 0; +}; - if (strList.size() == 1 || (strList.size() == 3 && strList[0] == "p" && strList[1] == "")) - { - // probe a kernel function - std::string func = probe; - if (strList.size() == 3 && strList[0] == "p" && strList[1] == "") - func = strList[2]; - skel->links.handle = - bpf_program__attach_kprobe(skel->progs.handle, false, func.c_str()); - CHECK_ERR(!skel->links.handle, "Fail to attach kprobe"); - return 0; - } - else if (strList.size() == 3 && strList[0] == "t") - { - // probe a kernel tracepoint - skel->links.handle_tp = - bpf_program__attach_tracepoint(skel->progs.handle_tp, strList[1].c_str(), strList[2].c_str()); - CHECK_ERR(!skel->links.handle_tp, "Fail to attach tracepoint"); - return 0; - } - else if (strList.size() == 2 || (strList.size() == 3 && strList[0] == "p" && strList[1] != "")) - { - // probe a user-space function in the library 'lib' - char *binary, *function; - char bin_path[128]; - std::string func = probe; - off_t func_off; - if (strList.size() == 3) - func = strList[1] + ":" + strList[2]; - - binary = strdup(func.c_str()); - function = strchr(binary, ':'); // 查找:首次出现的位置 - *function = '\0'; - function++; - - if (resolve_binary_path(binary, pid, bin_path, sizeof(bin_path))) - free(binary); - - func_off = get_elf_func_offset(bin_path, function); - if (func_off < 0) - free(binary); - skel->links.handle = - bpf_program__attach_uprobe(skel->progs.handle, false, pid, - bin_path, func_off); - CHECK_ERR(!skel->links.handle, "Fail to attach uprobe"); - return 0; - } - else if (strList.size() == 3 && strList[0] == "u") - { - // probe a USDT tracepoint - char bin_path[128]; - std::string func = probe; - int err = get_path(bin_path,pid); - CHECK_ERR(err, "Fail to get lib path"); - skel->links.handle_usdt = - bpf_program__attach_usdt(skel->progs.handle_usdt,pid,bin_path,"libc",strList[2].c_str(),NULL); - CHECK_ERR(!skel->links.handle_usdt, "Fail to attach usdt"); - return 0; - } - else - { - printf("Type must be 'p', 't', or 'u' or too any args"); - } +static int attach_tp(struct probe_bpf *skel, const std::string &tp_class, const std::string &func) +{ + skel->links.tp_exit = + bpf_program__attach_tracepoint(skel->progs.tp_exit, tp_class.c_str(), func.c_str()); + CHECK_ERR_RN1(!skel->links.tp_exit, "Fail to attach tracepoint"); return 0; }; -void ProbeStackCollector::detach(void) +static int attach_usdt(struct probe_bpf *skel, const std::string &func, int pid) { - DETACH_PROTO; + char bin_path[128]; + int err = get_binpath(bin_path, pid); + CHECK_ERR_RN1(err, "Fail to get lib path"); + skel->links.usdt_exit = + bpf_program__attach_usdt(skel->progs.usdt_exit, pid, bin_path, "libc", func.c_str(), NULL); + CHECK_ERR_RN1(!skel->links.usdt_exit, "Fail to attach usdt"); + return 0; +}; + +// ========== implement virtual func ========== + +uint64_t *ProbeStackCollector::count_values(void *data) +{ + time_tuple *p = (time_tuple *)data; + return new uint64_t[scale_num]{ + p->lat, + p->count, + }; +}; + +void ProbeStackCollector::setScale(std::string probe) +{ + this->probe = probe; + for (int i = 0; i < scale_num; i++) + scales[i].Type = probe + scales[i].Type; }; -void ProbeStackCollector::unload(void) +int ProbeStackCollector::ready(void) { + bool can_ftrace = true; + std::vector strList; + splitStr(probe, ':', strList); + EBPF_LOAD_OPEN_INIT( + if ((strList.size() == 3 && strList[0] == "p" && strList[1] == "") || + strList.size() == 1) + can_ftrace = try_fentry(skel, (strList.size() == 1 + ? probe + : strList[2]) + .c_str()); + else { + bpf_program__set_autoload(skel->progs.dummy_fentry, false); + bpf_program__set_autoload(skel->progs.dummy_fexit, false); + }); + + if ((strList.size() == 3 && strList[0] == "p" && strList[1] == "") || + strList.size() == 1) + if (can_ftrace) + err = attach_fentry(skel); + else + err = attach_kprobes(skel, (strList.size() == 1 + ? probe + : strList[2])); + else if (strList.size() == 3 && strList[0] == "t") + err = attach_tp(skel, strList[1], strList[2]); + else if (strList.size() == 2 || + (strList.size() == 3 && strList[0] == "p" && strList[1] != "")) + err = attach_uprobes(skel, + strList.size() == 3 + ? strList[1] + ":" + strList[2] + : probe, + tgid); + else if (strList.size() == 3 && strList[0] == "u") + err = attach_usdt(skel, strList[2], tgid); + else + err = 1; + CHECK_ERR_RN1(err, "Fail to attach"); + return 0; +}; + +void ProbeStackCollector::finish(void) +{ + DETACH_PROTO; UNLOAD_PROTO; }; void ProbeStackCollector::activate(bool tf) { ACTIVE_SET(tf); -} +}; -const char *ProbeStackCollector::getName(void) { +const char *ProbeStackCollector::getName(void) +{ return "ProbeStackCollector"; -} \ No newline at end of file +}; + +// ========== other implementations ========== + +ProbeStackCollector::ProbeStackCollector() +{ + scale_num = 2; + scales = new Scale[scale_num]{ + {"Time", 1, "nanoseconds"}, + {"Count", 1, "counts"}, + }; +}; \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/readahead.cpp b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/readahead.cpp index 3cc1ae2e5..536bd923b 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/readahead.cpp +++ b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/readahead.cpp @@ -37,25 +37,16 @@ ReadaheadStackCollector::ReadaheadStackCollector() }; }; -int ReadaheadStackCollector::load(void) +int ReadaheadStackCollector::ready(void) { EBPF_LOAD_OPEN_INIT(); - return 0; -} - -int ReadaheadStackCollector::attach(void) -{ ATTACH_PROTO; return 0; } -void ReadaheadStackCollector::detach(void) +void ReadaheadStackCollector::finish(void) { DETACH_PROTO; -} - -void ReadaheadStackCollector::unload(void) -{ UNLOAD_PROTO; } @@ -64,6 +55,7 @@ void ReadaheadStackCollector::activate(bool tf) ACTIVE_SET(tf); } -const char *ReadaheadStackCollector::getName(void) { +const char *ReadaheadStackCollector::getName(void) +{ return "ReadaheadStackCollector"; } \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/template.cpp b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/template.cpp index ac3e42725..3228bc7e6 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/template.cpp +++ b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/template.cpp @@ -27,26 +27,20 @@ uint64_t *TemplateClass::count_values(void *data) }; }; -int TemplateClass::load(void) +int TemplateClass::ready(void) { return 0; -}; - -int TemplateClass::attach(void) -{ - return 0; -}; - -void TemplateClass::detach(void){}; +} -void TemplateClass::unload(void){}; +void TemplateClass::finish(void) {}; void TemplateClass::activate(bool tf) { ACTIVE_SET(tf); } -const char *TemplateClass::getName(void) { +const char *TemplateClass::getName(void) +{ return "TemplateClass"; } diff --git a/eBPF_Supermarket/Stack_Analyser/src/cgroup.cpp b/eBPF_Supermarket/Stack_Analyser/src/cgroup.cpp new file mode 100644 index 000000000..fb6b4ad79 --- /dev/null +++ b/eBPF_Supermarket/Stack_Analyser/src/cgroup.cpp @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include +#include +#include +#include "cgroup.h" + +uint64_t get_cgroupid(const char *pathname) +{ + struct statfs fs; + int err; + struct cgid_file_handle *h; + int mount_id; + uint64_t ret; + + err = statfs(pathname, &fs); + if (err != 0) + { + fprintf(stderr, "statfs on %s failed: %s\n", pathname, strerror(errno)); + exit(1); + } + + if ((fs.f_type != (typeof(fs.f_type))CGROUP2_SUPER_MAGIC)) + { + fprintf(stderr, "File %s is not on a cgroup2 mount.\n", pathname); + exit(1); + } + + h = (cgid_file_handle *)malloc(sizeof(struct cgid_file_handle)); + if (!h) + { + fprintf(stderr, "Cannot allocate memory.\n"); + exit(1); + } + + h->handle_bytes = 8; + err = name_to_handle_at(AT_FDCWD, pathname, (struct file_handle *)h, &mount_id, 0); + if (err != 0) + { + fprintf(stderr, "name_to_handle_at failed: %s\n", strerror(errno)); + exit(1); + } + + if (h->handle_bytes != 8) + { + fprintf(stderr, "Unexpected handle size: %d. \n", h->handle_bytes); + exit(1); + } + + ret = h->cgid; + free(h); + + return ret; +} \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/src/dt_elf.cpp b/eBPF_Supermarket/Stack_Analyser/src/dt_elf.cpp deleted file mode 100644 index d040e8936..000000000 --- a/eBPF_Supermarket/Stack_Analyser/src/dt_elf.cpp +++ /dev/null @@ -1,621 +0,0 @@ -/* - * Linux内核诊断工具--elf相关公共函数 - * - * Copyright (C) 2020 Alibaba Ltd. - * - * License terms: GNU General Public License (GPL) version 3 - * - */ -#include "dt_elf.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define NOTE_ALIGN(n) (((n) + 3) & -4U) - -struct sym_section_ctx -{ - Elf_Data *syms; - Elf_Data *symstrs; - Elf_Data *rel_data; - int is_reloc; - int is_plt; - int sym_count; - int plt_rel_type; - unsigned long plt_offset; - unsigned long plt_entsize; -}; - -struct symbol_sections_ctx -{ - sym_section_ctx symtab; - sym_section_ctx symtab_in_dynsym; - sym_section_ctx dynsymtab; -}; - -struct section_info -{ - Elf_Scn *sec; - GElf_Shdr *hdr; -}; - -struct plt_ctx -{ - section_info dynsym; - section_info plt_rel; - section_info plt; -}; - -__attribute__((unused)) static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, - GElf_Shdr *shp, const char *name, - size_t *idx) -{ - Elf_Scn *sec = NULL; - size_t cnt = 1; - - /* Elf is corrupted/truncated, avoid calling elf_strptr. */ - if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL)) - return NULL; - - while ((sec = elf_nextscn(elf, sec)) != NULL) - { - char *str; - - gelf_getshdr(sec, shp); - str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); - - if (!strcmp(name, str)) - { - if (idx) - *idx = cnt; - - break; - } - - ++cnt; - } - - return sec; -} - -__attribute__((unused)) static int elf_read_build_id(Elf *elf, char *bf, size_t size) -{ - int err = -1; - GElf_Ehdr ehdr; - GElf_Shdr shdr; - Elf_Data *data; - Elf_Scn *sec; - Elf_Kind ek; - char *ptr; - - if (size < BUILD_ID_SIZE) - goto out; - - ek = elf_kind(elf); - - if (ek != ELF_K_ELF) - goto out; - - if (gelf_getehdr(elf, &ehdr) == NULL) - { - fprintf(stderr, "%s: cannot get elf header.\n", __func__); - goto out; - } - - /* - * Check following sections for notes: - * '.note.gnu.build-id' - * '.notes' - * '.note' (VDSO specific) - */ - do - { - sec = elf_section_by_name(elf, &ehdr, &shdr, - ".note.gnu.build-id", NULL); - - if (sec) - break; - - sec = elf_section_by_name(elf, &ehdr, &shdr, - ".notes", NULL); - - if (sec) - break; - - sec = elf_section_by_name(elf, &ehdr, &shdr, - ".note", NULL); - - if (sec) - break; - - return err; - - } while (0); - - data = elf_getdata(sec, NULL); - - if (data == NULL) - goto out; - - ptr = (char *)data->d_buf; - - while ((intptr_t)ptr < (intptr_t)((char *)data->d_buf + data->d_size)) - { - GElf_Nhdr *nhdr = (GElf_Nhdr *)ptr; - size_t namesz = NOTE_ALIGN(nhdr->n_namesz), - descsz = NOTE_ALIGN(nhdr->n_descsz); - const char *name; - - ptr += sizeof(*nhdr); - name = (const char *)ptr; - ptr += namesz; - - if (nhdr->n_type == NT_GNU_BUILD_ID && - nhdr->n_namesz == sizeof("GNU")) - { - if (memcmp(name, "GNU", sizeof("GNU")) == 0) - { - size_t sz = size < descsz ? size : descsz; - memcpy(bf, ptr, sz); - memset(bf + sz, 0, size - sz); - err = descsz; - break; - } - } - - ptr += descsz; - } - -out: - return err; -} - -extern int calc_sha1_1M(const char *filename, unsigned char *buf); - -int filename__read_build_id(int pid, const char *mnt_ns_name, const char *filename, char *bf, size_t size) -{ - int fd, err = -1; - struct stat sb; - - if (size < BUILD_ID_SIZE) - goto out; - - fd = open(filename, O_RDONLY); - - if (fd < 0) - goto out; - - if (fstat(fd, &sb) == 0) - { - snprintf(bf, size, "%s[%lu]", filename, sb.st_size); - err = 0; - } - - close(fd); -out: - return err; -} - -static int is_function(const GElf_Sym *sym) -{ - return GELF_ST_TYPE(sym->st_info) == STT_FUNC && - sym->st_name != 0 && - sym->st_shndx != SHN_UNDEF; -} - -static int get_symbols_in_section(sym_section_ctx *sym, Elf *elf, Elf_Scn *sec, GElf_Shdr *shdr, int is_reloc) -{ - sym->syms = elf_getdata(sec, NULL); - if (!sym->syms) - { - return -1; - } - - Elf_Scn *symstrs_sec = elf_getscn(elf, shdr->sh_link); - if (!sec) - { - return -1; - } - - sym->symstrs = elf_getdata(symstrs_sec, NULL); - if (!sym->symstrs) - { - return -1; - } - - sym->sym_count = shdr->sh_size / shdr->sh_entsize; - sym->is_plt = 0; - sym->is_reloc = is_reloc; - - return 0; -} - -static int get_plt_symbols_in_section(sym_section_ctx *sym, Elf *elf, plt_ctx *plt) -{ - sym->syms = elf_getdata(plt->dynsym.sec, NULL); - if (!sym->syms) - { - return -1; - } - - sym->rel_data = elf_getdata(plt->plt_rel.sec, NULL); - if (!sym->rel_data) - { - return -1; - } - - Elf_Scn *symstrs_sec = elf_getscn(elf, plt->dynsym.hdr->sh_link); - if (!symstrs_sec) - { - return -1; - } - - sym->symstrs = elf_getdata(symstrs_sec, NULL); - if (!sym->symstrs) - { - return -1; - } - - sym->is_plt = 1; - sym->plt_entsize = plt->plt.hdr->sh_type; - sym->plt_offset = plt->plt.hdr->sh_offset; - sym->sym_count = plt->plt_rel.hdr->sh_size / plt->plt_rel.hdr->sh_entsize; - sym->plt_rel_type = plt->plt_rel.hdr->sh_type; - - return 0; -} - -static void __get_plt_symbol(std::set &ss, symbol_sections_ctx *si, Elf *elf) -{ - symbol s; - GElf_Sym sym; - int symidx; - int index = 0; - const char *sym_name = NULL; - - s.end = 0; - s.start = 0; - - if (!si->dynsymtab.syms) - { - return; - } - - while (index < si->dynsymtab.sym_count) - { - if (si->dynsymtab.plt_rel_type == SHT_RELA) - { - GElf_Rela pos_mem, *pos; - pos = gelf_getrela(si->dynsymtab.rel_data, index, &pos_mem); - symidx = GELF_R_SYM(pos->r_info); - } - else if (si->dynsymtab.plt_rel_type == SHT_REL) - { - GElf_Rel pos_mem, *pos; - pos = gelf_getrel(si->dynsymtab.rel_data, index, &pos_mem); - symidx = GELF_R_SYM(pos->r_info); - } - else - { - return; - } - index++; - si->dynsymtab.plt_offset += si->dynsymtab.plt_entsize; - gelf_getsym(si->dynsymtab.syms, symidx, &sym); - - sym_name = (const char *)si->dynsymtab.symstrs->d_buf + sym.st_name; - s.start = si->dynsymtab.plt_offset; - s.end = s.start + si->dynsymtab.plt_entsize; - s.ip = s.start; - s.name = sym_name; - ss.insert(s); - } -} - -static void __get_symbol_without_plt(std::set &ss, sym_section_ctx *tab, Elf *elf) -{ - GElf_Sym sym; - int index = 0; - const char *sym_name; - symbol s; - s.end = 0; - s.start = 0; - - while (index < tab->sym_count) - { - gelf_getsym(tab->syms, index, &sym); - index++; - if (sym.st_shndx == SHN_ABS) - { - continue; - } - if (!is_function(&sym)) - { - continue; - } - sym_name = (const char *)tab->symstrs->d_buf + sym.st_name; - if (tab->is_reloc) - { - Elf_Scn *sec = elf_getscn(elf, sym.st_shndx); - if (!sec) - { - continue; - } - GElf_Shdr shdr; - gelf_getshdr(sec, &shdr); - sym.st_value -= shdr.sh_addr - shdr.sh_offset; - } - s.start = sym.st_value & 0xffffffff; - s.end = s.start + sym.st_size; - s.ip = s.start; - s.name = sym_name; - ss.insert(s); - } -} - -static void __get_symbol(std::set &ss, symbol_sections_ctx *si, Elf *elf) -{ - symbol s; - s.end = 0; - s.start = 0; - - if (!si->symtab.syms && !si->dynsymtab.syms) - { - return; - } - - sym_section_ctx *tab = &si->symtab; - __get_symbol_without_plt(ss, tab, elf); - tab = &si->symtab_in_dynsym; - __get_symbol_without_plt(ss, tab, elf); -} - -static void get_all_symbols(std::set &ss, symbol_sections_ctx *si, Elf *elf) -{ - __get_symbol(ss, si, elf); - __get_plt_symbol(ss, si, elf); -} - -bool search_symbol(const std::set &ss, symbol &sym) -{ - std::set::const_iterator it = ss.find(sym); - - if (it != ss.end()) - { - sym.end = it->end; - sym.start = it->start; - sym.name = it->name; - - return true; - } - - return false; -} - -bool get_symbol_from_elf(std::set &ss, const char *path) -{ - // static int first_init = 0; - - // if (!first_init) { - // first_init = true; - // init_global_env(); - // } - - int is_reloc = 0; - elf_version(EV_CURRENT); - int fd = open(path, O_RDONLY); - - Elf *elf = elf_begin(fd, ELF_C_READ, NULL); - if (elf == NULL) - { - close(fd); - return false; - } - - Elf_Kind ek = elf_kind(elf); - if (ek != ELF_K_ELF) - { - elf_end(elf); - close(fd); - return false; - } - GElf_Ehdr hdr; - if (gelf_getehdr(elf, &hdr) == NULL) - { - elf_end(elf); - close(fd); - return false; - } - - if (hdr.e_type == ET_EXEC) - { - is_reloc = 1; - } - - if (!elf_rawdata(elf_getscn(elf, hdr.e_shstrndx), NULL)) - { - elf_end(elf); - close(fd); - return false; - } - - GElf_Shdr shdr; - GElf_Shdr symtab_shdr; - GElf_Shdr dynsym_shdr; - GElf_Shdr plt_shdr; - GElf_Shdr plt_rel_shdr; - memset(&shdr, 0, sizeof(shdr)); - memset(&symtab_shdr, 0, sizeof(symtab_shdr)); - memset(&dynsym_shdr, 0, sizeof(dynsym_shdr)); - memset(&plt_shdr, 0, sizeof(plt_shdr)); - memset(&plt_rel_shdr, 0, sizeof(plt_rel_shdr)); - - Elf_Scn *sec = NULL; - Elf_Scn *dynsym_sec = NULL; - Elf_Scn *symtab_sec = NULL; - Elf_Scn *plt_sec = NULL; - Elf_Scn *plt_rel_sec = NULL; - - while ((sec = elf_nextscn(elf, sec)) != NULL) - { - char *str; - gelf_getshdr(sec, &shdr); - str = elf_strptr(elf, hdr.e_shstrndx, shdr.sh_name); - - if (str && strcmp(".symtab", str) == 0) - { - symtab_sec = sec; - memcpy(&symtab_shdr, &shdr, sizeof(dynsym_shdr)); - } - if (str && strcmp(".dynsym", str) == 0) - { - dynsym_sec = sec; - memcpy(&dynsym_shdr, &shdr, sizeof(dynsym_shdr)); - } - if (str && strcmp(".rela.plt", str) == 0) - { - plt_rel_sec = sec; - memcpy(&plt_rel_shdr, &shdr, sizeof(plt_rel_shdr)); - } - if (str && strcmp(".plt", str) == 0) - { - plt_sec = sec; - memcpy(&plt_shdr, &shdr, sizeof(plt_shdr)); - } - if (str && strcmp(".gnu.prelink_undo", str) == 0) - { - is_reloc = 1; - } - } - - plt_ctx plt; - plt.dynsym.hdr = &dynsym_shdr; - plt.dynsym.sec = dynsym_sec; - plt.plt.hdr = &plt_shdr; - plt.plt.sec = plt_sec; - plt.plt_rel.hdr = &plt_rel_shdr; - plt.plt_rel.sec = plt_rel_sec; - - symbol_sections_ctx si; - memset(&si, 0, sizeof(si)); - if (symtab_sec) - { - get_symbols_in_section(&si.symtab, elf, symtab_sec, &symtab_shdr, is_reloc); - } - if (dynsym_sec) - { - get_symbols_in_section(&si.symtab_in_dynsym, elf, dynsym_sec, &dynsym_shdr, is_reloc); - } - if (dynsym_sec && plt_sec) - { - get_plt_symbols_in_section(&si.dynsymtab, elf, &plt); - } - - get_all_symbols(ss, &si, elf); - elf_end(elf); - close(fd); - return true; -} - -struct symbol_cache_item -{ - int start; - int size; - char name[0]; -}; - -bool save_symbol_cache(std::set &ss, const char *path) -{ - char buf[2048]; - int len = 0; - bool status = true; - - int fd = open(path, O_RDONLY); - if (fd < 0) - { - status = false; - return status; - } - int ret; - ret = read(fd, &len, 4); - if (ret <= 0) - { - close(fd); - status = false; - return status; - } - ret = read(fd, buf, len); - if (ret <= 0) - { - close(fd); - status = false; - return status; - } - - while (1) - { - struct symbol_cache_item *sym; - symbol s; - ret = read(fd, &len, 4); - if (ret <= 0) - { - status = false; - break; - } - ret = read(fd, buf, len); - if (ret < len) - { - status = false; - break; - } - sym = (struct symbol_cache_item *)buf; - s.start = sym->start; - s.end = sym->start + sym->size; - s.ip = sym->start; - s.name = sym->name; - ss.insert(s); - } - close(fd); - return status; -} - -bool load_symbol_cache(std::set &ss, const char *path, const char *filename) -{ - int fd = open(path, O_RDWR | O_EXCL); - if (fd < 0) - { - return false; - } - int len = strlen(filename); - int ret = write(fd, &len, 4); - if (ret < 0) - { - close(fd); - return false; - } - ret = write(fd, filename, len); - if (ret < 0) - { - close(fd); - return false; - } - - std::set::iterator it; - int v; - for (it = ss.begin(); it != ss.end(); ++it) - { - v = it->start; - ret = write(fd, &v, 4); - v = it->end - it->start; - ret = write(fd, &v, 4); - ret = write(fd, it->name.c_str(), it->name.length()); - } - return true; -} diff --git a/eBPF_Supermarket/Stack_Analyser/src/dt_symbol.cpp b/eBPF_Supermarket/Stack_Analyser/src/dt_symbol.cpp deleted file mode 100644 index 92feb0d7b..000000000 --- a/eBPF_Supermarket/Stack_Analyser/src/dt_symbol.cpp +++ /dev/null @@ -1,582 +0,0 @@ -/* - * Linux内核诊断工具--用户态符号表解析 - * - * Copyright (C) 2020 Alibaba Ltd. - * - * License terms: GNU General Public License (GPL) version 3 - * - */ - -#include -#include -#include -#include - -#include "dt_symbol.h" -#include "dt_elf.h" - -void restore_global_env(); -int attach_ns_env(int pid); - -symbol_parser g_symbol_parser; -const bool debug_mode = false; - -bool symbol_parser::add_pid_maps(int pid, size_t start, size_t end, size_t offset, const char *name) -{ - std::map::iterator it; - it = machine_vma.find(pid); - if (it == machine_vma.end()) { - proc_vma proc; - machine_vma.insert(make_pair(pid, proc)); - it = machine_vma.find(pid); - if (it == machine_vma.end()) { - return false; - } - } - - vma vm(start, end, offset, name); - it->second.insert(std::make_pair(vm.start, std::move(vm))); - - return true; -} - -bool symbol_parser::load_pid_maps(int pid) -{ - std::map::iterator it; - it = machine_vma.find(pid); - if (it != machine_vma.end()) { - return true; - } - - proc_vma proc; - char fn[256]; - sprintf(fn, "/proc/%d/maps", pid); - FILE *fp = fopen(fn, "r"); - if (!fp) { - return false; - } - - char buf[4096]; - char exename[4096]; - size_t start, end, offset; - while (fgets(buf, sizeof(buf), fp) != NULL) { - start = end = offset = 0; - exename[0] = '\0'; - sscanf(buf, "%lx-%lx %*s %lx %*x:%*x %*u %s %*s\n", &start, &end, &offset, exename); - if (exename[0] == '\0') { - strcpy(exename, "[anon]"); - } - vma vm(start, end, offset, exename); - proc.insert(std::make_pair(vm.start, std::move(vm))); - } - - fclose(fp); - - machine_vma.insert(std::make_pair(pid, std::move(proc))); - it = machine_vma.find(pid); - if (it == machine_vma.end()) { - return false; - } - - return true; -} - -bool symbol_parser::load_perf_map(int pid, int pid_ns) -{ -#if 0 - if (pid != pid_ns) { - if (attach_ns_env(pid) < 0) { - return false; - } - } -#endif - char perfmapfile[64]; - snprintf(perfmapfile, sizeof(perfmapfile), "/tmp/perf-%d.map", pid); - FILE *fp = fopen(perfmapfile, "r"); - if (fp == NULL) { - if (debug_mode) { - printf("cannot read perf map %d\n", pid); - } - return false; - } - char line[256]; - char *buf; - long start; - int size; - char name[256]; - std::set syms; - symbol sym; - while ((buf = fgets(line, sizeof(line), fp)) != NULL) { - sscanf(buf, "%lx %x %s\n", &start, &size, name); - sym.start = start; - sym.end = sym.start + size; - sym.ip = sym.start; - sym.name = name; - syms.insert(sym); - } - java_symbols.insert(make_pair(pid, std::move(syms))); -#if 0 - if (pid != pid_ns) { - restore_global_env(); - } -#endif - return true; -} - -bool symbol_parser::find_java_symbol(symbol &sym, int pid, int pid_ns) -{ - std::set ss; - std::map >::iterator it; - //bool load_now = false; - it = java_symbols.find(pid); - if (it == java_symbols.end()) { - if (!load_perf_map(pid, pid_ns)) { - return false; - } - //load_now = true; - it = java_symbols.find(pid); - return search_symbol(it->second, sym); - } else { - return search_symbol(it->second, sym); - } - return true; - - //bool ret = search_symbol(syms, sym); -#if 0 - if (!ret && !load_now) { - java_symbols.erase(pid); - if (!load_perf_map(pid)) { - return false; - } - syms = java_symbols.find(pid)->second; - return search_symbol(syms, sym); - } -#endif - //return ret; -} - -static bool load_kernel_symbol_list(std::vector &sym_list) -{ - FILE *fp = fopen("/proc/kallsyms", "r"); - if (!fp) { - return -1; - } - - char buf[256]; - char type; - int len; - while (fgets(buf, sizeof(buf), fp) != NULL) { - sscanf(buf, "%*p %c %*s\n", &type); - if ((type | 0x20) != 't') { - continue; - } - len = strlen(buf); - if (buf[len-1] == '\n') { - buf[len-1] = '\0'; - } - sym_list.push_back(buf); - } - fclose(fp); - - std::sort(sym_list.begin(), sym_list.end()); - return true; -} - -bool is_space(int ch) { - return std::isspace(ch); -} - -static inline void rtrim(std::string &s) -{ - s.erase(std::find_if(s.rbegin(), s.rend(), is_space).base(), s.end()); -} - -static bool get_next_kernel_symbol( - std::set &syms, - std::vector &sym_list, - std::vector::iterator cursor) -{ - if (cursor == sym_list.end()) { - return false; - } - symbol sym; - size_t start, end; - sscanf(cursor->c_str(), "%p %*c %*s\n", (void **)&start); - sym.name = cursor->c_str() + 19; - // rtrim(sym.name); -// #if 0 - // if (sym.name[sym.name.size()-1] == ' ') { - // // sym.name[sym.name.size()-1] = '\0'; - // sym.name.pop_back(); - // } -// #endif - cursor++; - if (cursor != sym_list.end()) { - sscanf(cursor->c_str(), "%p %*c %*s\n", (void **)&end); - } - else { - end = INVALID_ADDR; - } - sym.start = start; - sym.end = end; - sym.ip = start; - - syms.insert(sym); - return true; -} - -bool symbol_parser::load_kernel() -{ - if (kernel_symbols.size() != 0) { - return true; - } - - std::vector sym_list; - if (!load_kernel_symbol_list(sym_list)) { - exit(0); - return false; - } - - std::vector::iterator cursor = sym_list.begin(); - while (get_next_kernel_symbol(kernel_symbols, sym_list, cursor)) { - cursor++; - } - return true; -} - -bool symbol_parser::load_elf(pid_t pid, const elf_file &file) -{ - std::map >::iterator it; - it = file_symbols.find(file); - std::set tmp; - std::set &syms = tmp; - if (it != file_symbols.end()) { - return true; - } - if (get_symbol_from_elf(syms, file.filename.c_str())) { - file_symbols.insert(make_pair(file, std::move(syms))); - return true; - } - return false; -} - -bool symbol_parser::find_kernel_symbol(symbol &sym) -{ - load_kernel(); - sym.end = sym.start = 0; - std::set::iterator it = kernel_symbols.find(sym); - if (it != kernel_symbols.end()) { - sym.end = it->end; - sym.start = it->start; - sym.name = it->name; - return true; - } - return false; -} - -bool symbol_parser::complete_kernel_symbol(symbol &sym) -{ - load_kernel(); - sym.end = sym.start = 0; - for (auto it = kernel_symbols.begin(); it != kernel_symbols.end(); ++it) { - auto size = sym.name.size(), tsize = it->name.size(); - if(size > tsize || it->name.substr(tsize-5, 5) == ".cold") { - continue; - } - if(it->name.substr(0, size) == sym.name) { - sym.end = it->end; - sym.start = it->start; - sym.name = it->name; - return true; - } - } - return false; -} - -bool symbol_parser::find_symbol_in_cache(int tgid, unsigned long addr, std::string &symbol) -{ - std::map >::const_iterator it_pid = - symbols_cache.find(tgid); - - if (it_pid != symbols_cache.end()) { - std::map map = symbols_cache[tgid]; - std::map::const_iterator it_symbol = - map.find(addr); - - if (it_symbol != map.end()) { - symbol = map[addr]; - - return true; - } - } - - return false; -} - -bool symbol_parser::putin_symbol_cache(int tgid, unsigned long addr, std::string &symbol) -{ - std::map >::const_iterator it_pid = - symbols_cache.find(tgid); - - if (it_pid == symbols_cache.end()) { - std::map map; - symbols_cache.insert(std::make_pair(tgid, map)); - } - - std::map &map = symbols_cache[tgid]; - std::map::const_iterator it_symbol = - map.find(addr); - - if (it_symbol == map.end()) { - map[addr] = symbol; - return true; - } - - return false; -} - -bool symbol_parser::get_symbol_info(int pid, symbol &sym, elf_file &file) -{ - std::map::iterator proc_vma_info; - - if (java_only) { - file.type = UNKNOWN; - return true; - } - - proc_vma_info = machine_vma.find(pid); - if (proc_vma_info == machine_vma.end()) { - if (!load_pid_maps(pid)) { - if (debug_mode) { - printf("load pid maps failed\n"); - } - return false; - } - } - - vma area(sym.ip); - if (!find_vma(pid, area)) { - if (debug_mode) { - printf("find vma failed\n"); - } - return false; - } - if (area.name == "[anon]") { - file.type = JIT_TYPE; - } - - file.reset(area.name); - if (file.type != JIT_TYPE) { - sym.reset(area.map(sym.ip)); - } - - return true; -} - -bool symbol_parser::find_elf_symbol(symbol &sym, const elf_file &file, int pid, int pid_ns) -{ - if (java_only) { - return find_java_symbol(sym, pid, pid_ns); - } - - if (file.type == JIT_TYPE) { - return find_java_symbol(sym, pid, pid_ns); - } - - std::map >::iterator it; - it = file_symbols.find(file); - std::set ss; - if (it == file_symbols.end()) { - if (!load_elf(pid, file)) { - return false; - } - it = file_symbols.find(file); - } - return search_symbol(it->second, sym); -} - -vma* symbol_parser::find_vma(pid_t pid, size_t pc) -{ - std::map::iterator it; - - it = machine_vma.find(pid); - if (it == machine_vma.end()) { - return NULL; - } - - proc_vma::iterator vma_iter = it->second.upper_bound(pc); - if (vma_iter == it->second.end() || vma_iter->second.end < pc) { - return NULL; - } - - if (vma_iter != it->second.begin()) { - --vma_iter; - } - - return &vma_iter->second; -} - -bool symbol_parser::find_vma(pid_t pid, vma &vm) -{ - std::map::iterator proc_vma_map; - - proc_vma_map = machine_vma.find(pid); - if (proc_vma_map == machine_vma.end()) { - return false; - } - - proc_vma::const_iterator vma_iter = proc_vma_map->second.upper_bound(vm.pc); - if (vma_iter == proc_vma_map->second.end()) { - return false; - } - if (vma_iter->second.end < vm.pc) { - return false; - } - - if (vma_iter != proc_vma_map->second.begin()) { - --vma_iter; - } - - vm.start = vma_iter->second.start; - vm.end = vma_iter->second.end; - vm.name = vma_iter->second.name; - vm.offset = vma_iter->second.offset; - - return true; -} - -class pid_cmdline { - private: - std::map cmdlines; - public: - void clear(void); - std::string & get_pid_cmdline(int pid); -}; - -void pid_cmdline::clear(void) -{ - cmdlines.clear(); -} - -void clear_symbol_info(class pid_cmdline &pid_cmdline, std::set &procs, int dist) -{ - pid_cmdline.clear(); - procs.clear(); - g_symbol_parser.clear_symbol_info(dist); -} - -void symbol_parser::clear_symbol_info(int dist) -{ - machine_vma.clear(); - java_symbols.clear(); - if (dist) { - kernel_symbols.clear(); - file_symbols.clear(); - } -} - -void symbol_parser::dump(void) -{ - int count1, count2, count3; - - if (!debug_mode) - return; - - { - count1 = 0; - count2 = 0; - count3 = 0; - std::map >::iterator iter = file_symbols.begin(); - for(; iter != file_symbols.end(); ++iter) { - std::set& map = iter->second; - const elf_file& file = iter->first; - - count1++; - printf("xby-debug, file_symbols: %s, %lu\n", - file.filename.c_str(), - map.size()); - - count2 += map.size(); - std::set::iterator it = map.begin(); - for(; it != map.end(); ++it) { - count3 += it->name.length(); - } - } - printf("xby-debug, file_symbols: %d, %d, %d\n", count1, count2, count3); - printf("xby-debug, sizeof(symbol): %ld\n", sizeof(symbol)); - } - - { - count1 = 0; - count2 = 0; - std::map >::iterator iter = java_symbols.begin(); - for(; iter != java_symbols.end(); ++iter) { - count1++; - std::set& map = iter->second; - count2 += map.size(); - } - printf("xby-debug, java_symbols: %d, %d\n", count1, count2); - } - - { - printf("xby-debug, kernel_symbols: %lu\n", kernel_symbols.size()); - } - - { - count1 = 0; - count2 = 0; - std::map::iterator iter = machine_vma.begin(); - for(; iter != machine_vma.end(); ++iter) { - count1++; - proc_vma map = iter->second; - count2 += map.size(); - } - printf("xby-debug, machine_vma: %d, %d\n", count1, count2); - } - - { - count1 = 0; - count2 = 0; - std::map >::iterator iter = symbols_cache.begin(); - for(; iter != symbols_cache.end(); ++iter) { - count1++; - std::map& map = iter->second; - count2 += map.size(); - } - printf("xby-debug, symbols_cache: %d, %d\n", count1, count2); - } -} - -std::string demangleCppSym(std::string symbol) -{ - size_t size = 0; - int status = 0; - char *demangled = abi::__cxa_demangle(symbol.c_str(), NULL, &size, &status); - - if (status == 0 && demangled != NULL) - { - std::string FuncName(demangled); - free(demangled); - return FuncName; - } - else - { - // 解码失败,返回原始符号 - return symbol; - } -} - -void clearSpace(std::string &sym) -{ - for (auto i = sym.begin(); i != sym.end();) - { - if (isblank(*i)) - { - sym.erase(i); - } - else - { - i++; - } - } -} \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/src/main.cpp b/eBPF_Supermarket/Stack_Analyser/src/main.cpp index 842363627..435d380b9 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/main.cpp +++ b/eBPF_Supermarket/Stack_Analyser/src/main.cpp @@ -29,145 +29,127 @@ #include "bpf_wapper/io.h" #include "bpf_wapper/readahead.h" #include "bpf_wapper/probe.h" -#include "sa_user.h" +#include "user.h" #include "clipp.h" +#include "cgroup.h" +#include "trace.h" -uint64_t stop_time = -1; bool timeout = false; -uint64_t IntTmp; -std::string StrTmp; -clipp::man_page *man_page; +std::vector StackCollectorList; +void end_handle(void); namespace MainConfig { uint64_t run_time = -1; // 运行时间 unsigned delay = 5; // 设置输出间隔 std::string command = ""; - int32_t target_pid = -1; + uint32_t target_tgid = 0; + uint64_t target_cgroup = 0; std::string trigger = ""; // 触发器 std::string trig_event = ""; // 触发事件 -} - -std::vector StackCollectorList; - -void end_handle(void) -{ - signal(SIGINT, SIG_IGN); - for (auto Item : StackCollectorList) - { - Item->activate(false); - if (!timeout) - { - std::cout << std::string(*Item) << std::endl; - } - Item->detach(); - Item->unload(); - } - if (MainConfig::command.length()) - { - kill(MainConfig::target_pid, SIGTERM); - } + uint32_t top = 10; + uint32_t freq = 49; + bool trace_user = false; + bool trace_kernel = false; } int main(int argc, char *argv[]) { - man_page = new clipp::man_page(); + uint64_t stop_time = -1; + clipp::man_page man_page; clipp::group cli; { - auto TraceOption = (clipp::option("-u") - .call([] - { StackCollectorList.back()->ustack = true; }) % - "Sample user stacks", - clipp::option("-k") - .call([] - { StackCollectorList.back()->kstack = true; }) % - "Sample kernel stacks\n"); - + uint64_t IntTmp; + std::string StrTmp; auto OnCpuOption = (clipp::option("on_cpu") .call([] { StackCollectorList.push_back(new OnCPUStackCollector()); }) % - COLLECTOR_INFO("on-cpu")) & - ((clipp::option("-f") & - clipp::value("freq", IntTmp) - .call([] - { static_cast(StackCollectorList.back()) - ->setScale(IntTmp); })) % - "Set sampling frequency; default is 49", - TraceOption); + COLLECTOR_INFO("on-cpu")); auto OffCpuOption = clipp::option("off_cpu") - .call([] - { StackCollectorList.push_back(new OffCPUStackCollector()); }) % - COLLECTOR_INFO("off-cpu") & - (TraceOption); + .call([] + { StackCollectorList.push_back(new OffCPUStackCollector()); }) % + COLLECTOR_INFO("off-cpu"); auto MemleakOption = (clipp::option("memleak") .call([] { StackCollectorList.push_back(new MemleakStackCollector()); }) % COLLECTOR_INFO("memleak")) & - ((clipp::option("-i") & - clipp::value("interval", IntTmp) - .call([] - { static_cast(StackCollectorList.back()) - ->sample_rate = IntTmp; })) % - "Set the sampling interval; default is 1", - clipp::option("-w") - .call([] - { static_cast(StackCollectorList.back()) - ->wa_missing_free = true; }) % - "Free when missing in kernel to alleviate misjudgments", - TraceOption); + (clipp::option("-W") + .call([] + { static_cast(StackCollectorList.back()) + ->wa_missing_free = true; }) % + "Free when missing in kernel to alleviate misjudgments"); auto IOOption = clipp::option("io") - .call([] - { StackCollectorList.push_back(new IOStackCollector()); }) % - COLLECTOR_INFO("io") & - (TraceOption); + .call([] + { StackCollectorList.push_back(new IOStackCollector()); }) % + COLLECTOR_INFO("io"); auto ReadaheadOption = clipp::option("readahead") - .call([] - { StackCollectorList.push_back(new ReadaheadStackCollector()); }) % - COLLECTOR_INFO("readahead") & - (TraceOption); - - auto ProbeOption = clipp::option("probe") .call([] - { StackCollectorList.push_back(new ProbeStackCollector()); }) % - COLLECTOR_INFO("probe") & - (clipp::value("probe", StrTmp) - .call([] - { static_cast(StackCollectorList.back()) - ->setScale(StrTmp); }) % - "Set the probe string; specific use is: \n probe func && probe p::func -- probe a kernel function; \n lib:func && p:lib:func -- probe a user-space function in the library 'lib';\n probe t:cat:event -- probe a kernel tracepoint; \n probe u:lib:probe -- probe a USDT tracepoint" & - TraceOption); + { StackCollectorList.push_back(new ReadaheadStackCollector()); }) % + COLLECTOR_INFO("readahead"); auto LlcStatOption = clipp::option("llc_stat").call([] { StackCollectorList.push_back(new LlcStatStackCollector()); }) % COLLECTOR_INFO("llc_stat") & - ((clipp::option("-i") & + ((clipp::option("-P") & clipp::value("period", IntTmp) - .call([] + .call([IntTmp] { static_cast(StackCollectorList.back()) ->setScale(IntTmp); })) % - "Set sampling period; default is 100", - TraceOption); + "Set sampling period; default is 100"); + + auto ProbeOption = clipp::option("probe") + .call([] + { StackCollectorList.push_back(new ProbeStackCollector()); }) % + COLLECTOR_INFO("probe") & + (clipp::value("probe", StrTmp) + .call([&StrTmp] + { static_cast(StackCollectorList.back()) + ->setScale(StrTmp); }) % + "Set the probe string; specific use is:\n" + " | p:: -- probe a kernel function;\n" + ": | p:: -- probe a user-space function in the library 'lib';\n" + "t:: -- probe a kernel tracepoint;\n" + "u:: -- probe a USDT tracepoint"); auto MainOption = _GREEN "Some overall options" _RE % (( + ((clipp::option("-g") & + clipp::value("cgroup path", StrTmp) + .call([&StrTmp] + { MainConfig::target_cgroup = get_cgroupid(StrTmp.c_str()); printf("Trace cgroup %ld\n", MainConfig::target_cgroup); })) % + "Set the cgroup of the process to be tracked; default is -1, which keeps track of all cgroups") | ((clipp::option("-p") & - clipp::value("pid", MainConfig::target_pid)) % + clipp::value("pid", MainConfig::target_tgid)) % "Set the pid of the process to be tracked; default is -1, which keeps track of all processes") | ((clipp::option("-c") & clipp::value("command", MainConfig::command)) % "Set the command to be run and sampled; defaults is none")), - (clipp::option("-d") & + (clipp::option("-o") & + clipp::value("top", MainConfig::top)) % + "Set the top number; default is 10", + (clipp::option("-f") & + clipp::value("freq", MainConfig::freq)) % + "Set sampling frequency, 0 for close; default is 49", + (clipp::option("-i") & clipp::value("interval", MainConfig::delay)) % "Set the output delay time (seconds); default is 5", - (clipp::option("-t") & + (clipp::option("-d") & clipp::value("duration", MainConfig::run_time) - .call([] + .call([&stop_time] { stop_time = time(NULL) + MainConfig::run_time; })) % "Set the total sampling time; default is __INT_MAX__", + (clipp::option("-u") + .call([] + { MainConfig::trace_user = true; }) % + "Sample user stacks", + clipp::option("-k") + .call([] + { MainConfig::trace_kernel = true; }) % + "Sample kernel stacks"), (clipp::option("-T") & ((clipp::required("cpu").set(MainConfig::trigger) | clipp::required("memory").set(MainConfig::trigger) | @@ -183,8 +165,8 @@ int main(int argc, char *argv[]) { std::cout << "verion 2.0\n\n"; }) % "Show version"), (clipp::option("-h", "--help") - .call([] - { std::cout << *man_page << std::endl; exit(0); }) % + .call([&man_page] + { std::cout << man_page << std::endl; exit(0); }) % "Show man page")); cli = (OnCpuOption, @@ -192,8 +174,8 @@ int main(int argc, char *argv[]) MemleakOption, IOOption, ReadaheadOption, - ProbeOption, LlcStatOption, + clipp::repeatable(ProbeOption), MainOption, Info); } @@ -202,13 +184,13 @@ int main(int argc, char *argv[]) .first_column(3) .doc_column(25) .last_column(128); - *man_page = clipp::make_man_page(cli, argv[0], fmt) - .prepend_section("DESCRIPTION", _RED "Count the function call stack associated with some metric.\n" _RE BANNER) - .append_section("LICENSE", _RED "Apache Licence 2.0" _RE); + man_page = clipp::make_man_page(cli, argv[0], fmt) + .prepend_section("DESCRIPTION", _RED "Count the function call stack associated with some metric.\n" _RE BANNER) + .append_section("LICENSE", _RED "Apache Licence 2.0" _RE); } if (!clipp::parse(argc, argv, cli)) { - std::cerr << *man_page << std::endl; + std::cerr << man_page << std::endl; return -1; } if (StackCollectorList.size() == 0) @@ -221,46 +203,63 @@ int main(int argc, char *argv[]) uint64_t eventbuff = 1; int child_exec_event_fd = eventfd(0, EFD_CLOEXEC); - CHECK_ERR(child_exec_event_fd < 0, "failed to create event fd"); + CHECK_ERR_RN1(child_exec_event_fd < 0, "failed to create event fd"); if (MainConfig::command.length()) { - MainConfig::target_pid = fork(); - switch (MainConfig::target_pid) + MainConfig::target_tgid = fork(); + switch (MainConfig::target_tgid) { - case -1: + case (uint32_t)-1: { - CHECK_ERR(true, "Command create failed."); + CHECK_ERR_RN1(true, "Command create failed."); } case 0: { const auto bytes = read(child_exec_event_fd, &eventbuff, sizeof(eventbuff)); - CHECK_ERR(bytes < 0, "Failed to read from fd %ld", bytes) - else CHECK_ERR(bytes != sizeof(eventbuff), "Read unexpected size %ld", bytes); + CHECK_ERR_RN1(bytes < 0, "Failed to read from fd %ld", bytes) + else CHECK_ERR_RN1(bytes != sizeof(eventbuff), "Read unexpected size %ld", bytes); printf("child exec %s\n", MainConfig::command.c_str()); - CHECK_ERR_EXIT(execl("/bin/bash", "bash", "-c", MainConfig::command.c_str(), NULL), "failed to execute child command"); + CHECK_ERR(exit(-1), execl("/bin/bash", "bash", "-c", MainConfig::command.c_str(), NULL), "failed to execute child command"); break; } default: { - printf("Create child %d\n", MainConfig::target_pid); + printf("Create child %d\n", MainConfig::target_tgid); break; } } } + ksyms = ksyms__load(); + if (!ksyms) + { + fprintf(stderr, "failed to load kallsyms\n"); + exit(1); + } + syms_cache = syms_cache__new(0); + if (!syms_cache) + { + fprintf(stderr, "failed to create syms_cache\n"); + exit(1); + } + for (auto Item = StackCollectorList.begin(); Item != StackCollectorList.end();) { fprintf(stderr, _RED "Attach collecotor%d %s.\n" _RE, (int)(Item - StackCollectorList.begin()) + 1, (*Item)->getName()); - (*Item)->pid = MainConfig::target_pid; - if ((*Item)->load() || (*Item)->attach()) + (*Item)->tgid = MainConfig::target_tgid; + (*Item)->cgroup = MainConfig::target_cgroup; + (*Item)->top = MainConfig::top; + (*Item)->freq = MainConfig::freq; + (*Item)->kstack = MainConfig::trace_kernel; + (*Item)->ustack = MainConfig::trace_user; + if ((*Item)->ready()) goto err; Item++; continue; err: - fprintf(stderr, _ERED "Collector %s err.\n" _RE, (*Item)->scales->Type.c_str()); - (*Item)->detach(); - (*Item)->unload(); + fprintf(stderr, _ERED "Collector %s err.\n" _RE, (*Item)->getName()); + (*Item)->finish(); Item = StackCollectorList.erase(Item); } @@ -287,21 +286,21 @@ int main(int argc, char *argv[]) auto trig = MainConfig::trig_event.c_str(); fds.fd = open(path, O_RDWR | O_NONBLOCK); - CHECK_ERR(fds.fd < 0, "%s open error", path); + CHECK_ERR_RN1(fds.fd < 0, "%s open error", path); fds.events = POLLPRI; - CHECK_ERR(write(fds.fd, trig, strlen(trig) + 1) < 0, "%s write error", path); + CHECK_ERR_RN1(write(fds.fd, trig, strlen(trig) + 1) < 0, "%s write error", path); fprintf(stderr, _RED "Waiting for events...\n" _RE); } fprintf(stderr, _RED "Running for %lus or Hit Ctrl-C to end.\n" _RE, MainConfig::run_time); - for (; (uint64_t)time(NULL) < stop_time && (MainConfig::target_pid < 0 || !kill(MainConfig::target_pid, 0));) + for (; (uint64_t)time(NULL) < stop_time && (MainConfig::target_tgid < 0 || !kill(MainConfig::target_tgid, 0));) { if (fds.fd >= 0) { while (true) { int n = poll(&fds, 1, -1); - CHECK_ERR(n < 0, "Poll error"); - CHECK_ERR(fds.revents & POLLERR, "Got POLLERR, event source is gone"); + CHECK_ERR_RN1(n < 0, "Poll error"); + CHECK_ERR_RN1(fds.revents & POLLERR, "Got POLLERR, event source is gone"); if (fds.revents & POLLPRI) { fprintf(stderr, _RED "Event triggered!\n" _RE); @@ -319,4 +318,22 @@ int main(int argc, char *argv[]) } timeout = true; return 0; -} \ No newline at end of file +}; + +void end_handle(void) +{ + signal(SIGINT, SIG_IGN); + for (auto Item : StackCollectorList) + { + Item->activate(false); + if (!timeout) + { + std::cout << std::string(*Item) << std::endl; + } + Item->finish(); + } + if (MainConfig::command.length()) + { + kill(MainConfig::target_tgid, SIGTERM); + } +}; \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/src/trace_helpers.cpp b/eBPF_Supermarket/Stack_Analyser/src/trace.cpp similarity index 82% rename from eBPF_Supermarket/Stack_Analyser/src/trace_helpers.cpp rename to eBPF_Supermarket/Stack_Analyser/src/trace.cpp index 6097ec6e7..a8dc45044 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/trace_helpers.cpp +++ b/eBPF_Supermarket/Stack_Analyser/src/trace.cpp @@ -20,8 +20,8 @@ #include #include #include -#include "trace_helpers.h" -#include "uprobe_helpers.h" +#include "trace.h" +#include "uprobe.h" #define min(x, y) ({ \ typeof(x) _min1 = (x); \ @@ -29,14 +29,15 @@ (void) (&_min1 == &_min2); \ _min1 < _min2 ? _min1 : _min2; }) -#define DISK_NAME_LEN 32 +#define DISK_NAME_LEN 32 -#define MINORBITS 20 -#define MINORMASK ((1U << MINORBITS) - 1) +#define MINORBITS 20 +#define MINORMASK ((1U << MINORBITS) - 1) -#define MKDEV(ma, mi) (((ma) << MINORBITS) | (mi)) +#define MKDEV(ma, mi) (((ma) << MINORBITS) | (mi)) -struct ksyms { +struct ksyms +{ struct ksym *syms; int syms_sz; int syms_cap; @@ -51,7 +52,8 @@ static int ksyms__add_symbol(struct ksyms *ksyms, const char *name, unsigned lon struct ksym *ksym; void *tmp; - if (ksyms->strs_sz + name_len > ksyms->strs_cap) { + if (ksyms->strs_sz + name_len > ksyms->strs_cap) + { new_cap = ksyms->strs_cap * 4 / 3; if (new_cap < ksyms->strs_sz + name_len) new_cap = ksyms->strs_sz + name_len; @@ -63,7 +65,8 @@ static int ksyms__add_symbol(struct ksyms *ksyms, const char *name, unsigned lon ksyms->strs = (char *)tmp; ksyms->strs_cap = new_cap; } - if (ksyms->syms_sz + 1 > ksyms->syms_cap) { + if (ksyms->syms_sz + 1 > ksyms->syms_cap) + { new_cap = ksyms->syms_cap * 4 / 3; if (new_cap < 1024) new_cap = 1024; @@ -111,9 +114,10 @@ struct ksyms *ksyms__load(void) if (!ksyms) goto err_out; - while (true) { + while (true) + { ret = fscanf(f, "%lx %c %s%*[^\n]\n", - &sym_addr, &sym_type, sym_name); + &sym_addr, &sym_type, sym_name); if (ret == EOF && feof(f)) break; if (ret != 3) @@ -148,13 +152,14 @@ void ksyms__free(struct ksyms *ksyms) } const struct ksym *ksyms__map_addr(const struct ksyms *ksyms, - unsigned long addr) + unsigned long addr) { int start = 0, end = ksyms->syms_sz - 1, mid; unsigned long sym_addr; /* find largest sym_addr <= addr using binary search */ - while (start < end) { + while (start < end) + { mid = start + (end - start + 1) / 2; sym_addr = ksyms->syms[mid].addr; @@ -170,11 +175,12 @@ const struct ksym *ksyms__map_addr(const struct ksyms *ksyms, } const struct ksym *ksyms__get_symbol(const struct ksyms *ksyms, - const char *name) + const char *name) { int i; - for (i = 0; i < ksyms->syms_sz; i++) { + for (i = 0; i < ksyms->syms_sz; i++) + { if (strcmp(ksyms->syms[i].name, name) == 0) return &ksyms->syms[i]; } @@ -182,13 +188,15 @@ const struct ksym *ksyms__get_symbol(const struct ksyms *ksyms, return NULL; } -struct load_range { +struct load_range +{ uint64_t start; uint64_t end; uint64_t file_off; }; -enum elf_type { +enum elf_type +{ EXEC, DYN, PERF_MAP, @@ -196,7 +204,8 @@ enum elf_type { UNKNOWN, }; -struct dso { +struct dso +{ char *name; struct load_range *ranges; int range_sz; @@ -218,7 +227,8 @@ struct dso { struct btf *btf; }; -struct map { +struct map +{ uint64_t start_addr; uint64_t end_addr; uint64_t file_off; @@ -227,7 +237,8 @@ struct map { uint64_t inode; }; -struct syms { +struct syms +{ struct dso *dsos; int dso_sz; }; @@ -238,14 +249,15 @@ static bool is_file_backed(const char *mapname) (!strncmp(mapname, prefix, sizeof(prefix) - 1)) return mapname[0] && !( - STARTS_WITH(mapname, "//anon") || - STARTS_WITH(mapname, "/dev/zero") || - STARTS_WITH(mapname, "/anon_hugepage") || - STARTS_WITH(mapname, "[stack") || - STARTS_WITH(mapname, "/SYSV") || - STARTS_WITH(mapname, "[heap]") || - STARTS_WITH(mapname, "[uprobes]") || - STARTS_WITH(mapname, "[vsyscall]")); + STARTS_WITH(mapname, "//anon") || + STARTS_WITH(mapname, "/dev/zero") || + STARTS_WITH(mapname, "/anon_hugepage") || + STARTS_WITH(mapname, "[stack") || + STARTS_WITH(mapname, "/SYSV") || + STARTS_WITH(mapname, "[heap]") || + STARTS_WITH(mapname, "[uprobes]") || + STARTS_WITH(mapname, "[vsyscall]") || + STARTS_WITH(mapname, "[vdso]")); } static bool is_perf_map(const char *path) @@ -285,7 +297,7 @@ static int get_elf_type(const char *path) } static int get_elf_text_scn_info(const char *path, uint64_t *addr, - uint64_t *offset) + uint64_t *offset) { Elf_Scn *section = NULL; int fd = -1, err = -1; @@ -302,12 +314,14 @@ static int get_elf_text_scn_info(const char *path, uint64_t *addr, goto err_out; err = -1; - while ((section = elf_nextscn(e, section)) != 0) { + while ((section = elf_nextscn(e, section)) != 0) + { if (!gelf_getshdr(section, &header)) continue; name = elf_strptr(e, stridx, header.sh_name); - if (name && !strcmp(name, ".text")) { + if (name && !strcmp(name, ".text")) + { *addr = (uint64_t)header.sh_addr; *offset = (uint64_t)header.sh_offset; err = 0; @@ -326,16 +340,19 @@ static int syms__add_dso(struct syms *syms, struct map *map, const char *name) int i, type; void *tmp; - for (i = 0; i < syms->dso_sz; i++) { - if (!strcmp(syms->dsos[i].name, name)) { + for (i = 0; i < syms->dso_sz; i++) + { + if (!strcmp(syms->dsos[i].name, name)) + { dso = &syms->dsos[i]; break; } } - if (!dso) { + if (!dso) + { tmp = realloc(syms->dsos, (syms->dso_sz + 1) * - sizeof(*syms->dsos)); + sizeof(*syms->dsos)); if (!tmp) return -1; syms->dsos = (struct dso *)tmp; @@ -354,41 +371,55 @@ static int syms__add_dso(struct syms *syms, struct map *map, const char *name) dso->ranges[dso->range_sz].file_off = map->file_off; dso->range_sz++; type = get_elf_type(name); - if (type == ET_EXEC) { + if (type == ET_EXEC) + { dso->type = EXEC; - } else if (type == ET_DYN) { + } + else if (type == ET_DYN) + { dso->type = DYN; if (get_elf_text_scn_info(name, &dso->sh_addr, &dso->sh_offset) < 0) return -1; - } else if (is_perf_map(name)) { + } + else if (is_perf_map(name)) + { dso->type = PERF_MAP; - } else if (is_vdso(name)) { + } + else if (is_vdso(name)) + { dso->type = VDSO; - } else { + } + else + { dso->type = UNKNOWN; } return 0; } static struct dso *syms__find_dso(const struct syms *syms, unsigned long addr, - uint64_t *offset) + uint64_t *offset) { struct load_range *range; struct dso *dso; int i, j; - for (i = 0; i < syms->dso_sz; i++) { + for (i = 0; i < syms->dso_sz; i++) + { dso = &syms->dsos[i]; - for (j = 0; j < dso->range_sz; j++) { + for (j = 0; j < dso->range_sz; j++) + { range = &dso->ranges[j]; if (addr <= range->start || addr >= range->end) continue; - if (dso->type == DYN || dso->type == VDSO) { + if (dso->type == DYN || dso->type == VDSO) + { /* Offset within the mmap */ *offset = addr - range->start + range->file_off; /* Offset within the ELF for dyn symbol lookup */ *offset += dso->sh_addr - dso->sh_offset; - } else { + } + else + { *offset = addr; } @@ -405,7 +436,7 @@ static int dso__load_sym_table_from_perf_map(struct dso *dso) } static int dso__add_sym(struct dso *dso, const char *name, uint64_t start, - uint64_t size) + uint64_t size) { struct sym *sym; size_t new_cap; @@ -416,7 +447,8 @@ static int dso__add_sym(struct dso *dso, const char *name, uint64_t start, if (off < 0) return off; - if (dso->syms_sz + 1 > dso->syms_cap) { + if (dso->syms_sz + 1 > dso->syms_cap) + { new_cap = dso->syms_cap * 4 / 3; if (new_cap < 1024) new_cap = 1024; @@ -447,17 +479,19 @@ static int sym_cmp(const void *p1, const void *p2) } static int dso__add_syms(struct dso *dso, Elf *e, Elf_Scn *section, - size_t stridx, size_t symsize) + size_t stridx, size_t symsize) { Elf_Data *data = NULL; - while ((data = elf_getdata(section, data)) != 0) { + while ((data = elf_getdata(section, data)) != 0) + { size_t i, symcount = data->d_size / symsize; if (data->d_size % symsize) return -1; - for (i = 0; i < symcount; ++i) { + for (i = 0; i < symcount; ++i) + { const char *name; GElf_Sym sym; @@ -503,18 +537,19 @@ static int dso__load_sym_table_from_elf(struct dso *dso, int fd) if (!e) return -1; - while ((section = elf_nextscn(e, section)) != 0) { + while ((section = elf_nextscn(e, section)) != 0) + { GElf_Shdr header; if (!gelf_getshdr(section, &header)) continue; if (header.sh_type != SHT_SYMTAB && - header.sh_type != SHT_DYNSYM) + header.sh_type != SHT_DYNSYM) continue; if (dso__add_syms(dso, e, section, header.sh_link, - header.sh_entsize)) + header.sh_entsize)) goto err_out; } @@ -522,7 +557,7 @@ static int dso__load_sym_table_from_elf(struct dso *dso, int fd) for (i = 0; i < dso->syms_sz; i++) dso->syms[i].name = btf__name_by_offset(dso->btf, - (unsigned long)dso->syms[i].name); + (unsigned long)dso->syms[i].name); qsort(dso->syms, dso->syms_sz, sizeof(*dso->syms), sym_cmp); @@ -552,10 +587,11 @@ static int create_tmp_vdso_image(struct dso *dso) if (!f) return -1; - while (true) { + while (true) + { ret = fscanf(f, "%llx-%llx %*s %*x %*x:%*x %*u%[^\n]", - (long long*)&start_addr, (long long*)&end_addr, - buf); + (long long *)&start_addr, (long long *)&end_addr, + buf); if (ret == EOF && feof(f)) break; if (ret != 3) @@ -577,20 +613,22 @@ static int create_tmp_vdso_image(struct dso *dso) memcpy(image, (void *)start_addr, sz); snprintf(tmpfile, sizeof(tmpfile), - "/tmp/libbpf_%ld_vdso_image_XXXXXX", pid); + "/tmp/libbpf_%ld_vdso_image_XXXXXX", pid); fd = mkostemp(tmpfile, O_CLOEXEC); - if (fd < 0) { + if (fd < 0) + { fprintf(stderr, "failed to create temp file: %s\n", - strerror(errno)); + strerror(errno)); goto err_out; } /* Unlink the file to avoid leaking */ if (unlink(tmpfile) == -1) fprintf(stderr, "failed to unlink %s: %s\n", tmpfile, - strerror(errno)); - if (write(fd, image, sz) == -1) { + strerror(errno)); + if (write(fd, image, sz) == -1) + { fprintf(stderr, "failed to write to vDSO image: %s\n", - strerror(errno)); + strerror(errno)); close(fd); fd = -1; goto err_out; @@ -636,7 +674,8 @@ static struct sym *dso__find_sym(struct dso *dso, uint64_t offset) end = dso->syms_sz - 1; /* find largest sym_addr <= addr using binary search */ - while (start < end) { + while (start < end) + { mid = start + (end - start + 1) / 2; sym_addr = dso->syms[mid].start; @@ -647,16 +686,17 @@ static struct sym *dso__find_sym(struct dso *dso, uint64_t offset) } if (start == end && dso->syms[start].start <= offset && - offset < dso->syms[start].start + dso->syms[start].size) { + offset < dso->syms[start].start + dso->syms[start].size) + { (dso->syms[start]).offset = offset - dso->syms[start].start; return &dso->syms[start]; } return NULL; } -struct syms *syms__load_file(const char *fname) +struct syms *syms__load_file(const char *fname, pid_t tgid) { - char buf[PATH_MAX], perm[5]; + char buf[PATH_MAX], perm[5], path[PATH_MAX]; struct syms *syms; struct map map; char *name; @@ -671,17 +711,18 @@ struct syms *syms__load_file(const char *fname) if (!syms) goto err_out; - while (true) { + while (true) + { ret = fscanf(f, "%llx-%llx %4s %llx %llx:%llx %llu%[^\n]", - (long long*)&map.start_addr, - (long long*)&map.end_addr, perm, - (long long*)&map.file_off, - (long long*)&map.dev_major, - (long long*)&map.dev_minor, - (long long*)&map.inode, buf); + (long long *)&map.start_addr, + (long long *)&map.end_addr, perm, + (long long *)&map.file_off, + (long long *)&map.dev_major, + (long long *)&map.dev_minor, + (long long *)&map.inode, buf); if (ret == EOF && feof(f)) break; - if (ret != 8) /* perf-.map */ + if (ret != 8) /* perf-.map */ goto err_out; if (perm[2] != 'x') @@ -693,7 +734,8 @@ struct syms *syms__load_file(const char *fname) if (!is_file_backed(name)) continue; - if (syms__add_dso(syms, &map, name)) + sprintf(path, "/proc/%d/root/%s", tgid, name); + if (syms__add_dso(syms, &map, path)) goto err_out; } @@ -711,7 +753,7 @@ struct syms *syms__load_pid(pid_t tgid) char fname[128]; snprintf(fname, sizeof(fname), "/proc/%ld/maps", (long)tgid); - return syms__load_file(fname); + return syms__load_file(fname, tgid); } void syms__free(struct syms *syms) @@ -727,7 +769,7 @@ void syms__free(struct syms *syms) free(syms); } -const struct sym *syms__map_addr(const struct syms *syms, unsigned long addr) +struct sym *syms__map_addr(const struct syms *syms, unsigned long addr) { struct dso *dso; uint64_t offset; @@ -739,7 +781,7 @@ const struct sym *syms__map_addr(const struct syms *syms, unsigned long addr) } const struct sym *syms__map_addr_dso(const struct syms *syms, unsigned long addr, - char **dso_name, unsigned long *dso_offset) + char **dso_name, unsigned long *dso_offset) { struct dso *dso; uint64_t offset; @@ -754,8 +796,10 @@ const struct sym *syms__map_addr_dso(const struct syms *syms, unsigned long addr return dso__find_sym(dso, offset); } -struct syms_cache { - struct { +struct syms_cache +{ + struct + { struct syms *syms; int tgid; } *data; @@ -792,13 +836,14 @@ struct syms *syms_cache__get_syms(struct syms_cache *syms_cache, int tgid) void *tmp; int i; - for (i = 0; i < syms_cache->nr; i++) { + for (i = 0; i < syms_cache->nr; i++) + { if (syms_cache->data[i].tgid == tgid) return syms_cache->data[i].syms; } tmp = realloc(syms_cache->data, (syms_cache->nr + 1) * - sizeof(*syms_cache->data)); + sizeof(*syms_cache->data)); if (!tmp) return NULL; syms_cache->data = (typeof(syms_cache->data))tmp; @@ -807,19 +852,20 @@ struct syms *syms_cache__get_syms(struct syms_cache *syms_cache, int tgid) return syms_cache->data[syms_cache->nr++].syms; } -struct partitions { +struct partitions +{ struct partition *items; int sz; }; static int partitions__add_partition(struct partitions *partitions, - const char *name, unsigned int dev) + const char *name, unsigned int dev) { struct partition *partition; void *tmp; tmp = realloc(partitions->items, (partitions->sz + 1) * - sizeof(*partitions->items)); + sizeof(*partitions->items)); if (!tmp) return -1; partitions->items = (struct partition *)tmp; @@ -848,15 +894,16 @@ struct partitions *partitions__load(void) if (!partitions) goto err_out; - while (fgets(buf, sizeof(buf), f) != NULL) { + while (fgets(buf, sizeof(buf), f) != NULL) + { /* skip heading */ if (buf[0] != ' ' || buf[0] == '\n') continue; if (sscanf(buf, "%u %u %llu %s", &devmaj, &devmin, &nop, - part_name) != 4) + part_name) != 4) goto err_out; if (partitions__add_partition(partitions, part_name, - MKDEV(devmaj, devmin))) + MKDEV(devmaj, devmin))) goto err_out; } @@ -887,7 +934,8 @@ partitions__get_by_dev(const struct partitions *partitions, unsigned int dev) { int i; - for (i = 0; i < partitions->sz; i++) { + for (i = 0; i < partitions->sz; i++) + { if (partitions->items[i].dev == dev) return &partitions->items[i]; } @@ -900,7 +948,8 @@ partitions__get_by_name(const struct partitions *partitions, const char *name) { int i; - for (i = 0; i < partitions->sz; i++) { + for (i = 0; i < partitions->sz; i++) + { if (strcmp(partitions->items[i].name, name) == 0) return &partitions->items[i]; } @@ -932,7 +981,8 @@ void print_log2_hist(unsigned int *vals, int vals_size, const char *val_type) unsigned long long low, high; int stars, width, i; - for (i = 0; i < vals_size; i++) { + for (i = 0; i < vals_size; i++) + { val = vals[i]; if (val > 0) idx_max = i; @@ -944,14 +994,15 @@ void print_log2_hist(unsigned int *vals, int vals_size, const char *val_type) return; printf("%*s%-*s : count distribution\n", idx_max <= 32 ? 5 : 15, "", - idx_max <= 32 ? 19 : 29, val_type); + idx_max <= 32 ? 19 : 29, val_type); if (idx_max <= 32) stars = stars_max; else stars = stars_max / 2; - for (i = 0; i <= idx_max; i++) { + for (i = 0; i <= idx_max; i++) + { low = (1ULL << (i + 1)) >> 1; high = (1ULL << (i + 1)) - 1; if (low == high) @@ -965,14 +1016,16 @@ void print_log2_hist(unsigned int *vals, int vals_size, const char *val_type) } void print_linear_hist(unsigned int *vals, int vals_size, unsigned int base, - unsigned int step, const char *val_type) + unsigned int step, const char *val_type) { int i, stars_max = 40, idx_min = -1, idx_max = -1; unsigned int val, val_max = 0; - for (i = 0; i < vals_size; i++) { + for (i = 0; i < vals_size; i++) + { val = vals[i]; - if (val > 0) { + if (val > 0) + { idx_max = i; if (idx_min < 0) idx_min = i; @@ -985,7 +1038,8 @@ void print_linear_hist(unsigned int *vals, int vals_size, unsigned int base, return; printf(" %-13s : count distribution\n", val_type); - for (i = idx_min; i <= idx_max; i++) { + for (i = idx_min; i <= idx_max; i++) + { val = vals[i]; if (!val) continue; @@ -1013,10 +1067,12 @@ bool is_kernel_module(const char *name) if (!f) return false; - while (fgets(buf, sizeof(buf), f) != NULL) { + while (fgets(buf, sizeof(buf), f) != NULL) + { if (sscanf(buf, "%s %*s\n", buf) != 1) break; - if (!strcmp(buf, name)) { + if (!strcmp(buf, name)) + { found = true; break; } @@ -1031,18 +1087,17 @@ static bool fentry_try_attach(int id) int prog_fd, attach_fd; char error[4096]; struct bpf_insn insns[] = { - { .code = BPF_ALU64 | BPF_MOV | BPF_K, .dst_reg = BPF_REG_0, .imm = 0 }, - { .code = BPF_JMP | BPF_EXIT }, + {.code = BPF_ALU64 | BPF_MOV | BPF_K, .dst_reg = BPF_REG_0, .imm = 0}, + {.code = BPF_JMP | BPF_EXIT}, }; LIBBPF_OPTS(bpf_prog_load_opts, opts, - .expected_attach_type = BPF_TRACE_FENTRY, - .attach_btf_id = (__u32)id, - .log_size = sizeof(error), - .log_buf = error, - ); + .expected_attach_type = BPF_TRACE_FENTRY, + .attach_btf_id = (__u32)id, + .log_size = sizeof(error), + .log_buf = error, ); prog_fd = bpf_prog_load(BPF_PROG_TYPE_TRACING, "test", "GPL", insns, - sizeof(insns) / sizeof(struct bpf_insn), &opts); + sizeof(insns) / sizeof(struct bpf_insn), &opts); if (prog_fd < 0) return false; @@ -1066,7 +1121,8 @@ bool fentry_can_attach(const char *name, const char *mod) btf = vmlinux_btf; - if (mod) { + if (mod) + { module_btf = btf__load_module_btf(mod, vmlinux_btf); err = libbpf_get_error(module_btf); if (!err) @@ -1100,8 +1156,7 @@ static const char *tracefs_path(void) static const char *tracefs_available_filter_functions(void) { - return use_debugfs() ? DEBUGFS"/available_filter_functions" : - TRACEFS"/available_filter_functions"; + return use_debugfs() ? DEBUGFS "/available_filter_functions" : TRACEFS "/available_filter_functions"; } bool kprobe_exists(const char *name) @@ -1115,15 +1170,18 @@ bool kprobe_exists(const char *name) if (!f) goto avail_filter; - while (true) { + while (true) + { ret = fscanf(f, "%s %s%*[^\n]\n", addr_range, sym_name); if (ret == EOF && feof(f)) break; - if (ret != 2) { + if (ret != 2) + { fprintf(stderr, "failed to read symbol from kprobe blacklist\n"); break; } - if (!strcmp(name, sym_name)) { + if (!strcmp(name, sym_name)) + { fclose(f); return false; } @@ -1135,15 +1193,18 @@ bool kprobe_exists(const char *name) if (!f) goto slow_path; - while (true) { + while (true) + { ret = fscanf(f, "%s%*[^\n]\n", sym_name); if (ret == EOF && feof(f)) break; - if (ret != 1) { + if (ret != 1) + { fprintf(stderr, "failed to read symbol from available_filter_functions\n"); break; } - if (!strcmp(name, sym_name)) { + if (!strcmp(name, sym_name)) + { fclose(f); return true; } @@ -1157,15 +1218,18 @@ bool kprobe_exists(const char *name) if (!f) return false; - while (true) { + while (true) + { ret = fscanf(f, "%*x %*c %s%*[^\n]\n", sym_name); if (ret == EOF && feof(f)) break; - if (ret != 1) { + if (ret != 1) + { fprintf(stderr, "failed to read symbol from kallsyms\n"); break; } - if (!strcmp(name, sym_name)) { + if (!strcmp(name, sym_name)) + { fclose(f); return true; } @@ -1203,7 +1267,8 @@ bool module_btf_exists(const char *mod) { char sysfs_mod[80]; - if (mod) { + if (mod) + { snprintf(sysfs_mod, sizeof(sysfs_mod), "/sys/kernel/btf/%s", mod); if (!access(sysfs_mod, R_OK)) return true; @@ -1215,8 +1280,8 @@ bool probe_tp_btf(const char *name) { LIBBPF_OPTS(bpf_prog_load_opts, opts, .expected_attach_type = BPF_TRACE_RAW_TP); struct bpf_insn insns[] = { - { .code = BPF_ALU64 | BPF_MOV | BPF_K, .dst_reg = BPF_REG_0, .imm = 0 }, - { .code = BPF_JMP | BPF_EXIT }, + {.code = BPF_ALU64 | BPF_MOV | BPF_K, .dst_reg = BPF_REG_0, .imm = 0}, + {.code = BPF_JMP | BPF_EXIT}, }; int fd, insn_cnt = sizeof(insns) / sizeof(struct bpf_insn); @@ -1238,3 +1303,21 @@ bool probe_ringbuf() close(map_fd); return true; } + +const struct ksym *ksyms__find_symbol(const struct ksyms *ksyms, + const char *name) +{ + for (int i = 0; i < ksyms->syms_sz; i++) + { + int j; + for (j = 0; name[j] && name[j] == ksyms->syms[i].name[j]; j++) + ; + if (!name[j]) + return &ksyms->syms[i]; + } + return NULL; +} + +struct ksyms *ksyms; +struct syms_cache *syms_cache; +struct syms *syms; \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/src/uprobe_helpers.cpp b/eBPF_Supermarket/Stack_Analyser/src/uprobe.cpp similarity index 100% rename from eBPF_Supermarket/Stack_Analyser/src/uprobe_helpers.cpp rename to eBPF_Supermarket/Stack_Analyser/src/uprobe.cpp diff --git a/eBPF_Supermarket/Stack_Analyser/testdir/uprobe_malloc.c b/eBPF_Supermarket/Stack_Analyser/testdir/uprobe_malloc.c new file mode 100644 index 000000000..182a29555 --- /dev/null +++ b/eBPF_Supermarket/Stack_Analyser/testdir/uprobe_malloc.c @@ -0,0 +1,69 @@ +#include +#include +#include +#include + +#define BUFFER_SIZE 1024 + +int main() { + // 打印当前进程号 + printf("Process ID: %d\n", getpid()); + + // 申请内存 + char *buffer = (char *)malloc(BUFFER_SIZE * sizeof(char)); + if (buffer == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + return 1; + } + + // 文件描述符 + int fd = open("data.txt", O_CREAT | O_WRONLY, 0644); + if (fd == -1) { + fprintf(stderr, "Failed to open file\n"); + free(buffer); + return 1; + } + + // 写数据 + printf("Writing data. Process ID: %d\n", getpid()); + write(fd, "Hello, World!\n", 14); + + // 关闭文件描述符 + close(fd); + + // 打开文件以读取数据 + fd = open("data.txt", O_RDONLY); + if (fd == -1) { + fprintf(stderr, "Failed to open file for reading\n"); + free(buffer); + return 1; + } + + // 读取数据 + printf("Reading data. Process ID: %d\n", getpid()); + ssize_t bytesRead = read(fd, buffer, BUFFER_SIZE); + if (bytesRead == -1) { + fprintf(stderr, "Failed to read data from file\n"); + free(buffer); + close(fd); + return 1; + } + + // 输出读取的数据 + printf("Data read: %s\n", buffer); + + // 关闭文件描述符 + close(fd); + + // 持续打印进程号 + while(1) { + printf("Still running. Process ID: %d\n", getpid()); + sleep(1); // 暂停一秒钟 + } + + // 释放内存 + free(buffer); + printf("Memory freed. Process ID: %d\n", getpid()); + + return 0; +} diff --git a/eBPF_Supermarket/Stack_Analyser/testdir/usdt_pthread.c b/eBPF_Supermarket/Stack_Analyser/testdir/usdt_pthread.c index 6c9729b8d..6714faf65 100644 --- a/eBPF_Supermarket/Stack_Analyser/testdir/usdt_pthread.c +++ b/eBPF_Supermarket/Stack_Analyser/testdir/usdt_pthread.c @@ -1,45 +1,33 @@ +#define _GNU_SOURCE #include #include #include #include +#include // 线程函数 -void *thread_function(void *arg) { - while (1) { +void *thread_function(void *arg) +{ + while (1) + { // 打印当前线程的pid - printf("Thread PID: %d\n", getpid()); + printf("Thread ID: %d\n", gettid()); // 等待一秒 sleep(1); } - pthread_exit(NULL); -} +}; -// 创建线程的函数 -void create_thread() { - pthread_t thread; - int rc; +int main() +{ - // 创建线程 - rc = pthread_create(&thread, NULL, thread_function, NULL); - if (rc) { - fprintf(stderr, "Error: pthread_create() failed with code %d\n", rc); + pthread_t thread; + if (pthread_create(&thread, NULL, thread_function, NULL)) + { + fprintf(stderr, "Error: pthread_create() failed\n"); exit(EXIT_FAILURE); } -} - -int main() { - // 打印主进程的pid - printf("Main process PID: %d\n", getpid()); - - // 调用函数创建线程 - create_thread(); - // 主进程死循环打印pid - while (1) { - printf("Main process PID: %d\n", getpid()); - // 等待一秒 - sleep(1); - } + thread_function(NULL); return 0; -} \ No newline at end of file +}; \ No newline at end of file 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/README.md b/eBPF_Supermarket/eBPF_Performance_Analysis/README.md new file mode 100644 index 000000000..5a3944280 --- /dev/null +++ b/eBPF_Supermarket/eBPF_Performance_Analysis/README.md @@ -0,0 +1,54 @@ +# eBPF多维度分析 +## 一、简介 + +​ **eBPF(扩展伯克利数据包过滤器)** 是一项革命性技术,允许在内核态执行用户定义的程序。这些程序可以在内核中安全高效地运行,而无需更改内核源代码,从而实现了对系统行为的深度观察和实时调整。eBPF在网络、安全、监控、性能调优等领域有着广泛的应用。通过eBPF实现高效的数据包过滤和流量分析,优化网络性能。例如,Cilium利用eBPF技术提供可伸缩的网络和安全性,特别适用于Kubernetes和其他云原生环境。通过内核态的性能数据收集和分析,发现系统瓶颈并进行优化。eBPF可以用于捕捉各种性能指标,如CPU使用率、内存使用情况等,从而提供深度的性能分析。eBPF可以无缝地插入内核代码路径,提供详细的性能监测功能。它能够实时收集详细的性能数据,帮助系统管理员和开发者识别并解决性能瓶颈。eBPF可以用于监测容器的资源使用情况,包括CPU、内存、网络等方面的数据。通过与Prometheus等监控工具集成,eBPF可以提供精细的容器监控数据,帮助运维人员更好地管理和优化容器化应用。 + +​ 尽管eBPF在网络、安全、监控、性能调优等领域展现了巨大的潜力和广泛的应用,但目前业界对eBPF本身的性能分析和优化研究仍然不足。因此,本次研究的目的在于通过系统化的性能评估和对比分析,深入探索eBPF在不同使用场景下的表现,特别是对比在不同负载的情境下eBPF中不同类型的map、不同挂载点的性能差异、不同内核版本的使用差异。通过编写和运行各种测试程序,我们将全面分析eBPF在实际操作中的效率和性能瓶颈。最终,基于这些数据,提出针对不同应用场景的最佳实践指导,帮助开发者和运维人员更高效地利用eBPF技术,从而推动eBPF在业界的深入应用和发展。 + +## 二、测试目的 + +​ 我的研究旨在通过系统化的性能评估和对比分析,全面评估和优化eBPF(扩展伯克利数据包过滤器)的性能。具体而言,我们希望深入探索eBPF在各种使用场景下的性能表现,包括比较不同类型的映射(map)、分析不同挂载点的性能差异,以及评估不同内核版本的影响。通过编写和执行一系列测试程序,我们的目标是深入了解eBPF的操作效率,并识别潜在的性能瓶颈。最终,我们将根据研究结果提出针对特定应用场景的最佳实践指南,推动eBPF技术在各行业的应用和发展。 + +## 三、测试方案: +[理论分析.md](./docs/Map理论分析.md) + +[测试方案.md](./docs/eBPF性能测试方案.md) + +## 四、工具使用说明: + +### 1.环境准备: + +1.1ebpf运行环境: + +```shell +#在eBPF_Performance_Analysis/目录下执行指令: +make deps +``` + +1.2python环境: + +```shell +#下载python3 +sudo apt install python3 +#检查是否下载成功: +python3 --version +#下载pandas库 +sudo pip3 install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple +#下载matplotlib库 +pip3 install matplotlib +``` + +2.运行: + +```shell +#在eBPF_Performance_Analysis/目录下运行shell脚本: +#此脚本用来比较不同Map类型在时间层面进行增删改查操作的差异 +sudo bash run_ebpf_and_process.sh +``` +3.结果: + +```shell +#运行结束后,程序会生成python工具分析后的图像文件 +``` + + diff --git "a/eBPF_Supermarket/eBPF_Performance_Analysis/docs/Map\347\220\206\350\256\272\345\210\206\346\236\220.md" "b/eBPF_Supermarket/eBPF_Performance_Analysis/docs/Map\347\220\206\350\256\272\345\210\206\346\236\220.md" new file mode 100644 index 000000000..819a67e62 --- /dev/null +++ "b/eBPF_Supermarket/eBPF_Performance_Analysis/docs/Map\347\220\206\350\256\272\345\210\206\346\236\220.md" @@ -0,0 +1,748 @@ +# Map理论分析 + +在测试之前,我们需要先对需要测试的Map类型进行一个详细的理论分析,通过对Map的理论分析,来得到一个有利的理论指导,并通过这个指导来设计测试方案并且验证测试结果的正确性。本次分析是针对 + +## 一、特征与源码分析 + +### **1.BPF_MAP_TYPE_HASH** + +- 在eBPF中,HashMap实际上是一个哈希表,用于将键映射到值。它使用哈希函数来计算键的哈希值,并将其映射到一个存储桶(bucket)中。 +- 哈希表内部由一个数组(buckets)和链表(或者是红黑树)组成。数组中的每个元素是一个链表头或树的根节点,用于处理哈希冲突。 +- HashMap在eBPF中可以动态调整大小,这意味着它可以根据需要动态增长或缩小。这一点是通过在哈希表达到负载因子阈值时重新分配更大的存储空间来实现的。 +- 在eBPF的内核代码中,HashMap的实现涉及到哈希函数的选择和哈希冲突的处理。 +- 适合在运行时需要动态添加、删除键值对的场景。 +- 适合快速查找特定键对应的值,复杂度为 O(1)。 + +接下来分析Hashmap的重要操作:查找和删除操作。 + +**1.1Hashmap的查找元素源码:(/kernel/bpf/hashtab.c)** + +```c +/* + * 函数: __htab_map_lookup_elem + * ---------------------------- + * 此函数用于在 eBPF 程序或系统调用上下文中查找 HashTable Map 中的元素。 + * 根据提供的键查找并返回对应的元素。 + * + * 参数: + * - map: 指向 BPF map(bpf_map)基本结构的指针。 + * - key: 用于在 HashTable Map 中定位元素的键的指针。 + * + * 返回: + * - 成功时返回指向查找到的元素的指针;如果未找到则返回 NULL。 + */ + +static void *__htab_map_lookup_elem(struct bpf_map *map, void *key) +{ + struct bpf_htab *htab = container_of(map, struct bpf_htab, map); + struct hlist_nulls_head *head; + struct htab_elem *l; + u32 hash, key_size; + + // 检查是否在 RCU 读取锁定期间执行 + WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() && + !rcu_read_lock_bh_held()); + + key_size = map->key_size; // 获取键的大小 + + // 计算键的哈希值 + hash = htab_map_hash(key, key_size, htab->hashrnd); + + // 根据哈希值选择对应的桶 + head = select_bucket(htab, hash); + + // 在选定的桶中查找元素 + l = lookup_nulls_elem_raw(head, hash, key, key_size, htab->n_buckets); + + return l; // 返回查找到的元素指针,如果未找到则返回 NULL +} +``` + +```c +static inline u32 htab_map_hash(const void *key, u32 key_len, u32 hashrnd) +{ + // 如果键的长度是 4 的倍数,使用 jhash2 计算哈希值 + if (likely(key_len % 4 == 0)) + return jhash2(key, key_len / 4, hashrnd); + + // 否则,使用 jhash 计算哈希值 + return jhash(key, key_len, hashrnd); +} +``` + +```c +/* + * 函数: jhash + * ------------ + * 哈希一个任意的键序列。 + * 参数: + * - key: 作为键的字节序列的指针。 + * - length: 键的长度(以字节为单位)。 + * - initval: 先前的哈希值,或者一个任意的值作为初始哈希值。 + * + * 返回: + * - 键的哈希值。结果依赖于系统的字节序。 + *这段代码通过对输入的字节序列进行迭代处理,按照特定的算法(包括混合操作和最终化操作)计算出一个哈希值,用 + *于对任意数据进行快速的哈希映射。 + */ +static inline u32 jhash(const void *key, u32 length, u32 initval) +{ + u32 a, b, c; + const u8 *k = key; + + /* 设置内部状态 */ + a = b = c = JHASH_INITVAL + length + initval; + + /* 处理除最后一个块外的所有块:影响(a, b, c)的32位 */ + while (length > 12) { + a += __get_unaligned_cpu32(k); // 获取未对齐的32位整数 + b += __get_unaligned_cpu32(k + 4); // 获取未对齐的32位整数 + c += __get_unaligned_cpu32(k + 8); // 获取未对齐的32位整数 + __jhash_mix(a, b, c); // 混合操作,影响哈希值 + length -= 12; // 减去处理的字节数 + k += 12; // 指针移动到下一个块 + } + /* 最后一个块:影响(c)的所有32位 */ + /* 所有 case 语句都会顺序执行 */ + switch (length) { + case 12: c += (u32)k[11]<<24; + case 11: c += (u32)k[10]<<16; + case 10: c += (u32)k[9]<<8; + case 9: c += k[8]; + case 8: b += (u32)k[7]<<24; + case 7: b += (u32)k[6]<<16; + case 6: b += (u32)k[5]<<8; + case 5: b += k[4]; + case 4: a += (u32)k[3]<<24; + case 3: a += (u32)k[2]<<16; + case 2: a += (u32)k[1]<<8; + case 1: a += k[0]; + __jhash_final(a, b, c); // 最终混合和收尾操作 + case 0: /* 没有剩余的字节需要处理 */ + break; + } + + return c; // 返回计算得到的哈希值 +} +``` + +**1.2Hashmap的删除元素源码:(/kernel/bpf/hashtab.c)** + +```c +static long htab_lru_map_delete_elem(struct bpf_map *map, void *key) +{ + // 将 map 转换为包含它的 htab 结构体 + struct bpf_htab *htab = container_of(map, struct bpf_htab, map); + + // 定义一个指向 hlist_nulls_head 结构体的指针 head + struct hlist_nulls_head *head; + + // 定义一个指向 bucket 结构体的指针 b + struct bucket *b; + + // 定义一个指向 htab_elem 结构体的指针 l + struct htab_elem *l; + + // 定义一个用于保存标志位的变量 flags + unsigned long flags; + + // 定义一个无符号 32 位整数变量 hash 和 key_size + u32 hash, key_size; + + // 定义一个整数变量 ret 用于保存返回值 + int ret; + + // 检查是否持有 RCU 读锁,如果没有则发出警告 + WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() && + !rcu_read_lock_bh_held()); + + // 获取 map 的 key_size + key_size = map->key_size; + + // 计算 key 的 hash 值 + hash = htab_map_hash(key, key_size, htab->hashrnd); + + // 根据 hash 值选择对应的桶 + b = __select_bucket(htab, hash); + + // 获取桶的头部指针 + head = &b->head; + + // 尝试锁定桶,返回值保存在 ret 中 + ret = htab_lock_bucket(htab, b, hash, &flags); + + // 如果锁定失败,则直接返回错误码 + if (ret) + return ret; + + // 在桶中查找与key对应的元素 + l = lookup_elem_raw(head, hash, key, key_size); + + // 如果找到了元素,则从链表中删除该元素 + if (l) + hlist_nulls_del_rcu(&l->hash_node); + else + // 如果没有找到元素,则设置返回值为 -ENOENT + ret = -ENOENT; + + // 解锁桶 + htab_unlock_bucket(htab, b, hash, flags); + + // 如果找到了元素,则将其推送到 LRU free 列表 + if (l) + htab_lru_push_free(htab, l); + + // 返回结果 + return ret; +} +``` + +```c +static inline void __hlist_nulls_del(struct hlist_nulls_node *n) +{ + // 获取当前节点的下一个节点 + struct hlist_nulls_node *next = n->next; + + // 获取指向当前节点前一个节点的指针 + struct hlist_nulls_node **pprev = n->pprev; + + // 使用 WRITE_ONCE 宏将当前节点的前一个节点的 next 指针指向当前节点的下一个节点 + WRITE_ONCE(*pprev, next); + + // 如果当前节点的下一个节点不是一个空节点 + if (!is_a_nulls(next)) + // 使用 WRITE_ONCE 宏将下一个节点的前一个节点的指针指向当前节点的前一个节点 + WRITE_ONCE(next->pprev, pprev); +} +``` + +### **2.BPF_MAP_TYPE_ARRAY** + +- ArrayMap是一个数组,其中每个元素存储一个键值对。这种设计使得ArrayMap的存储顺序和插入顺序完全一致。 +- ArrayMap在创建时需要指定最大大小,因为它的大小在运行时是不可变的。 +- ArrayMap的实现比较简单,只需要一个数组和一个计数器。它的插入和查找操作都非常高效,因为它可以直接通过数组索引访问元素。 +- 适合在运行时知道最大键值对数量的场景。 +- 适合需要**按照插入顺序进行遍历**或处理的场景,因为它的元素存储顺序与插入顺序完全一致。 + +**2.1Arraymap的查找元素源码:(/kernel/bpf/arraymap.c)** + +```c +static void *array_map_lookup_elem(struct bpf_map *map, void *key) +{ + /* 将基本的 BPF map 指针转换为特定的 BPF array map 结构 */ + struct bpf_array *array = container_of(map, struct bpf_array, map); + + /* 解引用键指针以获取索引 */ + u32 index = *(u32 *)key; + + /* 检查索引是否超出数组最大条目数 */ + if (unlikely(index >= array->map.max_entries)) + return NULL; + + /* 计算并返回元素地址 */ + return array->value + (u64)array->elem_size * (index & array->index_mask); +} +``` + +**2.2Arraymap的插入元素源码:(/kernel/bpf/arraymap.c)** + +```c +static long array_map_update_elem(struct bpf_map *map, void *key, void *value, + u64 map_flags) +{ + struct bpf_array *array = container_of(map, struct bpf_array, map); + u32 index = *(u32 *)key; // 解引用键指针以获取索引 + char *val; + // 检查是否存在未知标志位 + if (unlikely((map_flags & ~BPF_F_LOCK) > BPF_EXIST)) + return -EINVAL; + // 检查索引是否超出最大条目数限制 + if (unlikely(index >= array->map.max_entries)) + return -E2BIG; + // 检查是否禁止不存在元素的插入 + if (unlikely(map_flags & BPF_NOEXIST)) + return -EEXIST; + // 如果要求使用锁定,并且未定义自旋锁字段,则返回错误 + if (unlikely((map_flags & BPF_F_LOCK) && + !btf_record_has_field(map->record, BPF_SPIN_LOCK))) + return -EINVAL; + // 根据数组类型进行操作分支 + if (array->map.map_type == BPF_MAP_TYPE_PERCPU_ARRAY) { + // 对于 BPF_MAP_TYPE_PERCPU_ARRAY 类型的数组 + val = this_cpu_ptr(array->pptrs[index & array->index_mask]); + copy_map_value(map, val, value); // 复制值到本地 CPU 的指定位置 + bpf_obj_free_fields(array->map.record, val); // 释放对象字段 + } else { + // 对于其他类型的数组 + val = array->value + + (u64)array->elem_size * (index & array->index_mask); + if (map_flags & BPF_F_LOCK) + copy_map_value_locked(map, val, value, false); // 锁定状态下复制值 + else + copy_map_value(map, val, value); // 非锁定状态下复制值 + bpf_obj_free_fields(array->map.record, val); // 释放对象字段 + } + return 0; // 操作成功返回 0 +} +``` + +**2.3AraayMap删除元素:(/kernel/bpf/arraymap.c)** + +```c +/* + * 此函数用于在 eBPF 程序或系统调用上下文中删除 ArrayMap 中的元素。 + * 根据提供的键删除 ArrayMap 中的元素,并释放相关资源。 + */ + +static long fd_array_map_delete_elem(struct bpf_map *map, void *key) +{ + struct bpf_array *array = container_of(map, struct bpf_array, map); + void *old_ptr; + u32 index = *(u32 *)key; + + // 检查索引是否超出最大条目数限制 + if (index >= array->map.max_entries) + return -E2BIG; + + // 如果 map_poke_run 操作存在,则使用 mutex 锁定并执行相关操作 + //当执行特定的 map 操作时(如更新或删除元素),如果该操作涉及到需要额外的处理或者通知机制,就会调用 //map_poke_run 函数来完成这些额外的任务。 + if (map->ops->map_poke_run) { + mutex_lock(&array->aux->poke_mutex); + old_ptr = xchg(array->ptrs + index, NULL); + map->ops->map_poke_run(map, index, old_ptr, NULL); + mutex_unlock(&array->aux->poke_mutex); + } else { + // 否则直接执行元素交换操作 + old_ptr = xchg(array->ptrs + index, NULL); + } + + // 如果成功删除元素,则释放相关资源 + if (old_ptr) { + //在 eBPF 中,文件描述符通常用于操作与内核资源相关的对象,如 bpf_map 中的数据结构。当不再需要 + //这些资源时,就需要调用 map_fd_put_ptr 函数指针所指向的函数,来释放相关的资源或执行清理操作, + //以避免资源泄漏或者内存泄漏问题。 + map->ops->map_fd_put_ptr(old_ptr); + return 0; + } else { + // 如果元素不存在,则返回错误码 -ENOENT + return -ENOENT; + } +} +``` + +### 3.BPF_MAP_TYPE_PERCPU_HASH + +- **Per-CPU Hash Map** 是一种哈希表,其中每个 CPU 都有自己**独立的哈希表副本**。这种设计使得在多核环境下访问哈希表时可以**避免锁争用**,从而提高并发性能。 +- 每个哈希表副本在创建时并不需要指定最大大小,因此其大小可以根据需要动态增长。 +- **Per-CPU Hash Map** 的实现相对复杂,因为它需要管理多个哈希表副本,并在访问时根据当前 CPU 进行相应的查找或更新操作。 +- 适合**需要高并发读写**的场景,尤其是在多核系统中,因为每个 CPU 的操作都是独立的,不会因为其他 CPU 的操作而产生竞争。 +- 适合**按需动态调整大小**的场景,因为它不像数组那样需要预定义大小。 + +**3.1per-cpu hash的查找操作源码:(/kernel/bpf/hashtab.c)** + +```c +static void *htab_percpu_map_lookup_elem(struct bpf_map *map, void *key) +{ + // 通过键在哈希表中查找对应的哈希表元素,此方法和普通的hash表查找操作相同 + struct htab_elem *l = __htab_map_lookup_elem(map, key); + + // 如果找到元素 l,此时的l若不为空,则l代表查到了每个cpu的数据副本 + if (l) + // 获取当前 CPU 上对应的数据副本并返回 + return this_cpu_ptr(htab_elem_get_ptr(l, map->key_size)); + else + // 如果没有找到元素,返回 NULL + return NULL; +} +``` + +接下来查看htab_elem_get_ptr()函数: + +```c +static inline void __percpu *htab_elem_get_ptr(struct htab_elem *l, u32 key_size) +{ + // 返回指向 per-CPU 数据的指针 + // 计算元素 l 中数据的存储位置,跳过键(key)后的空间,获取 per-CPU 数据的指针 + return *(void __percpu **)(l->key + key_size); +} +``` + +该函数的作用是从哈希表元素 `l` 中获取指向 per-CPU 数据的指针。哈希表元素 `l` 中存储了键值对,而键的大小由 `key_size` 决定,`l->key` 是指向键的指针,因此 `l->key + key_size` 表示跳过键后,定位到 per-CPU 数据存储的起始位置。`l->key + key_size`: 从 `l` 结构体中的 `key` 开始,偏移 `key_size` 个字节后到达数据区域。`*(void __percpu **)(...)`: 将偏移后的地址解释为一个 `void __percpu *` 类型的指针,并返回该指针。 + +`htab_elem_get_ptr()` 返回的是一个 **per-CPU 数据区域的基地址**,即包含了所有 CPU 的数据。 + +`this_cpu_ptr()` 使用当前 CPU 的 ID,从这个基地址中 **获取当前 CPU 的数据副本**。 + +**3.2per-cpu hash的创建源码:(/kernel/bpf/memalloc.c)** + +```c +/* + * 初始化 BPF 内存分配器 + * + * @ma: 指向 bpf_mem_alloc 结构的指针,负责管理内存分配 + * @size: 每个内存块的大小。如果 size 不为 0,则表示所有分配的元素具有相同的大小 + * @percpu: 如果为 true,则为每个 CPU 分配独立的内存 + * + * 返回值: 0 表示成功,负数表示分配失败或参数错误 + */ +int bpf_mem_alloc_init(struct bpf_mem_alloc *ma, int size, bool percpu) +{ + // 内存池支持的缓存大小,NUM_CACHES 是缓存数量 + static u16 sizes[NUM_CACHES] = {96, 192, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096}; + + struct bpf_mem_caches *cc, __percpu *pcc; + struct bpf_mem_cache *c, __percpu *pc; + struct obj_cgroup *objcg = NULL; + int cpu, i, unit_size, percpu_size = 0; + + // 标记是否为 per-cpu 分配 + ma->percpu = percpu; + + // 如果 size 不为 0,表示所有元素大小相同,执行此分支 + if (size) { + // 为每个 CPU 分配一个 bpf_mem_cache 结构的 per-cpu 存储空间 + pc = __alloc_percpu_gfp(sizeof(*pc), 8, GFP_KERNEL); + if (!pc) + return -ENOMEM; // 分配失败,返回内存不足错误 + + // 如果是 per-cpu 模式,计算 per-cpu 内存大小,包含链表节点和指针 + if (percpu) + percpu_size = LLIST_NODE_SZ + sizeof(void *); + else + size += LLIST_NODE_SZ; // 非 per-cpu 模式,只需为链表节点预留空间 + + unit_size = size; + +#ifdef CONFIG_MEMCG_KMEM + // 如果内存控制组(memory cgroup)启用,从当前任务获取对象控制组 + if (memcg_bpf_enabled()) + objcg = get_obj_cgroup_from_current(); +#endif + + // 为系统中所有可能的 CPU 分配内存,并初始化相关数据结构 + for_each_possible_cpu(cpu) { + c = per_cpu_ptr(pc, cpu); // 获取当前 CPU 的内存缓存指针 + c->unit_size = unit_size; // 设置内存块大小 + c->objcg = objcg; // 关联对象控制组 + c->percpu_size = percpu_size; // 设置 per-cpu 内存大小 + c->tgt = c; // 目标指针指向自身 + + // 初始化缓存的 refill 工作,并填充缓存 + init_refill_work(c); + prefill_mem_cache(c, cpu); + } + + ma->cache = pc; // 保存分配的 per-cpu 缓存 + return 0; // 分配成功,返回 0 + } + + // 如果 size == 0 并且 percpu 为 true,这是无效的组合 + if (WARN_ON_ONCE(percpu)) + return -EINVAL; // 返回无效参数错误 + + // 为每个 CPU 分配多个大小的缓存(对应 sizes 数组) + pcc = __alloc_percpu_gfp(sizeof(*cc), 8, GFP_KERNEL); + if (!pcc) + return -ENOMEM; // 分配失败,返回内存不足错误 + +#ifdef CONFIG_MEMCG_KMEM + // 获取当前任务的对象控制组 + objcg = get_obj_cgroup_from_current(); +#endif + + // 为所有可能的 CPU 分配缓存,并初始化 + for_each_possible_cpu(cpu) { + cc = per_cpu_ptr(pcc, cpu); // 获取当前 CPU 的内存缓存指针 + for (i = 0; i < NUM_CACHES; i++) { + c = &cc->cache[i]; // 获取每个缓存的指针 + c->unit_size = sizes[i]; // 设置缓存的大小 + c->objcg = objcg; // 关联对象控制组 + c->tgt = c; // 目标指针指向自身 + + // 初始化缓存的 refill 工作,并填充缓存 + init_refill_work(c); + prefill_mem_cache(c, cpu); + } + } + + ma->caches = pcc; // 保存分配的 per-cpu 缓存 + return 0; // 分配成功,返回 0 +} +``` + +### 4.BPF_MAP_TYPE_PERCPU_ARRAY + +- **Per-CPU Array Map** 是一种数组,每个 CPU 都有自己独立的数组副本。每个数组中的元素存储一个键值对,且所有 CPU 的数组结构是相同的。 +- 与普通的 Array Map 类似,**Per-CPU Array Map** 也需要在创建时指定最大大小,因为数组的大小在运行时是不可变的。 +- **Per-CPU Array Map** 的实现相对简单,可以通过数组索引直接访问元素,因此插入和查找操作都非常高效。 +- 适合**需要高并发读写且按索引访问**的场景,尤其是在多核系统中,因为每个 CPU 的数组操作都是独立的。 +- 适合**在运行时已知最大键值对数量**的场景,因为它在创建时需要指定数组的最大大小。 + +**4.1per-cpu-array的创建源码:(/kernel/bpf/arraymap.c)** + +```c +static int bpf_array_alloc_percpu(struct bpf_array *array) +{ + // 用于存储 per-cpu 分配的指针 + void __percpu *ptr; + int i; + + // 遍历每个数组条目,为每个条目分配 per-cpu 的内存 + for (i = 0; i < array->map.max_entries; i++) { + // 调用 bpf_map_alloc_percpu 为当前数组条目分配 per-cpu 内存 + ptr = bpf_map_alloc_percpu(&array->map, array->elem_size, 8, + GFP_USER | __GFP_NOWARN); + if (!ptr) { + // 如果分配失败,释放之前已分配的 per-cpu 内存 + bpf_array_free_percpu(array); + return -ENOMEM; // 返回内存分配失败错误 + } + // 将分配的 per-cpu 内存指针存储到数组的 pptrs(per-cpu 指针数组)中 + array->pptrs[i] = ptr; + + // 在分配过程中调用 cond_resched,以便让内核有机会调度其他任务 + cond_resched(); + } + + // 所有数组条目成功分配 per-cpu 内存后,返回 0,表示成功 + return 0; +} +``` + +该函数负责为 `BPF_MAP_TYPE_PERCPU_ARRAY` 类型的 BPF 数组的每个元素分配独立的 per-cpu 内存。循环遍历每个数组元素,调用 `bpf_map_alloc_percpu` 为每个元素分配 per-cpu 内存,将分配的 per-cpu 内存指针存储到 `array->pptrs` 中,方便后续使用。该函数的目标是确保 `BPF_MAP_TYPE_PERCPU_ARRAY` 的每个条目都拥有独立的 per-cpu 内存空间,以便在多 CPU 环境中高效处理数据。 + +**4.2per-cpu-array的查找源码:(/kernel/bpf/arraymap.c)** + +```c +static void *percpu_array_map_lookup_elem(struct bpf_map *map, void *key) +{ + // 将 bpf_map 结构体转换为 bpf_array 结构体,以便访问数组特有的字段 + struct bpf_array *array = container_of(map, struct bpf_array, map); + // 将 key 转换为 u32 类型,作为数组的索引 + u32 index = *(u32 *)key; + + // 检查索引是否超出数组的最大有效索引范围 + if (unlikely(index >= array->map.max_entries)) + return NULL; + + // 使用 this_cpu_ptr 宏获取当前 CPU 上的值 + // array->pptrs 存储每个索引的 per-cpu 指针 + // 通过 index & array->index_mask 确保索引在有效范围内 + return this_cpu_ptr(array->pptrs[index & array->index_mask]); +} +``` + +### 5.BPF_MAP_TYPE_RINGBUF + +- **Ring Buffer Map** 是一种先进先出(FIFO)环形缓冲区,设计用于存储和管理动态生成的数据。**Ring Buffer Map** 允许用户在内核空间和用户空间之间进行高效的数据传输,尤其适合处理大量实时数据。 +- **Ring Buffer Map** 的大小在创建时需要指定,且要求该大小是 2 的幂次方并且页面对齐。其大小在运行时是固定的,不能动态调整。 +- **Ring Buffer Map** 的实现基于环形缓冲区结构,数据插入和读取操作遵循先进先出的原则。由于其设计允许重用缓冲区中的空间,因此可以有效地管理内存。 +- 适合**需要高效数据流传输和处理**的场景,尤其是在需要频繁写入和读取数据的高性能应用中,如网络数据包捕获和性能监控。 +- 适合**需要稳定和高效的循环缓冲区管理**的场景,因为**Ring Buffer Map** 可以高效地处理持续不断的数据流,同时避免了传统缓冲区的空间浪费问题。 + +**5.1 ring-buff的创建源码:(/kernel/bpf/ringbuf.c)** + +```c +static struct bpf_map *ringbuf_map_alloc(union bpf_attr *attr) +{ + struct bpf_ringbuf_map *rb_map; + + // 检查属性标志是否有效,确保没有使用不支持的标志位 + if (attr->map_flags & ~RINGBUF_CREATE_FLAG_MASK) + return ERR_PTR(-EINVAL); + + // 检查 key_size 和 value_size 是否为零,最大条目数是否为 2 的幂,并且是否与页面对齐 + if (attr->key_size || attr->value_size || + !is_power_of_2(attr->max_entries) || + !PAGE_ALIGNED(attr->max_entries)) + return ERR_PTR(-EINVAL); + + // 分配 bpf_ringbuf_map 结构体的内存 + rb_map = bpf_map_area_alloc(sizeof(*rb_map), NUMA_NO_NODE); + if (!rb_map) + return ERR_PTR(-ENOMEM); + + // 根据属性初始化 BPF map + bpf_map_init_from_attr(&rb_map->map, attr); + + // 分配 ring buffer 的内存 + rb_map->rb = bpf_ringbuf_alloc(attr->max_entries, rb_map->map.numa_node); + if (!rb_map->rb) { + // 如果分配失败,释放之前分配的内存 + bpf_map_area_free(rb_map); + return ERR_PTR(-ENOMEM); + } + + // 返回初始化后的 BPF map + return &rb_map->map; +} +``` + +接下来再查看bpf_ringbuf_alloc()函数是如何分配ring buffer 内存的。 + +```c +static struct bpf_ringbuf *bpf_ringbuf_area_alloc(size_t data_sz, int numa_node) +{ + const gfp_t flags = GFP_KERNEL_ACCOUNT | __GFP_RETRY_MAYFAIL | + __GFP_NOWARN | __GFP_ZERO; + int nr_meta_pages = RINGBUF_NR_META_PAGES; // 元数据页面的数量 + int nr_data_pages = data_sz >> PAGE_SHIFT; // 数据页面的数量 + int nr_pages = nr_meta_pages + nr_data_pages; // 总页面数量 + struct page **pages, *page; + struct bpf_ringbuf *rb; + size_t array_size; + int i; + + /* 每个数据页被映射两次,以允许对环形缓冲区中 + * 尾部环绕的样本进行“虚拟”连续读取: + * ------------------------------------------------------ + * | 元数据页面 | 实际的数据页面 | 相同的数据页面 | + * ------------------------------------------------------ + * | | 1 2 3 4 5 6 7 8 9 | 1 2 3 4 5 6 7 8 9 | + * ------------------------------------------------------ + * | | TA DA | TA DA | + * ------------------------------------------------------ + * ^^^^^^^ + * | + * 由于双重映射数据页,这里不需要担心环绕数据的特殊处理。 + * 这在内核和用户空间 mmap() 时均有效,显著简化了 + * 内核和用户空间的实现。 + */ + array_size = (nr_meta_pages + 2 * nr_data_pages) * sizeof(*pages); // 计算页面数组大小,包括元数据和双重映射的数据页 + pages = bpf_map_area_alloc(array_size, numa_node); // 为页面数组分配内存 + if (!pages) + return NULL; // 如果分配页面数组失败,则返回 NULL + + // 为所有页面分配物理内存 + for (i = 0; i < nr_pages; i++) { + page = alloc_pages_node(numa_node, flags, 0); // 分配单个页面 + if (!page) { + nr_pages = i; // 更新成功分配的页面数量 + goto err_free_pages; // 如果分配失败,释放已分配的页面 + } + pages[i] = page; // 将页面存储到页面数组中 + // 为环形缓冲区分配双重映射的页面 + if (i >= nr_meta_pages) + pages[nr_data_pages + i] = page; // 将相同的数据页进行双重映射 + } + + // 将所有页面映射到内核虚拟地址空间 + rb = vmap(pages, nr_meta_pages + 2 * nr_data_pages, + VM_MAP | VM_USERMAP, PAGE_KERNEL); // 将元数据页和数据页映射到虚拟内存 + if (rb) { + kmemleak_not_leak(pages); // 告诉内存泄漏探测器忽略这些页面 + rb->pages = pages; // 保存页面数组指针 + rb->nr_pages = nr_pages; // 保存总页面数量 + return rb; // 返回已分配和映射的环形缓冲区 + } + +err_free_pages: + // 如果发生错误,释放已分配的页面 + for (i = 0; i < nr_pages; i++) + __free_page(pages[i]); // 释放页面 + bpf_map_area_free(pages); // 释放页面数组内存 + return NULL; // 返回 NULL,表示环形缓冲区分配失败 +} +``` + +这里需要来说明一下为什么要进行双重映射: + +这里我们举一个例子:假设我们有一个环形缓冲区,总大小为 **8 页**,每页大小为 **1KB**。为了简化说明,假设缓冲区的虚拟地址从 **0x1000** 开始。双重映射后的内存布局: + +```c +物理内存(假设物理地址): +[Page 0][Page 1][Page 2][Page 3][Page 4][Page 5][Page 6][Page 7] +虚拟地址空间: +[0x1000-0x13FF] -> Page 0 +[0x1400-0x17FF] -> Page 1 +... +[0x1C00-0x1FFF] -> Page 7 +[0x2000-0x23FF] -> Page 0 (再次映射 Page 0) +[0x2400-0x27FF] -> Page 1 (再次映射 Page 1) +... +[0x2C00-0x2FFF] -> Page 7 (再次映射 Page 7) + +``` + +**写入数据:**写入指针(head)从 **0x1000** 开始,依次写入 Page 0 到 Page 7。当写入指针到达 Page 7 后,再次回绕到 Page 0。 + +**读取数据**:假设当前写入指针在 **Page 2**,读取指针在 **Page 6**。读取指针位于 **Page 6**,需要读取从 **Page 6** 到 **Page 2** 的数据。在物理内存中,这些数据分布在 Page 6、Page 7 和回绕到 Page 0、Page 1、Page 2。但由于每个页面被映射了两次,读取指针在虚拟地址空间中可以连续读取:从 **0x1C00-0x1FFF** (Page 7) 继续读取到 **0x2000-0x23FF** (Page 0)。这样,读取过程在虚拟地址空间中是连续的,无需处理跨页边界的逻辑。 + +**优点:**开发者或内核在读取数据时,可以像处理一个线性内存块一样连续读取,即使数据在物理内存中实际上是分开的。避免了在读取数据时需要检查是否跨越缓冲区边界,从而减少了额外的条件判断和可能的性能开销。 + +## 二、理论分析结果: + +### **1. BPF_MAP_TYPE_HASH** + +##### **特点:** + +- **数据结构**:基于哈希表实现,每个键值对通过哈希函数来确定其在表中的位置。 +- **查找效率**:通常具有常数时间复杂度(O(1))用于插入、查找和删除操作,但在哈希冲突严重时,性能可能下降。 +- **键值对**:键(key)和值(value)的数据类型可以不同,但键必须是可哈希的。 +- **内存使用**:由于哈希表需要存储哈希桶和链表(或其他结构),内存使用可能较高。 + +##### **适用场景:** + +- **动态键值对**:适合用于存储和检索动态的键值对,尤其是当键的范围不固定或不可预测时。 +- **频繁查找**:当需要快速查找、更新和删除操作时,hashmap 是理想的选择。 +- **数据稀疏**:适用于数据稀疏且键值对不连续的场景,例如,跟踪网络流量中的源IP和目的IP。 + +### 2. **BPF_MAP_TYPE_ARRAY** + +##### **特点:** + +- **数据结构**:基于数组实现,所有的键是整数索引,值是与索引对应的数据。 +- **查找效率**:具有常数时间复杂度(O(1))用于插入、查找和删除操作,因索引可以直接映射到数组位置。 +- **键值对**:键必须是整数索引,通常从0开始,并且连续。值可以是任意类型,但数组的大小必须在创建时定义。 +- **内存使用**:由于使用数组,内存使用较为稳定,不受哈希表可能导致的内存碎片影响。 + +##### **适用场景:** + +- **固定范围键**:适合用于存储键为连续整数索引的场景,例如,跟踪数组中每个位置的状态。 +- **简单索引**:当键是已知的、有限范围的整数时,arraymap 是更高效的选择。 +- **性能稳定**:在内存使用和性能上都较为稳定,适合用于固定大小的配置数据或状态跟踪。 + +### 3.BPF_MAP_TYPE_PERCPU_HASH + +##### **特点:** + +- **数据结构**:基于哈希表实现,每个 CPU 都有独立的哈希表副本,键可以是任意类型(如整数、结构体),而值则存储在每个 CPU 上独立的哈希表中。 +- **查找效率**:插入、查找和删除操作的时间复杂度通常为 O(1),但是会根据哈希冲突情况有所不同。 +- **键值对**:键可以是任意类型,值在每个 CPU 上有独立的副本。每个 CPU 的哈希表是相互独立的,避免了多核之间的锁竞争。 +- **内存使用**:由于每个 CPU 都有独立的哈希表副本,内存消耗与 CPU 数量成正比。适合多核场景下的并行读写操作。 + +##### **适用场景:** + +- **高并发场景**:适合多核系统中并发读写操作的场景,如跟踪每个 CPU 独立的数据统计(例如每个 CPU 的网络包计数)。 +- **读写分离**:当数据操作可以分散到每个 CPU 上进行独立处理,减少锁竞争时,`percpu_hash` 是理想选择。 +- **动态键**:适用于键是动态生成的、不固定的场景,尤其在每个 CPU 都需要独立存储和读取数据的情况下。 + +### 4.BPF_MAP_TYPE_PERCPU_ARRAY + +##### **特点:** + +- **数据结构**:基于数组实现,每个 CPU 都有自己独立的数组副本。数组中的元素存储键值对,所有 CPU 的数组结构相同。 +- **查找效率**:常数时间复杂度(O(1))的插入、查找和删除操作,通过数组索引直接访问元素。 +- **键值对**:键必须是整数索引,且所有 CPU 的数组在创建时大小固定。值在每个 CPU 上有独立的副本,支持并行操作。 +- **内存使用**:内存消耗较为稳定,每个 CPU 都有自己的数组副本,因此内存开销与 CPU 数量和数组大小成正比。 + +##### **适用场景:** + +- **高并发读写**:适合多核系统中频繁读写、按索引访问的场景。每个 CPU 独立操作各自的数组,避免了跨 CPU 的锁竞争。 +- **已知大小的索引访问**:适用于数组大小在创建时已知、键是有限范围的场景,例如在多核系统中统计数据或跟踪状态。 +- **性能稳定**:适合需要高效索引访问,并且对性能稳定性有要求的场景。 + +### 5.BPF_MAP_TYPE_RINGBUF + +##### **特点:** + +- **数据结构**:基于环形缓冲区(ring buffer)实现,支持用户空间和内核空间之间的数据传输。它使用双映射(双重页映射)来实现高效的数据读取,避免缓冲区溢出时复杂的处理逻辑。 +- **查找效率**:采用生产者-消费者模式,生产者(通常是内核)将数据写入缓冲区,消费者(如用户空间程序)通过轮询或通知的方式读取数据。缓冲区满时,生产者会暂停写入。 +- **键值对**:没有显式的键值对概念,而是按照数据的写入顺序读取。适合顺序写入和读取的大量数据场景。 +- **内存使用**:内存使用量根据环形缓冲区的大小和页数固定。双重页映射机制允许读取者访问连续数据,即使数据跨越缓冲区边界。 + +##### **适用场景:** + +- **高效数据传输**:适合高吞吐量的数据传输场景,尤其是从内核空间向用户空间传输大量数据的场景,如网络包捕获或性能数据收集。 +- **顺序写入读取**:适合连续写入和读取数据的场景,不适合随机访问。读写操作之间严格遵循生产者-消费者模式。 +- **性能敏感场景**:适用于需要低延迟、高效数据传输的场景,特别是在需要频繁从内核向用户空间传递信息时。 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" new file mode 100644 index 000000000..c947c1d72 --- /dev/null +++ "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" @@ -0,0 +1,490 @@ +# eBPF多维度性能分析测试方案 + +## 一、测试准备 + +​ 在本次eBPF性能分析以及测试中,将从多个维度去分析eBPF程序在不同内核版本下以及在不同负载情境下,不同Map类型和挂载点类型的性能差异。 + +​ 首先,我们先在测试之前进行测试的一些必要的准备。 + +​ 使用以下表格来说明本次测试需要的准备: + +| 准备项 | 说明 | +| -------------- | ---------------------------------- | +| 内核版本的选取 | 选取本次测试需要测试的内核版本 | +| 负载环境的选取 | 确定本次测试的负载方案 | +| Map类型的选取 | 确定好本次测试将要测试的Map类型 | +| 挂载点的选取 | 确定好本次测试将要测试的挂载点类型 | +| 测试机配置 | 说明测试机的各项配置 | + +### **1.内核版本的选取:** + +​ 首先,在选取需要测试的Linux内核版本时,考虑两个方面,第一个是在eBPF发展中比较有转折点的内核版本,其次是目前企业应用和日常学习中,大家比较常用的Linux内核版本。首先,以下是eBPF技术在主要内核版本中的发展过程及其引入的功能(主要关注Map和程序类型): + +| 内核版本 | 时间 | 新增功能 | +| ---------- | ------- | ------------------------------------------------------------ | +| Linux 3.18 | 2014.12 | 基础的eBPF Maps引入:最初引入的map类型有`hash map`和`array map` | +| Linux 4.6 | 2016.5 | 引入了`Per-CPU`的哈希表和数组类型 | +| Linux 4.3 | 2015.11 | 支持了`kprobe`和`tracepoints` | +| Linux 4.14 | 2017.11 | 支持eBPF程序附加到`perf events`,用于性能分析和监控 | +| Linux 4.19 | 2018.10 | 引入了环形缓冲区类型的`Ring Buffer Map` | +| Linux 5.19 | 2022.7 | 增强了`XDP`和`BPF trampoline`功能,使得动态附加eBPF程序更加高效和灵活 | +| Linux 6.2 | 2023.2 | 增强了对 `fentry` 和 `fexit` 程序类型的支持,使得 eBPF 程序可以更灵活地附加到内核函数的入口和退出点 | +| Linux 6.5 | 2023.8 | 引入了新的机制`bpf_cookie`:允许 eBPF 程序为特定事件分配和管理 `cookie` | + +​ 通过查阅资料并进行调研,本次测试选取的内核版本为4.19、5.19、6.5三个Linux内核版本来进行分别的测试。 + +### 2.负载环境的选取: + +#### 1.系统负载 + +​ 在本次测试中,会使用负载工具(stress-ng)对整个系统进行加压,比如,对cpu、内存、IO速率进行加压。在这种系统高负载以及在内核版本固定,负载环境固定的情况下,比较不同Map类型和不同挂载点类型在高负载环境下的表现情况。 + +​ 这种设计是考虑eBPF程序在整个系统高负载情况下不同Map类型、不同挂载点类型的性能测试。接下来,还需要测试在eBPF程序本身的高负载环境下,不同Map类型、不同挂载点类型的表现情况。 + +#### 2.程序内部高负载 + +​ 在本次实验中,也会对eBPF的不同Map进行大量的增删改查操作,来模拟对Map操作的高负载情境。比如,控制eBPF的挂载点相同、内核版本相同、测试环境相同、对不同的Map类型进行相同且大量的增删改查操作,来对比不同Map类型在各种前提都相同,它们的操作在时间性能上存在的差异。 + +### 3.Map类型的选取: + +​ 在本次测试中,我们选取的Linux内核版本分别为:4.19、5.19、6.5这三个版本,接下来通过查看内核源码来查看这三个版本分别支持的Map类型,内核路径为:(/include/uapi/linux/bpf.h) + +Linux4.19版本支持的Map类型: + +```c +enum bpf_map_type { + BPF_MAP_TYPE_UNSPEC, + BPF_MAP_TYPE_HASH, + BPF_MAP_TYPE_ARRAY, + BPF_MAP_TYPE_PROG_ARRAY, + BPF_MAP_TYPE_PERF_EVENT_ARRAY, + BPF_MAP_TYPE_PERCPU_HASH, + BPF_MAP_TYPE_PERCPU_ARRAY, + BPF_MAP_TYPE_STACK_TRACE, + BPF_MAP_TYPE_CGROUP_ARRAY, + BPF_MAP_TYPE_LRU_HASH, + BPF_MAP_TYPE_LRU_PERCPU_HASH, + BPF_MAP_TYPE_LPM_TRIE, + BPF_MAP_TYPE_ARRAY_OF_MAPS, + BPF_MAP_TYPE_HASH_OF_MAPS, + BPF_MAP_TYPE_DEVMAP, + BPF_MAP_TYPE_SOCKMAP, + BPF_MAP_TYPE_CPUMAP, + BPF_MAP_TYPE_XSKMAP, + BPF_MAP_TYPE_SOCKHASH, + BPF_MAP_TYPE_CGROUP_STORAGE, + BPF_MAP_TYPE_REUSEPORT_SOCKARRAY, +};//21种 +``` + +Linux5.19版本支持的Map类型: + +```c +enum bpf_map_type { + BPF_MAP_TYPE_UNSPEC, + BPF_MAP_TYPE_HASH, + BPF_MAP_TYPE_ARRAY, + BPF_MAP_TYPE_PROG_ARRAY, + BPF_MAP_TYPE_PERF_EVENT_ARRAY, + BPF_MAP_TYPE_PERCPU_HASH, + BPF_MAP_TYPE_PERCPU_ARRAY, + BPF_MAP_TYPE_STACK_TRACE, + BPF_MAP_TYPE_CGROUP_ARRAY, + BPF_MAP_TYPE_LRU_HASH, + BPF_MAP_TYPE_LRU_PERCPU_HASH, + BPF_MAP_TYPE_LPM_TRIE, + BPF_MAP_TYPE_ARRAY_OF_MAPS, + BPF_MAP_TYPE_HASH_OF_MAPS, + BPF_MAP_TYPE_DEVMAP, + BPF_MAP_TYPE_SOCKMAP, + BPF_MAP_TYPE_CPUMAP, + BPF_MAP_TYPE_XSKMAP, + BPF_MAP_TYPE_SOCKHASH, + BPF_MAP_TYPE_CGROUP_STORAGE, + BPF_MAP_TYPE_REUSEPORT_SOCKARRAY, + BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE, + BPF_MAP_TYPE_QUEUE, + BPF_MAP_TYPE_STACK, + BPF_MAP_TYPE_SK_STORAGE, + BPF_MAP_TYPE_DEVMAP_HASH, + BPF_MAP_TYPE_STRUCT_OPS, + BPF_MAP_TYPE_RINGBUF, + BPF_MAP_TYPE_INODE_STORAGE, + BPF_MAP_TYPE_TASK_STORAGE, + BPF_MAP_TYPE_BLOOM_FILTER, +};//31种 +``` + +Linux6.5版本支持的Map类型: + +```c +enum bpf_map_type { + BPF_MAP_TYPE_UNSPEC, + BPF_MAP_TYPE_HASH, + BPF_MAP_TYPE_ARRAY, + BPF_MAP_TYPE_PROG_ARRAY, + BPF_MAP_TYPE_PERF_EVENT_ARRAY, + BPF_MAP_TYPE_PERCPU_HASH, + BPF_MAP_TYPE_PERCPU_ARRAY, + BPF_MAP_TYPE_STACK_TRACE, + BPF_MAP_TYPE_CGROUP_ARRAY, + BPF_MAP_TYPE_LRU_HASH, + BPF_MAP_TYPE_LRU_PERCPU_HASH, + BPF_MAP_TYPE_LPM_TRIE, + BPF_MAP_TYPE_ARRAY_OF_MAPS, + BPF_MAP_TYPE_HASH_OF_MAPS, + BPF_MAP_TYPE_DEVMAP, + BPF_MAP_TYPE_SOCKMAP, + BPF_MAP_TYPE_CPUMAP, + BPF_MAP_TYPE_XSKMAP, + BPF_MAP_TYPE_SOCKHASH, + BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED, + BPF_MAP_TYPE_CGROUP_STORAGE = BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED, + BPF_MAP_TYPE_REUSEPORT_SOCKARRAY, + BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE, + BPF_MAP_TYPE_QUEUE, + BPF_MAP_TYPE_STACK, + BPF_MAP_TYPE_SK_STORAGE, + BPF_MAP_TYPE_DEVMAP_HASH, + BPF_MAP_TYPE_STRUCT_OPS, + BPF_MAP_TYPE_RINGBUF, + BPF_MAP_TYPE_INODE_STORAGE, + BPF_MAP_TYPE_TASK_STORAGE, + BPF_MAP_TYPE_BLOOM_FILTER, + BPF_MAP_TYPE_USER_RINGBUF, + BPF_MAP_TYPE_CGRP_STORAGE, +};//34种 +``` + +​ 在本次测试中,将对三个内核版本进行分别的分析和测试。 + +​ 目前,将分析并测试常用的Map类型,在Linux4.19内核版本中,将分析并测试以下Map类型: + +```c + BPF_MAP_TYPE_HASH, + BPF_MAP_TYPE_ARRAY, + BPF_MAP_TYPE_PERF_EVENT_ARRAY, + BPF_MAP_TYPE_PERCPU_HASH, + BPF_MAP_TYPE_PERCPU_ARRAY, +``` + +​ 在Linux5.19内核版本中,将分析并测试常用的以下Map类型: + +```c + BPF_MAP_TYPE_HASH, + BPF_MAP_TYPE_ARRAY, + BPF_MAP_TYPE_PERF_EVENT_ARRAY, + BPF_MAP_TYPE_PERCPU_HASH, + BPF_MAP_TYPE_PERCPU_ARRAY, + BPF_MAP_TYPE_QUEUE, + BPF_MAP_TYPE_STACK, + BPF_MAP_TYPE_RINGBUF, +``` + +​ 在Linux6.5内核版本中,将分析并测试常用的以下Map类型: + +```c + BPF_MAP_TYPE_HASH, + BPF_MAP_TYPE_ARRAY, + BPF_MAP_TYPE_PERF_EVENT_ARRAY, + BPF_MAP_TYPE_PERCPU_HASH, + BPF_MAP_TYPE_PERCPU_ARRAY, + BPF_MAP_TYPE_QUEUE, + BPF_MAP_TYPE_STACK, + BPF_MAP_TYPE_RINGBUF, + BPF_MAP_TYPE_USER_RINGBUF, +``` + +### 4.挂载点类型的选取: + +​ 和上面的分析类似,我们先看不同内核版本所支持的挂载点类型: + +Linux4.19版本支持的挂载点类型: + +```c +enum bpf_prog_type { + BPF_PROG_TYPE_UNSPEC, + BPF_PROG_TYPE_SOCKET_FILTER, + BPF_PROG_TYPE_KPROBE, + BPF_PROG_TYPE_SCHED_CLS, + BPF_PROG_TYPE_SCHED_ACT, + BPF_PROG_TYPE_TRACEPOINT, + BPF_PROG_TYPE_XDP, + BPF_PROG_TYPE_PERF_EVENT, + BPF_PROG_TYPE_CGROUP_SKB, + BPF_PROG_TYPE_CGROUP_SOCK, + BPF_PROG_TYPE_LWT_IN, + BPF_PROG_TYPE_LWT_OUT, + BPF_PROG_TYPE_LWT_XMIT, + BPF_PROG_TYPE_SOCK_OPS, + BPF_PROG_TYPE_SK_SKB, + BPF_PROG_TYPE_CGROUP_DEVICE, + BPF_PROG_TYPE_SK_MSG, + BPF_PROG_TYPE_RAW_TRACEPOINT, + BPF_PROG_TYPE_CGROUP_SOCK_ADDR, + BPF_PROG_TYPE_LWT_SEG6LOCAL, + BPF_PROG_TYPE_LIRC_MODE2, + BPF_PROG_TYPE_SK_REUSEPORT, +};//22种 +``` + +Linux5.19版本支持的挂载点类型: + +```c +enum bpf_prog_type { + BPF_PROG_TYPE_UNSPEC, + BPF_PROG_TYPE_SOCKET_FILTER, + BPF_PROG_TYPE_KPROBE, + BPF_PROG_TYPE_SCHED_CLS, + BPF_PROG_TYPE_SCHED_ACT, + BPF_PROG_TYPE_TRACEPOINT, + BPF_PROG_TYPE_XDP, + BPF_PROG_TYPE_PERF_EVENT, + BPF_PROG_TYPE_CGROUP_SKB, + BPF_PROG_TYPE_CGROUP_SOCK, + BPF_PROG_TYPE_LWT_IN, + BPF_PROG_TYPE_LWT_OUT, + BPF_PROG_TYPE_LWT_XMIT, + BPF_PROG_TYPE_SOCK_OPS, + BPF_PROG_TYPE_SK_SKB, + BPF_PROG_TYPE_CGROUP_DEVICE, + BPF_PROG_TYPE_SK_MSG, + BPF_PROG_TYPE_RAW_TRACEPOINT, + BPF_PROG_TYPE_CGROUP_SOCK_ADDR, + BPF_PROG_TYPE_LWT_SEG6LOCAL, + BPF_PROG_TYPE_LIRC_MODE2, + BPF_PROG_TYPE_SK_REUSEPORT, + BPF_PROG_TYPE_FLOW_DISSECTOR, + BPF_PROG_TYPE_CGROUP_SYSCTL, + BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, + BPF_PROG_TYPE_CGROUP_SOCKOPT, + BPF_PROG_TYPE_TRACING, + BPF_PROG_TYPE_STRUCT_OPS, + BPF_PROG_TYPE_EXT, + BPF_PROG_TYPE_LSM, + BPF_PROG_TYPE_SK_LOOKUP, + BPF_PROG_TYPE_SYSCALL, +};//32种 +``` + +Linux6.5版本支持的挂载点类型: + +```c +enum bpf_prog_type { + BPF_PROG_TYPE_UNSPEC, + BPF_PROG_TYPE_SOCKET_FILTER, + BPF_PROG_TYPE_KPROBE, + BPF_PROG_TYPE_SCHED_CLS, + BPF_PROG_TYPE_SCHED_ACT, + BPF_PROG_TYPE_TRACEPOINT, + BPF_PROG_TYPE_XDP, + BPF_PROG_TYPE_PERF_EVENT, + BPF_PROG_TYPE_CGROUP_SKB, + BPF_PROG_TYPE_CGROUP_SOCK, + BPF_PROG_TYPE_LWT_IN, + BPF_PROG_TYPE_LWT_OUT, + BPF_PROG_TYPE_LWT_XMIT, + BPF_PROG_TYPE_SOCK_OPS, + BPF_PROG_TYPE_SK_SKB, + BPF_PROG_TYPE_CGROUP_DEVICE, + BPF_PROG_TYPE_SK_MSG, + BPF_PROG_TYPE_RAW_TRACEPOINT, + BPF_PROG_TYPE_CGROUP_SOCK_ADDR, + BPF_PROG_TYPE_LWT_SEG6LOCAL, + BPF_PROG_TYPE_LIRC_MODE2, + BPF_PROG_TYPE_SK_REUSEPORT, + BPF_PROG_TYPE_FLOW_DISSECTOR, + BPF_PROG_TYPE_CGROUP_SYSCTL, + BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, + BPF_PROG_TYPE_CGROUP_SOCKOPT, + BPF_PROG_TYPE_TRACING, + BPF_PROG_TYPE_STRUCT_OPS, + BPF_PROG_TYPE_EXT, + BPF_PROG_TYPE_LSM, + BPF_PROG_TYPE_SK_LOOKUP, + BPF_PROG_TYPE_SYSCALL, + BPF_PROG_TYPE_NETFILTER, +};//33种 +``` + +​ 在测试这三个不同内核版本的挂载点表现差异时,我们选取常用的挂载点类型,如下所示: + +```c + BPF_PROG_TYPE_SOCKET_FILTER, + BPF_PROG_TYPE_KPROBE, + BPF_PROG_TYPE_TRACEPOINT, + BPF_PROG_TYPE_XDP, + BPF_PROG_TYPE_PERF_EVENT, + BPF_PROG_TYPE_SYSCALL, +``` + +### 5.测试机的配置 + +本次测试的主机配置如下所示: + +| 名称 | 配置 | +| ------------ | ------------------------------------------------------ | +| CPU | 13th Gen Intel(R) Core(TM) i9-13900HX 2.20 GHz 16核 | +| 内存 | 16GB | +| 硬盘大小 | 100GB | +| 虚拟机 | VMware Workstation 上搭建的ubuntu 22.04.3 LTS | +| 内核版本 | 4.19、5.19、6.5 | +| eBPF开发工具 | libbpf | + +在测试之前,我们需要关闭CPU的P-states和C-states,来确保CPU频率一致性。**P-states** 是处理器的性能状态,用于调整 CPU 的工作频率和电压,以达到性能和功耗的平衡。P-states 允许处理器在不同的性能状态之间切换,以适应当前的计算负载。**C-states** 是处理器的休眠状态,用于降低 CPU 的功耗当其处于空闲状态时。C-states 允许处理器在不使用时进入更深的节能状态,从而减少功耗。 + +配置如下: + +**关闭P-states:** + +```shell +GRUB_CMDLINE_LINUX="resume=/dev/mapper/ao_anolis-swap rd.lvm.lv=ao_anolis/root rd.lvm.lv=ao_anolis/swap rhgb quiet" +更改为: +GRUB_CMDLINE_LINUX="resume=/dev/mapper/ao_anolis-swap rd.lvm.lv=ao_anolis/root rd.lvm.lv=ao_anolis/swap rhgb quiet intel_pstate=disable noacpi" +``` + +**关闭C-states:** + +```shell +GRUB_CMDLINE_LINUX="resume=/dev/mapper/ao_anolis-swap rd.lvm.lv=ao_anolis/root rd.lvm.lv=ao_anolis/swap rhgb quiet intel_pstate=disable noacpi" +更改为: +GRUB_CMDLINE_LINUX="resume=/dev/mapper/ao_anolis-swap rd.lvm.lv=ao_anolis/root rd.lvm.lv=ao_anolis/swap rhgb quiet intel_pstate=disable noacpi processor.max_cstate=0" +``` + +## 二、测试工具引入: + +​ 在本次测试过程中,会使用以下工具来进行测试: + +| 工具 | 工具说明 | +| ------------------ | ------------------------------------------------------------ | +| libbpf | 使用libbpf工具来进行eBPF程序的编写 | +| stress-ng | 通过该工具对系统进行加压,模拟高负载环境 | +| Visual Studio Code | 使用该开发工具进行虚拟机控制和程序编写 | +| Python | 使用Python语言喝Python数据分析的相关库来对测试后的数据进行分析 | +| shell脚本 | 编写shell脚本来自动化测试用例和数据分析的运行 | +| Typora | 编写测试相关的文档,包括测试分析和测试结果等 | + +## 三、测试计划 + +| 时间 | 任务 | 产出 | +| ---------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| 7.20-8.2 | 详细设计出测试方案,对后面做出详细的规划 | 输出详细的测试方案 | +| Map: | | | +| 8.3-8.9 | 在不同的内核版本下,对Map的各个类型进行详细的理论分析 | 输出详细的Map类型分析报告并给出分析结论 | +| 8.10-8.16 | 编写测试代码并从时间维度,不同负载的情境下对不同Map进行测试 | 输出详细的测试结果 | +| 8.17-8.23 | 完善测试并对结果进行分析 | 输出测试结果和理论分析,并不同版本的Map特性,结合理论分析给出一个操作指南 | +| 挂载点: | | | +| 8.24-8.30 | 在不同的内核版本下,对挂载点的各个类型进行详细的理论分析 | 输出详细的挂载点类型分析报告并给出分析结论 | +| 8.31-9.6 | 编写测试代码并从时间维度,不同负载的情境下对不同挂载点进行测试 | 输出详细的测试结果 | +| 9.7-9.13 | 完善测试并对结果进行分析 | 输出测试结果和理论分析,并给出不同版本的挂载点特性,结合理论分析给出一个操作指南 | +| 测试补充: | | | +| 9.14-9.20 | 通过测试过程中发现的问题和遗漏,再补充一些需要的测试结果 | 初步输出一个项目总体测试文档,并查漏补缺 | +| 9.21-9.30 | 完善项目总体开发测试文档 | 输出最终的测试报告并整理项目代码 | + +## 四、测试方案 + +​ 在测试之前,需要通过查阅资料和阅读内核源码给出一个详细且准确的理论分析报告,然后再通过下述的测试过程和测试结果来验证理论分析的正确性。并且最终给出一个eBPF最佳实践指南。 + +#### 4.1 Map类型的测试方案: + +​ Map类型的测试方案说明了本次测试方案是从时间的角度去分析不同Map类型的差异。 + +**测试的流程如下图所示:** + +![Map测试方案](./images/Map测试方案.png) + +对上图进行详细解释: + +1.首先,通过上面的分析。我们需要在测试之前确定一些环境因素: + +- 确定内核版本,本次测试将从内核版本6.5、5.19、4.19这个顺序来进行测试。 +- 确定负载,本次测试将负载定为两大类,分别为:对系统进行加压负载、对Map的操作次数进行设置。 +- 确定测试指标,本次测试会对不同Map类型定义相同的空间大小,并且进行相同的操作,来测试这些不同的Map类型在各种环境都确定的情况下,它们在时间维度上的差异。 + +2.接下来就是编写测试程序,这里会使用libbpf来编写,编写程序的关键点为: + +- 根据前面分析的结果,定义选定内核版本需要测试的Map类型。 +- 将定义好的所有Map结构体挂载在同一个函数上。 +- 在用户态对这些定义的Map进行获取并进行相同次数的相同操作。 +- 将每个Map类型的操作时间记录下来。 +- 循环上述操作,获取多组测试数据。 + +3.最后,编写python脚本来对得出的操作时间进行数据分析,并编写shell脚本进行上述所有操作的整合,自动化测试程序: + +- 通过测试程序输出的时间数据,进行数据分析。 +- 将分析的结果以文件和图表的方式展现出来。 +- 输出测试报告并结合理论分析的结果给出一个在Map方面的最佳实践指南。 + +通过上述的描述,接下来给出一个流程图来说明本次测试的具体过程: + +![Map性能测试流程](./images/Map测试流程.png) + +测试用例设计: + +用例1: + +| 事项 | 内容 | +| ---------------- | ------------------------------------------------------------ | +| 场景 | 在系统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/Map\346\265\213\350\257\225\346\226\271\346\241\210.png" "b/eBPF_Supermarket/eBPF_Performance_Analysis/docs/images/Map\346\265\213\350\257\225\346\226\271\346\241\210.png" new file mode 100644 index 000000000..cae6c82c4 Binary files /dev/null and "b/eBPF_Supermarket/eBPF_Performance_Analysis/docs/images/Map\346\265\213\350\257\225\346\226\271\346\241\210.png" differ diff --git "a/eBPF_Supermarket/eBPF_Performance_Analysis/docs/images/Map\346\265\213\350\257\225\346\265\201\347\250\213.png" "b/eBPF_Supermarket/eBPF_Performance_Analysis/docs/images/Map\346\265\213\350\257\225\346\265\201\347\250\213.png" new file mode 100644 index 000000000..411fe5fea Binary files /dev/null and "b/eBPF_Supermarket/eBPF_Performance_Analysis/docs/images/Map\346\265\213\350\257\225\346\265\201\347\250\213.png" differ 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 diff --git a/eBPF_Supermarket/kvm_watcher/Makefile b/eBPF_Supermarket/kvm_watcher/Makefile index 30f6a4b89..cc5a05466 100644 --- a/eBPF_Supermarket/kvm_watcher/Makefile +++ b/eBPF_Supermarket/kvm_watcher/Makefile @@ -1,4 +1,3 @@ -.SILENT: ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \ | sed 's/arm.*/arm/' \ | sed 's/aarch64/arm64/' \ @@ -18,19 +17,12 @@ BPF_CFLAGS=-g -O2 -target bpf LIBS=-lbpf -lelf -lz -lzstd # 头文件目录 -INCLUDE_DIRS=-I/usr/include/x86_64-linux-gnu -I. +INCLUDE_DIRS=-I/usr/include/x86_64-linux-gnu -I. -I./include -I./include/bpf -I./include/helpers -# qemu 命令行参数变量化 -QEMU_CMD=sudo qemu-system-x86_64 -enable-kvm -cpu host -m 2048 -smp 4 -drive file=cirros-0.5.2-x86_64-disk.img,format=qcow2 -boot c -nographic - -CIRROS_IMG_URL=https://gitee.com/nan-shuaibo/cirros/releases/download/0.5.2/cirros-0.5.2-x86_64-disk.img -CIRROS_IMG_FILE=cirros-0.5.2-x86_64-disk.img - -# 定义检查虚拟化支持的命令 -CHECK_VIRT_SUPPORT = [ $$(grep -Eoc '(vmx|svm)' /proc/cpuinfo) -eq 0 ] - -# 定义检查 qemu-system-x86_64 进程是否存在的命令 -CHECK_QEMU_RUNNING = [ "$$(pgrep -f qemu-system-x86_64)" ] +# 帮助函数 +HELPERS_OBJ_DIR := src/helpers +HELPERS_FILES := $(wildcard $(HELPERS_OBJ_DIR)/*.c) +HELPERS_OBJ_FILES := $(patsubst $(HELPERS_OBJ_DIR)/%.c,$(HELPERS_OBJ_DIR)/%.o,$(HELPERS_FILES)) # 默认目标 .PHONY: default @@ -39,27 +31,53 @@ default: bpf # 安装必要的依赖 .PHONY: deps deps: - sudo apt-get update + 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) - sudo apt-get install -y lolcat qemu-kvm wget + linux-tools-$$(uname -r) linux-cloud-tools-$$(uname -r) \ + libpcap-dev gcc-multilib build-essential lolcat qemu-kvm wget + # 生成 vmlinux.h .PHONY: vmlinux vmlinux: bpftool btf dump file /sys/kernel/btf/kvm format c > ./include/vmlinux.h +# 编译helpers目录下的所有.c文件 +$(HELPERS_OBJ_DIR)/%.o: $(HELPERS_OBJ_DIR)/%.c + clang $(CFLAGS) $(INCLUDE_DIRS) -c $< -o $@ + +# 编译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: vmlinux - # 编译BPF程序 - clang $(BPF_CFLAGS) -D__TARGET_ARCH_$(ARCH) $(INCLUDE_DIRS) -c $(APP).bpf.c -o $(APP).bpf.o - # 生成BPF骨架文件 - bpftool gen skeleton ${APP}.bpf.o > $(APP).skel.h - # 编译用户空间应用程序 - clang $(CFLAGS) $(INCLUDE_DIRS) -c $(APP).c -o ${APP}.o - # 将用户空间应用程序与库链接 - clang -Wall $(CFLAGS) ${APP}.o $(LIBS) -o $(notdir $(APP)) - echo "BPF program compiled successfully." +bpf: $(APP).skel.h $(APP).bpf.o ${APP}.o $(HELPERS_OBJ_FILES) $(notdir $(APP)) + + +# qemu 命令行参数变量化 +QEMU_CMD=sudo qemu-system-x86_64 -enable-kvm -cpu host -m 2048 -smp 4 -drive file=cirros-0.5.2-x86_64-disk.img,format=qcow2 -boot c -nographic + +CIRROS_IMG_URL=https://gitee.com/nan-shuaibo/cirros/releases/download/0.5.2/cirros-0.5.2-x86_64-disk.img +CIRROS_IMG_FILE=cirros-0.5.2-x86_64-disk.img + +# 定义检查虚拟化支持的命令 +CHECK_VIRT_SUPPORT = [ $$(grep -Eoc '(vmx|svm)' /proc/cpuinfo) -eq 0 ] + +# 定义检查 qemu-system-x86_64 进程是否存在的命令 +CHECK_QEMU_RUNNING = [ "$$(pgrep -f qemu-system-x86_64)" ] .PHONY: test test: bpf @@ -95,7 +113,7 @@ test: bpf clean: - cd src && rm -f *.o *.skel.h *.bpf.o + rm -f src/*.o src/*.skel.h src/helpers/*.o sudo rm -rf $(notdir $(APP)) include/vmlinux.h temp diff --git a/eBPF_Supermarket/kvm_watcher/README.md b/eBPF_Supermarket/kvm_watcher/README.md index 3c83aa161..c35a4647d 100755 --- a/eBPF_Supermarket/kvm_watcher/README.md +++ b/eBPF_Supermarket/kvm_watcher/README.md @@ -10,6 +10,8 @@ `kvm_watcher`是一款基于eBPF的kvm虚拟机检测工具,其旨在使用户方便快捷在宿主机侧获取kvm虚拟机中的各种信息,报告所有正在运行的guest行为。 +![kvm watcher项目框图](https://gitee.com/nan-shuaibo/image/raw/master/202404251704350.png) + 目前,其实现的功能主要包括: - **[VM Exit 事件分析](./docs/kvm_exit.md)** @@ -116,26 +118,33 @@ Report bugs to . ## 四、代码结构 ``` -├── docs //功能模块说明文档 +├── docs //功能模块说明文档 │ ├── kvm_exit.md │ ├── kvm_hypercall.md │ ├── kvm_irq.md │ ├── kvm_mmu.md │ └── kvm_vcpu.md -├── include //内核态bpf程序 -│ ├── kvm_exits.h -│ ├── kvm_hypercall.h -│ ├── kvm_ioctl.h -│ ├── kvm_irq.h -│ ├── kvm_mmu.h -│ ├── kvm_vcpu.h -│ └── kvm_watcher.h //公共头文件 +├── include +│ ├── bpf //内核态bpf程序 +│ │ ├── kvm_exits.h +│ │ ├── kvm_hypercall.h +│ │ ├── kvm_ioctl.h +│ │ ├── kvm_irq.h +│ │ ├── kvm_mmu.h +│ │ └── kvm_vcpu.h +│ ├── common.h //内核态和用户态公共头文件 +│ └── helpers //用户态帮助函数 +│ ├── trace_helpers.h +│ └── uprobe_helpers.h ├── Makefile //编译脚本 ├── README.md -├── src -│ ├── kvm_watcher.bpf.c //内核态bpf程序入口 -│ └── kvm_watcher.c //用户态bpf程序 -└── temp //临时文件目录 +├── src +│ ├── helpers //用户态帮助函数 +│ │ ├── trace_helpers.c +│ │ └── uprobe_helpers.c +│ ├── kvm_watcher.bpf.c //内核态bpf程序入口 +│ └── kvm_watcher.c //用户态bpf程序 +└── temp //临时文件目录 ``` ## 五、测试 diff --git a/eBPF_Supermarket/kvm_watcher/docs/Visualization_conf.md b/eBPF_Supermarket/kvm_watcher/docs/Visualization_conf.md new file mode 100644 index 000000000..043259e7e --- /dev/null +++ b/eBPF_Supermarket/kvm_watcher/docs/Visualization_conf.md @@ -0,0 +1,151 @@ +# KVM_WATCHER可视化指南 + +​ 结合2023年开源之夏项目《使用 Prometheus 和 Grafana 构建 Linux 内核监控平台(基于 eBPF 程序采集内核数据)》进行可视化。 + +### 一、可视化策略: + +**1.工具介绍:** + +**Prometheus** 是一个开源的系统监控和报警工具,最初由SoundCloud开发,现在是CNCF(Cloud Native Computing Foundation)的一部分。 + +**Grafana** 是一个开源的分析和可视化平台,广泛用于数据可视化监控系统中。它能够与多种数据源集成,包括Prometheus。 + +Prometheus和Grafana常常一起使用,以实现全面的监控和可视化解决方案: + +1. **数据收集**:Prometheus负责从各种服务和应用程序中收集时间序列数据,并将这些数据存储在其时间序列数据库中。 +2. **数据可视化**:Grafana通过Prometheus数据源连接到Prometheus,使用PromQL查询数据,并在仪表盘中进行可视化展示。 +3. **报警**:虽然Prometheus可以独立管理报警,但与Grafana结合使用时,可以在Grafana中直观地配置和管理报警规则,并利用Grafana的通知渠道。 + +​ 通过kvm_watcher工具将虚拟机的各项指标采集出来,并将其放入到Prometheus所支持的Metrics中,利用http服务将采集出的Metrics数据通过端口的形式暴露出来,并将其存储到Prometheus的数据库中。接下来再通过grafana工具对Prometheus数据库中的数据进行可视化渲染,最终可以输出直观的监测结果。 + +**2.可视化流程:** + +![](./images/visualization_platform.png) + +### 二、使用说明: + +环境要求: + +```shell +golang 1.19+ +Docker version 24.0.4+ +``` + +数据格式要求: + +数据的格式必须要满足第一行是各项指标的名称,之后的每一行是数据(且是数字类型),并都以空格分隔。 + +``` +pid tid total_time max_time min_time counts vcpuid +6898 6908 2.0879 0.2376 0.0737 12 0 +6970 6979 0.5860 0.2039 0.1088 4 0 +6970 6980 1.6281 0.2237 0.1169 9 1 +6898 6908 2.0206 0.2355 0.0632 13 0 +``` + +#### **1.安装Golang:** + +```shell +#通过APT包下载go语言 +sudo apt install golang +#查看go版本: +go version +``` + +#### **2.安装docker:** + +```shell +请大家在docker官网的说明文档中,自行安装符合自己linux发行版的docker,链接如下: +https://docs.docker.com/engine/install/ubuntu +#开启docker服务 +sudo service docker start +#若如下指令出现hello-world,则表明docker创建成功 +sudo docker run hello-world +``` + +#### **3.运行可视化工具:** + +在运行可视化工具之前,我们需要先配置需要被监控机器的地址: + +配置文件目录:lmp/eBPF_Visualization/eBPF_prometheus/prom_core/prometheus.yaml + +``` +#全局配置部分,应用于所有的 job 配置 +global: + # 设置全局的抓取间隔,即 Prometheus 将每隔 15 秒收集一次指标 + scrape_interval: 15s + +# 抓取配置部分,用于指定要抓取的目标和相应的设置 +scrape_configs: + # 定义一个 job,命名为 'bpf_collector',这个 job 用于配置 Prometheus 如何抓取数据 + - job_name: 'bpf_collector' + # 指定从目标获取指标的路径,即采集路径 + metrics_path: '/metrics' + # 配置静态目标的部分,其中的目标地址是静态的,不会动态变化 + static_configs: + - targets: ["192.168.79.128:8090"] +``` + +这里我们需要将 - targets: ["192.168.79.128:8090"]配置项的192.168.79.128改为需要被采集的主机IP或者域名。 + +**3.1编译工具** + +```shell +#进入lmp/eBPF_Visualization/eBPF_prometheus目录 + +#执行make指令,编译可视化的go语言工具 +make +``` + +**3.2开启prometheus和grafana容器服务:** + +```shell +#进入lmp/eBPF_Visualization/eBPF_prometheus目录,执行以下操作 +make start_service +``` + +**3.3开启ebpf程序** + +```shell +#进入lmp/eBPF_Visualization/eBPF_prometheus目录,执行以下操作 +#开启ebpf程序,并且向8090端口推送ebpf程序采集的数据,发送给prometheus服务端 +#这里以监测vcpu调度的数据来举例: +#目前-e、-o、-i、-f -m功能的数据格式已经适配了可视化工具,请读者使用以上四种功能的数据来进行可视化展示 +./data-visual collect lmp/eBPF_Supermarket/kvm_watcher/kvm_watcher -o -p [进程号] +``` + +#### **4.可视化工具** + +**4.1metrics:** + +在浏览器中输入http://192.168.79.128:8090/metrics(IP地址是自己需要采集数据的主机地址),就可以查看ebpf程序输出的metrics数据了。 + +![image-20240523144259149](./images/metrics.png) + +**4.2 prometheus** + +在浏览器中输入http://192.168.79.128:9090,就可以进入到prometheus管理界面,在Status/Targets选项中,就可以看到上面的metrics信息了。 + +![image-20240523144553617](./images/prometheus.png) + +**4.3Grafana** + +在浏览器中输入http://192.168.79.128:3000/,就可以进入到Grafana管理界面。 + +点击【Home-Connection-Add new connection】,选择Prometheus,建立与Prometheus服务器的连接: + +![image-20240523145817562](./images/prome-config.png) + +这里的Prometheus server URL为被监测主机的IP+9090端口。 + +进入可视化配置界面: + +![](./images/new-data-source.png) + +选择需要可视化的数据,并点击Run queries,最后点击右上角的Apply,即可生成可视化图像: + +![image-20240523150326003](./images/result.png) + +### 三、说明 + +​ Prometheus和Grafana提供了许多强大的查询和运算功能,大家可以在环境搭建好以后,自己去定制化自己想要的可视化图表。总结一下,在对kvm_watcher进行可视化的时候,必须要注意数据的输出格式,并且要注意涉及的工具版本。 \ No newline at end of file diff --git a/eBPF_Supermarket/kvm_watcher/docs/grafana_kvm_watcher_dashboard.json b/eBPF_Supermarket/kvm_watcher/docs/grafana_kvm_watcher_dashboard.json new file mode 100644 index 000000000..166cc6917 --- /dev/null +++ b/eBPF_Supermarket/kvm_watcher/docs/grafana_kvm_watcher_dashboard.json @@ -0,0 +1,522 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 5, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "cdncx8i00775sa" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cdncx8i00775sa" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"DELAY(us)\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "mmio_delay", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cdncx8i00775sa" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cdncx8i00775sa" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"DELAY\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "irq_delay", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cdncx8i00775sa" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 16 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cdncx8i00775sa" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"MAX_TIME\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "cdncx8i00775sa" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"min_time\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "cdncx8i00775sa" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"max_time\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "C", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "cdncx8i00775sa" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"total_time\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "D", + "useBackend": false + } + ], + "title": "vcpu_load", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cdncx8i00775sa" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cdncx8i00775sa" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"MAX_TIME\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "cdncx8i00775sa" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"MIN_TIME\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "cdncx8i00775sa" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"TOTAL_TIME\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "C", + "useBackend": false + } + ], + "title": "vm_exit", + "type": "timeseries" + } + ], + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "123123", + "uid": "edonx3cyzyyv4d", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/eBPF_Supermarket/kvm_watcher/docs/images/metrics.png b/eBPF_Supermarket/kvm_watcher/docs/images/metrics.png new file mode 100644 index 000000000..f319d8201 Binary files /dev/null and b/eBPF_Supermarket/kvm_watcher/docs/images/metrics.png differ diff --git a/eBPF_Supermarket/kvm_watcher/docs/images/new-data-source.png b/eBPF_Supermarket/kvm_watcher/docs/images/new-data-source.png new file mode 100644 index 000000000..28b3704fa Binary files /dev/null and b/eBPF_Supermarket/kvm_watcher/docs/images/new-data-source.png differ diff --git a/eBPF_Supermarket/kvm_watcher/docs/images/prome-config.png b/eBPF_Supermarket/kvm_watcher/docs/images/prome-config.png new file mode 100644 index 000000000..11140788e Binary files /dev/null and b/eBPF_Supermarket/kvm_watcher/docs/images/prome-config.png differ diff --git a/eBPF_Supermarket/kvm_watcher/docs/images/prometheus.png b/eBPF_Supermarket/kvm_watcher/docs/images/prometheus.png new file mode 100644 index 000000000..a7cb7b997 Binary files /dev/null and b/eBPF_Supermarket/kvm_watcher/docs/images/prometheus.png differ diff --git a/eBPF_Supermarket/kvm_watcher/docs/images/result.png b/eBPF_Supermarket/kvm_watcher/docs/images/result.png new file mode 100644 index 000000000..3ad019d48 Binary files /dev/null and b/eBPF_Supermarket/kvm_watcher/docs/images/result.png differ diff --git a/eBPF_Supermarket/kvm_watcher/docs/images/visualization_platform.png b/eBPF_Supermarket/kvm_watcher/docs/images/visualization_platform.png new file mode 100644 index 000000000..039052268 Binary files /dev/null and b/eBPF_Supermarket/kvm_watcher/docs/images/visualization_platform.png differ diff --git a/eBPF_Supermarket/kvm_watcher/docs/kvm_exit.md b/eBPF_Supermarket/kvm_watcher/docs/kvm_exit.md index bb84b25ec..65f954df8 100644 --- a/eBPF_Supermarket/kvm_watcher/docs/kvm_exit.md +++ b/eBPF_Supermarket/kvm_watcher/docs/kvm_exit.md @@ -2,6 +2,8 @@ 考虑到频繁的虚拟机退出事件可能会导致性能问题,kvm_watcher中的kvm_exit子功能通过显示详细的退出原因和在一台主机上运行的所有vm的每个虚拟机的vcpu上的退出计数及处理时延,可以捕获和分析vm exit事件,该工具旨在定位频繁退出的原因(如EPT_VIOLATION、EPT_MISCONFIG、PML_FULL等),在vm exit基础上,如果kvm这个时候因为某些原因,需要退出到用户态的hypervisor(比如qemu),kvm就要设置KVM_EXIT_XXX,此工具包含了这两部分exit reason。 +![kvm exit](https://gitee.com/nan-shuaibo/image/raw/master/202404251707665.png) + ## 原理介绍 ### VMX 操作模式 @@ -46,7 +48,7 @@ ## 示例输出 4391为主机上的虚拟机进程,4508、4509、4510...分别是虚拟机中的vcpu子进程,每隔两秒输出虚拟机中产生的exit事件及其处理延时等信息。 - +结果会以进程号(VM的唯一标识)以及线程号(VM中每个VCPU的唯一标识)的优先级依次从小到大的顺序输出。 ``` ubuntu@rd350x:~/nans/lmp/eBPF_Supermarket/kvm_watcher$ sudo ./kvm_watcher -e @@ -102,5 +104,4 @@ pid tid total_time max_time min_time counts re - **VM Exit 原因统计**:记录并展示触发 VM Exit 的具体原因,帮助用户理解 VM Exit 发生的上下文和背景。 - **VM Exit 延时分析**:统计每次 VM Exit 处理的最大、最小和总共延时,为性能分析提供量化数据。 - **VM Exit 次数计数**:计算每种类型的 VM Exit 发生的次数,帮助识别最频繁的性能瓶颈。 -- **PID、TID号**:其中PID为主机侧的虚拟机进程号,TID为虚拟机内部的vcpu**的进程号** - \ No newline at end of file +- **PID、TID号**:其中PID为主机侧的虚拟机进程号,TID为虚拟机内部的vcpu**的进程号** \ No newline at end of file diff --git a/eBPF_Supermarket/kvm_watcher/docs/kvm_irq.md b/eBPF_Supermarket/kvm_watcher/docs/kvm_irq.md index 9a7e2a801..5f30152f4 100644 --- a/eBPF_Supermarket/kvm_watcher/docs/kvm_irq.md +++ b/eBPF_Supermarket/kvm_watcher/docs/kvm_irq.md @@ -4,6 +4,8 @@ kvm watcher中的kvm irq子功能模块可以对kvm中的虚拟化中断事件的实时监控和分析能力,可以捕获和记录各种中断事件,支持监控传统的PIC中断、高级的IOAPIC中断以及基于消息的MSI中断,覆盖了KVM虚拟化环境中的主要中断类型。对于每个捕获的中断事件,记录详细信息,包括中断类型、中断注入延时、引脚号、触发方式、目标LAPIC的ID、向量号以及是否被屏蔽等关键数据。 +![kvm_irq](https://gitee.com/nan-shuaibo/image/raw/master/202404251710847.png) + ## 原理介绍 x86平台主要使用的中断类型有pic、apic及msi中断,在多核系统下的apic结构图如下所示,每个cpu有一个lapic,外部中断通过ioapic转发到lapic,如果是msi中断,则绕过了io apic直接发给lapic。 @@ -14,48 +16,48 @@ KVM_CREATE_IRQCHIP的ioctl用于在虚拟机初始化阶段创建中断请求芯 ``` int kvm_set_routing_entry(struct kvm *kvm, - struct kvm_kernel_irq_routing_entry *e, - const struct kvm_irq_routing_entry *ue) + struct kvm_kernel_irq_routing_entry *e, + const struct kvm_irq_routing_entry *ue) { - switch (ue->type) { - case KVM_IRQ_ROUTING_IRQCHIP: //中断路由芯片 - if (irqchip_split(kvm)) - return -EINVAL; - e->irqchip.pin = ue->u.irqchip.pin;//设置中断芯片引脚 - switch (ue->u.irqchip.irqchip) { - case KVM_IRQCHIP_PIC_SLAVE: - e->irqchip.pin += PIC_NUM_PINS / 2; //从片引脚 - fallthrough; - case KVM_IRQCHIP_PIC_MASTER: - if (ue->u.irqchip.pin >= PIC_NUM_PINS / 2) - return -EINVAL; - //// 设置处理 PIC 中断的回调函数 - e->set = kvm_set_pic_irq; - break; - case KVM_IRQCHIP_IOAPIC: - if (ue->u.irqchip.pin >= KVM_IOAPIC_NUM_PINS) - return -EINVAL; - // 设置处理 IOPIC 中断的回调函数 - e->set = kvm_set_ioapic_irq; - break; - default: - return -EINVAL; - } - e->irqchip.irqchip = ue->u.irqchip.irqchip; - break; - case KVM_IRQ_ROUTING_MSI: - // 设置处理 MSI 中断的回调函数 - e->set = kvm_set_msi; - e->msi.address_lo = ue->u.msi.address_lo; - e->msi.address_hi = ue->u.msi.address_hi; - e->msi.data = ue->u.msi.data; - - if (kvm_msi_route_invalid(kvm, e)) - return -EINVAL; - break; + switch (ue->type) { + case KVM_IRQ_ROUTING_IRQCHIP: //中断路由芯片 + if (irqchip_split(kvm)) + return -EINVAL; + e->irqchip.pin = ue->u.irqchip.pin;//设置中断芯片引脚 + switch (ue->u.irqchip.irqchip) { + case KVM_IRQCHIP_PIC_SLAVE: + e->irqchip.pin += PIC_NUM_PINS / 2; //从片引脚 + fallthrough; + case KVM_IRQCHIP_PIC_MASTER: + if (ue->u.irqchip.pin >= PIC_NUM_PINS / 2) + return -EINVAL; + //// 设置处理 PIC 中断的回调函数 + e->set = kvm_set_pic_irq; + break; + case KVM_IRQCHIP_IOAPIC: + if (ue->u.irqchip.pin >= KVM_IOAPIC_NUM_PINS) + return -EINVAL; + // 设置处理 IOPIC 中断的回调函数 + e->set = kvm_set_ioapic_irq; + break; + default: + return -EINVAL; + } + e->irqchip.irqchip = ue->u.irqchip.irqchip; + break; + case KVM_IRQ_ROUTING_MSI: + // 设置处理 MSI 中断的回调函数 + e->set = kvm_set_msi; + e->msi.address_lo = ue->u.msi.address_lo; + e->msi.address_hi = ue->u.msi.address_hi; + e->msi.data = ue->u.msi.data; + + if (kvm_msi_route_invalid(kvm, e)) + return -EINVAL; + break; ..... - return 0; + return 0; } ``` @@ -75,22 +77,22 @@ KVM_CREATE_IRQCHIP用于虚拟机向VMM的虚拟apic发送中断请求,再有V * > 0 中断成功送达的 CPU 数量 */ int kvm_set_irq(struct kvm *kvm, int irq_source_id, u32 irq, int level, - bool line_status) + bool line_status) { - struct kvm_kernel_irq_routing_entry irq_set[KVM_NR_IRQCHIPS]; - .... + struct kvm_kernel_irq_routing_entry irq_set[KVM_NR_IRQCHIPS]; + .... - while (i--) { - int r; - r = irq_set[i].set(&irq_set[i], kvm, irq_source_id, level, - line_status); - if (r < 0) - continue; + while (i--) { + int r; + r = irq_set[i].set(&irq_set[i], kvm, irq_source_id, level, + line_status); + if (r < 0) + continue; - ret = r + ((ret < 0) ? 0 : ret); - } + ret = r + ((ret < 0) ? 0 : ret); + } - return ret; + return ret; } ``` @@ -101,7 +103,7 @@ KVM_CREATE_IRQCHIP用于虚拟机向VMM的虚拟apic发送中断请求,再有V 其中ioapic的回调函数kvm_set_ioapic_irq依次调用kvm_ioapic_set_irq、ioapic_set_irq最后调用ioapic_service函数,ioapic_service主要是找到中断的重映射表,然后查找中断的目的地信息并转发到对应vcpu的lapic去处理。然后会调用kvm_irq_delivery_to_apic负责将中断分发给lapic。 > 中断虚拟化详细介绍可以参考:[kvm中断虚拟化 ](https://blog.csdn.net/zgy666/article/details/105456569) -> [内核虚拟化:虚拟中断注入](https://blog.csdn.net/weixin_46324627/article/details/136661252?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22136661252%22%2C%22source%22%3A%22weixin_46324627%22%7D) +> [内核虚拟化:虚拟中断注入](https://blog.csdn.net/weixin_46324627/article/details/136661252?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22136661252%22%2C%22source%22%3A%22weixin_46324627%22%7D) ## 挂载点 diff --git a/eBPF_Supermarket/kvm_watcher/docs/kvm_vcpu.md b/eBPF_Supermarket/kvm_watcher/docs/kvm_vcpu.md index e3733231b..59eec0e20 100644 --- a/eBPF_Supermarket/kvm_watcher/docs/kvm_vcpu.md +++ b/eBPF_Supermarket/kvm_watcher/docs/kvm_vcpu.md @@ -2,12 +2,14 @@ ## 概述 -kvm watcher中的kvm vcpu子功能模块是设计用于监控和分析虚拟化环境中虚拟 CPU (VCPU) 活动的工具,它通过精确记录 VCPU 的唤醒/挂起事件、halt poll 时间的变化,以及 KVM 虚拟机在热迁移过程中产生的脏页信息,为优化虚拟机性能、提高系统响应速度、提供数据支持。 +kvm watcher中的kvm vcpu子功能模块是设计用于监控和分析虚拟化环境中虚拟 CPU (VCPU) 活动的工具,它通过精确记录 VCPU 的唤醒/挂起事件、halt poll 时间的变化,虚拟cpu进调度和出调度的时间记录,以及 KVM 虚拟机在热迁移过程中产生的脏页信息,为优化虚拟机性能、提高系统响应速度、提供数据支持。 ## 原理介绍 ### wakeup、暂停轮询 +![kvm vcpu](https://gitee.com/nan-shuaibo/image/raw/master/202404251709557.png) + KVM 暂停轮询系统是 KVM 内的一项功能,其在某些情况下可以通过在vCPU 放弃运行并让出后,在主机中进行一段时间的轮询来降低虚拟机的延迟。简而言之,当vCPU 放弃运行(即执行 cede 操作)或在 PowerPC 中,当一个虚拟核心(vcore)的所有vCPU 都放弃运行时,主机内核会在将 CPU 让给调度程序之前,通过轮询等待唤醒条件。 轮询在某些情况下提供了延迟优势,尤其是在虚拟机可以非常快速地再次运行的情况下。这至少可以通过减少通过调度程序的开销来节省一些时间,通常在几微秒的数量级上,尽管性能优势取决于工作负载。在轮询间隔内,如果没有唤醒源到达,或者运行队列上有其他可运行的任务,则会调用调度程序。因此,在具有非常短唤醒周期的工作负载中,halt轮询特别有用,因为最小化了halt轮询的时间,同时可以避免调用调度程序的时间花费。 @@ -16,8 +18,13 @@ KVM 暂停轮询系统是 KVM 内的一项功能,其在某些情况下可以 ### dirty page +![dirty page](https://gitee.com/nan-shuaibo/image/raw/master/202404251709559.png) + 在虚拟化环境中,脏页指的是自上次同步以来已经被修改的内存页。特别是在虚拟机热迁移过程中,源虚拟机上的内存页在复制到目标虚拟机的同时仍然处于活动状态,任何在此过程中对这些页的修改都会导致脏页的产生。监控这些脏页对于优化热迁移过程至关重要,因为过多的脏页生成可能会导致迁移延迟,甚至影响到虚拟机的运行性能。此监控功能特别适用于虚拟机热迁移的场景,其中脏页的精确监控和管理可以显著优化迁移过程。 +### load_vcpu + +加载虚拟 CPU(vCPU)是虚拟化环境中的一个重要步骤,它涉及为虚拟机创建和配置虚拟 CPU 实例,并将其加载到虚拟化平台中的运行环境中。加载 vCPU 将为客户机提供计算资源,允许其执行操作系统和应用程序代码。每个 vCPU 实例代表了一个独立的处理器核心,可以并行执行客户机指令。虚拟化环境中的多个 vCPU 可以并发执行客户机代码,从而支持虚拟机中的多任务和并发执行。加载多个 vCPU 允许虚拟机同时运行多个线程或进程。通过合理地分配物理资源并加载 vCPU,KVM可以优化虚拟机的性能和资源利用率。 ## 挂载点 ### wakeup @@ -39,6 +46,12 @@ KVM 暂停轮询系统是 KVM 内的一项功能,其在某些情况下可以 | :----- | :---------------------- | | kprobe | mark_page_dirty_in_slot | +### load_vcpu + +| 类型 | 名称 | +| :----- | :----------------------| +| kprobe | vmx_vcpu_load | +| kprobe | vmx_vcpu_put | ## 示例输出 ### wakeup @@ -124,7 +137,22 @@ PID GFN REL_GFN SLOT_ID COUNTS 630632 f0122 122 3 61 .... ``` +### load_vcpu +``` +sudo ./kvm_watcher -o +TIME:15:23:05 +pid tid total_time max_time min_time counts vcpuid pcpuid +------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ +49214 49224 1.2344 0.3670 0.0346 9 1 2 +49214 49225 0.5978 0.2171 0.1820 3 2 0 +49062 49072 2.4008 0.2656 0.0806 14 0 4 +49214 49223 4.5310 0.2794 0.0217 66 0 7 +54504 54514 6.9243 0.8872 0.0235 34 1 10 +49145 49166 2.8076 1.1653 0.0689 10 1 9 +54504 54513 3.8860 0.3099 0.0210 30 0 0 +49145 49165 1.4737 0.4106 0.0690 8 0 12 +``` ## 参数介绍 ### dirty page @@ -136,4 +164,15 @@ PID GFN REL_GFN SLOT_ID COUNTS - **REL_GFN**: 相对于 GFN 的偏移量。 - **NPAGES**: 内存槽的页面数量。 - **USERSPACE_ADDR**: 触发脏页的用户空间地址。 -- **SLOT_ID**: 内存插槽标识,指示哪个内存区域包含了脏页。 \ No newline at end of file +- **SLOT_ID**: 内存插槽标识,指示哪个内存区域包含了脏页。 + +### load_vcpu + +- **TIME(ms)**: 事件发生的时间,以毫秒为单位。 +- **PID/TID**: 进程或线程的标识符。 +- **total_time**: 2秒内调度vcpu的总时间。 +- **max_time**: 2秒内调度vcpu的最大时间。 +- **min_time**: 2秒内调度vcpu的最小时间。 +- **counts**: 2秒内调度vcpu的次数。 +- **vcpuid**: 虚拟CPU号。 +- **pcpuid**: vCPU对应绑定的物理CPU号。 \ No newline at end of file diff --git a/eBPF_Supermarket/kvm_watcher/include/bpf/container.h b/eBPF_Supermarket/kvm_watcher/include/bpf/container.h new file mode 100644 index 000000000..5271054ef --- /dev/null +++ b/eBPF_Supermarket/kvm_watcher/include/bpf/container.h @@ -0,0 +1,136 @@ +// Copyright 2023 The LMP Authors. +// +// 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/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 counting container sys_entry/sys_exit info. + +#ifndef __CONTAINER_H +#define __CONTAINER_H + +#include "common.h" +#include "vmlinux.h" +#include +#include +#include +#define MAX_NODENAME_LEN 64 +struct { + __uint(type,BPF_MAP_TYPE_HASH); + __uint(max_entries, 8192); + __type(key, pid_t); + __type(value, u64); +}time_info SEC(".maps"); + +struct { + __uint(type,BPF_MAP_TYPE_HASH); + __uint(max_entries, 8192); + __type(key, pid_t); + __type(value, u64); +}id SEC(".maps"); + +struct { + __uint(type,BPF_MAP_TYPE_HASH); + __uint(max_entries, 8192); + __type(key, pid_t); + __type(value,struct container_id); +}container_id_map SEC(".maps"); + + +static int trace_container_sys_entry(struct trace_event_raw_sys_enter *args){ + u64 st = bpf_ktime_get_ns(); + pid_t pid = bpf_get_current_pid_tgid(); + u64 syscall_id = (u64)args->id; + bpf_map_update_elem(&time_info,&pid,&st,BPF_ANY); + bpf_map_update_elem(&id,&pid,&syscall_id,BPF_ANY); + return 0; +} +static int trace_container_sys_exit(struct trace_event_raw_sys_exit *args,void *rb,struct common_event *e){ + u64 exit_time = bpf_ktime_get_ns(); + pid_t pid = bpf_get_current_pid_tgid(); + u64 delay,start_time,syscallid; + u64 *st = bpf_map_lookup_elem(&time_info,&pid); + if( st !=0){ + start_time = *st; + delay = (exit_time - start_time)/1000; + bpf_map_delete_elem(&time_info, &pid); + }else{ + return 0; + } + u64 *sc_id = bpf_map_lookup_elem(&id,&pid); + if( sc_id != 0){ + syscallid = *sc_id; + bpf_map_delete_elem(&id, &pid); + }else{ + return 0; + } + const void *contain_id = bpf_map_lookup_elem(&container_id_map,&pid); + if(contain_id != NULL){ + bpf_printk("hostname=%s\n",contain_id); + }else{ + return 0; + } + RESERVE_RINGBUF_ENTRY(rb, e); + e->syscall_data.delay = delay; + bpf_get_current_comm(&e->syscall_data.comm, sizeof(e->syscall_data.comm)); + e->syscall_data.pid = pid; + bpf_probe_read_kernel_str(&(e->syscall_data.container_id),sizeof(e->syscall_data.container_id),contain_id); + e->syscall_data.syscall_id = syscallid; + bpf_ringbuf_submit(e, 0); + return 0; +} + +struct data_t { + char nodename[MAX_NODENAME_LEN]; +}; +static bool is_container_task(const volatile char hostname[MAX_NODENAME_LEN]){ + struct task_struct *task; + struct nsproxy *ns; + struct uts_namespace *uts; + struct data_t data = {}; + // 获取当前任务的 task_struct + task = (struct task_struct *)bpf_get_current_task(); + + // 获取 nsproxy + bpf_probe_read_kernel(&ns, sizeof(ns), &task->nsproxy); + if (!ns) { + return false; + } + + // 获取 uts_namespace + bpf_probe_read_kernel(&uts, sizeof(uts), &ns->uts_ns); + if (!uts) { + return false; + } + // 读取主机名 + bpf_probe_read_kernel_str(&data.nodename, sizeof(data.nodename), uts->name.nodename); + // 打印主机名 + bool is_equal = true; + for(int i = 0;i #include @@ -135,7 +135,7 @@ static int trace_kvm_entry() { return 0; } -static int trace_kvm_userspace_entry(struct kvm_vcpu *vcpu) { +static int trace_kvm_vcpu_ioctl() { pid_t tid = (u32)bpf_get_current_pid_tgid(); u64 ts = bpf_ktime_get_ns(); bpf_map_update_elem(&userspace_exit_times, &tid, &ts, BPF_ANY); diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_hypercall.h b/eBPF_Supermarket/kvm_watcher/include/bpf/kvm_hypercall.h similarity index 97% rename from eBPF_Supermarket/kvm_watcher/include/kvm_hypercall.h rename to eBPF_Supermarket/kvm_watcher/include/bpf/kvm_hypercall.h index ab9b26a52..723200827 100644 --- a/eBPF_Supermarket/kvm_watcher/include/kvm_hypercall.h +++ b/eBPF_Supermarket/kvm_watcher/include/bpf/kvm_hypercall.h @@ -19,7 +19,7 @@ #ifndef __KVM_HYPERCALL_H #define __KVM_HYPERCALL_H -#include "kvm_watcher.h" +#include "common.h" #include "vmlinux.h" #include #include @@ -53,7 +53,7 @@ struct { __type(value, u32); } hc_count SEC(".maps"); -static int entry_emulate_hypercall(struct kvm_vcpu *vcpu, void *rb, +static int trace_emulate_hypercall(struct kvm_vcpu *vcpu, void *rb, struct common_event *e) { u32 pid = bpf_get_current_pid_tgid() >> 32; u64 nr, a0, a1, a2, a3; diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_ioctl.h b/eBPF_Supermarket/kvm_watcher/include/bpf/kvm_ioctl.h similarity index 84% rename from eBPF_Supermarket/kvm_watcher/include/kvm_ioctl.h rename to eBPF_Supermarket/kvm_watcher/include/bpf/kvm_ioctl.h index b3cda58b1..6b619e437 100644 --- a/eBPF_Supermarket/kvm_watcher/include/kvm_ioctl.h +++ b/eBPF_Supermarket/kvm_watcher/include/bpf/kvm_ioctl.h @@ -19,24 +19,11 @@ #ifndef __KVM_IOCTL_H #define __KVM_IOCTL_H -#include "kvm_watcher.h" +#include "common.h" #include "vmlinux.h" -#include #include #include #include -#include - -#define KVMIO 0xAE -#define KVM_CREATE_VM _IO(KVMIO, 0x01) /* returns a VM fd */ -#define KVM_CREATE_VCPU _IO(KVMIO, 0x41) -#define KVM_GET_VCPU_EVENTS _IOR(KVMIO, 0x9f, struct kvm_vcpu_events) -#define KVM_SET_VCPU_EVENTS _IOW(KVMIO, 0xa0, struct kvm_vcpu_events) -#define KVM_SET_USER_MEMORY_REGION \ - _IOW(KVMIO, 0x46, struct kvm_userspace_memory_region) -#define KVM_TRANSLATE _IOWR(KVMIO, 0x85, struct kvm_translation) -#define KVM_INTERRUPT _IOW(KVMIO, 0x86, struct kvm_interrupt) -#define KVM_RUN _IO(KVMIO, 0x80) static int trace_kvm_ioctl(struct trace_event_raw_sys_enter *args) { int fd = (int)args->args[0]; diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_irq.h b/eBPF_Supermarket/kvm_watcher/include/bpf/kvm_irq.h similarity index 99% rename from eBPF_Supermarket/kvm_watcher/include/kvm_irq.h rename to eBPF_Supermarket/kvm_watcher/include/bpf/kvm_irq.h index fa1fda75c..2325f2467 100644 --- a/eBPF_Supermarket/kvm_watcher/include/kvm_irq.h +++ b/eBPF_Supermarket/kvm_watcher/include/bpf/kvm_irq.h @@ -18,7 +18,7 @@ #ifndef __KVM_IRQ_H #define __KVM_IRQ_H -#include "kvm_watcher.h" +#include "common.h" #include "vmlinux.h" #include #include diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_mmu.h b/eBPF_Supermarket/kvm_watcher/include/bpf/kvm_mmu.h similarity index 97% rename from eBPF_Supermarket/kvm_watcher/include/kvm_mmu.h rename to eBPF_Supermarket/kvm_watcher/include/bpf/kvm_mmu.h index ceea6d10d..45d5c1c52 100644 --- a/eBPF_Supermarket/kvm_watcher/include/kvm_mmu.h +++ b/eBPF_Supermarket/kvm_watcher/include/bpf/kvm_mmu.h @@ -19,7 +19,7 @@ #ifndef __KVM_MMU_H #define __KVM_MMU_H -#include "kvm_watcher.h" +#include "common.h" #include "vmlinux.h" #include #include @@ -60,16 +60,18 @@ static int trace_tdp_page_fault(struct kvm_vcpu *vcpu, struct common_event *e) { u64 addr; bpf_probe_read_kernel(&addr, sizeof(u64), &fault->addr); - u64 *ts; - ts = bpf_map_lookup_elem(&pf_delay, &addr); - if (!ts) { - return 0; - } u32 *count; u32 new_count = 1; u32 error_code; u64 hva, pfn; bpf_probe_read_kernel(&error_code, sizeof(u32), &fault->error_code); + u64 *ts; + ts = bpf_map_lookup_elem(&pf_delay, &addr); + if (!ts) { + int a = *ts; + bpf_printk("trace_tdp_page_fault:ts = %d", a); + return 0; + } bpf_probe_read_kernel(&hva, sizeof(u64), &fault->hva); bpf_probe_read_kernel(&pfn, sizeof(u64), &fault->pfn); short memslot_id = BPF_CORE_READ(fault, slot, id); diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_vcpu.h b/eBPF_Supermarket/kvm_watcher/include/bpf/kvm_vcpu.h similarity index 99% rename from eBPF_Supermarket/kvm_watcher/include/kvm_vcpu.h rename to eBPF_Supermarket/kvm_watcher/include/bpf/kvm_vcpu.h index abf6f3f5a..65ef18a54 100644 --- a/eBPF_Supermarket/kvm_watcher/include/kvm_vcpu.h +++ b/eBPF_Supermarket/kvm_watcher/include/bpf/kvm_vcpu.h @@ -19,7 +19,7 @@ #ifndef __KVM_VCPU_H #define __KVM_VCPU_H -#include "kvm_watcher.h" +#include "common.h" #include "vmlinux.h" #include #include diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_watcher.h b/eBPF_Supermarket/kvm_watcher/include/common.h similarity index 70% rename from eBPF_Supermarket/kvm_watcher/include/kvm_watcher.h rename to eBPF_Supermarket/kvm_watcher/include/common.h index 6d5e34e53..930821f2b 100644 --- a/eBPF_Supermarket/kvm_watcher/include/kvm_watcher.h +++ b/eBPF_Supermarket/kvm_watcher/include/common.h @@ -19,6 +19,49 @@ #ifndef __KVM_WATCHER_H #define __KVM_WATCHER_H +#define SET_KP_OR_FENTRY_LOAD(function_name, module_name) \ + do { \ + if (fentry_can_attach(#function_name, #module_name)) { \ + bpf_program__set_autoload(skel->progs.fentry_##function_name, \ + true); \ + } else { \ + bpf_program__set_autoload(skel->progs.kp_##function_name, true); \ + } \ + } while (0) + +static const char binary_path[] = "/bin/qemu-system-x86_64"; +#define __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe) \ + do { \ + LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts, .func_name = #sym_name, \ + .retprobe = is_retprobe); \ + skel->links.prog_name = bpf_program__attach_uprobe_opts( \ + skel->progs.prog_name, env.vm_pid, binary_path, 0, &uprobe_opts); \ + } while (false) + +#define __CHECK_PROGRAM(skel, prog_name) \ + do { \ + if (!skel->links.prog_name) { \ + perror("no program attached for " #prog_name); \ + return -errno; \ + } \ + } while (false) + +#define __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, is_retprobe) \ + do { \ + __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe); \ + __CHECK_PROGRAM(skel, prog_name); \ + } while (false) + +#define ATTACH_UPROBE(skel, sym_name, prog_name) \ + __ATTACH_UPROBE(skel, sym_name, prog_name, false) +#define ATTACH_URETPROBE(skel, sym_name, prog_name) \ + __ATTACH_UPROBE(skel, sym_name, prog_name, true) + +#define ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name) \ + __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, false) +#define ATTACH_URETPROBE_CHECKED(skel, sym_name, prog_name) \ + __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, true) + #define TASK_COMM_LEN 16 #define KVM_MEM_LOG_DIRTY_PAGES (1UL << 0) @@ -51,11 +94,25 @@ #define PFERR_RSVD_MASK (1UL << 3) // mmio + // 定时器模式 #define APIC_LVT_TIMER_ONESHOT (0 << 17) // 单次触发 #define APIC_LVT_TIMER_PERIODIC (1 << 17) // 周期性触发模式 #define APIC_LVT_TIMER_TSCDEADLINE (2 << 17) // TSC 截止模式 +// IOCTL +#include +#define KVMIO 0xAE +#define KVM_CREATE_VM _IO(KVMIO, 0x01) +#define KVM_CREATE_VCPU _IO(KVMIO, 0x41) +#define KVM_GET_VCPU_EVENTS _IOR(KVMIO, 0x9f, struct kvm_vcpu_events) +#define KVM_SET_VCPU_EVENTS _IOW(KVMIO, 0xa0, struct kvm_vcpu_events) +#define KVM_SET_USER_MEMORY_REGION \ + _IOW(KVMIO, 0x46, struct kvm_userspace_memory_region) +#define KVM_TRANSLATE _IOWR(KVMIO, 0x85, struct kvm_translation) +#define KVM_INTERRUPT _IOW(KVMIO, 0x86, struct kvm_interrupt) +#define KVM_RUN _IO(KVMIO, 0x80) + #define PRINT_USAGE_ERR() \ do { \ fprintf(stderr, "Please specify exactly one option from %s.\n", \ @@ -134,7 +191,9 @@ struct exit_value { __u32 count; __u32 pad; }; - +struct container_id{ + char container_id[20]; +}; struct dirty_page_info { __u64 gfn; __u64 rel_gfn; @@ -176,6 +235,7 @@ struct process { char comm[TASK_COMM_LEN]; }; + enum EventType { NONE_TYPE, VCPU_WAKEUP, @@ -188,6 +248,7 @@ enum EventType { IRQ_INJECT, HYPERCALL, IOCTL, + CONTAINER_SYSCALL, TIMER, } event_type; @@ -286,6 +347,14 @@ struct common_event { __u32 vcpu_id; // HYPERCALL 特有成员 } hypercall_data; + + struct{ + __u64 pid; + __u64 syscall_id; + __u64 delay; + char comm[20]; + char container_id[20]; + } syscall_data; }; }; diff --git a/eBPF_Supermarket/Stack_Analyser/include/trace_helpers.h b/eBPF_Supermarket/kvm_watcher/include/helpers/trace_helpers.h similarity index 76% rename from eBPF_Supermarket/Stack_Analyser/include/trace_helpers.h rename to eBPF_Supermarket/kvm_watcher/include/helpers/trace_helpers.h index 171bc4ee2..052e278ac 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/trace_helpers.h +++ b/eBPF_Supermarket/kvm_watcher/include/helpers/trace_helpers.h @@ -4,11 +4,11 @@ #include -#define NSEC_PER_SEC 1000000000ULL +#define NSEC_PER_SEC 1000000000ULL struct ksym { - const char *name; - unsigned long addr; + const char *name; + unsigned long addr; }; struct ksyms; @@ -16,15 +16,15 @@ struct ksyms; struct ksyms *ksyms__load(void); void ksyms__free(struct ksyms *ksyms); const struct ksym *ksyms__map_addr(const struct ksyms *ksyms, - unsigned long addr); + unsigned long addr); const struct ksym *ksyms__get_symbol(const struct ksyms *ksyms, - const char *name); + const char *name); struct sym { - const char *name; - unsigned long start; - unsigned long size; - unsigned long offset; + const char *name; + unsigned long start; + unsigned long size; + unsigned long offset; }; struct syms; @@ -33,8 +33,9 @@ struct syms *syms__load_pid(int tgid); struct syms *syms__load_file(const char *fname); void syms__free(struct syms *syms); const struct sym *syms__map_addr(const struct syms *syms, unsigned long addr); -const struct sym *syms__map_addr_dso(const struct syms *syms, unsigned long addr, - char **dso_name, unsigned long *dso_offset); +const struct sym *syms__map_addr_dso(const struct syms *syms, + unsigned long addr, char **dso_name, + unsigned long *dso_offset); struct syms_cache; @@ -43,22 +44,22 @@ struct syms *syms_cache__get_syms(struct syms_cache *syms_cache, int tgid); void syms_cache__free(struct syms_cache *syms_cache); struct partition { - char *name; - unsigned int dev; + char *name; + unsigned int dev; }; struct partitions; struct partitions *partitions__load(void); void partitions__free(struct partitions *partitions); -const struct partition * -partitions__get_by_dev(const struct partitions *partitions, unsigned int dev); -const struct partition * -partitions__get_by_name(const struct partitions *partitions, const char *name); +const struct partition *partitions__get_by_dev( + const struct partitions *partitions, unsigned int dev); +const struct partition *partitions__get_by_name( + const struct partitions *partitions, const char *name); void print_log2_hist(unsigned int *vals, int vals_size, const char *val_type); void print_linear_hist(unsigned int *vals, int vals_size, unsigned int base, - unsigned int step, const char *val_type); + unsigned int step, const char *val_type); unsigned long long get_ktime_ns(void); diff --git a/eBPF_Supermarket/kvm_watcher/include/helpers/uprobe_helpers.h b/eBPF_Supermarket/kvm_watcher/include/helpers/uprobe_helpers.h new file mode 100644 index 000000000..d33f7c1cf --- /dev/null +++ b/eBPF_Supermarket/kvm_watcher/include/helpers/uprobe_helpers.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +/* Copyright (c) 2021 Google LLC. */ +#ifndef __UPROBE_HELPERS_H +#define __UPROBE_HELPERS_H + +#include +#include +#include + +int get_pid_binary_path(pid_t pid, char *path, size_t path_sz); +int get_pid_lib_path(pid_t pid, const char *lib, char *path, size_t path_sz); +int resolve_binary_path(const char *binary, pid_t pid, char *path, + size_t path_sz); +off_t get_elf_func_offset(const char *path, const char *func); +Elf *open_elf(const char *path, int *fd_close); +Elf *open_elf_by_fd(int fd); +void close_elf(Elf *e, int fd_close); + +#endif /* __UPROBE_HELPERS_H */ diff --git a/eBPF_Supermarket/kvm_watcher/src/helpers/trace_helpers.c b/eBPF_Supermarket/kvm_watcher/src/helpers/trace_helpers.c new file mode 100644 index 000000000..72b78524b --- /dev/null +++ b/eBPF_Supermarket/kvm_watcher/src/helpers/trace_helpers.c @@ -0,0 +1,1182 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +// Copyright (c) 2020 Wenbo Zhang +// +// Based on ksyms improvements from Andrii Nakryiko, add more helpers. +// 28-Feb-2020 Wenbo Zhang Created this. +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "trace_helpers.h" +#include "uprobe_helpers.h" + +#define min(x, y) \ + ({ \ + typeof(x) _min1 = (x); \ + typeof(y) _min2 = (y); \ + (void)(&_min1 == &_min2); \ + _min1 < _min2 ? _min1 : _min2; \ + }) + +#define DISK_NAME_LEN 32 + +#define MINORBITS 20 +#define MINORMASK ((1U << MINORBITS) - 1) + +#define MKDEV(ma, mi) (((ma) << MINORBITS) | (mi)) + +struct ksyms { + struct ksym *syms; + int syms_sz; + int syms_cap; + char *strs; + int strs_sz; + int strs_cap; +}; + +static int ksyms__add_symbol(struct ksyms *ksyms, const char *name, + unsigned long addr) { + size_t new_cap, name_len = strlen(name) + 1; + struct ksym *ksym; + void *tmp; + + if (ksyms->strs_sz + name_len > ksyms->strs_cap) { + new_cap = ksyms->strs_cap * 4 / 3; + if (new_cap < ksyms->strs_sz + name_len) + new_cap = ksyms->strs_sz + name_len; + if (new_cap < 1024) + new_cap = 1024; + tmp = realloc(ksyms->strs, new_cap); + if (!tmp) + return -1; + ksyms->strs = tmp; + ksyms->strs_cap = new_cap; + } + if (ksyms->syms_sz + 1 > ksyms->syms_cap) { + new_cap = ksyms->syms_cap * 4 / 3; + if (new_cap < 1024) + new_cap = 1024; + tmp = realloc(ksyms->syms, sizeof(*ksyms->syms) * new_cap); + if (!tmp) + return -1; + ksyms->syms = tmp; + ksyms->syms_cap = new_cap; + } + + ksym = &ksyms->syms[ksyms->syms_sz]; + /* while constructing, re-use pointer as just a plain offset */ + ksym->name = (void *)(unsigned long)ksyms->strs_sz; + ksym->addr = addr; + + memcpy(ksyms->strs + ksyms->strs_sz, name, name_len); + ksyms->strs_sz += name_len; + ksyms->syms_sz++; + + return 0; +} + +static int ksym_cmp(const void *p1, const void *p2) { + const struct ksym *s1 = p1, *s2 = p2; + + if (s1->addr == s2->addr) + return strcmp(s1->name, s2->name); + return s1->addr < s2->addr ? -1 : 1; +} + +struct ksyms *ksyms__load(void) { + char sym_type, sym_name[256]; + struct ksyms *ksyms; + unsigned long sym_addr; + int i, ret; + FILE *f; + + f = fopen("/proc/kallsyms", "r"); + if (!f) + return NULL; + + ksyms = calloc(1, sizeof(*ksyms)); + if (!ksyms) + goto err_out; + + while (true) { + ret = fscanf(f, "%lx %c %s%*[^\n]\n", &sym_addr, &sym_type, sym_name); + if (ret == EOF && feof(f)) + break; + if (ret != 3) + goto err_out; + if (ksyms__add_symbol(ksyms, sym_name, sym_addr)) + goto err_out; + } + + /* now when strings are finalized, adjust pointers properly */ + for (i = 0; i < ksyms->syms_sz; i++) + ksyms->syms[i].name += (unsigned long)ksyms->strs; + + qsort(ksyms->syms, ksyms->syms_sz, sizeof(*ksyms->syms), ksym_cmp); + + fclose(f); + return ksyms; + +err_out: + ksyms__free(ksyms); + fclose(f); + return NULL; +} + +void ksyms__free(struct ksyms *ksyms) { + if (!ksyms) + return; + + free(ksyms->syms); + free(ksyms->strs); + free(ksyms); +} + +const struct ksym *ksyms__map_addr(const struct ksyms *ksyms, + unsigned long addr) { + int start = 0, end = ksyms->syms_sz - 1, mid; + unsigned long sym_addr; + + /* find largest sym_addr <= addr using binary search */ + while (start < end) { + mid = start + (end - start + 1) / 2; + sym_addr = ksyms->syms[mid].addr; + + if (sym_addr <= addr) + start = mid; + else + end = mid - 1; + } + + if (start == end && ksyms->syms[start].addr <= addr) + return &ksyms->syms[start]; + return NULL; +} + +const struct ksym *ksyms__get_symbol(const struct ksyms *ksyms, + const char *name) { + int i; + + for (i = 0; i < ksyms->syms_sz; i++) { + if (strcmp(ksyms->syms[i].name, name) == 0) + return &ksyms->syms[i]; + } + + return NULL; +} + +struct load_range { + uint64_t start; + uint64_t end; + uint64_t file_off; +}; + +enum elf_type { + EXEC, + DYN, + PERF_MAP, + VDSO, + UNKNOWN, +}; + +struct dso { + char *name; + struct load_range *ranges; + int range_sz; + /* Dyn's first text section virtual addr at execution */ + uint64_t sh_addr; + /* Dyn's first text section file offset */ + uint64_t sh_offset; + enum elf_type type; + + struct sym *syms; + int syms_sz; + int syms_cap; + + /* + * libbpf's struct btf is actually a pretty efficient + * "set of strings" data structure, so we create an + * empty one and use it to store symbol names. + */ + struct btf *btf; +}; + +struct map { + uint64_t start_addr; + uint64_t end_addr; + uint64_t file_off; + uint64_t dev_major; + uint64_t dev_minor; + uint64_t inode; +}; + +struct syms { + struct dso *dsos; + int dso_sz; +}; + +static bool is_file_backed(const char *mapname) { +#define STARTS_WITH(mapname, prefix) \ + (!strncmp(mapname, prefix, sizeof(prefix) - 1)) + + return mapname[0] && + !(STARTS_WITH(mapname, "//anon") || + STARTS_WITH(mapname, "/dev/zero") || + STARTS_WITH(mapname, "/anon_hugepage") || + STARTS_WITH(mapname, "[stack") || STARTS_WITH(mapname, "/SYSV") || + STARTS_WITH(mapname, "[heap]") || + STARTS_WITH(mapname, "[uprobes]") || + STARTS_WITH(mapname, "[vsyscall]")); +} + +static bool is_perf_map(const char *path) { + return false; +} + +static bool is_vdso(const char *path) { + return !strcmp(path, "[vdso]"); +} + +static bool is_uprobes(const char *path) { + return !strcmp(path, "[uprobes]"); +} + +static int get_elf_type(const char *path) { + GElf_Ehdr hdr; + void *res; + Elf *e; + int fd; + + if (is_vdso(path)) + return -1; + if (is_uprobes(path)) + return -1; + e = open_elf(path, &fd); + if (!e) + return -1; + res = gelf_getehdr(e, &hdr); + close_elf(e, fd); + if (!res) + return -1; + return hdr.e_type; +} + +static int get_elf_text_scn_info(const char *path, uint64_t *addr, + uint64_t *offset) { + Elf_Scn *section = NULL; + int fd = -1, err = -1; + GElf_Shdr header; + size_t stridx; + Elf *e = NULL; + char *name; + + e = open_elf(path, &fd); + if (!e) + goto err_out; + err = elf_getshdrstrndx(e, &stridx); + if (err < 0) + goto err_out; + + err = -1; + while ((section = elf_nextscn(e, section)) != 0) { + if (!gelf_getshdr(section, &header)) + continue; + + name = elf_strptr(e, stridx, header.sh_name); + if (name && !strcmp(name, ".text")) { + *addr = (uint64_t)header.sh_addr; + *offset = (uint64_t)header.sh_offset; + err = 0; + break; + } + } + +err_out: + close_elf(e, fd); + return err; +} + +static int syms__add_dso(struct syms *syms, struct map *map, const char *name) { + struct dso *dso = NULL; + int i, type; + void *tmp; + + for (i = 0; i < syms->dso_sz; i++) { + if (!strcmp(syms->dsos[i].name, name)) { + dso = &syms->dsos[i]; + break; + } + } + + if (!dso) { + tmp = realloc(syms->dsos, (syms->dso_sz + 1) * sizeof(*syms->dsos)); + if (!tmp) + return -1; + syms->dsos = tmp; + dso = &syms->dsos[syms->dso_sz++]; + memset(dso, 0, sizeof(*dso)); + dso->name = strdup(name); + dso->btf = btf__new_empty(); + } + + tmp = realloc(dso->ranges, (dso->range_sz + 1) * sizeof(*dso->ranges)); + if (!tmp) + return -1; + dso->ranges = tmp; + dso->ranges[dso->range_sz].start = map->start_addr; + dso->ranges[dso->range_sz].end = map->end_addr; + dso->ranges[dso->range_sz].file_off = map->file_off; + dso->range_sz++; + type = get_elf_type(name); + if (type == ET_EXEC) { + dso->type = EXEC; + } else if (type == ET_DYN) { + dso->type = DYN; + if (get_elf_text_scn_info(name, &dso->sh_addr, &dso->sh_offset) < 0) + return -1; + } else if (is_perf_map(name)) { + dso->type = PERF_MAP; + } else if (is_vdso(name)) { + dso->type = VDSO; + } else { + dso->type = UNKNOWN; + } + return 0; +} + +static struct dso *syms__find_dso(const struct syms *syms, unsigned long addr, + uint64_t *offset) { + struct load_range *range; + struct dso *dso; + int i, j; + + for (i = 0; i < syms->dso_sz; i++) { + dso = &syms->dsos[i]; + for (j = 0; j < dso->range_sz; j++) { + range = &dso->ranges[j]; + if (addr <= range->start || addr >= range->end) + continue; + if (dso->type == DYN || dso->type == VDSO) { + /* Offset within the mmap */ + *offset = addr - range->start + range->file_off; + /* Offset within the ELF for dyn symbol lookup */ + *offset += dso->sh_addr - dso->sh_offset; + } else { + *offset = addr; + } + + return dso; + } + } + + return NULL; +} + +static int dso__load_sym_table_from_perf_map(struct dso *dso) { + return -1; +} + +static int dso__add_sym(struct dso *dso, const char *name, uint64_t start, + uint64_t size) { + struct sym *sym; + size_t new_cap; + void *tmp; + int off; + + off = btf__add_str(dso->btf, name); + if (off < 0) + return off; + + if (dso->syms_sz + 1 > dso->syms_cap) { + new_cap = dso->syms_cap * 4 / 3; + if (new_cap < 1024) + new_cap = 1024; + tmp = realloc(dso->syms, sizeof(*dso->syms) * new_cap); + if (!tmp) + return -1; + dso->syms = tmp; + dso->syms_cap = new_cap; + } + + sym = &dso->syms[dso->syms_sz++]; + /* while constructing, re-use pointer as just a plain offset */ + sym->name = (void *)(unsigned long)off; + sym->start = start; + sym->size = size; + sym->offset = 0; + + return 0; +} + +static int sym_cmp(const void *p1, const void *p2) { + const struct sym *s1 = p1, *s2 = p2; + + if (s1->start == s2->start) + return strcmp(s1->name, s2->name); + return s1->start < s2->start ? -1 : 1; +} + +static int dso__add_syms(struct dso *dso, Elf *e, Elf_Scn *section, + size_t stridx, size_t symsize) { + Elf_Data *data = NULL; + + while ((data = elf_getdata(section, data)) != 0) { + size_t i, symcount = data->d_size / symsize; + + if (data->d_size % symsize) + return -1; + + for (i = 0; i < symcount; ++i) { + const char *name; + GElf_Sym sym; + + if (!gelf_getsym(data, (int)i, &sym)) + continue; + if (!(name = elf_strptr(e, stridx, sym.st_name))) + continue; + if (name[0] == '\0') + continue; + + if (sym.st_value == 0) + continue; + + if (dso__add_sym(dso, name, sym.st_value, sym.st_size)) + goto err_out; + } + } + + return 0; + +err_out: + return -1; +} + +static void dso__free_fields(struct dso *dso) { + if (!dso) + return; + + free(dso->name); + free(dso->ranges); + free(dso->syms); + btf__free(dso->btf); +} + +static int dso__load_sym_table_from_elf(struct dso *dso, int fd) { + Elf_Scn *section = NULL; + Elf *e; + int i; + + e = fd > 0 ? open_elf_by_fd(fd) : open_elf(dso->name, &fd); + if (!e) + return -1; + + while ((section = elf_nextscn(e, section)) != 0) { + GElf_Shdr header; + + if (!gelf_getshdr(section, &header)) + continue; + + if (header.sh_type != SHT_SYMTAB && header.sh_type != SHT_DYNSYM) + continue; + + if (dso__add_syms(dso, e, section, header.sh_link, header.sh_entsize)) + goto err_out; + } + + /* now when strings are finalized, adjust pointers properly */ + for (i = 0; i < dso->syms_sz; i++) + dso->syms[i].name = + btf__name_by_offset(dso->btf, (unsigned long)dso->syms[i].name); + + qsort(dso->syms, dso->syms_sz, sizeof(*dso->syms), sym_cmp); + + close_elf(e, fd); + return 0; + +err_out: + dso__free_fields(dso); + close_elf(e, fd); + return -1; +} + +static int create_tmp_vdso_image(struct dso *dso) { + uint64_t start_addr, end_addr; + long pid = getpid(); + char buf[PATH_MAX]; + void *image = NULL; + char tmpfile[128]; + int ret, fd = -1; + uint64_t sz; + char *name; + FILE *f; + + snprintf(tmpfile, sizeof(tmpfile), "/proc/%ld/maps", pid); + f = fopen(tmpfile, "r"); + if (!f) + return -1; + + while (true) { + ret = fscanf(f, "%llx-%llx %*s %*x %*x:%*x %*u%[^\n]", + (long long *)&start_addr, (long long *)&end_addr, buf); + if (ret == EOF && feof(f)) + break; + if (ret != 3) + goto err_out; + + name = buf; + while (isspace(*name)) + name++; + if (!is_file_backed(name)) + continue; + if (is_vdso(name)) + break; + } + + sz = end_addr - start_addr; + image = malloc(sz); + if (!image) + goto err_out; + memcpy(image, (void *)start_addr, sz); + + snprintf(tmpfile, sizeof(tmpfile), "/tmp/libbpf_%ld_vdso_image_XXXXXX", + pid); + fd = mkostemp(tmpfile, O_CLOEXEC); + if (fd < 0) { + fprintf(stderr, "failed to create temp file: %s\n", strerror(errno)); + goto err_out; + } + /* Unlink the file to avoid leaking */ + if (unlink(tmpfile) == -1) + fprintf(stderr, "failed to unlink %s: %s\n", tmpfile, strerror(errno)); + if (write(fd, image, sz) == -1) { + fprintf(stderr, "failed to write to vDSO image: %s\n", strerror(errno)); + close(fd); + fd = -1; + goto err_out; + } + +err_out: + fclose(f); + free(image); + return fd; +} + +static int dso__load_sym_table_from_vdso_image(struct dso *dso) { + int fd = create_tmp_vdso_image(dso); + + if (fd < 0) + return -1; + return dso__load_sym_table_from_elf(dso, fd); +} + +static int dso__load_sym_table(struct dso *dso) { + if (dso->type == UNKNOWN) + return -1; + if (dso->type == PERF_MAP) + return dso__load_sym_table_from_perf_map(dso); + if (dso->type == EXEC || dso->type == DYN) + return dso__load_sym_table_from_elf(dso, 0); + if (dso->type == VDSO) + return dso__load_sym_table_from_vdso_image(dso); + return -1; +} + +static struct sym *dso__find_sym(struct dso *dso, uint64_t offset) { + unsigned long sym_addr; + int start, end, mid; + + if (!dso->syms && dso__load_sym_table(dso)) + return NULL; + + start = 0; + end = dso->syms_sz - 1; + + /* find largest sym_addr <= addr using binary search */ + while (start < end) { + mid = start + (end - start + 1) / 2; + sym_addr = dso->syms[mid].start; + + if (sym_addr <= offset) + start = mid; + else + end = mid - 1; + } + + if (start == end && dso->syms[start].start <= offset && + offset < dso->syms[start].start + dso->syms[start].size) { + (dso->syms[start]).offset = offset - dso->syms[start].start; + return &dso->syms[start]; + } + return NULL; +} + +struct syms *syms__load_file(const char *fname) { + char buf[PATH_MAX], perm[5]; + struct syms *syms; + struct map map; + char *name; + FILE *f; + int ret; + + f = fopen(fname, "r"); + if (!f) + return NULL; + + syms = calloc(1, sizeof(*syms)); + if (!syms) + goto err_out; + + while (true) { + ret = fscanf(f, "%llx-%llx %4s %llx %llx:%llx %llu%[^\n]", + (long long *)&map.start_addr, (long long *)&map.end_addr, + perm, (long long *)&map.file_off, + (long long *)&map.dev_major, (long long *)&map.dev_minor, + (long long *)&map.inode, buf); + if (ret == EOF && feof(f)) + break; + if (ret != 8) /* perf-.map */ + goto err_out; + + if (perm[2] != 'x') + continue; + + name = buf; + while (isspace(*name)) + name++; + if (!is_file_backed(name)) + continue; + + if (syms__add_dso(syms, &map, name)) + goto err_out; + } + + fclose(f); + return syms; + +err_out: + syms__free(syms); + fclose(f); + return NULL; +} + +struct syms *syms__load_pid(pid_t tgid) { + char fname[128]; + + snprintf(fname, sizeof(fname), "/proc/%ld/maps", (long)tgid); + return syms__load_file(fname); +} + +void syms__free(struct syms *syms) { + int i; + + if (!syms) + return; + + for (i = 0; i < syms->dso_sz; i++) + dso__free_fields(&syms->dsos[i]); + free(syms->dsos); + free(syms); +} + +const struct sym *syms__map_addr(const struct syms *syms, unsigned long addr) { + struct dso *dso; + uint64_t offset; + + dso = syms__find_dso(syms, addr, &offset); + if (!dso) + return NULL; + return dso__find_sym(dso, offset); +} + +const struct sym *syms__map_addr_dso(const struct syms *syms, + unsigned long addr, char **dso_name, + unsigned long *dso_offset) { + struct dso *dso; + uint64_t offset; + + dso = syms__find_dso(syms, addr, &offset); + if (!dso) + return NULL; + + *dso_name = dso->name; + *dso_offset = offset; + + return dso__find_sym(dso, offset); +} + +struct syms_cache { + struct { + struct syms *syms; + int tgid; + } *data; + int nr; +}; + +struct syms_cache *syms_cache__new(int nr) { + struct syms_cache *syms_cache; + + syms_cache = calloc(1, sizeof(*syms_cache)); + if (!syms_cache) + return NULL; + if (nr > 0) + syms_cache->data = calloc(nr, sizeof(*syms_cache->data)); + return syms_cache; +} + +void syms_cache__free(struct syms_cache *syms_cache) { + int i; + + if (!syms_cache) + return; + + for (i = 0; i < syms_cache->nr; i++) + syms__free(syms_cache->data[i].syms); + free(syms_cache->data); + free(syms_cache); +} + +struct syms *syms_cache__get_syms(struct syms_cache *syms_cache, int tgid) { + void *tmp; + int i; + + for (i = 0; i < syms_cache->nr; i++) { + if (syms_cache->data[i].tgid == tgid) + return syms_cache->data[i].syms; + } + + tmp = realloc(syms_cache->data, + (syms_cache->nr + 1) * sizeof(*syms_cache->data)); + if (!tmp) + return NULL; + syms_cache->data = tmp; + syms_cache->data[syms_cache->nr].syms = syms__load_pid(tgid); + syms_cache->data[syms_cache->nr].tgid = tgid; + return syms_cache->data[syms_cache->nr++].syms; +} + +struct partitions { + struct partition *items; + int sz; +}; + +static int partitions__add_partition(struct partitions *partitions, + const char *name, unsigned int dev) { + struct partition *partition; + void *tmp; + + tmp = realloc(partitions->items, + (partitions->sz + 1) * sizeof(*partitions->items)); + if (!tmp) + return -1; + partitions->items = tmp; + partition = &partitions->items[partitions->sz]; + partition->name = strdup(name); + partition->dev = dev; + partitions->sz++; + + return 0; +} + +struct partitions *partitions__load(void) { + char part_name[DISK_NAME_LEN]; + unsigned int devmaj, devmin; + unsigned long long nop; + struct partitions *partitions; + char buf[64]; + FILE *f; + + f = fopen("/proc/partitions", "r"); + if (!f) + return NULL; + + partitions = calloc(1, sizeof(*partitions)); + if (!partitions) + goto err_out; + + while (fgets(buf, sizeof(buf), f) != NULL) { + /* skip heading */ + if (buf[0] != ' ' || buf[0] == '\n') + continue; + if (sscanf(buf, "%u %u %llu %s", &devmaj, &devmin, &nop, part_name) != + 4) + goto err_out; + if (partitions__add_partition(partitions, part_name, + MKDEV(devmaj, devmin))) + goto err_out; + } + + fclose(f); + return partitions; + +err_out: + partitions__free(partitions); + fclose(f); + return NULL; +} + +void partitions__free(struct partitions *partitions) { + int i; + + if (!partitions) + return; + + for (i = 0; i < partitions->sz; i++) + free(partitions->items[i].name); + free(partitions->items); + free(partitions); +} + +const struct partition *partitions__get_by_dev( + const struct partitions *partitions, unsigned int dev) { + int i; + + for (i = 0; i < partitions->sz; i++) { + if (partitions->items[i].dev == dev) + return &partitions->items[i]; + } + + return NULL; +} + +const struct partition *partitions__get_by_name( + const struct partitions *partitions, const char *name) { + int i; + + for (i = 0; i < partitions->sz; i++) { + if (strcmp(partitions->items[i].name, name) == 0) + return &partitions->items[i]; + } + + return NULL; +} + +static void print_stars(unsigned int val, unsigned int val_max, int width) { + int num_stars, num_spaces, i; + bool need_plus; + + num_stars = min(val, val_max) * width / val_max; + num_spaces = width - num_stars; + need_plus = val > val_max; + + for (i = 0; i < num_stars; i++) + printf("*"); + for (i = 0; i < num_spaces; i++) + printf(" "); + if (need_plus) + printf("+"); +} + +void print_log2_hist(unsigned int *vals, int vals_size, const char *val_type) { + int stars_max = 40, idx_max = -1; + unsigned int val, val_max = 0; + unsigned long long low, high; + int stars, width, i; + + for (i = 0; i < vals_size; i++) { + val = vals[i]; + if (val > 0) + idx_max = i; + if (val > val_max) + val_max = val; + } + + if (idx_max < 0) + return; + + printf("%*s%-*s : count distribution\n", idx_max <= 32 ? 5 : 15, "", + idx_max <= 32 ? 19 : 29, val_type); + + if (idx_max <= 32) + stars = stars_max; + else + stars = stars_max / 2; + + for (i = 0; i <= idx_max; i++) { + low = (1ULL << (i + 1)) >> 1; + high = (1ULL << (i + 1)) - 1; + if (low == high) + low -= 1; + val = vals[i]; + width = idx_max <= 32 ? 10 : 20; + printf("%*lld -> %-*lld : %-8d |", width, low, width, high, val); + print_stars(val, val_max, stars); + printf("|\n"); + } +} + +void print_linear_hist(unsigned int *vals, int vals_size, unsigned int base, + unsigned int step, const char *val_type) { + int i, stars_max = 40, idx_min = -1, idx_max = -1; + unsigned int val, val_max = 0; + + for (i = 0; i < vals_size; i++) { + val = vals[i]; + if (val > 0) { + idx_max = i; + if (idx_min < 0) + idx_min = i; + } + if (val > val_max) + val_max = val; + } + + if (idx_max < 0) + return; + + printf(" %-13s : count distribution\n", val_type); + for (i = idx_min; i <= idx_max; i++) { + val = vals[i]; + if (!val) + continue; + printf(" %-10d : %-8d |", base + i * step, val); + print_stars(val, val_max, stars_max); + printf("|\n"); + } +} + +unsigned long long get_ktime_ns(void) { + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec; +} + +bool is_kernel_module(const char *name) { + bool found = false; + char buf[64]; + FILE *f; + + f = fopen("/proc/modules", "r"); + if (!f) + return false; + + while (fgets(buf, sizeof(buf), f) != NULL) { + if (sscanf(buf, "%s %*s\n", buf) != 1) + break; + if (!strcmp(buf, name)) { + found = true; + break; + } + } + + fclose(f); + return found; +} + +bool fentry_try_attach(int id) { + int prog_fd, attach_fd; + char error[4096]; + struct bpf_insn insns[] = { + {.code = BPF_ALU64 | BPF_MOV | BPF_K, .dst_reg = BPF_REG_0, .imm = 0}, + {.code = BPF_JMP | BPF_EXIT}, + }; + LIBBPF_OPTS(bpf_prog_load_opts, opts, + .expected_attach_type = BPF_TRACE_FENTRY, .attach_btf_id = id, + .log_buf = error, .log_size = sizeof(error), ); + + prog_fd = bpf_prog_load(BPF_PROG_TYPE_TRACING, "test", "GPL", insns, + sizeof(insns) / sizeof(struct bpf_insn), &opts); + if (prog_fd < 0) + return false; + + attach_fd = bpf_raw_tracepoint_open(NULL, prog_fd); + if (attach_fd >= 0) + close(attach_fd); + + close(prog_fd); + return attach_fd >= 0; +} + +bool fentry_can_attach(const char *name, const char *mod) { + struct btf *btf, *vmlinux_btf, *module_btf = NULL; + int err, id; + + vmlinux_btf = btf__load_vmlinux_btf(); + err = libbpf_get_error(vmlinux_btf); + if (err) + return false; + + btf = vmlinux_btf; + + if (mod) { + module_btf = btf__load_module_btf(mod, vmlinux_btf); + err = libbpf_get_error(module_btf); + if (!err) { + btf = module_btf; + } + } + + id = btf__find_by_name_kind(btf, name, BTF_KIND_FUNC); + + btf__free(module_btf); + btf__free(vmlinux_btf); + return id > 0; +} + +#define DEBUGFS "/sys/kernel/debug/tracing" +#define TRACEFS "/sys/kernel/tracing" + +static bool use_debugfs(void) { + static int has_debugfs = -1; + + if (has_debugfs < 0) + has_debugfs = faccessat(AT_FDCWD, DEBUGFS, F_OK, AT_EACCESS) == 0; + + return has_debugfs == 1; +} + +static const char *tracefs_path(void) { + return use_debugfs() ? DEBUGFS : TRACEFS; +} + +static const char *tracefs_available_filter_functions(void) { + return use_debugfs() ? DEBUGFS "/available_filter_functions" + : TRACEFS "/available_filter_functions"; +} + +bool kprobe_exists(const char *name) { + char addr_range[256]; + char sym_name[256]; + FILE *f; + int ret; + + f = fopen("/sys/kernel/debug/kprobes/blacklist", "r"); + if (!f) + goto avail_filter; + + while (true) { + ret = fscanf(f, "%s %s%*[^\n]\n", addr_range, sym_name); + if (ret == EOF && feof(f)) + break; + if (ret != 2) { + fprintf(stderr, "failed to read symbol from kprobe blacklist\n"); + break; + } + if (!strcmp(name, sym_name)) { + fclose(f); + return false; + } + } + fclose(f); + +avail_filter: + f = fopen(tracefs_available_filter_functions(), "r"); + if (!f) + goto slow_path; + + while (true) { + ret = fscanf(f, "%s%*[^\n]\n", sym_name); + if (ret == EOF && feof(f)) + break; + if (ret != 1) { + fprintf(stderr, + "failed to read symbol from available_filter_functions\n"); + break; + } + if (!strcmp(name, sym_name)) { + fclose(f); + return true; + } + } + + fclose(f); + return false; + +slow_path: + f = fopen("/proc/kallsyms", "r"); + if (!f) + return false; + + while (true) { + ret = fscanf(f, "%*x %*c %s%*[^\n]\n", sym_name); + if (ret == EOF && feof(f)) + break; + if (ret != 1) { + fprintf(stderr, "failed to read symbol from kallsyms\n"); + break; + } + if (!strcmp(name, sym_name)) { + fclose(f); + return true; + } + } + + fclose(f); + return false; +} + +bool tracepoint_exists(const char *category, const char *event) { + char path[PATH_MAX]; + + snprintf(path, sizeof(path), "%s/events/%s/%s/format", tracefs_path(), + category, event); + if (!access(path, F_OK)) + return true; + return false; +} + +bool vmlinux_btf_exists(void) { + struct btf *btf; + int err; + + btf = btf__load_vmlinux_btf(); + err = libbpf_get_error(btf); + if (err) + return false; + + btf__free(btf); + return true; +} + +bool module_btf_exists(const char *mod) { + char sysfs_mod[80]; + + if (mod) { + snprintf(sysfs_mod, sizeof(sysfs_mod), "/sys/kernel/btf/%s", mod); + if (!access(sysfs_mod, R_OK)) + return true; + } + return false; +} + +bool probe_tp_btf(const char *name) { + LIBBPF_OPTS(bpf_prog_load_opts, opts, + .expected_attach_type = BPF_TRACE_RAW_TP); + struct bpf_insn insns[] = { + {.code = BPF_ALU64 | BPF_MOV | BPF_K, .dst_reg = BPF_REG_0, .imm = 0}, + {.code = BPF_JMP | BPF_EXIT}, + }; + int fd, insn_cnt = sizeof(insns) / sizeof(struct bpf_insn); + + opts.attach_btf_id = libbpf_find_vmlinux_btf_id(name, BPF_TRACE_RAW_TP); + fd = bpf_prog_load(BPF_PROG_TYPE_TRACING, NULL, "GPL", insns, insn_cnt, + &opts); + if (fd >= 0) + close(fd); + return fd >= 0; +} + +bool probe_ringbuf() { + int map_fd; + + map_fd = + bpf_map_create(BPF_MAP_TYPE_RINGBUF, NULL, 0, 0, getpagesize(), NULL); + if (map_fd < 0) + return false; + + close(map_fd); + return true; +} diff --git a/eBPF_Supermarket/kvm_watcher/src/helpers/uprobe_helpers.c b/eBPF_Supermarket/kvm_watcher/src/helpers/uprobe_helpers.c new file mode 100644 index 000000000..5cf6d1743 --- /dev/null +++ b/eBPF_Supermarket/kvm_watcher/src/helpers/uprobe_helpers.c @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +/* Copyright (c) 2021 Google LLC. */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define warn(...) fprintf(stderr, __VA_ARGS__) + +/* + * Returns 0 on success; -1 on failure. On sucess, returns via `path` the full + * path to the program for pid. + */ +int get_pid_binary_path(pid_t pid, char *path, size_t path_sz) { + ssize_t ret; + char proc_pid_exe[32]; + + if (snprintf(proc_pid_exe, sizeof(proc_pid_exe), "/proc/%d/exe", pid) >= + sizeof(proc_pid_exe)) { + warn("snprintf /proc/PID/exe failed"); + return -1; + } + ret = readlink(proc_pid_exe, path, path_sz); + if (ret < 0) { + warn("No such pid %d\n", pid); + return -1; + } + if (ret >= path_sz) { + warn("readlink truncation"); + return -1; + } + path[ret] = '\0'; + + return 0; +} + +/* + * Returns 0 on success; -1 on failure. On success, returns via `path` the full + * path to a library matching the name `lib` that is loaded into pid's address + * space. + */ +int get_pid_lib_path(pid_t pid, const char *lib, char *path, size_t path_sz) { + FILE *maps; + char *p; + char proc_pid_maps[32]; + char line_buf[1024]; + char path_buf[1024]; + + if (snprintf(proc_pid_maps, sizeof(proc_pid_maps), "/proc/%d/maps", pid) >= + sizeof(proc_pid_maps)) { + warn("snprintf /proc/PID/maps failed"); + return -1; + } + maps = fopen(proc_pid_maps, "r"); + if (!maps) { + warn("No such pid %d\n", pid); + return -1; + } + while (fgets(line_buf, sizeof(line_buf), maps)) { + if (sscanf(line_buf, "%*x-%*x %*s %*x %*s %*u %s", path_buf) != 1) + continue; + /* e.g. /usr/lib/x86_64-linux-gnu/libc-2.31.so */ + p = strrchr(path_buf, '/'); + if (!p) + continue; + if (strncmp(p, "/lib", 4)) + continue; + p += 4; + if (strncmp(lib, p, strlen(lib))) + continue; + p += strlen(lib); + /* libraries can have - or . after the name */ + if (*p != '.' && *p != '-') + continue; + if (strnlen(path_buf, 1024) >= path_sz) { + warn("path size too small\n"); + return -1; + } + strcpy(path, path_buf); + fclose(maps); + return 0; + } + + warn("Cannot find library %s\n", lib); + fclose(maps); + return -1; +} + +/* + * Returns 0 on success; -1 on failure. On success, returns via `path` the full + * path to the program. + */ +static int which_program(const char *prog, char *path, size_t path_sz) { + FILE *which; + char cmd[100]; + + if (snprintf(cmd, sizeof(cmd), "which %s", prog) >= sizeof(cmd)) { + warn("snprintf which prog failed"); + return -1; + } + which = popen(cmd, "r"); + if (!which) { + warn("which failed"); + return -1; + } + if (!fgets(path, path_sz, which)) { + warn("fgets which failed"); + pclose(which); + return -1; + } + /* which has a \n at the end of the string */ + path[strlen(path) - 1] = '\0'; + pclose(which); + return 0; +} + +/* + * Returns 0 on success; -1 on failure. On success, returns via `path` the full + * path to the binary for the given pid. + * 1) pid == x, binary == "" : returns the path to x's program + * 2) pid == x, binary == "foo" : returns the path to libfoo linked in x + * 3) pid == 0, binary == "" : failure: need a pid or a binary + * 4) pid == 0, binary == "bar" : returns the path to `which bar` + * + * For case 4), ideally we'd like to search for libbar too, but we don't support + * that yet. + */ +int resolve_binary_path(const char *binary, pid_t pid, char *path, + size_t path_sz) { + if (!strcmp(binary, "")) { + if (!pid) { + warn("Uprobes need a pid or a binary\n"); + return -1; + } + return get_pid_binary_path(pid, path, path_sz); + } + if (pid) + return get_pid_lib_path(pid, binary, path, path_sz); + + if (which_program(binary, path, path_sz)) { + /* + * If the user is tracing a program by name, we can find it. + * But we can't find a library by name yet. We'd need to parse + * ld.so.cache or something similar. + */ + warn("Can't find %s (Need a PID if this is a library)\n", binary); + return -1; + } + return 0; +} + +/* + * Opens an elf at `path` of kind ELF_K_ELF. Returns NULL on failure. On + * success, close with close_elf(e, fd_close). + */ +Elf *open_elf(const char *path, int *fd_close) { + int fd; + Elf *e; + + if (elf_version(EV_CURRENT) == EV_NONE) { + warn("elf init failed\n"); + return NULL; + } + fd = open(path, O_RDONLY); + if (fd < 0) { + warn("Could not open %s\n", path); + return NULL; + } + e = elf_begin(fd, ELF_C_READ, NULL); + if (!e) { + warn("elf_begin failed: %s\n", elf_errmsg(-1)); + close(fd); + return NULL; + } + if (elf_kind(e) != ELF_K_ELF) { + warn("elf kind %d is not ELF_K_ELF\n", elf_kind(e)); + elf_end(e); + close(fd); + return NULL; + } + *fd_close = fd; + return e; +} + +Elf *open_elf_by_fd(int fd) { + Elf *e; + + if (elf_version(EV_CURRENT) == EV_NONE) { + warn("elf init failed\n"); + return NULL; + } + e = elf_begin(fd, ELF_C_READ, NULL); + if (!e) { + warn("elf_begin failed: %s\n", elf_errmsg(-1)); + close(fd); + return NULL; + } + if (elf_kind(e) != ELF_K_ELF) { + warn("elf kind %d is not ELF_K_ELF\n", elf_kind(e)); + elf_end(e); + close(fd); + return NULL; + } + return e; +} + +void close_elf(Elf *e, int fd_close) { + elf_end(e); + close(fd_close); +} + +/* Returns the offset of a function in the elf file `path`, or -1 on failure. */ +off_t get_elf_func_offset(const char *path, const char *func) { + off_t ret = -1; + int i, fd = -1; + Elf *e; + Elf_Scn *scn; + Elf_Data *data; + GElf_Ehdr ehdr; + GElf_Shdr shdr[1]; + GElf_Phdr phdr; + GElf_Sym sym[1]; + size_t shstrndx, nhdrs; + char *n; + + e = open_elf(path, &fd); + + if (!gelf_getehdr(e, &ehdr)) + goto out; + + if (elf_getshdrstrndx(e, &shstrndx) != 0) + goto out; + + scn = NULL; + while ((scn = elf_nextscn(e, scn))) { + if (!gelf_getshdr(scn, shdr)) + continue; + if (!(shdr->sh_type == SHT_SYMTAB || shdr->sh_type == SHT_DYNSYM)) + continue; + data = NULL; + while ((data = elf_getdata(scn, data))) { + for (i = 0; gelf_getsym(data, i, sym); i++) { + n = elf_strptr(e, shdr->sh_link, sym->st_name); + if (!n) + continue; + if (GELF_ST_TYPE(sym->st_info) != STT_FUNC) + continue; + if (!strcmp(n, func)) { + ret = sym->st_value; + goto check; + } + } + } + } + +check: + if (ehdr.e_type == ET_EXEC || ehdr.e_type == ET_DYN) { + if (elf_getphdrnum(e, &nhdrs) != 0) { + ret = -1; + goto out; + } + for (i = 0; i < (int)nhdrs; i++) { + if (!gelf_getphdr(e, i, &phdr)) + continue; + if (phdr.p_type != PT_LOAD || !(phdr.p_flags & PF_X)) + continue; + if (phdr.p_vaddr <= ret && ret < (phdr.p_vaddr + phdr.p_memsz)) { + ret = ret - phdr.p_vaddr + phdr.p_offset; + goto out; + } + } + ret = -1; + } +out: + close_elf(e, fd); + return ret; +} diff --git a/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.bpf.c b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.bpf.c index b44ee3922..edd07f96b 100644 --- a/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.bpf.c +++ b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.bpf.c @@ -16,21 +16,23 @@ // // Kernel space BPF program used for monitoring data for KVM event. -#include "../include/vmlinux.h" +#include "vmlinux.h" #include #include #include -#include "../include/kvm_watcher.h" -#include "../include/kvm_exits.h" -#include "../include/kvm_ioctl.h" -#include "../include/kvm_vcpu.h" -#include "../include/kvm_mmu.h" -#include "../include/kvm_irq.h" -#include "../include/kvm_hypercall.h" +#include "common.h" +#include "kvm_exits.h" +#include "kvm_ioctl.h" +#include "kvm_vcpu.h" +#include "kvm_mmu.h" +#include "kvm_irq.h" +#include "kvm_hypercall.h" +#include "container.h" char LICENSE[] SEC("license") = "Dual BSD/GPL"; const volatile pid_t vm_pid = -1; +const volatile char hostname[64] = ""; static struct common_event *e; // 定义环形缓冲区maps @@ -45,6 +47,13 @@ int BPF_PROG(fentry_kvm_vcpu_halt, struct kvm_vcpu *vcpu) { CHECK_PID(vm_pid); return trace_kvm_vcpu_halt(vcpu); } + +SEC("kprobe/kvm_vcpu_halt") +int BPF_KPROBE(kp_kvm_vcpu_halt, struct kvm_vcpu *vcpu) { + CHECK_PID(vm_pid); + return trace_kvm_vcpu_halt(vcpu); +} + // 追踪vcpu运行信息 SEC("tp/kvm/kvm_vcpu_wakeup") int tp_vcpu_wakeup(struct vcpu_wakeup *ctx) { @@ -67,17 +76,38 @@ SEC("tp/kvm/kvm_entry") int tp_entry(struct exit *ctx) { return trace_kvm_entry(); } + // 记录VCPU调度的信息--进入 +SEC("fentry/vmx_vcpu_load") +int BPF_PROG(fentry_vmx_vcpu_load, struct kvm_vcpu *vcpu, int cpu) { + CHECK_PID(vm_pid); + return trace_vmx_vcpu_load(vcpu, cpu); +} + SEC("kprobe/vmx_vcpu_load") int BPF_KPROBE(kp_vmx_vcpu_load, struct kvm_vcpu *vcpu, int cpu) { CHECK_PID(vm_pid); return trace_vmx_vcpu_load(vcpu, cpu); } + // 记录VCPU调度的信息--退出 +SEC("fentry/vmx_vcpu_put") +int BPF_PROG(fentry_vmx_vcpu_put) { + return trace_vmx_vcpu_put(); +} + SEC("kprobe/vmx_vcpu_put") -int BPF_KPROBE(kp_vmx_vcpu_put, struct kvm_vcpu *vcpu) { +int BPF_KPROBE(kp_vmx_vcpu_put) { return trace_vmx_vcpu_put(); } + +SEC("fentry/mark_page_dirty_in_slot") +int BPF_PROG(fentry_mark_page_dirty_in_slot, struct kvm *kvm, + const struct kvm_memory_slot *memslot, gfn_t gfn) { + CHECK_PID(vm_pid); + return trace_mark_page_dirty_in_slot(kvm, memslot, gfn, &rb, e); +} + SEC("kprobe/mark_page_dirty_in_slot") int BPF_KPROBE(kp_mark_page_dirty_in_slot, struct kvm *kvm, const struct kvm_memory_slot *memslot, gfn_t gfn) { @@ -162,9 +192,15 @@ int BPF_PROG(fexit_vmx_inject_irq, struct kvm_vcpu *vcpu, bool reinjected) { } SEC("fentry/kvm_emulate_hypercall") -int BPF_PROG(fentry_emulate_hypercall, struct kvm_vcpu *vcpu) { +int BPF_PROG(fentry_kvm_emulate_hypercall, struct kvm_vcpu *vcpu) { + CHECK_PID(vm_pid); + return trace_emulate_hypercall(vcpu, &rb, e); +} + +SEC("kprobe/kvm_emulate_hypercall") +int BPF_KPROBE(kp_kvm_emulate_hypercall, struct kvm_vcpu *vcpu) { CHECK_PID(vm_pid); - return entry_emulate_hypercall(vcpu, &rb, e); + return trace_emulate_hypercall(vcpu, &rb, e); } SEC("tp/syscalls/sys_enter_ioctl") @@ -172,10 +208,13 @@ int tp_ioctl(struct trace_event_raw_sys_enter *args) { CHECK_PID(vm_pid); return trace_kvm_ioctl(args); } -SEC("fentry/kvm_arch_vcpu_ioctl_run") -int BPF_PROG(fentry_kvm_arch_vcpu_ioctl_run, struct kvm_vcpu *vcpu) { + +SEC("uprobe") +int BPF_KPROBE(up_kvm_vcpu_ioctl, void *cpu, int type) { CHECK_PID(vm_pid); - return trace_kvm_userspace_entry(vcpu); + if (type != KVM_RUN) + return 0; + return trace_kvm_vcpu_ioctl(); } SEC("tp/kvm/kvm_userspace_exit") @@ -189,8 +228,42 @@ int BPF_PROG(fentry_start_hv_timer, struct kvm_lapic *apic) { return trace_start_hv_timer(apic); } +SEC("kprobe/start_hv_timer") +int BPF_KPROBE(kp_start_hv_timer, struct kvm_lapic *apic) { + CHECK_PID(vm_pid); + return trace_start_hv_timer(apic); +} + SEC("fentry/start_sw_timer") int BPF_PROG(fentry_start_sw_timer, struct kvm_lapic *apic) { CHECK_PID(vm_pid); return trace_start_sw_timer(apic); } + +SEC("kprobe/start_sw_timer") +int BPF_KPROBE(kp_start_sw_timer, struct kvm_lapic *apic) { + CHECK_PID(vm_pid); + return trace_start_sw_timer(apic); +} + +//采集容器的系统用调用信息 +SEC("tracepoint/raw_syscalls/sys_enter") +int tp_container_sys_entry(struct trace_event_raw_sys_enter *args) { + //过滤进程 + bool is_container = is_container_task(hostname); + if (is_container) { + return trace_container_sys_entry(args); + } else { + return 0; + } +} +SEC("tracepoint/raw_syscalls/sys_exit") +int tracepoint__syscalls__sys_exit(struct trace_event_raw_sys_exit *args) { + //过滤进程 + bool is_container = is_container_task(hostname); + if (is_container) { + return trace_container_sys_exit(args, &rb, e); + } else { + return 0; + } +} \ No newline at end of file diff --git a/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c index bcb271968..928a422da 100644 --- a/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c +++ b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c @@ -27,9 +27,13 @@ #include #include #include -#include "../include/kvm_watcher.h" +#include "common.h" +#include "trace_helpers.h" +#include "uprobe_helpers.h" #include "kvm_watcher.skel.h" +//可视化调整输出格式 +int is_first = 1; // 创建并打开临时文件 FILE *create_temp_file(const char *filename) { const char *directory = "./temp"; @@ -324,8 +328,11 @@ static struct env { bool execute_ioctl; bool execute_timer; bool verbose; + bool show; + bool execute_container_syscall; int monitoring_time; pid_t vm_pid; + char hostname[64]; enum EventType event_type; } env = { .execute_vcpu_wakeup = false, @@ -343,6 +350,9 @@ static struct env { .verbose = false, .monitoring_time = 0, .vm_pid = -1, + .hostname = "", + .show = false, + .execute_container_syscall = false, .event_type = NONE_TYPE, }; @@ -353,6 +363,7 @@ int option_selected = 0; // 功能标志变量,确保激活子功能 // 具体解释命令行参数 static const struct argp_option opts[] = { {"vcpu_wakeup", 'w', NULL, 0, "Monitoring the wakeup of vcpu."}, + {"container_syscall", 'a', NULL, 0, "Monitoring the syscall of container."}, {"vcpu_load", 'o', NULL, 0, "Monitoring the load of vcpu."}, {"vm_exit", 'e', NULL, 0, "Monitoring the event of vm exit(including exiting to KVM and user " @@ -372,6 +383,7 @@ static const struct argp_option opts[] = { "Monitoring the data of mmio page fault.(The -f option must be " "specified.)"}, {"vm_pid", 'p', "PID", 0, "Specify the virtual machine pid to monitor."}, + {"show", 's', NULL, 0, "Visual display"}, {"monitoring_time", 't', "SEC", 0, "Time for monitoring."}, {"kvm_ioctl", 'l', NULL, 0, "Monitoring the KVM IOCTL."}, {"kvm_timer", 'T', NULL, 0, "Monitoring the KVM hv or software timer."}, @@ -382,6 +394,20 @@ static const struct argp_option opts[] = { // 解析命令行参数 static error_t parse_arg(int key, char *arg, struct argp_state *state) { switch (key) { + case 's': + env.show = true; + break; + case 'a': + SET_OPTION_AND_CHECK_USAGE(option_selected, + env.execute_container_syscall); + char hostname[64]; + int result = gethostname(hostname, sizeof(hostname)); + if (result == 0) { + strcpy(env.hostname, hostname); + } else { + perror("gethostname"); + } + break; case 'H': argp_state_help(state, stderr, ARGP_HELP_STD_HELP); break; @@ -506,6 +532,8 @@ static int determineEventType(struct env *env) { env->event_type = VCPU_LOAD; } else if (env->execute_timer) { env->event_type = TIMER; + } else if (env->execute_container_syscall) { + env->event_type = CONTAINER_SYSCALL; } else { env->event_type = NONE_TYPE; // 或者根据需要设置一个默认的事件类型 } @@ -534,6 +562,12 @@ static int handle_event(void *ctx, void *data, size_t data_sz) { case VCPU_LOAD: { break; } + case CONTAINER_SYSCALL: { + printf("%-8u %-22s %-10lld %-10lld %-16s\n", e->syscall_data.pid, + e->syscall_data.container_id, e->syscall_data.delay, + e->syscall_data.syscall_id, e->syscall_data.comm); + break; + } case HALT_POLL: { // 使用 e->halt_poll_data 访问 HALT_POLL 特有成员 printf("%-18.6f %-15s %-6d/%-8d %-10s %-7d %-7d --> %d \n", @@ -556,6 +590,12 @@ static int handle_event(void *ctx, void *data, size_t data_sz) { } case PAGE_FAULT: { // 使用 e->page_fault_data 访问 PAGE_FAULT 特有成员 + if (env.show) { + printf("%-18.6f %-10u %-6u %-10.4f\n", timestamp_ms, + e->process.pid, e->page_fault_data.count, + NS_TO_US_WITH_DECIMAL(e->page_fault_data.delay)); + break; + } printf("%-18.6f %-15s %-10u %-12llx %-6u %-10.4f ", timestamp_ms, e->process.comm, e->process.pid, e->page_fault_data.addr, e->page_fault_data.count, @@ -660,11 +700,19 @@ static int handle_event(void *ctx, void *data, size_t data_sz) { break; } case IRQ_INJECT: { - printf("%-18.6f %-15s %-10d %-10lld %#-10x %-10d %-10lld %-10s\n", - timestamp_ms, e->process.comm, e->process.pid, - e->irq_inject_data.delay, e->irq_inject_data.irq_nr, - e->irq_inject_data.vcpu_id, e->irq_inject_data.injections, - e->irq_inject_data.soft ? "Soft/INTn" : "IRQ"); + if (env.show) { + printf("%-18.6f %-10d %-10lld %-10d %-10d %-10lld\n", + timestamp_ms, e->process.pid, e->irq_inject_data.delay, + e->irq_inject_data.irq_nr, e->irq_inject_data.vcpu_id, + e->irq_inject_data.injections); + } else { + printf( + "%-18.6f %-15s %-10d %-10lld %#-10x %-10d %-10lld %-10s\n", + timestamp_ms, e->process.comm, e->process.pid, + e->irq_inject_data.delay, e->irq_inject_data.irq_nr, + e->irq_inject_data.vcpu_id, e->irq_inject_data.injections, + e->irq_inject_data.soft ? "Soft/INTn" : "IRQ"); + } break; } case HYPERCALL: { @@ -730,11 +778,17 @@ static int print_event_head(struct env *env) { "DUR_HALT(ms)", "COMM", "PID/TID", "VCPU_ID", "WAIT/POLL", "VAILD?"); break; + case CONTAINER_SYSCALL: + printf("%-8s %-22s %-9s %10s %-16s\n", "PID", "CONTAINER_ID", + "DELAY(us)", "SYSCALLID", "COMM"); + break; case EXIT: - printf("Waiting vm_exit ... \n"); + //可视化调整输出格式 + // printf("Waiting vm_exit ... \n"); break; case VCPU_LOAD: - printf("Waiting vm_vcpu_load ... \n"); + //可视化调整输出格式 + // printf("Waiting vm_vcpu_load ... \n"); break; case HALT_POLL: printf("%-18s %-15s %-15s %-10s %-7s %-11s %-10s\n", "TIME(ms)", @@ -746,18 +800,29 @@ static int print_event_head(struct env *env) { "USERSPACE_ADDR", "SLOT_ID"); break; case PAGE_FAULT: - printf("%-18s %-15s %-10s %-12s %-6s %-10s %-20s %-17s %-10s %s\n", - "TIME(ms)", "COMM", "PID", "GPA", "COUNT", "DELAY(us)", - "HVA", "PFN", "MEM_SLOTID", "ERROR_TYPE"); + if (env->show) { + printf("%-18s %-10s %-6s %-10s \n", "TIME(ms)", "PID", "COUNT", + "DELAY(us)"); + } else { + printf( + "%-18s %-15s %-10s %-12s %-6s %-10s %-20s %-17s %-10s %s\n", + "TIME(ms)", "COMM", "PID", "GPA", "COUNT", "DELAY(us)", + "HVA", "PFN", "MEM_SLOTID", "ERROR_TYPE"); + } break; case IRQCHIP: printf("%-18s %-15s %-10s %-10s %-14s %-10s %-10s\n", "TIME(ms)", "COMM", "PID", "DELAY", "TYPE/PIN", "DST/VEC", "OTHERS"); break; case IRQ_INJECT: - printf("%-18s %-15s %-10s %-10s %-10s %-10s %-10s %-10s\n", - "TIME(ms)", "COMM", "PID", "DELAY", "IRQ_NR", "VCPU_ID", - "INJECTIONS", "TYPE"); + if (env->show) { + printf("%-18s %-10s %-10s %-10s %-10s %-10s \n", "TIME(ms)", + "PID", "DELAY", "IRQ_NR", "VCPU_ID", "INJECTIONS"); + } else { + printf("%-18s %-15s %-10s %-10s %-10s %-10s %-10s %-10s\n", + "TIME(ms)", "COMM", "PID", "DELAY", "IRQ_NR", "VCPU_ID", + "INJECTIONS", "TYPE"); + } break; case HYPERCALL: { printf("Waiting hypercall ... \n"); @@ -791,28 +856,56 @@ static int print_event_head(struct env *env) { return 0; } -/*通过env结构体的属性真值来判断是否加载某个挂载函数*/ static void set_disable_load(struct kvm_watcher_bpf *skel) { + bpf_program__set_autoload(skel->progs.fentry_vmx_vcpu_load, false); + bpf_program__set_autoload(skel->progs.kp_vmx_vcpu_load, false); + bpf_program__set_autoload(skel->progs.fentry_vmx_vcpu_put, false); + bpf_program__set_autoload(skel->progs.kp_vmx_vcpu_put, false); + bpf_program__set_autoload(skel->progs.fentry_kvm_vcpu_halt, false); + bpf_program__set_autoload(skel->progs.kp_kvm_vcpu_halt, false); + bpf_program__set_autoload(skel->progs.fentry_mark_page_dirty_in_slot, + false); + bpf_program__set_autoload(skel->progs.kp_mark_page_dirty_in_slot, false); + bpf_program__set_autoload(skel->progs.fentry_kvm_emulate_hypercall, false); + bpf_program__set_autoload(skel->progs.kp_kvm_emulate_hypercall, false); + bpf_program__set_autoload(skel->progs.fentry_start_hv_timer, false); + bpf_program__set_autoload(skel->progs.kp_start_hv_timer, false); + bpf_program__set_autoload(skel->progs.fentry_start_sw_timer, false); + bpf_program__set_autoload(skel->progs.kp_start_sw_timer, false); + + if (env.execute_vcpu_load) { + SET_KP_OR_FENTRY_LOAD(vmx_vcpu_load, kvm_intel); + SET_KP_OR_FENTRY_LOAD(vmx_vcpu_put, kvm_intel); + } + if (env.execute_vcpu_wakeup) { + SET_KP_OR_FENTRY_LOAD(kvm_vcpu_halt, kvm); + } + if (env.execute_mark_page_dirty) { + SET_KP_OR_FENTRY_LOAD(mark_page_dirty_in_slot, kvm); + } + if (env.execute_timer) { + SET_KP_OR_FENTRY_LOAD(start_hv_timer, kvm); + SET_KP_OR_FENTRY_LOAD(start_sw_timer, kvm); + } + if (env.execute_hypercall) { + SET_KP_OR_FENTRY_LOAD(kvm_emulate_hypercall, kvm); + } + bpf_program__set_autoload(skel->progs.tp_container_sys_entry, + env.execute_container_syscall ? true : false); + bpf_program__set_autoload(skel->progs.tracepoint__syscalls__sys_exit, + env.execute_container_syscall ? true : false); bpf_program__set_autoload(skel->progs.tp_vcpu_wakeup, env.execute_vcpu_wakeup ? true : false); - bpf_program__set_autoload(skel->progs.kp_vmx_vcpu_load, - env.execute_vcpu_load ? true : false); - bpf_program__set_autoload(skel->progs.kp_vmx_vcpu_put, - env.execute_vcpu_load ? true : false); - bpf_program__set_autoload(skel->progs.fentry_kvm_vcpu_halt, - env.execute_vcpu_wakeup ? true : false); bpf_program__set_autoload(skel->progs.tp_exit, env.execute_exit ? true : false); bpf_program__set_autoload(skel->progs.tp_entry, env.execute_exit ? true : false); - bpf_program__set_autoload(skel->progs.fentry_kvm_arch_vcpu_ioctl_run, + bpf_program__set_autoload(skel->progs.up_kvm_vcpu_ioctl, env.execute_exit ? true : false); bpf_program__set_autoload(skel->progs.tp_kvm_userspace_exit, env.execute_exit ? true : false); bpf_program__set_autoload(skel->progs.tp_kvm_halt_poll_ns, env.execute_halt_poll_ns ? true : false); - bpf_program__set_autoload(skel->progs.kp_mark_page_dirty_in_slot, - env.execute_mark_page_dirty ? true : false); bpf_program__set_autoload(skel->progs.tp_page_fault, env.execute_page_fault ? true : false); bpf_program__set_autoload(skel->progs.fexit_tdp_page_fault, @@ -837,14 +930,8 @@ static void set_disable_load(struct kvm_watcher_bpf *skel) { env.execute_irq_inject ? true : false); bpf_program__set_autoload(skel->progs.fexit_vmx_inject_irq, env.execute_irq_inject ? true : false); - bpf_program__set_autoload(skel->progs.fentry_emulate_hypercall, - env.execute_hypercall ? true : false); bpf_program__set_autoload(skel->progs.tp_ioctl, env.execute_ioctl ? true : false); - bpf_program__set_autoload(skel->progs.fentry_start_hv_timer, - env.execute_timer ? true : false); - bpf_program__set_autoload(skel->progs.fentry_start_sw_timer, - env.execute_timer ? true : false); } // 函数不接受参数,返回一个静态分配的字符串 @@ -857,11 +944,72 @@ const char *getCurrentTimeFormatted() { tm = localtime(&t); // 格式化时间到静态分配的字符串中 - strftime(ts, sizeof(ts), "%H:%M:%S", tm); + strftime(ts, sizeof(ts), "%Y/%m/%d %H:%M:%S", tm); return ts; // 返回指向静态字符串的指针 } +// In order to sort vm_exit maps +int sort_by_key(int fd, struct exit_key *keys, struct exit_value *values) { + int err = 0; + struct exit_key lookup_key = {}; + struct exit_key next_key = {}; + struct exit_value exit_value; + int i = 0, j = 0, count = 0; + while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { + j = i - 1; + struct exit_key temp_key = next_key; + err = bpf_map_lookup_elem(fd, &next_key, &exit_value); + if (err < 0) { + fprintf(stderr, "failed to lookup exit_value: %d\n", err); + return -1; + } + struct exit_value temp_value = exit_value; + while (j >= 0 && + (keys[j].pid > temp_key.pid || (keys[j].tid > temp_key.tid))) { + keys[j + 1] = keys[j]; + values[j + 1] = values[j]; + j--; + } + keys[j + 1] = temp_key; + values[j + 1] = temp_value; + lookup_key = next_key; + count++; + i++; + } + return count; +} + +// clear the specific map +int clear_map(void *lookup_key, void *next_key, enum EventType type, int fd) { + int err; + switch (type) { + case HYPERCALL: + memset(lookup_key, 0, sizeof(struct hc_key)); + break; + case TIMER: + memset(lookup_key, 0, sizeof(struct timer_key)); + break; + case VCPU_LOAD: + memset(lookup_key, 0, sizeof(struct load_key)); + break; + case EXIT: + memset(lookup_key, 0, sizeof(struct exit_key)); + break; + default: + return -1; + } + while (!bpf_map_get_next_key(fd, lookup_key, next_key)) { + err = bpf_map_delete_elem(fd, next_key); + if (err < 0) { + fprintf(stderr, "failed to cleanup map: %d\n", err); + return -1; + } + lookup_key = next_key; + } + return 1; +} + int print_hc_map(struct kvm_watcher_bpf *skel) { int fd = bpf_map__fd(skel->maps.hc_map); int count_fd = bpf_map__fd(skel->maps.hc_count); @@ -894,24 +1042,8 @@ int print_hc_map(struct kvm_watcher_bpf *skel) { // // Move to the next key lookup_key = next_key; } - memset(&lookup_key, 0, sizeof(struct hc_key)); - while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { - err = bpf_map_delete_elem(fd, &next_key); - if (err < 0) { - fprintf(stderr, "failed to cleanup hc_map: %d\n", err); - return -1; - } - lookup_key = next_key; - } - memset(&lookup_key, 0, sizeof(struct hc_key)); - while (!bpf_map_get_next_key(count_fd, &lookup_key, &next_key)) { - err = bpf_map_delete_elem(count_fd, &next_key); - if (err < 0) { - fprintf(stderr, "failed to cleanup hc_count: %d\n", err); - return -1; - } - lookup_key = next_key; - } + clear_map(&lookup_key, &next_key, HYPERCALL, fd); + clear_map(&lookup_key, &next_key, HYPERCALL, count_fd); return 0; } @@ -946,64 +1078,10 @@ int print_timer_map(struct kvm_watcher_bpf *skel) { // Move to the next key lookup_key = next_key; } - - // Clear the timer_map - memset(&lookup_key, 0, sizeof(struct timer_key)); - while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { - err = bpf_map_delete_elem(fd, &next_key); - if (err < 0) { - fprintf(stderr, "failed to cleanup timer_map: %d\n", err); - return -1; - } - lookup_key = next_key; - } + clear_map(&lookup_key, &next_key, TIMER, fd); return 0; } -// In order to sort vm_exit maps -int sort_by_key(int fd, struct exit_key *keys, struct exit_value *values) { - int err = 0; - struct exit_key lookup_key = {}; - struct exit_key next_key = {}; - struct exit_value exit_value; - int first = 1; - int i = 0, j; - int count = 0; - while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { - count++; - if (first) { - first = 0; - bpf_map_lookup_elem(fd, &next_key, &exit_value); - keys[0] = next_key; - values[0] = exit_value; - i++; - lookup_key = next_key; - continue; - } - err = bpf_map_lookup_elem(fd, &next_key, &exit_value); - if (err < 0) { - fprintf(stderr, "failed to lookup exit_value: %d\n", err); - return -1; - } - // insert sort - j = i - 1; - struct exit_key temp_key = next_key; - struct exit_value temp_value = exit_value; - while (j >= 0 && - (keys[j].pid > temp_key.pid || (keys[j].tid > temp_key.tid))) { - keys[j + 1] = keys[j]; - values[j + 1] = values[j]; - j--; - } - i++; - keys[j + 1] = next_key; - values[j + 1] = temp_value; - // Move to the next key - lookup_key = next_key; - } - return count; -} - int print_vcpu_load_map(struct kvm_watcher_bpf *skel) { int fd = bpf_map__fd(skel->maps.load_map); int err; @@ -1011,7 +1089,30 @@ int print_vcpu_load_map(struct kvm_watcher_bpf *skel) { struct load_key next_key = {}; struct load_value load_value = {}; int first = 1; - while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { + if (env.show) { + if (is_first) { + printf("%-12s %-12s %-12s %-12s %-12s %-12s %-12s %-12s\n", "pid", + "tid", "total_time", "max_time", "min_time", "counts", + "vcpuid", "pcpuid"); + is_first = 0; + } + while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { + err = bpf_map_lookup_elem(fd, &next_key, &load_value); + if (err < 0) { + fprintf(stderr, "failed to lookup vcpu_load_value: %d\n", err); + return -1; + } + printf("%-12d %-12d %-12.4f %-12.4f %-12.4f %-12u %-12d %-12d\n", + next_key.pid, next_key.tid, + NS_TO_MS_WITH_DECIMAL(load_value.total_time), + NS_TO_MS_WITH_DECIMAL(load_value.max_time), + NS_TO_MS_WITH_DECIMAL(load_value.min_time), load_value.count, + load_value.vcpu_id, load_value.pcpu_id); + + lookup_key = next_key; + } + + } else { if (first) { first = 0; printf("\nTIME:%s\n", getCurrentTimeFormatted()); @@ -1025,29 +1126,22 @@ int print_vcpu_load_map(struct kvm_watcher_bpf *skel) { "------------ " "------------\n"); } - err = bpf_map_lookup_elem(fd, &next_key, &load_value); - if (err < 0) { - fprintf(stderr, "failed to lookup vcpu_load_value: %d\n", err); - return -1; - } - printf("%-12d %-12d %-12.4f %-12.4f %-12.4f %-12u %-12d %-12d\n", - next_key.pid, next_key.tid, - NS_TO_MS_WITH_DECIMAL(load_value.total_time), - NS_TO_MS_WITH_DECIMAL(load_value.max_time), - NS_TO_MS_WITH_DECIMAL(load_value.min_time), load_value.count, - load_value.vcpu_id, load_value.pcpu_id); - lookup_key = next_key; - } - // clear the maps - memset(&lookup_key, 0, sizeof(struct load_key)); - while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { - err = bpf_map_delete_elem(fd, &next_key); - if (err < 0) { - fprintf(stderr, "failed to cleanup counters: %d\n", err); - return -1; + while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { + err = bpf_map_lookup_elem(fd, &next_key, &load_value); + if (err < 0) { + fprintf(stderr, "failed to lookup vcpu_load_value: %d\n", err); + return -1; + } + printf("%-12d %-12d %-12.4f %-12.4f %-12.4f %-12u %-12d %-12d\n", + next_key.pid, next_key.tid, + NS_TO_MS_WITH_DECIMAL(load_value.total_time), + NS_TO_MS_WITH_DECIMAL(load_value.max_time), + NS_TO_MS_WITH_DECIMAL(load_value.min_time), load_value.count, + load_value.vcpu_id, load_value.pcpu_id); + lookup_key = next_key; } - lookup_key = next_key; } + clear_map(&lookup_key, &next_key, VCPU_LOAD, fd); return 0; } @@ -1061,67 +1155,76 @@ void __print_exit_map(int fd, enum NameType name_type) { // Iterate over the array __u32 pid = 0; __u32 tid = 0; - for (int i = 0; i < count; i++) { - if (first_run) { - first_run = 0; - printf("\nTIME:%s\n", getCurrentTimeFormatted()); - if (name_type == EXIT_NR) { - printf( - "============================================KVM_EXIT======" - "========" - "==============================\n"); - } else if (name_type == EXIT_USERSPACE_NR) { - printf( - "\n=======================================KVM_USERSPACE_" - "EXIT=======" - "================================\n"); - } else { - return; - } - printf("%-12s %-12s %-12s %-12s %-12s %-12s %-12s\n", "PID", "TID", - "TOTAL_TIME", "MAX_TIME", "MIN_TIME", "COUNT", "REASON"); - printf( - "------------ ------------ ------------ ------------ " - "------------ " - "------------ " - "------------\n"); + if (env.show) { + if (is_first) { + printf("%-12s %-12s %-12s %-12s %-12s %-12s\n", "PID", "TID", + "TOTAL_TIME", "MAX_TIME", "MIN_TIME", "COUNT"); + is_first = 0; } - // Print the current entry - if (tid == 0 || tid != keys[i].tid) { - tid = keys[i].tid; - if (pid == 0 || pid != keys[i].pid) { - pid = keys[i].pid; - printf("%-13d", pid); - } else { - printf("%-13s", ""); - } - printf("%-12d %-12.4f %-12.4f %-12.4f %-12u %-12s\n", keys[i].tid, - NS_TO_MS_WITH_DECIMAL(values[i].total_time), - NS_TO_MS_WITH_DECIMAL(values[i].max_time), - NS_TO_MS_WITH_DECIMAL(values[i].min_time), values[i].count, - getName(keys[i].reason, name_type)); - } else if (tid == keys[i].tid) { - printf("%25s %-12.4f %-12.4f %-12.4f %-12u %-12s\n", "", - NS_TO_MS_WITH_DECIMAL(values[i].total_time), + for (int i = 0; i < count; i++) { + printf("%-12d %-12d %-12.4f %-12.4f %-12.4f %-12u\n", keys[i].pid, + keys[i].tid, NS_TO_MS_WITH_DECIMAL(values[i].total_time), NS_TO_MS_WITH_DECIMAL(values[i].max_time), - NS_TO_MS_WITH_DECIMAL(values[i].min_time), values[i].count, - getName(keys[i].reason, name_type)); + NS_TO_MS_WITH_DECIMAL(values[i].min_time), values[i].count); } - } - // clear the maps - memset(&lookup_key, 0, sizeof(struct exit_key)); - while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { - int err = bpf_map_delete_elem(fd, &next_key); - if (err < 0) { - fprintf(stderr, "failed to cleanup counters: %d\n", err); - return; + + } else { + for (int i = 0; i < count; i++) { + if (first_run) { + first_run = 0; + if (name_type == EXIT_NR) { + printf( + "============================================KVM_EXIT==" + "====" + "========" + "==============================\n"); + } else if (name_type == EXIT_USERSPACE_NR) { + printf( + "\n=======================================KVM_" + "USERSPACE_" + "EXIT=======" + "================================\n"); + } else { + return; + } + printf("%-12s %-12s %-12s %-12s %-12s %-12s %-12s\n", "PID", + "TID", "TOTAL_TIME", "MAX_TIME", "MIN_TIME", "COUNT", + "REASON"); + printf( + "------------ ------------ ------------ ------------ " + "------------ " + "------------ " + "------------\n"); + } + // Print the current entry + if (tid == 0 || tid != keys[i].tid) { + tid = keys[i].tid; + if (pid == 0 || pid != keys[i].pid) { + pid = keys[i].pid; + printf("%-13d", pid); + } else { + printf("%-13s", ""); + } + printf("%-12d %-12.4f %-12.4f %-12.4f %-12u %-12s\n", + keys[i].tid, NS_TO_MS_WITH_DECIMAL(values[i].total_time), + NS_TO_MS_WITH_DECIMAL(values[i].max_time), + NS_TO_MS_WITH_DECIMAL(values[i].min_time), + values[i].count, getName(keys[i].reason, name_type)); + } else if (tid == keys[i].tid) { + printf("%25s %-12.4f %-12.4f %-12.4f %-12u %-12s\n", "", + NS_TO_MS_WITH_DECIMAL(values[i].total_time), + NS_TO_MS_WITH_DECIMAL(values[i].max_time), + NS_TO_MS_WITH_DECIMAL(values[i].min_time), + values[i].count, getName(keys[i].reason, name_type)); + } } - lookup_key = next_key; } + clear_map(&lookup_key, &next_key, EXIT, fd); } int print_exit_map(struct kvm_watcher_bpf *skel) { int exit_fd = bpf_map__fd(skel->maps.exit_map); int userspace_exit_fd = bpf_map__fd(skel->maps.userspace_exit_map); + // printf("\nTIME:%s\n", getCurrentTimeFormatted()); __print_exit_map(exit_fd, EXIT_NR); __print_exit_map(userspace_exit_fd, EXIT_USERSPACE_NR); return 0; @@ -1131,7 +1234,7 @@ void print_map_and_check_error(int (*print_func)(struct kvm_watcher_bpf *), const char *map_name, int err) { OUTPUT_INTERVAL(2); print_func(skel); - if (err < 0) { + if (err < 0 && err != -4) { printf("Error printing %s map: %d\n", map_name, err); } } @@ -1143,21 +1246,23 @@ void print_logo() { system(command); } +int attach_probe(struct kvm_watcher_bpf *skel) { + if (env.execute_exit) { + ATTACH_UPROBE_CHECKED(skel, kvm_vcpu_ioctl, up_kvm_vcpu_ioctl); + } + return kvm_watcher_bpf__attach(skel); +} int main(int argc, char **argv) { // 定义一个环形缓冲区 struct ring_buffer *rb = NULL; struct kvm_watcher_bpf *skel; int err; - - print_logo(); - /*解析命令行参数*/ 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); @@ -1171,7 +1276,7 @@ int main(int argc, char **argv) { /* Parameterize BPF code with parameter */ skel->rodata->vm_pid = env.vm_pid; - + strcpy(skel->rodata->hostname, env.hostname); /* 禁用或加载内核挂钩函数 */ set_disable_load(skel); @@ -1183,12 +1288,11 @@ int main(int argc, char **argv) { } /* 附加跟踪点处理程序 */ - err = kvm_watcher_bpf__attach(skel); + err = attach_probe(skel); if (err) { fprintf(stderr, "Failed to attach BPF skeleton\n"); goto cleanup; } - /* 设置环形缓冲区轮询 */ rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), handle_event, NULL, NULL); if (!rb) { @@ -1203,6 +1307,8 @@ int main(int argc, char **argv) { fprintf(stderr, "Invalid env parm\n"); goto cleanup; } + if (!env.show) + print_logo(); /*打印信息头*/ err = print_event_head(&env); diff --git a/eBPF_Supermarket/lib/bpftool b/eBPF_Supermarket/lib/bpftool new file mode 160000 index 000000000..1174341ef --- /dev/null +++ b/eBPF_Supermarket/lib/bpftool @@ -0,0 +1 @@ +Subproject commit 1174341ef321a84003c2f0cf80025823145eff22 diff --git a/eBPF_Supermarket/lib/libbpf b/eBPF_Supermarket/lib/libbpf new file mode 160000 index 000000000..02724cfd0 --- /dev/null +++ b/eBPF_Supermarket/lib/libbpf @@ -0,0 +1 @@ +Subproject commit 02724cfd0702c4102138e62c3ae7d2721c7b190e