551 lines
13 KiB
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;
|
|
}
|