/*++ Copyright (c) 1998 Microsoft Corporation Module Name: ReLog.c Abstract: Program to test relogging to a log file Author: Bob Watson (bobw) 2-apr-97 Revision History: --*/ #include #include #include #include #include #include #include #include "resource.h" #include "varg.c" #include "relog.h" /* LPCTSTR cszInputSwitch = (LPCTSTR)TEXT("/input:"); LPCTSTR cszOutputSwitch = (LPCTSTR)TEXT("/output:"); LPCTSTR cszSettingsSwitch = (LPCTSTR)TEXT("/settings:"); LPCTSTR cszLogtypeSwitch = (LPCTSTR)TEXT("/logtype:"); LPCTSTR cszFilterSwitch = (LPCTSTR)TEXT("/filter:"); LPCTSTR cszStartTime = (LPCTSTR)TEXT("/starttime:"); LPCTSTR cszEndTime = (LPCTSTR)TEXT("/endtime:"); LPCTSTR cszListSwitch = (LPCTSTR)TEXT("/list"); LPCTSTR cszAppendSwitch = (LPCTSTR)TEXT("/append"); */ VARG_RECORD Commands[] = { VARG_HELP( VARG_FLAG_OPTIONAL ) VARG_BOOL( IDS_PARAM_APPEND, VARG_FLAG_OPTIONAL, FALSE ) VARG_STR ( IDS_PARAM_COUNTERFILE, VARG_FLAG_OPTIONAL, _T("") ) VARG_STR ( IDS_PARAM_FORMAT,VARG_FLAG_OPTIONAL,_T("BLG") ) VARG_MSZ ( IDS_PARAM_INPUT, VARG_FLAG_REQUIRED|VARG_FLAG_NOFLAG, _T("") ) VARG_INT ( IDS_PARAM_INTERVAL, VARG_FLAG_OPTIONAL, 0 ) VARG_TIME( IDS_PARAM_BEGIN, VARG_FLAG_OPTIONAL|VARG_FLAG_ARG_TIME ) VARG_TIME( IDS_PARAM_END, VARG_FLAG_OPTIONAL|VARG_FLAG_ARG_TIME ) VARG_STR ( IDS_PARAM_OUTPUT, VARG_FLAG_OPTIONAL, _T("") ) VARG_BOOL( IDS_PARAM_QUERY, VARG_FLAG_OPTIONAL, FALSE ) VARG_TERMINATOR }; enum _Commands { eHelp, eAppend, eCounters, eFormat, eInput, eInterval, eBegin, eEnd, eOutput, eQuery }; LPCTSTR cszBinType = (LPCTSTR)TEXT("bin"); LPCTSTR cszBlgType = (LPCTSTR)TEXT("blg"); LPCTSTR cszCsvType = (LPCTSTR)TEXT("csv"); LPCTSTR cszTsvType = (LPCTSTR)TEXT("tsv"); LPCSTR cszBinLogFileHeader = "\"(PDH-BIN 4.0)\"\n"; LPCSTR cszTextRecordTerminator = "\n"; //LPCTSTR cszRelogVersionStr = (LPCWSTR)TEXT(RELOG_VERSION_ID); #define TextRecordTerminatorSize 1 // size of the string above FILE *fSettingsFile = NULL; #define DBG_SHOW_STATUS_PRINTS 1 #define DBG_NO_STATUS_PRINTS 0 DWORD dwDbgPrintLevel = DBG_NO_STATUS_PRINTS; #define ALLOW_REOPEN_OF_SETTINGS_FILE FALSE // assume string format of YYYY-MM-DD-hh:mm:ss // 0123456789012345678 // value is index of the first char of that field #define DATE_STRING_LENGTH 19 #define DATE_SECONDS_OFFSET 17 #define DATE_MINUTES_OFFSET 14 #define DATE_HOURS_OFFSET 11 #define DATE_DAYS_OFFSET 8 #define DATE_MONTH_OFFSET 5 #define NORMAL_MODE 1 #define LIST_MODE 2 #define SUMMARY_MODE 3 #define APPEND_MODE 4 #define HEADER_MODE 5 // structures lifted from pdh\log_bin.h typedef struct _PDHI_BINARY_LOG_RECORD_HEADER { DWORD dwType; DWORD dwLength; } PDHI_BINARY_LOG_RECORD_HEADER, *PPDHI_BINARY_LOG_RECORD_HEADER; // // the first data record after the log file type record is // an information record followed by the list of counters contained in this // log file. the record length is the size of the info header record and // all the counter info blocks in bytes. // note that this record can occur later in the log file if the query // is changed or the log file is appended. // typedef struct _PDHI_BINARY_LOG_INFO { LONGLONG FileLength; // file space allocated (optional) DWORD dwLogVersion; // version stamp DWORD dwFlags; // option flags LONGLONG StartTime; LONGLONG EndTime; LONGLONG CatalogOffset; // offset in file to wild card catalog LONGLONG CatalogChecksum; // checksum of catalog header LONGLONG CatalogDate; // date/time catalog was updated LONGLONG FirstRecordOffset; // pointer to first record [to read] in log LONGLONG LastRecordOffset; // pointer to last record [to read] in log LONGLONG NextRecordOffset; // pointer to where next one goes LONGLONG WrapOffset; // pointer to last byte used in file LONGLONG LastUpdateTime; // date/time last record was written LONGLONG FirstDataRecordOffset; // location of first data record in file // makes the info struct 256 bytes // and leaves room for future information DWORD dwReserved[38]; } PDHI_BINARY_LOG_INFO, *PPDHI_BINARY_LOG_INFO; typedef struct _PDHI_BINARY_LOG_HEADER_RECORD { PDHI_BINARY_LOG_RECORD_HEADER RecHeader; PDHI_BINARY_LOG_INFO Info; } PDHI_BINARY_LOG_HEADER_RECORD, *PPDHI_BINARY_LOG_HEADER_RECORD; // new but unexported pdh functions // these will need to be moved to the PDH.H file // in the final shipped version #ifdef UNICODE PDH_FUNCTION PdhListLogFileHeaderW ( IN LPCWSTR szFileName, IN LPWSTR mszHeaderList, IN LPDWORD pcchHeaderListSize ); #define PdhListLogFileHeader PdhListLogFileHeaderW #else //ANSI PDH_FUNCTION PdhListLogFileHeaderA ( IN LPCSTR szFileName, IN LPSTR mszHeaderList, IN LPDWORD pcchHeaderListSize ); #define PdhListLogFileHeader PdhListLogFileHeaderA #endif DWORD GetOutputLogType( LPTSTR str ) { DWORD dwLogType = PDH_LOG_TYPE_CSV; if (_tcscmp(str, cszBinType) == 0) { dwLogType = PDH_LOG_TYPE_BINARY; } else if (_tcscmp(str, cszBlgType) == 0) { dwLogType = PDH_LOG_TYPE_BINARY; } else if (_tcscmp(str, cszCsvType) == 0) { dwLogType = PDH_LOG_TYPE_CSV; } else if (_tcscmp(str, cszTsvType) == 0) { dwLogType = PDH_LOG_TYPE_TSV; } else { // return unknown dwLogType = PDH_LOG_TYPE_UNDEFINED; } return dwLogType; } DWORD DoListMode ( LPCTSTR szInputFile, LPCTSTR szOutputFile ) { // BUGBUG: note these should be dynamically allocated LPTSTR szReturnBuffer; TCHAR szMachineList[32768]; TCHAR szWildPath[32768]; // end bugbug PDH_STATUS pdhStatus; DWORD dwBufSize; DWORD dwMlSize; LPTSTR szThisString; LPTSTR szThisMachine; FILE *fOut = NULL; DWORD dwNumEntries; PDH_TIME_INFO pInfo[2]; DWORD dwBufferSize = (sizeof(pInfo)); SYSTEMTIME stLogTime; dwMlSize = sizeof(szMachineList) / sizeof(szMachineList[0]); pdhStatus = PdhEnumMachines ( szInputFile, szMachineList, &dwMlSize); if ((pdhStatus == ERROR_SUCCESS) && (dwMlSize > 0)) { if (*szOutputFile == 0) { fOut = stdout; } else { fOut = _tfopen (szOutputFile, (LPCTSTR)TEXT("wt")); } pdhStatus = PdhGetDataSourceTimeRange ( szInputFile, &dwNumEntries, &pInfo[0], &dwBufferSize); if (pdhStatus == ERROR_SUCCESS) { _ftprintf(fOut, (LPCTSTR)TEXT("\nLogfile: \"%s\" contains %d Records."), szInputFile, pInfo[0].SampleCount); // write time range out to file FileTimeToSystemTime ((FILETIME *)(&pInfo[0].StartTime), &stLogTime); _ftprintf(fOut, (LPCTSTR)TEXT("\n Start Time: %4.4d-%2.2d-%2.2d-%2.2d:%2.2d:%2.2d"), stLogTime.wYear, stLogTime.wMonth, stLogTime.wDay, stLogTime.wHour, stLogTime.wMinute, stLogTime.wSecond); FileTimeToSystemTime ((FILETIME *)(&pInfo[0].EndTime), &stLogTime); _ftprintf(fOut, (LPCTSTR)TEXT("\n End Time: %4.4d-%2.2d-%2.2d-%2.2d:%2.2d:%2.2d"), stLogTime.wYear, stLogTime.wMonth, stLogTime.wDay, stLogTime.wHour, stLogTime.wMinute, stLogTime.wSecond); } for (szThisMachine = szMachineList; *szThisMachine != 0; szThisMachine += _tcsclen(szThisMachine)) { if (*szThisMachine != _T('\\')) { _tcscpy (szWildPath, (LPCTSTR)TEXT("\\\\")); _tcscat (szWildPath, szThisMachine); _tcscat (szWildPath, (LPCTSTR)TEXT("\\*(*)\\*")); } else { _tcscpy (szWildPath, szThisMachine); _tcscat (szWildPath, (LPCTSTR)TEXT("\\*(*)\\*")); } dwBufSize = 512000; szReturnBuffer = HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, dwBufSize * sizeof(TCHAR)); if (szReturnBuffer != NULL) { pdhStatus = PdhExpandWildCardPath ( szInputFile, szWildPath, szReturnBuffer, &dwBufSize, 0); if (dwBufSize != 0) { if (fOut == stdout) { _ftprintf (fOut, (LPCTSTR)TEXT("\nCounters contained in \"%s\":\n"), szInputFile); } for (szThisString = szReturnBuffer; *szThisString != 0; szThisString += _tcsclen(szThisString) + 1) { _ftprintf (fOut, (LPCTSTR)TEXT("%s\n"), szThisString); } } else { _tprintf ((LPCTSTR)TEXT("\nError 0x%8.8x (%d) returned from enumeration of machine %s in %s"), pdhStatus, (DWORD)(pdhStatus & 0x0000FFFF), szThisMachine, szInputFile); } HeapFree (GetProcessHeap(), 0, szReturnBuffer); } } } else { // unable to list log file contents _tprintf ((LPCTSTR)TEXT("\nError 0x%8.8x (%d) returned from enumeration of %s"), pdhStatus, (DWORD)(pdhStatus & 0x0000FFFF), szInputFile); } return ERROR_SUCCESS; } DWORD DoHeaderListMode ( LPCTSTR szInputFile, LPCTSTR szOutputFile ) { // end bugbug PDH_STATUS pdhStatus; LPTSTR szThisString; FILE *fOut = NULL; DWORD dwType = 0L; HLOG hLog = NULL; LPTSTR mszHeaderList; DWORD cchHeaderListSize = 0x80000; // 512K DWORD dwNumEntries; PDH_TIME_INFO pInfo[2]; DWORD dwBufferSize = (sizeof(pInfo)); SYSTEMTIME stLogTime; mszHeaderList = (LPTSTR)HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, cchHeaderListSize * sizeof(TCHAR)); if (mszHeaderList != NULL) { pdhStatus = PdhListLogFileHeader ( szInputFile, mszHeaderList, &cchHeaderListSize); if (pdhStatus == ERROR_SUCCESS) { if (*szOutputFile == 0) { fOut = stdout; } else { fOut = _tfopen (szOutputFile, (LPCTSTR)TEXT("wt")); } pdhStatus = PdhGetDataSourceTimeRange ( szInputFile, &dwNumEntries, &pInfo[0], &dwBufferSize); if (pdhStatus == ERROR_SUCCESS) { _ftprintf(fOut, (LPCTSTR)TEXT("\nLogfile: \"%s\" contains %d Records."), szInputFile, pInfo[0].SampleCount); // write time range out to file FileTimeToSystemTime ((FILETIME *)(&pInfo[0].StartTime), &stLogTime); _ftprintf(fOut, (LPCTSTR)TEXT("\n Start Time: %4.4d-%2.2d-%2.2d-%2.2d:%2.2d:%2.2d"), stLogTime.wYear, stLogTime.wMonth, stLogTime.wDay, stLogTime.wHour, stLogTime.wMinute, stLogTime.wSecond); FileTimeToSystemTime ((FILETIME *)(&pInfo[0].EndTime), &stLogTime); _ftprintf(fOut, (LPCTSTR)TEXT("\n End Time: %4.4d-%2.2d-%2.2d-%2.2d:%2.2d:%2.2d"), stLogTime.wYear, stLogTime.wMonth, stLogTime.wDay, stLogTime.wHour, stLogTime.wMinute, stLogTime.wSecond); } if (fOut == stdout) { _ftprintf (fOut, (LPCTSTR)TEXT("\nCounters contained in %s\n"), szInputFile); } for (szThisString = mszHeaderList; *szThisString != 0; szThisString += _tcsclen(szThisString) + 1) { _ftprintf (fOut, (LPCTSTR)TEXT("%s\n"), szThisString); } } else { // unable to list log file contents _tprintf ((LPCTSTR)TEXT("\nError 0x%8.8x (%d) returned from enumeration of %s"), pdhStatus, (DWORD)(pdhStatus & 0x0000FFFF), szInputFile); } HeapFree (GetProcessHeap(), HEAP_ZERO_MEMORY, mszHeaderList); } return ERROR_SUCCESS; } HRESULT GetCountersFromFile( HQUERY hQuery ) { TCHAR buffer[MAXSTR]; HCOUNTER pCounter; HRESULT hr; LPTSTR strCounter = NULL; FILE* f = _tfopen( Commands[eCounters].strValue, _T("r") ); if( !f ){ return GetLastError(); } while( NULL != _fgetts( buffer, MAXSTR, f ) ){ if( buffer[0] == ';' || // comments buffer[0] == '#' ){ continue; } Chomp(buffer); strCounter = _tcstok( buffer, _T("\"\n") ); if( strCounter != NULL ){ hr = PdhAddCounter( hQuery, buffer, 0, &pCounter ); } } fclose( f ); return ERROR_SUCCESS; } DWORD DoNormalMode ( LPCTSTR szInputFile, LPCTSTR szOutputFile, LPCTSTR szSettingsFile, DWORD dwOutputLogType, PDH_TIME_INFO pdhTimeInfo, DWORD dwFilterCount ) { DWORD dwNumOutputCounters = 0; DWORD dwRecCount = 0; DWORD dwFiltered; LONG Status = ERROR_SUCCESS; PDH_STATUS PdhStatus; PDH_RAW_COUNTER RawValue; LPTSTR szCounterPath = NULL; HQUERY hQuery = NULL; HLOG hOutLog = NULL; HLOG hInLog = NULL; HCOUNTER hCounter = NULL; HCOUNTER hLastGoodCounter = NULL; DWORD dwType; DWORD dwOpenMode; LPTSTR szReturnBuffer; TCHAR szMachineList[32768]; TCHAR szWildPath[32768]; // end bugbug PDH_STATUS pdhStatus; DWORD dwBufSize; DWORD dwMlSize; LPTSTR szThisString; LPTSTR szThisMachine; FILE *fOut = NULL; if (Status == ERROR_SUCCESS) { _tprintf ((LPCTSTR)TEXT("\nRelogging: \"%s\" to \"%s\""), szInputFile, szOutputFile); PdhStatus = PdhOpenQuery (szInputFile, 0L, &hQuery); if (PdhStatus != ERROR_SUCCESS) { Status = PdhStatus; _tprintf ((LPCTSTR)TEXT("\nPdhOpenQuery returned: 0x%8.8x (%d)"), PdhStatus, PdhStatus); } } if (Status == ERROR_SUCCESS) { if (*szSettingsFile == 0) { if (dwOutputLogType == PDH_LOG_TYPE_BINARY) { // load all counters from input file into query LPTSTR mszHeaderList; DWORD cchHeaderListSize = 0x80000; // 512K mszHeaderList = (LPTSTR)HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, cchHeaderListSize * sizeof(TCHAR)); if (mszHeaderList != NULL) { PdhStatus = PdhListLogFileHeader ( szInputFile, mszHeaderList, &cchHeaderListSize); if (PdhStatus == ERROR_SUCCESS) { // we can recycle the hCounter value since we don't need it for anything after this. for (szCounterPath = mszHeaderList; *szCounterPath != 0; szCounterPath += _tcsclen(szCounterPath) + 1) { PdhStatus = PdhAddCounter (hQuery, szCounterPath, 0L, &hCounter); if (PdhStatus != ERROR_SUCCESS) { _tprintf ((LPCTSTR)TEXT("\nUnable to add \"%s\", error: 0x%8.8x (%d)"), szCounterPath, PdhStatus, PdhStatus); } else { hLastGoodCounter = hCounter; dwNumOutputCounters++; if (dwDbgPrintLevel == DBG_SHOW_STATUS_PRINTS) { _tprintf ((LPCTSTR)TEXT("\nRelogging \"%s\""), szCounterPath); } } } } HeapFree (GetProcessHeap(), 0, mszHeaderList); } } else { // enumerate each counter for all non-binary log types dwMlSize = sizeof(szMachineList) / sizeof(szMachineList[0]); pdhStatus = PdhEnumMachines ( szInputFile, szMachineList, &dwMlSize); if ((pdhStatus == ERROR_SUCCESS) && (dwMlSize > 0)) { for (szThisMachine = szMachineList; *szThisMachine != 0; szThisMachine += _tcsclen(szThisMachine)) { if (*szThisMachine != _T('\\')) { _tcscpy (szWildPath, (LPCTSTR)TEXT("\\\\")); _tcscat (szWildPath, szThisMachine); _tcscat (szWildPath, (LPCTSTR)TEXT("\\*(*)\\*")); } else { _tcscpy (szWildPath, szThisMachine); _tcscat (szWildPath, (LPCTSTR)TEXT("\\*(*)\\*")); } dwBufSize = 512000; szReturnBuffer = HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, dwBufSize * sizeof(TCHAR)); if (szReturnBuffer != NULL) { pdhStatus = PdhExpandWildCardPath ( szInputFile, szWildPath, szReturnBuffer, &dwBufSize, 0); if (dwBufSize != 0) { for (szThisString = szReturnBuffer; *szThisString != 0; szThisString += _tcsclen(szThisString) + 1) { PdhStatus = PdhAddCounter (hQuery, szThisString, 0L, &hCounter); if (PdhStatus != ERROR_SUCCESS) { _tprintf ((LPCTSTR)TEXT("\nUnable to add \"%s\", error: 0x%8.8x (%d)"), szThisString, PdhStatus, PdhStatus); } else { hLastGoodCounter = hCounter; dwNumOutputCounters++; if (dwDbgPrintLevel == DBG_SHOW_STATUS_PRINTS) { _tprintf ((LPCTSTR)TEXT("\nAdded \"%s\", error: 0x%8.8x (%d)"), szThisString, PdhStatus, PdhStatus); } } } } HeapFree (GetProcessHeap(), 0, szReturnBuffer); } else { // unable to allocate memory PdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; } } // end for each machine in the log file } else { // unable to list machines // return PDH status } } } else { // we can recycle the hCounter value since we don't need it for anything after this. GetCountersFromFile( hQuery ); } } if ((Status == ERROR_SUCCESS) && (dwNumOutputCounters > 0)) { dwOpenMode = PDH_LOG_WRITE_ACCESS | PDH_LOG_CREATE_ALWAYS; PdhStatus = PdhOpenLog (szOutputFile, dwOpenMode, &dwOutputLogType, hQuery, 0L, NULL, &hOutLog); if (PdhStatus != ERROR_SUCCESS) { Status = PdhStatus; _tprintf ((LPCTSTR)TEXT("\nUnable to open \"%s\" for output, error: 0x%8.8x (%d)"), szOutputFile, PdhStatus, PdhStatus); } else { // set query range PdhStatus = PdhSetQueryTimeRange (hQuery, &pdhTimeInfo); // copy log data to output log PdhStatus = PdhUpdateLog (hOutLog, NULL); while (PdhStatus == ERROR_SUCCESS) { dwRecCount++; dwFiltered = 1; while ((dwFiltered < dwFilterCount) && (PdhStatus == ERROR_SUCCESS)) { PdhStatus = PdhCollectQueryData(hQuery); if (PdhStatus == ERROR_SUCCESS) { PdhStatus = PdhGetRawCounterValue ( hLastGoodCounter, &dwType, &RawValue); if (PdhStatus == ERROR_SUCCESS) { // check for bogus timestamps as an inidcation we ran off the end of the file if ((*(LONGLONG *)&RawValue.TimeStamp == 0) || (*(LONGLONG *)&RawValue.TimeStamp >= pdhTimeInfo.EndTime)){ PdhStatus = PDH_END_OF_LOG_FILE; } } } dwFiltered++; } if (PdhStatus == ERROR_SUCCESS) { PdhStatus = PdhUpdateLog (hOutLog, NULL); } } // PdhStatus should be either PDH_END_OF_LOG_FILE or PDH_NO_MORE_DATA when // the loop above exits, if that's the case then reset status to SUCCESS // otherwise display the error if ((PdhStatus == PDH_END_OF_LOG_FILE) || (PdhStatus == PDH_NO_MORE_DATA)) { PdhStatus = ERROR_SUCCESS; } else { printf ("\nPdhUpdateLog returned: 0x%8.8x (%d)", PdhStatus, PdhStatus); } // update log catalog while we're at it // // BUGBUG: For now this isn't working very well so this step // will be skipped until it works better (5.1 maybe?) /* if (dwOutputLogType == PDH_LOG_TYPE_BINARY) { PdhStatus = PdhUpdateLogFileCatalog (hOutLog); if (PdhStatus != ERROR_SUCCESS) { Status = PdhStatus; _tprintf ((LPCTSTR)TEXT("\nPdhUpdateLogFileCatalog returned: 0x%8.8x (%d)"), PdhStatus, PdhStatus); } } */ PdhStatus = PdhCloseLog (hOutLog, 0L); if (PdhStatus != ERROR_SUCCESS) { Status = PdhStatus; printf ("\nPdhCloseLog returned: 0x%8.8x (%d)", PdhStatus, PdhStatus); } else { hOutLog = NULL; } } } if (hQuery != NULL) { PdhStatus = PdhCloseQuery (hQuery); if (PdhStatus != ERROR_SUCCESS) { Status = PdhStatus; printf ("\nPdhCloseLog returned: 0x%8.8x (%d)", PdhStatus, PdhStatus); } else { hQuery = NULL; hCounter = NULL; } } if (Status == ERROR_SUCCESS) { _tprintf ((LPCTSTR)TEXT("\n%d records from %s have been relogged to %s"), dwRecCount, szInputFile, szOutputFile); } return Status; } DWORD DoAppendFiles ( IN LPCTSTR szAppendFile, IN LPCTSTR szBaseFile, IN DWORD dwFilterCount ) /* append data records from Append file to base file if they contain the same counter data (and hopefully append file starts after base file) */ { HANDLE hTempFile; TCHAR szTempFileName[MAX_PATH]; TCHAR szTempDirPath[MAX_PATH]; DWORD dwReturn = ERROR_SUCCESS; PDH_STATUS pdhStatus; DWORD dwFiltered; DWORD dwAppendLogType; DWORD dwBaseLogType; HLOG hAppendLogFile; HLOG hBaseLogFile; LPTSTR mszBaseFileHeader; DWORD cchBaseFileHeaderSize; LPTSTR mszAppendFileHeader; DWORD cchAppendFileHeaderSize; PPDH_RAW_LOG_RECORD pRawRecord; DWORD dwRecordBuffSize; DWORD dwRecordSize; DWORD dwRecordIdx; BOOL bStatus; DWORD dwBytesWritten; PPDHI_BINARY_LOG_HEADER_RECORD pBinLogHead; FILETIME ftValue; DWORD dwLogRecType; BOOL bIsDataRecord; // see if the file headers match // read headers of log files cchBaseFileHeaderSize = 0x80000; mszBaseFileHeader = (LPTSTR)HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, cchBaseFileHeaderSize * sizeof(TCHAR)); if (mszBaseFileHeader != NULL) { pdhStatus = PdhListLogFileHeader ( szBaseFile, mszBaseFileHeader, &cchBaseFileHeaderSize); if (pdhStatus == ERROR_SUCCESS) { cchAppendFileHeaderSize = 0x80000; mszAppendFileHeader = (LPTSTR)HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, cchAppendFileHeaderSize * sizeof(TCHAR)); if (mszAppendFileHeader != NULL) { pdhStatus = PdhListLogFileHeader ( szAppendFile, mszAppendFileHeader, &cchAppendFileHeaderSize); if (pdhStatus == ERROR_SUCCESS) { // compare buffers if (cchAppendFileHeaderSize == cchBaseFileHeaderSize) { if (memcmp(mszAppendFileHeader, mszBaseFileHeader, cchBaseFileHeaderSize) == 0) { // same pdhStatus = ERROR_SUCCESS; } else { // different _tprintf ((LPCTSTR)TEXT("\nInput file counter list is different from that of the output file.")); pdhStatus = ERROR_DATATYPE_MISMATCH; } } else { _tprintf ((LPCTSTR)TEXT("\nInput file counter list is different from that of the output file.")); // different sizes pdhStatus = ERROR_DATATYPE_MISMATCH; } } else { // unable to read append file _tprintf ((LPCTSTR)TEXT("\nUnable to read the input file header.")); } HeapFree (GetProcessHeap(), HEAP_ZERO_MEMORY, mszAppendFileHeader); } else { _tprintf ((LPCTSTR)TEXT("\nUnable to allocate an internal memory buffer.")); pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; } } else { // unable to read base file header _tprintf ((LPCTSTR)TEXT("\nUnable to read the output file header.")); } HeapFree (GetProcessHeap(), HEAP_ZERO_MEMORY, mszBaseFileHeader); } else { pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; } if (pdhStatus == ERROR_SUCCESS) { // the files have matching headers so get ready to copy them // create temporary output file name GetTempPath (MAX_PATH, szTempDirPath); GetTempFileName (szTempDirPath, (LPCTSTR)TEXT("PDH"), 0, szTempFileName); hTempFile = CreateFile ( szTempFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, // the file is created by GetTempFileName above (go figure) FILE_ATTRIBUTE_NORMAL, NULL); if (hTempFile != INVALID_HANDLE_VALUE) { // open log files pdhStatus = PdhOpenLog ( szBaseFile, PDH_LOG_READ_ACCESS | PDH_LOG_OPEN_EXISTING, &dwBaseLogType, NULL, 0, NULL, &hBaseLogFile); if (pdhStatus == ERROR_SUCCESS) { pdhStatus = PdhOpenLog ( szAppendFile, PDH_LOG_READ_ACCESS | PDH_LOG_OPEN_EXISTING, &dwAppendLogType, NULL, 0, NULL, &hAppendLogFile); if (pdhStatus == ERROR_SUCCESS) { dwRecordIdx = 1; ftValue.dwHighDateTime = 0xFFFFFFFF; ftValue.dwLowDateTime = dwRecordIdx; dwRecordBuffSize = 0x80000; pRawRecord = HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, dwRecordBuffSize); if (pRawRecord != NULL) { dwFiltered = 1; // write headers from first file to temp file while (pdhStatus == ERROR_SUCCESS) { ftValue.dwHighDateTime = 0xFFFFFFFF; ftValue.dwLowDateTime = dwRecordIdx; dwRecordSize = dwRecordBuffSize; pdhStatus = PdhReadRawLogRecord ( hBaseLogFile, ftValue, pRawRecord, &dwRecordSize); if (pdhStatus == ERROR_SUCCESS) { bIsDataRecord = TRUE; if (dwRecordIdx == 1) { bIsDataRecord = FALSE; } else if ((dwRecordIdx == 2) && (pRawRecord->dwRecordType == PDH_LOG_TYPE_BINARY)) { pBinLogHead = (PPDHI_BINARY_LOG_HEADER_RECORD)&pRawRecord->RawBytes[0]; // only linear files can be appended if (pBinLogHead->Info.WrapOffset == 0) { // now fix up the fields in the header so they'll work // with the new combined file pBinLogHead->Info.FileLength = 0; // file space allocated (optional) pBinLogHead->Info.CatalogOffset = 0; // the catalog is removed pBinLogHead->Info.CatalogChecksum = 0; pBinLogHead->Info.CatalogDate = 0; // date/time catalog was updated //pBinLogHead->Info.FirstRecordOffset = 0; // pointer to first record [to read] in log pBinLogHead->Info.LastRecordOffset = 0; // pointer to last record [to read] in log pBinLogHead->Info.NextRecordOffset = 0; // pointer to where next one goes pBinLogHead->Info.WrapOffset = 0; // pointer to last byte used in file pBinLogHead->Info.LastUpdateTime = 0; // date/time last record was written pBinLogHead->Info.FirstDataRecordOffset = 0; // location of first data record in file } else { // file is circular so bail pdhStatus = PDH_UNKNOWN_LOG_FORMAT; } bIsDataRecord = FALSE; } if (pdhStatus == ERROR_SUCCESS) { // write the first 2 records of the base file // then only the data records after that if ((dwRecordIdx > 2) && (pRawRecord->dwRecordType == PDH_LOG_TYPE_BINARY)) { // it must be a data record or else skip it dwLogRecType = *((LPDWORD)&pRawRecord->RawBytes[0]); if ((dwLogRecType & 0x00FF0000) != 0x00030000) { // then this is not a data record // so skip it and get next record dwRecordIdx++; continue; } } if ((!bIsDataRecord) || (dwFiltered == dwFilterCount)) { // write the record to the output file bStatus = WriteFile ( hTempFile, &pRawRecord->RawBytes[0], pRawRecord->dwItems, &dwBytesWritten, NULL); if (!bStatus || (dwBytesWritten != pRawRecord->dwItems)) { pdhStatus = GetLastError(); } else { // reset the count dwFiltered = 1; } } else { // skip this one if (bIsDataRecord) dwFiltered += 1; } // get next record dwRecordIdx++; } } else if (pdhStatus == PDH_INSUFFICIENT_BUFFER) { // BUGBUG: expand and retry } else { if ((pdhStatus == PDH_END_OF_LOG_FILE) || (pdhStatus == PDH_ENTRY_NOT_IN_LOG_FILE)) { // fix up return codes to continue pdhStatus = ERROR_SUCCESS; } else { _tprintf ((LPCTSTR)TEXT("\n ReadRaw returned %d (0x%8.8x)"), pdhStatus, pdhStatus); } // bail break; } } // add records from new file if (pdhStatus == ERROR_SUCCESS) { dwRecordIdx = 1; ftValue.dwHighDateTime = 0xFFFFFFFF; ftValue.dwLowDateTime = dwRecordIdx; if (pRawRecord != NULL) { // write headers from first file to temp file while (pdhStatus == ERROR_SUCCESS) { ftValue.dwHighDateTime = 0xFFFFFFFF; ftValue.dwLowDateTime = dwRecordIdx; dwRecordSize = dwRecordBuffSize; pdhStatus = PdhReadRawLogRecord ( hAppendLogFile, ftValue, pRawRecord, &dwRecordSize); if (pdhStatus == ERROR_SUCCESS) { bIsDataRecord = TRUE; if (dwRecordIdx == 1) { bIsDataRecord = FALSE; } else if ((dwRecordIdx == 2) && (pRawRecord->dwRecordType == PDH_LOG_TYPE_BINARY)) { // write only the data records to the output file // if this isn't the first record, then it must be a data record or // or else skip it dwLogRecType = *((LPDWORD)&pRawRecord->RawBytes[0]); if ((pRawRecord->dwRecordType == PDH_LOG_TYPE_BINARY) && ((dwLogRecType & 0x00FF0000) != 0x00030000)) { // then this is not a data record // so skip it and get next record dwRecordIdx++; continue; } } if ((!bIsDataRecord) || (dwFiltered == dwFilterCount)) { bStatus = WriteFile ( hTempFile, &pRawRecord->RawBytes[0], pRawRecord->dwItems, &dwBytesWritten, NULL); if (!bStatus || (dwBytesWritten != pRawRecord->dwItems)) { pdhStatus = GetLastError(); } else { // reset the count dwFiltered = 1; } } else { // skip this one if (bIsDataRecord) dwFiltered += 1; } // get next record dwRecordIdx++; } else if (pdhStatus == PDH_INSUFFICIENT_BUFFER) { // BUGBUG: expand and retry } else { if ((pdhStatus == PDH_END_OF_LOG_FILE) || (pdhStatus == PDH_ENTRY_NOT_IN_LOG_FILE)) { // fix up return codes to continue pdhStatus = ERROR_SUCCESS; } else { _tprintf ((LPCTSTR)TEXT("\n ReadRaw returned %d (0x%8.8x)"), pdhStatus, pdhStatus); } // bail break; } } } else { // no buffer } } // clean up PdhCloseLog (hAppendLogFile,0); PdhCloseLog (hBaseLogFile,0); CloseHandle (hTempFile); // shuffle the files around to make it look like it was appended if (pdhStatus == ERROR_SUCCESS) { if (CopyFile (szTempFileName, szBaseFile, FALSE)) { DeleteFile (szTempFileName); } } } else { // alloc fail _tprintf ((LPCTSTR)TEXT("\nUnable to allocate temporary memory buffer.")); } } else { //unable to open new file for reading _tprintf ((LPCTSTR)TEXT("\nUnable to open input file for reading.")); dwReturn = pdhStatus; PdhCloseLog (hBaseLogFile,0); CloseHandle (hTempFile); DeleteFile (szTempFileName); } } else { //unable to open base file for reading _tprintf ((LPCTSTR)TEXT("\nUnable to open output file for reading.")); dwReturn = pdhStatus; CloseHandle (hTempFile); DeleteFile (szTempFileName); } } else { // unable to create temp file _tprintf ((LPCTSTR)TEXT("\nUnable to create temporary file in temp dir.")); dwReturn = GetLastError(); } } else { dwReturn = pdhStatus; } return dwReturn; } int __cdecl wmain( int argc, _TCHAR *argv[] ) /* relog /Input: /Output: /Settings: /Logtype:[BIN|BLG|TSV|CSV] /StartTime:yyyy-mm-dd-hh:mm:ss /EndTime;yyyy-mm-dd-hh:mm:ss /Filter:n */ { LONG Status = ERROR_SUCCESS; DWORD dwOutputLogType = PDH_LOG_TYPE_UNDEFINED; DWORD dwFilterCount = 1; DWORD dwMode = NORMAL_MODE; PDH_TIME_INFO pdhTimeInfo; DWORD dwPdhVersion = 0; ParseCmd( argc, argv ); Status = PdhGetDllVersion (&dwPdhVersion); dwOutputLogType = GetOutputLogType( Commands[eFormat].strValue ); pdhTimeInfo.StartTime = 0; pdhTimeInfo.EndTime = 0; if( Commands[eBegin].bDefined ){ FILETIME ft; SystemTimeToFileTime( &Commands[eBegin].stValue, &ft ); pdhTimeInfo.StartTime = *(LONGLONG *)&ft; } if( Commands[eEnd].bDefined ){ FILETIME ft; SystemTimeToFileTime( &Commands[eEnd].stValue, &ft ); pdhTimeInfo.EndTime = *(LONGLONG *)&ft; } pdhTimeInfo.SampleCount = 0; // szXXXFile cannot be NULL at this point! if ( Commands[eQuery].bValue ) { dwMode = LIST_MODE; } else if (Commands[eAppend].bValue ) { dwMode = APPEND_MODE; } if (Status == ERROR_SUCCESS) { switch (dwMode) { case HEADER_MODE: Status = DoHeaderListMode ( Commands[eInput].strValue, Commands[eOutput].strValue); break; case LIST_MODE: Status = DoListMode ( Commands[eInput].strValue, Commands[eOutput].strValue); break; case APPEND_MODE: Status = DoAppendFiles ( Commands[eInput].strValue, Commands[eOutput].strValue, Commands[eInterval].nValue ); break; case NORMAL_MODE: default: Status = DoNormalMode ( Commands[eInput].strValue, Commands[eOutput].strValue, Commands[eCounters].strValue, dwOutputLogType, pdhTimeInfo, Commands[eInterval].nValue); break; } } return Status; }