overlay: Add graph for GPU power consumption

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
This commit is contained in:
Chris Wilson 2013-08-20 10:04:23 +01:00
parent baa5be07d6
commit 5c81cda0ff
7 changed files with 188 additions and 51 deletions

View File

@ -25,6 +25,8 @@ intel_gpu_overlay_SOURCES = \
gpu-freq.c \
igfx.h \
igfx.c \
power.h \
power.c \
rc6.h \
rc6.c \
$(NULL)

View File

@ -108,6 +108,8 @@ static void chart_update_range(struct chart *chart)
else if (chart->samples[n] > chart->range[1])
chart->range[1] = chart->samples[n];
}
if (strcmp(chart->name, "power") == 0)
printf ("chart_update_range [%f, %f]\n", chart->range[0], chart->range[1]);
}
static double value_at(struct chart *chart, int n)

View File

@ -15,37 +15,40 @@ int gpu_freq_init(struct gpu_freq *gf)
fd = open("/sys/kernel/debug/dri/0/i915_cur_delayinfo", 0);
if (fd < 0)
return errno;
return gf->error = errno;
len = read(fd, buf, sizeof(buf)-1);
close(fd);
if (len < 0)
return EIO;
goto err;
buf[len] = '\0';
s = strstr(buf, "(RPN)");
if (s == NULL)
return EIO;
goto err;
sscanf(s, "(RPN) frequency: %dMHz", &gf->rpn);
s = strstr(s, "(RP1)");
if (s == NULL)
return EIO;
goto err;
sscanf(s, "(RP1) frequency: %dMHz", &gf->rp1);
s = strstr(s, "(RP0)");
if (s == NULL)
return EIO;
goto err;
sscanf(s, "(RP0) frequency: %dMHz", &gf->rp0);
s = strstr(s, "Max");
if (s == NULL)
return EIO;
goto err;
sscanf(s, "Max overclocked frequency: %dMHz", &gf->max);
gf->min = gf->rpn;
return 0;
err:
return gf->error = EIO;
}
int gpu_freq_update(struct gpu_freq *gf)
@ -53,14 +56,17 @@ int gpu_freq_update(struct gpu_freq *gf)
char buf[4096], *s;
int fd, len = -1;
if (gf->error)
return gf->error;
fd = open("/sys/kernel/debug/dri/0/i915_cur_delayinfo", 0);
if (fd < 0)
return errno;
return gf->error = errno;
len = read(fd, buf, sizeof(buf)-1);
close(fd);
if (len < 0)
return EIO;
return gf->error = EIO;
buf[len] = '\0';

View File

@ -3,6 +3,7 @@ struct gpu_freq {
int rpn, rp1, rp0;
int request;
int current;
int error;
};
int gpu_freq_init(struct gpu_freq *gf);

View File

@ -17,6 +17,7 @@
#include "gpu-freq.h"
#include "gpu-top.h"
#include "gpu-perf.h"
#include "power.h"
#include "rc6.h"
const cairo_user_data_key_t overlay_key;
@ -73,9 +74,11 @@ struct overlay_gpu_perf {
struct overlay_gpu_freq {
struct gpu_freq gpu_freq;
struct rc6 rc6;
struct power power;
struct chart current;
struct chart request;
int error;
struct chart power_chart;
double power_range[2];
};
struct overlay_gem_objects {
@ -387,27 +390,34 @@ static void show_gpu_perf(struct overlay_context *ctx, struct overlay_gpu_perf *
static void init_gpu_freq(struct overlay_context *ctx,
struct overlay_gpu_freq *gf)
{
gf->error = gpu_freq_init(&gf->gpu_freq);
if (gf->error)
return;
if (gpu_freq_init(&gf->gpu_freq) == 0) {
chart_init(&gf->current, "current", 120);
chart_set_position(&gf->current, 12, ctx->height/2 + 6);
chart_set_size(&gf->current, ctx->width/2 - 18, ctx->height/2 - 18);
chart_set_stroke_rgba(&gf->current, 0.75, 0.25, 0.50, 1.);
chart_set_mode(&gf->current, CHART_STROKE);
chart_set_smooth(&gf->current, CHART_LINE);
chart_set_range(&gf->current, 0, gf->gpu_freq.max);
chart_init(&gf->request, "request", 120);
chart_set_position(&gf->request, 12, ctx->height/2 + 6);
chart_set_size(&gf->request, ctx->width/2 - 18, ctx->height/2 - 18);
chart_set_fill_rgba(&gf->request, 0.25, 0.25, 0.50, 1.);
chart_set_mode(&gf->request, CHART_FILL);
chart_set_smooth(&gf->request, CHART_LINE);
chart_set_range(&gf->request, 0, gf->gpu_freq.max);
}
if (power_init(&gf->power) == 0) {
chart_init(&gf->power_chart, "power", 120);
chart_set_position(&gf->power_chart, 12, ctx->height/2 + 6);
chart_set_size(&gf->power_chart, ctx->width/2 - 18, ctx->height/2 - 18);
chart_set_stroke_rgba(&gf->power_chart, 0.45, 0.55, 0.45, 1.);
memset(gf->power_range, 0, sizeof(gf->power_range));
}
rc6_init(&gf->rc6);
chart_init(&gf->current, "current", 120);
chart_set_position(&gf->current, 12, ctx->height/2 + 6);
chart_set_size(&gf->current, ctx->width/2 - 18, ctx->height/2 - 18);
chart_set_stroke_rgba(&gf->current, 0.75, 0.25, 0.50, 1.);
chart_set_mode(&gf->current, CHART_STROKE);
chart_set_smooth(&gf->current, CHART_LINE);
chart_set_range(&gf->current, 0, gf->gpu_freq.max);
chart_init(&gf->request, "request", 120);
chart_set_position(&gf->request, 12, ctx->height/2 + 6);
chart_set_size(&gf->request, ctx->width/2 - 18, ctx->height/2 - 18);
chart_set_fill_rgba(&gf->request, 0.25, 0.25, 0.50, 1.);
chart_set_mode(&gf->request, CHART_FILL);
chart_set_smooth(&gf->request, CHART_LINE);
chart_set_range(&gf->request, 0, gf->gpu_freq.max);
}
static void show_gpu_freq(struct overlay_context *ctx, struct overlay_gpu_freq *gf)
@ -415,32 +425,30 @@ static void show_gpu_freq(struct overlay_context *ctx, struct overlay_gpu_freq *
char buf[160];
int y, len;
if (gf->error == 0)
gf->error = gpu_freq_update(&gf->gpu_freq);
if (gf->error)
return;
if (gf->gpu_freq.current)
chart_add_sample(&gf->current, gf->gpu_freq.current);
if (gf->gpu_freq.request)
chart_add_sample(&gf->request, gf->gpu_freq.request);
chart_draw(&gf->request, ctx->cr);
chart_draw(&gf->current, ctx->cr);
y = ctx->height/2 + 6 + 12 - 2;
len = sprintf(buf, "Frequency: %dMHz", gf->gpu_freq.current);
if (gf->gpu_freq.request)
sprintf(buf + len, " (requested %dMHz)", gf->gpu_freq.request);
cairo_set_source_rgba(ctx->cr, 1, 1, 1, 1);
cairo_move_to(ctx->cr, 12, y);
cairo_show_text(ctx->cr, buf);
y += 14;
sprintf(buf, "min: %dMHz, max: %dMHz", gf->gpu_freq.min, gf->gpu_freq.max);
cairo_move_to(ctx->cr, 12, y);
cairo_show_text(ctx->cr, buf);
y += 14;
if (gpu_freq_update(&gf->gpu_freq) == 0) {
if (gf->gpu_freq.current)
chart_add_sample(&gf->current, gf->gpu_freq.current);
if (gf->gpu_freq.request)
chart_add_sample(&gf->request, gf->gpu_freq.request);
chart_draw(&gf->request, ctx->cr);
chart_draw(&gf->current, ctx->cr);
len = sprintf(buf, "Frequency: %dMHz", gf->gpu_freq.current);
if (gf->gpu_freq.request)
sprintf(buf + len, " (requested %dMHz)", gf->gpu_freq.request);
cairo_set_source_rgba(ctx->cr, 1, 1, 1, 1);
cairo_move_to(ctx->cr, 12, y);
cairo_show_text(ctx->cr, buf);
y += 14;
sprintf(buf, "min: %dMHz, max: %dMHz", gf->gpu_freq.min, gf->gpu_freq.max);
cairo_move_to(ctx->cr, 12, y);
cairo_show_text(ctx->cr, buf);
y += 14;
}
if (rc6_update(&gf->rc6) == 0) {
sprintf(buf, "RC6: %d%%", gf->rc6.rc6_combined);
@ -453,6 +461,21 @@ static void show_gpu_freq(struct overlay_context *ctx, struct overlay_gpu_freq *
}
y += 14;
}
if (power_update(&gf->power) == 0) {
chart_add_sample(&gf->power_chart, gf->power.power_mW);
if (gf->power.new_sample) {
chart_get_range(&gf->power_chart, gf->power_range);
chart_set_range(&gf->power_chart, gf->power_range[0], gf->power_range[1]);
gf->power.new_sample = 0;
}
chart_draw(&gf->power_chart, ctx->cr);
sprintf(buf, "Power: %llumW", (long long unsigned)gf->power.power_mW);
cairo_move_to(ctx->cr, 12, y);
cairo_show_text(ctx->cr, buf);
y += 14;
}
}
static void init_gem_objects(struct overlay_context *ctx,

86
overlay/power.c Normal file
View File

@ -0,0 +1,86 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <errno.h>
#include "power.h"
/* XXX Is this exposed through RAPL? */
int power_init(struct power *power)
{
char buf[4096];
int fd, len;
memset(power, 0, sizeof(*power));
fd = open("/sys/kernel/debug/dri/0/i915_energy_uJ", 0);
if (fd < 0)
return power->error = errno;
len = read(fd, buf, sizeof(buf));
close(fd);
if (len < 0)
return power->error = errno;
return 0;
}
static uint64_t file_to_u64(const char *path)
{
char buf[4096];
int fd, len;
fd = open(path, 0);
if (fd < 0)
return 0;
len = read(fd, buf, sizeof(buf)-1);
close(fd);
if (len < 0)
return 0;
buf[len] = '\0';
return strtoull(buf, 0, 0);
}
static uint64_t clock_ms_to_u64(void)
{
struct timespec tv;
if (clock_gettime(CLOCK_MONOTONIC, &tv) < 0)
return 0;
return (uint64_t)tv.tv_sec * 1000 + tv.tv_nsec / 10000000;
}
int power_update(struct power *power)
{
struct power_stat *s = &power->stat[power->count++&1];
struct power_stat *d = &power->stat[power->count&1];
uint64_t d_time;
if (power->error)
return power->error;
s->energy = file_to_u64("/sys/kernel/debug/dri/0/i915_energy_uJ");
s->timestamp = clock_ms_to_u64();
if (power->count == 1)
return EAGAIN;
d_time = s->timestamp - d->timestamp;
if (d_time < 1200) { /* HW sample rate seems to be stable ~1Hz */
power->count--;
return power->count <= 1 ? EAGAIN : 0;
}
power->power_mW = (s->energy - d->energy) / d_time;
power->new_sample = 1;
return 0;
}

17
overlay/power.h Normal file
View File

@ -0,0 +1,17 @@
#include <stdint.h>
struct power {
struct power_stat {
uint64_t energy;
uint64_t timestamp;
} stat[2];
int error;
int count;
int new_sample;
uint64_t power_mW;
};
int power_init(struct power *power);
int power_update(struct power *power);