#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>
#include "MICmnLLDBDebugSessionInfo.h"
#include "MICmnLLDBDebugger.h"
#include "MICmnLLDBDebuggerHandleEvents.h"
#include "MICmnLog.h"
#include "MICmnResources.h"
#include "MICmnThreadMgrStd.h"
#include "MIDriverBase.h"
#include "MIUtilSingletonHelper.h"
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;
}
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
}
CMICmnLLDBDebugger::CMICmnLLDBDebugger()
: m_constStrThisThreadId("MI debugger event") {}
CMICmnLLDBDebugger::~CMICmnLLDBDebugger() { Shutdown(); }
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);
}
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);
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;
}
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;
lldb::SBTarget sbTarget = CMICmnLLDBDebugSessionInfo::Instance().GetTarget();
m_lldbDebugger.DeleteTarget(sbTarget);
lldb::SBDebugger::Destroy(m_lldbDebugger);
lldb::SBDebugger::Terminate();
m_pClientDriver = nullptr;
m_mapBroadcastClassNameToEventMask.clear();
m_mapIdToEventMask.clear();
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;
}
lldb::SBDebugger &CMICmnLLDBDebugger::GetTheDebugger() {
return m_lldbDebugger;
}
lldb::SBListener &CMICmnLLDBDebugger::GetTheListener() {
return m_lldbListener;
}
bool CMICmnLLDBDebugger::SetDriver(const CMIDriverBase &vClientDriver) {
m_pClientDriver = const_cast<CMIDriverBase *>(&vClientDriver);
return MIstatus::success;
}
CMIDriverBase &CMICmnLLDBDebugger::GetDriver() const {
return *m_pClientDriver;
}
void CMICmnLLDBDebugger::WaitForHandleEvent() {
std::unique_lock<std::mutex> lock(m_mutexEventQueue);
lldb::SBEvent event;
if (ThreadIsActive() && m_lldbListener.PeekAtNextEvent(event))
m_conditionEventQueueEmpty.wait(lock);
}
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;
}
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);
}
}
bool CMICmnLLDBDebugger::InitSBDebugger() {
#ifdef MS_DEBUGGER
m_lldbDebugger = lldb::SBDebugger::Create(true);
#else
m_lldbDebugger = lldb::SBDebugger::Create(false);
#endif
if (!m_lldbDebugger.IsValid()) {
SetErrorDescription(MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDDEBUGGER));
return MIstatus::failure;
}
m_lldbDebugger.GetCommandInterpreter().SetPromptOnQuit(false);
return MIstatus::success;
}
bool CMICmnLLDBDebugger::InitStdStreams() {
return MIstatus::success;
}
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::eBroadcastBitWatchpointChanged |
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;
}
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);
}
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);
}
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);
}
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;
}
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;
}
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;
}
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;
}
++it;
}
return mask;
}
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;
}
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;
}
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;
}
bool CMICmnLLDBDebugger::MonitorSBListenerEvents(bool &vrbIsAlive) {
vrbIsAlive = true;
std::unique_lock<std::mutex> lock(m_mutexEventQueue);
lldb::SBEvent event;
const bool bGotEvent = m_lldbListener.GetNextEvent(event);
if (!bGotEvent) {
m_conditionEventQueueEmpty.notify_one();
lock.unlock();
const std::chrono::milliseconds time(1);
std::this_thread::sleep_for(time);
return MIstatus::success;
}
assert(event.IsValid());
assert(event.GetBroadcaster().IsValid());
m_pLog->WriteLog(CMIUtilString::Format("##### An event occurred: %s",
event.GetBroadcasterClass()));
bool bHandledEvent = false;
bool bOk = false;
{
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;
}
bool CMICmnLLDBDebugger::ThreadRun(bool &vrbIsAlive) {
return MonitorSBListenerEvents(vrbIsAlive);
}
bool CMICmnLLDBDebugger::ThreadFinish() { return MIstatus::success; }
const CMIUtilString &CMICmnLLDBDebugger::ThreadGetName() const {
return m_constStrThisThreadId;
}
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;
}
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;
}