https://github.com/walter-erquinigo updated 
https://github.com/llvm/llvm-project/pull/97871

>From aa2ad3b675f67581dde07a476725d2574fc6e7da Mon Sep 17 00:00:00 2001
From: walter erquinigo <wal...@modular.com>
Date: Fri, 5 Jul 2024 20:30:44 -0400
Subject: [PATCH] [lldb-dap] Support throw and catch exception breakpoints for
 dynamically registered languages

First of all, this is done to support exceptions for the Mojo language, but 
it's done in a way that will benefit any other plugin language.

1. I added a new lldb-dap CLI argument (not DAP field) called 
`pre-init-commands`. These commands are executed before DAP initialization. The 
other `init-commands` are executed after DAP initialization. It's worth 
mentioning that the debug adapter returns to VSCode the list of supported 
exception breakpoints during DAP initialization, which means that I need to 
register the Mojo plugin before that initialization step, hence the need for 
`pre-init-commands`. In general, language plugins should be registered in that 
step, as they affect the capabilities of the debugger.
2. I added a set of APIs for lldb-dap to query information of each language 
related to exception breakpoints. E.g. whether a language supports throw or 
catch breakpoints, how the throw keyword is called in each particular language, 
etc.
3. I'm realizing that the Swift support for exception breakpoints in lldb-dap 
should have been implemented in this way, instead of hardcoding it.
---
 lldb/include/lldb/API/SBLanguageRuntime.h | 26 +++++++++++
 lldb/include/lldb/Target/Language.h       |  8 ++++
 lldb/source/API/SBLanguageRuntime.cpp     | 40 +++++++++++++++++
 lldb/tools/lldb-dap/DAP.cpp               | 54 ++++++++++++++++++++++-
 lldb/tools/lldb-dap/DAP.h                 |  2 +
 lldb/tools/lldb-dap/Options.td            |  8 ++++
 lldb/tools/lldb-dap/lldb-dap.cpp          | 19 ++++++--
 7 files changed, 153 insertions(+), 4 deletions(-)

diff --git a/lldb/include/lldb/API/SBLanguageRuntime.h 
b/lldb/include/lldb/API/SBLanguageRuntime.h
index 38aac05d490c1..acdc256fa2ac5 100644
--- a/lldb/include/lldb/API/SBLanguageRuntime.h
+++ b/lldb/include/lldb/API/SBLanguageRuntime.h
@@ -18,6 +18,32 @@ class SBLanguageRuntime {
   static lldb::LanguageType GetLanguageTypeFromString(const char *string);
 
   static const char *GetNameForLanguageType(lldb::LanguageType language);
+
+  /// Returns whether the given language is any version of  C++.
+  static bool LanguageIsCPlusPlus(lldb::LanguageType language);
+
+  /// Returns whether the given language is Obj-C or Obj-C++.
+  static bool LanguageIsObjC(lldb::LanguageType language);
+
+  /// Returns whether the given language is any version of C, C++ or Obj-C.
+  static bool LanguageIsCFamily(lldb::LanguageType language);
+
+  /// Returns whether the given language supports exception breakpoints on
+  /// throw statements.
+  static bool SupportsExceptionBreakpointsOnThrow(lldb::LanguageType language);
+
+  /// Returns whether the given language supports exception breakpoints on
+  /// catch statements.
+  static bool SupportsExceptionBreakpointsOnCatch(lldb::LanguageType language);
+
+  /// Returns the keyword used for throw statements in the given language, e.g.
+  /// Python uses \b raise. Returns \b nullptr if the language is not 
supported.
+  static const char *GetThrowKeywordForLanguage(lldb::LanguageType language);
+
+  /// Returns the keyword used for catch statements in the given language, e.g.
+  /// Python uses \b except. Returns \b nullptr if the language is not
+  /// supported.
+  static const char *GetCatchKeywordForLanguage(lldb::LanguageType language);
 };
 
 } // namespace lldb
diff --git a/lldb/include/lldb/Target/Language.h 
b/lldb/include/lldb/Target/Language.h
index 83bf7635e369a..41d8eeef469ea 100644
--- a/lldb/include/lldb/Target/Language.h
+++ b/lldb/include/lldb/Target/Language.h
@@ -371,6 +371,14 @@ class Language : public PluginInterface {
   /// a corresponding LanguageRuntime plugin.
   virtual bool SupportsExceptionBreakpointsOnCatch() const { return false; }
 
+  /// Returns the keyword used for throw statements in this language, e.g.
+  /// Python uses \b raise. Defaults to \b throw.
+  virtual llvm::StringRef GetThrowKeyword() const { return "throw"; }
+
+  /// Returns the keyword used for catch statements in this language, e.g.
+  /// Python uses \b except. Defaults to \b catch.
+  virtual llvm::StringRef GetCatchKeyword() const { return "catch"; }
+
 protected:
   // Classes that inherit from Language can see and modify these
 
diff --git a/lldb/source/API/SBLanguageRuntime.cpp 
b/lldb/source/API/SBLanguageRuntime.cpp
index d571f282fce03..8599c2598b9fb 100644
--- a/lldb/source/API/SBLanguageRuntime.cpp
+++ b/lldb/source/API/SBLanguageRuntime.cpp
@@ -26,3 +26,43 @@ SBLanguageRuntime::GetNameForLanguageType(lldb::LanguageType 
language) {
 
   return Language::GetNameForLanguageType(language);
 }
+
+bool SBLanguageRuntime::LanguageIsCPlusPlus(lldb::LanguageType language) {
+  return Language::LanguageIsCPlusPlus(language);
+}
+
+bool SBLanguageRuntime::LanguageIsObjC(lldb::LanguageType language) {
+  return Language::LanguageIsObjC(language);
+}
+
+bool SBLanguageRuntime::LanguageIsCFamily(lldb::LanguageType language) {
+  return Language::LanguageIsCFamily(language);
+}
+
+bool SBLanguageRuntime::SupportsExceptionBreakpointsOnThrow(
+    lldb::LanguageType language) {
+  if (Language *lang_plugin = Language::FindPlugin(language))
+    return lang_plugin->SupportsExceptionBreakpointsOnThrow();
+  return false;
+}
+
+bool SBLanguageRuntime::SupportsExceptionBreakpointsOnCatch(
+    lldb::LanguageType language) {
+  if (Language *lang_plugin = Language::FindPlugin(language))
+    return lang_plugin->SupportsExceptionBreakpointsOnCatch();
+  return false;
+}
+
+const char *
+SBLanguageRuntime::GetThrowKeywordForLanguage(lldb::LanguageType language) {
+  if (Language *lang_plugin = Language::FindPlugin(language))
+    return ConstString(lang_plugin->GetThrowKeyword().data()).AsCString();
+  return nullptr;
+}
+
+const char *
+SBLanguageRuntime::GetCatchKeywordForLanguage(lldb::LanguageType language) {
+  if (Language *lang_plugin = Language::FindPlugin(language))
+    return ConstString(lang_plugin->GetCatchKeyword().data()).AsCString();
+  return nullptr;
+}
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 0196aed819f2b..07c3391267dc8 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -58,10 +58,17 @@ DAP::DAP()
 
 DAP::~DAP() = default;
 
+/// Return string with first character capitalized.
+static std::string capitalize(llvm::StringRef str) {
+  if (str.empty())
+    return "";
+  return ((llvm::Twine)llvm::toUpper(str[0]) + str.drop_front()).str();
+}
+
 void DAP::PopulateExceptionBreakpoints() {
   llvm::call_once(init_exception_breakpoints_flag, [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);
@@ -80,6 +87,45 @@ void DAP::PopulateExceptionBreakpoints() {
       exception_breakpoints->emplace_back("swift_throw", "Swift Throw",
                                           lldb::eLanguageTypeSwift);
     }
+    // Besides handling the hardcoded list of languages from above, we try to
+    // find any other languages that support exception breakpoints using the
+    // SB API.
+    for (int raw_lang = lldb::eLanguageTypeUnknown;
+         raw_lang < lldb::eNumLanguageTypes; ++raw_lang) {
+      lldb::LanguageType lang = static_cast<lldb::LanguageType>(raw_lang);
+
+      // We first discard any languages already handled above.
+      if (lldb::SBLanguageRuntime::LanguageIsCFamily(lang) ||
+          lang == lldb::eLanguageTypeSwift)
+        continue;
+
+      if (!lldb::SBDebugger::SupportsLanguage(lang))
+        continue;
+
+      const char *name = lldb::SBLanguageRuntime::GetNameForLanguageType(lang);
+      if (!name)
+        continue;
+      std::string raw_lang_name = name;
+      std::string capitalized_lang_name = capitalize(name);
+      const char *raw_throw_keyword =
+          lldb::SBLanguageRuntime::GetThrowKeywordForLanguage(lang);
+      const char *raw_catch_keyword =
+          lldb::SBLanguageRuntime::GetCatchKeywordForLanguage(lang);
+      std::string throw_keyword =
+          raw_throw_keyword ? raw_throw_keyword : "throw";
+      std::string catch_keyword =
+          raw_catch_keyword ? raw_catch_keyword : "catch";
+
+      if (lldb::SBLanguageRuntime::SupportsExceptionBreakpointsOnThrow(lang))
+        exception_breakpoints->emplace_back(
+            raw_lang_name + "_" + throw_keyword,
+            capitalized_lang_name + " " + capitalize(throw_keyword), lang);
+
+      if (lldb::SBLanguageRuntime::SupportsExceptionBreakpointsOnCatch(lang))
+        exception_breakpoints->emplace_back(
+            raw_lang_name + "_" + catch_keyword,
+            capitalized_lang_name + " " + capitalize(catch_keyword), lang);
+    }
     assert(!exception_breakpoints->empty() && "should not be empty");
   });
 }
@@ -514,6 +560,12 @@ llvm::Error DAP::RunInitCommands() {
   return llvm::Error::success();
 }
 
+llvm::Error DAP::RunPreInitCommands() {
+  if (!RunLLDBCommands("Running preInitCommands:", pre_init_commands))
+    return createRunLLDBCommandsErrorMessage("preInitCommands");
+  return llvm::Error::success();
+}
+
 llvm::Error DAP::RunPreRunCommands() {
   if (!RunLLDBCommands("Running preRunCommands:", pre_run_commands))
     return createRunLLDBCommandsErrorMessage("preRunCommands");
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index 37e57d58968d9..57562a1498351 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -158,6 +158,7 @@ struct DAP {
   FunctionBreakpointMap function_breakpoints;
   std::optional<std::vector<ExceptionBreakpoint>> exception_breakpoints;
   llvm::once_flag init_exception_breakpoints_flag;
+  std::vector<std::string> pre_init_commands;
   std::vector<std::string> init_commands;
   std::vector<std::string> pre_run_commands;
   std::vector<std::string> post_run_commands;
@@ -246,6 +247,7 @@ struct DAP {
 
   llvm::Error RunAttachCommands(llvm::ArrayRef<std::string> attach_commands);
   llvm::Error RunLaunchCommands(llvm::ArrayRef<std::string> launch_commands);
+  llvm::Error RunPreInitCommands();
   llvm::Error RunInitCommands();
   llvm::Error RunPreRunCommands();
   void RunPostRunCommands();
diff --git a/lldb/tools/lldb-dap/Options.td b/lldb/tools/lldb-dap/Options.td
index 571967b232b4a..d7b4a065abec0 100644
--- a/lldb/tools/lldb-dap/Options.td
+++ b/lldb/tools/lldb-dap/Options.td
@@ -43,3 +43,11 @@ def debugger_pid: S<"debugger-pid">,
 def repl_mode: S<"repl-mode">,
   MetaVarName<"<mode>">,
   HelpText<"The mode for handling repl evaluation requests, supported modes: 
variable, command, auto.">;
+
+def pre_init_command: S<"pre-init-command">,
+  MetaVarName<"<command>">,
+  HelpText<"A command to execute before the DAP initialization request and "
+    "right after a Debugger has been created.">;
+def: Separate<["-"], "c">,
+  Alias<pre_init_command>,
+  HelpText<"Alias for --pre-init-command">;
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index b74474b9d383c..b50d40acb51a2 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -1600,6 +1600,10 @@ void request_modules(const llvm::json::Object &request) {
 //   }]
 // }
 void request_initialize(const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  llvm::json::Object body;
+
   auto log_cb = [](const char *buf, void *baton) -> void {
     g_dap.SendOutput(OutputType::Console, llvm::StringRef{buf});
   };
@@ -1611,6 +1615,13 @@ 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);
+  if (llvm::Error err = g_dap.RunPreInitCommands()) {
+    response["success"] = false;
+    EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
+    g_dap.SendJSON(llvm::json::Value(std::move(response)));
+    return;
+  }
+
   g_dap.PopulateExceptionBreakpoints();
   auto cmd = g_dap.debugger.GetCommandInterpreter().AddMultiwordCommand(
       "lldb-dap", "Commands for managing lldb-dap.");
@@ -1630,9 +1641,6 @@ void request_initialize(const llvm::json::Object 
&request) {
   // process and more.
   g_dap.event_thread = std::thread(EventThreadFunction);
 
-  llvm::json::Object response;
-  FillResponse(request, response);
-  llvm::json::Object body;
   // The debug adapter supports the configurationDoneRequest.
   body.try_emplace("supportsConfigurationDoneRequest", true);
   // The debug adapter supports function breakpoints.
@@ -4318,6 +4326,11 @@ int main(int argc, char *argv[]) {
     g_dap.output.descriptor = StreamDescriptor::from_file(new_stdout_fd, 
false);
   }
 
+  for (const std::string &arg :
+       input_args.getAllArgValues(OPT_pre_init_command)) {
+    g_dap.pre_init_commands.push_back(arg);
+  }
+
   bool CleanExit = true;
   if (auto Err = g_dap.Loop()) {
     if (g_dap.log)

_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to