2009-10-07 10:47:31 +00:00
|
|
|
#include "symbol.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "debug.h"
|
2009-12-13 21:50:26 +00:00
|
|
|
#include "thread.h"
|
2009-12-13 21:50:25 +00:00
|
|
|
#include "session.h"
|
2009-10-07 10:47:31 +00:00
|
|
|
|
|
|
|
static unsigned long mmap_window = 32;
|
|
|
|
static char __cwd[PATH_MAX];
|
|
|
|
|
2009-12-13 21:50:24 +00:00
|
|
|
static int process_event_stub(event_t *event __used,
|
|
|
|
struct perf_session *session __used)
|
2009-10-07 10:47:31 +00:00
|
|
|
{
|
2009-11-27 18:29:22 +00:00
|
|
|
dump_printf(": unhandled!\n");
|
2009-10-07 10:47:31 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-12-13 21:50:25 +00:00
|
|
|
static void perf_event_ops__fill_defaults(struct perf_event_ops *handler)
|
2009-10-07 10:47:31 +00:00
|
|
|
{
|
|
|
|
if (!handler->process_sample_event)
|
|
|
|
handler->process_sample_event = process_event_stub;
|
|
|
|
if (!handler->process_mmap_event)
|
|
|
|
handler->process_mmap_event = process_event_stub;
|
|
|
|
if (!handler->process_comm_event)
|
|
|
|
handler->process_comm_event = process_event_stub;
|
|
|
|
if (!handler->process_fork_event)
|
|
|
|
handler->process_fork_event = process_event_stub;
|
|
|
|
if (!handler->process_exit_event)
|
|
|
|
handler->process_exit_event = process_event_stub;
|
|
|
|
if (!handler->process_lost_event)
|
|
|
|
handler->process_lost_event = process_event_stub;
|
|
|
|
if (!handler->process_read_event)
|
|
|
|
handler->process_read_event = process_event_stub;
|
|
|
|
if (!handler->process_throttle_event)
|
|
|
|
handler->process_throttle_event = process_event_stub;
|
|
|
|
if (!handler->process_unthrottle_event)
|
|
|
|
handler->process_unthrottle_event = process_event_stub;
|
|
|
|
}
|
|
|
|
|
2009-11-27 18:29:22 +00:00
|
|
|
static const char *event__name[] = {
|
|
|
|
[0] = "TOTAL",
|
|
|
|
[PERF_RECORD_MMAP] = "MMAP",
|
|
|
|
[PERF_RECORD_LOST] = "LOST",
|
|
|
|
[PERF_RECORD_COMM] = "COMM",
|
|
|
|
[PERF_RECORD_EXIT] = "EXIT",
|
|
|
|
[PERF_RECORD_THROTTLE] = "THROTTLE",
|
|
|
|
[PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE",
|
|
|
|
[PERF_RECORD_FORK] = "FORK",
|
|
|
|
[PERF_RECORD_READ] = "READ",
|
|
|
|
[PERF_RECORD_SAMPLE] = "SAMPLE",
|
|
|
|
};
|
|
|
|
|
|
|
|
unsigned long event__total[PERF_RECORD_MAX];
|
|
|
|
|
|
|
|
void event__print_totals(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < PERF_RECORD_MAX; ++i)
|
|
|
|
pr_info("%10s events: %10ld\n",
|
|
|
|
event__name[i], event__total[i]);
|
|
|
|
}
|
|
|
|
|
2009-12-13 21:50:24 +00:00
|
|
|
static int process_event(event_t *event, struct perf_session *session,
|
2009-12-13 21:50:25 +00:00
|
|
|
struct perf_event_ops *ops,
|
2009-12-13 21:50:24 +00:00
|
|
|
unsigned long offset, unsigned long head)
|
2009-10-07 10:47:31 +00:00
|
|
|
{
|
|
|
|
trace_event(event);
|
|
|
|
|
2009-11-27 18:29:22 +00:00
|
|
|
if (event->header.type < PERF_RECORD_MAX) {
|
|
|
|
dump_printf("%p [%p]: PERF_RECORD_%s",
|
|
|
|
(void *)(offset + head),
|
|
|
|
(void *)(long)(event->header.size),
|
|
|
|
event__name[event->header.type]);
|
|
|
|
++event__total[0];
|
|
|
|
++event__total[event->header.type];
|
|
|
|
}
|
|
|
|
|
2009-10-07 10:47:31 +00:00
|
|
|
switch (event->header.type) {
|
|
|
|
case PERF_RECORD_SAMPLE:
|
2009-12-13 21:50:25 +00:00
|
|
|
return ops->process_sample_event(event, session);
|
2009-10-07 10:47:31 +00:00
|
|
|
case PERF_RECORD_MMAP:
|
2009-12-13 21:50:25 +00:00
|
|
|
return ops->process_mmap_event(event, session);
|
2009-10-07 10:47:31 +00:00
|
|
|
case PERF_RECORD_COMM:
|
2009-12-13 21:50:25 +00:00
|
|
|
return ops->process_comm_event(event, session);
|
2009-10-07 10:47:31 +00:00
|
|
|
case PERF_RECORD_FORK:
|
2009-12-13 21:50:25 +00:00
|
|
|
return ops->process_fork_event(event, session);
|
2009-10-07 10:47:31 +00:00
|
|
|
case PERF_RECORD_EXIT:
|
2009-12-13 21:50:25 +00:00
|
|
|
return ops->process_exit_event(event, session);
|
2009-10-07 10:47:31 +00:00
|
|
|
case PERF_RECORD_LOST:
|
2009-12-13 21:50:25 +00:00
|
|
|
return ops->process_lost_event(event, session);
|
2009-10-07 10:47:31 +00:00
|
|
|
case PERF_RECORD_READ:
|
2009-12-13 21:50:25 +00:00
|
|
|
return ops->process_read_event(event, session);
|
2009-10-07 10:47:31 +00:00
|
|
|
case PERF_RECORD_THROTTLE:
|
2009-12-13 21:50:25 +00:00
|
|
|
return ops->process_throttle_event(event, session);
|
2009-10-07 10:47:31 +00:00
|
|
|
case PERF_RECORD_UNTHROTTLE:
|
2009-12-13 21:50:25 +00:00
|
|
|
return ops->process_unthrottle_event(event, session);
|
2009-10-07 10:47:31 +00:00
|
|
|
default:
|
2009-12-13 21:50:25 +00:00
|
|
|
ops->total_unknown++;
|
2009-10-07 10:47:31 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-09 22:09:38 +00:00
|
|
|
int perf_header__read_build_ids(int input, u64 offset, u64 size)
|
perf symbols: Use the buildids if present
With this change 'perf record' will intercept PERF_RECORD_MMAP
calls, creating a linked list of DSOs, then when the session
finishes, it will traverse this list and read the buildids,
stashing them at the end of the file and will set up a new
feature bit in the header bitmask.
'perf report' will then notice this feature and populate the
'dsos' list and set the build ids.
When reading the symtabs it will refuse to load from a file that
doesn't have the same build id. This improves the
reliability of the profiler output, as symbols and profiling
data is more guaranteed to match.
Example:
[root@doppio ~]# perf report | head
/home/acme/bin/perf with build id b1ea544ac3746e7538972548a09aadecc5753868 not found, continuing without symbols
# Samples: 2621434559
#
# Overhead Command Shared Object Symbol
# ........ ............... ............................. ......
#
7.91% init [kernel] [k] read_hpet
7.64% init [kernel] [k] mwait_idle_with_hints
7.60% swapper [kernel] [k] read_hpet
7.60% swapper [kernel] [k] mwait_idle_with_hints
3.65% init [kernel] [k] 0xffffffffa02339d9
[root@doppio ~]#
In this case the 'perf' binary was an older one, vanished,
so its symbols probably wouldn't match or would cause subtly
different (and misleading) output.
Next patches will support the kernel as well, reading the build
id notes for it and the modules from /sys.
Another patch should also introduce a new plumbing command:
'perf list-buildids'
that will then be used in porcelain that is distro specific to
fetch -debuginfo packages where such buildids are present. This
will in turn allow for one to run 'perf record' in one machine
and 'perf report' in another.
Future work on having the buildid sent directly from the kernel
in the PERF_RECORD_MMAP event is needed to close races, as the
DSO can be changed during a 'perf record' session, but this
patch at least helps with non-corner cases and current/older
kernels.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Frank Ch. Eigler <fche@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jason Baron <jbaron@redhat.com>
Cc: Jim Keniston <jkenisto@us.ibm.com>
Cc: K. Prasad <prasad@linux.vnet.ibm.com>
Cc: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Roland McGrath <roland@redhat.com>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
LKML-Reference: <1257367843-26224-1-git-send-email-acme@infradead.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2009-11-04 20:50:43 +00:00
|
|
|
{
|
|
|
|
struct build_id_event bev;
|
|
|
|
char filename[PATH_MAX];
|
2009-12-09 22:09:38 +00:00
|
|
|
u64 limit = offset + size;
|
perf symbols: Use the buildids if present
With this change 'perf record' will intercept PERF_RECORD_MMAP
calls, creating a linked list of DSOs, then when the session
finishes, it will traverse this list and read the buildids,
stashing them at the end of the file and will set up a new
feature bit in the header bitmask.
'perf report' will then notice this feature and populate the
'dsos' list and set the build ids.
When reading the symtabs it will refuse to load from a file that
doesn't have the same build id. This improves the
reliability of the profiler output, as symbols and profiling
data is more guaranteed to match.
Example:
[root@doppio ~]# perf report | head
/home/acme/bin/perf with build id b1ea544ac3746e7538972548a09aadecc5753868 not found, continuing without symbols
# Samples: 2621434559
#
# Overhead Command Shared Object Symbol
# ........ ............... ............................. ......
#
7.91% init [kernel] [k] read_hpet
7.64% init [kernel] [k] mwait_idle_with_hints
7.60% swapper [kernel] [k] read_hpet
7.60% swapper [kernel] [k] mwait_idle_with_hints
3.65% init [kernel] [k] 0xffffffffa02339d9
[root@doppio ~]#
In this case the 'perf' binary was an older one, vanished,
so its symbols probably wouldn't match or would cause subtly
different (and misleading) output.
Next patches will support the kernel as well, reading the build
id notes for it and the modules from /sys.
Another patch should also introduce a new plumbing command:
'perf list-buildids'
that will then be used in porcelain that is distro specific to
fetch -debuginfo packages where such buildids are present. This
will in turn allow for one to run 'perf record' in one machine
and 'perf report' in another.
Future work on having the buildid sent directly from the kernel
in the PERF_RECORD_MMAP event is needed to close races, as the
DSO can be changed during a 'perf record' session, but this
patch at least helps with non-corner cases and current/older
kernels.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Frank Ch. Eigler <fche@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jason Baron <jbaron@redhat.com>
Cc: Jim Keniston <jkenisto@us.ibm.com>
Cc: K. Prasad <prasad@linux.vnet.ibm.com>
Cc: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Roland McGrath <roland@redhat.com>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
LKML-Reference: <1257367843-26224-1-git-send-email-acme@infradead.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2009-11-04 20:50:43 +00:00
|
|
|
int err = -1;
|
|
|
|
|
2009-11-11 03:51:07 +00:00
|
|
|
while (offset < limit) {
|
perf symbols: Use the buildids if present
With this change 'perf record' will intercept PERF_RECORD_MMAP
calls, creating a linked list of DSOs, then when the session
finishes, it will traverse this list and read the buildids,
stashing them at the end of the file and will set up a new
feature bit in the header bitmask.
'perf report' will then notice this feature and populate the
'dsos' list and set the build ids.
When reading the symtabs it will refuse to load from a file that
doesn't have the same build id. This improves the
reliability of the profiler output, as symbols and profiling
data is more guaranteed to match.
Example:
[root@doppio ~]# perf report | head
/home/acme/bin/perf with build id b1ea544ac3746e7538972548a09aadecc5753868 not found, continuing without symbols
# Samples: 2621434559
#
# Overhead Command Shared Object Symbol
# ........ ............... ............................. ......
#
7.91% init [kernel] [k] read_hpet
7.64% init [kernel] [k] mwait_idle_with_hints
7.60% swapper [kernel] [k] read_hpet
7.60% swapper [kernel] [k] mwait_idle_with_hints
3.65% init [kernel] [k] 0xffffffffa02339d9
[root@doppio ~]#
In this case the 'perf' binary was an older one, vanished,
so its symbols probably wouldn't match or would cause subtly
different (and misleading) output.
Next patches will support the kernel as well, reading the build
id notes for it and the modules from /sys.
Another patch should also introduce a new plumbing command:
'perf list-buildids'
that will then be used in porcelain that is distro specific to
fetch -debuginfo packages where such buildids are present. This
will in turn allow for one to run 'perf record' in one machine
and 'perf report' in another.
Future work on having the buildid sent directly from the kernel
in the PERF_RECORD_MMAP event is needed to close races, as the
DSO can be changed during a 'perf record' session, but this
patch at least helps with non-corner cases and current/older
kernels.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Frank Ch. Eigler <fche@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jason Baron <jbaron@redhat.com>
Cc: Jim Keniston <jkenisto@us.ibm.com>
Cc: K. Prasad <prasad@linux.vnet.ibm.com>
Cc: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Roland McGrath <roland@redhat.com>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
LKML-Reference: <1257367843-26224-1-git-send-email-acme@infradead.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2009-11-04 20:50:43 +00:00
|
|
|
struct dso *dso;
|
|
|
|
ssize_t len;
|
|
|
|
|
|
|
|
if (read(input, &bev, sizeof(bev)) != sizeof(bev))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
len = bev.header.size - sizeof(bev);
|
|
|
|
if (read(input, filename, len) != len)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
dso = dsos__findnew(filename);
|
|
|
|
if (dso != NULL)
|
|
|
|
dso__set_build_id(dso, &bev.build_id);
|
|
|
|
|
|
|
|
offset += bev.header.size;
|
|
|
|
}
|
|
|
|
err = 0;
|
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2009-12-13 21:50:26 +00:00
|
|
|
static struct thread *perf_session__register_idle_thread(struct perf_session *self __used)
|
|
|
|
{
|
|
|
|
struct thread *thread = threads__findnew(0);
|
|
|
|
|
|
|
|
if (!thread || thread__set_comm(thread, "swapper")) {
|
|
|
|
pr_err("problem inserting idle task.\n");
|
|
|
|
thread = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return thread;
|
|
|
|
}
|
|
|
|
|
2009-12-11 23:24:02 +00:00
|
|
|
int perf_session__process_events(struct perf_session *self,
|
2009-12-13 21:50:25 +00:00
|
|
|
struct perf_event_ops *ops,
|
2009-12-11 23:24:02 +00:00
|
|
|
int full_paths, int *cwdlen, char **cwd)
|
2009-10-07 10:47:31 +00:00
|
|
|
{
|
2009-11-19 16:55:57 +00:00
|
|
|
int err;
|
2009-10-07 10:47:31 +00:00
|
|
|
unsigned long head, shift;
|
|
|
|
unsigned long offset = 0;
|
|
|
|
size_t page_size;
|
|
|
|
u64 sample_type;
|
|
|
|
event_t *event;
|
|
|
|
uint32_t size;
|
|
|
|
char *buf;
|
|
|
|
|
2009-12-13 21:50:26 +00:00
|
|
|
if (perf_session__register_idle_thread(self) == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2009-12-13 21:50:25 +00:00
|
|
|
perf_event_ops__fill_defaults(ops);
|
2009-10-07 10:47:31 +00:00
|
|
|
|
|
|
|
page_size = getpagesize();
|
|
|
|
|
2009-12-11 23:24:02 +00:00
|
|
|
head = self->header.data_offset;
|
|
|
|
sample_type = perf_header__sample_type(&self->header);
|
2009-10-07 10:47:31 +00:00
|
|
|
|
2009-11-19 16:55:57 +00:00
|
|
|
err = -EINVAL;
|
2009-12-13 21:50:25 +00:00
|
|
|
if (ops->sample_type_check &&
|
|
|
|
ops->sample_type_check(sample_type) < 0)
|
2009-12-11 23:24:02 +00:00
|
|
|
goto out_err;
|
2009-10-07 10:47:31 +00:00
|
|
|
|
|
|
|
if (!full_paths) {
|
|
|
|
if (getcwd(__cwd, sizeof(__cwd)) == NULL) {
|
2009-11-19 16:55:57 +00:00
|
|
|
pr_err("failed to get the current directory\n");
|
|
|
|
err = -errno;
|
2009-12-11 23:24:02 +00:00
|
|
|
goto out_err;
|
2009-10-07 10:47:31 +00:00
|
|
|
}
|
|
|
|
*cwd = __cwd;
|
|
|
|
*cwdlen = strlen(*cwd);
|
|
|
|
} else {
|
|
|
|
*cwd = NULL;
|
|
|
|
*cwdlen = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
shift = page_size * (head / page_size);
|
|
|
|
offset += shift;
|
|
|
|
head -= shift;
|
|
|
|
|
|
|
|
remap:
|
2009-11-19 16:55:57 +00:00
|
|
|
buf = mmap(NULL, page_size * mmap_window, PROT_READ,
|
2009-12-11 23:24:02 +00:00
|
|
|
MAP_SHARED, self->fd, offset);
|
2009-10-07 10:47:31 +00:00
|
|
|
if (buf == MAP_FAILED) {
|
2009-11-19 16:55:57 +00:00
|
|
|
pr_err("failed to mmap file\n");
|
|
|
|
err = -errno;
|
2009-12-11 23:24:02 +00:00
|
|
|
goto out_err;
|
2009-10-07 10:47:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
more:
|
|
|
|
event = (event_t *)(buf + head);
|
|
|
|
|
|
|
|
size = event->header.size;
|
|
|
|
if (!size)
|
|
|
|
size = 8;
|
|
|
|
|
|
|
|
if (head + event->header.size >= page_size * mmap_window) {
|
|
|
|
int munmap_ret;
|
|
|
|
|
|
|
|
shift = page_size * (head / page_size);
|
|
|
|
|
|
|
|
munmap_ret = munmap(buf, page_size * mmap_window);
|
|
|
|
assert(munmap_ret == 0);
|
|
|
|
|
|
|
|
offset += shift;
|
|
|
|
head -= shift;
|
|
|
|
goto remap;
|
|
|
|
}
|
|
|
|
|
|
|
|
size = event->header.size;
|
|
|
|
|
|
|
|
dump_printf("\n%p [%p]: event: %d\n",
|
|
|
|
(void *)(offset + head),
|
|
|
|
(void *)(long)event->header.size,
|
|
|
|
event->header.type);
|
|
|
|
|
2009-12-13 21:50:25 +00:00
|
|
|
if (!size || process_event(event, self, ops, offset, head) < 0) {
|
2009-10-07 10:47:31 +00:00
|
|
|
|
|
|
|
dump_printf("%p [%p]: skipping unknown header type: %d\n",
|
|
|
|
(void *)(offset + head),
|
|
|
|
(void *)(long)(event->header.size),
|
|
|
|
event->header.type);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* assume we lost track of the stream, check alignment, and
|
|
|
|
* increment a single u64 in the hope to catch on again 'soon'.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (unlikely(head & 7))
|
|
|
|
head &= ~7ULL;
|
|
|
|
|
|
|
|
size = 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
head += size;
|
|
|
|
|
2009-12-11 23:24:02 +00:00
|
|
|
if (offset + head >= self->header.data_offset + self->header.data_size)
|
2009-10-07 10:47:31 +00:00
|
|
|
goto done;
|
|
|
|
|
2009-12-11 23:24:02 +00:00
|
|
|
if (offset + head < self->size)
|
2009-10-07 10:47:31 +00:00
|
|
|
goto more;
|
|
|
|
|
|
|
|
done:
|
2009-11-19 16:55:57 +00:00
|
|
|
err = 0;
|
2009-12-11 23:24:02 +00:00
|
|
|
out_err:
|
2009-11-19 16:55:57 +00:00
|
|
|
return err;
|
2009-10-07 10:47:31 +00:00
|
|
|
}
|