#include "base.h" #include ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // refer refer::refer() : ref_(1) { on_born(); } refer::~refer() { on_dead(); } void refer::on_born(void) {} void refer::on_dead(void) {} int32_t refer::add_ref(void) { LOCKER lock(mutex_); return ++ref_; } int32_t refer::release(void) { int32_t ref = 0; { LOCKER lock(mutex_); ref = --ref_; } if (ref == 0) delete this; return ref; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // chronograph #include chronograph::chronograph() { reset(); } chronograph::~chronograph() {} bool chronograph::now(struct timeval* tv) { struct timezone tz = { 0 }; return gettimeofday(tv, &tz) == 0; } bool chronograph::now(uint64_t* seconds, uint64_t* u_seconds) { struct timeval tv = { 0 }; struct timezone tz = { 0 }; if (gettimeofday(&tv, &tz) == 0) { if (seconds) *seconds = tv.tv_sec; if (u_seconds) *u_seconds = tv.tv_usec; return true; } else { return false; } } std::string chronograph::now(bool with_ms/*whether with milliseconds*/) // return '2022-11-30 10:38:42.123', no '.123' if with_ms was false { struct timeval tv = { 0 }; if (!chronograph::now(&tv)) return ""; char buf[40] = { 0 }; time_t t = tv.tv_sec; struct tm* l = localtime(&t); if (with_ms) sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d.%06d", l->tm_year + 1900, l->tm_mon + 1, l->tm_mday , l->tm_hour, l->tm_min, l->tm_sec, tv.tv_usec); else sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d", l->tm_year + 1900, l->tm_mon + 1, l->tm_mday , l->tm_hour, l->tm_min, l->tm_sec); return buf; } uint64_t chronograph::elapse_s(void) { struct timeval tv = { 0 }; chronograph::now(&tv); return tv.tv_sec - bgn_.tv_sec; } uint64_t chronograph::elapse_ms(void) { struct timeval tv = { 0 }; uint64_t dif = 0; chronograph::now(&tv); dif = SEC_2_MS(tv.tv_sec - bgn_.tv_sec); dif += tv.tv_usec / MSEC_2_US(1); dif -= bgn_.tv_usec / MSEC_2_US(1); return dif; } uint64_t chronograph::elapse_us(void) { struct timeval tv = { 0 }; uint64_t dif = 0; chronograph::now(&tv); dif = SEC_2_US(tv.tv_sec - bgn_.tv_sec); dif += tv.tv_usec; dif -= bgn_.tv_usec; return dif; } void chronograph::reset() { chronograph::now(&bgn_); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // sys utility #include #include #include #include namespace sys_util { static bool find_module(const char* path, bool is_dir, void* param) { std::string* para = (std::string*)param; if (para[0].empty()) { const char* now = getenv("PWD"); bool found = strstr(path, now) == path; if(found) { found = path[strlen(now)] == '/' && strstr(path + strlen(now) + 1, "/") == nullptr; } if(found || para[1].empty()) para[1] = path; return !found; } else { const char* name = strrchr(path, '/'); if (name++ == nullptr) name = path; if (strstr(name, para[0].c_str())) { para[1] = path; return false; } return true; } } typedef struct _enum_proc_cb { bool process; bool(*on_found)(uint64_t pid, const char* path_name, void* param); void* param; }ENPROCCB, * LPENPROCCB; static bool found_process(const char* path, bool is_dir, void* param) { LPENPROCCB cb = (LPENPROCCB)param; const char* id = strrchr(path, '/'); uint64_t pid = 0; std::string path_name(""); if (id++ == nullptr) { id = path; } while (*id) { if (*id < '0' || *id > '9') break; pid *= 10; pid += *id - '0'; id++; } if (*id) return true; if (cb->process) { path_name = get_module_path(nullptr, pid); id = path_name.c_str(); } else { // get start address of pid to path_name id = 0; } return cb->on_found(pid, id, cb->param); } static bool on_stack_line_read(char* line, void* param) { LPENPROCCB cb = (LPENPROCCB)param; std::string m(line); uint64_t off = 0; size_t pos = m.find("]"); if (pos++ != std::string::npos) m.erase(0, pos); trim_left(m); pos = m.find("+0x"); if (pos != std::string::npos) { off = from_hex_str(m.c_str() + pos + 3); m.erase(pos); } return cb->on_found(off, m.c_str(), cb->param); } int32_t enum_modules(bool(*on_found)(const char* path_module_name, bool is_dir, void* param),// return false to stop enumeratin void* param, // user defined data, passed into callback on_found unsigned pid // process id, -1 is self ) // return errno { char path[128] = { 0 }; if (pid == -1) pid = getpid(); sprintf(path, "/proc/%u/map_files/", pid); return enum_files(path, on_found, param, false); } int32_t enum_files(const char* dir, // dir path bool(*on_found)(const char* path_name, bool is_dir, void* param), // return false to stop enumeratin void* param // user defined data, passed into callback on_found , bool recursive ) // return errno { int32_t ret = 0; DIR* pdir = nullptr; struct dirent* ent = nullptr; pdir = opendir(dir); if (!pdir) return errno; while ((ent = readdir(pdir))) { if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) continue; std::string file(dir); file += "/"; file += ent->d_name; if ((ent->d_type & DT_DIR) == 0) { file = read_link(file.c_str()); if(file.empty()) file = std::string(dir) + "/" + ent->d_name; } if (!on_found(file.c_str(), ent->d_type & DT_DIR, param)) { ret = 0x5e17; break; } if ((ent->d_type & DT_DIR) && recursive) { std::string sub(dir); sub += "/"; sub += ent->d_name; ret = enum_files(sub.c_str(), on_found, param, recursive); if (ret == 0x5e17) break; } } closedir(pdir); return ret == 0x5e17 ? 0 : ret; } int32_t enum_processes(bool(*on_found)(uint64_t pid, const char* path_name, void* param), void* param) { ENPROCCB cb; cb.on_found = on_found; cb.param = param; cb.process = true; return enum_files("/proc", found_process, &cb, false); } uint32_t enum_threads(uint64_t pid, bool(*on_found)(uint64_t tid, void* start_addr, void* param), void* param) { ENPROCCB cb; cb.process = false; cb.param = param; *(void**)&cb.on_found = *(void**)&on_found; return enum_files(("/proc/" + std::to_string(pid) + "/task").c_str(), found_process, &cb, false); } uint32_t get_thread_callstack(uint64_t pid, uint64_t tid, bool(*on_found)(uint64_t off, const char* module, void* param), void* param) { ENPROCCB cb; cb.process = false; cb.param = param; *(void**)&cb.on_found = *(void**)&on_found; return read_line(("/proc/" + std::to_string(pid) + "/task/" + std::to_string(tid) + "/stack").c_str(), on_stack_line_read, &cb); } uint32_t read_line(const char* file, bool(*on_line)(char* line, void* param), void* param) { FILE* src = fopen(file, "rb"); int err = 0; if (!src) return errno; size_t ll = 1024; char* line = new char[ll]; while (fgets(line, ll - 1, src)) { if (!on_line(line, param)) break; memset(line, 0, ll); } err = errno; delete[] line; fclose(src); return err; } std::string get_module_path(const char* module_name, unsigned pid) // get module full path, nullptr is for main-exe { std::string param[] = { module_name ? module_name : "", "" }; enum_modules(find_module, param, pid); return param[1]; } std::string read_link(const char* lnk) { char path[512] = { 0 }; readlink(lnk, path, sizeof(path) - 1); return path; } size_t get_page_size(void) { size_t size = sysconf(_SC_PAGESIZE); if (size < 1024 || (size & 0x0fe0000ff)) // nKB && < 16MB size = getpagesize(); return size; } bool create_folder(const char* dir) { bool ret = mkdir(dir, S_IREAD | S_IWRITE | S_IEXEC) == 0 || errno == EEXIST; if(errno == ENOENT) { std::string path(dir), cur(""); size_t pos = path.find("/", 1); while(pos != std::string::npos) { ret = mkdir(path.substr(0, pos).c_str(), S_IREAD | S_IWRITE | S_IEXEC) == 0 || errno == EEXIST; if(!ret) { printf("mkdir(%s) = %d(%s)\n", path.substr(0, pos).c_str(), errno, strerror(errno)); break; } pos = path.find("/", pos + 1); } if(ret) ret = mkdir(path.c_str(), S_IREAD | S_IWRITE | S_IEXEC) == 0; } return ret; } int32_t get_memory_info(uint64_t* total, uint64_t* available) { if (!total && !available) return 0; char line[128] = { 0 }; FILE* src = fopen("/proc/meminfo", "rb"); int32_t count = total && available ? 2 : 1; unsigned long val = 0; if (!src) return errno; while (fgets(line, sizeof(line) - 1, src)) { if (sscanf(line, "MemTotal: %ld", &val)) { if (total) { *total = val * 1024; if (--count == 0) break; } } else if (sscanf(line, "MemFree: %ld", &val)) { if (available) { *available = val * 1024; if (--count == 0) break; } } } fclose(src); return 0; } std::string format_readable_bytes(uint64_t bytes) { std::string str("\0", 80); if (bytes >= SIZE_GB(1)) { double v = bytes * 1.0f / (SIZE_GB(1)); size_t pos = 0; sprintf(&str[0], "%.2fGB", v); pos = str.find("."); while (pos > 3) { pos -= 3; str.insert(pos, ","); } } else if (bytes >= SIZE_MB(1)) { double v = bytes * 1.0f / (SIZE_MB(1)); sprintf(&str[0], "%.2fMB", v); } else if (bytes >= SIZE_KB(1)) { double v = bytes * 1.0f / (SIZE_KB(1)); sprintf(&str[0], "%.2fKB", v); } else { sprintf(&str[0], "%uB", (unsigned)bytes); } return str; } std::string get_command_output(const char* cmd, uint16_t max_line_len, bool one_line) { FILE* src = popen(cmd, "r"); std::string ret(""); if (src) { char* buf = new char[max_line_len + 4]; if (buf) { memset(buf, 0, max_line_len + 4); fgets(buf, max_line_len, src); ret = buf; while (!one_line && fgets(buf, max_line_len, src)) ret += "\n" + std::string(buf); delete[] buf; } pclose(src); } return ret; } static bool is_char_in(const char* str, char ch) { if (ch == 0) return false; while (*str) { if (*str++ == ch) return true; } return false; } bool trim_left(std::string& str, const char* space) { int off = 0; for (; off < str.length(); ++off) { if (!is_char_in(space, str[off])) break; } if (off) str.erase(0, off); return off > 0; } bool trim_right(std::string& str, const char* space) { int off = str.length() - 1; for (; off >= 0; --off) { if (!is_char_in(space, str[off])) break; } if (off < str.length() - 1) { str.erase(off + 1); return true; } return false; } uint64_t from_hex_str(const char* hex, const char** end) { uint64_t val = 0; for (int i = 0; i < 16; ++i) { if (*hex >= '0' && *hex <= '9') { val <<= 4; val += *hex - '0'; } else if (*hex >= 'a' && *hex <= 'f') { val <<= 4; val += *hex - 'a' + 10; } else if (*hex >= 'A' && *hex <= 'F') { val <<= 4; val += *hex - 'A' + 10; } else { break; } hex++; } if (end) *end = hex; return val; } static uint64_t from_dec_str(const char* str, const char** end) { uint64_t val = 0; for(int i = 0; *str && i < 19; ++i, ++str) { if(*str >= '0' && *str <= '9') { val *= 10; val += *str - '0'; } else break; } if(end) *end = str; return val; } static uint64_t from_oct_str(const char* str, const char** end) { uint64_t val = 0; for(int i = 0; *str && i < 21; ++i, ++str) { if(*str >= '0' && *str <= '7') { val *= 8; val += *str - '0'; } else break; } if(end) *end = str; return val; } static uint64_t from_bin_str(const char* str, const char** end) { uint64_t val = 0; for(int i = 0; *str && i < 64; ++i, ++str) { if(*str >= '0' && *str <= '1') { val *= 2; val += *str - '0'; } else break; } if(end) *end = str; return val; } uint64_t to_int(const char* str, const char** end) { if(!str || *str == 0) { if(end) *end = str; return 0; } if(strstr(str, "0x") == str) return from_hex_str(str + 2, end); if(str[strlen(str) - 1] == 'h' || str[strlen(str) - 1] == 'H') return from_hex_str(str, end); if(str[strlen(str) - 1] == 'o' || str[strlen(str) - 1] == 'O') return from_oct_str(str, end); if(str[strlen(str) - 1] == 'b' || str[strlen(str) - 1] == 'B') return from_bin_str(str, end); bool hex = false; for(int i = 0; str[i]; ++i) { if(str[i] < '0') break; if(str[i] > '9') { if((str[i] >= 'a' && str[i] <= 'f') || (str[i] >= 'A' && str[i] <= 'F')) hex = true; break; } } return hex ? from_hex_str(str, end) : from_dec_str(str, end); } /* iconv_open language options: Europe: ASCII, ISO-8859-{1,2,3,4,5,7,9,10,13,14,15,16}, KOI8-R, KOI8-U, KOI8-RU, CP{1250,1251,1252,1253,1254,1257}, CP{850,866}, Mac{Roman,CentralEurope,Iceland,Croatian,Romania}, Mac{Cyrillic,Ukraine,Greek,Turkish}, Macintosh Semitic: ISO-8859-{6,8}, CP{1255,1256}, CP862, Mac{Hebrew,Arabic} Janpanese: EUC-JP, SHIFT-JIS, CP932, ISO-2022-JP, ISO-2022-JP-2, ISO-2022-JP-1 Chinese: EUC-CN, HZ, GBK, GB18030, EUC-TW, BIG5, CP950, BIG5-HKSCS, ISO-2022-CN, ISO-2022-CN-EXT Korean: EUC-KR, CP949, ISO-2022-KR, JOHAB Armenian: ARMSCII-8 Georgian: Georgian-Academy, Georgian-PS Thai: TIS-620, CP874, MacThai Laos: MuleLao-1, CP1133 Vietnam: VISCII, TCVN, CP1258 Special: HP-ROMAN8, NEXTSTEP Full Unicode: UTF-8 UCS-2, UCS-2BE, UCS-2LE UCS-4, UCS-4BE, UCS-4LE UTF-16, UTF-16BE, UTF-16LE UTF-32, UTF-32BE, UTF-32LE UTF-7 JAVA */ std::string transform_between_gbk_and_utf8(const char* in, bool to_utf8, int *err) { size_t len = strlen(in) + 8, ol = len * 2; char *buf = (char*)malloc(len), *oper = buf, *out = nullptr, *oper1 = nullptr; iconv_t conv; memset(buf, 0, len); strcpy(buf, in); if(to_utf8) conv = iconv_open("UTF-8", "GBK"); else conv = iconv_open("GBK", "UTF-8"); if(conv == (iconv_t)-1) { if(err) *err = errno; free(buf); return ""; } oper1 = out = (char*)malloc(ol); memset(out, 0, ol); len -= 8; if(iconv(conv, &oper, &len, &oper1, &ol)) { if(err) *err = errno; } else if(err) *err = 0; std::string ret(out); free(buf); free(out); iconv_close(conv); return std::move(ret); } std::string transform_between_utf16_and_utf8(const char* in, size_t bytes, bool to_utf8) { static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; std::string ret(""); if(to_utf8) { unsigned short *uc = *(unsigned short**)&in, val = 0; for(size_t i = 0; i < bytes / 2; ++i, ++uc) { val = *uc; if ((*uc>=0xDC00 && *uc<=0xDFFF) || *uc==0) break; /* check for invalid. */ if (*uc>=0xD800 && *uc<=0xDBFF) /* UTF16 surrogate pairs. */ { unsigned short uc2 = uc[1]; if (uc2 < 0xDC00 || uc2 > 0xDFFF) break; /* invalid second-half of surrogate. */ val = 0x10000 + (((*uc & 0x3FF) << 10) | (uc2 & 0x3FF)); } int len = 4; char buf[4] = {0}, *ptr2 = buf; if (val<0x80) len=1;else if (val<0x800) len=2;else if (val<0x10000) len=3; ptr2+=len; switch (len) { case 4: *--ptr2 =((val | 0x80) & 0xBF); val >>= 6; case 3: *--ptr2 =((val | 0x80) & 0xBF); val >>= 6; case 2: *--ptr2 =((val | 0x80) & 0xBF); val >>= 6; case 1: *--ptr2 =(val | firstByteMark[len]); } ret += std::string(buf, len); } } else { char* unic = (char*)malloc(strlen(in) * 3 + 8); unsigned char * cur = (unsigned char*)unic; while (*cur = *in++) { if ((*cur & 0x0f0) == 0x0e0) { if (((unsigned char)in[0] & 0x0c0) == 0x80 && ((unsigned char)in[1] & 0x0c0) == 0x80) { char* hex = "0123456789ABCDEF"; unsigned short us = *cur & 0x0f; us <<= 6; us += in[0] & 0x3f; us <<= 6; us += in[1] & 0x3f; *cur++ = '\\'; *cur++ = 'u'; cur[3] = hex[us & 0x0f]; us >>= 4; cur[2] = hex[us & 0x0f]; us >>= 4; cur[1] = hex[us & 0x0f]; us >>= 4; cur[0] = hex[us & 0x0f]; cur += 3; in += 2; } } cur++; } *cur++ = 0; ret = std::string(unic, (char*)cur - unic); free(unic); } return std::move(ret); } std::string to_abs_path(const char* base, const char* rel_path) { if(*rel_path == '/') return rel_path; std::string b(base), r(rel_path); size_t pos = 0; while(b.length() && b[b.length() - 1] == '/') b.erase(b.length() - 1); while(r[0] == '.') { if(r[1] == '/') r.erase(0, 2); else if(r[1] == '.' && r[2] == '/') { pos = b.rfind('/'); if(pos == std::string::npos) break; b.erase(pos); r.erase(0, 3); } else break; } b += "/" + r; return std::move(b); } std::string to_rel_path(const char* base, const char* abs_path) { if(*abs_path != '/') return abs_path; std::string rel(""), b(base), a(abs_path); size_t pos = 0; while(b.length() && b[b.length() - 1] == '/') b.erase(b.length() - 1); while(a.find(b) == std::string::npos) { rel += "../"; pos = b.rfind('/'); if(pos == 0) break; b.erase(pos - 1); } if(rel.empty()) rel = "./"; return std::move(rel + a); } const char* pick_simple_block(const char* head, char end) { const char* tail = head + 1; int cnt = 1; while(*tail) { if(*tail == end) { cnt--; if(cnt == 0) break; } else if(*tail == *head) { cnt++; } else if(*tail == '\\') { tail++; if(*tail == 0) break; } tail++; } return cnt == 0 ? tail : nullptr; } }