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 "internal_macros.h" 16 17 #ifdef BENCHMARK_OS_WINDOWS 18 #include <shlwapi.h> 19 #undef StrCat // Don't let StrCat in string_util.h be renamed to lstrcatA 20 #include <versionhelpers.h> 21 #include <windows.h> 22 23 #include <codecvt> 24 #else 25 #include <fcntl.h> 26 #ifndef BENCHMARK_OS_FUCHSIA 27 #include <sys/resource.h> 28 #endif 29 #include <sys/time.h> 30 #include <sys/types.h> // this header must be included before 'sys/sysctl.h' to avoid compilation error on FreeBSD 31 #include <unistd.h> 32 #if defined BENCHMARK_OS_FREEBSD || defined BENCHMARK_OS_MACOSX || \ 33 defined BENCHMARK_OS_NETBSD || defined BENCHMARK_OS_OPENBSD || \ 34 defined BENCHMARK_OS_DRAGONFLY 35 #define BENCHMARK_HAS_SYSCTL 36 #include <sys/sysctl.h> 37 #endif 38 #endif 39 #if defined(BENCHMARK_OS_SOLARIS) 40 #include <kstat.h> 41 #endif 42 #if defined(BENCHMARK_OS_QNX) 43 #include <sys/syspage.h> 44 #endif 45 46 #include <algorithm> 47 #include <array> 48 #include <bitset> 49 #include <cerrno> 50 #include <climits> 51 #include <cstdint> 52 #include <cstdio> 53 #include <cstdlib> 54 #include <cstring> 55 #include <fstream> 56 #include <iostream> 57 #include <iterator> 58 #include <limits> 59 #include <locale> 60 #include <memory> 61 #include <sstream> 62 #include <utility> 63 64 #include "check.h" 65 #include "cycleclock.h" 66 #include "internal_macros.h" 67 #include "log.h" 68 #include "sleep.h" 69 #include "string_util.h" 70 71 namespace benchmark { 72 namespace { 73 74 void PrintImp(std::ostream& out) { out << std::endl; } 75 76 template <class First, class... Rest> 77 void PrintImp(std::ostream& out, First&& f, Rest&&... rest) { 78 out << std::forward<First>(f); 79 PrintImp(out, std::forward<Rest>(rest)...); 80 } 81 82 template <class... Args> 83 BENCHMARK_NORETURN void PrintErrorAndDie(Args&&... args) { 84 PrintImp(std::cerr, std::forward<Args>(args)...); 85 std::exit(EXIT_FAILURE); 86 } 87 88 #ifdef BENCHMARK_HAS_SYSCTL 89 90 /// ValueUnion - A type used to correctly alias the byte-for-byte output of 91 /// `sysctl` with the result type it's to be interpreted as. 92 struct ValueUnion { 93 union DataT { 94 uint32_t uint32_value; 95 uint64_t uint64_value; 96 // For correct aliasing of union members from bytes. 97 char bytes[8]; 98 }; 99 using DataPtr = std::unique_ptr<DataT, decltype(&std::free)>; 100 101 // The size of the data union member + its trailing array size. 102 size_t Size; 103 DataPtr Buff; 104 105 public: 106 ValueUnion() : Size(0), Buff(nullptr, &std::free) {} 107 108 explicit ValueUnion(size_t BuffSize) 109 : Size(sizeof(DataT) + BuffSize), 110 Buff(::new (std::malloc(Size)) DataT(), &std::free) {} 111 112 ValueUnion(ValueUnion&& other) = default; 113 114 explicit operator bool() const { return bool(Buff); } 115 116 char* data() const { return Buff->bytes; } 117 118 std::string GetAsString() const { return std::string(data()); } 119 120 int64_t GetAsInteger() const { 121 if (Size == sizeof(Buff->uint32_value)) 122 return static_cast<int32_t>(Buff->uint32_value); 123 else if (Size == sizeof(Buff->uint64_value)) 124 return static_cast<int64_t>(Buff->uint64_value); 125 BENCHMARK_UNREACHABLE(); 126 } 127 128 uint64_t GetAsUnsigned() const { 129 if (Size == sizeof(Buff->uint32_value)) 130 return Buff->uint32_value; 131 else if (Size == sizeof(Buff->uint64_value)) 132 return Buff->uint64_value; 133 BENCHMARK_UNREACHABLE(); 134 } 135 136 template <class T, int N> 137 std::array<T, N> GetAsArray() { 138 const int ArrSize = sizeof(T) * N; 139 BM_CHECK_LE(ArrSize, Size); 140 std::array<T, N> Arr; 141 std::memcpy(Arr.data(), data(), ArrSize); 142 return Arr; 143 } 144 }; 145 146 ValueUnion GetSysctlImp(std::string const& Name) { 147 #if defined BENCHMARK_OS_OPENBSD 148 int mib[2]; 149 150 mib[0] = CTL_HW; 151 if ((Name == "hw.ncpu") || (Name == "hw.cpuspeed")) { 152 ValueUnion buff(sizeof(int)); 153 154 if (Name == "hw.ncpu") { 155 mib[1] = HW_NCPU; 156 } else { 157 mib[1] = HW_CPUSPEED; 158 } 159 160 if (sysctl(mib, 2, buff.data(), &buff.Size, nullptr, 0) == -1) { 161 return ValueUnion(); 162 } 163 return buff; 164 } 165 return ValueUnion(); 166 #else 167 size_t CurBuffSize = 0; 168 if (sysctlbyname(Name.c_str(), nullptr, &CurBuffSize, nullptr, 0) == -1) 169 return ValueUnion(); 170 171 ValueUnion buff(CurBuffSize); 172 if (sysctlbyname(Name.c_str(), buff.data(), &buff.Size, nullptr, 0) == 0) 173 return buff; 174 return ValueUnion(); 175 #endif 176 } 177 178 BENCHMARK_MAYBE_UNUSED 179 bool GetSysctl(std::string const& Name, std::string* Out) { 180 Out->clear(); 181 auto Buff = GetSysctlImp(Name); 182 if (!Buff) return false; 183 Out->assign(Buff.data()); 184 return true; 185 } 186 187 template <class Tp, 188 class = typename std::enable_if<std::is_integral<Tp>::value>::type> 189 bool GetSysctl(std::string const& Name, Tp* Out) { 190 *Out = 0; 191 auto Buff = GetSysctlImp(Name); 192 if (!Buff) return false; 193 *Out = static_cast<Tp>(Buff.GetAsUnsigned()); 194 return true; 195 } 196 197 template <class Tp, size_t N> 198 bool GetSysctl(std::string const& Name, std::array<Tp, N>* Out) { 199 auto Buff = GetSysctlImp(Name); 200 if (!Buff) return false; 201 *Out = Buff.GetAsArray<Tp, N>(); 202 return true; 203 } 204 #endif 205 206 template <class ArgT> 207 bool ReadFromFile(std::string const& fname, ArgT* arg) { 208 *arg = ArgT(); 209 std::ifstream f(fname.c_str()); 210 if (!f.is_open()) return false; 211 f >> *arg; 212 return f.good(); 213 } 214 215 CPUInfo::Scaling CpuScaling(int num_cpus) { 216 // We don't have a valid CPU count, so don't even bother. 217 if (num_cpus <= 0) return CPUInfo::Scaling::UNKNOWN; 218 #if defined(BENCHMARK_OS_QNX) 219 return CPUInfo::Scaling::UNKNOWN; 220 #elif !defined(BENCHMARK_OS_WINDOWS) 221 // On Linux, the CPUfreq subsystem exposes CPU information as files on the 222 // local file system. If reading the exported files fails, then we may not be 223 // running on Linux, so we silently ignore all the read errors. 224 std::string res; 225 for (int cpu = 0; cpu < num_cpus; ++cpu) { 226 std::string governor_file = 227 StrCat("/sys/devices/system/cpu/cpu", cpu, "/cpufreq/scaling_governor"); 228 if (ReadFromFile(governor_file, &res) && res != "performance") 229 return CPUInfo::Scaling::ENABLED; 230 } 231 return CPUInfo::Scaling::DISABLED; 232 #else 233 return CPUInfo::Scaling::UNKNOWN; 234 #endif 235 } 236 237 int CountSetBitsInCPUMap(std::string Val) { 238 auto CountBits = [](std::string Part) { 239 using CPUMask = std::bitset<sizeof(std::uintptr_t) * CHAR_BIT>; 240 Part = "0x" + Part; 241 CPUMask Mask(benchmark::stoul(Part, nullptr, 16)); 242 return static_cast<int>(Mask.count()); 243 }; 244 size_t Pos; 245 int total = 0; 246 while ((Pos = Val.find(',')) != std::string::npos) { 247 total += CountBits(Val.substr(0, Pos)); 248 Val = Val.substr(Pos + 1); 249 } 250 if (!Val.empty()) { 251 total += CountBits(Val); 252 } 253 return total; 254 } 255 256 BENCHMARK_MAYBE_UNUSED 257 std::vector<CPUInfo::CacheInfo> GetCacheSizesFromKVFS() { 258 std::vector<CPUInfo::CacheInfo> res; 259 std::string dir = "/sys/devices/system/cpu/cpu0/cache/"; 260 int Idx = 0; 261 while (true) { 262 CPUInfo::CacheInfo info; 263 std::string FPath = StrCat(dir, "index", Idx++, "/"); 264 std::ifstream f(StrCat(FPath, "size").c_str()); 265 if (!f.is_open()) break; 266 std::string suffix; 267 f >> info.size; 268 if (f.fail()) 269 PrintErrorAndDie("Failed while reading file '", FPath, "size'"); 270 if (f.good()) { 271 f >> suffix; 272 if (f.bad()) 273 PrintErrorAndDie( 274 "Invalid cache size format: failed to read size suffix"); 275 else if (f && suffix != "K") 276 PrintErrorAndDie("Invalid cache size format: Expected bytes ", suffix); 277 else if (suffix == "K") 278 info.size *= 1024; 279 } 280 if (!ReadFromFile(StrCat(FPath, "type"), &info.type)) 281 PrintErrorAndDie("Failed to read from file ", FPath, "type"); 282 if (!ReadFromFile(StrCat(FPath, "level"), &info.level)) 283 PrintErrorAndDie("Failed to read from file ", FPath, "level"); 284 std::string map_str; 285 if (!ReadFromFile(StrCat(FPath, "shared_cpu_map"), &map_str)) 286 PrintErrorAndDie("Failed to read from file ", FPath, "shared_cpu_map"); 287 info.num_sharing = CountSetBitsInCPUMap(map_str); 288 res.push_back(info); 289 } 290 291 return res; 292 } 293 294 #ifdef BENCHMARK_OS_MACOSX 295 std::vector<CPUInfo::CacheInfo> GetCacheSizesMacOSX() { 296 std::vector<CPUInfo::CacheInfo> res; 297 std::array<uint64_t, 4> CacheCounts{{0, 0, 0, 0}}; 298 GetSysctl("hw.cacheconfig", &CacheCounts); 299 300 struct { 301 std::string name; 302 std::string type; 303 int level; 304 uint64_t num_sharing; 305 } Cases[] = {{"hw.l1dcachesize", "Data", 1, CacheCounts[1]}, 306 {"hw.l1icachesize", "Instruction", 1, CacheCounts[1]}, 307 {"hw.l2cachesize", "Unified", 2, CacheCounts[2]}, 308 {"hw.l3cachesize", "Unified", 3, CacheCounts[3]}}; 309 for (auto& C : Cases) { 310 int val; 311 if (!GetSysctl(C.name, &val)) continue; 312 CPUInfo::CacheInfo info; 313 info.type = C.type; 314 info.level = C.level; 315 info.size = val; 316 info.num_sharing = static_cast<int>(C.num_sharing); 317 res.push_back(std::move(info)); 318 } 319 return res; 320 } 321 #elif defined(BENCHMARK_OS_WINDOWS) 322 std::vector<CPUInfo::CacheInfo> GetCacheSizesWindows() { 323 std::vector<CPUInfo::CacheInfo> res; 324 DWORD buffer_size = 0; 325 using PInfo = SYSTEM_LOGICAL_PROCESSOR_INFORMATION; 326 using CInfo = CACHE_DESCRIPTOR; 327 328 using UPtr = std::unique_ptr<PInfo, decltype(&std::free)>; 329 GetLogicalProcessorInformation(nullptr, &buffer_size); 330 UPtr buff((PInfo*)malloc(buffer_size), &std::free); 331 if (!GetLogicalProcessorInformation(buff.get(), &buffer_size)) 332 PrintErrorAndDie("Failed during call to GetLogicalProcessorInformation: ", 333 GetLastError()); 334 335 PInfo* it = buff.get(); 336 PInfo* end = buff.get() + (buffer_size / sizeof(PInfo)); 337 338 for (; it != end; ++it) { 339 if (it->Relationship != RelationCache) continue; 340 using BitSet = std::bitset<sizeof(ULONG_PTR) * CHAR_BIT>; 341 BitSet B(it->ProcessorMask); 342 // To prevent duplicates, only consider caches where CPU 0 is specified 343 if (!B.test(0)) continue; 344 CInfo* Cache = &it->Cache; 345 CPUInfo::CacheInfo C; 346 C.num_sharing = static_cast<int>(B.count()); 347 C.level = Cache->Level; 348 C.size = Cache->Size; 349 C.type = "Unknown"; 350 switch (Cache->Type) { 351 case CacheUnified: 352 C.type = "Unified"; 353 break; 354 case CacheInstruction: 355 C.type = "Instruction"; 356 break; 357 case CacheData: 358 C.type = "Data"; 359 break; 360 case CacheTrace: 361 C.type = "Trace"; 362 break; 363 } 364 res.push_back(C); 365 } 366 return res; 367 } 368 #elif BENCHMARK_OS_QNX 369 std::vector<CPUInfo::CacheInfo> GetCacheSizesQNX() { 370 std::vector<CPUInfo::CacheInfo> res; 371 struct cacheattr_entry* cache = SYSPAGE_ENTRY(cacheattr); 372 uint32_t const elsize = SYSPAGE_ELEMENT_SIZE(cacheattr); 373 int num = SYSPAGE_ENTRY_SIZE(cacheattr) / elsize; 374 for (int i = 0; i < num; ++i) { 375 CPUInfo::CacheInfo info; 376 switch (cache->flags) { 377 case CACHE_FLAG_INSTR: 378 info.type = "Instruction"; 379 info.level = 1; 380 break; 381 case CACHE_FLAG_DATA: 382 info.type = "Data"; 383 info.level = 1; 384 break; 385 case CACHE_FLAG_UNIFIED: 386 info.type = "Unified"; 387 info.level = 2; 388 break; 389 case CACHE_FLAG_SHARED: 390 info.type = "Shared"; 391 info.level = 3; 392 break; 393 default: 394 continue; 395 break; 396 } 397 info.size = cache->line_size * cache->num_lines; 398 info.num_sharing = 0; 399 res.push_back(std::move(info)); 400 cache = SYSPAGE_ARRAY_ADJ_OFFSET(cacheattr, cache, elsize); 401 } 402 return res; 403 } 404 #endif 405 406 std::vector<CPUInfo::CacheInfo> GetCacheSizes() { 407 #ifdef BENCHMARK_OS_MACOSX 408 return GetCacheSizesMacOSX(); 409 #elif defined(BENCHMARK_OS_WINDOWS) 410 return GetCacheSizesWindows(); 411 #elif defined(BENCHMARK_OS_QNX) 412 return GetCacheSizesQNX(); 413 #else 414 return GetCacheSizesFromKVFS(); 415 #endif 416 } 417 418 std::string GetSystemName() { 419 #if defined(BENCHMARK_OS_WINDOWS) 420 std::string str; 421 const unsigned COUNT = MAX_COMPUTERNAME_LENGTH + 1; 422 TCHAR hostname[COUNT] = {'\0'}; 423 DWORD DWCOUNT = COUNT; 424 if (!GetComputerName(hostname, &DWCOUNT)) return std::string(""); 425 #ifndef UNICODE 426 str = std::string(hostname, DWCOUNT); 427 #else 428 // Using wstring_convert, Is deprecated in C++17 429 using convert_type = std::codecvt_utf8<wchar_t>; 430 std::wstring_convert<convert_type, wchar_t> converter; 431 std::wstring wStr(hostname, DWCOUNT); 432 str = converter.to_bytes(wStr); 433 #endif 434 return str; 435 #else // defined(BENCHMARK_OS_WINDOWS) 436 #ifndef HOST_NAME_MAX 437 #ifdef BENCHMARK_HAS_SYSCTL // BSD/Mac Doesnt have HOST_NAME_MAX defined 438 #define HOST_NAME_MAX 64 439 #elif defined(BENCHMARK_OS_NACL) 440 #define HOST_NAME_MAX 64 441 #elif defined(BENCHMARK_OS_QNX) 442 #define HOST_NAME_MAX 154 443 #elif defined(BENCHMARK_OS_RTEMS) 444 #define HOST_NAME_MAX 256 445 #else 446 #pragma message("HOST_NAME_MAX not defined. using 64") 447 #define HOST_NAME_MAX 64 448 #endif 449 #endif // def HOST_NAME_MAX 450 char hostname[HOST_NAME_MAX]; 451 int retVal = gethostname(hostname, HOST_NAME_MAX); 452 if (retVal != 0) return std::string(""); 453 return std::string(hostname); 454 #endif // Catch-all POSIX block. 455 } 456 457 int GetNumCPUs() { 458 #ifdef BENCHMARK_HAS_SYSCTL 459 int NumCPU = -1; 460 if (GetSysctl("hw.ncpu", &NumCPU)) return NumCPU; 461 fprintf(stderr, "Err: %s\n", strerror(errno)); 462 std::exit(EXIT_FAILURE); 463 #elif defined(BENCHMARK_OS_WINDOWS) 464 SYSTEM_INFO sysinfo; 465 // Use memset as opposed to = {} to avoid GCC missing initializer false 466 // positives. 467 std::memset(&sysinfo, 0, sizeof(SYSTEM_INFO)); 468 GetSystemInfo(&sysinfo); 469 return sysinfo.dwNumberOfProcessors; // number of logical 470 // processors in the current 471 // group 472 #elif defined(BENCHMARK_OS_SOLARIS) 473 // Returns -1 in case of a failure. 474 int NumCPU = sysconf(_SC_NPROCESSORS_ONLN); 475 if (NumCPU < 0) { 476 fprintf(stderr, "sysconf(_SC_NPROCESSORS_ONLN) failed with error: %s\n", 477 strerror(errno)); 478 } 479 return NumCPU; 480 #elif defined(BENCHMARK_OS_QNX) 481 return static_cast<int>(_syspage_ptr->num_cpu); 482 #else 483 int NumCPUs = 0; 484 int MaxID = -1; 485 std::ifstream f("/proc/cpuinfo"); 486 if (!f.is_open()) { 487 std::cerr << "failed to open /proc/cpuinfo\n"; 488 return -1; 489 } 490 const std::string Key = "processor"; 491 std::string ln; 492 while (std::getline(f, ln)) { 493 if (ln.empty()) continue; 494 size_t SplitIdx = ln.find(':'); 495 std::string value; 496 #if defined(__s390__) 497 // s390 has another format in /proc/cpuinfo 498 // it needs to be parsed differently 499 if (SplitIdx != std::string::npos) 500 value = ln.substr(Key.size() + 1, SplitIdx - Key.size() - 1); 501 #else 502 if (SplitIdx != std::string::npos) value = ln.substr(SplitIdx + 1); 503 #endif 504 if (ln.size() >= Key.size() && ln.compare(0, Key.size(), Key) == 0) { 505 NumCPUs++; 506 if (!value.empty()) { 507 int CurID = benchmark::stoi(value); 508 MaxID = std::max(CurID, MaxID); 509 } 510 } 511 } 512 if (f.bad()) { 513 std::cerr << "Failure reading /proc/cpuinfo\n"; 514 return -1; 515 } 516 if (!f.eof()) { 517 std::cerr << "Failed to read to end of /proc/cpuinfo\n"; 518 return -1; 519 } 520 f.close(); 521 522 if ((MaxID + 1) != NumCPUs) { 523 fprintf(stderr, 524 "CPU ID assignments in /proc/cpuinfo seem messed up." 525 " This is usually caused by a bad BIOS.\n"); 526 } 527 return NumCPUs; 528 #endif 529 BENCHMARK_UNREACHABLE(); 530 } 531 532 double GetCPUCyclesPerSecond(CPUInfo::Scaling scaling) { 533 // Currently, scaling is only used on linux path here, 534 // suppress diagnostics about it being unused on other paths. 535 (void)scaling; 536 537 #if defined BENCHMARK_OS_LINUX || defined BENCHMARK_OS_CYGWIN 538 long freq; 539 540 // If the kernel is exporting the tsc frequency use that. There are issues 541 // where cpuinfo_max_freq cannot be relied on because the BIOS may be 542 // exporintg an invalid p-state (on x86) or p-states may be used to put the 543 // processor in a new mode (turbo mode). Essentially, those frequencies 544 // cannot always be relied upon. The same reasons apply to /proc/cpuinfo as 545 // well. 546 if (ReadFromFile("/sys/devices/system/cpu/cpu0/tsc_freq_khz", &freq) 547 // If CPU scaling is disabled, use the *current* frequency. 548 // Note that we specifically don't want to read cpuinfo_cur_freq, 549 // because it is only readable by root. 550 || (scaling == CPUInfo::Scaling::DISABLED && 551 ReadFromFile("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq", 552 &freq)) 553 // Otherwise, if CPU scaling may be in effect, we want to use 554 // the *maximum* frequency, not whatever CPU speed some random processor 555 // happens to be using now. 556 || ReadFromFile("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", 557 &freq)) { 558 // The value is in kHz (as the file name suggests). For example, on a 559 // 2GHz warpstation, the file contains the value "2000000". 560 return freq * 1000.0; 561 } 562 563 const double error_value = -1; 564 double bogo_clock = error_value; 565 566 std::ifstream f("/proc/cpuinfo"); 567 if (!f.is_open()) { 568 std::cerr << "failed to open /proc/cpuinfo\n"; 569 return error_value; 570 } 571 572 auto startsWithKey = [](std::string const& Value, std::string const& Key) { 573 if (Key.size() > Value.size()) return false; 574 auto Cmp = [&](char X, char Y) { 575 return std::tolower(X) == std::tolower(Y); 576 }; 577 return std::equal(Key.begin(), Key.end(), Value.begin(), Cmp); 578 }; 579 580 std::string ln; 581 while (std::getline(f, ln)) { 582 if (ln.empty()) continue; 583 size_t SplitIdx = ln.find(':'); 584 std::string value; 585 if (SplitIdx != std::string::npos) value = ln.substr(SplitIdx + 1); 586 // When parsing the "cpu MHz" and "bogomips" (fallback) entries, we only 587 // accept positive values. Some environments (virtual machines) report zero, 588 // which would cause infinite looping in WallTime_Init. 589 if (startsWithKey(ln, "cpu MHz")) { 590 if (!value.empty()) { 591 double cycles_per_second = benchmark::stod(value) * 1000000.0; 592 if (cycles_per_second > 0) return cycles_per_second; 593 } 594 } else if (startsWithKey(ln, "bogomips")) { 595 if (!value.empty()) { 596 bogo_clock = benchmark::stod(value) * 1000000.0; 597 if (bogo_clock < 0.0) bogo_clock = error_value; 598 } 599 } 600 } 601 if (f.bad()) { 602 std::cerr << "Failure reading /proc/cpuinfo\n"; 603 return error_value; 604 } 605 if (!f.eof()) { 606 std::cerr << "Failed to read to end of /proc/cpuinfo\n"; 607 return error_value; 608 } 609 f.close(); 610 // If we found the bogomips clock, but nothing better, we'll use it (but 611 // we're not happy about it); otherwise, fallback to the rough estimation 612 // below. 613 if (bogo_clock >= 0.0) return bogo_clock; 614 615 #elif defined BENCHMARK_HAS_SYSCTL 616 constexpr auto* FreqStr = 617 #if defined(BENCHMARK_OS_FREEBSD) || defined(BENCHMARK_OS_NETBSD) 618 "machdep.tsc_freq"; 619 #elif defined BENCHMARK_OS_OPENBSD 620 "hw.cpuspeed"; 621 #elif defined BENCHMARK_OS_DRAGONFLY 622 "hw.tsc_frequency"; 623 #else 624 "hw.cpufrequency"; 625 #endif 626 unsigned long long hz = 0; 627 #if defined BENCHMARK_OS_OPENBSD 628 if (GetSysctl(FreqStr, &hz)) return hz * 1000000; 629 #else 630 if (GetSysctl(FreqStr, &hz)) return hz; 631 #endif 632 fprintf(stderr, "Unable to determine clock rate from sysctl: %s: %s\n", 633 FreqStr, strerror(errno)); 634 635 #elif defined BENCHMARK_OS_WINDOWS 636 // In NT, read MHz from the registry. If we fail to do so or we're in win9x 637 // then make a crude estimate. 638 DWORD data, data_size = sizeof(data); 639 if (IsWindowsXPOrGreater() && 640 SUCCEEDED( 641 SHGetValueA(HKEY_LOCAL_MACHINE, 642 "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 643 "~MHz", nullptr, &data, &data_size))) 644 return static_cast<double>((int64_t)data * 645 (int64_t)(1000 * 1000)); // was mhz 646 #elif defined(BENCHMARK_OS_SOLARIS) 647 kstat_ctl_t* kc = kstat_open(); 648 if (!kc) { 649 std::cerr << "failed to open /dev/kstat\n"; 650 return -1; 651 } 652 kstat_t* ksp = kstat_lookup(kc, (char*)"cpu_info", -1, (char*)"cpu_info0"); 653 if (!ksp) { 654 std::cerr << "failed to lookup in /dev/kstat\n"; 655 return -1; 656 } 657 if (kstat_read(kc, ksp, NULL) < 0) { 658 std::cerr << "failed to read from /dev/kstat\n"; 659 return -1; 660 } 661 kstat_named_t* knp = 662 (kstat_named_t*)kstat_data_lookup(ksp, (char*)"current_clock_Hz"); 663 if (!knp) { 664 std::cerr << "failed to lookup data in /dev/kstat\n"; 665 return -1; 666 } 667 if (knp->data_type != KSTAT_DATA_UINT64) { 668 std::cerr << "current_clock_Hz is of unexpected data type: " 669 << knp->data_type << "\n"; 670 return -1; 671 } 672 double clock_hz = knp->value.ui64; 673 kstat_close(kc); 674 return clock_hz; 675 #elif defined(BENCHMARK_OS_QNX) 676 return static_cast<double>((int64_t)(SYSPAGE_ENTRY(cpuinfo)->speed) * 677 (int64_t)(1000 * 1000)); 678 #endif 679 // If we've fallen through, attempt to roughly estimate the CPU clock rate. 680 const int estimate_time_ms = 1000; 681 const auto start_ticks = cycleclock::Now(); 682 SleepForMilliseconds(estimate_time_ms); 683 return static_cast<double>(cycleclock::Now() - start_ticks); 684 } 685 686 std::vector<double> GetLoadAvg() { 687 #if (defined BENCHMARK_OS_FREEBSD || defined(BENCHMARK_OS_LINUX) || \ 688 defined BENCHMARK_OS_MACOSX || defined BENCHMARK_OS_NETBSD || \ 689 defined BENCHMARK_OS_OPENBSD || defined BENCHMARK_OS_DRAGONFLY) && \ 690 !defined(__ANDROID__) 691 constexpr int kMaxSamples = 3; 692 std::vector<double> res(kMaxSamples, 0.0); 693 const int nelem = getloadavg(res.data(), kMaxSamples); 694 if (nelem < 1) { 695 res.clear(); 696 } else { 697 res.resize(nelem); 698 } 699 return res; 700 #else 701 return {}; 702 #endif 703 } 704 705 } // end namespace 706 707 const CPUInfo& CPUInfo::Get() { 708 static const CPUInfo* info = new CPUInfo(); 709 return *info; 710 } 711 712 CPUInfo::CPUInfo() 713 : num_cpus(GetNumCPUs()), 714 scaling(CpuScaling(num_cpus)), 715 cycles_per_second(GetCPUCyclesPerSecond(scaling)), 716 caches(GetCacheSizes()), 717 load_avg(GetLoadAvg()) {} 718 719 const SystemInfo& SystemInfo::Get() { 720 static const SystemInfo* info = new SystemInfo(); 721 return *info; 722 } 723 724 SystemInfo::SystemInfo() : name(GetSystemName()) {} 725 } // end namespace benchmark 726