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 *= 1024;
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#ifndef HOST_NAME_MAX
433#ifdef BENCHMARK_HAS_SYSCTL // BSD/Mac Doesnt have HOST_NAME_MAX defined
434#define HOST_NAME_MAX 64
435#elif defined(BENCHMARK_OS_NACL)
436#define HOST_NAME_MAX 64
437#elif defined(BENCHMARK_OS_QNX)
438#define HOST_NAME_MAX 154
439#elif defined(BENCHMARK_OS_RTEMS)
440#define HOST_NAME_MAX 256
441#else
442#warning "HOST_NAME_MAX not defined. using 64"
443#define HOST_NAME_MAX 64
444#endif
445#endif // def HOST_NAME_MAX
446 char hostname[HOST_NAME_MAX];
447 int retVal = gethostname(hostname, HOST_NAME_MAX);
448 if (retVal != 0) return std::string("");
449 return std::string(hostname);
450#endif // Catch-all POSIX block.
451}
452
453int GetNumCPUs() {
454#ifdef BENCHMARK_HAS_SYSCTL
455 int NumCPU = -1;
456 if (GetSysctl("hw.ncpu", &NumCPU)) return NumCPU;
457 fprintf(stderr, "Err: %s\n", strerror(errno));
458 std::exit(EXIT_FAILURE);
459#elif defined(BENCHMARK_OS_WINDOWS)
460 SYSTEM_INFO sysinfo;
461 // Use memset as opposed to = {} to avoid GCC missing initializer false
462 // positives.
463 std::memset(&sysinfo, 0, sizeof(SYSTEM_INFO));
464 GetSystemInfo(&sysinfo);
465 return sysinfo.dwNumberOfProcessors; // number of logical
466 // processors in the current
467 // group
468#elif defined(BENCHMARK_OS_SOLARIS)
469 // Returns -1 in case of a failure.
470 int NumCPU = sysconf(_SC_NPROCESSORS_ONLN);
471 if (NumCPU < 0) {
472 fprintf(stderr,
473 "sysconf(_SC_NPROCESSORS_ONLN) failed with error: %s\n",
474 strerror(errno));
475 }
476 return NumCPU;
477#elif defined(BENCHMARK_OS_QNX)
478 return static_cast<int>(_syspage_ptr->num_cpu);
479#else
480 int NumCPUs = 0;
481 int MaxID = -1;
482 std::ifstream f("/proc/cpuinfo");
483 if (!f.is_open()) {
484 std::cerr << "failed to open /proc/cpuinfo\n";
485 return -1;
486 }
487 const std::string Key = "processor";
488 std::string ln;
489 while (std::getline(f, ln)) {
490 if (ln.empty()) continue;
491 size_t SplitIdx = ln.find(':');
492 std::string value;
493#if defined(__s390__)
494 // s390 has another format in /proc/cpuinfo
495 // it needs to be parsed differently
496 if (SplitIdx != std::string::npos) value = ln.substr(Key.size()+1,SplitIdx-Key.size()-1);
497#else
498 if (SplitIdx != std::string::npos) value = ln.substr(SplitIdx + 1);
499#endif
500 if (ln.size() >= Key.size() && ln.compare(0, Key.size(), Key) == 0) {
501 NumCPUs++;
502 if (!value.empty()) {
503 int CurID = benchmark::stoi(value);
504 MaxID = std::max(CurID, MaxID);
505 }
506 }
507 }
508 if (f.bad()) {
509 std::cerr << "Failure reading /proc/cpuinfo\n";
510 return -1;
511 }
512 if (!f.eof()) {
513 std::cerr << "Failed to read to end of /proc/cpuinfo\n";
514 return -1;
515 }
516 f.close();
517
518 if ((MaxID + 1) != NumCPUs) {
519 fprintf(stderr,
520 "CPU ID assignments in /proc/cpuinfo seem messed up."
521 " This is usually caused by a bad BIOS.\n");
522 }
523 return NumCPUs;
524#endif
525 BENCHMARK_UNREACHABLE();
526}
527
528double GetCPUCyclesPerSecond() {
529#if defined BENCHMARK_OS_LINUX || defined BENCHMARK_OS_CYGWIN
530 long freq;
531
532 // If the kernel is exporting the tsc frequency use that. There are issues
533 // where cpuinfo_max_freq cannot be relied on because the BIOS may be
534 // exporintg an invalid p-state (on x86) or p-states may be used to put the
535 // processor in a new mode (turbo mode). Essentially, those frequencies
536 // cannot always be relied upon. The same reasons apply to /proc/cpuinfo as
537 // well.
538 if (ReadFromFile("/sys/devices/system/cpu/cpu0/tsc_freq_khz", &freq)
539 // If CPU scaling is in effect, we want to use the *maximum* frequency,
540 // not whatever CPU speed some random processor happens to be using now.
541 || ReadFromFile("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq",
542 &freq)) {
543 // The value is in kHz (as the file name suggests). For example, on a
544 // 2GHz warpstation, the file contains the value "2000000".
545 return freq * 1000.0;
546 }
547
548 const double error_value = -1;
549 double bogo_clock = error_value;
550
551 std::ifstream f("/proc/cpuinfo");
552 if (!f.is_open()) {
553 std::cerr << "failed to open /proc/cpuinfo\n";
554 return error_value;
555 }
556
557 auto startsWithKey = [](std::string const& Value, std::string const& Key) {
558 if (Key.size() > Value.size()) return false;
559 auto Cmp = [&](char X, char Y) {
560 return std::tolower(X) == std::tolower(Y);
561 };
562 return std::equal(Key.begin(), Key.end(), Value.begin(), Cmp);
563 };
564
565 std::string ln;
566 while (std::getline(f, ln)) {
567 if (ln.empty()) continue;
568 size_t SplitIdx = ln.find(':');
569 std::string value;
570 if (SplitIdx != std::string::npos) value = ln.substr(SplitIdx + 1);
571 // When parsing the "cpu MHz" and "bogomips" (fallback) entries, we only
572 // accept positive values. Some environments (virtual machines) report zero,
573 // which would cause infinite looping in WallTime_Init.
574 if (startsWithKey(ln, "cpu MHz")) {
575 if (!value.empty()) {
576 double cycles_per_second = benchmark::stod(value) * 1000000.0;
577 if (cycles_per_second > 0) return cycles_per_second;
578 }
579 } else if (startsWithKey(ln, "bogomips")) {
580 if (!value.empty()) {
581 bogo_clock = benchmark::stod(value) * 1000000.0;
582 if (bogo_clock < 0.0) bogo_clock = error_value;
583 }
584 }
585 }
586 if (f.bad()) {
587 std::cerr << "Failure reading /proc/cpuinfo\n";
588 return error_value;
589 }
590 if (!f.eof()) {
591 std::cerr << "Failed to read to end of /proc/cpuinfo\n";
592 return error_value;
593 }
594 f.close();
595 // If we found the bogomips clock, but nothing better, we'll use it (but
596 // we're not happy about it); otherwise, fallback to the rough estimation
597 // below.
598 if (bogo_clock >= 0.0) return bogo_clock;
599
600#elif defined BENCHMARK_HAS_SYSCTL
601 constexpr auto* FreqStr =
602#if defined(BENCHMARK_OS_FREEBSD) || defined(BENCHMARK_OS_NETBSD)
603 "machdep.tsc_freq";
604#elif defined BENCHMARK_OS_OPENBSD
605 "hw.cpuspeed";
606#else
607 "hw.cpufrequency";
608#endif
609 unsigned long long hz = 0;
610#if defined BENCHMARK_OS_OPENBSD
611 if (GetSysctl(FreqStr, &hz)) return hz * 1000000;
612#else
613 if (GetSysctl(FreqStr, &hz)) return hz;
614#endif
615 fprintf(stderr, "Unable to determine clock rate from sysctl: %s: %s\n",
616 FreqStr, strerror(errno));
617
618#elif defined BENCHMARK_OS_WINDOWS
619 // In NT, read MHz from the registry. If we fail to do so or we're in win9x
620 // then make a crude estimate.
621 DWORD data, data_size = sizeof(data);
622 if (IsWindowsXPOrGreater() &&
623 SUCCEEDED(
624 SHGetValueA(HKEY_LOCAL_MACHINE,
625 "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
626 "~MHz", nullptr, &data, &data_size)))
627 return static_cast<double>((int64_t)data *
628 (int64_t)(1000 * 1000)); // was mhz
629#elif defined (BENCHMARK_OS_SOLARIS)
630 kstat_ctl_t *kc = kstat_open();
631 if (!kc) {
632 std::cerr << "failed to open /dev/kstat\n";
633 return -1;
634 }
635 kstat_t *ksp = kstat_lookup(kc, (char*)"cpu_info", -1, (char*)"cpu_info0");
636 if (!ksp) {
637 std::cerr << "failed to lookup in /dev/kstat\n";
638 return -1;
639 }
640 if (kstat_read(kc, ksp, NULL) < 0) {
641 std::cerr << "failed to read from /dev/kstat\n";
642 return -1;
643 }
644 kstat_named_t *knp =
645 (kstat_named_t*)kstat_data_lookup(ksp, (char*)"current_clock_Hz");
646 if (!knp) {
647 std::cerr << "failed to lookup data in /dev/kstat\n";
648 return -1;
649 }
650 if (knp->data_type != KSTAT_DATA_UINT64) {
651 std::cerr << "current_clock_Hz is of unexpected data type: "
652 << knp->data_type << "\n";
653 return -1;
654 }
655 double clock_hz = knp->value.ui64;
656 kstat_close(kc);
657 return clock_hz;
658#elif defined (BENCHMARK_OS_QNX)
659 return static_cast<double>((int64_t)(SYSPAGE_ENTRY(cpuinfo)->speed) *
660 (int64_t)(1000 * 1000));
661#endif
662 // If we've fallen through, attempt to roughly estimate the CPU clock rate.
663 const int estimate_time_ms = 1000;
664 const auto start_ticks = cycleclock::Now();
665 SleepForMilliseconds(estimate_time_ms);
666 return static_cast<double>(cycleclock::Now() - start_ticks);
667}
668
669std::vector<double> GetLoadAvg() {
670#if (defined BENCHMARK_OS_FREEBSD || defined(BENCHMARK_OS_LINUX) || \
671 defined BENCHMARK_OS_MACOSX || defined BENCHMARK_OS_NETBSD || \
672 defined BENCHMARK_OS_OPENBSD) && !defined(__ANDROID__)
673 constexpr int kMaxSamples = 3;
674 std::vector<double> res(kMaxSamples, 0.0);
675 const int nelem = getloadavg(res.data(), kMaxSamples);
676 if (nelem < 1) {
677 res.clear();
678 } else {
679 res.resize(nelem);
680 }
681 return res;
682#else
683 return {};
684#endif
685}
686
687} // end namespace
688
689const CPUInfo& CPUInfo::Get() {
690 static const CPUInfo* info = new CPUInfo();
691 return *info;
692}
693
694CPUInfo::CPUInfo()
695 : num_cpus(GetNumCPUs()),
696 cycles_per_second(GetCPUCyclesPerSecond()),
697 caches(GetCacheSizes()),
698 scaling_enabled(CpuScalingEnabled(num_cpus)),
699 load_avg(GetLoadAvg()) {}
700
701
702const SystemInfo& SystemInfo::Get() {
703 static const SystemInfo* info = new SystemInfo();
704 return *info;
705}
706
707SystemInfo::SystemInfo() : name(GetSystemName()) {}
708} // end namespace benchmark
709