diff options
Diffstat (limited to 'kernel/trace/trace_uprobe.c')
-rw-r--r-- | kernel/trace/trace_uprobe.c | 217 |
1 files changed, 168 insertions, 49 deletions
diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c index c86e6d4f67fb..8dad2a92dee9 100644 --- a/kernel/trace/trace_uprobe.c +++ b/kernel/trace/trace_uprobe.c | |||
@@ -28,20 +28,21 @@ | |||
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 | */ |
34 | struct trace_uprobe; | ||
35 | struct uprobe_trace_consumer { | ||
36 | struct uprobe_consumer cons; | ||
37 | struct trace_uprobe *tu; | ||
38 | }; | ||
39 | |||
40 | struct trace_uprobe { | 40 | struct trace_uprobe { |
41 | struct list_head list; | 41 | struct list_head list; |
42 | struct ftrace_event_class class; | 42 | struct ftrace_event_class class; |
43 | struct ftrace_event_call call; | 43 | struct ftrace_event_call call; |
44 | struct uprobe_trace_consumer *consumer; | 44 | struct trace_uprobe_filter filter; |
45 | struct uprobe_consumer consumer; | ||
45 | struct inode *inode; | 46 | struct inode *inode; |
46 | char *filename; | 47 | char *filename; |
47 | unsigned long offset; | 48 | unsigned long offset; |
@@ -64,6 +65,18 @@ static LIST_HEAD(uprobe_list); | |||
64 | 65 | ||
65 | 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); |
66 | 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 | |||
67 | /* | 80 | /* |
68 | * Allocate new trace_uprobe and initialize it (including uprobes). | 81 | * Allocate new trace_uprobe and initialize it (including uprobes). |
69 | */ | 82 | */ |
@@ -92,6 +105,8 @@ alloc_trace_uprobe(const char *group, const char *event, int nargs) | |||
92 | goto error; | 105 | goto error; |
93 | 106 | ||
94 | INIT_LIST_HEAD(&tu->list); | 107 | INIT_LIST_HEAD(&tu->list); |
108 | tu->consumer.handler = uprobe_dispatcher; | ||
109 | init_trace_uprobe_filter(&tu->filter); | ||
95 | return tu; | 110 | return tu; |
96 | 111 | ||
97 | error: | 112 | error: |
@@ -253,12 +268,18 @@ static int create_trace_uprobe(int argc, char **argv) | |||
253 | if (ret) | 268 | if (ret) |
254 | goto fail_address_parse; | 269 | goto fail_address_parse; |
255 | 270 | ||
271 | inode = igrab(path.dentry->d_inode); | ||
272 | path_put(&path); | ||
273 | |||
274 | if (!inode || !S_ISREG(inode->i_mode)) { | ||
275 | ret = -EINVAL; | ||
276 | goto fail_address_parse; | ||
277 | } | ||
278 | |||
256 | ret = kstrtoul(arg, 0, &offset); | 279 | ret = kstrtoul(arg, 0, &offset); |
257 | if (ret) | 280 | if (ret) |
258 | goto fail_address_parse; | 281 | goto fail_address_parse; |
259 | 282 | ||
260 | inode = igrab(path.dentry->d_inode); | ||
261 | |||
262 | argc -= 2; | 283 | argc -= 2; |
263 | argv += 2; | 284 | argv += 2; |
264 | 285 | ||
@@ -356,7 +377,7 @@ fail_address_parse: | |||
356 | if (inode) | 377 | if (inode) |
357 | iput(inode); | 378 | iput(inode); |
358 | 379 | ||
359 | pr_info("Failed to parse address.\n"); | 380 | pr_info("Failed to parse address or file.\n"); |
360 | 381 | ||
361 | return ret; | 382 | return ret; |
362 | } | 383 | } |
@@ -465,7 +486,7 @@ static const struct file_operations uprobe_profile_ops = { | |||
465 | }; | 486 | }; |
466 | 487 | ||
467 | /* uprobe handler */ | 488 | /* uprobe handler */ |
468 | static void uprobe_trace_func(struct trace_uprobe *tu, struct pt_regs *regs) | 489 | static int uprobe_trace_func(struct trace_uprobe *tu, struct pt_regs *regs) |
469 | { | 490 | { |
470 | struct uprobe_trace_entry_head *entry; | 491 | struct uprobe_trace_entry_head *entry; |
471 | struct ring_buffer_event *event; | 492 | struct ring_buffer_event *event; |
@@ -475,8 +496,6 @@ static void uprobe_trace_func(struct trace_uprobe *tu, struct pt_regs *regs) | |||
475 | unsigned long irq_flags; | 496 | unsigned long irq_flags; |
476 | struct ftrace_event_call *call = &tu->call; | 497 | struct ftrace_event_call *call = &tu->call; |
477 | 498 | ||
478 | tu->nhit++; | ||
479 | |||
480 | local_save_flags(irq_flags); | 499 | local_save_flags(irq_flags); |
481 | pc = preempt_count(); | 500 | pc = preempt_count(); |
482 | 501 | ||
@@ -485,16 +504,18 @@ static void uprobe_trace_func(struct trace_uprobe *tu, struct pt_regs *regs) | |||
485 | event = trace_current_buffer_lock_reserve(&buffer, call->event.type, | 504 | event = trace_current_buffer_lock_reserve(&buffer, call->event.type, |
486 | size, irq_flags, pc); | 505 | size, irq_flags, pc); |
487 | if (!event) | 506 | if (!event) |
488 | return; | 507 | return 0; |
489 | 508 | ||
490 | entry = ring_buffer_event_data(event); | 509 | entry = ring_buffer_event_data(event); |
491 | entry->ip = uprobe_get_swbp_addr(task_pt_regs(current)); | 510 | entry->ip = instruction_pointer(task_pt_regs(current)); |
492 | data = (u8 *)&entry[1]; | 511 | data = (u8 *)&entry[1]; |
493 | for (i = 0; i < tu->nr_args; i++) | 512 | for (i = 0; i < tu->nr_args; i++) |
494 | call_fetch(&tu->args[i].fetch, regs, data + tu->args[i].offset); | 513 | call_fetch(&tu->args[i].fetch, regs, data + tu->args[i].offset); |
495 | 514 | ||
496 | if (!filter_current_check_discard(buffer, call, entry, event)) | 515 | if (!filter_current_check_discard(buffer, call, entry, event)) |
497 | trace_buffer_unlock_commit(buffer, event, irq_flags, pc); | 516 | trace_buffer_unlock_commit(buffer, event, irq_flags, pc); |
517 | |||
518 | return 0; | ||
498 | } | 519 | } |
499 | 520 | ||
500 | /* Event entry printers */ | 521 | /* Event entry printers */ |
@@ -533,42 +554,43 @@ partial: | |||
533 | return TRACE_TYPE_PARTIAL_LINE; | 554 | return TRACE_TYPE_PARTIAL_LINE; |
534 | } | 555 | } |
535 | 556 | ||
536 | static int probe_event_enable(struct trace_uprobe *tu, int flag) | 557 | static inline bool is_trace_uprobe_enabled(struct trace_uprobe *tu) |
537 | { | 558 | { |
538 | struct uprobe_trace_consumer *utc; | 559 | return tu->flags & (TP_FLAG_TRACE | TP_FLAG_PROFILE); |
539 | int ret = 0; | 560 | } |
540 | 561 | ||
541 | if (!tu->inode || tu->consumer) | 562 | typedef bool (*filter_func_t)(struct uprobe_consumer *self, |
542 | return -EINTR; | 563 | enum uprobe_filter_ctx ctx, |
564 | struct mm_struct *mm); | ||
543 | 565 | ||
544 | utc = kzalloc(sizeof(struct uprobe_trace_consumer), GFP_KERNEL); | 566 | static int |
545 | if (!utc) | 567 | probe_event_enable(struct trace_uprobe *tu, int flag, filter_func_t filter) |
568 | { | ||
569 | int ret = 0; | ||
570 | |||
571 | if (is_trace_uprobe_enabled(tu)) | ||
546 | return -EINTR; | 572 | return -EINTR; |
547 | 573 | ||
548 | utc->cons.handler = uprobe_dispatcher; | 574 | WARN_ON(!uprobe_filter_is_empty(&tu->filter)); |
549 | utc->cons.filter = NULL; | ||
550 | ret = uprobe_register(tu->inode, tu->offset, &utc->cons); | ||
551 | if (ret) { | ||
552 | kfree(utc); | ||
553 | return ret; | ||
554 | } | ||
555 | 575 | ||
556 | tu->flags |= flag; | 576 | tu->flags |= flag; |
557 | utc->tu = tu; | 577 | tu->consumer.filter = filter; |
558 | tu->consumer = utc; | 578 | ret = uprobe_register(tu->inode, tu->offset, &tu->consumer); |
579 | if (ret) | ||
580 | tu->flags &= ~flag; | ||
559 | 581 | ||
560 | return 0; | 582 | return ret; |
561 | } | 583 | } |
562 | 584 | ||
563 | static void probe_event_disable(struct trace_uprobe *tu, int flag) | 585 | static void probe_event_disable(struct trace_uprobe *tu, int flag) |
564 | { | 586 | { |
565 | if (!tu->inode || !tu->consumer) | 587 | if (!is_trace_uprobe_enabled(tu)) |
566 | return; | 588 | return; |
567 | 589 | ||
568 | uprobe_unregister(tu->inode, tu->offset, &tu->consumer->cons); | 590 | WARN_ON(!uprobe_filter_is_empty(&tu->filter)); |
591 | |||
592 | uprobe_unregister(tu->inode, tu->offset, &tu->consumer); | ||
569 | tu->flags &= ~flag; | 593 | tu->flags &= ~flag; |
570 | kfree(tu->consumer); | ||
571 | tu->consumer = NULL; | ||
572 | } | 594 | } |
573 | 595 | ||
574 | static int uprobe_event_define_fields(struct ftrace_event_call *event_call) | 596 | static int uprobe_event_define_fields(struct ftrace_event_call *event_call) |
@@ -642,8 +664,96 @@ static int set_print_fmt(struct trace_uprobe *tu) | |||
642 | } | 664 | } |
643 | 665 | ||
644 | #ifdef CONFIG_PERF_EVENTS | 666 | #ifdef CONFIG_PERF_EVENTS |
667 | static bool | ||
668 | __uprobe_perf_filter(struct trace_uprobe_filter *filter, struct mm_struct *mm) | ||
669 | { | ||
670 | struct perf_event *event; | ||
671 | |||
672 | if (filter->nr_systemwide) | ||
673 | return true; | ||
674 | |||
675 | list_for_each_entry(event, &filter->perf_events, hw.tp_list) { | ||
676 | if (event->hw.tp_target->mm == mm) | ||
677 | return true; | ||
678 | } | ||
679 | |||
680 | return false; | ||
681 | } | ||
682 | |||
683 | static inline bool | ||
684 | uprobe_filter_event(struct trace_uprobe *tu, struct perf_event *event) | ||
685 | { | ||
686 | return __uprobe_perf_filter(&tu->filter, event->hw.tp_target->mm); | ||
687 | } | ||
688 | |||
689 | static int uprobe_perf_open(struct trace_uprobe *tu, struct perf_event *event) | ||
690 | { | ||
691 | bool done; | ||
692 | |||
693 | write_lock(&tu->filter.rwlock); | ||
694 | if (event->hw.tp_target) { | ||
695 | /* | ||
696 | * event->parent != NULL means copy_process(), we can avoid | ||
697 | * uprobe_apply(). current->mm must be probed and we can rely | ||
698 | * on dup_mmap() which preserves the already installed bp's. | ||
699 | * | ||
700 | * attr.enable_on_exec means that exec/mmap will install the | ||
701 | * breakpoints we need. | ||
702 | */ | ||
703 | done = tu->filter.nr_systemwide || | ||
704 | event->parent || event->attr.enable_on_exec || | ||
705 | uprobe_filter_event(tu, event); | ||
706 | list_add(&event->hw.tp_list, &tu->filter.perf_events); | ||
707 | } else { | ||
708 | done = tu->filter.nr_systemwide; | ||
709 | tu->filter.nr_systemwide++; | ||
710 | } | ||
711 | write_unlock(&tu->filter.rwlock); | ||
712 | |||
713 | if (!done) | ||
714 | uprobe_apply(tu->inode, tu->offset, &tu->consumer, true); | ||
715 | |||
716 | return 0; | ||
717 | } | ||
718 | |||
719 | static int uprobe_perf_close(struct trace_uprobe *tu, struct perf_event *event) | ||
720 | { | ||
721 | bool done; | ||
722 | |||
723 | write_lock(&tu->filter.rwlock); | ||
724 | if (event->hw.tp_target) { | ||
725 | list_del(&event->hw.tp_list); | ||
726 | done = tu->filter.nr_systemwide || | ||
727 | (event->hw.tp_target->flags & PF_EXITING) || | ||
728 | uprobe_filter_event(tu, event); | ||
729 | } else { | ||
730 | tu->filter.nr_systemwide--; | ||
731 | done = tu->filter.nr_systemwide; | ||
732 | } | ||
733 | write_unlock(&tu->filter.rwlock); | ||
734 | |||
735 | if (!done) | ||
736 | uprobe_apply(tu->inode, tu->offset, &tu->consumer, false); | ||
737 | |||
738 | return 0; | ||
739 | } | ||
740 | |||
741 | static bool uprobe_perf_filter(struct uprobe_consumer *uc, | ||
742 | enum uprobe_filter_ctx ctx, struct mm_struct *mm) | ||
743 | { | ||
744 | struct trace_uprobe *tu; | ||
745 | int ret; | ||
746 | |||
747 | tu = container_of(uc, struct trace_uprobe, consumer); | ||
748 | read_lock(&tu->filter.rwlock); | ||
749 | ret = __uprobe_perf_filter(&tu->filter, mm); | ||
750 | read_unlock(&tu->filter.rwlock); | ||
751 | |||
752 | return ret; | ||
753 | } | ||
754 | |||
645 | /* uprobe profile handler */ | 755 | /* uprobe profile handler */ |
646 | static void uprobe_perf_func(struct trace_uprobe *tu, struct pt_regs *regs) | 756 | static int uprobe_perf_func(struct trace_uprobe *tu, struct pt_regs *regs) |
647 | { | 757 | { |
648 | struct ftrace_event_call *call = &tu->call; | 758 | struct ftrace_event_call *call = &tu->call; |
649 | struct uprobe_trace_entry_head *entry; | 759 | struct uprobe_trace_entry_head *entry; |
@@ -652,11 +762,14 @@ static void uprobe_perf_func(struct trace_uprobe *tu, struct pt_regs *regs) | |||
652 | int size, __size, i; | 762 | int size, __size, i; |
653 | int rctx; | 763 | int rctx; |
654 | 764 | ||
765 | if (!uprobe_perf_filter(&tu->consumer, 0, current->mm)) | ||
766 | return UPROBE_HANDLER_REMOVE; | ||
767 | |||
655 | __size = sizeof(*entry) + tu->size; | 768 | __size = sizeof(*entry) + tu->size; |
656 | size = ALIGN(__size + sizeof(u32), sizeof(u64)); | 769 | size = ALIGN(__size + sizeof(u32), sizeof(u64)); |
657 | size -= sizeof(u32); | 770 | size -= sizeof(u32); |
658 | if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE, "profile buffer not large enough")) | 771 | if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE, "profile buffer not large enough")) |
659 | return; | 772 | return 0; |
660 | 773 | ||
661 | preempt_disable(); | 774 | preempt_disable(); |
662 | 775 | ||
@@ -664,7 +777,7 @@ static void uprobe_perf_func(struct trace_uprobe *tu, struct pt_regs *regs) | |||
664 | if (!entry) | 777 | if (!entry) |
665 | goto out; | 778 | goto out; |
666 | 779 | ||
667 | entry->ip = uprobe_get_swbp_addr(task_pt_regs(current)); | 780 | entry->ip = instruction_pointer(task_pt_regs(current)); |
668 | data = (u8 *)&entry[1]; | 781 | data = (u8 *)&entry[1]; |
669 | for (i = 0; i < tu->nr_args; i++) | 782 | for (i = 0; i < tu->nr_args; i++) |
670 | call_fetch(&tu->args[i].fetch, regs, data + tu->args[i].offset); | 783 | call_fetch(&tu->args[i].fetch, regs, data + tu->args[i].offset); |
@@ -674,6 +787,7 @@ static void uprobe_perf_func(struct trace_uprobe *tu, struct pt_regs *regs) | |||
674 | 787 | ||
675 | out: | 788 | out: |
676 | preempt_enable(); | 789 | preempt_enable(); |
790 | return 0; | ||
677 | } | 791 | } |
678 | #endif /* CONFIG_PERF_EVENTS */ | 792 | #endif /* CONFIG_PERF_EVENTS */ |
679 | 793 | ||
@@ -684,7 +798,7 @@ int trace_uprobe_register(struct ftrace_event_call *event, enum trace_reg type, | |||
684 | 798 | ||
685 | switch (type) { | 799 | switch (type) { |
686 | case TRACE_REG_REGISTER: | 800 | case TRACE_REG_REGISTER: |
687 | return probe_event_enable(tu, TP_FLAG_TRACE); | 801 | return probe_event_enable(tu, TP_FLAG_TRACE, NULL); |
688 | 802 | ||
689 | case TRACE_REG_UNREGISTER: | 803 | case TRACE_REG_UNREGISTER: |
690 | probe_event_disable(tu, TP_FLAG_TRACE); | 804 | probe_event_disable(tu, TP_FLAG_TRACE); |
@@ -692,11 +806,18 @@ int trace_uprobe_register(struct ftrace_event_call *event, enum trace_reg type, | |||
692 | 806 | ||
693 | #ifdef CONFIG_PERF_EVENTS | 807 | #ifdef CONFIG_PERF_EVENTS |
694 | case TRACE_REG_PERF_REGISTER: | 808 | case TRACE_REG_PERF_REGISTER: |
695 | return probe_event_enable(tu, TP_FLAG_PROFILE); | 809 | return probe_event_enable(tu, TP_FLAG_PROFILE, uprobe_perf_filter); |
696 | 810 | ||
697 | case TRACE_REG_PERF_UNREGISTER: | 811 | case TRACE_REG_PERF_UNREGISTER: |
698 | probe_event_disable(tu, TP_FLAG_PROFILE); | 812 | probe_event_disable(tu, TP_FLAG_PROFILE); |
699 | return 0; | 813 | return 0; |
814 | |||
815 | case TRACE_REG_PERF_OPEN: | ||
816 | return uprobe_perf_open(tu, data); | ||
817 | |||
818 | case TRACE_REG_PERF_CLOSE: | ||
819 | return uprobe_perf_close(tu, data); | ||
820 | |||
700 | #endif | 821 | #endif |
701 | default: | 822 | default: |
702 | return 0; | 823 | return 0; |
@@ -706,22 +827,20 @@ int trace_uprobe_register(struct ftrace_event_call *event, enum trace_reg type, | |||
706 | 827 | ||
707 | static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs) | 828 | static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs) |
708 | { | 829 | { |
709 | struct uprobe_trace_consumer *utc; | ||
710 | struct trace_uprobe *tu; | 830 | struct trace_uprobe *tu; |
831 | int ret = 0; | ||
711 | 832 | ||
712 | utc = container_of(con, struct uprobe_trace_consumer, cons); | 833 | tu = container_of(con, struct trace_uprobe, consumer); |
713 | tu = utc->tu; | 834 | tu->nhit++; |
714 | if (!tu || tu->consumer != utc) | ||
715 | return 0; | ||
716 | 835 | ||
717 | if (tu->flags & TP_FLAG_TRACE) | 836 | if (tu->flags & TP_FLAG_TRACE) |
718 | uprobe_trace_func(tu, regs); | 837 | ret |= uprobe_trace_func(tu, regs); |
719 | 838 | ||
720 | #ifdef CONFIG_PERF_EVENTS | 839 | #ifdef CONFIG_PERF_EVENTS |
721 | if (tu->flags & TP_FLAG_PROFILE) | 840 | if (tu->flags & TP_FLAG_PROFILE) |
722 | uprobe_perf_func(tu, regs); | 841 | ret |= uprobe_perf_func(tu, regs); |
723 | #endif | 842 | #endif |
724 | return 0; | 843 | return ret; |
725 | } | 844 | } |
726 | 845 | ||
727 | static struct trace_event_functions uprobe_funcs = { | 846 | static struct trace_event_functions uprobe_funcs = { |