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