blob: 07eeb4cf50ccf86f5a3a9cce62075fbfc43dd72a [file] [log] [blame]
// Copyright 2014 The Goma Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef __linux__
#error "We only expect this is used by Linux."
#endif
#include "cros_util.h"
#include <memory>
#include "absl/strings/ascii.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "basictypes.h"
#include "file_helper.h"
#include "glog/logging.h"
#include "glog/stl_logging.h"
#include "ioutil.h"
#include "scoped_fd.h"
#include "util.h"
namespace {
const char* const kDefaultBlacklist[] = {
"/dev-libs/nss", // make -j fails
"/app-crypt/nss", // make -j fails
"/dev-libs/m17n-lib", // make -j fails
"/sys-fs/mtools", // make -j fails
"/dev-java/icedtea", // make -j fails
"/dev-libs/openssl", // Makefile force -j1
};
} // namespace
namespace devtools_goma {
std::vector<string> ParseBlacklistContents(const string& contents) {
std::vector<string> parsed;
for (auto&& line : absl::StrSplit(contents,
absl::ByAnyChar("\r\n"),
absl::SkipEmpty())) {
absl::string_view stripped_line = absl::StripAsciiWhitespace(line);
if (!stripped_line.empty()) {
parsed.push_back(string(stripped_line));
}
}
return parsed;
}
std::vector<string> GetBlacklist() {
const char* blacklist_file = getenv("GOMACC_BLACKLIST");
if (blacklist_file == nullptr) {
std::vector<string> default_blacklist;
for (const auto& it : kDefaultBlacklist) {
default_blacklist.push_back(it);
}
return default_blacklist;
}
string contents;
CHECK(ReadFileToString(blacklist_file, &contents))
<< "Failed to read GOMACC_BLACKLIST=" << blacklist_file;
return ParseBlacklistContents(contents);
}
bool IsBlacklisted(const string& path, const std::vector<string>& blacklist) {
for (size_t i = 0; i < blacklist.size(); ++i) {
if (path.find(blacklist[i]) != string::npos) {
LOG(INFO) << "The path is blacklisted. "
<< " path=" << path;
return true;
}
}
return false;
}
float GetLoadAverage() {
string line;
ScopedFd fd(ScopedFd::OpenForRead("/proc/loadavg"));
if (!fd.valid()) {
PLOG(ERROR) << "failed to open /proc/loadavg";
return -1;
}
char buf[1024];
int r = fd.Read(buf, sizeof(buf) - 1);
if (r < 5) { // should read at least "x.yz "
PLOG(ERROR) << "failed to read /proc/loadavg";
return -1;
}
buf[r] = '\0';
std::vector<string> loadavgs = ToVector(
absl::StrSplit(buf, absl::ByAnyChar(" \t"), absl::SkipEmpty()));
if (loadavgs.empty()) {
LOG(ERROR) << "failed to get load average.";
return -1;
}
char* endptr;
float load = strtof(loadavgs[0].c_str(), &endptr);
if (loadavgs[0].c_str() == endptr) {
LOG(ERROR) << "failed to parse load average."
<< " buf=" << buf
<< " loadavgs[0]=" << loadavgs[0];
return -1;
}
return load;
}
int64_t RandInt64(int64_t a, int64_t b) {
static bool initialized = false;
if (!initialized) {
srand(absl::ToInt64Nanoseconds(
absl::Now().In(absl::UTCTimeZone()).subsecond));
}
int64_t rand_value = (static_cast<uint64_t>(rand()) << 32) + rand();
return a + rand_value % (b - a + 1);
}
bool CanGomaccHandleCwd() {
const std::vector<string> blacklist = GetBlacklist();
std::unique_ptr<char, decltype(&free)> cwd(getcwd(nullptr, 0), free);
if (IsBlacklisted(cwd.get(), blacklist) || getuid() == 0) {
return false;
}
return true;
}
void WaitUntilLoadAvgLowerThan(float load, absl::Duration max_sleep_time) {
CHECK_GT(load, 0.0)
<< "load must be larger than 0. Or, this function won't finish."
<< " load=" << load;
CHECK(max_sleep_time > absl::ZeroDuration())
<< "Max sleep time should be larger than 0 seconds."
<< " max_sleep_time=" << max_sleep_time;
absl::Time current_time, last_update;
current_time = last_update = absl::Now();
absl::Duration sleep_time = absl::Seconds(1);
for (;;) {
float current_loadavg = GetLoadAverage();
CHECK_GE(current_loadavg, 0.0)
<< "load average < 0. Possibly GetLoadAverage is broken."
<< " current_loadavg=" << current_loadavg;
if (current_loadavg < load)
break;
current_time = absl::Now();
if (current_time - last_update > max_sleep_time) {
LOG(WARNING) << "waiting."
<< " load=" << load
<< " current_loadavg=" << current_loadavg
<< " max_sleep_time=" << max_sleep_time;
last_update = current_time;
}
sleep_time *= 2;
if (sleep_time > max_sleep_time)
sleep_time = max_sleep_time;
absl::SleepFor(
absl::Nanoseconds(
RandInt64(absl::ToInt64Nanoseconds(absl::Seconds(1)),
absl::ToInt64Nanoseconds(sleep_time))));
}
}
} // namespace devtools_goma