Skip to content

Conversation

oontvoo
Copy link
Member

@oontvoo oontvoo commented Jun 14, 2024

New fixes:

  • properly init the std::optional<std::vector> to an empty vector as opposed to {} (which was effectively std::nullopt).

@oontvoo oontvoo requested a review from JDevlieghere as a code owner June 14, 2024 17:03
@llvmbot llvmbot added the lldb label Jun 14, 2024
@llvmbot
Copy link
Member

llvmbot commented Jun 14, 2024

@llvm/pr-subscribers-lldb

Author: Vy Nguyen (oontvoo)

Changes

New fixes:

  • properly init the std::optional&lt;std::vector&gt; to an empty vector as opposed to {} (which was effectively std::nullopt).

Full diff: https://github.com/llvm/llvm-project/pull/95571.diff

7 Files Affected:

  • (modified) lldb/include/lldb/API/SBDebugger.h (+2)
  • (modified) lldb/include/lldb/Symbol/TypeSystem.h (+1)
  • (modified) lldb/source/API/SBDebugger.cpp (+4)
  • (modified) lldb/source/Symbol/TypeSystem.cpp (+11)
  • (modified) lldb/tools/lldb-dap/DAP.cpp (+54-10)
  • (modified) lldb/tools/lldb-dap/DAP.h (+4-1)
  • (modified) lldb/tools/lldb-dap/lldb-dap.cpp (+4-2)
diff --git a/lldb/include/lldb/API/SBDebugger.h b/lldb/include/lldb/API/SBDebugger.h
index af19b1faf3bf5..84ea9c0f772e1 100644
--- a/lldb/include/lldb/API/SBDebugger.h
+++ b/lldb/include/lldb/API/SBDebugger.h
@@ -57,6 +57,8 @@ class LLDB_API SBDebugger {
 
   static const char *GetBroadcasterClass();
 
+  static bool SupportsLanguage(lldb::LanguageType language);
+
   lldb::SBBroadcaster GetBroadcaster();
 
   /// Get progress data from a SBEvent whose type is eBroadcastBitProgress.
diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h
index b4025c173a186..7d48f9b316138 100644
--- a/lldb/include/lldb/Symbol/TypeSystem.h
+++ b/lldb/include/lldb/Symbol/TypeSystem.h
@@ -209,6 +209,7 @@ class TypeSystem : public PluginInterface,
   // TypeSystems can support more than one language
   virtual bool SupportsLanguage(lldb::LanguageType language) = 0;
 
+  static bool SupportsLanguageStatic(lldb::LanguageType language);
   // Type Completion
 
   virtual bool GetCompleteType(lldb::opaque_compiler_type_t type) = 0;
diff --git a/lldb/source/API/SBDebugger.cpp b/lldb/source/API/SBDebugger.cpp
index 7ef0d6efd4aaa..29da7d33dd80b 100644
--- a/lldb/source/API/SBDebugger.cpp
+++ b/lldb/source/API/SBDebugger.cpp
@@ -1742,3 +1742,7 @@ bool SBDebugger::InterruptRequested()   {
     return m_opaque_sp->InterruptRequested();
   return false;
 }
+
+bool SBDebugger::SupportsLanguage(lldb::LanguageType language) {
+  return TypeSystem::SupportsLanguageStatic(language);
+}
diff --git a/lldb/source/Symbol/TypeSystem.cpp b/lldb/source/Symbol/TypeSystem.cpp
index 4956f10a0b0a7..931ce1b0203a9 100644
--- a/lldb/source/Symbol/TypeSystem.cpp
+++ b/lldb/source/Symbol/TypeSystem.cpp
@@ -335,3 +335,14 @@ TypeSystemMap::GetTypeSystemForLanguage(lldb::LanguageType language,
   }
   return GetTypeSystemForLanguage(language);
 }
+
+bool TypeSystem::SupportsLanguageStatic(lldb::LanguageType language) {
+  if (language == eLanguageTypeUnknown || language >= eNumLanguageTypes)
+    return false;
+
+  LanguageSet languages =
+      PluginManager::GetAllTypeSystemSupportedLanguagesForTypes();
+  if (languages.Empty())
+    return false;
+  return languages[language];
+}
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index d419f821999e6..e4fb3181a2d99 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -32,14 +32,7 @@ namespace lldb_dap {
 DAP g_dap;
 
 DAP::DAP()
-    : broadcaster("lldb-dap"),
-      exception_breakpoints(
-          {{"cpp_catch", "C++ Catch", lldb::eLanguageTypeC_plus_plus},
-           {"cpp_throw", "C++ Throw", lldb::eLanguageTypeC_plus_plus},
-           {"objc_catch", "Objective-C Catch", lldb::eLanguageTypeObjC},
-           {"objc_throw", "Objective-C Throw", lldb::eLanguageTypeObjC},
-           {"swift_catch", "Swift Catch", lldb::eLanguageTypeSwift},
-           {"swift_throw", "Swift Throw", lldb::eLanguageTypeSwift}}),
+    : broadcaster("lldb-dap"), exception_breakpoints(),
       focus_tid(LLDB_INVALID_THREAD_ID), stop_at_entry(false), is_attach(false),
       enable_auto_variable_summaries(false),
       enable_synthetic_child_debugging(false),
@@ -65,8 +58,54 @@ DAP::DAP()
 
 DAP::~DAP() = default;
 
+void DAP::PopulateExceptionBreakpoints() {
+  llvm::call_once(initExceptionBreakpoints, [this]() {
+    exception_breakpoints = std::vector<ExceptionBreakpoint> {};
+    
+    if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeC_plus_plus)) {
+      exception_breakpoints->emplace_back("cpp_catch", "C++ Catch",
+                                          lldb::eLanguageTypeC_plus_plus);
+      exception_breakpoints->emplace_back("cpp_throw", "C++ Throw",
+                                          lldb::eLanguageTypeC_plus_plus);
+    }
+    if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeObjC)) {
+      exception_breakpoints->emplace_back("objc_catch", "Objective-C Catch",
+                                          lldb::eLanguageTypeObjC);
+      exception_breakpoints->emplace_back("objc_throw", "Objective-C Throw",
+                                          lldb::eLanguageTypeObjC);
+    }
+    if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeSwift)) {
+      exception_breakpoints->emplace_back("swift_catch", "Swift Catch",
+                                          lldb::eLanguageTypeSwift);
+      exception_breakpoints->emplace_back("swift_throw", "Swift Throw",
+                                          lldb::eLanguageTypeSwift);
+    }
+    assert(exception_breakpoints.has_value() && "should have been initted");
+    assert(!exception_breakpoints->empty() && "should not be empty");
+  });
+}
+
 ExceptionBreakpoint *DAP::GetExceptionBreakpoint(const std::string &filter) {
-  for (auto &bp : exception_breakpoints) {
+  // PopulateExceptionBreakpoints() is called after g_dap.debugger is created
+  // in a request-initialize.
+  //
+  // But this GetExceptionBreakpoint() method may be called before attaching, in
+  // which case, we may not have populated the filter yet.
+  //
+  // We also cannot call PopulateExceptionBreakpoints() in DAP::DAP() because
+  // we need SBDebugger::Initialize() to have been called before this.
+  //
+  // So just calling PopulateExceptionBreakoints(),which does lazy-populating
+  // seems easiest. Two other options include:
+  //  + call g_dap.PopulateExceptionBreakpoints() in lldb-dap.cpp::main()
+  //    right after the call to SBDebugger::Initialize()
+  //  + Just call PopulateExceptionBreakpoints() to get a fresh list  everytime
+  //    we query (a bit overkill since it's not likely to change?)
+  PopulateExceptionBreakpoints();
+  assert(exception_breakpoints.has_value() &&
+         "exception_breakpoints must have been populated");
+
+  for (auto &bp : *exception_breakpoints) {
     if (bp.filter == filter)
       return &bp;
   }
@@ -74,7 +113,12 @@ ExceptionBreakpoint *DAP::GetExceptionBreakpoint(const std::string &filter) {
 }
 
 ExceptionBreakpoint *DAP::GetExceptionBreakpoint(const lldb::break_id_t bp_id) {
-  for (auto &bp : exception_breakpoints) {
+  // See comment in the other GetExceptionBreakpoint().
+  PopulateExceptionBreakpoints();
+  assert(exception_breakpoints.has_value() &&
+         "exception_breakpoints must have been populated");
+
+  for (auto &bp : *exception_breakpoints) {
     if (bp.bp.GetID() == bp_id)
       return &bp;
   }
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index a88ee3e1dec6b..daa0d9f1aa7f0 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -156,7 +156,8 @@ struct DAP {
   std::unique_ptr<std::ofstream> log;
   llvm::StringMap<SourceBreakpointMap> source_breakpoints;
   FunctionBreakpointMap function_breakpoints;
-  std::vector<ExceptionBreakpoint> exception_breakpoints;
+  std::optional<std::vector<ExceptionBreakpoint>> exception_breakpoints;
+  llvm::once_flag initExceptionBreakpoints;
   std::vector<std::string> init_commands;
   std::vector<std::string> pre_run_commands;
   std::vector<std::string> post_run_commands;
@@ -228,6 +229,8 @@ struct DAP {
 
   llvm::json::Value CreateTopLevelScopes();
 
+  void PopulateExceptionBreakpoints();
+
   /// \return
   ///   Attempt to determine if an expression is a variable expression or
   ///   lldb command using a hueristic based on the first term of the
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 7746afb6cbbf3..470c9f84c6a20 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -16,6 +16,7 @@
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
+#include <optional>
 #include <sys/stat.h>
 #include <sys/types.h>
 #if defined(_WIN32)
@@ -1586,6 +1587,7 @@ void request_initialize(const llvm::json::Object &request) {
   bool source_init_file = GetBoolean(arguments, "sourceInitFile", true);
 
   g_dap.debugger = lldb::SBDebugger::Create(source_init_file, log_cb, nullptr);
+  g_dap.PopulateExceptionBreakpoints();
   auto cmd = g_dap.debugger.GetCommandInterpreter().AddMultiwordCommand(
       "lldb-dap", "Commands for managing lldb-dap.");
   if (GetBoolean(arguments, "supportsStartDebuggingRequest", false)) {
@@ -1621,7 +1623,7 @@ void request_initialize(const llvm::json::Object &request) {
   body.try_emplace("supportsEvaluateForHovers", true);
   // Available filters or options for the setExceptionBreakpoints request.
   llvm::json::Array filters;
-  for (const auto &exc_bp : g_dap.exception_breakpoints) {
+  for (const auto &exc_bp : *g_dap.exception_breakpoints) {
     filters.emplace_back(CreateExceptionBreakpointFilter(exc_bp));
   }
   body.try_emplace("exceptionBreakpointFilters", std::move(filters));
@@ -2476,7 +2478,7 @@ void request_setExceptionBreakpoints(const llvm::json::Object &request) {
   // Keep a list of any exception breakpoint filter names that weren't set
   // so we can clear any exception breakpoints if needed.
   std::set<std::string> unset_filters;
-  for (const auto &bp : g_dap.exception_breakpoints)
+  for (const auto &bp : *g_dap.exception_breakpoints)
     unset_filters.insert(bp.filter);
 
   for (const auto &value : *filters) {

Copy link

⚠️ We detected that you are using a GitHub private e-mail address to contribute to the repo.
Please turn off Keep my email addresses private setting in your account.
See LLVM Discourse for more information.

Copy link

github-actions bot commented Jun 14, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@labath labath requested a review from walter-erquinigo June 18, 2024 09:44
Comment on lines 83 to 84
assert(exception_breakpoints.has_value() && "should have been initted");
assert(!exception_breakpoints->empty() && "should not be empty");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems rather excessive. I realize you were just bitten by this, but the assertions basically amount to

int foo = 2+2;
// some code (not too much)
assert(foo == 4);

which we don't usually do.

Copy link
Member Author

@oontvoo oontvoo Jun 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The exceptions_breakpoints could be empty if for some reason all of the if-conditions above returned false. That would be unexpected and I think it's easier to catch the bug right here rather than later

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's the second assertion, not the first one. The first one is really just a test for the implementation of optional::operator=.

I don't have such strong feelings about the second one, although one could easily argue that the fact that lldb says it does not support any of the mentioned languages is not a bug (at least, not a bug in lldb-dap). Correctness of the code following the assertion does not depend on the vector being non-empty (only on its existence), so this feels like it would be better off as a test.

Comment on lines 89 to 106
// PopulateExceptionBreakpoints() is called after g_dap.debugger is created
// in a request-initialize.
//
// But this GetExceptionBreakpoint() method may be called before attaching, in
// which case, we may not have populated the filter yet.
//
// We also cannot call PopulateExceptionBreakpoints() in DAP::DAP() because
// we need SBDebugger::Initialize() to have been called before this.
//
// So just calling PopulateExceptionBreakoints(),which does lazy-populating
// seems easiest. Two other options include:
// + call g_dap.PopulateExceptionBreakpoints() in lldb-dap.cpp::main()
// right after the call to SBDebugger::Initialize()
// + Just call PopulateExceptionBreakpoints() to get a fresh list everytime
// we query (a bit overkill since it's not likely to change?)
PopulateExceptionBreakpoints();
assert(exception_breakpoints.has_value() &&
"exception_breakpoints must have been populated");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with the lazy call_once initialization, I think that a better interface would be to have the populate function directly return the exception lists that it has created (maybe then it should be called ArrayRef<ExceptionBreakpoint> GetExceptionBreakpoints()). Then there would be nothing to assert here, as the type system would make sure you return a valid exception list.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done - i've removed the unnecessary asserts(but keeping the method name to make it clearer that this function's job is to popualte the list)

@oontvoo
Copy link
Member Author

oontvoo commented Jun 21, 2024

Ran all the tests:

Unresolved Tests (23):
  lldb-api :: api/multithreaded/TestMultithreaded.py
  lldb-api :: commands/expression/multiline-completion/TestMultilineCompletion.py
  lldb-api :: commands/expression/multiline-navigation/TestMultilineNavigation.py
  lldb-api :: commands/gui/basic/TestGuiBasic.py
  lldb-api :: commands/gui/breakpoints/TestGuiBreakpoints.py
  lldb-api :: commands/gui/spawn-threads/TestGuiSpawnThreads.py
  lldb-api :: commands/gui/viewlarge/TestGuiViewLarge.py
  lldb-api :: driver/batch_mode/TestBatchMode.py
  lldb-api :: driver/job_control/TestJobControl.py
  lldb-api :: driver/quit_speed/TestQuitWithProcess.py
  lldb-api :: functionalities/breakpoint/breakpoint_callback_command_source/TestBreakpointCallbackCommandSource.py
  lldb-api :: functionalities/progress_reporting/TestTrimmedProgressReporting.py
  lldb-api :: iohandler/autosuggestion/TestAutosuggestion.py
  lldb-api :: iohandler/completion/TestIOHandlerCompletion.py
  lldb-api :: iohandler/resize/TestIOHandlerResize.py
  lldb-api :: iohandler/sigint/TestIOHandlerPythonREPLSigint.py
  lldb-api :: iohandler/sigint/TestProcessIOHandlerInterrupt.py
  lldb-api :: iohandler/stdio/TestIOHandlerProcessSTDIO.py
  lldb-api :: iohandler/unicode/TestUnicode.py
  lldb-api :: macosx/nslog/TestDarwinNSLogOutput.py
  lldb-api :: repl/clang/TestClangREPL.py
  lldb-api :: terminal/TestEditline.py
  lldb-api :: terminal/TestSTTYBeforeAndAfter.py

********************
Failed Tests (1):
  lldb-api :: commands/platform/sdk/TestPlatformSDK.py


Testing Time: 11310.46s

Total Discovered Tests: 1183
  Unsupported      : 155 (13.10%)
  Passed           : 988 (83.52%)
  Expectedly Failed:  16 (1.35%)
  Unresolved       :  23 (1.94%)
  Failed           :   1 (0.08%)

(Compared to main branch:

Unresolved Tests (27):
  lldb-api :: api/multithreaded/TestMultithreaded.py
  lldb-api :: commands/expression/import-std-module/deque-dbg-info-content/TestDbgInfoContentDequeFromStdModule.py
  lldb-api :: commands/expression/import-std-module/list-dbg-info-content/TestDbgInfoContentListFromStdModule.py
  lldb-api :: commands/expression/import-std-module/vector-dbg-info-content/TestDbgInfoContentVectorFromStdModule.py
  lldb-api :: commands/expression/multiline-completion/TestMultilineCompletion.py
  lldb-api :: commands/expression/multiline-navigation/TestMultilineNavigation.py
  lldb-api :: commands/gui/basic/TestGuiBasic.py
  lldb-api :: commands/gui/breakpoints/TestGuiBreakpoints.py
  lldb-api :: commands/gui/spawn-threads/TestGuiSpawnThreads.py
  lldb-api :: commands/gui/viewlarge/TestGuiViewLarge.py
  lldb-api :: driver/batch_mode/TestBatchMode.py
  lldb-api :: driver/job_control/TestJobControl.py
  lldb-api :: driver/quit_speed/TestQuitWithProcess.py
  lldb-api :: functionalities/breakpoint/breakpoint_callback_command_source/TestBreakpointCallbackCommandSource.py
  lldb-api :: functionalities/progress_reporting/TestTrimmedProgressReporting.py
  lldb-api :: iohandler/autosuggestion/TestAutosuggestion.py
  lldb-api :: iohandler/completion/TestIOHandlerCompletion.py
  lldb-api :: iohandler/resize/TestIOHandlerResize.py
  lldb-api :: iohandler/sigint/TestIOHandlerPythonREPLSigint.py
  lldb-api :: iohandler/sigint/TestProcessIOHandlerInterrupt.py
  lldb-api :: iohandler/stdio/TestIOHandlerProcessSTDIO.py
  lldb-api :: iohandler/unicode/TestUnicode.py
  lldb-api :: macosx/nslog/TestDarwinNSLogOutput.py
  lldb-api :: python_api/thread/TestThreadAPI.py
  lldb-api :: repl/clang/TestClangREPL.py
  lldb-api :: terminal/TestEditline.py
  lldb-api :: terminal/TestSTTYBeforeAndAfter.py

********************
Failed Tests (5):
  lldb-api :: commands/platform/sdk/TestPlatformSDK.py
  lldb-api :: functionalities/thread/concurrent_events/TestConcurrentBreakpointsDelayedBreakpointOneWatchpoint.py
  lldb-api :: lang/cpp/constructors/TestCppConstructors.py
  lldb-api :: lang/objc/modules-non-objc-target/TestObjCModulesNonObjCTarget.py
  lldb-api :: python_api/lldbutil/iter/TestLLDBIterator.py


Testing Time: 11040.07s

Total Discovered Tests: 1183
  Unsupported      : 155 (13.10%)
  Passed           : 980 (82.84%)
  Expectedly Failed:  16 (1.35%)
  Unresolved       :  27 (2.28%)
  Failed           :   5 (0.42%)

)

@oontvoo oontvoo merged commit e951bd0 into llvm:main Jun 25, 2024
AlexisPerry pushed a commit to llvm-project-tlp/llvm-project that referenced this pull request Jul 9, 2024
New fixes:
- properly init the `std::optional<std::vector>` to an empty vector as
opposed to `{}` (which was effectively `std::nullopt`).

---------

Co-authored-by: Vy Nguyen <oontvoo@users.noreply.github.com>
@oontvoo oontvoo deleted the reapply_2nd_87550 branch June 2, 2025 17:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants