diff --git a/.gitmodules b/.gitmodules index 117e1aa04..8557f3dde 100644 --- a/.gitmodules +++ b/.gitmodules @@ -51,4 +51,4 @@ url = https://github.com/libbpf/libbpf-bootstrap.git [submodule "eBPF_Supermarket/Stack_Analyser/speedscope"] path = eBPF_Supermarket/Stack_Analyser/speedscope - url = https://github.com/jlfwong/speedscope + url = https://github.com/jlfwong/speedscope \ No newline at end of file diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/developing/proc_image.c b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/developing/proc_image.c new file mode 100644 index 000000000..6852427a8 --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/developing/proc_image.c @@ -0,0 +1,285 @@ +// Copyright 2023 The LMP Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// author: zhangziheng0525@163.com +// +// user-mode code for the process image + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "include/proc_image.h" +#include "resource_image.skel.h" + +#define __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe) \ + do \ + { \ + LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts, \ + .retprobe = is_retprobe, \ + .func_name = #sym_name); \ + skel->links.prog_name = bpf_program__attach_uprobe_opts( \ + skel->progs.prog_name, \ + env.pid, \ + object, \ + 0, \ + &uprobe_opts); \ + } while (false) + +#define __CHECK_PROGRAM(skel, prog_name) \ + do \ + { \ + if (!skel->links.prog_name) \ + { \ + fprintf(stderr, "[%s] no program attached for" #prog_name "\n", strerror(errno)); \ + return -errno; \ + } \ + } while (false) + +#define __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, is_retprobe) \ + do \ + { \ + __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe); \ + __CHECK_PROGRAM(skel, prog_name); \ + } while (false) + +#define ATTACH_UPROBE(skel, sym_name, prog_name) __ATTACH_UPROBE(skel, sym_name, prog_name, false) +#define ATTACH_URETPROBE(skel, sym_name, prog_name) __ATTACH_UPROBE(skel, sym_name, prog_name, true) + +#define ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name) __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, false) +#define ATTACH_URETPROBE_CHECKED(skel, sym_name, prog_name) __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, true) + +#define CHECK_ERR(cond, info) \ + if (cond) \ + { \ + fprintf(stderr, "[%s]" info "\n", strerror(errno)); \ + return -1; \ + } + +#define warn(...) fprintf(stderr, __VA_ARGS__) + +#define RESOURCE_IMAGE 1 + +static int prev_image = 0; +static volatile bool exiting = false; +static const char object[] = "/usr/lib/x86_64-linux-gnu/libc.so.6"; +static struct env { + int pid; + int cpu_id; + int time; + bool enable_resource; +} env = { + .pid = -1, + .cpu_id = -1, + .time = 0, + .enable_resource = false, +}; + +const char argp_program_doc[] ="Trace process to get process image.\n"; + +static const struct argp_option opts[] = { + { "pid", 'p', "PID", 0, "Process ID to trace" }, + { "cpuid", 'c', "CPUID", 0, "Set For Tracing per-CPU Process(other processes don't need to set this parameter)" }, + { "time", 't', "TIME-SEC", 0, "Max Running Time(0 for infinite)" }, + { "resource", 'r', NULL, 0, "Collects resource usage information about a process" }, + {}, +}; + +static error_t parse_arg(int key, char *arg, struct argp_state *state) +{ + long pid; + long cpu_id; + switch (key) { + case 'p': + errno = 0; + pid = strtol(arg, NULL, 10); + if (errno || pid < 0) { + warn("Invalid PID: %s\n", arg); + // 调用argp_usage函数,用于打印用法信息并退出程序 + argp_usage(state); + } + env.pid = pid; + break; + case 'c': + cpu_id = strtol(arg, NULL, 10); + if(cpu_id < 0){ + warn("Invalid CPUID: %s\n", arg); + argp_usage(state); + } + env.cpu_id = cpu_id; + break; + case 't': + env.time = strtol(arg, NULL, 10); + if(env.time) alarm(env.time); + break; + case 'r': + env.enable_resource = true; + break; + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +static void sig_handler(int signo) +{ + exiting = 1; +} + +static int print_resource(struct bpf_map *map) +{ + struct proc_id lookup_key = {-1}, next_key; + int err, fd = bpf_map__fd(map); + struct total_rsc event; + float pcpu,pmem; + double read_rate,write_rate; + unsigned long memtotal = sysconf(_SC_PHYS_PAGES); + time_t now = time(NULL); + struct tm *localTime = localtime(&now); + int hour = localTime->tm_hour; + int min = localTime->tm_min; + int sec = localTime->tm_sec; + + if(prev_image != RESOURCE_IMAGE){ + printf("RESOURCE----------------------------------------------------\n"); + printf("%-8s %-6s %-6s %-6s %-12s %-12s\n","TIME","PID","CPU(%)","MEM(%)","read(kb/s)","write(kb/s)\n"); + } + + while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { + err = bpf_map_lookup_elem(fd, &next_key, &event); + if (err < 0) { + fprintf(stderr, "failed to lookup infos: %d\n", err); + return -1; + } + + pcpu = (1.0*event.time)/1000000000; + pmem = (1.0*event.memused)/memtotal; + read_rate = (1.0*event.readchar)/1024/((1.0*event.time)/1000000000); // kb/s + write_rate = (1.0*event.writechar)/1024/((1.0*event.time)/1000000000); // kb/s + + printf("%02d:%02d:%02d %-6d %-6.4f %-6.4f %-12.2lf %-12.2lf\n", + hour,min,sec,event.pid,pcpu,pmem,read_rate,write_rate); + + lookup_key = next_key; + } + + prev_image = RESOURCE_IMAGE; + + lookup_key.pid = -1; + lookup_key.cpu_id = -1; + while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { + err = bpf_map_delete_elem(fd, &next_key); + if (err < 0) { + fprintf(stderr, "failed to cleanup infos: %d\n", err); + return -1; + } + lookup_key = next_key; + } + + return 0; +} + +static void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt) +{ + fprintf(stderr, "Lost %llu events on CPU #%d!\n", lost_cnt, cpu); +} + +static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) +{ + return vfprintf(stderr, format, args); +} + +int main(int argc, char **argv) +{ + struct resource_image_bpf *resource_skel; + int err; + static const struct argp argp = { + .options = opts, + .parser = parse_arg, + .doc = argp_program_doc, + }; + + err = argp_parse(&argp, argc, argv, 0, NULL, NULL); + if (err) + return err; + + libbpf_set_strict_mode(LIBBPF_STRICT_ALL); + /* 设置libbpf错误和调试信息回调 */ + libbpf_set_print(libbpf_print_fn); + + /* 更干净地处理Ctrl-C + SIGINT:由Interrupt Key产生,通常是CTRL+C或者DELETE。发送给所有ForeGround Group的进程 + SIGTERM:请求中止进程,kill命令发送 + */ + signal(SIGINT, sig_handler); + signal(SIGTERM, sig_handler); + signal(SIGALRM,sig_handler); + + if(env.enable_resource){ + resource_skel = resource_image_bpf__open(); + if(!resource_skel) { + fprintf(stderr, "Failed to open BPF resource skeleton\n"); + return 1; + } + + resource_skel->rodata->target_pid = env.pid; + resource_skel->rodata->target_cpu_id = env.cpu_id; + + err = resource_image_bpf__load(resource_skel); + if (err) { + fprintf(stderr, "Failed to load and verify BPF resource skeleton\n"); + goto cleanup; + } + + err = resource_bpf__attach(resource_skel); + if (err) { + fprintf(stderr, "Failed to attach BPF resource skeleton\n"); + goto cleanup; + } + } + + /* 处理事件 */ + /* 后期修改方案二:可以用锁进行实现; + 后期修改方案一:每秒输出的事件改用定时器实现,先将数据写到用户空间缓冲区(字符串数组)中, + 然后循环访问缓冲区中是否有数据,避免事件的数据交叉输出;*/ + while (!exiting) { + sleep(1); + + if(env.enable_resource){ + err = print_resource(resource_skel->maps.total); + /* Ctrl-C will cause -EINTR */ + if (err == -EINTR) { + err = 0; + break; + } + if (err < 0) { + break; + } + } + } + +/* 卸载BPF程序 */ +cleanup: + resource_image_bpf__destroy(resource_skel); + + return err < 0 ? -err : 0; +} \ No newline at end of file diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/developing/proc_image.h b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/developing/proc_image.h new file mode 100644 index 000000000..23c556ac5 --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/developing/proc_image.h @@ -0,0 +1,42 @@ +// Copyright 2023 The LMP Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// author: zhangziheng0525@163.com +// +// eBPF map for the process image + +#ifndef __PROC_IMAGE_H +#define __PROC_IMAGE_H + +// resource_image +struct proc_id{ + long unsigned int pid; + long unsigned int cpu_id; +}; + +struct start_rsc{ + long long unsigned int time; + long long unsigned int readchar; + long long unsigned int writechar; +}; + +struct total_rsc{ + long unsigned int pid; + long long unsigned int time; + long unsigned int memused; + long long unsigned int readchar; + long long unsigned int writechar; +}; + +#endif /* __PROCESS_H */ \ No newline at end of file diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/developing/resource_image.bpf.c b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/developing/resource_image.bpf.c new file mode 100644 index 000000000..ebf85a1aa --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/developing/resource_image.bpf.c @@ -0,0 +1,124 @@ +// Copyright 2023 The LMP Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// author: zhangziheng0525@163.com +// +// eBPF kernel-mode code that collects process resource usage + +#include +#include +#include +#include +#include "include/proc_image.h" + +const volatile pid_t target_pid = -1; +const volatile int target_cpu_id = -1; + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 7000); + __type(key, struct proc_id); + __type(value, struct start_rsc); +} start SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 7000); + __type(key, struct proc_id); + __type(value, struct total_rsc); +} total SEC(".maps"); + +SEC("kprobe/finish_task_switch.isra.0") +int kprobe__finish_task_switch(struct pt_regs *ctx) +{ + struct task_struct *prev = (struct task_struct *)PT_REGS_PARM1(ctx); + pid_t prev_pid = BPF_CORE_READ(prev,pid); + int prev_cpu = bpf_get_smp_processor_id(); + struct task_struct *next = (struct task_struct *)bpf_get_current_task(); + pid_t next_pid = BPF_CORE_READ(next,pid); + int next_cpu = prev_cpu; + + if(target_pid==-1 || (target_pid!=0 && prev_pid==target_pid) || + (target_pid==0 && prev_pid==target_pid && prev_cpu==target_cpu_id)){ + struct proc_id prev_pd = {0}; + prev_pd.pid = prev_pid; + prev_pd.cpu_id = prev_cpu; + + if(bpf_map_lookup_elem(&start,&prev_pd) != NULL){ + struct start_rsc *prev_start = bpf_map_lookup_elem(&start,&prev_pd); + if (prev_start == NULL) { + return 0; + } + + if(bpf_map_lookup_elem(&total,&prev_pd) == NULL){ + struct total_rsc prev_total = {0}; + struct mm_rss_stat rss = {}; + long long *c; + long unsigned int memused; + + rss = BPF_CORE_READ(prev, mm, rss_stat); + c = (long long *)(rss.count); + memused = *c + *(c + 1) + *(c + 3); + + prev_total.pid = prev_pd.pid; + prev_total.time = bpf_ktime_get_ns() - prev_start->time; + prev_total.memused = memused; + prev_total.readchar = BPF_CORE_READ(prev,ioac.rchar) - prev_start->readchar; + prev_total.writechar = BPF_CORE_READ(prev,ioac.wchar) - prev_start->writechar; + + bpf_map_update_elem(&total,&prev_pd, &prev_total, BPF_ANY); + }else{ + struct total_rsc *prev_total = bpf_map_lookup_elem(&total,&prev_pd); + if (prev_total == NULL) { + return 0; + } + + struct mm_rss_stat rss = {}; + long long *c; + long unsigned int memused; + + rss = BPF_CORE_READ(prev, mm, rss_stat); + c = (long long *)(rss.count); + memused = *c + *(c + 1) + *(c + 3); + + //prev_total->pid = prev_pd.pid; + prev_total->time += bpf_ktime_get_ns() - prev_start->time; + prev_total->memused = memused; + prev_total->readchar += BPF_CORE_READ(prev,ioac.rchar) - prev_start->readchar; + prev_total->writechar += BPF_CORE_READ(prev,ioac.wchar) - prev_start->writechar; + + bpf_map_update_elem(&total,&prev_pd, &(*prev_total), BPF_ANY); + } + } + } + + if(target_pid==-1 || (target_pid!=0 && next_pid==target_pid) || + (target_pid==0 && next_pid==target_pid && next_cpu==target_cpu_id)){ + struct proc_id next_pd = {0}; + struct start_rsc next_start={0}; + + next_pd.pid = next_pid; + next_pd.cpu_id = next_cpu; + + next_start.time = bpf_ktime_get_ns(); + next_start.readchar = BPF_CORE_READ(next,ioac.rchar); + next_start.writechar = BPF_CORE_READ(next,ioac.wchar); + + bpf_map_update_elem(&start,&next_pd, &next_start, BPF_ANY); + } + + return 0; +} \ No newline at end of file