/*++ Copyright (C) 1996-1999 Microsoft Corporation Module Name: log_bin.c Abstract: --*/ #include #include #include #include #include //#define _SHOW_PDH_MEM_ALLOCS 1 #include "pdhidef.h" #include "log_bin.h" #include "log_wmi.h" #include "strings.h" #include "pdhmsg.h" typedef struct _LOG_BIN_CAT_RECORD { PDHI_BINARY_LOG_RECORD_HEADER RecHeader; PDHI_LOG_CAT_ENTRY CatEntry; DWORD dwEntryRecBuff[1]; } LOG_BIN_CAT_RECORD, *PLOG_BIN_CAT_RECORD; typedef struct _LOG_BIN_CAT_ENTRY { DWORD dwEntrySize; DWORD dwOffsetToNextInstance; DWORD dwEntryOffset; LOG_BIN_CAT_RECORD bcRec; } LOG_BIN_CAT_ENTRY, *PLOG_BIN_CAT_ENTRY; #define RECORD_AT(p,lo) ((PPDHI_BINARY_LOG_RECORD_HEADER)((LPBYTE)(p->lpMappedFileBase) + lo)) LPCSTR PdhiszRecordTerminator = "\r\n"; DWORD PdhidwRecordTerminatorLength = 2; #define MAX_BINLOG_FILE_SIZE ((LONGLONG)0x0000000040000000) // dwFlags values #define WBLR_WRITE_DATA_RECORD 0 #define WBLR_WRITE_LOG_HEADER 1 #define WBLR_WRITE_COUNTER_HEADER 2 PDH_FUNCTION PdhiWriteOneBinaryLogRecord ( PPDHI_LOG pLog, LPCVOID pData, DWORD dwSize, LPDWORD pdwRtnSize, DWORD dwFlags) { PDH_STATUS pdhStatus = ERROR_SUCCESS; DWORD dwRtnSize; LONGLONG llFirstOffset = 0; LONGLONG llThisOffset =0; LONGLONG llNextOffset =0; LONGLONG llLastOffset =0; LONGLONG llWrapOffset =0; PPDHI_BINARY_LOG_HEADER_RECORD pHeader; DWORD dwRecLen; LPVOID lpDest = NULL; LARGE_INTEGER liFileSize; SetLastError (ERROR_SUCCESS); if (pLog->lpMappedFileBase != NULL) { if (dwSize > pLog->llMaxSize) { // This record is too large to ever fit in this file pdhStatus = PDH_LOG_FILE_TOO_SMALL; } else { // T if (dwFlags == WBLR_WRITE_DATA_RECORD) { // this is a mapped file so it's either a circular // or a limited linear file. // get the address of the log file header record. pHeader = (PPDHI_BINARY_LOG_HEADER_RECORD) ((LPBYTE)(pLog->lpMappedFileBase) + pLog->dwRecord1Size); // then write the record using memory functions since the // file is mapped as a memory section // 1st see if there's room in the file for this record llNextOffset = pHeader->Info.NextRecordOffset + dwSize; if (llNextOffset > pLog->llMaxSize) { if (pLog->dwLogFormat & PDH_LOG_OPT_CIRCULAR) { // if circular, then start back at the beginning llWrapOffset = pHeader->Info.NextRecordOffset; if ((pLog->llMaxSize - llWrapOffset) > dwSize) { // This record is too large to ever fit in the rest of this file pdhStatus = PDH_LOG_FILE_TOO_SMALL; } else { llThisOffset = pHeader->Info.FirstDataRecordOffset; llNextOffset = llThisOffset + dwSize; llLastOffset = llThisOffset; llFirstOffset = llThisOffset; while (llFirstOffset < llNextOffset) { dwRecLen = RECORD_AT(pLog, llFirstOffset)->dwLength; if (dwRecLen > 0) { llFirstOffset += dwRecLen; } else { // this record is unused so try it. break; } assert (llFirstOffset < llWrapOffset); } } } else { // that's all that will fit so return // file is full error pdhStatus = PDH_END_OF_LOG_FILE; dwRtnSize = 0; } } else { // this record will fit in the remaining space of // the log file so write it llThisOffset = pHeader->Info.NextRecordOffset; llLastOffset = llThisOffset; llWrapOffset = pHeader->Info.WrapOffset; llFirstOffset = pHeader->Info.FirstRecordOffset; if (llWrapOffset != 0) { // check next pointer to see if we're on the // 2nd or more lap through a circular log // in which case, the first record should come after the last while (llNextOffset > llFirstOffset) { llFirstOffset += RECORD_AT(pLog, llFirstOffset)->dwLength; // check for running past the end of the file, in which case // the first record can be found at the beginning of the log if (llFirstOffset >= llWrapOffset) { llFirstOffset = pHeader->Info.FirstDataRecordOffset; llWrapOffset = llNextOffset; break; } } if (llNextOffset > llWrapOffset) llWrapOffset = llNextOffset; } else { // this is just a linear log or a circular log on the first lap // so there's nothing to do } } if (pdhStatus == ERROR_SUCCESS) { // test for strays here before continuing assert (llThisOffset < pLog->llMaxSize); assert (llNextOffset < pLog->llMaxSize); assert (llWrapOffset < pLog->llMaxSize); // here first, this, next and last should be set // first == the first record to be read from the log // this == where this record will be placed // next == where the next record after this one will be placed // last == the last record in the sequence // wrap == the last byte used in the log file // (not necessarily the end of the file) __try { // move the caller's data into the file lpDest = (LPVOID)RECORD_AT(pLog, llThisOffset); // make sure there's room in the section for this // record... if ((llThisOffset + dwSize) <= pLog->llMaxSize) { // looks like it'll fit RtlCopyMemory(lpDest, (LPVOID)pData, dwSize); // update the header fields pHeader->Info.NextRecordOffset = llNextOffset; pHeader->Info.FirstRecordOffset = llFirstOffset; pHeader->Info.LastRecordOffset = llLastOffset; pHeader->Info.WrapOffset = llWrapOffset; // write update time stamp GetSystemTimeAsFileTime ((LPFILETIME)(&pHeader->Info.LastUpdateTime)); if (pdwRtnSize != NULL) { *pdwRtnSize = dwSize; } // update the filesize if (llNextOffset > pHeader->Info.FileLength) { pHeader->Info.FileLength = llNextOffset; } assert (pHeader->Info.FileLength >= pHeader->Info.WrapOffset); } else { // this record is too big for the file pdhStatus = PDH_LOG_FILE_TOO_SMALL; } } __except (EXCEPTION_EXECUTE_HANDLER) { pdhStatus = GetExceptionCode(); } } else { // a error occured so pass it back up to the caller } } else { if (dwFlags == WBLR_WRITE_LOG_HEADER) { // this goes right at the start of the file lpDest = (LPVOID)pLog->lpMappedFileBase; RtlCopyMemory(lpDest, (LPVOID)pData, dwSize); } else if (dwFlags == WBLR_WRITE_COUNTER_HEADER) { assert (pLog->dwRecord1Size != 0); lpDest = (LPVOID)RECORD_AT(pLog, pLog->dwRecord1Size); RtlCopyMemory(lpDest, (LPVOID)pData, dwSize); } else { // This should never happen assert( dwFlags == WBLR_WRITE_LOG_HEADER || dwFlags == WBLR_WRITE_COUNTER_HEADER || dwFlags == WBLR_WRITE_DATA_RECORD); pdhStatus = PDH_INVALID_ARGUMENT; } } } } else { liFileSize.LowPart = GetFileSize ( pLog->hLogFileHandle, (LPDWORD)&liFileSize.HighPart); // add in new record to see if it will fit. liFileSize.QuadPart += dwSize; // test for maximum allowable filesize if (liFileSize.QuadPart <= MAX_BINLOG_FILE_SIZE) { // write the data to the file as a file if (!WriteFile (pLog->hLogFileHandle, pData, dwSize, pdwRtnSize, NULL)) { pdhStatus = GetLastError(); } else { FlushFileBuffers(pLog->hLogFileHandle); pdhStatus = ERROR_SUCCESS; } } else { pdhStatus = ERROR_LOG_FILE_FULL; } } return pdhStatus; } DWORD PdhiComputeDwordChecksum ( IN LPVOID pBuffer, IN DWORD dwBufferSize // in bytes ) { LPDWORD pDwVal; LPBYTE pByteVal; DWORD dwDwCount; DWORD dwByteCount; DWORD dwThisByte; DWORD dwCheckSum = 0; DWORD dwByteVal = 0; if (dwBufferSize > 0) { dwDwCount = dwBufferSize / sizeof(DWORD); dwByteCount = dwBufferSize % sizeof(DWORD); assert (dwByteCount >= 0); assert (dwByteCount < sizeof(DWORD)); pDwVal = (LPDWORD)pBuffer; while (dwDwCount != 0) { dwCheckSum += *pDwVal++; dwDwCount--; } assert (dwDwCount == 0); assert ((DWORD)((LPBYTE)pDwVal - (LPBYTE)pBuffer) <= dwBufferSize); pByteVal = (LPBYTE)pDwVal; dwThisByte = 0; while (dwThisByte < dwByteCount) { dwByteVal |= ((*pByteVal & 0x000000FF) << (dwThisByte * 8)); dwThisByte++; } assert ((DWORD)((LPBYTE)pByteVal - (LPBYTE)pBuffer) == dwBufferSize); dwCheckSum += dwByteVal; } return dwCheckSum; } PPDHI_BINARY_LOG_RECORD_HEADER PdhiGetSubRecord ( IN PPDHI_BINARY_LOG_RECORD_HEADER pRecord, IN DWORD dwRecordId ) // locates the specified sub record in the pRecord Buffer // the return pointer is between pRecord and pRecord + pRecord->dwLength; // NULL is returned if the specified record could not be found // ID values start at 1 for the first sub record in buffer { PPDHI_BINARY_LOG_RECORD_HEADER pThisRecord; DWORD dwRecordType; DWORD dwRecordLength; DWORD dwBytesProcessed; DWORD dwThisSubRecordId; dwRecordType = ((PPDHI_BINARY_LOG_RECORD_HEADER)pRecord)->dwType; dwRecordLength = ((PPDHI_BINARY_LOG_RECORD_HEADER)pRecord)->dwLength; pThisRecord = (PPDHI_BINARY_LOG_RECORD_HEADER)((LPBYTE)pRecord + sizeof (PDHI_BINARY_LOG_RECORD_HEADER)); dwBytesProcessed = sizeof(PDHI_BINARY_LOG_RECORD_HEADER); if (dwBytesProcessed < dwRecordLength) { dwThisSubRecordId = 1; while (dwThisSubRecordId < dwRecordId) { if ((WORD)(pThisRecord->dwType & 0x0000FFFF) == BINLOG_START_WORD) { // go to next sub record dwBytesProcessed += pThisRecord->dwLength; pThisRecord = (PPDHI_BINARY_LOG_RECORD_HEADER) (((LPBYTE)pThisRecord) + pThisRecord->dwLength); if (dwBytesProcessed >= dwRecordLength) { // out of sub-records so exit break; } else { dwThisSubRecordId++; } } else { // we're lost so bail break; } } } else { dwThisSubRecordId = 0; } if (dwThisSubRecordId == dwRecordId) { // then validate this is really a record and it's within the // master record. if ((WORD)(pThisRecord->dwType & 0x0000FFFF) != BINLOG_START_WORD) { // bogus record so return a NULL pointer pThisRecord = NULL; } else { // this is OK so return pointer } } else { // record not found so return a NULL pointer pThisRecord = NULL; } return pThisRecord; } STATIC_PDH_FUNCTION PdhiReadBinaryMappedRecord( IN PPDHI_LOG pLog, IN DWORD dwRecordId, IN LPVOID pRecord, IN DWORD dwMaxSize ) { PDH_STATUS pdhStatus= ERROR_SUCCESS; LPVOID pEndOfFile; LPVOID pLastRecord; DWORD dwLastRecordIndex; PPDHI_BINARY_LOG_HEADER_RECORD pHeader; PPDHI_BINARY_LOG_RECORD_HEADER pRecHeader; LPVOID pLastRecordInLog; DWORD dwBytesToRead; DWORD dwBytesRead; BOOL bStatus; if (dwRecordId == 0) return PDH_ENTRY_NOT_IN_LOG_FILE; // record numbers start at 1 // see if the file has been mapped if (pLog->hMappedLogFile == NULL) { // then it's not mapped so read it using the file system if ((pLog->dwLastRecordRead == 0) || (dwRecordId < pLog->dwLastRecordRead)) { // then we know no record has been read yet so assign // pointer just to be sure SetFilePointer (pLog->hLogFileHandle, 0, NULL, FILE_BEGIN); // allocate a new buffer if (pLog->dwMaxRecordSize < 0x10000) pLog->dwMaxRecordSize = 0x10000; dwBytesToRead = pLog->dwMaxRecordSize; // allocate a fresh buffer if (pLog->pLastRecordRead != NULL) { G_FREE(pLog->pLastRecordRead); pLog->pLastRecordRead = NULL; } pLog->pLastRecordRead = G_ALLOC (pLog->dwMaxRecordSize); if (pLog->pLastRecordRead == NULL) { pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; } else { // initialize the first record header dwBytesToRead = pLog->dwRecord1Size; dwBytesRead = 0; bStatus = ReadFile ( pLog->hLogFileHandle, pLog->pLastRecordRead, dwBytesToRead, &dwBytesRead, NULL); if (bStatus && (dwBytesRead == pLog->dwRecord1Size)) { // make sure the buffer is big enough pLog->dwLastRecordRead = 1; pdhStatus = ERROR_SUCCESS; } else { // unable to read the first record pdhStatus = PDH_UNABLE_READ_LOG_HEADER; } } } else { // assume everything is already set up and OK } // pHeader = (PPDHI_BINARY_LOG_HEADER_RECORD)RECORD_AT(pLog, pLog->dwRecord1Size); // assert (*(WORD *)&(pHeader->RecHeader.dwType) == BINLOG_START_WORD); // "seek" to the desired record file pointer should either be pointed // to the start of a new record or at the end of the file while ((dwRecordId != pLog->dwLastRecordRead) && (pdhStatus == ERROR_SUCCESS)) { // clear the buffer memset (pLog->pLastRecordRead, 0, pLog->dwMaxRecordSize); // read record header field dwBytesToRead = sizeof(PDHI_BINARY_LOG_RECORD_HEADER); dwBytesRead = 0; bStatus = ReadFile ( pLog->hLogFileHandle, pLog->pLastRecordRead, dwBytesToRead, &dwBytesRead, NULL); if (bStatus && (dwBytesRead == dwBytesToRead)) { // make sure the rest of the record will fit in the buffer pRecHeader = (PPDHI_BINARY_LOG_RECORD_HEADER)pLog->pLastRecordRead; // make sure this is a valid record if (*(WORD *)&(pRecHeader->dwType) == BINLOG_START_WORD) { if (pRecHeader->dwLength > pLog->dwMaxRecordSize) { // realloc the buffer LPVOID pTmp = pLog->pLastRecordRead; pLog->dwMaxRecordSize = pRecHeader->dwLength; pLog->pLastRecordRead = G_REALLOC(pTmp, pLog->dwMaxRecordSize); if (pLog->pLastRecordRead == NULL) { G_FREE(pTmp); } } if (pLog->pLastRecordRead != NULL) { dwBytesToRead = pRecHeader->dwLength - sizeof(PDHI_BINARY_LOG_RECORD_HEADER); dwBytesRead = 0; pLastRecord = (LPVOID)((LPBYTE)(pLog->pLastRecordRead) + sizeof(PDHI_BINARY_LOG_RECORD_HEADER)); bStatus = ReadFile ( pLog->hLogFileHandle, pLastRecord, dwBytesToRead, &dwBytesRead, NULL); if (bStatus) { pLog->dwLastRecordRead++; } else { pdhStatus = PDH_END_OF_LOG_FILE; } } else { pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; } } else { // file is corrupt pdhStatus = PDH_INVALID_DATA; } } else { pdhStatus = PDH_END_OF_LOG_FILE; } } // here the result will be success when the specified file has been read or // a PDH error if not } else { // the file has been memory mapped so use that interface if (pLog->dwLastRecordRead == 0) { // then we know no record has been read yet so assign // pointer just to be sure pLog->pLastRecordRead = pLog->lpMappedFileBase; pLog->dwLastRecordRead = 1; } pHeader = (PPDHI_BINARY_LOG_HEADER_RECORD)RECORD_AT(pLog, pLog->dwRecord1Size); assert (*(WORD *)&(pHeader->RecHeader.dwType) == BINLOG_START_WORD); // "seek" to the desired record if (dwRecordId < pLog->dwLastRecordRead) { if (dwRecordId >= BINLOG_FIRST_DATA_RECORD) { // rewind the file pLog->pLastRecordRead = (LPVOID)((LPBYTE)pLog->lpMappedFileBase + pHeader->Info.FirstRecordOffset); pLog->dwLastRecordRead = BINLOG_FIRST_DATA_RECORD; } else { // rewind the file pLog->pLastRecordRead = pLog->lpMappedFileBase; pLog->dwLastRecordRead = 1; } } // then use the point specified as the end of the file // if the log file contians a specified Wrap offset, then use that // if not, then if the file length is specified, use that // if not, the use the reported file length pEndOfFile = (LPVOID)((LPBYTE)pLog->lpMappedFileBase); if (pHeader->Info.WrapOffset > 0) { pEndOfFile = (LPVOID)((LPBYTE)pEndOfFile + pHeader->Info.WrapOffset); assert (pHeader->Info.FileLength >= pHeader->Info.WrapOffset); } else if (pHeader->Info.FileLength > 0) { pEndOfFile = (LPVOID)((LPBYTE)pEndOfFile + pHeader->Info.FileLength); assert (pHeader->Info.FileLength <= pLog->llFileSize); } else { pEndOfFile = (LPVOID)((LPBYTE)pEndOfFile + pLog->llFileSize); } pLastRecord = pLog->pLastRecordRead; dwLastRecordIndex = pLog->dwLastRecordRead; __try { // walk around the file until an access violation occurs or // the record is found. If an access violation occurs, // we can assume we went off the end of the file and out // of the mapped section // make sure the record has a valid header if (pLog->dwLastRecordRead != BINLOG_TYPE_ID_RECORD ? (*(WORD *)pLog->pLastRecordRead == BINLOG_START_WORD) : TRUE) { // then it looks OK so continue while (pLog->dwLastRecordRead != dwRecordId) { // go to next record pLastRecord = pLog->pLastRecordRead; if (pLog->dwLastRecordRead != BINLOG_TYPE_ID_RECORD) { if (pLog->dwLastRecordRead == BINLOG_HEADER_RECORD) { // if the last record was the header, then the next record // is the "first" data , not the first after the header pLog->pLastRecordRead = (LPVOID)((LPBYTE)pLog->lpMappedFileBase + pHeader->Info.FirstRecordOffset); } else { // if the current record is any record other than the header // ...then if (((PPDHI_BINARY_LOG_RECORD_HEADER)pLog->pLastRecordRead)->dwLength > 0) { // go to the next record in the file pLog->pLastRecordRead = (LPVOID)((LPBYTE)pLog->pLastRecordRead+ ((PPDHI_BINARY_LOG_RECORD_HEADER) pLog->pLastRecordRead)->dwLength); // test for exceptions here if (pLog->pLastRecordRead >= pEndOfFile) { // find out if this is a circular log or not if (pLog->dwLogFormat & PDH_LOG_OPT_CIRCULAR) { // test to see if the file has wrapped if (pHeader->Info.WrapOffset != 0) { // then wrap to the beginning of the file pLog->pLastRecordRead = (LPVOID)((LPBYTE)pLog->lpMappedFileBase + pHeader->Info.FirstDataRecordOffset); } else { // the file is still linear so this is the end pdhStatus = PDH_END_OF_LOG_FILE; } } else { // this is the end of the file // so reset to the previous pointer pdhStatus = PDH_END_OF_LOG_FILE; } } else { // not at the physical end of the file, but if this is a circular // log, it could be the logical end of the records so test that // here if (pLog->dwLogFormat & PDH_LOG_OPT_CIRCULAR) { pLastRecordInLog = (LPVOID)((LPBYTE)pLog->lpMappedFileBase + pHeader->Info.LastRecordOffset); pLastRecordInLog = (LPVOID)((LPBYTE)pLastRecordInLog + ((PPDHI_BINARY_LOG_RECORD_HEADER)pLastRecordInLog)->dwLength); if (pLog->pLastRecordRead == pLastRecordInLog) { // then this is the last record in the log pdhStatus = PDH_END_OF_LOG_FILE; } } else { // nothing to do since this is a normal case } } // end if / if not end of log file } else { // length is 0 so we've probably run off the end of the log somehow pdhStatus = PDH_END_OF_LOG_FILE; } } // end if /if not header record } else { pLog->pLastRecordRead = (LPBYTE)pLog->pLastRecordRead + pLog->dwRecord1Size; } if (pdhStatus == ERROR_SUCCESS) { // update pointers & indices pLog->dwLastRecordRead++; dwLastRecordIndex = pLog->dwLastRecordRead; } else { pLog->pLastRecordRead = pLastRecord; break; // out of the while loop } } } else { pdhStatus = PDH_END_OF_LOG_FILE; } } __except (EXCEPTION_EXECUTE_HANDLER) { pLog->pLastRecordRead = pLastRecord; pLog->dwLastRecordRead = dwLastRecordIndex; } } if (pdhStatus == ERROR_SUCCESS) { // see if we ended up at the right place if (pLog->dwLastRecordRead == dwRecordId) { if (pRecord != NULL) { // then try to copy it // if the record ID is 1 then it's the header record so this is // a special case record that is actually a CR/LF terminated record if (dwRecordId != BINLOG_TYPE_ID_RECORD) { if (((PPDHI_BINARY_LOG_RECORD_HEADER)pLog->pLastRecordRead)->dwLength <= dwMaxSize) { // then it'll fit so copy it memcpy (pRecord, pLog->pLastRecordRead, ((PPDHI_BINARY_LOG_RECORD_HEADER)pLog->pLastRecordRead)->dwLength); pdhStatus = ERROR_SUCCESS; } else { // then copy as much as will fit memcpy (pRecord, pLog->pLastRecordRead, dwMaxSize); pdhStatus = PDH_MORE_DATA; } } else { // copy the first record and zero terminate it if (pLog->dwRecord1Size <= dwMaxSize) { memcpy (pRecord, pLog->pLastRecordRead, pLog->dwRecord1Size); // null terminate after string ((LPBYTE)pRecord)[pLog->dwRecord1Size - PdhidwRecordTerminatorLength + 1] = 0; } else { memcpy (pRecord, pLog->pLastRecordRead, dwMaxSize); pdhStatus = PDH_MORE_DATA; } } } else { // just return success // no buffer was passed, but the record pointer has been // positioned pdhStatus = ERROR_SUCCESS; } } else { pdhStatus = PDH_END_OF_LOG_FILE; } } return pdhStatus; } STATIC_PDH_FUNCTION PdhiReadOneBinLogRecord ( IN PPDHI_LOG pLog, IN DWORD dwRecordId, IN LPVOID pRecord, IN DWORD dwMaxSize ) { PDH_STATUS pdhStatus= ERROR_SUCCESS; LPVOID pEndOfFile; LPVOID pLastRecord; DWORD dwLastRecordIndex = 0; PPDHI_BINARY_LOG_HEADER_RECORD pHeader = NULL; BOOL bCircular = FALSE; DWORD dwRecordSize; DWORD dwRecordReadSize; LONGLONG llLastFileOffset; LPVOID pTmpBuffer; assert (dwRecordId > 0); // record numbers start at 1 if ( (LOWORD(pLog->dwLogFormat) == PDH_LOG_TYPE_BINARY) && (dwRecordId == BINLOG_HEADER_RECORD)) { // special handling for WMI event trace logfile format // return PdhiReadWmiHeaderRecord(pLog, pRecord, dwMaxSize); } if (pLog->iRunidSQL != 0) { return PdhiReadBinaryMappedRecord(pLog, dwRecordId, pRecord, dwMaxSize); } if (pLog->dwLastRecordRead == 0) { // then we know no record has been read yet so assign // pointer just to be sure pLog->pLastRecordRead = NULL; pLog->liLastRecordOffset.QuadPart = 0; SetFilePointer (pLog->hLogFileHandle, pLog->liLastRecordOffset.LowPart, &pLog->liLastRecordOffset.HighPart, FILE_BEGIN); if (pLog->liLastRecordOffset.LowPart == INVALID_SET_FILE_POINTER) { pdhStatus = GetLastError(); } } if (pdhStatus == ERROR_SUCCESS) { // map header to local structure (the header data should be mapped into memory pHeader = (PPDHI_BINARY_LOG_HEADER_RECORD)RECORD_AT(pLog, pLog->dwRecord1Size); assert (*(WORD *)&(pHeader->RecHeader.dwType) == BINLOG_START_WORD); if (pHeader->Info.WrapOffset > 0) { bCircular = TRUE; } // "seek" to the desired record if ((dwRecordId < pLog->dwLastRecordRead) || (pLog->dwLastRecordRead == 0)) { // rewind if not initialized or the desired record is before this one if (dwRecordId >= BINLOG_FIRST_DATA_RECORD) { // rewind the file to the first regular record pLog->liLastRecordOffset.QuadPart = pHeader->Info.FirstRecordOffset; pLog->dwLastRecordRead = BINLOG_FIRST_DATA_RECORD; } else { // rewind the file to the very start of the file pLog->liLastRecordOffset.QuadPart = 0; pLog->dwLastRecordRead = 1; } pLog->liLastRecordOffset.LowPart = SetFilePointer (pLog->hLogFileHandle, pLog->liLastRecordOffset.LowPart, &pLog->liLastRecordOffset.HighPart, FILE_BEGIN); if (pLog->liLastRecordOffset.LowPart == INVALID_SET_FILE_POINTER) { pdhStatus = GetLastError(); } else { if (pLog->pLastRecordRead != NULL) { G_FREE (pLog->pLastRecordRead); pLog->pLastRecordRead = NULL; } if (pLog->dwLastRecordRead == 1) { // the this is the text ID field dwRecordSize = pLog->dwRecord1Size; } else { dwRecordSize = sizeof(PDHI_BINARY_LOG_RECORD_HEADER); } pLog->pLastRecordRead = G_ALLOC (dwRecordSize); if (pLog->pLastRecordRead != NULL) { // read in the header (or entire record if the 1st record // otherwise it's a data record if (ReadFile (pLog->hLogFileHandle, pLog->pLastRecordRead, dwRecordSize, &dwRecordReadSize, NULL)) { // then we have the record header or type record so // complete the operation and read the rest of the record if (pLog->dwLastRecordRead != BINLOG_TYPE_ID_RECORD) { // the Type ID record is of fixed length and has not header record dwRecordSize = ((PPDHI_BINARY_LOG_RECORD_HEADER)pLog->pLastRecordRead)->dwLength; pTmpBuffer = pLog->pLastRecordRead; pLog->pLastRecordRead = G_REALLOC(pTmpBuffer, dwRecordSize); if (pLog->pLastRecordRead != NULL) { // read in the rest of the record and append it to the header data already read in // otherwise it's a data record pLastRecord = (LPVOID)&((LPBYTE)pLog->pLastRecordRead)[sizeof(PDHI_BINARY_LOG_RECORD_HEADER)]; if (ReadFile (pLog->hLogFileHandle, pLastRecord, dwRecordSize - sizeof(PDHI_BINARY_LOG_RECORD_HEADER), &dwRecordReadSize, NULL)) { // then we have the record header or type record pdhStatus = ERROR_SUCCESS; } else { pdhStatus = GetLastError(); } } else { G_FREE(pTmpBuffer); pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; } } pdhStatus = ERROR_SUCCESS; } else { pdhStatus = GetLastError(); } } else { pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; } } } // then use the point specified as the end of the file // if the log file contians a specified Wrap offset, then use that // if not, then if the file length is specified, use that // if not, the use the reported file length pEndOfFile = (LPVOID)((LPBYTE)pLog->lpMappedFileBase); if (pHeader->Info.WrapOffset > 0) { pEndOfFile = (LPVOID)((LPBYTE)pEndOfFile + pHeader->Info.WrapOffset); assert (pHeader->Info.FileLength >= pHeader->Info.WrapOffset); } else if (pHeader->Info.FileLength > 0) { pEndOfFile = (LPVOID)((LPBYTE)pEndOfFile + pHeader->Info.FileLength); assert (pHeader->Info.FileLength <= pLog->llFileSize); } else { pEndOfFile = (LPVOID)((LPBYTE)pEndOfFile + pLog->llFileSize); } dwLastRecordIndex = pLog->dwLastRecordRead; } if (pdhStatus == ERROR_SUCCESS) { __try { // walk around the file until an access violation occurs or // the record is found. If an access violation occurs, // we can assume we went off the end of the file and out // of the mapped section // make sure the record has a valid header if (pLog->dwLastRecordRead != BINLOG_TYPE_ID_RECORD ? (*(WORD *)pLog->pLastRecordRead == BINLOG_START_WORD) : TRUE) { // then it looks OK so continue while (pLog->dwLastRecordRead != dwRecordId) { // go to next record if (pLog->dwLastRecordRead != BINLOG_TYPE_ID_RECORD) { llLastFileOffset = pLog->liLastRecordOffset.QuadPart; if (pLog->dwLastRecordRead == BINLOG_HEADER_RECORD) { // if the last record was the header, then the next record // is the "first" data , not the first after the header // the function returns the new offset pLog->liLastRecordOffset.QuadPart = pHeader->Info.FirstRecordOffset; pLog->liLastRecordOffset.LowPart = SetFilePointer (pLog->hLogFileHandle, pLog->liLastRecordOffset.LowPart, &pLog->liLastRecordOffset.HighPart, FILE_BEGIN); } else { // if the current record is any record other than the header // ...then if (((PPDHI_BINARY_LOG_RECORD_HEADER)pLog->pLastRecordRead)->dwLength > 0) { // go to the next record in the file pLog->liLastRecordOffset.QuadPart += ((PPDHI_BINARY_LOG_RECORD_HEADER) pLog->pLastRecordRead)->dwLength; // test for exceptions here if (pLog->liLastRecordOffset.QuadPart >= pLog->llFileSize) { // find out if this is a circular log or not if (pLog->dwLogFormat & PDH_LOG_OPT_CIRCULAR) { // test to see if the file has wrapped if (pHeader->Info.WrapOffset != 0) { // then wrap to the beginning of the file pLog->liLastRecordOffset.QuadPart = pHeader->Info.FirstDataRecordOffset; } else { // the file is still linear so this is the end pdhStatus = PDH_END_OF_LOG_FILE; } } else { // this is the end of the file // so reset to the previous pointer pdhStatus = PDH_END_OF_LOG_FILE; } } else { // not at the physical end of the file, but if this is a circular // log, it could be the logical end of the records so test that // here if (pLog->dwLogFormat & PDH_LOG_OPT_CIRCULAR) { if (llLastFileOffset == pHeader->Info.LastRecordOffset) { // then this is the last record in the log pdhStatus = PDH_END_OF_LOG_FILE; } } else { // nothing to do since this is a normal case } } // end if / if not end of log file } else { // length is 0 so we've probably run off the end of the log somehow pdhStatus = PDH_END_OF_LOG_FILE; } // now go to that record if (pdhStatus == ERROR_SUCCESS) { pLog->liLastRecordOffset.LowPart = SetFilePointer (pLog->hLogFileHandle, pLog->liLastRecordOffset.LowPart, &pLog->liLastRecordOffset.HighPart, FILE_BEGIN); } } // end if /if not header record } else { pLog->liLastRecordOffset.QuadPart = pLog->dwRecord1Size; pLog->liLastRecordOffset.LowPart = SetFilePointer (pLog->hLogFileHandle, pLog->liLastRecordOffset.LowPart, &pLog->liLastRecordOffset.HighPart, FILE_BEGIN); } if (pdhStatus == ERROR_SUCCESS) { // the last record buffer should not be NULL and it should // be large enough to hold the header if (pLog->pLastRecordRead != NULL) { // read in the header (or entire record if the 1st record // otherwise it's a data record dwRecordSize = sizeof(PDHI_BINARY_LOG_RECORD_HEADER); if (ReadFile (pLog->hLogFileHandle, pLog->pLastRecordRead, dwRecordSize, &dwRecordReadSize, NULL)) { // then we have the record header or type record // update pointers & indices pLog->dwLastRecordRead++; pdhStatus = ERROR_SUCCESS; } else { pdhStatus = GetLastError(); } } else { DebugBreak(); } } else { break; // out of the while loop } } } else { pdhStatus = PDH_END_OF_LOG_FILE; } } __except (EXCEPTION_EXECUTE_HANDLER) { pLog->dwLastRecordRead = dwLastRecordIndex; } } // see if we ended up at the right place if ((pdhStatus == ERROR_SUCCESS) && (pLog->dwLastRecordRead == dwRecordId)) { if (dwLastRecordIndex != pLog->dwLastRecordRead) { // then we've move the file pointer so read the entire data record dwRecordSize = ((PPDHI_BINARY_LOG_RECORD_HEADER)pLog->pLastRecordRead)->dwLength; pTmpBuffer = pLog->pLastRecordRead; pLog->pLastRecordRead = G_REALLOC(pTmpBuffer, dwRecordSize); if (pLog->pLastRecordRead != NULL) { // read in the rest of the record and append it to the header data already read in // otherwise it's a data record pLastRecord = (LPVOID)&((LPBYTE)pLog->pLastRecordRead)[sizeof(PDHI_BINARY_LOG_RECORD_HEADER)]; if (ReadFile (pLog->hLogFileHandle, pLastRecord, dwRecordSize - sizeof(PDHI_BINARY_LOG_RECORD_HEADER), &dwRecordReadSize, NULL)) { // then we have the record header or type record pdhStatus = ERROR_SUCCESS; } else { pdhStatus = GetLastError(); } } else { G_FREE(pTmpBuffer); pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; } } if ((pdhStatus == ERROR_SUCCESS) && (pRecord != NULL)) { // then try to copy it // if the record ID is 1 then it's the header record so this is // a special case record that is actually a CR/LF terminated record if (dwRecordId != BINLOG_TYPE_ID_RECORD) { if (((PPDHI_BINARY_LOG_RECORD_HEADER)pLog->pLastRecordRead)->dwLength <= dwMaxSize) { // then it'll fit so copy it RtlCopyMemory(pRecord, pLog->pLastRecordRead, ((PPDHI_BINARY_LOG_RECORD_HEADER)pLog->pLastRecordRead)->dwLength); pdhStatus = ERROR_SUCCESS; } else { // then copy as much as will fit RtlCopyMemory(pRecord, pLog->pLastRecordRead, dwMaxSize); pdhStatus = PDH_MORE_DATA; } } else { // copy the first record and zero terminate it if (pLog->dwRecord1Size <= dwMaxSize) { RtlCopyMemory(pRecord, pLog->pLastRecordRead, pLog->dwRecord1Size); // null terminate after string ((LPBYTE)pRecord)[pLog->dwRecord1Size - PdhidwRecordTerminatorLength + 1] = 0; } else { RtlCopyMemory(pRecord, pLog->pLastRecordRead, dwMaxSize); pdhStatus = PDH_MORE_DATA; } } } else { // just return current status value // no buffer was passed, but the record pointer has been // positioned } } else { // if successful so far, then return EOF if (pdhStatus == ERROR_SUCCESS) pdhStatus = PDH_END_OF_LOG_FILE; } return pdhStatus; } PDH_FUNCTION PdhiUpdateBinaryLogFileCatalog ( IN PPDHI_LOG pLog ) { LPVOID pTempBuffer = NULL; LPVOID pOldBuffer; DWORD dwTempBufferSize; BOOL bWildCardObjects = FALSE; PDH_STATUS pdhStatus = ERROR_SUCCESS; // the file must be mapped for this to work if (pLog->hMappedLogFile == NULL) return PDH_LOG_FILE_OPEN_ERROR; // read the header record and enum the machine name from the entries if (pLog->dwMaxRecordSize == 0) { // no size is defined so start with 64K pLog->dwMaxRecordSize = 0x010000; } dwTempBufferSize = pLog->dwMaxRecordSize; pTempBuffer = G_ALLOC (dwTempBufferSize); if (pTempBuffer == NULL) { return PDH_MEMORY_ALLOCATION_FAILURE; } // read in the catalog record at the beginning of the file while ((pdhStatus = PdhiReadOneBinLogRecord (pLog, BINLOG_HEADER_RECORD, pTempBuffer, dwTempBufferSize)) != ERROR_SUCCESS) { if (pdhStatus == PDH_MORE_DATA) { // read the 1st WORD to see if this is a valid record if (*(WORD *)pTempBuffer == BINLOG_START_WORD) { // it's a valid record so read the 2nd DWORD to get the // record size; dwTempBufferSize = ((DWORD *)pTempBuffer)[1]; if (dwTempBufferSize < pLog->dwMaxRecordSize) { // then something is bogus so return an error pdhStatus = PDH_ENTRY_NOT_IN_LOG_FILE; break; // out of while loop } else { pLog->dwMaxRecordSize = dwTempBufferSize; } } else { // we're lost in this file pdhStatus = PDH_ENTRY_NOT_IN_LOG_FILE; break; // out of while loop } // realloc a new buffer pOldBuffer = pTempBuffer; pTempBuffer = G_REALLOC (pOldBuffer, dwTempBufferSize); if (pTempBuffer == NULL) { // return memory error G_FREE(pOldBuffer); assert (GetLastError() == ERROR_SUCCESS); pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; break; } } else { // some other error was returned so // return error from read function break; } } // if that worked, then examine the catalog record and prepare to scan the file if (pdhStatus == ERROR_SUCCESS) { PPDHI_BINARY_LOG_HEADER_RECORD pHeader; PPDHI_LOG_COUNTER_PATH pPath; DWORD dwBytesProcessed; LONG nItemCount = 0; LPBYTE pFirstChar; LPWSTR szThisMachineName; LPWSTR szThisObjectName; LPWSTR szThisInstanceName; WCHAR szThisMachineObjectName[MAX_PATH]; LPWSTR szThisEntriesName; DWORD dwRecordLength; DWORD dwNewBuffer = 0; PLOG_BIN_CAT_ENTRY *ppCatEntryArray = NULL; PLOG_BIN_CAT_ENTRY pThisCatEntry; DWORD dwCatEntryArrayUsed = 0; DWORD dwCatEntryArrayAllocated = 0; DWORD dwThisCatEntry; DWORD *pLogIndexArray = NULL; DWORD dwLogIndexArrayUsed = 0; DWORD dwLogIndexArrayAllocated = 0; DWORD dwThisIndex; DWORD dwCurrentEntryOffset; // we can assume the record was read successfully so read in the // objects that match the machine name and detail level criteria dwRecordLength = ((PPDHI_BINARY_LOG_RECORD_HEADER)pTempBuffer)->dwLength; pPath = (PPDHI_LOG_COUNTER_PATH) ((LPBYTE)pTempBuffer + sizeof (PDHI_BINARY_LOG_HEADER_RECORD)); dwBytesProcessed = sizeof(PDHI_BINARY_LOG_HEADER_RECORD); while (dwBytesProcessed < dwRecordLength) { szThisObjectName = NULL; memset (szThisMachineObjectName, 0, sizeof (szThisMachineObjectName)); pFirstChar = (LPBYTE)&pPath->Buffer[0]; if (pPath->lMachineNameOffset >= 0L) { // then there's a machine name in this record so get // it's size szThisMachineName = (LPWSTR)((LPBYTE)pFirstChar + pPath->lMachineNameOffset); lstrcatW (szThisMachineObjectName, szThisMachineName); } else { // no machine name so just add the object name } if (szThisObjectName >= 0) { szThisObjectName = (LPWSTR)((LPBYTE)pFirstChar + pPath->lObjectNameOffset); lstrcatW (szThisMachineObjectName, cszBackSlash); lstrcatW (szThisMachineObjectName, szThisObjectName); } else { // no object to copy // so clear the string and skip to the next item memset (szThisMachineObjectName, 0, sizeof (szThisMachineObjectName)); } if (*szThisMachineObjectName != 0) { // search the list of machine/object entries and add this if // it's not new. if (dwCatEntryArrayUsed > 0) { // then there are entries in the array to examine for (dwThisCatEntry = 0; dwThisCatEntry < dwCatEntryArrayUsed; dwThisCatEntry++) { szThisEntriesName = (LPWSTR)((LPBYTE)(&ppCatEntryArray[dwThisCatEntry]->bcRec.CatEntry) + ppCatEntryArray[dwThisCatEntry]->bcRec.CatEntry.dwMachineObjNameOffset); if (lstrcmpiW (szThisEntriesName, szThisMachineObjectName) == 0) { // match found so no need to add it break; } } } else { dwThisCatEntry = 0; } if (dwThisCatEntry == dwCatEntryArrayUsed) { dwCatEntryArrayUsed++; // this machine/object was not found so allocate a new one if (dwCatEntryArrayUsed > dwCatEntryArrayAllocated) { // extend the array dwCatEntryArrayAllocated += 256; // for starters if (ppCatEntryArray == NULL) { // then initialize a new one ppCatEntryArray = G_ALLOC ( dwCatEntryArrayAllocated * sizeof(PLOG_BIN_CAT_RECORD)); } else { // extend it pOldBuffer = ppCatEntryArray; ppCatEntryArray = G_REALLOC(pOldBuffer, dwCatEntryArrayAllocated * sizeof(PLOG_BIN_CAT_RECORD)); } if (ppCatEntryArray == NULL) { G_FREE(pOldBuffer); pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; } } if (pdhStatus == ERROR_SUCCESS) { assert (dwThisCatEntry == (dwCatEntryArrayUsed - 1)); // initialize the entry // allocate the record buffer ppCatEntryArray[dwCatEntryArrayUsed-1] = G_ALLOC(LARGE_BUFFER_SIZE); if (ppCatEntryArray[dwCatEntryArrayUsed-1] == NULL) { assert (GetLastError() == ERROR_SUCCESS); pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; break; // break out of loop since we can't allocate any memory } else { pThisCatEntry = ppCatEntryArray[dwCatEntryArrayUsed-1]; pThisCatEntry->dwEntrySize = (DWORD)G_SIZE(pThisCatEntry); // initialize the fields of the new structure // start with the record header pThisCatEntry->bcRec.RecHeader.dwType = BINLOG_TYPE_CATALOG_ITEM; // this field will be filled in when the list is completed pThisCatEntry->bcRec.RecHeader.dwLength =0; // now initialize the catalog entry record // offsets are from the start address of the catalog entry record pThisCatEntry->bcRec.CatEntry.dwMachineObjNameOffset = sizeof (PDHI_LOG_CAT_ENTRY); assert ((LONG)pThisCatEntry->bcRec.CatEntry.dwMachineObjNameOffset == (LONG)((LPBYTE)&pThisCatEntry->bcRec.dwEntryRecBuff[0] - (LPBYTE)&pThisCatEntry->bcRec.CatEntry)); // now copy the machine/object string to the buffer lstrcpyW ((LPWSTR)((LPBYTE)&pThisCatEntry->bcRec.CatEntry + pThisCatEntry->bcRec.CatEntry.dwMachineObjNameOffset), szThisMachineObjectName); // the instance string list will follow the machine name pThisCatEntry->bcRec.CatEntry.dwInstanceStringOffset = pThisCatEntry->bcRec.CatEntry.dwMachineObjNameOffset + ((lstrlenW (szThisMachineObjectName) + 1) * sizeof(WCHAR)); // finish off by initializing the offsets // this offset is from the start of the Cat Entry NOT the // cat data record. pThisCatEntry->dwOffsetToNextInstance = // offset to cat entry structure (DWORD)((LPBYTE)(&pThisCatEntry->bcRec.CatEntry) - (LPBYTE)(pThisCatEntry)); // offset from there to the instance string list pThisCatEntry->dwOffsetToNextInstance += pThisCatEntry->bcRec.CatEntry.dwInstanceStringOffset; assert (pThisCatEntry->dwOffsetToNextInstance < pThisCatEntry->dwEntrySize); } } else { // error encountered so break from loop break; } } else { // already in the list so go continue } // pThisCatEntry = pointer to the matching structure so // add it to the index of counter path items if it has // a wild card instance dwLogIndexArrayUsed++; if (dwLogIndexArrayUsed > dwLogIndexArrayAllocated) { // extend the array dwLogIndexArrayAllocated += 256; // for starters if (pLogIndexArray == NULL) { // then initialize a new one pLogIndexArray = G_ALLOC ( dwLogIndexArrayAllocated * sizeof(DWORD)); } else { // extend it pOldBuffer = pLogIndexArray; pLogIndexArray = G_REALLOC (pOldBuffer, dwLogIndexArrayAllocated * sizeof(DWORD)); if (pLogIndexArray == NULL) { G_FREE(pOldBuffer); } } if (pLogIndexArray == NULL) { assert (GetLastError() == ERROR_SUCCESS); pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; break; // break out of loop since we can't allocate any memory } } if (pPath->lInstanceOffset >= 0) { szThisInstanceName = (LPWSTR)((LPBYTE)pFirstChar + pPath->lInstanceOffset); if (*szThisInstanceName == SPLAT_L) { // then this is a wild card instance so save it pLogIndexArray[dwLogIndexArrayUsed-1] = dwThisCatEntry; bWildCardObjects = TRUE; // there's at least 1 item to scan from the file } else { // this is not a wildcard instance so no instance list // entry is necessary pLogIndexArray[dwLogIndexArrayUsed-1] = (DWORD)-1; } } else { // this object doesn't have instances szThisInstanceName = NULL; pLogIndexArray[dwLogIndexArrayUsed-1] = (DWORD)-1; } } else { // no machine or object name to look up } // get next path entry from log file record dwBytesProcessed += pPath->dwLength; pPath = (PPDHI_LOG_COUNTER_PATH) ((LPBYTE)pPath + pPath->dwLength); } // If everything is OK so far, fill in the list(s) of instances if ((pdhStatus == ERROR_SUCCESS) && (ppCatEntryArray != NULL) && (bWildCardObjects)) { PPDHI_BINARY_LOG_RECORD_HEADER pThisMasterRecord; PPDHI_BINARY_LOG_RECORD_HEADER pThisSubRecord; PPDHI_RAW_COUNTER_ITEM_BLOCK pDataBlock; PPDHI_RAW_COUNTER_ITEM pDataItem; DWORD dwThisRecordIndex; DWORD dwDataItemIndex; DWORD dwSize; DWORD dwLowSize, dwHighSize; LONGLONG llEndOfFileOffset; DWORD dwLowPos, dwHighPos; DWORD dwBytesWritten; // run through the log file and add the instances to the appropriate list // look up individual instances in log... // read records from file and store instances dwThisRecordIndex = BINLOG_FIRST_DATA_RECORD; // this call just moves the record pointer pdhStatus = PdhiReadOneBinLogRecord ( pLog, dwThisRecordIndex, NULL, 0); while (pdhStatus == ERROR_SUCCESS) { pThisMasterRecord = (PPDHI_BINARY_LOG_RECORD_HEADER) pLog->pLastRecordRead; // make sure we haven't left the file assert (pThisMasterRecord != NULL); assert ((LPBYTE)pThisMasterRecord > (LPBYTE)pLog->lpMappedFileBase); assert ((LPBYTE)pThisMasterRecord < ((LPBYTE)pLog->lpMappedFileBase + pLog->llFileSize)); // examine each entry in the record // sub records start with index 1 for (dwThisIndex = 1; dwThisIndex <= dwLogIndexArrayUsed; dwThisIndex++) { pThisSubRecord = PdhiGetSubRecord ( pThisMasterRecord, dwThisIndex); assert (pThisSubRecord != NULL); // this would imply a bad log file // only do multi entries if ((pThisSubRecord != NULL) && (pThisSubRecord->dwType == BINLOG_TYPE_DATA_MULTI)) { // if this is a multi-counter, then this should have an entry assert (pLogIndexArray[dwThisIndex] != (DWORD)-1); // the array index is 0 based while the records are 1 based // so adjust index value here pThisCatEntry = ppCatEntryArray[pLogIndexArray[dwThisIndex-1]]; assert (pThisCatEntry != NULL); // make sure this is a valid entry pDataBlock = (PPDHI_RAW_COUNTER_ITEM_BLOCK) ((LPBYTE)pThisSubRecord + sizeof (PDHI_BINARY_LOG_RECORD_HEADER)); // walk down list of entries and add them to the // list of instances (these should already // be assembled in parent/instance format) for (dwDataItemIndex = 0; dwDataItemIndex < pDataBlock->dwItemCount; dwDataItemIndex++) { pDataItem = &pDataBlock->pItemArray[dwDataItemIndex]; szThisInstanceName = (LPWSTR) (((LPBYTE) pDataBlock) + pDataItem->szName); dwNewBuffer = lstrlenW (szThisInstanceName) + 1; dwNewBuffer *= sizeof (WCHAR); if ((pThisCatEntry->dwOffsetToNextInstance + dwNewBuffer) >= pThisCatEntry->dwEntrySize) { // grow the buffer dwSize = pThisCatEntry->dwEntrySize + LARGE_BUFFER_SIZE; pOldBuffer = pThisCatEntry; pThisCatEntry = G_REALLOC (pOldBuffer, dwSize); if (pThisCatEntry != NULL) { // update array ppCatEntryArray[pLogIndexArray[dwThisIndex-1]] = pThisCatEntry; pThisCatEntry->dwEntrySize = dwSize; } else { // skip & try the next one G_FREE(pOldBuffer); continue; } } // copy string to the buffer dwNewBuffer = AddUniqueWideStringToMultiSz ( (LPVOID)((LPBYTE)&pThisCatEntry->bcRec.CatEntry + pThisCatEntry->bcRec.CatEntry.dwInstanceStringOffset), szThisInstanceName, TRUE); // if the string was added to the list, then // dwNewBuffer is the size of the resulting MSZ // in characters not including the double NULL // terminating the MSZ // if no string was added, then it is 0 if (dwNewBuffer > 0) { // string was added so update size used. // this is the new size of the MSZ instance list pThisCatEntry->dwOffsetToNextInstance = dwNewBuffer * sizeof (WCHAR); // + the offset of the istance list from the start of the Cat entry pThisCatEntry->dwOffsetToNextInstance += pThisCatEntry->bcRec.CatEntry.dwInstanceStringOffset; // + the start of the cat entry from the main structure start pThisCatEntry->dwOffsetToNextInstance += (DWORD)((DWORD_PTR)&pThisCatEntry->bcRec.CatEntry - (DWORD_PTR)pThisCatEntry); nItemCount++; } else { // nothing added so nothing to do } } // end for each istance entry in the array counter } else { // this is not an array counter } } // end for each item in this record // go to next record in log file pdhStatus = PdhiReadOneBinLogRecord ( pLog, ++dwThisRecordIndex, NULL, 0); } // end while not end of file if (pdhStatus == PDH_END_OF_LOG_FILE) { // this is good so fix the status pdhStatus = ERROR_SUCCESS; } // update the length fields of the various records dwCurrentEntryOffset = 0; for (dwThisIndex = 0; dwThisIndex < dwCatEntryArrayUsed; dwThisIndex++) { pThisCatEntry = ppCatEntryArray[dwThisIndex]; // update the record size of the overall record pThisCatEntry->bcRec.RecHeader.dwLength = (pThisCatEntry->dwOffsetToNextInstance + sizeof(WCHAR)) // to include MSZ term null // now subtract offset to record struct in // containing structure - (DWORD)((DWORD_PTR)(&pThisCatEntry->bcRec) - (DWORD_PTR)(pThisCatEntry)); // update the size of this catalog entry pThisCatEntry->bcRec.CatEntry.dwEntrySize = (pThisCatEntry->dwOffsetToNextInstance + sizeof(WCHAR)) // to include MSZ term null // now subtract offset to record struct in // containing structure - (DWORD)((DWORD_PTR)(&pThisCatEntry->bcRec.CatEntry) - (DWORD_PTR)(pThisCatEntry)); // update the size of the MSZ instance list string pThisCatEntry->bcRec.CatEntry.dwStringSize = // size of the entry... pThisCatEntry->bcRec.CatEntry.dwEntrySize // - the offset to the start of the string - pThisCatEntry->bcRec.CatEntry.dwInstanceStringOffset; // only entries with strings will be written to the // file so only they will have offsets if (pThisCatEntry->bcRec.CatEntry.dwStringSize > sizeof(DWORD)) { pThisCatEntry->dwEntryOffset = dwCurrentEntryOffset; dwCurrentEntryOffset += pThisCatEntry->bcRec.RecHeader.dwLength; } else { pThisCatEntry->dwEntryOffset = 0; } #if _DBG swprintf (szThisMachineObjectName, (LPCWSTR)L"\nEntry %d: Offset: %d, Rec Len: %d String Len: %d", dwThisIndex, pThisCatEntry->dwEntryOffset, pThisCatEntry->bcRec.RecHeader.dwLength, pThisCatEntry->bcRec.CatEntry.dwStringSize ); OutputDebugStringW (szThisMachineObjectName); #endif } #if _DBG swprintf (szThisMachineObjectName, (LPCWSTR)L"\nCatalog Size: %d", dwCurrentEntryOffset); OutputDebugStringW (szThisMachineObjectName); #endif // see if the end of file is defined as something other // then the physical end of the file (e.g. the last record // in a circular log file pHeader = (PPDHI_BINARY_LOG_HEADER_RECORD) ((LPBYTE)(pLog->lpMappedFileBase) + pLog->dwRecord1Size); assert (*(WORD *)&(pHeader->RecHeader.dwType) == BINLOG_START_WORD); // use the greater of Wrap offset or Next Offset llEndOfFileOffset = pHeader->Info.WrapOffset; if (pHeader->Info.NextRecordOffset > llEndOfFileOffset) { llEndOfFileOffset = pHeader->Info.NextRecordOffset; } // if neither is defined, then use the physical end of file if (llEndOfFileOffset == 0) { dwLowSize = GetFileSize (pLog->hLogFileHandle, &dwHighSize); assert (dwLowSize != 0xFFFFFFFF); } else { dwLowSize = LODWORD(llEndOfFileOffset); dwHighSize = HIDWORD(llEndOfFileOffset); } // now get ready to update the log file. // 1st unmap the view of the file so we can update it if (!UnmapViewOfFile(pLog->lpMappedFileBase)) { pdhStatus = GetLastError(); } else { pLog->lpMappedFileBase = NULL; pLog->pLastRecordRead = NULL; CloseHandle (pLog->hMappedLogFile); pLog->hMappedLogFile = NULL; } assert (pdhStatus == ERROR_SUCCESS); // lock the file while we fiddle with it if (!LockFile (pLog->hLogFileHandle,0,0,dwLowSize, dwHighSize)) { pdhStatus = GetLastError (); } assert (pdhStatus == ERROR_SUCCESS); // 3rd move to the end of the file dwLowPos = dwLowSize; dwHighPos = dwHighSize; dwLowPos = SetFilePointer (pLog->hLogFileHandle, dwLowPos, (LONG *)&dwHighPos, FILE_BEGIN); if (dwLowPos == 0xFFFFFFFF) { pdhStatus = GetLastError (); } assert (pdhStatus == ERROR_SUCCESS); assert (dwLowPos == dwLowSize); assert (dwHighPos == dwHighSize); // 4th write the new catalog records for (dwThisIndex = 0; dwThisIndex < dwCatEntryArrayUsed; dwThisIndex++) { pThisCatEntry = ppCatEntryArray[dwThisIndex]; if (pThisCatEntry->bcRec.CatEntry.dwStringSize > sizeof (DWORD)) { // then this entry has something to write pdhStatus = PdhiWriteOneBinaryLogRecord ( pLog, (LPCVOID)&pThisCatEntry->bcRec, pThisCatEntry->bcRec.RecHeader.dwLength, &dwBytesWritten, 0); if (pdhStatus == ERROR_SUCCESS) { // operation succeeded assert (dwBytesWritten == pThisCatEntry->bcRec.RecHeader.dwLength); } } else { // this record does not need to be written } } // end for each machine/object in this log // truncate the file here since this must be at the // end if (!SetEndOfFile (pLog->hLogFileHandle)) { pdhStatus = GetLastError(); } // 5th re-map the file to include the new catalog sections pdhStatus = PdhiOpenUpdateBinaryLog (pLog); assert (pdhStatus == ERROR_SUCCESS); // 6th update the catalog entries in the header pdhStatus = PdhiReadOneBinLogRecord (pLog, BINLOG_HEADER_RECORD, pTempBuffer, dwTempBufferSize); assert (pdhStatus == ERROR_SUCCESS); // we can assume the record was read successfully so read in the // objects that match the machine name and detail level criteria dwRecordLength = ((PPDHI_BINARY_LOG_RECORD_HEADER)pTempBuffer)->dwLength; // the following will update the temp buffer, when // everything is finished, we'll copy this back to the // mapped file. // // enter the location of the Catalog ((PPDHI_BINARY_LOG_HEADER_RECORD)pTempBuffer)->Info.CatalogOffset = (LONGLONG)(dwLowPos + (dwHighPos << 32)); // update the catalog time GetSystemTimeAsFileTime ( (FILETIME *)&((PPDHI_BINARY_LOG_HEADER_RECORD)pTempBuffer)->Info.CatalogDate); // update the log file update time ((PPDHI_BINARY_LOG_HEADER_RECORD)pTempBuffer)->Info.LastUpdateTime = ((PPDHI_BINARY_LOG_HEADER_RECORD)pTempBuffer)->Info.CatalogDate; // go through each counter path item and insert the catalog offset // as appropriate (i.e. only to wild card path entries pPath = (PPDHI_LOG_COUNTER_PATH) ((LPBYTE)pTempBuffer + sizeof (PDHI_BINARY_LOG_HEADER_RECORD)); dwBytesProcessed = sizeof(PDHI_BINARY_LOG_HEADER_RECORD); dwThisIndex = 0; // this counter entry index while (dwBytesProcessed < dwRecordLength) { // get next path entry from log file record if (pLogIndexArray[dwThisIndex] != (DWORD)-1) { // make sure we're not going to step on anything assert (pPath->lMachineNameOffset > 0); // then this item has an extended catalog entry // so load the offset into the catalog here *((LPDWORD)&pPath->Buffer[0]) = ppCatEntryArray[pLogIndexArray[dwThisIndex]]->dwEntryOffset; } else { // skip this and go to the next one } dwThisIndex++; dwBytesProcessed += pPath->dwLength; pPath = (PPDHI_LOG_COUNTER_PATH) ((LPBYTE)pPath + pPath->dwLength); } assert (dwThisIndex == dwLogIndexArrayUsed); // write the changes to the file RtlCopyMemory(pLog->pLastRecordRead, pTempBuffer, dwRecordLength); // write changes to disk if (!FlushViewOfFile (pLog->lpMappedFileBase, 0)) { pdhStatus = GetLastError(); } assert (pdhStatus == ERROR_SUCCESS); // unlock the file if (!UnlockFile (pLog->hLogFileHandle, 0, 0, dwLowSize, dwHighSize)) { pdhStatus = GetLastError(); } // done // check the index entries... for (dwThisIndex = 0; dwThisIndex < dwCatEntryArrayUsed; dwThisIndex++) { pThisCatEntry = ppCatEntryArray[dwThisIndex]; // free the cat entry G_FREE (pThisCatEntry); ppCatEntryArray[dwThisIndex] = NULL; } } else { // then there's nothing to list } if (pLogIndexArray != NULL) G_FREE(pLogIndexArray); if (ppCatEntryArray != NULL) G_FREE(ppCatEntryArray); } if (pTempBuffer != NULL) G_FREE (pTempBuffer); return pdhStatus; } PDH_FUNCTION PdhiGetBinaryLogCounterInfo ( IN PPDHI_LOG pLog, IN PPDHI_COUNTER pCounter ) { PDH_STATUS pdhStatus; DWORD dwIndex; DWORD dwPrevious = pCounter->dwIndex; PPDHI_COUNTER_PATH pTempPath = NULL; PPDHI_BINARY_LOG_RECORD_HEADER pThisMasterRecord; PPDHI_LOG_COUNTER_PATH pPath; DWORD dwBufferSize; DWORD dwRecordLength; DWORD dwBytesProcessed; LPBYTE pFirstChar; LPWSTR szThisMachineName; LPWSTR szThisObjectName; LPWSTR szThisCounterName; LPWSTR szThisInstanceName; LPWSTR szThisParentName; BOOL bCheckThisObject = FALSE; DWORD dwTmpIndex; // crack the path in to components pTempPath = G_ALLOC (LARGE_BUFFER_SIZE); if (pTempPath == NULL) { return PDH_MEMORY_ALLOCATION_FAILURE; } dwBufferSize = (DWORD)G_SIZE(pTempPath); if (ParseFullPathNameW (pCounter->szFullName, &dwBufferSize, pTempPath, FALSE)) { // read the header record to find the matching entry pdhStatus = PdhiReadOneBinLogRecord ( pLog, BINLOG_HEADER_RECORD, NULL, 0); if (pdhStatus == ERROR_SUCCESS) { pThisMasterRecord = (PPDHI_BINARY_LOG_RECORD_HEADER) pLog->pLastRecordRead; dwRecordLength = ((PPDHI_BINARY_LOG_RECORD_HEADER)pThisMasterRecord)->dwLength; pPath = (PPDHI_LOG_COUNTER_PATH) ((LPBYTE)pThisMasterRecord + sizeof (PDHI_BINARY_LOG_HEADER_RECORD)); dwBytesProcessed = sizeof(PDHI_BINARY_LOG_HEADER_RECORD); dwIndex = 0; pdhStatus = PDH_ENTRY_NOT_IN_LOG_FILE; dwTmpIndex = 0; while (dwBytesProcessed < dwRecordLength) { // go through catalog to find a match dwIndex++; pFirstChar = (LPBYTE)&pPath->Buffer[0]; if (dwPrevious != 0 && dwPrevious >= dwIndex) { bCheckThisObject = FALSE; } else if (pPath->lMachineNameOffset >= 0L) { // then there's a machine name in this record so get // it's size szThisMachineName = (LPWSTR)((LPBYTE)pFirstChar + pPath->lMachineNameOffset); // if this is for the desired machine, then select the object if (lstrcmpiW(szThisMachineName, pTempPath->szMachineName) == 0) { szThisObjectName = (LPWSTR)((LPBYTE)pFirstChar + pPath->lObjectNameOffset); if (lstrcmpiW(szThisObjectName, pTempPath->szObjectName) == 0) { // then this is the object to look up bCheckThisObject = TRUE; } else { // not this object szThisObjectName = NULL; } } else { // this machine isn't selected } } else { // there's no machine specified so for this counter so list it by default if (pPath->lObjectNameOffset >= 0) { szThisObjectName = (LPWSTR)((LPBYTE)pFirstChar + pPath->lObjectNameOffset); if (lstrcmpiW(szThisObjectName,pTempPath->szObjectName) == 0) { // then this is the object to look up bCheckThisObject = TRUE; } else { // not this object szThisObjectName = NULL; } } else { // no object to copy szThisObjectName = NULL; } } if (bCheckThisObject) { szThisCounterName = (LPWSTR)((LPBYTE)pFirstChar + pPath->lCounterOffset); if (* szThisCounterName == SPLAT_L) { if (pPath->dwFlags & PDHIC_COUNTER_OBJECT) { pdhStatus = PdhiGetWmiLogCounterInfo(pLog, pCounter); pCounter->dwIndex = dwIndex; break; } else { // pPath->dwFlags & PDHIC_COUNTER_BLOCK // this is logged counter object. Since all couter // objects from the same machine are from the same // datablock, dwIndex might be incorrect // DWORD dwTemp = sizeof(PDHI_BINARY_LOG_HEADER_RECORD); PPDHI_LOG_COUNTER_PATH pLPath = (PPDHI_LOG_COUNTER_PATH) ((LPBYTE) pThisMasterRecord + dwTemp); DWORD dwLIndex = 0; LPBYTE pLChar; LPWSTR szMachine; pdhStatus = PdhiGetWmiLogCounterInfo(pLog, pCounter); while (dwTemp < dwRecordLength) { dwLIndex ++; if (dwPrevious == 0 || dwPrevious < dwLIndex) { pLChar = (LPBYTE)&pLPath->Buffer[0]; if (pLPath->lMachineNameOffset >= 0L) { szMachine = (LPWSTR) ((LPBYTE)pLChar + pLPath->lMachineNameOffset); if (lstrcmpiW(szMachine, pTempPath->szMachineName) == 0) { if (pLPath->dwFlags & PDHIC_COUNTER_BLOCK) { break; } } } else if (pLPath->dwFlags & PDHIC_COUNTER_BLOCK) { break; } } dwTemp += pLPath->dwLength; pLPath = (PPDHI_LOG_COUNTER_PATH) ((LPBYTE) pThisMasterRecord + dwTemp); } if (dwTemp >= dwRecordLength) { pdhStatus = PDH_ENTRY_NOT_IN_LOG_FILE; } else { pCounter->dwIndex = dwLIndex; } } } else if (lstrcmpiW(szThisCounterName, pTempPath->szCounterName) == 0) { // check instance name // get the instance name from this counter and add it to the list if (pPath->lInstanceOffset >= 0) { szThisInstanceName = (LPWSTR)((LPBYTE)pFirstChar + pPath->lInstanceOffset); if (*szThisInstanceName != SPLAT_L) { if (pPath->lParentOffset >= 0) { szThisParentName = (LPWSTR)((LPBYTE)pFirstChar + pPath->lParentOffset); if (lstrcmpiW(szThisParentName, pTempPath->szParentName) != 0) { // wrong parent bCheckThisObject = FALSE; } } if (lstrcmpiW(szThisInstanceName, pTempPath->szInstanceName) != 0) { // wrong instance bCheckThisObject = FALSE; } if (pTempPath->dwIndex > 0) { if (pPath->dwIndex == pTempPath->dwIndex) { bCheckThisObject = TRUE; } else if (pPath->dwIndex == 0) { if (dwTmpIndex == pTempPath->dwIndex) { bCheckThisObject = TRUE; } else { dwTmpIndex ++; bCheckThisObject = FALSE; } } else { // wrong index bCheckThisObject = FALSE; } } else if ( pPath->dwIndex != 0 && LOWORD(pLog->dwLogFormat) == PDH_LOG_TYPE_BINARY) { bCheckThisObject = FALSE; } } else { // this is a wild card spec // so assume it's valid since that's // faster than reading the file each time. // if the instance DOESN't exist in this // file then the appropriate status will // be returned in each query. } } else { // there is no instance name to compare // so assume it's OK } if (bCheckThisObject) { // fill in the data and return // this data is NOT used by the log file reader pCounter->plCounterInfo.dwObjectId = 0; pCounter->plCounterInfo.lInstanceId = 0; if (pPath->lInstanceOffset >= 0) { pCounter->plCounterInfo.szInstanceName = pCounter->pCounterPath->szInstanceName; pCounter->plCounterInfo.dwParentObjectId = 0; pCounter->plCounterInfo.szParentInstanceName = pCounter->pCounterPath->szParentName; } else { pCounter->plCounterInfo.szInstanceName = NULL; pCounter->plCounterInfo.dwParentObjectId = 0; pCounter->plCounterInfo.szParentInstanceName = NULL; } //define as multi instance if necessary // if the user is passing in a "*" character if (pCounter->plCounterInfo.szInstanceName != NULL) { if (*pCounter->plCounterInfo.szInstanceName == SPLAT_L) { pCounter->dwFlags |= PDHIC_MULTI_INSTANCE; } } // this data is used by the log file readers pCounter->plCounterInfo.dwCounterId = dwIndex; // entry in log pCounter->plCounterInfo.dwCounterType = pPath->dwCounterType; pCounter->plCounterInfo.dwCounterSize = pPath->dwCounterType & PERF_SIZE_LARGE ? sizeof (LONGLONG) : sizeof(DWORD); pCounter->plCounterInfo.lDefaultScale = pPath->lDefaultScale; pCounter->TimeBase = pPath->llTimeBase; pCounter->dwIndex = dwIndex; pdhStatus = ERROR_SUCCESS; break; } } } else { // we aren't interested in this so just ignore it } // get next path entry from log file record dwBytesProcessed += pPath->dwLength; pPath = (PPDHI_LOG_COUNTER_PATH) ((LPBYTE)pPath + pPath->dwLength); } // end while searching the catalog entries } else { // unable to find desired record so return status } } else { // unable to read the path pdhStatus = PDH_INVALID_PATH; } if (pTempPath != NULL) G_FREE (pTempPath); return pdhStatus; } PDH_FUNCTION PdhiOpenInputBinaryLog ( IN PPDHI_LOG pLog ) { PDH_STATUS pdhStatus; PPDHI_BINARY_LOG_HEADER_RECORD pHeader; pLog->StreamFile = (FILE *)((DWORD_PTR)(-1)); // map file header as a memory array for reading assert (pLog->hMappedLogFile != NULL); // should be open! if ((pLog->hMappedLogFile != NULL) && (pLog->lpMappedFileBase != NULL)) { // save size of binary log record header pLog->dwRecord1Size = dwFileHeaderLength + // ID characters 2 + // quotations PdhidwRecordTerminatorLength; // CR/LF terminator pLog->dwRecord1Size = QWORD_MULTIPLE(pLog->dwRecord1Size); // read the header and get the option flags pHeader = (PPDHI_BINARY_LOG_HEADER_RECORD) ((LPBYTE)(pLog->lpMappedFileBase) + pLog->dwRecord1Size); assert (*(WORD *)&(pHeader->RecHeader.dwType) == BINLOG_START_WORD); pLog->dwLogFormat |= pHeader->Info.dwFlags; pdhStatus = ERROR_SUCCESS; } else { // return PDH Error pdhStatus = PDH_LOG_FILE_OPEN_ERROR; } pdhStatus = ERROR_SUCCESS; return pdhStatus; } PDH_FUNCTION PdhiOpenUpdateBinaryLog ( IN PPDHI_LOG pLog ) { PDH_STATUS pdhStatus; LONG lWin32Status; PPDHI_BINARY_LOG_HEADER_RECORD pHeader; pLog->StreamFile = (FILE *)((DWORD_PTR)(-1)); // map file header as a memory array for reading assert (pLog->hMappedLogFile != NULL); // should be open! if ((pLog->hMappedLogFile != NULL) && (pLog->lpMappedFileBase != NULL)) { // save size of binary log record header pLog->dwRecord1Size = dwFileHeaderLength + // ID characters 2 + // quotations PdhidwRecordTerminatorLength; // CR/LF terminator pLog->dwRecord1Size = QWORD_MULTIPLE(pLog->dwRecord1Size); // read the header and get the option flags pHeader = (PPDHI_BINARY_LOG_HEADER_RECORD) ((LPBYTE)(pLog->lpMappedFileBase) + pLog->dwRecord1Size); assert (*(WORD *)&(pHeader->RecHeader.dwType) == BINLOG_START_WORD); pLog->dwLogFormat |= pHeader->Info.dwFlags; pdhStatus = ERROR_SUCCESS; } else { // return PDH Error lWin32Status = GetLastError(); pdhStatus = PDH_LOG_FILE_OPEN_ERROR; } pdhStatus = ERROR_SUCCESS; return pdhStatus; } PDH_FUNCTION PdhiOpenOutputBinaryLog ( IN PPDHI_LOG pLog ) { PDH_STATUS pdhStatus = ERROR_SUCCESS; if (pLog->llMaxSize > 0) { // this is a circular or limited linear log file so: // 1) allocate a file of the desired size, pLog->hMappedLogFile = CreateFileMappingW ( pLog->hLogFileHandle, NULL, PAGE_READWRITE, HIDWORD(pLog->llMaxSize), LODWORD(pLog->llMaxSize), NULL); if (pLog->hMappedLogFile != NULL) { // 2) map it as a memory section pLog->lpMappedFileBase = MapViewOfFile ( pLog->hMappedLogFile, FILE_MAP_WRITE, 0, 0, LODWORD(pLog->llMaxSize)); if (pLog->lpMappedFileBase == NULL) { // close the file mapping and return error pdhStatus = GetLastError(); CloseHandle (pLog->hMappedLogFile); pLog->hMappedLogFile = NULL; } } else { pdhStatus = GetLastError(); pLog->hMappedLogFile = NULL; } } else { // this is just a sequential access file where each record will // be appended to the last one pLog->StreamFile = (FILE *)((DWORD_PTR)(-1)); pdhStatus = ERROR_SUCCESS; } return pdhStatus; } PDH_FUNCTION PdhiCloseBinaryLog ( IN PPDHI_LOG pLog, IN DWORD dwFlags ) { PDH_STATUS pdhStatus = ERROR_SUCCESS; BOOL bStatus; LONGLONG llEndOfFile = 0; PPDHI_BINARY_LOG_HEADER_RECORD pHeader; BOOL bNeedToCloseHandles = FALSE; UNREFERENCED_PARAMETER (dwFlags); // if open for reading, then the file is also mapped as a memory section if (pLog->lpMappedFileBase != NULL) { // if open for output, get "logical" end of file so it // can be truncated to to the amount of file used in order to // save disk space if ((pLog->dwLogFormat & PDH_LOG_ACCESS_MASK) == PDH_LOG_WRITE_ACCESS) { pHeader = (PPDHI_BINARY_LOG_HEADER_RECORD) ((LPBYTE)(pLog->lpMappedFileBase) + pLog->dwRecord1Size); llEndOfFile = pHeader->Info.WrapOffset; if (llEndOfFile < pHeader->Info.NextRecordOffset) { llEndOfFile = pHeader->Info.NextRecordOffset; } } pdhStatus = UnmapReadonlyMappedFile (pLog->lpMappedFileBase, &bNeedToCloseHandles); assert (pdhStatus == ERROR_SUCCESS); pLog->lpMappedFileBase = NULL; // for mapped files, this is a pointer into the file/memory section // so once the view is unmapped, it's no longer valid pLog->pLastRecordRead = NULL; } if (bNeedToCloseHandles) { if (pLog->hMappedLogFile != NULL) { bStatus = CloseHandle (pLog->hMappedLogFile); assert (bStatus); pLog->hMappedLogFile = NULL; } if (pdhStatus == ERROR_SUCCESS) { if (!(FlushFileBuffers (pLog->hLogFileHandle))) { pdhStatus = GetLastError(); } } else { // close them anyway, but save the status from the prev. call FlushFileBuffers (pLog->hLogFileHandle); } // see if we can truncate the file if (llEndOfFile > 0) { DWORD dwLoPos, dwHighPos; // truncate at the last byte used dwLoPos = LODWORD(llEndOfFile); dwHighPos = HIDWORD(llEndOfFile); dwLoPos = SetFilePointer (pLog->hLogFileHandle, dwLoPos, (LONG *)&dwHighPos, FILE_BEGIN); if (dwLoPos == 0xFFFFFFFF) { pdhStatus = GetLastError (); } assert (pdhStatus == ERROR_SUCCESS); assert (dwLoPos == LODWORD(llEndOfFile)); assert (dwHighPos == HIDWORD(llEndOfFile)); if (pdhStatus == ERROR_SUCCESS) { if (!SetEndOfFile(pLog->hLogFileHandle)) { pdhStatus = GetLastError(); } } } // else don't know where the end is so continue if (pLog->hLogFileHandle != INVALID_HANDLE_VALUE) { bStatus = CloseHandle (pLog->hLogFileHandle); assert (bStatus); pLog->hLogFileHandle = INVALID_HANDLE_VALUE; } } else { // the handles have already been closed so just // clear their values pLog->lpMappedFileBase = NULL; pLog->hMappedLogFile = NULL; pLog->hLogFileHandle = INVALID_HANDLE_VALUE; } return pdhStatus; } PDH_FUNCTION PdhiWriteBinaryLogHeader ( IN PPDHI_LOG pLog, IN LPCWSTR szUserCaption ) { PDH_STATUS pdhStatus = ERROR_SUCCESS; DWORD dwBytesWritten; CHAR szTrailDelim[4]; DWORD dwTrailSize; PPDHI_BINARY_LOG_HEADER_RECORD pLogHeader; PPDHI_LOG_COUNTER_PATH pLogCounterBuffer = NULL; PPDHI_LOG_COUNTER_PATH pThisLogCounter = NULL; PPDHI_COUNTER pThisCounter; DWORD dwPathBuffSize; DWORD dwBufSize = 0; DWORD dwBufToCopy; DWORD dwNewSize; PCHAR szOutputBuffer = NULL; DWORD dwOutputBufferSize = 0; DWORD dwOutputBufferUsed = 0; PWCHAR pBufferBase; LONG lBufferOffset; PBYTE pSourceBase; LONGLONG llStartingOffset; UNREFERENCED_PARAMETER (szUserCaption); // this is just used for the header string so it // doesn't need to be very large dwOutputBufferSize = SMALL_BUFFER_SIZE; szOutputBuffer = G_ALLOC (dwOutputBufferSize); if (szOutputBuffer == NULL) { assert (GetLastError() == ERROR_SUCCESS); return PDH_MEMORY_ALLOCATION_FAILURE; } szTrailDelim[0] = DOUBLEQUOTE_A; szTrailDelim[1] = 0; szTrailDelim[2] = 0; szTrailDelim[3] = 0; dwTrailSize = 1; // write log file type record memset (szOutputBuffer, 0, dwOutputBufferSize); lstrcpyA (szOutputBuffer, szTrailDelim); lstrcatA (szOutputBuffer, szBinLogFileHeader); lstrcatA (szOutputBuffer, szTrailDelim); lstrcatA (szOutputBuffer, PdhiszRecordTerminator); dwOutputBufferUsed = lstrlenA(szOutputBuffer); // align following structures on LONGLONG boundries dwOutputBufferUsed = QWORD_MULTIPLE (dwOutputBufferUsed); // save size of binary log record header pLog->dwRecord1Size = dwOutputBufferUsed; // from here on out all records have a 2-byte length field at // the beginning to indicate how long the record is // go through all the counters in the query and // write them to the log file pThisCounter = pLog->pQuery ? pLog->pQuery->pCounterListHead : NULL; if (pThisCounter != NULL) { do { // get the counter path information from the counter struct dwPathBuffSize = (DWORD)G_SIZE (pThisCounter->pCounterPath); dwBufToCopy = dwPathBuffSize; dwBufToCopy -= (DWORD)((DWORD_PTR)&pThisCounter->pCounterPath->pBuffer[0] - (DWORD_PTR)pThisCounter->pCounterPath); dwPathBuffSize -= (DWORD)((DWORD_PTR)&pThisCounter->pCounterPath->pBuffer[0] - (DWORD_PTR)pThisCounter->pCounterPath); dwPathBuffSize += sizeof (PDHI_LOG_COUNTER_PATH) - sizeof (WCHAR); // Buffer[0]. // adjust buffer size for possible wildcard entry // note that this may not be used, it'll be allocated in // any event dwPathBuffSize += sizeof (DWORD); // dword align the stuctures dwPathBuffSize = QWORD_MULTIPLE(dwPathBuffSize); //extend buffer to accomodate this new counter if (pLogCounterBuffer == NULL) { // then allocate the first one dwNewSize = dwPathBuffSize + sizeof(PDHI_BINARY_LOG_HEADER_RECORD); assert (dwNewSize > 0); // to make sure the structure size doesn't change accidentally assert (sizeof(PDHI_BINARY_LOG_INFO) == 256); pLogCounterBuffer = G_ALLOC (dwNewSize); if (pLogCounterBuffer == NULL) { pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; goto Cleanup; } pThisLogCounter = (PPDHI_LOG_COUNTER_PATH)( (LPBYTE)pLogCounterBuffer + sizeof(PDHI_BINARY_LOG_HEADER_RECORD)); dwBufSize = dwNewSize; } else { PPDHI_LOG_COUNTER_PATH pOldCounter = pLogCounterBuffer; // extend buffer for new entry dwNewSize = (dwBufSize + dwPathBuffSize); assert (dwNewSize > 0); pLogCounterBuffer = G_REALLOC (pOldCounter, dwNewSize); if (pLogCounterBuffer == NULL) { G_FREE(pOldCounter); pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; goto Cleanup; } pThisLogCounter = (PPDHI_LOG_COUNTER_PATH) ((LPBYTE)pLogCounterBuffer + dwBufSize); dwBufSize += dwPathBuffSize; } assert (pLogCounterBuffer != NULL); assert (pThisLogCounter != NULL); assert (G_SIZE (pLogCounterBuffer) > 0); pThisLogCounter->dwLength = dwPathBuffSize; pThisLogCounter->dwFlags = pThisCounter->dwFlags; pThisLogCounter->dwUserData = pThisCounter->dwUserData; pThisLogCounter->dwCounterType = pThisCounter->plCounterInfo.dwCounterType; pThisLogCounter->lDefaultScale = pThisCounter->plCounterInfo.lDefaultScale; pThisLogCounter->llTimeBase = pThisCounter->TimeBase; // copy the counter path string data to the log buffer, then // convert the pointers to offsets pSourceBase = &pThisCounter->pCounterPath->pBuffer[0]; // if this is a wild card path, then move the strings up // 1 dword in the buffer allowing the first DWORD of the // the buffer to contain the offset into the catalog // of the instances found in this log file. This list // will be built after the log is closed. lBufferOffset = 0; // in WORDS (not bytes) if (pThisCounter->pCounterPath->szInstanceName != NULL) { if (*pThisCounter->pCounterPath->szInstanceName == SPLAT_L) { // this is a wildcard path so save room for the // pointer into the catalog lBufferOffset = sizeof(DWORD); } } #if DBG if (lBufferOffset > 0) *(LPDWORD)(&pThisLogCounter->Buffer[0]) = 0x12345678; #endif pBufferBase = (PWCHAR)((LPBYTE)&pThisLogCounter->Buffer[0] + lBufferOffset); RtlCopyMemory((LPVOID) pBufferBase, (LPVOID)pSourceBase, dwBufToCopy); // find offsets from the start of the buffer if (pThisCounter->pCounterPath->szMachineName != NULL) { pThisLogCounter->lMachineNameOffset = lBufferOffset + (LONG)((DWORD_PTR)pThisCounter->pCounterPath->szMachineName - (DWORD_PTR)pSourceBase); //assert (pThisLogCounter->lMachineNameOffset == 0); } else { pThisLogCounter->lMachineNameOffset = (LONG)-1; } if (pThisCounter->pCounterPath->szObjectName != NULL) { pThisLogCounter->lObjectNameOffset = lBufferOffset + (LONG)((DWORD_PTR)pThisCounter->pCounterPath->szObjectName - (DWORD_PTR)pSourceBase); } else { pThisLogCounter->lObjectNameOffset = (LONG)-1; } if (pThisCounter->pCounterPath->szInstanceName != NULL) { pThisLogCounter->lInstanceOffset = lBufferOffset + (LONG)((DWORD_PTR)pThisCounter->pCounterPath->szInstanceName - (DWORD_PTR)pSourceBase); } else { pThisLogCounter->lInstanceOffset = (LONG)-1; } if (pThisCounter->pCounterPath->szParentName != NULL) { pThisLogCounter->lParentOffset = lBufferOffset + (LONG)((DWORD_PTR)pThisCounter->pCounterPath->szParentName - (DWORD_PTR)pSourceBase); } else { pThisLogCounter->lParentOffset = (LONG)-1; } pThisLogCounter->dwIndex = pThisCounter->pCounterPath->dwIndex; if (pThisCounter->pCounterPath->szCounterName != NULL) { pThisLogCounter->lCounterOffset = lBufferOffset + (LONG)((DWORD_PTR)pThisCounter->pCounterPath->szCounterName - (DWORD_PTR)pSourceBase); } else { pThisLogCounter->lCounterOffset = (LONG)-1; } pThisCounter = pThisCounter->next.flink; // go to next in list } while (pThisCounter != pLog->pQuery->pCounterListHead); if (pdhStatus == ERROR_SUCCESS) { assert (dwBufSize < 0x00010000); // just to see if we get any big lists // update the record header pLogHeader = (PPDHI_BINARY_LOG_HEADER_RECORD)(pLogCounterBuffer); pLogHeader->RecHeader.dwType = BINLOG_TYPE_CATALOG_LIST; pLogHeader->RecHeader.dwLength = dwBufSize; pLogHeader->Info.FileLength = 0; pLogHeader->Info.dwLogVersion = BINLOG_VERSION; // save the log options only here pLogHeader->Info.dwFlags = pLog->dwLogFormat & PDH_LOG_OPT_MASK; pLogHeader->Info.StartTime = 0; pLogHeader->Info.EndTime = 0; pLogHeader->Info.CatalogOffset = 0; pLogHeader->Info.CatalogChecksum = PdhiComputeDwordChecksum ( (LPVOID) &pLogHeader[1], (dwBufSize - sizeof(PDHI_BINARY_LOG_HEADER_RECORD))); pLogHeader->Info.CatalogDate = 0; // record pointers are all the same for the first record llStartingOffset = dwBufSize + pLog->dwRecord1Size; pLogHeader->Info.FirstDataRecordOffset = llStartingOffset; pLogHeader->Info.FirstRecordOffset = llStartingOffset; pLogHeader->Info.LastRecordOffset = llStartingOffset; pLogHeader->Info.NextRecordOffset = llStartingOffset; pLogHeader->Info.WrapOffset = 0; // write log file header to file if ((pdhStatus = PdhiWriteOneBinaryLogRecord (pLog, (LPCVOID)szOutputBuffer, dwOutputBufferUsed, &dwBytesWritten, WBLR_WRITE_LOG_HEADER)) == ERROR_SUCCESS) { // write log contents record to file pdhStatus = PdhiWriteOneBinaryLogRecord (pLog, (LPCVOID)pLogCounterBuffer, dwBufSize, &dwBytesWritten, WBLR_WRITE_COUNTER_HEADER); } } if (pLogCounterBuffer != NULL) G_FREE (pLogCounterBuffer); } else { // no counter's assigned to this query pdhStatus = PDH_NO_DATA; } Cleanup: if (szOutputBuffer != NULL) G_FREE (szOutputBuffer); return pdhStatus; } PDH_FUNCTION PdhiWriteBinaryLogRecord ( IN PPDHI_LOG pLog, IN SYSTEMTIME *stTimeStamp, IN LPCWSTR szUserString ) { PDH_STATUS pdhStatus = ERROR_SUCCESS; DWORD dwBytesWritten; PPDHI_BINARY_LOG_RECORD_HEADER pLogCounterBuffer = NULL; PPDHI_BINARY_LOG_RECORD_HEADER pThisLogCounter = NULL; PPDH_RAW_COUNTER pSingleCounter; PPDHI_RAW_COUNTER_ITEM_BLOCK pMultiCounter; PPDHI_COUNTER pThisCounter; DWORD dwCtrBufSize; DWORD dwBufSize = 0; BOOL bVarCtr; DWORD dwNewSize; DWORD dwBytesCopied = 0; int nItem; UNREFERENCED_PARAMETER (stTimeStamp); DBG_UNREFERENCED_PARAMETER (szUserString); // get first counter in list pThisCounter = pLog->pQuery ? pLog->pQuery->pCounterListHead : NULL; if (pThisCounter != NULL) { do { bVarCtr = pThisCounter->dwFlags & PDHIC_MULTI_INSTANCE ? TRUE : FALSE; if (bVarCtr) { dwCtrBufSize = pThisCounter->pThisRawItemList->dwLength; } else { dwCtrBufSize = sizeof (PDH_RAW_COUNTER); } // each counter gets a header dwCtrBufSize += sizeof (PDHI_BINARY_LOG_RECORD_HEADER); //extend buffer to accomodate this new counter if (pLogCounterBuffer == NULL) { // add in room for the master record header // then allocate the first one pLogCounterBuffer = G_ALLOC ((dwCtrBufSize + sizeof (PDHI_BINARY_LOG_RECORD_HEADER))); // set counter data pointer to just after the master // record header if (pLogCounterBuffer == NULL) { pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; break; } pThisLogCounter = (PPDHI_BINARY_LOG_RECORD_HEADER)( &pLogCounterBuffer[1]); dwBufSize = dwCtrBufSize + sizeof (PDHI_BINARY_LOG_RECORD_HEADER); } else { PPDHI_BINARY_LOG_RECORD_HEADER pOldBuffer = pLogCounterBuffer; // extend buffer for new entry dwNewSize = (dwBufSize + dwCtrBufSize); assert (dwNewSize); pLogCounterBuffer = G_REALLOC (pOldBuffer, dwNewSize); if (pLogCounterBuffer == NULL) { G_FREE(pOldBuffer); pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; goto Cleanup; } pThisLogCounter = (PPDHI_BINARY_LOG_RECORD_HEADER) ((LPBYTE)pLogCounterBuffer + dwBufSize); dwBufSize += dwCtrBufSize; } assert (pLogCounterBuffer != NULL); assert (pThisLogCounter != NULL); assert (G_SIZE (pLogCounterBuffer) > 0); // set the header fields and data pointer assert (dwCtrBufSize < 0x00010000); pThisLogCounter->dwLength = LOWORD(dwCtrBufSize); // add in size of record header dwBytesCopied += sizeof (PDHI_BINARY_LOG_RECORD_HEADER); if (bVarCtr) { // multiple counter pThisLogCounter->dwType = BINLOG_TYPE_DATA_MULTI; pMultiCounter = (PPDHI_RAW_COUNTER_ITEM_BLOCK) ( (LPBYTE)pThisLogCounter + sizeof(PDHI_BINARY_LOG_RECORD_HEADER)); RtlCopyMemory(pMultiCounter, pThisCounter->pThisRawItemList, pThisCounter->pThisRawItemList->dwLength); dwBytesCopied += pThisCounter->pThisRawItemList->dwLength; } else { // single counter pThisLogCounter->dwType = BINLOG_TYPE_DATA_SINGLE; pSingleCounter = (PPDH_RAW_COUNTER) ( (LPBYTE)pThisLogCounter + sizeof(PDHI_BINARY_LOG_RECORD_HEADER)); RtlCopyMemory(pSingleCounter, &pThisCounter->ThisValue, sizeof (PDH_RAW_COUNTER)); dwBytesCopied += sizeof (PDH_RAW_COUNTER); } pThisCounter = pThisCounter->next.flink; // go to next in list } while (pThisCounter != pLog->pQuery->pCounterListHead); // update the record header if (pdhStatus == ERROR_SUCCESS) { // add in size of master record header dwBytesCopied += sizeof (PDHI_BINARY_LOG_RECORD_HEADER); pLogCounterBuffer->dwType = BINLOG_TYPE_DATA; // Need to handle the case where the resulting record is // greater than 64K in length. Probably by breaking it into // multiple records. pLogCounterBuffer->dwLength = dwBufSize; assert (dwBufSize == dwBytesCopied); // write record to file pdhStatus = PdhiWriteOneBinaryLogRecord ( pLog, (LPCVOID)pLogCounterBuffer, dwBufSize, &dwBytesWritten, 0); } if (pLogCounterBuffer != NULL) G_FREE (pLogCounterBuffer); } else { // no counter's assigned to this query pdhStatus = PDH_NO_DATA; } Cleanup: return pdhStatus; } PDH_FUNCTION PdhiEnumMachinesFromBinaryLog ( PPDHI_LOG pLog, LPVOID pBuffer, LPDWORD pcchBufferSize, BOOL bUnicodeDest ) { LPVOID pTempBuffer = NULL; LPVOID pOldBuffer; DWORD dwTempBufferSize; LPVOID LocalBuffer = NULL; DWORD dwLocalBufferSize; PDH_STATUS pdhStatus = ERROR_SUCCESS; // read the header record and enum the machine name from the entries if (pLog->dwMaxRecordSize == 0) { // no size is defined so start with 64K pLog->dwMaxRecordSize = 0x010000; } dwTempBufferSize = pLog->dwMaxRecordSize; pTempBuffer = G_ALLOC(dwTempBufferSize); if (pTempBuffer == NULL) { pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; goto Cleanup; } dwLocalBufferSize = MEDIUM_BUFFER_SIZE; LocalBuffer = G_ALLOC(dwLocalBufferSize * (bUnicodeDest ? sizeof(WCHAR) : sizeof(CHAR))); if (LocalBuffer == NULL) { pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; goto Cleanup; } // read in the catalog record while ((pdhStatus = PdhiReadOneBinLogRecord (pLog, BINLOG_HEADER_RECORD, pTempBuffer, dwTempBufferSize)) != ERROR_SUCCESS) { if (pdhStatus == PDH_MORE_DATA) { // read the 1st WORD to see if this is a valid record if (*(WORD *)pTempBuffer == BINLOG_START_WORD) { // it's a valid record so read the 2nd DWORD to get the // record size; dwTempBufferSize = ((DWORD *)pTempBuffer)[1]; if (dwTempBufferSize < pLog->dwMaxRecordSize) { // then something is bogus so return an error pdhStatus = PDH_ENTRY_NOT_IN_LOG_FILE; break; // out of while loop } else { pLog->dwMaxRecordSize = dwTempBufferSize; } } else { // we're lost in this file pdhStatus = PDH_ENTRY_NOT_IN_LOG_FILE; break; // out of while loop } // realloc a new buffer pOldBuffer = pTempBuffer; pTempBuffer = G_REALLOC (pOldBuffer, dwTempBufferSize); if (pTempBuffer == NULL) { // return memory error G_FREE(pOldBuffer); assert (GetLastError() == ERROR_SUCCESS); pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; break; } } else { // some other error was returned so // return error from read function break; } } if (pdhStatus == ERROR_SUCCESS) { PPDHI_LOG_COUNTER_PATH pPath; DWORD dwBytesProcessed; LONG nItemCount = 0; LPBYTE pFirstChar; LPWSTR szMachineName; DWORD dwRecordLength; DWORD dwBufferUsed = 0; DWORD dwNewBuffer = 0; // we can assume the record was read successfully so read in the // machine names dwRecordLength = ((PPDHI_BINARY_LOG_RECORD_HEADER)pTempBuffer)->dwLength; pPath = (PPDHI_LOG_COUNTER_PATH) ((LPBYTE)pTempBuffer + sizeof (PDHI_BINARY_LOG_HEADER_RECORD)); dwBytesProcessed = sizeof(PDHI_BINARY_LOG_HEADER_RECORD); while (dwBytesProcessed < dwRecordLength) { if (pPath->lMachineNameOffset >= 0L) { // then there's a machine name in this record so get // it's size pFirstChar = (LPBYTE)&pPath->Buffer[0]; szMachineName = (LPWSTR)((LPBYTE)pFirstChar + pPath->lMachineNameOffset); dwNewBuffer = (lstrlenW (szMachineName) + 1); while (dwNewBuffer + dwBufferUsed > dwLocalBufferSize) { pOldBuffer = LocalBuffer; dwLocalBufferSize += MEDIUM_BUFFER_SIZE; LocalBuffer = G_REALLOC(pOldBuffer, dwLocalBufferSize * (bUnicodeDest ? sizeof(WCHAR) : sizeof(CHAR))); if (LocalBuffer == NULL) { if (pOldBuffer != NULL) G_FREE(pOldBuffer); pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; goto Cleanup; } } dwNewBuffer = AddUniqueWideStringToMultiSz((LPVOID) LocalBuffer, szMachineName, bUnicodeDest); if (dwNewBuffer > 0) { dwBufferUsed = dwNewBuffer; nItemCount ++; } } // get next path entry from log file record dwBytesProcessed += pPath->dwLength; pPath = (PPDHI_LOG_COUNTER_PATH) ((LPBYTE) pPath + pPath->dwLength); } if ((nItemCount > 0) && (pdhStatus != PDH_INSUFFICIENT_BUFFER) && (pdhStatus != PDH_MORE_DATA)) { // then the routine was successful. Errors that occurred // while scanning will be ignored as long as at least // one entry was successfully read pdhStatus = ERROR_SUCCESS; } if (nItemCount > 0) { dwBufferUsed ++; } if (pBuffer && dwBufferUsed <= * pcchBufferSize) { RtlCopyMemory(pBuffer, LocalBuffer, dwBufferUsed * (bUnicodeDest ? sizeof(WCHAR) : sizeof(CHAR))); } else { if (pBuffer) RtlCopyMemory(pBuffer, LocalBuffer, (* pcchBufferSize) * (bUnicodeDest ? sizeof(WCHAR) : sizeof(CHAR))); pdhStatus = PDH_MORE_DATA; } *pcchBufferSize = dwBufferUsed; } Cleanup: if (LocalBuffer != NULL) G_FREE(LocalBuffer); if (pTempBuffer != NULL) G_FREE(pTempBuffer); return pdhStatus; } PDH_FUNCTION PdhiEnumObjectsFromBinaryLog ( IN PPDHI_LOG pLog, IN LPCWSTR szMachineName, IN LPVOID pBuffer, IN LPDWORD pcchBufferSize, IN DWORD dwDetailLevel, IN BOOL bUnicodeDest ) { LPVOID pTempBuffer = NULL; LPVOID pOldBuffer; DWORD dwTempBufferSize; LPVOID LocalBuffer = NULL; DWORD dwLocalBufferSize; LPCWSTR szLocalMachine = szMachineName; PDH_STATUS pdhStatus = ERROR_SUCCESS; // read the header record and enum the machine name from the entries UNREFERENCED_PARAMETER (dwDetailLevel); if (pLog->dwMaxRecordSize == 0) { // no size is defined so start with 64K pLog->dwMaxRecordSize = 0x010000; } if (szLocalMachine == NULL) szLocalMachine = szStaticLocalMachineName; else if (szLocalMachine[0] == L'\0') szLocalMachine = szStaticLocalMachineName; dwTempBufferSize = pLog->dwMaxRecordSize; pTempBuffer = G_ALLOC(dwTempBufferSize); if (pTempBuffer == NULL) { pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; goto Cleanup; } dwLocalBufferSize = MEDIUM_BUFFER_SIZE; LocalBuffer = G_ALLOC(dwLocalBufferSize * (bUnicodeDest ? sizeof(WCHAR) : sizeof(CHAR))); if (LocalBuffer == NULL) { pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; goto Cleanup; } // read in the catalog record while ((pdhStatus = PdhiReadOneBinLogRecord (pLog, BINLOG_HEADER_RECORD, pTempBuffer, dwTempBufferSize)) != ERROR_SUCCESS) { if (pdhStatus == PDH_MORE_DATA) { // read the 1st WORD to see if this is a valid record if (*(WORD *)pTempBuffer == BINLOG_START_WORD) { // it's a valid record so read the 2nd DWORD to get the // record size; dwTempBufferSize = ((DWORD *)pTempBuffer)[1]; if (dwTempBufferSize < pLog->dwMaxRecordSize) { // then something is bogus so return an error pdhStatus = PDH_ENTRY_NOT_IN_LOG_FILE; break; // out of while loop } else { pLog->dwMaxRecordSize = dwTempBufferSize; } } else { // we're lost in this file pdhStatus = PDH_ENTRY_NOT_IN_LOG_FILE; break; // out of while loop } // realloc a new buffer pOldBuffer = pTempBuffer; pTempBuffer = G_REALLOC (pOldBuffer, dwTempBufferSize); if (pTempBuffer == NULL) { // return memory error G_FREE(pOldBuffer); assert (GetLastError() == ERROR_SUCCESS); pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; break; } } else { // some other error was returned so // return error from read function break; } } if (pdhStatus == ERROR_SUCCESS) { PPDHI_LOG_COUNTER_PATH pPath; DWORD dwBytesProcessed; LONG nItemCount = 0; LPBYTE pFirstChar; LPWSTR szThisMachineName; LPWSTR szThisObjectName; DWORD dwRecordLength; DWORD dwBufferUsed = 0; DWORD dwNewBuffer = 0; BOOL bCopyThisObject; // we can assume the record was read successfully so read in the // objects that match the machine name and detail level criteria dwRecordLength = ((PPDHI_BINARY_LOG_RECORD_HEADER)pTempBuffer)->dwLength; pPath = (PPDHI_LOG_COUNTER_PATH) ((LPBYTE)pTempBuffer + sizeof (PDHI_BINARY_LOG_HEADER_RECORD)); dwBytesProcessed = sizeof(PDHI_BINARY_LOG_HEADER_RECORD); while (dwBytesProcessed < dwRecordLength) { bCopyThisObject = FALSE; szThisObjectName = NULL; pFirstChar = (LPBYTE)&pPath->Buffer[0]; if (pPath->lMachineNameOffset >= 0L) { // then there's a machine name in this record so get // it's size szThisMachineName = (LPWSTR)((LPBYTE)pFirstChar + pPath->lMachineNameOffset); // if this is for the desired machine, then copy this object if (lstrcmpiW(szThisMachineName, szLocalMachine) == 0) { if (szThisObjectName >= 0) { szThisObjectName = (LPWSTR)((LPBYTE)pFirstChar + pPath->lObjectNameOffset); bCopyThisObject = TRUE; } else { // no object to copy } } else { // this machine isn't selected } } else { // there's no machine specified so for this counter so list it by default if (szThisObjectName >= 0) { szThisObjectName = (LPWSTR)((LPBYTE)pFirstChar + pPath->lObjectNameOffset); bCopyThisObject = TRUE; } else { // no object to copy } } if (bCopyThisObject && szThisObjectName != NULL) { // get the size of this object's name dwNewBuffer = (lstrlenW(szThisObjectName) + 1); while (dwNewBuffer + dwBufferUsed > dwLocalBufferSize) { pOldBuffer = LocalBuffer; dwLocalBufferSize += MEDIUM_BUFFER_SIZE; LocalBuffer = G_REALLOC(pOldBuffer, dwLocalBufferSize * (bUnicodeDest ? sizeof(WCHAR) : sizeof(CHAR))); if (LocalBuffer == NULL) { if (pOldBuffer != NULL) G_FREE(pOldBuffer); pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; goto Cleanup; } } dwNewBuffer = AddUniqueWideStringToMultiSz((LPVOID) LocalBuffer, szThisObjectName, bUnicodeDest); if (dwNewBuffer > 0) { dwBufferUsed = dwNewBuffer; nItemCount ++; } } // get next path entry from log file record dwBytesProcessed += pPath->dwLength; pPath = (PPDHI_LOG_COUNTER_PATH) ((LPBYTE)pPath + pPath->dwLength); } if ((nItemCount > 0) && (pdhStatus != PDH_INSUFFICIENT_BUFFER) && (pdhStatus != PDH_MORE_DATA)) { // then the routine was successful. Errors that occurred // while scanning will be ignored as long as at least // one entry was successfully read pdhStatus = ERROR_SUCCESS; } if (nItemCount > 0) { dwBufferUsed ++; } if (pBuffer && dwBufferUsed <= * pcchBufferSize) { RtlCopyMemory(pBuffer, LocalBuffer, dwBufferUsed * (bUnicodeDest ? sizeof(WCHAR) : sizeof(CHAR))); } else { if (pBuffer) RtlCopyMemory(pBuffer, LocalBuffer, (* pcchBufferSize) * (bUnicodeDest ? sizeof(WCHAR) : sizeof(CHAR))); pdhStatus = PDH_MORE_DATA; } * pcchBufferSize = dwBufferUsed; } Cleanup: if (LocalBuffer != NULL) G_FREE(LocalBuffer); if (pTempBuffer != NULL) G_FREE(pTempBuffer); return pdhStatus; } PDH_FUNCTION PdhiEnumObjectItemsFromBinaryLog ( IN PPDHI_LOG pLog, IN LPCWSTR szMachineName, IN LPCWSTR szObjectName, IN PDHI_COUNTER_TABLE CounterTable, IN DWORD dwDetailLevel, IN DWORD dwFlags ) { LPVOID pTempBuffer = NULL; LPVOID pOldBuffer; DWORD dwTempBufferSize; PDH_STATUS pdhStatus = ERROR_SUCCESS; PPDHI_INST_LIST pInstList; PPDHI_INSTANCE pInstance; BOOL bProcessInstance = FALSE; UNREFERENCED_PARAMETER (dwDetailLevel); UNREFERENCED_PARAMETER (dwFlags); // read the header record and enum the machine name from the entries if (pLog->dwMaxRecordSize == 0) { // no size is defined so start with 64K pLog->dwMaxRecordSize = 0x010000; } dwTempBufferSize = pLog->dwMaxRecordSize; pTempBuffer = G_ALLOC (dwTempBufferSize); assert (pTempBuffer != NULL); if (pTempBuffer == NULL) { assert (GetLastError() == ERROR_SUCCESS); return PDH_MEMORY_ALLOCATION_FAILURE; } // read in the catalog record while ((pdhStatus = PdhiReadOneBinLogRecord (pLog, BINLOG_HEADER_RECORD, pTempBuffer, dwTempBufferSize)) != ERROR_SUCCESS) { if (pdhStatus == PDH_MORE_DATA) { // read the 1st WORD to see if this is a valid record if (*(WORD *)pTempBuffer == BINLOG_START_WORD) { // it's a valid record so read the 2nd DWORD to get the // record size; dwTempBufferSize = ((DWORD *)pTempBuffer)[1]; if (dwTempBufferSize < pLog->dwMaxRecordSize) { // then something is bogus so return an error pdhStatus = PDH_ENTRY_NOT_IN_LOG_FILE; break; // out of while loop } else { pLog->dwMaxRecordSize = dwTempBufferSize; } } else { // we're lost in this file pdhStatus = PDH_ENTRY_NOT_IN_LOG_FILE; break; // out of while loop } // realloc a new buffer pOldBuffer = pTempBuffer; pTempBuffer = G_REALLOC (pOldBuffer, dwTempBufferSize); if (pTempBuffer == NULL) { // return memory error G_FREE(pOldBuffer); assert (GetLastError() == ERROR_SUCCESS); pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; break; } } else { // some other error was returned so // return error from read function break; } } if (pdhStatus == ERROR_SUCCESS) { PPDHI_BINARY_LOG_HEADER_RECORD pHeader; PPDHI_LOG_COUNTER_PATH pPath; PPDHI_BINARY_LOG_RECORD_HEADER pThisMasterRecord; PPDHI_BINARY_LOG_RECORD_HEADER pThisSubRecord; PPDHI_RAW_COUNTER_ITEM_BLOCK pDataBlock; PPDHI_RAW_COUNTER_ITEM pDataItem; DWORD dwBytesProcessed; LONG nItemCount = 0; LPBYTE pFirstChar; LPWSTR szThisMachineName; LPWSTR szThisObjectName; LPWSTR szThisCounterName = NULL; LPWSTR szThisInstanceName; LPWSTR szThisParentName; WCHAR szCompositeInstance[1024]; DWORD dwRecordLength; BOOL bCopyThisObject; DWORD dwIndex; DWORD dwThisRecordIndex; DWORD dwDataItemIndex; PLOG_BIN_CAT_RECORD pCatRec; LPWSTR szWideInstanceName; pHeader = (PPDHI_BINARY_LOG_HEADER_RECORD)pTempBuffer; // we can assume the record was read successfully so read in the // objects that match the machine name and detail level criteria dwRecordLength = ((PPDHI_BINARY_LOG_RECORD_HEADER)pTempBuffer)->dwLength; pPath = (PPDHI_LOG_COUNTER_PATH) ((LPBYTE)pTempBuffer + sizeof (PDHI_BINARY_LOG_HEADER_RECORD)); dwBytesProcessed = sizeof(PDHI_BINARY_LOG_HEADER_RECORD); dwIndex = 0; while (dwBytesProcessed < dwRecordLength) { bCopyThisObject = FALSE; szThisObjectName = NULL; dwIndex++; pFirstChar = (LPBYTE)&pPath->Buffer[0]; if (pPath->lMachineNameOffset >= 0L) { // then there's a machine name in this record so get // it's size szThisMachineName = (LPWSTR)((LPBYTE)pFirstChar + pPath->lMachineNameOffset); // if this is for the desired machine, then select the object if (lstrcmpiW(szThisMachineName,szMachineName) == 0) { if (szThisObjectName >= 0) { szThisObjectName = (LPWSTR)((LPBYTE)pFirstChar + pPath->lObjectNameOffset); if (lstrcmpiW(szThisObjectName,szObjectName) == 0) { // then this is the object to look up bCopyThisObject = TRUE; } else { // not this object } } else { // no object to copy } } else { // this machine isn't selected } } else { // there's no machine specified so for this counter so list it by default if (pPath->lObjectNameOffset >= 0) { szThisObjectName = (LPWSTR)((LPBYTE)pFirstChar + pPath->lObjectNameOffset); if (lstrcmpiW(szThisObjectName,szObjectName) == 0) { // then this is the object to look up bCopyThisObject = TRUE; } else { // not this object } } else { // no object to copy } } if (bCopyThisObject) { // if here, then there should be a name assert (szThisObjectName != NULL); // get the counter name from this counter and add it to the list if (pPath->lCounterOffset > 0) { szThisCounterName = (LPWSTR)((LPBYTE)pFirstChar + pPath->lCounterOffset); } else { szThisCounterName = NULL; bCopyThisObject = FALSE; } } if (bCopyThisObject) { pdhStatus = PdhiFindCounterInstList( CounterTable, szThisCounterName, & pInstList); if (pdhStatus != ERROR_SUCCESS || pInstList == NULL) { continue; } // check instance now // get the instance name from this counter and add it to the list if (pPath->lInstanceOffset >= 0) { szThisInstanceName = (LPWSTR)((LPBYTE)pFirstChar + pPath->lInstanceOffset); if (*szThisInstanceName != SPLAT_L) { if (pPath->lParentOffset >= 0) { szThisParentName = (LPWSTR)((LPBYTE)pFirstChar + pPath->lParentOffset); lstrcpyW (szCompositeInstance, szThisParentName); lstrcatW (szCompositeInstance, cszSlash); lstrcatW (szCompositeInstance, szThisInstanceName); } else { lstrcpyW (szCompositeInstance, szThisInstanceName); } //if (pPath->dwIndex > 0) { // _ltow (pPath->dwIndex, (LPWSTR) // (szCompositeInstance + lstrlenW(szCompositeInstance)), // 10L); //} pdhStatus = PdhiFindInstance( & pInstList->InstList, szCompositeInstance, TRUE, & pInstance); if (pdhStatus == ERROR_SUCCESS) { nItemCount++; } } else { // only use the catalog if it's up to date and present if ((pHeader->Info.CatalogOffset > 0) && (pHeader->Info.LastUpdateTime <= pHeader->Info.CatalogDate)){ // find catalog record pCatRec = (PLOG_BIN_CAT_RECORD) // base of mapped log file ((LPBYTE)pLog->lpMappedFileBase + // + offset to catalog records pHeader->Info.CatalogOffset + // + offset to the instance entry for this item *(LPDWORD)&pPath->Buffer[0]); assert (pCatRec != NULL); assert (pCatRec->RecHeader.dwType == BINLOG_TYPE_CATALOG_ITEM); for (szWideInstanceName = (LPWSTR)((LPBYTE)&pCatRec->CatEntry + pCatRec->CatEntry.dwInstanceStringOffset); * szWideInstanceName != 0; szWideInstanceName += lstrlenW(szWideInstanceName) + 1) { pdhStatus = PdhiFindInstance( & pInstList->InstList, szWideInstanceName, TRUE, & pInstance); } } else if (! bProcessInstance) { // look up individual instances in log... // read records from file and store instances dwThisRecordIndex = BINLOG_FIRST_DATA_RECORD; // this call just moved the record pointer pdhStatus = PdhiReadOneBinLogRecord ( pLog, dwThisRecordIndex, NULL, 0); while (pdhStatus == ERROR_SUCCESS) { PdhiResetInstanceCount(CounterTable); pThisMasterRecord = (PPDHI_BINARY_LOG_RECORD_HEADER) pLog->pLastRecordRead; // make sure we haven't left the file assert (pThisMasterRecord != NULL); assert ((LPBYTE)pThisMasterRecord > (LPBYTE)pLog->lpMappedFileBase); assert ((LPBYTE)pThisMasterRecord < ((LPBYTE)pLog->lpMappedFileBase + pLog->llFileSize)); pThisSubRecord = PdhiGetSubRecord ( pThisMasterRecord, dwIndex); assert (pThisSubRecord != NULL); assert (pThisSubRecord->dwType == BINLOG_TYPE_DATA_MULTI); if (pThisSubRecord == NULL) { // bail on a null record pdhStatus = PDH_END_OF_LOG_FILE; break; } pDataBlock = (PPDHI_RAW_COUNTER_ITEM_BLOCK) ((LPBYTE)pThisSubRecord + sizeof (PDHI_BINARY_LOG_RECORD_HEADER)); // walk down list of entries and add them to the // list of instances (these should already // be assembled in parent/instance format) if (pDataBlock->dwLength > 0) { for (dwDataItemIndex = 0; dwDataItemIndex < pDataBlock->dwItemCount; dwDataItemIndex++) { pDataItem = &pDataBlock->pItemArray[dwDataItemIndex]; szThisInstanceName = (LPWSTR) (((LPBYTE) pDataBlock) + pDataItem->szName); pdhStatus = PdhiFindInstance( & pInstList->InstList, szThisInstanceName, TRUE, & pInstance); } } else { // no data in this record } if (pdhStatus != ERROR_SUCCESS) { // then exit loop, otherwise break; } else { // go to next record in log pdhStatus = PdhiReadOneBinLogRecord( pLog, ++dwThisRecordIndex, NULL, 0); } } if (pdhStatus == PDH_END_OF_LOG_FILE) { pdhStatus = ERROR_SUCCESS; } if (pdhStatus == ERROR_SUCCESS) { bProcessInstance = TRUE; } } } } memset (szCompositeInstance, 0, (sizeof(szCompositeInstance))); } // get next path entry from log file record dwBytesProcessed += pPath->dwLength; pPath = (PPDHI_LOG_COUNTER_PATH) ((LPBYTE)pPath + pPath->dwLength); } if ((nItemCount > 0) && (pdhStatus != PDH_INSUFFICIENT_BUFFER) && (pdhStatus != PDH_MORE_DATA)) { // then the routine was successful. Errors that occurred // while scanning will be ignored as long as at least // one entry was successfully read pdhStatus = ERROR_SUCCESS; } } if (pTempBuffer != NULL) G_FREE (pTempBuffer); return pdhStatus; } PDH_FUNCTION PdhiGetMatchingBinaryLogRecord ( IN PPDHI_LOG pLog, IN LONGLONG *pStartTime, IN LPDWORD pdwIndex ) { PDH_STATUS pdhStatus = ERROR_SUCCESS; DWORD dwRecordId; LONGLONG RecordTimeValue; LONGLONG LastTimeValue = 0; PPDHI_BINARY_LOG_RECORD_HEADER pThisMasterRecord; PPDHI_BINARY_LOG_RECORD_HEADER pThisSubRecord; PPDHI_RAW_COUNTER_ITEM_BLOCK pDataBlock; PPDH_RAW_COUNTER pRawItem; // read the first data record in the log file // note that the record read is not copied to the local buffer // rather the internal buffer is used in "read-only" mode // if the high dword of the time value is 0xFFFFFFFF, then the // low dword is the record id to read if ((*pStartTime & 0xFFFFFFFF00000000) == 0xFFFFFFFF00000000) { dwRecordId = (DWORD)(*pStartTime & 0x00000000FFFFFFFF); LastTimeValue = *pStartTime; if (dwRecordId == 0) return PDH_ENTRY_NOT_IN_LOG_FILE; } else { dwRecordId = BINLOG_FIRST_DATA_RECORD; } pdhStatus = PdhiReadOneBinLogRecord ( pLog, dwRecordId, NULL, 0); // to prevent copying the record while ((pdhStatus == ERROR_SUCCESS) && (dwRecordId >= BINLOG_FIRST_DATA_RECORD)) { // define pointer to the current record pThisMasterRecord = (PPDHI_BINARY_LOG_RECORD_HEADER) pLog->pLastRecordRead; // get timestamp of this record by looking at the first entry in the // record. assert (pThisMasterRecord->dwType == BINLOG_TYPE_DATA); pThisSubRecord = (PPDHI_BINARY_LOG_RECORD_HEADER)((LPBYTE)pThisMasterRecord + sizeof (PDHI_BINARY_LOG_RECORD_HEADER)); switch (pThisSubRecord->dwType) { case BINLOG_TYPE_DATA_SINGLE: pRawItem = (PPDH_RAW_COUNTER)((LPBYTE)pThisSubRecord + sizeof (PDHI_BINARY_LOG_RECORD_HEADER)); RecordTimeValue = MAKELONGLONG( pRawItem->TimeStamp.dwLowDateTime, pRawItem->TimeStamp.dwHighDateTime); break; case BINLOG_TYPE_DATA_MULTI: pDataBlock = (PPDHI_RAW_COUNTER_ITEM_BLOCK)((LPBYTE)pThisSubRecord + sizeof (PDHI_BINARY_LOG_RECORD_HEADER)); RecordTimeValue = *(LONGLONG *)&pDataBlock->TimeStamp; break; default: // unknown record type assert (FALSE); RecordTimeValue = 0; break; } if (RecordTimeValue != 0) { if ((*pStartTime == RecordTimeValue) || (*pStartTime == 0)) { // found the match so bail here LastTimeValue = RecordTimeValue; break; } else if (RecordTimeValue > *pStartTime) { // then this is the first record > than the desired time // so the desired value is the one before this one // unless it's the first data record of the log if (dwRecordId > BINLOG_FIRST_DATA_RECORD) { dwRecordId--; } else { // this hasnt' been initialized yet. LastTimeValue = RecordTimeValue; } break; } else { // save value for next trip through loop LastTimeValue = RecordTimeValue; // advance record counter and try the next entry dwRecordId++; } } else { // no timestamp field so ignore this record. dwRecordId++; } // read the next record in the file pdhStatus = PdhiReadOneBinLogRecord ( pLog, dwRecordId, NULL, 1); // to prevent copying the record } if (pdhStatus == ERROR_SUCCESS) { // then dwRecordId is the desired entry *pdwIndex = dwRecordId; *pStartTime = LastTimeValue; pdhStatus = ERROR_SUCCESS; } else if (dwRecordId < BINLOG_FIRST_DATA_RECORD) { // handle special cases for log type field and header record *pdwIndex = dwRecordId; *pStartTime = LastTimeValue; pdhStatus = ERROR_SUCCESS; } else { pdhStatus = PDH_ENTRY_NOT_IN_LOG_FILE; } return pdhStatus; } PDH_FUNCTION PdhiGetCounterFromDataBlock( IN PPDHI_LOG pLog, IN PVOID pDataBuffer, IN PPDHI_COUNTER pCounter); PDH_FUNCTION PdhiGetCounterValueFromBinaryLog ( IN PPDHI_LOG pLog, IN DWORD dwIndex, IN PPDHI_COUNTER pCounter ) { PDH_STATUS pdhStatus; PPDH_RAW_COUNTER pValue = & pCounter->ThisValue; // read the first data record in the log file // note that the record read is not copied to the local buffer // rather the internal buffer is used in "read-only" mode pdhStatus = PdhiReadOneBinLogRecord(pLog, dwIndex, NULL, 0); if (pdhStatus == ERROR_SUCCESS) { pdhStatus = PdhiGetCounterFromDataBlock(pLog, pLog->pLastRecordRead, pCounter); } else { // no more records in log file pdhStatus = PDH_NO_MORE_DATA; // unable to find entry in the log file pValue->CStatus = PDH_CSTATUS_INVALID_DATA; pValue->TimeStamp.dwLowDateTime = pValue->TimeStamp.dwHighDateTime = 0; pValue->FirstValue = 0; pValue->SecondValue = 0; pValue->MultiCount = 1; } return pdhStatus; } PDH_FUNCTION PdhiGetTimeRangeFromBinaryLog ( IN PPDHI_LOG pLog, IN LPDWORD pdwNumEntries, IN PPDH_TIME_INFO pInfo, IN LPDWORD pdwBufferSize ) /*++ the first entry in the buffer returned is the total time range covered in the file, if there are multiple time blocks in the log file, then subsequent entries will identify each segment in the file. --*/ { PDH_STATUS pdhStatus; LONGLONG llStartTime = MAX_TIME_VALUE; LONGLONG llEndTime = MIN_TIME_VALUE; LONGLONG llThisTime = (LONGLONG)0; DWORD dwThisRecord = BINLOG_FIRST_DATA_RECORD; DWORD dwValidEntries = 0; PPDHI_BINARY_LOG_RECORD_HEADER pThisMasterRecord; PPDHI_BINARY_LOG_RECORD_HEADER pThisSubRecord; PPDHI_RAW_COUNTER_ITEM_BLOCK pDataBlock; PPDH_RAW_COUNTER pRawItem; // read the first data record in the log file // note that the record read is not copied to the local buffer // rather the internal buffer is used in "read-only" mode pdhStatus = PdhiReadOneBinLogRecord ( pLog, dwThisRecord, NULL, 0); // to prevent copying the record while (pdhStatus == ERROR_SUCCESS) { // define pointer to the current record pThisMasterRecord = (PPDHI_BINARY_LOG_RECORD_HEADER) pLog->pLastRecordRead; // get timestamp of this record by looking at the first entry in the // record. assert ((pThisMasterRecord->dwType & 0x0000FFFF) == BINLOG_START_WORD); if ((pThisMasterRecord->dwType & BINLOG_TYPE_DATA) == BINLOG_TYPE_DATA) { // only evaluate data records pThisSubRecord = (PPDHI_BINARY_LOG_RECORD_HEADER)((LPBYTE)pThisMasterRecord + sizeof (PDHI_BINARY_LOG_RECORD_HEADER)); switch (pThisSubRecord->dwType) { case BINLOG_TYPE_DATA_SINGLE: pRawItem = (PPDH_RAW_COUNTER)((LPBYTE)pThisSubRecord + sizeof (PDHI_BINARY_LOG_RECORD_HEADER)); llThisTime = MAKELONGLONG( pRawItem->TimeStamp.dwLowDateTime, pRawItem->TimeStamp.dwHighDateTime); break; case BINLOG_TYPE_DATA_MULTI: pDataBlock = (PPDHI_RAW_COUNTER_ITEM_BLOCK)((LPBYTE)pThisSubRecord + sizeof (PDHI_BINARY_LOG_RECORD_HEADER)); llThisTime = MAKELONGLONG( pDataBlock->TimeStamp.dwLowDateTime, pDataBlock->TimeStamp.dwHighDateTime); break; default: // unknown record type assert (FALSE); llThisTime = 0; break; } } else { llThisTime = 0; } if (llThisTime > 0) { if (llThisTime < llStartTime) { llStartTime = llThisTime; } if (llThisTime > llEndTime) { llEndTime = llThisTime; } dwValidEntries++; } else { // no timestamp field so ignore this record. } // read the next record in the file pdhStatus = PdhiReadOneBinLogRecord ( pLog, ++dwThisRecord, NULL, 0); // to prevent copying the record } if (pdhStatus == PDH_END_OF_LOG_FILE) { // clear out any temp values if (llStartTime == MAX_TIME_VALUE) llStartTime = 0; if (llEndTime == MIN_TIME_VALUE) llEndTime = 0; // then the whole file was read so update the args. if (*pdwBufferSize >= sizeof(PDH_TIME_INFO)) { *(LONGLONG *)(&pInfo->StartTime) = llStartTime; *(LONGLONG *)(&pInfo->EndTime) = llEndTime; pInfo->SampleCount = dwValidEntries; *pdwBufferSize = sizeof(PDH_TIME_INFO); *pdwNumEntries = 1; } else { pdhStatus = PDH_MORE_DATA; } pdhStatus = ERROR_SUCCESS; } else { pdhStatus = PDH_ENTRY_NOT_IN_LOG_FILE; } return pdhStatus; } PDH_FUNCTION PdhiReadRawBinaryLogRecord ( IN PPDHI_LOG pLog, IN FILETIME *ftRecord, IN PPDH_RAW_LOG_RECORD pBuffer, IN LPDWORD pdwBufferLength ) { PDH_STATUS pdhStatus = ERROR_SUCCESS; LONGLONG llStartTime; DWORD dwIndex = 0; DWORD dwSizeRequired; DWORD dwLocalRecordLength; // including terminating NULL PPDHI_BINARY_LOG_RECORD_HEADER pThisMasterRecord; llStartTime = *(LONGLONG *)ftRecord; pdhStatus = PdhiGetMatchingBinaryLogRecord ( pLog, &llStartTime, &dwIndex); // copy results from internal log buffer if it'll fit. if (pdhStatus == ERROR_SUCCESS) { if (dwIndex != BINLOG_TYPE_ID_RECORD) { // then record is a Binary log type pThisMasterRecord = (PPDHI_BINARY_LOG_RECORD_HEADER)pLog->pLastRecordRead; dwLocalRecordLength = pThisMasterRecord ? pThisMasterRecord->dwLength : 0; } else { // this is a fixed size dwLocalRecordLength = pLog->dwRecord1Size; } dwSizeRequired = sizeof (PDH_RAW_LOG_RECORD) - sizeof (UCHAR) + dwLocalRecordLength; if (*pdwBufferLength >= dwSizeRequired) { pBuffer->dwRecordType = (DWORD)(LOWORD(pLog->dwLogFormat)); pBuffer->dwItems = dwLocalRecordLength; // copy it if (dwLocalRecordLength > 0) { RtlCopyMemory(&pBuffer->RawBytes[0], pLog->pLastRecordRead, dwLocalRecordLength); } pBuffer->dwStructureSize = dwSizeRequired; } else { pdhStatus = PDH_MORE_DATA; } *pdwBufferLength = dwSizeRequired; } return pdhStatus; } PDH_FUNCTION PdhiListHeaderFromBinaryLog ( IN PPDHI_LOG pLogFile, IN LPVOID pBufferArg, IN LPDWORD pcchBufferSize, IN BOOL bUnicodeDest ) { LPVOID pTempBuffer = NULL; LPVOID pOldBuffer; DWORD dwTempBufferSize; PDH_STATUS pdhStatus = ERROR_SUCCESS; // read the header record and enum the machine name from the entries if (pLogFile->dwMaxRecordSize == 0) { // no size is defined so start with 64K pLogFile->dwMaxRecordSize = 0x010000; } dwTempBufferSize = pLogFile->dwMaxRecordSize; pTempBuffer = G_ALLOC (dwTempBufferSize); assert (pTempBuffer != NULL); if (pTempBuffer == NULL) { assert (GetLastError() == ERROR_SUCCESS); return PDH_MEMORY_ALLOCATION_FAILURE; } // read in the catalog record while ((pdhStatus = PdhiReadOneBinLogRecord (pLogFile, BINLOG_HEADER_RECORD, pTempBuffer, dwTempBufferSize)) != ERROR_SUCCESS) { if (pdhStatus == PDH_MORE_DATA) { // read the 1st WORD to see if this is a valid record if (*(WORD *)pTempBuffer == BINLOG_START_WORD) { // it's a valid record so read the 2nd DWORD to get the // record size; dwTempBufferSize = ((DWORD *)pTempBuffer)[1]; if (dwTempBufferSize < pLogFile->dwMaxRecordSize) { // then something is bogus so return an error pdhStatus = PDH_ENTRY_NOT_IN_LOG_FILE; break; // out of while loop } else { pLogFile->dwMaxRecordSize = dwTempBufferSize; } } else { // we're lost in this file pdhStatus = PDH_ENTRY_NOT_IN_LOG_FILE; break; // out of while loop } // realloc a new buffer pOldBuffer = pTempBuffer; pTempBuffer = G_REALLOC (pOldBuffer, dwTempBufferSize); if (pTempBuffer == NULL) { // return memory error G_FREE(pOldBuffer); assert (GetLastError() == ERROR_SUCCESS); pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; break; } } else { // some other error was returned so // return error from read function break; } } if (pdhStatus == ERROR_SUCCESS) { // walk down list and copy strings to msz buffer PPDHI_LOG_COUNTER_PATH pPath; DWORD dwBytesProcessed; LONG nItemCount = 0; LPBYTE pFirstChar; PDH_COUNTER_PATH_ELEMENTS_W pdhPathElem; WCHAR szPathString[1024]; DWORD dwRecordLength; DWORD dwBufferUsed = 0; DWORD dwNewBuffer = 0; // we can assume the record was read successfully so read in the // machine names dwRecordLength = ((PPDHI_BINARY_LOG_RECORD_HEADER)pTempBuffer)->dwLength; pPath = (PPDHI_LOG_COUNTER_PATH) ((LPBYTE)pTempBuffer + sizeof (PDHI_BINARY_LOG_HEADER_RECORD)); dwBytesProcessed = sizeof(PDHI_BINARY_LOG_HEADER_RECORD); while (dwBytesProcessed < dwRecordLength) { if (pPath->lMachineNameOffset >= 0L) { // then there's a machine name in this record so get // it's size memset (&pdhPathElem, 0, sizeof(pdhPathElem)); pFirstChar = (LPBYTE)&pPath->Buffer[0]; if (pPath->lMachineNameOffset >= 0) { pdhPathElem.szMachineName = (LPWSTR)((LPBYTE)pFirstChar + pPath->lMachineNameOffset); } if (pPath->lObjectNameOffset >= 0) { pdhPathElem.szObjectName = (LPWSTR)((LPBYTE)pFirstChar + pPath->lObjectNameOffset); } if (pPath->lInstanceOffset >= 0) { pdhPathElem.szInstanceName = (LPWSTR)((LPBYTE)pFirstChar + pPath->lInstanceOffset); } if (pPath->lParentOffset >= 0) { pdhPathElem.szParentInstance = (LPWSTR)((LPBYTE)pFirstChar + pPath->lParentOffset); } if (pPath->dwIndex == 0) { // don't display #0 in path pdhPathElem.dwInstanceIndex = (DWORD)-1; } else { pdhPathElem.dwInstanceIndex = pPath->dwIndex; } if (pPath->lCounterOffset >= 0) { pdhPathElem.szCounterName = (LPWSTR)((LPBYTE)pFirstChar + pPath->lCounterOffset); } dwNewBuffer = sizeof (szPathString) / sizeof(szPathString[0]); pdhStatus = PdhMakeCounterPathW ( &pdhPathElem, szPathString, &dwNewBuffer, 0); if (pdhStatus == ERROR_SUCCESS) { if (pBufferArg != NULL) { // copy string to the buffer if ((dwBufferUsed + dwNewBuffer) < *pcchBufferSize) { dwNewBuffer = AddUniqueWideStringToMultiSz ( (LPVOID)pBufferArg, szPathString, bUnicodeDest); } else { // this one won't fit, so set the status pdhStatus = PDH_MORE_DATA; // and update the size required to return // add in size of the delimiter dwNewBuffer++; // update the size of the string required dwNewBuffer = dwBufferUsed + dwNewBuffer; nItemCount++; } if (dwNewBuffer > 0) { // string was added so update size used. dwBufferUsed = dwNewBuffer; nItemCount++; } } else { // add in size of the delimiter dwNewBuffer++; // add size of this string to the size required dwBufferUsed += dwNewBuffer; nItemCount++; } } // else ignore this entry } // get next path entry from log file record dwBytesProcessed += pPath->dwLength; pPath = (PPDHI_LOG_COUNTER_PATH) ((LPBYTE)pPath + pPath->dwLength); } if ((nItemCount > 0) && (pdhStatus != PDH_INSUFFICIENT_BUFFER) && (pdhStatus != PDH_MORE_DATA)) { // then the routine was successful. Errors that occurred // while scanning will be ignored as long as at least // one entry was successfully read pdhStatus = ERROR_SUCCESS; } if (pBufferArg == NULL) { // add in size of MSZ null; // (AddUnique... already includes this in the return value dwBufferUsed++; } // update the buffer used or required. *pcchBufferSize = dwBufferUsed; } if (pTempBuffer != NULL) G_FREE (pTempBuffer); return pdhStatus; } PDH_FUNCTION PdhiGetCounterArrayFromBinaryLog ( IN PPDHI_LOG pLog, IN DWORD dwIndex, IN PPDHI_COUNTER pCounter, IN OUT PPDHI_RAW_COUNTER_ITEM_BLOCK *ppValue ) { PDH_STATUS pdhStatus; DWORD dwDataItemIndex; PPDHI_BINARY_LOG_RECORD_HEADER pThisMasterRecord; PPDHI_BINARY_LOG_RECORD_HEADER pThisSubRecord; PPDHI_RAW_COUNTER_ITEM_BLOCK pDataBlock; PPDHI_RAW_COUNTER_ITEM_BLOCK pNewArrayHeader; // allocate a new array for // update counter's Current counter array contents // read the first data record in the log file // note that the record read is not copied to the local buffer // rather the internal buffer is used in "read-only" mode pdhStatus = PdhiReadOneBinLogRecord ( pLog, dwIndex, NULL, 0); // to prevent copying the record if (pdhStatus == ERROR_SUCCESS) { // define pointer to the current record pThisMasterRecord = (PPDHI_BINARY_LOG_RECORD_HEADER) pLog->pLastRecordRead; // get timestamp of this record by looking at the first entry in the // record. if (pThisMasterRecord->dwType != BINLOG_TYPE_DATA) return PDH_NO_MORE_DATA; pThisSubRecord = PdhiGetSubRecord ( pThisMasterRecord, pCounter->plCounterInfo.dwCounterId); if (pThisSubRecord != NULL) { switch (pThisSubRecord->dwType) { case BINLOG_TYPE_DATA_SINGLE: // return data as one instance // for now this isn't supported as it won't be hit. // break; case BINLOG_TYPE_DATA_MULTI: // cast pointer to this part of the data record pDataBlock = (PPDHI_RAW_COUNTER_ITEM_BLOCK)((LPBYTE)pThisSubRecord + sizeof (PDHI_BINARY_LOG_RECORD_HEADER)); // allocate a new buffer for the data pNewArrayHeader = (PPDHI_RAW_COUNTER_ITEM_BLOCK) G_ALLOC (pDataBlock->dwLength); if (pNewArrayHeader != NULL) { // copy the log record to the local buffer RtlCopyMemory(pNewArrayHeader, pDataBlock, pDataBlock->dwLength); // convert offsets to pointers for (dwDataItemIndex = 0; dwDataItemIndex < pNewArrayHeader->dwItemCount; dwDataItemIndex++) { // add in the address of the base of the structure // to the offset stored in the field pNewArrayHeader->pItemArray[dwDataItemIndex].szName = pNewArrayHeader->pItemArray[dwDataItemIndex].szName; } // clear any old buffers if (pCounter->pThisRawItemList != NULL) { G_FREE(pCounter->pThisRawItemList); pCounter->pThisRawItemList = NULL; } pCounter->pThisRawItemList = pNewArrayHeader; *ppValue = pNewArrayHeader; } else { pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; } break; default: pdhStatus = PDH_LOG_TYPE_NOT_FOUND; break; } } else { // entry not found in record pdhStatus = PDH_ENTRY_NOT_IN_LOG_FILE; } } else { // no more records in log file pdhStatus = PDH_NO_MORE_DATA; } return pdhStatus; }