From 3487722d7a7acecd11a59906c03119131656c77e Mon Sep 17 00:00:00 2001 From: Max Qian <64824374+AstroAir@users.noreply.github.com> Date: Sun, 27 Oct 2024 14:11:40 +0800 Subject: [PATCH 1/6] feat: :sparkles: add stacktrace and system infomation collector --- CMakeLists.txt | 1 + src/CMakeLists.txt | 14 + src/Infrastructure/System/Cpu.cc | 567 ++++++++++++++++++++++++++++ src/Infrastructure/System/Cpu.h | 117 ++++++ src/Infrastructure/System/Crash.cc | 176 +++++++++ src/Infrastructure/System/Crash.h | 5 + src/Infrastructure/System/Memory.cc | 412 ++++++++++++++++++++ src/Infrastructure/System/Memory.h | 116 ++++++ src/Infrastructure/System/Os.cc | 199 ++++++++++ src/Infrastructure/System/Os.h | 35 ++ src/Infrastructure/System/Signal.cc | 128 +++++++ src/Infrastructure/System/Signal.h | 23 ++ src/Infrastructure/System/Wm.cc | 166 ++++++++ src/Infrastructure/System/Wm.h | 18 + src/main.cc | 12 + ui/modules/surrealism-ui | 1 + vcpkg.json | 1 + 17 files changed, 1991 insertions(+) create mode 100644 src/Infrastructure/System/Cpu.cc create mode 100644 src/Infrastructure/System/Cpu.h create mode 100644 src/Infrastructure/System/Crash.cc create mode 100644 src/Infrastructure/System/Crash.h create mode 100644 src/Infrastructure/System/Memory.cc create mode 100644 src/Infrastructure/System/Memory.h create mode 100644 src/Infrastructure/System/Os.cc create mode 100644 src/Infrastructure/System/Os.h create mode 100644 src/Infrastructure/System/Signal.cc create mode 100644 src/Infrastructure/System/Signal.h create mode 100644 src/Infrastructure/System/Wm.cc create mode 100644 src/Infrastructure/System/Wm.h create mode 160000 ui/modules/surrealism-ui diff --git a/CMakeLists.txt b/CMakeLists.txt index bd1386cf..da6711aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,6 +65,7 @@ endif (NOT Slint_FOUND) # find vcpkg-installed find_package(spdlog REQUIRED) find_package(Boost REQUIRED COMPONENTS system url filesystem) +find_package(cpptrace CONFIG REQUIRED) find_package(OpenSSL 3.3.0 REQUIRED) find_package(nlohmann_json REQUIRED) find_package(PkgConfig REQUIRED) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3ece096d..bff584c7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -64,6 +64,7 @@ target_link_libraries(${PROJECT_NAME} Boost::system Boost::filesystem Boost::url + cpptrace::cpptrace OpenSSL::Crypto OpenSSL::SSL nlohmann_json::nlohmann_json @@ -75,6 +76,19 @@ target_link_libraries(${PROJECT_NAME} ${URING_LIBRARY} ) +if(WIN32) + target_link_libraries(${PROJECT_NAME} PRIVATE pdh wbemuuid) +endif() + +if(CMAKE_BUILD_TYPE STREQUAL "Release") + if(MSVC) + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi /DEBUG") + else() + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -g") + endif() +endif() + + # On Windows, copy the Slint DLL next to the application binary so that it's found. if (WIN32) add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $ $ COMMAND_EXPAND_LISTS) diff --git a/src/Infrastructure/System/Cpu.cc b/src/Infrastructure/System/Cpu.cc new file mode 100644 index 00000000..a5bcf079 --- /dev/null +++ b/src/Infrastructure/System/Cpu.cc @@ -0,0 +1,567 @@ +#include "Cpu.h" +#include "Os.h" + +#include +#include +#include +#include + +#ifdef _WIN32 +// clang-format off +#include +#include +#include +#include +#include +#include +#include +#include +#include +// clang-format on +#elif __linux__ +#include +#include +#include +#include +#include +#include +#include +#include + +#elif __APPLE__ +#include +#include +#include +#include +#include +#include +#include +#endif + +#include + +auto getCurrentCpuUsage() -> float { + float cpuUsage = 0.0; + +#ifdef _WIN32 + PDH_HQUERY query; + PdhOpenQuery(nullptr, 0, &query); + + PDH_HCOUNTER counter; + PdhAddCounter(query, "\\Processor(_Total)\\% Processor Time", 0, &counter); + PdhCollectQueryData(query); + + PDH_FMT_COUNTERVALUE counterValue; + PdhGetFormattedCounterValue(counter, PDH_FMT_DOUBLE, nullptr, &counterValue); + + cpuUsage = static_cast(counterValue.doubleValue); + + PdhCloseQuery(query); +#elif __linux__ + std::ifstream file("/proc/stat"); + if (!file.is_open()) { + spdlog::error("GetCpuUsage error: open /proc/stat error"); + return cpuUsage; + } + std::string line; + std::getline(file, line); + + std::istringstream iss(line); + std::vector tokens(std::istream_iterator{iss}, + std::istream_iterator()); + + unsigned long totalTime = 0; + for (size_t i = 1; i < tokens.size(); i++) { + totalTime += std::stoul(tokens[i]); + } + + unsigned long idleTime = std::stoul(tokens[4]); + + float usage = static_cast(totalTime - idleTime) / totalTime; + cpuUsage = usage * 100.0; +#elif __APPLE__ + host_cpu_load_info_data_t cpu_load; + mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT; + if (host_statistics64(mach_host_self(), + HOST_CPU_LOAD_INFO, + reinterpret_cast(&cpu_load), + &count) + == KERN_SUCCESS) { + uint64_t user_time = cpu_load.cpu_ticks[CPU_STATE_USER] + - cpu_load.cpu_ticks[CPU_STATE_NICE]; + uint64_t sys_time = cpu_load.cpu_ticks[CPU_STATE_SYSTEM] + + cpu_load.cpu_ticks[CPU_STATE_NICE]; + uint64_t idle_time = cpu_load.cpu_ticks[CPU_STATE_IDLE]; + uint64_t total_time = user_time + sys_time + idle_time; + + cpu_usage = static_cast(user_time + sys_time) / total_time; + cpu_usage *= 100.0; + } else { + spdlog::error("GetCpuUsage error: host_statistics64 failed"); + } +#endif + + return cpuUsage; +} + +auto getCurrentCpuTemperature() -> float { + float temperature = 0.0F; + +#ifdef _WIN32 + HRESULT hres; + + // Initialize COM. + hres = CoInitializeEx(0, COINIT_MULTITHREADED); + if (FAILED(hres)) { + spdlog::error("Failed to initialize COM library. Error code = {}", hres); + return temperature; + } + + // Initialize security. + hres = CoInitializeSecurity( + NULL, + -1, + NULL, + NULL, + RPC_C_AUTHN_LEVEL_DEFAULT, + RPC_C_IMP_LEVEL_IMPERSONATE, + NULL, + EOAC_NONE, + NULL); + + if (FAILED(hres)) { + spdlog::error("Failed to initialize security. Error code = {}", hres); + CoUninitialize(); + return temperature; + } + + // Obtain the initial locator to WMI. + IWbemLocator *pLoc = NULL; + hres = CoCreateInstance( + CLSID_WbemLocator, + 0, + CLSCTX_INPROC_SERVER, + IID_IWbemLocator, (LPVOID *)&pLoc); + + if (FAILED(hres)) { + spdlog::error("Failed to create IWbemLocator object. Error code = {}", hres); + CoUninitialize(); + return temperature; + } + + IWbemServices *pSvc = NULL; + + // Connect to the root\cimv2 namespace with the current user. + hres = pLoc->ConnectServer( + _bstr_t(L"ROOT\\CIMV2"), + NULL, + NULL, + 0, + NULL, + 0, + 0, + &pSvc); + + if (FAILED(hres)) { + spdlog::error("Could not connect. Error code = {}", hres); + pLoc->Release(); + CoUninitialize(); + return temperature; + } + + // Set security levels on the proxy. + hres = CoSetProxyBlanket( + pSvc, + RPC_C_AUTHN_WINNT, + RPC_C_AUTHZ_NONE, + NULL, + RPC_C_AUTHN_LEVEL_CALL, + RPC_C_IMP_LEVEL_IMPERSONATE, + NULL, + EOAC_NONE); + + if (FAILED(hres)) { + spdlog::error("Could not set proxy blanket. Error code = {}", hres); + pSvc->Release(); + pLoc->Release(); + CoUninitialize(); + return temperature; + } + + // Use the IWbemServices pointer to make requests of WMI. + IEnumWbemClassObject *pEnumerator = NULL; + hres = pSvc->ExecQuery( + bstr_t("WQL"), + bstr_t("SELECT * FROM Win32_PerfFormattedData_Counters_ThermalZoneInformation"), + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + NULL, + &pEnumerator); + + if (FAILED(hres)) { + spdlog::error("Query for thermal data failed. Error code = {}", hres); + pSvc->Release(); + pLoc->Release(); + CoUninitialize(); + return temperature; + } + + IWbemClassObject *pclsObj = NULL; + ULONG uReturn = 0; + + while (pEnumerator) { + HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); + + if (0 == uReturn) { + break; + } + + VARIANT vtProp; + hr = pclsObj->Get(L"Temperature", 0, &vtProp, 0, 0); + if (SUCCEEDED(hr) && (vtProp.vt == VT_I4)) { + temperature = static_cast(vtProp.intVal) / 10.0F; // Adjust if necessary + } + VariantClear(&vtProp); + pclsObj->Release(); + } + + // Cleanup + pEnumerator->Release(); + pSvc->Release(); + pLoc->Release(); + CoUninitialize(); + +#elif defined(__APPLE__) + FILE* pipe = popen("sysctl -a | grep machdep.xcpm.cpu_thermal_level", "r"); + if (pipe != nullptr) { + char buffer[128]; + if (fgets(buffer, sizeof(buffer), pipe) != nullptr) { + std::string result(buffer); + size_t pos1 = result.find(": "); + size_t pos2 = result.find("\n"); + if (pos1 != std::string::npos && pos2 != std::string::npos) { + std::string tempStr = result.substr(pos1 + 2, pos2 - pos1 - 2); + try { + temperature = std::stof(tempStr); + } catch (const std::exception& e) { + spdlog::error("GetCpuTemperature error: {}", e.what()); + } + } + } else { + spdlog::error("GetCpuTemperature error: popen error"); + } + pclose(pipe); + } +#elif defined(__linux__) + if (isWsl()) { + spdlog::warn("GetCpuTemperature error: WSL not supported"); + } else { + std::ifstream tempFile("/sys/class/thermal/thermal_zone0/temp"); + if (tempFile.is_open()) { + int temp = 0; + tempFile >> temp; + tempFile.close(); + temperature = static_cast(temp) / 1000.0F; + } else { + spdlog::error( + "GetCpuTemperature error: open /sys/class/thermal/thermal_zone0/temp error"); + } + } +#elif defined(__ANDROID__) + std::ifstream tempFile("/sys/class/thermal/thermal_zone0/temp"); + if (tempFile.is_open()) { + int temp = 0; + tempFile >> temp; + tempFile.close(); + temperature = static_cast(temp) / 1000.0f; + } else { + spdlog::error("GetCpuTemperature error: open /sys/class/thermal/thermal_zone0/temp error"); + } +#endif + + return temperature; +} + +auto getCPUModel() -> std::string { + std::string cpuModel; +#ifdef _WIN32 + + HKEY hKey; + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, + R"(HARDWARE\DESCRIPTION\System\CentralProcessor\0)", + 0, + KEY_READ, + &hKey) + == ERROR_SUCCESS) { + char cpuName[1024]; + DWORD size = sizeof(cpuName); + if (RegQueryValueEx(hKey, "ProcessorNameString", nullptr, nullptr, (LPBYTE) cpuName, &size) + == ERROR_SUCCESS) { + cpuModel = cpuName; + } + RegCloseKey(hKey); + } + +#elif __linux__ + + std::ifstream cpuinfo("/proc/cpuinfo"); + std::string line; + while (std::getline(cpuinfo, line)) { + if (line.substr(0, 10) == "model name") { + cpuModel = line.substr(line.find(':') + 2); + break; + } + } + cpuinfo.close(); +#elif defined(__APPLE__) + FILE* pipe = popen("sysctl -n machdep.cpu.brand_string", "r"); + if (pipe != nullptr) { + char buffer[128]; + if (fgets(buffer, sizeof(buffer), pipe) != nullptr) { + cpuModel = buffer; + cpuModel.erase(std::remove(cpuModel.begin(), cpuModel.end(), '\n'), cpuModel.end()); + } else { + spdlog::error("GetCPUModel error: popen error"); + } + pclose(pipe); + } +#elif defined(__ANDROID__) + FILE* pipe = popen("getprop ro.product.model", "r"); + if (pipe != nullptr) { + char buffer[128]; + if (fgets(buffer, sizeof(buffer), pipe) != nullptr) { + cpuModel = buffer; + cpuModel.erase(std::remove(cpuModel.begin(), cpuModel.end(), '\n'), cpuModel.end()); + } else { + spdlog::error("GetCPUModel error: popen error"); + } + pclose(pipe); + } +#endif + return cpuModel; +} + +auto getProcessorIdentifier() -> std::string { + std::string identifier; + +#ifdef _WIN32 + HKEY hKey; + char identifierValue[256]; + DWORD bufSize = sizeof(identifierValue); + + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, + R"(HARDWARE\DESCRIPTION\System\CentralProcessor\0)", + 0, + KEY_READ, + &hKey) + == ERROR_SUCCESS) { + RegQueryValueEx(hKey, "Identifier", nullptr, nullptr, (LPBYTE) identifierValue, &bufSize); + RegCloseKey(hKey); + + identifier = identifierValue; + } +#elif defined(__linux__) + std::ifstream cpuinfo("/proc/cpuinfo"); + std::string line; + while (std::getline(cpuinfo, line)) { + if (line.substr(0, 9) == "processor") { + identifier = line.substr(line.find(':') + 2); + break; + } + } +#elif defined(__APPLE__) + FILE* pipe = popen("sysctl -n machdep.cpu.brand_string", "r"); + if (pipe != nullptr) { + char buffer[128]; + if (fgets(buffer, sizeof(buffer), pipe) != nullptr) { + identifier = buffer; + identifier.erase(std::remove(identifier.begin(), identifier.end(), '\n'), + identifier.end()); + } else { + spdlog::error("GetProcessorIdentifier error: popen error"); + } + pclose(pipe); + } +#endif + + return identifier; +} + +auto getProcessorFrequency() -> double { + double frequency = 0; + +#ifdef _WIN32 + HKEY hKey; + DWORD frequencyValue; + DWORD bufSize = sizeof(frequencyValue); + + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, + R"(HARDWARE\DESCRIPTION\System\CentralProcessor\0)", + 0, + KEY_READ, + &hKey) + == ERROR_SUCCESS) { + RegQueryValueEx(hKey, "~MHz", nullptr, nullptr, (LPBYTE) &frequencyValue, &bufSize); + RegCloseKey(hKey); + + frequency = static_cast(frequencyValue) / 1000.0; // Convert frequency to GHz + } +#elif defined(__linux__) + std::ifstream cpuinfo("/proc/cpuinfo"); + std::string line; + while (std::getline(cpuinfo, line)) { + if (line.substr(0, 7) == "cpu MHz") { + std::size_t pos = line.find(':') + 2; + frequency = std::stod(line.substr(pos)) / 1000.0; // Convert frequency to GHz + break; + } + } + cpuinfo.close(); +#elif defined(__APPLE__) + FILE* pipe = popen("sysctl -n hw.cpufrequency", "r"); + if (pipe != nullptr) { + char buffer[128]; + if (fgets(buffer, sizeof(buffer), pipe) != nullptr) { + frequency = std::stod(buffer) / 1e9; // Convert frequency to GHz + } else { + spdlog::error("GetProcessorFrequency error: popen error"); + } + pclose(pipe); + } +#endif + + return frequency; +} + +auto getNumberOfPhysicalPackages() -> int { + int numberOfPackages = 0; + +#ifdef _WIN32 + SYSTEM_INFO systemInfo; + GetSystemInfo(&systemInfo); + numberOfPackages = systemInfo.dwNumberOfProcessors; +#elif defined(__APPLE__) + FILE* pipe = popen("sysctl -n hw.packages", "r"); + if (pipe != nullptr) { + char buffer[128]; + if (fgets(buffer, sizeof(buffer), pipe) != nullptr) { + numberOfPackages = std::stoi(buffer); + } else { + spdlog::error("GetNumberOfPhysicalPackages error: popen error"); + } + pclose(pipe); + } +#elif defined(__linux__) + numberOfPackages = static_cast(sysconf(_SC_PHYS_PAGES)); +#endif + + return numberOfPackages; +} + +auto getNumberOfPhysicalCPUs() -> int { + int numberOfCPUs = 0; + +#ifdef _WIN32 + SYSTEM_INFO systemInfo; + GetSystemInfo(&systemInfo); + numberOfCPUs = systemInfo.dwNumberOfProcessors; +#elif defined(__APPLE__) + FILE* pipe = popen("sysctl -n hw.physicalcpu", "r"); + if (pipe != nullptr) { + char buffer[128]; + if (fgets(buffer, sizeof(buffer), pipe) != nullptr) { + numberOfCPUs = std::stoi(buffer); + } else { + spdlog::error("GetNumberOfPhysicalCPUs error: popen error"); + } + pclose(pipe); + } +#elif defined(__linux__) + std::ifstream cpuinfo("/proc/cpuinfo"); + std::string line; + while (std::getline(cpuinfo, line)) { + if (line.substr(0, 7) == "physical") { + numberOfCPUs = std::stoi(line.substr(line.find(':') + 2)); + break; + } + } +#endif + + return numberOfCPUs; +} + +auto getCacheSizes() -> CacheSizes { + CacheSizes cacheSizes{0, 0, 0, 0}; + +#ifdef _WIN32 + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX* info = nullptr; + DWORD bufferSize = 0; + + // Get required buffer size + GetLogicalProcessorInformationEx(RelationCache, nullptr, &bufferSize); + info = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*) malloc(bufferSize); + if (!info) + return cacheSizes; + + if (GetLogicalProcessorInformationEx(RelationCache, info, &bufferSize)) { + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX* current = info; + while ((char*) current < (char*) info + bufferSize) { + if (current->Relationship == RelationCache) { + switch (current->Cache.Type) { + case CacheUnified: + if (current->Cache.Level == 3) + cacheSizes.l3 = current->Cache.CacheSize / 1024; + break; + case CacheData: + if (current->Cache.Level == 1) + cacheSizes.l1d = current->Cache.CacheSize / 1024; + else if (current->Cache.Level == 2) + cacheSizes.l2 = current->Cache.CacheSize / 1024; + break; + case CacheInstruction: + if (current->Cache.Level == 1) + cacheSizes.l1i = current->Cache.CacheSize / 1024; + break; + default: + break; + } + } + current = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*) ((char*) current + current->Size); + } + } + free(info); + +#elif defined(__linux__) + std::vector cacheLevels = {"/sys/devices/system/cpu/cpu0/cache/index1/size", + "/sys/devices/system/cpu/cpu0/cache/index2/size", + "/sys/devices/system/cpu/cpu0/cache/index3/size"}; + for (const auto& path : cacheLevels) { + std::ifstream file(path); + if (file) { + std::string sizeStr; + std::getline(file, sizeStr); + size_t size = std::stoul(sizeStr) * 1024; // Convert KB to bytes + if (path.find("index1") != std::string::npos) + cacheSizes.l1i = size / 1024; + else if (path.find("index2") != std::string::npos) + cacheSizes.l2 = size / 1024; + else if (path.find("index3") != std::string::npos) + cacheSizes.l3 = size / 1024; + } + } + +#elif defined(__APPLE__) + size_t l1i = 0, l1d = 0, l2 = 0, l3 = 0; + size_t length = sizeof(size_t); + + if (sysctlbyname("machdep.cpu.cache.l1i.size", &l1i, &length, nullptr, 0) == 0) + cacheSizes.l1i = l1i / 1024; + if (sysctlbyname("machdep.cpu.cache.l1d.size", &l1d, &length, nullptr, 0) == 0) + cacheSizes.l1d = l1d / 1024; + if (sysctlbyname("machdep.cpu.cache.l2.size", &l2, &length, nullptr, 0) == 0) + cacheSizes.l2 = l2 / 1024; + if (sysctlbyname("machdep.cpu.cache.l3.size", &l3, &length, nullptr, 0) == 0) + cacheSizes.l3 = l3 / 1024; +#endif + + return cacheSizes; +} diff --git a/src/Infrastructure/System/Cpu.h b/src/Infrastructure/System/Cpu.h new file mode 100644 index 00000000..2158a048 --- /dev/null +++ b/src/Infrastructure/System/Cpu.h @@ -0,0 +1,117 @@ +#pragma once + +#include + +/** + * @brief A structure to hold the sizes of the CPU caches. + * + * This structure contains the sizes of the L1 data cache, L1 instruction cache, + * L2 cache, and L3 cache of the CPU. These values are typically provided in + * bytes. The cache size information is important for performance tuning and + * understanding the CPU's capabilities. + */ +struct CacheSizes { + size_t l1d; ///< The size of the L1 data cache in bytes. + size_t l1i; ///< The size of the L1 instruction cache in bytes. + size_t l2; ///< The size of the L2 cache in bytes. + size_t l3; ///< The size of the L3 cache in bytes. +}; ///< Ensure the structure is aligned to a 32-byte boundary. + +/** + * @brief Retrieves the current CPU usage percentage. + * + * This function calculates and returns the current percentage of CPU usage. + * It typically measures how much time the CPU spends in active processing + * compared to idle time. The CPU usage percentage can be useful for + * monitoring system performance and detecting high load conditions. + * + * @return A float representing the current CPU usage as a percentage (0.0 to + * 100.0). + */ +[[nodiscard]] auto getCurrentCpuUsage() -> float; + +/** + * @brief Retrieves the current CPU temperature. + * + * This function returns the current temperature of the CPU in degrees Celsius. + * Monitoring the CPU temperature is important for preventing overheating and + * ensuring optimal performance. If the temperature is too high, it could + * indicate cooling issues or high load on the system. + * + * @return A float representing the CPU temperature in degrees Celsius. + */ +[[nodiscard]] auto getCurrentCpuTemperature() -> float; + +/** + * @brief Retrieves the CPU model name. + * + * This function returns a string that contains the model name of the CPU. + * The CPU model provides information about the manufacturer and specific model + * (e.g., "Intel Core i7-10700K", "AMD Ryzen 9 5900X"). This information can be + * useful for system diagnostics and performance evaluations. + * + * @return A string representing the CPU model name. + */ +[[nodiscard]] auto getCPUModel() -> std::string; + +/** + * @brief Retrieves the CPU identifier. + * + * This function returns a unique string identifier for the CPU. This identifier + * typically includes details about the CPU architecture, model, stepping, and + * other low-level characteristics. It can be useful for identifying specific + * processor versions in a system. + * + * @return A string representing the CPU identifier. + */ +[[nodiscard]] auto getProcessorIdentifier() -> std::string; + +/** + * @brief Retrieves the current CPU frequency. + * + * This function returns the current operating frequency of the CPU in GHz. + * The frequency may vary depending on system load, power settings, and + * the capabilities of the CPU (e.g., turbo boost, power-saving modes). + * Understanding the CPU frequency is important for performance tuning and + * optimizing application performance. + * + * @return A double representing the CPU frequency in gigahertz (GHz). + */ +[[nodiscard]] auto getProcessorFrequency() -> double; + +/** + * @brief Retrieves the number of physical CPU packages. + * + * This function returns the number of physical CPU packages (sockets) installed + * in the system. A system may have multiple CPU packages, especially in + * server configurations. Each physical package may contain multiple cores. + * + * @return An integer representing the number of physical CPU packages. + */ +[[nodiscard]] auto getNumberOfPhysicalPackages() -> int; + +/** + * @brief Retrieves the number of logical CPUs (cores). + * + * This function returns the number of logical CPUs (cores) available in the + * system. Logical CPUs include both physical cores and additional virtual cores + * created by technologies like Hyper-Threading (on Intel processors) or SMT + * (on AMD processors). This value represents the total number of logical + * processors that the operating system can use. + * + * @return An integer representing the total number of logical CPUs (cores). + */ +[[nodiscard]] auto getNumberOfPhysicalCPUs() -> int; + +/** + * @brief Retrieves the sizes of the CPU caches (L1, L2, L3). + * + * This function returns a `CacheSizes` structure that contains the sizes of + * the L1 data cache (L1D), L1 instruction cache (L1I), L2 cache, and L3 cache. + * Cache sizes play an important role in determining CPU performance, as + * larger caches can improve data locality and reduce memory latency. + * + * @return A `CacheSizes` structure containing the sizes of the L1, L2, and L3 + * caches in bytes. + */ +[[nodiscard]] auto getCacheSizes() -> CacheSizes; diff --git a/src/Infrastructure/System/Crash.cc b/src/Infrastructure/System/Crash.cc new file mode 100644 index 00000000..bfbf2695 --- /dev/null +++ b/src/Infrastructure/System/Crash.cc @@ -0,0 +1,176 @@ +#include "Crash.h" + +#include "Cpu.h" +#include "Memory.h" +#include "Os.h" +#include "Wm.h" + +#include +#include +#include +#include +#include +#include + +#ifndef _MSC_VER +#include +#endif + +#ifdef _WIN32 +// clang-format off +#include +#include +// clang-format on +#endif + +#include + +auto getSystemInfoStr() -> std::string { + std::stringstream sss; + + auto osInfo = getOperatingSystemInfo(); + sss << "System Information:\n"; + sss << "-------------------\n"; + sss << "Operating system: " << osInfo.osName << " " << osInfo.osVersion + << "\n"; + sss << "Architecture: " << osInfo.architecture << "\n"; + sss << "Kernel version: " << osInfo.kernelVersion << "\n"; + sss << "Computer name: " << osInfo.computerName << "\n"; + sss << "Compiler: " << osInfo.compiler << "\n"; + sss << "WSL: " << (isWsl() ? "Yes" : "No") << "\n\n"; + + sss << "CPU Information:\n"; + sss << "----------------\n"; + sss << "Usage: " << getCurrentCpuUsage() << "%\n"; + sss << "Temperature: " << getCurrentCpuTemperature() << " °C\n"; + sss << "Model: " << getCPUModel() << "\n"; + sss << "Identifier: " << getProcessorIdentifier() << "\n"; + sss << "Packages: " << getNumberOfPhysicalPackages() << "\n\n"; + sss << "Frequency: " << getProcessorFrequency() << " GHz\n"; + sss << "Cores: " << getNumberOfPhysicalCPUs() << "\n"; + auto cache = getCacheSizes(); + sss << "Cache sizes:\n"; + sss << " L1D: " << cache.l1d << " KB\n"; + sss << " L1I: " << cache.l1i << " KB\n"; + sss << " L2: " << cache.l2 << " KB\n"; + sss << " L3: " << cache.l3 << " KB\n\n"; + + + sss << "Memory Status:\n"; + sss << "--------------\n"; + sss << "Usage: " << getMemoryUsage() << "%\n"; + sss << "Total: " << getTotalMemorySize() << " MB\n"; + sss << "Free: " << getAvailableMemorySize() << " MB\n"; + sss << "Virtual memory max: " << getVirtualMemoryMax() << " MB\n"; + sss << "Virtual memory used: " << getVirtualMemoryUsed() << " MB\n"; + sss << "Swap memory total: " << getSwapMemoryTotal() << " MB\n"; + sss << "Swap memory used: " << getSwapMemoryUsed() << " MB\n"; + sss << "Committed memory: " << getCommittedMemory() << " MB\n"; + +#ifdef _WIN32 + sss << "Window Manager Information:\n"; + sss << "---------------------------\n"; + auto wmInfo = getSystemInfo(); + sss << "Desktop Environment: " << wmInfo.desktopEnvironment << "\n"; + sss << "Window Manager: " << wmInfo.windowManager << "\n"; + sss << "WM Theme: " << wmInfo.wmTheme << "\n"; + sss << "Icons: " << wmInfo.icons << "\n"; + sss << "Font: " << wmInfo.font << "\n"; + sss << "Cursor: " << wmInfo.cursor << "\n"; +#endif + return sss.str(); +} + +auto getChinaTimestampString() -> std::string { + auto now = std::chrono::system_clock::now(); + std::time_t nowC = std::chrono::system_clock::to_time_t(now); + std::tm localTime; + if (localtime_s(&localTime, &nowC) != 0) { + } + std::stringstream sss; + sss << std::put_time(&localTime, "%Y-%m-%d %H:%M:%S"); + return sss.str(); +} + +auto Environ() -> std::unordered_map { + std::unordered_map env; + for (char **envp = environ; *envp != nullptr; ++envp) { + std::string envStr(*envp); + size_t pos = envStr.find('='); + if (pos != std::string::npos) { + env[envStr.substr(0, pos)] = envStr.substr(pos + 1); + } + } + return env; +} + +void saveCrashLog(std::string_view error_msg) { + std::string systemInfo = getSystemInfoStr(); + std::string environmentInfo; + for (const auto &[key, value] : Environ()) { + environmentInfo += key + ": " + value + "\n"; + } + + std::stringstream sss; + sss << "Program crashed at: " << getChinaTimestampString() << "\n"; + sss << "Error message: " << error_msg << "\n\n"; + + sss << "==================== Stack Trace ====================\n"; + // TODO: Boost stacktrace could not be included in the project + // sss << boost::stacktrace::stacktrace() << "\n\n"; + sss << cpptrace::generate_trace() << "\n\n"; + cpptrace::generate_trace().print(); + + sss << "==================== System Information ====================\n"; + sss << systemInfo << "\n"; + + sss << "================= Environment Variables ===================\n"; + if (environmentInfo.empty()) { + sss << "Failed to get environment information.\n"; + } else { + sss << environmentInfo << "\n"; + } + + std::stringstream ssss; + auto now = std::chrono::system_clock::now(); + std::time_t nowC = std::chrono::system_clock::to_time_t(now); + std::tm localTime; + if (localtime_s(&localTime, &nowC) != 0) { + // Handle error + return; + } + ssss << "crash_report/crash_" << std::put_time(&localTime, "%Y%m%d_%H%M%S") + << ".log"; + std::filesystem::path dirPath("crash_report"); + if (!std::filesystem::exists(dirPath)) { + std::filesystem::create_directory(dirPath); + } + std::ofstream ofs(ssss.str()); + if (ofs.good()) { + ofs << sss.str(); + ofs.close(); + } + + // Create a dump file +#ifdef _WIN32 + std::stringstream wss; + wss << "crash_report/crash_" << std::put_time(&localTime, "%Y%m%d_%H%M%S") + << ".dmp"; + std::string dumpFile = wss.str(); + HANDLE hFile = + CreateFile(dumpFile.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + if (hFile == INVALID_HANDLE_VALUE) { + return; + } + MINIDUMP_EXCEPTION_INFORMATION mdei; + mdei.ThreadId = GetCurrentThreadId(); + EXCEPTION_POINTERS *pep = nullptr; + mdei.ExceptionPointers = pep; + mdei.ClientPointers = FALSE; + MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, + MiniDumpNormal, (pep != nullptr) ? &mdei : nullptr, + nullptr, nullptr); + CloseHandle(hFile); +#endif +} diff --git a/src/Infrastructure/System/Crash.h b/src/Infrastructure/System/Crash.h new file mode 100644 index 00000000..bb28aad9 --- /dev/null +++ b/src/Infrastructure/System/Crash.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +void saveCrashLog(std::string_view error_msg); \ No newline at end of file diff --git a/src/Infrastructure/System/Memory.cc b/src/Infrastructure/System/Memory.cc new file mode 100644 index 00000000..d26d61d7 --- /dev/null +++ b/src/Infrastructure/System/Memory.cc @@ -0,0 +1,412 @@ +#include "Memory.h" +#include +#include + +#ifdef _WIN32 +// clang-format off +#include +#include +#include +#include +#include +#include +#include +// clang-format on +#elif __linux__ +#include +#include +#include +#include +#include +#include +#include +#include +#elif __APPLE__ +#include +#include +#include +#include +#include +#include +#include +#endif + +#include + +auto getMemoryUsage() -> float { + float memoryUsage = 0.0; + +#ifdef _WIN32 + MEMORYSTATUSEX status; + status.dwLength = sizeof(status); + float totalMemory = 0.0f; + float availableMemory = 0.0f; + if (GlobalMemoryStatusEx(&status)) { + totalMemory = static_cast(status.ullTotalPhys / 1024 / 1024); + availableMemory = static_cast(status.ullAvailPhys / 1024 / 1024); + memoryUsage = (totalMemory - availableMemory) / totalMemory * 100.0; + } else { + spdlog::error("GetMemoryUsage error: GlobalMemoryStatusEx error"); + } +#elif __linux__ + std::ifstream file("/proc/meminfo"); + if (!file.is_open()) { + spdlog::error("GetMemoryUsage error: open /proc/meminfo error"); + } + std::string line; + + unsigned long totalMemory = 0; + unsigned long freeMemory = 0; + unsigned long bufferMemory = 0; + unsigned long cacheMemory = 0; + + while (std::getline(file, line)) { + std::istringstream iss(line); + std::string name; + unsigned long value; + + if (iss >> name >> value) { + if (name == "MemTotal:") { + totalMemory = value; + } else if (name == "MemFree:") { + freeMemory = value; + } else if (name == "Buffers:") { + bufferMemory = value; + } else if (name == "Cached:") { + cacheMemory = value; + } + } + } + + unsigned long usedMemory = totalMemory - freeMemory - bufferMemory - cacheMemory; + memoryUsage = static_cast(usedMemory) / totalMemory * 100.0; +#elif __APPLE__ + struct statfs stats; + statfs("/", &stats); + + unsigned long long total_space = stats.f_blocks * stats.f_bsize; + unsigned long long free_space = stats.f_bfree * stats.f_bsize; + + unsigned long long used_space = total_space - free_space; + memory_usage = static_cast(used_space) / total_space * 100.0; +#endif + + return memoryUsage; +} + +auto getTotalMemorySize() -> unsigned long long { + unsigned long long totalMemorySize = 0; + +#ifdef _WIN32 + MEMORYSTATUSEX status; + status.dwLength = sizeof(status); + GlobalMemoryStatusEx(&status); + totalMemorySize = status.ullTotalPhys; +#elif defined(__APPLE__) + FILE* pipe = popen("sysctl -n hw.memsize", "r"); + if (pipe != nullptr) { + char buffer[128]; + if (fgets(buffer, sizeof(buffer), pipe) != nullptr) { + totalMemorySize = std::stoull(buffer); + } else { + spdlog::error("GetTotalMemorySize error: popen error"); + } + pclose(pipe); + } +#elif defined(__linux__) + long pages = sysconf(_SC_PHYS_PAGES); + long pageSize = sysconf(_SC_PAGE_SIZE); + totalMemorySize = static_cast(pages) + * static_cast(pageSize); +#endif + + return totalMemorySize; +} + +auto getAvailableMemorySize() -> unsigned long long { + unsigned long long availableMemorySize = 0; + +#ifdef _WIN32 + MEMORYSTATUSEX status; + status.dwLength = sizeof(status); + GlobalMemoryStatusEx(&status); + availableMemorySize = status.ullAvailPhys; +#elif defined(__APPLE__) + FILE* pipe = popen("vm_stat | grep 'Pages free:' | awk '{print $3}'", "r"); + if (pipe != nullptr) { + char buffer[128]; + if (fgets(buffer, sizeof(buffer), pipe) != nullptr) { + availableMemorySize = std::stoull(buffer) * getpagesize(); + } else { + spdlog::error("GetAvailableMemorySize error: popen error"); + } + pclose(pipe); + } +#elif defined(__linux__) + std::ifstream meminfo("/proc/meminfo"); + if (!meminfo.is_open()) { + spdlog::error("GetAvailableMemorySize error: open /proc/meminfo error"); + return 1; // Return error code + } + + std::string line; + bool found = false; + + // Read the file line by line + while (std::getline(meminfo, line)) { + if (line.substr(0, 13) == "MemAvailable:") { + unsigned long long availableMemory; + // Parse the line + if (std::sscanf(line.c_str(), "MemAvailable: %llu kB", &availableMemory) == 1) { + availableMemorySize = availableMemory * 1024; // Convert from kB to bytes + found = true; + break; + } else { + spdlog::error("GetAvailableMemorySize error: parse error"); + return -1; + } + } + } + + meminfo.close(); + + if (!found) { + spdlog::error("GetAvailableMemorySize error: MemAvailable entry not " + "found in /proc/meminfo"); + return -1; // Return error code + } +#endif + return availableMemorySize; +} + +auto getPhysicalMemoryInfo() -> MemoryInfo::MemorySlot { + MemoryInfo::MemorySlot slot; + +#ifdef _WIN32 + MEMORYSTATUSEX memoryStatus; + memoryStatus.dwLength = sizeof(memoryStatus); + GlobalMemoryStatusEx(&memoryStatus); + + slot.capacity = std::to_string(memoryStatus.ullTotalPhys / (1024 * 1024)); // Convert bytes to MB +#elif defined(__APPLE__) + FILE* pipe = popen("sysctl hw.memsize | awk '{print $2}'", "r"); + if (pipe != nullptr) { + char buffer[128]; + if (fgets(buffer, sizeof(buffer), pipe) != nullptr) { + slot.capacity = std::string(buffer) / (1024 * 1024); + } else { + spdlog::error("GetPhysicalMemoryInfo error: popen error"); + } + pclose(pipe); + } +#elif defined(__linux__) + std::ifstream meminfo("/proc/meminfo"); + std::string line; + while (std::getline(meminfo, line)) { + if (line.substr(0, 10) == "MemTotal: ") { + std::istringstream iss(line); + std::vector tokens{std::istream_iterator{iss}, + std::istream_iterator{}}; + slot.capacity = tokens[1]; + break; + } + } +#endif + + return slot; +} + +auto getVirtualMemoryMax() -> unsigned long long { + unsigned long long virtualMemoryMax; + +#ifdef _WIN32 + MEMORYSTATUSEX memoryStatus; + memoryStatus.dwLength = sizeof(memoryStatus); + GlobalMemoryStatusEx(&memoryStatus); + virtualMemoryMax = memoryStatus.ullTotalVirtual / (1024 * 1024); +#elif defined(__APPLE__) + FILE* pipe = popen("sysctl vm.swapusage | awk '{print $2}'", "r"); + if (pipe != nullptr) { + char buffer[128]; + if (fgets(buffer, sizeof(buffer), pipe) != nullptr) { + virtualMemoryMax = std::stoull(buffer) / (1024 * 1024); + } else { + spdlog::error("GetVirtualMemoryMax error: popen error"); + } + pclose(pipe); + } +#elif defined(__linux__) + struct sysinfo si {}; + sysinfo(&si); + virtualMemoryMax = (si.totalram + si.totalswap) / 1024; +#endif + + return virtualMemoryMax; +} + +auto getVirtualMemoryUsed() -> unsigned long long { + unsigned long long virtualMemoryUsed; + +#ifdef _WIN32 + // Windows 实现 + MEMORYSTATUSEX memoryStatus; + memoryStatus.dwLength = sizeof(memoryStatus); + GlobalMemoryStatusEx(&memoryStatus); + virtualMemoryUsed = (memoryStatus.ullTotalVirtual - memoryStatus.ullAvailVirtual) + / (1024 * 1024); +#elif defined(__APPLE__) + FILE* pipe = popen("sysctl vm.swapusage | awk '{print $6}'", "r"); + if (pipe != nullptr) { + char buffer[128]; + if (fgets(buffer, sizeof(buffer), pipe) != nullptr) { + virtualMemoryUsed = std::stoull(buffer) / (1024 * 1024); + } else { + spdlog::error("GetVirtualMemoryUsed error: popen error"); + } + pclose(pipe); + } +#elif defined(__linux__) + struct sysinfo si {}; + sysinfo(&si); + virtualMemoryUsed = (si.totalram - si.freeram + si.totalswap - si.freeswap) / 1024; +#endif + + return virtualMemoryUsed; +} + +auto getSwapMemoryTotal() -> unsigned long long { + unsigned long long swapMemoryTotal = 0; + +#ifdef _WIN32 + MEMORYSTATUSEX memoryStatus; + memoryStatus.dwLength = sizeof(memoryStatus); + GlobalMemoryStatusEx(&memoryStatus); + swapMemoryTotal = memoryStatus.ullTotalPageFile / (1024 * 1024); +#elif defined(__APPLE__) + FILE* pipe = popen("sysctl vm.swapusage | awk '{print $2}'", "r"); + if (pipe != nullptr) { + char buffer[128]; + if (fgets(buffer, sizeof(buffer), pipe) != nullptr) { + swapMemoryTotal = std::stoull(buffer) / (1024 * 1024); + } else { + spdlog::error("GetSwapMemoryTotal error: popen error"); + } + pclose(pipe); + } +#elif defined(__linux__) + struct sysinfo si {}; + sysinfo(&si); + swapMemoryTotal = si.totalswap / 1024; +#endif + + return swapMemoryTotal; +} + +unsigned long long getSwapMemoryUsed() { + unsigned long long swapMemoryUsed = 0; + +#ifdef _WIN32 + MEMORYSTATUSEX memoryStatus; + memoryStatus.dwLength = sizeof(memoryStatus); + GlobalMemoryStatusEx(&memoryStatus); + swapMemoryUsed = (memoryStatus.ullTotalPageFile - memoryStatus.ullAvailPageFile) + / (1024 * 1024); +#elif defined(__APPLE__) + FILE* pipe = popen("sysctl vm.swapusage | awk '{print $6}'", "r"); + if (pipe != nullptr) { + char buffer[128]; + if (fgets(buffer, sizeof(buffer), pipe) != nullptr) { + swapMemoryUsed = std::stoull(buffer) / (1024 * 1024); + } else { + spdlog::error("GetSwapMemoryUsed error: popen error"); + } + pclose(pipe); + } +#elif defined(__linux__) + struct sysinfo si {}; + sysinfo(&si); + swapMemoryUsed = (si.totalswap - si.freeswap) / 1024; +#endif + + return swapMemoryUsed; +} + +auto getTotalMemory() -> size_t { +#ifdef _WIN32 + MEMORYSTATUSEX status; + status.dwLength = sizeof(status); + if (GlobalMemoryStatusEx(&status)) { + return status.ullTotalPhys; + } + return 0; +#elif defined(__linux__) + std::ifstream memInfoFile("/proc/meminfo"); + std::string line; + size_t totalMemory = 0; + while (std::getline(memInfoFile, line)) { + size_t value; + if (sscanf(line.c_str(), "MemTotal: %zu kB", &value) == 1) { + totalMemory = value * 1024; // Convert kB to bytes + break; + } + } + return totalMemory; +#elif defined(__APPLE__) + int mib[2]; + size_t length = sizeof(size_t); + size_t totalMemory; + + mib[0] = CTL_HW; + mib[1] = HW_MEMSIZE; + if (sysctl(mib, 2, &totalMemory, &length, nullptr, 0) == 0) { + return totalMemory; + } + return 0; +#endif +} + +auto getAvailableMemory() -> size_t { +#ifdef _WIN32 + MEMORYSTATUSEX status; + status.dwLength = sizeof(status); + if (GlobalMemoryStatusEx(&status)) { + return status.ullAvailPhys; + } + return 0; +#elif defined(__linux__) + std::ifstream memInfoFile("/proc/meminfo"); + std::string line; + size_t availableMemory = 0; + while (std::getline(memInfoFile, line)) { + size_t value; + if (sscanf(line.c_str(), "MemAvailable: %zu kB", &value) == 1) { + availableMemory = value * 1024; // Convert kB to bytes + break; + } + } + return availableMemory; +#elif defined(__APPLE__) + int mib[2]; + size_t length = sizeof(vm_statistics64); + struct vm_statistics64 vm_stats; + + mib[0] = CTL_VM; + mib[1] = VM_LOADAVG; + if (sysctl(mib, 2, &vm_stats, &length, nullptr, 0) == 0) { + return vm_stats.free_count * vm_page_size; + } + return 0; +#endif +} + +auto getCommittedMemory() -> size_t { + size_t totalMemory = getTotalMemory(); + size_t availableMemory = getAvailableMemory(); + return totalMemory - availableMemory; +} + +auto getUncommittedMemory() -> size_t { + size_t totalMemory = getTotalMemory(); + size_t committedMemory = getCommittedMemory(); + return totalMemory - committedMemory; +} diff --git a/src/Infrastructure/System/Memory.h b/src/Infrastructure/System/Memory.h new file mode 100644 index 00000000..778df013 --- /dev/null +++ b/src/Infrastructure/System/Memory.h @@ -0,0 +1,116 @@ +#pragma once + +#include +#include +#include + +struct MemoryInfo { + struct MemorySlot { + std::string capacity; + std::string clockSpeed; + std::string type; + + MemorySlot() = default; + MemorySlot(std::string capacity, std::string clockSpeed, + std::string type) + : capacity(std::move(capacity)), + clockSpeed(std::move(clockSpeed)), + type(std::move(type)) {} + }; + + std::vector slots; + unsigned long long virtualMemoryMax; + unsigned long long virtualMemoryUsed; + unsigned long long swapMemoryTotal; + unsigned long long swapMemoryUsed; +}; + +/** + * @brief Get the memory usage percentage. + * 获取内存使用率百分比 + * + * @return The memory usage percentage. + * 内存使用率百分比 + */ +auto getMemoryUsage() -> float; + +/** + * @brief Get the total memory size. + * 获取总内存大小 + * + * @return The total memory size. + * 总内存大小 + */ +auto getTotalMemorySize() -> unsigned long long; + +/** + * @brief Get the available memory size. + * 获取可用内存大小 + * + * @return The available memory size. + * 可用内存大小 + */ +auto getAvailableMemorySize() -> unsigned long long; + +/** + * @brief Get the physical memory slot info. + * 获取物理内存槽信息 + * + * @return The physical memory slot info. + * 物理内存槽信息 + */ +auto getPhysicalMemoryInfo() -> MemoryInfo::MemorySlot; + +/** + * @brief Get the virtual memory max size. + * 获取虚拟内存最大值 + * + * @return The virtual memory max size. + * 虚拟内存最大值 + */ +auto getVirtualMemoryMax() -> unsigned long long; + +/** + * @brief Get the virtual memory used size. + * 获取虚拟内存已用值 + * + * @return The virtual memory used size. + * 虚拟内存已用值 + */ +auto getVirtualMemoryUsed() -> unsigned long long; + +/** + * @brief Get the swap memory total size. + * 获取交换内存总值 + * + * @return The swap memory total size. + * 交换内存总值 + */ +auto getSwapMemoryTotal() -> unsigned long long; + +/** + * @brief Get the swap memory used size. + * 获取交换内存已用值 + * + * @return The swap memory used size. + * 交换内存已用值 + */ +auto getSwapMemoryUsed() -> unsigned long long; + +/** + * @brief Get the committed memory. + * 获取已分配内存 + * + * @return The committed memory. + * 已分配内存 + */ +auto getCommittedMemory() -> size_t; + +/** + * @brief Get the uncommitted memory. + * 获取未分配内存 + * + * @return The uncommitted memory. + * 未分配内存 + */ +auto getUncommittedMemory() -> size_t; diff --git a/src/Infrastructure/System/Os.cc b/src/Infrastructure/System/Os.cc new file mode 100644 index 00000000..4efdb161 --- /dev/null +++ b/src/Infrastructure/System/Os.cc @@ -0,0 +1,199 @@ +#include "Os.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#elif __linux__ +#include +#include +#elif __APPLE__ +#include +#endif + +#include + +auto OperatingSystemInfo::toJson() const -> std::string { + std::stringstream stringstream; + stringstream << "{\n"; + stringstream << R"( "osName": ")" << osName << "\",\n"; + stringstream << R"( "osVersion": ")" << osVersion << "\",\n"; + stringstream << R"( "kernelVersion": ")" << kernelVersion << "\"\n"; + stringstream << R"( "architecture": ")" << architecture << "\"\n"; + stringstream << "}\n"; + return stringstream.str(); +} + +auto getComputerName() -> std::optional { + constexpr size_t bufferSize = 256; + std::array buffer; + +#if defined(_WIN32) + auto size = static_cast(buffer.size()); + if (BOOL result = GetComputerNameA(buffer.data(), &size); result) { + return std::string(buffer.data()); + } +#elif defined(__APPLE__) + CFStringRef name = SCDynamicStoreCopyComputerName(NULL, NULL); + if (name != NULL) { + CFStringGetCString(name, buffer.data(), buffer.size(), + kCFStringEncodingUTF8); + CFRelease(name); + return std::string(buffer.data()); + } +#elif defined(__linux__) || defined(__linux) + if (gethostname(buffer.data(), buffer.size()) == 0) { + return std::string(buffer.data()); + } +#elif defined(__ANDROID__) + return std::nullopt; +#endif + + return std::nullopt; +} + +auto parseFile(const std::string& filePath) + -> std::pair { + std::ifstream file(filePath); + if (!file.is_open()) { + spdlog::error("Cannot open file: {}", filePath); + return {}; + } + + std::pair osInfo; + std::string line; + + while (std::getline(file, line)) { + if (line.empty() || line[0] == '#') { + continue; // Skip empty lines and comments + } + + size_t delimiterPos = line.find('='); + if (delimiterPos != std::string::npos) { + std::string key = line.substr(0, delimiterPos); + std::string value = line.substr(delimiterPos + 1); + + // Remove double quotes from the value + if (!value.empty() && value.front() == '"' && value.back() == '"') { + value = value.substr(1, value.size() - 2); + } + + if (key == "PRETTY_NAME") { + osInfo.first = value; + } else if (key == "VERSION") { + osInfo.second = value; + } + } + } + + return osInfo; +} + +auto getOperatingSystemInfo() -> OperatingSystemInfo { + OperatingSystemInfo osInfo; + +#ifdef _WIN32 + OSVERSIONINFOEX osvi; + ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + if (GetVersionEx((LPOSVERSIONINFO)&osvi) != 0) { + osInfo.osName = "Windows"; + osInfo.osVersion = std::format("{}.{} (Build {})", osvi.dwMajorVersion, + osvi.dwMinorVersion, osvi.dwBuildNumber); + } else { + spdlog::error("Failed to get OS version"); + } +#elif __linux__ + auto osReleaseInfo = parseFile("/etc/os-release"); + if (!osReleaseInfo.first.empty()) { + osInfo.osName = osReleaseInfo.first; + osInfo.osVersion = osReleaseInfo.second; + } else { + auto lsbReleaseInfo = parseFile("/etc/lsb-release"); + if (!lsbReleaseInfo.first.empty()) { + osInfo.osName = lsbReleaseInfo.first; + osInfo.osVersion = lsbReleaseInfo.second; + } else { + std::ifstream redhatReleaseFile("/etc/redhat-release"); + if (redhatReleaseFile.is_open()) { + std::string line; + std::getline(redhatReleaseFile, line); + osInfo.osName = line; + redhatReleaseFile.close(); + } + } + } + + if (osInfo.osName.empty()) { + spdlog::error("Failed to get OS name"); + } + + std::ifstream kernelVersionFile("/proc/version"); + if (kernelVersionFile.is_open()) { + std::string line; + std::getline(kernelVersionFile, line); + osInfo.kernelVersion = line.substr(0, line.find(" ")); + kernelVersionFile.close(); + } else { + spdlog::error("Failed to open /proc/version"); + } +#elif __APPLE__ + struct utsname info; + if (uname(&info) == 0) { + osInfo.osName = info.sysname; + osInfo.osVersion = info.release; + osInfo.kernelVersion = info.version; + } +#endif + +#if defined(__i386__) || defined(__i386) + const std::string ARCHITECTURE = "x86"; +#elif defined(__x86_64__) + const std::string ARCHITECTURE = "x86_64"; +#elif defined(__arm__) + const std::string ARCHITECTURE = "ARM"; +#elif defined(__aarch64__) + const std::string ARCHITECTURE = "ARM64"; +#else + const std::string ARCHITECTURE = "Unknown architecture"; +#endif + osInfo.architecture = ARCHITECTURE; + + const std::string COMPILER = +#if defined(__clang__) + std::format("Clang {}.{}.{}", __clang_major__, __clang_minor__, + __clang_patchlevel__); +#elif defined(__GNUC__) + std::format("GCC {}.{}.{}", __GNUC__, __GNUC_MINOR__, + __GNUC_PATCHLEVEL__); +#elif defined(_MSC_VER) + std::format("MSVC {}", _MSC_FULL_VER); +#else + "Unknown compiler"; +#endif + osInfo.compiler = COMPILER; + + osInfo.computerName = getComputerName().value_or("Unknown computer name"); + + return osInfo; +} + +auto isWsl() -> bool { + std::ifstream procVersion("/proc/version"); + std::string line; + if (procVersion.is_open()) { + std::getline(procVersion, line); + procVersion.close(); + // Check if the line contains "Microsoft" which is a typical indicator + // of WSL + return line.find("microsoft") != std::string::npos || + line.find("WSL") != std::string::npos; + } + return false; +} diff --git a/src/Infrastructure/System/Os.h b/src/Infrastructure/System/Os.h new file mode 100644 index 00000000..e635f4ac --- /dev/null +++ b/src/Infrastructure/System/Os.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +/** + * @brief Represents information about the operating system. + */ +struct OperatingSystemInfo { + std::string osName; /**< The name of the operating system. */ + std::string osVersion; /**< The version of the operating system. */ + std::string kernelVersion; /**< The version of the kernel. */ + std::string architecture; /**< The architecture of the operating system. */ + std::string + compiler; /**< The compiler used to compile the operating system. */ + std::string computerName; /**< The name of the computer. */ + + OperatingSystemInfo() = default; + + std::string toJson() const; +}; + +/** + * @brief Retrieves the information about the operating system. + * @return The `OperatingSystemInfo` struct containing the operating system + * information. + */ +OperatingSystemInfo getOperatingSystemInfo(); + +/** + * @brief Checks if the operating system is running in a Windows Subsystem for + * Linux (WSL) environment. + * @return `true` if the operating system is running in a WSL environment, + * `false` otherwise. + */ +auto isWsl() -> bool; \ No newline at end of file diff --git a/src/Infrastructure/System/Signal.cc b/src/Infrastructure/System/Signal.cc new file mode 100644 index 00000000..567e70a5 --- /dev/null +++ b/src/Infrastructure/System/Signal.cc @@ -0,0 +1,128 @@ +#include "Signal.h" +#include +#include + +#ifdef _WIN32 +// clang-format off +#include +#include +// clang-format on +#pragma comment(lib, "dbghelp.lib") +#else +#include +#include +#endif + +SignalHandler& SignalHandler::getInstance() { + static SignalHandler instance; + return instance; +} + +void SignalHandler::registerHandler(int signal, Callback callback) { + handlers[signal] = std::move(callback); + std::signal(signal, SignalHandler::handleSignal); +} + +std::string SignalHandler::getStackTrace() { + std::string result; +#ifdef _WIN32 + HANDLE process = GetCurrentProcess(); + HANDLE thread = GetCurrentThread(); + + CONTEXT context; + memset(&context, 0, sizeof(CONTEXT)); + context.ContextFlags = CONTEXT_FULL; + RtlCaptureContext(&context); + + SymInitialize(process, NULL, TRUE); + + STACKFRAME64 stack; + memset(&stack, 0, sizeof(STACKFRAME64)); + stack.AddrPC.Offset = context.Rip; + stack.AddrPC.Mode = AddrModeFlat; + stack.AddrFrame.Offset = context.Rbp; + stack.AddrFrame.Mode = AddrModeFlat; + stack.AddrStack.Offset = context.Rsp; + stack.AddrStack.Mode = AddrModeFlat; + + for (ULONG frame = 0; ; frame++) { + BOOL more = StackWalk64( + IMAGE_FILE_MACHINE_AMD64, + process, + thread, + &stack, + &context, + NULL, + SymFunctionTableAccess64, + SymGetModuleBase64, + NULL + ); + + if (!more || stack.AddrPC.Offset == 0) { + break; + } + + char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; + PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer; + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = MAX_SYM_NAME; + + if (SymFromAddr(process, stack.AddrPC.Offset, NULL, symbol)) { + result += std::to_string(frame) + ": " + symbol->Name + "\n"; + } else { + result += std::to_string(frame) + ": Unknown\n"; + } + } + + SymCleanup(process); +#else + void* array[50]; + int size = backtrace(array, 50); + char** messages = backtrace_symbols(array, size); + + for (int i = 1; i < size && messages != NULL; ++i) { + char* mangled_name = 0, * offset_begin = 0, * offset_end = 0; + + for (char* p = messages[i]; *p; ++p) { + if (*p == '(') { + mangled_name = p; + } else if (*p == '+') { + offset_begin = p; + } else if (*p == ')') { + offset_end = p; + break; + } + } + + if (mangled_name && offset_begin && offset_end && mangled_name < offset_begin) { + *mangled_name++ = '\0'; + *offset_begin++ = '\0'; + *offset_end++ = '\0'; + + int status; + char* demangled_name = abi::__cxa_demangle(mangled_name, 0, 0, &status); + if (status == 0) { + result += std::string(messages[i]) + ": " + demangled_name + "+" + offset_begin + offset_end + "\n"; + free(demangled_name); + } else { + result += std::string(messages[i]) + ": " + mangled_name + "+" + offset_begin + offset_end + "\n"; + } + } else { + result += std::string(messages[i]) + "\n"; + } + } + free(messages); +#endif + return result; +} + +SignalHandler::SignalHandler() = default; + +void SignalHandler::handleSignal(int signal) { + auto& instance = getInstance(); + if (auto it = instance.handlers.find(signal); it != instance.handlers.end()) { + it->second(signal); + } + std::cout << "Stack trace:\n" << getStackTrace() << std::endl; + std::exit(signal); +} \ No newline at end of file diff --git a/src/Infrastructure/System/Signal.h b/src/Infrastructure/System/Signal.h new file mode 100644 index 00000000..227219a2 --- /dev/null +++ b/src/Infrastructure/System/Signal.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include +#include + +class SignalHandler { +public: + using Callback = std::function; + + static SignalHandler& getInstance(); + + void registerHandler(int signal, Callback callback); + + static std::string getStackTrace(); + +private: + SignalHandler(); + std::unordered_map handlers; + + static void handleSignal(int signal); +}; diff --git a/src/Infrastructure/System/Wm.cc b/src/Infrastructure/System/Wm.cc new file mode 100644 index 00000000..550cb350 --- /dev/null +++ b/src/Infrastructure/System/Wm.cc @@ -0,0 +1,166 @@ +#include "Wm.h" + +#include + +#ifdef _WIN32 +// clang-format off +#include +#include // For Desktop Window Manager (DWM) +// clang-format on +#if _MSC_VER +#pragma comment(lib, "dwmapi.lib") +#endif +#elif __linux__ +#include // For Linux system commands +#endif + +auto getSystemInfo() -> SystemInfo { + SystemInfo info; + +#ifdef _WIN32 + // Windows: Get desktop environment, window manager, theme, icons, font, + // cursor, etc. + + info.desktopEnvironment = "Fluent"; // Windows Fluent Design + + // Get the status of the window manager (DWM) + BOOL isDWMEnabled = FALSE; + HRESULT resultDWM = DwmIsCompositionEnabled(&isDWMEnabled); + if (SUCCEEDED(resultDWM) && (isDWMEnabled != 0)) { + info.windowManager = "Desktop Window Manager (DWM)"; + } else { + info.windowManager = "Unknown WM"; + } + + // Get theme information (Light/Dark Mode) + DWORD appsUseLightTheme = 1; + DWORD systemUsesLightTheme = 1; + HKEY hKey; + if (RegOpenKeyExW(HKEY_CURRENT_USER, + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\P" + L"ersonalize", + 0, KEY_READ, &hKey) == ERROR_SUCCESS) { + DWORD dataSize = sizeof(DWORD); + RegQueryValueExW(hKey, L"AppsUseLightTheme", nullptr, nullptr, + reinterpret_cast(&appsUseLightTheme), + &dataSize); + RegQueryValueExW(hKey, L"SystemUsesLightTheme", nullptr, nullptr, + reinterpret_cast(&systemUsesLightTheme), + &dataSize); + RegCloseKey(hKey); + } + info.wmTheme = + "Oem - Blue (System: " + + std::string(systemUsesLightTheme ? "Light" : "Dark") + + ", Apps: " + std::string(appsUseLightTheme ? "Light" : "Dark") + ")"; + + // Icon information (Recycle Bin) + info.icons = "Recycle Bin"; + + // Get font information + NONCLIENTMETRICS metrics = {}; + metrics.cbSize = sizeof(NONCLIENTMETRICS); + metrics.iBorderWidth = 0; + metrics.iScrollWidth = 0; + metrics.iScrollHeight = 0; + metrics.iCaptionWidth = 0; + metrics.iCaptionHeight = 0; + metrics.lfCaptionFont = {}; + metrics.iSmCaptionWidth = 0; + metrics.iSmCaptionHeight = 0; + metrics.lfSmCaptionFont = {}; + metrics.iMenuWidth = 0; + metrics.iMenuHeight = 0; + metrics.lfMenuFont = {}; + metrics.lfStatusFont = {}; + metrics.lfMessageFont = {}; + metrics.iPaddedBorderWidth = 0; + metrics.cbSize = sizeof(NONCLIENTMETRICS); + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(metrics), &metrics, 0); + info.font = std::format("{} ({}pt)", metrics.lfMessageFont.lfFaceName, + metrics.lfMessageFont.lfHeight); + + // Get cursor information + info.cursor = "Windows Default (32px)"; + +#elif __linux__ + // Linux: Get desktop environment, window manager, theme, icons, font, + // cursor, etc. + + // Get desktop environment (DE) + const char* de = std::getenv("XDG_CURRENT_DESKTOP"); + if (de == nullptr) { + info.desktopEnvironment = "Unknown"; + } else { + info.desktopEnvironment = de; + } + + // Get window manager (WM) + char buffer[128]; + std::string result = ""; + FILE* pipe = popen("wmctrl -m | grep 'Name' | awk '{print $2}'", "r"); + if (pipe) { + while (fgets(buffer, sizeof(buffer), pipe) != NULL) { + result += buffer; + } + pclose(pipe); + info.windowManager = result; + } else { + info.windowManager = "Unknown WM"; + } + + // Get window manager theme + pipe = popen("gsettings get org.gnome.desktop.interface gtk-theme", "r"); + result = ""; + if (pipe) { + while (fgets(buffer, sizeof(buffer), pipe) != NULL) { + result += buffer; + } + pclose(pipe); + info.wmTheme = result; + } else { + info.wmTheme = "Unknown Theme"; + } + + // Icons (Recycle Bin) + info.icons = "Recycle Bin"; // Getting icons on Linux is complex, hardcoded + // as an example + + // Get font information + pipe = popen("gsettings get org.gnome.desktop.interface font-name", "r"); + result = ""; + if (pipe) { + while (fgets(buffer, sizeof(buffer), pipe) != NULL) { + result += buffer; + } + pclose(pipe); + info.font = result; + } else { + info.font = "Unknown Font"; + } + + // Get cursor information + pipe = popen("gsettings get org.gnome.desktop.interface cursor-theme", "r"); + result = ""; + if (pipe) { + while (fgets(buffer, sizeof(buffer), pipe) != NULL) { + result += buffer; + } + pclose(pipe); + info.cursor = result; + } else { + info.cursor = "Unknown Cursor"; + } + +#else + // Unsupported platform + info.desktopEnvironment = "Unsupported Platform"; + info.windowManager = "Unsupported Platform"; + info.wmTheme = "Unsupported Platform"; + info.icons = "Unsupported Platform"; + info.font = "Unsupported Platform"; + info.cursor = "Unsupported Platform"; +#endif + + return info; +} \ No newline at end of file diff --git a/src/Infrastructure/System/Wm.h b/src/Infrastructure/System/Wm.h new file mode 100644 index 00000000..a18daea2 --- /dev/null +++ b/src/Infrastructure/System/Wm.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +struct SystemInfo { + std::string desktopEnvironment; // DE: Fluent + std::string windowManager; // WM: Desktop Window Manager + std::string wmTheme; // WM Theme: Oem - Blue (System: Light, Apps: Light) + std::string icons; // Icons: Recycle Bin + std::string font; // Font: Microsoft YaHei UI (12pt) + std::string cursor; // Cursor: Windows Default (32px) +}; + +/** + * @brief Retrieves the information about the window manager. + * @return The `SystemInfo` struct containing the window manager information. + */ +[[nodiscard]] auto getSystemInfo() -> SystemInfo; diff --git a/src/main.cc b/src/main.cc index 71f083c7..37b1aca5 100644 --- a/src/main.cc +++ b/src/main.cc @@ -10,7 +10,19 @@ #include #endif +#include "Infrastructure/System/Crash.h" +#include "Infrastructure/System/Signal.h" + +void signalCallback(int signal) { + // Here we can save crash log + std::string msg = "Signal " + std::to_string(signal) + " received."; + saveCrashLog(msg); +} + int main(int argc, char** argv) { + // Here we register signal handler + SignalHandler::getInstance().registerHandler(SIGABRT, signalCallback); + SignalHandler::getInstance().registerHandler(SIGSEGV, signalCallback); Logger logger( #ifdef EVENTO_DEBUG Logger::Level::debug, diff --git a/ui/modules/surrealism-ui b/ui/modules/surrealism-ui new file mode 160000 index 00000000..cb11d5d8 --- /dev/null +++ b/ui/modules/surrealism-ui @@ -0,0 +1 @@ +Subproject commit cb11d5d81752d712630de59418ee73e2ae362f29 diff --git a/vcpkg.json b/vcpkg.json index 6b74d955..ac99b995 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -6,6 +6,7 @@ "boost-dll", "boost-url", "boost-process", + "cpptrace", "openssl", "nlohmann-json", "spdlog", From 369e68ce6eab051ae4c62da9413527320dd6a704 Mon Sep 17 00:00:00 2001 From: Max Qian <64824374+AstroAir@users.noreply.github.com> Date: Sun, 27 Oct 2024 14:39:02 +0800 Subject: [PATCH 2/6] refactor: clang-format --- src/Infrastructure/System/Cpu.h | 10 ++++---- src/Infrastructure/System/Crash.cc | 39 ++++++++++++++++------------- src/Infrastructure/System/Memory.h | 9 +++---- src/Infrastructure/System/Os.cc | 28 +++++++++------------ src/Infrastructure/System/Os.h | 5 ++-- src/Infrastructure/System/Signal.cc | 35 +++++++++++++------------- src/Infrastructure/System/Wm.cc | 34 +++++++++++++++---------- 7 files changed, 83 insertions(+), 77 deletions(-) diff --git a/src/Infrastructure/System/Cpu.h b/src/Infrastructure/System/Cpu.h index 2158a048..0e6659fc 100644 --- a/src/Infrastructure/System/Cpu.h +++ b/src/Infrastructure/System/Cpu.h @@ -11,11 +11,11 @@ * understanding the CPU's capabilities. */ struct CacheSizes { - size_t l1d; ///< The size of the L1 data cache in bytes. - size_t l1i; ///< The size of the L1 instruction cache in bytes. - size_t l2; ///< The size of the L2 cache in bytes. - size_t l3; ///< The size of the L3 cache in bytes. -}; ///< Ensure the structure is aligned to a 32-byte boundary. + size_t l1d; ///< The size of the L1 data cache in bytes. + size_t l1i; ///< The size of the L1 instruction cache in bytes. + size_t l2; ///< The size of the L2 cache in bytes. + size_t l3; ///< The size of the L3 cache in bytes. +}; ///< Ensure the structure is aligned to a 32-byte boundary. /** * @brief Retrieves the current CPU usage percentage. diff --git a/src/Infrastructure/System/Crash.cc b/src/Infrastructure/System/Crash.cc index bfbf2695..82f1c94d 100644 --- a/src/Infrastructure/System/Crash.cc +++ b/src/Infrastructure/System/Crash.cc @@ -1,10 +1,8 @@ #include "Crash.h" - #include "Cpu.h" #include "Memory.h" #include "Os.h" #include "Wm.h" - #include #include #include @@ -12,6 +10,7 @@ #include #include + #ifndef _MSC_VER #include #endif @@ -31,8 +30,7 @@ auto getSystemInfoStr() -> std::string { auto osInfo = getOperatingSystemInfo(); sss << "System Information:\n"; sss << "-------------------\n"; - sss << "Operating system: " << osInfo.osName << " " << osInfo.osVersion - << "\n"; + sss << "Operating system: " << osInfo.osName << " " << osInfo.osVersion << "\n"; sss << "Architecture: " << osInfo.architecture << "\n"; sss << "Kernel version: " << osInfo.kernelVersion << "\n"; sss << "Computer name: " << osInfo.computerName << "\n"; @@ -54,7 +52,6 @@ auto getSystemInfoStr() -> std::string { sss << " L1I: " << cache.l1i << " KB\n"; sss << " L2: " << cache.l2 << " KB\n"; sss << " L3: " << cache.l3 << " KB\n\n"; - sss << "Memory Status:\n"; sss << "--------------\n"; @@ -94,7 +91,7 @@ auto getChinaTimestampString() -> std::string { auto Environ() -> std::unordered_map { std::unordered_map env; - for (char **envp = environ; *envp != nullptr; ++envp) { + for (char** envp = environ; *envp != nullptr; ++envp) { std::string envStr(*envp); size_t pos = envStr.find('='); if (pos != std::string::npos) { @@ -107,7 +104,7 @@ auto Environ() -> std::unordered_map { void saveCrashLog(std::string_view error_msg) { std::string systemInfo = getSystemInfoStr(); std::string environmentInfo; - for (const auto &[key, value] : Environ()) { + for (const auto& [key, value] : Environ()) { environmentInfo += key + ": " + value + "\n"; } @@ -139,8 +136,7 @@ void saveCrashLog(std::string_view error_msg) { // Handle error return; } - ssss << "crash_report/crash_" << std::put_time(&localTime, "%Y%m%d_%H%M%S") - << ".log"; + ssss << "crash_report/crash_" << std::put_time(&localTime, "%Y%m%d_%H%M%S") << ".log"; std::filesystem::path dirPath("crash_report"); if (!std::filesystem::exists(dirPath)) { std::filesystem::create_directory(dirPath); @@ -154,23 +150,30 @@ void saveCrashLog(std::string_view error_msg) { // Create a dump file #ifdef _WIN32 std::stringstream wss; - wss << "crash_report/crash_" << std::put_time(&localTime, "%Y%m%d_%H%M%S") - << ".dmp"; + wss << "crash_report/crash_" << std::put_time(&localTime, "%Y%m%d_%H%M%S") << ".dmp"; std::string dumpFile = wss.str(); - HANDLE hFile = - CreateFile(dumpFile.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, - CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + HANDLE hFile = CreateFile(dumpFile.c_str(), + GENERIC_READ | GENERIC_WRITE, + 0, + nullptr, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + nullptr); if (hFile == INVALID_HANDLE_VALUE) { return; } MINIDUMP_EXCEPTION_INFORMATION mdei; mdei.ThreadId = GetCurrentThreadId(); - EXCEPTION_POINTERS *pep = nullptr; + EXCEPTION_POINTERS* pep = nullptr; mdei.ExceptionPointers = pep; mdei.ClientPointers = FALSE; - MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, - MiniDumpNormal, (pep != nullptr) ? &mdei : nullptr, - nullptr, nullptr); + MiniDumpWriteDump(GetCurrentProcess(), + GetCurrentProcessId(), + hFile, + MiniDumpNormal, + (pep != nullptr) ? &mdei : nullptr, + nullptr, + nullptr); CloseHandle(hFile); #endif } diff --git a/src/Infrastructure/System/Memory.h b/src/Infrastructure/System/Memory.h index 778df013..6a9d66c0 100644 --- a/src/Infrastructure/System/Memory.h +++ b/src/Infrastructure/System/Memory.h @@ -11,11 +11,10 @@ struct MemoryInfo { std::string type; MemorySlot() = default; - MemorySlot(std::string capacity, std::string clockSpeed, - std::string type) - : capacity(std::move(capacity)), - clockSpeed(std::move(clockSpeed)), - type(std::move(type)) {} + MemorySlot(std::string capacity, std::string clockSpeed, std::string type) + : capacity(std::move(capacity)) + , clockSpeed(std::move(clockSpeed)) + , type(std::move(type)) {} }; std::vector slots; diff --git a/src/Infrastructure/System/Os.cc b/src/Infrastructure/System/Os.cc index 4efdb161..086ac78b 100644 --- a/src/Infrastructure/System/Os.cc +++ b/src/Infrastructure/System/Os.cc @@ -1,5 +1,4 @@ #include "Os.h" - #include #include #include @@ -11,8 +10,8 @@ #ifdef _WIN32 #include #elif __linux__ -#include #include +#include #elif __APPLE__ #include #endif @@ -42,8 +41,7 @@ auto getComputerName() -> std::optional { #elif defined(__APPLE__) CFStringRef name = SCDynamicStoreCopyComputerName(NULL, NULL); if (name != NULL) { - CFStringGetCString(name, buffer.data(), buffer.size(), - kCFStringEncodingUTF8); + CFStringGetCString(name, buffer.data(), buffer.size(), kCFStringEncodingUTF8); CFRelease(name); return std::string(buffer.data()); } @@ -58,8 +56,7 @@ auto getComputerName() -> std::optional { return std::nullopt; } -auto parseFile(const std::string& filePath) - -> std::pair { +auto parseFile(const std::string& filePath) -> std::pair { std::ifstream file(filePath); if (!file.is_open()) { spdlog::error("Cannot open file: {}", filePath); @@ -71,7 +68,7 @@ auto parseFile(const std::string& filePath) while (std::getline(file, line)) { if (line.empty() || line[0] == '#') { - continue; // Skip empty lines and comments + continue; // Skip empty lines and comments } size_t delimiterPos = line.find('='); @@ -102,10 +99,12 @@ auto getOperatingSystemInfo() -> OperatingSystemInfo { OSVERSIONINFOEX osvi; ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); - if (GetVersionEx((LPOSVERSIONINFO)&osvi) != 0) { + if (GetVersionEx((LPOSVERSIONINFO) &osvi) != 0) { osInfo.osName = "Windows"; - osInfo.osVersion = std::format("{}.{} (Build {})", osvi.dwMajorVersion, - osvi.dwMinorVersion, osvi.dwBuildNumber); + osInfo.osVersion = std::format("{}.{} (Build {})", + osvi.dwMajorVersion, + osvi.dwMinorVersion, + osvi.dwBuildNumber); } else { spdlog::error("Failed to get OS version"); } @@ -167,11 +166,9 @@ auto getOperatingSystemInfo() -> OperatingSystemInfo { const std::string COMPILER = #if defined(__clang__) - std::format("Clang {}.{}.{}", __clang_major__, __clang_minor__, - __clang_patchlevel__); + std::format("Clang {}.{}.{}", __clang_major__, __clang_minor__, __clang_patchlevel__); #elif defined(__GNUC__) - std::format("GCC {}.{}.{}", __GNUC__, __GNUC_MINOR__, - __GNUC_PATCHLEVEL__); + std::format("GCC {}.{}.{}", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); #elif defined(_MSC_VER) std::format("MSVC {}", _MSC_FULL_VER); #else @@ -192,8 +189,7 @@ auto isWsl() -> bool { procVersion.close(); // Check if the line contains "Microsoft" which is a typical indicator // of WSL - return line.find("microsoft") != std::string::npos || - line.find("WSL") != std::string::npos; + return line.find("microsoft") != std::string::npos || line.find("WSL") != std::string::npos; } return false; } diff --git a/src/Infrastructure/System/Os.h b/src/Infrastructure/System/Os.h index e635f4ac..64b3658f 100644 --- a/src/Infrastructure/System/Os.h +++ b/src/Infrastructure/System/Os.h @@ -10,9 +10,8 @@ struct OperatingSystemInfo { std::string osVersion; /**< The version of the operating system. */ std::string kernelVersion; /**< The version of the kernel. */ std::string architecture; /**< The architecture of the operating system. */ - std::string - compiler; /**< The compiler used to compile the operating system. */ - std::string computerName; /**< The name of the computer. */ + std::string compiler; /**< The compiler used to compile the operating system. */ + std::string computerName; /**< The name of the computer. */ OperatingSystemInfo() = default; diff --git a/src/Infrastructure/System/Signal.cc b/src/Infrastructure/System/Signal.cc index 567e70a5..5bb0c0db 100644 --- a/src/Infrastructure/System/Signal.cc +++ b/src/Infrastructure/System/Signal.cc @@ -9,8 +9,9 @@ // clang-format on #pragma comment(lib, "dbghelp.lib") #else -#include #include +#include + #endif SignalHandler& SignalHandler::getInstance() { @@ -45,25 +46,23 @@ std::string SignalHandler::getStackTrace() { stack.AddrStack.Offset = context.Rsp; stack.AddrStack.Mode = AddrModeFlat; - for (ULONG frame = 0; ; frame++) { - BOOL more = StackWalk64( - IMAGE_FILE_MACHINE_AMD64, - process, - thread, - &stack, - &context, - NULL, - SymFunctionTableAccess64, - SymGetModuleBase64, - NULL - ); + for (ULONG frame = 0;; frame++) { + BOOL more = StackWalk64(IMAGE_FILE_MACHINE_AMD64, + process, + thread, + &stack, + &context, + NULL, + SymFunctionTableAccess64, + SymGetModuleBase64, + NULL); if (!more || stack.AddrPC.Offset == 0) { break; } char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; - PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer; + PSYMBOL_INFO symbol = (PSYMBOL_INFO) buffer; symbol->SizeOfStruct = sizeof(SYMBOL_INFO); symbol->MaxNameLen = MAX_SYM_NAME; @@ -81,7 +80,7 @@ std::string SignalHandler::getStackTrace() { char** messages = backtrace_symbols(array, size); for (int i = 1; i < size && messages != NULL; ++i) { - char* mangled_name = 0, * offset_begin = 0, * offset_end = 0; + char *mangled_name = 0, *offset_begin = 0, *offset_end = 0; for (char* p = messages[i]; *p; ++p) { if (*p == '(') { @@ -102,10 +101,12 @@ std::string SignalHandler::getStackTrace() { int status; char* demangled_name = abi::__cxa_demangle(mangled_name, 0, 0, &status); if (status == 0) { - result += std::string(messages[i]) + ": " + demangled_name + "+" + offset_begin + offset_end + "\n"; + result += std::string(messages[i]) + ": " + demangled_name + "+" + offset_begin + + offset_end + "\n"; free(demangled_name); } else { - result += std::string(messages[i]) + ": " + mangled_name + "+" + offset_begin + offset_end + "\n"; + result += std::string(messages[i]) + ": " + mangled_name + "+" + offset_begin + + offset_end + "\n"; } } else { result += std::string(messages[i]) + "\n"; diff --git a/src/Infrastructure/System/Wm.cc b/src/Infrastructure/System/Wm.cc index 550cb350..e23eb05d 100644 --- a/src/Infrastructure/System/Wm.cc +++ b/src/Infrastructure/System/Wm.cc @@ -1,7 +1,7 @@ #include "Wm.h" - #include + #ifdef _WIN32 // clang-format off #include @@ -11,7 +11,7 @@ #pragma comment(lib, "dwmapi.lib") #endif #elif __linux__ -#include // For Linux system commands +#include // For Linux system commands #endif auto getSystemInfo() -> SystemInfo { @@ -21,7 +21,7 @@ auto getSystemInfo() -> SystemInfo { // Windows: Get desktop environment, window manager, theme, icons, font, // cursor, etc. - info.desktopEnvironment = "Fluent"; // Windows Fluent Design + info.desktopEnvironment = "Fluent"; // Windows Fluent Design // Get the status of the window manager (DWM) BOOL isDWMEnabled = FALSE; @@ -39,20 +39,27 @@ auto getSystemInfo() -> SystemInfo { if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\P" L"ersonalize", - 0, KEY_READ, &hKey) == ERROR_SUCCESS) { + 0, + KEY_READ, + &hKey) + == ERROR_SUCCESS) { DWORD dataSize = sizeof(DWORD); - RegQueryValueExW(hKey, L"AppsUseLightTheme", nullptr, nullptr, + RegQueryValueExW(hKey, + L"AppsUseLightTheme", + nullptr, + nullptr, reinterpret_cast(&appsUseLightTheme), &dataSize); - RegQueryValueExW(hKey, L"SystemUsesLightTheme", nullptr, nullptr, + RegQueryValueExW(hKey, + L"SystemUsesLightTheme", + nullptr, + nullptr, reinterpret_cast(&systemUsesLightTheme), &dataSize); RegCloseKey(hKey); } - info.wmTheme = - "Oem - Blue (System: " + - std::string(systemUsesLightTheme ? "Light" : "Dark") + - ", Apps: " + std::string(appsUseLightTheme ? "Light" : "Dark") + ")"; + info.wmTheme = "Oem - Blue (System: " + std::string(systemUsesLightTheme ? "Light" : "Dark") + + ", Apps: " + std::string(appsUseLightTheme ? "Light" : "Dark") + ")"; // Icon information (Recycle Bin) info.icons = "Recycle Bin"; @@ -77,7 +84,8 @@ auto getSystemInfo() -> SystemInfo { metrics.iPaddedBorderWidth = 0; metrics.cbSize = sizeof(NONCLIENTMETRICS); SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(metrics), &metrics, 0); - info.font = std::format("{} ({}pt)", metrics.lfMessageFont.lfFaceName, + info.font = std::format("{} ({}pt)", + metrics.lfMessageFont.lfFaceName, metrics.lfMessageFont.lfHeight); // Get cursor information @@ -123,8 +131,8 @@ auto getSystemInfo() -> SystemInfo { } // Icons (Recycle Bin) - info.icons = "Recycle Bin"; // Getting icons on Linux is complex, hardcoded - // as an example + info.icons = "Recycle Bin"; // Getting icons on Linux is complex, hardcoded + // as an example // Get font information pipe = popen("gsettings get org.gnome.desktop.interface font-name", "r"); From 70d9c9a24d248b723f16e0f2e65b8b63fc5855b7 Mon Sep 17 00:00:00 2001 From: Max Qian <64824374+AstroAir@users.noreply.github.com> Date: Sun, 27 Oct 2024 14:42:29 +0800 Subject: [PATCH 3/6] clang-format --- src/Infrastructure/System/Cpu.cc | 68 +++++++++++++----------------- src/Infrastructure/System/Crash.cc | 1 - 2 files changed, 29 insertions(+), 40 deletions(-) diff --git a/src/Infrastructure/System/Cpu.cc b/src/Infrastructure/System/Cpu.cc index a5bcf079..886a38b0 100644 --- a/src/Infrastructure/System/Cpu.cc +++ b/src/Infrastructure/System/Cpu.cc @@ -1,11 +1,11 @@ #include "Cpu.h" #include "Os.h" - #include #include #include #include + #ifdef _WIN32 // clang-format off #include @@ -118,16 +118,15 @@ auto getCurrentCpuTemperature() -> float { } // Initialize security. - hres = CoInitializeSecurity( - NULL, - -1, - NULL, - NULL, - RPC_C_AUTHN_LEVEL_DEFAULT, - RPC_C_IMP_LEVEL_IMPERSONATE, - NULL, - EOAC_NONE, - NULL); + hres = CoInitializeSecurity(NULL, + -1, + NULL, + NULL, + RPC_C_AUTHN_LEVEL_DEFAULT, + RPC_C_IMP_LEVEL_IMPERSONATE, + NULL, + EOAC_NONE, + NULL); if (FAILED(hres)) { spdlog::error("Failed to initialize security. Error code = {}", hres); @@ -136,12 +135,12 @@ auto getCurrentCpuTemperature() -> float { } // Obtain the initial locator to WMI. - IWbemLocator *pLoc = NULL; - hres = CoCreateInstance( - CLSID_WbemLocator, - 0, - CLSCTX_INPROC_SERVER, - IID_IWbemLocator, (LPVOID *)&pLoc); + IWbemLocator* pLoc = NULL; + hres = CoCreateInstance(CLSID_WbemLocator, + 0, + CLSCTX_INPROC_SERVER, + IID_IWbemLocator, + (LPVOID*) &pLoc); if (FAILED(hres)) { spdlog::error("Failed to create IWbemLocator object. Error code = {}", hres); @@ -149,18 +148,10 @@ auto getCurrentCpuTemperature() -> float { return temperature; } - IWbemServices *pSvc = NULL; + IWbemServices* pSvc = NULL; // Connect to the root\cimv2 namespace with the current user. - hres = pLoc->ConnectServer( - _bstr_t(L"ROOT\\CIMV2"), - NULL, - NULL, - 0, - NULL, - 0, - 0, - &pSvc); + hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0, NULL, 0, 0, &pSvc); if (FAILED(hres)) { spdlog::error("Could not connect. Error code = {}", hres); @@ -170,15 +161,14 @@ auto getCurrentCpuTemperature() -> float { } // Set security levels on the proxy. - hres = CoSetProxyBlanket( - pSvc, - RPC_C_AUTHN_WINNT, - RPC_C_AUTHZ_NONE, - NULL, - RPC_C_AUTHN_LEVEL_CALL, - RPC_C_IMP_LEVEL_IMPERSONATE, - NULL, - EOAC_NONE); + hres = CoSetProxyBlanket(pSvc, + RPC_C_AUTHN_WINNT, + RPC_C_AUTHZ_NONE, + NULL, + RPC_C_AUTHN_LEVEL_CALL, + RPC_C_IMP_LEVEL_IMPERSONATE, + NULL, + EOAC_NONE); if (FAILED(hres)) { spdlog::error("Could not set proxy blanket. Error code = {}", hres); @@ -189,7 +179,7 @@ auto getCurrentCpuTemperature() -> float { } // Use the IWbemServices pointer to make requests of WMI. - IEnumWbemClassObject *pEnumerator = NULL; + IEnumWbemClassObject* pEnumerator = NULL; hres = pSvc->ExecQuery( bstr_t("WQL"), bstr_t("SELECT * FROM Win32_PerfFormattedData_Counters_ThermalZoneInformation"), @@ -205,7 +195,7 @@ auto getCurrentCpuTemperature() -> float { return temperature; } - IWbemClassObject *pclsObj = NULL; + IWbemClassObject* pclsObj = NULL; ULONG uReturn = 0; while (pEnumerator) { @@ -218,7 +208,7 @@ auto getCurrentCpuTemperature() -> float { VARIANT vtProp; hr = pclsObj->Get(L"Temperature", 0, &vtProp, 0, 0); if (SUCCEEDED(hr) && (vtProp.vt == VT_I4)) { - temperature = static_cast(vtProp.intVal) / 10.0F; // Adjust if necessary + temperature = static_cast(vtProp.intVal) / 10.0F; // Adjust if necessary } VariantClear(&vtProp); pclsObj->Release(); diff --git a/src/Infrastructure/System/Crash.cc b/src/Infrastructure/System/Crash.cc index 82f1c94d..71fc00a0 100644 --- a/src/Infrastructure/System/Crash.cc +++ b/src/Infrastructure/System/Crash.cc @@ -10,7 +10,6 @@ #include #include - #ifndef _MSC_VER #include #endif From 3693e9ef0379c94bd8c18b6a1c7d31709bc1993d Mon Sep 17 00:00:00 2001 From: Max Qian <64824374+AstroAir@users.noreply.github.com> Date: Sun, 27 Oct 2024 14:45:20 +0800 Subject: [PATCH 4/6] clang-format: stupid local version --- src/Infrastructure/System/Cpu.cc | 1 - src/Infrastructure/System/Wm.cc | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Infrastructure/System/Cpu.cc b/src/Infrastructure/System/Cpu.cc index 886a38b0..42a59612 100644 --- a/src/Infrastructure/System/Cpu.cc +++ b/src/Infrastructure/System/Cpu.cc @@ -5,7 +5,6 @@ #include #include - #ifdef _WIN32 // clang-format off #include diff --git a/src/Infrastructure/System/Wm.cc b/src/Infrastructure/System/Wm.cc index e23eb05d..6a5244bb 100644 --- a/src/Infrastructure/System/Wm.cc +++ b/src/Infrastructure/System/Wm.cc @@ -1,7 +1,6 @@ #include "Wm.h" #include - #ifdef _WIN32 // clang-format off #include From f6e5f052db049ac74d9a1a606f9643550900d0b5 Mon Sep 17 00:00:00 2001 From: Max Qian <64824374+AstroAir@users.noreply.github.com> Date: Mon, 28 Oct 2024 20:22:50 +0800 Subject: [PATCH 5/6] Remove submodule surrealist-ui --- ui/modules/surrealism-ui | 1 - 1 file changed, 1 deletion(-) delete mode 160000 ui/modules/surrealism-ui diff --git a/ui/modules/surrealism-ui b/ui/modules/surrealism-ui deleted file mode 160000 index cb11d5d8..00000000 --- a/ui/modules/surrealism-ui +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cb11d5d81752d712630de59418ee73e2ae362f29 From 1201d1c097c5ad701f360417b330c5a03aee8a86 Mon Sep 17 00:00:00 2001 From: Max Qian <64824374+AstroAir@users.noreply.github.com> Date: Mon, 28 Oct 2024 20:36:46 +0800 Subject: [PATCH 6/6] =?UTF-8?q?refactor:=20=E4=BF=AE=E5=A4=8DCrash.cc?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E6=97=B6=E9=97=B4=E5=A4=84=E7=90=86=E9=94=99?= =?UTF-8?q?=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复了Crash.cc文件中的时间处理错误,使用了平台特定的时间处理函数来获取本地时间。 --- src/Infrastructure/System/Crash.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Infrastructure/System/Crash.cc b/src/Infrastructure/System/Crash.cc index 71fc00a0..72380446 100644 --- a/src/Infrastructure/System/Crash.cc +++ b/src/Infrastructure/System/Crash.cc @@ -81,7 +81,11 @@ auto getChinaTimestampString() -> std::string { auto now = std::chrono::system_clock::now(); std::time_t nowC = std::chrono::system_clock::to_time_t(now); std::tm localTime; +#if PLATFORM_WINDOWS if (localtime_s(&localTime, &nowC) != 0) { +#else + if (localtime_r(&localTime, &nowC) != 0) { +#endif } std::stringstream sss; sss << std::put_time(&localTime, "%Y-%m-%d %H:%M:%S"); @@ -131,7 +135,11 @@ void saveCrashLog(std::string_view error_msg) { auto now = std::chrono::system_clock::now(); std::time_t nowC = std::chrono::system_clock::to_time_t(now); std::tm localTime; +#if PLATFORM_WINDOWS if (localtime_s(&localTime, &nowC) != 0) { +#else + if (localtime_r(&localTime, &nowC) != 0) { +#endif // Handle error return; }