Created
March 12, 2014 21:28
-
-
Save kylemanna/9516777 to your computer and use it in GitHub Desktop.
EMMC Lifetime Test Utility
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <unistd.h> | |
#include <assert.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <dirent.h> | |
#include <stdint.h> | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
#include <fcntl.h> | |
#include <time.h> | |
#ifdef __ANDROID__ | |
#include <android/log.h> | |
#endif | |
#define STATESIZE 64 | |
#define LOG_CSV | |
/* File size = blk_size * blk_cnt */ | |
const size_t g_blk_size = 512; | |
const size_t g_blk_cnt = 4096; | |
const char *app_name = "EMMCBurn"; | |
int write_urandom(const char *filename, size_t blk_size, size_t blk_cnt) | |
{ | |
int ret = 0; | |
int fd; | |
size_t i, j; | |
//const int flags = O_CREAT | O_SYNC | O_WRONLY; | |
const int flags = O_CREAT | O_WRONLY; | |
const int mode = S_IRUSR | S_IWUSR; | |
int32_t buf[blk_size/4]; | |
assert(filename != NULL); | |
assert(blk_size > sizeof(int32_t)); | |
assert(blk_size && (sizeof(int32_t) - 1)); /* Multiple of 4 */ | |
fd = open(filename, flags, mode); | |
if (fd < 0) { | |
fprintf(stderr, "Failed to open %s, fd = %d\n", filename, fd); | |
return fd; | |
} | |
srandom((unsigned int)time(NULL)); | |
for (i = 0; i < blk_cnt; i++) { | |
for (j = 0; j < blk_size/sizeof(int32_t); j++) { | |
/* Generate a block */ | |
buf[j] = random(); | |
if (ret < 0) { | |
fprintf(stderr, "Failed to get random, ret = %d\n", ret); | |
return -1; | |
} | |
} | |
ret = write(fd, buf, sizeof(buf)); | |
if (ret < 0) { | |
fprintf(stderr, "Failed to write, ret = %d\n", ret); | |
return -1; | |
} | |
} | |
fsync(fd); | |
close(fd); | |
return 0; | |
} | |
static int mkdir_r(const char *dir) | |
{ | |
/* FIXME: Reckless, no error checking */ | |
char tmp[256]; | |
char *p = NULL; | |
size_t len; | |
snprintf(tmp, sizeof(tmp),"%s",dir); | |
len = strlen(tmp); | |
if(tmp[len - 1] == '/') | |
tmp[len - 1] = 0; | |
for(p = tmp + 1; *p; p++) | |
if(*p == '/') { | |
*p = 0; | |
mkdir(tmp, S_IRWXU); | |
*p = '/'; | |
} | |
mkdir(tmp, S_IRWXU); | |
return 0; | |
} | |
static int clean_data(const char *dir, int64_t max) | |
{ | |
/* Fixme, function not threadsafe, readdir_r() */ | |
DIR *fd_dir; | |
struct dirent *dirp; | |
int ret = 0; | |
if (max < 0) | |
return 0; | |
if (!(fd_dir = opendir(dir))) { | |
fprintf(stderr, "Unable to open dir = %s\n", dir); | |
return -1; | |
} | |
while ((dirp = readdir(fd_dir)) != NULL) { | |
long long int tmp = 0; | |
ret = sscanf(dirp->d_name, "rand_%lld", &tmp); | |
if (ret == 1 && tmp < max) { | |
char buf[256]; | |
sprintf(buf, "%s/%s", dir, dirp->d_name); | |
ret = remove(buf); | |
if (ret) { | |
fprintf(stderr, "Unable to remove %s\n", buf); | |
return -1; | |
} | |
} | |
} | |
closedir(fd_dir); | |
return 0; | |
} | |
static int64_t get_largest_idx(const char *dir) | |
{ | |
DIR *fd_dir; | |
struct dirent *dirp; | |
int64_t largest = 0; | |
if (!(fd_dir = opendir(dir))) { | |
printf( "Unable to open dir = %s\n", dir); | |
return -1; | |
} | |
while ((dirp = readdir(fd_dir)) != NULL) { | |
long long int tmp = 0; | |
//printf(" %s\n",dirp->d_name); | |
sscanf(dirp->d_name, "rand_%lld", &tmp); | |
if (tmp > largest) | |
largest = tmp; | |
} | |
closedir(fd_dir); | |
//printf("Largest: %d\n", largest); | |
return largest; | |
} | |
static inline uint8_t strtou8(const char *s, off_t off) | |
{ | |
off = 1023 - off * 2; | |
uint8_t b0 = s[off] - '0'; | |
uint8_t b1 = s[off - 1] - '0'; | |
return (b1 << 4) | b0; | |
} | |
static int get_health_status(const int mmc_dev, uint8_t *slc, uint8_t *mlc) | |
{ | |
int fd; | |
int ret = -1; | |
const int flags = O_RDONLY; | |
const int mode = S_IRUSR | S_IWUSR; | |
const int slc_offset = 87; | |
const int mlc_offset = 94; | |
char buf[2048]; | |
ssize_t cnt; | |
char filename[256]; | |
snprintf(filename, sizeof(filename), "/d/mmc%u/mmc%u:0001/ext_csd", mmc_dev, mmc_dev); | |
fd = open(filename, flags, mode); | |
cnt = read(fd, buf, sizeof(buf)); | |
if (cnt > (mlc_offset + 1) && cnt > (slc_offset + 1)) { | |
*slc = strtou8(buf, slc_offset); | |
*mlc = strtou8(buf, mlc_offset); | |
ret = 0; | |
} | |
close(fd); | |
return ret; | |
} | |
static int log_data(const char *dir, unsigned long long megabytes) | |
{ | |
int fd; | |
int ret; | |
const int flags = O_CREAT | O_WRONLY | O_APPEND; | |
const int mode = S_IRUSR | S_IWUSR; | |
uint8_t mlc = 0xff, slc = 0xff; | |
char log_buf[256]; | |
char filename[256]; | |
snprintf(filename, sizeof(filename), "%s/log", dir); | |
fd = open(filename, flags, mode); | |
if (fd < 0) { | |
fprintf(stderr, "Failed to open %s, fd = %d\n", filename, fd); | |
return fd; | |
} | |
get_health_status(0, &slc, &mlc); | |
#if defined(LOG_JSON) | |
ret = snprintf(log_buf, sizeof(log_buf), | |
"\"%d\":{ \"data\":{\"written\":\"%lld MB\"}, \"health\":{\"slc\":%u, \"mlc\":%u }},\n", | |
(int)time(NULL), megabytes, slc, mlc); | |
#elif defined(LOG_CSV) | |
ret = snprintf(log_buf, sizeof(log_buf), | |
"%d,%lld,%u,%u\n", (int)time(NULL), megabytes, slc, mlc); | |
#else | |
/* Human readable */ | |
ret = snprintf(log_buf, sizeof(log_buf), | |
"%d :: Data Written = %lld MB, Health SLC = %u, MLC = %u\n", | |
(int)time(NULL), megabytes, slc, mlc); | |
#endif | |
#ifdef __ANDROID__ | |
__android_log_print(ANDROID_LOG_INFO, app_name, "%s", log_buf); | |
#endif | |
write(fd, log_buf, ret); | |
close(fd); | |
return 0; | |
} | |
static int spin() | |
{ | |
char *base_dir = "/data/emmc-test/"; | |
char data_dir[256]; | |
int start; | |
long long int i; | |
/* Determine base file name */ | |
sprintf(data_dir, "%s/data", base_dir); | |
mkdir_r(data_dir); | |
/* Find highest number index */ | |
start = get_largest_idx(data_dir); | |
clean_data(data_dir, start - 100); | |
/* Spin */ | |
for (i = start; ; i++) { | |
long long int megabytes = i * g_blk_size * g_blk_cnt / 1024 / 1024; | |
char filename[64]; | |
/* Determine next file name */ | |
snprintf(filename, sizeof(filename), "%s/rand_%09lld", data_dir, i); | |
/* Write file data */ | |
write_urandom(filename, g_blk_size, g_blk_cnt); | |
clean_data(data_dir, i - 20); | |
/* Write log entries */ | |
if (megabytes % 100 == 0) { | |
log_data(base_dir, megabytes); | |
} | |
} | |
return 0; | |
} | |
int main(int argc, char *argv[]) | |
{ | |
int fork_requested = 1; | |
/* Parse command line arguments */ | |
/* TODO: fork, block_size, block_cnt, iterations */ | |
/* Fork if requested */ | |
if (fork_requested) { | |
pid_t pid = fork(); | |
if (pid == 0) { | |
int i; | |
/* If child */ | |
/* Close FDs so that the caller doesn't wait */ | |
close(STDIN_FILENO); | |
close(STDOUT_FILENO); | |
close(STDERR_FILENO); | |
/* Become the process group leader */ | |
setsid(); | |
/* Copy args so that execvp() can use them */ | |
char * args[argc]; | |
args[argc - 1] = NULL; | |
for (i = 0; i < (argc - 1); i++) | |
args[i] = argv[i + 1]; | |
#ifdef __ANDROID__ | |
__android_log_print(ANDROID_LOG_DEBUG, app_name, "Starting %s", args[0]); | |
#endif | |
/* Actually run the program */ | |
spin(); | |
#ifdef __ANDROID__ | |
__android_log_print(ANDROID_LOG_DEBUG, app_name, "Completed %s", args[0]); | |
#endif | |
_exit(0); | |
} | |
} else { | |
spin(); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Is this a freeware?