666 lines
19 KiB
C++
666 lines
19 KiB
C++
/*
|
||
* Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved.
|
||
*
|
||
* This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit).
|
||
*
|
||
* Use of this source code is governed by MIT license that can be found in the
|
||
* LICENSE file in the root of the source tree. All contributing project authors
|
||
* may be found in the AUTHORS file in the root of the source tree.
|
||
*/
|
||
|
||
#include <cassert>
|
||
#include <cstdio>
|
||
#include <cstdlib>
|
||
#include <cstring>
|
||
#include <string>
|
||
#include <algorithm>
|
||
#include <random>
|
||
|
||
#include "util.h"
|
||
#include "local_time.h"
|
||
#include "File.h"
|
||
#include "onceToken.h"
|
||
#include "logger.h"
|
||
#include "uv_errno.h"
|
||
#include "Network/sockutil.h"
|
||
|
||
#if defined(_WIN32)
|
||
#include <cstdio>
|
||
#include <cstdlib>
|
||
#include <cstring>
|
||
#include <shlwapi.h>
|
||
#pragma comment(lib, "shlwapi.lib")
|
||
extern "C" const IMAGE_DOS_HEADER __ImageBase;
|
||
#endif // defined(_WIN32)
|
||
|
||
#if defined(__MACH__) || defined(__APPLE__)
|
||
#include <limits.h>
|
||
#include <mach-o/dyld.h> /* _NSGetExecutablePath */
|
||
|
||
int uv_exepath(char *buffer, int *size) {
|
||
/* realpath(exepath) may be > PATH_MAX so double it to be on the safe side. */
|
||
char abspath[PATH_MAX * 2 + 1];
|
||
char exepath[PATH_MAX + 1];
|
||
uint32_t exepath_size;
|
||
size_t abspath_size;
|
||
|
||
if (buffer == nullptr || size == nullptr || *size == 0)
|
||
return -EINVAL;
|
||
|
||
exepath_size = sizeof(exepath);
|
||
if (_NSGetExecutablePath(exepath, &exepath_size))
|
||
return -EIO;
|
||
|
||
if (realpath(exepath, abspath) != abspath)
|
||
return -errno;
|
||
|
||
abspath_size = strlen(abspath);
|
||
if (abspath_size == 0)
|
||
return -EIO;
|
||
|
||
*size -= 1;
|
||
if ((size_t) *size > abspath_size)
|
||
*size = abspath_size;
|
||
|
||
memcpy(buffer, abspath, *size);
|
||
buffer[*size] = '\0';
|
||
|
||
return 0;
|
||
}
|
||
|
||
#endif //defined(__MACH__) || defined(__APPLE__)
|
||
|
||
using namespace std;
|
||
|
||
namespace toolkit {
|
||
|
||
static constexpr char CCH[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||
|
||
string makeRandStr(int sz, bool printable) {
|
||
string ret;
|
||
ret.resize(sz);
|
||
std::mt19937 rng(std::random_device{}());
|
||
for (int i = 0; i < sz; ++i) {
|
||
if (printable) {
|
||
uint32_t x = rng() % (sizeof(CCH) - 1);
|
||
ret[i] = CCH[x];
|
||
} else {
|
||
ret[i] = rng() % 0xFF;
|
||
}
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
bool is_safe(uint8_t b) {
|
||
return b >= ' ' && b < 128;
|
||
}
|
||
|
||
string hexdump(const void *buf, size_t len) {
|
||
string ret("\r\n");
|
||
char tmp[8];
|
||
const uint8_t *data = (const uint8_t *) buf;
|
||
for (size_t i = 0; i < len; i += 16) {
|
||
for (int j = 0; j < 16; ++j) {
|
||
if (i + j < len) {
|
||
int sz = snprintf(tmp, sizeof(tmp), "%.2x ", data[i + j]);
|
||
ret.append(tmp, sz);
|
||
} else {
|
||
int sz = snprintf(tmp, sizeof(tmp), " ");
|
||
ret.append(tmp, sz);
|
||
}
|
||
}
|
||
for (int j = 0; j < 16; ++j) {
|
||
if (i + j < len) {
|
||
ret += (is_safe(data[i + j]) ? data[i + j] : '.');
|
||
} else {
|
||
ret += (' ');
|
||
}
|
||
}
|
||
ret += ('\n');
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
string hexmem(const void *buf, size_t len) {
|
||
string ret;
|
||
char tmp[8];
|
||
const uint8_t *data = (const uint8_t *) buf;
|
||
for (size_t i = 0; i < len; ++i) {
|
||
int sz = sprintf(tmp, "%.2x ", data[i]);
|
||
ret.append(tmp, sz);
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
string exePath(bool isExe /*= true*/) {
|
||
char buffer[PATH_MAX * 2 + 1] = {0};
|
||
int n = -1;
|
||
#if defined(_WIN32)
|
||
n = GetModuleFileNameA(isExe?nullptr:(HINSTANCE)&__ImageBase, buffer, sizeof(buffer));
|
||
#elif defined(__MACH__) || defined(__APPLE__)
|
||
n = sizeof(buffer);
|
||
if (uv_exepath(buffer, &n) != 0) {
|
||
n = -1;
|
||
}
|
||
#elif defined(__linux__)
|
||
n = readlink("/proc/self/exe", buffer, sizeof(buffer));
|
||
#endif
|
||
|
||
string filePath;
|
||
if (n <= 0) {
|
||
filePath = "./";
|
||
} else {
|
||
filePath = buffer;
|
||
}
|
||
|
||
#if defined(_WIN32)
|
||
//windows下把路径统一转换层unix风格,因为后续都是按照unix风格处理的
|
||
for (auto &ch : filePath) {
|
||
if (ch == '\\') {
|
||
ch = '/';
|
||
}
|
||
}
|
||
#endif //defined(_WIN32)
|
||
|
||
return filePath;
|
||
}
|
||
|
||
string exeDir(bool isExe /*= true*/) {
|
||
auto path = exePath(isExe);
|
||
return path.substr(0, path.rfind('/') + 1);
|
||
}
|
||
|
||
string exeName(bool isExe /*= true*/) {
|
||
auto path = exePath(isExe);
|
||
return path.substr(path.rfind('/') + 1);
|
||
}
|
||
|
||
// string转小写
|
||
std::string &strToLower(std::string &str) {
|
||
transform(str.begin(), str.end(), str.begin(), towlower);
|
||
return str;
|
||
}
|
||
|
||
// string转大写
|
||
std::string &strToUpper(std::string &str) {
|
||
transform(str.begin(), str.end(), str.begin(), towupper);
|
||
return str;
|
||
}
|
||
|
||
// string转小写
|
||
std::string strToLower(std::string &&str) {
|
||
transform(str.begin(), str.end(), str.begin(), towlower);
|
||
return std::move(str);
|
||
}
|
||
|
||
// string转大写
|
||
std::string strToUpper(std::string &&str) {
|
||
transform(str.begin(), str.end(), str.begin(), towupper);
|
||
return std::move(str);
|
||
}
|
||
|
||
vector<string> split(const string &s, const char *delim) {
|
||
vector<string> ret;
|
||
size_t last = 0;
|
||
auto index = s.find(delim, last);
|
||
while (index != string::npos) {
|
||
if (index - last > 0) {
|
||
ret.push_back(s.substr(last, index - last));
|
||
}
|
||
last = index + strlen(delim);
|
||
index = s.find(delim, last);
|
||
}
|
||
if (!s.size() || s.size() - last > 0) {
|
||
ret.push_back(s.substr(last));
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
#define TRIM(s, chars) \
|
||
do{ \
|
||
string map(0xFF, '\0'); \
|
||
for (auto &ch : chars) { \
|
||
map[(unsigned char &)ch] = '\1'; \
|
||
} \
|
||
while( s.size() && map.at((unsigned char &)s.back())) s.pop_back(); \
|
||
while( s.size() && map.at((unsigned char &)s.front())) s.erase(0,1); \
|
||
}while(0);
|
||
|
||
//去除前后的空格、回车符、制表符
|
||
std::string &trim(std::string &s, const string &chars) {
|
||
TRIM(s, chars);
|
||
return s;
|
||
}
|
||
|
||
std::string trim(std::string &&s, const string &chars) {
|
||
TRIM(s, chars);
|
||
return std::move(s);
|
||
}
|
||
|
||
void replace(string &str, const string &old_str, const string &new_str,std::string::size_type b_pos) {
|
||
if (old_str.empty() || old_str == new_str) {
|
||
return;
|
||
}
|
||
auto pos = str.find(old_str,b_pos);
|
||
if (pos == string::npos) {
|
||
return;
|
||
}
|
||
str.replace(pos, old_str.size(), new_str);
|
||
replace(str, old_str, new_str,pos + new_str.length());
|
||
}
|
||
|
||
bool start_with(const string &str, const string &substr) {
|
||
return str.find(substr) == 0;
|
||
}
|
||
|
||
bool end_with(const string &str, const string &substr) {
|
||
auto pos = str.rfind(substr);
|
||
return pos != string::npos && pos == str.size() - substr.size();
|
||
}
|
||
|
||
bool isIP(const char *str) {
|
||
return SockUtil::is_ipv4(str) || SockUtil::is_ipv6(str);
|
||
}
|
||
|
||
#if defined(_WIN32)
|
||
void sleep(int second) {
|
||
Sleep(1000 * second);
|
||
}
|
||
void usleep(int micro_seconds) {
|
||
this_thread::sleep_for(std::chrono::microseconds(micro_seconds));
|
||
}
|
||
|
||
int gettimeofday(struct timeval *tp, void *tzp) {
|
||
auto now_stamp = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||
tp->tv_sec = (decltype(tp->tv_sec))(now_stamp / 1000000LL);
|
||
tp->tv_usec = now_stamp % 1000000LL;
|
||
return 0;
|
||
}
|
||
|
||
const char *strcasestr(const char *big, const char *little){
|
||
string big_str = big;
|
||
string little_str = little;
|
||
strToLower(big_str);
|
||
strToLower(little_str);
|
||
auto pos = strstr(big_str.data(), little_str.data());
|
||
if (!pos){
|
||
return nullptr;
|
||
}
|
||
return big + (pos - big_str.data());
|
||
}
|
||
|
||
int vasprintf(char **strp, const char *fmt, va_list ap) {
|
||
// _vscprintf tells you how big the buffer needs to be
|
||
int len = _vscprintf(fmt, ap);
|
||
if (len == -1) {
|
||
return -1;
|
||
}
|
||
size_t size = (size_t)len + 1;
|
||
char *str = (char*)malloc(size);
|
||
if (!str) {
|
||
return -1;
|
||
}
|
||
// _vsprintf_s is the "secure" version of vsprintf
|
||
int r = vsprintf_s(str, len + 1, fmt, ap);
|
||
if (r == -1) {
|
||
free(str);
|
||
return -1;
|
||
}
|
||
*strp = str;
|
||
return r;
|
||
}
|
||
|
||
int asprintf(char **strp, const char *fmt, ...) {
|
||
va_list ap;
|
||
va_start(ap, fmt);
|
||
int r = vasprintf(strp, fmt, ap);
|
||
va_end(ap);
|
||
return r;
|
||
}
|
||
|
||
#endif //WIN32
|
||
|
||
static long s_gmtoff = 0; //时间差
|
||
static onceToken s_token([]() {
|
||
#ifdef _WIN32
|
||
TIME_ZONE_INFORMATION tzinfo;
|
||
DWORD dwStandardDaylight;
|
||
long bias;
|
||
dwStandardDaylight = GetTimeZoneInformation(&tzinfo);
|
||
bias = tzinfo.Bias;
|
||
if (dwStandardDaylight == TIME_ZONE_ID_STANDARD) {
|
||
bias += tzinfo.StandardBias;
|
||
}
|
||
if (dwStandardDaylight == TIME_ZONE_ID_DAYLIGHT) {
|
||
bias += tzinfo.DaylightBias;
|
||
}
|
||
s_gmtoff = -bias * 60; //时间差(分钟)
|
||
#else
|
||
local_time_init();
|
||
s_gmtoff = getLocalTime(time(nullptr)).tm_gmtoff;
|
||
#endif // _WIN32
|
||
});
|
||
|
||
long getGMTOff() {
|
||
return s_gmtoff;
|
||
}
|
||
|
||
static inline uint64_t getCurrentMicrosecondOrigin() {
|
||
#if !defined(_WIN32)
|
||
struct timeval tv;
|
||
gettimeofday(&tv, nullptr);
|
||
return tv.tv_sec * 1000000LL + tv.tv_usec;
|
||
#else
|
||
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||
#endif
|
||
}
|
||
|
||
static atomic<uint64_t> s_currentMicrosecond(0);
|
||
static atomic<uint64_t> s_currentMillisecond(0);
|
||
static atomic<uint64_t> s_currentMicrosecond_system(getCurrentMicrosecondOrigin());
|
||
static atomic<uint64_t> s_currentMillisecond_system(getCurrentMicrosecondOrigin() / 1000);
|
||
|
||
static inline bool initMillisecondThread() {
|
||
static std::thread s_thread([]() {
|
||
setThreadName("stamp thread");
|
||
DebugL << "Stamp thread started";
|
||
uint64_t last = getCurrentMicrosecondOrigin();
|
||
uint64_t now;
|
||
uint64_t microsecond = 0;
|
||
while (true) {
|
||
now = getCurrentMicrosecondOrigin();
|
||
//记录系统时间戳,可回退
|
||
s_currentMicrosecond_system.store(now, memory_order_release);
|
||
s_currentMillisecond_system.store(now / 1000, memory_order_release);
|
||
|
||
//记录流逝时间戳,不可回退
|
||
int64_t expired = now - last;
|
||
last = now;
|
||
if (expired > 0 && expired < 1000 * 1000) {
|
||
//流逝时间处于0~1000ms之间,那么是合理的,说明没有调整系统时间
|
||
microsecond += expired;
|
||
s_currentMicrosecond.store(microsecond, memory_order_release);
|
||
s_currentMillisecond.store(microsecond / 1000, memory_order_release);
|
||
} else if (expired != 0) {
|
||
WarnL << "Stamp expired is abnormal: " << expired;
|
||
}
|
||
//休眠0.5 ms
|
||
usleep(500);
|
||
}
|
||
});
|
||
static onceToken s_token([]() {
|
||
s_thread.detach();
|
||
});
|
||
return true;
|
||
}
|
||
|
||
uint64_t getCurrentMillisecond(bool system_time) {
|
||
static bool flag = initMillisecondThread();
|
||
if (system_time) {
|
||
return s_currentMillisecond_system.load(memory_order_acquire);
|
||
}
|
||
return s_currentMillisecond.load(memory_order_acquire);
|
||
}
|
||
|
||
uint64_t getCurrentMicrosecond(bool system_time) {
|
||
static bool flag = initMillisecondThread();
|
||
if (system_time) {
|
||
return s_currentMicrosecond_system.load(memory_order_acquire);
|
||
}
|
||
return s_currentMicrosecond.load(memory_order_acquire);
|
||
}
|
||
|
||
string getTimeStr(const char *fmt, time_t time) {
|
||
if (!time) {
|
||
time = ::time(nullptr);
|
||
}
|
||
auto tm = getLocalTime(time);
|
||
size_t size = strlen(fmt) + 64;
|
||
string ret;
|
||
ret.resize(size);
|
||
size = std::strftime(&ret[0], size, fmt, &tm);
|
||
if (size > 0) {
|
||
ret.resize(size);
|
||
}
|
||
else{
|
||
ret = fmt;
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
|
||
struct tm getLocalTime(time_t sec) {
|
||
struct tm tm;
|
||
#ifdef _WIN32
|
||
localtime_s(&tm, &sec);
|
||
#else
|
||
no_locks_localtime(&tm, sec);
|
||
#endif //_WIN32
|
||
return tm;
|
||
}
|
||
|
||
static thread_local string thread_name;
|
||
|
||
static string limitString(const char *name, size_t max_size) {
|
||
string str = name;
|
||
if (str.size() + 1 > max_size) {
|
||
auto erased = str.size() + 1 - max_size + 3;
|
||
str.replace(5, erased, "...");
|
||
}
|
||
return str;
|
||
}
|
||
|
||
void setThreadName(const char *name) {
|
||
assert(name);
|
||
#if defined(__linux) || defined(__linux__) || defined(__MINGW32__)
|
||
pthread_setname_np(pthread_self(), limitString(name, 16).data());
|
||
#elif defined(__MACH__) || defined(__APPLE__)
|
||
pthread_setname_np(limitString(name, 32).data());
|
||
#elif defined(_MSC_VER)
|
||
// SetThreadDescription was added in 1607 (aka RS1). Since we can't guarantee the user is running 1607 or later, we need to ask for the function from the kernel.
|
||
using SetThreadDescriptionFunc = HRESULT(WINAPI * )(_In_ HANDLE hThread, _In_ PCWSTR lpThreadDescription);
|
||
static auto setThreadDescription = reinterpret_cast<SetThreadDescriptionFunc>(::GetProcAddress(::GetModuleHandle("Kernel32.dll"), "SetThreadDescription"));
|
||
if (setThreadDescription) {
|
||
// Convert the thread name to Unicode
|
||
wchar_t threadNameW[MAX_PATH];
|
||
size_t numCharsConverted;
|
||
errno_t wcharResult = mbstowcs_s(&numCharsConverted, threadNameW, name, MAX_PATH - 1);
|
||
if (wcharResult == 0) {
|
||
HRESULT hr = setThreadDescription(::GetCurrentThread(), threadNameW);
|
||
if (!SUCCEEDED(hr)) {
|
||
int i = 0;
|
||
i++;
|
||
}
|
||
}
|
||
} else {
|
||
// For understanding the types and values used here, please see:
|
||
// https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code
|
||
|
||
const DWORD MS_VC_EXCEPTION = 0x406D1388;
|
||
#pragma pack(push, 8)
|
||
struct THREADNAME_INFO {
|
||
DWORD dwType = 0x1000; // Must be 0x1000
|
||
LPCSTR szName; // Pointer to name (in user address space)
|
||
DWORD dwThreadID; // Thread ID (-1 for caller thread)
|
||
DWORD dwFlags = 0; // Reserved for future use; must be zero
|
||
};
|
||
#pragma pack(pop)
|
||
|
||
THREADNAME_INFO info;
|
||
info.szName = name;
|
||
info.dwThreadID = (DWORD) - 1;
|
||
|
||
__try{
|
||
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), reinterpret_cast<const ULONG_PTR *>(&info));
|
||
} __except(GetExceptionCode() == MS_VC_EXCEPTION ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_EXECUTE_HANDLER) {
|
||
}
|
||
}
|
||
#else
|
||
thread_name = name ? name : "";
|
||
#endif
|
||
}
|
||
|
||
string getThreadName() {
|
||
#if ((defined(__linux) || defined(__linux__)) && !defined(ANDROID)) || (defined(__MACH__) || defined(__APPLE__)) || (defined(ANDROID) && __ANDROID_API__ >= 26) || defined(__MINGW32__)
|
||
string ret;
|
||
ret.resize(32);
|
||
auto tid = pthread_self();
|
||
pthread_getname_np(tid, (char *) ret.data(), ret.size());
|
||
if (ret[0]) {
|
||
ret.resize(strlen(ret.data()));
|
||
return ret;
|
||
}
|
||
return to_string((uint64_t) tid);
|
||
#elif defined(_MSC_VER)
|
||
using GetThreadDescriptionFunc = HRESULT(WINAPI * )(_In_ HANDLE hThread, _In_ PWSTR * ppszThreadDescription);
|
||
static auto getThreadDescription = reinterpret_cast<GetThreadDescriptionFunc>(::GetProcAddress(::GetModuleHandleA("Kernel32.dll"), "GetThreadDescription"));
|
||
|
||
if (!getThreadDescription) {
|
||
std::ostringstream ss;
|
||
ss << std::this_thread::get_id();
|
||
return ss.str();
|
||
} else {
|
||
PWSTR data;
|
||
HRESULT hr = getThreadDescription(GetCurrentThread(), &data);
|
||
if (SUCCEEDED(hr) && data[0] != '\0') {
|
||
char threadName[MAX_PATH];
|
||
size_t numCharsConverted;
|
||
errno_t charResult = wcstombs_s(&numCharsConverted, threadName, data, MAX_PATH - 1);
|
||
if (charResult == 0) {
|
||
LocalFree(data);
|
||
std::ostringstream ss;
|
||
ss << threadName;
|
||
return ss.str();
|
||
} else {
|
||
if (data) {
|
||
LocalFree(data);
|
||
}
|
||
return to_string((uint64_t) GetCurrentThreadId());
|
||
}
|
||
} else {
|
||
if (data) {
|
||
LocalFree(data);
|
||
}
|
||
return to_string((uint64_t) GetCurrentThreadId());
|
||
}
|
||
}
|
||
#else
|
||
if (!thread_name.empty()) {
|
||
return thread_name;
|
||
}
|
||
std::ostringstream ss;
|
||
ss << std::this_thread::get_id();
|
||
return ss.str();
|
||
#endif
|
||
}
|
||
|
||
bool setThreadAffinity(int i) {
|
||
#if (defined(__linux) || defined(__linux__)) && !defined(ANDROID)
|
||
cpu_set_t mask;
|
||
CPU_ZERO(&mask);
|
||
if (i >= 0) {
|
||
CPU_SET(i, &mask);
|
||
} else {
|
||
for (auto j = 0u; j < thread::hardware_concurrency(); ++j) {
|
||
CPU_SET(j, &mask);
|
||
}
|
||
}
|
||
if (!pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask)) {
|
||
return true;
|
||
}
|
||
WarnL << "pthread_setaffinity_np failed: " << get_uv_errmsg();
|
||
#endif
|
||
return false;
|
||
}
|
||
|
||
#ifndef HAS_CXA_DEMANGLE
|
||
// We only support some compilers that support __cxa_demangle.
|
||
// TODO: Checks if Android NDK has fixed this issue or not.
|
||
#if defined(__ANDROID__) && (defined(__i386__) || defined(__x86_64__))
|
||
#define HAS_CXA_DEMANGLE 0
|
||
#elif (__GNUC__ >= 4 || (__GNUC__ >= 3 && __GNUC_MINOR__ >= 4)) && \
|
||
!defined(__mips__)
|
||
#define HAS_CXA_DEMANGLE 1
|
||
#elif defined(__clang__) && !defined(_MSC_VER)
|
||
#define HAS_CXA_DEMANGLE 1
|
||
#else
|
||
#define HAS_CXA_DEMANGLE 0
|
||
#endif
|
||
#endif
|
||
#if HAS_CXA_DEMANGLE
|
||
#include <cxxabi.h>
|
||
#endif
|
||
|
||
// Demangle a mangled symbol name and return the demangled name.
|
||
// If 'mangled' isn't mangled in the first place, this function
|
||
// simply returns 'mangled' as is.
|
||
//
|
||
// This function is used for demangling mangled symbol names such as
|
||
// '_Z3bazifdPv'. It uses abi::__cxa_demangle() if your compiler has
|
||
// the API. Otherwise, this function simply returns 'mangled' as is.
|
||
//
|
||
// Currently, we support only GCC 3.4.x or later for the following
|
||
// reasons.
|
||
//
|
||
// - GCC 2.95.3 doesn't have cxxabi.h
|
||
// - GCC 3.3.5 and ICC 9.0 have a bug. Their abi::__cxa_demangle()
|
||
// returns junk values for non-mangled symbol names (ex. function
|
||
// names in C linkage). For example,
|
||
// abi::__cxa_demangle("main", 0, 0, &status)
|
||
// returns "unsigned long" and the status code is 0 (successful).
|
||
//
|
||
// Also,
|
||
//
|
||
// - MIPS is not supported because abi::__cxa_demangle() is not defined.
|
||
// - Android x86 is not supported because STLs don't define __cxa_demangle
|
||
//
|
||
string demangle(const char *mangled) {
|
||
int status = 0;
|
||
char *demangled = nullptr;
|
||
#if HAS_CXA_DEMANGLE
|
||
demangled = abi::__cxa_demangle(mangled, nullptr, nullptr, &status);
|
||
#endif
|
||
string out;
|
||
if (status == 0 && demangled) { // Demangling succeeeded.
|
||
out.append(demangled);
|
||
#ifdef ASAN_USE_DELETE
|
||
delete [] demangled; // 开启asan后,用free会卡死
|
||
#else
|
||
free(demangled);
|
||
#endif
|
||
} else {
|
||
out.append(mangled);
|
||
}
|
||
return out;
|
||
}
|
||
|
||
string getEnv(const string &key) {
|
||
auto ekey = key.c_str();
|
||
if (*ekey == '$') {
|
||
++ekey;
|
||
}
|
||
auto value = *ekey ? getenv(ekey) : nullptr;
|
||
return value ? value : "";
|
||
}
|
||
|
||
|
||
void Creator::onDestoryException(const type_info &info, const exception &ex) {
|
||
ErrorL << "Invoke " << demangle(info.name()) << "::onDestory throw a exception: " << ex.what();
|
||
}
|
||
|
||
} // namespace toolkit
|
||
|
||
|
||
extern "C" {
|
||
void Assert_Throw(int failed, const char *exp, const char *func, const char *file, int line, const char *str) {
|
||
if (failed) {
|
||
toolkit::_StrPrinter printer;
|
||
printer << "Assertion failed: (" << exp ;
|
||
if(str && *str){
|
||
printer << ", " << str;
|
||
}
|
||
printer << "), function " << func << ", file " << file << ", line " << line << ".";
|
||
throw toolkit::AssertFailedException(printer);
|
||
}
|
||
}
|
||
} |