| //===-- 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; |
| } |