//
// Systrack - System resource tracking
// Copyright (c) Microsoft Corporation, 1998
//

//
// module: systrack.cxx
// author: silviuc
// created: Mon Nov 09 12:20:41 1998
//


#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>

extern "C" {
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
}

#include <windows.h>
#include <common.ver>

#define VERSION_DEFINITION_MODULE
#include "version.hxx"

#define DEBUGINFO_DEFINITION_MODULE
#include "debug.hxx"

#include "pooltag.hxx"
#include "process.hxx"
#include "memory.hxx"
#include "systrack.hxx"

#define BUFFER_SIZE_STEP    ( 128 * 1024 )

void PrintCurrentTime ();
void PrintSystemBasicInformation ();
void PrintSystemPerformanceInformation ();
void PrintSystemProcessInformation (BOOL ShortDump);
void PrintSystemPoolDetailedInformation ();
void PrintSystemPoolTagInformation ();
void PrintProcessStackInformation ();

VOID
GetProcessStackInfo (
    PSYSTEM_PROCESS_INFORMATION Info,
    PSIZE_T MaxSize,
    PSIZE_T TotalSize,
    PBOOL ErrorsFound
    );

//
// Functions:
//
//     Help
//
// Decription:
//
//     Prints help information to the stdout. Exits with status code 1.
//

static void
Help ()
{
    static char help_text [] =
"                                                                          \n"
"systrack - System resource tracking --" BUILD_MACHINE_TAG "\n"
VER_LEGALCOPYRIGHT_STR "\n"
"                                                                          \n"
"    systrack [INFO-CLASS]                                                 \n"
"                                                                          \n"
"    <> : if no class specified, print process information.                \n"
"    /system : print system basic information.                             \n"
"    /process : print process information.                                 \n"
"    /stack : print stack usage information for all processes.             \n"
"    /performance: print performance information.                          \n"
"    /pool : print pool tag information (pool tags should be enabled).     \n"
"    /pooldetailed : print pool information (only checked builds).         \n"
"    /all : print everything.                                              \n"
"                                                                          \n"
"    /trackpool PERIOD DELTA                                               \n"
"    /trackpooltag PERIOD PATTERN DELTA                                    \n"
"    /trackprocess PERIOD HANDLE THREAD WSET VSIZE PFILE                   \n"
"    /trackprocessid PERIOD ID HANDLE THREAD WSET VSIZE PFILE              \n"
"    /trackavailablepages PERIOD DELTA                                     \n"
"    /trackcommittedpages PERIOD DELTA                                     \n"
"    /trackcommitlimit PERIOD DELTA                                        \n"
"    /trackpagefaultcount PERIOD DELTA                                     \n"
"    /tracksystemcalls PERIOD DELTA                                        \n"
"    /tracktotalsystemdriverpages PERIOD DELTA                             \n"
"    /tracktotalsystemcodepages PERIOD DELTA                               \n"
"                                                                          \n"
"    /help TOPIC  detailed help for the topic (e.g. process, trackpool,    \n"
"                 trackprocessid, etc.).                                   \n"
"    ?, /?        help                                                     \n"
"    -version     version information                                      \n"
"                                                                          \n"
"Examples:                                                                 \n"
"                                                                          \n"
"    systrack /trackpool 1000 10000                                        \n"
"                                                                          \n"
"        Polls every 1000ms the kernel pools and will print every pool tag \n"
"        whose pool usage increased by more than 10000 bytes.              \n"
"                                                                          \n"
"    systrack /trackpooltag 1000 \"G*\" 10000                              \n"
"                                                                          \n"
"        Polls every 1000ms the kernel pools and will print every pool tag \n"
"        that matches the pattern if its pool usage increased by more      \n"
"        than 10000 bytes.                                                 \n"
"                                                                          \n"
"    systrack /trackprocess 1000 5 5 1000000 1000000 1000000               \n"
"                                                                          \n"
"        Polls every 1000ms the processes running and prints every process \n"
"        whose handle count increased by more than 5 or thread count       \n"
"        increased by more than 5 or working set size increased by more    \n"
"        than 1000000 or virtual size increased by more than 1000000 or    \n"
"        pagefile usage increased by more than 1000000.                    \n"
"                                                                          \n"
"    systrack /trackprocessid 1000 136 5 5 1000000 1000000 1000000         \n"
"                                                                          \n"
"        Polls every 1000ms the process with id 136 and reports if the     \n"
"        handle count increased by more than 5 or thread count             \n"
"        increased by more than 5 or working set size increased by more    \n"
"        than 1000000 or virtual size increased by more than 1000000 or    \n"
"        pagefile usage increased by more than 1000000.                    \n"
"                                                                          \n"
"                                                                          \n";

    printf (help_text);
    exit (1);
}


//
// Functions:
//
//     DetailedHelp
//
// Decription:
//
//     Prints help information to the stdout for a specific topic.
//     Exits with status code 1.
//

static void
DetailedHelp (

    char * Topic)
{
    char * help_text;

    if (_stricmp (Topic, "system") == 0) {
      help_text =
          "systrack /system                                                     \n"
          "                                                                     \n"
          "                                                                     \n";
    }
    else if (_stricmp (Topic, "process") == 0) {
      help_text =
          "systrack /proces                                                     \n"
          "                                                                     \n"
          "                                                                     \n";
    }
    else if (_stricmp (Topic, "performance") == 0) {
      help_text =
          "systrack /performance                                                \n"
          "                                                                     \n"
          "                                                                     \n";
    }
    else if (_stricmp (Topic, "trackprocess") == 0) {
      help_text =
          "systrack /trackprocess                                               \n"
          "                                                                     \n"
          "                                                                     \n";
    }
    else if (_stricmp (Topic, "trackprocessid") == 0) {
      help_text =
          "systrack /system                                                     \n"
          "                                                                     \n"
          "                                                                     \n";
    }
    else if (_stricmp (Topic, "trackpool") == 0) {
      help_text =
          "systrack /trackpool                                                  \n"
          "                                                                     \n"
          "                                                                     \n";
    }
    else if (_stricmp (Topic, "trackpooltag") == 0) {
      help_text =
          "systrack /trackpooltag                                               \n"
          "                                                                     \n"
          "                                                                     \n";
    }
    else {
      printf ("Unknown help topic %s \n", Topic);
      exit (1);
    }


    printf (help_text, VERSION_INFORMATION_VERSION);
    exit (1);
}


//
// Function:
//
//     main
//
// Description:
//
//     ?, -?, /? - print help information.
//     -version - print version information
//
//     default (system, process, pool)
//     /process
//     /stack
//     /system
//     /performance
//     /pool
//     /pooldetailed
//
//

void _cdecl
main (int argc, char *argv[])
{
    if (argc == 2 && _stricmp (argv[1], "?") == 0)
        Help ();
    else if (argc == 2 && _stricmp (argv[1], "/?") == 0)
        Help ();
    else if (argc == 2 && _stricmp (argv[1], "-?") == 0)
        Help ();
    else if (argc == 2 && _stricmp (argv[1], "-h") == 0)
        Help ();
    else if (argc == 2 && _stricmp (argv[1], "/h") == 0)
        Help ();
    // if (argc == 3 && _stricmp (argv[1], "/help") == 0)
    //     DetailedHelp (argv[2]);

    if (argc == 2 && _stricmp (argv[1], "-version") == 0)
        dump_version_information ();

    try
      {
        //
        // Here comes the code ...
        //

        PrintCurrentTime ();

        if (argc == 1)
          {
            //
            // <> default options
            //

            // PrintSystemBasicInformation ();
            PrintSystemProcessInformation (TRUE);
            // PrintSystemPoolTagInformation ();
          }
        else if (argc == 2 && _stricmp (argv[1], "/stack") == 0)
          {
            //
            // /stack option
            //

            PrintProcessStackInformation ();
          }
        else if (argc == 2 && _stricmp (argv[1], "/all") == 0)
          {
            //
            // /all option
            //

            PrintSystemBasicInformation ();
            PrintSystemPerformanceInformation ();
            PrintSystemProcessInformation (FALSE);
            PrintSystemPoolTagInformation ();
            PrintSystemPoolDetailedInformation ();
          }
        else if (argc == 4 && _stricmp (argv[1], "/trackpool") == 0)
          {
            //
            // /trackpool PERIOD DELTA
            //

            ULONG Delta;
            ULONG Period;

            Period = atoi (argv[2]);
            Delta = atoi (argv[3]);

            if (Delta == 0)
                Delta = 8192;

            if (Period == 0)
                Period = 1000;

            SystemPoolTrack (Period, Delta);
          }
        else if (argc == 5 && _stricmp (argv[1], "/trackpooltag") == 0)
          {
            //
            // /trackpooltag PERIOD PATTERN DELTA
            //

            ULONG Delta;
            ULONG Period;
            UCHAR * Pattern;

            Period = atoi (argv[2]);
            Pattern = (UCHAR *)(argv[3]);
            Delta = atoi (argv[4]);

            if (Delta == 0)
                Delta = 8192;

            if (Period == 0)
                Period = 1000;

            SystemPoolTagTrack (Period, Pattern, Delta);
          }
        else if (argc == 8 && _stricmp (argv[1], "/trackprocess") == 0)
          {
            //
            // /trackprocess PERIOD HANDLES THREADS WSET VSIZE PFILE
            //

            ULONG DeltaHandles;
            ULONG DeltaThreads;
            ULONG DeltaWorkingSet;
            SIZE_T DeltaVirtualSize;
            SIZE_T DeltaPagefileUsage;
            ULONG Period;

            Period = atoi (argv[2]);
            DeltaHandles = atoi (argv[3]);
            DeltaThreads = atoi (argv[4]);
            DeltaWorkingSet = atoi (argv[5]);
            DeltaVirtualSize = atoi (argv[6]);
            DeltaPagefileUsage = atoi (argv[7]);

            if (Period == 0)
                Period = 1000;

            if (DeltaHandles == 0)
                DeltaHandles = 32;

            if (DeltaThreads == 0)
                DeltaThreads = 8;

            if (DeltaWorkingSet == 0)
                DeltaWorkingSet = 0x100000;

            if (DeltaVirtualSize == 0)
                DeltaVirtualSize = 0x100000;

            if (DeltaPagefileUsage == 0)
                DeltaPagefileUsage = 0x100000;

            SystemProcessTrack (Period,
                                DeltaHandles, DeltaThreads, DeltaWorkingSet,
                                DeltaVirtualSize, DeltaPagefileUsage);
          }
        else if (argc == 9 && _stricmp (argv[1], "/trackprocessid") == 0)
          {
            //
            // /trackprocessid PERIOD ID HANDLES THREADS WSET VSIZE PFILE
            //

            ULONG ProcessId;
            ULONG DeltaHandles;
            ULONG DeltaThreads;
            ULONG DeltaWorkingSet;
            SIZE_T DeltaVirtualSize;
            SIZE_T DeltaPagefileUsage;
            ULONG Period;

            Period = atoi (argv[2]);
            ProcessId = atoi (argv[3]);
            DeltaHandles = atoi (argv[4]);
            DeltaThreads = atoi (argv[5]);
            DeltaWorkingSet = atoi (argv[6]);
            DeltaVirtualSize = atoi (argv[7]);
            DeltaPagefileUsage = atoi (argv[8]);

            if (Period == 0)
                Period = 1000;

            if (ProcessId == 0) {
              printf ("Bad process id %s\n", argv[3]);
              exit (1);
            }

            if (DeltaHandles == 0)
                DeltaHandles = 32;

            if (DeltaThreads == 0)
                DeltaThreads = 8;

            if (DeltaWorkingSet == 0)
                DeltaWorkingSet = 0x100000;

            if (DeltaVirtualSize == 0)
                DeltaVirtualSize = 0x100000;

            if (DeltaPagefileUsage == 0)
                DeltaPagefileUsage = 0x100000;

            SystemProcessIdTrack (Period, ProcessId,
                                  DeltaHandles, DeltaThreads, DeltaWorkingSet,
                                  DeltaVirtualSize, DeltaPagefileUsage);
          }
        else if (argc == 4 && _stricmp (argv[1], "/trackavailablepages") == 0)
          {
            //
            // /trackavailablepages PERIOD DELTA
            //

            LONG Delta;
            ULONG Period;

            Period = atoi (argv[2]);
            Delta = atoi (argv[3]);

            if (Period == 0)
                Period = 1000;

            if (Delta == 0)
                Delta = 100;

            //
            // We track decreasing values therefore delta should be negative.
            //

            TrackPerformanceCounter (argv[1], TRACK_AVAILABLE_PAGES, Period, -Delta);
          }
        else if (argc == 4 && _stricmp (argv[1], "/trackcommittedpages") == 0)
          {
            //
            // /trackcommittedpages PERIOD DELTA
            //

            LONG Delta;
            ULONG Period;

            Period = atoi (argv[2]);
            Delta = atoi (argv[3]);

            if (Period == 0)
                Period = 1000;

            if (Delta == 0)
                Delta = 100;

            //
            // We track increasing values therefore delta should be positive.
            //

            TrackPerformanceCounter (argv[1], TRACK_COMMITTED_PAGES, Period, Delta);
          }
        else if (argc == 4 && _stricmp (argv[1], "/trackcommitlimit") == 0)
          {
            //
            // /trackcommitlimit PERIOD DELTA
            //

            LONG Delta;
            ULONG Period;

            Period = atoi (argv[2]);
            Delta = atoi (argv[3]);

            if (Period == 0)
                Period = 1000;

            if (Delta == 0)
                Delta = 100;

            //
            // We track increasing values therefore delta should be positive.
            //

            TrackPerformanceCounter (argv[1], TRACK_COMMIT_LIMIT, Period, Delta);
          }
        else if (argc == 4 && _stricmp (argv[1], "/trackpagefaultcount") == 0)
          {
            //
            // /trackpagefaultcount PERIOD DELTA
            //

            LONG Delta;
            ULONG Period;

            Period = atoi (argv[2]);
            Delta = atoi (argv[3]);

            if (Period == 0)
                Period = 1000;

            if (Delta == 0)
                Delta = 100;

            //
            // We track increasing values therefore delta should be positive.
            //

            TrackPerformanceCounter (argv[1], TRACK_PAGE_FAULT_COUNT, Period, Delta);
          }
        else if (argc == 4 && _stricmp (argv[1], "/tracksystemcalls") == 0)
          {
            //
            // /tracksystemcalls PERIOD DELTA
            //

            LONG Delta;
            ULONG Period;

            Period = atoi (argv[2]);
            Delta = atoi (argv[3]);

            if (Period == 0)
                Period = 1000;

            if (Delta == 0)
                Delta = 100;

            //
            // We track increasing values therefore delta should be positive.
            //

            TrackPerformanceCounter (argv[1], TRACK_SYSTEM_CALLS, Period, Delta);
          }
        else if (argc == 4 && _stricmp (argv[1], "/tracktotalsystemdriverpages") == 0)
          {
            //
            // /tracktotalsystemdriverpages PERIOD DELTA
            //

            LONG Delta;
            ULONG Period;

            Period = atoi (argv[2]);
            Delta = atoi (argv[3]);

            if (Period == 0)
                Period = 1000;

            if (Delta == 0)
                Delta = 100;

            //
            // We track increasing values therefore delta should be positive.
            //

            TrackPerformanceCounter (argv[1], TRACK_TOTAL_SYSTEM_DRIVER_PAGES, Period, Delta);
          }
        else if (argc == 4 && _stricmp (argv[1], "/tracktotalsystemcodepages") == 0)
          {
            //
            // /tracktotalsystemcodepages PERIOD DELTA
            //

            LONG Delta;
            ULONG Period;

            Period = atoi (argv[2]);
            Delta = atoi (argv[3]);

            if (Period == 0)
                Period = 1000;

            if (Delta == 0)
                Delta = 100;

            //
            // We track increasing values therefore delta should be positive.
            //

            TrackPerformanceCounter (argv[1], TRACK_TOTAL_SYSTEM_CODE_PAGES, Period, Delta);
          }
        else
          {
            for (int Count = 1; Count < argc; Count++)
              {
                if (_stricmp (argv[Count], "/system") == 0)
                    PrintSystemBasicInformation ();
                else if (_stricmp (argv[Count], "/performance") == 0)
                    PrintSystemPerformanceInformation ();
                else if (_stricmp (argv[Count], "/process") == 0)
                    PrintSystemProcessInformation (TRUE);
                else if (_stricmp (argv[Count], "/pool") == 0)
                    PrintSystemPoolTagInformation ();
                else if (_stricmp (argv[Count], "/pooldetailed") == 0)
                    PrintSystemPoolDetailedInformation ();
                else
                    Help ();
              }
          }
      }
    catch (...)
      {
        printf ("unexpected exception ...\n");
        fflush (stdout);
        exit (1);
      }

    exit (0);
}


//
// Function:
//
//     PrintCurrentTime
//
// Description:
//
//     Prints current time, machine name, etc.
//
//

void PrintCurrentTime ()
{
    TCHAR MachineName [32];
    LPCTSTR TimeString;
    time_t Time;
    DWORD Result;

    if (GetEnvironmentVariable (TEXT("COMPUTERNAME"), MachineName, sizeof MachineName) == 0)
        strcpy (MachineName, "unknown");

    time (&Time);
    TimeString = asctime (localtime (&Time));

    printf ("Systrack - System resource tracking, %s\n", VERSION_INFORMATION_VERSION);
    printf ("Machine: %s\n", MachineName);
    printf ("Time: %s\n", TimeString);
    fflush( stdout );
}

//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////


//
// Macro:
//
//     _dump_, _dump_quad_ (object, field)
//
// Description:
//
//     Handy macros to dump the fields of a structure.
//

#define _dump_(object,field) printf ("%-30s %08X (%u)\n", #field, (ULONG)(object->field), (ULONG)(object->field))

#define _dump_quad_(object,field) printf ("%-30s %I64X (%I64u)\n", #field, (object->field.QuadPart), (object->field.QuadPart))


//
// Local:
//
//     InfoBuffer
//
// Description:
//
//     Large enough structure to hold theinformation returned by
//     NtQuerySystemInformation. I've opted for this solution because
//     systrack can run under heavy stress conditions and we do not
//     to allocate big chunks of memory dynamically in such a situation.
//
//     Note. If we decide to multithread the application we will need a
//     critical section to protect the information buffer.
//     CRITICAL_SECTION InfoBufferLock;
//

static TCHAR InfoBuffer [0x40000];

//
// Local:
//
//     PoolTagInformationBuffer
//
// Description:
//
//     Buffer for NtQuerySystemInformation( SystemPoolTagInformation ).
//     Its size is grown by QueryPoolTagInformationIterative if necessary.
//     The length of the buffer is held in PoolTagInformationBufferLength.
//

static TCHAR *PoolTagInformationBuffer = NULL;

//
// Local:
//
//     PoolTagInformationBufferLength
//
// Description:
//
//     The current length of PoolTagInformationBuffer.
//

size_t PoolTagInformationBufferLength = 0;


//
// Function:
//
//     QueryPoolTagInformationIterative
//
// Description:
//
// 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(
    TCHAR **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( TCHAR ) * BUFFER_SIZE_STEP;

        *CurrentBuffer = (TCHAR *) 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 = (TCHAR *) 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;
}

//
// Function:
//
//     QuerySystemPoolTagInformation
//
// Description:
//
//     Fills InfoBuffer with SystemPoolTagInformation and returns
//     a pointer to it.
//

PVOID
QuerySystemPoolTagInformation ()
{
    NTSTATUS Status;
    ULONG RealLength;

    //
    // SystemPoolTagInformation
    //

    Status = QueryPoolTagInformationIterative(
        &PoolTagInformationBuffer,
        &PoolTagInformationBufferLength );

    if (! NT_SUCCESS(Status))
        printf ("NtQuerySystemInformation(pooltag): error %08X\n",
            Status);

    return NT_SUCCESS(Status) ? PoolTagInformationBuffer : NULL;
}


//
// Function:
//
//     QuerySystemProcessInformation
//
// Description:
//
//     Fills InfoBuffer with SystemProcessInformation and returns
//     a pointer to it.
//

PVOID
QuerySystemProcessInformation ()
{
    NTSTATUS Status;
    ULONG RealLength;

    //
    // SystemProcessInformation
    //

    Status = NtQuerySystemInformation (
        SystemProcessInformation,
        InfoBuffer,
        sizeof InfoBuffer,
        &RealLength);

    if (! NT_SUCCESS(Status))
        printf ("NtQuerySystemInformation(process): error %08X\n",
            Status);

    return NT_SUCCESS(Status) ? InfoBuffer : NULL;
}


//
// Function:
//
//     QuerySystemPerformanceInformation
//
// Description:
//
//     Fills InfoBuffer with SystemPerformanceInformation and returns
//     a pointer to it.
//

PVOID
QuerySystemPerformanceInformation ()
{
    NTSTATUS Status;
    ULONG RealLength;

    //
    // SystemPerformanceInformation
    //

    Status = NtQuerySystemInformation (
        SystemPerformanceInformation,
        InfoBuffer,
        sizeof InfoBuffer,
        &RealLength);

    if (! NT_SUCCESS(Status))
        printf ("NtQuerySystemInformation(performance): error %08X\n",
            Status);

    return NT_SUCCESS(Status) ? InfoBuffer : NULL;
}


//
// Function:
//
//     PrintSystemBasicInformation
//
// Description:
//
//     Prints SystemPerformanceInformation.
//
//

void PrintSystemBasicInformation ()
{
    NTSTATUS Status;
    ULONG RealLength;

    printf ("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - ");
    printf ("- - - - - - - - - - - - - - - - - - - - - - - - - - - \n");
    printf ("System basic information \n");
    printf ("- - - - - - - - - - - - - - - - - - - - - - - - - - - ");
    printf ("- - - - - - - - - - - - - - - - - - - - - - - - - - - \n");
    fflush( stdout );

    //
    // SystemBasicInformation
    //

    Status = NtQuerySystemInformation (
        SystemBasicInformation,
        InfoBuffer,
        sizeof (SYSTEM_BASIC_INFORMATION),
        &RealLength);

    if (! NT_SUCCESS(Status))
      {
        printf ("NtQuerySystemInformation(Basic): error %08X\n", Status);
        return;
      }

    {
      PSYSTEM_BASIC_INFORMATION Info = (PSYSTEM_BASIC_INFORMATION)InfoBuffer;

      _dump_(Info, PageSize);
      _dump_(Info, NumberOfPhysicalPages);
      _dump_(Info, LowestPhysicalPageNumber);
      _dump_(Info, HighestPhysicalPageNumber);
      _dump_(Info, AllocationGranularity);
      _dump_(Info, MinimumUserModeAddress);
      _dump_(Info, MaximumUserModeAddress);
      _dump_(Info, ActiveProcessorsAffinityMask);
      _dump_(Info, NumberOfProcessors);
    }
}


//
// Function:
//
//     PrintSystemPerformanceInformation
//
// Description:
//
//     Prints systemPerformanceInformation.
//

void PrintSystemPerformanceInformation ()
{
    NTSTATUS Status;
    ULONG RealLength;

    printf ("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - ");
    printf ("- - - - - - - - - - - - - - - - - - - - - - - - - - - \n");
    printf ("System performance information \n");
    printf ("- - - - - - - - - - - - - - - - - - - - - - - - - - - ");
    printf ("- - - - - - - - - - - - - - - - - - - - - - - - - - - \n");
    fflush( stdout );

    //
    // SystemPerformanceInformation
    //

    Status = NtQuerySystemInformation (
        SystemPerformanceInformation,
        InfoBuffer,
        sizeof (SYSTEM_PERFORMANCE_INFORMATION),
        &RealLength);

    if (! NT_SUCCESS(Status))
      {
        printf ("NtQuerySystemInformation(Performance): error %08X\n", Status);
        return;
      }

    {
      PSYSTEM_PERFORMANCE_INFORMATION Info = (PSYSTEM_PERFORMANCE_INFORMATION)InfoBuffer;

      _dump_quad_ (Info, IdleProcessTime);
      _dump_quad_ (Info, IoReadTransferCount);
      _dump_quad_ (Info, IoWriteTransferCount);
      _dump_quad_ (Info, IoOtherTransferCount);

      _dump_ (Info, IoReadOperationCount);
      _dump_ (Info, IoWriteOperationCount);
      _dump_ (Info, IoOtherOperationCount);
      _dump_ (Info, AvailablePages);
      _dump_ (Info, CommittedPages);
      _dump_ (Info, CommitLimit);
      _dump_ (Info, PeakCommitment);
      _dump_ (Info, PageFaultCount);
      _dump_ (Info, CopyOnWriteCount);
      _dump_ (Info, TransitionCount);
      _dump_ (Info, CacheTransitionCount);
      _dump_ (Info, DemandZeroCount);
      _dump_ (Info, PageReadCount);
      _dump_ (Info, PageReadIoCount);
      _dump_ (Info, CacheReadCount);
      _dump_ (Info, CacheIoCount);
      _dump_ (Info, DirtyPagesWriteCount);
      _dump_ (Info, DirtyWriteIoCount);
      _dump_ (Info, MappedPagesWriteCount);
      _dump_ (Info, MappedWriteIoCount);
      _dump_ (Info, PagedPoolPages);
      _dump_ (Info, NonPagedPoolPages);
      _dump_ (Info, PagedPoolAllocs);
      _dump_ (Info, PagedPoolFrees);
      _dump_ (Info, NonPagedPoolAllocs);
      _dump_ (Info, NonPagedPoolFrees);
      _dump_ (Info, FreeSystemPtes);
      _dump_ (Info, ResidentSystemCodePage);
      _dump_ (Info, TotalSystemDriverPages);
      _dump_ (Info, TotalSystemCodePages);
      _dump_ (Info, NonPagedPoolLookasideHits);
      _dump_ (Info, PagedPoolLookasideHits);
#if 0
      _dump_ (Info, Spare3Count);
#endif
      _dump_ (Info, ResidentSystemCachePage);
      _dump_ (Info, ResidentPagedPoolPage);
      _dump_ (Info, ResidentSystemDriverPage);
      _dump_ (Info, CcFastReadNoWait);
      _dump_ (Info, CcFastReadWait);
      _dump_ (Info, CcFastReadResourceMiss);
      _dump_ (Info, CcFastReadNotPossible);
      _dump_ (Info, CcFastMdlReadNoWait);
      _dump_ (Info, CcFastMdlReadWait);
      _dump_ (Info, CcFastMdlReadResourceMiss);
      _dump_ (Info, CcFastMdlReadNotPossible);
      _dump_ (Info, CcMapDataNoWait);
      _dump_ (Info, CcMapDataWait);
      _dump_ (Info, CcMapDataNoWaitMiss);
      _dump_ (Info, CcMapDataWaitMiss);
      _dump_ (Info, CcPinMappedDataCount);
      _dump_ (Info, CcPinReadNoWait);
      _dump_ (Info, CcPinReadWait);
      _dump_ (Info, CcPinReadNoWaitMiss);
      _dump_ (Info, CcPinReadWaitMiss);
      _dump_ (Info, CcCopyReadNoWait);
      _dump_ (Info, CcCopyReadWait);
      _dump_ (Info, CcCopyReadNoWaitMiss);
      _dump_ (Info, CcCopyReadWaitMiss);
      _dump_ (Info, CcMdlReadNoWait);
      _dump_ (Info, CcMdlReadWait);
      _dump_ (Info, CcMdlReadNoWaitMiss);
      _dump_ (Info, CcMdlReadWaitMiss);
      _dump_ (Info, CcReadAheadIos);
      _dump_ (Info, CcLazyWriteIos);
      _dump_ (Info, CcLazyWritePages);
      _dump_ (Info, CcDataFlushes);
      _dump_ (Info, CcDataPages);
      _dump_ (Info, ContextSwitches);
      _dump_ (Info, FirstLevelTbFills);
      _dump_ (Info, SecondLevelTbFills);
      _dump_ (Info, SystemCalls);

    }
}


//
// Function:
//
//     PrintSystemProcessInformation
//
// Description:
//
//     Prints SystemProcessInformation.
//
// Details:
//
//     These are the fields of a SYSTEM_PROCESS_INFORMATION structure:
//
//     ULONG NextEntryOffset;
//     ULONG NumberOfThreads;
//     LARGE_INTEGER SpareLi1;
//     LARGE_INTEGER SpareLi2;
//     LARGE_INTEGER SpareLi3;
//     LARGE_INTEGER CreateTime;
//     LARGE_INTEGER UserTime;
//     LARGE_INTEGER KernelTime;
//     UNICODE_STRING ImageName;
//     KPRIORITY BasePriority;
//     HANDLE UniqueProcessId;
//     HANDLE InheritedFromUniqueProcessId;
//     ULONG HandleCount;
//     ULONG SessionId;
//     ULONG SpareUl3;
//     SIZE_T PeakVirtualSize;
//     SIZE_T VirtualSize;
//     ULONG PageFaultCount;
//     ULONG PeakWorkingSetSize;
//     ULONG WorkingSetSize;
//     SIZE_T QuotaPeakPagedPoolUsage;
//     SIZE_T QuotaPagedPoolUsage;
//     SIZE_T QuotaPeakNonPagedPoolUsage;
//     SIZE_T QuotaNonPagedPoolUsage;
//     SIZE_T PagefileUsage;
//     SIZE_T PeakPagefileUsage;
//     SIZE_T PrivatePageCount;
//     LARGE_INTEGER ReadOperationCount;
//     LARGE_INTEGER WriteOperationCount;
//     LARGE_INTEGER OtherOperationCount;
//     LARGE_INTEGER ReadTransferCount;
//     LARGE_INTEGER WriteTransferCount;
//     LARGE_INTEGER OtherTransferCount;
//

void PrintSystemProcessInformation (

    BOOL ShortDump)
{
    NTSTATUS Status;
    ULONG RealLength;
    SYSTEM_BASIC_INFORMATION SysInfo;
    BOOL FinishNextTime = FALSE;

    if (ShortDump)
      {
        printf ("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - ");
        printf ("- - - - - - - - - - - - - - - - - - - - - -\n");
        printf ("System process information \n");
        printf ("- - - - - - - - - - - - - - - - - - - - - - - - - - - ");
        printf ("- - - - - - - - - - - - - - - - - - - - - -\n");
      }
    else
      {
        printf ("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - ");
        printf ("- - - - - - - - - - - - - - - - - - - - - - - - - - - \n");
        printf ("System process information \n");
        printf ("- - - - - - - - - - - - - - - - - - - - - - - - - - - ");
        printf ("- - - - - - - - - - - - - - - - - - - - - - - - - - - \n");
      }

    fflush( stdout );

    //
    // SystemBasicInformation
    //

    Status = NtQuerySystemInformation (
        SystemBasicInformation,
        &SysInfo,
        sizeof (SysInfo),
        &RealLength);

    if (! NT_SUCCESS(Status))
      {
        printf ("NtQuerySystemInformation(Basic): error %08X\n", Status);
        return;
      }

    //
    // SystemProcessInformation
    //

    Status = NtQuerySystemInformation (
        SystemProcessInformation,
        InfoBuffer,
        sizeof InfoBuffer,
        &RealLength);

    if (! NT_SUCCESS(Status))
      {
        printf ("NtQuerySystemInformation(Process): error %08X\n", Status);
        return;
      }

    {
      PSYSTEM_PROCESS_INFORMATION Info = (PSYSTEM_PROCESS_INFORMATION)InfoBuffer;

      if (ShortDump)
        {
          printf ("%-15s %-5s %-5s %-4s %-5s %-8s %-8s %-5s %-5s %-6s %-5s %-5s %-5s\n",
                  "Process",
                  "Id",
                  "Sess",
                  "Pri",
                  "Thrds",
                  "Faults",
                  "Handles",
                  "Utime",
                  "Ktime",
                  "Wset",
                  "Vsize",
                  "Pfile",
                  "I/O");

          printf ("%-15s %-5s %-5s %-4s %-5s %-8s %-8s %-5s %-5s %-6s %-5s %-5s %-5s\n",
                  "",
                  "",
                  "",
                  "",
                  "",
                  "",
                  "",
                  "%",
                  "%",
                  "pages",
                  "Mb",
                  "Mb",
                  "x1000");
        }
      else
        {
          printf ("%-15s %-5s %-5s %-4s %-5s %-8s %-8s %-5s %-5s %-6s %-5s %-5s %-5s %-5s %-5s\n",
                  "Process",
                  "Id",
                  "Sess",
                  "Pri",
                  "Thrds",
                  "Faults",
                  "Handles",
                  "Utime",
                  "Ktime",
                  "Wset",
                  "Vsize",
                  "Pfile",
                  "I/O",
                  "Npool",
                  "Ppool");

          printf ("%-15s %-5s %-5s %-4s %-5s %-8s %-8s %-5s %-5s %-6s %-5s %-5s %-5s %-5s %-5s\n",
                  "",
                  "",
                  "",
                  "",
                  "",
                  "",
                  "",
                  "%",
                  "%",
                  "pages",
                  "Mb",
                  "Mb",
                  "x1000",
                  "Mb",
                  "Mb");
        }

      if (ShortDump)
        {
          printf ("- - - - - - - - - - - - - - - - - - - - - - - - - - - ");
          printf ("- - - - - - - - - - - - - - - - - - - - - -\n");
        }
      else
        {
          printf ("- - - - - - - - - - - - - - - - - - - - - - - - - - - ");
          printf ("- - - - - - - - - - - - - - - - - - - - - - - - - - - \n");
        }

      fflush( stdout );

      for (FinishNextTime = FALSE ;
           FinishNextTime == FALSE;
           Info = (PSYSTEM_PROCESS_INFORMATION)((ULONG_PTR)Info + Info->NextEntryOffset))
        {
          if (Info->NextEntryOffset == 0)
              FinishNextTime = TRUE;

          //
          // User time vs Kernel time.
          //

          ULONG UserPercent, KernelPercent;

          UserPercent = (ULONG)((Info->UserTime.QuadPart) * 100
              / (Info->UserTime.QuadPart + Info->KernelTime.QuadPart));
          KernelPercent = 100 - UserPercent;

          //
          // I/O total count.
          //

          LARGE_INTEGER IoTotalCount;

          IoTotalCount.QuadPart = Info->ReadOperationCount.QuadPart
              + Info->WriteOperationCount.QuadPart
              + Info->OtherOperationCount.QuadPart;

          IoTotalCount.QuadPart /= 1000;

          //
          // Image name (special case the idle process).
          //

          if (Info->ImageName.Buffer == NULL)
              printf ("%-15s ", "Idle");
          else
              printf ("%-15ws ", Info->ImageName.Buffer);

          //
          // Print the stuff.
          //

          if (ShortDump)
            {
              printf ("%-5I64u %-5u %-4u %-5u %-8u %-8u %-5u %-5u %-6u %-5u %-5u %-5I64u\n",
                      (ULONG64)((ULONG_PTR)(Info->UniqueProcessId)),
                      Info->SessionId,
                      Info->BasePriority,
                      Info->NumberOfThreads,
                      Info->PageFaultCount,
                      Info->HandleCount,
                      UserPercent,
                      KernelPercent,
                      Info->WorkingSetSize / SysInfo.PageSize,
                      Info->VirtualSize / 0x100000,
                      Info->PagefileUsage / 0x100000,
                      (IoTotalCount.QuadPart));
            }
          else
            {
              printf ("%-5I64u %-5u %-4u %-5u %-8u %-8u %-5u %-5u %-6u %-5u %-5u %-5I64u %-5u %-5u\n",
                      (ULONG64)((ULONG_PTR)(Info->UniqueProcessId)),
                      Info->SessionId,
                      Info->BasePriority,
                      Info->NumberOfThreads,
                      Info->PageFaultCount,
                      Info->HandleCount,
                      UserPercent,
                      KernelPercent,
                      Info->WorkingSetSize / SysInfo.PageSize,
                      Info->VirtualSize / 0x100000,
                      Info->PagefileUsage / 0x100000,
                      (IoTotalCount.QuadPart),
                      Info->QuotaNonPagedPoolUsage / 0x100000,
                      Info->QuotaPagedPoolUsage / 0x100000);
            }

          fflush( stdout );
        }
    }
}


//
// Function:
//
//     PrintSystemPoolDetailedInformation
//
// Description:
//
//     Prints systemNonPagedPoolInformation and systemPagedPoolInformation.
//     The function returns something meaningful only on checked builds.
//
//     typedef struct _SYSTEM_POOL_ENTRY {
//         BOOLEAN Allocated;
//         BOOLEAN Spare0;
//         USHORT AllocatorBackTraceIndex;
//         ULONG Size;
//         union {
//             UCHAR Tag[4];
//             ULONG TagUlong;
//             PVOID ProcessChargedQuota;
//         };
//     } SYSTEM_POOL_ENTRY, *PSYSTEM_POOL_ENTRY;
//
//     typedef struct _SYSTEM_POOL_INFORMATION {
//         SIZE_T TotalSize;
//         PVOID FirstEntry;
//         USHORT EntryOverhead;
//         BOOLEAN PoolTagPresent;
//         BOOLEAN Spare0;
//         ULONG NumberOfEntries;
//         SYSTEM_POOL_ENTRY Entries[1];
//     } SYSTEM_POOL_INFORMATION, *PSYSTEM_POOL_INFORMATION;
//

void PrintSystemPoolDetailedInformation ()
{
    NTSTATUS Status;
    ULONG RealLength;

    printf ("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - ");
    printf ("- - - - - - - - - - - - - - - - - - - - - - - - - - - \n");
    printf ("System pool detailed information \n");
    printf ("- - - - - - - - - - - - - - - - - - - - - - - - - - - ");
    printf ("- - - - - - - - - - - - - - - - - - - - - - - - - - - \n");
    fflush( stdout );

    //
    // SystemPoolInformation
    //

    Status = NtQuerySystemInformation (
        SystemNonPagedPoolInformation,
        InfoBuffer,
        sizeof InfoBuffer,
        &RealLength);

    if (! NT_SUCCESS(Status))
      {
        printf ("NtQuerySystemInformation(NonPagedPool): error %08X\n", Status);
        return;
      }

    {
      ULONG Index;
      PSYSTEM_POOL_INFORMATION Info = (PSYSTEM_POOL_INFORMATION)InfoBuffer;

      for (Index = 0; Index < Info->NumberOfEntries; Index++)
        {
          if (Index != 0 && Index%5 == 0)
              printf ("\n");

          printf ("%c%c%c%c %-5u ",
                  Info->Entries[Index].Tag[0],
                  Info->Entries[Index].Tag[1],
                  Info->Entries[Index].Tag[2],
                  Info->Entries[Index].Tag[3],
                  Info->Entries[Index].Size);
        }

      fflush( stdout );
    }
}


//
// Function:
//
//     PrintSystemPoolTagInformation
//
// Description:
//
//     Prints SystemPoolTagInformation.
//
//     typedef struct _SYSTEM_POOLTAG {
//         union {
//             UCHAR Tag[4];
//             ULONG TagUlong;
//         };
//         ULONG PagedAllocs;
//         ULONG PagedFrees;
//         SIZE_T PagedUsed;
//         ULONG NonPagedAllocs;
//         ULONG NonPagedFrees;
//         SIZE_T NonPagedUsed;
//     } SYSTEM_POOLTAG, *PSYSTEM_POOLTAG;
//
//     typedef struct _SYSTEM_POOLTAG_INFORMATION {
//         ULONG Count;
//         SYSTEM_POOLTAG TagInfo[1];
//     } SYSTEM_POOLTAG_INFORMATION, *PSYSTEM_POOLTAG_INFORMATION;
//

void PrintSystemPoolTagInformation ()
{
    NTSTATUS Status;
    ULONG RealLength;

    printf ("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - ");
    printf ("- - - - - - - - - - - - - - - - - - - - - - - - - - - \n");
    printf ("System pool tag information \n");
    printf ("- - - - - - - - - - - - - - - - - - - - - - - - - - - ");
    printf ("- - - - - - - - - - - - - - - - - - - - - - - - - - - \n");
    printf ("%-4s     %-8s %-8s %-8s %-8s %-8s %-8s\n",
            "Tag",
            "NP used", "P used",
            "NP alloc", "NP free",
            "P alloc", "P free");
    printf ("%-4s     %-8s %-8s %-8s %-8s %-8s %-8s\n",
            "",
            "x bytes", "x bytes",
            "x ops", "x ops",
            "x ops", "x ops");
    printf ("- - - - - - - - - - - - - - - - - - - - - - - - - - - ");
    printf ("- - - - - - - - - - - - - - - - - - - - - - - - - - - \n");
    fflush( stdout );

    //
    // SystemPoolTagInformation
    //

    Status = NtQuerySystemInformation (
        SystemPoolTagInformation,
        InfoBuffer,
        sizeof InfoBuffer,
        &RealLength);

    if (! NT_SUCCESS(Status))
      {
        printf ("NtQuerySystemInformation(PoolTag): error %08X\n", Status);
        return;
      }

    {
      ULONG Index;
      PSYSTEM_POOLTAG_INFORMATION Info = (PSYSTEM_POOLTAG_INFORMATION)InfoBuffer;

      for (Index = 0; Index < Info->Count; Index++)
        {
          printf ("%c%c%c%c     %-8u %-8u %-8u %-8u %-8u %-8u\n",
                  Info->TagInfo[Index].Tag[0],
                  Info->TagInfo[Index].Tag[1],
                  Info->TagInfo[Index].Tag[2],
                  Info->TagInfo[Index].Tag[3],
                  Info->TagInfo[Index].NonPagedUsed,
                  Info->TagInfo[Index].PagedUsed,
                  Info->TagInfo[Index].NonPagedAllocs,
                  Info->TagInfo[Index].NonPagedFrees,
                  Info->TagInfo[Index].PagedAllocs,
                  Info->TagInfo[Index].PagedFrees);
        }

      fflush( stdout );
    }
}




//
// Function:
//
//     PrintProcessStackInformation
//
// Description:
//
//     Prints stack usage information for each process.
//



BOOL
ComputeMaxStackInProcess (

    DWORD Pid,
    PSIZE_T MaxSize,
    PSIZE_T TotalSize);


VOID 
PrintProcessStackInformation (
    VOID
    )
{
    NTSTATUS Status;
    ULONG RealLength;
    SYSTEM_BASIC_INFORMATION SysInfo;
    BOOL FinishNextTime = FALSE;
    BOOL ErrorsFound = FALSE;
    SIZE_T MaxStack = 0;
    SIZE_T TotalStack = 0;
    BOOLEAN WasEnabled;

    printf ("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n");
    printf ("Process stack information \n");
    printf ("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n");

    //
    // SystemBasicInformation
    //

    Status = NtQuerySystemInformation (
                                      SystemBasicInformation,
                                      &SysInfo,
                                      sizeof (SysInfo),
                                      &RealLength);

    if (! NT_SUCCESS(Status)) {
        printf ("NtQuerySystemInformation(Basic): error %08X\n", Status);
        return;
    }

    //
    // SystemProcessInformation
    //

    Status = NtQuerySystemInformation (SystemProcessInformation,
                                       InfoBuffer,
                                       sizeof InfoBuffer,
                                       &RealLength);

    if (! NT_SUCCESS(Status)) {
        printf ("NtQuerySystemInformation(Process): error %08X\n", Status);
        return;
    }

    //
    // Get debug privilege.
    //

    Status = RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE,
                                TRUE,
                                FALSE,
                                &WasEnabled);

    if (! NT_SUCCESS(Status)) {
        printf("Failed to enable debug privilege (%X) \n", Status);
    }

    {
        PSYSTEM_PROCESS_INFORMATION Info = (PSYSTEM_PROCESS_INFORMATION)InfoBuffer;

        printf ("%-15s %-5s %-5s %-4s %-5s %-8s %-5s %-5s\n",
                "",
                "",
                "",
                "",
                "",
                "",
                "Total",
                "Max");

        printf ("%-15s %-5s %-5s %-4s %-5s %-8s %-5s %-5s\n",
                "Process",
                "Id",
                "Sess",
                "Pri",
                "Thrds",
                "Handles",
                "stack",
                "stack");

        printf ("%-15s %-5s %-5s %-4s %-5s %-8s %-5s %-5s\n",
                "",
                "",
                "",
                "",
                "",
                "",
                "Kb",
                "Kb");

        printf ("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n");

        for (FinishNextTime = FALSE ;
            FinishNextTime == FALSE;
            Info = (PSYSTEM_PROCESS_INFORMATION)((ULONG_PTR)Info + Info->NextEntryOffset)) {

            if (Info->NextEntryOffset == 0)
                FinishNextTime = TRUE;

            //
            // User time vs Kernel time.
            //

            ULONG UserPercent, KernelPercent;

            UserPercent = (ULONG)((Info->UserTime.QuadPart) * 100
                                  / (Info->UserTime.QuadPart + Info->KernelTime.QuadPart));
            KernelPercent = 100 - UserPercent;

            //
            // I/O total count.
            //

            LARGE_INTEGER IoTotalCount;

            IoTotalCount.QuadPart = Info->ReadOperationCount.QuadPart
                                    + Info->WriteOperationCount.QuadPart
                                    + Info->OtherOperationCount.QuadPart;

            IoTotalCount.QuadPart /= 1000;

            //
            // Image name (special case the idle process).
            //

            if (Info->ImageName.Buffer == NULL) {

                printf ("%-15s ", "Idle");
            }
            else {

                printf ("%-15ws ", Info->ImageName.Buffer);
            }

            //
            // Compute stack info by iterating all threads in the process.
            //

            GetProcessStackInfo (Info, &MaxStack, &TotalStack, &ErrorsFound);
            
            //
            // Print the stuff.
            //

            printf ("%-5I64u %-5u %-4u %-5u %-8u %-5u %-5u\n",
                    (ULONG64)((ULONG_PTR)(Info->UniqueProcessId)),
                    Info->SessionId,
                    Info->BasePriority,
                    Info->NumberOfThreads,
                    Info->HandleCount,
                    (ULONG)(TotalStack/1024),
                    (ULONG)(MaxStack/1024));

        }
    }

    printf(
        "                                                                      \n"
        " * Total stack: total committed memory used for stacks by all threads \n"
        "       in the process.                                                \n"
        " * Max stack: the biggest committed stack in the process.             \n"
        "                                                                      \n");
}


VOID
GetProcessStackInfo (
    PSYSTEM_PROCESS_INFORMATION Info,
    PSIZE_T MaxSize,
    PSIZE_T TotalSize,
    PBOOL ErrorsFound
    )
{
    ULONG Ti;
    HANDLE Id;
    HANDLE Thread;
    HANDLE Process;
    THREAD_BASIC_INFORMATION ThreadInfo;
    TEB TebInfo;
    SIZE_T BytesRead;
    BOOL ReadResult;
    NTSTATUS Status;
    SIZE_T StackSize;
    BOOLEAN WasEnabled;

    *MaxSize = 0;
    *TotalSize = 0;
    *ErrorsFound = FALSE;

    //
    // Open the process.
    // 

    Process = OpenProcess (PROCESS_VM_READ,
                           FALSE,
                           HandleToUlong(Info->UniqueProcessId));

    if (Process == FALSE) {
        //printf("Failed to open process %p (error %u) \n", Info->UniqueProcessId, GetLastError());
        *ErrorsFound = TRUE;
        return;
    }

    //
    // Iterate all threads in the process and for each determine the
    // thread ID, open the thread and query for TEB address. Finally
    // read user mode stack sizes from the TEB.
    //

    for (Ti = 0; Ti < Info->NumberOfThreads; Ti += 1) {

        Id = ((PSYSTEM_THREAD_INFORMATION)(Info + 1) + Ti)->ClientId.UniqueThread;

        Thread = OpenThread (THREAD_QUERY_INFORMATION, 
                             FALSE, 
                             HandleToUlong(Id));

        if (Thread == NULL) {
            //printf("failed to open thread %u \n", GetLastError());
            *ErrorsFound = TRUE;
            continue;
        }

        Status = NtQueryInformationThread (Thread,
                                           ThreadBasicInformation,
                                           &ThreadInfo,
                                           sizeof ThreadInfo,
                                           NULL);


        if (!NT_SUCCESS(Status)) {
            //printf("query thread failed with %X \n", Status);
            *ErrorsFound = TRUE;
            CloseHandle (Thread);
            continue;
        }

        ReadResult = ReadProcessMemory (Process,
                                        ThreadInfo.TebBaseAddress,
                                        &TebInfo,
                                        sizeof TebInfo,
                                        &BytesRead);

        if (ReadResult == FALSE) {
            //printf("failed to read teb with %u \n", GetLastError());
            *ErrorsFound = TRUE;
            CloseHandle (Thread);
            continue;
        }

        StackSize = (SIZE_T)(TebInfo.NtTib.StackBase) - (SIZE_T)(TebInfo.NtTib.StackLimit);

        *TotalSize += StackSize;

        if (StackSize > *MaxSize) {
            *MaxSize = StackSize;
        }

        CloseHandle (Thread);
    }

    CloseHandle (Process);
}


//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////

//
// Function:
//
//     DebugMessage
//
// Description:
//
//     Printf like function that prints a message into debugger.
//

void __cdecl DebugMessage (char *fmt, ...)
{
    va_list prms;
    char Buffer [1024];

    va_start (prms, fmt);
    vsprintf (Buffer, fmt, prms);
    OutputDebugString (Buffer);
    va_end (prms);
}


//
// end of module: systrack.cxx
//