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. 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. 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 91 static std::string convertToLowerCase(std::string s) { 92 for (auto& ch : s) 93 ch = std::tolower(ch); 94 return s; 95 } 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 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 309 double CyclesPerSecond(void) { 310 std::call_once(cpuinfo_init, InitializeSystemInfo); 311 return cpuinfo_cycles_per_second; 312 } 313 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 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