C++ - Win32 시스템 정보 얻기(Code)

출처
win32_systemInfo.h

#pragma once

#include <cstdint>
#include <vector>
#include <string>
#include <memory>

#include <rapid/utils/singleton.h>

namespace rapid {

namespace platform {

struct ProcessorInformation {
    ProcessorInformation()
        : numaNodeCount(0)
        , processorCoreCount(0)
        , logicalProcessorCount(0)
        , processorL1CacheCount(0)
        , processorL2CacheCount(0)
        , processorL3CacheCount(0)
        , processorPackageCount(0) {
    }

    friend std::ostream& operator<< (std::ostream& ostr, ProcessorInformation const &info);

    uint32_t numaNodeCount;
    uint32_t processorCoreCount;
    uint32_t logicalProcessorCount;
    uint32_t processorL1CacheCount;
    uint32_t processorL2CacheCount;
    uint32_t processorL3CacheCount;
    uint32_t processorPackageCount;
};

struct NumaProcessor {
    NumaProcessor()
        : processor(0)
        , node(0)
        , processorMask(0) {
    }

    friend std::ostream& operator<< (std::ostream& ostr, std::vector<NumaProcessor> const &numaProcessors);

    uint8_t processor;
    uint8_t node;
    uint64_t processorMask;
};

class SystemInfo : public utils::Singleton<SystemInfo> {
public:
    SystemInfo();

    ~SystemInfo() noexcept;

    uint32_t getNumberOfProcessors(uint32_t perCPU) const noexcept;

    uint32_t getPageSize() const noexcept;

    uint32_t getPageBoundarySize() const noexcept;

    uint32_t roundUpToPageSize(uint32_t size) const noexcept;

    bool isNumaSystem();

    uint64_t getNumaNodeProcessorMask(uint8_t node);

    std::vector<NumaProcessor> getNumaProcessorInformation();

    ProcessorInformation getProcessorInformation();

private:
    class SystemInfoImpl;
    std::unique_ptr<SystemInfoImpl> pImpl_;
};

bool startupWinSocket();

std::wstring getApplicationFilePath();

std::wstring getApplicationFileName();

void setProcessPriorityBoost(bool enableBoost);

void prefetchVirtualMemory(char const * virtualAddress, size_t size);

std::vector<std::string> getNetworkInterfaceNameList();

}

}

win32_systemInfo.cpp

#include <cstdint>
#include <vector>
#include <fstream>
#include <bitset>

#include <rapid/platform/platform.h>

#include <tlhelp32.h>
#include <Objbase.h>
#include <Winsock2.h>
#include <Psapi.h>
#include <iphlpapi.h>

#pragma comment(lib, "iphlpapi.lib")

#include <rapid/exception.h>

#include <rapid/details/contracts.h>
#include <rapid/details/common.h>
#include <rapid/platform/utils.h>

namespace rapid {

namespace platform {

struct SocketInitializer {
    SocketInitializer() noexcept {
        auto retval = ::WSAStartup(MAKEWORD(2, 2), &wsadata_);
        if (retval != ERROR_SUCCESS) {
            lastError_ = ::WSAGetLastError();
        } else {
            lastError_ = ERROR_SUCCESS;
        }
    }

    ~SocketInitializer() noexcept {
        if (lastError_ == ERROR_SUCCESS) {
            ::WSACleanup();
        }
    }

    WSADATA    wsadata_;
    uint32_t lastError_;
};

class SystemInfo::SystemInfoImpl {
public:
    SystemInfoImpl();

    ~SystemInfoImpl() noexcept;

    uint32_t getNumberOfProcessors(uint32_t perCPU) const noexcept;

    uint32_t getPageSize() const noexcept;

    size_t getPageBoundarySize() const noexcept;

    uint32_t roundUpToPageSize(uint32_t size) const noexcept;

    bool isNumaSystem();

    uint64_t getNumaNodeProcessorMask(uint8_t node);

    std::vector<NumaProcessor> getNumaProcessorInformation();

    ProcessorInformation getProcessorInformation();

private:
    bool isNumaSystem_;
    SYSTEM_INFO info_;
};

SystemInfo::SystemInfoImpl::SystemInfoImpl()
    : isNumaSystem_(false) {
    ::GetSystemInfo(&info_);
}

SystemInfo::SystemInfoImpl::~SystemInfoImpl() noexcept {
}

uint32_t SystemInfo::SystemInfoImpl::getNumberOfProcessors(uint32_t perCPU) const noexcept {
    return perCPU * info_.dwNumberOfProcessors;
}

uint32_t SystemInfo::SystemInfoImpl::getPageSize() const noexcept {
    return info_.dwPageSize;
}

size_t SystemInfo::SystemInfoImpl::getPageBoundarySize() const noexcept {
    return info_.dwAllocationGranularity;
}

uint32_t SystemInfo::SystemInfoImpl::roundUpToPageSize(uint32_t size) const noexcept {
    return details::roundUp(size, getPageSize());
}

static inline uint32_t countSetBits(ULONG_PTR bitMask) {
    uint32_t LSHIFT = sizeof(ULONG_PTR) * 8 - 1;
    uint32_t bitSetCount = 0;
    auto bitTest = static_cast<ULONG_PTR>(1) << LSHIFT;
    uint32_t i;

    for (i = 0; i <= LSHIFT; ++i) {
        bitSetCount += ((bitMask & bitTest) ? 1 : 0);
        bitTest /= 2;
    }
    return bitSetCount;
}

std::ostream& operator << (std::ostream& ostr, ProcessorInformation const &info) {
    ostr << "Number of NUMA nodes: " << info.numaNodeCount << "\r\n"
        << "Number of physical processor packages: " << info.processorPackageCount << "\r\n"
        << "Number of processor cores: " << info.processorCoreCount << "\r\n"
        << "Number of logical processors: " << info.logicalProcessorCount << "\r\n"
        << "L1/L2/L3 caches: "
        << info.processorL1CacheCount << " MB"
        << "/" << info.processorL2CacheCount << " MB"
        << "/" << info.processorL3CacheCount << " MB";
    return ostr;
}

std::ostream & operator<<(std::ostream & ostr, std::vector<NumaProcessor> const & numaProcessors) {
    for (auto processor : numaProcessors) {
        ostr << "processor: " << (int)processor.processor << "\r\n"
            << "node: " << (int)processor.node << "\r\n"
            << "processorMask: " << std::bitset<64>(processor.processorMask).to_string() << "\r\n";
    }
    return ostr;
}

ProcessorInformation SystemInfo::SystemInfoImpl::getProcessorInformation() {
    ProcessorInformation info;

    DWORD returnLength = 0;
    uint32_t byteOffset = 0;
    PCACHE_DESCRIPTOR cache = nullptr;
    PSYSTEM_LOGICAL_PROCESSOR_INFORMATION pLogicalProcInfo = nullptr;

    std::vector<uint8_t> buffer;

    if (!::GetLogicalProcessorInformation(nullptr, &returnLength)) {
        auto lastError = ::GetLastError();

        if (lastError == ERROR_INSUFFICIENT_BUFFER) {
            buffer.resize(returnLength);
            pLogicalProcInfo = reinterpret_cast<PSYSTEM_LOGICAL_PROCESSOR_INFORMATION>(buffer.data());
            ::GetLogicalProcessorInformation(pLogicalProcInfo, &returnLength);
        } else {
            throw Exception(lastError);
        }
    }

    while (byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength) {
        switch (pLogicalProcInfo->Relationship) {
        case RelationNumaNode:
        info.numaNodeCount++;
        break;
        case RelationProcessorCore:
        info.processorCoreCount++;
        // A hyper-threaded core supplies more than one logical processor.
        info.logicalProcessorCount += countSetBits(pLogicalProcInfo->ProcessorMask);
        break;
        case RelationCache:
        cache = &pLogicalProcInfo->Cache;
        if (cache->Level == 1) {
            info.processorL1CacheCount++;
        } else if (cache->Level == 2) {
            info.processorL2CacheCount++;
        } else if (cache->Level == 3) {
            info.processorL3CacheCount++;
        }
        break;
        case RelationProcessorPackage:
        // Logical processors share a physical package.
        info.processorPackageCount++;
        break;
        case RelationGroup:
        break;
        case RelationAll:
        break;
        default:
        break;
        }

        byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
        pLogicalProcInfo++;
    }
    return info;
}

uint64_t SystemInfo::SystemInfoImpl::getNumaNodeProcessorMask(uint8_t node) {
    uint64_t processorMask = 0;
    if (!::GetNumaNodeProcessorMask(node, &processorMask)) {
        throw Exception();
    }
    return processorMask;
}

std::vector<NumaProcessor> SystemInfo::SystemInfoImpl::getNumaProcessorInformation() {
    std::vector<NumaProcessor> numaProcessorInfo;

    auto processorInfo = getProcessorInformation();
    for (uint32_t i = 0; i < processorInfo.logicalProcessorCount; ++i) {
        NumaProcessor numaProcessor;

        numaProcessor.processor = i;
        if (!::GetNumaProcessorNode(i, &numaProcessor.node)) {
            throw Exception();
        }

        if (!::GetNumaNodeProcessorMask(numaProcessor.node, &numaProcessor.processorMask)) {
            throw Exception();
        }
        numaProcessorInfo.push_back(numaProcessor);
    }
    return numaProcessorInfo;
}

bool SystemInfo::SystemInfoImpl::isNumaSystem() {
    ULONG highestNodeNumber = 0;
    if (!::GetNumaHighestNodeNumber(&highestNodeNumber)) {
        throw Exception();
    }
    return highestNodeNumber > 0;
}

SystemInfo::SystemInfo()
    : pImpl_(std::make_unique<SystemInfoImpl>()) {
}

SystemInfo::~SystemInfo() noexcept {
}

uint32_t SystemInfo::getPageSize() const noexcept {
    return pImpl_->getPageSize();
}

uint32_t SystemInfo::getPageBoundarySize() const noexcept {
    return pImpl_->getPageBoundarySize();
}

uint32_t SystemInfo::roundUpToPageSize(uint32_t size) const noexcept {
    return pImpl_->roundUpToPageSize(size);
}

uint32_t SystemInfo::getNumberOfProcessors(uint32_t perCPU) const noexcept {
    return pImpl_->getNumberOfProcessors(perCPU);
}

bool SystemInfo::isNumaSystem() {
    return pImpl_->isNumaSystem();
}

uint64_t SystemInfo::getNumaNodeProcessorMask(uint8_t node) {
    return pImpl_->getNumaNodeProcessorMask(node);
}

std::vector<NumaProcessor> SystemInfo::getNumaProcessorInformation() {
    return pImpl_->getNumaProcessorInformation();
}

ProcessorInformation SystemInfo::getProcessorInformation() {
    return pImpl_->getProcessorInformation();
}

bool startupWinSocket() {
    static const SocketInitializer socketIniter;
    return socketIniter.lastError_ == ERROR_SUCCESS;
}

std::wstring getApplicationFilePath() {
    wchar_t fileName[MAX_PATH] = { 0 };
    ::GetModuleFileNameW(nullptr, fileName, MAX_PATH - 1);
    return fileName;
}

std::wstring getApplicationFileName() {
    wchar_t drive[MAX_PATH];
    wchar_t dir[MAX_PATH];
    wchar_t filename[MAX_PATH];
    wchar_t ext[MAX_PATH];
    wchar_t exe_filename[MAX_PATH];

    auto filePath = getApplicationFilePath();
    ::_wsplitpath_s(filePath.c_str(),
        drive,
        MAX_PATH,
        dir,
        MAX_PATH,
        filename,
        MAX_PATH,
        ext,
        MAX_PATH);
    ::_wmakepath_s(exe_filename, MAX_PATH, nullptr, nullptr, filename, ext);
    return filename;
}

std::string getFinalPathNameByHandle(HANDLE fileHandle) {
    RAPID_ENSURE(fileHandle != INVALID_HANDLE_VALUE);
    char path[MAX_PATH] = { 0 };
    ::GetFinalPathNameByHandleA(fileHandle, path, MAX_PATH - 1, VOLUME_NAME_NT);
    return path;
}

void setProcessPriorityBoost(bool enableBoost) {
    if (!::SetProcessPriorityBoost(::GetCurrentProcess(), enableBoost)) {
        throw Exception();
    }
}

void prefetchVirtualMemory(char const * virtualAddress, size_t size) {
    // TODO: Windows 2008 not support!
    WIN32_MEMORY_RANGE_ENTRY entry;
    entry.VirtualAddress = const_cast<void*>(reinterpret_cast<const void*>(virtualAddress));
    entry.NumberOfBytes = size;

    if (!::PrefetchVirtualMemory(::GetCurrentProcess(), 1, &entry, 0)) {
        throw rapid::Exception();
    }
}

std::vector<std::string> getNetworkInterfaceNameList() {
    std::vector<std::string> interfaceList;
    std::vector<char> buffer;
    
    PIP_ADAPTER_INFO pInterfaceInfo = nullptr;
    DWORD interfaceInfoSize = 0;
    auto ret = ::GetAdaptersInfo(nullptr, &interfaceInfoSize);
    if (ret == ERROR_BUFFER_OVERFLOW) {
        buffer.resize(interfaceInfoSize);
        pInterfaceInfo = reinterpret_cast<PIP_ADAPTER_INFO>(buffer.data());
    } else {
        throw Exception();
    }

    ret = ::GetAdaptersInfo(pInterfaceInfo, &interfaceInfoSize);
    if (ret == NO_ERROR) {
        while (pInterfaceInfo) {
            interfaceList.emplace_back(pInterfaceInfo->Description);
            pInterfaceInfo = pInterfaceInfo->Next;
        }
    }
    return interfaceList;
}

}

}

이 글은 2019-06-03에 작성되었습니다.