[PATCH v5 2/4] tracing: add aggregate buffer memory usage statistics
From: Xiang Gao
Date: Thu Nov 05 2026 - 09:20:40 EST
Add a trace_stats directory with memory_usage_kb reporting the page
memory consumed by the tracing ring buffers, categorized by buffer type
and memory source:
global:
instances:
snapshots:
persistents:
remotes:
total_system:
total_carveout:
The global, instances, and snapshots values come from the page
allocator. Persistent buffers use reserved memory and are reported under
persistents, while the backup instance uses vmalloc and is skipped since
it is already accounted in VmallocUsed. Remote buffers are not yet
enumerated and report zero.
total_system is the page-allocator memory that contributes to lost RAM,
while total_carveout is reserved memory that was never in the free pool.
Signed-off-by: Xiang Gao <gaoxiang17@xxxxxxxxxx>
---
kernel/trace/trace.c | 115 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 115 insertions(+)
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index e8c4efa8107b..6036e368fe10 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -5797,6 +5797,107 @@ tracing_total_capacity_read(struct file *filp, char __user *ubuf, size_t cnt, lo
return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
}
+struct trace_mem_stats {
+ unsigned long global;
+ unsigned long instances;
+ unsigned long snapshots;
+ unsigned long persistents;
+ unsigned long remotes;
+ unsigned long total_system;
+ unsigned long total_carveout;
+};
+
+static void
+trace_array_buffer_memory(struct trace_array *tr, int cpu,
+ struct trace_mem_stats *stats)
+{
+ if (tr->range_addr_start) {
+ /*
+ * Persistent buffers use reserved memory (carveout). The
+ * backup instance uses vmalloc and is already accounted in
+ * VmallocUsed, so skip it.
+ */
+ if (!(tr->flags & TRACE_ARRAY_FL_VMALLOC))
+ stats->persistents +=
+ ring_buffer_memory_size(tr->array_buffer.buffer, cpu);
+ } else if (tr->flags & TRACE_ARRAY_FL_GLOBAL) {
+ stats->global +=
+ ring_buffer_memory_size(tr->array_buffer.buffer, cpu);
+ } else {
+ stats->instances +=
+ ring_buffer_memory_size(tr->array_buffer.buffer, cpu);
+ }
+
+#ifdef CONFIG_TRACER_SNAPSHOT
+ if (tr->snapshot_buffer.buffer)
+ stats->snapshots +=
+ ring_buffer_memory_size(tr->snapshot_buffer.buffer, cpu);
+#endif
+}
+
+static struct trace_mem_stats trace_buffers_memory(void)
+{
+ struct trace_mem_stats stats = {};
+ struct trace_array *tr;
+ int cpu;
+
+ guard(mutex)(&trace_types_lock);
+
+ list_for_each_entry(tr, &ftrace_trace_arrays, list) {
+ for_each_tracing_cpu(cpu)
+ trace_array_buffer_memory(tr, cpu, &stats);
+ }
+
+ /*
+ * temp_buffer is allocated in tracer_alloc_buffers() and is never
+ * attached to a trace array. It temporarily holds event data for
+ * triggers when tracing is off. Account for its pages too.
+ */
+ if (temp_buffer) {
+ for_each_tracing_cpu(cpu)
+ stats.global += ring_buffer_memory_size(temp_buffer, cpu);
+ }
+
+ stats.total_system = stats.global + stats.instances +
+ stats.snapshots + stats.remotes;
+ stats.total_carveout = stats.persistents;
+
+ return stats;
+}
+
+static int trace_mem_show(struct seq_file *m, void *v)
+{
+ struct trace_mem_stats stats = trace_buffers_memory();
+
+ seq_printf(m, "global: %lu\n", stats.global >> 10);
+ seq_printf(m, "instances: %lu\n", stats.instances >> 10);
+ seq_printf(m, "snapshots: %lu\n", stats.snapshots >> 10);
+ seq_printf(m, "persistents: %lu\n", stats.persistents >> 10);
+ seq_printf(m, "remotes: %lu\n", stats.remotes >> 10);
+ seq_printf(m, "total_system: %lu\n", stats.total_system >> 10);
+ seq_printf(m, "total_carveout: %lu\n", stats.total_carveout >> 10);
+
+ return 0;
+}
+
+static int trace_mem_open(struct inode *inode, struct file *file)
+{
+ int ret;
+
+ ret = tracing_check_open_get_tr(NULL);
+ if (ret)
+ return ret;
+
+ return single_open(file, trace_mem_show, inode->i_private);
+}
+
+static const struct file_operations trace_mem_fops = {
+ .open = trace_mem_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
#define LAST_BOOT_HEADER ((void *)1)
static void *l_next(struct seq_file *m, void *v, loff_t *pos)
@@ -9313,6 +9414,18 @@ static struct notifier_block trace_module_nb = {
};
#endif /* CONFIG_MODULES */
+static __init void init_trace_stats_tracefs(void)
+{
+ struct dentry *stats_dir;
+
+ stats_dir = tracefs_create_dir("trace_stats", NULL);
+ if (!stats_dir)
+ return;
+
+ trace_create_file("memory_usage_kb", TRACE_MODE_READ, stats_dir,
+ NULL, &trace_mem_fops);
+}
+
static __init void tracer_init_tracefs_work_func(struct work_struct *work)
{
/*
@@ -9327,6 +9440,8 @@ static __init void tracer_init_tracefs_work_func(struct work_struct *work)
init_tracer_tracefs(&global_trace, NULL);
ftrace_init_tracefs_toplevel(&global_trace, NULL);
+ init_trace_stats_tracefs();
+
trace_create_file("tracing_thresh", TRACE_MODE_WRITE, NULL,
&global_trace, &tracing_thresh_fops);
--
2.34.1