All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 6/8] makedumpfile: Read and process 'for' command from config file.
@ 2011-05-17 20:05 Mahesh J Salgaonkar
  2011-08-11  8:06 ` Ken'ichi Ohmichi
  0 siblings, 1 reply; 8+ messages in thread
From: Mahesh J Salgaonkar @ 2011-05-17 20:05 UTC (permalink / raw)
  To: kexec, Ken'ichi Ohmichi
  Cc: V Srivatsa, Ananth N Mavinakayanahalli, Mahesh Salgaonkar,
	Dave Anderson, Prerna Saxena, Reinhard

From: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>

This patch adds support to read and process 'for' command from config file
to filter multiple memory locations that are accessible through an array,
link list or list_head.

The syntax for 'for' (loop construct) filter command is:

for <id> in {<ArrayVar> |
	     <StructVar> via <NextMember> |
	     <ListHeadVar> within <StructName>:<ListHeadMember>}
	erase <id>[.MemberExpression] [size <SizeExpression>|nullify]
	[erase <id> ...]
	[...]
endfor

Updated filter.conf(8) man page that describes the syntax for loop construct.

Signed-off-by: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
Signed-off-by: Prerna Saxena <prerna@linux.vnet.ibm.com>
---

 makedumpfile.c      |  533 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 makedumpfile.conf   |   67 ++++++
 makedumpfile.conf.8 |  212 ++++++++++++++++++++
 makedumpfile.h      |   19 ++
 4 files changed, 814 insertions(+), 17 deletions(-)

diff --git a/makedumpfile.c b/makedumpfile.c
index 1c7237a..a616bae 100644
--- a/makedumpfile.c
+++ b/makedumpfile.c
@@ -7788,13 +7788,20 @@ free_config_entry(struct config_entry *ce)
 void
 free_config(struct config *config)
 {
+	int i;
 	if (config) {
 		if (config->module_name)
 			free(config->module_name);
+		for (i = 0; i < config->num_filter_symbols; i++) {
+			if (config->filter_symbol[i])
+				free_config_entry(config->filter_symbol[i]);
+			if (config->size_symbol[i])
+				free_config_entry(config->size_symbol[i]);
+		}
 		if (config->filter_symbol)
-			free_config_entry(config->filter_symbol);
+			free(config->filter_symbol);
 		if (config->size_symbol)
-			free_config_entry(config->size_symbol);
+			free(config->size_symbol);
 		free(config);
 	}
 }
@@ -7851,7 +7858,16 @@ create_config_entry(const char *token, unsigned short flag, int line)
 			/* First node is always a symbol name */
 			ptr->flag |= SYMBOL_ENTRY;
 		}
-		if (flag & FILTER_ENTRY) {
+		if (flag & ITERATION_ENTRY) {
+			/* Max depth for iteration entry is 1 */
+			if (depth > 0) {
+				ERRMSG("Config error at %d: Invalid iteration "
+					"variable entry.\n", line);
+				goto err_out;
+			}
+			ptr->name = strdup(cur);
+		}
+		if (flag & (FILTER_ENTRY | LIST_ENTRY)) {
 			ptr->name = strdup(cur);
 		}
 		if (flag & SIZE_ENTRY) {
@@ -8050,6 +8066,7 @@ get_config_token(char *expected_token, unsigned char flag, int *line,
 static int
 read_size_entry(struct config *config, int line)
 {
+	int idx = config->num_filter_symbols - 1;
 	char *token = get_config_token(NULL, 0, &line, NULL, NULL);
 
 	if (!token || IS_KEYWORD(token)) {
@@ -8057,12 +8074,19 @@ read_size_entry(struct config *config, int line)
 		" 'size' keyword.\n", line);
 		return FALSE;
 	}
-	config->size_symbol = create_config_entry(token, SIZE_ENTRY, line);
-	if (!config->size_symbol) {
+	config->size_symbol[idx] = create_config_entry(token, SIZE_ENTRY, line);
+	if (!config->size_symbol[idx]) {
 		ERRMSG("Error at line %d: Failed to read size symbol\n",
 									line);
 		return FALSE;
 	}
+	if (config->iter_entry && config->size_symbol[idx]->name &&
+					(!strcmp(config->size_symbol[idx]->name,
+					config->iter_entry->name))) {
+		config->size_symbol[idx]->flag &= ~SYMBOL_ENTRY;
+		config->size_symbol[idx]->flag |= VAR_ENTRY;
+		config->size_symbol[idx]->refer_to = config->iter_entry;
+	}
 	return TRUE;
 }
 
@@ -8076,6 +8100,7 @@ read_size_entry(struct config *config, int line)
 static int
 read_filter_entry(struct config *config, int line)
 {
+	int size, idx;
 	char *token = get_config_token(NULL, 0, &line, NULL, NULL);
 
 	if (!token || IS_KEYWORD(token)) {
@@ -8083,15 +8108,41 @@ read_filter_entry(struct config *config, int line)
 		" 'erase' command.\n", line);
 		return FALSE;
 	}
-	config->filter_symbol =
+
+	idx = config->num_filter_symbols;
+	config->num_filter_symbols++;
+	size = config->num_filter_symbols * sizeof(struct config_entry *);
+	config->filter_symbol = realloc(config->filter_symbol, size);
+	config->size_symbol = realloc(config->size_symbol, size);
+
+	if (!config->filter_symbol || !config->size_symbol) {
+		ERRMSG("Can't get memory to read config symbols.\n");
+		return FALSE;
+	}
+	config->filter_symbol[idx] = NULL;
+	config->size_symbol[idx] = NULL;
+
+	config->filter_symbol[idx] =
 			create_config_entry(token, FILTER_ENTRY, line);
-	if (!config->filter_symbol) {
+	if (!config->filter_symbol[idx]) {
 		ERRMSG("Error at line %d: Failed to read filter symbol\n",
 									line);
 		return FALSE;
 	}
+	if (config->iter_entry) {
+		if (strcmp(config->filter_symbol[idx]->name,
+				config->iter_entry->name)) {
+			ERRMSG("Config error at %d: unused iteration"
+				" variable '%s'.\n", line,
+				config->iter_entry->name);
+			return FALSE;
+		}
+		config->filter_symbol[idx]->flag &= ~SYMBOL_ENTRY;
+		config->filter_symbol[idx]->flag |= VAR_ENTRY;
+		config->filter_symbol[idx]->refer_to = config->iter_entry;
+	}
 	if (get_config_token("nullify", 0, &line, NULL, NULL)) {
-		config->filter_symbol->nullify = 1;
+		config->filter_symbol[idx]->nullify = 1;
 	}
 	else if (get_config_token("size", 0, &line, NULL, NULL)) {
 		if (!read_size_entry(config, line))
@@ -8100,6 +8151,150 @@ read_filter_entry(struct config *config, int line)
 	return TRUE;
 }
 
+static int
+add_traversal_entry(struct config_entry *ce, char *member, int line)
+{
+	if (!ce)
+		return FALSE;
+
+	while (ce->next)
+		ce = ce->next;
+
+	ce->next = create_config_entry(member, LIST_ENTRY, line);
+	if (ce->next == NULL) {
+		ERRMSG("Error at line %d: Failed to read 'via' member\n",
+									line);
+		return FALSE;
+	}
+
+	ce->next->flag |= TRAVERSAL_ENTRY;
+	ce->next->flag &= ~SYMBOL_ENTRY;
+	return TRUE;
+}
+
+static int
+read_list_entry(struct config *config, int line)
+{
+	char *token = get_config_token(NULL, 0, &line, NULL, NULL);
+
+	if (!token || IS_KEYWORD(token)) {
+		ERRMSG("Config error at %d: expected list symbol after"
+		" 'in' keyword.\n", line);
+		return FALSE;
+	}
+	config->list_entry = create_config_entry(token, LIST_ENTRY, line);
+	if (!config->list_entry) {
+		ERRMSG("Error at line %d: Failed to read list symbol\n",
+									line);
+		return FALSE;
+	}
+	/* Check if user has provided 'via' or 'within' keyword */
+	if (get_config_token("via", 0, &line, NULL, NULL)) {
+		/* next token is traversal member NextMember */
+		token = get_config_token(NULL, 0, &line, NULL, NULL);
+		if (!token) {
+			ERRMSG("Config error at %d: expected member name after"
+			" 'via' keyword.\n", line);
+			return FALSE;
+		}
+		if (!add_traversal_entry(config->list_entry, token, line))
+			return FALSE;
+	}
+	else if (get_config_token("within", 0, &line, NULL, NULL)) {
+		char *s_name, *lh_member;
+		/* next value is StructName:ListHeadMember */
+		s_name = get_config_token(NULL, 0, &line, NULL, NULL);
+		if (!s_name || IS_KEYWORD(s_name)) {
+			ERRMSG("Config error at %d: expected struct name after"
+			" 'within' keyword.\n", line);
+			return FALSE;
+		}
+		lh_member = strchr(s_name, ':');
+		if (lh_member) {
+			*lh_member++ = '\0';
+			if (!strlen(lh_member)) {
+				ERRMSG("Config error at %d: expected list_head"
+					" member after ':'.\n", line);
+				return FALSE;
+			}
+			config->iter_entry->next =
+				create_config_entry(lh_member,
+							ITERATION_ENTRY, line);
+			if (!config->iter_entry->next)
+				return FALSE;
+			config->iter_entry->next->flag &= ~SYMBOL_ENTRY;
+		}
+		if (!strlen(s_name)) {
+			ERRMSG("Config error at %d: Invalid token found "
+				"after 'within' keyword.\n", line);
+			return FALSE;
+		}
+		config->iter_entry->type_name = strdup(s_name);
+	}
+	return TRUE;
+}
+
+/*
+ * Read the iteration entry (LoopConstruct). The syntax is:
+ *
+ *	for <id> in {<ArrayVar> |
+ *		    <StructVar> via <NextMember> |
+ *		    <ListHeadVar> within <StructName>:<ListHeadMember>}
+ *		erase <id>[.MemberExpression] [size <SizeExpression>|nullify]
+ *		[erase <id>...]
+ *		[...]
+ *	endfor
+ */
+static int
+read_iteration_entry(struct config *config, int line)
+{
+	int eof = 0;
+	char *token = get_config_token(NULL, 0, &line, NULL, NULL);
+
+	if (!token || IS_KEYWORD(token)) {
+		ERRMSG("Config error at %d: expected iteration VAR entry after"
+		" 'for' keyword.\n", line);
+		return FALSE;
+	}
+	config->iter_entry =
+		create_config_entry(token, ITERATION_ENTRY, line);
+	if (!config->iter_entry) {
+		ERRMSG("Error at line %d: "
+			"Failed to read iteration VAR entry.\n", line);
+		return FALSE;
+	}
+	if (!get_config_token("in", 0, &line, NULL, NULL)) {
+		char *token;
+		token = get_config_token(NULL, 0, &line, NULL, NULL);
+		if (token)
+			ERRMSG("Config error at %d: Invalid token '%s'.\n",
+								line, token);
+		ERRMSG("Config error at %d: expected token 'in'.\n", line);
+		return FALSE;
+	}
+	if (!read_list_entry(config, line))
+		return FALSE;
+
+	while (!get_config_token("endfor", 0, &line, NULL, &eof) && !eof) {
+		if (get_config_token("erase", 0, &line, NULL, NULL)) {
+			if (!read_filter_entry(config, line))
+				return FALSE;
+		}
+		else {
+			token = get_config_token(NULL, 0, &line, NULL, NULL);
+			ERRMSG("Config error at %d: "
+				"Invalid token '%s'.\n", line, token);
+			return FALSE;
+		}
+	}
+	if (eof) {
+		ERRMSG("Config error at %d: No matching 'endfor' found.\n",
+									line);
+		return FALSE;
+	}
+	return TRUE;
+}
+
 /*
  * Configuration file 'makedumpfile.conf' contains filter commands.
  * Every individual filter command is considered as a config entry. A config
@@ -8125,6 +8320,13 @@ get_config(int skip)
 		if (!read_filter_entry(config, line_count))
 			goto err_out;
 	}
+	else if (get_config_token("for", 0, &line_count, &cur_module, &eof)) {
+		if (cur_module)
+			config->module_name = strdup(cur_module);
+
+		if (!read_iteration_entry(config, line_count))
+			goto err_out;
+	}
 	else {
 		if (!eof) {
 			token = get_config_token(NULL, 0, &line_count,
@@ -8193,6 +8395,24 @@ resolve_config_entry(struct config_entry *ce, unsigned long long base_addr,
 				ce->array_length = 0;
 		}
 	}
+	else if (ce->flag & VAR_ENTRY) {
+		/* iteration variable.
+		 * read the value from ce->refer_to
+		 */
+		ce->addr = ce->refer_to->addr;
+		ce->sym_addr = ce->refer_to->sym_addr;
+		ce->size = ce->refer_to->size;
+		ce->type_flag = ce->refer_to->type_flag;
+		if (!ce->type_name)
+			ce->type_name = strdup(ce->refer_to->type_name);
+
+		/* This entry has been changed hence next entry needs to
+		 * be resolved accordingly.
+		 */
+		if (ce->next)
+			ce->next->flag &= ~ENTRY_RESOLVED;
+		return TRUE;
+	}
 	else {
 		/* find the member offset */
 		ce->offset = get_member_offset(base_struct_name,
@@ -8216,9 +8436,58 @@ resolve_config_entry(struct config_entry *ce, unsigned long long base_addr,
 				ce->line, base_struct_name, ce->name);
 		return FALSE;
 	}
+	if (!strcmp(ce->type_name, "list_head")) {
+		ce->type_flag |= TYPE_LIST_HEAD;
+		/* If this list head expression is a LIST entry then
+		 * mark the next entry as TRAVERSAL_ENTRY, if any.
+		 * Error out if next entry is not a last node.
+		 */
+		if ((ce->flag & LIST_ENTRY) && ce->next) {
+			if (ce->next->next) {
+				ERRMSG("Config error at %d: Only one traversal"
+					" entry is allowed for list_head type"
+					" LIST entry", ce->line);
+				return FALSE;
+			}
+			ce->next->flag |= TRAVERSAL_ENTRY;
+		}
+	}
 	ce->addr = ce->sym_addr;
 	if (ce->size < 0)
 		ce->size = 0;
+	if ((ce->flag & LIST_ENTRY) && !ce->next) {
+		/* This is the last node of LIST entry.
+		 * For the list entry symbol, the allowed data types are:
+		 * Array, Structure Pointer (with 'next' member) and list_head.
+		 *
+		 * If this is a struct or list_head data type then
+		 * create a leaf node entry with 'next' member.
+		 */
+		if ((ce->type_flag & TYPE_BASE)
+					&& (strcmp(ce->type_name, "void")))
+			return FALSE;
+
+		if ((ce->type_flag & TYPE_LIST_HEAD)
+			|| ((ce->type_flag & (TYPE_STRUCT | TYPE_ARRAY))
+							== TYPE_STRUCT)) {
+			if (!(ce->flag & TRAVERSAL_ENTRY)) {
+				ce->next = create_config_entry("next",
+							LIST_ENTRY, ce->line);
+				if (ce->next == NULL)
+					return FALSE;
+
+				ce->next->flag |= TRAVERSAL_ENTRY;
+				ce->next->flag &= ~SYMBOL_ENTRY;
+			}
+		}
+		if (ce->flag & TRAVERSAL_ENTRY) {
+			/* type name of traversal entry should match with
+			 * that of parent node.
+			 */
+			if (strcmp(base_struct_name, ce->type_name))
+				return FALSE;
+		}
+	}
 	if ((ce->type_flag & (TYPE_ARRAY | TYPE_PTR)) == TYPE_PTR) {
 		/* If it's a pointer variable (not array) then read the
 		 * pointer value. */
@@ -8227,7 +8496,7 @@ resolve_config_entry(struct config_entry *ce, unsigned long long base_addr,
 		/*
 		 * if it is a void pointer then reset the size to 0
 		 * User need to provide a size to filter data referenced
-		 * by 'void *' pointer.
+		 * by 'void *' pointer or nullify option.
 		 */
 		if (!strcmp(ce->type_name, "void"))
 			ce->size = 0;
@@ -8286,6 +8555,8 @@ resolve_config_entry(struct config_entry *ce, unsigned long long base_addr,
 		free(val);
 	}
 	ce->flag |= ENTRY_RESOLVED;
+	if (ce->next)
+		ce->next->flag &= ~ENTRY_RESOLVED;
 	return TRUE;
 }
 
@@ -8294,18 +8565,87 @@ get_config_symbol_addr(struct config_entry *ce,
 			unsigned long long base_addr,
 			char *base_struct_name)
 {
+	unsigned long long addr = 0;
+
 	if (!(ce->flag & ENTRY_RESOLVED)) {
 		if (!resolve_config_entry(ce, base_addr, base_struct_name))
 			return 0;
 	}
 
+	if ((ce->flag & LIST_ENTRY)) {
+		/* handle List entry differently */
+		if (!ce->next) {
+			/* leaf node. */
+			if (ce->type_flag & TYPE_ARRAY) {
+				if (ce->index == ce->array_length)
+					return 0;
+				if (!(ce->type_flag & TYPE_PTR))
+					return (ce->addr +
+							(ce->index * ce->size));
+				/* Array of pointers.
+				 *
+				 * Array may contain NULL pointers at some
+				 * indexes. Hence return the next non-null
+				 * address value.
+				 */
+				while (ce->index < ce->array_length) {
+					addr = read_pointer_value(ce->addr +
+						(ce->index * pointer_size));
+					ce->index++;
+					if (addr)
+						break;
+				}
+				return addr;
+			}
+			else {
+				if (ce->addr == ce->cmp_addr)
+					return 0;
+
+				/* Set the leaf node as unresolved, so that
+				 * it will be resolved every time when
+				 * get_config_symbol_addr is called untill
+				 * it hits the exit condiftion.
+				 */
+				ce->flag &= ~ENTRY_RESOLVED;
+			}
+		}
+		else if ((ce->next->next == NULL) &&
+					!(ce->next->type_flag & TYPE_ARRAY)) {
+			/* the next node is leaf node. for non-array element
+			 * Set the sym_addr and addr of this node with that of
+			 * leaf node.
+			 */
+			addr = ce->addr;
+			ce->addr = ce->next->addr;
+
+			if (!(ce->type_flag & TYPE_LIST_HEAD)) {
+				if (addr == ce->next->cmp_addr)
+					return 0;
+
+				if (!ce->next->cmp_addr) {
+					/* safeguard against circular
+					 * link-list
+					 */
+					ce->next->cmp_addr = addr;
+				}
+
+				/* Force resolution of traversal node */
+				if (ce->addr && !resolve_config_entry(ce->next,
+						ce->addr, ce->type_name))
+					return 0;
+
+				return addr;
+			}
+		}
+	}
+
 	if (ce->next && ce->addr) {
 		/* Populate nullify flag down the list */
 		ce->next->nullify = ce->nullify;
 		return get_config_symbol_addr(ce->next, ce->addr,
 							ce->type_name);
 	}
-	else if (ce->nullify) {
+	else if (!ce->next && ce->nullify) {
 		/* nullify is applicable to pointer type */
 		if (ce->type_flag & TYPE_PTR)
 			return ce->sym_addr;
@@ -8340,6 +8680,48 @@ get_config_symbol_size(struct config_entry *ce,
 	}
 }
 
+static int
+resolve_list_entry(struct config_entry *ce, unsigned long long base_addr,
+			char *base_struct_name, char **out_type_name,
+			unsigned char *out_type_flag)
+{
+	if (!(ce->flag & ENTRY_RESOLVED)) {
+		if (!resolve_config_entry(ce, base_addr, base_struct_name))
+			return FALSE;
+	}
+
+	if (ce->next && (ce->next->flag & TRAVERSAL_ENTRY) &&
+				(ce->type_flag & TYPE_ARRAY)) {
+		/*
+		 * We are here because user has provided
+		 * traversal member for ArrayVar using 'via' keyword.
+		 *
+		 * Print warning and continue.
+		 */
+		ERRMSG("Warning: line %d: 'via' keyword not required "
+			"for ArrayVar.\n", ce->next->line);
+		free_config_entry(ce->next);
+		ce->next = NULL;
+	}
+	if ((ce->type_flag & TYPE_LIST_HEAD) && ce->next &&
+			(ce->next->flag & TRAVERSAL_ENTRY)) {
+		/* set cmp_addr for list empty condition.  */
+		ce->next->cmp_addr = ce->sym_addr;
+	}
+	if (ce->next && ce->addr) {
+		return resolve_list_entry(ce->next, ce->addr,
+				ce->type_name, out_type_name, out_type_flag);
+	}
+	else {
+		ce->index = 0;
+		if (out_type_name)
+			*out_type_name = ce->type_name;
+		if (out_type_flag)
+			*out_type_flag = ce->type_flag;
+	}
+	return TRUE;
+}
+
 /*
  * Insert the filter info node using insertion sort.
  * If filter node for a given paddr is aready present then update the size
@@ -8420,6 +8802,107 @@ update_filter_info(struct config_entry *filter_symbol,
 	return TRUE;
 }
 
+int
+initialize_iteration_entry(struct config_entry *ie,
+				char *type_name, unsigned char type_flag)
+{
+	if (!(ie->flag & ITERATION_ENTRY))
+		return FALSE;
+
+	if (type_flag & TYPE_LIST_HEAD) {
+		if (!ie->type_name) {
+			ERRMSG("Config error at %d: Use 'within' keyword "
+				"to specify StructName:ListHeadMember.\n",
+				ie->line);
+			return FALSE;
+		}
+		/*
+		 * If the LIST entry is of list_head type and user has not
+		 * specified the member name where iteration entry is hooked
+		 * on to list_head, then we default to member name 'list'.
+		 */
+		if (!ie->next) {
+			ie->next = create_config_entry("list", ITERATION_ENTRY,
+								ie->line);
+			ie->next->flag &= ~SYMBOL_ENTRY;
+		}
+	}
+	else {
+		if (ie->type_name) {
+			/* looks like user has used 'within' keyword for
+			 * non-list_head VAR. Print the warning and continue.
+			 */
+			ERRMSG("Warning: line %d: 'within' keyword not "
+				"required for ArrayVar/StructVar.\n", ie->line);
+			free(ie->type_name);
+
+			/* remove the next list_head member from iteration
+			 * entry that would have added as part of 'within'
+			 * keyword processing.
+			 */
+			if (ie->next) {
+				free_config_entry(ie->next);
+				ie->next = NULL;
+			}
+		}
+		ie->type_name = strdup(type_name);
+	}
+
+	if (!ie->size) {
+		ie->size = get_structure_size(ie->type_name,
+						DWARF_INFO_GET_STRUCT_SIZE);
+		if (ie->size == FAILED_DWARFINFO) {
+			ERRMSG("Config error at %d: "
+				"Can't get size for type: %s.\n",
+				ie->line, ie->type_name);
+			return FALSE;
+		}
+		else if (ie->size == NOT_FOUND_STRUCTURE) {
+			ERRMSG("Config error at %d: "
+				"Can't find structure: %s.\n",
+				ie->line, ie->type_name);
+			return FALSE;
+		}
+	}
+	if (type_flag & TYPE_LIST_HEAD) {
+		if (!resolve_config_entry(ie->next, 0, ie->type_name))
+			return FALSE;
+
+		if (strcmp(ie->next->type_name, "list_head")) {
+			ERRMSG("Config error at %d: "
+				"Member '%s' is not of 'list_head' type.\n",
+				ie->next->line, ie->next->name);
+			return FALSE;
+		}
+	}
+	return TRUE;
+}
+
+int
+list_entry_empty(struct config_entry *le, struct config_entry *ie)
+{
+	unsigned long long addr;
+
+	/* Error out if arguments are not correct */
+	if (!(le->flag & LIST_ENTRY) || !(ie->flag & ITERATION_ENTRY)) {
+		ERRMSG("Invalid arguments\n");
+		return TRUE;
+	}
+	addr = get_config_symbol_addr(le, 0, NULL);
+	if (!addr)
+		return TRUE;
+
+	if (ie->next) {
+		/* we are dealing with list_head */
+		ie->next->addr = addr;
+		ie->addr = addr - ie->next->offset;
+		//resolve_iteration_entry(ie, addr);
+	}
+	else
+		ie->addr = addr;
+	return FALSE;
+}
+
 /*
  * Process the config entry that has been read by get_config.
  * return TRUE on success
@@ -8427,7 +8910,35 @@ update_filter_info(struct config_entry *filter_symbol,
 int
 process_config(struct config *config)
 {
-	update_filter_info(config->filter_symbol, config->size_symbol);
+	int i;
+	if (config->list_entry) {
+		unsigned char type_flag;
+		char *type_name = NULL;
+		/*
+		 * We are dealing with 'for' command.
+		 * - First resolve list entry.
+		 * - Initialize iteration entry for iteration.
+		 * - Populate iteration entry untill list entry empty.
+		 */
+		if (!resolve_list_entry(config->list_entry, 0, NULL,
+					&type_name, &type_flag)) {
+			return FALSE;
+		}
+		if (!initialize_iteration_entry(config->iter_entry,
+						type_name, type_flag)) {
+			return FALSE;
+		}
+
+		while (!list_entry_empty(config->list_entry,
+						config->iter_entry)) {
+			for (i = 0; i < config->num_filter_symbols; i++)
+				update_filter_info(config->filter_symbol[i],
+							config->size_symbol[i]);
+		}
+	}
+	else
+		update_filter_info(config->filter_symbol[0],
+						config->size_symbol[0]);
 
 	return TRUE;
 }
diff --git a/makedumpfile.conf b/makedumpfile.conf
index 3d47e25..03e604a 100644
--- a/makedumpfile.conf
+++ b/makedumpfile.conf
@@ -80,3 +80,70 @@
 ##	erase cred_jar.array
 ##	erase vmlist.addr nullify
 ##
+##
+## - To filter kernel data referred through Array/list_head Symbol
+## =================================================================
+## Syntax:
+## for <id> in { <ArrayVar> |
+##		 <StructVar> via <NextMember> |
+##		 <ListHeadVar> within <StructName>:<ListHeadMember> }
+##	erase <id>[.MemberExpression] [size <SizeExpression>|nullify]
+##	[erase <id> ...]
+##	[...]
+## endfor
+##
+## where
+##	<id>
+##		Arbitrary name used to temporarily point to elements of the
+##		list. Referred as iteration variable.
+##	<ArrayVar>
+##		A simple expression in the form of <Symbol>[.member[...]] that
+##		results into an array variable.
+##	<StructVar>
+##		A simple expression in the form of <Symbol>[.member[...]] that
+##		results into a variable that points to a structure.
+##	<NextMember>
+##		Member within <StructVar> that points to an object of same
+##		type that of <StructVar>.
+##	<ListHeadVar>
+##		A simple expression in the form of <Symbol>[.member[...]] that
+##		results into a variable of type struct list_head.
+##	<StructName>
+##		Name of the structure type that can be traversed using
+##		HEAD variable <ListHeadVar> and contains a member named
+##		<ListHeadMember>.
+##	<ListHeadMember>
+##		Name of a member in <StructName>, of type struct list_head.
+##	<MemberExpression>
+##		A simple expression in the form of [.member[...]] to specify a
+##		member or component of a member in <ArrayVar>, <StructVar> or
+##		<StructName>.
+##	<SizeExpression>
+##		One of the following:
+##		- An integer value.
+##		- <Symbol>[.member[...]]
+##		- <id>[.MemberExpresion]
+##
+## The <ArrayVar>, <StructVar> and <ListHeadVar> is also referred as LIST
+## entry
+##
+## Filter out the specified size of the data accessible through LIST entries.
+## e.g.
+##	[vmlinux]
+##	# Traversing <ListHeadVar>
+##	for m in modules.next within module:list
+##		erase m.holders_dir.name
+##	endfor
+##	# Traversing <ArrayVar>
+##	for lc in lowcore_ptr
+##		erase lc
+##	endfor
+##	# Traversing link-list
+##	for cj in cred_jar via slabp_cache
+##		erase cj.name
+##	endfor
+##	[z90crypt]
+##	for ap_dev in ap_device_list.next within ap_device:list
+##		erase ap_dev.reply.message size ap_dev.reply.length
+##	endfor
+##
diff --git a/makedumpfile.conf.8 b/makedumpfile.conf.8
index 0a7d22a..ef0e86a 100644
--- a/makedumpfile.conf.8
+++ b/makedumpfile.conf.8
@@ -48,17 +48,21 @@ will skip the section with a warning message.
 .SH FILTER COMMANDS
 .SS filter command
 .PP
-A filter command is an erase command. Each erase command must start with a new
-line. Each filter command describes data in the dump to be erased.
+A filter command is either an erase command or a loop construct. Each erase
+command and loop construct must start with a new line. Each filter command
+describes data in the dump to be erased. Syntax:
 
 .br
-<\fIEraseCommands\fR>
+<\fIEraseCommands\fR>|<\fILoopConstruct\fR>
 .br
 
 where
 .TP
 <\fIEraseCommands\fR>
 Described in the subsection \fBerase command\fR of this manual page.
+.TP
+<\fILoopConstruct\fR>
+Described in the subsection \fBLoop construct\fR of this manual page.
 .SS erase command
 .PP
 Erase specified size of a kernel data referred by specified kernel/module
@@ -207,6 +211,208 @@ erase mystruct2.addr size mystruct2.addr_size
 .br
 .B EOF
 
+.SS Loop construct
+.PP
+A Loop construct allows the user to traverse the linked list or array elements
+and erase the data contents referred by each element.
+
+.br
+\fBfor\fR <\fIid\fR> \fBin\fR {<\fIArrayVar\fR> |
+.br
+		   <\fIStructVar\fR> \fBvia\fR <\fINextMember\fR> |
+.br
+		   <\fIListHeadVar\fR> \fBwithin\fR
+<\fIStructName\fR>\fB:\fR<\fIListHeadMember\fR>}
+.br
+	\fBerase\fR <\fIid\fR>[.\fIMemberExpression\fR]
+[\fBsize\fR <\fISizeExpression\fR>|\fBnullify\fR]
+.br
+	[\fBerase\fR <\fIid\fR>...]
+.br
+	[...]
+.br
+\fBendfor\fR
+.PP
+where
+.PP
+.TP
+<\fIid\fR>
+Arbitrary name used to temporarily point to elements of the list. This is
+also called iteration variable.
+.TP
+<\fIArrayVar\fR>
+A simple expression in the form of <\fISymbol\fR>[.\fImember\fR[...]] that
+results into an array variable.
+.TP
+<\fIStructVar\fR>
+A simple expression in the form of <\fISymbol\fR>[.\fImember\fR[...]] that
+results into a variable that points to a structure.
+.TP
+<\fINextMember\fR>
+Member within <\fIStructVar\fR> that points to an object of same type that of
+<\fIStructVar\fR>.
+.TP
+<\fIListHeadVar\fR>
+A simple expression in the form of <\fISymbol\fR>[.\fImember\fR[...]] that
+results into a variable of type struct list_head.
+.TP
+<\fIStructName\fR>
+Name of the structure type that can be traversed using HEAD variable
+<\fIListHeadVar\fR> and contains a member named <\fIListHeadMember\fR>.
+.TP
+<\fIListHeadMember\fR>
+Name of a member in <\fIStructName\fR>, of type struct list_head.
+.TP
+<\fIMemberExpression\fR>
+A simple expression in the form of [.\fImember\fR[...]] to specify a member
+or component of an element in <\fIArrayVar\fR>, <\fIStructVar\fR>
+or <\fIStructName\fR>.
+.TP
+<\fISizeExpression\fR>
+Size value in the form of <\fISizeValue\fR>, <\fIid\fR>[.\fIMemberExpression\fR]
+or <\fISymbol\fR>[.\fImember\fR[...]].
+.PP
+The \fBfor\fR loop construct allows to iterate on list of elements in an array
+or linked lists. Each element in the list is assigned to iteration variable
+<\fIid\fR>. The type of the iteration variable is determined by that of the
+list elements. The entry specified after '\fBin\fR' terminal is called LIST
+entry. The LIST entry can be an array variable, structure variable/pointer or a
+struct list_head type variable. The set of \fBerase\fR commands specified
+between \fBfor\fR and \fBendfor\fR, will be executed for each element in the
+LIST entry.
+.PP
+If the LIST entry specified is an array variable, then the loop will be
+executed for each array element. The size of the array will be determined by
+using dwarf information.
+.PP
+If the LIST entry specified is a structure variable/pointer, then a traversal
+member (<\fINextMember\fR>) must be specified using '\fBvia\fR' terminal. The
+\fBfor\fR loop will continue until the value of traversal member is NULL or
+matches with address of the first node <\fIStructVar\fR> if it is a circular
+linked list.
+.PP
+If the LIST entry is specified using a struct list_head type variable, then
+\fBwithin\fR terminal must be used to specify the structure name
+<\fIStructName\fR> that is surrounding to it along with the struct list_head
+type member after '\fB:\fR' which is part of the linked list. In the erase
+statement <\fIid\fR> then denotes the structure that the list_head is
+contained in (ELEMENT_OF).
+.PP
+The below example illustrates how to use loop construct for traversing
+Array, linked list via next member and list_head.
+
+.B Example:
+.PP
+Assuming following piece of code is from kernel module 'mymodule':
+.br
+
+struct s1 {
+.br
+	struct *next;
+.br
+	struct list_head list;
+.br
+	char private[100];
+.br
+	void *key;
+.br
+	long key_size;
+.br
+};
+.br
+
+/* Global symbols */
+.br
+struct s1 mystruct1;
+.br
+static LIST_HEAD(s1_list_head);
+.br
+struct s1 myarray[100];
+.br
+
+void foo()
+.br
+{
+.br
+	struct s1 *s1_ptr;
+.br
+	...
+.br
+	...
+.br
+	s1_ptr = malloc(...);
+.br
+	...
+.br
+	...
+.br
+	list_add(&s1_ptr->list, &s1_list_head);
+.br
+	...
+.br
+}
+.br
+
+\fBmakedumpfile.conf:\fR
+.br
+[mymodule]
+.br
+# erase private fields from list starting with mystruct1 connected via
+.br
+# 'next' member:
+.br
+for mys1 in mystruct1 via next
+.br
+	erase mys1.private
+.br
+	erase mys1.key size mys1.key_size
+.br
+endfor
+.br
+
+# erase private fields from list starting with list_head variable
+.br
+# s1_list_head.
+.br
+for mys1 in s1_list_head.next within s1:list
+.br
+	erase mys1.private
+.br
+	erase mys1.key size mys1.key_size
+.br
+endfor
+.br
+
+# erase private fields from all elements of the array myarray:
+.br
+for mys1 in myarray
+.br
+	erase mys1.private
+.br
+	erase mys1.key size mys1.key_size
+.br
+endfor
+.br
+.B EOF
+.PP
+In the above example, the first \fBfor\fR construct traverses the linked list
+through a specified structure variable \fBmystruct1\fR of type \fBstruct s1\fR.
+The linked list can be traversed using '\fBnext\fR' member of \fBmystruct1\fR.
+Hence a \fBvia\fR terminal has been used to specify the traversal member
+name '\fBnext\fR'.
+.PP
+The second \fBfor\fR construct traverses the linked list through a specified
+struct list_head variable \fBs1_list_head.next\fR. The global symbol
+\fBs1_list_head\fR is a start address of the linked list and its \fBnext\fR
+member points to the address of struct list_head type member '\fBlist\fR' from
+\fBstruct s1\fR. Hence a \fBwithin\fR terminal is used to specify the structure
+name '\fBs1\fR' that can be traversed using \fBs1_list_head.next\fR variable
+along with the name of struct list_head type member '\fBlist\fR' which is part
+of the linked list that starts from \fBs1_list_head\fR global symbol.
+.PP
+The third \fBfor\fR construct traverses the array elements specified through
+a array variable \fBmyarray\fR.
+.br
 .SH SEE ALSO
 .PP
 makedumpfile(8)
diff --git a/makedumpfile.h b/makedumpfile.h
index 94f3521..1c61edb 100644
--- a/makedumpfile.h
+++ b/makedumpfile.h
@@ -1275,6 +1275,7 @@ struct dwarf_info {
 #define TYPE_ARRAY	0x02
 #define TYPE_PTR	0x04
 #define TYPE_STRUCT	0x08
+#define TYPE_LIST_HEAD	0x10
 
 extern struct dwarf_info	dwarf_info;
 
@@ -1297,18 +1298,25 @@ struct config_entry {
 	unsigned long long	sym_addr;	/* Symbol address */
 	unsigned long long	addr;		/* Symbol address or
 						   value pointed by sym_addr */
+	unsigned long long	cmp_addr;	/* for LIST_ENTRY */
 	unsigned long		offset;
 	unsigned long		type_flag;
 	long			array_length;
+	long			index;
 	long			size;
 	int			line;	/* Line number in config file. */
+	struct config_entry	*refer_to;
 	struct config_entry	*next;
 };
 
 /* flags for config_entry.flag */
 #define FILTER_ENTRY	0x0001
 #define SIZE_ENTRY	0x0002
+#define ITERATION_ENTRY	0x0004
+#define LIST_ENTRY	0x0008
 #define SYMBOL_ENTRY	0x0010
+#define VAR_ENTRY	0x0020
+#define TRAVERSAL_ENTRY	0x0040
 #define ENTRY_RESOLVED	0x8000
 
 /*
@@ -1329,13 +1337,18 @@ struct filter_config {
 
 struct config {
 	char			*module_name;
-	struct config_entry	*filter_symbol;
-	struct config_entry	*size_symbol;
+	struct config_entry	*iter_entry;
+	struct config_entry	*list_entry;
+	int			num_filter_symbols;
+	struct config_entry	**filter_symbol;
+	struct config_entry	**size_symbol;
 };
 
 #define IS_KEYWORD(tkn)	\
 	(!strcmp(tkn, "erase") || !strcmp(tkn, "size") || \
-	!strcmp(tkn, "nullify"))
+	!strcmp(tkn, "nullify") || !strcmp(tkn, "for") || \
+	!strcmp(tkn, "in") || !strcmp(tkn, "within") || \
+	!strcmp(tkn, "endfor"))
 
 int readmem(int type_addr, unsigned long long addr, void *bufptr, size_t size);
 off_t paddr_to_offset(unsigned long long paddr);


_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 6/8] makedumpfile: Read and process 'for' command from config file.
  2011-05-17 20:05 [PATCH v2 6/8] makedumpfile: Read and process 'for' command from config file Mahesh J Salgaonkar
@ 2011-08-11  8:06 ` Ken'ichi Ohmichi
  2011-08-30 18:16   ` Mahesh J Salgaonkar
  0 siblings, 1 reply; 8+ messages in thread
From: Ken'ichi Ohmichi @ 2011-08-11  8:06 UTC (permalink / raw)
  To: Mahesh J Salgaonkar
  Cc: V Srivatsa, Ananth N Mavinakayanahalli, kexec, Dave Anderson,
	Prerna Saxena, Reinhard


Hi Mahesh,

On Wed, 18 May 2011 01:35:19 +0530
Mahesh J Salgaonkar <mahesh@linux.vnet.ibm.com> wrote:
>
> From: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
> 
> This patch adds support to read and process 'for' command from config file
> to filter multiple memory locations that are accessible through an array,
> link list or list_head.
> 
> The syntax for 'for' (loop construct) filter command is:
> 
> for <id> in {<ArrayVar> |
> 	     <StructVar> via <NextMember> |
> 	     <ListHeadVar> within <StructName>:<ListHeadMember>}
> 	erase <id>[.MemberExpression] [size <SizeExpression>|nullify]
> 	[erase <id> ...]
> 	[...]
> endfor
> 
> Updated filter.conf(8) man page that describes the syntax for loop construct.
> 
> Signed-off-by: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
> Signed-off-by: Prerna Saxena <prerna@linux.vnet.ibm.com>
> ---

Thank you for the patch.
I think this patch is good.

Acked-by: Ken'ichi Ohmichi <oomichi@mxs.nes.nec.co.jp>


Thanks
Ken'ichi Ohmichi

>  makedumpfile.c      |  533 ++++++++++++++++++++++++++++++++++++++++++++++++++-
>  makedumpfile.conf   |   67 ++++++
>  makedumpfile.conf.8 |  212 ++++++++++++++++++++
>  makedumpfile.h      |   19 ++
>  4 files changed, 814 insertions(+), 17 deletions(-)
> 
> diff --git a/makedumpfile.c b/makedumpfile.c
> index 1c7237a..a616bae 100644
> --- a/makedumpfile.c
> +++ b/makedumpfile.c
> @@ -7788,13 +7788,20 @@ free_config_entry(struct config_entry *ce)
>  void
>  free_config(struct config *config)
>  {
> +	int i;
>  	if (config) {
>  		if (config->module_name)
>  			free(config->module_name);
> +		for (i = 0; i < config->num_filter_symbols; i++) {
> +			if (config->filter_symbol[i])
> +				free_config_entry(config->filter_symbol[i]);
> +			if (config->size_symbol[i])
> +				free_config_entry(config->size_symbol[i]);
> +		}
>  		if (config->filter_symbol)
> -			free_config_entry(config->filter_symbol);
> +			free(config->filter_symbol);
>  		if (config->size_symbol)
> -			free_config_entry(config->size_symbol);
> +			free(config->size_symbol);
>  		free(config);
>  	}
>  }
> @@ -7851,7 +7858,16 @@ create_config_entry(const char *token, unsigned short flag, int line)
>  			/* First node is always a symbol name */
>  			ptr->flag |= SYMBOL_ENTRY;
>  		}
> -		if (flag & FILTER_ENTRY) {
> +		if (flag & ITERATION_ENTRY) {
> +			/* Max depth for iteration entry is 1 */
> +			if (depth > 0) {
> +				ERRMSG("Config error at %d: Invalid iteration "
> +					"variable entry.\n", line);
> +				goto err_out;
> +			}
> +			ptr->name = strdup(cur);
> +		}
> +		if (flag & (FILTER_ENTRY | LIST_ENTRY)) {
>  			ptr->name = strdup(cur);
>  		}
>  		if (flag & SIZE_ENTRY) {
> @@ -8050,6 +8066,7 @@ get_config_token(char *expected_token, unsigned char flag, int *line,
>  static int
>  read_size_entry(struct config *config, int line)
>  {
> +	int idx = config->num_filter_symbols - 1;
>  	char *token = get_config_token(NULL, 0, &line, NULL, NULL);
>  
>  	if (!token || IS_KEYWORD(token)) {
> @@ -8057,12 +8074,19 @@ read_size_entry(struct config *config, int line)
>  		" 'size' keyword.\n", line);
>  		return FALSE;
>  	}
> -	config->size_symbol = create_config_entry(token, SIZE_ENTRY, line);
> -	if (!config->size_symbol) {
> +	config->size_symbol[idx] = create_config_entry(token, SIZE_ENTRY, line);
> +	if (!config->size_symbol[idx]) {
>  		ERRMSG("Error at line %d: Failed to read size symbol\n",
>  									line);
>  		return FALSE;
>  	}
> +	if (config->iter_entry && config->size_symbol[idx]->name &&
> +					(!strcmp(config->size_symbol[idx]->name,
> +					config->iter_entry->name))) {
> +		config->size_symbol[idx]->flag &= ~SYMBOL_ENTRY;
> +		config->size_symbol[idx]->flag |= VAR_ENTRY;
> +		config->size_symbol[idx]->refer_to = config->iter_entry;
> +	}
>  	return TRUE;
>  }
>  
> @@ -8076,6 +8100,7 @@ read_size_entry(struct config *config, int line)
>  static int
>  read_filter_entry(struct config *config, int line)
>  {
> +	int size, idx;
>  	char *token = get_config_token(NULL, 0, &line, NULL, NULL);
>  
>  	if (!token || IS_KEYWORD(token)) {
> @@ -8083,15 +8108,41 @@ read_filter_entry(struct config *config, int line)
>  		" 'erase' command.\n", line);
>  		return FALSE;
>  	}
> -	config->filter_symbol =
> +
> +	idx = config->num_filter_symbols;
> +	config->num_filter_symbols++;
> +	size = config->num_filter_symbols * sizeof(struct config_entry *);
> +	config->filter_symbol = realloc(config->filter_symbol, size);
> +	config->size_symbol = realloc(config->size_symbol, size);
> +
> +	if (!config->filter_symbol || !config->size_symbol) {
> +		ERRMSG("Can't get memory to read config symbols.\n");
> +		return FALSE;
> +	}
> +	config->filter_symbol[idx] = NULL;
> +	config->size_symbol[idx] = NULL;
> +
> +	config->filter_symbol[idx] =
>  			create_config_entry(token, FILTER_ENTRY, line);
> -	if (!config->filter_symbol) {
> +	if (!config->filter_symbol[idx]) {
>  		ERRMSG("Error at line %d: Failed to read filter symbol\n",
>  									line);
>  		return FALSE;
>  	}
> +	if (config->iter_entry) {
> +		if (strcmp(config->filter_symbol[idx]->name,
> +				config->iter_entry->name)) {
> +			ERRMSG("Config error at %d: unused iteration"
> +				" variable '%s'.\n", line,
> +				config->iter_entry->name);
> +			return FALSE;
> +		}
> +		config->filter_symbol[idx]->flag &= ~SYMBOL_ENTRY;
> +		config->filter_symbol[idx]->flag |= VAR_ENTRY;
> +		config->filter_symbol[idx]->refer_to = config->iter_entry;
> +	}
>  	if (get_config_token("nullify", 0, &line, NULL, NULL)) {
> -		config->filter_symbol->nullify = 1;
> +		config->filter_symbol[idx]->nullify = 1;
>  	}
>  	else if (get_config_token("size", 0, &line, NULL, NULL)) {
>  		if (!read_size_entry(config, line))
> @@ -8100,6 +8151,150 @@ read_filter_entry(struct config *config, int line)
>  	return TRUE;
>  }
>  
> +static int
> +add_traversal_entry(struct config_entry *ce, char *member, int line)
> +{
> +	if (!ce)
> +		return FALSE;
> +
> +	while (ce->next)
> +		ce = ce->next;
> +
> +	ce->next = create_config_entry(member, LIST_ENTRY, line);
> +	if (ce->next == NULL) {
> +		ERRMSG("Error at line %d: Failed to read 'via' member\n",
> +									line);
> +		return FALSE;
> +	}
> +
> +	ce->next->flag |= TRAVERSAL_ENTRY;
> +	ce->next->flag &= ~SYMBOL_ENTRY;
> +	return TRUE;
> +}
> +
> +static int
> +read_list_entry(struct config *config, int line)
> +{
> +	char *token = get_config_token(NULL, 0, &line, NULL, NULL);
> +
> +	if (!token || IS_KEYWORD(token)) {
> +		ERRMSG("Config error at %d: expected list symbol after"
> +		" 'in' keyword.\n", line);
> +		return FALSE;
> +	}
> +	config->list_entry = create_config_entry(token, LIST_ENTRY, line);
> +	if (!config->list_entry) {
> +		ERRMSG("Error at line %d: Failed to read list symbol\n",
> +									line);
> +		return FALSE;
> +	}
> +	/* Check if user has provided 'via' or 'within' keyword */
> +	if (get_config_token("via", 0, &line, NULL, NULL)) {
> +		/* next token is traversal member NextMember */
> +		token = get_config_token(NULL, 0, &line, NULL, NULL);
> +		if (!token) {
> +			ERRMSG("Config error at %d: expected member name after"
> +			" 'via' keyword.\n", line);
> +			return FALSE;
> +		}
> +		if (!add_traversal_entry(config->list_entry, token, line))
> +			return FALSE;
> +	}
> +	else if (get_config_token("within", 0, &line, NULL, NULL)) {
> +		char *s_name, *lh_member;
> +		/* next value is StructName:ListHeadMember */
> +		s_name = get_config_token(NULL, 0, &line, NULL, NULL);
> +		if (!s_name || IS_KEYWORD(s_name)) {
> +			ERRMSG("Config error at %d: expected struct name after"
> +			" 'within' keyword.\n", line);
> +			return FALSE;
> +		}
> +		lh_member = strchr(s_name, ':');
> +		if (lh_member) {
> +			*lh_member++ = '\0';
> +			if (!strlen(lh_member)) {
> +				ERRMSG("Config error at %d: expected list_head"
> +					" member after ':'.\n", line);
> +				return FALSE;
> +			}
> +			config->iter_entry->next =
> +				create_config_entry(lh_member,
> +							ITERATION_ENTRY, line);
> +			if (!config->iter_entry->next)
> +				return FALSE;
> +			config->iter_entry->next->flag &= ~SYMBOL_ENTRY;
> +		}
> +		if (!strlen(s_name)) {
> +			ERRMSG("Config error at %d: Invalid token found "
> +				"after 'within' keyword.\n", line);
> +			return FALSE;
> +		}
> +		config->iter_entry->type_name = strdup(s_name);
> +	}
> +	return TRUE;
> +}
> +
> +/*
> + * Read the iteration entry (LoopConstruct). The syntax is:
> + *
> + *	for <id> in {<ArrayVar> |
> + *		    <StructVar> via <NextMember> |
> + *		    <ListHeadVar> within <StructName>:<ListHeadMember>}
> + *		erase <id>[.MemberExpression] [size <SizeExpression>|nullify]
> + *		[erase <id>...]
> + *		[...]
> + *	endfor
> + */
> +static int
> +read_iteration_entry(struct config *config, int line)
> +{
> +	int eof = 0;
> +	char *token = get_config_token(NULL, 0, &line, NULL, NULL);
> +
> +	if (!token || IS_KEYWORD(token)) {
> +		ERRMSG("Config error at %d: expected iteration VAR entry after"
> +		" 'for' keyword.\n", line);
> +		return FALSE;
> +	}
> +	config->iter_entry =
> +		create_config_entry(token, ITERATION_ENTRY, line);
> +	if (!config->iter_entry) {
> +		ERRMSG("Error at line %d: "
> +			"Failed to read iteration VAR entry.\n", line);
> +		return FALSE;
> +	}
> +	if (!get_config_token("in", 0, &line, NULL, NULL)) {
> +		char *token;
> +		token = get_config_token(NULL, 0, &line, NULL, NULL);
> +		if (token)
> +			ERRMSG("Config error at %d: Invalid token '%s'.\n",
> +								line, token);
> +		ERRMSG("Config error at %d: expected token 'in'.\n", line);
> +		return FALSE;
> +	}
> +	if (!read_list_entry(config, line))
> +		return FALSE;
> +
> +	while (!get_config_token("endfor", 0, &line, NULL, &eof) && !eof) {
> +		if (get_config_token("erase", 0, &line, NULL, NULL)) {
> +			if (!read_filter_entry(config, line))
> +				return FALSE;
> +		}
> +		else {
> +			token = get_config_token(NULL, 0, &line, NULL, NULL);
> +			ERRMSG("Config error at %d: "
> +				"Invalid token '%s'.\n", line, token);
> +			return FALSE;
> +		}
> +	}
> +	if (eof) {
> +		ERRMSG("Config error at %d: No matching 'endfor' found.\n",
> +									line);
> +		return FALSE;
> +	}
> +	return TRUE;
> +}
> +
>  /*
>   * Configuration file 'makedumpfile.conf' contains filter commands.
>   * Every individual filter command is considered as a config entry. A config
> @@ -8125,6 +8320,13 @@ get_config(int skip)
>  		if (!read_filter_entry(config, line_count))
>  			goto err_out;
>  	}
> +	else if (get_config_token("for", 0, &line_count, &cur_module, &eof)) {
> +		if (cur_module)
> +			config->module_name = strdup(cur_module);
> +
> +		if (!read_iteration_entry(config, line_count))
> +			goto err_out;
> +	}
>  	else {
>  		if (!eof) {
>  			token = get_config_token(NULL, 0, &line_count,
> @@ -8193,6 +8395,24 @@ resolve_config_entry(struct config_entry *ce, unsigned long long base_addr,
>  				ce->array_length = 0;
>  		}
>  	}
> +	else if (ce->flag & VAR_ENTRY) {
> +		/* iteration variable.
> +		 * read the value from ce->refer_to
> +		 */
> +		ce->addr = ce->refer_to->addr;
> +		ce->sym_addr = ce->refer_to->sym_addr;
> +		ce->size = ce->refer_to->size;
> +		ce->type_flag = ce->refer_to->type_flag;
> +		if (!ce->type_name)
> +			ce->type_name = strdup(ce->refer_to->type_name);
> +
> +		/* This entry has been changed hence next entry needs to
> +		 * be resolved accordingly.
> +		 */
> +		if (ce->next)
> +			ce->next->flag &= ~ENTRY_RESOLVED;
> +		return TRUE;
> +	}
>  	else {
>  		/* find the member offset */
>  		ce->offset = get_member_offset(base_struct_name,
> @@ -8216,9 +8436,58 @@ resolve_config_entry(struct config_entry *ce, unsigned long long base_addr,
>  				ce->line, base_struct_name, ce->name);
>  		return FALSE;
>  	}
> +	if (!strcmp(ce->type_name, "list_head")) {
> +		ce->type_flag |= TYPE_LIST_HEAD;
> +		/* If this list head expression is a LIST entry then
> +		 * mark the next entry as TRAVERSAL_ENTRY, if any.
> +		 * Error out if next entry is not a last node.
> +		 */
> +		if ((ce->flag & LIST_ENTRY) && ce->next) {
> +			if (ce->next->next) {
> +				ERRMSG("Config error at %d: Only one traversal"
> +					" entry is allowed for list_head type"
> +					" LIST entry", ce->line);
> +				return FALSE;
> +			}
> +			ce->next->flag |= TRAVERSAL_ENTRY;
> +		}
> +	}
>  	ce->addr = ce->sym_addr;
>  	if (ce->size < 0)
>  		ce->size = 0;
> +	if ((ce->flag & LIST_ENTRY) && !ce->next) {
> +		/* This is the last node of LIST entry.
> +		 * For the list entry symbol, the allowed data types are:
> +		 * Array, Structure Pointer (with 'next' member) and list_head.
> +		 *
> +		 * If this is a struct or list_head data type then
> +		 * create a leaf node entry with 'next' member.
> +		 */
> +		if ((ce->type_flag & TYPE_BASE)
> +					&& (strcmp(ce->type_name, "void")))
> +			return FALSE;
> +
> +		if ((ce->type_flag & TYPE_LIST_HEAD)
> +			|| ((ce->type_flag & (TYPE_STRUCT | TYPE_ARRAY))
> +							== TYPE_STRUCT)) {
> +			if (!(ce->flag & TRAVERSAL_ENTRY)) {
> +				ce->next = create_config_entry("next",
> +							LIST_ENTRY, ce->line);
> +				if (ce->next == NULL)
> +					return FALSE;
> +
> +				ce->next->flag |= TRAVERSAL_ENTRY;
> +				ce->next->flag &= ~SYMBOL_ENTRY;
> +			}
> +		}
> +		if (ce->flag & TRAVERSAL_ENTRY) {
> +			/* type name of traversal entry should match with
> +			 * that of parent node.
> +			 */
> +			if (strcmp(base_struct_name, ce->type_name))
> +				return FALSE;
> +		}
> +	}
>  	if ((ce->type_flag & (TYPE_ARRAY | TYPE_PTR)) == TYPE_PTR) {
>  		/* If it's a pointer variable (not array) then read the
>  		 * pointer value. */
> @@ -8227,7 +8496,7 @@ resolve_config_entry(struct config_entry *ce, unsigned long long base_addr,
>  		/*
>  		 * if it is a void pointer then reset the size to 0
>  		 * User need to provide a size to filter data referenced
> -		 * by 'void *' pointer.
> +		 * by 'void *' pointer or nullify option.
>  		 */
>  		if (!strcmp(ce->type_name, "void"))
>  			ce->size = 0;
> @@ -8286,6 +8555,8 @@ resolve_config_entry(struct config_entry *ce, unsigned long long base_addr,
>  		free(val);
>  	}
>  	ce->flag |= ENTRY_RESOLVED;
> +	if (ce->next)
> +		ce->next->flag &= ~ENTRY_RESOLVED;
>  	return TRUE;
>  }
>  
> @@ -8294,18 +8565,87 @@ get_config_symbol_addr(struct config_entry *ce,
>  			unsigned long long base_addr,
>  			char *base_struct_name)
>  {
> +	unsigned long long addr = 0;
> +
>  	if (!(ce->flag & ENTRY_RESOLVED)) {
>  		if (!resolve_config_entry(ce, base_addr, base_struct_name))
>  			return 0;
>  	}
>  
> +	if ((ce->flag & LIST_ENTRY)) {
> +		/* handle List entry differently */
> +		if (!ce->next) {
> +			/* leaf node. */
> +			if (ce->type_flag & TYPE_ARRAY) {
> +				if (ce->index == ce->array_length)
> +					return 0;
> +				if (!(ce->type_flag & TYPE_PTR))
> +					return (ce->addr +
> +							(ce->index * ce->size));
> +				/* Array of pointers.
> +				 *
> +				 * Array may contain NULL pointers at some
> +				 * indexes. Hence return the next non-null
> +				 * address value.
> +				 */
> +				while (ce->index < ce->array_length) {
> +					addr = read_pointer_value(ce->addr +
> +						(ce->index * pointer_size));
> +					ce->index++;
> +					if (addr)
> +						break;
> +				}
> +				return addr;
> +			}
> +			else {
> +				if (ce->addr == ce->cmp_addr)
> +					return 0;
> +
> +				/* Set the leaf node as unresolved, so that
> +				 * it will be resolved every time when
> +				 * get_config_symbol_addr is called untill
> +				 * it hits the exit condiftion.
> +				 */
> +				ce->flag &= ~ENTRY_RESOLVED;
> +			}
> +		}
> +		else if ((ce->next->next == NULL) &&
> +					!(ce->next->type_flag & TYPE_ARRAY)) {
> +			/* the next node is leaf node. for non-array element
> +			 * Set the sym_addr and addr of this node with that of
> +			 * leaf node.
> +			 */
> +			addr = ce->addr;
> +			ce->addr = ce->next->addr;
> +
> +			if (!(ce->type_flag & TYPE_LIST_HEAD)) {
> +				if (addr == ce->next->cmp_addr)
> +					return 0;
> +
> +				if (!ce->next->cmp_addr) {
> +					/* safeguard against circular
> +					 * link-list
> +					 */
> +					ce->next->cmp_addr = addr;
> +				}
> +
> +				/* Force resolution of traversal node */
> +				if (ce->addr && !resolve_config_entry(ce->next,
> +						ce->addr, ce->type_name))
> +					return 0;
> +
> +				return addr;
> +			}
> +		}
> +	}
> +
>  	if (ce->next && ce->addr) {
>  		/* Populate nullify flag down the list */
>  		ce->next->nullify = ce->nullify;
>  		return get_config_symbol_addr(ce->next, ce->addr,
>  							ce->type_name);
>  	}
> -	else if (ce->nullify) {
> +	else if (!ce->next && ce->nullify) {
>  		/* nullify is applicable to pointer type */
>  		if (ce->type_flag & TYPE_PTR)
>  			return ce->sym_addr;
> @@ -8340,6 +8680,48 @@ get_config_symbol_size(struct config_entry *ce,
>  	}
>  }
>  
> +static int
> +resolve_list_entry(struct config_entry *ce, unsigned long long base_addr,
> +			char *base_struct_name, char **out_type_name,
> +			unsigned char *out_type_flag)
> +{
> +	if (!(ce->flag & ENTRY_RESOLVED)) {
> +		if (!resolve_config_entry(ce, base_addr, base_struct_name))
> +			return FALSE;
> +	}
> +
> +	if (ce->next && (ce->next->flag & TRAVERSAL_ENTRY) &&
> +				(ce->type_flag & TYPE_ARRAY)) {
> +		/*
> +		 * We are here because user has provided
> +		 * traversal member for ArrayVar using 'via' keyword.
> +		 *
> +		 * Print warning and continue.
> +		 */
> +		ERRMSG("Warning: line %d: 'via' keyword not required "
> +			"for ArrayVar.\n", ce->next->line);
> +		free_config_entry(ce->next);
> +		ce->next = NULL;
> +	}
> +	if ((ce->type_flag & TYPE_LIST_HEAD) && ce->next &&
> +			(ce->next->flag & TRAVERSAL_ENTRY)) {
> +		/* set cmp_addr for list empty condition.  */
> +		ce->next->cmp_addr = ce->sym_addr;
> +	}
> +	if (ce->next && ce->addr) {
> +		return resolve_list_entry(ce->next, ce->addr,
> +				ce->type_name, out_type_name, out_type_flag);
> +	}
> +	else {
> +		ce->index = 0;
> +		if (out_type_name)
> +			*out_type_name = ce->type_name;
> +		if (out_type_flag)
> +			*out_type_flag = ce->type_flag;
> +	}
> +	return TRUE;
> +}
> +
>  /*
>   * Insert the filter info node using insertion sort.
>   * If filter node for a given paddr is aready present then update the size
> @@ -8420,6 +8802,107 @@ update_filter_info(struct config_entry *filter_symbol,
>  	return TRUE;
>  }
>  
> +int
> +initialize_iteration_entry(struct config_entry *ie,
> +				char *type_name, unsigned char type_flag)
> +{
> +	if (!(ie->flag & ITERATION_ENTRY))
> +		return FALSE;
> +
> +	if (type_flag & TYPE_LIST_HEAD) {
> +		if (!ie->type_name) {
> +			ERRMSG("Config error at %d: Use 'within' keyword "
> +				"to specify StructName:ListHeadMember.\n",
> +				ie->line);
> +			return FALSE;
> +		}
> +		/*
> +		 * If the LIST entry is of list_head type and user has not
> +		 * specified the member name where iteration entry is hooked
> +		 * on to list_head, then we default to member name 'list'.
> +		 */
> +		if (!ie->next) {
> +			ie->next = create_config_entry("list", ITERATION_ENTRY,
> +								ie->line);
> +			ie->next->flag &= ~SYMBOL_ENTRY;
> +		}
> +	}
> +	else {
> +		if (ie->type_name) {
> +			/* looks like user has used 'within' keyword for
> +			 * non-list_head VAR. Print the warning and continue.
> +			 */
> +			ERRMSG("Warning: line %d: 'within' keyword not "
> +				"required for ArrayVar/StructVar.\n", ie->line);
> +			free(ie->type_name);
> +
> +			/* remove the next list_head member from iteration
> +			 * entry that would have added as part of 'within'
> +			 * keyword processing.
> +			 */
> +			if (ie->next) {
> +				free_config_entry(ie->next);
> +				ie->next = NULL;
> +			}
> +		}
> +		ie->type_name = strdup(type_name);
> +	}
> +
> +	if (!ie->size) {
> +		ie->size = get_structure_size(ie->type_name,
> +						DWARF_INFO_GET_STRUCT_SIZE);
> +		if (ie->size == FAILED_DWARFINFO) {
> +			ERRMSG("Config error at %d: "
> +				"Can't get size for type: %s.\n",
> +				ie->line, ie->type_name);
> +			return FALSE;
> +		}
> +		else if (ie->size == NOT_FOUND_STRUCTURE) {
> +			ERRMSG("Config error at %d: "
> +				"Can't find structure: %s.\n",
> +				ie->line, ie->type_name);
> +			return FALSE;
> +		}
> +	}
> +	if (type_flag & TYPE_LIST_HEAD) {
> +		if (!resolve_config_entry(ie->next, 0, ie->type_name))
> +			return FALSE;
> +
> +		if (strcmp(ie->next->type_name, "list_head")) {
> +			ERRMSG("Config error at %d: "
> +				"Member '%s' is not of 'list_head' type.\n",
> +				ie->next->line, ie->next->name);
> +			return FALSE;
> +		}
> +	}
> +	return TRUE;
> +}
> +
> +int
> +list_entry_empty(struct config_entry *le, struct config_entry *ie)
> +{
> +	unsigned long long addr;
> +
> +	/* Error out if arguments are not correct */
> +	if (!(le->flag & LIST_ENTRY) || !(ie->flag & ITERATION_ENTRY)) {
> +		ERRMSG("Invalid arguments\n");
> +		return TRUE;
> +	}
> +	addr = get_config_symbol_addr(le, 0, NULL);
> +	if (!addr)
> +		return TRUE;
> +
> +	if (ie->next) {
> +		/* we are dealing with list_head */
> +		ie->next->addr = addr;
> +		ie->addr = addr - ie->next->offset;
> +		//resolve_iteration_entry(ie, addr);
> +	}
> +	else
> +		ie->addr = addr;
> +	return FALSE;
> +}
> +
>  /*
>   * Process the config entry that has been read by get_config.
>   * return TRUE on success
> @@ -8427,7 +8910,35 @@ update_filter_info(struct config_entry *filter_symbol,
>  int
>  process_config(struct config *config)
>  {
> -	update_filter_info(config->filter_symbol, config->size_symbol);
> +	int i;
> +	if (config->list_entry) {
> +		unsigned char type_flag;
> +		char *type_name = NULL;
> +		/*
> +		 * We are dealing with 'for' command.
> +		 * - First resolve list entry.
> +		 * - Initialize iteration entry for iteration.
> +		 * - Populate iteration entry untill list entry empty.
> +		 */
> +		if (!resolve_list_entry(config->list_entry, 0, NULL,
> +					&type_name, &type_flag)) {
> +			return FALSE;
> +		}
> +		if (!initialize_iteration_entry(config->iter_entry,
> +						type_name, type_flag)) {
> +			return FALSE;
> +		}
> +
> +		while (!list_entry_empty(config->list_entry,
> +						config->iter_entry)) {
> +			for (i = 0; i < config->num_filter_symbols; i++)
> +				update_filter_info(config->filter_symbol[i],
> +							config->size_symbol[i]);
> +		}
> +	}
> +	else
> +		update_filter_info(config->filter_symbol[0],
> +						config->size_symbol[0]);
>  
>  	return TRUE;
>  }
> diff --git a/makedumpfile.conf b/makedumpfile.conf
> index 3d47e25..03e604a 100644
> --- a/makedumpfile.conf
> +++ b/makedumpfile.conf
> @@ -80,3 +80,70 @@
>  ##	erase cred_jar.array
>  ##	erase vmlist.addr nullify
>  ##
> +##
> +## - To filter kernel data referred through Array/list_head Symbol
> +## =================================================================
> +## Syntax:
> +## for <id> in { <ArrayVar> |
> +##		 <StructVar> via <NextMember> |
> +##		 <ListHeadVar> within <StructName>:<ListHeadMember> }
> +##	erase <id>[.MemberExpression] [size <SizeExpression>|nullify]
> +##	[erase <id> ...]
> +##	[...]
> +## endfor
> +##
> +## where
> +##	<id>
> +##		Arbitrary name used to temporarily point to elements of the
> +##		list. Referred as iteration variable.
> +##	<ArrayVar>
> +##		A simple expression in the form of <Symbol>[.member[...]] that
> +##		results into an array variable.
> +##	<StructVar>
> +##		A simple expression in the form of <Symbol>[.member[...]] that
> +##		results into a variable that points to a structure.
> +##	<NextMember>
> +##		Member within <StructVar> that points to an object of same
> +##		type that of <StructVar>.
> +##	<ListHeadVar>
> +##		A simple expression in the form of <Symbol>[.member[...]] that
> +##		results into a variable of type struct list_head.
> +##	<StructName>
> +##		Name of the structure type that can be traversed using
> +##		HEAD variable <ListHeadVar> and contains a member named
> +##		<ListHeadMember>.
> +##	<ListHeadMember>
> +##		Name of a member in <StructName>, of type struct list_head.
> +##	<MemberExpression>
> +##		A simple expression in the form of [.member[...]] to specify a
> +##		member or component of a member in <ArrayVar>, <StructVar> or
> +##		<StructName>.
> +##	<SizeExpression>
> +##		One of the following:
> +##		- An integer value.
> +##		- <Symbol>[.member[...]]
> +##		- <id>[.MemberExpresion]
> +##
> +## The <ArrayVar>, <StructVar> and <ListHeadVar> is also referred as LIST
> +## entry
> +##
> +## Filter out the specified size of the data accessible through LIST entries.
> +## e.g.
> +##	[vmlinux]
> +##	# Traversing <ListHeadVar>
> +##	for m in modules.next within module:list
> +##		erase m.holders_dir.name
> +##	endfor
> +##	# Traversing <ArrayVar>
> +##	for lc in lowcore_ptr
> +##		erase lc
> +##	endfor
> +##	# Traversing link-list
> +##	for cj in cred_jar via slabp_cache
> +##		erase cj.name
> +##	endfor
> +##	[z90crypt]
> +##	for ap_dev in ap_device_list.next within ap_device:list
> +##		erase ap_dev.reply.message size ap_dev.reply.length
> +##	endfor
> +##
> diff --git a/makedumpfile.conf.8 b/makedumpfile.conf.8
> index 0a7d22a..ef0e86a 100644
> --- a/makedumpfile.conf.8
> +++ b/makedumpfile.conf.8
> @@ -48,17 +48,21 @@ will skip the section with a warning message.
>  .SH FILTER COMMANDS
>  .SS filter command
>  .PP
> -A filter command is an erase command. Each erase command must start with a new
> -line. Each filter command describes data in the dump to be erased.
> +A filter command is either an erase command or a loop construct. Each erase
> +command and loop construct must start with a new line. Each filter command
> +describes data in the dump to be erased. Syntax:
>  
>  .br
> -<\fIEraseCommands\fR>
> +<\fIEraseCommands\fR>|<\fILoopConstruct\fR>
>  .br
>  
>  where
>  .TP
>  <\fIEraseCommands\fR>
>  Described in the subsection \fBerase command\fR of this manual page.
> +.TP
> +<\fILoopConstruct\fR>
> +Described in the subsection \fBLoop construct\fR of this manual page.
>  .SS erase command
>  .PP
>  Erase specified size of a kernel data referred by specified kernel/module
> @@ -207,6 +211,208 @@ erase mystruct2.addr size mystruct2.addr_size
>  .br
>  .B EOF
>  
> +.SS Loop construct
> +.PP
> +A Loop construct allows the user to traverse the linked list or array elements
> +and erase the data contents referred by each element.
> +
> +.br
> +\fBfor\fR <\fIid\fR> \fBin\fR {<\fIArrayVar\fR> |
> +.br
> +		   <\fIStructVar\fR> \fBvia\fR <\fINextMember\fR> |
> +.br
> +		   <\fIListHeadVar\fR> \fBwithin\fR
> +<\fIStructName\fR>\fB:\fR<\fIListHeadMember\fR>}
> +.br
> +	\fBerase\fR <\fIid\fR>[.\fIMemberExpression\fR]
> +[\fBsize\fR <\fISizeExpression\fR>|\fBnullify\fR]
> +.br
> +	[\fBerase\fR <\fIid\fR>...]
> +.br
> +	[...]
> +.br
> +\fBendfor\fR
> +.PP
> +where
> +.PP
> +.TP
> +<\fIid\fR>
> +Arbitrary name used to temporarily point to elements of the list. This is
> +also called iteration variable.
> +.TP
> +<\fIArrayVar\fR>
> +A simple expression in the form of <\fISymbol\fR>[.\fImember\fR[...]] that
> +results into an array variable.
> +.TP
> +<\fIStructVar\fR>
> +A simple expression in the form of <\fISymbol\fR>[.\fImember\fR[...]] that
> +results into a variable that points to a structure.
> +.TP
> +<\fINextMember\fR>
> +Member within <\fIStructVar\fR> that points to an object of same type that of
> +<\fIStructVar\fR>.
> +.TP
> +<\fIListHeadVar\fR>
> +A simple expression in the form of <\fISymbol\fR>[.\fImember\fR[...]] that
> +results into a variable of type struct list_head.
> +.TP
> +<\fIStructName\fR>
> +Name of the structure type that can be traversed using HEAD variable
> +<\fIListHeadVar\fR> and contains a member named <\fIListHeadMember\fR>.
> +.TP
> +<\fIListHeadMember\fR>
> +Name of a member in <\fIStructName\fR>, of type struct list_head.
> +.TP
> +<\fIMemberExpression\fR>
> +A simple expression in the form of [.\fImember\fR[...]] to specify a member
> +or component of an element in <\fIArrayVar\fR>, <\fIStructVar\fR>
> +or <\fIStructName\fR>.
> +.TP
> +<\fISizeExpression\fR>
> +Size value in the form of <\fISizeValue\fR>, <\fIid\fR>[.\fIMemberExpression\fR]
> +or <\fISymbol\fR>[.\fImember\fR[...]].
> +.PP
> +The \fBfor\fR loop construct allows to iterate on list of elements in an array
> +or linked lists. Each element in the list is assigned to iteration variable
> +<\fIid\fR>. The type of the iteration variable is determined by that of the
> +list elements. The entry specified after '\fBin\fR' terminal is called LIST
> +entry. The LIST entry can be an array variable, structure variable/pointer or a
> +struct list_head type variable. The set of \fBerase\fR commands specified
> +between \fBfor\fR and \fBendfor\fR, will be executed for each element in the
> +LIST entry.
> +.PP
> +If the LIST entry specified is an array variable, then the loop will be
> +executed for each array element. The size of the array will be determined by
> +using dwarf information.
> +.PP
> +If the LIST entry specified is a structure variable/pointer, then a traversal
> +member (<\fINextMember\fR>) must be specified using '\fBvia\fR' terminal. The
> +\fBfor\fR loop will continue until the value of traversal member is NULL or
> +matches with address of the first node <\fIStructVar\fR> if it is a circular
> +linked list.
> +.PP
> +If the LIST entry is specified using a struct list_head type variable, then
> +\fBwithin\fR terminal must be used to specify the structure name
> +<\fIStructName\fR> that is surrounding to it along with the struct list_head
> +type member after '\fB:\fR' which is part of the linked list. In the erase
> +statement <\fIid\fR> then denotes the structure that the list_head is
> +contained in (ELEMENT_OF).
> +.PP
> +The below example illustrates how to use loop construct for traversing
> +Array, linked list via next member and list_head.
> +
> +.B Example:
> +.PP
> +Assuming following piece of code is from kernel module 'mymodule':
> +.br
> +
> +struct s1 {
> +.br
> +	struct *next;
> +.br
> +	struct list_head list;
> +.br
> +	char private[100];
> +.br
> +	void *key;
> +.br
> +	long key_size;
> +.br
> +};
> +.br
> +
> +/* Global symbols */
> +.br
> +struct s1 mystruct1;
> +.br
> +static LIST_HEAD(s1_list_head);
> +.br
> +struct s1 myarray[100];
> +.br
> +
> +void foo()
> +.br
> +{
> +.br
> +	struct s1 *s1_ptr;
> +.br
> +	...
> +.br
> +	...
> +.br
> +	s1_ptr = malloc(...);
> +.br
> +	...
> +.br
> +	...
> +.br
> +	list_add(&s1_ptr->list, &s1_list_head);
> +.br
> +	...
> +.br
> +}
> +.br
> +
> +\fBmakedumpfile.conf:\fR
> +.br
> +[mymodule]
> +.br
> +# erase private fields from list starting with mystruct1 connected via
> +.br
> +# 'next' member:
> +.br
> +for mys1 in mystruct1 via next
> +.br
> +	erase mys1.private
> +.br
> +	erase mys1.key size mys1.key_size
> +.br
> +endfor
> +.br
> +
> +# erase private fields from list starting with list_head variable
> +.br
> +# s1_list_head.
> +.br
> +for mys1 in s1_list_head.next within s1:list
> +.br
> +	erase mys1.private
> +.br
> +	erase mys1.key size mys1.key_size
> +.br
> +endfor
> +.br
> +
> +# erase private fields from all elements of the array myarray:
> +.br
> +for mys1 in myarray
> +.br
> +	erase mys1.private
> +.br
> +	erase mys1.key size mys1.key_size
> +.br
> +endfor
> +.br
> +.B EOF
> +.PP
> +In the above example, the first \fBfor\fR construct traverses the linked list
> +through a specified structure variable \fBmystruct1\fR of type \fBstruct s1\fR.
> +The linked list can be traversed using '\fBnext\fR' member of \fBmystruct1\fR.
> +Hence a \fBvia\fR terminal has been used to specify the traversal member
> +name '\fBnext\fR'.
> +.PP
> +The second \fBfor\fR construct traverses the linked list through a specified
> +struct list_head variable \fBs1_list_head.next\fR. The global symbol
> +\fBs1_list_head\fR is a start address of the linked list and its \fBnext\fR
> +member points to the address of struct list_head type member '\fBlist\fR' from
> +\fBstruct s1\fR. Hence a \fBwithin\fR terminal is used to specify the structure
> +name '\fBs1\fR' that can be traversed using \fBs1_list_head.next\fR variable
> +along with the name of struct list_head type member '\fBlist\fR' which is part
> +of the linked list that starts from \fBs1_list_head\fR global symbol.
> +.PP
> +The third \fBfor\fR construct traverses the array elements specified through
> +a array variable \fBmyarray\fR.
> +.br
>  .SH SEE ALSO
>  .PP
>  makedumpfile(8)
> diff --git a/makedumpfile.h b/makedumpfile.h
> index 94f3521..1c61edb 100644
> --- a/makedumpfile.h
> +++ b/makedumpfile.h
> @@ -1275,6 +1275,7 @@ struct dwarf_info {
>  #define TYPE_ARRAY	0x02
>  #define TYPE_PTR	0x04
>  #define TYPE_STRUCT	0x08
> +#define TYPE_LIST_HEAD	0x10
>  
>  extern struct dwarf_info	dwarf_info;
>  
> @@ -1297,18 +1298,25 @@ struct config_entry {
>  	unsigned long long	sym_addr;	/* Symbol address */
>  	unsigned long long	addr;		/* Symbol address or
>  						   value pointed by sym_addr */
> +	unsigned long long	cmp_addr;	/* for LIST_ENTRY */
>  	unsigned long		offset;
>  	unsigned long		type_flag;
>  	long			array_length;
> +	long			index;
>  	long			size;
>  	int			line;	/* Line number in config file. */
> +	struct config_entry	*refer_to;
>  	struct config_entry	*next;
>  };
>  
>  /* flags for config_entry.flag */
>  #define FILTER_ENTRY	0x0001
>  #define SIZE_ENTRY	0x0002
> +#define ITERATION_ENTRY	0x0004
> +#define LIST_ENTRY	0x0008
>  #define SYMBOL_ENTRY	0x0010
> +#define VAR_ENTRY	0x0020
> +#define TRAVERSAL_ENTRY	0x0040
>  #define ENTRY_RESOLVED	0x8000
>  
>  /*
> @@ -1329,13 +1337,18 @@ struct filter_config {
>  
>  struct config {
>  	char			*module_name;
> -	struct config_entry	*filter_symbol;
> -	struct config_entry	*size_symbol;
> +	struct config_entry	*iter_entry;
> +	struct config_entry	*list_entry;
> +	int			num_filter_symbols;
> +	struct config_entry	**filter_symbol;
> +	struct config_entry	**size_symbol;
>  };
>  
>  #define IS_KEYWORD(tkn)	\
>  	(!strcmp(tkn, "erase") || !strcmp(tkn, "size") || \
> -	!strcmp(tkn, "nullify"))
> +	!strcmp(tkn, "nullify") || !strcmp(tkn, "for") || \
> +	!strcmp(tkn, "in") || !strcmp(tkn, "within") || \
> +	!strcmp(tkn, "endfor"))
>  
>  int readmem(int type_addr, unsigned long long addr, void *bufptr, size_t size);
>  off_t paddr_to_offset(unsigned long long paddr);
> 

_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 6/8] makedumpfile: Read and process 'for' command from config file.
  2011-08-11  8:06 ` Ken'ichi Ohmichi
@ 2011-08-30 18:16   ` Mahesh J Salgaonkar
  2011-09-01  8:06     ` Ken'ichi Ohmichi
  0 siblings, 1 reply; 8+ messages in thread
From: Mahesh J Salgaonkar @ 2011-08-30 18:16 UTC (permalink / raw)
  To: Ken'ichi Ohmichi
  Cc: V Srivatsa, Ananth N Mavinakayanahalli, kexec, Dave Anderson,
	Prerna Saxena, Reinhard

On 2011-08-11 17:06:21 Thu, Ken'ichi Ohmichi wrote:
> 
> Hi Mahesh,
> 
> On Wed, 18 May 2011 01:35:19 +0530
> Mahesh J Salgaonkar <mahesh@linux.vnet.ibm.com> wrote:
> >
> > From: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
> > 
> > This patch adds support to read and process 'for' command from config file
> > to filter multiple memory locations that are accessible through an array,
> > link list or list_head.
> > 
> > The syntax for 'for' (loop construct) filter command is:
> > 
> > for <id> in {<ArrayVar> |
> > 	     <StructVar> via <NextMember> |
> > 	     <ListHeadVar> within <StructName>:<ListHeadMember>}
> > 	erase <id>[.MemberExpression] [size <SizeExpression>|nullify]
> > 	[erase <id> ...]
> > 	[...]
> > endfor
> > 
> > Updated filter.conf(8) man page that describes the syntax for loop construct.
> > 
> > Signed-off-by: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
> > Signed-off-by: Prerna Saxena <prerna@linux.vnet.ibm.com>
> > ---
> 
> Thank you for the patch.
> I think this patch is good.
> 
> Acked-by: Ken'ichi Ohmichi <oomichi@mxs.nes.nec.co.jp>
> 
> 
> Thanks
> Ken'ichi Ohmichi

Hi Ken'ichi,

As I told you earlier I found few BUGs w.r.t patch 6/8 that processes loop
construct. Please find the patch below that fixes those BUGs. Please review.

Thanks,
-Mahesh.

-------

[PATCH] makedumpfile: Fix array traversal for array of structure and char type.

From: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>

Introduce new function get_next_list_entry() that returns config entry
for each element in the LIST entry. This approach improves the handling
of LIST entry. With this change the function get_config_symbol_addr()
no longer required to handle LIST entry separately.

This patch fixes following BUGs:

- If the loop construct is used for array of structures (non-pointer), then
the array index value is not incremented resulting in an infinite loop. This
patch fixes this BUG.
- The loop construct used for array of char* (pointer) silently fails and
does not filter the strings.
- Corrected 2nd argument while calling get_structure_size().
- Fixed dwarf_info.c:get_data_array_length() to handle array of const data
type.
  e.g.
	<1><1a49df3>: Abbrev Number: 90 (DW_TAG_variable)
	   <1a49df4>   DW_AT_name        : policycap_names
	   <1a49df8>   DW_AT_decl_file   : 1
	   <1a49df9>   DW_AT_decl_line   : 43
	   <1a49dfa>   DW_AT_type        : <0x1a49e08>
	<1><1a49e08>: Abbrev Number: 7 (DW_TAG_const_type)
	   <1a49e09>   DW_AT_type        : <0x1a49de3>
	<1><1a49de3>: Abbrev Number: 4 (DW_TAG_array_type)
	   <1a49de4>   DW_AT_type        : <0x1a3b276>
	   <1a49de8>   DW_AT_sibling     : <0x1a49df3>


Signed-off-by: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
---
 dwarf_info.c   |    8 ++
 makedumpfile.c |  292 +++++++++++++++++++++++++++++++++++---------------------
 2 files changed, 188 insertions(+), 112 deletions(-)

diff --git a/dwarf_info.c b/dwarf_info.c
index 744ecf7..46dcd8e 100644
--- a/dwarf_info.c
+++ b/dwarf_info.c
@@ -328,6 +328,14 @@ get_data_array_length(Dwarf_Die *die)
 		return FALSE;
 	}
 	tag = dwarf_tag(&die_type);
+	if (tag == DW_TAG_const_type) {
+		/* This array is of const type. Get the die type again */
+		if (!get_die_type(&die_type, &die_type)) {
+			ERRMSG("Can't get CU die of DW_AT_type.\n");
+			return FALSE;
+		}
+		tag = dwarf_tag(&die_type);
+	}
 	if (tag != DW_TAG_array_type) {
 		/*
 		 * This kernel doesn't have the member of array.
diff --git a/makedumpfile.c b/makedumpfile.c
index 599950d..1a88171 100644
--- a/makedumpfile.c
+++ b/makedumpfile.c
@@ -6763,11 +6763,27 @@ read_pointer_value(unsigned long long addr)
 	return val;
 }
 
+static long
+get_strlen(unsigned long long addr)
+{
+	char buf[BUFSIZE + 1];
+	long len = 0;
+
+	/*
+	 * Determine the string length for 'char' pointer.
+	 * BUFSIZE(1024) is the upper limit for string length.
+	 */
+	if (readmem(VADDR, addr, buf, BUFSIZE)) {
+		buf[BUFSIZE] = '\0';
+		len = strlen(buf);
+	}
+	return len;
+}
+
 int
 resolve_config_entry(struct config_entry *ce, unsigned long long base_addr,
 						char *base_struct_name)
 {
-	char buf[BUFSIZE + 1];
 	unsigned long long symbol;
 
 	if (ce->flag & SYMBOL_ENTRY) {
@@ -6866,7 +6882,7 @@ resolve_config_entry(struct config_entry *ce, unsigned long long base_addr,
 		 * If this is a struct or list_head data type then
 		 * create a leaf node entry with 'next' member.
 		 */
-		if ((ce->type_flag & TYPE_BASE)
+		if (((ce->type_flag & (TYPE_BASE | TYPE_ARRAY)) == TYPE_BASE)
 					&& (strcmp(ce->type_name, "void")))
 			return FALSE;
 
@@ -6905,17 +6921,14 @@ resolve_config_entry(struct config_entry *ce, unsigned long long base_addr,
 			ce->size = 0;
 
 	}
-	if ((ce->type_flag & TYPE_BASE) && (ce->type_flag & TYPE_PTR)) {
+	if ((ce->type_flag & TYPE_BASE) && (ce->type_flag & TYPE_PTR)
+					&& !(ce->type_flag & TYPE_ARRAY)) {
 		/*
 		 * Determine the string length for 'char' pointer.
 		 * BUFSIZE(1024) is the upper limit for string length.
 		 */
-		if (!strcmp(ce->type_name, "char")) {
-			if (readmem(VADDR, ce->addr, buf, BUFSIZE)) {
-				buf[BUFSIZE] = '\0';
-				ce->size = strlen(buf);
-			}
-		}
+		if (!strcmp(ce->type_name, "char"))
+			ce->size = get_strlen(ce->addr);
 	}
 	if (!ce->next && (ce->flag & SIZE_ENTRY)) {
 		void *val;
@@ -6968,80 +6981,11 @@ get_config_symbol_addr(struct config_entry *ce,
 			unsigned long long base_addr,
 			char *base_struct_name)
 {
-	unsigned long addr = 0;
-
 	if (!(ce->flag & ENTRY_RESOLVED)) {
 		if (!resolve_config_entry(ce, base_addr, base_struct_name))
 			return 0;
 	}
 
-	if ((ce->flag & LIST_ENTRY)) {
-		/* handle List entry differently */
-		if (!ce->next) {
-			/* leaf node. */
-			if (ce->type_flag & TYPE_ARRAY) {
-				if (ce->index == ce->array_length)
-					return 0;
-				if (!(ce->type_flag & TYPE_PTR))
-					return (ce->addr +
-							(ce->index * ce->size));
-				/* Array of pointers.
-				 *
-				 * Array may contain NULL pointers at some
-				 * indexes. Hence return the next non-null
-				 * address value.
-				 */
-				while (ce->index < ce->array_length) {
-					addr = read_pointer_value(ce->addr +
-						(ce->index * get_pointer_size()));
-					ce->index++;
-					if (addr)
-						break;
-				}
-				return addr;
-			}
-			else {
-				if (ce->addr == ce->cmp_addr)
-					return 0;
-
-				/* Set the leaf node as unresolved, so that
-				 * it will be resolved every time when
-				 * get_config_symbol_addr is called untill
-				 * it hits the exit condiftion.
-				 */
-				ce->flag &= ~ENTRY_RESOLVED;
-			}
-		}
-		else if ((ce->next->next == NULL) &&
-					!(ce->next->type_flag & TYPE_ARRAY)) {
-			/* the next node is leaf node. for non-array element
-			 * Set the sym_addr and addr of this node with that of
-			 * leaf node.
-			 */
-			addr = ce->addr;
-			ce->addr = ce->next->addr;
-
-			if (!(ce->type_flag & TYPE_LIST_HEAD)) {
-				if (addr == ce->next->cmp_addr)
-					return 0;
-
-				if (!ce->next->cmp_addr) {
-					/* safeguard against circular
-					 * link-list
-					 */
-					ce->next->cmp_addr = addr;
-				}
-
-				/* Force resolution of traversal node */
-				if (ce->addr && !resolve_config_entry(ce->next,
-						ce->addr, ce->type_name))
-					return 0;
-
-				return addr;
-			}
-		}
-	}
-
 	if (ce->next && ce->addr) {
 		/* Populate nullify flag down the list */
 		ce->next->nullify = ce->nullify;
@@ -7083,6 +7027,116 @@ get_config_symbol_size(struct config_entry *ce,
 	}
 }
 
+int
+get_next_list_entry(struct config_entry *ce, unsigned long long base_addr,
+			char *base_struct_name, struct config_entry *out_ce)
+{
+	unsigned long addr = 0;
+
+	/* This function only deals with LIST_ENTRY config entry. */
+	if (!(ce->flag & LIST_ENTRY))
+		return FALSE;
+
+	if (!(ce->flag & ENTRY_RESOLVED)) {
+		if (!resolve_config_entry(ce, base_addr, base_struct_name))
+			return FALSE;
+	}
+
+	if (!ce->next) {
+		/* leaf node. */
+		if (ce->type_flag & TYPE_ARRAY) {
+			if (ce->index == ce->array_length)
+				return FALSE;
+
+			if (ce->type_flag & TYPE_PTR) {
+				/* Array of pointers.
+				 *
+				 * Array may contain NULL pointers at some
+				 * indexes. Hence jump to the next non-null
+				 * address value.
+				 */
+				while (ce->index < ce->array_length) {
+					addr = read_pointer_value(ce->addr +
+						(ce->index * get_pointer_size()));
+					if (addr)
+						break;
+					ce->index++;
+				}
+				if (ce->index == ce->array_length)
+					return FALSE;
+				out_ce->sym_addr = ce->addr + (ce->index *
+							get_pointer_size());
+				out_ce->addr = addr;
+				if (!strcmp(ce->type_name, "char"))
+					out_ce->size = get_strlen(addr);
+				else
+					out_ce->size = ce->size;
+			}
+			else {
+				out_ce->sym_addr = ce->addr +
+							(ce->index * ce->size);
+				out_ce->addr = out_ce->sym_addr;
+				out_ce->size = ce->size;
+			}
+			ce->index++;
+		}
+		else {
+			if (ce->addr == ce->cmp_addr)
+				return FALSE;
+
+			out_ce->addr = ce->addr;
+			/* Set the leaf node as unresolved, so that
+			 * it will be resolved every time when
+			 * get_next_list_entry is called untill
+			 * it hits the exit condiftion.
+			 */
+			ce->flag &= ~ENTRY_RESOLVED;
+		}
+		return TRUE;
+	}
+	else if ((ce->next->next == NULL) &&
+				!(ce->next->type_flag & TYPE_ARRAY)) {
+		/* the next node is leaf node. for non-array element
+		 * Set the sym_addr and addr of this node with that of
+		 * leaf node.
+		 */
+		if (!(ce->type_flag & TYPE_LIST_HEAD)) {
+			if (!ce->addr || ce->addr == ce->next->cmp_addr)
+				return FALSE;
+
+			if (!ce->next->cmp_addr) {
+				/* safeguard against circular
+				 * link-list
+				 */
+				ce->next->cmp_addr = ce->addr;
+			}
+
+			out_ce->addr = ce->addr;
+			out_ce->sym_addr = ce->sym_addr;
+			out_ce->size = ce->size;
+
+			ce->sym_addr = ce->next->sym_addr;
+			ce->addr = ce->next->addr;
+
+			/* Force resolution of traversal node */
+			if (ce->addr && !resolve_config_entry(ce->next,
+					ce->addr, ce->type_name))
+				return FALSE;
+
+			return TRUE;
+		}
+		else {
+			ce->sym_addr = ce->next->sym_addr;
+			ce->addr = ce->next->addr;
+		}
+	}
+
+	if (ce->next && ce->addr)
+		return get_next_list_entry(ce->next, ce->addr,
+						ce->type_name, out_ce);
+	return FALSE;
+}
+
 static int
 resolve_list_entry(struct config_entry *ce, unsigned long long base_addr,
 			char *base_struct_name, char **out_type_name,
@@ -7308,31 +7362,14 @@ initialize_iteration_entry(struct config_entry *ie,
 								ie->line);
 			ie->next->flag &= ~SYMBOL_ENTRY;
 		}
-	}
-	else {
-		if (ie->type_name) {
-			/* looks like user has used 'within' keyword for
-			 * non-list_head VAR. Print the warning and continue.
-			 */
-			ERRMSG("Warning: line %d: 'within' keyword not "
-				"required for ArrayVar/StructVar.\n", ie->line);
-			free(ie->type_name);
-
-			/* remove the next list_head member from iteration
-			 * entry that would have added as part of 'within'
-			 * keyword processing.
-			 */
-			if (ie->next) {
-				free_config_entry(ie->next);
-				ie->next = NULL;
-			}
-		}
-		ie->type_name = strdup(type_name);
-	}
 
-	if (!ie->size) {
-		ie->size = get_structure_size(ie->type_name,
-						DWARF_INFO_GET_STRUCT_SIZE);
+		/*
+		 * For list_head find out the size of the StructName and
+		 * populate ie->size now. For array and link list we get the
+		 * size info from config entry returned by
+		 * get_next_list_entry().
+		 */
+		ie->size = get_structure_size(ie->type_name, 0);
 		if (ie->size == FAILED_DWARFINFO) {
 			ERRMSG("Config error at %d: "
 				"Can't get size for type: %s.\n",
@@ -7345,8 +7382,7 @@ initialize_iteration_entry(struct config_entry *ie,
 				ie->line, ie->type_name);
 			return FALSE;
 		}
-	}
-	if (type_flag & TYPE_LIST_HEAD) {
+
 		if (!resolve_config_entry(ie->next, 0, ie->type_name))
 			return FALSE;
 
@@ -7356,6 +7392,34 @@ initialize_iteration_entry(struct config_entry *ie,
 				ie->next->line, ie->next->name);
 			return FALSE;
 		}
+		ie->type_flag = TYPE_STRUCT;
+	}
+	else {
+		if (ie->type_name) {
+			/* looks like user has used 'within' keyword for
+			 * non-list_head VAR. Print the warning and continue.
+			 */
+			ERRMSG("Warning: line %d: 'within' keyword not "
+				"required for ArrayVar/StructVar.\n", ie->line);
+			free(ie->type_name);
+
+			/* remove the next list_head member from iteration
+			 * entry that would have added as part of 'within'
+			 * keyword processing.
+			 */
+			if (ie->next) {
+				free_config_entry(ie->next);
+				ie->next = NULL;
+			}
+		}
+		/*
+		 * Set type flag for iteration entry. The iteration entry holds
+		 * individual element from array/list, hence strip off the
+		 * array type flag bit.
+		 */
+		ie->type_name = strdup(type_name);
+		ie->type_flag = type_flag;
+		ie->type_flag &= ~TYPE_ARRAY;
 	}
 	return TRUE;
 }
@@ -7363,25 +7427,29 @@ initialize_iteration_entry(struct config_entry *ie,
 int
 list_entry_empty(struct config_entry *le, struct config_entry *ie)
 {
-	unsigned long long addr;
+	struct config_entry ce;
 
 	/* Error out if arguments are not correct */
 	if (!(le->flag & LIST_ENTRY) || !(ie->flag & ITERATION_ENTRY)) {
 		ERRMSG("Invalid arguments\n");
 		return TRUE;
 	}
-	addr = get_config_symbol_addr(le, 0, NULL);
-	if (!addr)
+
+	memset(&ce, 0, sizeof(struct config_entry));
+	/* get next available entry from LIST entry. */
+	if (!get_next_list_entry(le, 0, NULL, &ce))
 		return TRUE;
 
 	if (ie->next) {
 		/* we are dealing with list_head */
-		ie->next->addr = addr;
-		ie->addr = addr - ie->next->offset;
-		//resolve_iteration_entry(ie, addr);
+		ie->next->addr = ce.addr;
+		ie->addr = ce.addr - ie->next->offset;
+	}
+	else {
+		ie->addr = ce.addr;
+		ie->sym_addr = ce.sym_addr;
+		ie->size = ce.size;
 	}
-	else
-		ie->addr = addr;
 	return FALSE;
 }
 


-- 
Mahesh J Salgaonkar


_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 6/8] makedumpfile: Read and process 'for' command from config file.
  2011-08-30 18:16   ` Mahesh J Salgaonkar
@ 2011-09-01  8:06     ` Ken'ichi Ohmichi
  2011-09-05 14:40       ` Mahesh J Salgaonkar
  0 siblings, 1 reply; 8+ messages in thread
From: Ken'ichi Ohmichi @ 2011-09-01  8:06 UTC (permalink / raw)
  To: mahesh
  Cc: V Srivatsa, Ananth N Mavinakayanahalli, kexec, Dave Anderson,
	Prerna Saxena, Reinhard


Hi Mahesh,

Thank you for the patch.
I have one question.

On Tue, 30 Aug 2011 23:46:42 +0530
Mahesh J Salgaonkar <mahesh@linux.vnet.ibm.com> wrote:
> 
> [PATCH] makedumpfile: Fix array traversal for array of structure and char type.
> 
> From: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
> 
> Introduce new function get_next_list_entry() that returns config entry
> for each element in the LIST entry. This approach improves the handling
> of LIST entry. With this change the function get_config_symbol_addr()
> no longer required to handle LIST entry separately.
> 
> This patch fixes following BUGs:

[..]

> - The loop construct used for array of char* (pointer) silently fails and
> does not filter the strings.

Did the silent failure happen at the following code of list_entry_empty() ?

   7373         addr = get_config_symbol_addr(le, 0, NULL);
   7374         if (!addr)
   7375                 return TRUE;

If list_entry_empty() returns TRUE, a "while" loop of process_config() breaks
and update_filter_info() is not called.
Is that the problem you said ?

If yes, I feel the behavior related with this problem has not changed.
because the method for getting a pointer array has not changed like the
following:

o OLD
    111 -                               while (ce->index < ce->array_length) {
    112 -                                       addr = read_pointer_value(ce->addr +
    113 -                                               (ce->index * get_pointer_size()));
    114 -                                       ce->index++;
    115 -                                       if (addr)
    116 -                                               break;
    117 -                               }
    118 -                               return addr;

o NEW
    197 +                               while (ce->index < ce->array_length) {
    198 +                                       addr = read_pointer_value(ce->addr +
    199 +                                               (ce->index * get_pointer_size()));
    200 +                                       if (addr)
    201 +                                               break;
    202 +                                       ce->index++;
    203 +                               }
    204 +                               if (ce->index == ce->array_length)
    205 +                                       return FALSE;


I think the other bugfixes are right, thanks.


Thanks
Ken'ichi Ohmichi

> ---
>  dwarf_info.c   |    8 ++
>  makedumpfile.c |  292 +++++++++++++++++++++++++++++++++++---------------------
>  2 files changed, 188 insertions(+), 112 deletions(-)
> 
> diff --git a/dwarf_info.c b/dwarf_info.c
> index 744ecf7..46dcd8e 100644
> --- a/dwarf_info.c
> +++ b/dwarf_info.c
> @@ -328,6 +328,14 @@ get_data_array_length(Dwarf_Die *die)
>  		return FALSE;
>  	}
>  	tag = dwarf_tag(&die_type);
> +	if (tag == DW_TAG_const_type) {
> +		/* This array is of const type. Get the die type again */
> +		if (!get_die_type(&die_type, &die_type)) {
> +			ERRMSG("Can't get CU die of DW_AT_type.\n");
> +			return FALSE;
> +		}
> +		tag = dwarf_tag(&die_type);
> +	}
>  	if (tag != DW_TAG_array_type) {
>  		/*
>  		 * This kernel doesn't have the member of array.
> diff --git a/makedumpfile.c b/makedumpfile.c
> index 599950d..1a88171 100644
> --- a/makedumpfile.c
> +++ b/makedumpfile.c
> @@ -6763,11 +6763,27 @@ read_pointer_value(unsigned long long addr)
>  	return val;
>  }
>  
> +static long
> +get_strlen(unsigned long long addr)
> +{
> +	char buf[BUFSIZE + 1];
> +	long len = 0;
> +
> +	/*
> +	 * Determine the string length for 'char' pointer.
> +	 * BUFSIZE(1024) is the upper limit for string length.
> +	 */
> +	if (readmem(VADDR, addr, buf, BUFSIZE)) {
> +		buf[BUFSIZE] = '\0';
> +		len = strlen(buf);
> +	}
> +	return len;
> +}
> +
>  int
>  resolve_config_entry(struct config_entry *ce, unsigned long long base_addr,
>  						char *base_struct_name)
>  {
> -	char buf[BUFSIZE + 1];
>  	unsigned long long symbol;
>  
>  	if (ce->flag & SYMBOL_ENTRY) {
> @@ -6866,7 +6882,7 @@ resolve_config_entry(struct config_entry *ce, unsigned long long base_addr,
>  		 * If this is a struct or list_head data type then
>  		 * create a leaf node entry with 'next' member.
>  		 */
> -		if ((ce->type_flag & TYPE_BASE)
> +		if (((ce->type_flag & (TYPE_BASE | TYPE_ARRAY)) == TYPE_BASE)
>  					&& (strcmp(ce->type_name, "void")))
>  			return FALSE;
>  
> @@ -6905,17 +6921,14 @@ resolve_config_entry(struct config_entry *ce, unsigned long long base_addr,
>  			ce->size = 0;
>  
>  	}
> -	if ((ce->type_flag & TYPE_BASE) && (ce->type_flag & TYPE_PTR)) {
> +	if ((ce->type_flag & TYPE_BASE) && (ce->type_flag & TYPE_PTR)
> +					&& !(ce->type_flag & TYPE_ARRAY)) {
>  		/*
>  		 * Determine the string length for 'char' pointer.
>  		 * BUFSIZE(1024) is the upper limit for string length.
>  		 */
> -		if (!strcmp(ce->type_name, "char")) {
> -			if (readmem(VADDR, ce->addr, buf, BUFSIZE)) {
> -				buf[BUFSIZE] = '\0';
> -				ce->size = strlen(buf);
> -			}
> -		}
> +		if (!strcmp(ce->type_name, "char"))
> +			ce->size = get_strlen(ce->addr);
>  	}
>  	if (!ce->next && (ce->flag & SIZE_ENTRY)) {
>  		void *val;
> @@ -6968,80 +6981,11 @@ get_config_symbol_addr(struct config_entry *ce,
>  			unsigned long long base_addr,
>  			char *base_struct_name)
>  {
> -	unsigned long addr = 0;
> -
>  	if (!(ce->flag & ENTRY_RESOLVED)) {
>  		if (!resolve_config_entry(ce, base_addr, base_struct_name))
>  			return 0;
>  	}
>  
> -	if ((ce->flag & LIST_ENTRY)) {
> -		/* handle List entry differently */
> -		if (!ce->next) {
> -			/* leaf node. */
> -			if (ce->type_flag & TYPE_ARRAY) {
> -				if (ce->index == ce->array_length)
> -					return 0;
> -				if (!(ce->type_flag & TYPE_PTR))
> -					return (ce->addr +
> -							(ce->index * ce->size));
> -				/* Array of pointers.
> -				 *
> -				 * Array may contain NULL pointers at some
> -				 * indexes. Hence return the next non-null
> -				 * address value.
> -				 */
> -				while (ce->index < ce->array_length) {
> -					addr = read_pointer_value(ce->addr +
> -						(ce->index * get_pointer_size()));
> -					ce->index++;
> -					if (addr)
> -						break;
> -				}
> -				return addr;
> -			}
> -			else {
> -				if (ce->addr == ce->cmp_addr)
> -					return 0;
> -
> -				/* Set the leaf node as unresolved, so that
> -				 * it will be resolved every time when
> -				 * get_config_symbol_addr is called untill
> -				 * it hits the exit condiftion.
> -				 */
> -				ce->flag &= ~ENTRY_RESOLVED;
> -			}
> -		}
> -		else if ((ce->next->next == NULL) &&
> -					!(ce->next->type_flag & TYPE_ARRAY)) {
> -			/* the next node is leaf node. for non-array element
> -			 * Set the sym_addr and addr of this node with that of
> -			 * leaf node.
> -			 */
> -			addr = ce->addr;
> -			ce->addr = ce->next->addr;
> -
> -			if (!(ce->type_flag & TYPE_LIST_HEAD)) {
> -				if (addr == ce->next->cmp_addr)
> -					return 0;
> -
> -				if (!ce->next->cmp_addr) {
> -					/* safeguard against circular
> -					 * link-list
> -					 */
> -					ce->next->cmp_addr = addr;
> -				}
> -
> -				/* Force resolution of traversal node */
> -				if (ce->addr && !resolve_config_entry(ce->next,
> -						ce->addr, ce->type_name))
> -					return 0;
> -
> -				return addr;
> -			}
> -		}
> -	}
> -
>  	if (ce->next && ce->addr) {
>  		/* Populate nullify flag down the list */
>  		ce->next->nullify = ce->nullify;
> @@ -7083,6 +7027,116 @@ get_config_symbol_size(struct config_entry *ce,
>  	}
>  }
>  
> +int
> +get_next_list_entry(struct config_entry *ce, unsigned long long base_addr,
> +			char *base_struct_name, struct config_entry *out_ce)
> +{
> +	unsigned long addr = 0;
> +
> +	/* This function only deals with LIST_ENTRY config entry. */
> +	if (!(ce->flag & LIST_ENTRY))
> +		return FALSE;
> +
> +	if (!(ce->flag & ENTRY_RESOLVED)) {
> +		if (!resolve_config_entry(ce, base_addr, base_struct_name))
> +			return FALSE;
> +	}
> +
> +	if (!ce->next) {
> +		/* leaf node. */
> +		if (ce->type_flag & TYPE_ARRAY) {
> +			if (ce->index == ce->array_length)
> +				return FALSE;
> +
> +			if (ce->type_flag & TYPE_PTR) {
> +				/* Array of pointers.
> +				 *
> +				 * Array may contain NULL pointers at some
> +				 * indexes. Hence jump to the next non-null
> +				 * address value.
> +				 */
> +				while (ce->index < ce->array_length) {
> +					addr = read_pointer_value(ce->addr +
> +						(ce->index * get_pointer_size()));
> +					if (addr)
> +						break;
> +					ce->index++;
> +				}
> +				if (ce->index == ce->array_length)
> +					return FALSE;
> +				out_ce->sym_addr = ce->addr + (ce->index *
> +							get_pointer_size());
> +				out_ce->addr = addr;
> +				if (!strcmp(ce->type_name, "char"))
> +					out_ce->size = get_strlen(addr);
> +				else
> +					out_ce->size = ce->size;
> +			}
> +			else {
> +				out_ce->sym_addr = ce->addr +
> +							(ce->index * ce->size);
> +				out_ce->addr = out_ce->sym_addr;
> +				out_ce->size = ce->size;
> +			}
> +			ce->index++;
> +		}
> +		else {
> +			if (ce->addr == ce->cmp_addr)
> +				return FALSE;
> +
> +			out_ce->addr = ce->addr;
> +			/* Set the leaf node as unresolved, so that
> +			 * it will be resolved every time when
> +			 * get_next_list_entry is called untill
> +			 * it hits the exit condiftion.
> +			 */
> +			ce->flag &= ~ENTRY_RESOLVED;
> +		}
> +		return TRUE;
> +	}
> +	else if ((ce->next->next == NULL) &&
> +				!(ce->next->type_flag & TYPE_ARRAY)) {
> +		/* the next node is leaf node. for non-array element
> +		 * Set the sym_addr and addr of this node with that of
> +		 * leaf node.
> +		 */
> +		if (!(ce->type_flag & TYPE_LIST_HEAD)) {
> +			if (!ce->addr || ce->addr == ce->next->cmp_addr)
> +				return FALSE;
> +
> +			if (!ce->next->cmp_addr) {
> +				/* safeguard against circular
> +				 * link-list
> +				 */
> +				ce->next->cmp_addr = ce->addr;
> +			}
> +
> +			out_ce->addr = ce->addr;
> +			out_ce->sym_addr = ce->sym_addr;
> +			out_ce->size = ce->size;
> +
> +			ce->sym_addr = ce->next->sym_addr;
> +			ce->addr = ce->next->addr;
> +
> +			/* Force resolution of traversal node */
> +			if (ce->addr && !resolve_config_entry(ce->next,
> +					ce->addr, ce->type_name))
> +				return FALSE;
> +
> +			return TRUE;
> +		}
> +		else {
> +			ce->sym_addr = ce->next->sym_addr;
> +			ce->addr = ce->next->addr;
> +		}
> +	}
> +
> +	if (ce->next && ce->addr)
> +		return get_next_list_entry(ce->next, ce->addr,
> +						ce->type_name, out_ce);
> +	return FALSE;
> +}
> +
>  static int
>  resolve_list_entry(struct config_entry *ce, unsigned long long base_addr,
>  			char *base_struct_name, char **out_type_name,
> @@ -7308,31 +7362,14 @@ initialize_iteration_entry(struct config_entry *ie,
>  								ie->line);
>  			ie->next->flag &= ~SYMBOL_ENTRY;
>  		}
> -	}
> -	else {
> -		if (ie->type_name) {
> -			/* looks like user has used 'within' keyword for
> -			 * non-list_head VAR. Print the warning and continue.
> -			 */
> -			ERRMSG("Warning: line %d: 'within' keyword not "
> -				"required for ArrayVar/StructVar.\n", ie->line);
> -			free(ie->type_name);
> -
> -			/* remove the next list_head member from iteration
> -			 * entry that would have added as part of 'within'
> -			 * keyword processing.
> -			 */
> -			if (ie->next) {
> -				free_config_entry(ie->next);
> -				ie->next = NULL;
> -			}
> -		}
> -		ie->type_name = strdup(type_name);
> -	}
>  
> -	if (!ie->size) {
> -		ie->size = get_structure_size(ie->type_name,
> -						DWARF_INFO_GET_STRUCT_SIZE);
> +		/*
> +		 * For list_head find out the size of the StructName and
> +		 * populate ie->size now. For array and link list we get the
> +		 * size info from config entry returned by
> +		 * get_next_list_entry().
> +		 */
> +		ie->size = get_structure_size(ie->type_name, 0);
>  		if (ie->size == FAILED_DWARFINFO) {
>  			ERRMSG("Config error at %d: "
>  				"Can't get size for type: %s.\n",
> @@ -7345,8 +7382,7 @@ initialize_iteration_entry(struct config_entry *ie,
>  				ie->line, ie->type_name);
>  			return FALSE;
>  		}
> -	}
> -	if (type_flag & TYPE_LIST_HEAD) {
> +
>  		if (!resolve_config_entry(ie->next, 0, ie->type_name))
>  			return FALSE;
>  
> @@ -7356,6 +7392,34 @@ initialize_iteration_entry(struct config_entry *ie,
>  				ie->next->line, ie->next->name);
>  			return FALSE;
>  		}
> +		ie->type_flag = TYPE_STRUCT;
> +	}
> +	else {
> +		if (ie->type_name) {
> +			/* looks like user has used 'within' keyword for
> +			 * non-list_head VAR. Print the warning and continue.
> +			 */
> +			ERRMSG("Warning: line %d: 'within' keyword not "
> +				"required for ArrayVar/StructVar.\n", ie->line);
> +			free(ie->type_name);
> +
> +			/* remove the next list_head member from iteration
> +			 * entry that would have added as part of 'within'
> +			 * keyword processing.
> +			 */
> +			if (ie->next) {
> +				free_config_entry(ie->next);
> +				ie->next = NULL;
> +			}
> +		}
> +		/*
> +		 * Set type flag for iteration entry. The iteration entry holds
> +		 * individual element from array/list, hence strip off the
> +		 * array type flag bit.
> +		 */
> +		ie->type_name = strdup(type_name);
> +		ie->type_flag = type_flag;
> +		ie->type_flag &= ~TYPE_ARRAY;
>  	}
>  	return TRUE;
>  }
> @@ -7363,25 +7427,29 @@ initialize_iteration_entry(struct config_entry *ie,
>  int
>  list_entry_empty(struct config_entry *le, struct config_entry *ie)
>  {
> -	unsigned long long addr;
> +	struct config_entry ce;
>  
>  	/* Error out if arguments are not correct */
>  	if (!(le->flag & LIST_ENTRY) || !(ie->flag & ITERATION_ENTRY)) {
>  		ERRMSG("Invalid arguments\n");
>  		return TRUE;
>  	}
> -	addr = get_config_symbol_addr(le, 0, NULL);
> -	if (!addr)
> +
> +	memset(&ce, 0, sizeof(struct config_entry));
> +	/* get next available entry from LIST entry. */
> +	if (!get_next_list_entry(le, 0, NULL, &ce))
>  		return TRUE;
>  
>  	if (ie->next) {
>  		/* we are dealing with list_head */
> -		ie->next->addr = addr;
> -		ie->addr = addr - ie->next->offset;
> -		//resolve_iteration_entry(ie, addr);
> +		ie->next->addr = ce.addr;
> +		ie->addr = ce.addr - ie->next->offset;
> +	}
> +	else {
> +		ie->addr = ce.addr;
> +		ie->sym_addr = ce.sym_addr;
> +		ie->size = ce.size;
>  	}
> -	else
> -		ie->addr = addr;
>  	return FALSE;
>  }
>  
> 
> 
> -- 
> Mahesh J Salgaonkar


_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 6/8] makedumpfile: Read and process 'for' command from config file.
  2011-09-01  8:06     ` Ken'ichi Ohmichi
@ 2011-09-05 14:40       ` Mahesh J Salgaonkar
  2011-09-07  6:41         ` Ken'ichi Ohmichi
  0 siblings, 1 reply; 8+ messages in thread
From: Mahesh J Salgaonkar @ 2011-09-05 14:40 UTC (permalink / raw)
  To: Ken'ichi Ohmichi
  Cc: V Srivatsa, Ananth N Mavinakayanahalli, kexec, Dave Anderson,
	Prerna Saxena, Reinhard

Hi Ken'ichi,

On 2011-09-01 17:06:58 Thu, Ken'ichi Ohmichi wrote:
> 
> Hi Mahesh,
> 
> Thank you for the patch.
> I have one question.
> 
> On Tue, 30 Aug 2011 23:46:42 +0530
> Mahesh J Salgaonkar <mahesh@linux.vnet.ibm.com> wrote:
> > 
> > [PATCH] makedumpfile: Fix array traversal for array of structure and char type.
> > 
> > From: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
> > 
> > Introduce new function get_next_list_entry() that returns config entry
> > for each element in the LIST entry. This approach improves the handling
> > of LIST entry. With this change the function get_config_symbol_addr()
> > no longer required to handle LIST entry separately.
> > 
> > This patch fixes following BUGs:
> 
> [..]
> 
> > - The loop construct used for array of char* (pointer) silently fails and
> > does not filter the strings.
> 
> Did the silent failure happen at the following code of list_entry_empty() ?
> 
>    7373         addr = get_config_symbol_addr(le, 0, NULL);
>    7374         if (!addr)
>    7375                 return TRUE;
> 

Nope. It use to fail in resolve_list_entry()->resolve_config_entry()
and following hunk from the patch fixes it:

@@ -6866,7 +6882,7 @@ resolve_config_entry(struct config_entry *ce, unsigned long long base_addr,
 		 * If this is a struct or list_head data type then
 		 * create a leaf node entry with 'next' member.
 		 */
-		if ((ce->type_flag & TYPE_BASE)
+		if (((ce->type_flag & (TYPE_BASE | TYPE_ARRAY)) == TYPE_BASE)
 					&& (strcmp(ce->type_name, "void")))
 			return FALSE;
 
The old code use to check only TYPE_BASE flag ignoring TYPE_ARRAY flag.

> If list_entry_empty() returns TRUE, a "while" loop of process_config() breaks
> and update_filter_info() is not called.
> Is that the problem you said ?
> 
> If yes, I feel the behavior related with this problem has not changed.
> because the method for getting a pointer array has not changed like the
> following:

The while loop below still holds good as it just ignores the pointer array
elements which are NULL pointers and we dont have anything to be erased for
NULL pointers.

> 
> o OLD
>     111 -                               while (ce->index < ce->array_length) {
>     112 -                                       addr = read_pointer_value(ce->addr +
>     113 -                                               (ce->index * get_pointer_size()));
>     114 -                                       ce->index++;
>     115 -                                       if (addr)
>     116 -                                               break;
>     117 -                               }
>     118 -                               return addr;
> 
> o NEW
>     197 +                               while (ce->index < ce->array_length) {
>     198 +                                       addr = read_pointer_value(ce->addr +
>     199 +                                               (ce->index * get_pointer_size()));
>     200 +                                       if (addr)
>     201 +                                               break;
>     202 +                                       ce->index++;
>     203 +                               }
>     204 +                               if (ce->index == ce->array_length)
>     205 +                                       return FALSE;
> 
> 
> I think the other bugfixes are right, thanks.
> 
> 
> Thanks
> Ken'ichi Ohmichi
> 

Thanks,
-Mahesh.


_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 6/8] makedumpfile: Read and process 'for' command from config file.
  2011-09-05 14:40       ` Mahesh J Salgaonkar
@ 2011-09-07  6:41         ` Ken'ichi Ohmichi
  2011-09-07 11:14           ` Mahesh Jagannath Salgaonkar
  0 siblings, 1 reply; 8+ messages in thread
From: Ken'ichi Ohmichi @ 2011-09-07  6:41 UTC (permalink / raw)
  To: mahesh
  Cc: V Srivatsa, Ananth N Mavinakayanahalli, kexec, Dave Anderson,
	Prerna Saxena, Reinhard


Hi Mahesh,

On Mon, 5 Sep 2011 20:10:33 +0530
Mahesh J Salgaonkar <mahesh@linux.vnet.ibm.com> wrote:
> > > 
> > > This patch fixes following BUGs:
> > 
> > [..]
> > 
> > > - The loop construct used for array of char* (pointer) silently fails and
> > > does not filter the strings.
> > 
> > Did the silent failure happen at the following code of list_entry_empty() ?
> > 
> >    7373         addr = get_config_symbol_addr(le, 0, NULL);
> >    7374         if (!addr)
> >    7375                 return TRUE;
> > 
> 
> Nope. It use to fail in resolve_list_entry()->resolve_config_entry()
> and following hunk from the patch fixes it:
> 
> @@ -6866,7 +6882,7 @@ resolve_config_entry(struct config_entry *ce, unsigned long long base_addr,
>  		 * If this is a struct or list_head data type then
>  		 * create a leaf node entry with 'next' member.
>  		 */
> -		if ((ce->type_flag & TYPE_BASE)
> +		if (((ce->type_flag & (TYPE_BASE | TYPE_ARRAY)) == TYPE_BASE)
>  					&& (strcmp(ce->type_name, "void")))
>  			return FALSE;
>  
> The old code use to check only TYPE_BASE flag ignoring TYPE_ARRAY flag.

Thank you for the explanation.
I feel I see it.
Is the below understanding right ?

At the part of earlier resolve_config_entry(), necessary information
(sym_addr, type_name, and array_length) can be gotten in the case
of pointer array.
However, the old resolve_config_entry() returned FALSE because of
the check lack you said.


Thanks
Ken'ichi Ohmichi

_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 6/8] makedumpfile: Read and process 'for' command from config file.
  2011-09-07  6:41         ` Ken'ichi Ohmichi
@ 2011-09-07 11:14           ` Mahesh Jagannath Salgaonkar
  2011-09-07 23:35             ` Ken'ichi Ohmichi
  0 siblings, 1 reply; 8+ messages in thread
From: Mahesh Jagannath Salgaonkar @ 2011-09-07 11:14 UTC (permalink / raw)
  To: Ken'ichi Ohmichi
  Cc: V Srivatsa, Ananth N Mavinakayanahalli, kexec, Dave Anderson,
	Prerna Saxena, Reinhard

Hi Ken'ichi,

On 09/07/2011 12:11 PM, Ken'ichi Ohmichi wrote:
> 
> Hi Mahesh,
> 
> On Mon, 5 Sep 2011 20:10:33 +0530
> Mahesh J Salgaonkar <mahesh@linux.vnet.ibm.com> wrote:
>>>>
>>>> This patch fixes following BUGs:
>>>
>>> [..]
>>>
>>>> - The loop construct used for array of char* (pointer) silently fails and
>>>> does not filter the strings.
>>>
>>> Did the silent failure happen at the following code of list_entry_empty() ?
>>>
>>>    7373         addr = get_config_symbol_addr(le, 0, NULL);
>>>    7374         if (!addr)
>>>    7375                 return TRUE;
>>>
>>
>> Nope. It use to fail in resolve_list_entry()->resolve_config_entry()
>> and following hunk from the patch fixes it:
>>We dont allow no-array 
>> @@ -6866,7 +6882,7 @@ resolve_config_entry(struct config_entry *ce, unsigned long long base_addr,
>>  		 * If this is a struct or list_head data type then
>>  		 * create a leaf node entry with 'next' member.
>>  		 */
>> -		if ((ce->type_flag & TYPE_BASE)
>> +		if (((ce->type_flag & (TYPE_BASE | TYPE_ARRAY)) == TYPE_BASE)
>>  					&& (strcmp(ce->type_name, "void")))
>>  			return FALSE;
>>  
>> The old code use to check only TYPE_BASE flag ignoring TYPE_ARRAY flag.
> 
> Thank you for the explanation.
> I feel I see it.
> Is the below understanding right ?
> 
> At the part of earlier resolve_config_entry(), necessary information
> (sym_addr, type_name, and array_length) can be gotten in the case
> of pointer array.

Yes, including the size information for type of element (type_name) in
the LIST entry.

> However, the old resolve_config_entry() returned FALSE because of
> the check lack you said.

Yes. The old resolve_config_entry() use to return FALSE only for array
of base type elements (e.g. array of char, int, long etc.). However, it
was working well for array of structures (pointer/non-pointer).

The LIST entry can be of one of the following kind:
1. Array of base types (pointer/non-pointer).
2. Array of structures (pointer/non-pointer).
3. Link list.
4. list_head link list.

The old code was working for all of the above except (1).

Thanks,
-Mahesh.

_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 6/8] makedumpfile: Read and process 'for' command from config file.
  2011-09-07 11:14           ` Mahesh Jagannath Salgaonkar
@ 2011-09-07 23:35             ` Ken'ichi Ohmichi
  0 siblings, 0 replies; 8+ messages in thread
From: Ken'ichi Ohmichi @ 2011-09-07 23:35 UTC (permalink / raw)
  To: Mahesh Jagannath Salgaonkar
  Cc: V Srivatsa, Ananth N Mavinakayanahalli, kexec, Dave Anderson,
	Prerna Saxena, Reinhard


Hi Mahesh,

On Wed, 07 Sep 2011 16:44:06 +0530
Mahesh Jagannath Salgaonkar <mahesh@linux.vnet.ibm.com> wrote:
> >>>>
> >>>> This patch fixes following BUGs:
> >>>
> >>> [..]
> >>>
> >>>> - The loop construct used for array of char* (pointer) silently fails and
> >>>> does not filter the strings.
> >>>
> >>> Did the silent failure happen at the following code of list_entry_empty() ?
> >>>
> >>>    7373         addr = get_config_symbol_addr(le, 0, NULL);
> >>>    7374         if (!addr)
> >>>    7375                 return TRUE;
> >>>
> >>
> >> Nope. It use to fail in resolve_list_entry()->resolve_config_entry()
> >> and following hunk from the patch fixes it:
> >>We dont allow no-array 
> >> @@ -6866,7 +6882,7 @@ resolve_config_entry(struct config_entry *ce, unsigned long long base_addr,
> >>  		 * If this is a struct or list_head data type then
> >>  		 * create a leaf node entry with 'next' member.
> >>  		 */
> >> -		if ((ce->type_flag & TYPE_BASE)
> >> +		if (((ce->type_flag & (TYPE_BASE | TYPE_ARRAY)) == TYPE_BASE)
> >>  					&& (strcmp(ce->type_name, "void")))
> >>  			return FALSE;
> >>  
> >> The old code use to check only TYPE_BASE flag ignoring TYPE_ARRAY flag.
> > 
> > Thank you for the explanation.
> > I feel I see it.
> > Is the below understanding right ?
> > 
> > At the part of earlier resolve_config_entry(), necessary information
> > (sym_addr, type_name, and array_length) can be gotten in the case
> > of pointer array.
> 
> Yes, including the size information for type of element (type_name) in
> the LIST entry.
> 
> > However, the old resolve_config_entry() returned FALSE because of
> > the check lack you said.
> 
> Yes. The old resolve_config_entry() use to return FALSE only for array
> of base type elements (e.g. array of char, int, long etc.). However, it
> was working well for array of structures (pointer/non-pointer).
> 
> The LIST entry can be of one of the following kind:
> 1. Array of base types (pointer/non-pointer).
> 2. Array of structures (pointer/non-pointer).
> 3. Link list.
> 4. list_head link list.
> 
> The old code was working for all of the above except (1).

Thank you.
I can see.


> [PATCH] makedumpfile: Fix array traversal for array of structure and char type.
> 
> From: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
> 
> Introduce new function get_next_list_entry() that returns config entry
> for each element in the LIST entry. This approach improves the handling
> of LIST entry. With this change the function get_config_symbol_addr()
> no longer required to handle LIST entry separately.
> 
> This patch fixes following BUGs:
> 
> - If the loop construct is used for array of structures (non-pointer), then
> the array index value is not incremented resulting in an infinite loop. This
> patch fixes this BUG.
> - The loop construct used for array of char* (pointer) silently fails and
> does not filter the strings.
> - Corrected 2nd argument while calling get_structure_size().
> - Fixed dwarf_info.c:get_data_array_length() to handle array of const data
> type.
>   e.g.
> 	<1><1a49df3>: Abbrev Number: 90 (DW_TAG_variable)
> 	   <1a49df4>   DW_AT_name        : policycap_names
> 	   <1a49df8>   DW_AT_decl_file   : 1
> 	   <1a49df9>   DW_AT_decl_line   : 43
> 	   <1a49dfa>   DW_AT_type        : <0x1a49e08>
> 	<1><1a49e08>: Abbrev Number: 7 (DW_TAG_const_type)
> 	   <1a49e09>   DW_AT_type        : <0x1a49de3>
> 	<1><1a49de3>: Abbrev Number: 4 (DW_TAG_array_type)
> 	   <1a49de4>   DW_AT_type        : <0x1a3b276>
> 	   <1a49de8>   DW_AT_sibling     : <0x1a49df3>
> 
> 
> Signed-off-by: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>

Acked-by: Ken'ichi Ohmichi <oomichi@mxs.nes.nec.co.jp>


Thanks
Ken'ichi Ohmichi

_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2011-09-08  0:58 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-05-17 20:05 [PATCH v2 6/8] makedumpfile: Read and process 'for' command from config file Mahesh J Salgaonkar
2011-08-11  8:06 ` Ken'ichi Ohmichi
2011-08-30 18:16   ` Mahesh J Salgaonkar
2011-09-01  8:06     ` Ken'ichi Ohmichi
2011-09-05 14:40       ` Mahesh J Salgaonkar
2011-09-07  6:41         ` Ken'ichi Ohmichi
2011-09-07 11:14           ` Mahesh Jagannath Salgaonkar
2011-09-07 23:35             ` Ken'ichi Ohmichi

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.