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