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:
parent
e5c5954779
commit
7c6a1c65bb
9 changed files with 377 additions and 54 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
242
tools/perf/util/header.c
Normal 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
37
tools/perf/util/header.h
Normal 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 */
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#define _PERF_SYMBOL_ 1
|
||||
|
||||
#include <linux/types.h>
|
||||
#include "../types.h"
|
||||
#include "types.h"
|
||||
#include "list.h"
|
||||
#include "rbtree.h"
|
||||
|
||||
|
|
Loading…
Reference in a new issue