#if defined(__linux__) #include "export.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if __has_include("Core/JSON.h") #include "Core/JSON.h" #elif __has_include("../Base/JSON.h") #include "../Base/JSON.h" #endif namespace Psc { namespace { std::string trim_copy(std::string value) { auto not_space = [](unsigned char ch) { return !std::isspace(ch); }; value.erase(value.begin(), std::find_if(value.begin(), value.end(), not_space)); value.erase(std::find_if(value.rbegin(), value.rend(), not_space).base(), value.end()); return value; } std::string read_first_line(const std::string& file_name) { std::ifstream in(file_name); std::string line; if (std::getline(in, line)) { return trim_copy(line); } return ""; } std::string execute_command_single(const char* cmd) { if (!cmd || !*cmd) { return ""; } int pipefd[2]{}; if (pipe(pipefd) != 0) { return ""; } pid_t pid = fork(); if (pid < 0) { close(pipefd[0]); close(pipefd[1]); return ""; } if (pid == 0) { close(pipefd[0]); dup2(pipefd[1], STDOUT_FILENO); close(pipefd[1]); setsid(); execl("/bin/sh", "sh", "-c", cmd, static_cast(nullptr)); _exit(127); } close(pipefd[1]); std::string out; char buf[1024]; ssize_t n = 0; while ((n = read(pipefd[0], buf, sizeof(buf))) > 0) { out.append(buf, static_cast(n)); } close(pipefd[0]); waitpid(pid, nullptr, 0); return out; } size_t read_proc_status_kb(const char* key) { std::ifstream status("/proc/self/status"); std::string line; while (std::getline(status, line)) { if (line.rfind(key, 0) == 0) { size_t value_kb = 0; if (std::sscanf(line.c_str(), "%*s %zu kB", &value_kb) == 1) { return value_kb; } } } return 0; } } std::string extract_address(const std::string& input) { const std::regex address_regex(R"(\[([0-9a-fA-Fx]+)\])"); std::smatch match; if (std::regex_search(input, match, address_regex)) { return match[1].str(); } return ""; } std::string execute_command(const std::string& cmd) { FILE* fp = popen(cmd.c_str(), "r"); if (!fp) { return ""; } std::string result; char buffer[1024]; while (fgets(buffer, sizeof(buffer), fp) != nullptr) { result.append(buffer); } pclose(fp); return result; } std::string get_tool(const std::string& name) { std::string ret = name; const std::string exe_dir = get_exe_dir(); const std::string ret_1 = exe_dir + "/" + name; std::filesystem::path path = std::filesystem::u8path(exe_dir); const auto parent = path.parent_path(); const std::string parent_path = path_to_utf8(parent); const std::string ret_2 = parent_path + "/lib/" + name; if (std::filesystem::exists(ret_1)) { ret = ret_1; } else if (std::filesystem::exists(ret_2)) { ret = ret_2; } return ret; } std::string get_stack_trace() { std::ostringstream oss; constexpr int max_frames = 64; void* stack[max_frames]{}; const int stack_size = backtrace(stack, max_frames); char** symbols = backtrace_symbols(stack, stack_size); if (!symbols) { oss << "Error retrieving stack symbols\n"; return oss.str(); } const std::string addr2line = get_tool("addr2line"); const std::string cppfilt = get_tool("c++filt"); const std::string exe = get_exe_path(); oss << "addr2line: " << addr2line << '\n'; oss << "cppfilt: " << cppfilt << '\n'; oss << "Stack trace: " << exe << '\n'; for (int i = 0; i < stack_size; ++i) { const auto addr = reinterpret_cast(stack[i]); oss << "【" << std::dec << std::left << std::setw(2) << i << "】"; char cmd[4096]; std::snprintf(cmd, sizeof(cmd), R"(%s -e "%s" -f -i 0x%lx | %s)", addr2line.c_str(), exe.c_str(), static_cast(addr), cppfilt.c_str()); std::string str = execute_command_single(cmd); if (str.empty() && symbols[i]) { str = symbols[i]; } std::replace(str.begin(), str.end(), '\n', ' '); oss << " " << str << "【0x" << std::hex << addr << std::dec << "】" << '\n'; } free(symbols); return oss.str(); } std::string get_computer_serial_number() { static const char* candidates[] = { "/sys/class/dmi/id/product_serial", "/sys/class/dmi/id/board_serial", "/sys/class/dmi/id/product_uuid", "/sys/class/dmi/id/chassis_serial" }; for (const char* file : candidates) { std::string value = read_first_line(file); if (!value.empty() && value != "None" && value != "Not Specified" && value != "To Be Filled By O.E.M.") { return value; } } return "Unknown"; } void set_current_thread_name(const std::string& name) { std::string trimmed = name.substr(0, 15); // Linux 线程名最多 16 字节,含结尾 \0。 (void)pthread_setname_np(pthread_self(), trimmed.c_str()); } void set_console_utf8() { if (setlocale(LC_ALL, "") != nullptr) { return; } if (setlocale(LC_ALL, "C.UTF-8") != nullptr) { return; } (void)setlocale(LC_ALL, "en_US.UTF-8"); } PSC_DEFINE_TRIPLE_API_FROM_BOOL(void*, load_library, PSC_LOAD_LIBRARY_PARAMS) bool load_library_ec(const std::string& library_path, void*& out, std::error_code& ec) noexcept { out = nullptr; ec.clear(); (void)dlerror(); void* library = dlopen(library_path.c_str(), RTLD_LAZY); if (!library) { const char* err = dlerror(); std::cerr << library_path << " Failed to load library: " << (err ? err : "unknown") << '\n'; ec = std::make_error_code(std::errc::no_such_file_or_directory); return false; } out = library; return true; } void* load_library_fail_fast(const std::string& library_path) { auto ret = try_load_library(library_path); if (!ret) { Psc::fail_fast(); } return ret.value(); } void free_library(void* library) { if (library) { dlclose(library); } } PSC_DEFINE_TRIPLE_API_FROM_BOOL(void*, load_function, PSC_LOAD_FUNCTION_PARAMS) bool load_function_ec(void* library, const std::string& function_name, void*& out, std::error_code& ec) noexcept { out = nullptr; ec.clear(); if (!library) { std::cerr << function_name << " function not loaded (library == nullptr)\n"; ec = std::make_error_code(std::errc::invalid_argument); return false; } (void)dlerror(); void* func = dlsym(library, function_name.c_str()); const char* err = dlerror(); if (err != nullptr) { std::cerr << "dlsym failed for " << function_name << " : " << err << '\n'; ec = std::make_error_code(std::errc::function_not_supported); return false; } out = func; return true; } void* load_function_fail_fast(void* library, const std::string& function_name) { auto ret = try_load_function(library, function_name); if (!ret) { Psc::fail_fast(); } return ret.value(); } std::string get_error_message() { const int ec = errno; return "错误码:" + std::to_string(ec) + ": " + std::strerror(ec); } std::string get_error_message(ERROR_CODE_TYPE ec) { return "错误码:" + std::to_string(ec) + ": " + std::strerror(static_cast(ec)); } ERROR_CODE_TYPE get_error_code() { return errno; } void set_error_code(ERROR_CODE_TYPE code) { errno = static_cast(code); } std::string get_exe_path() { std::vector buffer(PATH_MAX + 1, '\0'); const ssize_t len = readlink("/proc/self/exe", buffer.data(), buffer.size() - 1); if (len == -1) { return ""; } buffer[static_cast(len)] = '\0'; return std::string(buffer.data()); } std::string get_home_dir() { if (const char* home = std::getenv("HOME")) { return home; } struct passwd* pw = getpwuid(getuid()); if (pw && pw->pw_dir) { return std::string(pw->pw_dir); } return ""; } bool set_thread_priority(void* thread_ptr, int priority, bool realtime) { if (!thread_ptr) { return false; } pthread_t pthread = *static_cast(thread_ptr); int policy = realtime ? SCHED_FIFO : SCHED_OTHER; sched_param param{}; if (realtime) { const int min_prio = sched_get_priority_min(policy); const int max_prio = sched_get_priority_max(policy); if (min_prio >= 0 && max_prio >= min_prio) { priority = std::max(min_prio, std::min(max_prio, priority)); } param.sched_priority = priority; } else { param.sched_priority = 0; } const int ret = pthread_setschedparam(pthread, policy, ¶m); if (ret != 0) { errno = ret; return false; } return true; } bool set_thread_priority(std::thread* thread, int priority, bool realtime) { if (!thread) { return false; } pthread_t pthread = thread->native_handle(); return set_thread_priority(static_cast(&pthread), priority, realtime); } bool set_thread_affinity(void* thread_ptr, unsigned cpu_index) { if (!thread_ptr) { return false; } if (cpu_index >= CPU_SETSIZE) { errno = EINVAL; return false; } pthread_t pthread = *static_cast(thread_ptr); cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(cpu_index, &cpuset); const int ret = pthread_setaffinity_np(pthread, sizeof(cpu_set_t), &cpuset); if (ret != 0) { errno = ret; std::cerr << "Error setting thread affinity: " << std::strerror(ret) << std::endl; return false; } return true; } bool set_thread_affinity(std::thread* thread, unsigned cpu_index) { if (!thread) { return false; } pthread_t pthread = thread->native_handle(); return set_thread_affinity(static_cast(&pthread), cpu_index); } std::string get_memory_info() { auto ret = JSON::object(); ret.append({"VmRSS (MB)", read_proc_status_kb("VmRSS:") / 1024}); ret.append({"VmSize (MB)", read_proc_status_kb("VmSize:") / 1024}); ret.append({"VmPeak (MB)", read_proc_status_kb("VmPeak:") / 1024}); ret.append({"VmHWM (MB)", read_proc_status_kb("VmHWM:") / 1024}); return ret.to_json_string(); } size_t get_system_memory() { std::ifstream meminfo("/proc/meminfo"); std::string line; while (std::getline(meminfo, line)) { if (line.rfind("MemTotal:", 0) == 0) { size_t mem_total_kb = 0; if (std::sscanf(line.c_str(), "MemTotal: %zu kB", &mem_total_kb) == 1) { return mem_total_kb * 1024; } } } return 0; } size_t get_process_memory() { return read_proc_status_kb("VmRSS:") * 1024; } } // namespace Psc #endif // defined(__linux__)