Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

完善基于ebpf观测用户态函数时延项目:利用ptrace机制解决短时间程序的观测 #494

Merged
merged 17 commits into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .github/workflows/user_function_tracer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
16 changes: 11 additions & 5 deletions eBPF_Supermarket/User_Function_Tracer/.clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -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: '<getopt.h>'
Priority: -1
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
- Regex: '^(<|"(gtest|isl|json)/)'
Priority: 2
- Regex: '.\*'
Priority: 1
38 changes: 25 additions & 13 deletions eBPF_Supermarket/User_Function_Tracer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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++符号的展示
- 更多的测试
40 changes: 26 additions & 14 deletions eBPF_Supermarket/User_Function_Tracer/src/demangle.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,36 @@

#include "utrace.h"

#include "demangle.h"

#include <stdlib.h>
#include <string.h>

#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);
}
2 changes: 1 addition & 1 deletion eBPF_Supermarket/User_Function_Tracer/src/demangle.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
55 changes: 55 additions & 0 deletions eBPF_Supermarket/User_Function_Tracer/src/gdb.c
Original file line number Diff line number Diff line change
@@ -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 <stdlib.h>
#include <sys/ptrace.h>
#include <sys/wait.h>

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);
}
74 changes: 74 additions & 0 deletions eBPF_Supermarket/User_Function_Tracer/src/gdb.h
Original file line number Diff line number Diff line change
@@ -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 <stdint.h>
#include <unistd.h>

/*
* @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
33 changes: 24 additions & 9 deletions eBPF_Supermarket/User_Function_Tracer/src/log.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,36 @@

#include "log.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

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[] = {
Expand All @@ -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]);
}
Loading