/******************************************************************************

Copyright (c) 2000 Microsoft Corporation

Module Name:
    Perf.cpp

Abstract:
    This file contains debugging stuff.

Revision History:
    Davide Massarenti   (dmassare) 01/17/2000
        created

******************************************************************************/

#include "stdafx.h"

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

#ifdef HSS_PERFORMANCEDUMP

#include <ProjectConstants.h>
#include <MPC_utils.h>

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

class PerfLog
{
public:
    MPC::string   szText;
    LARGE_INTEGER liTime;
    DWORDLONG     ullTotal;
};

typedef std::list< PerfLog >        PerfResults;
typedef PerfResults::iterator       PerfResultsIter;
typedef PerfResults::const_iterator PerfResultsIterConst;

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

static PerfResults                      s_lst;
static LARGE_INTEGER                    s_liStartOfPerf;
static LARGE_INTEGER                    s_liAdjust;
static MPC::CComSafeAutoCriticalSection s_csec;

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

#define DEBUG_PERF_EVENTS DEBUG_PERF_EVENTS_IN | DEBUG_PERF_EVENTS_OUT

static const MPC::StringToBitField s_arrMap[] =
{
    { L"BASIC"        , DEBUG_PERF_BASIC        , DEBUG_PERF_BASIC        , -1 },
    { L"PROTOCOL"     , DEBUG_PERF_PROTOCOL     , DEBUG_PERF_PROTOCOL     , -1 },
    { L"PROTOCOL_READ", DEBUG_PERF_PROTOCOL_READ, DEBUG_PERF_PROTOCOL_READ, -1 },
    { L"MARS"         , DEBUG_PERF_MARS         , DEBUG_PERF_MARS         , -1 },
    { L"EVENTS_IN"    , DEBUG_PERF_EVENTS_IN    , DEBUG_PERF_EVENTS_IN    , -1 },
    { L"EVENTS_OUT"   , DEBUG_PERF_EVENTS_OUT   , DEBUG_PERF_EVENTS_OUT   , -1 },
    { L"EVENTS"       , DEBUG_PERF_EVENTS       , DEBUG_PERF_EVENTS       , -1 },
    { L"PROXIES"      , DEBUG_PERF_PROXIES      , DEBUG_PERF_PROXIES      , -1 },
    { L"QUERIES"      , DEBUG_PERF_QUERIES      , DEBUG_PERF_QUERIES      , -1 },

    { L"CACHE_L1"     , DEBUG_PERF_CACHE_L1     , DEBUG_PERF_CACHE_L1     , -1 },
    { L"CACHE_L2"     , DEBUG_PERF_CACHE_L2     , DEBUG_PERF_CACHE_L2     , -1 },

    { L"HELPSVC"      , DEBUG_PERF_HELPSVC      , DEBUG_PERF_HELPSVC      , -1 },
    { L"HELPHOST"     , DEBUG_PERF_HELPHOST     , DEBUG_PERF_HELPHOST     , -1 },

    { L"ALL"          , -1                      , -1                      , -1 },
    { NULL                                                                     }
};

static const WCHAR s_Key  [] = HC_REGISTRY_BASE L"\\Perf";
static const WCHAR s_Value[] = L"Mask";

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

static DWORD         s_mode;
static LARGE_INTEGER s_liEnter;
static LARGE_INTEGER s_liExit;
static CHAR          s_rgLineA[4096];
static WCHAR         s_rgLineW[4096];

static void StopCounter()
{
    s_csec.Lock();

    ::QueryPerformanceCounter( &s_liEnter );
    if(s_liStartOfPerf.QuadPart == 0)
    {
        bool fFound;

        s_liStartOfPerf = s_liEnter;
        s_mode          = DEBUG_PERF_BASIC;

        if(FAILED(MPC::RegKey_Value_Read( s_mode, fFound, s_Key, s_Value )) || fFound == false)
        {
            MPC::wstring szValue;

            if(SUCCEEDED(MPC::RegKey_Value_Read( szValue, fFound, s_Key, s_Value )) && fFound)
            {
                DWORD dwMode;

                if(SUCCEEDED(MPC::ConvertStringToBitField( szValue.c_str(), dwMode, s_arrMap, true )) && dwMode)
                {
                    s_mode = dwMode;
                }
            }
        }
    }
}

static void RestartCounter()
{
    ::QueryPerformanceCounter( &s_liExit );

    s_liAdjust.QuadPart += s_liExit.QuadPart - s_liEnter.QuadPart;

    s_csec.Unlock();
}


static void DEBUG_AppendPerf( LPCSTR szMessage )
{
    PerfResultsIter it;
    DWORDLONG       ullTotal = 0;

    {
        HANDLE pHeaps[256];
        DWORD  dwNumberOfHeaps = ::GetProcessHeaps( ARRAYSIZE(pHeaps), pHeaps );

        for(DWORD i=0; i<dwNumberOfHeaps; i++)
        {
            PROCESS_HEAP_ENTRY entry; entry.lpData = NULL;

            while(::HeapWalk( pHeaps[i], &entry ))
            {
                if(entry.wFlags & PROCESS_HEAP_ENTRY_BUSY)
                {
                    //
                    // Allocated block. Add both it's size and its overhead to the total
                    // We want the overhead since it figures into the total required
                    // commitment.
                    //
                    ullTotal += (entry.cbData + entry.cbOverhead);
                }
            }
        }
    }
    it = s_lst.insert( s_lst.end() );

    it->szText          = szMessage;
    it->liTime.QuadPart = s_liEnter.QuadPart - s_liStartOfPerf.QuadPart - s_liAdjust.QuadPart;

    it->ullTotal = ullTotal;
}

void DEBUG_AppendPerf( DWORD  mode         ,
                       LPCSTR szMessageFmt ,
                       ...                 )
{
    StopCounter();

    if(mode & s_mode)
    {
        va_list arglist;
        int     iLen;

        //
        // Format the log line.
        //
        va_start( arglist, szMessageFmt );
        iLen = _vsnprintf( s_rgLineA, MAXSTRLEN(s_rgLineA), szMessageFmt, arglist );
        va_end( arglist );

        //
        // Is the arglist too big for us?
        //
        if(iLen < 0)
        {
            iLen = MAXSTRLEN(s_rgLineA);
        }
        s_rgLineA[iLen] = 0;


        DEBUG_AppendPerf( s_rgLineA );
    }

    RestartCounter();
}

void DEBUG_AppendPerf( DWORD   mode         ,
                       LPCWSTR szMessageFmt ,
                       ...                  )
{
    StopCounter();

    if(mode & s_mode)
    {
        USES_CONVERSION;

        va_list arglist;
        int     iLen;

        //
        // Format the log line.
        //
        va_start( arglist, szMessageFmt );
        iLen = _vsnwprintf( s_rgLineW, MAXSTRLEN(s_rgLineW), szMessageFmt, arglist );
        va_end( arglist );

        //
        // Is the arglist too big for us?
        //
        if(iLen < 0)
        {
            iLen = MAXSTRLEN(s_rgLineW);
        }
        s_rgLineW[iLen] = 0;


        DEBUG_AppendPerf( W2A(s_rgLineW) );
    }

    RestartCounter();
}

void DEBUG_DumpPerf( LPCWSTR szFile )
{
    HANDLE          hFile;
    SYSTEMTIME      st;
    DWORD           dwWritten;
    LARGE_INTEGER   liFreq;
    LARGE_INTEGER*  pliPrev = NULL;
    MPC::wstring    strFile = szFile; MPC::SubstituteEnvVariables( strFile );
    PerfResultsIter it;
    int             len;
    double          scale;


    ::QueryPerformanceFrequency( &liFreq ); scale = (double)liFreq.QuadPart / 1E6;

    //
    // Calc max entry length.
    //
    for(len=0,it=s_lst.begin(); it!=s_lst.end(); it++)
    {
        if(len < it->szText.size())
        {
            len = it->szText.size();
        }
    }


    hFile = ::CreateFileW( strFile.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, NULL );
    if(hFile == INVALID_HANDLE_VALUE) goto end;
    ::SetFilePointer( hFile, 0, NULL, FILE_END );

    //
    // Prepend current time.
    //
    ::GetLocalTime( &st );

    sprintf( s_rgLineA, "Performance Dump: %04u/%02u/%02u %02u:%02u:%02u\n", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond );
    if(::WriteFile( hFile, s_rgLineA, strlen( s_rgLineA ), &dwWritten, NULL ) == FALSE) goto end;

    sprintf( s_rgLineA, "=====================================\n" );
    if(::WriteFile( hFile, s_rgLineA, strlen( s_rgLineA ), &dwWritten, NULL ) == FALSE) goto end;

    //
    // Dump all the entries.
    //
    for(it=s_lst.begin(); it!=s_lst.end(); )
    {
        PerfLog& pl = *it++;
        double   t0 =                      (double)pl. liTime.QuadPart          ;
        double   dT = it != s_lst.end() ? ((double)it->liTime.QuadPart - t0) : 0;

        sprintf( s_rgLineA, "%-*s : %9ld us  dT: %9ld us (Mem: %9ld)\n", len, pl.szText.c_str(), (long)(t0 / scale), (long)(dT / scale), (long)pl.ullTotal );

        if(::WriteFile( hFile, s_rgLineA, strlen( s_rgLineA ), &dwWritten, NULL ) == FALSE) goto end;
    }

    sprintf( s_rgLineA, "\n\n\n" );
    if(::WriteFile( hFile, s_rgLineA, strlen( s_rgLineA ), &dwWritten, NULL ) == FALSE) goto end;

end:

    if(hFile != INVALID_HANDLE_VALUE) ::CloseHandle( hFile );
}

#endif