/* * Utilj.cc * * Created on: Nov 4, 2010 * Author: jjacobs */ #include <cstdarg> #include <cstdio> #include <cstring> #include <errno.h> #include <casacore/casa/aips.h> #include <casacore/casa/aipstype.h> #include <casacore/casa/BasicSL/String.h> #include <casacore/casa/Utilities/CountedPtr.h> #include <sys/time.h> #include <execinfo.h> #include <algorithm> #include <math.h> #include <fstream> #include <unistd.h> #include <sys/time.h> #include <sys/resource.h> using std::max; using std::min; using namespace casacore; using namespace std; #include "UtilJ.h" using namespace casacore; namespace casa { namespace utilj { // Leave a tag to allow manual verification of build setting #if defined (CASA_THREAD_NEUTRAL) String CasaThreadNeutral = "CasaThreadNeutral:YES"; #else String CasaThreadNeutral = "CasaThreadNeutral:NO"; #endif // defined (CASA_THREAD_NEUTRAL) String formatV (const String & formatString, va_list vaList) { char buffer [4096]; int nPrinted = vsnprintf (buffer, sizeof (buffer), formatString.c_str(), vaList); if (nPrinted >= (int) sizeof (buffer) - 1){ buffer [sizeof (buffer) - 2] = '|'; // mark as truncated } return buffer; } /*String format (const char * formatString, ...) { // Return a String object created using the printf-like format string // and the provided arguments. If the text is truncated because the // internal buffer is too small, the last character will be a pipe "|". va_list vaList; va_start (vaList, formatString); String result = formatV (formatString, vaList); va_end (vaList); return result; }*/ Bool getEnv (const String & name, const Bool & defaultValue) { char * value = getenv (name.c_str()); if (value == NULL){ return defaultValue; } else{ String stringValue = value; stringValue.downcase(); Bool truthValue = true; if (stringValue == "false" || stringValue == "f" || stringValue == "0" || stringValue == "no"){ truthValue = false; } return truthValue; } } Int getEnv (const String & name, const Int & defaultValue) { char * value = getenv (name.c_str()); if (value == NULL){ return defaultValue; } else{ char * next; long longValue = strtol (value, & next, 10); // If all of the value characters weren't consumed, assume // an error occurred and use the default value. if (* next != '\0') longValue = defaultValue; return longValue; } } String getTimestamp () { // Get a possibly decent resolution time value struct timeval now; gettimeofday (& now, NULL); // Convert from UTC to local time struct tm localNow = * localtime (& now.tv_sec); // Output the seconds portion of the time in the format // hh:mm:ss char buffer [128]; strftime (buffer, sizeof(buffer), "%X", & localNow); // Add on the higher resolution portion (if any) of the time // as milliseconds char buffer2 [128]; snprintf (buffer2, sizeof(buffer2), "%s.%03d", buffer, (int) now.tv_usec/1000); // Return the final result in the format "hh:mm:ss.ttt" return buffer2; } Bool isEnvDefined (const String & name) { char * value = getenv (name.c_str()); return value != NULL; } void printBacktrace (ostream & os, const String & prefix) { void * stack [512]; int nUsed = backtrace (stack, 512); char ** trace = backtrace_symbols (stack, nUsed); if (! prefix.empty()){ os << prefix << endl; } os << "*** Stack trace (use c++filt to demangle):" << endl; for (int i = 0; i < nUsed; i++){ os << trace[i] << endl; } os.flush(); delete trace; } AipsError repackageAipsError (AipsError & error, const String & message, const String & file, Int line, const String & func) { ostringstream os; AipsError tmp (message, file, line); os << func << ": " << tmp.what() << "\n " << error.what(); return AipsError (os.str()); } long round (Double d) { Double sign = (d < 0) ? -1.0 : 1.0; d += 0.5 * sign; long result = (long) d; return result; } void sleepMs (Int milliseconds) { struct timespec t, tRemaining; t.tv_sec = milliseconds / 1000; t.tv_nsec = (milliseconds - t.tv_sec) * 1000000; // Because nanosleep can be interrupted by a signal, it is necessary // to continue the wait if errno is EINTR. When interrupted, nanosleep // copies the amount of time remaining in the wait into tRemaining; so // the remainder of one interation becomes the wait value proper on the // next iteration. Bool done; do { done = nanosleep (& t, & tRemaining) == 0 || errno != EINTR; t = tRemaining; } while (! done); } vector<String> split (const String & string, const String & splitter, Bool ignoreConsecutiveSplitters) { vector<String> result; Int start = 0; while (true){ Int matchStart = string.find (splitter, start); if (matchStart == (int) String::npos){ // No match: put rest of string into the result String text = string.substr (start); if (! text.empty()){ result.push_back (string.substr (start)); } break; } String text = string.substr (start, matchStart - start); if (! (text.empty() && ignoreConsecutiveSplitters)){ // If the text is nonempty or we're not ignored consecutive // occurrences of splitters, then push text onto the result. result.push_back (text); } start = matchStart + splitter.length(); } return result; } void toStdError (const String & m, const String & prefix) { cerr << prefix << m << endl; cerr.flush(); } void throwIf (bool condition, const String & message, const String & file, Int line, const String & func) { // If the condition is met then throw an AipsError if (condition) { String m = func + ": " + message; AipsErrorTrace e (m.c_str(), file.c_str(), line); # if defined (NDEBUG) toStdError (e.what()); # endif throw e; } } void throwIfError (int errorCode, const String & prefix, const String & file, Int line, const String & func) { // If the provided error code is not equal to success (0) then // throw an AipsError using the provided suffix and then details // of the error. if (errorCode != 0) { AipsErrorTrace e (String::format ("%s: %s (errno=%d):%s", func.c_str(), prefix.c_str(), errorCode, strerror (errorCode)), file.c_str(), line); # if defined (NDEBUG) toStdError (e.what()); # endif throw e; } } DeltaThreadTimes ThreadTimes::operator- (const ThreadTimes & tEarlier) const { return DeltaThreadTimes (this->elapsed() - tEarlier.elapsed(), this->cpu() - tEarlier.cpu()); } DeltaThreadTimes & DeltaThreadTimes::operator+= (const DeltaThreadTimes & other) { cpu_p += other.cpu(); elapsed_p += other.elapsed(); n_p += 1; if (doStats_p){ cpuSsq_p += other.cpu() * other.cpu(); cpuMin_p = min (cpuMin_p, other.cpu()); cpuMax_p = max (cpuMax_p, other.cpu()); elapsedSsq_p += other.elapsed() * other.elapsed(); elapsedMin_p = min (elapsedMin_p, other.elapsed()); elapsedMax_p = max (elapsedMax_p, other.elapsed()); } return * this; } String DeltaThreadTimes::formatAverage (const String & floatFormat, Double scale, const String & units) const // to convert to ms { String realFormat = String::format ("(el=%s,cp=%s,%%4.1f%%%%) %s", floatFormat.c_str(), floatFormat.c_str(), units.c_str()); Int n = n_p != 0 ? n_p : 1; Double c = cpu_p / n * scale; Double e = elapsed_p / n * scale; Double p = c / e * 100; String result = String::format (realFormat.c_str(), e, c, p); return result; } String DeltaThreadTimes::formatStats (const String & floatFormat, Double scale, const String & units) const // to convert to ms { String realFormat = String::format ("(el=%s {%s-%s,%s}, cp=%s {%s-%s,%s}, %%4.1f%%%%) %s", floatFormat.c_str(), floatFormat.c_str(), floatFormat.c_str(), floatFormat.c_str(), floatFormat.c_str(), floatFormat.c_str(), floatFormat.c_str(), floatFormat.c_str(), units.c_str()); Int n = n_p != 0 ? n_p : 1; Double c = cpu_p / n * scale; Double cS = sqrt (cpuSsq_p / n_p - c * c); Double e = elapsed_p / n * scale; Double eS = sqrt (elapsedSsq_p / n_p - e * e); Double p = c / e * 100; String result = String::format (realFormat.c_str(), e, elapsedMin_p, elapsedMax_p, eS, c, cpuMin_p, cpuMax_p, cS, p); return result; } AipsErrorTrace::AipsErrorTrace ( const String &msg, const String &filename, uInt lineNumber, Category c) : AipsError (msg, filename, lineNumber, c) { void * stack [512]; int n = backtrace (stack, 512); char ** trace = backtrace_symbols (stack, n); message += "\nStack trace (use c++filt to demangle):\n"; for (int i = 0; i < n; i++){ message += trace[i] + String ("\n"); } free (trace); } MemoryStatistics::MemoryStatistics () { char buffer [128]; pid_t pid = getpid (); sprintf (buffer, "/proc/%d/statm", pid); filename_p = buffer; pageSize_p = getpagesize (); bytesPerMb_p = 1024 * 1024.0; } double MemoryStatistics::getVmInMB() const { return getVmInBytes () / bytesPerMb_p; } int64_t MemoryStatistics::getVmInBytes() const { return vmPages_p * pageSize_p; } double MemoryStatistics::getRssInMB() const { return getRssInBytes () / bytesPerMb_p; } int64_t MemoryStatistics::getRssInBytes() const { return rssPages_p * pageSize_p; } void MemoryStatistics::update () { ifstream is (filename_p.c_str()); is >> vmPages_p >> rssPages_p; is.close (); } IoStatistics::IoStatistics () { Int pid = getpid(); ostringstream os; statFile_p = String::format ("/proc/%d/io", pid); capture (); } IoStatistics IoStatistics::operator- (const IoStatistics & other) const { IoStatistics result; result.nBytesRead_p = nBytesRead_p - other.nBytesRead_p; result.nBytesWritten_p = nBytesWritten_p - other.nBytesWritten_p; result.nReads_p = nReads_p - other.nReads_p; result.nWrites_p = nWrites_p - other.nWrites_p; return result; } IoStatistics IoStatistics::operator+ (const IoStatistics & other) const { IoStatistics result; result.nBytesRead_p = nBytesRead_p + other.nBytesRead_p; result.nBytesWritten_p = nBytesWritten_p + other.nBytesWritten_p; result.nReads_p = nReads_p + other.nReads_p; result.nWrites_p = nWrites_p + other.nWrites_p; return result; } IoStatistics IoStatistics::operator* (Double f) const { IoStatistics result; result.nBytesRead_p = nBytesRead_p * f; result.nBytesWritten_p = nBytesWritten_p * f; result.nReads_p = nReads_p * f; result.nWrites_p = nWrites_p * f; return result; } IoStatistics IoStatistics::operator/ (const IoStatistics & other) const { IoStatistics result; result.nBytesRead_p = nBytesRead_p / other.nBytesRead_p; result.nBytesWritten_p = nBytesWritten_p / other.nBytesWritten_p; result.nReads_p = nReads_p / other.nReads_p; result.nWrites_p = nWrites_p / other.nWrites_p; return result; } void IoStatistics::capture () { ifstream is (statFile_p.c_str()); ThrowIf (! is.good(), String::format ("Failed to open %s file", statFile_p.c_str())); String tag; is >> tag; ThrowIf (tag != "rchar:", String::format ("Expected 'rchar:', got '%s'", tag.c_str())); is >> nBytesRead_p; is >> tag; ThrowIf (tag != "wchar:", String::format ("Expected 'wchar:', got '%s'", tag.c_str())); is >> nBytesWritten_p; is >> tag; ThrowIf (tag != "syscr:", String::format ("Expected 'syscr:', got '%s'", tag.c_str())); is >> nReads_p; is >> tag; ThrowIf (tag != "syscw:", String::format ("Expected 'syscw:', got '%s'", tag.c_str())); is >> nWrites_p; is.close(); } Double IoStatistics::getBytesRead () const { return nBytesRead_p; } Double IoStatistics::getBytesWritten () const { return nBytesWritten_p; } Double IoStatistics::getNReads () const { return nReads_p; } Double IoStatistics::getNWrites () const { return nWrites_p; } String IoStatistics::report (float scale, const String & scaleTag) const { IoStatistics t = (* this) * scale; return String::format ("read: %.3f %sB, %.3f %sOps; write: %.3f %sB, %.3f %sOps", t.nBytesRead_p, scaleTag.c_str(), t.nReads_p, scaleTag.c_str(), t.nBytesWritten_p, scaleTag.c_str(), t.nWrites_p, scaleTag.c_str()); } } // end namespace utilj using namespace casacore; } // end namespace casa