linux/tools/perf/util/data_map.c
Frederic Weisbecker 9e827dd00a perf tools: Bring linear set of section headers for features
Build a set of section headers for features right after the
datas. Each implemented feature will have one of such section
header that provides the offset and the size of the data
manipulated by the feature.

The trace informations have moved after the data and are
recorded on exit time.

The new layout is as follows:

 -----------------------
                             ___
 [ magic               ]      |
 [ header size         ]      |
 [ attr size           ]      |
 [ attr content offset ]      |
 [ attr content size   ]      |
 [ data offset         ]  File Headers
 [ data size           ]      |
 [ event_types offset  ]      |
 [ event_types size    ]      |
 [ feature bitmap      ]      v

 [ attr section        ]
 [ events section      ]

                             ___
 [         X           ]      |
 [         X           ]      |
 [         X           ]    Datas
 [         X           ]      |
 [         X           ]      v

                             ___
 [ Feature 1 offset    ]      |
 [ Feature 1 size      ] Features headers
 [ Feature 2 offset    ]      |
 [ Feature 2 size      ]      v

 [ Feature 1 content   ]
 [ Feature 2 content   ]
 -----------------------

We have as many feature's section headers as we have features in
use for the current file.

Say Feat 1 and Feat 3 are used by the file, but not Feat 2. Then
the feature headers will be like follows:

[ Feature 1 offset    ]      |
[ Feature 1 size      ] Features headers
[ Feature 3 offset    ]      |
[ Feature 3 size      ]      v

There is no hole to cover Feature 2 that is not in use here. We
only need to cover the needed headers in order, from the lowest
feature bit to the highest.

Currently we have two features: HEADER_TRACE_INFO and
HEADER_BUILD_ID. Both have their contents that follow the
feature headers. Putting the contents right after the feature
headers is not mandatory though. While we keep the feature
headers right after the data and in order, their offsets can
point everywhere. We have just put the two above feature
contents in the end of the file for convenience.

The purpose of this layout change is to have a file format that
scales while keeping it simple: having such linear feature
headers is less error prone wrt forward/backward compatibility
as the content of a feature can be put anywhere, its location
can even change by the time, it's fine because its headers will
tell where it is. And we know how to find these headers,
following the above rules.

Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
LKML-Reference: <1257911467-28276-6-git-send-email-fweisbec@gmail.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2009-11-11 07:30:19 +01:00

253 lines
5.8 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 size)
{
struct build_id_event bev;
char filename[PATH_MAX];
off_t offset = lseek(input, 0, SEEK_CUR);
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 ret, rc = EXIT_FAILURE;
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)
die("Forgot to register perf file handler");
page_size = getpagesize();
input = open(input_name, O_RDONLY);
if (input < 0) {
fprintf(stderr, " failed to open file: %s", input_name);
if (!strcmp(input_name, "perf.data"))
fprintf(stderr, " (try 'perf record' first)");
fprintf(stderr, "\n");
exit(-1);
}
ret = fstat(input, &input_stat);
if (ret < 0) {
perror("failed to stat file");
exit(-1);
}
if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) {
fprintf(stderr, "file: %s not owned by current user or root\n",
input_name);
exit(-1);
}
if (!input_stat.st_size) {
fprintf(stderr, "zero-sized file, nothing to do!\n");
exit(0);
}
*pheader = perf_header__read(input);
header = *pheader;
head = header->data_offset;
sample_type = perf_header__sample_type(header);
if (curr_handler->sample_type_check)
if (curr_handler->sample_type_check(sample_type) < 0)
exit(-1);
if (load_kernel(NULL) < 0) {
perror("failed to load kernel symbols");
return EXIT_FAILURE;
}
if (!full_paths) {
if (getcwd(__cwd, sizeof(__cwd)) == NULL) {
perror("failed to get the current directory");
return EXIT_FAILURE;
}
*cwd = __cwd;
*cwdlen = strlen(*cwd);
} else {
*cwd = NULL;
*cwdlen = 0;
}
shift = page_size * (head / page_size);
offset += shift;
head -= shift;
remap:
buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
MAP_SHARED, input, offset);
if (buf == MAP_FAILED) {
perror("failed to mmap file");
exit(-1);
}
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:
rc = EXIT_SUCCESS;
close(input);
return rc;
}