diff options
Diffstat (limited to 'contrib/llvm-project/lldb/tools/lldb-mi/MICmnLLDBDebugger.cpp')
| -rw-r--r-- | contrib/llvm-project/lldb/tools/lldb-mi/MICmnLLDBDebugger.cpp | 905 |
1 files changed, 905 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/tools/lldb-mi/MICmnLLDBDebugger.cpp b/contrib/llvm-project/lldb/tools/lldb-mi/MICmnLLDBDebugger.cpp new file mode 100644 index 000000000000..b22e7d9a1fe6 --- /dev/null +++ b/contrib/llvm-project/lldb/tools/lldb-mi/MICmnLLDBDebugger.cpp @@ -0,0 +1,905 @@ +//===-- MICmnLLDBDebugger.cpp -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// 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(), + CMIUtilString::WithNullAsEmpty(value.GetValue())); + else if (type_code == lldb::eBasicTypeUnsignedChar) + stream.Printf("%u %s", (unsigned)value.GetValueAsUnsigned(), + CMIUtilString::WithNullAsEmpty(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 = MIuint(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; +} |
