605 lines
14 KiB
C++
605 lines
14 KiB
C++
/*
|
|
* 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
|