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) _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits