summaryrefslogtreecommitdiff
path: root/tests/sdcard/sysutil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/sdcard/sysutil.cpp')
-rw-r--r--tests/sdcard/sysutil.cpp604
1 files changed, 604 insertions, 0 deletions
diff --git a/tests/sdcard/sysutil.cpp b/tests/sdcard/sysutil.cpp
new file mode 100644
index 00000000..0182590a
--- /dev/null
+++ b/tests/sdcard/sysutil.cpp
@@ -0,0 +1,604 @@
+/*
+ * 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 <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include "sysutil.h"
+
+namespace {
+const int kError = -1;
+// Max number of retries on EAGAIN and EINTR. Totally arbitrary.
+const int kMaxAttempts = 8;
+
+// How long to wait after a cache purge. A few seconds (arbitrary).
+const int kCachePurgeSleepDuration = 2; // seconds
+
+const bool kSilentIfMissing = false;
+
+const char *kKernelVersion = "/proc/version";
+const char *kScalingGovernorFormat = "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_governor";
+const char *kDropCaches = "/proc/sys/vm/drop_caches";
+const char *kSchedFeatures = "/sys/kernel/debug/sched_features";
+const char *kNewFairSleepers = "NEW_FAIR_SLEEPERS";
+const char *kNoNewFairSleepers = "NO_NEW_FAIR_SLEEPERS";
+const char *kNormalizedSleepers = "NORMALIZED_SLEEPER"; // no 's' at the end
+const char *kNoNormalizedSleepers = "NO_NORMALIZED_SLEEPER";
+
+const char *kDebugfsWarningMsg = "Did you 'adb root; adb shell mount -t debugfs none /sys/kernel/debug' ?";
+
+// TODO: Surely these file utility functions must exist already. A
+// quick grep did not turn up anything. Look harder later.
+
+void printErrno(const char *msg, const char *filename)
+{
+ fprintf(stderr, "# %s %s %d %s\n", msg, filename, errno, strerror(errno));
+}
+
+// Read a C-string from a file. If the buffer is too short, an error
+// message will be printed on stderr.
+// @param filename Of the file to read.
+// @param start Buffer where the data should be written to.
+// @param size The size of the buffer pointed by str. Must be >= 1.
+// @return The number of characters read (not including the trailing'\0' used
+// to end the string) or -1 if there was an error.
+int readStringFromFile(const char *filename, char *const start, size_t size, bool must_exist=true)
+{
+ if (NULL == start || size == 0)
+ {
+ return 0;
+ }
+ char *end = start;
+ int fd = open(filename, O_RDONLY);
+
+ if (fd < 0)
+ {
+ if (ENOENT != errno || must_exist)
+ {
+ printErrno("Failed to open", filename);
+ }
+ return kError;
+ }
+
+ bool eof = false;
+ bool error = false;
+ int attempts = 0;
+
+ --size; // reserve space for trailing '\0'
+
+ while (size > 0 && !error && !eof && attempts < kMaxAttempts)
+ {
+ ssize_t s;
+
+ s = read(fd, end, size);
+
+ if (s < 0)
+ {
+ error = EAGAIN != errno && EINTR != errno;
+ if (error)
+ {
+ printErrno("Failed to read", filename);
+ }
+ }
+ else if (0 == s)
+ {
+ eof = true;
+ }
+ else
+ {
+ end += s;
+ size -= s;
+ }
+ ++attempts;
+ }
+
+ close(fd);
+
+ if (error)
+ {
+ return kError;
+ }
+ else
+ {
+ *end = '\0';
+ if (!eof)
+ {
+ fprintf(stderr, "Buffer too small for %s\n", filename);
+ }
+ return end - start;
+ }
+}
+
+// Write a C string ('\0' terminated) to a file.
+//
+int writeStringToFile(const char *filename, const char *start, bool must_exist=true)
+{
+ int fd = open(filename, O_WRONLY);
+
+
+ if (fd < 0)
+ {
+ if (ENOENT != errno || must_exist)
+ {
+ printErrno("Failed to open", filename);
+ }
+ return kError;
+ }
+
+ const size_t len = strlen(start);
+ size_t size = len;
+ bool error = false;
+ int attempts = 0;
+
+ while (size > 0 && !error && attempts < kMaxAttempts)
+ {
+ ssize_t s = write(fd, start, size);
+
+ if (s < 0)
+ {
+ error = EAGAIN != errno && EINTR != errno;
+ if (error)
+ {
+ printErrno("Failed to write", filename);
+ }
+ }
+ else
+ {
+ start += s;
+ size -= s;
+ }
+ ++attempts;
+ }
+ close(fd);
+
+ if (error)
+ {
+ return kError;
+ }
+ else
+ {
+ if (size > 0)
+ {
+ fprintf(stderr, "Partial write to %s (%d out of %d)\n",
+ filename, size, len);
+ }
+ return len - size;
+ }
+}
+
+int writeIntToFile(const char *filename, long value)
+{
+ char buffer[16] = {0,};
+ sprintf(buffer, "%ld", value);
+ return writeStringToFile(filename, buffer);
+}
+
+// @return a message describing the reason why the child exited. The
+// message is in a shared buffer, not thread safe, erased by
+// subsequent calls.
+const char *reasonChildExited(int status)
+{
+ static char buffer[80];
+
+ if (WIFEXITED(status))
+ {
+ snprintf(buffer, sizeof(buffer), "ok (%d)", WEXITSTATUS(status));
+ }
+ else if (WIFSIGNALED(status))
+ {
+ snprintf(buffer, sizeof(buffer), "signaled (%d %s)", WTERMSIG(status), strsignal(WTERMSIG(status)));
+ }
+ else
+ {
+ snprintf(buffer, sizeof(buffer), "stopped?");
+ }
+ return buffer;
+}
+
+
+} // anonymous namespace
+
+namespace android {
+
+int kernelVersion(char *str, size_t size)
+{
+ return readStringFromFile(kKernelVersion, str, size);
+}
+
+int pidOutOfMemoryAdj()
+{
+ char filename[FILENAME_MAX];
+
+ snprintf(filename, sizeof(filename), "/proc/%d/oom_adj", getpid());
+
+ char value[16];
+ if (readStringFromFile(filename, value, sizeof(value)) == -1)
+ {
+ return -127;
+ }
+ else
+ {
+ return atoi(value);
+ }
+}
+
+void setPidOutOfMemoryAdj(int level)
+{
+ char filename[FILENAME_MAX];
+
+ snprintf(filename, sizeof(filename), "/proc/%d/oom_adj", getpid());
+ writeIntToFile(filename, level);
+}
+
+void disableCpuScaling()
+{
+ for (int cpu = 0; cpu < 16; ++cpu) // 16 cores mobile phones, abestos pockets recommended.
+ {
+ char governor[FILENAME_MAX];
+ sprintf(governor, kScalingGovernorFormat, cpu);
+
+ if (writeStringToFile(governor, "performance", kSilentIfMissing) < 0)
+ {
+ if (cpu > 0 && errno == ENOENT)
+ {
+ break; // cpu1 or above not found, ok since we have cpu0.
+ }
+ fprintf(stderr, "Failed to write to scaling governor file for cpu %d: %d %s",
+ cpu, errno, strerror(errno));
+ break;
+ }
+ }
+}
+
+int schedFeatures(char *str, size_t size)
+{
+ return readStringFromFile(kSchedFeatures, str, size);
+}
+
+bool newFairSleepers()
+{
+ char value[256] = {0,};
+
+ if (readStringFromFile(kSchedFeatures, value, sizeof(value)) == -1)
+ {
+ printErrno(kDebugfsWarningMsg, kSchedFeatures);
+ return false;
+ }
+ return strstr(value, "NO_NEW_FAIR_SLEEPERS") == NULL;
+}
+
+void setNewFairSleepers(bool on)
+{
+ int retcode;
+
+ if (on)
+ {
+ retcode = writeStringToFile(kSchedFeatures, kNewFairSleepers);
+ }
+ else
+ {
+ retcode = writeStringToFile(kSchedFeatures, kNoNewFairSleepers);
+ }
+ if (retcode < 0)
+ {
+ fprintf(stderr, "# %s\n", kDebugfsWarningMsg);
+ }
+}
+
+bool normalizedSleepers()
+{
+ char value[256] = {0,};
+
+ if (readStringFromFile(kSchedFeatures, value, sizeof(value)) == -1)
+ {
+ printErrno(kDebugfsWarningMsg, kSchedFeatures);
+ return false;
+ }
+ return strstr(value, "NO_NEW_FAIR_SLEEPERS") == NULL;
+}
+
+void setNormalizedSleepers(bool on)
+{
+ int retcode;
+
+ if (on)
+ {
+ retcode = writeStringToFile(kSchedFeatures, kNormalizedSleepers);
+ }
+ else
+ {
+ retcode = writeStringToFile(kSchedFeatures, kNoNormalizedSleepers);
+ }
+ if (retcode < 0)
+ {
+ fprintf(stderr, "# %s\n", kDebugfsWarningMsg);
+ }
+}
+
+pid_t forkOrExit()
+{
+ pid_t childpid = fork();
+
+ if (-1 == childpid)
+ {
+ fprintf(stderr, "Fork failed: %d %s", errno, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ return childpid;
+}
+
+void waitForChildrenOrExit(int num)
+{
+ while (num > 0)
+ {
+ int status;
+ pid_t pid = wait(&status);
+ if (-1 == pid)
+ {
+ fprintf(stderr, "Wait failed\n");
+ }
+ else
+ {
+ if (!WIFEXITED(status))
+ {
+ fprintf(stderr, "Child pid %d did not exit cleanly %s\n",
+ pid, reasonChildExited(status));
+ exit(EXIT_FAILURE);
+ }
+ }
+ --num;
+ }
+}
+
+// Sync and cache cleaning functions. In the old hpux days I was told
+// to always call *sync twice. The same advice seems to be still true
+// today so *sync is called twice.
+// Also we wait 'a little' to give a chance to background threads to
+// purge their caches.
+void syncAndDropCaches(int code)
+{
+ sync();
+ sync();
+ writeIntToFile(kDropCaches, code);
+ sleep(kCachePurgeSleepDuration);
+}
+
+
+void fsyncAndDropCaches(int fd, int code)
+{
+ fsync(fd);
+ fsync(fd);
+ writeIntToFile(kDropCaches, code);
+ sleep(kCachePurgeSleepDuration);
+}
+
+
+void resetDirectory(const char *directory)
+{
+ DIR *dir = opendir(directory);
+
+ if (NULL != dir)
+ {
+ struct dirent *entry;
+ char name_buffer[PATH_MAX];
+
+ while((entry = readdir(dir)))
+ {
+ if (0 == strcmp(entry->d_name, ".")
+ || 0 == strcmp(entry->d_name, "..")
+ || 0 == strcmp(entry->d_name, "lost+found"))
+ {
+ continue;
+ }
+ strcpy(name_buffer, directory);
+ strcat(name_buffer, "/");
+ strcat(name_buffer, entry->d_name);
+ unlink(name_buffer);
+ }
+ closedir(dir);
+ } else {
+ mkdir(directory, S_IRWXU);
+ }
+}
+
+
+// IPC
+bool writePidAndWaitForReply(int writefd, int readfd)
+{
+ if (writefd > readfd)
+ {
+ fprintf(stderr, "Called with args in wrong order!!\n");
+ return false;
+ }
+ pid_t pid = getpid();
+ char *start = reinterpret_cast<char *>(&pid);
+ size_t size = sizeof(pid);
+ bool error = false;
+ int attempts = 0;
+
+ while (size > 0 && !error && attempts < kMaxAttempts)
+ {
+ ssize_t s = write(writefd, start, size);
+
+ if (s < 0)
+ {
+ error = EAGAIN != errno && EINTR != errno;
+ if (error)
+ {
+ printErrno("Failed to write", "parent");
+ }
+ }
+ else
+ {
+ start += s;
+ size -= s;
+ }
+ ++attempts;
+ }
+
+ if (error || 0 != size)
+ {
+ return false;
+ }
+
+ bool eof = false;
+ char dummy;
+ size = sizeof(dummy);
+ error = false;
+ attempts = 0;
+
+ while (size > 0 && !error && !eof && attempts < kMaxAttempts)
+ {
+ ssize_t s;
+
+ s = read(readfd, &dummy, size);
+
+ if (s < 0)
+ {
+ error = EAGAIN != errno && EINTR != errno;
+ if (error)
+ {
+ printErrno("Failed to read", "parent");
+ }
+ }
+ else if (0 == s)
+ {
+ eof = true;
+ }
+ else
+ {
+ size -= s;
+ }
+ ++attempts;
+ }
+ if (error || 0 != size)
+ {
+ return false;
+ }
+ return true;
+}
+
+
+
+bool waitForChildrenAndSignal(int mProcessNb, int readfd, int writefd)
+{
+ if (readfd > writefd)
+ {
+ fprintf(stderr, "Called with args in wrong order!!\n");
+ return false;
+ }
+
+ bool error;
+ int attempts;
+ size_t size;
+
+ for (int p = 0; p < mProcessNb; ++p)
+ {
+ bool eof = false;
+ pid_t pid;
+ char *end = reinterpret_cast<char *>(&pid);
+
+ error = false;
+ attempts = 0;
+ size = sizeof(pid);
+
+ while (size > 0 && !error && !eof && attempts < kMaxAttempts)
+ {
+ ssize_t s;
+
+ s = read(readfd, end, size);
+
+ if (s < 0)
+ {
+ error = EAGAIN != errno && EINTR != errno;
+ if (error)
+ {
+ printErrno("Failed to read", "child");
+ }
+ }
+ else if (0 == s)
+ {
+ eof = true;
+ }
+ else
+ {
+ end += s;
+ size -= s;
+ }
+ ++attempts;
+ }
+
+ if (error || 0 != size)
+ {
+ return false;
+ }
+ }
+
+ for (int p = 0; p < mProcessNb; ++p)
+ {
+ char dummy;
+
+ error = false;
+ attempts = 0;
+ size = sizeof(dummy);
+
+ while (size > 0 && !error && attempts < kMaxAttempts)
+ {
+ ssize_t s = write(writefd, &dummy, size);
+
+ if (s < 0)
+ {
+ error = EAGAIN != errno && EINTR != errno;
+ if (error)
+ {
+ printErrno("Failed to write", "child");
+ }
+ }
+ else
+ {
+ size -= s;
+ }
+ ++attempts;
+ }
+
+ if (error || 0 != size)
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace android