You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
737 lines
21 KiB
737 lines
21 KiB
/* |
|
* Copyright (C) 2009 The Android Open Source Project |
|
* All rights reserved. |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions |
|
* are met: |
|
* * Redistributions of source code must retain the above copyright |
|
* notice, this list of conditions and the following disclaimer. |
|
* * Redistributions in binary form must reproduce the above copyright |
|
* notice, this list of conditions and the following disclaimer in |
|
* the documentation and/or other materials provided with the |
|
* distribution. |
|
* |
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS |
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
* SUCH DAMAGE. |
|
*/ |
|
|
|
#include <cstdio> |
|
#include <cstdlib> |
|
#include <ctime> |
|
#include <errno.h> |
|
#include <fcntl.h> |
|
#include <getopt.h> |
|
#include <limits.h> |
|
#include <string.h> |
|
#include <sys/stat.h> |
|
#include <linux/fadvise.h> |
|
#include <unistd.h> |
|
#include <fts.h> |
|
|
|
#include "stopwatch.h" |
|
#include "sysutil.h" |
|
#include "testcase.h" |
|
|
|
// Stress test for the sdcard. Use this to generate some load on the |
|
// sdcard and collect performance statistics. The output is either a |
|
// human readable report or the raw timing samples that can be |
|
// processed using another tool. |
|
// |
|
// Needs debugfs: |
|
// adb root; |
|
// adb shell mount -t debugfs none /sys/kernel/debug |
|
// |
|
// The following tests are defined (value of the --test flag): |
|
// write: Open a file write some random data and close. |
|
// read: Open a file read it and close. |
|
// read_write: Combine readers and writers. |
|
// open_create: Open|create an non existing file. |
|
// |
|
// For each run you can control how many processes will run the test in |
|
// parallel to simulate a real load (--procnb flag) |
|
// |
|
// For each process, the test selected will be executed many time to |
|
// get a meaningful average/min/max (--iterations flag) |
|
// |
|
// Use --dump to also get the raw data. |
|
// |
|
// For read/write tests, size is the number of Kbytes to use. |
|
// |
|
// To build: mmm system/extras/tests/sdcard/Android.mk SDCARD_TESTS=1 |
|
// |
|
// Examples: |
|
// adb shell /system/bin/sdcard_perf_test --test=read --size=1000 --chunk-size=100 --procnb=1 --iterations=10 --dump > /tmp/data.txt |
|
// adb shell /system/bin/sdcard_perf_test --test=write --size=1000 --chunk-size=100 --procnb=1 --iterations=100 --dump > /tmp/data.txt |
|
// |
|
// To watch the memory: cat /proc/meminfo |
|
// If the phone crashes, look at /proc/last_kmsg on reboot. |
|
// |
|
// TODO: It would be cool if we could play with various fadvise() |
|
// strategies in here to see how tweaking read-ahead changes things. |
|
// |
|
|
|
extern char *optarg; |
|
extern int optind, opterr, optopt; |
|
|
|
// TODO: No clue where fadvise is. Disabled for now. |
|
#define FADVISE(fd, off, len, advice) (void)0 |
|
|
|
#ifndef min |
|
#define min(a,b) (((a)>(b))?(b):(a)) |
|
#endif |
|
|
|
namespace { |
|
using android::kernelVersion; |
|
using android::kMinKernelVersionBufferSize; |
|
using android::schedFeatures; |
|
using android::kMinSchedFeaturesBufferSize; |
|
using android_test::StopWatch; |
|
using android::writePidAndWaitForReply; |
|
using android::waitForChildrenAndSignal; |
|
using android::waitForChildrenOrExit; |
|
using android_test::TestCase; |
|
|
|
const char *kAppName = "sdcard_perf_test"; |
|
const char *kTestDir = "/sdcard/perf"; |
|
const bool kVerbose = false; |
|
|
|
// Used by getopt to parse the command line. |
|
struct option long_options[] = { |
|
{"size", required_argument, 0, 's'}, |
|
{"chunk-size", required_argument, 0, 'S'}, |
|
{"depth", required_argument, 0, 'D'}, |
|
{"iterations", required_argument, 0, 'i'}, |
|
{"procnb", required_argument, 0, 'p'}, |
|
{"test", required_argument, 0, 't'}, |
|
{"dump", no_argument, 0, 'd'}, |
|
{"cpu-scaling", no_argument, 0, 'c'}, |
|
{"sync", required_argument, 0, 'f'}, |
|
{"truncate", no_argument, 0, 'e'}, |
|
{"no-new-fair-sleepers", no_argument, 0, 'z'}, |
|
{"no-normalized-sleepers", no_argument, 0, 'Z'}, |
|
{"fadvise", required_argument, 0, 'a'}, |
|
{"help", no_argument, 0, 'h'}, |
|
{0, 0, 0, 0}, |
|
}; |
|
|
|
void usage() |
|
{ |
|
printf("sdcard_perf_test --test=write|read|read_write|open_create|traverse [options]\n\n" |
|
" -t --test: Select the test.\n" |
|
" -s --size: Size in kbytes of the data.\n" |
|
" -S --chunk-size: Size of a chunk. Default to size ie 1 chunk.\n" |
|
" Data will be written/read using that chunk size.\n" |
|
" -D --depth: Depth of directory tree to create for traversal.\n" |
|
" -i --iterations: Number of time a process should carry its task.\n" |
|
" -p --procnb: Number of processes to use.\n" |
|
" -d --dump: Print the raw timing on stdout.\n" |
|
" -c --cpu-scaling: Enable cpu scaling.\n" |
|
" -s --sync: fsync|sync Use fsync or sync in write test. Default: no sync call.\n" |
|
" -e --truncate: Truncate to size the file on creation.\n" |
|
" -z --no-new-fair-sleepers: Turn them off. You need to mount debugfs.\n" |
|
" -Z --no-normalized-sleepers: Turn them off. You need to mount debugfs.\n" |
|
" -a --fadvise: Specify an fadvise policy (not supported).\n" |
|
); |
|
} |
|
|
|
// Print command line, pid, kernel version, OOM adj and scheduler. |
|
void printHeader(int argc, char **argv, const TestCase& testCase) |
|
{ |
|
printf("# Command: "); |
|
for (int i = 0; i < argc; ++i) |
|
{ |
|
printf("%s ", argv[i]); |
|
} |
|
printf("\n"); |
|
|
|
printf("# Pid: %d\n", getpid()); |
|
|
|
{ |
|
char buffer[kMinKernelVersionBufferSize] = {0, }; |
|
if (kernelVersion(buffer, sizeof(buffer)) > 0) |
|
{ |
|
printf("# Kernel: %s", buffer); |
|
} |
|
} |
|
|
|
// Earlier on, running this test was crashing the phone. It turned |
|
// out that it was using too much memory but its oom_adj value was |
|
// -17 which means disabled. -16 is the system_server and 0 is |
|
// typically what applications run at. The issue is that adb runs |
|
// at -17 and so is this test. We force oom_adj to 0 unless the |
|
// oom_adj option has been used. |
|
// TODO: We talked about adding an option to control oom_adj, not |
|
// sure if we still need that. |
|
int oomAdj = android::pidOutOfMemoryAdj(); |
|
|
|
printf("# Oom_adj: %d ", oomAdj); |
|
if (oomAdj < 0) |
|
{ |
|
android::setPidOutOfMemoryAdj(0); |
|
printf("adjuted to %d", android::pidOutOfMemoryAdj()); |
|
} |
|
printf("\n"); |
|
|
|
{ |
|
char buffer[kMinSchedFeaturesBufferSize] = {0, }; |
|
if (schedFeatures(buffer, sizeof(buffer)) > 0) |
|
{ |
|
printf("# Sched features: %s", buffer); |
|
} |
|
} |
|
printf("# Fadvise: %s\n", testCase.fadviseAsStr()); |
|
} |
|
|
|
// Remove all the files under kTestDir and clear the caches. |
|
void cleanup() { |
|
android::resetDirectory(kTestDir); |
|
android::syncAndDropCaches(); // don't pollute runs. |
|
} |
|
|
|
// @param argc, argv have a wild guess. |
|
// @param[out] testCase Structure built from the cmd line args. |
|
void parseCmdLine(int argc, char **argv, TestCase *testCase)\ |
|
{ |
|
int c; |
|
|
|
while(true) |
|
{ |
|
// getopt_long stores the option index here. |
|
int option_index = 0; |
|
|
|
c = getopt_long (argc, argv, |
|
"hS:s:D:i:p:t:dcf:ezZa:", |
|
long_options, |
|
&option_index); |
|
// Detect the end of the options. |
|
if (c == -1) break; |
|
|
|
switch (c) |
|
{ |
|
case 's': |
|
testCase->setDataSize(atoi(optarg) * 1024); |
|
break; |
|
case 'S': |
|
testCase->setChunkSize(atoi(optarg) * 1024); |
|
break; |
|
case 'D': // tree depth |
|
testCase->setTreeDepth(atoi(optarg)); |
|
break; |
|
case 'i': |
|
testCase->setIter(atoi(optarg)); |
|
printf("# Iterations: %d\n", testCase->iter()); |
|
break; |
|
case 'p': |
|
testCase->setNproc(atoi(optarg)); |
|
printf("# Proc nb: %d\n", testCase->nproc()); |
|
break; |
|
case 't': |
|
testCase->setTypeFromName(optarg); |
|
printf("# Test name %s\n", testCase->name()); |
|
break; |
|
case 'd': |
|
testCase->setDump(); |
|
break; |
|
case 'c': |
|
printf("# Cpu scaling is on\n"); |
|
testCase->setCpuScaling(); |
|
break; |
|
case 'f': |
|
if (strcmp("sync", optarg) == 0) { |
|
testCase->setSync(TestCase::SYNC); |
|
} else if (strcmp("fsync", optarg) == 0) { |
|
testCase->setSync(TestCase::FSYNC); |
|
} |
|
break; |
|
case 'e': // e for empty |
|
printf("# Will truncate to size\n"); |
|
testCase->setTruncateToSize(); |
|
break; |
|
case 'z': // no new fair sleepers |
|
testCase->setNewFairSleepers(false); |
|
break; |
|
case 'Z': // no normalized sleepers |
|
testCase->setNormalizedSleepers(false); |
|
break; |
|
case 'a': // fadvise |
|
testCase->setFadvise(optarg); |
|
break; |
|
case 'h': |
|
usage(); |
|
exit(0); |
|
default: |
|
fprintf(stderr, "Unknown option %s\n", optarg); |
|
exit(EXIT_FAILURE); |
|
} |
|
} |
|
} |
|
|
|
// ---------------------------------------------------------------------- |
|
// READ TEST |
|
|
|
// Read a file. We use a new file each time to avoid any caching |
|
// effect that would happen if we were reading the same file each |
|
// time. |
|
// @param chunk buffer large enough where the chunk read are written. |
|
// @param idx iteration number. |
|
// @param testCase has all the timers and paramters to run the test. |
|
|
|
bool readData(char *const chunk, const int idx, TestCase *testCase) |
|
{ |
|
char filename[80] = {'\0',}; |
|
|
|
sprintf(filename, "%s/file-%d-%d", kTestDir, idx, getpid()); |
|
|
|
testCase->openTimer()->start(); |
|
int fd = open(filename, O_RDONLY); |
|
testCase->openTimer()->stop(); |
|
|
|
if (fd < 0) |
|
{ |
|
fprintf(stderr, "Open read only failed."); |
|
return false; |
|
} |
|
FADVISE(fd, 0, 0, testCase->fadvise()); |
|
|
|
size_t left = testCase->dataSize(); |
|
pid_t *pid = (pid_t*)chunk; |
|
while (left > 0) |
|
{ |
|
char *dest = chunk; |
|
size_t chunk_size = testCase->chunkSize(); |
|
|
|
if (chunk_size > left) |
|
{ |
|
chunk_size = left; |
|
left = 0; |
|
} |
|
else |
|
{ |
|
left -= chunk_size; |
|
} |
|
|
|
testCase->readTimer()->start(); |
|
while (chunk_size > 0) |
|
{ |
|
ssize_t s = read(fd, dest, chunk_size); |
|
if (s < 0) |
|
{ |
|
fprintf(stderr, "Failed to read.\n"); |
|
close(fd); |
|
return false; |
|
} |
|
chunk_size -= s; |
|
dest += s; |
|
} |
|
testCase->readTimer()->stop(); |
|
} |
|
close(fd); |
|
if (testCase->pid() != *pid) |
|
{ |
|
fprintf(stderr, "Wrong pid found @ read block %x != %x\n", testCase->pid(), *pid); |
|
return false; |
|
} |
|
else |
|
{ |
|
return true; |
|
} |
|
} |
|
|
|
|
|
bool testRead(TestCase *testCase) { |
|
// Setup the testcase by writting some dummy files. |
|
const size_t size = testCase->dataSize(); |
|
size_t chunk_size = testCase->chunkSize(); |
|
char *const chunk = new char[chunk_size]; |
|
|
|
memset(chunk, 0xaa, chunk_size); |
|
*((pid_t *)chunk) = testCase->pid(); // write our pid at the beginning of each chunk |
|
|
|
size_t iter = testCase->iter(); |
|
|
|
// since readers are much faster we increase the number of |
|
// iteration to last longer and have concurrent read/write |
|
// thoughout the whole test. |
|
if (testCase->type() == TestCase::READ_WRITE) |
|
{ |
|
iter *= TestCase::kReadWriteFactor; |
|
} |
|
|
|
for (size_t i = 0; i < iter; ++i) |
|
{ |
|
char filename[80] = {'\0',}; |
|
|
|
sprintf(filename, "%s/file-%d-%d", kTestDir, i, testCase->pid()); |
|
int fd = open(filename, O_RDWR | O_CREAT, S_IRWXU); |
|
|
|
size_t left = size; |
|
while (left > 0) |
|
{ |
|
if (chunk_size > left) |
|
{ |
|
chunk_size = left; |
|
} |
|
ssize_t written = write(fd, chunk, chunk_size); |
|
if (written < 0) |
|
{ |
|
fprintf(stderr, "Write failed %d %s.", errno, strerror(errno)); |
|
return false; |
|
} |
|
left -= written; |
|
} |
|
close(fd); |
|
} |
|
if (kVerbose) printf("Child %d all chunk written\n", testCase->pid()); |
|
|
|
android::syncAndDropCaches(); |
|
testCase->signalParentAndWait(); |
|
|
|
// Start the read test. |
|
testCase->testTimer()->start(); |
|
for (size_t i = 0; i < iter; ++i) |
|
{ |
|
if (!readData(chunk, i, testCase)) |
|
{ |
|
return false; |
|
} |
|
} |
|
testCase->testTimer()->stop(); |
|
|
|
delete [] chunk; |
|
return true; |
|
} |
|
|
|
// ---------------------------------------------------------------------- |
|
// WRITE TEST |
|
|
|
bool writeData(const char *const chunk, const int idx, TestCase *testCase) { |
|
char filename[80] = {'\0',}; |
|
|
|
sprintf(filename, "%s/file-%d-%d", kTestDir, idx, getpid()); |
|
testCase->openTimer()->start(); |
|
int fd = open(filename, O_RDWR | O_CREAT, S_IRWXU); // no O_TRUNC, see header comment |
|
testCase->openTimer()->stop(); |
|
|
|
if (fd < 0) |
|
{ |
|
fprintf(stderr, "Open write failed."); |
|
return false; |
|
} |
|
FADVISE(fd, 0, 0, testCase->fadvise()); |
|
|
|
if (testCase->truncateToSize()) |
|
{ |
|
testCase->truncateTimer()->start(); |
|
ftruncate(fd, testCase->dataSize()); |
|
testCase->truncateTimer()->stop(); |
|
} |
|
|
|
size_t left = testCase->dataSize(); |
|
while (left > 0) |
|
{ |
|
const char *dest = chunk; |
|
size_t chunk_size = testCase->chunkSize(); |
|
|
|
if (chunk_size > left) |
|
{ |
|
chunk_size = left; |
|
left = 0; |
|
} |
|
else |
|
{ |
|
left -= chunk_size; |
|
} |
|
|
|
|
|
testCase->writeTimer()->start(); |
|
while (chunk_size > 0) |
|
{ |
|
ssize_t s = write(fd, dest, chunk_size); |
|
if (s < 0) |
|
{ |
|
fprintf(stderr, "Failed to write.\n"); |
|
close(fd); |
|
return false; |
|
} |
|
chunk_size -= s; |
|
dest += s; |
|
} |
|
testCase->writeTimer()->stop(); |
|
} |
|
|
|
if (TestCase::FSYNC == testCase->sync()) |
|
{ |
|
testCase->syncTimer()->start(); |
|
fsync(fd); |
|
testCase->syncTimer()->stop(); |
|
} |
|
else if (TestCase::SYNC == testCase->sync()) |
|
{ |
|
testCase->syncTimer()->start(); |
|
sync(); |
|
testCase->syncTimer()->stop(); |
|
} |
|
close(fd); |
|
return true; |
|
} |
|
|
|
bool testWrite(TestCase *testCase) |
|
{ |
|
const size_t size = testCase->dataSize(); |
|
size_t chunk_size = testCase->chunkSize(); |
|
char *data = new char[chunk_size]; |
|
|
|
memset(data, 0xaa, chunk_size); |
|
// TODO: write the pid at the beginning like in the write |
|
// test. Have a mode where we check the write was correct. |
|
testCase->signalParentAndWait(); |
|
|
|
testCase->testTimer()->start(); |
|
for (size_t i = 0; i < testCase->iter(); ++i) |
|
{ |
|
if (!writeData(data, i, testCase)) |
|
{ |
|
return false; |
|
} |
|
} |
|
testCase->testTimer()->stop(); |
|
delete [] data; |
|
return true; |
|
} |
|
|
|
|
|
// ---------------------------------------------------------------------- |
|
// READ WRITE |
|
|
|
// Mix of read and write test. Even PID run the write test. Odd PID |
|
// the read test. Not fool proof but work most of the time. |
|
bool testReadWrite(TestCase *testCase) |
|
{ |
|
if (getpid() & 0x1) { |
|
return testRead(testCase); |
|
} else { |
|
return testWrite(testCase); |
|
} |
|
} |
|
|
|
// ---------------------------------------------------------------------- |
|
// OPEN CREATE TEST |
|
|
|
bool testOpenCreate(TestCase *testCase) |
|
{ |
|
char filename[80] = {'\0',}; |
|
|
|
testCase->signalParentAndWait(); |
|
testCase->testTimer()->start(); |
|
|
|
for (size_t i = 0; i < testCase->iter(); ++i) |
|
{ |
|
sprintf(filename, "%s/file-%d-%d", kTestDir, i, testCase->pid()); |
|
|
|
int fd = open(filename, O_RDWR | O_CREAT, S_IRWXU); |
|
FADVISE(fd, 0, 0, testCase->fadvise()); |
|
|
|
if (testCase->truncateToSize()) |
|
{ |
|
ftruncate(fd, testCase->dataSize()); |
|
} |
|
if (fd < 0) |
|
{ |
|
return false; |
|
} |
|
close(fd); |
|
} |
|
testCase->testTimer()->stop(); |
|
return true; |
|
} |
|
|
|
bool writeTestFile(TestCase *testCase, const char* filename) { |
|
int fd = open(filename, O_RDWR | O_CREAT, S_IRWXU); |
|
if (fd < 0) { |
|
fprintf(stderr, "open() failed: %s\n", strerror(errno)); |
|
return false; |
|
} |
|
|
|
bool res = false; |
|
|
|
char * const chunk = new char[testCase->chunkSize()]; |
|
memset(chunk, 0xaa, testCase->chunkSize()); |
|
|
|
size_t left = testCase->dataSize(); |
|
while (left > 0) { |
|
char *dest = chunk; |
|
size_t chunk_size = testCase->chunkSize(); |
|
|
|
if (chunk_size > left) { |
|
chunk_size = left; |
|
left = 0; |
|
} else { |
|
left -= chunk_size; |
|
} |
|
|
|
while (chunk_size > 0) { |
|
ssize_t s = write(fd, dest, chunk_size); |
|
if (s < 0) { |
|
fprintf(stderr, "write() failed: %s\n", strerror(errno)); |
|
goto fail; |
|
} |
|
chunk_size -= s; |
|
dest += s; |
|
} |
|
} |
|
|
|
res = true; |
|
fail: |
|
close(fd); |
|
delete[] chunk; |
|
return res; |
|
} |
|
|
|
// ---------------------------------------------------------------------- |
|
// TRAVERSE |
|
|
|
#define MAX_PATH 512 |
|
|
|
// Creates a directory tree that is both deep and wide, and times |
|
// traversal using fts_open(). |
|
bool testTraverse(TestCase *testCase) { |
|
char path[MAX_PATH]; |
|
char filepath[MAX_PATH]; |
|
strcpy(path, kTestDir); |
|
|
|
// Generate a deep directory hierarchy |
|
size_t depth = testCase->treeDepth(); |
|
for (size_t i = 0; i < depth; i++) { |
|
// Go deeper by appending onto current path |
|
snprintf(path + strlen(path), MAX_PATH - strlen(path), "/dir%d", i); |
|
mkdir(path, S_IRWXU); |
|
|
|
// Create some files at this depth |
|
strcpy(filepath, path); |
|
int pathlen = strlen(path); |
|
char* nameStart = filepath + pathlen; |
|
for (size_t j = 0; j < depth; j++) { |
|
snprintf(nameStart, MAX_PATH - pathlen, "/file%d", j); |
|
writeTestFile(testCase, filepath); |
|
} |
|
} |
|
|
|
testCase->signalParentAndWait(); |
|
testCase->testTimer()->start(); |
|
|
|
// Now traverse structure |
|
size_t iter = testCase->iter(); |
|
for (size_t i = 0; i < iter; i++) { |
|
testCase->traverseTimer()->start(); |
|
|
|
FTS *ftsp; |
|
if ((ftsp = fts_open((char **) &kTestDir, FTS_LOGICAL | FTS_XDEV, NULL)) == NULL) { |
|
fprintf(stderr, "fts_open() failed: %s\n", strerror(errno)); |
|
return false; |
|
} |
|
|
|
// Count discovered files |
|
int dirs = 0, files = 0; |
|
|
|
FTSENT *curr; |
|
while ((curr = fts_read(ftsp)) != NULL) { |
|
switch (curr->fts_info) { |
|
case FTS_D: |
|
dirs++; |
|
break; |
|
case FTS_F: |
|
files++; |
|
break; |
|
} |
|
} |
|
|
|
fts_close(ftsp); |
|
|
|
testCase->traverseTimer()->stop(); |
|
|
|
int expectedDirs = depth + 1; |
|
if (expectedDirs != dirs) { |
|
fprintf(stderr, "expected %d dirs, but found %d\n", expectedDirs, dirs); |
|
return false; |
|
} |
|
|
|
int expectedFiles = depth * depth; |
|
if (expectedFiles != files) { |
|
fprintf(stderr, "expected %d files, but found %d\n", expectedFiles, files); |
|
return false; |
|
} |
|
} |
|
|
|
testCase->testTimer()->stop(); |
|
return true; |
|
} |
|
|
|
} // anonymous namespace |
|
|
|
int main(int argc, char **argv) |
|
{ |
|
android_test::TestCase testCase(kAppName); |
|
|
|
cleanup(); |
|
|
|
parseCmdLine(argc, argv, &testCase); |
|
printHeader(argc, argv, testCase); |
|
|
|
printf("# File size %d kbytes\n", testCase.dataSize() / 1024); |
|
printf("# Chunk size %d kbytes\n", testCase.chunkSize() / 1024); |
|
printf("# Sync: %s\n", testCase.syncAsStr()); |
|
|
|
if (!testCase.cpuScaling()) |
|
{ |
|
android::disableCpuScaling(); |
|
} |
|
|
|
switch(testCase.type()) { |
|
case TestCase::WRITE: |
|
testCase.mTestBody = testWrite; |
|
break; |
|
case TestCase::READ: |
|
testCase.mTestBody = testRead; |
|
break; |
|
case TestCase::OPEN_CREATE: |
|
testCase.mTestBody = testOpenCreate; |
|
break; |
|
case TestCase::READ_WRITE: |
|
testCase.mTestBody = testReadWrite; |
|
break; |
|
case TestCase::TRAVERSE: |
|
testCase.mTestBody = testTraverse; |
|
break; |
|
default: |
|
fprintf(stderr, "Unknown test type %s", testCase.name()); |
|
exit(EXIT_FAILURE); |
|
} |
|
|
|
testCase.createTimers(); |
|
|
|
bool ok; |
|
|
|
ok = testCase.runTest(); |
|
|
|
cleanup(); |
|
if (!ok) |
|
{ |
|
printf("error %d %s", errno, strerror(errno)); |
|
exit(EXIT_FAILURE); |
|
} |
|
else |
|
{ |
|
exit(EXIT_SUCCESS); |
|
} |
|
}
|
|
|