[Crash-utility] [PATCH 1/1] Resend CLI list command, print deep structure members

Alexandr Terekhov Alexandr_Terekhov at epam.com
Mon May 11 13:13:53 UTC 2015


Hello Dave,

here is the second reincarnation of the parser. I've updated
`task` and `struct` handlers along with the `list` handler:

crash> list task_struct.tasks -s task_struct.comm,pid,thread.cr2 -H 0xde83a1e0
de840aa0
  comm = "ksoftirqd/0\000\000\000\000"
  pid = 4
  thread.cr2 = 0,
de840550
  comm = "stopper/0\000\000\000\000\000\000"
  pid = 5
  thread.cr2 = 0,
..................

crash> task -R restart_block.fn,restart_block.futex,thread.tls_array[0].base1,cpu_timers[2].next
  restart_block.fn = 0xc046fe10 <do_no_restart_syscall>,
  restart_block.futex = {
    uaddr = 0x0,
    val = 0,
    flags = 0,
    bitset = 0,
    time = 0,
    uaddr2 = 0x0
  },
  thread.tls_array[0].base1 = 112,
  cpu_timers[2].next = 0xdb9a7838,

crash> task_struct.comm,thread.tls_array[0].dpl 0xdb9a7550
  comm = "bash\000\000\000\000\000\000\000\000\000\000\000"
  thread.tls_array[0].dpl = 3,

I'm looking forward to your comments/suggestions.

Best regards,
Alexandr

--- crash-7.1.0.orig/symbols.c	2015-02-06 21:44:11.000000000 +0300
+++ crash-7.1.0/symbols.c	2015-05-11 16:11:56.587128595 +0300
@@ -93,6 +93,7 @@ static int dereference_pointer(ulong, st
 #define PARSE_FOR_DATA        (1)
 #define PARSE_FOR_DECLARATION (2)
 static void parse_for_member(struct datatype_member *, ulong);
+void parse_for_member_new(struct datatype_member *, ulong);
 static int show_member_offset(FILE *, struct datatype_member *, char *);
 
 
@@ -5576,17 +5577,18 @@ dump_struct_member(char *s, ulong addr,
 		FREEBUF(buf);
                 error(FATAL, "invalid structure name: %s\n", dm->name);
 	}
-	if (!MEMBER_EXISTS(dm->name, dm->member)) {
-		FREEBUF(buf);
-                error(FATAL, "invalid structure member name: %s\n", 
-			dm->member);
-	}
  
 	set_temporary_radix(radix, &restore_radix);
                 
         open_tmpfile();
         print_struct(dm->name, addr);
-        parse_for_member(dm, PARSE_FOR_DATA);
+
+	if (MEMBER_EXISTS(dm->name, dm->member)) {
+		parse_for_member(dm, PARSE_FOR_DATA);
+	} else {
+		parse_for_member_new(dm, PARSE_FOR_DATA);
+        }
+
         close_tmpfile();
                 
 	restore_current_radix(restore_radix);
@@ -6026,8 +6028,7 @@ cmd_datatype_common(ulong flags)
         if ((count_chars(args[optind], ',')+1) > MAXARGS)
                 error(FATAL, "too many members in comma-separated list!\n");
 
-	if ((count_chars(args[optind], '.') > 1) ||
-	    (LASTCHAR(args[optind]) == ',') ||
+	if ((LASTCHAR(args[optind]) == ',') ||
 	    (LASTCHAR(args[optind]) == '.'))
 		error(FATAL, "invalid format: %s\n", args[optind]);
 
@@ -6219,6 +6220,10 @@ do_datatype_addr(struct datatype_member
 		i = 0;
         	do {
                 	if (argc_members) {
+                                /* This call works fine with fields
+                                 * of the second, third, ... levels.
+                                 * There is no need to fix it
+                                 */
 				if (!member_to_datatype(memberlist[i], dm,
 							ANON_MEMBER_QUERY))
 					error(FATAL, "invalid data structure reference: %s.%s\n",
@@ -6250,7 +6255,12 @@ do_datatype_addr(struct datatype_member
 				if (dm->member) {
 					if (!((flags & DEREF_POINTERS) &&
 				    	    dereference_pointer(addr, dm, flags)))
-						parse_for_member(dm, PARSE_FOR_DATA);
+                                        {
+                                                if (count_chars(dm->member, '.'))
+                                                    parse_for_member_new(dm, PARSE_FOR_DATA);
+                                                else
+                                                    parse_for_member(dm, PARSE_FOR_DATA);
+                                        }
 					close_tmpfile();
 				}
 
@@ -6275,6 +6285,10 @@ do_datatype_declaration(struct datatype_
 	if (CRASHDEBUG(1))
 		dump_datatype_member(fp, dm);
 
+        if (dm->member && count_chars(dm->member, '.'))
+            error(FATAL, "invalid data structure reference: %s.%s\n",
+                    dm->name, dm->member);
+
         open_tmpfile();
         whatis_datatype(dm->name, flags, pc->tmpfile);
         rewind(pc->tmpfile);
@@ -7383,6 +7397,270 @@ next_item:
 	}
 }
 
+struct struct_elem {
+    char field_name[BUFSIZE];
+    unsigned char field_len;
+    char value[BUFSIZE];
+    unsigned char is_array_root:1;
+
+    struct struct_elem *parent;
+    struct struct_elem *inner;
+    struct struct_elem *next;
+    struct struct_elem *prev;
+};
+
+#define ALLOC_XXX_ELEMENT(xxx, clone_parent) \
+{ \
+    if (NULL == current) { \
+        error(FATAL, "Internal error while parsing structure %s\n", dm->name); \
+    } \
+    current->xxx = calloc(1, sizeof(struct struct_elem)); \
+    if (clone_parent) current->xxx->parent = current->parent; \
+    else current->xxx->parent = current; \
+    current = current->xxx; \
+}
+
+#define ALLOC_INNER_ELEMENT { ALLOC_XXX_ELEMENT(inner, 0) }
+#define ALLOC_NEXT_ELEMENT { ALLOC_XXX_ELEMENT(next, 1) }
+
+void free_structure(struct struct_elem *p) {
+    if (p == NULL)
+        return;
+    free_structure(p->inner);
+    free_structure(p->next);
+    free(p);
+}
+
+unsigned char is_right_brace(const char *b) {
+    unsigned char r = 0;
+    for (; *b == ' '; b++);
+    if (*b == '}') {
+        b++;
+        r = 1;
+        if (*b == '}') {
+            r = 2;
+            b++;
+        }
+    }
+
+    if (*b == ',')
+        b++;
+
+    if (*b == '\0')
+        return r;
+    else
+        return 0;
+}
+
+struct struct_elem *find_node(struct struct_elem *s, char *n) {
+    char *p, *b, *e;
+    struct struct_elem *t = s;
+    unsigned i;
+
+    if (('\0' == *n) || (NULL == s))
+        return s;
+
+    /* [n .. p) - struct member with index*/
+    if (NULL == (p = strstr(n, ".")))
+        p = n + strlen(n);
+
+    /* [n .. b) - struct member without index*/
+    for (b = n; (b < p) && (*b != '['); b++);
+
+    /* s - is the current level of items [s, s->next, ..., s->...->next] */
+    for (; s; s = s->next) {
+        if (*s->field_name == '\0')
+            continue;
+
+        /* `field_name` doesn't match */
+        if (((b - n) != s->field_len) || memcmp(s->field_name, n, b - n))
+            continue;
+
+        if (*b == '[') { /* Array */
+            i = strtol(b + 1, &e, 10);
+            /* Check if the current node is array and
+             * we've parsed index more or less correctly
+             */
+            if (!(s->is_array_root && *e == ']'&& (e != b + 1)))
+                return NULL;
+
+            /* Look for the i-th element */
+            for (s = s->inner; s && i; s = s->next, i--);
+            if (i || (NULL == s))
+                return NULL;
+        }
+        
+        /* Ok. We've found node, it's - the last member
+         * in our search string, let's return it.
+         */
+        if ('\0' == *p)
+            return s;
+        else
+            return find_node(s->inner, p + 1);
+    }
+
+    // We haven't found any field.
+    // Might happen, we've encountered anonymous structure
+    // of union. Lets try every record without `field_name`
+    s = t;
+    t = NULL;
+    for(; s; s = s->next) {
+        if (*s->field_name)
+            continue;
+        t = find_node(s->inner, n);
+        if (t)
+            break;
+    }
+
+    return t;
+}
+
+void dump_node(struct struct_elem *p, char *f, unsigned char level, unsigned char is_array) {
+    unsigned int i;
+    if (p == NULL)
+        return;
+    do {
+#define PUT_INDENTED_STRING(m, ...) { \
+        for (i = 0; i++ < 2 + 2 * (m * is_array + level); printf(" ")); \
+        printf(__VA_ARGS__); }
+
+        if (p->inner) {
+            if (*p->field_name) {
+                PUT_INDENTED_STRING(1, "%s = %s\n", f ? f : p->field_name, p->inner->is_array_root ? "{{" : "{");
+            } else {
+                if (f) /* For union */
+                    PUT_INDENTED_STRING(1, "%s = ", f);
+                PUT_INDENTED_STRING(1, "%s\n", p->inner->is_array_root ? "{{" : "{");
+            }
+            dump_node(p->inner, NULL, is_array + level + 1, p->inner->is_array_root);
+            PUT_INDENTED_STRING(1, "%s%s\n", p->inner->is_array_root ? "}}" : "}", (p->next && !p->next->is_array_root) ? "," : "");
+        } else {
+            PUT_INDENTED_STRING(1, "%s = %s%s", f ? f : p->field_name, p->value, p->next ? ",\n" : "\n");
+        }
+        if (level) {
+            p = p->next;
+            if (p && p->is_array_root)
+                PUT_INDENTED_STRING(0, "}, {\n");
+        }
+    } while (p && level);
+}
+
+void parse_for_member_new(struct datatype_member *dm,
+				ulong __attribute__ ((unused)) flag)
+{
+    struct struct_elem *i, *current = NULL, *root = NULL;
+
+    char buf[BUFSIZE];
+    char *p, *p1;
+    char *s_e; // structure_element
+    unsigned int len;
+    unsigned char trailing_comma, braces, found = 0;
+
+    rewind(pc->tmpfile);
+
+    root = calloc(1, sizeof(struct struct_elem));
+    current = root;
+    ALLOC_INNER_ELEMENT;
+
+    while (fgets(buf, BUFSIZE, pc->tmpfile)) {
+        len = strlen(buf) - 1;
+        for (; buf[len] <= ' '; buf[len--] = '\0');
+        if ((trailing_comma = (buf[len] == ',')))
+            buf[len--] = '\0';
+
+        if ((braces = is_right_brace(buf))) {
+            for (; braces && current; braces--)
+                current = current->parent;
+
+            if ((current->parent == root) || trailing_comma)
+                ALLOC_NEXT_ELEMENT;
+            continue;
+        }
+
+        for (p1 = buf; *p1 == ' '; p1++);
+        
+        if (NULL != (p = strstr(buf, " = "))) {
+            s_e = p + 3;
+        } else {
+            s_e = p1;
+        }
+
+        /*
+         * After that we have pointers:
+         *      foobar = bazzz
+         * -----^     ^  ^
+         * |    ------|  |
+         * |    |        |
+         * p1   p        s_e
+         *
+         * OR
+         *
+         *      {
+         *      ^
+         *      |
+         *  ---------
+         *  |       |
+         *  p1      s_e
+         *      
+         *      p == NULL
+         *
+         *
+         * p1   - the first non-whitespace symbol in line
+         * p    - pointer to line ' = '. If not NULL, there is identifier
+         * s_e  - element of structure (brace / double brace / array separator / scalar :))
+         *
+         */
+
+        if (current && p) strncpy(current->field_name, p1, p - p1);
+        current->field_len = p - p1;
+
+        if ( p && (*s_e != '{' || (*s_e == '{' && buf[len] == '}') )) {
+            /* Scalar or one-line array
+             * next = 0x0 
+             *   or 
+             * files = {0x0, 0x0}
+             */
+            strcpy(current->value, s_e);
+            if (trailing_comma) ALLOC_NEXT_ELEMENT;
+        }
+        else
+        if ( *s_e == '{' ) {
+            ALLOC_INNER_ELEMENT;
+            if (*(s_e + 1) == '{') {
+                current->parent->is_array_root = 1;
+                ALLOC_INNER_ELEMENT;
+            }
+        }
+        else
+        if (strstr(s_e, "}, {")) {
+            /* Next array element */
+            current = current->parent;
+            ALLOC_NEXT_ELEMENT;
+            ALLOC_INNER_ELEMENT;
+        }
+        else 
+        if (buf == (p = strstr(buf, "struct "))) {
+            p += 7; /* strlen "struct " */
+            p1 = strstr(buf, " {");
+            strncpy(current->field_name, p, p1 - p);
+            ALLOC_INNER_ELEMENT;
+        }
+    }
+
+    for (i = root->inner; i; i = i->next) {
+        if ((current = find_node(i->inner, dm->member))) {
+            dump_node(current, dm->member, 0, 0);
+            found = 1;
+            break;
+        }
+    }
+    
+    free_structure(root);
+
+    if (!found)
+        error(WARNING, "invalid structure reference: %s\n", dm->member);
+}
+
 /*
  *  Dig out a member name from a formatted gdb structure declaration dump,
  *  and print its offset from the named structure passed in.
--- crash-7.1.0.orig/task.c	2015-02-06 21:44:11.000000000 +0300
+++ crash-7.1.0/task.c	2015-05-11 15:52:57.387111513 +0300
@@ -107,6 +107,8 @@ static int sort_by_last_run(const void *
 static void sort_context_array_by_last_run(void);
 static void show_ps_summary(ulong);
 static void irqstacks_init(void);
+static void parse_task_thread(int argcnt, char *arglist[], struct task_context *);
+void parse_for_member_new(struct datatype_member *, ulong);
 
 /*
  *  Figure out how much space will be required to hold the task context
@@ -2746,13 +2748,9 @@ task_struct_member(struct task_context *
 	int argcnt;
 	char *arglist[MAXARGS];
 	char *refcopy;
-	char buf[BUFSIZE];
-	char lookfor1[BUFSIZE];
-	char lookfor2[BUFSIZE];
-	char lookfor3[BUFSIZE];
-	int header_printed;
-
-	header_printed = FALSE;
+        unsigned char call_new_parser = 0;
+        struct datatype_member dm;
+        char *member = GETBUF(BUFSIZE);
 
 	if ((count_chars(ref->str, ',')+1) > MAXARGS) {
 		error(INFO, 
@@ -2767,14 +2765,39 @@ task_struct_member(struct task_context *
 	argcnt = parse_line(refcopy, arglist);
 	for (i = 0; i < argcnt; i++)
 		if (!MEMBER_EXISTS("task_struct", arglist[i]) &&
-		    !MEMBER_EXISTS("thread_info", arglist[i]))
-			error(INFO, "%s: not a task_struct or thread_info member\n", 
-				arglist[i]);
-
+		    !MEMBER_EXISTS("thread_info", arglist[i])) {
+			if (count_chars(arglist[i], '.') || count_chars(arglist[i], '['))
+				call_new_parser = 1;
+			else
+                                error(INFO, "%s: not a task_struct or "
+                                        "thread_info member\n", arglist[i]);
+                }
         open_tmpfile();
         dump_struct("task_struct", tc->task, radix);
-	if (tt->flags & THREAD_INFO)
-		dump_struct("thread_info", tc->thread_info, radix);
+        if (tt->flags & THREAD_INFO)
+                dump_struct("thread_info", tc->thread_info, radix);
+        if (call_new_parser) {
+                for (i = 0; i < argcnt; i++) {
+                        dm.member = arglist[i];
+                        parse_for_member_new(&dm, 0);
+                }
+        } else {
+                parse_task_thread(argcnt, arglist, tc);
+        }
+        close_tmpfile();
+
+        FREEBUF(member);
+
+}
+
+static void parse_task_thread(int argcnt, char *arglist[], struct task_context *tc) {
+	char buf[BUFSIZE];
+	char lookfor1[BUFSIZE];
+	char lookfor2[BUFSIZE];
+	char lookfor3[BUFSIZE];
+	int header_printed = FALSE;
+        int i;
+
         rewind(pc->tmpfile);
 
 	BZERO(lookfor1, BUFSIZE);
@@ -2825,7 +2848,6 @@ task_struct_member(struct task_context *
 			}
 		}
 	}
-	close_tmpfile();
 }
 
 static char *ps_exclusive = 
--- crash-7.1.0.orig/tools.c	2015-02-06 21:44:11.000000000 +0300
+++ crash-7.1.0/tools.c	2015-05-11 15:52:57.391111513 +0300
@@ -3526,13 +3526,9 @@ do_list(struct list_data *ld)
 						dump_struct(ld->structname[i], 
 							next - ld->list_head_offset, radix);
 						break;
-					case 1:
+                                        default:
 						dump_struct_members(ld, i, next);
 						break;
-					default:
-						error(FATAL, 
-						    "invalid structure reference: %s\n",
-							ld->structname[i]);
 					}
 				}
 			}




More information about the Crash-utility mailing list