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