commit 745bb42ca78713373db1f06e7d94f4d5fd2d2ce7 Author: Mahesh Salgaonkar Date: Fri Aug 19 17:11:18 2011 +0900 [PATCH v2 7/8] Add erased information in compressed kdump file Include the erase information in the makedumpfile generated dumpfile. The erased information contains only effective lines from the filter configuration file. This patch supports inclusion of erase information in kdump compressed, flatten and split dumpfiles. On re-filtering, the erase information from input dumpfile (if any) will be carried over to resulting dumpfile. This patch introduces new members offset_eraseinfo, size_eraseinfo into the sub header in compressed kdump file. The erased information would contain *multiple* erased symbol names. For example, the following is based on your above example. struct S1 { int a; int b; int c; }; struct S2 { struct S1 *mystruct1; struct S1 *mystruct2; }; struct S2 mystruct2; #Filter command erase mystruct2.mystruct1.a erase mystruct2.mystruct1.c erase mystruct2.mystruct2.b #The erased information erase mystruct2.mystruct1.a 4 erase mystruct2.mystruct1.c 4 erase mystruct2.mystruct2.b 4 The dumpfile image : header dump data the erased information +------------------+-----------------------+-------------------------------+ | ................ | ..................... | erase mystruct2.mystruct1.a 4 | | offset_eraseinfo | ..................... | erase mystruct2.mystruct1.c 4 | | size_eraseinfo | ..................... | erase mystruct2.mystruct2.b 4 | +------------------+-------------------------------------------------------+ : offset_eraseinfo (offset in dumpfile) <------ size_eraseinfo -------> Signed-off-by: Mahesh Salgaonkar --- diff -Nrup kexec-tools-2.0.0.orig/makedumpfile-1.3.5/diskdump_mod.h kexec-tools-2.0.0/makedumpfile-1.3.5/diskdump_mod.h --- kexec-tools-2.0.0.orig/makedumpfile-1.3.5/diskdump_mod.h 2012-01-02 17:07:47.000000000 +0800 +++ kexec-tools-2.0.0/makedumpfile-1.3.5/diskdump_mod.h 2012-01-02 17:08:39.000000000 +0800 @@ -75,6 +75,8 @@ struct kdump_sub_header { 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 */ }; /* page flags */ diff -Nrup kexec-tools-2.0.0.orig/makedumpfile-1.3.5/IMPLEMENTATION kexec-tools-2.0.0/makedumpfile-1.3.5/IMPLEMENTATION --- kexec-tools-2.0.0.orig/makedumpfile-1.3.5/IMPLEMENTATION 2012-01-02 17:07:47.000000000 +0800 +++ kexec-tools-2.0.0/makedumpfile-1.3.5/IMPLEMENTATION 2012-01-02 17:18:25.000000000 +0800 @@ -25,6 +25,10 @@ | page data (pfn 1) | | : | | page data (pfn Z) | + +------------------------------------------+ offset_eraseinfo + | erase mystruct2.mystruct1.var 4 | + | erase mystruct2.mystruct1.ptr nullify | + | erase mystruct2.mystruct.array 100 | +------------------------------------------+ @@ -64,6 +68,15 @@ 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 */ + 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 */ }; - 1st-bitmap diff -Nrup kexec-tools-2.0.0.orig/makedumpfile-1.3.5/makedumpfile.c kexec-tools-2.0.0/makedumpfile-1.3.5/makedumpfile.c --- kexec-tools-2.0.0.orig/makedumpfile-1.3.5/makedumpfile.c 2012-01-02 17:07:47.000000000 +0800 +++ kexec-tools-2.0.0/makedumpfile-1.3.5/makedumpfile.c 2012-01-02 17:12:59.000000000 +0800 @@ -28,6 +28,8 @@ struct DumpInfo *info = NULL; struct module_sym_table mod_st = { 0 }; struct filter_info *filter_info = NULL; struct filter_config filter_config; +struct erase_info *erase_info = NULL; +unsigned long num_erase_info = 1; /* Node 0 is unused. */ char filename_stdout[] = FILENAME_STDOUT; int message_level; @@ -924,6 +926,11 @@ get_kdump_compressed_header_info(char *f info->offset_note = kh.offset_note; info->size_note = kh.size_note; } + if (dh.header_version >= 5) { + /* A dumpfile contains erased information. */ + info->offset_eraseinfo = kh.offset_eraseinfo; + info->size_eraseinfo = kh.size_eraseinfo; + } return TRUE; error: free(info->dh_memory); @@ -5883,7 +5890,7 @@ write_kdump_header(void) * Write common header */ strcpy(dh->signature, KDUMP_SIGNATURE); - dh->header_version = 4; + dh->header_version = 5; dh->block_size = info->page_size; dh->sub_hdr_size = sizeof(kh) + info->size_note; dh->sub_hdr_size = divideup(dh->sub_hdr_size, dh->block_size); @@ -5951,10 +5958,17 @@ write_kdump_header(void) kh.size_vmcoreinfo = info->size_vmcoreinfo; } } - if (!write_buffer(info->fd_dumpfile, dh->block_size, &kh, - size, info->name_dumpfile)) - goto out; + /* + * While writing dump data to STDOUT, delay the writing of sub header + * untill we gather erase info offset and size. + */ + if (!info->flag_flatten) { + if (!write_buffer(info->fd_dumpfile, dh->block_size, &kh, + size, info->name_dumpfile)) + goto out; + } + info->sub_header = kh; info->offset_bitmap1 = (DISKDUMP_HEADER_BLOCKS + dh->sub_hdr_size) * dh->block_size; @@ -6018,12 +6032,39 @@ split_filter_info(struct filter_info *pr return; } new->nullify = prev->nullify; + new->erase_info_idx = prev->erase_info_idx; + new->size_idx = prev->size_idx; new->paddr = next_paddr; new->size = size; new->next = prev->next; prev->next = new; } +void +update_erase_info(struct filter_info *fi) +{ + struct erase_info *ei; + + if (!fi->erase_info_idx) + return; + + ei = &erase_info[fi->erase_info_idx]; + + if (!ei->sizes) { + /* First time, allocate sizes array */ + ei->sizes = calloc(ei->num_sizes, sizeof(long)); + if (!ei->sizes) { + ERRMSG("Can't allocate memory for erase info sizes\n"); + return; + } + } + ei->erased = 1; + if (!fi->nullify) + ei->sizes[fi->size_idx] += fi->size; + else + ei->sizes[fi->size_idx] = -1; +} + int extract_filter_info(unsigned long long start_paddr, unsigned long long end_paddr, @@ -6057,6 +6098,7 @@ extract_filter_info(unsigned long long s filter_info = fi->next; else prev->next = fi->next; + update_erase_info(fi); free(fi); return TRUE; } @@ -6516,6 +6558,159 @@ out: return ret; } +/* + * Copy eraseinfo from input dumpfile/vmcore to output dumpfile. + */ +static int +copy_eraseinfo(struct cache_data *cd_eraseinfo) +{ + char *buf = NULL; + int ret = FALSE; + + buf = malloc(info->size_eraseinfo); + if (buf == NULL) { + ERRMSG("Can't allocate memory for erase info section. %s\n", + strerror(errno)); + return FALSE; + } + if (lseek(info->fd_memory, info->offset_eraseinfo, SEEK_SET) < 0) { + ERRMSG("Can't seek the dump memory(%s). %s\n", + info->name_memory, strerror(errno)); + goto out; + } + if (read(info->fd_memory, buf, info->size_eraseinfo) + != info->size_eraseinfo) { + ERRMSG("Can't read the dump memory(%s). %s\n", + info->name_memory, strerror(errno)); + goto out; + } + if (!write_cache(cd_eraseinfo, buf, info->size_eraseinfo)) + goto out; + ret = TRUE; +out: + if (buf) + free(buf); + return ret; +} + +static int +update_sub_header(void) +{ + off_t offset; + + /* seek to kdump sub header offset */ + offset = DISKDUMP_HEADER_BLOCKS * info->page_size; + + info->sub_header.offset_eraseinfo = info->offset_eraseinfo; + info->sub_header.size_eraseinfo = info->size_eraseinfo; + + if (!write_buffer(info->fd_dumpfile, offset, &info->sub_header, + sizeof(struct kdump_sub_header), info->name_dumpfile)) + return FALSE; + + return TRUE; +} + +/* + * Traverse through eraseinfo nodes and write it to the o/p dumpfile if the + * node has erased flag set. + */ +int +write_eraseinfo(struct cache_data *cd_page, unsigned long *size_out) +{ + int i, j, obuf_size = 0, ei_size = 0; + int ret = FALSE; + unsigned long size_eraseinfo = 0; + char *obuf = NULL; + char size_str[MAX_SIZE_STR_LEN]; + + for (i = 1; i < num_erase_info; i++) { + if (!erase_info[i].erased) + continue; + for (j = 0; j < erase_info[i].num_sizes; j++) { + if (erase_info[i].sizes[j] > 0) + sprintf(size_str, "%ld\n", + erase_info[i].sizes[j]); + else if (erase_info[i].sizes[j] == -1) + sprintf(size_str, "nullify\n"); + + /* Calculate the required buffer size. */ + ei_size = strlen("erase ") + + strlen(erase_info[i].symbol_expr) + 1 + + strlen(size_str) + + 1; + /* + * If obuf is allocated in the previous run and is + * big enough to hold current erase info string then + * reuse it otherwise realloc. + */ + if (ei_size > obuf_size) { + obuf_size = ei_size; + obuf = realloc(obuf, obuf_size); + if (!obuf) { + ERRMSG("Can't allocate memory for" + " output buffer\n"); + return FALSE; + } + } + sprintf(obuf, "erase %s %s", erase_info[i].symbol_expr, + size_str); + DEBUG_MSG(obuf); + if (!write_cache(cd_page, obuf, strlen(obuf))) + goto out; + size_eraseinfo += strlen(obuf); + } + } + /* + * Write the remainder. + */ + if (!write_cache_bufsz(cd_page)) + goto out; + + *size_out = size_eraseinfo; + ret = TRUE; +out: + if (obuf) + free(obuf); + + return ret; +} + +int +write_kdump_eraseinfo(struct cache_data *cd_page) +{ + off_t offset_eraseinfo; + unsigned long size_eraseinfo; + + DEBUG_MSG("Writing erase info...\n"); + offset_eraseinfo = cd_page->offset; + + /* + * In case of refiltering copy the existing eraseinfo from input + * dumpfile to o/p dumpfile. + */ + if (info->offset_eraseinfo && info->size_eraseinfo) { + if (!copy_eraseinfo(cd_page)) + return FALSE; + } + + /* Initialize eraseinfo offset with new offset value. */ + info->offset_eraseinfo = offset_eraseinfo; + + if (!write_eraseinfo(cd_page, &size_eraseinfo)) + return FALSE; + + info->size_eraseinfo += size_eraseinfo; + DEBUG_MSG("offset_eraseinfo: %lx, size_eraseinfo: %ld\n", + info->offset_eraseinfo, info->size_eraseinfo); + + /* Update the erase info offset and size in kdump sub header */ + if (!update_sub_header()) + return FALSE; + + return TRUE; +} + int write_kdump_bitmap(void) { @@ -7335,6 +7530,8 @@ writeout_dumpfile(void) goto out; if (!write_kdump_pages(&cd_header, &cd_page)) goto out; + if (!write_kdump_eraseinfo(&cd_page)) + goto out; if (!write_kdump_bitmap()) goto out; } @@ -7725,6 +7922,8 @@ free_config_entry(struct config_entry *c free(p->name); if (p->type_name) free(p->type_name); + if (p->symbol_expr) + free(p->symbol_expr); free(p); } } @@ -8073,6 +8272,13 @@ read_filter_entry(struct config *config, line); return FALSE; } + + /* + * Save the symbol expression string for generation of eraseinfo data + * later while writing dumpfile. + */ + config->filter_symbol[idx]->symbol_expr = strdup(token); + if (config->iter_entry) { if (strcmp(config->filter_symbol[idx]->name, config->iter_entry->name)) { @@ -8670,8 +8876,11 @@ resolve_list_entry(struct config_entry * * Insert the filter info node using insertion sort. * If filter node for a given paddr is aready present then update the size * and delete the fl_info node passed. + * + * Return 1 on successfull insertion. + * Return 0 if filter node with same paddr is found. */ -void +int insert_filter_info(struct filter_info *fl_info) { struct filter_info *prev = NULL; @@ -8679,7 +8888,7 @@ insert_filter_info(struct filter_info *f if (!ptr) { filter_info = fl_info; - return; + return 1; } while (ptr) { @@ -8692,7 +8901,7 @@ insert_filter_info(struct filter_info *f if (fl_info->size > ptr->size) ptr->size = fl_info->size; free(fl_info); - return; + return 0; } if (prev) { @@ -8703,7 +8912,80 @@ insert_filter_info(struct filter_info *f fl_info->next = filter_info; filter_info = fl_info; } - return; + return 1; +} + +/* + * Create an erase info node for each erase command. One node per erase + * command even if it is part of loop construct. + * For erase commands that are not part of loop construct, the num_sizes will + * always be 1 + * For erase commands that are part of loop construct, the num_sizes may be + * 1 or >1 depending on number iterations. This function will called multiple + * times depending on iterations. At first invokation create a node and + * increment num_sizes for subsequent invokations. + * + * The valid erase info node starts from index value 1. (index 0 is invalid + * index). + * + * Index 0 1 2 3 + * +------+--------+--------+--------+ + * erase_info->|Unused| | | |...... + * +------+--------+--------+--------+ + * | . . ..... + * V + * +---------+ + * | char* |----> Original erase command string + * +---------+ + * |num_sizes| + * +---------+ +--+--+--+ + * | sizes |----> | | | |... Sizes array of num_sizes + * +---------+ +--+--+--+ + * + * On success, return the index value of erase node for given erase command. + * On failure, return 0. + */ +static int +add_erase_info_node(struct config_entry *filter_symbol) +{ + int idx = filter_symbol->erase_info_idx; + + /* + * Check if node is already created, if yes, increment the num_sizes. + */ + if (idx) { + erase_info[idx].num_sizes++; + return idx; + } + + /* Allocate a new node. */ + DEBUG_MSG("Allocating new erase info node for command \"%s\"\n", + filter_symbol->symbol_expr); + idx = num_erase_info++; + erase_info = realloc(erase_info, + sizeof(struct erase_info) * num_erase_info); + if (!erase_info) { + ERRMSG("Can't get memory to create erase information.\n"); + return 0; + } + + memset(&erase_info[idx], 0, sizeof(struct erase_info)); + erase_info[idx].symbol_expr = filter_symbol->symbol_expr; + erase_info[idx].num_sizes = 1; + + filter_symbol->symbol_expr = NULL; + filter_symbol->erase_info_idx = idx; + + return idx; +} + +/* Return the index value in sizes array for given erase command index. */ +static inline int +get_size_index(int ei_idx) +{ + if (ei_idx) + return erase_info[ei_idx].num_sizes - 1; + return 0; } int @@ -8742,7 +9024,10 @@ update_filter_info(struct config_entry * fl_info->size = size; fl_info->nullify = filter_symbol->nullify; - insert_filter_info(fl_info); + if (insert_filter_info(fl_info)) { + fl_info->erase_info_idx = add_erase_info_node(filter_symbol); + fl_info->size_idx = get_size_index(fl_info->erase_info_idx); + } return TRUE; } @@ -8993,6 +9278,7 @@ void clear_filter_info(void) { struct filter_info *prev, *fi = filter_info; + int i; /* Delete filter_info nodes that are left out. */ while (fi) { @@ -9001,6 +9287,13 @@ clear_filter_info(void) free(prev); } filter_info = NULL; + if (erase_info) { + for (i = 1; i < num_erase_info; i++) { + free(erase_info[i].symbol_expr); + free(erase_info[i].sizes); + } + free(erase_info); + } } int @@ -9189,6 +9482,8 @@ store_splitting_info(void) } SPLITTING_START_PFN(i) = kh.start_pfn; SPLITTING_END_PFN(i) = kh.end_pfn; + SPLITTING_OFFSET_EI(i) = kh.offset_eraseinfo; + SPLITTING_SIZE_EI(i) = kh.size_eraseinfo; } return TRUE; } @@ -9395,6 +9690,7 @@ reassemble_kdump_pages(void) struct page_desc pd, pd_zero; struct cache_data cd_pd, cd_data; char *data = NULL; + unsigned long data_buf_size = info->page_size; initialize_2nd_bitmap(&bitmap2); @@ -9408,7 +9704,7 @@ reassemble_kdump_pages(void) free_cache_data(&cd_pd); return FALSE; } - if ((data = malloc(info->page_size)) == NULL) { + if ((data = malloc(data_buf_size)) == NULL) { ERRMSG("Can't allcate memory for page data.\n"); free_cache_data(&cd_pd); free_cache_data(&cd_data); @@ -9511,6 +9807,49 @@ reassemble_kdump_pages(void) if (!write_cache_bufsz(&cd_data)) goto out; + info->offset_eraseinfo = cd_data.offset; + /* Copy eraseinfo from split dumpfiles to o/p dumpfile */ + for (i = 0; i < info->num_dumpfile; i++) { + if (!SPLITTING_SIZE_EI(i)) + continue; + + if (SPLITTING_SIZE_EI(i) > data_buf_size) { + data_buf_size = SPLITTING_SIZE_EI(i); + if ((data = realloc(data, data_buf_size)) == NULL) { + ERRMSG("Can't allcate memory for eraseinfo" + " data.\n"); + goto out; + } + } + if ((fd = open(SPLITTING_DUMPFILE(i), O_RDONLY)) < 0) { + ERRMSG("Can't open a file(%s). %s\n", + SPLITTING_DUMPFILE(i), strerror(errno)); + goto out; + } + if (lseek(fd, SPLITTING_OFFSET_EI(i), SEEK_SET) < 0) { + ERRMSG("Can't seek a file(%s). %s\n", + SPLITTING_DUMPFILE(i), strerror(errno)); + goto out; + } + if (read(fd, data, SPLITTING_SIZE_EI(i)) != + SPLITTING_SIZE_EI(i)) { + ERRMSG("Can't read a file(%s). %s\n", + SPLITTING_DUMPFILE(i), strerror(errno)); + goto out; + } + if (!write_cache(&cd_data, data, SPLITTING_SIZE_EI(i))) + goto out; + info->size_eraseinfo += SPLITTING_SIZE_EI(i); + + close(fd); + fd = 0; + } + if (!write_cache_bufsz(&cd_data)) + goto out; + + if (!update_sub_header()) + goto out; + print_progress(PROGRESS_COPY, num_dumpable, num_dumpable); ret = TRUE; out: diff -Nrup kexec-tools-2.0.0.orig/makedumpfile-1.3.5/makedumpfile.h kexec-tools-2.0.0/makedumpfile-1.3.5/makedumpfile.h --- kexec-tools-2.0.0.orig/makedumpfile-1.3.5/makedumpfile.h 2012-01-02 17:07:47.000000000 +0800 +++ kexec-tools-2.0.0/makedumpfile-1.3.5/makedumpfile.h 2012-01-02 17:08:39.000000000 +0800 @@ -437,6 +437,8 @@ do { \ #define SPLITTING_FD_BITMAP(i) info->splitting_info[i].fd_bitmap #define SPLITTING_START_PFN(i) info->splitting_info[i].start_pfn #define SPLITTING_END_PFN(i) info->splitting_info[i].end_pfn +#define SPLITTING_OFFSET_EI(i) info->splitting_info[i].offset_eraseinfo +#define SPLITTING_SIZE_EI(i) info->splitting_info[i].size_eraseinfo /* * kernel version @@ -502,6 +504,8 @@ do { \ #define XEN_ELFNOTE_CRASH_INFO (0x1000001) #define SIZE_XEN_CRASH_INFO_V2 (sizeof(unsigned long) * 10) +#define MAX_SIZE_STR_LEN (21) + /* * The value of dependence on machine */ @@ -809,6 +813,8 @@ struct splitting_info { int fd_bitmap; unsigned long long start_pfn; unsigned long long end_pfn; + off_t offset_eraseinfo; + unsigned long size_eraseinfo; } splitting_info_t; struct DumpInfo { @@ -873,6 +879,7 @@ struct DumpInfo { struct dump_bitmap *bitmap1; struct dump_bitmap *bitmap2; struct disk_dump_header *dump_header; + struct kdump_sub_header sub_header; /* * ELF header info: @@ -941,6 +948,12 @@ struct DumpInfo { unsigned long size_note; /* + * erased information in dump memory image info: + */ + off_t offset_eraseinfo; + unsigned long size_eraseinfo; + + /* * for Xen extraction */ off_t offset_xen_crash_info; @@ -1248,12 +1261,27 @@ struct dwarf_info { extern struct dwarf_info dwarf_info; /* + * Erase information, original symbol expressions. + */ +struct erase_info { + char *symbol_expr; + int num_sizes; + long *sizes; + int erased; /* 1= erased, 0= Not erased */ +}; + +/* * Filtering information */ struct filter_info { unsigned long long address; unsigned long long paddr; long size; + + /* direct access to update erase information node */ + int erase_info_idx; /* 0= invalid index */ + int size_idx; + struct filter_info *next; unsigned short nullify; }; @@ -1261,6 +1289,7 @@ struct filter_info { struct config_entry { char *name; char *type_name; + char *symbol_expr; /* original symbol expression */ unsigned short flag; unsigned short nullify; unsigned long long sym_addr; /* Symbol address */ @@ -1273,6 +1302,7 @@ struct config_entry { long index; long size; int line; /* Line number in config file. */ + int erase_info_idx; /* 0= invalid index */ struct config_entry *refer_to; struct config_entry *next; };