1 #include "util/usage.hh"
2 
3 #include "util/exception.hh"
4 
5 #include <fstream>
6 #include <ostream>
7 #include <sstream>
8 #include <set>
9 #include <string>
10 #include <cstring>
11 #include <cctype>
12 #include <ctime>
13 #if defined(_WIN32) || defined(_WIN64)
14 // This code lifted from physmem.c in gnulib.  See the copyright statement
15 // below.
16 # define WIN32_LEAN_AND_MEAN
17 # include <windows.h>
18 /*  MEMORYSTATUSEX is missing from older windows headers, so define
19     a local replacement.  */
20 typedef struct
21 {
22   DWORD dwLength;
23   DWORD dwMemoryLoad;
24   DWORDLONG ullTotalPhys;
25   DWORDLONG ullAvailPhys;
26   DWORDLONG ullTotalPageFile;
27   DWORDLONG ullAvailPageFile;
28   DWORDLONG ullTotalVirtual;
29   DWORDLONG ullAvailVirtual;
30   DWORDLONG ullAvailExtendedVirtual;
31 } lMEMORYSTATUSEX;
32 // Is this really supposed to be defined like this?
33 typedef int WINBOOL;
34 typedef WINBOOL (WINAPI *PFN_MS_EX) (lMEMORYSTATUSEX*);
35 #else
36 #include <sys/resource.h>
37 #include <sys/time.h>
38 #include <unistd.h>
39 #endif
40 
41 #if defined(__MACH__) || defined(__APPLE__)
42 #include <sys/types.h>
43 #include <sys/sysctl.h>
44 #include <mach/task.h>
45 #include <mach/mach.h>
46 #endif
47 
48 namespace util {
49 namespace {
50 
51 #if defined(__MACH__)
52 typedef struct timeval Wall;
GetWall()53 Wall GetWall() {
54   struct timeval tv;
55   gettimeofday(&tv, NULL);
56   return tv;
57 }
58 #elif defined(_WIN32) || defined(_WIN64)
59 typedef time_t Wall;
60 Wall GetWall() {
61   return time(NULL);
62 }
63 #else
64 typedef struct timespec Wall;
65 Wall GetWall() {
66   Wall ret;
67   UTIL_THROW_IF(-1 == clock_gettime(CLOCK_MONOTONIC, &ret), ErrnoException, "Could not get wall time");
68   return ret;
69 }
70 #endif
71 
72 // gcc possible-unused function flags
73 #ifdef __GNUC__
74 double Subtract(time_t first, time_t second) __attribute__ ((unused));
75 double DoubleSec(time_t tv) __attribute__ ((unused));
76 #if !defined(_WIN32) && !defined(_WIN64)
77 double Subtract(const struct timeval &first, const struct timeval &second) __attribute__ ((unused));
78 double Subtract(const struct timespec &first, const struct timespec &second) __attribute__ ((unused));
79 double DoubleSec(const struct timeval &tv) __attribute__ ((unused));
80 double DoubleSec(const struct timespec &tv) __attribute__ ((unused));
81 #endif
82 #endif
83 
84 // Some of these functions are only used on some platforms.
85 #ifdef __clang__
86 #pragma clang diagnostic push
87 #pragma clang diagnostic ignored "-Wunused-function"
88 #endif
89 // These all assume first > second
Subtract(time_t first,time_t second)90 double Subtract(time_t first, time_t second) {
91   return difftime(first, second);
92 }
DoubleSec(time_t tv)93 double DoubleSec(time_t tv) {
94   return static_cast<double>(tv);
95 }
96 #if !defined(_WIN32) && !defined(_WIN64)
Subtract(const struct timeval & first,const struct timeval & second)97 double Subtract(const struct timeval &first, const struct timeval &second) {
98   return static_cast<double>(first.tv_sec - second.tv_sec) + static_cast<double>(first.tv_usec - second.tv_usec) / 1000000.0;
99 }
Subtract(const struct timespec & first,const struct timespec & second)100 double Subtract(const struct timespec &first, const struct timespec &second) {
101   return static_cast<double>(first.tv_sec - second.tv_sec) + static_cast<double>(first.tv_nsec - second.tv_nsec) / 1000000000.0;
102 }
DoubleSec(const struct timeval & tv)103 double DoubleSec(const struct timeval &tv) {
104   return static_cast<double>(tv.tv_sec) + (static_cast<double>(tv.tv_usec) / 1000000.0);
105 }
DoubleSec(const struct timespec & tv)106 double DoubleSec(const struct timespec &tv) {
107   return static_cast<double>(tv.tv_sec) + (static_cast<double>(tv.tv_nsec) / 1000000000.0);
108 }
109 #endif
110 #ifdef __clang__
111 #pragma clang diagnostic pop
112 #endif
113 
114 class RecordStart {
115   public:
RecordStart()116     RecordStart() {
117       started_ = GetWall();
118     }
119 
Started() const120     const Wall &Started() const {
121       return started_;
122     }
123 
124   private:
125     Wall started_;
126 };
127 
128 const RecordStart kRecordStart;
129 
SkipSpaces(const char * at)130 const char *SkipSpaces(const char *at) {
131   for (; *at == ' ' || *at == '\t'; ++at) {}
132   return at;
133 }
134 } // namespace
135 
WallTime()136 double WallTime() {
137   return Subtract(GetWall(), kRecordStart.Started());
138 }
139 
CPUTime()140 double CPUTime() {
141 #if defined(_WIN32) || defined(_WIN64)
142   return 0.0;
143 #elif defined(__MACH__) || defined(__FreeBSD__) || defined(__APPLE__)
144   struct rusage usage;
145   UTIL_THROW_IF(getrusage(RUSAGE_SELF, &usage), ErrnoException, "getrusage failed");
146   return DoubleSec(usage.ru_utime) + DoubleSec(usage.ru_stime);
147 #else
148   struct timespec usage;
149   UTIL_THROW_IF(clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &usage), ErrnoException, "clock_gettime failed?!");
150   return DoubleSec(usage);
151 #endif
152 }
153 
ThreadTime()154 double ThreadTime() {
155 #if defined(_WIN32) || defined(_WIN64)
156   // Output parameters for querying thread CPU usage:
157   FILETIME sys_time, user_time;
158   // Unused, but apparently need to be passed:
159   FILETIME c_time, e_time;
160 
161   HANDLE this_thread = GetCurrentThread();
162   UTIL_THROW_IF(!GetThreadTimes(this_thread, &c_time, &e_time, &sys_time, &user_time), WindowsException, "GetThreadTime");
163   // Convert LPFILETIME to 64-bit number, and from there to double.
164   ULARGE_INTEGER sys_ticks, user_ticks;
165   sys_ticks.LowPart = sys_time.dwLowDateTime;
166   sys_ticks.HighPart = sys_time.dwHighDateTime;
167   user_ticks.LowPart = user_time.dwLowDateTime;
168   user_ticks.HighPart = user_time.dwHighDateTime;
169   const double ticks = double(sys_ticks.QuadPart + user_ticks.QuadPart);
170   // GetThreadTimes() reports in units of 100 nanoseconds, i.e. ten-millionths
171   // of a second.
172   return ticks / (10 * 1000 * 1000);
173 #elif defined(__MACH__) || defined(__APPLE__)
174   struct task_basic_info t_info;
175   mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
176   task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count);
177 
178   return 0.0;
179 #else
180   struct timespec usage;
181   UTIL_THROW_IF(clock_gettime(CLOCK_THREAD_CPUTIME_ID, &usage), ErrnoException, "clock_gettime failed?!");
182   return DoubleSec(usage);
183 #endif
184 }
185 
RSSMax()186 uint64_t RSSMax() {
187 #if defined(_WIN32) || defined(_WIN64)
188   return 0;
189 #else
190   struct rusage usage;
191   if (getrusage(RUSAGE_SELF, &usage))
192     return 0;
193   return static_cast<uint64_t>(usage.ru_maxrss) * 1024;
194 #endif
195 }
196 
PrintUsage(std::ostream & out)197 void PrintUsage(std::ostream &out) {
198 #if !defined(_WIN32) && !defined(_WIN64)
199   // Linux doesn't set memory usage in getrusage :-(
200   std::set<std::string> headers;
201   headers.insert("VmPeak:");
202   headers.insert("VmRSS:");
203   headers.insert("Name:");
204 
205   std::ifstream status("/proc/self/status", std::ios::in);
206   std::string header, value;
207   while ((status >> header) && getline(status, value)) {
208     if (headers.find(header) != headers.end()) {
209       out << header << SkipSpaces(value.c_str()) << '\t';
210     }
211   }
212 
213   struct rusage usage;
214   if (getrusage(RUSAGE_SELF, &usage)) {
215     perror("getrusage");
216     return;
217   }
218   out << "RSSMax:" << usage.ru_maxrss << " kB" << '\t';
219   out << "user:" << DoubleSec(usage.ru_utime) << "\tsys:" << DoubleSec(usage.ru_stime) << '\t';
220   out << "CPU:" << CPUTime() << '\t';
221 #endif
222 
223   out << "real:" << WallTime() << '\n';
224 }
225 
226 /* Adapted from physmem.c in gnulib 831b84c59ef413c57a36b67344467d66a8a2ba70 */
227 /* Calculate the size of physical memory.
228 
229    Copyright (C) 2000-2001, 2003, 2005-2006, 2009-2013 Free Software
230    Foundation, Inc.
231 
232    This program is free software: you can redistribute it and/or modify
233    it under the terms of the GNU Lesser General Public License as published by
234    the Free Software Foundation; either version 2.1 of the License, or
235    (at your option) any later version.
236 
237    This program is distributed in the hope that it will be useful,
238    but WITHOUT ANY WARRANTY; without even the implied warranty of
239    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
240    GNU Lesser General Public License for more details.
241 
242    You should have received a copy of the GNU Lesser General Public License
243    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
244 
245 /* Written by Paul Eggert.  */
GuessPhysicalMemory()246 uint64_t GuessPhysicalMemory() {
247 #if defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE)
248   {
249     long pages = sysconf(_SC_PHYS_PAGES);
250     long page_size = sysconf(_SC_PAGESIZE);
251     if (pages != -1 && page_size != -1)
252       return static_cast<uint64_t>(pages) * static_cast<uint64_t>(page_size);
253   }
254 #endif
255 #ifdef HW_PHYSMEM
256   { /* This works on *bsd and darwin.  */
257     unsigned int physmem;
258     size_t len = sizeof physmem;
259     static int mib[2] = { CTL_HW, HW_PHYSMEM };
260 
261     if (sysctl (mib, sizeof(mib) / sizeof(mib[0]), &physmem, &len, NULL, 0) == 0
262         && len == sizeof (physmem))
263       return static_cast<uint64_t>(physmem);
264   }
265 #endif
266 
267 #if defined(_WIN32) || defined(_WIN64)
268   { /* this works on windows */
269     PFN_MS_EX pfnex;
270     HMODULE h = GetModuleHandle (TEXT("kernel32.dll"));
271 
272     if (!h)
273       return 0;
274 
275     /*  Use GlobalMemoryStatusEx if available.  */
276     if ((pfnex = (PFN_MS_EX) GetProcAddress (h, "GlobalMemoryStatusEx")))
277       {
278         lMEMORYSTATUSEX lms_ex;
279         lms_ex.dwLength = sizeof lms_ex;
280         if (!pfnex (&lms_ex))
281           return 0;
282         return lms_ex.ullTotalPhys;
283       }
284 
285     /*  Fall back to GlobalMemoryStatus which is always available.
286         but returns wrong results for physical memory > 4GB.  */
287     else
288       {
289         MEMORYSTATUS ms;
290         GlobalMemoryStatus (&ms);
291         return ms.dwTotalPhys;
292       }
293   }
294 #endif
295   return 0;
296 }
297 
298 namespace {
299 class SizeParseError : public Exception {
300   public:
SizeParseError(const std::string & str)301     explicit SizeParseError(const std::string &str) throw() {
302       *this << "Failed to parse " << str << " into a memory size ";
303     }
304 };
305 
ParseNum(const std::string & arg)306 template <class Num> uint64_t ParseNum(const std::string &arg) {
307   std::stringstream stream(arg);
308   Num value;
309   stream >> value;
310   UTIL_THROW_IF_ARG(!stream, SizeParseError, (arg), "for the leading number.");
311   std::string after;
312   stream >> after;
313   UTIL_THROW_IF_ARG(after.size() > 1, SizeParseError, (arg), "because there are more than two characters after the number.");
314   std::string throwaway;
315   UTIL_THROW_IF_ARG(stream >> throwaway, SizeParseError, (arg), "because there was more cruft " << throwaway << " after the number.");
316 
317   // Silly sort, using kilobytes as your default unit.
318   if (after.empty()) after = "K";
319   if (after == "%") {
320     uint64_t mem = GuessPhysicalMemory();
321     UTIL_THROW_IF_ARG(!mem, SizeParseError, (arg), "because % was specified but the physical memory size could not be determined.");
322     return static_cast<uint64_t>(static_cast<double>(value) * static_cast<double>(mem) / 100.0);
323   }
324 
325   if (after == "k") after = "K";
326   std::string units("bKMGTPEZY");
327   std::string::size_type index = units.find(after[0]);
328   UTIL_THROW_IF_ARG(index == std::string::npos, SizeParseError, (arg), "the allowed suffixes are " << units << "%.");
329   for (std::string::size_type i = 0; i < index; ++i) {
330     value *= 1024;
331   }
332   return static_cast<uint64_t>(value);
333 }
334 
335 } // namespace
336 
ParseSize(const std::string & arg)337 uint64_t ParseSize(const std::string &arg) {
338   return arg.find('.') == std::string::npos ? ParseNum<double>(arg) : ParseNum<uint64_t>(arg);
339 }
340 
341 } // namespace util
342