diff --git a/.github/workflows/user_function_tracer.yml b/.github/workflows/user_function_tracer.yml new file mode 100644 index 000000000..91dfff2af --- /dev/null +++ b/.github/workflows/user_function_tracer.yml @@ -0,0 +1,37 @@ +name: user_function_tracer + +on: + push: + branches: + - "*" + paths: + - "eBPF_Supermarket/User_Function_Tracer/**" + - ".github/workflows/user_function_tracer.yml" + pull_request: + branches: + - "*" + paths: + - "eBPF_Supermarket/User_Function_Tracer/**" + - ".github/workflows/user_function_tracer.yml" + +jobs: + build-and-run-tracer: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install dependencies + run: | + sudo apt update + sudo apt install -y clang cmake ninja-build libelf1 libelf-dev zlib1g-dev libbpf-dev linux-tools-$(uname -r) linux-cloud-tools-$(uname -r) + + - name: Build and run + run: | + cd eBPF_Supermarket/User_Function_Tracer + mkdir -p vmlinux + bash tools/gen_vmlinux_h.sh > vmlinux/vmlinux.h + cmake -B build -S . -G Ninja + cmake --build build + gcc test/sleep.c -o test/sleep + sudo build/utrace test/sleep diff --git a/eBPF_Supermarket/User_Function_Tracer/.clang-format b/eBPF_Supermarket/User_Function_Tracer/.clang-format new file mode 100644 index 000000000..d35c6c267 --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/.clang-format @@ -0,0 +1,12 @@ +Language: Cpp +BasedOnStyle: Google +IndentWidth: 2 +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 diff --git a/eBPF_Supermarket/User_Function_Tracer/.gitignore b/eBPF_Supermarket/User_Function_Tracer/.gitignore new file mode 100644 index 000000000..53877c240 --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/.gitignore @@ -0,0 +1,5 @@ +build/ +doc/ +vmlinux/vmlinux.h +*.out +*.data/ diff --git a/eBPF_Supermarket/User_Function_Tracer/CMakeLists.txt b/eBPF_Supermarket/User_Function_Tracer/CMakeLists.txt new file mode 100644 index 000000000..28d1b53a4 --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/CMakeLists.txt @@ -0,0 +1,42 @@ +cmake_minimum_required(VERSION 3.16) + +project(utrace + DESCRIPTION "User Function Tracer" + LANGUAGES C +) + +SET(CMAKE_C_COMPILER /usr/bin/clang) + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake) + +set(BPFOBJECT_VMLINUX_H ${CMAKE_CURRENT_SOURCE_DIR}/vmlinux/vmlinux.h) + +find_package(BpfObject REQUIRED) +find_package(LibBpf REQUIRED) + +set(EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/build) +set(CMAKE_EXE_LINKER_FLAGS "-lstdc++ -lelf") + +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src) + +# format target +file(GLOB + FILES_NEED_FORMAT + CONFIGURE_DEPENDS + "src/*.c" + "src/*.h" + "test/*.c" +) + +add_custom_target(format + COMMAND clang-format --style=file -i ${FILES_NEED_FORMAT} + COMMENT "Running clang-format." + VERBATIM +) + +add_custom_target(doc + COMMAND doxygen ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile + COMMENT "Generating docs." + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + VERBATIM +) diff --git a/eBPF_Supermarket/User_Function_Tracer/Doxyfile b/eBPF_Supermarket/User_Function_Tracer/Doxyfile new file mode 100644 index 000000000..b30980489 --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/Doxyfile @@ -0,0 +1,340 @@ +# Doxyfile 1.8.17 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = "User Function Tracer" +PROJECT_NUMBER = +PROJECT_BRIEF = +PROJECT_LOGO = +OUTPUT_DIRECTORY = doc/ +CREATE_SUBDIRS = NO +ALLOW_UNICODE_NAMES = NO +OUTPUT_LANGUAGE = Chinese +OUTPUT_TEXT_DIRECTION = None +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +JAVADOC_BANNER = NO +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 4 +ALIASES = +TCL_SUBST = +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +OPTIMIZE_OUTPUT_SLICE = NO +EXTENSION_MAPPING = +MARKDOWN_SUPPORT = YES +TOC_INCLUDE_HEADINGS = 5 +AUTOLINK_SUPPORT = YES +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = YES +DISTRIBUTE_GROUP_DOC = NO +GROUP_NESTED_COMPOUNDS = NO +SUBGROUPING = YES +INLINE_GROUPED_CLASSES = NO +INLINE_SIMPLE_STRUCTS = NO +TYPEDEF_HIDES_STRUCT = YES +LOOKUP_CACHE_SIZE = 0 +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = YES +EXTRACT_PRIVATE = NO +EXTRACT_PRIV_VIRTUAL = NO +EXTRACT_PACKAGE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = YES +HIDE_COMPOUND_REFERENCE= NO +SHOW_INCLUDE_FILES = YES +SHOW_GROUPED_MEMB_INC = NO +FORCE_LOCAL_INCLUDES = NO +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_MEMBERS_CTORS_1ST = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +STRICT_PROTO_MATCHING = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +LAYOUT_FILE = +CITE_BIB_FILES = +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = YES +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_AS_ERROR = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = src/ +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = *.c \ + *.h +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +FILTER_SOURCE_PATTERNS = +USE_MDFILE_AS_MAINPAGE = +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +SOURCE_TOOLTIPS = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +CLANG_ASSISTED_PARSING = NO +CLANG_OPTIONS = +CLANG_DATABASE_PATH = +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_EXTRA_STYLESHEET = +HTML_EXTRA_FILES = +HTML_COLORSTYLE_HUE = 220 +HTML_COLORSTYLE_SAT = 100 +HTML_COLORSTYLE_GAMMA = 80 +HTML_TIMESTAMP = NO +HTML_DYNAMIC_MENUS = YES +HTML_DYNAMIC_SECTIONS = NO +HTML_INDEX_NUM_ENTRIES = 100 +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project +DOCSET_PUBLISHER_ID = org.doxygen.Publisher +DOCSET_PUBLISHER_NAME = Publisher +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = org.doxygen.Project +QHP_VIRTUAL_FOLDER = doc +QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = +QHG_LOCATION = +GENERATE_ECLIPSEHELP = NO +ECLIPSE_DOC_ID = org.doxygen.Project +DISABLE_INDEX = NO +GENERATE_TREEVIEW = YES +ENUM_VALUES_PER_LINE = 4 +TREEVIEW_WIDTH = 250 +EXT_LINKS_IN_WINDOW = NO +FORMULA_FONTSIZE = 10 +FORMULA_TRANSPARENT = YES +FORMULA_MACROFILE = +USE_MATHJAX = NO +MATHJAX_FORMAT = HTML-CSS +MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/ +MATHJAX_EXTENSIONS = +MATHJAX_CODEFILE = +SEARCHENGINE = YES +SERVER_BASED_SEARCH = NO +EXTERNAL_SEARCH = NO +SEARCHENGINE_URL = +SEARCHDATA_FILE = searchdata.xml +EXTERNAL_SEARCH_ID = +EXTRA_SEARCH_MAPPINGS = +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = +MAKEINDEX_CMD_NAME = makeindex +LATEX_MAKEINDEX_CMD = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4 +EXTRA_PACKAGES = +LATEX_HEADER = +LATEX_FOOTER = +LATEX_EXTRA_STYLESHEET = +LATEX_EXTRA_FILES = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +LATEX_SOURCE_CODE = NO +LATEX_BIB_STYLE = plain +LATEX_TIMESTAMP = NO +LATEX_EMOJI_DIRECTORY = +#--------------------------------------------------------------------------- +# Configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +RTF_SOURCE_CODE = NO +#--------------------------------------------------------------------------- +# Configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_SUBDIR = +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# Configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_PROGRAMLISTING = YES +XML_NS_MEMB_FILE_SCOPE = NO +#--------------------------------------------------------------------------- +# Configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- +GENERATE_DOCBOOK = NO +DOCBOOK_OUTPUT = docbook +DOCBOOK_PROGRAMLISTING = NO +#--------------------------------------------------------------------------- +# Configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# Configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +EXTERNAL_PAGES = YES +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +DIA_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +DOT_NUM_THREADS = 0 +DOT_FONTNAME = Helvetica +DOT_FONTSIZE = 10 +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +UML_LIMIT_NUM_FIELDS = 10 +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +INTERACTIVE_SVG = NO +DOT_PATH = +DOTFILE_DIRS = +MSCFILE_DIRS = +DIAFILE_DIRS = +PLANTUML_JAR_PATH = +PLANTUML_CFG_FILE = +PLANTUML_INCLUDE_PATH = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES diff --git a/eBPF_Supermarket/User_Function_Tracer/README.md b/eBPF_Supermarket/User_Function_Tracer/README.md new file mode 100644 index 000000000..ed4e6d40f --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/README.md @@ -0,0 +1,33 @@ +## 基于eBPF观测用户态函数时延和调用栈 (eBPF-utrace) + +### 简介 +借助eBPF用户态探针`uprobe`和`uretprobe`实现实时观测C/C++程序或者已运行进程中的用户态函数的调用时延以及调用栈, +从而清晰地了解一个程序的运行情况及其性能瓶颈。 + +### 安装依赖 +```shell +sudo apt install -y clang cmake ninja-build libelf1 libelf-dev zlib1g-dev libbpf-dev linux-tools-$(uname -r) linux-cloud-tools-$(uname -r) +``` + +### 编译运行 +```shell +mkdir -p vmlinux +bash tools/gen_vmlinux_h.sh > vmlinux/vmlinux.h +cmake -B build -S . -G Ninja +cmake --build build + +build/utrace PROGRAM|PID +``` + +### 特点 ++ 不同于`ftrace`,`eBPF-utrace`用于观测用户态函数。 ++ 不同于`uftrace`,`eBPF-utrace`基于eBPF,不依赖于任何编译技术,但是需要内核的支持,需要root权限,且可能有更高的开销。 ++ 不同于`perf`, `gprof`等性能分析工具,`eBPF-utrace`输出准确的函数调用时延,而不是基于perf_event()的采样方式。 + +### TODO +- [] short live process +- [] arg parser +- [] input pid +- [] more tests +- [] colorful log +- [] multithread diff --git a/eBPF_Supermarket/User_Function_Tracer/src/CMakeLists.txt b/eBPF_Supermarket/User_Function_Tracer/src/CMakeLists.txt new file mode 100644 index 000000000..4c0c5c303 --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/src/CMakeLists.txt @@ -0,0 +1,11 @@ +SET(app utrace.bpf.c) +get_filename_component(app_stem ${app} NAME_WE) + +bpf_object(${app_stem} ${app_stem}.bpf.c) +add_dependencies(${app_stem}_skel libbpf-build bpftool-build) + +file(GLOB_RECURSE srcs CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.c) +list(REMOVE_ITEM srcs ${CMAKE_CURRENT_SOURCE_DIR}/${app_stem}.bpf.c ${CMAKE_CURRENT_SOURCE_DIR}/${app_stem}.c) +add_executable(${app_stem} ${app_stem}.c ${srcs}) + +target_link_libraries(${app_stem} ${app_stem}_skel) diff --git a/eBPF_Supermarket/User_Function_Tracer/src/demangle.c b/eBPF_Supermarket/User_Function_Tracer/src/demangle.c new file mode 100644 index 000000000..6d5694f0f --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/src/demangle.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 +// +// 还原C++重整后的符号 + +#include "utrace.h" + +#include "demangle.h" + +#include +#include + +char *demangle(const char *mangled_name) { + char *original_name; + long len = MAX_SYMBOL_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; +} diff --git a/eBPF_Supermarket/User_Function_Tracer/src/demangle.h b/eBPF_Supermarket/User_Function_Tracer/src/demangle.h new file mode 100644 index 000000000..fc9b035c3 --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/src/demangle.h @@ -0,0 +1,34 @@ +// 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 +// +// 还原C++重整符号 + +#ifndef UTRACE_DEMANGLE_H +#define UTRACE_DEMANGLE_H + +extern char *__cxa_demangle(const char *name, char *output, long *len, int *status); + +/** + * @brief 还原重整符号 + * @param[in] mangled_name 符号 + * @return 还原后的符号 + * @details 对于未重整的符号,调用strdup() + * 对于重整过的符号(以"_Z"起始),调用abi::__cxa_demangle() + * @retval 指向堆内存 + */ +char *demangle(const char *mangled_name); + +#endif // UTRACE_DEMANGLE_H diff --git a/eBPF_Supermarket/User_Function_Tracer/src/elf.c b/eBPF_Supermarket/User_Function_Tracer/src/elf.c new file mode 100644 index 000000000..c992caa1c --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/src/elf.c @@ -0,0 +1,63 @@ +// 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 +// +// 解析ELF格式以遍历ELF中的各个节以及符号节中的各个条目 + +#include "elf.h" + +#include +#include +#include + +void elf_head_begin(struct elf_head* elf, const char* filename) { + elf->fd = open(filename, O_RDONLY); + assert(elf->fd >= 0); + + assert(elf_version(EV_CURRENT) != EV_NONE); + + elf->e = elf_begin(elf->fd, ELF_C_READ_MMAP, NULL); + assert(elf->e); + + assert(gelf_getehdr(elf->e, &elf->ehdr)); +} + +void elf_head_end(struct elf_head* elf) { + elf_end(elf->e); + close(elf->fd); +} + +void elf_section_begin(struct elf_section* elf_s, struct elf_head* elf) { elf_s->scn = NULL; } + +int elf_section_next(struct elf_section* elf_s, struct elf_head* elf) { + elf_s->scn = elf_nextscn(elf->e, elf_s->scn); + return elf_s->scn && gelf_getshdr(elf_s->scn, &elf_s->shdr); +} + +void elf_symbol_entry_begin(struct elf_entry* elf_e, struct elf_section* elf_s) { + elf_e->i = 0; + elf_e->num = elf_s->shdr.sh_size / elf_s->shdr.sh_entsize; + elf_e->data = elf_getdata(elf_s->scn, NULL); + elf_e->str_idx = elf_s->shdr.sh_link; +} + +int elf_symbol_entry_next(struct elf_entry* elf_e, struct elf_section* elf_s) { + if ((elf_s->shdr.sh_type != SHT_SYMTAB && elf_s->shdr.sh_type != SHT_DYNSYM) || + elf_e->i >= elf_e->num) + return 0; + gelf_getsym(elf_e->data, elf_e->i, &elf_e->sym); + elf_e->i++; + return 1; +} diff --git a/eBPF_Supermarket/User_Function_Tracer/src/elf.h b/eBPF_Supermarket/User_Function_Tracer/src/elf.h new file mode 100644 index 000000000..e5abefeeb --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/src/elf.h @@ -0,0 +1,99 @@ +// 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 +// +// 解析ELF格式以遍历ELF中的各个节以及符号节中的各个条目 + +#ifndef UTRACE_ELF_H +#define UTRACE_ELF_H + +#include + +/** + * @brief 保存ELF头信息 + */ +struct elf_head { + int fd; /**< 文件描述符 */ + Elf* e; /**< ELF指针 */ + GElf_Ehdr ehdr; /**< ELF头 */ +}; + +/** + * @brief 根绝文件名初始化ELF头信息 + * @param[out] elf 待初始化的ELF头 + * @param[in] filename 文件名 + */ +void elf_head_begin(struct elf_head* elf, const char* filename); + +/** + * @brief 在使用完ELF头信息后释放资源 + * @param[in] elf 经过elf_head_begin()初始化后的ELF头 + */ +void elf_head_end(struct elf_head* elf); + +/** + * @brief 保存ELF节信息,包括节指针以及节头表 + */ +struct elf_section { + Elf_Scn* scn; /**< ELF节 */ + GElf_Shdr shdr; /**< ELF节头 */ +}; + +/** + * @brief 开始遍历ELF的各个节 + * @param[out] elf_s 指向一个节 + * @param[in] elf 被遍历的ELF头信息 + */ +void elf_section_begin(struct elf_section* elf_s, struct elf_head* elf); + +/** + * @brief 移动到下一个ELF节 + * @param[out] elf_s 指向一个节 + * @param[in] elf 被遍历的ELF头信息 + * @return 指示是否遍历结束 + * @retval 0 当前elf_s合法 + * 1 当前elf_s不合法,即遍历结束 + */ +int elf_section_next(struct elf_section* elf_s, struct elf_head* elf); + +/** + * @brief 保存ELF条目 + */ +struct elf_entry { + size_t i; /**< 当前条目序号 */ + size_t num; /**< 条目总数 */ + Elf_Data* data; /**< 具体数据 */ + GElf_Sym sym; /**< 符号表项 */ + size_t str_idx; /**< 字符串表序号 */ +}; + +/** + * @brief 开始遍历ELF节(.symtab, .dynsym)中的各个条目 + * @param[out] elf_e 指向一个条目 + * @param[in] elf_s 被遍历的ELF节 + */ +void elf_symbol_entry_begin(struct elf_entry* elf_e, struct elf_section* elf_s); + +/** + * @brief 移动到下一个ELF条目 + * @param[out] elf_e 指向一个条目 + * @param[in] elf_s 被遍历的ELF节信息 + * @return 指示是否遍历结束 + * @retval 0 当前elf_e合法 + * 1 当前elf_e不合法,即遍历结束 + */ +int elf_symbol_entry_next(struct elf_entry* elf_e, struct elf_section* elf_s); + +#endif // UTRACE_ELF_H diff --git a/eBPF_Supermarket/User_Function_Tracer/src/log.c b/eBPF_Supermarket/User_Function_Tracer/src/log.c new file mode 100644 index 000000000..35ec9e2a6 --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/src/log.c @@ -0,0 +1,52 @@ +// 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 "log.h" + +#include + +void print_char(char c, int cnt) { + while (cnt > 0) { + printf("%c", c); + --cnt; + } +} + +void print_header() { printf("# DURATION TID FUNCTION\n"); } + +void print_tid(int tid) { printf("[%6d]", tid); } + +void print_time_unit(size_t ns) { + static char *units[] = { + "ns", "us", "ms", " s", " m", " h", + }; + static size_t limit[] = { + 1000, 1000, 1000, 1000, 60, 24, 0, + }; + + size_t t = ns, t_mod = 0; + int i = 0; + while (i < sizeof(units) / sizeof(units[0]) - 1) { + if (t < limit[i]) break; + t_mod = t % limit[i]; + t = t / limit[i]; + ++i; + } + + printf("%3zu.%03zu %s", t, t_mod, units[i]); +} diff --git a/eBPF_Supermarket/User_Function_Tracer/src/log.h b/eBPF_Supermarket/User_Function_Tracer/src/log.h new file mode 100644 index 000000000..4eab7d57b --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/src/log.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_LOG_H +#define UTRACE_LOG_H + +#include + +/** + * @brief 输出cnt个字符c + * @param[in] c 字符 + * @param[in] cnt 个数 + */ +void print_char(char c, int cnt); + +/** + * @brief 输出头部 + */ +void print_header(); + +/** + * @brief 输出线程号 + * @param[in] tid 线程号 + */ +void print_tid(int tid); + +/** + * @brief 根据参数打印合适时间单位 + * @param[in] ns 时间(单位纳秒) + * @details 从[ns,us,ms,s,m,h]中选择合适的单位输出 + */ +void print_time_unit(size_t ns); + +/** 控制是否显示调试信息,在utrace.c中定义 */ +extern int debug; + +/** + * @brief 输出调试信息 + * @details 包装了fprintf(stderr)函数的宏 + */ +#define DEBUG(fmt, ...) \ + do { \ + if (debug) { \ + fprintf(stderr, "[DEBUG] "); \ + fprintf(stderr, fmt, ##__VA_ARGS__); \ + } \ + } while (0) + +/** + * @brief 输出错误信息 + * @details 包装了fprintf(stderr)函数的宏 + */ +#define ERROR(fmt, ...) \ + do { \ + fprintf(stderr, "[ERROR] "); \ + fprintf(stderr, fmt, ##__VA_ARGS__); \ + } while (0) + +#endif // UTRACE_LOG_H diff --git a/eBPF_Supermarket/User_Function_Tracer/src/symbol.c b/eBPF_Supermarket/User_Function_Tracer/src/symbol.c new file mode 100644 index 000000000..6574fb1cd --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/src/symbol.c @@ -0,0 +1,172 @@ +// 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 "symbol.h" + +#include "demangle.h" +#include "elf.h" +#include "log.h" +#include +#include +#include +#include +#include + +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; + + if (addrl > addrr) return 1; + if (addrl < addrr) return -1; + return 0; +} + +static int namesort(const void* lhs, const void* rhs) { + const char** namel = (const char**)lhs; + const char** namer = (const char**)rhs; + return strcmp(*namel, *namer); +} + +struct dyn_symbol_set* new_dyn_symbol_set() { + struct dyn_symbol_set* dyn_symset = (struct dyn_symbol_set*)malloc(sizeof(struct dyn_symbol_set)); + dyn_symset->size = 0; + dyn_symset->cap = 16; + dyn_symset->names = (char**)malloc(dyn_symset->cap * sizeof(char*)); + return dyn_symset; +} + +static void insert_dyn_symbol(struct dyn_symbol_set* dyn_symset, char* name) { + if (dyn_symset->size == dyn_symset->cap) { + dyn_symset->cap <<= 1; + dyn_symset->names = (char**)realloc(dyn_symset->names, dyn_symset->cap * sizeof(char*)); + } + dyn_symset->names[dyn_symset->size] = strdup(name); + dyn_symset->size++; +} + +static int contain_dyn_symbol(struct dyn_symbol_set* dyn_symset, char* name) { + return (char*)bsearch(&name, dyn_symset->names, dyn_symset->size, sizeof(char*), namesort) != + NULL; +} + +void delete_dyn_symbol_set(struct dyn_symbol_set* dyn_symset) { + for (int i = 0; i < dyn_symset->size; i++) { + free(dyn_symset->names[i]); + } + free(dyn_symset->names); + free(dyn_symset); +} + +struct symbol_arr* new_symbol_arr(char* libname, struct dyn_symbol_set* dyn_symset, int lib) { + struct elf_head elf; + elf_head_begin(&elf, libname); + + struct symbol_arr* symbols = (struct symbol_arr*)malloc(sizeof(struct symbol_arr)); + symbols->size = 0; + symbols->cap = 16; // NOTE + symbols->sym = (struct symbol*)malloc(symbols->cap * sizeof(struct symbol)); + symbols->next = NULL; + symbols->libname = strdup(basename(libname)); + struct elf_section elf_s; + for (elf_section_begin(&elf_s, &elf); elf_section_next(&elf_s, &elf);) { + 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 && + GELF_ST_TYPE(elf_e.sym.st_info) != STT_GNU_IFUNC) + continue; + + sym.addr = elf_e.sym.st_value; + sym.size = elf_e.sym.st_size; + sym.name = elf_strptr(elf.e, elf_e.str_idx, elf_e.sym.st_name); + sym.name = demangle(sym.name); + + if (!lib && elf_s.shdr.sh_type == SHT_DYNSYM) { + insert_dyn_symbol(dyn_symset, sym.name); + 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); + qsort(symbols->sym, symbols->size, sizeof(struct symbol), addrsort); + if (!lib) qsort(dyn_symset->names, dyn_symset->size, sizeof(char*), namesort); + + DEBUG("Symbols in %s:\n", libname); + int i = 0; + for (struct symbol* sym = symbols->sym; sym != symbols->sym + symbols->size; sym++, i++) { + DEBUG("[%d] %lx %lx %s\n", i + 1, sym->addr, sym->size, sym->name); + } + + return symbols; +} + +static void push_symbol(struct symbol_arr* symbols, struct symbol* symbol) { + if (symbols->size == symbols->cap) { + symbols->cap <<= 1; + symbols->sym = (struct symbol*)realloc(symbols->sym, symbols->cap * sizeof(struct symbol)); + } + symbols->sym[symbols->size] = *symbol; + symbols->size++; +} + +struct symbol_tab* new_symbol_tab() { + struct symbol_tab* symtab = (struct symbol_tab*)malloc(sizeof(struct symbol_tab)); + symtab->head = NULL; + return symtab; +} + +void push_symbol_arr(struct symbol_tab* symbol_tab, struct symbol_arr* symbols) { + symbols->next = symbol_tab->head; + symbol_tab->head = symbols; +} + +void delete_symbol_tab(struct symbol_tab* symbol_tab) { + for (struct symbol_arr* symbols = symbol_tab->head; symbols != NULL;) { + for (struct symbol* sym = symbols->sym; sym != symbols->sym + symbols->size; sym++) { + free(sym->name); + } + free(symbols->sym); + free(symbols->libname); + struct symbol_arr* next_symbols = symbols->next; + free(symbols); + symbols = next_symbols; + } + free(symbol_tab); +} + +char* find_symbol_name(struct symbol_arr* symbols, size_t addr) { + for (struct symbol* sym = symbols->sym; sym != symbols->sym + symbols->size; sym++) { + if (sym->addr <= addr && addr < sym->addr + sym->size) { + return demangle(sym->name); + } + } + return NULL; +} diff --git a/eBPF_Supermarket/User_Function_Tracer/src/symbol.h b/eBPF_Supermarket/User_Function_Tracer/src/symbol.h new file mode 100644 index 000000000..7dfa27833 --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/src/symbol.h @@ -0,0 +1,142 @@ +// 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_SYMBOL_H +#define UTRACE_SYMBOL_H + +#include "elf.h" +#include + +/** + * @brief 表示一个符号条目 + */ +struct symbol { + size_t addr; /**< 虚拟地址 */ + size_t size; /**< 符号大小 */ + char* name; /**< 符号名称(指向堆内存) */ +}; + +/** + * @brief 动态符号集合 + * @details 动态扩展的vector + */ +struct dyn_symbol_set { + int size; /**< 动态符号个数 */ + int cap; /**< 已分配个数 */ + char** names; /**< 动态符号名 */ +}; + +/** + * @brief 创建一个空的动态符号集合 + * @return 指向该符号集合的指针 + * @details 从堆内存中分配 + */ +struct dyn_symbol_set* new_dyn_symbol_set(); + +/** + * @brief 向动态符号集合中插入一个符号 + * @param[in] dyn_symset 指向一个由new_dyn_symbol_set()创建的动态符号集合的指针 + * @param[in] name 要插入的符号的名称 + * @details 在插入时不保证集合的有序性和唯一性 + */ +static void insert_dyn_symbol(struct dyn_symbol_set* dyn_symset, char* name); + +/** + * @brief 查询都动态符号集合中是否存在一个符号 + * @param[in] dyn_symset 指向待查询的动态符号集合的指针 + * @param[in] name 待查询的符号名称 + * @retval 1 包含 + * 0 不包含 + * @details 需提前对集合中的符号按名称的字典序升序排列后,再进行二分查找 + */ +static int contain_dyn_symbol(struct dyn_symbol_set* dyn_symset, char* name); + +/** + * @brief 删除一个动态符号集合 + * @param[in] dyn_symset 指向要删除的动态符号集合的指针 + */ +void delete_dyn_symbol_set(struct dyn_symbol_set* dyn_symset); + +/** + * @brief 符号数组 + * @details 动态扩展的vector + */ +struct symbol_arr { + int size; /**< 符号个数 */ + int cap; /**< 已分配个数 */ + struct symbol* sym; /**< 符号条目数组 */ + + struct symbol_arr* next; /**< 下一个符号数组 */ + char* libname; /**< 该符号数组对应的库/进程名(指向堆内存)*/ +}; + +/** + * @brief 新建并初始化一个库/进程对应的符号数组 + * @param[in] libname 库/进程名 + * @param[in] dyn_symset 观测进程的动态符号数组 + * @param[in] lib 指示libname是库名(1)还是进程名(0) + * @return 指向初始化后的符号数组 + * @details 解析libname对应的ELF格式中的.symtab节和.dynsym节 + * 如果是进程(lib = 0),将.dynsym节的符号加入到dyn_symset集合中 + * 是动态库(lib = 1),只解析在dyn_symset集合中的符号 + */ +struct symbol_arr* new_symbol_arr(char* libname, struct dyn_symbol_set* dyn_symset, int lib); + +/** + * @brief 向符号数组中添加一个符号 + * @param[in] symbols 指向符号数组的指针 + * @param[in] symbol 指向待加入符号的指针 + */ +static void push_symbol(struct symbol_arr* symbols, struct symbol* symbol); + +/** + * @brief 从一个符号数组中查找一个虚拟地址对应的符号名称 + * @param[in] symbols 指向符号数组的指针 + * @param[in] addr 待查找的虚拟地址 + * @return addr对应的符号名称 + */ +char* find_symbol_name(struct symbol_arr* symbols, size_t addr); + +/** + * @brief 符号表 + * @details 由符号数组组成的链表 + */ +struct symbol_tab { + struct symbol_arr* head; /**< 指向链表头节点的指针 */ +}; + +/** + * @brief 新建一个符号表 + * @details 在堆上申请空间 + */ +struct symbol_tab* new_symbol_tab(); + +/** + * @brief 向符号表中添加一个符号数组 + * @param[in] symbol_tab 指向符号表的指针 + * @param[in] symbols 指向要插入的符号数组的指针 + */ +void push_symbol_arr(struct symbol_tab* symbol_tab, struct symbol_arr* symbols); + +/** + * @brief 删除一个符号表 + * @param[in] 指向要删除的符号表的指针 + */ +void delete_symbol_tab(struct symbol_tab* symbol_tab); + +#endif // UTRACE_SYMTAB_H diff --git a/eBPF_Supermarket/User_Function_Tracer/src/util.h b/eBPF_Supermarket/User_Function_Tracer/src/util.h new file mode 100644 index 000000000..c23ade3e2 --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/src/util.h @@ -0,0 +1,22 @@ +// 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_UTIL_H +#define UTRACE_UTIL_H + +#endif // UTRACE_UTIL_H diff --git a/eBPF_Supermarket/User_Function_Tracer/src/utrace.bpf.c b/eBPF_Supermarket/User_Function_Tracer/src/utrace.bpf.c new file mode 100644 index 000000000..fd6b39937 --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/src/utrace.bpf.c @@ -0,0 +1,89 @@ +// 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 +// +// eBPF用户态探针部分 + +#include "utrace.h" + +#include "vmlinux.h" + +#include +#include +#include + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 8192); + __type(key, s32); + __type(value, u64); +} function_start SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} records SEC(".maps"); + +SEC("uprobe/trace") +int BPF_KPROBE(uprobe) { + struct profile_record *r; + pid_t pid, tid; + u64 id, ts; + + id = bpf_get_current_pid_tgid(); + tid = (u32)id; + pid = id >> 32; + + r = bpf_ringbuf_reserve(&records, sizeof(*r), 0); + if (!r) return 0; + r->tid = tid; + r->duration_ns = 0; + r->ustack_sz = + bpf_get_stack(ctx, r->ustack, sizeof(r->ustack), BPF_F_USER_STACK) / sizeof(r->ustack[0]); + 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); + 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; + + id = bpf_get_current_pid_tgid(); + pid = id >> 32; + tid = (u32)id; + + r = bpf_ringbuf_reserve(&records, sizeof(*r), 0); + if (!r) return 0; + 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->exit = 1; + bpf_ringbuf_submit(r, 0); + return 0; +} diff --git a/eBPF_Supermarket/User_Function_Tracer/src/utrace.c b/eBPF_Supermarket/User_Function_Tracer/src/utrace.c new file mode 100644 index 000000000..58c22d732 --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/src/utrace.c @@ -0,0 +1,235 @@ +// 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 +// +// 基于eBPF的用户态函数观测主程序 + +#include "utrace.h" + +#include "utrace.skel.h" + +#include "log.h" +#include "symbol.h" +#include "vmap.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// TODO: delete global variables +int debug = 1; + +struct symbol_tab *symtab; +struct dyn_symbol_set *dyn_symset; +struct vmap_list *vmaps; + +char buf[256]; +char stack_func[MAX_STACK_DEPTH][MAX_SYMBOL_LEN]; +int status = -1, pre_ustack_sz = 0; + +static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) { + if (level == LIBBPF_DEBUG) return 0; + return vfprintf(stderr, format, args); +} + +void uprobe_attach(struct utrace_bpf *skel, pid_t pid, const char *exe, size_t addr) { + DEBUG("Attach to %s:%zx with pid = %d\n", exe, addr, pid); + + /* Attach tracepoint handler */ + skel->links.uprobe = + bpf_program__attach_uprobe(skel->progs.uprobe, false /* not uretprobe */, pid, exe, addr); + + assert(skel->links.uprobe); + + skel->links.uretprobe = + 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) { + struct vmap *vmap = find_vmap(vmaps, addr); + if (vmap == NULL) return false; + for (struct symbol_arr *symbols = symtab->head; symbols != NULL; symbols = symbols->next) { + if (strcmp(symbols->libname, basename(vmap->libname)) != 0) continue; + char *name = find_symbol_name(symbols, addr - vmap->addr_st + vmap->offset); + if (name == NULL) return false; + memcpy(buf, name, strlen(name)); + buf[strlen(name)] = 0; + free(name); + return true; + } + return false; +} + +static int handle_event(void *ctx, void *data, size_t data_sz) { + 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; + } + } 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; + 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]); + } + if (symbolize(r->ustack[0])) { + memcpy(stack_func[r->ustack_sz], buf, sizeof(buf)); + status = 0; + pre_ustack_sz = r->ustack_sz; + } + } + return 0; +} + +int main(int argc, char **argv) { + struct ring_buffer *records = NULL; + struct utrace_bpf *skel; + char *exe; + int err; + + // TODO: arg parser + if (argc <= 1) return 1; + exe = argv[1]; + + /* Set up libbpf errors and debug info callback */ + libbpf_set_print(libbpf_print_fn); + + /* Load and verify BPF application */ + skel = utrace_bpf__open(); + if (!skel) { + ERROR("Failed to open and load BPF skeleton\n"); + return 1; + } + + /* Load & verify BPF programs */ + err = utrace_bpf__load(skel); + if (err) { + ERROR("Failed to load and verify BPF skeleton\n"); + 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) { + err = -1; + ERROR("Failed to create ring buffer\n"); + 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); + } else { + // TODO: short live process + sleep(1); // wait for the address mapping + 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 (prev_vmap != NULL && strcmp(prev_vmap->libname, vmap->libname) == 0) continue; + push_symbol_arr(symtab, new_symbol_arr(vmap->libname, dyn_symset, 1)); + 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); + } + } + + 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; + break; + } + if (err < 0) { + ERROR("Error polling perf buffer: %d\n", err); + break; + } + if (err == 0) { + pid_t ret = waitpid(pid, &status, WNOHANG); + if (ret > 0) + break; + else if (ret < 0) { + ERROR("Exec %s error\n", exe); + break; + } + } + } + } + +cleanup: + /* Clean up */ + ring_buffer__free(records); + utrace_bpf__destroy(skel); + + delete_symbol_tab(symtab); + delete_dyn_symbol_set(dyn_symset); + delete_vmap_list(vmaps); + + return err < 0 ? -err : 0; +} diff --git a/eBPF_Supermarket/User_Function_Tracer/src/utrace.h b/eBPF_Supermarket/User_Function_Tracer/src/utrace.h new file mode 100644 index 000000000..b79fe384d --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/src/utrace.h @@ -0,0 +1,44 @@ +// 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_UTRACE_H +#define UTRACE_UTRACE_H + +#define MAX_SYMBOL_LEN 64 +#define MAX_STACK_DEPTH 128 +#define MAX_PATH_LEN 256 + +typedef unsigned long long stack_trace_t[MAX_STACK_DEPTH]; + +/** + * @brief 内核态传给用户态的数据 + */ +struct profile_record { + unsigned int tid; /**< 线程编号 */ + unsigned long long duration_ns; /**< 函数时延 */ + + unsigned int kstack_sz; /**< 内核栈大小 */ + stack_trace_t kstack; /**< 内核栈 */ + + unsigned int ustack_sz; /**< 用户栈大小 */ + stack_trace_t ustack; /**< 用户栈 */ + + int exit; /**< 是否为函数退出时 */ +}; + +#endif // UTRACE_UTRACE_H diff --git a/eBPF_Supermarket/User_Function_Tracer/src/vmap.c b/eBPF_Supermarket/User_Function_Tracer/src/vmap.c new file mode 100644 index 000000000..3ae2df9e8 --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/src/vmap.c @@ -0,0 +1,104 @@ +// 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 "utrace.h" + +#include "vmap.h" + +#include "log.h" +#include +#include +#include + +static struct vmap *new_vmap() { + struct vmap *v = (struct vmap *)malloc(sizeof(struct vmap)); + v->next = NULL; + return v; +} + +struct vmap_list *new_vmap_list(pid_t pid) { + static char buf[MAX_PATH_LEN]; + snprintf(buf, sizeof(buf), "/proc/%d/maps", pid); + + FILE *fmap = fopen(buf, "r"); + if (fmap == NULL) { + ERROR("Cannot open %s\n", buf); + exit(1); + } + + size_t addr_st, addr_ed, offset; + char prot[5]; + int dev_major, dev_minor, inode; + char libname[MAX_PATH_LEN]; + + struct vmap_list *vmaps = (struct vmap_list *)malloc(sizeof(struct vmap_list)); + vmaps->head = NULL; + + struct vmap *prev_vmap = NULL; + while (fgets(buf, sizeof(buf), fmap)) { + if (sscanf(buf, "%zx-%zx %s %zx %x:%x %d %s\n", &addr_st, &addr_ed, prot, &offset, &dev_major, + &dev_minor, &inode, libname) != 8) + continue; + if (strlen(libname) == 0 || libname[0] == '[') continue; + if (prev_vmap != NULL && addr_st == prev_vmap->addr_ed && + strcmp(libname, prev_vmap->libname) == 0) { + prev_vmap->addr_ed = addr_ed; + } else { + struct vmap *vmap = new_vmap(); + vmap->addr_st = addr_st; + vmap->addr_ed = addr_ed; + vmap->offset = offset; + vmap->libname = strdup(libname); + if (prev_vmap) + prev_vmap->next = vmap; + else + vmaps->head = vmap; + prev_vmap = vmap; + } + } + + fclose(fmap); + + DEBUG("Virtual memory map:\n"); + int i = 0; + for (struct vmap *vmap = vmaps->head; vmap != NULL; vmap = vmap->next, i++) { + DEBUG("[%d] %zx-%zx %zx %s\n", i + 1, vmap->addr_st, vmap->addr_ed, vmap->offset, + vmap->libname); + } + + return vmaps; +} + +void delete_vmap_list(struct vmap_list *vmaps) { + for (struct vmap *vmap = vmaps->head; vmap != NULL;) { + struct vmap *next_vmap = vmap->next; + free(vmap->libname); + free(vmap); + vmap = next_vmap; + } + free(vmaps); +} + +struct vmap *find_vmap(struct vmap_list *vmaps, size_t addr) { + for (struct vmap *vmap = vmaps->head; vmap != NULL; vmap = vmap->next) { + if (vmap->addr_st <= addr && addr <= vmap->addr_ed) { + return vmap; + } + } + return NULL; +} diff --git a/eBPF_Supermarket/User_Function_Tracer/src/vmap.h b/eBPF_Supermarket/User_Function_Tracer/src/vmap.h new file mode 100644 index 000000000..ec9413473 --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/src/vmap.h @@ -0,0 +1,77 @@ +// 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_VMAP_H +#define UTRACE_VMAP_H + +#include +#include + +/** + * @brief 表示一个虚拟内存映射条目 + */ +struct vmap { + size_t addr_st; /**< 起始物理地址 */ + size_t addr_ed; /**< 结束物理地址 */ + size_t offset; /**< 虚拟地址偏移 */ + char* libname; /**< 进程/共享库名称(指向堆内存) */ + + struct vmap* next; /**< 下一个条目 */ +}; + +/** + * @brief 创建一个新的虚拟内存映射条目 + * @details 在堆上申请空间 + */ +static struct vmap* new_vmap(); + +/** + * @brief 表示一个虚拟内存映射表 + * @details 链表 + */ +struct vmap_list { + struct vmap* head; /**< 链表头节点 */ +}; + +/** + * @brief 创建进程号为pid的虚拟内存映射表 + * @param[in] pid 进程号 + * @return 对应的虚拟内存表 + * @details 查看并记录虚拟文件/proc/pid/maps + * 在堆上申请空间 + */ +struct vmap_list* new_vmap_list(pid_t pid); + +/** + * @brief 释放一个虚拟内存映射表 + * @param[in] vmaps 指向一个由new_vmap_list()创建的虚拟内存映射表 + * @details 先释放每个条目的内存,再释放表内存 + */ +void delete_vmap_list(struct vmap_list* vmaps); + +/** + * @brief 根据一个虚拟内存映射表,查找虚拟地址addr所在的虚拟内存映射条目 + * @param[in] vmaps 指向一个虚拟内存映射表 + * @param[in] addr 待查找的虚拟内存地址 + * @return 对应的虚拟内存映射条目 + * @retval struct vmap* + * NULL 失败 + */ +struct vmap* find_vmap(struct vmap_list* vmaps, size_t addr); + +#endif // UTRACE_VMAP_H diff --git a/eBPF_Supermarket/User_Function_Tracer/test/args.c b/eBPF_Supermarket/User_Function_Tracer/test/args.c new file mode 100644 index 000000000..b91582ef9 --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/test/args.c @@ -0,0 +1,68 @@ +#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 new file mode 100644 index 000000000..b610a5c01 --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/test/fib.c @@ -0,0 +1,15 @@ +#include +#include +#include + +int fib(int n) { + if (n <= 2) return 1; + return fib(n - 1) + fib(n - 2); +} + +int main() { + sleep(2); + int n = 8; + + return !!fib(n); +} diff --git a/eBPF_Supermarket/User_Function_Tracer/test/mmap.c b/eBPF_Supermarket/User_Function_Tracer/test/mmap.c new file mode 100644 index 000000000..7d5cbb4e7 --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/test/mmap.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +int foo(long sz) { + int fd; + void *ptr; + + fd = open("/dev/zero", O_RDONLY); + ptr = mmap(NULL, sz, PROT_READ, MAP_ANON | MAP_PRIVATE, fd, 0); + mprotect(ptr, sz, PROT_NONE); + munmap(ptr, sz); + close(fd); + + return 0; +} + +int main(int argc, char *argv[]) { + sleep(2); + + int n = 4096; + + foo(n); + return 0; +} diff --git a/eBPF_Supermarket/User_Function_Tracer/test/short.c b/eBPF_Supermarket/User_Function_Tracer/test/short.c new file mode 100644 index 000000000..3308f521b --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/test/short.c @@ -0,0 +1,10 @@ +#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 new file mode 100644 index 000000000..761a3e1ad --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/test/sleep.c @@ -0,0 +1,18 @@ +#include +#include + +void g() { sleep(2); } + +void f() { + g(); + sleep(1); +} + +void h() { sleep(3); } + +int main() { + sleep(5); + f(); + h(); + return 0; +} diff --git a/eBPF_Supermarket/User_Function_Tracer/test/test.c b/eBPF_Supermarket/User_Function_Tracer/test/test.c new file mode 100644 index 000000000..f07b4e027 --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/test/test.c @@ -0,0 +1,6 @@ +#include + +int main() { + sleep(2); + return 0; +} diff --git a/eBPF_Supermarket/User_Function_Tracer/tools/cmake/FindBpfObject.cmake b/eBPF_Supermarket/User_Function_Tracer/tools/cmake/FindBpfObject.cmake new file mode 100644 index 000000000..00c00ec62 --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/tools/cmake/FindBpfObject.cmake @@ -0,0 +1,185 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause + +#[=======================================================================[.rst: +FindBpfObject +-------- + +Find BpfObject + +This module finds if all the dependencies for eBPF Compile-Once-Run-Everywhere +programs are available and where all the components are located. + +The caller may set the following variables to disable automatic +search/processing for the associated component: + + ``BPFOBJECT_BPFTOOL_EXE`` + Path to ``bpftool`` binary + + ``BPFOBJECT_CLANG_EXE`` + Path to ``clang`` binary + + ``LIBBPF_INCLUDE_DIRS`` + Path to ``libbpf`` development headers + + ``LIBBPF_LIBRARIES`` + Path to `libbpf` library + + ``BPFOBJECT_VMLINUX_H`` + Path to ``vmlinux.h`` generated by ``bpftool``. If unset, this module will + attempt to automatically generate a copy. + +This module sets the following result variables: + +:: + + BpfObject_FOUND = TRUE if all components are found + + +This module also provides the ``bpf_object()`` macro. This macro generates a +cmake interface library for the BPF object's generated skeleton as well +as the associated dependencies. + +.. code-block:: cmake + + bpf_object( ) + +Given an abstract ```` for a BPF object and the associated ```` +file, generates an interface library target, ``_skel``, that may be +linked against by other cmake targets. + +Example Usage: + +:: + + find_package(BpfObject REQUIRED) + bpf_object(myobject myobject.bpf.c) + add_executable(myapp myapp.c) + target_link_libraries(myapp myobject_skel) + +#]=======================================================================] + +if(NOT BPFOBJECT_BPFTOOL_EXE) + find_program(BPFOBJECT_BPFTOOL_EXE NAMES bpftool DOC "Path to bpftool executable") +endif() + +if(NOT BPFOBJECT_CLANG_EXE) + find_program(BPFOBJECT_CLANG_EXE NAMES clang DOC "Path to clang executable") + + execute_process(COMMAND ${BPFOBJECT_CLANG_EXE} --version + OUTPUT_VARIABLE CLANG_version_output + ERROR_VARIABLE CLANG_version_error + RESULT_VARIABLE CLANG_version_result + OUTPUT_STRIP_TRAILING_WHITESPACE) + + # Check that clang is new enough + if(${CLANG_version_result} EQUAL 0) + if("${CLANG_version_output}" MATCHES "clang version ([^\n]+)\n") + # Transform X.Y.Z into X;Y;Z which can then be interpreted as a list + set(CLANG_VERSION "${CMAKE_MATCH_1}") + string(REPLACE "." ";" CLANG_VERSION_LIST ${CLANG_VERSION}) + list(GET CLANG_VERSION_LIST 0 CLANG_VERSION_MAJOR) + + # Anything older than clang 10 doesn't really work + string(COMPARE LESS ${CLANG_VERSION_MAJOR} 10 CLANG_VERSION_MAJOR_LT10) + if(${CLANG_VERSION_MAJOR_LT10}) + message(FATAL_ERROR "clang ${CLANG_VERSION} is too old for BPF CO-RE") + endif() + + message(STATUS "Found clang version: ${CLANG_VERSION}") + else() + message(FATAL_ERROR "Failed to parse clang version string: ${CLANG_version_output}") + endif() + else() + message(FATAL_ERROR "Command \"${BPFOBJECT_CLANG_EXE} --version\" failed with output:\n${CLANG_version_error}") + endif() +endif() + +if(NOT LIBBPF_INCLUDE_DIRS OR NOT LIBBPF_LIBRARIES) + find_package(LibBpf) +endif() + +if(BPFOBJECT_VMLINUX_H) + get_filename_component(GENERATED_VMLINUX_DIR ${BPFOBJECT_VMLINUX_H} DIRECTORY) +elseif(BPFOBJECT_BPFTOOL_EXE) + # Generate vmlinux.h + set(GENERATED_VMLINUX_DIR ${CMAKE_CURRENT_BINARY_DIR}) + set(BPFOBJECT_VMLINUX_H ${GENERATED_VMLINUX_DIR}/vmlinux.h) + execute_process(COMMAND ${BPFOBJECT_BPFTOOL_EXE} btf dump file /sys/kernel/btf/vmlinux format c + OUTPUT_FILE ${BPFOBJECT_VMLINUX_H} + ERROR_VARIABLE VMLINUX_error + RESULT_VARIABLE VMLINUX_result) + if(${VMLINUX_result} EQUAL 0) + set(VMLINUX ${BPFOBJECT_VMLINUX_H}) + else() + message(FATAL_ERROR "Failed to dump vmlinux.h from BTF: ${VMLINUX_error}") + endif() +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(BpfObject + REQUIRED_VARS + BPFOBJECT_BPFTOOL_EXE + BPFOBJECT_CLANG_EXE + LIBBPF_INCLUDE_DIRS + LIBBPF_LIBRARIES + GENERATED_VMLINUX_DIR) + +# Get clang bpf system includes +execute_process( + COMMAND bash -c "${BPFOBJECT_CLANG_EXE} -v -E - < /dev/null 2>&1 | + sed -n '/<...> search starts here:/,/End of search list./{ s| \\(/.*\\)|-idirafter \\1|p }'" + OUTPUT_VARIABLE CLANG_SYSTEM_INCLUDES_output + ERROR_VARIABLE CLANG_SYSTEM_INCLUDES_error + RESULT_VARIABLE CLANG_SYSTEM_INCLUDES_result + OUTPUT_STRIP_TRAILING_WHITESPACE) +if(${CLANG_SYSTEM_INCLUDES_result} EQUAL 0) + separate_arguments(CLANG_SYSTEM_INCLUDES UNIX_COMMAND ${CLANG_SYSTEM_INCLUDES_output}) + message(STATUS "BPF system include flags: ${CLANG_SYSTEM_INCLUDES}") +else() + message(FATAL_ERROR "Failed to determine BPF system includes: ${CLANG_SYSTEM_INCLUDES_error}") +endif() + +# Get target arch +execute_process(COMMAND uname -m + COMMAND sed -e "s/x86_64/x86/" -e "s/aarch64/arm64/" -e "s/ppc64le/powerpc/" -e "s/mips.*/mips/" + OUTPUT_VARIABLE ARCH_output + ERROR_VARIABLE ARCH_error + RESULT_VARIABLE ARCH_result + OUTPUT_STRIP_TRAILING_WHITESPACE) +if(${ARCH_result} EQUAL 0) + set(ARCH ${ARCH_output}) + message(STATUS "BPF target arch: ${ARCH}") +else() + message(FATAL_ERROR "Failed to determine target architecture: ${ARCH_error}") +endif() + +# Public macro +macro(bpf_object name input) + set(BPF_C_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${input}) + set(BPF_O_FILE ${CMAKE_CURRENT_BINARY_DIR}/${name}.bpf.o) + set(BPF_SKEL_FILE ${CMAKE_CURRENT_BINARY_DIR}/${name}.skel.h) + set(OUTPUT_TARGET ${name}_skel) + + # Build BPF object file + add_custom_command(OUTPUT ${BPF_O_FILE} + COMMAND ${BPFOBJECT_CLANG_EXE} -g -O2 -target bpf -D__TARGET_ARCH_${ARCH} + ${CLANG_SYSTEM_INCLUDES} -I${GENERATED_VMLINUX_DIR} + -isystem ${LIBBPF_INCLUDE_DIRS} -c ${BPF_C_FILE} -o ${BPF_O_FILE} + COMMAND_EXPAND_LISTS + VERBATIM + DEPENDS ${BPF_C_FILE} + COMMENT "[clang] Building BPF object: ${name}") + + # Build BPF skeleton header + add_custom_command(OUTPUT ${BPF_SKEL_FILE} + COMMAND bash -c "${BPFOBJECT_BPFTOOL_EXE} gen skeleton ${BPF_O_FILE} > ${BPF_SKEL_FILE}" + VERBATIM + DEPENDS ${BPF_O_FILE} + COMMENT "[skel] Building BPF skeleton: ${name}") + + add_library(${OUTPUT_TARGET} INTERFACE) + target_sources(${OUTPUT_TARGET} INTERFACE ${BPF_SKEL_FILE}) + target_include_directories(${OUTPUT_TARGET} INTERFACE ${CMAKE_CURRENT_BINARY_DIR}) + target_include_directories(${OUTPUT_TARGET} SYSTEM INTERFACE ${LIBBPF_INCLUDE_DIRS}) + target_link_libraries(${OUTPUT_TARGET} INTERFACE ${LIBBPF_LIBRARIES} -lelf -lz) +endmacro() diff --git a/eBPF_Supermarket/User_Function_Tracer/tools/cmake/FindLibBpf.cmake b/eBPF_Supermarket/User_Function_Tracer/tools/cmake/FindLibBpf.cmake new file mode 100644 index 000000000..cd558f554 --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/tools/cmake/FindLibBpf.cmake @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause + +find_path(LIBBPF_INCLUDE_DIRS + NAMES + bpf/bpf.h + bpf/btf.h + bpf/libbpf.h + PATHS + /usr/include + /usr/local/include + /opt/local/include + /sw/include + ENV CPATH) + +find_library(LIBBPF_LIBRARIES + NAMES + bpf + PATHS + /usr/lib + /usr/local/lib + /opt/local/lib + /sw/lib + ENV LIBRARY_PATH + ENV LD_LIBRARY_PATH) + +include (FindPackageHandleStandardArgs) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibBpf "Please install the libbpf development package" + LIBBPF_LIBRARIES + LIBBPF_INCLUDE_DIRS) + +mark_as_advanced(LIBBPF_INCLUDE_DIRS LIBBPF_LIBRARIES) diff --git a/eBPF_Supermarket/User_Function_Tracer/tools/gen_vmlinux_h.sh b/eBPF_Supermarket/User_Function_Tracer/tools/gen_vmlinux_h.sh new file mode 100755 index 000000000..fd220abce --- /dev/null +++ b/eBPF_Supermarket/User_Function_Tracer/tools/gen_vmlinux_h.sh @@ -0,0 +1,3 @@ +#/bin/sh + +bpftool btf dump file /sys/kernel/btf/vmlinux format c