[Lldb-commits] [lldb] 541f22e - [lldb-dap] Support throw and catch exception breakpoints for dynamica… (#97871)
via lldb-commits
lldb-commits at lists.llvm.org
Wed Jul 10 16:05:41 PDT 2024
Author: Walter Erquinigo
Date: 2024-07-10T19:05:38-04:00
New Revision: 541f22ee361a8b3029ac898db29d3e9184fb1671
URL: https://github.com/llvm/llvm-project/commit/541f22ee361a8b3029ac898db29d3e9184fb1671
DIFF: https://github.com/llvm/llvm-project/commit/541f22ee361a8b3029ac898db29d3e9184fb1671.diff
LOG: [lldb-dap] Support throw and catch exception breakpoints for dynamica… (#97871)
…lly 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.
Added:
Modified:
lldb/include/lldb/API/SBLanguageRuntime.h
lldb/include/lldb/Target/Language.h
lldb/source/API/SBLanguageRuntime.cpp
lldb/tools/lldb-dap/DAP.cpp
lldb/tools/lldb-dap/DAP.h
lldb/tools/lldb-dap/Options.td
lldb/tools/lldb-dap/lldb-dap.cpp
Removed:
################################################################################
diff --git a/lldb/include/lldb/API/SBLanguageRuntime.h b/lldb/include/lldb/API/SBLanguageRuntime.h
index 38aac05d490c1..011015ec46463 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..958652ab6f136 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()).AsCString();
+ return nullptr;
+}
+
+const char *
+SBLanguageRuntime::GetCatchKeywordForLanguage(lldb::LanguageType language) {
+ if (Language *lang_plugin = Language::FindPlugin(language))
+ return ConstString(lang_plugin->GetCatchKeyword()).AsCString();
+ return nullptr;
+}
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 0196aed819f2b..c3c70e9d73984 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,49 @@ 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);
+
+ if (lldb::SBLanguageRuntime::SupportsExceptionBreakpointsOnThrow(lang)) {
+ const char *raw_throw_keyword =
+ lldb::SBLanguageRuntime::GetThrowKeywordForLanguage(lang);
+ std::string throw_keyword =
+ raw_throw_keyword ? raw_throw_keyword : "throw";
+
+ exception_breakpoints->emplace_back(
+ raw_lang_name + "_" + throw_keyword,
+ capitalized_lang_name + " " + capitalize(throw_keyword), lang);
+ }
+
+ if (lldb::SBLanguageRuntime::SupportsExceptionBreakpointsOnCatch(lang)) {
+ const char *raw_catch_keyword =
+ lldb::SBLanguageRuntime::GetCatchKeywordForLanguage(lang);
+ std::string catch_keyword =
+ raw_catch_keyword ? raw_catch_keyword : "catch";
+
+ 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 +564,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)
More information about the lldb-commits
mailing list