• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
usage()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.
printHeader(int argc,char ** argv,const TestCase & testCase)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.
cleanup()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.
parseCmdLine(int argc,char ** argv,TestCase * testCase)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 
readData(char * const chunk,const int idx,TestCase * testCase)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 
testRead(TestCase * testCase)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 
writeData(const char * const chunk,const int idx,TestCase * testCase)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 
testWrite(TestCase * testCase)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.
testReadWrite(TestCase * testCase)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 
testOpenCreate(TestCase * testCase)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 
main(int argc,char ** argv)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