|
@@ -7,11 +7,16 @@
|
|
|
#include <unordered_set>
|
|
#include <unordered_set>
|
|
|
#include <regex>
|
|
#include <regex>
|
|
|
#include <phpcpp.h>
|
|
#include <phpcpp.h>
|
|
|
-#include "bcc_common.h"
|
|
|
|
|
-#include "bpf_module.h"
|
|
|
|
|
-#include "libbpf.h"
|
|
|
|
|
#include <BPF.h>
|
|
#include <BPF.h>
|
|
|
|
|
+#include <string>
|
|
|
|
|
+
|
|
|
|
|
+#include "main.h"
|
|
|
|
|
+#include "libbpf.h"
|
|
|
|
|
+#include "bpf_module.h"
|
|
|
|
|
+#include "bcc_common.h"
|
|
|
#include "table.h"
|
|
#include "table.h"
|
|
|
|
|
+#include "function.h"
|
|
|
|
|
+
|
|
|
|
|
|
|
|
#define TRACE_PIPE_PATH "/sys/kernel/debug/tracing/trace_pipe"
|
|
#define TRACE_PIPE_PATH "/sys/kernel/debug/tracing/trace_pipe"
|
|
|
|
|
|
|
@@ -175,19 +180,15 @@ public:
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 过滤掉 NOKPROBE_SYMBOL() 定义的 _kbl_addr_*
|
|
|
|
|
if (func_name.rfind("_kbl_addr_", 0) == 0) {
|
|
if (func_name.rfind("_kbl_addr_", 0) == 0) {
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
|
- // 过滤掉 perf 相关函数
|
|
|
|
|
if (func_name.rfind("__perf", 0) == 0 || func_name.rfind("perf_", 0) == 0) {
|
|
if (func_name.rfind("__perf", 0) == 0 || func_name.rfind("perf_", 0) == 0) {
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
|
- // 过滤掉 __SCT__ 前缀的静态函数
|
|
|
|
|
if (func_name.rfind("__SCT__", 0) == 0) {
|
|
if (func_name.rfind("__SCT__", 0) == 0) {
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
|
- // 过滤掉 GCC 8 编译生成的 .cold 函数
|
|
|
|
|
if (std::regex_match(func_name, cold_regex)) {
|
|
if (std::regex_match(func_name, cold_regex)) {
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
@@ -208,25 +209,46 @@ public:
|
|
|
auto res = get_kprobe_functions(fn);
|
|
auto res = get_kprobe_functions(fn);
|
|
|
Php::Array result;
|
|
Php::Array result;
|
|
|
for (const auto &item: res) {
|
|
for (const auto &item: res) {
|
|
|
- result[result.size()] = item; // 将每个匹配的函数名添加到 Php::Array
|
|
|
|
|
|
|
+ result[result.size()] = item;
|
|
|
}
|
|
}
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
Php::Value php_test(Php::Parameters ¶ms) {
|
|
Php::Value php_test(Php::Parameters ¶ms) {
|
|
|
|
|
+
|
|
|
std::string fn = params[0].stringValue();
|
|
std::string fn = params[0].stringValue();
|
|
|
- Php::out << fn << std::endl;
|
|
|
|
|
|
|
+ Php::out << fn << std::endl;
|
|
|
auto res = get_kprobe_functions(fn);
|
|
auto res = get_kprobe_functions(fn);
|
|
|
Php::Array result;
|
|
Php::Array result;
|
|
|
- for (const auto &item : res) {
|
|
|
|
|
- result[result.size()] = item; // 将每个匹配的函数名添加到 Php::Array
|
|
|
|
|
|
|
+ for (const auto &item: res) {
|
|
|
|
|
+ result[result.size()] = item;
|
|
|
}
|
|
}
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void __construct(Php::Parameters ¶ms) {
|
|
void __construct(Php::Parameters ¶ms) {
|
|
|
- std::string bpf_code = params[0].stringValue();
|
|
|
|
|
- auto init_res = bpf.init(bpf_code);
|
|
|
|
|
|
|
+ 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()) {
|
|
if (!init_res.ok()) {
|
|
|
throw Php::Exception("init error: " + init_res.msg());
|
|
throw Php::Exception("init error: " + init_res.msg());
|
|
|
}
|
|
}
|
|
@@ -361,17 +383,54 @@ public:
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- [[noreturn]] static void php_trace_print() {
|
|
|
|
|
|
|
+ static void php_trace_print(Php::Parameters ¶ms) {
|
|
|
std::ifstream pipe(TRACE_PIPE_PATH);
|
|
std::ifstream pipe(TRACE_PIPE_PATH);
|
|
|
- std::string line;
|
|
|
|
|
if (!pipe.is_open()) {
|
|
if (!pipe.is_open()) {
|
|
|
throw Php::Exception("Failed to open trace_pipe");
|
|
throw Php::Exception("Failed to open trace_pipe");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- std::cout << "Press Ctrl+C to stop..." << std::endl;
|
|
|
|
|
|
|
+ std::string fmt;
|
|
|
|
|
+ if (params.size() > 0) {
|
|
|
|
|
+ fmt = params[0].stringValue();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ std::string line;
|
|
|
while (true) {
|
|
while (true) {
|
|
|
- if (std::getline(pipe, line)) {
|
|
|
|
|
|
|
+ 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;
|
|
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;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -412,10 +471,10 @@ public:
|
|
|
Php::Array result;
|
|
Php::Array result;
|
|
|
result[0] = task;
|
|
result[0] = task;
|
|
|
result[1] = std::stoi(pid);
|
|
result[1] = std::stoi(pid);
|
|
|
- result[2] = std::stoi(cpu.substr(1, cpu.size() - 2)); // 去掉方括号
|
|
|
|
|
|
|
+ result[2] = std::stoi(cpu.substr(1, cpu.size() - 2));
|
|
|
result[3] = flags;
|
|
result[3] = flags;
|
|
|
result[4] = std::stod(ts);
|
|
result[4] = std::stod(ts);
|
|
|
- result[5] = msg;
|
|
|
|
|
|
|
+ result[5] = Php::Value(msg.c_str(), msg.size());
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
return Php::Value();
|
|
return Php::Value();
|
|
@@ -438,7 +497,8 @@ public:
|
|
|
return Php::Object("ProgArrayTable", new ProgArrayTable(&this->bpf, table_name));
|
|
return Php::Object("ProgArrayTable", new ProgArrayTable(&this->bpf, table_name));
|
|
|
case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
|
|
case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
|
|
|
if (_class_perf_event_obj.isNull()) {
|
|
if (_class_perf_event_obj.isNull()) {
|
|
|
- _class_perf_event_obj = Php::Object("PerfEventArrayTable",new PerfEventArrayTable(&this->bpf, table_name));
|
|
|
|
|
|
|
+ _class_perf_event_obj = Php::Object("PerfEventArrayTable",
|
|
|
|
|
+ new PerfEventArrayTable(&this->bpf, table_name));
|
|
|
}
|
|
}
|
|
|
return _class_perf_event_obj;
|
|
return _class_perf_event_obj;
|
|
|
case BPF_MAP_TYPE_PERCPU_HASH:
|
|
case BPF_MAP_TYPE_PERCPU_HASH:
|
|
@@ -506,6 +566,40 @@ public:
|
|
|
return bpf.get_syscall_fnname(name);
|
|
return bpf.get_syscall_fnname(name);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ Php::Value php_load_func(Php::Parameters ¶ms) {
|
|
|
|
|
+ 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());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void php_attach_raw_socket(Php::Parameters ¶ms) {
|
|
|
|
|
+ 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.");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
std::string sanitize_str(std::string str, bool (*validator)(char),
|
|
std::string sanitize_str(std::string str, bool (*validator)(char),
|
|
@@ -540,11 +634,13 @@ std::string get_kprobe_event(const std::string &kernel_func,
|
|
|
extern "C" {
|
|
extern "C" {
|
|
|
PHPCPP_EXPORT void *get_module() {
|
|
PHPCPP_EXPORT void *get_module() {
|
|
|
static Php::Extension extension("ebpf", "1.0.0");
|
|
static Php::Extension extension("ebpf", "1.0.0");
|
|
|
- Php::Class<EbpfExtension> ebpf_class("Ebpf");
|
|
|
|
|
- ebpf_class.method<&EbpfExtension::php_trace_print>("trace_print");
|
|
|
|
|
|
|
+ Php::Class<EbpfExtension> ebpf_class("Bpf");
|
|
|
|
|
+ ebpf_class.method<&EbpfExtension::php_trace_print>("trace_print", {
|
|
|
|
|
+ Php::ByVal("fmt", Php::Type::String, false)
|
|
|
|
|
+ });
|
|
|
ebpf_class.method<&EbpfExtension::php_trace_fields>("trace_fields");
|
|
ebpf_class.method<&EbpfExtension::php_trace_fields>("trace_fields");
|
|
|
ebpf_class.method<&EbpfExtension::__construct>("__construct", {
|
|
ebpf_class.method<&EbpfExtension::__construct>("__construct", {
|
|
|
- Php::ByVal("bpf_code", Php::Type::String)
|
|
|
|
|
|
|
+ Php::ByVal("opt", Php::Type::Array)
|
|
|
});
|
|
});
|
|
|
ebpf_class.method<&EbpfExtension::php_attach_kprobe>("attach_kprobe", {
|
|
ebpf_class.method<&EbpfExtension::php_attach_kprobe>("attach_kprobe", {
|
|
|
Php::ByVal("kernel_func", Php::Type::String),
|
|
Php::ByVal("kernel_func", Php::Type::String),
|
|
@@ -601,11 +697,19 @@ PHPCPP_EXPORT void *get_module() {
|
|
|
ebpf_class.method<&EbpfExtension::php_get_kprobe_functions>("get_kprobe_functions", {
|
|
ebpf_class.method<&EbpfExtension::php_get_kprobe_functions>("get_kprobe_functions", {
|
|
|
Php::ByVal("fn", Php::Type::String),
|
|
Php::ByVal("fn", Php::Type::String),
|
|
|
});
|
|
});
|
|
|
- extension.add(std::move(ebpf_class));
|
|
|
|
|
|
|
+ ebpf_class.method<&EbpfExtension::php_load_func>("load_func", {
|
|
|
|
|
+ Php::ByVal("fn", Php::Type::String),
|
|
|
|
|
+ Php::ByVal("type", Php::Type::Numeric),
|
|
|
|
|
+ });
|
|
|
|
|
+ ebpf_class.method<&EbpfExtension::php_attach_raw_socket>("attach_raw_socket", {
|
|
|
|
|
+// Php::ByVal("fn", Php::Type::Object),
|
|
|
|
|
+// Php::ByVal("interface", Php::Type::String),
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
/*HashTable*/
|
|
/*HashTable*/
|
|
|
Php::Class<HashTable> ebpf_hashtable_cls("HashTable");
|
|
Php::Class<HashTable> ebpf_hashtable_cls("HashTable");
|
|
|
ebpf_hashtable_cls.method<&HashTable::php_get_values>("values", {});
|
|
ebpf_hashtable_cls.method<&HashTable::php_get_values>("values", {});
|
|
|
|
|
+ ebpf_hashtable_cls.method<&HashTable::php_clear>("clear",{});
|
|
|
/*ArrayTable*/
|
|
/*ArrayTable*/
|
|
|
Php::Class<ArrayTable> ebpf_array_table_cls("ArrayTable");
|
|
Php::Class<ArrayTable> ebpf_array_table_cls("ArrayTable");
|
|
|
ebpf_array_table_cls.method<&ArrayTable::php_get_value>("get_value", {
|
|
ebpf_array_table_cls.method<&ArrayTable::php_get_value>("get_value", {
|
|
@@ -654,6 +758,34 @@ PHPCPP_EXPORT void *get_module() {
|
|
|
Php::Class<QueueStackTable> ebpf_queue_stack_table_cls("QueueStackTable");
|
|
Php::Class<QueueStackTable> ebpf_queue_stack_table_cls("QueueStackTable");
|
|
|
/*RingBufTable*/
|
|
/*RingBufTable*/
|
|
|
Php::Class<RingBufTable> ebpf_ringbuf_table_cls("RingBufTable");
|
|
Php::Class<RingBufTable> ebpf_ringbuf_table_cls("RingBufTable");
|
|
|
|
|
+ /*ProgFunc*/
|
|
|
|
|
+ Php::Class<ProgFunc> ebpf_prog_func_cls("BPFProgFunction");
|
|
|
|
|
+ extension.add(std::move(ebpf_prog_func_cls));
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ /* const */
|
|
|
|
|
+ ebpf_class.constant("SOCKET_FILTER", BPFProgType::SOCKET_FILTER);
|
|
|
|
|
+ ebpf_class.constant("KPROBE", BPFProgType::KPROBE);
|
|
|
|
|
+ ebpf_class.constant("SCHED_CLS", BPFProgType::SCHED_CLS);
|
|
|
|
|
+ ebpf_class.constant("SCHED_ACT", BPFProgType::SCHED_ACT);
|
|
|
|
|
+ ebpf_class.constant("TRACEPOINT", BPFProgType::TRACEPOINT);
|
|
|
|
|
+ ebpf_class.constant("XDP", BPFProgType::XDP);
|
|
|
|
|
+ ebpf_class.constant("PERF_EVENT", BPFProgType::PERF_EVENT);
|
|
|
|
|
+ ebpf_class.constant("CGROUP_SKB", BPFProgType::CGROUP_SKB);
|
|
|
|
|
+ ebpf_class.constant("CGROUP_SOCK", BPFProgType::CGROUP_SOCK);
|
|
|
|
|
+ ebpf_class.constant("LWT_IN", BPFProgType::LWT_IN);
|
|
|
|
|
+ ebpf_class.constant("LWT_OUT", BPFProgType::LWT_OUT);
|
|
|
|
|
+ ebpf_class.constant("LWT_XMIT", BPFProgType::LWT_XMIT);
|
|
|
|
|
+ ebpf_class.constant("SOCK_OPS", BPFProgType::SOCK_OPS);
|
|
|
|
|
+ ebpf_class.constant("SK_SKB", BPFProgType::SK_SKB);
|
|
|
|
|
+ ebpf_class.constant("CGROUP_DEVICE", BPFProgType::CGROUP_DEVICE);
|
|
|
|
|
+ ebpf_class.constant("SK_MSG", BPFProgType::SK_MSG);
|
|
|
|
|
+ ebpf_class.constant("RAW_TRACEPOINT", BPFProgType::RAW_TRACEPOINT);
|
|
|
|
|
+ ebpf_class.constant("CGROUP_SOCK_ADDR", BPFProgType::CGROUP_SOCK_ADDR);
|
|
|
|
|
+ ebpf_class.constant("CGROUP_SOCKOPT", BPFProgType::CGROUP_SOCKOPT);
|
|
|
|
|
+ ebpf_class.constant("TRACING", BPFProgType::TRACING);
|
|
|
|
|
+ ebpf_class.constant("LSM", BPFProgType::LSM);
|
|
|
|
|
|
|
|
extension.add(std::move(ebpf_hashtable_cls));
|
|
extension.add(std::move(ebpf_hashtable_cls));
|
|
|
extension.add(std::move(ebpf_array_table_cls));
|
|
extension.add(std::move(ebpf_array_table_cls));
|
|
@@ -673,6 +805,7 @@ PHPCPP_EXPORT void *get_module() {
|
|
|
extension.add(std::move(ebpf_map_in_maphash_table_cls));
|
|
extension.add(std::move(ebpf_map_in_maphash_table_cls));
|
|
|
extension.add(std::move(ebpf_queue_stack_table_cls));
|
|
extension.add(std::move(ebpf_queue_stack_table_cls));
|
|
|
extension.add(std::move(ebpf_ringbuf_table_cls));
|
|
extension.add(std::move(ebpf_ringbuf_table_cls));
|
|
|
|
|
+ extension.add(std::move(ebpf_class));
|
|
|
return extension;
|
|
return extension;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|