/*
* Copyright (c) 2021, 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.
*/
//!
//! \file     media_perf_profiler_next.cpp
//! \brief    Defines data structures and interfaces for media performance profiler.
//! \details
//!

#include <stddef.h>
#include "media_perf_profiler_next.h"
#include "media_skuwa_specific.h"
#include "mhw_itf.h"
#include "mhw_mi.h"
#include "mhw_mi_cmdpar.h"
#include "mhw_mi_itf.h"
#include "mos_resource_defs.h"
#include "mos_util_debug.h"
#include "mos_utilities.h"
#include "mos_utilities_common.h"

#define UMD_PERF_LOG            8
#define NAME_LEN                60
#define LOCAL_STRING_SIZE       64
#define OFFSET_OF(TYPE, MEMBER) ((size_t) & ((TYPE *)0)->MEMBER )

typedef enum _UMD_PERF_MODE
{
    UMD_PERF_MODE_TIMING_ONLY      = 0,
    UMD_PERF_MODE_WITH_MEMORY_INFO = 4
} UMD_PERF_MODE;

#pragma pack(push)
#pragma pack(8)
struct PerfEntry
{
    uint32_t    nodeIndex;                  //!< Perf node index
    uint32_t    processId;                  //!< Process Id
    uint32_t    instanceId;                 //!< Instance Id
    uint32_t    engineTag;                  //!< Engine tag
    uint32_t    perfTag;                    //!< Performance tag
    uint32_t    timeStampBase;              //!< HW timestamp base
    uint32_t    beginRegisterValue[8];      //!< Begin register value
    uint32_t    endRegisterValue[8];        //!< End register value
    uint32_t    beginCpuTime[2];            //!< Begin CPU Time Stamp
    uint32_t    reserved[14];               //!< Reserved[14]
    uint64_t    beginTimeClockValue;        //!< Begin timestamp
    uint64_t    endTimeClockValue;          //!< End timestamp
};
#pragma pack(pop)

struct NodeHeader
{
    uint32_t osPlatform  : 3;
    uint32_t genPlatform : 3;
    uint32_t eventType   : 4;
    uint32_t perfMode    : 3;
    uint32_t genAndroid  : 4;
    uint32_t genPlatform_ext : 2;
    uint32_t reserved    : 13;
};

#define BASE_OF_NODE(perfDataIndex) (sizeof(NodeHeader) + (sizeof(PerfEntry) * perfDataIndex))

#define CHK_STATUS_RETURN(_stmt)                   \
{                                                  \
    MOS_STATUS stmtStatus = (MOS_STATUS)(_stmt);   \
    if (stmtStatus != MOS_STATUS_SUCCESS)          \
    {                                              \
        return stmtStatus;                         \
    }                                              \
}

#define CHK_NULL_RETURN(_ptr)                      \
{                                                  \
    if ((_ptr) == nullptr)                         \
    {                                              \
        return MOS_STATUS_NULL_POINTER;            \
    }                                              \
}

#define CHK_NULL_NO_STATUS_RETURN(_ptr)            \
{                                                  \
    if ((_ptr) == nullptr)                         \
    {                                              \
        return;                                    \
    }                                              \
}

#define CHK_STATUS_UNLOCK_MUTEX_RETURN(_stmt)      \
{                                                  \
    MOS_STATUS stmtStatus = (MOS_STATUS)(_stmt);   \
    if (stmtStatus != MOS_STATUS_SUCCESS)          \
    {                                              \
        MosUtilities::MosUnlockMutex(m_mutex);                  \
        return stmtStatus;                         \
    }                                              \
}

#define CHK_NULL_UNLOCK_MUTEX_RETURN(_ptr)         \
{                                                  \
    if ((_ptr) == nullptr)                         \
    {                                              \
        MosUtilities::MosUnlockMutex(m_mutex);                  \
        return MOS_STATUS_NULL_POINTER;            \
    }                                              \
}

MediaPerfProfilerNext::MediaPerfProfilerNext()
 {
    m_perfStoreBufferMap.clear();
    m_perfDataIndexMap.clear();
    m_refMap.clear();
    m_initializedMap.clear();

    m_profilerEnabled = 0;

    m_mutex = MosUtilities::MosCreateMutex();

    if (m_mutex)
    {
        // m_mutex is destroyed after MemNinja report, this will cause fake memory leak,
        // the following 2 lines is to circumvent Memninja counter validation and log parser
        MosUtilities::MosAtomicDecrement(&MosUtilities::m_mosMemAllocCounter);
        MOS_MEMNINJA_FREE_MESSAGE(m_mutex, __FUNCTION__, __FILE__, __LINE__);
    }
    else
    {
        MOS_OS_ASSERTMESSAGE("Create Mutex failed!");
    }
    
}

MediaPerfProfilerNext::~MediaPerfProfilerNext()
{
    if (m_mutex != nullptr)
    {
        MosUtilities::MosDestroyMutex(m_mutex);
        m_mutex = nullptr;
    }
}

MediaPerfProfilerNext* MediaPerfProfilerNext::Instance()
{
    static MediaPerfProfilerNext instance;
    if (!instance.m_mutex && instance.m_profilerEnabled)
    {
        MOS_OS_ASSERTMESSAGE("Create MediaPerfProfilerNext failed!");
        return nullptr;
    }
    else
    {
        return &instance;
    }
}

void MediaPerfProfilerNext::Destroy(MediaPerfProfilerNext* profiler, void* context, MOS_INTERFACE *osInterface)
{
    PERF_UTILITY_PRINT;

    CHK_NULL_NO_STATUS_RETURN(profiler);
    CHK_NULL_NO_STATUS_RETURN(osInterface);

    if (profiler->m_profilerEnabled == 0 || profiler->m_mutex == nullptr)
    {
        return;
    }

    PMOS_CONTEXT pOsContext = osInterface->pOsContext;
    MosUtilities::MosLockMutex(profiler->m_mutex);
    profiler->m_refMap[pOsContext]--;

    osInterface->pfnWaitAllCmdCompletion(osInterface);

    profiler->m_contextIndexMap.erase(context);

    if (profiler->m_refMap[pOsContext] == 0)
    {
        if (profiler->m_initializedMap[pOsContext] == true)
        {
            if(profiler->m_enableProfilerDump)
            {
                profiler->SavePerfData(osInterface);
            }

            osInterface->pfnFreeResource(
                osInterface,
                profiler->m_perfStoreBufferMap[pOsContext]);

            MOS_FreeMemAndSetNull(profiler->m_perfStoreBufferMap[pOsContext]);

            profiler->m_perfStoreBufferMap.erase(pOsContext);
            profiler->m_initializedMap.erase(pOsContext);
            profiler->m_refMap.erase(pOsContext);
            profiler->m_perfDataIndexMap.erase(pOsContext);
        }

        MosUtilities::MosUnlockMutex(profiler->m_mutex);
    }
    else
    {
        MosUtilities::MosUnlockMutex(profiler->m_mutex);
    }
}

MOS_STATUS MediaPerfProfilerNext::Initialize(void* context, MOS_INTERFACE *osInterface)
{
    MOS_STATUS status = MOS_STATUS_SUCCESS;
    MOS_USER_FEATURE_VALUE_DATA userFeatureData;

    CHK_NULL_RETURN(osInterface);
    CHK_NULL_RETURN(m_mutex);

    PMOS_CONTEXT pOsContext = osInterface->pOsContext;
    CHK_NULL_RETURN(pOsContext);

    // Check whether profiler is enabled
    MOS_ZeroMemory(&userFeatureData, sizeof(userFeatureData));
    MOS_UserFeature_ReadValue_ID(
        nullptr,
        __MEDIA_USER_FEATURE_VALUE_PERF_PROFILER_ENABLE_ID,
        &userFeatureData,
        pOsContext);

    m_profilerEnabled = userFeatureData.bData;

    if (m_profilerEnabled == 0 || m_mutex == nullptr)
    {
        return MOS_STATUS_SUCCESS;
    }

    MosUtilities::MosLockMutex(m_mutex);

    m_contextIndexMap[context] = 0;

    if (m_initializedMap[pOsContext] == true)
    {
        MosUtilities::MosUnlockMutex(m_mutex);
        return status;
    }
    
    m_refMap[pOsContext]++;

    m_enableProfilerDump = MosUtilities::MosIsProfilerDumpEnabled();

    // Read output file name
    MOS_ZeroMemory(&userFeatureData, sizeof(userFeatureData));
    userFeatureData.StringData.pStringData = m_outputFileName;
    status = MOS_UserFeature_ReadValue_ID(
        NULL,
        __MEDIA_USER_FEATURE_VALUE_PERF_PROFILER_OUTPUT_FILE,
        &userFeatureData,
        osInterface->pOsContext);

    if (status != MOS_STATUS_SUCCESS)
    {
        MosUtilities::MosUnlockMutex(m_mutex);
        return status;
    }

    if (userFeatureData.StringData.uSize == MOS_MAX_PATH_LENGTH + 1)
    {
        userFeatureData.StringData.uSize = 0;
    }

    if (userFeatureData.StringData.uSize > 0)
    {
        userFeatureData.StringData.pStringData[userFeatureData.StringData.uSize] = '\0';
        userFeatureData.StringData.uSize++;
    }

    // Read buffer size
    MOS_ZeroMemory(&userFeatureData, sizeof(userFeatureData));
    MOS_UserFeature_ReadValue_ID(
        nullptr,
        __MEDIA_USER_FEATURE_VALUE_PERF_PROFILER_BUFFER_SIZE,
        &userFeatureData,
        osInterface->pOsContext);
    m_bufferSize = userFeatureData.u32Data;

    m_timerBase = Mos_Specific_GetTsFrequency(osInterface);

    // Read multi processes support
    MOS_ZeroMemory(&userFeatureData, sizeof(userFeatureData));
    MOS_UserFeature_ReadValue_ID(
        nullptr,
        __MEDIA_USER_FEATURE_VALUE_PERF_PROFILER_ENABLE_MULTI_PROCESS,
        &userFeatureData,
        osInterface->pOsContext);
    m_multiprocess = userFeatureData.u32Data;

    // Read memory information register address
    int8_t regIndex = 0;
    for (regIndex = 0; regIndex < 8; regIndex++)
    {
        MOS_ZeroMemory(&userFeatureData, sizeof(userFeatureData));
        MOS_UserFeature_ReadValue_ID(
            nullptr,
            __MEDIA_USER_FEATURE_VALUE_PERF_PROFILER_REGISTER_1 + regIndex,
            &userFeatureData,
            osInterface->pOsContext);
        m_registers[regIndex] = userFeatureData.u32Data;
    }

    PMOS_RESOURCE  pPerfStoreBuffer = (PMOS_RESOURCE)MOS_AllocAndZeroMemory(sizeof(MOS_RESOURCE));
    m_perfStoreBufferMap[pOsContext] = pPerfStoreBuffer;
    // Allocate the buffer which store the performance data
    MOS_ALLOC_GFXRES_PARAMS allocParams;
    MOS_ZeroMemory(&allocParams, sizeof(MOS_ALLOC_GFXRES_PARAMS));
    allocParams.Type        = MOS_GFXRES_BUFFER;
    allocParams.TileType    = MOS_TILE_LINEAR;
    allocParams.Format      = Format_Buffer;
    allocParams.dwBytes     = m_bufferSize;
    allocParams.pBufName    = "PerfStoreBuffer";

    status = osInterface->pfnAllocateResource(
                                        osInterface,
                                        &allocParams,
                                        pPerfStoreBuffer);

    CHK_STATUS_UNLOCK_MUTEX_RETURN(status);

    CHK_STATUS_UNLOCK_MUTEX_RETURN(
        osInterface->pfnSkipResourceSync(pPerfStoreBuffer));

    PLATFORM platform = { IGFX_UNKNOWN };
    osInterface->pfnGetPlatform(osInterface, &platform);

    MOS_LOCK_PARAMS lockFlags;
    MOS_ZeroMemory(&lockFlags, sizeof(MOS_LOCK_PARAMS));
    lockFlags.WriteOnly   = 1;

    NodeHeader* header = (NodeHeader*)osInterface->pfnLockResource(
            osInterface,
            pPerfStoreBuffer,
            &lockFlags);

    CHK_NULL_UNLOCK_MUTEX_RETURN(header);

    // Append the header info
    MOS_ZeroMemory(header, m_bufferSize);
    header->eventType   = UMD_PERF_LOG;

    uint32_t mappedPlatFormId = PlatFormIdMap(platform);
    header->genPlatform = (mappedPlatFormId - 8) & 0x7;
    header->genPlatform_ext = ((mappedPlatFormId - 8) >> 3) & 0x3;

    if (IsPerfModeWidthMemInfo(m_registers))
    {
        header->perfMode    = UMD_PERF_MODE_WITH_MEMORY_INFO;
    }
    else
    {
        header->perfMode    = UMD_PERF_MODE_TIMING_ONLY;
    }

    osInterface->pfnUnlockResource(
            osInterface,
            pPerfStoreBuffer);

    m_initializedMap[pOsContext] = true;

    MosUtilities::MosUnlockMutex(m_mutex);

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MediaPerfProfilerNext::StoreData(
    std::shared_ptr<mhw::mi::Itf> miItf,
    PMOS_COMMAND_BUFFER           cmdBuffer,
    PMOS_CONTEXT                  pOsContext,
    uint32_t offset,
    uint32_t value)
{
    CHK_NULL_RETURN(miItf);

    auto& storeDataParams            = miItf->MHW_GETPAR_F(MI_STORE_DATA_IMM)();
    storeDataParams                  = {};
    storeDataParams.pOsResource      = m_perfStoreBufferMap[pOsContext];
    storeDataParams.dwResourceOffset = offset;
    storeDataParams.dwValue          = value;
    CHK_STATUS_RETURN(miItf->MHW_ADDCMD_F(MI_STORE_DATA_IMM)(cmdBuffer));

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MediaPerfProfilerNext::StoreRegister(
    MOS_INTERFACE                 *osInterface,
    std::shared_ptr<mhw::mi::Itf> miItf,
    PMOS_COMMAND_BUFFER           cmdBuffer,
    uint32_t offset,
    uint32_t reg)
{
    CHK_NULL_RETURN(osInterface);
    CHK_NULL_RETURN(miItf);

    auto& storeRegMemParams           = miItf->MHW_GETPAR_F(MI_STORE_REGISTER_MEM)();
    storeRegMemParams                 = {};
    storeRegMemParams.presStoreBuffer = m_perfStoreBufferMap[osInterface->pOsContext];
    storeRegMemParams.dwOffset        = offset;
    storeRegMemParams.dwRegister      = reg;

    MEDIA_FEATURE_TABLE* skuTable = osInterface->pfnGetSkuTable(osInterface);
    if(skuTable && MEDIA_IS_SKU(skuTable, FtrMemoryRemapSupport))
    {
        storeRegMemParams.dwOption = CCS_HW_FRONT_END_MMIO_REMAP;
    }

    CHK_STATUS_RETURN(miItf->MHW_ADDCMD_F(MI_STORE_REGISTER_MEM)(cmdBuffer));

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MediaPerfProfilerNext::StoreTSByPipeCtrl(
    std::shared_ptr<mhw::mi::Itf> miItf,
    PMOS_COMMAND_BUFFER           cmdBuffer,
    PMOS_CONTEXT                  pOsContext,
    uint32_t                      offset)
{
    CHK_NULL_RETURN(miItf);

    auto& PipeControlParams            = miItf->MHW_GETPAR_F(PIPE_CONTROL)();
    PipeControlParams                  = {};
    PipeControlParams.dwResourceOffset = offset;
    PipeControlParams.dwPostSyncOp     = MHW_FLUSH_WRITE_TIMESTAMP_REG;
    PipeControlParams.dwFlushMode      = MHW_FLUSH_READ_CACHE;
    PipeControlParams.presDest         = m_perfStoreBufferMap[pOsContext];

    CHK_STATUS_RETURN(miItf->MHW_ADDCMD_F(PIPE_CONTROL)(cmdBuffer));

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MediaPerfProfilerNext::StoreTSByMiFlush(
    std::shared_ptr<mhw::mi::Itf> miItf,
    PMOS_COMMAND_BUFFER           cmdBuffer,
    PMOS_CONTEXT                  pOsContext,
    uint32_t                      offset)
{
    CHK_NULL_RETURN(miItf);

    auto& FlushDwParams             = miItf->MHW_GETPAR_F(MI_FLUSH_DW)();
    FlushDwParams                   = {};
    FlushDwParams.postSyncOperation = MHW_FLUSH_WRITE_TIMESTAMP_REG;
    FlushDwParams.dwResourceOffset  = offset;
    FlushDwParams.pOsResource       = m_perfStoreBufferMap[pOsContext];

    CHK_STATUS_RETURN(miItf->MHW_ADDCMD_F(MI_FLUSH_DW)(cmdBuffer));

    return MOS_STATUS_SUCCESS;
}

MOS_STATUS MediaPerfProfilerNext::AddPerfCollectStartCmd(void* context,
    MOS_INTERFACE                 *osInterface,
    std::shared_ptr<mhw::mi::Itf> miItf,
    MOS_COMMAND_BUFFER            *cmdBuffer)
{
    MOS_STATUS status = MOS_STATUS_SUCCESS;

    CHK_NULL_RETURN(osInterface);
    CHK_NULL_RETURN(miItf);
    CHK_NULL_RETURN(cmdBuffer);
    CHK_NULL_RETURN(m_mutex);

    PMOS_CONTEXT pOsContext = osInterface->pOsContext;
    CHK_NULL_RETURN(pOsContext);

    if (m_profilerEnabled == 0 || m_initializedMap[pOsContext] == false)
    {
        return status;
    }

    uint32_t perfDataIndex = 0;

    MosUtilities::MosLockMutex(m_mutex);

    perfDataIndex = m_perfDataIndexMap[pOsContext];
    m_perfDataIndexMap[pOsContext]++;
    m_contextIndexMap[context] = perfDataIndex;

    MosUtilities::MosUnlockMutex(m_mutex);

    bool             rcsEngineUsed = false;
    MOS_GPU_CONTEXT  gpuContext;

    gpuContext     = osInterface->pfnGetGpuContext(osInterface);
    rcsEngineUsed = MOS_RCS_ENGINE_USED(gpuContext);

    if (m_multiprocess)
    {
        CHK_STATUS_RETURN(StoreData(
            miItf,
            cmdBuffer,
            pOsContext,
            BASE_OF_NODE(perfDataIndex) + OFFSET_OF(PerfEntry, processId),
            MosUtilities::MosGetPid()));
    }

    CHK_STATUS_RETURN(StoreData(
        miItf,
        cmdBuffer,
        pOsContext,
        BASE_OF_NODE(perfDataIndex) + OFFSET_OF(PerfEntry, perfTag),
        osInterface->pfnGetPerfTag(osInterface)));

    CHK_STATUS_RETURN(StoreData(
        miItf,
        cmdBuffer,
        pOsContext,
        BASE_OF_NODE(perfDataIndex) + OFFSET_OF(PerfEntry, engineTag),
        GpuContextToGpuNode(gpuContext)));

    if (m_timerBase != 0)
    {
        CHK_STATUS_RETURN(StoreData(
            miItf,
            cmdBuffer,
            pOsContext,
            BASE_OF_NODE(perfDataIndex) + OFFSET_OF(PerfEntry, timeStampBase),
            m_timerBase));
    }

    int8_t regIndex = 0;
    for (regIndex = 0; regIndex < 8; regIndex++)
    {
        if (m_registers[regIndex] != 0)
        {
            CHK_STATUS_RETURN(StoreRegister(
                osInterface,
                miItf,
                cmdBuffer,
                BASE_OF_NODE(perfDataIndex) + OFFSET_OF(PerfEntry, beginRegisterValue[regIndex]),
                m_registers[regIndex]));
        }
    }

    uint64_t beginCPUTimestamp = MosUtilities::MosGetCurTime();
    uint32_t timeStamp[2];
    MOS_SecureMemcpy(timeStamp, 2*sizeof(uint32_t), &beginCPUTimestamp, 2*sizeof(uint32_t));

    for (int i = 0; i < 2; i++)
    {
        CHK_STATUS_RETURN(StoreData(
            miItf,
            cmdBuffer,
            pOsContext,
            BASE_OF_NODE(perfDataIndex) + OFFSET_OF(PerfEntry, beginCpuTime[i]),
            timeStamp[i]));
    }

    // The address of timestamp must be 8 bytes aligned.
    uint32_t offset = BASE_OF_NODE(perfDataIndex) + OFFSET_OF(PerfEntry, beginTimeClockValue);
    offset = MOS_ALIGN_CEIL(offset, 8);

    if (rcsEngineUsed)
    {
        CHK_STATUS_RETURN(StoreTSByPipeCtrl(
            miItf,
            cmdBuffer,
            pOsContext,
            offset));
    }
    else
    {
        CHK_STATUS_RETURN(StoreTSByMiFlush(
            miItf,
            cmdBuffer,
            pOsContext,
            offset));
    }

    return status;
}

MOS_STATUS MediaPerfProfilerNext::AddPerfCollectEndCmd(void* context,
    MOS_INTERFACE                 *osInterface,
    std::shared_ptr<mhw::mi::Itf> miItf,
    MOS_COMMAND_BUFFER            *cmdBuffer)
{
    MOS_STATUS       status        = MOS_STATUS_SUCCESS;

    CHK_NULL_RETURN(osInterface);
    CHK_NULL_RETURN(miItf);
    CHK_NULL_RETURN(cmdBuffer);

    PMOS_CONTEXT pOsContext = osInterface->pOsContext;
    CHK_NULL_RETURN(pOsContext);

    if (m_profilerEnabled == 0 || m_initializedMap[pOsContext] == false)
    {
        return status;
    }

    MOS_GPU_CONTEXT  gpuContext;
    bool             rcsEngineUsed = false;
    uint32_t         perfDataIndex = 0;

    gpuContext     = osInterface->pfnGetGpuContext(osInterface);
    rcsEngineUsed = MOS_RCS_ENGINE_USED(gpuContext);

    perfDataIndex = m_contextIndexMap[context];

    int8_t regIndex = 0;
    for (regIndex = 0; regIndex < 8; regIndex++)
    {
        if (m_registers[regIndex] != 0)
        {
            CHK_STATUS_RETURN(StoreRegister(
                osInterface,
                miItf,
                cmdBuffer,
                BASE_OF_NODE(perfDataIndex) + OFFSET_OF(PerfEntry, endRegisterValue[regIndex]),
                m_registers[regIndex]));
        }
    }

    // The address of timestamp must be 8 bytes aligned.
    uint32_t offset = BASE_OF_NODE(perfDataIndex) + OFFSET_OF(PerfEntry, endTimeClockValue);
    offset = MOS_ALIGN_CEIL(offset, 8);

    if (rcsEngineUsed)
    {
        CHK_STATUS_RETURN(StoreTSByPipeCtrl(
            miItf,
            cmdBuffer,
            pOsContext,
            offset));
    }
    else
    {
        CHK_STATUS_RETURN(StoreTSByMiFlush(
            miItf,
            cmdBuffer,
            pOsContext,
            offset));
    }

    return status;
}

MOS_STATUS MediaPerfProfilerNext::SavePerfData(MOS_INTERFACE *osInterface)
{
    MOS_STATUS status = MOS_STATUS_SUCCESS;

    CHK_NULL_RETURN(osInterface);

    PMOS_CONTEXT pOsContext = osInterface->pOsContext;
    CHK_NULL_RETURN(pOsContext);

    if (m_contextIndexMap[pOsContext] > 0)
    {
        MOS_LOCK_PARAMS     LockFlagsNoOverWrite;
        MOS_ZeroMemory(&LockFlagsNoOverWrite, sizeof(MOS_LOCK_PARAMS));

        LockFlagsNoOverWrite.WriteOnly = 1;
        LockFlagsNoOverWrite.NoOverWrite = 1;

        uint8_t* pData = (uint8_t*)osInterface->pfnLockResource(
            osInterface,
            m_perfStoreBufferMap[pOsContext],
            &LockFlagsNoOverWrite);

        CHK_NULL_RETURN(pData);

        if (m_multiprocess)
        {
            int32_t pid = MosUtilities::MosGetPid();
            tm      localtime = { 0 };
            MosUtilities::MosGetLocalTime(&localtime);
            char outputFileName[MOS_MAX_PATH_LENGTH + 1];

            MOS_SecureStringPrint(outputFileName, MOS_MAX_PATH_LENGTH + 1, MOS_MAX_PATH_LENGTH + 1, "%s-pid%d-%04d%02d%02d%02d%02d%02d.bin",
                m_outputFileName, pid, localtime.tm_year + 1900, localtime.tm_mon + 1, localtime.tm_mday, localtime.tm_hour, localtime.tm_min, localtime.tm_sec);

            MosUtilities::MosWriteFileFromPtr(outputFileName, pData, BASE_OF_NODE(m_perfDataIndexMap[pOsContext]));
        }
        else
        {
            MosUtilities::MosWriteFileFromPtr(m_outputFileName, pData, BASE_OF_NODE(m_perfDataIndexMap[pOsContext]));
        }

        osInterface->pfnUnlockResource(
            osInterface,
            m_perfStoreBufferMap[pOsContext]);
    }

    return status;
}

PerfGPUNode MediaPerfProfilerNext::GpuContextToGpuNode(MOS_GPU_CONTEXT context)
{
    PerfGPUNode node = PERF_GPU_NODE_UNKNOW;

    switch (context)
    {
        case MOS_GPU_CONTEXT_RENDER:
        case MOS_GPU_CONTEXT_RENDER2:
        case MOS_GPU_CONTEXT_RENDER3:
        case MOS_GPU_CONTEXT_RENDER4:
        case MOS_GPU_OVERLAY_CONTEXT:
        case MOS_GPU_CONTEXT_RENDER_RA:
            node = PERF_GPU_NODE_3D;
            break;
        case MOS_GPU_CONTEXT_COMPUTE:
        case MOS_GPU_CONTEXT_CM_COMPUTE:
        case MOS_GPU_CONTEXT_COMPUTE_RA:
            node = PERF_GPU_NODE_3D;
            break;
        case MOS_GPU_CONTEXT_VIDEO:
        case MOS_GPU_CONTEXT_VIDEO2:
        case MOS_GPU_CONTEXT_VIDEO3:
        case MOS_GPU_CONTEXT_VIDEO4:
        case MOS_GPU_CONTEXT_VIDEO5:
        case MOS_GPU_CONTEXT_VIDEO6:
        case MOS_GPU_CONTEXT_VIDEO7:
            node = PERF_GPU_NODE_VIDEO;
            break;
        case MOS_GPU_CONTEXT_VDBOX2_VIDEO:
        case MOS_GPU_CONTEXT_VDBOX2_VIDEO2:
        case MOS_GPU_CONTEXT_VDBOX2_VIDEO3:
            node = PERF_GPU_NODE_VIDEO2;
            break;
        case MOS_GPU_CONTEXT_VEBOX:
        case MOS_GPU_CONTEXT_VEBOX2:
            node = PERF_GPU_NODE_VE;
            break;
        default:
            node = PERF_GPU_NODE_UNKNOW;
            break;
    }

    return node;
}

uint32_t MediaPerfProfilerNext::PlatFormIdMap(PLATFORM platform)
{
    uint32_t perfPlatFormId = 0;

    if (GFX_GET_CURRENT_RENDERCORE(platform) > IGFX_GEN12LP_CORE)
    {
        perfPlatFormId = ((((uint32_t)(GFX_GET_CURRENT_RENDERCORE(platform)) >> 8) - 0xc) << 2) + (GFX_GET_CURRENT_RENDERCORE(platform) & 0x3) + (uint32_t)(IGFX_GEN12LP_CORE);
    }
    else
    {
        perfPlatFormId = (uint32_t)(GFX_GET_CURRENT_RENDERCORE(platform));
    }

    return perfPlatFormId;
}

bool MediaPerfProfilerNext::IsPerfModeWidthMemInfo(uint32_t *regs)
{
    int8_t index = 0;
    bool   ret   = false;

    for (index = 0; index < 8; index++)
    {
        if (regs[index] != 0)
        {
            ret = true;
            break;
        }
    }

    return ret;
}
