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