//===-- MICmnLLDBDebuggerHandleEvents.cpp --------------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

//++
// File:		MICmnLLDBDebuggerHandleEvents.cpp
//
// Overview:	CMICmnLLDBDebuggerHandleEvents 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/SBEvent.h>
#include <lldb/API/SBProcess.h>
#include <lldb/API/SBBreakpoint.h>
#include <lldb/API/SBStream.h>
#include <lldb/API/SBThread.h>
#include <lldb/API/SBCommandInterpreter.h>
#include <lldb/API/SBCommandReturnObject.h>
#ifdef _WIN32
	#include <io.h>			// For the ::_access()
#else
	#include <unistd.h>		// For the ::access()
#endif // _WIN32
#include <limits.h>

// In-house headers:
#include "MICmnLLDBDebuggerHandleEvents.h"
#include "MICmnResources.h"
#include "MICmnLog.h"
#include "MICmnLLDBDebugSessionInfo.h"
#include "MICmnMIResultRecord.h"
#include "MICmnMIValueConst.h"
#include "MICmnMIValueList.h"
#include "MICmnMIOutOfBandRecord.h"
#include "MICmnStreamStdout.h"
#include "MICmnStreamStderr.h"
#include "MIUtilDebug.h"
#include "MIDriver.h"

//++ ------------------------------------------------------------------------------------
// Details:	CMICmnLLDBDebuggerHandleEvents constructor.
// Type:	Method.
// Args:	None.
// Return:	None.
// Throws:	None.
//--
CMICmnLLDBDebuggerHandleEvents::CMICmnLLDBDebuggerHandleEvents( void )
{
}

//++ ------------------------------------------------------------------------------------
// Details:	CMICmnLLDBDebuggerHandleEvents destructor.
// Type:	Overridable.
// Args:	None.
// Return:	None.
// Throws:	None.
//--
CMICmnLLDBDebuggerHandleEvents::~CMICmnLLDBDebuggerHandleEvents( void )
{
	Shutdown();
}

//++ ------------------------------------------------------------------------------------
// Details:	Initialize resources for *this broardcaster object.
// Type:	Method.
// Args:	None.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::Initialize( void )
{
	m_clientUsageRefCnt++;

	if( m_bInitialized )
		return MIstatus::success;

	m_bInitialized = MIstatus::success;
	
	return m_bInitialized;
}

//++ ------------------------------------------------------------------------------------
// Details:	Release resources for *this broardcaster object.
// Type:	Method.
// Args:	None.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::Shutdown( void )
{
	if( --m_clientUsageRefCnt > 0 )
		return MIstatus::success;
	
	if( !m_bInitialized )
		return MIstatus::success;

	m_bInitialized = false;

	return MIstatus::success;
}	

//++ ------------------------------------------------------------------------------------
// Details:	Interpret the event object to asscertain the action to take or information to
//			to form and put in a MI Out-of-band record object which is given to stdout.
// Type:	Method.
// Args:	vEvent			- (R) An LLDB broadcast event.
//			vrbHandledEvent	- (W) True - event handled, false = not handled.
//			vrbExitAppEvent	- (W) True - Received LLDB exit app event, false = did not.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::HandleEvent( const lldb::SBEvent & vEvent, bool & vrbHandledEvent, bool & vrbExitAppEvent )
{
	bool bOk = MIstatus::success;
	vrbHandledEvent = false;
	vrbExitAppEvent = false;
	
	if( lldb::SBProcess::EventIsProcessEvent( vEvent ) )
	{
		vrbHandledEvent = true;
		bOk = HandleEventSBProcess( vEvent, vrbExitAppEvent );
	}
	else if( lldb::SBBreakpoint::EventIsBreakpointEvent( vEvent ) )
	{
		vrbHandledEvent = true;
		bOk = HandleEventSBBreakPoint( vEvent );
	}
	else if( lldb::SBThread::EventIsThreadEvent( vEvent ) )
	{
		vrbHandledEvent = true;
		bOk = HandleEventSBThread( vEvent );
	}
	
	return bOk;
}

//++ ------------------------------------------------------------------------------------
// Details:	Handle a LLDB SBProcess event.
// Type:	Method.
// Args:	vEvent			- (R) An LLDB broadcast event.
//			vrbExitAppEvent	- (W) True - Received LLDB exit app event, false = did not.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::HandleEventSBProcess( const lldb::SBEvent & vEvent, bool & vrbExitAppEvent )
{
	bool bOk = MIstatus::success;

	const MIchar * pEventType = "";
	const MIuint nEventType = vEvent.GetType();
	switch( nEventType )
	{
	case lldb::SBProcess::eBroadcastBitInterrupt:
		pEventType = "eBroadcastBitInterrupt";
		break;
	case lldb::SBProcess::eBroadcastBitProfileData:
		pEventType = "eBroadcastBitProfileData";
		break;
	case lldb::SBProcess::eBroadcastBitStateChanged:
		pEventType = "eBroadcastBitStateChanged";
		bOk = HandleProcessEventBroadcastBitStateChanged( vEvent, vrbExitAppEvent );
		break;
	case lldb::SBProcess::eBroadcastBitSTDERR:
		pEventType = "eBroadcastBitSTDERR";
		bOk = GetProcessStderr();
		break;
	case lldb::SBProcess::eBroadcastBitSTDOUT:
		pEventType = "eBroadcastBitSTDOUT";
		bOk = GetProcessStdout();
		break;
	default:
	{
		const CMIUtilString msg( CMIUtilString::Format( MIRSRC( IDS_LLDBOUTOFBAND_ERR_UNKNOWN_EVENT ), "SBProcess", (MIuint) nEventType ) );
		SetErrorDescription( msg );
		return MIstatus::failure;
	}
	}
	m_pLog->WriteLog( CMIUtilString::Format( "##### An SB Process event occurred: %s", pEventType ) );

	return bOk;
}
  
//++ ------------------------------------------------------------------------------------
// Details:	Handle a LLDB SBBreakpoint event.
// Type:	Method.
// Args:	vEvent	- (R) An LLDB broadcast event.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::HandleEventSBBreakPoint( const lldb::SBEvent & vEvent )
{
	bool bOk = MIstatus::success;

	const MIchar * pEventType ="";
    const lldb::BreakpointEventType eEvent = lldb::SBBreakpoint::GetBreakpointEventTypeFromEvent( vEvent );
	switch( eEvent )
	{
	case lldb::eBreakpointEventTypeThreadChanged:
		pEventType = "eBreakpointEventTypeThreadChanged";
		break;
	case lldb::eBreakpointEventTypeLocationsRemoved:
		pEventType = "eBreakpointEventTypeLocationsRemoved";
		break;
    case lldb::eBreakpointEventTypeInvalidType:
		pEventType = "eBreakpointEventTypeInvalidType";
		break;
    case lldb::eBreakpointEventTypeLocationsAdded:
		pEventType = "eBreakpointEventTypeLocationsAdded";
		bOk = HandleEventSBBreakpointLocationsAdded( vEvent );
		break;
    case lldb::eBreakpointEventTypeAdded:
		pEventType = "eBreakpointEventTypeAdded";
		bOk = HandleEventSBBreakpointCmn( vEvent );
		break;
    case lldb::eBreakpointEventTypeRemoved:
		pEventType = "eBreakpointEventTypeRemoved";
		bOk = HandleEventSBBreakpointCmn( vEvent );
		break;
    case lldb::eBreakpointEventTypeLocationsResolved:
		pEventType = "eBreakpointEventTypeLocationsResolved";
		break;
    case lldb::eBreakpointEventTypeEnabled:
		pEventType ="eBreakpointEventTypeEnabled";
		bOk = HandleEventSBBreakpointCmn( vEvent );
		break;
    case lldb::eBreakpointEventTypeDisabled:
		pEventType = "eBreakpointEventTypeDisabled";
		bOk = HandleEventSBBreakpointCmn( vEvent );
		break;
    case lldb::eBreakpointEventTypeCommandChanged:
		pEventType = "eBreakpointEventTypeCommandChanged";
		bOk = HandleEventSBBreakpointCmn( vEvent );
		break;
    case lldb::eBreakpointEventTypeConditionChanged:
		pEventType ="eBreakpointEventTypeConditionChanged";
		bOk = HandleEventSBBreakpointCmn( vEvent );
		break;
    case lldb::eBreakpointEventTypeIgnoreChanged:
		pEventType = "eBreakpointEventTypeIgnoreChanged";
		bOk = HandleEventSBBreakpointCmn( vEvent );
		break;
    default:
	{
		const CMIUtilString msg( CMIUtilString::Format( MIRSRC( IDS_LLDBOUTOFBAND_ERR_UNKNOWN_EVENT ), "SBBreakPoint", (MIuint) eEvent ) );
		SetErrorDescription( msg );
		return MIstatus::failure;
	}
	}
	m_pLog->WriteLog( CMIUtilString::Format( "##### An SB Breakpoint event occurred: %s", pEventType ) );

	return bOk;
}

//++ ------------------------------------------------------------------------------------
// Details:	Handle a LLDB SBBreakpoint event.
// Type:	Method.
// Args:	vEvent	- (R) An LLDB broadcast event.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::HandleEventSBBreakpointLocationsAdded( const lldb::SBEvent & vEvent )
{
	const MIuint nLoc = lldb::SBBreakpoint::GetNumBreakpointLocationsFromEvent( vEvent );
	if( nLoc == 0 )
		return MIstatus::success;

	lldb::SBBreakpoint brkPt = lldb::SBBreakpoint::GetBreakpointFromEvent( vEvent );
	const CMIUtilString plural( (nLoc == 1) ? "" : "s" );
	const CMIUtilString msg( CMIUtilString::Format( "%d location%s added to breakpoint %d", nLoc, plural.c_str(), brkPt.GetID() ) );
        
	return TextToStdout( msg );
}
	
//++ ------------------------------------------------------------------------------------
// Details:	Handle a LLDB SBBreakpoint event.
// Type:	Method.
// Args:	vEvent	- (R) An LLDB broadcast event.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::HandleEventSBBreakpointCmn( const lldb::SBEvent & vEvent )
{
	lldb::SBBreakpoint brkPt = lldb::SBBreakpoint::GetBreakpointFromEvent( vEvent );
	if( !brkPt.IsValid() )
		return MIstatus::success;

	CMICmnLLDBDebugSessionInfo & rSessionInfo( CMICmnLLDBDebugSessionInfo::Instance() );
	CMICmnLLDBDebugSessionInfo::SBrkPtInfo sBrkPtInfo;
	if( !rSessionInfo.GetBrkPtInfo( brkPt, sBrkPtInfo ) )
	{
		SetErrorDescription( CMIUtilString::Format( MIRSRC( IDS_LLDBOUTOFBAND_ERR_BRKPT_INFO_GET ), "HandleEventSBBreakpointCmn()", brkPt.GetID() ) );
		return MIstatus::failure;
	}
	
	// CODETAG_LLDB_BREAKPOINT_CREATION
	// This is in a worker thread
	// Add more breakpoint information or overwrite existing information
	CMICmnLLDBDebugSessionInfo::SBrkPtInfo sBrkPtInfoRec;
	if( !rSessionInfo.RecordBrkPtInfoGet( brkPt.GetID(), sBrkPtInfoRec ) )
	{
		SetErrorDescription( CMIUtilString::Format( MIRSRC( IDS_LLDBOUTOFBAND_ERR_BRKPT_NOTFOUND ), "HandleEventSBBreakpointCmn()", brkPt.GetID() ) );
		return MIstatus::failure;
	}
	sBrkPtInfo.m_bDisp = sBrkPtInfoRec.m_bDisp;		
	sBrkPtInfo.m_bEnabled = brkPt.IsEnabled();		
	sBrkPtInfo.m_bHaveArgOptionThreadGrp = false; 
	sBrkPtInfo.m_strOptThrdGrp = "";				
	sBrkPtInfo.m_nTimes = brkPt.GetHitCount();		
	sBrkPtInfo.m_strOrigLoc = sBrkPtInfoRec.m_strOrigLoc;			
	sBrkPtInfo.m_nIgnore = sBrkPtInfoRec.m_nIgnore;
	sBrkPtInfo.m_bPending = sBrkPtInfoRec.m_bPending;
	sBrkPtInfo.m_bCondition = sBrkPtInfoRec.m_bCondition;
	sBrkPtInfo.m_strCondition = sBrkPtInfoRec.m_strCondition;
	sBrkPtInfo.m_bBrkPtThreadId = sBrkPtInfoRec.m_bBrkPtThreadId;
	sBrkPtInfo.m_nBrkPtThreadId = sBrkPtInfoRec.m_nBrkPtThreadId;
																						
	// MI print "=breakpoint-modified,bkpt={number=\"%d\",type=\"breakpoint\",disp=\"%s\",enabled=\"%c\",addr=\"0x%08x\", func=\"%s\",file=\"%s\",fullname=\"%s/%s\",line=\"%d\",times=\"%d\",original-location=\"%s\"}"
	CMICmnMIValueTuple miValueTuple;
	if( !rSessionInfo.MIResponseFormBrkPtInfo( sBrkPtInfo, miValueTuple ) )
	{
		SetErrorDescription( CMIUtilString::Format( MIRSRC( IDS_LLDBOUTOFBAND_ERR_FORM_MI_RESPONSE ), "HandleEventSBBreakpointCmn()" ) );
		return MIstatus::failure;
	}
	
	const CMICmnMIValueResult miValueResultC( "bkpt", miValueTuple );
	const CMICmnMIOutOfBandRecord miOutOfBandRecord( CMICmnMIOutOfBandRecord::eOutOfBand_BreakPointModified, miValueResultC );
	const bool bOk = MiOutOfBandRecordToStdout( miOutOfBandRecord );
		
	return bOk;
}

//++ ------------------------------------------------------------------------------------
// Details:	Handle a LLDB SBThread event.
// Type:	Method.
// Args:	vEvent	- (R) An LLDB broadcast event.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::HandleEventSBThread( const lldb::SBEvent & vEvent )
{
	if( !ChkForStateChanges() )
		return MIstatus::failure;

	bool bOk = MIstatus::success;
	const MIchar * pEventType = "";
    const MIuint nEventType = vEvent.GetType();
	switch( nEventType )
	{
	case lldb::SBThread::eBroadcastBitStackChanged:
		pEventType = "eBroadcastBitStackChanged";
		bOk = HandleEventSBThreadBitStackChanged( vEvent );
		break;
	case lldb::SBThread::eBroadcastBitThreadSuspended:
		pEventType = "eBroadcastBitThreadSuspended";
		bOk = HandleEventSBThreadSuspended( vEvent );
		break;
	case lldb::SBThread::eBroadcastBitThreadResumed:
		pEventType = "eBroadcastBitThreadResumed";
		break;
	case lldb::SBThread::eBroadcastBitSelectedFrameChanged:
		pEventType = "eBroadcastBitSelectedFrameChanged";
		break;
	case lldb::SBThread::eBroadcastBitThreadSelected:
		pEventType = "eBroadcastBitThreadSelected";
		break;
	default:
	{
		const CMIUtilString msg( CMIUtilString::Format( MIRSRC( IDS_LLDBOUTOFBAND_ERR_UNKNOWN_EVENT ), "SBThread", (MIuint) nEventType ) );
		SetErrorDescription( msg );
		return MIstatus::failure;
	}
	}
	m_pLog->WriteLog( CMIUtilString::Format( "##### An SBThread event occurred: %s", pEventType ) );

	return bOk;
}

//++ ------------------------------------------------------------------------------------
// Details:	Handle a LLDB SBThread event.
// Type:	Method.
// Args:	vEvent	- (R) An LLDB broadcast event.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::HandleEventSBThreadSuspended( const lldb::SBEvent & vEvent )
{
	lldb::SBThread thread = lldb::SBThread::GetThreadFromEvent( vEvent );
	if( !thread.IsValid() )
		return MIstatus::success;

	const lldb::StopReason eStopReason = thread.GetStopReason();
	if( eStopReason != lldb::eStopReasonSignal )
		return MIstatus::success;

	// MI print "@thread=%d,signal=%lld"
	const MIuint64 nId = thread.GetStopReasonDataAtIndex( 0 );
	const CMIUtilString strThread( CMIUtilString::Format( "%d", thread.GetThreadID() ) );
	const CMICmnMIValueConst  miValueConst( strThread );
	const CMICmnMIValueResult miValueResult( "thread", miValueConst );
	CMICmnMIOutOfBandRecord miOutOfBandRecord( CMICmnMIOutOfBandRecord::eOutOfBand_Thread, miValueResult );
	const CMIUtilString strSignal( CMIUtilString::Format( "%lld", nId ) );
	const CMICmnMIValueConst  miValueConst2( strSignal );
	const CMICmnMIValueResult miValueResult2( "signal", miValueConst2 );
	bool bOk = miOutOfBandRecord.Add( miValueResult2 );
	bOk = bOk && MiOutOfBandRecordToStdout( miOutOfBandRecord );	

	return bOk;
}

//++ ------------------------------------------------------------------------------------
// Details:	Handle a LLDB SBThread event.
// Type:	Method.
// Args:	vEvent	- (R) An LLDB broadcast event.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::HandleEventSBThreadBitStackChanged( const lldb::SBEvent & vEvent )
{
	lldb::SBThread thread = lldb::SBThread::GetThreadFromEvent( vEvent );
	if( !thread.IsValid() )
		return MIstatus::success;

	lldb::SBStream streamOut;
	const bool bOk = thread.GetStatus( streamOut );
	return bOk && TextToStdout( streamOut.GetData() );
}
	
//++ ------------------------------------------------------------------------------------
// Details:	Handle a LLDB SBCommandInterpreter event.
// Type:	Method.
// Args:	vEvent	- (R) An LLDB command interpreter event.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::HandleEventSBCommandInterpreter( const lldb::SBEvent & vEvent )
{
	// This function is not used
	// *** This function is under development

    const MIchar * pEventType = "";
    const MIuint nEventType = vEvent.GetType();
	switch( nEventType )
	{
	case lldb::SBCommandInterpreter::eBroadcastBitThreadShouldExit:
		pEventType ="eBroadcastBitThreadShouldExit";
	// ToDo: IOR: Reminder to maybe handle this here
	//const MIuint nEventType = event.GetType();
	//if( nEventType & lldb::SBCommandInterpreter::eBroadcastBitThreadShouldExit )
	//{
	//	m_pClientDriver->SetExitApplicationFlag();    
	//	vrbYesExit = true;
	//	return MIstatus::success;
	//}		break;
	case lldb::SBCommandInterpreter::eBroadcastBitResetPrompt:
		pEventType = "eBroadcastBitResetPrompt";
		break;
	case lldb::SBCommandInterpreter::eBroadcastBitQuitCommandReceived:
		pEventType = "eBroadcastBitQuitCommandReceived";
		break;
	case lldb::SBCommandInterpreter::eBroadcastBitAsynchronousOutputData:
		pEventType = "eBroadcastBitAsynchronousOutputData";
		break;
	case lldb::SBCommandInterpreter::eBroadcastBitAsynchronousErrorData:
		pEventType = "eBroadcastBitAsynchronousErrorData";
		break;
	default:
	{
		const CMIUtilString msg( CMIUtilString::Format( MIRSRC( IDS_LLDBOUTOFBAND_ERR_UNKNOWN_EVENT ), "SBCommandInterpreter", (MIuint) nEventType ) );
		SetErrorDescription( msg );
		return MIstatus::failure;
	}
	}
	m_pLog->WriteLog( CMIUtilString::Format( "##### An SBCommandInterpreter event occurred: %s", pEventType ) );

	return MIstatus::success;
}
 
//++ ------------------------------------------------------------------------------------
// Details:	Handle SBProcess event eBroadcastBitStateChanged.
// Type:	Method.
// Args:	vEvent			- (R) An LLDB event object.
//			vrbExitAppEvent	- (W) True - Received LLDB exit app event, false = did not.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::HandleProcessEventBroadcastBitStateChanged( const lldb::SBEvent & vEvent, bool & vrbExitAppEvent )
{
	bool bOk = ChkForStateChanges();
	bOk = bOk && GetProcessStdout();
	bOk = bOk && GetProcessStderr();
	if( !bOk )
		return MIstatus::failure;

	// Something changed in the process; get the event and report the process's current 
	// status and location
    const lldb::StateType eEventState = lldb::SBProcess::GetStateFromEvent( vEvent );
	if( eEventState == lldb::eStateInvalid )
		return MIstatus::success;

	lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent( vEvent );
	if( !process.IsValid() )
	{
		const CMIUtilString msg( CMIUtilString::Format( MIRSRC( IDS_LLDBOUTOFBAND_ERR_PROCESS_INVALID ), "SBProcess", "HandleProcessEventBroadcastBitStateChanged()" ) );
		SetErrorDescription( msg );
		return MIstatus::failure;
	}

	bool bShouldBrk = true;
	const MIchar * pEventType = "";
    switch( eEventState )
	{
	case lldb::eStateUnloaded:
		pEventType = "eStateUnloaded";
		break;
    case lldb::eStateConnected:
		pEventType = "eStateConnected";
		break;
    case lldb::eStateAttaching:
		pEventType = "eStateAttaching";
		break;
    case lldb::eStateLaunching:
		pEventType ="eStateLaunching";
		break;
    case lldb::eStateStopped:
		pEventType = "eStateStopped";
		bOk = HandleProcessEventStateStopped( bShouldBrk );
		if( bShouldBrk )
			break;
    case lldb::eStateCrashed:
	case lldb::eStateSuspended:
		pEventType = "eStateSuspended";
		bOk = HandleProcessEventStateSuspended( vEvent );
		break;
	case lldb::eStateRunning:
		pEventType = "eStateRunning";
		bOk = HandleProcessEventStateRunning();
		break;
    case lldb::eStateStepping:
		pEventType = "eStateStepping";
		break;
   case lldb::eStateDetached:
		pEventType = "eStateDetached";
		break;
    case lldb::eStateExited:
		pEventType = "eStateExited";
		vrbExitAppEvent = true;
		bOk = HandleProcessEventStateExited();
		break;
    default:
	{
		const CMIUtilString msg( CMIUtilString::Format( MIRSRC( IDS_LLDBOUTOFBAND_ERR_UNKNOWN_EVENT ), "SBProcess BroadcastBitStateChanged", (MIuint) eEventState ) );
		SetErrorDescription( msg );
		return MIstatus::failure;
	}
	}

	// ToDo: Remove when finished coding application
	m_pLog->WriteLog( CMIUtilString::Format( "##### An SB Process event BroadcastBitStateChanged occurred: %s", pEventType ) );

	return bOk;
}

//++ ------------------------------------------------------------------------------------
// Details:	Asynchronous event handler for LLDB Process state suspended.
// Type:	Method.
// Args:	vEvent	- (R) An LLDB event object.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::HandleProcessEventStateSuspended( const lldb::SBEvent & vEvent )
{
	// Make sure the program hasn't been auto-restarted:
    if( lldb::SBProcess::GetRestartedFromEvent( vEvent ) )
		return MIstatus::success;

	bool bOk = MIstatus::success;
	lldb::SBDebugger & rDebugger = CMICmnLLDBDebugSessionInfo::Instance().m_rLldbDebugger;
	lldb::SBProcess & rProcess = CMICmnLLDBDebugSessionInfo::Instance().m_lldbProcess;
	lldb::SBTarget target = rProcess.GetTarget();
	if( rDebugger.GetSelectedTarget() == target )
	{
		if( !UpdateSelectedThread() )
			return MIstatus::failure;

		lldb::SBCommandReturnObject result;
		const lldb::ReturnStatus status = rDebugger.GetCommandInterpreter().HandleCommand( "process status", result, false ); MIunused( status );
		bOk = TextToStderr( result.GetError() );
		bOk = bOk && TextToStdout( result.GetOutput() );
	}
	else
	{
		lldb::SBStream streamOut;
		const MIuint nTargetIndex = rDebugger.GetIndexOfTarget( target );
		if( nTargetIndex != UINT_MAX )
			streamOut.Printf( "Target %d: (", nTargetIndex );
		else
			streamOut.Printf( "Target <unknown index>: (" );
		target.GetDescription( streamOut, lldb::eDescriptionLevelBrief );
		streamOut.Printf( ") stopped.\n" );
		bOk = TextToStdout( streamOut.GetData() );
	}
	
	return bOk;
}
		
//++ ------------------------------------------------------------------------------------
// Details:	Print to stdout MI formatted text to indicate process stopped.
// Type:	Method.
// Args:	vwrbShouldBrk	- (W) True = Yes break, false = do not.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::HandleProcessEventStateStopped( bool & vwrbShouldBrk )
{
	if( !UpdateSelectedThread() )
		return MIstatus::failure;

	const MIchar * pEventType = "";
	bool bOk = MIstatus::success;
	lldb::SBProcess & rProcess = CMICmnLLDBDebugSessionInfo::Instance().m_lldbProcess;
	const lldb::StopReason eStoppedReason = rProcess.GetSelectedThread().GetStopReason();
	switch( eStoppedReason )
	{
	case lldb::eStopReasonInvalid:
		pEventType = "eStopReasonInvalid";
		vwrbShouldBrk = false;
		break;
    case lldb::eStopReasonNone:
		pEventType = "eStopReasonNone";
		break;
    case lldb::eStopReasonTrace:
		pEventType = "eStopReasonTrace";
		bOk = HandleProcessEventStopReasonTrace();
		break;
    case lldb::eStopReasonBreakpoint:
		pEventType = "eStopReasonBreakpoint";
		bOk = HandleProcessEventStopReasonBreakpoint();
		break;
    case lldb::eStopReasonWatchpoint:
		pEventType = "eStopReasonWatchpoint";
		break;
    case lldb::eStopReasonSignal:
		pEventType = "eStopReasonSignal";
		bOk = HandleProcessEventStopSignal( vwrbShouldBrk );
		break;
    case lldb::eStopReasonException:
		pEventType ="eStopReasonException";
		break;
    case lldb::eStopReasonExec:
		pEventType = "eStopReasonExec";
		break;
    case lldb::eStopReasonPlanComplete:
		pEventType = "eStopReasonPlanComplete";
		bOk = HandleProcessEventStopReasonTrace();
		break;
    case lldb::eStopReasonThreadExiting:
		pEventType = "eStopReasonThreadExiting";
		break;
	default:
	{
		vwrbShouldBrk = false;
		
		// MI print "*stopped,reason=\"%d\",stopped-threads=\"all\",from-thread=\"%u\""
		const CMIUtilString strReason( CMIUtilString::Format( "%d", eStoppedReason ) );
		const CMICmnMIValueConst miValueConst( strReason );
		const CMICmnMIValueResult miValueResult( "reason", miValueConst );
		CMICmnMIOutOfBandRecord miOutOfBandRecord( CMICmnMIOutOfBandRecord::eOutOfBand_Stopped, miValueResult );
		const CMICmnMIValueConst miValueConst2( "all" );
		const CMICmnMIValueResult miValueResult2( "stopped-threads", miValueConst2 );
		bOk = miOutOfBandRecord.Add( miValueResult2 );
		const CMIUtilString strFromThread( CMIUtilString::Format( "%u", rProcess.GetSelectedThread().GetIndexID() ) );
		const CMICmnMIValueConst miValueConst3( strFromThread );
		const CMICmnMIValueResult miValueResult3( "from-thread", miValueConst3 );
		bOk = bOk && miOutOfBandRecord.Add( miValueResult3 );
		bOk = bOk && MiOutOfBandRecordToStdout( miOutOfBandRecord );
		bOk = bOk && TextToStdout( "(gdb)" );
	}
	}
	
	// ToDo: Remove when finished coding application
	m_pLog->WriteLog( CMIUtilString::Format( "##### An SB Process event stop state occurred: %s", pEventType ) );

	return bOk;
}

//++ ------------------------------------------------------------------------------------
// Details:	Asynchronous event handler for LLDB Process stop signal.
// Type:	Method.
// Args:	vwrbShouldBrk	- (W) True = Yes break, false = do not.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::HandleProcessEventStopSignal( bool & vwrbShouldBrk )
{
	bool bOk = MIstatus::success;

	lldb::SBProcess & rProcess = CMICmnLLDBDebugSessionInfo::Instance().m_lldbProcess;
	const MIuint64 nStopReason = rProcess.GetSelectedThread().GetStopReasonDataAtIndex( 0 );
	switch( nStopReason )
	{
	case 2:	// Terminal interrupt signal. SIGINT
		{
			// MI print "*stopped,reason=\"signal-received\",signal-name=\"SIGNINT\",signal-meaning=\"Interrupt\",frame={%s}"
			const CMICmnMIValueConst miValueConst( "signal-received" );
			const CMICmnMIValueResult miValueResult( "reason", miValueConst );
			CMICmnMIOutOfBandRecord miOutOfBandRecord( CMICmnMIOutOfBandRecord::eOutOfBand_Stopped, miValueResult );
			const CMICmnMIValueConst miValueConst2( "SIGINT" );
			const CMICmnMIValueResult miValueResult2( "signal-name", miValueConst2 );
			bOk = miOutOfBandRecord.Add( miValueResult2 );
			const CMICmnMIValueConst miValueConst3( "Interrupt" );
			const CMICmnMIValueResult miValueResult3( "signal-meaning", miValueConst3 );
			bOk = bOk && miOutOfBandRecord.Add( miValueResult3 );
			CMICmnMIValueTuple miValueTuple;
			bOk = bOk && MiHelpGetCurrentThreadFrame( miValueTuple );
			const CMICmnMIValueResult miValueResult5( "frame", miValueTuple );
			bOk = bOk && miOutOfBandRecord.Add( miValueResult5 );
			bOk = bOk && MiOutOfBandRecordToStdout( miOutOfBandRecord );
			bOk = bOk && TextToStdout( "(gdb)" );
		}
		break;
	case 11: // Invalid memory reference. SIGSEGV
		{
			// MI print "*stopped,reason=\"signal-received\",signal-name=\"SIGSEGV\",signal-meaning=\"Segmentation fault\",thread-id=\"%d\",frame={%s}"
			const CMICmnMIValueConst miValueConst( "signal-received" );
			const CMICmnMIValueResult miValueResult( "reason", miValueConst );
			CMICmnMIOutOfBandRecord miOutOfBandRecord( CMICmnMIOutOfBandRecord::eOutOfBand_Stopped, miValueResult );
			const CMICmnMIValueConst miValueConst2( "SIGSEGV" );
			const CMICmnMIValueResult miValueResult2( "signal-name", miValueConst2 );
			bOk = miOutOfBandRecord.Add( miValueResult2 );
			const CMICmnMIValueConst miValueConst3( "Segmentation fault" );
			const CMICmnMIValueResult miValueResult3( "signal-meaning", miValueConst3 );
			bOk = bOk && miOutOfBandRecord.Add( miValueResult3 );
			const CMIUtilString strThreadId( CMIUtilString::Format( "%d", rProcess.GetSelectedThread().GetIndexID() ) );
			const CMICmnMIValueConst miValueConst4( strThreadId );
			const CMICmnMIValueResult miValueResult4( "thread-id", miValueConst4 );
			bOk = bOk && miOutOfBandRecord.Add( miValueResult4 );
			CMICmnMIValueTuple miValueTuple;
			bOk = bOk && MiHelpGetCurrentThreadFrame( miValueTuple );
			const CMICmnMIValueResult miValueResult5( "frame", miValueTuple );
			bOk = bOk && miOutOfBandRecord.Add( miValueResult5 );
			bOk = bOk && MiOutOfBandRecordToStdout( miOutOfBandRecord );
			// Note no "(gdb)" output here
		}
		break;
	case 19:
		if( rProcess.IsValid() )
			rProcess.Continue();
		break;
	case 5:	//  Trace/breakpoint trap. SIGTRAP
	{
		lldb::SBThread thread = rProcess.GetSelectedThread();
		const MIuint nFrames = thread.GetNumFrames();
		if( nFrames > 0 )
		{
			lldb::SBFrame frame = thread.GetFrameAtIndex( 0 );
			const char * pFnName = frame.GetFunctionName();
			if( pFnName != nullptr )
			{
				const CMIUtilString fnName = CMIUtilString( pFnName );
				static const CMIUtilString threadCloneFn = CMIUtilString( "__pthread_clone" );

				if( CMIUtilString::Compare( threadCloneFn, fnName ) )
				{
					if( rProcess.IsValid() )
					{
						rProcess.Continue();
						vwrbShouldBrk = true;
						break;
					}
				}
			}
		}
	}
	default:
	{
		// MI print "*stopped,reason=\"signal-received\",signal=\"%lld\",stopped-threads=\"all\""
		const CMICmnMIValueConst miValueConst( "signal-received" );
		const CMICmnMIValueResult miValueResult( "reason", miValueConst );
		CMICmnMIOutOfBandRecord miOutOfBandRecord( CMICmnMIOutOfBandRecord::eOutOfBand_Stopped, miValueResult );
		const CMIUtilString strReason( CMIUtilString::Format( "%lld", nStopReason ) );
		const CMICmnMIValueConst miValueConst2( strReason );
		const CMICmnMIValueResult miValueResult2( "signal", miValueConst2 );
		bOk = miOutOfBandRecord.Add( miValueResult2 );
		const CMICmnMIValueConst miValueConst3( "all" );
		const CMICmnMIValueResult miValueResult3( "stopped-threads", miValueConst3 );
		bOk = bOk && miOutOfBandRecord.Add( miValueResult3 );
		bOk = bOk && MiOutOfBandRecordToStdout( miOutOfBandRecord );
		bOk = bOk && TextToStdout( "(gdb)" );
	}
	} // switch( nStopReason )
	 
	return bOk;
}
	
//++ ------------------------------------------------------------------------------------
// Details:	Form partial MI response in a MI value tuple object.
// Type:	Method.
// Args:	vwrMiValueTuple	- (W) MI value tuple object.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::MiHelpGetCurrentThreadFrame( CMICmnMIValueTuple & vwrMiValueTuple )
{
	CMIUtilString strThreadFrame;
	lldb::SBProcess & rProcess = CMICmnLLDBDebugSessionInfo::Instance().m_lldbProcess;
	lldb::SBThread thread = rProcess.GetSelectedThread();
	const MIuint nFrame = thread.GetNumFrames();
	if( nFrame == 0 )
	{
		// MI print "addr=\"??\",func=\"??\",args=[],file=\"??\",fullname=\"??\",line=\"??\""
		const CMICmnMIValueConst miValueConst( "??" );
		const CMICmnMIValueResult miValueResult( "addr", miValueConst );
		CMICmnMIValueTuple miValueTuple( miValueResult );
		const CMICmnMIValueResult miValueResult2( "func", miValueConst );
		miValueTuple.Add( miValueResult2 );
		const CMICmnMIValueConst miValueConst2( "[]", true );
		const CMICmnMIValueResult miValueResult3( "args", miValueConst2 );
		miValueTuple.Add( miValueResult3 );
		const CMICmnMIValueResult miValueResult4( "file", miValueConst );
		miValueTuple.Add( miValueResult4 );
		const CMICmnMIValueResult miValueResult5( "fullname", miValueConst );
		miValueTuple.Add( miValueResult5 );
		const CMICmnMIValueResult miValueResult6( "line", miValueConst );
		miValueTuple.Add( miValueResult6 );

		vwrMiValueTuple = miValueTuple;

		return MIstatus::success;
	}

	CMICmnMIValueTuple miValueTuple;
	if( !CMICmnLLDBDebugSessionInfo::Instance().MIResponseFormFrameInfo( thread, 0, miValueTuple ) )
	{
		SetErrorDescription( CMIUtilString::Format( MIRSRC( IDS_LLDBOUTOFBAND_ERR_FORM_MI_RESPONSE ), "MiHelpGetCurrentThreadFrame()" ) );
		return MIstatus::failure;
	}
		
	vwrMiValueTuple = miValueTuple;

	return MIstatus::success;
}
	
//++ ------------------------------------------------------------------------------------
// Details:	Asynchronous event handler for LLDB Process stop reason breakpoint.
// Type:	Method.
// Args:	None.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::HandleProcessEventStopReasonBreakpoint( void )
{
	// CODETAG_DEBUG_SESSION_RUNNING_PROG_RECEIVED_SIGINT_PAUSE_PROGRAM
	if( !CMIDriver::Instance().SetDriverStateRunningNotDebugging() )
	{
		const CMIUtilString & rErrMsg( CMIDriver::Instance().GetErrorDescription() );
		SetErrorDescription( CMIUtilString::Format( MIRSRC( IDS_LLDBOUTOFBAND_ERR_SETNEWDRIVERSTATE ), "HandleProcessEventStopReasonBreakpoint()", rErrMsg.c_str() ) );
		return MIstatus::failure;
	}

	lldb::SBProcess & rProcess = CMICmnLLDBDebugSessionInfo::Instance().m_lldbProcess;
	const MIuint64 brkPtId = rProcess.GetSelectedThread().GetStopReasonDataAtIndex( 0 );
	lldb::SBBreakpoint brkPt = CMICmnLLDBDebugSessionInfo::Instance().m_lldbTarget.GetBreakpointAtIndex( (MIuint) brkPtId );
	
	return MiStoppedAtBreakPoint( brkPtId, brkPt );
}

//++ ------------------------------------------------------------------------------------
// Details:	Form the MI Out-of-band response for stopped reason on hitting a break point.
// Type:	Method.
// Args:	vBrkPtId	- (R) The LLDB break point's ID
//			vBrkPt		- (R) THe LLDB break point object.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::MiStoppedAtBreakPoint( const MIuint64 vBrkPtId, const lldb::SBBreakpoint & vBrkPt )
{
	bool bOk = MIstatus::success;

	lldb::SBProcess & rProcess = CMICmnLLDBDebugSessionInfo::Instance().m_lldbProcess;
	lldb::SBThread thread = rProcess.GetSelectedThread();
	const MIuint nFrame = thread.GetNumFrames();
	if( nFrame == 0 )
	{
		// MI print "*stopped,reason=\"breakpoint-hit\",disp=\"del\",bkptno=\"%d\",frame={},thread-id=\"%d\",stopped-threads=\"all\""
		const CMICmnMIValueConst miValueConst( "breakpoint-hit" );
		const CMICmnMIValueResult miValueResult( "reason", miValueConst );
		CMICmnMIOutOfBandRecord miOutOfBandRecord( CMICmnMIOutOfBandRecord::eOutOfBand_Stopped, miValueResult );
		const CMICmnMIValueConst miValueConst2( "del" );
		const CMICmnMIValueResult miValueResult2( "disp", miValueConst2 );
		bOk = miOutOfBandRecord.Add( miValueResult2 );
		const CMIUtilString strBkp( CMIUtilString::Format( "%d", vBrkPtId ) );
		const CMICmnMIValueConst miValueConst3( strBkp );
		CMICmnMIValueResult miValueResult3( "bkptno", miValueConst3 );
		bOk = bOk && miOutOfBandRecord.Add( miValueResult3 );
		const CMICmnMIValueConst miValueConst4( "{}" );
		const CMICmnMIValueResult miValueResult4( "frame", miValueConst4 );
		bOk = bOk && miOutOfBandRecord.Add( miValueResult4 );
		const CMIUtilString strThreadId( CMIUtilString::Format( "%d", vBrkPt.GetThreadIndex() ) );
		const CMICmnMIValueConst miValueConst5( strThreadId );
		const CMICmnMIValueResult miValueResult5( "thread-id", miValueConst5 );
		bOk = bOk && miOutOfBandRecord.Add( miValueResult5 );
		const CMICmnMIValueConst miValueConst6( "all" );
		const CMICmnMIValueResult miValueResult6( "stopped-threads", miValueConst6 );
		bOk = bOk && miOutOfBandRecord.Add( miValueResult6 );
		bOk = bOk && MiOutOfBandRecordToStdout( miOutOfBandRecord );
		bOk = bOk && TextToStdout( "(gdb)" );
		return bOk;
	}

	CMICmnLLDBDebugSessionInfo & rSession =  CMICmnLLDBDebugSessionInfo::Instance();

	lldb::SBFrame frame = thread.GetFrameAtIndex( 0 );
	lldb::addr_t pc = 0;
	CMIUtilString fnName;
	CMIUtilString fileName;
	CMIUtilString path; 
	MIuint nLine = 0;
	if( !rSession.GetFrameInfo( frame, pc, fnName, fileName, path, nLine ) )
	{
		SetErrorDescription( CMIUtilString::Format( MIRSRC( IDS_LLDBOUTOFBAND_ERR_FRAME_INFO_GET ), "MiStoppedAtBreakPoint()" ) );
		return MIstatus::failure;
	}
	
	// MI print "*stopped,reason=\"breakpoint-hit\",disp=\"del\",bkptno=\"%d\",frame={addr=\"0x%08x\",func=\"%s\",args=[],file=\"%s\",fullname=\"%s\",line=\"%d\"},thread-id=\"%d\",stopped-threads=\"all\""
	const CMICmnMIValueConst miValueConst( "breakpoint-hit" );
	const CMICmnMIValueResult miValueResult( "reason", miValueConst );
	CMICmnMIOutOfBandRecord miOutOfBandRecord( CMICmnMIOutOfBandRecord::eOutOfBand_Stopped, miValueResult );
	const CMICmnMIValueConst miValueConstA( "del" );
	const CMICmnMIValueResult miValueResultA( "disp", miValueConstA );
	bOk = miOutOfBandRecord.Add( miValueResultA );
	const CMIUtilString strBkp( CMIUtilString::Format( "%d", vBrkPtId ) );
	const CMICmnMIValueConst miValueConstB( strBkp );
	CMICmnMIValueResult miValueResultB( "bkptno", miValueConstB );
	bOk = bOk && miOutOfBandRecord.Add( miValueResultB );

	// frame={addr=\"0x%08x\",func=\"%s\",args=[],file=\"%s\",fullname=\"%s\",line=\"%d\"}
	if( bOk )
	{
		CMICmnMIValueList miValueList( true );
		const MIuint vMaskVarTypes = 0x1000;
		bOk = rSession.MIResponseFormVariableInfo( frame, vMaskVarTypes, miValueList );
		CMICmnMIValueTuple miValueTuple; 
		bOk = bOk && rSession.MIResponseFormFrameInfo( pc, fnName, miValueList.GetString(), fileName, path, nLine, miValueTuple );
		const CMICmnMIValueResult miValueResult8( "frame", miValueTuple );
		bOk = bOk && miOutOfBandRecord.Add( miValueResult8 );
	}

	// Add to MI thread-id=\"%d\",stopped-threads=\"all\"
	if( bOk )
	{
		const CMIUtilString strThreadId( CMIUtilString::Format( "%d", thread.GetIndexID() ) );
		const CMICmnMIValueConst miValueConst8( strThreadId );
		const CMICmnMIValueResult miValueResult8( "thread-id", miValueConst8 );
		bOk = miOutOfBandRecord.Add( miValueResult8 );
	}	
	if( bOk )
	{
		const CMICmnMIValueConst miValueConst9( "all" );
		const CMICmnMIValueResult miValueResult9( "stopped-threads", miValueConst9 );
		bOk = miOutOfBandRecord.Add( miValueResult9 );
		bOk = MiOutOfBandRecordToStdout( miOutOfBandRecord );
		bOk = bOk && TextToStdout( "(gdb)" );
	}	

	return MIstatus::success;
}
	
//++ ------------------------------------------------------------------------------------
// Details:	Asynchronous event handler for LLDB Process stop reason trace.
// Type:	Method.
// Args:	None.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::HandleProcessEventStopReasonTrace( void )
{
	bool bOk = true;
	lldb::SBProcess & rProcess = CMICmnLLDBDebugSessionInfo::Instance().m_lldbProcess;
	lldb::SBThread thread = rProcess.GetSelectedThread();
	const MIuint nFrame = thread.GetNumFrames();
	if( nFrame == 0 )
	{
		// MI print "*stopped,reason=\"trace\",stopped-threads=\"all\""
		const CMICmnMIValueConst miValueConst( "trace" );
		const CMICmnMIValueResult miValueResult( "reason", miValueConst );
		CMICmnMIOutOfBandRecord miOutOfBandRecord( CMICmnMIOutOfBandRecord::eOutOfBand_Stopped, miValueResult );
		const CMICmnMIValueConst miValueConst2( "all" );
		const CMICmnMIValueResult miValueResult2( "stopped-threads", miValueConst2 );
		bOk = miOutOfBandRecord.Add( miValueResult2 );
		bOk = MiOutOfBandRecordToStdout( miOutOfBandRecord );
		bOk = bOk && TextToStdout( "(gdb)" );
		return bOk;
	}

	CMICmnLLDBDebugSessionInfo & rSession = CMICmnLLDBDebugSessionInfo::Instance();

	// MI print "*stopped,reason=\"end-stepping-range\",frame={addr=\"0x%08x\",func=\"%s\",args=[],file=\"%s\",fullname=\"%s\",line=\"%d\"},thread-id=\"%d\",stopped-threads=\"all\""
	lldb::SBFrame frame = thread.GetFrameAtIndex( 0 );
	lldb::addr_t pc = 0;
	CMIUtilString fnName;
	CMIUtilString fileName;
	CMIUtilString path; 
	MIuint nLine = 0;
	if( !rSession.GetFrameInfo( frame, pc, fnName, fileName, path, nLine ) )
	{
		SetErrorDescription( CMIUtilString::Format( MIRSRC( IDS_LLDBOUTOFBAND_ERR_FRAME_INFO_GET ), "HandleProcessEventStopReasonTrace()" ) );
		return MIstatus::failure;
	}

	// frame={addr=\"0x%08x\",func=\"%s\",args=[],file=\"%s\",fullname=\"%s\",line=\"%d\"}
	CMICmnMIValueTuple miValueTuple;
	if( !rSession.MIResponseFormFrameInfo( pc, fnName, "[]", fileName, path, nLine, miValueTuple ) )
		return MIstatus::failure;

	const CMICmnMIValueConst miValueConst( "end-stepping-range" );
	const CMICmnMIValueResult miValueResult( "reason", miValueConst );
	CMICmnMIOutOfBandRecord miOutOfBandRecord( CMICmnMIOutOfBandRecord::eOutOfBand_Stopped, miValueResult );
	bOk = miOutOfBandRecord.Add( miValueTuple );

	// Add to MI thread-id=\"%d\",stopped-threads=\"all\"
	if( bOk )
	{
		const CMIUtilString strThreadId( CMIUtilString::Format( "%d", thread.GetIndexID() ) );
		const CMICmnMIValueConst miValueConst8( strThreadId );
		const CMICmnMIValueResult miValueResult8( "thread-id", miValueConst8 );
		bOk = miOutOfBandRecord.Add( miValueResult8 );
	}	
	if( bOk )
	{
		const CMICmnMIValueConst miValueConst9( "all" );
		const CMICmnMIValueResult miValueResult9( "stopped-threads", miValueConst9 );
		bOk = miOutOfBandRecord.Add( miValueResult9 );
		bOk = MiOutOfBandRecordToStdout( miOutOfBandRecord );
		bOk = bOk && TextToStdout( "(gdb)" );
	}	

	return bOk;
}

//++ ------------------------------------------------------------------------------------
// Details:	Asynchronous function update selected thread.
// Type:	Method.
// Args:	None.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::UpdateSelectedThread( void )
{
	lldb::SBProcess process = CMICmnLLDBDebugSessionInfo::Instance().m_rLldbDebugger.GetSelectedTarget().GetProcess();
	if( !process.IsValid() )
		return MIstatus::success;

	lldb::SBThread currentThread = process.GetSelectedThread();
	lldb::SBThread thread;
	const lldb::StopReason eCurrentThreadStoppedReason = currentThread.GetStopReason();
	if( !currentThread.IsValid() || (eCurrentThreadStoppedReason == lldb::eStopReasonInvalid) || (eCurrentThreadStoppedReason == lldb::eStopReasonNone) )
	{
		// Prefer a thread that has just completed its plan over another thread as current thread
		lldb::SBThread planThread;
		lldb::SBThread otherThread;
		const size_t nThread = process.GetNumThreads();
		for( MIuint i = 0; i < nThread; i++ )
		{
			//	GetThreadAtIndex() uses a base 0 index
			//	GetThreadByIndexID() uses a base 1 index
			thread = process.GetThreadAtIndex( i );
			const lldb::StopReason eThreadStopReason = thread.GetStopReason();
			switch( eThreadStopReason )
			{
                case lldb::eStopReasonTrace:
                case lldb::eStopReasonBreakpoint:
                case lldb::eStopReasonWatchpoint:
                case lldb::eStopReasonSignal:
                case lldb::eStopReasonException:
                    if( !otherThread.IsValid() )
                        otherThread = thread;
                    break;
                case lldb::eStopReasonPlanComplete:
                    if( !planThread.IsValid() )
                        planThread = thread;
                    break;
				case lldb::eStopReasonInvalid:
                case lldb::eStopReasonNone:
                default:
                    break;
			}
		}
		if( planThread.IsValid() )
			process.SetSelectedThread( planThread );
		else if( otherThread.IsValid() )
			process.SetSelectedThread( otherThread );
		else
		{
			if( currentThread.IsValid() )
				thread = currentThread;
			else
				thread = process.GetThreadAtIndex( 0 );

			if( thread.IsValid() )
				process.SetSelectedThread( thread );
		}
	} // if( !currentThread.IsValid() || (eCurrentThreadStoppedReason == lldb::eStopReasonInvalid) || (eCurrentThreadStoppedReason == lldb::eStopReasonNone) )
	
	return MIstatus::success;
}

//++ ------------------------------------------------------------------------------------
// Details:	Print to stdout "*running,thread-id=\"all\"", "(gdb)".
// Type:	Method.
// Args:	None.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::HandleProcessEventStateRunning( void )
{
	CMICmnMIValueConst miValueConst( "all" );
	CMICmnMIValueResult miValueResult( "thread-id", miValueConst );
	CMICmnMIOutOfBandRecord miOutOfBandRecord( CMICmnMIOutOfBandRecord::eOutOfBand_Running, miValueResult );
	bool bOk = MiOutOfBandRecordToStdout( miOutOfBandRecord );
	bOk = bOk && TextToStdout( "(gdb)" );

	return bOk;
}

//++ ------------------------------------------------------------------------------------
// Details:	Print to stdout "=thread-exited,id=\"%ld\",group-id=\"i1\"", 
//							"=thread-group-exited,id=\"i1\",exit-code=\"0\""),
//							"*stopped,reason=\"exited-normally\"",
//							"(gdb)"
// Type:	Method.
// Args:	None.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::HandleProcessEventStateExited( void )
{
	const CMIUtilString strId( CMIUtilString::Format( "%ld", 1 ) );
	CMICmnMIValueConst miValueConst( strId );
	CMICmnMIValueResult miValueResult( "id", miValueConst );
	CMICmnMIOutOfBandRecord miOutOfBandRecord( CMICmnMIOutOfBandRecord::eOutOfBand_ThreadExited, miValueResult );
	CMICmnMIValueConst miValueConst2( "i1" );
	CMICmnMIValueResult miValueResult2( "group-id", miValueConst2 );
	bool bOk = miOutOfBandRecord.Add( miValueResult2 );
	bOk = bOk && MiOutOfBandRecordToStdout( miOutOfBandRecord );
	if( bOk )
	{
		CMICmnMIValueConst miValueConst3( "i1" );
		CMICmnMIValueResult miValueResult3( "id", miValueConst3 );
		CMICmnMIOutOfBandRecord miOutOfBandRecord2( CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupExited, miValueResult3 );
		CMICmnMIValueConst miValueConst2( "0" );
		CMICmnMIValueResult miValueResult2( "exit-code", miValueConst2 );
		bOk = miOutOfBandRecord2.Add( miValueResult2 );
		bOk = bOk && MiOutOfBandRecordToStdout( miOutOfBandRecord2 );
	}	
	if( bOk )
	{
		CMICmnMIValueConst miValueConst4( "exited-normally" );
		CMICmnMIValueResult miValueResult4( "reason", miValueConst4 );
		CMICmnMIOutOfBandRecord miOutOfBandRecord3( CMICmnMIOutOfBandRecord::eOutOfBand_Stopped, miValueResult4 );
		bOk = MiOutOfBandRecordToStdout( miOutOfBandRecord3 );
	}	
	bOk = bOk && TextToStdout( "(gdb)" );

	return bOk;
}

//++ ------------------------------------------------------------------------------------
// Details:	Drain all stdout so we don't see any output come after we print our prompts. 
//			The process has stuff waiting for stdout; get it and write it out to the 
//			appropriate place.
// Type:	Method.
// Args:	None.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::GetProcessStdout( void )
{
	bool bOk = MIstatus::success;

	char c;
	size_t nBytes = 0;
	CMIUtilString text;
	lldb::SBProcess process = CMICmnLLDBDebugSessionInfo::Instance().m_rLldbDebugger.GetSelectedTarget().GetProcess();
	while( process.GetSTDOUT( &c, 1 ) > 0 )
	{
		CMIUtilString str;
		if( ConvertPrintfCtrlCodeToString( c, str ) )
			text += str;
		nBytes++;
	}
	if( nBytes > 0 )
	{
		const CMIUtilString t( CMIUtilString::Format( "~\"%s\"", text.c_str() ) );
		bOk = TextToStdout( t );
	}
	
	return bOk;
}

//++ ------------------------------------------------------------------------------------
// Details:	Drain all stderr so we don't see any output come after we print our prompts.
//			The process has stuff waiting for stderr; get it and write it out to the 
//			appropriate place.
// Type:	Method.
// Args:	None.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::GetProcessStderr( void )
{
	bool bOk = MIstatus::success;

	char c;
	size_t nBytes = 0;
	CMIUtilString text;
	lldb::SBProcess process = CMICmnLLDBDebugSessionInfo::Instance().m_rLldbDebugger.GetSelectedTarget().GetProcess();
	while( process.GetSTDERR( &c, 1 ) > 0 )
	{
		CMIUtilString str;
		if( ConvertPrintfCtrlCodeToString( c, str ) )
			text += str;
		nBytes++;
	}
	if( nBytes > 0 )
	{
		const CMIUtilString t( CMIUtilString::Format( "~\"%s\"", text.c_str() ) );
		bOk = TextToStdout( t );
	}
	
	return bOk;
}
	
//++ ------------------------------------------------------------------------------------
// Details:	Convert text stream control codes to text equivalent.
// Type:	Method.
// Args:	vCtrl				- (R) The control code.
//			vwrStrEquivalent	- (W) The text equivalent.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::ConvertPrintfCtrlCodeToString( const MIchar vCtrl, CMIUtilString & vwrStrEquivalent )
{
	switch( vCtrl )
	{
	    case '\033': vwrStrEquivalent = "\\e"; break;
		case '\a': vwrStrEquivalent = "\\a"; break;
		case '\b': vwrStrEquivalent = "\\b"; break;
		case '\f': vwrStrEquivalent = "\\f"; break;
		case '\n': vwrStrEquivalent = "\\n"; break;
		case '\r': vwrStrEquivalent = "\\r"; break;
		case '\t': vwrStrEquivalent = "\\t"; break;
		case '\v': vwrStrEquivalent = "\\v"; break;
		default: 
			vwrStrEquivalent = CMIUtilString::Format( "%c", vCtrl ); 
			break;
	}

	return MIstatus::success;
}

//++ ------------------------------------------------------------------------------------
// Details:	Asynchronous event function check for state changes.
// Type:	Method.
// Args:	None.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::ChkForStateChanges( void )
{
	lldb::SBProcess & rProcess = CMICmnLLDBDebugSessionInfo::Instance().m_lldbProcess;
	if( !rProcess.IsValid() )
		return MIstatus::success;
	lldb::SBTarget & rTarget = CMICmnLLDBDebugSessionInfo::Instance().m_lldbTarget;
	if( !rTarget.IsValid() )
		return MIstatus::success;

	bool bOk = MIstatus::success;

	// Check for created threads
    const MIuint nThread = rProcess.GetNumThreads();
	for( MIuint i = 0; i < nThread; i++ )
	{
		//	GetThreadAtIndex() uses a base 0 index
		//	GetThreadByIndexID() uses a base 1 index
		lldb::SBThread thread = rProcess.GetThreadAtIndex( i );
		if( !thread.IsValid() )
			continue;

		CMICmnLLDBDebugSessionInfo::VecActiveThreadId_t::const_iterator it = CMICmnLLDBDebugSessionInfo::Instance().m_vecActiveThreadId.begin();
		bool bFound = false;
		while( it != CMICmnLLDBDebugSessionInfo::Instance().m_vecActiveThreadId.end() )
		{
			const MIuint nThreadId = *it;
			if( nThreadId == i )
			{
				bFound = true;
				break;
			}

			// Next
			++it;
		}
		if( !bFound )
		{
			CMICmnLLDBDebugSessionInfo::Instance().m_vecActiveThreadId.push_back( i );

			// Form MI "=thread-created,id=\"%d\",group-id=\"i1\""
			const CMIUtilString strValue( CMIUtilString::Format( "%d", thread.GetIndexID() ) );
			const CMICmnMIValueConst miValueConst( strValue );
			const CMICmnMIValueResult miValueResult( "id", miValueConst );
			CMICmnMIOutOfBandRecord miOutOfBand( CMICmnMIOutOfBandRecord::eOutOfBand_ThreadCreated, miValueResult );
			const CMICmnMIValueConst miValueConst2( "i1" );
			const CMICmnMIValueResult miValueResult2( "group-id", miValueConst2 );
			bOk = miOutOfBand.Add( miValueResult2 );
			bOk = bOk && MiOutOfBandRecordToStdout( miOutOfBand );
			if( !bOk )
				return MIstatus::failure;
		}
	}

	lldb::SBThread currentThread = rProcess.GetSelectedThread();
	if( currentThread.IsValid() )
	{
		const MIuint threadId = currentThread.GetIndexID();
		if( CMICmnLLDBDebugSessionInfo::Instance().m_currentSelectedThread != threadId )
		{
			CMICmnLLDBDebugSessionInfo::Instance().m_currentSelectedThread = threadId;

			// Form MI "=thread-selected,id=\"%d\""
			const CMIUtilString strValue( CMIUtilString::Format( "%d", currentThread.GetIndexID() ) );
			const CMICmnMIValueConst miValueConst( strValue );
			const CMICmnMIValueResult miValueResult( "id", miValueConst );
			CMICmnMIOutOfBandRecord miOutOfBand( CMICmnMIOutOfBandRecord::eOutOfBand_ThreadSelected, miValueResult );
			if( !MiOutOfBandRecordToStdout( miOutOfBand ) )
				return MIstatus::failure;
		}
	}

	// Check for invalid (removed) threads
	CMICmnLLDBDebugSessionInfo::VecActiveThreadId_t::const_iterator it = CMICmnLLDBDebugSessionInfo::Instance().m_vecActiveThreadId.begin();
	while( it != CMICmnLLDBDebugSessionInfo::Instance().m_vecActiveThreadId.end() )
	{
		const MIuint nThreadId = *it;
		lldb::SBThread thread = rProcess.GetThreadAtIndex( nThreadId );
		if( !thread.IsValid() )
		{
			// Form MI "=thread-exited,id=\"%ld\",group-id=\"i1\""
			const CMIUtilString strValue( CMIUtilString::Format( "%ld", thread.GetIndexID() ) );
			const CMICmnMIValueConst miValueConst( strValue );
			const CMICmnMIValueResult miValueResult( "id", miValueConst );
			CMICmnMIOutOfBandRecord miOutOfBand( CMICmnMIOutOfBandRecord::eOutOfBand_ThreadExited, miValueResult );
			const CMICmnMIValueConst miValueConst2( "i1" );
			const CMICmnMIValueResult miValueResult2( "group-id", miValueConst2 );
			bOk = miOutOfBand.Add( miValueResult2 );
			bOk = bOk && MiOutOfBandRecordToStdout( miOutOfBand );
			if( !bOk )
				return MIstatus::failure;
		}

		// Next
		++it;
	}
			
	return TextToStdout( "(gdb)" ); 
}

//++ ------------------------------------------------------------------------------------
// Details:	Take a fully formed MI result record and send to the stdout stream.
//			Also output to the MI Log file.
// Type:	Method.
// Args:	vrMiResultRecord	- (R) MI result record object.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::MiResultRecordToStdout( const CMICmnMIResultRecord & vrMiResultRecord )
{
	return TextToStdout( vrMiResultRecord.GetString() );
}

//++ ------------------------------------------------------------------------------------
// Details:	Take a fully formed MI Out-of-band record and send to the stdout stream. 
//			Also output to the MI Log file.
// Type:	Method.
// Args:	vrMiOutOfBandRecord	- (R) MI Out-of-band record object.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::MiOutOfBandRecordToStdout( const CMICmnMIOutOfBandRecord & vrMiOutOfBandRecord )
{
	return TextToStdout( vrMiOutOfBandRecord.GetString() );
}

//++ ------------------------------------------------------------------------------------
// Details:	Take a text data and send to the stdout stream. Also output to the MI Log
//			file.
// Type:	Method.
// Args:	vrTxt	- (R) Text.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::TextToStdout( const CMIUtilString & vrTxt )
{
	return CMICmnStreamStdout::TextToStdout( vrTxt );
}

//++ ------------------------------------------------------------------------------------
// Details:	Take a text data and send to the stderr stream. Also output to the MI Log
//			file.
// Type:	Method.
// Args:	vrTxt	- (R) Text.
// Return:	MIstatus::success - Functionality succeeded.
//			MIstatus::failure - Functionality failed.
// Throws:	None.
//--
bool CMICmnLLDBDebuggerHandleEvents::TextToStderr( const CMIUtilString & vrTxt )
{
	return CMICmnStreamStderr::TextToStderr( vrTxt );
}
