/* * 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), 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); 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()); } 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; 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