[igt-dev] [PATCH igt] RFC tools: capture execution pathways
Joonas Lahtinen
joonas.lahtinen at linux.intel.com
Mon Apr 23 11:29:21 UTC 2018
Two typos: s/sortest/shortest/, s/asci85/ascii85/
Other than that, makes sense. Did not dive deep into the utility funcs
or validity of the algorithms.
Anything stopping to polish and merge?
Regards, Joonas
Quoting Chris Wilson (2018-04-21 00:50:33)
> igt_kcov edges -o edges.yaml command-to-trace args
> ...
> igt_kcov sort -o sorted.yaml edges.yaml
>
> Requires CONFIG_KCOV
>
> Use LD_PRELOAD to wrap calls to DRM ioctls and capture the execution
> trace of all basic blocks invoked directly from the syscall. (Key
> limitation!) From the chain of basic blocks, the "edges" are computed
> and the number of times each edge is hit is stored in a vector (modulo a
> large hash). The goal then is to select a series of tests that execute
> the most unique pathways in the sortest amount of time. To do this we
> compare the similarity of the coverage vectors between all commands, and
> try to preferentially pick those that are dissimilar from the rest.
>
> * Direct execution pathways from syscall is a huge limitation for
> evaluating general testing of driver pathways, but is not so huge when
> myopically focusing on the ABI as say used by mesa.
>
> ** Caveat lector. Hastily thrown together.
> ---
> tools/Makefile.am | 10 +-
> tools/igt_kcov.c | 905 +++++++++++++++++++++++++++++++++++++++++
> tools/igt_kcov_edges.c | 144 +++++++
> 3 files changed, 1058 insertions(+), 1 deletion(-)
> create mode 100644 tools/igt_kcov.c
> create mode 100644 tools/igt_kcov_edges.c
>
> diff --git a/tools/Makefile.am b/tools/Makefile.am
> index a0b016ddd..f0c2ef3df 100644
> --- a/tools/Makefile.am
> +++ b/tools/Makefile.am
> @@ -1,6 +1,7 @@
> include Makefile.sources
>
> -bin_PROGRAMS = $(tools_prog_lists)
> +bin_PROGRAMS = $(tools_prog_lists) igt_kcov
> +lib_LTLIBRARIES = igt_kcov_edges.la
>
> if HAVE_LIBDRM_INTEL
> bin_PROGRAMS += $(LIBDRM_INTEL_BIN)
> @@ -30,6 +31,13 @@ intel_aubdump_la_LIBADD = $(top_builddir)/lib/libintel_tools.la -ldl
>
> intel_gpu_top_LDADD = $(top_builddir)/lib/libigt_perf.la
>
> +igt_kcov_SOURCES = igt_kcov.c
> +igt_kcov_LDADD = -lyaml -lz -lrt -lm
> +
> +igt_kcov_edges_la_SOURCES = igt_kcov_edges.c
> +igt_kcov_edges_la_LDFLAGS = -module -no-undefined -avoid-version
> +igt_kcov_edges_la_LIBADD = -ldl
> +
> bin_SCRIPTS = intel_aubdump
> CLEANFILES = $(bin_SCRIPTS)
>
> diff --git a/tools/igt_kcov.c b/tools/igt_kcov.c
> new file mode 100644
> index 000000000..9fb522c20
> --- /dev/null
> +++ b/tools/igt_kcov.c
> @@ -0,0 +1,905 @@
> +#include <sys/types.h>
> +#include <sys/mman.h>
> +#include <sys/stat.h>
> +#include <sys/wait.h>
> +
> +#include <ctype.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <getopt.h>
> +#include <inttypes.h>
> +#include <math.h>
> +#include <stdbool.h>
> +#include <stdlib.h>
> +#include <stdint.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <time.h>
> +#include <unistd.h>
> +
> +#include <yaml.h>
> +#include <zlib.h>
> +
> +#define SZ_8 (1 << 16)
> +#define SZ_32 (sizeof(uint32_t) * SZ_8)
> +
> +static pid_t child = 0;
> +
> +static void sighandler(int sig)
> +{
> + kill(sig, child);
> +}
> +
> +static uint64_t gettime(void)
> +{
> + struct timespec ts;
> +
> + clock_gettime(CLOCK_MONOTONIC, &ts);
> +
> + return ts.tv_sec * 1000000000 + ts.tv_nsec;
> +}
> +
> +static int edges_exec(const char *preload, char **argv, uint64_t *elapsed)
> +{
> + char buf[10];
> + int status;
> + int shm;
> + int ret;
> +
> + if (access("/sys/kernel/debug/kcov", W_OK)) {
> + fprintf(stderr, "kcov not found in debugfs\n");
> + return -ENOENT;
> + }
> +
> + shm = memfd_create("igt_kcov", 0);
> + if (shm == -1)
> + return -errno;
> +
> + ftruncate(shm, SZ_32);
> +
> + switch ((child = fork())) {
> + case 0: /* child */
> + sprintf(buf, "%d", shm);
> + setenv("IGT_KCOV_FD", buf, 1);
> + setenv("LD_PRELOAD", preload, 1);
> + exit(execvp(argv[0], argv));
> + break;
> +
> + case -1:
> + ret = -errno;
> + break;
> +
> + default:
> + signal(SIGINT, sighandler);
> +
> + *elapsed = -gettime();
> + do {
> + ret = waitpid(child, &status, 0);
> + if (ret == -1)
> + ret = -errno;
> + } while (ret == -EINTR);
> + *elapsed += gettime();
> +
> + signal(SIGINT, SIG_DFL);
> + child = 0;
> + }
> +
> + if (ret < 0) {
> + close(shm);
> + shm = ret;
> + }
> +
> + return shm;
> +}
> +
> +static inline unsigned long __fls(unsigned long word)
> +{
> +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86__) || defined(__x86_64__))
> + asm("bsr %1,%0"
> + : "=r" (word)
> + : "rm" (word));
> + return word;
> +#else
> + unsigned int v = 0;
> +
> + while (word >>= 1)
> + v++;
> +
> + return v;
> +#endif
> +}
> +
> +static uint8_t lower_u32(uint32_t x)
> +{
> + if (x < 16)
> + return x;
> + else
> + return (x >> (__fls(x) - 2)) + ((__fls(x) - 1) << 2);
> +}
> +
> +static uint8_t *lower_edges(int shm)
> +{
> + uint8_t *lower;
> + uint32_t *tbl;
> + unsigned int n;
> +
> + tbl = mmap(NULL, SZ_32, PROT_READ, MAP_SHARED, shm, 0);
> + if (tbl == MAP_FAILED)
> + return NULL;
> +
> + if (tbl[0] == 0) /* empty */
> + goto out;
> +
> + lower = malloc(SZ_8);
> + if (!lower)
> + goto out;
> +
> + for (n = 0; n < 1 << 16; n++)
> + lower[n] = lower_u32(tbl[n]);
> +
> +out:
> + munmap(tbl, SZ_32);
> + return lower;
> +}
> +
> +static bool ascii85_encode(uint32_t in, char *out)
> +{
> + int i;
> +
> + if (in == 0)
> + return false;
> +
> + for (i = 5; i--; ) {
> + out[i] = '!' + in % 85;
> + in /= 85;
> + }
> +
> + return true;
> +}
> +
> +static char *edges_to_ascii85(uint8_t *tbl)
> +{
> + z_stream zs;
> + uint32_t *p;
> + void *out;
> + char *str, *s;
> + int sz;
> +
> + sz = SZ_8 * 3 /2;
> + out = malloc(sz);
> + if (!out)
> + return NULL;
> +
> + memset(&zs, 0, sizeof(zs));
> + if (deflateInit(&zs, 9)) {
> + free(out);
> + return NULL;
> + }
> +
> + zs.next_in = tbl;
> + zs.avail_in = SZ_8;
> + zs.total_in = 0;
> + zs.avail_out = sz;
> + zs.total_out = 0;
> + zs.next_out = out;
> +
> + deflate(&zs, Z_FINISH);
> + deflateEnd(&zs);
> +
> + if (zs.total_out & 3)
> + memset((char *)out + zs.total_out, 0, 4 - (zs.total_out & 3));
> + zs.total_out = (zs.total_out + 3) / 4;
> +
> + str = malloc(zs.total_out * 5 + 1);
> + if (!str) {
> + free(out);
> + return NULL;
> + }
> +
> + p = out;
> + s = str;
> + for (int i = 0; i < zs.total_out; i++) {
> + if (ascii85_encode(*p++, s))
> + s += 5;
> + else
> + *s++ = 'z';
> + }
> + *s++ = '\0';
> + free(out);
> +
> + return str;
> +}
> +
> +static void edges(int argc, char **argv)
> +{
> + static const struct option longopts[] = {
> + {"output", required_argument, 0, 'o'},
> + {"preload", required_argument, 0, 'p'},
> + { NULL, 0, NULL, 0 }
> + };
> + const char *preload = "/tmp/igt_kcov_edges.so";
> + FILE *out = stdout;
> + uint64_t elapsed;
> + uint8_t *tbl;
> + char *str, *s;
> + int shm, i;
> +
> + while ((i = getopt_long(argc, argv,
> + "+o:",
> + longopts, NULL)) != -1) {
> + switch (i) {
> + case 'o':
> + if (strcmp(optarg, "-"))
> + out = fopen(optarg, "a");
> + if (!out) {
> + fprintf(stderr,
> + "Unable to open output file '%s'\n",
> + optarg);
> + exit(1);
> + }
> + break;
> + case 'p':
> + preload = optarg;
> + break;
> + }
> + }
> + argc -= optind;
> + argv += optind;
> +
> + if (argc < 1) {
> + fprintf(stderr,
> + "usage: igt_kcov edges [options] program-to-edges args...\n");
> + exit(1);
> + }
> +
> + shm = edges_exec(preload, argv, &elapsed);
> + if (shm < 0) {
> + fprintf(stderr,
> + "Execution of %s failed: err=%d\n",
> + argv[0], shm);
> + exit(1);
> + }
> +
> + tbl = lower_edges(shm);
> + close(shm);
> + if (!tbl)
> + exit(1);
> +
> + str = edges_to_ascii85(tbl);
> + fprintf(out, "---\n");
> + fprintf(out, "cmd: | \n");
> + for (i = 0; i < argc; i++) {
> + fprintf(out, " '");
> + for (s = argv[i]; *s; s++) {
> + if (*s == '\'')
> + fprintf(out, "\\\'");
> + else
> + fprintf(out, "%c", *s);
> + }
> + fprintf(out, "'");
> + }
> + fprintf(out, "\n");
> +
> + fprintf(out, "elapsed: %"PRIu64" # %.1fms\n", elapsed, 1e-6 * elapsed);
> +
> + fprintf(out, "edges: !!asci85.gz |\n");
> + i = strlen(str);
> + s = str;
> + while (i) {
> + int len = i > 70 ? 70 : i;
> + char tmp;
> +
> + tmp = s[len];
> + s[len] = '\0';
> + fprintf(out, " %s\n", s);
> + s[len] = tmp;
> +
> + s += len;
> + i -= len;
> + }
> +
> + free(str);
> +}
> +
> +static unsigned long zlib_inflate(void *in, unsigned long len,
> + void *ptr, unsigned long max)
> +{
> + struct z_stream_s zstream;
> +
> + memset(&zstream, 0, sizeof(zstream));
> +
> + zstream.next_in = in;
> + zstream.avail_in = len;
> +
> + if (inflateInit(&zstream) != Z_OK)
> + return 0;
> +
> + zstream.next_out = ptr;
> + zstream.avail_out = max;
> +
> + switch (inflate(&zstream, Z_SYNC_FLUSH)) {
> + case Z_STREAM_END:
> + case Z_OK:
> + break;
> + default:
> + zstream.total_out = 0;
> + break;
> + }
> +
> + inflateEnd(&zstream);
> + return zstream.total_out;
> +}
> +
> +static unsigned long ascii85_decode(const char *in,
> + void *ptr, unsigned long max)
> +{
> + unsigned long sz = max / sizeof(uint32_t);
> + unsigned long len = 0;
> + uint32_t *out;
> +
> + out = malloc(sz * sizeof(uint32_t));
> + if (out == NULL)
> + return 0;
> +
> + while (*in) {
> + uint32_t v = 0;
> +
> + if (isspace(*in)) {
> + in++;
> + continue;
> + }
> +
> + if (*in < '!' || *in > 'z') {
> + fprintf(stderr, "Invalid value in ascii85 block\n");
> + free(out);
> + return 0;
> + }
> +
> + if (len == sz) {
> + sz *= 2;
> + out = realloc(out, sz * sizeof(uint32_t));
> + if (out == NULL)
> + return 0;
> + }
> +
> + if (*in == 'z') {
> + in++;
> + } else {
> + v += in[0] - 33; v *= 85;
> + v += in[1] - 33; v *= 85;
> + v += in[2] - 33; v *= 85;
> + v += in[3] - 33; v *= 85;
> + v += in[4] - 33;
> + in += 5;
> + }
> + out[len++] = v;
> + }
> +
> + len = zlib_inflate(out, len * sizeof(*out), ptr, max);
> + free(out);
> +
> + return len;
> +}
> +
> +static void yaml_print_parser_error(yaml_parser_t *parser, FILE *stream)
> +{
> + switch (parser->error) {
> + case YAML_MEMORY_ERROR:
> + fprintf(stderr, "Memory error: Not enough memory for parsing\n");
> + break;
> +
> + case YAML_READER_ERROR:
> + if (parser->problem_value != -1) {
> + fprintf(stderr, "Reader error: %s: #%X at %zd\n", parser->problem,
> + parser->problem_value, parser->problem_offset);
> + } else {
> + fprintf(stderr, "Reader error: %s at %zd\n", parser->problem,
> + parser->problem_offset);
> + }
> + break;
> +
> + case YAML_SCANNER_ERROR:
> + if (parser->context) {
> + fprintf(stderr, "Scanner error: %s at line %lu, column %lu\n"
> + "%s at line %lu, column %lu\n", parser->context,
> + parser->context_mark.line+1, parser->context_mark.column+1,
> + parser->problem, parser->problem_mark.line+1,
> + parser->problem_mark.column+1);
> + } else {
> + fprintf(stderr, "Scanner error: %s at line %lu, column %lu\n",
> + parser->problem, parser->problem_mark.line+1,
> + parser->problem_mark.column+1);
> + }
> + break;
> +
> + case YAML_PARSER_ERROR:
> + if (parser->context) {
> + fprintf(stderr, "Parser error: %s at line %lu, column %lu\n"
> + "%s at line %lu, column %lu\n", parser->context,
> + parser->context_mark.line+1, parser->context_mark.column+1,
> + parser->problem, parser->problem_mark.line+1,
> + parser->problem_mark.column+1);
> + } else {
> + fprintf(stderr, "Parser error: %s at line %lu, column %lu\n",
> + parser->problem, parser->problem_mark.line+1,
> + parser->problem_mark.column+1);
> + }
> + break;
> +
> + default:
> + /* Couldn't happen. */
> + fprintf(stderr, "Internal error\n");
> + break;
> + }
> +}
> +
> +struct edges {
> + struct edges *next;
> +
> + char *command;
> + uint64_t elapsed;
> +
> + unsigned int weight;
> +
> + uint8_t tbl[SZ_8];
> +};
> +
> +struct sort {
> + struct edges *edges;
> + unsigned int count;
> +
> + unsigned int max_weight;
> + struct edges *best;
> +};
> +
> +static bool sort_parse_command(struct sort *sort,
> + struct edges *e,
> + yaml_parser_t *parser)
> +{
> + yaml_event_t ev;
> + const char *s;
> + int len;
> +
> + if (!yaml_parser_parse(parser, &ev))
> + return false;
> +
> + switch (ev.type) {
> + case YAML_SCALAR_EVENT:
> + break;
> +
> + default:
> + return false;
> + }
> +
> +
> + s = (const char *)ev.data.scalar.value;
> + len = strlen(s);
> + while (s[len - 1] == '\n')
> + len--;
> + e->command = malloc(len + 1);
> + if (e->command) {
> + memcpy(e->command, s, len);
> + e->command[len] = '\0';
> + }
> + yaml_event_delete(&ev);
> +
> + return true;
> +}
> +
> +static bool sort_parse_elapsed(struct sort *sort,
> + struct edges *e,
> + yaml_parser_t *parser)
> +{
> + yaml_event_t ev;
> + const char *s;
> +
> + if (!yaml_parser_parse(parser, &ev))
> + return false;
> +
> + switch (ev.type) {
> + case YAML_SCALAR_EVENT:
> + break;
> +
> + default:
> + return false;
> + }
> +
> + s = (const char *)ev.data.scalar.value;
> + e->elapsed = strtoull(s, NULL, 0);
> + yaml_event_delete(&ev);
> +
> + return true;
> +}
> +
> +static unsigned int bitmap_weight(const void *bitmap, unsigned int bits)
> +{
> + const uint32_t *b = bitmap;
> + unsigned int k, lim = bits / 32;
> + unsigned int w = 0;
> +
> + for (k = 0; k < lim; k++)
> + w += __builtin_popcount(b[k]);
> +
> + if (bits % 32)
> + w += __builtin_popcount(b[k] << (32 - bits % 32));
> +
> + return w;
> +}
> +
> +static bool sort_parse_edges(struct sort *sort,
> + struct edges *e,
> + yaml_parser_t *parser)
> +{
> + yaml_event_t ev;
> + const char *s;
> +
> + if (!yaml_parser_parse(parser, &ev))
> + return false;
> +
> + switch (ev.type) {
> + case YAML_SCALAR_EVENT:
> + break;
> +
> + default:
> + return false;
> + }
> +
> + s = (const char *)ev.data.scalar.value;
> + if (ascii85_decode(s, e->tbl, sizeof(e->tbl)))
> + e->weight = bitmap_weight(e->tbl, sizeof(e->tbl) * 8);
> + yaml_event_delete(&ev);
> +
> + return true;
> +}
> +
> +static bool edges_valid(const struct edges *e)
> +{
> + if (!e->command)
> + return false;
> +
> + if (!e->weight)
> + return false;
> +
> + if (!e->elapsed)
> + return false;
> +
> + return true; /* good enough at least */
> +}
> +
> +static bool sort_add_edges(struct sort *sort, struct edges *e)
> +{
> + if (!edges_valid(e))
> + return false;
> +
> + e->next = sort->edges;
> + sort->edges = e;
> +
> + if (e->weight > sort->max_weight) {
> + sort->max_weight = e->weight;
> + sort->best = e;
> + }
> +
> + sort->count++;
> +
> + return true;
> +}
> +
> +static bool sort_parse_node(struct sort *sort, yaml_parser_t *parser)
> +{
> + struct edges *e;
> + yaml_event_t ev;
> + char *s;
> +
> + e = malloc(sizeof(*e));
> + if (!e)
> + return false;
> +
> + e->weight = 0;
> +
> + do {
> + if (!yaml_parser_parse(parser, &ev))
> + goto err;
> +
> + switch (ev.type) {
> + case YAML_MAPPING_END_EVENT:
> + if (!sort_add_edges(sort, e))
> + goto err;
> +
> + return true;
> +
> + case YAML_SCALAR_EVENT:
> + break;
> +
> + default:
> + goto err;
> + }
> +
> + s = (char *)ev.data.scalar.value;
> + if (!strcmp(s, "cmd")) {
> + sort_parse_command(sort, e, parser);
> + } else if (!strcmp(s, "elapsed")) {
> + sort_parse_elapsed(sort, e, parser);
> + } else if (!strcmp(s, "edges")) {
> + sort_parse_edges(sort, e, parser);
> + } else {
> + fprintf(stderr,
> + "Unknown element in edges file: %s\n",
> + s);
> + }
> +
> + yaml_event_delete(&ev);
> + } while (1);
> +
> +err:
> + free(e);
> + return false;
> +}
> +
> +static bool sort_parse_doc(struct sort *sort, yaml_parser_t *parser)
> +{
> + yaml_event_t ev;
> +
> + do {
> + if (!yaml_parser_parse(parser, &ev))
> + return false;
> +
> + switch (ev.type) {
> + case YAML_DOCUMENT_END_EVENT:
> + return true;
> +
> + case YAML_MAPPING_START_EVENT:
> + break;
> +
> + default:
> + return false;
> + }
> + yaml_event_delete(&ev);
> +
> + if (!sort_parse_node(sort, parser))
> + return false;
> + } while(1);
> +}
> +
> +static bool sort_add(struct sort *sort, FILE *file)
> +{
> + yaml_parser_t parser;
> + bool done = false;
> + yaml_event_t ev;
> +
> + yaml_parser_initialize(&parser);
> + yaml_parser_set_input_file(&parser, file);
> +
> + memset(&ev, 0, sizeof(ev));
> + yaml_parser_parse(&parser, &ev);
> + if (ev.type != YAML_STREAM_START_EVENT) {
> + fprintf(stderr, "Parser setup failed\n");
> + return false;
> + }
> + yaml_event_delete(&ev);
> +
> + do {
> + if (!yaml_parser_parse(&parser, &ev)) {
> + fprintf(stderr, "Invalid yaml\n");
> + yaml_print_parser_error(&parser, stderr);
> + return false;
> + }
> +
> + switch (ev.type) {
> + case YAML_DOCUMENT_START_EVENT:
> + break;
> +
> + case YAML_STREAM_END_EVENT:
> + done = true;
> + break;
> +
> + default:
> + return false;
> + }
> + yaml_event_delete(&ev);
> +
> + sort_parse_doc(sort, &parser);
> + } while (!done);
> +
> + yaml_parser_delete(&parser);
> + return true;
> +}
> +
> +static double edges_similarity(const struct edges *ta, const struct edges *tb)
> +{
> + uint64_t sab, saa, sbb;
> + const uint8_t *a, *b;
> +
> + if (ta == tb)
> + return 1;
> +
> + a = ta->tbl;
> + b = tb->tbl;
> + sab = 0;
> + saa = 0;
> + sbb = 0;
> +
> + for (unsigned int i = 0; i < SZ_8; i++) {
> + sab += (uint32_t)a[i] * b[i];
> + saa += (uint32_t)a[i] * a[i];
> + sbb += (uint32_t)b[i] * b[i];
> + }
> +
> + return sab / (sqrt(saa) * sqrt(sbb));
> +}
> +
> +static void rank_by_dissimilarity(struct sort *sort, FILE *out)
> +{
> + const unsigned int count = sort->count;
> + double *M, *dis, *sim;
> + struct edges **edges, **t;
> + bool *used;
> + int last = -1;
> +
> + t = edges = malloc(count * sizeof(*edges));
> + for (struct edges *e = sort->edges; e; e = e->next)
> + *t++ = e;
> +
> + M = malloc(sizeof(double) * count * (count + 2));
> + dis = M + count * count;
> + sim = dis + count;
> + for (int i = 0; i < count; i++) {
> + dis[i] = 0.;
> + sim[i] = 1.;
> + for (int j = 0; j < i; j++)
> + dis[i] += M[j*count + i];
> + for (int j = i + 1; j < count; j++) {
> + double s = edges_similarity(edges[i], edges[j]);
> + s = 1. - s*s;
> + M[i*count + j] = s;
> + dis[i] += s;
> + }
> + }
> +
> + fprintf(out, "---\n");
> +
> + used = calloc(count, sizeof(bool));
> + for (int rank = 0; rank < count; rank++) {
> + struct edges *e;
> + double best = -HUGE_VAL;
> + int this = -1;
> +
> + for (int i = 0; i < count; i++) {
> + double d;
> +
> + if (used[i])
> + continue;
> +
> + d = dis[i];
> + if (last != -1) {
> + double s;
> +
> + if (last < i)
> + s = M[last * count + i];
> + else
> + s = M[i * count + last];
> +
> + s *= sim[i];
> + sim[i] = s;
> +
> + d *= sqrt(s);
> + }
> + if (d > best) {
> + best = d;
> + this = i;
> + }
> + }
> +
> + e = edges[this];
> + used[this] = true;
> + last = this;
> +
> + fprintf(out, "- cmd: |\n %s\n", e->command);
> + fprintf(out, " elapsed: %"PRIu64" # %.1fms\n",
> + e->elapsed, 1e-6 * e->elapsed);
> + fprintf(out, " dissimilarity: %f\n",
> + sqrt(best / (count - rank)));
> + }
> +
> + free(M);
> + free(edges);
> +}
> +
> +static void sort(int argc, char **argv)
> +{
> + static const struct option longopts[] = {
> + {"output", required_argument, 0, 'o'},
> + { NULL, 0, NULL, 0 }
> + };
> + FILE *out = stdout;
> + struct sort sort;
> + int i;
> +
> + while ((i = getopt_long(argc, argv,
> + "+o:",
> + longopts, NULL)) != -1) {
> + switch (i) {
> + case 'o':
> + if (strcmp(optarg, "-"))
> + out = fopen(optarg, "a");
> + if (!out) {
> + fprintf(stderr,
> + "Unable to open output file '%s'\n",
> + optarg);
> + exit(1);
> + }
> + break;
> + }
> + }
> + argc -= optind;
> + argv += optind;
> +
> + memset(&sort, 0, sizeof(sort));
> +
> + if (argc == 0) {
> + sort_add(&sort, stdin);
> + } else {
> + for (i = 0; i < argc; i++) {
> + FILE *in;
> +
> + fprintf(stderr, "%d: %s\n", i, argv[i]);
> + in = fopen(argv[i], "r");
> + if (!in) {
> + fprintf(stderr, "unable to open input '%s'\n",
> + argv[i]);
> + exit(1);
> + }
> +
> + sort_add(&sort, in);
> +
> + fclose(in);
> + }
> + }
> +
> + if (!sort.count)
> + return;
> +
> + printf("%d edges best, : %s, %"PRIu64" ns, weight=%d\n", sort.count, sort.best->command, sort.best->elapsed, sort.best->weight);
> +
> + rank_by_dissimilarity(&sort, out);
> +}
> +
> +int main(int argc, char **argv)
> +{
> + static const struct option longopts[] = {
> + {"verbose", no_argument, 0, 'v'},
> + { NULL, 0, NULL, 0 }
> + };
> + int o;
> +
> + while ((o = getopt_long(argc, argv,
> + "+v",
> + longopts, NULL)) != -1) {
> + switch (o) {
> + case 'v':
> + printf("Be verbose!\n");
> + break;
> + default:
> + break;
> + }
> + }
> +
> + if (optind == argc) {
> + fprintf(stderr, "no subcommand specified\n");
> + exit(1);
> + }
> +
> + argc -= optind;
> + argv += optind;
> + optind = 1;
> +
> + if (!strcmp(argv[0], "edges")) {
> + edges(argc, argv);
> + } else if (!strcmp(argv[0], "sort")) {
> + sort(argc, argv);
> + } else {
> + fprintf(stderr, "Unknown command '%s'\n", argv[0]);
> + exit(1);
> + }
> +
> + return 0;
> +}
> diff --git a/tools/igt_kcov_edges.c b/tools/igt_kcov_edges.c
> new file mode 100644
> index 000000000..0f1fef4bd
> --- /dev/null
> +++ b/tools/igt_kcov_edges.c
> @@ -0,0 +1,144 @@
> +#include <sys/ioctl.h>
> +#include <sys/mman.h>
> +#include <sys/types.h>
> +
> +#include <dlfcn.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <stdarg.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <stdint.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <pthread.h>
> +
> +#include <linux/types.h>
> +
> +static int (*libc_ioctl)(int fd, unsigned long request, void *argp);
> +
> +static struct kcov {
> + unsigned long *trace;
> + uint32_t *table;
> + int fd;
> +} kcov;
> +
> +#define KCOV_INIT_TRACE _IOR('c', 1, unsigned long)
> +#define KCOV_ENABLE _IO('c', 100)
> +#define KCOV_TRACE_PC 0
> +#define KCOV_TRACE_CMP 1
> +#define KCOV_DISABLE _IO('c', 101)
> +
> +#define DRM_IOCTL_BASE 'd'
> +
> +#define GOLDEN_RATIO_32 0x61C88647
> +#define GOLDEN_RATIO_64 0x61C8864680B583EBull
> +
> +static inline uint32_t hash_32(uint32_t val, unsigned int bits)
> +{
> + return val * GOLDEN_RATIO_32 >> (32 - bits);
> +}
> +
> +static inline uint32_t hash_64(uint64_t val, unsigned int bits)
> +{
> + return val * GOLDEN_RATIO_64 >> (64 - bits);
> +}
> +
> +#define hash_long(x, y) hash_64(x, y)
> +
> +static bool kcov_open(struct kcov *kc, unsigned long count)
> +{
> + const char *env;
> + int shm;
> +
> + env = getenv("IGT_KCOV_FD");
> + if (!env)
> + return false;
> +
> + shm = atoi(env);
> + kc->table = mmap(NULL, sizeof(uint32_t) << 16,
> + PROT_WRITE, MAP_SHARED, shm, 0);
> + close(shm);
> + if (kc->table == (uint32_t *)MAP_FAILED)
> + return false;
> +
> + kc->fd = open("/sys/kernel/debug/kcov", O_RDWR);
> + if (kc->fd < 0)
> + goto err_shm;
> +
> + if (libc_ioctl(kc->fd, KCOV_INIT_TRACE, (void *)count))
> + goto err_close;
> +
> + kc->trace = mmap(NULL, count * sizeof(unsigned long),
> + PROT_WRITE, MAP_SHARED, kc->fd, 0);
> + if (kc->trace == MAP_FAILED)
> + goto err_close;
> +
> + return true;
> +
> +err_close:
> + close(kc->fd);
> +err_shm:
> + munmap(kc->table, sizeof(uint32_t) << 16);
> + return false;
> +}
> +
> +static void kcov_enable(struct kcov *kc)
> +{
> + libc_ioctl(kc->fd, KCOV_ENABLE, KCOV_TRACE_PC);
> + __atomic_store_n(&kc->trace[0], 0, __ATOMIC_RELAXED);
> +}
> +
> +static unsigned long kcov_disable(struct kcov *kc)
> +{
> + unsigned long depth;
> +
> + depth = __atomic_load_n(&kc->trace[0], __ATOMIC_RELAXED);
> + if (libc_ioctl(kc->fd, KCOV_DISABLE, 0))
> + depth = 0;
> +
> + return depth;
> +}
> +
> +int ioctl(int fd, unsigned long request, ...)
> +{
> + static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
> + unsigned long count, n, prev;
> + va_list args;
> + void *argp;
> + int res;
> +
> + va_start(args, request);
> + argp = va_arg(args, void *);
> + va_end(args);
> +
> + if (kcov.fd < 0 || _IOC_TYPE(request) != DRM_IOCTL_BASE)
> + return libc_ioctl(fd, request, argp);
> +
> + pthread_mutex_lock(&mutex);
> + kcov_enable(&kcov);
> +
> + res = libc_ioctl(fd, request, argp);
> +
> + count = kcov_disable(&kcov);
> + prev = hash_long(kcov.trace[1], 16);
> + for (n = 2; n <= count; n++) {
> + unsigned long loc = hash_long(kcov.trace[n], 16);
> +
> + kcov.table[prev ^ loc]++;
> + prev = loc >> 1;
> + }
> + kcov.table[0] |= 1;
> + pthread_mutex_unlock(&mutex);
> +
> + return res;
> +}
> +
> +__attribute__((constructor))
> +static void init(void)
> +{
> + libc_ioctl = dlsym(RTLD_NEXT, "ioctl");
> +
> + if (!kcov_open(&kcov, 64 << 10))
> + kcov.fd = -1;
> +}
> --
> 2.17.0
>
> _______________________________________________
> igt-dev mailing list
> igt-dev at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/igt-dev
More information about the igt-dev
mailing list