|
|
@@ -0,0 +1,748 @@
|
|
|
+/*
|
|
|
+ * Copyright 2024 carl.guo <[email protected]>
|
|
|
+ *
|
|
|
+ * 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
|
|
|
+ *
|
|
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
+ *
|
|
|
+ * 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.
|
|
|
+ */
|
|
|
+
|
|
|
+#include "php_ebpf.h"
|
|
|
+#include "libbpf.h"
|
|
|
+#include "bpf_module.h"
|
|
|
+#include "bcc_common.h"
|
|
|
+#include "table.h"
|
|
|
+#include "function.h"
|
|
|
+
|
|
|
+
|
|
|
+void EbpfExtension::_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);
|
|
|
+ size_t sep = tp_name.find("__");
|
|
|
+ if (sep != std::string::npos) {
|
|
|
+ tp_name.replace(sep, 2, ":");
|
|
|
+ }
|
|
|
+ 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);
|
|
|
+ this->attach_kfunc(fn_name);
|
|
|
+ } else if (fn_name.rfind("lsm__", 0) == 0) {
|
|
|
+ fn_name = add_prefix("lsm__", fn_name);
|
|
|
+ this->attach_lsm(fn_name);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+std::string EbpfExtension::add_prefix(const std::string &prefix, const std::string &name) {
|
|
|
+ if (name.rfind(prefix, 0) != 0) {
|
|
|
+ return prefix + name;
|
|
|
+ }
|
|
|
+ return name;
|
|
|
+}
|
|
|
+
|
|
|
+std::string EbpfExtension::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;
|
|
|
+}
|
|
|
+
|
|
|
+Php::Value EbpfExtension::__get(const Php::Value &name) {
|
|
|
+ int from_attr = 1;
|
|
|
+ auto res = get_table_cls(name, from_attr);
|
|
|
+ if (!res.isObject()) {
|
|
|
+ return Php::Base::__get(name);
|
|
|
+ } else {
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+Php::Value EbpfExtension::get_table_cls(const char *table_name, int from_attr) {
|
|
|
+ int ttype = bpf_table_type(this->mod, table_name);
|
|
|
+ switch (ttype) {
|
|
|
+ case BPF_MAP_TYPE_HASH:
|
|
|
+ return Php::Object("HashTable", new HashTable(&this->bpf, table_name));
|
|
|
+ case BPF_MAP_TYPE_ARRAY:
|
|
|
+ return Php::Object("ArrayTable", new ArrayTable(&this->bpf, table_name));
|
|
|
+ case BPF_MAP_TYPE_PROG_ARRAY:
|
|
|
+ return Php::Object("ProgArrayTable", new ProgArrayTable(&this->bpf, table_name));
|
|
|
+ case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
|
|
|
+ if (_class_perf_event_obj.isNull()) {
|
|
|
+ _class_perf_event_obj = Php::Object("PerfEventArrayTable",
|
|
|
+ new PerfEventArrayTable(&this->bpf, table_name));
|
|
|
+ }
|
|
|
+ return _class_perf_event_obj;
|
|
|
+ case BPF_MAP_TYPE_PERCPU_HASH:
|
|
|
+ return Php::Object("PerCpuHashTable", new PerCpuHashTable(&this->bpf, table_name));
|
|
|
+ case BPF_MAP_TYPE_PERCPU_ARRAY:
|
|
|
+ return Php::Object("PerCpuArrayTable", new PerCpuArrayTable(&this->bpf, table_name));
|
|
|
+ case BPF_MAP_TYPE_LPM_TRIE:
|
|
|
+ return Php::Object("LpmTrieTable", new LpmTrieTable(&this->bpf, table_name));
|
|
|
+ case BPF_MAP_TYPE_STACK_TRACE:
|
|
|
+ return Php::Object("StackTraceTable", new StackTraceTable(&this->bpf, table_name));
|
|
|
+ case BPF_MAP_TYPE_LRU_HASH:
|
|
|
+ return Php::Object("LruHashTable", new LruHashTable(&this->bpf, table_name));
|
|
|
+ case BPF_MAP_TYPE_LRU_PERCPU_HASH:
|
|
|
+ return Php::Object("LruPerCpuHashTable", new LruPerCpuHashTable(&this->bpf, table_name));
|
|
|
+ case BPF_MAP_TYPE_CGROUP_ARRAY:
|
|
|
+ return Php::Object("CgroupArrayTable", new CgroupArrayTable(&this->bpf, table_name));
|
|
|
+ case BPF_MAP_TYPE_DEVMAP:
|
|
|
+ return Php::Object("DevMapTable", new DevMapTable(&this->bpf, table_name));
|
|
|
+ case BPF_MAP_TYPE_CPUMAP:
|
|
|
+ return Php::Object("CpuMapTable", new CpuMapTable(&this->bpf, table_name));
|
|
|
+ case BPF_MAP_TYPE_XSKMAP:
|
|
|
+ return Php::Object("XskMapTable", new XskMapTable(&this->bpf, table_name));
|
|
|
+ case BPF_MAP_TYPE_ARRAY_OF_MAPS:
|
|
|
+ return Php::Object("MapInMapArrayTable", new MapInMapArrayTable(&this->bpf, table_name));
|
|
|
+ case BPF_MAP_TYPE_HASH_OF_MAPS:
|
|
|
+ return Php::Object("MapInMapHashTable", new MapInMapHashTable(&this->bpf, table_name));
|
|
|
+ case BPF_MAP_TYPE_QUEUE:
|
|
|
+ case BPF_MAP_TYPE_STACK:
|
|
|
+ return Php::Object("QueueStackTable", new QueueStackTable(&this->bpf, table_name));
|
|
|
+ case BPF_MAP_TYPE_RINGBUF:
|
|
|
+ return Php::Object("RingBufTable", new RingBufTable(&this->bpf, table_name));
|
|
|
+ default:
|
|
|
+ if (from_attr) {
|
|
|
+ return ttype;
|
|
|
+ }
|
|
|
+ throw Php::Exception("Unknown table type " + std::to_string(ttype));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+std::unordered_set<std::string> EbpfExtension::get_kprobe_functions(const std::string &event_re) {
|
|
|
+ std::unordered_set<std::string> blacklist;
|
|
|
+ std::unordered_set<std::string> avail_filter;
|
|
|
+ std::unordered_set<std::string> fns;
|
|
|
+
|
|
|
+ std::string blacklist_file = std::string(DEBUGFS) + "/kprobes/blacklist";
|
|
|
+ std::ifstream blacklist_f(blacklist_file);
|
|
|
+ if (blacklist_f.is_open()) {
|
|
|
+ std::string line;
|
|
|
+ while (std::getline(blacklist_f, line)) {
|
|
|
+ std::istringstream iss(line);
|
|
|
+ std::string addr, func_name;
|
|
|
+ if (iss >> addr >> func_name) {
|
|
|
+ blacklist.insert(func_name);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ blacklist_f.close();
|
|
|
+ }
|
|
|
+
|
|
|
+ std::string avail_filter_file = std::string(DEBUGFS) + "/tracing/available_filter_functions";
|
|
|
+ std::ifstream avail_filter_f(avail_filter_file);
|
|
|
+ if (avail_filter_f.is_open()) {
|
|
|
+ std::string line;
|
|
|
+ while (std::getline(avail_filter_f, line)) {
|
|
|
+ std::istringstream iss(line);
|
|
|
+ std::string func_name;
|
|
|
+ if (iss >> func_name) {
|
|
|
+ avail_filter.insert(func_name);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ avail_filter_f.close();
|
|
|
+ }
|
|
|
+
|
|
|
+ std::ifstream kallsyms_f("/proc/kallsyms");
|
|
|
+ if (!kallsyms_f.is_open()) {
|
|
|
+ std::cerr << "Failed to open /proc/kallsyms\n";
|
|
|
+ return fns;
|
|
|
+ }
|
|
|
+
|
|
|
+ std::string line;
|
|
|
+ bool in_init_section = false;
|
|
|
+ bool in_irq_section = false;
|
|
|
+ std::regex cold_regex(".*\\.cold(\\.\\d+)?$");
|
|
|
+
|
|
|
+ while (std::getline(kallsyms_f, line)) {
|
|
|
+ std::istringstream iss(line);
|
|
|
+ std::string addr, type, func_name;
|
|
|
+ if (!(iss >> addr >> type >> func_name)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!in_init_section) {
|
|
|
+ if (func_name == "__init_begin") {
|
|
|
+ in_init_section = true;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ } else if (func_name == "__init_end") {
|
|
|
+ in_init_section = false;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!in_irq_section) {
|
|
|
+ if (func_name == "__irqentry_text_start") {
|
|
|
+ in_irq_section = true;
|
|
|
+ continue;
|
|
|
+ } else if (func_name == "__irqentry_text_end") {
|
|
|
+ in_irq_section = false;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ } else if (func_name == "__irqentry_text_end") {
|
|
|
+ in_irq_section = false;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (func_name.rfind("_kbl_addr_", 0) == 0) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (func_name.rfind("__perf", 0) == 0 || func_name.rfind("perf_", 0) == 0) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (func_name.rfind("__SCT__", 0) == 0) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (std::regex_match(func_name, cold_regex)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((type == "t" || type == "T" || type == "w" || type == "W") &&
|
|
|
+ func_name == event_re &&
|
|
|
+ blacklist.find(func_name) == blacklist.end() &&
|
|
|
+ avail_filter.find(func_name) != avail_filter.end()) {
|
|
|
+ fns.insert(func_name);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return fns;
|
|
|
+}
|
|
|
+
|
|
|
+ebpf::StatusTuple EbpfExtension::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 EbpfExtension::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();
|
|
|
+}
|
|
|
+
|
|
|
+EBPF_PHP_METHOD(EbpfExtension, __construct, void) {
|
|
|
+ std::string source;
|
|
|
+ if (params.size() == 1 && params[0].isArray()) {
|
|
|
+ Php::Array opts = params[0];
|
|
|
+ if (opts.contains("text")) {
|
|
|
+ source = opts.get("text").stringValue();
|
|
|
+ } else if (opts.contains("src_file")) {
|
|
|
+ std::string path = opts.get("src_file");
|
|
|
+ std::ifstream f(path);
|
|
|
+ if (!f) {
|
|
|
+ throw Php::Exception("Failed to open BPF source file: " + path);
|
|
|
+ }
|
|
|
+ std::stringstream buffer;
|
|
|
+ buffer << f.rdbuf();
|
|
|
+ source = buffer.str();
|
|
|
+ } else {
|
|
|
+ throw Php::Exception("Ebpf expects either 'text' or 'src_file' as options.");
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ throw Php::Exception("Ebpf constructor requires an array with 'text' or 'src_file'.");
|
|
|
+ }
|
|
|
+
|
|
|
+ auto init_res = bpf.init(source);
|
|
|
+ if (!init_res.ok()) {
|
|
|
+ throw Php::Exception("init error: " + init_res.msg());
|
|
|
+ }
|
|
|
+ this->mod = (void *) bpf.get_mod();
|
|
|
+ _trace_autoload();
|
|
|
+}
|
|
|
+
|
|
|
+EBPF_PHP_METHOD(EbpfExtension, get_kprobe_functions, Php::Value) {
|
|
|
+ std::string fn = params[0].stringValue();
|
|
|
+ auto res = get_kprobe_functions(fn);
|
|
|
+ Php::Array result;
|
|
|
+ for (const auto &item: res) {
|
|
|
+ result[result.size()] = item;
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+EBPF_PHP_METHOD(EbpfExtension, attach_kprobe, void) {
|
|
|
+ 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());
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+EBPF_PHP_METHOD(EbpfExtension, attach_tracepoint, void) {
|
|
|
+ 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());
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+EBPF_PHP_METHOD(EbpfExtension, attach_raw_tracepoint, void) {
|
|
|
+ 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());
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+EBPF_PHP_METHOD(EbpfExtension, attach_kfunc, void) {
|
|
|
+ std::string kfunc = params[0].stringValue();
|
|
|
+ auto attach_res = attach_kfunc(kfunc);
|
|
|
+ if (!attach_res.ok()) {
|
|
|
+ throw Php::Exception("attach error: " + attach_res.msg());
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+EBPF_PHP_METHOD(EbpfExtension, attach_lsm, void) {
|
|
|
+ std::string kfunc = params[0].stringValue();
|
|
|
+ auto attach_res = attach_lsm(kfunc);
|
|
|
+ if (!attach_res.ok()) {
|
|
|
+ throw Php::Exception("attach error: " + attach_res.msg());
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+EBPF_PHP_METHOD(EbpfExtension, attach_uprobe, void) {
|
|
|
+ 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());
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+EBPF_PHP_METHOD(EbpfExtension, detach_kprobe, void) {
|
|
|
+ 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());
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+EBPF_PHP_METHOD(EbpfExtension, detach_uprobe, void) {
|
|
|
+ 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());
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+EBPF_PHP_METHOD(EbpfExtension, trace_print, void) {
|
|
|
+ std::ifstream pipe(TRACE_PIPE_PATH);
|
|
|
+ if (!pipe.is_open()) {
|
|
|
+ throw Php::Exception("Failed to open trace_pipe");
|
|
|
+ }
|
|
|
+
|
|
|
+ std::string fmt;
|
|
|
+ if (params.size() > 0) {
|
|
|
+ fmt = params[0].stringValue();
|
|
|
+ }
|
|
|
+
|
|
|
+ std::string line;
|
|
|
+ while (true) {
|
|
|
+ if (!std::getline(pipe, line)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (line.empty() || 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);
|
|
|
+ }
|
|
|
+
|
|
|
+ std::vector<std::string> fields = {task, pid, cpu, flags, ts, msg};
|
|
|
+ if (fmt.empty()) {
|
|
|
+ std::cout << line << std::endl;
|
|
|
+ } else {
|
|
|
+ std::string output = fmt;
|
|
|
+ std::regex pattern(R"(\{(\d+)\})");
|
|
|
+ std::smatch match;
|
|
|
+ while (std::regex_search(output, match, pattern)) {
|
|
|
+ int index = std::stoi(match[1]);
|
|
|
+ std::string replacement = (index >= 0 && index < (int) fields.size()) ? fields[index] : "";
|
|
|
+ output.replace(match.position(0), match.length(0), replacement);
|
|
|
+ }
|
|
|
+ std::cout << output << std::endl;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+EBPF_PHP_METHOD(EbpfExtension, trace_fields, Php::Value) {
|
|
|
+ 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] = Php::Value(msg.c_str(), msg.size());
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ return Php::Value();
|
|
|
+}
|
|
|
+
|
|
|
+EBPF_PHP_METHOD(EbpfExtension, get_table, Php::Value) {
|
|
|
+ std::string table_name = params[0].stringValue();
|
|
|
+ int from_fn = 0;
|
|
|
+ return get_table_cls(table_name.c_str(), from_fn);
|
|
|
+}
|
|
|
+
|
|
|
+EBPF_PHP_METHOD(EbpfExtension, perf_buffer_poll, void) {
|
|
|
+ if (_class_perf_event_obj.isNull()) {
|
|
|
+ throw Php::Exception("perf event is null.");
|
|
|
+ }
|
|
|
+
|
|
|
+ auto *_class_perf_event = (PerfEventArrayTable *) _class_perf_event_obj.implementation();
|
|
|
+ if (!_class_perf_event) {
|
|
|
+ throw Php::Exception("perf buffer poll error1.");
|
|
|
+ }
|
|
|
+ int timeout_ms = -1;
|
|
|
+ int res = _class_perf_event->perf_buffer_poll(timeout_ms);
|
|
|
+ if (res < 0) {
|
|
|
+ throw Php::Exception("perf buffer poll error.");
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+EBPF_PHP_METHOD(EbpfExtension, get_syscall_fnname, Php::Value) {
|
|
|
+ std::string name = params[0].stringValue();
|
|
|
+ return bpf.get_syscall_fnname(name);
|
|
|
+}
|
|
|
+
|
|
|
+EBPF_PHP_METHOD(EbpfExtension, load_func, Php::Value) {
|
|
|
+ std::string fn = params[0].stringValue();
|
|
|
+ auto prog_type = static_cast<bpf_prog_type>(params[1].numericValue());
|
|
|
+ int probe_fd;
|
|
|
+ auto res = bpf.load_func(fn, prog_type, probe_fd);
|
|
|
+ if (res.ok()) {
|
|
|
+ Php::Object prog_fn = Php::Object("BPFProgFunction", new ProgFunc(&this->bpf, fn, probe_fd));
|
|
|
+ prog_fn.set("name", fn);
|
|
|
+ prog_fn.set("fd", probe_fd);
|
|
|
+ return prog_fn;
|
|
|
+ } else {
|
|
|
+ throw Php::Exception(res.msg());
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+EBPF_PHP_METHOD(EbpfExtension, attach_raw_socket, void) {
|
|
|
+ if (!params[0].isObject()) {
|
|
|
+ throw Php::Exception("First parameter must be a BPFProgFunction object.");
|
|
|
+ }
|
|
|
+ Php::Object prog_fn = params[0];
|
|
|
+ std::string interface = params[1].stringValue();
|
|
|
+ auto *prog = static_cast<ProgFunc *>(prog_fn.implementation());
|
|
|
+ if (prog == nullptr) {
|
|
|
+ throw Php::Exception("Invalid BPFProgFunction object.");
|
|
|
+ }
|
|
|
+ int sock = bpf_open_raw_sock(interface.c_str());
|
|
|
+ if (sock < 0) {
|
|
|
+ throw Php::Exception("Failed to open raw socket on interface: " + interface);
|
|
|
+ }
|
|
|
+ auto res = bpf_attach_socket(sock, prog->_fd);
|
|
|
+ if (res < 0) {
|
|
|
+ throw Php::Exception("Failed to attach BPF program to socket.");
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extern "C" {
|
|
|
+void register_all_classes(Php::Extension &ext) {
|
|
|
+ /*Bpf*/
|
|
|
+ REGISTER_PHP_CLASS(EbpfExtension, "Bpf");
|
|
|
+ REGISTER_EBPF_PHP_METHOD(EbpfExtension, trace_print, {
|
|
|
+ Php::ByVal("fmt", Php::Type::String, false)
|
|
|
+ });
|
|
|
+ REGISTER_EBPF_PHP_METHOD(EbpfExtension, __construct, {});
|
|
|
+ REGISTER_EBPF_PHP_METHOD(EbpfExtension, trace_fields, {});
|
|
|
+ REGISTER_EBPF_PHP_METHOD(EbpfExtension, attach_kprobe, {
|
|
|
+ Php::ByVal("kernel_func", Php::Type::String),
|
|
|
+ Php::ByVal("probe_func", Php::Type::String)
|
|
|
+ });
|
|
|
+ REGISTER_EBPF_PHP_METHOD(EbpfExtension, attach_tracepoint, {
|
|
|
+ Php::ByVal("tp", Php::Type::String),
|
|
|
+ Php::ByVal("probe_func", Php::Type::String)
|
|
|
+ });
|
|
|
+ REGISTER_EBPF_PHP_METHOD(EbpfExtension, attach_raw_tracepoint, {
|
|
|
+ Php::ByVal("tp", Php::Type::String),
|
|
|
+ Php::ByVal("probe_func", Php::Type::String)
|
|
|
+ });
|
|
|
+ REGISTER_EBPF_PHP_METHOD(EbpfExtension, attach_kfunc, {
|
|
|
+ Php::ByVal("kernel_func", Php::Type::String),
|
|
|
+ });
|
|
|
+ REGISTER_EBPF_PHP_METHOD(EbpfExtension, attach_lsm, {
|
|
|
+ Php::ByVal("lsm", Php::Type::String),
|
|
|
+ });
|
|
|
+ REGISTER_EBPF_PHP_METHOD(EbpfExtension, 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),
|
|
|
+ });
|
|
|
+ REGISTER_EBPF_PHP_METHOD(EbpfExtension, get_table, {
|
|
|
+ Php::ByVal("tb_name", Php::Type::String),
|
|
|
+ });
|
|
|
+ REGISTER_EBPF_PHP_METHOD(EbpfExtension, perf_buffer_poll, {});
|
|
|
+ REGISTER_EBPF_PHP_METHOD(EbpfExtension, get_syscall_fnname, {
|
|
|
+ Php::ByVal("name", Php::Type::String),
|
|
|
+ });
|
|
|
+ REGISTER_EBPF_PHP_METHOD(EbpfExtension, detach_kprobe, {
|
|
|
+ Php::ByVal("kernel_func", Php::Type::String),
|
|
|
+ });
|
|
|
+ REGISTER_EBPF_PHP_METHOD(EbpfExtension, detach_uprobe, {
|
|
|
+ Php::ByVal("binary_path", Php::Type::String),
|
|
|
+ Php::ByVal("symbol", Php::Type::String),
|
|
|
+ Php::ByVal("options", Php::Type::Array, false),
|
|
|
+ });
|
|
|
+ REGISTER_EBPF_PHP_METHOD(EbpfExtension, get_kprobe_functions, {
|
|
|
+ Php::ByVal("fn", Php::Type::String),
|
|
|
+ });
|
|
|
+ REGISTER_EBPF_PHP_METHOD(EbpfExtension, load_func, {
|
|
|
+ Php::ByVal("fn", Php::Type::String),
|
|
|
+ Php::ByVal("type", Php::Type::Numeric),
|
|
|
+ });
|
|
|
+ REGISTER_EBPF_PHP_METHOD(EbpfExtension, attach_raw_socket, {
|
|
|
+ Php::ByVal("fn", Php::Type::Object),
|
|
|
+ Php::ByVal("interface", Php::Type::String),
|
|
|
+ });
|
|
|
+ /* const */
|
|
|
+ REGISTER_CONSTANT(EbpfExtension, "SOCKET_FILTER", BPFProgType::SOCKET_FILTER);
|
|
|
+ REGISTER_CONSTANT(EbpfExtension, "KPROBE", BPFProgType::KPROBE);
|
|
|
+ REGISTER_CONSTANT(EbpfExtension, "SCHED_CLS", BPFProgType::SCHED_CLS);
|
|
|
+ REGISTER_CONSTANT(EbpfExtension, "SCHED_ACT", BPFProgType::SCHED_ACT);
|
|
|
+ REGISTER_CONSTANT(EbpfExtension, "TRACEPOINT", BPFProgType::TRACEPOINT);
|
|
|
+ REGISTER_CONSTANT(EbpfExtension, "XDP", BPFProgType::XDP);
|
|
|
+ REGISTER_CONSTANT(EbpfExtension, "PERF_EVENT", BPFProgType::PERF_EVENT);
|
|
|
+ REGISTER_CONSTANT(EbpfExtension, "CGROUP_SKB", BPFProgType::CGROUP_SKB);
|
|
|
+ REGISTER_CONSTANT(EbpfExtension, "CGROUP_SOCK", BPFProgType::CGROUP_SOCK);
|
|
|
+ REGISTER_CONSTANT(EbpfExtension, "LWT_IN", BPFProgType::LWT_IN);
|
|
|
+ REGISTER_CONSTANT(EbpfExtension, "LWT_OUT", BPFProgType::LWT_OUT);
|
|
|
+ REGISTER_CONSTANT(EbpfExtension, "LWT_XMIT", BPFProgType::LWT_XMIT);
|
|
|
+ REGISTER_CONSTANT(EbpfExtension, "SOCK_OPS", BPFProgType::SOCK_OPS);
|
|
|
+ REGISTER_CONSTANT(EbpfExtension, "SK_SKB", BPFProgType::SK_SKB);
|
|
|
+ REGISTER_CONSTANT(EbpfExtension, "CGROUP_DEVICE", BPFProgType::CGROUP_DEVICE);
|
|
|
+ REGISTER_CONSTANT(EbpfExtension, "SK_MSG", BPFProgType::SK_MSG);
|
|
|
+ REGISTER_CONSTANT(EbpfExtension, "RAW_TRACEPOINT", BPFProgType::RAW_TRACEPOINT);
|
|
|
+ REGISTER_CONSTANT(EbpfExtension, "CGROUP_SOCK_ADDR", BPFProgType::CGROUP_SOCK_ADDR);
|
|
|
+ REGISTER_CONSTANT(EbpfExtension, "CGROUP_SOCKOPT", BPFProgType::CGROUP_SOCKOPT);
|
|
|
+ REGISTER_CONSTANT(EbpfExtension, "TRACING", BPFProgType::TRACING);
|
|
|
+ REGISTER_CONSTANT(EbpfExtension, "LSM", BPFProgType::LSM);
|
|
|
+ FINALIZE_PHP_CLASS(ext, EbpfExtension);
|
|
|
+
|
|
|
+ /*HashTable*/
|
|
|
+ REGISTER_PHP_CLASS(HashTable, "HashTable");
|
|
|
+ REGISTER_EBPF_PHP_METHOD(HashTable, get_values, {});
|
|
|
+ REGISTER_EBPF_PHP_METHOD(HashTable, clear, {});
|
|
|
+ FINALIZE_PHP_CLASS(ext, HashTable);
|
|
|
+
|
|
|
+ /*ArrayTable*/
|
|
|
+ REGISTER_PHP_CLASS(ArrayTable, "ArrayTable");
|
|
|
+ REGISTER_EBPF_PHP_METHOD(ArrayTable, get_value, {
|
|
|
+ Php::ByVal("idx", Php::Type::Numeric),
|
|
|
+ });
|
|
|
+ REGISTER_EBPF_PHP_METHOD(ArrayTable, print_log2_hist, {
|
|
|
+ Php::ByVal("header", Php::Type::String),
|
|
|
+ });
|
|
|
+ REGISTER_EBPF_PHP_METHOD(ArrayTable, print_linear_hist, {
|
|
|
+ Php::ByVal("header", Php::Type::String),
|
|
|
+ });
|
|
|
+ FINALIZE_PHP_CLASS(ext, ArrayTable);
|
|
|
+
|
|
|
+ /*ProgArrayTable*/
|
|
|
+ REGISTER_PHP_CLASS(ProgArrayTable, "ProgArrayTable");
|
|
|
+ FINALIZE_PHP_CLASS(ext, ProgArrayTable);
|
|
|
+
|
|
|
+ /*PerfEventArrayTable*/
|
|
|
+ REGISTER_PHP_CLASS(PerfEventArrayTable, "PerfEventArrayTable");
|
|
|
+ REGISTER_EBPF_PHP_METHOD(PerfEventArrayTable, open_perf_buffer, {
|
|
|
+ Php::ByVal("fn", Php::Type::String),
|
|
|
+ });
|
|
|
+ FINALIZE_PHP_CLASS(ext, PerfEventArrayTable);
|
|
|
+
|
|
|
+ /*PerCpuHashTable*/
|
|
|
+ REGISTER_PHP_CLASS(PerCpuHashTable, "PerCpuHashTable");
|
|
|
+ FINALIZE_PHP_CLASS(ext, PerCpuHashTable);
|
|
|
+
|
|
|
+ /*PerCpuArrayTable*/
|
|
|
+ REGISTER_PHP_CLASS(PerCpuArrayTable, "PerCpuArrayTable");
|
|
|
+ REGISTER_EBPF_PHP_METHOD(PerCpuArrayTable, sum_value, {
|
|
|
+ Php::ByVal("idx", Php::Type::Numeric),
|
|
|
+ });
|
|
|
+ FINALIZE_PHP_CLASS(ext, PerCpuArrayTable);
|
|
|
+
|
|
|
+ /*LpmTrieTable*/
|
|
|
+ REGISTER_PHP_CLASS(LpmTrieTable, "LpmTrieTable");
|
|
|
+ FINALIZE_PHP_CLASS(ext, LpmTrieTable);
|
|
|
+
|
|
|
+ /*StackTraceTable*/
|
|
|
+ REGISTER_PHP_CLASS(StackTraceTable, "StackTraceTable");
|
|
|
+ REGISTER_EBPF_PHP_METHOD(StackTraceTable, get_values, {
|
|
|
+ Php::ByVal("stack", Php::Type::Numeric),
|
|
|
+ Php::ByVal("pid", Php::Type::Numeric, false),
|
|
|
+ });
|
|
|
+ FINALIZE_PHP_CLASS(ext, StackTraceTable);
|
|
|
+
|
|
|
+ /*LruHashTable*/
|
|
|
+ REGISTER_PHP_CLASS(LruHashTable, "LruHashTable");
|
|
|
+ FINALIZE_PHP_CLASS(ext, LruHashTable);
|
|
|
+
|
|
|
+ /*LruPerCpuHashTable*/
|
|
|
+ REGISTER_PHP_CLASS(LruPerCpuHashTable, "LruPerCpuHashTable");
|
|
|
+ FINALIZE_PHP_CLASS(ext, LruPerCpuHashTable);
|
|
|
+
|
|
|
+ /*CgroupArrayTable*/
|
|
|
+ REGISTER_PHP_CLASS(CgroupArrayTable, "CgroupArrayTable");
|
|
|
+ FINALIZE_PHP_CLASS(ext, CgroupArrayTable);
|
|
|
+
|
|
|
+ /*DevMapTable*/
|
|
|
+ REGISTER_PHP_CLASS(DevMapTable, "DevMapTable");
|
|
|
+ FINALIZE_PHP_CLASS(ext, DevMapTable);
|
|
|
+
|
|
|
+ /*CpuMapTable*/
|
|
|
+ REGISTER_PHP_CLASS(CpuMapTable, "CpuMapTable");
|
|
|
+ FINALIZE_PHP_CLASS(ext, CpuMapTable);
|
|
|
+
|
|
|
+ /*XskMapTable*/
|
|
|
+ REGISTER_PHP_CLASS(XskMapTable, "XskMapTable");
|
|
|
+ FINALIZE_PHP_CLASS(ext, XskMapTable);
|
|
|
+
|
|
|
+ /*MapInMapArrayTable*/
|
|
|
+ REGISTER_PHP_CLASS(MapInMapArrayTable, "MapInMapArrayTable");
|
|
|
+ FINALIZE_PHP_CLASS(ext, MapInMapArrayTable);
|
|
|
+
|
|
|
+ /*MapInMapHashTable*/
|
|
|
+ REGISTER_PHP_CLASS(MapInMapHashTable, "MapInMapHashTable");
|
|
|
+ FINALIZE_PHP_CLASS(ext, MapInMapHashTable);
|
|
|
+
|
|
|
+ /*QueueStackTable*/
|
|
|
+ REGISTER_PHP_CLASS(QueueStackTable, "QueueStackTable");
|
|
|
+ FINALIZE_PHP_CLASS(ext, QueueStackTable);
|
|
|
+
|
|
|
+ /*RingBufTable*/
|
|
|
+ REGISTER_PHP_CLASS(RingBufTable, "RingBufTable");
|
|
|
+ FINALIZE_PHP_CLASS(ext, RingBufTable);
|
|
|
+
|
|
|
+ /*ProgFunc*/
|
|
|
+ REGISTER_PHP_CLASS(ProgFunc, "ProgFunc");
|
|
|
+ FINALIZE_PHP_CLASS(ext, ProgFunc);
|
|
|
+}
|
|
|
+PHPCPP_EXPORT void *get_module() {
|
|
|
+ static Php::Extension extension(EXT_NAME, EXT_VERSION);
|
|
|
+ register_all_classes(extension);
|
|
|
+ return extension;
|
|
|
+}
|
|
|
+}
|