windows-nt/Source/XPSP1/NT/sdktools/pdh/pdhlog/log_bin.c

3835 lines
160 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (C) 1996-1999 Microsoft Corporation
Module Name:
log_bin.c
Abstract:
<abstract>
--*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <pdh.h>
//#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;
}