Blame SOURCES/github_6596f112_alternate_list_loop_detect.patch

58c03c
commit 6596f1121b89162f96d1e1825c2905b83b59bec1
58c03c
Author: Dave Anderson <anderson@redhat.com>
58c03c
Date:   Wed Jul 11 16:25:59 2018 -0400
58c03c
58c03c
    The existing "list" command uses a hash table to detect duplicate
58c03c
    items as it traverses the list.  The hash table approach has worked
58c03c
    well for many years.  However, with increasing memory sizes and list
58c03c
    sizes, the overhead of the hash table can be substantial, often
58c03c
    leading to commands running for a very long time.  For large lists,
58c03c
    we have found that the existing hash based approach may slow the
58c03c
    system to a crawl and possibly never complete.  You can turn off
58c03c
    the hash with "set hash off" but then there is no loop detection; in
58c03c
    that case, loop detection must be done manually after dumping the
58c03c
    list to disk or some other method.  This patch is an implementation
58c03c
    of the cycle detection algorithm from R. P. Brent as an alternative
58c03c
    algorithm for the "list" command.  The algorithm both avoids the
58c03c
    overhead of the hash table and yet is able to detect a loop.  In
58c03c
    addition, further loop characteristics are printed, such as the
58c03c
    distance to the start of the loop as well as the loop length.
58c03c
    An excellent description of the algorithm can be found here on
58c03c
    the crash-utility mailing list:
58c03c
    
58c03c
    https://www.redhat.com/archives/crash-utility/2018-July/msg00019.html
58c03c
    
58c03c
    A new "list -B" option has been added to the "list" command to
58c03c
    invoke this new algorithm rather than using the hash table.  In
58c03c
    addition to low memory usage, the output of the list command is
58c03c
    slightly different when a loop is detected.  In addition to printing
58c03c
    the first duplicate entry, the length of the loop, and the distance
58c03c
    to the loop is output.
58c03c
    (dwysocha@redhat.com)
58c03c
58c03c
diff --git a/defs.h b/defs.h
58c03c
index b05aecc..5af82be 100644
58c03c
--- a/defs.h
58c03c
+++ b/defs.h
58c03c
@@ -2491,6 +2491,7 @@ struct list_data {             /* generic structure used by do_list() to walk */
58c03c
 #define CALLBACK_RETURN     (VERBOSE << 12)
58c03c
 #define LIST_PARSE_MEMBER   (VERBOSE << 13)
58c03c
 #define LIST_READ_MEMBER    (VERBOSE << 14)
58c03c
+#define LIST_BRENT_ALGO     (VERBOSE << 15)
58c03c
 
58c03c
 struct tree_data {
58c03c
 	ulong flags;
58c03c
@@ -4944,6 +4945,7 @@ char *shift_string_right(char *, int);
58c03c
 int bracketed(char *, char *, int);
58c03c
 void backspace(int);
58c03c
 int do_list(struct list_data *);
58c03c
+int do_list_no_hash(struct list_data *);
58c03c
 struct radix_tree_ops {
58c03c
 	void (*entry)(ulong node, ulong slot, const char *path,
58c03c
 		      ulong index, void *private);
58c03c
diff --git a/help.c b/help.c
58c03c
index 638c6ec..54bf9b4 100644
58c03c
--- a/help.c
58c03c
+++ b/help.c
58c03c
@@ -5724,7 +5724,7 @@ char *help__list[] = {
58c03c
 "list",
58c03c
 "linked list",
58c03c
 "[[-o] offset][-e end][-[s|S] struct[.member[,member] [-l offset]] -[x|d]]"
58c03c
-"\n       [-r|-h|-H] start",
58c03c
+"\n       [-r|-B] [-h|-H] start",
58c03c
 " ",
58c03c
 "  This command dumps the contents of a linked list.  The entries in a linked",
58c03c
 "  list are typically data structures that are tied together in one of two",
58c03c
@@ -5822,6 +5822,12 @@ char *help__list[] = {
58c03c
 "           -r  For a list linked with list_head structures, traverse the list",
58c03c
 "               in the reverse order by using the \"prev\" pointer instead",
58c03c
 "               of \"next\".",
58c03c
+"           -B  Use the algorithm from R. P. Brent to detect loops instead of",
58c03c
+"               using a hash table.  This algorithm uses a tiny fixed amount of",
58c03c
+"               memory and so is especially helpful for longer lists.  The output",
58c03c
+"               is slightly different than the normal list output as it will",
58c03c
+"               print the length of the loop, the start of the loop, and the",
58c03c
+"               first duplicate in the list.",
58c03c
 " ",
58c03c
 "  The meaning of the \"start\" argument, which can be expressed symbolically,",
58c03c
 "  in hexadecimal format, or an expression evaluating to an address, depends",
58c03c
diff --git a/tools.c b/tools.c
58c03c
index 1a83643..634aec6 100644
58c03c
--- a/tools.c
58c03c
+++ b/tools.c
58c03c
@@ -3266,9 +3266,12 @@ cmd_list(void)
58c03c
 	BZERO(ld, sizeof(struct list_data));
58c03c
 	struct_list_offset = 0;
58c03c
 
58c03c
-	while ((c = getopt(argcnt, args, "Hhrs:S:e:o:xdl:")) != EOF) {
58c03c
+	while ((c = getopt(argcnt, args, "BHhrs:S:e:o:xdl:")) != EOF) {
58c03c
                 switch(c)
58c03c
 		{
58c03c
+		case 'B':
58c03c
+			ld->flags |= LIST_BRENT_ALGO;
58c03c
+			break;
58c03c
 		case 'H':
58c03c
 			ld->flags |= LIST_HEAD_FORMAT;
58c03c
 			ld->flags |= LIST_HEAD_POINTER;
58c03c
@@ -3516,9 +3519,13 @@ next_arg:
58c03c
 	ld->flags &= ~(LIST_OFFSET_ENTERED|LIST_START_ENTERED);
58c03c
 	ld->flags |= VERBOSE;
58c03c
 
58c03c
-	hq_open();
58c03c
-	c = do_list(ld);
58c03c
-	hq_close();
58c03c
+	if (ld->flags & LIST_BRENT_ALGO)
58c03c
+		c = do_list_no_hash(ld);
58c03c
+	else {
58c03c
+		hq_open();
58c03c
+		c = do_list(ld);
58c03c
+		hq_close();
58c03c
+	}
58c03c
 
58c03c
         if (ld->structname_args)
58c03c
 		FREEBUF(ld->structname);
58c03c
@@ -3862,6 +3869,283 @@ do_list(struct list_data *ld)
58c03c
 	return count;
58c03c
 }
58c03c
 
58c03c
+static void 
58c03c
+do_list_debug_entry(struct list_data *ld)
58c03c
+{
58c03c
+	int i, others;
58c03c
+
58c03c
+	if (CRASHDEBUG(1)) {
58c03c
+		others = 0;
58c03c
+		console("             flags: %lx (", ld->flags);
58c03c
+		if (ld->flags & VERBOSE)
58c03c
+			console("%sVERBOSE", others++ ? "|" : "");
58c03c
+		if (ld->flags & LIST_OFFSET_ENTERED)
58c03c
+			console("%sLIST_OFFSET_ENTERED", others++ ? "|" : "");
58c03c
+		if (ld->flags & LIST_START_ENTERED)
58c03c
+			console("%sLIST_START_ENTERED", others++ ? "|" : "");
58c03c
+		if (ld->flags & LIST_HEAD_FORMAT)
58c03c
+			console("%sLIST_HEAD_FORMAT", others++ ? "|" : "");
58c03c
+		if (ld->flags & LIST_HEAD_POINTER)
58c03c
+			console("%sLIST_HEAD_POINTER", others++ ? "|" : "");
58c03c
+		if (ld->flags & RETURN_ON_DUPLICATE)
58c03c
+			console("%sRETURN_ON_DUPLICATE", others++ ? "|" : "");
58c03c
+		if (ld->flags & RETURN_ON_LIST_ERROR)
58c03c
+			console("%sRETURN_ON_LIST_ERROR", others++ ? "|" : "");
58c03c
+		if (ld->flags & RETURN_ON_LIST_ERROR)
58c03c
+			console("%sRETURN_ON_LIST_ERROR", others++ ? "|" : "");
58c03c
+		if (ld->flags & LIST_STRUCT_RADIX_10)
58c03c
+			console("%sLIST_STRUCT_RADIX_10", others++ ? "|" : "");
58c03c
+		if (ld->flags & LIST_STRUCT_RADIX_16)
58c03c
+			console("%sLIST_STRUCT_RADIX_16", others++ ? "|" : "");
58c03c
+		if (ld->flags & LIST_ALLOCATE)
58c03c
+			console("%sLIST_ALLOCATE", others++ ? "|" : "");
58c03c
+		if (ld->flags & LIST_CALLBACK)
58c03c
+			console("%sLIST_CALLBACK", others++ ? "|" : "");
58c03c
+		if (ld->flags & CALLBACK_RETURN)
58c03c
+			console("%sCALLBACK_RETURN", others++ ? "|" : "");
58c03c
+		console(")\n");
58c03c
+		console("             start: %lx\n", ld->start);
58c03c
+		console("     member_offset: %ld\n", ld->member_offset);
58c03c
+		console("  list_head_offset: %ld\n", ld->list_head_offset);
58c03c
+		console("               end: %lx\n", ld->end);
58c03c
+		console("         searchfor: %lx\n", ld->searchfor);
58c03c
+		console("   structname_args: %lx\n", ld->structname_args);
58c03c
+		if (!ld->structname_args)
58c03c
+			console("        structname: (unused)\n");
58c03c
+		for (i = 0; i < ld->structname_args; i++)
58c03c
+			console("     structname[%d]: %s\n", i, ld->structname[i]);
58c03c
+		console("            header: %s\n", ld->header);
58c03c
+		console("          list_ptr: %lx\n", (ulong)ld->list_ptr);
58c03c
+		console("     callback_func: %lx\n", (ulong)ld->callback_func);
58c03c
+		console("     callback_data: %lx\n", (ulong)ld->callback_data);
58c03c
+		console("struct_list_offset: %lx\n", ld->struct_list_offset);
58c03c
+	}
58c03c
+}
58c03c
+
58c03c
+
58c03c
+static void 
58c03c
+do_list_output_struct(struct list_data *ld, ulong next, ulong offset,
58c03c
+				  unsigned int radix, struct req_entry **e)
58c03c
+{
58c03c
+	int i;
58c03c
+
58c03c
+	for (i = 0; i < ld->structname_args; i++) {
58c03c
+		switch (count_chars(ld->structname[i], '.'))
58c03c
+		{
58c03c
+			case 0:
58c03c
+				dump_struct(ld->structname[i],
58c03c
+					    next - offset, radix);
58c03c
+				break;
58c03c
+			default:
58c03c
+				if (ld->flags & LIST_PARSE_MEMBER)
58c03c
+					dump_struct_members(ld, i, next);
58c03c
+				else if (ld->flags & LIST_READ_MEMBER)
58c03c
+					dump_struct_members_fast(e[i],
58c03c
+						 radix, next - offset);
58c03c
+				break;
58c03c
+		}
58c03c
+	}
58c03c
+}
58c03c
+
58c03c
+static int 
58c03c
+do_list_no_hash_readmem(struct list_data *ld, ulong *next_ptr,
58c03c
+				   ulong readflag)
58c03c
+{
58c03c
+	if (!readmem(*next_ptr + ld->member_offset, KVADDR, next_ptr,
58c03c
+		     sizeof(void *), "list entry", readflag)) {
58c03c
+		error(INFO, "\ninvalid list entry: %lx\n", *next_ptr);
58c03c
+		return -1;
58c03c
+	}
58c03c
+	return 0;
58c03c
+}
58c03c
+
58c03c
+static ulong brent_x; /* tortoise */
58c03c
+static ulong brent_y; /* hare */
58c03c
+static ulong brent_r; /* power */
58c03c
+static ulong brent_lambda; /* loop length */
58c03c
+static ulong brent_mu; /* distance to start of loop */
58c03c
+static ulong brent_loop_detect;
58c03c
+static ulong brent_loop_exit;
58c03c
+/*
58c03c
+ * 'ptr': representative of x or y; modified on return
58c03c
+ */
58c03c
+static int 
58c03c
+brent_f(ulong *ptr, struct list_data *ld, ulong readflag)
58c03c
+{
58c03c
+       return do_list_no_hash_readmem(ld, ptr, readflag);
58c03c
+}
58c03c
+
58c03c
+/*
58c03c
+ * Similar to do_list() but without the hash_table or LIST_ALLOCATE.
58c03c
+ * Useful for the 'list' command and other callers needing faster list
58c03c
+ * enumeration.
58c03c
+ */
58c03c
+int
58c03c
+do_list_no_hash(struct list_data *ld)
58c03c
+{
58c03c
+	ulong next, last, first, offset;
58c03c
+	ulong searchfor, readflag;
58c03c
+	int i, count, ret;
58c03c
+	unsigned int radix;
58c03c
+	struct req_entry **e = NULL;
58c03c
+
58c03c
+	do_list_debug_entry(ld);
58c03c
+
58c03c
+	count = 0;
58c03c
+	searchfor = ld->searchfor;
58c03c
+	ld->searchfor = 0;
58c03c
+	if (ld->flags & LIST_STRUCT_RADIX_10)
58c03c
+		radix = 10;
58c03c
+	else if (ld->flags & LIST_STRUCT_RADIX_16)
58c03c
+		radix = 16;
58c03c
+	else
58c03c
+		radix = 0;
58c03c
+	next = ld->start;
58c03c
+
58c03c
+	readflag = ld->flags & RETURN_ON_LIST_ERROR ?
58c03c
+		(RETURN_ON_ERROR|QUIET) : FAULT_ON_ERROR;
58c03c
+
58c03c
+	if (!readmem(next + ld->member_offset, KVADDR, &first, sizeof(void *),
58c03c
+            "first list entry", readflag)) {
58c03c
+                error(INFO, "\ninvalid list entry: %lx\n", next);
58c03c
+		return -1;
58c03c
+	}
58c03c
+
58c03c
+	if (ld->header)
58c03c
+		fprintf(fp, "%s", ld->header);
58c03c
+
58c03c
+	offset = ld->list_head_offset + ld->struct_list_offset;
58c03c
+
58c03c
+	if (ld->structname && (ld->flags & LIST_READ_MEMBER)) {
58c03c
+		e = (struct req_entry **)GETBUF(sizeof(*e) * ld->structname_args);
58c03c
+		for (i = 0; i < ld->structname_args; i++)
58c03c
+			e[i] = fill_member_offsets(ld->structname[i]);
58c03c
+	}
58c03c
+
58c03c
+	brent_loop_detect = brent_loop_exit = 0;
58c03c
+	brent_lambda = 0;
58c03c
+	brent_r = 2;
58c03c
+	brent_x = brent_y = next;
58c03c
+	ret = brent_f(&brent_y, ld, readflag);
58c03c
+	if (ret == -1)
58c03c
+		return -1;
58c03c
+	while (1) {
58c03c
+		if (!brent_loop_detect && ld->flags & VERBOSE) {
58c03c
+			fprintf(fp, "%lx\n", next - ld->list_head_offset);
58c03c
+			if (ld->structname) {
58c03c
+				do_list_output_struct(ld, next, offset, radix, e);
58c03c
+			}
58c03c
+		}
58c03c
+
58c03c
+                if (next && brent_loop_exit) {
58c03c
+			if (ld->flags &
58c03c
+			    (RETURN_ON_DUPLICATE|RETURN_ON_LIST_ERROR)) {
58c03c
+				error(INFO, "\nduplicate list entry: %lx\n",
58c03c
+					brent_x);
58c03c
+				return -1;
58c03c
+			}
58c03c
+			error(FATAL, "\nduplicate list entry: %lx\n", brent_x);
58c03c
+		}
58c03c
+
58c03c
+		if ((searchfor == next) ||
58c03c
+		    (searchfor == (next - ld->list_head_offset)))
58c03c
+			ld->searchfor = searchfor;
58c03c
+
58c03c
+		count++;
58c03c
+                last = next;
58c03c
+
58c03c
+		if ((ld->flags & LIST_CALLBACK) &&
58c03c
+		    ld->callback_func((void *)(next - ld->list_head_offset),
58c03c
+		    ld->callback_data) && (ld->flags & CALLBACK_RETURN))
58c03c
+			break;
58c03c
+
58c03c
+		ret = do_list_no_hash_readmem(ld, &next, readflag);
58c03c
+		if (ret == -1)
58c03c
+			return -1;
58c03c
+
58c03c
+		if (!brent_loop_detect) {
58c03c
+			if (brent_x == brent_y) {
58c03c
+				brent_loop_detect = 1;
58c03c
+				error(INFO, "loop detected, loop length: %lx\n", brent_lambda);
58c03c
+				/* reset x and y to start; advance y loop length */
58c03c
+				brent_mu = 0;
58c03c
+				brent_x = brent_y = ld->start;
58c03c
+				while (brent_lambda--) {
58c03c
+					ret = brent_f(&brent_y, ld, readflag);
58c03c
+					if (ret == -1)
58c03c
+						return -1;
58c03c
+				}
58c03c
+			} else {
58c03c
+				if (brent_r == brent_lambda) {
58c03c
+					brent_x = brent_y;
58c03c
+					brent_r *= 2;
58c03c
+					brent_lambda = 0;
58c03c
+				}
58c03c
+				brent_y = next;
58c03c
+				brent_lambda++;
58c03c
+			}
58c03c
+		} else {
58c03c
+			if (!brent_loop_exit && brent_x == brent_y) {
58c03c
+				brent_loop_exit = 1;
58c03c
+				error(INFO, "length from start to loop: %lx",
58c03c
+					brent_mu);
58c03c
+			} else {
58c03c
+				ret = brent_f(&brent_x, ld, readflag);
58c03c
+				if (ret == -1)
58c03c
+					return -1;
58c03c
+				ret = brent_f(&brent_y, ld, readflag);
58c03c
+				if (ret == -1)
58c03c
+					return -1;
58c03c
+				brent_mu++;
58c03c
+			}
58c03c
+		}
58c03c
+
58c03c
+		if (next == 0) {
58c03c
+			if (ld->flags & LIST_HEAD_FORMAT) {
58c03c
+				error(INFO, "\ninvalid list entry: 0\n");
58c03c
+				return -1;
58c03c
+			}
58c03c
+			if (CRASHDEBUG(1))
58c03c
+				console("do_list end: next:%lx\n", next);
58c03c
+
58c03c
+			break;
58c03c
+		}
58c03c
+
58c03c
+		if (next == ld->end) {
58c03c
+			if (CRASHDEBUG(1))
58c03c
+				console("do_list end: next:%lx == end:%lx\n",
58c03c
+					next, ld->end);
58c03c
+			break;
58c03c
+		}
58c03c
+
58c03c
+		if (next == ld->start) {
58c03c
+			if (CRASHDEBUG(1))
58c03c
+				console("do_list end: next:%lx == start:%lx\n",
58c03c
+					next, ld->start);
58c03c
+			break;
58c03c
+		}
58c03c
+
58c03c
+		if (next == last) {
58c03c
+			if (CRASHDEBUG(1))
58c03c
+				console("do_list end: next:%lx == last:%lx\n",
58c03c
+					next, last);
58c03c
+			break;
58c03c
+		}
58c03c
+
58c03c
+		if ((next == first) && (count != 1)) {
58c03c
+			if (CRASHDEBUG(1))
58c03c
+		      console("do_list end: next:%lx == first:%lx (count %d)\n",
58c03c
+				next, last, count);
58c03c
+			break;
58c03c
+		}
58c03c
+	}
58c03c
+
58c03c
+	if (CRASHDEBUG(1))
58c03c
+		console("do_list count: %d\n", count);
58c03c
+
58c03c
+	return count;
58c03c
+}
58c03c
+
58c03c
 /*
58c03c
  *  Issue a dump_struct_member() call for one or more structure
58c03c
  *  members.  Multiple members are passed in a comma-separated