Files
clang-p2996/lldb/tools/lldb-mi/MIDriver.cpp
Pavel Labath b6dbe9a99c Clean up lldb-types.h
Summary:
It defined a couple of types (condition_t) which we don't use anymore,
as we have c++11 goodies now. I remove these definitions.

Also it unnecessarily included a couple of headers which weren't
necessary for it's operation. I remove these, and place the includes in
the relevant files (usually .cpp, usually in Host code) which use them.
This allows us to reduce namespace pollution in most of the lldb files
which don't need the OS-specific definitions.

Reviewers: zturner, jingham

Subscribers: ki.stfu, lldb-commits

Differential Revision: https://reviews.llvm.org/D35113

llvm-svn: 308304
2017-07-18 13:14:01 +00:00

1367 lines
48 KiB
C++

//===-- MIDriver.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/SBError.h"
#include <cassert>
#include <csignal>
#include <fstream>
// In-house headers:
#include "MICmdArgValFile.h"
#include "MICmdArgValString.h"
#include "MICmdMgr.h"
#include "MICmnConfig.h"
#include "MICmnLLDBDebugSessionInfo.h"
#include "MICmnLLDBDebugger.h"
#include "MICmnLog.h"
#include "MICmnMIResultRecord.h"
#include "MICmnMIValueConst.h"
#include "MICmnResources.h"
#include "MICmnStreamStderr.h"
#include "MICmnStreamStdout.h"
#include "MICmnThreadMgrStd.h"
#include "MIDriver.h"
#include "MIUtilDebug.h"
#include "MIUtilSingletonHelper.h"
// Instantiations:
#if _DEBUG
const CMIUtilString CMIDriver::ms_constMIVersion =
MIRSRC(IDS_MI_VERSION_DESCRIPTION_DEBUG);
#else
const CMIUtilString CMIDriver::ms_constMIVersion =
MIRSRC(IDS_MI_VERSION_DESCRIPTION); // Matches version in resources file
#endif // _DEBUG
const CMIUtilString
CMIDriver::ms_constAppNameShort(MIRSRC(IDS_MI_APPNAME_SHORT));
const CMIUtilString CMIDriver::ms_constAppNameLong(MIRSRC(IDS_MI_APPNAME_LONG));
//++
//------------------------------------------------------------------------------------
// Details: CMIDriver constructor.
// Type: Method.
// Args: None.
// Return: None.
// Throws: None.
//--
CMIDriver::CMIDriver()
: m_bFallThruToOtherDriverEnabled(false), m_bDriverIsExiting(false),
m_handleMainThread(0), m_rStdin(CMICmnStreamStdin::Instance()),
m_rLldbDebugger(CMICmnLLDBDebugger::Instance()),
m_rStdOut(CMICmnStreamStdout::Instance()),
m_eCurrentDriverState(eDriverState_NotRunning),
m_bHaveExecutableFileNamePathOnCmdLine(false),
m_bDriverDebuggingArgExecutable(false),
m_bHaveCommandFileNamePathOnCmdLine(false) {}
//++
//------------------------------------------------------------------------------------
// Details: CMIDriver destructor.
// Type: Overridden.
// Args: None.
// Return: None.
// Throws: None.
//--
CMIDriver::~CMIDriver() {}
//++
//------------------------------------------------------------------------------------
// Details: Set whether *this driver (the parent) is enabled to pass a command
// to its
// fall through (child) driver to interpret the command and do work
// instead
// (if *this driver decides it can't handle the command).
// Type: Method.
// Args: vbYes - (R) True = yes fall through, false = do not pass on
// command.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool CMIDriver::SetEnableFallThru(const bool vbYes) {
m_bFallThruToOtherDriverEnabled = vbYes;
return MIstatus::success;
}
//++
//------------------------------------------------------------------------------------
// Details: Get whether *this driver (the parent) is enabled to pass a command
// to its
// fall through (child) driver to interpret the command and do work
// instead
// (if *this driver decides it can't handle the command).
// Type: Method.
// Args: None.
// Return: bool - True = yes fall through, false = do not pass on command.
// Throws: None.
//--
bool CMIDriver::GetEnableFallThru() const {
return m_bFallThruToOtherDriverEnabled;
}
//++
//------------------------------------------------------------------------------------
// Details: Retrieve MI's application name of itself.
// Type: Method.
// Args: None.
// Return: CMIUtilString & - Text description.
// Throws: None.
//--
const CMIUtilString &CMIDriver::GetAppNameShort() const {
return ms_constAppNameShort;
}
//++
//------------------------------------------------------------------------------------
// Details: Retrieve MI's application name of itself.
// Type: Method.
// Args: None.
// Return: CMIUtilString & - Text description.
// Throws: None.
//--
const CMIUtilString &CMIDriver::GetAppNameLong() const {
return ms_constAppNameLong;
}
//++
//------------------------------------------------------------------------------------
// Details: Retrieve MI's version description of itself.
// Type: Method.
// Args: None.
// Return: CMIUtilString & - Text description.
// Throws: None.
//--
const CMIUtilString &CMIDriver::GetVersionDescription() const {
return ms_constMIVersion;
}
//++
//------------------------------------------------------------------------------------
// Details: Initialize setup *this driver ready for use.
// Type: Method.
// Args: None.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool CMIDriver::Initialize() {
m_eCurrentDriverState = eDriverState_Initialising;
m_clientUsageRefCnt++;
ClrErrorDescription();
if (m_bInitialized)
return MIstatus::success;
bool bOk = MIstatus::success;
CMIUtilString errMsg;
// Initialize all of the modules we depend on
MI::ModuleInit<CMICmnLog>(IDS_MI_INIT_ERR_LOG, bOk, errMsg);
MI::ModuleInit<CMICmnStreamStdout>(IDS_MI_INIT_ERR_STREAMSTDOUT, bOk, errMsg);
MI::ModuleInit<CMICmnStreamStderr>(IDS_MI_INIT_ERR_STREAMSTDERR, bOk, errMsg);
MI::ModuleInit<CMICmnResources>(IDS_MI_INIT_ERR_RESOURCES, bOk, errMsg);
MI::ModuleInit<CMICmnThreadMgrStd>(IDS_MI_INIT_ERR_THREADMANAGER, bOk,
errMsg);
MI::ModuleInit<CMICmnStreamStdin>(IDS_MI_INIT_ERR_STREAMSTDIN, bOk, errMsg);
MI::ModuleInit<CMICmdMgr>(IDS_MI_INIT_ERR_CMDMGR, bOk, errMsg);
bOk &= m_rLldbDebugger.SetDriver(*this);
MI::ModuleInit<CMICmnLLDBDebugger>(IDS_MI_INIT_ERR_LLDBDEBUGGER, bOk, errMsg);
m_bExitApp = false;
m_bInitialized = bOk;
if (!bOk) {
const CMIUtilString msg =
CMIUtilString::Format(MIRSRC(IDS_MI_INIT_ERR_DRIVER), errMsg.c_str());
SetErrorDescription(msg);
return MIstatus::failure;
}
m_eCurrentDriverState = eDriverState_RunningNotDebugging;
return bOk;
}
//++
//------------------------------------------------------------------------------------
// Details: Unbind detach or release resources used by *this driver.
// Type: Method.
// Args: None.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool CMIDriver::Shutdown() {
if (--m_clientUsageRefCnt > 0)
return MIstatus::success;
if (!m_bInitialized)
return MIstatus::success;
m_eCurrentDriverState = eDriverState_ShuttingDown;
ClrErrorDescription();
bool bOk = MIstatus::success;
CMIUtilString errMsg;
// Shutdown all of the modules we depend on
MI::ModuleShutdown<CMICmnLLDBDebugger>(IDS_MI_INIT_ERR_LLDBDEBUGGER, bOk,
errMsg);
MI::ModuleShutdown<CMICmdMgr>(IDS_MI_INIT_ERR_CMDMGR, bOk, errMsg);
MI::ModuleShutdown<CMICmnStreamStdin>(IDS_MI_INIT_ERR_STREAMSTDIN, bOk,
errMsg);
MI::ModuleShutdown<CMICmnThreadMgrStd>(IDS_MI_INIT_ERR_THREADMANAGER, bOk,
errMsg);
MI::ModuleShutdown<CMICmnResources>(IDS_MI_INIT_ERR_RESOURCES, bOk, errMsg);
MI::ModuleShutdown<CMICmnStreamStderr>(IDS_MI_INIT_ERR_STREAMSTDERR, bOk,
errMsg);
MI::ModuleShutdown<CMICmnStreamStdout>(IDS_MI_INIT_ERR_STREAMSTDOUT, bOk,
errMsg);
MI::ModuleShutdown<CMICmnLog>(IDS_MI_INIT_ERR_LOG, bOk, errMsg);
if (!bOk) {
SetErrorDescriptionn(MIRSRC(IDS_MI_SHUTDOWN_ERR), errMsg.c_str());
}
m_eCurrentDriverState = eDriverState_NotRunning;
return bOk;
}
//++
//------------------------------------------------------------------------------------
// Details: Work function. Client (the driver's user) is able to append their
// own message
// in to the MI's Log trace file.
// Type: Method.
// Args: vMessage - (R) Client's text message.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool CMIDriver::WriteMessageToLog(const CMIUtilString &vMessage) {
CMIUtilString msg;
msg = CMIUtilString::Format(MIRSRC(IDS_MI_CLIENT_MSG), vMessage.c_str());
return m_pLog->Write(msg, CMICmnLog::eLogVerbosity_ClientMsg);
}
//++
//------------------------------------------------------------------------------------
// Details: CDriverMgr calls *this driver initialize setup ready for use.
// Type: Overridden.
// Args: None.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool CMIDriver::DoInitialize() { return CMIDriver::Instance().Initialize(); }
//++
//------------------------------------------------------------------------------------
// Details: CDriverMgr calls *this driver to unbind detach or release resources
// used by
// *this driver.
// Type: Overridden.
// Args: None.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool CMIDriver::DoShutdown() { return CMIDriver::Instance().Shutdown(); }
//++
//------------------------------------------------------------------------------------
// Details: Retrieve the name for *this driver.
// Type: Overridden.
// Args: None.
// Return: CMIUtilString & - Driver name.
// Throws: None.
//--
const CMIUtilString &CMIDriver::GetName() const {
const CMIUtilString &rName = GetAppNameLong();
const CMIUtilString &rVsn = GetVersionDescription();
static CMIUtilString strName =
CMIUtilString::Format("%s %s", rName.c_str(), rVsn.c_str());
return strName;
}
//++
//------------------------------------------------------------------------------------
// Details: Retrieve *this driver's last error condition.
// Type: Overridden.
// Args: None.
// Return: CMIUtilString - Text description.
// Throws: None.
//--
CMIUtilString CMIDriver::GetError() const { return GetErrorDescription(); }
//++
//------------------------------------------------------------------------------------
// Details: Call *this driver to return it's debugger.
// Type: Overridden.
// Args: None.
// Return: lldb::SBDebugger & - LLDB debugger object reference.
// Throws: None.
//--
lldb::SBDebugger &CMIDriver::GetTheDebugger() {
return m_rLldbDebugger.GetTheDebugger();
}
//++
//------------------------------------------------------------------------------------
// Details: Specify another driver *this driver can call should this driver not
// be able
// to handle the client data input. DoFallThruToAnotherDriver() makes
// the call.
// Type: Overridden.
// Args: vrOtherDriver - (R) Reference to another driver object.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool CMIDriver::SetDriverToFallThruTo(const CMIDriverBase &vrOtherDriver) {
m_pDriverFallThru = const_cast<CMIDriverBase *>(&vrOtherDriver);
return m_pDriverFallThru->SetDriverParent(*this);
}
//++
//------------------------------------------------------------------------------------
// Details: Proxy function CMIDriverMgr IDriver interface implementation. *this
// driver's
// implementation called from here to match the existing function name
// of the
// original LLDB driver class (the extra indirection is not necessarily
// required).
// Check the arguments that were passed to this program to make sure
// they are
// valid and to get their argument values (if any).
// Type: Overridden.
// Args: argc - (R) An integer that contains the count of arguments
// that follow in
// argv. The argc parameter is always greater than
// or equal to 1.
// argv - (R) An array of null-terminated strings representing
// command-line
// arguments entered by the user of the program. By
// convention,
// argv[0] is the command with which the program is
// invoked.
// vpStdOut - (R) Pointer to a standard output stream.
// vwbExiting - (W) True = *this want to exit, Reasons: help,
// invalid arg(s),
// version information only.
// False = Continue to work, start debugger i.e.
// Command
// interpreter.
// Return: lldb::SBError - LLDB current error status.
// Throws: None.
//--
lldb::SBError CMIDriver::DoParseArgs(const int argc, const char *argv[],
FILE *vpStdOut, bool &vwbExiting) {
return ParseArgs(argc, argv, vpStdOut, vwbExiting);
}
//++
//------------------------------------------------------------------------------------
// Details: Check the arguments that were passed to this program to make sure
// they are
// valid and to get their argument values (if any). The following are
// options
// that are only handled by *this driver:
// --executable <file>
// --source <file> or -s <file>
// The application's options --interpreter and --executable in code act
// very similar.
// The --executable is necessary to differentiate whether the MI Driver
// is being
// used by a client (e.g. Eclipse) or from the command line. Eclipse
// issues the option
// --interpreter and also passes additional arguments which can be
// interpreted as an
// executable if called from the command line. Using --executable tells
// the MI Driver
// it is being called from the command line and to prepare to launch
// the executable
// argument for a debug session. Using --interpreter on the command
// line does not
// issue additional commands to initialise a debug session.
// Type: Overridden.
// Args: argc - (R) An integer that contains the count of arguments
// that follow in
// argv. The argc parameter is always greater than
// or equal to 1.
// argv - (R) An array of null-terminated strings representing
// command-line
// arguments entered by the user of the program. By
// convention,
// argv[0] is the command with which the program is
// invoked.
// vpStdOut - (R) Pointer to a standard output stream.
// vwbExiting - (W) True = *this want to exit, Reasons: help,
// invalid arg(s),
// version information only.
// False = Continue to work, start debugger i.e.
// Command
// interpreter.
// Return: lldb::SBError - LLDB current error status.
// Throws: None.
//--
lldb::SBError CMIDriver::ParseArgs(const int argc, const char *argv[],
FILE *vpStdOut, bool &vwbExiting) {
lldb::SBError errStatus;
const bool bHaveArgs(argc >= 2);
// *** Add any args handled here to GetHelpOnCmdLineArgOptions() ***
// CODETAG_MIDRIVE_CMD_LINE_ARG_HANDLING
// Look for the command line options
bool bHaveExecutableFileNamePath = false;
bool bHaveExecutableLongOption = false;
if (bHaveArgs) {
// Search right to left to look for filenames
for (MIint i = argc - 1; i > 0; i--) {
const CMIUtilString strArg(argv[i]);
const CMICmdArgValFile argFile;
// Check for a filename
if (argFile.IsFilePath(strArg) ||
CMICmdArgValString(true, false, true).IsStringArg(strArg)) {
// Is this the command file for the '-s' or '--source' options?
const CMIUtilString strPrevArg(argv[i - 1]);
if (strPrevArg.compare("-s") == 0 ||
strPrevArg.compare("--source") == 0) {
m_strCmdLineArgCommandFileNamePath = strArg;
m_bHaveCommandFileNamePathOnCmdLine = true;
i--; // skip '-s' on the next loop
continue;
}
// Else, must be the executable
bHaveExecutableFileNamePath = true;
m_strCmdLineArgExecuteableFileNamePath = strArg;
m_bHaveExecutableFileNamePathOnCmdLine = true;
}
// Report error if no command file was specified for the '-s' or
// '--source' options
else if (strArg.compare("-s") == 0 || strArg.compare("--source") == 0) {
vwbExiting = true;
const CMIUtilString errMsg = CMIUtilString::Format(
MIRSRC(IDS_CMD_ARGS_ERR_VALIDATION_MISSING_INF), strArg.c_str());
errStatus.SetErrorString(errMsg.c_str());
break;
}
// This argument is also checked for in CMIDriverMgr::ParseArgs()
else if (strArg.compare("--executable") == 0) // Used to specify that
// there is executable
// argument also on the
// command line
{ // See fn description.
bHaveExecutableLongOption = true;
}
}
}
if (bHaveExecutableFileNamePath && bHaveExecutableLongOption) {
SetDriverDebuggingArgExecutable();
}
return errStatus;
}
//++
//------------------------------------------------------------------------------------
// Details: A client can ask if *this driver is GDB/MI compatible.
// Type: Overridden.
// Args: None.
// Return: True - GBD/MI compatible LLDB front end.
// False - Not GBD/MI compatible LLDB front end.
// Throws: None.
//--
bool CMIDriver::GetDriverIsGDBMICompatibleDriver() const { return true; }
//++
//------------------------------------------------------------------------------------
// Details: Start worker threads for the driver.
// Type: Method.
// Args: None.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool CMIDriver::StartWorkerThreads() {
bool bOk = MIstatus::success;
// Grab the thread manager
CMICmnThreadMgrStd &rThreadMgr = CMICmnThreadMgrStd::Instance();
// Start the event polling thread
if (bOk && !rThreadMgr.ThreadStart<CMICmnLLDBDebugger>(m_rLldbDebugger)) {
const CMIUtilString errMsg = CMIUtilString::Format(
MIRSRC(IDS_THREADMGR_ERR_THREAD_FAIL_CREATE),
CMICmnThreadMgrStd::Instance().GetErrorDescription().c_str());
SetErrorDescription(errMsg);
return MIstatus::failure;
}
return bOk;
}
//++
//------------------------------------------------------------------------------------
// Details: Stop worker threads for the driver.
// Type: Method.
// Args: None.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool CMIDriver::StopWorkerThreads() {
CMICmnThreadMgrStd &rThreadMgr = CMICmnThreadMgrStd::Instance();
return rThreadMgr.ThreadAllTerminate();
}
//++
//------------------------------------------------------------------------------------
// Details: Call this function puts *this driver to work.
// This function is used by the application's main thread.
// Type: Overridden.
// Args: None.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool CMIDriver::DoMainLoop() {
if (!InitClientIDEToMIDriver()) // Init Eclipse IDE
{
SetErrorDescriptionn(MIRSRC(IDS_MI_INIT_ERR_CLIENT_USING_DRIVER));
return MIstatus::failure;
}
if (!StartWorkerThreads())
return MIstatus::failure;
bool bOk = MIstatus::success;
if (HaveExecutableFileNamePathOnCmdLine()) {
if (!LocalDebugSessionStartupExecuteCommands()) {
SetErrorDescription(MIRSRC(IDS_MI_INIT_ERR_LOCAL_DEBUG_SESSION));
bOk = MIstatus::failure;
}
}
// App is not quitting currently
m_bExitApp = false;
// Handle source file
if (m_bHaveCommandFileNamePathOnCmdLine) {
const bool bAsyncMode = false;
ExecuteCommandFile(bAsyncMode);
}
// While the app is active
while (bOk && !m_bExitApp) {
CMIUtilString errorText;
const char *pCmd = m_rStdin.ReadLine(errorText);
if (pCmd != nullptr) {
CMIUtilString lineText(pCmd);
if (!lineText.empty()) {
// Check that the handler thread is alive (otherwise we stuck here)
assert(CMICmnLLDBDebugger::Instance().ThreadIsActive());
{
// Lock Mutex before processing commands so that we don't disturb an
// event
// being processed
CMIUtilThreadLock lock(
CMICmnLLDBDebugSessionInfo::Instance().GetSessionMutex());
bOk = InterpretCommand(lineText);
}
// Draw prompt if desired
bOk = bOk && CMICmnStreamStdout::WritePrompt();
// Wait while the handler thread handles incoming events
CMICmnLLDBDebugger::Instance().WaitForHandleEvent();
}
}
}
// Signal that the application is shutting down
DoAppQuit();
// Close and wait for the workers to stop
StopWorkerThreads();
return MIstatus::success;
}
//++
//------------------------------------------------------------------------------------
// Details: Set things in motion, set state etc that brings *this driver (and
// the
// application) to a tidy shutdown.
// This function is used by the application's main thread.
// Type: Method.
// Args: None.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool CMIDriver::DoAppQuit() {
bool bYesQuit = true;
// Shutdown stuff, ready app for exit
{
CMIUtilThreadLock lock(m_threadMutex);
m_bDriverIsExiting = true;
}
return bYesQuit;
}
//++
//------------------------------------------------------------------------------------
// Details: *this driver passes text commands to a fall through driver is it
// does not
// understand them (the LLDB driver).
// This function is used by the application's main thread.
// Type: Method.
// Args: vTextLine - (R) Text data representing a possible command.
// vwbCmdYesValid - (W) True = Command valid, false = command not
// handled.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool CMIDriver::InterpretCommandFallThruDriver(const CMIUtilString &vTextLine,
bool &vwbCmdYesValid) {
MIunused(vTextLine);
MIunused(vwbCmdYesValid);
// ToDo: Implement when less urgent work to be done or decide remove as not
// required
// bool bOk = MIstatus::success;
// bool bCmdNotUnderstood = true;
// if( bCmdNotUnderstood && GetEnableFallThru() )
//{
// CMIUtilString errMsg;
// bOk = DoFallThruToAnotherDriver( vStdInBuffer, errMsg );
// if( !bOk )
// {
// errMsg = errMsg.StripCREndOfLine();
// errMsg = errMsg.StripCRAll();
// const CMIDriverBase * pOtherDriver = GetDriverToFallThruTo();
// const char * pName = pOtherDriver->GetDriverName().c_str();
// const char * pId = pOtherDriver->GetDriverId().c_str();
// const CMIUtilString msg( CMIUtilString::Format( MIRSRC(
// IDS_DRIVER_ERR_FALLTHRU_DRIVER_ERR ), pName, pId, errMsg.c_str() )
//);
// m_pLog->WriteMsg( msg );
// }
//}
//
// vwbCmdYesValid = bOk;
// CMIUtilString strNot;
// if( vwbCmdYesValid)
// strNot = CMIUtilString::Format( "%s ", MIRSRC( IDS_WORD_NOT ) );
// const CMIUtilString msg( CMIUtilString::Format( MIRSRC(
// IDS_FALLTHRU_DRIVER_CMD_RECEIVED ), vTextLine.c_str(), strNot.c_str() ) );
// m_pLog->WriteLog( msg );
return MIstatus::success;
}
//++
//------------------------------------------------------------------------------------
// Details: Retrieve the name for *this driver.
// Type: Overridden.
// Args: None.
// Return: CMIUtilString & - Driver name.
// Throws: None.
//--
const CMIUtilString &CMIDriver::GetDriverName() const { return GetName(); }
//++
//------------------------------------------------------------------------------------
// Details: Get the unique ID for *this driver.
// Type: Overridden.
// Args: None.
// Return: CMIUtilString & - Text description.
// Throws: None.
//--
const CMIUtilString &CMIDriver::GetDriverId() const { return GetId(); }
//++
//------------------------------------------------------------------------------------
// Details: This function allows *this driver to call on another driver to
// perform work
// should this driver not be able to handle the client data input.
// SetDriverToFallThruTo() specifies the fall through to driver.
// Check the error message if the function returns a failure.
// Type: Overridden.
// Args: vCmd - (R) Command instruction to interpret.
// vwErrMsg - (W) Status description on command failing.
// Return: MIstatus::success - Command succeeded.
// MIstatus::failure - Command failed.
// Throws: None.
//--
bool CMIDriver::DoFallThruToAnotherDriver(const CMIUtilString &vCmd,
CMIUtilString &vwErrMsg) {
bool bOk = MIstatus::success;
CMIDriverBase *pOtherDriver = GetDriverToFallThruTo();
if (pOtherDriver == nullptr)
return bOk;
return pOtherDriver->DoFallThruToAnotherDriver(vCmd, vwErrMsg);
}
//++
//------------------------------------------------------------------------------------
// Details: *this driver provides a file stream to other drivers on which *this
// driver
// write's out to and they read as expected input. *this driver is
// passing
// through commands to the (child) pass through assigned driver.
// Type: Overrdidden.
// Args: None.
// Return: FILE * - Pointer to stream.
// Throws: None.
//--
FILE *CMIDriver::GetStdin() const {
// Note this fn is called on CMIDriverMgr register driver so stream has to be
// available before *this driver has been initialized! Flaw?
// This very likely to change later to a stream that the pass thru driver
// will read and we write to give it 'input'
return stdin;
}
//++
//------------------------------------------------------------------------------------
// Details: *this driver provides a file stream to other pass through assigned
// drivers
// so they know what to write to.
// Type: Overidden.
// Args: None.
// Return: FILE * - Pointer to stream.
// Throws: None.
//--
FILE *CMIDriver::GetStdout() const {
// Note this fn is called on CMIDriverMgr register driver so stream has to be
// available before *this driver has been initialized! Flaw?
// Do not want to pass through driver to write to stdout
return NULL;
}
//++
//------------------------------------------------------------------------------------
// Details: *this driver provides a error file stream to other pass through
// assigned drivers
// so they know what to write to.
// Type: Overidden.
// Args: None.
// Return: FILE * - Pointer to stream.
// Throws: None.
//--
FILE *CMIDriver::GetStderr() const {
// Note this fn is called on CMIDriverMgr register driver so stream has to be
// available before *this driver has been initialized! Flaw?
// This very likely to change later to a stream that the pass thru driver
// will write to and *this driver reads from to pass on the CMICmnLog object
return stderr;
}
//++
//------------------------------------------------------------------------------------
// Details: Set a unique ID for *this driver. It cannot be empty.
// Type: Overridden.
// Args: vId - (R) Text description.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool CMIDriver::SetId(const CMIUtilString &vId) {
if (vId.empty()) {
SetErrorDescriptionn(MIRSRC(IDS_DRIVER_ERR_ID_INVALID), GetName().c_str(),
vId.c_str());
return MIstatus::failure;
}
m_strDriverId = vId;
return MIstatus::success;
}
//++
//------------------------------------------------------------------------------------
// Details: Get the unique ID for *this driver.
// Type: Overridden.
// Args: None.
// Return: CMIUtilString & - Text description.
// Throws: None.
//--
const CMIUtilString &CMIDriver::GetId() const { return m_strDriverId; }
//++
//------------------------------------------------------------------------------------
// Details: Interpret the text data and match against current commands to see if
// there
// is a match. If a match then the command is issued and actioned on.
// The
// text data if not understood by *this driver is past on to the Fall
// Thru
// driver.
// This function is used by the application's main thread.
// Type: Method.
// Args: vTextLine - (R) Text data representing a possible command.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool CMIDriver::InterpretCommand(const CMIUtilString &vTextLine) {
const bool bNeedToRebroadcastStopEvent =
m_rLldbDebugger.CheckIfNeedToRebroadcastStopEvent();
bool bCmdYesValid = false;
bool bOk = InterpretCommandThisDriver(vTextLine, bCmdYesValid);
if (bOk && !bCmdYesValid)
bOk = InterpretCommandFallThruDriver(vTextLine, bCmdYesValid);
if (bNeedToRebroadcastStopEvent)
m_rLldbDebugger.RebroadcastStopEvent();
return bOk;
}
//++
//------------------------------------------------------------------------------------
// Details: Helper function for CMIDriver::InterpretCommandThisDriver.
// Convert a CLI command to MI command (just wrap any CLI command
// into "<tokens>-interpreter-exec command \"<CLI command>\"").
// Type: Method.
// Args: vTextLine - (R) Text data representing a possible command.
// Return: CMIUtilString - The original MI command or converted CLI command.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
CMIUtilString
CMIDriver::WrapCLICommandIntoMICommand(const CMIUtilString &vTextLine) const {
// Tokens contain following digits
static const CMIUtilString digits("0123456789");
// Consider an algorithm on the following example:
// 001-file-exec-and-symbols "/path/to/file"
//
// 1. Skip a command token
// For example:
// 001-file-exec-and-symbols "/path/to/file"
// 001target create "/path/to/file"
// ^ -- command starts here (in both cases)
// Also possible case when command not found:
// 001
// ^ -- i.e. only tokens are present (or empty string at all)
const size_t nCommandOffset = vTextLine.find_first_not_of(digits);
// 2. Check if command is empty
// For example:
// 001-file-exec-and-symbols "/path/to/file"
// 001target create "/path/to/file"
// ^ -- command not empty (in both cases)
// or:
// 001
// ^ -- command wasn't found
const bool bIsEmptyCommand = (nCommandOffset == CMIUtilString::npos);
// 3. Check and exit if it isn't a CLI command
// For example:
// 001-file-exec-and-symbols "/path/to/file"
// 001
// ^ -- it isn't CLI command (in both cases)
// or:
// 001target create "/path/to/file"
// ^ -- it's CLI command
const bool bIsCliCommand =
!bIsEmptyCommand && (vTextLine.at(nCommandOffset) != '-');
if (!bIsCliCommand)
return vTextLine;
// 4. Wrap CLI command to make it MI-compatible
//
// 001target create "/path/to/file"
// ^^^ -- token
const std::string vToken(vTextLine.begin(),
vTextLine.begin() + nCommandOffset);
// 001target create "/path/to/file"
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- CLI command
const CMIUtilString vCliCommand(std::string(vTextLine, nCommandOffset));
// 5. Escape special characters and embed the command in a string
// Result: it looks like -- target create \"/path/to/file\".
const std::string vShieldedCliCommand(vCliCommand.AddSlashes());
// 6. Turn the CLI command into an MI command, as in:
// 001-interpreter-exec command "target create \"/path/to/file\""
// ^^^ -- token
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ -- wrapper
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- shielded
// CLI command
return CMIUtilString::Format("%s-interpreter-exec command \"%s\"",
vToken.c_str(), vShieldedCliCommand.c_str());
}
//++
//------------------------------------------------------------------------------------
// Details: Interpret the text data and match against current commands to see if
// there
// is a match. If a match then the command is issued and actioned on.
// If a
// command cannot be found to match then vwbCmdYesValid is set to false
// and
// nothing else is done here.
// This function is used by the application's main thread.
// Type: Method.
// Args: vTextLine - (R) Text data representing a possible command.
// vwbCmdYesValid - (W) True = Command valid, false = command not
// handled.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool CMIDriver::InterpretCommandThisDriver(const CMIUtilString &vTextLine,
bool &vwbCmdYesValid) {
// Convert any CLI commands into MI commands
const CMIUtilString vMITextLine(WrapCLICommandIntoMICommand(vTextLine));
vwbCmdYesValid = false;
bool bCmdNotInCmdFactor = false;
SMICmdData cmdData;
CMICmdMgr &rCmdMgr = CMICmdMgr::Instance();
if (!rCmdMgr.CmdInterpret(vMITextLine, vwbCmdYesValid, bCmdNotInCmdFactor,
cmdData))
return MIstatus::failure;
if (vwbCmdYesValid) {
// For debugging only
// m_pLog->WriteLog( cmdData.strMiCmdAll.c_str() );
return ExecuteCommand(cmdData);
}
// Check for escape character, may be cursor control characters
// This code is not necessary for application operation, just want to keep
// tabs on what
// has been given to the driver to try and interpret.
if (vMITextLine.at(0) == 27) {
CMIUtilString logInput(MIRSRC(IDS_STDIN_INPUT_CTRL_CHARS));
for (MIuint i = 0; i < vMITextLine.length(); i++) {
logInput += CMIUtilString::Format("%d ", vMITextLine.at(i));
}
m_pLog->WriteLog(logInput);
return MIstatus::success;
}
// Write to the Log that a 'command' was not valid.
// Report back to the MI client via MI result record.
CMIUtilString strNotInCmdFactory;
if (bCmdNotInCmdFactor)
strNotInCmdFactory = CMIUtilString::Format(
MIRSRC(IDS_DRIVER_CMD_NOT_IN_FACTORY), cmdData.strMiCmd.c_str());
const CMIUtilString strNot(
CMIUtilString::Format("%s ", MIRSRC(IDS_WORD_NOT)));
const CMIUtilString msg(CMIUtilString::Format(
MIRSRC(IDS_DRIVER_CMD_RECEIVED), vMITextLine.c_str(), strNot.c_str(),
strNotInCmdFactory.c_str()));
const CMICmnMIValueConst vconst = CMICmnMIValueConst(msg);
const CMICmnMIValueResult valueResult("msg", vconst);
const CMICmnMIResultRecord miResultRecord(
cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error,
valueResult);
const bool bOk = m_rStdOut.WriteMIResponse(miResultRecord.GetString());
// Proceed to wait for or execute next command
return bOk;
}
//++
//------------------------------------------------------------------------------------
// Details: Having previously had the potential command validated and found
// valid now
// get the command executed.
// This function is used by the application's main thread.
// Type: Method.
// Args: vCmdData - (RW) Command meta data.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool CMIDriver::ExecuteCommand(const SMICmdData &vCmdData) {
CMICmdMgr &rCmdMgr = CMICmdMgr::Instance();
return rCmdMgr.CmdExecute(vCmdData);
}
//++
//------------------------------------------------------------------------------------
// Details: Set the MI Driver's exit application flag. The application checks
// this flag
// after every stdin line is read so the exit may not be instantaneous.
// If vbForceExit is false the MI Driver queries its state and
// determines if is
// should exit or continue operating depending on that running state.
// This is related to the running state of the MI driver.
// Type: Overridden.
// Args: None.
// Return: None.
// Throws: None.
//--
void CMIDriver::SetExitApplicationFlag(const bool vbForceExit) {
if (vbForceExit) {
CMIUtilThreadLock lock(m_threadMutex);
m_bExitApp = true;
return;
}
// CODETAG_DEBUG_SESSION_RUNNING_PROG_RECEIVED_SIGINT_PAUSE_PROGRAM
// Did we receive a SIGINT from the client during a running debug program, if
// so then SIGINT is not to be taken as meaning kill the MI driver application
// but halt the inferior program being debugged instead
if (m_eCurrentDriverState == eDriverState_RunningDebugging) {
InterpretCommand("-exec-interrupt");
return;
}
m_bExitApp = true;
}
//++
//------------------------------------------------------------------------------------
// Details: Get the MI Driver's exit exit application flag.
// This is related to the running state of the MI driver.
// Type: Method.
// Args: None.
// Return: bool - True = MI Driver is shutting down, false = MI driver is
// running.
// Throws: None.
//--
bool CMIDriver::GetExitApplicationFlag() const { return m_bExitApp; }
//++
//------------------------------------------------------------------------------------
// Details: Get the current running state of the MI Driver.
// Type: Method.
// Args: None.
// Return: DriverState_e - The current running state of the application.
// Throws: None.
//--
CMIDriver::DriverState_e CMIDriver::GetCurrentDriverState() const {
return m_eCurrentDriverState;
}
//++
//------------------------------------------------------------------------------------
// Details: Set the current running state of the MI Driver to running and
// currently not in
// a debug session.
// Type: Method.
// Return: MIstatus::success - Functionality succeeded.
// MIstatus::failure - Functionality failed.
// Return: DriverState_e - The current running state of the application.
// Throws: None.
//--
bool CMIDriver::SetDriverStateRunningNotDebugging() {
// CODETAG_DEBUG_SESSION_RUNNING_PROG_RECEIVED_SIGINT_PAUSE_PROGRAM
if (m_eCurrentDriverState == eDriverState_RunningNotDebugging)
return MIstatus::success;
// Driver cannot be in the following states to set
// eDriverState_RunningNotDebugging
switch (m_eCurrentDriverState) {
case eDriverState_NotRunning:
case eDriverState_Initialising:
case eDriverState_ShuttingDown: {
SetErrorDescription(MIRSRC(IDS_DRIVER_ERR_DRIVER_STATE_ERROR));
return MIstatus::failure;
}
case eDriverState_RunningDebugging:
case eDriverState_RunningNotDebugging:
break;
case eDriverState_count:
SetErrorDescription(
CMIUtilString::Format(MIRSRC(IDS_CODE_ERR_INVALID_ENUMERATION_VALUE),
"SetDriverStateRunningNotDebugging()"));
return MIstatus::failure;
}
// Driver must be in this state to set eDriverState_RunningNotDebugging
if (m_eCurrentDriverState != eDriverState_RunningDebugging) {
SetErrorDescription(MIRSRC(IDS_DRIVER_ERR_DRIVER_STATE_ERROR));
return MIstatus::failure;
}
m_eCurrentDriverState = eDriverState_RunningNotDebugging;
return MIstatus::success;
}
//++
//------------------------------------------------------------------------------------
// Details: Set the current running state of the MI Driver to running and
// currently not in
// a debug session. The driver's state must in the state running and in
// a
// debug session to set this new state.
// Type: Method.
// Return: MIstatus::success - Functionality succeeded.
// MIstatus::failure - Functionality failed.
// Return: DriverState_e - The current running state of the application.
// Throws: None.
//--
bool CMIDriver::SetDriverStateRunningDebugging() {
// CODETAG_DEBUG_SESSION_RUNNING_PROG_RECEIVED_SIGINT_PAUSE_PROGRAM
if (m_eCurrentDriverState == eDriverState_RunningDebugging)
return MIstatus::success;
// Driver cannot be in the following states to set
// eDriverState_RunningDebugging
switch (m_eCurrentDriverState) {
case eDriverState_NotRunning:
case eDriverState_Initialising:
case eDriverState_ShuttingDown: {
SetErrorDescription(MIRSRC(IDS_DRIVER_ERR_DRIVER_STATE_ERROR));
return MIstatus::failure;
}
case eDriverState_RunningDebugging:
case eDriverState_RunningNotDebugging:
break;
case eDriverState_count:
SetErrorDescription(
CMIUtilString::Format(MIRSRC(IDS_CODE_ERR_INVALID_ENUMERATION_VALUE),
"SetDriverStateRunningDebugging()"));
return MIstatus::failure;
}
// Driver must be in this state to set eDriverState_RunningDebugging
if (m_eCurrentDriverState != eDriverState_RunningNotDebugging) {
SetErrorDescription(MIRSRC(IDS_DRIVER_ERR_DRIVER_STATE_ERROR));
return MIstatus::failure;
}
m_eCurrentDriverState = eDriverState_RunningDebugging;
return MIstatus::success;
}
//++
//------------------------------------------------------------------------------------
// Details: Prepare the client IDE so it will start working/communicating with
// *this MI
// driver.
// Type: Method.
// Args: None.
// Return: MIstatus::success - Functionality succeeded.
// MIstatus::failure - Functionality failed.
// Throws: None.
//--
bool CMIDriver::InitClientIDEToMIDriver() const {
// Put other IDE init functions here
return InitClientIDEEclipse();
}
//++
//------------------------------------------------------------------------------------
// Details: The IDE Eclipse when debugging locally expects "(gdb)\n" character
// sequence otherwise it refuses to communicate and times out. This
// should be
// sent to Eclipse before anything else.
// Type: Method.
// Args: None.
// Return: MIstatus::success - Functionality succeeded.
// MIstatus::failure - Functionality failed.
// Throws: None.
//--
bool CMIDriver::InitClientIDEEclipse() const {
return CMICmnStreamStdout::WritePrompt();
}
//++
//------------------------------------------------------------------------------------
// Details: Ask *this driver whether it found an executable in the MI Driver's
// list of
// arguments which to open and debug. If so instigate commands to set
// up a debug
// session for that executable.
// Type: Method.
// Args: None.
// Return: bool - True = True = Yes executable given as one of the parameters
// to the MI
// Driver.
// False = not found.
// Throws: None.
//--
bool CMIDriver::HaveExecutableFileNamePathOnCmdLine() const {
return m_bHaveExecutableFileNamePathOnCmdLine;
}
//++
//------------------------------------------------------------------------------------
// Details: Retrieve from *this driver executable file name path to start a
// debug session
// with (if present see HaveExecutableFileNamePathOnCmdLine()).
// Type: Method.
// Args: None.
// Return: CMIUtilString & - Executeable file name path or empty string.
// Throws: None.
//--
const CMIUtilString &CMIDriver::GetExecutableFileNamePathOnCmdLine() const {
return m_strCmdLineArgExecuteableFileNamePath;
}
//++
//------------------------------------------------------------------------------------
// Details: Execute commands (by injecting them into the stdin line queue
// container) and
// other code to set up the MI Driver such that is can take the
// executable
// argument passed on the command and create a debug session for it.
// Type: Method.
// Args: None.
// Return: MIstatus::success - Functionality succeeded.
// MIstatus::failure - Functionality failed.
// Throws: None.
//--
bool CMIDriver::LocalDebugSessionStartupExecuteCommands() {
const CMIUtilString strCmd(CMIUtilString::Format(
"-file-exec-and-symbols \"%s\"",
m_strCmdLineArgExecuteableFileNamePath.AddSlashes().c_str()));
bool bOk = CMICmnStreamStdout::TextToStdout(strCmd);
bOk = bOk && InterpretCommand(strCmd);
bOk = bOk && CMICmnStreamStdout::WritePrompt();
return bOk;
}
//++
//------------------------------------------------------------------------------------
// Details: Set the MI Driver into "its debugging an executable passed as an
// argument"
// mode as against running via a client like Eclipse.
// Type: Method.
// Args: None.
// Return: None.
// Throws: None.
//--
void CMIDriver::SetDriverDebuggingArgExecutable() {
m_bDriverDebuggingArgExecutable = true;
}
//++
//------------------------------------------------------------------------------------
// Details: Retrieve the MI Driver state indicating if it is operating in "its
// debugging
// an executable passed as an argument" mode as against running via a
// client
// like Eclipse.
// Type: Method.
// Args: None.
// Return: None.
// Throws: None.
//--
bool CMIDriver::IsDriverDebuggingArgExecutable() const {
return m_bDriverDebuggingArgExecutable;
}
//++
//------------------------------------------------------------------------------------
// Details: Execute commands from command source file in specified mode, and
// set exit-flag if needed.
// Type: Method.
// Args: vbAsyncMode - (R) True = execute commands in asynchronous
// mode, false = otherwise.
// Return: MIstatus::success - Function succeeded.
// MIstatus::failure - Function failed.
// Throws: None.
//--
bool CMIDriver::ExecuteCommandFile(const bool vbAsyncMode) {
std::ifstream ifsStartScript(m_strCmdLineArgCommandFileNamePath.c_str());
if (!ifsStartScript.is_open()) {
const CMIUtilString errMsg(
CMIUtilString::Format(MIRSRC(IDS_UTIL_FILE_ERR_OPENING_FILE_UNKNOWN),
m_strCmdLineArgCommandFileNamePath.c_str()));
SetErrorDescription(errMsg.c_str());
const bool bForceExit = true;
SetExitApplicationFlag(bForceExit);
return MIstatus::failure;
}
// Switch lldb to synchronous mode
CMICmnLLDBDebugSessionInfo &rSessionInfo(
CMICmnLLDBDebugSessionInfo::Instance());
const bool bAsyncSetting = rSessionInfo.GetDebugger().GetAsync();
rSessionInfo.GetDebugger().SetAsync(vbAsyncMode);
// Execute commands from file
bool bOk = MIstatus::success;
CMIUtilString strCommand;
while (!m_bExitApp && std::getline(ifsStartScript, strCommand)) {
// Print command
bOk = CMICmnStreamStdout::TextToStdout(strCommand);
// Skip if it's a comment or empty line
if (strCommand.empty() || strCommand[0] == '#')
continue;
// Execute if no error
if (bOk) {
CMIUtilThreadLock lock(rSessionInfo.GetSessionMutex());
bOk = InterpretCommand(strCommand);
}
// Draw the prompt after command will be executed (if enabled)
bOk = bOk && CMICmnStreamStdout::WritePrompt();
// Exit if there is an error
if (!bOk) {
const bool bForceExit = true;
SetExitApplicationFlag(bForceExit);
break;
}
// Wait while the handler thread handles incoming events
CMICmnLLDBDebugger::Instance().WaitForHandleEvent();
}
// Switch lldb back to initial mode
rSessionInfo.GetDebugger().SetAsync(bAsyncSetting);
return bOk;
}
//++
//------------------------------------------------------------------------------------
// Details: Gets called when lldb-mi gets a signal. Stops the process if it was
// SIGINT.
//
// Type: Method.
// Args: signal that was delivered
// Return: None.
// Throws: None.
//--
void CMIDriver::DeliverSignal(int signal) {
if (signal == SIGINT &&
(m_eCurrentDriverState == eDriverState_RunningDebugging))
InterpretCommand("-exec-interrupt");
}