It should just load kernel symbols, not load the list of modules. There are more stuff to move to other routines, but lets do it in several steps. End goal is to be able to defer symbol table loading till we find a hit for that map address range. So that the kernel & modules are handled just like all the other DSOs in the system. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Frédéric Weisbecker <fweisbec@gmail.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Paul Mackerras <paulus@samba.org> LKML-Reference: <1258757489-5978-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar <mingo@elte.hu>
268 lines
6.0 KiB
C
268 lines
6.0 KiB
C
#include "data_map.h"
|
|
#include "symbol.h"
|
|
#include "util.h"
|
|
#include "debug.h"
|
|
|
|
|
|
static struct perf_file_handler *curr_handler;
|
|
static unsigned long mmap_window = 32;
|
|
static char __cwd[PATH_MAX];
|
|
|
|
static int
|
|
process_event_stub(event_t *event __used,
|
|
unsigned long offset __used,
|
|
unsigned long head __used)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void register_perf_file_handler(struct perf_file_handler *handler)
|
|
{
|
|
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;
|
|
|
|
curr_handler = handler;
|
|
}
|
|
|
|
static int
|
|
process_event(event_t *event, unsigned long offset, unsigned long head)
|
|
{
|
|
trace_event(event);
|
|
|
|
switch (event->header.type) {
|
|
case PERF_RECORD_SAMPLE:
|
|
return curr_handler->process_sample_event(event, offset, head);
|
|
case PERF_RECORD_MMAP:
|
|
return curr_handler->process_mmap_event(event, offset, head);
|
|
case PERF_RECORD_COMM:
|
|
return curr_handler->process_comm_event(event, offset, head);
|
|
case PERF_RECORD_FORK:
|
|
return curr_handler->process_fork_event(event, offset, head);
|
|
case PERF_RECORD_EXIT:
|
|
return curr_handler->process_exit_event(event, offset, head);
|
|
case PERF_RECORD_LOST:
|
|
return curr_handler->process_lost_event(event, offset, head);
|
|
case PERF_RECORD_READ:
|
|
return curr_handler->process_read_event(event, offset, head);
|
|
case PERF_RECORD_THROTTLE:
|
|
return curr_handler->process_throttle_event(event, offset, head);
|
|
case PERF_RECORD_UNTHROTTLE:
|
|
return curr_handler->process_unthrottle_event(event, offset, head);
|
|
default:
|
|
curr_handler->total_unknown++;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
int perf_header__read_build_ids(int input, off_t offset, off_t size)
|
|
{
|
|
struct build_id_event bev;
|
|
char filename[PATH_MAX];
|
|
off_t limit = offset + size;
|
|
int err = -1;
|
|
|
|
while (offset < limit) {
|
|
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;
|
|
}
|
|
|
|
int mmap_dispatch_perf_file(struct perf_header **pheader,
|
|
const char *input_name,
|
|
int force,
|
|
int full_paths,
|
|
int *cwdlen,
|
|
char **cwd)
|
|
{
|
|
int err;
|
|
struct perf_header *header;
|
|
unsigned long head, shift;
|
|
unsigned long offset = 0;
|
|
struct stat input_stat;
|
|
size_t page_size;
|
|
u64 sample_type;
|
|
event_t *event;
|
|
uint32_t size;
|
|
int input;
|
|
char *buf;
|
|
|
|
if (curr_handler == NULL) {
|
|
pr_debug("Forgot to register perf file handler\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
page_size = getpagesize();
|
|
|
|
input = open(input_name, O_RDONLY);
|
|
if (input < 0) {
|
|
pr_err("Failed to open file: %s", input_name);
|
|
if (!strcmp(input_name, "perf.data"))
|
|
pr_err(" (try 'perf record' first)");
|
|
pr_err("\n");
|
|
return -errno;
|
|
}
|
|
|
|
if (fstat(input, &input_stat) < 0) {
|
|
pr_err("failed to stat file");
|
|
err = -errno;
|
|
goto out_close;
|
|
}
|
|
|
|
err = -EACCES;
|
|
if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) {
|
|
pr_err("file: %s not owned by current user or root\n",
|
|
input_name);
|
|
goto out_close;
|
|
}
|
|
|
|
if (input_stat.st_size == 0) {
|
|
pr_info("zero-sized file, nothing to do!\n");
|
|
goto done;
|
|
}
|
|
|
|
err = -ENOMEM;
|
|
header = perf_header__new();
|
|
if (header == NULL)
|
|
goto out_close;
|
|
|
|
err = perf_header__read(header, input);
|
|
if (err < 0)
|
|
goto out_delete;
|
|
*pheader = header;
|
|
head = header->data_offset;
|
|
|
|
sample_type = perf_header__sample_type(header);
|
|
|
|
err = -EINVAL;
|
|
if (curr_handler->sample_type_check &&
|
|
curr_handler->sample_type_check(sample_type) < 0)
|
|
goto out_delete;
|
|
|
|
err = -ENOMEM;
|
|
if (load_kernel(NULL, 1) < 0) {
|
|
pr_err("failed to load kernel symbols\n");
|
|
goto out_delete;
|
|
}
|
|
|
|
if (!full_paths) {
|
|
if (getcwd(__cwd, sizeof(__cwd)) == NULL) {
|
|
pr_err("failed to get the current directory\n");
|
|
err = -errno;
|
|
goto out_delete;
|
|
}
|
|
*cwd = __cwd;
|
|
*cwdlen = strlen(*cwd);
|
|
} else {
|
|
*cwd = NULL;
|
|
*cwdlen = 0;
|
|
}
|
|
|
|
shift = page_size * (head / page_size);
|
|
offset += shift;
|
|
head -= shift;
|
|
|
|
remap:
|
|
buf = mmap(NULL, page_size * mmap_window, PROT_READ,
|
|
MAP_SHARED, input, offset);
|
|
if (buf == MAP_FAILED) {
|
|
pr_err("failed to mmap file\n");
|
|
err = -errno;
|
|
goto out_delete;
|
|
}
|
|
|
|
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);
|
|
|
|
if (!size || process_event(event, offset, head) < 0) {
|
|
|
|
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;
|
|
|
|
if (offset + head >= header->data_offset + header->data_size)
|
|
goto done;
|
|
|
|
if (offset + head < (unsigned long)input_stat.st_size)
|
|
goto more;
|
|
|
|
done:
|
|
err = 0;
|
|
out_close:
|
|
close(input);
|
|
|
|
return err;
|
|
out_delete:
|
|
perf_header__delete(header);
|
|
goto out_close;
|
|
}
|