[i-g-t] tools: Add a simple rapl wrapper
diff mbox series

Message ID 20191108165046.25545-1-chris@chris-wilson.co.uk
State New
Headers show
Series
  • [i-g-t] tools: Add a simple rapl wrapper
Related show

Commit Message

Chris Wilson Nov. 8, 2019, 4:50 p.m. UTC
Run a command; print how much power rapl reported the system using.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Andi Shyti <andi.shyti@intel.com>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
 tools/igt_rapl.c  | 202 ++++++++++++++++++++++++++++++++++++++++++++++
 tools/meson.build |   5 ++
 2 files changed, 207 insertions(+)
 create mode 100644 tools/igt_rapl.c

Patch
diff mbox series

diff --git a/tools/igt_rapl.c b/tools/igt_rapl.c
new file mode 100644
index 000000000..8d985681f
--- /dev/null
+++ b/tools/igt_rapl.c
@@ -0,0 +1,202 @@ 
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <locale.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "igt_perf.h"
+
+struct parse {
+	uint64_t config, type;
+};
+
+struct rapl {
+	const char *name;
+	double scale;
+	int fd;
+};
+
+__attribute__((format(scanf,3,4)))
+static int dir_scanf(int dir, const char *attr, const char *fmt, ...)
+{
+	FILE *file;
+	int fd;
+	int ret = -1;
+
+	fd = openat(dir, attr, O_RDONLY);
+	if (fd < 0)
+		return -1;
+
+	file = fdopen(fd, "r");
+	if (file) {
+		va_list ap;
+
+		va_start(ap, fmt);
+		ret = vfscanf(file, fmt, ap);
+		va_end(ap);
+
+		fclose(file);
+	} else {
+		close(fd);
+	}
+
+	return ret;
+}
+
+static int rapl_parse(struct parse *r, const char *str, double *scale)
+{
+	locale_t locale, oldlocale;
+	bool result = true;
+	char buf[128];
+	int dir;
+
+	memset(r, 0, sizeof(*r));
+
+	dir = open("/sys/devices/power", O_RDONLY);
+	if (dir < 0)
+		return -errno;
+
+	/* Replace user environment with plain C to match kernel format */
+	locale = newlocale(LC_ALL, "C", 0);
+	oldlocale = uselocale(locale);
+
+	result &= dir_scanf(dir, "type", "%"PRIu64, &r->type) == 1;
+
+	snprintf(buf, sizeof(buf), "events/energy-%s", str);
+	result &= dir_scanf(dir, buf, "event=%"PRIx64, &r->config) == 1;
+
+	snprintf(buf, sizeof(buf), "events/energy-%s.scale", str);
+	result &= dir_scanf(dir, buf, "%lf", scale) == 1;
+
+	uselocale(oldlocale);
+	freelocale(locale);
+
+	close(dir);
+
+	if (!result)
+		return -EINVAL;
+
+	if (isnan(*scale) || !*scale)
+		return -ERANGE;
+
+	return 0;
+}
+
+static int rapl_open(int fd, const char *domain, double *scale)
+{
+	struct parse r;
+	int err;
+
+	err = rapl_parse(&r, domain, scale);
+	if (err < 0)
+		goto err;
+
+	return igt_perf_open_group(r.type, r.config, fd);
+
+err:
+	errno = 0;
+	return err;
+}
+
+static int runit(struct rapl *r, int count, int argc, char **argv)
+{
+	int ret, status;
+
+	switch (ret = fork()) {
+	case -1: return errno;
+	case 0: /* child */
+		 while (count--)
+			 close(r[count].fd);
+		 execvp(argv[0], argv);
+		 exit(1);
+		 break;
+	default: /* parent */
+		 if (wait(&status) < 0) {
+			 kill(ret, SIGKILL);
+			 return errno;
+		 }
+
+		 return status;
+	}
+}
+
+static inline double power_J(const uint64_t p0,
+			     const uint64_t p1,
+			     const double scale)
+{
+	return (p1 - p0) * scale;
+}
+
+static inline double power_s(const uint64_t t0,
+			     const uint64_t t1)
+{
+	return (t1 - t0) * 1e-9;
+}
+
+static inline double power_W(const uint64_t *s0,
+			     const uint64_t *s1,
+			     int idx,
+			     const double scale)
+{
+	return power_J(s0[idx+2], s1[idx+2], scale) / power_s(s0[1], s1[1]);
+}
+
+static int rapl_read(int fd, uint64_t *data, int count)
+{
+	if (read(fd, data, (count + 2) * sizeof(data[0])) < 0)
+		return -errno;
+
+	return 0;
+}
+
+int main(int argc, char **argv)
+{
+	static const char *domains[] = { "gpu", "pkg", /* "ram"  */};
+	uint64_t before[8] = {}, after[8] = {};
+	struct rapl r[4];
+	int count = 0;
+	int ret;
+	int i;
+
+	r[0].fd = -1;
+	for (i = 0; i < sizeof(domains)/sizeof(*domains); i++) {
+		r[count].fd = rapl_open(r[0].fd, domains[i], &r[count].scale);
+		if (r[count].fd < 0)
+			continue;
+
+		r[count++].name = domains[i];
+	}
+	if (!count) {
+		fprintf(stderr, "Unable to open any rapl power domains\n");
+		return 1;
+	}
+
+	ret = rapl_read(r[0].fd, before, count);
+	if (ret < 0) {
+		fprintf(stderr, "Unable to read perf event\n");
+		return 1;
+	}
+
+	ret = runit(r, count, argc - 1, argv + 1);
+
+	ret = rapl_read(r[0].fd, after, count);
+	if (ret < 0) {
+		fprintf(stderr, "Unable to read perf event\n");
+		return 1;
+	}
+
+	printf("time:%.2fms", power_s(before[1], after[1]) * 1e3);
+	for (i = 0; i < count; i++)
+		printf(", %s:%.2fms",
+		       r[i].name, power_W(before, after, i, r[i].scale) * 1e3);
+	printf("\n");
+
+	return ret;
+}
diff --git a/tools/meson.build b/tools/meson.build
index eecb122bc..5b462c131 100644
--- a/tools/meson.build
+++ b/tools/meson.build
@@ -100,6 +100,11 @@  executable('intel_gpu_top', 'intel_gpu_top.c',
 	   install_rpath : bindir_rpathdir,
 	   dependencies : lib_igt_perf)
 
+executable('igt_rapl', 'igt_rapl.c',
+	   install : true,
+	   install_rpath : bindir_rpathdir,
+	   dependencies : lib_igt_perf)
+
 executable('amd_hdmi_compliance', 'amd_hdmi_compliance.c',
 	   dependencies : [tool_deps],
 	   install_rpath : bindir_rpathdir,