/* * Copyright (c) International Business Machines Corp., 2006 * * 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 * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Authors: Drake Dowsett, dowsett@de.ibm.com * Contact: Andreas Arnez, arnez@de.ibm.com * * unubi uses the following functions to generate analysis output based on * the header information in a raw-UBI image */ /* * TODO: use OOB data to check for eraseblock validity in NAND images */ #include #include #include #include #include #include #include "eb_chain.h" #include "crc32.h" #define MAXPATH 1024 #define EC_X_INT 50 #define FN_STATS "analysis_stats.txt" #define FN_EH_DATA "analysis_ec_hdr.data" #define FN_EH_PLOT "analysis_ec_hdr.plot" #define FN_VH_DATA "analysis_vid_hdr.data" #define FN_VH_PLOT "analysis_vid_hdr.plot" /** * intcmp - function needed by qsort to order integers **/ int intcmp(const void *a, const void *b) { int A = *(int *)a; int B = *(int *)b; return A - B; } int longcmp(const void *a, const void *b) { long long A = *(long long *)a; long long B = *(long long *)b; return A - B; } /** * unubi_analyze_group_index - finds the normalized index in an array * item: look for this item in the array * array: array to search through * size: length of the array * array should be sorted for this algorithm to perform properly; * if the item is not found returns -1, otherwise return value is the * index in the array (note this contricts the array size to 2^32-1); **/ int norm_index(uint32_t item, uint32_t *array, size_t length) { size_t i, index; for (index = 0, i = 0; i < length; i++) { if ((i != 0) && (array[i] != array[i - 1])) index++; if (item == array[i]) return index; } return -1; } /** * unubi_analyze_ec_hdr - generate data table and plot script * first: head of simple linked list * path: folder to write into * generates a data file containing the eraseblock index in the image * and the erase counter found in its ec header; * if the crc check fails, the line is commented out in the data file; * also generates a simple gnuplot sript for quickly viewing one * display of the data file; **/ int unubi_analyze_ec_hdr(eb_info_t first, const char *path) { char filename[MAXPATH + 1]; size_t count, eraseblocks; uint32_t crc, crc32_table[256]; uint64_t *erase_counts; FILE* fpdata; FILE* fpplot; eb_info_t cur; /* crc check still needed for `first' linked list */ init_crc32_table(crc32_table); /* prepare output files */ memset(filename, 0, MAXPATH + 1); snprintf(filename, MAXPATH, "%s/%s", path, FN_EH_DATA); fpdata = fopen(filename, "w"); if (fpdata == NULL) return -1; memset(filename, 0, MAXPATH + 1); snprintf(filename, MAXPATH, "%s/%s", path, FN_EH_PLOT); fpplot = fopen(filename, "w"); if (fpplot == NULL) return -1; chmod(filename, 0755); /* first run: count elements */ count = 0; cur = first; while (cur != NULL) { cur = cur->next; count++; } eraseblocks = count; erase_counts = malloc(eraseblocks * sizeof(*erase_counts)); memset(erase_counts, 0, eraseblocks * sizeof(*erase_counts)); /* second run: populate array to sort */ count = 0; cur = first; while(cur != NULL) { erase_counts[count] = ubi64_to_cpu(cur->outer.ec); cur = cur->next; count++; } qsort(erase_counts, eraseblocks, sizeof(*erase_counts), (void *)longcmp); /* third run: generate data file */ count = 0; cur = first; fprintf(fpdata, "# eraseblock_no actual_erase_count " "sorted_erase_count\n"); while (cur != NULL) { crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &cur->outer, UBI_EC_HDR_SIZE_CRC); if ((ubi32_to_cpu(cur->outer.magic) != UBI_EC_HDR_MAGIC) || (crc != ubi32_to_cpu(cur->outer.hdr_crc))) fprintf(fpdata, "# "); fprintf(fpdata, "%zu %llu %llu", count, (unsigned long long) ubi64_to_cpu(cur->outer.ec), (unsigned long long) erase_counts[count]); if (ubi32_to_cpu(cur->outer.magic) != UBI_EC_HDR_MAGIC) fprintf(fpdata, " ## bad magic: %08x", ubi32_to_cpu(cur->outer.magic)); if (crc != ubi32_to_cpu(cur->outer.hdr_crc)) fprintf(fpdata, " ## CRC mismatch: given=%08x, " "calc=%08x", ubi32_to_cpu(cur->outer.hdr_crc), crc); fprintf(fpdata, "\n"); cur = cur->next; count++; } fclose(fpdata); fprintf(fpplot, "#!/usr/bin/gnuplot -persist\n"); fprintf(fpplot, "set xlabel \"eraseblock\"\n"); /* fourth run: generate plot file xtics */ count = 0; cur = first; fprintf(fpplot, "set xtics ("); while (cur != NULL) { if ((count % EC_X_INT) == 0) { if (count > 0) fprintf(fpplot, ", "); fprintf(fpplot, "%zd", count); } cur = cur->next; count++; } fprintf(fpplot, ")\n"); fprintf(fpplot, "set ylabel \"erase count\"\n"); fprintf(fpplot, "set xrange [-1:%zu]\n", eraseblocks + 1); fprintf(fpplot, "# set yrange [-1:%llu]\n", (unsigned long long) erase_counts[eraseblocks - 1] + 1); fprintf(fpplot, "plot \"%s\" u 1:2 t \"unsorted: %s\" with boxes\n", FN_EH_DATA, FN_EH_DATA); fprintf(fpplot, "# replot \"%s\" u 1:3 t \"sorted: %s\" with lines\n", FN_EH_DATA, FN_EH_DATA); fprintf(fpplot, "pause -1 \"press ENTER\"\n"); fclose(fpplot); return 0; } /** * unubi_analyze_vid_hdr - generate data table and plot script * head: head of complex linked list (eb_chain) * path: folder to write into * generates a data file containing the volume id, logical number, leb version, * and data size from the vid header; * all eraseblocks listed in the eb_chain are valid (checked in unubi); * also generates a simple gnuplot sript for quickly viewing one * display of the data file; **/ int unubi_analyze_vid_hdr(eb_info_t *head, const char *path) { char filename[MAXPATH + 1]; int y1, y2; size_t count, step, breadth; uint32_t *leb_versions, *data_sizes; FILE* fpdata; FILE* fpplot; eb_info_t cur; /* prepare output files */ memset(filename, 0, MAXPATH + 1); snprintf(filename, MAXPATH, "%s/%s", path, FN_VH_DATA); fpdata = fopen(filename, "w"); if (fpdata == NULL) return -1; memset(filename, 0, MAXPATH + 1); snprintf(filename, MAXPATH, "%s/%s", path, FN_VH_PLOT); fpplot = fopen(filename, "w"); if (fpplot == NULL) return -1; chmod(filename, 0755); /* first run: count elements */ count = 0; cur = *head; while (cur != NULL) { cur = cur->next; count++; } breadth = count; leb_versions = malloc(breadth * sizeof(*leb_versions)); memset(leb_versions, 0, breadth * sizeof(*leb_versions)); data_sizes = malloc(breadth * sizeof(*data_sizes)); memset(data_sizes, 0, breadth * sizeof(*data_sizes)); /* second run: populate arrays to sort */ count = 0; cur = *head; while (cur != NULL) { leb_versions[count] = ubi32_to_cpu(cur->inner.leb_ver); data_sizes[count] = ubi32_to_cpu(cur->inner.data_size); cur = cur->next; count++; } qsort(leb_versions, breadth, sizeof(*leb_versions), (void *)intcmp); qsort(data_sizes, breadth, sizeof(*data_sizes), (void *)intcmp); /* third run: generate data file */ count = 0; cur = *head; fprintf(fpdata, "# x_axis vol_id lnum y1_axis leb_ver " "y2_axis data_size\n"); while (cur != NULL) { y1 = norm_index(ubi32_to_cpu(cur->inner.leb_ver), leb_versions, breadth); y2 = norm_index(ubi32_to_cpu(cur->inner.data_size), data_sizes, breadth); if ((y1 == -1) || (y2 == -1)) return -1; fprintf(fpdata, "%zu %u %u %u %u %u %u\n", count, ubi32_to_cpu(cur->inner.vol_id), ubi32_to_cpu(cur->inner.lnum), y1, ubi32_to_cpu(cur->inner.leb_ver), y2, ubi32_to_cpu(cur->inner.data_size)); cur = cur->next; count++; } fclose(fpdata); fprintf(fpplot, "#!/usr/bin/gnuplot -persist\n"); fprintf(fpplot, "set xlabel \"volume\"\n"); /* fourth run: generate plot file xtics */ count = 0; step = 0; cur = *head; fprintf(fpplot, "set xtics ("); while (cur != NULL) { if (count > 0) fprintf(fpplot, ", "); if (step != ubi32_to_cpu(cur->inner.vol_id)) { step = ubi32_to_cpu(cur->inner.vol_id); fprintf(fpplot, "\"%zd\" %zd 0", step, count); } else fprintf(fpplot, "\"%d\" %zd 1", ubi32_to_cpu(cur->inner.lnum), count); cur = cur->next; count++; } fprintf(fpplot, ")\n"); fprintf(fpplot, "set nox2tics\n"); /* fifth run: generate plot file ytics */ count = 0; cur = *head; fprintf(fpplot, "set ylabel \"leb version\"\n"); fprintf(fpplot, "set ytics ("); while (cur != NULL) { y1 = norm_index(ubi32_to_cpu(cur->inner.leb_ver), leb_versions, breadth); if (y1 == -1) return -1; if (count > 0) fprintf(fpplot, ", "); fprintf(fpplot, "\"%u\" %u", ubi32_to_cpu(cur->inner.leb_ver), y1); cur = cur->next; count++; } fprintf(fpplot, ")\n"); /* sixth run: generate plot file y2tics */ count = 0; cur = *head; fprintf(fpplot, "set y2label \"data size\"\n"); fprintf(fpplot, "set y2tics ("); while (cur != NULL) { y2 = norm_index(ubi32_to_cpu(cur->inner.data_size), data_sizes, breadth); if (y2 == -1) return -1; if (count > 0) fprintf(fpplot, ", "); fprintf(fpplot, "\"%u\" %u", ubi32_to_cpu(cur->inner.data_size), y2); cur = cur->next; count++; } fprintf(fpplot, ")\n"); y1 = norm_index(leb_versions[breadth - 1], leb_versions, breadth); y2 = norm_index(data_sizes[breadth - 1], data_sizes, breadth); fprintf(fpplot, "set xrange [-1:%zu]\n", count + 1); fprintf(fpplot, "set yrange [-1:%u]\n", y1 + 1); fprintf(fpplot, "set y2range [-1:%u]\n", y2 + 1); fprintf(fpplot, "plot \"%s\" u 1:4 t \"leb version: %s\" " "axes x1y1 with lp\n", FN_VH_DATA, FN_VH_DATA); fprintf(fpplot, "replot \"%s\" u 1:6 t \"data size: %s\" " "axes x1y2 with lp\n", FN_VH_DATA, FN_VH_DATA); fprintf(fpplot, "pause -1 \"press ENTER\"\n"); fclose(fpplot); free(data_sizes); free(leb_versions); return 0; } /** * unubi_analyze - run all analyses * head: eb_chain head * first: simple linked list of eraseblock headers (use .next) * path: directory (without trailing slash) to output to * returns 0 upon successful completion, or -1 otherwise **/ int unubi_analyze(eb_info_t *head, eb_info_t first, const char *path) { int rc; if (path == NULL) return -1; if (first == NULL) return -1; if ((head == NULL) || (*head == NULL)) return -1; rc = unubi_analyze_ec_hdr(first, path); if (rc < 0) return -1; rc = unubi_analyze_vid_hdr(head, path); if (rc < 0) return -1; return 0; }