* [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.