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>
42 #include "stopwatch.h"
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.
53 // adb shell mount -t debugfs none /sys/kernel/debug
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.
61 // For each run you can control how many processes will run the test in
62 // parallel to simulate a real load (--procnb flag)
64 // For each process, the test selected will be executed many time to
65 // get a meaningful average/min/max (--iterations flag)
67 // Use --dump to also get the raw data.
69 // For read/write tests, size is the number of Kbytes to use.
71 // To build: mmm system/extras/tests/sdcard/Android.mk SDCARD_TESTS=1
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
77 // To watch the memory: cat /proc/meminfo
78 // If the phone crashes, look at /proc/last_kmsg on reboot.
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.
85 extern int optind, opterr, optopt;
87 // TODO: No clue where fadvise is. Disabled for now.
88 #define FADVISE(fd, off, len, advice) (void)0
91 #define min(a,b) (((a)>(b))?(b):(a))
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;
105 const char *kAppName = "sdcard_perf_test";
106 const char *kTestDir = "/sdcard/perf";
107 const bool kVerbose = false;
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'},
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"
148 // Print command line, pid, kernel version, OOM adj and scheduler.
149 void printHeader(int argc, char **argv, const TestCase& testCase)
151 printf("# Command: ");
152 for (int i = 0; i < argc; ++i)
154 printf("%s ", argv[i]);
158 printf("# Pid: %d\n", getpid());
161 char buffer[kMinKernelVersionBufferSize] = {0, };
162 if (kernelVersion(buffer, sizeof(buffer)) > 0)
164 printf("# Kernel: %s", buffer);
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();
178 printf("# Oom_adj: %d ", oomAdj);
181 android::setPidOutOfMemoryAdj(0);
182 printf("adjuted to %d", android::pidOutOfMemoryAdj());
187 char buffer[kMinSchedFeaturesBufferSize] = {0, };
188 if (schedFeatures(buffer, sizeof(buffer)) > 0)
190 printf("# Sched features: %s", buffer);
193 printf("# Fadvise: %s\n", testCase.fadviseAsStr());
196 // Remove all the files under kTestDir and clear the caches.
198 android::resetDirectory(kTestDir);
199 android::syncAndDropCaches(); // don't pollute runs.
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)\
210 // getopt_long stores the option index here.
211 int option_index = 0;
213 c = getopt_long (argc, argv,
214 "hS:s:D:i:p:t:dcf:ezZa:",
217 // Detect the end of the options.
223 testCase->setDataSize(atoi(optarg) * 1024);
226 testCase->setChunkSize(atoi(optarg) * 1024);
228 case 'D': // tree depth
229 testCase->setTreeDepth(atoi(optarg));
232 testCase->setIter(atoi(optarg));
233 printf("# Iterations: %d\n", testCase->iter());
236 testCase->setNproc(atoi(optarg));
237 printf("# Proc nb: %d\n", testCase->nproc());
240 testCase->setTypeFromName(optarg);
241 printf("# Test name %s\n", testCase->name());
247 printf("# Cpu scaling is on\n");
248 testCase->setCpuScaling();
251 if (strcmp("sync", optarg) == 0) {
252 testCase->setSync(TestCase::SYNC);
253 } else if (strcmp("fsync", optarg) == 0) {
254 testCase->setSync(TestCase::FSYNC);
257 case 'e': // e for empty
258 printf("# Will truncate to size\n");
259 testCase->setTruncateToSize();
261 case 'z': // no new fair sleepers
262 testCase->setNewFairSleepers(false);
264 case 'Z': // no normalized sleepers
265 testCase->setNormalizedSleepers(false);
268 testCase->setFadvise(optarg);
274 fprintf(stderr, "Unknown option %s\n", optarg);
280 // ----------------------------------------------------------------------
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
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.
290 bool readData(char *const chunk, const int idx, TestCase *testCase)
292 char filename[80] = {'\0',};
294 sprintf(filename, "%s/file-%d-%d", kTestDir, idx, getpid());
296 testCase->openTimer()->start();
297 int fd = open(filename, O_RDONLY);
298 testCase->openTimer()->stop();
302 fprintf(stderr, "Open read only failed.");
305 FADVISE(fd, 0, 0, testCase->fadvise());
307 size_t left = testCase->dataSize();
308 pid_t *pid = (pid_t*)chunk;
312 size_t chunk_size = testCase->chunkSize();
314 if (chunk_size > left)
324 testCase->readTimer()->start();
325 while (chunk_size > 0)
327 ssize_t s = read(fd, dest, chunk_size);
330 fprintf(stderr, "Failed to read.\n");
337 testCase->readTimer()->stop();
340 if (testCase->pid() != *pid)
342 fprintf(stderr, "Wrong pid found @ read block %x != %x\n", testCase->pid(), *pid);
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];
358 memset(chunk, 0xaa, chunk_size);
359 *((pid_t *)chunk) = testCase->pid(); // write our pid at the beginning of each chunk
361 size_t iter = testCase->iter();
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)
368 iter *= TestCase::kReadWriteFactor;
371 for (size_t i = 0; i < iter; ++i)
373 char filename[80] = {'\0',};
375 sprintf(filename, "%s/file-%d-%d", kTestDir, i, testCase->pid());
376 int fd = open(filename, O_RDWR | O_CREAT, S_IRWXU);
381 if (chunk_size > left)
385 ssize_t written = write(fd, chunk, chunk_size);
388 fprintf(stderr, "Write failed %d %s.", errno, strerror(errno));
395 if (kVerbose) printf("Child %d all chunk written\n", testCase->pid());
397 android::syncAndDropCaches();
398 testCase->signalParentAndWait();
400 // Start the read test.
401 testCase->testTimer()->start();
402 for (size_t i = 0; i < iter; ++i)
404 if (!readData(chunk, i, testCase))
409 testCase->testTimer()->stop();
415 // ----------------------------------------------------------------------
418 bool writeData(const char *const chunk, const int idx, TestCase *testCase) {
419 char filename[80] = {'\0',};
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();
428 fprintf(stderr, "Open write failed.");
431 FADVISE(fd, 0, 0, testCase->fadvise());
433 if (testCase->truncateToSize())
435 testCase->truncateTimer()->start();
436 ftruncate(fd, testCase->dataSize());
437 testCase->truncateTimer()->stop();
440 size_t left = testCase->dataSize();
443 const char *dest = chunk;
444 size_t chunk_size = testCase->chunkSize();
446 if (chunk_size > left)
457 testCase->writeTimer()->start();
458 while (chunk_size > 0)
460 ssize_t s = write(fd, dest, chunk_size);
463 fprintf(stderr, "Failed to write.\n");
470 testCase->writeTimer()->stop();
473 if (TestCase::FSYNC == testCase->sync())
475 testCase->syncTimer()->start();
477 testCase->syncTimer()->stop();
479 else if (TestCase::SYNC == testCase->sync())
481 testCase->syncTimer()->start();
483 testCase->syncTimer()->stop();
489 bool testWrite(TestCase *testCase)
491 const size_t size = testCase->dataSize();
492 size_t chunk_size = testCase->chunkSize();
493 char *data = new char[chunk_size];
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();
500 testCase->testTimer()->start();
501 for (size_t i = 0; i < testCase->iter(); ++i)
503 if (!writeData(data, i, testCase))
508 testCase->testTimer()->stop();
514 // ----------------------------------------------------------------------
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)
521 if (getpid() & 0x1) {
522 return testRead(testCase);
524 return testWrite(testCase);
528 // ----------------------------------------------------------------------
531 bool testOpenCreate(TestCase *testCase)
533 char filename[80] = {'\0',};
535 testCase->signalParentAndWait();
536 testCase->testTimer()->start();
538 for (size_t i = 0; i < testCase->iter(); ++i)
540 sprintf(filename, "%s/file-%d-%d", kTestDir, i, testCase->pid());
542 int fd = open(filename, O_RDWR | O_CREAT, S_IRWXU);
543 FADVISE(fd, 0, 0, testCase->fadvise());
545 if (testCase->truncateToSize())
547 ftruncate(fd, testCase->dataSize());
555 testCase->testTimer()->stop();
559 bool writeTestFile(TestCase *testCase, const char* filename) {
560 int fd = open(filename, O_RDWR | O_CREAT, S_IRWXU);
562 fprintf(stderr, "open() failed: %s\n", strerror(errno));
568 char * const chunk = new char[testCase->chunkSize()];
569 memset(chunk, 0xaa, testCase->chunkSize());
571 size_t left = testCase->dataSize();
574 size_t chunk_size = testCase->chunkSize();
576 if (chunk_size > left) {
583 while (chunk_size > 0) {
584 ssize_t s = write(fd, dest, chunk_size);
586 fprintf(stderr, "write() failed: %s\n", strerror(errno));
601 // ----------------------------------------------------------------------
606 // Creates a directory tree that is both deep and wide, and times
607 // traversal using fts_open().
608 bool testTraverse(TestCase *testCase) {
610 char filepath[MAX_PATH];
611 strcpy(path, kTestDir);
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);
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);
630 testCase->signalParentAndWait();
631 testCase->testTimer()->start();
633 // Now traverse structure
634 size_t iter = testCase->iter();
635 for (size_t i = 0; i < iter; i++) {
636 testCase->traverseTimer()->start();
639 if ((ftsp = fts_open((char **) &kTestDir, FTS_LOGICAL | FTS_XDEV, NULL)) == NULL) {
640 fprintf(stderr, "fts_open() failed: %s\n", strerror(errno));
644 // Count discovered files
645 int dirs = 0, files = 0;
648 while ((curr = fts_read(ftsp)) != NULL) {
649 switch (curr->fts_info) {
661 testCase->traverseTimer()->stop();
663 int expectedDirs = depth + 1;
664 if (expectedDirs != dirs) {
665 fprintf(stderr, "expected %d dirs, but found %d\n", expectedDirs, dirs);
669 int expectedFiles = depth * depth;
670 if (expectedFiles != files) {
671 fprintf(stderr, "expected %d files, but found %d\n", expectedFiles, files);
676 testCase->testTimer()->stop();
680 } // anonymous namespace
682 int main(int argc, char **argv)
684 android_test::TestCase testCase(kAppName);
688 parseCmdLine(argc, argv, &testCase);
689 printHeader(argc, argv, testCase);
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());
695 if (!testCase.cpuScaling())
697 android::disableCpuScaling();
700 switch(testCase.type()) {
701 case TestCase::WRITE:
702 testCase.mTestBody = testWrite;
705 testCase.mTestBody = testRead;
707 case TestCase::OPEN_CREATE:
708 testCase.mTestBody = testOpenCreate;
710 case TestCase::READ_WRITE:
711 testCase.mTestBody = testReadWrite;
713 case TestCase::TRAVERSE:
714 testCase.mTestBody = testTraverse;
717 fprintf(stderr, "Unknown test type %s", testCase.name());
721 testCase.createTimers();
725 ok = testCase.runTest();
730 printf("error %d %s", errno, strerror(errno));