diff --git a/eBPF_Supermarket/Stack_Analyser/include/bpf/MemoryStackCollector.h b/eBPF_Supermarket/Stack_Analyser/include/bpf/MemoryStackCollector.h index 70dcf9715..9ac120bc2 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/bpf/MemoryStackCollector.h +++ b/eBPF_Supermarket/Stack_Analyser/include/bpf/MemoryStackCollector.h @@ -39,7 +39,7 @@ class MemoryStackCollector : public StackCollector skel->progs.prog_name, \ pid, \ object, \ - 1, \ + 0, \ &uprobe_opts); \ } while (false) #else diff --git a/eBPF_Supermarket/Stack_Analyser/include/uprobe_helpers.h b/eBPF_Supermarket/Stack_Analyser/include/uprobe_helpers.h new file mode 100644 index 000000000..134b557ad --- /dev/null +++ b/eBPF_Supermarket/Stack_Analyser/include/uprobe_helpers.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +/* Copyright (c) 2021 Google LLC. */ +#ifndef __UPROBE_HELPERS_H +#define __UPROBE_HELPERS_H + +#include +#include +#include + +int get_pid_binary_path(pid_t pid, char *path, size_t path_sz); +int get_pid_lib_path(pid_t pid, const char *lib, char *path, size_t path_sz); +int resolve_binary_path(const char *binary, pid_t pid, char *path, size_t path_sz); +off_t get_elf_func_offset(const char *path, const char *func); +Elf *open_elf(const char *path, int *fd_close); +Elf *open_elf_by_fd(int fd); +void close_elf(Elf *e, int fd_close); + +#endif /* __UPROBE_HELPERS_H */ \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/src/bpf/IOStackCollector.cpp b/eBPF_Supermarket/Stack_Analyser/src/bpf/IOStackCollector.cpp index 5160adf42..b12d38ee7 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/bpf/IOStackCollector.cpp +++ b/eBPF_Supermarket/Stack_Analyser/src/bpf/IOStackCollector.cpp @@ -34,7 +34,7 @@ IOStackCollector::IOStackCollector() int IOStackCollector::load(void) { - StackProgLoadOpen(); + StackProgLoadOpen(skel->bss->apid=pid;); return 0; } diff --git a/eBPF_Supermarket/Stack_Analyser/src/bpf/ProbeStackCollector.cpp b/eBPF_Supermarket/Stack_Analyser/src/bpf/ProbeStackCollector.cpp index 3c69c5af1..a61189f68 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/bpf/ProbeStackCollector.cpp +++ b/eBPF_Supermarket/Stack_Analyser/src/bpf/ProbeStackCollector.cpp @@ -1,7 +1,9 @@ #include "bpf/ProbeStackCollector.h" +#include "uprobe_helpers.h" -double StackCountStackCollector::count_value(void *data) { - return *(uint32_t*)data; +double StackCountStackCollector::count_value(void *data) +{ + return *(uint32_t *)data; } StackCountStackCollector::StackCountStackCollector() @@ -13,23 +15,74 @@ StackCountStackCollector::StackCountStackCollector() }; }; +void splitString(std::string symbol, const char split, std::vector &res) +{ + if (symbol == "") + return; + std::string strs = symbol + split; + size_t pos = strs.find(split); + while (pos != strs.npos) + { + std::string temp = strs.substr(0, pos); + res.push_back(temp); + strs = strs.substr(pos + 1, strs.size()); + pos = strs.find(split); + } +} void StackCountStackCollector::setScale(std::string probe) { this->probe = probe; - scale.Type = (probe + scale.Type).c_str(); + auto type = new std::string(probe+scale.Type); + scale.Type = type->c_str(); }; int StackCountStackCollector::load(void) { - StackProgLoadOpen(); + StackProgLoadOpen(skel->bss->apid = pid;); return 0; }; int StackCountStackCollector::attach(void) { - skel->links.handle = - bpf_program__attach_kprobe(skel->progs.handle, false, probe.c_str()); - CHECK_ERR(!skel->links.handle, "Fail to attach kprobe"); + std::vector strList; + splitString(probe, ':', strList); + + if (strList.size() == 1 || (strList.size() == 3 && strList[0] == "p" && strList[1] == "")) + { + // probe a kernel function + std::string func = probe; + if (strList.size() == 3 && strList[0] == "p" && strList[1] == "") + func = strList[2]; + skel->links.handle = + bpf_program__attach_kprobe(skel->progs.handle, false, func.c_str()); + CHECK_ERR(!skel->links.handle, "Fail to attach kprobe111"); + return 0; + } + else if (strList.size() == 3 && strList[0] == "t") + { + // probe a kernel tracepoint + skel->links.handle_tp = + bpf_program__attach_tracepoint(skel->progs.handle_tp, strList[1].c_str(), strList[2].c_str()); + CHECK_ERR(!skel->links.handle_tp, "Fail to attach tracepoint"); + return 0; + } + else if (strList.size() == 2 || (strList.size() == 3 && strList[0] == "p" && strList[1] != "")) + { + // probe a user-space function in the library 'lib' + + return 0; + } + else if (strList.size() == 3 && strList[0] == "u") + { + // probe a USDT tracepoint + return 0; + } + else + { + printf("Type must be 'p', 't', or 'u' or too any args"); + } + + scale.Type = (probe + scale.Type).c_str(); return 0; }; diff --git a/eBPF_Supermarket/Stack_Analyser/src/bpf/stack_count.bpf.c b/eBPF_Supermarket/Stack_Analyser/src/bpf/stack_count.bpf.c index 17afce185..a5304610a 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/bpf/stack_count.bpf.c +++ b/eBPF_Supermarket/Stack_Analyser/src/bpf/stack_count.bpf.c @@ -24,6 +24,7 @@ #include "../include/sa_ebpf.h" #include "../include/task.h" + DeclareCommonMaps(u32); DeclareCommonVar(); diff --git a/eBPF_Supermarket/Stack_Analyser/src/uprobe_helpers.cc b/eBPF_Supermarket/Stack_Analyser/src/uprobe_helpers.cc new file mode 100644 index 000000000..56dfcdb28 --- /dev/null +++ b/eBPF_Supermarket/Stack_Analyser/src/uprobe_helpers.cc @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +/* Copyright (c) 2021 Google LLC. */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define warn(...) fprintf(stderr, __VA_ARGS__) + +/* + * Returns 0 on success; -1 on failure. On sucess, returns via `path` the full + * path to the program for pid. + */ +int get_pid_binary_path(pid_t pid, char *path, size_t path_sz) +{ + ssize_t ret; + char proc_pid_exe[32]; + + if (snprintf(proc_pid_exe, sizeof(proc_pid_exe), "/proc/%d/exe", pid) + >= sizeof(proc_pid_exe)) { + warn("snprintf /proc/PID/exe failed"); + return -1; + } + ret = readlink(proc_pid_exe, path, path_sz); + if (ret < 0) { + warn("No such pid %d\n", pid); + return -1; + } + if (ret >= path_sz) { + warn("readlink truncation"); + return -1; + } + path[ret] = '\0'; + + return 0; +} + +/* + * Returns 0 on success; -1 on failure. On success, returns via `path` the full + * path to a library matching the name `lib` that is loaded into pid's address + * space. + */ +int get_pid_lib_path(pid_t pid, const char *lib, char *path, size_t path_sz) +{ + FILE *maps; + char *p; + char proc_pid_maps[32]; + char line_buf[1024]; + char path_buf[1024]; + + if (snprintf(proc_pid_maps, sizeof(proc_pid_maps), "/proc/%d/maps", pid) + >= sizeof(proc_pid_maps)) { + warn("snprintf /proc/PID/maps failed"); + return -1; + } + maps = fopen(proc_pid_maps, "r"); + if (!maps) { + warn("No such pid %d\n", pid); + return -1; + } + while (fgets(line_buf, sizeof(line_buf), maps)) { + if (sscanf(line_buf, "%*x-%*x %*s %*x %*s %*u %s", path_buf) != 1) + continue; + /* e.g. /usr/lib/x86_64-linux-gnu/libc-2.31.so */ + p = strrchr(path_buf, '/'); + if (!p) + continue; + if (strncmp(p, "/lib", 4)) + continue; + p += 4; + if (strncmp(lib, p, strlen(lib))) + continue; + p += strlen(lib); + /* libraries can have - or . after the name */ + if (*p != '.' && *p != '-') + continue; + if (strnlen(path_buf, 1024) >= path_sz) { + warn("path size too small\n"); + return -1; + } + strcpy(path, path_buf); + fclose(maps); + return 0; + } + + warn("Cannot find library %s\n", lib); + fclose(maps); + return -1; +} + +/* + * Returns 0 on success; -1 on failure. On success, returns via `path` the full + * path to the program. + */ +static int which_program(const char *prog, char *path, size_t path_sz) +{ + FILE *which; + char cmd[100]; + + if (snprintf(cmd, sizeof(cmd), "which %s", prog) >= sizeof(cmd)) { + warn("snprintf which prog failed"); + return -1; + } + which = popen(cmd, "r"); + if (!which) { + warn("which failed"); + return -1; + } + if (!fgets(path, path_sz, which)) { + warn("fgets which failed"); + pclose(which); + return -1; + } + /* which has a \n at the end of the string */ + path[strlen(path) - 1] = '\0'; + pclose(which); + return 0; +} + +/* + * Returns 0 on success; -1 on failure. On success, returns via `path` the full + * path to the binary for the given pid. + * 1) pid == x, binary == "" : returns the path to x's program + * 2) pid == x, binary == "foo" : returns the path to libfoo linked in x + * 3) pid == 0, binary == "" : failure: need a pid or a binary + * 4) pid == 0, binary == "bar" : returns the path to `which bar` + * + * For case 4), ideally we'd like to search for libbar too, but we don't support + * that yet. + */ +int resolve_binary_path(const char *binary, pid_t pid, char *path, size_t path_sz) +{ + if (!strcmp(binary, "")) { + if (!pid) { + warn("Uprobes need a pid or a binary\n"); + return -1; + } + return get_pid_binary_path(pid, path, path_sz); + } + if (pid) + return get_pid_lib_path(pid, binary, path, path_sz); + + if (which_program(binary, path, path_sz)) { + /* + * If the user is tracing a program by name, we can find it. + * But we can't find a library by name yet. We'd need to parse + * ld.so.cache or something similar. + */ + warn("Can't find %s (Need a PID if this is a library)\n", binary); + return -1; + } + return 0; +} + +/* + * Opens an elf at `path` of kind ELF_K_ELF. Returns NULL on failure. On + * success, close with close_elf(e, fd_close). + */ +Elf *open_elf(const char *path, int *fd_close) +{ + int fd; + Elf *e; + + if (elf_version(EV_CURRENT) == EV_NONE) { + warn("elf init failed\n"); + return NULL; + } + fd = open(path, O_RDONLY); + if (fd < 0) { + warn("Could not open %s\n", path); + return NULL; + } + e = elf_begin(fd, ELF_C_READ, NULL); + if (!e) { + warn("elf_begin failed: %s\n", elf_errmsg(-1)); + close(fd); + return NULL; + } + if (elf_kind(e) != ELF_K_ELF) { + warn("elf kind %d is not ELF_K_ELF\n", elf_kind(e)); + elf_end(e); + close(fd); + return NULL; + } + *fd_close = fd; + return e; +} + +Elf *open_elf_by_fd(int fd) +{ + Elf *e; + + if (elf_version(EV_CURRENT) == EV_NONE) { + warn("elf init failed\n"); + return NULL; + } + e = elf_begin(fd, ELF_C_READ, NULL); + if (!e) { + warn("elf_begin failed: %s\n", elf_errmsg(-1)); + close(fd); + return NULL; + } + if (elf_kind(e) != ELF_K_ELF) { + warn("elf kind %d is not ELF_K_ELF\n", elf_kind(e)); + elf_end(e); + close(fd); + return NULL; + } + return e; +} + +void close_elf(Elf *e, int fd_close) +{ + elf_end(e); + close(fd_close); +} + +/* Returns the offset of a function in the elf file `path`, or -1 on failure. */ +off_t get_elf_func_offset(const char *path, const char *func) +{ + off_t ret = -1; + int i, fd = -1; + Elf *e; + Elf_Scn *scn; + Elf_Data *data; + GElf_Ehdr ehdr; + GElf_Shdr shdr[1]; + GElf_Phdr phdr; + GElf_Sym sym[1]; + size_t shstrndx, nhdrs; + char *n; + + e = open_elf(path, &fd); + + if (!gelf_getehdr(e, &ehdr)) + goto out; + + if (elf_getshdrstrndx(e, &shstrndx) != 0) + goto out; + + scn = NULL; + while ((scn = elf_nextscn(e, scn))) { + if (!gelf_getshdr(scn, shdr)) + continue; + if (!(shdr->sh_type == SHT_SYMTAB || shdr->sh_type == SHT_DYNSYM)) + continue; + data = NULL; + while ((data = elf_getdata(scn, data))) { + for (i = 0; gelf_getsym(data, i, sym); i++) { + n = elf_strptr(e, shdr->sh_link, sym->st_name); + if (!n) + continue; + if (GELF_ST_TYPE(sym->st_info) != STT_FUNC) + continue; + if (!strcmp(n, func)) { + ret = sym->st_value; + goto check; + } + } + } + } + +check: + if (ehdr.e_type == ET_EXEC || ehdr.e_type == ET_DYN) { + if (elf_getphdrnum(e, &nhdrs) != 0) { + ret = -1; + goto out; + } + for (i = 0; i < (int)nhdrs; i++) { + if (!gelf_getphdr(e, i, &phdr)) + continue; + if (phdr.p_type != PT_LOAD || !(phdr.p_flags & PF_X)) + continue; + if (phdr.p_vaddr <= ret && ret < (phdr.p_vaddr + phdr.p_memsz)) { + ret = ret - phdr.p_vaddr + phdr.p_offset; + goto out; + } + } + ret = -1; + } +out: + close_elf(e, fd); + return ret; +} \ No newline at end of file