overlay: Add graph for GPU power consumption
authorChris Wilson <chris@chris-wilson.co.uk>
Tue, 20 Aug 2013 09:04:23 +0000 (10:04 +0100)
committerChris Wilson <chris@chris-wilson.co.uk>
Tue, 20 Aug 2013 09:26:23 +0000 (10:26 +0100)
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
overlay/Makefile.am
overlay/chart.c
overlay/gpu-freq.c
overlay/gpu-freq.h
overlay/overlay.c
overlay/power.c [new file with mode: 0644]
overlay/power.h [new file with mode: 0644]

index cc748a7..516508b 100644 (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)
index d72fe75..979cd9e 100644 (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)
index 545ba78..23af1d4 100644 (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';
 
index 252ad95..cce63a9 100644 (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);
index 405eb5e..bf508fd 100644 (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;
+       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 (file)
index 0000000..68470a2
--- /dev/null
@@ -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 (file)
index 0000000..d77dbab
--- /dev/null
@@ -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);