Skip to content

Commit

Permalink
hw-breakpoints: Use struct perf_event_attr to define kernel breakpoints
Browse files Browse the repository at this point in the history
Kernel breakpoints are created using functions in which we pass
breakpoint parameters as individual variables: address, length
and type.

Although it fits well for x86, this just does not scale across
architectures that may support this api later as these may have
more or different needs. Pass in a perf_event_attr structure
instead because it is meant to evolve as much as possible into
a generic hardware breakpoint parameter structure.

Reported-by: K.Prasad <[email protected]>
Signed-off-by: Frederic Weisbecker <[email protected]>
LKML-Reference: <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
  • Loading branch information
fweisbec authored and Ingo Molnar committed Nov 27, 2009
1 parent 5fa10b2 commit dd1853c
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 78 deletions.
35 changes: 15 additions & 20 deletions include/linux/hw_breakpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ struct perf_event_attr name = { \
.pinned = 1, \
};

static inline void hw_breakpoint_init(struct perf_event_attr *attr)
{
attr->type = PERF_TYPE_BREAKPOINT;
attr->size = sizeof(*attr);
attr->pinned = 1;
}

static inline unsigned long hw_breakpoint_addr(struct perf_event *bp)
{
return bp->attr.bp_addr;
Expand Down Expand Up @@ -59,19 +66,13 @@ modify_user_hw_breakpoint(struct perf_event *bp,
* Kernel breakpoints are not associated with any particular thread.
*/
extern struct perf_event *
register_wide_hw_breakpoint_cpu(unsigned long addr,
int len,
int type,
register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr,
perf_callback_t triggered,
int cpu,
bool active);
int cpu);

extern struct perf_event **
register_wide_hw_breakpoint(unsigned long addr,
int len,
int type,
perf_callback_t triggered,
bool active);
register_wide_hw_breakpoint(struct perf_event_attr *attr,
perf_callback_t triggered);

extern int register_perf_hw_breakpoint(struct perf_event *bp);
extern int __register_perf_hw_breakpoint(struct perf_event *bp);
Expand Down Expand Up @@ -100,18 +101,12 @@ modify_user_hw_breakpoint(struct perf_event *bp,
perf_callback_t triggered,
struct task_struct *tsk) { return NULL; }
static inline struct perf_event *
register_wide_hw_breakpoint_cpu(unsigned long addr,
int len,
int type,
register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr,
perf_callback_t triggered,
int cpu,
bool active) { return NULL; }
int cpu) { return NULL; }
static inline struct perf_event **
register_wide_hw_breakpoint(unsigned long addr,
int len,
int type,
perf_callback_t triggered,
bool active) { return NULL; }
register_wide_hw_breakpoint(struct perf_event_attr *attr,
perf_callback_t triggered) { return NULL; }
static inline int
register_perf_hw_breakpoint(struct perf_event *bp) { return -ENOSYS; }
static inline int
Expand Down
35 changes: 4 additions & 31 deletions kernel/hw_breakpoint.c
Original file line number Diff line number Diff line change
Expand Up @@ -339,42 +339,16 @@ void unregister_hw_breakpoint(struct perf_event *bp)
}
EXPORT_SYMBOL_GPL(unregister_hw_breakpoint);

static struct perf_event *
register_kernel_hw_breakpoint_cpu(unsigned long addr,
int len,
int type,
perf_callback_t triggered,
int cpu,
bool active)
{
DEFINE_BREAKPOINT_ATTR(attr);

attr.bp_addr = addr;
attr.bp_len = len;
attr.bp_type = type;

if (!active)
attr.disabled = 1;

return perf_event_create_kernel_counter(&attr, cpu, -1, triggered);
}

/**
* register_wide_hw_breakpoint - register a wide breakpoint in the kernel
* @addr: is the memory address that triggers the breakpoint
* @len: the length of the access to the memory (1 byte, 2 bytes etc...)
* @type: the type of the access to the memory (read/write/exec)
* @attr: breakpoint attributes
* @triggered: callback to trigger when we hit the breakpoint
* @active: should we activate it while registering it
*
* @return a set of per_cpu pointers to perf events
*/
struct perf_event **
register_wide_hw_breakpoint(unsigned long addr,
int len,
int type,
perf_callback_t triggered,
bool active)
register_wide_hw_breakpoint(struct perf_event_attr *attr,
perf_callback_t triggered)
{
struct perf_event **cpu_events, **pevent, *bp;
long err;
Expand All @@ -386,8 +360,7 @@ register_wide_hw_breakpoint(unsigned long addr,

for_each_possible_cpu(cpu) {
pevent = per_cpu_ptr(cpu_events, cpu);
bp = register_kernel_hw_breakpoint_cpu(addr, len, type,
triggered, cpu, active);
bp = perf_event_create_kernel_counter(attr, cpu, -1, triggered);

*pevent = bp;

Expand Down
42 changes: 20 additions & 22 deletions kernel/trace/trace_ksym.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,7 @@

struct trace_ksym {
struct perf_event **ksym_hbp;
unsigned long ksym_addr;
int type;
int len;
struct perf_event_attr attr;
#ifdef CONFIG_PROFILE_KSYM_TRACER
unsigned long counter;
#endif
Expand All @@ -71,7 +69,7 @@ void ksym_collect_stats(unsigned long hbp_hit_addr)

rcu_read_lock();
hlist_for_each_entry_rcu(entry, node, &ksym_filter_head, ksym_hlist) {
if ((entry->ksym_addr == hbp_hit_addr) &&
if ((entry->attr.bp_addr == hbp_hit_addr) &&
(entry->counter <= MAX_UL_INT)) {
entry->counter++;
break;
Expand Down Expand Up @@ -192,14 +190,15 @@ int process_new_ksym_entry(char *ksymname, int op, unsigned long addr)
if (!entry)
return -ENOMEM;

entry->type = op;
entry->ksym_addr = addr;
entry->len = HW_BREAKPOINT_LEN_4;
hw_breakpoint_init(&entry->attr);

entry->attr.bp_type = op;
entry->attr.bp_addr = addr;
entry->attr.bp_len = HW_BREAKPOINT_LEN_4;

ret = -EAGAIN;
entry->ksym_hbp = register_wide_hw_breakpoint(entry->ksym_addr,
entry->len, entry->type,
ksym_hbp_handler, true);
entry->ksym_hbp = register_wide_hw_breakpoint(&entry->attr,
ksym_hbp_handler);

if (IS_ERR(entry->ksym_hbp)) {
ret = PTR_ERR(entry->ksym_hbp);
Expand Down Expand Up @@ -236,12 +235,12 @@ static ssize_t ksym_trace_filter_read(struct file *filp, char __user *ubuf,
mutex_lock(&ksym_tracer_mutex);

hlist_for_each_entry(entry, node, &ksym_filter_head, ksym_hlist) {
ret = trace_seq_printf(s, "%pS:", (void *)entry->ksym_addr);
if (entry->type == HW_BREAKPOINT_R)
ret = trace_seq_printf(s, "%pS:", (void *)entry->attr.bp_addr);
if (entry->attr.bp_type == HW_BREAKPOINT_R)
ret = trace_seq_puts(s, "r--\n");
else if (entry->type == HW_BREAKPOINT_W)
else if (entry->attr.bp_type == HW_BREAKPOINT_W)
ret = trace_seq_puts(s, "-w-\n");
else if (entry->type == (HW_BREAKPOINT_W | HW_BREAKPOINT_R))
else if (entry->attr.bp_type == (HW_BREAKPOINT_W | HW_BREAKPOINT_R))
ret = trace_seq_puts(s, "rw-\n");
WARN_ON_ONCE(!ret);
}
Expand Down Expand Up @@ -317,9 +316,9 @@ static ssize_t ksym_trace_filter_write(struct file *file,

ret = -EINVAL;
hlist_for_each_entry(entry, node, &ksym_filter_head, ksym_hlist) {
if (entry->ksym_addr == ksym_addr) {
if (entry->attr.bp_addr == ksym_addr) {
/* Check for malformed request: (6) */
if (entry->type != op)
if (entry->attr.bp_type != op)
changed = 1;
else
goto out;
Expand All @@ -328,13 +327,12 @@ static ssize_t ksym_trace_filter_write(struct file *file,
}
if (changed) {
unregister_wide_hw_breakpoint(entry->ksym_hbp);
entry->type = op;
entry->attr.bp_type = op;
ret = 0;
if (op > 0) {
entry->ksym_hbp =
register_wide_hw_breakpoint(entry->ksym_addr,
entry->len, entry->type,
ksym_hbp_handler, true);
register_wide_hw_breakpoint(&entry->attr,
ksym_hbp_handler);
if (IS_ERR(entry->ksym_hbp))
ret = PTR_ERR(entry->ksym_hbp);
else
Expand Down Expand Up @@ -489,7 +487,7 @@ static int ksym_tracer_stat_show(struct seq_file *m, void *v)

entry = hlist_entry(stat, struct trace_ksym, ksym_hlist);

access_type = entry->type;
access_type = entry->attr.bp_type;

switch (access_type) {
case HW_BREAKPOINT_R:
Expand All @@ -505,7 +503,7 @@ static int ksym_tracer_stat_show(struct seq_file *m, void *v)
seq_puts(m, " NA ");
}

if (lookup_symbol_name(entry->ksym_addr, fn_name) >= 0)
if (lookup_symbol_name(entry->attr.bp_addr, fn_name) >= 0)
seq_printf(m, " %-36s", fn_name);
else
seq_printf(m, " %-36s", "<NA>");
Expand Down
10 changes: 5 additions & 5 deletions samples/hw_breakpoint/data_breakpoint.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,13 @@ static void sample_hbp_handler(struct perf_event *temp, void *data)
static int __init hw_break_module_init(void)
{
int ret;
unsigned long addr;
DEFINE_BREAKPOINT_ATTR(attr);

addr = kallsyms_lookup_name(ksym_name);
attr.bp_addr = kallsyms_lookup_name(ksym_name);
attr.bp_len = HW_BREAKPOINT_LEN_4;
attr.bp_type = HW_BREAKPOINT_W | HW_BREAKPOINT_R;

sample_hbp = register_wide_hw_breakpoint(addr, HW_BREAKPOINT_LEN_4,
HW_BREAKPOINT_W | HW_BREAKPOINT_R,
sample_hbp_handler, true);
sample_hbp = register_wide_hw_breakpoint(&attr, sample_hbp_handler);
if (IS_ERR(sample_hbp)) {
ret = PTR_ERR(sample_hbp);
goto fail;
Expand Down

0 comments on commit dd1853c

Please sign in to comment.