// Copyright 2022 The Centipede Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "./centipede/rusage_stats.h" #ifdef __APPLE__ #include #include #include #endif // __APPLE__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // NOLINT: For hardware_concurrency() only. #include "absl/base/nullability.h" #include "absl/log/check.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/time/clock.h" #include "absl/time/time.h" namespace fuzztest::internal { //------------------------------------------------------------------------------ // ProcessTimer //------------------------------------------------------------------------------ ProcessTimer::ProcessTimer() : start_time_{absl::Now()}, start_rusage_{} { getrusage(RUSAGE_SELF, &start_rusage_); } void ProcessTimer::Get(double& user, double& sys, double& wall) const { struct rusage curr_rusage = {}; getrusage(RUSAGE_SELF, &curr_rusage); // clang-format off user = absl::ToDoubleSeconds( absl::DurationFromTimeval(curr_rusage.ru_utime) - absl::DurationFromTimeval(start_rusage_.ru_utime)); sys = absl::ToDoubleSeconds( absl::DurationFromTimeval(curr_rusage.ru_stime) - absl::DurationFromTimeval(start_rusage_.ru_stime)); wall = absl::ToDoubleSeconds(absl::Now() - start_time_); // clang-format on } //------------------------------------------------------------------------------ // RUsageScope //------------------------------------------------------------------------------ #ifdef __APPLE__ class RUsageScope::PlatformInfo { public: PlatformInfo(pid_t pid) : pid_(pid) {} pid_t pid() const { return pid_; } private: pid_t pid_; }; #else class RUsageScope::PlatformInfo { public: enum ProcFile : size_t { kSched = 0, kStatm = 1, kStatus = 2, kNumDoNotUseDirectly = 3 }; PlatformInfo(pid_t pid) : proc_file_paths_{ absl::StrFormat("/proc/%d/sched", pid), absl::StrFormat("/proc/%d/statm", pid), absl::StrFormat("/proc/%d/status", pid), } {} // Returns a path to the /proc// or /proc//task//. [[nodiscard]] const std::string& GetProcFilePath(ProcFile file) const { CHECK_LT(file, proc_file_paths_.size()); return proc_file_paths_[file]; } private: std::array proc_file_paths_; }; #endif RUsageScope RUsageScope::ThisProcess() { // return RUsageScope{getpid()}; } RUsageScope RUsageScope::Process(pid_t pid) { // return RUsageScope{pid}; } RUsageScope::RUsageScope(pid_t pid) : description_{absl::StrFormat("PID=%d", pid)}, info_(std::make_shared(pid)) {} namespace detail { namespace { // A global static is fine: this object depends on getrusage() syscall ONLY, and // absolutely no other globals in the program. const ProcessTimer global_process_timer; //------------------------------------------------------------------------------ // Read values from /proc/* files //------------------------------------------------------------------------------ bool ReadProcFileFields(const std::string& path, const char* absl_nonnull format, ...) { bool success = false; va_list value_list; va_start(value_list, format); std::ifstream file{path}; // TODO(b/265461840): Silently ignoring missing /proc/ files. The current // callers ignore the returned status too. Improve. if (file.good()) { std::stringstream contents; contents << file.rdbuf(); if (contents.good()) { if (vsscanf(contents.str().c_str(), format, value_list) != EOF) { success = true; } } } va_end(value_list); return success; } template bool ReadProcFileKeyword( // const std::string& path, const char* format, T* value) { std::ifstream file{path}; // TODO(b/265461840): Silently ignoring missing /proc/ files. The current // callers ignore the returned status too. Improve. if (file.good()) { constexpr std::streamsize kMaxLineLen = 1024; char line[kMaxLineLen] = {0}; while (file.good()) { file.getline(line, kMaxLineLen); if (sscanf(line, format, value) == 1) { return true; } } } return false; } //------------------------------------------------------------------------------ // Comparison overloads //------------------------------------------------------------------------------ template std::string NormalizeSign(T* value, bool always_signed) { if (*value < T{}) { *value = -(*value); return "-"; } else if (always_signed) { return "+"; } else { return ""; } } template