Skip to content

Commit 2fc38b2

Browse files
committed
[lldb] Report debugger diagnostics as events
Report warnings and errors through events instead of printing directly the to the debugger's error stream. By using events, IDEs such as Xcode can report these issues in the UI instead of having them show up in the debugger console. The new diagnostic events are handled by the default event loop. If a diagnostic is reported while nobody is listening for the new event types, it is printed directly to the debugger's error stream. Differential revision: https://reviews.llvm.org/D121511
1 parent 2c4e38f commit 2fc38b2

File tree

13 files changed

+465
-80
lines changed

13 files changed

+465
-80
lines changed

lldb/include/lldb/Core/Debugger.h

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <memory>
1515
#include <vector>
1616

17+
#include "lldb/Core/DebuggerEvents.h"
1718
#include "lldb/Core/FormatEntity.h"
1819
#include "lldb/Core/IOHandler.h"
1920
#include "lldb/Core/SourceManager.h"
@@ -57,7 +58,6 @@ class Process;
5758
class Stream;
5859
class SymbolContext;
5960
class Target;
60-
class ProgressEventData;
6161

6262
namespace repro {
6363
class DataRecorder;
@@ -77,6 +77,8 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
7777
/// Broadcaster event bits definitions.
7878
enum {
7979
eBroadcastBitProgress = (1 << 0),
80+
eBroadcastBitWarning = (1 << 1),
81+
eBroadcastBitError = (1 << 2),
8082
};
8183

8284
static ConstString GetStaticBroadcasterClass();
@@ -375,6 +377,50 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
375377
return m_broadcaster_manager_sp;
376378
}
377379

380+
/// Report warning events.
381+
///
382+
/// Progress events will be delivered to any debuggers that have listeners
383+
/// for the eBroadcastBitError.
384+
///
385+
/// \param[in] message
386+
/// The warning message to be reported.
387+
///
388+
/// \param [in] debugger_id
389+
/// If this optional parameter has a value, it indicates the unique
390+
/// debugger identifier that this progress should be delivered to. If this
391+
/// optional parameter does not have a value, the progress will be
392+
/// delivered to all debuggers.
393+
///
394+
/// \param [in] once
395+
/// If a pointer is passed to a std::once_flag, then it will be used to
396+
/// ensure the given warning is only broadcast once.
397+
static void
398+
ReportWarning(std::string messsage,
399+
llvm::Optional<lldb::user_id_t> debugger_id = llvm::None,
400+
std::once_flag *once = nullptr);
401+
402+
/// Report error events.
403+
///
404+
/// Progress events will be delivered to any debuggers that have listeners
405+
/// for the eBroadcastBitError.
406+
///
407+
/// \param[in] message
408+
/// The error message to be reported.
409+
///
410+
/// \param [in] debugger_id
411+
/// If this optional parameter has a value, it indicates the unique
412+
/// debugger identifier that this progress should be delivered to. If this
413+
/// optional parameter does not have a value, the progress will be
414+
/// delivered to all debuggers.
415+
///
416+
/// \param [in] once
417+
/// If a pointer is passed to a std::once_flag, then it will be used to
418+
/// ensure the given error is only broadcast once.
419+
static void
420+
ReportError(std::string messsage,
421+
llvm::Optional<lldb::user_id_t> debugger_id = llvm::None,
422+
std::once_flag *once = nullptr);
423+
378424
protected:
379425
friend class CommandInterpreter;
380426
friend class REPL;
@@ -413,6 +459,11 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
413459
uint64_t completed, uint64_t total,
414460
llvm::Optional<lldb::user_id_t> debugger_id);
415461

462+
static void ReportDiagnosticImpl(DiagnosticEventData::Type type,
463+
std::string message,
464+
llvm::Optional<lldb::user_id_t> debugger_id,
465+
std::once_flag *once);
466+
416467
void PrintProgress(const ProgressEventData &data);
417468

418469
bool StartEventHandlerThread();
@@ -444,6 +495,8 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
444495

445496
void HandleProgressEvent(const lldb::EventSP &event_sp);
446497

498+
void HandleDiagnosticEvent(const lldb::EventSP &event_sp);
499+
447500
// Ensures two threads don't attempt to flush process output in parallel.
448501
std::mutex m_output_flush_mutex;
449502
void FlushProcessOutput(Process &process, bool flush_stdout,

lldb/include/lldb/Core/DebuggerEvents.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,40 @@ class ProgressEventData : public EventData {
4646
ProgressEventData(const ProgressEventData &) = delete;
4747
const ProgressEventData &operator=(const ProgressEventData &) = delete;
4848
};
49+
50+
class DiagnosticEventData : public EventData {
51+
public:
52+
enum class Type {
53+
Warning,
54+
Error,
55+
};
56+
DiagnosticEventData(Type type, std::string message, bool debugger_specific)
57+
: m_message(std::move(message)), m_type(type),
58+
m_debugger_specific(debugger_specific) {}
59+
~DiagnosticEventData() {}
60+
61+
const std::string &GetMessage() const { return m_message; }
62+
Type GetType() const { return m_type; }
63+
64+
llvm::StringRef GetPrefix() const;
65+
66+
void Dump(Stream *s) const override;
67+
68+
static ConstString GetFlavorString();
69+
ConstString GetFlavor() const override;
70+
71+
static const DiagnosticEventData *
72+
GetEventDataFromEvent(const Event *event_ptr);
73+
74+
protected:
75+
std::string m_message;
76+
Type m_type;
77+
const bool m_debugger_specific;
78+
79+
DiagnosticEventData(const DiagnosticEventData &) = delete;
80+
const DiagnosticEventData &operator=(const DiagnosticEventData &) = delete;
81+
};
82+
4983
} // namespace lldb_private
5084

5185
#endif // LLDB_CORE_DEBUGGER_EVENTS_H

lldb/source/Core/Debugger.cpp

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1326,6 +1326,79 @@ void Debugger::ReportProgress(uint64_t progress_id, const std::string &message,
13261326
}
13271327
}
13281328

1329+
static void PrivateReportDiagnostic(Debugger &debugger,
1330+
DiagnosticEventData::Type type,
1331+
std::string message,
1332+
bool debugger_specific) {
1333+
uint32_t event_type = 0;
1334+
switch (type) {
1335+
case DiagnosticEventData::Type::Warning:
1336+
event_type = Debugger::eBroadcastBitWarning;
1337+
break;
1338+
case DiagnosticEventData::Type::Error:
1339+
event_type = Debugger::eBroadcastBitError;
1340+
break;
1341+
}
1342+
1343+
Broadcaster &broadcaster = debugger.GetBroadcaster();
1344+
if (!broadcaster.EventTypeHasListeners(event_type)) {
1345+
// Diagnostics are too important to drop. If nobody is listening, print the
1346+
// diagnostic directly to the debugger's error stream.
1347+
DiagnosticEventData event_data(type, std::move(message), debugger_specific);
1348+
StreamSP stream = debugger.GetAsyncErrorStream();
1349+
event_data.Dump(stream.get());
1350+
return;
1351+
}
1352+
EventSP event_sp = std::make_shared<Event>(
1353+
event_type,
1354+
new DiagnosticEventData(type, std::move(message), debugger_specific));
1355+
broadcaster.BroadcastEvent(event_sp);
1356+
}
1357+
1358+
void Debugger::ReportDiagnosticImpl(DiagnosticEventData::Type type,
1359+
std::string message,
1360+
llvm::Optional<lldb::user_id_t> debugger_id,
1361+
std::once_flag *once) {
1362+
auto ReportDiagnosticLambda = [&]() {
1363+
// Check if this progress is for a specific debugger.
1364+
if (debugger_id) {
1365+
// It is debugger specific, grab it and deliver the event if the debugger
1366+
// still exists.
1367+
DebuggerSP debugger_sp = FindDebuggerWithID(*debugger_id);
1368+
if (debugger_sp)
1369+
PrivateReportDiagnostic(*debugger_sp, type, std::move(message), true);
1370+
return;
1371+
}
1372+
// The progress event is not debugger specific, iterate over all debuggers
1373+
// and deliver a progress event to each one.
1374+
if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
1375+
std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
1376+
for (const auto &debugger : *g_debugger_list_ptr)
1377+
PrivateReportDiagnostic(*debugger, type, message, false);
1378+
}
1379+
};
1380+
1381+
if (once)
1382+
std::call_once(*once, ReportDiagnosticLambda);
1383+
else
1384+
ReportDiagnosticLambda();
1385+
}
1386+
1387+
void Debugger::ReportWarning(std::string message,
1388+
llvm::Optional<lldb::user_id_t> debugger_id,
1389+
std::once_flag *once) {
1390+
ReportDiagnosticImpl(DiagnosticEventData::Type::Warning, std::move(message),
1391+
debugger_id, once);
1392+
}
1393+
1394+
void Debugger::ReportError(std::string message,
1395+
llvm::Optional<lldb::user_id_t> debugger_id,
1396+
std::once_flag *once) {
1397+
1398+
ReportDiagnosticImpl(DiagnosticEventData::Type::Error, std::move(message),
1399+
debugger_id, once);
1400+
}
1401+
13291402
bool Debugger::EnableLog(llvm::StringRef channel,
13301403
llvm::ArrayRef<const char *> categories,
13311404
llvm::StringRef log_file, uint32_t log_options,
@@ -1605,8 +1678,9 @@ lldb::thread_result_t Debugger::DefaultEventHandler() {
16051678
CommandInterpreter::eBroadcastBitAsynchronousOutputData |
16061679
CommandInterpreter::eBroadcastBitAsynchronousErrorData);
16071680

1608-
listener_sp->StartListeningForEvents(&m_broadcaster,
1609-
Debugger::eBroadcastBitProgress);
1681+
listener_sp->StartListeningForEvents(
1682+
&m_broadcaster,
1683+
eBroadcastBitProgress | eBroadcastBitWarning | eBroadcastBitError);
16101684

16111685
// Let the thread that spawned us know that we have started up and that we
16121686
// are now listening to all required events so no events get missed
@@ -1660,6 +1734,10 @@ lldb::thread_result_t Debugger::DefaultEventHandler() {
16601734
} else if (broadcaster == &m_broadcaster) {
16611735
if (event_type & Debugger::eBroadcastBitProgress)
16621736
HandleProgressEvent(event_sp);
1737+
else if (event_type & Debugger::eBroadcastBitWarning)
1738+
HandleDiagnosticEvent(event_sp);
1739+
else if (event_type & Debugger::eBroadcastBitError)
1740+
HandleDiagnosticEvent(event_sp);
16631741
}
16641742
}
16651743

@@ -1793,6 +1871,15 @@ void Debugger::HandleProgressEvent(const lldb::EventSP &event_sp) {
17931871
output->Flush();
17941872
}
17951873

1874+
void Debugger::HandleDiagnosticEvent(const lldb::EventSP &event_sp) {
1875+
auto *data = DiagnosticEventData::GetEventDataFromEvent(event_sp.get());
1876+
if (!data)
1877+
return;
1878+
1879+
StreamSP stream = GetAsyncErrorStream();
1880+
data->Dump(stream.get());
1881+
}
1882+
17961883
bool Debugger::HasIOHandlerThread() { return m_io_handler_thread.IsJoinable(); }
17971884

17981885
bool Debugger::StartIOHandlerThread() {

lldb/source/Core/DebuggerEvents.cpp

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@
1010

1111
using namespace lldb_private;
1212

13+
template <typename T>
14+
static const T *GetEventDataFromEventImpl(const Event *event_ptr) {
15+
if (event_ptr)
16+
if (const EventData *event_data = event_ptr->GetData())
17+
if (event_data->GetFlavor() == T::GetFlavorString())
18+
return static_cast<const T *>(event_ptr->GetData());
19+
return nullptr;
20+
}
21+
1322
ConstString ProgressEventData::GetFlavorString() {
1423
static ConstString g_flavor("ProgressEventData");
1524
return g_flavor;
@@ -33,9 +42,33 @@ void ProgressEventData::Dump(Stream *s) const {
3342

3443
const ProgressEventData *
3544
ProgressEventData::GetEventDataFromEvent(const Event *event_ptr) {
36-
if (event_ptr)
37-
if (const EventData *event_data = event_ptr->GetData())
38-
if (event_data->GetFlavor() == ProgressEventData::GetFlavorString())
39-
return static_cast<const ProgressEventData *>(event_ptr->GetData());
40-
return nullptr;
45+
return GetEventDataFromEventImpl<ProgressEventData>(event_ptr);
46+
}
47+
48+
llvm::StringRef DiagnosticEventData::GetPrefix() const {
49+
switch (m_type) {
50+
case Type::Warning:
51+
return "warning";
52+
case Type::Error:
53+
return "error";
54+
}
55+
}
56+
57+
void DiagnosticEventData::Dump(Stream *s) const {
58+
*s << GetPrefix() << ": " << GetMessage() << '\n';
59+
s->Flush();
60+
}
61+
62+
ConstString DiagnosticEventData::GetFlavorString() {
63+
static ConstString g_flavor("DiagnosticEventData");
64+
return g_flavor;
65+
}
66+
67+
ConstString DiagnosticEventData::GetFlavor() const {
68+
return DiagnosticEventData::GetFlavorString();
69+
}
70+
71+
const DiagnosticEventData *
72+
DiagnosticEventData::GetEventDataFromEvent(const Event *event_ptr) {
73+
return GetEventDataFromEventImpl<DiagnosticEventData>(event_ptr);
4174
}

lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -299,10 +299,12 @@ bool DynamicLoaderMacOS::NotifyBreakpointHit(void *baton,
299299
}
300300
}
301301
} else {
302-
process->GetTarget().GetDebugger().GetAsyncErrorStream()->Printf(
303-
"No ABI plugin located for triple %s -- shared libraries will not be "
304-
"registered!\n",
305-
process->GetTarget().GetArchitecture().GetTriple().getTriple().c_str());
302+
Target &target = process->GetTarget();
303+
Debugger::ReportWarning(
304+
"no ABI plugin located for triple " +
305+
target.GetArchitecture().GetTriple().getTriple() +
306+
": shared libraries will not be registered",
307+
target.GetDebugger().GetID());
306308
}
307309

308310
// Return true to stop the target, false to just let the target run

lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -400,10 +400,12 @@ bool DynamicLoaderMacOSXDYLD::NotifyBreakpointHit(
400400
}
401401
}
402402
} else {
403-
process->GetTarget().GetDebugger().GetAsyncErrorStream()->Printf(
404-
"No ABI plugin located for triple %s -- shared libraries will not be "
405-
"registered!\n",
406-
process->GetTarget().GetArchitecture().GetTriple().getTriple().c_str());
403+
Target &target = process->GetTarget();
404+
Debugger::ReportWarning(
405+
"no ABI plugin located for triple " +
406+
target.GetArchitecture().GetTriple().getTriple() +
407+
": shared libraries will not be registered",
408+
target.GetDebugger().GetID());
407409
}
408410

409411
// Return true to stop the target, false to just let the target run

0 commit comments

Comments
 (0)