diff options
author | Drake Dowsett <dowsett@de.ibm.com> | 2006-11-06 16:54:10 +0100 |
---|---|---|
committer | Frank Haverkamp <haver@vnet.ibm.com> | 2006-11-06 16:54:10 +0100 |
commit | e820054fd876faa7306d64957c71c9c5c29db78c (patch) | |
tree | 16bca5adbad6cddf2390b44b2fb14f084ca9a557 /ubi-utils/src/unubi_analyze.c | |
parent | 31b015fc08a13a5b63245808f7d1a49eadd8193a (diff) |
[MTD] UBI: rework of off-line UBI analysis tool
The new version can create a gnuplot graph of the erase count statistics.
It can also extract UBI volumes and single blocks with a preanalysis of
the EC as well as the VID header. It has a manual page too ;-).
Signed-off-by: Frank Haverkamp <haver@vnet.ibm.com>
Diffstat (limited to 'ubi-utils/src/unubi_analyze.c')
-rw-r--r-- | ubi-utils/src/unubi_analyze.c | 435 |
1 files changed, 435 insertions, 0 deletions
diff --git a/ubi-utils/src/unubi_analyze.c b/ubi-utils/src/unubi_analyze.c new file mode 100644 index 0000000..2e94ca9 --- /dev/null +++ b/ubi-utils/src/unubi_analyze.c @@ -0,0 +1,435 @@ +/* + * 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 <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> + +#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, "%u %llu %llu", count, + ubi64_to_cpu(cur->outer.ec), + 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, "%d", count); + } + + cur = cur->next; + count++; + } + fprintf(fpplot, ")\n"); + + fprintf(fpplot, "set ylabel \"erase count\"\n"); + fprintf(fpplot, "set xrange [-1:%u]\n", eraseblocks + 1); + fprintf(fpplot, "# set yrange [-1:%llu]\n", + 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, "%u %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, "\"%d\" %d 0", step, count); + } + else + fprintf(fpplot, "\"%d\" %d 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:%u]\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; +} |