| /* |
| * 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 "testcase.h" |
| #include <hardware_legacy/power.h> // wake lock |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| #include <unistd.h> |
| #include <linux/fadvise.h> |
| |
| namespace { |
| const bool kVerbose = false; |
| } |
| |
| namespace android_test { |
| |
| TestCase::TestCase(const char *appName) |
| : mTestBody(NULL), mAppName(appName), mDataSize(1000 * 1000), |
| mChunkSize(mDataSize), mTreeDepth(8), mIter(20), mNproc(1), |
| mType(UNKNOWN_TEST), mDump(false), mCpuScaling(false), |
| mSync(NO_SYNC), mFadvice(POSIX_FADV_NORMAL), mTruncateToSize(false), |
| mTestTimer(NULL) |
| { |
| // Make sure the cpu and phone are fully awake. The |
| // FULL_WAKE_LOCK was used by java apps and don't do |
| // anything. Also the partial wake lock is a nop if the phone |
| // is plugged in via USB. |
| acquire_wake_lock(PARTIAL_WAKE_LOCK, mAppName); |
| mPid = getpid(); |
| setNewFairSleepers(true); |
| setNormalizedSleepers(true); |
| if (pipe(mIpc) < 0) |
| { |
| fprintf(stderr, "pipe failed\n"); |
| exit(1); |
| } |
| if (pipe(mIpc + 2) < 0) |
| { |
| fprintf(stderr, "pipe failed\n"); |
| exit(1); |
| } |
| } |
| |
| TestCase::~TestCase() |
| { |
| release_wake_lock(mAppName); |
| for (int i = 0; i < 4; ++i) close(mIpc[i]); |
| delete mTestTimer; |
| delete mOpenTimer; |
| } |
| |
| |
| bool TestCase::runTest() |
| { |
| if (UNKNOWN_TEST == mType) |
| { |
| fprintf(stderr, "No test set."); |
| return false; |
| } |
| for (size_t p = 0; p < mNproc; ++p) |
| { |
| pid_t childpid = android::forkOrExit(); |
| |
| if (0 == childpid) { // I am a child, run the test. |
| mPid = getpid(); |
| close(mIpc[READ_FROM_CHILD]); |
| close(mIpc[WRITE_TO_CHILD]); |
| |
| if (kVerbose) printf("Child pid: %d\n", mPid); |
| if (!mTestBody(this)) { |
| printf("Test failed\n"); |
| } |
| char buffer[32000] = {0,}; |
| char *str = buffer; |
| size_t size_left = sizeof(buffer); |
| |
| testTimer()->sprint(&str, &size_left); |
| if(openTimer()->used()) openTimer()->sprint(&str, &size_left); |
| if(readTimer()->used()) readTimer()->sprint(&str, &size_left); |
| if(writeTimer()->used()) writeTimer()->sprint(&str, &size_left); |
| if(syncTimer()->used()) syncTimer()->sprint(&str, &size_left); |
| if(truncateTimer()->used()) truncateTimer()->sprint(&str, &size_left); |
| if(traverseTimer()->used()) traverseTimer()->sprint(&str, &size_left); |
| |
| write(mIpc[TestCase::WRITE_TO_PARENT], buffer, str - buffer); |
| |
| |
| close(mIpc[WRITE_TO_PARENT]); |
| close(mIpc[READ_FROM_PARENT]); |
| exit(EXIT_SUCCESS); |
| } |
| } |
| // I am the parent process |
| close(mIpc[WRITE_TO_PARENT]); |
| close(mIpc[READ_FROM_PARENT]); |
| |
| // Block until all the children have reported for |
| // duty. Unblock them so they start the work. |
| if (!android::waitForChildrenAndSignal(mNproc, mIpc[READ_FROM_CHILD], mIpc[WRITE_TO_CHILD])) |
| { |
| exit(1); |
| } |
| |
| // Process the output of each child. |
| // TODO: handle EINTR |
| char buffer[32000] = {0,}; |
| while(read(mIpc[READ_FROM_CHILD], buffer, sizeof(buffer)) != 0) |
| { |
| printf("%s", buffer); |
| fflush(stdout); |
| memset(buffer, 0, sizeof(buffer)); |
| } |
| // Parent is waiting for children to exit. |
| android::waitForChildrenOrExit(mNproc); |
| return true; |
| } |
| |
| void TestCase::setIter(size_t iter) |
| { |
| mIter = iter; |
| } |
| |
| void TestCase::createTimers() |
| { |
| char total_time[80]; |
| |
| snprintf(total_time, sizeof(total_time), "%s_total", mName); |
| mTestTimer = new StopWatch(total_time, 1); |
| mTestTimer->setDataSize(dataSize()); |
| |
| mOpenTimer = new StopWatch("open", iter() * kReadWriteFactor); |
| |
| mReadTimer = new StopWatch("read", iter() * dataSize() / chunkSize() * kReadWriteFactor); |
| mReadTimer->setDataSize(dataSize()); |
| |
| mWriteTimer = new StopWatch("write", iter() * dataSize() / chunkSize()); |
| mWriteTimer->setDataSize(dataSize()); |
| |
| mSyncTimer = new StopWatch("sync", iter()); |
| |
| mTruncateTimer = new StopWatch("truncate", iter()); |
| |
| mTraverseTimer = new StopWatch("traversal", iter()); |
| } |
| |
| bool TestCase::setTypeFromName(const char *test_name) |
| { |
| strcpy(mName, test_name); |
| if (strcmp(mName, "write") == 0) mType = WRITE; |
| if (strcmp(mName, "read") == 0) mType = READ; |
| if (strcmp(mName, "read_write") == 0) mType = READ_WRITE; |
| if (strcmp(mName, "open_create") == 0) mType = OPEN_CREATE; |
| if (strcmp(mName, "traverse") == 0) mType = TRAVERSE; |
| |
| return UNKNOWN_TEST != mType; |
| } |
| |
| void TestCase::setSync(Sync s) |
| { |
| mSync = s; |
| } |
| |
| const char *TestCase::syncAsStr() const |
| { |
| return mSync == NO_SYNC ? "disabled" : (mSync == FSYNC ? "fsync" : "sync"); |
| } |
| |
| void TestCase::setFadvise(const char *advice) |
| { |
| mFadvice = POSIX_FADV_NORMAL; |
| if (strcmp(advice, "sequential") == 0) |
| { |
| mFadvice = POSIX_FADV_SEQUENTIAL; |
| } |
| else if (strcmp(advice, "random") == 0) |
| { |
| mFadvice = POSIX_FADV_RANDOM; |
| } |
| else if (strcmp(advice, "noreuse") == 0) |
| { |
| mFadvice = POSIX_FADV_NOREUSE; |
| } |
| else if (strcmp(advice, "willneed") == 0) |
| { |
| mFadvice = POSIX_FADV_WILLNEED; |
| } |
| else if (strcmp(advice, "dontneed") == 0) |
| { |
| mFadvice = POSIX_FADV_DONTNEED; |
| } |
| } |
| |
| const char *TestCase::fadviseAsStr() const |
| { |
| switch (mFadvice) { |
| case POSIX_FADV_NORMAL: return "fadv_normal"; |
| case POSIX_FADV_SEQUENTIAL: return "fadv_sequential"; |
| case POSIX_FADV_RANDOM: return "fadv_random"; |
| case POSIX_FADV_NOREUSE: return "fadv_noreuse"; |
| case POSIX_FADV_WILLNEED: return "fadv_willneed"; |
| case POSIX_FADV_DONTNEED: return "fadv_dontneed"; |
| default: return "fadvice_unknown"; |
| } |
| } |
| |
| |
| } // namespace android_test |