Commit 9b51f66d authored by Ingo Molnar's avatar Ingo Molnar

perfcounters: implement "counter inheritance"

Impact: implement new performance feature

Counter inheritance can be used to run performance counters in a workload,
transparently - and pipe back the counter results to the parent counter.

Inheritance for performance counters works the following way: when creating
a counter it can be marked with the .inherit=1 flag. Such counters are then
'inherited' by all child tasks (be they fork()-ed or clone()-ed). These
counters get inherited through exec() boundaries as well (except through
setuid boundaries).

The counter values get added back to the parent counter(s) when the child
task(s) exit - much like stime/utime statistics are gathered. So inherited
counters are ideal to gather summary statistics about an application's
behavior via shell commands, without having to modify that application.

The timec.c command utilizes counter inheritance:

  http://redhat.com/~mingo/perfcounters/timec.c

Sample output:

   $ ./timec -e 1 -e 3 -e 5 ls -lR /usr/include/ >/dev/null

   Performance counter stats for 'ls':

           163516953 instructions
                2295 cache-misses
             2855182 branch-misses
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent ee06094f
...@@ -78,7 +78,8 @@ struct perf_counter_hw_event { ...@@ -78,7 +78,8 @@ struct perf_counter_hw_event {
u32 disabled : 1, /* off by default */ u32 disabled : 1, /* off by default */
nmi : 1, /* NMI sampling */ nmi : 1, /* NMI sampling */
raw : 1, /* raw event type */ raw : 1, /* raw event type */
__reserved_1 : 29; inherit : 1, /* children inherit it */
__reserved_1 : 28;
u64 __reserved_2; u64 __reserved_2;
}; };
...@@ -138,6 +139,8 @@ enum perf_counter_active_state { ...@@ -138,6 +139,8 @@ enum perf_counter_active_state {
PERF_COUNTER_STATE_ACTIVE = 1, PERF_COUNTER_STATE_ACTIVE = 1,
}; };
struct file;
/** /**
* struct perf_counter - performance counter kernel representation: * struct perf_counter - performance counter kernel representation:
*/ */
...@@ -156,7 +159,10 @@ struct perf_counter { ...@@ -156,7 +159,10 @@ struct perf_counter {
struct perf_counter_context *ctx; struct perf_counter_context *ctx;
struct task_struct *task; struct task_struct *task;
struct file *filp;
unsigned int nr_inherited;
struct perf_counter *parent;
/* /*
* Protect attach/detach: * Protect attach/detach:
*/ */
...@@ -210,13 +216,16 @@ struct perf_cpu_context { ...@@ -210,13 +216,16 @@ struct perf_cpu_context {
extern int perf_max_counters; extern int perf_max_counters;
#ifdef CONFIG_PERF_COUNTERS #ifdef CONFIG_PERF_COUNTERS
extern void
perf_counter_show(struct perf_counter *counter, char *str, int trace);
extern const struct hw_perf_counter_ops * extern const struct hw_perf_counter_ops *
hw_perf_counter_init(struct perf_counter *counter); hw_perf_counter_init(struct perf_counter *counter);
extern void perf_counter_task_sched_in(struct task_struct *task, int cpu); extern void perf_counter_task_sched_in(struct task_struct *task, int cpu);
extern void perf_counter_task_sched_out(struct task_struct *task, int cpu); extern void perf_counter_task_sched_out(struct task_struct *task, int cpu);
extern void perf_counter_task_tick(struct task_struct *task, int cpu); extern void perf_counter_task_tick(struct task_struct *task, int cpu);
extern void perf_counter_init_task(struct task_struct *task); extern void perf_counter_init_task(struct task_struct *child);
extern void perf_counter_exit_task(struct task_struct *child);
extern void perf_counter_notify(struct pt_regs *regs); extern void perf_counter_notify(struct pt_regs *regs);
extern void perf_counter_print_debug(void); extern void perf_counter_print_debug(void);
extern u64 hw_perf_save_disable(void); extern u64 hw_perf_save_disable(void);
...@@ -226,12 +235,15 @@ extern int perf_counter_task_enable(void); ...@@ -226,12 +235,15 @@ extern int perf_counter_task_enable(void);
#else #else
static inline void static inline void
perf_counter_show(struct perf_counter *counter, char *str, int trace) { }
static inline void
perf_counter_task_sched_in(struct task_struct *task, int cpu) { } perf_counter_task_sched_in(struct task_struct *task, int cpu) { }
static inline void static inline void
perf_counter_task_sched_out(struct task_struct *task, int cpu) { } perf_counter_task_sched_out(struct task_struct *task, int cpu) { }
static inline void static inline void
perf_counter_task_tick(struct task_struct *task, int cpu) { } perf_counter_task_tick(struct task_struct *task, int cpu) { }
static inline void perf_counter_init_task(struct task_struct *task) { } static inline void perf_counter_init_task(struct task_struct *child) { }
static inline void perf_counter_exit_task(struct task_struct *child) { }
static inline void perf_counter_notify(struct pt_regs *regs) { } static inline void perf_counter_notify(struct pt_regs *regs) { }
static inline void perf_counter_print_debug(void) { } static inline void perf_counter_print_debug(void) { }
static inline void hw_perf_restore(u64 ctrl) { } static inline void hw_perf_restore(u64 ctrl) { }
......
...@@ -1093,11 +1093,12 @@ NORET_TYPE void do_exit(long code) ...@@ -1093,11 +1093,12 @@ NORET_TYPE void do_exit(long code)
mpol_put(tsk->mempolicy); mpol_put(tsk->mempolicy);
tsk->mempolicy = NULL; tsk->mempolicy = NULL;
#endif #endif
#ifdef CONFIG_FUTEX
/* /*
* This must happen late, after the PID is not * These must happen late, after the PID is not
* hashed anymore: * hashed anymore, but still at a point that may sleep:
*/ */
perf_counter_exit_task(tsk);
#ifdef CONFIG_FUTEX
if (unlikely(!list_empty(&tsk->pi_state_list))) if (unlikely(!list_empty(&tsk->pi_state_list)))
exit_pi_state_list(tsk); exit_pi_state_list(tsk);
if (unlikely(current->pi_state_cache)) if (unlikely(current->pi_state_cache))
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment