summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2013-08-20 10:04:23 +0100
committerChris Wilson <chris@chris-wilson.co.uk>2013-08-20 10:26:23 +0100
commit5c81cda0ff092a13c6a1eb24149e7bf98e7242fa (patch)
treed04fc37c213aed9ea124427469298f3fad3dc206
parentbaa5be07d6652bcd86353d25188505cb0199450a (diff)
overlay: Add graph for GPU power consumption
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
-rw-r--r--overlay/Makefile.am2
-rw-r--r--overlay/chart.c2
-rw-r--r--overlay/gpu-freq.c22
-rw-r--r--overlay/gpu-freq.h1
-rw-r--r--overlay/overlay.c105
-rw-r--r--overlay/power.c86
-rw-r--r--overlay/power.h17
7 files changed, 186 insertions, 49 deletions
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 <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;
+}
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 <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);