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