#include "util/usage.hh" #include "util/exception.hh" #include #include #include #include #include #include #include #if !defined(_WIN32) && !defined(_WIN64) #include #include #include #include #endif namespace util { #if !defined(_WIN32) && !defined(_WIN64) namespace { float FloatSec(const struct timeval &tv) { return static_cast(tv.tv_sec) + (static_cast(tv.tv_usec) / 1000000.0); } float FloatSec(const struct timespec &tv) { return static_cast(tv.tv_sec) + (static_cast(tv.tv_nsec) / 1000000000.0); } const char *SkipSpaces(const char *at) { for (; *at == ' ' || *at == '\t'; ++at) {} return at; } class RecordStart { public: RecordStart() { #ifdef CLOCK_MONOTONIC clock_gettime(CLOCK_MONOTONIC, &started_); #endif } const struct timespec &Started() const { return started_; } private: struct timespec started_; }; const RecordStart kRecordStart; } // namespace #endif void PrintUsage(std::ostream &out) { #if !defined(_WIN32) && !defined(_WIN64) // Linux doesn't set memory usage in getrusage :-( std::set headers; headers.insert("VmPeak:"); headers.insert("VmRSS:"); headers.insert("Name:"); std::ifstream status("/proc/self/status", std::ios::in); std::string header, value; while ((status >> header) && getline(status, value)) { if (headers.find(header) != headers.end()) { out << header << SkipSpaces(value.c_str()) << '\t'; } } struct rusage usage; if (getrusage(RUSAGE_CHILDREN, &usage)) { perror("getrusage"); return; } out << "RSSMax:" << usage.ru_maxrss << " kB" << '\t'; out << "user:" << FloatSec(usage.ru_utime) << "\tsys:" << FloatSec(usage.ru_stime) << '\t'; out << "CPU:" << (FloatSec(usage.ru_utime) + FloatSec(usage.ru_stime)); struct timespec current; #ifdef CLOCK_MONOTONIC clock_gettime(CLOCK_MONOTONIC, ¤t); #endif out << "\treal:" << (FloatSec(current) - FloatSec(kRecordStart.Started())) << '\n'; #endif } uint64_t GuessPhysicalMemory() { #if defined(_WIN32) || defined(_WIN64) return 0; #elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE) long pages = sysconf(_SC_PHYS_PAGES); if (pages == -1) return 0; long page_size = sysconf(_SC_PAGESIZE); if (page_size == -1) return 0; return static_cast(pages) * static_cast(page_size); #else return 0; #endif } namespace { class SizeParseError : public Exception { public: explicit SizeParseError(const std::string &str) throw() { *this << "Failed to parse " << str << " into a memory size "; } }; template uint64_t ParseNum(const std::string &arg) { std::stringstream stream(arg); Num value; stream >> value; UTIL_THROW_IF_ARG(!stream, SizeParseError, (arg), "for the leading number."); std::string after; stream >> after; UTIL_THROW_IF_ARG(after.size() > 1, SizeParseError, (arg), "because there are more than two characters after the number."); std::string throwaway; UTIL_THROW_IF_ARG(stream >> throwaway, SizeParseError, (arg), "because there was more cruft " << throwaway << " after the number."); // Silly sort, using kilobytes as your default unit. if (after.empty()) after = "K"; if (after == "%") { uint64_t mem = GuessPhysicalMemory(); UTIL_THROW_IF_ARG(!mem, SizeParseError, (arg), "because % was specified but the physical memory size could not be determined."); return static_cast(value) * static_cast(mem) / 100.0; } std::string units("bKMGTPEZY"); std::string::size_type index = units.find(after[0]); UTIL_THROW_IF_ARG(index == std::string::npos, SizeParseError, (arg), "the allowed suffixes are " << units << "%."); for (std::string::size_type i = 0; i < index; ++i) { value *= 1024; } return value; } } // namespace uint64_t ParseSize(const std::string &arg) { return arg.find('.') == std::string::npos ? ParseNum(arg) : ParseNum(arg); } } // namespace util