//==============================================================================
// Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved.
/// \author AMD Developer Tools Team
/// \file
/// \brief  This file contains classes for tracing HSA AQL Packets
//==============================================================================

#include <algorithm>
#include <iomanip>

#include "DeviceInfoUtils.h"
#include "../Common/StringUtils.h"
#include "HSAFdnAPIInfoManager.h"
#include "HSAAqlPacketInfo.h"
#include "FinalizerInfoManager.h"
#include "HSAKernelDemangler.h"
#include "AutoGenerated/HSATraceInterception.h"
#include "AutoGenerated/HSATraceStringOutput.h"
#include "../HSAFdnCommon/HSAAgentIterateReplacer.h"
#include "../HSAFdnCommon/HSAAgentUtils.h"
#include "HSAFdnMaxApiTime.h"
#include <Logger.h>


static const uint64_t s_invalidPacketId = static_cast<uint64_t>(-1);

HSAAqlPacketBase::HSAAqlPacketBase(hsa_packet_type_t type) :
    m_type(type),
    m_packetId(s_invalidPacketId),
    m_agent({0}),
    m_pQueue(nullptr),
    m_isReady(false)
{
}

HSAAqlPacketBase:: ~HSAAqlPacketBase()
{
}

bool HSAAqlPacketBase::WritePacketEntry(std::ostream& sout)
{
    std::string strDeviceName;
    strDeviceName = HSAAgentsContainer::Instance()->GetAgentName(m_agent);

    if (strDeviceName.empty())
    {
        strDeviceName = "<UnknownDeviceName>";
    }

    // agent (device) name
    sout << std::left << std::setw(64) << strDeviceName;

    // agent (device) handle
    sout << std::left << std::setw(21) << HSAAgentUtils::GetHSAAgentString(m_agent, false, true);

    // queue index
    uint64_t queueId = 0;

    if (!HSAAPIInfoManager::Instance()->GetQueueId(m_pQueue, queueId))
    {
        queueId = 0;
    }

    sout << std::left << std::setw(21) << StringUtils::ToString(queueId);

    // device index
    unsigned int deviceIndex = 0;

    if (!HSAAgentIterateReplacer::Instance()->GetAgentGPUIndex(m_agent.handle, deviceIndex))
    {
        deviceIndex = 0;
    }

    sout << std::left << std::setw(10) << StringUtils::ToString(deviceIndex);

    // packet type
    sout << std::left << std::setw(35) << HSATraceStringUtils::Get_hsa_packet_type_t_String(m_type);

    // packet id
    sout << std::left << std::setw(21) << m_packetId;

    return true;
}

HSAAqlKernelDispatchPacket::HSAAqlKernelDispatchPacket(hsa_kernel_dispatch_packet_t kernelDispatchPacket) :
    HSAAqlPacketBase(HSA_PACKET_TYPE_KERNEL_DISPATCH),
    m_isRocProfilerPacket(false),
    m_pContextEntry(nullptr),
    m_start(0),
    m_end(0),
    m_packet(kernelDispatchPacket)
{
}

bool HSAAqlKernelDispatchPacket::WritePacketEntry(std::ostream& sout)
{
    bool recordKernelTime = true;
    uint64_t maxApiCallEndTime;

    if (HSAFdnMaxApiCallTime::Instance()->GetMaxApiCallEndTime(maxApiCallEndTime))
    {
        recordKernelTime &= m_start <= maxApiCallEndTime;
    }

    if (recordKernelTime)
    {
        // NOTE: the reason for the odd ordering of items below (for instance, the location of the
        // call to base class) is to match the order of items when not using aql-packet-tracing.
        // this means the HSA ATP file parser does not need to be updated and RCP will be able
        // to load the .atp file.
        FinalizerInfoManager* pFinalizerInfoMan = FinalizerInfoManager::Instance();

        std::string symName;

        if (pFinalizerInfoMan->m_codeHandleToSymbolHandleMap.count(m_packet.kernel_object) > 0)
        {
            uint64_t symHandle = pFinalizerInfoMan->m_codeHandleToSymbolHandleMap[m_packet.kernel_object];

            if (pFinalizerInfoMan->m_symbolHandleToNameMap.count(symHandle) > 0)
            {
                symName = pFinalizerInfoMan->m_symbolHandleToNameMap[symHandle];
                GPULogger::Log(GPULogger::logMESSAGE, "Lookup: CodeHandle: %llu, SymHandle: %llu, symName: %s\n", m_packet.kernel_object, symHandle, symName.c_str());
            }
        }

        if (symName.empty())
        {
            symName = "<UnknownKernelName>";
        }
        else
        {
            symName = DemangleKernelName(symName);
        }

        // kernel name
        sout << std::left << std::setw(std::max((size_t)50, symName.length() + 1)) << symName;

        // kernel pointer
        sout << std::left << std::setw(21) << StringUtils::ToHexString(m_packet.kernel_object);

        // start timestamp
        sout << std::left << std::setw(21) << m_start;

        // end timestamp
        sout << std::left << std::setw(21) << m_end;

        // call base class
        HSAAqlPacketBase::WritePacketEntry(sout);

        // kernel dispatch packet
        sout << HSATraceStringUtils::Get_hsa_kernel_dispatch_packet_t_String(m_packet);
    }

    return true;
}

void HSAAqlKernelDispatchPacket::SetTimestamps(uint64_t start, uint64_t end)
{
    m_start = start;
    m_end = end;
    m_isReady = true;
}

uint64_t HSAAqlKernelDispatchPacket::GetStartTimestamp()
{
    return m_start;
}

uint64_t HSAAqlKernelDispatchPacket::GetEndTimestamp()
{
    return m_end;
}

HSAAqlAgentDispatchPacket::HSAAqlAgentDispatchPacket(hsa_agent_dispatch_packet_t agentDispatchPacket) :
    HSAAqlPacketBase(HSA_PACKET_TYPE_AGENT_DISPATCH),
    m_packet(agentDispatchPacket)
{
    m_isReady = true; // this packet type is always ready
}

bool HSAAqlAgentDispatchPacket::WritePacketEntry(std::ostream& sout)
{
    bool retVal = HSAAqlPacketBase::WritePacketEntry(sout);
    sout << HSATraceStringUtils::Get_hsa_agent_dispatch_packet_t_String(m_packet);

    return retVal;
}

HSAAqlBarrierAndPacket::HSAAqlBarrierAndPacket(hsa_barrier_and_packet_t barrierAndPacket) :
    HSAAqlPacketBase(HSA_PACKET_TYPE_BARRIER_AND),
    m_packet(barrierAndPacket)
{
    m_isReady = true; // this packet type is always ready
}

bool HSAAqlBarrierAndPacket::WritePacketEntry(std::ostream& sout)
{
    bool retVal = HSAAqlPacketBase::WritePacketEntry(sout);
    sout << HSATraceStringUtils::Get_hsa_barrier_and_packet_t_String(m_packet);

    return retVal;
}

HSAAqlBarrierOrPacket::HSAAqlBarrierOrPacket(hsa_barrier_or_packet_t barrierOrPacket) :
    HSAAqlPacketBase(HSA_PACKET_TYPE_BARRIER_OR),
    m_packet(barrierOrPacket)
{
    m_isReady = true; // this packet type is always ready
}

bool HSAAqlBarrierOrPacket::WritePacketEntry(std::ostream& sout)
{
    bool retVal = HSAAqlPacketBase::WritePacketEntry(sout);
    sout << HSATraceStringUtils::Get_hsa_barrier_or_packet_t_String(m_packet);

    return retVal;
}

