#include "common.h" #include "base/Trace.h" #include "base/String.h" #ifndef STAMP_BUILD #define STAMP_BUILD "(unstamped)" #endif #ifdef NDEBUG #define BUILD_TYPE "RELEASE" #endif #ifdef _DEBUG #define BUILD_TYPE "DEBUG" #endif #ifndef BUILD_TYPE #define BUILD_TYPE "UNKNOWN" #endif ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// static const char* g_sCurrent = "(BASE)"; static const char* g_sFilter = "*>Aa"; static unsigned g_nLevel = 0; static FILE* g_fileLog = stdout; Trace::Scope::Scope(const char* s) : m_sOuter(0) { logPrint("> > %s",s); { m_sOuter = g_sCurrent; g_sCurrent = s; ++g_nLevel; } } Trace::Scope::Scope(const char* s1,const char* s2) : m_sOuter(0) { logPrint("> > %s [%s]",s1,s2); { m_sOuter = g_sCurrent; g_sCurrent = s1; ++g_nLevel; } } Trace::Scope::Scope(const char* s1,const char* s2,const char* s3) : m_sOuter(0) { logPrint("> > %s [%s:%s]",s1,s2,s3); { m_sOuter = g_sCurrent; g_sCurrent = s1; ++g_nLevel; } } Trace::Scope::~Scope() { const char* s; { --g_nLevel; s = g_sCurrent; g_sCurrent = m_sOuter; } logPrint("< < %s",s); } static inline int InFilter(char c) { return 0 != strchr(g_sFilter,c); } static inline char* AddPrefix(char* p) { *p++ = '\n'; return p; } static inline char* AddIndent(char* p) { for (int n = 31 & g_nLevel; 0 < n; --n) { *p++ = ' '; *p++ = ' '; } return p; } static void LogWrite(const char* s) { if (g_fileLog) { fputs(s,g_fileLog); } } static base::String g_sLogBaseName; static int g_ydayLog; static time_t g_tFlushed; static bool openLog(struct tm* tm) { // Add a time-specific extension. char s[80]; ::sprintf(s,"-%04d-%02d-%02d-%02d-%02d-%02d.log",tm->tm_year + 1900,tm->tm_mon + 1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec); base::String sFilename = g_sLogBaseName; sFilename.strcat(s); g_fileLog = ::fopen(sFilename,"a"); if (!g_fileLog) { return false; } g_ydayLog = tm->tm_yday; return true; } void Trace::initializeLog(const char* sName) { g_sLogBaseName = sName; time_t t = ::time(0); struct tm* tm = ::localtime(&t); if (!openLog(tm)) { return; } // Write who we are as the first entry. LogWrite("\n\n===== START LOG ====\n"); LogWrite(ctime(&t)); LogWrite("VERSION " STAMP_BUILD " " BUILD_TYPE); g_tFlushed = t; ::fflush(g_fileLog); } void Trace::rolloverLog() { time_t t = ::time(0); struct tm* tm = ::localtime(&t); if (g_ydayLog == tm->tm_yday) { if (10 < (t - g_tFlushed)) { g_tFlushed = t; ::fflush(g_fileLog); } return; } fclose(g_fileLog); openLog(tm); } void Trace::cleanupLog(void) { fclose(g_fileLog); g_fileLog = 0; } void Trace::setFilter(const char* s) { g_sFilter = strdup(s); } const char* Trace::getFilter() { return g_sFilter; } void Trace::logPrint(const char* s,...) { // skip if not logging if (!g_fileLog) { return; } // omit if not in filter if (!InFilter(*s++)) { return; } char sBuffer[8000]; char* p = AddPrefix(sBuffer); // insert leading spaces as required if (' ' == *s) { ++s; p = AddIndent(p); } // format the arguments va_list a; va_start(a,s); vsprintf(p,s,a); va_end(a); // drop into log file LogWrite(sBuffer); } void Trace::logString(const char* s,const char* sValue) { // skip if not logging if (!g_fileLog) { return; } if (256 < strlen(sValue)) { logBuffer(s,sValue,strlen(sValue)); return; } // omit if not in filter if (!InFilter(*s++)) { return; } char sBuffer[1000]; char* p = AddPrefix(sBuffer); // insert leading spaces as required if (' ' == *s) { ++s; p = AddIndent(p); } // format the arguments sprintf(p,"%s : \"%s\"",s,sValue); // drop into log file LogWrite(sBuffer); } void Trace::logSection(const char* sName) { LogWrite("\n\n##################################################################\n## "); LogWrite(sName); LogWrite("\n##################################################################\n"); } static const char sHex[] = "0123456789ABCDEF"; void Trace::logBuffer(const char* sName,const char* sBuffer,unsigned cbBuffer) { // skip if not logging if (!g_fileLog) { return; } // omit if not in filter if (!InFilter(*sName++)) { return; } // mark start of buffer logPrint("*[BUFFER %s (%u bytes)]",sName,cbBuffer); // dump contents of buffer for (unsigned i = 0; i < cbBuffer;) { char sLine[256]; char* s1 = sLine; sprintf(s1,"\n%05x:",i); s1 += strlen(s1); memset(s1,' ',50); char* s2 = s1 + 50; while (i < cbBuffer) { unsigned b = *(unsigned char*) (sBuffer + i); ++s1; *s1++ = sHex[15 & (b >> 4)]; *s1++ = sHex[15 & b]; *s2++ = (char) (((31 < b) && (b < 127)) ? b : '.'); ++i; if (!(15 & i)) break; } *s2 = 0; LogWrite(sLine); } // mark end of buffer logPrint("*[END BUFFER %s]",sName); } void Trace::logText(const char* sName,const char* sBuffer) { // skip if not logging if (!g_fileLog) { return; } // omit if not in filter if (!InFilter(*sName++)) { return; } // mark start of buffer logPrint("*[TEXT %s]",sName); // dump contents of buffer LogWrite(sBuffer); // mark end of buffer logPrint("*[END TEXT %s]",sName); } void Trace::assertAt(const char* sFile,unsigned iLine,const char* sExpression) { Trace::logPrint("*%s:%u ASSERT FAILED: %s",sFile,iLine,sExpression); }