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