| //===-- MICmnLLDBDebugger.cpp -----------------------------------*- C++ -*-===// | 
 | // | 
 | //                     The LLVM Compiler Infrastructure | 
 | // | 
 | // This file is distributed under the University of Illinois Open Source | 
 | // License. See LICENSE.TXT for details. | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | // Third party headers: | 
 | #include "lldb/API/SBCommandInterpreter.h" | 
 | #include "lldb/API/SBProcess.h" | 
 | #include "lldb/API/SBStream.h" | 
 | #include "lldb/API/SBTarget.h" | 
 | #include "lldb/API/SBThread.h" | 
 | #include "lldb/API/SBType.h" | 
 | #include "lldb/API/SBTypeCategory.h" | 
 | #include "lldb/API/SBTypeNameSpecifier.h" | 
 | #include "lldb/API/SBTypeSummary.h" | 
 | #include <cassert> | 
 |  | 
 | // In-house headers: | 
 | #include "MICmnLLDBDebugSessionInfo.h" | 
 | #include "MICmnLLDBDebugger.h" | 
 | #include "MICmnLLDBDebuggerHandleEvents.h" | 
 | #include "MICmnLog.h" | 
 | #include "MICmnResources.h" | 
 | #include "MICmnThreadMgrStd.h" | 
 | #include "MIDriverBase.h" | 
 | #include "MIUtilSingletonHelper.h" | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // MI private summary providers | 
 | static inline bool MI_char_summary_provider(lldb::SBValue value, | 
 |                                             lldb::SBTypeSummaryOptions options, | 
 |                                             lldb::SBStream &stream) { | 
 |   if (!value.IsValid()) | 
 |     return false; | 
 |  | 
 |   lldb::SBType value_type = value.GetType(); | 
 |   if (!value_type.IsValid()) | 
 |     return false; | 
 |  | 
 |   lldb::BasicType type_code = value_type.GetBasicType(); | 
 |   if (type_code == lldb::eBasicTypeSignedChar) | 
 |     stream.Printf("%d %s", (int)value.GetValueAsSigned(), value.GetValue()); | 
 |   else if (type_code == lldb::eBasicTypeUnsignedChar) | 
 |     stream.Printf("%u %s", (unsigned)value.GetValueAsUnsigned(), | 
 |                   value.GetValue()); | 
 |   else | 
 |     return false; | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // MI summary helper routines | 
 | static inline bool MI_add_summary(lldb::SBTypeCategory category, | 
 |                                   const char *typeName, | 
 |                                   lldb::SBTypeSummary::FormatCallback cb, | 
 |                                   uint32_t options, bool regex = false) { | 
 | #if defined(LLDB_DISABLE_PYTHON) | 
 |   return false; | 
 | #else | 
 |   lldb::SBTypeSummary summary = | 
 |       lldb::SBTypeSummary::CreateWithCallback(cb, options); | 
 |   return summary.IsValid() | 
 |              ? category.AddTypeSummary( | 
 |                    lldb::SBTypeNameSpecifier(typeName, regex), summary) | 
 |              : false; | 
 | #endif | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: CMICmnLLDBDebugger constructor. | 
 | // Type:    Method. | 
 | // Args:    None. | 
 | // Return:  None. | 
 | // Throws:  None. | 
 | //-- | 
 | CMICmnLLDBDebugger::CMICmnLLDBDebugger() | 
 |     : m_constStrThisThreadId("MI debugger event") {} | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: CMICmnLLDBDebugger destructor. | 
 | // Type:    Overridable. | 
 | // Args:    None. | 
 | // Return:  None. | 
 | // Throws:  None. | 
 | //-- | 
 | CMICmnLLDBDebugger::~CMICmnLLDBDebugger() { Shutdown(); } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: Initialize resources for *this debugger object. | 
 | // Type:    Method. | 
 | // Args:    None. | 
 | // Return:  MIstatus::success - Functionality succeeded. | 
 | //          MIstatus::failure - Functionality failed. | 
 | // Throws:  None. | 
 | //-- | 
 | bool CMICmnLLDBDebugger::Initialize() { | 
 |   m_clientUsageRefCnt++; | 
 |  | 
 |   if (m_bInitialized) | 
 |     return MIstatus::success; | 
 |  | 
 |   bool bOk = MIstatus::success; | 
 |   CMIUtilString errMsg; | 
 |   ClrErrorDescription(); | 
 |  | 
 |   if (m_pClientDriver == nullptr) { | 
 |     bOk = false; | 
 |     errMsg = MIRSRC(IDS_LLDBDEBUGGER_ERR_CLIENTDRIVER); | 
 |   } | 
 |  | 
 |   // Note initialization 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); | 
 |   MI::ModuleInit<CMICmnThreadMgrStd>(IDS_MI_INIT_ERR_THREADMGR, bOk, errMsg); | 
 |   MI::ModuleInit<CMICmnLLDBDebuggerHandleEvents>( | 
 |       IDS_MI_INIT_ERR_OUTOFBANDHANDLER, bOk, errMsg); | 
 |   MI::ModuleInit<CMICmnLLDBDebugSessionInfo>(IDS_MI_INIT_ERR_DEBUGSESSIONINFO, | 
 |                                              bOk, errMsg); | 
 |  | 
 |   // Note order is important here! | 
 |   if (bOk) | 
 |     lldb::SBDebugger::Initialize(); | 
 |   if (bOk && !InitSBDebugger()) { | 
 |     bOk = false; | 
 |     if (!errMsg.empty()) | 
 |       errMsg += ", "; | 
 |     errMsg += GetErrorDescription().c_str(); | 
 |   } | 
 |   if (bOk && !InitSBListener()) { | 
 |     bOk = false; | 
 |     if (!errMsg.empty()) | 
 |       errMsg += ", "; | 
 |     errMsg += GetErrorDescription().c_str(); | 
 |   } | 
 |   bOk = bOk && InitStdStreams(); | 
 |   bOk = bOk && RegisterMISummaryProviders(); | 
 |   m_bInitialized = bOk; | 
 |  | 
 |   if (!bOk && !HaveErrorDescription()) { | 
 |     CMIUtilString strInitError(CMIUtilString::Format( | 
 |         MIRSRC(IDS_MI_INIT_ERR_LLDBDEBUGGER), errMsg.c_str())); | 
 |     SetErrorDescription(strInitError); | 
 |   } | 
 |  | 
 |   return bOk; | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: Release resources for *this debugger object. | 
 | // Type:    Method. | 
 | // Args:    None. | 
 | // Return:  MIstatus::success - Functionality succeeded. | 
 | //          MIstatus::failure - Functionality failed. | 
 | // Throws:  None. | 
 | //-- | 
 | bool CMICmnLLDBDebugger::Shutdown() { | 
 |   if (--m_clientUsageRefCnt > 0) | 
 |     return MIstatus::success; | 
 |  | 
 |   if (!m_bInitialized) | 
 |     return MIstatus::success; | 
 |  | 
 |   m_bInitialized = false; | 
 |  | 
 |   ClrErrorDescription(); | 
 |  | 
 |   bool bOk = MIstatus::success; | 
 |   CMIUtilString errMsg; | 
 |  | 
 |   // Explicitly delete the remote target in case MI needs to exit prematurely | 
 |   // otherwise | 
 |   // LLDB debugger may hang in its Destroy() fn waiting on events | 
 |   lldb::SBTarget sbTarget = CMICmnLLDBDebugSessionInfo::Instance().GetTarget(); | 
 |   m_lldbDebugger.DeleteTarget(sbTarget); | 
 |  | 
 |   // Debug: May need this but does seem to work without it so commented out the | 
 |   // fudge 19/06/2014 | 
 |   // It appears we need to wait as hang does not occur when hitting a debug | 
 |   // breakpoint here | 
 |   // const std::chrono::milliseconds time( 1000 ); | 
 |   // std::this_thread::sleep_for( time ); | 
 |  | 
 |   lldb::SBDebugger::Destroy(m_lldbDebugger); | 
 |   lldb::SBDebugger::Terminate(); | 
 |   m_pClientDriver = nullptr; | 
 |   m_mapBroadcastClassNameToEventMask.clear(); | 
 |   m_mapIdToEventMask.clear(); | 
 |  | 
 |   // Note shutdown order is important here | 
 |   MI::ModuleShutdown<CMICmnLLDBDebugSessionInfo>( | 
 |       IDS_MI_INIT_ERR_DEBUGSESSIONINFO, bOk, errMsg); | 
 |   MI::ModuleShutdown<CMICmnLLDBDebuggerHandleEvents>( | 
 |       IDS_MI_INIT_ERR_OUTOFBANDHANDLER, bOk, errMsg); | 
 |   MI::ModuleShutdown<CMICmnThreadMgrStd>(IDS_MI_INIT_ERR_THREADMGR, bOk, | 
 |                                          errMsg); | 
 |   MI::ModuleShutdown<CMICmnResources>(IDS_MI_INIT_ERR_RESOURCES, bOk, errMsg); | 
 |   MI::ModuleShutdown<CMICmnLog>(IDS_MI_INIT_ERR_LOG, bOk, errMsg); | 
 |  | 
 |   if (!bOk) { | 
 |     SetErrorDescriptionn(MIRSRC(IDS_MI_SHTDWN_ERR_LLDBDEBUGGER), | 
 |                          errMsg.c_str()); | 
 |   } | 
 |  | 
 |   return MIstatus::success; | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: Return the LLDB debugger instance created for this debug session. | 
 | // Type:    Method. | 
 | // Args:    None. | 
 | // Return:  lldb::SBDebugger & - LLDB debugger object reference. | 
 | // Throws:  None. | 
 | //-- | 
 | lldb::SBDebugger &CMICmnLLDBDebugger::GetTheDebugger() { | 
 |   return m_lldbDebugger; | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: Return the LLDB listener instance created for this debug session. | 
 | // Type:    Method. | 
 | // Args:    None. | 
 | // Return:  lldb::SBListener & - LLDB listener object reference. | 
 | // Throws:  None. | 
 | //-- | 
 | lldb::SBListener &CMICmnLLDBDebugger::GetTheListener() { | 
 |   return m_lldbListener; | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: Set the client driver that wants to use *this LLDB debugger. Call | 
 | // this function | 
 | //          prior to Initialize(). | 
 | // Type:    Method. | 
 | // Args:    vClientDriver   - (R) A driver. | 
 | // Return:  MIstatus::success - Functionality succeeded. | 
 | //          MIstatus::failure - Functionality failed. | 
 | // Throws:  None. | 
 | //-- | 
 | bool CMICmnLLDBDebugger::SetDriver(const CMIDriverBase &vClientDriver) { | 
 |   m_pClientDriver = const_cast<CMIDriverBase *>(&vClientDriver); | 
 |  | 
 |   return MIstatus::success; | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: Get the client driver that is use *this LLDB debugger. | 
 | // Type:    Method. | 
 | // Args:    vClientDriver   - (R) A driver. | 
 | // Return:  CMIDriverBase & - A driver instance. | 
 | // Throws:  None. | 
 | //-- | 
 | CMIDriverBase &CMICmnLLDBDebugger::GetDriver() const { | 
 |   return *m_pClientDriver; | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: Wait until all events have been handled. | 
 | //          This function works in pair with | 
 | //          CMICmnLLDBDebugger::MonitorSBListenerEvents | 
 | //          that handles events from queue. When all events were handled and | 
 | //          queue is | 
 | //          empty the MonitorSBListenerEvents notifies this function that it's | 
 | //          ready to | 
 | //          go on. To synchronize them the m_mutexEventQueue and | 
 | //          m_conditionEventQueueEmpty are used. | 
 | // Type:    Method. | 
 | // Args:    None. | 
 | // Return:  None. | 
 | // Throws:  None. | 
 | //-- | 
 | void CMICmnLLDBDebugger::WaitForHandleEvent() { | 
 |   std::unique_lock<std::mutex> lock(m_mutexEventQueue); | 
 |  | 
 |   lldb::SBEvent event; | 
 |   if (ThreadIsActive() && m_lldbListener.PeekAtNextEvent(event)) | 
 |     m_conditionEventQueueEmpty.wait(lock); | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: Check if need to rebroadcast stop event. This function will return | 
 | // true if | 
 | //          debugger is in synchronouse mode. In such case the | 
 | //          CMICmnLLDBDebugger::RebroadcastStopEvent should be called to | 
 | //          rebroadcast | 
 | //          a new stop event (if any). | 
 | // Type:    Method. | 
 | // Args:    None. | 
 | // Return:  bool    - True = Need to rebroadcast stop event, false = otherwise. | 
 | // Throws:  None. | 
 | //-- | 
 | bool CMICmnLLDBDebugger::CheckIfNeedToRebroadcastStopEvent() { | 
 |   CMICmnLLDBDebugSessionInfo &rSessionInfo( | 
 |       CMICmnLLDBDebugSessionInfo::Instance()); | 
 |   if (!rSessionInfo.GetDebugger().GetAsync()) { | 
 |     const bool include_expression_stops = false; | 
 |     m_nLastStopId = | 
 |         CMICmnLLDBDebugSessionInfo::Instance().GetProcess().GetStopID( | 
 |             include_expression_stops); | 
 |     return true; | 
 |   } | 
 |  | 
 |   return false; | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: Rebroadcast stop event if needed. This function should be called | 
 | // only if the | 
 | //          CMICmnLLDBDebugger::CheckIfNeedToRebroadcastStopEvent() returned | 
 | //          true. | 
 | // Type:    Method. | 
 | // Args:    None. | 
 | // Return:  None. | 
 | // Throws:  None. | 
 | //-- | 
 | void CMICmnLLDBDebugger::RebroadcastStopEvent() { | 
 |   lldb::SBProcess process = CMICmnLLDBDebugSessionInfo::Instance().GetProcess(); | 
 |   const bool include_expression_stops = false; | 
 |   const uint32_t nStopId = process.GetStopID(include_expression_stops); | 
 |   if (m_nLastStopId != nStopId) { | 
 |     lldb::SBEvent event = process.GetStopEventForStopID(nStopId); | 
 |     process.GetBroadcaster().BroadcastEvent(event); | 
 |   } | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: Initialize the LLDB Debugger object. | 
 | // Type:    Method. | 
 | // Args:    None. | 
 | // Return:  MIstatus::success - Functionality succeeded. | 
 | //          MIstatus::failure - Functionality failed. | 
 | // Throws:  None. | 
 | //-- | 
 | bool CMICmnLLDBDebugger::InitSBDebugger() { | 
 |   m_lldbDebugger = lldb::SBDebugger::Create(false); | 
 |   if (!m_lldbDebugger.IsValid()) { | 
 |     SetErrorDescription(MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDDEBUGGER)); | 
 |     return MIstatus::failure; | 
 |   } | 
 |  | 
 |   m_lldbDebugger.GetCommandInterpreter().SetPromptOnQuit(false); | 
 |  | 
 |   return MIstatus::success; | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: Set the LLDB Debugger's std in, err and out streams. (Not | 
 | // implemented left | 
 | //          here for reference. Was called in the | 
 | //          CMICmnLLDBDebugger::Initialize() ) | 
 | // Type:    Method. | 
 | // Args:    None. | 
 | // Return:  MIstatus::success - Functionality succeeded. | 
 | //          MIstatus::failure - Functionality failed. | 
 | // Throws:  None. | 
 | //-- | 
 | bool CMICmnLLDBDebugger::InitStdStreams() { | 
 |   // This is not required when operating the MI driver's code as it has its own | 
 |   // streams. Setting the Stdin for the lldbDebugger especially on LINUX will | 
 |   // cause | 
 |   // another thread to run and partially consume stdin data meant for MI stdin | 
 |   // handler | 
 |   // m_lldbDebugger.SetErrorFileHandle( m_pClientDriver->GetStderr(), false ); | 
 |   // m_lldbDebugger.SetOutputFileHandle( m_pClientDriver->GetStdout(), false ); | 
 |   // m_lldbDebugger.SetInputFileHandle( m_pClientDriver->GetStdin(), false ); | 
 |  | 
 |   return MIstatus::success; | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: Set up the events from the SBDebugger's we would like to listen to. | 
 | // Type:    Method. | 
 | // Args:    None. | 
 | // Return:  MIstatus::success - Functionality succeeded. | 
 | //          MIstatus::failure - Functionality failed. | 
 | // Throws:  None. | 
 | //-- | 
 | bool CMICmnLLDBDebugger::InitSBListener() { | 
 |   m_lldbListener = m_lldbDebugger.GetListener(); | 
 |   if (!m_lldbListener.IsValid()) { | 
 |     SetErrorDescription(MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDLISTENER)); | 
 |     return MIstatus::failure; | 
 |   } | 
 |  | 
 |   const CMIUtilString strDbgId("CMICmnLLDBDebugger1"); | 
 |   MIuint eventMask = lldb::SBTarget::eBroadcastBitBreakpointChanged | | 
 |                      lldb::SBTarget::eBroadcastBitModulesLoaded | | 
 |                      lldb::SBTarget::eBroadcastBitModulesUnloaded | | 
 |                      lldb::SBTarget::eBroadcastBitWatchpointChanged | | 
 |                      lldb::SBTarget::eBroadcastBitSymbolsLoaded; | 
 |   bool bOk = RegisterForEvent( | 
 |       strDbgId, CMIUtilString(lldb::SBTarget::GetBroadcasterClassName()), | 
 |       eventMask); | 
 |  | 
 |   eventMask = lldb::SBThread::eBroadcastBitStackChanged; | 
 |   bOk = bOk && | 
 |         RegisterForEvent( | 
 |             strDbgId, CMIUtilString(lldb::SBThread::GetBroadcasterClassName()), | 
 |             eventMask); | 
 |  | 
 |   eventMask = lldb::SBProcess::eBroadcastBitStateChanged | | 
 |               lldb::SBProcess::eBroadcastBitInterrupt | | 
 |               lldb::SBProcess::eBroadcastBitSTDOUT | | 
 |               lldb::SBProcess::eBroadcastBitSTDERR | | 
 |               lldb::SBProcess::eBroadcastBitProfileData | | 
 |               lldb::SBProcess::eBroadcastBitStructuredData; | 
 |   bOk = bOk && | 
 |         RegisterForEvent( | 
 |             strDbgId, CMIUtilString(lldb::SBProcess::GetBroadcasterClassName()), | 
 |             eventMask); | 
 |  | 
 |   eventMask = lldb::SBCommandInterpreter::eBroadcastBitQuitCommandReceived | | 
 |               lldb::SBCommandInterpreter::eBroadcastBitThreadShouldExit | | 
 |               lldb::SBCommandInterpreter::eBroadcastBitAsynchronousOutputData | | 
 |               lldb::SBCommandInterpreter::eBroadcastBitAsynchronousErrorData; | 
 |   bOk = bOk && | 
 |         RegisterForEvent( | 
 |             strDbgId, m_lldbDebugger.GetCommandInterpreter().GetBroadcaster(), | 
 |             eventMask); | 
 |  | 
 |   return bOk; | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: Register with the debugger, the SBListener, the type of events you | 
 | // are interested | 
 | //          in. Others, like commands, may have already set the mask. | 
 | // Type:    Method. | 
 | // Args:    vClientName         - (R) ID of the client who wants these events | 
 | // set. | 
 | //          vBroadcasterClass   - (R) The SBBroadcaster's class name. | 
 | //          vEventMask          - (R) The mask of events to listen for. | 
 | // Return:  MIstatus::success - Functionality succeeded. | 
 | //          MIstatus::failure - Functionality failed. | 
 | // Throws:  None. | 
 | //-- | 
 | bool CMICmnLLDBDebugger::RegisterForEvent( | 
 |     const CMIUtilString &vClientName, const CMIUtilString &vBroadcasterClass, | 
 |     const MIuint vEventMask) { | 
 |   MIuint existingMask = 0; | 
 |   if (!BroadcasterGetMask(vBroadcasterClass, existingMask)) | 
 |     return MIstatus::failure; | 
 |  | 
 |   if (!ClientSaveMask(vClientName, vBroadcasterClass, vEventMask)) | 
 |     return MIstatus::failure; | 
 |  | 
 |   const char *pBroadCasterName = vBroadcasterClass.c_str(); | 
 |   MIuint eventMask = vEventMask; | 
 |   eventMask += existingMask; | 
 |   const MIuint result = m_lldbListener.StartListeningForEventClass( | 
 |       m_lldbDebugger, pBroadCasterName, eventMask); | 
 |   if (result == 0) { | 
 |     SetErrorDescription(CMIUtilString::Format( | 
 |         MIRSRC(IDS_LLDBDEBUGGER_ERR_STARTLISTENER), pBroadCasterName)); | 
 |     return MIstatus::failure; | 
 |   } | 
 |  | 
 |   return BroadcasterSaveMask(vBroadcasterClass, eventMask); | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: Register with the debugger, the SBListener, the type of events you | 
 | // are interested | 
 | //          in. Others, like commands, may have already set the mask. | 
 | // Type:    Method. | 
 | // Args:    vClientName     - (R) ID of the client who wants these events set. | 
 | //          vBroadcaster    - (R) An SBBroadcaster's derived class. | 
 | //          vEventMask      - (R) The mask of events to listen for. | 
 | // Return:  MIstatus::success - Functionality succeeded. | 
 | //          MIstatus::failure - Functionality failed. | 
 | // Throws:  None. | 
 | //-- | 
 | bool CMICmnLLDBDebugger::RegisterForEvent( | 
 |     const CMIUtilString &vClientName, const lldb::SBBroadcaster &vBroadcaster, | 
 |     const MIuint vEventMask) { | 
 |   const char *pBroadcasterName = vBroadcaster.GetName(); | 
 |   if (pBroadcasterName == nullptr) { | 
 |     SetErrorDescription( | 
 |         CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_ERR_BROADCASTER_NAME), | 
 |                               MIRSRC(IDS_WORD_INVALIDNULLPTR))); | 
 |     return MIstatus::failure; | 
 |   } | 
 |   CMIUtilString broadcasterName(pBroadcasterName); | 
 |   if (broadcasterName.length() == 0) { | 
 |     SetErrorDescription( | 
 |         CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_ERR_BROADCASTER_NAME), | 
 |                               MIRSRC(IDS_WORD_INVALIDEMPTY))); | 
 |     return MIstatus::failure; | 
 |   } | 
 |  | 
 |   MIuint existingMask = 0; | 
 |   if (!BroadcasterGetMask(broadcasterName, existingMask)) | 
 |     return MIstatus::failure; | 
 |  | 
 |   if (!ClientSaveMask(vClientName, broadcasterName, vEventMask)) | 
 |     return MIstatus::failure; | 
 |  | 
 |   MIuint eventMask = vEventMask; | 
 |   eventMask += existingMask; | 
 |   const MIuint result = | 
 |       m_lldbListener.StartListeningForEvents(vBroadcaster, eventMask); | 
 |   if (result == 0) { | 
 |     SetErrorDescription(CMIUtilString::Format( | 
 |         MIRSRC(IDS_LLDBDEBUGGER_ERR_STARTLISTENER), pBroadcasterName)); | 
 |     return MIstatus::failure; | 
 |   } | 
 |  | 
 |   return BroadcasterSaveMask(broadcasterName, eventMask); | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: Unregister with the debugger, the SBListener, the type of events you | 
 | // are no | 
 | //          longer interested in. Others, like commands, may still remain | 
 | //          interested so | 
 | //          an event may not necessarily be stopped. | 
 | // Type:    Method. | 
 | // Args:    vClientName         - (R) ID of the client who no longer requires | 
 | // these events. | 
 | //          vBroadcasterClass   - (R) The SBBroadcaster's class name. | 
 | // Return:  MIstatus::success - Functionality succeeded. | 
 | //          MIstatus::failure - Functionality failed. | 
 | // Throws:  None. | 
 | //-- | 
 | bool CMICmnLLDBDebugger::UnregisterForEvent( | 
 |     const CMIUtilString &vClientName, const CMIUtilString &vBroadcasterClass) { | 
 |   MIuint clientsEventMask = 0; | 
 |   if (!ClientGetTheirMask(vClientName, vBroadcasterClass, clientsEventMask)) | 
 |     return MIstatus::failure; | 
 |   if (!ClientRemoveTheirMask(vClientName, vBroadcasterClass)) | 
 |     return MIstatus::failure; | 
 |  | 
 |   const MIuint otherClientsEventMask = | 
 |       ClientGetMaskForAllClients(vBroadcasterClass); | 
 |   MIuint newEventMask = 0; | 
 |   for (MIuint i = 0; i < 32; i++) { | 
 |     const MIuint bit = 1 << i; | 
 |     const MIuint clientBit = bit & clientsEventMask; | 
 |     const MIuint othersBit = bit & otherClientsEventMask; | 
 |     if ((clientBit != 0) && (othersBit == 0)) { | 
 |       newEventMask += clientBit; | 
 |     } | 
 |   } | 
 |  | 
 |   const char *pBroadCasterName = vBroadcasterClass.c_str(); | 
 |   if (!m_lldbListener.StopListeningForEventClass( | 
 |           m_lldbDebugger, pBroadCasterName, newEventMask)) { | 
 |     SetErrorDescription( | 
 |         CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_ERR_STOPLISTENER), | 
 |                               vClientName.c_str(), pBroadCasterName)); | 
 |     return MIstatus::failure; | 
 |   } | 
 |  | 
 |   return BroadcasterSaveMask(vBroadcasterClass, otherClientsEventMask); | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: Given the SBBroadcaster class name retrieve it's current event mask. | 
 | // Type:    Method. | 
 | // Args:    vBroadcasterClass   - (R) The SBBroadcaster's class name. | 
 | //          vEventMask          - (W) The mask of events to listen for. | 
 | // Return:  MIstatus::success - Functionality succeeded. | 
 | //          MIstatus::failure - Functionality failed. | 
 | // Throws:  None. | 
 | //-- | 
 | bool CMICmnLLDBDebugger::BroadcasterGetMask( | 
 |     const CMIUtilString &vBroadcasterClass, MIuint &vwEventMask) const { | 
 |   vwEventMask = 0; | 
 |  | 
 |   if (vBroadcasterClass.empty()) { | 
 |     SetErrorDescription( | 
 |         CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDBROADCASTER), | 
 |                               vBroadcasterClass.c_str())); | 
 |     return MIstatus::failure; | 
 |   } | 
 |  | 
 |   const MapBroadcastClassNameToEventMask_t::const_iterator it = | 
 |       m_mapBroadcastClassNameToEventMask.find(vBroadcasterClass); | 
 |   if (it != m_mapBroadcastClassNameToEventMask.end()) { | 
 |     vwEventMask = (*it).second; | 
 |   } | 
 |  | 
 |   return MIstatus::success; | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: Remove the event mask for the specified SBBroadcaster class name. | 
 | // Type:    Method. | 
 | // Args:    vBroadcasterClass - (R) The SBBroadcaster's class name. | 
 | // Return:  MIstatus::success - Functionality succeeded. | 
 | //          MIstatus::failure - Functionality failed. | 
 | // Throws:  None. | 
 | //-- | 
 | bool CMICmnLLDBDebugger::BroadcasterRemoveMask( | 
 |     const CMIUtilString &vBroadcasterClass) { | 
 |   MapBroadcastClassNameToEventMask_t::const_iterator it = | 
 |       m_mapBroadcastClassNameToEventMask.find(vBroadcasterClass); | 
 |   if (it != m_mapBroadcastClassNameToEventMask.end()) { | 
 |     m_mapBroadcastClassNameToEventMask.erase(it); | 
 |   } | 
 |  | 
 |   return MIstatus::success; | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: Given the SBBroadcaster class name save it's current event mask. | 
 | // Type:    Method. | 
 | // Args:    vBroadcasterClass - (R) The SBBroadcaster's class name. | 
 | //          vEventMask        - (R) The mask of events to listen for. | 
 | // Return:  MIstatus::success - Functionality succeeded. | 
 | //          MIstatus::failure - Functionality failed. | 
 | // Throws:  None. | 
 | //-- | 
 | bool CMICmnLLDBDebugger::BroadcasterSaveMask( | 
 |     const CMIUtilString &vBroadcasterClass, const MIuint vEventMask) { | 
 |   if (vBroadcasterClass.empty()) { | 
 |     SetErrorDescription( | 
 |         CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDBROADCASTER), | 
 |                               vBroadcasterClass.c_str())); | 
 |     return MIstatus::failure; | 
 |   } | 
 |  | 
 |   BroadcasterRemoveMask(vBroadcasterClass); | 
 |   MapPairBroadcastClassNameToEventMask_t pr(vBroadcasterClass, vEventMask); | 
 |   m_mapBroadcastClassNameToEventMask.insert(pr); | 
 |  | 
 |   return MIstatus::success; | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: Iterate all the clients who have registered event masks against | 
 | // particular | 
 | //          SBBroadcasters and build up the mask that is for all of them. | 
 | // Type:    Method. | 
 | // Args:    vBroadcasterClass   - (R) The broadcaster to retrieve the mask for. | 
 | // Return:  MIuint - Event mask. | 
 | // Throws:  None. | 
 | //-- | 
 | MIuint CMICmnLLDBDebugger::ClientGetMaskForAllClients( | 
 |     const CMIUtilString &vBroadcasterClass) const { | 
 |   MIuint mask = 0; | 
 |   MapIdToEventMask_t::const_iterator it = m_mapIdToEventMask.begin(); | 
 |   while (it != m_mapIdToEventMask.end()) { | 
 |     const CMIUtilString &rId((*it).first); | 
 |     if (rId.find(vBroadcasterClass) != std::string::npos) { | 
 |       const MIuint clientsMask = (*it).second; | 
 |       mask |= clientsMask; | 
 |     } | 
 |  | 
 |     // Next | 
 |     ++it; | 
 |   } | 
 |  | 
 |   return mask; | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: Given the client save its particular event requirements. | 
 | // Type:    Method. | 
 | // Args:    vClientName       - (R) The Client's unique ID. | 
 | //          vBroadcasterClass - (R) The SBBroadcaster's class name targeted for | 
 | //          the events. | 
 | //          vEventMask        - (R) The mask of events to listen for. | 
 | // Return:  MIstatus::success - Functionality succeeded. | 
 | //          MIstatus::failure - Functionality failed. | 
 | // Throws:  None. | 
 | //-- | 
 | bool CMICmnLLDBDebugger::ClientSaveMask(const CMIUtilString &vClientName, | 
 |                                         const CMIUtilString &vBroadcasterClass, | 
 |                                         const MIuint vEventMask) { | 
 |   if (vClientName.empty()) { | 
 |     SetErrorDescription(CMIUtilString::Format( | 
 |         MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDCLIENTNAME), vClientName.c_str())); | 
 |     return MIstatus::failure; | 
 |   } | 
 |  | 
 |   CMIUtilString strId(vBroadcasterClass); | 
 |   strId += vClientName; | 
 |  | 
 |   ClientRemoveTheirMask(vClientName, vBroadcasterClass); | 
 |   MapPairIdToEventMask_t pr(strId, vEventMask); | 
 |   m_mapIdToEventMask.insert(pr); | 
 |  | 
 |   return MIstatus::success; | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: Given the client remove it's particular event requirements. | 
 | // Type:    Method. | 
 | // Args:    vClientName       - (R) The Client's unique ID. | 
 | //          vBroadcasterClass - (R) The SBBroadcaster's class name. | 
 | // Return:  MIstatus::success - Functionality succeeded. | 
 | //          MIstatus::failure - Functionality failed. | 
 | // Throws:  None. | 
 | //-- | 
 | bool CMICmnLLDBDebugger::ClientRemoveTheirMask( | 
 |     const CMIUtilString &vClientName, const CMIUtilString &vBroadcasterClass) { | 
 |   if (vClientName.empty()) { | 
 |     SetErrorDescription(CMIUtilString::Format( | 
 |         MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDCLIENTNAME), vClientName.c_str())); | 
 |     return MIstatus::failure; | 
 |   } | 
 |  | 
 |   CMIUtilString strId(vBroadcasterClass); | 
 |   strId += vClientName; | 
 |  | 
 |   const MapIdToEventMask_t::const_iterator it = m_mapIdToEventMask.find(strId); | 
 |   if (it != m_mapIdToEventMask.end()) { | 
 |     m_mapIdToEventMask.erase(it); | 
 |   } | 
 |  | 
 |   return MIstatus::success; | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: Retrieve the client's event mask used for on a particular | 
 | // SBBroadcaster. | 
 | // Type:    Method. | 
 | // Args:    vClientName       - (R) The Client's unique ID. | 
 | //          vBroadcasterClass - (R) The SBBroadcaster's class name. | 
 | //          vwEventMask       - (W) The client's mask. | 
 | // Return:  MIstatus::success - Functionality succeeded. | 
 | //          MIstatus::failure - Functionality failed. | 
 | // Throws:  None. | 
 | //-- | 
 | bool CMICmnLLDBDebugger::ClientGetTheirMask( | 
 |     const CMIUtilString &vClientName, const CMIUtilString &vBroadcasterClass, | 
 |     MIuint &vwEventMask) { | 
 |   vwEventMask = 0; | 
 |  | 
 |   if (vClientName.empty()) { | 
 |     SetErrorDescription(CMIUtilString::Format( | 
 |         MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDCLIENTNAME), vClientName.c_str())); | 
 |     return MIstatus::failure; | 
 |   } | 
 |  | 
 |   const CMIUtilString strId(vBroadcasterClass + vClientName); | 
 |   const MapIdToEventMask_t::const_iterator it = m_mapIdToEventMask.find(strId); | 
 |   if (it != m_mapIdToEventMask.end()) { | 
 |     vwEventMask = (*it).second; | 
 |   } | 
 |  | 
 |   SetErrorDescription(CMIUtilString::Format( | 
 |       MIRSRC(IDS_LLDBDEBUGGER_ERR_CLIENTNOTREGISTERED), vClientName.c_str())); | 
 |  | 
 |   return MIstatus::failure; | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: Momentarily wait for an events being broadcast and inspect those | 
 | // that do | 
 | //          come this way. Check if the target should exit event if so start | 
 | //          shutting | 
 | //          down this thread and the application. Any other events pass on to | 
 | //          the | 
 | //          Out-of-band handler to further determine what kind of event arrived. | 
 | //          This function runs in the thread "MI debugger event". | 
 | // Type:    Method. | 
 | // Args:    vrbIsAlive  - (W) False = yes exit event monitoring thread, true = | 
 | // continue. | 
 | // Return:  MIstatus::success - Functional succeeded. | 
 | //          MIstatus::failure - Functional failed. | 
 | // Throws:  None. | 
 | //-- | 
 | bool CMICmnLLDBDebugger::MonitorSBListenerEvents(bool &vrbIsAlive) { | 
 |   vrbIsAlive = true; | 
 |  | 
 |   // Lock the mutex of event queue | 
 |   // Note that it should be locked while we are in | 
 |   // CMICmnLLDBDebugger::MonitorSBListenerEvents to | 
 |   // avoid a race condition with CMICmnLLDBDebugger::WaitForHandleEvent | 
 |   std::unique_lock<std::mutex> lock(m_mutexEventQueue); | 
 |  | 
 |   lldb::SBEvent event; | 
 |   const bool bGotEvent = m_lldbListener.GetNextEvent(event); | 
 |   if (!bGotEvent) { | 
 |     // Notify that we are finished and unlock the mutex of event queue before | 
 |     // sleeping | 
 |     m_conditionEventQueueEmpty.notify_one(); | 
 |     lock.unlock(); | 
 |  | 
 |     // Wait a bit to reduce CPU load | 
 |     const std::chrono::milliseconds time(1); | 
 |     std::this_thread::sleep_for(time); | 
 |     return MIstatus::success; | 
 |   } | 
 |   assert(event.IsValid()); | 
 |   assert(event.GetBroadcaster().IsValid()); | 
 |  | 
 |   // Debugging | 
 |   m_pLog->WriteLog(CMIUtilString::Format("##### An event occurred: %s", | 
 |                                          event.GetBroadcasterClass())); | 
 |  | 
 |   bool bHandledEvent = false; | 
 |   bool bOk = false; | 
 |   { | 
 |     // Lock Mutex before handling events so that we don't disturb a running cmd | 
 |     CMIUtilThreadLock lock( | 
 |         CMICmnLLDBDebugSessionInfo::Instance().GetSessionMutex()); | 
 |     bOk = CMICmnLLDBDebuggerHandleEvents::Instance().HandleEvent(event, | 
 |                                                                  bHandledEvent); | 
 |   } | 
 |  | 
 |   if (!bHandledEvent) { | 
 |     const CMIUtilString msg( | 
 |         CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_WRN_UNKNOWN_EVENT), | 
 |                               event.GetBroadcasterClass())); | 
 |     m_pLog->WriteLog(msg); | 
 |   } | 
 |  | 
 |   if (!bOk) | 
 |     m_pLog->WriteLog( | 
 |         CMICmnLLDBDebuggerHandleEvents::Instance().GetErrorDescription()); | 
 |  | 
 |   return MIstatus::success; | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: The main worker method for this thread. | 
 | // Type:    Method. | 
 | // Args:    vrbIsAlive  - (W) True = *this thread is working, false = thread has | 
 | // exited. | 
 | // Return:  MIstatus::success - Functional succeeded. | 
 | //          MIstatus::failure - Functional failed. | 
 | // Throws:  None. | 
 | //-- | 
 | bool CMICmnLLDBDebugger::ThreadRun(bool &vrbIsAlive) { | 
 |   return MonitorSBListenerEvents(vrbIsAlive); | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: Let this thread clean up after itself. | 
 | // Type:    Method. | 
 | // Args: | 
 | // Return:  MIstatus::success - Functionality succeeded. | 
 | //          MIstatus::failure - Functionality failed. | 
 | // Throws:  None. | 
 | //-- | 
 | bool CMICmnLLDBDebugger::ThreadFinish() { return MIstatus::success; } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: Retrieve *this thread object's name. | 
 | // Type:    Overridden. | 
 | // Args:    None. | 
 | // Return:  CMIUtilString & - Text. | 
 | // Throws:  None. | 
 | //-- | 
 | const CMIUtilString &CMICmnLLDBDebugger::ThreadGetName() const { | 
 |   return m_constStrThisThreadId; | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: Loads lldb-mi formatters | 
 | // Type:    Method. | 
 | // Args:    None. | 
 | // Return:  true - Functionality succeeded. | 
 | //          false - Functionality failed. | 
 | // Throws:  None. | 
 | //-- | 
 | bool CMICmnLLDBDebugger::LoadMIFormatters(lldb::SBTypeCategory miCategory) { | 
 |   if (!MI_add_summary(miCategory, "char", MI_char_summary_provider, | 
 |                       lldb::eTypeOptionHideValue | | 
 |                           lldb::eTypeOptionSkipPointers)) | 
 |     return false; | 
 |  | 
 |   if (!MI_add_summary(miCategory, "unsigned char", MI_char_summary_provider, | 
 |                       lldb::eTypeOptionHideValue | | 
 |                           lldb::eTypeOptionSkipPointers)) | 
 |     return false; | 
 |  | 
 |   if (!MI_add_summary(miCategory, "signed char", MI_char_summary_provider, | 
 |                       lldb::eTypeOptionHideValue | | 
 |                           lldb::eTypeOptionSkipPointers)) | 
 |     return false; | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | //++ | 
 | //------------------------------------------------------------------------------------ | 
 | // Details: Registers lldb-mi custom summary providers | 
 | // Type:    Method. | 
 | // Args:    None. | 
 | // Return:  true - Functionality succeeded. | 
 | //          false - Functionality failed. | 
 | // Throws:  None. | 
 | //-- | 
 | bool CMICmnLLDBDebugger::RegisterMISummaryProviders() { | 
 |   static const char *miCategoryName = "lldb-mi"; | 
 |   lldb::SBTypeCategory miCategory = | 
 |       m_lldbDebugger.CreateCategory(miCategoryName); | 
 |   if (!miCategory.IsValid()) | 
 |     return false; | 
 |  | 
 |   if (!LoadMIFormatters(miCategory)) { | 
 |     m_lldbDebugger.DeleteCategory(miCategoryName); | 
 |     return false; | 
 |   } | 
 |   miCategory.SetEnabled(true); | 
 |   return true; | 
 | } |