text: /**
   * Copyright (C) 2016-2022 Xilinx, Inc
   *
   * Licensed under the Apache License, Version 2.0 (the "License"). You may
   * not use this file except in compliance with the License. A copy of the
   * License is located at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   * License for the specific language governing permissions and limitations
   * under the License.
   */
  
  #include <iostream>
  #include <sstream>
  
  #ifdef _WIN32
  #include <process.h>
  #else
  #include <sys/types.h>
  #include <unistd.h>
  #endif
  
  #ifdef _WIN32
  #pragma warning (disable : 4996 4267 4244 4200)
  /* 4267 : Disable warning for conversion of size_t to int32_t */
  /* 4244 : Disable warning for conversion of uint64_t to uint32_t */
  #endif
  
  #include <boost/property_tree/ptree.hpp>
  #include <boost/property_tree/json_parser.hpp>
  #include <boost/property_tree/xml_parser.hpp>
  
  #include "core/common/config_reader.h"
  #include "core/common/message.h"
  #include "core/include/xclbin.h"
  
  #define XDP_SOURCE
  
  #include "xdp/profile/database/database.h"
  #include "xdp/profile/database/static_info/device_info.h"
  #include "xdp/profile/database/static_info/pl_constructs.h"
  #include "xdp/profile/database/static_info/xclbin_info.h"
  #include "xdp/profile/database/static_info_database.h"
  #include "xdp/profile/device/device_intf.h"
  #include "xdp/profile/plugin/vp_base/utility.h"
  #include "xdp/profile/writer/vp_base/vp_run_summary.h"
  
  #define XAM_STALL_PROPERTY_MASK        0x4
  #define XMON_TRACE_PROPERTY_MASK       0x1
  
  static std::string convertMemoryName(const std::string &mem)
  {
    if (0 == mem.compare("DDR[0]"))
      return "bank0";
    if (0 == mem.compare("DDR[1]"))
      return "bank1";
    if (0 == mem.compare("DDR[2]"))
      return "bank2";
    if (0 == mem.compare("DDR[3]"))
      return "bank3";
  
    return mem;
  }
  
  namespace xdp {
  
    VPStaticDatabase::VPStaticDatabase(VPDatabase* d)
      : db(d)
      , runSummary(nullptr)
      , systemDiagram("")
      , softwareEmulationDeviceName("")
      , aieDevInst(nullptr)
      , aieDevice(nullptr)
      , deallocateAieDevice(nullptr)
    {
  #ifdef _WIN32
      pid = _getpid() ;
  #else
      pid = static_cast<int>(getpid()) ;
  #endif
    }
  
    VPStaticDatabase::~VPStaticDatabase()
    {
      if (runSummary != nullptr) {
        runSummary->write(false) ;
        delete runSummary ;
      }
  
      // AIE specific functions
      if (aieDevice != nullptr && deallocateAieDevice != nullptr)
        deallocateAieDevice(aieDevice) ;
  
      for (auto iter : deviceInfo) {
        delete iter.second ;
      }
    }
  
    // ***********************************************************************
    // ***** Functions related to information on the running application *****
    int VPStaticDatabase::getPid() const
    {
      return pid ;
    }
  
    uint64_t VPStaticDatabase::getApplicationStartTime() const
    {
      return applicationStartTime ;
    }
  
    void VPStaticDatabase::setApplicationStartTime(uint64_t t)
    {
      std::lock_guard<std::mutex> lock(summaryLock) ;
      applicationStartTime = t ;
    }
  
    // ***************************************************
    // ***** Functions related to OpenCL information *****
  
    std::set<uint64_t>& VPStaticDatabase::getCommandQueueAddresses()
    {
      return commandQueueAddresses ;
    }
  
    std::set<std::string>& VPStaticDatabase::getEnqueuedKernels()
    {
      return enqueuedKernels ;
    }
  
    void VPStaticDatabase::addEnqueuedKernel(const std::string& identifier)
    {
      std::lock_guard<std::mutex> lock(openCLLock) ;
      enqueuedKernels.emplace(identifier) ;
    }
  
    void VPStaticDatabase::setNumDevices(uint64_t contextId, uint64_t numDevices)
    {
      contextIdToNumDevices[contextId] = numDevices ;
    }
  
    uint64_t VPStaticDatabase::getNumDevices(uint64_t contextId)
    {
      if (contextIdToNumDevices.find(contextId) == contextIdToNumDevices.end())
        return 0 ;
      return contextIdToNumDevices[contextId] ;
    }
  
    std::string VPStaticDatabase::getSoftwareEmulationDeviceName()
    {
      std::lock_guard<std::mutex> lock(openCLLock) ;
      return softwareEmulationDeviceName ;
    }
  
    void VPStaticDatabase::setSoftwareEmulationDeviceName(const std::string& name)
    {
      std::lock_guard<std::mutex> lock(openCLLock) ;
      softwareEmulationDeviceName = name ;
    }
  
    std::map<std::string, uint64_t>
    VPStaticDatabase::getSoftwareEmulationCUCounts()
    {
      std::lock_guard<std::mutex> lock(openCLLock) ;
      return softwareEmulationCUCounts ;
    }
  
    void
    VPStaticDatabase::addSoftwareEmulationCUInstance(const std::string& kName)
    {
      std::lock_guard<std::mutex> lock(openCLLock) ;
      if (softwareEmulationCUCounts.find(kName) ==
          softwareEmulationCUCounts.end())
        softwareEmulationCUCounts[kName] = 1 ;
      else
        softwareEmulationCUCounts[kName] += 1 ;
    }
  
    std::map<std::string, bool>&
    VPStaticDatabase::getSoftwareEmulationMemUsage()
    {
      std::lock_guard<std::mutex> lock(openCLLock) ;
      return softwareEmulationMemUsage ;
    }
  
    void
    VPStaticDatabase::
    addSoftwareEmulationMemUsage(const std::string& mem, bool used)
    {
      std::lock_guard<std::mutex> lock(openCLLock) ;
      softwareEmulationMemUsage[mem] = used ;
    }
  
    std::vector<std::string>&
    VPStaticDatabase::getSoftwareEmulationPortBitWidths()
    {
      std::lock_guard<std::mutex> lock(openCLLock) ;
      return softwareEmulationPortBitWidths ;
    }
  
    void VPStaticDatabase::addSoftwareEmulationPortBitWidth(const std::string& s)
    {
      std::lock_guard<std::mutex> lock(openCLLock) ;
      softwareEmulationPortBitWidths.push_back(s) ;
    }
  
    // ************************************************
    // ***** Functions related to the run summary *****
    std::vector<std::pair<std::string, std::string>>&
    VPStaticDatabase::getOpenedFiles()
    {
      std::lock_guard<std::mutex> lock(summaryLock) ;
      return openedFiles ;
    }
  
    void VPStaticDatabase::addOpenedFile(const std::string& name,
                                         const std::string& type)
    {
      {
        // Protect changes to openedFiles and creation of the run summary.
        //  The write function, however, needs to query the opened files so
        //  place the lock inside its own scope.
        std::lock_guard<std::mutex> lock(summaryLock) ;
  
        openedFiles.push_back(std::make_pair(name, type)) ;
  
        if (runSummary == nullptr)
          runSummary = new VPRunSummaryWriter("xrt.run_summary", db) ;
      }
      runSummary->write(false) ;
    }
  
    std::string VPStaticDatabase::getSystemDiagram()
    {
      std::lock_guard<std::mutex> lock(summaryLock) ;
      return systemDiagram ;
    }
  
    // ***************************************************************
    // ***** Functions related to information on all the devices *****
    uint64_t VPStaticDatabase::getNumDevices()
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      return deviceInfo.size() ;
    }
  
    DeviceInfo* VPStaticDatabase::getDeviceInfo(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return nullptr ;
      return deviceInfo[deviceId] ;
    }
  
    std::vector<std::string> VPStaticDatabase::getDeviceNames()
    {
      std::vector<std::string> uniqueNames ;
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      for (auto device : deviceInfo) {
        uniqueNames.push_back(device.second->getUniqueDeviceName()) ;
      }
      return uniqueNames ;
    }
  
    std::vector<DeviceInfo*> VPStaticDatabase::getDeviceInfos()
    {
      std::vector<DeviceInfo*> infos ;
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      for (auto device : deviceInfo) {
        infos.push_back(device.second) ;
      }
      return infos ;
    }
  
    bool VPStaticDatabase::hasStallInfo()
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      for (auto device : deviceInfo) {
        for (auto xclbin : device.second->getLoadedXclbins()) {
          for (auto cu : xclbin->pl.cus) {
            if (cu.second->getStallEnabled())
              return true ;
          }
        }
      }
      return false ;
    }
  
    XclbinInfo* VPStaticDatabase::getCurrentlyLoadedXclbin(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return nullptr ;
      return deviceInfo[deviceId]->currentXclbin() ;
    }
  
    void VPStaticDatabase::deleteCurrentlyUsedDeviceInterface(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return ;
  
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return ;
      if (xclbin->deviceIntf) {
        delete xclbin->deviceIntf ;
        xclbin->deviceIntf = nullptr ;
      }
    }
  
    bool VPStaticDatabase::isDeviceReady(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return false ;
      return deviceInfo[deviceId]->isReady ;
    }
  
    double VPStaticDatabase::getClockRateMHz(uint64_t deviceId, bool PL)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      // If we don't have any information on the specific ID, return
      //  defaults.  300 MHz for PL clock rate and 1 GHz for AIE clock rate.
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return PL ? 300.0 : 1000.0 ;
  
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return PL ? 300.0 : 1000.0 ;
  
      if (PL)
        return xclbin->pl.clockRatePLMHz ;
      else
        return xclbin->aie.clockRateAIEMHz ;
    }
  
    void
    VPStaticDatabase::setDeviceName(uint64_t deviceId, const std::string& name)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return ;
      deviceInfo[deviceId]->deviceName = name ;
    }
  
    std::string VPStaticDatabase::getDeviceName(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return "" ;
      return deviceInfo[deviceId]->deviceName ;
    }
  
    void VPStaticDatabase::setDeviceIntf(uint64_t deviceId, DeviceIntf* devIntf)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return ;
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return ;
      xclbin->deviceIntf = devIntf ;
    }
  
    DeviceIntf* VPStaticDatabase::getDeviceIntf(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return nullptr ;
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return nullptr ;
  
      return xclbin->deviceIntf ;
    }
  
    void VPStaticDatabase::setKDMACount(uint64_t deviceId, uint64_t num)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return ;
      deviceInfo[deviceId]->kdmaCount = num ;
    }
  
    uint64_t VPStaticDatabase::getKDMACount(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return 0 ;
      return deviceInfo[deviceId]->kdmaCount ;
    }
  
    void VPStaticDatabase::setHostMaxReadBW(uint64_t deviceId, double bw)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return ;
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return ;
      xclbin->pl.hostMaxReadBW = bw ;
    }
  
    double VPStaticDatabase::getHostMaxReadBW(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return 0.0 ;
  
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return 0.0 ;
  
      return xclbin->pl.hostMaxReadBW ;
    }
  
    void VPStaticDatabase::setHostMaxWriteBW(uint64_t deviceId, double bw)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return ;
  
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return ;
  
      xclbin->pl.hostMaxWriteBW = bw ;
    }
  
    double VPStaticDatabase::getHostMaxWriteBW(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return 0.0 ;
  
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return 0.0 ;
  
      return xclbin->pl.hostMaxWriteBW ;
    }
  
    void VPStaticDatabase::setKernelMaxReadBW(uint64_t deviceId, double bw)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return ;
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return ;
      xclbin->pl.kernelMaxReadBW = bw ;
    }
  
    double VPStaticDatabase::getKernelMaxReadBW(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return 0.0 ;
  
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return 0.0 ;
  
      return xclbin->pl.kernelMaxReadBW ;
    }
  
    void VPStaticDatabase::setKernelMaxWriteBW(uint64_t deviceId, double bw)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return ;
  
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return ;
  
      xclbin->pl.kernelMaxWriteBW = bw ;
    }
  
    double VPStaticDatabase::getKernelMaxWriteBW(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return 0.0 ;
  
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return 0.0 ;
  
      return xclbin->pl.kernelMaxWriteBW ;
    }
  
    std::string VPStaticDatabase::getXclbinName(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return "" ;
  
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return "" ;
  
      return xclbin->name ;
    }
  
    std::vector<XclbinInfo*> VPStaticDatabase::getLoadedXclbins(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end()) {
        std::vector<XclbinInfo*> blank ;
        return blank ;
      }
      return deviceInfo[deviceId]->getLoadedXclbins() ;
    }
  
    ComputeUnitInstance* VPStaticDatabase::getCU(uint64_t deviceId, int32_t cuId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return nullptr ;
  
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return nullptr ;
  
      return xclbin->pl.cus[cuId] ;
    }
  
    std::map<int32_t, ComputeUnitInstance*>*
    VPStaticDatabase::getCUs(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return nullptr ;
  
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return nullptr ;
  
      return &(xclbin->pl.cus) ;
    }
  
    std::map<int32_t, Memory*>*
    VPStaticDatabase::getMemoryInfo(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return nullptr ;
  
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return nullptr ;
  
      return &(xclbin->pl.memoryInfo) ;
    }
  
    Memory* VPStaticDatabase::getMemory(uint64_t deviceId, int32_t memId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return nullptr ;
  
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return nullptr ;
  
      if (xclbin->pl.memoryInfo.find(memId) == xclbin->pl.memoryInfo.end())
        return nullptr ;
  
      return xclbin->pl.memoryInfo[memId] ;
    }
  
    void VPStaticDatabase::getDataflowConfiguration(uint64_t deviceId,
                                                    bool* config,
                                                    size_t size)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return ;
  
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return ;
  
      // User space AM in sorted order of their slotIds.  Matches with
      //  sorted list of AM in xdp::DeviceIntf
      size_t count = 0 ;
      for (auto mon : xclbin->pl.ams) {
        if (count >= size)
          return ;
        auto cu = xclbin->pl.cus[mon->cuIndex] ;
        config[count] = cu->getDataflowEnabled() ;
        ++count ;
      }
    }
  
    void VPStaticDatabase::getFaConfiguration(uint64_t deviceId, bool* config,
                                              size_t size)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return ;
  
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return ;
  
      // User space AM in sorted order of their slotIds.  Matches with
      //  sorted list of AM in xdp::DeviceIntf
      size_t count = 0 ;
      for (auto mon : xclbin->pl.ams) {
        if (count >= size)
          return ;
        auto cu = xclbin->pl.cus[mon->cuIndex] ;
        config[count] = cu->getHasFA() ;
        ++count ;
      }
    }
  
    std::string VPStaticDatabase::getCtxInfo(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return "" ;
      return deviceInfo[deviceId]->ctxInfo ;
    }
  
    // *********************************************************
    // ***** Functions related to AIE specific information *****
    bool VPStaticDatabase::isAIECounterRead(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return false ;
  
      for (auto xclbin : deviceInfo[deviceId]->getLoadedXclbins()) {
        if (!xclbin)
          continue ;
        if (xclbin->aie.isAIEcounterRead)
          return true ;
      }
      return false ;
    }
  
    void VPStaticDatabase::setIsAIECounterRead(uint64_t deviceId, bool val)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return ;
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return ;
  
      xclbin->aie.isAIEcounterRead = val ;
    }
  
    void VPStaticDatabase::setIsGMIORead(uint64_t deviceId, bool val)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return ;
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return ;
  
      xclbin->aie.isGMIORead = val ;
    }
  
    bool VPStaticDatabase::isGMIORead(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return false ;
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return false ;
      return xclbin->aie.isGMIORead ;
    }
  
    uint64_t VPStaticDatabase::getNumAIECounter(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return 0 ;
  
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return 0 ;
  
      return xclbin->aie.aieList.size() ;
    }
  
    uint64_t VPStaticDatabase::getNumTraceGMIO(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return 0 ;
  
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return 0 ;
  
      return xclbin->aie.gmioList.size() ;
    }
  
    AIECounter* VPStaticDatabase::getAIECounter(uint64_t deviceId, uint64_t idx)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return nullptr ;
  
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return nullptr ;
  
      return xclbin->aie.aieList[idx] ;
    }
  
    std::map<uint32_t, uint32_t>*
    VPStaticDatabase::getAIECoreCounterResources(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return nullptr ;
  
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return nullptr ;
  
      return &(xclbin->aie.aieCoreCountersMap) ;
    }
  
    std::map<uint32_t, uint32_t>*
    VPStaticDatabase::getAIEMemoryCounterResources(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return nullptr ;
  
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return nullptr ;
  
      return &(xclbin->aie.aieMemoryCountersMap) ;
    }
  
    std::map<uint32_t, uint32_t>*
    VPStaticDatabase::getAIEShimCounterResources(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return nullptr ;
  
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return nullptr ;
  
      return &(xclbin->aie.aieShimCountersMap) ;
    }
  
    std::map<uint32_t, uint32_t>*
    VPStaticDatabase::getAIECoreEventResources(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return nullptr ;
  
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return nullptr ;
  
      return &(xclbin->aie.aieCoreEventsMap) ;
    }
  
    std::map<uint32_t, uint32_t>*
    VPStaticDatabase::getAIEMemoryEventResources(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return nullptr ;
  
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return nullptr ;
  
      return &(xclbin->aie.aieMemoryEventsMap) ;
    }
  
    std::map<uint32_t, uint32_t>*
    VPStaticDatabase::getAIEShimEventResources(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return nullptr ;
  
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return nullptr ;
  
      return &(xclbin->aie.aieShimEventsMap) ;
    }
  
    std::vector<std::unique_ptr<aie_cfg_tile>>*
    VPStaticDatabase::getAIECfgTiles(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return nullptr ;
  
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return nullptr ;
  
      return &(xclbin->aie.aieCfgList) ;
    }
  
    TraceGMIO* VPStaticDatabase::getTraceGMIO(uint64_t deviceId, uint64_t idx)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return nullptr ;
  
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return nullptr ;
  
      if (xclbin->aie.gmioList.size() <= idx)
        return nullptr ;
      return xclbin->aie.gmioList[idx] ;
    }
  
    void VPStaticDatabase::addTraceGMIO(uint64_t deviceId, uint32_t i,
                                        uint16_t col, uint16_t num,
                                        uint16_t stream, uint16_t len)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return ;
      deviceInfo[deviceId]->addTraceGMIO(i, col, num, stream, len) ;
    }
  
    void VPStaticDatabase::addAIECounter(uint64_t deviceId, uint32_t i,
                                         uint16_t col, uint16_t r, uint8_t num,
                                         uint16_t start, uint16_t end,
                                         uint8_t reset, uint32_t load,
                                         double freq, const std::string& mod,
                                         const std::string& aieName)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return ;
      deviceInfo[deviceId]->addAIECounter(i, col, r, num, start, end, reset,
                                          load, freq, mod, aieName) ;
    }
  
    void VPStaticDatabase::addAIECounterResources(uint64_t deviceId,
                                                  uint32_t numCounters,
                                                  uint32_t numTiles,
                                                  bool isCore)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return ;
      deviceInfo[deviceId]->addAIECounterResources(numCounters, numTiles, isCore);
    }
  
    void VPStaticDatabase::addAIECoreEventResources(uint64_t deviceId,
                                                    uint32_t numEvents,
                                                    uint32_t numTiles)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return ;
      deviceInfo[deviceId]->addAIECoreEventResources(numEvents, numTiles) ;
    }
  
    void VPStaticDatabase::addAIEMemoryEventResources(uint64_t deviceId,
                                                      uint32_t numEvents,
                                                      uint32_t numTiles)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return ;
      deviceInfo[deviceId]->addAIEMemoryEventResources(numEvents, numTiles) ;
    }
  
    void VPStaticDatabase::addAIECfgTile(uint64_t deviceId, 
                                         std::unique_ptr<aie_cfg_tile>& tile)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return ;
      deviceInfo[deviceId]->addAIECfgTile(tile) ;
    }
  
    uint64_t VPStaticDatabase::getNumTracePLIO(uint64_t deviceId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return 0 ;
  
      XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
      if (!xclbin)
        return 0 ;
      return xclbin->aie.numTracePLIO ;
    }
  
    uint64_t VPStaticDatabase::getNumAIETraceStream(uint64_t deviceId)
    {
      uint64_t numAIETraceStream = getNumTracePLIO(deviceId) ;
      if (numAIETraceStream)
        return numAIETraceStream ;
      {
        // NumTracePLIO also locks the database, so put this lock in its own
        //  scope after numTracePLIO has returned.
        std::lock_guard<std::mutex> lock(deviceLock) ;
  
        if (deviceInfo.find(deviceId) == deviceInfo.end())
          return 0 ;
  
        XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
        if (!xclbin)
          return 0 ;
        return xclbin->aie.gmioList.size() ;
      }
    }
  
    void* VPStaticDatabase::getAieDevInst(std::function<void* (void*)> fetch,
                                          void* devHandle)
    {
      std::lock_guard<std::mutex> lock(aieLock) ;
      if (aieDevInst)
        return aieDevInst ;
  
      aieDevInst = fetch(devHandle) ;
      return aieDevInst ;
    }
  
    void* VPStaticDatabase::getAieDevice(std::function<void* (void*)> allocate,
                                         std::function<void (void*)> deallocate,
                                         void* devHandle)
    {
      std::lock_guard<std::mutex> lock(aieLock) ;
      if (aieDevice)
        return aieDevice;
      if (!aieDevInst)
        return nullptr ;
  
      deallocateAieDevice = deallocate ;
      aieDevice = allocate(devHandle) ;
      return aieDevice ;
    }
  
    // ************************************************************************
    // ***** Functions for information from a specific xclbin on a device *****
    uint64_t VPStaticDatabase::getNumAM(uint64_t deviceId, XclbinInfo* xclbin)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return 0 ;
      return deviceInfo[deviceId]->getNumAM(xclbin) ;
    }
  
    uint64_t VPStaticDatabase::getNumUserAMWithTrace(uint64_t deviceId,
                                                     XclbinInfo* xclbin)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return 0 ;
      return deviceInfo[deviceId]->getNumUserAMWithTrace(xclbin) ;
    }
  
    // Get the total number of AIMs in the design.  This includes shell monitors
    //  and all user space monitors.
    uint64_t VPStaticDatabase::getNumAIM(uint64_t deviceId, XclbinInfo* xclbin)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return 0 ;
      return deviceInfo[deviceId]->getNumAIM(xclbin) ;
    }
  
    // Get the number of AIMs in the user space, including monitors configured
    //  for counters only and counters + trace.  Exclude shell monitors.
    uint64_t
    VPStaticDatabase::getNumUserAIM(uint64_t deviceId, XclbinInfo* xclbin)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return 0 ;
      return deviceInfo[deviceId]->getNumUserAIM(xclbin) ;
    }
  
    // Get the number of AIMs only in the user space configured with trace.
    //  Exclude shell monitors, memory monitors, and any other monitors configured
    //  just with counters.
    uint64_t
    VPStaticDatabase::getNumUserAIMWithTrace(uint64_t deviceId, XclbinInfo* xclbin)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return 0 ;
      return deviceInfo[deviceId]->getNumUserAIMWithTrace(xclbin) ;
    }
  
    // Get the total number of ASMs in the design.  This includes shell monitors
    //  and all user space monitors.
    uint64_t VPStaticDatabase::getNumASM(uint64_t deviceId, XclbinInfo* xclbin)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return 0 ;
      return deviceInfo[deviceId]->getNumASM(xclbin) ;
    }
  
    // Get the number of ASMs in the user space, including monitors configured
    //  for counters only and counters + trace.  Exclude shell monitors.
    uint64_t
    VPStaticDatabase::getNumUserASM(uint64_t deviceId, XclbinInfo* xclbin)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return 0 ;
      return deviceInfo[deviceId]->getNumUserASM(xclbin) ;
    }
  
    // Get the number of ASMs only in the user space configured with trace.
    //  Exclude shell monitors and any other monitors configured
    //  just with counters.
    uint64_t
    VPStaticDatabase::getNumUserASMWithTrace(uint64_t deviceId, XclbinInfo* xclbin)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return 0 ;
      return deviceInfo[deviceId]->getNumUserASMWithTrace(xclbin) ;
    }
  
    uint64_t VPStaticDatabase::getNumNOC(uint64_t deviceId, XclbinInfo* xclbin)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return 0 ;
      return deviceInfo[deviceId]->getNumNOC(xclbin) ;
    }
  
    std::vector<Monitor*>*
    VPStaticDatabase::getAIMonitors(uint64_t deviceId, XclbinInfo* xclbin)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return nullptr ;
      return deviceInfo[deviceId]->getAIMonitors(xclbin) ;
    }
  
    std::vector<Monitor*>
    VPStaticDatabase::getUserAIMsWithTrace(uint64_t deviceId, XclbinInfo* xclbin)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end()) {
        std::vector<Monitor*> constructed ;
        return constructed ;
      }
      return deviceInfo[deviceId]->getUserAIMsWithTrace(xclbin) ;
    }
  
    std::vector<Monitor*>*
    VPStaticDatabase::getASMonitors(uint64_t deviceId, XclbinInfo* xclbin)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return nullptr ;
      return deviceInfo[deviceId]->getASMonitors(xclbin) ;
    }
  
    std::vector<Monitor*>
    VPStaticDatabase::getUserASMsWithTrace(uint64_t deviceId, XclbinInfo* xclbin)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end()) {
        std::vector<Monitor*> constructed ;
        return constructed ;
      }
      return deviceInfo[deviceId]->getUserASMsWithTrace(xclbin) ;
    }
  
    bool VPStaticDatabase::hasFloatingAIMWithTrace(uint64_t deviceId,
                                                   XclbinInfo* xclbin)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return false ;
      return deviceInfo[deviceId]->hasFloatingAIMWithTrace(xclbin) ;
    }
  
    bool VPStaticDatabase::hasFloatingASMWithTrace(uint64_t deviceId,
                                                   XclbinInfo* xclbin)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return false ;
      return deviceInfo[deviceId]->hasFloatingASMWithTrace(xclbin) ;
    }
  
    // ********************************************************************
    // ***** Functions for single monitors from an xclbin on a device *****
    Monitor*
    VPStaticDatabase::
    getAMonitor(uint64_t deviceId, XclbinInfo* xclbin, uint64_t slotId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return nullptr ;
      return deviceInfo[deviceId]->getAMonitor(xclbin, slotId) ;
    }
  
    Monitor*
    VPStaticDatabase::
    getAIMonitor(uint64_t deviceId, XclbinInfo* xclbin, uint64_t slotId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return nullptr ;
      return deviceInfo[deviceId]->getAIMonitor(xclbin, slotId) ;
    }
  
    Monitor*
    VPStaticDatabase::
    getASMonitor(uint64_t deviceId, XclbinInfo* xclbin, uint64_t slotId)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return nullptr ;
      return deviceInfo[deviceId]->getASMonitor(xclbin, slotId) ;
    }
  
    NoCNode*
    VPStaticDatabase::getNOC(uint64_t deviceId, XclbinInfo* xclbin, uint64_t idx)
    {
      std::lock_guard<std::mutex> lock(deviceLock) ;
  
      if (deviceInfo.find(deviceId) == deviceInfo.end())
        return nullptr ;
      return deviceInfo[deviceId]->getNOC(xclbin, idx) ;
    }
  
    // ************************************************************************
    // ***** Functions for  *****
  
  
    bool VPStaticDatabase::validXclbin(void* devHandle)
    {
      std::shared_ptr<xrt_core::device> device =
        xrt_core::get_userpf_device(devHandle);
  
      // If this xclbin was built with tools before the 2019.2 release, we
      //  do not support profiling
      std::pair<const char*, size_t> buildMetadata =
        device->get_axlf_section(BUILD_METADATA);
      const char* buildMetadataSection = buildMetadata.first;
      size_t      buildMetadataSz      = buildMetadata.second;
      if (buildMetadataSection != nullptr) {
        std::stringstream ss ;
        ss.write(buildMetadataSection, buildMetadataSz) ;
  
        boost::property_tree::ptree pt;
        boost::property_tree::read_json(ss, pt);
  
        std::string version =
          pt.get<std::string>("build_metadata.xclbin.generated_by.version", "") ;
  
        if (version == "" || version.find(".") == std::string::npos)
          return false ;
  
        // The stod function handles strings that have one decimal point
        //  or multiple decimal points and return only the major number
        //  and minor number and strips away the revision.
        double majorAndMinor = std::stod(version, nullptr) ;
        if (majorAndMinor < earliestSupportedToolVersion())
          return false ;
  
        return true ;
      }
  
      // If the build section does not exist, then this is not a valid xclbin
      return false ;
    }
  
  
  
  
  
  
  
    double VPStaticDatabase::findClockRate(std::shared_ptr<xrt_core::device> device)
    {
      double defaultClockSpeed = 300.0 ;
  
      // First, check the clock frequency topology
      const clock_freq_topology* clockSection =
        device->get_axlf_section<const clock_freq_topology*>(CLOCK_FREQ_TOPOLOGY);
  
      if(clockSection) {
        for(int32_t i = 0; i < clockSection->m_count; i++) {
          const struct clock_freq* clk = &(clockSection->m_clock_freq[i]);
          if(clk->m_type != CT_DATA) {
            continue;
          }
          return clk->m_freq_Mhz ;
        }
      }
  
      if (isEdge()) {
        // On Edge, we can try to get the "DATA_CLK" from the embedded metadata
        std::pair<const char*, size_t> metadataSection =
          device->get_axlf_section(EMBEDDED_METADATA) ;
        const char* rawXml = metadataSection.first ;
        size_t xmlSize = metadataSection.second ;
        if (rawXml == nullptr || xmlSize == 0)
          return defaultClockSpeed ;
  
        // Convert the raw character stream into a boost::property_tree
        std::string xmlFile ;
        xmlFile.assign(rawXml, xmlSize) ;
        std::stringstream xmlStream ;
        xmlStream << xmlFile ;
        boost::property_tree::ptree xmlProject ;
        boost::property_tree::read_xml(xmlStream, xmlProject) ;
  
        // Dig in and find all of the kernel clocks
        for (auto& clock : xmlProject.get_child("project.platform.device.core.kernelClocks")) {
          if (clock.first != "clock")
            continue ;
  
          try {
            std::string port = clock.second.get<std::string>("<xmlattr>.port") ;
            if (port != "DATA_CLK")
              continue ;
            std::string freq = clock.second.get<std::string>("<xmlattr>.frequency") ;
            std::string freqNumeral = freq.substr(0, freq.find('M')) ;
            double frequency = defaultClockSpeed ;
            std::stringstream convert ;
            convert << freqNumeral ;
            convert >> frequency ;
            return frequency ;
          }
          catch (std::exception& /*e*/) {
            continue ;
          }
        }
      }
  
      // We didn't find it in any section, so just assume 300 MHz for now
      return defaultClockSpeed ;
    }
  
    // This function is called whenever a device is loaded with an 
    //  xclbin.  It has to clear out any previous device information and
    //  reload our information.
    void VPStaticDatabase::updateDevice(uint64_t deviceId, void* devHandle)
    {
      std::shared_ptr<xrt_core::device> device = xrt_core::get_userpf_device(devHandle);
      if(nullptr == device) return;
  
      if(false == resetDeviceInfo(deviceId, device)) {
        /* If multiple plugins are enabled for the current run, the first plugin has already updated device information
         * in the static data base. So, no need to read the xclbin information again.
         */
        return;
      }
      
      // We need to update the device, but if we had an xclbin previously loaded
      //  then we need to mark it
      if (deviceInfo.find(deviceId) != deviceInfo.end()) {
        XclbinInfo* xclbin = deviceInfo[deviceId]->currentXclbin() ;
        if (xclbin)
          db->getDynamicInfo().markXclbinEnd(deviceId) ;
      }
  
      DeviceInfo* devInfo = nullptr ;
      auto itr = deviceInfo.find(deviceId);
      if (itr == deviceInfo.end()) {
        // This is the first time this device was loaded with an xclbin
        devInfo = new DeviceInfo();
        devInfo->deviceId = deviceId ;
        if (isEdge())
          devInfo->isEdgeDevice = true ;
        if (device->is_nodma())
          devInfo->isNoDMADevice = true ;
        deviceInfo[deviceId] = devInfo ;
  
      } else {
        // This is a previously used device being reloaded with a new xclbin
        devInfo = itr->second ;
        devInfo->cleanCurrentXclbinInfo() ;
      }
      
      XclbinInfo* currentXclbin = new XclbinInfo() ;
      currentXclbin->uuid = device->get_xclbin_uuid() ;
      currentXclbin->pl.clockRatePLMHz = findClockRate(device) ;
  
  
      /* Configure AMs if context monitoring is supported
       * else disable alll AMs on this device
       */
      devInfo->ctxInfo = xrt_core::config::get_kernel_channel_info();
  
      if (!setXclbinName(currentXclbin, device)) {
        // If there is no SYSTEM_METADATA section, use a default name
        currentXclbin->name = "default.xclbin";
      }
      if (!initializeComputeUnits(currentXclbin, device)) {
        delete currentXclbin;
        return;
      }
  
      devInfo->addXclbin(currentXclbin);
      initializeProfileMonitors(devInfo, device);
      devInfo->isReady = true;
    }
  
    // Return true if we should reset the device information.
    // Return false if we should not reset device information
    bool VPStaticDatabase::resetDeviceInfo(uint64_t deviceId, const std::shared_ptr<xrt_core::device>& device)
    {
      std::lock_guard<std::mutex> lock(deviceLock);
  
      auto itr = deviceInfo.find(deviceId);
      if(itr != deviceInfo.end()) {
        DeviceInfo *devInfo = itr->second;
        // Are we attempting to load the same xclbin multiple times?
        XclbinInfo* xclbin = devInfo->currentXclbin() ;
        if (xclbin && device->get_xclbin_uuid() == xclbin->uuid) {
          return false ;
        }
      }
      return true;
    }
  
    bool VPStaticDatabase::setXclbinName(XclbinInfo* currentXclbin, const std::shared_ptr<xrt_core::device>& device)
    {
      // Get SYSTEM_METADATA section
      std::pair<const char*, size_t> systemMetadata = device->get_axlf_section(SYSTEM_METADATA);
      const char* systemMetadataSection = systemMetadata.first;
      size_t      systemMetadataSz      = systemMetadata.second;
      if(systemMetadataSection == nullptr) return false;
  
      // For now, also update the System metadata for the run summary.
      //  TODO: Expand this so that multiple devices and multiple xclbins
      //  don't overwrite the single system diagram information
      std::ostringstream buf ;
      for (size_t index = 0 ; index < systemMetadataSz ; ++index)
      {
        buf << std::hex << std::setw(2) << std::setfill('0') << (unsigned int)(systemMetadataSection[index]);
      }
      {
        std::lock_guard<std::mutex> lock(summaryLock) ;
        systemDiagram = buf.str() ;
      }
  
      try {
        std::stringstream ss;
        ss.write(systemMetadataSection, systemMetadataSz);
  
        // Create a property tree and determine if the variables are all default values
        boost::property_tree::ptree pt;
        boost::property_tree::read_json(ss, pt);
  
        currentXclbin->name = pt.get<std::string>("system_diagram_metadata.xclbin.generated_by.xclbin_name", "");
        if(!currentXclbin->name.empty()) {
          currentXclbin->name += ".xclbin";
        }
      } catch(...) {
        // keep default value in "currentXclbin.name" i.e. empty string
      }
      return true;
    }
  
    bool VPStaticDatabase::initializeComputeUnits(XclbinInfo* currentXclbin, const std::shared_ptr<xrt_core::device>& device)
    {
      // Get IP_LAYOUT section 
      const ip_layout* ipLayoutSection = device->get_axlf_section<const ip_layout*>(IP_LAYOUT);
      if(ipLayoutSection == nullptr) return true;
  
      ComputeUnitInstance* cu = nullptr;
      for(int32_t i = 0; i < ipLayoutSection->m_count; i++) {
        const struct ip_data* ipData = &(ipLayoutSection->m_ip_data[i]);
        if(ipData->m_type != IP_KERNEL) {
          continue;
        }
        std::string cuName(reinterpret_cast<const char*>(ipData->m_name));
        if(std::string::npos != cuName.find(":dm_")) {
          /* Assumption : If the IP_KERNEL CU name is of the format "<kernel_name>:dm_*", then it is a 
           *              data mover and it should not be identified as a "CU" in profiling
           */
          continue;
        }
        cu = new ComputeUnitInstance(i, cuName);
        currentXclbin->pl.cus[i] = cu ;
        if((ipData->properties >> IP_CONTROL_SHIFT) & AP_CTRL_CHAIN) {
          cu->setDataflowEnabled(true);
        } else
        if((ipData->properties >> IP_CONTROL_SHIFT) & FAST_ADAPTER) {
          cu->setFaEnabled(true);
        }
      }
  
      // Get MEM_TOPOLOGY section 
      const mem_topology* memTopologySection = device->get_axlf_section<const mem_topology*>(MEM_TOPOLOGY);
      if(memTopologySection == nullptr) return false;
  
      for(int32_t i = 0; i < memTopologySection->m_count; i++) {
        const struct mem_data* memData = &(memTopologySection->m_mem_data[i]);
        currentXclbin->pl.memoryInfo[i] = new Memory(memData->m_type, i, memData->m_base_address, memData->m_size,
                                            reinterpret_cast<const char*>(memData->m_tag), memData->m_used);
      }
  
      // Look into the connectivity section and load information about Compute Units and their Memory connections
      // Get CONNECTIVITY section
      const connectivity* connectivitySection = device->get_axlf_section<const connectivity*>(CONNECTIVITY);    
      if(connectivitySection == nullptr) return true;
  
      // Now make the connections
      cu = nullptr;
      for(int32_t i = 0; i < connectivitySection->m_count; i++) {
        const struct connection* connctn = &(connectivitySection->m_connection[i]);
  
        if(currentXclbin->pl.cus.find(connctn->m_ip_layout_index) == currentXclbin->pl.cus.end()) {
          const struct ip_data* ipData = &(ipLayoutSection->m_ip_data[connctn->m_ip_layout_index]);
          if(ipData->m_type != IP_KERNEL) {
            // error ?
            continue;
          }
          std::string cuName(reinterpret_cast<const char*>(ipData->m_name));
          if(std::string::npos != cuName.find(":dm_")) {
            /* Assumption : If the IP_KERNEL CU name is of the format "<kernel_name>:dm_*", then it is a 
             *              data mover and it should not be identified as a "CU" in profiling
             */
            continue;
          }
          cu = new ComputeUnitInstance(connctn->m_ip_layout_index, cuName);
          currentXclbin->pl.cus[connctn->m_ip_layout_index] = cu;
          if((ipData->properties >> IP_CONTROL_SHIFT) & AP_CTRL_CHAIN) {
            cu->setDataflowEnabled(true);
          } else
          if((ipData->properties >> IP_CONTROL_SHIFT) & FAST_ADAPTER) {
            cu->setFaEnabled(true);
          }
        } else {
          cu = currentXclbin->pl.cus[connctn->m_ip_layout_index];
        }
  
        if(currentXclbin->pl.memoryInfo.find(connctn->mem_data_index) == currentXclbin->pl.memoryInfo.end()) {
          const struct mem_data* memData = &(memTopologySection->m_mem_data[connctn->mem_data_index]);
          currentXclbin->pl.memoryInfo[connctn->mem_data_index]
                   = new Memory(memData->m_type, connctn->mem_data_index,
                                memData->m_base_address, memData->m_size, reinterpret_cast<const char*>(memData->m_tag), memData->m_used);
        }
        cu->addConnection(connctn->arg_index, connctn->mem_data_index);
      }
  
      // Set Static WorkGroup Size of CUs using the EMBEDDED_METADATA section
      std::pair<const char*, size_t> embeddedMetadata = device->get_axlf_section(EMBEDDED_METADATA);
      const char* embeddedMetadataSection = embeddedMetadata.first;
      size_t      embeddedMetadataSz      = embeddedMetadata.second;
  
      boost::property_tree::ptree xmlProject;
      std::stringstream xmlStream;
      xmlStream.write(embeddedMetadataSection, embeddedMetadataSz);
      boost::property_tree::read_xml(xmlStream, xmlProject);
  
      for(auto coreItem : xmlProject.get_child("project.platform.device.core")) {
        std::string coreItemName = coreItem.first;
        if(0 != coreItemName.compare("kernel")) {  // skip items other than "kernel"
          continue;
        }
        auto kernel = coreItem;
        auto kernelNameItem    = kernel.second.get_child("<xmlattr>");
        std::string kernelName = kernelNameItem.get<std::string>("name", "");
  
        std::string x ;
        std::string y ;
        std::string z ;
  
        try {
          auto workGroupSz = kernel.second.get_child("compileWorkGroupSize");
          x = workGroupSz.get<std::string>("<xmlattr>.x", "");
          y = workGroupSz.get<std::string>("<xmlattr>.y", "");
          z = workGroupSz.get<std::string>("<xmlattr>.z", "");
        } catch (...) {
          // RTL kernels might not have this information, so if the fetch
          //  fails default to 1:1:1
          x = "1" ;
          y = "1" ;
          z = "1" ;
        }
  
        // Find the ComputeUnitInstance
        for(auto cuItr : currentXclbin->pl.cus) {
          if(0 != cuItr.second->getKernelName().compare(kernelName)) {
            continue;
          }
          cuItr.second->setDim(std::stoi(x), std::stoi(y), std::stoi(z));
        }
      }
  
      return true;
    }
  
    void VPStaticDatabase::initializeAM(DeviceInfo* devInfo,
                                        const std::string& name,
                                        const struct debug_ip_data* debugIpData)
    {
      XclbinInfo* xclbin = devInfo->currentXclbin() ;
      if (!xclbin) {
        xrt_core::message::send(xrt_core::message::severity_level::warning, "XRT",
                                "Attempt to initialize an AM without a loaded xclbin") ;
        return ;
      }
  
      uint64_t index = static_cast<uint64_t>(debugIpData->m_index_lowbyte) |
        (static_cast<uint64_t>(debugIpData->m_index_highbyte) << 8);
  
      // Find the compute unit that this AM is attached to.
      for (auto cu : xclbin->pl.cus) {
        ComputeUnitInstance* cuObj = cu.second ;
        int32_t cuId = cu.second->getIndex() ;
  
        if (0 == name.compare(cu.second->getName())) {
          // Set properties on this specific CU
          if(debugIpData->m_properties & XAM_STALL_PROPERTY_MASK) {
            cuObj->setStallEnabled(true);
          }
  
          Monitor* mon =
            new Monitor(static_cast<DEBUG_IP_TYPE>(debugIpData->m_type), index,
                        debugIpData->m_name, cuId) ;
  
          if (debugIpData->m_properties & XMON_TRACE_PROPERTY_MASK)
            mon->traceEnabled = true ;
  
          // Add the monitor to the list of all monitors in this xclbin
          xclbin->pl.ams.push_back(mon);
          // Associate it with this compute unit
          cuObj->setAccelMon(mon->slotIndex) ;
          break ;
        }
      }
    }
  
    void VPStaticDatabase::initializeAIM(DeviceInfo* devInfo,
                                         const std::string& name,
                                         const struct debug_ip_data* debugIpData)
    {
      XclbinInfo* xclbin = devInfo->currentXclbin() ;
      if (!xclbin) {
        xrt_core::message::send(xrt_core::message::severity_level::warning, "XRT",
                                "Attempt to initialize an AIM without a loaded xclbin") ;
        return ;
      }
  
      uint64_t index = static_cast<uint64_t>(debugIpData->m_index_lowbyte) |
        (static_cast<uint64_t>(debugIpData->m_index_highbyte) << 8);
      if (index < MIN_TRACE_ID_AIM) {
        std::stringstream msg;
        msg << "AIM with incorrect index: " << index ;
        xrt_core::message::send(xrt_core::message::severity_level::info, "XRT",
                                msg.str());
        index = MIN_TRACE_ID_AIM ;
      }
  
      // Parse name to find CU Name and Memory.  We expect the name in
      //  debug_ip_layout to be in the form of "cu_name/memory_name-port_name"
      size_t pos = name.find('/');
      std::string monCuName = name.substr(0, pos);
  
      if (monCuName == "memory_subsystem") {
        if (xclbin)
          xclbin->pl.hasMemoryAIM = true ;
      }
  
      std::string memName = "" ;
      std::string memName1 = "" ;
      std::string portName = "" ;
      size_t pos1 = name.find('-');
      if(pos1 != std::string::npos) {
        memName = name.substr(pos1+1);
        portName = name.substr(pos+1, pos1-pos-1);
        memName1 = convertMemoryName(memName);
      }
  
      ComputeUnitInstance* cuObj = nullptr ;
      int32_t cuId = -1 ;
      int32_t memId = -1;
  
      // Find both the compute unit this AIM is attached to (if applicable)
      //  and the memory this AIM is attached to (if applicable).
      for(auto cu : xclbin->pl.cus) {
        if(0 == monCuName.compare(cu.second->getName())) {
          cuId = cu.second->getIndex();
          cuObj = cu.second;
          break;
        }
      }
      for(auto mem : xclbin->pl.memoryInfo) {
        if (0 == memName1.compare(mem.second->name) || 0 == memName.compare(mem.second->name)) {
          memId = mem.second->index;
          break;
        }
      }
      Monitor* mon = new Monitor(static_cast<DEBUG_IP_TYPE>(debugIpData->m_type),
                                 index, debugIpData->m_name, cuId, memId);
      mon->port = portName;
      if (debugIpData->m_properties & XMON_TRACE_PROPERTY_MASK) {
        mon->traceEnabled = true ;
      }
  
      // Add the monitor to the list of all AIMs
      xclbin->pl.aims.push_back(mon) ;
  
      // Attach to a CU if appropriate
      if (cuObj) {
        cuObj->addAIM(mon->slotIndex, mon->traceEnabled) ;
      }
      else if(mon->traceEnabled) {
        // If not connected to CU and not a shell monitor, then a floating monitor
        // This floating monitor is enabled for trace too
        xclbin->pl.hasFloatingAIMWithTrace = true ;
      }
    }
  
    void VPStaticDatabase::initializeASM(DeviceInfo* devInfo,
                                         const std::string& name,
                                         const struct debug_ip_data* debugIpData)
    {
      XclbinInfo* xclbin = devInfo->currentXclbin() ;
      if (!xclbin) {
        xrt_core::message::send(xrt_core::message::severity_level::warning, "XRT",
                                "Attempt to initialize an ASM without a loaded xclbin") ;
        return ;
      }
  
      uint64_t index = static_cast<uint64_t>(debugIpData->m_index_lowbyte) |
        (static_cast<uint64_t>(debugIpData->m_index_highbyte) << 8);
      if (index < MIN_TRACE_ID_ASM) {
        std::stringstream msg;
        msg << "ASM with incorrect index: " << index ;
        xrt_core::message::send(xrt_core::message::severity_level::info, "XRT",
                                msg.str());
        index = MIN_TRACE_ID_ASM ;
      }
  
      // Parse out the name of the compute unit this monitor is attached to if
      //  possible.  We expect the name in debug_ip_layout to be in the form of
      //  compute_unit_name/port_name.  If this is a floating 
  
      size_t pos = name.find('/');
      std::string monCuName = name.substr(0, pos);
  
      std::string portName = "";
      ComputeUnitInstance* cuObj = nullptr ;
      int32_t cuId = -1 ;
  
      for(auto cu : xclbin->pl.cus) {
        if(0 == monCuName.compare(cu.second->getName())) {
          cuId = cu.second->getIndex();
          cuObj = cu.second;
          break;
        }
      }
      if(-1 != cuId) {
        size_t pos1 = name.find('-');
        if(std::string::npos != pos1) {
          portName = name.substr(pos+1, pos1-pos-1);
        }
      } else { /* (-1 == cuId) */
        pos = name.find("-");
        if(std::string::npos != pos) {
          pos = name.find_first_not_of(" ", pos+1);
          monCuName = name.substr(pos);
          pos = monCuName.find('/');
  
          size_t pos1 = monCuName.find('-');
          if(std::string::npos != pos1) {
            portName = monCuName.substr(pos+1, pos1-pos-1);
          }
  
          monCuName = monCuName.substr(0, pos);
  
          for(auto cu : xclbin->pl.cus) {
            if(0 == monCuName.compare(cu.second->getName())) {
              cuId = cu.second->getIndex();
              cuObj = cu.second;
              break;
            }
          }
        }
      }
  
      Monitor* mon = new Monitor(static_cast<DEBUG_IP_TYPE>(debugIpData->m_type),
                                 index, debugIpData->m_name,
                                 cuId);
      mon->port = portName;
      if (debugIpData->m_properties & 0x2) {
        mon->isStreamRead = true;
      }
      if (debugIpData->m_properties & XMON_TRACE_PROPERTY_MASK) {
        mon->traceEnabled = true ;
      }
  
      // Add this monitor to the list of all monitors
      xclbin->pl.asms.push_back(mon) ;
  
      // If the ASM is an User Space ASM i.e. either connected to a CU or floating but not shell ASM
      if (cuObj) {
        cuObj->addASM(mon->slotIndex, mon->traceEnabled) ;
      }
      else if (mon->traceEnabled) {
        // If not connected to CU and not a shell monitor, then a floating monitor
        // This floating monitor is enabled for trace too
        xclbin->pl.hasFloatingASMWithTrace = true ;
      }
    }
  
    void VPStaticDatabase::initializeNOC(DeviceInfo* devInfo,
                                         const struct debug_ip_data* debugIpData)
    {
      XclbinInfo* xclbin = devInfo->currentXclbin() ;
      if (!xclbin)
        return ;
  
      uint64_t index = static_cast<uint64_t>(debugIpData->m_index_lowbyte) |
        (static_cast<uint64_t>(debugIpData->m_index_highbyte) << 8);
      uint8_t readTrafficClass  = debugIpData->m_properties >> 2;
      uint8_t writeTrafficClass = debugIpData->m_properties & 0x3;
  
      NoCNode* noc = new NoCNode(index, debugIpData->m_name, readTrafficClass,
                                 writeTrafficClass) ;
      xclbin->aie.nocList.push_back(noc) ;
      // nocList in xdp::DeviceIntf is sorted; Is that required here?
    }
  
    void VPStaticDatabase::initializeTS2MM(DeviceInfo* devInfo,
                                           const struct debug_ip_data* debugIpData)
    {
      XclbinInfo* xclbin = devInfo->currentXclbin() ;
      if (!xclbin)
        return ;
  
      // TS2MM IP for either AIE PLIO or PL trace offload
      if (debugIpData->m_properties & 0x1)
        xclbin->aie.numTracePLIO++ ;
      else
        xclbin->pl.usesTs2mm = true ;
    }
  
    bool VPStaticDatabase::initializeProfileMonitors(DeviceInfo* devInfo, const std::shared_ptr<xrt_core::device>& device)
    {
      // Look into the debug_ip_layout section and load information about Profile Monitors
      // Get DEBUG_IP_LAYOUT section
      const debug_ip_layout* debugIpLayoutSection =
        device->get_axlf_section<const debug_ip_layout*>(DEBUG_IP_LAYOUT);
      if(debugIpLayoutSection == nullptr) return false;
  
      for(uint16_t i = 0; i < debugIpLayoutSection->m_count; i++) {
        const struct debug_ip_data* debugIpData =
          &(debugIpLayoutSection->m_debug_ip_data[i]);
        uint64_t index = static_cast<uint64_t>(debugIpData->m_index_lowbyte) |
          (static_cast<uint64_t>(debugIpData->m_index_highbyte) << 8);
  
        std::string name(debugIpData->m_name);
  
        std::stringstream msg;
        msg << "Initializing profile monitor " << i
            << ": name = " << name << ", index = " << index;
        xrt_core::message::send(xrt_core::message::severity_level::info, "XRT",
                                msg.str());
  
        switch (debugIpData->m_type) {
        case ACCEL_MONITOR:
          initializeAM(devInfo, name, debugIpData) ;
          break ;
        case AXI_MM_MONITOR:
          initializeAIM(devInfo, name, debugIpData) ;
          break ;
        case AXI_STREAM_MONITOR:
          initializeASM(devInfo, name, debugIpData) ;
          break ;
        case AXI_NOC:
          initializeNOC(devInfo, debugIpData) ;
          break ;
        case TRACE_S2MM:
          initializeTS2MM(devInfo, debugIpData) ;
          break ;
        default:
          break ;
        }
      }
  
      return true; 
    }
  
  
  
    void VPStaticDatabase::addCommandQueueAddress(uint64_t a)
    {
      std::lock_guard<std::mutex> lock(openCLLock) ;
  
      commandQueueAddresses.emplace(a) ;
    }
  
  
  
  } // end namespace xdp
  