diff options
author | Oleg Nesterov <oleg@redhat.com> | 2013-02-03 14:58:35 -0500 |
---|---|---|
committer | Oleg Nesterov <oleg@redhat.com> | 2013-02-08 12:28:06 -0500 |
commit | 736288ba5016e255869c26296014eeff649971c2 (patch) | |
tree | 235ab829ec63136e19d97a817489484f88ad3da8 /kernel/trace/trace_uprobe.c | |
parent | bdf8647c44766590ed02f9a84a450a796558b753 (diff) |
uprobes/perf: Teach trace_uprobe/perf code to track the active perf_event's
Introduce "struct trace_uprobe_filter" which records the "active"
perf_event's attached to ftrace_event_call. For the start we simply
use list_head, we can optimize this later if needed. For example, we
do not really need to record an event with ->parent != NULL, we can
rely on parent->child_list. And we can certainly do some optimizations
for the case when 2 events have the same ->tp_target or tp_target->mm.
Change trace_uprobe_register() to process TRACE_REG_PERF_OPEN/CLOSE
and add/del this perf_event to the list.
We can probably avoid any locking, but lets start with the "obvioulsy
correct" trace_uprobe_filter->rwlock which protects everything.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Diffstat (limited to 'kernel/trace/trace_uprobe.c')
-rw-r--r-- | kernel/trace/trace_uprobe.c | 55 |
1 files changed, 55 insertions, 0 deletions
diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c index c4e29e19fdd7..2a74a93afdae 100644 --- a/kernel/trace/trace_uprobe.c +++ b/kernel/trace/trace_uprobe.c | |||
@@ -28,6 +28,12 @@ | |||
28 | 28 | ||
29 | #define UPROBE_EVENT_SYSTEM "uprobes" | 29 | #define UPROBE_EVENT_SYSTEM "uprobes" |
30 | 30 | ||
31 | struct trace_uprobe_filter { | ||
32 | rwlock_t rwlock; | ||
33 | int nr_systemwide; | ||
34 | struct list_head perf_events; | ||
35 | }; | ||
36 | |||
31 | /* | 37 | /* |
32 | * uprobe event core functions | 38 | * uprobe event core functions |
33 | */ | 39 | */ |
@@ -35,6 +41,7 @@ struct trace_uprobe { | |||
35 | struct list_head list; | 41 | struct list_head list; |
36 | struct ftrace_event_class class; | 42 | struct ftrace_event_class class; |
37 | struct ftrace_event_call call; | 43 | struct ftrace_event_call call; |
44 | struct trace_uprobe_filter filter; | ||
38 | struct uprobe_consumer consumer; | 45 | struct uprobe_consumer consumer; |
39 | struct inode *inode; | 46 | struct inode *inode; |
40 | char *filename; | 47 | char *filename; |
@@ -58,6 +65,18 @@ static LIST_HEAD(uprobe_list); | |||
58 | 65 | ||
59 | static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs); | 66 | static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs); |
60 | 67 | ||
68 | static inline void init_trace_uprobe_filter(struct trace_uprobe_filter *filter) | ||
69 | { | ||
70 | rwlock_init(&filter->rwlock); | ||
71 | filter->nr_systemwide = 0; | ||
72 | INIT_LIST_HEAD(&filter->perf_events); | ||
73 | } | ||
74 | |||
75 | static inline bool uprobe_filter_is_empty(struct trace_uprobe_filter *filter) | ||
76 | { | ||
77 | return !filter->nr_systemwide && list_empty(&filter->perf_events); | ||
78 | } | ||
79 | |||
61 | /* | 80 | /* |
62 | * Allocate new trace_uprobe and initialize it (including uprobes). | 81 | * Allocate new trace_uprobe and initialize it (including uprobes). |
63 | */ | 82 | */ |
@@ -87,6 +106,7 @@ alloc_trace_uprobe(const char *group, const char *event, int nargs) | |||
87 | 106 | ||
88 | INIT_LIST_HEAD(&tu->list); | 107 | INIT_LIST_HEAD(&tu->list); |
89 | tu->consumer.handler = uprobe_dispatcher; | 108 | tu->consumer.handler = uprobe_dispatcher; |
109 | init_trace_uprobe_filter(&tu->filter); | ||
90 | return tu; | 110 | return tu; |
91 | 111 | ||
92 | error: | 112 | error: |
@@ -544,6 +564,8 @@ static int probe_event_enable(struct trace_uprobe *tu, int flag) | |||
544 | if (is_trace_uprobe_enabled(tu)) | 564 | if (is_trace_uprobe_enabled(tu)) |
545 | return -EINTR; | 565 | return -EINTR; |
546 | 566 | ||
567 | WARN_ON(!uprobe_filter_is_empty(&tu->filter)); | ||
568 | |||
547 | tu->flags |= flag; | 569 | tu->flags |= flag; |
548 | ret = uprobe_register(tu->inode, tu->offset, &tu->consumer); | 570 | ret = uprobe_register(tu->inode, tu->offset, &tu->consumer); |
549 | if (ret) | 571 | if (ret) |
@@ -557,6 +579,8 @@ static void probe_event_disable(struct trace_uprobe *tu, int flag) | |||
557 | if (!is_trace_uprobe_enabled(tu)) | 579 | if (!is_trace_uprobe_enabled(tu)) |
558 | return; | 580 | return; |
559 | 581 | ||
582 | WARN_ON(!uprobe_filter_is_empty(&tu->filter)); | ||
583 | |||
560 | uprobe_unregister(tu->inode, tu->offset, &tu->consumer); | 584 | uprobe_unregister(tu->inode, tu->offset, &tu->consumer); |
561 | tu->flags &= ~flag; | 585 | tu->flags &= ~flag; |
562 | } | 586 | } |
@@ -632,6 +656,30 @@ static int set_print_fmt(struct trace_uprobe *tu) | |||
632 | } | 656 | } |
633 | 657 | ||
634 | #ifdef CONFIG_PERF_EVENTS | 658 | #ifdef CONFIG_PERF_EVENTS |
659 | static int uprobe_perf_open(struct trace_uprobe *tu, struct perf_event *event) | ||
660 | { | ||
661 | write_lock(&tu->filter.rwlock); | ||
662 | if (event->hw.tp_target) | ||
663 | list_add(&event->hw.tp_list, &tu->filter.perf_events); | ||
664 | else | ||
665 | tu->filter.nr_systemwide++; | ||
666 | write_unlock(&tu->filter.rwlock); | ||
667 | |||
668 | return 0; | ||
669 | } | ||
670 | |||
671 | static int uprobe_perf_close(struct trace_uprobe *tu, struct perf_event *event) | ||
672 | { | ||
673 | write_lock(&tu->filter.rwlock); | ||
674 | if (event->hw.tp_target) | ||
675 | list_del(&event->hw.tp_list); | ||
676 | else | ||
677 | tu->filter.nr_systemwide--; | ||
678 | write_unlock(&tu->filter.rwlock); | ||
679 | |||
680 | return 0; | ||
681 | } | ||
682 | |||
635 | /* uprobe profile handler */ | 683 | /* uprobe profile handler */ |
636 | static void uprobe_perf_func(struct trace_uprobe *tu, struct pt_regs *regs) | 684 | static void uprobe_perf_func(struct trace_uprobe *tu, struct pt_regs *regs) |
637 | { | 685 | { |
@@ -687,6 +735,13 @@ int trace_uprobe_register(struct ftrace_event_call *event, enum trace_reg type, | |||
687 | case TRACE_REG_PERF_UNREGISTER: | 735 | case TRACE_REG_PERF_UNREGISTER: |
688 | probe_event_disable(tu, TP_FLAG_PROFILE); | 736 | probe_event_disable(tu, TP_FLAG_PROFILE); |
689 | return 0; | 737 | return 0; |
738 | |||
739 | case TRACE_REG_PERF_OPEN: | ||
740 | return uprobe_perf_open(tu, data); | ||
741 | |||
742 | case TRACE_REG_PERF_CLOSE: | ||
743 | return uprobe_perf_close(tu, data); | ||
744 | |||
690 | #endif | 745 | #endif |
691 | default: | 746 | default: |
692 | return 0; | 747 | return 0; |