summaryrefslogtreecommitdiff
path: root/ubi-utils/src/unubi_analyze.c
diff options
context:
space:
mode:
Diffstat (limited to 'ubi-utils/src/unubi_analyze.c')
-rw-r--r--ubi-utils/src/unubi_analyze.c435
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..6009fc0
--- /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, "%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;
+}