[PATCH] erofs-utils: stress: add support for dumping inconsistent data

Gao Xiang hsiangkao at linux.alibaba.com
Sun Mar 16 19:45:33 AEDT 2025


... dump inconsistent data for further analysis during stress tests.

Signed-off-by: Gao Xiang <hsiangkao at linux.alibaba.com>
---
 contrib/stress.c | 65 +++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 59 insertions(+), 6 deletions(-)

diff --git a/contrib/stress.c b/contrib/stress.c
index eedd9b9..27d5133 100644
--- a/contrib/stress.c
+++ b/contrib/stress.c
@@ -256,6 +256,7 @@ static struct fent *getfent(int which, int r)
 }
 
 static int testdir_fd = -1, chkdir_fd = -1;
+static char *dumpfile;
 
 static int __getdents_f(unsigned int sn, struct fent *fe)
 {
@@ -288,6 +289,47 @@ static int getdents_f(int op, unsigned int sn)
 	return __getdents_f(sn, fe);
 }
 
+static void baddump(unsigned int sn, char *buf1, char *buf2, unsigned int sz)
+{
+	int fd, err, i;
+	char *fn = dumpfile;
+
+	if (!fn)
+		return;
+	for (i = 0;;) {
+		fd = open(fn, O_CREAT | O_EXCL | O_WRONLY, 0644);
+		if (fd >= 0) {
+			printf("%d[%u]/%u: dump inconsistent data to \"%s\"\n",
+			       getpid(), procid, sn, fn);
+			if (fn != dumpfile)
+				free(fn);
+			break;
+		}
+		if (fd < 0 && errno != EEXIST) {
+			fprintf(stderr, "%d[%u]/%u: failed to create dumpfile %s\n",
+				getpid(), procid, sn, fn);
+			if (fn != dumpfile)
+				free(fn);
+			return;
+		}
+		if (fn != dumpfile)
+			free(fn);
+		err = asprintf(&fn, "%s.%d", dumpfile, ++i);
+		if (err < 0) {
+			fprintf(stderr, "%d[%u]/%u: failed to allocate filename\n",
+				getpid(), procid, sn);
+			return;
+		}
+	}
+	if (write(fd, buf1, sz) != sz)
+		fprintf(stderr, "%d[%u]/%u: failed to write buffer1 @ %u",
+			getpid(), procid, sn, sz);
+	if (write(fd, buf2, sz) != sz)
+		fprintf(stderr, "%d[%u]/%u: failed to write buffer2 @ %u",
+			getpid(), procid, sn, sz);
+	close(fd);
+}
+
 static int readlink_f(int op, unsigned int sn)
 {
 	char buf1[PATH_MAX], buf2[PATH_MAX];
@@ -317,6 +359,7 @@ static int readlink_f(int op, unsigned int sn)
 		if (memcmp(buf1, buf2, sz)) {
 			fprintf(stderr, "%d[%u]/%u %s: symlink mismatch @%s\n",
 				getpid(), procid, sn, __func__, fe->subpath);
+			baddump(sn, buf1, buf2, sz);
 			return -EBADMSG;
 		}
 	}
@@ -382,8 +425,8 @@ static int __read_f(unsigned int sn, struct fent *fe, uint64_t filesize)
 		trimmed = len <= filesize - off ? len : filesize - off;
 	}
 
-	printf("%d[%u]/%u read_f: %llu bytes @ %llu\n", getpid(), procid, sn,
-	       len | 0ULL, off | 0ULL);
+	printf("%d[%u]/%u read_f: %llu bytes @ %llu of %s\n", getpid(), procid,
+	       sn, len | 0ULL, off | 0ULL, fe->subpath);
 	nread = pread64(fe->fd, buf, len, off);
 	if (nread != trimmed) {
 		fprintf(stderr, "%d[%u]/%u read_f: failed to read %llu bytes @ %llu of %s\n",
@@ -414,6 +457,7 @@ static int __read_f(unsigned int sn, struct fent *fe, uint64_t filesize)
 		fprintf(stderr, "%d[%u]/%u read_f: data mismatch %llu bytes @ %llu of %s\n",
 			getpid(), procid, sn, len | 0ULL, off | 0ULL,
 			fe->subpath);
+		baddump(sn, buf, chkbuf, nread);
 		return -EBADMSG;
 	}
 	return 0;
@@ -481,6 +525,7 @@ static int __doscan_f(unsigned int sn, const char *op, struct fent *fe,
 			fprintf(stderr, "%d[%u]/%u %s: %llu bytes mismatch @ %llu of %s\n",
 				getpid(), procid, sn, op, chunksize | 0ULL,
 				pos | 0ULL, fe->subpath);
+			baddump(sn, buf, chkbuf, nread);
 			return -EBADMSG;
 		}
 	}
@@ -589,7 +634,7 @@ static int parse_options(int argc, char *argv[])
 	char *testdir, *chkdir;
 	int opt;
 
-	while ((opt = getopt(argc, argv, "l:p:s:")) != -1) {
+	while ((opt = getopt(argc, argv, "d:l:p:s:")) != -1) {
 		switch (opt) {
 		case 'l':
 			loops = atoi(optarg);
@@ -614,6 +659,13 @@ static int parse_options(int argc, char *argv[])
 				return -EINVAL;
 			}
 			break;
+		case 'd':
+			if (!*optarg) {
+				fprintf(stderr, "invalid dump file\n");
+				return -EINVAL;
+			}
+			dumpfile = optarg;
+			break;
 		default: /* '?' */
 			return -EINVAL;
 		}
@@ -650,9 +702,10 @@ static void usage(void)
 	fputs("usage: [options] TESTDIR [COMPRDIR]\n\n"
 	      "Stress test for EROFS filesystem, where TESTDIR is the directory to test and\n"
 	      "COMPRDIR (optional) serves as a directory for data comparison.\n"
-	      " -l#     Number of times each worker should loop (0 for infinite, default: 1)\n"
-	      " -p#     Number of parallel worker processes (default: 1)\n"
-	      " -s#     Seed for random generator (default: random)\n",
+	      " -l#             Number of times each worker should loop (0 for infinite, default: 1)\n"
+	      " -p#             Number of parallel worker processes (default: 1)\n"
+	      " -s#             Seed for random generator (default: random)\n"
+	      " -d<file>        Specify a dumpfile for the inconsistent data\n",
 	      stderr);
 }
 
-- 
2.43.5



More information about the Linux-erofs mailing list