llvm/third-party/benchmark/src/sysinfo.cc

// Copyright 2015 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#if defined(_MSC_VER)
// FIXME: This must be defined before any other includes to disable deprecation
// warnings for use of codecvt from C++17. We should remove our reliance on
// the deprecated functionality instead.
#define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING
#endif

#include "internal_macros.h"

#ifdef BENCHMARK_OS_WINDOWS
#if !defined(WINVER) || WINVER < 0x0600
#undef WINVER
#define WINVER
#endif  // WINVER handling
#include <shlwapi.h>
#undef StrCat  // Don't let StrCat in string_util.h be renamed to lstrcatA
#include <versionhelpers.h>
#include <windows.h>

#include <codecvt>
#else
#include <fcntl.h>
#if !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT)
#include <sys/resource.h>
#endif
#include <sys/time.h>
#include <sys/types.h>  // this header must be included before 'sys/sysctl.h' to avoid compilation error on FreeBSD
#include <unistd.h>
#if defined BENCHMARK_OS_FREEBSD || defined BENCHMARK_OS_MACOSX || \
    defined BENCHMARK_OS_NETBSD || defined BENCHMARK_OS_OPENBSD || \
    defined BENCHMARK_OS_DRAGONFLY
#define BENCHMARK_HAS_SYSCTL
#include <sys/sysctl.h>
#endif
#endif
#if defined(BENCHMARK_OS_SOLARIS)
#include <kstat.h>
#include <netdb.h>
#endif
#if defined(BENCHMARK_OS_QNX)
#include <sys/syspage.h>
#endif
#if defined(BENCHMARK_OS_QURT)
#include <qurt.h>
#endif
#if defined(BENCHMARK_HAS_PTHREAD_AFFINITY)
#include <pthread.h>
#endif

#include <algorithm>
#include <array>
#include <bitset>
#include <cerrno>
#include <climits>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <iterator>
#include <limits>
#include <locale>
#include <memory>
#include <random>
#include <sstream>
#include <utility>

#include "benchmark/benchmark.h"
#include "check.h"
#include "cycleclock.h"
#include "internal_macros.h"
#include "log.h"
#include "string_util.h"
#include "timers.h"

namespace benchmark {
namespace {

void PrintImp(std::ostream& out) {}

template <class First, class... Rest>
void PrintImp(std::ostream& out, First&& f, Rest&&... rest) {}

template <class... Args>
BENCHMARK_NORETURN void PrintErrorAndDie(Args&&... args) {}

#ifdef BENCHMARK_HAS_SYSCTL

/// ValueUnion - A type used to correctly alias the byte-for-byte output of
/// `sysctl` with the result type it's to be interpreted as.
struct ValueUnion {
  union DataT {
    int32_t int32_value;
    int64_t int64_value;
    // For correct aliasing of union members from bytes.
    char bytes[8];
  };
  using DataPtr = std::unique_ptr<DataT, decltype(&std::free)>;

  // The size of the data union member + its trailing array size.
  std::size_t size;
  DataPtr buff;

 public:
  ValueUnion() : size(0), buff(nullptr, &std::free) {}

  explicit ValueUnion(std::size_t buff_size)
      : size(sizeof(DataT) + buff_size),
        buff(::new (std::malloc(size)) DataT(), &std::free) {}

  ValueUnion(ValueUnion&& other) = default;

  explicit operator bool() const { return bool(buff); }

  char* data() const { return buff->bytes; }

  std::string GetAsString() const { return std::string(data()); }

  int64_t GetAsInteger() const {
    if (size == sizeof(buff->int32_value))
      return buff->int32_value;
    else if (size == sizeof(buff->int64_value))
      return buff->int64_value;
    BENCHMARK_UNREACHABLE();
  }

  template <class T, int N>
  std::array<T, N> GetAsArray() {
    const int arr_size = sizeof(T) * N;
    BM_CHECK_LE(arr_size, size);
    std::array<T, N> arr;
    std::memcpy(arr.data(), data(), arr_size);
    return arr;
  }
};

ValueUnion GetSysctlImp(std::string const& name) {
#if defined BENCHMARK_OS_OPENBSD
  int mib[2];

  mib[0] = CTL_HW;
  if ((name == "hw.ncpu") || (name == "hw.cpuspeed")) {
    ValueUnion buff(sizeof(int));

    if (name == "hw.ncpu") {
      mib[1] = HW_NCPU;
    } else {
      mib[1] = HW_CPUSPEED;
    }

    if (sysctl(mib, 2, buff.data(), &buff.size, nullptr, 0) == -1) {
      return ValueUnion();
    }
    return buff;
  }
  return ValueUnion();
#else
  std::size_t cur_buff_size = 0;
  if (sysctlbyname(name.c_str(), nullptr, &cur_buff_size, nullptr, 0) == -1)
    return ValueUnion();

  ValueUnion buff(cur_buff_size);
  if (sysctlbyname(name.c_str(), buff.data(), &buff.size, nullptr, 0) == 0)
    return buff;
  return ValueUnion();
#endif
}

BENCHMARK_MAYBE_UNUSED
bool GetSysctl(std::string const& name, std::string* out) {
  out->clear();
  auto buff = GetSysctlImp(name);
  if (!buff) return false;
  out->assign(buff.data());
  return true;
}

template <class Tp,
          class = typename std::enable_if<std::is_integral<Tp>::value>::type>
bool GetSysctl(std::string const& name, Tp* out) {
  *out = 0;
  auto buff = GetSysctlImp(name);
  if (!buff) return false;
  *out = static_cast<Tp>(buff.GetAsInteger());
  return true;
}

template <class Tp, size_t N>
bool GetSysctl(std::string const& name, std::array<Tp, N>* out) {
  auto buff = GetSysctlImp(name);
  if (!buff) return false;
  *out = buff.GetAsArray<Tp, N>();
  return true;
}
#endif

template <class ArgT>
bool ReadFromFile(std::string const& fname, ArgT* arg) {}

CPUInfo::Scaling CpuScaling(int num_cpus) {}

int CountSetBitsInCPUMap(std::string val) {}

BENCHMARK_MAYBE_UNUSED
std::vector<CPUInfo::CacheInfo> GetCacheSizesFromKVFS() {}

#ifdef BENCHMARK_OS_MACOSX
std::vector<CPUInfo::CacheInfo> GetCacheSizesMacOSX() {
  std::vector<CPUInfo::CacheInfo> res;
  std::array<int, 4> cache_counts{{0, 0, 0, 0}};
  GetSysctl("hw.cacheconfig", &cache_counts);

  struct {
    std::string name;
    std::string type;
    int level;
    int num_sharing;
  } cases[] = {{"hw.l1dcachesize", "Data", 1, cache_counts[1]},
               {"hw.l1icachesize", "Instruction", 1, cache_counts[1]},
               {"hw.l2cachesize", "Unified", 2, cache_counts[2]},
               {"hw.l3cachesize", "Unified", 3, cache_counts[3]}};
  for (auto& c : cases) {
    int val;
    if (!GetSysctl(c.name, &val)) continue;
    CPUInfo::CacheInfo info;
    info.type = c.type;
    info.level = c.level;
    info.size = val;
    info.num_sharing = c.num_sharing;
    res.push_back(std::move(info));
  }
  return res;
}
#elif defined(BENCHMARK_OS_WINDOWS)
std::vector<CPUInfo::CacheInfo> GetCacheSizesWindows() {
  std::vector<CPUInfo::CacheInfo> res;
  DWORD buffer_size = 0;
  using PInfo = SYSTEM_LOGICAL_PROCESSOR_INFORMATION;
  using CInfo = CACHE_DESCRIPTOR;

  using UPtr = std::unique_ptr<PInfo, decltype(&std::free)>;
  GetLogicalProcessorInformation(nullptr, &buffer_size);
  UPtr buff(static_cast<PInfo*>(std::malloc(buffer_size)), &std::free);
  if (!GetLogicalProcessorInformation(buff.get(), &buffer_size))
    PrintErrorAndDie("Failed during call to GetLogicalProcessorInformation: ",
                     GetLastError());

  PInfo* it = buff.get();
  PInfo* end = buff.get() + (buffer_size / sizeof(PInfo));

  for (; it != end; ++it) {
    if (it->Relationship != RelationCache) continue;
    using BitSet = std::bitset<sizeof(ULONG_PTR) * CHAR_BIT>;
    BitSet b(it->ProcessorMask);
    // To prevent duplicates, only consider caches where CPU 0 is specified
    if (!b.test(0)) continue;
    const CInfo& cache = it->Cache;
    CPUInfo::CacheInfo C;
    C.num_sharing = static_cast<int>(b.count());
    C.level = cache.Level;
    C.size = cache.Size;
    C.type = "Unknown";
    switch (cache.Type) {
      case CacheUnified:
        C.type = "Unified";
        break;
      case CacheInstruction:
        C.type = "Instruction";
        break;
      case CacheData:
        C.type = "Data";
        break;
      case CacheTrace:
        C.type = "Trace";
        break;
    }
    res.push_back(C);
  }
  return res;
}
#elif BENCHMARK_OS_QNX
std::vector<CPUInfo::CacheInfo> GetCacheSizesQNX() {
  std::vector<CPUInfo::CacheInfo> res;
  struct cacheattr_entry* cache = SYSPAGE_ENTRY(cacheattr);
  uint32_t const elsize = SYSPAGE_ELEMENT_SIZE(cacheattr);
  int num = SYSPAGE_ENTRY_SIZE(cacheattr) / elsize;
  for (int i = 0; i < num; ++i) {
    CPUInfo::CacheInfo info;
    switch (cache->flags) {
      case CACHE_FLAG_INSTR:
        info.type = "Instruction";
        info.level = 1;
        break;
      case CACHE_FLAG_DATA:
        info.type = "Data";
        info.level = 1;
        break;
      case CACHE_FLAG_UNIFIED:
        info.type = "Unified";
        info.level = 2;
        break;
      case CACHE_FLAG_SHARED:
        info.type = "Shared";
        info.level = 3;
        break;
      default:
        continue;
        break;
    }
    info.size = cache->line_size * cache->num_lines;
    info.num_sharing = 0;
    res.push_back(std::move(info));
    cache = SYSPAGE_ARRAY_ADJ_OFFSET(cacheattr, cache, elsize);
  }
  return res;
}
#endif

std::vector<CPUInfo::CacheInfo> GetCacheSizes() {}

std::string GetSystemName() {}

int GetNumCPUsImpl() {}

int GetNumCPUs() {}

class ThreadAffinityGuard final {};

double GetCPUCyclesPerSecond(CPUInfo::Scaling scaling) {}

std::vector<double> GetLoadAvg() {}

}  // end namespace

const CPUInfo& CPUInfo::Get() {}

CPUInfo::CPUInfo()
    :{}

const SystemInfo& SystemInfo::Get() {}

SystemInfo::SystemInfo() :{}
}  // end namespace benchmark