/*===================== begin_copyright_notice ==================================

Copyright (c) 2017 Intel Corporation

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


======================= end_copyright_notice ==================================*/

#include "common/Stats.hpp"
#include "Compiler/CodeGenPublic.h"
#include "common/debug/Dump.hpp"

#include <iStdLib/File.h>
#include <iStdLib/Timestamp.h>

#include "common/LLVMWarningsPush.hpp"
#include <llvm/Support/FormattedStream.h>
#include <llvm/Support/raw_os_ostream.h>
#include <llvm/Support/Debug.h>
#include "common/LLVMWarningsPop.hpp"

#include "common/secure_string.h"
#include <fstream>
#include <algorithm>
#include <iomanip>
#include <sstream>
#include <iostream>
#include "Probe/Assertion.h"

#if GET_TIME_STATS
// Functions exposed by VISA lib API
extern "C" int64_t getTimerTicks(unsigned int idx);
extern "C" double getTimerCounts(unsigned int idx);
extern "C" void getTimerNames(char* timerName, unsigned int idx);
extern "C" unsigned int getTimerHits(unsigned int idx);
extern "C" unsigned int getTotalTimers();
#endif

namespace {
    static const unsigned g_cIndentSZ = 4; //<! number of spaces to indent by
}

const char* g_cShaderStatItems[STATS_MAX_SHADER_STATS_ITEMS+1] =
{
#define DEFINE_SHADER_STAT( enumName, stringName ) stringName,
#include "shaderStats.def"
#undef DEFINE_SHADER_STAT
};

#if GET_SHADER_STATS

int ShaderStats::getShaderStats( SHADER_STATS_ITEMS compileInterval )
{
    IGC_ASSERT(0 <= compileInterval);
    IGC_ASSERT(compileInterval < STATS_MAX_SHADER_STATS_ITEMS);
    return m_CompileShaderStats[compileInterval];
}

void ShaderStats::printSumShaderStats()
{
    std::string outputFile = IGC::Debug::GetShaderOutputFolder() + std::string("\\SQM\\")+"shaderStatSum.csv";
    std::string sqm_out = IGC::Debug::GetShaderOutputFolder() + std::string("\\SQM\\") + "sqmStats.txt";
    bool fileExist = 0, fileExist_sqm=0;

    FILE* fp = fopen(outputFile.c_str(), "r");
    if( fp )
    {
        fileExist = 1;
        fclose(fp);
    }

    FILE* fileName = fopen(outputFile.c_str(), "a");

    FILE* fp_sqm = fopen(sqm_out.c_str(), "r");
    if (fp_sqm)
    {
        fileExist_sqm = 1;
        fclose(fp_sqm);
    }

    FILE* fileName_sqm = fopen(sqm_out.c_str(), "a");

    if( !fileExist && fileName )
    {
        fprintf(fileName, "test label, shader type, shader count,");
        for (int i=0;i<STATS_MAX_SHADER_STATS_ITEMS;i++)
        {
            fprintf(fileName, "%s,", g_cShaderStatItems[i] );
        }
        fprintf(fileName, "\n");
    }

    if (fileName && m_totalShaderCount!=0)
    {
        fprintf(fileName, "%s,", IGC::Debug::GetShaderCorpusName() );
        fprintf(fileName, "%s,", ShaderTypeString[(int)m_shaderType] );
        fprintf(fileName, "%d,", m_totalShaderCount);
        for (int i=0;i<STATS_MAX_SHADER_STATS_ITEMS;i++)
        {
            fprintf(fileName, "%d,", m_CompileShaderStats[i] );
        }
        fprintf(fileName, "\n");

        fclose(fileName);
    }

    bool printToConsole = true;
    if (printToConsole && m_totalShaderCount!=0)
    {
        fprintf(fileName_sqm, "\n%d %s shaders\n", m_totalShaderCount, ShaderTypeString[(int)m_shaderType]);
        printf("\n%d %s shaders\n", m_totalShaderCount, ShaderTypeString[(int)m_shaderType]);
        if( m_CompileShaderStats[STATS_ISA_INST_COUNT] != 0 )
        {
            fprintf(fileName_sqm, "total SIMD8  isa count = %d\n", m_CompileShaderStats[STATS_ISA_INST_COUNT]);
            printf("total SIMD8  isa count = %d\n", m_CompileShaderStats[STATS_ISA_INST_COUNT] );
        }
        if( m_CompileShaderStats[STATS_ISA_INST_COUNT_SIMD16] != 0 )
        {
            fprintf(fileName_sqm, "total SIMD16 isa count = %d\n", m_CompileShaderStats[STATS_ISA_INST_COUNT_SIMD16]);
            printf("total SIMD16 isa count = %d\n", m_CompileShaderStats[STATS_ISA_INST_COUNT_SIMD16] );
        }
        if( m_CompileShaderStats[STATS_ISA_INST_COUNT_SIMD32] != 0 )
        {
            fprintf(fileName_sqm, "total SIMD32 isa count = %d\n", m_CompileShaderStats[STATS_ISA_INST_COUNT_SIMD32]);
            printf("total SIMD32 isa count = %d\n", m_CompileShaderStats[STATS_ISA_INST_COUNT_SIMD32] );
        }
        if (m_CompileShaderStats[STATS_ISA_SPILL8] != 0)
        {
            fprintf(fileName_sqm, "total SIMD8 spill count = %d\n", m_CompileShaderStats[STATS_ISA_SPILL8]);
            printf("total SIMD8 spill count = %d\n", m_CompileShaderStats[STATS_ISA_SPILL8]);
        }
        if (m_CompileShaderStats[STATS_ISA_SPILL16] != 0)
        {
            fprintf(fileName_sqm, "total SIMD16 spill count = %d\n", m_CompileShaderStats[STATS_ISA_SPILL16]);
            printf("total SIMD16 spill count = %d\n", m_CompileShaderStats[STATS_ISA_SPILL16]);
        }
        if (m_CompileShaderStats[STATS_ISA_SPILL32] != 0)
        {
            fprintf(fileName_sqm,"total SIMD32 spill count = %d\n", m_CompileShaderStats[STATS_ISA_SPILL32]);
            printf("total SIMD32 spill count = %d\n", m_CompileShaderStats[STATS_ISA_SPILL32]);
        }
        fprintf(fileName_sqm, "total SIMD8  shaders = %d\n", m_TotalSimd8);
        fprintf(fileName_sqm, "total SIMD16 shaders = %d\n", m_TotalSimd16);
        fprintf(fileName_sqm, "total SIMD32 shaders = %d\n", m_TotalSimd32);
        printf("total SIMD8  shaders = %d\n", m_TotalSimd8);
        printf("total SIMD16 shaders = %d\n", m_TotalSimd16);
        printf("total SIMD32 shaders = %d\n", m_TotalSimd32);
    }
    fclose(fileName);
}

void ShaderStats::printShaderStats( ShaderHash hash, ShaderType shaderType, const std::string &postFix)
{
    const std::string outputFilePath = IGC::Debug::GetShaderOutputFolder() + std::string("\\SQM\\") + IGC::Debug::GetShaderCorpusName() + "ShaderStats.csv";
    const char *outputFile = outputFilePath.c_str();

    bool fileExist = 0;

    FILE* fp = fopen(outputFile, "r");
    if (fp)
    {
        fileExist = 1;
        fclose(fp);
    }

    FILE* fileName = fopen(outputFile, "a");

    if (!fileExist)
    {
        fprintf(fileName, "shader,");
        for (int i = 0; i<STATS_MAX_SHADER_STATS_ITEMS; i++)
        {
            fprintf(fileName, "%s,", g_cShaderStatItems[i]);
        }
        fprintf(fileName, "\n");
    }

    std::string asmFileName;

        asmFileName =
            IGC::Debug::DumpName(IGC::Debug::GetShaderOutputName())
            .Type(shaderType)
            .Hash(hash)
            .Extension("asm")
            .str();
        if (asmFileName.find_last_of("\\") != std::string::npos)
        {
            asmFileName = asmFileName.substr(asmFileName.find_last_of("\\") + 1, asmFileName.size());
        }


    fprintf(fileName, "%s,", asmFileName.c_str());
    for (int i = 0; i<STATS_MAX_SHADER_STATS_ITEMS; i++)
    {
        fprintf(fileName, "%d,", m_CompileShaderStats[i]);
    }

    fprintf(fileName, "\n");
    fclose(fileName);
}

void ShaderStats::printOpcodeStats(ShaderHash hash, ShaderType shaderType, const std::string &postFix)
{
    /*const std::string opcodeFilePath = IGC::Debug::GetShaderOutputFolder() + std::string("\\SQM\\") + IGC::Debug::GetShaderCorpusName() + "OpcodeShaderStats.csv";
    const char *opcodeFile = opcodeFilePath.c_str();

    const std::string targetUnitFilePath = IGC::Debug::GetShaderOutputFolder() + std::string("\\SQM\\") + IGC::Debug::GetShaderCorpusName() + "TargetUnitShaderStats.csv";
    const char *targetUnitFile = targetUnitFilePath.c_str();

    const std::string listFilePath = IGC::Debug::GetShaderOutputFolder() + std::string("\\SQM\\") + IGC::Debug::GetShaderCorpusName() + "ShadersList.txt";
    const char *listUnitFile = listFilePath.c_str();

    FILE* opcodeFileName = fopen(opcodeFile, "a");
    FILE* targetUnitFileName = fopen(targetUnitFile, "a");
    FILE* listUnitFileName = fopen(listUnitFile, "a");

    std::string asmFileName;
    if (shaderType == ShaderType::OPENCL_SHADER)
    {
        asmFileName =
            IGC::Debug::DumpName(IGC::Debug::GetShaderOutputName())
            .Type(shaderType)
            .Hash(hash)
            .PostFix(postFix)
            .Extension("asm")
            .str();
        if (asmFileName.find_last_of("\\") != std::string::npos)
        {
            asmFileName = asmFileName.substr(asmFileName.find_last_of("\\") + 1, asmFileName.size());
        }
    }
    else
    {
        asmFileName =
            IGC::Debug::DumpName(IGC::Debug::GetShaderOutputName())
            .Type(shaderType)
            .Hash(hash)
            .Extension("asm")
            .str();
        if (asmFileName.find_last_of("\\") != std::string::npos)
        {
            asmFileName = asmFileName.substr(asmFileName.find_last_of("\\") + 1, asmFileName.size());
        }
    }


    fprintf(opcodeFileName, "%s","");
    fclose(opcodeFileName);

    fprintf(targetUnitFileName, "%s","");
    fclose(targetUnitFileName);

    fprintf(listUnitFileName, "%s\n", asmFileName.c_str());
    fclose(listUnitFileName);
    ?*/
}

void ShaderStats::parseIsaShader( ShaderHash hash, ShaderType shaderType, SIMDMode simd )
{

    std::string line, instStr;

    std::string asmFileName =
        IGC::Debug::DumpName(IGC::Debug::GetShaderOutputName())
            .Type(shaderType)
            .Hash(hash)
            .SIMDSize(simd)
            .Extension("asm")
            .str();
    if (asmFileName.find_last_of("\\") != std::string::npos)
    {
        asmFileName = asmFileName.substr(asmFileName.find_last_of("\\") + 1, asmFileName.size());
    }

    std::ifstream asmFile(asmFileName.c_str());

    while( getline(asmFile, line) )
    {
        if( line == ".code" )
        {
            break;
        }
    }

    while( getline(asmFile, line) )
    {
        if( line == "" || line.find( "//", 0 ) == 0 || line == "main:" || line.find( "label", 0 ) == 0)
        {
            continue;
        }
        else if (line.find(" ", 0) == std::string::npos && line.find(":", 0) == line.size() - 1)
        {
            continue;
        }
        else if( line == ".end_code" )
        {
            break;
        }

        if( line.find("(",0 ) == 0 )
        {
            line = line.substr( line.find(")",0) + 2, line.length() );
        }

        instStr = line.substr( 0, line.find(" ", 0) );

        auto hasDot = instStr.find(".",0);
        if (hasDot != std::string::npos)
        {
            instStr = instStr.substr( 0, hasDot );
        }

        if( line.find("L",0) == 0  || line.find("_AUTO_LABEL", 0) == 0)
        {
            m_CompileShaderStats[STATS_ISA_BASIC_BLOCKS]++;
        }
        else if( instStr == "add" || instStr == "addc" || instStr == "avg" || instStr == "dp2" ||
            instStr == "dp3" || instStr == "dp4" || instStr == "dph" || instStr == "frc" ||
            instStr == "line" || instStr == "lrp" || instStr == "mac" || instStr == "mach" ||
            instStr == "mad" || instStr == "madm" || instStr == "math" || instStr == "mul" ||
            instStr == "pln" || instStr == "rndd" || instStr == "rnde" || instStr == "rndu" ||
            instStr == "rndz" || instStr == "sad2" || instStr == "sada2" || instStr == "subb" )
        {
            m_CompileShaderStats[STATS_ISA_ALU]++;
        }
        else if( instStr == "and" || instStr == "asr" || instStr == "bfe" || instStr == "bfi1" ||
            instStr == "bfi2" || instStr == "bfrev" || instStr == "cbit" || instStr == "fbh" ||
            instStr == "fbl" || instStr == "lzd" || instStr == "not" || instStr == "or" ||
            instStr == "shl" || instStr == "shr" || instStr == "xor" )
        {
            m_CompileShaderStats[STATS_ISA_LOGIC]++;
        }
        else if( instStr == "brc" || instStr == "brd" || instStr == "jumpi")
        {
            m_CompileShaderStats[STATS_ISA_THREADCF]++;
        }
        else if(instStr == "break" || instStr == "cont" || instStr == "while" ||
            instStr == "else" || instStr == "endif" || instStr == "if" )
        {
            m_CompileShaderStats[STATS_ISA_STRUCTCF]++;
        }
        else if( instStr == "goto" || instStr == "join" )
        {
            m_CompileShaderStats[STATS_ISA_GOTOJOIN]++;
        }
        else if( instStr == "call" || instStr == "calla" )
        {
            m_CompileShaderStats[STATS_ISA_CALL]++;
        }
        else if( instStr == "cmp" || instStr == "cmpn" || instStr == "csel" || instStr == "sel")
        {
            m_CompileShaderStats[STATS_ISA_SEL_CMP]++;
        }
        else if( instStr == "mov" || instStr == "movi" || instStr == "smov" )
        {
            m_CompileShaderStats[STATS_ISA_MOV]++;
        }
        else if( instStr == "send" || instStr == "sendc" || instStr == "sends" )
        {
            m_CompileShaderStats[STATS_ISA_SEND]++;
        }
        else if( instStr == "halt" || instStr == "illegal" || instStr == "nop" || instStr == "wait" ||
            instStr == "ret" )
        {
            m_CompileShaderStats[STATS_ISA_OTHERS]++;
        }
        else if( line.find( "_GOTO_TARGET", 0 ) == 0 )
        {
            ;
        }
        else
        {
            IGC_ASSERT(0);
        }
    }

    int statsIndex = STATS_ISA_INST_COUNT;
    if( simd == SIMDMode::SIMD16 )
    {
        statsIndex = STATS_ISA_INST_COUNT_SIMD16;
    }
    else if( simd == SIMDMode::SIMD32 )
    {
        statsIndex = STATS_ISA_INST_COUNT_SIMD32;
    }

    for( int i=STATS_ISA_ALU; i<STATS_MAX_SHADER_STATS_ITEMS; i++)
    {
        m_CompileShaderStats[statsIndex] += m_CompileShaderStats[i];
    }

    if( simd == SIMDMode::SIMD16 )
    {
        m_CompileShaderStats[STATS_ISA_INST_COUNT_SIMD16] -= m_CompileShaderStats[STATS_ISA_INST_COUNT];
    }
    else if( simd == SIMDMode::SIMD32 )
    {
        m_CompileShaderStats[STATS_ISA_INST_COUNT_SIMD32] -= m_CompileShaderStats[STATS_ISA_INST_COUNT];
    }
    asmFile.close();
}

void ShaderStats::sumShaderStat( SHADER_STATS_ITEMS compileInterval, int count )
{
    IGC_ASSERT(0 <= compileInterval);
    IGC_ASSERT(compileInterval < STATS_MAX_SHADER_STATS_ITEMS);
    m_CompileShaderStats[ compileInterval ] += count;
};

void ShaderStats::miscSumShaderStat(ShaderStats* sStats)
{
    ++m_totalShaderCount;
    if (sStats->getShaderStats(STATS_ISA_INST_COUNT) > 0)
    {
        ++m_TotalSimd8;
    }
    if (sStats->getShaderStats(STATS_ISA_INST_COUNT_SIMD16) > 0)
    {
        ++m_TotalSimd16;
    }
    if (sStats->getShaderStats(STATS_ISA_INST_COUNT_SIMD32) > 0)
    {
        ++m_TotalSimd32;
    }
}

#endif // GET_SHADER_STATS

const char* g_cCompTimeIntervals[MAX_COMPILE_TIME_INTERVALS+1] =
{
#define DEFINE_TIME_STAT( enumName, stringName, parentEnum, isVISA, isUnacc, isCoarseTimer, isDashBoardTimer ) stringName,
#include "timeStats.def"
#undef DEFINE_TIME_STAT
};

std::string str(COMPILE_TIME_INTERVALS cti)
{
    switch (cti)
    {
#define DEFINE_TIME_STAT( enumName, stringName, parentEnum, isVISA, isUnacc, isCoarseTimer, isDashBoardTimer ) case enumName: return #enumName;
#include "timeStats.def"
#undef DEFINE_TIME_STAT
    default: IGC_ASSERT_MESSAGE(0, "unreachable"); break;
    }

    return "";
}

COMPILE_TIME_INTERVALS interval( std::string const& str )
{
#define DEFINE_TIME_STAT( enumName, stringName, parentEnum, isVISA, isUnacc, isCoarseTimer, isDashBoardTimer ) \
    if ( str == stringName ) \
    { \
        return enumName; \
    }
#include "timeStats.def"
#undef DEFINE_TIME_STAT
    //llvm::errs() << str;
    IGC_ASSERT_MESSAGE(0, "unreachable, unknown COMPILE_TIME_INTERVALS name");
    return MAX_COMPILE_TIME_INTERVALS;
}

bool isVISATimer( COMPILE_TIME_INTERVALS cti )
{
    switch (cti)
    {
#define DEFINE_TIME_STAT( enumName, stringName, parentEnum, isVISA, isUnacc, isCoarseTimer, isDashBoardTimer ) case enumName: return isVISA;
#include "timeStats.def"
#undef DEFINE_TIME_STAT
    default: IGC_ASSERT_MESSAGE(0, "unreachable"); break;
    }
    return false;
}

bool isUnaccounted( COMPILE_TIME_INTERVALS cti )
{
    switch (cti)
    {
#define DEFINE_TIME_STAT( enumName, stringName, parentEnum, isVISA, isUnacc, isCoarseTimer, isDashBoardTimer ) case enumName: return isUnacc;
#include "timeStats.def"
#undef DEFINE_TIME_STAT
    default: IGC_ASSERT_MESSAGE(0, "unreachable"); break;
    }
    return false;
}

bool isCoarseTimer( COMPILE_TIME_INTERVALS cti )
{
    switch (cti)
    {
#define DEFINE_TIME_STAT( enumName, stringName, parentEnum, isVISA, isUnacc, isCoarseTimer, isDashBoardTimer ) case enumName: return isCoarseTimer;
#include "timeStats.def"
#undef DEFINE_TIME_STAT
    default: IGC_ASSERT_MESSAGE(0, "unreachable"); break;
    }
    return true;
}

bool isDashboardTimer( COMPILE_TIME_INTERVALS cti )
{
    switch (cti)
    {
#define DEFINE_TIME_STAT( enumName, stringName, parentEnum, isVISA, isUnacc, isCoarseTimer, isDashBoardTimer ) case enumName: return isDashBoardTimer;
#include "timeStats.def"
#undef DEFINE_TIME_STAT
    default: IGC_ASSERT_MESSAGE(0, "unreachable"); break;
    }

    return true;
}

COMPILE_TIME_INTERVALS parentInterval( COMPILE_TIME_INTERVALS cti )
{
    switch (cti)
    {
#define DEFINE_TIME_STAT( enumName, stringName, parentEnum, isVISA, isUnacc, isCoarseTimer, isDashBoardTimer ) case enumName: return parentEnum;
#include "timeStats.def"
#undef DEFINE_TIME_STAT
    default: IGC_ASSERT_MESSAGE(0, "unreachable"); break;
    }
    return MAX_COMPILE_TIME_INTERVALS;
}

int parentIntervalDepth( COMPILE_TIME_INTERVALS cti )
{
    if ( cti == TIME_TOTAL )
    {
        return 0;
    }
    else
    {
        return 1 + parentIntervalDepth( parentInterval( cti ) );
    }
}

#if GET_TIME_STATS

TimeStats::TimeStats()
    : m_isPostProcessed(false)
    , m_totalShaderCount(0)
    , m_PassTotalTicks (0)
{
    std::fill(std::begin(m_wallclockStart), std::end(m_wallclockStart), 0);
    std::fill(std::begin(m_elapsedTime),    std::end(m_elapsedTime),    0);
    std::fill(std::begin(m_hitCount),       std::end(m_hitCount),       0);
    m_freq = iSTD::GetTimestampFrequency();

    m_PassTimeStatsMap.clear();
}

TimeStats::~TimeStats()
{
    m_PassTimeStatsMap.clear();
}

void TimeStats::recordVISATimers()
{
    // getTotalTimers() +1 because there is a unaccounted counter
    for (unsigned int i = 0; i < getTotalTimers(); ++i)
    {
        m_elapsedTime[TIME_VISA_TOTAL+i] += getTimerTicks(i);
        m_hitCount[TIME_VISA_TOTAL + i] = getTimerHits(i);
    }
}

void TimeStats::recordTimerStart( COMPILE_TIME_INTERVALS compileInterval )
{
    IGC_ASSERT(0 <= compileInterval);
    IGC_ASSERT(compileInterval < MAX_COMPILE_TIME_INTERVALS);
    m_wallclockStart[ compileInterval ] = iSTD::GetTimestampCounter();
}

void TimeStats::recordTimerEnd( COMPILE_TIME_INTERVALS compileInterval )
{
    IGC_ASSERT(0 <= compileInterval);
    IGC_ASSERT(compileInterval < MAX_COMPILE_TIME_INTERVALS);
    m_elapsedTime[ compileInterval ] += iSTD::GetTimestampCounter() - m_wallclockStart[ compileInterval ];
    m_hitCount[ compileInterval ]++;
}

uint64_t TimeStats::getCompileTime( COMPILE_TIME_INTERVALS compileInterval ) const
{
    IGC_ASSERT(0 <= compileInterval);
    IGC_ASSERT(compileInterval < MAX_COMPILE_TIME_INTERVALS);
    return m_elapsedTime[ compileInterval ];
}

uint64_t TimeStats::getCompileHit( COMPILE_TIME_INTERVALS compileInterval ) const
{
    IGC_ASSERT(0 <= compileInterval);
    IGC_ASSERT(compileInterval < MAX_COMPILE_TIME_INTERVALS);
    return m_hitCount[ compileInterval ];
}

void TimeStats::sumWith( const TimeStats* pOther )
{
    // Add pOther's compile time's to us
    for (int i=0;i<MAX_COMPILE_TIME_INTERVALS;i++)
    {
        m_elapsedTime[ i ] += pOther->m_elapsedTime[ i ];
        m_hitCount[ i ]    += pOther->m_hitCount[ i ];
    }

    m_totalShaderCount++;

    m_PassTotalTicks += pOther->m_PassTotalTicks;

    if (IGC_REGKEY_OR_FLAG_ENABLED(DumpTimeStatsPerPass, TIME_STATS_PER_PASS))
    {
        if (m_PassTimeStatsMap.empty())
        {
            m_PassTimeStatsMap.insert(pOther->m_PassTimeStatsMap.begin(), pOther->m_PassTimeStatsMap.end());
        }
        else
        {
            std::map<std::string, PerPassTimeStat>::const_iterator fromIter;
            std::map<std::string, PerPassTimeStat>::iterator toIter;

            for (fromIter = pOther->m_PassTimeStatsMap.begin(); fromIter != pOther->m_PassTimeStatsMap.end(); fromIter++)
            {
                toIter = m_PassTimeStatsMap.find(fromIter->first);
                if (toIter != m_PassTimeStatsMap.end())
                {
                    // If the pass is already included in the combined list, add the numbers
                    toIter->second.PassHitCount += fromIter->second.PassHitCount;
                    toIter->second.PassElapsedTime += fromIter->second.PassElapsedTime;
                }
                else
                {
                    // Add new pass from this run to the combined list
                    PerPassTimeStat stat;
                    stat.PassElapsedTime = fromIter->second.PassElapsedTime;
                    stat.PassHitCount = fromIter->second.PassHitCount;
                    m_PassTimeStatsMap.insert(std::pair<std::string, PerPassTimeStat>(fromIter->first, stat));
                }
            }
        }
    }
}

void TimeStats::printTime( ShaderType type, ShaderHash hash) const
{
    printTime(type, hash, nullptr);
}

void TimeStats::printTime( ShaderType type, ShaderHash hash, void* context ) const
{
    TimeStats pp = postProcess();

    std::string shaderName = IGC::Debug::DumpName(IGC::Debug::GetShaderOutputName()).Type(type).Hash(hash).StagedInfo(context).str().c_str();
    if (shaderName.find_last_of("\\") != std::string::npos)
    {
        shaderName = shaderName.substr(shaderName.find_last_of("\\") + 1, shaderName.size());
    }

    pp.printTimeCSV( shaderName );

    // Skip printing PerPass info to CSV for now
    //pp.printPerPassTimeCSV( shaderName );
    // Avoid printing the timestats for each shader to screen
    // pp.printSumTimeTable(llvm::dbgs());
}

void TimeStats::printSumTime() const
{
    // If using regkey to turn on timestats, CorpusName is not initialized properly
    if (strlen(IGC::Debug::GetShaderCorpusName()) == 0)
    {
        std::stringstream corpusName;
        corpusName << m_totalShaderCount << " shaders";
        IGC::Debug::SetShaderCorpusName(corpusName.str().c_str());
    }

    TimeStats pp = postProcess();

    bool dumpCoarse = IGC_REGKEY_OR_FLAG_ENABLED(DumpTimeStatsCoarse, TIME_STATS_COARSE);

    if ( dumpCoarse )
    {
        pp.printSumTimeCSV("c:\\Intel\\TimeStatCoarseSum.csv");
    }
    else
    {
        pp.printSumTimeCSV("c:\\Intel\\TimeStatSum.csv");
    }

    if (IGC_REGKEY_OR_FLAG_ENABLED(DumpTimeStatsPerPass, TIME_STATS_PER_PASS))
    {
        pp.printPerPassSumTime(llvm::dbgs());
        pp.printPerPassSumTimeCSV("c:\\Intel\\TimeStatPerPassSum.csv");
    }

    pp.printSumTimeTable(llvm::dbgs());
}

bool TimeStats::skipTimer( int i ) const
{
    const COMPILE_TIME_INTERVALS interval = static_cast<COMPILE_TIME_INTERVALS>(i);
    if( IGC_REGKEY_OR_FLAG_ENABLED(DumpTimeStatsCoarse, TIME_STATS_COARSE ) && !isCoarseTimer( interval ) )
    {
        return true;
    }
    if( !isDashboardTimer( interval ) )
    {
        return true;
    }
    return false;
}
void TimeStats::printSumTimeCSV(const char* outputFile) const
{
    IGC_ASSERT_MESSAGE(m_isPostProcessed, "Print functions should only be called on a Post-Processed TimeStats object");

    bool fileExist = false;

    FILE* fp = fopen(outputFile, "r");
    if( fp )
    {
        fileExist = true;
        fclose(fp);
    }

    FILE* fileName = fopen(outputFile, "a");

    if( !fileExist && fileName )
    {
        fprintf(fileName, "Frequency,%ju\n\n", m_freq);
        fprintf(fileName, "corpus name,shader count,");
        for (int i=0;i<MAX_COMPILE_TIME_INTERVALS;i++)
        {
            if( !skipTimer( i ) )
            {
                fprintf(fileName, "%s,", g_cCompTimeIntervals[i] );
            }
        }
        fprintf(fileName, "\n");
    }

    if( fileName )
    {
        fprintf(fileName, "%s,%d,", IGC::Debug::GetShaderCorpusName(), m_totalShaderCount );

        // print ticks
        for (int i=0;i<MAX_COMPILE_TIME_INTERVALS;i++)
        {
            if( !skipTimer( i ) )
            {
                fprintf(fileName, "%jd,", getCompileTime(static_cast<COMPILE_TIME_INTERVALS>(i)) );
            }
        }
        fprintf(fileName, "\n");

        if( !IGC_REGKEY_OR_FLAG_ENABLED(DumpTimeStatsCoarse, TIME_STATS_COARSE))
        {
            // print secs
            fprintf(fileName, "seconds,," );
            for (int i=0;i<MAX_COMPILE_TIME_INTERVALS;i++)
            {
                fprintf(fileName, "%f,", (double)getCompileTime(static_cast<COMPILE_TIME_INTERVALS>(i))/(double)m_freq);
            }
            fprintf(fileName, "\n");

            // print percentage
            fprintf(fileName, "percentage,," );
            for (int i=0;i<MAX_COMPILE_TIME_INTERVALS;i++)
            {
                fprintf(fileName, "%f,", (double)getCompileTime(static_cast<COMPILE_TIME_INTERVALS>(i))/(double)getCompileTime(TIME_TOTAL) * 100. );
            }
            fprintf(fileName, "\n");

            // print hit count
            fprintf(fileName, "hit,," );
            for (int i=0;i<MAX_COMPILE_TIME_INTERVALS;i++)
            {
                fprintf(fileName, "%ju,", m_hitCount[i] );
            }
            fprintf(fileName, "\n");
        }

        fclose(fileName);
    }
}

void TimeStats::printPerPassSumTimeCSV(const char* outputFile) const
{
    IGC_ASSERT_MESSAGE(m_isPostProcessed, "Print functions should only be called on a Post-Processed TimeStats object");

    if (m_PassTimeStatsMap.empty())
    {
        return;
    }

    bool fileExist = false;

    FILE* fp = fopen(outputFile, "r");
    if (fp)
    {
        fileExist = true;
        fclose(fp);
    }

    FILE* fileName = fopen(outputFile, "a");
    std::map<std::string, PerPassTimeStat>::const_iterator iter;

    if (!fileExist && fileName)
    {
        fprintf(fileName, "Frequency,%ju\n\n", m_freq);
        fprintf(fileName, "corpus name,passes count,Total,");

        for (iter = m_PassTimeStatsMap.begin(); iter != m_PassTimeStatsMap.end(); iter++)
        {
            fprintf(fileName, "%s,", iter->first.c_str());
        }
        fprintf(fileName, "\n");
    }

    if (fileName)
    {
        fprintf(fileName, "%s,%d,%ju,", IGC::Debug::GetShaderCorpusName(), (int)m_PassTimeStatsMap.size(), m_PassTotalTicks);

        for (iter = m_PassTimeStatsMap.begin(); iter != m_PassTimeStatsMap.end(); iter++)
        {
            fprintf(fileName, "%jd,", iter->second.PassElapsedTime);
        }

        fprintf(fileName, "\n");
        fclose(fileName);
    }
}

void TimeStats::recordPerPassTimerStart(std::string PassName)
{
    IGC_ASSERT(!PassName.empty());

    std::map<std::string, PerPassTimeStat>::iterator iter = m_PassTimeStatsMap.find(PassName);
    if (iter == m_PassTimeStatsMap.end())
    {
        PerPassTimeStat stat;
        stat.PassElapsedTime = 0;
        stat.PassHitCount = 0;
        stat.PassClockStart = iSTD::GetTimestampCounter();

        m_PassTimeStatsMap.insert(std::pair<std::string, PerPassTimeStat>(PassName, stat));
    }
    else
    {
        iter->second.PassClockStart = iSTD::GetTimestampCounter();
    }
}

void TimeStats::recordPerPassTimerEnd(std::string PassName)
{
    IGC_ASSERT(!PassName.empty());

    std::map<std::string, PerPassTimeStat>::iterator iter = m_PassTimeStatsMap.find(PassName);

    if (iter == m_PassTimeStatsMap.end())
    {
        std::cerr << "Invalid Pass " << PassName << std::endl;
    }
    else
    {
        uint64_t start = iter->second.PassClockStart;
        uint64_t elapsed = iSTD::GetTimestampCounter() - start;

        iter->second.PassHitCount += 1;
        iter->second.PassElapsedTime += elapsed;
        m_PassTotalTicks += elapsed;
    }
}

namespace {
    std::string str( double num, unsigned width, unsigned sigFigs )
    {
        char numStr[32];
#if defined( WIN32 ) || defined( _WIN64 )
        sprintf_s(numStr,                "%*.*f", width, sigFigs, num);
#else
        snprintf(numStr, sizeof(numStr), "%*.*f", width, sigFigs, num);
#endif
        return numStr;
    }

    std::string str( uint64_t num, unsigned width )
    {
        char numStr[32];
#if defined( WIN32 ) || defined( _WIN64 )
        sprintf_s(numStr,                "%*llu", width, num);
#else
        snprintf(numStr, sizeof(numStr), "%*llu", width, (long long unsigned int)num);
#endif
        return numStr;
    }
} // anonymous namespace

void TimeStats::printSumTimeTable( llvm::raw_ostream & OS ) const
{
    IGC_ASSERT_MESSAGE(m_isPostProcessed, "Print functions should only be called on a Post-Processed TimeStats object");

    llvm::formatted_raw_ostream FS(OS);

    FS << "\n";
    FS << "ShaderCount:  "  << m_totalShaderCount << " shaders\n";
    FS << "CorpusName:   \"" << IGC::Debug::GetShaderCorpusName() << "\"\n";
    FS << "Frequency:    "  << m_freq << " ticks/sec\n";

    const unsigned colWidth =  8;                        //<! Width of each of the data columns
    const unsigned startCol =  4;                        //<! Spacing to the left of the whole table
    const unsigned ticksCol = 50;                        //<! Location of the first character of the ticks column
    const unsigned secsCol  = ticksCol + colWidth + 2;   //<! Location of the first character of the seconds column
    const unsigned percCol  =  secsCol + colWidth + 2;   //<! Location of the first character of the percent column
    const unsigned hitCol   =  percCol + colWidth + 2;   //<! Location of the first character of the hit column

    // table header
    FS.PadToColumn(ticksCol) << "ticks";
    FS.PadToColumn( secsCol) << "seconds";
    FS.PadToColumn( percCol) << "percent";
    FS.PadToColumn(  hitCol) << "hits";
    FS << "\n";
    const std::string bar(colWidth+1,'-');
    FS.PadToColumn(ticksCol) << bar;
    FS.PadToColumn( secsCol) << bar;
    FS.PadToColumn( percCol) << bar;
    FS.PadToColumn(  hitCol) << bar;
    FS << "\n";

    // table body
    if (IGC_REGKEY_OR_FLAG_ENABLED(DumpTimeStatsCoarse, TIME_STATS_COARSE))
    {
        uint64_t timeNotInCoarse = getCompileTime(TIME_TOTAL);
        for (int i = 0; i < MAX_COMPILE_TIME_INTERVALS; i++)
        {
            const COMPILE_TIME_INTERVALS interval = static_cast<COMPILE_TIME_INTERVALS>(i);
            if (!skipTimer(i))
            {
                const uint64_t intervalTicks = getCompileTime(interval);
                if (intervalTicks)
                {
                    if (interval != TIME_TOTAL)
                    {
                        timeNotInCoarse -= intervalTicks;
                    }
                    FS.PadToColumn(startCol) << g_cCompTimeIntervals[interval];
                    FS.PadToColumn(ticksCol) << str(intervalTicks, colWidth);
                    FS.PadToColumn(secsCol) << str(intervalTicks / (double)m_freq, colWidth, 4);
                    FS.PadToColumn(percCol) << str(intervalTicks / (double)getCompileTime(TIME_TOTAL) * 100.0, colWidth, 2);
                    FS.PadToColumn(percCol) << str(m_hitCount[i], colWidth);
                    FS << "\n";
                }
            }
        }
        // print the "others" for coarse
        FS.PadToColumn(startCol) << "Others";
        FS.PadToColumn(ticksCol) << str(timeNotInCoarse, colWidth);
        FS.PadToColumn(secsCol) << str(timeNotInCoarse / (double)m_freq, colWidth, 4);
        FS.PadToColumn(percCol) << str(timeNotInCoarse / (double)getCompileTime(TIME_TOTAL) * 100.0, colWidth, 2);
        FS.PadToColumn(percCol) << str(0, colWidth);
        FS << "\n";
    }
    else
    {
        for (int i = 0; i < MAX_COMPILE_TIME_INTERVALS; i++)
        {
            const COMPILE_TIME_INTERVALS interval = static_cast<COMPILE_TIME_INTERVALS>(i);
            if (!skipTimer(i))
            {
                const uint64_t intervalTicks = getCompileTime(interval);
                if (intervalTicks)
                {
                    FS.PadToColumn(startCol).indent(2 * parentIntervalDepth(interval))
                        << g_cCompTimeIntervals[interval];
                    FS.PadToColumn(ticksCol) << str(intervalTicks, colWidth);
                    FS.PadToColumn(secsCol) << str(intervalTicks / (double)m_freq, colWidth, 4);
                    FS.PadToColumn(percCol) << str(intervalTicks / (double)getCompileTime(TIME_TOTAL) * 100.0, colWidth, 2);
                    FS.PadToColumn(percCol) << str(m_hitCount[i], colWidth);
                    FS << "\n";
                }
            }
        }
    }
    FS << "\n";

    FS.flush();
    OS.flush();
}

void TimeStats::printPerPassSumTime(llvm::raw_ostream& OS) const
{
    if (m_PassTimeStatsMap.empty())
    {
        return;
    }

    llvm::formatted_raw_ostream FS(OS);

    FS << "\n";
    FS << "PassesCount:  " << m_PassTimeStatsMap.size() << " IGC/LLVM passes\n";
    FS << "Total Ticks:  " << m_PassTotalTicks << " ticks\n";

    const unsigned colWidth = 8;                      //<! Width of each of the data columns
    const unsigned startCol = 6;                      //<! Spacing to the left of the whole table
    const unsigned ticksCol = 50;                     //<! Location of the first character of the ticks column
    const unsigned percCol = ticksCol + colWidth + 2; //<! Location of the first character of the percent column
    const unsigned hitCol = percCol + colWidth + 2;   //<! Location of the first character of the hit column

    // table header
    FS.PadToColumn(ticksCol) << "ticks";
    FS.PadToColumn(percCol)  << "percent";
    FS.PadToColumn(hitCol)   << "hits";
    FS << "\n";
    const std::string bar(colWidth + 1, '-');
    FS.PadToColumn(ticksCol) << bar;
    FS.PadToColumn(percCol) << bar;
    FS.PadToColumn(hitCol) << bar;
    FS << "\n";

    PerPassTimeStat stat;
    uint64_t ticks = 0;
    std::map<std::string, PerPassTimeStat>::const_iterator iter;

    for (iter = m_PassTimeStatsMap.begin(); iter != m_PassTimeStatsMap.end(); iter++)
    {
        stat = iter->second;
        ticks = stat.PassElapsedTime;

        // pass ticks % hit
        FS.PadToColumn(startCol) << iter->first.substr(0, ticksCol - startCol - 1);
        FS.PadToColumn(ticksCol) << str(ticks, colWidth);
        FS.PadToColumn(percCol) << str(ticks / (double)m_PassTotalTicks * 100.0, colWidth, 2);
        FS.PadToColumn(percCol) << str(stat.PassHitCount, colWidth);
        FS << "\n";
    }

    FS << "\n";
    FS.flush();
    OS.flush();
}

void TimeStats::printTimeCSV( std::string const& corpusName ) const
{
    IGC_ASSERT_MESSAGE(m_isPostProcessed, "Print functions should only be called on a Post-Processed TimeStats object");

    std::string subFile = "TimeStat_";
    if (strlen(IGC::Debug::GetShaderCorpusName()) == 0)
        subFile += "Shaders";
    else
        subFile += IGC::Debug::GetShaderCorpusName();
    const std::string outputFilePath = std::string("c:\\Intel\\") + subFile + ".csv";
    const char *outputFile = outputFilePath.c_str();

    bool fileExist = false;

    FILE* fp = fopen(outputFile, "r");
    if( fp )
    {
        fileExist = true;
        fclose(fp);
    }

    FILE* fileName = fopen(outputFile, "a");

    if( !fileExist && fileName)
    {
        fprintf(fileName, "Frequency:%ju,", m_freq);
        for (int i=0;i<MAX_COMPILE_TIME_INTERVALS;i++)
        {
            if( !skipTimer(i) )
            {
                fprintf(fileName, "%s,", g_cCompTimeIntervals[i] );
            }
        }
        fprintf(fileName, "\n");
    }

    if (fileName)
    {
        fprintf(fileName, "%s.isa,", corpusName.c_str());
        for (int i = 0; i < MAX_COMPILE_TIME_INTERVALS; i++)
        {
            if (!skipTimer(i))
            {
                const COMPILE_TIME_INTERVALS interval = static_cast<COMPILE_TIME_INTERVALS>(i);
                fprintf(fileName, "%jd,", getCompileTime(interval));
            }
        }

        fprintf(fileName, "\n");
        fclose(fileName);
    }

}

void TimeStats::printPerPassTimeCSV(std::string const& corpusName) const
{
    IGC_ASSERT_MESSAGE(m_isPostProcessed, "Print functions should only be called on a Post-Processed TimeStats object");

    if (m_PassTimeStatsMap.empty())
    {
        return;
    }

    std::string subFile = "TimeStatPerPass_";
    if (strlen(IGC::Debug::GetShaderCorpusName()) == 0)
        subFile += "Shaders";
    else
        subFile += IGC::Debug::GetShaderCorpusName();
    const std::string outputFilePath = std::string("c:\\Intel\\") + subFile + ".csv";
    const char* outputFile = outputFilePath.c_str();
    bool fileExist = false;

    FILE* fp = fopen(outputFile, "r");
    if (fp)
    {
        fileExist = true;
        fclose(fp);
    }

    FILE* fileName = fopen(outputFile, "a");

    if (!fileExist && fileName)
    {
        fprintf(fileName, "Frequency:%ju,", m_freq);

        std::map<std::string, PerPassTimeStat>::const_iterator iter;
        fprintf(fileName, "Passes Count,Total,");
        for (iter = m_PassTimeStatsMap.begin(); iter != m_PassTimeStatsMap.end(); iter++)
        {
            fprintf(fileName, "%s,", iter->first.c_str());
        }
        fprintf(fileName, "\n");
    }

    if (fileName)
    {
        fprintf(fileName, "%s.isa,%d,%ju,", corpusName.c_str(), (int)m_PassTimeStatsMap.size(), m_PassTotalTicks);
        std::map<std::string, PerPassTimeStat>::const_iterator iter;

        for (iter = m_PassTimeStatsMap.begin(); iter != m_PassTimeStatsMap.end(); iter++)
        {
            fprintf(fileName, "%jd,", iter->second.PassElapsedTime);
        }

        fprintf(fileName, "\n");
        fclose(fileName);
    }
}

TimeStats TimeStats::postProcess() const
{
    TimeStats copy(*this);

    uint64_t childSum[MAX_COMPILE_TIME_INTERVALS+1];
    std::fill( std::begin(childSum), std::end(childSum), 0 );

    for (int i=0; i <MAX_COMPILE_TIME_INTERVALS; ++i)
    {
        const COMPILE_TIME_INTERVALS interval = static_cast<COMPILE_TIME_INTERVALS>(i);
        const COMPILE_TIME_INTERVALS parent = parentInterval( interval );

        if (m_elapsedTime[interval] == 0) {
            continue;
        }

        if ( isUnaccounted(interval) == false )
        {
            childSum[ parent ] += m_elapsedTime[ interval ];
        }
    }

    for (int i=0; i <MAX_COMPILE_TIME_INTERVALS; ++i)
    {
        const COMPILE_TIME_INTERVALS interval = static_cast<COMPILE_TIME_INTERVALS>(i);
        const COMPILE_TIME_INTERVALS parent = parentInterval( interval );

        if ( isUnaccounted(interval) == true )
        {
            copy.m_elapsedTime[ interval ] = m_elapsedTime[ parent ] - childSum[ parent ];
        }
    }

    copy.m_isPostProcessed = true;

    return copy;
}

#endif // GET_TIME_STATS

#if GET_MEM_STATS

void CMemoryReport::CreateMemStatsFiles()
{
    if( IGC::Debug::GetDebugFlag(IGC::Debug::DebugFlag::MEM_STATS ) )
    {
        sprintf_s(g_MemoryReport.m_CsvNameUsageSum, sizeof(m_CsvNameUsageSum),
            "c:\\Intel\\MemoryStatsSum.csv" );

        sprintf_s(g_MemoryReport.m_CsvNameUsage, sizeof(m_CsvNameUsage),
            "c:\\Intel\\%sMemoryStatsUsage.csv",
            IGC::Debug::GetShaderCorpusName() );

        sprintf_s(g_MemoryReport.m_CsvNameAllocs, sizeof(m_CsvNameAllocs),
            "c:\\Intel\\%sMemoryStatsAllocs.csv",
            IGC::Debug::GetShaderCorpusName() );

        sprintf_s(g_MemoryReport.m_CsvNameAllocsSubset, sizeof(m_CsvNameAllocsSubset),
            "c:\\Intel\\%sMemoryStatsAllocsSubset.csv",
            IGC::Debug::GetShaderCorpusName() );

        FILE* outputFileUsage           = (FILE*)iSTD::FileOpen( (const char*)g_MemoryReport.m_CsvNameUsage, "w" );
        FILE* outputFileAllocs          = (FILE*)iSTD::FileOpen( (const char*)g_MemoryReport.m_CsvNameAllocs, "w" );
        FILE* outputFileAllocsSubset    = (FILE*)iSTD::FileOpen( (const char*)g_MemoryReport.m_CsvNameAllocsSubset, "w" );
        // Write header
        iSTD::FileWrite( outputFileUsage, "Name,Global mem. peak" );
        iSTD::FileWrite( outputFileAllocs, "Name,Global num alloc." );
        iSTD::FileWrite( outputFileAllocsSubset, "Name,Type of Subset" );
        for( int i = 0; i < IGC::MAX_SHADER_MEMORY_SNAPSHOT; i++ )
        {
            if( g_MemoryReport.m_GrabDetailed || IGC::g_cShaderMemorySnapshot[ i ].IsMilestone )
            {
                iSTD::FileWrite( outputFileUsage, ",%s peak,%s end", IGC::g_cShaderMemorySnapshot[ i ].Name, IGC::g_cShaderMemorySnapshot[ i ].Name );
                iSTD::FileWrite( outputFileAllocs, ",%s allocs,%s curr. allocs", IGC::g_cShaderMemorySnapshot[ i ].Name, IGC::g_cShaderMemorySnapshot[ i ].Name );
                iSTD::FileWrite( outputFileAllocsSubset, ",%s allocs", IGC::g_cShaderMemorySnapshot[ i ].Name, IGC::g_cShaderMemorySnapshot[ i ].Name );
            }
        }
        iSTD::FileWrite( outputFileUsage, "\n" );
        iSTD::FileWrite( outputFileAllocs, "\n" );
        iSTD::FileWrite( outputFileAllocsSubset, "\n" );

        iSTD::FileClose( outputFileUsage );
        iSTD::FileClose( outputFileAllocs );
        iSTD::FileClose( outputFileAllocsSubset );
    }
}

/*****************************************************************************\

Function: CMemoryReport::DumpMemoryStats

Description:
    Saves memory statistics to already created file.

Input:
    None

Output:
    None

\*****************************************************************************/
void CMemoryReport::DumpMemoryStats( ShaderType type, ShaderHash hash )
{
    if( IGC::Debug::GetDebugFlag(IGC::Debug::DebugFlag::MEM_STATS ) )
    {
        m_type = type;
        CopyToSummary();

        FILE* csvFileGlobal = (FILE*)iSTD::FileOpen( (const char*) g_MemoryReport.m_CsvNameUsage, "a" );
        FILE* csvFileAllocs = (FILE*)iSTD::FileOpen( (const char*) g_MemoryReport.m_CsvNameAllocs, "a" );
        FILE* csvFileAllocsSubset = (FILE*)iSTD::FileOpen( (const char*) g_MemoryReport.m_CsvNameAllocsSubset, "a" );

        std::string shaderName = IGC::Debug::DumpName(IGC::Debug::GetShaderOutputName()).Type(type).Hash(hash).str().c_str();
        if (shaderName.find_last_of("\\") != std::string::npos)
        {
            shaderName = shaderName.substr(shaderName.find_last_of("\\") + 1, shaderName.size());
        }

        sprintf(m_DumpMemoryStatsFileName, "%s", shaderName.c_str());

        iSTD::FileWrite(  csvFileGlobal, "%s,%u", m_DumpMemoryStatsFileName, g_MemoryReport.m_Stat.HeapUsedPeak/1024 );
        iSTD::FileWrite(  csvFileAllocs, "%s,%u", m_DumpMemoryStatsFileName, g_MemoryReport.m_Stat.NumAllocations );

        for( int i = 0; i < IGC::MAX_SHADER_MEMORY_SNAPSHOT; i++ )
        {
            if( g_MemoryReport.m_GrabDetailed || IGC::g_cShaderMemorySnapshot[ i ].IsMilestone )
            {
                iSTD::FileWrite( csvFileGlobal, ",%d,%d", g_MemoryReport.m_Snapshots[ i ].SnapHeapUsedAbsolutePeak/1024,
                    g_MemoryReport.m_Snapshots[ i ].HeapUsed/1024 );
                iSTD::FileWrite( csvFileAllocs, ",%d,%d", g_MemoryReport.m_Snapshots[ i ].NumSnapAllocations,
                    g_MemoryReport.m_Snapshots[ i ].NumCurrSnapAllocationsPeak );
            }
        }
        iSTD::FileWrite( csvFileGlobal, "\n" );
        iSTD::FileWrite( csvFileAllocs, "\n" );

        for ( int i = 0; i < IGC::SMAT_NUM_OF_TYPES; i++ )
        {
            iSTD::FileWrite( csvFileAllocsSubset, "%s,%s", m_DumpMemoryStatsFileName,
                IGC::g_cShaderMemoryAllocsType[ i ].Name );
            for( int j = 0; j < IGC::MAX_SHADER_MEMORY_SNAPSHOT; j++ )
            {
                if( g_MemoryReport.m_GrabDetailed || IGC::g_cShaderMemorySnapshot[ j ].IsMilestone )
                {
                    iSTD::FileWrite( csvFileAllocsSubset, ",%d", g_MemoryReport.m_Snapshots[ j ].NumSnapAllocationsType[ i ]);
                }
            }
            iSTD::FileWrite( csvFileAllocsSubset, "\n" );
        }
        iSTD::FileClose( csvFileGlobal );
        iSTD::FileClose( csvFileAllocs );
        iSTD::FileClose( csvFileAllocsSubset );
    }
}

CMemoryReport g_MemoryReport;

/*****************************************************************************\

Function: MemUsageSnapshot

Description: Helper global function for setting usage snapshot

Input: IGC::SHADER_MEMORY_SNAPSHOT phase

Output: None

\*****************************************************************************/
void MemUsageSnapshot( IGC::SHADER_MEMORY_SNAPSHOT phase )
{
    if( IGC::Debug::GetDebugFlag(IGC::Debug::DebugFlag::MEM_STATS ) )
    {
        if( phase == IGC::SMS_COMPILE_START )
        {
            g_MemoryReport.UsageReset();
        }
        g_MemoryReport.UsageSnapshot( phase );
    }
}

/*****************************************************************************\

Function: CMemoryReport::CMemoryReport

Description: CMemoryReport constructor

Input: None

Output: None

\*****************************************************************************/
CMemoryReport::CMemoryReport()
{
    m_GrabDetailed = false;
    m_SnapCnt = 0;
    m_LastSnapHeapUsed = 0;

    for( int sumI = 0; sumI<MAX_MEMORY_SUMMARY_ITEM; sumI++ )
    {
        m_SummaryDump[sumI].clear();
    }
}

/*****************************************************************************\

Function: CMemoryReport::~CMemoryReport

Description: CMemoryReport constructor

Input: None

Output: None

\*****************************************************************************/
CMemoryReport::~CMemoryReport()
{
}

/*****************************************************************************\

Function:
    CMemoryReport::MallocMemInstrumentation

Description:
    Instrumentation for malloc

Input:
    size_t _Size - size of allocated memory

Output:
    None

\*****************************************************************************/
void CMemoryReport::MallocMemInstrumentation( size_t _Size )
{
    ++g_MemoryReport.m_Stat.NumAllocations;
    ++g_MemoryReport.m_Stat.NumSnapAllocations;
    ++g_MemoryReport.m_Stat.NumCurrSnapAllocationsPeak;
    ++g_MemoryReport.m_Stat.NumCurrAllocations;

    for (int i = 0; i < IGC::SMAT_NUM_OF_TYPES; i++)
    {
        if ( _Size <= IGC::g_cShaderMemoryAllocsType[ i ].max_size)
        {
            ++g_MemoryReport.m_Stat.NumSnapAllocationsType[ i ];
            break;
        }
    }

    g_MemoryReport.m_Stat.HeapUsed += reinterpret_cast<int&>(_Size);
    g_MemoryReport.m_Stat.NumCurrAllocationsPeak =
        iSTD::Max<DWORD>( g_MemoryReport.m_Stat.NumCurrAllocationsPeak,
                          g_MemoryReport.m_Stat.NumCurrAllocations );
    g_MemoryReport.m_Stat.NumCurrSnapAllocationsPeak =
        iSTD::Max<DWORD>( g_MemoryReport.m_Stat.NumCurrSnapAllocationsPeak,
                          g_MemoryReport.m_Stat.NumCurrAllocations );
    g_MemoryReport.m_Stat.HeapUsedPeak =
        iSTD::Max<DWORD>( g_MemoryReport.m_Stat.HeapUsedPeak,
                          g_MemoryReport.m_Stat.HeapUsed > 0 ? g_MemoryReport.m_Stat.HeapUsed : 0 );

    g_MemoryReport.m_Stat.SnapHeapUsed += reinterpret_cast<int&>(_Size);
    g_MemoryReport.m_Stat.SnapHeapUsedPeak =
        iSTD::Max<DWORD>( g_MemoryReport.m_Stat.SnapHeapUsedPeak,
                          g_MemoryReport.m_Stat.SnapHeapUsed > 0 ? g_MemoryReport.m_Stat.SnapHeapUsed : 0 );
}

/*****************************************************************************\

Function: CMemoryReport::FreeMemInstrumentation

Description:
    Instrumentation for free instruction

Input:
    size_t size - size of freed memory

Output: None

\*****************************************************************************/
void CMemoryReport::FreeMemInstrumentation( size_t size )
{
    ++g_MemoryReport.m_Stat.NumReleases;
    ++g_MemoryReport.m_Stat.NumSnapReleases;

    g_MemoryReport.m_Stat.HeapUsed -= reinterpret_cast<int&>(size);
    g_MemoryReport.m_Stat.SnapHeapUsed -= reinterpret_cast<int&>(size);

    if( g_MemoryReport.m_Stat.NumCurrAllocations != 0 )
    {
        --g_MemoryReport.m_Stat.NumCurrAllocations;
    }
}

/*****************************************************************************\

Function: CMemoryReport::UsageReset

Description:
    Resets statistics.

Input: None

Output: None

\*****************************************************************************/
void CMemoryReport::UsageReset()
{
    if( IGC::Debug::GetDebugFlag(IGC::Debug::DebugFlag::MEM_STATS ) )
    {
        MemStat cleanStat = {};
        m_Stat = cleanStat;

        memset( m_Snapshots, 0, sizeof( *m_Snapshots ) * IGC::MAX_SHADER_MEMORY_SNAPSHOT );
        m_SnapCnt = 1;   // reserve [0] for 0-based deltas
    }
}

/*****************************************************************************\

Function: CMemoryReport::UsageSnapshot

Description:
    Grab memory stat snapshot and save it under given phase.

Input: IGC::SHADER_MEMORY_SNAPSHOT phase

Output: None

\*****************************************************************************/
void CMemoryReport::UsageSnapshot(
    IGC::SHADER_MEMORY_SNAPSHOT phase )
{
    if( IGC::Debug::GetDebugFlag(IGC::Debug::DebugFlag::MEM_STATS ) )
    {
        if( m_GrabDetailed || IGC::g_cShaderMemorySnapshot[ phase ].IsMilestone )
        {
            m_SnapCnt++;
            if( phase < IGC::MAX_SHADER_MEMORY_SNAPSHOT )
            {
                m_LastSnapHeapUsed = m_LastSnapHeapUsed > 0 ? m_LastSnapHeapUsed : 0;
                // Calculate absolute peak in this snapshot.
                m_Stat.SnapHeapUsedAbsolutePeak = m_LastSnapHeapUsed + m_Stat.SnapHeapUsedPeak;

                m_Snapshots[ phase ] = m_Stat;

                m_LastSnapHeapUsed = m_Stat.HeapUsed;
                m_Stat.SnapHeapUsed = 0;
                m_Stat.SnapHeapUsedPeak = 0;
                m_Stat.NumCurrSnapAllocationsPeak = 0;
                m_Stat.NumSnapAllocations = 0;

                for (int i = 0; i < IGC::SMAT_NUM_OF_TYPES; i++)
                {
                    g_MemoryReport.m_Stat.NumSnapAllocationsType[ i ] = 0;
                }
                m_Stat.NumSnapReleases = 0;
            }
        }
    }
}

/*****************************************************************************\

Function: CMemoryReport::SetDetailed

Description:
    Enables or disables detailed stats aquisition.

Input: bool Enabled

Output: None

\*****************************************************************************/
void CMemoryReport::SetDetailed( bool Enabled )
{
    m_GrabDetailed = Enabled;
}

void CMemoryReport::CopyToSummary()
{
    m_SummaryDump[SMSUM_HeapUsedPeak].push_back( m_Stat.HeapUsedPeak/1024 );
    m_SummaryDump[SMSUM_NumAllocations].push_back( m_Stat.NumAllocations );
    for( int i = 0; i < IGC::MAX_SHADER_MEMORY_SNAPSHOT; i++ )
    {
        m_SummaryDump[ i + SMSUM_SnapHeapUsedAbsolutePeak ].push_back( m_Snapshots[ i ].SnapHeapUsedAbsolutePeak/1024 );
        m_SummaryDump[ i + SMSUM_NumSnapAllocations ].push_back( m_Snapshots[ i ].NumSnapAllocations );
    }
}

const char* CMemoryReport::ShaderTypeText()
{
    if( m_type == ShaderType::PIXEL_SHADER )
    {
        return "PS";
    }
    else if( m_type == ShaderType::VERTEX_SHADER )
    {
        return "VS";
    }
    else if( m_type == ShaderType::GEOMETRY_SHADER )
    {
        return "GS";
    }
    else if( m_type == ShaderType::HULL_SHADER )
    {
        return "HS";
    }
    else if( m_type == ShaderType::DOMAIN_SHADER )
    {
        return "DS";
    }
    else if( m_type == ShaderType::COMPUTE_SHADER )
    {
        return "CS";
    }
    else if( m_type == ShaderType::OPENCL_SHADER )
    {
        return "OCL";
    }
    return "";
}

void CMemoryReport::DumpSummaryStats()
{
    if( IGC::Debug::GetDebugFlag(IGC::Debug::DebugFlag::MEM_STATS ) )
    {

        bool fileExist = false;

        FILE* fileExistFP = fopen( m_CsvNameUsageSum, "r" );
        if( fileExistFP )
        {
            fileExist = true;
            fclose( fileExistFP );
        }

        FILE* csvSummary = fopen( m_CsvNameUsageSum, "a" );
        if( !fileExist )
        {
            // Write header
            fprintf( csvSummary, ",,Global mem. peak" );
            for( int i = 0; i < IGC::MAX_SHADER_MEMORY_SNAPSHOT; i++ )
            {
                if( m_GrabDetailed || IGC::g_cShaderMemorySnapshot[ i ].IsMilestone )
                {
                    fprintf( csvSummary, ",%s peak", IGC::g_cShaderMemorySnapshot[ i ].Name );
                }
            }
            fprintf( csvSummary, ",Shader Type\n" );
        }

        for( int i = 0; i < SMSUM_NumAllocations; i++ )
        {
            m_SummaryDump[ i ].sort();
        }

        fprintf(csvSummary, "%s,max,", IGC::Debug::GetShaderCorpusName());
        for (int i = 0; i < SMSUM_NumAllocations; i++)
        {
            fprintf(csvSummary, "%d,", m_SummaryDump[i].back());
        }
        fprintf(csvSummary, "%s\n", ShaderTypeText());

        fprintf( csvSummary, "%s,max,", IGC::Debug::GetShaderCorpusName() );
        for( int i = 0; i < SMSUM_NumAllocations; i++ )
        {
            fprintf( csvSummary, "%d,", m_SummaryDump[i].back() );
        }
        fprintf( csvSummary, "%s\n",  ShaderTypeText() );

        fprintf( csvSummary, "%s,average,", IGC::Debug::GetShaderCorpusName() );

        int shaderCount = m_SummaryDump[0].size();
        for( int i = 0; i < SMSUM_NumAllocations; i++ )
        {
            iter = m_SummaryDump[i].begin();
            int tempSum = 0;
            // find average
            for( int i=0;i<shaderCount;i++ )
            {
                tempSum += *iter;
                iter++;
            }
            fprintf( csvSummary, "%.02f,", (float)tempSum/shaderCount );

            // find median
            // for( int i=0;i<shaderCount/2;i++ )
            // {
            //     iter++;
            // }
            // fprintf( csvSummary, "%d,", *iter );
        }
        fprintf( csvSummary, "%s\n",  ShaderTypeText() );

        fprintf( csvSummary, "%s,median,", IGC::Debug::GetShaderCorpusName() );

        for( int i = 0; i < SMSUM_NumAllocations; i++ )
        {
            iter = m_SummaryDump[i].begin();
            // find median
            for( int i=0;i<shaderCount/2;i++ )
            {
                iter++;
            }
            fprintf( csvSummary, "%d,", *iter );
        }
        fprintf( csvSummary, "%s\n",  ShaderTypeText() );

        fprintf( csvSummary, "%s,sum,", IGC::Debug::GetShaderCorpusName() );
        for( int i = SMSUM_NumAllocations; i < MAX_MEMORY_SUMMARY_ITEM; i++ )
        {
            unsigned totalAllocs = 0;

            for( iter = m_SummaryDump[i].begin(); iter != m_SummaryDump[i].end(); iter++ )
            {
                totalAllocs += *iter;
            }
            fprintf( csvSummary, "%d,", totalAllocs );
        }
        fprintf( csvSummary, "%s\n",  ShaderTypeText() );

        fclose( csvSummary );
    }
}

#endif //GET_MEM_STATS
