├── Makefile ├── .gitignore ├── size_unit.h ├── fops.h ├── crc16.h ├── log.h ├── rwcheck.h ├── size_unit.c ├── config.h ├── log.c ├── sys_info.h ├── fops.c ├── crc16.c ├── README.zh.md ├── sys_info.c ├── README.md ├── LICENSE └── rwcheck.c /Makefile: -------------------------------------------------------------------------------- 1 | CC ?= gcc 2 | CFLAGS ?= -Wall -O 3 | TARGET := rwcheck 4 | OBJS := rwcheck.o crc16.o fops.o log.o size_unit.o sys_info.o 5 | LDFLAGS += -lpthread -rdynamic 6 | 7 | .PHONY: all 8 | all : $(TARGET) 9 | 10 | $(TARGET): $(OBJS) 11 | $(CC) -o $@ $^ $(LDFLAGS) 12 | 13 | %.o:%.c 14 | $(CC) -o $@ -c $< $(CFLAGS) 15 | 16 | %.d:%.c 17 | @set -e; rm -f $@; $(CC) -MM $< > .$@.$$$$; \ 18 | sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < .$@.$$$$ > $@; \ 19 | rm -f .$@.$$$$ 20 | 21 | -include $(OBJS:.o=.d) 22 | 23 | .PHONY: clean 24 | clean: 25 | rm -f $(TARGET) *.o *.d *.d.* 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | rwcheck 39 | 40 | # Debug files 41 | *.dSYM/ 42 | *.su 43 | *.idb 44 | *.pdb 45 | 46 | # Kernel Module Compile Results 47 | *.mod* 48 | *.cmd 49 | .tmp_versions/ 50 | modules.order 51 | Module.symvers 52 | Mkfile.old 53 | dkms.conf 54 | -------------------------------------------------------------------------------- /size_unit.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef __SIZE_UNIT__ 16 | #define __SIZE_UNIT__ 17 | 18 | struct size_unit { 19 | unsigned long long bytes; 20 | char str[128]; 21 | }; 22 | 23 | int str_to_size(struct size_unit *unit, const char *str); 24 | int num_to_size(struct size_unit *unit, unsigned long long num); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /fops.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef __FOPS_H__ 16 | #define __FOPS_H__ 17 | 18 | #include 19 | #include 20 | 21 | bool is_existed(const char *path); 22 | bool is_dir(const char *path); 23 | bool is_file(const char *path); 24 | int copy_file(const char *in_path, const char *out_path, unsigned long long bytes, 25 | unsigned long long buf_bytes); 26 | int append_crc(const char *path); 27 | char *abs_path(const char *path); 28 | ssize_t file_size(int fd); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /crc16.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef __CRC16_H 16 | #define __CRC16_H 17 | 18 | #include 19 | #include 20 | 21 | #define CRC16_MAX_BYTES ((unsigned long long)-1) 22 | int get_crc16_by_buf(unsigned short *crc16, const char *buf, unsigned long long bytes); 23 | int get_crc16_by_fd(unsigned short *crc16, int fd, unsigned long long bytes); 24 | int get_crc16_by_path(unsigned short *crc16, const char *path, unsigned long long bytes); 25 | bool check_crc16(unsigned short crc); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /log.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef __LOG_H_ 16 | #define __LOG_H_ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | /* enable this macro if you really need, to show all debug log */ 23 | #define DEBUG_LOG 0 24 | 25 | void delay_errlog(int err_no, const char *file, unsigned int line, 26 | const char *func_name, const char *fmt, ...); 27 | void show_errlog(void); 28 | 29 | #define INFO(fmt, ...) \ 30 | do { \ 31 | fprintf(stdout, fmt, ## __VA_ARGS__); \ 32 | fflush(stdout); \ 33 | } while (0) 34 | 35 | #define ERROR(fmt, ...) \ 36 | do { \ 37 | delay_errlog(errno, __FILE__, __LINE__, __func__, \ 38 | fmt, ## __VA_ARGS__); \ 39 | } while (0) 40 | 41 | #if DEBUG_LOG 42 | 43 | #define DEBUG(fmt, ...) \ 44 | do { \ 45 | delay_errlog(errno, __FILE__, __LINE__, __func__, \ 46 | fmt, ## __VA_ARGS__); \ 47 | show_errlog(); \ 48 | } while (0) 49 | 50 | #else /* DEBUG_LOG */ 51 | 52 | #define DEBUG(fmt, ...) 53 | 54 | #endif /* DEBUG_LOG */ 55 | #endif /* __LOG_H_ */ 56 | -------------------------------------------------------------------------------- /rwcheck.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef __RWCHECK_H__ 16 | #define __RWCHECK_H__ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include "log.h" 23 | #include "sys_info.h" 24 | #include "size_unit.h" 25 | #include "fops.h" 26 | #include "crc16.h" 27 | #include "config.h" 28 | 29 | #define VERSION "v0.1.0" 30 | #define COMPILE "Compiled in " __DATE__ " at " __TIME__ 31 | 32 | /* operation macro */ 33 | #define min(x, y) ({ \ 34 | typeof(x) min1 = (x); \ 35 | typeof(y) min2 = (y); \ 36 | (void) (&min1 == &min2); \ 37 | min1 < min2 ? min1 : min2; }) 38 | #define max(x, y) ({ \ 39 | typeof(x) max1 = (x); \ 40 | typeof(y) max2 = (y); \ 41 | (void) (&max1 == &max2); \ 42 | max1 < max2 ? max2 : max1; }) 43 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) 44 | #define ALIGN(x, a) __ALIGN((x), (a)) 45 | #define ALIGN_DOWN(x, a) __ALIGN((x) - ((a) - 1), (a)) 46 | #define __ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a) - 1) 47 | #define __ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask)) 48 | #define __round_mask(x, y) ((__typeof__(x))((y)-1)) 49 | #define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1) 50 | #define round_down(x, y) ((x) & ~__round_mask(x, y)) 51 | 52 | #endif /* __RWCHECK_H__ */ 53 | -------------------------------------------------------------------------------- /size_unit.c: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include "rwcheck.h" 20 | 21 | void num_to_str(char *str, int len, unsigned long long bytes) 22 | { 23 | if (bytes >= GB) 24 | snprintf(str, len, "%llu GB", bytes / GB); 25 | else if (bytes >= MB) 26 | snprintf(str, len, "%llu MB", bytes / MB); 27 | else if (bytes >= KB) 28 | snprintf(str, len, "%llu KB", bytes / KB); 29 | else 30 | snprintf(str, len, "%llu B", bytes); 31 | } 32 | 33 | int str_to_size(struct size_unit *unit, const char *str) 34 | { 35 | unsigned long long size; 36 | char c; 37 | 38 | c = str[strlen(str) - 1]; 39 | size = atol(str); 40 | if (size == 0) { 41 | ERROR("invalid size %s\n", str); 42 | return -EINVAL; 43 | } 44 | 45 | switch (c) { 46 | case '0'...'9': unit->bytes = size; break; 47 | case 'k': 48 | case 'K': unit->bytes = size * KB; break; 49 | case 'm': 50 | case 'M': unit->bytes = size * MB; break; 51 | case 'g': 52 | case 'G': unit->bytes = size * GB; break; 53 | default: 54 | return -EINVAL; 55 | } 56 | 57 | num_to_str(unit->str, 128, unit->bytes); 58 | return 0; 59 | } 60 | 61 | int num_to_size(struct size_unit *unit, unsigned long long num) 62 | { 63 | unit->bytes = num; 64 | num_to_str(unit->str, 128, unit->bytes); 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef __CONFIG_H_ 16 | #define __CONFIG_H_ 17 | 18 | /* macro for size */ 19 | #define KB ((unsigned long long)1024) 20 | #define MB ( KB * 1024 ) 21 | #define GB ( MB * 1024 ) 22 | 23 | /* dump stack when error */ 24 | #define DUMP_TRACE 0 25 | /* default orgin file name */ 26 | #define DEFAULT_ORGIN_FILE "rwcheck.org" 27 | /* default orgin file size */ 28 | #define DEFAULT_ORG_SIZE (64 * KB) 29 | /* default read/write file base name */ 30 | #define DEFAULT_OUTPUT "rwcheck.tmp" 31 | /* default percent of total size of flash to test */ 32 | #define DEFAULT_PERCENT 95 33 | /* default check times */ 34 | #define DEFAULT_TIMES 1 35 | /* default buffer size for each read/write for paltfrom with little ddr */ 36 | #define DEFAULT_SMALL_BUF_SIZE (512 * KB) 37 | /* default buffer size for each read/write for paltfrom with large ddr */ 38 | #define DEFAULT_LARGE_BUF_SIZE (8 * MB) 39 | /* default boundary for little/large ddr */ 40 | #define DEFAULT_BUF_SIZE_DDR_LIMIT (64 * MB) 41 | /* default begin size for up mode */ 42 | #define DEFAULT_UP_MODE_BEGIN_SIZE (4 * KB) 43 | /* default end size for up mode */ 44 | #define DEFAULT_UP_MODE_END_SIZE (16 * GB) 45 | /* default multiplying power for up mode */ 46 | #define DEFAULT_UP_MODE_POWER (2) 47 | /* default max size for auto mode */ 48 | #define DEFAULT_AUTO_MODE_MAX_SIZE (4 * GB) 49 | /* default end size for auto mode */ 50 | #define DEFAULT_AUTO_MODE_END_SIZE (64 * KB) 51 | /* default worker when do write testing */ 52 | #define DEFAULT_JOBS 1 53 | /* max worker when do write testing */ 54 | #define MAX_JOBS 10 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /log.c: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "rwcheck.h" 23 | 24 | #if DUMP_TRACE 25 | #include 26 | #endif 27 | 28 | #define MAX_ERRLOG_LEN 1024 29 | #define DUMP_TRACE_BUF_SIZE 1024 30 | static struct errlog { 31 | int _errno; 32 | unsigned int line; 33 | const char *file; 34 | const char *func; 35 | char log[MAX_ERRLOG_LEN]; 36 | struct errlog *next; 37 | #if DUMP_TRACE 38 | void *trace[DUMP_TRACE_BUF_SIZE]; 39 | int trace_cnt; 40 | #endif 41 | } *errlog; 42 | 43 | void delay_errlog(int err_no, const char *file, unsigned int line, 44 | const char *func_name, const char *fmt, ...) 45 | { 46 | va_list ap; 47 | struct errlog *err; 48 | 49 | err = malloc(sizeof(*err)); 50 | if (!err) { 51 | printf("malloc for errlog failed\n"); 52 | return; 53 | } 54 | memset(err, 0, sizeof(*err)); 55 | 56 | err->_errno = err_no; 57 | err->file = file; 58 | err->line = line; 59 | err->func = func_name; 60 | 61 | va_start(ap, fmt); 62 | vsnprintf(err->log, MAX_ERRLOG_LEN, fmt, ap); 63 | va_end(ap); 64 | 65 | if (errlog) { 66 | struct errlog *next = errlog, *pre; 67 | while (next) { 68 | pre = next; 69 | next = errlog->next; 70 | } 71 | pre->next = err; 72 | } else { 73 | errlog = err; 74 | } 75 | 76 | #if DUMP_TRACE 77 | err->trace_cnt = backtrace(err->trace, DUMP_TRACE_BUF_SIZE); 78 | #endif 79 | } 80 | 81 | void show_errlog(void) 82 | { 83 | struct errlog *err; 84 | #if DUMP_TRACE 85 | char **str; 86 | int i; 87 | #endif 88 | 89 | while ((err = errlog)) { 90 | fprintf(stderr, "\n\t--- ERROR INFO ---\n"); 91 | fprintf(stderr, "\tfile: %s (%d)\n", err->file, err->line); 92 | fprintf(stderr, "\terrno: %s (%d)\n", strerror(err->_errno), 93 | err->_errno); 94 | fprintf(stderr, "\tinfo: %s", err->log); 95 | if (err->log[strlen(err->log) - 1] != '\n') 96 | fprintf(stderr, "\n"); 97 | 98 | #if DUMP_TRACE 99 | fprintf(stderr, "\tstack:\n"); 100 | str = backtrace_symbols(err->trace, err->trace_cnt); 101 | for (i = 0; i < err->trace_cnt; i++) 102 | fprintf(stderr, "\t | %s\n", str[i]); 103 | free(str); 104 | #endif 105 | errlog = errlog->next; 106 | free(err); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /sys_info.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef __SYS_H__ 16 | #define __SYS_H__ 17 | 18 | /* the follow Filesystem types are from 'man 2 statfs' */ 19 | #define FS_ADFS_MAGIC 0xadf5 20 | #define FS_AFFS_MAGIC 0xADFF 21 | #define FS_BEFS_MAGIC 0x42465331 22 | #define FS_BFS_MAGIC 0x1BADFACE 23 | #define FS_CIFS_MAGIC 0xFF534D42 24 | #define FS_CODA_MAGIC 0x73757245 25 | #define FS_COH_MAGIC 0x012FF7B7 26 | #define FS_CRAMFS_MAGIC 0x28cd3d45 27 | #define FS_DEVFS_MAGIC 0x1373 28 | #define FS_EFS_MAGIC 0x00414A53 29 | #define FS_EXT_MAGIC 0x137D 30 | #define FS_EXT2_OLD_MAGIC 0xEF51 31 | #define FS_EXT2_MAGIC 0xEF53 32 | #define FS_EXT3_MAGIC 0xEF53 33 | #define FS_EXT4_MAGIC 0xEF53 34 | #define FS_HFS_MAGIC 0x4244 35 | #define FS_HPFS_MAGIC 0xF995E849 36 | #define FS_HUGETLBFS_MAGIC 0x958458f6 37 | #define FS_ISOFS_MAGIC 0x9660 38 | #define FS_JFFS2_MAGIC 0x72b6 39 | #define FS_JFS_MAGIC 0x3153464a 40 | #define FS_MINIX_MAGIC 0x137F 41 | #define FS_MINIX_MAGIC2 0x138F 42 | #define FS_MINIX2_MAGIC 0x2468 43 | #define FS_MINIX2_MAGIC2 0x2478 44 | #define FS_MSDOS_MAGIC 0x4d44 45 | #define FS_NCP_MAGIC 0x564c 46 | #define FS_NFS_MAGIC 0x6969 47 | #define FS_NTFS_MAGIC 0x5346544e 48 | #define FS_OPENPROM_MAGIC 0x9fa1 49 | #define FS_PROC_MAGIC 0x9fa0 50 | #define FS_QNX4_MAGIC 0x002f 51 | #define FS_REISERFS_MAGIC 0x52654973 52 | #define FS_ROMFS_MAGIC 0x7275 53 | #define FS_SMB_MAGIC 0x517B 54 | #define FS_SYSV2_MAGIC 0x012FF7B6 55 | #define FS_SYSV4_MAGIC 0x012FF7B5 56 | #define FS_TMPFS_MAGIC 0x01021994 57 | #define FS_UDF_MAGIC 0x15013346 58 | #define FS_UFS_MAGIC 0x00011954 59 | #define FS_USBDEVICE_MAGIC 0x9fa2 60 | #define FS_VXFS_MAGIC 0xa501FCF5 61 | #define FS_XENIX_MAGIC 0x012FF7B4 62 | #define FS_XFS_MAGIC 0x58465342 63 | #define FS_XIAFS_MAGIC 0x012FD16D 64 | /* the follow Filesystem types are from FS codes */ 65 | #define FS_UBIFS_MAGIC 0x24051905 66 | 67 | #define FILE_SIZE_UNLIMITED ((unsigned long long)(~0ULL>>1)) 68 | 69 | struct _fs { 70 | unsigned long long magic; 71 | const char str[20]; 72 | long long max_file_size; 73 | }; 74 | 75 | struct sys_info { 76 | /* all the follow size in Bytes */ 77 | unsigned long long total_ddr; 78 | unsigned long long free_ddr; 79 | unsigned long long total_flash; 80 | /* 81 | * free_flash should be a function rather than variable 82 | * because rwcheck always needs the real-time free space. 83 | */ 84 | unsigned long long (*free_flash)(struct sys_info *); 85 | unsigned long long (*max_file_size)(struct sys_info *); 86 | const char *(*fs_name)(struct sys_info *); 87 | int (*clean_mem)(void); 88 | 89 | /* private */ 90 | const struct _fs *fs; 91 | const char *dir; 92 | }; 93 | 94 | int update_sys_info(struct sys_info *sys, const char *dir); 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /fops.c: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "rwcheck.h" 24 | 25 | bool is_existed(const char *path) 26 | { 27 | return !access(path, F_OK | R_OK); 28 | } 29 | 30 | bool is_dir(const char *path) 31 | { 32 | int ret; 33 | struct stat s; 34 | 35 | ret = stat(path, &s); 36 | if (ret) 37 | return false; 38 | return S_ISDIR(s.st_mode); 39 | } 40 | 41 | bool is_file(const char *path) 42 | { 43 | int ret; 44 | struct stat s; 45 | 46 | ret = stat(path, &s); 47 | if (ret) 48 | return false; 49 | return S_ISREG(s.st_mode); 50 | } 51 | 52 | int copy_file(const char *in, const char *out, unsigned long long size, 53 | unsigned long long buf_size) 54 | { 55 | char *buf; 56 | int fd_in, fd_out; 57 | int ret = -1; 58 | 59 | buf = malloc(buf_size); 60 | if (!buf) { 61 | ERROR("malloc failed\n"); 62 | return -1; 63 | } 64 | 65 | fd_in = open(in, O_RDONLY); 66 | if (fd_in < 0) { 67 | ERROR("open %s failed\n", in); 68 | goto free_buf; 69 | } 70 | fd_out = open(out, O_WRONLY | O_CREAT | O_TRUNC, 0666); 71 | if (fd_out < 0) { 72 | ERROR("open %s failed\n", out); 73 | goto close_in; 74 | } 75 | /* pre-allocate flash space, in case of no enough space while writing */ 76 | if (ftruncate(fd_out, size)) { 77 | ERROR("ftruncate %s to %lu failed\n", out, size); 78 | goto close_out; 79 | } 80 | 81 | while (size > 0) { 82 | unsigned long long sz = min(size, buf_size); 83 | unsigned long long done = 0; 84 | 85 | while (sz > done) { 86 | int rret; 87 | 88 | rret = read(fd_in, buf + done, sz - done); 89 | if (rret < 0) { 90 | ERROR("read %s failed\n", in); 91 | goto close_out; 92 | } 93 | /* lseek to start of file to ensure read full size */ 94 | if (rret != sz - done) 95 | lseek(fd_in, 0, SEEK_SET); 96 | done += rret; 97 | } 98 | done = write(fd_out, buf, sz); 99 | if (done != sz) { 100 | ERROR("write %s failed\n", out); 101 | goto close_out; 102 | } 103 | size -= sz; 104 | } 105 | 106 | fsync(fd_out); 107 | ret = 0; 108 | close_out: 109 | close(fd_out); 110 | close_in: 111 | close(fd_in); 112 | free_buf: 113 | free(buf); 114 | return ret; 115 | } 116 | 117 | int append_crc(const char *path) 118 | { 119 | int fd, ret = -1; 120 | unsigned long long fsize; 121 | unsigned short crc = 0; 122 | 123 | fd = open(path, O_RDWR); 124 | if (fd < 0) { 125 | ERROR("open %s failed\n", path); 126 | goto err; 127 | } 128 | 129 | fsize = file_size(fd); 130 | if ((ssize_t)fsize < 0) 131 | goto err; 132 | 133 | ret = get_crc16_by_fd(&crc, fd, fsize - 2); 134 | if (ret) { 135 | ERROR("get crc16 failed\n"); 136 | goto err; 137 | } 138 | 139 | ret = lseek(fd, -2, SEEK_END); 140 | if (ret < 0) { 141 | ERROR("lseek %s failed\n", path); 142 | goto err; 143 | } 144 | 145 | ret = write(fd, &crc, 2); 146 | if (ret != 2) { 147 | ERROR("write %s failed\n", path); 148 | goto err; 149 | } 150 | 151 | fsync(fd); 152 | ret = 0; 153 | err: 154 | close(fd); 155 | return ret; 156 | } 157 | 158 | char *abs_path(const char *path) 159 | { 160 | char *ret = NULL, *pwd; 161 | 162 | pwd = getcwd(NULL, 0); 163 | if (!pwd) 164 | return NULL; 165 | 166 | if (chdir(path) < 0) { 167 | ERROR("chdir failed\n"); 168 | goto out; 169 | } 170 | ret = getcwd(NULL, 0); 171 | 172 | if (chdir(pwd) < 0) { 173 | free(ret); 174 | ret = NULL; 175 | } 176 | 177 | out: 178 | free(pwd); 179 | return ret; 180 | } 181 | 182 | ssize_t file_size(int fd) 183 | { 184 | struct stat s; 185 | 186 | if (fstat(fd, &s) < 0) { 187 | ERROR("fstat failed\n"); 188 | return -1; 189 | } 190 | return s.st_size; 191 | } 192 | -------------------------------------------------------------------------------- /crc16.c: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "rwcheck.h" 23 | 24 | #define CRC_INIT ((unsigned short)0xffff) 25 | #define GOOD_CRC ((unsigned short)0xf0b8) 26 | 27 | /* Needed for CRC generation/checking */ 28 | static const unsigned short crc16_table[] = { 29 | 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 30 | 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 31 | 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 32 | 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 33 | 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 34 | 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 35 | 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 36 | 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 37 | 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 38 | 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 39 | 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 40 | 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 41 | 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 42 | 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 43 | 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 44 | 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 45 | 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 46 | 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 47 | 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 48 | 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 49 | 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 50 | 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 51 | 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 52 | 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 53 | 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 54 | 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 55 | 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 56 | 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 57 | 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 58 | 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 59 | 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 60 | 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 61 | }; 62 | 63 | /* 64 | * extracting common part of crc16 for get/check crc16 from buf/file 65 | * 66 | * @oldcrc: old crc 67 | * @buf: the data to be checked 68 | * @numBytes: the count bytes of the buf to calculate crc16 69 | * 70 | * return crc 71 | */ 72 | unsigned short crc16_common(unsigned short oldcrc, const char *buf, 73 | unsigned long long bytes) 74 | { 75 | while (bytes--) 76 | oldcrc = (oldcrc >> 8) ^ crc16_table[(oldcrc ^ *buf++) & 0xff]; 77 | return oldcrc; 78 | } 79 | 80 | /* 81 | * step 1: get initial crc 82 | */ 83 | unsigned short crc16_start(void) 84 | { 85 | return CRC_INIT; 86 | } 87 | 88 | /* 89 | * step 2: get crc16 base on old crc 90 | * 91 | * @oldcrc: old crc from step 1 in first time or step 2 in others 92 | * @buf: the data to be checked 93 | * @bytes: the count bytes of the buf to calculate crc16 94 | * 95 | * return crc 96 | */ 97 | unsigned short crc16_continue(unsigned short oldcrc, 98 | const char *buf, unsigned long long bytes) 99 | { 100 | return crc16_common(oldcrc, buf, bytes); 101 | } 102 | 103 | /* 104 | * step 3: get final crc 105 | * 106 | * @oldcrc: old crc from step 2 107 | */ 108 | unsigned short crc16_end(unsigned short oldcrc) 109 | { 110 | return ~oldcrc; 111 | } 112 | 113 | /* 114 | * step 4: check crc 115 | * 116 | * @crc: crc from step 3 117 | */ 118 | bool check_crc16(unsigned short crc) 119 | { 120 | return ((~crc & CRC_INIT) == GOOD_CRC); 121 | } 122 | 123 | int get_crc16_by_buf(unsigned short *crc16, const char *buf, 124 | unsigned long long bytes) 125 | { 126 | *crc16 = ~crc16_common(CRC_INIT, buf, bytes); 127 | return 0; 128 | } 129 | 130 | #define BUF_SIZE (4 * KB) 131 | int get_crc16_by_fd(unsigned short *crc16, int fd, unsigned long long bytes) 132 | { 133 | char buf[BUF_SIZE] = {0}; 134 | unsigned short crc; 135 | 136 | crc = crc16_start(); 137 | 138 | if (bytes == CRC16_MAX_BYTES) { 139 | ssize_t size = file_size(fd); 140 | if (size < 0) 141 | return -1; 142 | bytes = (unsigned long long)size; 143 | } 144 | 145 | while (bytes > 0) { 146 | ssize_t rlen = min((unsigned long long)(BUF_SIZE), bytes); 147 | ssize_t rdlen = read(fd, buf, rlen); 148 | 149 | if (rdlen < 0) { 150 | ERROR("read failed\n"); 151 | return -1; 152 | } else if (rdlen != rlen) { 153 | ERROR("read failed (%d != %d)\n", rdlen, rlen); 154 | return -1; 155 | } 156 | crc = crc16_continue(crc, buf, rdlen); 157 | bytes -= rdlen; 158 | } 159 | 160 | *crc16 = crc16_end(crc); 161 | return 0; 162 | } 163 | 164 | int get_crc16_by_path(unsigned short *crc16, const char *path, 165 | unsigned long long bytes) 166 | { 167 | int fd, ret = -1; 168 | 169 | fd = open(path, O_RDONLY); 170 | if (fd < 0) { 171 | ERROR("open %s failed\n", path); 172 | return -1; 173 | } 174 | 175 | if (bytes == CRC16_MAX_BYTES) { 176 | ssize_t size; 177 | 178 | size = file_size(fd); 179 | if (size < 0) 180 | goto out; 181 | bytes = (unsigned long long)size; 182 | } 183 | 184 | ret = get_crc16_by_fd(crc16, fd, bytes); 185 | out: 186 | close(fd); 187 | return ret; 188 | } 189 | -------------------------------------------------------------------------------- /README.zh.md: -------------------------------------------------------------------------------- 1 | # read/write check 2 | 3 | rwcheck是一个对嵌入式设备进行读写压测的工具 4 | 5 | ## 什么是rwcheck 6 | 7 | 正如其名,rwcheck工具用于读写压测。它是什么工作原理呢?为什么要用rwcheck呢? 8 | 9 | rwcheck的初衷是为嵌入式Linux设备提供读写冒烟测试的工具,在大压力的IO测试中,确保存储稳定。rwcheck也被用于读写掉电测试,在读写时掉电,并在上电后对掉电前写入的文件进行检查。 10 | 11 | 他有以下特点: 12 | 13 | * 多种测试模式,覆盖各种尺寸的测试文件 14 | * 读写随机数据,覆盖到各种数据组合 15 | * CRC校验数据,错误即刻退出并保留问题现场 16 | * 支持读写掉电,重启后校验历史数据 17 | * 动态获取系统信息,按存储空间使用比例限制测试总大小 18 | * 支持多线程写,模拟真实使用场景 19 | 20 | ## 怎么用rwcheck 21 | 22 | ### 下载与编译 23 | 24 | ``` 25 | git clone https://github.com/gmpy/rwcheck.git rwcheck && make -C rwcheck 26 | ``` 27 | 28 | ### 使用说明 29 | 30 | **rwcheck -h**可以获取到使用说明 31 | 32 | ``` 33 | Usage: rwcheck [-h] [-d dir] [-t times] [-b size] [-e size] 34 | [-s size] [-u size] [-p percent] [-i input] [-j jobs] 35 | 36 | -h : show this massage and exit 37 | -d # : the diretory to check [default currect path] 38 | -t # : check times 39 | -b # : [up mode] set begin size 40 | -e # : [up mode] set end size 41 | -s # : [same mode] set file size 42 | -u # : set read/write buf size 43 | -p # : set maximum ratio of total flash size to check. Eg. -p 95 44 | -i # : input path of file [default /rwcheck.org] 45 | if file don't existed, create 64K from /dev/urandom 46 | -j # : multiple jobs 47 | 48 | size trailing with k|m|g or not 49 | 50 | rwcheck work in 3 mode: 51 | 1. -s # : files have the same size, keep testing until no enough space 52 | 2. -b # : 53 | 2. -e # : file size increase by The multiplier of 2, loop 54 | from beginning to ending size until no enough space 55 | 3. none : file size is 50% of the free space, keep testing until 56 | the less space is less than 64K 57 | ``` 58 | 59 | 例如: 60 | 61 | ``` 62 | rwcheck -d /mnt/UDISK -b 128k -p 90 -j 2 -t 10000000 63 | ``` 64 | 65 | 上面命令的含义: 66 | 67 | * 在 **/mnt/UDISK** 目录下进行读写 68 | * **UP模式**,测试从 **128K** 文件大小开始,使用默认最大文件大小(16G) 69 | * 测试总大小为总容量的 **90%** 70 | * 以 **2个线程** 同时写 71 | * 循环测试 **10000000次** 72 | 73 | ### 1次循环 74 | 75 | ```-t <次数>```可以指定循环测试次数,那么,怎样才是1次循环呢? 76 | 77 | 每一次循环,包含3个步骤: 78 | 79 | 1. 写:按[不同模式](#测试模式)要求创建多个文件,循环创建直至空间使用率达到设定的百分比 80 | 2. 校验:读取每一个文件,校验文件CRC值 *(允许最后一个文件CRC校验值错误)* 81 | 3. 删除:删除测试文件 82 | 83 | ``` 84 | Q:为什么允许最后一个文件CRC校验值错误? 85 | A:因为在随机掉电情况下,最后一个文件写入不完整,会导致校验值不准确,但实际上,这并不是错误 86 | 87 | Q:为什么有时候没写任何文件直接跳入到校验环节? 88 | A:当检查到已有文件存在的时候,写环节会跳过,进入到校验环节,以此实现检查上一次执行时创建的文件 89 | ``` 90 | 91 | ### 随机数据 92 | 93 | rwcheck写入的数据是从```/dev/urandom```获取的随机数据,确保了测试覆盖到尽可能多的数据组合。但这样也存在个问题,当出现数据校验出错时,并没有原始数据对比分析,怎么办呢? 94 | 95 | rwcheck是这么做的: 96 | 97 | 1. 在每一次循环开始之前,从```/dev/urandom```读取一定大小的随机数据到 **rwcheck.org** 98 | 2. **rwcheck.org** 作为数据源,从 **rwcheck.org** 循环读,获取足够数据写入到测试文件 99 | 3. 一次测试循环后,删除旧的 **rwcheck.org** 文件,以便下一次循环创建新的随机数据源 100 | 101 | 由于**一旦出错rwcheck会即刻退出以保留现场**,因此可以通过对比 **rwcheck.org** 与出错文件,分析错误情况 102 | 103 | ### 测试日志 104 | 105 | ```Shell 106 | root# rwcheck -d /mnt/UDISK -t 2 -b 128k -p 85 -j2 107 | 108 | rwcheck: do read and write check 109 | 110 | version: v0.1.0 111 | build: Compiled in Aug 20 2019 at 02:32:41 112 | date: Mon Aug 19 18:01:21 2019 113 | 114 | free/total ddr: 33/53 MB 115 | free/total flash: 62/63 MB 116 | flash filesystem ubifs 117 | set mode to Up Mode 118 | set file begin size to 128 KB 119 | set file end size to 1 GB 120 | set times to 2 121 | set max percent of total space to 85% 122 | set buf size to 512 KB 123 | set check diretory as /mnt/UDISK 124 | set orgin file as /mnt/UDISK/rwcheck.org 125 | set jobs as 2 126 | 127 | --- CREATE --- 128 | create : /mnt/UDISK/rwcheck.tmp.0_1 ... OK (64K) 129 | create : /mnt/UDISK/rwcheck.tmp.0_0 ... OK (64K) 130 | create : /mnt/UDISK/rwcheck.tmp.1_1 ... OK (128K) 131 | create : /mnt/UDISK/rwcheck.tmp.1_0 ... OK (128K) 132 | ...... 133 | create : /mnt/UDISK/rwcheck.tmp.7_0 ... OK (64K) 134 | create : /mnt/UDISK/rwcheck.tmp.7_1 ... OK (64K) 135 | --- CHECK --- 136 | check : /mnt/UDISK/rwcheck.tmp.0_0 ... OK 137 | check : /mnt/UDISK/rwcheck.tmp.0_1 ... OK 138 | ...... 139 | check : /mnt/UDISK/rwcheck.tmp.7_0 ... OK 140 | check : /mnt/UDISK/rwcheck.tmp.7_1 ... OK 141 | --- REMOVE --- 142 | remove : /mnt/UDISK/rwcheck.tmp.0_0 ... OK 143 | remove : /mnt/UDISK/rwcheck.tmp.0_1 ... OK 144 | ...... 145 | remove : /mnt/UDISK/rwcheck.tmp.7_0 ... OK 146 | remove : /mnt/UDISK/rwcheck.tmp.7_1 ... OK 147 | ``` 148 | 149 | 一旦出现错误,会打印类似的错误信息 150 | 151 | ``` 152 | --- CREATE --- 153 | --- CHECK --- 154 | check : /mnt/UDISK/rwcheck.tmp.0_0 ... OK 155 | check : /mnt/UDISK/rwcheck.tmp.0_1 ... OK 156 | check : /mnt/UDISK/rwcheck.tmp.1_0 ... OK 157 | check : /mnt/UDISK/rwcheck.tmp.1_1 ... OK 158 | check : /mnt/UDISK/rwcheck.tmp.2_0 ... FAILED (crc error) 159 | 160 | --- ERROR INFO --- 161 | file: rwcheck.c (573) 162 | errno: I/O error (5) 163 | info: /mnt/UDISK/rwcheck.tmp.2_0 crc error 164 | ``` 165 | 166 | ## 测试模式 167 | 168 | rwcheck支持3种测试模式,分别是**AUTO模式**,**UP模式**,**SAME模式** 169 | 170 | ***不同测试模式下,测试文件大小会不一样***,测试文件大小策略可见函数 **get_test_size()** 。 171 | 172 | | 模式 | 选项 | 特点 | 173 | | :---: | :--- | :--- | 174 | | AUTO | (default) | 每次测试取当前剩余空间的一半做测试文件大小 | 175 | | UP | -b/-e | 从设定的begin开始到设定的end结束,文件大小以2的倍数递增 | 176 | | SAME | -s | 固定每个测试文件的大小 | 177 | 178 | ### AUTO模式 179 | 180 | 默认为 **auto模式**,在此模式下,每次测试的文件大小为剩余分区空间的 **50%**(最大不允许超过4G),直至测试文件大小 **小于64K** 或 **剩余空间达到了设置的百分比**。 181 | 182 | 因此,** auto模式 ** 也可以说是二分递减模式 183 | 184 | 例如,当前剩余空间为64M,测试百分比设置为95%,则创建的文件大小依次为: 185 | 186 | ``` 187 | 32M -> 16M -> 8M -> 4M -> 1M 188 | ``` 189 | 190 | 在最后一次测试中,由于剩余空间不足,因此跳过2M,直接测试1M大小文件 191 | 192 | ### UP模式 193 | 194 | 与 **auto模式** 相反,**up模式** 是以2的倍数递增,通过选项```-b <开始大小>```和```-e <结束大小>```设置递增的范围,直至 **剩余空间达到了设置的百分比** 。 195 | 196 | 如果达到了设置的结束大小,但还有充足剩余空间,则从设置的开始大小重新循环。 197 | 198 | 例如,当前剩余空间为64M,测试百分比设置为95%,开始大小设置为128K,则创建的文件大小为: 199 | 200 | ``` 201 | 128K -> 256K -> 512K -> 1M -> 2M -> 4M -> 8M -> 16M -> 128K -> 256K -> ... 202 | ``` 203 | 204 | ### SAME模式 205 | 206 | **same模式** 是固定测试文件大小,通过选项```-s <文件大小>```设置测试文件大小,循环创建文件直至 **剩余空间达到了设置的百分比** 或 **没有充足空间继续创建 ** 207 | 208 | 例如,当前剩余空间为64M,测试百分比设置为95%,设置固定大小为5M,则创建的文件大小为: 209 | 210 | ``` 211 | 5M -> 5M -> 5M -> 5M -> 5M -> 5M -> 5M -> 5M -> 5M -> 5M -> 5M -> 5M 212 | ``` 213 | -------------------------------------------------------------------------------- /sys_info.c: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "rwcheck.h" 24 | 25 | #define MEMINFO "/proc/meminfo" 26 | #define CLEANMEM "/proc/sys/vm/drop_caches" 27 | 28 | /* 29 | * vfat should not larger than 4G(4294967296) 30 | * but i don't know why that allway end in 4294967295. 31 | * so, for fat fs, we limit (4294967296 - 2) 32 | */ 33 | #define FAT_MAX_SIZE ((unsigned long long)4 * GB - 2) 34 | 35 | static struct _fs fs_info[] = { 36 | {FS_MSDOS_MAGIC, "fat", FAT_MAX_SIZE}, 37 | {FS_EXT2_MAGIC, "ext", FILE_SIZE_UNLIMITED}, 38 | {FS_JFFS2_MAGIC, "jffs2", FILE_SIZE_UNLIMITED}, 39 | {FS_NTFS_MAGIC, "ntfs", FILE_SIZE_UNLIMITED}, 40 | {FS_TMPFS_MAGIC, "tmpfs", FILE_SIZE_UNLIMITED}, 41 | {FS_UBIFS_MAGIC, "ubifs", FILE_SIZE_UNLIMITED}, 42 | {FS_CIFS_MAGIC, "cifs", FILE_SIZE_UNLIMITED}, 43 | {0, "Other", FILE_SIZE_UNLIMITED}, 44 | }; 45 | 46 | const struct _fs *get_fs(const char *dir) 47 | { 48 | struct statfs sfs; 49 | int i; 50 | 51 | if (!is_dir(dir)) { 52 | ERROR("not existed or directory: %s\n", dir); 53 | return NULL; 54 | } 55 | 56 | if (statfs(dir, &sfs) < 0) { 57 | ERROR("statfs %s failed\n", dir); 58 | return NULL; 59 | } 60 | 61 | for (i = 0; i < ARRAY_SIZE(fs_info) - 1; i++) { 62 | struct _fs *info = &fs_info[i]; 63 | 64 | DEBUG("try match fs %s(%llu) with %lu\n", info->str, 65 | info->magic, sfs.f_type); 66 | if ((unsigned long long)sfs.f_type == info->magic) 67 | return info; 68 | } 69 | return &fs_info[i]; 70 | } 71 | 72 | unsigned long long get_total_mem(void) 73 | { 74 | unsigned long long size_kb = 0; 75 | char buf[1024]= {0}, *p; 76 | FILE *fp; 77 | 78 | fp = fopen(MEMINFO, "r"); 79 | if (fp == NULL) { 80 | ERROR("open %s failed\n", MEMINFO); 81 | return 0; 82 | } 83 | 84 | if (fread(buf, 1, 1024, fp) == 0 && ferror(fp)) { 85 | ERROR("fread %s failed\n", MEMINFO); 86 | goto err; 87 | } 88 | 89 | p = strstr(buf, "MemTotal"); 90 | if (p == NULL) { 91 | ERROR("not found 'MemTotal' in %s\n", MEMINFO); 92 | goto err; 93 | } 94 | 95 | if (sscanf(p, "%*s%llu%*s", &size_kb) != 1) { 96 | ERROR("sscanf %s failed\n", MEMINFO); 97 | size_kb = 0; 98 | } 99 | 100 | err: 101 | fclose(fp); 102 | return size_kb * KB; 103 | } 104 | 105 | int clean_mem(void) 106 | { 107 | int ret = -1, fd, uid; 108 | 109 | /* only root has permissiom to control CLEANMEM */ 110 | #define ROOT_UID 0 111 | uid = getuid(); 112 | if (uid != ROOT_UID) 113 | return 0; 114 | 115 | sync(); 116 | fd = open(CLEANMEM, O_WRONLY); 117 | if (fd < 0) { 118 | ERROR("open %s failed\n", CLEANMEM); 119 | return fd; 120 | } 121 | 122 | ret = write(fd, "3", 1); 123 | if (ret != 1) 124 | ERROR("write '3' to %s failed\n", CLEANMEM); 125 | else 126 | ret = 0; 127 | 128 | close(fd); 129 | return ret; 130 | } 131 | 132 | unsigned long long get_free_mem(void ) 133 | { 134 | unsigned long long free_kb = 0; 135 | char buf[1024]= {0}, *p; 136 | FILE *fp; 137 | 138 | /* we must sync and clean cache before */ 139 | sync(); 140 | clean_mem(); 141 | 142 | fp = fopen(MEMINFO, "r"); 143 | if (fp == NULL) { 144 | ERROR("open %s failed\n", MEMINFO); 145 | return 0; 146 | } 147 | 148 | if (fread(buf, 1, 1024, fp) == 0 && ferror(fp)) { 149 | ERROR("fread %s failed\n", MEMINFO); 150 | goto err; 151 | } 152 | 153 | p = strstr(buf, "MemFree"); 154 | if (p == NULL) { 155 | ERROR("not found 'MemFree' in %s\n", MEMINFO); 156 | goto err; 157 | } 158 | 159 | if (sscanf(p, "%*s%llu%*s", &free_kb) != 1) { 160 | ERROR("sscanf %s failed\n", MEMINFO); 161 | free_kb = 0; 162 | } 163 | 164 | err: 165 | fclose(fp); 166 | return free_kb * KB; 167 | } 168 | 169 | unsigned long long get_free_flash(struct sys_info *sys) 170 | { 171 | struct statfs sfs; 172 | const char *dir = sys->dir; 173 | 174 | if (!sys->dir) 175 | return 0; 176 | 177 | if (!is_dir(dir)) { 178 | ERROR("not existed or directory: %s\n", dir); 179 | return 0; 180 | } 181 | 182 | if (statfs(dir, &sfs) < 0) 183 | return 0; 184 | 185 | /* get free by f_bavail rather than f_bfree */ 186 | return sfs.f_bsize * sfs.f_bavail; 187 | } 188 | 189 | unsigned long long get_total_flash(const char *dir) 190 | { 191 | struct statfs sfs; 192 | 193 | if (!is_dir(dir)) { 194 | ERROR("not existed or directory: %s\n", dir); 195 | return 0; 196 | } 197 | 198 | if (statfs(dir, &sfs) < 0) { 199 | ERROR("statfs %s failed\n", dir); 200 | return 0; 201 | } 202 | 203 | return sfs.f_bsize * sfs.f_blocks; 204 | } 205 | 206 | unsigned long long get_max_file_size(struct sys_info *sys) 207 | { 208 | if (sys->dir && sys->fs) 209 | return sys->fs->max_file_size; 210 | return 0; 211 | } 212 | 213 | const char *get_fs_name(struct sys_info *sys) 214 | { 215 | if (sys->dir && sys->fs) 216 | return sys->fs->str; 217 | return 0; 218 | } 219 | 220 | int update_sys_info(struct sys_info *sys, const char *dir) 221 | { 222 | if (!sys->total_ddr) 223 | sys->total_ddr = get_total_mem(); 224 | if (!sys->free_ddr) 225 | sys->free_ddr = get_free_mem(); 226 | if (!sys->total_ddr || !sys->free_ddr) 227 | return -1; 228 | sys->clean_mem = clean_mem; 229 | 230 | /* If no dir, no need to get flash information */ 231 | if (!dir) 232 | return 0; 233 | if (sys->dir && !strcmp(sys->dir, dir)) 234 | return 0; 235 | 236 | sys->total_flash = get_total_flash(dir); 237 | if (!sys->total_flash) 238 | return -1; 239 | 240 | sys->fs = get_fs(dir); 241 | if (!sys->fs) 242 | return -1; 243 | 244 | if (sys->dir) 245 | free((void *)sys->dir); 246 | sys->dir = strdup(dir); 247 | if (!sys->dir) 248 | return -1; 249 | 250 | sys->free_flash = get_free_flash; 251 | sys->max_file_size = get_max_file_size; 252 | sys->fs_name = get_fs_name; 253 | return 0; 254 | } 255 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # read/write check 2 | 3 | A tool to do read/write test for embedded devices. 4 | 5 | [显示中文介绍(Chinese README)](https://github.com/gmpy/rwcheck/blob/master/README.zh.md) 6 | 7 | ## What's rwcheck 8 | 9 | As it's name, rwcheck is a tool to do read/write check. Where do it work? When do it work? And how do it work? 10 | 11 | rwcheck is designed for embedded devices, to take a storage smoke test. Make sure that the storage is strong enough under much I/O test. Besides, rwcheck can also be used to do read/write power-failures testing, random power-off during reading and writing, becuase it will check the files, created before power failure. 12 | 13 | It has the following features: 14 | 15 | * Multiple test modes, covering test files of various sizes 16 | * Read and write random data, covering various data combinations 17 | * CRC check data, the error immediately exits and retains the problem site 18 | * Support reading and writing power failure, verify historical data after restart 19 | * Dynamically obtain system information, limit the total test size according to the storage space usage ratio 20 | * Support multi-threaded write, simulate real-life scenarios 21 | 22 | ## How to begin 23 | 24 | ### Download and Compile 25 | 26 | ``` 27 | git clone https://github.com/gmpy/rwcheck.git rwcheck && make -C rwcheck 28 | ``` 29 | 30 | ### Usage 31 | 32 | You can get the follow message by **rwcheck -h** 33 | 34 | ``` 35 | Usage: rwcheck [-h] [-d dir] [-t times] [-b size] [-e size] 36 | [-s size] [-u size] [-p percent] [-i input] [-j jobs] 37 | 38 | -h : show this massage and exit 39 | -d # : the diretory to check [default currect path] 40 | -t # : check times 41 | -b # : [up mode] set begin size 42 | -e # : [up mode] set end size 43 | -s # : [same mode] set file size 44 | -u # : set read/write buf size 45 | -p # : set maximum ratio of total flash size to check. Eg. -p 95 46 | -i # : input path of file [default /rwcheck.org] 47 | if file don't existed, create 64K from /dev/urandom 48 | -j # : multiple jobs 49 | 50 | size trailing with k|m|g or not 51 | 52 | rwcheck work in 3 mode: 53 | 1. -s # : files have the same size, keep testing until no enough space 54 | 2. -b # : 55 | 2. -e # : file size increase by The multiplier of 2, loop 56 | from beginning to ending size until no enough space 57 | 3. none : file size is 50% of the free space, keep testing until 58 | the less space is less than 64K 59 | ``` 60 | 61 | For example: 62 | 63 | ``` 64 | rwcheck -d /mnt/UDISK -b 128k -p 90 -j 2 -t 10000000 65 | ``` 66 | 67 | It means that: 68 | 69 | * Test on **/mnt/UDISK** directory 70 | * **UP Mode**,test starts with **128K** file size, using the default maximum file size(16G) 71 | * The total test size is 90% of total capacity 72 | * **2 threads** write at the same time 73 | * Ring test **10000000 times** 74 | 75 | ### One cycle 76 | 77 | ```-t ``` can specify the number of loop tests, then, how is one cycle? 78 | 79 | Each cycle consists of 3 steps: 80 | 81 | 1. Write: Press [Different Mode](#test-mode) to create multiple files, create a loop until the space usage reaches the set percentage 82 | 2. Check: Read each file, check the file CRC value *(Allow the last file CRC check value is wrong)* 83 | 3. Delete: delete the test file 84 | 85 | ``` 86 | Q: Why is the last file CRC checksum error allowed? 87 | A: Because the last file is written incomplete under random power failure, the check value will be inaccurate, but in fact, this is not an error. 88 | 89 | Q: Why do it sometimes skip to the verification step without writing any files? 90 | A: When it is checked that the existing file exists, the write step will be skipped and the verification step will be entered to check the file created during the last execution. 91 | ``` 92 | 93 | ### Random data 94 | 95 | The data written by rwcheck is random data obtained from ```/dev/urandom```, ensuring that the test covers as many data combinations as possible. However, there is also a problem. When there is an error in data verification, there is no comparison analysis of the original data. What should rwcheck do? 96 | 97 | Rwcheck does this: 98 | 99 | 1. Read a random amount of random data from ```/dev/urandom``` to **rwcheck.org** before each loop starts. 100 | 2. **rwcheck.org** as a data source, loop reading from **rwcheck.org** to get enough data to write to the test file 101 | 3. After the test loop, delete the old **rwcheck.org** file so that the next loop creates a new random data source 102 | 103 | Since **once the error rwcheck will exit immediately to retain the live**, you can analyze the error by comparing **rwcheck.org** with the error file. 104 | 105 | ### Test log 106 | 107 | ```Shell 108 | root# rwcheck -d /mnt/UDISK -t 2 -b 128k -p 85 -j2 109 | 110 | rwcheck: do read and write check 111 | 112 | version: v0.1.0 113 | build: Compiled in Aug 20 2019 at 02:32:41 114 | date: Mon Aug 19 18:01:21 2019 115 | 116 | free/total ddr: 33/53 MB 117 | free/total flash: 62/63 MB 118 | flash filesystem ubifs 119 | set mode to Up Mode 120 | set file begin size to 128 KB 121 | set file end size to 1 GB 122 | set times to 2 123 | set max percent of total space to 85% 124 | set buf size to 512 KB 125 | set check diretory as /mnt/UDISK 126 | set orgin file as /mnt/UDISK/rwcheck.org 127 | set jobs as 2 128 | 129 | --- CREATE --- 130 | create : /mnt/UDISK/rwcheck.tmp.0_1 ... OK (64K) 131 | create : /mnt/UDISK/rwcheck.tmp.0_0 ... OK (64K) 132 | create : /mnt/UDISK/rwcheck.tmp.1_1 ... OK (128K) 133 | create : /mnt/UDISK/rwcheck.tmp.1_0 ... OK (128K) 134 | ...... 135 | create : /mnt/UDISK/rwcheck.tmp.7_0 ... OK (64K) 136 | create : /mnt/UDISK/rwcheck.tmp.7_1 ... OK (64K) 137 | --- CHECK --- 138 | check : /mnt/UDISK/rwcheck.tmp.0_0 ... OK 139 | check : /mnt/UDISK/rwcheck.tmp.0_1 ... OK 140 | ...... 141 | check : /mnt/UDISK/rwcheck.tmp.7_0 ... OK 142 | check : /mnt/UDISK/rwcheck.tmp.7_1 ... OK 143 | --- REMOVE --- 144 | remove : /mnt/UDISK/rwcheck.tmp.0_0 ... OK 145 | remove : /mnt/UDISK/rwcheck.tmp.0_1 ... OK 146 | ...... 147 | remove : /mnt/UDISK/rwcheck.tmp.7_0 ... OK 148 | remove : /mnt/UDISK/rwcheck.tmp.7_1 ... OK 149 | ``` 150 | 151 | Once an error occurs, a similar error message will be printed: 152 | 153 | ``` 154 | --- CREATE --- 155 | --- CHECK --- 156 | check : /mnt/UDISK/rwcheck.tmp.0_0 ... OK 157 | check : /mnt/UDISK/rwcheck.tmp.0_1 ... OK 158 | check : /mnt/UDISK/rwcheck.tmp.1_0 ... OK 159 | check : /mnt/UDISK/rwcheck.tmp.1_1 ... OK 160 | check : /mnt/UDISK/rwcheck.tmp.2_0 ... FAILED (crc error) 161 | 162 | --- ERROR INFO --- 163 | file: rwcheck.c (573) 164 | errno: I/O error (5) 165 | info: /mnt/UDISK/rwcheck.tmp.2_0 crc error 166 | ``` 167 | 168 | ## Test mode 169 | 170 | Rwcheck supports 3 test modes, namely **AUTO mode**, **UP mode**, **SAME mode** 171 | 172 | ***In different test modes, the test file size will be different***, test file size policy visible function **get_test_size()**. 173 | 174 | | Mode | Options | Features | 175 | | :---: | :--- | :--- | 176 | | AUTO | (default) | Test the file size by half of the current remaining space for each test | 177 | | UP | -b/-e | From the set begin to the end of the set end, the file size is incremented by 2 | 178 | | SAME | -s | Fix the size of each test file | 179 | 180 | ### AUTO mode 181 | 182 | The default is **auto mode**, in which the file size for each test is **50%** of the remaining partition space (maximum of 4G is not allowed) until the test file size** is less than 64K** or **the remaining space has reached the set percentage**. 183 | 184 | Therefore, **auto mode ** can also be said to be a dichot mode 185 | 186 | For example, if the current free space is 64M and the test percentage is set to 95%, the file size created is: 187 | 188 | ``` 189 | 32M -> 16M -> 8M -> 4M -> 1M 190 | ``` 191 | 192 | In the last test, due to insufficient space, skip 2M and test 1M size files directly. 193 | 194 | ### UP mode 195 | 196 | Contrary to **auto mode**, **up mode** is incremented by a multiple of 2, with options ```-b ``` and ```-e ```. Increment the range until **the remaining space reaches the set percentage**. 197 | 198 | If the set end size is reached, but there is enough free space, re-circulate from the set begin size. 199 | 200 | For example, if the current free space is 64M, the test percentage is set to 95%, and the starting size is set to 128K, the file size created is: 201 | 202 | ``` 203 | 128K -> 256K -> 512K -> 1M -> 2M -> 4M -> 8M -> 16M -> 128K -> 256K -> ... 204 | ``` 205 | 206 | ### SAME mode 207 | 208 | **same mode** is a fixed test file size, set the test file size by the option ```-s ```, create a file cyclically until **the remaining space reaches the set percentage** or **no sufficient space to continue to create** 209 | 210 | For example, if the current free space is 64M, the test percentage is set to 95%, and the fixed size is set to 5M, the created file size is: 211 | 212 | ``` 213 | 5M -> 5M -> 5M -> 5M -> 5M -> 5M -> 5M -> 5M -> 5M -> 5M -> 5M -> 5M 214 | ``` 215 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /rwcheck.c: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "rwcheck.h" 27 | 28 | #define RANDOM_DATA "/dev/urandom" 29 | #define get_mode(task) ((task)->mode->mode) 30 | #define set_mode(task, _mode) ((task)->mode = &run_mode[_mode]) 31 | #define get_strmode(task) ((task)->mode->str_mode) 32 | 33 | /* 34 | * AUTO MODE: 35 | * size of each file to write is half of the free space, end testing only 36 | * when the less space is less than 64K 37 | */ 38 | #define AUTO_MODE 0 39 | /* 40 | * SAME MODE: 41 | * size of each file to write is the same, end testing only when there is no 42 | * enough space 43 | */ 44 | #define SAME_MODE 1 45 | /* 46 | * UP_MODE: 47 | * size of each file will increase by a multiple of 2, loop from beginning 48 | * size to end size until there is no enough space 49 | */ 50 | #define UP_MODE 2 51 | struct run_mode { 52 | int mode; 53 | const char *str_mode; 54 | }; 55 | 56 | static struct run_mode run_mode[] = { 57 | {AUTO_MODE, "Auto Mode"}, 58 | {SAME_MODE, "Same Mode"}, 59 | {UP_MODE, "Up Mode"}, 60 | }; 61 | 62 | #define MAX_PATH_LEN 1024 63 | struct rwtask { 64 | struct sys_info sys; 65 | struct run_mode *mode; 66 | 67 | unsigned long long min_free_bytes; 68 | const char *dir; 69 | char org[MAX_PATH_LEN]; 70 | bool set_org; 71 | int jobs; 72 | int percent; 73 | unsigned int times; 74 | unsigned int count; 75 | 76 | struct size_unit sz_begin; 77 | struct size_unit sz_end; 78 | union { 79 | struct size_unit sz_cur; 80 | struct size_unit sz_same; 81 | }; 82 | struct size_unit sz_buf; 83 | }; 84 | 85 | void help(void) 86 | { 87 | INFO(" Usage: rwcheck [-h] [-d dir] [-t times] [-b size] [-e size]\n"); 88 | INFO(" [-s size] [-u size] [-p percent] [-i input] [-j jobs]\n"); 89 | INFO("\n"); 90 | INFO("\t-h : show this massage and exit\n"); 91 | INFO("\t-d # : the diretory to check [default currect path]\n"); 92 | INFO("\t-t # : check times\n"); 93 | INFO("\t-b # : [up mode] set begin size\n"); 94 | INFO("\t-e # : [up mode] set end size\n"); 95 | INFO("\t-s # : [same mode] set file size\n"); 96 | INFO("\t-u # : set read/write buf size\n"); 97 | INFO("\t-p # : set maximum ratio of total flash size to check. Eg. -p 95\n"); 98 | INFO("\t-i # : input path of file [default /rwcheck.org]\n"); 99 | INFO("\t if file don't existed, create 64K from /dev/urandom\n"); 100 | INFO("\t-j # : multiple jobs\n"); 101 | INFO("\n"); 102 | INFO(" size trailing with k|m|g or not\n"); 103 | INFO("\n"); 104 | INFO(" rwcheck work in 3 mode:\n"); 105 | INFO(" 1. -s # : files have the same size, keep testing until no enough space\n"); 106 | INFO(" 2. -b # :\n"); 107 | INFO(" 2. -e # : file size increase by The multiplier of 2, loop\n"); 108 | INFO(" from beginning to ending size until no enough space\n"); 109 | INFO(" 3. none : file size is 50%% of the free space, keep testing until\n"); 110 | INFO(" the less space is less than 64K\n"); 111 | } 112 | 113 | int parse_args(struct rwtask *task, int argc, char **argv) 114 | { 115 | int ret; 116 | int opts = 0; 117 | 118 | while ((opts = getopt(argc, argv, ":d:t:b:e:s:u:p:hi:j:")) != EOF) { 119 | switch (opts) { 120 | case 'd': 121 | if (task->dir) 122 | free((void *)task->dir); 123 | if (!is_dir(optarg)) { 124 | ERROR("not existed or directory: %s\n", optarg); 125 | return -1; 126 | } 127 | task->dir = abs_path(optarg); 128 | if (!task->dir) { 129 | ERROR("can get absolute path: %s\n", optarg); 130 | return -1; 131 | } 132 | break; 133 | case 't': 134 | task->times = (unsigned int)atoi(optarg); 135 | if (task->times == 0) { 136 | ERROR("invalid times: %s\n", optarg); 137 | return -1; 138 | } 139 | break; 140 | case 'b': 141 | case 'e': 142 | if (get_mode(task) == SAME_MODE) { 143 | ERROR("incompatible with -s and -b|-e\n"); 144 | return -1; 145 | } 146 | if (opts == 'b') 147 | ret = str_to_size(&(task->sz_begin), optarg); 148 | else 149 | ret = str_to_size(&(task->sz_end), optarg); 150 | if (ret < 0) { 151 | ERROR("invalid size: %s\n", optarg); 152 | return -1; 153 | } 154 | set_mode(task, UP_MODE); 155 | break; 156 | case 's': 157 | if (get_mode(task) == UP_MODE) { 158 | ERROR("incompatible with -s and -b|-e\n"); 159 | return -1; 160 | } 161 | ret = str_to_size(&(task->sz_same), optarg); 162 | if (ret < 0) { 163 | ERROR("invalid size: %s\n", optarg); 164 | return -1; 165 | } 166 | set_mode(task, SAME_MODE); 167 | break; 168 | case 'u': 169 | ret = str_to_size(&(task->sz_buf), optarg); 170 | if (ret < 0) { 171 | ERROR("invalid size: %s\n", optarg); 172 | return -1; 173 | } 174 | break; 175 | case 'i': 176 | if (!is_file(optarg)) { 177 | ERROR("not existed or file: %s\n", optarg); 178 | return -1; 179 | } 180 | 181 | strncpy(task->org, optarg, MAX_PATH_LEN); 182 | task->set_org = true; 183 | break; 184 | case 'p': 185 | task->percent = atoi(optarg); 186 | if (task->percent < 0 || task->percent > 99) { 187 | ERROR("invalid percent [1, 99]: %s\n", optarg); 188 | return -1; 189 | } 190 | break; 191 | case 'j': 192 | task->jobs = (int)atoi(optarg); 193 | if (task->jobs <= 0) { 194 | ERROR("invalid jobs: %s\n", optarg); 195 | return -1; 196 | } else if (task->jobs > MAX_JOBS) { 197 | ERROR("over limit [1, %d]: %s\n", MAX_JOBS, optarg); 198 | return -1; 199 | } 200 | break; 201 | case 'h': 202 | help(); 203 | exit(0); 204 | case '?': 205 | ERROR("invalid option -%c\n", optopt); 206 | help(); 207 | return -1; 208 | case ':': 209 | ERROR("option -%c requires an argument\n", optopt); 210 | help(); 211 | return -1; 212 | } 213 | } 214 | return 0; 215 | } 216 | 217 | struct rwtask *init_rwtask(void) 218 | { 219 | int ret = -EINVAL; 220 | struct rwtask *task; 221 | 222 | task = malloc(sizeof(struct rwtask)); 223 | if (!task) { 224 | ERROR("malloc for task failed\n"); 225 | return NULL; 226 | } 227 | memset(task, 0, sizeof(struct rwtask)); 228 | 229 | /* run mode */ 230 | set_mode(task, AUTO_MODE); 231 | /* run times */ 232 | task->times = DEFAULT_TIMES; 233 | /* percent of all flash */ 234 | task->percent = DEFAULT_PERCENT; 235 | /* update sys info */ 236 | task->dir = abs_path("."); 237 | if (!task->dir) 238 | goto err; 239 | ret = update_sys_info(&task->sys, task->dir); 240 | if (ret) 241 | goto err; 242 | /* parallel writing worker */ 243 | task->jobs = DEFAULT_JOBS; 244 | /* set orgin flag */ 245 | task->set_org = false; 246 | /* buf size */ 247 | if (task->sys.total_ddr > DEFAULT_BUF_SIZE_DDR_LIMIT) 248 | num_to_size(&(task->sz_buf), DEFAULT_LARGE_BUF_SIZE); 249 | else 250 | num_to_size(&(task->sz_buf), DEFAULT_SMALL_BUF_SIZE); 251 | 252 | return task; 253 | err: 254 | free(task); 255 | return NULL; 256 | } 257 | 258 | int check_args(struct rwtask *task) 259 | { 260 | int ret; 261 | struct sys_info *sys = &task->sys; 262 | 263 | /* update flash information if new dir */ 264 | ret = update_sys_info(&task->sys, task->dir); 265 | if (ret) 266 | return -1; 267 | 268 | /* min_free_bytes */ 269 | task->min_free_bytes = 100 - task->percent; 270 | task->min_free_bytes *= task->sys.total_flash; 271 | task->min_free_bytes /= 100; 272 | 273 | /* set orgin file */ 274 | snprintf(task->org, MAX_PATH_LEN, "%s/%s", task->dir, DEFAULT_ORGIN_FILE); 275 | 276 | /* buf_size */ 277 | if (task->sz_buf.bytes >= 16 * MB) { 278 | ERROR("too large buf, should not greater than 16MB free memory\n"); 279 | return -1; 280 | } 281 | /* In case of little ram has no enough buffer */ 282 | if (task->sz_buf.bytes >= sys->free_ddr * 60 / 100) { 283 | ERROR("too large buf, should not greater than 60%% free memory\n"); 284 | return -1; 285 | } 286 | 287 | /* begin_size & end_size */ 288 | if (get_mode(task) == UP_MODE) { 289 | if (!task->sz_begin.bytes) 290 | num_to_size(&(task->sz_begin), DEFAULT_UP_MODE_BEGIN_SIZE); 291 | if (!task->sz_end.bytes) 292 | num_to_size(&(task->sz_end), DEFAULT_UP_MODE_END_SIZE); 293 | if (task->sz_end.bytes > sys->max_file_size(sys)) 294 | num_to_size(&(task->sz_end), sys->max_file_size(sys)); 295 | if (task->sz_begin.bytes > task->sz_end.bytes) { 296 | ERROR("[UP Mode]: begin size %s larger than end size %s\n", 297 | task->sz_begin.str, task->sz_end.str); 298 | return -1; 299 | } 300 | } else if (get_mode(task) == SAME_MODE) { 301 | if (task->sz_same.bytes > sys->max_file_size(sys)) 302 | num_to_size(&(task->sz_same), sys->max_file_size(sys)); 303 | } 304 | 305 | /* orgin file */ 306 | if (task->set_org && !is_file(task->org)) { 307 | ERROR("not found %s for orgin file\n", task->org); 308 | return -1; 309 | } 310 | 311 | return 0; 312 | } 313 | 314 | void print_head(struct rwtask *task) 315 | { 316 | time_t t = time(NULL); 317 | struct sys_info *sys = &task->sys; 318 | 319 | INFO("\n\trwcheck: do read and write check\n\n"); 320 | INFO("\tversion: %s\n", VERSION); 321 | INFO("\tbuild: %s\n", COMPILE); 322 | INFO("\tdate: %s\n", ctime(&t)); 323 | INFO("\tfree/total ddr: %llu/%llu MB\n", 324 | sys->free_ddr / MB, sys->total_ddr / MB); 325 | INFO("\tfree/total flash: %llu/%llu MB\n", 326 | sys->free_flash(sys) / MB, sys->total_flash / MB); 327 | INFO("\tflash filesystem %s\n", sys->fs_name(sys)); 328 | INFO("\tset mode to %s\n", get_strmode(task)); 329 | if (get_mode(task) == SAME_MODE) { 330 | INFO("\tset file size to %s\n", task->sz_same.str); 331 | } else if (get_mode(task) == UP_MODE) { 332 | INFO("\tset file begin size to %s\n", task->sz_begin.str); 333 | INFO("\tset file end size to %s\n", task->sz_end.str); 334 | } 335 | INFO("\tset times to %u\n", task->times); 336 | INFO("\tset max percent of total space to %d%%\n", task->percent); 337 | INFO("\tset buf size to %s\n", task->sz_buf.str); 338 | INFO("\tset check diretory as %s\n", task->dir); 339 | INFO("\tset orgin file as %s\n", task->org); 340 | INFO("\tset jobs as %d\n", task->jobs); 341 | } 342 | 343 | int init_orgin_file(struct rwtask *task) 344 | { 345 | if (!task->org[0]) 346 | return -ENOENT; 347 | 348 | if (task->set_org) 349 | return 0; 350 | 351 | if (is_existed(task->org)) 352 | return 0; 353 | 354 | /* create orgin file */ 355 | return copy_file(RANDOM_DATA, task->org, DEFAULT_ORG_SIZE, 4 * KB); 356 | } 357 | 358 | int deinit_orgin_file(struct rwtask *task) 359 | { 360 | /* 361 | * we remove old orgin to ensure data difference each time 362 | * do not remove before create, to avoid destorying problem scene 363 | */ 364 | if (!task->set_org) 365 | remove(task->org); 366 | return 0; 367 | } 368 | 369 | struct pthread_arg { 370 | struct rwtask *task; 371 | char path[128]; 372 | unsigned long long bytes; 373 | int ret; 374 | }; 375 | 376 | void *sub_thread_do_create(void *_arg) 377 | { 378 | struct pthread_arg *arg = (struct pthread_arg *)_arg; 379 | struct rwtask *task = arg->task; 380 | unsigned long long bytes = arg->bytes; 381 | const char *path = arg->path; 382 | int ret = -1; 383 | 384 | if (is_existed(path)) { 385 | arg->ret = 0; 386 | pthread_exit(arg); 387 | } 388 | 389 | INFO("\r\tcreate\t: %s ... ", path); 390 | 391 | ret = copy_file(task->org, path, bytes, task->sz_buf.bytes); 392 | if (ret) 393 | goto err; 394 | 395 | /* the last 2 btyes are reserved for saving crc */ 396 | ret = append_crc(path); 397 | if (ret) 398 | goto err; 399 | 400 | INFO("\r\tcreate\t: %s ... OK (%lluK)\n", path, bytes / KB); 401 | err: 402 | arg->ret = ret; 403 | pthread_exit(arg); 404 | } 405 | 406 | unsigned long long get_task_size_auto_mode(struct rwtask *task) 407 | { 408 | struct sys_info *sys = &task->sys; 409 | unsigned long long free_bytes = sys->free_flash(sys); 410 | unsigned long long test_bytes = 0; 411 | 412 | test_bytes = round_down(free_bytes / 2, KB); 413 | while (test_bytes && free_bytes - test_bytes < task->min_free_bytes) 414 | test_bytes = round_down(test_bytes / 2, KB); 415 | test_bytes = min(test_bytes, DEFAULT_AUTO_MODE_MAX_SIZE); 416 | 417 | if (test_bytes < DEFAULT_AUTO_MODE_END_SIZE) 418 | return 0; 419 | return test_bytes; 420 | } 421 | 422 | unsigned long long get_task_size_same_mode(struct rwtask *task) 423 | { 424 | struct sys_info *sys = &task->sys; 425 | unsigned long long free_bytes = sys->free_flash(sys); 426 | 427 | if (free_bytes <= task->sz_same.bytes) 428 | return 0; 429 | return task->sz_same.bytes; 430 | } 431 | 432 | unsigned long long get_task_size_up_mode(struct rwtask *task) 433 | { 434 | struct sys_info *sys = &task->sys; 435 | unsigned long long free_bytes = sys->free_flash(sys); 436 | 437 | if (task->sz_cur.bytes == 0) 438 | task->sz_cur.bytes = task->sz_begin.bytes; 439 | else 440 | task->sz_cur.bytes *= DEFAULT_UP_MODE_POWER; 441 | 442 | /* larger than end size, reset */ 443 | if (task->sz_cur.bytes > task->sz_end.bytes) 444 | task->sz_cur.bytes = task->sz_begin.bytes; 445 | /* larger than free size, reset */ 446 | if (task->sz_cur.bytes >= free_bytes) 447 | task->sz_cur.bytes = task->sz_begin.bytes; 448 | /* larger than limit size, reset */ 449 | if (task->min_free_bytes > free_bytes - task->sz_cur.bytes) 450 | task->sz_cur.bytes = task->sz_begin.bytes; 451 | return task->sz_cur.bytes; 452 | } 453 | 454 | unsigned long long get_test_size(struct rwtask *task) 455 | { 456 | struct sys_info *sys = &task->sys; 457 | unsigned long long free_bytes = sys->free_flash(sys); 458 | unsigned long long test_bytes = 0; 459 | 460 | if (free_bytes == 0 || free_bytes <= task->min_free_bytes) 461 | return 0; 462 | 463 | switch (get_mode(task)) { 464 | case AUTO_MODE: 465 | test_bytes = get_task_size_auto_mode(task); 466 | break; 467 | case SAME_MODE: 468 | test_bytes = get_task_size_same_mode(task); 469 | break; 470 | case UP_MODE: 471 | test_bytes = get_task_size_up_mode(task); 472 | break; 473 | } 474 | 475 | if (!test_bytes) 476 | return 0; 477 | 478 | if (task->min_free_bytes > free_bytes - test_bytes) 479 | return 0; 480 | 481 | if (test_bytes > sys->max_file_size(sys)) 482 | test_bytes = sys->max_file_size(sys); 483 | 484 | return test_bytes; 485 | } 486 | 487 | #define testfile(task, path, len, num, job) \ 488 | do { \ 489 | if (task->jobs == 1) \ 490 | snprintf(path, len, "%s/" DEFAULT_OUTPUT ".%d", \ 491 | task->dir, num); \ 492 | else \ 493 | snprintf(path, len, "%s/" DEFAULT_OUTPUT ".%d_%d", \ 494 | task->dir, num, job); \ 495 | } while(0) 496 | 497 | int do_create(struct rwtask *task) 498 | { 499 | int num = 0, ret = 0, job; 500 | unsigned long long size; 501 | struct pthread_arg pargs[MAX_JOBS] = {{0}}; 502 | pthread_t pid[MAX_JOBS] = {0}; 503 | 504 | INFO("\t--- CREATE ---\n"); 505 | 506 | if (get_mode(task) == UP_MODE) 507 | num_to_size(&(task->sz_cur), 0); 508 | 509 | while ((size = get_test_size(task))) { 510 | /* create thread */ 511 | for (job = 0; job < task->jobs; job++) { 512 | struct pthread_arg *p = &pargs[job]; 513 | 514 | p->task = task; 515 | p->bytes = size / task->jobs; 516 | testfile(task, p->path, 128, num, job); 517 | if (is_existed(p->path)) { 518 | ret = 0; 519 | goto out; 520 | } 521 | 522 | ret = pthread_create(pid + job, NULL, 523 | sub_thread_do_create, (void *)p); 524 | if (ret) { 525 | ERROR("pthread create failed\n"); 526 | goto out; 527 | } 528 | } 529 | /* waiting end */ 530 | for (job = 0; job < task->jobs; job++) { 531 | struct pthread_arg *p; 532 | 533 | if (!pid[job]) 534 | continue; 535 | 536 | pthread_join(pid[job], (void **)&p); 537 | if ((ret = p->ret)) { 538 | ERROR("wait pthread end failed\n"); 539 | goto out; 540 | } 541 | } 542 | num++; 543 | } 544 | 545 | return 0; 546 | out: 547 | for (job = 0; job < task->jobs; job++) { 548 | if (!pid[job]) 549 | continue; 550 | pthread_cancel(pid[job]); 551 | } 552 | return ret; 553 | } 554 | 555 | int do_check(struct rwtask *task) 556 | { 557 | char file[128]; 558 | int num = 0, job, ret = -1; 559 | struct sys_info *sys = &task->sys; 560 | unsigned short crc; 561 | 562 | /* do clean memory before check to ensure read data from flash */ 563 | sys->clean_mem(); 564 | 565 | INFO("\t--- CHECK ---\n"); 566 | 567 | while (1) { 568 | for (job = 0; job < task->jobs; job++) { 569 | testfile(task, file, 128, num, job); 570 | if (!is_file(file)) { 571 | task->count = num; 572 | return 0; 573 | } 574 | 575 | INFO("\tcheck\t: %s ... ", file); 576 | 577 | ret = get_crc16_by_path(&crc, file, CRC16_MAX_BYTES); 578 | if (ret) 579 | return ret; 580 | if (check_crc16(crc) == true) { 581 | INFO("OK\n"); 582 | continue; 583 | } 584 | testfile(task, file, 128, num + 1, job); 585 | if (is_file(file)) { 586 | INFO("FAILED (crc error)\n"); 587 | errno = EIO; 588 | testfile(task, file, 128, num, job); 589 | ERROR("%s crc error\n", file); 590 | return -1; 591 | } 592 | INFO("OK: ignore the last one\n"); 593 | } 594 | num++; 595 | } 596 | } 597 | 598 | int do_remove(struct rwtask *task) 599 | { 600 | struct sys_info *sys = &task->sys; 601 | int num, jobs; 602 | char file[100]; 603 | 604 | INFO("\t--- REMOVE ---\n"); 605 | for (num = task->count - 1; num >= 0; num--) { 606 | for (jobs = 0; jobs < task->jobs; jobs++) { 607 | testfile(task, file, 100, num, jobs); 608 | if (!is_existed(file)) 609 | goto out; 610 | 611 | INFO("\tremove\t: %s ... ", file); 612 | if (remove(file) != 0) { 613 | ERROR("remove %s failed", file); 614 | return -EINVAL; 615 | } 616 | INFO("OK\n"); 617 | } 618 | } 619 | 620 | out: 621 | if (task->min_free_bytes >= sys->free_flash(sys)) { 622 | errno = ENOSPC; 623 | ERROR("no enough space as already used over %u%%\n", 624 | task->percent); 625 | return -1; 626 | } 627 | sync(); 628 | return 0; 629 | } 630 | 631 | int begin(struct rwtask *task) 632 | { 633 | int ret = 0; 634 | 635 | print_head(task); 636 | while (task->times-- > 0) { 637 | INFO("\n"); 638 | 639 | ret = init_orgin_file(task); 640 | if (ret) 641 | return ret; 642 | 643 | ret = do_create(task); 644 | if (ret) 645 | return ret; 646 | 647 | ret = do_check(task); 648 | if (ret) 649 | return ret; 650 | 651 | ret = do_remove(task); 652 | if (ret) 653 | return ret; 654 | 655 | ret = deinit_orgin_file(task); 656 | if (ret) 657 | return ret; 658 | } 659 | 660 | return 0; 661 | } 662 | 663 | int main(int argc, char **argv) 664 | { 665 | int ret = -1; 666 | struct rwtask *task; 667 | 668 | setbuf(stdout, NULL); 669 | setbuf(stderr, NULL); 670 | 671 | task = init_rwtask(); 672 | if (!task) 673 | goto err; 674 | 675 | ret = parse_args(task, argc, argv); 676 | if (ret) 677 | goto err; 678 | 679 | ret = check_args(task); 680 | if (ret) 681 | goto err; 682 | 683 | ret = begin(task); 684 | if (ret) 685 | goto err; 686 | 687 | return 0; 688 | err: 689 | show_errlog(); 690 | return ret; 691 | } 692 | --------------------------------------------------------------------------------