Windows-Server-2003/sdktools/memsnap/poolsnap.c

551 lines
13 KiB
C

// poolsnap.c
// this program takes a snapshot of all the kernel pool tags.
// and appends it to the logfile (arg)
// pmon was model for this
/* includes */
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <io.h>
#include <srvfsctl.h>
#include <search.h>
#if !defined(POOLSNAP_INCLUDED)
#define SORTLOG_INCLUDED
#define ANALOG_INCLUDED
#include "analog.c"
#include "sortlog.c"
#endif
#include "tags.c"
//
// declarations
//
int __cdecl ulcomp(const void *e1,const void *e2);
NTSTATUS
QueryPoolTagInformationIterative(
PUCHAR *CurrentBuffer,
size_t *CurrentBufferSize
);
//
// definitions
//
#define NONPAGED 0
#define PAGED 1
#define BOTH 2
//
// Printf format string for pool tag info.
//
#ifdef _WIN64
#define POOLTAG_PRINT_FORMAT " %4s %5s %18I64d %18I64d %16I64d %14I64d %12I64d\n"
#else
#define POOLTAG_PRINT_FORMAT " %4s %5s %9ld %9ld %8ld %7ld %6ld\n"
#endif
// from poolmon
// raw input
PSYSTEM_POOLTAG_INFORMATION PoolInfo;
//
// the amount of memory to increase the size
// of the buffer for NtQuerySystemInformation at each step
//
#define BUFFER_SIZE_STEP 65536
//
// the buffer used for NtQuerySystemInformation
//
PUCHAR CurrentBuffer = NULL;
//
// the size of the buffer used for NtQuerySystemInformation
//
size_t CurrentBufferSize = 0;
//
// formatted output
//
typedef struct _POOLMON_OUT {
union {
UCHAR Tag[4];
ULONG TagUlong;
};
UCHAR NullByte;
BOOL Changed;
ULONG Type;
SIZE_T Allocs[2];
SIZE_T AllocsDiff[2];
SIZE_T Frees[2];
SIZE_T FreesDiff[2];
SIZE_T Allocs_Frees[2];
SIZE_T Used[2];
SIZE_T UsedDiff[2];
SIZE_T Each[2];
} POOLMON_OUT, *PPOOLMON_OUT;
PPOOLMON_OUT OutBuffer;
PPOOLMON_OUT Out;
UCHAR *PoolType[] = {
"Nonp ",
"Paged"};
VOID PoolsnapUsage(VOID)
{
printf("poolsnap [-?] [-t] [<logfile>]\n");
printf("poolsnap logs system pool usage to <logfile>\n");
printf("<logfile> = poolsnap.log by default\n");
printf("-? Gives this help\n");
printf("-a Analyze the log file for leaks.\n");
printf("-t Output extra tagged information\n");
exit(-1);
}
#if !defined(POOLSNAP_INCLUDED)
VOID
AnalyzeLog (
PCHAR FileName,
BOOL HtmlOutput
)
{
char * Args[4];
UNREFERENCED_PARAMETER(HtmlOutput);
Args[0] = "memsnap.exe";
Args[1] = FileName;
Args[2] = "_memsnap_temp_";
Args[3] = NULL;
SortlogMain (3, Args);
Args[0] = "memsnap.exe";
Args[1] = "-d";
Args[2] = "_memsnap_temp_";
Args[3] = NULL;
AnalogMain (3, Args);
DeleteFile ("_memsnap_temp_");
}
#endif
/*
* FUNCTION: Main
*
* ARGUMENTS: See Usage
*
* RETURNS: 0
*
*/
#if defined(POOLSNAP_INCLUDED)
int __cdecl PoolsnapMain (int argc, char* argv[])
#else
int __cdecl main (int argc, char* argv[])
#endif
{
NTSTATUS Status; // status from NT api
FILE* LogFile= NULL; // log file handle
DWORD x= 0; // counter
SIZE_T NumberOfPoolTags;
INT iCmdIndex; // index into argv
BOOL bOutputTags= FALSE; // if true, output standard tags
// get higher priority in case system is bogged down
if ( GetPriorityClass(GetCurrentProcess()) == NORMAL_PRIORITY_CLASS) {
SetPriorityClass(GetCurrentProcess(),HIGH_PRIORITY_CLASS);
}
//
// parse command line arguments
//
for( iCmdIndex=1; iCmdIndex < argc; iCmdIndex++ ) {
CHAR chr;
chr= *argv[iCmdIndex];
if( (chr=='-') || (chr=='/') ) {
chr= argv[iCmdIndex][1];
switch( chr ) {
case '?':
PoolsnapUsage();
break;
case 't': case 'T':
bOutputTags= TRUE;
break;
case 'a':
case 'A':
if (argv[iCmdIndex + 1] != NULL) {
AnalyzeLog (argv[iCmdIndex + 1], FALSE);
}
else {
AnalyzeLog ("poolsnap.log", FALSE);
}
exit (0);
default:
printf("Invalid switch: %s\n",argv[iCmdIndex]);
PoolsnapUsage();
break;
}
}
else {
if( LogFile ) {
printf("Error: more than one file specified: %s\n",argv[iCmdIndex]);
return(0);
}
LogFile= fopen(argv[iCmdIndex],"a");
if( !LogFile ) {
printf("Error: Opening file %s\n",argv[iCmdIndex]);
return(0);
}
}
}
//
// if no file specified, use default name
//
if( !LogFile ) {
if( (LogFile = fopen("poolsnap.log","a")) == NULL ) {
printf("Error: opening file poolsnap.log\n");
return(0);
}
}
//
// print file header once
//
if( _filelength(_fileno(LogFile)) == 0 ) {
fprintf(LogFile," Tag Type Allocs Frees Diff Bytes Per Alloc\n");
}
fprintf(LogFile,"\n");
if( bOutputTags ) {
OutputStdTags(LogFile, "poolsnap" );
}
// grab all pool information
// log line format, fixed column format
Status = QueryPoolTagInformationIterative(
&CurrentBuffer,
&CurrentBufferSize
);
if (! NT_SUCCESS(Status)) {
printf("Failed to query pool tags information (status %08X). \n", Status);
printf("Please check if pool tags are enabled. \n");
return (0);
}
PoolInfo = (PSYSTEM_POOLTAG_INFORMATION)CurrentBuffer;
//
// Allocate the output buffer.
//
OutBuffer = malloc (PoolInfo->Count * sizeof(POOLMON_OUT));
if (OutBuffer == NULL) {
printf ("Error: cannot allocate internal buffer of %p bytes \n",
(PVOID)(PoolInfo->Count * sizeof(POOLMON_OUT)));
return (0);
}
Out = OutBuffer;
if( NT_SUCCESS(Status) ) {
for (x = 0; x < (int)PoolInfo->Count; x++) {
// get pool info from buffer
Out->Type = 0;
// non-paged
if (PoolInfo->TagInfo[x].NonPagedAllocs != 0) {
Out->Allocs[NONPAGED] = PoolInfo->TagInfo[x].NonPagedAllocs;
Out->Frees[NONPAGED] = PoolInfo->TagInfo[x].NonPagedFrees;
Out->Used[NONPAGED] = PoolInfo->TagInfo[x].NonPagedUsed;
Out->Allocs_Frees[NONPAGED] = PoolInfo->TagInfo[x].NonPagedAllocs -
PoolInfo->TagInfo[x].NonPagedFrees;
Out->TagUlong = PoolInfo->TagInfo[x].TagUlong;
Out->Type |= (1 << NONPAGED);
Out->Changed = FALSE;
Out->NullByte = '\0';
Out->Each[NONPAGED] = Out->Used[NONPAGED] /
(Out->Allocs_Frees[NONPAGED]?Out->Allocs_Frees[NONPAGED]:1);
}
// paged
if (PoolInfo->TagInfo[x].PagedAllocs != 0) {
Out->Allocs[PAGED] = PoolInfo->TagInfo[x].PagedAllocs;
Out->Frees[PAGED] = PoolInfo->TagInfo[x].PagedFrees;
Out->Used[PAGED] = PoolInfo->TagInfo[x].PagedUsed;
Out->Allocs_Frees[PAGED] = PoolInfo->TagInfo[x].PagedAllocs -
PoolInfo->TagInfo[x].PagedFrees;
Out->TagUlong = PoolInfo->TagInfo[x].TagUlong;
Out->Type |= (1 << PAGED);
Out->Changed = FALSE;
Out->NullByte = '\0';
Out->Each[PAGED] = Out->Used[PAGED] /
(Out->Allocs_Frees[PAGED]?Out->Allocs_Frees[PAGED]:1);
}
Out += 1;
}
}
else {
fprintf(LogFile, "Query pooltags Failed %lx\n",Status);
fprintf(LogFile, " Be sure to turn on 'enable pool tagging' in gflags and reboot.\n");
if( bOutputTags ) {
fprintf(LogFile, "!Error:Query pooltags failed %lx\n",Status);
fprintf(LogFile, "!Error: Be sure to turn on 'enable pool tagging' in gflags and reboot.\n");
}
// If there is an operator around, wake him up, but keep moving
Beep(1000,350); Beep(500,350); Beep(1000,350);
exit(0);
}
//
// sort by tag value which is big endian
//
NumberOfPoolTags = Out - OutBuffer;
qsort((void *)OutBuffer,
(size_t)NumberOfPoolTags,
(size_t)sizeof(POOLMON_OUT),
ulcomp);
//
// print in file
//
for (x = 0; x < (int)PoolInfo->Count; x++) {
if ((OutBuffer[x].Type & (1 << NONPAGED))) {
fprintf(LogFile,
POOLTAG_PRINT_FORMAT,
OutBuffer[x].Tag,
PoolType[NONPAGED],
OutBuffer[x].Allocs[NONPAGED],
OutBuffer[x].Frees[NONPAGED],
OutBuffer[x].Allocs_Frees[NONPAGED],
OutBuffer[x].Used[NONPAGED],
OutBuffer[x].Each[NONPAGED]);
}
if ((OutBuffer[x].Type & (1 << PAGED))) {
fprintf(LogFile,
POOLTAG_PRINT_FORMAT,
OutBuffer[x].Tag,
PoolType[PAGED],
OutBuffer[x].Allocs[PAGED],
OutBuffer[x].Frees[PAGED],
OutBuffer[x].Allocs_Frees[PAGED],
OutBuffer[x].Used[PAGED],
OutBuffer[x].Each[PAGED]);
}
}
// close file
fclose(LogFile);
return 0;
}
// comparison function for qsort
// Tags are big endian
int __cdecl ulcomp(const void *e1,const void *e2)
{
ULONG u1;
u1 = ((PUCHAR)e1)[0] - ((PUCHAR)e2)[0];
if (u1 != 0) {
return u1;
}
u1 = ((PUCHAR)e1)[1] - ((PUCHAR)e2)[1];
if (u1 != 0) {
return u1;
}
u1 = ((PUCHAR)e1)[2] - ((PUCHAR)e2)[2];
if (u1 != 0) {
return u1;
}
u1 = ((PUCHAR)e1)[3] - ((PUCHAR)e2)[3];
return u1;
}
/*
* FUNCTION:
*
* QueryPoolTagInformationIterative
*
* ARGUMENTS:
*
* CurrentBuffer - a pointer to the buffer currently used for
* NtQuerySystemInformation( SystemPoolTagInformation ).
* It will be allocated if NULL or its size grown
* if necessary.
*
* CurrentBufferSize - a pointer to a variable that holds the current
* size of the buffer.
*
*
* RETURNS:
*
* NTSTATUS returned by NtQuerySystemInformation or
* STATUS_INSUFFICIENT_RESOURCES if the buffer must grow and the
* heap allocation for it fails.
*
*/
NTSTATUS
QueryPoolTagInformationIterative(
PUCHAR *CurrentBuffer,
size_t *CurrentBufferSize
)
{
size_t NewBufferSize;
NTSTATUS ReturnedStatus = STATUS_SUCCESS;
if( CurrentBuffer == NULL || CurrentBufferSize == NULL ) {
return STATUS_INVALID_PARAMETER;
}
if( *CurrentBufferSize == 0 || *CurrentBuffer == NULL ) {
//
// there is no buffer allocated yet
//
NewBufferSize = sizeof( UCHAR ) * BUFFER_SIZE_STEP;
*CurrentBuffer = (PUCHAR) malloc( NewBufferSize );
if( *CurrentBuffer != NULL ) {
*CurrentBufferSize = NewBufferSize;
} else {
//
// insufficient memory
//
ReturnedStatus = STATUS_INSUFFICIENT_RESOURCES;
}
}
//
// iterate by buffer's size
//
while( *CurrentBuffer != NULL ) {
ReturnedStatus = NtQuerySystemInformation (
SystemPoolTagInformation,
*CurrentBuffer,
(ULONG)*CurrentBufferSize,
NULL );
if( ! NT_SUCCESS(ReturnedStatus) ) {
//
// free the current buffer
//
free( *CurrentBuffer );
*CurrentBuffer = NULL;
if (ReturnedStatus == STATUS_INFO_LENGTH_MISMATCH) {
//
// try with a greater buffer size
//
NewBufferSize = *CurrentBufferSize + BUFFER_SIZE_STEP;
*CurrentBuffer = (PUCHAR) malloc( NewBufferSize );
if( *CurrentBuffer != NULL ) {
//
// allocated new buffer
//
*CurrentBufferSize = NewBufferSize;
} else {
//
// insufficient memory
//
ReturnedStatus = STATUS_INSUFFICIENT_RESOURCES;
*CurrentBufferSize = 0;
}
} else {
*CurrentBufferSize = 0;
}
} else {
//
// NtQuerySystemInformation returned success
//
break;
}
}
return ReturnedStatus;
}