EMMC Lifetime Test Utility
#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;
}