aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/perf_counter.h50
-rw-r--r--kernel/perf_counter.c101
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 */
79enum 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 */
100enum 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
213enum perf_event_type { 213enum 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
1768static void perf_output_simple(struct perf_counter *counter, 1768void 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
1824static 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
1861void 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/*