ba21594cdd
There are still some problems related to loading vmlinux files, but those are unrelated to the feature implemented in this patch, so will get fixed in the next patches, but here are some results: 1. collect perf.data file on a Fedora 12 machine, x86_64, 64-bit userland 2. transfer it to a Debian Testing machine, PARISC64, 32-bit userland acme@parisc:~/git/linux-2.6-tip$ perf buildid-list | head -5 74f9930ee94475b6b3238caf3725a50d59cb994b [kernel.kallsyms] 55fdd56670453ea66c011158c4b9d30179c1d049 /lib/modules/2.6.33-rc4-tip+/kernel/net/ipv4/netfilter/ipt_MASQUERADE.ko 41adff63c730890480980d5d8ba513f1c216a858 /lib/modules/2.6.33-rc4-tip+/kernel/net/ipv4/netfilter/iptable_nat.ko 90a33def1077bb8e97b8a78546dc96c2de62df46 /lib/modules/2.6.33-rc4-tip+/kernel/net/ipv4/netfilter/nf_nat.ko 984c7bea90ce1376d5c8e7ef43a781801286e62d /lib/modules/2.6.33-rc4-tip+/kernel/drivers/net/tun.ko acme@parisc:~/git/linux-2.6-tip$ perf buildid-list | tail -5 22492f3753c6a67de5c7ccbd6b863390c92c0723 /usr/lib64/libXt.so.6.0.0 353802bb7e1b895ba43507cc678f951e778e4c6f /usr/lib64/libMagickCore.so.2.0.0 d10c2897558595efe7be8b0584cf7e6398bc776c /usr/lib64/libfprint.so.0.0.0 a83ecfb519a788774a84d5ddde633c9ba56c03ab /home/acme/bin/perf d3ca765a8ecf257d263801d7ad8c49c189082317 /usr/lib64/libdwarf.so.0.0 acme@parisc:~/git/linux-2.6-tip$ acme@parisc:~/git/linux-2.6-tip$ perf report --sort comm The file [kernel.kallsyms] cannot be used, trying to use /proc/kallsyms... ^^^^ The problem related to vmlinux handling, it shouldn't be trying this ^^^^ rather alien /proc/kallsyms at all... /lib64/libpthread-2.10.2.so with build id 5c68f7afeb33309c78037e374b0deee84dd441f6 not found, continuing without symbols /lib64/libc-2.10.2.so with build id eb4ec8fa8b2a5eb18cad173c92f27ed8887ed1c1 not found, continuing without symbols /home/acme/bin/perf with build id a83ecfb519a788774a84d5ddde633c9ba56c03ab not found, continuing without symbols /usr/sbin/openvpn with build id f2037a091ef36b591187a858d75e203690ea9409 not found, continuing without symbols Failed to open /lib/modules/2.6.33-rc4-tip+/kernel/drivers/net/e1000e/e1000e.ko, continuing without symbols Failed to open /lib/modules/2.6.33-rc4-tip+/kernel/drivers/net/wireless/iwlwifi/iwlcore.ko, continuing without symbols <SNIP more complaints about not finding the right build-ids, those will have to wait for 'perf archive' or plain copying what was collected by 'perf record' on the x86_64, source machine, see further below for an example of this > # Samples: 293085637 # # Overhead Command # ........ ............... # 61.70% find 23.50% perf 5.86% swapper 3.12% sshd 2.39% init 0.87% bash 0.86% sleep 0.59% dbus-daemon 0.25% hald 0.24% NetworkManager 0.19% hald-addon-rfki 0.15% openvpn 0.07% phy0 0.07% events/0 0.05% iwl3945 0.05% events/1 0.03% kondemand/0 acme@parisc:~/git/linux-2.6-tip$ Which matches what we get when running the same command for the same perf.data file on the F12, x86_64, source machine: [root@doppio linux-2.6-tip]# perf report --sort comm # Samples: 293085637 # # Overhead Command # ........ ............... # 61.70% find 23.50% perf 5.86% swapper 3.12% sshd 2.39% init 0.87% bash 0.86% sleep 0.59% dbus-daemon 0.25% hald 0.24% NetworkManager 0.19% hald-addon-rfki 0.15% openvpn 0.07% phy0 0.07% events/0 0.05% iwl3945 0.05% events/1 0.03% kondemand/0 [root@doppio linux-2.6-tip]# The other modes work as well, modulo the problem with vmlinux: acme@parisc:~/git/linux-2.6-tip$ perf report --sort comm,dso 2> /dev/null | head -15 # Samples: 293085637 # # Overhead Command Shared Object # ........ ............... ................................. # 35.11% find ffffffff81002b5a 18.25% perf ffffffff8102235f 16.17% find libc-2.10.2.so 9.07% find find 5.80% swapper ffffffff8102235f 3.95% perf libc-2.10.2.so 2.33% init ffffffff810091b9 1.65% sshd libcrypto.so.0.9.8k 1.35% find [e1000e] 0.68% sleep libc-2.10.2.so acme@parisc:~/git/linux-2.6-tip$ And the lack of the right buildids: acme@parisc:~/git/linux-2.6-tip$ perf report --sort comm,dso,symbol 2> /dev/null | head -15 # Samples: 293085637 # # Overhead Command Shared Object Symbol # ........ ............... ................................. ...... # 35.11% find ffffffff81002b5a [k] 0xffffffff81002b5a 18.25% perf ffffffff8102235f [k] 0xffffffff8102235f 16.17% find libc-2.10.2.so [.] 0x00000000045782 9.07% find find [.] 0x0000000000fb0e 5.80% swapper ffffffff8102235f [k] 0xffffffff8102235f 3.95% perf libc-2.10.2.so [.] 0x0000000007f398 2.33% init ffffffff810091b9 [k] 0xffffffff810091b9 1.65% sshd libcrypto.so.0.9.8k [.] 0x00000000105440 1.35% find [e1000e] [k] 0x00000000010948 0.68% sleep libc-2.10.2.so [.] 0x0000000011ad5b acme@parisc:~/git/linux-2.6-tip$ But if we: acme@parisc:~/git/linux-2.6-tip$ ls ~/.debug ls: cannot access /home/acme/.debug: No such file or directory acme@parisc:~/git/linux-2.6-tip$ mkdir -p ~/.debug/lib64/libc-2.10.2.so/ acme@parisc:~/git/linux-2.6-tip$ scp doppio:.debug/lib64/libc-2.10.2.so/* ~/.debug/lib64/libc-2.10.2.so/ acme@doppio's password: eb4ec8fa8b2a5eb18cad173c92f27ed8887ed1c1 100% 1783KB 714.7KB/s 00:02 acme@parisc:~/git/linux-2.6-tip$ mkdir -p ~/.debug/.build-id/eb acme@parisc:~/git/linux-2.6-tip$ ln -s ../../lib64/libc-2.10.2.so/eb4ec8fa8b2a5eb18cad173c92f27ed8887ed1c1 ~/.debug/.build-id/eb/4ec8fa8b2a5eb18cad173c92f27ed8887ed1c1 acme@parisc:~/git/linux-2.6-tip$ perf report --dsos libc-2.10.2.so 2> /dev/null # dso: libc-2.10.2.so # Samples: 64281170 # # Overhead Command Symbol # ........ ............... ...... # 14.98% perf [.] __GI_strcmp 12.30% find [.] __GI_memmove 9.25% find [.] _int_malloc 7.60% find [.] _IO_vfprintf_internal 6.10% find [.] _IO_new_file_xsputn 6.02% find [.] __GI_close 3.08% find [.] _IO_file_overflow_internal 3.08% find [.] malloc_consolidate 3.08% find [.] _int_free 3.08% find [.] __strchrnul 3.08% find [.] __getdents64 3.08% find [.] __write_nocancel 3.08% sleep [.] __GI__dl_addr 3.08% sshd [.] __libc_select 3.08% find [.] _IO_new_file_write 3.07% find [.] _IO_new_do_write 3.06% find [.] __GI___errno_location 3.05% find [.] __GI___libc_malloc 3.04% perf [.] __GI_memcpy 1.71% find [.] __fprintf_chk 1.29% bash [.] __gconv_transform_utf8_internal 0.79% dbus-daemon [.] __GI_strlen # # (For a higher level overview, try: perf report --sort comm,dso) # acme@parisc:~/git/linux-2.6-tip$ Which matches what we get on the source, F12, x86_64 machine: [root@doppio linux-2.6-tip]# perf report --dsos libc-2.10.2.so # dso: libc-2.10.2.so # Samples: 64281170 # # Overhead Command Symbol # ........ ............... ...... # 14.98% perf [.] __GI_strcmp 12.30% find [.] __GI_memmove 9.25% find [.] _int_malloc 7.60% find [.] _IO_vfprintf_internal 6.10% find [.] _IO_new_file_xsputn 6.02% find [.] __GI_close 3.08% find [.] _IO_file_overflow_internal 3.08% find [.] malloc_consolidate 3.08% find [.] _int_free 3.08% find [.] __strchrnul 3.08% find [.] __getdents64 3.08% find [.] __write_nocancel 3.08% sleep [.] __GI__dl_addr 3.08% sshd [.] __libc_select 3.08% find [.] _IO_new_file_write 3.07% find [.] _IO_new_do_write 3.06% find [.] __GI___errno_location 3.05% find [.] __GI___libc_malloc 3.04% perf [.] __GI_memcpy 1.71% find [.] __fprintf_chk 1.29% bash [.] __gconv_transform_utf8_internal 0.79% dbus-daemon [.] __GI_strlen # # (For a higher level overview, try: perf report --sort comm,dso) # [root@doppio linux-2.6-tip]# So I think this is really, really nice in that it demonstrates the portability of perf.data files and the use of build-ids accross such aliens worlds :-) There are some things to fix tho, like the bitmap on the header, but things are looking good. 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: <1263478990-8200-2-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar <mingo@elte.hu>
700 lines
15 KiB
C
700 lines
15 KiB
C
#include <sys/types.h>
|
|
#include <byteswap.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <linux/list.h>
|
|
#include <linux/kernel.h>
|
|
|
|
#include "util.h"
|
|
#include "header.h"
|
|
#include "../perf.h"
|
|
#include "trace-event.h"
|
|
#include "session.h"
|
|
#include "symbol.h"
|
|
#include "debug.h"
|
|
|
|
/*
|
|
* Create new perf.data header attribute:
|
|
*/
|
|
struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr)
|
|
{
|
|
struct perf_header_attr *self = malloc(sizeof(*self));
|
|
|
|
if (self != NULL) {
|
|
self->attr = *attr;
|
|
self->ids = 0;
|
|
self->size = 1;
|
|
self->id = malloc(sizeof(u64));
|
|
if (self->id == NULL) {
|
|
free(self);
|
|
self = NULL;
|
|
}
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
void perf_header_attr__delete(struct perf_header_attr *self)
|
|
{
|
|
free(self->id);
|
|
free(self);
|
|
}
|
|
|
|
int perf_header_attr__add_id(struct perf_header_attr *self, u64 id)
|
|
{
|
|
int pos = self->ids;
|
|
|
|
self->ids++;
|
|
if (self->ids > self->size) {
|
|
int nsize = self->size * 2;
|
|
u64 *nid = realloc(self->id, nsize * sizeof(u64));
|
|
|
|
if (nid == NULL)
|
|
return -1;
|
|
|
|
self->size = nsize;
|
|
self->id = nid;
|
|
}
|
|
self->id[pos] = id;
|
|
return 0;
|
|
}
|
|
|
|
int perf_header__init(struct perf_header *self)
|
|
{
|
|
self->size = 1;
|
|
self->attr = malloc(sizeof(void *));
|
|
return self->attr == NULL ? -ENOMEM : 0;
|
|
}
|
|
|
|
void perf_header__exit(struct perf_header *self)
|
|
{
|
|
int i;
|
|
for (i = 0; i < self->attrs; ++i)
|
|
perf_header_attr__delete(self->attr[i]);
|
|
free(self->attr);
|
|
}
|
|
|
|
int perf_header__add_attr(struct perf_header *self,
|
|
struct perf_header_attr *attr)
|
|
{
|
|
if (self->frozen)
|
|
return -1;
|
|
|
|
if (self->attrs == self->size) {
|
|
int nsize = self->size * 2;
|
|
struct perf_header_attr **nattr;
|
|
|
|
nattr = realloc(self->attr, nsize * sizeof(void *));
|
|
if (nattr == NULL)
|
|
return -1;
|
|
|
|
self->size = nsize;
|
|
self->attr = nattr;
|
|
}
|
|
|
|
self->attr[self->attrs++] = attr;
|
|
return 0;
|
|
}
|
|
|
|
#define MAX_EVENT_NAME 64
|
|
|
|
struct perf_trace_event_type {
|
|
u64 event_id;
|
|
char name[MAX_EVENT_NAME];
|
|
};
|
|
|
|
static int event_count;
|
|
static struct perf_trace_event_type *events;
|
|
|
|
int perf_header__push_event(u64 id, const char *name)
|
|
{
|
|
if (strlen(name) > MAX_EVENT_NAME)
|
|
pr_warning("Event %s will be truncated\n", name);
|
|
|
|
if (!events) {
|
|
events = malloc(sizeof(struct perf_trace_event_type));
|
|
if (events == NULL)
|
|
return -ENOMEM;
|
|
} else {
|
|
struct perf_trace_event_type *nevents;
|
|
|
|
nevents = realloc(events, (event_count + 1) * sizeof(*events));
|
|
if (nevents == NULL)
|
|
return -ENOMEM;
|
|
events = nevents;
|
|
}
|
|
memset(&events[event_count], 0, sizeof(struct perf_trace_event_type));
|
|
events[event_count].event_id = id;
|
|
strncpy(events[event_count].name, name, MAX_EVENT_NAME - 1);
|
|
event_count++;
|
|
return 0;
|
|
}
|
|
|
|
char *perf_header__find_event(u64 id)
|
|
{
|
|
int i;
|
|
for (i = 0 ; i < event_count; i++) {
|
|
if (events[i].event_id == id)
|
|
return events[i].name;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static const char *__perf_magic = "PERFFILE";
|
|
|
|
#define PERF_MAGIC (*(u64 *)__perf_magic)
|
|
|
|
struct perf_file_attr {
|
|
struct perf_event_attr attr;
|
|
struct perf_file_section ids;
|
|
};
|
|
|
|
void perf_header__set_feat(struct perf_header *self, int feat)
|
|
{
|
|
set_bit(feat, self->adds_features);
|
|
}
|
|
|
|
bool perf_header__has_feat(const struct perf_header *self, int feat)
|
|
{
|
|
return test_bit(feat, self->adds_features);
|
|
}
|
|
|
|
static int do_write(int fd, const void *buf, size_t size)
|
|
{
|
|
while (size) {
|
|
int ret = write(fd, buf, size);
|
|
|
|
if (ret < 0)
|
|
return -errno;
|
|
|
|
size -= ret;
|
|
buf += ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define NAME_ALIGN 64
|
|
|
|
static int write_padded(int fd, const void *bf, size_t count,
|
|
size_t count_aligned)
|
|
{
|
|
static const char zero_buf[NAME_ALIGN];
|
|
int err = do_write(fd, bf, count);
|
|
|
|
if (!err)
|
|
err = do_write(fd, zero_buf, count_aligned - count);
|
|
|
|
return err;
|
|
}
|
|
|
|
#define dsos__for_each_with_build_id(pos, head) \
|
|
list_for_each_entry(pos, head, node) \
|
|
if (!pos->has_build_id) \
|
|
continue; \
|
|
else
|
|
|
|
static int __dsos__write_buildid_table(struct list_head *head, u16 misc, int fd)
|
|
{
|
|
struct dso *pos;
|
|
|
|
dsos__for_each_with_build_id(pos, head) {
|
|
int err;
|
|
struct build_id_event b;
|
|
size_t len = pos->long_name_len + 1;
|
|
|
|
len = ALIGN(len, NAME_ALIGN);
|
|
memset(&b, 0, sizeof(b));
|
|
memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id));
|
|
b.header.misc = misc;
|
|
b.header.size = sizeof(b) + len;
|
|
err = do_write(fd, &b, sizeof(b));
|
|
if (err < 0)
|
|
return err;
|
|
err = write_padded(fd, pos->long_name,
|
|
pos->long_name_len + 1, len);
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dsos__write_buildid_table(int fd)
|
|
{
|
|
int err = __dsos__write_buildid_table(&dsos__kernel,
|
|
PERF_RECORD_MISC_KERNEL, fd);
|
|
if (err == 0)
|
|
err = __dsos__write_buildid_table(&dsos__user,
|
|
PERF_RECORD_MISC_USER, fd);
|
|
return err;
|
|
}
|
|
|
|
static int dso__cache_build_id(struct dso *self, const char *debugdir)
|
|
{
|
|
const size_t size = PATH_MAX;
|
|
char *filename = malloc(size),
|
|
*linkname = malloc(size), *targetname, *sbuild_id;
|
|
int len, err = -1;
|
|
|
|
if (filename == NULL || linkname == NULL)
|
|
goto out_free;
|
|
|
|
len = snprintf(filename, size, "%s%s", debugdir, self->long_name);
|
|
if (mkdir_p(filename, 0755))
|
|
goto out_free;
|
|
|
|
len += snprintf(filename + len, sizeof(filename) - len, "/");
|
|
sbuild_id = filename + len;
|
|
build_id__sprintf(self->build_id, sizeof(self->build_id), sbuild_id);
|
|
|
|
if (access(filename, F_OK) && link(self->long_name, filename) &&
|
|
copyfile(self->long_name, filename))
|
|
goto out_free;
|
|
|
|
len = snprintf(linkname, size, "%s/.build-id/%.2s",
|
|
debugdir, sbuild_id);
|
|
|
|
if (access(linkname, X_OK) && mkdir_p(linkname, 0755))
|
|
goto out_free;
|
|
|
|
snprintf(linkname + len, size - len, "/%s", sbuild_id + 2);
|
|
targetname = filename + strlen(debugdir) - 5;
|
|
memcpy(targetname, "../..", 5);
|
|
|
|
if (symlink(targetname, linkname) == 0)
|
|
err = 0;
|
|
out_free:
|
|
free(filename);
|
|
free(linkname);
|
|
return err;
|
|
}
|
|
|
|
static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir)
|
|
{
|
|
struct dso *pos;
|
|
int err = 0;
|
|
|
|
dsos__for_each_with_build_id(pos, head)
|
|
if (dso__cache_build_id(pos, debugdir))
|
|
err = -1;
|
|
|
|
return err;
|
|
}
|
|
|
|
static int dsos__cache_build_ids(void)
|
|
{
|
|
int err_kernel, err_user;
|
|
char debugdir[PATH_MAX];
|
|
|
|
snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"),
|
|
DEBUG_CACHE_DIR);
|
|
|
|
if (mkdir(debugdir, 0755) != 0 && errno != EEXIST)
|
|
return -1;
|
|
|
|
err_kernel = __dsos__cache_build_ids(&dsos__kernel, debugdir);
|
|
err_user = __dsos__cache_build_ids(&dsos__user, debugdir);
|
|
return err_kernel || err_user ? -1 : 0;
|
|
}
|
|
|
|
static int perf_header__adds_write(struct perf_header *self, int fd)
|
|
{
|
|
int nr_sections;
|
|
struct perf_file_section *feat_sec;
|
|
int sec_size;
|
|
u64 sec_start;
|
|
int idx = 0, err;
|
|
|
|
if (dsos__read_build_ids())
|
|
perf_header__set_feat(self, HEADER_BUILD_ID);
|
|
|
|
nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS);
|
|
if (!nr_sections)
|
|
return 0;
|
|
|
|
feat_sec = calloc(sizeof(*feat_sec), nr_sections);
|
|
if (feat_sec == NULL)
|
|
return -ENOMEM;
|
|
|
|
sec_size = sizeof(*feat_sec) * nr_sections;
|
|
|
|
sec_start = self->data_offset + self->data_size;
|
|
lseek(fd, sec_start + sec_size, SEEK_SET);
|
|
|
|
if (perf_header__has_feat(self, HEADER_TRACE_INFO)) {
|
|
struct perf_file_section *trace_sec;
|
|
|
|
trace_sec = &feat_sec[idx++];
|
|
|
|
/* Write trace info */
|
|
trace_sec->offset = lseek(fd, 0, SEEK_CUR);
|
|
read_tracing_data(fd, attrs, nr_counters);
|
|
trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset;
|
|
}
|
|
|
|
|
|
if (perf_header__has_feat(self, HEADER_BUILD_ID)) {
|
|
struct perf_file_section *buildid_sec;
|
|
|
|
buildid_sec = &feat_sec[idx++];
|
|
|
|
/* Write build-ids */
|
|
buildid_sec->offset = lseek(fd, 0, SEEK_CUR);
|
|
err = dsos__write_buildid_table(fd);
|
|
if (err < 0) {
|
|
pr_debug("failed to write buildid table\n");
|
|
goto out_free;
|
|
}
|
|
buildid_sec->size = lseek(fd, 0, SEEK_CUR) - buildid_sec->offset;
|
|
dsos__cache_build_ids();
|
|
}
|
|
|
|
lseek(fd, sec_start, SEEK_SET);
|
|
err = do_write(fd, feat_sec, sec_size);
|
|
if (err < 0)
|
|
pr_debug("failed to write feature section\n");
|
|
out_free:
|
|
free(feat_sec);
|
|
return err;
|
|
}
|
|
|
|
int perf_header__write(struct perf_header *self, int fd, bool at_exit)
|
|
{
|
|
struct perf_file_header f_header;
|
|
struct perf_file_attr f_attr;
|
|
struct perf_header_attr *attr;
|
|
int i, err;
|
|
|
|
lseek(fd, sizeof(f_header), SEEK_SET);
|
|
|
|
|
|
for (i = 0; i < self->attrs; i++) {
|
|
attr = self->attr[i];
|
|
|
|
attr->id_offset = lseek(fd, 0, SEEK_CUR);
|
|
err = do_write(fd, attr->id, attr->ids * sizeof(u64));
|
|
if (err < 0) {
|
|
pr_debug("failed to write perf header\n");
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
self->attr_offset = lseek(fd, 0, SEEK_CUR);
|
|
|
|
for (i = 0; i < self->attrs; i++) {
|
|
attr = self->attr[i];
|
|
|
|
f_attr = (struct perf_file_attr){
|
|
.attr = attr->attr,
|
|
.ids = {
|
|
.offset = attr->id_offset,
|
|
.size = attr->ids * sizeof(u64),
|
|
}
|
|
};
|
|
err = do_write(fd, &f_attr, sizeof(f_attr));
|
|
if (err < 0) {
|
|
pr_debug("failed to write perf header attribute\n");
|
|
return err;
|
|
}
|
|
}
|
|
|
|
self->event_offset = lseek(fd, 0, SEEK_CUR);
|
|
self->event_size = event_count * sizeof(struct perf_trace_event_type);
|
|
if (events) {
|
|
err = do_write(fd, events, self->event_size);
|
|
if (err < 0) {
|
|
pr_debug("failed to write perf header events\n");
|
|
return err;
|
|
}
|
|
}
|
|
|
|
self->data_offset = lseek(fd, 0, SEEK_CUR);
|
|
|
|
if (at_exit) {
|
|
err = perf_header__adds_write(self, fd);
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
|
|
f_header = (struct perf_file_header){
|
|
.magic = PERF_MAGIC,
|
|
.size = sizeof(f_header),
|
|
.attr_size = sizeof(f_attr),
|
|
.attrs = {
|
|
.offset = self->attr_offset,
|
|
.size = self->attrs * sizeof(f_attr),
|
|
},
|
|
.data = {
|
|
.offset = self->data_offset,
|
|
.size = self->data_size,
|
|
},
|
|
.event_types = {
|
|
.offset = self->event_offset,
|
|
.size = self->event_size,
|
|
},
|
|
};
|
|
|
|
memcpy(&f_header.adds_features, &self->adds_features, sizeof(self->adds_features));
|
|
|
|
lseek(fd, 0, SEEK_SET);
|
|
err = do_write(fd, &f_header, sizeof(f_header));
|
|
if (err < 0) {
|
|
pr_debug("failed to write perf header\n");
|
|
return err;
|
|
}
|
|
lseek(fd, self->data_offset + self->data_size, SEEK_SET);
|
|
|
|
self->frozen = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int do_read(int fd, void *buf, size_t size)
|
|
{
|
|
while (size) {
|
|
int ret = read(fd, buf, size);
|
|
|
|
if (ret <= 0)
|
|
return -1;
|
|
|
|
size -= ret;
|
|
buf += ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int perf_header__getbuffer64(struct perf_header *self,
|
|
int fd, void *buf, size_t size)
|
|
{
|
|
if (do_read(fd, buf, size))
|
|
return -1;
|
|
|
|
if (self->needs_swap)
|
|
mem_bswap_64(buf, size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int perf_header__process_sections(struct perf_header *self, int fd,
|
|
int (*process)(struct perf_file_section *self,
|
|
struct perf_header *ph,
|
|
int feat, int fd))
|
|
{
|
|
struct perf_file_section *feat_sec;
|
|
int nr_sections;
|
|
int sec_size;
|
|
int idx = 0;
|
|
int err = -1, feat = 1;
|
|
|
|
nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS);
|
|
if (!nr_sections)
|
|
return 0;
|
|
|
|
feat_sec = calloc(sizeof(*feat_sec), nr_sections);
|
|
if (!feat_sec)
|
|
return -1;
|
|
|
|
sec_size = sizeof(*feat_sec) * nr_sections;
|
|
|
|
lseek(fd, self->data_offset + self->data_size, SEEK_SET);
|
|
|
|
if (perf_header__getbuffer64(self, fd, feat_sec, sec_size))
|
|
goto out_free;
|
|
|
|
err = 0;
|
|
while (idx < nr_sections && feat < HEADER_LAST_FEATURE) {
|
|
if (perf_header__has_feat(self, feat)) {
|
|
struct perf_file_section *sec = &feat_sec[idx++];
|
|
|
|
err = process(sec, self, feat, fd);
|
|
if (err < 0)
|
|
break;
|
|
}
|
|
++feat;
|
|
}
|
|
out_free:
|
|
free(feat_sec);
|
|
return err;
|
|
}
|
|
|
|
int perf_file_header__read(struct perf_file_header *self,
|
|
struct perf_header *ph, int fd)
|
|
{
|
|
lseek(fd, 0, SEEK_SET);
|
|
|
|
if (do_read(fd, self, sizeof(*self)) ||
|
|
memcmp(&self->magic, __perf_magic, sizeof(self->magic)))
|
|
return -1;
|
|
|
|
if (self->attr_size != sizeof(struct perf_file_attr)) {
|
|
u64 attr_size = bswap_64(self->attr_size);
|
|
|
|
if (attr_size != sizeof(struct perf_file_attr))
|
|
return -1;
|
|
|
|
mem_bswap_64(self, offsetof(struct perf_file_header,
|
|
adds_features));
|
|
ph->needs_swap = true;
|
|
}
|
|
|
|
if (self->size != sizeof(*self)) {
|
|
/* Support the previous format */
|
|
if (self->size == offsetof(typeof(*self), adds_features))
|
|
bitmap_zero(self->adds_features, HEADER_FEAT_BITS);
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
memcpy(&ph->adds_features, &self->adds_features,
|
|
sizeof(ph->adds_features));
|
|
/*
|
|
* FIXME: hack that assumes that if we need swap the perf.data file
|
|
* may be coming from an arch with a different word-size, ergo different
|
|
* DEFINE_BITMAP format, investigate more later, but for now its mostly
|
|
* safe to assume that we have a build-id section. Trace files probably
|
|
* have several other issues in this realm anyway...
|
|
*/
|
|
if (ph->needs_swap) {
|
|
memset(&ph->adds_features, 0, sizeof(ph->adds_features));
|
|
perf_header__set_feat(ph, HEADER_BUILD_ID);
|
|
}
|
|
|
|
ph->event_offset = self->event_types.offset;
|
|
ph->event_size = self->event_types.size;
|
|
ph->data_offset = self->data.offset;
|
|
ph->data_size = self->data.size;
|
|
return 0;
|
|
}
|
|
|
|
static int perf_file_section__process(struct perf_file_section *self,
|
|
struct perf_header *ph,
|
|
int feat, int fd)
|
|
{
|
|
if (lseek(fd, self->offset, SEEK_SET) < 0) {
|
|
pr_debug("Failed to lseek to %Ld offset for feature %d, "
|
|
"continuing...\n", self->offset, feat);
|
|
return 0;
|
|
}
|
|
|
|
switch (feat) {
|
|
case HEADER_TRACE_INFO:
|
|
trace_report(fd);
|
|
break;
|
|
|
|
case HEADER_BUILD_ID:
|
|
if (perf_header__read_build_ids(ph, fd, self->offset, self->size))
|
|
pr_debug("Failed to read buildids, continuing...\n");
|
|
break;
|
|
default:
|
|
pr_debug("unknown feature %d, continuing...\n", feat);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int perf_header__read(struct perf_header *self, int fd)
|
|
{
|
|
struct perf_file_header f_header;
|
|
struct perf_file_attr f_attr;
|
|
u64 f_id;
|
|
int nr_attrs, nr_ids, i, j;
|
|
|
|
if (perf_file_header__read(&f_header, self, fd) < 0) {
|
|
pr_debug("incompatible file format\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
nr_attrs = f_header.attrs.size / sizeof(f_attr);
|
|
lseek(fd, f_header.attrs.offset, SEEK_SET);
|
|
|
|
for (i = 0; i < nr_attrs; i++) {
|
|
struct perf_header_attr *attr;
|
|
off_t tmp;
|
|
|
|
if (perf_header__getbuffer64(self, fd, &f_attr, sizeof(f_attr)))
|
|
goto out_errno;
|
|
|
|
tmp = lseek(fd, 0, SEEK_CUR);
|
|
|
|
attr = perf_header_attr__new(&f_attr.attr);
|
|
if (attr == NULL)
|
|
return -ENOMEM;
|
|
|
|
nr_ids = f_attr.ids.size / sizeof(u64);
|
|
lseek(fd, f_attr.ids.offset, SEEK_SET);
|
|
|
|
for (j = 0; j < nr_ids; j++) {
|
|
if (perf_header__getbuffer64(self, fd, &f_id, sizeof(f_id)))
|
|
goto out_errno;
|
|
|
|
if (perf_header_attr__add_id(attr, f_id) < 0) {
|
|
perf_header_attr__delete(attr);
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
if (perf_header__add_attr(self, attr) < 0) {
|
|
perf_header_attr__delete(attr);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
lseek(fd, tmp, SEEK_SET);
|
|
}
|
|
|
|
if (f_header.event_types.size) {
|
|
lseek(fd, f_header.event_types.offset, SEEK_SET);
|
|
events = malloc(f_header.event_types.size);
|
|
if (events == NULL)
|
|
return -ENOMEM;
|
|
if (perf_header__getbuffer64(self, fd, events,
|
|
f_header.event_types.size))
|
|
goto out_errno;
|
|
event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type);
|
|
}
|
|
|
|
perf_header__process_sections(self, fd, perf_file_section__process);
|
|
|
|
lseek(fd, self->data_offset, SEEK_SET);
|
|
|
|
self->frozen = 1;
|
|
return 0;
|
|
out_errno:
|
|
return -errno;
|
|
}
|
|
|
|
u64 perf_header__sample_type(struct perf_header *header)
|
|
{
|
|
u64 type = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < header->attrs; i++) {
|
|
struct perf_header_attr *attr = header->attr[i];
|
|
|
|
if (!type)
|
|
type = attr->attr.sample_type;
|
|
else if (type != attr->attr.sample_type)
|
|
die("non matching sample_type");
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
struct perf_event_attr *
|
|
perf_header__find_attr(u64 id, struct perf_header *header)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < header->attrs; i++) {
|
|
struct perf_header_attr *attr = header->attr[i];
|
|
int j;
|
|
|
|
for (j = 0; j < attr->ids; j++) {
|
|
if (attr->id[j] == id)
|
|
return &attr->attr;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|