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
