diff options
Diffstat (limited to 'tools/lldb-mi/MIDriver.cpp')
-rw-r--r-- | tools/lldb-mi/MIDriver.cpp | 1276 |
1 files changed, 1276 insertions, 0 deletions
diff --git a/tools/lldb-mi/MIDriver.cpp b/tools/lldb-mi/MIDriver.cpp new file mode 100644 index 000000000000..759d54a5364d --- /dev/null +++ b/tools/lldb-mi/MIDriver.cpp @@ -0,0 +1,1276 @@ +//===-- 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" + +// 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 + const bool 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; + 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; +} + +//++ ------------------------------------------------------------------------------------ +// 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: + default: + 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: + default: + 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; +} + +//++ ------------------------------------------------------------------------------------ +// 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; +} + |