Home | History | Annotate | Download | only in sdcard
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  *  * Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  *  * Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in
     12  *    the documentation and/or other materials provided with the
     13  *    distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     18  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
     19  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
     22  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     25  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 #include <cstdio>
     30 #include <cstdlib>
     31 #include <ctime>
     32 #include <errno.h>
     33 #include <fcntl.h>
     34 #include <getopt.h>
     35 #include <limits.h>
     36 #include <string.h>
     37 #include <sys/stat.h>
     38 #include <linux/fadvise.h>
     39 #include <unistd.h>
     40 #include <fts.h>
     41 
     42 #include "stopwatch.h"
     43 #include "sysutil.h"
     44 #include "testcase.h"
     45 
     46 // Stress test for the sdcard. Use this to generate some load on the
     47 // sdcard and collect performance statistics. The output is either a
     48 // human readable report or the raw timing samples that can be
     49 // processed using another tool.
     50 //
     51 // Needs debugfs:
     52 //   adb root;
     53 //   adb shell mount -t debugfs none /sys/kernel/debug
     54 //
     55 // The following tests are defined (value of the --test flag):
     56 //  write:       Open a file write some random data and close.
     57 //  read:        Open a file read it and close.
     58 //  read_write:  Combine readers and writers.
     59 //  open_create: Open|create an non existing file.
     60 //
     61 // For each run you can control how many processes will run the test in
     62 // parallel to simulate a real load (--procnb flag)
     63 //
     64 // For each process, the test selected will be executed many time to
     65 // get a meaningful average/min/max (--iterations flag)
     66 //
     67 // Use --dump to also get the raw data.
     68 //
     69 // For read/write tests, size is the number of Kbytes to use.
     70 //
     71 // To build: mmm system/extras/tests/sdcard/Android.mk SDCARD_TESTS=1
     72 //
     73 // Examples:
     74 // adb shell /system/bin/sdcard_perf_test --test=read --size=1000 --chunk-size=100 --procnb=1 --iterations=10 --dump > /tmp/data.txt
     75 // adb shell /system/bin/sdcard_perf_test --test=write --size=1000 --chunk-size=100 --procnb=1 --iterations=100 --dump > /tmp/data.txt
     76 //
     77 // To watch the memory: cat /proc/meminfo
     78 // If the phone crashes, look at /proc/last_kmsg on reboot.
     79 //
     80 // TODO: It would be cool if we could play with various fadvise()
     81 // strategies in here to see how tweaking read-ahead changes things.
     82 //
     83 
     84 extern char *optarg;
     85 extern int optind, opterr, optopt;
     86 
     87 // TODO: No clue where fadvise is. Disabled for now.
     88 #define FADVISE(fd, off, len, advice) (void)0
     89 
     90 #ifndef min
     91 #define min(a,b) (((a)>(b))?(b):(a))
     92 #endif
     93 
     94 namespace  {
     95 using android::kernelVersion;
     96 using android::kMinKernelVersionBufferSize;
     97 using android::schedFeatures;
     98 using android::kMinSchedFeaturesBufferSize;
     99 using android_test::StopWatch;
    100 using android::writePidAndWaitForReply;
    101 using android::waitForChildrenAndSignal;
    102 using android::waitForChildrenOrExit;
    103 using android_test::TestCase;
    104 
    105 const char *kAppName = "sdcard_perf_test";
    106 const char *kTestDir = "/sdcard/perf";
    107 const bool kVerbose = false;
    108 
    109 // Used by getopt to parse the command line.
    110 struct option long_options[] = {
    111     {"size", required_argument, 0, 's'},
    112     {"chunk-size", required_argument, 0, 'S'},
    113     {"depth", required_argument, 0, 'D'},
    114     {"iterations",  required_argument, 0, 'i'},
    115     {"procnb",  required_argument, 0, 'p'},
    116     {"test",  required_argument, 0, 't'},
    117     {"dump",  no_argument, 0, 'd'},
    118     {"cpu-scaling",  no_argument, 0, 'c'},
    119     {"sync",  required_argument, 0, 'f'},
    120     {"truncate", no_argument, 0, 'e'},
    121     {"no-new-fair-sleepers", no_argument, 0, 'z'},
    122     {"no-normalized-sleepers", no_argument, 0, 'Z'},
    123     {"fadvise", required_argument, 0, 'a'},
    124     {"help", no_argument, 0, 'h'},
    125     {0, 0, 0, 0},
    126 };
    127 
    128 void usage()
    129 {
    130     printf("sdcard_perf_test --test=write|read|read_write|open_create|traverse [options]\n\n"
    131            "  -t --test:        Select the test.\n"
    132            "  -s --size:        Size in kbytes of the data.\n"
    133            "  -S --chunk-size:  Size of a chunk. Default to size ie 1 chunk.\n"
    134            "                    Data will be written/read using that chunk size.\n"
    135            "  -D --depth:       Depth of directory tree to create for traversal.\n"
    136            "  -i --iterations:  Number of time a process should carry its task.\n"
    137            "  -p --procnb:      Number of processes to use.\n"
    138            "  -d --dump:        Print the raw timing on stdout.\n"
    139            "  -c --cpu-scaling: Enable cpu scaling.\n"
    140            "  -s --sync: fsync|sync Use fsync or sync in write test. Default: no sync call.\n"
    141            "  -e --truncate:    Truncate to size the file on creation.\n"
    142            "  -z --no-new-fair-sleepers: Turn them off. You need to mount debugfs.\n"
    143            "  -Z --no-normalized-sleepers: Turn them off. You need to mount debugfs.\n"
    144            "  -a --fadvise:     Specify an fadvise policy (not supported).\n"
    145            );
    146 }
    147 
    148 // Print command line, pid, kernel version, OOM adj and scheduler.
    149 void printHeader(int argc, char **argv, const TestCase& testCase)
    150 {
    151     printf("# Command: ");
    152     for (int i = 0; i < argc; ++i)
    153     {
    154         printf("%s ", argv[i]);
    155     }
    156     printf("\n");
    157 
    158     printf("# Pid: %d\n", getpid());
    159 
    160     {
    161         char buffer[kMinKernelVersionBufferSize] = {0, };
    162         if (kernelVersion(buffer, sizeof(buffer)) > 0)
    163         {
    164             printf("# Kernel: %s", buffer);
    165         }
    166     }
    167 
    168     // Earlier on, running this test was crashing the phone. It turned
    169     // out that it was using too much memory but its oom_adj value was
    170     // -17 which means disabled. -16 is the system_server and 0 is
    171     // typically what applications run at. The issue is that adb runs
    172     // at -17 and so is this test. We force oom_adj to 0 unless the
    173     // oom_adj option has been used.
    174     // TODO: We talked about adding an option to control oom_adj, not
    175     // sure if we still need that.
    176     int oomAdj = android::pidOutOfMemoryAdj();
    177 
    178     printf("# Oom_adj: %d ", oomAdj);
    179     if (oomAdj < 0)
    180     {
    181         android::setPidOutOfMemoryAdj(0);
    182         printf("adjuted to %d", android::pidOutOfMemoryAdj());
    183     }
    184     printf("\n");
    185 
    186     {
    187         char buffer[kMinSchedFeaturesBufferSize] = {0, };
    188         if (schedFeatures(buffer, sizeof(buffer)) > 0)
    189         {
    190             printf("# Sched features: %s", buffer);
    191         }
    192     }
    193     printf("# Fadvise: %s\n", testCase.fadviseAsStr());
    194 }
    195 
    196 // Remove all the files under kTestDir and clear the caches.
    197 void cleanup() {
    198     android::resetDirectory(kTestDir);
    199     android::syncAndDropCaches();  // don't pollute runs.
    200 }
    201 
    202 // @param argc, argv have a wild guess.
    203 // @param[out] testCase Structure built from the cmd line args.
    204 void parseCmdLine(int argc, char **argv, TestCase *testCase)\
    205 {
    206     int c;
    207 
    208     while(true)
    209     {
    210         // getopt_long stores the option index here.
    211         int option_index = 0;
    212 
    213         c = getopt_long (argc, argv,
    214                          "hS:s:D:i:p:t:dcf:ezZa:",
    215                          long_options,
    216                          &option_index);
    217         // Detect the end of the options.
    218         if (c == -1) break;
    219 
    220         switch (c)
    221         {
    222             case 's':
    223                 testCase->setDataSize(atoi(optarg) * 1024);
    224                 break;
    225             case 'S':
    226                 testCase->setChunkSize(atoi(optarg) * 1024);
    227                 break;
    228             case 'D': // tree depth
    229                 testCase->setTreeDepth(atoi(optarg));
    230                 break;
    231             case 'i':
    232                 testCase->setIter(atoi(optarg));
    233                 printf("# Iterations: %d\n", testCase->iter());
    234                 break;
    235             case 'p':
    236                 testCase->setNproc(atoi(optarg));
    237                 printf("# Proc nb: %d\n", testCase->nproc());
    238                 break;
    239             case 't':
    240                 testCase->setTypeFromName(optarg);
    241                 printf("# Test name %s\n", testCase->name());
    242                 break;
    243             case 'd':
    244                 testCase->setDump();
    245                 break;
    246             case 'c':
    247                 printf("# Cpu scaling is on\n");
    248                 testCase->setCpuScaling();
    249                 break;
    250             case 'f':
    251                 if (strcmp("sync", optarg) == 0) {
    252                     testCase->setSync(TestCase::SYNC);
    253                 } else if (strcmp("fsync", optarg) == 0) {
    254                     testCase->setSync(TestCase::FSYNC);
    255                 }
    256                 break;
    257             case 'e':  // e for empty
    258                 printf("# Will truncate to size\n");
    259                 testCase->setTruncateToSize();
    260                 break;
    261             case 'z':  // no new fair sleepers
    262                 testCase->setNewFairSleepers(false);
    263                 break;
    264             case 'Z':  // no normalized sleepers
    265                 testCase->setNormalizedSleepers(false);
    266                 break;
    267             case 'a':  // fadvise
    268                 testCase->setFadvise(optarg);
    269                 break;
    270             case 'h':
    271                 usage();
    272                 exit(0);
    273             default:
    274                 fprintf(stderr, "Unknown option %s\n", optarg);
    275                 exit(EXIT_FAILURE);
    276         }
    277     }
    278 }
    279 
    280 // ----------------------------------------------------------------------
    281 // READ TEST
    282 
    283 // Read a file.  We use a new file each time to avoid any caching
    284 // effect that would happen if we were reading the same file each
    285 // time.
    286 // @param chunk buffer large enough where the chunk read are written.
    287 // @param idx iteration number.
    288 // @param testCase has all the timers and paramters to run the test.
    289 
    290 bool readData(char *const chunk, const int idx, TestCase *testCase)
    291 {
    292     char filename[80] = {'\0',};
    293 
    294     sprintf(filename, "%s/file-%d-%d", kTestDir, idx, getpid());
    295 
    296     testCase->openTimer()->start();
    297     int fd = open(filename, O_RDONLY);
    298     testCase->openTimer()->stop();
    299 
    300     if (fd < 0)
    301     {
    302         fprintf(stderr, "Open read only failed.");
    303         return false;
    304     }
    305     FADVISE(fd, 0, 0, testCase->fadvise());
    306 
    307     size_t left = testCase->dataSize();
    308     pid_t *pid = (pid_t*)chunk;
    309     while (left > 0)
    310     {
    311         char *dest = chunk;
    312         size_t chunk_size = testCase->chunkSize();
    313 
    314         if (chunk_size > left)
    315         {
    316             chunk_size = left;
    317             left = 0;
    318         }
    319         else
    320         {
    321             left -= chunk_size;
    322         }
    323 
    324         testCase->readTimer()->start();
    325         while (chunk_size > 0)
    326         {
    327             ssize_t s = read(fd, dest, chunk_size);
    328             if (s < 0)
    329             {
    330                 fprintf(stderr, "Failed to read.\n");
    331                 close(fd);
    332                 return false;
    333             }
    334             chunk_size -= s;
    335             dest += s;
    336         }
    337         testCase->readTimer()->stop();
    338     }
    339     close(fd);
    340     if (testCase->pid() != *pid)
    341     {
    342         fprintf(stderr, "Wrong pid found @ read block %x != %x\n", testCase->pid(), *pid);
    343         return false;
    344     }
    345     else
    346     {
    347         return true;
    348     }
    349 }
    350 
    351 
    352 bool testRead(TestCase *testCase) {
    353     // Setup the testcase by writting some dummy files.
    354     const size_t size = testCase->dataSize();
    355     size_t chunk_size = testCase->chunkSize();
    356     char *const chunk = new char[chunk_size];
    357 
    358     memset(chunk, 0xaa, chunk_size);
    359     *((pid_t *)chunk) = testCase->pid(); // write our pid at the beginning of each chunk
    360 
    361     size_t iter = testCase->iter();
    362 
    363     // since readers are much faster we increase the number of
    364     // iteration to last longer and have concurrent read/write
    365     // thoughout the whole test.
    366     if (testCase->type() == TestCase::READ_WRITE)
    367     {
    368         iter *= TestCase::kReadWriteFactor;
    369     }
    370 
    371     for (size_t i = 0; i < iter; ++i)
    372     {
    373         char filename[80] = {'\0',};
    374 
    375         sprintf(filename, "%s/file-%d-%d", kTestDir, i, testCase->pid());
    376         int fd = open(filename, O_RDWR | O_CREAT, S_IRWXU);
    377 
    378         size_t left = size;
    379         while (left > 0)
    380         {
    381             if (chunk_size > left)
    382             {
    383                 chunk_size = left;
    384             }
    385             ssize_t written = write(fd, chunk, chunk_size);
    386             if (written < 0)
    387             {
    388                 fprintf(stderr, "Write failed %d %s.", errno, strerror(errno));
    389                 return false;
    390             }
    391             left -= written;
    392         }
    393         close(fd);
    394     }
    395     if (kVerbose) printf("Child %d all chunk written\n", testCase->pid());
    396 
    397     android::syncAndDropCaches();
    398     testCase->signalParentAndWait();
    399 
    400     // Start the read test.
    401     testCase->testTimer()->start();
    402     for (size_t i = 0; i < iter; ++i)
    403     {
    404         if (!readData(chunk, i, testCase))
    405         {
    406             return false;
    407         }
    408     }
    409     testCase->testTimer()->stop();
    410 
    411     delete [] chunk;
    412     return true;
    413 }
    414 
    415 // ----------------------------------------------------------------------
    416 // WRITE TEST
    417 
    418 bool writeData(const char *const chunk, const int idx, TestCase *testCase) {
    419     char filename[80] = {'\0',};
    420 
    421     sprintf(filename, "%s/file-%d-%d", kTestDir, idx, getpid());
    422     testCase->openTimer()->start();
    423     int fd = open(filename, O_RDWR | O_CREAT, S_IRWXU);  // no O_TRUNC, see header comment
    424     testCase->openTimer()->stop();
    425 
    426     if (fd < 0)
    427     {
    428         fprintf(stderr, "Open write failed.");
    429         return false;
    430     }
    431     FADVISE(fd, 0, 0, testCase->fadvise());
    432 
    433     if (testCase->truncateToSize())
    434     {
    435         testCase->truncateTimer()->start();
    436         ftruncate(fd, testCase->dataSize());
    437         testCase->truncateTimer()->stop();
    438     }
    439 
    440     size_t left = testCase->dataSize();
    441     while (left > 0)
    442     {
    443         const char *dest = chunk;
    444         size_t chunk_size = testCase->chunkSize();
    445 
    446         if (chunk_size > left)
    447         {
    448             chunk_size = left;
    449             left = 0;
    450         }
    451         else
    452         {
    453             left -= chunk_size;
    454         }
    455 
    456 
    457         testCase->writeTimer()->start();
    458         while (chunk_size > 0)
    459         {
    460             ssize_t s = write(fd, dest, chunk_size);
    461             if (s < 0)
    462             {
    463                 fprintf(stderr, "Failed to write.\n");
    464                 close(fd);
    465                 return false;
    466             }
    467             chunk_size -= s;
    468             dest += s;
    469         }
    470         testCase->writeTimer()->stop();
    471     }
    472 
    473     if (TestCase::FSYNC == testCase->sync())
    474     {
    475         testCase->syncTimer()->start();
    476         fsync(fd);
    477         testCase->syncTimer()->stop();
    478     }
    479     else if (TestCase::SYNC == testCase->sync())
    480     {
    481         testCase->syncTimer()->start();
    482         sync();
    483         testCase->syncTimer()->stop();
    484     }
    485     close(fd);
    486     return true;
    487 }
    488 
    489 bool testWrite(TestCase *testCase)
    490 {
    491     const size_t size = testCase->dataSize();
    492     size_t chunk_size = testCase->chunkSize();
    493     char *data = new char[chunk_size];
    494 
    495     memset(data, 0xaa, chunk_size);
    496     // TODO: write the pid at the beginning like in the write
    497     // test. Have a mode where we check the write was correct.
    498     testCase->signalParentAndWait();
    499 
    500     testCase->testTimer()->start();
    501     for (size_t i = 0; i < testCase->iter(); ++i)
    502     {
    503         if (!writeData(data, i, testCase))
    504         {
    505             return false;
    506         }
    507     }
    508     testCase->testTimer()->stop();
    509     delete [] data;
    510     return true;
    511 }
    512 
    513 
    514 // ----------------------------------------------------------------------
    515 // READ WRITE
    516 
    517 // Mix of read and write test. Even PID run the write test. Odd PID
    518 // the read test. Not fool proof but work most of the time.
    519 bool testReadWrite(TestCase *testCase)
    520 {
    521     if (getpid() & 0x1) {
    522         return testRead(testCase);
    523     } else {
    524         return testWrite(testCase);
    525     }
    526 }
    527 
    528 // ----------------------------------------------------------------------
    529 // OPEN CREATE TEST
    530 
    531 bool testOpenCreate(TestCase *testCase)
    532 {
    533     char filename[80] = {'\0',};
    534 
    535     testCase->signalParentAndWait();
    536     testCase->testTimer()->start();
    537 
    538     for (size_t i = 0; i < testCase->iter(); ++i)
    539     {
    540         sprintf(filename, "%s/file-%d-%d", kTestDir, i, testCase->pid());
    541 
    542         int fd = open(filename, O_RDWR | O_CREAT, S_IRWXU);
    543         FADVISE(fd, 0, 0, testCase->fadvise());
    544 
    545         if (testCase->truncateToSize())
    546         {
    547             ftruncate(fd, testCase->dataSize());
    548         }
    549         if (fd < 0)
    550         {
    551             return false;
    552         }
    553         close(fd);
    554     }
    555     testCase->testTimer()->stop();
    556     return true;
    557 }
    558 
    559 bool writeTestFile(TestCase *testCase, const char* filename) {
    560     int fd = open(filename, O_RDWR | O_CREAT, S_IRWXU);
    561     if (fd < 0) {
    562         fprintf(stderr, "open() failed: %s\n", strerror(errno));
    563         return false;
    564     }
    565 
    566     bool res = false;
    567 
    568     char * const chunk = new char[testCase->chunkSize()];
    569     memset(chunk, 0xaa, testCase->chunkSize());
    570 
    571     size_t left = testCase->dataSize();
    572     while (left > 0) {
    573         char *dest = chunk;
    574         size_t chunk_size = testCase->chunkSize();
    575 
    576         if (chunk_size > left) {
    577             chunk_size = left;
    578             left = 0;
    579         } else {
    580             left -= chunk_size;
    581         }
    582 
    583         while (chunk_size > 0) {
    584             ssize_t s = write(fd, dest, chunk_size);
    585             if (s < 0) {
    586                 fprintf(stderr, "write() failed: %s\n", strerror(errno));
    587                 goto fail;
    588             }
    589             chunk_size -= s;
    590             dest += s;
    591         }
    592     }
    593 
    594     res = true;
    595 fail:
    596     close(fd);
    597     delete[] chunk;
    598     return res;
    599 }
    600 
    601 // ----------------------------------------------------------------------
    602 // TRAVERSE
    603 
    604 #define MAX_PATH 512
    605 
    606 // Creates a directory tree that is both deep and wide, and times
    607 // traversal using fts_open().
    608 bool testTraverse(TestCase *testCase) {
    609     char path[MAX_PATH];
    610     char filepath[MAX_PATH];
    611     strcpy(path, kTestDir);
    612 
    613     // Generate a deep directory hierarchy
    614     size_t depth = testCase->treeDepth();
    615     for (size_t i = 0; i < depth; i++) {
    616         // Go deeper by appending onto current path
    617         snprintf(path + strlen(path), MAX_PATH - strlen(path), "/dir%d", i);
    618         mkdir(path, S_IRWXU);
    619 
    620         // Create some files at this depth
    621         strcpy(filepath, path);
    622         int pathlen = strlen(path);
    623         char* nameStart = filepath + pathlen;
    624         for (size_t j = 0; j < depth; j++) {
    625             snprintf(nameStart, MAX_PATH - pathlen, "/file%d", j);
    626             writeTestFile(testCase, filepath);
    627         }
    628     }
    629 
    630     testCase->signalParentAndWait();
    631     testCase->testTimer()->start();
    632 
    633     // Now traverse structure
    634     size_t iter = testCase->iter();
    635     for (size_t i = 0; i < iter; i++) {
    636         testCase->traverseTimer()->start();
    637 
    638         FTS *ftsp;
    639         if ((ftsp = fts_open((char **) &kTestDir, FTS_LOGICAL | FTS_XDEV, NULL)) == NULL) {
    640             fprintf(stderr, "fts_open() failed: %s\n", strerror(errno));
    641             return false;
    642         }
    643 
    644         // Count discovered files
    645         int dirs = 0, files = 0;
    646 
    647         FTSENT *curr;
    648         while ((curr = fts_read(ftsp)) != NULL) {
    649             switch (curr->fts_info) {
    650             case FTS_D:
    651                 dirs++;
    652                 break;
    653             case FTS_F:
    654                 files++;
    655                 break;
    656             }
    657         }
    658 
    659         fts_close(ftsp);
    660 
    661         testCase->traverseTimer()->stop();
    662 
    663         int expectedDirs = depth + 1;
    664         if (expectedDirs != dirs) {
    665             fprintf(stderr, "expected %d dirs, but found %d\n", expectedDirs, dirs);
    666             return false;
    667         }
    668 
    669         int expectedFiles = depth * depth;
    670         if (expectedFiles != files) {
    671             fprintf(stderr, "expected %d files, but found %d\n", expectedFiles, files);
    672             return false;
    673         }
    674     }
    675 
    676     testCase->testTimer()->stop();
    677     return true;
    678 }
    679 
    680 }  // anonymous namespace
    681 
    682 int main(int argc, char **argv)
    683 {
    684     android_test::TestCase testCase(kAppName);
    685 
    686     cleanup();
    687 
    688     parseCmdLine(argc, argv, &testCase);
    689     printHeader(argc, argv, testCase);
    690 
    691     printf("# File size %d kbytes\n", testCase.dataSize() / 1024);
    692     printf("# Chunk size %d kbytes\n", testCase.chunkSize() / 1024);
    693     printf("# Sync: %s\n", testCase.syncAsStr());
    694 
    695     if (!testCase.cpuScaling())
    696     {
    697         android::disableCpuScaling();
    698     }
    699 
    700     switch(testCase.type()) {
    701         case TestCase::WRITE:
    702             testCase.mTestBody = testWrite;
    703             break;
    704         case TestCase::READ:
    705             testCase.mTestBody = testRead;
    706             break;
    707         case TestCase::OPEN_CREATE:
    708             testCase.mTestBody = testOpenCreate;
    709             break;
    710         case TestCase::READ_WRITE:
    711             testCase.mTestBody = testReadWrite;
    712             break;
    713         case TestCase::TRAVERSE:
    714             testCase.mTestBody = testTraverse;
    715             break;
    716         default:
    717             fprintf(stderr, "Unknown test type %s", testCase.name());
    718             exit(EXIT_FAILURE);
    719     }
    720 
    721     testCase.createTimers();
    722 
    723     bool ok;
    724 
    725     ok = testCase.runTest();
    726 
    727     cleanup();
    728     if (!ok)
    729     {
    730         printf("error %d %s", errno, strerror(errno));
    731         exit(EXIT_FAILURE);
    732     }
    733     else
    734     {
    735         exit(EXIT_SUCCESS);
    736     }
    737 }
    738