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