KernelTrace 1.0.0
dotnet add package KernelTrace --version 1.0.0
NuGet\Install-Package KernelTrace -Version 1.0.0
<PackageReference Include="KernelTrace" Version="1.0.0" />
<PackageVersion Include="KernelTrace" Version="1.0.0" />
<PackageReference Include="KernelTrace" />
paket add KernelTrace --version 1.0.0
#r "nuget: KernelTrace, 1.0.0"
#:package KernelTrace@1.0.0
#addin nuget:?package=KernelTrace&version=1.0.0
#tool nuget:?package=KernelTrace&version=1.0.0
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.
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.
Recommended: setcap instead of sudo
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 |
Documentation
Building the native library
Using the helper scripts (recommended)
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_PERFMONat runtime (see Privileges)
License
| Product | Versions 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. |
-
net10.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Options (>= 10.0.8)
-
net8.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Options (>= 10.0.8)
-
net9.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Options (>= 10.0.8)
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 |