//===-- MICmdCmdExec.cpp ----------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// Overview:    CMICmdCmdExecRun                implementation.
//              CMICmdCmdExecContinue           implementation.
//              CMICmdCmdExecNext               implementation.
//              CMICmdCmdExecStep               implementation.
//              CMICmdCmdExecNextInstruction    implementation.
//              CMICmdCmdExecStepInstruction    implementation.
//              CMICmdCmdExecFinish             implementation.
//              CMICmdCmdExecInterrupt          implementation.
//              CMICmdCmdExecArguments          implementation.
//              CMICmdCmdExecAbort              implementation.

// Third Party Headers:
#include "lldb/API/SBCommandInterpreter.h"
#include "lldb/API/SBProcess.h"
#include "lldb/API/SBStream.h"
#include "lldb/API/SBThread.h"
#include "lldb/lldb-enumerations.h"

// In-house headers:
#include "MICmdArgValListOfN.h"
#include "MICmdArgValNumber.h"
#include "MICmdArgValOptionLong.h"
#include "MICmdArgValOptionShort.h"
#include "MICmdArgValString.h"
#include "MICmdArgValThreadGrp.h"
#include "MICmdCmdExec.h"
#include "MICmnLLDBDebugSessionInfo.h"
#include "MICmnLLDBDebugger.h"
#include "MICmnMIOutOfBandRecord.h"
#include "MICmnMIResultRecord.h"
#include "MICmnMIValueConst.h"
#include "MICmnStreamStdout.h"
#include "MIDriver.h"

//++
// Details: CMICmdCmdExecRun constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdExecRun::CMICmdCmdExecRun() : m_constStrArgStart("start") {
  // Command factory matches this name with that received from the stdin stream
  m_strMiCmd = "exec-run";

  // Required by the CMICmdFactory when registering *this command
  m_pSelfCreatorFn = &CMICmdCmdExecRun::CreateSelf;
}

//++
// Details: CMICmdCmdExecRun destructor.
// Type:    Overrideable.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdExecRun::~CMICmdCmdExecRun() {}

//++
// Details: The invoker requires this function. It parses the command line
// options'
//          arguments to extract values for each of those arguments.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdExecRun::ParseArgs() {
  m_setCmdArgs.Add(new CMICmdArgValOptionLong(
      m_constStrArgStart, false, true,
      CMICmdArgValListBase::eArgValType_OptionLong, 0));
  return ParseValidateCmdOptions();
}

//++
// Details: The invoker requires this function. The command does work in this
// function.
//          The command is likely to communicate with the LLDB SBDebugger in
//          here.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdExecRun::Execute() {
  CMICmnLLDBDebugSessionInfo &rSessionInfo(
      CMICmnLLDBDebugSessionInfo::Instance());

  {
    // Check we have a valid target.
    // Note: target created via 'file-exec-and-symbols' command.
    lldb::SBTarget sbTarget = rSessionInfo.GetTarget();
    if (!sbTarget.IsValid() ||
        sbTarget == rSessionInfo.GetDebugger().GetDummyTarget()) {
      SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_TARGET_CURRENT),
                                     m_cmdData.strMiCmd.c_str()));
      return MIstatus::failure;
    }
  }

  lldb::SBError error;
  lldb::SBStream errMsg;
  lldb::SBLaunchInfo launchInfo = rSessionInfo.GetTarget().GetLaunchInfo();
  launchInfo.SetListener(rSessionInfo.GetListener());

  // Run to first instruction or main() requested?
  CMICMDBASE_GETOPTION(pArgStart, OptionLong, m_constStrArgStart);
  if (pArgStart->GetFound()) {
    launchInfo.SetLaunchFlags(launchInfo.GetLaunchFlags() |
                              lldb::eLaunchFlagStopAtEntry);
  }

  lldb::SBProcess process = rSessionInfo.GetTarget().Launch(launchInfo, error);
  if (!process.IsValid()) {
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_PROCESS),
                                   m_cmdData.strMiCmd.c_str(),
                                   errMsg.GetData()));
    return MIstatus::failure;
  }

  const auto successHandler = [this] {
    if (!CMIDriver::Instance().SetDriverStateRunningDebugging()) {
      const CMIUtilString &rErrMsg(CMIDriver::Instance().GetErrorDescription());
      SetError(CMIUtilString::Format(
          MIRSRC(IDS_CMD_ERR_SET_NEW_DRIVER_STATE),
          m_cmdData.strMiCmd.c_str(), rErrMsg.c_str()));
      return MIstatus::failure;
    }
    return MIstatus::success;
  };

  return HandleSBErrorWithSuccess(error, successHandler);
}

//++
// Details: The invoker requires this function. The command prepares a MI Record
// Result
//          for the work carried out in the Execute().
//          Called only if Execute() set status as successful on completion.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdExecRun::Acknowledge() {
  m_miResultRecord = CMICmnMIResultRecord(
      m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Running);

  CMICmnLLDBDebugSessionInfo &rSessionInfo(
      CMICmnLLDBDebugSessionInfo::Instance());
  lldb::pid_t pid = rSessionInfo.GetProcess().GetProcessID();
  // Give the client '=thread-group-started,id="i1" pid="xyz"'
  m_bHasResultRecordExtra = true;
  const CMICmnMIValueConst miValueConst2("i1");
  const CMICmnMIValueResult miValueResult2("id", miValueConst2);
  const CMIUtilString strPid(CMIUtilString::Format("%lld", pid));
  const CMICmnMIValueConst miValueConst(strPid);
  const CMICmnMIValueResult miValueResult("pid", miValueConst);
  CMICmnMIOutOfBandRecord miOutOfBand(
      CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupStarted, miValueResult2);
  miOutOfBand.Add(miValueResult);
  m_miResultRecordExtra = miOutOfBand.GetString();

  return MIstatus::success;
}

//++
// Details: Required by the CMICmdFactory when registering *this command. The
// factory
//          calls this function to create an instance of *this command.
// Type:    Static method.
// Args:    None.
// Return:  CMICmdBase * - Pointer to a new command.
// Throws:  None.
//--
CMICmdBase *CMICmdCmdExecRun::CreateSelf() { return new CMICmdCmdExecRun(); }


//++
// Details: CMICmdCmdExecContinue constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdExecContinue::CMICmdCmdExecContinue() {
  // Command factory matches this name with that received from the stdin stream
  m_strMiCmd = "exec-continue";

  // Required by the CMICmdFactory when registering *this command
  m_pSelfCreatorFn = &CMICmdCmdExecContinue::CreateSelf;
}

//++
// Details: CMICmdCmdExecContinue destructor.
// Type:    Overrideable.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdExecContinue::~CMICmdCmdExecContinue() {}

//++
// Details: The invoker requires this function. The command does work in this
// function.
//          The command is likely to communicate with the LLDB SBDebugger in
//          here.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdExecContinue::Execute() {
  const auto successHandler = [this] {
    // CODETAG_DEBUG_SESSION_RUNNING_PROG_RECEIVED_SIGINT_PAUSE_PROGRAM
    if (!CMIDriver::Instance().SetDriverStateRunningDebugging()) {
      const CMIUtilString &rErrMsg(CMIDriver::Instance().GetErrorDescription());
      SetError(CMIUtilString::Format(
          MIRSRC(IDS_CMD_ERR_SET_NEW_DRIVER_STATE),
          m_cmdData.strMiCmd.c_str(), rErrMsg.c_str()));
      return MIstatus::failure;
    }
    return MIstatus::success;
  };

  return HandleSBErrorWithSuccess(
      CMICmnLLDBDebugSessionInfo::Instance().GetProcess().Continue(),
      successHandler);
}

//++
// Details: The invoker requires this function. The command prepares a MI Record
// Result
//          for the work carried out in the Execute().
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdExecContinue::Acknowledge() {
  m_miResultRecord = CMICmnMIResultRecord(
      m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Running);
  return MIstatus::success;
}

//++
// Details: Required by the CMICmdFactory when registering *this command. The
// factory
//          calls this function to create an instance of *this command.
// Type:    Static method.
// Args:    None.
// Return:  CMICmdBase * - Pointer to a new command.
// Throws:  None.
//--
CMICmdBase *CMICmdCmdExecContinue::CreateSelf() {
  return new CMICmdCmdExecContinue();
}


//++
// Details: CMICmdCmdExecNext constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdExecNext::CMICmdCmdExecNext() : m_constStrArgNumber("number") {
  // Command factory matches this name with that received from the stdin stream
  m_strMiCmd = "exec-next";

  // Required by the CMICmdFactory when registering *this command
  m_pSelfCreatorFn = &CMICmdCmdExecNext::CreateSelf;
}

//++
// Details: CMICmdCmdExecNext destructor.
// Type:    Overrideable.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdExecNext::~CMICmdCmdExecNext() {}

//++
// Details: The invoker requires this function. The parses the command line
// options
//          arguments to extract values for each of those arguments.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdExecNext::ParseArgs() {
  m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgNumber, false, false));
  return ParseValidateCmdOptions();
}

//++
// Details: The invoker requires this function. The command does work in this
// function.
//          The command is likely to communicate with the LLDB SBDebugger in
//          here.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdExecNext::Execute() {
  CMICMDBASE_GETOPTION(pArgThread, OptionLong, m_constStrArgThread);

  // Retrieve the --thread option's thread ID (only 1)
  MIuint64 nThreadId = UINT64_MAX;
  if (pArgThread->GetFound() &&
      !pArgThread->GetExpectedOption<CMICmdArgValNumber, MIuint64>(nThreadId)) {
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_THREAD_INVALID),
                                   m_cmdData.strMiCmd.c_str(),
                                   m_constStrArgThread.c_str()));
    return MIstatus::failure;
  }

  CMICmnLLDBDebugSessionInfo &rSessionInfo(
      CMICmnLLDBDebugSessionInfo::Instance());

  lldb::SBError error;
  if (nThreadId != UINT64_MAX) {
    lldb::SBThread sbThread = rSessionInfo.GetProcess().GetThreadByIndexID(nThreadId);
    if (!sbThread.IsValid()) {
      SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_THREAD_INVALID),
                                     m_cmdData.strMiCmd.c_str(),
                                     m_constStrArgThread.c_str()));
      return MIstatus::failure;
    }
    sbThread.StepOver(lldb::eOnlyDuringStepping, error);
  } else
    rSessionInfo.GetProcess().GetSelectedThread().StepOver(
        lldb::eOnlyDuringStepping, error);

  return HandleSBError(error);
}

//++
// Details: The invoker requires this function. The command prepares a MI Record
// Result
//          for the work carried out in the Execute().
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdExecNext::Acknowledge() {
  m_miResultRecord = CMICmnMIResultRecord(
      m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Running);
  return MIstatus::success;
}

//++
// Details: Required by the CMICmdFactory when registering *this command. The
// factory
//          calls this function to create an instance of *this command.
// Type:    Static method.
// Args:    None.
// Return:  CMICmdBase * - Pointer to a new command.
// Throws:  None.
//--
CMICmdBase *CMICmdCmdExecNext::CreateSelf() { return new CMICmdCmdExecNext(); }


//++
// Details: CMICmdCmdExecStep constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdExecStep::CMICmdCmdExecStep() : m_constStrArgNumber("number") {
  // Command factory matches this name with that received from the stdin stream
  m_strMiCmd = "exec-step";

  // Required by the CMICmdFactory when registering *this command
  m_pSelfCreatorFn = &CMICmdCmdExecStep::CreateSelf;
}

//++
// Details: CMICmdCmdExecStep destructor.
// Type:    Overrideable.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdExecStep::~CMICmdCmdExecStep() {}

//++
// Details: The invoker requires this function. The parses the command line
// options
//          arguments to extract values for each of those arguments.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdExecStep::ParseArgs() {
  m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgNumber, false, false));
  return ParseValidateCmdOptions();
}

//++
// Details: The invoker requires this function. The command does work in this
// function.
//          The command is likely to communicate with the LLDB SBDebugger in
//          here.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdExecStep::Execute() {
  CMICMDBASE_GETOPTION(pArgThread, OptionLong, m_constStrArgThread);

  // Retrieve the --thread option's thread ID (only 1)
  MIuint64 nThreadId = UINT64_MAX;
  if (pArgThread->GetFound() &&
      !pArgThread->GetExpectedOption<CMICmdArgValNumber, MIuint64>(nThreadId)) {
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND),
                                   m_cmdData.strMiCmd.c_str(),
                                   m_constStrArgThread.c_str()));
    return MIstatus::failure;
  }

  CMICmnLLDBDebugSessionInfo &rSessionInfo(
      CMICmnLLDBDebugSessionInfo::Instance());

  lldb::SBError error;
  if (nThreadId != UINT64_MAX) {
    lldb::SBThread sbThread =
        rSessionInfo.GetProcess().GetThreadByIndexID(nThreadId);
    if (!sbThread.IsValid()) {
      SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_THREAD_INVALID),
                                     m_cmdData.strMiCmd.c_str(),
                                     m_constStrArgThread.c_str()));
      return MIstatus::failure;
    }
    sbThread.StepInto(nullptr, LLDB_INVALID_LINE_NUMBER, error);
  } else
    rSessionInfo.GetProcess().GetSelectedThread().StepInto(
        nullptr, LLDB_INVALID_LINE_NUMBER, error);

  return HandleSBError(error);
}

//++
// Details: The invoker requires this function. The command prepares a MI Record
// Result
//          for the work carried out in the Execute().
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdExecStep::Acknowledge() {
  m_miResultRecord = CMICmnMIResultRecord(
      m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Running);
  return MIstatus::success;
}

//++
// Details: Required by the CMICmdFactory when registering *this command. The
// factory
//          calls this function to create an instance of *this command.
// Type:    Static method.
// Args:    None.
// Return:  CMICmdBase * - Pointer to a new command.
// Throws:  None.
//--
CMICmdBase *CMICmdCmdExecStep::CreateSelf() { return new CMICmdCmdExecStep(); }


//++
// Details: CMICmdCmdExecNextInstruction constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdExecNextInstruction::CMICmdCmdExecNextInstruction()
    : m_constStrArgNumber("number") {
  // Command factory matches this name with that received from the stdin stream
  m_strMiCmd = "exec-next-instruction";

  // Required by the CMICmdFactory when registering *this command
  m_pSelfCreatorFn = &CMICmdCmdExecNextInstruction::CreateSelf;
}

//++
// Details: CMICmdCmdExecNextInstruction destructor.
// Type:    Overrideable.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdExecNextInstruction::~CMICmdCmdExecNextInstruction() {}

//++
// Details: The invoker requires this function. The parses the command line
// options
//          arguments to extract values for each of those arguments.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdExecNextInstruction::ParseArgs() {
  m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgNumber, false, false));
  return ParseValidateCmdOptions();
}

//++
// Details: The invoker requires this function. The command does work in this
// function.
//          The command is likely to communicate with the LLDB SBDebugger in
//          here.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdExecNextInstruction::Execute() {
  CMICMDBASE_GETOPTION(pArgThread, OptionLong, m_constStrArgThread);

  // Retrieve the --thread option's thread ID (only 1)
  MIuint64 nThreadId = UINT64_MAX;
  if (pArgThread->GetFound() &&
      !pArgThread->GetExpectedOption<CMICmdArgValNumber, MIuint64>(nThreadId)) {
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND),
                                   m_cmdData.strMiCmd.c_str(),
                                   m_constStrArgThread.c_str()));
    return MIstatus::failure;
  }

  CMICmnLLDBDebugSessionInfo &rSessionInfo(
      CMICmnLLDBDebugSessionInfo::Instance());

  lldb::SBError error;
  if (nThreadId != UINT64_MAX) {
    lldb::SBThread sbThread =
        rSessionInfo.GetProcess().GetThreadByIndexID(nThreadId);
    if (!sbThread.IsValid()) {
      SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_THREAD_INVALID),
                                     m_cmdData.strMiCmd.c_str(),
                                     m_constStrArgThread.c_str()));
      return MIstatus::failure;
    }
    sbThread.StepInstruction(true, error);
  } else
    rSessionInfo.GetProcess().GetSelectedThread().StepInstruction(
        true, error);

  return HandleSBError(error);
}

//++
// Details: The invoker requires this function. The command prepares a MI Record
// Result
//          for the work carried out in the Execute().
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdExecNextInstruction::Acknowledge() {
  m_miResultRecord = CMICmnMIResultRecord(
      m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Running);
  return MIstatus::success;
}

//++
// Details: Required by the CMICmdFactory when registering *this command. The
// factory
//          calls this function to create an instance of *this command.
// Type:    Static method.
// Args:    None.
// Return:  CMICmdBase * - Pointer to a new command.
// Throws:  None.
//--
CMICmdBase *CMICmdCmdExecNextInstruction::CreateSelf() {
  return new CMICmdCmdExecNextInstruction();
}


//++
// Details: CMICmdCmdExecStepInstruction constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdExecStepInstruction::CMICmdCmdExecStepInstruction()
    : m_constStrArgNumber("number") {
  // Command factory matches this name with that received from the stdin stream
  m_strMiCmd = "exec-step-instruction";

  // Required by the CMICmdFactory when registering *this command
  m_pSelfCreatorFn = &CMICmdCmdExecStepInstruction::CreateSelf;
}

//++
// Details: CMICmdCmdExecStepInstruction destructor.
// Type:    Overrideable.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdExecStepInstruction::~CMICmdCmdExecStepInstruction() {}

//++
// Details: The invoker requires this function. The parses the command line
// options
//          arguments to extract values for each of those arguments.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdExecStepInstruction::ParseArgs() {
  m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgNumber, false, false));
  return ParseValidateCmdOptions();
}

//++
// Details: The invoker requires this function. The command does work in this
// function.
//          The command is likely to communicate with the LLDB SBDebugger in
//          here.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdExecStepInstruction::Execute() {
  CMICMDBASE_GETOPTION(pArgThread, OptionLong, m_constStrArgThread);

  // Retrieve the --thread option's thread ID (only 1)
  MIuint64 nThreadId = UINT64_MAX;
  if (pArgThread->GetFound() &&
      !pArgThread->GetExpectedOption<CMICmdArgValNumber, MIuint64>(nThreadId)) {
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND),
                                   m_cmdData.strMiCmd.c_str(),
                                   m_constStrArgThread.c_str()));
    return MIstatus::failure;
  }

  CMICmnLLDBDebugSessionInfo &rSessionInfo(
      CMICmnLLDBDebugSessionInfo::Instance());

  lldb::SBError error;
  if (nThreadId != UINT64_MAX) {
    lldb::SBThread sbThread =
        rSessionInfo.GetProcess().GetThreadByIndexID(nThreadId);
    if (!sbThread.IsValid()) {
      SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_THREAD_INVALID),
                                     m_cmdData.strMiCmd.c_str(),
                                     m_constStrArgThread.c_str()));
      return MIstatus::failure;
    }
    sbThread.StepInstruction(false, error);
  } else
    rSessionInfo.GetProcess().GetSelectedThread().StepInstruction(
        false, error);

  return HandleSBError(error);
}

//++
// Details: The invoker requires this function. The command prepares a MI Record
// Result
//          for the work carried out in the Execute().
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdExecStepInstruction::Acknowledge() {
  m_miResultRecord = CMICmnMIResultRecord(
      m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Running);
  return MIstatus::success;
}

//++
// Details: Required by the CMICmdFactory when registering *this command. The
// factory
//          calls this function to create an instance of *this command.
// Type:    Static method.
// Args:    None.
// Return:  CMICmdBase * - Pointer to a new command.
// Throws:  None.
//--
CMICmdBase *CMICmdCmdExecStepInstruction::CreateSelf() {
  return new CMICmdCmdExecStepInstruction();
}


//++
// Details: CMICmdCmdExecFinish constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdExecFinish::CMICmdCmdExecFinish() {
  // Command factory matches this name with that received from the stdin stream
  m_strMiCmd = "exec-finish";

  // Required by the CMICmdFactory when registering *this command
  m_pSelfCreatorFn = &CMICmdCmdExecFinish::CreateSelf;
}

//++
// Details: CMICmdCmdExecFinish destructor.
// Type:    Overrideable.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdExecFinish::~CMICmdCmdExecFinish() {}

//++
// Details: The invoker requires this function. The parses the command line
// options
//          arguments to extract values for each of those arguments.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdExecFinish::ParseArgs() { return ParseValidateCmdOptions(); }

//++
// Details: The invoker requires this function. The command does work in this
// function.
//          The command is likely to communicate with the LLDB SBDebugger in
//          here.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdExecFinish::Execute() {
  CMICMDBASE_GETOPTION(pArgThread, OptionLong, m_constStrArgThread);

  // Retrieve the --thread option's thread ID (only 1)
  MIuint64 nThreadId = UINT64_MAX;
  if (pArgThread->GetFound() &&
      !pArgThread->GetExpectedOption<CMICmdArgValNumber, MIuint64>(nThreadId)) {
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND),
                                   m_cmdData.strMiCmd.c_str(),
                                   m_constStrArgThread.c_str()));
    return MIstatus::failure;
  }

  CMICmnLLDBDebugSessionInfo &rSessionInfo(
      CMICmnLLDBDebugSessionInfo::Instance());

  lldb::SBError error;
  if (nThreadId != UINT64_MAX) {
    lldb::SBThread sbThread =
        rSessionInfo.GetProcess().GetThreadByIndexID(nThreadId);
    if (!sbThread.IsValid()) {
      SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_THREAD_INVALID),
                                     m_cmdData.strMiCmd.c_str(),
                                     m_constStrArgThread.c_str()));
      return MIstatus::failure;
    }
    sbThread.StepOut(error);
  } else
    rSessionInfo.GetProcess().GetSelectedThread().StepOut(error);

  return HandleSBError(error);
}

//++
// Details: The invoker requires this function. The command prepares a MI Record
// Result
//          for the work carried out in the Execute().
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdExecFinish::Acknowledge() {
  m_miResultRecord = CMICmnMIResultRecord(
      m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Running);
  return MIstatus::success;
}

//++
// Details: Required by the CMICmdFactory when registering *this command. The
// factory
//          calls this function to create an instance of *this command.
// Type:    Static method.
// Args:    None.
// Return:  CMICmdBase * - Pointer to a new command.
// Throws:  None.
//--
CMICmdBase *CMICmdCmdExecFinish::CreateSelf() {
  return new CMICmdCmdExecFinish();
}


//++
// Details: CMICmdCmdExecInterrupt constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdExecInterrupt::CMICmdCmdExecInterrupt() {
  // Command factory matches this name with that received from the stdin stream
  m_strMiCmd = "exec-interrupt";

  // Required by the CMICmdFactory when registering *this command
  m_pSelfCreatorFn = &CMICmdCmdExecInterrupt::CreateSelf;
}

//++
// Details: CMICmdCmdExecInterrupt destructor.
// Type:    Overrideable.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdExecInterrupt::~CMICmdCmdExecInterrupt() {}

//++
// Details: The invoker requires this function. The command does work in this
// function.
//          The command is likely to communicate with the LLDB SBDebugger in
//          here.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdExecInterrupt::Execute() {
  const auto successHandler = [this] {
    // CODETAG_DEBUG_SESSION_RUNNING_PROG_RECEIVED_SIGINT_PAUSE_PROGRAM
    if (!CMIDriver::Instance().SetDriverStateRunningNotDebugging()) {
      const CMIUtilString &rErrMsg(CMIDriver::Instance().GetErrorDescription());
      SetErrorDescription(CMIUtilString::Format(
          MIRSRC(IDS_CMD_ERR_SET_NEW_DRIVER_STATE),
          m_cmdData.strMiCmd.c_str(),
          rErrMsg.c_str()));
      return MIstatus::failure;
    }
    return MIstatus::success;
  };

  return HandleSBErrorWithSuccess(
      CMICmnLLDBDebugSessionInfo::Instance().GetProcess().Stop(),
      successHandler);
}

//++
// Details: The invoker requires this function. The command prepares a MI Record
// Result
//          for the work carried out in the Execute().
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool CMICmdCmdExecInterrupt::Acknowledge() {
  m_miResultRecord = CMICmnMIResultRecord(
      m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done);
  return MIstatus::success;
}

//++
// Details: Required by the CMICmdFactory when registering *this command. The
// factory
//          calls this function to create an instance of *this command.
// Type:    Static method.
// Args:    None.
// Return:  CMICmdBase * - Pointer to a new command.
// Throws:  None.
//--
CMICmdBase *CMICmdCmdExecInterrupt::CreateSelf() {
  return new CMICmdCmdExecInterrupt();
}


//++
// Details: CMICmdCmdExecArguments constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdExecArguments::CMICmdCmdExecArguments()
    : m_constStrArgArguments("arguments") {
  // Command factory matches this name with that received from the stdin stream
  m_strMiCmd = "exec-arguments";

  // Required by the CMICmdFactory when registering *this command
  m_pSelfCreatorFn = &CMICmdCmdExecArguments::CreateSelf;
}

//++
// Details: CMICmdCmdExecArguments destructor.
// Type:    Overrideable.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdExecArguments::~CMICmdCmdExecArguments() {}

//++
// Details: The invoker requires this function. The parses the command line
// options
//          arguments to extract values for each of those arguments.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Function succeeded.
//          MIstatus::failure - Function failed.
// Throws:  None.
//--
bool CMICmdCmdExecArguments::ParseArgs() {
  m_setCmdArgs.Add(new CMICmdArgValListOfN(
      m_constStrArgArguments, false, true,
      CMICmdArgValListBase::eArgValType_StringAnything));
  return ParseValidateCmdOptions();
}

//++
// Details: The invoker requires this function. The command does work in this
// function.
//          The command is likely to communicate with the LLDB SBDebugger in
//          here.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Function succeeded.
//          MIstatus::failure - Function failed.
// Throws:  None.
//--
bool CMICmdCmdExecArguments::Execute() {
  CMICMDBASE_GETOPTION(pArgArguments, ListOfN, m_constStrArgArguments);

  CMICmnLLDBDebugSessionInfo &rSessionInfo(
      CMICmnLLDBDebugSessionInfo::Instance());
  lldb::SBTarget sbTarget = rSessionInfo.GetTarget();
  if (!sbTarget.IsValid()) {
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_TARGET_CURRENT),
                                   m_cmdData.strMiCmd.c_str()));
    return MIstatus::failure;
  }

  lldb::SBLaunchInfo sbLaunchInfo = sbTarget.GetLaunchInfo();
  sbLaunchInfo.SetArguments(nullptr, false);

  CMIUtilString strArg;
  size_t nArgIndex = 0;
  while (pArgArguments->GetExpectedOption<CMICmdArgValString, CMIUtilString>(
      strArg, nArgIndex)) {
    const char *argv[2] = {strArg.c_str(), nullptr};
    sbLaunchInfo.SetArguments(argv, true);
    ++nArgIndex;
  }

  sbTarget.SetLaunchInfo(sbLaunchInfo);

  return MIstatus::success;
}

//++
// Details: The invoker requires this function. The command prepares a MI Record
// Result
//          for the work carried out in the Execute().
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Function succeeded.
//          MIstatus::failure - Function failed.
// Throws:  None.
//--
bool CMICmdCmdExecArguments::Acknowledge() {
  m_miResultRecord = CMICmnMIResultRecord(
      m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done);
  return MIstatus::success;
}

//++
// Details: Required by the CMICmdFactory when registering *this command. The
// factory
//          calls this function to create an instance of *this command.
// Type:    Static method.
// Args:    None.
// Return:  CMICmdBase * - Pointer to a new command.
// Throws:  None.
//--
CMICmdBase *CMICmdCmdExecArguments::CreateSelf() {
  return new CMICmdCmdExecArguments();
}


//++
// Details: CMICmdCmdExecAbort constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdExecAbort::CMICmdCmdExecAbort() {
  // Command factory matches this name with that received from the stdin stream
  m_strMiCmd = "exec-abort";

  // Required by the CMICmdFactory when registering *this command
  m_pSelfCreatorFn = &CMICmdCmdExecAbort::CreateSelf;
}

//++
// Details: CMICmdCmdExecAbort destructor.
// Type:    Overrideable.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMICmdCmdExecAbort::~CMICmdCmdExecAbort() {}

//++
// Details: The invoker requires this function. The command does work in this
// function.
//          The command is likely to communicate with the LLDB SBDebugger in
//          here.
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Function succeeded.
//          MIstatus::failure - Function failed.
// Throws:  None.
//--
bool CMICmdCmdExecAbort::Execute() {
  CMICmnLLDBDebugSessionInfo &rSessionInfo(
      CMICmnLLDBDebugSessionInfo::Instance());
  lldb::SBProcess sbProcess = rSessionInfo.GetProcess();
  if (!sbProcess.IsValid()) {
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_PROCESS),
                                   m_cmdData.strMiCmd.c_str()));
    return MIstatus::failure;
  }

  lldb::SBError sbError = sbProcess.Destroy();
  if (sbError.Fail()) {
    SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_LLDBPROCESS_DESTROY),
                                   m_cmdData.strMiCmd.c_str(),
                                   sbError.GetCString()));
    return MIstatus::failure;
  }

  return MIstatus::success;
}

//++
// Details: The invoker requires this function. The command prepares a MI Record
// Result
//          for the work carried out in the Execute().
// Type:    Overridden.
// Args:    None.
// Return:  MIstatus::success - Function succeeded.
//          MIstatus::failure - Function failed.
// Throws:  None.
//--
bool CMICmdCmdExecAbort::Acknowledge() {
  m_miResultRecord = CMICmnMIResultRecord(
      m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done);
  return MIstatus::success;
}

//++
// Details: Required by the CMICmdFactory when registering *this command. The
// factory
//          calls this function to create an instance of *this command.
// Type:    Static method.
// Args:    None.
// Return:  CMICmdBase * - Pointer to a new command.
// Throws:  None.
//--
CMICmdBase *CMICmdCmdExecAbort::CreateSelf() {
  return new CMICmdCmdExecAbort();
}
