|
|
/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
lookmon.c
Abstract:
This module contains the NT/Win32 Lookaside List Monitor
Author:
David N. Cutler (davec) 8-Jun-1996
Revision History:
--*/
#include "perfmtrp.h"
#include <search.h>
#include <malloc.h>
#include <limits.h>
#include <stdlib.h>
//
// Define lookaside query information buffer size and buffers.
//
#define BUFFER_SIZE (64 * 1024 / sizeof(ULONG))
ULONG LargeBuffer1[BUFFER_SIZE]; ULONG LargeBuffer2[BUFFER_SIZE];
//
// Define lookaside output structure and lookaside output information buffer.
//
typedef struct _LOOKASIDE_OUTPUT { USHORT CurrentDepth; USHORT MaximumDepth; ULONG Allocates; ULONG AllocateRate; ULONG AllocateHits; ULONG AllocateMisses; ULONG Frees; ULONG FreeRate; ULONG Type; ULONG Tag; ULONG Size; LOGICAL Changed; } LOOKASIDE_OUTPUT, *PLOOKASIDE_OUTPUT;
LOOKASIDE_OUTPUT OutputBuffer[1000];
//
// Define sort types and default sort type.
//
#define TOTAL_ALLOCATES 0
#define ALLOCATE_HITS 1
#define ALLOCATE_MISSES 2
#define CURRENT_DEPTH 3
#define MAXIMUM_DEPTH 4
#define RATE 5
#define TAG 6
ULONG SortBy = TAG;
//
// Define pool types to include and default pool type.
//
#define NONPAGED 0
#define PAGED 1
#define BOTH 2
UCHAR *PoolType[] = { "Nonp", "Page" };
ULONG DisplayType = BOTH;
//
// Define miscellaneous values.
//
ULONG DelayTimeMsec = 5000; ULONG NumberOfInputRecords; INPUT_RECORD InputRecord; HANDLE InputHandle; HANDLE OutputHandle; DWORD OriginalInputMode; WORD NormalAttribute; WORD HighlightAttribute; ULONG NumberOfCols; ULONG NumberOfRows; SIZE_T FirstDetailLine = 0; CONSOLE_SCREEN_BUFFER_INFO OriginalConsoleInfo; ULONG NoHighlight;
//
// Define filter structure and filter data.
//
#define MAX_FILTER 64
typedef struct _FILTER { union { UCHAR Tag[4]; ULONG TagUlong; }; BOOLEAN Exclude; } FILTER, *PFILTER;
FILTER Filter[MAX_FILTER]; ULONG FilterCount = 0;
VOID ShowHelpPopup( VOID );
int __cdecl ulcomp( const void *e1, const void *e2 );
int __cdecl ulcomp( const void *E1, const void *E2 )
/*++
Routine Description:
This function compares two lookaside entries and returns the comparison value based on the comparison type.
Arguments:
E1 - Supplies a pointer to a lookaside output entry.
E2 - Supplies a pointer to a lookaside output entry.
Return Value:
A negative value is returned if the first lookaside entry compares less than the second lookaside entry. A zero value is returned if the two lookaside entries compare equal. A positive nonzero value is returned if the first lookaside entry is greater than the second lookaside entry.
--*/
{
PUCHAR C1; PUCHAR C2; PLOOKASIDE_OUTPUT L1 = (PLOOKASIDE_OUTPUT)E1; PLOOKASIDE_OUTPUT L2 = (PLOOKASIDE_OUTPUT)E2; LONG U1;
C1 = (PUCHAR)&L1->Tag; C2 = (PUCHAR)&L2->Tag; switch (SortBy) {
//
// Sort by number of allocations in descending order.
//
case TOTAL_ALLOCATES: return L2->Allocates - L1->Allocates; break;
//
// Sort by number of allocate hits in descending order.
//
case ALLOCATE_HITS: return L2->AllocateHits - L1->AllocateHits; break;
//
// Sort by number of allocate misses in descending order.
//
case ALLOCATE_MISSES: return L2->AllocateMisses - L1->AllocateMisses; break;
//
// Sort by current depth in descending order.
//
case CURRENT_DEPTH: return L2->CurrentDepth - L1->CurrentDepth; break;
//
// Sort by maximum depth in descending order.
//
case MAXIMUM_DEPTH: return L2->MaximumDepth - L1->MaximumDepth; break;
//
// Sort by allocation rate in descending order.
//
case RATE: return L2->AllocateRate - L1->AllocateRate; break;
//
// Sort by tag, type, and size.
//
case TAG: U1 = *C1++ - *C2++; if (U1 == 0) { U1 = *C1++ - *C2++; if (U1 == 0) { U1 = *C1++ - *C2++; if (U1 == 0) { U1 = *C1 - *C2; if (U1 == 0) { U1 = L1->Type - L2->Type; if (U1 == 0) { U1 = L1->Size - L2->Size; } } } } }
return U1; break; }
return 0; }
LOGICAL CheckSingleFilter ( PUCHAR Tag, PUCHAR Filter )
{ UCHAR Fc; ULONG Index; UCHAR Tc;
//
// Check if tag matches filter.
//
for (Index = 0; Index < 4; Index += 1) { Fc = *Filter++; Tc = *Tag++; if (Fc == '*') { return TRUE;
} else if (Fc == '?') { continue;
} else if (Fc != Tc) { return FALSE; } }
return TRUE; }
LOGICAL CheckFilters ( PUCHAR Tag )
{
ULONG Index; LOGICAL Pass;
//
// If there are no filters, then all tags pass. Otherwise, tags pass or
// do not pass based on whether they are included or excluded.
//
Pass = TRUE; if (FilterCount != 0) {
//
// If the first filter excludes tags, then any tag not explicitly
// specified passes. If the first filter includes tags, then any
// tag not explicitly specified fails.
//
Pass = Filter[0].Exclude; for (Index = 0; Index < FilterCount; Index += 1) { if (CheckSingleFilter(Tag, (PUCHAR)&Filter[Index].Tag) != FALSE) { Pass = !Filter[Index].Exclude; break; } } }
return Pass; }
VOID AddFilter ( BOOLEAN Exclude, PCHAR FilterString )
{
PFILTER f; ULONG i; PCHAR p;
if (FilterCount == MAX_FILTER) { printf("Too many filters specified. Limit is %d\n", MAX_FILTER); return; }
f = &Filter[FilterCount]; p = f->Tag;
for (i = 0; i < 4; i++) { if (*FilterString == 0) { break; }
*p++ = *FilterString++; }
for (; i < 4; i++) { *p++ = ' '; }
f->Exclude = Exclude; FilterCount += 1; return; }
VOID ParseArgs ( int argc, char *argv[] )
/*++
Routine Description:
This function parses the input arguments and sets global state variables.
Arguments:
argc - Supplies the number of argument strings.
argv - Supplies a pointer to an array of pointers to argument strings.
Return Value:
None.
--*/
{
char *p; BOOLEAN exclude;
argc -= 1; argv += 1; while (argc-- > 0) { p = *argv++; if (*p == '-' || *p == '/') { p++; exclude = TRUE; switch (tolower(*p)) { case 'i': exclude = FALSE; case 'x': p++; if (strlen(p) == 0) { printf("missing filter string\n"); ExitProcess(1);
} else if (strlen(p) > sizeof(ULONG)) { printf("filter string too long: %s\n", p); ExitProcess(1); }
AddFilter(exclude, p); break;
default: printf("unknown switch: %s\n", p); ExitProcess(2); }
} else { printf("unknown switch: %s\n", p); ExitProcess(2); } }
return; }
LOGICAL WriteConsoleLine( HANDLE OutputHandle, WORD LineNumber, LPSTR Text, LOGICAL Highlight )
{
COORD WriteCoord; DWORD NumberWritten; DWORD TextLength;
WriteCoord.X = 0; WriteCoord.Y = LineNumber; if (!FillConsoleOutputCharacter(OutputHandle, ' ', NumberOfCols, WriteCoord, &NumberWritten)) { return FALSE; }
if (!FillConsoleOutputAttribute(OutputHandle, (WORD)((Highlight && !NoHighlight) ? HighlightAttribute : NormalAttribute), NumberOfCols, WriteCoord, &NumberWritten)) { return FALSE; }
if (Text == NULL || (TextLength = strlen(Text)) == 0) { return TRUE;
} else { return WriteConsoleOutputCharacter(OutputHandle, Text, TextLength, WriteCoord, &NumberWritten); } }
int __cdecl main( int argc, char *argv[] )
/*++
Routine Description:
This function is the main program entry.
Arguments:
argc - Supplies the number of argument strings.
argv - Supplies a pointer to an array of pointers to argument strings.
Return Value:
Final execution status.
--*/
{
SIZE_T ActiveNumber; CHAR Buffer[512]; SYSTEM_BASIC_INFORMATION BasicInfo; PULONG CurrentBuffer; ULONG GeneralNonpagedTotal; ULONG GeneralPagedTotal; SIZE_T Index; BOOLEAN Interactive; ULONG Length; ULONG LinesInHeader; PSYSTEM_LOOKASIDE_INFORMATION LookasideNew; PSYSTEM_LOOKASIDE_INFORMATION LookasideOld; HANDLE OriginalOutputHandle; PLOOKASIDE_OUTPUT Output; SYSTEM_PERFORMANCE_INFORMATION PerfInfo; ULONG PoolNonpagedTotal; ULONG PoolPagedTotal; PULONG PreviousBuffer; NTSTATUS Status; PULONG TempBuffer; BOOLEAN DoHelp; BOOLEAN DoQuit; UCHAR LastKey; LONG ScrollDelta; WORD DisplayLine; UCHAR T1; UCHAR T2; UCHAR T3; UCHAR T4;
//
// Parse command line arguments.
//
DoHelp = FALSE; DoQuit = FALSE; Interactive = TRUE; ParseArgs(argc, argv);
//
// Get input and output handles.
//
InputHandle = GetStdHandle(STD_INPUT_HANDLE); OriginalOutputHandle = GetStdHandle(STD_OUTPUT_HANDLE); if (InputHandle == NULL || OriginalOutputHandle == NULL || !GetConsoleMode(InputHandle, &OriginalInputMode)) { Interactive = FALSE;
} else { OutputHandle = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
if (OutputHandle == NULL || !GetConsoleScreenBufferInfo(OriginalOutputHandle, &OriginalConsoleInfo) || !SetConsoleScreenBufferSize(OutputHandle, OriginalConsoleInfo.dwSize) || !SetConsoleActiveScreenBuffer(OutputHandle) || !SetConsoleMode(InputHandle, 0)) { if (OutputHandle != NULL) { CloseHandle(OutputHandle); OutputHandle = NULL; }
Interactive = FALSE;
} else { NormalAttribute = 0x1F; HighlightAttribute = 0x71; NumberOfCols = OriginalConsoleInfo.dwSize.X; NumberOfRows = OriginalConsoleInfo.dwSize.Y; } }
NtQuerySystemInformation(SystemBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
//
// If the priority class on the current process is normal, then raise
// the priority class to high.
//
if (GetPriorityClass(GetCurrentProcess()) == NORMAL_PRIORITY_CLASS) { SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); }
//
// Continuously display the lookaside information until an exit signal
// is received.
//
CurrentBuffer = &LargeBuffer1[0]; PreviousBuffer = &LargeBuffer2[0]; while(TRUE) { Status = NtQuerySystemInformation(SystemPerformanceInformation, &PerfInfo, sizeof(PerfInfo), NULL);
if (!NT_SUCCESS(Status)) { printf("Query performance information failed %lx\n", Status); break; }
//
// Query system lookaside information.
//
Status = NtQuerySystemInformation(SystemLookasideInformation, CurrentBuffer, BUFFER_SIZE, &Length);
if (!NT_SUCCESS(Status)) { printf("Query lookaside information failed %lx\n", Status); break; }
//
// Compute total memory allocated to paged and nonpaged lookaside
// lists.
//
Length /= sizeof(SYSTEM_LOOKASIDE_INFORMATION); LookasideNew = (PSYSTEM_LOOKASIDE_INFORMATION)CurrentBuffer; GeneralNonpagedTotal = 0; GeneralPagedTotal = 0; PoolNonpagedTotal = 0; PoolPagedTotal = 0; for (Index = 0; Index < Length; Index += 1) { if ((LookasideNew->Tag == 'looP') || (LookasideNew->Tag == 'LooP')) { if (LookasideNew->Type == NONPAGED) { PoolNonpagedTotal += (LookasideNew->CurrentDepth * LookasideNew->Size);
} else { PoolPagedTotal += (LookasideNew->CurrentDepth * LookasideNew->Size); }
} else { if (LookasideNew->Type == NONPAGED) { GeneralNonpagedTotal += (LookasideNew->CurrentDepth * LookasideNew->Size);
} else { GeneralPagedTotal += (LookasideNew->CurrentDepth * LookasideNew->Size); } }
LookasideNew += 1; }
//
// Output total memory and available memory in kbytes.
//
DisplayLine = 0; sprintf(Buffer, " Total Memory: %ldkb Available Memory: %ldkb", BasicInfo.NumberOfPhysicalPages * (BasicInfo.PageSize / 1024), PerfInfo.AvailablePages * (BasicInfo.PageSize / 1024));
WriteConsoleLine(OutputHandle, DisplayLine++, Buffer, FALSE);
//
// Output total memory reserved for nonpaged and paged pool.
//
sprintf(Buffer, " Pool Memory - Nonpaged: %ldkb Paged: %ldkb", PerfInfo.NonPagedPoolPages * (BasicInfo.PageSize / 1024), PerfInfo.PagedPoolPages * (BasicInfo.PageSize / 1024));
WriteConsoleLine(OutputHandle, DisplayLine++, Buffer, FALSE);
//
// Output total memory allocated for nonpaged and paged lookaside
// lists.
//
sprintf(Buffer, " Pool Lookaside - Nonpaged: %ldkb Paged: %ldkb", PoolNonpagedTotal / 1024, PoolPagedTotal / 1024);
WriteConsoleLine(OutputHandle, DisplayLine++, Buffer, FALSE);
sprintf(Buffer, " General Lookaside - Nonpaged: %ldkb Paged: %ldkb", GeneralNonpagedTotal / 1024, GeneralPagedTotal / 1024);
WriteConsoleLine(OutputHandle, DisplayLine++, Buffer, FALSE);
//
// Output report headings.
//
WriteConsoleLine(OutputHandle, DisplayLine++, " Tag Type Size CurDp MaxDp Allocates Rate Frees Rate A-Hits A-Misses", FALSE);
WriteConsoleLine(OutputHandle, DisplayLine++, NULL, FALSE);
//
// Extract the specified lookaside information.
//
LinesInHeader = DisplayLine; LookasideNew = (PSYSTEM_LOOKASIDE_INFORMATION)CurrentBuffer; LookasideOld = (PSYSTEM_LOOKASIDE_INFORMATION)PreviousBuffer; Output = &OutputBuffer[0]; for (Index = 0; Index < Length; Index += 1) {
//
// Check if the tag should be extracted.
//
if (!CheckFilters((PUCHAR)&LookasideNew[Index].Tag)) { continue; }
//
// Check if the lookaside information should be extracted.
//
if ((DisplayType == BOTH) || ((LookasideNew[Index].Type == 0) && (DisplayType == NONPAGED)) || ((LookasideNew[Index].Type != 0) && (DisplayType == PAGED))) { Output->CurrentDepth = LookasideNew[Index].CurrentDepth; Output->MaximumDepth = LookasideNew[Index].MaximumDepth; Output->Allocates = LookasideNew[Index].TotalAllocates; Output->AllocateRate = Output->Allocates - LookasideNew[Index].AllocateMisses; if (Output->Allocates != 0) { Output->AllocateRate = (Output->AllocateRate * 100) / Output->Allocates; }
Output->Frees = LookasideNew[Index].TotalFrees; Output->FreeRate = Output->Frees - LookasideNew[Index].FreeMisses; if (Output->Frees != 0) { Output->FreeRate = (Output->FreeRate * 100) / Output->Frees; }
Output->Tag = LookasideNew[Index].Tag; Output->Type = LookasideNew[Index].Type; Output->Size = LookasideNew[Index].Size; if (LookasideNew[Index].Tag == LookasideOld[Index].Tag) { Output->Changed = LookasideNew[Index].CurrentDepth != LookasideOld[Index].CurrentDepth;
Output->AllocateMisses = LookasideNew[Index].AllocateMisses - LookasideOld[Index].AllocateMisses;
Output->AllocateHits = LookasideNew[Index].TotalAllocates - LookasideOld[Index].TotalAllocates - Output->AllocateMisses;
} else { Output->Changed = FALSE; Output->AllocateHits = 0; Output->AllocateMisses = 0; }
Output += 1; } }
//
// Sort the extracted lookaside information.
//
ActiveNumber = Output - &OutputBuffer[0]; qsort((void *)&OutputBuffer, (size_t)ActiveNumber, (size_t)sizeof(LOOKASIDE_OUTPUT), ulcomp);
//
// Display the selected information.
//
for (Index = FirstDetailLine; Index < ActiveNumber; Index += 1) { if (DisplayLine >= NumberOfRows) { break; }
//
// Check to make sure the tag is displayable.
//
if ((OutputBuffer[Index].Tag == 0) || (OutputBuffer[Index].Tag == ' ')) { OutputBuffer[Index].Tag = 'nknU'; }
T1 = (UCHAR)(OutputBuffer[Index].Tag & 0xff); T2 = (UCHAR)((OutputBuffer[Index].Tag >> 8) & 0xff); T3 = (UCHAR)((OutputBuffer[Index].Tag >> 16) & 0xff); T4 = (UCHAR)((OutputBuffer[Index].Tag >> 24) & 0xff); if (T1 == 0) { T1 = ' '; }
if (T2 == 0) { T2 = ' '; }
if (T3 == 0) { T3 = ' '; }
if (T4 == 0) { T4 = ' '; }
if ((!isalpha(T1) && (T1 != ' ')) || (!isalpha(T2) && (T2 != ' ')) || (!isalpha(T3) && (T3 != ' ')) || (!isalpha(T4) && (T4 != ' '))) {
OutputBuffer[Index].Tag = 'nknU'; }
sprintf(Buffer, " %c%c%c%c %4s %4ld %5ld %5ld %9ld %3ld%% %9ld %3ld%% %6ld %6ld", T1, T2, T3, T4, PoolType[OutputBuffer[Index].Type], OutputBuffer[Index].Size, OutputBuffer[Index].CurrentDepth, OutputBuffer[Index].MaximumDepth, OutputBuffer[Index].Allocates, OutputBuffer[Index].AllocateRate, OutputBuffer[Index].Frees, OutputBuffer[Index].FreeRate, OutputBuffer[Index].AllocateHits, OutputBuffer[Index].AllocateMisses);
WriteConsoleLine(OutputHandle, DisplayLine++, Buffer, OutputBuffer[Index].Changed); }
//
// If the entire screen is not filled by the selected information,
// then fill the rest of the screen with blank lines.
//
while (DisplayLine < NumberOfRows) { WriteConsoleLine(OutputHandle, DisplayLine++, "", FALSE); }
//
// Wait for input or timeout.
//
TempBuffer = PreviousBuffer; PreviousBuffer = CurrentBuffer; CurrentBuffer = TempBuffer; while (WaitForSingleObject(InputHandle, DelayTimeMsec) == STATUS_WAIT_0) {
//
// Check for input record
//
if (ReadConsoleInput(InputHandle, &InputRecord, 1, &NumberOfInputRecords) && InputRecord.EventType == KEY_EVENT && InputRecord.Event.KeyEvent.bKeyDown) { LastKey = InputRecord.Event.KeyEvent.uChar.AsciiChar; if (LastKey<' ') { ScrollDelta = 0; if (LastKey == 'C'-'A' + 1) { DoQuit = TRUE;
} else switch (InputRecord.Event.KeyEvent.wVirtualKeyCode) { case VK_ESCAPE: DoQuit = TRUE; break;
case VK_PRIOR: ScrollDelta = -(LONG)(InputRecord.Event.KeyEvent.wRepeatCount * NumberOfRows); break;
case VK_NEXT: ScrollDelta = InputRecord.Event.KeyEvent.wRepeatCount * NumberOfRows; break;
case VK_UP: ScrollDelta = -InputRecord.Event.KeyEvent.wRepeatCount; break;
case VK_DOWN: ScrollDelta = InputRecord.Event.KeyEvent.wRepeatCount; break;
case VK_HOME: FirstDetailLine = 0; break;
case VK_END: if (ActiveNumber <= (NumberOfRows - LinesInHeader)) { FirstDetailLine = 0;
} else { FirstDetailLine = ActiveNumber - NumberOfRows + LinesInHeader; }
break; }
if (ScrollDelta != 0) { if (ScrollDelta < 0) { if (FirstDetailLine <= (ULONG)-ScrollDelta) { FirstDetailLine = 0;
} else { FirstDetailLine += ScrollDelta; }
} else { if ((ActiveNumber + LinesInHeader) > NumberOfRows) { FirstDetailLine += ScrollDelta; if (FirstDetailLine >= (ActiveNumber - NumberOfRows + LinesInHeader)) { FirstDetailLine = ActiveNumber - NumberOfRows + LinesInHeader; } } } }
} else { switch (toupper( LastKey )) { case 'Q': DoQuit = TRUE; break;
case 'A': SortBy = TOTAL_ALLOCATES; FirstDetailLine = 0; break;
case 'C': SortBy = CURRENT_DEPTH; FirstDetailLine = 0; break;
case 'H': case '?': DoHelp = TRUE; break;
case 'L': NoHighlight = 1 - NoHighlight; break;
case 'M': SortBy = MAXIMUM_DEPTH; FirstDetailLine = 0; break;
case 'P': DisplayType += 1; if (DisplayType > BOTH) { DisplayType = NONPAGED; } FirstDetailLine = 0; break;
case 'R': SortBy = RATE; FirstDetailLine = 0; break;
case 'S': SortBy = ALLOCATE_MISSES; FirstDetailLine = 0; break;
case 'T': SortBy = TAG; FirstDetailLine = 0; break;
case 'X': SortBy = ALLOCATE_HITS; FirstDetailLine = 0; break; } }
break; } }
if (DoQuit) { break; }
if (DoHelp) { DoHelp = FALSE; ShowHelpPopup(); } }
if (Interactive) { SetConsoleActiveScreenBuffer(OriginalOutputHandle); SetConsoleMode(InputHandle, OriginalInputMode); CloseHandle(OutputHandle); }
ExitProcess(0); return 0; }
VOID ShowHelpPopup( VOID )
{
HANDLE PopupHandle; WORD n;
PopupHandle = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, CONSOLE_TEXTMODE_BUFFER, NULL); if (PopupHandle == NULL) { return; }
SetConsoleActiveScreenBuffer( PopupHandle );
n = 0;
WriteConsoleLine(PopupHandle, n++, NULL, FALSE ); WriteConsoleLine(PopupHandle, n++, " Lookaside Monitor Help", FALSE); WriteConsoleLine(PopupHandle, n++, NULL, FALSE ); WriteConsoleLine(PopupHandle, n++, " columns:", FALSE ); WriteConsoleLine(PopupHandle, n++, " Tag is the four character name of the lookaside list", FALSE); WriteConsoleLine(PopupHandle, n++, " Type is page(d) or nonp(aged)", FALSE); WriteConsoleLine(PopupHandle, n++, " Size is size of the pool allocation in bytes", FALSE); WriteConsoleLine(PopupHandle, n++, " CurDp is the current depth of the lookaside list", FALSE); WriteConsoleLine(PopupHandle, n++, " MaxDp is the maximum depth of the lookaside list", FALSE); WriteConsoleLine(PopupHandle, n++, " Allocates is the total number of allocations from the lookaside list", FALSE); WriteConsoleLine(PopupHandle, n++, " Rate is the percent of allocates that hit in the lookaside list", FALSE); WriteConsoleLine(PopupHandle, n++, " Frees is the total number of frees to the lookaside list", FALSE); WriteConsoleLine(PopupHandle, n++, " Rate is the percent of frees that hit in the lookaside list", FALSE); WriteConsoleLine(PopupHandle, n++, " A-Hits is the number of allocation hits within the display period", FALSE); WriteConsoleLine(PopupHandle, n++, " A-Misses is the number of allocation misses within the display period", FALSE); WriteConsoleLine(PopupHandle, n++, NULL, FALSE); WriteConsoleLine(PopupHandle, n++, " switches:", FALSE); WriteConsoleLine(PopupHandle, n++, " ? or h - gives this help", FALSE); WriteConsoleLine(PopupHandle, n++, " l - toggles highlighting of changed lines on and off", FALSE); WriteConsoleLine(PopupHandle, n++, " q - quits", FALSE); WriteConsoleLine(PopupHandle, n++, " p - toggles default pool display between both, page(d), and nonp(aged)", FALSE); WriteConsoleLine(PopupHandle, n++, NULL, FALSE); WriteConsoleLine(PopupHandle, n++, " sorting switches:", FALSE); WriteConsoleLine(PopupHandle, n++, " a - sort by total allocations", FALSE); WriteConsoleLine(PopupHandle, n++, " c - sort by current depth", FALSE); WriteConsoleLine(PopupHandle, n++, " m - sort by maximum depth", FALSE); WriteConsoleLine(PopupHandle, n++, " r - sort by allocation hit rate", FALSE); WriteConsoleLine(PopupHandle, n++, " s - sort by allocate misses", FALSE); WriteConsoleLine(PopupHandle, n++, " t - sort by tag, type, and size", FALSE); WriteConsoleLine(PopupHandle, n++, " x - sort by allocate hits", FALSE); WriteConsoleLine(PopupHandle, n++, NULL, FALSE); WriteConsoleLine(PopupHandle, n++, " command line switches", FALSE); WriteConsoleLine(PopupHandle, n++, " -i<tag> - list only matching tags", FALSE); WriteConsoleLine(PopupHandle, n++, " -x<tag> - list everything except matching tags", FALSE); WriteConsoleLine(PopupHandle, n++, " <tag> can include * and ?", FALSE); WriteConsoleLine(PopupHandle, n++, NULL, FALSE ); WriteConsoleLine(PopupHandle, n++, NULL, FALSE );
while (TRUE) { if (WaitForSingleObject(InputHandle, DelayTimeMsec) == STATUS_WAIT_0 && ReadConsoleInput(InputHandle, &InputRecord, 1, &NumberOfInputRecords) && InputRecord.EventType == KEY_EVENT && InputRecord.Event.KeyEvent.bKeyDown && InputRecord.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE ) { break; } }
SetConsoleActiveScreenBuffer(OutputHandle); CloseHandle(PopupHandle); return; }
|