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