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

Rewrite bpftime-cli in C++ #125

Merged
merged 8 commits into from
Dec 15, 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
12 changes: 3 additions & 9 deletions .github/workflows/test-examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,11 @@ jobs:
- name: Build and install runtime (with llvm-jit)
if: ${{matrix.enable_jit}}
run: |
CC=gcc-12 CXX=g++-12 make release-with-llvm-jit-without-cli -j
CC=gcc-12 CXX=g++-12 make release-with-llvm-jit -j
- name: Build and install runtime (without llvm-jit)
if: ${{!matrix.enable_jit}}
run: |
CC=gcc-12 CXX=g++-12 make release-without-cli -j
- name: Build and install CLI
run: |
cd tools/cli-rs
RUSTFLAGS="-C target-feature=+crt-static" cargo build --release --target x86_64-unknown-linux-gnu
mkdir -p ~/.bpftime
cp ./target/x86_64-unknown-linux-gnu/release/bpftime ~/.bpftime
CC=gcc-12 CXX=g++-12 make release -j
- name: Upload build results (without jit)
uses: actions/upload-artifact@v3
if: ${{!matrix.enable_jit}}
Expand Down Expand Up @@ -101,7 +95,7 @@ jobs:
syscall_trace: false
expected_str: "info"
- path: libbpf-tools/funclatency
executable: ./funclatency -- -i 1 ./victim:plus
executable: ./funclatency -i 1 ./victim:plus
victim: ./victim
syscall_trace: false
expected_str: ": 1"
Expand Down
14 changes: 3 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -47,32 +47,24 @@ unit-test: unit-test-daemon unit-test-runtime ## run catch2 unit tests
build: ## build the package
cmake -Bbuild -DBPFTIME_ENABLE_UNIT_TESTING=1
cmake --build build --config Debug
cd tools/cli-rs && cargo build

build-iouring: ## build the package
cmake -Bbuild -DBPFTIME_ENABLE_UNIT_TESTING=0 -DBPFTIME_ENABLE_IOURING_EXT=1 -DCMAKE_BUILD_TYPE:STRING=Release
cmake --build build --config Release
cd tools/cli-rs && cargo build

release-without-cli:
release:
cmake -Bbuild -DBPFTIME_ENABLE_UNIT_TESTING=0 \
-DCMAKE_BUILD_TYPE:STRING=Release \
-DBPFTIME_ENABLE_LTO=0
cmake --build build --config Release --target install

release: release-without-cli ## build the package
cd tools/cli-rs && cargo build --release

release-with-llvm-jit-without-cli: ## build the package, with llvm-jit
release-with-llvm-jit: ## build the package, with llvm-jit
cmake -Bbuild -DBPFTIME_ENABLE_UNIT_TESTING=0 \
-DCMAKE_BUILD_TYPE:STRING=Release \
-DBPFTIME_ENABLE_LTO=0 \
-DBPFTIME_LLVM_JIT=1
cmake --build build --config Release --target install

release-with-llvm-jit: release-with-llvm-jit-without-cli## build the package, with llvm-jit
cd tools/cli-rs && cargo build --release

build-vm: ## build only the core library
make -C vm build

Expand All @@ -86,4 +78,4 @@ clean: ## clean the project
make -C vm clean

install: release ## Invoke cmake to install..
cd tools/cli-rs && mkdir -p ~/.bpftime && cp ./target/release/bpftime ~/.bpftime

2 changes: 0 additions & 2 deletions documents/build-and-test.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ On Ubuntu 20.04, you may need to manually switch to gcc-12.
```bash
sudo apt-get install libelf-dev zlib1g-dev # Install dependencies
make release && make install # Build and install the runtime
cd tools/cli-rs && cargo build --release
mkdir -p ~/.bpftime && cp ./target/release/bpftime ~/.bpftime
export PATH=$PATH:~/.bpftime
```

Expand Down
1 change: 1 addition & 0 deletions tools/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
add_subdirectory(bpftimetool)
add_subdirectory(cli-cpp)
4 changes: 1 addition & 3 deletions tools/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ Use for inject dynamic lib of runtime into target process, a wrapper and `LD_PRE
## Install the cli tools an libraries

```bash
sudo apt-get install libelf-dev zlib1g-dev # Install dependencies
cd tools/cli-rs && cargo build --release
mkdir ~/.bpftime && cp ./target/release/bpftime ~/.bpftime
make install
export PATH=$PATH:~/.bpftime
```

Expand Down
23 changes: 23 additions & 0 deletions tools/cli-cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
add_executable(
bpftime-cli-cpp
main.cpp
)

set_target_properties(bpftime-cli-cpp PROPERTIES OUTPUT_NAME "bpftime")

include(FetchContent)
FetchContent_Declare(
argparse
GIT_REPOSITORY https://github.com/p-ranav/argparse.git
)
FetchContent_MakeAvailable(argparse)

target_include_directories(bpftime-cli-cpp PRIVATE ${FRIDA_CORE_INSTALL_DIR} ${SPDLOG_INCLUDE} ${argparse_INCLUDE})
target_link_libraries(bpftime-cli-cpp PRIVATE spdlog::spdlog ${FRIDA_CORE_INSTALL_DIR}/libfrida-core.a argparse)
set_property(TARGET bpftime-agent-transformer PROPERTY CXX_STANDARD 20)

target_compile_definitions(bpftime-cli-cpp PRIVATE _GNU_SOURCE)

add_dependencies(bpftime-cli-cpp spdlog::spdlog FridaCore argparse)

install(TARGETS bpftime-cli-cpp CONFIGURATIONS Release Debug DESTINATION ~/.bpftime)
246 changes: 246 additions & 0 deletions tools/cli-cpp/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
#include "spdlog/spdlog.h"
#include <cstdlib>
#include <cstring>
#include <frida-core.h>
#include <argparse/argparse.hpp>
#include <filesystem>
#include <stdexcept>
#include <string_view>
#include <unistd.h>
#include <vector>
#include <string>
#include <utility>
#include <tuple>
#include <sys/wait.h>

static bool str_starts_with(const char *main, const char *pat)
{
if (strstr(main, pat) == main)
return true;
return false;
}

static int run_command(const char *path, const std::vector<std::string> &argv,
const char *ld_preload, const char *agent_so)
{
int pid = fork();
if (pid == 0) {
std::string ld_preload_str("LD_PRELOAD=");
std::string agent_so_str("AGENT_SO=");
ld_preload_str += ld_preload;

if (agent_so) {
agent_so_str += agent_so;
}
std::vector<const char *> env_arr;
char **p = environ;
while (*p) {
env_arr.push_back(*p);
p++;
}
bool ld_preload_set = false, agent_so_set = false;
for (auto &s : env_arr) {
if (str_starts_with(s, "LD_PRELOAD=")) {
s = ld_preload_str.c_str();
ld_preload_set = true;
} else if (str_starts_with(s, "AGENT_SO=")) {
s = agent_so_str.c_str();
agent_so_set = true;
}
}
if (!ld_preload_set)
env_arr.push_back(ld_preload_str.c_str());
if (!agent_so_set)
env_arr.push_back(agent_so_str.c_str());

env_arr.push_back(nullptr);
std::vector<const char *> argv_arr;
argv_arr.push_back(path);
for (const auto &str : argv)
argv_arr.push_back(str.c_str());
argv_arr.push_back(nullptr);
execvpe(path, (char *const *)argv_arr.data(),
(char *const *)env_arr.data());
} else {
int status;
if (int cid = waitpid(pid, &status, 0); cid > 0) {
if (WIFEXITED(status)) {
int exit_code = WEXITSTATUS(status);
if (exit_code != 0) {
spdlog::error(
"Program exited abnormally: {}",
exit_code);
return 1;
}
}
}
}
return 1;
}
static int inject_by_frida(int pid, const char *inject_path, const char *arg)
{
spdlog::info("Injecting to {}", pid);
frida_init();
auto injector = frida_injector_new();
GError *err = nullptr;
auto id = frida_injector_inject_library_file_sync(injector, pid,
inject_path,
"bpftime_agent_main",
arg, nullptr, &err);
if (err) {
spdlog::error("Failed to inject: {}", err->message);
g_error_free(err);
frida_unref(injector);
frida_deinit();
return 1;
}
spdlog::info("Successfully injected. ID: {}", id);
frida_injector_close_sync(injector, nullptr, nullptr);
frida_unref(injector);
frida_deinit();
return 0;
}

static std::pair<std::string, std::vector<std::string> >
extract_path_and_args(const argparse::ArgumentParser &parser)
{
std::vector<std::string> items;
try {
items = parser.get<std::vector<std::string> >("COMMAND");
} catch (std::logic_error &err) {
std::cerr << parser;
exit(1);
}
std::string executable = items[0];
items.erase(items.begin());
return { executable, items };
}
int main(int argc, const char **argv)
{
argparse::ArgumentParser program(argv[0]);

if (auto home_env = getenv("HOME"); home_env) {
std::string default_location(home_env);
default_location += "/.bpftime";
program.add_argument("-i", "--install-location")
.help("Installing location of bpftime")
.default_value(default_location)
.required()
.nargs(1);
} else {
spdlog::warn(
"Unable to determine home directory. You must specify --install-location");
program.add_argument("-i", "--install-location")
.help("Installing location of bpftime")
.required()
.nargs(1);
}

program.add_argument("-d", "--dry-run")
.help("Run without commiting any modifications")
.flag();

argparse::ArgumentParser load_command("load");

load_command.add_description(
"Start an application with bpftime-server injected");
load_command.add_argument("COMMAND")
.help("Command to run")
.nargs(argparse::nargs_pattern::at_least_one)
.remaining();

argparse::ArgumentParser start_command("start");

start_command.add_description(
"Start an application with bpftime-agent injected");
start_command.add_argument("-s", "--enable-syscall-trace")
.help("Whether to enable syscall trace")
.flag();
start_command.add_argument("COMMAND")
.nargs(argparse::nargs_pattern::at_least_one)
.help("Command to run");

argparse::ArgumentParser attach_command("attach");

attach_command.add_description("Inject bpftime-agent to a certain pid");
attach_command.add_argument("-s", "--enable-syscall-trace")
.help("Whether to enable syscall trace")
.flag();
attach_command.add_argument("PID").scan<'i', int>();

program.add_subparser(load_command);
program.add_subparser(start_command);
program.add_subparser(attach_command);
try {
program.parse_args(argc, argv);
} catch (const std::exception &err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
std::exit(1);
}
if (!program) {
std::cerr << program;
std::exit(1);
}
std::filesystem::path install_path(program.get("install-location"));
if (program.is_subcommand_used("load")) {
auto so_path = install_path / "libbpftime-syscall-server.so";
if (!std::filesystem::exists(so_path)) {
spdlog::error("Library not found: {}", so_path.c_str());
return 1;
}
auto [executable_path, extra_args] =
extract_path_and_args(load_command);
return run_command(executable_path.c_str(), extra_args,
so_path.c_str(), nullptr);
} else if (program.is_subcommand_used("start")) {
auto agent_path = install_path / "libbpftime-agent.so";
if (!std::filesystem::exists(agent_path)) {
spdlog::error("Library not found: {}",
agent_path.c_str());
return 1;
}
auto [executable_path, extra_args] =
extract_path_and_args(start_command);
if (start_command.get<bool>("enable-syscall-trace")) {
auto transformer_path =
install_path /
"libbpftime-agent-transformer.so";
if (!std::filesystem::exists(transformer_path)) {
spdlog::error("Library not found: {}",
transformer_path.c_str());
return 1;
}

return run_command(executable_path.c_str(), extra_args,
transformer_path.c_str(),
agent_path.c_str());
} else {
return run_command(executable_path.c_str(), extra_args,
agent_path.c_str(), nullptr);
}
} else if (program.is_subcommand_used("attach")) {
auto agent_path = install_path / "libbpftime-agent.so";
if (!std::filesystem::exists(agent_path)) {
spdlog::error("Library not found: {}",
agent_path.c_str());
return 1;
}
auto pid = attach_command.get<int>("PID");
if (attach_command.get<bool>("enable-syscall-trace")) {
auto transformer_path =
install_path /
"libbpftime-agent-transformer.so";
if (!std::filesystem::exists(transformer_path)) {
spdlog::error("Library not found: {}",
transformer_path.c_str());
return 1;
}
return inject_by_frida(pid, transformer_path.c_str(),
agent_path.c_str());
} else {
return inject_by_frida(pid, agent_path.c_str(), "");
}
}
return 0;
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading