380 lines
10 KiB
C
380 lines
10 KiB
C
// sortlog.c
|
|
//
|
|
// this program sorts memsnap and poolsnap logs into a more readable form
|
|
// sorts by pid
|
|
// scans the data file one time, inserts record offsets based on PID into linked list
|
|
// then writes data into new file in sorted order
|
|
// determine whether we have a poolsnap or memsnap log - in pid is equivalent
|
|
// to pooltag for our sorting
|
|
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <io.h>
|
|
#include <stdlib.h>
|
|
|
|
// definitions
|
|
#define RECSIZE 1024 // record size (max line size)
|
|
#define MAXTAGSIZE 200 // max length of tag name
|
|
#define PIDSIZE (sizeof(HANDLE) << 1) //Buffer size needed to store hex pid
|
|
|
|
#define DEFAULT_INFILE "memsnap.log"
|
|
#define DEFAULT_OUTFILE "memsort.log"
|
|
|
|
typedef enum _FILE_LOG_TYPES {
|
|
MEMSNAPLOG=0,
|
|
POOLSNAPLOG,
|
|
UNKNOWNLOG
|
|
} FILE_LOG_TYPES;
|
|
|
|
//
|
|
// Linked list of unique PID or PoolTag
|
|
//
|
|
|
|
struct PIDList {
|
|
char PIDItem[PIDSIZE + 1];
|
|
struct RecList* RecordList;
|
|
struct PIDList* Next;
|
|
DWORD Count; // number of items pointed to by RecordList
|
|
};
|
|
|
|
//
|
|
// For each PID or pool tag, we have a linked list of offsets into file of each line
|
|
//
|
|
|
|
struct RecList {
|
|
LONG rOffset;
|
|
struct RecList* Next;
|
|
};
|
|
|
|
// global data
|
|
FILE_LOG_TYPES CurrentFileType= UNKNOWNLOG;
|
|
CHAR szHeader[RECSIZE]; // first line of file
|
|
BOOL bIgnoreTransients= FALSE; // Ignore tags or processes that aren't in every snapshot
|
|
DWORD g_MaxSnapShots= 0; // max snap shots in the file
|
|
|
|
#define INVALIDOFFSET (-2) /* invalid file offset */
|
|
|
|
// prototypes
|
|
VOID ScanFile(FILE *, struct PIDList *);
|
|
VOID WriteFilex(FILE *, FILE *, struct PIDList *);
|
|
|
|
VOID SortlogUsage(VOID)
|
|
{
|
|
|
|
printf("sortlog [-?] [<logfile>] [<outfile>]\n");
|
|
printf("Sorts an outputfile from memsnap.exe/poolsnap.exe in PID/PoolTag order\n");
|
|
printf("-? prints this help\n");
|
|
printf("-i ignore tags or processes that are not in every snapshot\n");
|
|
printf("<logfile> = %s by default\n",DEFAULT_INFILE );
|
|
printf("<outfile> = %s by default\n",DEFAULT_OUTFILE);
|
|
exit(-1);
|
|
}
|
|
|
|
// CheckPointer
|
|
//
|
|
// Make sure it is not NULL. Otherwise print error message and exit.
|
|
//
|
|
|
|
|
|
VOID CheckPointer( PVOID ptr )
|
|
{
|
|
if( ptr == NULL ) {
|
|
printf("Out of memory\n");
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
#include "tags.c"
|
|
|
|
#if defined(SORTLOG_INCLUDED)
|
|
int __cdecl SortlogMain (int argc, char* argv[])
|
|
#else
|
|
int __cdecl main (int argc, char* argv[])
|
|
#endif
|
|
{
|
|
FILE* InFile;
|
|
FILE* OutFile;
|
|
struct PIDList ThePIDList = {0};
|
|
CHAR* pszInFile= NULL; // input filename
|
|
CHAR* pszOutFile= NULL; // output filename
|
|
INT iFileIndex= 0;
|
|
INT iCmdIndex; // index into argv
|
|
|
|
ThePIDList.RecordList = (struct RecList *)LocalAlloc(LPTR, sizeof(struct RecList));
|
|
CheckPointer( ThePIDList.RecordList );
|
|
ThePIDList.RecordList->rOffset= INVALIDOFFSET;
|
|
|
|
//
|
|
// parse command line
|
|
//
|
|
|
|
for( iCmdIndex=1; iCmdIndex<argc; iCmdIndex++ ) {
|
|
CHAR chr;
|
|
|
|
chr= argv[iCmdIndex][0];
|
|
|
|
if( (chr=='-') || (chr=='/') ) {
|
|
chr= argv[iCmdIndex][1];
|
|
switch( chr ) {
|
|
case '?':
|
|
SortlogUsage();
|
|
break;
|
|
case 'i': // ignore all process that weren't running the whole time
|
|
bIgnoreTransients= TRUE;
|
|
break;
|
|
default:
|
|
printf("Invalid switch %s\n",argv[iCmdIndex]);
|
|
SortlogUsage();
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
if( iFileIndex == 0 ) {
|
|
pszInFile= argv[iCmdIndex];
|
|
iFileIndex++;
|
|
}
|
|
else if( iFileIndex == 1 ) {
|
|
pszOutFile= argv[iCmdIndex];
|
|
iFileIndex++;
|
|
}
|
|
else {
|
|
printf("Too many files specified\n");
|
|
SortlogUsage();
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// fill in default filenames if some aren't given
|
|
//
|
|
|
|
switch( iFileIndex ) {
|
|
case 0:
|
|
pszInFile= DEFAULT_INFILE;
|
|
pszOutFile= DEFAULT_OUTFILE;
|
|
break;
|
|
|
|
case 1:
|
|
pszOutFile= DEFAULT_OUTFILE;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// open the files
|
|
//
|
|
|
|
InFile= fopen( pszInFile, "r" );
|
|
if( InFile == NULL ) {
|
|
printf("Error opening input file %s\n",pszInFile);
|
|
return( 0 );
|
|
}
|
|
|
|
OutFile= fopen( pszOutFile, "w" );
|
|
if( OutFile == NULL ) {
|
|
printf("Error opening output file %s\n",pszOutFile);
|
|
return( 0 );
|
|
}
|
|
|
|
//
|
|
// read in the data and set up the list
|
|
//
|
|
|
|
ScanFile(InFile, &ThePIDList);
|
|
|
|
//
|
|
// write the output file
|
|
//
|
|
|
|
WriteFilex(InFile, OutFile, &ThePIDList);
|
|
|
|
// close and exit
|
|
_fcloseall();
|
|
return 0;
|
|
}
|
|
|
|
// read the input file and get the offset to each record in order and put in list
|
|
|
|
VOID ScanFile(FILE *InFile, struct PIDList *ThePIDList)
|
|
{
|
|
char inchar = 0;
|
|
char inBuff[RECSIZE] = {0};
|
|
char PID[PIDSIZE + 1] = {0};
|
|
LONG Offset = 0;
|
|
BOOL Found = FALSE;
|
|
struct PIDList *TmpPIDList;
|
|
struct RecList *TmpRecordList;
|
|
INT iGarb = 0;
|
|
|
|
/* initialize temp list pointer */
|
|
TmpPIDList = ThePIDList;
|
|
|
|
/* read to the first newline, check for EOF */
|
|
/* determine whether it is a poolsnap or memsnap log */
|
|
if ((fscanf(InFile, "%[^\n]", &szHeader)) == EOF)
|
|
return;
|
|
if (strncmp("Process ID", szHeader, 10) == 0)
|
|
CurrentFileType= MEMSNAPLOG;
|
|
if (strncmp(" Tag Type", szHeader, 10) == 0)
|
|
CurrentFileType= POOLSNAPLOG;
|
|
|
|
if( CurrentFileType == UNKNOWNLOG )
|
|
{
|
|
printf("unrecognized log file\n");
|
|
return;
|
|
}
|
|
|
|
inBuff[0] = 0;
|
|
|
|
/* read to the end of file */
|
|
while (!feof(InFile)) {
|
|
/* record the offset */
|
|
Offset = ftell(InFile);
|
|
|
|
/* if first char == newline, skip to next */
|
|
if ((fscanf(InFile, "%[^\n]", inBuff)) == EOF) {
|
|
return;
|
|
}
|
|
/* read past delimiter */
|
|
inchar = (char)fgetc(InFile);
|
|
// skip if its an empty line
|
|
if (strlen(inBuff) == 0) {
|
|
continue;
|
|
}
|
|
//
|
|
// Handle tags if this is a tagged line
|
|
//
|
|
|
|
if( inBuff[0] == '!' )
|
|
{
|
|
ProcessTag( inBuff+1 );
|
|
continue;
|
|
}
|
|
|
|
|
|
if (3 == sscanf(inBuff, "%2u\\%2u\\%4u", &iGarb, &iGarb, &iGarb)){
|
|
continue;
|
|
}
|
|
|
|
/* read the PID */
|
|
strncpy(PID,inBuff,PIDSIZE);
|
|
|
|
// scan list of PIDS, find matching, if no matching, make new one
|
|
// keep this list sorted
|
|
|
|
TmpPIDList = ThePIDList; /* point to top of list */
|
|
Found= FALSE;
|
|
while( TmpPIDList->Next != 0 ) {
|
|
int iComp;
|
|
|
|
iComp= strcmp( PID, TmpPIDList->PIDItem);
|
|
if( iComp == 0 ) { // found
|
|
Found= TRUE;
|
|
break;
|
|
} else { // not found
|
|
if( iComp < 0 ) { // exit if we have gone far enough
|
|
break;
|
|
}
|
|
TmpPIDList= TmpPIDList->Next;
|
|
}
|
|
}
|
|
|
|
// if matching, append offset to RecordList
|
|
// add offset to current PID list
|
|
|
|
if( Found ) {
|
|
TmpPIDList->Count= TmpPIDList->Count + 1;
|
|
if( TmpPIDList->Count > g_MaxSnapShots ) g_MaxSnapShots= TmpPIDList->Count;
|
|
|
|
TmpRecordList= TmpPIDList->RecordList;
|
|
// walk to end of list
|
|
while( TmpRecordList->Next != 0 ) {
|
|
TmpRecordList= TmpRecordList->Next;
|
|
}
|
|
|
|
TmpRecordList->Next= (struct RecList*)LocalAlloc(LPTR, sizeof(struct RecList));
|
|
CheckPointer( TmpRecordList->Next );
|
|
TmpRecordList->Next->rOffset= Offset;
|
|
}
|
|
// make new PID list, add new PID, add offset
|
|
else {
|
|
struct PIDList* pNewPID;
|
|
// allocate a new PID,
|
|
// copy current PID information to it
|
|
// overwrite current PID information with new PID information
|
|
// have current PID point to new PID which may point on
|
|
|
|
pNewPID= (struct PIDList*) LocalAlloc(LPTR, sizeof(struct PIDList));
|
|
CheckPointer( pNewPID );
|
|
memcpy( pNewPID, TmpPIDList, sizeof(*pNewPID) );
|
|
|
|
strcpy( TmpPIDList->PIDItem, PID );
|
|
TmpPIDList->RecordList= (struct RecList*) LocalAlloc(LPTR, sizeof(struct RecList));
|
|
CheckPointer( TmpPIDList->RecordList );
|
|
TmpPIDList->RecordList->rOffset= Offset;
|
|
TmpPIDList->Next= pNewPID;
|
|
TmpPIDList->Count= 1;
|
|
|
|
}
|
|
|
|
/* if EOF, return */
|
|
/* clear the inBuff */
|
|
inBuff[0] = 0;
|
|
}
|
|
|
|
}
|
|
|
|
// look for the next PID line in the first table
|
|
|
|
VOID WriteFilex(FILE *InFile, FILE *OutFile, struct PIDList *ThePIDList)
|
|
{
|
|
struct PIDList *TmpPIDList;
|
|
struct RecList *TmpRecordList;
|
|
char inBuff[RECSIZE] = {0};
|
|
|
|
/* initialize temp list pointer */
|
|
TmpPIDList = ThePIDList;
|
|
|
|
/* heading */
|
|
fprintf(OutFile,"%s\n",szHeader);
|
|
|
|
OutputTags( OutFile );
|
|
|
|
|
|
/* while not end of list, write records at offset to end of output file */
|
|
while (TmpPIDList != 0) {
|
|
TmpRecordList = TmpPIDList->RecordList;
|
|
|
|
|
|
if( (!bIgnoreTransients) || (TmpPIDList->Count == g_MaxSnapShots) ) {
|
|
while (TmpRecordList != 0) {
|
|
LONG Offset;
|
|
|
|
Offset= TmpRecordList->rOffset;
|
|
if( Offset != INVALIDOFFSET ) {
|
|
/* read in record */
|
|
if (fseek(InFile, TmpRecordList->rOffset, SEEK_SET) == -1) break;
|
|
if (fscanf(InFile, "%[^\n]", inBuff) != 1) break;
|
|
|
|
/* read out record */
|
|
fprintf(OutFile, "%s\n", inBuff);
|
|
}
|
|
|
|
/* get next record */
|
|
TmpRecordList = TmpRecordList->Next;
|
|
}
|
|
|
|
/* add a line here */
|
|
fputc('\n', OutFile);
|
|
}
|
|
|
|
/* get next record */
|
|
TmpPIDList = TmpPIDList->Next;
|
|
}
|
|
|
|
}
|