//--------------------------------------------------------------------------------------
VOID Fini(INT32 ExitCode, VOID *v)
{
    std::string LogCommon = KnobOutputDir.Value();
    LogCommon += "/";        
    LogCommon += KnobOutputFile.Value();

    std::string LogBlocks   = LogCommon + std::string(".blocks");
    std::string LogRoutines = LogCommon + std::string(".routines");
    std::string LogModules  = LogCommon + std::string(".modules");

    // create common log
    FILE *f = fopen(LogCommon.c_str(), "wb+");
    if (f)
    {
        UINT32 CoverageSize = 0;

        // enumerate loged basic blocks
        for (BASIC_BLOCKS::iterator it = m_BasicBlocks.begin(); it != m_BasicBlocks.end(); it++)
        {
            // calculate total coverage size
            CoverageSize += (*it).first.second;
        }

        fprintf(f, APP_NAME);
        fprintf(f, "===============================================\r\n");
        fprintf(f, "Program command line: %s\r\n", m_CommandLine.c_str());
        fprintf(f, "          Process ID: %d\r\n", m_ProcessId);
        fprintf(f, "   Number of threads: %d\r\n", m_ThreadCount);
        fprintf(f, "   Number of modules: %d\r\n", m_ModuleList.size());
        fprintf(f, "  Number of routines: %d\r\n", m_RoutinesList.size());
        fprintf(f, "      Number of BBLs: %d\r\n", m_BasicBlocks.size());
        fprintf(f, " Total coverage size: %d\r\n", CoverageSize);
        fprintf(f, "===============================================\r\n");        

        fclose(f);
    }   

    // create basic blocks log
    f = fopen(LogBlocks.c_str(), "wb+");
    if (f)
    {
        PrintLogFileHeader(f);
        fprintf(f, "# Basic blocks log file\r\n#\r\n");

        // enumerate loged basic blocks
        for (BASIC_BLOCKS::iterator it = m_BasicBlocks.begin(); it != m_BasicBlocks.end(); it++)
        {
            const string *Symbol = LookupSymbol((*it).first.first);

            // dump single basic block information
            fprintf(
                f, "0x%.8x:%s:0x%.8x:%d\r\n", 
                (*it).first.first, Symbol->c_str(), (*it).first.second, (*it).second
            );

            delete Symbol;
        }

        fclose(f);
    }

    // create routines log
    f = fopen(LogRoutines.c_str(), "wb+");
    if (f)
    {
        PrintLogFileHeader(f);
        fprintf(f, "# Routines log file\r\n#\r\n");

        // enumerate loged routines
        for (ROUTINES_LIST::iterator it = m_RoutinesList.begin(); it != m_RoutinesList.end(); it++)
        {
            const string *Symbol = LookupSymbol((*it).first);

            // dump single routine information
            fprintf(f, "0x%.8x:%s:%d\r\n", (*it).first, Symbol->c_str(), (*it).second);

            delete Symbol;
        }

        fclose(f);
    }

    // create modules log
    f = fopen(LogModules.c_str(), "wb+");
    if (f)
    {
        PrintLogFileHeader(f);
        fprintf(f, "# Modules log file\r\n#\r\n");

        // have to do this whole thing because the IMG_* functions don't work here
        for (std::list<std::string>::iterator it = m_ModulePathList.begin(); it != m_ModulePathList.end(); it++) 
        {
            // dump single routine information
            fprintf(f, "%s\r\n", (*it).c_str());            
        }

        fclose(f);
    }
}
//--------------------------------------------------------------------------------------
VOID Fini(INT32 ExitCode, VOID *v)
{
    std::string LogCommon = KnobOutputDir.Value();
    LogCommon += "/";        
    LogCommon += KnobOutputFile.Value();

    std::string LogBlocks   = LogCommon + std::string(".blocks");
    std::string LogRoutines = LogCommon + std::string(".routines");
    std::string LogModules  = LogCommon + std::string(".modules");

    // create common log
    FILE *f = fopen(LogCommon.c_str(), "wb+");
    if (f)
    {
        UINT32 CoverageSize = 0;

        // enumerate loged basic blocks
        for (BASIC_BLOCKS::iterator it = m_BasicBlocks.begin(); it != m_BasicBlocks.end(); it++)
        {
            // calculate total coverage size
            CoverageSize += (*it).first.second;
        }

        time_t Now;
        time(&Now); 

        fprintf(f, APP_NAME_INI);
        fprintf(f, "; =============================================\r\n");
        fprintf(f, "[coverager]\r\n");
        fprintf(f, "cmdline = %s ; program command line\r\n", m_CommandLine.c_str());
        fprintf(f, "pid = %d ; process ID\r\n", m_ProcessId);
        fprintf(f, "threads = %d ; number of threads\r\n", m_ThreadCount);
        fprintf(f, "modules = %d ; number of modules\r\n", m_ModuleList.size());
        fprintf(f, "routines = %d ; number of routines\r\n", m_RoutinesList.size());
        fprintf(f, "blocks = %d ; number of basic blocks\r\n", m_BasicBlocks.size());
        fprintf(f, "total_size = %d ; Total coverage size\r\n", CoverageSize);
        fprintf(f, "time = %d ; Execution time in seconds\r\n", Now - m_StartTime);
        fprintf(f, "; =============================================\r\n");        

        fclose(f);
    }   

    // create basic blocks log
    f = fopen(LogBlocks.c_str(), "wb+");
    if (f)
    {
        PrintLogFileHeader(f);
        fprintf(f, "# Basic blocks log file\r\n#\r\n");
        fprintf(f, "# <address>:<size>:<instructions>:<name>:<calls>\r\n#\r\n");

        // enumerate loged basic blocks
        for (BASIC_BLOCKS::iterator it = m_BasicBlocks.begin(); it != m_BasicBlocks.end(); it++)
        {
            const string *Symbol = LookupSymbol((*it).first.first);

            // dump single basic block information
            fprintf(
                f, "0x%.8x:0x%.8x:%d:%s:%d\r\n", 
                (*it).first.first, (*it).first.second, (*it).second.Instructions, Symbol->c_str(), (*it).second.Calls
            );

            delete Symbol;
        }

        fclose(f);
    }

    // create routines log
    f = fopen(LogRoutines.c_str(), "wb+");
    if (f)
    {
        PrintLogFileHeader(f);
        fprintf(f, "# Routines log file\r\n#\r\n");
        fprintf(f, "# <address>:<name>:<calls>\r\n#\r\n");

        // enumerate loged routines
        for (ROUTINES_LIST::iterator it = m_RoutinesList.begin(); it != m_RoutinesList.end(); it++)
        {
            const string *Symbol = LookupSymbol((*it).first);

            // dump single routine information
            fprintf(f, "0x%.8x:%s:%d\r\n", (*it).first, Symbol->c_str(), (*it).second);

            delete Symbol;
        }

        fclose(f);
    }

    // create modules log
    f = fopen(LogModules.c_str(), "wb+");
    if (f)
    {
        PrintLogFileHeader(f);
        fprintf(f, "# Modules log file\r\n#\r\n");
        fprintf(f, "# <address>:<size>:<name>\r\n#\r\n");

        // have to do this whole thing because the IMG_* functions don't work here
        for (std::list<std::string>::iterator it = m_ModulePathList.begin(); it != m_ModulePathList.end(); it++) 
        {
            // get image file name from full path
            std::string ModuleName = NameFromPath((*it));

            // lookup for module information
            if (m_ModuleList.find(ModuleName) != m_ModuleList.end())
            {
                // dump single routine information
                fprintf(
                    f, "0x%.8x:0x%.8x:%s\r\n",
                    m_ModuleList[ModuleName].first, 
                    m_ModuleList[ModuleName].second,
                    (*it).c_str()
                );            
            }            
        }

        fclose(f);
    }
}