diff --git a/.github/workflows/user_function_tracer.yml b/.github/workflows/user_function_tracer.yml index 91dfff2af..f4a998425 100644 --- a/.github/workflows/user_function_tracer.yml +++ b/.github/workflows/user_function_tracer.yml @@ -34,4 +34,12 @@ jobs: cmake -B build -S . -G Ninja cmake --build build gcc test/sleep.c -o test/sleep - sudo build/utrace test/sleep + gcc test/mmap.c -o test/mmap + gcc test/strcpy.c -o test/strcpy + gcc test/fib.c -o test/fib + gcc test/thread.c -o test/thread + sudo build/utrace -c test/sleep + sudo build/utrace -c test/mmap + sudo build/utrace -c test/strcpy + sudo build/utrace -c test/fib + sudo build/utrace -c test/thread diff --git a/eBPF_Supermarket/User_Function_Tracer/.clang-format b/eBPF_Supermarket/User_Function_Tracer/.clang-format index d35c6c267..5af6aa680 100644 --- a/eBPF_Supermarket/User_Function_Tracer/.clang-format +++ b/eBPF_Supermarket/User_Function_Tracer/.clang-format @@ -5,8 +5,14 @@ ColumnLimit: 100 PointerAlignment: Right IncludeCategories: - Regex: '"vmlinux.h"' # let vmlinux.h come first - Priority: -1000 - - Regex: '"utrace.h"' - Priority: -1001 - - Regex: '"utrace.skel.h"' - Priority: -1000 + Priority: -1 + - Regex: '"utrace.*h"' + Priority: 0 + - Regex: '' + Priority: -1 + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + - Regex: '^(<|"(gtest|isl|json)/)' + Priority: 2 + - Regex: '.\*' + Priority: 1 \ No newline at end of file diff --git a/eBPF_Supermarket/User_Function_Tracer/README.md b/eBPF_Supermarket/User_Function_Tracer/README.md index ed4e6d40f..72e471624 100644 --- a/eBPF_Supermarket/User_Function_Tracer/README.md +++ b/eBPF_Supermarket/User_Function_Tracer/README.md @@ -11,23 +11,35 @@ sudo apt install -y clang cmake ninja-build libelf1 libelf-dev zlib1g-dev libbpf ### 编译运行 ```shell -mkdir -p vmlinux -bash tools/gen_vmlinux_h.sh > vmlinux/vmlinux.h -cmake -B build -S . -G Ninja -cmake --build build +$ mkdir -p vmlinux +$ bash tools/gen_vmlinux_h.sh > vmlinux/vmlinux.h +$ cmake -B build -S . -G Ninja +$ cmake --build build +$ build/utrace -h +Usage: build/utrace [$OPTIONS...] -build/utrace PROGRAM|PID +Options: + -c --command: the command to run the program to be traced. + -p --pid: the PID of the program to be traced. + -d --debug: enable debug mode. + --no-ASLR: disable Address Space Layout Randomization (ASLR). + -h --help: disaply this usage information. + +Examples: + sudo build/utrace -c "$PROGRAM $ARGS" + sudo build/utrace -p $PID ``` ### 特点 +- 观测用户态函数调用流程以及调用时延 +- 非侵入式,不依赖任何编译选项 +- 支持多线程程序、已经运行的程序(输入进程PID号) + 不同于`ftrace`,`eBPF-utrace`用于观测用户态函数。 -+ 不同于`uftrace`,`eBPF-utrace`基于eBPF,不依赖于任何编译技术,但是需要内核的支持,需要root权限,且可能有更高的开销。 -+ 不同于`perf`, `gprof`等性能分析工具,`eBPF-utrace`输出准确的函数调用时延,而不是基于perf_event()的采样方式。 ++ 不同于`uftrace`,`eBPF-utrace`基于eBPF,不依赖于任何编译技术,但是需要内核的支持,需要root权限。 ++ 不同于`perf`, `gprof`等性能分析工具,`eBPF-utrace`输出准确的函数调用时延,而不是基于perf_event的采样方式。 ### TODO -- [] short live process -- [] arg parser -- [] input pid -- [] more tests -- [] colorful log -- [] multithread +- IFUNC符号的观测 +- 嵌套的共享库观测 +- 简化C++符号的展示 +- 更多的测试 diff --git a/eBPF_Supermarket/User_Function_Tracer/src/demangle.c b/eBPF_Supermarket/User_Function_Tracer/src/demangle.c index 6d5694f0f..107ca5b48 100644 --- a/eBPF_Supermarket/User_Function_Tracer/src/demangle.c +++ b/eBPF_Supermarket/User_Function_Tracer/src/demangle.c @@ -18,24 +18,36 @@ #include "utrace.h" -#include "demangle.h" - #include #include +#include "demangle.h" + char *demangle(const char *mangled_name) { char *original_name; - long len = MAX_SYMBOL_LEN; + long len = MAX_MANGLED_LEN; + long name_len; int status; - /** 确保只还原重整过的符号(以"_Z"起始)*/ - if (mangled_name[0] != '_' || mangled_name[1] != 'Z') return strdup(mangled_name); - - __cxa_demangle(mangled_name, NULL, &len, &status); - if (status < 0) return strdup(mangled_name); - - original_name = malloc(len); - __cxa_demangle(mangled_name, original_name, &len, &status); - - return original_name; -} + if (strncmp(mangled_name, "_Z", 2) == 0) { + __cxa_demangle(mangled_name, NULL, &len, &status); + if (status < 0) { + return strdup(mangled_name); + } + original_name = malloc(len); + __cxa_demangle(mangled_name, original_name, &len, &status); + + name_len = strlen(original_name); + if (original_name[name_len - 1] == ')') { + int cont = 1; + while (cont) { + --name_len; + if (original_name[name_len] == '(') cont = 0; + original_name[name_len] = 0; + } + } + return original_name; + } + + return strdup(mangled_name); +} \ No newline at end of file diff --git a/eBPF_Supermarket/User_Function_Tracer/src/demangle.h b/eBPF_Supermarket/User_Function_Tracer/src/demangle.h index fc9b035c3..77d8f81ab 100644 --- a/eBPF_Supermarket/User_Function_Tracer/src/demangle.h +++ b/eBPF_Supermarket/User_Function_Tracer/src/demangle.h @@ -31,4 +31,4 @@ extern char *__cxa_demangle(const char *name, char *output, long *len, int *stat */ char *demangle(const char *mangled_name); -#endif // UTRACE_DEMANGLE_H +#endif // UTRACE_DEMANGLE_H \ No newline at end of file diff --git a/eBPF_Supermarket/User_Function_Tracer/src/gdb.c b/eBPF_Supermarket/User_Function_Tracer/src/gdb.c new file mode 100644 index 000000000..a256577f2 --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/src/gdb.c @@ -0,0 +1,55 @@ +// Copyright 2023 The LMP Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// author: jinyufeng2000@gmail.com +// +// 设置断点,为探针提供时间 + +#include "gdb.h" + +#include +#include +#include + +struct gdb* new_gdb(pid_t pid) { + struct gdb* gdb = (struct gdb*)malloc(sizeof(struct gdb)); + gdb->pid = pid; + return gdb; +} + +void enable_breakpoint(struct gdb* gdb, uint64_t addr) { + long data = ptrace(PTRACE_PEEKDATA, gdb->pid, addr, NULL); + gdb->inst = (uint8_t)data & 0xFF; + + uint64_t int3 = 0xCC; + ptrace(PTRACE_POKEDATA, gdb->pid, addr, (data & ~0xFF) | int3); +} + +void disable_breakpoint(struct gdb* gdb, uint64_t addr) { + long data = ptrace(PTRACE_PEEKDATA, gdb->pid, addr, NULL); + ptrace(PTRACE_POKEDATA, gdb->pid, (data & ~0xFF) | gdb->inst); +} + +long continue_execution(struct gdb* gdb) { return ptrace(PTRACE_CONT, gdb->pid, NULL, NULL); } + +long wait_for_signal(struct gdb* gdb) { + int wstatus; + int options = 0; + return waitpid(gdb->pid, &wstatus, options); +} + +void delete_gdb(struct gdb* gdb) { + ptrace(PTRACE_DETACH, gdb->pid, NULL, NULL); + free(gdb); +} \ No newline at end of file diff --git a/eBPF_Supermarket/User_Function_Tracer/src/gdb.h b/eBPF_Supermarket/User_Function_Tracer/src/gdb.h new file mode 100644 index 000000000..7c1de535a --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/src/gdb.h @@ -0,0 +1,74 @@ +// 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: jinyufeng2000@gmail.com +// +// 设置断点,为探针提供时间 + +#ifndef UTRACE_GDB_H +#define UTRACE_GDB_H + +#include +#include + +/* + * @brief 记录跨函数传递的信息 + */ +struct gdb { + pid_t pid; /**< gdb附加到的进程的编号 */ + uint8_t inst; /**< 被int3覆盖的单字节指令 */ +}; + +/* + * @brief 创建一个gdb结构体 + * @param[in] pid 进程号 + * @return 指向gdb结构体的指针 + * @note 从堆中申请空间 + */ +struct gdb* new_gdb(pid_t pid); + +/* + * @brief 设置一个断点 + * @param[in] gdb 指向一个gdb结构体 + * @param[in] addr 物理地址 + */ +void enable_breakpoint(struct gdb* gdb, uint64_t addr); + +/* + * @brief 取消一个断点 + * @param[in] gdb 指向一个gdb结构体 + * @param[in] addr 物理地址 + * @note 需要保证之前调用过enable_breakpoint(gdb, pid, addr) + */ +void disable_breakpoint(struct gdb* gdb, uint64_t addr); + +/* + * @brief 继续执行 + * @param[in] gdb 指向一个gdb结构体 + */ +long continue_execution(struct gdb* gdb); + +/* + * @brief 等待进程收到信号 + * @param[in] gdb 指向一个gdb结构体 + */ +long wait_for_signal(struct gdb* gdb); + +/* + * @brief 取消ptrace并释放gdb结构体的空间 + * @param[in] gdb 指向要释放的gdb结构体 + */ +void delete_gdb(struct gdb* gdb); + +#endif // UTRACE_GDB_H \ No newline at end of file diff --git a/eBPF_Supermarket/User_Function_Tracer/src/log.c b/eBPF_Supermarket/User_Function_Tracer/src/log.c index 35ec9e2a6..8d28babd7 100644 --- a/eBPF_Supermarket/User_Function_Tracer/src/log.c +++ b/eBPF_Supermarket/User_Function_Tracer/src/log.c @@ -18,21 +18,36 @@ #include "log.h" -#include +#include +#include +#include -void print_char(char c, int cnt) { +int debug; + +void log_color(const char* color) { + char* term = getenv("TERM"); + if (isatty(fileno(stderr)) && !(term && !strcmp(term, "dumb"))) { + LOG("%s", color); + } +} + +void log_char(char c, int cnt) { while (cnt > 0) { - printf("%c", c); + LOG("%c", c); --cnt; } } -void print_header() { printf("# DURATION TID FUNCTION\n"); } +void log_header() { LOG(" CPU | TID | DURATION | FUNCTION CALLS\n"); } -void print_tid(int tid) { printf("[%6d]", tid); } +void log_tid(int tid) { LOG("%6d", tid); } -void print_time_unit(size_t ns) { - static char *units[] = { +void log_cpuid(int cpuid) { LOG("%4d", cpuid); } + +void log_split() { LOG(" | "); } + +void log_time(size_t ns) { + static char* units[] = { "ns", "us", "ms", " s", " m", " h", }; static size_t limit[] = { @@ -48,5 +63,5 @@ void print_time_unit(size_t ns) { ++i; } - printf("%3zu.%03zu %s", t, t_mod, units[i]); -} + LOG("%4zu.%03zu %s", t, t_mod, units[i]); +} \ No newline at end of file diff --git a/eBPF_Supermarket/User_Function_Tracer/src/log.h b/eBPF_Supermarket/User_Function_Tracer/src/log.h index 4eab7d57b..fec5ec1c0 100644 --- a/eBPF_Supermarket/User_Function_Tracer/src/log.h +++ b/eBPF_Supermarket/User_Function_Tracer/src/log.h @@ -20,31 +20,50 @@ #define UTRACE_LOG_H #include +#include /** - * @brief 输出cnt个字符c + * @brief 设置输出的颜色 + * @param[in] color 颜色控制符 + */ +void log_color(const char* color); + +/** + * @brief 输出连续cnt个字符c * @param[in] c 字符 * @param[in] cnt 个数 */ -void print_char(char c, int cnt); +void log_char(char c, int cnt); /** * @brief 输出头部 */ -void print_header(); +void log_header(); /** * @brief 输出线程号 * @param[in] tid 线程号 */ -void print_tid(int tid); +void log_tid(int tid); + +/** + * @brief 输出CPU编号 + * @param[in] cpuid CPU编号 + */ +void log_cpuid(int cpuid); + +/** + * @brief 输出分隔符 + * @details 分隔符为" | " + */ +void log_split(); /** - * @brief 根据参数打印合适时间单位 + * @brief 输出时间及其单位 * @param[in] ns 时间(单位纳秒) - * @details 从[ns,us,ms,s,m,h]中选择合适的单位输出 + * @details 从[ns,us,ms,s,m,h]中选择合适的单位输出时间信息 */ -void print_time_unit(size_t ns); +void log_time(size_t ns); /** 控制是否显示调试信息,在utrace.c中定义 */ extern int debug; @@ -71,4 +90,17 @@ extern int debug; fprintf(stderr, fmt, ##__VA_ARGS__); \ } while (0) -#endif // UTRACE_LOG_H +/** + * @brief 输出到stderr + */ +#define LOG(fmt, ...) \ + do { \ + fprintf(stderr, fmt, ##__VA_ARGS__); \ + } while (0) + +#define TERM_RED "\033[0;31m" +#define TERM_GREEN "\033[0;32m" +#define TERM_GRAY "\033[0;90m" +#define TERM_NC "\033[0m" + +#endif // UTRACE_LOG_H \ No newline at end of file diff --git a/eBPF_Supermarket/User_Function_Tracer/src/symbol.c b/eBPF_Supermarket/User_Function_Tracer/src/symbol.c index 6574fb1cd..5f69975a1 100644 --- a/eBPF_Supermarket/User_Function_Tracer/src/symbol.c +++ b/eBPF_Supermarket/User_Function_Tracer/src/symbol.c @@ -18,15 +18,16 @@ #include "symbol.h" -#include "demangle.h" -#include "elf.h" -#include "log.h" #include #include #include #include #include +#include "demangle.h" +#include "elf.h" +#include "log.h" + static int addrsort(const void* lhs, const void* rhs) { const size_t addrl = ((const struct symbol*)(lhs))->addr; const size_t addrr = ((const struct symbol*)(rhs))->addr; @@ -87,7 +88,6 @@ struct symbol_arr* new_symbol_arr(char* libname, struct dyn_symbol_set* dyn_syms if (elf_s.shdr.sh_type != SHT_SYMTAB && elf_s.shdr.sh_type != SHT_DYNSYM) continue; struct elf_entry elf_e; struct symbol sym; - size_t prev_sym_value = 0; for (elf_symbol_entry_begin(&elf_e, &elf_s); elf_symbol_entry_next(&elf_e, &elf_s);) { if (GELF_ST_TYPE(elf_e.sym.st_info) != STT_FUNC && // GELF_ST_TYPE(elf_e.sym.st_info) != STT_OBJECT && @@ -104,15 +104,11 @@ struct symbol_arr* new_symbol_arr(char* libname, struct dyn_symbol_set* dyn_syms DEBUG("Dynamic symbol: %s\n", sym.name); } - if (elf_e.sym.st_value == prev_sym_value) continue; if (elf_e.sym.st_shndx == STN_UNDEF) continue; if (sym.size == 0) continue; if (lib && !contain_dyn_symbol(dyn_symset, sym.name)) continue; - // if (strcmp(sym.name, "_start") == 0) continue; - push_symbol(symbols, &sym); - prev_sym_value = elf_e.sym.st_value; } } elf_head_end(&elf); @@ -169,4 +165,4 @@ char* find_symbol_name(struct symbol_arr* symbols, size_t addr) { } } return NULL; -} +} \ No newline at end of file diff --git a/eBPF_Supermarket/User_Function_Tracer/src/symbol.h b/eBPF_Supermarket/User_Function_Tracer/src/symbol.h index 7dfa27833..4421ddb81 100644 --- a/eBPF_Supermarket/User_Function_Tracer/src/symbol.h +++ b/eBPF_Supermarket/User_Function_Tracer/src/symbol.h @@ -19,9 +19,10 @@ #ifndef UTRACE_SYMBOL_H #define UTRACE_SYMBOL_H -#include "elf.h" #include +#include "elf.h" + /** * @brief 表示一个符号条目 */ @@ -139,4 +140,4 @@ void push_symbol_arr(struct symbol_tab* symbol_tab, struct symbol_arr* symbols); */ void delete_symbol_tab(struct symbol_tab* symbol_tab); -#endif // UTRACE_SYMTAB_H +#endif // UTRACE_SYMTAB_H \ No newline at end of file diff --git a/eBPF_Supermarket/User_Function_Tracer/src/utrace.bpf.c b/eBPF_Supermarket/User_Function_Tracer/src/utrace.bpf.c index fd6b39937..6a5c8ef7a 100644 --- a/eBPF_Supermarket/User_Function_Tracer/src/utrace.bpf.c +++ b/eBPF_Supermarket/User_Function_Tracer/src/utrace.bpf.c @@ -16,74 +16,106 @@ // // eBPF用户态探针部分 -#include "utrace.h" - #include "vmlinux.h" +#include "utrace.h" + #include #include #include char LICENSE[] SEC("license") = "Dual BSD/GPL"; +static int global_sz; +static int current_pid; struct { __uint(type, BPF_MAP_TYPE_HASH); - __uint(max_entries, 8192); - __type(key, s32); - __type(value, u64); + __type(key, __u32); + __type(value, __u64); + __uint(max_entries, MAX_STACK_DEPTH); } function_start SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, __u32); + __type(value, __u32); + __uint(max_entries, MAX_THREAD_NUM); +} stack_depth SEC(".maps"); + struct { __uint(type, BPF_MAP_TYPE_RINGBUF); - __uint(max_entries, 256 * 1024); + __uint(max_entries, 2048 * 4096); } records SEC(".maps"); SEC("uprobe/trace") int BPF_KPROBE(uprobe) { struct profile_record *r; - pid_t pid, tid; - u64 id, ts; + pid_t tid, cpu_id; + __u64 ts; + __u32 *depth_ptr; + __u32 depth; - id = bpf_get_current_pid_tgid(); - tid = (u32)id; - pid = id >> 32; + current_pid = tid = (__u32)bpf_get_current_pid_tgid(); + cpu_id = bpf_get_smp_processor_id(); r = bpf_ringbuf_reserve(&records, sizeof(*r), 0); if (!r) return 0; + r->tid = tid; + r->cpu_id = cpu_id; + r->timestamp = 0; r->duration_ns = 0; - r->ustack_sz = - bpf_get_stack(ctx, r->ustack, sizeof(r->ustack), BPF_F_USER_STACK) / sizeof(r->ustack[0]); + bpf_get_stack(ctx, &r->ustack, sizeof(r->ustack), BPF_F_USER_STACK); + depth_ptr = bpf_map_lookup_elem(&stack_depth, &tid); + if (depth_ptr) + r->ustack_sz = depth = *depth_ptr + 1; + else + r->ustack_sz = depth = 0; + r->global_sz = global_sz; r->exit = 0; - s32 ustack_sz = r->ustack_sz; - ts = bpf_ktime_get_ns(); - bpf_map_update_elem(&function_start, &ustack_sz, &ts, BPF_ANY); + bpf_ringbuf_submit(r, 0); + + ts = bpf_ktime_get_ns(); + bpf_map_update_elem(&function_start, &global_sz, &ts, BPF_NOEXIST); + ++global_sz; + bpf_map_update_elem(&stack_depth, &tid, &depth, BPF_ANY); return 0; } SEC("uretprobe/trace") int BPF_KRETPROBE(uretprobe) { struct profile_record *r; - pid_t pid, tid; - u64 id, end_ts = bpf_ktime_get_ns(); - u64 *start_ts; - s32 ustack_sz; + pid_t tid, cpu_id; + __u64 *start_ts_ptr; + __u64 end_ts = bpf_ktime_get_ns(); + __u32 *depth_ptr; - id = bpf_get_current_pid_tgid(); - pid = id >> 32; - tid = (u32)id; + current_pid = tid = (__u32)bpf_get_current_pid_tgid(); + cpu_id = bpf_get_smp_processor_id(); + + depth_ptr = bpf_map_lookup_elem(&stack_depth, &tid); + if (!depth_ptr) return 0; r = bpf_ringbuf_reserve(&records, sizeof(*r), 0); if (!r) return 0; + + --global_sz; + r->tid = tid; - r->ustack_sz = bpf_get_stack(ctx, r->ustack, sizeof(r->ustack), BPF_F_USER_STACK) / - sizeof(sizeof(r->ustack[0])); - ustack_sz = r->ustack_sz; - start_ts = bpf_map_lookup_elem(&function_start, &ustack_sz); - if (start_ts) r->duration_ns = end_ts - *start_ts; - bpf_map_delete_elem(&function_start, &ustack_sz); + r->cpu_id = cpu_id; + r->timestamp = 0; + start_ts_ptr = bpf_map_lookup_elem(&function_start, &global_sz); + if (start_ts_ptr) r->duration_ns = end_ts - *start_ts_ptr; + bpf_get_stack(ctx, &r->ustack, sizeof(r->ustack), BPF_F_USER_STACK); + r->ustack_sz = *depth_ptr; + r->global_sz = global_sz; r->exit = 1; + bpf_ringbuf_submit(r, 0); + + bpf_map_delete_elem(&function_start, &global_sz); + --*depth_ptr; + bpf_map_update_elem(&stack_depth, &tid, depth_ptr, BPF_ANY); return 0; -} +} \ No newline at end of file diff --git a/eBPF_Supermarket/User_Function_Tracer/src/utrace.c b/eBPF_Supermarket/User_Function_Tracer/src/utrace.c index 58c22d732..0ad40b892 100644 --- a/eBPF_Supermarket/User_Function_Tracer/src/utrace.c +++ b/eBPF_Supermarket/User_Function_Tracer/src/utrace.c @@ -16,13 +16,11 @@ // // 基于eBPF的用户态函数观测主程序 -#include "utrace.h" +#include +#include "utrace.h" #include "utrace.skel.h" -#include "log.h" -#include "symbol.h" -#include "vmap.h" #include #include #include @@ -30,17 +28,109 @@ #include #include #include +#include +#include +#include #include #include -// TODO: delete global variables -int debug = 1; +#include "gdb.h" +#include "log.h" +#include "symbol.h" +#include "vmap.h" + +#define BASE_ADDR 0x400000 // for no-pie option + +#define NOASLR 1000 +static const struct option longopts[] = {{"command", required_argument, NULL, 'c'}, + {"pid", required_argument, NULL, 'p'}, + {"no-ASLR", no_argument, NULL, NOASLR}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0}}; + +static const char *optstring = "c:p:hd"; + +struct args { + pid_t pid; + int aslr; + char **argv; +}; + +void print_usage(char *program) { + LOG("Usage: %s [$OPTIONS...]\n", program); + LOG("\n"); + LOG("Options:\n"); + LOG(" -c --command: the command to run the program to be traced.\n"); + LOG(" -p --pid: the PID of the program to be traced.\n"); + LOG(" -d --debug: enable debug mode.\n"); + LOG(" --no-ASLR: disable Address Space Layout Randomization (ASLR).\n"); + LOG(" -h --help: disaply this usage information.\n"); + LOG("\n"); + LOG("Examples:\n"); + LOG(" sudo %s -c \"$PROGRAM $ARGS\"\n", program); + LOG(" sudo %s -p $PID\n", program); +} + +void parse_args(int argc, char *argv[], struct args *arg) { + int len, c = 0; + + arg->pid = 0; + arg->aslr = 1; + arg->argv = (char **)malloc(sizeof(char *) * 16); + + debug = 0; + + int opt, opt_index = 1; + while ((opt = getopt_long(argc, argv, optstring, longopts, NULL)) != -1) { + switch (opt) { + case 'c': // -c --command + len = strlen(optarg); + for (int i = 0; i < len; i++) { + if (optarg[i] != ' ') { + int j = i + 1; + while (j < len && optarg[j] != ' ') { + ++j; + } + optarg[j] = 0; + arg->argv[c] = strdup(optarg + i); + ++c; + optarg[j] = ' '; + i = j; + } + } + arg->argv[c] = NULL; + opt_index += 2; + break; + case 'p': // -p --pid + arg->pid = atoi(optarg); + opt_index += 2; + break; + case 'd': // -d --debug + debug = 1; + opt_index += 1; + break; + case NOASLR: + arg->aslr = 0; + break; + case 'h': // -h --help + print_usage(argv[0]); + exit(0); + default: + print_usage(argv[0]); + exit(1); + } + } + if (!c && !arg->pid) { + print_usage(argv[0]); + exit(1); + } +} struct symbol_tab *symtab; struct dyn_symbol_set *dyn_symset; struct vmap_list *vmaps; -char buf[256]; +char buf[MAX_SYMBOL_LEN]; char stack_func[MAX_STACK_DEPTH][MAX_SYMBOL_LEN]; int status = -1, pre_ustack_sz = 0; @@ -62,9 +152,6 @@ void uprobe_attach(struct utrace_bpf *skel, pid_t pid, const char *exe, size_t a bpf_program__attach_uprobe(skel->progs.uretprobe, true /* uretprobe */, pid, exe, addr); assert(skel->links.uretprobe); - - /* Attach tracepoints */ - assert(utrace_bpf__attach(skel) == 0); } bool symbolize(size_t addr) { @@ -83,47 +170,64 @@ bool symbolize(size_t addr) { } static int handle_event(void *ctx, void *data, size_t data_sz) { + static struct profile_record pending_r; + static int pending = 0; + static int status = -1; /**< 0: exec 1: exit */ + const struct profile_record *r = data; if (r->exit) { - // for (int i = 0; i < r->ustack_sz; i++) printf("%llx ", r->ustack[i]); - if (status == 0 && r->ustack_sz == pre_ustack_sz) { - printf(" "); - print_time_unit(r->duration_ns); - printf(" "); - print_tid(r->tid); - printf(" | "); - print_char(' ', 2 * r->ustack_sz - 2); - printf("%s();\n", stack_func[r->ustack_sz]); - status = 1; - pre_ustack_sz = r->ustack_sz; - } else if (status == 1 && r->ustack_sz == pre_ustack_sz - 1) { - printf(" "); - print_time_unit(r->duration_ns); - printf(" "); - print_tid(r->tid); - printf(" | "); - print_char(' ', 2 * r->ustack_sz - 2); - printf("} // %s\n", stack_func[r->ustack_sz]); - status = 1; - pre_ustack_sz = r->ustack_sz; + // LOG("EXIT"); + // for (int i = 0; i < r->ustack_sz; i++) LOG(" %llx", r->ustack[i]); + // LOG(" %s\n", stack_func[r->ustack_sz]); + // return 0; + if (status == 0) { + log_cpuid(pending_r.cpu_id); + log_split(); + log_tid(pending_r.tid); + log_split(); + log_time(r->duration_ns); + log_split(); + log_char(' ', 2 * pending_r.ustack_sz); + LOG("%s();\n", stack_func[pending_r.global_sz]); + } else { // status == 1 + log_cpuid(r->cpu_id); + log_split(); + log_tid(r->tid); + log_split(); + log_time(r->duration_ns); + log_split(); + log_char(' ', 2 * r->ustack_sz); + LOG("} "); + log_color(TERM_GRAY); + LOG("/* %s */\n", stack_func[r->global_sz]); + log_color(TERM_NC); } + pending = 0; + status = 1; } else { - // for (int i = 0; i < r->ustack_sz; i++) printf("%llx ", r->ustack[i]); - if (status == -1 && r->ustack_sz != 1) return 0; - if (status == 0 && r->ustack_sz != pre_ustack_sz + 1) return 0; - if (status == 1 && r->ustack_sz != pre_ustack_sz) return 0; + // LOG("EXEC"); + // for (int i = 0; i < r->ustack_sz; i++) LOG(" %llx", r->ustack[i]); + // if (symbolize(r->ustack[0])) { + // memcpy(stack_func[r->ustack_sz], buf, sizeof(buf)); + // } + // LOG(" %s\n", stack_func[r->ustack_sz]); + // return 0; if (status == 0) { - print_char(' ', 12); - print_tid(r->tid); - printf(" | "); - print_char(' ', 2 * r->ustack_sz - 4); - printf("%s() {\n", stack_func[r->ustack_sz - 1]); + log_cpuid(pending_r.cpu_id); + log_split(); + log_tid(pending_r.tid); + log_split(); + log_char(' ', 11); + log_split(); + log_char(' ', 2 * pending_r.ustack_sz); + LOG("%s() {\n", stack_func[pending_r.global_sz]); } - if (symbolize(r->ustack[0])) { - memcpy(stack_func[r->ustack_sz], buf, sizeof(buf)); + if (symbolize(r->ustack)) { + memcpy(stack_func[r->global_sz], buf, sizeof(buf)); + pending_r = *r; + pending = 1; status = 0; - pre_ustack_sz = r->ustack_sz; } } return 0; @@ -132,12 +236,18 @@ static int handle_event(void *ctx, void *data, size_t data_sz) { int main(int argc, char **argv) { struct ring_buffer *records = NULL; struct utrace_bpf *skel; - char *exe; + struct args arg; + struct rlimit old_rlim; + pid_t pid; + char *program; int err; - // TODO: arg parser - if (argc <= 1) return 1; - exe = argv[1]; + parse_args(argc, argv, &arg); + + if (geteuid() != 0) { + ERROR("Failed to run %s: permission denied\n", argv[0]); + return 1; + } /* Set up libbpf errors and debug info callback */ libbpf_set_print(libbpf_print_fn); @@ -156,14 +266,6 @@ int main(int argc, char **argv) { goto cleanup; } - symtab = new_symbol_tab(); - dyn_symset = new_dyn_symbol_set(); - push_symbol_arr(symtab, new_symbol_arr(exe, dyn_symset, 0)); - for (struct symbol *sym = symtab->head->sym; sym != symtab->head->sym + symtab->head->size; - sym++) { - uprobe_attach(skel, -1, exe, sym->addr); - } - /* Set up ring buffer polling */ records = ring_buffer__new(bpf_map__fd(skel->maps.records), handle_event, NULL, NULL); if (!records) { @@ -172,50 +274,171 @@ int main(int argc, char **argv) { goto cleanup; } - pid_t pid = fork(); - if (pid < 0) { - ERROR("Fork error\n"); - goto cleanup; - } else if (pid == 0) { - execlp(exe, exe, NULL); - ERROR("Execlp %s error\n", exe); - exit(1); + if (arg.pid) { + pid = arg.pid; + program = get_program(pid); } else { - // TODO: short live process - sleep(1); // wait for the address mapping + program = strdup(arg.argv[0]); + } + + symtab = new_symbol_tab(); + dyn_symset = new_dyn_symbol_set(); + push_symbol_arr(symtab, new_symbol_arr(program, dyn_symset, 0)); + for (struct symbol *sym = symtab->head->sym; sym != symtab->head->sym + symtab->head->size; + sym++) { + if (sym->addr >= BASE_ADDR) sym->addr -= BASE_ADDR; + } + + if (getrlimit(RLIMIT_NOFILE, &old_rlim) == -1) { + ERROR("getrlimit error"); + exit(1); + } + struct rlimit rlim = { + .rlim_cur = 1 << 20, + .rlim_max = 1 << 20, + }; + if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) { + ERROR("setrlimit error"); + exit(1); + } + + if (arg.pid) { + size_t pre_addr = 0; + for (struct symbol *sym = symtab->head->sym; sym != symtab->head->sym + symtab->head->size; + sym++) { + if (strcmp(sym->name, "_start") == 0) + continue; + else if (strcmp(sym->name, "__libc_csu_init") == 0) + continue; + else if (strcmp(sym->name, "__libc_csu_fini") == 0) + continue; + if (sym->addr != pre_addr) { + uprobe_attach(skel, pid, program, sym->addr); + pre_addr = sym->addr; + } + } + vmaps = new_vmap_list(pid); for (struct vmap *vmap = vmaps->head, *prev_vmap = NULL; vmap != NULL; prev_vmap = vmap, vmap = vmap->next) { - if (strcmp(basename(vmap->libname), basename(exe)) == 0) continue; + if (strcmp(basename(vmap->libname), basename(program)) == 0) continue; if (prev_vmap != NULL && strcmp(prev_vmap->libname, vmap->libname) == 0) continue; push_symbol_arr(symtab, new_symbol_arr(vmap->libname, dyn_symset, 1)); + size_t pre_addr = 0; for (struct symbol *sym = symtab->head->sym; sym != symtab->head->sym + symtab->head->size; sym++) { - if (sym->addr != 0x48a80) continue; - uprobe_attach(skel, pid, vmap->libname, sym->addr); + if (strcmp(sym->name, "__cxa_finalize") == 0) + continue; + else if (strcmp(sym->name, "__libc_start_main") == 0) + continue; + if (sym->addr != pre_addr) { + uprobe_attach(skel, pid, vmap->libname, sym->addr); + pre_addr = sym->addr; + } } } - print_header(); - /* Process events */ - int status = 0; - while (true) { - err = ring_buffer__poll(records, 100 /* timeout, ms */); - /* Ctrl-C will cause -EINTR */ - if (err == -EINTR) { - err = 0; + /* Attach tracepoints */ + assert(utrace_bpf__attach(skel) == 0); + } else { + size_t break_addr = 0; + for (struct symbol *sym = symtab->head->sym; sym != symtab->head->sym + symtab->head->size; + sym++) { + if (strcmp(sym->name, "_start") == 0) { + break_addr = sym->addr; break; } - if (err < 0) { - ERROR("Error polling perf buffer: %d\n", err); - break; + } + + pid = fork(); + if (pid < 0) { + ERROR("Fork error\n"); + goto cleanup; + } else if (pid == 0) { + if (!arg.aslr) personality(ADDR_NO_RANDOMIZE); + ptrace(PTRACE_TRACEME, 0, 0, 0); + execv(program, arg.argv); + ERROR("Execv %s error\n", program); + exit(1); + } else { + struct gdb *gdb = new_gdb(pid); + wait_for_signal(gdb); + + break_addr += get_base_addr(pid); + DEBUG("break address: %zx\n", break_addr); + + enable_breakpoint(gdb, break_addr); + continue_execution(gdb); + wait_for_signal(gdb); + + size_t pre_addr = 0; + for (struct symbol *sym = symtab->head->sym; sym != symtab->head->sym + symtab->head->size; + sym++) { + if (strcmp(sym->name, "_start") == 0) + continue; + else if (strcmp(sym->name, "__libc_csu_init") == 0) + continue; + else if (strcmp(sym->name, "__libc_csu_fini") == 0) + continue; + else if (strcmp(sym->name, "_dl_relocate_static_pie") == 0) + continue; + if (sym->addr != pre_addr) { + uprobe_attach(skel, pid, program, sym->addr); + pre_addr = sym->addr; + } } - if (err == 0) { - pid_t ret = waitpid(pid, &status, WNOHANG); + + vmaps = new_vmap_list(pid); + for (struct vmap *vmap = vmaps->head, *prev_vmap = NULL; vmap != NULL; + prev_vmap = vmap, vmap = vmap->next) { + if (strcmp(basename(vmap->libname), basename(program)) == 0) continue; + if (prev_vmap != NULL && strcmp(prev_vmap->libname, vmap->libname) == 0) continue; + push_symbol_arr(symtab, new_symbol_arr(vmap->libname, dyn_symset, 1)); + size_t pre_addr = 0; + for (struct symbol *sym = symtab->head->sym; sym != symtab->head->sym + symtab->head->size; + sym++) { + if (strcmp(sym->name, "__cxa_finalize") == 0) + continue; + else if (strcmp(sym->name, "__libc_start_main") == 0) + continue; + if (sym->addr != pre_addr) { + uprobe_attach(skel, pid, vmap->libname, sym->addr); + pre_addr = sym->addr; + } + } + } + + /* Attach tracepoints */ + assert(utrace_bpf__attach(skel) == 0); + + disable_breakpoint(gdb, break_addr); + delete_gdb(gdb); + } + } + + log_header(); + /* Process events */ + while (true) { + err = ring_buffer__poll(records, 100 /* timeout, ms */); + /* Ctrl-C will cause -EINTR */ + if (err == -EINTR) { + err = 0; + break; + } + if (err < 0) { + ERROR("Error polling perf buffer: %d\n", err); + break; + } + if (err == 0) { + if (arg.pid) { + if (kill(pid, 0)) break; + } else { + int wstatus; + pid_t ret = waitpid(pid, &wstatus, WNOHANG); if (ret > 0) break; else if (ret < 0) { - ERROR("Exec %s error\n", exe); + ERROR("Exec %s error\n", program); break; } } @@ -231,5 +454,12 @@ int main(int argc, char **argv) { delete_dyn_symbol_set(dyn_symset); delete_vmap_list(vmaps); + free(program); + + if (setrlimit(RLIMIT_NOFILE, &old_rlim) == -1) { + ERROR("setrlimit error"); + exit(1); + } + return err < 0 ? -err : 0; -} +} \ No newline at end of file diff --git a/eBPF_Supermarket/User_Function_Tracer/src/utrace.h b/eBPF_Supermarket/User_Function_Tracer/src/utrace.h index b79fe384d..77fce87d1 100644 --- a/eBPF_Supermarket/User_Function_Tracer/src/utrace.h +++ b/eBPF_Supermarket/User_Function_Tracer/src/utrace.h @@ -19,17 +19,23 @@ #ifndef UTRACE_UTRACE_H #define UTRACE_UTRACE_H -#define MAX_SYMBOL_LEN 64 +#define MAX_THREAD_NUM 32 #define MAX_STACK_DEPTH 128 +#define MAX_MANGLED_LEN 128 +#define MAX_SYMBOL_LEN 1024 #define MAX_PATH_LEN 256 -typedef unsigned long long stack_trace_t[MAX_STACK_DEPTH]; +typedef unsigned long long stack_trace_t; /** * @brief 内核态传给用户态的数据 */ struct profile_record { - unsigned int tid; /**< 线程编号 */ + unsigned int tid; /**< 线程编号 */ + unsigned int next_tid; /**< 切换后的线程编号 */ + unsigned int cpu_id; /**< CPU编号 */ + + unsigned long long timestamp; /**< 时间戳 */ unsigned long long duration_ns; /**< 函数时延 */ unsigned int kstack_sz; /**< 内核栈大小 */ @@ -38,7 +44,9 @@ struct profile_record { unsigned int ustack_sz; /**< 用户栈大小 */ stack_trace_t ustack; /**< 用户栈 */ + unsigned int global_sz; /**< 当前函数深度(考虑了多线程) */ + int exit; /**< 是否为函数退出时 */ }; -#endif // UTRACE_UTRACE_H +#endif // UTRACE_UTRACE_H \ No newline at end of file diff --git a/eBPF_Supermarket/User_Function_Tracer/src/vmap.c b/eBPF_Supermarket/User_Function_Tracer/src/vmap.c index 3ae2df9e8..404209044 100644 --- a/eBPF_Supermarket/User_Function_Tracer/src/vmap.c +++ b/eBPF_Supermarket/User_Function_Tracer/src/vmap.c @@ -18,13 +18,13 @@ #include "utrace.h" -#include "vmap.h" - -#include "log.h" #include #include #include +#include "log.h" +#include "vmap.h" + static struct vmap *new_vmap() { struct vmap *v = (struct vmap *)malloc(sizeof(struct vmap)); v->next = NULL; @@ -102,3 +102,40 @@ struct vmap *find_vmap(struct vmap_list *vmaps, size_t addr) { } return NULL; } + +size_t get_base_addr(pid_t pid) { + static char buf[MAX_PATH_LEN]; + size_t base_addr = 0; + snprintf(buf, sizeof(buf), "/proc/%d/maps", pid); + + FILE *fmap = fopen(buf, "r"); + if (fmap == NULL) { + ERROR("Cannot open %s\n", buf); + exit(1); + } + + fgets(buf, sizeof(buf), fmap); + sscanf(buf, "%zx", &base_addr); + return base_addr; +} + +char *get_program(pid_t pid) { + static char buf[MAX_PATH_LEN]; + size_t base_addr = 0; + snprintf(buf, sizeof(buf), "/proc/%d/maps", pid); + + FILE *fmap = fopen(buf, "r"); + if (fmap == NULL) { + ERROR("Cannot open %s\n", buf); + exit(1); + } + + fgets(buf, sizeof(buf), fmap); + int i = strlen(buf) - 1; + buf[i] = 0; // '\n' + while (i >= 0) { + if (buf[i] == ' ') break; + --i; + } + return strdup(buf + i + 1); +} \ No newline at end of file diff --git a/eBPF_Supermarket/User_Function_Tracer/src/vmap.h b/eBPF_Supermarket/User_Function_Tracer/src/vmap.h index ec9413473..4937afce4 100644 --- a/eBPF_Supermarket/User_Function_Tracer/src/vmap.h +++ b/eBPF_Supermarket/User_Function_Tracer/src/vmap.h @@ -74,4 +74,20 @@ void delete_vmap_list(struct vmap_list* vmaps); */ struct vmap* find_vmap(struct vmap_list* vmaps, size_t addr); +/** + * @brief 获得进程在运行时的起始物理地址 + * @param[in] pid 对应进程的进程号 + * @return 起始地址 + * @details 虚拟文件/proc/pid/maps中第一行的第一个字段 + */ +size_t get_base_addr(pid_t pid); + +/** + * @brief 获得进程的名称 + * @param[in] pid 对应进程的进程号 + * @return 进程名称 + * @details 虚拟文件/proc/pid/maps中第一行的最后一个字段 + */ +char* get_program(pid_t pid); + #endif // UTRACE_VMAP_H diff --git a/eBPF_Supermarket/User_Function_Tracer/test/args.c b/eBPF_Supermarket/User_Function_Tracer/test/args.c deleted file mode 100644 index b91582ef9..000000000 --- a/eBPF_Supermarket/User_Function_Tracer/test/args.c +++ /dev/null @@ -1,68 +0,0 @@ -#include -#include -#include - -void foobar() {} - -void foo() {} - -void bar() { - foobar(); - foobar(); -} - -void tf1() {} - -void tf2() {} - -void arg_int3(int x, int y, int z) { - /* - printf("&x: %p\n", &x); - printf("&y: %p\n", &y); - printf("&z: %p\n", &z); - */ -} -void arg_int2(int a, int b) { arg_int3(a, b, b + 1); } -void arg_int1(int i) { arg_int2(i, i + 1); } - -void arg_uint3(unsigned int x, unsigned int y, unsigned int z) {} -void arg_uint2(unsigned int a, unsigned int b) { arg_uint3(a, b, b + 1); } -void arg_uint1(unsigned int i) { arg_uint2(i, i + 1); } - -void arg_char3(char x, char y, char z) {} -void arg_char2(char a, char b) { arg_char3(a, b, b + 1); } -void arg_char1(char i) { arg_char2(i, i + 1); } - -void arg_short3(short x, short y, short z) {} -void arg_short2(short a, short b) { arg_short3(a, b, b + 1); } -void arg_short1(short i) { arg_short2(i, i + 1); } - -void arg_long3(long x, long y, long z) {} -void arg_long2(long a, long b) { arg_long3(a, b, b + 1); } -void arg_long1(long i) { arg_long2(i, i + 1); } -void arg_ulong3(unsigned long x, unsigned long y, unsigned long z) {} -void arg_ulong2(unsigned long a, unsigned long b) { arg_ulong3(a, b, b + 1); } -void arg_ulong1(long i) { arg_ulong2(i, i + 1); } - -void arg_longlong3(long long x, long long y, long long z) {} -void arg_longlong2(long long a, long long b) { arg_longlong3(a, b, b + 1); } -void arg_longlong1(long long i) { arg_longlong2(i, i + 1); } -void arg_ulonglong3(unsigned long long x, unsigned long long y, unsigned long long z) {} -void arg_ulonglong2(unsigned long long a, unsigned long long b) { arg_ulonglong3(a, b, b + 1); } -void arg_ulonglong1(unsigned long long i) { arg_ulonglong2(i, i + 1); } - -void arg_test(int x, char *z) {} - -int main(int argc, char *argv[]) { - sleep(2); - arg_int1(10); - arg_uint1(10); - arg_char1(20); - arg_short1(30); - arg_long1(40); - arg_ulong1(50); - arg_longlong1(60); - arg_ulonglong1(70); - arg_test(100, NULL); - return 0; -} diff --git a/eBPF_Supermarket/User_Function_Tracer/test/fib.c b/eBPF_Supermarket/User_Function_Tracer/test/fib.c index b610a5c01..a3d112d9b 100644 --- a/eBPF_Supermarket/User_Function_Tracer/test/fib.c +++ b/eBPF_Supermarket/User_Function_Tracer/test/fib.c @@ -1,6 +1,20 @@ -#include -#include -#include +// 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: jinyufeng2000@gmail.com +// +// 测试递归函数调用 int fib(int n) { if (n <= 2) return 1; @@ -8,8 +22,6 @@ int fib(int n) { } int main() { - sleep(2); - int n = 8; - - return !!fib(n); + fib(9); + return 0; } diff --git a/eBPF_Supermarket/User_Function_Tracer/test/mmap.c b/eBPF_Supermarket/User_Function_Tracer/test/mmap.c index 7d5cbb4e7..4bc2cb73f 100644 --- a/eBPF_Supermarket/User_Function_Tracer/test/mmap.c +++ b/eBPF_Supermarket/User_Function_Tracer/test/mmap.c @@ -1,4 +1,23 @@ +// 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: jinyufeng2000@gmail.com +// +// 测试库函数调用 + #include +#include #include #include #include @@ -13,14 +32,12 @@ int foo(long sz) { munmap(ptr, sz); close(fd); + printf("Finish mmap.\n"); + return 0; } -int main(int argc, char *argv[]) { - sleep(2); - - int n = 4096; - - foo(n); +int main() { + foo(4096); return 0; } diff --git a/eBPF_Supermarket/User_Function_Tracer/test/short.c b/eBPF_Supermarket/User_Function_Tracer/test/short.c deleted file mode 100644 index 3308f521b..000000000 --- a/eBPF_Supermarket/User_Function_Tracer/test/short.c +++ /dev/null @@ -1,10 +0,0 @@ -#include - -void h() { printf("h!"); } -void g() { h(); } -void f() { g(); } - -int main() { - f(); - return 0; -} diff --git a/eBPF_Supermarket/User_Function_Tracer/test/sleep.c b/eBPF_Supermarket/User_Function_Tracer/test/sleep.c index 761a3e1ad..53ed5583b 100644 --- a/eBPF_Supermarket/User_Function_Tracer/test/sleep.c +++ b/eBPF_Supermarket/User_Function_Tracer/test/sleep.c @@ -1,4 +1,21 @@ -#include +// 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: jinyufeng2000@gmail.com +// +// 测试函数时延统计 + #include void g() { sleep(2); } diff --git a/eBPF_Supermarket/User_Function_Tracer/test/strcpy.c b/eBPF_Supermarket/User_Function_Tracer/test/strcpy.c new file mode 100644 index 000000000..0552347a2 --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/test/strcpy.c @@ -0,0 +1,33 @@ +// 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: jinyufeng2000@gmail.com +// +// 测试库函数调用,涉及IFUNC符号 + +#include +#include +int main() { + char *s = "Hello!"; + char *t = malloc(15 * sizeof(char)); + int m = strlen(s); + memcpy(t, s, m); + int n = strlen(t); + int ss = strlen(s) + strlen(t); + memset(t, 0, n); + free(t); + t = malloc(12 * sizeof(char)); + free(t); + return 0; +} diff --git a/eBPF_Supermarket/User_Function_Tracer/test/test.c b/eBPF_Supermarket/User_Function_Tracer/test/test.c deleted file mode 100644 index f07b4e027..000000000 --- a/eBPF_Supermarket/User_Function_Tracer/test/test.c +++ /dev/null @@ -1,6 +0,0 @@ -#include - -int main() { - sleep(2); - return 0; -} diff --git a/eBPF_Supermarket/User_Function_Tracer/test/thread.c b/eBPF_Supermarket/User_Function_Tracer/test/thread.c new file mode 100644 index 000000000..1ee2c463d --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/test/thread.c @@ -0,0 +1,41 @@ +// 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: jinyufeng2000@gmail.com +// +// 测试多线程程序 + +#include +#include + +static void* c(void* n) { return n; } + +static void* b(void* n) { return c(n); } + +static void* a(void* n) { return b(n); } + +int main() { + int i; + void* v; + int n = 10; + pthread_t t[4]; + + for (i = 0; i < 4; i++) pthread_create(&t[i], NULL, a, &n); + for (i = 0; i < 4; i++) { + pthread_join(t[i], &v); + } + + assert(*(int*)v == n); + return 0; +}