You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
hw-breakpoints: ftrace plugin for kernel symbol tracing using HW Breakpoint interfaces
This patch adds an ftrace plugin to detect and profile memory access over kernel variables. It uses HW Breakpoint interfaces to 'watch memory addresses. Signed-off-by: K.Prasad <prasad@linux.vnet.ibm.com> Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
This commit is contained in:
committed by
Frederic Weisbecker
parent
432039933a
commit
0722db015c
@@ -314,6 +314,27 @@ config POWER_TRACER
|
||||
power management decisions, specifically the C-state and P-state
|
||||
behavior.
|
||||
|
||||
config KSYM_TRACER
|
||||
bool "Trace read and write access on kernel memory locations"
|
||||
depends on HAVE_HW_BREAKPOINT
|
||||
select TRACING
|
||||
help
|
||||
This tracer helps find read and write operations on any given kernel
|
||||
symbol i.e. /proc/kallsyms.
|
||||
|
||||
config PROFILE_KSYM_TRACER
|
||||
bool "Profile all kernel memory accesses on 'watched' variables"
|
||||
depends on KSYM_TRACER
|
||||
help
|
||||
This tracer profiles kernel accesses on variables watched through the
|
||||
ksym tracer ftrace plugin. Depending upon the hardware, all read
|
||||
and write operations on kernel variables can be monitored for
|
||||
accesses.
|
||||
|
||||
The results will be displayed in:
|
||||
/debugfs/tracing/profile_ksym
|
||||
|
||||
Say N if unsure.
|
||||
|
||||
config STACK_TRACER
|
||||
bool "Trace max stack"
|
||||
|
||||
@@ -51,5 +51,6 @@ obj-$(CONFIG_EVENT_TRACING) += trace_export.o
|
||||
obj-$(CONFIG_FTRACE_SYSCALLS) += trace_syscalls.o
|
||||
obj-$(CONFIG_EVENT_PROFILE) += trace_event_profile.o
|
||||
obj-$(CONFIG_EVENT_TRACING) += trace_events_filter.o
|
||||
obj-$(CONFIG_KSYM_TRACER) += trace_ksym.o
|
||||
|
||||
libftrace-y := ftrace.o
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
#include <linux/trace_seq.h>
|
||||
#include <linux/ftrace_event.h>
|
||||
|
||||
#ifdef CONFIG_KSYM_TRACER
|
||||
#include <asm/hw_breakpoint.h>
|
||||
#endif
|
||||
|
||||
enum trace_type {
|
||||
__TRACE_FIRST_TYPE = 0,
|
||||
|
||||
@@ -40,6 +44,7 @@ enum trace_type {
|
||||
TRACE_KMEM_FREE,
|
||||
TRACE_POWER,
|
||||
TRACE_BLK,
|
||||
TRACE_KSYM,
|
||||
|
||||
__TRACE_LAST_TYPE,
|
||||
};
|
||||
@@ -207,6 +212,21 @@ struct syscall_trace_exit {
|
||||
unsigned long ret;
|
||||
};
|
||||
|
||||
#define KSYM_SELFTEST_ENTRY "ksym_selftest_dummy"
|
||||
extern int process_new_ksym_entry(char *ksymname, int op, unsigned long addr);
|
||||
|
||||
struct trace_ksym {
|
||||
struct trace_entry ent;
|
||||
struct hw_breakpoint *ksym_hbp;
|
||||
unsigned long ksym_addr;
|
||||
unsigned long ip;
|
||||
#ifdef CONFIG_PROFILE_KSYM_TRACER
|
||||
unsigned long counter;
|
||||
#endif
|
||||
struct hlist_node ksym_hlist;
|
||||
char ksym_name[KSYM_NAME_LEN];
|
||||
char p_name[TASK_COMM_LEN];
|
||||
};
|
||||
|
||||
/*
|
||||
* trace_flag_type is an enumeration that holds different
|
||||
@@ -323,6 +343,7 @@ extern void __ftrace_bad_type(void);
|
||||
TRACE_SYSCALL_ENTER); \
|
||||
IF_ASSIGN(var, ent, struct syscall_trace_exit, \
|
||||
TRACE_SYSCALL_EXIT); \
|
||||
IF_ASSIGN(var, ent, struct trace_ksym, TRACE_KSYM); \
|
||||
__ftrace_bad_type(); \
|
||||
} while (0)
|
||||
|
||||
@@ -540,6 +561,8 @@ extern int trace_selftest_startup_branch(struct tracer *trace,
|
||||
struct trace_array *tr);
|
||||
extern int trace_selftest_startup_hw_branches(struct tracer *trace,
|
||||
struct trace_array *tr);
|
||||
extern int trace_selftest_startup_ksym(struct tracer *trace,
|
||||
struct trace_array *tr);
|
||||
#endif /* CONFIG_FTRACE_STARTUP_TEST */
|
||||
|
||||
extern void *head_page(struct trace_array_cpu *data);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,6 +17,7 @@ static inline int trace_valid_entry(struct trace_entry *entry)
|
||||
case TRACE_GRAPH_ENT:
|
||||
case TRACE_GRAPH_RET:
|
||||
case TRACE_HW_BRANCHES:
|
||||
case TRACE_KSYM:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@@ -807,3 +808,55 @@ trace_selftest_startup_hw_branches(struct tracer *trace,
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_HW_BRANCH_TRACER */
|
||||
|
||||
#ifdef CONFIG_KSYM_TRACER
|
||||
static int ksym_selftest_dummy;
|
||||
|
||||
int
|
||||
trace_selftest_startup_ksym(struct tracer *trace, struct trace_array *tr)
|
||||
{
|
||||
unsigned long count;
|
||||
int ret;
|
||||
|
||||
/* start the tracing */
|
||||
ret = tracer_init(trace, tr);
|
||||
if (ret) {
|
||||
warn_failed_init_tracer(trace, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ksym_selftest_dummy = 0;
|
||||
/* Register the read-write tracing request */
|
||||
ret = process_new_ksym_entry(KSYM_SELFTEST_ENTRY, HW_BREAKPOINT_RW,
|
||||
(unsigned long)(&ksym_selftest_dummy));
|
||||
|
||||
if (ret < 0) {
|
||||
printk(KERN_CONT "ksym_trace read-write startup test failed\n");
|
||||
goto ret_path;
|
||||
}
|
||||
/* Perform a read and a write operation over the dummy variable to
|
||||
* trigger the tracer
|
||||
*/
|
||||
if (ksym_selftest_dummy == 0)
|
||||
ksym_selftest_dummy++;
|
||||
|
||||
/* stop the tracing. */
|
||||
tracing_stop();
|
||||
/* check the trace buffer */
|
||||
ret = trace_test_buffer(tr, &count);
|
||||
trace->reset(tr);
|
||||
tracing_start();
|
||||
|
||||
/* read & write operations - one each is performed on the dummy variable
|
||||
* triggering two entries in the trace buffer
|
||||
*/
|
||||
if (!ret && count != 2) {
|
||||
printk(KERN_CONT "Ksym tracer startup test failed");
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
ret_path:
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_KSYM_TRACER */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user