blob: 7b9b04414adf26d07f5073f75898496ea6d3a366 [file] [log] [blame]
//===-- MIDriverMgr.cpp -----------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//++
// File: MIDriverMgr.cpp
//
// Overview: CMIDriverMgr 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 <lldb/API/SBError.h>
// In-house headers:
#include "MICmnConfig.h"
#include "MIDriverMgr.h"
#include "MICmnResources.h"
#include "MICmnLog.h"
#include "MICmnLogMediumFile.h"
#include "MIDriver.h"
#include "MIUtilTermios.h"
#include "MICmnStreamStdout.h"
#include "MIUtilSingletonHelper.h"
//++ ------------------------------------------------------------------------------------
// Details: CMIDriverMgr constructor.
// Type: Method.
// Args: None.
// Return: None.
// Throws: None.
//--
CMIDriverMgr::CMIDriverMgr( void )
: m_pDriverCurrent( nullptr )
, m_bInMi2Mode( false )
{
}
//++ ------------------------------------------------------------------------------------
// Details: CMIDriverMgr destructor.
// Type: Overridden.
// Args: None.
// Return: None.
// Throws: None.
//--
CMIDriverMgr::~CMIDriverMgr( void )
{
Shutdown();
}
//++ ------------------------------------------------------------------------------------
// Details: Initialize *this manager.
// Type: Method.
// Args: None.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool CMIDriverMgr::Initialize( void )
{
m_clientUsageRefCnt++;
ClrErrorDescription();
if( m_bInitialized )
return MIstatus::success;
bool bOk = MIstatus::success;
CMIUtilString errMsg;
// Note initialisation order is important here as some resources depend on previous
MI::ModuleInit< CMICmnLog > ( IDS_MI_INIT_ERR_LOG , bOk, errMsg );
MI::ModuleInit< CMICmnResources >( IDS_MI_INIT_ERR_RESOURCES, bOk, errMsg );
if( bOk )
{
MIUtilTermios::StdinTermiosSet();
}
m_bInitialized = bOk;
if( !bOk )
{
CMIUtilString strInitError( CMIUtilString::Format( MIRSRC( IDS_MI_INIT_ERR_DRIVERMGR ), errMsg.c_str() ) );
SetErrorDescription( strInitError );
return MIstatus::failure;
}
return bOk;
}
//++ ------------------------------------------------------------------------------------
// Details: Unbind detach or release resources used by this server in general common
// functionality shared between versions of any server interfaces implemented.
// Type: Method.
// Args: vbAppExitOk - (R) True = No problems, false = App exiting with problems (investigate!).
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool CMIDriverMgr::Shutdown( void )
{
// Do not want a ref counter because this function needs to be called how ever this
// application stops running
//if( --m_clientUsageRefCnt > 0 )
// return MIstatus::success;
bool vbAppExitOk = true;
ClrErrorDescription();
if( !m_bInitialized )
return MIstatus::success;
if( vbAppExitOk )
{
// The MI Driver's log updating may have been switched off switch back on to say all is ok.
CMICmnLog::Instance().SetEnabled( true );
#if _DEBUG
CMICmnStreamStdout::Instance().Write( MIRSRC( IDE_MI_APP_EXIT_OK ) ); // Both stdout and Log
#else
CMICmnLog::WriteLog( MIRSRC( IDE_MI_APP_EXIT_OK ) ); // Just to the Log
#endif // _DEBUG
}
else
{
CMICmnLog & rAppLog = CMICmnLog::Instance();
if( rAppLog.GetEnabled() )
{
const CMIUtilString msg( CMIUtilString::Format( MIRSRC( IDE_MI_APP_EXIT_WITH_PROBLEM ), CMICmnLogMediumFile::Instance().GetFileName().c_str() ) );
CMICmnStreamStdout::Instance().Write( msg );
}
else
{
// The MI Driver's log updating may have been switched off switch back on to say there has been problem.
rAppLog.SetEnabled( true );
const CMIUtilString msg( CMIUtilString::Format( MIRSRC( IDE_MI_APP_EXIT_WITH_PROBLEM_NO_LOG ), CMICmnLogMediumFile::Instance().GetFileName().c_str() ) );
CMICmnStreamStdout::Instance().Write( msg );
}
}
m_bInitialized = false;
bool bOk = MIstatus::success;
CMIUtilString errMsg;
// Tidy up
UnregisterDriverAll();
MIUtilTermios::StdinTermiosReset();
// Note shutdown order is important here
MI::ModuleShutdown< CMICmnResources >( IDE_MI_SHTDWN_ERR_RESOURCES, bOk, errMsg );
MI::ModuleShutdown< CMICmnLog > ( IDS_MI_SHTDWN_ERR_LOG , bOk, errMsg );
if( !bOk )
{
SetErrorDescriptionn( MIRSRC( IDS_MI_SHTDWN_ERR_DRIVERMGR ), errMsg.c_str() );
}
return bOk;
}
//++ ------------------------------------------------------------------------------------
// Details: Unregister all the Driver registered with *this manager. The manager also
// deletes
// Type: Method.
// Args: None.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool CMIDriverMgr::UnregisterDriverAll( void )
{
MapDriverIdToDriver_t::const_iterator it = m_mapDriverIdToDriver.begin();
while( it != m_mapDriverIdToDriver.end() )
{
IDriver * pDriver = (*it).second;
pDriver->DoShutdown();
// Next
++it;
}
m_mapDriverIdToDriver.clear();
m_pDriverCurrent = NULL;
return MIstatus::success;
}
//++ ------------------------------------------------------------------------------------
// Details: Register a driver with *this Driver Manager. Call SetUseThisDriverToDoWork()
// inform the manager which driver is the one to the work. The manager calls
// the driver's init function which must be successful in order to complete the
// registration.
// Type: Method.
// Args: vrDriver - (R) The driver to register.
// vrDriverID - (R) The driver's ID to lookup by.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool CMIDriverMgr::RegisterDriver( const IDriver & vrDriver, const CMIUtilString & vrDriverID )
{
if( HaveDriverAlready( vrDriver ) )
return MIstatus::success;
IDriver * pDriver = const_cast< IDriver * >( &vrDriver );
if( !pDriver->SetId( vrDriverID ) )
return MIstatus::failure;
if( !pDriver->DoInitialize() )
{
SetErrorDescriptionn( MIRSRC( IDS_DRIVERMGR_DRIVER_ERR_INIT ), pDriver->GetName().c_str(), vrDriverID.c_str(), pDriver->GetError().c_str() );
return MIstatus::failure;
}
MapPairDriverIdToDriver_t pr( vrDriverID, pDriver );
m_mapDriverIdToDriver.insert( pr );
return MIstatus::success;
}
//++ ------------------------------------------------------------------------------------
// Details: Query the Driver Manager to see if *this manager has the driver already
// registered.
// Type: Method.
// Args: vrDriver - (R) The driver to query.
// Return: True - registered.
// False - not registered.
// Throws: None.
//--
bool CMIDriverMgr::HaveDriverAlready( const IDriver & vrDriver ) const
{
MapDriverIdToDriver_t::const_iterator it = m_mapDriverIdToDriver.begin();
while( it != m_mapDriverIdToDriver.end() )
{
const IDriver * pDrvr = (*it).second;
if( pDrvr == &vrDriver )
return true;
// Next
++it;
}
return false;
}
//++ ------------------------------------------------------------------------------------
// Details: Unregister a driver from the Driver Manager. Call the SetUseThisDriverToDoWork()
// function to define another driver to do work if the one being unregistered did
// the work previously.
// Type: Method.
// Args: vrDriver - (R) The driver to unregister.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool CMIDriverMgr::UnregisterDriver( const IDriver & vrDriver )
{
const IDriver * pDrvr = nullptr;
MapDriverIdToDriver_t::const_iterator it = m_mapDriverIdToDriver.begin();
while( it != m_mapDriverIdToDriver.end() )
{
pDrvr = (*it).second;
if( pDrvr == &vrDriver )
break;
// Next
++it;
}
m_mapDriverIdToDriver.erase( it );
if( m_pDriverCurrent == pDrvr )
m_pDriverCurrent = nullptr;
return MIstatus::success;
}
//++ ------------------------------------------------------------------------------------
// Details: Specify the driver to do work. The Driver Manager drives this driver. Any
// previous driver doing work is not called anymore (so be sure the previous
// driver is in a tidy state before stopping it working).
// Type: Method.
// Args: vrADriver - (R) A lldb::SBBroadcaster/IDriver derived object.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool CMIDriverMgr::SetUseThisDriverToDoWork( const IDriver & vrADriver )
{
m_pDriverCurrent = const_cast< IDriver * >( &vrADriver );
const CMIUtilString msg( CMIUtilString::Format( MIRSRC( IDS_DRIVER_SAY_DRIVER_USING ), m_pDriverCurrent->GetName().c_str() ) );
m_pLog->Write( msg, CMICmnLog::eLogVerbosity_Log );
m_bInMi2Mode = m_pDriverCurrent->GetDriverIsGDBMICompatibleDriver();
return MIstatus::success;
}
//++ ------------------------------------------------------------------------------------
// Details: Ask *this manager which driver is currently doing the work.
// Type: Method.
// Args: None.
// Return: IDriver * - Pointer to a driver, NULL if there is no current working driver.
// Throws: None.
//--
CMIDriverMgr::IDriver * CMIDriverMgr::GetUseThisDriverToDoWork( void ) const
{
return m_pDriverCurrent;
}
//++ ------------------------------------------------------------------------------------
// Details: Call this function puts *this driver to work.
// Type: Method.
// Args: None.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool CMIDriverMgr::DriverMainLoop( void )
{
if( m_pDriverCurrent != nullptr )
return m_pDriverCurrent->DoMainLoop();
return MIstatus::failure;
}
//++ ------------------------------------------------------------------------------------
// Details: Call *this driver to resize the console window.
// Type: Method.
// Args: vWindowSizeWsCol - (R) New window column size.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
void CMIDriverMgr::DriverResizeWindow( const uint32_t vWindowSizeWsCol )
{
if( m_pDriverCurrent != nullptr )
return m_pDriverCurrent->DoResizeWindow( vWindowSizeWsCol );
}
//++ ------------------------------------------------------------------------------------
// Details: Get the current driver to validate executable command line arguments.
// Type: Method.
// 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) Point to a standard output stream.
// vwbExiting - (W) True = *this want to exit, false = continue to work.
// Return: MIstatus::success - Functional succeeded.
// MIstatus::failure - Functional failed.
// Throws: None.
//--
bool CMIDriverMgr::DriverParseArgs( const int argc, const char * argv[], FILE * vpStdOut, bool & vwbExiting )
{
if( m_pDriverCurrent == nullptr )
{
const CMIUtilString errMsg( CMIUtilString::Format( MIRSRC( IDS_DRIVER_ERR_CURRENT_NOT_SET ) ) );
CMICmnStreamStdout::Instance().Write( errMsg, true );
return MIstatus::failure;
}
const lldb::SBError error( m_pDriverCurrent->DoParseArgs( argc, argv, vpStdOut, vwbExiting ) );
bool bOk = !error.Fail();
if( !bOk )
{
CMIUtilString errMsg;
const char * pErrorCstr = error.GetCString();
if( pErrorCstr != nullptr )
errMsg = CMIUtilString::Format( MIRSRC( IDS_DRIVER_ERR_PARSE_ARGS ), m_pDriverCurrent->GetName().c_str(), pErrorCstr );
else
errMsg = CMIUtilString::Format( MIRSRC( IDS_DRIVER_ERR_PARSE_ARGS_UNKNOWN ), m_pDriverCurrent->GetName().c_str() );
bOk = CMICmnStreamStdout::Instance().Write( errMsg, true );
}
return bOk;
}
//++ ------------------------------------------------------------------------------------
// Details: Retrieve the current driver's last error condition.
// Type: Method.
// Args: None.
// Return: CMIUtilString - Text description.
// Throws: None.
//--
CMIUtilString CMIDriverMgr::DriverGetError( void ) const
{
if( m_pDriverCurrent != nullptr )
return m_pDriverCurrent->GetError();
return CMIUtilString();
}
//++ ------------------------------------------------------------------------------------
// Details: Retrieve the current driver's name.
// Type: Method.
// Args: None.
// Return: CMIUtilString - Driver name.
// Empty string = no current working driver specified.
// Throws: None.
//--
CMIUtilString CMIDriverMgr::DriverGetName( void ) const
{
if( m_pDriverCurrent != nullptr )
return m_pDriverCurrent->GetName();
return CMIUtilString();
}
//++ ------------------------------------------------------------------------------------
// Details: Retrieve the current driver's debugger object.
// Type: Method.
// Args: None.
// Return: lldb::SBDebugger * - Ptr to driver's debugger object.
// - NULL = no current working driver specified.
// Throws: None.
//--
lldb::SBDebugger * CMIDriverMgr::DriverGetTheDebugger( void )
{
lldb::SBDebugger * pDebugger = nullptr;
if( m_pDriverCurrent != nullptr )
pDebugger = &m_pDriverCurrent->GetTheDebugger();
return pDebugger;
}
//++ ------------------------------------------------------------------------------------
// Details: Check the arguments given on the command line. The main purpose of this
// function is to check for the presence of the --interpreter option. Having
// this option present tells *this manager to set the CMIDriver to do work. If
// not use the LLDB driver. The following are options that are only handled by
// the CMIDriverMgr are:
// --help or -h
// --interpreter
// --version
// --versionLong
// The above arguments are not handled by any driver object.
// Type: Method.
// 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.
// 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.
//--
bool CMIDriverMgr::ParseArgs( const int argc, const char * argv[], bool & vwbExiting )
{
bool bOk = MIstatus::success;
vwbExiting = false;
// Print MI application path to the Log file
const CMIUtilString appPath( CMIUtilString::Format( MIRSRC( IDS_MI_APP_FILEPATHNAME ), argv[ 0 ] ) );
bOk = m_pLog->Write( appPath, CMICmnLog::eLogVerbosity_Log );
// Print application arguments to the Log file
const bool bHaveArgs( argc >= 2 );
CMIUtilString strArgs( MIRSRC( IDS_MI_APP_ARGS ) );
if( !bHaveArgs )
{
strArgs += MIRSRC( IDS_WORD_NONE );
bOk = bOk && m_pLog->Write( strArgs, CMICmnLog::eLogVerbosity_Log );
}
else
{
for( MIint i = 1; i < argc; i++ )
{
strArgs += CMIUtilString::Format( "%d:'%s' ", i, argv[ i ] );
}
bOk = bOk && m_pLog->Write( strArgs, CMICmnLog::eLogVerbosity_Log );
}
// Look for the command line options
bool bHaveArgInterpret = false;
bool bHaveArgVersion = false;
bool bHaveArgVersionLong = false;
bool bHaveArgNoLog = false;
bool bHaveArgHelp = false;
// Hardcode the use of the MI driver
#if MICONFIG_DEFAULT_TO_MI_DRIVER
bHaveArgInterpret = true;
#endif // MICONFIG_DEFAULT_TO_MI_DRIVER
if( bHaveArgs )
{
for( MIint i = 1; i < argc; i++ )
{
// *** Add args to help in GetHelpOnCmdLineArgOptions() ***
const CMIUtilString strArg( argv[ i ] );
if( 0 == strArg.compare( "--interpreter" ) )
{
bHaveArgInterpret = true;
}
if( 0 == strArg.compare( "--version" ) )
{
bHaveArgVersion = true;
}
if( 0 == strArg.compare( "--versionLong" ) )
{
bHaveArgVersionLong = true;
}
if( 0 == strArg.compare( "--noLog" ) )
{
bHaveArgNoLog = true;
}
if( (0 == strArg.compare( "--help" )) || (0 == strArg.compare( "-h" )) )
{
bHaveArgHelp = true;
}
}
}
if( bHaveArgNoLog )
{
CMICmnLog::Instance().SetEnabled( false );
}
// Todo: Remove this output when MI is finished. It is temporary to persuade Ecllipse plugin to work.
// Eclipse reads this literally and will not work unless it gets this exact version text.
// Handle --version option (ignore the --interpreter option if present)
if( bHaveArgVersion )
{
vwbExiting = true;
bOk = bOk && CMICmnStreamStdout::Instance().WriteMIResponse( MIRSRC( IDE_MI_VERSION_GDB ) );
return bOk;
}
// Todo: Make this the --version when the the above --version version is removed
// Handle --versionlong option (ignore the --interpreter option if present)
if( bHaveArgVersionLong )
{
vwbExiting = true;
bOk = bOk && CMICmnStreamStdout::Instance().WriteMIResponse( GetAppVersion() );
return bOk;
}
// Both '--help' and '--intepreter' means give help for MI only. Without
// '--interpreter' help the LLDB driver is working and so help is for that.
if( bHaveArgHelp && bHaveArgInterpret )
{
vwbExiting = true;
bOk = bOk && CMICmnStreamStdout::Instance().WriteMIResponse( GetHelpOnCmdLineArgOptions() );
return bOk;
}
// This makes the assumption that there is at least one MI compatible
// driver registered and one LLDB driver registerd and the CMIDriver
// is the first one found.
// ToDo: Implement a better solution that handle any order, any number
// of drivers. Or this 'feature' may be removed if deemed not required.
IDriver * pLldbDriver = GetFirstNonMIDriver();
IDriver * pMi2Driver = GetFirstMIDriver();
if( bHaveArgInterpret && (pMi2Driver != nullptr) )
bOk = bOk && SetUseThisDriverToDoWork( *pMi2Driver );
else if( pLldbDriver != nullptr )
bOk = bOk && SetUseThisDriverToDoWork( *pLldbDriver );
else
{
if( bOk )
{
vwbExiting = true;
const CMIUtilString msg( MIRSRC( IDS_DRIVER_ERR_NON_REGISTERED ) );
bOk = bOk && CMICmnStreamStdout::Instance().WriteMIResponse( msg );
}
}
return bOk;
}
//++ ------------------------------------------------------------------------------------
// Details: Return formatted application version and name information.
// Type: Method.
// Args: None.
// Return: CMIUtilString - Text data.
// Throws: None.
//--
CMIUtilString CMIDriverMgr::GetAppVersion( void ) const
{
const CMIUtilString strProj( MIRSRC( IDS_PROJNAME ) );
const CMIUtilString strVsn( CMIDriver::Instance().GetVersionDescription() );
const CMIUtilString strGdb( MIRSRC( IDE_MI_VERSION_GDB ) );
const CMIUtilString strVrsnInfo( CMIUtilString::Format( "%s\n%s\n%s", strProj.c_str(), strVsn.c_str(), strGdb.c_str() ) );
return strVrsnInfo;
}
//++ ------------------------------------------------------------------------------------
// Details: Return formatted help information on all the MI command line options.
// Type: Method.
// Args: None.
// Return: CMIUtilString - Text data.
// Throws: None.
//--
CMIUtilString CMIDriverMgr::GetHelpOnCmdLineArgOptions( void ) const
{
const CMIUtilString pHelp[] =
{
MIRSRC( IDE_MI_APP_ARG_USAGE ),
MIRSRC( IDE_MI_APP_ARG_HELP ),
MIRSRC( IDE_MI_APP_ARG_VERSION ),
MIRSRC( IDE_MI_APP_ARG_VERSION_LONG ),
MIRSRC( IDE_MI_APP_ARG_INTERPRETER ),
CMIUtilString::Format( MIRSRC( IDE_MI_APP_ARG_NO_APP_LOG ), CMICmnLogMediumFile::Instance().GetFileName().c_str() ),
MIRSRC( IDS_CMD_QUIT_HELP ),
MIRSRC( IDE_MI_APP_ARG_EXAMPLE )
};
const MIuint nHelpItems = sizeof pHelp / sizeof pHelp[ 0 ];
CMIUtilString strHelp;
for( MIuint i = 0; i < nHelpItems; i++ )
{
strHelp += pHelp[ i ];
strHelp += "\n\n";
}
return strHelp;
}
//++ ------------------------------------------------------------------------------------
// Details: Search the registered drivers and return the first driver which says it is
// GDB/MI compatible i.e. the CMIDriver class.
// Type: Method.
// Args: None.
// Return: IDriver * - Ptr to driver, NULL = no driver found.
// Throws: None.
//--
CMIDriverMgr::IDriver * CMIDriverMgr::GetFirstMIDriver( void ) const
{
IDriver * pDriver = nullptr;
MapDriverIdToDriver_t::const_iterator it = m_mapDriverIdToDriver.begin();
while( it != m_mapDriverIdToDriver.end() )
{
const CMIUtilString & rDrvId = (*it).first; MIunused( rDrvId );
IDriver * pDvr = (*it).second;
if( pDvr->GetDriverIsGDBMICompatibleDriver() )
{
pDriver = pDvr;
break;
}
// Next
++it;
}
return pDriver;
}
//++ ------------------------------------------------------------------------------------
// Details: Search the registered drivers and return the first driver which says it is
// not GDB/MI compatible i.e. the LLDB Driver class.
// Type: Method.
// Args: None.
// Return: IDriver * - Ptr to driver, NULL = no driver found.
// Throws: None.
//--
CMIDriverMgr::IDriver * CMIDriverMgr::GetFirstNonMIDriver( void ) const
{
IDriver * pDriver = nullptr;
MapDriverIdToDriver_t::const_iterator it = m_mapDriverIdToDriver.begin();
while( it != m_mapDriverIdToDriver.end() )
{
const CMIUtilString & rDrvId = (*it).first; MIunused( rDrvId );
IDriver * pDvr = (*it).second;
if( !pDvr->GetDriverIsGDBMICompatibleDriver() )
{
pDriver = pDvr;
break;
}
// Next
++it;
}
return pDriver;
}
//++ ------------------------------------------------------------------------------------
// Details: Search the registered drivers and return driver with the specified ID.
// Type: Method.
// Args: vrDriverId - (R) ID of a driver.
// Return: IDriver * - Ptr to driver, NULL = no driver found.
// Throws: None.
//--
CMIDriverMgr::IDriver * CMIDriverMgr::GetDriver( const CMIUtilString & vrDriverId ) const
{
MapDriverIdToDriver_t::const_iterator it = m_mapDriverIdToDriver.find( vrDriverId );
if( it == m_mapDriverIdToDriver.end() )
return nullptr;
IDriver * pDriver = (*it).second;
return pDriver;
}