kylemanna
3/12/2014 - 9:28 PM

EMMC Lifetime Test Utility

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;
}