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

1227 lines
44 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: 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 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 (!LocalDebugSessionStartupExecuteCommands())
{
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)
{
CMIUtilString errorText;
const MIchar *pCmd = m_rStdin.ReadLine (errorText);
if (pCmd != nullptr)
{
CMIUtilString lineText(pCmd);
if (!lineText.empty ())
{
if (lineText == "quit")
{
// We want to be exiting when receiving a quit command
m_bExitApp = true;
break;
}
bool bOk = false;
{
// 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
if (bOk && m_rStdin.GetEnablePrompt())
m_rStdOut.WriteMIResponse(m_rStdin.GetPrompt());
}
}
}
// 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: 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: 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);
bool bOk = m_rStdOut.WriteMIResponse(miResultRecord.GetString());
if (bOk && m_rStdin.GetEnablePrompt())
bOk = m_rStdOut.WriteMIResponse(m_rStdin.GetPrompt());
// 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(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::LocalDebugSessionStartupExecuteCommands(void)
{
Fix handling of double quotes (MI) Summary: * Clean CMICmdArgValString::Validate: now it's based on CMIUtilString::SplitConsiderQuotes method: A bit of introduction: # Command line is wrapped into CMICmdArgContext. # CMICmdArgSet is a set of arguments to be parsed. This class contains CMICmdArgContext as a private member. # MI command is class which is inhereted from CMICmdBase. It contains CMICmdArgSet as a private member. When command is executed CMICmdBase::ParseArgs() is called. This method adds args for parsing using CMICmdArgSet::Add(). Then CMICmdBase::ParseValidateCmdOptions() is called, which calls CMICmdArgSet::Validate(). Then it gets a number of arguments (using SplitConsiderQuotes().array_length) and for each arguments registered in ParseArgs() tries to validate it using CMICmdArgValBase::Validate(). Every user commands parses this string again (first time it was made in SplitConsiderQuotes) and in case of CMICmdArgValString it was made incorrectly. It searches the first and last quotes (but it should be first and next after first). Besides, it was splitted into 4 cases. I'm just using SplitConsiderQuotes directly, and I don't split them by hand again. Actually, I think we should do so in every CMICmdArgVal_XXX::Validate() method. * Enable MiInterpreterExecTestCase.test_lldbmi_target_create test * Fix MiExecTestCase.test_lldbmi_exec_arguments_set test All tests pass on OS X. Reviewers: abidh, emaste, zturner, clayborg Reviewed By: clayborg Subscribers: lldb-commits, zturner, emaste, clayborg, abidh Differential Revision: http://reviews.llvm.org/D7860 llvm-svn: 230654
2015-02-27 02:21:22 +08:00
const CMIUtilString strCmd(CMIUtilString::Format("-file-exec-and-symbols \"%s\"", m_strCmdLineArgExecuteableFileNamePath.AddSlashes().c_str()));
bool bOk = CMICmnStreamStdout::TextToStdout(strCmd);
bOk = bOk && InterpretCommand(strCmd);
if (bOk && m_rStdin.GetEnablePrompt())
bOk = m_rStdOut.WriteMIResponse(m_rStdin.GetPrompt());
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(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;
}
//++ ------------------------------------------------------------------------------------
// 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");
}