[PATCH v5 1/5] tracing: add refcount_final_put tracepoint
From: Eugene Mavick
Date: Wed Aug 12 2026 - 23:52:03 EST
Add refcount_final_put tracepoint and related core infrastructure
refcount_final_put fires when a reference
count reaches zero and the object enters its final release path.
The tracepoint records three fields:
- caller: function that called the refcounting
function(refcount_sub_and_test, percpu_ref_put_many)
- ip: return address of trace wrapper macro call
- obj: refcount object(struct percpu_ref, refcount_t)
Signed-off-by: Eugene Mavick <m@xxxxxxxxxx>
---
include/linux/refcount_trace.h | 33 +++++++++++++++++++++++++
include/trace/events/refcount.h | 55 +++++++++++++++++++++++++++++++++++++++++
lib/Kconfig | 8 ++++++
lib/Makefile | 2 ++
lib/refcount_trace.c | 14 +++++++++++
5 files changed, 112 insertions(+)
diff --git a/include/linux/refcount_trace.h b/include/linux/refcount_trace.h
new file mode 100644
index 000000000000..6f8d0ba910f0
--- /dev/null
+++ b/include/linux/refcount_trace.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_REFCOUNT_TRACE_H
+#define _LINUX_REFCOUNT_TRACE_H
+
+#include <linux/tracepoint-defs.h>
+#include <linux/instruction_pointer.h>
+
+#ifdef CONFIG_REFCOUNT_TRACE_FINAL_PUT
+/* Declare the tracepoint so tracepoint_enabled() can be used */
+DECLARE_TRACEPOINT(refcount_final_put);
+
+/* Wrapper function implemented in lib/ref_trace.c */
+extern void do_refcount_trace_final_put(unsigned long caller, unsigned long ip, const void *obj);
+
+#define do_trace_refcount_final_put(obj) \
+ do { \
+ if (tracepoint_enabled(refcount_final_put)) \
+ do_refcount_trace_final_put(_RET_IP_, _THIS_IP_, obj); \
+ } while (0)
+
+#define do_trace_refcount_final_put_cond(cond, obj) \
+ do { \
+ if (tracepoint_enabled(refcount_final_put) && cond) \
+ do_refcount_trace_final_put(_RET_IP_, _THIS_IP_, obj); \
+ } while (0)
+
+#else /* !CONFIG_REFCOUNT_TRACE_FINAL_PUT */
+extern void do_refcount_trace_final_put(unsigned long caller, unsigned long ip, const void *obj);
+#define do_trace_refcount_final_put(obj) do { } while (0)
+#define do_trace_refcount_final_put_cond(cond, obj) do { } while (0)
+#endif
+
+#endif /* _LINUX_REFCOUNT_TRACE_H */
diff --git a/include/trace/events/refcount.h b/include/trace/events/refcount.h
new file mode 100644
index 000000000000..a4dc23aff93b
--- /dev/null
+++ b/include/trace/events/refcount.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM refcount
+
+#if !defined(_TRACE_REFCOUNT_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_REFCOUNT_H
+
+#include <linux/tracepoint.h>
+
+#ifdef CONFIG_REFCOUNT_TRACE_FINAL_PUT
+
+/**
+ * refcount_final_put - trace when a reference count reaches zero
+ * @caller: return address of refcount
+ * function(refcount_sub_and_test, percpu_ref_put_many)
+ * @ip: return address of trace wrapper macro call,
+ * inlining may cause it to be something else tho
+ * @obj: refcount object(struct percpu_ref, refcount_t)
+ *
+ * Tracepoint instrumentation can be added using the do_refcount_trace_final_put
+ * macro defined in include/linux/refcount_trace.h
+ * which uses _RET_IP_ and _THIS_IP_ for caller and ip arguments respectively,
+ * thus only requiring obj arg to be supplied
+ */
+TRACE_EVENT(refcount_final_put,
+
+ TP_PROTO(unsigned long caller, unsigned long ip, const void *obj),
+
+ TP_ARGS(caller, ip, obj),
+
+ TP_STRUCT__entry(
+ __field( unsigned long, caller )
+ __field( unsigned long, ip )
+ __field( const void *, obj )
+ ),
+
+ TP_fast_assign(
+ __entry->caller = caller;
+ __entry->ip = ip;
+ __entry->obj = obj;
+ ),
+
+ TP_printk("caller=%pS ip=%pS obj=%p",
+ (void *)__entry->caller,
+ (void *)__entry->ip,
+ __entry->obj
+ )
+);
+
+#endif /* CONFIG_REFCOUNT_TRACE_FINAL_PUT */
+
+#endif /* _TRACE_REFCOUNT_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/lib/Kconfig b/lib/Kconfig
index 00a9509636c1..12bc59515a3e 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -52,6 +52,14 @@ config PACKING_KUNIT_TEST
When in doubt, say N.
+config REFCOUNT_TRACE_FINAL_PUT
+ bool "Trace final puts on multiple refcount implementations"
+ depends on TRACEPOINTS && DEBUG_KERNEL
+ help
+ Trace when a refcount implementation considers refcounted object dead.
+
+ If unsure, say N.
+
config BITREVERSE
tristate
diff --git a/lib/Makefile b/lib/Makefile
index f33a24bf1c19..e75e88f0c870 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -335,3 +335,5 @@ CONTEXT_ANALYSIS_test_context-analysis.o := y
obj-$(CONFIG_CONTEXT_ANALYSIS_TEST) += test_context-analysis.o
subdir-$(CONFIG_FORTIFY_SOURCE) += test_fortify
+
+obj-$(CONFIG_REFCOUNT_TRACE_FINAL_PUT) += refcount_trace.o
diff --git a/lib/refcount_trace.c b/lib/refcount_trace.c
new file mode 100644
index 000000000000..2476bb5f2f60
--- /dev/null
+++ b/lib/refcount_trace.c
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0
+#define CREATE_TRACE_POINTS
+#include <trace/events/refcount.h>
+#include <linux/refcount_trace.h>
+
+//Wrapper function for functions defined entirely in header files
+void do_refcount_trace_final_put(unsigned long caller,
+ unsigned long ip,
+ const void *obj)
+{
+ trace_call__refcount_final_put(caller, ip, obj);
+}
+EXPORT_SYMBOL_GPL(do_refcount_trace_final_put);
+EXPORT_TRACEPOINT_SYMBOL_GPL(refcount_final_put);
--
2.51.2