Log Type |
Description |
File Extension |
Read/Write? |
PDH_LOG_TYPE_PERFMON |
Log file from Performance Monitor in Windows NT 4.0 |
LOG |
Read-only |
PDH_LOG_TYPE_BINARY |
Log file from System Monitor in Windows 2000 |
BLG |
Read and Write |
PDH_LOG_TYPE_CSV |
Log File in comma-separated value (CSV) format |
CSV |
Read and Write |
PDH_LOG_TYPE_TSV |
Log File in tab-separated value (TSV) format |
TSV |
Read and Write |
Figure 5 DumpLog Highlights
// FUNCTION: enum_perflog_items
//
// This function does the task of enumeration performance objects
// and object items of the given log file. Because the enum functions
// require the machine name when enumerating on a log file, the
// PdhEnumMachines function is used to get a machine name that exists
// in the give log file.
void enum_perflog_items(LPCTSTR lpszLogFile)
{
•
•
•
// enumerate machines in the log file
dwCharsNeeded = MACHINE_LIST_SIZE;
pdhStatus = PdhEnumMachines(lpszLogFile, mszMachines, &dwCharsNeeded);
if (IsErrorSeverity(pdhStatus))
{
print_pdh_error(TEXT("PdhEnumObjects"), pdhStatus);
return;
}
// output multistring enumeration
lpszMachine = mszMachines;
_tprintf(TEXT("%s contains info from the following machine names\n"),
lpszLogFile);
while (*lpszMachine)
{
_tprintf(TEXT("%s\n"), lpszMachine);
lpszMachine = lpszMachine + lstrlen(lpszMachine) + 1; // next string
}
_tprintf(TEXT("\n"));
// typically you would enumerate objects/counters for each
// machine but in this example I only enumerate them for
// the first machine.
lpszMachine = mszMachines;
dwCharsNeeded = OBJECT_LIST_SIZE;
pdhStatus = PdhEnumObjects(lpszLogFile, lpszMachine,
mszObjects, &dwCharsNeeded, PERF_DETAIL_WIZARD, TRUE);
if (IsErrorSeverity(pdhStatus))
{
print_pdh_error(TEXT("PdhEnumObjects"), pdhStatus);
return;
}
// Here's a funky thing to note.
// If lpszLogFile refers to a perfmon-type log, and
// if the buffer is not large enough for object titles,
// this function will succeed in return object indexes
// This is also true for PdhEnumObjectItems.
// iterate through objects and get items for each
lptrObj = mszObjects;
while (*lptrObj)
{
DWORD dwCntrCharsNeeded;
DWORD dwInstCharsNeeded;
LPTSTR lptrItem;
dwCntrCharsNeeded = COUNTER_LIST_SIZE;
dwInstCharsNeeded = INSTANCE_LIST_SIZE;
pdhStatus = PdhEnumObjectItems(lpszLogFile,
lpszMachine, lptrObj, mszCounters,
&dwCntrCharsNeeded, mszInstances,
&dwInstCharsNeeded, PERF_DETAIL_WIZARD,0);
if (IsErrorSeverity(pdhStatus))
{
print_pdh_error(TEXT("PdhEnumObjectItems"), pdhStatus);
break;
}
_tprintf(TEXT("Items for %s\n================\n"), lptrObj);
// enumerate counter items
_tprintf(TEXT("Counters\n"));
lptrItem = mszCounters;
while (*lptrItem)
{
_tprintf(TEXT("\t%s\n"), lptrItem);
lptrItem += lstrlen(lptrItem) + 1; // get next string
}
_tprintf(TEXT("\n"));
// not all objects have instances
// dwInstCharsNeeded will be zero if no instances
if (dwInstCharsNeeded)
{
lptrItem = mszInstances;
_tprintf(TEXT("Instances\n"));
while (*lptrItem)
{
_tprintf(TEXT("\t%s\n"), lptrItem);
lptrItem += lstrlen(lptrItem) + 1; // get next string
}
_tprintf(TEXT("\n\n"));
}
lptrObj += lstrlen(lptrObj) + 1; // get next object
} // while (*lptrObj)
// todo: add code to display the data query range
dwBufferSize = sizeof(PDH_TIME_INFO);
pdhStatus = PdhGetDataSourceTimeRange(lpszLogFile, &dwNumEntries,
&pdhTimeInfo, &dwBufferSize);
if (IsErrorSeverity(pdhStatus))
{
print_pdh_error(TEXT("PdhGetDataSourceTimeRange"), pdhStatus);
return;
}
FileTimeToSystemTime((LPFILETIME)&(pdhTimeInfo.StartTime), &stStart);
FileTimeToSystemTime((LPFILETIME)&(pdhTimeInfo.EndTime), &stEnd);
_tprintf(TEXT("Time range of %s\n"), lpszLogFile);
_tprintf(TEXT("Start Time: %2d/%2d/%4d %2d:%2d:%2d\n"),
stStart.wMonth, stStart.wDay, stStart.wYear,
stStart.wHour, stStart.wMinute, stStart.wSecond);
_tprintf(TEXT(" End Time: %2d/%2d/%4d %2d:%2d:%2d\n"),
stEnd.wMonth, stEnd.wDay, stEnd.wYear,
stEnd.wHour, stEnd.wMinute, stEnd.wSecond);
_tprintf(TEXT("Contains %d data samplings\n"), pdhTimeInfo.SampleCount);
}
// FUNCTION: perf_log_dump
//
// This function dumps the counter data from a log file specified by
// the log file name, the object name, the counter name, and the optional
// instance name. Because the machine name is required for the
// counter path when operating on a log file, PdhEnumMachines is used
// to get a machine name appearing in the performance log.
void perf_log_dump(LPTSTR lpszLogFile, LPTSTR lpszObj,
LPTSTR lpszCntr, LPTSTR lpszInst){
•
•
•
cchMachines = MACHINE_LIST_SIZE;
pdhStatus = PdhEnumMachines(lpszLogFile, szMachines, &cchMachines);
if (IsErrorSeverity(pdhStatus))
{
print_pdh_error(TEXT("PdhEnumMachines"), pdhStatus);
return;
}
// open a query
pdhStatus = PdhOpenQuery(lpszLogFile, 0, &hQuery);
if (IsErrorSeverity(pdhStatus))
{
print_pdh_error(TEXT("PdhOpenQuery"), pdhStatus);
return;
}
// make the counter path
pdhCPE.szMachineName = szMachines; // assume first machine is in
// the path
pdhCPE.szObjectName = lpszObj;
pdhCPE.szInstanceName = lpszInst;
pdhCPE.szParentInstance = NULL;
pdhCPE.dwInstanceIndex = 0;
pdhCPE.szCounterName = lpszCntr;
dwBufferSize = PATH_BUFF_SIZE;
pdhStatus = PdhMakeCounterPath(&pdhCPE, szPath, &dwBufferSize, 0);
if (IsErrorSeverity(pdhStatus))
{
PdhCloseQuery(hQuery);
print_pdh_error(TEXT("PdhMakeCounterPath"), pdhStatus);
return;
}
// add the counter
pdhStatus = PdhAddCounter(hQuery, szPath, 0, &hCounter);
if (IsErrorSeverity(pdhStatus))
{
PdhCloseQuery(hQuery);
print_pdh_error(TEXT("PdhAddCounter"), pdhStatus);
return;
}
dwBufferSize = 0;
ppdhCounterInfo = NULL;
pdhStatus = PdhGetCounterInfo(hCounter, FALSE, &dwBufferSize,
ppdhCounterInfo);
ppdhCounterInfo = malloc(dwBufferSize);
pdhStatus = PdhGetCounterInfo(hCounter, FALSE, &dwBufferSize,
ppdhCounterInfo);
if (IsErrorSeverity(pdhStatus))
{
PdhCloseQuery(hQuery);
print_pdh_error(TEXT("PdhGetCounterInfo"), pdhStatus);
return;
}
_tprintf(TEXT("Log file: %s\n"), lpszLogFile);
_tprintf(TEXT("Counter Path: %s\n"), ppdhCounterInfo->szFullPath);
_tprintf(TEXT("Machine: %s\n"),
ppdhCounterInfo->CounterPath.szMachineName);
_tprintf(TEXT("Counter Data scaled at 10 to the %d power\n\n"),
ppdhCounterInfo->lScale);
free(ppdhCounterInfo);
// iterate through log file by "collecting" the query data
pdhStatus = PdhCollectQueryData(hQuery); // reads first log record
while (pdhStatus == ERROR_SUCCESS)
{
pdhStatus = PdhGetRawCounterValue(hCounter, &dwType, &pdhRawCounter);
if (IsErrorSeverity(pdhStatus))
{
print_pdh_error(TEXT("PdhGetRawCounterValue"), pdhStatus);
}
FileTimeToSystemTime((LPFILETIME)&(pdhRawCounter.TimeStamp), &st);
dwType = PDH_FMT_DOUBLE;
pdhStatus = PdhGetFormattedCounterValue(hCounter, PDH_FMT_DOUBLE,
&dwType, &pdhFmtCounter);
if (IsErrorSeverity(pdhStatus))
{
print_pdh_error(TEXT("PdhGetFormattedCounterValue"), pdhStatus);
}
_tprintf(TEXT("Time Stamp: %02d:%02d:%02d "), st.wHour,
st.wMinute, st.wSecond);
_tprintf(TEXT("Value: %0.4lf\n"), pdhFmtCounter.doubleValue);
pdhStatus = PdhCollectQueryData(hQuery); // reads next log record
} // while (pdhStatus == ERROR_SUCCESS)
PdhCloseQuery(hQuery);
}
Figure 6 CvtLog Excerpts
•
•
•
// FUNCTION: _tmain
//
// The main routine contains the bulk of the algorithm of transferring
// data from one log file and placing it into another performance log.
//
// The procedure for doing so is
// open source log using a query
// add counters to the query
// open the destination log file and refer to the query
// for each data sample in the source log
// read a data sample and write it to the output log
// close both log files.
void __cdecl _tmain(int argc, TCHAR **argv)
{
•
•
•
pdhStatus = PdhOpenQuery(lpszInfile, 0, &hQuery);
if (IsErrorSeverity(pdhStatus))
{
print_pdh_error(TEXT("PdhOpenQuery"), pdhStatus);
return;
}
•
•
•
pdhStatus = PdhBrowseCounters (&BrowseInfo);
if (IsErrorSeverity(pdhStatus))
{
PdhCloseQuery(hQuery);
print_pdh_error(TEXT("PdhBrowseCounters"), pdhStatus);
return;
}
pdhStatus = PdhOpenLog(
lpszOutfile, PDH_LOG_WRITE_ACCESS | PDH_LOG_CREATE_ALWAYS,
&dwOutFileType, hQuery, // links the input file to output file
0, NULL, // no user caption
&hLog);
if (IsErrorSeverity(pdhStatus))
{
PdhCloseQuery(hQuery);
print_pdh_error(TEXT("PdhOpenLog"), pdhStatus);
return;
}
// now do the transfer from the hQuery to the hLog
// this will loop until all datapoint from the
// input log file is exhausted
// Typically, you can call PdhUpdateLog until
// the all of the records of the input log have
// been read. But in my experience PdhUpdateLog
// may fail for the first record or two on some
// perfmon logs. So instead we can get the
// number of data samplings and use a for loop
// Get the time range of the input log data.
// This also retrieves the number sample count
// which is the number of records in the log file
dwBuffSize = sizeof(PDH_TIME_INFO);
pdhStatus = PdhGetDataSourceTimeRange(lpszInfile,
&dwNumEntries, &pdhTimeRange, &dwBuffSize);
// PdhUpdateLog will get the data from the hQuery
// and write it to the output log
for (i=0; i<pdhTimeRange.SampleCount; i++)
pdhStatus = PdhUpdateLog(hLog, NULL);
pdhStatus = PdhUpdateLogFileCatalog(hLog);
if (IsErrorSeverity(pdhStatus))
{
print_pdh_error(TEXT("PdhUpdateLogFileCatalog"), pdhStatus);
}
PdhCloseLog(hLog, 0);
PdhCloseQuery(hQuery);
}
// FUNCTION: pdhBrowseCallback
//
// When the user clicks the Add button on the browse counters dialog
// presented by the PdhBrowseCounters function, this function is called.
// The browse counters dialog fills in the szCounterBuffer buffer (locally
// declared in main) with a path of the counter selected by the user.
// This function has a reference to this buffer, the query handle, and
// other information for counter set bookkeeping by a pointer to a
// structure (PDH_BROWSE_COUNTER_ARGS) I define in the beginning of
// this source.
PDH_STATUS WINAPI pdhBrowseCallback(DWORD dwArg)
{
•
•
•
pdhArgs = (PPDH_BROWSE_COUNTER_ARGS)dwArg;
hQuery = pdhArgs->hQuery;
ahCounters = pdhArgs->ahCounters;
i = pdhArgs->dwIndexCount; // current location in ahCounters array
// lpszCounterPath may be a multistring
// this works for one or many paths passed to this function
lptrPath = pdhArgs->lpszCounterPath;
while(*lptrPath)
{
if (i >= pdhArgs->dwMaxCounters)
{
_tprintf(TEXT("Counter Handle Array Limit (%d) Reached\n"),
pdhArgs->dwMaxCounters);
return ERROR_SUCCESS;
break;
}
_tprintf(TEXT("adding counter -- %s\n"), lptrPath);
pdhStatus = PdhAddCounter(hQuery, lptrPath, 0, &(ahCounters[i]));
if (ERROR_SUCCESS != pdhStatus)
{
print_pdh_error(TEXT("PdhAddCounter"), pdhStatus);
break;
}
lptrPath += lstrlen(lptrPath) + 1;
i++;
}
pdhArgs->dwIndexCount = i; // update new location in ahCounters array
return ERROR_SUCCESS; // means give control back to the UI
}