//===-- MICmdInvoker.cpp ----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// //++ // File: MICmdInvoker.cpp // // Overview: CMICmdInvoker implementation. // // Environment: Compilers: Visual C++ 12. // gcc (Ubuntu/Linaro 4.8.1-10ubuntu9) 4.8.1 // Libraries: See MIReadmetxt. // // Copyright: None. //-- // In-house headers: #include "MICmdInvoker.h" #include "MICmdBase.h" #include "MICmdMgr.h" #include "MICmnLog.h" #include "MICmnStreamStdout.h" #include "MIDriver.h" //++ ------------------------------------------------------------------------------------ // Details: CMICmdInvoker constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmdInvoker::CMICmdInvoker( void ) : m_rStreamOut( CMICmnStreamStdout::Instance() ) { } //++ ------------------------------------------------------------------------------------ // Details: CMICmdInvoker destructor. // Type: Overridable. // Args: None. // Return: None. // Throws: None. //-- CMICmdInvoker::~CMICmdInvoker( void ) { Shutdown(); } //++ ------------------------------------------------------------------------------------ // Details: Initialize resources for *this Command Invoker. // Type: Method. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdInvoker::Initialize( void ) { m_clientUsageRefCnt++; if( m_bInitialized ) return MIstatus::success; m_bInitialized = true; return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Release resources for *this Stdin stream. // Type: Method. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdInvoker::Shutdown( void ) { if( --m_clientUsageRefCnt > 0 ) return MIstatus::success; if( !m_bInitialized ) return MIstatus::success; CmdDeleteAll(); m_bInitialized = false; return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Empty the map of invoked commands doing work. Command objects are deleted too. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- void CMICmdInvoker::CmdDeleteAll( void ) { CMICmdMgr & rMgr = CMICmdMgr::Instance(); MapCmdIdToCmd_t::const_iterator it = m_mapCmdIdToCmd.begin(); while( it != m_mapCmdIdToCmd.end() ) { const MIuint cmdId( (*it).first ); MIunused( cmdId ); CMICmdBase * pCmd = (*it).second; const CMIUtilString & rCmdName( pCmd->GetCmdData().strMiCmd ); MIunused( rCmdName ); rMgr.CmdDelete( pCmd->GetCmdData() ); // Next ++it; } m_mapCmdIdToCmd.clear(); } //++ ------------------------------------------------------------------------------------ // Details: Remove from the map of invoked commands doing work a command that has finished // its work. The command object is deleted too. // Type: Method. // Args: vId - (R) Command object's unique ID. // vbYesDeleteCmd - (R) True = Delete command object, false = delete via the Command Manager. // Return: None. // Throws: None. //-- bool CMICmdInvoker::CmdDelete( const MIuint vId, const bool vbYesDeleteCmd /*= false*/ ) { CMICmdMgr & rMgr = CMICmdMgr::Instance(); MapCmdIdToCmd_t::const_iterator it = m_mapCmdIdToCmd.find( vId ); if( it != m_mapCmdIdToCmd.end() ) { CMICmdBase * pCmd = (*it).second; if( vbYesDeleteCmd ) { // Via registered interest command manager callback *this object to delete the command m_mapCmdIdToCmd.erase( it ); delete pCmd; } else // Notify other interested object of this command's pending deletion rMgr.CmdDelete( pCmd->GetCmdData() ); } if( m_mapCmdIdToCmd.empty() ) rMgr.CmdUnregisterForDeleteNotification( *this ); return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Add to the map of invoked commands doing work a command that is about to // start to do work. // Type: Method. // Args: vCmd - (R) Command object. // Return: None. // Throws: None. //-- bool CMICmdInvoker::CmdAdd( const CMICmdBase & vCmd ) { if( m_mapCmdIdToCmd.empty() ) { CMICmdMgr & rMgr = CMICmdMgr::Instance(); rMgr.CmdRegisterForDeleteNotification( *this ); } const MIuint & cmdId( vCmd.GetCmdData().id ); MapCmdIdToCmd_t::const_iterator it = m_mapCmdIdToCmd.find( cmdId ); if( it != m_mapCmdIdToCmd.end() ) return MIstatus::success; MapPairCmdIdToCmd_t pr( cmdId, const_cast< CMICmdBase *>( &vCmd )); m_mapCmdIdToCmd.insert( pr ); return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Having previously had the potential command validated and found valid now // get the command executed. // If the Functionalityity returns MIstatus::failure call GetErrorDescription(). // This function is used by the application's main thread. // Type: Method. // Args: vCmd - (RW) Command object. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmdInvoker::CmdExecute( CMICmdBase & vCmd ) { bool bOk = CmdAdd( vCmd ); if( bOk && !vCmd.ParseArgs() ) { // Report command execution failed const SMICmdData cmdData( vCmd.GetCmdData() ); CmdStdout( cmdData ); CmdCauseAppExit( vCmd ); CmdDelete( cmdData.id ); // Proceed to wait or execute next command return MIstatus::success; } if( bOk && !vCmd.Execute() ) { // Report command execution failed const SMICmdData cmdData( vCmd.GetCmdData() ); CmdStdout( cmdData ); CmdCauseAppExit( vCmd ); CmdDelete( cmdData.id ); // Proceed to wait or execute next command return MIstatus::success; } bOk = CmdExecuteFinished( vCmd ); return bOk; } //++ ------------------------------------------------------------------------------------ // Details: Called when a command has finished its Execution() work either synchronously // because the command executed was the type a non event type or asynchronoulsy // via the command's callback (because of an SB Listener event). Needs to be called // so that *this invoker call do some house keeping and then proceed to call // the command's Acknowledge() function. // Type: Method. // Args: vCmd - (R) Command object. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Throws: None. //-- bool CMICmdInvoker::CmdExecuteFinished( CMICmdBase & vCmd ) { // Command finished now get the command to gather it's information and form the MI // Result record if( !vCmd.Acknowledge() ) { // Report command acknowledge functionality failed const SMICmdData cmdData( vCmd.GetCmdData() ); CmdStdout( cmdData ); CmdCauseAppExit( vCmd ); CmdDelete( cmdData.id ); // Proceed to wait or execute next command return MIstatus::success; } // Retrieve the command's latest data/information. Needed for commands of the event type so have // a record of commands pending finishing execution. const CMIUtilString & rMIResultRecord( vCmd.GetMIResultRecord() ); SMICmdData cmdData( vCmd.GetCmdData() ); // Make a copy as the command will be deleted soon cmdData.strMiCmdResultRecord = rMIResultRecord; // Precautionary copy as the command might forget to do this if( vCmd.HasMIResultRecordExtra() ) { cmdData.bHasResultRecordExtra = true; const CMIUtilString & rMIExtra( vCmd.GetMIResultRecordExtra() ); cmdData.strMiCmdResultRecordExtra = rMIExtra; // Precautionary copy as the command might forget to do this } // Send command's MI response to the client bool bOk = CmdStdout( cmdData ); // Delete the command object as do not require anymore bOk = bOk && CmdDelete( vCmd.GetCmdData().id ); return bOk; } //++ ------------------------------------------------------------------------------------ // Details: If the MI Driver is not operating via a client i.e. Eclipse check the command // on failure suggests the application exits. A command can be such that a // failure cannot the allow the application to continue operating. // Args: vCmd - (R) Command object. // Return: None. // Return: None. // Throws: None. //-- void CMICmdInvoker::CmdCauseAppExit( const CMICmdBase & vCmd ) const { if( vCmd.GetExitAppOnCommandFailure() ) { CMIDriver & rDriver( CMIDriver::Instance() ); if( rDriver.IsDriverDebuggingArgExecutable() ) { rDriver.SetExitApplicationFlag( true ); } } } //++ ------------------------------------------------------------------------------------ // Details: Write to stdout and the Log file the command's MI formatted result. // Type: vCmdData - (R) A command's information. // Return: MIstatus::success - Functionality succeeded. // MIstatus::failure - Functionality failed. // Return: None. // Throws: None. //-- bool CMICmdInvoker::CmdStdout( const SMICmdData & vCmdData ) const { bool bOk = m_pLog->WriteLog( vCmdData.strMiCmdAll ); const bool bLock = bOk && m_rStreamOut.Lock(); bOk = bOk && bLock && m_rStreamOut.WriteMIResponse( vCmdData.strMiCmdResultRecord ); if( bOk && vCmdData.bHasResultRecordExtra ) { bOk = m_rStreamOut.WriteMIResponse( vCmdData.strMiCmdResultRecordExtra ); } bOk = bLock && m_rStreamOut.Unlock(); return bOk; } //++ ------------------------------------------------------------------------------------ // Details: Required by the CMICmdMgr::ICmdDeleteCallback. *this object is registered // with the Command Manager to receive callbacks when a command is being deleted. // An object, *this invoker, does not delete a command object itself but calls // the Command Manager to delete a command object. This function is the Invoker's // called. // The Invoker owns the command objects and so can delete them but must do it // via the manager so other objects can be notified of the deletion. // Type: Method. // Args: vCmd - (RW) Command. // Return: None. // Throws: None. //-- void CMICmdInvoker::Delete( SMICmdData & vCmd ) { CmdDelete( vCmd.id, true ); }