Kaynağa Gözat

Initial commit (add demo)

Initial commit (add demo)

Initial commit (demo)
Carl 8 ay önce
ebeveyn
işleme
2afbb1cf84

+ 200 - 3
ebpf.cpp

@@ -36,6 +36,7 @@ extern "C" {
 #include <fstream>
 #include <sstream>
 #include <regex>
+#include <iomanip>
 
 /* Handlers */
 zend_object_handlers bpf_object_handlers;
@@ -1297,15 +1298,211 @@ PHP_METHOD (HashTable, clear) {
 }
 
 PHP_METHOD (ArrayTable, get_value) {
-	return;
+	zend_long index;
+	zval name_rv;
+	zval *name_zv;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &index) == FAILURE) {
+		RETURN_NULL();
+	}
+
+	name_zv = zend_read_property(
+			Z_OBJCE_P(getThis()),
+			getThis(),
+			"name",
+			sizeof("name") - 1,
+			1,
+			&name_rv
+	);
+
+	if (!name_zv || Z_TYPE_P(name_zv) != IS_STRING) {
+		zend_throw_error(NULL, "Invalid or missing name property");
+		RETURN_NULL();
+	}
+
+	sub_object *obj = table_fetch_object(Z_OBJ_P(getThis()));
+	if (!obj || !obj->bpf) {
+		zend_throw_error(NULL, "Invalid object state");
+		RETURN_NULL();
+	}
+
+	try {
+		auto table = obj->bpf->get_array_table<uint64_t>(Z_STRVAL_P(name_zv));
+		uint64_t val;
+		auto res = table.get_value(index, val);
+		if (!res.ok()) {
+			zend_throw_error(NULL, "Get value error in %s", Z_STRVAL_P(name_zv));
+			RETURN_NULL();
+		}
+		RETURN_LONG(val);
+	} catch (const std::exception &e) {
+		zend_throw_error(NULL, "Exception: %s", e.what());
+		RETURN_NULL();
+	}
 }
 
 PHP_METHOD (ArrayTable, print_log2_hist) {
-	return;
+	char *header;
+	size_t header_len;
+	zval name_rv;
+	zval *name_zv;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &header, &header_len) == FAILURE) {
+		RETURN_NULL();
+	}
+
+	name_zv = zend_read_property(
+			Z_OBJCE_P(getThis()),
+			getThis(),
+			"name",
+			sizeof("name") - 1,
+			1,
+			&name_rv
+	);
+
+	if (!name_zv || Z_TYPE_P(name_zv) != IS_STRING) {
+		zend_throw_error(NULL, "Invalid or missing name property");
+		RETURN_NULL();
+	}
+
+	sub_object *obj = table_fetch_object(Z_OBJ_P(getThis()));
+	if (!obj || !obj->bpf) {
+		zend_throw_error(NULL, "Invalid object state");
+		RETURN_NULL();
+	}
+
+	auto table = obj->bpf->get_array_table<uint64_t>(Z_STRVAL_P(name_zv));
+
+	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_FALSE
+	}
+
+	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_FALSE
 }
 
 PHP_METHOD (ArrayTable, print_linear_hist) {
-	return;
+	char *header;
+	size_t header_len;
+	zval name_rv;
+	zval *name_zv;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &header, &header_len) == FAILURE) {
+		RETURN_NULL();
+	}
+
+	name_zv = zend_read_property(
+			Z_OBJCE_P(getThis()),
+			getThis(),
+			"name",
+			sizeof("name") - 1,
+			1,
+			&name_rv
+	);
+
+	if (!name_zv || Z_TYPE_P(name_zv) != IS_STRING) {
+		zend_throw_error(NULL, "Invalid or missing name property");
+		RETURN_NULL();
+	}
+
+	sub_object *obj = table_fetch_object(Z_OBJ_P(getThis()));
+	if (!obj || !obj->bpf) {
+		zend_throw_error(NULL, "Invalid object state");
+		RETURN_NULL();
+	}
+
+	auto table = obj->bpf->get_array_table<uint64_t>(Z_STRVAL_P(name_zv));
+	auto res = table.get_table_offline();
+
+	if (res.empty()) {
+		std::cout << "empty histogram" << std::endl;
+		RETURN_FALSE
+	}
+
+	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";
+	}
 }
 
 PHP_METHOD (PerCpuArrayTable, sum_value) {

+ 10 - 0
examples/hello_world.php

@@ -0,0 +1,10 @@
+<?php
+
+$prog = <<<EOT
+int kprobe__sys_clone(void *ctx) {
+    bpf_trace_printk("Hello, World!\\n");
+    return 0;
+}
+EOT;
+
+(new Bpf(["text" => $prog]))-> trace_print();

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

+ 102 - 0
examples/networking/net_monitor.php

@@ -0,0 +1,102 @@
+<?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);
+
+int packet_monitor(struct __sk_buff *skb) {
+    u8 *cursor = 0;
+    u32 saddr, daddr;
+    long* count = 0;
+    long one = 1;
+    u64 pass_value = 0;
+
+    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;
+}
+
+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);
+}
+
+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";
+    }
+
+    foreach ($items as $entry) {
+        $key = unpack("Q", $entry["key"])[1];
+        $val = unpack("l", $entry["value"])[1];
+
+        $src = ($key >> 32) & 0xFFFFFFFF;
+        $dst = $key & 0xFFFFFFFF;
+
+        echo sprintf(
+            "source: %s -> destination: %s count: %d time: %s\n",
+            decimal_to_ip($src),
+            decimal_to_ip($dst),
+            $val,
+            $time
+        );
+    }
+
+    $packet_cnt->clear();
+}

+ 130 - 0
examples/tracing/biolatpcts.php

@@ -0,0 +1,130 @@
+<?php
+$bpf_source = <<<EOT
+#include <linux/blkdev.h>
+#include <linux/time64.h>
+
+BPF_PERCPU_ARRAY(lat_100ms, u64, 100);
+BPF_PERCPU_ARRAY(lat_1ms, u64, 100);
+BPF_PERCPU_ARRAY(lat_10us, u64, 100);
+
+RAW_TRACEPOINT_PROBE(block_rq_complete)
+{
+        // TP_PROTO(struct request *rq, blk_status_t error, unsigned int nr_bytes)
+        struct request *rq = (void *)ctx->args[0];
+        unsigned int cmd_flags;
+        u64 dur;
+        size_t base, slot;
+
+        if (!rq->io_start_time_ns)
+                return 0;
+
+        dur = bpf_ktime_get_ns() - rq->io_start_time_ns;
+
+        slot = min_t(size_t, div_u64(dur, 100 * NSEC_PER_MSEC), 99);
+        lat_100ms.increment(slot);
+        if (slot)
+                return 0;
+
+        slot = min_t(size_t, div_u64(dur, NSEC_PER_MSEC), 99);
+        lat_1ms.increment(slot);
+        if (slot)
+                return 0;
+
+        slot = min_t(size_t, div_u64(dur, 10 * NSEC_PER_USEC), 99);
+        lat_10us.increment(slot);
+        return 0;
+}
+EOT;
+
+$ebpf = new Bpf(["text" => $bpf_source]);
+
+$cur_lat_100ms = $ebpf->lat_100ms;
+$cur_lat_1ms =$ebpf->lat_1ms;
+$cur_lat_10us = $ebpf->lat_10us;
+
+
+$last_lat_100ms = array_fill(0, 100, 0);
+$last_lat_1ms = array_fill(0, 100, 0);
+$last_lat_10us = array_fill(0, 100, 0);
+
+$lat_100ms = array_fill(0, 100, 0);
+$lat_1ms = array_fill(0, 100, 0);
+$lat_10us = array_fill(0, 100, 0);
+
+function find_pct($req, $total, &$slots, $idx, $counted) {
+    while ($idx > 0) {
+        $idx--;
+        if ($slots[$idx] > 0) {
+            $counted += $slots[$idx];
+            if (($counted / $total) * 100 >= 100 - $req) {
+                break;
+            }
+        }
+    }
+    return [$idx, $counted];
+}
+
+function calc_lat_pct($req_pcts, $total, &$lat_100ms, &$lat_1ms, &$lat_10us) {
+    $pcts = array_fill(0, count($req_pcts), 0);
+
+    if ($total == 0) {
+        return $pcts;
+    }
+
+    $data = [[100 * 1000, &$lat_100ms], [1000, &$lat_1ms], [10, &$lat_10us]];
+    $data_sel = 0;
+    $idx = 100;
+    $counted = 0;
+
+    for ($pct_idx = count($req_pcts) - 1; $pct_idx >= 0; $pct_idx--) {
+        $req = floatval($req_pcts[$pct_idx]);
+        while (true) {
+            $last_counted = $counted;
+            [$gran, $slots] = $data[$data_sel];
+            [$idx, $counted] = find_pct($req, $total, $slots, $idx, $counted);
+            if ($idx > 0 || $data_sel == count($data) - 1) {
+                break;
+            }
+            $counted = $last_counted;
+            $data_sel++;
+            $idx = 100;
+        }
+
+        $pcts[$pct_idx] = $gran * $idx + $gran / 2;
+    }
+
+    return $pcts;
+}
+
+echo "Block I/O latency percentile example.\n";
+
+
+while (true) {
+    sleep(3);
+
+    $lat_total = 0;
+
+    for ($i = 0; $i < 100; $i++) {
+        $v = $cur_lat_100ms->sum_value($i);
+        $lat_100ms[$i] = max($v - $last_lat_100ms[$i], 0);
+        $last_lat_100ms[$i] = $v;
+
+        $v =  $cur_lat_1ms->sum_value($i);
+        $lat_1ms[$i] = max($v - $last_lat_1ms[$i], 0);
+        $last_lat_1ms[$i] = $v;
+
+        $v = $cur_lat_10us->sum_value($i);
+        $lat_10us[$i] = max($v - $last_lat_10us[$i], 0);
+        $last_lat_10us[$i] = $v;
+
+        $lat_total += $lat_100ms[$i];
+    }
+
+    $target_pcts = [50, 75, 90, 99];
+    $pcts = calc_lat_pct($target_pcts, $lat_total, $lat_100ms, $lat_1ms, $lat_10us);
+
+    for ($i = 0; $i < count($target_pcts); $i++) {
+        echo "p" . $target_pcts[$i] . "=" . intval($pcts[$i]) . "us ";
+    }
+    echo PHP_EOL;
+}

+ 70 - 0
examples/tracing/bitehist.php

@@ -0,0 +1,70 @@
+<?php
+#
+# bitehist.php	Block I/O size histogram.
+#		For Linux, uses BCC, eBPF. Embedded C.
+#
+# Written as a basic example of using histograms to show a distribution.
+#
+# A Ctrl-C will print the gathered histogram then exit.
+#
+# Copyright (c) 2015 Brendan Gregg.
+# Licensed under the Apache License, Version 2.0 (the "License")
+#
+# 15-Aug-2015	Brendan Gregg	Created this.
+# 03-Feb-2019   Xiaozhou Liu    added linear histogram.
+# 02-Mar-2025   Wei             Use blk_mq_end_request for newer kernel.
+
+$bpf_text = <<<EOT
+#include <uapi/linux/ptrace.h>
+#include <linux/blk-mq.h>
+
+BPF_HISTOGRAM(dist);
+BPF_HISTOGRAM(dist_linear);
+
+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;
+
+# load BPF program
+$b = new Bpf(["text" => $bpf_text]);
+
+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");
+}
+
+# header
+echo "Tracing... Hit Ctrl-C to end.\n";
+
+# Set up signal handler for Ctrl-C
+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 "\nlog2 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);
+    }
+} 

+ 87 - 0
examples/tracing/dddos.php

@@ -0,0 +1,87 @@
+<?php
+$prog = <<<EOT
+#include <linux/skbuff.h>
+#include <uapi/linux/ip.h>
+
+#define MAX_NB_PACKETS 1000
+#define LEGAL_DIFF_TIMESTAMP_PACKETS 1000000
+
+BPF_HASH(rcv_packets);
+
+struct detectionPackets {
+    u64 nb_ddos_packets;
+};
+
+BPF_PERF_OUTPUT(events);
+
+int detect_ddos(struct pt_regs *ctx, void *skb){
+    struct detectionPackets detectionPacket = {};
+
+    // Used to count number of received packets
+    u64 rcv_packets_nb_index = 0, rcv_packets_nb_inter=1, *rcv_packets_nb_ptr;
+
+    // Used to measure elapsed time between 2 successive received packets
+    u64 rcv_packets_ts_index = 1, rcv_packets_ts_inter=0, *rcv_packets_ts_ptr;
+
+    /* The algorithm analyses packets received by ip_rcv function
+    * and measures the difference in reception time between each packet.
+    * DDOS flooders send millions of packets such that difference of
+    * timestamp between 2 successive packets is so small
+    * (which is not like regular applications behaviour).
+    * This script looks for this difference in time and if it sees
+    * more than MAX_NB_PACKETS successive packets with a difference
+    * of timestamp between each one of them less than
+    * LEGAL_DIFF_TIMESTAMP_PACKETS ns,
+    * ------------------ It Triggers an ALERT -----------------
+    * Those settings must be adapted depending on regular network traffic
+    * -------------------------------------------------------------------
+    * Important: this is a rudimentary intrusion detection system, one can
+    * test a real case attack using hping3. However; if regular network
+    * traffic increases above predefined detection settings, a false
+    * positive alert will be triggered (an example would be the
+    * case of large file downloads).
+    */
+    rcv_packets_nb_ptr = rcv_packets.lookup(&rcv_packets_nb_index);
+    rcv_packets_ts_ptr = rcv_packets.lookup(&rcv_packets_ts_index);
+    if(rcv_packets_nb_ptr != 0 && rcv_packets_ts_ptr != 0){
+        rcv_packets_nb_inter = *rcv_packets_nb_ptr;
+        rcv_packets_ts_inter = bpf_ktime_get_ns() - *rcv_packets_ts_ptr;
+        if(rcv_packets_ts_inter < LEGAL_DIFF_TIMESTAMP_PACKETS){
+            rcv_packets_nb_inter++;
+        } else {
+            rcv_packets_nb_inter = 0;
+        }
+        if(rcv_packets_nb_inter > MAX_NB_PACKETS){
+            detectionPacket.nb_ddos_packets = rcv_packets_nb_inter;
+            events.perf_submit(ctx, &detectionPacket, sizeof(detectionPacket));
+        }
+    }
+    rcv_packets_ts_inter = bpf_ktime_get_ns();
+    rcv_packets.update(&rcv_packets_nb_index, &rcv_packets_nb_inter);
+    rcv_packets.update(&rcv_packets_ts_index, &rcv_packets_ts_inter);
+    return 0;
+}
+EOT;
+$b    = new Bpf(["text" => $prog]);
+
+$b->attach_kprobe("ip_rcv", "detect_ddos");
+
+echo "DDOS detector started ... Hit Ctrl-C to end!\n";
+
+echo sprintf("%-26s %-10s\n", "TIME(s)", "MESSAGE");
+
+function trigger_alert_event($ctx, $data, $size)
+{
+    $event = unpack("Qnb_ddos_packets", $data);
+    echo sprintf("%-26s %s %ld\n", date("Y-m-d H:i:s"), "DDOS Attack => nb of packets up to now: ", $event['nb_ddos_packets']);
+}
+
+$b->events->open_perf_buffer("trigger_alert_event");
+
+while (true) {
+    try {
+        $b->perf_buffer_poll();
+    } catch (Exception $e) {
+        exit;
+    }
+}

+ 69 - 0
examples/tracing/disksnoop.php

@@ -0,0 +1,69 @@
+<?php
+
+define("REQ_WRITE", 1); // from include/linux/blk_types.h
+
+$prog = <<<EOT
+#include <uapi/linux/ptrace.h>
+#include <linux/blk-mq.h>
+
+BPF_HASH(start, struct request *);
+
+void trace_start(struct pt_regs *ctx, struct request *req) {
+    u64 ts = bpf_ktime_get_ns();
+    start.update(&req, &ts);
+}
+
+void trace_completion(struct pt_regs *ctx, struct request *req) {
+    u64 *tsp, delta;
+    tsp = start.lookup(&req);
+    if (tsp != 0) {
+        delta = bpf_ktime_get_ns() - *tsp;
+        bpf_trace_printk("%d %x %d\\n", req->__data_len,
+                         req->cmd_flags, delta / 1000);
+        start.delete(&req);
+    }
+}
+EOT;
+
+$b = new Bpf(["text" => $prog]);
+
+// attach trace_start
+if ($b->get_kprobe_functions("blk_start_request")) {
+    $b->attach_kprobe("blk_start_request", "trace_start");
+}
+$b->attach_kprobe("blk_mq_start_request", "trace_start");
+
+// attach trace_completion
+if ($b->get_kprobe_functions("__blk_account_io_done")) {
+    $b->attach_kprobe("__blk_account_io_done", "trace_completion");
+} elseif ($b->get_kprobe_functions("blk_account_io_done")) {
+    $b->attach_kprobe("blk_account_io_done", "trace_completion");
+} else {
+    $b->attach_kprobe("blk_mq_end_request", "trace_completion");
+}
+
+printf("%-18s %-2s %-7s %8s\n", "TIME(s)", "T", "BYTES", "LAT(ms)");
+
+$start = 0;
+
+while (true) {
+    $fields = $b->trace_fields();
+    list($task, $pid, $cpu, $flags, $ts, $msg) = $fields;
+
+    $parts = preg_split('/\s+/', $msg);
+
+    if (count($parts) < 3) continue;
+
+    list($bytes_s, $bflags_s, $us_s) = $parts;
+
+    $bflags = intval($bflags_s, 16);
+
+    if ($bflags & REQ_WRITE) {
+        $type_s = "W";
+    } else {
+        $type_s = "R";
+    }
+    $ms = intval($us_s) / 1000.0;
+
+    printf("%-18.9f %-2s %-7s %8.2f\n", $ts, $type_s, $bytes_s, $ms);
+}

+ 22 - 0
examples/tracing/hello_fields.php

@@ -0,0 +1,22 @@
+<?php
+$prog = <<<EOT
+int hello(void *ctx) {
+    bpf_trace_printk("Hello, World!\\n");
+    return 0;
+}
+EOT;
+# load BPF program
+$ebpf = new Bpf(["text" => $prog]);
+$ebpf->attach_kprobe($ebpf->get_syscall_fnname("clone"),"hello");
+# header
+printf("%-18s %-16s %-6s %s\n", "TIME(s)", "COMM", "PID", "MESSAGE");
+# format output
+while (true) {
+    try {
+        list($task, $pid, $cpu, $flags, $ts, $msg) =$ebpf->trace_fields();
+        printf("%-18.9f %-16s %-6d %s\n", $ts, $task, $pid, $msg);
+        flush();
+    } catch (Exception $e) {
+        continue;
+    }
+}

+ 54 - 0
examples/tracing/hello_perf_output.php

@@ -0,0 +1,54 @@
+<?php
+$prog = <<<EOT
+#include <linux/sched.h>
+
+// define output data structure in C
+struct data_t {
+    u32 pid;
+    u64 ts;
+    char comm[TASK_COMM_LEN];
+};
+BPF_PERF_OUTPUT(events);
+
+int hello(struct pt_regs *ctx) {
+    struct data_t data = {};
+
+    data.pid = bpf_get_current_pid_tgid();
+    data.ts = bpf_ktime_get_ns();
+    bpf_get_current_comm(&data.comm, sizeof(data.comm));
+
+    events.perf_submit(ctx, &data, sizeof(data));
+
+    return 0;
+}
+EOT;
+
+# load BPF program
+$b    = new Bpf(["text" => $prog]);
+$b->attach_kprobe($b->get_syscall_fnname("clone"), "hello");
+
+# header
+printf("%-18s %-16s %-6s %s\n", "TIME(s)", "COMM", "PID", "MESSAGE");
+
+# process event
+$start = 0;
+function print_event($cpu, $data, $size) {
+    global $start;
+    $event = unpack("Qpid/Qts/A16comm", $data);
+    if ($start == 0) {
+        $start = $event['ts'];
+    }
+    $time_s = ($event['ts'] - $start) / 1000000000.0;
+    printf("%-18.9f %-16s %-6d %s\n", $time_s, $event['comm'], $event['pid'], "Hello, perf_output!");
+}
+
+# loop with callback to print_event
+$b->events->open_perf_buffer("print_event");
+
+while (true) {
+    try {
+        $b->perf_buffer_poll();
+    } catch (Exception $e) {
+        exit();
+    }
+}

+ 62 - 0
examples/tracing/hello_perf_output_using_ns.php

@@ -0,0 +1,62 @@
+<?php
+$prog = <<<EOT
+#include <linux/sched.h>
+
+// define output data structure in C
+struct data_t {
+    u32 pid;
+    u64 ts;
+    char comm[TASK_COMM_LEN];
+};
+BPF_PERF_OUTPUT(events);
+
+int hello(struct pt_regs *ctx) {
+    struct data_t data = {};
+    struct bpf_pidns_info ns = {};
+
+    if(bpf_get_ns_current_pid_tgid(DEV, INO, &ns, sizeof(struct bpf_pidns_info)))
+    return 0;
+    data.pid = ns.pid;
+    data.ts = bpf_ktime_get_ns();
+    bpf_get_current_comm(&data.comm, sizeof(data.comm));
+
+    events.perf_submit(ctx, &data, sizeof(data));
+
+    return 0;
+}
+EOT;
+$stat = stat("/proc/self/ns/pid");
+$dev = $stat["dev"];
+$ino = $stat["ino"];
+$prog = str_replace("DEV", strval($dev), $prog);
+$prog = str_replace("INO", strval($ino), $prog);
+
+# load BPF program
+$b    = new Bpf(["text" => $prog]);
+$b->attach_kprobe($b->get_syscall_fnname("clone"), "hello");
+
+# header
+printf("%-18s %-16s %-6s %s\n", "TIME(s)", "COMM", "PID", "MESSAGE");
+
+# process event
+$start = 0;
+function print_event($cpu, $data, $size) {
+    global $start;
+    $event = unpack("Qpid/Qts/A16comm", $data);
+    if ($start == 0) {
+        $start = $event['ts'];
+    }
+    $time_s = ($event['ts'] - $start) / 1000000000.0;
+    printf("%-18.9f %-16s %-6d %s\n", $time_s, $event['comm'], $event['pid'], "Hello, perf_output!");
+}
+
+# loop with callback to print_event
+$b->events->open_perf_buffer("print_event");
+
+while (true) {
+    try {
+        $b->perf_buffer_poll();
+    } catch (Exception $e) {
+        exit();
+    }
+}

+ 54 - 0
examples/tracing/kvm_hypercall.php

@@ -0,0 +1,54 @@
+<?php
+$prog = <<<EOT
+#define EXIT_REASON 18
+BPF_HASH(start, u8, u8);
+
+TRACEPOINT_PROBE(kvm, kvm_exit) {
+    u8 e = EXIT_REASON;
+    u8 one = 1;
+    if (args->exit_reason == EXIT_REASON) {
+        bpf_trace_printk("KVM_EXIT exit_reason : %d\\n", args->exit_reason);
+        start.update(&e, &one);
+    }
+    return 0;
+}
+
+TRACEPOINT_PROBE(kvm, kvm_entry) {
+    u8 e = EXIT_REASON;
+    u8 zero = 0;
+    u8 *s = start.lookup(&e);
+    if (s != NULL && *s == 1) {
+        bpf_trace_printk("KVM_ENTRY vcpu_id : %u\\n", args->vcpu_id);
+        start.update(&e, &zero);
+    }
+    return 0;
+}
+
+TRACEPOINT_PROBE(kvm, kvm_hypercall) {
+    u8 e = EXIT_REASON;
+    u8 zero = 0;
+    u8 *s = start.lookup(&e);
+    if (s != NULL && *s == 1) {
+        bpf_trace_printk("HYPERCALL nr : %d\\n", args->nr);
+    }
+    return 0;
+};
+EOT;
+
+
+# load BPF program
+$b = new Bpf(["text" => $prog]);
+
+# header
+printf("%-18s %-16s %-6s %s\n", "TIME(s)", "COMM", "PID", "EVENT");
+
+# format output
+while (true) {
+    try {
+        list($task, $pid, $cpu, $flags, $ts, $msg) = $b->trace_fields();
+        printf("%-18.9f %-16s %-6d %s\n", $ts, $task, $pid, $msg);
+        flush();
+    } catch (Exception $e) {
+        exit();
+    }
+}

+ 83 - 0
examples/tracing/mallocstacks.php

@@ -0,0 +1,83 @@
+<?php
+if ($argc < 2) {
+    echo "USAGE: mallocstacks PID [NUM_STACKS=1024]\n";
+    exit(1);
+}
+$pid    = (int)$argv[1];
+$stacks = ($argc == 3 && is_numeric($argv[2]) && (int)$argv[2] > 0) ? $argv[2] : "1024";
+
+$bpf_text = <<<EOT
+#include <uapi/linux/ptrace.h>
+
+BPF_HASH(calls, int);
+BPF_STACK_TRACE(stack_traces, {$stacks});
+
+int alloc_enter(struct pt_regs *ctx, size_t size) {
+    int key = stack_traces.get_stackid(ctx, BPF_F_USER_STACK);
+    if (key < 0)
+        return 0;
+
+    u64 zero = 0, *val;
+    val = calls.lookup_or_try_init(&key, &zero);
+    if (val) {
+      (*val) += size;
+    }
+    return 0;
+};
+EOT;
+
+$ebpf = new Bpf(["text" => $bpf_text]);
+$ebpf->attach_uprobe("c", "malloc", "alloc_enter", ["pid" => $pid]);
+echo "Attaching to malloc in pid {$pid}, Ctrl+C to quit.\n";
+
+pcntl_signal(SIGINT, "signalHandler");
+
+pcntl_async_signals(true);
+
+# sleep until Ctrl-C
+while (true) {
+    sleep(99999999);
+}
+
+function signalHandler($signo)
+{
+    global $ebpf;
+    global $pid;
+    switch ($signo) {
+        case SIGINT:
+            $calls        = $ebpf->get_table("calls");
+            $stack_traces = $ebpf->get_table("stack_traces");
+            $calls_vals   = $calls->values();
+
+            $mapped = array_map(function ($val) {
+                return [
+                    'stack_id' => unpack("L", $val['key'])[1],
+                    'value'    => unpack("Q", $val['value'])[1]
+                ];
+            }, $calls_vals);
+
+            usort($mapped, function($first, $sec) {
+                if ($first['value'] == $sec['value']) {
+                    return 0;
+                }
+                return ($first['value'] < $sec['value']) ? 1 : -1;
+            });
+
+            foreach ($mapped as $entry) {
+                $stack_id = $entry['stack_id'];
+                $value = $entry['value'];
+
+                printf("%d bytes allocated at:\n", $value);
+
+                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
examples/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 Bpf(["text" => $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();
+    }
+}

+ 91 - 0
examples/tracing/stacksnoop.php

@@ -0,0 +1,91 @@
+<?php
+if ($argc < 2) {
+    echo "USAGE: php stacksnoop.php [-p PID] [-s] [-v] function_name\n";
+    exit(1);
+}
+$function = end($argv) ?? null;
+$pid = null;
+$offset = false;
+$verbose = false;
+
+for ($i = 1; $i < $argc; $i++) {
+    $arg = $argv[$i];
+    if ($arg === "-p" && isset($argv[$i + 1])) {
+        $pid = $argv[++$i];
+    } elseif ($arg === "-s") {
+        $offset = true;
+    } elseif ($arg === "-v") {
+        $verbose = true;
+    } else {
+        $function = $arg;
+    }
+}
+
+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 Bpf(["text" => $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
examples/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 Bpf(["text" => $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);
+    }
+}

+ 43 - 0
examples/tracing/sync_timing.php

@@ -0,0 +1,43 @@
+<?php
+$prog = <<<EOT
+#include <uapi/linux/ptrace.h>
+
+BPF_HASH(last);
+
+int do_trace(struct pt_regs *ctx) {
+    u64 ts, *tsp, delta, key = 0;
+
+    // attempt to read stored timestamp
+    tsp = last.lookup(&key);
+    if (tsp != NULL) {
+        delta = bpf_ktime_get_ns() - *tsp;
+        if (delta < 1000000000) {
+            // output if time is less than 1 second
+            bpf_trace_printk("%d\\n", delta / 1000000);
+        }
+        last.delete(&key);
+    }
+
+    // update stored timestamp
+    ts = bpf_ktime_get_ns();
+    last.update(&key, &ts);
+    return 0;
+}
+EOT;
+
+$b = new Bpf(["text" => $prog]);
+
+$b->attach_kprobe($b->get_syscall_fnname("sync"), "do_trace");
+
+echo "Tracing for quick sync's... Ctrl-C to end\n";
+
+$start = 0;
+
+while (true) {
+    list($task, $pid, $cpu, $flags, $ts, $ms) =  $b->trace_fields();
+
+    if ($start === 0) $start = $ts;
+    $ts -= $start;
+
+    printf("At time %.2f s: multiple syncs detected, last %s ms ago\n", $ts, $ms);
+}

+ 72 - 0
examples/tracing/tcpv4connect.php

@@ -0,0 +1,72 @@
+<?php
+$bpf_text = <<<EOT
+#include <uapi/linux/ptrace.h>
+#include <net/sock.h>
+#include <bcc/proto.h>
+
+BPF_HASH(currsock, u32, struct sock *);
+
+int kprobe__tcp_v4_connect(struct pt_regs *ctx, struct sock *sk)
+{
+        u32 pid = bpf_get_current_pid_tgid();
+
+        // stash the sock ptr for lookup on return
+        currsock.update(&pid, &sk);
+
+        return 0;
+};
+
+int kretprobe__tcp_v4_connect(struct pt_regs *ctx)
+{
+        int ret = PT_REGS_RC(ctx);
+        u32 pid = bpf_get_current_pid_tgid();
+
+        struct sock **skpp;
+        skpp = currsock.lookup(&pid);
+        if (skpp == 0) {
+                return 0;       // missed entry
+        }
+
+        if (ret != 0) {
+                // failed to send SYNC packet, may not have populated
+                // socket __sk_common.{skc_rcv_saddr, ...}
+                currsock.delete(&pid);
+                return 0;
+        }
+
+        // pull in details
+        struct sock *skp = *skpp;
+        u32 saddr = skp->__sk_common.skc_rcv_saddr;
+        u32 daddr = skp->__sk_common.skc_daddr;
+        u16 dport = skp->__sk_common.skc_dport;
+
+        // output
+        bpf_trace_printk("trace_tcp4connect %x %x %d\\n", saddr, daddr, ntohs(dport));
+
+        currsock.delete(&pid);
+
+        return 0;
+}
+EOT;
+
+$ebpf = new Bpf(["text" => $bpf_text]);
+# header
+printf("%-6s %-12s %-16s %-16s %-4s\n", "PID", "COMM", "SADDR", "DADDR","DPORT");
+# format output
+while (true) {
+    try {
+        list($task, $pid, $cpu, $flags, $ts, $msg) =$ebpf->trace_fields();
+        list($tag, $saddr_hs, $daddr_hs, $dport_s) = explode(" ", $msg, 4);
+
+        printf("%-6d %-12.12s %-16s %-16s %-4s\n",
+            $pid,
+            $task,
+            long2ip(unpack('V', pack('H*', $saddr_hs))[1]),
+            long2ip(unpack('V', pack('H*', $daddr_hs))[1]),
+            $dport_s
+        );
+        flush();
+    } catch (Exception $e) {
+        continue;
+    }
+}

+ 32 - 0
examples/tracing/tp.php

@@ -0,0 +1,32 @@
+<?php
+$bpf_text = <<<EOT
+#include <uapi/linux/ptrace.h>
+struct trace_event_raw_sys_enter_rw__stub {
+    __u64 unused;
+    long int id;
+    __u64 fd;
+    char* buf;
+    __u64 size;
+};
+
+int test(struct trace_event_raw_sys_enter_rw__stub* ctx)
+{
+        bpf_trace_printk("%s\\n",ctx->size);
+        return 1;
+}
+EOT;
+
+$ebpf = new Bpf(["text" => $bpf_text]);
+$ebpf->attach_tracepoint("syscalls:sys_enter_write","test");
+# header
+printf("%-18s %-16s %-6s %s\n", "TIME(s)", "COMM", "PID", "MESSAGE");
+# format output
+while (true) {
+    try {
+        list($task, $pid, $cpu, $flags, $ts, $msg) =$ebpf->trace_fields();
+        printf("%-18.9f %-16s %-6d %s\n", $ts, $task, $pid, $msg);
+        flush();
+    } catch (Exception $e) {
+        continue;
+    }
+}

+ 14 - 0
examples/tracing/trace_fields.php

@@ -0,0 +1,14 @@
+<?php
+$prog = <<<EOT
+int hello(void *ctx) {
+    bpf_trace_printk("Hello, World!\\n");
+    return 0;
+}
+EOT;
+# load BPF program
+$b = new Bpf(["text" => $prog]);
+$b->attach_kprobe($b->get_syscall_fnname("clone"),"hello");
+# header
+echo sprintf("%s %s\n", "PID", "MESSAGE");
+# format output
+$b->trace_print("{1} {5}");

+ 42 - 0
examples/tracing/trace_perf_output.php

@@ -0,0 +1,42 @@
+<?php
+function cb($cpu,$data,$size) {
+    $event = unpack("Qcpu/Qts/Qmagic/A16msg", $data);
+    if ($event === false) {
+        echo "error.\n";
+        return;
+    }
+    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;
+}
+EOT;
+
+$ebpf = new Bpf(["text" => $prog]);
+$event_name = $ebpf->get_syscall_fnname("clone");
+$ebpf->attach_kprobe($event_name,"do_sys_clone");
+$ebpf->events->open_perf_buffer("cb");
+echo("Tracing... Hit Ctrl-C to end.\n");
+while (true) {
+    try {
+        $ebpf->perf_buffer_poll();
+        flush();
+    } catch (Exception $e) {
+        exit;
+    }
+}

+ 106 - 0
examples/tracing/undump.php

@@ -0,0 +1,106 @@
+<?php
+$pid = null;
+$hexdump = false;
+
+foreach ($argv as $index => $arg) {
+    if ($arg === "-p" && isset($argv[$index + 1])) {
+        $pid = intval($argv[$index + 1]);
+    } elseif ($arg === "--hexdump") {
+        $hexdump = true;
+    }
+}
+
+$bpf_prog = <<<EOT
+#include <uapi/linux/ptrace.h>
+#include <net/sock.h>
+#include <bcc/proto.h>
+#include <linux/aio.h>
+#include <linux/socket.h>
+#include <linux/net.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/module.h>
+#include <net/sock.h>
+#include <net/af_unix.h>
+
+#define MAX_PKT 512
+struct recv_data_t {
+    u32 recv_len;
+    u8  pkt[MAX_PKT];
+};
+
+BPF_PERCPU_ARRAY(unix_data, struct recv_data_t, 1);
+BPF_PERF_OUTPUT(unix_recv_events);
+
+int trace_unix_stream_read_actor(struct pt_regs *ctx)
+{
+    u32 zero = 0;
+    int ret = PT_REGS_RC(ctx);
+    u64 pid_tgid = bpf_get_current_pid_tgid();
+    u32 pid = pid_tgid >> 32;
+    u32 tid = pid_tgid;
+
+    FILTER_PID
+
+    struct sk_buff *skb = (struct sk_buff *)PT_REGS_PARM1(ctx);
+    struct recv_data_t *data = unix_data.lookup(&zero);
+    if (!data)
+        return 0;
+
+    unsigned int data_len = skb->len;
+    if (data_len > MAX_PKT)
+        return 0;
+
+    void *iodata = (void *)skb->data;
+    data->recv_len = data_len;
+
+    bpf_probe_read(data->pkt, data_len, iodata);
+    unix_recv_events.perf_submit(ctx, data, data_len + sizeof(u32));
+
+    return 0;
+}
+EOT;
+
+if ($pid !== null) {
+    $filter = "if (pid != $pid) { return 0; }";
+    $bpf_prog = str_replace("FILTER_PID", $filter, $bpf_prog);
+} else {
+    $bpf_prog = str_replace("FILTER_PID", "", $bpf_prog);
+}
+// initialize BPF
+$b = new Bpf(["text" => $bpf_prog]);
+$b->attach_kprobe("unix_stream_read_actor", "trace_unix_stream_read_actor");
+
+echo $pid ? "Tracing PID $pid UNIX socket packets ... Hit Ctrl-C to end\n"
+          : "Tracing UNIX socket packets ... Hit Ctrl-C to end\n";
+
+function print_recv_pkg($cpu,$data,$size){
+    $recv_len = unpack("L", substr($data, 0, 4))[1];
+    $pkt = substr($data, 4, $recv_len);
+    global $pid;
+    global $hexdump;
+    if ($pid) {
+        echo "PID \033[1;31m$pid\033[0m ";
+    }
+
+    echo "Recv \033[1;31m$recv_len\033[0m bytes\n";
+    if ($hexdump) {
+        echo chunk_split(bin2hex($pkt), 32, "\n");
+    } else {
+        echo "    ";
+        for ($i = 0; $i < $recv_len; $i++) {
+            printf("%02x ", ord($pkt[$i]));
+            if (($i + 1) % 16 == 0) echo "\n    ";
+        }
+        echo "\n";
+    }
+}
+$b->unix_recv_events->open_perf_buffer("print_recv_pkg");
+
+while (true) {
+    try {
+        $b->perf_buffer_poll();
+    } catch (Exception $e) {
+        exit();
+    }
+}

+ 33 - 0
examples/tracing/uprobe.php

@@ -0,0 +1,33 @@
+<?php
+if ($argc < 2) {
+    fwrite(STDERR, "Usage: php script.php <binary_path> <pid>\n");
+    exit(1);
+}
+$binary = $argv[1];
+$pid = intval($argv[2]);
+
+$bpf_text = <<<EOT
+#include <uapi/linux/ptrace.h>
+
+int test(struct pt_regs *ctx)
+{
+        bpf_trace_printk("%d---%d\\n",ctx->di,ctx->si);
+        return 1;
+}
+EOT;
+
+$ebpf = new Bpf(["text" => $bpf_text]);
+$opt = array("pid"=>$pid);
+$ebpf->attach_uprobe($binary,"add","test",$opt);
+# header
+printf("%-18s %-16s %-6s %s\n", "TIME(s)", "COMM", "PID", "MESSAGE");
+# format output
+while (true) {
+    try {
+        list($task, $pid, $cpu, $flags, $ts, $msg) = $ebpf->trace_fields();
+        printf("%-18.9f %-16s %-6d %s\n", $ts, $task, $pid, $msg);
+        flush();
+    } catch (Exception $e) {
+        continue;
+    }
+}

+ 30 - 0
examples/tracing/urandomread-explicit.php

@@ -0,0 +1,30 @@
+<?php
+$bpf_text = <<<EOT
+#include <uapi/linux/ptrace.h>
+
+struct urandom_read_args {
+    u64 __unused__;
+    u32 got_bits;
+    u32 pool_left;
+    u32 input_left;
+};
+
+int printarg(struct urandom_read_args *args) {
+    bpf_trace_printk("%d\\n", args->got_bits);
+    return 0;
+}
+EOT;
+
+$b = new Bpf(["text" => $bpf_text]);
+$b->attach_tracepoint("random:urandom_read", "printarg");
+
+echo sprintf("%-18s %-16s %-6s %s\n", "TIME(s)", "COMM", "PID", "GOTBITS");
+
+while (true) {
+    try {
+        list($task, $pid, $cpu, $flags, $ts, $msg) = $ebpf->trace_fields();
+        printf("%-18.9f %-16s %-6d %s\n", $ts, $task, $pid, $msg);
+    } catch (Exception $e) {
+        break;
+    }
+}

+ 21 - 0
examples/tracing/urandomread.php

@@ -0,0 +1,21 @@
+<?php
+$bpf_text = <<<EOT
+TRACEPOINT_PROBE(random, urandom_read) {
+    // args is from /sys/kernel/debug/tracing/events/random/urandom_read/format
+    bpf_trace_printk("%d\\n", args->got_bits);
+    return 0;
+}
+EOT;
+
+$b = new Bpf(["text" => $bpf_text]);
+
+echo sprintf("%-18s %-16s %-6s %s\n", "TIME(s)", "COMM", "PID", "GOTBITS");
+
+while (true) {
+    try {
+        list($task, $pid, $cpu, $flags, $ts, $msg) = $ebpf->trace_fields();
+        printf("%-18.9f %-16s %-6d %s\n", $ts, $task, $pid, $msg);
+    } catch (Exception $e) {
+        break;
+    }
+}