perf_counter tools: Rework the file format

Create a structured file format that includes the full
perf_counter_attr and all its relevant counter IDs so that
the reporting program has full information.

Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
Peter Zijlstra 2009-06-25 17:05:54 +02:00 committed by Ingo Molnar
parent e5c5954779
commit 7c6a1c65bb
9 changed files with 377 additions and 54 deletions

View file

@ -290,7 +290,7 @@ LIB_FILE=libperf.a
LIB_H += ../../include/linux/perf_counter.h
LIB_H += perf.h
LIB_H += types.h
LIB_H += util/types.h
LIB_H += util/list.h
LIB_H += util/rbtree.h
LIB_H += util/levenshtein.h
@ -328,6 +328,7 @@ LIB_OBJS += util/sigchain.o
LIB_OBJS += util/symbol.o
LIB_OBJS += util/color.o
LIB_OBJS += util/pager.o
LIB_OBJS += util/header.o
BUILTIN_OBJS += builtin-annotate.o
BUILTIN_OBJS += builtin-help.o

View file

@ -14,6 +14,8 @@
#include "util/parse-events.h"
#include "util/string.h"
#include "util/header.h"
#include <unistd.h>
#include <sched.h>
@ -52,7 +54,8 @@ static int nr_poll;
static int nr_cpu;
static int file_new = 1;
static struct perf_file_header file_header;
struct perf_header *header;
struct mmap_event {
struct perf_event_header header;
@ -328,7 +331,7 @@ static void pid_synthesize_mmap_samples(pid_t pid)
fclose(fp);
}
static void synthesize_samples(void)
static void synthesize_all(void)
{
DIR *proc;
struct dirent dirent, *next;
@ -352,10 +355,35 @@ static void synthesize_samples(void)
static int group_fd;
static struct perf_header_attr *get_header_attr(struct perf_counter_attr *a, int nr)
{
struct perf_header_attr *h_attr;
if (nr < header->attrs) {
h_attr = header->attr[nr];
} else {
h_attr = perf_header_attr__new(a);
perf_header__add_attr(header, h_attr);
}
return h_attr;
}
static void create_counter(int counter, int cpu, pid_t pid)
{
struct perf_counter_attr *attr = attrs + counter;
int track = 1;
struct perf_header_attr *h_attr;
int track = !counter; /* only the first counter needs these */
struct {
u64 count;
u64 time_enabled;
u64 time_running;
u64 id;
} read_data;
attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
PERF_FORMAT_TOTAL_TIME_RUNNING |
PERF_FORMAT_ID;
attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID;
@ -368,22 +396,11 @@ static void create_counter(int counter, int cpu, pid_t pid)
if (call_graph)
attr->sample_type |= PERF_SAMPLE_CALLCHAIN;
if (file_new) {
file_header.sample_type = attr->sample_type;
} else {
if (file_header.sample_type != attr->sample_type) {
fprintf(stderr, "incompatible append\n");
exit(-1);
}
}
attr->mmap = track;
attr->comm = track;
attr->inherit = (cpu < 0) && inherit;
attr->disabled = 1;
track = 0; /* only the first counter needs these */
try_again:
fd[nr_cpu][counter] = sys_perf_counter_open(attr, pid, cpu, group_fd, 0);
@ -414,6 +431,19 @@ static void create_counter(int counter, int cpu, pid_t pid)
exit(-1);
}
h_attr = get_header_attr(attr, counter);
if (!file_new) {
if (memcmp(&h_attr->attr, attr, sizeof(*attr))) {
fprintf(stderr, "incompatible append\n");
exit(-1);
}
}
read(fd[nr_cpu][counter], &read_data, sizeof(read_data));
perf_header_attr__add_id(h_attr, read_data.id);
assert(fd[nr_cpu][counter] >= 0);
fcntl(fd[nr_cpu][counter], F_SETFL, O_NONBLOCK);
@ -444,11 +474,6 @@ static void open_counters(int cpu, pid_t pid)
{
int counter;
if (pid > 0) {
pid_synthesize_comm_event(pid, 0);
pid_synthesize_mmap_samples(pid);
}
group_fd = -1;
for (counter = 0; counter < nr_counters; counter++)
create_counter(counter, cpu, pid);
@ -458,17 +483,16 @@ static void open_counters(int cpu, pid_t pid)
static void atexit_header(void)
{
file_header.data_size += bytes_written;
header->data_size += bytes_written;
if (pwrite(output, &file_header, sizeof(file_header), 0) == -1)
perror("failed to write on file headers");
perf_header__write(header, output);
}
static int __cmd_record(int argc, const char **argv)
{
int i, counter;
struct stat st;
pid_t pid;
pid_t pid = 0;
int flags;
int ret;
@ -499,22 +523,31 @@ static int __cmd_record(int argc, const char **argv)
exit(-1);
}
if (!file_new) {
if (read(output, &file_header, sizeof(file_header)) == -1) {
perror("failed to read file headers");
exit(-1);
}
lseek(output, file_header.data_size, SEEK_CUR);
}
if (!file_new)
header = perf_header__read(output);
else
header = perf_header__new();
atexit(atexit_header);
if (!system_wide) {
open_counters(-1, target_pid != -1 ? target_pid : getpid());
pid = target_pid;
if (pid == -1)
pid = getpid();
open_counters(-1, pid);
} else for (i = 0; i < nr_cpus; i++)
open_counters(i, target_pid);
if (file_new)
perf_header__write(header, output);
if (!system_wide) {
pid_synthesize_comm_event(pid, 0);
pid_synthesize_mmap_samples(pid);
} else
synthesize_all();
if (target_pid == -1 && argc) {
pid = fork();
if (pid < 0)
@ -538,9 +571,6 @@ static int __cmd_record(int argc, const char **argv)
}
}
if (system_wide)
synthesize_samples();
while (!done) {
int hits = samples;

View file

@ -17,6 +17,7 @@
#include "util/string.h"
#include "perf.h"
#include "util/header.h"
#include "util/parse-options.h"
#include "util/parse-events.h"
@ -1385,13 +1386,27 @@ process_event(event_t *event, unsigned long offset, unsigned long head)
return 0;
}
static struct perf_file_header file_header;
static struct perf_header *header;
static int perf_header__has_sample(u64 sample_mask)
{
int i;
for (i = 0; i < header->attrs; i++) {
struct perf_header_attr *attr = header->attr[i];
if (!(attr->attr.sample_type & sample_mask))
return 0;
}
return 1;
}
static int __cmd_report(void)
{
int ret, rc = EXIT_FAILURE;
unsigned long offset = 0;
unsigned long head = sizeof(file_header);
unsigned long head, shift;
struct stat stat;
event_t *event;
uint32_t size;
@ -1419,13 +1434,11 @@ static int __cmd_report(void)
exit(0);
}
if (read(input, &file_header, sizeof(file_header)) == -1) {
perror("failed to read file headers");
exit(-1);
}
header = perf_header__read(input);
head = header->data_offset;
if (sort__has_parent &&
!(file_header.sample_type & PERF_SAMPLE_CALLCHAIN)) {
!perf_header__has_sample(PERF_SAMPLE_CALLCHAIN)) {
fprintf(stderr, "selected --sort parent, but no callchain data\n");
exit(-1);
}
@ -1445,6 +1458,11 @@ static int __cmd_report(void)
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);
@ -1461,9 +1479,10 @@ static int __cmd_report(void)
size = 8;
if (head + event->header.size >= page_size * mmap_window) {
unsigned long shift = page_size * (head / page_size);
int ret;
shift = page_size * (head / page_size);
ret = munmap(buf, page_size * mmap_window);
assert(ret == 0);
@ -1501,7 +1520,7 @@ static int __cmd_report(void)
head += size;
if (offset + head >= sizeof(file_header) + file_header.data_size)
if (offset + head >= header->data_offset + header->data_size)
goto done;
if (offset + head < stat.st_size)

View file

@ -19,7 +19,7 @@
#include <sys/syscall.h>
#include "../../include/linux/perf_counter.h"
#include "types.h"
#include "util/types.h"
/*
* prctl(PR_TASK_PERF_COUNTERS_DISABLE) will (cheaply) disable all
@ -66,10 +66,4 @@ sys_perf_counter_open(struct perf_counter_attr *attr,
#define MAX_COUNTERS 256
#define MAX_NR_CPUS 256
struct perf_file_header {
u64 version;
u64 sample_type;
u64 data_size;
};
#endif

242
tools/perf/util/header.c Normal file
View file

@ -0,0 +1,242 @@
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include "util.h"
#include "header.h"
/*
*
*/
struct perf_header_attr *perf_header_attr__new(struct perf_counter_attr *attr)
{
struct perf_header_attr *self = malloc(sizeof(*self));
if (!self)
die("nomem");
self->attr = *attr;
self->ids = 0;
self->size = 1;
self->id = malloc(sizeof(u64));
if (!self->id)
die("nomem");
return self;
}
void perf_header_attr__add_id(struct perf_header_attr *self, u64 id)
{
int pos = self->ids;
self->ids++;
if (self->ids > self->size) {
self->size *= 2;
self->id = realloc(self->id, self->size * sizeof(u64));
if (!self->id)
die("nomem");
}
self->id[pos] = id;
}
/*
*
*/
struct perf_header *perf_header__new(void)
{
struct perf_header *self = malloc(sizeof(*self));
if (!self)
die("nomem");
self->frozen = 0;
self->attrs = 0;
self->size = 1;
self->attr = malloc(sizeof(void *));
if (!self->attr)
die("nomem");
self->data_offset = 0;
self->data_size = 0;
return self;
}
void perf_header__add_attr(struct perf_header *self,
struct perf_header_attr *attr)
{
int pos = self->attrs;
if (self->frozen)
die("frozen");
self->attrs++;
if (self->attrs > self->size) {
self->size *= 2;
self->attr = realloc(self->attr, self->size * sizeof(void *));
if (!self->attr)
die("nomem");
}
self->attr[pos] = attr;
}
static const char *__perf_magic = "PERFFILE";
#define PERF_MAGIC (*(u64 *)__perf_magic)
struct perf_file_section {
u64 offset;
u64 size;
};
struct perf_file_attr {
struct perf_counter_attr attr;
struct perf_file_section ids;
};
struct perf_file_header {
u64 magic;
u64 size;
u64 attr_size;
struct perf_file_section attrs;
struct perf_file_section data;
};
static void do_write(int fd, void *buf, size_t size)
{
while (size) {
int ret = write(fd, buf, size);
if (ret < 0)
die("failed to write");
size -= ret;
buf += ret;
}
}
void perf_header__write(struct perf_header *self, int fd)
{
struct perf_file_header f_header;
struct perf_file_attr f_attr;
struct perf_header_attr *attr;
int i;
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);
do_write(fd, attr->id, attr->ids * sizeof(u64));
}
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),
}
};
do_write(fd, &f_attr, sizeof(f_attr));
}
self->data_offset = lseek(fd, 0, SEEK_CUR);
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,
},
};
lseek(fd, 0, SEEK_SET);
do_write(fd, &f_header, sizeof(f_header));
lseek(fd, self->data_offset + self->data_size, SEEK_SET);
self->frozen = 1;
}
static void do_read(int fd, void *buf, size_t size)
{
while (size) {
int ret = read(fd, buf, size);
if (ret < 0)
die("failed to read");
size -= ret;
buf += ret;
}
}
struct perf_header *perf_header__read(int fd)
{
struct perf_header *self = perf_header__new();
struct perf_file_header f_header;
struct perf_file_attr f_attr;
u64 f_id;
int nr_attrs, nr_ids, i, j;
lseek(fd, 0, SEEK_SET);
do_read(fd, &f_header, sizeof(f_header));
if (f_header.magic != PERF_MAGIC ||
f_header.size != sizeof(f_header) ||
f_header.attr_size != sizeof(f_attr))
die("incompatible file format");
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 = lseek(fd, 0, SEEK_CUR);
do_read(fd, &f_attr, sizeof(f_attr));
attr = perf_header_attr__new(&f_attr.attr);
nr_ids = f_attr.ids.size / sizeof(u64);
lseek(fd, f_attr.ids.offset, SEEK_SET);
for (j = 0; j < nr_ids; j++) {
do_read(fd, &f_id, sizeof(f_id));
perf_header_attr__add_id(attr, f_id);
}
perf_header__add_attr(self, attr);
lseek(fd, tmp, SEEK_SET);
}
self->data_offset = f_header.data.offset;
self->data_size = f_header.data.size;
lseek(fd, self->data_offset + self->data_size, SEEK_SET);
self->frozen = 1;
return self;
}

37
tools/perf/util/header.h Normal file
View file

@ -0,0 +1,37 @@
#ifndef _PERF_HEADER_H
#define _PERF_HEADER_H
#include "../../../include/linux/perf_counter.h"
#include <sys/types.h>
#include "types.h"
struct perf_header_attr {
struct perf_counter_attr attr;
int ids, size;
u64 *id;
off_t id_offset;
};
struct perf_header {
int frozen;
int attrs, size;
struct perf_header_attr **attr;
off_t attr_offset;
u64 data_offset;
u64 data_size;
};
struct perf_header *perf_header__read(int fd);
void perf_header__write(struct perf_header *self, int fd);
void perf_header__add_attr(struct perf_header *self,
struct perf_header_attr *attr);
struct perf_header_attr *
perf_header_attr__new(struct perf_counter_attr *attr);
void perf_header_attr__add_id(struct perf_header_attr *self, u64 id);
struct perf_header *perf_header__new(void);
#endif /* _PERF_HEADER_H */

View file

@ -1,7 +1,7 @@
#ifndef _PERF_STRING_H_
#define _PERF_STRING_H_
#include "../types.h"
#include "types.h"
int hex2u64(const char *ptr, u64 *val);

View file

@ -2,7 +2,7 @@
#define _PERF_SYMBOL_ 1
#include <linux/types.h>
#include "../types.h"
#include "types.h"
#include "list.h"
#include "rbtree.h"