[Not-a-patch] Add flight recorder to MTDRAM

Dirk Behme dirk.behme at de.bosch.com
Wed Dec 6 01:06:28 PST 2017


From: Manfred Spraul <manfred at colorfullife.com>

Hi,

final part:
The tool to replay flight recorder output.

--
	Manfred

xxxxxxxx
/*
 * replay.cpp
 *
 * Copyright (C) 1999, 2001, 2005, 2008, 2013, 2015, 2017 by Manfred Spraul.
 *	All rights reserved except the rights granted by the GPL.
 *
 * Redistribution of this file is permitted under the terms of the GNU 
 * General Public License (GPL) version 2 or later.
 */

#define _DEFAULT_SOURCE
#include <endian.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <errno.h>
#include <limits.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <getopt.h>
#include <fcntl.h>

#define VERBOSE_DEBUG	3
#define VERBOSE_DETAILS	2
#define	VERBOSE_NORMAL	1
#define VERBOSE_OFF	0

///
/// \brief global setting to enable more verbose output
///
/// g_verbose is used to set the logging level.
/// - VERBOSE_OFF (0) means no logs
/// - VERBOSE_NORMAL (1) means some logs
/// - VERBOSE_DETAILS (2) means more details.
/// - VERBOSE_DEBUG (3) prints internal details, only relevant for debugging
///
int g_verbose = VERBOSE_OFF;

unsigned long g_erasesize = ULONG_MAX;
unsigned long g_writesize = ULONG_MAX;

void * map_file(const char *name, size_t *len, mode_t mode)
{
	int fd;
	struct stat s;
	void *addr;

	fd = open(name, mode);
	if (fd == -1) {
		printf("  Failed to open %s.\n", name);
		exit (2);
	}

	if (fstat(fd, &s) == -1) {
		printf("  Failed to stat %s.\n", name);
		exit (2);
	}
	*len = s.st_size;

	if (mode == O_RDONLY) {
		addr = mmap(NULL, s.st_size, PROT_READ, MAP_SHARED, fd, 0);
	} else {
		addr = mmap(NULL, s.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
	}
	if (addr == NULL) {
		printf("  Failed to mmap %s.\n", name);
		exit (2);
	}

	return addr;
}

////////////////////////////////////////
// binary interface


#define round_up(a, b)	((((a)+(b)-1)/(b))*(b))

#define MAGIC_START	0x12345678UL
#define MAGIC_END	0x87654321UL

#define FUNC_WRITE	1UL
#define FUNC_ERASE	2UL
#define FUNC_WRITE_CHK	3UL
#define FUNC_ERASE_CHK	4UL
#define CHECK_VAL1	7ULL
#define CHECK_VAL2	(2*76777ULL)
#define CHECK_VAL3	104677ULL

static u_int32_t read_u32(const void *log, size_t *pos)
{
	u_int32_t retval;
	u_int32_t *src = (u_int32_t*)(((char*)log)+*pos);

	retval = le32toh(*src);
	*pos += round_up(sizeof(u_int32_t), 8);

	if (g_verbose >= VERBOSE_DEBUG) {
		printf("read_u32: val %u, pos: %ld.\n", retval, *pos);
	}

	return retval;
}

static u_int64_t read_u64(const void *log, size_t *pos)
{
	u_int64_t retval;
	u_int64_t *src = (u_int64_t*)(((char*)log)+*pos);

	retval = le64toh(*src);
	*pos += round_up(sizeof(u_int64_t), 8);

	if (g_verbose >= VERBOSE_DEBUG) {
		printf("read_u64: val %lu, pos: %ld.\n", retval, *pos);
	}

	return retval;
}

static void read_blob(const void *log, size_t *pos, void *data, int len)
{
	void *src = (void *)(((char*)log)+*pos);

	memcpy(data, src, len);
	*pos += round_up(len, 8);

	if (g_verbose >= VERBOSE_DEBUG) {
		printf("read_blob:len %d pos: %ld.\n", len, *pos);
	}
}

static void skip_blob(const void *log, size_t *pos, void *data, int len)
{
	*pos += round_up(len, 8);

	if (g_verbose >= VERBOSE_DEBUG) {
		printf("skip_blob:len %d pos: %ld.\n", len, *pos);
	}
}

//////////////////////////////////////////////////////////////////////////////

static void show_help(void)
{
	printf("\n");
	printf(" Applies up to <replay_cnt> elements from file <log> to file <dump>.\n");
	printf(" Command line parameters:\n");
	printf(" <dump>: nand dump of initial image.\n");
	printf(" <log>: output from debugfs/mtdram.\n");
	printf(" [replaycnt]: number of entries from <log> to be applied.\n");
	printf("              If not specified, then all entries are applied.\n");
	printf("  -h: Print this help\n");
	printf("  -w x: Split writes larger than <x> into multiple operations.\n");
	printf("  -e x: Split erases larger than <x> into multiple operations.\n");
	printf("  -v: Increase verbose level. Specify multiple times to increase further.\n");
}

u_int64_t get_len(u_int64_t len, u_int64_t atomic, int maxops, int *pops)
{
	u_int64_t maxlen;
	int ops;

	ops = 0;
	maxlen = 0;
	do {
		ops++;
		maxlen += atomic;
		if (maxlen >= len) {
			maxlen = len;
			break;
		}
	} while (ops < maxops);

	if (g_verbose >= VERBOSE_DEBUG) {
		printf("getlen(len=%lxh, atomic=%lxh, maxops=%d) returns len=%lxh, ops=%d.\n",
				len, atomic, maxops, maxlen, ops);
	}

	*pops = ops;
	return maxlen;
}

//////////////////////////////////////////////////////////////////////////////

int main(int argc, char **argv)
{
	int count, errno, i;
	size_t dump_len, log_len, pos;
	char *dump, *log;
	char linebuf[4096];
	int opt;

	printf("replay [-h] [-v] [-e erasesize] [-w writesize] <dump> <log> [replay_cnt]\n");

	while ((opt = getopt(argc, argv, "vhe:w:")) != -1) {
		switch(opt) {
		case 'v':
			g_verbose++;
			break;
		case '?':
		case 'h':
			show_help();
			return 0;
		case 'e':
			g_erasesize = atoi(optarg);
			if (g_erasesize == 0) {
				printf("  Invalid erase size.\n");
				return 1;
			}
			break;
		case 'w':
			g_writesize = atoi(optarg);
			if (g_writesize == 0) {
				printf("  Invalid erase size.\n");
				return 1;
			}
			break;
		}
	}

	if (argc == optind+3) {
		count=atoi(argv[optind+2]);
		if (count <= 0) {
			printf("  Invalid replay_cnt: %s (%d).\n", argv[optind+3], count);
			return 1;
		}
	} else if (argc != optind+2) {
		printf("  Missing mandatory parameters.\n");
		show_help();
		return 1;
	} else {
		count = INT_MAX;
	}

	if (g_verbose >= VERBOSE_NORMAL) {
		printf(" atomic write size: %lx.\n", g_writesize);
		printf(" atomic erase size: %lx.\n", g_erasesize);
		printf(" max opcount: %d.\n", count);
	}

	dump = (char*)map_file(argv[optind+0], &dump_len, O_RDWR);
	log = (char*)map_file(argv[optind+1], &log_len, O_RDONLY);

	if (g_verbose >= VERBOSE_NORMAL) {
		printf("Mapped file %s (len:%ld) at %p.\n", argv[optind+0], dump_len, dump);
		printf("Mapped file %s (len:%ld) at %p.\n", argv[optind+1], log_len, log);
	}

	pos=0;
	linebuf[0]='\0';
	i = 0;
	for (;;) {
		u_int32_t val1;
		u_int64_t addr, len, chk, chk_actual;
		u_int64_t len_limited;
		const char *name;
		int maxops, ops;

		if (pos >=log_len)
			break;

		if (i >= count)
			break;

		maxops = count-i;

		if (g_verbose >= VERBOSE_DEBUG)
			printf("Entry %d, start at %lu.\n", i, pos);

		val1 = read_u32(log, &pos);
		if (val1 != MAGIC_START) {
			printf("Bad MAGIC_START: 0x%x.\n", val1);
			return 3;
		}
		val1 = read_u32(log, &pos);
		switch (val1) {
		case FUNC_ERASE:
		case FUNC_ERASE_CHK:
			addr=read_u64(log, &pos);
			len=read_u64(log, &pos);
			if (val1 == FUNC_ERASE_CHK) {
				chk=read_u64(log, &pos);
				chk_actual=CHECK_VAL1*len+CHECK_VAL2*addr;

				if (chk != chk_actual) {
					printf("Checksum error!: got 0x%lx expected 0x%lx.\n",
						chk, chk_actual);
				}
				name="FUNC_ERASE_CHK";
			} else {
				name="FUNC_ERASE";
			}
			len_limited = get_len(len, g_erasesize, maxops, &ops);
			memset(&dump[addr], 0xff, len_limited);
			if (len != len_limited) {
				sprintf(linebuf,"%d: %s(addr=0x%lx, len=0x%lx of total %lx).\n",
							i, name, addr, len_limited, len);
			} else {
				sprintf(linebuf,"%d: %s(addr=0x%lx, len=0x%lx).\n",
							i, name, addr, len_limited);
			}
			i += ops;
			break;

		case FUNC_WRITE:
		case FUNC_WRITE_CHK:
			addr=read_u64(log, &pos);
			len=read_u64(log, &pos);
			len_limited = get_len(len, g_writesize, maxops, &ops);
			if (len_limited != len) {
				read_blob(log, &pos, &dump[addr], len_limited);
				skip_blob(log, &pos, &dump[addr], len-len_limited);
			} else {
				read_blob(log, &pos, &dump[addr], len);
			}
			if (val1 == FUNC_WRITE_CHK) {
				chk=read_u64(log, &pos);
				if (len_limited == len) {
					u_int8_t *buf = (u_int8_t*) &dump[addr];
					u_int32_t j;

					chk_actual = CHECK_VAL1*addr+CHECK_VAL2*len;
					for (j=0;j<len;j++)
						chk_actual += (j+buf[j])*CHECK_VAL3;
					if (chk != chk_actual) {
						printf("Checksum error!: got 0x%lx expected 0x%lx.\n",
							chk, chk_actual);
					}
				}
				name = "FUNC_WRITE_CHK";
			} else {
				name = "FUNC_WRITE";
			}
			if (len_limited != len) {
				sprintf(linebuf,"%d: %s(addr=0x%lx, len=0x%lx of total %lx).\n",
							i, name, addr, len_limited, len);
			} else {
				sprintf(linebuf,"%d: %s(addr=0x%lx, len=0x%lx).\n",
							i, name, addr, len);
			}
			i += ops;
			break;
		default:
			printf("Unknown function: 0x%x.\n", val1);
			return 3;
		}
		val1 = read_u32(log, &pos);
		if (val1 != MAGIC_END) {
			printf("Bad MAGIC_END: 0x%x.\n", val1);
			return 3;
		}
		if (g_verbose >= VERBOSE_DETAILS)
			printf("%s", linebuf);
	}
	printf("Processed %d entries, consumed %lu bytes\n", i, pos);
	if (g_verbose >= VERBOSE_NORMAL)
		printf("Last entry was %s", linebuf);
	/* Note: The files are implicitely closed by ending the process. */
	return 0;
}



More information about the linux-mtd mailing list