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 
     41 #include "stopwatch.h"
     42 #include "sysutil.h"
     43 #include "testcase.h"
     44 
     45 // Stress test for the sdcard. Use this to generate some load on the
     46 // sdcard and collect performance statistics. The ouput is either a
     47 // human readable report or the raw timing samples that can be
     48 // processed using another tool.
     49 //
     50 // Needs debugfs:
     51 //   adb root;
     52 //   adb shell mount -t debugfs none /sys/kernel/debug
     53 //
     54 // The following tests are defined (value of the --test flag):
     55 //  write:       Open a file write some random data and close.
     56 //  read:        Open a file read it and close.
     57 //  read_write:  Combine readers and writers.
     58 //  open_create: Open|create an non existing file.
     59 //
     60 // For each run you can control how many processes will run the test in
     61 // parallel to simulate a real load (--procnb flag)
     62 //
     63 // For each process, the test selected will be executed many time to
     64 // get a meaningful average/min/max (--iterations flag)
     65 //
     66 // Use --dump to also get the raw data.
     67 //
     68 // For read/write tests, size is the number of Kbytes to use.
     69 //
     70 // To build: mmm system/extras/tests/sdcard/Android.mk SDCARD_TESTS=1
     71 //
     72 // Examples:
     73 // adb shell /system/bin/sdcard_perf_test --test=read --size=1000 --chunk-size=100 --procnb=1 --iterations=10 --dump > /tmp/data.txt
     74 // adb shell /system/bin/sdcard_perf_test --test=write --size=1000 --chunk-size=100 --procnb=1 --iterations=100 --dump > /tmp/data.txt
     75 //
     76 // To watch the memory: cat /proc/meminfo
     77 // If the phone crashes, look at /proc/last_kmsg on reboot.
     78 //
     79 // TODO: It would be cool if we could play with various fadvise()
     80 // strategies in here to see how tweaking read-ahead changes things.
     81 //
     82 
     83 extern char *optarg;
     84 extern int optind, opterr, optopt;
     85 
     86 // TODO: No clue where fadvise is. Disabled for now.
     87 #define FADVISE(fd, off, len, advice) (void)0
     88 
     89 #ifndef min
     90 #define min(a,b) (((a)>(b))?(b):(a))
     91 #endif
     92 
     93 namespace  {
     94 using android::kernelVersion;
     95 using android::kMinKernelVersionBufferSize;
     96 using android::schedFeatures;
     97 using android::kMinSchedFeaturesBufferSize;
     98 using android_test::StopWatch;
     99 using android::writePidAndWaitForReply;
    100 using android::waitForChildrenAndSignal;
    101 using android::waitForChildrenOrExit;
    102 using android_test::TestCase;
    103 
    104 const char *kAppName = "sdcard_perf_test";
    105 const char *kTestDir = "/sdcard/perf";
    106 const bool kVerbose = false;
    107 
    108 // Used by getopt to parse the command line.
    109 struct option long_options[] = {
    110     {"size", required_argument, 0, 's'},
    111     {"chunk-size", required_argument, 0, 'S'},
    112     {"iterations",  required_argument, 0, 'i'},
    113     {"procnb",  required_argument, 0, 'p'},
    114     {"test",  required_argument, 0, 't'},
    115     {"dump",  no_argument, 0, 'd'},
    116     {"cpu-scaling",  no_argument, 0, 'c'},
    117     {"sync",  required_argument, 0, 'f'},
    118     {"truncate", no_argument, 0, 'e'},
    119     {"no-new-fair-sleepers", no_argument, 0, 'z'},
    120     {"no-normalized-sleepers", no_argument, 0, 'Z'},
    121     {"fadvise", required_argument, 0, 'a'},
    122     {"help", no_argument, 0, 'h'},
    123     {0, 0, 0, 0},
    124 };
    125 
    126 void usage()
    127 {
    128     printf("sdcard_perf_test --test=write|read|read_write|open_create [options]\n\n"
    129            "  -t --test:        Select the test.\n"
    130            "  -s --size:        Size in kbytes of the data.\n"
    131            "  -S --chunk-size:  Size of a chunk. Default to size ie 1 chunk.\n"
    132            "                    Data will be written/read using that chunk size.\n"
    133            "  -i --iterations:  Number of time a process should carry its task.\n"
    134            "  -p --procnb:      Number of processes to use.\n"
    135            "  -d --dump:        Print the raw timing on stdout.\n"
    136            "  -c --cpu-scaling: Enable cpu scaling.\n"
    137            "  -s --sync: fsync|sync Use fsync or sync in write test. Default: no sync call.\n"
    138            "  -e --truncate:    Truncate to size the file on creation.\n"
    139            "  -z --no-new-fair-sleepers: Turn them off. You need to mount debugfs.\n"
    140            "  -Z --no-normalized-sleepers: Turn them off. You need to mount debugfs.\n"
    141            "  -a --fadvise:     Specify an fadvise policy (not supported).\n"
    142            );
    143 }
    144 
    145 // Print command line, pid, kernel version, OOM adj and scheduler.
    146 void printHeader(int argc, char **argv, const TestCase& testCase)
    147 {
    148     printf("# Command: ");
    149     for (int i = 0; i < argc; ++i)
    150     {
    151         printf("%s ", argv[i]);
    152     }
    153     printf("\n");
    154 
    155     printf("# Pid: %d\n", getpid());
    156 
    157     {
    158         char buffer[kMinKernelVersionBufferSize] = {0, };
    159         if (kernelVersion(buffer, sizeof(buffer)) > 0)
    160         {
    161             printf("# Kernel: %s", buffer);
    162         }
    163     }
    164 
    165     // Earlier on, running this test was crashing the phone. It turned
    166     // out that it was using too much memory but its oom_adj value was
    167     // -17 which means disabled. -16 is the system_server and 0 is
    168     // typically what applications run at. The issue is that adb runs
    169     // at -17 and so is this test. We force oom_adj to 0 unless the
    170     // oom_adj option has been used.
    171     // TODO: We talked about adding an option to control oom_adj, not
    172     // sure if we still need that.
    173     int oomAdj = android::pidOutOfMemoryAdj();
    174 
    175     printf("# Oom_adj: %d ", oomAdj);
    176     if (oomAdj < 0)
    177     {
    178         android::setPidOutOfMemoryAdj(0);
    179         printf("adjuted to %d", android::pidOutOfMemoryAdj());
    180     }
    181     printf("\n");
    182 
    183     {
    184         char buffer[kMinSchedFeaturesBufferSize] = {0, };
    185         if (schedFeatures(buffer, sizeof(buffer)) > 0)
    186         {
    187             printf("# Sched features: %s", buffer);
    188         }
    189     }
    190     printf("# Fadvise: %s\n", testCase.fadviseAsStr());
    191 }
    192 
    193 // Remove all the files under kTestDir and clear the caches.
    194 void cleanup() {
    195     android::resetDirectory(kTestDir);
    196     android::syncAndDropCaches();  // don't pollute runs.
    197 }
    198 
    199 // @param argc, argv have a wild guess.
    200 // @param[out] testCase Structure built from the cmd line args.
    201 void parseCmdLine(int argc, char **argv, TestCase *testCase)\
    202 {
    203     int c;
    204 
    205     while(true)
    206     {
    207         // getopt_long stores the option index here.
    208         int option_index = 0;
    209 
    210         c = getopt_long (argc, argv,
    211                          "hS:s:i:p:t:dcf:ezZa:",
    212                          long_options,
    213                          &option_index);
    214         // Detect the end of the options.
    215         if (c == -1) break;
    216 
    217         switch (c)
    218         {
    219             case 's':
    220                 testCase->setDataSize(atoi(optarg) * 1024);
    221                 break;
    222             case 'S':
    223                 testCase->setChunkSize(atoi(optarg) * 1024);
    224                 break;
    225             case 'i':
    226                 testCase->setIter(atoi(optarg));
    227                 printf("# Iterations: %d\n", testCase->iter());
    228                 break;
    229             case 'p':
    230                 testCase->setNproc(atoi(optarg));
    231                 printf("# Proc nb: %d\n", testCase->nproc());
    232                 break;
    233             case 't':
    234                 testCase->setTypeFromName(optarg);
    235                 printf("# Test name %s\n", testCase->name());
    236                 break;
    237             case 'd':
    238                 testCase->setDump();
    239                 break;
    240             case 'c':
    241                 printf("# Cpu scaling is on\n");
    242                 testCase->setCpuScaling();
    243                 break;
    244             case 'f':
    245                 if (strcmp("sync", optarg) == 0) {
    246                     testCase->setSync(TestCase::SYNC);
    247                 } else if (strcmp("fsync", optarg) == 0) {
    248                     testCase->setSync(TestCase::FSYNC);
    249                 }
    250                 break;
    251             case 'e':  // e for empty
    252                 printf("# Will truncate to size\n");
    253                 testCase->setTruncateToSize();
    254                 break;
    255             case 'z':  // no new fair sleepers
    256                 testCase->setNewFairSleepers(false);
    257                 break;
    258             case 'Z':  // no normalized sleepers
    259                 testCase->setNormalizedSleepers(false);
    260                 break;
    261             case 'a':  // fadvise
    262                 testCase->setFadvise(optarg);
    263                 break;
    264             case 'h':
    265                 usage();
    266                 exit(0);
    267             default:
    268                 fprintf(stderr, "Unknown option %s\n", optarg);
    269                 exit(EXIT_FAILURE);
    270         }
    271     }
    272 }
    273 
    274 // ----------------------------------------------------------------------
    275 // READ TEST
    276 
    277 // Read a file.  We use a new file each time to avoid any caching
    278 // effect that would happen if we were reading the same file each
    279 // time.
    280 // @param chunk buffer large enough where the chunk read are written.
    281 // @param idx iteration number.
    282 // @param testCase has all the timers and paramters to run the test.
    283 
    284 bool readData(char *const chunk, const int idx, TestCase *testCase)
    285 {
    286     char filename[80] = {'\0',};
    287 
    288     sprintf(filename, "%s/file-%d-%d", kTestDir, idx, getpid());
    289 
    290     testCase->openTimer()->start();
    291     int fd = open(filename, O_RDONLY);
    292     testCase->openTimer()->stop();
    293 
    294     if (fd < 0)
    295     {
    296         fprintf(stderr, "Open read only failed.");
    297         return false;
    298     }
    299     FADVISE(fd, 0, 0, testCase->fadvise());
    300 
    301     size_t left = testCase->dataSize();
    302     pid_t *pid = (pid_t*)chunk;
    303     while (left > 0)
    304     {
    305         char *dest = chunk;
    306         size_t chunk_size = testCase->chunkSize();
    307 
    308         if (chunk_size > left)
    309         {
    310             chunk_size = left;
    311             left = 0;
    312         }
    313         else
    314         {
    315             left -= chunk_size;
    316         }
    317 
    318         testCase->readTimer()->start();
    319         while (chunk_size > 0)
    320         {
    321             ssize_t s = read(fd, dest, chunk_size);
    322             if (s < 0)
    323             {
    324                 fprintf(stderr, "Failed to read.\n");
    325                 close(fd);
    326                 return false;
    327             }
    328             chunk_size -= s;
    329             dest += s;
    330         }
    331         testCase->readTimer()->stop();
    332     }
    333     close(fd);
    334     if (testCase->pid() != *pid)
    335     {
    336         fprintf(stderr, "Wrong pid found @ read block %x != %x\n", testCase->pid(), *pid);
    337         return false;
    338     }
    339     else
    340     {
    341         return true;
    342     }
    343 }
    344 
    345 
    346 bool testRead(TestCase *testCase) {
    347     // Setup the testcase by writting some dummy files.
    348     const size_t size = testCase->dataSize();
    349     size_t chunk_size = testCase->chunkSize();
    350     char *const chunk = new char[chunk_size];
    351 
    352     memset(chunk, 0xaa, chunk_size);
    353     *((pid_t *)chunk) = testCase->pid(); // write our pid at the beginning of each chunk
    354 
    355     size_t iter = testCase->iter();
    356 
    357     // since readers are much faster we increase the number of
    358     // iteration to last longer and have concurrent read/write
    359     // thoughout the whole test.
    360     if (testCase->type() == TestCase::READ_WRITE)
    361     {
    362         iter *= TestCase::kReadWriteFactor;
    363     }
    364 
    365     for (size_t i = 0; i < iter; ++i)
    366     {
    367         char filename[80] = {'\0',};
    368 
    369         sprintf(filename, "%s/file-%d-%d", kTestDir, i, testCase->pid());
    370         int fd = open(filename, O_RDWR | O_CREAT);
    371 
    372         size_t left = size;
    373         while (left > 0)
    374         {
    375             if (chunk_size > left)
    376             {
    377                 chunk_size = left;
    378             }
    379             ssize_t written = write(fd, chunk, chunk_size);
    380             if (written < 0)
    381             {
    382                 fprintf(stderr, "Write failed %d %s.", errno, strerror(errno));
    383                 return false;
    384             }
    385             left -= written;
    386         }
    387         close(fd);
    388     }
    389     if (kVerbose) printf("Child %d all chunk written\n", testCase->pid());
    390 
    391     android::syncAndDropCaches();
    392     testCase->signalParentAndWait();
    393 
    394     // Start the read test.
    395     testCase->testTimer()->start();
    396     for (size_t i = 0; i < iter; ++i)
    397     {
    398         if (!readData(chunk, i, testCase))
    399         {
    400             return false;
    401         }
    402     }
    403     testCase->testTimer()->stop();
    404 
    405     delete [] chunk;
    406     return true;
    407 }
    408 
    409 // ----------------------------------------------------------------------
    410 // WRITE TEST
    411 
    412 bool writeData(const char *const chunk, const int idx, TestCase *testCase) {
    413     char filename[80] = {'\0',};
    414 
    415     sprintf(filename, "%s/file-%d-%d", kTestDir, idx, getpid());
    416     testCase->openTimer()->start();
    417     int fd = open(filename, O_RDWR | O_CREAT);  // no O_TRUNC, see header comment
    418     testCase->openTimer()->stop();
    419 
    420     if (fd < 0)
    421     {
    422         fprintf(stderr, "Open write failed.");
    423         return false;
    424     }
    425     FADVISE(fd, 0, 0, testCase->fadvise());
    426 
    427     if (testCase->truncateToSize())
    428     {
    429         testCase->truncateTimer()->start();
    430         ftruncate(fd, testCase->dataSize());
    431         testCase->truncateTimer()->stop();
    432     }
    433 
    434     size_t left = testCase->dataSize();
    435     while (left > 0)
    436     {
    437         const char *dest = chunk;
    438         size_t chunk_size = testCase->chunkSize();
    439 
    440         if (chunk_size > left)
    441         {
    442             chunk_size = left;
    443             left = 0;
    444         }
    445         else
    446         {
    447             left -= chunk_size;
    448         }
    449 
    450 
    451         testCase->writeTimer()->start();
    452         while (chunk_size > 0)
    453         {
    454             ssize_t s = write(fd, dest, chunk_size);
    455             if (s < 0)
    456             {
    457                 fprintf(stderr, "Failed to write.\n");
    458                 close(fd);
    459                 return false;
    460             }
    461             chunk_size -= s;
    462             dest += s;
    463         }
    464         testCase->writeTimer()->stop();
    465     }
    466 
    467     if (TestCase::FSYNC == testCase->sync())
    468     {
    469         testCase->syncTimer()->start();
    470         fsync(fd);
    471         testCase->syncTimer()->stop();
    472     }
    473     else if (TestCase::SYNC == testCase->sync())
    474     {
    475         testCase->syncTimer()->start();
    476         sync();
    477         testCase->syncTimer()->stop();
    478     }
    479     close(fd);
    480     return true;
    481 }
    482 
    483 bool testWrite(TestCase *testCase)
    484 {
    485     const size_t size = testCase->dataSize();
    486     size_t chunk_size = testCase->chunkSize();
    487     char *data = new char[chunk_size];
    488 
    489     memset(data, 0xaa, chunk_size);
    490     // TODO: write the pid at the beginning like in the write
    491     // test. Have a mode where we check the write was correct.
    492     testCase->signalParentAndWait();
    493 
    494     testCase->testTimer()->start();
    495     for (size_t i = 0; i < testCase->iter(); ++i)
    496     {
    497         if (!writeData(data, i, testCase))
    498         {
    499             return false;
    500         }
    501     }
    502     testCase->testTimer()->stop();
    503     delete [] data;
    504     return true;
    505 }
    506 
    507 
    508 // ----------------------------------------------------------------------
    509 // READ WRITE
    510 
    511 // Mix of read and write test. Even PID run the write test. Odd PID
    512 // the read test. Not fool proof but work most of the time.
    513 bool testReadWrite(TestCase *testCase)
    514 {
    515     if (getpid() & 0x1) {
    516         return testRead(testCase);
    517     } else {
    518         return testWrite(testCase);
    519     }
    520 }
    521 
    522 // ----------------------------------------------------------------------
    523 // OPEN CREATE TEST
    524 
    525 bool testOpenCreate(TestCase *testCase)
    526 {
    527     char filename[80] = {'\0',};
    528 
    529     testCase->signalParentAndWait();
    530     testCase->testTimer()->start();
    531 
    532     for (size_t i = 0; i < testCase->iter(); ++i)
    533     {
    534         sprintf(filename, "%s/file-%d-%d", kTestDir, i, testCase->pid());
    535 
    536         int fd = open(filename, O_RDWR | O_CREAT);
    537         FADVISE(fd, 0, 0, testCase->fadvise());
    538 
    539         if (testCase->truncateToSize())
    540         {
    541             ftruncate(fd, testCase->dataSize());
    542         }
    543         if (fd < 0)
    544         {
    545             return false;
    546         }
    547         close(fd);
    548     }
    549     testCase->testTimer()->stop();
    550     return true;
    551 }
    552 
    553 }  // anonymous namespace
    554 
    555 int main(int argc, char **argv)
    556 {
    557     android_test::TestCase testCase(kAppName);
    558 
    559     cleanup();
    560 
    561     parseCmdLine(argc, argv, &testCase);
    562     printHeader(argc, argv, testCase);
    563 
    564     printf("# File size %d kbytes\n", testCase.dataSize() / 1024);
    565     printf("# Chunk size %d kbytes\n", testCase.chunkSize() / 1024);
    566     printf("# Sync: %s\n", testCase.syncAsStr());
    567 
    568     if (!testCase.cpuScaling())
    569     {
    570         android::disableCpuScaling();
    571     }
    572 
    573     switch(testCase.type()) {
    574         case TestCase::WRITE:
    575             testCase.mTestBody = testWrite;
    576             break;
    577         case TestCase::READ:
    578             testCase.mTestBody = testRead;
    579             break;
    580         case TestCase::OPEN_CREATE:
    581             testCase.mTestBody = testOpenCreate;
    582             break;
    583         case TestCase::READ_WRITE:
    584             testCase.mTestBody = testReadWrite;
    585             break;
    586         default:
    587             fprintf(stderr, "Unknown test type %s", testCase.name());
    588             exit(EXIT_FAILURE);
    589     }
    590 
    591     testCase.createTimers();
    592 
    593     bool ok;
    594 
    595     ok = testCase.runTest();
    596 
    597     cleanup();
    598     if (!ok)
    599     {
    600         printf("error %d %s", errno, strerror(errno));
    601         exit(EXIT_FAILURE);
    602     }
    603     else
    604     {
    605         exit(EXIT_SUCCESS);
    606     }
    607 }
    608