From dca25a91af45f37c6f5edcf13e9807b91fc888c9 Mon Sep 17 00:00:00 2001 From: Mauro Ezequiel Moltrasio Date: Wed, 12 Aug 2026 16:19:33 +0200 Subject: [PATCH] refactor(metrics): improve d_instantiate dispatch metric tracking Add new metrics to the d_instantiate hook that allow for tracking individual event types dispatched. This is helpful to identify if one of these types are being lost by comparing the `Added` label from the originating hook (like path_mkdir or path_symlink) with the corresponding `Added` label. In order for `d_instantiate` to have its own type some trait + iterator gymnastics were necessary. The summary for this is: * A new `KernelMetric` trait is created which describes how metrics coming from the kernel should be accumulated and encoded using a `KernelMetricLabel` helper type. * The trait is implemented for `metrics_by_hook_t` and `metrics_d_instantiate_t`. * The metrics module know how to translate from `KernelMetricLabel` to `LabelValues`, so it does the same `accumulate` process it used to do and then uses the iterator produced by `encode` to add metrics dynamically. --- fact-ebpf/src/bpf/main.c | 6 +++- fact-ebpf/src/bpf/types.h | 8 ++++- fact-ebpf/src/lib.rs | 49 +++++++++++++++++++++++++++++- fact/src/config/mod.rs | 4 +++ fact/src/metrics/kernel_metrics.rs | 16 +++------- fact/src/metrics/mod.rs | 20 ++++++++++++ 6 files changed, 89 insertions(+), 14 deletions(-) diff --git a/fact-ebpf/src/bpf/main.c b/fact-ebpf/src/bpf/main.c index ede0cd4e..94471cdd 100644 --- a/fact-ebpf/src/bpf/main.c +++ b/fact-ebpf/src/bpf/main.c @@ -336,7 +336,7 @@ int BPF_PROG(trace_d_instantiate, struct dentry* dentry, struct inode* inode) { if (m == NULL) { return 0; } - struct submit_event_args_t args = {.metrics = &m->d_instantiate}; + struct submit_event_args_t args = {.metrics = &m->d_instantiate.base}; args.metrics->total++; @@ -364,6 +364,7 @@ int BPF_PROG(trace_d_instantiate, struct dentry* dentry, struct inode* inode) { args.metrics->error++; } + m->d_instantiate.added_mkdir++; submit_mkdir_event(&args); break; case FILE_ACTIVITY_SYMLINK: @@ -375,7 +376,10 @@ int BPF_PROG(trace_d_instantiate, struct dentry* dentry, struct inode* inode) { } if (args.monitored != NOT_MONITORED) { + m->d_instantiate.added_symlink++; submit_symlink_event(&args, d_inst_ctx->symlink_target); + } else { + args.metrics->ignored++; } break; default: diff --git a/fact-ebpf/src/bpf/types.h b/fact-ebpf/src/bpf/types.h index a2fa3449..4037861e 100644 --- a/fact-ebpf/src/bpf/types.h +++ b/fact-ebpf/src/bpf/types.h @@ -179,6 +179,12 @@ struct metrics_by_hook_t { unsigned long long ringbuffer_full; }; +struct metrics_d_instantiate_t { + struct metrics_by_hook_t base; + unsigned long long added_mkdir; + unsigned long long added_symlink; +}; + struct metrics_t { struct metrics_by_hook_t file_open; struct metrics_by_hook_t path_unlink; @@ -186,7 +192,7 @@ struct metrics_t { struct metrics_by_hook_t path_chown; struct metrics_by_hook_t path_rename; struct metrics_by_hook_t path_mkdir; - struct metrics_by_hook_t d_instantiate; + struct metrics_d_instantiate_t d_instantiate; struct metrics_by_hook_t path_rmdir; struct metrics_by_hook_t inode_setxattr; struct metrics_by_hook_t inode_removexattr; diff --git a/fact-ebpf/src/lib.rs b/fact-ebpf/src/lib.rs index 32f6f7b1..0d52fb2e 100644 --- a/fact-ebpf/src/lib.rs +++ b/fact-ebpf/src/lib.rs @@ -201,7 +201,25 @@ impl<'de> Deserialize<'de> for monitored_t { } } -impl metrics_by_hook_t { +pub enum KernelMetricLabel { + Total, + Added, + Dropped, + Ignored, + Error, + RingbufferFull, + + // d_instantiate specific metrics + AddedMkDir, + AddedSymlink, +} + +pub trait KernelMetric { + fn accumulate(self, other: &Self) -> Self; + fn encode(&self) -> impl Iterator; +} + +impl KernelMetric for metrics_by_hook_t { fn accumulate(mut self, other: &metrics_by_hook_t) -> metrics_by_hook_t { self.total += other.total; self.added += other.added; @@ -210,6 +228,35 @@ impl metrics_by_hook_t { self.ringbuffer_full += other.ringbuffer_full; self } + + fn encode(&self) -> impl Iterator { + use KernelMetricLabel::*; + [ + (Total, self.total), + (Added, self.added), + (Error, self.error), + (Ignored, self.ignored), + (RingbufferFull, self.ringbuffer_full), + ] + .into_iter() + } +} + +impl KernelMetric for metrics_d_instantiate_t { + fn accumulate(mut self, other: &metrics_d_instantiate_t) -> metrics_d_instantiate_t { + self.base = self.base.accumulate(&other.base); + self.added_mkdir += other.added_mkdir; + self.added_symlink += other.added_symlink; + self + } + + fn encode(&self) -> impl Iterator { + use KernelMetricLabel::*; + self.base.encode().chain([ + (AddedSymlink, self.added_symlink), + (AddedMkDir, self.added_mkdir), + ]) + } } macro_rules! impl_metrics_t { diff --git a/fact/src/config/mod.rs b/fact/src/config/mod.rs index 567da9d1..1542f86a 100644 --- a/fact/src/config/mod.rs +++ b/fact/src/config/mod.rs @@ -869,6 +869,10 @@ pub struct FactCli { /// This is an advanced configuration parameter, it can be used to /// increase the amount of in-flight events that use the /// d_instantiate LSM hook for resolving inode numbers. + /// + /// Whether this value needs to be tweaked can be determined with + /// the metrics exposed for the d_instantiate hook and the ones for + /// the originating hooks. #[arg( long = "d-inst-size", env = "FACT_D_INSTANTIATE_CTX_SIZE", diff --git a/fact/src/metrics/kernel_metrics.rs b/fact/src/metrics/kernel_metrics.rs index 10492470..59ad1d38 100644 --- a/fact/src/metrics/kernel_metrics.rs +++ b/fact/src/metrics/kernel_metrics.rs @@ -1,11 +1,11 @@ use aya::maps::{MapData, PerCpuArray}; use prometheus_client::registry::Registry; -use fact_ebpf::{metrics_by_hook_t, metrics_t}; +use fact_ebpf::{KernelMetric, metrics_t}; use crate::metrics::MetricEvents; -use super::{EventCounter, LabelValues}; +use super::EventCounter; macro_rules! define_kernel_metrics { ($($hook:ident),+ $(,)?) => { @@ -46,17 +46,11 @@ macro_rules! define_kernel_metrics { Ok(()) } - fn refresh_labels(ec: &EventCounter, m: &metrics_by_hook_t) { + fn refresh_labels(ec: &EventCounter, m: &impl KernelMetric) { ec.counter.clear(); - for (label, value) in [ - (LabelValues::Total, m.total), - (LabelValues::Added, m.added), - (LabelValues::Error, m.error), - (LabelValues::Ignored, m.ignored), - (LabelValues::RingbufferFull, m.ringbuffer_full), - ] { + for (label, value) in m.encode() { ec.counter - .get_or_create(&MetricEvents { label }) + .get_or_create(&MetricEvents{ label: label.into() }) .inc_by(value); } } diff --git a/fact/src/metrics/mod.rs b/fact/src/metrics/mod.rs index 14308b08..faae0ba8 100644 --- a/fact/src/metrics/mod.rs +++ b/fact/src/metrics/mod.rs @@ -6,6 +6,7 @@ use prometheus_client::{ use host_scanner::HostScannerMetrics; +use fact_ebpf::KernelMetricLabel; pub mod exporter; pub mod host_scanner; pub mod kernel_metrics; @@ -18,6 +19,25 @@ enum LabelValues { Ignored, Error, RingbufferFull, + + // d_instantiate specific metrics + AddedMkDir, + AddedSymlink, +} + +impl From for LabelValues { + fn from(value: KernelMetricLabel) -> Self { + match value { + KernelMetricLabel::Total => LabelValues::Total, + KernelMetricLabel::Added => LabelValues::Added, + KernelMetricLabel::Dropped => LabelValues::Dropped, + KernelMetricLabel::Ignored => LabelValues::Ignored, + KernelMetricLabel::Error => LabelValues::Error, + KernelMetricLabel::RingbufferFull => LabelValues::RingbufferFull, + KernelMetricLabel::AddedMkDir => LabelValues::AddedMkDir, + KernelMetricLabel::AddedSymlink => LabelValues::AddedSymlink, + } + } } #[derive(Clone, Hash, Eq, Debug, PartialEq, EncodeLabelSet)]