diff options
-rw-r--r-- | include/linux/perf_counter.h | 50 | ||||
-rw-r--r-- | kernel/perf_counter.c | 101 |
2 files changed, 68 insertions, 83 deletions
diff --git a/include/linux/perf_counter.h b/include/linux/perf_counter.h index 43083afffe0f..06a6fba9f531 100644 --- a/include/linux/perf_counter.h +++ b/include/linux/perf_counter.h | |||
@@ -73,15 +73,6 @@ enum sw_event_ids { | |||
73 | PERF_SW_EVENTS_MAX = 7, | 73 | PERF_SW_EVENTS_MAX = 7, |
74 | }; | 74 | }; |
75 | 75 | ||
76 | /* | ||
77 | * IRQ-notification data record type: | ||
78 | */ | ||
79 | enum perf_counter_record_type { | ||
80 | PERF_RECORD_SIMPLE = 0, | ||
81 | PERF_RECORD_IRQ = 1, | ||
82 | PERF_RECORD_GROUP = 2, | ||
83 | }; | ||
84 | |||
85 | #define __PERF_COUNTER_MASK(name) \ | 76 | #define __PERF_COUNTER_MASK(name) \ |
86 | (((1ULL << PERF_COUNTER_##name##_BITS) - 1) << \ | 77 | (((1ULL << PERF_COUNTER_##name##_BITS) - 1) << \ |
87 | PERF_COUNTER_##name##_SHIFT) | 78 | PERF_COUNTER_##name##_SHIFT) |
@@ -103,6 +94,17 @@ enum perf_counter_record_type { | |||
103 | #define PERF_COUNTER_EVENT_MASK __PERF_COUNTER_MASK(EVENT) | 94 | #define PERF_COUNTER_EVENT_MASK __PERF_COUNTER_MASK(EVENT) |
104 | 95 | ||
105 | /* | 96 | /* |
97 | * Bits that can be set in hw_event.record_type to request information | ||
98 | * in the overflow packets. | ||
99 | */ | ||
100 | enum perf_counter_record_format { | ||
101 | PERF_RECORD_IP = 1U << 0, | ||
102 | PERF_RECORD_TID = 1U << 1, | ||
103 | PERF_RECORD_GROUP = 1U << 2, | ||
104 | PERF_RECORD_CALLCHAIN = 1U << 3, | ||
105 | }; | ||
106 | |||
107 | /* | ||
106 | * Bits that can be set in hw_event.read_format to request that | 108 | * Bits that can be set in hw_event.read_format to request that |
107 | * reads on the counter should return the indicated quantities, | 109 | * reads on the counter should return the indicated quantities, |
108 | * in increasing order of bit value, after the counter value. | 110 | * in increasing order of bit value, after the counter value. |
@@ -125,8 +127,8 @@ struct perf_counter_hw_event { | |||
125 | __u64 config; | 127 | __u64 config; |
126 | 128 | ||
127 | __u64 irq_period; | 129 | __u64 irq_period; |
128 | __u64 record_type; | 130 | __u32 record_type; |
129 | __u64 read_format; | 131 | __u32 read_format; |
130 | 132 | ||
131 | __u64 disabled : 1, /* off by default */ | 133 | __u64 disabled : 1, /* off by default */ |
132 | nmi : 1, /* NMI sampling */ | 134 | nmi : 1, /* NMI sampling */ |
@@ -137,12 +139,10 @@ struct perf_counter_hw_event { | |||
137 | exclude_kernel : 1, /* ditto kernel */ | 139 | exclude_kernel : 1, /* ditto kernel */ |
138 | exclude_hv : 1, /* ditto hypervisor */ | 140 | exclude_hv : 1, /* ditto hypervisor */ |
139 | exclude_idle : 1, /* don't count when idle */ | 141 | exclude_idle : 1, /* don't count when idle */ |
140 | include_tid : 1, /* include the tid */ | ||
141 | mmap : 1, /* include mmap data */ | 142 | mmap : 1, /* include mmap data */ |
142 | munmap : 1, /* include munmap data */ | 143 | munmap : 1, /* include munmap data */ |
143 | callchain : 1, /* add callchain data */ | ||
144 | 144 | ||
145 | __reserved_1 : 51; | 145 | __reserved_1 : 53; |
146 | 146 | ||
147 | __u32 extra_config_len; | 147 | __u32 extra_config_len; |
148 | __u32 __reserved_4; | 148 | __u32 __reserved_4; |
@@ -212,15 +212,21 @@ struct perf_event_header { | |||
212 | 212 | ||
213 | enum perf_event_type { | 213 | enum perf_event_type { |
214 | 214 | ||
215 | PERF_EVENT_GROUP = 1, | 215 | PERF_EVENT_MMAP = 1, |
216 | 216 | PERF_EVENT_MUNMAP = 2, | |
217 | PERF_EVENT_MMAP = 2, | ||
218 | PERF_EVENT_MUNMAP = 3, | ||
219 | 217 | ||
220 | PERF_EVENT_OVERFLOW = 1UL << 31, | 218 | /* |
221 | __PERF_EVENT_IP = 1UL << 30, | 219 | * Half the event type space is reserved for the counter overflow |
222 | __PERF_EVENT_TID = 1UL << 29, | 220 | * bitfields, as found in hw_event.record_type. |
223 | __PERF_EVENT_CALLCHAIN = 1UL << 28, | 221 | * |
222 | * These events will have types of the form: | ||
223 | * PERF_EVENT_COUNTER_OVERFLOW { | __PERF_EVENT_* } * | ||
224 | */ | ||
225 | PERF_EVENT_COUNTER_OVERFLOW = 1UL << 31, | ||
226 | __PERF_EVENT_IP = PERF_RECORD_IP, | ||
227 | __PERF_EVENT_TID = PERF_RECORD_TID, | ||
228 | __PERF_EVENT_GROUP = PERF_RECORD_GROUP, | ||
229 | __PERF_EVENT_CALLCHAIN = PERF_RECORD_CALLCHAIN, | ||
224 | }; | 230 | }; |
225 | 231 | ||
226 | #ifdef __KERNEL__ | 232 | #ifdef __KERNEL__ |
diff --git a/kernel/perf_counter.c b/kernel/perf_counter.c index 860cdc26bd7a..995063df910f 100644 --- a/kernel/perf_counter.c +++ b/kernel/perf_counter.c | |||
@@ -1765,27 +1765,34 @@ static void perf_output_end(struct perf_output_handle *handle) | |||
1765 | rcu_read_unlock(); | 1765 | rcu_read_unlock(); |
1766 | } | 1766 | } |
1767 | 1767 | ||
1768 | static void perf_output_simple(struct perf_counter *counter, | 1768 | void perf_counter_output(struct perf_counter *counter, |
1769 | int nmi, struct pt_regs *regs) | 1769 | int nmi, struct pt_regs *regs) |
1770 | { | 1770 | { |
1771 | int ret; | 1771 | int ret; |
1772 | u64 record_type = counter->hw_event.record_type; | ||
1772 | struct perf_output_handle handle; | 1773 | struct perf_output_handle handle; |
1773 | struct perf_event_header header; | 1774 | struct perf_event_header header; |
1774 | u64 ip; | 1775 | u64 ip; |
1775 | struct { | 1776 | struct { |
1776 | u32 pid, tid; | 1777 | u32 pid, tid; |
1777 | } tid_entry; | 1778 | } tid_entry; |
1779 | struct { | ||
1780 | u64 event; | ||
1781 | u64 counter; | ||
1782 | } group_entry; | ||
1778 | struct perf_callchain_entry *callchain = NULL; | 1783 | struct perf_callchain_entry *callchain = NULL; |
1779 | int callchain_size = 0; | 1784 | int callchain_size = 0; |
1780 | 1785 | ||
1781 | header.type = PERF_EVENT_OVERFLOW; | 1786 | header.type = PERF_EVENT_COUNTER_OVERFLOW; |
1782 | header.size = sizeof(header); | 1787 | header.size = sizeof(header); |
1783 | 1788 | ||
1784 | ip = instruction_pointer(regs); | 1789 | if (record_type & PERF_RECORD_IP) { |
1785 | header.type |= __PERF_EVENT_IP; | 1790 | ip = instruction_pointer(regs); |
1786 | header.size += sizeof(ip); | 1791 | header.type |= __PERF_EVENT_IP; |
1792 | header.size += sizeof(ip); | ||
1793 | } | ||
1787 | 1794 | ||
1788 | if (counter->hw_event.include_tid) { | 1795 | if (record_type & PERF_RECORD_TID) { |
1789 | /* namespace issues */ | 1796 | /* namespace issues */ |
1790 | tid_entry.pid = current->group_leader->pid; | 1797 | tid_entry.pid = current->group_leader->pid; |
1791 | tid_entry.tid = current->pid; | 1798 | tid_entry.tid = current->pid; |
@@ -1794,7 +1801,13 @@ static void perf_output_simple(struct perf_counter *counter, | |||
1794 | header.size += sizeof(tid_entry); | 1801 | header.size += sizeof(tid_entry); |
1795 | } | 1802 | } |
1796 | 1803 | ||
1797 | if (counter->hw_event.callchain) { | 1804 | if (record_type & PERF_RECORD_GROUP) { |
1805 | header.type |= __PERF_EVENT_GROUP; | ||
1806 | header.size += sizeof(u64) + | ||
1807 | counter->nr_siblings * sizeof(group_entry); | ||
1808 | } | ||
1809 | |||
1810 | if (record_type & PERF_RECORD_CALLCHAIN) { | ||
1798 | callchain = perf_callchain(regs); | 1811 | callchain = perf_callchain(regs); |
1799 | 1812 | ||
1800 | if (callchain) { | 1813 | if (callchain) { |
@@ -1810,69 +1823,35 @@ static void perf_output_simple(struct perf_counter *counter, | |||
1810 | return; | 1823 | return; |
1811 | 1824 | ||
1812 | perf_output_put(&handle, header); | 1825 | perf_output_put(&handle, header); |
1813 | perf_output_put(&handle, ip); | ||
1814 | 1826 | ||
1815 | if (counter->hw_event.include_tid) | 1827 | if (record_type & PERF_RECORD_IP) |
1816 | perf_output_put(&handle, tid_entry); | 1828 | perf_output_put(&handle, ip); |
1817 | 1829 | ||
1818 | if (callchain) | 1830 | if (record_type & PERF_RECORD_TID) |
1819 | perf_output_copy(&handle, callchain, callchain_size); | 1831 | perf_output_put(&handle, tid_entry); |
1820 | |||
1821 | perf_output_end(&handle); | ||
1822 | } | ||
1823 | |||
1824 | static void perf_output_group(struct perf_counter *counter, int nmi) | ||
1825 | { | ||
1826 | struct perf_output_handle handle; | ||
1827 | struct perf_event_header header; | ||
1828 | struct perf_counter *leader, *sub; | ||
1829 | unsigned int size; | ||
1830 | struct { | ||
1831 | u64 event; | ||
1832 | u64 counter; | ||
1833 | } entry; | ||
1834 | int ret; | ||
1835 | |||
1836 | size = sizeof(header) + counter->nr_siblings * sizeof(entry); | ||
1837 | 1832 | ||
1838 | ret = perf_output_begin(&handle, counter, size, nmi); | 1833 | if (record_type & PERF_RECORD_GROUP) { |
1839 | if (ret) | 1834 | struct perf_counter *leader, *sub; |
1840 | return; | 1835 | u64 nr = counter->nr_siblings; |
1841 | 1836 | ||
1842 | header.type = PERF_EVENT_GROUP; | 1837 | perf_output_put(&handle, nr); |
1843 | header.size = size; | ||
1844 | 1838 | ||
1845 | perf_output_put(&handle, header); | 1839 | leader = counter->group_leader; |
1840 | list_for_each_entry(sub, &leader->sibling_list, list_entry) { | ||
1841 | if (sub != counter) | ||
1842 | sub->hw_ops->read(sub); | ||
1846 | 1843 | ||
1847 | leader = counter->group_leader; | 1844 | group_entry.event = sub->hw_event.config; |
1848 | list_for_each_entry(sub, &leader->sibling_list, list_entry) { | 1845 | group_entry.counter = atomic64_read(&sub->count); |
1849 | if (sub != counter) | ||
1850 | sub->hw_ops->read(sub); | ||
1851 | 1846 | ||
1852 | entry.event = sub->hw_event.config; | 1847 | perf_output_put(&handle, group_entry); |
1853 | entry.counter = atomic64_read(&sub->count); | 1848 | } |
1854 | |||
1855 | perf_output_put(&handle, entry); | ||
1856 | } | 1849 | } |
1857 | 1850 | ||
1858 | perf_output_end(&handle); | 1851 | if (callchain) |
1859 | } | 1852 | perf_output_copy(&handle, callchain, callchain_size); |
1860 | |||
1861 | void perf_counter_output(struct perf_counter *counter, | ||
1862 | int nmi, struct pt_regs *regs) | ||
1863 | { | ||
1864 | switch (counter->hw_event.record_type) { | ||
1865 | case PERF_RECORD_SIMPLE: | ||
1866 | return; | ||
1867 | |||
1868 | case PERF_RECORD_IRQ: | ||
1869 | perf_output_simple(counter, nmi, regs); | ||
1870 | break; | ||
1871 | 1853 | ||
1872 | case PERF_RECORD_GROUP: | 1854 | perf_output_end(&handle); |
1873 | perf_output_group(counter, nmi); | ||
1874 | break; | ||
1875 | } | ||
1876 | } | 1855 | } |
1877 | 1856 | ||
1878 | /* | 1857 | /* |