diff --git a/.github/workflows/net_watcher.yml b/.github/workflows/net_watcher.yml index 851647ecb..5dc1a6d95 100644 --- a/.github/workflows/net_watcher.yml +++ b/.github/workflows/net_watcher.yml @@ -41,4 +41,7 @@ jobs: sudo timeout -s SIGINT 5 ./netwatcher -r || 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 + sudo timeout -s SIGINT 5 ./netwatcher -n || if [[ $? != 124 && $? != 0 ]];then exit $?;fi + 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 timeout-minutes: 5 diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/dropreason.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/dropreason.h new file mode 100644 index 000000000..68ff87eaa --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/dropreason.h @@ -0,0 +1,83 @@ +#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/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.bpf.c b/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.bpf.c index 492df07d8..352bcaf09 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.bpf.c +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.bpf.c @@ -104,6 +104,12 @@ 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); +} kfree_rb SEC(".maps"); + // 存储每个tcp连接所对应的conn_t struct { __uint(type, BPF_MAP_TYPE_LRU_HASH); @@ -142,10 +148,17 @@ struct { __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"); + 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; + layer_time = 0, http_info = 0, retrans_info = 0, udp_info =0,net_filter = 0,kfree_info = 0; /* help macro */ @@ -1353,6 +1366,9 @@ int BPF_KPROBE(ip_send_skb, struct net *net,struct sk_buff *skb) { if (tinfo == NULL) { return 0; } + + bpf_map_update_elem(&pid_filter, &pid, &pt, BPF_ANY); + struct udp_message *message; struct udp_message *udp_message = bpf_map_lookup_elem(×tamps, pt); @@ -1421,7 +1437,6 @@ int BPF_KPROBE(ip_local_deliver_finish) { 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; @@ -1478,11 +1493,14 @@ int BPF_KPROBE(ip_finish_output) { } 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->saddr = pkt_tuple->saddr; + message->daddr =pkt_tuple->daddr; + message->sport =pkt_tuple->sport; + message->dport = pkt_tuple->dport; 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; @@ -1490,3 +1508,32 @@ int BPF_KPROBE(ip_finish_output) { return 0; } + +//drop +SEC("tp/skb/kfree_skb") +int tp_kfree(struct trace_event_raw_kfree_skb *ctx) { + if(!kfree_info) + 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); + return 0; +} \ 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 96e5917a9..e99c96761 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.c +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.c @@ -29,6 +29,7 @@ #include #include #include +#include "dropreason.h" static volatile bool exiting = false; @@ -39,7 +40,7 @@ static char udp_file_path[1024]; 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,kfree_info = 0,addr_to_func=0; // flag static const char argp_program_doc[] = "Watch tcp/ip in network subsystem \n"; @@ -54,6 +55,8 @@ static const struct argp_option opts[] = { {"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"}, {}}; static error_t parse_arg(int key, char *arg, struct argp_state *state) { @@ -89,6 +92,12 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) { case 'n': net_filter = 1; break; + case 'k': + kfree_info = 1; + break; + case 'T': + addr_to_func = 1; + break; default: return ARGP_ERR_UNKNOWN; } @@ -101,6 +110,50 @@ static const struct argp argp = { .doc = argp_program_doc, }; +struct SymbolEntry{ + unsigned long addr; + char name[30]; +}; +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; + + while (low <= high) { + int mid = low + (high - low) / 2; + if (symbols[mid].addr < addr) { + result = mid; + low = mid + 1; + } else { + high = mid - 1; + } + } + + 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); +} static void sig_handler(int signo) { exiting = true; } static void bytes_to_str(char *str, unsigned long long num) { @@ -204,7 +257,7 @@ static int print_conns(struct netwatcher_bpf *skel) { } static int print_packet(void *ctx, void *packet_info, size_t size) { - if (udp_info || net_filter) + if (udp_info || net_filter || kfree_info) return 0; const struct pack_t *pack_info = packet_info; if (pack_info->err) { @@ -311,17 +364,51 @@ static int print_netfilter(void *ctx, void *packet_info, size_t size) { const struct netfilter *pack_info = packet_info; 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", 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); - } return 0; } +static int print_kfree(void *ctx, void *packet_info, size_t size) { + if(!kfree_info) + 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"); + } + 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); + else { + struct SymbolEntry data= findfunc(pack_info->location); + printf("%s+0x%-10lx",data.name,pack_info->location-data.addr); + } + printf("%s\n", SKB_Drop_Reason_Strings[pack_info->drop_reason]); + return 0; +} int main(int argc, char **argv) { char *last_slash = strrchr(argv[0], '/'); if (last_slash) { @@ -338,6 +425,7 @@ int main(int argc, char **argv) { struct ring_buffer *rb = NULL; struct ring_buffer *udp_rb = NULL; struct ring_buffer *netfilter_rb = NULL; + struct ring_buffer *kfree_rb = NULL; struct netwatcher_bpf *skel; int err; /* Parse command line arguments */ @@ -369,6 +457,10 @@ int main(int argc, char **argv) { skel->rodata->retrans_info = retrans_info; skel->rodata->udp_info = udp_info; skel->rodata->net_filter = net_filter; + skel->rodata->kfree_info = kfree_info; + + if(addr_to_func) + readallsym(); err = netwatcher_bpf__load(skel); if (err) { @@ -390,9 +482,13 @@ int main(int argc, char **argv) { { printf("%-20s %-20s %-20s %-20s %-20s %-20s %-20s %-20s %-20s\n", "saddr", "daddr","dprot", "sprot","local_input","pre_routing","local_out","post_routing","flag"); } + else if(kfree_info) + { + printf("%-20s %-20s %-10s %-10s %-9s %-24s %-25s\n", "saddr", "daddr","sprot", "dprot","prot","addr","reason"); + } else{ printf("%-22s %-10s %-10s %-10s %-10s %-10s %-5s %s\n", "SOCK", "SEQ", - "ACK", "MAC_TIME", "IP_TIME", "TRAN_TIME", "RX", "HTTP"); + "ACK", "MAC_TIME", "IP_TIME", "TRAN_TIME", "RX", "HTTP"); } udp_rb =ring_buffer__new(bpf_map__fd(skel->maps.udp_rb), print_udp, NULL, NULL); if (!udp_rb) { @@ -406,6 +502,12 @@ int main(int argc, char **argv) { fprintf(stderr, "Failed to create ring buffer\n"); goto cleanup; } + 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\n"); + goto cleanup; + } /* Set up ring buffer polling */ rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), print_packet, NULL, NULL); if (!rb) { @@ -437,6 +539,7 @@ int main(int argc, char **argv) { 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 */); print_conns(skel); sleep(1); /* Ctrl-C will cause -EINTR */ diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.h index e56b21a8c..ded67af39 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.h +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.h @@ -107,4 +107,15 @@ struct netfilter unsigned long long post_routing_time; unsigned int flag; }; +struct reasonissue +{ + unsigned int saddr; + unsigned int daddr; + unsigned short sport; + unsigned short dport; + long location; + unsigned short protocol; + int drop_reason; +}; + #endif /* __NETWATCHER_H */ \ No newline at end of file diff --git a/eBPF_Supermarket/kvm_watcher/Makefile b/eBPF_Supermarket/kvm_watcher/Makefile index a5d2d2eb0..e1ba47998 100644 --- a/eBPF_Supermarket/kvm_watcher/Makefile +++ b/eBPF_Supermarket/kvm_watcher/Makefile @@ -96,6 +96,6 @@ test: bpf clean: cd src && rm -f *.o *.skel.h *.bpf.o - rm -f $(notdir $(APP)) - rm -rf include/vmlinux.h + 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 f1d99cbb8..efe76f5be 100755 --- a/eBPF_Supermarket/kvm_watcher/README.md +++ b/eBPF_Supermarket/kvm_watcher/README.md @@ -31,14 +31,13 @@ **安装依赖:** ``` -sudo apt install clang libelf1 libelf-dev zlib1g-dev libbpf-dev linux-tools-$(uname -r) linux-cloud-tools-$(uname -r) -sudo modprobe kvm && sudo modprobe kvm-intel //加载kvm模块 +make deps ``` + **编译运行:** ``` -make deps make bpf sudo ./kvm_watcher [options] make clean @@ -93,6 +92,8 @@ BPF program used for monitoring KVM event `-w`:记录vcpu唤醒时的相关信息 +`-l`:记录kvm相关ioctl系统调用命令字 + `-p`:指定kvm虚拟机进程pid `-t`:监控时间 @@ -114,9 +115,6 @@ BPF program used for monitoring KVM event │ ├── kvm_mmu.h │ ├── kvm_vcpu.h │ └── kvm_watcher.h //公共头文件 -├── kvm_exit_bcc //bcc版本的vm exit实现 -│ ├── kvmexit_example.txt -│ └── kvmexit.py ├── Makefile //编译脚本 ├── README.md ├── src diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_exits.h b/eBPF_Supermarket/kvm_watcher/include/kvm_exits.h index 29fb8043a..5e9bc16de 100644 --- a/eBPF_Supermarket/kvm_watcher/include/kvm_exits.h +++ b/eBPF_Supermarket/kvm_watcher/include/kvm_exits.h @@ -54,8 +54,7 @@ struct exit { unsigned int vcpu_id; }; -static int trace_kvm_exit(struct exit *ctx, pid_t vm_pid) { - CHECK_PID(vm_pid); +static int trace_kvm_exit(struct exit *ctx) { u32 reason; reason = (u32)ctx->exit_reason; // 如果是节能停止退出,就不采集数据 diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_hypercall.h b/eBPF_Supermarket/kvm_watcher/include/kvm_hypercall.h index dba8bf0d8..ab9b26a52 100644 --- a/eBPF_Supermarket/kvm_watcher/include/kvm_hypercall.h +++ b/eBPF_Supermarket/kvm_watcher/include/kvm_hypercall.h @@ -54,8 +54,8 @@ struct { } hc_count SEC(".maps"); static int entry_emulate_hypercall(struct kvm_vcpu *vcpu, void *rb, - struct common_event *e, pid_t vm_pid) { - CHECK_PID(vm_pid); + struct common_event *e) { + u32 pid = bpf_get_current_pid_tgid() >> 32; u64 nr, a0, a1, a2, a3; nr = kvm_rax_read(vcpu); // 超级调用号 // 超级调用参数 diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_ioctl.h b/eBPF_Supermarket/kvm_watcher/include/kvm_ioctl.h index 97d13ba03..23ae5257c 100644 --- a/eBPF_Supermarket/kvm_watcher/include/kvm_ioctl.h +++ b/eBPF_Supermarket/kvm_watcher/include/kvm_ioctl.h @@ -21,11 +21,80 @@ #include "kvm_watcher.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]; + unsigned int cmd = (unsigned int)args->args[1]; + unsigned long arg = (unsigned long)args->args[2]; + char buf[256]; // 创建一个缓冲区来存储格式化后的字符串 + switch (cmd) { + case KVM_CREATE_VM: + bpf_printk("KVM_CREATE_VM: fd=%d\n", fd); + break; + case KVM_CREATE_VCPU: { + int vcpu_id; + bpf_probe_read(&vcpu_id, sizeof(vcpu_id), (void *)arg); + bpf_printk("KVM_CREATE_VCPU: fd=%d, vcpu_id=%d\n", fd, vcpu_id); + break; + } + case KVM_SET_USER_MEMORY_REGION: { + struct kvm_userspace_memory_region region; + bpf_probe_read(®ion, sizeof(region), (void *)arg); + // 打印或处理 region 数据 + bpf_printk( + "KVM_SET_USER_MEMORY_REGION: fd=%d, slot=%u,flags=%u\n", + fd, region.slot, region.flags); + bpf_printk( + "KVM_SET_USER_MEMORY_REGION: guest_phys_addr=%llx, memory_size=%lluK,userspace_addr=%llx\n", + region.guest_phys_addr, + region.memory_size / 1024, region.userspace_addr); + break; + } + case KVM_GET_VCPU_EVENTS: + case KVM_SET_VCPU_EVENTS: { + struct kvm_vcpu_events events; + bpf_probe_read(&events, sizeof(events), (void *)arg); + // 打印或处理 events 数据 + bpf_printk( + "KVM_SET/GET_VCPU_EVENTS: fd=%d, exception=%u, interrupt=%u\n", + fd, events.exception.nr, events.interrupt.nr); + break; + } + case KVM_TRANSLATE: { + struct kvm_translation tr; + bpf_probe_read(&tr, sizeof(tr), (void *)arg); + bpf_printk( + "KVM_TRANSLATE: fd=%d,linear_address=%llx, " + "physical_address=%llx\n", + fd, tr.linear_address, tr.physical_address); + break; + } + case KVM_INTERRUPT: { + struct kvm_interrupt irq; + bpf_probe_read(&irq, sizeof(irq), (void *)arg); + bpf_printk("KVM_INTERRUPT:fd=%d,interrupt vector:%d\n", fd, + irq.irq); + break; + } + default: + break; + } return 0; } diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_irq.h b/eBPF_Supermarket/kvm_watcher/include/kvm_irq.h index 6fa41d5df..90cc6545c 100644 --- a/eBPF_Supermarket/kvm_watcher/include/kvm_irq.h +++ b/eBPF_Supermarket/kvm_watcher/include/kvm_irq.h @@ -38,8 +38,7 @@ struct { __type(value, u64); } irq_inject_delay SEC(".maps"); -static int entry_kvm_pic_set_irq(int irq, pid_t vm_pid) { - CHECK_PID(vm_pid); +static int entry_kvm_pic_set_irq(int irq) { if (irq < 0 || irq >= PIC_NUM_PINS) { return 0; } @@ -77,8 +76,7 @@ static int exit_kvm_pic_set_irq(struct kvm_pic *s, int irq, int ret, void *rb, return 0; } -static int entry_kvm_ioapic_set_irq(int irq, pid_t vm_pid) { - CHECK_PID(vm_pid); +static int entry_kvm_ioapic_set_irq(int irq) { if (irq < 0 || irq >= IOAPIC_NUM_PINS) { return 0; } @@ -118,17 +116,25 @@ static int exit_kvm_ioapic_set_irq(struct kvm_ioapic *ioapic, int irq, int ret, return 0; } -static int entry_kvm_set_msi_irq(struct kvm *kvm, pid_t vm_pid) { - CHECK_PID(vm_pid); +static int entry_kvm_set_msi(struct kvm *kvm, + struct kvm_kernel_irq_routing_entry *routing_entry, + int level) { + bool x2apic_format; + bpf_probe_read_kernel(&x2apic_format, sizeof(bool), + &kvm->arch.x2apic_format); + if (x2apic_format && (routing_entry->msi.address_hi & 0xff)) + return 0; + if (!level) + return 0; pid_t tid = (u32)bpf_get_current_pid_tgid(); u64 ts = bpf_ktime_get_ns(); bpf_map_update_elem(&irq_set_delay, &tid, &ts, BPF_ANY); return 0; } -static int exit_kvm_set_msi_irq( - struct kvm *kvm, struct kvm_kernel_irq_routing_entry *routing_entry, - void *rb, struct common_event *e) { +static int exit_kvm_set_msi(struct kvm *kvm, + struct kvm_kernel_irq_routing_entry *routing_entry, + void *rb, struct common_event *e) { struct msi_msg msg = {.address_lo = routing_entry->msi.address_lo, .address_hi = routing_entry->msi.address_hi, .data = routing_entry->msi.data}; @@ -156,8 +162,7 @@ static int exit_kvm_set_msi_irq( return 0; } -static int entry_vmx_inject_irq(struct kvm_vcpu *vcpu, pid_t vm_pid) { - CHECK_PID(vm_pid); +static int entry_vmx_inject_irq(struct kvm_vcpu *vcpu) { u32 irq_nr; bool rei; bpf_probe_read_kernel(&irq_nr, sizeof(u32), &vcpu->arch.interrupt.nr); diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_mmu.h b/eBPF_Supermarket/kvm_watcher/include/kvm_mmu.h index 52d5b4fa0..ceea6d10d 100644 --- a/eBPF_Supermarket/kvm_watcher/include/kvm_mmu.h +++ b/eBPF_Supermarket/kvm_watcher/include/kvm_mmu.h @@ -25,8 +25,6 @@ #include #include -#define PAGE_SHIFT 12 - struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 8192); @@ -50,8 +48,7 @@ struct page_fault { char __data[0]; }; -static int trace_page_fault(struct page_fault *ctx, pid_t vm_pid) { - CHECK_PID(vm_pid); +static int trace_page_fault(struct page_fault *ctx) { u64 ts = bpf_ktime_get_ns(); u64 addr = ctx->fault_address; bpf_map_update_elem(&pf_delay, &addr, &ts, BPF_ANY); @@ -101,8 +98,7 @@ static int trace_tdp_page_fault(struct kvm_vcpu *vcpu, } static int trace_kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, - u64 error_code, pid_t vm_pid) { - CHECK_PID(vm_pid); + u64 error_code) { if (error_code & PFERR_RSVD_MASK) { u64 ts = bpf_ktime_get_ns(); u64 gfn = cr2_or_gpa >> PAGE_SHIFT; @@ -141,7 +137,7 @@ static int trace_handle_mmio_page_fault(struct mmio_page_fault *ctx, void *rb, bpf_map_update_elem(&pf_count, &gfn, &new_count, BPF_ANY); } e->page_fault_data.delay = delay; - e->page_fault_data.addr = gfn; + e->page_fault_data.addr = gfn << PAGE_SHIFT; e->page_fault_data.error_code = PFERR_RSVD_MASK; e->process.pid = bpf_get_current_pid_tgid() >> 32; bpf_get_current_comm(&e->process.comm, sizeof(e->process.comm)); diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_vcpu.h b/eBPF_Supermarket/kvm_watcher/include/kvm_vcpu.h index 4be02a2c4..f38a939b2 100644 --- a/eBPF_Supermarket/kvm_watcher/include/kvm_vcpu.h +++ b/eBPF_Supermarket/kvm_watcher/include/kvm_vcpu.h @@ -55,8 +55,7 @@ struct { } vcpu_tid SEC(".maps"); // 记录vcpu_halt的id信息 -static int trace_kvm_vcpu_halt(struct kvm_vcpu *vcpu, pid_t vm_pid) { - CHECK_PID(vm_pid); +static int trace_kvm_vcpu_halt(struct kvm_vcpu *vcpu) { u32 tid = bpf_get_current_pid_tgid(); u32 vcpu_id; bpf_probe_read_kernel(&vcpu_id, sizeof(vcpu->vcpu_id), &vcpu->vcpu_id); @@ -65,8 +64,8 @@ static int trace_kvm_vcpu_halt(struct kvm_vcpu *vcpu, pid_t vm_pid) { } // 使用kvm_vcpu_halt记录的数据,来获取vcpu的启动信息 static int trace_kvm_vcpu_wakeup(struct vcpu_wakeup *ctx, void *rb, - struct common_event *e, pid_t vm_pid) { - CHECK_PID(vm_pid); + struct common_event *e) { + u32 pid = bpf_get_current_pid_tgid() >> 32; u32 tid = bpf_get_current_pid_tgid(); u32 *vcpu_id = bpf_map_lookup_elem(&vcpu_tid, &tid); if (!vcpu_id) { @@ -87,8 +86,8 @@ static int trace_kvm_vcpu_wakeup(struct vcpu_wakeup *ctx, void *rb, } static int trace_kvm_halt_poll_ns(struct halt_poll_ns *ctx, void *rb, - struct common_event *e, pid_t vm_pid) { - CHECK_PID(vm_pid); + struct common_event *e) { + u32 pid = bpf_get_current_pid_tgid() >> 32; u32 tid = bpf_get_current_pid_tgid(); RESERVE_RINGBUF_ENTRY(rb, e); u64 time = bpf_ktime_get_ns(); @@ -127,8 +126,8 @@ static int trace_vmx_vcpu_load(struct kvm_vcpu *vcpu, int cpu, void *rb, static int trace_mark_page_dirty_in_slot(struct kvm *kvm, const struct kvm_memory_slot *memslot, gfn_t gfn, void *rb, - struct common_event *e, pid_t vm_pid) { - CHECK_PID(vm_pid); + struct common_event *e) { + u32 pid = bpf_get_current_pid_tgid() >> 32; u32 flags; struct kvm_memory_slot *slot; bpf_probe_read_kernel(&slot, sizeof(memslot), &memslot); diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_watcher.h b/eBPF_Supermarket/kvm_watcher/include/kvm_watcher.h index d94ea6bfa..4cc3db9c4 100644 --- a/eBPF_Supermarket/kvm_watcher/include/kvm_watcher.h +++ b/eBPF_Supermarket/kvm_watcher/include/kvm_watcher.h @@ -22,6 +22,8 @@ #define TASK_COMM_LEN 16 #define KVM_MEM_LOG_DIRTY_PAGES (1UL << 0) +#define PAGE_SHIFT 12 + #define NS_TO_US_FACTOR 1000.0 #define NS_TO_MS_FACTOR 1000000.0 @@ -66,9 +68,6 @@ } \ } while (0) -// 定义清屏宏 -#define CLEAR_SCREEN() printf("\033[2J\033[H\n") - #define RING_BUFFER_TIMEOUT_MS 100 #define RESERVE_RINGBUF_ENTRY(rb, e) \ @@ -79,10 +78,9 @@ e = _tmp; \ } while (0) -#define CHECK_PID(vm_pid) \ - __u32 pid = bpf_get_current_pid_tgid() >> 32; \ - if ((vm_pid) > 0 && pid != (vm_pid)) { \ - return 0; \ +#define CHECK_PID(vm_pid) \ + if ((vm_pid) > 0 && (bpf_get_current_pid_tgid() >> 32) != (vm_pid)) { \ + return 0; \ } struct reason_info { diff --git a/eBPF_Supermarket/kvm_watcher/kvm_exit_bcc/kvmexit.py b/eBPF_Supermarket/kvm_watcher/kvm_exit_bcc/kvmexit.py deleted file mode 100644 index dd157488f..000000000 --- a/eBPF_Supermarket/kvm_watcher/kvm_exit_bcc/kvmexit.py +++ /dev/null @@ -1,378 +0,0 @@ -#!/usr/bin/env python -# -# kvmexit.py -# -# Display the exit_reason and its statistics of each vm exit -# for all vcpus of all virtual machines. For example: -# $./kvmexit.py -# PID TID KVM_EXIT_REASON COUNT -# 1273551 1273568 EXIT_REASON_MSR_WRITE 6 -# 1274253 1274261 EXIT_REASON_EXTERNAL_INTERRUPT 1 -# 1274253 1274261 EXIT_REASON_HLT 12 -# ... -# -# Besides, we also allow users to specify one pid, tid(s), or one -# pid and its vcpu. See kvmexit_example.txt for more examples. -# -# @PID: each vitual machine's pid in the user space. -# @TID: the user space's thread of each vcpu of that virtual machine. -# @KVM_EXIT_REASON: the reason why the vm exits. -# @COUNT: the counts of the @KVM_EXIT_REASONS. -# -# REQUIRES: Linux 4.7+ (BPF_PROG_TYPE_TRACEPOINT support) -# -# Copyright (c) 2024 YYS. All rights reserved. -# Original code © 2024 ByteDance Inc. All rights reserved. -# Author(s): -# YYS -# 以下代码段是根据Fei Li的实现进行的修改 -# 原始代码链接:https://github.com/iovisor/bcc/blob/master/tools/kvmexit.py - - -from __future__ import print_function -from time import sleep -from bcc import BPF -import argparse -import multiprocessing -import os -import subprocess - -# -# Process Arguments -# -def valid_args_list(args): - args_list = args.split(",") - for arg in args_list: - try: - int(arg) - except: - raise argparse.ArgumentTypeError("must be valid integer") - return args_list - -# arguments -examples = """examples: - ./kvmexit # Display kvm_exit_reason and its statistics in real-time until Ctrl-C - ./kvmexit 5 # Display in real-time after sleeping 5s - ./kvmexit -p 3195281 # Collpase all tids for pid 3195281 with exit reasons sorted in descending order - ./kvmexit -p 3195281 20 # Collpase all tids for pid 3195281 with exit reasons sorted in descending order, and display after sleeping 20s - ./kvmexit -p 3195281 -v 0 # Display only vcpu0 for pid 3195281, descending sort by default - ./kvmexit -p 3195281 -a # Display all tids for pid 3195281 - ./kvmexit -t 395490 # Display only for tid 395490 with exit reasons sorted in descending order - ./kvmexit -t 395490 20 # Display only for tid 395490 with exit reasons sorted in descending order after sleeping 20s - ./kvmexit -T '395490,395491' # Display for a union like {395490, 395491} -""" -parser = argparse.ArgumentParser( - description="Display kvm_exit_reason and its statistics at a timed interval", - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=examples) -parser.add_argument("duration", nargs="?", default=99999999, type=int, help="show delta for next several seconds") -parser.add_argument("-p", "--pid", type=int, help="trace this PID only") -exgroup = parser.add_mutually_exclusive_group() -exgroup.add_argument("-t", "--tid", type=int, help="trace this TID only") -exgroup.add_argument("-T", "--tids", type=valid_args_list, help="trace a comma separated series of tids with no space in between") -exgroup.add_argument("-v", "--vcpu", type=int, help="trace this vcpu only") -exgroup.add_argument("-a", "--alltids", action="store_true", help="trace all tids for this pid") -args = parser.parse_args() -duration = int(args.duration) - -# -# Setup BPF -# - -# load BPF program -bpf_text = """ -#include - -#define REASON_NUM 76 -#define TGID_NUM 1024 - -struct exit_count { - u64 exit_ct[REASON_NUM]; -}; -BPF_PERCPU_ARRAY(init_value, struct exit_count, 1); -BPF_TABLE("percpu_hash", u64, struct exit_count, pcpu_kvm_stat, TGID_NUM); - -struct cache_info { - u64 cache_pid_tgid; - struct exit_count cache_exit_ct; -}; -BPF_PERCPU_ARRAY(pcpu_cache, struct cache_info, 1); - -TRACEPOINT_PROBE(kvm, kvm_exit) { - int cache_miss = 0; - int zero = 0; - u32 er = args->exit_reason; - if (er >= REASON_NUM) { - return 0; - } - - u64 cur_pid_tgid = bpf_get_current_pid_tgid(); - u32 tgid = cur_pid_tgid >> 32; - u32 pid = cur_pid_tgid; - - if (THREAD_FILTER) - return 0; - - struct exit_count *tmp_info = NULL, *initial = NULL; - struct cache_info *cache_p; - cache_p = pcpu_cache.lookup(&zero); - if (cache_p == NULL) { - return 0; - } - - if (cache_p->cache_pid_tgid == cur_pid_tgid) { - //a. If the cur_pid_tgid hit this physical cpu consecutively, save it to pcpu_cache - tmp_info = &cache_p->cache_exit_ct; - } else { - //b. If another pid_tgid matches this pcpu for the last hit, OR it is the first time to hit this physical cpu. - cache_miss = 1; - - // b.a Try to load the last cache struct if exists. - tmp_info = pcpu_kvm_stat.lookup(&cur_pid_tgid); - - // b.b If it is the first time for the cur_pid_tgid to hit this pcpu, employ a - // per_cpu array to initialize pcpu_kvm_stat's exit_count with each exit reason's count is zero - if (tmp_info == NULL) { - initial = init_value.lookup(&zero); - if (initial == NULL) { - return 0; - } - - pcpu_kvm_stat.update(&cur_pid_tgid, initial); - tmp_info = pcpu_kvm_stat.lookup(&cur_pid_tgid); - // To pass the verifier - if (tmp_info == NULL) { - return 0; - } - } - } - - if (er < REASON_NUM) { - tmp_info->exit_ct[er]++; - if (cache_miss == 1) { - if (cache_p->cache_pid_tgid != 0) { - // b.*.a Let's save the last hit cache_info into kvm_stat. - pcpu_kvm_stat.update(&cache_p->cache_pid_tgid, &cache_p->cache_exit_ct); - } - // b.* As the cur_pid_tgid meets current pcpu_cache_array for the first time, save it. - cache_p->cache_pid_tgid = cur_pid_tgid; - bpf_probe_read(&cache_p->cache_exit_ct, sizeof(*tmp_info), tmp_info); - } - return 0; - } - - return 0; -} -""" - -# format output -exit_reasons = ( - "EXCEPTION_NMI", - "EXTERNAL_INTERRUPT", - "TRIPLE_FAULT", - "INIT_SIGNAL", - "SIPI_SIGNAL ", - "N/A", - "N/A", - "INTERRUPT_WINDOW", - "NMI_WINDOW", - "TASK_SWITCH", - "CPUID", - "N/A", - "HLT", - "INVD", - "INVLPG", - "RDPMC", - "RDTSC", - "N/A", - "VMCALL", - "VMCLEAR", - "VMLAUNCH", - "VMPTRLD", - "VMPTRST", - "VMREAD", - "VMRESUME", - "VMWRITE", - "VMOFF", - "VMON", - "CR_ACCESS", - "DR_ACCESS", - "IO_INSTRUCTION", - "MSR_READ", - "MSR_WRITE", - "INVALID_STATE", - "MSR_LOAD_FAIL", - "N/A", - "MWAIT_INSTRUCTION", - "MONITOR_TRAP_FLAG", - "N/A", - "MONITOR_INSTRUCTION", - "PAUSE_INSTRUCTION", - "MCE_DURING_VMENTRY", - "N/A", - "TPR_BELOW_THRESHOLD", - "APIC_ACCESS", - "EOI_INDUCED", - "GDTR_IDTR", - "LDTR_TR", - "EPT_VIOLATION", - "EPT_MISCONFIG", - "INVEPT", - "RDTSCP", - "PREEMPTION_TIMER", - "INVVPID", - "WBINVD", - "XSETBV", - "APIC_WRITE", - "RDRAND", - "INVPCID", - "VMFUNC", - "ENCLS", - "RDSEED", - "PML_FULL", - "XSAVES", - "XRSTORS", - "N/A", - "N/A", - "UMWAIT", - "TPAUSE", - "N/A", - "N/A", - "N/A", - "N/A", - "N/A", - "BUS_LOCK", - "NOTIFY " -) - -# -# Do some checks -# -try: - # Currently, only adapte on intel architecture - cmd = "cat /proc/cpuinfo | grep vendor_id | head -n 1" - arch_info = subprocess.check_output(cmd, shell=True).strip() - if b"Intel" in arch_info: - pass - else: - raise Exception("Currently we only support Intel architecture, please do expansion if needs more.") - - # Check if kvm module is loaded - if os.access("/dev/kvm", os.R_OK | os.W_OK): - pass - else: - raise Exception("Please insmod kvm module to use kvmexit tool.") -except Exception as e: - raise Exception("Failed to do precondition check, due to: %s." % e) - -def find_tid(tgt_dir, tgt_vcpu): - for tid in os.listdir(tgt_dir): - path = tgt_dir + "/" + tid + "/comm" - fp = open(path, "r") - comm = fp.read() - if (comm.find(tgt_vcpu) != -1): - return tid - return -1 - -# set process/thread filter -thread_context = "" -header_format = "" -need_collapse = not args.alltids -if args.tid is not None: - thread_context = "TID %s" % args.tid - thread_filter = 'pid != %s' % args.tid -elif args.tids is not None: - thread_context = "TIDS %s" % args.tids - thread_filter = "pid != " + " && pid != ".join(args.tids) - header_format = "TIDS " -elif args.pid is not None: - thread_context = "PID %s" % args.pid - thread_filter = 'tgid != %s' % args.pid - if args.vcpu is not None: - thread_context = "PID %s VCPU %s" % (args.pid, args.vcpu) - # transfer vcpu to tid - tgt_dir = '/proc/' + str(args.pid) + '/task' - tgt_vcpu = "CPU " + str(args.vcpu) - args.tid = find_tid(tgt_dir, tgt_vcpu) - if args.tid == -1: - raise Exception("There's no v%s for PID %d." % (tgt_vcpu, args.pid)) - thread_filter = 'pid != %s' % args.tid - elif args.alltids: - thread_context = "PID %s and its all threads" % args.pid - header_format = "TID " -else: - thread_context = "all threads" - thread_filter = '0' - header_format = "PID TID " -bpf_text = bpf_text.replace('THREAD_FILTER', thread_filter) -b = BPF(text=bpf_text) - - -# header -print("Display kvm exit reasons and statistics for %s" % thread_context, end="") -if duration < 99999999: - print(" after sleeping %d secs." % duration) -else: - print("... Hit Ctrl-C to end.") - -try: - sleep(duration) -except KeyboardInterrupt: - print() - - -# Currently, sort multiple tids in descending order is not supported. -if (args.pid or args.tid): - ct_reason = [] - if args.pid: - tgid_exit = [0 for i in range(len(exit_reasons))] - -# output -print("%s%-35s %s" % (header_format, "KVM_EXIT_REASON", "COUNT")) - -pcpu_kvm_stat = b["pcpu_kvm_stat"] -pcpu_cache = b["pcpu_cache"] -for k, v in pcpu_kvm_stat.items(): - tgid = k.value >> 32 - pid = k.value & 0xffffffff - for i in range(0, len(exit_reasons)): - sum1 = 0 - for inner_cpu in range(0, multiprocessing.cpu_count()): - cachePIDTGID = pcpu_cache[0][inner_cpu].cache_pid_tgid - # Take priority to check if it is in cache - if cachePIDTGID == k.value: - sum1 += pcpu_cache[0][inner_cpu].cache_exit_ct.exit_ct[i] - # If not in cache, find from kvm_stat - else: - sum1 += v[inner_cpu].exit_ct[i] - if sum1 == 0: - continue - - if (args.pid and args.pid == tgid and need_collapse): - tgid_exit[i] += sum1 - elif (args.tid and args.tid == pid): - ct_reason.append((sum1, i)) - elif not need_collapse or args.tids: - print("%-8u %-35s %-8u" % (pid, exit_reasons[i], sum1)) - else: - print("%-8u %-8u %-35s %-8u" % (tgid, pid, exit_reasons[i], sum1)) - - # Display only for the target tid in descending sort - if (args.tid and args.tid == pid): - ct_reason.sort(reverse=True) - for i in range(0, len(ct_reason)): - if ct_reason[i][0] == 0: - continue - print("%-35s %-8u" % (exit_reasons[ct_reason[i][1]], ct_reason[i][0])) - break - - -# Aggregate all tids' counts for this args.pid in descending sort -if args.pid and need_collapse: - for i in range(0, len(exit_reasons)): - ct_reason.append((tgid_exit[i], i)) - ct_reason.sort(reverse=True) - for i in range(0, len(ct_reason)): - if ct_reason[i][0] == 0: - continue - print("%-35s %-8u" % (exit_reasons[ct_reason[i][1]], ct_reason[i][0])) diff --git a/eBPF_Supermarket/kvm_watcher/kvm_exit_bcc/kvmexit_example.txt b/eBPF_Supermarket/kvm_watcher/kvm_exit_bcc/kvmexit_example.txt deleted file mode 100644 index 3ee773bbe..000000000 --- a/eBPF_Supermarket/kvm_watcher/kvm_exit_bcc/kvmexit_example.txt +++ /dev/null @@ -1,250 +0,0 @@ -Demonstrations of kvm exit reasons, the Linux eBPF/bcc version. - - -Considering virtual machines' frequent exits can cause performance problems, -this tool aims to locate the frequent exited reasons and then find solutions -to reduce or even avoid the exit, by displaying the detail exit reasons and -the counts of each vm exit for all vms running on one physical machine. - - -Features of this tool -===================== - -- Although there is a patch: [KVM: x86: add full vm-exit reason debug entries] - (https://patchwork.kernel.org/project/kvm/patch/1555939499-30854-1-git-send-email-pizhenwei@bytedance.com/) - trying to fill more vm-exit reason debug entries, just as the comments said, - the code allocates lots of memory that may never be consumed, misses some - arch-specific kvm causes, and can not do kernel aggregation. Instead bcc, as - a user space tool, can implement all these functions more easily and flexibly. -- The bcc python logic could provide nice kernel aggregation and custom output, - like collpasing all tids for one pid (e.i. one vm's qemu process id) with exit - reasons sorted in descending order. For more information, see the following - #USAGE message. -- The bpf in-kernel percpu_array and percpu_cache further improves performance. - For more information, see the following #Help to understand. - - -Limited -======= - -In view of the hardware-assisted virtualization technology of -different architectures, currently we only adapt on vmx in intel. -And the amd feature is on the road.. - - -Example output: -=============== - -# ./kvmexit.py -Display kvm exit reasons and statistics for all threads... Hit Ctrl-C to end. -PID TID KVM_EXIT_REASON COUNT -^C1273551 1273568 EXIT_REASON_HLT 12 -1273551 1273568 EXIT_REASON_MSR_WRITE 6 -1274253 1274261 EXIT_REASON_EXTERNAL_INTERRUPT 1 -1274253 1274261 EXIT_REASON_HLT 12 -1274253 1274261 EXIT_REASON_MSR_WRITE 4 - -# ./kvmexit.py 6 -Display kvm exit reasons and statistics for all threads after sleeping 6 secs. -PID TID KVM_EXIT_REASON COUNT -1273903 1273922 EXIT_REASON_EXTERNAL_INTERRUPT 175 -1273903 1273922 EXIT_REASON_CPUID 10 -1273903 1273922 EXIT_REASON_HLT 6043 -1273903 1273922 EXIT_REASON_IO_INSTRUCTION 24 -1273903 1273922 EXIT_REASON_MSR_WRITE 15025 -1273903 1273922 EXIT_REASON_PAUSE_INSTRUCTION 11 -1273903 1273922 EXIT_REASON_EOI_INDUCED 12 -1273903 1273922 EXIT_REASON_EPT_VIOLATION 6 -1273903 1273922 EXIT_REASON_EPT_MISCONFIG 380 -1273903 1273922 EXIT_REASON_PREEMPTION_TIMER 194 -1273551 1273568 EXIT_REASON_EXTERNAL_INTERRUPT 18 -1273551 1273568 EXIT_REASON_HLT 989 -1273551 1273568 EXIT_REASON_IO_INSTRUCTION 10 -1273551 1273568 EXIT_REASON_MSR_WRITE 2205 -1273551 1273568 EXIT_REASON_PAUSE_INSTRUCTION 1 -1273551 1273568 EXIT_REASON_EOI_INDUCED 5 -1273551 1273568 EXIT_REASON_EPT_MISCONFIG 61 -1273551 1273568 EXIT_REASON_PREEMPTION_TIMER 14 - -# ./kvmexit.py -p 1273795 5 -Display kvm exit reasons and statistics for PID 1273795 after sleeping 5 secs. -KVM_EXIT_REASON COUNT -MSR_WRITE 13467 -HLT 5060 -PREEMPTION_TIMER 345 -EPT_MISCONFIG 264 -EXTERNAL_INTERRUPT 169 -EPT_VIOLATION 18 -PAUSE_INSTRUCTION 6 -IO_INSTRUCTION 4 -EOI_INDUCED 2 - -# ./kvmexit.py -p 1273795 5 -a -Display kvm exit reasons and statistics for PID 1273795 and its all threads after sleeping 5 secs. -TID KVM_EXIT_REASON COUNT -1273819 EXTERNAL_INTERRUPT 64 -1273819 HLT 2802 -1273819 IO_INSTRUCTION 4 -1273819 MSR_WRITE 7196 -1273819 PAUSE_INSTRUCTION 2 -1273819 EOI_INDUCED 2 -1273819 EPT_VIOLATION 6 -1273819 EPT_MISCONFIG 162 -1273819 PREEMPTION_TIMER 194 -1273820 EXTERNAL_INTERRUPT 78 -1273820 HLT 2054 -1273820 MSR_WRITE 5199 -1273820 EPT_VIOLATION 2 -1273820 EPT_MISCONFIG 77 -1273820 PREEMPTION_TIMER 102 - -# ./kvmexit.py -p 1273795 -v 0 -Display kvm exit reasons and statistics for PID 1273795 VCPU 0... Hit Ctrl-C to end. -KVM_EXIT_REASON COUNT -^CMSR_WRITE 2076 -HLT 795 -PREEMPTION_TIMER 86 -EXTERNAL_INTERRUPT 20 -EPT_MISCONFIG 10 -PAUSE_INSTRUCTION 2 -IO_INSTRUCTION 2 -EPT_VIOLATION 1 -EOI_INDUCED 1 - -# ./kvmexit.py -p 1273795 -v 0 4 -Display kvm exit reasons and statistics for PID 1273795 VCPU 0 after sleeping 4 secs. -KVM_EXIT_REASON COUNT -MSR_WRITE 4726 -HLT 1827 -PREEMPTION_TIMER 78 -EPT_MISCONFIG 67 -EXTERNAL_INTERRUPT 28 -IO_INSTRUCTION 4 -EOI_INDUCED 2 -PAUSE_INSTRUCTION 2 - -# ./kvmexit.py -p 1273795 -v 4 4 -Traceback (most recent call last): - File "tools/kvmexit.py", line 306, in - raise Exception("There's no v%s for PID %d." % (tgt_vcpu, args.pid)) - Exception: There's no vCPU 4 for PID 1273795. - -# ./kvmexit.py -t 1273819 10 -Display kvm exit reasons and statistics for TID 1273819 after sleeping 10 secs. -KVM_EXIT_REASON COUNT -MSR_WRITE 13318 -HLT 5274 -EPT_MISCONFIG 263 -PREEMPTION_TIMER 171 -EXTERNAL_INTERRUPT 109 -IO_INSTRUCTION 8 -PAUSE_INSTRUCTION 5 -EOI_INDUCED 4 -EPT_VIOLATION 2 - -# ./kvmexit.py -T '1273820,1273819' -Display kvm exit reasons and statistics for TIDS ['1273820', '1273819']... Hit Ctrl-C to end. -TIDS KVM_EXIT_REASON COUNT -^C1273819 EXTERNAL_INTERRUPT 300 -1273819 HLT 13718 -1273819 IO_INSTRUCTION 26 -1273819 MSR_WRITE 37457 -1273819 PAUSE_INSTRUCTION 13 -1273819 EOI_INDUCED 13 -1273819 EPT_VIOLATION 53 -1273819 EPT_MISCONFIG 654 -1273819 PREEMPTION_TIMER 958 -1273820 EXTERNAL_INTERRUPT 212 -1273820 HLT 9002 -1273820 MSR_WRITE 25495 -1273820 PAUSE_INSTRUCTION 2 -1273820 EPT_VIOLATION 64 -1273820 EPT_MISCONFIG 396 -1273820 PREEMPTION_TIMER 268 - - -Help to understand -================== - -We use a PERCPU_ARRAY: pcpuArrayA and a percpu_hash: hashA to collaboratively -store each kvm exit reason and its count. The reason is there exists a rule when -one vcpu exits and re-enters, it tends to continue to run on the same physical -cpu (pcpu as follows) as the last cycle, which is also called 'cache hit'. Thus -we turn to use a PERCPU_ARRAY to record the 'cache hit' situation to speed -things up; and for other cases, then use a percpu_hash. - -BTW, we originally use a common hash to do this, with a u64(exit_reason) -key and a struct exit_info {tgid_pid, exit_reason} value. But due to -the big lock in bpf_hash, each updating is quite performance consuming. - -Now imagine here is a pid_tgidA (vcpu A) exits and is going to run on -pcpuArrayA, the BPF code flow is as follows: - - pid_tgidA keeps running on the same pcpu - // \\ - // \\ - // Y N \\ - // \\ - a. cache_hit b. cache_miss -(cacheA's pid_tgid matches pid_tgidA) || - | || - | || - "increase percpu exit_ct and return" || - [*Note*] || - pid_tgidA ever been exited on pcpuArrayA? - // \\ - // \\ - // \\ - // Y N \\ - // \\ - b.a load_last_hashA b.b initialize_hashA_with_zero - \ / - \ / - \ / - "increase percpu exit_ct" - || - || - is another pid_tgid been running on pcpuArrayA? - // \\ - // Y N \\ - // \\ - b.*.a save_theLastHit_hashB do_nothing - \\ // - \\ // - \\ // - b.* save_to_pcpuArrayA - - -[*Note*] we do not update the table in above "a.", in case the vcpu hit the same -pcpu again when exits next time, instead we only update until this pcpu is not -hitted by the same tgidpid(vcpu) again, which is in "b.*.a" and "b.*". - - -USAGE message: -============== - -# ./kvmexit.py -h -usage: kvmexit.py [-h] [-p PID [-v VCPU | -a] ] [-t TID | -T 'TID1,TID2'] [duration] - -Display kvm_exit_reason and its statistics at a timed interval - -optional arguments: - -h, --help show this help message and exit - -p PID, --pid PID display process with this PID only, collpase all tids with exit reasons sorted in descending order - -v VCPU, --v VCPU display this VCPU only for this PID - -a, --alltids display all TIDS for this PID - -t TID, --tid TID display thread with this TID only with exit reasons sorted in descending order - -T 'TID1,TID2', --tids 'TID1,TID2' - display threads for a union like {395490, 395491} - duration duration of display, after sleeping several seconds - -examples: - ./kvmexit # Display kvm_exit_reason and its statistics in real-time until Ctrl-C - ./kvmexit 5 # Display in real-time after sleeping 5s - ./kvmexit -p 3195281 # Collpase all tids for pid 3195281 with exit reasons sorted in descending order - ./kvmexit -p 3195281 20 # Collpase all tids for pid 3195281 with exit reasons sorted in descending order, and display after sleeping 20s - ./kvmexit -p 3195281 -v 0 # Display only vcpu0 for pid 3195281, descending sort by default - ./kvmexit -p 3195281 -a # Display all tids for pid 3195281 - ./kvmexit -t 395490 # Display only for tid 395490 with exit reasons sorted in descending order - ./kvmexit -t 395490 20 # Display only for tid 395490 with exit reasons sorted in descending order after sleeping 20s - ./kvmexit -T '395490,395491' # Display for a union like {395490, 395491} \ No newline at end of file diff --git a/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.bpf.c b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.bpf.c index e7d480cc3..a7867c8eb 100644 --- a/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.bpf.c +++ b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.bpf.c @@ -22,11 +22,11 @@ #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 "../include/kvm_ioctl.h" char LICENSE[] SEC("license") = "Dual BSD/GPL"; @@ -42,22 +42,25 @@ struct { // 获取vcpu的id SEC("fentry/kvm_vcpu_halt") int BPF_PROG(fentry_kvm_vcpu_halt, struct kvm_vcpu *vcpu) { - return trace_kvm_vcpu_halt(vcpu, vm_pid); + 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) { - return trace_kvm_vcpu_wakeup(ctx, &rb, e, vm_pid); + return trace_kvm_vcpu_wakeup(ctx, &rb, e); } // 记录vcpu的halt_poll(暂停轮询)时间变化 SEC("tp/kvm/kvm_halt_poll_ns") int tp_kvm_halt_poll_ns(struct halt_poll_ns *ctx) { - return trace_kvm_halt_poll_ns(ctx, &rb, e, vm_pid); + CHECK_PID(vm_pid); + return trace_kvm_halt_poll_ns(ctx, &rb, e); } // 记录vm_exit的时间 SEC("tp/kvm/kvm_exit") int tp_exit(struct exit *ctx) { - return trace_kvm_exit(ctx, vm_pid); + CHECK_PID(vm_pid); + return trace_kvm_exit(ctx); } // 记录vm_entry和vm_exit的时间差 SEC("tp/kvm/kvm_entry") @@ -72,12 +75,14 @@ int BPF_KPROBE(kp_vmx_vcpu_load, struct kvm_vcpu *vcpu, int cpu) { 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) { - return trace_mark_page_dirty_in_slot(kvm, memslot, gfn, &rb, e, vm_pid); + CHECK_PID(vm_pid); + return trace_mark_page_dirty_in_slot(kvm, memslot, gfn, &rb, e); } SEC("tp/kvm/kvm_page_fault") int tp_page_fault(struct page_fault *ctx) { - return trace_page_fault(ctx, vm_pid); + CHECK_PID(vm_pid); + return trace_page_fault(ctx); } SEC("fexit/kvm_tdp_page_fault") @@ -89,7 +94,8 @@ int BPF_PROG(fexit_tdp_page_fault, struct kvm_vcpu *vcpu, SEC("fentry/kvm_mmu_page_fault") int BPF_PROG(fentry_kvm_mmu_page_fault, struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, u64 error_code) { - return trace_kvm_mmu_page_fault(vcpu, cr2_or_gpa, error_code, vm_pid); + CHECK_PID(vm_pid); + return trace_kvm_mmu_page_fault(vcpu, cr2_or_gpa, error_code); } SEC("tp/kvmmmu/handle_mmio_page_fault") @@ -100,7 +106,8 @@ int tp_handle_mmio_page_fault(struct mmio_page_fault *ctx) { SEC("fentry/kvm_pic_set_irq") int BPF_PROG(fentry_kvm_pic_set_irq, struct kvm_pic *s, int irq, int irq_source_id, int level) { - return entry_kvm_pic_set_irq(irq, vm_pid); + CHECK_PID(vm_pid); + return entry_kvm_pic_set_irq(irq); } SEC("fexit/kvm_pic_set_irq") @@ -112,7 +119,8 @@ int BPF_PROG(fexit_kvm_pic_set_irq, struct kvm_pic *s, int irq, SEC("fentry/ioapic_set_irq") int BPF_PROG(fentry_kvm_ioapic_set_irq, struct kvm_ioapic *ioapic, int irq, int irq_level, bool line_status) { - return entry_kvm_ioapic_set_irq(irq, vm_pid); + CHECK_PID(vm_pid); + return entry_kvm_ioapic_set_irq(irq); } SEC("fexit/ioapic_set_irq") @@ -121,23 +129,25 @@ int BPF_PROG(fexit_kvm_ioapic_set_irq, struct kvm_ioapic *ioapic, int irq, return exit_kvm_ioapic_set_irq(ioapic, irq, ret, &rb, e); } -SEC("fentry/kvm_set_msi_irq") -int BPF_PROG(fentry_kvm_set_msi_irq, struct kvm *kvm, +SEC("fentry/kvm_set_msi") +int BPF_PROG(fentry_kvm_set_msi, struct kvm_kernel_irq_routing_entry *routing_entry, - struct kvm_lapic_irq *irq) { - return entry_kvm_set_msi_irq(kvm, vm_pid); + struct kvm *kvm, int irq_source_id, int level, bool line_status) { + CHECK_PID(vm_pid); + return entry_kvm_set_msi(kvm, routing_entry, level); } -SEC("fexit/kvm_set_msi_irq") -int BPF_PROG(fexit_kvm_set_msi_irq, struct kvm *kvm, +SEC("fexit/kvm_set_msi") +int BPF_PROG(fexit_kvm_set_msi, struct kvm_kernel_irq_routing_entry *routing_entry, - struct kvm_lapic_irq *irq) { - return exit_kvm_set_msi_irq(kvm, routing_entry, &rb, e); + struct kvm *kvm, int irq_source_id, int level, bool line_status) { + return exit_kvm_set_msi(kvm, routing_entry, &rb, e); } SEC("fentry/vmx_inject_irq") int BPF_PROG(fentry_vmx_inject_irq, struct kvm_vcpu *vcpu, bool reinjected) { - return entry_vmx_inject_irq(vcpu, vm_pid); + CHECK_PID(vm_pid); + return entry_vmx_inject_irq(vcpu); } SEC("fexit/vmx_inject_irq") @@ -147,10 +157,11 @@ 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) { - return entry_emulate_hypercall(vcpu, &rb, e, vm_pid); + CHECK_PID(vm_pid); + return entry_emulate_hypercall(vcpu, &rb, e); } -SEC("tracepoint/syscalls/sys_enter_ioctl") +SEC("tp/syscalls/sys_enter_ioctl") int tp_ioctl(struct trace_event_raw_sys_enter *args) { return trace_kvm_ioctl(args); } \ 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 2259a45c4..1a751baf8 100644 --- a/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c +++ b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c @@ -266,6 +266,7 @@ static struct env { bool execute_irq_inject; bool execute_hypercall; bool execute_ioctl; + bool verbose; int monitoring_time; pid_t vm_pid; enum EventType event_type; @@ -281,6 +282,7 @@ static struct env { .mmio_page_fault = false, .execute_hypercall = false, .execute_ioctl = false, + .verbose = false, .monitoring_time = 0, .vm_pid = -1, .event_type = NONE_TYPE, @@ -312,6 +314,7 @@ static const struct argp_option opts[] = { {"vm_pid", 'p', "PID", 0, "Specify the virtual machine pid to monitor."}, {"monitoring_time", 't', "SEC", 0, "Time for monitoring."}, {"kvm_ioctl", 'l', NULL, 0, "Monitoring the KVM IOCTL."}, + {"verbose", 'v', NULL, 0, "Verbose debug output"}, {NULL, 'H', NULL, OPTION_HIDDEN, "Show the full help"}, {}, }; @@ -321,6 +324,9 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) { case 'H': argp_state_help(state, stderr, ARGP_HELP_STD_HELP); break; + case 'v': + env.verbose = true; + break; case 'w': SET_OPTION_AND_CHECK_USAGE(option_selected, env.execute_vcpu_wakeup); @@ -398,6 +404,8 @@ static const struct argp argp = { 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); } @@ -485,7 +493,7 @@ static int handle_event(void *ctx, void *data, size_t data_sz) { } case PAGE_FAULT: { // 使用 e->page_fault_data 访问 PAGE_FAULT 特有成员 - printf("%-18.6f %-15s %-10u %-14llx %-6u %-10.4f ", timestamp_ms, + 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, NS_TO_US_WITH_DECIMAL(e->page_fault_data.delay)); @@ -657,6 +665,7 @@ static int print_event_head(struct env *env) { "VAILD?"); break; case EXIT: + printf("Waiting vm_exit ... \n"); break; case HALT_POLL: printf("%-18s %-15s %-15s %-10s %-7s %-11s %-10s\n", "TIME(ms)", @@ -669,12 +678,12 @@ static int print_event_head(struct env *env) { break; case PAGE_FAULT: printf("%-18s %-15s %-10s %-12s %-6s %-10s %-20s %-17s %-10s %s\n", - "TIME(ms)", "COMM", "PID", "(f(GPA)m(GFN))", "COUNT", - "DELAY(us)", "HVA", "PFN", "MEM_SLOTID", "ERROR_TYPE"); + "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", "CHIP/PIN", "DST/VEC", "OTHERS"); + "COMM", "PID", "DELAY", "TYPE/PIN", "DST/VEC", "OTHERS"); break; case IRQ_INJECT: printf("%-18s %-15s %-10s %-10s %-10s %-10s %-10s %-10s\n", @@ -695,7 +704,10 @@ static int print_event_head(struct env *env) { break; } case IOCTL: { - printf("wait....\n"); + printf( + "Successfully started! Please run `sudo cat " + "/sys/kernel/debug/tracing/trace_pipe` " + "to see output of the BPF programs.\n"); break; } default: @@ -737,9 +749,9 @@ static void set_disable_load(struct kvm_watcher_bpf *skel) { env.execute_irqchip ? true : false); bpf_program__set_autoload(skel->progs.fexit_kvm_ioapic_set_irq, env.execute_irqchip ? true : false); - bpf_program__set_autoload(skel->progs.fentry_kvm_set_msi_irq, + bpf_program__set_autoload(skel->progs.fentry_kvm_set_msi, env.execute_irqchip ? true : false); - bpf_program__set_autoload(skel->progs.fexit_kvm_set_msi_irq, + bpf_program__set_autoload(skel->progs.fexit_kvm_set_msi, env.execute_irqchip ? true : false); bpf_program__set_autoload(skel->progs.fentry_vmx_inject_irq, env.execute_irq_inject ? true : false); @@ -990,11 +1002,6 @@ int main(int argc, char **argv) { goto cleanup; } - // 清屏 - if (option_selected) { - CLEAR_SCREEN(); - } - /*打印信息头*/ err = print_event_head(&env); if (err) { @@ -1003,7 +1010,6 @@ int main(int argc, char **argv) { } while (!exiting) { err = ring_buffer__poll(rb, RING_BUFFER_TIMEOUT_MS /* timeout, ms */); - if (env.execute_hypercall) { print_map_and_check_error(print_hc_map, skel, "hypercall", err); } diff --git a/eBPF_Supermarket/mem_watcher/cma/Makefile b/eBPF_Supermarket/mem_watcher/cma/Makefile new file mode 100644 index 000000000..c3a2cd4fc --- /dev/null +++ b/eBPF_Supermarket/mem_watcher/cma/Makefile @@ -0,0 +1,114 @@ +# 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_OBJ := $(abspath $(OUTPUT)/libbpf.a) +BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) +BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool + +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 := ../libbpf-bootstrap/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)) +CFLAGS := -g -Wall +ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) + +APPS = cma_monitor + +# 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: $(APPS) + +.PHONY: clean +clean: + $(call msg,CLEAN) + $(Q)rm -rf $(OUTPUT) $(APPS) + +$(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 + +# Build BPF code +$(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) \ + $(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 +$(OUTPUT)/%.skel.h: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL) + $(call msg,GEN-SKEL,$@) + $(Q)$(BPFTOOL) gen skeleton $< > $@ + +# 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 $(filter %.c,$^) -o $@ + +# Build application binary +$(APPS): %: $(OUTPUT)/%.o $(LIBBPF_OBJ) | $(OUTPUT) + $(call msg,BINARY,$@) + $(Q)$(CC) $(CFLAGS) $^ $(ALL_LDFLAGS) -lelf -lz -o $@ + +# delete failed targets +.DELETE_ON_ERROR: + +# keep intermediate (.skel.h, .bpf.o, etc) targets +.SECONDARY: + diff --git a/eBPF_Supermarket/mem_watcher/cma/cma_monitor.bpf.c b/eBPF_Supermarket/mem_watcher/cma/cma_monitor.bpf.c new file mode 100644 index 000000000..dd18dd50c --- /dev/null +++ b/eBPF_Supermarket/mem_watcher/cma/cma_monitor.bpf.c @@ -0,0 +1,62 @@ +#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/mem_watcher/cma/cma_monitor.c b/eBPF_Supermarket/mem_watcher/cma/cma_monitor.c new file mode 100644 index 000000000..cecf5bc82 --- /dev/null +++ b/eBPF_Supermarket/mem_watcher/cma/cma_monitor.c @@ -0,0 +1,51 @@ +#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/mem_watcher/cma/cma_monitor.h b/eBPF_Supermarket/mem_watcher/cma/cma_monitor.h new file mode 100644 index 000000000..b9b3eddb1 --- /dev/null +++ b/eBPF_Supermarket/mem_watcher/cma/cma_monitor.h @@ -0,0 +1,10 @@ +// 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/mem_watcher/ion/Makefile b/eBPF_Supermarket/mem_watcher/ion/Makefile new file mode 100644 index 000000000..e9e8ef726 --- /dev/null +++ b/eBPF_Supermarket/mem_watcher/ion/Makefile @@ -0,0 +1,114 @@ +# 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_OBJ := $(abspath $(OUTPUT)/libbpf.a) +BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) +BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool + +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 := ../libbpf-bootstrap/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)) +CFLAGS := -g -Wall +ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) + +APPS = ion_monitor + +# 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: $(APPS) + +.PHONY: clean +clean: + $(call msg,CLEAN) + $(Q)rm -rf $(OUTPUT) $(APPS) + +$(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 + +# Build BPF code +$(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) \ + $(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 +$(OUTPUT)/%.skel.h: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL) + $(call msg,GEN-SKEL,$@) + $(Q)$(BPFTOOL) gen skeleton $< > $@ + +# 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 $(filter %.c,$^) -o $@ + +# Build application binary +$(APPS): %: $(OUTPUT)/%.o $(LIBBPF_OBJ) | $(OUTPUT) + $(call msg,BINARY,$@) + $(Q)$(CC) $(CFLAGS) $^ $(ALL_LDFLAGS) -lelf -lz -o $@ + +# delete failed targets +.DELETE_ON_ERROR: + +# keep intermediate (.skel.h, .bpf.o, etc) targets +.SECONDARY: + diff --git a/eBPF_Supermarket/mem_watcher/ion/ion_monitor.bpf.c b/eBPF_Supermarket/mem_watcher/ion/ion_monitor.bpf.c new file mode 100644 index 000000000..e91936618 --- /dev/null +++ b/eBPF_Supermarket/mem_watcher/ion/ion_monitor.bpf.c @@ -0,0 +1,63 @@ +#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/mem_watcher/ion/ion_monitor.c b/eBPF_Supermarket/mem_watcher/ion/ion_monitor.c new file mode 100644 index 000000000..d22a7e004 --- /dev/null +++ b/eBPF_Supermarket/mem_watcher/ion/ion_monitor.c @@ -0,0 +1,57 @@ +#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/mem_watcher/ion/ion_monitor.h b/eBPF_Supermarket/mem_watcher/ion/ion_monitor.h new file mode 100644 index 000000000..e6712713e --- /dev/null +++ b/eBPF_Supermarket/mem_watcher/ion/ion_monitor.h @@ -0,0 +1,10 @@ +// 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/mem_watcher/page_fault/cma/cma_monitor.bpf.c b/eBPF_Supermarket/mem_watcher/page_fault/cma/cma_monitor.bpf.c new file mode 100644 index 000000000..dd18dd50c --- /dev/null +++ b/eBPF_Supermarket/mem_watcher/page_fault/cma/cma_monitor.bpf.c @@ -0,0 +1,62 @@ +#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/mem_watcher/page_fault/cma/cma_monitor.c b/eBPF_Supermarket/mem_watcher/page_fault/cma/cma_monitor.c new file mode 100644 index 000000000..cecf5bc82 --- /dev/null +++ b/eBPF_Supermarket/mem_watcher/page_fault/cma/cma_monitor.c @@ -0,0 +1,51 @@ +#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/mem_watcher/page_fault/cma/cma_monitor.h b/eBPF_Supermarket/mem_watcher/page_fault/cma/cma_monitor.h new file mode 100644 index 000000000..b9b3eddb1 --- /dev/null +++ b/eBPF_Supermarket/mem_watcher/page_fault/cma/cma_monitor.h @@ -0,0 +1,10 @@ +// 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/mem_watcher/page_fault/ion/Makefile b/eBPF_Supermarket/mem_watcher/page_fault/ion/Makefile new file mode 100644 index 000000000..e9e8ef726 --- /dev/null +++ b/eBPF_Supermarket/mem_watcher/page_fault/ion/Makefile @@ -0,0 +1,114 @@ +# 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_OBJ := $(abspath $(OUTPUT)/libbpf.a) +BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) +BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool + +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 := ../libbpf-bootstrap/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)) +CFLAGS := -g -Wall +ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) + +APPS = ion_monitor + +# 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: $(APPS) + +.PHONY: clean +clean: + $(call msg,CLEAN) + $(Q)rm -rf $(OUTPUT) $(APPS) + +$(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 + +# Build BPF code +$(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) \ + $(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 +$(OUTPUT)/%.skel.h: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL) + $(call msg,GEN-SKEL,$@) + $(Q)$(BPFTOOL) gen skeleton $< > $@ + +# 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 $(filter %.c,$^) -o $@ + +# Build application binary +$(APPS): %: $(OUTPUT)/%.o $(LIBBPF_OBJ) | $(OUTPUT) + $(call msg,BINARY,$@) + $(Q)$(CC) $(CFLAGS) $^ $(ALL_LDFLAGS) -lelf -lz -o $@ + +# delete failed targets +.DELETE_ON_ERROR: + +# keep intermediate (.skel.h, .bpf.o, etc) targets +.SECONDARY: + diff --git a/eBPF_Supermarket/mem_watcher/page_fault/ion/ion_monitor.bpf.c b/eBPF_Supermarket/mem_watcher/page_fault/ion/ion_monitor.bpf.c new file mode 100644 index 000000000..e91936618 --- /dev/null +++ b/eBPF_Supermarket/mem_watcher/page_fault/ion/ion_monitor.bpf.c @@ -0,0 +1,63 @@ +#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/mem_watcher/page_fault/ion/ion_monitor.c b/eBPF_Supermarket/mem_watcher/page_fault/ion/ion_monitor.c new file mode 100644 index 000000000..d22a7e004 --- /dev/null +++ b/eBPF_Supermarket/mem_watcher/page_fault/ion/ion_monitor.c @@ -0,0 +1,57 @@ +#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/mem_watcher/page_fault/ion/ion_monitor.h b/eBPF_Supermarket/mem_watcher/page_fault/ion/ion_monitor.h new file mode 100644 index 000000000..e6712713e --- /dev/null +++ b/eBPF_Supermarket/mem_watcher/page_fault/ion/ion_monitor.h @@ -0,0 +1,10 @@ +// 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 */