Skip to content

Commit 4d213e9

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 (cherry picked from commit 2fc38b2)
1 parent 607208f commit 4d213e9

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();
@@ -383,6 +385,50 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
383385
return m_broadcaster_manager_sp;
384386
}
385387

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

471+
static void ReportDiagnosticImpl(DiagnosticEventData::Type type,
472+
std::string message,
473+
llvm::Optional<lldb::user_id_t> debugger_id,
474+
std::once_flag *once);
475+
425476
void PrintProgress(const ProgressEventData &data);
426477

427478
bool StartEventHandlerThread();
@@ -455,6 +506,8 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
455506

456507
void HandleProgressEvent(const lldb::EventSP &event_sp);
457508

509+
void HandleDiagnosticEvent(const lldb::EventSP &event_sp);
510+
458511
// Ensures two threads don't attempt to flush process output in parallel.
459512
std::mutex m_output_flush_mutex;
460513
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
@@ -1362,6 +1362,79 @@ void Debugger::ReportProgress(uint64_t progress_id, const std::string &message,
13621362
}
13631363
}
13641364

1365+
static void PrivateReportDiagnostic(Debugger &debugger,
1366+
DiagnosticEventData::Type type,
1367+
std::string message,
1368+
bool debugger_specific) {
1369+
uint32_t event_type = 0;
1370+
switch (type) {
1371+
case DiagnosticEventData::Type::Warning:
1372+
event_type = Debugger::eBroadcastBitWarning;
1373+
break;
1374+
case DiagnosticEventData::Type::Error:
1375+
event_type = Debugger::eBroadcastBitError;
1376+
break;
1377+
}
1378+
1379+
Broadcaster &broadcaster = debugger.GetBroadcaster();
1380+
if (!broadcaster.EventTypeHasListeners(event_type)) {
1381+
// Diagnostics are too important to drop. If nobody is listening, print the
1382+
// diagnostic directly to the debugger's error stream.
1383+
DiagnosticEventData event_data(type, std::move(message), debugger_specific);
1384+
StreamSP stream = debugger.GetAsyncErrorStream();
1385+
event_data.Dump(stream.get());
1386+
return;
1387+
}
1388+
EventSP event_sp = std::make_shared<Event>(
1389+
event_type,
1390+
new DiagnosticEventData(type, std::move(message), debugger_specific));
1391+
broadcaster.BroadcastEvent(event_sp);
1392+
}
1393+
1394+
void Debugger::ReportDiagnosticImpl(DiagnosticEventData::Type type,
1395+
std::string message,
1396+
llvm::Optional<lldb::user_id_t> debugger_id,
1397+
std::once_flag *once) {
1398+
auto ReportDiagnosticLambda = [&]() {
1399+
// Check if this progress is for a specific debugger.
1400+
if (debugger_id) {
1401+
// It is debugger specific, grab it and deliver the event if the debugger
1402+
// still exists.
1403+
DebuggerSP debugger_sp = FindDebuggerWithID(*debugger_id);
1404+
if (debugger_sp)
1405+
PrivateReportDiagnostic(*debugger_sp, type, std::move(message), true);
1406+
return;
1407+
}
1408+
// The progress event is not debugger specific, iterate over all debuggers
1409+
// and deliver a progress event to each one.
1410+
if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
1411+
std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
1412+
for (const auto &debugger : *g_debugger_list_ptr)
1413+
PrivateReportDiagnostic(*debugger, type, message, false);
1414+
}
1415+
};
1416+
1417+
if (once)
1418+
std::call_once(*once, ReportDiagnosticLambda);
1419+
else
1420+
ReportDiagnosticLambda();
1421+
}
1422+
1423+
void Debugger::ReportWarning(std::string message,
1424+
llvm::Optional<lldb::user_id_t> debugger_id,
1425+
std::once_flag *once) {
1426+
ReportDiagnosticImpl(DiagnosticEventData::Type::Warning, std::move(message),
1427+
debugger_id, once);
1428+
}
1429+
1430+
void Debugger::ReportError(std::string message,
1431+
llvm::Optional<lldb::user_id_t> debugger_id,
1432+
std::once_flag *once) {
1433+
1434+
ReportDiagnosticImpl(DiagnosticEventData::Type::Error, std::move(message),
1435+
debugger_id, once);
1436+
}
1437+
13651438
bool Debugger::EnableLog(llvm::StringRef channel,
13661439
llvm::ArrayRef<const char *> categories,
13671440
llvm::StringRef log_file, uint32_t log_options,
@@ -1654,8 +1727,9 @@ void Debugger::DefaultEventHandler() {
16541727
CommandInterpreter::eBroadcastBitAsynchronousOutputData |
16551728
CommandInterpreter::eBroadcastBitAsynchronousErrorData);
16561729

1657-
listener_sp->StartListeningForEvents(&m_broadcaster,
1658-
Debugger::eBroadcastBitProgress);
1730+
listener_sp->StartListeningForEvents(
1731+
&m_broadcaster,
1732+
eBroadcastBitProgress | eBroadcastBitWarning | eBroadcastBitError);
16591733

16601734
// Let the thread that spawned us know that we have started up and that we
16611735
// are now listening to all required events so no events get missed
@@ -1709,6 +1783,10 @@ void Debugger::DefaultEventHandler() {
17091783
} else if (broadcaster == &m_broadcaster) {
17101784
if (event_type & Debugger::eBroadcastBitProgress)
17111785
HandleProgressEvent(event_sp);
1786+
else if (event_type & Debugger::eBroadcastBitWarning)
1787+
HandleDiagnosticEvent(event_sp);
1788+
else if (event_type & Debugger::eBroadcastBitError)
1789+
HandleDiagnosticEvent(event_sp);
17121790
}
17131791
}
17141792

@@ -1844,6 +1922,15 @@ void Debugger::HandleProgressEvent(const lldb::EventSP &event_sp) {
18441922
output.Flush();
18451923
}
18461924

1925+
void Debugger::HandleDiagnosticEvent(const lldb::EventSP &event_sp) {
1926+
auto *data = DiagnosticEventData::GetEventDataFromEvent(event_sp.get());
1927+
if (!data)
1928+
return;
1929+
1930+
StreamSP stream = GetAsyncErrorStream();
1931+
data->Dump(stream.get());
1932+
}
1933+
18471934
bool Debugger::HasIOHandlerThread() { return m_io_handler_thread.IsJoinable(); }
18481935

18491936
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
@@ -401,10 +401,12 @@ bool DynamicLoaderMacOSXDYLD::NotifyBreakpointHit(
401401
}
402402
}
403403
} else {
404-
process->GetTarget().GetDebugger().GetAsyncErrorStream()->Printf(
405-
"No ABI plugin located for triple %s -- shared libraries will not be "
406-
"registered!\n",
407-
process->GetTarget().GetArchitecture().GetTriple().getTriple().c_str());
404+
Target &target = process->GetTarget();
405+
Debugger::ReportWarning(
406+
"no ABI plugin located for triple " +
407+
target.GetArchitecture().GetTriple().getTriple() +
408+
": shared libraries will not be registered",
409+
target.GetDebugger().GetID());
408410
}
409411

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

0 commit comments

Comments
 (0)