| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567 |
- #include <iostream>
- #include <phpcpp.h>
- #include <fstream>
- #include "bcc_common.h"
- #include "bpf_module.h"
- #include "libbpf.h"
- #include "BPF.h"
- #include <csignal>
- #include <atomic>
- #include <sstream>
- #include <utility>
- #include <vector>
- 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<std::string> 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<PerfEvent *>(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_t>(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_t>(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<const bpf_insn *>(func_start),
- static_cast<int>(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>("bpf_new", {Php::ByVal("a", Php::Type::String)});
- extension.add<bpf_new2>("bpf_new2", {Php::ByVal("a", Php::Type::String)});
- Php::Class<EbpfExtension> 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<PerfEvent> 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;
- }
- }
|