aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorPeter Zijlstra <a.p.zijlstra@chello.nl>2009-07-21 07:19:40 -0400
committerPeter Zijlstra <a.p.zijlstra@chello.nl>2009-07-22 12:05:56 -0400
commit7f453c24b95a085fc7bd35d53b33abc4dc5a048b (patch)
tree63d2b80acb3095a3e1a56c69d20a8137a1337aed /kernel
parent573402db02746179b3f95f83a11a787501f52d0a (diff)
perf_counter: PERF_SAMPLE_ID and inherited counters
Anton noted that for inherited counters the counter-id as provided by PERF_SAMPLE_ID isn't mappable to the id found through PERF_RECORD_ID because each inherited counter gets its own id. His suggestion was to always return the parent counter id, since that is the primary counter id as exposed. However, these inherited counters have a unique identifier so that events like PERF_EVENT_PERIOD and PERF_EVENT_THROTTLE can be specific about which counter gets modified, which is important when trying to normalize the sample streams. This patch removes PERF_EVENT_PERIOD in favour of PERF_SAMPLE_PERIOD, which is more useful anyway, since changing periods became a lot more common than initially thought -- rendering PERF_EVENT_PERIOD the less useful solution (also, PERF_SAMPLE_PERIOD reports the more accurate value, since it reports the value used to trigger the overflow, whereas PERF_EVENT_PERIOD simply reports the requested period changed, which might only take effect on the next cycle). This still leaves us PERF_EVENT_THROTTLE to consider, but since that _should_ be a rare occurrence, and linking it to a primary id is the most useful bit to diagnose the problem, we introduce a PERF_SAMPLE_STREAM_ID, for those few cases where the full reconstruction is important. [Does change the ABI a little, but I see no other way out] Suggested-by: Anton Blanchard <anton@samba.org> Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl> LKML-Reference: <1248095846.15751.8781.camel@twins>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/perf_counter.c92
1 files changed, 31 insertions, 61 deletions
diff --git a/kernel/perf_counter.c b/kernel/perf_counter.c
index e1d6a3aa1333..7530588fa5c5 100644
--- a/kernel/perf_counter.c
+++ b/kernel/perf_counter.c
@@ -155,6 +155,20 @@ static void unclone_ctx(struct perf_counter_context *ctx)
155} 155}
156 156
157/* 157/*
158 * If we inherit counters we want to return the parent counter id
159 * to userspace.
160 */
161static u64 primary_counter_id(struct perf_counter *counter)
162{
163 u64 id = counter->id;
164
165 if (counter->parent)
166 id = counter->parent->id;
167
168 return id;
169}
170
171/*
158 * Get the perf_counter_context for a task and lock it. 172 * Get the perf_counter_context for a task and lock it.
159 * This has to cope with with the fact that until it is locked, 173 * This has to cope with with the fact that until it is locked,
160 * the context could get moved to another task. 174 * the context could get moved to another task.
@@ -1296,7 +1310,6 @@ static void perf_counter_cpu_sched_in(struct perf_cpu_context *cpuctx, int cpu)
1296#define MAX_INTERRUPTS (~0ULL) 1310#define MAX_INTERRUPTS (~0ULL)
1297 1311
1298static void perf_log_throttle(struct perf_counter *counter, int enable); 1312static void perf_log_throttle(struct perf_counter *counter, int enable);
1299static void perf_log_period(struct perf_counter *counter, u64 period);
1300 1313
1301static void perf_adjust_period(struct perf_counter *counter, u64 events) 1314static void perf_adjust_period(struct perf_counter *counter, u64 events)
1302{ 1315{
@@ -1315,8 +1328,6 @@ static void perf_adjust_period(struct perf_counter *counter, u64 events)
1315 if (!sample_period) 1328 if (!sample_period)
1316 sample_period = 1; 1329 sample_period = 1;
1317 1330
1318 perf_log_period(counter, sample_period);
1319
1320 hwc->sample_period = sample_period; 1331 hwc->sample_period = sample_period;
1321} 1332}
1322 1333
@@ -1705,7 +1716,7 @@ perf_read_hw(struct perf_counter *counter, char __user *buf, size_t count)
1705 values[n++] = counter->total_time_running + 1716 values[n++] = counter->total_time_running +
1706 atomic64_read(&counter->child_total_time_running); 1717 atomic64_read(&counter->child_total_time_running);
1707 if (counter->attr.read_format & PERF_FORMAT_ID) 1718 if (counter->attr.read_format & PERF_FORMAT_ID)
1708 values[n++] = counter->id; 1719 values[n++] = primary_counter_id(counter);
1709 mutex_unlock(&counter->child_mutex); 1720 mutex_unlock(&counter->child_mutex);
1710 1721
1711 if (count < n * sizeof(u64)) 1722 if (count < n * sizeof(u64))
@@ -1812,8 +1823,6 @@ static int perf_counter_period(struct perf_counter *counter, u64 __user *arg)
1812 1823
1813 counter->attr.sample_freq = value; 1824 counter->attr.sample_freq = value;
1814 } else { 1825 } else {
1815 perf_log_period(counter, value);
1816
1817 counter->attr.sample_period = value; 1826 counter->attr.sample_period = value;
1818 counter->hw.sample_period = value; 1827 counter->hw.sample_period = value;
1819 } 1828 }
@@ -2662,6 +2671,9 @@ static void perf_counter_output(struct perf_counter *counter, int nmi,
2662 if (sample_type & PERF_SAMPLE_ID) 2671 if (sample_type & PERF_SAMPLE_ID)
2663 header.size += sizeof(u64); 2672 header.size += sizeof(u64);
2664 2673
2674 if (sample_type & PERF_SAMPLE_STREAM_ID)
2675 header.size += sizeof(u64);
2676
2665 if (sample_type & PERF_SAMPLE_CPU) { 2677 if (sample_type & PERF_SAMPLE_CPU) {
2666 header.size += sizeof(cpu_entry); 2678 header.size += sizeof(cpu_entry);
2667 2679
@@ -2705,7 +2717,13 @@ static void perf_counter_output(struct perf_counter *counter, int nmi,
2705 if (sample_type & PERF_SAMPLE_ADDR) 2717 if (sample_type & PERF_SAMPLE_ADDR)
2706 perf_output_put(&handle, data->addr); 2718 perf_output_put(&handle, data->addr);
2707 2719
2708 if (sample_type & PERF_SAMPLE_ID) 2720 if (sample_type & PERF_SAMPLE_ID) {
2721 u64 id = primary_counter_id(counter);
2722
2723 perf_output_put(&handle, id);
2724 }
2725
2726 if (sample_type & PERF_SAMPLE_STREAM_ID)
2709 perf_output_put(&handle, counter->id); 2727 perf_output_put(&handle, counter->id);
2710 2728
2711 if (sample_type & PERF_SAMPLE_CPU) 2729 if (sample_type & PERF_SAMPLE_CPU)
@@ -2728,7 +2746,7 @@ static void perf_counter_output(struct perf_counter *counter, int nmi,
2728 if (sub != counter) 2746 if (sub != counter)
2729 sub->pmu->read(sub); 2747 sub->pmu->read(sub);
2730 2748
2731 group_entry.id = sub->id; 2749 group_entry.id = primary_counter_id(sub);
2732 group_entry.counter = atomic64_read(&sub->count); 2750 group_entry.counter = atomic64_read(&sub->count);
2733 2751
2734 perf_output_put(&handle, group_entry); 2752 perf_output_put(&handle, group_entry);
@@ -2788,15 +2806,8 @@ perf_counter_read_event(struct perf_counter *counter,
2788 } 2806 }
2789 2807
2790 if (counter->attr.read_format & PERF_FORMAT_ID) { 2808 if (counter->attr.read_format & PERF_FORMAT_ID) {
2791 u64 id;
2792
2793 event.header.size += sizeof(u64); 2809 event.header.size += sizeof(u64);
2794 if (counter->parent) 2810 event.format[i++] = primary_counter_id(counter);
2795 id = counter->parent->id;
2796 else
2797 id = counter->id;
2798
2799 event.format[i++] = id;
2800 } 2811 }
2801 2812
2802 ret = perf_output_begin(&handle, counter, event.header.size, 0, 0); 2813 ret = perf_output_begin(&handle, counter, event.header.size, 0, 0);
@@ -3191,49 +3202,6 @@ void __perf_counter_mmap(struct vm_area_struct *vma)
3191} 3202}
3192 3203
3193/* 3204/*
3194 * Log sample_period changes so that analyzing tools can re-normalize the
3195 * event flow.
3196 */
3197
3198struct freq_event {
3199 struct perf_event_header header;
3200 u64 time;
3201 u64 id;
3202 u64 period;
3203};
3204
3205static void perf_log_period(struct perf_counter *counter, u64 period)
3206{
3207 struct perf_output_handle handle;
3208 struct freq_event event;
3209 int ret;
3210
3211 if (counter->hw.sample_period == period)
3212 return;
3213
3214 if (counter->attr.sample_type & PERF_SAMPLE_PERIOD)
3215 return;
3216
3217 event = (struct freq_event) {
3218 .header = {
3219 .type = PERF_EVENT_PERIOD,
3220 .misc = 0,
3221 .size = sizeof(event),
3222 },
3223 .time = sched_clock(),
3224 .id = counter->id,
3225 .period = period,
3226 };
3227
3228 ret = perf_output_begin(&handle, counter, sizeof(event), 1, 0);
3229 if (ret)
3230 return;
3231
3232 perf_output_put(&handle, event);
3233 perf_output_end(&handle);
3234}
3235
3236/*
3237 * IRQ throttle logging 3205 * IRQ throttle logging
3238 */ 3206 */
3239 3207
@@ -3246,14 +3214,16 @@ static void perf_log_throttle(struct perf_counter *counter, int enable)
3246 struct perf_event_header header; 3214 struct perf_event_header header;
3247 u64 time; 3215 u64 time;
3248 u64 id; 3216 u64 id;
3217 u64 stream_id;
3249 } throttle_event = { 3218 } throttle_event = {
3250 .header = { 3219 .header = {
3251 .type = PERF_EVENT_THROTTLE + 1, 3220 .type = PERF_EVENT_THROTTLE + 1,
3252 .misc = 0, 3221 .misc = 0,
3253 .size = sizeof(throttle_event), 3222 .size = sizeof(throttle_event),
3254 }, 3223 },
3255 .time = sched_clock(), 3224 .time = sched_clock(),
3256 .id = counter->id, 3225 .id = primary_counter_id(counter),
3226 .stream_id = counter->id,
3257 }; 3227 };
3258 3228
3259 ret = perf_output_begin(&handle, counter, sizeof(throttle_event), 1, 0); 3229 ret = perf_output_begin(&handle, counter, sizeof(throttle_event), 1, 0);