/*
 * \file       ss_to_dcdtree.cpp
 * \brief      OpenCSD : 
 * 
 * \copyright  Copyright (c) 2015, ARM Limited. All Rights Reserved.
 */

/* 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice, 
 * this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright notice, 
 * this list of conditions and the following disclaimer in the documentation 
 * and/or other materials provided with the distribution. 
 * 
 * 3. Neither the name of the copyright holder nor the names of its contributors 
 * may be used to endorse or promote products derived from this software without 
 * specific prior written permission. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND 
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */ 

#include "ss_to_dcdtree.h"
#include "ss_key_value_names.h"


CreateDcdTreeFromSnapShot::CreateDcdTreeFromSnapShot() :
    m_bInit(false),
    m_pDecodeTree(0),
    m_pReader(0),
    m_pErrLogInterface(0),    
    m_bPacketProcOnly(false),
    m_BufferFileName("")
{
    m_errlog_handle = 0;
    m_add_create_flags = 0;
}

CreateDcdTreeFromSnapShot::~CreateDcdTreeFromSnapShot()
{
    destroyDecodeTree();
}
    
void CreateDcdTreeFromSnapShot::initialise(SnapShotReader *pReader, ITraceErrorLog *pErrLogInterface)
{
    if((pErrLogInterface != 0) && (pReader != 0))
    {
        m_pReader = pReader;
        m_pErrLogInterface = pErrLogInterface;
        m_errlog_handle = m_pErrLogInterface->RegisterErrorSource("ss2_dcdtree");
        m_bInit = true;
    }
}

bool CreateDcdTreeFromSnapShot::createDecodeTree(const std::string &SourceName, bool bPacketProcOnly, uint32_t add_create_flags)
{    
    m_add_create_flags = add_create_flags;
    if(m_bInit)
    {
        if(!m_pReader->snapshotReadOK())
        {
            LogError("Supplied snapshot reader has not correctly read the snapshot.\n");
            return false;
        }

        m_bPacketProcOnly = bPacketProcOnly;
        Parser::TraceBufferSourceTree tree;

        if(m_pReader->getTraceBufferSourceTree(SourceName, tree))
        {
            int numDecodersCreated = 0; // count how many we create - if none then give up.
            uint32_t formatter_flags = OCSD_DFRMTR_FRAME_MEM_ALIGN;

            /* make a note of the trace binary file name + path to ss directory */            
            m_BufferFileName = m_pReader->getSnapShotDir() + tree.buffer_info.dataFileName;

            ocsd_dcd_tree_src_t src_format = tree.buffer_info.dataFormat == "source_data" ? OCSD_TRC_SRC_SINGLE : OCSD_TRC_SRC_FRAME_FORMATTED;

            if (tree.buffer_info.dataFormat == "dstream_coresight")
                formatter_flags = OCSD_DFRMTR_HAS_FSYNCS;

            /* create the initial device tree */
            // TBD:     handle syncs / hsyncs data from TPIU
            m_pDecodeTree = DecodeTree::CreateDecodeTree(src_format, formatter_flags);
            if(m_pDecodeTree == 0)
            {
                LogError("Failed to create decode tree object\n");
                return false;
            }

            // use our error logger - don't use the tree default.
            m_pDecodeTree->setAlternateErrorLogger(m_pErrLogInterface);

            if(!bPacketProcOnly)
            {
                m_pDecodeTree->createMemAccMapper();
            }

            /* run through each protocol source to this buffer... */
            std::map<std::string, std::string>::iterator it = tree.source_core_assoc.begin();

            while(it != tree.source_core_assoc.end())
            {
                Parser::Parsed *etm_dev, *core_dev;
                if(m_pReader->getDeviceData(it->first,&etm_dev))
                {
                    // found the device data for this device.

                    // see if we have a core name (STM / ITM not associated with a core);
                    std::string coreDevName = it->second;
                    if(coreDevName.size() > 0)
                    {
                        if(m_pReader->getDeviceData(coreDevName,&core_dev))
                        {
                            if(createPEDecoder(core_dev->deviceTypeName,etm_dev))
                            {
                                numDecodersCreated++;
                                if(!bPacketProcOnly &&(core_dev->dumpDefs.size() > 0))
                                {
                                    processDumpfiles(core_dev->dumpDefs);
                                }
                            }
                            else
                            {
                                std::ostringstream oss;
                                oss << "Failed to create decoder for source " << it->first << ".\n";
                                LogError(oss.str());
                            }
                        }
                        else
                        {
                            // Could not find the device data for the core.
                            // unexpected - since we created the associations.
                            std::ostringstream oss;
                            oss << "Failed to get device data for source " << it->first << ".\n";
                            LogError(oss.str());
                        }
                    }
                    else
                    {
                        // none-core source 
                        if(createSTDecoder(etm_dev))
                        {
                             numDecodersCreated++;
                        }
                        else
                        {
                            std::ostringstream oss;
                            oss << "Failed to create decoder for none core source " << it->first << ".\n";
                            LogError(oss.str());
                        }
                    }
                }
                else
                {
                    // TBD: could not find the device data for the source.
                    // again unexpected - suggests ss format error.
                    std::ostringstream oss;
                    oss << "Failed to find device data for source " << it->first << ".\n";
                    LogError(oss.str());
                }
                if(src_format == OCSD_TRC_SRC_SINGLE)
                    it = tree.source_core_assoc.end();
                else
                    it++;
            }

            if(numDecodersCreated == 0)
            {
                // nothing useful found 
                destroyDecodeTree();
            }
        }
        else
        {
            std::ostringstream oss;
            oss << "Failed to get parsed source tree for buffer " << SourceName << ".\n";
            LogError(oss.str());
        }
    }
    return (bool)(m_pDecodeTree != 0);
}

void CreateDcdTreeFromSnapShot::destroyDecodeTree()
{
    if(m_pDecodeTree)
        DecodeTree::DestroyDecodeTree(m_pDecodeTree);
    m_pDecodeTree = 0;
    m_pReader = 0;
    m_pErrLogInterface = 0;
    m_errlog_handle = 0;
    m_BufferFileName = "";
}

void  CreateDcdTreeFromSnapShot::LogError(const std::string &msg)
{
    ocsdError err(OCSD_ERR_SEV_ERROR,OCSD_ERR_TEST_SS_TO_DECODER,msg);
    m_pErrLogInterface->LogError(m_errlog_handle,&err);
}

void CreateDcdTreeFromSnapShot::LogError(const ocsdError &err)
{
    m_pErrLogInterface->LogError(m_errlog_handle,&err);
}

bool CreateDcdTreeFromSnapShot::createPEDecoder(const std::string &coreName, Parser::Parsed *devSrc)
{
    bool bCreatedDecoder = false;
    std::string devTypeName = devSrc->deviceTypeName;

    // split off .x from type name.
    std::string::size_type pos = devTypeName.find_first_of('.');
    if(pos != std::string::npos)
        devTypeName = devTypeName.substr(0,pos);

    // split according to protocol 
    if(devTypeName == ETMv4Protocol)
    {
        bCreatedDecoder = createETMv4Decoder(coreName,devSrc);
    }
    else if(devTypeName == ETMv3Protocol)
    {
        bCreatedDecoder = createETMv3Decoder(coreName,devSrc);
    }
    else if(devTypeName == PTMProtocol || devTypeName == PFTProtocol)
    {
        bCreatedDecoder = createPTMDecoder(coreName,devSrc);
    }
    else if (devTypeName == ETEProtocol)
    {
        bCreatedDecoder = createETEDecoder(coreName, devSrc);
    }

    return bCreatedDecoder;
}

// create an ETMv4 decoder based on the deviceN.ini file.
bool CreateDcdTreeFromSnapShot::createETMv4Decoder(const std::string &coreName, Parser::Parsed *devSrc, const bool bDataChannel /* = false*/)
{
    bool createdDecoder = false;
    bool configOK = true;
    
    // generate the config data from the device data.
    ocsd_etmv4_cfg config;

    regs_to_access_t regs_to_access[] = {
        { ETMv4RegCfg, true, &config.reg_configr, 0 },
        { ETMv4RegIDR, true, &config.reg_traceidr, 0 },
        { ETMv4RegIDR0, true, &config.reg_idr0, 0 },
        { ETMv4RegIDR1, false, &config.reg_idr1, 0x4100F403 },
        { ETMv4RegIDR2, true, &config.reg_idr2, 0 },
        { ETMv4RegIDR8, false, &config.reg_idr8, 0 },
        { ETMv4RegIDR9, false, &config.reg_idr9, 0 },
        { ETMv4RegIDR10, false, &config.reg_idr10, 0 },
        { ETMv4RegIDR11, false, &config.reg_idr11, 0 },
        { ETMv4RegIDR12, false, &config.reg_idr12, 0 },
        { ETMv4RegIDR13,false, &config.reg_idr13, 0 },
    };

    // extract registers
    configOK = getRegisters(devSrc->regDefs,sizeof(regs_to_access)/sizeof(regs_to_access_t), regs_to_access);

    // extract core profile
    if(configOK)
        configOK = getCoreProfile(coreName,config.arch_ver,config.core_prof);

    // good config - generate the decoder on the tree.
    if(configOK)
    {
        ocsd_err_t err = OCSD_OK;
        EtmV4Config configObj(&config);
        const char *decoderName = bDataChannel ? OCSD_BUILTIN_DCD_ETMV4D : OCSD_BUILTIN_DCD_ETMV4I;

        err = m_pDecodeTree->createDecoder(decoderName, m_add_create_flags | (m_bPacketProcOnly ? OCSD_CREATE_FLG_PACKET_PROC : OCSD_CREATE_FLG_FULL_DECODER),&configObj);
        
        if(err ==  OCSD_OK)
            createdDecoder = true;
        else
        {
            std::string msg = "Snapshot processor : failed to create " +  (std::string)decoderName + " decoder on decode tree.";
            LogError(ocsdError(OCSD_ERR_SEV_ERROR,err,msg));
        }
    }

    return createdDecoder;
}

bool CreateDcdTreeFromSnapShot::createETEDecoder(const std::string &coreName, Parser::Parsed *devSrc)
{
    bool createdDecoder = false;
    bool configOK = true;

    // generate the config data from the device data.
    ocsd_ete_cfg config;   

    // ete regs are same names Etmv4 in places...
    regs_to_access_t regs_to_access[] = {
        { ETMv4RegCfg, true, &config.reg_configr, 0 },
        { ETMv4RegIDR, true, &config.reg_traceidr, 0 },
        { ETMv4RegIDR0, true, &config.reg_idr0, 0 },
        { ETMv4RegIDR1, false, &config.reg_idr1, 0x4100F403 },
        { ETMv4RegIDR2, true, &config.reg_idr2, 0 },
        { ETMv4RegIDR8, false, &config.reg_idr8, 0 },
        { ETERegDevArch, false, &config.reg_devarch, 0x47705A13 },
    };

    // extract registers
    configOK = getRegisters(devSrc->regDefs, sizeof(regs_to_access) / sizeof(regs_to_access_t), regs_to_access);

    // extract core profile
    if (configOK)
        configOK = getCoreProfile(coreName, config.arch_ver, config.core_prof);

    // good config - generate the decoder on the tree.
    if (configOK)
    {
        ocsd_err_t err = OCSD_OK;
        ETEConfig configObj(&config);
        const char *decoderName = OCSD_BUILTIN_DCD_ETE;

        err = m_pDecodeTree->createDecoder(decoderName, m_add_create_flags | (m_bPacketProcOnly ? OCSD_CREATE_FLG_PACKET_PROC : OCSD_CREATE_FLG_FULL_DECODER), &configObj);

        if (err == OCSD_OK)
            createdDecoder = true;
        else
        {
            std::string msg = "Snapshot processor : failed to create " + (std::string)decoderName + " decoder on decode tree.";
            LogError(ocsdError(OCSD_ERR_SEV_ERROR, err, msg));
        }
    }

    return createdDecoder;
}

// create an ETMv3 decoder based on the register values in the deviceN.ini file.
bool CreateDcdTreeFromSnapShot::createETMv3Decoder(const std::string &coreName, Parser::Parsed *devSrc)
{
    bool createdDecoder = false;
    bool configOK = true;
    
    // generate the config data from the device data.
    ocsd_etmv3_cfg cfg_regs;

    regs_to_access_t regs_to_access[] = {
        { ETMv3PTMRegIDR, true, &cfg_regs.reg_idr, 0 },
        { ETMv3PTMRegCR, true, &cfg_regs.reg_ctrl, 0 },
        { ETMv3PTMRegCCER, true, &cfg_regs.reg_ccer, 0 },
        { ETMv3PTMRegTraceIDR, true, &cfg_regs.reg_trc_id, 0}
    };

    // extract registers
    configOK = getRegisters(devSrc->regDefs,sizeof(regs_to_access)/sizeof(regs_to_access_t), regs_to_access);

    // extract core profile
    if(configOK)
        configOK = getCoreProfile(coreName,cfg_regs.arch_ver,cfg_regs.core_prof);

    // good config - generate the decoder on the tree.
    if(configOK)
    {
        EtmV3Config config(&cfg_regs);
        ocsd_err_t err = OCSD_OK;
        err = m_pDecodeTree->createDecoder(OCSD_BUILTIN_DCD_ETMV3, m_bPacketProcOnly ? OCSD_CREATE_FLG_PACKET_PROC : OCSD_CREATE_FLG_FULL_DECODER,&config);

        if(err ==  OCSD_OK)
            createdDecoder = true;
        else
            LogError(ocsdError(OCSD_ERR_SEV_ERROR,err,"Snapshot processor : failed to create ETMV3 decoder on decode tree."));
    }
    return createdDecoder;
}

bool CreateDcdTreeFromSnapShot::createPTMDecoder(const std::string &coreName, Parser::Parsed *devSrc)
{
    bool createdDecoder = false;
    bool configOK = true;
    
    // generate the config data from the device data.
    
    ocsd_ptm_cfg config;

    regs_to_access_t regs_to_access[] = {
        { ETMv3PTMRegIDR, true, &config.reg_idr, 0 },
        { ETMv3PTMRegCR, true, &config.reg_ctrl, 0 },
        { ETMv3PTMRegCCER, true, &config.reg_ccer, 0 },
        { ETMv3PTMRegTraceIDR, true, &config.reg_trc_id, 0}
    };

    // extract registers
    configOK = getRegisters(devSrc->regDefs,sizeof(regs_to_access)/sizeof(regs_to_access_t), regs_to_access);

    // extract core profile
    if(configOK)
        configOK = getCoreProfile(coreName,config.arch_ver,config.core_prof);

    // good config - generate the decoder on the tree.
    if(configOK)
    {
        PtmConfig configObj(&config);
        ocsd_err_t err = OCSD_OK;
        err = m_pDecodeTree->createDecoder(OCSD_BUILTIN_DCD_PTM, m_bPacketProcOnly ? OCSD_CREATE_FLG_PACKET_PROC : OCSD_CREATE_FLG_FULL_DECODER,&configObj);

        if(err ==  OCSD_OK)
            createdDecoder = true;
        else
            LogError(ocsdError(OCSD_ERR_SEV_ERROR,err,"Snapshot processor : failed to create PTM decoder on decode tree."));
    }
    return createdDecoder;
}

bool CreateDcdTreeFromSnapShot::createSTDecoder(Parser::Parsed *devSrc)
{
    bool bCreatedDecoder = false;
    std::string devTypeName = devSrc->deviceTypeName;

    // split off .x from type name.
    std::string::size_type pos = devTypeName.find_first_of('.');
    if(pos != std::string::npos)
        devTypeName = devTypeName.substr(0,pos);

    if(devTypeName == STMProtocol)
    {
        bCreatedDecoder = createSTMDecoder(devSrc);
    }

    return bCreatedDecoder;
}

bool CreateDcdTreeFromSnapShot::createSTMDecoder(Parser::Parsed *devSrc)
{
    bool createdDecoder = false;
    bool configOK = true;

    // generate the config data from the device data.
    
    ocsd_stm_cfg config;

    regs_to_access_t regs_to_access[] = {
        { STMRegTCSR, true, &config.reg_tcsr, 0 }
    };

    configOK = getRegisters(devSrc->regDefs,sizeof(regs_to_access)/sizeof(regs_to_access_t), regs_to_access);
    if(configOK)
    {
        ocsd_err_t err = OCSD_OK;
        STMConfig configObj(&config);

        err = m_pDecodeTree->createDecoder(OCSD_BUILTIN_DCD_STM, m_bPacketProcOnly ? OCSD_CREATE_FLG_PACKET_PROC : OCSD_CREATE_FLG_FULL_DECODER,&configObj);

        if(err ==  OCSD_OK)
            createdDecoder = true;
        else
            LogError(ocsdError(OCSD_ERR_SEV_ERROR,err,"Snapshot processor : failed to create STM decoder on decode tree."));
    }

    return createdDecoder;
}



// get a set of register values.
bool CreateDcdTreeFromSnapShot::getRegisters(std::map<std::string, std::string, Util::CaseInsensitiveLess> &regDefs, int numRegs, regs_to_access_t *reg_access_array)
{
    bool regsOK = true;

    for(int rv = 0; rv < numRegs; rv++)
    {
        if(!getRegByPrefix( regDefs,reg_access_array[rv]))
            regsOK = false;
    }
    return regsOK;
}

// strip out any parts with brackets
bool CreateDcdTreeFromSnapShot::getRegByPrefix(std::map<std::string, std::string, Util::CaseInsensitiveLess> &regDefs, 
    regs_to_access_t &reg_accessor)
{
    std::ostringstream oss;
    bool bFound = false;
    std::map<std::string, std::string, Util::CaseInsensitiveLess>::iterator it;
    std::string prefix_cmp;
    std::string::size_type pos;
    std::string strval;
    
    *reg_accessor.value = 0;

    it = regDefs.begin();
    while((it != regDefs.end()) && !bFound)
    {
        prefix_cmp = it->first;
        pos = prefix_cmp.find_first_of('(');
        if(pos != std::string::npos)
        {
            prefix_cmp = prefix_cmp.substr(0, pos);
        }
        if(prefix_cmp == reg_accessor.pszName)
        {
            strval = it->second;
            bFound = true;
        }
        it++;
    }

    if(bFound)
        *reg_accessor.value = strtoul(strval.c_str(),0,0);
    else
    {
        ocsd_err_severity_t sev = OCSD_ERR_SEV_ERROR;
        if(reg_accessor.failIfMissing)
        {
            oss << "Error:";
        }
        else
        {
            // no fail if missing - set any default and just warn.
            bFound = true;
            oss << "Warning: Default set for register. ";
            sev = OCSD_ERR_SEV_WARN;
            *reg_accessor.value = reg_accessor.val_default;
        }
        oss << "Missing " << reg_accessor.pszName << "\n";
        m_pErrLogInterface->LogMessage(m_errlog_handle, sev, oss.str());
    }
    return bFound;
}

bool CreateDcdTreeFromSnapShot::getCoreProfile(const std::string &coreName, ocsd_arch_version_t &arch_ver, ocsd_core_profile_t &core_prof)
{
    bool profileOK = true;
    ocsd_arch_profile_t ap = m_arch_profiles.getArchProfile(coreName);
    if(ap.arch != ARCH_UNKNOWN)
    {
        arch_ver = ap.arch;
        core_prof = ap.profile;
    }
    else
    {
        std::ostringstream oss;
        oss << "Unrecognized Core name " << coreName << ". Cannot evaluate profile or architecture.";
        LogError(oss.str());
        profileOK = false;
    }
    return profileOK;
}

void CreateDcdTreeFromSnapShot::processDumpfiles(std::vector<Parser::DumpDef> &dumps)
{
    std::string dumpFilePathName;
    std::vector<Parser::DumpDef>::const_iterator it;

    it = dumps.begin();
    while(it != dumps.end())
    {        
        dumpFilePathName = m_pReader->getSnapShotDir() + it->path;
        ocsd_file_mem_region_t region;
        ocsd_err_t err = OCSD_OK;

        region.start_address = it->address;
        region.file_offset = it->offset;
        region.region_size = it->length;

        // ensure we respect optional length and offset parameter and
        // allow multiple dump entries with same file name to define regions
        if (!TrcMemAccessorFile::isExistingFileAccessor(dumpFilePathName))
            err = m_pDecodeTree->addBinFileRegionMemAcc(&region, 1, OCSD_MEM_SPACE_ANY, dumpFilePathName);
        else
            err = m_pDecodeTree->updateBinFileRegionMemAcc(&region, 1, OCSD_MEM_SPACE_ANY, dumpFilePathName);
        if(err != OCSD_OK)
        {                            
            std::ostringstream oss;
            oss << "Failed to create memory accessor for file " << dumpFilePathName << ".";
            LogError(ocsdError(OCSD_ERR_SEV_ERROR,err,oss.str()));
        }
        it++;
    }
}

/* End of File ss_to_dcdtree.cpp */
