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

Update with embedded-ebpf uprobe & update CLI with syscall trace #13

Merged
merged 3 commits into from
Sep 30, 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
27 changes: 27 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,33 @@ if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
message(FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there.\n")
endif()

include(cmake/libbpf.cmake)

# install frida
include(cmake/frida.cmake)

# Define a helper function
function(add_ebpf_program_target target_name source_file output_file)
# opensnoop.bpf.o
execute_process(COMMAND bash -c "uname -m | sed 's/x86_64/x86/' \
| sed 's/arm.*/arm/' \
| sed 's/aarch64/arm64/' \
| sed 's/ppc64le/powerpc/' \
| sed 's/mips.*/mips/' \
| sed 's/riscv64/riscv/' \
| sed 's/loongarch64/loongarch/'"
OUTPUT_VARIABLE UNAME_ARCH
COMMAND_ERROR_IS_FATAL ANY
)
string(STRIP ${UNAME_ARCH} UNAME_ARCH_STRIPPED)
add_custom_target(${target_name} ALL
COMMAND clang -O2 -target bpf -c -g -D__TARGET_ARCH_${UNAME_ARCH_STRIPPED} -I${CMAKE_SOURCE_DIR}/third_party/vmlinux/${UNAME_ARCH_STRIPPED} -I${LIBBPF_INCLUDE_DIRS}/uapi -I${LIBBPF_INCLUDE_DIRS} ${source_file} -o ${output_file}
BYPRODUCTS ${output_file}
SOURCES ${source_file}
)
add_dependencies(${target_name} copy_headers)
endfunction()

# ebpf-verifier
option(ENABLE_EBPF_VERIFIER "Whether to enable ebpf verifier" OFF)
set(CMAKE_POSITION_INDEPENDENT_CODE YES)
Expand All @@ -82,6 +106,9 @@ add_subdirectory(vm)
add_subdirectory(runtime)
add_subdirectory(tools)

# benchmark that requires bpftime libraries
add_subdirectory(benchmark)

set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")

install(TARGETS bpftime-agent bpftime-agent-transformer bpftime-syscall-server CONFIGURATIONS Release Debug DESTINATION ~/.bpftime)
1 change: 1 addition & 0 deletions benchmark/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test
16 changes: 16 additions & 0 deletions benchmark/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
add_executable(simple-benchmark-with-embed-ebpf-calling test_embed.c)

add_ebpf_program_target(uprobe_prog ${CMAKE_CURRENT_SOURCE_DIR}/uprobe/uprobe.bpf.c ${CMAKE_CURRENT_BINARY_DIR}/uprobe_prog.bpf.o)
add_ebpf_program_target(uretprobe_prog ${CMAKE_CURRENT_SOURCE_DIR}/uretprobe/uretprobe.bpf.c ${CMAKE_CURRENT_BINARY_DIR}/uretprobe_prog.bpf.o)

add_dependencies(simple-benchmark-with-embed-ebpf-calling vm-bpf uprobe_prog uretprobe_prog libbpf)
target_compile_definitions(simple-benchmark-with-embed-ebpf-calling
PRIVATE
UPROBE_PROG=${CMAKE_CURRENT_BINARY_DIR}/uprobe_prog.bpf.o
URETPROBE_PROG=${CMAKE_CURRENT_BINARY_DIR}/uretprobe_prog.bpf.o
)
target_link_libraries(simple-benchmark-with-embed-ebpf-calling vm-bpf ${LIBBPF_LIBRARIES} elf z)
target_include_directories(simple-benchmark-with-embed-ebpf-calling
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}../vm/include
${LIBBPF_INCLUDE_DIRS})
1 change: 1 addition & 0 deletions benchmark/syscall/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ bootstrap
opensnoop
victim
dump*
syscall
Binary file removed benchmark/syscall/syscall
Binary file not shown.
9 changes: 5 additions & 4 deletions benchmark/syscall/victim.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ uint64_t count = 0;
void sigint_handler(int signum)
{
double avg = ((double)total_time / (double)count) / ITER_COUNT;
std::cout << "Average time usage " << std::fixed
<< std::setprecision(5) << avg << "ns, "
std::cout << "Average time usage " << std::fixed << std::setprecision(5)
<< avg << "ns, "
<< " count " << count * ITER_COUNT << std::endl;
if (signum != 0)
exit(0);
}

void test_syscall() {
void test_syscall()
{
int fd = open("/dev/null", O_CREAT | O_RDWR);
while (count < 10) {
printf("Iteration %lu\n", count);
Expand Down Expand Up @@ -60,7 +61,7 @@ int main()
return 1;
}
}
test_syscall();
puts("Done");
test_syscall();
return 0;
}
4 changes: 2 additions & 2 deletions benchmark/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ void do_benchmark_userspace(int iter)

int main()
{
putchar('\n');
puts("");
int iter = 100 * 1000;
do_benchmark_userspace(iter);
return 0;
}
}
189 changes: 189 additions & 0 deletions benchmark/test_embed.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
#include "ebpf_vm.h"
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <sys/cdefs.h>
#include <time.h>
#include <stdio.h>
#include <stdint.h>
#include <ebpf-vm.h>
#include <bpf/libbpf.h>
#include <bpf/bpf.h>
#include <errno.h>
#include <stdbool.h>

#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)

#if defined(__x86_64__) || defined(_M_X64)

struct pt_regs {
uint64_t r15;
uint64_t r14;
uint64_t r13;
uint64_t r12;
uint64_t bp;
uint64_t bx;
uint64_t r11;
uint64_t r10;
uint64_t r9;
uint64_t r8;
uint64_t ax;
uint64_t cx;
uint64_t dx;
uint64_t si;
uint64_t di;
uint64_t orig_ax;
uint64_t ip;
uint64_t cs;
uint64_t flags;
uint64_t sp;
uint64_t ss;
};
#else
#error Only x86_64 is supported
#endif

struct ebpf_vm *begin_vm = NULL;
struct ebpf_vm *end_vm = NULL;
// ebpf_jit_fn begin_fn = NULL, end_fn = NULL;
const char *uprobe_prog = TOSTRING(UPROBE_PROG);
const char *uretprobe_prog = TOSTRING(URETPROBE_PROG);
bool enable_ebpf = false;

// The timespec struct holds seconds and nanoseconds
struct timespec start_time, end_time;

void start_timer()
{
clock_gettime(CLOCK_MONOTONIC_RAW, &start_time);
}

void end_timer()
{
clock_gettime(CLOCK_MONOTONIC_RAW, &end_time);
}

__attribute_noinline__ uint64_t __benchmark_test_function3(const char *a, int b,
uint64_t c)
{
return a[b] + c;
}

uint64_t test_func_wrapper(const char *a, int b, uint64_t c)
{
struct pt_regs regs;
uint64_t ret;
if (enable_ebpf) {
memset(&regs, 0, sizeof(regs));
regs.di = (uintptr_t)a;
regs.si = b;
regs.dx = c;
ebpf_exec(begin_vm, &regs, sizeof(regs), &ret);
}
uint64_t hook_func_ret = __benchmark_test_function3(a, b, c);
if (enable_ebpf) {
memset(&regs, 0, sizeof(regs));
regs.di = hook_func_ret;
ebpf_exec(end_vm, &regs, sizeof(regs), &ret);
}
return hook_func_ret;
}

static double get_elapsed_time()
{
long seconds = end_time.tv_sec - start_time.tv_sec;
long nanoseconds = end_time.tv_nsec - start_time.tv_nsec;
if (start_time.tv_nsec > end_time.tv_nsec) { // clock underflow
--seconds;
nanoseconds += 1000000000;
}
printf("Elapsed time: %ld.%09ld seconds\n", seconds, nanoseconds);
return seconds * 1.0 + nanoseconds / 1000000000.0;
}

static double get_function_time(int iter)
{
start_timer();
// test base line
for (int i = 0; i < iter; i++) {
test_func_wrapper("hello", i % 4, i);
}
end_timer();
double time = get_elapsed_time();
return time;
}

void do_benchmark_userspace(int iter)
{
double base_line_time, after_hook_time, total_time;

printf("a[b] + c for %d times\n", iter);
base_line_time = get_function_time(iter);
printf("avg function elapse time: %lf ns\n\n",
(base_line_time) / iter * 1000000000.0);
}

struct ebpf_vm *create_vm_from_elf(const char *elf_file,
ebpf_jit_fn *compiled_fn)
{
LIBBPF_OPTS(bpf_object_open_opts, open_opts);
int err;
struct ebpf_vm *vm = NULL;
struct bpf_object *obj = bpf_object__open_file(elf_file, &open_opts);
if (!obj) {
fprintf(stderr, "Failed to open elf file, errno=%d\n", errno);
return NULL;
}
struct bpf_program *prog = bpf_object__next_program(obj, NULL);
if (!prog) {
fprintf(stderr, "No program found in %s\n", elf_file);
goto out;
}
vm = ebpf_create();
if (!vm) {
goto out;
}
char *errmsg;
err = ebpf_load(vm, bpf_program__insns(prog),
bpf_program__insn_cnt(prog) * 8, &errmsg);
if (err != 0) {
fprintf(stderr, "Failed to load program: %s\n", errmsg);
free(errmsg);
goto out;
}
if (compiled_fn) {
*compiled_fn = ebpf_compile(vm, &errmsg);
if (!*compiled_fn) {
fprintf(stderr, "Failed to compile: %s\n", errmsg);
free(errmsg);
goto err_out;
}
}
goto out;
err_out:
if (vm) {
ebpf_destroy(vm);
}

out:
if (obj)
bpf_object__close(obj);
return vm;
}

int main()
{
printf("uprobe elf: %s\nuretprobe elf:%s\n", uprobe_prog,
uretprobe_prog);
enable_ebpf = true;
begin_vm = create_vm_from_elf(uprobe_prog, NULL);
assert(begin_vm);
end_vm = create_vm_from_elf(uretprobe_prog, NULL);
assert(end_vm);
int iter = 100 * 1000;
do_benchmark_userspace(iter);
ebpf_destroy(begin_vm);
ebpf_destroy(end_vm);
return 0;
}
Binary file modified benchmark/uprobe/uprobe
Binary file not shown.
1 change: 0 additions & 1 deletion benchmark/uretprobe/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@ package.json
*.skel.yaml
package.yaml
ecli
uertprobe
.output
test
Binary file modified benchmark/uretprobe/uretprobe
Binary file not shown.
4 changes: 2 additions & 2 deletions benchmark/uretprobe/uretprobe.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@


SEC("uretprobe/benchmark/test:__benchmark_test_function3")
int BPF_URETPROBE(__benchmark_test_function, const char *a, int b, uint64_t c)
int BPF_URETPROBE(__benchmark_test_function, int ret)
{
return b + c;
return ret;
}

char LICENSE[] SEC("license") = "GPL";
63 changes: 63 additions & 0 deletions cmake/libbpf.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@

#
# Setup libbpf
#
set(LIBBPF_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/libbpf/)
include(ExternalProject)
ExternalProject_Add(libbpf
PREFIX libbpf
SOURCE_DIR ${LIBBPF_DIR}/src
CONFIGURE_COMMAND "mkdir" "-p" "${CMAKE_CURRENT_BINARY_DIR}/libbpf/libbpf"
BUILD_COMMAND "INCLUDEDIR=" "LIBDIR=" "UAPIDIR=" "OBJDIR=${CMAKE_CURRENT_BINARY_DIR}/libbpf/libbpf" "DESTDIR=${CMAKE_CURRENT_BINARY_DIR}/libbpf" "make" "CFLAGS=-g -O2 -Werror -Wall -std=gnu89 -fPIC -fvisibility=hidden -DSHARED -DCUSTOM_DEFINE=1" "-j" "install"
BUILD_IN_SOURCE TRUE
INSTALL_COMMAND ""
STEP_TARGETS build
BUILD_BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/libbpf/libbpf.a
)

# Set BpfObject input parameters -- note this is usually not necessary unless
# you're in a highly vendored environment (like libbpf-bootstrap)
set(LIBBPF_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/libbpf)
set(LIBBPF_LIBRARIES ${CMAKE_CURRENT_BINARY_DIR}/libbpf/libbpf.a)

#
add_custom_target(copy_headers ALL
COMMENT "Copying headers"
)

function(copy_header TARGET SRC_DIR TARGET_DIR)
file(GLOB_RECURSE FILES RELATIVE "${SRC_DIR}" "${SRC_DIR}/*")
message(STATUS "copying ${FILES} from ${SRC_DIR} to ${TARGET_DIR}")

foreach(file ${FILES})
get_filename_component(PARENT_DIR "${TARGET_DIR}/${file}" DIRECTORY)
add_custom_command(
TARGET ${TARGET}
PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory ${PARENT_DIR}
COMMAND ${CMAKE_COMMAND} -E copy
${SRC_DIR}/${file}
${TARGET_DIR}/${file}
COMMENT "Copying file ${HEADER_DIRS}/${file} to ${TARGET_DIR}/${file}"
BYPRODUCTS ${TARGET_DIR}/${file}
)
endforeach()
endfunction()

copy_header(copy_headers "${LIBBPF_DIR}/include/linux" "${LIBBPF_INCLUDE_DIRS}/linux")
copy_header(copy_headers "${LIBBPF_DIR}/include/uapi/linux" "${LIBBPF_INCLUDE_DIRS}/linux")

set(HEADER_FILES relo_core.h hashmap.h nlattr.h libbpf_internal.h)

foreach(file ${HEADER_FILES})
add_custom_command(
TARGET copy_headers
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${LIBBPF_DIR}/src/${file}
${LIBBPF_INCLUDE_DIRS}/bpf/${file}
COMMENT "Copying ${file}"
BYPRODUCTS ${LIBBPF_INCLUDE_DIRS}/bpf/${file}
)
endforeach()

add_dependencies(copy_headers libbpf)
Loading
Loading