--- crash-7.0.2/main.c 2013-10-25 15:28:55.938879568 -0400 +++ crash-7.0.3/main.c 2013-10-15 15:55:10.136390264 -0400 @@ -1335,6 +1335,8 @@ dump_program_context(void) fprintf(fp, "%sGET_LOG", others++ ? "|" : ""); if (pc->flags2 & VMCOREINFO) fprintf(fp, "%sVMCOREINFO", others++ ? "|" : ""); + if (pc->flags2 & ALLOW_FP) + fprintf(fp, "%sALLOW_FP", others++ ? "|" : ""); fprintf(fp, ")\n"); fprintf(fp, " namelist: %s\n", pc->namelist); --- crash-7.0.2/tools.c 2013-10-25 15:28:55.922879569 -0400 +++ crash-7.0.3/tools.c 2013-10-25 10:09:31.938757537 -0400 @@ -989,7 +989,7 @@ dtoi(char *s, int flags, int *errptr) if ((s[j] < '0' || s[j] > '9')) break ; - if (s[j] != '\0' || (sscanf(s, "%d", &retval) != 1)) { + if (s[j] != '\0' || (sscanf(s, "%d", (int *)&retval) != 1)) { if (!(flags & QUIET)) error(INFO, "%s: \"%c\" is not a digit 0 - 9\n", s, s[j]); @@ -5489,6 +5489,19 @@ swap32(uint32_t val, int swap) return val; } +/* + * Get a sufficiently large buffer for cpumask. + * You should call FREEBUF() on the result when you no longer need it. + */ +ulong * +get_cpumask_buf(void) +{ + int cpulen; + if ((cpulen = STRUCT_SIZE("cpumask_t")) < 0) + cpulen = DIV_ROUND_UP(kt->cpus, BITS_PER_LONG) * sizeof(ulong); + return (ulong *)GETBUF(cpulen); +} + int make_cpumask(char *s, ulong *mask, int flags, int *errptr) { @@ -5505,14 +5518,20 @@ make_cpumask(char *s, ulong *mask, int f p = strtok(s, ","); while (p) { s = strtok(NULL, ""); - start = end = -1; - q = strtok(p, "-"); - start = dtoi(q, flags, errptr); - if ((q = strtok(NULL, "-"))) - end = dtoi(q, flags, errptr); - if (end == -1) - end = start; + if (STREQ(p, "a") || STREQ(p, "all")) { + start = 0; + end = kt->cpus - 1; + } else { + start = end = -1; + q = strtok(p, "-"); + start = dtoi(q, flags, errptr); + if ((q = strtok(NULL, "-"))) + end = dtoi(q, flags, errptr); + + if (end == -1) + end = start; + } for (i = start; i <= end; i++) SET_BIT(mask, i); --- crash-7.0.2/memory.c 2013-10-25 15:28:55.934879568 -0400 +++ crash-7.0.3/memory.c 2013-10-25 10:09:35.641757367 -0400 @@ -134,6 +134,10 @@ static char *error_handle_string(ulong); static void dump_mem_map(struct meminfo *); static void dump_mem_map_SPARSEMEM(struct meminfo *); static void fill_mem_map_cache(ulong, ulong, char *); +static void page_flags_init(void); +static int page_flags_init_from_pageflag_names(void); +static int page_flags_init_from_pageflags_enum(void); +static int translate_page_flags(char *, ulong); static void dump_free_pages(struct meminfo *); static int dump_zone_page_usage(void); static void dump_multidimensional_free_pages(struct meminfo *); @@ -613,6 +617,13 @@ vm_init(void) MEMBER_OFFSET_INIT(kmem_list3_free_objects, kmem_cache_node_struct, "free_objects"); MEMBER_OFFSET_INIT(kmem_list3_shared, kmem_cache_node_struct, "shared"); + /* + * Common to slab/slub + */ + ANON_MEMBER_OFFSET_INIT(page_slab, "page", "slab_cache"); + ANON_MEMBER_OFFSET_INIT(page_slab_page, "page", "slab_page"); + ANON_MEMBER_OFFSET_INIT(page_first_page, "page", "first_page"); + } else if (MEMBER_EXISTS("kmem_cache", "cpu_slab") && STRUCT_EXISTS("kmem_cache_node")) { vt->flags |= KMALLOC_SLUB; @@ -642,6 +653,7 @@ vm_init(void) ANON_MEMBER_OFFSET_INIT(page_slab, "page", "slab"); if (INVALID_MEMBER(page_slab)) ANON_MEMBER_OFFSET_INIT(page_slab, "page", "slab_cache"); + ANON_MEMBER_OFFSET_INIT(page_slab_page, "page", "slab_page"); ANON_MEMBER_OFFSET_INIT(page_first_page, "page", "first_page"); ANON_MEMBER_OFFSET_INIT(page_freelist, "page", "freelist"); if (INVALID_MEMBER(kmem_cache_objects)) { @@ -703,16 +715,16 @@ vm_init(void) } if (!kt->kernel_NR_CPUS) { - if (ARRAY_LENGTH(kmem_cache_s_cpudata)) + if (enumerator_value("WORK_CPU_UNBOUND", (long *)&value1)) + kt->kernel_NR_CPUS = (int)value1; + else if ((i = get_array_length("__per_cpu_offset", NULL, 0))) + kt->kernel_NR_CPUS = i; + else if (ARRAY_LENGTH(kmem_cache_s_cpudata)) kt->kernel_NR_CPUS = ARRAY_LENGTH(kmem_cache_s_cpudata); else if (ARRAY_LENGTH(kmem_cache_s_array)) kt->kernel_NR_CPUS = ARRAY_LENGTH(kmem_cache_s_array); else if (ARRAY_LENGTH(kmem_cache_cpu_slab)) kt->kernel_NR_CPUS = ARRAY_LENGTH(kmem_cache_cpu_slab); - else if (enumerator_value("WORK_CPU_UNBOUND", (long *)&value1)) - kt->kernel_NR_CPUS = (int)value1; - else if ((i = get_array_length("__per_cpu_offset", NULL, 0))) - kt->kernel_NR_CPUS = i; } if (CRASHDEBUG(1)) @@ -1025,8 +1037,7 @@ vm_init(void) kmem_cache_init(); - PG_reserved_flag_init(); - PG_slab_flag_init(); + page_flags_init(); vt->flags |= VM_INIT; } @@ -4533,15 +4544,37 @@ PG_slab_flag_init(void) char buf[BUFSIZE]; /* safe for a page struct */ /* - * Set the old defaults in case the search below fails. + * Set the old defaults in case all else fails. */ - if (VALID_MEMBER(page_pte)) { + if (enumerator_value("PG_slab", (long *)&flags)) { + vt->PG_slab = flags; + if (CRASHDEBUG(2)) + fprintf(fp, "PG_slab (enum): %lx\n", vt->PG_slab); + } else if (VALID_MEMBER(page_pte)) { if (THIS_KERNEL_VERSION < LINUX(2,6,0)) vt->PG_slab = 10; else if (THIS_KERNEL_VERSION >= LINUX(2,6,0)) vt->PG_slab = 7; - } else if (THIS_KERNEL_VERSION >= LINUX(2,6,0)) - vt->PG_slab = 7; + } else if (THIS_KERNEL_VERSION >= LINUX(2,6,0)) { + vt->PG_slab = 7; + } else { + if (try_get_symbol_data("vm_area_cachep", sizeof(void *), &vaddr) && + phys_to_page((physaddr_t)VTOP(vaddr), &pageptr) && + readmem(pageptr, KVADDR, buf, SIZE(page), + "vm_area_cachep page", RETURN_ON_ERROR|QUIET)) { + + flags = ULONG(buf + OFFSET(page_flags)); + + if ((bit = ffsl(flags))) { + vt->PG_slab = bit - 1; + + if (CRASHDEBUG(2)) + fprintf(fp, + "PG_slab bit: vaddr: %lx page: %lx flags: %lx => %ld\n", + vaddr, pageptr, flags, vt->PG_slab); + } + } + } if (vt->flags & KMALLOC_SLUB) { /* @@ -4561,34 +4594,20 @@ PG_slab_flag_init(void) fprintf(fp, "PG_head_tail_mask: %lx\n", vt->PG_head_tail_mask); } - - return; - } - - if (enumerator_value("PG_slab", (long *)&flags)) { - vt->PG_slab = flags; - if (CRASHDEBUG(2)) - fprintf(fp, "PG_slab (enum): %lx\n", vt->PG_slab); - return; - } - - if (try_get_symbol_data("vm_area_cachep", sizeof(void *), &vaddr) && - phys_to_page((physaddr_t)VTOP(vaddr), &pageptr) && - readmem(pageptr, KVADDR, buf, SIZE(page), - "vm_area_cachep page", RETURN_ON_ERROR|QUIET)) { - - flags = ULONG(buf + OFFSET(page_flags)); - - if ((bit = ffsl(flags))) { - vt->PG_slab = bit - 1; - - if (CRASHDEBUG(2)) - fprintf(fp, - "PG_slab bit: vaddr: %lx page: %lx flags: %lx => %ld\n", - vaddr, pageptr, flags, vt->PG_slab); - + } else { + if (enumerator_value("PG_tail", (long *)&flags)) + vt->PG_head_tail_mask = (1L << flags); + else if (enumerator_value("PG_compound", (long *)&flags) && + enumerator_value("PG_reclaim", (long *)&flags2)) { + vt->PG_head_tail_mask = ((1L << flags) | (1L << flags2)); + if (CRASHDEBUG(2)) + fprintf(fp, "PG_head_tail_mask: %lx (PG_compound|PG_reclaim)\n", + vt->PG_head_tail_mask); } } + + if (!vt->PG_slab) + error(INFO, "cannot determine PG_slab bit value\n"); } /* @@ -5009,7 +5028,10 @@ dump_mem_map_SPARSEMEM(struct meminfo *m bufferindex += sprintflag("%sreserved"); bufferindex += sprintf(outputbuffer+bufferindex, "\n"); } else if (THIS_KERNEL_VERSION > LINUX(2,4,9)) { - bufferindex += sprintf(outputbuffer+bufferindex, "%lx\n", flags); + if (vt->flags & PAGEFLAGS) + bufferindex += translate_page_flags(outputbuffer+bufferindex, flags); + else + bufferindex += sprintf(outputbuffer+bufferindex, "%lx\n", flags); } else { if ((flags >> v24_PG_locked) & 1) @@ -5444,7 +5466,10 @@ dump_mem_map(struct meminfo *mi) bufferindex += sprintflag("%sreserved"); bufferindex += sprintf(outputbuffer+bufferindex, "\n"); } else if (THIS_KERNEL_VERSION > LINUX(2,4,9)) { - bufferindex += sprintf(outputbuffer+bufferindex, "%lx\n", flags); + if (vt->flags & PAGEFLAGS) + bufferindex += translate_page_flags(outputbuffer+bufferindex, flags); + else + bufferindex += sprintf(outputbuffer+bufferindex, "%lx\n", flags); } else { if ((flags >> v24_PG_locked) & 1) @@ -5591,6 +5616,196 @@ fill_mem_map_cache(ulong pp, ulong ppend } } +static void +page_flags_init(void) +{ + if (!page_flags_init_from_pageflag_names()) + page_flags_init_from_pageflags_enum(); + + PG_reserved_flag_init(); + PG_slab_flag_init(); +} + +static int +page_flags_init_from_pageflag_names(void) +{ + int i, len; + char *buffer, *nameptr; + char namebuf[BUFSIZE]; + ulong mask; + void *name; + + MEMBER_OFFSET_INIT(trace_print_flags_mask, "trace_print_flags", "mask"); + MEMBER_OFFSET_INIT(trace_print_flags_name, "trace_print_flags", "name"); + STRUCT_SIZE_INIT(trace_print_flags, "trace_print_flags"); + + if (INVALID_SIZE(trace_print_flags) || + INVALID_MEMBER(trace_print_flags_mask) || + INVALID_MEMBER(trace_print_flags_name) || + !kernel_symbol_exists("pageflag_names") || + !(len = get_array_length("pageflag_names", NULL, 0))) + return FALSE; + + buffer = GETBUF(SIZE(trace_print_flags) * len); + + if (!readmem(symbol_value("pageflag_names"), KVADDR, buffer, + SIZE(trace_print_flags) * len, "pageflag_names array", + RETURN_ON_ERROR)) { + FREEBUF(buffer); + return FALSE; + } + + if (!(vt->pageflags_data = (struct pageflags_data *) + malloc(sizeof(struct pageflags_data) * len))) { + error(INFO, "cannot malloc pageflags_data cache\n"); + FREEBUF(buffer); + return FALSE; + } + + if (CRASHDEBUG(1)) + fprintf(fp, "pageflags from pageflag_names: \n"); + + for (i = 0; i < len; i++) { + mask = ULONG(buffer + (SIZE(trace_print_flags)*i) + + OFFSET(trace_print_flags_mask)); + name = VOID_PTR(buffer + (SIZE(trace_print_flags)*i) + + OFFSET(trace_print_flags_name)); + + if ((mask == -1UL) && !name) { /* Linux 3.5 and earlier */ + len--; + break; + } + + if (!read_string((ulong)name, namebuf, BUFSIZE-1)) { + error(INFO, "failed to read pageflag_names entry\n", + i, name, mask); + goto pageflags_fail; + } + + if (!(nameptr = (char *)malloc(strlen(namebuf)+1))) { + error(INFO, "cannot malloc pageflag_names space\n"); + goto pageflags_fail; + } + strcpy(nameptr, namebuf); + + vt->pageflags_data[i].name = nameptr; + vt->pageflags_data[i].mask = mask; + + if (CRASHDEBUG(1)) { + fprintf(fp, " %08lx %s\n", + vt->pageflags_data[i].mask, + vt->pageflags_data[i].name); + } + } + + FREEBUF(buffer); + vt->nr_pageflags = len; + vt->flags |= PAGEFLAGS; + return TRUE; + +pageflags_fail: + FREEBUF(buffer); + free(vt->pageflags_data); + vt->pageflags_data = NULL; + return FALSE; +} + +static int +page_flags_init_from_pageflags_enum(void) +{ + int c; + int p, len; + char *nameptr; + char buf[BUFSIZE]; + char *arglist[MAXARGS]; + + if (!(vt->pageflags_data = (struct pageflags_data *) + malloc(sizeof(struct pageflags_data) * 32))) { + error(INFO, "cannot malloc pageflags_data cache\n"); + return FALSE; + } + + p = 0; + pc->flags2 |= ALLOW_FP; + open_tmpfile(); + + if (dump_enumerator_list("pageflags")) { + rewind(pc->tmpfile); + while (fgets(buf, BUFSIZE, pc->tmpfile)) { + if (!strstr(buf, " = ")) + continue; + + c = parse_line(buf, arglist); + + if (strstr(arglist[0], "__NR_PAGEFLAGS")) { + len = atoi(arglist[2]); + if (!len || (len > 32)) + goto enum_fail; + vt->nr_pageflags = len; + break; + } + + if (!(nameptr = (char *)malloc(strlen(arglist[0])))) { + error(INFO, "cannot malloc pageflags name space\n"); + goto enum_fail; + } + strcpy(nameptr, arglist[0] + strlen("PG_")); + vt->pageflags_data[p].name = nameptr; + vt->pageflags_data[p].mask = 1 << atoi(arglist[2]); + + p++; + } + } else + goto enum_fail; + + close_tmpfile(); + pc->flags2 &= ~ALLOW_FP; + + if (CRASHDEBUG(1)) { + fprintf(fp, "pageflags from enum: \n"); + for (p = 0; p < vt->nr_pageflags; p++) + fprintf(fp, " %08lx %s\n", + vt->pageflags_data[p].mask, + vt->pageflags_data[p].name); + } + + vt->flags |= PAGEFLAGS; + return TRUE; + +enum_fail: + close_tmpfile(); + pc->flags2 &= ~ALLOW_FP; + + for (c = 0; c < p; c++) + free(vt->pageflags_data[c].name); + free(vt->pageflags_data); + vt->pageflags_data = NULL; + vt->nr_pageflags = 0; + + return FALSE; +} + +static int +translate_page_flags(char *buffer, ulong flags) +{ + char buf[BUFSIZE]; + int i, others; + + sprintf(buf, "%lx", flags); + + if (flags) { + for (i = others = 0; i < vt->nr_pageflags; i++) { + if (flags & vt->pageflags_data[i].mask) + sprintf(&buf[strlen(buf)], "%s%s", + others++ ? "," : " ", + vt->pageflags_data[i].name); + } + } + strcat(buf, "\n"); + strcpy(buffer, buf); + + return(strlen(buf)); +} /* * dump_page_hash_table() displays the entries in each page_hash_table. @@ -7240,7 +7455,8 @@ dump_kmeminfo(void) page_cache_size = nr_file_pages - swapper_space_nrpages - buffer_pages; FREEBUF(swapper_space); - } + } else + page_cache_size = 0; pct = (page_cache_size * 100)/totalram_pages; @@ -8085,7 +8301,9 @@ vaddr_to_kmem_cache(ulong vaddr, char *b return NULL; } - if (vt->flags & KMALLOC_SLUB) { + if ((vt->flags & KMALLOC_SLUB) || + ((vt->flags & KMALLOC_COMMON) && + VALID_MEMBER(page_slab) && VALID_MEMBER(page_first_page))) { readmem(compound_head(page)+OFFSET(page_slab), KVADDR, &cache, sizeof(void *), "page.slab", FAULT_ON_ERROR); @@ -8136,6 +8354,10 @@ vaddr_to_slab(ulong vaddr) if (vt->flags & KMALLOC_SLUB) slab = compound_head(page); + else if ((vt->flags & KMALLOC_COMMON) && VALID_MEMBER(page_slab_page)) + readmem(page+OFFSET(page_slab_page), + KVADDR, &slab, sizeof(void *), + "page.slab_page", FAULT_ON_ERROR); else if (VALID_MEMBER(page_prev)) readmem(page+OFFSET(page_prev), KVADDR, &slab, sizeof(void *), @@ -8212,7 +8434,7 @@ kmem_cache_init(void) OFFSET(kmem_cache_s_num) : OFFSET(kmem_cache_s_c_num); next_offset = vt->flags & (PERCPU_KMALLOC_V1|PERCPU_KMALLOC_V2) ? OFFSET(kmem_cache_s_next) : OFFSET(kmem_cache_s_c_nextp); - max_cnum = max_limit = max_cpus = cache_count = 0; + max_cnum = max_limit = max_cpus = cache_count = tmp2 = 0; /* * Pre-2.6 versions used the "cache_cache" as the head of the @@ -8431,6 +8653,43 @@ kmem_cache_downsize(void) FREEBUF(cache_buf); } +/* + * Stash a list of presumably-corrupted slab cache addresses. + */ +static void +mark_bad_slab_cache(ulong cache) +{ + size_t sz; + + if (vt->nr_bad_slab_caches) { + sz = sizeof(ulong) * (vt->nr_bad_slab_caches + 1); + if (!(vt->bad_slab_caches = realloc(vt->bad_slab_caches, sz))) { + error(INFO, "cannot realloc bad_slab_caches array\n"); + vt->nr_bad_slab_caches = 0; + return; + } + } else { + if (!(vt->bad_slab_caches = (ulong *)malloc(sizeof(ulong)))) { + error(INFO, "cannot malloc bad_slab_caches array\n"); + return; + } + } + + vt->bad_slab_caches[vt->nr_bad_slab_caches++] = cache; +} + +static int +bad_slab_cache(ulong cache) +{ + int i; + + for (i = 0; i < vt->nr_bad_slab_caches; i++) { + if (vt->bad_slab_caches[i] == cache) + return TRUE; + } + + return FALSE; +} /* * Determine the largest cpudata limit for a given cache. @@ -8526,8 +8785,13 @@ kmem_cache_s_array_nodes: for (i = max_limit = 0; (i < kt->cpus) && cpudata[i]; i++) { if (!readmem(cpudata[i]+OFFSET(array_cache_limit), KVADDR, &limit, sizeof(int), - "array cache limit", RETURN_ON_ERROR)) - goto bail_out; + "array cache limit", RETURN_ON_ERROR)) { + error(INFO, + "kmem_cache: %lx: invalid array_cache pointer: %lx\n", + cache, cpudata[i]); + mark_bad_slab_cache(cache); + return max_limit; + } if (CRASHDEBUG(3)) fprintf(fp, " array limit[%d]: %d\n", i, limit); if (limit > max_limit) @@ -9232,6 +9496,11 @@ dump_kmem_cache_percpu_v2(struct meminfo goto next_cache; } + if (bad_slab_cache(si->cache)) { + fprintf(fp, "%lx %-18s [INVALID/CORRUPTED]\n", si->cache, buf); + goto next_cache; + } + si->curname = buf; readmem(si->cache+OFFSET(kmem_cache_s_objsize), @@ -9264,7 +9533,7 @@ dump_kmem_cache_percpu_v2(struct meminfo "kmem_cache_s num", FAULT_ON_ERROR); si->c_num = (ulong)tmp_val; - if( vt->flags & PERCPU_KMALLOC_V2_NODES ) + if (vt->flags & PERCPU_KMALLOC_V2_NODES) do_slab_chain_percpu_v2_nodes(SLAB_GET_COUNTS, si); else do_slab_chain_percpu_v2(SLAB_GET_COUNTS, si); @@ -10160,22 +10429,22 @@ do_slab_chain_percpu_v2_nodes(long cmd, if (!slab_chains[s]) continue; - if (!specified_slab) { - if (!readmem(slab_chains[s], - KVADDR, &si->slab, sizeof(ulong), - "slabs", QUIET|RETURN_ON_ERROR)) { - error(INFO, - "%s: %s list: bad slab pointer: %lx\n", - si->curname, - slab_chain_name_v2[s], - slab_chains[s]); - list_borked = 1; - continue; + if (!specified_slab) { + if (!readmem(slab_chains[s], + KVADDR, &si->slab, sizeof(ulong), + "slabs", QUIET|RETURN_ON_ERROR)) { + error(INFO, "%s: %s list: " + "bad slab pointer: %lx\n", + si->curname, + slab_chain_name_v2[s], + slab_chains[s]); + list_borked = 1; + continue; } last = slab_chains[s]; } else last = 0; - + if (si->slab == slab_chains[s]) continue; @@ -11709,6 +11978,8 @@ dump_vm_table(int verbose) fprintf(fp, "%sVM_EVENT", others++ ? "|" : "");\ if (vt->flags & PGCNT_ADJ) fprintf(fp, "%sPGCNT_ADJ", others++ ? "|" : "");\ + if (vt->flags & PAGEFLAGS) + fprintf(fp, "%sPAGEFLAGS", others++ ? "|" : "");\ if (vt->flags & SWAPINFO_V1) fprintf(fp, "%sSWAPINFO_V1", others++ ? "|" : "");\ if (vt->flags & SWAPINFO_V2) @@ -11747,10 +12018,15 @@ dump_vm_table(int verbose) fprintf(fp, " kmem_cache_count: %ld\n", vt->kmem_cache_count); fprintf(fp, " kmem_cache_namelen: %d\n", vt->kmem_cache_namelen); fprintf(fp, "kmem_cache_len_nodes: %ld\n", vt->kmem_cache_len_nodes); - fprintf(fp, " PG_reserved: %lx\n", vt->PG_reserved); - fprintf(fp, " PG_slab: %ld (%lx)\n", vt->PG_slab, - (ulong)1 << vt->PG_slab); - fprintf(fp, " PG_head_tail_mask: %lx\n", vt->PG_head_tail_mask); + fprintf(fp, " nr_bad_slab_caches: %d\n", vt->nr_bad_slab_caches); + if (!vt->nr_bad_slab_caches) + fprintf(fp, " bad_slab_caches: (unused)\n"); + else { + for (i = 0; i < vt->nr_bad_slab_caches; i++) { + fprintf(fp, " bad_slab_caches[%d]: %lx\n", + i, vt->bad_slab_caches[i]); + } + } fprintf(fp, " paddr_prlen: %d\n", vt->paddr_prlen); fprintf(fp, " numnodes: %d\n", vt->numnodes); fprintf(fp, " nr_zones: %d\n", vt->nr_zones); @@ -11824,6 +12100,21 @@ dump_vm_table(int verbose) for (i = 0; i < vt->nr_vm_event_items; i++) fprintf(fp, " [%d] %s\n", i, vt->vm_event_items[i]); + fprintf(fp, " PG_reserved: %lx\n", vt->PG_reserved); + fprintf(fp, " PG_slab: %ld (%lx)\n", vt->PG_slab, + (ulong)1 << vt->PG_slab); + fprintf(fp, " PG_head_tail_mask: %lx\n", vt->PG_head_tail_mask); + + fprintf(fp, " nr_pageflags: %d\n", vt->nr_pageflags); + fprintf(fp, " pageflags_data: %s\n", + vt->nr_pageflags ? "" : "(not used)"); + for (i = 0; i < vt->nr_pageflags; i++) { + fprintf(fp, " %s[%d] %08lx: %s\n", + i < 10 ? " " : "", i, + vt->pageflags_data[i].mask, + vt->pageflags_data[i].name); + } + dump_vma_cache(VERBOSE); } --- crash-7.0.2/filesys.c 2013-10-25 15:28:55.920879569 -0400 +++ crash-7.0.3/filesys.c 2013-10-25 10:09:39.568757187 -0400 @@ -727,8 +727,10 @@ get_proc_version(void) return FALSE; if (fread(&kt->proc_version, sizeof(char), - BUFSIZE-1, version) <= 0) + BUFSIZE-1, version) <= 0) { + fclose(version); return FALSE; + } fclose(version); --- crash-7.0.2/help.c 2013-10-25 15:28:55.954879567 -0400 +++ crash-7.0.3/help.c 2013-10-24 15:32:58.495826737 -0400 @@ -1083,10 +1083,16 @@ NULL char *help_p[] = { "p", "print the value of an expression", -"[-x|-d][-u] expression", +"[-x|-d][-u] [expression | symbol[:cpuspec]]", " This command passes its arguments on to gdb \"print\" command for evaluation.", "", -" expression The expression to be evaluated.", +" expression an expression to be evaluated.", +" symbol a kernel symbol.", +" :cpuspec CPU specification for a per-cpu symbol:", +" : CPU of the currently selected task.", +" :a[ll] all CPUs.", +" :#[-#][,...] CPU list(s), e.g. \"1,3,5\", \"1-3\",", +" or \"1,3,5-7,10\".", " -x override default output format with hexadecimal format.", " -d override default output format with decimal format.", " -u the expression evaluates to a user address reference.", @@ -1144,6 +1150,39 @@ char *help_p[] = { " swap_address = 0x0, ", " segments = 0x0", " }", +"", +" If a per-cpu symbol is entered as a argument, its data type", +" and all of its per-cpu addresses are displayed:", +" ", +" %s> p irq_stat", +" PER-CPU DATA TYPE:", +" irq_cpustat_t irq_stat;", +" PER-CPU ADDRESSES:", +" [0]: ffff88021e211540", +" [1]: ffff88021e251540", +" [2]: ffff88021e291540", +" [3]: ffff88021e2d1540", +" ", +" To display the contents a per-cpu symbol for CPU 1, append", +" a cpu-specifier:", +" ", +" %s> p irq_stat:1", +" per_cpu(irq_stat, 1) = $29 = {", +" __softirq_pending = 0, ", +" __nmi_count = 209034, ", +" apic_timer_irqs = 597509876, ", +" irq_spurious_count = 0, ", +" icr_read_retry_count = 2, ", +" x86_platform_ipis = 0, ", +" apic_perf_irqs = 209034, ", +" apic_irq_work_irqs = 0, ", +" irq_resched_count = 264922233, ", +" irq_call_count = 7036692, ", +" irq_tlb_count = 4750442, ", +" irq_thermal_count = 0, ", +" irq_threshold_count = 0", +" }", +" ", NULL }; @@ -2841,7 +2880,7 @@ char *help_irq[] = { " irq stats of all cpus will be displayed.", " -c cpu only usable with the -s option, dump the irq stats of the ", " specified cpu[s]; cpu can be specified as \"1,3,5\", \"1-3\",", -" or \"1,3,5-7,10\".", +" \"1,3,5-7,10\", \"all\", or \"a\" (shortcut for \"all\").", "\nEXAMPLES", " Display the relevant data for IRQ 18 from a pre-2.6.37 kernel:\n", " %s> irq 18", @@ -4092,8 +4131,8 @@ NULL char *help_struct[] = { "struct", "structure contents", -"struct_name[.member[,member]][-o][-l offset][-rfuxdp][address | symbol]\n" -" [count | -c count]", +"struct_name[.member[,member]][-o][-l offset][-rfuxdp]\n" +" [address | symbol][:cpuspec] [count | -c count]", " This command displays either a structure definition, or a formatted display", " of the contents of a structure at a specified address. When no address is", " specified, the structure definition is shown along with the structure size.", @@ -4127,6 +4166,11 @@ char *help_struct[] = { " to an embedded list_head structure contained within the", " target data structure, then the \"-l\" option must be used.", " symbol symbolic reference to the address of a structure.", +" :cpuspec CPU specification for a per-cpu address or symbol:", +" : CPU of the currently selected task.", +" :a[ll] all CPUs.", +" :#[-#][,...] CPU list(s), e.g. \"1,3,5\", \"1-3\",", +" or \"1,3,5-7,10\".", " count count of structures to dump from an array of structures;", " if used, this must be the last argument entered.", " -c count \"-c\" is only required if \"count\" is not the last argument", @@ -4363,6 +4407,59 @@ char *help_struct[] = { " [ffff8100145d20b8] struct list_head run_list;", " [ffff8100145d20c8] struct prio_array *array;", " ...", +" ", +" For an example of displaying per-cpu variables, consider the", +" struct hd_struct.dkstats member, which is a percpu pointer to", +" a disk_stats structure:", +"", +" %s> struct hd_struct.dkstats ", +" struct hd_struct {", +" [1232] struct disk_stats *dkstats;", +" }", +"", +" Taking an hd_struct at address ffff8802450e2848, display all", +" of the per-cpu disk_stats structures that it references:", +" ", +" %s> struct hd_struct.dkstats ffff8802450e2848", +" dkstats = 0x60fdb48026c8", +" %s> struct disk_stats 0x60fdb48026c8:a", +" [0]: ffffe8fefe6026c8", +" struct disk_stats {", +" sectors = {451376, 80468}, ", +" ios = {6041, 971}, ", +" merges = {386, 390}, ", +" ticks = {194877, 56131}, ", +" io_ticks = 12371, ", +" time_in_queue = 309163", +" }", +" [1]: ffffe8fefe8026c8", +" struct disk_stats {", +" sectors = {0, 0}, ", +" ios = {0, 0}, ", +" merges = {7, 242}, ", +" ticks = {0, 0}, ", +" io_ticks = 23, ", +" time_in_queue = 581", +" }", +" [2]: ffffe8fefea026c8", +" struct disk_stats {", +" sectors = {0, 0}, ", +" ios = {0, 0}, ", +" merges = {4, 112}, ", +" ticks = {0, 0}, ", +" io_ticks = 11, ", +" time_in_queue = 305", +" }", +" [3]: ffffe8fefec026c8", +" struct disk_stats {", +" sectors = {0, 0}, ", +" ios = {0, 0}, ", +" merges = {5, 54}, ", +" ticks = {0, 0}, ", +" io_ticks = 17, ", +" time_in_queue = 41", +" }", +" ", "\nNOTE", " If the structure name does not conflict with any %s command name, the", " \"struct\" command may be dropped. Accordingly, the examples above could", @@ -4382,8 +4479,8 @@ NULL char *help_union[] = { "union", "union contents", -"union_name[.member[,member]] [-o][-l offset][-rfuxdp] [address | symbol]\n" -" [count | -c count]", +"union_name[.member[,member]] [-o][-l offset][-rfuxdp]\n" +" [address | symbol][:cpuspec] [count | -c count]", " This command displays either a union definition, or a formatted display", " of the contents of a union at a specified address. When no address is", " specified, the union definition is shown along with the union size.", @@ -4418,6 +4515,11 @@ char *help_union[] = { " to an embedded list_head structure contained within the", " target union structure, then the \"-l\" option must be used.", " symbol symbolic reference to the address of a union.", +" :cpuspec CPU specification for a per-cpu address or symbol:", +" : CPU of the currently selected task.", +" :a[ll] all CPUs.", +" :#[-#][,...] CPU list(s), e.g. \"1,3,5\", \"1-3\",", +" or \"1,3,5-7,10\".", " count count of unions to dump from an array of unions; if used,", " this must be the last argument entered.", " -c count \"-c\" is only required if \"count\" is not the last argument", @@ -5433,30 +5535,26 @@ char *help_kmem[] = { " ", " Dump the mem_map[] array:\n", " %s> kmem -p", -" PAGE PHYSICAL MAPPING INDEX CNT FLAGS", -" ffffea0000000000 0 0 0 0 0", -" ffffea0000000038 1000 0 0 1 400", -" ffffea0000000070 2000 0 0 1 400", -" ffffea00000000a8 3000 0 0 1 400", -" ffffea00000000e0 4000 0 0 1 400", -" ffffea0000000118 5000 0 0 1 400", -" ffffea0000000150 6000 0 0 1 400", -" ffffea0000000188 7000 0 0 1 80", -" ffffea00000001c0 8000 0 0 1 400", -" ffffea00000001f8 9000 0 0 1 80", -" ffffea0000000230 a000 0 0 1 80", -" ffffea0000000268 b000 ffff880012d9bd68 695b 1 2002c", -" ffffea00000002a0 c000 0 0 1 80", -" ffffea00000002d8 d000 ffff88002a9ee210 9 1 2002c", -" ffffea0000000310 e000 ffff880010b265d8 33c 1 2002c", -" ffffea0000000348 f000 ffff88001404dd68 2d1 2 868", -" ffffea0000000380 10000 ffff88001404dd68 2d6 2 868", -" ffffea00000003b8 11000 ffff88001404dd68 2d7 2 868", -" ffffea00000003f0 12000 ffff88001404dd68 2d8 2 868", -" ffffea0000000428 13000 ffff88001404dd68 2d9 2 868", -" ffffea0000000460 14000 ffff88001404dd68 2da 2 868", -" ffffea0000000498 15000 ffff88001404dd68 2db 2 868", -" ffffea00000004d0 16000 ffff88001404dd68 2dc 2 868", +" PAGE PHYSICAL MAPPING INDEX CNT FLAGS", +" f5c51200 10000 0 0 1 80 slab", +" f5c51220 11000 0 0 1 80 slab", +" f5c51240 12000 0 0 1 80 slab", +" f5c51260 13000 0 0 1 80 slab", +" f5c51280 14000 0 0 1 80 slab", +" f5c512a0 15000 0 0 1 80 slab", +" f5c512c0 16000 0 0 1 80 slab", +" f5c512e0 17000 0 0 1 80 slab", +" f5c51300 18000 0 0 1 80 slab", +" f5c51320 19000 0 0 1 80 slab", +" f5c51340 1a000 0 0 1 80 slab", +" f5c51360 1b000 0 0 1 80 slab", +" f5c51380 1c000 e6c6a754 13b67 2 868 uptodate,lru,active,private", +" f5c513a0 1d000 0 0 1 80 slab", +" f5c513c0 1e000 0 0 1 80 slab", +" f5c513e0 1f000 0 0 1 80 slab", +" f5c51400 20000 e6c6a754 13bbb 2 868 uptodate,lru,active,private", +" f5c51420 21000 0 0 1 80 slab", +" f5c51440 22000 0 0 1 80 slab", " ...", " ", " Use the commands above with a page pointer or a physical address argument:\n", @@ -5469,12 +5567,14 @@ char *help_kmem[] = { " 2 16k c02eb01c ", " c40425b0 (c40425b0 is 1st of 4 pages) ", " ", -" %s> kmem -p c035de00", -" PAGE PHYSICAL INODE OFFSET CNT FLAGS", -" c035de00 50c0000 0 129000 0 uptodate\n", -" %s> kmem -p 50c0000", -" PAGE PHYSICAL INODE OFFSET CNT FLAGS", -" c035de00 50c0000 0 129000 0 uptodate\n", +" %s> kmem -p c25a9c00", +" PAGE PHYSICAL MAPPING INDEX CNT FLAGS", +" c25a9c00 1fe0000 f429d2e4 21fe3eb 2 800828 uptodate,lru,private", +" ", +" %s> kmem -p 1fe0000", +" PAGE PHYSICAL MAPPING INDEX CNT FLAGS", +" c25a9c00 1fe0000 f429d2e4 21fe3eb 2 800828 uptodate,lru,private", +" ", " Display the mapped memory regions allocated by vmalloc():\n", " %s> kmem -v", " VMAP_AREA VM_STRUCT ADDRESS RANGE SIZE", --- crash-7.0.2/task.c 2013-10-25 15:28:55.949879567 -0400 +++ crash-7.0.3/task.c 2013-10-17 14:42:50.532672513 -0400 @@ -474,7 +474,7 @@ task_init(void) tt->this_task = pid_to_task(active_pid); } else { - if (KDUMP_DUMPFILE()) + if (KDUMP_DUMPFILE() && !(pc->flags2 & QEMU_MEM_DUMP)) map_cpus_to_prstatus(); else if (ELF_NOTES_VALID() && DISKDUMP_DUMPFILE()) map_cpus_to_prstatus_kdump_cmprs(); @@ -4873,10 +4873,10 @@ task_mm(ulong task, int fill) char * task_cpu(int processor, char *buf, int verbose) { - if (processor < NO_PROC_ID) + if (processor < NR_CPUS) sprintf(buf, "%d", processor); - if (processor == NO_PROC_ID) - sprintf(buf, verbose ? "NO_PROC_ID" : "-"); + else + sprintf(buf, verbose ? "(unknown)" : "?"); return buf; } --- crash-7.0.2/kernel.c 2013-10-25 15:28:55.958879567 -0400 +++ crash-7.0.3/kernel.c 2013-10-25 10:13:04.143747815 -0400 @@ -5377,7 +5377,6 @@ cmd_irq(void) int i, c; int nr_irqs; ulong *cpus; - int len; int show_intr, choose_cpu; char buf[10]; char arg_buf[BUFSIZE]; @@ -5485,9 +5484,7 @@ cmd_irq(void) error(FATAL, "cannot determine number of IRQs\n"); if (show_intr) { - if ((len = STRUCT_SIZE("cpumask_t")) < 0) - len = DIV_ROUND_UP(kt->cpus, BITS_PER_LONG) * sizeof(ulong); - cpus = (ulong *)GETBUF(len); + cpus = get_cpumask_buf(); if (choose_cpu) { make_cpumask(arg_buf, cpus, FAULT_ON_ERROR, NULL); @@ -5648,6 +5645,7 @@ generic_dump_irq(int irq) ulong tmp1, tmp2; handler = UNINITIALIZED; + action = 0; irq_desc_addr = get_irq_desc_addr(irq); if (!irq_desc_addr && symbol_exists("irq_desc_ptrs")) { @@ -8398,6 +8396,7 @@ static void add_ikconfig_entry(char *lin static int setup_ikconfig(char *config) { char *ent, *tokptr; + struct ikconfig_list *new; ikconfig_all = calloc(1, sizeof(struct ikconfig_list) * IKCONFIG_MAX); if (!ikconfig_all) { @@ -8424,8 +8423,9 @@ static int setup_ikconfig(char *config) free(ikconfig_all); return 0; } - ikconfig_all = realloc(ikconfig_all, - sizeof(struct ikconfig_list) * kt->ikconfig_ents); + if ((new = realloc(ikconfig_all, + sizeof(struct ikconfig_list) * kt->ikconfig_ents))) + ikconfig_all = new; return 1; } --- crash-7.0.2/gdb_interface.c 2013-10-25 15:28:55.936879568 -0400 +++ crash-7.0.3/gdb_interface.c 2013-10-15 15:55:20.440389792 -0400 @@ -357,8 +357,8 @@ gdb_interface(struct gnu_request *req) restart(0); if (!req->fp) { - req->fp = pc->flags & RUNTIME ? fp : - CRASHDEBUG(1) ? fp : pc->nullfp; + req->fp = ((pc->flags & RUNTIME) || (pc->flags2 & ALLOW_FP)) ? + fp : CRASHDEBUG(1) ? fp : pc->nullfp; } pc->cur_req = req; --- crash-7.0.2/configure.c 2013-10-25 15:28:55.956879567 -0400 +++ crash-7.0.3/configure.c 2013-10-25 10:09:47.567756821 -0400 @@ -790,7 +790,7 @@ make_rh_rpm_package(char *package, int r break; } } - fclose(fp); + pclose(fp); if (!cur) { fprintf(stderr, "cannot get version from \"crash -v\"\n"); --- crash-7.0.2/net.c 2013-10-25 15:28:55.923879569 -0400 +++ crash-7.0.3/net.c 2013-10-22 09:42:52.103705675 -0400 @@ -1,8 +1,8 @@ /* net.c - core analysis suite * * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. - * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 David Anderson - * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Red Hat, Inc. All rights reserved. + * Copyright (C) 2002-2013 David Anderson + * Copyright (C) 2002-2013 Red Hat, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -231,10 +231,22 @@ net_init(void) } else if ((MEMBER_OFFSET("inet_sock", "sk") == 0) && (MEMBER_OFFSET("sock", "__sk_common") == 0)) { MEMBER_OFFSET_INIT(inet_opt_daddr, "sock_common", "skc_daddr"); + if (INVALID_MEMBER(inet_opt_daddr)) + ANON_MEMBER_OFFSET_INIT(inet_opt_daddr, "sock_common", + "skc_daddr"); MEMBER_OFFSET_INIT(inet_opt_rcv_saddr, "sock_common", "skc_rcv_saddr"); + if (INVALID_MEMBER(inet_opt_rcv_saddr)) + ANON_MEMBER_OFFSET_INIT(inet_opt_rcv_saddr, "sock_common", + "skc_rcv_saddr"); MEMBER_OFFSET_INIT(inet_opt_dport, "inet_sock", "inet_dport"); + if (INVALID_MEMBER(inet_opt_dport)) + ANON_MEMBER_OFFSET_INIT(inet_opt_dport, "sock_common", + "skc_dport"); MEMBER_OFFSET_INIT(inet_opt_sport, "inet_sock", "inet_sport"); MEMBER_OFFSET_INIT(inet_opt_num, "inet_sock", "inet_num"); + if (INVALID_MEMBER(inet_opt_num)) + ANON_MEMBER_OFFSET_INIT(inet_opt_num, "sock_common", + "skc_num"); } } --- crash-7.0.2/s390x.c 2013-10-25 15:28:55.928879568 -0400 +++ crash-7.0.3/s390x.c 2013-10-10 14:57:54.080339089 -0400 @@ -590,9 +590,12 @@ static int swap_entry(ulong entry) if (THIS_KERNEL_VERSION < LINUX(2,6,19)) { if ((entry & 0x601ULL) == 0x600ULL) return 1; - } else { + } if (THIS_KERNEL_VERSION < LINUX(3,12,0)) { if ((entry & 0x403ULL) == 0x403ULL) return 1; + } else { + if ((entry & 0x603ULL) == 0x402ULL) + return 1; } return 0; } --- crash-7.0.2/x86_64.c 2013-10-25 15:28:55.928879568 -0400 +++ crash-7.0.3/x86_64.c 2013-10-17 09:25:14.265545546 -0400 @@ -7251,6 +7251,9 @@ x86_64_get_framesize(struct bt_info *bt, } } + if ((sp->value >= kt->init_begin) && (sp->value < kt->init_end)) + return 0; + framesize = max = 0; max_instructions = textaddr - sp->value; instr = arg = -1; --- crash-7.0.2/remote.c 2013-10-25 15:28:55.935879568 -0400 +++ crash-7.0.3/remote.c 2013-10-25 10:09:51.680756632 -0400 @@ -1116,8 +1116,10 @@ daemon_proc_version(char *buf) return FALSE; if (fread(buf, sizeof(char), - BUFSIZE-1, pipe) <= 0) + BUFSIZE-1, pipe) <= 0) { + pclose(pipe); return FALSE; + } pclose(pipe); --- crash-7.0.2/va_server.c 2013-10-25 15:28:55.939879568 -0400 +++ crash-7.0.3/va_server.c 2013-10-25 10:09:58.528756319 -0400 @@ -313,6 +313,7 @@ int read_map(char *crash_file) ret = fseek(vas_file_p, (long)0, SEEK_SET); if(ret == -1) { printf("va_server: unable to fseek, err = %d\n", ferror(vas_file_p)); + free(disk_hdr); return -1; } items = fread((void *)disk_hdr, 1, Page_Size, vas_file_p); --- crash-7.0.2/va_server_v1.c 2013-10-25 15:28:55.935879568 -0400 +++ crash-7.0.3/va_server_v1.c 2013-10-25 10:10:02.032756158 -0400 @@ -308,6 +308,7 @@ int read_map_v1(int blk_pos) ret = fseek(vas_file_p, (long)(blk_pos*Page_Size), SEEK_SET); if(ret == -1) { console("va_server: unable to fseek, err = %d\n", ferror(vas_file_p)); + free(disk_hdr); return -1; } items = fread((void *)disk_hdr, 1, Page_Size, vas_file_p); --- crash-7.0.2/symbols.c 2013-10-25 15:28:55.956879567 -0400 +++ crash-7.0.3/symbols.c 2013-10-24 15:33:12.359826102 -0400 @@ -72,7 +72,10 @@ struct elf_common; static void Elf32_Sym_to_common(Elf32_Sym *, struct elf_common *); static void Elf64_Sym_to_common(Elf64_Sym *, struct elf_common *); static void cmd_datatype_common(ulong); -static int display_per_cpu_info(struct syment *); +static void do_datatype_addr(struct datatype_member *, ulong, int, + ulong, char **, int); +static void process_gdb_output(char *, unsigned, const char *, int); +static int display_per_cpu_info(struct syment *, int, char *); static struct load_module *get_module_percpu_sym_owner(struct syment *); static int is_percpu_symbol(struct syment *); static void dump_percpu_symbols(struct load_module *); @@ -116,6 +119,8 @@ static int show_member_offset(FILE *, st #define IN_STRUCT (0x40000) #define DATATYPE_QUERY (0x80000) #define ANON_MEMBER_QUERY (0x100000) +#define SHOW_RAW_DATA (0x200000) +#define DEREF_POINTERS (0x400000) #define INTEGER_TYPE (UINT8|INT8|UINT16|INT16|UINT32|INT32|UINT64|INT64) @@ -132,6 +137,7 @@ static void dump_datatype_flags(ulong, F static long anon_member_offset(char *, char *); static int gdb_whatis(char *); static void do_datatype_declaration(struct datatype_member *, ulong); +static int member_to_datatype(char *, struct datatype_member *, ulong); #define DEBUGINFO_ERROR_MESSAGE1 \ "the use of a System.map file requires that the accompanying namelist\nargument is a kernel file built with the -g CFLAG. The namelist argument\nsupplied in this case is a debuginfo file, which must be accompanied by the\nkernel file from which it was derived.\n" @@ -5704,13 +5710,13 @@ dereference_pointer(ulong addr, struct d static void cmd_datatype_common(ulong flags) { - int i, c; + int c; ulong addr, aflag; + char *cpuspec; + ulong *cpus; struct syment *sp; - int rawdata; - long len; ulong list_head_offset; - int count, pflag; + int count; int argc_members; int optind_save; unsigned int radix, restore_radix; @@ -5721,19 +5727,19 @@ cmd_datatype_common(ulong flags) dm = &datatype_member; count = 0xdeadbeef; - rawdata = 0; aflag = addr = 0; list_head_offset = 0; argc_members = 0; radix = restore_radix = 0; separator = members = NULL; - pflag = 0; + cpuspec = NULL; + cpus = NULL; while ((c = getopt(argcnt, args, "pxdhfuc:rvol:")) != EOF) { switch (c) { case 'p': - pflag++; + flags |= DEREF_POINTERS; break; case 'd': @@ -5756,7 +5762,7 @@ cmd_datatype_common(ulong flags) break; case 'r': - rawdata = 1; + flags |= SHOW_RAW_DATA; break; case 'v': @@ -5816,11 +5822,22 @@ cmd_datatype_common(ulong flags) if (aflag && (count != 0xdeadbeef)) error(FATAL, "too many arguments!\n"); + if (!aflag) { + cpuspec = strchr(args[optind], ':'); + if (cpuspec) + *cpuspec++ = NULLCHAR; + } + if (clean_arg() && IS_A_NUMBER(args[optind])) { if (aflag) count = stol(args[optind], FAULT_ON_ERROR, NULL); - else { + else if (cpuspec) { + if (pc->curcmd_flags & MEMTYPE_FILEADDR) + error(FATAL, "-f option cannot be used with percpu\n"); + addr = htol(args[optind], FAULT_ON_ERROR, NULL); + aflag++; + } else { if (pc->curcmd_flags & MEMTYPE_FILEADDR) pc->curcmd_private = stoll(args[optind], FAULT_ON_ERROR, NULL); @@ -5835,6 +5852,12 @@ cmd_datatype_common(ulong flags) aflag++; } } else if ((sp = symbol_search(args[optind]))) { + if (cpuspec && !is_percpu_symbol(sp)) { + error(WARNING, + "%s is not percpu; cpuspec ignored.\n", + sp->name); + cpuspec = NULL; + } addr = sp->value; aflag++; } else { @@ -5846,6 +5869,14 @@ cmd_datatype_common(ulong flags) } } + if (cpuspec) { + cpus = get_cpumask_buf(); + if (STREQ(cpuspec, "")) + SET_BIT(cpus, CURRENT_CONTEXT()->processor); + else + make_cpumask(cpuspec, cpus, FAULT_ON_ERROR, NULL); + } + optind = optind_save; if (count == 0xdeadbeef) @@ -5853,7 +5884,7 @@ cmd_datatype_common(ulong flags) else if (!aflag) error(FATAL, "no kernel virtual address argument entered\n"); - if (pflag && !aflag) + if ((flags & DEREF_POINTERS) && !aflag) error(FATAL, "-p option requires address argument\n"); if (list_head_offset) @@ -5878,6 +5909,15 @@ cmd_datatype_common(ulong flags) DATATYPE_QUERY|ANON_MEMBER_QUERY|RETURN_ON_ERROR) < 1)) error(FATAL, "invalid data structure reference: %s\n", structname); + if (! (flags & (STRUCT_REQUEST|UNION_REQUEST)) ) { + flags |= dm->type; + if (!(flags & (UNION_REQUEST|STRUCT_REQUEST))) + error(FATAL, "invalid argument"); + } else if ( (flags &(STRUCT_REQUEST|UNION_REQUEST)) != dm->type) { + error(FATAL, "data type mismatch: %s is not a %s\n", + dm->name, flags & UNION_REQUEST ? "union" : "struct"); + } + if ((argc_members > 1) && !aflag) { error(INFO, flags & SHOW_OFFSET ? "-o option not valid with multiple member format\n" : @@ -5891,7 +5931,52 @@ cmd_datatype_common(ulong flags) error(FATAL, "-o option not valid with multiple member format\n"); - len = dm->size; + set_temporary_radix(radix, &restore_radix); + + /* + * No address was passed -- dump the structure/member declaration. + */ + if (!aflag) { + if (argc_members && + !member_to_datatype(memberlist[0], dm, + ANON_MEMBER_QUERY)) + error(FATAL, "invalid data structure reference: %s.%s\n", + dm->name, memberlist[0]); + do_datatype_declaration(dm, flags | (dm->flags & TYPEDEF)); + } else if (cpus) { + for (c = 0; c < kt->cpus; c++) { + ulong cpuaddr; + + if (!NUM_IN_BITMAP(cpus, c)) + continue; + + cpuaddr = addr + kt->__per_cpu_offset[c]; + fprintf(fp, "[%d]: %lx\n", c, cpuaddr); + do_datatype_addr(dm, cpuaddr , count, + flags, memberlist, argc_members); + } + } else + do_datatype_addr(dm, addr, count, flags, + memberlist, argc_members); + + restore_current_radix(restore_radix); + +freebuf: + if (argc_members) { + FREEBUF(structname); + FREEBUF(members); + } + + if (cpus) + FREEBUF(cpus); +} + +static void +do_datatype_addr(struct datatype_member *dm, ulong addr, int count, + ulong flags, char **memberlist, int argc_members) +{ + int i, c; + long len = dm->size; if (count < 0) { addr -= len * abs(count); @@ -5908,83 +5993,44 @@ cmd_datatype_common(ulong flags) i = 0; do { if (argc_members) { - *separator = '.'; - strcpy(separator+1, memberlist[i]); - } - - switch (arg_to_datatype(structname, dm, - ANON_MEMBER_QUERY|RETURN_ON_ERROR)) - { - case 0: error(FATAL, "invalid data structure reference: %s\n", - structname); - break; - case 1: break; - case 2: if (rawdata) + if (!member_to_datatype(memberlist[i], dm, + ANON_MEMBER_QUERY)) + error(FATAL, "invalid data structure reference: %s.%s\n", + dm->name, memberlist[i]); + if (flags & SHOW_RAW_DATA) error(FATAL, - "member-specific output not allowed with -r\n"); - break; + "member-specific output not allowed with -r\n"); } - if (!(dm->flags & TYPEDEF)) { - if (flags &(STRUCT_REQUEST|UNION_REQUEST) ) { - if ((flags & (STRUCT_REQUEST|UNION_REQUEST)) != dm->type) - goto freebuf; - } else - flags |= dm->type; - } - - /* - * No address was passed -- dump the structure/member declaration. - */ - if (!aflag || (aflag && (flags & SHOW_OFFSET))) { - if (aflag) - dm->vaddr = addr; - set_temporary_radix(radix, &restore_radix); - do_datatype_declaration(dm, flags | (dm->flags & TYPEDEF)); - restore_current_radix(restore_radix); - goto freebuf; - } - - if (!(flags & (UNION_REQUEST|STRUCT_REQUEST))) - error(FATAL, "invalid argument"); - /* - * Display data. + * Display member addresses or data */ - if (rawdata) + if (flags & SHOW_OFFSET) { + dm->vaddr = addr; + do_datatype_declaration(dm, flags | (dm->flags & TYPEDEF)); + } else if (flags & SHOW_RAW_DATA) raw_data_dump(addr, len, flags & STRUCT_VERBOSE); - else if (pflag && !dm->member) { - set_temporary_radix(radix, &restore_radix); + else if ((flags & DEREF_POINTERS) && !dm->member) { print_struct_with_dereference(addr, dm, flags); - restore_current_radix(restore_radix); } else { if (dm->member) open_tmpfile(); - set_temporary_radix(radix, &restore_radix); - if (flags & UNION_REQUEST) print_union(dm->name, addr); else if (flags & STRUCT_REQUEST) print_struct(dm->name, addr); if (dm->member) { - if (!(pflag && + if (!((flags & DEREF_POINTERS) && dereference_pointer(addr, dm, flags))) parse_for_member(dm, PARSE_FOR_DATA); close_tmpfile(); } - restore_current_radix(restore_radix); } } while (++i < argc_members); } - -freebuf: - if (argc_members) { - FREEBUF(structname); - FREEBUF(members); - } } @@ -6108,13 +6154,7 @@ arg_to_datatype(char *s, struct datatype if (!both) return 1; - dm->member = p1+1; - - if ((dm->member_offset = MEMBER_OFFSET(dm->name, dm->member)) >= 0) - return 2; - - if ((flags & ANON_MEMBER_QUERY) && - ((dm->member_offset = ANON_MEMBER_OFFSET(dm->name, dm->member)) >= 0)) + if (member_to_datatype(p1 + 1, dm, flags)) return 2; datatype_member_fatal: @@ -6137,6 +6177,21 @@ datatype_member_fatal: return (error(FATAL, "invalid argument: %s\n", s)); } +static int +member_to_datatype(char *s, struct datatype_member *dm, ulong flags) +{ + dm->member = s; + + if ((dm->member_offset = MEMBER_OFFSET(dm->name, s)) >= 0) + return TRUE; + + if ((flags & ANON_MEMBER_QUERY) && + ((dm->member_offset = ANON_MEMBER_OFFSET(dm->name, s)) >= 0)) + return TRUE; + + return FALSE; +} + /* * debug routine -- not called on purpose by anybody. */ @@ -6388,13 +6443,12 @@ cmd_p(void) { int c; struct syment *sp, *percpu_sp; - unsigned radix, restore_radix; - int leader, do_load_module_filter, success; + unsigned radix; + int do_load_module_filter; char buf1[BUFSIZE]; - char buf2[BUFSIZE]; - char *p1; + char *cpuspec; - leader = do_load_module_filter = radix = restore_radix = 0; + do_load_module_filter = radix = 0; while ((c = getopt(argcnt, args, "dhxu")) != EOF) { switch(c) @@ -6427,33 +6481,57 @@ cmd_p(void) if (argerrs || !args[optind]) cmd_usage(pc->curcmd, SYNOPSIS); + cpuspec = strrchr(args[optind], ':'); + if (cpuspec) + *cpuspec++ = NULLCHAR; + + sp = NULL; if ((sp = symbol_search(args[optind])) && !args[optind+1]) { if ((percpu_sp = per_cpu_symbol_search(args[optind])) && - display_per_cpu_info(percpu_sp)) + display_per_cpu_info(percpu_sp, radix, cpuspec)) return; - sprintf(buf2, "%s = ", args[optind]); - leader = strlen(buf2); if (module_symbol(sp->value, NULL, NULL, NULL, *gdb_output_radix)) do_load_module_filter = TRUE; } else if ((percpu_sp = per_cpu_symbol_search(args[optind])) && - display_per_cpu_info(percpu_sp)) + display_per_cpu_info(percpu_sp, radix, cpuspec)) return; else if (st->flags & LOAD_MODULE_SYMS) do_load_module_filter = TRUE; + if (cpuspec) { + if (sp) + error(WARNING, "%s is not percpu; cpuspec ignored.\n", + sp->name); + else + /* maybe a valid C expression (e.g. ':') */ + *(cpuspec-1) = ':'; + } + + process_gdb_output(concat_args(buf1, 0, TRUE), radix, + sp ? sp->name : NULL, do_load_module_filter); +} + +static void +process_gdb_output(char *gdb_request, unsigned radix, + const char *leader, int do_load_module_filter) +{ + unsigned restore_radix; + int success; + char buf1[BUFSIZE]; + char *p1; + if (leader || do_load_module_filter) open_tmpfile(); set_temporary_radix(radix, &restore_radix); - success = gdb_pass_through(concat_args(buf1, 0, TRUE), NULL, - GNU_RETURN_ON_ERROR); + success = gdb_pass_through(gdb_request, NULL, GNU_RETURN_ON_ERROR); if (success && (leader || do_load_module_filter)) { int firstline; if (leader) { - fprintf(pc->saved_fp, "%s", buf2); + fprintf(pc->saved_fp, "%s = ", leader); fflush(pc->saved_fp); } @@ -6482,8 +6560,41 @@ cmd_p(void) restore_current_radix(restore_radix); if (!success) - error(FATAL, "gdb request failed: %s\n", - concat_args(buf1, 0, TRUE)); + error(FATAL, "gdb request failed: %s\n", gdb_request); +} + +/* + * Get the type of an expression using gdb's "whatis" command. + * The returned string is dynamically allocated, and it should + * be passed to FREEBUF() when no longer needed. + * Return NULL if the type cannot be determined. + */ +static char * +expr_type_name(const char *expr) +{ + char buf[BUFSIZE], *p; + + open_tmpfile(); + sprintf(buf, "whatis %s", expr); + if (!gdb_pass_through(buf, fp, GNU_RETURN_ON_ERROR)) { + close_tmpfile(); + return NULL; + } + + rewind(pc->tmpfile); + while (fgets(buf, BUFSIZE, pc->tmpfile) && !STRNEQ(buf, "type = ")) + ; + p = feof(pc->tmpfile) ? NULL : buf + strlen("type = "); + close_tmpfile(); + + if (p) { + size_t len = strlen(clean_line(p)); + /* GDB reports unknown types as <...descriptive text...> */ + if (p[0] == '<' && p[len-1] == '>') + return NULL; + return strcpy(GETBUF(len + 1), p); + } + return NULL; } /* @@ -6491,30 +6602,73 @@ cmd_p(void) * the addresses of each its per-cpu instances. */ static int -display_per_cpu_info(struct syment *sp) +display_per_cpu_info(struct syment *sp, int radix, char *cpuspec) { + ulong *cpus; int c; ulong addr; char buf[BUFSIZE]; + char leader[sizeof("&per_cpu(") + strlen(sp->name) + + sizeof(", " STR(UINT_MAX) ")")]; + char *typename; + int do_load_module_filter; if (((kt->flags & (SMP|PER_CPU_OFF)) != (SMP|PER_CPU_OFF)) || (!is_percpu_symbol(sp)) || !((sp->type == 'd') || (sp->type == 'D') || (sp->type == 'V'))) return FALSE; - fprintf(fp, "PER-CPU DATA TYPE:\n "); - sprintf(buf, "whatis %s", sp->name); - if (!gdb_pass_through(buf, pc->nullfp, GNU_RETURN_ON_ERROR)) - fprintf(fp, "[undetermined type] %s;\n", sp->name); - else - whatis_variable(sp); + if (cpuspec) { + cpus = get_cpumask_buf(); + if (STREQ(cpuspec, "")) + SET_BIT(cpus, CURRENT_CONTEXT()->processor); + else + make_cpumask(cpuspec, cpus, FAULT_ON_ERROR, NULL); + } else + cpus = NULL; + + typename = expr_type_name(sp->name); + + if (!cpus) { + fprintf(fp, "PER-CPU DATA TYPE:\n "); + if (!typename) + fprintf(fp, "[undetermined type] %s;\n", sp->name); + else + whatis_variable(sp); + + fprintf(fp, "PER-CPU ADDRESSES:\n"); + } + + do_load_module_filter = + module_symbol(sp->value, NULL, NULL, NULL, *gdb_output_radix); - fprintf(fp, "PER-CPU ADDRESSES:\n"); for (c = 0; c < kt->cpus; c++) { + if (cpus && !NUM_IN_BITMAP(cpus, c)) + continue; addr = sp->value + kt->__per_cpu_offset[c]; - fprintf(fp, " [%d]: %lx\n", c, addr); + if (!cpus) + fprintf(fp, " [%d]: %lx\n", c, addr); + else if (typename) { + snprintf(buf, sizeof buf, "p *(%s*) 0x%lx", + typename, addr); + sprintf(leader, "per_cpu(%s, %u)", + sp->name, c); + process_gdb_output(buf, radix, leader, + do_load_module_filter); + } else { + snprintf(buf, sizeof buf, "p (void*) 0x%lx", addr); + sprintf(leader, "&per_cpu(%s, %u)", + sp->name, c); + process_gdb_output(buf, radix, leader, + do_load_module_filter); + } } + if (typename) + FREEBUF(typename); + if (cpus) + FREEBUF(cpus); + return TRUE; } @@ -6859,6 +7013,10 @@ dump_datatype_flags(ulong flags, FILE *o fprintf(ofp, "%sDATATYPE_QUERY", others++ ? "|" : ""); if (flags & ANON_MEMBER_QUERY) fprintf(ofp, "%sANON_MEMBER_QUERY", others++ ? "|" : ""); + if (flags & SHOW_RAW_DATA) + fprintf(ofp, "%sSHOW_RAW_DATA", others++ ? "|" : ""); + if (flags & DEREF_POINTERS) + fprintf(ofp, "%sDEREF_POINTERS", others++ ? "|" : ""); fprintf(ofp, ")\n"); } @@ -7890,11 +8048,18 @@ dump_offset_table(char *spec, ulong make OFFSET(page_objects)); fprintf(fp, " page_slab: %ld\n", OFFSET(page_slab)); + fprintf(fp, " page_slab_page: %ld\n", + OFFSET(page_slab_page)); fprintf(fp, " page_first_page: %ld\n", OFFSET(page_first_page)); fprintf(fp, " page_freelist: %ld\n", OFFSET(page_freelist)); + fprintf(fp, " trace_print_flags_mask: %ld\n", + OFFSET(trace_print_flags_mask)); + fprintf(fp, " trace_print_flags_name: %ld\n", + OFFSET(trace_print_flags_name)); + fprintf(fp, " swap_info_struct_swap_file: %ld\n", OFFSET(swap_info_struct_swap_file)); fprintf(fp, " swap_info_struct_swap_vfsmnt: %ld\n", @@ -9021,6 +9186,7 @@ dump_offset_table(char *spec, ulong make fprintf(fp, "\n size_table:\n"); fprintf(fp, " page: %ld\n", SIZE(page)); fprintf(fp, " page_flags: %ld\n", SIZE(page_flags)); + fprintf(fp, " trace_print_flags: %ld\n", SIZE(trace_print_flags)); fprintf(fp, " free_area_struct: %ld\n", SIZE(free_area_struct)); fprintf(fp, " free_area: %ld\n", --- crash-7.0.2/diskdump.c 2013-10-25 15:28:55.941879568 -0400 +++ crash-7.0.3/diskdump.c 2013-10-23 09:12:15.913831516 -0400 @@ -40,11 +40,13 @@ struct diskdump_data { struct disk_dump_sub_header *sub_header; struct kdump_sub_header *sub_header_kdump; + unsigned long long max_mapnr; /* 64bit max_mapnr */ + size_t data_offset; int block_size; int block_shift; char *bitmap; - int bitmap_len; + off_t bitmap_len; char *dumpable_bitmap; int byte, bit; char *compressed_page; /* copy of compressed page data */ @@ -170,9 +172,9 @@ add_diskdump_data(char* name) dd->filename = name; if (CRASHDEBUG(1)) - fprintf(fp, "%s: start_pfn=%lu, end_pfn=%lu\n", name, - dd->sub_header_kdump->start_pfn, - dd->sub_header_kdump->end_pfn); + fprintf(fp, "%s: start_pfn=%llu, end_pfn=%llu\n", name, + dd->sub_header_kdump->start_pfn_64, + dd->sub_header_kdump->end_pfn_64); } static void @@ -199,13 +201,13 @@ get_bit(char *map, int byte, int bit) } static inline int -page_is_ram(unsigned int nr) +page_is_ram(unsigned long nr) { return get_bit(dd->bitmap, nr >> 3, nr & 7); } static inline int -page_is_dumpable(unsigned int nr) +page_is_dumpable(unsigned long nr) { return dd->dumpable_bitmap[nr>>3] & (1 << (nr & 7)); } @@ -214,7 +216,7 @@ static inline int dump_is_partial(const struct disk_dump_header *header) { return header->bitmap_blocks >= - divideup(divideup(header->max_mapnr, 8), dd->block_size) * 2; + divideup(divideup(dd->max_mapnr, 8), dd->block_size) * 2; } static int @@ -321,6 +323,9 @@ x86_process_elf_notes(void *note_ptr, un * [40] unsigned long size_note; / header_version 4 and later / * [44] off_t offset_eraseinfo; / header_version 5 and later / * [52] unsigned long size_eraseinfo; / header_version 5 and later / + * [56] unsigned long long start_pfn_64; / header_version 6 and later / + * [64] unsigned long long end_pfn_64; / header_version 6 and later / + * [72] unsigned long long max_mapnr_64; / header_version 6 and later / * }; * * But when compiled on an ARM processor, each 64-bit "off_t" would be pushed @@ -337,7 +342,10 @@ x86_process_elf_notes(void *note_ptr, un * [40] off_t offset_note; / header_version 4 and later / * [48] unsigned long size_note; / header_version 4 and later / * [56] off_t offset_eraseinfo; / header_version 5 and later / - * [62] unsigned long size_eraseinfo; / header_version 5 and later / + * [64] unsigned long size_eraseinfo; / header_version 5 and later / + * [72] unsigned long long start_pfn_64; / header_version 6 and later / + * [80] unsigned long long end_pfn_64; / header_version 6 and later / + * [88] unsigned long long max_mapnr_64; / header_version 6 and later / * }; * */ @@ -357,6 +365,10 @@ struct kdump_sub_header_ARM_target { int pad3; off_t offset_eraseinfo; /* header_version 5 and later */ unsigned long size_eraseinfo; /* header_version 5 and later */ + int pad4; + unsigned long long start_pfn_64; /* header_version 6 and later */ + unsigned long long end_pfn_64; /* header_version 6 and later */ + unsigned long long max_mapnr_64; /* header_version 6 and later */ }; static void @@ -380,6 +392,15 @@ arm_kdump_header_adjust(int header_versi kdsh->offset_eraseinfo = kdsh_ARM_target->offset_eraseinfo; kdsh->size_eraseinfo = kdsh_ARM_target->size_eraseinfo; } + if (header_version >= 6) { + kdsh->start_pfn_64 = kdsh_ARM_target->start_pfn_64; + kdsh->end_pfn_64 = kdsh_ARM_target->end_pfn_64; + kdsh->max_mapnr_64 = kdsh_ARM_target->max_mapnr_64; + } else { + kdsh->start_pfn_64 = kdsh_ARM_target->start_pfn; + kdsh->end_pfn_64 = kdsh_ARM_target->end_pfn; + kdsh->max_mapnr_64 = dd->max_mapnr; + } } #endif /* __i386__ && ARM */ @@ -390,7 +411,10 @@ read_dump_header(char *file) struct disk_dump_sub_header *sub_header = NULL; struct kdump_sub_header *sub_header_kdump = NULL; size_t size; - int bitmap_len; + off_t bitmap_len; + char *bufptr; + size_t len; + ssize_t bytes_read; int block_size = (int)sysconf(_SC_PAGESIZE); off_t offset; const off_t failed = (off_t)-1; @@ -516,6 +540,13 @@ restart: } } dd->sub_header = sub_header; + + /* the 64bit max_mapnr only exists in sub-header of compressed + * kdump file, if it's not a compressed kdump file, we have to + * use the old 32bit max_mapnr in dumpfile header. + * max_mapnr may be truncated here. + */ + dd->max_mapnr = header->max_mapnr; } else if (KDUMP_CMPRS_VALID()) { if ((sub_header_kdump = malloc(block_size)) == NULL) error(FATAL, "compressed kdump: cannot malloc sub_header_kdump buffer\n"); @@ -540,8 +571,20 @@ restart: #if defined(__i386__) && defined(ARM) arm_kdump_header_adjust(header->header_version); #endif + /* use 64bit max_mapnr in compressed kdump file sub-header */ + if (header->header_version >= 6) + dd->max_mapnr = dd->sub_header_kdump->max_mapnr_64; + else { + dd->sub_header_kdump->start_pfn_64 + = dd->sub_header_kdump->start_pfn; + dd->sub_header_kdump->end_pfn_64 + = dd->sub_header_kdump->end_pfn; + } } + if (header->header_version < 6) + dd->max_mapnr = header->max_mapnr; + /* read memory bitmap */ bitmap_len = block_size * header->bitmap_blocks; dd->bitmap_len = bitmap_len; @@ -571,10 +614,18 @@ restart: DISKDUMP_VALID() ? "diskdump" : "compressed kdump"); goto err; } - if (read(dd->dfd, dd->bitmap, bitmap_len) < bitmap_len) { - error(INFO, "%s: cannot read memory bitmap\n", - DISKDUMP_VALID() ? "diskdump" : "compressed kdump"); - goto err; + bufptr = dd->bitmap; + len = bitmap_len; + while (len) { + bytes_read = read(dd->dfd, bufptr, len); + if (bytes_read < 0) { + error(INFO, "%s: cannot read memory bitmap\n", + DISKDUMP_VALID() ? "diskdump" + : "compressed kdump"); + goto err; + } + len -= bytes_read; + bufptr += bytes_read; } } @@ -679,13 +730,13 @@ restart: } if (!is_split) { - max_sect_len = divideup(header->max_mapnr, BITMAP_SECT_LEN); + max_sect_len = divideup(dd->max_mapnr, BITMAP_SECT_LEN); pfn = 0; dd->filename = file; } else { - ulong start = sub_header_kdump->start_pfn; - ulong end = sub_header_kdump->end_pfn; + unsigned long long start = sub_header_kdump->start_pfn_64; + unsigned long long end = sub_header_kdump->end_pfn_64; max_sect_len = divideup(end - start + 1, BITMAP_SECT_LEN); pfn = start; } @@ -727,8 +778,9 @@ pfn_to_pos(ulong pfn) ulong p1, p2; if (KDUMP_SPLIT()) { - p1 = pfn - dd->sub_header_kdump->start_pfn; - p2 = round(p1, BITMAP_SECT_LEN) + dd->sub_header_kdump->start_pfn; + p1 = pfn - dd->sub_header_kdump->start_pfn_64; + p2 = round(p1, BITMAP_SECT_LEN) + + dd->sub_header_kdump->start_pfn_64; } else { p1 = pfn; @@ -1034,12 +1086,12 @@ read_diskdump(int fd, void *bufptr, int if (KDUMP_SPLIT()) { /* Find proper dd */ int i; - unsigned long start_pfn; - unsigned long end_pfn; + unsigned long long start_pfn; + unsigned long long end_pfn; for (i=0; isub_header_kdump->start_pfn; - end_pfn = dd_list[i]->sub_header_kdump->end_pfn; + start_pfn = dd_list[i]->sub_header_kdump->start_pfn_64; + end_pfn = dd_list[i]->sub_header_kdump->end_pfn_64; if ((pfn >= start_pfn) && (pfn <= end_pfn)) { dd = dd_list[i]; break; @@ -1058,14 +1110,14 @@ read_diskdump(int fd, void *bufptr, int curpaddr = paddr & ~((physaddr_t)(dd->block_size-1)); page_offset = paddr & ((physaddr_t)(dd->block_size-1)); - if ((pfn >= dd->header->max_mapnr) || !page_is_ram(pfn)) { + if ((pfn >= dd->max_mapnr) || !page_is_ram(pfn)) { if (CRASHDEBUG(8)) { fprintf(fp, "read_diskdump: SEEK_ERROR: " "paddr/pfn: %llx/%lx ", (ulonglong)paddr, pfn); - if (pfn >= dd->header->max_mapnr) - fprintf(fp, "max_mapnr: %x\n", - dd->header->max_mapnr); + if (pfn >= dd->max_mapnr) + fprintf(fp, "max_mapnr: %llx\n", + dd->max_mapnr); else fprintf(fp, "!page_is_ram\n"); } @@ -1507,12 +1559,31 @@ __diskdump_memory_dump(FILE *fp) fprintf(fp, " tv_usec: %lx\n", dh->timestamp.tv_usec); fprintf(fp, " status: %x (", dh->status); others = 0; - if (dh->status & DUMP_HEADER_COMPLETED) - fprintf(fp, "%sDUMP_HEADER_COMPLETED", others++ ? "|" : ""); - if (dh->status & DUMP_HEADER_INCOMPLETED) - fprintf(fp, "%sDUMP_HEADER_INCOMPLETED", others++ ? "|" : ""); - if (dh->status & DUMP_HEADER_COMPRESSED) - fprintf(fp, "%sDUMP_HEADER_COMPRESSED", others++ ? "|" : ""); + switch (dd->flags & (DISKDUMP_LOCAL|KDUMP_CMPRS_LOCAL)) + { + case DISKDUMP_LOCAL: + if (dh->status == DUMP_HEADER_COMPLETED) + fprintf(fp, "%sDUMP_HEADER_COMPLETED", + others++ ? "|" : ""); + else if (dh->status == DUMP_HEADER_INCOMPLETED) + fprintf(fp, "%sDUMP_HEADER_INCOMPLETED", + others++ ? "|" : ""); + else if (dh->status == DUMP_HEADER_COMPRESSED) + fprintf(fp, "%sDUMP_HEADER_COMPRESSED", + others++ ? "|" : ""); + break; + case KDUMP_CMPRS_LOCAL: + if (dh->status & DUMP_DH_COMPRESSED_ZLIB) + fprintf(fp, "%sDUMP_DH_COMPRESSED_ZLIB", + others++ ? "|" : ""); + if (dh->status & DUMP_DH_COMPRESSED_LZO) + fprintf(fp, "%sDUMP_DH_COMPRESSED_LZO", + others++ ? "|" : ""); + if (dh->status & DUMP_DH_COMPRESSED_SNAPPY) + fprintf(fp, "%sDUMP_DH_COMPRESSED_SNAPPY", + others++ ? "|" : ""); + break; + } fprintf(fp, ")\n"); fprintf(fp, " block_size: %d\n", dh->block_size); fprintf(fp, " sub_hdr_size: %d\n", dh->sub_hdr_size); @@ -1662,6 +1733,23 @@ __diskdump_memory_dump(FILE *fp) dump_eraseinfo(fp); } } + if (dh->header_version >= 6) { + fprintf(fp, " start_pfn_64: "); + if (KDUMP_SPLIT()) + fprintf(fp, "%lld (0x%llx)\n", + kdsh->start_pfn_64, kdsh->start_pfn_64); + else + fprintf(fp, "(unused)\n"); + fprintf(fp, " end_pfn_64: "); + if (KDUMP_SPLIT()) + fprintf(fp, "%lld (0x%llx)\n", + kdsh->end_pfn_64, kdsh->end_pfn_64); + else + fprintf(fp, "(unused)\n"); + + fprintf(fp, " max_mapnr_64: %llu (0x%llx)\n", + kdsh->max_mapnr_64, kdsh->max_mapnr_64); + } fprintf(fp, "\n"); } else fprintf(fp, "(n/a)\n\n"); @@ -1670,7 +1758,8 @@ __diskdump_memory_dump(FILE *fp) fprintf(fp, " block_size: %d\n", dd->block_size); fprintf(fp, " block_shift: %d\n", dd->block_shift); fprintf(fp, " bitmap: %lx\n", (ulong)dd->bitmap); - fprintf(fp, " bitmap_len: %d\n", dd->bitmap_len); + fprintf(fp, " bitmap_len: %lld\n", (ulonglong)dd->bitmap_len); + fprintf(fp, " max_mapnr: %lld (0x%llx)\n", dd->max_mapnr, dd->max_mapnr); fprintf(fp, " dumpable_bitmap: %lx\n", (ulong)dd->dumpable_bitmap); fprintf(fp, " byte: %d\n", dd->byte); fprintf(fp, " bit: %d\n", dd->bit); --- crash-7.0.2/makedumpfile.c 2013-10-25 15:28:55.951879567 -0400 +++ crash-7.0.3/makedumpfile.c 2013-10-25 10:10:07.615755902 -0400 @@ -59,7 +59,7 @@ store_flat_data_array(char *file, struct unsigned long long num_allocated = 0; unsigned long long num_stored = 0; unsigned long long size_allocated; - struct flat_data *ptr = NULL, *cur; + struct flat_data *ptr = NULL, *cur, *new; struct makedumpfile_data_header fdh; fd = open(file, O_RDONLY); @@ -77,12 +77,13 @@ store_flat_data_array(char *file, struct num_allocated += 100; size_allocated = sizeof(struct flat_data) * num_allocated; - ptr = realloc(ptr, size_allocated); - if (ptr == NULL) { + new = realloc(ptr, size_allocated); + if (new == NULL) { error(INFO, "unable to realloc flat_data structures\n"); break; } + ptr = new; } offset_fdh = lseek(fd, 0x0, SEEK_CUR); --- crash-7.0.2/unwind_arm.c 2013-10-25 15:28:55.938879568 -0400 +++ crash-7.0.3/unwind_arm.c 2013-09-06 15:03:40.804904775 -0400 @@ -325,7 +325,8 @@ init_module_unwind_tables(void) hq_open(); cnt = do_list(&ld); if (cnt == -1) { - error(WARNING, "UNWIND: failed to gather unwind_table list"); + error(WARNING, "UNWIND: failed to gather unwind_table list\n"); + hq_close(); return FALSE; } table_list = (ulong *)GETBUF(cnt * sizeof(ulong)); --- crash-7.0.2/sadump.c 2013-10-25 15:28:55.941879568 -0400 +++ crash-7.0.3/sadump.c 2013-10-25 10:10:13.104755651 -0400 @@ -18,6 +18,7 @@ #include "defs.h" #include "sadump.h" +#include /* htonl, htons */ #include enum { @@ -118,7 +119,7 @@ read_dump_header(char *file) { struct sadump_part_header *sph = NULL; struct sadump_header *sh = NULL; - struct sadump_disk_set_header *sdh = NULL; + struct sadump_disk_set_header *new, *sdh = NULL; struct sadump_media_header *smh = NULL; struct sadump_diskset_data *sd_list_len_0 = NULL; size_t block_size = SADUMP_DEFAULT_BLOCK_SIZE; @@ -127,7 +128,8 @@ read_dump_header(char *file) uint32_t smram_cpu_state_size = 0; ulong bitmap_len, dumpable_bitmap_len; char *bitmap = NULL, *dumpable_bitmap = NULL, *page_buf = NULL; - char guid1[33], guid2[33]; + char guid1[SADUMP_EFI_GUID_TEXT_REPR_LEN+1]; + char guid2[SADUMP_EFI_GUID_TEXT_REPR_LEN+1]; sph = malloc(block_size); if (!sph) { @@ -226,12 +228,13 @@ restart: header_size = header_blocks * block_size; if (header_size > block_size) { - sdh = realloc(sdh, header_size); - if (!sdh) { + new = realloc(sdh, header_size); + if (!new) { error(INFO, "sadump: cannot re-allocate disk " "set buffer\n"); goto err; } + sdh = new; } if (!read_device(sdh, header_size, &offset)) { @@ -468,7 +471,8 @@ add_disk(char *file) struct sadump_part_header *ph; struct sadump_diskset_data *this_disk; int diskid; - char guid1[33], guid2[33]; + char guid1[SADUMP_EFI_GUID_TEXT_REPR_LEN+1]; + char guid2[SADUMP_EFI_GUID_TEXT_REPR_LEN+1]; diskid = sd->sd_list_len - 1; this_disk = sd->sd_list[diskid]; @@ -863,7 +867,7 @@ guid_to_str(efi_guid_t *guid, char *buf, { snprintf(buf, buflen, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", - guid->data1, guid->data2, guid->data3, + htonl(guid->data1), htons(guid->data2), htons(guid->data3), guid->data4[0], guid->data4[1], guid->data4[2], guid->data4[3], guid->data4[4], guid->data4[5], guid->data4[6], guid->data4[7]); @@ -905,7 +909,7 @@ int sadump_memory_dump(FILE *fp) struct sadump_header *sh; struct sadump_media_header *smh; int i, others; - char guid[33]; + char guid[SADUMP_EFI_GUID_TEXT_REPR_LEN+1]; fprintf(fp, "sadump_data: \n"); fprintf(fp, " filename: %s\n", sd->filename); --- crash-7.0.2/defs.h 2013-10-25 15:28:55.937879568 -0400 +++ crash-7.0.3/defs.h 2013-10-25 11:49:58.544481437 -0400 @@ -59,7 +59,7 @@ #define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) #endif -#define BASELEVEL_REVISION "7.0.2" +#define BASELEVEL_REVISION "7.0.3" #undef TRUE #undef FALSE @@ -503,6 +503,7 @@ struct program_context { #define QEMU_MEM_DUMP (0x100ULL) #define GET_LOG (0x200ULL) #define VMCOREINFO (0x400ULL) +#define ALLOW_FP (0x800ULL) char *cleanup; char *namelist_orig; char *namelist_debug_orig; @@ -1876,6 +1877,9 @@ struct offset_table { long task_struct_thread_context_fp; long task_struct_thread_context_sp; long task_struct_thread_context_pc; + long page_slab_page; + long trace_print_flags_mask; + long trace_print_flags_name; }; struct size_table { /* stash of commonly-used sizes */ @@ -2016,6 +2020,7 @@ struct size_table { /* stash of long hrtimer_clock_base; long hrtimer_base; long tnt; + long trace_print_flags; }; struct array_table { @@ -2183,6 +2188,13 @@ struct vm_table { /* kern int cpu_slab_type; int nr_vm_event_items; char **vm_event_items; + int nr_bad_slab_caches; + ulong *bad_slab_caches; + int nr_pageflags; + struct pageflags_data { + ulong mask; + char *name; + } *pageflags_data; }; #define NODES (0x1) @@ -2211,6 +2223,7 @@ struct vm_table { /* kern #define NODELISTS_IS_PTR (0x800000) #define KMALLOC_COMMON (0x1000000) #define USE_VMAP_AREA (0x2000000) +#define PAGEFLAGS (0x4000000) #define IS_FLATMEM() (vt->flags & FLATMEM) #define IS_DISCONTIGMEM() (vt->flags & DISCONTIGMEM) @@ -4321,6 +4334,7 @@ int calculate(char *, ulong *, ulonglong int endian_mismatch(char *, char, ulong); uint16_t swap16(uint16_t, int); uint32_t swap32(uint32_t, int); +ulong *get_cpumask_buf(void); int make_cpumask(char *, ulong *, int, int *); size_t strlcpy(char *, char *, size_t); struct rb_node *rb_first(struct rb_root *); --- crash-7.0.2/diskdump.h 2013-10-25 15:28:56.133879559 -0400 +++ crash-7.0.3/diskdump.h 2013-10-18 11:11:20.610295600 -0400 @@ -42,7 +42,9 @@ struct disk_dump_header { header in blocks */ unsigned int bitmap_blocks; /* Size of Memory bitmap in block */ - unsigned int max_mapnr; /* = max_mapnr */ + unsigned int max_mapnr; /* = max_mapnr, OBSOLETE! + 32bit only, full 64bit + in sub header. */ unsigned int total_ram_blocks;/* Number of blocks should be written */ unsigned int device_blocks; /* Number of total blocks in @@ -61,14 +63,21 @@ struct kdump_sub_header { unsigned long phys_base; int dump_level; /* header_version 1 and later */ int split; /* header_version 2 and later */ - unsigned long start_pfn; /* header_version 2 and later */ - unsigned long end_pfn; /* header_version 2 and later */ + unsigned long start_pfn; /* header_version 2 and later, + OBSOLETE! 32bit only, full 64bit + in start_pfn_64. */ + unsigned long end_pfn; /* header_version 2 and later, + OBSOLETE! 32bit only, full 64bit + in end_pfn_64. */ off_t offset_vmcoreinfo; /* header_version 3 and later */ unsigned long size_vmcoreinfo; /* header_version 3 and later */ off_t offset_note; /* header_version 4 and later */ unsigned long size_note; /* header_version 4 and later */ off_t offset_eraseinfo; /* header_version 5 and later */ unsigned long size_eraseinfo; /* header_version 5 and later */ + unsigned long long start_pfn_64; /* header_version 6 and later */ + unsigned long long end_pfn_64; /* header_version 6 and later */ + unsigned long long max_mapnr_64; /* header_version 6 and later */ }; /* page flags */ --- crash-7.0.2/lkcd_vmdump_v1.h 2013-10-25 15:28:55.935879568 -0400 +++ crash-7.0.3/lkcd_vmdump_v1.h 2013-10-24 09:06:51.222888110 -0400 @@ -117,10 +117,12 @@ typedef struct _dump_header_s { #ifndef IA64 #ifndef S390 #ifndef S390X +#ifndef ARM64 struct pt_regs dh_regs; #endif #endif #endif +#endif /* the address of the current task */ struct task_struct *dh_current_task; --- crash-7.0.2/lkcd_vmdump_v2_v3.h 2013-10-25 15:28:55.938879568 -0400 +++ crash-7.0.3/lkcd_vmdump_v2_v3.h 2013-10-24 09:13:13.588870592 -0400 @@ -84,9 +84,11 @@ typedef struct _dump_header_asm_s { /* the dump registers */ #ifndef S390 #ifndef S390X +#ifndef ARM64 struct pt_regs dha_regs; #endif #endif +#endif } dump_header_asm_t; --- crash-7.0.2/sadump.h 2013-10-25 15:28:55.939879568 -0400 +++ crash-7.0.3/sadump.h 2013-09-13 08:48:05.576229221 -0400 @@ -41,6 +41,8 @@ typedef struct { uint8_t data4[8]; } efi_guid_t; +#define SADUMP_EFI_GUID_TEXT_REPR_LEN 36 + struct sadump_part_header { #define SADUMP_SIGNATURE1 0x75646173 #define SADUMP_SIGNATURE2 0x0000706d --- crash-7.0.2/extensions/snap.c 2013-10-25 15:28:55.943879568 -0400 +++ crash-7.0.3/extensions/snap.c 2013-10-25 10:10:39.188754456 -0400 @@ -416,7 +416,8 @@ generate_elf_header(int type, int fd, ch } else if (machine_type("PPC64")) { e_machine = EM_PPC64; prstatus_len = sizeof(prstatus.ppc64); - } + } else + return NULL; /* should be enought for the notes + roundup + two blocks */ buffer = (char *)GETBUF(sizeof(Elf64_Ehdr) + @@ -538,7 +539,7 @@ generate_elf_header(int type, int fd, ch break; } - l_offset += load[i].p_filesz; +// l_offset += load[i].p_filesz; offset += sizeof(Elf64_Phdr); ptr += sizeof(Elf64_Phdr); } --- crash-7.0.2/extensions/trace.c 2013-10-25 15:28:55.944879568 -0400 +++ crash-7.0.3/extensions/trace.c 2013-10-25 10:10:44.031754234 -0400 @@ -1453,6 +1453,7 @@ static void ftrace_show(int argc, char * if ((file = popen(trace_cmd, "r"))) { ret = fread(buf, 1, sizeof(buf), file); buf[ret] = 0; + pclose(file); } if (!strstr(buf, "trace-cmd version")) { if (env_trace_cmd) --- crash-7.0.3/defs.h.orig +++ crash-7.0.3/defs.h @@ -1880,6 +1880,8 @@ struct offset_table { long page_slab_page; long trace_print_flags_mask; long trace_print_flags_name; + long task_struct_rss_stat; + long task_rss_stat_count; }; struct size_table { /* stash of commonly-used sizes */ --- crash-7.0.3/symbols.c.orig +++ crash-7.0.3/symbols.c @@ -7643,6 +7643,10 @@ dump_offset_table(char *spec, ulong make OFFSET(task_struct_tgid)); fprintf(fp, " task_struct_namespace: %ld\n", OFFSET(task_struct_namespace)); + fprintf(fp, " task_struct_rss_stat: %ld\n", + OFFSET(task_struct_rss_stat)); + fprintf(fp, " task_rss_stat_count: %ld\n", + OFFSET(task_rss_stat_count)); fprintf(fp, " task_struct_pids: %ld\n", OFFSET(task_struct_pids)); fprintf(fp, " task_struct_last_run: %ld\n", --- crash-7.0.3/memory.c.orig +++ crash-7.0.3/memory.c @@ -4003,6 +4003,7 @@ void get_task_mem_usage(ulong task, struct task_mem_usage *tm) { struct task_context *tc; + long rss = 0; BZERO(tm, sizeof(struct task_mem_usage)); @@ -4036,22 +4037,65 @@ get_task_mem_usage(ulong task, struct ta filepages = 0; anonpages = 1; } - tm->rss += ULONG(tt->mm_struct + + rss += LONG(tt->mm_struct + OFFSET(mm_struct_rss_stat) + OFFSET(mm_rss_stat_count) + - (filepages * sizeof(ulong))); - tm->rss += ULONG(tt->mm_struct + + (filepages * sizeof(long))); + rss += LONG(tt->mm_struct + OFFSET(mm_struct_rss_stat) + OFFSET(mm_rss_stat_count) + - (anonpages * sizeof(ulong))); + (anonpages * sizeof(long))); } + + /* Check whether SPLIT_RSS_COUNTING is enabled */ + if (VALID_MEMBER(task_struct_rss_stat)) { + int i, sync_rss; + ulong tgid; + struct task_context *tc1; + + tgid = task_tgid(task); + + tc1 = FIRST_CONTEXT(); + for (i = 0; i < RUNNING_TASKS(); i++, tc1++) { + if (task_tgid(tc1->task) != tgid) + continue; + + /* count 0 -> filepages */ + if (!readmem(tc1->task + + OFFSET(task_struct_rss_stat) + + OFFSET(task_rss_stat_count), KVADDR, + &sync_rss, + sizeof(int), + "task_struct rss_stat MM_FILEPAGES", + RETURN_ON_ERROR)) + continue; + + rss += sync_rss; + + /* count 1 -> anonpages */ + if (!readmem(tc1->task + + OFFSET(task_struct_rss_stat) + + OFFSET(task_rss_stat_count) + + sizeof(int), + KVADDR, &sync_rss, + sizeof(int), + "task_struct rss_stat MM_ANONPAGES", + RETURN_ON_ERROR)) + continue; + + rss += sync_rss; + } + } + /* * mm_struct._anon_rss and mm_struct._file_rss should exist. */ if (VALID_MEMBER(mm_struct_anon_rss)) - tm->rss += ULONG(tt->mm_struct + OFFSET(mm_struct_anon_rss)); + rss += LONG(tt->mm_struct + OFFSET(mm_struct_anon_rss)); if (VALID_MEMBER(mm_struct_file_rss)) - tm->rss += ULONG(tt->mm_struct + OFFSET(mm_struct_file_rss)); + rss += LONG(tt->mm_struct + OFFSET(mm_struct_file_rss)); + + tm->rss = (unsigned long)rss; } tm->total_vm = ULONG(tt->mm_struct + OFFSET(mm_struct_total_vm)); tm->pgd_addr = ULONG(tt->mm_struct + OFFSET(mm_struct_pgd)); --- crash-7.0.3/task.c.orig +++ crash-7.0.3/task.c @@ -349,6 +349,11 @@ task_init(void) MEMBER_OFFSET_INIT(task_struct_run_list, "task_struct", "run_list"); + MEMBER_OFFSET_INIT(task_struct_rss_stat, "task_struct", + "rss_stat"); + MEMBER_OFFSET_INIT(task_rss_stat_count, "task_rss_stat", + "count"); + if ((tt->task_struct = (char *)malloc(SIZE(task_struct))) == NULL) error(FATAL, "cannot malloc task_struct space.");