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
68 namespace benchmark {
69 namespace {
70
PrintImp(std::ostream & out)71 void PrintImp(std::ostream& out) { out << std::endl; }
72
73 template <class First, class... Rest>
PrintImp(std::ostream & out,First && f,Rest &&...rest)74 void PrintImp(std::ostream& out, First&& f, Rest&&... rest) {
75 out << std::forward<First>(f);
76 PrintImp(out, std::forward<Rest>(rest)...);
77 }
78
79 template <class... Args>
PrintErrorAndDie(Args &&...args)80 BENCHMARK_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.
89 struct 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:
ValueUnionbenchmark::__anon7f7363d20111::ValueUnion103 ValueUnion() : Size(0), Buff(nullptr, &std::free) {}
104
ValueUnionbenchmark::__anon7f7363d20111::ValueUnion105 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
operator boolbenchmark::__anon7f7363d20111::ValueUnion111 explicit operator bool() const { return bool(Buff); }
112
databenchmark::__anon7f7363d20111::ValueUnion113 char* data() const { return Buff->bytes; }
114
GetAsStringbenchmark::__anon7f7363d20111::ValueUnion115 std::string GetAsString() const { return std::string(data()); }
116
GetAsIntegerbenchmark::__anon7f7363d20111::ValueUnion117 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
GetAsUnsignedbenchmark::__anon7f7363d20111::ValueUnion125 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>
GetAsArraybenchmark::__anon7f7363d20111::ValueUnion134 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
GetSysctlImp(std::string const & Name)143 ValueUnion 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
175 BENCHMARK_MAYBE_UNUSED
GetSysctl(std::string const & Name,std::string * Out)176 bool 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
184 template <class Tp,
185 class = typename std::enable_if<std::is_integral<Tp>::value>::type>
GetSysctl(std::string const & Name,Tp * Out)186 bool 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
194 template <class Tp, size_t N>
GetSysctl(std::string const & Name,std::array<Tp,N> * Out)195 bool 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
203 template <class ArgT>
ReadFromFile(std::string const & fname,ArgT * arg)204 bool 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
CpuScalingEnabled(int num_cpus)212 bool 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
CountSetBitsInCPUMap(std::string Val)232 int 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
251 BENCHMARK_MAYBE_UNUSED
GetCacheSizesFromKVFS()252 std::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
GetCacheSizesMacOSX()290 std::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)
GetCacheSizesWindows()317 std::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
GetCacheSizesQNX()366 std::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
GetCacheSizes()401 std::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
GetSystemName()413 std::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
GetNumCPUs()444 int 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
GetCPUCyclesPerSecond()519 double 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
GetLoadAvg()660 std::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
Get()680 const CPUInfo& CPUInfo::Get() {
681 static const CPUInfo* info = new CPUInfo();
682 return *info;
683 }
684
CPUInfo()685 CPUInfo::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
Get()693 const SystemInfo& SystemInfo::Get() {
694 static const SystemInfo* info = new SystemInfo();
695 return *info;
696 }
697
SystemInfo()698 SystemInfo::SystemInfo() : name(GetSystemName()) {}
699 } // end namespace benchmark
700