KernelTrace 1.0.0

dotnet add package KernelTrace --version 1.0.0
                    
NuGet\Install-Package KernelTrace -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="KernelTrace" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="KernelTrace" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="KernelTrace" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add KernelTrace --version 1.0.0
                    
#r "nuget: KernelTrace, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package KernelTrace@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=KernelTrace&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=KernelTrace&version=1.0.0
                    
Install as a Cake Tool

KernelTrace

In-process eBPF kernel tracing for .NET

KernelTrace bridges Linux eBPF and the .NET managed runtime. Attach to kernel tracepoints, kprobes, and uprobes, stream type-safe events through a lock-free ring buffer — all from within your own process, with zero sidecars or agents.

NuGet License: MIT Platform: Linux .NET: 8 | 9 | 10


Why KernelTrace?

Kernel observability for .NET has historically meant spawning external processes (bpftrace, perf, BCC) and parsing their text output, or writing C/Python BPF glue code. KernelTrace removes the indirection entirely.

Without KernelTrace With KernelTrace
Separate agent process or sidecar Runs inside your process — no IPC overhead
Text output parsing, string allocations Typed C# structs, zero-copy ProcessAsync<T>
Manual P/Invoke struct layout Source generator maps C↔C# at build time
Root or broad CAP_SYS_ADMIN Just CAP_BPF + CAP_PERFMON on your binary
Custom threading and polling glue Lock-free ring buffer with AboveNormal polling thread
No integration with .NET tooling IAsyncEnumerable<T>, CancellationToken, IHostedService
No metrics story Built-in System.Diagnostics.Metrics

Features

Capability Details
Tracepoints Stable kernel ABI — tp/sched/sched_switch, tp/syscalls/sys_enter_*, etc.
kprobes / kretprobes Arbitrary kernel function entry and return
uprobes / uretprobes User-space function tracing (libc, JVM, .NET runtime, …)
USDT probes Attach to DTrace/SystemTap probe points in Python, Node.js, and any SDT-annotated binary
Lock-free ring buffer mmap'd BPF_MAP_TYPE_RINGBUF consumer — zero syscalls per event
BPF map access Read/write any BPF map (BpfMap<TKey,TValue>) from .NET — hash, array, LRU, and more
Stack traces StackTraceMap + KernelSymbolResolver for kernel and user-space frame symbolization
In-kernel aggregation Use BpfMap to read pre-aggregated counters/histograms kept in kernel space
CO-RE support Supply a custom BTF archive (BtfCustomPath) for kernels without built-in BTF
Source generator Auto-generates C# structs from .bpf.c definitions at build time
BTF validation Struct size verified against kernel BTF on session start
IAsyncEnumerable Idiomatic await foreach event streaming
Zero-copy callbacks ProcessAsync<T> — callback receives a ref readonly T from mmap memory
Raw byte streaming ReadRawAsync() — consume un-typed events for dynamic/multi-schema probes
ILogger integration .WithLogging() and LogEventsAsync() for structured event logging
Hot attach/detach Add and remove probes while the session is running
Current-process filter CurrentProcessOnly=true drops foreign PIDs in-kernel
Metrics System.Diagnostics.Metrics out of the box
ASP.NET Core hosting AddKernelTrace() + IHostedService integration
AOT-safe LibraryImport source-generated P/Invoke throughout

Quick Start

Prerequisites

  • Linux kernel 5.8+ with CONFIG_DEBUG_INFO_BTF=y
  • CAP_BPF + CAP_PERFMON (or root) — see Privileges below
  • libbpf 1.0+ (apt install libbpf-dev)

Install

dotnet add package KernelTrace

Stream network connection events

using KernelTrace.Events;
using KernelTrace.Probes;
using KernelTrace.Sessions;
using System.Runtime.InteropServices;

// Define the event struct — layout is auto-generated by the source generator
// from the struct definition in network_monitor.bpf.c.
[KernelEvent("sock_connect_event")]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public unsafe partial struct SocketConnectEvent
{
    public ulong  TimestampNs;
    public uint   Pid;
    public uint   Tgid;
    public uint   Uid;
    public uint   SrcIp;
    public uint   DstIp;
    public ushort SrcPort;
    public ushort DstPort;
    public fixed byte Comm[16];
    public byte   Family;
}

// Ctrl+C handling
using var cts = new CancellationTokenSource();
Console.CancelKeyPress += (_, e) => { e.Cancel = true; cts.Cancel(); };

// Create a session — both enter and exit tracepoints are required:
// the BPF program records the entry timestamp and emits the event on exit.
await using var session = await KernelTraceSession.CreateAsync(new SessionOptions
{
    ProbePath = "/usr/share/kerneltrace/probes/network_monitor.bpf.o",
    Probes    =
    [
        new TracepointSpec { Category = "syscalls", Name = "sys_enter_connect" },
        new TracepointSpec { Category = "syscalls", Name = "sys_exit_connect"  },
    ],
});

// Stream events — cancel on Ctrl+C
try
{
    await foreach (var ev in session.ReadAsync<SocketConnectEvent>(cts.Token))
    {
        Console.WriteLine($"PID={ev.Tgid}  dst={ev.DstIp}:{ev.DstPort}  comm={new string((sbyte*)ev.Comm)}");
    }
}
catch (OperationCanceledException) { }

Privileges

KernelTrace loads eBPF programs into the kernel, which always requires elevated capabilities regardless of how many or how few processes you trace.

Required Linux capabilities

Capability Why it is needed
CAP_BPF Load BPF programs, create/access BPF maps, mmap ring buffer
CAP_PERFMON Attach tracepoints, kprobes, and uprobes

CAP_SYS_ADMIN (root) is an alternative that implies both, but is far broader than needed.

Grant the two narrow capabilities to your binary once; afterwards it runs as a normal user:

sudo setcap cap_bpf,cap_perfmon+ep ./YourApp
# or, for the .NET host when running with dotnet-run:
sudo setcap cap_bpf,cap_perfmon+ep $(which dotnet)

This is the recommended production deployment model. The process retains no other elevated privileges — no filesystem root, no CAP_NET_ADMIN, no CAP_SYS_PTRACE.

Does CurrentProcessOnly remove the need for privileges?

No. SessionOptions.CurrentProcessOnly = true installs an eBPF filter that drops kernel events from all other processes before they reach the ring buffer. It reduces CPU overhead and ring-buffer pressure, but the eBPF program must still be loaded and attached, which requires CAP_BPF + CAP_PERFMON regardless of scope.

Operation Privilege required Affected by CurrentProcessOnly
Load BPF object (bpf_object__load) CAP_BPF No
Attach tracepoints / kprobes / uprobes CAP_PERFMON No
Write TGID filter into BPF map CAP_BPF N/A (is the filter itself)
Ring buffer mmap CAP_BPF No
Event emission per kernel event Yes (drops foreign PIDs in-kernel)

Packages

Package Description
KernelTrace Core library — session, ring buffer, source generator
KernelTrace.AspNetCore IHostedService + IServiceCollection extensions

Architecture Overview

Kernel                                   .NET Process
─────────────────────────────────────────────────────────────────
  BPF program (clang-compiled)
        │ bpf_ringbuf_submit
        ▼
  BPF_MAP_TYPE_RINGBUF (mmap'd)
        │ lock-free consumer
        ▼
  RingBufferReader ──► Channel<RingBufferRecord> ──► IAsyncEnumerable<T>
                   └──────────────────────────────► ProcessAsync<T> (zero-copy)

A dedicated polling thread (ThreadPriority.AboveNormal) polls the ring buffer via epoll_wait, drains records into a bounded channel, and updates metrics counters — all without touching the thread pool.

Full architecture documentation


Samples

Sample Probe file Description
samples/NetworkMonitor network_monitor.bpf.o Live outbound TCP/UDP connection table
samples/SchedulerProfiler scheduler_profiler.bpf.o Off-CPU profiler using sched_switch
samples/SecurityGuard security_guard.bpf.o execve auditing with suspicious-binary detection
samples/FileIoMonitor fs_io.bpf.o Per-syscall file I/O latency with hot-file ranking
samples/BlockIoAnalyzer block_io.bpf.o Per-device block I/O latency dashboard
samples/MemoryProfiler memory_profiler.bpf.o Kernel slab + page-allocator + page-fault tracking
samples/KernelInternals kernel_internals.bpf.o IRQ latency, lock contention, CPU P/C-state dashboard
samples/ContainerMonitor container_monitor.bpf.o Container-attributed events via cgroup v2
samples/DotNetRuntime dotnet_runtime.bpf.o .NET CLR uprobe tracing — GC, exceptions, JIT
samples/StackSampler stack_sampler.bpf.o Kernel + user-space stack traces with /proc/kallsyms symbolization
samples/UsdtPythonTracer usdt_python.bpf.o Python 3 USDT function__entry tracer
samples/CoreRelocations network_monitor.bpf.o CO-RE demo: custom BTF path, IsBtfAvailable(), debug output

Samples documentation


Documentation


Building the native library

Two scripts in native/scripts/ cover the full local build workflow:

Script Purpose
gen-vmlinux.sh Generates native/probes/vmlinux.h from the running kernel's BTF via bpftool
build-and-install.sh Builds libkerneltrace.so + all .bpf.o probes and copies them into runtimes/<RID>/native/
# 1. Generate vmlinux.h — required once per kernel version (file is gitignored)
bash native/scripts/gen-vmlinux.sh

# 2. Build libkerneltrace.so + *.bpf.o and install into runtimes/<RID>/native/
bash native/scripts/build-and-install.sh

build-and-install.sh auto-detects the host architecture and libc variant:

Architecture glibc musl (Alpine, Void Linux, …)
x86_64 linux-x64 linux-musl-x64
aarch64 linux-arm64 linux-musl-arm64
armv7l linux-arm linux-musl-arm

The output lands in runtimes/<RID>/native/ — exactly where the .NET SDK native-asset resolver and dotnet pack expect it, so no extra configuration is needed.

→ Full walkthrough in docs/getting-started.md — Building from source

Manual cmake build

For fine-grained control or to skip probe compilation:

cd native
cmake -B build -DCMAKE_BUILD_TYPE=Release   # KERNELTRACE_BUILD_PROBES=ON by default
cmake --build build -j$(nproc)              # builds libkerneltrace.so + all .bpf.o probes
sudo cmake --install build                   # installs into /usr/local

To skip eBPF probe compilation (no clang required):

cmake -B build -DCMAKE_BUILD_TYPE=Release -DKERNELTRACE_BUILD_PROBES=OFF
cmake --build build -j$(nproc)

The build system detects the host architecture (CMAKE_SYSTEM_PROCESSOR) and sets the appropriate BPF target-arch define and multiarch include path automatically. arm64, armv7, riscv64, s390x, and x86_64 are supported. On musl-based systems (Alpine) the multiarch subdirectory is absent and the build falls back to /usr/include seamlessly.


Testing

dotnet test tests/KernelTrace.Tests/            # core unit tests
dotnet test tests/KernelTrace.Generators.Tests/ # Roslyn generator tests

Tests run on any OS (Linux/macOS/Windows) using FakeNativeInterop and FakeRingBuffer — no kernel access required.


Benchmarks

cd benchmarks/KernelTrace.Benchmarks
dotnet run -c Release -- --filter '*'

Sample results on a 3.6 GHz Zen 3 core:

Benchmark Mean Alloc
TryReadRecord_Drain (64 × 64 B) ~1.2 µs 4.0 KB (pooled)
DrainInto_UnboundedChannel (64 × 64 B) ~2.1 µs 4.0 KB
Channel_IAsyncEnumerable_Drain (1 000 events) ~180 µs 48 KB

Requirements

  • Linux kernel ≥ 5.8 with CONFIG_DEBUG_INFO_BTF=y
  • .NET 8, 9, or 10 (multi-targeted)
  • libbpf ≥ 1.0
  • clang ≥ 14 (for compiling custom probes)
  • CAP_BPF + CAP_PERFMON at runtime (see Privileges)

License

MIT

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on KernelTrace:

Package Downloads
KernelTrace.AspNetCore

ASP.NET Core / Microsoft.Extensions.Hosting integration for KernelTrace. Provides IServiceCollection and IHostBuilder extensions for in-process kernel tracing.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 82 5/18/2026