#include #include #include #include "bcc_common.h" #include "bpf_module.h" #include "libbpf.h" #include "BPF.h" #include #include #include #include #include constexpr const char *TRACE_PIPE_PATH = "/sys/kernel/debug/tracing/trace_pipe"; // Php::out << "Value of fn: " << fnn << std::endl; //static std::string cb_fn; const std::vector syscall_prefixes = { "sys_", "__x64_sys_", "__x32_compat_sys_", "__ia32_compat_sys_", "__arm64_sys_", "__s390x_sys_", "__s390_sys_", "__riscv_sys_" }; class PerfEvent : public Php::Base { private: std::string cb; public: ebpf::BPF *bpf; std::string event_name; PerfEvent(ebpf::BPF *bpf_instance, const std::string &event_name) : bpf(bpf_instance), event_name(event_name) {} virtual ~PerfEvent() = default; static void callbackfn(void *cookie, void *data, int data_size) { auto *instance = static_cast(cookie); if (!instance) return; Php::Value phpData((const char *) data, data_size); Php::call(instance->cb.c_str(), nullptr, phpData, data_size); } void php_open_perf_buffer(Php::Parameters ¶ms) { this->cb = params[0].stringValue(); auto res = this->bpf->open_perf_buffer(this->event_name, callbackfn, nullptr, this); if (!res.ok()) { throw Php::Exception("open_perf_buffer error:" + res.msg()); } } int perf_buffer_poll(int timeout_ms) { return bpf->poll_perf_buffer(this->event_name, timeout_ms); // perf_reader_event_read } }; class EbpfExtension : public Php::Base { private: ebpf::BPF bpf; Php::Object _perf_event; PerfEvent *_class_perf_event; static std::string add_prefix(const std::string &prefix, const std::string &name) { if (name.rfind(prefix, 0) != 0) { return prefix + name; } return name; } std::string fix_syscall_fnname(const std::string &name) { for (const auto &prefix: syscall_prefixes) { if (name.rfind(prefix, 0) == 0) { return bpf.get_syscall_fnname(name.substr(prefix.length())); } } return name; } void _trace_autoload() { size_t num_funcs = bpf.get_num_functions(); for (size_t i = 0; i < num_funcs; i++) { const char *func_name = bpf.get_function_name(i); std::string fn_name(func_name); if (fn_name.rfind("kprobe__", 0) == 0) { std::string kernel_func = fix_syscall_fnname(fn_name.substr(8)); bpf.attach_kprobe(kernel_func, fn_name); } else if (fn_name.rfind("kretprobe__", 0) == 0) { std::string kernel_func = fix_syscall_fnname(fn_name.substr(11)); bpf.attach_kprobe(kernel_func, fn_name, 0, BPF_PROBE_RETURN); } else if (fn_name.rfind("tracepoint__", 0) == 0) { std::string tp_name = fn_name.substr(12); std::replace(tp_name.begin(), tp_name.end(), '_', ':'); bpf.attach_tracepoint(tp_name, fn_name); } else if (fn_name.rfind("raw_tracepoint__", 0) == 0) { std::string tp_name = fn_name.substr(16); bpf.attach_raw_tracepoint(tp_name, fn_name); } else if (fn_name.rfind("kfunc__", 0) == 0) { fn_name = add_prefix("kfunc__", fn_name); attach_kfunc(fn_name); } else if (fn_name.rfind("kretfunc__", 0) == 0) { fn_name = add_prefix("kretfunc__", fn_name); attach_kfunc(fn_name); } else if (fn_name.rfind("lsm__", 0) == 0) { fn_name = add_prefix("lsm__", fn_name); attach_lsm(fn_name); } } } public: EbpfExtension() = default; virtual ~EbpfExtension() = default; // Php::Value __get(const Php::Value &name) // { // if (name == "events") return _perf_event; // // auto a = new PerfEvent; // a->getMessage(); // // return Php::Base::__get(name); // } void __construct(Php::Parameters ¶ms) { std::string bpf_code = params[0].stringValue(); auto init_res = bpf.init(bpf_code); if (!init_res.ok()) { throw Php::Exception("init error: " + init_res.msg()); } _trace_autoload(); } ebpf::StatusTuple attach_kfunc(const std::string &kfn) { int probe_fd; auto fn = bpf.load_func(kfn, BPF_PROG_TYPE_TRACING, probe_fd); int res_fd = bpf_attach_kfunc(probe_fd); if (res_fd < 0) { TRY2(bpf.unload_func(kfn)); return ebpf::StatusTuple(-1, "Unable to attach kfunc using %s", kfn.c_str()); } return ebpf::StatusTuple::OK(); } ebpf::StatusTuple attach_lsm(const std::string &lsm) { int probe_fd; auto fn = bpf.load_func(lsm, BPF_PROG_TYPE_LSM, probe_fd); int res_fd = bpf_attach_lsm(probe_fd); if (res_fd < 0) { TRY2(bpf.unload_func(lsm)); return ebpf::StatusTuple(-1, "Unable to attach lsm using %s", lsm.c_str()); } return ebpf::StatusTuple::OK(); } void php_attach_kprobe(Php::Parameters ¶ms) { std::string kernel_func = params[0].stringValue(); std::string probe_func = params[1].stringValue(); auto attach_res = bpf.attach_kprobe(kernel_func, probe_func); if (!attach_res.ok()) { throw Php::Exception("attach error: " + attach_res.msg()); } } void php_attach_tracepoint(Php::Parameters ¶ms) { std::string tp_func = params[0].stringValue(); std::string probe_func = params[1].stringValue(); auto attach_res = bpf.attach_tracepoint(tp_func, probe_func); if (!attach_res.ok()) { throw Php::Exception("attach error: " + attach_res.msg()); } } void php_attach_raw_tracepoint(Php::Parameters ¶ms) { std::string tp_func = params[0].stringValue(); std::string probe_func = params[1].stringValue(); auto attach_res = bpf.attach_raw_tracepoint(tp_func, probe_func); if (!attach_res.ok()) { throw Php::Exception("attach error: " + attach_res.msg()); } } void php_attach_kfunc(Php::Parameters ¶ms) { std::string kfunc = params[0].stringValue(); auto attach_res = attach_kfunc(kfunc); if (!attach_res.ok()) { throw Php::Exception("attach error: " + attach_res.msg()); } } void php_attach_lsm(Php::Parameters ¶ms) { std::string kfunc = params[0].stringValue(); auto attach_res = attach_lsm(kfunc); if (!attach_res.ok()) { throw Php::Exception("attach error: " + attach_res.msg()); } } void php_attach_uprobe(Php::Parameters ¶ms) { std::string binary_path = params[0].stringValue(); std::string symbol = params[1].stringValue(); std::string probe_func = params[2].stringValue(); int64_t symbol_addr = 0, symbol_offset = 0, pid_param; uint32_t ref_ctr_offset = 0; pid_t pid = -1; if (params.size() > 3) { Php::Value options = params[3]; symbol_addr = options.get("symbol_addr").numericValue(); symbol_offset = options.get("symbol_offset").numericValue(); ref_ctr_offset = options.get("ref_ctr_offset").numericValue(); pid_param = options.get("pid").numericValue(); if (pid_param > 0) { pid = static_cast(pid_param); } } auto attach_res = bpf.attach_uprobe(binary_path, symbol, probe_func, symbol_addr, BPF_PROBE_ENTRY, pid, symbol_offset, ref_ctr_offset); if (!attach_res.ok()) { throw Php::Exception("attach error: " + attach_res.msg()); } } void php_detach_kprobe(Php::Parameters ¶ms) { std::string fn = params[0].stringValue(); auto detach_res = bpf.detach_kprobe(fn); if (!detach_res.ok()) { throw Php::Exception("detach_kprobe error: " + detach_res.msg()); } } void php_detach_uprobe(Php::Parameters ¶ms) { std::string binary_path = params[0].stringValue(); std::string symbol = params[1].stringValue(); int64_t symbol_addr = 0, symbol_offset = 0, pid_param; pid_t pid = -1; if (params.size() > 2) { Php::Value options = params[2]; symbol_addr = options.get("symbol_addr").numericValue(); symbol_offset = options.get("symbol_offset").numericValue(); pid_param = options.get("pid").numericValue(); if (pid_param > 0) { pid = static_cast(pid_param); } } auto detach_res = bpf.detach_uprobe(binary_path, symbol, symbol_addr, BPF_PROBE_ENTRY, pid, symbol_offset); if (!detach_res.ok()) { throw Php::Exception("detach_kprobe error: " + detach_res.msg()); } } [[noreturn]] static void php_trace_print() { std::ifstream pipe(TRACE_PIPE_PATH); std::string line; if (!pipe.is_open()) { throw Php::Exception("Failed to open trace_pipe"); } std::cout << "Press Ctrl+C to stop..." << std::endl; while (true) { if (std::getline(pipe, line)) { std::cout << line << std::endl; } } } static Php::Value php_trace_fields(Php::Parameters ¶ms) { std::ifstream traceFile(TRACE_PIPE_PATH); if (!traceFile.is_open()) { throw Php::Exception("Failed to open trace_pipe"); } std::string line; while (std::getline(traceFile, line)) { if (line.empty()) { continue; } if (line.rfind("CPU:", 0) == 0) { continue; } std::string task = line.substr(0, 16); task.erase(0, task.find_first_not_of(" ")); std::istringstream iss(line.substr(17)); std::string pid, cpu, flags, ts, msg; char delim; if (!(iss >> pid >> delim >> cpu >> delim >> flags >> ts)) { continue; } size_t sym_end = iss.str().find(": ", iss.tellg()); if (sym_end != std::string::npos) { msg = iss.str().substr(sym_end + 2); } Php::Array result; result[0] = task; result[1] = std::stoi(pid); result[2] = std::stoi(cpu.substr(1, cpu.size() - 2)); // 去掉方括号 result[3] = flags; result[4] = std::stod(ts); result[5] = msg; return result; } return Php::Value(); } Php::Value php_perf_event(Php::Parameters ¶ms) { std::string event_name = params[0].stringValue(); // Get event_name from the parameters this->_class_perf_event = new PerfEvent(&this->bpf, event_name); return Php::Object("PerfEvent", this->_class_perf_event); } void php_perf_buffer_poll(Php::Parameters ¶ms) { if (!this->_class_perf_event) { throw Php::Exception("perf event is null."); } int timeout_ms = -1; int res = this->_class_perf_event->perf_buffer_poll(timeout_ms); if (res < 0) { throw Php::Exception("perf buffer poll error."); } } Php::Value php_get_syscall_fnname(Php::Parameters ¶ms) { std::string name = params[0].stringValue(); return bpf.get_syscall_fnname(name); } }; std::string sanitize_str(std::string str, bool (*validator)(char), char replacement = '_') { for (size_t i = 0; i < str.length(); i++) if (!validator(str[i])) str[i] = replacement; return str; } std::string attach_type_prefix(bpf_probe_attach_type type) { switch (type) { case BPF_PROBE_ENTRY: return "p"; case BPF_PROBE_RETURN: return "r"; } return "ERROR"; } static bool kprobe_event_validator(char c) { return (c != '+') && (c != '.'); } std::string get_kprobe_event(const std::string &kernel_func, bpf_probe_attach_type type) { std::string res = attach_type_prefix(type) + "_"; res += sanitize_str(kernel_func, &kprobe_event_validator); return res; } void bpf_new2(Php::Parameters ¶ms) { std::string param = params[0].stringValue(); ebpf::BPF bpf; // void *mod = bpf_module_create_c_from_string(param.c_str(), 4, NULL, 0, true, NULL); std::string kernel_func = bpf.get_syscall_fnname("clone"); bpf.init(param); const char *prob_fn = "kprobe__sys_clone"; auto attach_res = bpf.attach_kprobe(kernel_func, prob_fn); Php::out << "Value of fn:kernel_func]" << kernel_func.c_str() << std::endl; Php::out << "Value of fn:prob_fn]" << prob_fn << std::endl; std::ifstream pipe("/sys/kernel/debug/tracing/trace_pipe"); std::string line; if (!attach_res.ok()) { std::cerr << attach_res.msg() << std::endl; } else { std::cout << "Press Ctrl+C to stop..." << std::endl; while (true) { if (std::getline(pipe, line)) { std::cout << line << std::endl; } } } } void bpf_new(Php::Parameters ¶ms) { std::string param = params[0].stringValue(); void *mod = bpf_module_create_c_from_string(param.c_str(), 4, NULL, 0, true, NULL); // void *mod = bpf_module_create_c_from_string("BPF_TABLE(\"array\", int, int, stats, 10);\n", 4, NULL, 0, true, NULL); // bpf_prog_get_fd_by_id() ; Php::out << "Value of mod: " << mod << std::endl; // size_t num = bpf_num_functions(mod); // for (size_t i = 0; i < num; ++i) { // const char *fnn = bpf_function_name(mod, i); // Php::out << "Value of fn: " << fnn << std::endl; // } // Php::out << "Value of bpf_num_functions: " << num << std::endl; ebpf::BPF bpf; std::string kernel_func = bpf.get_syscall_fnname("clone"); const char *prob_fn = "hello"; void *func_start = bpf_function_start(mod, prob_fn); if (!func_start) { Php::out << "can not find of func_start: " << func_start << std::endl; } int log_level = 1; std::cout << "func_load ]" << prob_fn << std::endl; std::cout << "func_load func_start]" << func_start << std::endl; std::cout << "func_load func_size]" << bpf_function_size(mod, prob_fn) << std::endl; std::cout << "func_load license]" << bpf_module_license(mod) << std::endl; std::cout << "func_load kern_version]" << bpf_module_kern_version(mod) << std::endl; int fn_fd = bcc_func_load(mod, BPF_PROG_TYPE_KPROBE, prob_fn, static_cast(func_start), static_cast(bpf_function_size(mod, prob_fn)), bpf_module_license(mod), bpf_module_kern_version(mod), log_level, nullptr, 0, 0, -1); if (fn_fd < 0) { Php::out << "Failed to load BPF program " << fn_fd << std::endl; } else { Php::out << "BPF program fd ]" << fn_fd << std::endl; } std::string probe_event = get_kprobe_event(kernel_func, BPF_PROBE_ENTRY); std::cout << "kernel_func]" << kernel_func.c_str() << "! probe_event]" << probe_event.c_str() << std::endl; int kernel_func_offset = 0; // bpf_attach_kprobe(fn_fd, BPF_PROBE_ENTRY, probe_event.c_str(), fn, fn_offset, 0); int attach_res2 = bpf_attach_kprobe(fn_fd, BPF_PROBE_ENTRY, probe_event.c_str(), kernel_func.c_str(), kernel_func_offset, 0); std::cout << "main.cpp attach_res2---------->]" << attach_res2 << std::endl; // Php::out << "Value of clone_fnname: " << kernel_func << std::endl; std::ifstream pipe("/sys/kernel/debug/tracing/trace_pipe"); std::string line; // // bpf.init(param); auto attach_res = bpf.attach_kprobe(kernel_func, prob_fn); std::cout << "main.cpp attach_res2-------->]" << attach_res.ok() << std::endl; if (!attach_res.ok() && attach_res2 <= 0) { std::cerr << attach_res.msg() << std::endl; } else { std::cout << "Starting HelloWorld with BCC " << 111 << std::endl; while (true) { if (std::getline(pipe, line)) { std::cout << "aaa Waiting for a sys_clone event" << std::endl; std::cout << line << std::endl; // Detach the probe if we got at least one line. auto detach_res = bpf.detach_kprobe(kernel_func); if (!detach_res.ok()) { std::cerr << detach_res.msg() << std::endl; } break; } else { std::cout << "Waiting for a sys_clone event" << std::endl; sleep(1); } } } } extern "C" { PHPCPP_EXPORT void *get_module() { static Php::Extension extension("ebpf", "1.0.0"); extension.add("bpf_new", {Php::ByVal("a", Php::Type::String)}); extension.add("bpf_new2", {Php::ByVal("a", Php::Type::String)}); Php::Class ebpf_class("Ebpf"); ebpf_class.method<&EbpfExtension::php_trace_print>("trace_print"); ebpf_class.method<&EbpfExtension::php_trace_fields>("trace_fields"); ebpf_class.method<&EbpfExtension::__construct>("__construct", { Php::ByVal("bpf_code", Php::Type::String) }); ebpf_class.method<&EbpfExtension::php_attach_kprobe>("attach_kprobe", { Php::ByVal("kernel_func", Php::Type::String), Php::ByVal("probe_func", Php::Type::String) }); ebpf_class.method<&EbpfExtension::php_attach_tracepoint>("attach_tracepoint", { Php::ByVal("tp", Php::Type::String), Php::ByVal("probe_func", Php::Type::String) }); ebpf_class.method<&EbpfExtension::php_attach_raw_tracepoint>("attach_raw_tracepoint", { Php::ByVal("tp", Php::Type::String), Php::ByVal("probe_func", Php::Type::String) }); ebpf_class.method<&EbpfExtension::php_attach_kfunc>("attach_kfunc", { Php::ByVal("kfn", Php::Type::String), }); ebpf_class.method<&EbpfExtension::php_attach_lsm>("attach_lsm", { Php::ByVal("lsm", Php::Type::String), }); ebpf_class.method<&EbpfExtension::php_attach_uprobe>("attach_uprobe", { Php::ByVal("binary_path", Php::Type::String), Php::ByVal("symbol", Php::Type::String), Php::ByVal("probe_func", Php::Type::String), Php::ByVal("options", Php::Type::Array, false), }); // ebpf_class.method<&EbpfExtension::php_open_perf_buffer>("open_perf_buffer", { // Php::ByVal("callback", Php::Type::Callable), // }); ebpf_class.method<&EbpfExtension::php_perf_event>("perf_event", { Php::ByVal("ev_name", Php::Type::String), }); ebpf_class.method<&EbpfExtension::php_perf_buffer_poll>("perf_buffer_poll", {}); ebpf_class.method<&EbpfExtension::php_get_syscall_fnname>("get_syscall_fnname", { Php::ByVal("name", Php::Type::String), }); /*detach*/ ebpf_class.method<&EbpfExtension::php_detach_kprobe>("detach_kprobe", { Php::ByVal("kernel_func", Php::Type::String), }); ebpf_class.method<&EbpfExtension::php_detach_uprobe>("detach_uprobe", { Php::ByVal("binary_path", Php::Type::String), Php::ByVal("symbol", Php::Type::String), Php::ByVal("options", Php::Type::Array, false), }); Php::Class ebpf_perf_event_class("PerfEvent"); ebpf_perf_event_class.method<&PerfEvent::php_open_perf_buffer>("open_perf_buffer", { Php::ByVal("callback", Php::Type::Callable), }); extension.add(std::move(ebpf_class)); extension.add(std::move(ebpf_perf_event_class)); return extension; } }