blob: 953c170cfd6e63213e5e0939b07831bc0b2556c6 [file] [log] [blame]
Eric Fiselier5cfd6bc2015-03-09 16:15:05 -04001// Copyright 2015 Google Inc. All rights reserved.
Dominic Hamon78fa0b92014-01-09 12:16:51 -08002//
Dominic Hamon3fb82682014-01-09 08:01:34 -08003// 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
Dominic Hamon78fa0b92014-01-09 12:16:51 -08006//
Dominic Hamon3fb82682014-01-09 08:01:34 -08007// http://www.apache.org/licenses/LICENSE-2.0
Dominic Hamon78fa0b92014-01-09 12:16:51 -08008//
Dominic Hamon3fb82682014-01-09 08:01:34 -08009// 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
Matt Clarksondd613912015-03-27 13:41:50 +000015#include "internal_macros.h"
Dominic Hamon403f3542013-12-18 16:55:45 -080016
Anton Danielssonba141ac2015-10-05 13:58:35 +020017#ifdef BENCHMARK_OS_WINDOWS
Martin Storsjö52613072018-09-18 11:42:20 +030018#include <shlwapi.h>
Wink Saville61497232018-03-07 03:20:06 -080019#undef StrCat // Don't let StrCat in string_util.h be renamed to lstrcatA
Martin Storsjö52613072018-09-18 11:42:20 +030020#include <versionhelpers.h>
21#include <windows.h>
Jatin Chaudhary47a5f772018-12-11 16:53:02 +053022#include <codecvt>
Matt Clarksondd613912015-03-27 13:41:50 +000023#else
Dominic Hamon403f3542013-12-18 16:55:45 -080024#include <fcntl.h>
Ian McKellar6ecf8a82018-02-14 13:17:12 -080025#ifndef BENCHMARK_OS_FUCHSIA
Dominic Hamon403f3542013-12-18 16:55:45 -080026#include <sys/resource.h>
Ian McKellar6ecf8a82018-02-14 13:17:12 -080027#endif
Dominic Hamon403f3542013-12-18 16:55:45 -080028#include <sys/time.h>
Dominic Hamon332f6772016-10-07 11:35:03 -070029#include <sys/types.h> // this header must be included before 'sys/sysctl.h' to avoid compilation error on FreeBSD
Dominic Hamon403f3542013-12-18 16:55:45 -080030#include <unistd.h>
Eric27e0b432017-11-22 09:33:52 -070031#if defined BENCHMARK_OS_FREEBSD || defined BENCHMARK_OS_MACOSX || \
Nan Xiaoea5551e2018-05-02 18:26:43 +080032 defined BENCHMARK_OS_NETBSD || defined BENCHMARK_OS_OPENBSD
Eric27e0b432017-11-22 09:33:52 -070033#define BENCHMARK_HAS_SYSCTL
Anton Danielsson0d35f5f2015-10-05 10:24:12 +020034#include <sys/sysctl.h>
35#endif
Matt Clarksondd613912015-03-27 13:41:50 +000036#endif
alekseyshl47df49e2018-03-02 03:53:58 -080037#if defined(BENCHMARK_OS_SOLARIS)
38#include <kstat.h>
39#endif
Jilin Zhoud205ead2019-02-28 05:42:44 -050040#if defined(BENCHMARK_OS_QNX)
41#include <sys/syspage.h>
42#endif
Dominic Hamon403f3542013-12-18 16:55:45 -080043
Eric27e0b432017-11-22 09:33:52 -070044#include <algorithm>
Eric11dc3682017-11-26 13:33:01 -070045#include <array>
46#include <bitset>
Eric Fiseliera187aa02015-03-06 17:01:05 -050047#include <cerrno>
Eric11dc3682017-11-26 13:33:01 -070048#include <climits>
Eric Fiseliera187aa02015-03-06 17:01:05 -050049#include <cstdint>
Dominic Hamon332f6772016-10-07 11:35:03 -070050#include <cstdio>
Eric Fiseliera187aa02015-03-06 17:01:05 -050051#include <cstdlib>
52#include <cstring>
Eric27e0b432017-11-22 09:33:52 -070053#include <fstream>
Dominic Hamon403f3542013-12-18 16:55:45 -080054#include <iostream>
Eric27e0b432017-11-22 09:33:52 -070055#include <iterator>
Dominic Hamon403f3542013-12-18 16:55:45 -080056#include <limits>
Eric27e0b432017-11-22 09:33:52 -070057#include <memory>
58#include <sstream>
Jatin Chaudhary47a5f772018-12-11 16:53:02 +053059#include <locale>
Dominic Hamon403f3542013-12-18 16:55:45 -080060
Dominic Hamona3b5e442014-10-31 21:54:55 -070061#include "check.h"
Dominic Hamon403f3542013-12-18 16:55:45 -080062#include "cycleclock.h"
Eric Fiseliera187aa02015-03-06 17:01:05 -050063#include "internal_macros.h"
Eric Fiselier7a767012015-03-12 18:03:33 -040064#include "log.h"
Dominic Hamon403f3542013-12-18 16:55:45 -080065#include "sleep.h"
Eric Fiseliera3308c62015-03-26 14:26:07 -040066#include "string_util.h"
Dominic Hamon403f3542013-12-18 16:55:45 -080067
Dominic Hamone390e4e2013-12-19 16:18:09 -080068namespace benchmark {
Dominic Hamon403f3542013-12-18 16:55:45 -080069namespace {
Dominic Hamon403f3542013-12-18 16:55:45 -080070
Eric27e0b432017-11-22 09:33:52 -070071void PrintImp(std::ostream& out) { out << std::endl; }
Lei Xu3460bf12014-07-24 23:57:09 -070072
Eric27e0b432017-11-22 09:33:52 -070073template <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
Eric27e0b432017-11-22 09:33:52 -070087/// 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;
Victor Costan95a14352017-11-30 08:05:38 -080093 // For correct aliasing of union members from bytes.
94 char bytes[8];
Eric27e0b432017-11-22 09:33:52 -070095 };
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 }
Eric11dc3682017-11-26 13:33:01 -0700132
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 }
Eric27e0b432017-11-22 09:33:52 -0700141};
142
Eric27e0b432017-11-22 09:33:52 -0700143ValueUnion GetSysctlImp(std::string const& Name) {
Nan Xiaoea5551e2018-05-02 18:26:43 +0800144#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
Eric27e0b432017-11-22 09:33:52 -0700164 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();
Nan Xiaoea5551e2018-05-02 18:26:43 +0800172#endif
Eric27e0b432017-11-22 09:33:52 -0700173}
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;
Dominic Hamon403f3542013-12-18 16:55:45 -0800192}
Eric11dc3682017-11-26 13:33:01 -0700193
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;
Victor Costan95a14352017-11-30 08:05:38 -0800200}
Dominic Hamone2633b92014-01-16 09:13:18 -0800201#endif
Dominic Hamon403f3542013-12-18 16:55:45 -0800202
Eric27e0b432017-11-22 09:33:52 -0700203template <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;
Jilin Zhoud205ead2019-02-28 05:42:44 -0500215#ifdef BENCHMARK_OS_QNX
216 return false;
217#endif
Eric27e0b432017-11-22 09:33:52 -0700218#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
Eric11dc3682017-11-26 13:33:01 -0700232int 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;
Marat Dukhan7fb3c562018-06-05 03:36:26 -0700236 CPUMask Mask(benchmark::stoul(Part, nullptr, 16));
Eric11dc3682017-11-26 13:33:01 -0700237 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
Eric27e0b432017-11-22 09:33:52 -0700251BENCHMARK_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;
Dominic Hamon403f3542013-12-18 16:55:45 -0800274 }
Eric27e0b432017-11-22 09:33:52 -0700275 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");
Eric11dc3682017-11-26 13:33:01 -0700279 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);
Eric27e0b432017-11-22 09:33:52 -0700283 res.push_back(info);
Dominic Hamon403f3542013-12-18 16:55:45 -0800284 }
Eric27e0b432017-11-22 09:33:52 -0700285
286 return res;
287}
288
289#ifdef BENCHMARK_OS_MACOSX
290std::vector<CPUInfo::CacheInfo> GetCacheSizesMacOSX() {
291 std::vector<CPUInfo::CacheInfo> res;
Eric11dc3682017-11-26 13:33:01 -0700292 std::array<uint64_t, 4> CacheCounts{{0, 0, 0, 0}};
293 GetSysctl("hw.cacheconfig", &CacheCounts);
294
Eric27e0b432017-11-22 09:33:52 -0700295 struct {
296 std::string name;
297 std::string type;
298 int level;
Roman Lebedevf0901412018-09-05 14:20:18 +0300299 uint64_t num_sharing;
Eric11dc3682017-11-26 13:33:01 -0700300 } 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]}};
Eric27e0b432017-11-22 09:33:52 -0700304 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;
Eric11dc3682017-11-26 13:33:01 -0700311 info.num_sharing = static_cast<int>(C.num_sharing);
Eric27e0b432017-11-22 09:33:52 -0700312 res.push_back(std::move(info));
313 }
314 return res;
Dominic Hamon403f3542013-12-18 16:55:45 -0800315}
Eric11dc3682017-11-26 13:33:01 -0700316#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;
oskidan4fe02062018-01-19 19:17:01 +0200341 C.num_sharing = static_cast<int>(B.count());
Eric11dc3682017-11-26 13:33:01 -0700342 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}
Jilin Zhoud205ead2019-02-28 05:42:44 -0500365#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}
Dominic Hamone2633b92014-01-16 09:13:18 -0800399#endif
Dominic Hamon403f3542013-12-18 16:55:45 -0800400
Eric27e0b432017-11-22 09:33:52 -0700401std::vector<CPUInfo::CacheInfo> GetCacheSizes() {
402#ifdef BENCHMARK_OS_MACOSX
403 return GetCacheSizesMacOSX();
Eric11dc3682017-11-26 13:33:01 -0700404#elif defined(BENCHMARK_OS_WINDOWS)
405 return GetCacheSizesWindows();
Jilin Zhoud205ead2019-02-28 05:42:44 -0500406#elif defined(BENCHMARK_OS_QNX)
407 return GetCacheSizesQNX();
Eric27e0b432017-11-22 09:33:52 -0700408#else
409 return GetCacheSizesFromKVFS();
Eric Fiselier03c44852016-10-07 22:54:06 -0600410#endif
Eric27e0b432017-11-22 09:33:52 -0700411}
Eric Fiselier03c44852016-10-07 22:54:06 -0600412
Jatin Chaudhary47a5f772018-12-11 16:53:02 +0530413std::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_OS_MACOSX //Mac Doesnt have HOST_NAME_MAX defined
433#define HOST_NAME_MAX 64
Jilin Zhou0ae233a2019-02-19 08:05:55 -0500434#elif defined(BENCHMARK_OS_QNX)
435#define HOST_NAME_MAX 154
Jatin Chaudhary47a5f772018-12-11 16:53:02 +0530436#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
Eric27e0b432017-11-22 09:33:52 -0700444int 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
alekseyshl47df49e2018-03-02 03:53:58 -0800459#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;
Jilin Zhoud205ead2019-02-28 05:42:44 -0500468#elif defined(BENCHMARK_OS_QNX)
469 return static_cast<int>(_syspage_ptr->num_cpu);
Eric27e0b432017-11-22 09:33:52 -0700470#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;
Anton Gladkyc6193af2018-10-21 10:01:42 +0200484#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
Eric27e0b432017-11-22 09:33:52 -0700489 if (SplitIdx != std::string::npos) value = ln.substr(SplitIdx + 1);
Anton Gladkyc6193af2018-10-21 10:01:42 +0200490#endif
Eric27e0b432017-11-22 09:33:52 -0700491 if (ln.size() >= Key.size() && ln.compare(0, Key.size(), Key) == 0) {
492 NumCPUs++;
493 if (!value.empty()) {
Marat Dukhan7fb3c562018-06-05 03:36:26 -0700494 int CurID = benchmark::stoi(value);
Eric27e0b432017-11-22 09:33:52 -0700495 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() {
Anton Danielssonba141ac2015-10-05 13:58:35 +0200520#if defined BENCHMARK_OS_LINUX || defined BENCHMARK_OS_CYGWIN
Dominic Hamon3968ff42015-02-18 10:07:45 -0800521 long freq;
Dominic Hamon403f3542013-12-18 16:55:45 -0800522
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.
Eric27e0b432017-11-22 09:33:52 -0700529 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)) {
Dominic Hamon4ce184d2014-01-09 12:12:11 -0800534 // The value is in kHz (as the file name suggests). For example, on a
535 // 2GHz warpstation, the file contains the value "2000000".
Eric27e0b432017-11-22 09:33:52 -0700536 return freq * 1000.0;
Dominic Hamon403f3542013-12-18 16:55:45 -0800537 }
538
Eric27e0b432017-11-22 09:33:52 -0700539 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;
Dominic Hamon403f3542013-12-18 16:55:45 -0800546 }
547
Eric27e0b432017-11-22 09:33:52 -0700548 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 };
Dominic Hamon403f3542013-12-18 16:55:45 -0800555
Eric27e0b432017-11-22 09:33:52 -0700556 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);
Dominic Hamon403f3542013-12-18 16:55:45 -0800562 // When parsing the "cpu MHz" and "bogomips" (fallback) entries, we only
Wink Saville69a52cf2018-03-06 03:44:25 -0800563 // accept positive values. Some environments (virtual machines) report zero,
Dominic Hamon403f3542013-12-18 16:55:45 -0800564 // which would cause infinite looping in WallTime_Init.
Eric27e0b432017-11-22 09:33:52 -0700565 if (startsWithKey(ln, "cpu MHz")) {
566 if (!value.empty()) {
Marat Dukhan7fb3c562018-06-05 03:36:26 -0700567 double cycles_per_second = benchmark::stod(value) * 1000000.0;
Eric27e0b432017-11-22 09:33:52 -0700568 if (cycles_per_second > 0) return cycles_per_second;
Dominic Hamon403f3542013-12-18 16:55:45 -0800569 }
Eric27e0b432017-11-22 09:33:52 -0700570 } else if (startsWithKey(ln, "bogomips")) {
571 if (!value.empty()) {
Marat Dukhan7fb3c562018-06-05 03:36:26 -0700572 bogo_clock = benchmark::stod(value) * 1000000.0;
Eric27e0b432017-11-22 09:33:52 -0700573 if (bogo_clock < 0.0) bogo_clock = error_value;
Dominic Hamon403f3542013-12-18 16:55:45 -0800574 }
Dominic Hamon403f3542013-12-18 16:55:45 -0800575 }
576 }
Eric27e0b432017-11-22 09:33:52 -0700577 if (f.bad()) {
578 std::cerr << "Failure reading /proc/cpuinfo\n";
579 return error_value;
Dominic Hamon403f3542013-12-18 16:55:45 -0800580 }
Eric27e0b432017-11-22 09:33:52 -0700581 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;
Dominic Hamon403f3542013-12-18 16:55:45 -0800590
Eric27e0b432017-11-22 09:33:52 -0700591#elif defined BENCHMARK_HAS_SYSCTL
592 constexpr auto* FreqStr =
593#if defined(BENCHMARK_OS_FREEBSD) || defined(BENCHMARK_OS_NETBSD)
594 "machdep.tsc_freq";
Nan Xiaoea5551e2018-05-02 18:26:43 +0800595#elif defined BENCHMARK_OS_OPENBSD
596 "hw.cpuspeed";
Dominic Hamon403f3542013-12-18 16:55:45 -0800597#else
Eric27e0b432017-11-22 09:33:52 -0700598 "hw.cpufrequency";
Dominic Hamon403f3542013-12-18 16:55:45 -0800599#endif
Eric27e0b432017-11-22 09:33:52 -0700600 unsigned long long hz = 0;
Nan Xiaoea5551e2018-05-02 18:26:43 +0800601#if defined BENCHMARK_OS_OPENBSD
602 if (GetSysctl(FreqStr, &hz)) return hz * 1000000;
603#else
Eric27e0b432017-11-22 09:33:52 -0700604 if (GetSysctl(FreqStr, &hz)) return hz;
Nan Xiaoea5551e2018-05-02 18:26:43 +0800605#endif
Eric27e0b432017-11-22 09:33:52 -0700606 fprintf(stderr, "Unable to determine clock rate from sysctl: %s: %s\n",
607 FreqStr, strerror(errno));
608
Anton Danielssonba141ac2015-10-05 13:58:35 +0200609#elif defined BENCHMARK_OS_WINDOWS
Dominic Hamon403f3542013-12-18 16:55:45 -0800610 // In NT, read MHz from the registry. If we fail to do so or we're in win9x
611 // then make a crude estimate.
Dominic Hamon403f3542013-12-18 16:55:45 -0800612 DWORD data, data_size = sizeof(data);
Anton Danielssonfcf4e992015-10-05 14:51:56 +0200613 if (IsWindowsXPOrGreater() &&
Dominic Hamon4ce184d2014-01-09 12:12:11 -0800614 SUCCEEDED(
615 SHGetValueA(HKEY_LOCAL_MACHINE,
616 "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
Eric Fiselier18098172015-03-12 19:32:50 -0400617 "~MHz", nullptr, &data, &data_size)))
Eric27e0b432017-11-22 09:33:52 -0700618 return static_cast<double>((int64_t)data *
619 (int64_t)(1000 * 1000)); // was mhz
alekseyshl47df49e2018-03-02 03:53:58 -0800620#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;
Jilin Zhoud205ead2019-02-28 05:42:44 -0500649#elif defined (BENCHMARK_OS_QNX)
650 return static_cast<double>((int64_t)(SYSPAGE_ENTRY(cpuinfo)->speed) *
651 (int64_t)(1000 * 1000));
Dominic Hamon403f3542013-12-18 16:55:45 -0800652#endif
Eric27e0b432017-11-22 09:33:52 -0700653 // 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);
Dominic Hamon403f3542013-12-18 16:55:45 -0800658}
Ericcba945e2016-09-02 21:34:34 -0600659
Ori Livnehda9ec3d2018-07-09 00:17:44 -0400660std::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
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
Dominic Hamon403f3542013-12-18 16:55:45 -0800678} // end namespace
679
Eric27e0b432017-11-22 09:33:52 -0700680const CPUInfo& CPUInfo::Get() {
681 static const CPUInfo* info = new CPUInfo();
682 return *info;
Dominic Hamon403f3542013-12-18 16:55:45 -0800683}
684
Eric27e0b432017-11-22 09:33:52 -0700685CPUInfo::CPUInfo()
686 : num_cpus(GetNumCPUs()),
687 cycles_per_second(GetCPUCyclesPerSecond()),
688 caches(GetCacheSizes()),
Ori Livnehda9ec3d2018-07-09 00:17:44 -0400689 scaling_enabled(CpuScalingEnabled(num_cpus)),
690 load_avg(GetLoadAvg()) {}
Eric Fiseliera3308c62015-03-26 14:26:07 -0400691
Jatin Chaudhary47a5f772018-12-11 16:53:02 +0530692
693const SystemInfo& SystemInfo::Get() {
694 static const SystemInfo* info = new SystemInfo();
695 return *info;
696}
697
698SystemInfo::SystemInfo() : name(GetSystemName()) {}
Dominic Hamone390e4e2013-12-19 16:18:09 -0800699} // end namespace benchmark