From 70a9bf612bd4d6113c29d86c37e722c7f7e3397f Mon Sep 17 00:00:00 2001 From: shuaibo_nan <117452805+nanshuaibo@users.noreply.github.com> Date: Fri, 1 Dec 2023 15:57:24 +0800 Subject: [PATCH] =?UTF-8?q?kvm=5Fwatcher:=E5=90=88=E5=B9=B6=E4=B8=BA?= =?UTF-8?q?=E4=B8=BB=E7=A8=8B=E5=BA=8F=EF=BC=8C=E5=AF=B9vcpu=E6=8C=82?= =?UTF-8?q?=E8=B5=B7=E8=BD=AE=E8=AF=A2=E6=97=B6=E9=97=B4=E5=8F=98=E5=8C=96?= =?UTF-8?q?=E7=9A=84=E7=9B=91=E6=8E=A7=20(#609)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Upload 2023.6.29 会议纪要.md * Add files via upload * 上传2023.8.4会议纪要 * Update 2023.8.4 会议纪要.md * Add files via upload * add kvm_watcher dir * modify directory name * add kvm_watcher.yml * update yml * update .yml * update kvm_exit.c * update .yml * load kvm mod * 添加文件头信息 * 优化结果输出 * Update kvm_exits.bpf.c 优化代码格式 * 调整Action文件 * Update kvm_exits.c * Update kvm_exits.c * update kvm_exit.c * update kvm_exits.c * 修改变量名称 * modify dir name * add vcpu dir * Update kvm_watcher.yml Update kvm_watcher.yml * Update kvm_watcher.yml * Update kvm_watcher.yml * update action * update vcpu * 修改Makefile,删除vmlinux.h头文件 * 1 * 合并为主程序 * 更改缩进 * 调整代码缩进 * 调整代码缩进 * 调整代码缩进 * 调整代码缩进 * 调整代码缩进 * 修改action * 修改action * vcpu_halt_poll_ns * update kvm_watcher.c * update * 修改action * 删除可执行文件 * 删除可执行文件 * 调整代码缩进 * 完善代码逻辑 * 完善代码逻辑 * 调整代码缩进 --- .github/workflows/kvm_watcher.yml | 15 +- .../kvm_watcher/{vcpu => }/Makefile | 11 +- .../kvm_watcher/include/kvm_exits.h | 125 +++++ .../kvm_watcher/include/kvm_vcpu.h | 87 ++++ .../kvm_watcher/include/kvm_watcher.h | 62 +++ .../kvm_watcher/src/kvm_watcher.bpf.c | 73 +++ .../kvm_watcher/src/kvm_watcher.c | 430 ++++++++++++++++++ .../kvm_watcher/vcpu/kvm_vcpu.bpf.c | 65 --- eBPF_Supermarket/kvm_watcher/vcpu/kvm_vcpu.c | 172 ------- eBPF_Supermarket/kvm_watcher/vcpu/kvm_vcpu.h | 14 - .../kvm_watcher/vm_exits/Makefile | 23 - .../kvm_watcher/vm_exits/kvm_exits.bpf.c | 133 ------ .../kvm_watcher/vm_exits/kvm_exits.c | 364 --------------- .../kvm_watcher/vm_exits/kvm_exits.h | 28 -- 14 files changed, 790 insertions(+), 812 deletions(-) rename eBPF_Supermarket/kvm_watcher/{vcpu => }/Makefile (63%) create mode 100644 eBPF_Supermarket/kvm_watcher/include/kvm_exits.h create mode 100644 eBPF_Supermarket/kvm_watcher/include/kvm_vcpu.h create mode 100644 eBPF_Supermarket/kvm_watcher/include/kvm_watcher.h create mode 100644 eBPF_Supermarket/kvm_watcher/src/kvm_watcher.bpf.c create mode 100644 eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c delete mode 100644 eBPF_Supermarket/kvm_watcher/vcpu/kvm_vcpu.bpf.c delete mode 100644 eBPF_Supermarket/kvm_watcher/vcpu/kvm_vcpu.c delete mode 100644 eBPF_Supermarket/kvm_watcher/vcpu/kvm_vcpu.h delete mode 100644 eBPF_Supermarket/kvm_watcher/vm_exits/Makefile delete mode 100644 eBPF_Supermarket/kvm_watcher/vm_exits/kvm_exits.bpf.c delete mode 100644 eBPF_Supermarket/kvm_watcher/vm_exits/kvm_exits.c delete mode 100644 eBPF_Supermarket/kvm_watcher/vm_exits/kvm_exits.h diff --git a/.github/workflows/kvm_watcher.yml b/.github/workflows/kvm_watcher.yml index 4e01b9867..861db6f2b 100644 --- a/.github/workflows/kvm_watcher.yml +++ b/.github/workflows/kvm_watcher.yml @@ -27,13 +27,12 @@ jobs: sudo apt install linux-tools-$(uname -r) sudo apt install linux-cloud-tools-$(uname -r) sudo modprobe kvm - - name: Run kvm_exits + - name: Run kvm_watcher run: | - cd eBPF_Supermarket/kvm_watcher/vm_exits + cd eBPF_Supermarket/kvm_watcher/ make - sudo ./kvm_exits -t 5 - - name: Run kvm_vcpu - run: | - cd eBPF_Supermarket/kvm_watcher/vcpu - make - sudo ./kvm_vcpu -w -t 5 + sudo ./kvm_watcher -w -t 5 + sudo ./kvm_watcher -e -t 5 -s + sudo ./kvm_watcher -n -t 5 + make clean + diff --git a/eBPF_Supermarket/kvm_watcher/vcpu/Makefile b/eBPF_Supermarket/kvm_watcher/Makefile similarity index 63% rename from eBPF_Supermarket/kvm_watcher/vcpu/Makefile rename to eBPF_Supermarket/kvm_watcher/Makefile index ff4e29b1e..8a10f9b44 100644 --- a/eBPF_Supermarket/kvm_watcher/vcpu/Makefile +++ b/eBPF_Supermarket/kvm_watcher/Makefile @@ -5,19 +5,20 @@ ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \ | sed 's/mips.*/mips/' \ | sed 's/riscv64/riscv/' \ | sed 's/loongarch64/loongarch/') -APP = kvm_vcpu +APP = src/kvm_watcher all: $(APP) .PHONY: $(APP) $(APP): -ifeq ($(wildcard ./vmlinux.h),) - bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h +ifeq ($(wildcard ./include/vmlinux.h),) + bpftool btf dump file /sys/kernel/btf/vmlinux format c > ./include/vmlinux.h endif clang -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) -I/usr/include/x86_64-linux-gnu -I. -c $@.bpf.c -o $@.bpf.o bpftool gen skeleton $@.bpf.o > $@.skel.h clang -g -O2 -Wall -I . -c $@.c -o $@.o - clang -Wall -O2 -g $@.o -static -lbpf -lelf -lz -o $@ + clang -Wall -O2 -g $@.o -static -lbpf -lelf -lz -o $(notdir $@) clean: - rm -f *.o *.skel.h $(APP) vmlinux.h + cd src && rm -f *.o *.skel.h + rm -f $(notdir $(APP)) include/vmlinux.h \ No newline at end of file diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_exits.h b/eBPF_Supermarket/kvm_watcher/include/kvm_exits.h new file mode 100644 index 000000000..a676c7386 --- /dev/null +++ b/eBPF_Supermarket/kvm_watcher/include/kvm_exits.h @@ -0,0 +1,125 @@ +// 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: nanshuaibo811@163.com +// +// Kernel space BPF program used for counting VM exit reason. + +#ifndef __KVM_EXITS_H +#define __KVM_EXITS_H + +#include "kvm_watcher.h" +#include "vmlinux.h" +#include +#include +#include + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 8192); + __type(key, pid_t); + __type(value, struct reason_info); +} times SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 8192); + __type(key, u32); + __type(value, u32); +} counts SEC(".maps"); + +struct exit{ + u64 pad; + unsigned int exit_reason; + unsigned long guest_rip; + u32 isa; + u64 info1; + u64 info2; + u32 intr_info; + u32 error_code; + unsigned int vcpu_id; +}; + +int total=0; + +static int trace_kvm_exit(struct exit *ctx, pid_t vm_pid) +{ + u64 id,ts; + id = bpf_get_current_pid_tgid(); + pid_t tid = (u32)id; + pid_t pid = id >> 32; + if (vm_pid < 0 || pid == vm_pid){ + ts = bpf_ktime_get_ns(); + u32 reason; + reason=(u32)ctx->exit_reason; + struct reason_info reas={}; + reas.reason=reason; + reas.time=ts; + u32 *count; + count=bpf_map_lookup_elem(&counts, &reason); + if(count){ + (*count)++; + reas.count=*count; + }else{ + u32 new_count = 1; + reas.count=new_count; + bpf_map_update_elem(&counts, &reason, &new_count, BPF_ANY); + } + bpf_map_update_elem(×, &tid, &reas, BPF_ANY); + } + return 0; +} + +static int trace_kvm_entry(void *rb) +{ + struct reason_info *reas; + pid_t pid, tid; + u64 id, ts, *start_ts, duration_ns=0; + id = bpf_get_current_pid_tgid(); + pid = id >> 32; + tid = (u32)id; + reas = bpf_map_lookup_elem(×, &tid); + if(reas){ + u32 reason; + struct exit_event *e; + int count=0; + duration_ns=bpf_ktime_get_ns() - reas->time; + bpf_map_delete_elem(×, &tid); + reason=reas->reason; + count=reas->count; + e = bpf_ringbuf_reserve(rb, sizeof(*e), 0); + if (!e){ + return 0; + } + e->reason_number=reason; + e->process.pid=pid; + e->duration_ns = duration_ns; + bpf_get_current_comm(&e->process.comm, sizeof(e->process.comm)); + e->process.tid=tid; + e->total=++total; + if (count) { + e->count = count; + } else { + e->count = 1; + } + bpf_ringbuf_submit(e, 0); + return 0; + } + else{ + return 0; + } +} +#endif /* __KVM_EXITS_H */ + + diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_vcpu.h b/eBPF_Supermarket/kvm_watcher/include/kvm_vcpu.h new file mode 100644 index 000000000..5f18cd7f4 --- /dev/null +++ b/eBPF_Supermarket/kvm_watcher/include/kvm_vcpu.h @@ -0,0 +1,87 @@ +// 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: nanshuaibo811@163.com +// +// Kernel space BPF program used for monitoring data for vCPU HLT. + +#ifndef __KVM_VCPU_H +#define __KVM_VCPU_H + +#include "kvm_watcher.h" +#include "vmlinux.h" +#include +#include +#include + +struct vcpu_wakeup{ + u64 pad; + __u64 ns; + bool waited; + bool vaild; +}; + +struct halt_poll_ns{ + u64 pad; + bool grow; + unsigned int vcpu_id; + unsigned int new; + unsigned int old; +}; + +static int trace_kvm_vcpu_wakeup(struct vcpu_wakeup *ctx,void *rb,pid_t vm_pid) +{ + unsigned pid = bpf_get_current_pid_tgid() >> 32; + if (vm_pid < 0 || pid == vm_pid){ + u32 tid = bpf_get_current_pid_tgid(); + struct vcpu_wakeup_event *e; + e = bpf_ringbuf_reserve(rb, sizeof(*e), 0); + if (!e){ + return 0; + } + u64 hlt_time = bpf_ktime_get_ns(); + e->waited = ctx->waited; + e->process.pid = pid; + e->process.tid = tid; + e->dur_hlt_ns = ctx->ns; + e->hlt_time = hlt_time; + bpf_get_current_comm(&e->process.comm, sizeof(e->process.comm)); + bpf_ringbuf_submit(e, 0); + } + return 0; +} + +static int trace_kvm_halt_poll_ns(struct halt_poll_ns *ctx,void *rb,pid_t vm_pid) +{ + unsigned pid = bpf_get_current_pid_tgid() >> 32; + if (vm_pid < 0 || pid == vm_pid){ + u32 tid = bpf_get_current_pid_tgid(); + struct halt_poll_ns_event *e; + e = bpf_ringbuf_reserve(rb, sizeof(*e), 0); + if (!e) + return 0; + u64 time = bpf_ktime_get_ns(); + e->process.pid = pid; + e->process.tid = tid; + e->time = time; + e->grow=ctx->grow; + e->old=ctx->old; + e->new=ctx->new; + bpf_get_current_comm(&e->process.comm, sizeof(e->process.comm)); + bpf_ringbuf_submit(e, 0); + } + return 0; +} + +#endif /* __KVM_VCPU_H */ diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_watcher.h b/eBPF_Supermarket/kvm_watcher/include/kvm_watcher.h new file mode 100644 index 000000000..135d43ae9 --- /dev/null +++ b/eBPF_Supermarket/kvm_watcher/include/kvm_watcher.h @@ -0,0 +1,62 @@ +// 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: nanshuaibo811@163.com +// +// BPF program used for monitoring KVM event. + +#ifndef __KVM_WATCHER_H +#define __KVM_WATCHER_H + +#define TASK_COMM_LEN 16 + +struct process{ + unsigned pid; + unsigned tid; + char comm[TASK_COMM_LEN]; +}; +struct vcpu_wakeup_event { + struct process process; + unsigned long long dur_hlt_ns; + bool waited; + unsigned long long hlt_time; +}; + +struct exit_event { + struct process process; + unsigned reason_number; + unsigned long long duration_ns; + int count; + int total; +}; + +struct ExitReason { + int number; + const char* name; +}; + +struct reason_info { + unsigned long long time; + unsigned long reason; + int count; +}; + +struct halt_poll_ns_event { + struct process process; + bool grow; + unsigned int new; + unsigned int old; + unsigned long long time; +}; +#endif /* __KVM_WATCHER_H */ \ 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 new file mode 100644 index 000000000..37cf689af --- /dev/null +++ b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.bpf.c @@ -0,0 +1,73 @@ +// 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: nanshuaibo811@163.com +// +// Kernel space BPF program used for monitoring data for KVM event. + +#include "../include/vmlinux.h" +#include +#include +#include +#include "../include/kvm_watcher.h" +#include "../include/kvm_vcpu.h" +#include "../include/kvm_exits.h" + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; + +const volatile pid_t vm_pid = -1; +const volatile bool execute_vcpu_wakeup=false; +const volatile bool execute_exit=false; +const volatile bool execute_halt_poll_ns=false; + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} rb SEC(".maps"); + +SEC("tp/kvm/kvm_vcpu_wakeup") +int tp_vcpu_wakeup(struct vcpu_wakeup *ctx) +{ + if(execute_vcpu_wakeup){ + trace_kvm_vcpu_wakeup(ctx,&rb,vm_pid); + } + return 0; +} + +SEC("tp/kvm/kvm_halt_poll_ns") +int tp_kvm_halt_poll_ns(struct halt_poll_ns *ctx) +{ + if(execute_halt_poll_ns){ + trace_kvm_halt_poll_ns(ctx,&rb,vm_pid); + } + return 0; +} + +SEC("tp/kvm/kvm_exit") +int tp_exit(struct exit *ctx) +{ + if(execute_exit){ + trace_kvm_exit(ctx,vm_pid); + } + return 0; +} + +SEC("tp/kvm/kvm_entry") +int tp_entry(struct exit *ctx) +{ + if(execute_exit){ + trace_kvm_entry(&rb); + } + return 0; +} diff --git a/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c new file mode 100644 index 000000000..f2e371843 --- /dev/null +++ b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c @@ -0,0 +1,430 @@ +// 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: nanshuaibo811@163.com +// +// User space BPF program used for monitoring KVM event. + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kvm_watcher.skel.h" +#include "../include/kvm_watcher.h" + + +struct ExitReason exitReasons[] = { + {0, "EXCEPTION_NMI"}, + {1, "EXTERNAL_INTERRUPT"}, + {2, "TRIPLE_FAULT"}, + {3, "INIT_SIGNAL"}, + {4, "SIPI_SIGNAL"}, + {7, "INTERRUPT_WINDOW"}, + {8, "NMI_WINDOW"}, + {9, "TASK_SWITCH"}, + {10, "CPUID"}, + {12, "HLT"}, + {13, "INVD"}, + {14, "INVLPG"}, + {15, "RDPMC"}, + {16, "RDTSC"}, + {18, "VMCALL"}, + {19, "VMCLEAR"}, + {20, "VMLAUNCH"}, + {21, "VMPTRLD"}, + {22, "VMPTRST"}, + {23, "VMREAD"}, + {24, "VMRESUME"}, + {25, "VMWRITE"}, + {26, "VMOFF"}, + {27, "VMON"}, + {28, "CR_ACCESS"}, + {29, "DR_ACCESS"}, + {30, "IO_INSTRUCTION"}, + {31, "MSR_READ"}, + {32, "MSR_WRITE"}, + {33, "INVALID_STATE"}, + {34, "MSR_LOAD_FAIL"}, + {36, "MWAIT_INSTRUCTION"}, + {37, "MONITOR_TRAP_FLAG"}, + {39, "MONITOR_INSTRUCTION"}, + {40, "PAUSE_INSTRUCTION"}, + {41, "MCE_DURING_VMENTRY"}, + {43, "TPR_BELOW_THRESHOLD"}, + {44, "APIC_ACCESS"}, + {45, "EOI_INDUCED"}, + {46, "GDTR_IDTR"}, + {47, "LDTR_TR"}, + {48, "EPT_VIOLATION"}, + {49, "EPT_MISCONFIG"}, + {50, "INVEPT"}, + {51, "RDTSCP"}, + {52, "PREEMPTION_TIMER"}, + {53, "INVVPID"}, + {54, "WBINVD"}, + {55, "XSETBV"}, + {56, "APIC_WRITE"}, + {57, "RDRAND"}, + {58, "INVPCID"}, + {59, "VMFUNC"}, + {60, "ENCLS"}, + {61, "RDSEED"}, + {62, "PML_FULL"}, + {63, "XSAVES"}, + {64, "XRSTORS"}, + {67, "UMWAIT"}, + {68, "TPAUSE"}, + {74, "BUS_LOCK"}, + {75, "NOTIFY"} +}; + +const char* getExitReasonName(int number) { + for (int i = 0; i < sizeof(exitReasons) / sizeof(exitReasons[0]); i++) { + if (exitReasons[i].number == number) { + return exitReasons[i].name; + } + } + return "Unknown"; // 如果找不到对应的退出原因,返回一个默认值 +} + +typedef struct { + int exit_reason; + char info[256]; // 替换成适当的大小 + unsigned long long total_dur; + unsigned long long avg_dur; +} ExitInfo; + +// 链表节点 +typedef struct Node { + ExitInfo data; + struct Node* next; +} Node; + +Node* exitInfoBuffer = NULL; + +void addExitInfo(Node** head, int exit_reason, const char* info,unsigned long long dur,int count) { + Node* newNode = (Node*)malloc(sizeof(Node)); + newNode->data.exit_reason = exit_reason; + strncpy(newNode->data.info, info, sizeof(newNode->data.info)); + newNode->next = NULL; + newNode->data.total_dur = dur; + newNode->data.avg_dur = dur / count; + + // 检查是否已经存在相同 exit reason 的信息 + Node* current = *head; + Node* previous = NULL; + while (current != NULL) { + if (current->data.exit_reason == exit_reason) { + // 更新已存在的信息 + strncpy(current->data.info, info, sizeof(current->data.info)); + current->data.total_dur=dur+current->data.total_dur; + current->data.avg_dur=current->data.total_dur/count; + free(newNode); // 释放新节点,因为信息已经更新 + return; + } + previous = current; + current = current->next; + } + // 没有找到相同的 exit reason,将新节点添加到链表 + if (previous != NULL) { + previous->next = newNode; + } else { + *head = newNode; + } +} + +// 查找指定退出原因的信息 +const char* findExitInfo(Node* head, int exit_reason) { + Node* current = head; + while (current != NULL) { + if (current->data.exit_reason == exit_reason) { + return current->data.info; + } + current = current->next; + } + return NULL; +} + +// 释放链表 +void freeExitInfoList(Node* head) { + while (head != NULL) { + Node* temp = head; + head = head->next; + free(temp); + } +} + +void printExitInfo(Node* head) { + Node* current = head; + printf("%-23s %-10s %-15s %-8s %-13s \n", "EXIT_REASON", "COMM","PID/TID","COUNT","AVG_DURATION(ns)"); + while (current != NULL) { + printf("%-2d/%-20s %-33s %-13llu \n", current->data.exit_reason,getExitReasonName(current->data.exit_reason), current->data.info,current->data.avg_dur); + current = current->next; + } +} + +int doesVmProcessExist(pid_t pid) { + char proc_name[256]; + snprintf(proc_name, sizeof(proc_name), "/proc/%d/cmdline", pid); + FILE *file = fopen(proc_name, "r"); + if (file) { + size_t size; + size = fread(proc_name, 1, sizeof(proc_name), file); + if (size > 0) { + if (proc_name[size - 1] == '\n') { + proc_name[size - 1] = '\0'; // Remove newline character + } + if (strstr(proc_name, "qemu-system-x86_64") != NULL) { + fclose(file); + return 1; // VmProcess name contains the target string + } else { + fclose(file); + fprintf(stderr, "Process exist!but is not vmprocess: %d\n", pid); + return 0; // VmProcess name does not contain the target string + } + } + fclose(file); + } + fprintf(stderr, "Process name does not find: %d\n", pid); + return 0; // VmProcess with the given PID not found +} + +static struct env { + bool execute_vcpu_wakeup; + bool execute_exit; + bool ShowStats; + bool execute_halt_poll_ns; + int monitoring_time; + pid_t vm_pid; +} env={ + .execute_vcpu_wakeup=false, + .execute_exit=false, + .ShowStats=false, + .execute_halt_poll_ns=false, + .monitoring_time=0, + .vm_pid=-1, +}; + +const char *argp_program_version = "kvm_watcher 1.0"; +const char *argp_program_bug_address = ""; +const char argp_program_doc[] = "BPF program used for monitoring KVM event\n"; +int option_selected = 0; // 功能标志变量,确保激活子功能 + +static const struct argp_option opts[] = { + { "vcpu_wakeup", 'w', NULL, 0, "Monitoring the wakeup of vcpu." }, + { "vm_exit)", 'e', NULL, 0, "Monitoring the event of vm exit." }, + { "vcpu_halt_poll_ns)", 'n', NULL, 0, "Monitoring the variation in vCPU polling time." }, + { "stat",'s',NULL,0,"Display statistical data.(The -e option must be specified.)" }, + { "vm_pid", 'p', "PID", 0, "Specify the virtual machine pid to monitor." }, + { "monitoring_time", 't', "SEC", 0, "Time for monitoring event." }, + {}, +}; + +static error_t parse_arg(int key, char *arg, struct argp_state *state) +{ + switch (key) { + case 'w': + if (option_selected == 0) { + env.execute_vcpu_wakeup = true; + option_selected = 1; + } else { + fprintf(stderr, "Use either the -w ,-p or -e option.\n"); + argp_usage(state); + } + break; + case 'e': + if (option_selected == 0) { + env.execute_exit = true; + option_selected = 1; + } else { + fprintf(stderr, "Use either the -w ,p or -e option.\n"); + argp_usage(state); + } + break; + case 'n': + if (option_selected == 0) { + env.execute_halt_poll_ns = true; + option_selected = 1; + } else { + fprintf(stderr, "Use either the -w ,-n or -e option.\n"); + argp_usage(state); + } + break; + case 's': + if(env.execute_exit){ + env.ShowStats=true; + }else{ + fprintf(stderr, "The -e option must be specified.\n"); + argp_usage(state); + } + break; + case 't': + env.monitoring_time = strtol(arg, NULL, 10); + if (env.monitoring_time <= 0) { + fprintf(stderr, "Invalid duration: %s\n", arg); + argp_usage(state); + } else if (!option_selected) { + fprintf(stderr, "No monitoring options activated!\n"); + argp_usage(state); + }else{ + alarm(env.monitoring_time); + } + break; + case 'p': + env.vm_pid=strtol(arg, NULL, 10); + if(env.vm_pid<=0 || doesVmProcessExist(env.vm_pid)==0 ){ + fprintf(stderr, "Invalid vm_pid: %s\n", arg); + argp_usage(state); + } + break; + case ARGP_KEY_ARG: + argp_usage(state); + 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) +{ + 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) +{ + if(env.execute_vcpu_wakeup){ + const struct vcpu_wakeup_event *e = data; + printf("%-18llu %-20llu %-15s %-6d/%-8d %-10s\n", e->hlt_time,e->dur_hlt_ns, e->process.comm, e->process.pid,e->process.tid,e->waited ? "wait" : "poll"); + } + if(env.execute_exit){ + char info_buffer[256]; + const struct exit_event *e = data; + printf("%-2d/%-20s %-10s %-6u/%-8u %-8d %-13llu \n", e->reason_number,getExitReasonName(e->reason_number), e->process.comm, e->process.pid,e->process.tid,e->count,e->duration_ns); + if(env.ShowStats){ + snprintf(info_buffer, sizeof(info_buffer), "%-10s %-6u/%-8u %-8d", e->process.comm, e->process.pid,e->process.tid,e->count); + addExitInfo(&exitInfoBuffer,e->reason_number,info_buffer,e->duration_ns,e->count); + } + } + if(env.execute_halt_poll_ns){ + const struct halt_poll_ns_event *e = data; + printf("%-18llu %-15s %-6d/%-8d %-10s %-7d --> %d \n", e->time, e->process.comm, e->process.pid,e->process.tid,e->grow ? "grow" : "shrink",e->old,e->new); + } + return 0; +} + +int main(int argc, char **argv) +{ + struct ring_buffer *rb = NULL; + struct kvm_watcher_bpf *skel; + int err; + + /* Parse command line arguments */ + err = argp_parse(&argp, argc, argv, 0, NULL, NULL); + if (err) + return 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); + signal(SIGALRM, sig_handler); + /* Open BPF application */ + skel = kvm_watcher_bpf__open(); + if (!skel) { + fprintf(stderr, "Failed to open BPF skeleton\n"); + return 1; + } + + /* Parameterize BPF code with parameter */ + skel->rodata->vm_pid= env.vm_pid; + skel->rodata->execute_vcpu_wakeup = env.execute_vcpu_wakeup; + skel->rodata->execute_exit = env.execute_exit; + skel->rodata->execute_halt_poll_ns=env.execute_halt_poll_ns; + + /* Load & verify BPF programs */ + err = kvm_watcher_bpf__load(skel); + if (err) { + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); + goto cleanup; + } + + /* Attach tracepoint handler */ + err = kvm_watcher_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 */ + if(env.execute_vcpu_wakeup){ + printf("%-18s %-20s %-15s %-15s %-10s\n", "HLT_TIME(ns)", "DURATIONS_TIME(ns)","VCPUID/COMM","PID/TID","WAIT/POLL"); + } + if(env.execute_exit){ + printf("%-23s %-10s %-15s %-8s %-13s \n", "EXIT_REASON", "COMM","PID/TID","COUNT","DURATION(ns)"); + } + if(env.execute_halt_poll_ns){ + printf("%-18s %-15s %-15s %-10s %-11s %-10s\n", "TIME(ns)", "VCPUID/COMM","PID/TID","TYPE","OLD(ns)","NEW(ns)"); + } + while (!exiting) { + err = ring_buffer__poll(rb, 10 /* 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; + } + } + if(env.ShowStats){ + printf("\n---------------------------------------------------------------------------\n"); + printExitInfo(exitInfoBuffer); + freeExitInfoList(exitInfoBuffer); + } +cleanup: + ring_buffer__free(rb); + kvm_watcher_bpf__destroy(skel); + return -err; +} diff --git a/eBPF_Supermarket/kvm_watcher/vcpu/kvm_vcpu.bpf.c b/eBPF_Supermarket/kvm_watcher/vcpu/kvm_vcpu.bpf.c deleted file mode 100644 index f3d9b21bc..000000000 --- a/eBPF_Supermarket/kvm_watcher/vcpu/kvm_vcpu.bpf.c +++ /dev/null @@ -1,65 +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: nanshuaibo811@163.com -// -// Kernel space BPF program used for monitoring data for vCPU HLT. - -#include "vmlinux.h" -#include "kvm_vcpu.h" -#include -#include -#include -char LICENSE[] SEC("license") = "Dual BSD/GPL"; - -struct { - __uint(type, BPF_MAP_TYPE_RINGBUF); - __uint(max_entries, 256 * 1024); -} rb SEC(".maps"); - -const volatile bool execute_vcpu_wakeup=false; - -struct vcpu_wakeup{ - u64 pad; - __u64 ns; - bool waited; - bool vaild; -}; - -int trace_kvm_vcpu_wakeup(struct vcpu_wakeup *ctx){ - unsigned pid = bpf_get_current_pid_tgid() >> 32; - u32 tid = bpf_get_current_pid_tgid(); - struct event *e; - e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); - u64 hlt_time = bpf_ktime_get_ns(); - if (!e) - return 0; - e->waited = ctx->waited; - e->pid = pid; - e->tid = tid; - e->dur_hlt_ns = ctx->ns; - e->hlt_time = hlt_time; - bpf_get_current_comm(&e->comm, sizeof(e->comm)); - bpf_ringbuf_submit(e, 0); - return 0; -} - -SEC("tp/kvm/kvm_vcpu_wakeup") -int tp_vcpu_wakeup(struct vcpu_wakeup *ctx) -{ - if(execute_vcpu_wakeup){ - trace_kvm_vcpu_wakeup(ctx); - } - return 0; -} diff --git a/eBPF_Supermarket/kvm_watcher/vcpu/kvm_vcpu.c b/eBPF_Supermarket/kvm_watcher/vcpu/kvm_vcpu.c deleted file mode 100644 index dadee9753..000000000 --- a/eBPF_Supermarket/kvm_watcher/vcpu/kvm_vcpu.c +++ /dev/null @@ -1,172 +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: nanshuaibo811@163.com -// -// User space BPF program used for monitoring data for vCPU. - - -#include -#include -#include -#include -#include -#include -#include -#include "kvm_vcpu.skel.h" -#include "kvm_vcpu.h" - -static struct env { - bool execute_vcpu_wakeup; - - int monitoring_time; -} env={ - .execute_vcpu_wakeup=false, - - .monitoring_time=0, -}; - -const char *argp_program_version = "kvm_vcpu 1.0"; -const char *argp_program_bug_address = ""; -const char argp_program_doc[] = "BPF program used for monitoring data for vCPU\n"; - -static const struct argp_option opts[] = { - { "monitoring_time", 't', "SEC", 0, "Time for monitoring wakeup of vcpu" }, - { "vcpu_wakeup", 'w', NULL, 0, "Monitoring the wakeup of vcpu" }, - {}, -}; - -static error_t parse_arg(int key, char *arg, struct argp_state *state) -{ - switch (key) { - case 'w': - env.execute_vcpu_wakeup=true; - break; - case 't': - env.monitoring_time = strtol(arg, NULL, 10); - if (env.monitoring_time <= 0) { - fprintf(stderr, "Invalid duration: %s\n", arg); - argp_usage(state); - } else if (!env.execute_vcpu_wakeup) { - fprintf(stderr, "No monitoring options activated!\n"); - argp_usage(state); - }else{ - alarm(env.monitoring_time); - } - break; - case ARGP_KEY_ARG: - argp_usage(state); - 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) -{ - 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("%-18llu %-20llu %-15s %-6d/%-8d %-10s\n", e->hlt_time,e->dur_hlt_ns, e->comm, e->pid,e->tid,e->waited ? "wait" : "poll"); - - return 0; -} - -int main(int argc, char **argv) -{ - struct ring_buffer *rb = NULL; - struct kvm_vcpu_bpf *skel; - int err; - - /* Parse command line arguments */ - err = argp_parse(&argp, argc, argv, 0, NULL, NULL); - if (err) - return 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); - signal(SIGALRM, sig_handler); - /* Open BPF application */ - skel = kvm_vcpu_bpf__open(); - if (!skel) { - fprintf(stderr, "Failed to open BPF skeleton\n"); - return 1; - } - /* Parameterize BPF code with parameter */ - skel->rodata->execute_vcpu_wakeup = env.execute_vcpu_wakeup; - - /* Load & verify BPF programs */ - err = kvm_vcpu_bpf__load(skel); - if (err) { - fprintf(stderr, "Failed to load and verify BPF skeleton\n"); - goto cleanup; - } - - /* Attach tracepoint handler */ - err = kvm_vcpu_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 */ - if(env.execute_vcpu_wakeup){ - printf("%-18s %-20s %-15s %-15s %-10s\n", "HLT_TIME(ns)", "DURATIONS_TIME(ns)","VCPUID/COMM","PID/TID","WAIT/POLL"); - } - while (!exiting) { - err = ring_buffer__poll(rb, 10 /* 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: - ring_buffer__free(rb); - kvm_vcpu_bpf__destroy(skel); - return -err; -} diff --git a/eBPF_Supermarket/kvm_watcher/vcpu/kvm_vcpu.h b/eBPF_Supermarket/kvm_watcher/vcpu/kvm_vcpu.h deleted file mode 100644 index 32e21d48a..000000000 --- a/eBPF_Supermarket/kvm_watcher/vcpu/kvm_vcpu.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef __KVM_VCPU_H -#define __KVM_VCPU_H - -#define TASK_COMM_LEN 16 -struct event { - char comm[TASK_COMM_LEN]; - unsigned long long dur_hlt_ns; - unsigned pid; - unsigned tid; - bool waited; - unsigned long long hlt_time; - //unsigned int hlt_poll_ns; -}; -#endif /* __KVM_VCPU_H */ diff --git a/eBPF_Supermarket/kvm_watcher/vm_exits/Makefile b/eBPF_Supermarket/kvm_watcher/vm_exits/Makefile deleted file mode 100644 index 85aa677fd..000000000 --- a/eBPF_Supermarket/kvm_watcher/vm_exits/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -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 = kvm_exits - -all: $(APP) - -.PHONY: $(APP) -$(APP): -ifeq ($(wildcard ./vmlinux.h),) - bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h -endif - clang -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) -I/usr/include/x86_64-linux-gnu -I. -c $@.bpf.c -o $@.bpf.o - bpftool gen skeleton $@.bpf.o > $@.skel.h - clang -g -O2 -Wall -I . -c $@.c -o $@.o - clang -Wall -O2 -g $@.o -static -lbpf -lelf -lz -o $@ - -clean: - rm -f *.o *.skel.h $(APP) vmlinux.h diff --git a/eBPF_Supermarket/kvm_watcher/vm_exits/kvm_exits.bpf.c b/eBPF_Supermarket/kvm_watcher/vm_exits/kvm_exits.bpf.c deleted file mode 100644 index a22cc6adb..000000000 --- a/eBPF_Supermarket/kvm_watcher/vm_exits/kvm_exits.bpf.c +++ /dev/null @@ -1,133 +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: nanshuaibo811@163.com -// -// Kernel space BPF program used for counting VM exit reason. - -#include "vmlinux.h" -#include "kvm_exits.h" -#include -#include -#include -char LICENSE[] SEC("license") = "Dual BSD/GPL"; - -struct { - __uint(type, BPF_MAP_TYPE_HASH); - __uint(max_entries, 8192); - __type(key, pid_t); - __type(value, struct reason_info); -} times SEC(".maps"); - -struct { - __uint(type, BPF_MAP_TYPE_HASH); - __uint(max_entries, 8192); - __type(key, u32); - __type(value, u32); -} counts SEC(".maps"); - -struct { - __uint(type, BPF_MAP_TYPE_RINGBUF); - __uint(max_entries, 256 * 1024); -} rb SEC(".maps"); - -struct exit{ - u64 pad; - unsigned int exit_reason; - unsigned long guest_rip; - u32 isa; - u64 info1; - u64 info2; - u32 intr_info; - u32 error_code; - unsigned int vcpu_id; -}; - -int total=0; -const volatile pid_t vm_pid = 0; - - -SEC("tp/kvm/kvm_exit") -int handle_kvm_exit(struct exit *ctx) -{ - pid_t tid,pid; - u64 id,ts; - id = bpf_get_current_pid_tgid(); - tid = (u32)id; - pid = id >> 32; - if (vm_pid == 0 || pid == vm_pid){ - ts = bpf_ktime_get_ns(); - u32 reason; - reason=(u32)ctx->exit_reason; - struct reason_info reas={}; - reas.reason=reason; - reas.time=ts; - u32 *count; - count=bpf_map_lookup_elem(&counts, &reason); - if(count){ - (*count)++; - reas.count=*count; - }else{ - u32 new_count = 1; - reas.count=new_count; - bpf_map_update_elem(&counts, &reason, &new_count, BPF_ANY); - } - bpf_map_update_elem(×, &tid, &reas, BPF_ANY); - } - return 0; -} - -SEC("tp/kvm/kvm_entry") -int handle_kvm_entry() -{ - struct reason_info *reas; - pid_t pid, tid; - u64 id, ts, *start_ts, duration_ns=0; - id = bpf_get_current_pid_tgid(); - pid = id >> 32; - tid = (u32)id; - reas = bpf_map_lookup_elem(×, &tid); - if(reas){ - u32 reason; - struct event *e; - int count=0; - duration_ns=bpf_ktime_get_ns() - reas->time; - bpf_map_delete_elem(×, &tid); - reason=reas->reason; - count=reas->count; - e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); - if (!e){ - return 0; - } - e->reason_number=reason; - e->pid=pid; - e->duration_ns = duration_ns; - bpf_get_current_comm(&e->comm, sizeof(e->comm)); - e->tid=tid; - e->total=++total; - if (count) { - e->count = count; - } else { - e->count = 1; - } - bpf_ringbuf_submit(e, 0); - return 0; - } - else{ - return 0; - } -} - - - diff --git a/eBPF_Supermarket/kvm_watcher/vm_exits/kvm_exits.c b/eBPF_Supermarket/kvm_watcher/vm_exits/kvm_exits.c deleted file mode 100644 index cbf487e5f..000000000 --- a/eBPF_Supermarket/kvm_watcher/vm_exits/kvm_exits.c +++ /dev/null @@ -1,364 +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: nanshuaibo811@163.com -// -// User space BPF program used for outputting VM exit reason. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "kvm_exits.skel.h" -#include "kvm_exits.h" - - -// 存储所有exit reason的映射关系 -// from arch/x86/include/uapi/asm/vmx.h -struct ExitReason exitReasons[] = { - {0, "EXCEPTION_NMI"}, - {1, "EXTERNAL_INTERRUPT"}, - {2, "TRIPLE_FAULT"}, - {3, "INIT_SIGNAL"}, - {4, "SIPI_SIGNAL"}, - {7, "INTERRUPT_WINDOW"}, - {8, "NMI_WINDOW"}, - {9, "TASK_SWITCH"}, - {10, "CPUID"}, - {12, "HLT"}, - {13, "INVD"}, - {14, "INVLPG"}, - {15, "RDPMC"}, - {16, "RDTSC"}, - {18, "VMCALL"}, - {19, "VMCLEAR"}, - {20, "VMLAUNCH"}, - {21, "VMPTRLD"}, - {22, "VMPTRST"}, - {23, "VMREAD"}, - {24, "VMRESUME"}, - {25, "VMWRITE"}, - {26, "VMOFF"}, - {27, "VMON"}, - {28, "CR_ACCESS"}, - {29, "DR_ACCESS"}, - {30, "IO_INSTRUCTION"}, - {31, "MSR_READ"}, - {32, "MSR_WRITE"}, - {33, "INVALID_STATE"}, - {34, "MSR_LOAD_FAIL"}, - {36, "MWAIT_INSTRUCTION"}, - {37, "MONITOR_TRAP_FLAG"}, - {39, "MONITOR_INSTRUCTION"}, - {40, "PAUSE_INSTRUCTION"}, - {41, "MCE_DURING_VMENTRY"}, - {43, "TPR_BELOW_THRESHOLD"}, - {44, "APIC_ACCESS"}, - {45, "EOI_INDUCED"}, - {46, "GDTR_IDTR"}, - {47, "LDTR_TR"}, - {48, "EPT_VIOLATION"}, - {49, "EPT_MISCONFIG"}, - {50, "INVEPT"}, - {51, "RDTSCP"}, - {52, "PREEMPTION_TIMER"}, - {53, "INVVPID"}, - {54, "WBINVD"}, - {55, "XSETBV"}, - {56, "APIC_WRITE"}, - {57, "RDRAND"}, - {58, "INVPCID"}, - {59, "VMFUNC"}, - {60, "ENCLS"}, - {61, "RDSEED"}, - {62, "PML_FULL"}, - {63, "XSAVES"}, - {64, "XRSTORS"}, - {67, "UMWAIT"}, - {68, "TPAUSE"}, - {74, "BUS_LOCK"}, - {75, "NOTIFY"} -}; - - -const char* getExitReasonName(int number) { - for (int i = 0; i < sizeof(exitReasons) / sizeof(exitReasons[0]); i++) { - if (exitReasons[i].number == number) { - return exitReasons[i].name; - } - } - return "Unknown"; // 如果找不到对应的退出原因,返回一个默认值 -} - -typedef struct { - int exit_reason; - char info[256]; // 替换成适当的大小 - unsigned long long total_dur; - unsigned long long avg_dur; -} ExitInfo; - -// 链表节点 -typedef struct Node { - ExitInfo data; - struct Node* next; -} Node; - -Node* exitInfoBuffer = NULL; - -void addExitInfo(Node** head, int exit_reason, const char* info,unsigned long long dur,int count) { - Node* newNode = (Node*)malloc(sizeof(Node)); - newNode->data.exit_reason = exit_reason; - strncpy(newNode->data.info, info, sizeof(newNode->data.info)); - newNode->next = NULL; - newNode->data.total_dur = dur; - newNode->data.avg_dur = dur / count; - - // 检查是否已经存在相同 exit reason 的信息 - Node* current = *head; - Node* previous = NULL; - while (current != NULL) { - if (current->data.exit_reason == exit_reason) { - // 更新已存在的信息 - strncpy(current->data.info, info, sizeof(current->data.info)); - current->data.total_dur=dur+current->data.total_dur; - current->data.avg_dur=current->data.total_dur/count; - free(newNode); // 释放新节点,因为信息已经更新 - return; - } - previous = current; - current = current->next; - } - // 没有找到相同的 exit reason,将新节点添加到链表 - if (previous != NULL) { - previous->next = newNode; - } else { - *head = newNode; - } -} - -// 查找指定退出原因的信息 -const char* findExitInfo(Node* head, int exit_reason) { - Node* current = head; - while (current != NULL) { - if (current->data.exit_reason == exit_reason) { - return current->data.info; - } - current = current->next; - } - return NULL; -} - -// 释放链表 -void freeExitInfoList(Node* head) { - while (head != NULL) { - Node* temp = head; - head = head->next; - free(temp); - } -} - -void printExitInfo(Node* head) { - Node* current = head; - printf("%-23s %-10s %-14s %-8s %-13s \n", "EXIT_REASON", "COMM","PID/TID","COUNT","AVG_DURATION(ns)"/*,"PCT"*/); - while (current != NULL) { - printf("%-2d/%-20s %-32s %-13llu \n", current->data.exit_reason,getExitReasonName(current->data.exit_reason), current->data.info,current->data.avg_dur); - current = current->next; - } -} - -int doesVmProcessExist(pid_t pid) { - char proc_name[256]; - snprintf(proc_name, sizeof(proc_name), "/proc/%d/cmdline", pid); - FILE *file = fopen(proc_name, "r"); - if (file) { - size_t size; - size = fread(proc_name, 1, sizeof(proc_name), file); - if (size > 0) { - if (proc_name[size - 1] == '\n') { - proc_name[size - 1] = '\0'; // Remove newline character - } - if (strstr(proc_name, "qemu-system-x86_64") != NULL) { - fclose(file); - return 1; // VmProcess name contains the target string - } else { - fclose(file); - fprintf(stderr, "Process exist!but is not vmprocess: %d\n", pid); - return 0; // VmProcess name does not contain the target string - } - } - fclose(file); - } - fprintf(stderr, "Process name does not find: %d\n", pid); - return 0; // VmProcess with the given PID not found -} - -static struct env { - int monitoring_time; - pid_t vm_pid; - bool ShowStats; -} env={ - .monitoring_time=0, - .vm_pid=0, - .ShowStats=false, -}; - -const char *argp_program_version = "kvm_exits 1.0"; -const char *argp_program_bug_address = ""; -const char argp_program_doc[] = "BPF program used for outputting VM exit reason\n"; - -static const struct argp_option opts[] = { - { "monitoring_time", 't', "TIME-SEC", 0, "Set the time for profiling VM exit event reasons" }, - { "vm_pid", 'p', "PID", 0, "Specify the virtual machine pid to monitor." }, - { "stat",'s',NULL,0,"After monitoring is completed, display statistical data." }, - {}, -}; - -static error_t parse_arg(int key, char *arg, struct argp_state *state) -{ - switch (key) { - case 't': - errno = 0; - env.monitoring_time = strtol(arg, NULL, 10); - if (errno || env.monitoring_time <= 0) { - fprintf(stderr, "Invalid duration: %s\n", arg); - argp_usage(state); - } - else{ - alarm(env.monitoring_time); - } - break; - case 'p': - env.vm_pid=strtol(arg, NULL, 10); - if(env.vm_pid<=0 || doesVmProcessExist(env.vm_pid)==0 ){ - fprintf(stderr, "Invalid vm_pid: %s\n", arg); - argp_usage(state); - } - break; - case 's': - env.ShowStats=true; - break; - case ARGP_KEY_ARG: - argp_usage(state); - 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) -{ - 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; - char info_buffer[256]; - printf("%-2d/%-20s %-10s %-5u/%-8u %-8d %-13llu \n", e->reason_number,getExitReasonName(e->reason_number), e->comm, e->pid,e->tid,e->count,e->duration_ns/*,(double)e->count / e->total * 100.0*/); - snprintf(info_buffer, sizeof(info_buffer), "%-10s %-5u/%-8u %-8d", e->comm, e->pid,e->tid,e->count); - addExitInfo(&exitInfoBuffer,e->reason_number,info_buffer,e->duration_ns,e->count); - return 0; -} - -int main(int argc, char **argv) -{ - struct ring_buffer *rb = NULL; - struct kvm_exits_bpf *skel; - int err; - - /* Parse command line arguments */ - err = argp_parse(&argp, argc, argv, 0, NULL, NULL); - if (err) - return 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); - signal(SIGALRM, sig_handler); - /* Open BPF application */ - skel = kvm_exits_bpf__open(); - if (!skel) { - fprintf(stderr, "Failed to open BPF skeleton\n"); - return 1; - } - skel->rodata->vm_pid = env.vm_pid; - /* Load & verify BPF programs */ - err = kvm_exits_bpf__load(skel); - if (err) { - fprintf(stderr, "Failed to load and verify BPF skeleton\n"); - goto cleanup; - } - - /* Attach tracepoint handler */ - err = kvm_exits_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 */ - printf("%-23s %-10s %-14s %-8s %-13s \n", "EXIT_REASON", "COMM","PID/TID","COUNT","DURATION(ns)"/*,"PCT"*/); - while (!exiting) { - err = ring_buffer__poll(rb, 10 /* 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; - } - } - if(env.ShowStats){ - printf("\n---------------------------------------------------------------------------\n"); - printExitInfo(exitInfoBuffer); - freeExitInfoList(exitInfoBuffer); - } -cleanup: - /* Clean up */ - ring_buffer__free(rb); - kvm_exits_bpf__destroy(skel); - return -err; -} diff --git a/eBPF_Supermarket/kvm_watcher/vm_exits/kvm_exits.h b/eBPF_Supermarket/kvm_watcher/vm_exits/kvm_exits.h deleted file mode 100644 index 7bdac218e..000000000 --- a/eBPF_Supermarket/kvm_watcher/vm_exits/kvm_exits.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef __KVM_EXITS_H -#define __KVM_EXITS_H - -#define TASK_COMM_LEN 16 - -struct ExitReason { - int number; - const char* name; -}; - -struct event { - unsigned reason_number; - char comm[TASK_COMM_LEN]; - unsigned pid; - unsigned long long duration_ns; - unsigned tid; - int count; - int total; -}; - -struct reason_info { - unsigned long long time; - unsigned long reason; - int count; -}; - - -#endif /* __KVM_EXITS_H */