linux 编译
This commit is contained in:
+300
-294
@@ -39,353 +39,359 @@
|
||||
#endif
|
||||
|
||||
namespace Psc {
|
||||
namespace {
|
||||
bool local_time_from_time_t(std::time_t value, std::tm &out) noexcept {
|
||||
namespace {
|
||||
bool local_time_from_time_t(std::time_t value, std::tm &out) noexcept {
|
||||
#if defined(_WIN32)
|
||||
// MinGW / Clang / MSVC 对 localtime_s 支持不完全一致,这里统一用带锁的
|
||||
// std::localtime。
|
||||
static std::mutex mtx;
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
std::tm *tmp = std::localtime(&value);
|
||||
if (!tmp) {
|
||||
return false;
|
||||
}
|
||||
out = *tmp;
|
||||
return true;
|
||||
// MinGW / Clang / MSVC 对 localtime_s 支持不完全一致,这里统一用带锁的
|
||||
// std::localtime。
|
||||
static std::mutex mtx;
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
std::tm *tmp = std::localtime(&value);
|
||||
if (!tmp) {
|
||||
return false;
|
||||
}
|
||||
out = *tmp;
|
||||
return true;
|
||||
#elif defined(__unix__) || defined(__APPLE__)
|
||||
return ::localtime_r(&value, &out) != nullptr;
|
||||
return ::localtime_r(&value, &out) != nullptr;
|
||||
#else
|
||||
std::tm *tmp = std::localtime(&value);
|
||||
if (!tmp) {
|
||||
return false;
|
||||
}
|
||||
out = *tmp;
|
||||
return true;
|
||||
std::tm *tmp = std::localtime(&value);
|
||||
if (!tmp) {
|
||||
return false;
|
||||
}
|
||||
out = *tmp;
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
} // namespace
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::uint64_t get_current_millisecond_timestamp() {
|
||||
const auto now = std::chrono::system_clock::now();
|
||||
const auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::uint64_t get_current_millisecond_timestamp() {
|
||||
const auto now = std::chrono::system_clock::now();
|
||||
const auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now.time_since_epoch());
|
||||
return static_cast<std::uint64_t>(duration.count());
|
||||
}
|
||||
return static_cast<std::uint64_t>(duration.count());
|
||||
}
|
||||
|
||||
int redict_main_with_gtest(int argc, char *argv[],
|
||||
Main_Function_Type main_func) {
|
||||
int redict_main_with_gtest(int argc, char *argv[],
|
||||
Main_Function_Type main_func) {
|
||||
#if defined(_MSC_VER) && defined(_CRTDBG_MAP_ALLOC)
|
||||
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
|
||||
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
|
||||
#endif
|
||||
|
||||
set_console_utf8();
|
||||
set_console_utf8();
|
||||
|
||||
if (argc == 1) {
|
||||
if (argc == 1) {
|
||||
#ifdef _USE_GTEST
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
#else
|
||||
return main_func(argc, argv);
|
||||
return main_func(argc, argv);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
const int new_argc = argc - 1;
|
||||
char **new_argv = &argv[1];
|
||||
const int new_argc = argc - 1;
|
||||
char **new_argv = &argv[1];
|
||||
|
||||
int ret = 0;
|
||||
int ret = 0;
|
||||
#ifdef _USE_GTEST
|
||||
const std::string type = argv[1];
|
||||
if (type != "main") {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
ret = RUN_ALL_TESTS();
|
||||
} else {
|
||||
ret = main_func(new_argc, new_argv);
|
||||
}
|
||||
#else
|
||||
ret = main_func(new_argc, new_argv);
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && defined(_CRTDBG_MAP_ALLOC)
|
||||
if (_CrtDumpMemoryLeaks()) {
|
||||
std::cout << "[CRT] Detected memory leaks.\n";
|
||||
} else {
|
||||
std::cout << "[CRT] No memory leaks detected.\n";
|
||||
}
|
||||
#endif
|
||||
|
||||
std::cout << "ecap ret: " << ret << std::endl;
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string get_exe_dir() {
|
||||
const std::string exe_path = get_exe_path();
|
||||
if (exe_path.empty()) {
|
||||
return "";
|
||||
}
|
||||
return std::filesystem::path(exe_path).parent_path().string();
|
||||
}
|
||||
|
||||
std::string get_abs_path(std::string_view path) {
|
||||
if (path.empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (path.rfind("@", 0) == 0) {
|
||||
return get_exe_dir() + std::string(path.substr(1));
|
||||
}
|
||||
|
||||
if (path.rfind("~", 0) == 0) {
|
||||
return get_home_dir() + std::string(path.substr(1));
|
||||
}
|
||||
|
||||
return std::string(path);
|
||||
}
|
||||
|
||||
std::string utc_2_local_time(std::time_t us_timestamp, const char *format) {
|
||||
std::tm local_tm{};
|
||||
if (!local_time_from_time_t(us_timestamp, local_tm)) {
|
||||
return "invalid_time";
|
||||
}
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << std::put_time(&local_tm, format ? format : "%H:%M:%S");
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
namespace {
|
||||
constexpr std::uint8_t alphabet_map[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
constexpr std::uint8_t invalid_base64 = 255;
|
||||
|
||||
constexpr std::uint8_t decode_char(unsigned char ch) noexcept {
|
||||
return (ch >= 'A' && ch <= 'Z') ? static_cast<std::uint8_t>(ch - 'A')
|
||||
: (ch >= 'a' && ch <= 'z') ? static_cast<std::uint8_t>(ch - 'a' + 26)
|
||||
: (ch >= '0' && ch <= '9') ? static_cast<std::uint8_t>(ch - '0' + 52)
|
||||
: (ch == '+') ? 62
|
||||
: (ch == '/') ? 63
|
||||
: (ch == '=') ? 64
|
||||
: invalid_base64;
|
||||
}
|
||||
|
||||
std::uint32_t base64_encode_raw(const std::uint8_t *text,
|
||||
std::uint32_t text_len, std::uint8_t *encode) {
|
||||
std::uint32_t i = 0;
|
||||
std::uint32_t j = 0;
|
||||
|
||||
for (; i + 3 <= text_len; i += 3) {
|
||||
encode[j++] = alphabet_map[text[i] >> 2];
|
||||
encode[j++] = alphabet_map[((text[i] << 4) & 0x30) | (text[i + 1] >> 4)];
|
||||
encode[j++] =
|
||||
alphabet_map[((text[i + 1] << 2) & 0x3c) | (text[i + 2] >> 6)];
|
||||
encode[j++] = alphabet_map[text[i + 2] & 0x3f];
|
||||
}
|
||||
|
||||
if (i < text_len) {
|
||||
const std::uint32_t tail = text_len - i;
|
||||
if (tail == 1) {
|
||||
encode[j++] = alphabet_map[text[i] >> 2];
|
||||
encode[j++] = alphabet_map[(text[i] << 4) & 0x30];
|
||||
encode[j++] = '=';
|
||||
encode[j++] = '=';
|
||||
const std::string type = argv[1];
|
||||
if (type != "main") {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
ret = RUN_ALL_TESTS();
|
||||
} else {
|
||||
encode[j++] = alphabet_map[text[i] >> 2];
|
||||
encode[j++] = alphabet_map[((text[i] << 4) & 0x30) | (text[i + 1] >> 4)];
|
||||
encode[j++] = alphabet_map[(text[i + 1] << 2) & 0x3c];
|
||||
encode[j++] = '=';
|
||||
ret = main_func(new_argc, new_argv);
|
||||
}
|
||||
#else
|
||||
ret = main_func(new_argc, new_argv);
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && defined(_CRTDBG_MAP_ALLOC)
|
||||
if (_CrtDumpMemoryLeaks()) {
|
||||
std::cout << "[CRT] Detected memory leaks.\n";
|
||||
} else {
|
||||
std::cout << "[CRT] No memory leaks detected.\n";
|
||||
}
|
||||
#endif
|
||||
|
||||
std::cout << "ecap ret: " << ret << std::endl;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
std::uint32_t base64_decode_raw(const std::uint8_t *code,
|
||||
std::uint32_t code_len, std::uint8_t *plain) {
|
||||
if (!code || !plain || (code_len & 0x03U) != 0U) {
|
||||
return 0;
|
||||
std::string get_exe_dir() {
|
||||
const std::string exe_path = get_exe_path();
|
||||
if (exe_path.empty()) {
|
||||
return "";
|
||||
}
|
||||
return std::filesystem::path(exe_path).parent_path().string();
|
||||
}
|
||||
|
||||
std::uint32_t j = 0;
|
||||
for (std::uint32_t i = 0; i < code_len; i += 4) {
|
||||
const std::uint8_t q0 = decode_char(code[i]);
|
||||
const std::uint8_t q1 = decode_char(code[i + 1]);
|
||||
const std::uint8_t q2 = decode_char(code[i + 2]);
|
||||
const std::uint8_t q3 = decode_char(code[i + 3]);
|
||||
std::string get_abs_path(std::string_view path) {
|
||||
if (path.empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (path.rfind("@", 0) == 0) {
|
||||
return get_exe_dir() + std::string(path.substr(1));
|
||||
}
|
||||
|
||||
if (path.rfind("~", 0) == 0) {
|
||||
return get_home_dir() + std::string(path.substr(1));
|
||||
}
|
||||
|
||||
return std::string(path);
|
||||
}
|
||||
|
||||
std::string utc_2_local_time(std::time_t us_timestamp, const char *format) {
|
||||
std::tm local_tm{};
|
||||
if (!local_time_from_time_t(us_timestamp, local_tm)) {
|
||||
return "invalid_time";
|
||||
}
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << std::put_time(&local_tm, format ? format : "%H:%M:%S");
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
namespace {
|
||||
constexpr std::uint8_t alphabet_map[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
constexpr std::uint8_t invalid_base64 = 255;
|
||||
|
||||
constexpr std::uint8_t decode_char(unsigned char ch) noexcept {
|
||||
return (ch >= 'A' && ch <= 'Z')
|
||||
? static_cast<std::uint8_t>(ch - 'A')
|
||||
: (ch >= 'a' && ch <= 'z')
|
||||
? static_cast<std::uint8_t>(ch - 'a' + 26)
|
||||
: (ch >= '0' && ch <= '9')
|
||||
? static_cast<std::uint8_t>(ch - '0' + 52)
|
||||
: (ch == '+')
|
||||
? 62
|
||||
: (ch == '/')
|
||||
? 63
|
||||
: (ch == '=')
|
||||
? 64
|
||||
: invalid_base64;
|
||||
}
|
||||
|
||||
std::uint32_t base64_encode_raw(const std::uint8_t *text,
|
||||
std::uint32_t text_len, std::uint8_t *encode) {
|
||||
std::uint32_t i = 0;
|
||||
std::uint32_t j = 0;
|
||||
|
||||
for (; i + 3 <= text_len; i += 3) {
|
||||
encode[j++] = alphabet_map[text[i] >> 2];
|
||||
encode[j++] = alphabet_map[((text[i] << 4) & 0x30) | (text[i + 1] >> 4)];
|
||||
encode[j++] =
|
||||
alphabet_map[((text[i + 1] << 2) & 0x3c) | (text[i + 2] >> 6)];
|
||||
encode[j++] = alphabet_map[text[i + 2] & 0x3f];
|
||||
}
|
||||
|
||||
if (i < text_len) {
|
||||
const std::uint32_t tail = text_len - i;
|
||||
if (tail == 1) {
|
||||
encode[j++] = alphabet_map[text[i] >> 2];
|
||||
encode[j++] = alphabet_map[(text[i] << 4) & 0x30];
|
||||
encode[j++] = '=';
|
||||
encode[j++] = '=';
|
||||
} else {
|
||||
encode[j++] = alphabet_map[text[i] >> 2];
|
||||
encode[j++] = alphabet_map[((text[i] << 4) & 0x30) | (text[i + 1] >> 4)];
|
||||
encode[j++] = alphabet_map[(text[i + 1] << 2) & 0x3c];
|
||||
encode[j++] = '=';
|
||||
}
|
||||
}
|
||||
|
||||
if (q0 >= 64 || q1 >= 64) {
|
||||
return j;
|
||||
}
|
||||
|
||||
plain[j++] = static_cast<std::uint8_t>((q0 << 2) | (q1 >> 4));
|
||||
std::uint32_t base64_decode_raw(const std::uint8_t *code,
|
||||
std::uint32_t code_len, std::uint8_t *plain) {
|
||||
if (!code || !plain || (code_len & 0x03U) != 0U) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::uint32_t j = 0;
|
||||
for (std::uint32_t i = 0; i < code_len; i += 4) {
|
||||
const std::uint8_t q0 = decode_char(code[i]);
|
||||
const std::uint8_t q1 = decode_char(code[i + 1]);
|
||||
const std::uint8_t q2 = decode_char(code[i + 2]);
|
||||
const std::uint8_t q3 = decode_char(code[i + 3]);
|
||||
|
||||
if (q0 >= 64 || q1 >= 64) {
|
||||
return j;
|
||||
}
|
||||
|
||||
plain[j++] = static_cast<std::uint8_t>((q0 << 2) | (q1 >> 4));
|
||||
|
||||
if (q2 == 64) {
|
||||
break;
|
||||
}
|
||||
if (q2 > 64) {
|
||||
return j;
|
||||
}
|
||||
|
||||
plain[j++] = static_cast<std::uint8_t>((q1 << 4) | (q2 >> 2));
|
||||
|
||||
if (q3 == 64) {
|
||||
break;
|
||||
}
|
||||
if (q3 > 64) {
|
||||
return j;
|
||||
}
|
||||
|
||||
plain[j++] = static_cast<std::uint8_t>((q2 << 6) | q3);
|
||||
}
|
||||
|
||||
if (q2 == 64) {
|
||||
break;
|
||||
}
|
||||
if (q2 > 64) {
|
||||
return j;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
plain[j++] = static_cast<std::uint8_t>((q1 << 4) | (q2 >> 2));
|
||||
|
||||
if (q3 == 64) {
|
||||
break;
|
||||
}
|
||||
if (q3 > 64) {
|
||||
return j;
|
||||
}
|
||||
|
||||
plain[j++] = static_cast<std::uint8_t>((q2 << 6) | q3);
|
||||
std::string base64_encode(std::string_view data) {
|
||||
const auto text_len = static_cast<std::uint32_t>(data.size());
|
||||
std::vector<std::uint8_t> encode(4 * ((text_len + 2) / 3));
|
||||
const std::uint32_t encoded_len =
|
||||
base64_encode_raw(reinterpret_cast<const std::uint8_t *>(data.data()),
|
||||
text_len, encode.data());
|
||||
return std::string(encode.begin(), encode.begin() + encoded_len);
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
} // namespace
|
||||
std::string base64_decode(std::string_view data) {
|
||||
const auto code_len = static_cast<std::uint32_t>(data.size());
|
||||
if ((code_len & 0x03U) != 0U) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string base64_encode(std::string_view data) {
|
||||
const auto text_len = static_cast<std::uint32_t>(data.size());
|
||||
std::vector<std::uint8_t> encode(4 * ((text_len + 2) / 3));
|
||||
const std::uint32_t encoded_len =
|
||||
base64_encode_raw(reinterpret_cast<const std::uint8_t *>(data.data()),
|
||||
text_len, encode.data());
|
||||
return std::string(encode.begin(), encode.begin() + encoded_len);
|
||||
}
|
||||
|
||||
std::string base64_decode(std::string_view data) {
|
||||
const auto code_len = static_cast<std::uint32_t>(data.size());
|
||||
if ((code_len & 0x03U) != 0U) {
|
||||
return "";
|
||||
std::vector<std::uint8_t> plain(code_len * 3 / 4);
|
||||
const std::uint32_t decoded_len =
|
||||
base64_decode_raw(reinterpret_cast<const std::uint8_t *>(data.data()),
|
||||
code_len, plain.data());
|
||||
return std::string(plain.begin(), plain.begin() + decoded_len);
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> plain(code_len * 3 / 4);
|
||||
const std::uint32_t decoded_len =
|
||||
base64_decode_raw(reinterpret_cast<const std::uint8_t *>(data.data()),
|
||||
code_len, plain.data());
|
||||
return std::string(plain.begin(), plain.begin() + decoded_len);
|
||||
}
|
||||
|
||||
std::string signal_to_string(int signal) {
|
||||
switch (signal) {
|
||||
case SIGINT:
|
||||
return "SIGINT (Ctrl+C)";
|
||||
case SIGILL:
|
||||
return "SIGILL (Illegal Instruction)";
|
||||
case SIGFPE:
|
||||
return "SIGFPE (Floating Point Exception)";
|
||||
case SIGSEGV:
|
||||
return "SIGSEGV (Segmentation Fault)";
|
||||
case SIGTERM:
|
||||
return "SIGTERM (Termination Request)";
|
||||
case SIGABRT:
|
||||
return "SIGABRT (Abort Signal)";
|
||||
std::string signal_to_string(int signal) {
|
||||
switch (signal) {
|
||||
case SIGINT:
|
||||
return "SIGINT (Ctrl+C)";
|
||||
case SIGILL:
|
||||
return "SIGILL (Illegal Instruction)";
|
||||
case SIGFPE:
|
||||
return "SIGFPE (Floating Point Exception)";
|
||||
case SIGSEGV:
|
||||
return "SIGSEGV (Segmentation Fault)";
|
||||
case SIGTERM:
|
||||
return "SIGTERM (Termination Request)";
|
||||
case SIGABRT:
|
||||
return "SIGABRT (Abort Signal)";
|
||||
#if defined(_WIN32) && defined(SIGBREAK)
|
||||
case SIGBREAK:
|
||||
return "SIGBREAK (Breakpoint)";
|
||||
case SIGBREAK:
|
||||
return "SIGBREAK (Breakpoint)";
|
||||
#endif
|
||||
#if defined(_MSC_VER) && defined(SIGABRT_COMPAT)
|
||||
case SIGABRT_COMPAT:
|
||||
return "SIGABRT_COMPAT (Abort Compatibility Signal)";
|
||||
case SIGABRT_COMPAT:
|
||||
return "SIGABRT_COMPAT (Abort Compatibility Signal)";
|
||||
#endif
|
||||
default:
|
||||
return "Unknown signal:" + std::to_string(signal);
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_current_time_as_string() {
|
||||
const auto now = std::chrono::system_clock::now();
|
||||
const std::time_t now_time = std::chrono::system_clock::to_time_t(now);
|
||||
|
||||
std::tm local_tm{};
|
||||
if (!local_time_from_time_t(now_time, local_tm)) {
|
||||
return "invalid_time";
|
||||
}
|
||||
|
||||
std::ostringstream ss;
|
||||
ss << std::put_time(&local_tm, "%Y-%m-%d_%H-%M-%S");
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
void create_file_if_not_exists(std::string_view file_name) {
|
||||
std::error_code ec;
|
||||
const fs::path file_path(file_name);
|
||||
|
||||
if (fs::exists(file_path, ec)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fs::path parent = file_path.parent_path();
|
||||
if (!parent.empty()) {
|
||||
fs::create_directories(parent, ec);
|
||||
if (ec) {
|
||||
std::cerr << "Failed to create parent directory: " << parent.string()
|
||||
<< ", error: " << ec.message() << std::endl;
|
||||
return;
|
||||
default:
|
||||
return "Unknown signal:" + std::to_string(signal);
|
||||
}
|
||||
}
|
||||
|
||||
std::ofstream ofs(file_path, std::ios::out | std::ios::app);
|
||||
if (!ofs) {
|
||||
std::cerr << "Failed to create the file: " << file_name << std::endl;
|
||||
return;
|
||||
std::string get_current_time_as_string() {
|
||||
const auto now = std::chrono::system_clock::now();
|
||||
const std::time_t now_time = std::chrono::system_clock::to_time_t(now);
|
||||
|
||||
std::tm local_tm{};
|
||||
if (!local_time_from_time_t(now_time, local_tm)) {
|
||||
return "invalid_time";
|
||||
}
|
||||
|
||||
std::ostringstream ss;
|
||||
ss << std::put_time(&local_tm, "%Y-%m-%d_%H-%M-%S");
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
const auto perms =
|
||||
fs::perms::owner_all | fs::perms::group_all | fs::perms::others_all;
|
||||
fs::permissions(file_path, perms, ec);
|
||||
}
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
void create_dir_if_not_exists(std::string_view dir_name) {
|
||||
std::error_code ec;
|
||||
const fs::path dir_path(dir_name);
|
||||
void create_file_if_not_exists(std::string_view file_name) {
|
||||
std::error_code ec;
|
||||
const fs::path file_path(file_name);
|
||||
|
||||
if (!fs::exists(dir_path, ec)) {
|
||||
fs::create_directories(dir_path, ec);
|
||||
if (ec) {
|
||||
std::cerr << "Failed to create directory: " << dir_name
|
||||
<< ", error: " << ec.message() << std::endl;
|
||||
if (fs::exists(file_path, ec)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fs::path parent = file_path.parent_path();
|
||||
if (!parent.empty()) {
|
||||
fs::create_directories(parent, ec);
|
||||
if (ec) {
|
||||
std::cerr << "Failed to create parent directory: " << parent.string()
|
||||
<< ", error: " << ec.message() << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::ofstream ofs(file_path, std::ios::out | std::ios::app);
|
||||
if (!ofs) {
|
||||
std::cerr << "Failed to create the file: " << file_name << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto perms =
|
||||
fs::perms::owner_all | fs::perms::group_all | fs::perms::others_all;
|
||||
fs::permissions(file_path, perms, ec);
|
||||
}
|
||||
|
||||
void create_dir_if_not_exists(std::string_view dir_name) {
|
||||
std::error_code ec;
|
||||
const fs::path dir_path(dir_name);
|
||||
|
||||
if (!fs::exists(dir_path, ec)) {
|
||||
fs::create_directories(dir_path, ec);
|
||||
if (ec) {
|
||||
std::cerr << "Failed to create directory: " << dir_name
|
||||
<< ", error: " << ec.message() << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const auto perms =
|
||||
fs::perms::owner_all | fs::perms::group_all | fs::perms::others_all;
|
||||
fs::permissions(dir_path, perms, ec);
|
||||
}
|
||||
|
||||
void write_stack_trace_to_file(std::string_view stack_trace,
|
||||
std::string_view filename) {
|
||||
create_file_if_not_exists(filename);
|
||||
std::ofstream out_file(filename.data(), std::ios::out | std::ios::app);
|
||||
if (out_file.is_open()) {
|
||||
out_file << stack_trace << std::endl;
|
||||
} else {
|
||||
std::cerr << "Unable to open file " << filename << " for writing."
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
const auto perms =
|
||||
fs::perms::owner_all | fs::perms::group_all | fs::perms::others_all;
|
||||
fs::permissions(dir_path, perms, ec);
|
||||
}
|
||||
|
||||
void write_stack_trace_to_file(std::string_view stack_trace,
|
||||
std::string_view filename) {
|
||||
create_file_if_not_exists(filename);
|
||||
std::ofstream out_file(filename, std::ios::out | std::ios::app);
|
||||
if (out_file.is_open()) {
|
||||
out_file << stack_trace << std::endl;
|
||||
} else {
|
||||
std::cerr << "Unable to open file " << filename << " for writing."
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void signal_handler(int signal_code) {
|
||||
std::cout << "signal_handler [" << signal_to_string(signal_code) << "]"
|
||||
<< std::endl;
|
||||
stop_program = signal_code;
|
||||
}
|
||||
|
||||
void signal_handler_ignore(int) {}
|
||||
|
||||
void init_signal_config(std::set<int> normal_quit,
|
||||
std::set<int> ignore_signal) {
|
||||
if (is_big_endian) {
|
||||
std::cout << "大端序 This system is Big-endian." << std::endl;
|
||||
} else {
|
||||
std::cout << "小端序 This system is Little-endian." << std::endl;
|
||||
void signal_handler(int signal_code) {
|
||||
std::cout << "signal_handler [" << signal_to_string(signal_code) << "]"
|
||||
<< std::endl;
|
||||
stop_program = signal_code;
|
||||
}
|
||||
|
||||
for (auto signum : normal_quit) {
|
||||
std::signal(signum, signal_handler);
|
||||
void signal_handler_ignore(int) {
|
||||
}
|
||||
for (auto signum : ignore_signal) {
|
||||
std::signal(signum, signal_handler_ignore);
|
||||
}
|
||||
}
|
||||
|
||||
void init_signal_config(std::set<int> normal_quit,
|
||||
std::set<int> ignore_signal) {
|
||||
if (is_big_endian) {
|
||||
std::cout << "大端序 This system is Big-endian." << std::endl;
|
||||
} else {
|
||||
std::cout << "小端序 This system is Little-endian." << std::endl;
|
||||
}
|
||||
|
||||
for (auto signum: normal_quit) {
|
||||
std::signal(signum, signal_handler);
|
||||
}
|
||||
for (auto signum: ignore_signal) {
|
||||
std::signal(signum, signal_handler_ignore);
|
||||
}
|
||||
}
|
||||
} // namespace Psc
|
||||
|
||||
+375
-371
@@ -41,399 +41,403 @@
|
||||
#endif
|
||||
|
||||
namespace Psc {
|
||||
namespace {
|
||||
std::string trim_copy(std::string_view 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(std::string_view 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<char *>(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<size_t>(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;
|
||||
namespace {
|
||||
std::string trim_copy(std::string_view value) {
|
||||
auto not_space = [](unsigned char ch) {
|
||||
return !std::isspace(ch);
|
||||
};
|
||||
auto first = std::find_if(value.begin(), value.end(), not_space);
|
||||
if (first == value.end()) {
|
||||
return {};
|
||||
}
|
||||
auto last = std::find_if(value.rbegin(), value.rend(), not_space).base();
|
||||
return std::string(first, last);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::string extract_address(std::string_view 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 read_first_line(std::string_view file_name) {
|
||||
std::ifstream in(file_name.data());
|
||||
std::string line;
|
||||
if (std::getline(in, line)) {
|
||||
return trim_copy(line);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string execute_command(std::string_view cmd) {
|
||||
FILE *fp = popen(cmd.c_str(), "r");
|
||||
if (!fp) {
|
||||
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<char *>(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<size_t>(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;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::string extract_address(std::string_view input) {
|
||||
static const std::regex address_regex(R"(\[([0-9a-fA-Fx]+)\])");
|
||||
std::match_results<std::string_view::const_iterator> match;
|
||||
if (std::regex_search(input.begin(), input.end(), match, address_regex)) {
|
||||
return match[1].str();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string result;
|
||||
char buffer[1024];
|
||||
while (fgets(buffer, sizeof(buffer), fp) != nullptr) {
|
||||
result.append(buffer);
|
||||
std::string execute_command(std::string_view cmd) {
|
||||
FILE *fp = popen(cmd.data(), "r");
|
||||
if (!fp) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string result;
|
||||
char buffer[1024];
|
||||
while (fgets(buffer, sizeof(buffer), fp) != nullptr) {
|
||||
result.append(buffer);
|
||||
}
|
||||
pclose(fp);
|
||||
return result;
|
||||
}
|
||||
pclose(fp);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string get_tool(std::string_view name) {
|
||||
std::string ret = name;
|
||||
const std::string exe_dir = get_exe_dir();
|
||||
const std::string ret_1 = exe_dir + "/" + name;
|
||||
std::string get_tool(std::string_view _name) {
|
||||
std::string name(_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;
|
||||
|
||||
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;
|
||||
if (std::filesystem::exists(ret_1)) {
|
||||
ret = ret_1;
|
||||
} else if (std::filesystem::exists(ret_2)) {
|
||||
ret = ret_2;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string get_stack_trace() {
|
||||
std::ostringstream oss;
|
||||
constexpr int max_frames = 64;
|
||||
void *stack[max_frames]{};
|
||||
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";
|
||||
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<std::uintptr_t>(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<unsigned long>(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();
|
||||
}
|
||||
|
||||
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<std::uintptr_t>(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<unsigned long>(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[] = {
|
||||
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"};
|
||||
"/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(std::string_view 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(std::string_view 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(std::string_view 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, std::string_view 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, std::string_view 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<int>(ec));
|
||||
}
|
||||
|
||||
ERROR_CODE_TYPE get_error_code() { return errno; }
|
||||
|
||||
void set_error_code(ERROR_CODE_TYPE code) { errno = static_cast<int>(code); }
|
||||
|
||||
std::string get_exe_path() {
|
||||
std::vector<char> 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<size_t>(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<pthread_t *>(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<void *>(&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<pthread_t *>(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<void *>(&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;
|
||||
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";
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t get_process_memory() { return read_proc_status_kb("VmRSS:") * 1024; }
|
||||
void set_current_thread_name(std::string_view name) {
|
||||
auto trimmed =
|
||||
name.substr(0, 15); // Linux 线程名最多 16 字节,含结尾 \0。
|
||||
(void) pthread_setname_np(pthread_self(), trimmed.data());
|
||||
}
|
||||
|
||||
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(std::string_view library_path, void *&out,
|
||||
std::error_code &ec) noexcept {
|
||||
out = nullptr;
|
||||
ec.clear();
|
||||
(void) dlerror();
|
||||
void *library = dlopen(library_path.data(), 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(std::string_view 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, std::string_view 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.data());
|
||||
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, std::string_view 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<int>(ec));
|
||||
}
|
||||
|
||||
ERROR_CODE_TYPE get_error_code() { return errno; }
|
||||
|
||||
void set_error_code(ERROR_CODE_TYPE code) {
|
||||
errno = static_cast<int>(code);
|
||||
}
|
||||
|
||||
std::string get_exe_path() {
|
||||
std::vector<char> 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<size_t>(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<pthread_t *>(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<void *>(&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<pthread_t *>(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<void *>(&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__)
|
||||
|
||||
Reference in New Issue
Block a user