• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 Google Inc. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "sysinfo.h"
16 #include "internal_macros.h"
17 
18 #ifdef BENCHMARK_OS_WINDOWS
19 #include <Shlwapi.h>
20 #include <VersionHelpers.h>
21 #include <Windows.h>
22 #else
23 #include <fcntl.h>
24 #include <sys/resource.h>
25 #include <sys/time.h>
26 #include <sys/types.h>  // this header must be included before 'sys/sysctl.h' to avoid compilation error on FreeBSD
27 #include <unistd.h>
28 #if defined BENCHMARK_OS_FREEBSD || defined BENCHMARK_OS_MACOSX
29 #include <sys/sysctl.h>
30 #endif
31 #endif
32 
33 #include <cerrno>
34 #include <cstdint>
35 #include <cstdio>
36 #include <cstdlib>
37 #include <cstring>
38 #include <iostream>
39 #include <limits>
40 #include <mutex>
41 
42 #include "arraysize.h"
43 #include "check.h"
44 #include "cycleclock.h"
45 #include "internal_macros.h"
46 #include "log.h"
47 #include "sleep.h"
48 #include "string_util.h"
49 
50 namespace benchmark {
51 namespace {
52 std::once_flag cpuinfo_init;
53 double cpuinfo_cycles_per_second = 1.0;
54 int cpuinfo_num_cpus = 1;  // Conservative guess
55 
56 #if !defined BENCHMARK_OS_MACOSX
57 const int64_t estimate_time_ms = 1000;
58 
59 // Helper function estimates cycles/sec by observing cycles elapsed during
60 // sleep(). Using small sleep time decreases accuracy significantly.
EstimateCyclesPerSecond()61 int64_t EstimateCyclesPerSecond() {
62   const int64_t start_ticks = cycleclock::Now();
63   SleepForMilliseconds(estimate_time_ms);
64   return cycleclock::Now() - start_ticks;
65 }
66 #endif
67 
68 #if defined BENCHMARK_OS_LINUX || defined BENCHMARK_OS_CYGWIN
69 // Helper function for reading an int from a file. Returns true if successful
70 // and the memory location pointed to by value is set to the value read.
ReadIntFromFile(const char * file,long * value)71 bool ReadIntFromFile(const char* file, long* value) {
72   bool ret = false;
73   int fd = open(file, O_RDONLY);
74   if (fd != -1) {
75     char line[1024];
76     char* err;
77     memset(line, '\0', sizeof(line));
78     CHECK(read(fd, line, sizeof(line) - 1));
79     const long temp_value = strtol(line, &err, 10);
80     if (line[0] != '\0' && (*err == '\n' || *err == '\0')) {
81       *value = temp_value;
82       ret = true;
83     }
84     close(fd);
85   }
86   return ret;
87 }
88 #endif
89 
90 #if defined BENCHMARK_OS_LINUX || defined BENCHMARK_OS_CYGWIN
convertToLowerCase(std::string s)91 static std::string convertToLowerCase(std::string s) {
92   for (auto& ch : s)
93     ch = std::tolower(ch);
94   return s;
95 }
startsWithKey(std::string Value,std::string Key,bool IgnoreCase=true)96 static bool startsWithKey(std::string Value, std::string Key,
97                           bool IgnoreCase = true) {
98   if (IgnoreCase) {
99     Key = convertToLowerCase(std::move(Key));
100     Value = convertToLowerCase(std::move(Value));
101   }
102   return Value.compare(0, Key.size(), Key) == 0;
103 }
104 #endif
105 
InitializeSystemInfo()106 void InitializeSystemInfo() {
107 #if defined BENCHMARK_OS_LINUX || defined BENCHMARK_OS_CYGWIN
108   char line[1024];
109   char* err;
110   long freq;
111 
112   bool saw_mhz = false;
113 
114   // If the kernel is exporting the tsc frequency use that. There are issues
115   // where cpuinfo_max_freq cannot be relied on because the BIOS may be
116   // exporintg an invalid p-state (on x86) or p-states may be used to put the
117   // processor in a new mode (turbo mode). Essentially, those frequencies
118   // cannot always be relied upon. The same reasons apply to /proc/cpuinfo as
119   // well.
120   if (!saw_mhz &&
121       ReadIntFromFile("/sys/devices/system/cpu/cpu0/tsc_freq_khz", &freq)) {
122     // The value is in kHz (as the file name suggests).  For example, on a
123     // 2GHz warpstation, the file contains the value "2000000".
124     cpuinfo_cycles_per_second = freq * 1000.0;
125     saw_mhz = true;
126   }
127 
128   // If CPU scaling is in effect, we want to use the *maximum* frequency,
129   // not whatever CPU speed some random processor happens to be using now.
130   if (!saw_mhz &&
131       ReadIntFromFile("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq",
132                       &freq)) {
133     // The value is in kHz.  For example, on a 2GHz warpstation, the file
134     // contains the value "2000000".
135     cpuinfo_cycles_per_second = freq * 1000.0;
136     saw_mhz = true;
137   }
138 
139   // Read /proc/cpuinfo for other values, and if there is no cpuinfo_max_freq.
140   const char* pname = "/proc/cpuinfo";
141   int fd = open(pname, O_RDONLY);
142   if (fd == -1) {
143     perror(pname);
144     if (!saw_mhz) {
145       cpuinfo_cycles_per_second =
146           static_cast<double>(EstimateCyclesPerSecond());
147     }
148     return;
149   }
150 
151   double bogo_clock = 1.0;
152   bool saw_bogo = false;
153   long max_cpu_id = 0;
154   int num_cpus = 0;
155   line[0] = line[1] = '\0';
156   size_t chars_read = 0;
157   do {  // we'll exit when the last read didn't read anything
158     // Move the next line to the beginning of the buffer
159     const size_t oldlinelen = strlen(line);
160     if (sizeof(line) == oldlinelen + 1)  // oldlinelen took up entire line
161       line[0] = '\0';
162     else  // still other lines left to save
163       memmove(line, line + oldlinelen + 1, sizeof(line) - (oldlinelen + 1));
164     // Terminate the new line, reading more if we can't find the newline
165     char* newline = strchr(line, '\n');
166     if (newline == nullptr) {
167       const size_t linelen = strlen(line);
168       const size_t bytes_to_read = sizeof(line) - 1 - linelen;
169       CHECK(bytes_to_read > 0);  // because the memmove recovered >=1 bytes
170       chars_read = read(fd, line + linelen, bytes_to_read);
171       line[linelen + chars_read] = '\0';
172       newline = strchr(line, '\n');
173     }
174     if (newline != nullptr) *newline = '\0';
175 
176     // When parsing the "cpu MHz" and "bogomips" (fallback) entries, we only
177     // accept postive values. Some environments (virtual machines) report zero,
178     // which would cause infinite looping in WallTime_Init.
179     if (!saw_mhz && startsWithKey(line, "cpu MHz")) {
180       const char* freqstr = strchr(line, ':');
181       if (freqstr) {
182         cpuinfo_cycles_per_second = strtod(freqstr + 1, &err) * 1000000.0;
183         if (freqstr[1] != '\0' && *err == '\0' && cpuinfo_cycles_per_second > 0)
184           saw_mhz = true;
185       }
186     } else if (startsWithKey(line, "bogomips")) {
187       const char* freqstr = strchr(line, ':');
188       if (freqstr) {
189         bogo_clock = strtod(freqstr + 1, &err) * 1000000.0;
190         if (freqstr[1] != '\0' && *err == '\0' && bogo_clock > 0)
191           saw_bogo = true;
192       }
193     } else if (startsWithKey(line, "processor", /*IgnoreCase*/false)) {
194       // The above comparison is case-sensitive because ARM kernels often
195       // include a "Processor" line that tells you about the CPU, distinct
196       // from the usual "processor" lines that give you CPU ids. No current
197       // Linux architecture is using "Processor" for CPU ids.
198       num_cpus++;  // count up every time we see an "processor :" entry
199       const char* id_str = strchr(line, ':');
200       if (id_str) {
201         const long cpu_id = strtol(id_str + 1, &err, 10);
202         if (id_str[1] != '\0' && *err == '\0' && max_cpu_id < cpu_id)
203           max_cpu_id = cpu_id;
204       }
205     }
206   } while (chars_read > 0);
207   close(fd);
208 
209   if (!saw_mhz) {
210     if (saw_bogo) {
211       // If we didn't find anything better, we'll use bogomips, but
212       // we're not happy about it.
213       cpuinfo_cycles_per_second = bogo_clock;
214     } else {
215       // If we don't even have bogomips, we'll use the slow estimation.
216       cpuinfo_cycles_per_second =
217           static_cast<double>(EstimateCyclesPerSecond());
218     }
219   }
220   if (num_cpus == 0) {
221     fprintf(stderr, "Failed to read num. CPUs correctly from /proc/cpuinfo\n");
222   } else {
223     if ((max_cpu_id + 1) != num_cpus) {
224       fprintf(stderr,
225               "CPU ID assignments in /proc/cpuinfo seem messed up."
226               " This is usually caused by a bad BIOS.\n");
227     }
228     cpuinfo_num_cpus = num_cpus;
229   }
230 
231 #elif defined BENCHMARK_OS_FREEBSD
232 // For this sysctl to work, the machine must be configured without
233 // SMP, APIC, or APM support.  hz should be 64-bit in freebsd 7.0
234 // and later.  Before that, it's a 32-bit quantity (and gives the
235 // wrong answer on machines faster than 2^32 Hz).  See
236 //  http://lists.freebsd.org/pipermail/freebsd-i386/2004-November/001846.html
237 // But also compare FreeBSD 7.0:
238 //  http://fxr.watson.org/fxr/source/i386/i386/tsc.c?v=RELENG70#L223
239 //  231         error = sysctl_handle_quad(oidp, &freq, 0, req);
240 // To FreeBSD 6.3 (it's the same in 6-STABLE):
241 //  http://fxr.watson.org/fxr/source/i386/i386/tsc.c?v=RELENG6#L131
242 //  139         error = sysctl_handle_int(oidp, &freq, sizeof(freq), req);
243 #if __FreeBSD__ >= 7
244   uint64_t hz = 0;
245 #else
246   unsigned int hz = 0;
247 #endif
248   size_t sz = sizeof(hz);
249   const char* sysctl_path = "machdep.tsc_freq";
250   if (sysctlbyname(sysctl_path, &hz, &sz, nullptr, 0) != 0) {
251     fprintf(stderr, "Unable to determine clock rate from sysctl: %s: %s\n",
252             sysctl_path, strerror(errno));
253     cpuinfo_cycles_per_second = static_cast<double>(EstimateCyclesPerSecond());
254   } else {
255     cpuinfo_cycles_per_second = hz;
256   }
257 // TODO: also figure out cpuinfo_num_cpus
258 
259 #elif defined BENCHMARK_OS_WINDOWS
260   // In NT, read MHz from the registry. If we fail to do so or we're in win9x
261   // then make a crude estimate.
262   DWORD data, data_size = sizeof(data);
263   if (IsWindowsXPOrGreater() &&
264       SUCCEEDED(
265           SHGetValueA(HKEY_LOCAL_MACHINE,
266                       "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
267                       "~MHz", nullptr, &data, &data_size)))
268     cpuinfo_cycles_per_second =
269         static_cast<double>((int64_t)data * (int64_t)(1000 * 1000));  // was mhz
270   else
271     cpuinfo_cycles_per_second = static_cast<double>(EstimateCyclesPerSecond());
272 
273   SYSTEM_INFO sysinfo;
274   // Use memset as opposed to = {} to avoid GCC missing initializer false
275   // positives.
276   std::memset(&sysinfo, 0, sizeof(SYSTEM_INFO));
277   GetSystemInfo(&sysinfo);
278   cpuinfo_num_cpus = sysinfo.dwNumberOfProcessors;  // number of logical
279                                                     // processors in the current
280                                                     // group
281 
282 #elif defined BENCHMARK_OS_MACOSX
283   int32_t num_cpus = 0;
284   size_t size = sizeof(num_cpus);
285   if (::sysctlbyname("hw.ncpu", &num_cpus, &size, nullptr, 0) == 0 &&
286       (size == sizeof(num_cpus))) {
287     cpuinfo_num_cpus = num_cpus;
288   } else {
289     fprintf(stderr, "%s\n", strerror(errno));
290     std::exit(EXIT_FAILURE);
291   }
292   int64_t cpu_freq = 0;
293   size = sizeof(cpu_freq);
294   if (::sysctlbyname("hw.cpufrequency", &cpu_freq, &size, nullptr, 0) == 0 &&
295       (size == sizeof(cpu_freq))) {
296     cpuinfo_cycles_per_second = cpu_freq;
297   } else {
298     fprintf(stderr, "%s\n", strerror(errno));
299     std::exit(EXIT_FAILURE);
300   }
301 #else
302   // Generic cycles per second counter
303   cpuinfo_cycles_per_second = static_cast<double>(EstimateCyclesPerSecond());
304 #endif
305 }
306 
307 }  // end namespace
308 
CyclesPerSecond(void)309 double CyclesPerSecond(void) {
310   std::call_once(cpuinfo_init, InitializeSystemInfo);
311   return cpuinfo_cycles_per_second;
312 }
313 
NumCPUs(void)314 int NumCPUs(void) {
315   std::call_once(cpuinfo_init, InitializeSystemInfo);
316   return cpuinfo_num_cpus;
317 }
318 
319 // The ""'s catch people who don't pass in a literal for "str"
320 #define strliterallen(str) (sizeof("" str "") - 1)
321 
322 // Must use a string literal for prefix.
323 #define memprefix(str, len, prefix)                       \
324   ((((len) >= strliterallen(prefix)) &&                   \
325     std::memcmp(str, prefix, strliterallen(prefix)) == 0) \
326        ? str + strliterallen(prefix)                      \
327        : nullptr)
328 
CpuScalingEnabled()329 bool CpuScalingEnabled() {
330 #ifndef BENCHMARK_OS_WINDOWS
331   // On Linux, the CPUfreq subsystem exposes CPU information as files on the
332   // local file system. If reading the exported files fails, then we may not be
333   // running on Linux, so we silently ignore all the read errors.
334   for (int cpu = 0, num_cpus = NumCPUs(); cpu < num_cpus; ++cpu) {
335     std::string governor_file =
336         StrCat("/sys/devices/system/cpu/cpu", cpu, "/cpufreq/scaling_governor");
337     FILE* file = fopen(governor_file.c_str(), "r");
338     if (!file) break;
339     char buff[16];
340     size_t bytes_read = fread(buff, 1, sizeof(buff), file);
341     fclose(file);
342     if (memprefix(buff, bytes_read, "performance") == nullptr) return true;
343   }
344 #endif
345   return false;
346 }
347 
348 }  // end namespace benchmark
349