//===-- MICmdCmdMiscellanous.cpp --------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// //++ // File: MICmdCmdMiscellanous.cpp // // Overview: CMICmdCmdGdbExit implementation. // CMICmdCmdListThreadGroups implementation. // CMICmdCmdInterpreterExec implementation. // CMICmdCmdInferiorTtySet implementation. // // Environment: Compilers: Visual C++ 12. // gcc (Ubuntu/Linaro 4.8.1-10ubuntu9) 4.8.1 // Libraries: See MIReadmetxt. // // Copyright: None. //-- // Third Party Headers: #include #include // In-house headers: #include "MICmdCmdMiscellanous.h" #include "MICmnMIResultRecord.h" #include "MICmnMIValueConst.h" #include "MICmnMIOutOfBandRecord.h" #include "MICmnLLDBDebugger.h" #include "MICmnLLDBDebugSessionInfo.h" #include "MIDriverBase.h" #include "MICmdArgValFile.h" #include "MICmdArgValNumber.h" #include "MICmdArgValString.h" #include "MICmdArgValThreadGrp.h" #include "MICmdArgValOptionLong.h" #include "MICmdArgValOptionShort.h" #include "MICmdArgValListOfN.h" #include "MICmnStreamStdout.h" #include "MICmnStreamStderr.h" //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdGdbExit constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdGdbExit::CMICmdCmdGdbExit( void ) { // Command factory matches this name with that received from the stdin stream m_strMiCmd = "gdb-exit"; // Required by the CMICmdFactory when registering *this command m_pSelfCreatorFn = &CMICmdCmdGdbExit::CreateSelf; } //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdGdbExit destructor. // Type: Overrideable. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdGdbExit::~CMICmdCmdGdbExit( void ) { } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command does work in this function. // The command is likely to communicate with the LLDB SBDebugger in here. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdGdbExit::Execute( void ) { CMICmnLLDBDebugger::Instance().GetDriver().SetExitApplicationFlag( true ); const lldb::SBError sbErr = m_rLLDBDebugSessionInfo.m_lldbProcess.Detach(); // Do not check for sbErr.Fail() here, m_lldbProcess is likely !IsValid() return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command prepares a MI Record Result // for the work carried out in the Execute(). // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdGdbExit::Acknowledge( void ) { const CMICmnMIResultRecord miRecordResult( m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Exit ); m_miResultRecord = miRecordResult; // Prod the client i.e. Eclipse with out-of-band results to help it 'continue' because it is using LLDB debugger // Give the client '=thread-group-exited,id="i1"' m_bHasResultRecordExtra = true; const CMICmnMIValueConst miValueConst2( "i1" ); const CMICmnMIValueResult miValueResult2( "id", miValueConst2 ); const CMICmnMIOutOfBandRecord miOutOfBand( CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupExited, miValueResult2 ); m_miResultRecordExtra = miOutOfBand.GetString(); return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Required by the CMICmdFactory when registering *this command. The factory // calls this function to create an instance of *this command. // Type: Static method. // Args: None. // Return: CMICmdBase * - Pointer to a new command. // Throws: None. //-- CMICmdBase * CMICmdCmdGdbExit::CreateSelf( void ) { return new CMICmdCmdGdbExit(); } //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdListThreadGroups constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdListThreadGroups::CMICmdCmdListThreadGroups( void ) : m_bIsI1( false ) , m_bHaveArgOption( false ) , m_bHaveArgRecurse( false ) , m_constStrArgNamedAvailable( "available" ) , m_constStrArgNamedRecurse( "recurse" ) , m_constStrArgNamedGroup( "group" ) , m_constStrArgNamedThreadGroup( "i1" ) { // Command factory matches this name with that received from the stdin stream m_strMiCmd = "list-thread-groups"; // Required by the CMICmdFactory when registering *this command m_pSelfCreatorFn = &CMICmdCmdListThreadGroups::CreateSelf; } //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdListThreadGroups destructor. // Type: Overrideable. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdListThreadGroups::~CMICmdCmdListThreadGroups( void ) { m_vecMIValueTuple.clear(); } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The parses the command line options // arguments to extract values for each of those arguments. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdListThreadGroups::ParseArgs( void ) { bool bOk = m_setCmdArgs.Add( *(new CMICmdArgValOptionLong( m_constStrArgNamedAvailable, false, true ) ) ); bOk = bOk && m_setCmdArgs.Add( *(new CMICmdArgValOptionLong( m_constStrArgNamedRecurse, false, true, CMICmdArgValListBase::eArgValType_Number, 1 ) ) ); bOk = bOk && m_setCmdArgs.Add( *(new CMICmdArgValListOfN( m_constStrArgNamedGroup, false, true, CMICmdArgValListBase::eArgValType_Number ) ) ); bOk = bOk && m_setCmdArgs.Add( *(new CMICmdArgValThreadGrp( m_constStrArgNamedThreadGroup, false, true ) ) ); return (bOk && ParseValidateCmdOptions() ); } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command does work in this function. // The command is likely to communicate with the LLDB SBDebugger in here. // Synopis: -list-thread-groups [ --available ] [ --recurse 1 ] [ group ... ] // This command does not follow the MI documentation exactly. Has an extra // argument "i1" to handle. // Ref: http://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Miscellaneous-Commands.html#GDB_002fMI-Miscellaneous-Commands // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdListThreadGroups::Execute( void ) { if( m_setCmdArgs.IsArgContextEmpty() ) // No options so "top level thread groups" return MIstatus::success; CMICMDBASE_GETOPTION( pArgAvailable, OptionLong, m_constStrArgNamedAvailable ); CMICMDBASE_GETOPTION( pArgRecurse, OptionLong, m_constStrArgNamedRecurse ); CMICMDBASE_GETOPTION( pArgThreadGroup, ThreadGrp, m_constStrArgNamedThreadGroup ); // Got some options so "threads" if( pArgAvailable->GetFound() ) { if( pArgRecurse->GetFound() ) { m_bHaveArgRecurse = true; return MIstatus::success; } m_bHaveArgOption = true; return MIstatus::success; } // "i1" as first argument (pos 0 of possible arg) if( !pArgThreadGroup->GetFound() ) return MIstatus::success; m_bIsI1 = true; CMICmnLLDBDebugSessionInfo & rSessionInfo( CMICmnLLDBDebugSessionInfo::Instance() ); lldb::SBProcess & rProcess = rSessionInfo.m_lldbProcess; // Note do not check for rProcess is IsValid(), continue m_vecMIValueTuple.clear(); const MIuint nThreads = rProcess.GetNumThreads(); for( MIuint i = 0; i < nThreads; i++ ) { // GetThreadAtIndex() uses a base 0 index // GetThreadByIndexID() uses a base 1 index lldb::SBThread thread = rProcess.GetThreadAtIndex( i ); if( thread.IsValid() ) { CMICmnMIValueTuple miTuple; if( !rSessionInfo.MIResponseFormThreadInfo2( m_cmdData, thread, miTuple ) ) return MIstatus::failure; m_vecMIValueTuple.push_back( miTuple ); } } return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command prepares a MI Record Result // for the work carried out in the Execute(). // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdListThreadGroups::Acknowledge( void ) { if( m_bHaveArgOption ) { if( m_bHaveArgRecurse ) { const CMICmnMIValueConst miValueConst( MIRSRC( IDS_WORD_NOT_IMPLEMENTED_BRKTS ) ); const CMICmnMIValueResult miValueResult( "msg", miValueConst ); const CMICmnMIResultRecord miRecordResult( m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, miValueResult ); m_miResultRecord = miRecordResult; return MIstatus::success; } const CMICmnMIValueConst miValueConst1( "i1" ); const CMICmnMIValueResult miValueResult1( "id", miValueConst1 ); CMICmnMIValueTuple miTuple( miValueResult1 ); const CMICmnMIValueConst miValueConst2( "process" ); const CMICmnMIValueResult miValueResult2( "type", miValueConst2 ); miTuple.Add( miValueResult2 ); CMICmnLLDBDebugSessionInfo & rSessionInfo( CMICmnLLDBDebugSessionInfo::Instance() ); if (rSessionInfo.m_lldbProcess.IsValid ()) { const lldb::pid_t pid = rSessionInfo.m_lldbProcess.GetProcessID(); const CMIUtilString strPid( CMIUtilString::Format( "%lld", pid ) ); const CMICmnMIValueConst miValueConst3( strPid ); const CMICmnMIValueResult miValueResult3( "pid", miValueConst3 ); miTuple.Add( miValueResult3 ); } const CMICmnMIValueConst miValueConst4( MIRSRC( IDS_WORD_NOT_IMPLEMENTED_BRKTS ) ); const CMICmnMIValueResult miValueResult4( "num_children", miValueConst4 ); miTuple.Add( miValueResult4 ); const CMICmnMIValueConst miValueConst5( MIRSRC( IDS_WORD_NOT_IMPLEMENTED_BRKTS ) ); const CMICmnMIValueResult miValueResult5( "cores", miValueConst5 ); miTuple.Add( miValueResult5 ); const CMICmnMIValueList miValueList( miTuple ); const CMICmnMIValueResult miValueResult6( "groups", miValueList ); const CMICmnMIResultRecord miRecordResult( m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, miValueResult6 ); m_miResultRecord = miRecordResult; return MIstatus::success; } if( !m_bIsI1 ) { const CMICmnMIValueConst miValueConst1( "i1" ); const CMICmnMIValueResult miValueResult1( "id", miValueConst1 ); CMICmnMIValueTuple miTuple( miValueResult1 ); const CMICmnMIValueConst miValueConst2( "process" ); const CMICmnMIValueResult miValueResult2( "type", miValueConst2 ); miTuple.Add( miValueResult2 ); CMICmnLLDBDebugSessionInfo & rSessionInfo( CMICmnLLDBDebugSessionInfo::Instance() ); if (rSessionInfo.m_lldbProcess.IsValid ()) { const lldb::pid_t pid = rSessionInfo.m_lldbProcess.GetProcessID(); const CMIUtilString strPid( CMIUtilString::Format( "%lld", pid ) ); const CMICmnMIValueConst miValueConst3( strPid ); const CMICmnMIValueResult miValueResult3( "pid", miValueConst3 ); miTuple.Add( miValueResult3 ); } if (rSessionInfo.m_lldbTarget.IsValid ()) { lldb::SBTarget & rTrgt = rSessionInfo.m_lldbTarget; const MIchar * pDir = rTrgt.GetExecutable().GetDirectory(); const MIchar * pFileName = rTrgt.GetExecutable().GetFilename(); const CMIUtilString strFile( CMIUtilString::Format( "%s/%s", pDir, pFileName ) ); const CMICmnMIValueConst miValueConst4( strFile ); const CMICmnMIValueResult miValueResult4( "executable", miValueConst4 ); miTuple.Add( miValueResult4 ); } const CMICmnMIValueList miValueList( miTuple ); const CMICmnMIValueResult miValueResult5( "groups", miValueList ); const CMICmnMIResultRecord miRecordResult( m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, miValueResult5 ); m_miResultRecord = miRecordResult; return MIstatus::success; } // Build up a list of thread information from tuples VecMIValueTuple_t::const_iterator it = m_vecMIValueTuple.begin(); if( it == m_vecMIValueTuple.end() ) { const CMICmnMIValueConst miValueConst( "[]" ); const CMICmnMIValueResult miValueResult( "threads", miValueConst ); const CMICmnMIResultRecord miRecordResult( m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, miValueResult ); m_miResultRecord = miRecordResult; return MIstatus::success; } CMICmnMIValueList miValueList( *it ); ++it; while( it != m_vecMIValueTuple.end() ) { const CMICmnMIValueTuple & rTuple( *it ); miValueList.Add( rTuple ); // Next ++it; } const CMICmnMIValueResult miValueResult( "threads", miValueList ); const CMICmnMIResultRecord miRecordResult( m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, miValueResult ); m_miResultRecord = miRecordResult; return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Required by the CMICmdFactory when registering *this command. The factory // calls this function to create an instance of *this command. // Type: Static method. // Args: None. // Return: CMICmdBase * - Pointer to a new command. // Throws: None. //-- CMICmdBase * CMICmdCmdListThreadGroups::CreateSelf( void ) { return new CMICmdCmdListThreadGroups(); } //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdInterpreterExec constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdInterpreterExec::CMICmdCmdInterpreterExec( void ) : m_constStrArgNamedInterpreter( "intepreter" ) , m_constStrArgNamedCommand( "command" ) { // Command factory matches this name with that received from the stdin stream m_strMiCmd = "interpreter-exec"; // Required by the CMICmdFactory when registering *this command m_pSelfCreatorFn = &CMICmdCmdInterpreterExec::CreateSelf; } //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdInterpreterExec destructor. // Type: Overrideable. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdInterpreterExec::~CMICmdCmdInterpreterExec( void ) { } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The parses the command line options // arguments to extract values for each of those arguments. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdInterpreterExec::ParseArgs( void ) { bool bOk = m_setCmdArgs.Add( *(new CMICmdArgValString( m_constStrArgNamedInterpreter, true, true ) ) ); bOk = bOk && m_setCmdArgs.Add( *(new CMICmdArgValString( m_constStrArgNamedCommand, true, true, true ) ) ); return (bOk && ParseValidateCmdOptions() ); } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command does work in this function. // The command is likely to communicate with the LLDB SBDebugger in here. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdInterpreterExec::Execute( void ) { CMICMDBASE_GETOPTION( pArgInterpreter, String, m_constStrArgNamedInterpreter ); CMICMDBASE_GETOPTION( pArgCommand, String, m_constStrArgNamedCommand ); // Handle the interpreter parameter by do nothing on purpose (set to 'handled' in // the arg definition above) const CMIUtilString & rStrInterpreter( pArgInterpreter->GetValue() ); MIunused( rStrInterpreter ); const CMIUtilString & rStrCommand( pArgCommand->GetValue() ); CMICmnLLDBDebugSessionInfo & rSessionInfo( CMICmnLLDBDebugSessionInfo::Instance() ); const lldb::ReturnStatus rtn = rSessionInfo.m_rLldbDebugger.GetCommandInterpreter().HandleCommand( rStrCommand.c_str(), m_lldbResult, true ); MIunused( rtn ); return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command prepares a MI Record Result // for the work carried out in the Execute(). // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdInterpreterExec::Acknowledge( void ) { if( m_lldbResult.GetOutputSize() > 0 ) { CMIUtilString strMsg( m_lldbResult.GetOutput() ); strMsg = strMsg.StripCREndOfLine(); CMICmnStreamStdout::TextToStdout( strMsg ); // Send the LLDB result message to console so the user can see the result of the // command they typed. It is not necessary an error message. CMICmnStreamStderr::LLDBMsgToConsole( strMsg ); } if( m_lldbResult.GetErrorSize() > 0 ) { CMIUtilString strMsg( m_lldbResult.GetError() ); strMsg = strMsg.StripCREndOfLine(); CMICmnStreamStderr::LLDBMsgToConsole( strMsg ); // Send LLDB's error message to the MI Driver's Log file CMICmnStreamStdout::TextToStdout( strMsg ); } const CMICmnMIResultRecord miRecordResult( m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done ); m_miResultRecord = miRecordResult; return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Required by the CMICmdFactory when registering *this command. The factory // calls this function to create an instance of *this command. // Type: Static method. // Args: None. // Return: CMICmdBase * - Pointer to a new command. // Throws: None. //-- CMICmdBase * CMICmdCmdInterpreterExec::CreateSelf( void ) { return new CMICmdCmdInterpreterExec(); } //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdInferiorTtySet constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdInferiorTtySet::CMICmdCmdInferiorTtySet( void ) { // Command factory matches this name with that received from the stdin stream m_strMiCmd = "inferior-tty-set"; // Required by the CMICmdFactory when registering *this command m_pSelfCreatorFn = &CMICmdCmdInferiorTtySet::CreateSelf; } //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdInferiorTtySet destructor. // Type: Overrideable. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdInferiorTtySet::~CMICmdCmdInferiorTtySet( void ) { } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command does work in this function. // The command is likely to communicate with the LLDB SBDebugger in here. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdInferiorTtySet::Execute( void ) { // Do nothing return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command prepares a MI Record Result // for the work carried out in the Execute(). // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdInferiorTtySet::Acknowledge( void ) { const CMICmnMIResultRecord miRecordResult( m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done ); m_miResultRecord = miRecordResult; return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Required by the CMICmdFactory when registering *this command. The factory // calls this function to create an instance of *this command. // Type: Static method. // Args: None. // Return: CMICmdBase * - Pointer to a new command. // Throws: None. //-- CMICmdBase * CMICmdCmdInferiorTtySet::CreateSelf( void ) { return new CMICmdCmdInferiorTtySet(); }