mirror of
https://github.com/AtlasLinux/linux.git
synced 2026-02-02 15:22:09 -08:00
fprobe: Rewrite fprobe on function-graph tracer
Rewrite fprobe implementation on function-graph tracer.
Major API changes are:
- 'nr_maxactive' field is deprecated.
- This depends on CONFIG_DYNAMIC_FTRACE_WITH_ARGS or
!CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS, and
CONFIG_HAVE_FUNCTION_GRAPH_FREGS. So currently works only
on x86_64.
- Currently the entry size is limited in 15 * sizeof(long).
- If there is too many fprobe exit handler set on the same
function, it will fail to probe.
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Acked-by: Heiko Carstens <hca@linux.ibm.com> # s390
Cc: Alexei Starovoitov <alexei.starovoitov@gmail.com>
Cc: Florent Revest <revest@chromium.org>
Cc: Martin KaFai Lau <martin.lau@linux.dev>
Cc: bpf <bpf@vger.kernel.org>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Alan Maguire <alan.maguire@oracle.com>
Cc: Heiko Carstens <hca@linux.ibm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Huacai Chen <chenhuacai@kernel.org>
Cc: WANG Xuerui <kernel@xen0n.name>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Nicholas Piggin <npiggin@gmail.com>
Cc: Christophe Leroy <christophe.leroy@csgroup.eu>
Cc: Naveen N Rao <naveen@kernel.org>
Cc: Madhavan Srinivasan <maddy@linux.ibm.com>
Cc: Paul Walmsley <paul.walmsley@sifive.com>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Albert Ou <aou@eecs.berkeley.edu>
Cc: Vasily Gorbik <gor@linux.ibm.com>
Cc: Alexander Gordeev <agordeev@linux.ibm.com>
Cc: Christian Borntraeger <borntraeger@linux.ibm.com>
Cc: Sven Schnelle <svens@linux.ibm.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: x86@kernel.org
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Link: https://lore.kernel.org/173519003970.391279.14406792285453830996.stgit@devnote2
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
This commit is contained in:
committed by
Steven Rostedt (Google)
parent
7495e179b4
commit
4346ba1604
@@ -135,6 +135,12 @@ ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs)
|
||||
return arch_ftrace_regs(fregs)->fp;
|
||||
}
|
||||
|
||||
static __always_inline unsigned long
|
||||
ftrace_regs_get_return_address(const struct ftrace_regs *fregs)
|
||||
{
|
||||
return arch_ftrace_regs(fregs)->lr;
|
||||
}
|
||||
|
||||
static __always_inline struct pt_regs *
|
||||
ftrace_partial_regs(const struct ftrace_regs *fregs, struct pt_regs *regs)
|
||||
{
|
||||
|
||||
@@ -61,6 +61,12 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs, unsigned long ip)
|
||||
#define ftrace_regs_get_frame_pointer(fregs) \
|
||||
(arch_ftrace_regs(fregs)->regs.regs[22])
|
||||
|
||||
static __always_inline unsigned long
|
||||
ftrace_regs_get_return_address(struct ftrace_regs *fregs)
|
||||
{
|
||||
return *(unsigned long *)(arch_ftrace_regs(fregs)->regs.regs[1]);
|
||||
}
|
||||
|
||||
#define ftrace_graph_func ftrace_graph_func
|
||||
void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
|
||||
struct ftrace_ops *op, struct ftrace_regs *fregs);
|
||||
|
||||
@@ -57,6 +57,12 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs,
|
||||
regs_set_return_ip(&arch_ftrace_regs(fregs)->regs, ip);
|
||||
}
|
||||
|
||||
static __always_inline unsigned long
|
||||
ftrace_regs_get_return_address(struct ftrace_regs *fregs)
|
||||
{
|
||||
return arch_ftrace_regs(fregs)->regs.link;
|
||||
}
|
||||
|
||||
struct ftrace_ops;
|
||||
|
||||
#define ftrace_graph_func ftrace_graph_func
|
||||
|
||||
@@ -186,6 +186,11 @@ static __always_inline unsigned long ftrace_regs_get_return_value(const struct f
|
||||
return arch_ftrace_regs(fregs)->a0;
|
||||
}
|
||||
|
||||
static __always_inline unsigned long ftrace_regs_get_return_address(const struct ftrace_regs *fregs)
|
||||
{
|
||||
return arch_ftrace_regs(fregs)->ra;
|
||||
}
|
||||
|
||||
static __always_inline void ftrace_regs_set_return_value(struct ftrace_regs *fregs,
|
||||
unsigned long ret)
|
||||
{
|
||||
|
||||
@@ -77,6 +77,12 @@ ftrace_regs_get_frame_pointer(struct ftrace_regs *fregs)
|
||||
return ftrace_regs_get_stack_pointer(fregs);
|
||||
}
|
||||
|
||||
static __always_inline unsigned long
|
||||
ftrace_regs_get_return_address(const struct ftrace_regs *fregs)
|
||||
{
|
||||
return arch_ftrace_regs(fregs)->regs.gprs[14];
|
||||
}
|
||||
|
||||
#define arch_ftrace_fill_perf_regs(fregs, _regs) do { \
|
||||
(_regs)->psw.mask = 0; \
|
||||
(_regs)->psw.addr = arch_ftrace_regs(fregs)->regs.psw.addr; \
|
||||
|
||||
@@ -58,6 +58,12 @@ arch_ftrace_get_regs(struct ftrace_regs *fregs)
|
||||
do { arch_ftrace_regs(fregs)->regs.ip = (_ip); } while (0)
|
||||
|
||||
|
||||
static __always_inline unsigned long
|
||||
ftrace_regs_get_return_address(struct ftrace_regs *fregs)
|
||||
{
|
||||
return *(unsigned long *)ftrace_regs_get_stack_pointer(fregs);
|
||||
}
|
||||
|
||||
struct ftrace_ops;
|
||||
#define ftrace_graph_func ftrace_graph_func
|
||||
void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/rethook.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/refcount.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct fprobe;
|
||||
|
||||
typedef int (*fprobe_entry_cb)(struct fprobe *fp, unsigned long entry_ip,
|
||||
unsigned long ret_ip, struct ftrace_regs *regs,
|
||||
void *entry_data);
|
||||
@@ -17,35 +18,57 @@ typedef void (*fprobe_exit_cb)(struct fprobe *fp, unsigned long entry_ip,
|
||||
unsigned long ret_ip, struct ftrace_regs *regs,
|
||||
void *entry_data);
|
||||
|
||||
/**
|
||||
* struct fprobe_hlist_node - address based hash list node for fprobe.
|
||||
*
|
||||
* @hlist: The hlist node for address search hash table.
|
||||
* @addr: One of the probing address of @fp.
|
||||
* @fp: The fprobe which owns this.
|
||||
*/
|
||||
struct fprobe_hlist_node {
|
||||
struct hlist_node hlist;
|
||||
unsigned long addr;
|
||||
struct fprobe *fp;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct fprobe_hlist - hash list nodes for fprobe.
|
||||
*
|
||||
* @hlist: The hlist node for existence checking hash table.
|
||||
* @rcu: rcu_head for RCU deferred release.
|
||||
* @fp: The fprobe which owns this fprobe_hlist.
|
||||
* @size: The size of @array.
|
||||
* @array: The fprobe_hlist_node for each address to probe.
|
||||
*/
|
||||
struct fprobe_hlist {
|
||||
struct hlist_node hlist;
|
||||
struct rcu_head rcu;
|
||||
struct fprobe *fp;
|
||||
int size;
|
||||
struct fprobe_hlist_node array[] __counted_by(size);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct fprobe - ftrace based probe.
|
||||
* @ops: The ftrace_ops.
|
||||
*
|
||||
* @nmissed: The counter for missing events.
|
||||
* @flags: The status flag.
|
||||
* @rethook: The rethook data structure. (internal data)
|
||||
* @entry_data_size: The private data storage size.
|
||||
* @nr_maxactive: The max number of active functions.
|
||||
* @nr_maxactive: The max number of active functions. (*deprecated)
|
||||
* @entry_handler: The callback function for function entry.
|
||||
* @exit_handler: The callback function for function exit.
|
||||
* @hlist_array: The fprobe_hlist for fprobe search from IP hash table.
|
||||
*/
|
||||
struct fprobe {
|
||||
#ifdef CONFIG_FUNCTION_TRACER
|
||||
/*
|
||||
* If CONFIG_FUNCTION_TRACER is not set, CONFIG_FPROBE is disabled too.
|
||||
* But user of fprobe may keep embedding the struct fprobe on their own
|
||||
* code. To avoid build error, this will keep the fprobe data structure
|
||||
* defined here, but remove ftrace_ops data structure.
|
||||
*/
|
||||
struct ftrace_ops ops;
|
||||
#endif
|
||||
unsigned long nmissed;
|
||||
unsigned int flags;
|
||||
struct rethook *rethook;
|
||||
size_t entry_data_size;
|
||||
int nr_maxactive;
|
||||
|
||||
fprobe_entry_cb entry_handler;
|
||||
fprobe_exit_cb exit_handler;
|
||||
|
||||
struct fprobe_hlist *hlist_array;
|
||||
};
|
||||
|
||||
/* This fprobe is soft-disabled. */
|
||||
@@ -121,4 +144,9 @@ static inline void enable_fprobe(struct fprobe *fp)
|
||||
fp->flags &= ~FPROBE_FL_DISABLED;
|
||||
}
|
||||
|
||||
/* The entry data size is 4 bits (=16) * sizeof(long) in maximum */
|
||||
#define FPROBE_DATA_SIZE_BITS 4
|
||||
#define MAX_FPROBE_DATA_SIZE_WORD ((1L << FPROBE_DATA_SIZE_BITS) - 1)
|
||||
#define MAX_FPROBE_DATA_SIZE (MAX_FPROBE_DATA_SIZE_WORD * sizeof(long))
|
||||
|
||||
#endif
|
||||
|
||||
@@ -307,11 +307,9 @@ config DYNAMIC_FTRACE_WITH_ARGS
|
||||
|
||||
config FPROBE
|
||||
bool "Kernel Function Probe (fprobe)"
|
||||
depends on FUNCTION_TRACER
|
||||
depends on DYNAMIC_FTRACE_WITH_REGS || DYNAMIC_FTRACE_WITH_ARGS
|
||||
depends on HAVE_FTRACE_REGS_HAVING_PT_REGS || !HAVE_DYNAMIC_FTRACE_WITH_ARGS
|
||||
depends on HAVE_RETHOOK
|
||||
select RETHOOK
|
||||
depends on HAVE_FUNCTION_GRAPH_FREGS && HAVE_FTRACE_GRAPH_FUNC
|
||||
depends on DYNAMIC_FTRACE_WITH_ARGS
|
||||
select FUNCTION_GRAPH_TRACER
|
||||
default n
|
||||
help
|
||||
This option enables kernel function probe (fprobe) based on ftrace.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,10 +17,8 @@ static u32 rand1, entry_val, exit_val;
|
||||
/* Use indirect calls to avoid inlining the target functions */
|
||||
static u32 (*target)(u32 value);
|
||||
static u32 (*target2)(u32 value);
|
||||
static u32 (*target_nest)(u32 value, u32 (*nest)(u32));
|
||||
static unsigned long target_ip;
|
||||
static unsigned long target2_ip;
|
||||
static unsigned long target_nest_ip;
|
||||
static int entry_return_value;
|
||||
|
||||
static noinline u32 fprobe_selftest_target(u32 value)
|
||||
@@ -33,11 +31,6 @@ static noinline u32 fprobe_selftest_target2(u32 value)
|
||||
return (value / div_factor) + 1;
|
||||
}
|
||||
|
||||
static noinline u32 fprobe_selftest_nest_target(u32 value, u32 (*nest)(u32))
|
||||
{
|
||||
return nest(value + 2);
|
||||
}
|
||||
|
||||
static notrace int fp_entry_handler(struct fprobe *fp, unsigned long ip,
|
||||
unsigned long ret_ip,
|
||||
struct ftrace_regs *fregs, void *data)
|
||||
@@ -79,22 +72,6 @@ static notrace void fp_exit_handler(struct fprobe *fp, unsigned long ip,
|
||||
KUNIT_EXPECT_NULL(current_test, data);
|
||||
}
|
||||
|
||||
static notrace int nest_entry_handler(struct fprobe *fp, unsigned long ip,
|
||||
unsigned long ret_ip,
|
||||
struct ftrace_regs *fregs, void *data)
|
||||
{
|
||||
KUNIT_EXPECT_FALSE(current_test, preemptible());
|
||||
return 0;
|
||||
}
|
||||
|
||||
static notrace void nest_exit_handler(struct fprobe *fp, unsigned long ip,
|
||||
unsigned long ret_ip,
|
||||
struct ftrace_regs *fregs, void *data)
|
||||
{
|
||||
KUNIT_EXPECT_FALSE(current_test, preemptible());
|
||||
KUNIT_EXPECT_EQ(current_test, ip, target_nest_ip);
|
||||
}
|
||||
|
||||
/* Test entry only (no rethook) */
|
||||
static void test_fprobe_entry(struct kunit *test)
|
||||
{
|
||||
@@ -191,25 +168,6 @@ static void test_fprobe_data(struct kunit *test)
|
||||
KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));
|
||||
}
|
||||
|
||||
/* Test nr_maxactive */
|
||||
static void test_fprobe_nest(struct kunit *test)
|
||||
{
|
||||
static const char *syms[] = {"fprobe_selftest_target", "fprobe_selftest_nest_target"};
|
||||
struct fprobe fp = {
|
||||
.entry_handler = nest_entry_handler,
|
||||
.exit_handler = nest_exit_handler,
|
||||
.nr_maxactive = 1,
|
||||
};
|
||||
|
||||
current_test = test;
|
||||
KUNIT_EXPECT_EQ(test, 0, register_fprobe_syms(&fp, syms, 2));
|
||||
|
||||
target_nest(rand1, target);
|
||||
KUNIT_EXPECT_EQ(test, 1, fp.nmissed);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));
|
||||
}
|
||||
|
||||
static void test_fprobe_skip(struct kunit *test)
|
||||
{
|
||||
struct fprobe fp = {
|
||||
@@ -247,10 +205,8 @@ static int fprobe_test_init(struct kunit *test)
|
||||
rand1 = get_random_u32_above(div_factor);
|
||||
target = fprobe_selftest_target;
|
||||
target2 = fprobe_selftest_target2;
|
||||
target_nest = fprobe_selftest_nest_target;
|
||||
target_ip = get_ftrace_location(target);
|
||||
target2_ip = get_ftrace_location(target2);
|
||||
target_nest_ip = get_ftrace_location(target_nest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -260,7 +216,6 @@ static struct kunit_case fprobe_testcases[] = {
|
||||
KUNIT_CASE(test_fprobe),
|
||||
KUNIT_CASE(test_fprobe_syms),
|
||||
KUNIT_CASE(test_fprobe_data),
|
||||
KUNIT_CASE(test_fprobe_nest),
|
||||
KUNIT_CASE(test_fprobe_skip),
|
||||
{}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user