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