diff options
author | Jiri Olsa <jolsa@redhat.com> | 2013-05-06 12:27:18 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2013-05-07 07:17:29 -0400 |
commit | 52d857a8784a09576215c71cebf368d61c12a754 (patch) | |
tree | 5bb32d39139ae0a6fb4ebe38f4d20f79317d18e4 | |
parent | 524eff183f51d080a83b348d0ea97c08b3607b9a (diff) |
perf: Factor out auxiliary events notification
Add perf_event_aux() function to send out all types of
auxiliary events - mmap, task, comm events. For each type
there's match and output functions defined and used as
callbacks during perf_event_aux processing.
This way we can centralize the pmu/context iterating and
event matching logic. Also since lot of the code was
duplicated, this patch reduces the .text size about 2kB
on my setup:
snipped output from 'objdump -x kernel/events/core.o'
before:
Idx Name Size
0 .text 0000d313
after:
Idx Name Size
0 .text 0000cad3
Signed-off-by: Jiri Olsa <jolsa@redhat.com>
Acked-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Stephane Eranian <eranian@google.com>
Cc: Borislav Petkov <bp@alien8.de>
Link: http://lkml.kernel.org/r/1367857638-27631-3-git-send-email-jolsa@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | kernel/events/core.c | 242 |
1 files changed, 89 insertions, 153 deletions
diff --git a/kernel/events/core.c b/kernel/events/core.c index 38b68a05c3c6..9dc297faf7c0 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c | |||
@@ -4394,6 +4394,64 @@ perf_event_read_event(struct perf_event *event, | |||
4394 | perf_output_end(&handle); | 4394 | perf_output_end(&handle); |
4395 | } | 4395 | } |
4396 | 4396 | ||
4397 | typedef int (perf_event_aux_match_cb)(struct perf_event *event, void *data); | ||
4398 | typedef void (perf_event_aux_output_cb)(struct perf_event *event, void *data); | ||
4399 | |||
4400 | static void | ||
4401 | perf_event_aux_ctx(struct perf_event_context *ctx, | ||
4402 | perf_event_aux_match_cb match, | ||
4403 | perf_event_aux_output_cb output, | ||
4404 | void *data) | ||
4405 | { | ||
4406 | struct perf_event *event; | ||
4407 | |||
4408 | list_for_each_entry_rcu(event, &ctx->event_list, event_entry) { | ||
4409 | if (event->state < PERF_EVENT_STATE_INACTIVE) | ||
4410 | continue; | ||
4411 | if (!event_filter_match(event)) | ||
4412 | continue; | ||
4413 | if (match(event, data)) | ||
4414 | output(event, data); | ||
4415 | } | ||
4416 | } | ||
4417 | |||
4418 | static void | ||
4419 | perf_event_aux(perf_event_aux_match_cb match, | ||
4420 | perf_event_aux_output_cb output, | ||
4421 | void *data, | ||
4422 | struct perf_event_context *task_ctx) | ||
4423 | { | ||
4424 | struct perf_cpu_context *cpuctx; | ||
4425 | struct perf_event_context *ctx; | ||
4426 | struct pmu *pmu; | ||
4427 | int ctxn; | ||
4428 | |||
4429 | rcu_read_lock(); | ||
4430 | list_for_each_entry_rcu(pmu, &pmus, entry) { | ||
4431 | cpuctx = get_cpu_ptr(pmu->pmu_cpu_context); | ||
4432 | if (cpuctx->unique_pmu != pmu) | ||
4433 | goto next; | ||
4434 | perf_event_aux_ctx(&cpuctx->ctx, match, output, data); | ||
4435 | if (task_ctx) | ||
4436 | goto next; | ||
4437 | ctxn = pmu->task_ctx_nr; | ||
4438 | if (ctxn < 0) | ||
4439 | goto next; | ||
4440 | ctx = rcu_dereference(current->perf_event_ctxp[ctxn]); | ||
4441 | if (ctx) | ||
4442 | perf_event_aux_ctx(ctx, match, output, data); | ||
4443 | next: | ||
4444 | put_cpu_ptr(pmu->pmu_cpu_context); | ||
4445 | } | ||
4446 | |||
4447 | if (task_ctx) { | ||
4448 | preempt_disable(); | ||
4449 | perf_event_aux_ctx(task_ctx, match, output, data); | ||
4450 | preempt_enable(); | ||
4451 | } | ||
4452 | rcu_read_unlock(); | ||
4453 | } | ||
4454 | |||
4397 | /* | 4455 | /* |
4398 | * task tracking -- fork/exit | 4456 | * task tracking -- fork/exit |
4399 | * | 4457 | * |
@@ -4416,8 +4474,9 @@ struct perf_task_event { | |||
4416 | }; | 4474 | }; |
4417 | 4475 | ||
4418 | static void perf_event_task_output(struct perf_event *event, | 4476 | static void perf_event_task_output(struct perf_event *event, |
4419 | struct perf_task_event *task_event) | 4477 | void *data) |
4420 | { | 4478 | { |
4479 | struct perf_task_event *task_event = data; | ||
4421 | struct perf_output_handle handle; | 4480 | struct perf_output_handle handle; |
4422 | struct perf_sample_data sample; | 4481 | struct perf_sample_data sample; |
4423 | struct task_struct *task = task_event->task; | 4482 | struct task_struct *task = task_event->task; |
@@ -4445,64 +4504,11 @@ out: | |||
4445 | task_event->event_id.header.size = size; | 4504 | task_event->event_id.header.size = size; |
4446 | } | 4505 | } |
4447 | 4506 | ||
4448 | static int perf_event_task_match(struct perf_event *event) | 4507 | static int perf_event_task_match(struct perf_event *event, |
4449 | { | 4508 | void *data __maybe_unused) |
4450 | if (event->state < PERF_EVENT_STATE_INACTIVE) | ||
4451 | return 0; | ||
4452 | |||
4453 | if (!event_filter_match(event)) | ||
4454 | return 0; | ||
4455 | |||
4456 | if (event->attr.comm || event->attr.mmap || | ||
4457 | event->attr.mmap_data || event->attr.task) | ||
4458 | return 1; | ||
4459 | |||
4460 | return 0; | ||
4461 | } | ||
4462 | |||
4463 | static void perf_event_task_ctx(struct perf_event_context *ctx, | ||
4464 | struct perf_task_event *task_event) | ||
4465 | { | ||
4466 | struct perf_event *event; | ||
4467 | |||
4468 | list_for_each_entry_rcu(event, &ctx->event_list, event_entry) { | ||
4469 | if (perf_event_task_match(event)) | ||
4470 | perf_event_task_output(event, task_event); | ||
4471 | } | ||
4472 | } | ||
4473 | |||
4474 | static void perf_event_task_event(struct perf_task_event *task_event) | ||
4475 | { | 4509 | { |
4476 | struct perf_cpu_context *cpuctx; | 4510 | return event->attr.comm || event->attr.mmap || |
4477 | struct perf_event_context *ctx, *task_ctx = task_event->task_ctx; | 4511 | event->attr.mmap_data || event->attr.task; |
4478 | struct pmu *pmu; | ||
4479 | int ctxn; | ||
4480 | |||
4481 | rcu_read_lock(); | ||
4482 | list_for_each_entry_rcu(pmu, &pmus, entry) { | ||
4483 | cpuctx = get_cpu_ptr(pmu->pmu_cpu_context); | ||
4484 | if (cpuctx->unique_pmu != pmu) | ||
4485 | goto next; | ||
4486 | perf_event_task_ctx(&cpuctx->ctx, task_event); | ||
4487 | |||
4488 | if (task_ctx) | ||
4489 | goto next; | ||
4490 | ctxn = pmu->task_ctx_nr; | ||
4491 | if (ctxn < 0) | ||
4492 | goto next; | ||
4493 | ctx = rcu_dereference(current->perf_event_ctxp[ctxn]); | ||
4494 | if (ctx) | ||
4495 | perf_event_task_ctx(ctx, task_event); | ||
4496 | next: | ||
4497 | put_cpu_ptr(pmu->pmu_cpu_context); | ||
4498 | } | ||
4499 | if (task_ctx) { | ||
4500 | preempt_disable(); | ||
4501 | perf_event_task_ctx(task_ctx, task_event); | ||
4502 | preempt_enable(); | ||
4503 | } | ||
4504 | |||
4505 | rcu_read_unlock(); | ||
4506 | } | 4512 | } |
4507 | 4513 | ||
4508 | static void perf_event_task(struct task_struct *task, | 4514 | static void perf_event_task(struct task_struct *task, |
@@ -4533,7 +4539,10 @@ static void perf_event_task(struct task_struct *task, | |||
4533 | }, | 4539 | }, |
4534 | }; | 4540 | }; |
4535 | 4541 | ||
4536 | perf_event_task_event(&task_event); | 4542 | perf_event_aux(perf_event_task_match, |
4543 | perf_event_task_output, | ||
4544 | &task_event, | ||
4545 | task_ctx); | ||
4537 | } | 4546 | } |
4538 | 4547 | ||
4539 | void perf_event_fork(struct task_struct *task) | 4548 | void perf_event_fork(struct task_struct *task) |
@@ -4559,8 +4568,9 @@ struct perf_comm_event { | |||
4559 | }; | 4568 | }; |
4560 | 4569 | ||
4561 | static void perf_event_comm_output(struct perf_event *event, | 4570 | static void perf_event_comm_output(struct perf_event *event, |
4562 | struct perf_comm_event *comm_event) | 4571 | void *data) |
4563 | { | 4572 | { |
4573 | struct perf_comm_event *comm_event = data; | ||
4564 | struct perf_output_handle handle; | 4574 | struct perf_output_handle handle; |
4565 | struct perf_sample_data sample; | 4575 | struct perf_sample_data sample; |
4566 | int size = comm_event->event_id.header.size; | 4576 | int size = comm_event->event_id.header.size; |
@@ -4587,39 +4597,16 @@ out: | |||
4587 | comm_event->event_id.header.size = size; | 4597 | comm_event->event_id.header.size = size; |
4588 | } | 4598 | } |
4589 | 4599 | ||
4590 | static int perf_event_comm_match(struct perf_event *event) | 4600 | static int perf_event_comm_match(struct perf_event *event, |
4591 | { | 4601 | void *data __maybe_unused) |
4592 | if (event->state < PERF_EVENT_STATE_INACTIVE) | ||
4593 | return 0; | ||
4594 | |||
4595 | if (!event_filter_match(event)) | ||
4596 | return 0; | ||
4597 | |||
4598 | if (event->attr.comm) | ||
4599 | return 1; | ||
4600 | |||
4601 | return 0; | ||
4602 | } | ||
4603 | |||
4604 | static void perf_event_comm_ctx(struct perf_event_context *ctx, | ||
4605 | struct perf_comm_event *comm_event) | ||
4606 | { | 4602 | { |
4607 | struct perf_event *event; | 4603 | return event->attr.comm; |
4608 | |||
4609 | list_for_each_entry_rcu(event, &ctx->event_list, event_entry) { | ||
4610 | if (perf_event_comm_match(event)) | ||
4611 | perf_event_comm_output(event, comm_event); | ||
4612 | } | ||
4613 | } | 4604 | } |
4614 | 4605 | ||
4615 | static void perf_event_comm_event(struct perf_comm_event *comm_event) | 4606 | static void perf_event_comm_event(struct perf_comm_event *comm_event) |
4616 | { | 4607 | { |
4617 | struct perf_cpu_context *cpuctx; | ||
4618 | struct perf_event_context *ctx; | ||
4619 | char comm[TASK_COMM_LEN]; | 4608 | char comm[TASK_COMM_LEN]; |
4620 | unsigned int size; | 4609 | unsigned int size; |
4621 | struct pmu *pmu; | ||
4622 | int ctxn; | ||
4623 | 4610 | ||
4624 | memset(comm, 0, sizeof(comm)); | 4611 | memset(comm, 0, sizeof(comm)); |
4625 | strlcpy(comm, comm_event->task->comm, sizeof(comm)); | 4612 | strlcpy(comm, comm_event->task->comm, sizeof(comm)); |
@@ -4629,24 +4616,11 @@ static void perf_event_comm_event(struct perf_comm_event *comm_event) | |||
4629 | comm_event->comm_size = size; | 4616 | comm_event->comm_size = size; |
4630 | 4617 | ||
4631 | comm_event->event_id.header.size = sizeof(comm_event->event_id) + size; | 4618 | comm_event->event_id.header.size = sizeof(comm_event->event_id) + size; |
4632 | rcu_read_lock(); | ||
4633 | list_for_each_entry_rcu(pmu, &pmus, entry) { | ||
4634 | cpuctx = get_cpu_ptr(pmu->pmu_cpu_context); | ||
4635 | if (cpuctx->unique_pmu != pmu) | ||
4636 | goto next; | ||
4637 | perf_event_comm_ctx(&cpuctx->ctx, comm_event); | ||
4638 | 4619 | ||
4639 | ctxn = pmu->task_ctx_nr; | 4620 | perf_event_aux(perf_event_comm_match, |
4640 | if (ctxn < 0) | 4621 | perf_event_comm_output, |
4641 | goto next; | 4622 | comm_event, |
4642 | 4623 | NULL); | |
4643 | ctx = rcu_dereference(current->perf_event_ctxp[ctxn]); | ||
4644 | if (ctx) | ||
4645 | perf_event_comm_ctx(ctx, comm_event); | ||
4646 | next: | ||
4647 | put_cpu_ptr(pmu->pmu_cpu_context); | ||
4648 | } | ||
4649 | rcu_read_unlock(); | ||
4650 | } | 4624 | } |
4651 | 4625 | ||
4652 | void perf_event_comm(struct task_struct *task) | 4626 | void perf_event_comm(struct task_struct *task) |
@@ -4708,8 +4682,9 @@ struct perf_mmap_event { | |||
4708 | }; | 4682 | }; |
4709 | 4683 | ||
4710 | static void perf_event_mmap_output(struct perf_event *event, | 4684 | static void perf_event_mmap_output(struct perf_event *event, |
4711 | struct perf_mmap_event *mmap_event) | 4685 | void *data) |
4712 | { | 4686 | { |
4687 | struct perf_mmap_event *mmap_event = data; | ||
4713 | struct perf_output_handle handle; | 4688 | struct perf_output_handle handle; |
4714 | struct perf_sample_data sample; | 4689 | struct perf_sample_data sample; |
4715 | int size = mmap_event->event_id.header.size; | 4690 | int size = mmap_event->event_id.header.size; |
@@ -4736,46 +4711,24 @@ out: | |||
4736 | } | 4711 | } |
4737 | 4712 | ||
4738 | static int perf_event_mmap_match(struct perf_event *event, | 4713 | static int perf_event_mmap_match(struct perf_event *event, |
4739 | struct perf_mmap_event *mmap_event, | 4714 | void *data) |
4740 | int executable) | ||
4741 | { | 4715 | { |
4742 | if (event->state < PERF_EVENT_STATE_INACTIVE) | 4716 | struct perf_mmap_event *mmap_event = data; |
4743 | return 0; | 4717 | struct vm_area_struct *vma = mmap_event->vma; |
4744 | 4718 | int executable = vma->vm_flags & VM_EXEC; | |
4745 | if (!event_filter_match(event)) | ||
4746 | return 0; | ||
4747 | |||
4748 | if ((!executable && event->attr.mmap_data) || | ||
4749 | (executable && event->attr.mmap)) | ||
4750 | return 1; | ||
4751 | |||
4752 | return 0; | ||
4753 | } | ||
4754 | |||
4755 | static void perf_event_mmap_ctx(struct perf_event_context *ctx, | ||
4756 | struct perf_mmap_event *mmap_event, | ||
4757 | int executable) | ||
4758 | { | ||
4759 | struct perf_event *event; | ||
4760 | 4719 | ||
4761 | list_for_each_entry_rcu(event, &ctx->event_list, event_entry) { | 4720 | return (!executable && event->attr.mmap_data) || |
4762 | if (perf_event_mmap_match(event, mmap_event, executable)) | 4721 | (executable && event->attr.mmap); |
4763 | perf_event_mmap_output(event, mmap_event); | ||
4764 | } | ||
4765 | } | 4722 | } |
4766 | 4723 | ||
4767 | static void perf_event_mmap_event(struct perf_mmap_event *mmap_event) | 4724 | static void perf_event_mmap_event(struct perf_mmap_event *mmap_event) |
4768 | { | 4725 | { |
4769 | struct perf_cpu_context *cpuctx; | ||
4770 | struct perf_event_context *ctx; | ||
4771 | struct vm_area_struct *vma = mmap_event->vma; | 4726 | struct vm_area_struct *vma = mmap_event->vma; |
4772 | struct file *file = vma->vm_file; | 4727 | struct file *file = vma->vm_file; |
4773 | unsigned int size; | 4728 | unsigned int size; |
4774 | char tmp[16]; | 4729 | char tmp[16]; |
4775 | char *buf = NULL; | 4730 | char *buf = NULL; |
4776 | const char *name; | 4731 | const char *name; |
4777 | struct pmu *pmu; | ||
4778 | int ctxn; | ||
4779 | 4732 | ||
4780 | memset(tmp, 0, sizeof(tmp)); | 4733 | memset(tmp, 0, sizeof(tmp)); |
4781 | 4734 | ||
@@ -4831,27 +4784,10 @@ got_name: | |||
4831 | 4784 | ||
4832 | mmap_event->event_id.header.size = sizeof(mmap_event->event_id) + size; | 4785 | mmap_event->event_id.header.size = sizeof(mmap_event->event_id) + size; |
4833 | 4786 | ||
4834 | rcu_read_lock(); | 4787 | perf_event_aux(perf_event_mmap_match, |
4835 | list_for_each_entry_rcu(pmu, &pmus, entry) { | 4788 | perf_event_mmap_output, |
4836 | cpuctx = get_cpu_ptr(pmu->pmu_cpu_context); | 4789 | mmap_event, |
4837 | if (cpuctx->unique_pmu != pmu) | 4790 | NULL); |
4838 | goto next; | ||
4839 | perf_event_mmap_ctx(&cpuctx->ctx, mmap_event, | ||
4840 | vma->vm_flags & VM_EXEC); | ||
4841 | |||
4842 | ctxn = pmu->task_ctx_nr; | ||
4843 | if (ctxn < 0) | ||
4844 | goto next; | ||
4845 | |||
4846 | ctx = rcu_dereference(current->perf_event_ctxp[ctxn]); | ||
4847 | if (ctx) { | ||
4848 | perf_event_mmap_ctx(ctx, mmap_event, | ||
4849 | vma->vm_flags & VM_EXEC); | ||
4850 | } | ||
4851 | next: | ||
4852 | put_cpu_ptr(pmu->pmu_cpu_context); | ||
4853 | } | ||
4854 | rcu_read_unlock(); | ||
4855 | 4791 | ||
4856 | kfree(buf); | 4792 | kfree(buf); |
4857 | } | 4793 | } |