mirror of
https://github.com/tiagovignatti/intel-gpu-tools.git
synced 2025-07-24 02:16:03 +00:00
overlay: Add graph for GPU power consumption
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
This commit is contained in:
parent
baa5be07d6
commit
5c81cda0ff
@ -25,6 +25,8 @@ intel_gpu_overlay_SOURCES = \
|
||||
gpu-freq.c \
|
||||
igfx.h \
|
||||
igfx.c \
|
||||
power.h \
|
||||
power.c \
|
||||
rc6.h \
|
||||
rc6.c \
|
||||
$(NULL)
|
||||
|
@ -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)
|
||||
|
@ -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';
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
86
overlay/power.c
Normal 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
17
overlay/power.h
Normal 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);
|
Loading…
x
Reference in New Issue
Block a user