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 <[email protected]>
---
 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


Reply via email to