perf_counter: fix the group mess
cover perf_counter: fix the group mess
With these two patches the below proglet gives:
# ./test EVNT: 0xffffffff811c0f4c scale: nan ID: 37 CNT: 1006180 ID: 38 CNT: 1010230 ID: 39 CNT: 1010332 ID: 40 CNT: 1010420
I can't seem to catch the SIGIO thing, but at least all the counters report more or less the same value. Also, that scale stuff doesn't seem to quite work yet.. I'll be poking at that too.
---
#include "perf.h"
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
void work(void)
{
int i;
for (i = 0; i < 1000000000; i++) {
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
}
}unsigned long page_size;
int fd = -1, fd1 = 0;pid_t me;
void *output;
void handle_sigio(int sig)
{
printf("signal\n"); // not signal safe
fflush(stdout);
ioctl(fd, PERF_COUNTER_IOC_REFRESH, 1);
}static unsigned long mmap_read_head(void)
{
struct perf_counter_mmap_page *pc = output;
long head;
head = pc->data_head;
rmb();
return head;
}static void *mmap_read_base(void)
{
return output + page_size;
}struct event {
struct perf_event_header header;
u64 ip;
u64 nr;
u64 time_enabled;
u64 time_running;
struct {
u64 val;
u64 id;
} cnt[0];
};int main(int argc, char **argv)
{
struct perf_counter_attr attr;
unsigned long offset = 0, head;
int err, i;
page_size = sysconf(_SC_PAGE_SIZE);
me = getpid();
memset(&attr, 0, sizeof(attr));
attr.type = PERF_TYPE_HARDWARE;
attr.config = PERF_COUNT_HW_CPU_CYCLES;
attr.sample_period = 1000000;
attr.sample_type = PERF_SAMPLE_IP |
PERF_SAMPLE_READ;
attr.read_format = PERF_FORMAT_TOTAL_TIME_RUNNING |
PERF_FORMAT_TOTAL_TIME_ENABLED |
PERF_FORMAT_ID |
PERF_FORMAT_GROUP;
attr.disabled = 1;
attr.wakeup_events = 1;
fd = sys_perf_counter_open(&attr, me, -1, fd, 0);
if (fd <= 0) {
perror("FAIL fd: ");
exit(-1);
}
attr.sample_period = 0;
attr.disabled = 0;
for (i = 0; i < 3; i++) {
fd1 = sys_perf_counter_open(&attr, me, -1, fd, 0);
if (fd1 <= 0) {
perror("FAIL fd1: ");
exit(-1);
}
}
signal(SIGIO, handle_sigio);
err = fcntl(fd, F_SETOWN, me);
if (err == -1) {
perror("FAIL fcntl: ");
exit(-1);
}
output = mmap(NULL, page_size * 3, PROT_READ, MAP_SHARED, fd, 0);
if (output == ((void *)-1)) {
perror("FAIL mmap:");
exit(-1);
}
ioctl(fd, PERF_COUNTER_IOC_REFRESH, 1);
work();
ioctl(fd, PERF_COUNTER_IOC_DISABLE, 0);
head = mmap_read_head();
for (; offset < head; ) {
struct event *evnt = mmap_read_base() + offset;
offset += evnt->header.size;
printf("EVNT: %p scale: %f ", (void *)evnt->ip,
((double)evnt->time_running)/evnt->time_enabled
);
for (i = 0; i < evnt->nr; i++) {
printf("ID: %Lu CNT: %Lu ",
evnt->cnt[i].id, evnt->cnt[i].val);
}
printf("\n");
}
return 0;
}PATCH 1/2 perf: rework 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.
--- include/linux/perf_counter.h | 36 +++++-- kernel/perf_counter.c | 213 +++++++++++++++++++++++++++++-------------- 2 files changed, 172 insertions(+), 77 deletions(-)
include/linux/perf_counter.h +28 -8
@@ -115,7 +115,7 @@ enum perf_counter_sample_format {PERF_SAMPLE_TID =1U <<1 ,PERF_SAMPLE_TIME =1U <<2 ,PERF_SAMPLE_ADDR =1U <<3 , -PERF_SAMPLE_GROUP =1U <<4 , +PERF_SAMPLE_READ =1U <<4 ,PERF_SAMPLE_CALLCHAIN =1U <<5 ,PERF_SAMPLE_ID =1U <<6 , PERF_SAMPLE_CPU = 1U << 7,
@@ -130,13 +130,25 @@ enum perf_counter_sample_format {* Bits that can be set in attr.read_format to request that * reads on the counter should return the indicated quantities, * in increasing order of bit value, after the counter value. +* +* struct { +* { u64 nr; } && PERF_FORMAT_GROUP +* { u64 time_enabled; } && PERF_FORMAT_ENABLED +* { u64 time_running; } && PERF_FORMAT_RUNNING +* { u64 value; +* { u64 id; } && PERF_FORMAT_ID +* } cntr[nr]; +* }; +* +* Where 'nr' defaults to 1 when !PERF_FORMAT_GROUP */ enum perf_counter_read_format {PERF_FORMAT_TOTAL_TIME_ENABLED =1U <<0 ,PERF_FORMAT_TOTAL_TIME_RUNNING =1U <<1 ,PERF_FORMAT_ID =1U <<2 , +PERF_FORMAT_GROUP =1U <<3 , -PERF_FORMAT_MAX =1U <<3 ,/* non-ABI */ +PERF_FORMAT_MAX =1U <<4 ,/* non-ABI */ };#define PERF_ATTR_SIZE_VER0 64/* sizeof first published struct */
@@ -343,10 +355,13 @@ enum perf_event_type {* struct { * struct perf_event_header header; * u32 pid, tid; -* u64 value; -* { u64 time_enabled; } && PERF_FORMAT_ENABLED -* { u64 time_running; } && PERF_FORMAT_RUNNING -* { u64 parent_id; } && PERF_FORMAT_ID +* +* { u64 nr; } && PERF_FORMAT_GROUP +* { u64 value; +* { u64 time_enabled; } && PERF_FORMAT_ENABLED +* { u64 time_running; } && PERF_FORMAT_RUNNING +* { u64 parent_id; } && PERF_FORMAT_ID +* } cntr[nr]; * }; */ PERF_EVENT_READ = 8,
@@ -364,11 +379,16 @@ enum perf_event_type {* { u32 cpu, res; } && PERF_SAMPLE_CPU * { u64 period; } && PERF_SAMPLE_PERIOD * -* { u64 nr; -* { u64 id, val; } cnt[nr]; } && PERF_SAMPLE_GROUP +* { { u64 nr; } && PERF_FORMAT_GROUP +* { u64 value; +* { u64 time_enabled; } && PERF_FORMAT_ENABLED +* { u64 time_running; } && PERF_FORMAT_RUNNING +* { u64 id; } && PERF_FORMAT_ID +* } cntr[nr]; } && PERF_SAMPLE_READ * * { u64 nr, * u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN +* * { u32 size; * char data[size];}&& PERF_SAMPLE_RAW * };
kernel/perf_counter.c +144 -69
295 lines not on this page · raw message
PATCH 2/2 perf_counter: Fix an ipi-deadlock
In reply to Peter Zijlstraperf_pending_counter() is called from IRQ context and will call perf_counter_disable(), however perf_counter_disable() uses smp_call_function_single() which doesn't fancy being used with IRQs disabled due to IPI deadlocks.
Fix this by making it use the local __perf_counter_disable() call and teaching the counter_sched_out() code about pending disables as well.
This should cover the case where a counter migrates before the pending queue gets processed.
--- kernel/perf_counter.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
kernel/perf_counter.c +5 -1
@@ -307,6 +307,10 @@ counter_sched_out(struct perf_counter *creturn ; counter->state =PERF_COUNTER_STATE_INACTIVE ; +if (counter->pending_disable) { + counter->pending_disable =0 ; + counter->state =PERF_COUNTER_STATE_OFF ; + } counter->tstamp_stopped = ctx->time; counter->pmu->disable (counter); counter->oncpu =-1 ;
@@ -2315,7 +2319,7 @@ static void perf_pending_counter(structif (counter->pending_disable) { counter->pending_disable =0 ; -perf_counter_disable (counter); +__perf_counter_disable (counter); }if (counter->pending_wakeup) {
Quoted 7 lines · Peter Zijlstra
With these two patches the below proglet gives:
# ./test EVNT: 0xffffffff811c0f4c scale: nan ID: 37 CNT: 1006180 ID: 38 CNT: 1010230 ID: 39 CNT: 1010332 ID: 40 CNT: 1010420I can't seem to catch the SIGIO thing, but at least all the counters report
I think it's because you are missing this fcntl() call to get async notification:
ret = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_ASYNC);
if (ret == -1)
err(1, "cannot set ASYNC");Quoted 7 lines · stephane eranian
Quoted 6 lines · Peter Zijlstra
With these two patches the below proglet gives:
# ./test EVNT: 0xffffffff811c0f4c scale: nan ID: 37 CNT: 1006180 ID: 38 CNT: 1010230 ID: 39 CNT: 1010332 ID: 40 CNT: 1010420
Quoted 8 lines
Quoted 1 line
I can't seem to catch the SIGIO thing, but at least all the counters report
I think it's because you are missing this
fcntl()call to get async notification:ret = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_ASYNC); if (ret == -1) err(1, "cannot set ASYNC");
Ah, indeed, thanks!
(with a reduced work function, or increased period)
# gcc -o test test.c; ./test signal signal signal signal signal signal EVNT: 0x40091c scale: nan ID: 49 CNT: 1006676 ID: 50 CNT: 1010796 ID: 51 CNT: 1010960 ID: 52 CNT: 1010937 EVNT: 0x400928 scale: 1.000000 ID: 49 CNT: 2003019 ID: 50 CNT: 2010882 ID: 51 CNT: 2011475 ID: 52 CNT: 2012032 EVNT: 0x400928 scale: 1.000000 ID: 49 CNT: 3002707 ID: 50 CNT: 3015571 ID: 51 CNT: 3016714 ID: 52 CNT: 3017830 EVNT: 0x400916 scale: 1.000000 ID: 49 CNT: 4002677 ID: 50 CNT: 4020137 ID: 51 CNT: 4021880 ID: 52 CNT: 4023639 EVNT: 0x40091c scale: 1.000000 ID: 49 CNT: 5002749 ID: 50 CNT: 5024707 ID: 51 CNT: 5027078 ID: 52 CNT: 5029378 EVNT: 0x40091c scale: 1.000000 ID: 49 CNT: 6002799 ID: 50 CNT: 6029452 ID: 51 CNT: 6032339 ID: 52 CNT: 6035277
Peter Zijlstra
maintainer[PATCH 3/2] perf tools: Fixup read ABI breakage
In reply to Peter ZijlstraSubject: perf tools: Fixup read ABI breakage
From: Peter Zijlstra <[email protected]>
Date: Thu Aug 13 09:03:33 CEST 2009
Update the perf tool to the new read output format.
--- tools/perf/builtin-record.c | 2 +- tools/perf/builtin-stat.c | 8 +++++--- tools/perf/util/event.h | 10 ++++++++-- 3 files changed, 14 insertions(+), 6 deletions(-)
tools/perf/builtin-record.c +1 -1
@@ -370,9 +370,9 @@ static void create_counter(int counter,struct perf_header_attr *h_attr;int track = !counter;/* only the first counter needs these */ struct { -u64 count;u64 time_enabled;u64 time_running; +u64 count;u64 id; } read_data;
tools/perf/builtin-stat.c +5 -3
@@ -168,10 +168,12 @@ static void read_counter(int counter)close (fd[cpu][counter]); fd[cpu][counter] =-1 ; - count[0 ] += single_count[0 ];if (scale) { - count[1 ] += single_count[1 ]; - count[2 ] += single_count[2 ]; + count[0 ] += single_count[2 ];/* value */ + count[1 ] += single_count[0 ];/* enabled */ + count[2 ] += single_count[1 ];/* running */ + }else { + count[0 ] += single_count[0 ];/* value */ } }
tools/perf/util/event.h +8 -2
@@ -4,6 +4,9 @@#include "util.h" #include <linux/list.h> +/* +* PERF_SAMPLE_IP | PERF_SAMPLE_TID | * +*/ struct ip_event {struct perf_event_header header;u64 ip;
@@ -38,12 +41,15 @@ struct lost_event {u64 lost; }; -struct read_event { +/* +* PERF_FORMAT_ENABLED | PERF_FORMAT_RUNNING | PERF_FORMAT_ID +*/ +struct read_event {struct perf_event_header header;u32 pid,tid; -u64 value;u64 time_enabled;u64 time_running; +u64 value;u64 id; };
Peter Zijlstra
maintainer[PATCH 4/2] perf_counter: Fix swcounter context invariance
In reply to Peter ZijlstraNot really related to this topic, but it needs posting anyway.
---
Subject: perf_counter: Fix swcounter context invariance
From: Peter Zijlstra <[email protected]>
Date: Fri Aug 07 13:29:13 CEST 2009
perf_swcounter_is_counting() uses a lock, which means we cannot use swcounters from NMI or when holding that particular lock, this is unintended.
The below removes the lock, this opens up race window, but not worse than the swcounters already experience due to RCU traversal of the context in perf_swcounter_ctx_event().
--- kernel/perf_counter.c | 44 ++++++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 26 deletions(-)
kernel/perf_counter.c +18 -26
@@ -3519,40 +3519,32 @@ static void perf_swcounter_add(struct pestatic int perf_swcounter_is_counting (struct perf_counter *counter) { -struct perf_counter_context *ctx; -unsigned long flags; -int count; - +/* +* The counter is active, we're good! +*/ if (counter->state ==PERF_COUNTER_STATE_ACTIVE )return 1 ; +/* +* The counter is off/error, not counting. +*/ if (counter->state !=PERF_COUNTER_STATE_INACTIVE )return 0 ;/* -* If the counter is inactive, it could be just because -* its task is scheduled out, or because it's in a group -* which could not go on the PMU. We want to count in -* the first case but not the second. If the context is -* currently active then an inactive software counter must -* be the second case. If it's not currently active then -* we need to know whether the counter was active when the -* context was last active, which we can determine by -* comparing counter->tstamp_stopped with ctx->time. -* -* We are within an RCU read-side critical section, -* which protects the existence of *ctx. +* The counter is inactive, if the context is active +* we're part of a group that didn't make it on the 'pmu', +* not counting. */ - ctx = counter->ctx; -spin_lock_irqsave (&ctx->lock, flags); - count =1 ; -/* Re-check state now we have the lock */ -if (counter->state <PERF_COUNTER_STATE_INACTIVE || - counter->ctx->is_active || - counter->tstamp_stopped < ctx->time) - count =0 ; -spin_unlock_irqrestore (&ctx->lock, flags); -return count; +if (counter->ctx->is_active) +return 0 ; + +/* +* We're inactive and the context is too, this means the +* task is scheduled out, we're counting events that happen +* to us, like migration events. +*/ +return 1 ; } static int perf_swcounter_match(struct perf_counter *counter,
Re: [PATCH 4/2] perf_counter: Fix swcounter context invariance
Quoted 12 lines · Peter Zijlstra
Not really related to this topic, but it needs posting anyway.
---
Subject:
perf_counter: Fix swcounter context invariance
From: Peter Zijlstra <[email protected]>
Date: Fri Aug 07 13:29:13 CEST 2009
perf_swcounter_is_counting()uses a lock, which means we cannot use swcounters from NMI or when holding that particular lock, this is unintended.
Quoted 6 lines
The below removes the lock, this opens up race window, but not worse than the swcounters already experience due to RCU traversal of the context in
perf_swcounter_ctx_event().Cc Paul Mackerras <[email protected]>Signed-off-by Peter Zijlstra <[email protected]>
As a side effect, it's possible this also fixes the hard lockups while opening a lockdep tracepoint counter.
Quoted 73 lines
--- kernel/perf_counter.c | 44 ++++++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 26 deletions(-)Index:
linux-2.6/kernel/perf_counter.c
===================================================================kernel/perf_counter.c +18
-27@@ -3519,40 +3519,32 @@ static void perf_swcounter_add(struct pestatic int perf_swcounter_is_counting (struct perf_counter *counter) { -struct perf_counter_context *ctx; -unsigned long flags; -int count; - +/* +* The counter is active, we're good! +*/ if (counter->state ==PERF_COUNTER_STATE_ACTIVE )return 1 ; +/* +* The counter is off/error, not counting. +*/ if (counter->state !=PERF_COUNTER_STATE_INACTIVE )return 0 ;/* -* If the counter is inactive, it could be just because -* its task is scheduled out, or because it's in a group -* which could not go on the PMU. We want to count in -* the first case but not the second. If the context is -* currently active then an inactive software counter must -* be the second case. If it's not currently active then -* we need to know whether the counter was active when the -* context was last active, which we can determine by -* comparing counter->tstamp_stopped with ctx->time. -* -* We are within an RCU read-side critical section, -* which protects the existence of *ctx. +* The counter is inactive, if the context is active +* we're part of a group that didn't make it on the 'pmu', +* not counting. */ - ctx = counter->ctx; -spin_lock_irqsave (&ctx->lock, flags); - count =1 ; -/* Re-check state now we have the lock */ -if (counter->state <PERF_COUNTER_STATE_INACTIVE || - counter->ctx->is_active || - counter->tstamp_stopped < ctx->time) - count =0 ; -spin_unlock_irqrestore (&ctx->lock, flags); -return count; +if (counter->ctx->is_active) +return 0 ; + +/* +* We're inactive and the context is too, this means the +* task is scheduled out, we're counting events that happen +* to us, like migration events. +*/ +return 1 ; } static int perf_swcounter_match(struct perf_counter *counter, --To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Peter Zijlstra
maintainerRe: [PATCH 4/2] perf_counter: Fix swcounter context invariance
Quoted 17 lines · Frederic Weisbecker
Quoted 16 lines · Peter Zijlstra
Not really related to this topic, but it needs posting anyway.
---
Subject:
perf_counter: Fix swcounter context invariance
From: Peter Zijlstra <[email protected]>
Date: Fri Aug 07 13:29:13 CEST 2009
perf_swcounter_is_counting()uses a lock, which means we cannot use swcounters from NMI or when holding that particular lock, this is unintended.The below removes the lock, this opens up race window, but not worse than the swcounters already experience due to RCU traversal of the context in
perf_swcounter_ctx_event().
Quoted 7 lines
Quoted 2 lines
Cc Paul Mackerras <[email protected]>Signed-off-by Peter Zijlstra <[email protected]>As a side effect, it's possible this also fixes the hard lockups while opening a lockdep tracepoint counter.
It will -- not a side-effect at all, intended consequence :-)
Re: [PATCH 4/2] perf_counter: Fix swcounter context invariance
Quoted 23 lines · Peter Zijlstra
Quoted 22 lines · Frederic Weisbecker
Quoted 18 lines · Peter Zijlstra
Not really related to this topic, but it needs posting anyway.
---
Subject:
perf_counter: Fix swcounter context invariance
From: Peter Zijlstra <[email protected]>
Date: Fri Aug 07 13:29:13 CEST 2009
perf_swcounter_is_counting()uses a lock, which means we cannot use swcounters from NMI or when holding that particular lock, this is unintended.The below removes the lock, this opens up race window, but not worse than the swcounters already experience due to RCU traversal of the context in
perf_swcounter_ctx_event().Cc Paul Mackerras <[email protected]>Signed-off-by Peter Zijlstra <[email protected]>
Quoted 4 lines
Quoted 2 lines
As a side effect, it's possible this also fixes the hard lockups while opening a lockdep tracepoint counter.
It will -- not a side-effect at all, intended consequence :-)
Great then! Important fix for .31 :-)
tip-bot for Peter Zijlstra
maintainer[tip:perfcounters/urgent] perf_counter: Fix swcounter context invariance
In reply to Peter ZijlstraCommit-ID: bcfc2602e8541ac13b1def38e2591dca072cff7a Gitweb: http://git.kernel.org/tip/bcfc2602e8541ac13b1def38e2591dca072cff7a Author: Peter Zijlstra <[email protected]> AuthorDate: Thu, 13 Aug 2009 09:51:55 +0200 Committer: Ingo Molnar <[email protected]> CommitDate: Thu, 13 Aug 2009 12:18:43 +0200
perf_counter: Fix swcounter context invariance
perf_swcounter_is_counting() uses a lock, which means we cannot use swcounters from NMI or when holding that particular lock, this is unintended.
The below removes the lock, this opens up race window, but not worse than the swcounters already experience due to RCU traversal of the context in perf_swcounter_ctx_event().
This also fixes the hard lockups while opening a lockdep tracepoint counter.
LKML-Reference: <1250149915.10001.66.camel@twins>
--- kernel/perf_counter.c | 44 ++++++++++++++++++-------------------------- 1 files changed, 18 insertions(+), 26 deletions(-)
kernel/perf_counter.c +18 -26
@@ -3444,40 +3444,32 @@ static void perf_swcounter_add(struct perf_counter *counter, u64 nr,static int perf_swcounter_is_counting (struct perf_counter *counter) { -struct perf_counter_context *ctx; -unsigned long flags; -int count; - +/* +* The counter is active, we're good! +*/ if (counter->state ==PERF_COUNTER_STATE_ACTIVE )return 1 ; +/* +* The counter is off/error, not counting. +*/ if (counter->state !=PERF_COUNTER_STATE_INACTIVE )return 0 ;/* -* If the counter is inactive, it could be just because -* its task is scheduled out, or because it's in a group -* which could not go on the PMU. We want to count in -* the first case but not the second. If the context is -* currently active then an inactive software counter must -* be the second case. If it's not currently active then -* we need to know whether the counter was active when the -* context was last active, which we can determine by -* comparing counter->tstamp_stopped with ctx->time. -* -* We are within an RCU read-side critical section, -* which protects the existence of *ctx. +* The counter is inactive, if the context is active +* we're part of a group that didn't make it on the 'pmu', +* not counting. */ - ctx = counter->ctx; -spin_lock_irqsave (&ctx->lock, flags); - count =1 ; -/* Re-check state now we have the lock */ -if (counter->state <PERF_COUNTER_STATE_INACTIVE || - counter->ctx->is_active || - counter->tstamp_stopped < ctx->time) - count =0 ; -spin_unlock_irqrestore (&ctx->lock, flags); -return count; +if (counter->ctx->is_active) +return 0 ; + +/* +* We're inactive and the context is too, this means the +* task is scheduled out, we're counting events that happen +* to us, like migration events. +*/ +return 1 ; } static int perf_swcounter_match(struct perf_counter *counter,