From 5c81cda0ff092a13c6a1eb24149e7bf98e7242fa Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 20 Aug 2013 10:04:23 +0100 Subject: overlay: Add graph for GPU power consumption Signed-off-by: Chris Wilson --- overlay/Makefile.am | 2 + overlay/chart.c | 2 + overlay/gpu-freq.c | 22 +++++++---- overlay/gpu-freq.h | 1 + overlay/overlay.c | 105 ++++++++++++++++++++++++++++++++-------------------- overlay/power.c | 86 ++++++++++++++++++++++++++++++++++++++++++ overlay/power.h | 17 +++++++++ 7 files changed, 186 insertions(+), 49 deletions(-) create mode 100644 overlay/power.c create mode 100644 overlay/power.h diff --git a/overlay/Makefile.am b/overlay/Makefile.am index cc748a7d..516508b9 100644 --- a/overlay/Makefile.am +++ b/overlay/Makefile.am @@ -25,6 +25,8 @@ intel_gpu_overlay_SOURCES = \ gpu-freq.c \ igfx.h \ igfx.c \ + power.h \ + power.c \ rc6.h \ rc6.c \ $(NULL) diff --git a/overlay/chart.c b/overlay/chart.c index d72fe758..979cd9e4 100644 --- a/overlay/chart.c +++ b/overlay/chart.c @@ -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) diff --git a/overlay/gpu-freq.c b/overlay/gpu-freq.c index 545ba781..23af1d48 100644 --- a/overlay/gpu-freq.c +++ b/overlay/gpu-freq.c @@ -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'; diff --git a/overlay/gpu-freq.h b/overlay/gpu-freq.h index 252ad95f..cce63a98 100644 --- a/overlay/gpu-freq.h +++ b/overlay/gpu-freq.h @@ -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); diff --git a/overlay/overlay.c b/overlay/overlay.c index 405eb5ee..bf508fd9 100644 --- a/overlay/overlay.c +++ b/overlay/overlay.c @@ -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; + y = ctx->height/2 + 6 + 12 - 2; - 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); + 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); + 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; + 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; + 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, diff --git a/overlay/power.c b/overlay/power.c new file mode 100644 index 00000000..68470a2c --- /dev/null +++ b/overlay/power.c @@ -0,0 +1,86 @@ +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/overlay/power.h b/overlay/power.h new file mode 100644 index 00000000..d77dbabc --- /dev/null +++ b/overlay/power.h @@ -0,0 +1,17 @@ +#include + +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); -- cgit v1.2.3