/* * 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