[PATCH 1/2] remap-log: Add remap-log by Al Viro

Arkadiusz Hiler arkadiusz.hiler at intel.com
Mon Nov 27 14:39:56 UTC 2017


remap-log parses patches to build a map of changes in the source files.
The tool can use that map to remap line numbers in log files, so the
logs are easily diffable.

Signed-off-by: Arkadiusz Hiler <arkadiusz.hiler at intel.com>
---
 .gitignore  |   1 +
 Makefile    |   7 +-
 remap-log.c | 588 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 595 insertions(+), 1 deletion(-)
 create mode 100644 remap-log.c

diff --git a/.gitignore b/.gitignore
index a176bd76eef5..dc928132fe47 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
 _build
 drm-intel-flow.svg
 drm-misc-commit-flow.svg
+remap-log
 *.html
 .*
 *~
diff --git a/Makefile b/Makefile
index 10ec80f0a85f..ad7954b3af96 100644
--- a/Makefile
+++ b/Makefile
@@ -17,8 +17,10 @@ ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
 # the i18n builder cannot share the environment and doctrees with the others
 I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
 
+CFLAGS = -O2 -g -Wextra
+
 .PHONY: all
-all: drm-intel.html dim.html drm-misc.html
+all: drm-intel.html dim.html drm-misc.html remap-log
 
 %.svg: %.dot
 	dot -T svg -o $@ $<
@@ -26,6 +28,9 @@ all: drm-intel.html dim.html drm-misc.html
 %.html: %.rst
 	rst2html $< > $@
 
+remap-log: remap-log.c
+		$(CC) $(CFLAGS) -o $@ $<
+
 drm-intel.html: drm-intel.rst drm-intel-flow.svg drm-intel-timeline.rst drm-intel-timeline.json
 
 drm-misc.html: drm-misc.rst drm-misc-timeline.rst drm-misc-timeline.json drm-misc-commit-flow.svg
diff --git a/remap-log.c b/remap-log.c
new file mode 100644
index 000000000000..518e00312cd2
--- /dev/null
+++ b/remap-log.c
@@ -0,0 +1,588 @@
+/*
+ * Copyright (c) 2006, Al Viro.  All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+char *prefix1 = "a/", *prefix2 = "b/";
+char *old_prefix = "O:";
+char *user_pattern;
+
+char *line;
+
+void die(char *s)
+{
+	fprintf(stderr, "remap: %s\n", s);
+	exit(1);
+}
+
+void Enomem(void)
+{
+	die("out of memory");
+}
+
+void Eio(void)
+{
+	die("IO error");
+}
+
+enum {SIZE = 4096};
+
+char *buffer;
+int get_line(int fd)
+{
+	static char *end, *end_buffer, *next;
+	static size_t size;
+	ssize_t count;
+
+	if (!buffer) {
+		next = end = buffer = malloc(size = SIZE);
+		if (!buffer)
+			Enomem();
+		end_buffer = buffer + SIZE;
+	}
+	line = next;
+
+	while (1) {
+		if (next < end) {
+			next = memchr(next, '\n', end - next);
+			if (next) {
+				*next++ = '\0';
+				return 1;
+			}
+		}
+		if (end == end_buffer) {
+			size_t n = line - buffer;
+			if (n >= SIZE) {
+				n -= n % SIZE;
+				memmove(line - n, line, end - line);
+				line -= n;
+				end -= n;
+			} else {
+				char *p = malloc(size *= 2);
+				if (!p)
+					Enomem();
+				memcpy(p + n, line, end - line);
+				line = p + n;
+				end = p + (end - buffer);
+				free(buffer);
+				buffer = p;
+				end_buffer = p + size;
+			}
+		}
+		next = end;
+		count = read(fd, end, end_buffer - end);
+		if (!count)
+			break;
+		if (count < 0)
+			Eio();
+		end += count;
+	}
+
+	*end = '\0';
+	return line != end;
+}
+
+/* to == 0 -> deletion */
+struct range_map {
+	int from, to;
+};
+
+struct file_map {
+	char *name;
+	size_t name_len;
+	struct file_map *next;
+	char *new_name;
+	int count;
+	int allocated;
+	int last;
+	struct range_map ranges[];
+};
+
+struct file_map *alloc_map(char *name)
+{
+	struct file_map *map;
+
+	map = malloc(sizeof(struct file_map) + 16 * sizeof(struct range_map));
+	if (!map)
+		Enomem();
+	map->name_len = strlen(name);
+	map->name = map->new_name = malloc(map->name_len + 1);
+	if (!map->name)
+		Enomem();
+	memcpy(map->name, name, map->name_len + 1);
+	map->count = 0;
+	map->allocated = 16;
+	map->next = NULL;
+	map->last = 0;
+	return map;
+}
+
+/* this is 32bit FNV1 */
+uint32_t FNV_hash(char *name, size_t len)
+{
+	uint32_t n = 0x811c9dc5;
+	while (len--) {
+		unsigned char c = *name++;
+		n *= 0x01000193;
+		n ^= c;
+	}
+	return n;
+}
+
+struct file_map *hash[1024];
+
+int hash_map(struct file_map *map)
+{
+	size_t len = map->name_len;
+	char *name = map->name;
+	int n = FNV_hash(name, len) % 1024;
+	struct file_map **p = &hash[n];
+
+	while (*p) {
+		if ((*p)->name_len == len && !memcmp((*p)->name, name, len))
+			return 0;
+		p = &(*p)->next;
+	}
+	*p = map;
+	if (map->new_name && !map->count)
+		return 0;
+	if (map->new_name && map->ranges[0].from != 1)
+		return 0;
+	return 1;
+}
+
+struct file_map *find_map(char *name, size_t len)
+{
+	static struct file_map *last = NULL;
+	int n = FNV_hash(name, len) % 1024;
+	struct file_map *p;
+
+	if (last && last->name_len == len && !memcmp(last->name, name, len))
+		return last;
+
+	for (p = hash[n]; p; p = p->next)
+		if (p->name_len == len && !memcmp(p->name, name, len))
+			break;
+	if (p)
+		last = p;
+	return p;
+}
+
+void parse_map(char *name)
+{
+	struct file_map *map = NULL;
+	struct range_map *range;
+	char *s;
+	int fd;
+
+	fd = open(name, O_RDONLY);
+	if (fd < 0)
+		die("can't open map");
+	while (get_line(fd)) {
+		if (line[0] == 'D') {
+			if (map && !hash_map(map))
+				goto Ebadmap;
+			if (line[1] != ' ')
+				goto Ebadmap;
+			if (strchr(line + 2, ' '))
+				goto Ebadmap;
+			map = alloc_map(line + 2);
+			map->new_name = NULL;
+			continue;
+		}
+		if (line[0] == 'M') {
+			if (map && !hash_map(map))
+				goto Ebadmap;
+			if (line[1] != ' ')
+				goto Ebadmap;
+			s = strchr(line + 2, ' ');
+			if (!s)
+				goto Ebadmap;
+			*s++ = '\0';
+			if (strchr(s, ' '))
+				goto Ebadmap;
+			map = alloc_map(line + 2);
+			if (strcmp(line + 2, s)) {
+				map->new_name = strdup(s);
+				if (!map->new_name)
+					Enomem();
+			}
+			continue;
+		}
+		if (!map || !map->new_name)
+			goto Ebadmap;
+		if (map->count == map->allocated) {
+			int n = 2 * map->allocated;
+			map = realloc(map, sizeof(struct file_map) +
+					   n * sizeof(struct range_map));
+			if (!map)
+				Enomem();
+			map->allocated = n;
+		}
+		range = &map->ranges[map->count++];
+		if (sscanf(line, "%d %d%*c", &range->from, &range->to) != 2)
+			goto Ebadmap;
+		if (range > map->ranges && range->from <= range[-1].from)
+			goto Ebadmap;
+	}
+	if (map && !hash_map(map))
+		goto Ebadmap;
+	close(fd);
+	return;
+Ebadmap:
+	die("bad map");
+}
+
+struct range_map *find_range(struct file_map *map, int l)
+{
+	struct range_map *range = &map->ranges[map->last];
+	struct range_map *p;
+
+	if (range->from <= l) {
+		p = &map->ranges[map->count - 1];
+		if (p->from > l) {
+			for (p = range; p->from <= l; p++)
+				;
+			p--;
+		}
+	} else {
+		for (p = map->ranges; p->from <= l; p++)
+			;
+		p--;
+	}
+	map->last = p - map->ranges;
+	return p;
+}
+
+char *strnstr(char *haystack, char *needle, ssize_t len)
+{
+	ssize_t nl = strlen(needle), i;
+
+	for (i = 0; i <= len - nl; i++) {
+		if (!memcmp(haystack + i, needle, nl))
+			return haystack + i;
+	}
+
+	return NULL;
+}
+
+void map_pattern(char *patt, char *start, char *end, struct file_map *map)
+{
+	size_t plen = strlen(patt);
+
+	while (end > start) {
+		struct range_map *range;
+		unsigned long l;
+		char *s, *more;
+
+		s = strnstr(start, patt, end - start);
+		if (!s)
+			break;
+
+		s += plen;
+		printf("%.*s", (int)(s - start), start);
+		start = s;
+
+		l = strtoul(s, &more, 10);
+		if (more == s || !l || l > INT_MAX)
+			continue;
+
+		if (map->new_name && (range = find_range(map, l))->to)
+			l += range->to - range->from;
+
+		printf("%lu", l);
+		start = s = more;
+	}
+
+	printf("%.*s", (int)(end - start), start);
+}
+
+void mapline(char *patt)
+{
+	char *s = line, *start = line, *end = line - 1, *sp = line - 1;
+	struct file_map *last_mapped = NULL;
+
+	while (1) {
+		struct file_map *map;
+		struct range_map *range;
+		unsigned long l;
+		char *more;
+
+		end = strchr(end + 1, ':');
+		if (!end)
+			break;
+
+		if (sp < s)
+			sp = strchr(s, ' ');
+
+		while (sp && sp < end) {
+			s = sp + 1;
+			sp = strchr(s , ' ');
+		}
+
+		l = strtoul(end + 1, &more, 10);
+		if (more == end + 1 || !l || l > INT_MAX)
+			continue;
+
+		map = find_map(s, end - s);
+		if (!map)
+			continue;
+
+		if (patt)
+			map_pattern(patt, start, s, map);
+
+		if (map->new_name && (range = find_range(map, l))->to) {
+			l += range->to - range->from;
+			printf("%s:%lu", map->new_name, l);
+		} else {
+			printf("%s%.*s", old_prefix, (int)(more - s), s);
+		}
+		start = s = more;
+		last_mapped = map;
+	}
+
+	if (patt && start != line)
+		map_pattern(patt, start, start + strlen(start), last_mapped);
+	else
+		printf("%s", start);
+
+	printf("\n");
+}
+
+int parse_hunk(int *l1, int *l2, int *n1, int *n2)
+{
+	unsigned long n;
+	char *s, *p;
+	if (line[3] != '-')
+		return 0;
+	n = strtoul(line + 4, &s, 10);
+	if (s == line + 4 || n > INT_MAX)
+		return 0;
+	*l1 = n;
+	if (*s == ',') {
+		n = strtoul(s + 1, &p, 10);
+		if (p == s + 1 || n > INT_MAX)
+			return 0;
+		*n1 = n;
+		if (!n)
+			(*l1)++;
+	} else {
+		p = s;
+		*n1 = 1;
+	}
+	if (*p != ' ' || p[1] != '+')
+		return 0;
+	n = strtoul(p + 2, &s, 10);
+	if (s == p + 2 || n > INT_MAX)
+		return 0;
+	*l2 = n;
+	if (*s == ',') {
+		n = strtoul(s + 1, &p, 10);
+		if (p == s + 1 || n > INT_MAX)
+			return 0;
+		*n2 = n;
+		if (!n)
+			(*l2)++;
+	} else {
+		p = s;
+		*n2 = 1;
+	}
+	return 1;
+}
+
+void parse_diff(void)
+{
+	int skipping = -1, suppress = 1;
+	char *name1 = NULL, *name2 = NULL;
+	int from = 1, to = 1;
+	int l1, l2, n1, n2;
+	enum cmd {
+		Diff, Hunk, New, Del, Copy, Rename, Junk
+	} cmd;
+	static struct { const char *s; size_t len; } pref[] = {
+		[Hunk] = {"@@ ", 3},
+		[Diff] = {"diff ", 5},
+		[New] = {"new file ", 9},
+		[Del] = {"deleted file ", 12},
+		[Copy] = {"copy from ", 10},
+		[Rename] = {"rename from ", 11},
+		[Junk] = {"", 0},
+	};
+	size_t len1 = strlen(prefix1), len2 = strlen(prefix2);
+
+	while (get_line(0)) {
+		if (skipping > 0) {
+			switch (line[0]) {
+			case '+':
+			case '-':
+			case '\\':
+				continue;
+			}
+		}
+		for (cmd = 0; strncmp(line, pref[cmd].s, pref[cmd].len); cmd++)
+			;
+		switch (cmd) {
+		case Hunk:
+			if (skipping < 0)
+				goto Ediff;
+			if (!suppress) {
+				if (!skipping)
+					printf("M %s %s\n", name1, name2);
+				if (!parse_hunk(&l1, &l2, &n1, &n2))
+					goto Ediff;
+				if (l1 > from)
+					printf("%d %d\n", from, to);
+				if (n1)
+					printf("%d 0\n", l1);
+				from = l1 + n1;
+				to = l2 + n2;
+			}
+			skipping = 1;
+			break;
+		case Diff:
+			if (!suppress) {
+				if (!skipping)
+					printf("M %s %s\n", name1, name2);
+				printf("%d %d\n", from, to);
+			}
+			free(name1);
+			free(name2);
+			name2 = strrchr(line, ' ');
+			if (!name2)
+				goto Ediff;
+			*name2 = '\0';
+			name1 = strrchr(line, ' ');
+			if (!name1)
+				goto Ediff;
+			if (strncmp(name1 + 1, prefix1, len1))
+				goto Ediff;
+			if (strncmp(name2 + 1, prefix2, len2))
+				goto Ediff;
+			name1 = strdup(name1 + len1 + 1);
+			name2 = strdup(name2 + len2 + 1);
+			if (!name1 || !name2)
+				goto Ediff;
+			skipping = 0;
+			suppress = 0;
+			from = to = 1;
+			break;
+		case New:
+			if (skipping)
+				goto Ediff;
+			suppress = 1;
+			break;
+		case Del:
+		case Copy:
+			if (skipping)
+				goto Ediff;
+			printf("D %s\n", name2);
+			suppress = 1;
+			break;
+		case Rename:
+			if (skipping)
+				goto Ediff;
+			printf("D %s\n", name2);
+			break;
+		default:
+			break;
+		}
+	}
+	if (!suppress) {
+		if (!skipping)
+			printf("M %s %s\n", name1, name2);
+		printf("%d %d\n", from, to);
+	}
+	return;
+Ediff:
+	die("odd diff");
+}
+
+int main(int argc, char **argv)
+{
+	char *map_name = NULL;
+	char opt;
+	char *arg;
+	size_t len;
+	for (argc--, argv++; argc; argc--, argv++) {
+		if (argv[0][0] != '-') {
+			map_name = argv[0];
+			continue;
+		}
+		opt = argv[0][1];
+		if (!opt)
+			goto Eargs;
+		arg = argv[0] + 2;
+		if (!*arg) {
+			if (!--argc)
+				goto Eargs;
+			arg = *++argv;
+		}
+		len = strlen(arg);
+		switch (opt) {
+		case 'O':
+			prefix1 = malloc(len + 2);
+			if (!prefix1)
+				Enomem();
+			memcpy(prefix1, arg, len);
+			prefix1[len] = '/';
+			prefix1[len + 1] = '\0';
+			break;
+		case 'N':
+			prefix2 = malloc(len + 2);
+			if (!prefix2)
+				Enomem();
+			memcpy(prefix2, arg, len);
+			prefix2[len] = '/';
+			prefix2[len + 1] = '\0';
+			break;
+		case 'o':
+			old_prefix = arg;
+			break;
+		case 'p':
+			user_pattern = arg;
+			break;
+		default:
+		Eargs:
+			die("bad arguments");
+		}
+	}
+
+	if (!map_name) {
+		parse_diff();
+	} else {
+		parse_map(map_name);
+		buffer = NULL;
+		while (get_line(0))
+			mapline(user_pattern);
+	}
+	return 0;
+
+}
-- 
2.13.6



More information about the dim-tools mailing list