llvm-project/lldb/tools/lldb-mi/MIDriver.cpp

1337 lines
47 KiB
C++
Raw Normal View History

//===-- MIDriver.cpp --------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//++
// File: MIDriver.cpp
//
// Overview: CMIDriver implementation.
//
// Environment: Compilers: Visual C++ 12.
// gcc (Ubuntu/Linaro 4.8.1-10ubuntu9) 4.8.1
// Libraries: See MIReadmetxt.
//
// Copyright: None.
//--
// Third party headers:
#include <stdarg.h> // va_list, va_start, var_end
#include <iostream>
#include "lldb/API/SBError.h"
// In-house headers:
#include "Driver.h"
#include "MIDriver.h"
#include "MICmnResources.h"
#include "MICmnLog.h"
#include "MICmdMgr.h"
#include "MICmnLLDBDebugger.h"
#include "MICmnMIResultRecord.h"
#include "MICmnMIValueConst.h"
#include "MICmnThreadMgrStd.h"
#include "MIUtilDebug.h"
#include "MIUtilSingletonHelper.h"
#include "MICmnStreamStdout.h"
#include "MICmnStreamStderr.h"
#include "MICmdArgValFile.h"
#include "MICmdArgValString.h"
#include "MICmnConfig.h"
#include "MICmnLLDBDebugSessionInfo.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(void)
: 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)
{
}
//++ ------------------------------------------------------------------------------------
// Details: CMIDriver destructor.
// Type: Overridden.
// Args: None.
// Return: None.
// Throws: None.
//--
CMIDriver::~CMIDriver(void)
{
}
//++ ------------------------------------------------------------------------------------
// 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 hanled 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 hanled the command).
// Type: Method.
// Args: None.
// Return: bool - True = yes fall through, false = do not pass on command.
// Throws: None.
//--
bool
CMIDriver::GetEnableFallThru(void) 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(void) 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(void) 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(void) 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(void)
{
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);
#if MICONFIG_COMPILE_MIDRIVER_WITH_LLDBDRIVER
CMIDriverMgr &rDrvMgr = CMIDriverMgr::Instance();
bOk = bOk && rDrvMgr.RegisterDriver(*g_driver, "LLDB driver"); // Will be pass thru driver
if (bOk)
{
bOk = SetEnableFallThru(false); // This is intentional at this time - yet to be fully implemented
bOk = bOk && SetDriverToFallThruTo(*g_driver);
CMIUtilString strOtherDrvErrMsg;
if (bOk && GetEnableFallThru() && !g_driver->MISetup(strOtherDrvErrMsg))
{
bOk = false;
errMsg = CMIUtilString::Format(MIRSRC(IDS_MI_INIT_ERR_FALLTHRUDRIVER), strOtherDrvErrMsg.c_str());
}
}
#endif // MICONFIG_COMPILE_MIDRIVER_WITH_LLDBDRIVER
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(void)
{
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(void)
{
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(void)
{
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(void) 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(void) const
{
return GetErrorDescription();
}
//++ ------------------------------------------------------------------------------------
// Details: Call *this driver to resize the console window.
// Type: Overridden.
// Args: vTermWidth - (R) New window column size.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
void
CMIDriver::DoResizeWindow(const uint32_t vTermWidth)
{
GetTheDebugger().SetTerminalWidth(vTermWidth);
}
//++ ------------------------------------------------------------------------------------
// 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(void)
{
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
// The application's options --interpreter and --executable in code act very similar.
// The --executable is necessary to differentiate whither the MI Driver is being
// using by a client i.e. 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 is being called the command line and that the executable argument is indeed
// a specified executable an so actions commands to set up the executable for a
// debug session. Using --interpreter on the commnd line does not action additional
// commands to initialise a debug session and so be able to launch the process.
// 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 the executable
for (MIint i = argc - 1; i > 0; i--)
{
const CMIUtilString strArg(argv[i]);
const CMICmdArgValFile argFile;
if (argFile.IsFilePath(strArg) || CMICmdArgValString(true, false, true).IsStringArg(strArg))
{
bHaveExecutableFileNamePath = true;
m_strCmdLineArgExecuteableFileNamePath = argFile.GetFileNamePath(strArg);
m_bHaveExecutableFileNamePathOnCmdLine = true;
}
// This argument is also check for in CMIDriverMgr::ParseArgs()
if (0 == strArg.compare("--executable")) // Used to specify that there is executable argument also on the command line
{ // See fn description.
bHaveExecutableLongOption = true;
}
}
}
if (bHaveExecutableFileNamePath && bHaveExecutableLongOption)
{
// CODETAG_CMDLINE_ARG_EXECUTABLE_DEBUG_SESSION
#if MICONFIG_ENABLE_MI_DRIVER_MI_MODE_CMDLINE_ARG_EXECUTABLE_DEBUG_SESSION
SetDriverDebuggingArgExecutable();
#else
vwbExiting = true;
errStatus.SetErrorString(MIRSRC(IDS_DRIVER_ERR_LOCAL_DEBUG_NOT_IMPL));
#endif // MICONFIG_ENABLE_MI_DRIVER_MI_MODE_CMDLINE_ARG_EXECUTABLE_DEBUG_SESSION
}
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(void) const
{
return true;
}
//++ ------------------------------------------------------------------------------------
// Details: Callback function for monitoring stream stdin object. Part of the visitor
// pattern.
// This function is called by the CMICmnStreamStdin::CThreadStdin
// "stdin monitor" thread (ID).
// Type: Overridden.
// Args: vStdInBuffer - (R) Copy of the current stdin line data.
// vrbYesExit - (RW) True = yes exit stdin monitoring, false = continue monitor.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool
CMIDriver::ReadLine(const CMIUtilString &vStdInBuffer, bool &vrwbYesExit)
{
// For debugging. Update prompt show stdin is working
// printf( "%s\n", vStdInBuffer.c_str() );
// fflush( stdout );
// Special case look for the quit command here so stop monitoring stdin stream
// So we do not go back to fgetc() and wait and hang thread on exit
if (vStdInBuffer == "quit")
vrwbYesExit = true;
// 1. Put new line in the queue container by stdin monitor thread
// 2. Then *this driver calls ReadStdinLineQueue() when ready to read the queue in its
// own thread
const bool bOk = QueueMICommand(vStdInBuffer);
// Check to see if the *this driver is shutting down (exit application)
if (!vrwbYesExit)
vrwbYesExit = m_bDriverIsExiting;
return bOk;
}
//++ ------------------------------------------------------------------------------------
// 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(void)
{
bool bOk = MIstatus::success;
// Grab the thread manager
CMICmnThreadMgrStd &rThreadMgr = CMICmnThreadMgrStd::Instance();
// Start the stdin thread
bOk &= m_rStdin.SetVisitor(*this);
if (bOk && !rThreadMgr.ThreadStart<CMICmnStreamStdin>(m_rStdin))
{
const CMIUtilString errMsg = CMIUtilString::Format(MIRSRC(IDS_THREADMGR_ERR_THREAD_FAIL_CREATE),
CMICmnThreadMgrStd::Instance().GetErrorDescription().c_str());
SetErrorDescriptionn(errMsg);
return MIstatus::failure;
}
// 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());
SetErrorDescriptionn(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(void)
{
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(void)
{
if (!InitClientIDEToMIDriver()) // Init Eclipse IDE
{
SetErrorDescriptionn(MIRSRC(IDS_MI_INIT_ERR_CLIENT_USING_DRIVER));
return MIstatus::failure;
}
if (!StartWorkerThreads())
return MIstatus::failure;
// App is not quitting currently
m_bExitApp = false;
// CODETAG_CMDLINE_ARG_EXECUTABLE_DEBUG_SESSION
#if MICONFIG_ENABLE_MI_DRIVER_MI_MODE_CMDLINE_ARG_EXECUTABLE_DEBUG_SESSION
if (HaveExecutableFileNamePathOnCmdLine())
{
if (!LocalDebugSessionStartupInjectCommands())
{
SetErrorDescription(MIRSRC(IDS_MI_INIT_ERR_LOCAL_DEBUG_SESSION));
return MIstatus::failure;
}
}
#endif // MICONFIG_ENABLE_MI_DRIVER_MI_MODE_CMDLINE_ARG_EXECUTABLE_DEBUG_SESSION
// While the app is active
while (!m_bExitApp)
{
// Poll stdin queue and dispatch
if (!ReadStdinLineQueue())
{
// Something went wrong
break;
}
}
// Signal that the application is shutting down
DoAppQuit();
// Close and wait for the workers to stop
StopWorkerThreads();
// Ensure that a new line is sent as the last act of the dying driver
m_rStdOut.WriteMIResponse("\n", false);
return MIstatus::success;
}
//++ ------------------------------------------------------------------------------------
// Details: *this driver sits and waits for input to the stdin line queue shared by *this
// driver and the stdin monitor thread, it queues, *this reads, interprets and
// reacts.
// 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::ReadStdinLineQueue(void)
{
// True when queue contains input
bool bHaveInput = false;
// Stores the current input line
CMIUtilString lineText;
{
// Lock while we access the queue
CMIUtilThreadLock lock(m_threadMutex);
if (!m_queueStdinLine.empty())
{
lineText = m_queueStdinLine.front();
m_queueStdinLine.pop();
bHaveInput = !lineText.empty();
}
}
// Process while we have input
if (bHaveInput)
{
if (lineText == "quit")
{
// We want to be exiting when receiving a quit command
m_bExitApp = true;
return MIstatus::success;
}
// Process the command
bool bOk = false;
{
// Lock Mutex before processing commands so that we don't disturb an event
// that is being processed.
CMIUtilThreadLock lock(CMICmnLLDBDebugSessionInfo::Instance().GetSessionMutex());
bOk = InterpretCommand(lineText);
}
// Draw prompt if desired
if (bOk && m_rStdin.GetEnablePrompt())
m_rStdOut.WriteMIResponse(m_rStdin.GetPrompt());
// Input has been processed
bHaveInput = false;
}
else
{
// Give resources back to the OS
const std::chrono::milliseconds time(1);
std::this_thread::sleep_for(time);
}
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(void)
{
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 MIchar * pName = pOtherDriver->GetDriverName().c_str();
// const MIchar * 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(void) 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(void) 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) Error 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(void) 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(void) 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(void) 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(void) const
{
return m_strDriverId;
}
//++ ------------------------------------------------------------------------------------
// Details: Inject a command into the command processing system to be interpreted as a
// command read from stdin. The text representing the command is also written
// out to stdout as the command did not come from via stdin.
// Type: Method.
// Args: vMICmd - (R) Text data representing a possible command.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool
CMIDriver::InjectMICommand(const CMIUtilString &vMICmd)
{
const bool bOk = m_rStdOut.WriteMIResponse(vMICmd);
return bOk && QueueMICommand(vMICmd);
}
//++ ------------------------------------------------------------------------------------
// Details: Add a new command candidate to the command queue to be processed by the
// command system.
// Type: Method.
// Args: vMICmd - (R) Text data representing a possible command.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool
CMIDriver::QueueMICommand(const CMIUtilString &vMICmd)
{
CMIUtilThreadLock lock(m_threadMutex);
m_queueStdinLine.push(vMICmd);
return MIstatus::success;
}
//++ ------------------------------------------------------------------------------------
// 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)
{
bool bCmdYesValid = false;
bool bOk = InterpretCommandThisDriver(vTextLine, bCmdYesValid);
if (bOk && !bCmdYesValid)
bOk = InterpretCommandFallThruDriver(vTextLine, bCmdYesValid);
return bOk;
}
//++ ------------------------------------------------------------------------------------
// 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 invalid, false = command acted on.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool
CMIDriver::InterpretCommandThisDriver(const CMIUtilString &vTextLine, bool &vwbCmdYesValid)
{
vwbCmdYesValid = false;
bool bCmdNotInCmdFactor = false;
SMICmdData cmdData;
CMICmdMgr &rCmdMgr = CMICmdMgr::Instance();
if (!rCmdMgr.CmdInterpret(vTextLine, 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
// is been given to the driver to try and intepret.
if (vTextLine.at(0) == 27)
{
CMIUtilString logInput(MIRSRC(IDS_STDIN_INPUT_CTRL_CHARS));
for (MIuint i = 0; i < vTextLine.length(); i++)
{
logInput += CMIUtilString::Format("%d ", vTextLine.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), vTextLine.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);
m_rStdOut.WriteMIResponse(miResultRecord.GetString());
// Proceed to wait for or execute next command
return MIstatus::success;
}
//++ ------------------------------------------------------------------------------------
// 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;
m_rStdin.OnExitHandler();
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)
{
InjectMICommand("-exec-interrupt");
return;
}
m_bExitApp = true;
m_rStdin.OnExitHandler();
}
//++ ------------------------------------------------------------------------------------
// 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(void) 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(void) 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(void)
{
// 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(void)
{
// 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(void) 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(void) const
{
std::cout << "(gdb)" << std::endl;
return MIstatus::success;
2014-06-27 10:42:12 +08:00
}
//++ ------------------------------------------------------------------------------------
// 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(void) 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(void) 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::LocalDebugSessionStartupInjectCommands(void)
{
const CMIUtilString strCmd(CMIUtilString::Format("-file-exec-and-symbols %s", m_strCmdLineArgExecuteableFileNamePath.c_str()));
return InjectMICommand(strCmd);
}
//++ ------------------------------------------------------------------------------------
// 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(void)
{
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(void) const
{
return m_bDriverDebuggingArgExecutable;
}