Carl 9 meses atrás
pai
commit
976629bdc9

+ 18 - 6
example/tracing/mallocstacks.php

@@ -47,21 +47,33 @@ function signalHandler($signo)
         case SIGINT:
             $calls        = $ebpf->get_table("calls");
             $stack_traces = $ebpf->get_table("stack_traces");
+            $calls_vals   = $calls->values();
 
-            $calls_vals = $calls->values();
-            arsort($calls_vals);
+            $mapped = array_map(function ($val) {
+                return [
+                    'stack_id' => unpack("L", $val['key'])[1],
+                    'value'    => unpack("Q", $val['value'])[1]
+                ];
+            }, $calls_vals);
+
+            usort($mapped, fn($first, $sec) => $sec['value'] <=> $first['value']);
+
+            foreach ($mapped as $entry) {
+                $stack_id = $entry['stack_id'];
+                $value = $entry['value'];
 
-            foreach ($calls_vals as $key => $value) {
                 printf("%d bytes allocated at:\n", $value);
 
-                if ($key > 0) {
-                    $a = $stack_traces->values($key, $pid);
-                    foreach ($a as $addr) {
+                if ($stack_id > 0) {
+                    $stack = $stack_traces->values($stack_id, $pid);
+                    foreach ($stack as $addr) {
                         printf("\t%s\n", $addr);
                     }
                     printf("    %d\n\n", $value);
                 }
             }
+
             exit(0);
     }
 }
+

+ 50 - 0
example/tracing/setuid_monitor.php

@@ -0,0 +1,50 @@
+<?php
+$prog = <<<EOT
+#include <linux/sched.h>
+
+// define output data structure in C
+struct data_t {
+    u32 pid;
+    u32 uid;
+    u64 ts;
+    char comm[TASK_COMM_LEN];
+};
+BPF_PERF_OUTPUT(events);
+
+TRACEPOINT_PROBE(syscalls, sys_enter_setuid) {
+    struct data_t data = {};
+
+    // Check /sys/kernel/debug/tracing/events/syscalls/sys_enter_setuid/format
+    // for the args format
+    data.uid = args->uid;
+    data.ts = bpf_ktime_get_ns();
+    data.pid = bpf_get_current_pid_tgid();
+    bpf_get_current_comm(&data.comm, sizeof(data.comm));
+    events.perf_submit(args, &data, sizeof(data));
+
+    return 0;
+}
+EOT;
+
+# load BPF program
+$b    = new Ebpf($prog);
+
+# header
+printf("%-14s %-12s %-6s %s\n", "TIME(s)", "COMMAND", "PID", "UID");
+
+# process event
+function print_event($cpu, $data, $size) {
+    $event = unpack("Lpid/Luid/Qts/A16comm", $data);
+    printf("%-14.3f %-12s %-6d %d\n", $event['ts'] / 1000000000, $event['comm'], $event['pid'], $event['uid']);
+}
+
+# loop with callback to print_event
+$b->events->open_perf_buffer("print_event");
+
+while (true) {
+    try {
+        $b->perf_buffer_poll();
+    } catch (Exception $e) {
+        exit();
+    }
+}

+ 81 - 0
example/tracing/stacksnoop.php

@@ -0,0 +1,81 @@
+<?php
+
+$function = $argv[1] ?? null;
+$pid = null;
+$offset = false;
+$verbose = false;
+
+foreach ($argv as $i => $arg) {
+    if ($arg === "-p") $pid = $argv[$i + 1];
+    if ($arg === "-s") $offset = true;
+    if ($arg === "-v") $verbose = true;
+}
+
+if (!$function) {
+    echo "USAGE: php stacksnoop.php [-p PID] [-s] [-v] function_name\n";
+    exit(1);
+}
+
+$filter = $pid ? "if (pid != $pid) { return; }" : "";
+
+$prog = <<<EOT
+#include <uapi/linux/ptrace.h>
+#include <linux/sched.h>
+
+struct data_t {
+    u64 stack_id;
+    u32 pid;
+    char comm[TASK_COMM_LEN];
+};
+
+BPF_STACK_TRACE(stack_traces, 128);
+BPF_PERF_OUTPUT(events);
+
+void trace_stack(struct pt_regs *ctx) {
+    u32 pid = bpf_get_current_pid_tgid() >> 32;
+    $filter
+    struct data_t data = {};
+    data.stack_id = stack_traces.get_stackid(ctx, 0);
+    data.pid = pid;
+    bpf_get_current_comm(&data.comm, sizeof(data.comm));
+    events.perf_submit(ctx, &data, sizeof(data));
+}
+EOT;
+
+$b = new Ebpf($prog);
+$b->attach_kprobe($function, "trace_stack");
+
+$stack_traces = $b->get_table("stack_traces");
+$start_ts = microtime(true);
+
+if ($verbose) {
+    printf("%-18s %-12s %-6s %-3s %s\n", "TIME(s)", "COMM", "PID", "CPU", "FUNCTION");
+} else {
+    printf("%-18s %s\n", "TIME(s)", "FUNCTION");
+}
+
+function print_event($cpu, $data, $size) {
+    global $b, $function, $offset, $verbose, $start_ts, $stack_traces;
+
+    $event = unpack("Qstack_id/Lpid/A16comm", $data);
+    $ts = microtime(true) - $start_ts;
+
+    if ($verbose) {
+        printf("%-18.9f %-12s %-6d %-3d %s\n", $ts, $event["comm"], $event["pid"], $cpu, $function);
+    } else {
+        printf("%-18.9f %s\n", $ts, $function);
+    }
+    foreach ($stack_traces->values($event['stack_id']) as $fn) {
+        echo "\t$fn".PHP_EOL;
+    }
+    echo PHP_EOL;
+}
+
+$b->events->open_perf_buffer("print_event");
+while (true) {
+    try {
+        $b->perf_buffer_poll();
+    } catch (Exception $e) {
+        exit();
+    }
+}

+ 54 - 0
example/tracing/strlen_count.php

@@ -0,0 +1,54 @@
+<?php
+$bpf_text = <<<EOT
+#include <uapi/linux/ptrace.h>
+
+struct key_t {
+    char c[80];
+};
+BPF_HASH(counts, struct key_t);
+
+int count(struct pt_regs *ctx) {
+    if (!PT_REGS_PARM1(ctx))
+        return 0;
+
+    struct key_t key = {};
+    u64 zero = 0, *val;
+
+    bpf_probe_read_user(&key.c, sizeof(key.c), (void *)PT_REGS_PARM1(ctx));
+    // could also use `counts.increment(key)`
+    val = counts.lookup_or_try_init(&key, &zero);
+    if (val) {
+      (*val)++;
+    }
+    return 0;
+};
+EOT;
+
+$b = new Ebpf($bpf_text);
+$b->attach_uprobe("c", "strlen", "count");
+
+echo "Tracing strlen()... Hit Ctrl-C to end.\n";
+pcntl_signal(SIGINT, "signalHandler");
+pcntl_async_signals(true);
+
+# sleep until Ctrl-C
+while (true) {
+    sleep(99999999);
+}
+
+function signalHandler($signo)
+{
+    global $b;
+    switch ($signo) {
+        case SIGINT:
+            echo sprintf("%10s %s\n", "COUNT", "STRING");
+            $counts        = $b->get_table("counts");
+            $vals = $counts->values();
+            foreach ($vals as $v) {
+                $k = unpack("A80c", $v['key']);
+                $v = unpack("Qval", $v['value']);
+                printf("%10d \"%s\"\n", $v['val'],$k['c']);
+            }
+            exit(0);
+    }
+}

+ 25 - 20
main.cpp

@@ -34,7 +34,7 @@ private:
 	ebpf::BPF bpf;
 	void *mod;
 	Php::Object _perf_event;
-	PerfEventArrayTable *_class_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) {
@@ -65,7 +65,10 @@ private:
 				bpf.attach_kprobe(kernel_func, fn_name, 0, BPF_PROBE_RETURN);
 			} else if (fn_name.rfind("tracepoint__", 0) == 0) {
 				std::string tp_name = fn_name.substr(12);
-				std::replace(tp_name.begin(), tp_name.end(), '_', ':');
+				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);
@@ -418,11 +421,11 @@ public:
 		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 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);
@@ -434,8 +437,10 @@ public:
 			case BPF_MAP_TYPE_PROG_ARRAY:
 				return Php::Object("ProgArrayTable", new ProgArrayTable(&this->bpf, table_name));
 			case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
-				this->_class_perf_event = new PerfEventArrayTable(&this->bpf, table_name);
-				return Php::Object("PerfEventArrayTable", this->_class_perf_event);
+				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:
@@ -481,11 +486,16 @@ public:
 
 	void php_perf_buffer_poll(Php::Parameters &params) {
 
-		if (!this->_class_perf_event) {
+		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 = this->_class_perf_event->perf_buffer_poll(timeout_ms);
+		int res = _class_perf_event->perf_buffer_poll(timeout_ms);
 		if (res < 0) {
 			throw Php::Exception("perf buffer poll error.");
 		}
@@ -561,14 +571,10 @@ PHPCPP_EXPORT void *get_module() {
 			Php::ByVal("options", Php::Type::Array, false),
 	});
 
-//	ebpf_class.method<&EbpfExtension::php_open_perf_buffer>("open_perf_buffer", {
-//			Php::ByVal("callback", Php::Type::Callable),
+//	ebpf_class.method<&EbpfExtension::php_perf_event>("perf_event", {
+//			Php::ByVal("ev_name", Php::Type::String),
 //	});
 
-	ebpf_class.method<&EbpfExtension::php_perf_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),
 	});
@@ -599,8 +605,7 @@ PHPCPP_EXPORT void *get_module() {
 
 	/*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", {});
 	/*ArrayTable*/
 	Php::Class<ArrayTable> ebpf_array_table_cls("ArrayTable");
 	ebpf_array_table_cls.method<&ArrayTable::php_get_value>("get_value", {
@@ -626,7 +631,7 @@ PHPCPP_EXPORT void *get_module() {
 	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),
+			Php::ByVal("pid", Php::Type::Numeric, false),
 	});
 
 	/*LruHashTable*/

+ 38 - 5
table.cpp

@@ -41,7 +41,11 @@ Php::Value ArrayTable::php_get_value(Php::Parameters &param) {
 
 Php::Value StackTraceTable::php_get_values(Php::Parameters &param) {
 	auto stack_id = param[0].numericValue();
-	auto pid = param[1].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);
@@ -52,12 +56,41 @@ Php::Value StackTraceTable::php_get_values(Php::Parameters &param) {
 	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;
-	auto table = bpf->get_hash_table<uint64_t, uint64_t>(tb_name);
-	auto entries = table.get_table_offline();
-	for (const auto &[key, value]: entries) {
-		result[key] = Php::Value(static_cast<int64_t>(value));
+	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;

+ 47 - 35
test.php

@@ -1,42 +1,54 @@
 <?php
-function cb($cpu,$data,$size) {
-    $event = unpack("Qcpu/Qts/Qmagic/A16msg", $data);
-    if ($event === false) {
-        echo "error.\n";
-        return;
+$bpf_text = <<<EOT
+#include <uapi/linux/ptrace.h>
+
+struct key_t {
+    char c[80];
+};
+BPF_HASH(counts, struct key_t);
+
+int count(struct pt_regs *ctx) {
+    if (!PT_REGS_PARM1(ctx))
+        return 0;
+
+    struct key_t key = {};
+    u64 zero = 0, *val;
+
+    bpf_probe_read_user(&key.c, sizeof(key.c), (void *)PT_REGS_PARM1(ctx));
+    // could also use `counts.increment(key)`
+    val = counts.lookup_or_try_init(&key, &zero);
+    if (val) {
+      (*val)++;
     }
-    printf("[%d] %.6f: %x %s\n", $event['cpu'], $event['ts'] / 1000000, $event['magic'],$event['msg']);
-}
-$prog = <<<EOT
-BPF_PERF_OUTPUT(events);
-BPF_ARRAY(counters, u64, 10);
-int do_sys_clone(void *ctx) {
-  struct {
-    u64 cpu;
-    u64 ts;
-    u64 magic;
-    char msg[16];
-  } data = {bpf_get_smp_processor_id(),bpf_ktime_get_ns(), 0x12345678,"Hello, world!"};
-  int rc;
-  if ((rc = events.perf_submit(ctx, &data, sizeof(data))) < 0)
-    bpf_trace_printk("perf_output failed: %d\\n", rc);
-  int zero = 0;
-  u64 *val = counters.lookup(&zero);
-  if (val) lock_xadd(val, 1);
-  return 0;
-}
+    return 0;
+};
 EOT;
 
-$ebpf = new Ebpf($prog);
-$ebpf->attach_kprobe("blk_account_io_done","do_sys_clone");
-$ebpf->perf_event("events")->open_perf_buffer("cb");
-echo("Tracing... Hit Ctrl-C to end.\n");
+$b = new Ebpf($bpf_text);
+$b->attach_uprobe("c", "strlen", "count");
+
+echo "Tracing strlen()... Hit Ctrl-C to end.\n";
+pcntl_signal(SIGINT, "signalHandler");
+pcntl_async_signals(true);
 
+# sleep until Ctrl-C
 while (true) {
-    try {
-        $ebpf->perf_buffer_poll();
-        flush();
-    } catch (Exception $e) {
-        exit;
+    sleep(99999999);
+}
+
+function signalHandler($signo)
+{
+    global $b;
+    switch ($signo) {
+        case SIGINT:
+            echo sprintf("%10s %s\n", "COUNT", "STRING");
+            $counts        = $b->get_table("counts");
+            $vals = $counts->values();
+            foreach ($vals as $v) {
+                $k = unpack("A80c", $v['key']);
+                $v = unpack("Qval", $v['value']);
+                printf("%10d \"%s\"\n", $v['val'],$k['c']);
+            }
+            exit(0);
     }
-}
+}