/*++

Copyright (c) 1995 Microsoft Corporation

Module Name:

    reducer.c

Abstract:

    Trace Reducer Tool

Author:

    08-Apr-1998 mraghu

Revision History:

--*/

#ifdef __cplusplus
extern "C"{
#endif

#define _UNICODE
#define UNICODE

#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#include <shellapi.h>
#include <tchar.h>

#include "pdhp.h"

#define MAXSTR       1024
#define MAXLOGFILES    16
#define MAX_BUFFER_SIZE     1048576

#if DBG
ULONG ETAAPITestFlag = 1;

VOID
TestETAApis(
    PTRACE_BASIC_INFO TraceBasicInfo
    );

#endif

void ReducerUsage()
{
    printf("Usage: reducer [Options] <EtlFile1 EtlFile2 ...> | [-h | -help | -?]\n");
    printf("\t-out <filename> Output file name. Default is Workload.txt\n");
    printf("\t-h        \n");
    printf("\t-help        \n");
    printf("\t-?     Display usage information\n");

//    printf("\t-start <time-stamp>        Start Time\n");
//    printf("\t-end   <time-stamp>        End Time\n");
//    printf("\t       <time-stamp> can be found in tracedmp result\n");
//    printf("\n");
//    printf("\t-base                      Original reducer report (default report)\n");
//    printf("\t-file                      Hot File Report\n");
//    printf("\t-pf                        page fault report\n");
//    printf("\t    -summary processes     summary of faults per process (default PF report).\n");
//    printf("\t             modules       summary of faults per module.\n");
//    printf("\t    -process <image name>  rundown for specific process.\n");
//    printf("\t             all\n");
//    printf("\t    -module  <module name> rundown for specific modules.\n");
//    printf("\t             all\n");
//    printf("\t    -sort    ALL           sort by all fault total.\n");
//    printf("\t             HPF           sort by HPF fault total.\n");
//    printf("\t             TF            sort by TF  fault total.\n");
//    printf("\t             DZF           sort by DZF fault total.\n");
//    printf("\t             COW           sort by COW fault total.\n");
//    printf("\n");
//    printf("\tNote: (1) Cannot generate HotFile Report and PageFault Report\n");
//    printf("\t          at the same time\n");
//    printf("\t0x%08X,%d,\n", STATUS_SEVERITY_WARNING, RtlNtStatusToDosError(STATUS_SEVERITY_WARNING));
}

ULONGLONG
ParseTimeString(TCHAR * strTime)
{
#if 0
    CHAR          lstrTime[25];
    PCHAR         strYear, strMonth, strDate,
                  strHour, strMinute, strSecond, strMilliSecond;
    LARGE_INTEGER largeTime;
    FILETIME      localTime, stdTime;
    SYSTEMTIME    sysTime;

    if (strTime == NULL)
        return (ULONGLONG) 0;

    strcpy(lstrTime, strTime);

    strMonth = lstrTime;
    for (strDate = strMonth;
         *strDate && *strDate >= '0' && *strDate <= '9';
         strDate ++);
    *strDate = '\0';
    strDate ++;

    for (strYear = strDate;
         *strYear && *strYear >= '0' && *strYear <= '9';
         strYear ++);
    *strYear = '\0';
    strYear ++;

    for (strHour = strYear;
         *strHour && *strHour >= '0' && *strHour <= '9';
         strHour ++);
    *strHour = '\0';
    strHour ++;

    for (strMinute = strHour;
         *strMinute && *strMinute >= '0' && *strMinute <= '9';
         strMinute ++);
    *strMinute = '\0';
    strMinute ++;

    for (strSecond = strMinute;
         *strSecond && *strSecond >= '0' && *strSecond <= '9';
         strSecond ++);
    *strSecond = '\0';
    strSecond ++;

    for (strMilliSecond = strSecond;
         *strMilliSecond && *strMilliSecond >= '0' && *strMilliSecond <= '9';
         strMilliSecond ++);
    *strMilliSecond = '\0';
    strMilliSecond ++;

    sysTime.wYear         = atoi(strYear);
    sysTime.wMonth        = atoi(strMonth);
    sysTime.wDay          = atoi(strDate);
    sysTime.wHour         = atoi(strHour);
    sysTime.wMinute       = atoi(strMinute);
    sysTime.wSecond       = atoi(strSecond);
    sysTime.wMilliseconds = atoi(strMilliSecond);

    SystemTimeToFileTime(&sysTime, &localTime);
    LocalFileTimeToFileTime(&localTime, &stdTime);
    largeTime.HighPart = stdTime.dwHighDateTime;
    largeTime.LowPart  = stdTime.dwLowDateTime;
    return (ULONGLONG) largeTime.QuadPart;
#else
    ULONGLONG TimeStamp = 0;
    ULONG     i = 0;

    for (i = 0; strTime[i] != '\0'; i ++)
    {
        TimeStamp = TimeStamp * 10 + (strTime[i] - '0');
    }

    return TimeStamp;
#endif
}

VOID
ProcessTrace(
    IN ULONG      LogFileCount,
    IN LPCTSTR   * LogFileName,
    IN ULONGLONG  StartTime,
    IN ULONGLONG  EndTime,
    IN ULONGLONG  DSStartTime,
    IN ULONGLONG  DSEndTime,
    IN ULONG      MoreFlags,
    IN PVOID      pUserContext,
    IN LPCTSTR      pOutFileName
    )
{
    // Call TraceLib and process it. 
    //
    TRACE_BASIC_INFO TraceBasicInfo;

    memset(&TraceBasicInfo, 0, sizeof(TRACE_BASIC_INFO));
    TraceBasicInfo.Flags        = TRACE_REDUCE | MoreFlags;
    TraceBasicInfo.LogFileName  = LogFileName; 
    TraceBasicInfo.LogFileCount = LogFileCount;
    TraceBasicInfo.pUserContext = pUserContext;
    TraceBasicInfo.StartTime    = StartTime;
    TraceBasicInfo.EndTime      = EndTime;
    TraceBasicInfo.DSStartTime  = DSStartTime;
    TraceBasicInfo.DSEndTime    = DSEndTime;

    TraceBasicInfo.ProcFileName = pOutFileName;
    InitTraceContext(&TraceBasicInfo);

#if DBG
    if (ETAAPITestFlag) {
        TestETAApis( & TraceBasicInfo );
    }

#endif


    DeinitTraceContext(&TraceBasicInfo);
}
    
int __cdecl main(int argc ,char * argv[])
{
    WCHAR     TraceLogFile[MAXSTR];
    WCHAR    PerfLogFile[MAXSTR];
    BOOLEAN  bTrace  = FALSE;
    LPTSTR   EvmFile[MAXLOGFILES];
    ULONGLONG StartTime = 0, EndTime = 0;
    ULONG    i;
    ULONG    LogFileCount = 0;
    LPTSTR *targv;
#ifdef UNICODE
    LPTSTR *cmdargv;
#endif

    ULONG               flagsMore    = 0;
    PVOID               pUserContext = NULL;
    CPD_USER_CONTEXT_MM UserContextMM;


#ifdef UNICODE
    if ((cmdargv = CommandLineToArgvW(
                        GetCommandLineW(),  // pointer to a command-line string
                        &argc               // receives the argument count
                        )) == NULL)
    {
        return(GetLastError());
    };
    targv = cmdargv ;
#else
    targv = argv;
#endif

    UserContextMM.reportNow  = REPORT_SUMMARY_PROCESS;
    UserContextMM.sortNow    = REPORT_SORT_ALL;
    UserContextMM.strImgName = NULL;
    memset(&TraceLogFile, 0, sizeof(WCHAR) * MAXSTR); 

    while (--argc > 0)
    {
        ++targv;
        if (**targv == '-' || **targv == '/')
        { 
            ** targv = '-';
            if (!_tcsicmp(targv[0], _T("-out")))
            {
                if (argc > 1)
                {
                    TCHAR TempStr[MAXSTR];

                    _tcscpy(TempStr, targv[1]);
                    ++targv; --argc;
                    _tfullpath(TraceLogFile, TempStr, MAXSTR);
                    printf("Setting output file to: '%ws'\n", TraceLogFile);
                    bTrace = TRUE;
                }
            }
            else if (!_tcsicmp(targv[0], _T("-start")))
            {
                if (argc > 1)
                {
                    flagsMore |= TRACE_DS_ONLY | TRACE_LOG_REPORT_BASIC;
                    StartTime  = ParseTimeString(targv[1]);
                    argc --; targv ++;
                }
            }
            else if (!_tcsicmp(targv[0], _T("-end")))
            {
                if (argc > 1)
                {
                    flagsMore |= TRACE_DS_ONLY | TRACE_LOG_REPORT_BASIC;
                    EndTime    = ParseTimeString(targv[1]);
                    argc --; targv ++;
                }
            }
            else if (!_tcsicmp(targv[0], _T("-base")))
            {
                flagsMore   |= TRACE_LOG_REPORT_BASIC;
            }
            else if (!_tcsicmp(targv[0], _T("-spooler")))
            {
                flagsMore   |= TRACE_LOG_REPORT_BASIC;
                flagsMore   |= TRACE_SPOOLER;
            }
            else if (!_tcsicmp(targv[0], _T("-total")))
            {
                flagsMore   |= TRACE_LOG_REPORT_TOTALS;
            }
            else if (!_tcsicmp(targv[0], _T("-file")))
            {
                flagsMore   |= TRACE_LOG_REPORT_FILE;
/*                if (argc > 1 && targv[1][0] >= '0' && targv[1][0] <= '9')
                {
                    pUserContext = UlongToPtr(atoi(targv[1]));
                    argc --; targv ++;
                }
                else
                {
                    pUserContext = UlongToPtr(DEFAULT_FILE_REPORT_SIZE);
                }
*/
            }
            else if (!_tcsicmp(targv[0], _T("-hpf")))
            {
                flagsMore   |= TRACE_LOG_REPORT_HARDFAULT;
            }
            else if (!_tcsicmp(targv[0], _T("-pf")))
            {
                flagsMore   |= TRACE_LOG_REPORT_MEMORY;
                pUserContext = (PVOID) & UserContextMM;
            }
            else if (!_tcsicmp(targv[0], _T("-summary")))
            {
                if (argc > 1)
                {
                    flagsMore   |= TRACE_LOG_REPORT_MEMORY;
                    pUserContext = (PVOID) & UserContextMM;

                    if (!_tcsicmp(targv[1], _T("processes")))
                    {
                        argc --; targv ++;
                        UserContextMM.reportNow = REPORT_SUMMARY_PROCESS;
                    }
                    else if (!_tcsicmp(targv[1], _T("modules")))
                    {
                        argc --; targv ++;
                        UserContextMM.reportNow = REPORT_SUMMARY_MODULE;
                    }
                }
            }

            else if (!_tcsicmp(targv[0], _T("-process")))
            {
                flagsMore   |= TRACE_LOG_REPORT_MEMORY;
                pUserContext = (PVOID) & UserContextMM;
                UserContextMM.reportNow = REPORT_LIST_PROCESS;

                if ((argc > 1) && (targv[1][0] != '-' || targv[1][0] != '/'))
                {
                    if (_tcsicmp(targv[1], _T("all")))
                    {
                        UserContextMM.strImgName =
                                malloc(sizeof(TCHAR) * (_tcslen(targv[1]) + 1));
                        if (UserContextMM.strImgName)
                        {
                            _tcscpy(UserContextMM.strImgName, targv[1]);
                        }
                    }
                    argc --; targv ++;
                }
            }
            else if (!_tcsicmp(targv[0], _T("-module")))
            {
                flagsMore   |= TRACE_LOG_REPORT_MEMORY;
                pUserContext = (PVOID) & UserContextMM;
                UserContextMM.reportNow = REPORT_LIST_MODULE;

                if ((argc > 1) && (targv[1][0] != '-' || targv[1][0] != '/'))
                {
                    if (_tcsicmp(targv[1], _T("all")))
                    {
                        UserContextMM.strImgName =
                                malloc(sizeof(TCHAR) * (_tcslen(targv[1]) + 1));
                        if (UserContextMM.strImgName)
                        {
                            _tcscpy(UserContextMM.strImgName, targv[1]);
                        }
                    }
                    argc --; targv ++;
                }
            }

            else if (!_tcsicmp(targv[0], _T("-sort")))
            {
                flagsMore   |= TRACE_LOG_REPORT_MEMORY;
                pUserContext = (PVOID) & UserContextMM;
 
                if ((argc > 1) && (targv[1][0] != '-' || targv[1][0] != '/'))
                {
                    if (!_tcsicmp(targv[1], _T("hpf")))
                    {
                        UserContextMM.sortNow = REPORT_SORT_HPF;
                    }
                    else if (!_tcsicmp(targv[1], _T("tf")))
                    {
                        UserContextMM.sortNow = REPORT_SORT_TF;
                    }
                    else if (!_tcsicmp(targv[1], _T("dzf")))
                    {
                        UserContextMM.sortNow = REPORT_SORT_DZF;
                    }
                    else if (!_tcsicmp(targv[1], _T("cow")))
                    {
                        UserContextMM.sortNow = REPORT_SORT_COW;
                    }
                    else
                    {
                        UserContextMM.sortNow = REPORT_SORT_ALL;
                    }
                    argc --; targv ++;
                }
            }
            else
            {
                goto Usage;
            }
        }
        else
        {
            LPTSTR pLogFile;

            pLogFile = malloc(sizeof(TCHAR) * MAXSTR);
            RtlZeroMemory((char *) pLogFile, sizeof(TCHAR) * MAXSTR);
            EvmFile[LogFileCount] = pLogFile;
            _tcscpy(EvmFile[LogFileCount ++], targv[0]);
            bTrace = TRUE;

            printf("LogFile %ws\n", (char *) EvmFile[LogFileCount - 1]);
        }
    }

    if (LogFileCount == 0)
    {
        goto Usage;
    }

    if (flagsMore == 0)
    {
        flagsMore |= TRACE_LOG_REPORT_BASIC;
    }

    if (   (flagsMore & TRACE_LOG_REPORT_MEMORY)
        && (flagsMore & TRACE_LOG_REPORT_FILE))
    {
        printf("Error: cannot generate HotFile report and PageFault report at the same time.\n");
        goto Cleanup;
    }

    if (bTrace)
    {
        ProcessTrace(LogFileCount, EvmFile, (ULONGLONG) 0, (ULONGLONG) 0,
                StartTime, EndTime, flagsMore, pUserContext, TraceLogFile);
    }

    for (i=0; i < LogFileCount; i++) 
    {
        free((char*)EvmFile[i]);
    }

    if (UserContextMM.strImgName)
    {
        free(UserContextMM.strImgName);
    }

Cleanup:
#ifdef UNICODE
    GlobalFree(cmdargv);
#endif
    return 0;

Usage:
    ReducerUsage();
    goto Cleanup;
}


#if DBG

VOID
PrintHeader(
    FILE* f,
    TRACEINFOCLASS CurrentClass,
    TRACEINFOCLASS RootClass,
    BOOLEAN bDrillDown,
    LPCWSTR InstanceName
    )
{


    if (bDrillDown) {
        fprintf(f, "-------------------------------------------\n");
        fprintf(f, "RootClass: %d  Instance %ws\n", RootClass, InstanceName);
        fprintf(f, "-------------------------------------------\n");
    }


    switch(CurrentClass) {

        case TraceProcessInformation:

            fprintf(f, "Trace Process Information\n");
            fprintf(f, "------------------------\n");
            fprintf(f, "\nPID     Name       Image                     UCpu  KCpu  ReadIO   WriteIO\n\n");
            break;

        case TraceDiskInformation:
            fprintf(f, "Trace Disk Information\n");
            fprintf(f, "----------------------\n");
            fprintf(f, "\nDiskId   Name  ReadIO   WriteIO\n\n");
            break;

        case TraceThreadInformation: 
            fprintf(f, "Trace Thread Information\n");
            fprintf(f, "------------------------\n");
            fprintf(f, "\nTID   PID   UCpu   KCpu   ReadIO   WriteIO  \n\n");
            break;
        case TraceFileInformation:
            fprintf(f, "Trace File Information\n");
            fprintf(f, "------------------------\n");
            fprintf(f, "\nFileName                      ReadIO   WriteIO  \n\n");
            break;

    }
    
}


VOID
PrintProcessInfo(
    FILE* f,
    PTRACE_PROCESS_INFO pProcessInfo
    )
{
    fprintf(f, "%4d   %-10ws  %-10ws    %5d  %5d   %5d   %5d\n", 
            pProcessInfo->PID,
            pProcessInfo->UserName,
            pProcessInfo->ImageName,
            pProcessInfo->UserCPU,
            pProcessInfo->KernelCPU,
            pProcessInfo->ReadCount,
            pProcessInfo->WriteCount);
}

VOID
PrintThreadInfo(
    FILE* f,
    PTRACE_THREAD_INFO pThreadInfo
    )
{
    fprintf(f, "%4x  \n", pThreadInfo->ThreadId);

}

VOID
PrintDiskInfo(
    FILE* f,
    PTRACE_DISK_INFO pDiskInfo
    )
{
    fprintf(f, "%d         %-10ws    %5d     %5d \n",
            pDiskInfo->DiskNumber,
            pDiskInfo->DiskName,
            pDiskInfo->ReadCount,
            pDiskInfo->WriteCount);
}


VOID
PrintFileInfo(
        FILE* f,
        PTRACE_FILE_INFO pFileInfo
        )
{
    fprintf(f, "%ws                    %5d     %5d\n", 
            pFileInfo->FileName,
            pFileInfo->ReadCount,
            pFileInfo->WriteCount
            );
}


VOID
TestETAApis(
    PTRACE_BASIC_INFO TraceBasicInfo
    )
{
    ULONG Status;
    ULONG OutLength;
    PTRACE_PROCESS_INFO pProcessInfo;
    PTRACE_DISK_INFO pDiskInfo;
    PTRACE_FILE_INFO pFileInfo;
    PTRACE_THREAD_INFO pThreadInfo;
    char*  LargeBuffer1;
    ULONG CurrentBufferSize;
    BOOLEAN Done, FinshPrinting, bDrillDown;
    TRACEINFOCLASS CurrentClass, RootClass;
    ULONG TotalOffset;
    FILE* f;
    int i = 0;
    LPWSTR InstanceName;
    WCHAR Name[MAXSTR+1];

    InstanceName = (LPWSTR)&Name;

    LargeBuffer1 = VirtualAlloc(NULL,
                                MAX_BUFFER_SIZE,
                                MEM_RESERVE,
                                PAGE_READWRITE);

    if (LargeBuffer1 == NULL) {
        return;
    }
    CurrentBufferSize = 10*8192;

    if (VirtualAlloc(LargeBuffer1,
                    81920,
                    MEM_COMMIT,
                    PAGE_READWRITE) == NULL) {
        return;
    }


    f = _wfopen(L"TraceAPI.rpt", L"w");

    if (f == NULL) {
        VirtualFree(LargeBuffer1, 0, MEM_RELEASE);
        return;
    }

    CurrentClass = TraceProcessInformation;
    RootClass = TraceProcessInformation;
    Done = FALSE;
    bDrillDown = FALSE;

    while (!Done) {

Retry1:
        if (!bDrillDown) {

            Status = TraceQueryAllInstances(
                            CurrentClass,
                            LargeBuffer1,
                            CurrentBufferSize,
                            &OutLength);
        }
        else {
            Status = TraceDrillDown(
                            RootClass,
                            InstanceName,
                            CurrentClass,
                            LargeBuffer1,
                            CurrentBufferSize,
                            &OutLength
                            );
        }
    
        if (Status == ERROR_MORE_DATA) {
            CurrentBufferSize += OutLength;
            if (VirtualAlloc(LargeBuffer1,
                             CurrentBufferSize,
                             MEM_COMMIT,
                             PAGE_READWRITE) == NULL) {
                 return;
            }
            goto Retry1;
        }

        if (Status != ERROR_SUCCESS) {
            Done = TRUE;
            break;
        }

        //
        // Print Header
        //
        PrintHeader(f, CurrentClass, RootClass, bDrillDown, InstanceName);

        //
        // Walk the Process List and Print report. 
        //

        TotalOffset = 0;
        pProcessInfo = (TRACE_PROCESS_INFO *) LargeBuffer1;
        pThreadInfo = (TRACE_THREAD_INFO *) LargeBuffer1;
        pFileInfo = (TRACE_FILE_INFO *) LargeBuffer1;
        pDiskInfo = (TRACE_DISK_INFO *) LargeBuffer1;

        FinshPrinting = FALSE; 
        while (!FinshPrinting) {

            switch(CurrentClass) {
                case TraceProcessInformation:

                    PrintProcessInfo(f, pProcessInfo);
                    if (pProcessInfo->NextEntryOffset == 0) {
                        FinshPrinting = TRUE;
                        break;
                    }
                    TotalOffset += pProcessInfo->NextEntryOffset;
                    pProcessInfo = (TRACE_PROCESS_INFO *) 
                                   &LargeBuffer1[TotalOffset];
                    break;
                case TraceDiskInformation:
                    PrintDiskInfo(f, pDiskInfo);
                    if (pDiskInfo->NextEntryOffset == 0) {
                        FinshPrinting = TRUE;
                        break;
                    }
                    TotalOffset += pDiskInfo->NextEntryOffset;
                    pDiskInfo = (TRACE_DISK_INFO *)
                                    &LargeBuffer1[TotalOffset];
                default:
                    FinshPrinting = TRUE;
                    break;
            }

            if (TotalOffset == 0) 
                break;
        }


        if (!bDrillDown) {
            switch(CurrentClass) {

                case TraceProcessInformation:
                    CurrentClass = TraceDiskInformation;
                    break;
                case TraceThreadInformation:
                    CurrentClass = TraceDiskInformation;
                    break;
                case TraceDiskInformation:
                    RootClass = TraceProcessInformation;
                    CurrentClass = TraceProcessInformation;
                    bDrillDown = TRUE;
                    break;

                case TraceFileInformation:
                    Done = TRUE;
                    break;
                default:
                    Done = TRUE;
                    break;
            }
        }


        if (bDrillDown) {
            switch(RootClass) {
                case TraceProcessInformation:
                    wcscpy(InstanceName, L"\\\\NTDEV\\mraghu");
                    if (CurrentClass == TraceProcessInformation)  {
                        CurrentClass = TraceFileInformation;
                    }
                    else if (CurrentClass == TraceFileInformation) {
                        CurrentClass = TraceDiskInformation;
                    }
                    else {
                        RootClass = TraceDiskInformation;
                        CurrentClass = TraceProcessInformation;
                        wcscpy (InstanceName, L"Disk1");
                    }
                    break;

                case TraceDiskInformation:
                        if (CurrentClass == TraceProcessInformation) {
                            CurrentClass = TraceFileInformation;
                            wcscpy (InstanceName, L"Disk1");
                        }
                        else {
                            Done = TRUE;
                        }

                       break; 


                case TraceFileInformation:
                    

                default: 
                    Done = TRUE;
                    break;
            }

        }
    }

    fclose (f);

    VirtualFree(LargeBuffer1, 0, MEM_RELEASE);

}
    
#endif



#ifdef __cplusplus
}
#endif