Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions crates/memtrack/src/ebpf/memtrack/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ impl MemtrackBpf {
/// would detect. Either attaches given host privileges; the token only
/// matters when `bpf()` is called from an unprivileged user namespace.
pub fn with_variant(variant: BpfVariant, track_rmap: bool) -> Result<Self> {
crate::kernel::KernelBtf::ensure_available()?;

let page_shift = page_shift()?;
let rmap = if track_rmap {
RmapSupport::detect()
Expand Down
51 changes: 45 additions & 6 deletions crates/memtrack/src/kernel.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
use crate::prelude::*;
use std::fmt;

/// The running kernel's full release, e.g. `6.12.8+`.
fn kernel_release() -> Result<String> {
const OSRELEASE_PATH: &str = "/proc/sys/kernel/osrelease";

std::fs::read_to_string(OSRELEASE_PATH)
.map(|release| release.trim().to_owned())
.with_context(|| format!("Failed to read {OSRELEASE_PATH}"))
}

/// A kernel release, ordered by `(major, minor)`. The patch level is ignored:
/// features are introduced in merge windows, never in a stable point release.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
Expand All @@ -16,12 +25,8 @@ impl KernelVersion {

/// The running kernel's release.
pub fn current() -> Result<Self> {
const PATH: &str = "/proc/sys/kernel/osrelease";

let release =
std::fs::read_to_string(PATH).with_context(|| format!("Failed to read {PATH}"))?;
Self::parse(&release)
.with_context(|| format!("Failed to parse kernel release {:?}", release.trim()))
let release = kernel_release()?;
Self::parse(&release).with_context(|| format!("Failed to parse kernel release {release:?}"))
}

/// Parse the leading `<major>.<minor>` of a release string, ignoring
Expand All @@ -46,6 +51,40 @@ impl fmt::Display for KernelVersion {
}
}

/// Whether the running kernel exposes its own BTF.
///
/// libbpf needs it to resolve CO-RE relocations, and the kernel resolves the
/// attach target of every `fentry`/`tp_btf` program against it, so a kernel
/// without BTF cannot load the programs at all. Detecting it up front replaces
/// libbpf's bare `-ESRCH` with something the reader can act on.
///
/// Minimal kernels built for fast boot — microVM images in particular — commonly
/// drop `CONFIG_DEBUG_INFO_BTF`, so the message has to name the option.
pub struct KernelBtf;

impl KernelBtf {
/// Present only on a kernel built with `CONFIG_DEBUG_INFO_BTF`.
const PATH: &'static str = "/sys/kernel/btf/vmlinux";

pub fn is_available() -> bool {
std::path::Path::new(Self::PATH).exists()
Comment on lines +69 to +70

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 BTF probe hides access errors

If the tracker runs where sysfs is absent, masked, or inaccessible, Path::exists returns false and the diagnostic states that the kernel was built without BTF. This sends users toward replacing or rebuilding the kernel instead of correcting sysfs visibility or access.

Knowledge Base Used:

Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/memtrack/src/kernel.rs
Line: 69-70

Comment:
**BTF probe hides access errors**

If the tracker runs where sysfs is absent, masked, or inaccessible, `Path::exists` returns false and the diagnostic states that the kernel was built without BTF. This sends users toward replacing or rebuilding the kernel instead of correcting sysfs visibility or access.

**Knowledge Base Used:**
- [Native profiling components](https://app.greptile.com/codspeed/-/custom-context/knowledge-base/codspeedhq/codspeed/-/docs/native-profiling-components.md)
- [eBPF memory tracker](https://app.greptile.com/codspeed/-/custom-context/knowledge-base/codspeedhq/codspeed/-/docs/ebpf-memory-tracker.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code Fix in Codex

}

pub fn ensure_available() -> Result<()> {
if Self::is_available() {
return Ok(());
}

let release = kernel_release().unwrap_or_else(|_| "unknown".to_owned());
bail!(
"Memory profiling is not supported on this runner: its kernel ({release}) \
was built without BTF, so {} does not exist. Use a runner whose kernel is \
built with CONFIG_DEBUG_INFO_BTF=y.",
Self::PATH
);
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
2 changes: 1 addition & 1 deletion crates/memtrack/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub use ipc::{
IpcCommand as MemtrackIpcCommand, IpcMessage as MemtrackIpcMessage,
IpcResponse as MemtrackIpcResponse, MemtrackIpcClient, MemtrackIpcServer,
};
pub use kernel::KernelVersion;
pub use kernel::{KernelBtf, KernelVersion};

#[cfg(feature = "ebpf")]
pub use ebpf::*;
Expand Down