aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/perf_counter.c
diff options
context:
space:
mode:
authorPeter Zijlstra <a.p.zijlstra@chello.nl>2009-08-13 05:47:53 -0400
committerIngo Molnar <mingo@elte.hu>2009-08-13 06:58:04 -0400
commit3dab77fb1bf89664bb1c9544607159dcab6f7d57 (patch)
tree9b0a47c53784cb066cecfd3091ac6042ce21a29e /kernel/perf_counter.c
parentbcfc2602e8541ac13b1def38e2591dca072cff7a (diff)
perf: Rework/fix the whole read vs group stuff
Replace PERF_SAMPLE_GROUP with PERF_SAMPLE_READ and introduce PERF_FORMAT_GROUP to deal with group reads in a more generic way. This allows you to get group reads out of read() as well. Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Corey J Ashford <cjashfor@us.ibm.com> Cc: Paul Mackerras <paulus@samba.org> Cc: stephane eranian <eranian@googlemail.com> LKML-Reference: <20090813103655.117411814@chello.nl> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/perf_counter.c')
-rw-r--r--kernel/perf_counter.c274
1 files changed, 202 insertions, 72 deletions
diff --git a/kernel/perf_counter.c b/kernel/perf_counter.c
index 3dd4339589a..b8c6b97a20a 100644
--- a/kernel/perf_counter.c
+++ b/kernel/perf_counter.c
@@ -1692,7 +1692,32 @@ static int perf_release(struct inode *inode, struct file *file)
1692 return 0; 1692 return 0;
1693} 1693}
1694 1694
1695static u64 perf_counter_read_tree(struct perf_counter *counter) 1695static int perf_counter_read_size(struct perf_counter *counter)
1696{
1697 int entry = sizeof(u64); /* value */
1698 int size = 0;
1699 int nr = 1;
1700
1701 if (counter->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
1702 size += sizeof(u64);
1703
1704 if (counter->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
1705 size += sizeof(u64);
1706
1707 if (counter->attr.read_format & PERF_FORMAT_ID)
1708 entry += sizeof(u64);
1709
1710 if (counter->attr.read_format & PERF_FORMAT_GROUP) {
1711 nr += counter->group_leader->nr_siblings;
1712 size += sizeof(u64);
1713 }
1714
1715 size += entry * nr;
1716
1717 return size;
1718}
1719
1720static u64 perf_counter_read_value(struct perf_counter *counter)
1696{ 1721{
1697 struct perf_counter *child; 1722 struct perf_counter *child;
1698 u64 total = 0; 1723 u64 total = 0;
@@ -1704,14 +1729,96 @@ static u64 perf_counter_read_tree(struct perf_counter *counter)
1704 return total; 1729 return total;
1705} 1730}
1706 1731
1732static int perf_counter_read_entry(struct perf_counter *counter,
1733 u64 read_format, char __user *buf)
1734{
1735 int n = 0, count = 0;
1736 u64 values[2];
1737
1738 values[n++] = perf_counter_read_value(counter);
1739 if (read_format & PERF_FORMAT_ID)
1740 values[n++] = primary_counter_id(counter);
1741
1742 count = n * sizeof(u64);
1743
1744 if (copy_to_user(buf, values, count))
1745 return -EFAULT;
1746
1747 return count;
1748}
1749
1750static int perf_counter_read_group(struct perf_counter *counter,
1751 u64 read_format, char __user *buf)
1752{
1753 struct perf_counter *leader = counter->group_leader, *sub;
1754 int n = 0, size = 0, err = -EFAULT;
1755 u64 values[3];
1756
1757 values[n++] = 1 + leader->nr_siblings;
1758 if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) {
1759 values[n++] = leader->total_time_enabled +
1760 atomic64_read(&leader->child_total_time_enabled);
1761 }
1762 if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) {
1763 values[n++] = leader->total_time_running +
1764 atomic64_read(&leader->child_total_time_running);
1765 }
1766
1767 size = n * sizeof(u64);
1768
1769 if (copy_to_user(buf, values, size))
1770 return -EFAULT;
1771
1772 err = perf_counter_read_entry(leader, read_format, buf + size);
1773 if (err < 0)
1774 return err;
1775
1776 size += err;
1777
1778 list_for_each_entry(sub, &leader->sibling_list, list_entry) {
1779 err = perf_counter_read_entry(counter, read_format,
1780 buf + size);
1781 if (err < 0)
1782 return err;
1783
1784 size += err;
1785 }
1786
1787 return size;
1788}
1789
1790static int perf_counter_read_one(struct perf_counter *counter,
1791 u64 read_format, char __user *buf)
1792{
1793 u64 values[4];
1794 int n = 0;
1795
1796 values[n++] = perf_counter_read_value(counter);
1797 if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) {
1798 values[n++] = counter->total_time_enabled +
1799 atomic64_read(&counter->child_total_time_enabled);
1800 }
1801 if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) {
1802 values[n++] = counter->total_time_running +
1803 atomic64_read(&counter->child_total_time_running);
1804 }
1805 if (read_format & PERF_FORMAT_ID)
1806 values[n++] = primary_counter_id(counter);
1807
1808 if (copy_to_user(buf, values, n * sizeof(u64)))
1809 return -EFAULT;
1810
1811 return n * sizeof(u64);
1812}
1813
1707/* 1814/*
1708 * Read the performance counter - simple non blocking version for now 1815 * Read the performance counter - simple non blocking version for now
1709 */ 1816 */
1710static ssize_t 1817static ssize_t
1711perf_read_hw(struct perf_counter *counter, char __user *buf, size_t count) 1818perf_read_hw(struct perf_counter *counter, char __user *buf, size_t count)
1712{ 1819{
1713 u64 values[4]; 1820 u64 read_format = counter->attr.read_format;
1714 int n; 1821 int ret;
1715 1822
1716 /* 1823 /*
1717 * Return end-of-file for a read on a counter that is in 1824 * Return end-of-file for a read on a counter that is in
@@ -1721,28 +1828,18 @@ perf_read_hw(struct perf_counter *counter, char __user *buf, size_t count)
1721 if (counter->state == PERF_COUNTER_STATE_ERROR) 1828 if (counter->state == PERF_COUNTER_STATE_ERROR)
1722 return 0; 1829 return 0;
1723 1830
1831 if (count < perf_counter_read_size(counter))
1832 return -ENOSPC;
1833
1724 WARN_ON_ONCE(counter->ctx->parent_ctx); 1834 WARN_ON_ONCE(counter->ctx->parent_ctx);
1725 mutex_lock(&counter->child_mutex); 1835 mutex_lock(&counter->child_mutex);
1726 values[0] = perf_counter_read_tree(counter); 1836 if (read_format & PERF_FORMAT_GROUP)
1727 n = 1; 1837 ret = perf_counter_read_group(counter, read_format, buf);
1728 if (counter->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) 1838 else
1729 values[n++] = counter->total_time_enabled + 1839 ret = perf_counter_read_one(counter, read_format, buf);
1730 atomic64_read(&counter->child_total_time_enabled);
1731 if (counter->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
1732 values[n++] = counter->total_time_running +
1733 atomic64_read(&counter->child_total_time_running);
1734 if (counter->attr.read_format & PERF_FORMAT_ID)
1735 values[n++] = primary_counter_id(counter);
1736 mutex_unlock(&counter->child_mutex); 1840 mutex_unlock(&counter->child_mutex);
1737 1841
1738 if (count < n * sizeof(u64)) 1842 return ret;
1739 return -EINVAL;
1740 count = n * sizeof(u64);
1741
1742 if (copy_to_user(buf, values, count))
1743 return -EFAULT;
1744
1745 return count;
1746} 1843}
1747 1844
1748static ssize_t 1845static ssize_t
@@ -2631,6 +2728,79 @@ static u32 perf_counter_tid(struct perf_counter *counter, struct task_struct *p)
2631 return task_pid_nr_ns(p, counter->ns); 2728 return task_pid_nr_ns(p, counter->ns);
2632} 2729}
2633 2730
2731static void perf_output_read_one(struct perf_output_handle *handle,
2732 struct perf_counter *counter)
2733{
2734 u64 read_format = counter->attr.read_format;
2735 u64 values[4];
2736 int n = 0;
2737
2738 values[n++] = atomic64_read(&counter->count);
2739 if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) {
2740 values[n++] = counter->total_time_enabled +
2741 atomic64_read(&counter->child_total_time_enabled);
2742 }
2743 if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) {
2744 values[n++] = counter->total_time_running +
2745 atomic64_read(&counter->child_total_time_running);
2746 }
2747 if (read_format & PERF_FORMAT_ID)
2748 values[n++] = primary_counter_id(counter);
2749
2750 perf_output_copy(handle, values, n * sizeof(u64));
2751}
2752
2753/*
2754 * XXX PERF_FORMAT_GROUP vs inherited counters seems difficult.
2755 */
2756static void perf_output_read_group(struct perf_output_handle *handle,
2757 struct perf_counter *counter)
2758{
2759 struct perf_counter *leader = counter->group_leader, *sub;
2760 u64 read_format = counter->attr.read_format;
2761 u64 values[5];
2762 int n = 0;
2763
2764 values[n++] = 1 + leader->nr_siblings;
2765
2766 if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
2767 values[n++] = leader->total_time_enabled;
2768
2769 if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
2770 values[n++] = leader->total_time_running;
2771
2772 if (leader != counter)
2773 leader->pmu->read(leader);
2774
2775 values[n++] = atomic64_read(&leader->count);
2776 if (read_format & PERF_FORMAT_ID)
2777 values[n++] = primary_counter_id(leader);
2778
2779 perf_output_copy(handle, values, n * sizeof(u64));
2780
2781 list_for_each_entry(sub, &leader->sibling_list, list_entry) {
2782 n = 0;
2783
2784 if (sub != counter)
2785 sub->pmu->read(sub);
2786
2787 values[n++] = atomic64_read(&sub->count);
2788 if (read_format & PERF_FORMAT_ID)
2789 values[n++] = primary_counter_id(sub);
2790
2791 perf_output_copy(handle, values, n * sizeof(u64));
2792 }
2793}
2794
2795static void perf_output_read(struct perf_output_handle *handle,
2796 struct perf_counter *counter)
2797{
2798 if (counter->attr.read_format & PERF_FORMAT_GROUP)
2799 perf_output_read_group(handle, counter);
2800 else
2801 perf_output_read_one(handle, counter);
2802}
2803
2634void perf_counter_output(struct perf_counter *counter, int nmi, 2804void perf_counter_output(struct perf_counter *counter, int nmi,
2635 struct perf_sample_data *data) 2805 struct perf_sample_data *data)
2636{ 2806{
@@ -2642,10 +2812,6 @@ void perf_counter_output(struct perf_counter *counter, int nmi,
2642 struct { 2812 struct {
2643 u32 pid, tid; 2813 u32 pid, tid;
2644 } tid_entry; 2814 } tid_entry;
2645 struct {
2646 u64 id;
2647 u64 counter;
2648 } group_entry;
2649 struct perf_callchain_entry *callchain = NULL; 2815 struct perf_callchain_entry *callchain = NULL;
2650 int callchain_size = 0; 2816 int callchain_size = 0;
2651 u64 time; 2817 u64 time;
@@ -2700,10 +2866,8 @@ void perf_counter_output(struct perf_counter *counter, int nmi,
2700 if (sample_type & PERF_SAMPLE_PERIOD) 2866 if (sample_type & PERF_SAMPLE_PERIOD)
2701 header.size += sizeof(u64); 2867 header.size += sizeof(u64);
2702 2868
2703 if (sample_type & PERF_SAMPLE_GROUP) { 2869 if (sample_type & PERF_SAMPLE_READ)
2704 header.size += sizeof(u64) + 2870 header.size += perf_counter_read_size(counter);
2705 counter->nr_siblings * sizeof(group_entry);
2706 }
2707 2871
2708 if (sample_type & PERF_SAMPLE_CALLCHAIN) { 2872 if (sample_type & PERF_SAMPLE_CALLCHAIN) {
2709 callchain = perf_callchain(data->regs); 2873 callchain = perf_callchain(data->regs);
@@ -2760,26 +2924,8 @@ void perf_counter_output(struct perf_counter *counter, int nmi,
2760 if (sample_type & PERF_SAMPLE_PERIOD) 2924 if (sample_type & PERF_SAMPLE_PERIOD)
2761 perf_output_put(&handle, data->period); 2925 perf_output_put(&handle, data->period);
2762 2926
2763 /* 2927 if (sample_type & PERF_SAMPLE_READ)
2764 * XXX PERF_SAMPLE_GROUP vs inherited counters seems difficult. 2928 perf_output_read(&handle, counter);
2765 */
2766 if (sample_type & PERF_SAMPLE_GROUP) {
2767 struct perf_counter *leader, *sub;
2768 u64 nr = counter->nr_siblings;
2769
2770 perf_output_put(&handle, nr);
2771
2772 leader = counter->group_leader;
2773 list_for_each_entry(sub, &leader->sibling_list, list_entry) {
2774 if (sub != counter)
2775 sub->pmu->read(sub);
2776
2777 group_entry.id = primary_counter_id(sub);
2778 group_entry.counter = atomic64_read(&sub->count);
2779
2780 perf_output_put(&handle, group_entry);
2781 }
2782 }
2783 2929
2784 if (sample_type & PERF_SAMPLE_CALLCHAIN) { 2930 if (sample_type & PERF_SAMPLE_CALLCHAIN) {
2785 if (callchain) 2931 if (callchain)
@@ -2818,8 +2964,6 @@ struct perf_read_event {
2818 2964
2819 u32 pid; 2965 u32 pid;
2820 u32 tid; 2966 u32 tid;
2821 u64 value;
2822 u64 format[3];
2823}; 2967};
2824 2968
2825static void 2969static void
@@ -2831,34 +2975,20 @@ perf_counter_read_event(struct perf_counter *counter,
2831 .header = { 2975 .header = {
2832 .type = PERF_EVENT_READ, 2976 .type = PERF_EVENT_READ,
2833 .misc = 0, 2977 .misc = 0,
2834 .size = sizeof(event) - sizeof(event.format), 2978 .size = sizeof(event) + perf_counter_read_size(counter),
2835 }, 2979 },
2836 .pid = perf_counter_pid(counter, task), 2980 .pid = perf_counter_pid(counter, task),
2837 .tid = perf_counter_tid(counter, task), 2981 .tid = perf_counter_tid(counter, task),
2838 .value = atomic64_read(&counter->count),
2839 }; 2982 };
2840 int ret, i = 0; 2983 int ret;
2841
2842 if (counter->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) {
2843 event.header.size += sizeof(u64);
2844 event.format[i++] = counter->total_time_enabled;
2845 }
2846
2847 if (counter->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) {
2848 event.header.size += sizeof(u64);
2849 event.format[i++] = counter->total_time_running;
2850 }
2851
2852 if (counter->attr.read_format & PERF_FORMAT_ID) {
2853 event.header.size += sizeof(u64);
2854 event.format[i++] = primary_counter_id(counter);
2855 }
2856 2984
2857 ret = perf_output_begin(&handle, counter, event.header.size, 0, 0); 2985 ret = perf_output_begin(&handle, counter, event.header.size, 0, 0);
2858 if (ret) 2986 if (ret)
2859 return; 2987 return;
2860 2988
2861 perf_output_copy(&handle, &event, event.header.size); 2989 perf_output_put(&handle, event);
2990 perf_output_read(&handle, counter);
2991
2862 perf_output_end(&handle); 2992 perf_output_end(&handle);
2863} 2993}
2864 2994
@@ -3921,9 +4051,9 @@ perf_counter_alloc(struct perf_counter_attr *attr,
3921 atomic64_set(&hwc->period_left, hwc->sample_period); 4051 atomic64_set(&hwc->period_left, hwc->sample_period);
3922 4052
3923 /* 4053 /*
3924 * we currently do not support PERF_SAMPLE_GROUP on inherited counters 4054 * we currently do not support PERF_FORMAT_GROUP on inherited counters
3925 */ 4055 */
3926 if (attr->inherit && (attr->sample_type & PERF_SAMPLE_GROUP)) 4056 if (attr->inherit && (attr->read_format & PERF_FORMAT_GROUP))
3927 goto done; 4057 goto done;
3928 4058
3929 switch (attr->type) { 4059 switch (attr->type) {