Commit a40694a3 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'perfcounters-fixes-for-linus' of...

Merge branch 'perfcounters-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip

* 'perfcounters-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
  perf_counter: Set the CONFIG_PERF_COUNTERS default to y if CONFIG_PROFILING=y
  perf: Fix read buffer overflow
  perf top: Add mwait_idle_with_hints to skip_symbols[]
  perf tools: Fix faulty check
  perf report: Update for the new FORK/EXIT events
  perf_counter: Full task tracing
  perf_counter: Collapse inherit on read()
  tracing, perf_counter: Add help text to CONFIG_EVENT_PROFILE
  perf_counter tools: Fix link errors with older toolchains
parents ea563424 f2654260
...@@ -181,8 +181,9 @@ struct perf_counter_attr { ...@@ -181,8 +181,9 @@ struct perf_counter_attr {
freq : 1, /* use freq, not period */ freq : 1, /* use freq, not period */
inherit_stat : 1, /* per task counts */ inherit_stat : 1, /* per task counts */
enable_on_exec : 1, /* next exec enables */ enable_on_exec : 1, /* next exec enables */
task : 1, /* trace fork/exit */
__reserved_1 : 51; __reserved_1 : 50;
__u32 wakeup_events; /* wakeup every n events */ __u32 wakeup_events; /* wakeup every n events */
__u32 __reserved_2; __u32 __reserved_2;
...@@ -308,6 +309,15 @@ enum perf_event_type { ...@@ -308,6 +309,15 @@ enum perf_event_type {
*/ */
PERF_EVENT_COMM = 3, PERF_EVENT_COMM = 3,
/*
* struct {
* struct perf_event_header header;
* u32 pid, ppid;
* u32 tid, ptid;
* };
*/
PERF_EVENT_EXIT = 4,
/* /*
* struct { * struct {
* struct perf_event_header header; * struct perf_event_header header;
...@@ -323,6 +333,7 @@ enum perf_event_type { ...@@ -323,6 +333,7 @@ enum perf_event_type {
* struct { * struct {
* struct perf_event_header header; * struct perf_event_header header;
* u32 pid, ppid; * u32 pid, ppid;
* u32 tid, ptid;
* }; * };
*/ */
PERF_EVENT_FORK = 7, PERF_EVENT_FORK = 7,
......
...@@ -940,6 +940,7 @@ menu "Performance Counters" ...@@ -940,6 +940,7 @@ menu "Performance Counters"
config PERF_COUNTERS config PERF_COUNTERS
bool "Kernel Performance Counters" bool "Kernel Performance Counters"
default y if PROFILING
depends on HAVE_PERF_COUNTERS depends on HAVE_PERF_COUNTERS
select ANON_INODES select ANON_INODES
help help
...@@ -961,9 +962,17 @@ config PERF_COUNTERS ...@@ -961,9 +962,17 @@ config PERF_COUNTERS
Say Y if unsure. Say Y if unsure.
config EVENT_PROFILE config EVENT_PROFILE
bool "Tracepoint profile sources" bool "Tracepoint profiling sources"
depends on PERF_COUNTERS && EVENT_TRACING depends on PERF_COUNTERS && EVENT_TRACING
default y default y
help
Allow the use of tracepoints as software performance counters.
When this is enabled, you can create perf counters based on
tracepoints using PERF_TYPE_TRACEPOINT and the tracepoint ID
found in debugfs://tracing/events/*/*/id. (The -e/--events
option to the perf tool can parse and interpret symbolic
tracepoints, in the subsystem:tracepoint_name format.)
endmenu endmenu
......
...@@ -1269,6 +1269,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, ...@@ -1269,6 +1269,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
write_unlock_irq(&tasklist_lock); write_unlock_irq(&tasklist_lock);
proc_fork_connector(p); proc_fork_connector(p);
cgroup_post_fork(p); cgroup_post_fork(p);
perf_counter_fork(p);
return p; return p;
bad_fork_free_pid: bad_fork_free_pid:
...@@ -1410,9 +1411,6 @@ long do_fork(unsigned long clone_flags, ...@@ -1410,9 +1411,6 @@ long do_fork(unsigned long clone_flags,
init_completion(&vfork); init_completion(&vfork);
} }
if (!(clone_flags & CLONE_THREAD))
perf_counter_fork(p);
audit_finish_fork(p); audit_finish_fork(p);
tracehook_report_clone(regs, clone_flags, nr, p); tracehook_report_clone(regs, clone_flags, nr, p);
......
...@@ -42,6 +42,7 @@ static int perf_overcommit __read_mostly = 1; ...@@ -42,6 +42,7 @@ static int perf_overcommit __read_mostly = 1;
static atomic_t nr_counters __read_mostly; static atomic_t nr_counters __read_mostly;
static atomic_t nr_mmap_counters __read_mostly; static atomic_t nr_mmap_counters __read_mostly;
static atomic_t nr_comm_counters __read_mostly; static atomic_t nr_comm_counters __read_mostly;
static atomic_t nr_task_counters __read_mostly;
/* /*
* perf counter paranoia level: * perf counter paranoia level:
...@@ -1654,6 +1655,8 @@ static void free_counter(struct perf_counter *counter) ...@@ -1654,6 +1655,8 @@ static void free_counter(struct perf_counter *counter)
atomic_dec(&nr_mmap_counters); atomic_dec(&nr_mmap_counters);
if (counter->attr.comm) if (counter->attr.comm)
atomic_dec(&nr_comm_counters); atomic_dec(&nr_comm_counters);
if (counter->attr.task)
atomic_dec(&nr_task_counters);
} }
if (counter->destroy) if (counter->destroy)
...@@ -1688,6 +1691,18 @@ static int perf_release(struct inode *inode, struct file *file) ...@@ -1688,6 +1691,18 @@ static int perf_release(struct inode *inode, struct file *file)
return 0; return 0;
} }
static u64 perf_counter_read_tree(struct perf_counter *counter)
{
struct perf_counter *child;
u64 total = 0;
total += perf_counter_read(counter);
list_for_each_entry(child, &counter->child_list, child_list)
total += perf_counter_read(child);
return total;
}
/* /*
* Read the performance counter - simple non blocking version for now * Read the performance counter - simple non blocking version for now
*/ */
...@@ -1707,7 +1722,7 @@ perf_read_hw(struct perf_counter *counter, char __user *buf, size_t count) ...@@ -1707,7 +1722,7 @@ perf_read_hw(struct perf_counter *counter, char __user *buf, size_t count)
WARN_ON_ONCE(counter->ctx->parent_ctx); WARN_ON_ONCE(counter->ctx->parent_ctx);
mutex_lock(&counter->child_mutex); mutex_lock(&counter->child_mutex);
values[0] = perf_counter_read(counter); values[0] = perf_counter_read_tree(counter);
n = 1; n = 1;
if (counter->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) if (counter->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
values[n++] = counter->total_time_enabled + values[n++] = counter->total_time_enabled +
...@@ -2819,10 +2834,12 @@ perf_counter_read_event(struct perf_counter *counter, ...@@ -2819,10 +2834,12 @@ perf_counter_read_event(struct perf_counter *counter,
} }
/* /*
* fork tracking * task tracking -- fork/exit
*
* enabled by: attr.comm | attr.mmap | attr.task
*/ */
struct perf_fork_event { struct perf_task_event {
struct task_struct *task; struct task_struct *task;
struct { struct {
...@@ -2830,37 +2847,42 @@ struct perf_fork_event { ...@@ -2830,37 +2847,42 @@ struct perf_fork_event {
u32 pid; u32 pid;
u32 ppid; u32 ppid;
u32 tid;
u32 ptid;
} event; } event;
}; };
static void perf_counter_fork_output(struct perf_counter *counter, static void perf_counter_task_output(struct perf_counter *counter,
struct perf_fork_event *fork_event) struct perf_task_event *task_event)
{ {
struct perf_output_handle handle; struct perf_output_handle handle;
int size = fork_event->event.header.size; int size = task_event->event.header.size;
struct task_struct *task = fork_event->task; struct task_struct *task = task_event->task;
int ret = perf_output_begin(&handle, counter, size, 0, 0); int ret = perf_output_begin(&handle, counter, size, 0, 0);
if (ret) if (ret)
return; return;
fork_event->event.pid = perf_counter_pid(counter, task); task_event->event.pid = perf_counter_pid(counter, task);
fork_event->event.ppid = perf_counter_pid(counter, task->real_parent); task_event->event.ppid = perf_counter_pid(counter, task->real_parent);
perf_output_put(&handle, fork_event->event); task_event->event.tid = perf_counter_tid(counter, task);
task_event->event.ptid = perf_counter_tid(counter, task->real_parent);
perf_output_put(&handle, task_event->event);
perf_output_end(&handle); perf_output_end(&handle);
} }
static int perf_counter_fork_match(struct perf_counter *counter) static int perf_counter_task_match(struct perf_counter *counter)
{ {
if (counter->attr.comm || counter->attr.mmap) if (counter->attr.comm || counter->attr.mmap || counter->attr.task)
return 1; return 1;
return 0; return 0;
} }
static void perf_counter_fork_ctx(struct perf_counter_context *ctx, static void perf_counter_task_ctx(struct perf_counter_context *ctx,
struct perf_fork_event *fork_event) struct perf_task_event *task_event)
{ {
struct perf_counter *counter; struct perf_counter *counter;
...@@ -2869,19 +2891,19 @@ static void perf_counter_fork_ctx(struct perf_counter_context *ctx, ...@@ -2869,19 +2891,19 @@ static void perf_counter_fork_ctx(struct perf_counter_context *ctx,
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(counter, &ctx->event_list, event_entry) { list_for_each_entry_rcu(counter, &ctx->event_list, event_entry) {
if (perf_counter_fork_match(counter)) if (perf_counter_task_match(counter))
perf_counter_fork_output(counter, fork_event); perf_counter_task_output(counter, task_event);
} }
rcu_read_unlock(); rcu_read_unlock();
} }
static void perf_counter_fork_event(struct perf_fork_event *fork_event) static void perf_counter_task_event(struct perf_task_event *task_event)
{ {
struct perf_cpu_context *cpuctx; struct perf_cpu_context *cpuctx;
struct perf_counter_context *ctx; struct perf_counter_context *ctx;
cpuctx = &get_cpu_var(perf_cpu_context); cpuctx = &get_cpu_var(perf_cpu_context);
perf_counter_fork_ctx(&cpuctx->ctx, fork_event); perf_counter_task_ctx(&cpuctx->ctx, task_event);
put_cpu_var(perf_cpu_context); put_cpu_var(perf_cpu_context);
rcu_read_lock(); rcu_read_lock();
...@@ -2891,32 +2913,40 @@ static void perf_counter_fork_event(struct perf_fork_event *fork_event) ...@@ -2891,32 +2913,40 @@ static void perf_counter_fork_event(struct perf_fork_event *fork_event)
*/ */
ctx = rcu_dereference(current->perf_counter_ctxp); ctx = rcu_dereference(current->perf_counter_ctxp);
if (ctx) if (ctx)
perf_counter_fork_ctx(ctx, fork_event); perf_counter_task_ctx(ctx, task_event);
rcu_read_unlock(); rcu_read_unlock();
} }
void perf_counter_fork(struct task_struct *task) static void perf_counter_task(struct task_struct *task, int new)
{ {
struct perf_fork_event fork_event; struct perf_task_event task_event;
if (!atomic_read(&nr_comm_counters) && if (!atomic_read(&nr_comm_counters) &&
!atomic_read(&nr_mmap_counters)) !atomic_read(&nr_mmap_counters) &&
!atomic_read(&nr_task_counters))
return; return;
fork_event = (struct perf_fork_event){ task_event = (struct perf_task_event){
.task = task, .task = task,
.event = { .event = {
.header = { .header = {
.type = PERF_EVENT_FORK, .type = new ? PERF_EVENT_FORK : PERF_EVENT_EXIT,
.misc = 0, .misc = 0,
.size = sizeof(fork_event.event), .size = sizeof(task_event.event),
}, },
/* .pid */ /* .pid */
/* .ppid */ /* .ppid */
/* .tid */
/* .ptid */
}, },
}; };
perf_counter_fork_event(&fork_event); perf_counter_task_event(&task_event);
}
void perf_counter_fork(struct task_struct *task)
{
perf_counter_task(task, 1);
} }
/* /*
...@@ -3875,6 +3905,8 @@ perf_counter_alloc(struct perf_counter_attr *attr, ...@@ -3875,6 +3905,8 @@ perf_counter_alloc(struct perf_counter_attr *attr,
atomic_inc(&nr_mmap_counters); atomic_inc(&nr_mmap_counters);
if (counter->attr.comm) if (counter->attr.comm)
atomic_inc(&nr_comm_counters); atomic_inc(&nr_comm_counters);
if (counter->attr.task)
atomic_inc(&nr_task_counters);
} }
return counter; return counter;
...@@ -4236,8 +4268,10 @@ void perf_counter_exit_task(struct task_struct *child) ...@@ -4236,8 +4268,10 @@ void perf_counter_exit_task(struct task_struct *child)
struct perf_counter_context *child_ctx; struct perf_counter_context *child_ctx;
unsigned long flags; unsigned long flags;
if (likely(!child->perf_counter_ctxp)) if (likely(!child->perf_counter_ctxp)) {
perf_counter_task(child, 0);
return; return;
}
local_irq_save(flags); local_irq_save(flags);
/* /*
...@@ -4255,15 +4289,22 @@ void perf_counter_exit_task(struct task_struct *child) ...@@ -4255,15 +4289,22 @@ void perf_counter_exit_task(struct task_struct *child)
* incremented the context's refcount before we do put_ctx below. * incremented the context's refcount before we do put_ctx below.
*/ */
spin_lock(&child_ctx->lock); spin_lock(&child_ctx->lock);
child->perf_counter_ctxp = NULL;
/* /*
* If this context is a clone; unclone it so it can't get * If this context is a clone; unclone it so it can't get
* swapped to another process while we're removing all * swapped to another process while we're removing all
* the counters from it. * the counters from it.
*/ */
unclone_ctx(child_ctx); unclone_ctx(child_ctx);
spin_unlock(&child_ctx->lock); spin_unlock_irqrestore(&child_ctx->lock, flags);
local_irq_restore(flags);
/*
* Report the task dead after unscheduling the counters so that we
* won't get any samples after PERF_EVENT_EXIT. We can however still
* get a few PERF_EVENT_READ events.
*/
perf_counter_task(child, 0);
child->perf_counter_ctxp = NULL;
/* /*
* We can recurse on the same lock type through: * We can recurse on the same lock type through:
......
...@@ -345,7 +345,7 @@ BUILTIN_OBJS += builtin-stat.o ...@@ -345,7 +345,7 @@ BUILTIN_OBJS += builtin-stat.o
BUILTIN_OBJS += builtin-top.o BUILTIN_OBJS += builtin-top.o
PERFLIBS = $(LIB_FILE) PERFLIBS = $(LIB_FILE)
EXTLIBS = -lbfd EXTLIBS = -lbfd -liberty
# #
# Platform specific tweaks # Platform specific tweaks
......
...@@ -99,6 +99,7 @@ struct comm_event { ...@@ -99,6 +99,7 @@ struct comm_event {
struct fork_event { struct fork_event {
struct perf_event_header header; struct perf_event_header header;
u32 pid, ppid; u32 pid, ppid;
u32 tid, ptid;
}; };
struct lost_event { struct lost_event {
...@@ -252,7 +253,7 @@ static int strcommon(const char *pathname) ...@@ -252,7 +253,7 @@ static int strcommon(const char *pathname)
{ {
int n = 0; int n = 0;
while (pathname[n] == cwd[n] && n < cwdlen) while (n < cwdlen && pathname[n] == cwd[n])
++n; ++n;
return n; return n;
...@@ -1608,15 +1609,27 @@ process_comm_event(event_t *event, unsigned long offset, unsigned long head) ...@@ -1608,15 +1609,27 @@ process_comm_event(event_t *event, unsigned long offset, unsigned long head)
} }
static int static int
process_fork_event(event_t *event, unsigned long offset, unsigned long head) process_task_event(event_t *event, unsigned long offset, unsigned long head)
{ {
struct thread *thread = threads__findnew(event->fork.pid); struct thread *thread = threads__findnew(event->fork.pid);
struct thread *parent = threads__findnew(event->fork.ppid); struct thread *parent = threads__findnew(event->fork.ppid);
dprintf("%p [%p]: PERF_EVENT_FORK: %d:%d\n", dprintf("%p [%p]: PERF_EVENT_%s: (%d:%d):(%d:%d)\n",
(void *)(offset + head), (void *)(offset + head),
(void *)(long)(event->header.size), (void *)(long)(event->header.size),
event->fork.pid, event->fork.ppid); event->header.type == PERF_EVENT_FORK ? "FORK" : "EXIT",
event->fork.pid, event->fork.tid,
event->fork.ppid, event->fork.ptid);
/*
* A thread clone will have the same PID for both
* parent and child.
*/
if (thread == parent)
return 0;
if (event->header.type == PERF_EVENT_EXIT)
return 0;
if (!thread || !parent || thread__fork(thread, parent)) { if (!thread || !parent || thread__fork(thread, parent)) {
dprintf("problem processing PERF_EVENT_FORK, skipping event.\n"); dprintf("problem processing PERF_EVENT_FORK, skipping event.\n");
...@@ -1706,7 +1719,8 @@ process_event(event_t *event, unsigned long offset, unsigned long head) ...@@ -1706,7 +1719,8 @@ process_event(event_t *event, unsigned long offset, unsigned long head)
return process_comm_event(event, offset, head); return process_comm_event(event, offset, head);
case PERF_EVENT_FORK: case PERF_EVENT_FORK:
return process_fork_event(event, offset, head); case PERF_EVENT_EXIT:
return process_task_event(event, offset, head);
case PERF_EVENT_LOST: case PERF_EVENT_LOST:
return process_lost_event(event, offset, head); return process_lost_event(event, offset, head);
......
...@@ -285,6 +285,7 @@ static const char *skip_symbols[] = { ...@@ -285,6 +285,7 @@ static const char *skip_symbols[] = {
"enter_idle", "enter_idle",
"exit_idle", "exit_idle",
"mwait_idle", "mwait_idle",
"mwait_idle_with_hints",
"ppc64_runlatch_off", "ppc64_runlatch_off",
"pseries_dedicated_idle_sleep", "pseries_dedicated_idle_sleep",
NULL NULL
......
...@@ -318,7 +318,7 @@ char *quote_path_relative(const char *in, int len, ...@@ -318,7 +318,7 @@ char *quote_path_relative(const char *in, int len,
strbuf_addch(out, '"'); strbuf_addch(out, '"');
if (prefix) { if (prefix) {
int off = 0; int off = 0;
while (prefix[off] && off < len && prefix[off] == in[off]) while (off < len && prefix[off] && prefix[off] == in[off])
if (prefix[off] == '/') { if (prefix[off] == '/') {
prefix += off + 1; prefix += off + 1;
in += off + 1; in += off + 1;
......
...@@ -565,7 +565,7 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, ...@@ -565,7 +565,7 @@ static int dso__load_sym(struct dso *self, int fd, const char *name,
goto out_elf_end; goto out_elf_end;
secstrs = elf_getdata(sec_strndx, NULL); secstrs = elf_getdata(sec_strndx, NULL);
if (symstrs == NULL) if (secstrs == NULL)
goto out_elf_end; goto out_elf_end;
nr_syms = shdr.sh_size / shdr.sh_entsize; nr_syms = shdr.sh_size / shdr.sh_entsize;
......
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