Browse Source

edit path

Carl 9 months ago
parent
commit
71442dd3e4

+ 3 - 0
.gitmodules

@@ -0,0 +1,3 @@
+[submodule "deps/php-cpp"]
+	path = deps/php-cpp
+	url = https://github.com/CopernicaMarketingSoftware/PHP-CPP.git

+ 4 - 8
Makefile

@@ -9,17 +9,14 @@ LINKER				=	g++
 LIB_BCC = /opt/github/bcc/src
 COMPILER_FLAGS		=	-Wall -c -O2 -fpic -I${LIB_BCC}/cc -I${LIB_BCC}/cc/api -I${LIB_BCC}/cc/frontends/clang -I/usr/lib/llvm-14/include -DKERNEL_MODULES_DIR=\"/lib/modules/5.15.0-130-generic\" -o
 LINKER_FLAGS		=	-shared
-LINKER_DEPENDENCIES	=	-lphpcpp -lbcc
-
-
+LINKER_DEPENDENCIES	=	-L/lib/llvm-14/lib /lib/libphpcpp.a /usr/lib/x86_64-linux-gnu/libbcc.a /usr/lib/x86_64-linux-gnu/libbcc-loader-static.a /usr/lib/x86_64-linux-gnu/libbcc_bpf.a -lclang-cpp
 
 RM					=	rm -f
 CP					=	cp -f
 MKDIR				=	mkdir -p
 
-SOURCES				=	$(wildcard *.cpp)
-OBJECTS				=	$(SOURCES:%.cpp=%.o)
-
+SOURCES				=	$(wildcard src/*.cpp)
+OBJECTS				=	$(SOURCES:src/%.cpp=%.o)
 
 #
 #	From here the build instructions start
@@ -28,14 +25,13 @@ OBJECTS				=	$(SOURCES:%.cpp=%.o)
 BCC_SHARED_DIR=/opt/github/bcc/build/src/cc/CMakeFiles/bcc-shared.dir
 BCC1 =  ${BCC_SHARED_DIR}/*.o
 
-
 all:					${OBJECTS} ${EXTENSION}
 
 ${EXTENSION}:			${OBJECTS}
 						${LINKER} ${LINKER_FLAGS} -o $@ ${OBJECTS} ${BCC} ${LINKER_DEPENDENCIES}
 
 ${OBJECTS}:
-						${COMPILER} ${COMPILER_FLAGS} $@ ${@:%.o=%.cpp}
+						${COMPILER} ${COMPILER_FLAGS} $@ src/${@:%.o=%.cpp}
 
 install:		
 						${CP} ${EXTENSION} ${EXTENSION_DIR}

+ 31 - 0
examples/local_storage/inode_storage.php

@@ -0,0 +1,31 @@
+<?php
+$prog = <<<EOT
+#include <linux/fs.h>
+
+BPF_INODE_STORAGE(inode_storage_map, int);
+
+LSM_PROBE(inode_rename, struct inode *old_dir, struct dentry *old_dentry,
+	  struct inode *new_dir, struct dentry *new_dentry, unsigned int flags)
+{
+	int *value;
+
+	value = inode_storage_map.inode_storage_get(old_dentry->d_inode, 0, BPF_LOCAL_STORAGE_GET_F_CREATE);
+	if (!value)
+		return 0;
+
+	bpf_trace_printk("%d", *value);
+	return 0;
+}
+EOT;
+
+# load BPF program
+$ebpf = new Bpf(["text" => $prog]);
+
+# format output
+while (true) {
+    try {
+        $ebpf->trace_print();
+    } catch (Exception $e) {
+        continue;
+    }
+} 

+ 44 - 0
examples/local_storage/task_storage.php

@@ -0,0 +1,44 @@
+<?php
+$prog = <<<EOT
+BPF_TASK_STORAGE(task_storage_map, __u64);
+
+KFUNC_PROBE(inet_listen)
+{
+	__u64 ts = bpf_ktime_get_ns();
+
+	/* save timestamp to local storage on function entry */
+	task_storage_map.task_storage_get(bpf_get_current_task_btf(), &ts, BPF_LOCAL_STORAGE_GET_F_CREATE);
+
+	bpf_trace_printk("inet_listen entry: store timestamp %lld", ts);
+	return 0;
+}
+
+KRETFUNC_PROBE(inet_listen)
+{
+	__u64 *ts;
+
+	/* retrieve timestamp stored at local storage on function exit */
+	ts = task_storage_map.task_storage_get(bpf_get_current_task_btf(), 0, 0);
+	if (!ts)
+		return 0;
+
+	/* delete timestamp from local storage */
+	task_storage_map.task_storage_delete(bpf_get_current_task_btf());
+
+	/* calculate latency */
+	bpf_trace_printk("inet_listen exit: cost %lldus", (bpf_ktime_get_ns() - *ts) / 1000);
+	return 0;
+}
+EOT;
+
+# load BPF program
+$ebpf = new Bpf(["text" => $prog]);
+
+# format output
+while (true) {
+    try {
+        $ebpf->trace_print();
+    } catch (Exception $e) {
+        continue;
+    }
+} 

+ 0 - 0
example/networking/net_monitor.php → examples/networking/net_monitor.php


+ 0 - 0
example/tracing/biolatpcts.php → examples/tracing/biolatpcts.php


+ 0 - 0
example/tracing/dddos.php → examples/tracing/dddos.php


+ 0 - 0
example/tracing/disksnoop.php → examples/tracing/disksnoop.php


+ 0 - 0
example/tracing/hello_fields.php → examples/tracing/hello_fields.php


+ 0 - 0
example/tracing/hello_perf_output.php → examples/tracing/hello_perf_output.php


+ 0 - 0
example/tracing/hello_perf_output_using_ns.php → examples/tracing/hello_perf_output_using_ns.php


+ 0 - 0
example/tracing/kvm_hypercall.php → examples/tracing/kvm_hypercall.php


+ 0 - 0
example/tracing/mallocstacks.php → examples/tracing/mallocstacks.php


+ 0 - 0
example/tracing/setuid_monitor.php → examples/tracing/setuid_monitor.php


+ 0 - 0
example/tracing/stacksnoop.php → examples/tracing/stacksnoop.php


+ 0 - 0
example/tracing/strlen_count.php → examples/tracing/strlen_count.php


+ 0 - 0
example/tracing/sync_timing.php → examples/tracing/sync_timing.php


+ 0 - 0
example/tracing/tcpv4connect.php → examples/tracing/tcpv4connect.php


+ 0 - 0
example/tracing/tp.php → examples/tracing/tp.php


+ 0 - 0
example/tracing/trace_fields.php → examples/tracing/trace_fields.php


+ 0 - 0
example/tracing/trace_perf_output.php → examples/tracing/trace_perf_output.php


+ 0 - 0
example/tracing/undump.php → examples/tracing/undump.php


+ 0 - 0
example/tracing/uprobe.php → examples/tracing/uprobe.php


+ 0 - 0
example/tracing/urandomread-explicit.php → examples/tracing/urandomread-explicit.php


+ 0 - 0
example/tracing/urandomread.php → examples/tracing/urandomread.php


+ 0 - 10
function.cpp

@@ -1,10 +0,0 @@
-//
-// Created by Carl.Guo on 2025/4/17.
-//
-
-#include "function.h"
-
-Php::Value ProgFunc::__get(const Php::Value &name)
-{
-	return Php::Base::__get(name);
-}

+ 0 - 31
function.h

@@ -1,31 +0,0 @@
-//
-// Created by Carl.Guo on 2025/4/17.
-//
-
-#ifndef PHPCPP_HELLOWORLD_FUNCTION_H
-#define PHPCPP_HELLOWORLD_FUNCTION_H
-
-#include <iostream>
-#include <utility>
-#include <phpcpp.h>
-#include "bpf_module.h"
-#include "BPF.h"
-
-class ProgFunc : public Php::Base {
-
-public:
-	ebpf::BPF *bpf;
-	std::string _name;
-	int _fd;
-
-	ProgFunc(ebpf::BPF *bpf_instance, std::string name, int fd)
-			: bpf(bpf_instance), _name(std::move(name)), _fd(fd) {
-	}
-
-	virtual ~ProgFunc() = default;
-
-	Php::Value __get(const Php::Value &name);
-};
-
-
-#endif //PHPCPP_HELLOWORLD_FUNCTION_H

+ 0 - 811
main.cpp

@@ -1,811 +0,0 @@
-#include <iostream>
-#include <fstream>
-#include <csignal>
-#include <sstream>
-#include <utility>
-#include <vector>
-#include <unordered_set>
-#include <regex>
-#include <phpcpp.h>
-#include <BPF.h>
-#include <string>
-
-#include "main.h"
-#include "libbpf.h"
-#include "bpf_module.h"
-#include "bcc_common.h"
-#include "table.h"
-#include "function.h"
-
-
-#define TRACE_PIPE_PATH "/sys/kernel/debug/tracing/trace_pipe"
-
-#define DEBUGFS "/sys/kernel/debug"
-
-
-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 EbpfExtension : public Php::Base {
-private:
-	ebpf::BPF bpf;
-	void *mod;
-	Php::Object _perf_event;
-	Php::Value _class_perf_event_obj;
-
-	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);
-				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);
-				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) {
-		int from_attr = 1;
-		auto res = get_table_cls(name, from_attr);
-		if (!res.isObject()) {
-			return Php::Base::__get(name);
-		} else {
-			return res;
-		}
-	}
-
-	std::unordered_set<std::string> 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;
-	}
-
-	Php::Value php_get_kprobe_functions(Php::Parameters &params) {
-		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;
-	}
-
-	Php::Value php_test(Php::Parameters &params) {
-
-		std::string fn = params[0].stringValue();
-		Php::out << fn << std::endl;
-		auto res = get_kprobe_functions(fn);
-		Php::Array result;
-		for (const auto &item: res) {
-			result[result.size()] = item;
-		}
-		return result;
-	}
-
-	void __construct(Php::Parameters &params) {
-		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::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 &params) {
-		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 &params) {
-		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 &params) {
-		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 &params) {
-		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 &params) {
-		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 &params) {
-		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 &params) {
-		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 &params) {
-		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());
-		}
-	}
-
-	static void php_trace_print(Php::Parameters &params) {
-		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;
-			}
-		}
-	}
-
-	static Php::Value php_trace_fields(Php::Parameters &params) {
-		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();
-	}
-
-//	Php::Value php_perf_event(Php::Parameters &params) {
-//		std::string event_name = params[0].stringValue();  // Get event_name from the parameters
-//		this->_class_perf_event = new PerfEventArrayTable(&this->bpf, event_name);
-//		return Php::Object("PerfEventArrayTable", this->_class_perf_event);
-//	}
-
-	Php::Value 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));
-		}
-	}
-
-	Php::Value php_get_table(Php::Parameters &params) {
-		std::string table_name = params[0].stringValue();
-		int from_fn = 0;
-		return get_table_cls(table_name.c_str(), from_fn);
-	}
-
-	void php_perf_buffer_poll(Php::Parameters &params) {
-
-		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.");
-		}
-	}
-
-	Php::Value php_get_syscall_fnname(Php::Parameters &params) {
-		std::string name = params[0].stringValue();
-		return bpf.get_syscall_fnname(name);
-	}
-
-	Php::Value php_load_func(Php::Parameters &params) {
-		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 &params) {
-		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),
-                         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;
-}
-
-extern "C" {
-PHPCPP_EXPORT void *get_module() {
-	static Php::Extension extension("ebpf", "1.0.0");
-	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::__construct>("__construct", {
-			Php::ByVal("opt", Php::Type::Array)
-	});
-	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_perf_event>("perf_event", {
-//			Php::ByVal("ev_name", Php::Type::String),
-//	});
-
-	ebpf_class.method<&EbpfExtension::php_get_table>("get_table", {
-			Php::ByVal("tb_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),
-	});
-	ebpf_class.method<&EbpfExtension::php_test>("test", {
-			Php::ByVal("idx", Php::Type::String),
-
-	});
-	ebpf_class.method<&EbpfExtension::php_get_kprobe_functions>("get_kprobe_functions", {
-			Php::ByVal("fn", Php::Type::String),
-	});
-	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*/
-	Php::Class<HashTable> ebpf_hashtable_cls("HashTable");
-	ebpf_hashtable_cls.method<&HashTable::php_get_values>("values", {});
-	ebpf_hashtable_cls.method<&HashTable::php_clear>("clear",{});
-	/*ArrayTable*/
-	Php::Class<ArrayTable> ebpf_array_table_cls("ArrayTable");
-	ebpf_array_table_cls.method<&ArrayTable::php_get_value>("get_value", {
-			Php::ByVal("idx", Php::Type::Numeric),
-	});
-	/*ProgArrayTable*/
-	Php::Class<ProgArrayTable> ebpf_prog_array_table_cls("ProgArrayTable");
-	/*PerfEventArrayTable*/
-	Php::Class<PerfEventArrayTable> ebpf_perf_event_class("PerfEventArrayTable");
-	ebpf_perf_event_class.method<&PerfEventArrayTable::php_open_perf_buffer>("open_perf_buffer", {
-			Php::ByVal("fn", Php::Type::String),
-	});
-	/*PerCpuHashTable*/
-	Php::Class<PerCpuHashTable> ebpf_percpuhash_table_cls("PerCpuHashTable");
-	/*PerCpuArrayTable*/
-	Php::Class<PerCpuArrayTable> ebpf_per_cpu_array_table_cls("PerCpuArrayTable");
-	ebpf_per_cpu_array_table_cls.method<&PerCpuArrayTable::php_sum_value>("sum_value", {
-			Php::ByVal("idx", Php::Type::Numeric),
-	});
-	/*LpmTrieTable*/
-	Php::Class<LpmTrieTable> ebpf_lpmtrie_table_cls("LpmTrieTable");
-	/*StackTraceTable*/
-	Php::Class<StackTraceTable> ebpf_stack_trace_table_cls("StackTraceTable");
-	ebpf_stack_trace_table_cls.method<&StackTraceTable::php_get_values>("values", {
-			Php::ByVal("stack", Php::Type::Numeric),
-			Php::ByVal("pid", Php::Type::Numeric, false),
-	});
-
-	/*LruHashTable*/
-	Php::Class<LruHashTable> ebpf_lruhash_table_cls("LruHashTable");
-	/*LruPerCpuHashTable*/
-	Php::Class<LruPerCpuHashTable> ebpf_lruper_cpuhash_table_cls("LruPerCpuHashTable");
-	/*CgroupArrayTable*/
-	Php::Class<CgroupArrayTable> ebpf_cgroup_array_table_cls("CgroupArrayTable");
-	/*DevMapTable*/
-	Php::Class<DevMapTable> ebpf_devmap_table_cls("DevMapTable");
-	/*CpuMapTable*/
-	Php::Class<CpuMapTable> ebpf_cpumap_table_cls("CpuMapTable");
-	/*XskMapTable*/
-	Php::Class<XskMapTable> ebpf_xskmap_table_cls("XskMapTable");
-	/*MapInMapArrayTable*/
-	Php::Class<MapInMapArrayTable> ebpf_map_in_map_array_table_cls("MapInMapArrayTable");
-	/*MapInMapHashTable*/
-	Php::Class<MapInMapHashTable> ebpf_map_in_maphash_table_cls("MapInMapHashTable");
-	/*QueueStackTable*/
-	Php::Class<QueueStackTable> ebpf_queue_stack_table_cls("QueueStackTable");
-	/*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_array_table_cls));
-	extension.add(std::move(ebpf_prog_array_table_cls));
-	extension.add(std::move(ebpf_perf_event_class));
-	extension.add(std::move(ebpf_percpuhash_table_cls));
-	extension.add(std::move(ebpf_per_cpu_array_table_cls));
-	extension.add(std::move(ebpf_lpmtrie_table_cls));
-	extension.add(std::move(ebpf_stack_trace_table_cls));
-	extension.add(std::move(ebpf_lruhash_table_cls));
-	extension.add(std::move(ebpf_lruper_cpuhash_table_cls));
-	extension.add(std::move(ebpf_cgroup_array_table_cls));
-	extension.add(std::move(ebpf_devmap_table_cls));
-	extension.add(std::move(ebpf_cpumap_table_cls));
-	extension.add(std::move(ebpf_xskmap_table_cls));
-	extension.add(std::move(ebpf_map_in_map_array_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_ringbuf_table_cls));
-	extension.add(std::move(ebpf_class));
-	return extension;
-}
-}

+ 0 - 28
main.h

@@ -1,28 +0,0 @@
-#ifndef PHPCPP_HELLOWORLD_MAIN_H
-#define PHPCPP_HELLOWORLD_MAIN_H
-class BPFProgType {
-public:
-	static constexpr int SOCKET_FILTER     = 1;
-	static constexpr int KPROBE            = 2;
-	static constexpr int SCHED_CLS         = 3;
-	static constexpr int SCHED_ACT         = 4;
-	static constexpr int TRACEPOINT        = 5;
-	static constexpr int XDP               = 6;
-	static constexpr int PERF_EVENT        = 7;
-	static constexpr int CGROUP_SKB        = 8;
-	static constexpr int CGROUP_SOCK       = 9;
-	static constexpr int LWT_IN            = 10;
-	static constexpr int LWT_OUT           = 11;
-	static constexpr int LWT_XMIT          = 12;
-	static constexpr int SOCK_OPS          = 13;
-	static constexpr int SK_SKB            = 14;
-	static constexpr int CGROUP_DEVICE     = 15;
-	static constexpr int SK_MSG            = 16;
-	static constexpr int RAW_TRACEPOINT    = 17;
-	static constexpr int CGROUP_SOCK_ADDR  = 18;
-	static constexpr int CGROUP_SOCKOPT    = 25;
-	static constexpr int TRACING           = 26;
-	static constexpr int LSM               = 29;
-};
-
-#endif //PHPCPP_HELLOWORLD_MAIN_H

+ 48 - 0
src/common.h

@@ -0,0 +1,48 @@
+#ifndef SRC_COMMON_H
+#define SRC_COMMON_H
+
+// Standard C++ headers
+#include <unordered_set>
+#include <iostream>
+#include <fstream>
+#include <csignal>
+#include <sstream>
+#include <utility>
+#include <vector>
+#include <regex>
+#include <string>
+
+// External library headers
+#include <phpcpp.h>
+#include <BPF.h>
+
+// Common definitions
+#define TRACE_PIPE_PATH "/sys/kernel/debug/tracing/trace_pipe"
+#define DEBUGFS "/sys/kernel/debug"
+#define EXT_NAME "ebpf"
+#define EXT_VERSION "1.0.0"
+
+// Common macros
+#define EBPF_CLASS(cls) ebpf##cls
+#define REGISTER_PHP_CLASS(cls, php_cls) Php::Class<cls> EBPF_CLASS(cls)(php_cls)
+#define REGISTER_EBPF_PHP_METHOD(cpp_cls, method_name, ...) \
+    ebpf##cpp_cls.method<&cpp_cls::php_##method_name>(#method_name, __VA_ARGS__)
+#define FINALIZE_PHP_CLASS(ext, cls) ext.add(std::move(EBPF_CLASS(cls)))
+#define REGISTER_CONSTANT(cls, k, v) EBPF_CLASS(cls).constant(k, v)
+#define EBPF_PHP_METHOD(CLASS, FUNC, RET) RET CLASS::php_##FUNC(Php::Parameters &params)
+#define PHP_DEFAULT_PARAMS Php::Parameters &param
+#define EBPF_PHP_METHOD_DECL(FUNC, RET) RET php_##FUNC(PHP_DEFAULT_PARAMS)
+
+// Common constants
+const std::vector<std::string> syscall_prefixes = {
+    "sys_",
+    "__x64_sys_",
+    "__x32_compat_sys_",
+    "__ia32_compat_sys_",
+    "__arm64_sys_",
+    "__s390x_sys_",
+    "__s390_sys_",
+    "__riscv_sys_"
+};
+
+#endif // SRC_COMMON_H 

+ 748 - 0
src/ebpf.cpp

@@ -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;
+}
+}

+ 22 - 0
src/function.cpp

@@ -0,0 +1,22 @@
+/*
+ * 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 "function.h"
+
+Php::Value ProgFunc::__get(const Php::Value &name)
+{
+	return Php::Base::__get(name);
+}

+ 44 - 0
src/function.h

@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#ifndef SRC_FUNCTION_H
+#define SRC_FUNCTION_H
+
+#include "common.h"
+#include <iostream>
+#include <utility>
+#include <phpcpp.h>
+#include "bpf_module.h"
+#include "BPF.h"
+
+class ProgFunc : public Php::Base {
+
+public:
+	ebpf::BPF *bpf;
+	std::string _name;
+	int _fd;
+
+	ProgFunc(ebpf::BPF *bpf_instance, std::string name, int fd)
+			: bpf(bpf_instance), _name(std::move(name)), _fd(fd) {
+	}
+
+	virtual ~ProgFunc() = default;
+
+	Php::Value __get(const Php::Value &name);
+};
+
+
+#endif //SRC_FUNCTION_H

+ 230 - 0
src/php_ebpf.h

@@ -0,0 +1,230 @@
+/*
+ * 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.
+ */
+
+#ifndef SRC_MAIN_H
+#define SRC_MAIN_H
+
+#include "common.h"
+
+class BPFProgType {
+public:
+	static constexpr int SOCKET_FILTER = 1;
+	static constexpr int KPROBE = 2;
+	static constexpr int SCHED_CLS = 3;
+	static constexpr int SCHED_ACT = 4;
+	static constexpr int TRACEPOINT = 5;
+	static constexpr int XDP = 6;
+	static constexpr int PERF_EVENT = 7;
+	static constexpr int CGROUP_SKB = 8;
+	static constexpr int CGROUP_SOCK = 9;
+	static constexpr int LWT_IN = 10;
+	static constexpr int LWT_OUT = 11;
+	static constexpr int LWT_XMIT = 12;
+	static constexpr int SOCK_OPS = 13;
+	static constexpr int SK_SKB = 14;
+	static constexpr int CGROUP_DEVICE = 15;
+	static constexpr int SK_MSG = 16;
+	static constexpr int RAW_TRACEPOINT = 17;
+	static constexpr int CGROUP_SOCK_ADDR = 18;
+	static constexpr int CGROUP_SOCKOPT = 25;
+	static constexpr int TRACING = 26;
+	static constexpr int LSM = 29;
+};
+
+class EbpfExtension : public Php::Base {
+private:
+	ebpf::BPF bpf;
+	void *mod;
+	Php::Object _perf_event;
+	Php::Value _class_perf_event_obj;
+
+	/**
+	 * @brief Add a prefix to a function name
+	 * @param prefix The prefix to add
+	 * @param name The original function name
+	 * @return The function name with prefix added
+	 */
+	static std::string add_prefix(const std::string &prefix, const std::string &name);
+
+	/**
+	 * @brief Fix syscall function name by adding appropriate prefix
+	 * @param name The original syscall function name
+	 * @return The fixed syscall function name with proper prefix
+	 */
+	std::string fix_syscall_fnname(const std::string &name);
+
+	/**
+	 * @brief Automatically load trace functions
+	 * This method is responsible for loading and initializing trace-related functions
+	 */
+	void _trace_autoload();
+
+public:
+	/**
+	 * @brief Default constructor for EbpfExtension
+	 */
+	EbpfExtension() = default;
+
+	/**
+	 * @brief Virtual destructor for EbpfExtension
+	 */
+	virtual ~EbpfExtension() = default;
+
+	/**
+	 * @brief Magic method to get property value
+	 * @param name The name of the property to get
+	 * @return The value of the property
+	 */
+	Php::Value __get(const Php::Value &name);
+
+	/**
+	 * @brief Get kprobe functions matching a regular expression
+	 * @param event_re Regular expression to match function names
+	 * @return Set of matching function names
+	 */
+	std::unordered_set<std::string> get_kprobe_functions(const std::string &event_re);
+
+	/**
+	 * @brief Attach a kfunc (kernel function) probe
+	 * @param kfn The kernel function name to attach to
+	 * @return Status tuple indicating success or failure
+	 */
+	ebpf::StatusTuple attach_kfunc(const std::string &kfn);
+
+	/**
+	 * @brief Attach an LSM (Linux Security Module) probe
+	 * @param lsm The LSM hook name to attach to
+	 * @return Status tuple indicating success or failure
+	 */
+	ebpf::StatusTuple attach_lsm(const std::string &lsm);
+
+	/**
+	 * @brief Get a BPF table class
+	 * @param table_name Name of the BPF table
+	 * @param from_attr Attribute to get from the table
+	 * @return The table class object
+	 */
+	Php::Value get_table_cls(const char *table_name, int from_attr);
+
+	/**
+	 * @brief PHP method to get kprobe functions
+	 * @param params PHP parameters
+	 * @return PHP value containing kprobe functions
+	 */
+	EBPF_PHP_METHOD_DECL(get_kprobe_functions, Php::Value);
+
+	/**
+	 * @brief PHP constructor method
+	 * @param params PHP parameters
+	 */
+	EBPF_PHP_METHOD_DECL(__construct, void);
+
+	/**
+	 * @brief PHP method to attach a kprobe
+	 * @param params PHP parameters
+	 */
+	EBPF_PHP_METHOD_DECL(attach_kprobe, void);
+
+	/**
+	 * @brief PHP method to attach a tracepoint
+	 * @param params PHP parameters
+	 */
+	EBPF_PHP_METHOD_DECL(attach_tracepoint, void);
+
+	/**
+	 * @brief PHP method to attach a raw tracepoint
+	 * @param params PHP parameters
+	 */
+	EBPF_PHP_METHOD_DECL(attach_raw_tracepoint, void);
+
+	/**
+	 * @brief PHP method to attach a kfunc
+	 * @param params PHP parameters
+	 */
+	EBPF_PHP_METHOD_DECL(attach_kfunc, void);
+
+	/**
+	 * @brief PHP method to attach an LSM probe
+	 * @param params PHP parameters
+	 */
+	EBPF_PHP_METHOD_DECL(attach_lsm, void);
+
+	/**
+	 * @brief PHP method to attach a uprobe
+	 * @param params PHP parameters
+	 */
+	EBPF_PHP_METHOD_DECL(attach_uprobe, void);
+
+	/**
+	 * @brief PHP method to detach a kprobe
+	 * @param params PHP parameters
+	 */
+	EBPF_PHP_METHOD_DECL(detach_kprobe, void);
+
+	/**
+	 * @brief PHP method to detach a uprobe
+	 * @param params PHP parameters
+	 */
+	EBPF_PHP_METHOD_DECL(detach_uprobe, void);
+
+	/**
+	 * @brief PHP method to print trace output
+	 * @param params PHP parameters
+	 */
+	EBPF_PHP_METHOD_DECL(trace_print, void);
+
+	/**
+	 * @brief PHP method to get trace fields
+	 * @param params PHP parameters
+	 * @return PHP value containing trace fields
+	 */
+	EBPF_PHP_METHOD_DECL(trace_fields, Php::Value);
+
+	/**
+	 * @brief PHP method to get a BPF table
+	 * @param params PHP parameters
+	 * @return PHP value containing the table
+	 */
+	EBPF_PHP_METHOD_DECL(get_table, Php::Value);
+
+	/**
+	 * @brief PHP method to poll perf buffer
+	 * @param params PHP parameters
+	 */
+	EBPF_PHP_METHOD_DECL(perf_buffer_poll, void);
+
+	/**
+	 * @brief PHP method to get syscall function name
+	 * @param params PHP parameters
+	 * @return PHP value containing the syscall function name
+	 */
+	EBPF_PHP_METHOD_DECL(get_syscall_fnname, Php::Value);
+
+	/**
+	 * @brief PHP method to load a BPF function
+	 * @param params PHP parameters
+	 * @return PHP value containing the loaded function
+	 */
+	EBPF_PHP_METHOD_DECL(load_func, Php::Value);
+
+	/**
+	 * @brief PHP method to attach a raw socket
+	 * @param params PHP parameters
+	 */
+	EBPF_PHP_METHOD_DECL(attach_raw_socket, void);
+};
+
+#endif //SRC_MAIN_H

+ 239 - 0
src/table.cpp

@@ -0,0 +1,239 @@
+/*
+ * 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 "table.h"
+#include <iomanip>
+
+std::string cb_fn;
+
+void PerfEventArrayTable::callbackfn(void *cookie, void *data, int data_size) {
+	Php::Value phpData((const char *) data, data_size);
+	Php::call(cb_fn.c_str(), nullptr, phpData, data_size);
+}
+
+EBPF_PHP_METHOD(PerfEventArrayTable, open_perf_buffer, void) {
+	cb_fn = params[0].stringValue();
+	auto res = this->bpf->open_perf_buffer(this->tb_name, &PerfEventArrayTable::callbackfn);
+	if (!res.ok()) {
+		throw Php::Exception("open_perf_buffer error:" + res.msg());
+	}
+}
+
+int PerfEventArrayTable::perf_buffer_poll(int timeout_ms) {
+	return bpf->poll_perf_buffer(this->tb_name, timeout_ms); // perf_reader_event_read
+}
+
+EBPF_PHP_METHOD(ArrayTable, get_value, Php::Value) {
+	auto index = params[0].numericValue();
+	uint64_t val;
+	auto table = bpf->get_array_table<uint64_t>(tb_name);
+	auto res = table.get_value(index, val);
+	if (!res.ok()) {
+		throw Php::Exception("Get value error in" + std::string(tb_name));
+	}
+	Php::Value phpValue = static_cast<int64_t>(val);
+	return phpValue;
+}
+
+EBPF_PHP_METHOD(ArrayTable, print_log2_hist, Php::Value) {
+	auto header = params[0].stringValue();
+	auto table = bpf->get_array_table<uint64_t>(tb_name);
+	auto vals = table.get_table_offline();
+	auto center_text = [](const std::string &text, int width) -> std::string {
+		int len = static_cast<int>(text.length());
+		if (width <= len) return text;
+		int padding = width - len;
+		int left = padding / 2;
+		int right = padding - left;
+		return std::string(left, ' ') + text + std::string(right, ' ');
+	};
+	auto stars = [](uint64_t val, uint64_t max_val, int width) -> std::string {
+		std::string result;
+		int limit = std::min(width, static_cast<int>((double) val * width / max_val));
+		for (int i = 0; i < limit; ++i) {
+			result += '*';
+		}
+		if (val > max_val && !result.empty()) {
+			result.back() = '+';
+		}
+		return result;
+	};
+
+	int idx_max = -1;
+	uint64_t val_max = 0;
+
+	for (size_t i = 0; i < vals.size(); ++i) {
+		if (vals[i] > 0) {
+			idx_max = static_cast<int>(i);
+			if (vals[i] > val_max) val_max = vals[i];
+		}
+	}
+
+	if (idx_max == -1) {
+		std::cout << "No data to display." << std::endl;
+		return Php::Value{};
+	}
+
+	int stars_max = 40;
+	bool long_format = idx_max > 32;
+
+	int col1_width = long_format ? 41 : 27;
+	int label_width = col1_width - 2;
+
+	std::cout << center_text(header, label_width) << ": count    distribution" << std::endl;
+
+	bool strip_leading_zero = true;
+	for (int i = 1; i <= idx_max; ++i) {
+		uint64_t low = (1ULL << (i - 1));
+		uint64_t high = (1ULL << i) - 1;
+		if (low == high) low -= 1;
+
+		uint64_t val = vals[i];
+		if (strip_leading_zero && val == 0) {
+			continue;
+		}
+		strip_leading_zero = false;
+
+		std::string bar = stars(val, val_max, stars_max);
+		if (long_format) {
+			std::cout << std::right << std::setw(20) << low << " -> "
+			          << std::left << std::setw(20) << high << " : "
+			          << std::setw(8) << val << "|" << bar << "|" << std::endl;
+		} else {
+			std::cout << std::right << std::setw(10) << low << " -> "
+			          << std::left << std::setw(10) << high << " : "
+			          << std::setw(8) << val << "|" << bar << "|" << std::endl;
+		}
+	}
+
+	return Php::Value{};
+}
+
+EBPF_PHP_METHOD(ArrayTable, print_linear_hist, Php::Value) {
+	auto header = params[0].stringValue();
+	auto tb_name = this->tb_name;
+	auto bpf = this->bpf;
+
+	auto table = bpf->get_array_table<uint64_t>(tb_name);
+	auto res = table.get_table_offline();
+
+	if (res.empty()) {
+		std::cout << "empty histogram" << std::endl;
+		return Php::Value{};
+	}
+
+	uint64_t max_val = *std::max_element(res.begin(), res.end());
+	if (max_val == 0) max_val = 1;
+	std::cout << "    " << std::left << std::setw(12) << header
+	          << std::setw(10) << ": count"
+	          << " distribution\n";
+
+
+	int stars_max = 40;
+	for (size_t i = 0; i < res.size(); ++i) {
+		if (res[i] == 0) continue;
+
+		std::string bar;
+		int limit = std::min(stars_max, static_cast<int>((double) res[i] * stars_max / max_val));
+		for (int j = 0; j < limit; ++j) bar += '*';
+		if (res[i] > max_val && !bar.empty()) bar.back() = '+';
+
+		std::string count_str = ": " + std::to_string(res[i]);
+		std::cout << "    " << std::left << std::setw(12) << i
+		          << std::setw(10) << count_str
+		          << "|" << bar << "|\n";
+	}
+
+	return Php::Value{};
+}
+
+EBPF_PHP_METHOD(StackTraceTable, get_values, Php::Value) {
+	auto stack_id = params[0].numericValue();
+	int pid = -1;
+	if (params.size() > 1) {
+		auto param_pid = params[1].numericValue();
+		if (param_pid >= 0) pid = param_pid;
+	}
+	auto table = bpf->get_stack_table(tb_name);
+
+	auto symbols = table.get_stack_symbol((int) stack_id, (int) pid);
+	std::vector<std::string> result;
+	for (const auto &str: symbols) {
+		result.push_back(Php::Value(str));
+	}
+	return Php::Array(result);
+}
+
+//std::string trim_braces(const std::string &input) {
+//	std::string result = input;
+//
+//	if (result.size() >= 2 && result.substr(0, 2) == "{ ") {
+//		result = result.substr(2);
+//	}
+//
+//	if (result.size() >= 2 && result.substr(result.size() - 2) == " }") {
+//		result = result.substr(0, result.size() - 2);
+//	}
+//
+//	return result;
+//}
+
+EBPF_PHP_METHOD(HashTable, get_values, Php::Value) {
+	Php::Array result;
+	std::vector<std::pair<std::vector<char>, std::vector<char>>> entries;
+
+	auto raw_table = bpf->get_table(tb_name);
+	auto status = raw_table.get_table_offline_ptr(entries);
+	if (status.code() != 0) {
+		return result;
+	}
+
+	int i = 0;
+
+	for (const auto &[key_data, val_data]: entries) {
+		Php::Value phpKeyData(key_data.data(), key_data.size());
+		Php::Value phpValData(val_data.data(), val_data.size());
+
+		Php::Array entry;
+		entry.set("key", phpKeyData);
+		entry.set("value", phpValData);
+
+		result.set(i++, entry);
+	}
+
+	return result;
+}
+
+EBPF_PHP_METHOD(HashTable, clear, void) {
+	auto raw_table = bpf->get_table(tb_name);
+	raw_table.clear_table_non_atomic();
+}
+
+EBPF_PHP_METHOD(PerCpuArrayTable, sum_value, Php::Value) {
+	auto index = params[0].numericValue();
+	std::vector<unsigned long> val;
+	auto table = bpf->get_percpu_array_table<uint64_t>(tb_name);
+	auto res = table.get_value(index, val);
+	if (!res.ok()) {
+		throw Php::Exception("Get value error in" + std::string(tb_name));
+	}
+	unsigned long long sum = 0;
+	for (const auto &v: val) {
+		sum += v;
+	}
+	Php::Value phpValue = static_cast<int64_t>(sum);
+	return phpValue;
+}

+ 46 - 24
table.h → src/table.h

@@ -1,8 +1,27 @@
-#ifndef PHPCPP_HELLOWORLD_TABLE_H
-#define PHPCPP_HELLOWORLD_TABLE_H
+/*
+ * 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.
+ */
+
+#ifndef SRC_TABLE_H
+#define SRC_TABLE_H
+
+#include "common.h"
+#include "bpf_module.h"
+#include "php_ebpf.h"
 #include <iostream>
 #include <phpcpp.h>
-#include "bpf_module.h"
 #include "BPF.h"
 
 
@@ -18,21 +37,6 @@ public:
 
 	void printName();
 
-//	void printName() { std::cout << "Table name: " << tb_name << std::endl; }
-
-//	Php::Value Value(Php::Parameters &param) {
-//		auto index = param[0].numericValue();
-//		// todo val type
-//		uint64_t val;
-//		auto table = bpf->get_array_table<uint64_t>(tb_name);
-//		auto res = table.get_value(index, val);
-//		if (!res.ok()) {
-//			throw Php::Exception("Get value error in" + std::string(tb_name));
-//		}
-//		Php::Value phpValue = static_cast<int64_t>(val);
-//		return phpValue;
-//	}
-
 };
 
 
@@ -41,23 +45,38 @@ private:
 	std::string cb;
 public:
 	using BaseTable::BaseTable;
+
 	virtual ~PerfEventArrayTable() = default;
+
 	static void callbackfn(void *cookie, void *data, int data_size);
-	void php_open_perf_buffer(Php::Parameters &params);
+
+//	void php_open_perf_buffer(Php::Parameters &params);
+	// php_open_perf_buffer
+	EBPF_PHP_METHOD_DECL(open_perf_buffer, void);
+
+
 	int perf_buffer_poll(int timeout_ms);
 };
 
 class HashTable : public BaseTable {
 public:
 	using BaseTable::BaseTable;
-	Php::Value php_get_values();
-	void php_clear();
+
+	EBPF_PHP_METHOD_DECL(get_values, Php::Value);
+
+	EBPF_PHP_METHOD_DECL(clear, void);
+
 };
 
 class ArrayTable : public BaseTable {
 public:
 	using BaseTable::BaseTable;
-	Php::Value php_get_value(Php::Parameters &param);
+
+	EBPF_PHP_METHOD_DECL(get_value, Php::Value);
+
+	EBPF_PHP_METHOD_DECL(print_log2_hist, Php::Value);
+
+	EBPF_PHP_METHOD_DECL(print_linear_hist, Php::Value);
 };
 
 class ProgArrayTable : public BaseTable {
@@ -82,7 +101,8 @@ public:
 class PerCpuArrayTable : public BaseTable {
 public:
 	using BaseTable::BaseTable;
-	Php::Value php_sum_value(Php::Parameters &param);
+
+	EBPF_PHP_METHOD_DECL(sum_value, Php::Value);
 };
 
 class LpmTrieTable : public BaseTable {
@@ -93,6 +113,7 @@ public:
 class StackTraceTable : public BaseTable {
 public:
 	using BaseTable::BaseTable;
+
 	Php::Value php_get_values(Php::Parameters &param);
 };
 
@@ -145,4 +166,5 @@ class RingBufTable : public BaseTable {
 public:
 	using BaseTable::BaseTable;
 };
-#endif //PHPCPP_HELLOWORLD_TABLE_H
+
+#endif //SRC_TABLE_H

+ 0 - 120
table.cpp

@@ -1,120 +0,0 @@
-#include "table.h"
-
-//BaseTable::BaseTable(ebpf::BPF *bpf_instance, const std::string &tb_name)
-//: bpf(bpf_instance), tb_name(tb_name){}
-
-void BaseTable::printName() {
-	std::cout << "Table name: " << tb_name << std::endl;
-}
-
-std::string cb_fn;
-
-void PerfEventArrayTable::callbackfn(void *cookie, void *data, int data_size) {
-	Php::Value phpData((const char *) data, data_size);
-	Php::call(cb_fn.c_str(), nullptr, phpData, data_size);
-}
-
-void PerfEventArrayTable::php_open_perf_buffer(Php::Parameters &params) {
-	cb_fn = params[0].stringValue();
-	auto res = this->bpf->open_perf_buffer(this->tb_name, &PerfEventArrayTable::callbackfn);
-	if (!res.ok()) {
-		throw Php::Exception("open_perf_buffer error:" + res.msg());
-	}
-}
-
-int PerfEventArrayTable::perf_buffer_poll(int timeout_ms) {
-	return bpf->poll_perf_buffer(this->tb_name, timeout_ms); // perf_reader_event_read
-}
-
-Php::Value ArrayTable::php_get_value(Php::Parameters &param) {
-	auto index = param[0].numericValue();
-	// todo val type
-	uint64_t val;
-	auto table = bpf->get_array_table<uint64_t>(tb_name);
-	auto res = table.get_value(index, val);
-	if (!res.ok()) {
-		throw Php::Exception("Get value error in" + std::string(tb_name));
-	}
-	Php::Value phpValue = static_cast<int64_t>(val);
-	return phpValue;
-}
-
-Php::Value StackTraceTable::php_get_values(Php::Parameters &param) {
-	auto stack_id = param[0].numericValue();
-	int pid = -1;
-	if (param.size() > 1) {
-		auto param_pid = param[1].numericValue();
-		if (param_pid >= 0) pid = param_pid;
-	}
-	auto table = bpf->get_stack_table(tb_name);
-
-	auto symbols = table.get_stack_symbol((int) stack_id, (int) pid);
-	std::vector<std::string> result;
-	for (const auto &str: symbols) {
-		result.push_back(Php::Value(str));
-	}
-	return Php::Array(result);
-}
-
-std::string trim_braces(const std::string& input) {
-	std::string result = input;
-
-	if (result.size() >= 2 && result.substr(0, 2) == "{ ") {
-		result = result.substr(2);
-	}
-
-	if (result.size() >= 2 && result.substr(result.size() - 2) == " }") {
-		result = result.substr(0, result.size() - 2);
-	}
-
-	return result;
-}
-
-Php::Value HashTable::php_get_values() {
-	Php::Array result;
-	std::vector<std::pair<std::vector<char>, std::vector<char>>> entries;
-
-	auto raw_table = bpf->get_table(tb_name);
-	auto status = raw_table.get_table_offline_ptr(entries);
-	if (status.code() != 0) {
-		return result;
-	}
-
-	int i = 0;
-
-	for (const auto& [key_data, val_data] : entries) {
-		Php::Value phpKeyData(key_data.data(), key_data.size());
-		Php::Value phpValData(val_data.data(), val_data.size());
-
-		Php::Array entry;
-		entry.set("key", phpKeyData);
-		entry.set("value", phpValData);
-
-		result.set(i++, entry);
-	}
-
-	return result;
-}
-
-void HashTable::php_clear() {
-	auto raw_table = bpf->get_table(tb_name);
-	raw_table.clear_table_non_atomic();
-}
-
-Php::Value PerCpuArrayTable::php_sum_value(Php::Parameters &param) {
-	auto index = param[0].numericValue();
-	std::vector<unsigned long> val;
-	auto table = bpf->get_percpu_array_table<uint64_t>(tb_name);
-	auto res = table.get_value(index, val);
-	if (!res.ok()) {
-		throw Php::Exception("Get value error in" + std::string(tb_name));
-	}
-	unsigned long long sum = 0;
-	for (const auto &v: val) {
-		sum += v;
-	}
-	Php::Value phpValue = static_cast<int64_t>(sum);
-	return phpValue;
-}
-
-

+ 40 - 85
test.php

@@ -1,102 +1,57 @@
 <?php
 
-if ($argc !== 2) {
-    echo "Usage: php {$argv[0]} <net_interface>\n";
-    echo "Example: php {$argv[0]} eno1\n";
-    exit(1);
-}
-
-$INTERFACE = $argv[1];
-define("OUTPUT_INTERVAL", 1);
-
 $bpf_text = <<<EOT
 #include <uapi/linux/ptrace.h>
-#include <net/sock.h>
-#include <bcc/proto.h>
-#include <linux/bpf.h>
-
-#define IP_TCP 6
-#define IP_UDP 17
-#define IP_ICMP 1
-#define ETH_HLEN 14
-
-BPF_PERF_OUTPUT(skb_events);
-BPF_HASH(packet_cnt, u64, long, 256);
+#include <linux/blk-mq.h>
 
-int packet_monitor(struct __sk_buff *skb) {
-    u8 *cursor = 0;
-    u32 saddr, daddr;
-    long* count = 0;
-    long one = 1;
-    u64 pass_value = 0;
+BPF_HISTOGRAM(dist);
+BPF_HISTOGRAM(dist_linear);
 
-    struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet));
-    struct ip_t *ip = cursor_advance(cursor, sizeof(*ip));
-    if (ip->ver != 4)
-        return 0;
-    if (ip->nextp != IP_TCP)
-    {
-        if (ip -> nextp != IP_UDP)
-        {
-            if (ip -> nextp != IP_ICMP)
-                return 0;
-        }
-    }
-
-    saddr = ip -> src;
-    daddr = ip -> dst;
-
-    pass_value = saddr;
-    pass_value = pass_value << 32;
-    pass_value = pass_value + daddr;
-
-    count = packet_cnt.lookup(&pass_value);
-    if (count)  // check if this map exists
-        *count += 1;
-    else        // if the map for the key doesn't exist, create one
-        {
-            packet_cnt.update(&pass_value, &one);
-        }
-    return -1;
+int trace_req_done(struct pt_regs *ctx, struct request *req)
+{
+    dist.increment(bpf_log2l(req->__data_len / 1024));
+    dist_linear.increment(req->__data_len / 1024);
+    return 0;
 }
-
 EOT;
 
 $b = new Bpf(["text" => $bpf_text]);
-$func = $b->load_func("packet_monitor", Bpf::SOCKET_FILTER);
-$b->attach_raw_socket($func, $INTERFACE);
 
-$packet_cnt = $b->get_table("packet_cnt");
-
-function decimal_to_ip($int) {
-    return long2ip($int);
+// Attach kprobe based on kernel version
+if ($b->get_kprobe_functions('__blk_account_io_done')) {
+    // __blk_account_io_done is available before kernel v6.4
+    $b->attach_kprobe('__blk_account_io_done', 'trace_req_done');
+} elseif ($b->get_kprobe_functions('blk_account_io_done')) {
+    // blk_account_io_done is traceable (not inline) before v5.16
+    $b->attach_kprobe('blk_account_io_done', 'trace_req_done');
+} else {
+    $b->attach_kprobe('blk_mq_end_request', 'trace_req_done');
 }
 
-while (true) {
-    sleep(OUTPUT_INTERVAL);
-
-    $items = $packet_cnt->values();
-    $time = date("Y-m-d H:i:s");
-
-    if (count($items) > 0) {
-        echo "\nCurrent packet stats:\n";
-    }
+echo "Tracing... Hit Ctrl-C to end.\n";
 
-    foreach ($items as $entry) {
-        $key = unpack("Q", $entry["key"])[1];
-        $val = unpack("l", $entry["value"])[1];
+// Set up signal handler for Ctrl-C
+pcntl_signal(SIGINT, "signalHandler");
+pcntl_async_signals(true);
 
-        $src = ($key >> 32) & 0xFFFFFFFF;
-        $dst = $key & 0xFFFFFFFF;
+// Keep running until interrupted
+while (true) {
+    sleep(99999999);
+}
 
-        echo sprintf(
-            "source: %s -> destination: %s count: %d time: %s\n",
-            decimal_to_ip($src),
-            decimal_to_ip($dst),
-            $val,
-            $time
-        );
+function signalHandler($signo)
+{
+    global $b;
+    switch ($signo) {
+        case SIGINT:
+            echo "\n";
+            echo "log2 histogram\n";
+            echo "~~~~~~~~~~~~~~\n";
+            $b->dist->print_log2_hist("kbytes");
+
+            echo "\nlinear histogram\n";
+            echo "~~~~~~~~~~~~~~~~\n";
+            $b->dist_linear->print_linear_hist("kbytes");
+            exit(0);
     }
-
-    $packet_cnt->clear();
-}
+}

+ 61 - 0
tests/hash_table_test.php

@@ -0,0 +1,61 @@
+<?php
+/*
+ * 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.
+ */
+
+$prog = <<<EOT
+BPF_HASH(counts, u32);
+
+int kprobe__do_sys_openat2(struct pt_regs *ctx) {
+    u32 key = 0;
+    u64 *val, zero = 0;
+    
+    val = counts.lookup_or_init(&key, &zero);
+    (*val)++;
+    
+    bpf_trace_printk("openat2 called %d times\\n", *val);
+    return 0;
+}
+EOT;
+
+try {
+    $bpf = new Bpf(["text" => $prog]);
+    echo "BPF program loaded successfully\n";
+    
+    // Get the hash table
+    $counts = $bpf->get_table("counts");
+    
+    // Start monitoring
+    echo "Monitoring openat2 syscalls with counter...\n";
+    while (true) {
+        try {
+            list($task, $pid, $cpu, $flags, $ts, $msg) = $bpf->trace_fields();
+            printf("%-18.9f %-16s %-6d %s\n", $ts, $task, $pid, $msg);
+            
+            // Print current count
+            $values = $counts->values();
+            foreach ($values as $key => $value) {
+                echo "Total calls: " . $value . "\n";
+            }
+            
+            flush();
+        } catch (Exception $e) {
+            continue;
+        }
+    }
+} catch (Exception $e) {
+    echo "Error: " . $e->getMessage() . "\n";
+    exit(1);
+} 

+ 43 - 0
tests/kprobe_test.php

@@ -0,0 +1,43 @@
+<?php
+/*
+ * 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.
+ */
+
+$prog = <<<EOT
+int kprobe__do_sys_openat2(struct pt_regs *ctx) {
+    bpf_trace_printk("openat2 called\\n");
+    return 0;
+}
+EOT;
+
+try {
+    $bpf = new Bpf(["text" => $prog]);
+    echo "BPF program loaded successfully\n";
+    
+    // Start monitoring
+    echo "Monitoring openat2 syscalls...\n";
+    while (true) {
+        try {
+            list($task, $pid, $cpu, $flags, $ts, $msg) = $bpf->trace_fields();
+            printf("%-18.9f %-16s %-6d %s\n", $ts, $task, $pid, $msg);
+            flush();
+        } catch (Exception $e) {
+            continue;
+        }
+    }
+} catch (Exception $e) {
+    echo "Error: " . $e->getMessage() . "\n";
+    exit(1);
+} 

+ 43 - 0
tests/tracepoint_test.php

@@ -0,0 +1,43 @@
+<?php
+/*
+ * 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.
+ */
+
+$prog = <<<EOT
+TRACEPOINT_PROBE(syscalls, sys_enter_openat) {
+    bpf_trace_printk("openat called\\n");
+    return 0;
+}
+EOT;
+
+try {
+    $bpf = new Bpf(["text" => $prog]);
+    echo "BPF program loaded successfully\n";
+    
+    // Start monitoring
+    echo "Monitoring openat syscalls...\n";
+    while (true) {
+        try {
+            list($task, $pid, $cpu, $flags, $ts, $msg) = $bpf->trace_fields();
+            printf("%-18.9f %-16s %-6d %s\n", $ts, $task, $pid, $msg);
+            flush();
+        } catch (Exception $e) {
+            continue;
+        }
+    }
+} catch (Exception $e) {
+    echo "Error: " . $e->getMessage() . "\n";
+    exit(1);
+}