mirror of
https://github.com/tiagovignatti/intel-gpu-tools.git
synced 2025-07-25 18:57:41 +00:00
overlay: Primitive integration with perf
We can now record when a pageflip occurs by listening for the flip tracepoint. Merely proof of principle at this point. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
This commit is contained in:
parent
06c0cc0bb4
commit
cc45a9a3db
@ -16,6 +16,7 @@ intel_gpu_overlay_SOURCES = \
|
|||||||
gem-objects.c \
|
gem-objects.c \
|
||||||
gpu-top.h \
|
gpu-top.h \
|
||||||
gpu-top.c \
|
gpu-top.c \
|
||||||
|
gpu-perf.c \
|
||||||
igfx.h \
|
igfx.h \
|
||||||
igfx.c \
|
igfx.c \
|
||||||
x11/dri2.c \
|
x11/dri2.c \
|
||||||
|
270
overlay/gpu-perf.c
Normal file
270
overlay/gpu-perf.c
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <linux/perf_event.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "gpu-perf.h"
|
||||||
|
|
||||||
|
#if defined(__i386__)
|
||||||
|
#define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__x86_64__)
|
||||||
|
#define rmb() asm volatile("lfence" ::: "memory")
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define TRACING_EVENT_PATH "/sys/kernel/debug/tracing/events"
|
||||||
|
#define N_PAGES 32
|
||||||
|
|
||||||
|
struct sample_event {
|
||||||
|
struct perf_event_header header;
|
||||||
|
uint32_t pid, tid;
|
||||||
|
uint64_t time;
|
||||||
|
uint64_t id;
|
||||||
|
uint32_t raw_size;
|
||||||
|
uint32_t raw[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
perf_event_open(struct perf_event_attr *attr,
|
||||||
|
pid_t pid,
|
||||||
|
int cpu,
|
||||||
|
int group_fd,
|
||||||
|
unsigned long flags)
|
||||||
|
{
|
||||||
|
#ifndef __NR_perf_event_open
|
||||||
|
#if defined(__i386__)
|
||||||
|
#define __NR_perf_event_open 336
|
||||||
|
#elif defined(__x86_64__)
|
||||||
|
#define __NR_perf_event_open 298
|
||||||
|
#else
|
||||||
|
#define __NR_perf_event_open 0
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
attr->size = sizeof(*attr);
|
||||||
|
return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t tracepoint_id(const char *sys, const char *name)
|
||||||
|
{
|
||||||
|
char buf[1024];
|
||||||
|
int fd, n;
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf), "%s/%s/%s/id", TRACING_EVENT_PATH, sys, name);
|
||||||
|
fd = open(buf, 0);
|
||||||
|
if (fd < 0)
|
||||||
|
return 0;
|
||||||
|
n = read(fd, buf, sizeof(buf)-1);
|
||||||
|
close(fd);
|
||||||
|
if (n < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
buf[n] = '\0';
|
||||||
|
return strtoull(buf, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int perf_tracepoint_open(struct gpu_perf *gp,
|
||||||
|
const char *sys, const char *name,
|
||||||
|
int (*func)(struct gpu_perf *, void *))
|
||||||
|
{
|
||||||
|
struct perf_event_attr attr;
|
||||||
|
struct gpu_perf_sample *sample;
|
||||||
|
int n, *fd;
|
||||||
|
|
||||||
|
memset(&attr, 0, sizeof (attr));
|
||||||
|
|
||||||
|
attr.type = PERF_TYPE_TRACEPOINT;
|
||||||
|
attr.config = tracepoint_id(sys, name);
|
||||||
|
if (attr.config == 0)
|
||||||
|
return ENOENT;
|
||||||
|
|
||||||
|
attr.sample_period = 1;
|
||||||
|
attr.sample_type = (PERF_SAMPLE_TIME | PERF_SAMPLE_STREAM_ID | PERF_SAMPLE_TID | PERF_SAMPLE_RAW);
|
||||||
|
attr.read_format = PERF_FORMAT_ID;
|
||||||
|
|
||||||
|
attr.exclude_guest = 1;
|
||||||
|
attr.mmap = gp->nr_events == 0;
|
||||||
|
attr.comm = gp->nr_events == 0;
|
||||||
|
|
||||||
|
n = gp->nr_cpus * (gp->nr_events+1);
|
||||||
|
fd = realloc(gp->fd, n*sizeof(int));
|
||||||
|
sample = realloc(gp->sample, n*sizeof(*gp->sample));
|
||||||
|
if (fd == NULL || sample == NULL)
|
||||||
|
return ENOMEM;
|
||||||
|
gp->fd = fd;
|
||||||
|
gp->sample = sample;
|
||||||
|
|
||||||
|
fd += gp->nr_events * gp->nr_cpus;
|
||||||
|
sample += gp->nr_events * gp->nr_cpus;
|
||||||
|
for (n = 0; n < gp->nr_cpus; n++) {
|
||||||
|
uint64_t track[2];
|
||||||
|
|
||||||
|
fd[n] = perf_event_open(&attr, -1, n, -1, 0);
|
||||||
|
if (fd[n] == -1)
|
||||||
|
return errno;
|
||||||
|
|
||||||
|
/* read back the event to establish id->tracepoint */
|
||||||
|
read(fd[n], track, sizeof(track));
|
||||||
|
sample[n].id = track[1];
|
||||||
|
sample[n].func = func;
|
||||||
|
|
||||||
|
if (gp->nr_events)
|
||||||
|
ioctl(fd[n], PERF_EVENT_IOC_SET_OUTPUT, gp->fd[n]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
gp->nr_events++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int perf_mmap(struct gpu_perf *gp)
|
||||||
|
{
|
||||||
|
int size = (1 + N_PAGES) * gp->page_size;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
gp->map = malloc(sizeof(void *)*gp->nr_cpus);
|
||||||
|
if (gp->map == NULL)
|
||||||
|
return ENOMEM;
|
||||||
|
|
||||||
|
for (n = 0; n < gp->nr_cpus; n++) {
|
||||||
|
gp->map[n] = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, gp->fd[n], 0);
|
||||||
|
if (gp->map[n] == (void *)-1)
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
while (--n > 0)
|
||||||
|
munmap(gp->map[n], size);
|
||||||
|
free(gp->map);
|
||||||
|
gp->map = NULL;
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int seqno_start(struct gpu_perf *gp, void *event)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int seqno_end(struct gpu_perf *gp, void *event)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int flip_complete(struct gpu_perf *gp, void *event)
|
||||||
|
{
|
||||||
|
gp->flip_complete++;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gpu_perf_init(struct gpu_perf *gp, unsigned flags)
|
||||||
|
{
|
||||||
|
memset(gp, 0, sizeof(*gp));
|
||||||
|
gp->nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
|
||||||
|
gp->page_size = getpagesize();
|
||||||
|
|
||||||
|
if (perf_tracepoint_open(gp, "i915", "i915_gem_ring_complete", seqno_end) == 0)
|
||||||
|
perf_tracepoint_open(gp, "i915", "i915_gem_ring_dispatch", seqno_start);
|
||||||
|
|
||||||
|
perf_tracepoint_open(gp, "i915", "i915_flip_complete", flip_complete);
|
||||||
|
|
||||||
|
if (gp->nr_events == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (perf_mmap(gp))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int process_sample(struct gpu_perf *gp,
|
||||||
|
struct perf_event_header *header)
|
||||||
|
{
|
||||||
|
struct sample_event *sample = (struct sample_event *)header;
|
||||||
|
int n, update = 0;
|
||||||
|
|
||||||
|
/* hash me! */
|
||||||
|
for (n = 0; n < gp->nr_cpus * gp->nr_events; n++) {
|
||||||
|
if (gp->sample[n].id != sample->id)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
update = 1;
|
||||||
|
if (gp->sample[n].func)
|
||||||
|
update = gp->sample[n].func(gp, sample);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return update;
|
||||||
|
}
|
||||||
|
|
||||||
|
int gpu_perf_update(struct gpu_perf *gp)
|
||||||
|
{
|
||||||
|
const int size = N_PAGES * gp->page_size;
|
||||||
|
const int mask = size - 1;
|
||||||
|
uint8_t *buffer = NULL;
|
||||||
|
int buffer_size = 0;
|
||||||
|
int n, update = 0;
|
||||||
|
|
||||||
|
if (gp->map == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (n = 0; n < gp->nr_cpus; n++) {
|
||||||
|
struct perf_event_mmap_page *mmap = gp->map[n];
|
||||||
|
uint8_t *data;
|
||||||
|
uint64_t head, tail;
|
||||||
|
|
||||||
|
tail = mmap->data_tail;
|
||||||
|
head = mmap->data_head;
|
||||||
|
rmb();
|
||||||
|
|
||||||
|
if (head < tail)
|
||||||
|
tail = head; /* XXX */
|
||||||
|
|
||||||
|
data = (uint8_t *)mmap + gp->page_size;
|
||||||
|
while (head - tail >= sizeof (struct perf_event_header)) {
|
||||||
|
struct perf_event_header *header;
|
||||||
|
|
||||||
|
header = (struct perf_event_header *)(data + (tail & mask));
|
||||||
|
if (header->size > head - tail)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (data + (tail & mask) + header->size > data + size) {
|
||||||
|
int before, after;
|
||||||
|
|
||||||
|
if (header->size > buffer_size) {
|
||||||
|
uint8_t *b = realloc(buffer, header->size);
|
||||||
|
if (b == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
buffer = b;
|
||||||
|
buffer_size = header->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
after = (tail & mask) + header->size - size;
|
||||||
|
before = header->size - after;
|
||||||
|
|
||||||
|
memcpy(buffer, data + (tail & mask), before);
|
||||||
|
memcpy(buffer + before, data, after);
|
||||||
|
|
||||||
|
header = (struct perf_event_header *)buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header->type == PERF_RECORD_SAMPLE)
|
||||||
|
update += process_sample(gp, header);
|
||||||
|
tail += header->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
mmap->data_tail = tail;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buffer);
|
||||||
|
return update;
|
||||||
|
}
|
18
overlay/gpu-perf.h
Normal file
18
overlay/gpu-perf.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct gpu_perf {
|
||||||
|
int page_size;
|
||||||
|
int nr_cpus;
|
||||||
|
int nr_events;
|
||||||
|
int *fd;
|
||||||
|
void **map;
|
||||||
|
struct gpu_perf_sample {
|
||||||
|
uint64_t id;
|
||||||
|
int (*func)(struct gpu_perf *, void *);
|
||||||
|
} *sample;
|
||||||
|
|
||||||
|
int flip_complete;
|
||||||
|
};
|
||||||
|
|
||||||
|
void gpu_perf_init(struct gpu_perf *gp, unsigned flags);
|
||||||
|
int gpu_perf_update(struct gpu_perf *gp);
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include "overlay.h"
|
#include "overlay.h"
|
||||||
#include "gpu-top.h"
|
#include "gpu-top.h"
|
||||||
|
#include "gpu-perf.h"
|
||||||
#include "gem-objects.h"
|
#include "gem-objects.h"
|
||||||
#include "chart.h"
|
#include "chart.h"
|
||||||
|
|
||||||
@ -140,6 +141,33 @@ static void show_gpu_top(cairo_t *cr, struct overlay_gpu_top *gt)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct overlay_gpu_perf {
|
||||||
|
struct gpu_perf gpu_perf;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void init_gpu_perf(struct overlay_gpu_perf *gp,
|
||||||
|
cairo_surface_t *surface)
|
||||||
|
{
|
||||||
|
gpu_perf_init(&gp->gpu_perf, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void show_gpu_perf(cairo_t *cr, struct overlay_gpu_perf *gp)
|
||||||
|
{
|
||||||
|
char buf[1024];
|
||||||
|
int y;
|
||||||
|
|
||||||
|
gpu_perf_update(&gp->gpu_perf);
|
||||||
|
|
||||||
|
y = 130;
|
||||||
|
|
||||||
|
sprintf(buf, "Flips: %d", gp->gpu_perf.flip_complete);
|
||||||
|
cairo_move_to(cr, 12, y);
|
||||||
|
cairo_show_text(cr, buf);
|
||||||
|
y += 14;
|
||||||
|
|
||||||
|
gp->gpu_perf.flip_complete = 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void show_gem_objects(cairo_t *cr)
|
static void show_gem_objects(cairo_t *cr)
|
||||||
{
|
{
|
||||||
char gem_objects[1024], *s, *t, *end;
|
char gem_objects[1024], *s, *t, *end;
|
||||||
@ -149,7 +177,7 @@ static void show_gem_objects(cairo_t *cr)
|
|||||||
if (len <= 0)
|
if (len <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
y = 130;
|
y = 150;
|
||||||
|
|
||||||
s = gem_objects;
|
s = gem_objects;
|
||||||
end = s + len - 1;
|
end = s + len - 1;
|
||||||
@ -171,6 +199,7 @@ int main(int argc, char **argv)
|
|||||||
{
|
{
|
||||||
cairo_surface_t *surface;
|
cairo_surface_t *surface;
|
||||||
struct overlay_gpu_top gpu_top;
|
struct overlay_gpu_top gpu_top;
|
||||||
|
struct overlay_gpu_perf gpu_perf;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
@ -183,6 +212,7 @@ int main(int argc, char **argv)
|
|||||||
return ENOMEM;
|
return ENOMEM;
|
||||||
|
|
||||||
init_gpu_top(&gpu_top, surface);
|
init_gpu_top(&gpu_top, surface);
|
||||||
|
init_gpu_perf(&gpu_perf, surface);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
cairo_t *cr;
|
cairo_t *cr;
|
||||||
@ -207,6 +237,7 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
show_gpu_top(cr, &gpu_top);
|
show_gpu_top(cr, &gpu_top);
|
||||||
|
show_gpu_perf(cr, &gpu_perf);
|
||||||
show_gem_objects(cr);
|
show_gem_objects(cr);
|
||||||
|
|
||||||
cairo_destroy(cr);
|
cairo_destroy(cr);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user