From a2274ef29ddee039358609a373d4cd7ec7a872f6 Mon Sep 17 00:00:00 2001 From: varun-r-mallya Date: Tue, 30 Sep 2025 14:29:55 +0530 Subject: [PATCH] deletions of demo --- demo/bcc.py | 46 ----- demo/clone-matplotlib.ipynb | 397 ------------------------------------ demo/pybpf0.py | 23 --- demo/pybpf1.py | 41 ---- demo/pybpf2.py | 43 ---- demo/pybpf3.py | 52 ----- demo/pybpf4.py | 56 ----- 7 files changed, 658 deletions(-) delete mode 100644 demo/bcc.py delete mode 100644 demo/clone-matplotlib.ipynb delete mode 100644 demo/pybpf0.py delete mode 100644 demo/pybpf1.py delete mode 100644 demo/pybpf2.py delete mode 100644 demo/pybpf3.py delete mode 100644 demo/pybpf4.py diff --git a/demo/bcc.py b/demo/bcc.py deleted file mode 100644 index f9f4d28..0000000 --- a/demo/bcc.py +++ /dev/null @@ -1,46 +0,0 @@ -from __future__ import print_function -from bcc import BPF -from bcc.utils import printb - -# load BPF program -b = BPF(text=""" -#include - -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; -} -""") - -b.attach_kprobe(event=b.get_syscall_fnname("sync"), fn_name="do_trace") -print("Tracing for quick sync's... Ctrl-C to end") - -# TODO -# format output -start = 0 -while 1: - try: - (task, pid, cpu, flags, ts, ms) = b.trace_fields() - if start == 0: - start = ts - ts = ts - start - printb(b"At time %.2f s: multiple syncs detected, last %s ms ago" % (ts, ms)) - except KeyboardInterrupt: - exit() diff --git a/demo/clone-matplotlib.ipynb b/demo/clone-matplotlib.ipynb deleted file mode 100644 index 414dbc9..0000000 --- a/demo/clone-matplotlib.ipynb +++ /dev/null @@ -1,397 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "id": "22dd4e7b-2ea2-49cb-a8d5-1da108c10034", - "metadata": {}, - "outputs": [], - "source": [ - "import time\n", - "\n", - "from pythonbpf import bpf, map, section, bpfglobal, BPF\n", - "from pythonbpf.helpers import pid\n", - "from pythonbpf.maps import HashMap\n", - "from pylibbpf import *\n", - "from ctypes import c_void_p, c_int64, c_uint64, c_int32\n", - "import matplotlib.pyplot as plt" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "ac7a07bf-440f-41e2-bec8-95f520f9cd53", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Module(\n", - " body=[\n", - " FunctionDef(\n", - " name='hist',\n", - " args=arguments(\n", - " posonlyargs=[],\n", - " args=[],\n", - " kwonlyargs=[],\n", - " kw_defaults=[],\n", - " defaults=[]),\n", - " body=[\n", - " Return(\n", - " value=Call(\n", - " func=Name(id='HashMap', ctx=Load()),\n", - " args=[],\n", - " keywords=[\n", - " keyword(\n", - " arg='key',\n", - " value=Name(id='c_int32', ctx=Load())),\n", - " keyword(\n", - " arg='value',\n", - " value=Name(id='c_uint64', ctx=Load())),\n", - " keyword(\n", - " arg='max_entries',\n", - " value=Constant(value=4096))]))],\n", - " decorator_list=[\n", - " Name(id='bpf', ctx=Load()),\n", - " Name(id='map', ctx=Load())],\n", - " returns=Name(id='HashMap', ctx=Load()),\n", - " type_params=[]),\n", - " FunctionDef(\n", - " name='hello',\n", - " args=arguments(\n", - " posonlyargs=[],\n", - " args=[\n", - " arg(\n", - " arg='ctx',\n", - " annotation=Name(id='c_void_p', ctx=Load()))],\n", - " kwonlyargs=[],\n", - " kw_defaults=[],\n", - " defaults=[]),\n", - " body=[\n", - " Assign(\n", - " targets=[\n", - " Name(id='process_id', ctx=Store())],\n", - " value=Call(\n", - " func=Name(id='pid', ctx=Load()),\n", - " args=[],\n", - " keywords=[])),\n", - " Assign(\n", - " targets=[\n", - " Name(id='one', ctx=Store())],\n", - " value=Constant(value=1)),\n", - " Assign(\n", - " targets=[\n", - " Name(id='prev', ctx=Store())],\n", - " value=Call(\n", - " func=Attribute(\n", - " value=Call(\n", - " func=Name(id='hist', ctx=Load()),\n", - " args=[],\n", - " keywords=[]),\n", - " attr='lookup',\n", - " ctx=Load()),\n", - " args=[\n", - " Name(id='process_id', ctx=Load())],\n", - " keywords=[])),\n", - " If(\n", - " test=Name(id='prev', ctx=Load()),\n", - " body=[\n", - " Assign(\n", - " targets=[\n", - " Name(id='previous_value', ctx=Store())],\n", - " value=BinOp(\n", - " left=Name(id='prev', ctx=Load()),\n", - " op=Add(),\n", - " right=Constant(value=1))),\n", - " Expr(\n", - " value=Call(\n", - " func=Name(id='print', ctx=Load()),\n", - " args=[\n", - " JoinedStr(\n", - " values=[\n", - " Constant(value='count: '),\n", - " FormattedValue(\n", - " value=Name(id='previous_value', ctx=Load()),\n", - " conversion=-1),\n", - " Constant(value=' with '),\n", - " FormattedValue(\n", - " value=Name(id='process_id', ctx=Load()),\n", - " conversion=-1)])],\n", - " keywords=[])),\n", - " Expr(\n", - " value=Call(\n", - " func=Attribute(\n", - " value=Call(\n", - " func=Name(id='hist', ctx=Load()),\n", - " args=[],\n", - " keywords=[]),\n", - " attr='update',\n", - " ctx=Load()),\n", - " args=[\n", - " Name(id='process_id', ctx=Load()),\n", - " Name(id='previous_value', ctx=Load())],\n", - " keywords=[])),\n", - " Return(\n", - " value=Call(\n", - " func=Name(id='c_int64', ctx=Load()),\n", - " args=[\n", - " Constant(value=0)],\n", - " keywords=[]))],\n", - " orelse=[\n", - " Expr(\n", - " value=Call(\n", - " func=Attribute(\n", - " value=Call(\n", - " func=Name(id='hist', ctx=Load()),\n", - " args=[],\n", - " keywords=[]),\n", - " attr='update',\n", - " ctx=Load()),\n", - " args=[\n", - " Name(id='process_id', ctx=Load()),\n", - " Name(id='one', ctx=Load())],\n", - " keywords=[]))]),\n", - " Return(\n", - " value=Call(\n", - " func=Name(id='c_int64', ctx=Load()),\n", - " args=[\n", - " Constant(value=0)],\n", - " keywords=[]))],\n", - " decorator_list=[\n", - " Name(id='bpf', ctx=Load()),\n", - " Call(\n", - " func=Name(id='section', ctx=Load()),\n", - " args=[\n", - " Constant(value='tracepoint/syscalls/sys_enter_clone')],\n", - " keywords=[])],\n", - " returns=Name(id='c_int64', ctx=Load()),\n", - " type_params=[]),\n", - " FunctionDef(\n", - " name='LICENSE',\n", - " args=arguments(\n", - " posonlyargs=[],\n", - " args=[],\n", - " kwonlyargs=[],\n", - " kw_defaults=[],\n", - " defaults=[]),\n", - " body=[\n", - " Return(\n", - " value=Constant(value='GPL'))],\n", - " decorator_list=[\n", - " Name(id='bpf', ctx=Load()),\n", - " Name(id='bpfglobal', ctx=Load())],\n", - " returns=Name(id='str', ctx=Load()),\n", - " type_params=[]),\n", - " Assign(\n", - " targets=[\n", - " Name(id='b', ctx=Store())],\n", - " value=Call(\n", - " func=Name(id='BPF', ctx=Load()),\n", - " args=[],\n", - " keywords=[]))],\n", - " type_ignores=[])\n", - "Found BPF function/struct: hist\n", - "Found BPF function/struct: hello\n", - "Found BPF function/struct: LICENSE\n", - "Found BPF map: hist\n", - "Processing BPF map: hist\n", - "Creating HashMap map: hist\n", - "Map parameters: {'type': 'HASH', 'key': 'c_int32', 'value': 'c_uint64', 'max_entries': 4096}\n", - "Created BPF map: hist\n", - "Found probe_string of hello: tracepoint/syscalls/sys_enter_clone\n", - "Pre-allocated variable process_id for helper\n", - "Pre-allocated variable one of type c_int64\n", - "Pre-allocated variable prev for map\n", - "Pre-allocated variable previous_value of type c_int64\n", - "Local symbol table: dict_keys(['process_id', 'one', 'prev', 'previous_value'])\n", - "Processing statement: Assign(targets=[Name(id='process_id', ctx=Store())], value=Call(func=Name(id='pid', ctx=Load()), args=[], keywords=[]))\n", - "Handling assignment to Name(id='process_id', ctx=Store())\n", - "Assignment call type: pid\n", - "Assigned constant pid to process_id\n", - "Processing statement: Assign(targets=[Name(id='one', ctx=Store())], value=Constant(value=1))\n", - "Handling assignment to Name(id='one', ctx=Store())\n", - "Assigned constant 1 to one\n", - "Processing statement: Assign(targets=[Name(id='prev', ctx=Store())], value=Call(func=Attribute(value=Call(func=Name(id='hist', ctx=Load()), args=[], keywords=[]), attr='lookup', ctx=Load()), args=[Name(id='process_id', ctx=Load())], keywords=[]))\n", - "Handling assignment to Name(id='prev', ctx=Store())\n", - "Assignment call attribute: Attribute(value=Call(func=Name(id='hist', ctx=Load()), args=[], keywords=[]), attr='lookup', ctx=Load())\n", - "{}\n", - "Processing statement: If(test=Name(id='prev', ctx=Load()), body=[Assign(targets=[Name(id='previous_value', ctx=Store())], value=BinOp(left=Name(id='prev', ctx=Load()), op=Add(), right=Constant(value=1))), Expr(value=Call(func=Name(id='print', ctx=Load()), args=[JoinedStr(values=[Constant(value='count: '), FormattedValue(value=Name(id='previous_value', ctx=Load()), conversion=-1), Constant(value=' with '), FormattedValue(value=Name(id='process_id', ctx=Load()), conversion=-1)])], keywords=[])), Expr(value=Call(func=Attribute(value=Call(func=Name(id='hist', ctx=Load()), args=[], keywords=[]), attr='update', ctx=Load()), args=[Name(id='process_id', ctx=Load()), Name(id='previous_value', ctx=Load())], keywords=[])), Return(value=Call(func=Name(id='c_int64', ctx=Load()), args=[Constant(value=0)], keywords=[]))], orelse=[Expr(value=Call(func=Attribute(value=Call(func=Name(id='hist', ctx=Load()), args=[], keywords=[]), attr='update', ctx=Load()), args=[Name(id='process_id', ctx=Load()), Name(id='one', ctx=Load())], keywords=[]))])\n", - "Handling if statement\n", - "Processing statement: Assign(targets=[Name(id='previous_value', ctx=Store())], value=BinOp(left=Name(id='prev', ctx=Load()), op=Add(), right=Constant(value=1)))\n", - "Handling assignment to Name(id='previous_value', ctx=Store())\n", - "; ModuleID = \"/tmp/tmp98sm2v54.py\"\n", - "target triple = \"bpf\"\n", - "target datalayout = \"e-m:e-p:64:64-i64:64-i128:128-n32:64-S128\"\n", - "\n", - "@\"hist\" = dso_local global {ptr, ptr, ptr, ptr} zeroinitializer, section \".maps\", align 8, !dbg !22\n", - "define dso_local i64 @\"hello\"(ptr nocapture %\".1\") noinline nounwind optnone section \"tracepoint/syscalls/sys_enter_clone\"\n", - "{\n", - "entry:\n", - " %\"process_id\" = alloca i64, align 8\n", - " %\"one\" = alloca i64, align 8\n", - " %\"prev\" = alloca i64*\n", - " %\"previous_value\" = alloca i64, align 8\n", - " %\".3\" = inttoptr i64 14 to i64 ()*\n", - " %\".4\" = call i64 %\".3\"()\n", - " %\".5\" = and i64 %\".4\", 4294967295\n", - " store i64 %\".5\", i64* %\"process_id\"\n", - " store i64 1, i64* %\"one\"\n", - " %\".8\" = inttoptr i64 1 to ptr (ptr, ptr)*\n", - " %\".9\" = call ptr %\".8\"({ptr, ptr, ptr, ptr}* @\"hist\", i64* %\"process_id\")\n", - " store ptr %\".9\", i64** %\"prev\"\n", - " %\".11\" = load i64*, i64** %\"prev\"\n", - " %\".12\" = icmp ne i64* %\".11\", null\n", - " br i1 %\".12\", label %\"if.then\", label %\"if.else\"\n", - "if.then:\n", - "if.end:\n", - "if.else:\n", - "}\n", - "\n", - "!llvm.dbg.cu = !{ !1 }\n", - "!0 = !DIFile(directory: \"/tmp\", filename: \"/tmp/tmp98sm2v54.py\")\n", - "!1 = distinct !DICompileUnit(emissionKind: 1, file: !0, isOptimized: true, language: 29, nameTableKind: 0, producer: \"PythonBPF DSL Compiler\", runtimeVersion: 0, splitDebugInlining: false)\n", - "!2 = !DIBasicType(encoding: 7, name: \"unsigned int\", size: 32)\n", - "!3 = !DIBasicType(encoding: 7, name: \"unsigned long long\", size: 64)\n", - "!4 = !DISubrange(count: 1)\n", - "!5 = !{ !4 }\n", - "!6 = !DICompositeType(baseType: !2, elements: !5, size: 32, tag: 1)\n", - "!7 = !DIDerivedType(baseType: !6, size: 64, tag: 15)\n", - "!8 = !DIDerivedType(baseType: !2, size: 64, tag: 15)\n", - "!9 = !DIDerivedType(baseType: !3, size: 64, tag: 15)\n", - "!10 = !DIDerivedType(baseType: !7, file: !0, name: \"type\", offset: 0, size: 64, tag: 13)\n", - "!11 = !DIDerivedType(baseType: !8, file: !0, name: \"key\", offset: 64, size: 64, tag: 13)\n", - "!12 = !DIDerivedType(baseType: !9, file: !0, name: \"value\", offset: 128, size: 64, tag: 13)\n", - "!13 = !DISubrange(count: 4096)\n", - "!14 = !{ !13 }\n", - "!15 = !DICompositeType(baseType: !2, elements: !14, size: 32, tag: 1)\n", - "!16 = !DIDerivedType(baseType: !15, size: 64, tag: 15)\n", - "!17 = !DIDerivedType(baseType: !16, file: !0, name: \"max_entries\", offset: 192, size: 64, tag: 13)\n", - "!18 = !{ !10, !11, !12, !17 }\n", - "!19 = distinct !DICompositeType(elements: !18, file: !0, size: 256, tag: 19)\n", - "!20 = distinct !DIGlobalVariable(file: !0, isDefinition: true, isLocal: false, name: \"hist\", scope: !1, type: !19)\n", - "!21 = !DIExpression()\n", - "!22 = !DIGlobalVariableExpression(expr: !21, var: !20)\n", - "left is %\".15\" = load i64, i64* %\".14\", right is i64 1, op is \n", - "Processing statement: Expr(value=Call(func=Name(id='print', ctx=Load()), args=[JoinedStr(values=[Constant(value='count: '), FormattedValue(value=Name(id='previous_value', ctx=Load()), conversion=-1), Constant(value=' with '), FormattedValue(value=Name(id='process_id', ctx=Load()), conversion=-1)])], keywords=[]))\n", - "Handling expression: Expr(value=Call(func=Name(id='print', ctx=Load()), args=[JoinedStr(values=[Constant(value='count: '), FormattedValue(value=Name(id='previous_value', ctx=Load()), conversion=-1), Constant(value=' with '), FormattedValue(value=Name(id='process_id', ctx=Load()), conversion=-1)])], keywords=[]))\n", - "Evaluating expression: Call(func=Name(id='print', ctx=Load()), args=[JoinedStr(values=[Constant(value='count: '), FormattedValue(value=Name(id='previous_value', ctx=Load()), conversion=-1), Constant(value=' with '), FormattedValue(value=Name(id='process_id', ctx=Load()), conversion=-1)])], keywords=[])\n", - "Value in f-string: Constant(value='count: ')\n", - "Value in f-string: FormattedValue(value=Name(id='previous_value', ctx=Load()), conversion=-1)\n", - "Formatted value: FormattedValue(value=Name(id='previous_value', ctx=Load()), conversion=-1)\n", - "Formatted value variable: %\"previous_value\" = alloca i64, align 8 i64\n", - "Value in f-string: Constant(value=' with ')\n", - "Value in f-string: FormattedValue(value=Name(id='process_id', ctx=Load()), conversion=-1)\n", - "Formatted value: FormattedValue(value=Name(id='process_id', ctx=Load()), conversion=-1)\n", - "Formatted value variable: %\"process_id\" = alloca i64, align 8 i64\n", - "Evaluating expression: Name(id='previous_value', ctx=Load())\n", - "Evaluating expression: Name(id='process_id', ctx=Load())\n", - "Processing statement: Expr(value=Call(func=Attribute(value=Call(func=Name(id='hist', ctx=Load()), args=[], keywords=[]), attr='update', ctx=Load()), args=[Name(id='process_id', ctx=Load()), Name(id='previous_value', ctx=Load())], keywords=[]))\n", - "Handling expression: Expr(value=Call(func=Attribute(value=Call(func=Name(id='hist', ctx=Load()), args=[], keywords=[]), attr='update', ctx=Load()), args=[Name(id='process_id', ctx=Load()), Name(id='previous_value', ctx=Load())], keywords=[]))\n", - "Evaluating expression: Call(func=Attribute(value=Call(func=Name(id='hist', ctx=Load()), args=[], keywords=[]), attr='update', ctx=Load()), args=[Name(id='process_id', ctx=Load()), Name(id='previous_value', ctx=Load())], keywords=[])\n", - "Handling method call: Attribute(value=Call(func=Name(id='hist', ctx=Load()), args=[], keywords=[]), attr='update', ctx=Load())\n", - "{}\n", - "Processing statement: Return(value=Call(func=Name(id='c_int64', ctx=Load()), args=[Constant(value=0)], keywords=[]))\n", - "Processing statement: Expr(value=Call(func=Attribute(value=Call(func=Name(id='hist', ctx=Load()), args=[], keywords=[]), attr='update', ctx=Load()), args=[Name(id='process_id', ctx=Load()), Name(id='one', ctx=Load())], keywords=[]))\n", - "Handling expression: Expr(value=Call(func=Attribute(value=Call(func=Name(id='hist', ctx=Load()), args=[], keywords=[]), attr='update', ctx=Load()), args=[Name(id='process_id', ctx=Load()), Name(id='one', ctx=Load())], keywords=[]))\n", - "Evaluating expression: Call(func=Attribute(value=Call(func=Name(id='hist', ctx=Load()), args=[], keywords=[]), attr='update', ctx=Load()), args=[Name(id='process_id', ctx=Load()), Name(id='one', ctx=Load())], keywords=[])\n", - "Handling method call: Attribute(value=Call(func=Name(id='hist', ctx=Load()), args=[], keywords=[]), attr='update', ctx=Load())\n", - "{}\n", - "Processing statement: Return(value=Call(func=Name(id='c_int64', ctx=Load()), args=[Constant(value=0)], keywords=[]))\n", - "IR written to /tmp/tmpx05plfu1.ll\n" - ] - } - ], - "source": [ - "@bpf\n", - "@map\n", - "def hist() -> HashMap:\n", - " return HashMap(key=c_int32, value=c_uint64, max_entries=4096)\n", - "\n", - "@bpf\n", - "@section(\"tracepoint/syscalls/sys_enter_clone\")\n", - "def hello(ctx: c_void_p) -> c_int64:\n", - " process_id = pid()\n", - " one = 1\n", - " prev = hist().lookup(process_id)\n", - " if prev:\n", - " previous_value = prev + 1\n", - " print(f\"count: {previous_value} with {process_id}\")\n", - " hist().update(process_id, previous_value)\n", - " return c_int64(0)\n", - " else:\n", - " hist().update(process_id, one)\n", - " return c_int64(0)\n", - "\n", - "\n", - "@bpf\n", - "@bpfglobal\n", - "def LICENSE() -> str:\n", - " return \"GPL\"\n", - "\n", - "b = BPF()" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "93fae9f8-464e-48d6-b61e-57b9f93e508a", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Recording\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjcAAAHHCAYAAABDUnkqAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAOxxJREFUeJzt3XlcFuX+//H3Dcoa4MoiEK64C65FllhipOQR66jZgnuLWpqlHU99XbKi8mhquZxOqXXMPbXSXMg1lTJLTM00V1IBdxBKVJjfH/68T3eAwi1ww/R6Ph7zeJy55rrm/szk8X57zcw9FsMwDAEAAJiEk6MLAAAAKE6EGwAAYCqEGwAAYCqEGwAAYCqEGwAAYCqEGwAAYCqEGwAAYCqEGwAAYCqEGwAAYCqEGwDFwmKxaOzYsdb1OXPmyGKx6OjRo7e87+LcFwDzI9wAZcTu3bv197//XSEhIXJzc1NgYKA6duyod99919GlwQS2bdumsWPH6sKFC44uBShxhBugDNi2bZtatWqlXbt2aeDAgXrvvfc0YMAAOTk5acqUKY4uDyawbds2jRs3jnCDv4QKji4AgPT666/Lx8dH3333nSpVqmSz7dSpU44pCgDKKWZugDLg0KFDaty4cZ5gI0m+vr7W/x0ZGamwsLB891G/fn1FR0db1xcsWKCWLVvKy8tL3t7eatq0aZ5ZoAsXLuj5559XzZo15erqqqCgIMXFxenMmTOSpMuXL2v06NFq2bKlfHx85OnpqXvuuUcbNmwohqP+n59//lk9evRQ9erV5e7urvr16+vll1++6bjp06ercePGcnV1VY0aNTR48OA8MxPt27dXkyZN9NNPP+nee++Vh4eHAgMD9fbbb+fZX3Z2tsaMGaO6devK1dVVwcHBGjlypLKzswt1HN9++606d+6sypUry9PTU82aNctzztevX6977rlHnp6eqlSpkrp27ap9+/bZ9OnTp49q1qyZZ/9jx46VxWKxabNYLBoyZIiWL1+uJk2ayNXVVY0bN9bq1attxo0YMUKSVKtWLVksFpt7mBISEnT33XerUqVKuu2221S/fn3985//LNQxA2URMzdAGRASEqLExETt2bNHTZo0KbDfE088oYEDB+bp99133+nAgQN65ZVXJF37surVq5c6dOigt956S5K0b98+bd26VUOHDpUkZWZm6p577tG+ffvUr18/tWjRQmfOnNHnn3+u48ePq1q1asrIyNAHH3ygXr16aeDAgbp48aI+/PBDRUdHa/v27QoPD7/lY//xxx91zz33qGLFinryySdVs2ZNHTp0SF988YVef/31AseNHTtW48aNU1RUlJ555hnt379fM2bM0HfffaetW7eqYsWK1r7nz5/XAw88oIceekg9evTQkiVL9NJLL6lp06bq1KmTJCk3N1d/+9vftGXLFj355JNq2LChdu/erXfeeUcHDhzQ8uXLb3gcCQkJevDBBxUQEKChQ4fK399f+/bt04oVK6zn/KuvvlKnTp1Uu3ZtjR07Vr///rveffddtW3bVj/88EO+gaYwtmzZoqVLl2rQoEHy8vLS1KlT9fDDDys5OVlVq1bVQw89pAMHDmj+/Pl65513VK1aNUlS9erVtXfvXj344INq1qyZXn31Vbm6uurgwYPaunWrXbUAZYIBwOHWrl1rODs7G87OzkZERIQxcuRIY82aNcbly5dt+l24cMFwc3MzXnrpJZv25557zvD09DQyMzMNwzCMoUOHGt7e3sbVq1cL/MzRo0cbkoylS5fm2Zabm2sYhmFcvXrVyM7Ottl2/vx5w8/Pz+jXr59NuyRjzJgx1vXZs2cbkowjR47c8NjbtWtneHl5GceOHcu3hvz2derUKcPFxcW4//77jZycHGu/9957z5BkzJo1y9oWGRlpSDI+/vhja1t2drbh7+9vPPzww9a2//73v4aTk5Px9ddf29Qxc+ZMQ5KxdevWAo/h6tWrRq1atYyQkBDj/PnzBR5HeHi44evra5w9e9batmvXLsPJycmIi4uztvXu3dsICQnJ8zljxowx/vzXtiTDxcXFOHjwoM0+JRnvvvuutW3ChAn5/vd45513DEnG6dOnCzw+oLzhshRQBnTs2FGJiYn629/+pl27duntt99WdHS0AgMD9fnnn1v7+fj4qGvXrpo/f74Mw5Ak5eTkaOHChYqNjZWnp6ckqVKlSsrKylJCQkKBn/npp58qLCxM3bp1y7Pt+qUPZ2dnubi4SLo2s3Hu3DldvXpVrVq10g8//HDLx3369Glt3rxZ/fr10+23355vDfn56quvdPnyZQ0bNkxOTv/7a2zgwIHy9vbWypUrbfrfdtttevzxx63rLi4uatOmjQ4fPmxtW7x4sRo2bKgGDRrozJkz1uW+++6TpBteitu5c6eOHDmiYcOG5bm0eP04UlJSlJSUpD59+qhKlSrW7c2aNVPHjh315ZdfFrj/m4mKilKdOnVs9unt7W1zfAW5Xu9nn32m3Nxcu2sAyhLCDVBGtG7dWkuXLtX58+e1fft2jRo1ShcvXtTf//53/fTTT9Z+cXFxSk5O1tdffy3p2hd9WlqannjiCWufQYMGKTQ0VJ06dVJQUJD69etncw+GdO0+nxtdArvuo48+UrNmzeTm5qaqVauqevXqWrlypdLT02/5mK9/+Ramjj86duyYpGv3Gf2Ri4uLateubd1+XVBQUJ6wVLlyZZ0/f966/ssvv2jv3r2qXr26zRIaGirpxjd2Hzp06KbHUVDNktSwYUOdOXNGWVlZBY6/kT8HQynv8RWkZ8+eatu2rQYMGCA/Pz898sgjWrRoEUEH5Rr33ABljIuLi1q3bq3WrVsrNDRUffv21eLFizVmzBhJUnR0tPz8/DR37ly1a9dOc+fOlb+/v6Kioqz78PX1VVJSktasWaNVq1Zp1apVmj17tuLi4vTRRx8Vupa5c+eqT58+io2N1YgRI+Tr6ytnZ2fFx8dbv9DLA2dn53zbr89+Sddmppo2bapJkybl2zc4OLhEastPQbNWOTk5+bYX5vgK4u7urs2bN2vDhg1auXKlVq9erYULF+q+++7T2rVrC9w3UJYxcwOUYa1atZJ07ZLGdc7Oznr00Ue1ZMkSnT9/XsuXL1evXr3yfAm5uLioS5cumj59ug4dOqSnnnpKH3/8sQ4ePChJqlOnjvbs2XPDz1+yZIlq166tpUuX6oknnlB0dLSioqJ06dKlYjm+2rVrS9JN6/izkJAQSdL+/ftt2i9fvqwjR45YtxdFnTp1dO7cOXXo0EFRUVF5lvxmXP44VrrxcRRUs3TtabFq1apZLytWrlw539+j+fOMVFHc6DKfk5OTOnTooEmTJumnn37S66+/rvXr1xf7U3FAaSHcAGXAhg0b8v1X9vX7MP78xfrEE0/o/Pnzeuqpp5SZmWlzP4kknT171mbdyclJzZo1kyTrY80PP/ywdu3apWXLluX53Ou1XA9Mf6zt22+/VWJiYpGOryDVq1dXu3btNGvWLCUnJ+dbQ36ioqLk4uKiqVOn2vT78MMPlZ6erpiYmCLX0qNHD504cUL/+c9/8mz7/fffb3jJqEWLFqpVq5YmT56cJ5Rcry8gIEDh4eH66KOPbPrs2bNHa9euVefOna1tderUUXp6un788UdrW0pKSr7/rQrrenD6c33nzp3L0/f6U3CFfQQeKGu4LAWUAc8++6x+++03devWTQ0aNNDly5e1bds2LVy4UDVr1lTfvn1t+jdv3lxNmjSx3gTbokULm+0DBgzQuXPndN999ykoKEjHjh3Tu+++q/DwcDVs2FCSNGLECC1ZskTdu3dXv3791LJlS507d06ff/65Zs6cqbCwMD344INaunSpunXrppiYGB05ckQzZ85Uo0aNlJmZWSzHPnXqVN19991q0aKFnnzySdWqVUtHjx7VypUrlZSUlO+Y6tWra9SoURo3bpweeOAB/e1vf9P+/fs1ffp0tW7dOk/YK4wnnnhCixYt0tNPP60NGzaobdu2ysnJ0c8//6xFixZpzZo11pm0P3NyctKMGTPUpUsXhYeHq2/fvgoICNDPP/+svXv3as2aNZKkCRMmqFOnToqIiFD//v2tj4L7+PjYvJfrkUce0UsvvaRu3brpueee02+//aYZM2YoNDTU7hu5W7ZsKUl6+eWX9cgjj6hixYrq0qWLXn31VW3evFkxMTEKCQnRqVOnNH36dAUFBenuu++267MAh3PYc1oArFatWmX069fPaNCggXHbbbcZLi4uRt26dY1nn33WSEtLy3fM22+/bUgy3njjjTzblixZYtx///2Gr6+v4eLiYtx+++3GU089ZaSkpNj0O3v2rDFkyBAjMDDQcHFxMYKCgozevXsbZ86cMQzj2mPMb7zxhhESEmK4uroazZs3N1asWJHvo8qy81FwwzCMPXv2GN26dTMqVapkuLm5GfXr1zf+7//+76b7eu+994wGDRoYFStWNPz8/Ixnnnkmz6PYkZGRRuPGjfN8Zn7HcPnyZeOtt94yGjdubLi6uhqVK1c2WrZsaYwbN85IT0+/6XFs2bLF6Nixo+Hl5WV4enoazZo1s3kc2zAM46uvvjLatm1ruLu7G97e3kaXLl2Mn376Kc++1q5dazRp0sRwcXEx6tevb8ydO7fAR8EHDx6cZ3xISIjRu3dvm7bx48cbgYGBhpOTk/V8rlu3zujatatRo0YNw8XFxahRo4bRq1cv48CBAzc9XqCsshhGIe44A1DmTJkyRc8//7yOHj2a79MyAPBXRbgByiHDMBQWFqaqVaty0ycA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AABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMhXADAABMxaHhJj4+Xq1bt5aXl5d8fX0VGxur/fv333Tc4sWL1aBBA7m5ualp06b68ssvS6FaAABQHjg03GzatEmDBw/WN998o4SEBF25ckX333+/srKyChyzbds29erVS/3799fOnTsVGxur2NhY7dmzpxQrBwAAZZXFMAzD0UVcd/r0afn6+mrTpk1q165dvn169uyprKwsrVixwtp25513Kjw8XDNnzrzpZ2RkZMjHx0fp6eny9vYuttoBACgJNf+x0tElFNnRN2OKfZ9F+f4uU/fcpKenS5KqVKlSYJ/ExERFRUXZtEVHRysxMTHf/tnZ2crIyLBZAACAeVVwdAHX5ebmatiwYWrbtq2aNGlSYL/U1FT5+fnZtPn5+Sk1NTXf/vHx8Ro3blyx1nojJGwAAByrzMzcDB48WHv27NGCBQuKdb+jRo1Senq6dfn111+Ldf8AAKBsKRMzN0OGDNGKFSu0efNmBQUF3bCvv7+/0tLSbNrS0tLk7++fb39XV1e5uroWW60AAKBsc+jMjWEYGjJkiJYtW6b169erVq1aNx0TERGhdevW2bQlJCQoIiKipMoEAADliENnbgYPHqx58+bps88+k5eXl/W+GR8fH7m7u0uS4uLiFBgYqPj4eEnS0KFDFRkZqYkTJyomJkYLFizQjh079P777zvsOAAAQNnh0JmbGTNmKD09Xe3bt1dAQIB1WbhwobVPcnKyUlJSrOt33XWX5s2bp/fff19hYWFasmSJli9ffsObkAEAwF+HQ2duCvMTOxs3bszT1r17d3Xv3r0EKgIAAOVdmXlaCgAAoDgQbgAAgKkQbgAAgKkQbgAAgKkQbgAAgKkQbgAAgKkQbgAAgKkQbgAAgKkQbgAAgKkQbgAAgKnYFW4OHz5c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AKWpgiM/vFq1anJ2dlZaWppNe1pamvz9/Qu1j4oVK6p58+Y6ePBgvttdXV3l6up6y7UCAIDywaEzNy4uLmrZsqXWrVtnbcvNzdW6desUERFRqH3k5ORo9+7dCggIKKkyAQBAOeLQmRtJGj58uHr37q1WrVqpTZs2mjx5srKystS3b19JUlxcnAIDAxUfHy9JevXVV3XnnXeqbt26unDhgiZMmKBjx45pwIABjjwMAABQRjg83PTs2VOnT5/W6NGjlZqaqvDwcK1evdp6k3FycrKcnP43wXT+/HkNHDhQqampqly5slq2bKlt27apUaNGjjoEAABQhlgMwzAcXURpysjIkI+Pj9LT0+Xt7V3s+6/5j5XFvs+SdvTNGEeXAAAoAN8r1xTl+7vcPS0FAABwI4QbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKoQbAABgKmUi3EybNk01a9aUm5ub7rjjDm3fvv2G/RcvXqwGDRrIzc1NTZs21ZdffllKlQIAgLLO4eFm4cKFGj58uMaMGaMffvhBYWFhio6O1qlTp/Ltv23bNvXq1Uv9+/fXzp07FRsbq9jYWO3Zs6eUKwcAAGWRw8PNpEmTNHDgQPXt21eNGjXSzJkz5eHhoVmzZuXbf8qUKXrggQc0YsQINWzYUOPHj1eLFi303nvvlXLlAACgLHJouLl8+bK+//57RUVFWducnJwUFRWlxMTEfMckJiba9Jek6OjoAvsDAIC/lgqO/PAzZ84oJydHfn5+Nu1+fn76+eef8x2Tmpqab//U1NR8+2dnZys7O9u6np6eLknKyMi4ldILlJv9W4nstySV1LkAANw6vlds92kYxk37OjTclIb4+HiNGzcuT3twcLADqimbfCY7ugIAgJmU5PfKxYsX5ePjc8M+Dg031apVk7Ozs9LS0mza09LS5O/vn+8Yf3//IvUfNWqUhg8fbl3Pzc3VuXPnVLVqVVkslls8AlsZGRkKDg7Wr7/+Km9v72LdN/6H81w6OM+lg/NcejjXpaOkzrNhGLp48aJq1Khx074ODTcuLi5q2bKl1q1bp9jYWEnXwse6des0ZMiQfMdERERo3bp1GjZsmLUtISFBERER+fZ3dXWVq6urTVulSpWKo/wCeXt783+cUsB5Lh2c59LBeS49nOvSURLn+WYzNtc5/LLU8OHD1bt3b7Vq1Upt2rTR5MmTlZWVpb59+0qS4uLiFBgYqPj4eEnS0KFDFRkZqYkTJyomJkYLFizQjh079P777zvyMAAAQBnh8HDTs2dPnT59WqNHj1ZqaqrCw8O1evVq603DycnJcnL630Ndd911l+bNm6dXXnlF//znP1WvXj0tX75cTZo0cdQhAACAMsTh4UaShgwZUuBlqI0bN+Zp6969u7p3717CVRWdq6urxowZk+cyGIoX57l0cJ5LB+e59HCuS0dZOM8WozDPVAEAAJQTDv+FYgAAgOJEuAEAAKZCuAEAAKZCuAEAAKZCuCmkzZs3q0uXLqpRo4YsFouWL19+0zEbN25UixYt5Orqqrp162rOnDklXqcZFPVcL126VB07dlT16tXl7e2tiIgIrVmzpnSKLcfs+TN93datW1WhQgWFh4eXWH1mYc95zs7O1ssvv6yQkBC5urqqZs2amjVrVskXW47Zc54/+eQThYWFycPDQwEBAerXr5/Onj1b8sWWY/Hx8WrdurW8vLzk6+ur2NhY7d+//6bjFi9erAYNGsjNzU1NmzbVl19+WaJ1Em4KKSsrS2FhYZo2bVqh+h85ckQxMTG69957lZSUpGHDhmnAgAF86RZCUc/15s2b1bFjR3355Zf6/vvvde+996pLly7auXNnCVdavhX1PF934cIFxcXFqUOHDiVUmbnYc5579OihdevW6cMPP9T+/fs1f/581a9fvwSrLP+Kep63bt2quLg49e/fX3v37tXixYu1fft2DRw4sIQrLd82bdqkwYMH65tvvlFCQoKuXLmi+++/X1lZWQWO2bZtm3r16qX+/ftr586dio2NVWxsrPbs2VNyhRooMknGsmXLbthn5MiRRuPGjW3aevbsaURHR5dgZeZTmHOdn0aNGhnjxo0r/oJMqijnuWfPnsYrr7xijBkzxggLCyvRusymMOd51apVho+Pj3H27NnSKcqECnOeJ0yYYNSuXdumberUqUZgYGAJVmY+p06dMiQZmzZtKrBPjx49jJiYGJu2O+64w3jqqadKrC5mbkpIYmKioqKibNqio6OVmJjooIr+OnJzc3Xx4kVVqVLF0aWYzuzZs3X48GGNGTPG0aWY1ueff65WrVrp7bffVmBgoEJDQ/Xiiy/q999/d3RpphIREaFff/1VX375pQzDUFpampYsWaLOnTs7urRyJT09XZJu+PetI74Py8QvFJtRamqq9RUS1/n5+SkjI0O///673N3dHVSZ+f3rX/9SZmamevTo4ehSTOWXX37RP/7xD3399deqUIG/OkrK4cOHtWXLFrm5uWnZsmU6c+aMBg0apLNnz2r27NmOLs802rZtq08++UQ9e/bUpUuXdPXqVXXp0qXIl2n/ynJzczVs2DC1bdv2hq9AKuj7MDU1tcRqY+YGpjJv3jyNGzdOixYtkq+vr6PLMY2cnBw9+uijGjdunEJDQx1djqnl5ubKYrHok08+UZs2bdS5c2dNmjRJH330EbM3xeinn37S0KFDNXr0aH3//fdavXq1jh49qqefftrRpZUbgwcP1p49e7RgwQJHl5IH//wqIf7+/kpLS7NpS0tLk7e3N7M2JWTBggUaMGCAFi9enGcKFLfm4sWL2rFjh3bu3Gl9D1xubq4Mw1CFChW0du1a3XfffQ6u0hwCAgIUGBgoHx8fa1vDhg1lGIaOHz+uevXqObA684iPj1fbtm01YsQISVKzZs3k6empe+65R6+99poCAgIcXGHZNmTIEK1YsUKbN29WUFDQDfsW9H3o7+9fYvUxc1NCIiIitG7dOpu2hIQERUREOKgic5s/f7769u2r+fPnKyYmxtHlmI63t7d2796tpKQk6/L000+rfv36SkpK0h133OHoEk2jbdu2OnnypDIzM61tBw4ckJOT002/RFB4v/32m5ycbL8CnZ2dJUkGr1wskGEYGjJkiJYtW6b169erVq1aNx3jiO9DZm4KKTMzUwcPHrSuHzlyRElJSapSpYpuv/12jRo1SidOnNDHH38sSXr66af13nvvaeTIkerXr5/Wr1+vRYsWaeXKlY46hHKjqOd63rx56t27t6ZMmaI7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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "\n", - "b.load_and_attach()\n", - "hist = BpfMap(b, hist)\n", - "print(\"Recording\")\n", - "time.sleep(10)\n", - "\n", - "counts = list(hist.values())\n", - "\n", - "plt.hist(counts, bins=10)\n", - "plt.xlabel(\"Clone calls per PID\")\n", - "plt.ylabel(\"Frequency\")\n", - "plt.title(\"Syscall clone counts\")\n", - "plt.show()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/demo/pybpf0.py b/demo/pybpf0.py deleted file mode 100644 index b292ced..0000000 --- a/demo/pybpf0.py +++ /dev/null @@ -1,23 +0,0 @@ -from pythonbpf import bpf, section, bpfglobal, compile - -from ctypes import c_void_p, c_int64 - -# Instructions to how to run this program -# 1. Install PythonBPF: pip install pythonbpf -# 2. Run the program: python demo/pybpf0.py -# 3. Run the program with sudo: sudo examples/check.sh run demo/pybpf0.o -# 4. Start up any program and watch the output - - -@bpf -@section("tracepoint/syscalls/sys_enter_execve") -def hello_world(ctx: c_void_p) -> c_int64: - print("Hello, World!") - return c_int64(0) - -@bpf -@bpfglobal -def LICENSE() -> str: - return "GPL" - -compile() diff --git a/demo/pybpf1.py b/demo/pybpf1.py deleted file mode 100644 index 409e553..0000000 --- a/demo/pybpf1.py +++ /dev/null @@ -1,41 +0,0 @@ -from pythonbpf import bpf, map, section, bpfglobal, compile -from pythonbpf.helpers import XDP_PASS -from pythonbpf.maps import HashMap - -from ctypes import c_void_p, c_int64 - -# Instructions to how to run this program -# 1. Install PythonBPF: pip install pythonbpf -# 2. Run the program: python demo/pybpf1.py -# 3. Run the program with sudo: sudo examples/check.sh run demo/pybpf1.o -# 4. Attach object file to any network device with something like ./check.sh xdp ../demo/pybpf1.o tailscale0 -# 5. send traffic through the device and observe effects - -@bpf -@map -def count() -> HashMap: - return HashMap(key=c_int64, value=c_int64, max_entries=1) - - -@bpf -@section("xdp") -def hello_world(ctx: c_void_p) -> c_int64: - key = 0 - one = 1 - prev = count().lookup(key) - if prev: - prevval = prev + 1 - print(f"count: {prevval}") - count().update(key, prevval) - return XDP_PASS - else: - count().update(key, one) - - return XDP_PASS - -@bpf -@bpfglobal -def LICENSE() -> str: - return "GPL" - -compile() diff --git a/demo/pybpf2.py b/demo/pybpf2.py deleted file mode 100644 index 94e0d6a..0000000 --- a/demo/pybpf2.py +++ /dev/null @@ -1,43 +0,0 @@ -from pythonbpf import bpf, map, section, bpfglobal, compile -from pythonbpf.helpers import ktime -from pythonbpf.maps import HashMap - -from ctypes import c_void_p, c_int64, c_uint64 - -# Instructions to how to run this program -# 1. Install PythonBPF: pip install pythonbpf -# 2. Run the program: python demo/pybpf2.py -# 3. Run the program with sudo: sudo examples/check.sh run demo/pybpf2.o -# 4. Start a Python repl and `import os` and then keep entering `os.sync()` to see reponses. - -@bpf -@map -def last() -> HashMap: - return HashMap(key=c_uint64, value=c_uint64, max_entries=3) - - -@bpf -@section("tracepoint/syscalls/sys_enter_sync") -def do_trace(ctx: c_void_p) -> c_int64: - key = 0 - tsp = last().lookup(key) - if tsp: - kt = ktime() - delta = (kt - tsp) - if delta < 1000000000: - time_ms = (delta // 1000000) - print(f"sync called within last second, last {time_ms} ms ago") - last().delete(key) - else: - kt = ktime() - last().update(key, kt) - return c_int64(0) - - -@bpf -@bpfglobal -def LICENSE() -> str: - return "GPL" - - -compile() diff --git a/demo/pybpf3.py b/demo/pybpf3.py deleted file mode 100644 index 13897c1..0000000 --- a/demo/pybpf3.py +++ /dev/null @@ -1,52 +0,0 @@ -from pythonbpf import bpf, map, section, bpfglobal, compile -from pythonbpf.helpers import ktime -from pythonbpf.maps import HashMap - -from ctypes import c_void_p, c_int64, c_uint64 - -# Instructions to how to run this program -# 1. Install PythonBPF: pip install pythonbpf -# 2. Run the program: python demo/pybpf3.py -# 3. Run the program with sudo: sudo examples/check.sh run demo/pybpf3.o -# 4. Start up any program and watch the output - -@bpf -@map -def last() -> HashMap: - return HashMap(key=c_uint64, value=c_uint64, max_entries=3) - - -@bpf -@section("tracepoint/syscalls/sys_enter_execve") -def do_trace(ctx: c_void_p) -> c_int64: - key = 0 - tsp = last().lookup(key) - if tsp: - kt = ktime() - delta = (kt - tsp) - if delta < 1000000000: - time_ms = (delta // 1000000) - print(f"Execve syscall entered within last second, last {time_ms} ms ago") - last().delete(key) - else: - kt = ktime() - last().update(key, kt) - return c_int64(0) - -@bpf -@section("tracepoint/syscalls/sys_exit_execve") -def do_exit(ctx: c_void_p) -> c_int64: - va = 8 - nm = 5 ^ va - al = 6 & 3 - ru = (nm + al) - print(f"this is a variable {ru}") - return c_int64(0) - -@bpf -@bpfglobal -def LICENSE() -> str: - return "GPL" - - -compile() diff --git a/demo/pybpf4.py b/demo/pybpf4.py deleted file mode 100644 index 8e9fd87..0000000 --- a/demo/pybpf4.py +++ /dev/null @@ -1,56 +0,0 @@ -import time - -from pythonbpf import bpf, map, section, bpfglobal, BPF -from pythonbpf.helpers import pid -from pythonbpf.maps import HashMap -from pylibbpf import * -from ctypes import c_void_p, c_int64, c_uint64, c_int32 -import matplotlib.pyplot as plt - -# This program attaches an eBPF tracepoint to sys_enter_clone, -# counts per-PID clone syscalls, stores them in a hash map, -# and then plots the distribution as a histogram using matplotlib. -# It provides a quick view of process creation activity over 10 seconds. -# Everything is done with Python only code and with the new pylibbpf library. -# Run `sudo /path/to/python/binary/ pybpf4.py` - -@bpf -@map -def hist() -> HashMap: - return HashMap(key=c_int32, value=c_uint64, max_entries=4096) - -@bpf -@section("tracepoint/syscalls/sys_enter_clone") -def hello(ctx: c_void_p) -> c_int64: - process_id = pid() - one = 1 - prev = hist().lookup(process_id) - if prev: - previous_value = prev + 1 - print(f"count: {previous_value} with {process_id}") - hist().update(process_id, previous_value) - return c_int64(0) - else: - hist().update(process_id, one) - return c_int64(0) - - -@bpf -@bpfglobal -def LICENSE() -> str: - return "GPL" - - -b = BPF() -b.load_and_attach() -hist = BpfMap(b, hist) -print("Recording") -time.sleep(10) - -counts = list(hist.values()) - -plt.hist(counts, bins=20) -plt.xlabel("Clone calls per PID") -plt.ylabel("Frequency") -plt.title("Syscall clone counts") -plt.show()