/* * Copyright 2011, 2014 Range Networks, Inc. * All Rights Reserved. * * This software is distributed under multiple licenses; * see the COPYING file in the main directory for licensing * information for this specific distribution. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #include // For usleep #include // For gettimeofday #include // For vsnprintf #include // For ostream #include // For ostringstream #include // For strcpy #include // For malloc #include // For backtrace #include "Utils.h" #include "MemoryLeak.h" namespace Utils { using namespace std; struct stringCaseInsensitive : public string { bool operator==(string &other) { return 0==strcasecmp(this->c_str(),other.c_str()); } bool operator!=(string &other) { return 0!=strcasecmp(this->c_str(),other.c_str()); } stringCaseInsensitive(string &other) : string(other) {} stringCaseInsensitive(const char* &other) : string(other) {} }; // (pat) This definition must be in the .cpp file to anchor the class vtable. RefCntBase::~RefCntBase() { LOG(DEBUG) << typeid(this).name(); } int RefCntBase::decRefCnt() const { int saveRefCnt; // Passes the refcnt out of the locked block. { ScopedLock lock(mRefMutex); assert(mRefCnt >= 0); mRefCnt = mRefCnt - 1; saveRefCnt = mRefCnt; } // Must not keep locked during the delete, since the Mutex itself will be deleted. // The typeid(this).name() doesnt add anything because it is just the name of the class here, not the derived class. LOG(DEBUG) <<" "<<(void*)this <<" " <= 0); mRefCnt++; } MemStats gMemStats; int gMemLeakDebug = 0; static Mutex memChkLock; MemStats::MemStats() { memset(mMemNow,0,sizeof(mMemNow)); memset(mMemTotal,0,sizeof(mMemTotal)); memset(mMemName,0,sizeof(mMemName)); } void MemStats::text(ostream &os) { os << "Structs current total:\n"; for (int i = 0; i < mMax; i++) { os << "\t" << (mMemName[i] ? mMemName[i] : "unknown") << " " << mMemNow[i] << " " << mMemTotal[i] << "\n"; } } void MemStats::memChkNew(MemoryNames memIndex, const char *id) { /*cout << "new " #type "\n";*/ ScopedLock lock(memChkLock); mMemNow[memIndex]++; mMemTotal[memIndex]++; mMemName[memIndex] = id; } void MemStats::memChkDel(MemoryNames memIndex, const char *id) { ScopedLock lock(memChkLock); /*cout << "del " #type "\n";*/ mMemNow[memIndex]--; if (gMemLeakDebug && mMemNow[memIndex] < 0) { // (pat) This message can happen for some classes because the decrement above is not mutex protected; // it is only for debugging purposes, not to cause alarm, so take it out. LOG(DEBUG) << "Memory reference count underflow on type "<= (300-4)) { strcpy(&buf[(300-4)],"..."); } os << buf; return os; } string format(const char *fmt, ...) { va_list ap; char buf[200]; va_start(ap,fmt); int n = vsnprintf(buf,199,fmt,ap); va_end(ap); string result; if (n <= 199) { result = string(buf); } else { if (n > 5000) { LOG(ERR) << "oversized string in format"; n = 5000; } // We could use vasprintf but we already computed the length... // We are not using alloca because it might overflow the small stacks used for our threads. char *buffer = (char*)malloc(n+2); // add 1 extra superstitiously. va_start(ap,fmt); vsnprintf(buffer,n+1,fmt,ap); va_end(ap); //if (n >= (2000-4)) { strcpy(&buf[(2000-4)],"..."); } result = string(buffer); free(buffer); } return result; #if 0 // Maybe ok, but not recommended. data() is const char* string result; va_list ap; va_start(ap,fmt); result.reserve(200); int n = vsnprintf(result.data(),198,fmt,ap); va_end(ap); if (n > 198) { if (n > 5000) { LOG(ERR) << "oversized string in format"; n = 5000; } result.reserve(n+2); // add 1 extra superstitiously. va_start(ap,fmt); vsnprintf(result.data(),n+1,fmt,ap); va_end(ap); } result.resize(n); return result; #endif } // Absolutely identical to format above. This sucks... string format1(const char *fmt, ...) { va_list ap; char buf[200]; va_start(ap,fmt); int n = vsnprintf(buf,199,fmt,ap); va_end(ap); string result; if (n <= 199) { result = string(buf); } else { if (n > 5000) { LOG(ERR) << "oversized string in format"; n = 5000; } // We could use vasprintf but we already computed the length... // We are not using alloca because it might overflow the small stacks used for our threads. char *buffer = (char*)malloc(n+2); // add 1 extra superstitiously. va_start(ap,fmt); vsnprintf(buffer,n+1,fmt,ap); va_end(ap); //if (n >= (2000-4)) { strcpy(&buf[(2000-4)],"..."); } result = string(buffer); free(buffer); } return result; } int myscanf(const char *str, const char *fmt, string *s1) { int maxlen = strlen(str)+1; char *a1 = (char*)alloca(maxlen); int n = sscanf(str,fmt,a1); s1->assign(a1); return n; } int myscanf(const char *str, const char *fmt, string *s1, string *s2) { int maxlen = strlen(str)+1; char *a1 = (char*)alloca(maxlen); char *a2 = (char*)alloca(maxlen); int n = sscanf(str,fmt,a1,a2); switch (n) { case 2: s2->assign(a2); case 1: s1->assign(a1); } return n; } int myscanf(const char *str, const char *fmt, string *s1, string *s2, string *s3) { int maxlen = strlen(str)+1; char *a1 = (char*)alloca(maxlen); char *a2 = (char*)alloca(maxlen); char *a3 = (char*)alloca(maxlen); int n = sscanf(str,fmt,a1,a2,a3); switch (n) { case 3: s3->assign(a3); case 2: s2->assign(a2); case 1: s1->assign(a1); } return n; } int myscanf(const char *str, const char *fmt, string *s1, string *s2, string *s3, string *s4) { int maxlen = strlen(str)+1; char *a1 = (char*)alloca(maxlen); char *a2 = (char*)alloca(maxlen); char *a3 = (char*)alloca(maxlen); char *a4 = (char*)alloca(maxlen); int n = sscanf(str,fmt,a1,a2,a3,a4); switch (n) { case 4: s4->assign(a4); case 3: s3->assign(a3); case 2: s2->assign(a2); case 1: s1->assign(a1); } return n; } #if 0 string format(const char *fmt, string s1) { return format(fmt,s1.c_str()); } string format(const char *fmt, string s1, string s2) { return format(fmt,s1.c_str(),s2.c_str()); } string format(const char *fmt, string s1, string s2, string s3) { return format(fmt,s1.c_str(),s2.c_str(),s3.c_str()); } string format(const char *fmt, string s1, int i1) { return format(fmt,s1.c_str(),i1); } string format(const char *fmt, int i1, string s1) { return format(fmt,i1,s1.c_str()); } string format(const char *fmt, string s1, string s2, int i1) { return format(fmt,s1.c_str(),s2.c_str(),i1); } string format(const char *fmt, string s1, string s2, int i1, int i2) { return format(fmt,s1.c_str(),s2.c_str(),i1,i2); } #endif // Return time in seconds with high resolution. // Note: In the past I found this to be a surprisingly expensive system call in linux. double timef() { struct timeval tv; gettimeofday(&tv,NULL); return tv.tv_usec / 1000000.0 + tv.tv_sec; } const string timestr(unsigned fieldwidth, bool addDate) // Use to pick the number of chars in the output. { struct timeval tv; struct tm tm; gettimeofday(&tv,NULL); localtime_r(&tv.tv_sec,&tm); unsigned tenths = tv.tv_usec / 100000; // Rounding down is ok. string result; if (addDate) // ISO time but with a fractional seconds number result = format(" %04d-%02d-%02dT%02d:%02d:%02d.%1d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tenths); else result = format(" %02d:%02d:%02d.%1d",tm.tm_hour,tm.tm_min,tm.tm_sec,tenths); return result.substr(fieldwidth >= result.size() ? 0 : result.size() - fieldwidth); //switch (maxfield) { //case 'h': case 'H': return format("%02d:%02d:%02d.%1d",tm.tm_hour,tm.tm_min,tm.tm_sec,tenths); //case 'm': case 'M': return format("%02d:%02d.%1d",tm.tm_min,tm.tm_sec,tenths); //case 's': case 'S': return format("%02d.%1d",tm.tm_sec,tenths); //default: return format(" %02d:%02d:%02d.%1d",tm.tm_hour,tm.tm_min,tm.tm_sec,tenths); //} } const string timestr() { return timestr(12); } // High resolution sleep for the specified time. // Return FALSE if time is already past. void sleepf(double howlong) { if (howlong <= 0.00001) return; // Less than 10 usecs, forget it. usleep((useconds_t) (1000000.0 * howlong)); } //bool sleepuntil(double untilwhen) //{ //double now = timef(); //double howlong = untilwhen - now; // Fractional time in seconds. // We are not worrying about overflow because all times should be in the near future. //if (howlong <= 0.00001) return false; // Less than 10 usecs, forget it. //sleepf(sleeptime); //} string Text2Str::str() const { ostringstream ss; text(ss); return ss.str(); } ostream& operator<<(std::ostream& os, const Text2Str *val) { ostringstream ss; if (val) { val->text(ss); os << ss.str(); } else { os << "(null)"; } return os; } // Greatest Common Denominator. // This is by Doug Brown. int gcd(int x, int y) { if (x > y) { return x % y == 0 ? y : gcd(y, x % y); } else { return y % x == 0 ? x : gcd(x, y % x); } } // Split a C string into an argc,argv array in place; the input string is modified. // Returns argc, and places results in argv, up to maxargc elements. // The final argv receives the rest of the input string from maxargc on, // even if it contains additional splitchars. // The correct idiom for use is to make a copy of your string, like this: // char *copy = strcpy((char*)alloca(the_string.length()+1),the_string.c_str()); // char *argv[2]; // int argc = cstrSplit(copy,argv,2,NULL); // If you want to detect the error of too many arguments, add 1 to argv, like this: // char *argv[3]; // int argc = cstrSplit(copy,argv,3,NULL); // if (argc == 3) { error("too many arguments"; } int cstrSplit(char *in, char **pargv,int maxargc, const char *splitchars) { if (splitchars == NULL) { splitchars = " \t\r\n"; } // Default is any space. int argc = 0; while (argc < maxargc) { while (*in && strchr(splitchars,*in)) {in++;} // scan past any splitchars if (! *in) return argc; // return if finished. pargv[argc++] = in; // save ptr to start of arg. in = strpbrk(in,splitchars); // go to end of arg. if (!in) return argc; // return if finished. *in++ = 0; // zero terminate this arg. } return argc; } // Return pointer to the nth (1 for first) space-separated non-empty argument from this string, and length in plength. // Note that strtok is not thread safe. char *cstrGetArg(const char *in, int nth, unsigned *length) { const char *result, *cp = in; while (*cp && nth-- > 0) { while (*cp && isspace(*cp)) { cp++; } result = cp; while (*cp && !isspace(*cp)) { cp++; } if (nth == 0) { *length = cp - result; // remove the ever-to-be-hated const for the convenience of our callers. return *length ? const_cast(result) : NULL; } } return NULL; } vector& stringSplit(vector &result,const char *input) { char *argv[40]; //char buf[202]; //strncpy(buf,input,200); buf[200] = 0; char *buf = strdup(input); int cnt = cstrSplit(buf,argv,40,NULL); for (int i = 0; i < cnt; i++) { result.push_back(string(argv[i])); } free(buf); return result; } // Print a table formatted as a vector of vector of strings. // The columns will be aligned. // Column size is determined from the columns. // An entry of "_" is suppressed. void printPrettyTable(prettyTable_t &tab, ostream&os, bool tabSeparated) { LOG(DEBUG); const unsigned maxcols = 30; // Determine the maximum width of each column. int width[maxcols]; memset(width,0,sizeof(width)); if (!tabSeparated) { for (prettyTable_t::iterator it = tab.begin(); it != tab.end(); ++it) { vector &row = *it; for (unsigned col = 0; col 100) colwidth = 100; width[col] = max(width[col],colwidth); } } } // Now print it. for (unsigned nrow = 0; nrow < tab.size(); nrow++) { vector &row = tab[nrow]; // DEBUG: print the column widths. if (0 && IS_LOG_LEVEL(DEBUG) && nrow == 0) { for (unsigned col = 0; col &stat) { stat.text(os); return os; } ostream& operator<<(std::ostream& os, const Statistic &stat) { stat.text(os); return os; } ostream& operator<<(std::ostream& os, const Statistic &stat) { stat.text(os); return os; } ostream& operator<<(std::ostream& os, const Statistic &stat) { stat.text(os); return os; } string replaceAll(const std::string input, const std::string search, const std::string replace) { string output = input; unsigned index1 = 0; while (index1 < output.size()) { try { index1 = output.find(search, index1); if (index1 == string::npos) { break; } output.replace(index1, search.length(), replace); // We want to scan past the piece we just replaced. index1 += replace.length(); } catch (...) { LOG(ERR) << "string replaceAll error"<> *hRAND; stringstream ssl; ssl << hex << strlRAND; ssl >> *lRAND; } string uintToString(uint64_t h, uint64_t l) { ostringstream os1; os1.width(16); os1.fill('0'); os1 << hex << h; ostringstream os2; os2.width(16); os2.fill('0'); os2 << hex << l; ostringstream os3; os3 << os1.str() << os2.str(); return os3.str(); } string uintToString(uint32_t x) { ostringstream os; os.width(8); os.fill('0'); os << hex << x; return os.str(); } // Return the first n lines of a string. string firstlines(string msgstr, int n) { // n must be >=1 size_t pos = msgstr.find('\n'); if (pos < msgstr.size()) pos++; //printf("firstlines pos=%d\n",(int)pos); while (--n > 0 && pos < msgstr.size()) { pos = msgstr.find('\n',pos); //printf("firstlines n=%d pos=%d\n",n,(int)pos); if (pos < msgstr.size()) pos++; } //printf("firstlines return n=%d pos=%d\n",n,(int)pos); return msgstr.substr(0,pos); } static string backtrace_failed = "backtrace failed"; string rn_backtrace() { void *buffer[30]; int nptrs = backtrace(buffer,30); if (nptrs <= 0) { return backtrace_failed; } char **strings = backtrace_symbols(buffer,nptrs); if (strings == NULL) { return backtrace_failed; } string result; try { result = "backtrace:"; for (int j = 0; j < nptrs; j++) { result = result + " " + strings[j]; } } catch(...) { return backtrace_failed; } free(strings); return result; } // Size is 65 instead of 64 so we can easily init from a char string, which is nul-terminated. static unsigned char sMapBase64[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static unsigned char sMapBase64Reverse[256]; static unsigned char sMapHexReverse[256]; // (pat) This class exists only to init the decoder maps above. static struct BaseNDecoder { BaseNDecoder() { // Init the base64 decoder: memset(sMapBase64Reverse,0xff,sizeof(sMapBase64Reverse)); // Set all illegal characters to 0xff. for (unsigned i = 0; i < 64; i++) { sMapBase64Reverse[sMapBase64[i]] = i; } sMapBase64Reverse['='] = 0; // The '=' char is used as the premature-end-of-string marker. // Init the hex decoder: memset(sMapHexReverse,0xff,sizeof(sMapHexReverse)); // Set all illegal characters to 0xff. for (unsigned ch = '0'; ch <= '9'; ch++) sMapHexReverse[ch] = ch - '0'; for (unsigned ch = 'A'; ch <= 'Z'; ch++) sMapHexReverse[ch] = ch - 'A' + 10; for (unsigned ch = 'a'; ch <= 'z'; ch++) sMapHexReverse[ch] = ch - 'a' + 10; } } sBaseNDecoderInit; // (pat) Encode buffer of binary data to base64 encoded string. static string base64EncodeToString(const unsigned char *ubuf, unsigned buflen) { string result; result.reserve((buflen*4+2)/3); for (unsigned i = 0; i < buflen; i += 3) { // Suck in 3 chars. uint32_t accum = ubuf[i]; accum <<= 8; if (i+1>18)]); result.push_back((char)sMapBase64[0x3f&(accum>>12)]); result.push_back(i+1 < buflen ? (char)sMapBase64[0x3f&(accum>>6)] : '='); result.push_back(i+2 < buflen ? (char)sMapBase64[0x3f&accum] : '='); } return result; } // (pat) Decode base64 encoded buffer of characters to a string of binary data. Ignore interspersed spaces and newlines. static string base64DecodeToString(const unsigned char *ubuf, unsigned buflen, string &errorMessage) { string result; result.reserve((buflen * 3 + 2) / 4); for (unsigned i = 0; i < buflen;) { uint32_t accum = 0; // Accumulator of up to 24 bits from 4 base64 chars. unsigned j = 0; // Number of characters accumulated, 0-4. // Suck in 4 chars. while (j < 4 && i < buflen) { unsigned ch = ubuf[i++]; if (isspace(ch)) { continue; } // Ignore spaces and newlines. if (ch == '=') { // The '=' at the end of a base64 encoded string indicates size was not evenly divisible by 3 and pre-terminates. // We will scan the rest of the string to detect errors. for ( ; i < buflen; i++) { if (! (isspace(ubuf[i]) || ubuf[i] == '=')) { errorMessage = "found data after '=' terminator in base64 data"; } } break; } unsigned decoded = sMapBase64Reverse[ch]; if (decoded >= 64) { errorMessage = "invalid characters in base64 data"; continue; // But we'll keep going and hope for the best. } accum |= decoded << ((3-j)*6); j++; } // Spit out 3 chars. if (j >= 1) result.push_back(0xff&(accum >>16)); if (j >= 3) result.push_back(0xff&(accum >>8)); if (j == 4) result.push_back(0xff&accum); } return result; } // (pat) Decode hex encoded buffer of characters to a string of binary data. Ignore interspersed spaces and newlines. // Undefined what happens if the number of input hex chars is odd. static string hexDecodeToString(const unsigned char *ubuf, unsigned buflen, string &errorMessage) { string result; result.reserve((buflen + 1) / 2); for (unsigned i = 0; i < buflen; ) { unsigned j = 0; unsigned char accum = 0; // Suck in 2 chars. while (j < 2 && i < buflen) { unsigned ch = ubuf[i++]; if (isspace(ch)) continue; unsigned decoded = sMapHexReverse[ch]; if (decoded >= 16) { errorMessage = "unexpected character in hex data"; } else { accum = (accum << 4) | decoded; } j++; } // Spit out 1 char. if (j) result.push_back(accum); if (j == 1) { errorMessage = "Unexpected odd length of hex encoded string"; } } return result; } static unsigned tohex1(unsigned data) { data &= 0xf; return data <= 9 ? data + '0' : data + 'A' - 10; } // Decode character data to binary data as per encodingArg which may be "binary", "hex", "base64" // In the "binary" case, the data is just copied verbatim. // On return the errorMessage will have a non-zero size if an error occurred. string decodeToString(const char *buf, unsigned buflen, string encodingArg, string &errorMessage) { stringCaseInsensitive encoding(encodingArg); if (encoding == "binary") { return string(buf,buflen); } else if (encoding == "hex" || encoding == "base16") { return hexDecodeToString((const unsigned char *)buf, buflen, errorMessage); } else if (encoding == "base64") { return base64DecodeToString((const unsigned char *)buf,buflen,errorMessage); } else { errorMessage = "Unexpected encoding specified:" + encodingArg; return ""; } } // Encode binary data into a character string as per encodingArg which may be "binary", "hex", "base64". // In the "binary" case, the data is just copied verbatim. // On return the errorMessage will have a non-zero size if an error occurred. string encodeToString(const char *data, unsigned datalen, string encodingArg, string &errorMessage) { stringCaseInsensitive encoding(encodingArg); if (encoding == "binary") { return string(data,datalen); } else if (encoding == "hex" || encoding == "base16") { string result; result.reserve(datalen*2); for (const char *dp = data; dp < &data[datalen]; dp++) { result.push_back(tohex1((*dp >> 4) & 0xf)); result.push_back(tohex1(*dp & 0xf)); } return result; } else if (encoding == "base64") { return base64EncodeToString((const unsigned char *)data,datalen); } else { errorMessage = "Unexpected encoding specified:" + encodingArg; return ""; } } }; // namespace