mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 00:32:38 +01:00 
			
		
		
		
	Replaced emubd with rambd and filebd
The idea behind emubd (file per block), was neat, but doesn't add much value over a block device that just operates on a single linear file (other than adding a significant amount of overhead). Initially it helped with debugging, but when the metadata format became more complex in v2, most debugging ends up going through the debug.py script anyways. Aside from being simpler, moving to filebd means it is also possible to mount disk images directly. Also introduced rambd, which keeps the disk contents in RAM. This is very useful for testing where it increases the speed _significantly_. - test_dirs w/ filebd - 0m7.170s - test_dirs w/ rambd - 0m0.966s These follow the emubd model of using the lfs_config for geometry. I'm not convinced this is the best approach, but it gets the job done. I've also added lfs_ramdb_createcfg to add additional config similar to lfs_file_opencfg. This is useful for specifying erase_value, which tells the block device to simulate erases similar to flash devices. Note that the default (-1) meets the minimum block device requirements and is the most performant.
This commit is contained in:
		
							
								
								
									
										101
									
								
								scripts/test_.py
									
									
									
									
									
								
							
							
						
						
									
										101
									
								
								scripts/test_.py
									
									
									
									
									
								
							| @@ -15,7 +15,6 @@ import subprocess as sp | ||||
| import base64 | ||||
| import sys | ||||
| import copy | ||||
| import shutil | ||||
| import shlex | ||||
|  | ||||
| TESTDIR = 'tests_' | ||||
| @@ -40,8 +39,10 @@ $(foreach target,$(SRC),$(eval $(FLATTEN))) | ||||
| GLOBALS = """ | ||||
| //////////////// AUTOGENERATED TEST //////////////// | ||||
| #include "lfs.h" | ||||
| #include "emubd/lfs_emubd.h" | ||||
| #include "filebd/lfs_filebd.h" | ||||
| #include "rambd/lfs_rambd.h" | ||||
| #include <stdio.h> | ||||
| const char *LFS_DISK = NULL; | ||||
| """ | ||||
| DEFINES = { | ||||
|     "LFS_READ_SIZE": 16, | ||||
| @@ -51,23 +52,25 @@ DEFINES = { | ||||
|     "LFS_BLOCK_CYCLES": 1024, | ||||
|     "LFS_CACHE_SIZE": "(64 % LFS_PROG_SIZE == 0 ? 64 : LFS_PROG_SIZE)", | ||||
|     "LFS_LOOKAHEAD_SIZE": 16, | ||||
|     "LFS_ERASE_VALUE": 0xff, | ||||
| } | ||||
| PROLOGUE = """ | ||||
|     // prologue | ||||
|     __attribute__((unused)) lfs_t lfs; | ||||
|     __attribute__((unused)) lfs_emubd_t bd; | ||||
|     __attribute__((unused)) lfs_filebd_t filebd; | ||||
|     __attribute__((unused)) lfs_rambd_t rambd; | ||||
|     __attribute__((unused)) lfs_file_t file; | ||||
|     __attribute__((unused)) lfs_dir_t dir; | ||||
|     __attribute__((unused)) struct lfs_info info; | ||||
|     __attribute__((unused)) uint8_t buffer[1024]; | ||||
|     __attribute__((unused)) char path[1024]; | ||||
|  | ||||
|      | ||||
|     __attribute__((unused)) const struct lfs_config cfg = { | ||||
|         .context = &bd, | ||||
|         .read  = &lfs_emubd_read, | ||||
|         .prog  = &lfs_emubd_prog, | ||||
|         .erase = &lfs_emubd_erase, | ||||
|         .sync  = &lfs_emubd_sync, | ||||
|         .context = LFS_DISK ? (void*)&filebd : (void*)&rambd, | ||||
|         .read  = LFS_DISK ? &lfs_filebd_read  : &lfs_rambd_read, | ||||
|         .prog  = LFS_DISK ? &lfs_filebd_prog  : &lfs_rambd_prog, | ||||
|         .erase = LFS_DISK ? &lfs_filebd_erase : &lfs_rambd_erase, | ||||
|         .sync  = LFS_DISK ? &lfs_filebd_sync  : &lfs_rambd_sync, | ||||
|  | ||||
|         .read_size      = LFS_READ_SIZE, | ||||
|         .prog_size      = LFS_PROG_SIZE, | ||||
| @@ -78,11 +81,26 @@ PROLOGUE = """ | ||||
|         .lookahead_size = LFS_LOOKAHEAD_SIZE, | ||||
|     }; | ||||
|  | ||||
|     lfs_emubd_create(&cfg, "blocks"); | ||||
|     __attribute__((unused)) const struct lfs_filebd_config filecfg = { | ||||
|         .erase_value = LFS_ERASE_VALUE, | ||||
|     }; | ||||
|     __attribute__((unused)) const struct lfs_rambd_config ramcfg = { | ||||
|         .erase_value = LFS_ERASE_VALUE, | ||||
|     }; | ||||
|  | ||||
|     if (LFS_DISK) { | ||||
|         lfs_filebd_createcfg(&cfg, LFS_DISK, &filecfg); | ||||
|     } else { | ||||
|         lfs_rambd_createcfg(&cfg, &ramcfg); | ||||
|     } | ||||
| """ | ||||
| EPILOGUE = """ | ||||
|     // epilogue | ||||
|     lfs_emubd_destroy(&cfg); | ||||
|     if (LFS_DISK) { | ||||
|         lfs_filebd_destroy(&cfg); | ||||
|     } else { | ||||
|         lfs_rambd_destroy(&cfg); | ||||
|     } | ||||
| """ | ||||
| PASS = '\033[32m✓\033[0m' | ||||
| FAIL = '\033[31m✗\033[0m' | ||||
| @@ -133,7 +151,8 @@ class TestCase: | ||||
|         f.write(') {\n') | ||||
|  | ||||
|         for k, v in sorted(self.defines.items()): | ||||
|             f.write(4*' '+'#define %s %s\n' % (k, v)) | ||||
|             if k not in self.suite.defines: | ||||
|                 f.write(4*' '+'#define %s %s\n' % (k, v)) | ||||
|  | ||||
|         f.write(PROLOGUE) | ||||
|         f.write('\n') | ||||
| @@ -148,27 +167,34 @@ class TestCase: | ||||
|         f.write('\n') | ||||
|  | ||||
|         for k, v in sorted(self.defines.items()): | ||||
|             f.write(4*' '+'#undef %s\n' % k) | ||||
|             if k not in self.suite.defines: | ||||
|                 f.write(4*' '+'#undef %s\n' % k) | ||||
|  | ||||
|         f.write('}\n') | ||||
|  | ||||
|     def test(self, exec=[], persist=False, gdb=False, failure=None, **args): | ||||
|         # clear disk first | ||||
|         if not persist: | ||||
|             shutil.rmtree('blocks', True) | ||||
|  | ||||
|         # build command | ||||
|         cmd = exec + ['./%s.test' % self.suite.path, | ||||
|             repr(self.caseno), repr(self.permno)] | ||||
|         if persist: | ||||
|             cmd.append(self.suite.path + '.test.disk') | ||||
|  | ||||
|         # failed? drop into debugger? | ||||
|         if gdb and failure: | ||||
|             cmd = (['gdb', '-ex', 'r' | ||||
|                 ] + (['-ex', 'up'] if failure.assert_ else []) + [ | ||||
|                 '--args'] + cmd) | ||||
|             ncmd = ['gdb'] | ||||
|             if gdb == 'assert': | ||||
|                 ncmd.extend(['-ex', 'r']) | ||||
|                 if failure.assert_: | ||||
|                     ncmd.extend(['-ex', 'up']) | ||||
|             elif gdb == 'start': | ||||
|                 ncmd.extend([ | ||||
|                     '-ex', 'b %s:%d' % (self.suite.path, self.lineno), | ||||
|                     '-ex', 'r']) | ||||
|             ncmd.extend(['--args'] + cmd) | ||||
|  | ||||
|             if args.get('verbose', False): | ||||
|                 print(' '.join(shlex.quote(c) for c in cmd)) | ||||
|             sys.exit(sp.call(cmd)) | ||||
|                 print(' '.join(shlex.quote(c) for c in ncmd)) | ||||
|             sys.exit(sp.call(ncmd)) | ||||
|  | ||||
|         # run test case! | ||||
|         stdout = [] | ||||
| @@ -231,22 +257,27 @@ class ReentrantTestCase(TestCase): | ||||
|         if not self.reentrant: | ||||
|             return | ||||
|  | ||||
|         for cycles in it.count(1): | ||||
|             npersist = persist or cycles > 1 | ||||
|         # clear disk first? | ||||
|         if not persist: | ||||
|             try: | ||||
|                 os.remove(self.suite.path + '.test.disk') | ||||
|             except FileNotFoundError: | ||||
|                 pass | ||||
|  | ||||
|         for cycles in it.count(1): | ||||
|             # exact cycle we should drop into debugger? | ||||
|             if gdb and failure and failure.cycleno == cycles: | ||||
|                 return super().test(exec=exec, persist=npersist, | ||||
|                 return super().test(exec=exec, persist=True, | ||||
|                     gdb=gdb, failure=failure, **args) | ||||
|  | ||||
|             # run tests, but kill the program after lfs_emubd_prog/erase has | ||||
|             # run tests, but kill the program after prog/erase has | ||||
|             # been hit n cycles. We exit with a special return code if the | ||||
|             # program has not finished, since this isn't a test failure. | ||||
|             nexec = exec + [ | ||||
|                 'gdb', '-batch-silent', | ||||
|                 '-ex', 'handle all nostop', | ||||
|                 '-ex', 'b lfs_emubd_prog', | ||||
|                 '-ex', 'b lfs_emubd_erase', | ||||
|                 '-ex', 'b lfs_filebd_prog', | ||||
|                 '-ex', 'b lfs_filebd_erase', | ||||
|                 '-ex', 'r', | ||||
|                 ] + cycles*['-ex', 'c'] + [ | ||||
|                 '-ex', 'q ' | ||||
| @@ -255,7 +286,7 @@ class ReentrantTestCase(TestCase): | ||||
|                     '33', | ||||
|                 '--args'] | ||||
|             try: | ||||
|                 return super().test(exec=nexec, persist=npersist, **args) | ||||
|                 return super().test(exec=nexec, persist=True, **args) | ||||
|             except TestFailure as nfailure: | ||||
|                 if nfailure.returncode == 33: | ||||
|                     continue | ||||
| @@ -370,10 +401,11 @@ class TestSuite: | ||||
|  | ||||
|         f.write('\n') | ||||
|         f.write('int main(int argc, char **argv) {\n') | ||||
|         f.write(4*' '+'int case_ = (argc == 3) ? atoi(argv[1]) : 0;\n') | ||||
|         f.write(4*' '+'int perm = (argc == 3) ? atoi(argv[2]) : 0;\n') | ||||
|         f.write(4*' '+'int case_ = (argc >= 3) ? atoi(argv[1]) : 0;\n') | ||||
|         f.write(4*' '+'int perm = (argc >= 3) ? atoi(argv[2]) : 0;\n') | ||||
|         f.write(4*' '+'LFS_DISK = (argc >= 4) ? argv[3] : NULL;\n') | ||||
|         for perm in self.perms: | ||||
|             f.write(4*' '+'if (argc != 3 || ' | ||||
|             f.write(4*' '+'if (argc < 3 || ' | ||||
|                 '(case_ == %d && perm == %d)) { ' % ( | ||||
|                     perm.caseno, perm.permno)) | ||||
|             f.write('test_case%d(' % perm.caseno) | ||||
| @@ -590,8 +622,9 @@ if __name__ == "__main__": | ||||
|         help="Run all tests instead of stopping on first error. Useful for CI.") | ||||
|     parser.add_argument('-p', '--persist', action='store_true', | ||||
|         help="Don't reset the tests disk before each test.") | ||||
|     parser.add_argument('-g', '--gdb', action='store_true', | ||||
|         help="Drop into gdb on failure.") | ||||
|     parser.add_argument('-g', '--gdb', choices=['init', 'start', 'assert'], | ||||
|         nargs='?', const='assert', | ||||
|         help="Drop into gdb on test failure.") | ||||
|     parser.add_argument('--valgrind', action='store_true', | ||||
|         help="Run non-leaky tests under valgrind to check for memory leaks.") | ||||
|     parser.add_argument('--reentrant', action='store_true', | ||||
|   | ||||
		Reference in New Issue
	
	Block a user