//===-- MICmnLLDBDebugSessionInfo.cpp ---------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// Third party headers:
#include "lldb/API/SBThread.h"
#include <inttypes.h> // For PRIx64
#ifdef _WIN32
#include <io.h> // For the ::_access()
#else
#include <unistd.h> // For the ::access()
#endif              // _WIN32
#include "lldb/API/SBBreakpointLocation.h"

// In-house headers:
#include "MICmdData.h"
#include "MICmnLLDBDebugSessionInfo.h"
#include "MICmnLLDBDebugger.h"
#include "MICmnLLDBUtilSBValue.h"
#include "MICmnMIResultRecord.h"
#include "MICmnMIValueConst.h"
#include "MICmnMIValueList.h"
#include "MICmnMIValueTuple.h"
#include "MICmnResources.h"
#include "Platform.h"

//++
//------------------------------------------------------------------------------------
// Details: CMICmnLLDBDebugSessionInfo constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmnLLDBDebugSessionInfo::CMICmnLLDBDebugSessionInfo()
    : m_nBrkPointCntMax(INT32_MAX),
      m_currentSelectedThread(LLDB_INVALID_THREAD_ID),
      m_constStrSharedDataKeyWkDir("Working Directory"),
      m_constStrSharedDataSolibPath("Solib Path"),
      m_constStrPrintCharArrayAsString("Print CharArrayAsString"),
      m_constStrPrintExpandAggregates("Print ExpandAggregates"),
      m_constStrPrintAggregateFieldNames("Print AggregateFieldNames") {}

//++
//------------------------------------------------------------------------------------
// Details: CMICmnLLDBDebugSessionInfo destructor.
// Type:    Overridable.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmnLLDBDebugSessionInfo::~CMICmnLLDBDebugSessionInfo() { Shutdown(); }

//++
//------------------------------------------------------------------------------------
// Details: Initialize resources for *this object.
// Type:    Method.
// Args:    None.
// Return:  MIstatus::success - Functionality succeeded.
//          MIstatus::failure - Functionality failed.
// Throws:  None.
//--
bool CMICmnLLDBDebugSessionInfo::Initialize() {
  m_clientUsageRefCnt++;

  if (m_bInitialized)
    return MIstatus::success;

  m_currentSelectedThread = LLDB_INVALID_THREAD_ID;
  CMICmnLLDBDebugSessionInfoVarObj::VarObjIdResetToZero();

  m_bInitialized = MIstatus::success;

  return m_bInitialized;
}

//++
//------------------------------------------------------------------------------------
// Details: Release resources for *this object.
// Type:    Method.
// Args:    None.
// Return:  MIstatus::success - Functionality succeeded.
//          MIstatus::failure - Functionality failed.
// Throws:  None.
//--
bool CMICmnLLDBDebugSessionInfo::Shutdown() {
  if (--m_clientUsageRefCnt > 0)
    return MIstatus::success;

  if (!m_bInitialized)
    return MIstatus::success;

  // Tidy up
  SharedDataDestroy();

  m_vecActiveThreadId.clear();
  CMICmnLLDBDebugSessionInfoVarObj::VarObjClear();

  m_bInitialized = false;

  return MIstatus::success;
}

//++
//------------------------------------------------------------------------------------
// Details: Command instances can create and share data between other instances
// of commands.
//          Data can also be assigned by a command and retrieved by LLDB event
//          handler.
//          This function takes down those resources build up over the use of
//          the commands.
//          This function should be called when the creation and running of
//          command has
//          stopped i.e. application shutdown.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
void CMICmnLLDBDebugSessionInfo::SharedDataDestroy() {
  m_mapIdToSessionData.Clear();
  m_vecVarObj.clear();
  m_mapBrkPtIdToBrkPtInfo.clear();
}

//++
//------------------------------------------------------------------------------------
// Details: Record information about a LLDB break point so that is can be
// recalled in other
//          commands or LLDB event handling functions.
// Type:    Method.
// Args:    vBrkPtId        - (R) LLDB break point ID.
//          vrBrkPtInfo     - (R) Break point information object.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmnLLDBDebugSessionInfo::RecordBrkPtInfo(
    const MIuint vnBrkPtId, const SBrkPtInfo &vrBrkPtInfo) {
  MapPairBrkPtIdToBrkPtInfo_t pr(vnBrkPtId, vrBrkPtInfo);
  m_mapBrkPtIdToBrkPtInfo.insert(pr);

  return MIstatus::success;
}

//++
//------------------------------------------------------------------------------------
// Details: Retrieve information about a LLDB break point previous recorded
// either by
//          commands or LLDB event handling functions.
// Type:    Method.
// Args:    vBrkPtId        - (R) LLDB break point ID.
//          vrwBrkPtInfo    - (W) Break point information object.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmnLLDBDebugSessionInfo::RecordBrkPtInfoGet(
    const MIuint vnBrkPtId, SBrkPtInfo &vrwBrkPtInfo) const {
  const MapBrkPtIdToBrkPtInfo_t::const_iterator it =
      m_mapBrkPtIdToBrkPtInfo.find(vnBrkPtId);
  if (it != m_mapBrkPtIdToBrkPtInfo.end()) {
    vrwBrkPtInfo = (*it).second;
    return MIstatus::success;
  }

  return MIstatus::failure;
}

//++
//------------------------------------------------------------------------------------
// Details: Delete information about a specific LLDB break point object. This
// function
//          should be called when a LLDB break point is deleted.
// Type:    Method.
// Args:    vBrkPtId        - (R) LLDB break point ID.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmnLLDBDebugSessionInfo::RecordBrkPtInfoDelete(const MIuint vnBrkPtId) {
  const MapBrkPtIdToBrkPtInfo_t::const_iterator it =
      m_mapBrkPtIdToBrkPtInfo.find(vnBrkPtId);
  if (it != m_mapBrkPtIdToBrkPtInfo.end()) {
    m_mapBrkPtIdToBrkPtInfo.erase(it);
    return MIstatus::success;
  }

  return MIstatus::failure;
}

//++
//------------------------------------------------------------------------------------
// Details: Retrieve the specified thread's frame information.
// Type:    Method.
// Args:    vCmdData        - (R) A command's information.
//          vThreadIdx      - (R) Thread index.
//          vwrThreadFrames - (W) Frame data.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmnLLDBDebugSessionInfo::GetThreadFrames(
    const SMICmdData &vCmdData, const MIuint vThreadIdx,
    const FrameInfoFormat_e veFrameInfoFormat, CMIUtilString &vwrThreadFrames) {
  lldb::SBThread thread = GetProcess().GetThreadByIndexID(vThreadIdx);
  const uint32_t nFrames = thread.GetNumFrames();
  if (nFrames == 0) {
    // MI print "frame={}"
    CMICmnMIValueTuple miValueTuple;
    CMICmnMIValueResult miValueResult("frame", miValueTuple);
    vwrThreadFrames = miValueResult.GetString();
    return MIstatus::success;
  }

  // MI print
  // "frame={level=\"%d\",addr=\"0x%016" PRIx64
  // "\",func=\"%s\",args=[%s],file=\"%s\",fullname=\"%s\",line=\"%d\"},frame={level=\"%d\",addr=\"0x%016"
  // PRIx64 "\",func=\"%s\",args=[%s],file=\"%s\",fullname=\"%s\",line=\"%d\"},
  // ..."
  CMIUtilString strListCommaSeparated;
  for (MIuint nLevel = 0; nLevel < nFrames; nLevel++) {
    CMICmnMIValueTuple miValueTuple;
    if (!MIResponseFormFrameInfo(thread, nLevel, veFrameInfoFormat,
                                 miValueTuple))
      return MIstatus::failure;

    const CMICmnMIValueResult miValueResult2("frame", miValueTuple);
    if (nLevel != 0)
      strListCommaSeparated += ",";
    strListCommaSeparated += miValueResult2.GetString();
  }

  vwrThreadFrames = strListCommaSeparated;

  return MIstatus::success;
}

//++
//------------------------------------------------------------------------------------
// Details: Return the resolved file's path for the given file.
// Type:    Method.
// Args:    vCmdData        - (R) A command's information.
//          vPath           - (R) Original path.
//          vwrResolvedPath - (W) Resolved path.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmnLLDBDebugSessionInfo::ResolvePath(const SMICmdData &vCmdData,
                                             const CMIUtilString &vPath,
                                             CMIUtilString &vwrResolvedPath) {
  // ToDo: Verify this code as it does not work as vPath is always empty

  CMIUtilString strResolvedPath;
  if (!SharedDataRetrieve<CMIUtilString>(m_constStrSharedDataKeyWkDir,
                                         strResolvedPath)) {
    vwrResolvedPath = "";
    SetErrorDescription(CMIUtilString::Format(
        MIRSRC(IDS_CMD_ERR_SHARED_DATA_NOT_FOUND), vCmdData.strMiCmd.c_str(),
        m_constStrSharedDataKeyWkDir.c_str()));
    return MIstatus::failure;
  }

  vwrResolvedPath = vPath;

  return ResolvePath(strResolvedPath, vwrResolvedPath);
}

//++
//------------------------------------------------------------------------------------
// Details: Return the resolved file's path for the given file.
// Type:    Method.
// Args:    vstrUnknown     - (R)   String assigned to path when resolved path
// is empty.
//          vwrResolvedPath - (RW)  The original path overwritten with resolved
//          path.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmnLLDBDebugSessionInfo::ResolvePath(const CMIUtilString &vstrUnknown,
                                             CMIUtilString &vwrResolvedPath) {
  if (vwrResolvedPath.size() < 1) {
    vwrResolvedPath = vstrUnknown;
    return MIstatus::success;
  }

  bool bOk = MIstatus::success;

  CMIUtilString::VecString_t vecPathFolders;
  const MIuint nSplits = vwrResolvedPath.Split("/", vecPathFolders);
  MIunused(nSplits);
  MIuint nFoldersBack = 1; // 1 is just the file (last element of vector)
  while (bOk && (vecPathFolders.size() >= nFoldersBack)) {
    CMIUtilString strTestPath;
    MIuint nFoldersToAdd = nFoldersBack;
    while (nFoldersToAdd > 0) {
      strTestPath += "/";
      strTestPath += vecPathFolders[vecPathFolders.size() - nFoldersToAdd];
      nFoldersToAdd--;
    }
    bool bYesAccessible = false;
    bOk = AccessPath(strTestPath, bYesAccessible);
    if (bYesAccessible) {
      vwrResolvedPath = strTestPath;
      return MIstatus::success;
    } else
      nFoldersBack++;
  }

  // No files exist in the union of working directory and debuginfo path
  // Simply use the debuginfo path and let the IDE handle it.

  return bOk;
}

//++
//------------------------------------------------------------------------------------
// Details: Determine the given file path exists or not.
// Type:    Method.
// Args:    vPath               - (R) File name path.
//          vwbYesAccessible    - (W) True - file exists, false = does not
//          exist.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmnLLDBDebugSessionInfo::AccessPath(const CMIUtilString &vPath,
                                            bool &vwbYesAccessible) {
#ifdef _WIN32
  vwbYesAccessible = (::_access(vPath.c_str(), 0) == 0);
#else
  vwbYesAccessible = (::access(vPath.c_str(), 0) == 0);
#endif // _WIN32

  return MIstatus::success;
}

//++
//------------------------------------------------------------------------------------
// Details: Form MI partial response by appending more MI value type objects to
// the
//          tuple type object past in.
// Type:    Method.
// Args:    vCmdData        - (R) A command's information.
//          vrThread        - (R) LLDB thread object.
//          vwrMIValueTuple - (W) MI value tuple object.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmnLLDBDebugSessionInfo::MIResponseFormThreadInfo(
    const SMICmdData &vCmdData, const lldb::SBThread &vrThread,
    const ThreadInfoFormat_e veThreadInfoFormat,
    CMICmnMIValueTuple &vwrMIValueTuple) {
  lldb::SBThread &rThread = const_cast<lldb::SBThread &>(vrThread);

  const bool bSuspended = rThread.IsSuspended();
  const lldb::StopReason eReason = rThread.GetStopReason();
  const bool bValidReason = !((eReason == lldb::eStopReasonNone) ||
                              (eReason == lldb::eStopReasonInvalid));
  const CMIUtilString strState((bSuspended || bValidReason) ? "stopped"
                                                            : "running");

  // Add "id"
  const CMIUtilString strId(CMIUtilString::Format("%d", rThread.GetIndexID()));
  const CMICmnMIValueConst miValueConst1(strId);
  const CMICmnMIValueResult miValueResult1("id", miValueConst1);
  vwrMIValueTuple.Add(miValueResult1);

  // Add "target-id"
  const char *pThreadName = rThread.GetName();
  const MIuint len =
      (pThreadName != nullptr) ? CMIUtilString(pThreadName).length() : 0;
  const bool bHaveName = ((pThreadName != nullptr) && (len > 0) && (len < 32) &&
                          CMIUtilString::IsAllValidAlphaAndNumeric(
                              pThreadName)); // 32 is arbitrary number
  const char *pThrdFmt = bHaveName ? "%s" : "Thread %d";
  CMIUtilString strThread;
  if (bHaveName)
    strThread = CMIUtilString::Format(pThrdFmt, pThreadName);
  else
    strThread = CMIUtilString::Format(pThrdFmt, rThread.GetIndexID());
  const CMICmnMIValueConst miValueConst2(strThread);
  const CMICmnMIValueResult miValueResult2("target-id", miValueConst2);
  vwrMIValueTuple.Add(miValueResult2);

  // Add "frame"
  if (veThreadInfoFormat != eThreadInfoFormat_NoFrames) {
    CMIUtilString strFrames;
    if (!GetThreadFrames(vCmdData, rThread.GetIndexID(),
                         eFrameInfoFormat_AllArgumentsInSimpleForm, strFrames))
      return MIstatus::failure;

    const CMICmnMIValueConst miValueConst3(strFrames, true);
    vwrMIValueTuple.Add(miValueConst3, false);
  }

  // Add "state"
  const CMICmnMIValueConst miValueConst4(strState);
  const CMICmnMIValueResult miValueResult4("state", miValueConst4);
  vwrMIValueTuple.Add(miValueResult4);

  return MIstatus::success;
}

//++
//------------------------------------------------------------------------------------
// Details: Form MI partial response by appending more MI value type objects to
// the
//          tuple type object past in.
// Type:    Method.
// Args:    vrFrame         - (R)   LLDB thread object.
//          vMaskVarTypes   - (R)   Construed according to VariableType_e.
//          veVarInfoFormat - (R)   The type of variable info that should be
//          shown.
//          vwrMIValueList  - (W)   MI value list object.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmnLLDBDebugSessionInfo::MIResponseFormVariableInfo(
    const lldb::SBFrame &vrFrame, const MIuint vMaskVarTypes,
    const VariableInfoFormat_e veVarInfoFormat,
    CMICmnMIValueList &vwrMiValueList, const MIuint vnMaxDepth, /* = 10 */
    const bool vbMarkArgs /* = false*/) {
  bool bOk = MIstatus::success;
  lldb::SBFrame &rFrame = const_cast<lldb::SBFrame &>(vrFrame);

  const bool bArg = (vMaskVarTypes & eVariableType_Arguments);
  const bool bLocals = (vMaskVarTypes & eVariableType_Locals);
  const bool bStatics = (vMaskVarTypes & eVariableType_Statics);
  const bool bInScopeOnly = (vMaskVarTypes & eVariableType_InScope);

  // Handle arguments first
  lldb::SBValueList listArg = rFrame.GetVariables(bArg, false, false, false);
  bOk = bOk && MIResponseForVariableInfoInternal(veVarInfoFormat,
                                                 vwrMiValueList, listArg,
                                                 vnMaxDepth, true, vbMarkArgs);

  // Handle remaining variables
  lldb::SBValueList listVars =
      rFrame.GetVariables(false, bLocals, bStatics, bInScopeOnly);
  bOk = bOk && MIResponseForVariableInfoInternal(veVarInfoFormat,
                                                 vwrMiValueList, listVars,
                                                 vnMaxDepth, false, vbMarkArgs);

  return bOk;
}

bool CMICmnLLDBDebugSessionInfo::MIResponseForVariableInfoInternal(
    const VariableInfoFormat_e veVarInfoFormat,
    CMICmnMIValueList &vwrMiValueList, const lldb::SBValueList &vwrSBValueList,
    const MIuint vnMaxDepth, const bool vbIsArgs, const bool vbMarkArgs) {
  const MIuint nArgs = vwrSBValueList.GetSize();
  for (MIuint i = 0; i < nArgs; i++) {
    CMICmnMIValueTuple miValueTuple;
    lldb::SBValue value = vwrSBValueList.GetValueAtIndex(i);
    // If one stops inside try block with, which catch clause type is unnamed
    // (e.g std::exception&) then value name will be nullptr as well as value
    // pointer
    const char *name = value.GetName();
    if (name == nullptr)
      continue;
    const CMICmnMIValueConst miValueConst(name);
    const CMICmnMIValueResult miValueResultName("name", miValueConst);
    if (vbMarkArgs && vbIsArgs) {
      const CMICmnMIValueConst miValueConstArg("1");
      const CMICmnMIValueResult miValueResultArg("arg", miValueConstArg);
      miValueTuple.Add(miValueResultArg);
    }
    if (veVarInfoFormat != eVariableInfoFormat_NoValues) {
      miValueTuple.Add(miValueResultName); // name
      if (veVarInfoFormat == eVariableInfoFormat_SimpleValues) {
        const CMICmnMIValueConst miValueConst3(value.GetTypeName());
        const CMICmnMIValueResult miValueResult3("type", miValueConst3);
        miValueTuple.Add(miValueResult3);
      }
      const MIuint nChildren = value.GetNumChildren();
      const bool bIsPointerType = value.GetType().IsPointerType();
      if (nChildren == 0 ||                                 // no children
          (bIsPointerType && nChildren == 1) ||             // pointers
          veVarInfoFormat == eVariableInfoFormat_AllValues) // show all values
      {
        CMIUtilString strValue;
        if (GetVariableInfo(value, vnMaxDepth == 0, strValue)) {
          const CMICmnMIValueConst miValueConst2(
              strValue.Escape().AddSlashes());
          const CMICmnMIValueResult miValueResult2("value", miValueConst2);
          miValueTuple.Add(miValueResult2);
        }
      }
      vwrMiValueList.Add(miValueTuple);
      continue;
    }

    if (vbMarkArgs) {
      // If we are printing names only with vbMarkArgs, we still need to add the
      // name to the value tuple
      miValueTuple.Add(miValueResultName); // name
      vwrMiValueList.Add(miValueTuple);
    } else {
      // If we are printing name only then no need to put it in the tuple.
      vwrMiValueList.Add(miValueResultName);
    }
  }
  return MIstatus::success;
}

//++
//------------------------------------------------------------------------------------
// Details: Extract the value's name and value or recurse into child value
// object.
// Type:    Method.
// Args:    vrValue         - (R)  LLDB value object.
//          vbInSimpleForm  - (R)  True = Get variable info in simple form (i.e.
//          don't expand aggregates).
//                          -      False = Get variable info (and expand
//                          aggregates if any).
//          vwrStrValue  t  - (W)  The string representation of this value.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmnLLDBDebugSessionInfo::GetVariableInfo(const lldb::SBValue &vrValue,
                                                 const bool vbInSimpleForm,
                                                 CMIUtilString &vwrStrValue) {
  const CMICmnLLDBUtilSBValue utilValue(vrValue, true, false);
  const bool bExpandAggregates = vbInSimpleForm ? false : true;
  vwrStrValue = utilValue.GetValue(bExpandAggregates);
  return MIstatus::success;
}

//++
//------------------------------------------------------------------------------------
// Details: Form MI partial response by appending more MI value type objects to
// the
//          tuple type object past in.
// Type:    Method.
// Args:    vrThread        - (R) LLDB thread object.
//          vwrMIValueTuple - (W) MI value tuple object.
//          vArgInfo        - (R) Args information in MI response form.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmnLLDBDebugSessionInfo::MIResponseFormFrameInfo(
    const lldb::SBThread &vrThread, const MIuint vnLevel,
    const FrameInfoFormat_e veFrameInfoFormat,
    CMICmnMIValueTuple &vwrMiValueTuple) {
  lldb::SBThread &rThread = const_cast<lldb::SBThread &>(vrThread);

  lldb::SBFrame frame = rThread.GetFrameAtIndex(vnLevel);
  lldb::addr_t pc = 0;
  CMIUtilString fnName;
  CMIUtilString fileName;
  CMIUtilString path;
  MIuint nLine = 0;
  if (!GetFrameInfo(frame, pc, fnName, fileName, path, nLine))
    return MIstatus::failure;

  // MI print "{level=\"0\",addr=\"0x%016" PRIx64
  // "\",func=\"%s\",file=\"%s\",fullname=\"%s\",line=\"%d\"}"
  const CMIUtilString strLevel(CMIUtilString::Format("%d", vnLevel));
  const CMICmnMIValueConst miValueConst(strLevel);
  const CMICmnMIValueResult miValueResult("level", miValueConst);
  vwrMiValueTuple.Add(miValueResult);
  const CMIUtilString strAddr(CMIUtilString::Format("0x%016" PRIx64, pc));
  const CMICmnMIValueConst miValueConst2(strAddr);
  const CMICmnMIValueResult miValueResult2("addr", miValueConst2);
  vwrMiValueTuple.Add(miValueResult2);
  const CMICmnMIValueConst miValueConst3(fnName);
  const CMICmnMIValueResult miValueResult3("func", miValueConst3);
  vwrMiValueTuple.Add(miValueResult3);
  if (veFrameInfoFormat != eFrameInfoFormat_NoArguments) {
    CMICmnMIValueList miValueList(true);
    const MIuint maskVarTypes = eVariableType_Arguments;
    if (veFrameInfoFormat == eFrameInfoFormat_AllArgumentsInSimpleForm) {
      if (!MIResponseFormVariableInfo(frame, maskVarTypes,
                                      eVariableInfoFormat_AllValues,
                                      miValueList, 0))
        return MIstatus::failure;
    } else if (!MIResponseFormVariableInfo(frame, maskVarTypes,
                                           eVariableInfoFormat_AllValues,
                                           miValueList))
      return MIstatus::failure;

    const CMICmnMIValueResult miValueResult4("args", miValueList);
    vwrMiValueTuple.Add(miValueResult4);
  }
  const CMICmnMIValueConst miValueConst5(fileName);
  const CMICmnMIValueResult miValueResult5("file", miValueConst5);
  vwrMiValueTuple.Add(miValueResult5);
  const CMICmnMIValueConst miValueConst6(path);
  const CMICmnMIValueResult miValueResult6("fullname", miValueConst6);
  vwrMiValueTuple.Add(miValueResult6);
  const CMIUtilString strLine(CMIUtilString::Format("%d", nLine));
  const CMICmnMIValueConst miValueConst7(strLine);
  const CMICmnMIValueResult miValueResult7("line", miValueConst7);
  vwrMiValueTuple.Add(miValueResult7);

  return MIstatus::success;
}

//++
//------------------------------------------------------------------------------------
// Details: Retrieve the frame information from LLDB frame object.
// Type:    Method.
// Args:    vrFrame         - (R) LLDB thread object.
//          vPc             - (W) Address number.
//          vFnName         - (W) Function name.
//          vFileName       - (W) File name text.
//          vPath           - (W) Full file name and path text.
//          vnLine          - (W) File line number.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmnLLDBDebugSessionInfo::GetFrameInfo(
    const lldb::SBFrame &vrFrame, lldb::addr_t &vwPc, CMIUtilString &vwFnName,
    CMIUtilString &vwFileName, CMIUtilString &vwPath, MIuint &vwnLine) {
  lldb::SBFrame &rFrame = const_cast<lldb::SBFrame &>(vrFrame);

  static char pBuffer[PATH_MAX];
  const MIuint nBytes =
      rFrame.GetLineEntry().GetFileSpec().GetPath(&pBuffer[0], sizeof(pBuffer));
  MIunused(nBytes);
  CMIUtilString strResolvedPath(&pBuffer[0]);
  const char *pUnkwn = "??";
  if (!ResolvePath(pUnkwn, strResolvedPath))
    return MIstatus::failure;
  vwPath = strResolvedPath;

  vwPc = rFrame.GetPC();

  const char *pFnName = rFrame.GetFunctionName();
  vwFnName = (pFnName != nullptr) ? pFnName : pUnkwn;

  const char *pFileName = rFrame.GetLineEntry().GetFileSpec().GetFilename();
  vwFileName = (pFileName != nullptr) ? pFileName : pUnkwn;

  vwnLine = rFrame.GetLineEntry().GetLine();

  return MIstatus::success;
}

//++
//------------------------------------------------------------------------------------
// Details: Form MI partial response by appending more MI value type objects to
// the
//          tuple type object past in.
// Type:    Method.
// Args:    vrBrkPtInfo     - (R) Break point information object.
//          vwrMIValueTuple - (W) MI value tuple object.
// Return:  None.
// Throws:  None.
//--
void CMICmnLLDBDebugSessionInfo::MIResponseFormBrkPtFrameInfo(
    const SBrkPtInfo &vrBrkPtInfo, CMICmnMIValueTuple &vwrMiValueTuple) {
  const CMIUtilString strAddr(
      CMIUtilString::Format("0x%016" PRIx64, vrBrkPtInfo.m_pc));
  const CMICmnMIValueConst miValueConst2(strAddr);
  const CMICmnMIValueResult miValueResult2("addr", miValueConst2);
  vwrMiValueTuple.Add(miValueResult2);
  const CMICmnMIValueConst miValueConst3(vrBrkPtInfo.m_fnName);
  const CMICmnMIValueResult miValueResult3("func", miValueConst3);
  vwrMiValueTuple.Add(miValueResult3);
  const CMICmnMIValueConst miValueConst5(vrBrkPtInfo.m_fileName);
  const CMICmnMIValueResult miValueResult5("file", miValueConst5);
  vwrMiValueTuple.Add(miValueResult5);
  const CMIUtilString strN5 = CMIUtilString::Format(
      "%s/%s", vrBrkPtInfo.m_path.c_str(), vrBrkPtInfo.m_fileName.c_str());
  const CMICmnMIValueConst miValueConst6(strN5);
  const CMICmnMIValueResult miValueResult6("fullname", miValueConst6);
  vwrMiValueTuple.Add(miValueResult6);
  const CMIUtilString strLine(CMIUtilString::Format("%d", vrBrkPtInfo.m_nLine));
  const CMICmnMIValueConst miValueConst7(strLine);
  const CMICmnMIValueResult miValueResult7("line", miValueConst7);
  vwrMiValueTuple.Add(miValueResult7);
}

//++
//------------------------------------------------------------------------------------
// Details: Form MI partial response by appending more MI value type objects to
// the
//          tuple type object past in.
// Type:    Method.
// Args:    vrBrkPtInfo     - (R) Break point information object.
//          vwrMIValueTuple - (W) MI value tuple object.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmnLLDBDebugSessionInfo::MIResponseFormBrkPtInfo(
    const SBrkPtInfo &vrBrkPtInfo, CMICmnMIValueTuple &vwrMiValueTuple) {
  // MI print
  // "=breakpoint-modified,bkpt={number=\"%d\",type=\"breakpoint\",disp=\"%s\",enabled=\"%c\",addr=\"0x%016"
  // PRIx64 "\",
  // func=\"%s\",file=\"%s\",fullname=\"%s/%s\",line=\"%d\",times=\"%d\",original-location=\"%s\"}"

  // "number="
  const CMICmnMIValueConst miValueConst(
      CMIUtilString::Format("%d", vrBrkPtInfo.m_id));
  const CMICmnMIValueResult miValueResult("number", miValueConst);
  CMICmnMIValueTuple miValueTuple(miValueResult);
  // "type="
  const CMICmnMIValueConst miValueConst2(vrBrkPtInfo.m_strType);
  const CMICmnMIValueResult miValueResult2("type", miValueConst2);
  miValueTuple.Add(miValueResult2);
  // "disp="
  const CMICmnMIValueConst miValueConst3(vrBrkPtInfo.m_bDisp ? "del" : "keep");
  const CMICmnMIValueResult miValueResult3("disp", miValueConst3);
  miValueTuple.Add(miValueResult3);
  // "enabled="
  const CMICmnMIValueConst miValueConst4(vrBrkPtInfo.m_bEnabled ? "y" : "n");
  const CMICmnMIValueResult miValueResult4("enabled", miValueConst4);
  miValueTuple.Add(miValueResult4);
  // "addr="
  // "func="
  // "file="
  // "fullname="
  // "line="
  MIResponseFormBrkPtFrameInfo(vrBrkPtInfo, miValueTuple);
  // "pending="
  if (vrBrkPtInfo.m_bPending) {
    const CMICmnMIValueConst miValueConst(vrBrkPtInfo.m_strOrigLoc);
    const CMICmnMIValueList miValueList(miValueConst);
    const CMICmnMIValueResult miValueResult("pending", miValueList);
    miValueTuple.Add(miValueResult);
  }
  if (vrBrkPtInfo.m_bHaveArgOptionThreadGrp) {
    const CMICmnMIValueConst miValueConst(vrBrkPtInfo.m_strOptThrdGrp);
    const CMICmnMIValueList miValueList(miValueConst);
    const CMICmnMIValueResult miValueResult("thread-groups", miValueList);
    miValueTuple.Add(miValueResult);
  }
  // "times="
  const CMICmnMIValueConst miValueConstB(
      CMIUtilString::Format("%d", vrBrkPtInfo.m_nTimes));
  const CMICmnMIValueResult miValueResultB("times", miValueConstB);
  miValueTuple.Add(miValueResultB);
  // "thread="
  if (vrBrkPtInfo.m_bBrkPtThreadId) {
    const CMICmnMIValueConst miValueConst(
        CMIUtilString::Format("%d", vrBrkPtInfo.m_nBrkPtThreadId));
    const CMICmnMIValueResult miValueResult("thread", miValueConst);
    miValueTuple.Add(miValueResult);
  }
  // "cond="
  if (vrBrkPtInfo.m_bCondition) {
    const CMICmnMIValueConst miValueConst(vrBrkPtInfo.m_strCondition);
    const CMICmnMIValueResult miValueResult("cond", miValueConst);
    miValueTuple.Add(miValueResult);
  }
  // "ignore="
  if (vrBrkPtInfo.m_nIgnore != 0) {
    const CMICmnMIValueConst miValueConst(
        CMIUtilString::Format("%d", vrBrkPtInfo.m_nIgnore));
    const CMICmnMIValueResult miValueResult("ignore", miValueConst);
    miValueTuple.Add(miValueResult);
  }
  // "original-location="
  const CMICmnMIValueConst miValueConstC(vrBrkPtInfo.m_strOrigLoc);
  const CMICmnMIValueResult miValueResultC("original-location", miValueConstC);
  miValueTuple.Add(miValueResultC);

  vwrMiValueTuple = miValueTuple;

  return MIstatus::success;
}

//++
//------------------------------------------------------------------------------------
// Details: Retrieve breakpoint information and write into the given breakpoint
// information
//          object. Note not all possible information is retrieved and so the
//          information
//          object may need to be filled in with more information after calling
//          this
//          function. Mainly breakpoint location information of information that
//          is
//          unlikely to change.
// Type:    Method.
// Args:    vBrkPt      - (R) LLDB break point object.
//          vrBrkPtInfo - (W) Break point information object.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmnLLDBDebugSessionInfo::GetBrkPtInfo(const lldb::SBBreakpoint &vBrkPt,
                                              SBrkPtInfo &vrwBrkPtInfo) const {
  lldb::SBBreakpoint &rBrkPt = const_cast<lldb::SBBreakpoint &>(vBrkPt);
  lldb::SBBreakpointLocation brkPtLoc = rBrkPt.GetLocationAtIndex(0);
  lldb::SBAddress brkPtAddr = brkPtLoc.GetAddress();
  lldb::SBSymbolContext symbolCntxt =
      brkPtAddr.GetSymbolContext(lldb::eSymbolContextEverything);
  const char *pUnkwn = "??";
  lldb::SBModule rModule = symbolCntxt.GetModule();
  const char *pModule =
      rModule.IsValid() ? rModule.GetFileSpec().GetFilename() : pUnkwn;
  MIunused(pModule);
  const char *pFile = pUnkwn;
  const char *pFn = pUnkwn;
  const char *pFilePath = pUnkwn;
  size_t nLine = 0;
  lldb::addr_t nAddr = brkPtAddr.GetLoadAddress(GetTarget());
  if (nAddr == LLDB_INVALID_ADDRESS)
    nAddr = brkPtAddr.GetFileAddress();

  lldb::SBCompileUnit rCmplUnit = symbolCntxt.GetCompileUnit();
  if (rCmplUnit.IsValid()) {
    lldb::SBFileSpec rFileSpec = rCmplUnit.GetFileSpec();
    pFile = rFileSpec.GetFilename();
    pFilePath = rFileSpec.GetDirectory();
    lldb::SBFunction rFn = symbolCntxt.GetFunction();
    if (rFn.IsValid())
      pFn = rFn.GetName();
    lldb::SBLineEntry rLnEntry = symbolCntxt.GetLineEntry();
    if (rLnEntry.GetLine() > 0)
      nLine = rLnEntry.GetLine();
  }

  vrwBrkPtInfo.m_id = vBrkPt.GetID();
  vrwBrkPtInfo.m_strType = "breakpoint";
  vrwBrkPtInfo.m_pc = nAddr;
  vrwBrkPtInfo.m_fnName = pFn;
  vrwBrkPtInfo.m_fileName = pFile;
  vrwBrkPtInfo.m_path = pFilePath;
  vrwBrkPtInfo.m_nLine = nLine;
  vrwBrkPtInfo.m_nTimes = vBrkPt.GetHitCount();

  return MIstatus::success;
}

//++
//------------------------------------------------------------------------------------
// Details: Get current debugger.
// Type:    Method.
// Args:    None.
// Return:  lldb::SBDebugger   - current debugger.
// Throws:  None.
//--
lldb::SBDebugger &CMICmnLLDBDebugSessionInfo::GetDebugger() const {
  return CMICmnLLDBDebugger::Instance().GetTheDebugger();
}

//++
//------------------------------------------------------------------------------------
// Details: Get current listener.
// Type:    Method.
// Args:    None.
// Return:  lldb::SBListener   - current listener.
// Throws:  None.
//--
lldb::SBListener &CMICmnLLDBDebugSessionInfo::GetListener() const {
  return CMICmnLLDBDebugger::Instance().GetTheListener();
}

//++
//------------------------------------------------------------------------------------
// Details: Get current target.
// Type:    Method.
// Args:    None.
// Return:  lldb::SBTarget   - current target.
// Throws:  None.
//--
lldb::SBTarget CMICmnLLDBDebugSessionInfo::GetTarget() const {
  return GetDebugger().GetSelectedTarget();
}

//++
//------------------------------------------------------------------------------------
// Details: Get current process.
// Type:    Method.
// Args:    None.
// Return:  lldb::SBProcess   - current process.
// Throws:  None.
//--
lldb::SBProcess CMICmnLLDBDebugSessionInfo::GetProcess() const {
  return GetTarget().GetProcess();
}
