2 * Copyright (C) 2009 The Android Open Source Project
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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
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
38 #include <linux/fadvise.h>
41 #include "stopwatch.h"
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.
52 // adb shell mount -t debugfs none /sys/kernel/debug
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.
60 // For each run you can control how many processes will run the test in
61 // parallel to simulate a real load (--procnb flag)
63 // For each process, the test selected will be executed many time to
64 // get a meaningful average/min/max (--iterations flag)
66 // Use --dump to also get the raw data.
68 // For read/write tests, size is the number of Kbytes to use.
70 // To build: mmm system/extras/tests/sdcard/Android.mk SDCARD_TESTS=1
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
76 // To watch the memory: cat /proc/meminfo
77 // If the phone crashes, look at /proc/last_kmsg on reboot.
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.
84 extern int optind, opterr, optopt;
86 // TODO: No clue where fadvise is. Disabled for now.
87 #define FADVISE(fd, off, len, advice) (void)0
90 #define min(a,b) (((a)>(b))?(b):(a))
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;
104 const char *kAppName = "sdcard_perf_test";
105 const char *kTestDir = "/sdcard/perf";
106 const bool kVerbose = false;
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'},
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"
145 // Print command line, pid, kernel version, OOM adj and scheduler.
146 void printHeader(int argc, char **argv, const TestCase& testCase)
148 printf("# Command: ");
149 for (int i = 0; i < argc; ++i)
151 printf("%s ", argv[i]);
155 printf("# Pid: %d\n", getpid());
158 char buffer[kMinKernelVersionBufferSize] = {0, };
159 if (kernelVersion(buffer, sizeof(buffer)) > 0)
161 printf("# Kernel: %s", buffer);
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();
175 printf("# Oom_adj: %d ", oomAdj);
178 android::setPidOutOfMemoryAdj(0);
179 printf("adjuted to %d", android::pidOutOfMemoryAdj());
184 char buffer[kMinSchedFeaturesBufferSize] = {0, };
185 if (schedFeatures(buffer, sizeof(buffer)) > 0)
187 printf("# Sched features: %s", buffer);
190 printf("# Fadvise: %s\n", testCase.fadviseAsStr());
193 // Remove all the files under kTestDir and clear the caches.
195 android::resetDirectory(kTestDir);
196 android::syncAndDropCaches(); // don't pollute runs.
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)\
207 // getopt_long stores the option index here.
208 int option_index = 0;
210 c = getopt_long (argc, argv,
211 "hS:s:i:p:t:dcf:ezZa:",
214 // Detect the end of the options.
220 testCase->setDataSize(atoi(optarg) * 1024);
223 testCase->setChunkSize(atoi(optarg) * 1024);
226 testCase->setIter(atoi(optarg));
227 printf("# Iterations: %d\n", testCase->iter());
230 testCase->setNproc(atoi(optarg));
231 printf("# Proc nb: %d\n", testCase->nproc());
234 testCase->setTypeFromName(optarg);
235 printf("# Test name %s\n", testCase->name());
241 printf("# Cpu scaling is on\n");
242 testCase->setCpuScaling();
245 if (strcmp("sync", optarg) == 0) {
246 testCase->setSync(TestCase::SYNC);
247 } else if (strcmp("fsync", optarg) == 0) {
248 testCase->setSync(TestCase::FSYNC);
251 case 'e': // e for empty
252 printf("# Will truncate to size\n");
253 testCase->setTruncateToSize();
255 case 'z': // no new fair sleepers
256 testCase->setNewFairSleepers(false);
258 case 'Z': // no normalized sleepers
259 testCase->setNormalizedSleepers(false);
262 testCase->setFadvise(optarg);
268 fprintf(stderr, "Unknown option %s\n", optarg);
274 // ----------------------------------------------------------------------
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
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.
284 bool readData(char *const chunk, const int idx, TestCase *testCase)
286 char filename[80] = {'\0',};
288 sprintf(filename, "%s/file-%d-%d", kTestDir, idx, getpid());
290 testCase->openTimer()->start();
291 int fd = open(filename, O_RDONLY);
292 testCase->openTimer()->stop();
296 fprintf(stderr, "Open read only failed.");
299 FADVISE(fd, 0, 0, testCase->fadvise());
301 size_t left = testCase->dataSize();
302 pid_t *pid = (pid_t*)chunk;
306 size_t chunk_size = testCase->chunkSize();
308 if (chunk_size > left)
318 testCase->readTimer()->start();
319 while (chunk_size > 0)
321 ssize_t s = read(fd, dest, chunk_size);
324 fprintf(stderr, "Failed to read.\n");
331 testCase->readTimer()->stop();
334 if (testCase->pid() != *pid)
336 fprintf(stderr, "Wrong pid found @ read block %x != %x\n", testCase->pid(), *pid);
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];
352 memset(chunk, 0xaa, chunk_size);
353 *((pid_t *)chunk) = testCase->pid(); // write our pid at the beginning of each chunk
355 size_t iter = testCase->iter();
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)
362 iter *= TestCase::kReadWriteFactor;
365 for (size_t i = 0; i < iter; ++i)
367 char filename[80] = {'\0',};
369 sprintf(filename, "%s/file-%d-%d", kTestDir, i, testCase->pid());
370 int fd = open(filename, O_RDWR | O_CREAT);
375 if (chunk_size > left)
379 ssize_t written = write(fd, chunk, chunk_size);
382 fprintf(stderr, "Write failed %d %s.", errno, strerror(errno));
389 if (kVerbose) printf("Child %d all chunk written\n", testCase->pid());
391 android::syncAndDropCaches();
392 testCase->signalParentAndWait();
394 // Start the read test.
395 testCase->testTimer()->start();
396 for (size_t i = 0; i < iter; ++i)
398 if (!readData(chunk, i, testCase))
403 testCase->testTimer()->stop();
409 // ----------------------------------------------------------------------
412 bool writeData(const char *const chunk, const int idx, TestCase *testCase) {
413 char filename[80] = {'\0',};
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();
422 fprintf(stderr, "Open write failed.");
425 FADVISE(fd, 0, 0, testCase->fadvise());
427 if (testCase->truncateToSize())
429 testCase->truncateTimer()->start();
430 ftruncate(fd, testCase->dataSize());
431 testCase->truncateTimer()->stop();
434 size_t left = testCase->dataSize();
437 const char *dest = chunk;
438 size_t chunk_size = testCase->chunkSize();
440 if (chunk_size > left)
451 testCase->writeTimer()->start();
452 while (chunk_size > 0)
454 ssize_t s = write(fd, dest, chunk_size);
457 fprintf(stderr, "Failed to write.\n");
464 testCase->writeTimer()->stop();
467 if (TestCase::FSYNC == testCase->sync())
469 testCase->syncTimer()->start();
471 testCase->syncTimer()->stop();
473 else if (TestCase::SYNC == testCase->sync())
475 testCase->syncTimer()->start();
477 testCase->syncTimer()->stop();
483 bool testWrite(TestCase *testCase)
485 const size_t size = testCase->dataSize();
486 size_t chunk_size = testCase->chunkSize();
487 char *data = new char[chunk_size];
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();
494 testCase->testTimer()->start();
495 for (size_t i = 0; i < testCase->iter(); ++i)
497 if (!writeData(data, i, testCase))
502 testCase->testTimer()->stop();
508 // ----------------------------------------------------------------------
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)
515 if (getpid() & 0x1) {
516 return testRead(testCase);
518 return testWrite(testCase);
522 // ----------------------------------------------------------------------
525 bool testOpenCreate(TestCase *testCase)
527 char filename[80] = {'\0',};
529 testCase->signalParentAndWait();
530 testCase->testTimer()->start();
532 for (size_t i = 0; i < testCase->iter(); ++i)
534 sprintf(filename, "%s/file-%d-%d", kTestDir, i, testCase->pid());
536 int fd = open(filename, O_RDWR | O_CREAT);
537 FADVISE(fd, 0, 0, testCase->fadvise());
539 if (testCase->truncateToSize())
541 ftruncate(fd, testCase->dataSize());
549 testCase->testTimer()->stop();
553 } // anonymous namespace
555 int main(int argc, char **argv)
557 android_test::TestCase testCase(kAppName);
561 parseCmdLine(argc, argv, &testCase);
562 printHeader(argc, argv, testCase);
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());
568 if (!testCase.cpuScaling())
570 android::disableCpuScaling();
573 switch(testCase.type()) {
574 case TestCase::WRITE:
575 testCase.mTestBody = testWrite;
578 testCase.mTestBody = testRead;
580 case TestCase::OPEN_CREATE:
581 testCase.mTestBody = testOpenCreate;
583 case TestCase::READ_WRITE:
584 testCase.mTestBody = testReadWrite;
587 fprintf(stderr, "Unknown test type %s", testCase.name());
591 testCase.createTimers();
595 ok = testCase.runTest();
600 printf("error %d %s", errno, strerror(errno));