//--------------------------------------------------------------------------- // log.cpp - theme logging routines (shared in "inc" directory) //--------------------------------------------------------------------------- #include "stdafx.h" //--------------------------------------------------------------------------- #include "log.h" #include #include #include "cfile.h" #include "tmreg.h" //--------------------------------------------------------------------------- //---- undo the defines that turn these into _xxx ---- #undef Log #undef LogStartUp #undef LogShutDown #undef LogControl #undef TimeToStr #undef StartTimer #undef StopTimer #undef OpenLogFile #undef CloseLogFile #undef LogOptionOn #undef GetMemUsage #undef GetUserCount #undef GetGdiCount //--------------------------------------------------------------------------- #define DEFAULT_LOGNAME L"c:\\Themes.log" //--------------------------------------------------------------------------- struct LOGNAMEINFO { LPCSTR pszOption; LPCSTR pszDescription; }; //--------------------------------------------------------------------------- #define MAKE_LOG_STRINGS #include "logopts.h" // log options as strings //----------------------------------------------------------------- static const WCHAR szDayOfWeekArray[7][4] = { L"Sun", L"Mon", L"Tue", L"Wed", L"Thu", L"Fri", L"Sat" } ; static const WCHAR szMonthOfYearArray[12][4] = { L"Jan", L"Feb", L"Mar", L"Apr", L"May", L"Jun", L"Jul", L"Aug", L"Sep", L"Oct", L"Nov", L"Dec" } ; //----------------------------------------------------------------- #define OPTIONCNT (ARRAYSIZE(LogNames)) #define LOGPROMPT "enter log cmd here." //----------------------------------------------------------------- //---- unprotected vars (set on init) ---- static WCHAR _szUtilProcessName[MAX_PATH] = {0}; static WCHAR _szLogAppName[MAX_PATH] = {0}; static CRITICAL_SECTION csLogFile = {0}; static WCHAR szLogFileName[MAX_PATH+1]; static BOOL fLogInitialized = FALSE; //---- protected vars (thread safe) ---- static UCHAR uLogOptions[OPTIONCNT]; // protected by csLogFile static UCHAR uBreakOptions[OPTIONCNT]; // protected by csLogFile static DWORD dwLogStartTimer = 0; // protected by csLogFile static char szLastOptions[999] = {0}; // protected by csLogFile static char szLogCmd[999] = {0}; // protected by csLogFile static int iIndentCount = 0; // protected by csLogFile static WCHAR s_szWorkerBuffer[512]; // protected by csLogFile static WCHAR s_szLogBuffer[512]; // protected by csLogFile static CHAR s_szConBuffer[512]; // protected by csLogFile static CSimpleFile *pLogFile = NULL; // protected by csLogFile //----------------------------------------------------------------- void ParseLogOptions(UCHAR *uOptions, LPCSTR pszName, LPCSTR pszOptions, BOOL fEcho); //----------------------------------------------------------------- void RawCon(LPCSTR pszFormat, ...) { CAutoCS cs(&csLogFile); va_list args; va_start(args, pszFormat); //---- format caller's string ---- StringCchVPrintfA(s_szConBuffer, ARRAYSIZE(s_szConBuffer), pszFormat, args); OutputDebugStringA(s_szConBuffer); va_end(args); } //----------------------------------------------------------------- void SetDefaultLoggingOptions() { RESOURCE HKEY hklm = NULL; HRESULT hr = S_OK; //---- open hklm ---- int code32 = RegOpenKeyEx(HKEY_LOCAL_MACHINE, THEMEMGR_REGKEY, 0, KEY_READ, &hklm); if (code32 != ERROR_SUCCESS) { hr = MakeErrorLast(); goto exit; } //---- read the "LogCmd" value ---- WCHAR szValBuff[MAX_PATH]; hr = RegistryStrRead(hklm, THEMEPROP_LOGCMD, szValBuff, ARRAYSIZE(szValBuff)); if (SUCCEEDED(hr)) { USES_CONVERSION; ParseLogOptions(uLogOptions, "Log", W2A(szValBuff), FALSE); } //---- read the "BreakCmd" value ---- hr = RegistryStrRead(hklm, THEMEPROP_BREAKCMD, szValBuff, ARRAYSIZE(szValBuff)); if (SUCCEEDED(hr)) { USES_CONVERSION; ParseLogOptions(uBreakOptions, "Break", W2A(szValBuff), FALSE); } //---- read the "LogAppName" value ---- RegistryStrRead(hklm, THEMEPROP_LOGAPPNAME, _szLogAppName, ARRAYSIZE(_szLogAppName)); exit: if (hklm) RegCloseKey(hklm); } //----------------------------------------------------------------- BOOL LogStartUp() { BOOL fInit = FALSE; dwLogStartTimer = StartTimer(); pLogFile = new CSimpleFile; if (! pLogFile) goto exit; //---- reset all log options ---- for (int i=0; i < OPTIONCNT; i++) { uLogOptions[i] = 0; uBreakOptions[i] = 0; } //---- turn on default OUTPUT options ---- uLogOptions[LO_CONSOLE] = TRUE; uLogOptions[LO_APPID] = TRUE; uLogOptions[LO_THREADID] = TRUE; //---- turn on default FILTER options ---- uLogOptions[LO_ERROR] = TRUE; uLogOptions[LO_ASSERT] = TRUE; uLogOptions[LO_BREAK] = TRUE; uLogOptions[LO_PARAMS] = TRUE; uLogOptions[LO_ALWAYS] = TRUE; //---- turn on default BREAK options ---- uBreakOptions[LO_ERROR] = TRUE; uBreakOptions[LO_ASSERT] = TRUE; if( InitializeCriticalSectionAndSpinCount(&csLogFile, 0) ) { //---- get process name (log has its own copy) ---- WCHAR szPath[MAX_PATH]; if (! GetModuleFileNameW( NULL, szPath, ARRAYSIZE(szPath) )) goto exit; WCHAR szDrive[_MAX_DRIVE], szDir[_MAX_DIR], szExt[_MAX_EXT]; _wsplitpath(szPath, szDrive, szDir, _szUtilProcessName, szExt); StringCchCopyA(szLogCmd, ARRAYSIZE(szLogCmd), LOGPROMPT); StringCchCopyW(szLogFileName, ARRAYSIZE(szLogFileName), DEFAULT_LOGNAME); fLogInitialized = TRUE; SetDefaultLoggingOptions(); fInit = TRUE; } exit: return fInit; } //--------------------------------------------------------------------------- BOOL LogShutDown() { fLogInitialized = FALSE; SAFE_DELETE(pLogFile); SAFE_DELETECRITICALSECTION(&csLogFile); return TRUE; } //--------------------------------------------------------------------------- void GetDateString(WCHAR *pszDateBuff, ULONG cchDateBuff) { // Sent Date SYSTEMTIME stNow; WCHAR szMonth[10], szWeekDay[12] ; GetLocalTime(&stNow); StringCchCopyW(szWeekDay, ARRAYSIZE(szWeekDay), szDayOfWeekArray[stNow.wDayOfWeek]) ; StringCchCopyW(szMonth, ARRAYSIZE(szMonth), szMonthOfYearArray[stNow.wMonth-1]) ; StringCchPrintfW(pszDateBuff, cchDateBuff, L"%s, %u %s %u %2d:%02d:%02d ", szWeekDay, stNow.wDay, szMonth, stNow.wYear, stNow.wHour, stNow.wMinute, stNow.wSecond) ; } //--------------------------------------------------------------------------- void LogMsgToFile(LPCWSTR pszMsg) { CAutoCS autoCritSect(&csLogFile); HRESULT hr; BOOL fWasOpen = pLogFile->IsOpen(); if (! fWasOpen) { BOOL fNewFile = !FileExists(szLogFileName); hr = pLogFile->Append(szLogFileName, TRUE); if (FAILED(hr)) goto exit; //---- write hdr if new file ---- if (fNewFile) { WCHAR pszBuff[100]; GetDateString(pszBuff, ARRAYSIZE(pszBuff)); pLogFile->Printf(L"Theme log - %s\r\n\r\n", pszBuff); } } pLogFile->Write((void*)pszMsg, lstrlen(pszMsg)*sizeof(WCHAR)); exit: if (! fWasOpen) pLogFile->Close(); } //--------------------------------------------------------------------------- void SimpleFileName(LPCSTR pszNarrowFile, OUT LPWSTR pszSimpleBuff, ULONG cchSimpleBuff) { USES_CONVERSION; WCHAR *pszFile = A2W(pszNarrowFile); //---- remove current dir marker for VS error navigation ---- if ((pszFile[0] == L'.') && (pszFile[1] == L'\\')) { StringCchPrintfW(pszSimpleBuff, cchSimpleBuff, L"f:\\nt\\shell\\Themes\\UxTheme\\%s", pszFile+2); if (! FileExists(pszSimpleBuff)) StringCchPrintfW(pszSimpleBuff, cchSimpleBuff, L"f:\\nt\\shell\\Themes\\ThemeSel\\%s", pszFile+2); if (! FileExists(pszSimpleBuff)) StringCchPrintfW(pszSimpleBuff, cchSimpleBuff, L"f:\\nt\\shell\\Themes\\packthem\\%s", pszFile+2); if (! FileExists(pszSimpleBuff)) StringCchPrintfW(pszSimpleBuff, cchSimpleBuff, L"%s", pszFile+2); } else StringCchCopyW(pszSimpleBuff, cchSimpleBuff, pszFile); } //----------------------------------------------------------------- #ifdef DEBUG // pulls in psapi.dll int GetMemUsage() { ULONG ulReturnLength; VM_COUNTERS vmCounters; if (!NT_SUCCESS(NtQueryInformationProcess(GetCurrentProcess(), ProcessVmCounters, &vmCounters, sizeof(vmCounters), &ulReturnLength))) { ZeroMemory(&vmCounters, sizeof(vmCounters)); } return static_cast(vmCounters.WorkingSetSize); } #else int GetMemUsage() { return 0; } #endif //----------------------------------------------------------------- int GetUserCount() { HANDLE hp = GetCurrentProcess(); return GetGuiResources(hp, GR_USEROBJECTS); } //----------------------------------------------------------------- int GetGdiCount() { HANDLE hp = GetCurrentProcess(); return GetGuiResources(hp, GR_GDIOBJECTS); } //----------------------------------------------------------------- void LogWorker(UCHAR uLogOption, LPCSTR pszSrcFile, int iLineNum, int iEntryExitCode, LPCWSTR pszMsg) { CAutoCS cs(&csLogFile); BOOL fBreaking = (uBreakOptions[uLogOption]); if (fBreaking) { OutputDebugString(L"\r\n"); // blank line at beginning WCHAR fn[_MAX_PATH+1]; SimpleFileName(pszSrcFile, fn, ARRAYSIZE(fn)); StringCchPrintfW(s_szWorkerBuffer, ARRAYSIZE(s_szWorkerBuffer), L"%s [%d]: BREAK at %s(%d):\r\n", _szUtilProcessName, GetCurrentThreadId(), fn, iLineNum); OutputDebugString(s_szWorkerBuffer); } //---- PRE API entry/exit indent adjustment ---- if (iEntryExitCode == -1) { if (iIndentCount >= 2) iIndentCount -= 2; } WCHAR *p = s_szWorkerBuffer; ULONG cch = ARRAYSIZE(s_szWorkerBuffer); ULONG len; //---- apply indenting ---- for (int i=0; i < iIndentCount; i++) { *p++ = ' '; cch--; } //---- POST API entry/exit indent adjustment ---- if (iEntryExitCode == 1) { iIndentCount += 2; } //---- apply app id ---- if (uLogOptions[LO_APPID] && cch > 0) { StringCchPrintfW(p, cch, L"%s ", _szUtilProcessName); len = lstrlen(p); p += len; cch -= len; } //---- apply thread id ---- if (uLogOptions[LO_THREADID] && cch > 0) { StringCchPrintfW(p, cch, L"[%d] ", GetCurrentThreadId()); len = lstrlen(p); p += len; cch -= len; } //---- apply src id ---- if (uLogOptions[LO_SRCID] && cch > 0) { WCHAR fn[_MAX_PATH+1]; SimpleFileName(pszSrcFile, fn, ARRAYSIZE(fn)); if (fBreaking) StringCchPrintfW(p, cch, L"BREAK at %s(%d) : ", fn, iLineNum); else StringCchPrintfW(p, cch, L"%s(%d) : ", fn, iLineNum); len = lstrlen(p); p += len; cch -= len; } //---- apply timer id ---- if (uLogOptions[LO_TIMERID] && cch > 0) { DWORD dwTicks = StopTimer(dwLogStartTimer); WCHAR buff[100]; TimeToStr(dwTicks, buff, ARRAYSIZE(buff)); StringCchPrintfW(p, cch, L"Timer: %s ", buff); len = lstrlen(p); p += len; cch -= len; } //---- apply clock id ---- if (uLogOptions[LO_CLOCKID] && cch > 0) { SYSTEMTIME stNow; GetLocalTime(&stNow); StringCchPrintfW(p, cch, L"Clock: %02d:%02d:%02d.%03d ", stNow.wHour, stNow.wMinute, stNow.wSecond, stNow.wMilliseconds); len = lstrlen(p); p += len; cch -= len; } //---- apply USER count ---- if (uLogOptions[LO_USERCOUNT] && cch > 0) { StringCchPrintfW(p, cch, L"UserCount=%d ", GetUserCount()); len = lstrlen(p); p += len; cch -= len; } //---- apply GDI count ---- if (uLogOptions[LO_GDICOUNT] && cch > 0) { StringCchPrintfW(p, cch, L"GDICount=%d ", GetGdiCount()); len = lstrlen(p); p += len; cch -= len; } //---- apply MEM usage ---- if (uLogOptions[LO_MEMUSAGE] && cch > 0) { int iUsage = GetMemUsage(); StringCchPrintfW(p, cch, L"MemUsage=%d ", iUsage); len = lstrlen(p); p += len; cch -= len; } //---- add "Assert:" or "Error:" strings as needed ---- if (uLogOption == LO_ASSERT && cch > 0) { StringCchCopyW(p, cch, L"Assert: "); len = lstrlen(p); p += len; cch -= len; } else if (uLogOption == LO_ERROR && cch > 0) { StringCchCopyW(p, cch, L"Error: "); len = lstrlen(p); p += len; cch -= len; } //---- apply caller's msg ---- if( cch > 0 ) { StringCchCopyW(p, cch, pszMsg); len = lstrlen(p); p += len; cch -= len; } if( cch >= 3 ) { *p++ = '\r'; // end with CR cch--; } if( cch >= 2 ) { *p++ = '\n'; // end with newline cch--; } if( cch >= 1 ) { *p = 0; cch--; } //---- log to CONSOLE and/or FILE ---- if (uLogOptions[LO_CONSOLE]) OutputDebugString(s_szWorkerBuffer); if (uLogOptions[LO_LOGFILE]) LogMsgToFile(s_szWorkerBuffer); //---- process Heap Check ---- if (uLogOptions[LO_HEAPCHECK]) { HANDLE hh = GetProcessHeap(); HeapValidate(hh, 0, NULL); } if (fBreaking) OutputDebugString(L"\r\n"); // blank line at end } //----------------------------------------------------------------- HRESULT OpenLogFile(LPCWSTR pszLogFileName) { CAutoCS cs(&csLogFile); HRESULT hr = pLogFile->Create(pszLogFileName, TRUE); return hr; } //----------------------------------------------------------------- void CloseLogFile() { CAutoCS cs(&csLogFile); if (pLogFile) pLogFile->Close(); } //----------------------------------------------------------------- void Log(UCHAR uLogOption, LPCSTR pszSrcFile, int iLineNum, int iEntryExitCode, LPCWSTR pszFormat, ...) { if (fLogInitialized) { CAutoCS cs(&csLogFile); //---- only log for a specified process? ---- if (* _szLogAppName) { if (lstrcmpi(_szLogAppName, _szUtilProcessName) != 0) return; } while (1) { if (strncmp(szLogCmd, LOGPROMPT, 6)!=0) { LogControl(szLogCmd, TRUE); StringCchCopyA(szLogCmd, ARRAYSIZE(szLogCmd), LOGPROMPT); DEBUG_BREAK; } else break; } if ((uLogOption >= 0) || (uLogOption < OPTIONCNT)) { if (uLogOptions[uLogOption]) { //---- apply caller's msg ---- va_list args; va_start(args, pszFormat); StringCchVPrintfW(s_szLogBuffer, ARRAYSIZE(s_szLogBuffer), pszFormat, args); va_end(args); LogWorker(uLogOption, pszSrcFile, iLineNum, iEntryExitCode, s_szLogBuffer); } if ((uBreakOptions[uLogOption]) && (uLogOption != LO_ASSERT)) DEBUG_BREAK; } } } //----------------------------------------------------------------- int MatchLogOption(LPCSTR lszOption) { for (int i=0; i < OPTIONCNT; i++) { if (lstrcmpiA(lszOption, LogNames[i].pszOption)==0) return i; } return -1; // not found } //----------------------------------------------------------------- void ParseLogOptions(UCHAR *uOptions, LPCSTR pszName, LPCSTR pszOptions, BOOL fEcho) { CAutoCS cs(&csLogFile); if (! fLogInitialized) return; //---- ignore debugger's multiple eval of same expression ---- if (strcmp(pszOptions, szLastOptions)==0) return; //---- make a copy of options so we can put NULLs in it ---- BOOL fErrors = FALSE; char szOptions[999]; char *pszErrorText = NULL; StringCchCopyA(szOptions, ARRAYSIZE(szOptions), pszOptions); //---- process each option in szOption ---- char *p = strchr(szOptions, '.'); if (p) // easy termination for NTSD users *p = 0; p = szOptions; while (*p == ' ') p++; while ((p) && (*p)) { //---- find end of current option "p" ---- char *q = strchr(p, ' '); if (q) *q = 0; UCHAR fSet = TRUE; if (*p == '+') p++; else if (*p == '-') { fSet = FALSE; p++; } if (! fErrors) // point to first error in case one is found pszErrorText = p; int iLogOpt = MatchLogOption(p); if (iLogOpt != -1) { if (iLogOpt == LO_ALL) { for (int i=0; i < OPTIONCNT; i++) uOptions[i] = fSet; } else uOptions[iLogOpt] = fSet; } else fErrors = TRUE; //---- skip to next valid space ---- if (! q) break; q++; while (*q == ' ') q++; p = q; } //---- fixup inconsistent option settings ---- if (! uOptions[LO_LOGFILE]) uOptions[LO_CONSOLE] = TRUE; if ((! fErrors) && (fEcho)) { //---- display active log options ---- BOOL fNoneSet = TRUE; RawCon("Active %s Options:\n", pszName); for (int i=0; i < LO_ALL; i++) { if (uOptions[i]) { RawCon("+%s ", LogNames[i].pszOption); fNoneSet = FALSE; } } if (fNoneSet) RawCon("\n"); else RawCon("\n"); //---- display active log filters (except "all") ---- RawCon("Active Msg Filters:\n "); for (i=LO_ALL+1; i < OPTIONCNT; i++) { if (uOptions[i]) { RawCon("+%s ", LogNames[i].pszOption); fNoneSet = FALSE; } } if (fNoneSet) RawCon("\n"); else RawCon("\n"); } else if (fErrors) { //---- one or more bad options - display available options ---- RawCon("Error - unrecognized %s option: %s\n", pszName, pszErrorText); if (fEcho) { RawCon("Available Log Options:\n"); for (int i=0; i < LO_ALL; i++) { RawCon(" +%-12s %s\n", LogNames[i].pszOption, LogNames[i].pszDescription); } RawCon("Available Msg Filters:\n"); for (i=LO_ALL; i < OPTIONCNT; i++) { RawCon(" +%-12s %s\n", LogNames[i].pszOption, LogNames[i].pszDescription); } } } StringCchCopyA(szLastOptions, ARRAYSIZE(szLastOptions), pszOptions); } //----------------------------------------------------------------- void LogControl(LPCSTR pszOptions, BOOL fEcho) { ParseLogOptions(uLogOptions, "Log", pszOptions, fEcho); } //--------------------------------------------------------------------------- DWORD StartTimer() { LARGE_INTEGER now; QueryPerformanceCounter(&now); return DWORD(now.QuadPart); } //--------------------------------------------------------------------------- DWORD StopTimer(DWORD dwStartTime) { LARGE_INTEGER now; QueryPerformanceCounter(&now); return DWORD(now.QuadPart) - dwStartTime; } //--------------------------------------------------------------------------- void TimeToStr(UINT uRaw, WCHAR *pszBuff, ULONG cchBuff) { LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); DWORD Freq = (UINT)freq.QuadPart; const _int64 factor6 = 1000000; _int64 secs = uRaw*factor6/Freq; StringCchPrintfW(pszBuff, cchBuff, L"%u.%04u secs", UINT(secs/factor6), UINT(secs%factor6)/100); } //--------------------------------------------------------------------------- BOOL LogOptionOn(int iLogOption) { if ((iLogOption < 0) || (iLogOption >= OPTIONCNT)) return FALSE; return (uLogOptions[iLogOption] != 0); } //---------------------------------------------------------------------------