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

Dave Anderson anderson at redhat.com
Tue Apr 28 13:16:21 UTC 2015



----- Original Message -----
> Hi Dave,
> 
> I found it useful to be able listing structure's fields which are resided
> deeper than the first level:
> 
> crash> list super_block.s_list -s
> super_block.s_id,s_dquot.info[1].dqi_dirty_list,s_dquot.dqonoff_mutex.count.counter
> -H 0xc0a9c800
> de805c00
> s_id =
> "sysfs\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
> s_dquot.info[1].dqi_dirty_list = {
>   next = 0x0,
>   prev = 0x0
> },
> s_dquot.dqonoff_mutex.count.counter = 1
> de805800
> s_id =
> "rootfs\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
> s_dquot.info[1].dqi_dirty_list = {
>   next = 0x0,
>   prev = 0x0
> },
> s_dquot.dqonoff_mutex.count.counter = 1
> 
> Here is a draft patch which contains corresponding logic.
> I will appreciate your comments and suggestions.
> 
> Best regards,
> Alexandr

Hi Alexandr,

This looks quite useful.

I haven't looked at the details, but this kind of change modifies an a critical
area of usage that I cannot risk breaking.  

To prevent any kind of possible regression/breakage or other unexpected behavior,
would it be possible to call parse_for_member_new() *only* if it is required?  
In other words, if the commented-out MEMBER_EXISTS() call fails, then you could
set a flag to force it to attempt parse_for_member_new().  Otherwise, it could 
continue to use the older/simpler parse_for_member() function.

I'll dig into the patch later, test it out, etc., and it's entirely possible
that I'll change my mind, but paranoia forces me to err on the side of caution.

Dave






 
> --- crash-7.1.0.orig/tools.c	2015-02-06 21:44:11.000000000 +0300
> +++ crash-7.1.0/tools.c	2015-04-28 14:09:40.764966266 +0300
> @@ -3526,13 +3526,9 @@
>  						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]);
>  					}
>  				}
>  			}
> --- crash-7.1.0.orig/symbols.c	2015-02-06 21:44:11.000000000 +0300
> +++ crash-7.1.0/symbols.c	2015-04-28 13:46:12.760992540 +0300
> @@ -93,6 +93,7 @@
>  #define PARSE_FOR_DATA        (1)
>  #define PARSE_FOR_DECLARATION (2)
>  static void parse_for_member(struct datatype_member *, ulong);
> +static void parse_for_member_new(struct datatype_member *, ulong);
>  static int show_member_offset(FILE *, struct datatype_member *, char *);
>  
>  
> @@ -5576,17 +5577,19 @@
>  		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);
> +/*        parse_for_member(dm, PARSE_FOR_DATA); */
> +        parse_for_member_new(dm, PARSE_FOR_DATA);
>          close_tmpfile();
>                  
>  	restore_current_radix(restore_radix);
> @@ -7251,6 +7254,206 @@
>  	fprintf(ofp, ")\n");
>  }
>  
> +struct struct_elem {
> +    char field_name[BUFSIZE];
> +    char value[BUFSIZE];
> +    unsigned char is_array;
> +
> +
> +    struct struct_elem *parent;
> +    struct struct_elem *inner;
> +    struct struct_elem *next;
> +    struct struct_elem *prev;
> +};
> +
> +#define ALLOC_XXX_ELEMENT(xxx, clone_parent, is_array_root) \
> +{ \
> +    if (NULL == current) { \
> +        return; \
> +    } \
> +    current->xxx = calloc(1, sizeof(struct struct_elem)); \
> +    if (NULL == current->xxx) \
> +        error(FATAL, "cannot allocate any more memory!\n"); \
> +    if (clone_parent) current->xxx->parent = current->parent; \
> +    else current->xxx->parent = current; \
> +    current = current->xxx; \
> +    current->is_array = is_array_root; \
> +}
> +
> +#define ALLOC_INNER_ELEMENT ALLOC_XXX_ELEMENT(inner, 0, 0)
> +#define ALLOC_NEXT_ELEMENT ALLOC_XXX_ELEMENT(next, 1, 0)
> +#define ALLOC_ARRAY_ELEMENT ALLOC_XXX_ELEMENT(inner, 0, 1)
> +
> +unsigned char is_right_brace(const char *b) {
> +    unsigned char r = 0;
> +    for (; *b == ' '; b++);
> +    if (*b == '}') {
> +        b++;
> +        r = 1;
> +        if (*b == '}')
> +            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 *f;
> +    unsigned i;
> +    do {
> +        f = NULL;
> +        /* [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++);
> +
> +        while (s) {
> +
> +            if (0 == memcmp(s->field_name, n, b - n)) {
> +                f = s; // Keep found node
> +                s = s->inner;
> +                if (*b == '[') {
> +                    i = strtol(b + 1, &e, 10);
> +                    if (!(s->is_array && *e == ']'&& (e != b + 1)))
> +                        return NULL;
> +
> +                    while (i && s) {
> +                        s = s->next;
> +                        if (s)
> +                            i -= !!s->is_array;
> +                    }
> +
> +                }
> +                break;
> +            }
> +            if (NULL == s)
> +                return NULL;
> +            s = s->next;
> +            if (s && s->is_array)
> +                break; // That is we encounter the next array item
> +        }
> +        if (*p == '.') n = p + 1; else n = p;
> +    } while (*n);
> +
> +    return f;
> +}
> +
> +
> +
> +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 * (m * is_array + level); printf(" ")); \
> +        printf(__VA_ARGS__); }
> +
> +        if (p->inner) {
> +            PUT_INDENTED_STRING(1, "%s = %s\n", f ? f : p->field_name,
> p->inner->is_array ? "{{" : "{");
> +            dump_node(p->inner, NULL, is_array + level + 1,
> p->inner->is_array);
> +            PUT_INDENTED_STRING(1, "%s%s\n", p->inner->is_array ? "}}" :
> "}", p->next ? "," : "");
> +        } else {
> +            PUT_INDENTED_STRING(1, "%s = %s%s\n", f ? f : p->field_name,
> p->value, p->next && !p->next->is_array ? "," : "");
> +        }
> +        if (level) {
> +            p = p->next;
> +            if (p && p->is_array)
> +                PUT_INDENTED_STRING(0, "}, {\n");
> +        }
> +    } while (p && level);
> +}
> +
> +void free_structure(struct struct_elem *p) {
> +    if (p == NULL)
> +        return;
> +    free_structure(p->inner);
> +    free_structure(p->next);
> +    free(p);
> +}
> +
> +static void
> +parse_for_member_new(struct datatype_member *dm, ulong flag)
> +{
> +    struct struct_elem *current = NULL, *root = NULL;
> +
> +    char buf[BUFSIZE];
> +    char *p, *p1;
> +    unsigned int len;
> +    unsigned char trailing_comma;
> +
> +    rewind(pc->tmpfile);
> +
> +    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 (is_right_brace(buf)) {
> +            current = current->parent;
> +            if (trailing_comma)
> +                ALLOC_NEXT_ELEMENT;
> +            continue;
> +        }
> +
> +        for (p1 = buf; *p1 == ' '; p1++);
> +
> +        if (
> +                (p = strstr(buf, " = ")) &&
> +                ((*(p + 3) != '{') || (*(p + 3) == '{' && buf[len] == '}'))
> +           ) /* next = 0x0 or files = {0x0, 0x0} */
> +        {
> +            strncpy(current->field_name, p1, p - p1);
> +            strcpy(current->value, p + 3);
> +            if (trailing_comma)
> +                ALLOC_NEXT_ELEMENT;
> +        }
> +        else
> +        if (p = strstr(buf, " = {")) {
> +            strncpy(current->field_name, p1, p - p1);
> +            if (*(p + 4) == '\0') {
> +                ALLOC_INNER_ELEMENT;
> +            } else if (*(p + 4) == '{' && *(p + 5) == '\0') {
> +                ALLOC_ARRAY_ELEMENT;
> +            }
> +        }
> +        else
> +        if (strstr(buf, "}, {")) { /* Next array element */
> +            ALLOC_NEXT_ELEMENT;
> +            current->is_array = 1;
> +        }
> +        else
> +        if (buf == (p = strstr(buf, "struct "))) { // The least likely
> branch
> +            /* Our parent */
> +            current = calloc(1, sizeof(struct struct_elem));
> +            p += 7; /* strlen "struct " */
> +            p1 = strstr(buf, " {");
> +            strncpy(current->field_name, p, p1 - p);
> +            root = current;
> +            ALLOC_INNER_ELEMENT;
> +        }
> +    }
> +
> +    current = find_node(root->inner, dm->member);
> +    if (NULL == current)
> +        error(FATAL, "invalid structure reference: %s\n", dm->member);
> +
> +    dump_node(current, dm->member, 0, 0);
> +    free_structure(root);
> +
> +    return;
> +}
> +
>  /*
>   *  When a request is made to print just a member of a structure or union,
>   *  the whole datatype is dumped to a temporary file, and this routine
> 
> --
> Crash-utility mailing list
> Crash-utility at redhat.com
> https://www.redhat.com/mailman/listinfo/crash-utility
> 




More information about the Crash-utility mailing list