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