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