https://github.com/ashgti updated https://github.com/llvm/llvm-project/pull/144153
>From e54b23052c17efd61297db619c355749d51e34c6 Mon Sep 17 00:00:00 2001 From: John Harrison <harj...@google.com> Date: Fri, 13 Jun 2025 11:58:27 -0700 Subject: [PATCH 1/2] [lldb-dap] Creating protocol types for setExceptionBreakpoints. This adds new types for setExceptionBreakpoints and adds support for `supportsExceptionFilterOptions`, which allows exception breakpoints to set a condition. --- .../test/tools/lldb-dap/dap_server.py | 6 +- .../TestDAP_setExceptionBreakpoints.py | 11 +- .../tools/lldb-dap/exception/objc/Makefile | 2 +- .../exception/objc/TestDAP_exception_objc.py | 39 ++++- .../API/tools/lldb-dap/exception/objc/main.m | 12 +- lldb/tools/lldb-dap/DAP.cpp | 156 ++++++++---------- lldb/tools/lldb-dap/DAP.h | 4 +- lldb/tools/lldb-dap/ExceptionBreakpoint.cpp | 25 ++- lldb/tools/lldb-dap/ExceptionBreakpoint.h | 10 +- .../Handler/InitializeRequestHandler.cpp | 1 - lldb/tools/lldb-dap/Handler/RequestHandler.h | 15 +- .../SetExceptionBreakpointsRequestHandler.cpp | 107 +++++------- lldb/tools/lldb-dap/JSONUtils.cpp | 3 + .../lldb-dap/Protocol/ProtocolRequests.cpp | 14 ++ .../lldb-dap/Protocol/ProtocolRequests.h | 50 ++++++ .../tools/lldb-dap/Protocol/ProtocolTypes.cpp | 44 +++-- lldb/tools/lldb-dap/Protocol/ProtocolTypes.h | 33 +++- lldb/unittests/DAP/ProtocolTypesTest.cpp | 109 +++++++++--- 18 files changed, 414 insertions(+), 227 deletions(-) diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py index 9786678aa53f9..c1108da17123b 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py @@ -1050,8 +1050,12 @@ def request_setBreakpoints(self, source: Source, line_array, data=None): self._update_verified_breakpoints(response["body"]["breakpoints"]) return response - def request_setExceptionBreakpoints(self, filters): + def request_setExceptionBreakpoints( + self, *, filters: list[str] = [], filter_options: list[dict] = [] + ): args_dict = {"filters": filters} + if filter_options: + args_dict["filterOptions"] = filter_options command_dict = { "command": "setExceptionBreakpoints", "type": "request", diff --git a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setExceptionBreakpoints.py b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setExceptionBreakpoints.py index 4dc8c5b3c7ded..4ca733a9a59ca 100644 --- a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setExceptionBreakpoints.py +++ b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setExceptionBreakpoints.py @@ -1,16 +1,12 @@ """ -Test lldb-dap setBreakpoints request +Test lldb-dap setExceptionBreakpoints request """ - -import dap_server from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * -from lldbsuite.test import lldbutil import lldbdap_testcase -@skip("Temporarily disable the breakpoint tests") class TestDAP_setExceptionBreakpoints(lldbdap_testcase.DAPTestCaseBase): @skipIfWindows def test_functionality(self): @@ -33,8 +29,9 @@ def test_functionality(self): program = self.getBuildArtifact("a.out") self.build_and_launch(program) - filters = ["cpp_throw", "cpp_catch"] - response = self.dap_server.request_setExceptionBreakpoints(filters) + response = self.dap_server.request_setExceptionBreakpoints( + filters=["cpp_throw", "cpp_catch"], + ) if response: self.assertTrue(response["success"]) diff --git a/lldb/test/API/tools/lldb-dap/exception/objc/Makefile b/lldb/test/API/tools/lldb-dap/exception/objc/Makefile index 9b6528337cb9d..17e6dc76699ab 100644 --- a/lldb/test/API/tools/lldb-dap/exception/objc/Makefile +++ b/lldb/test/API/tools/lldb-dap/exception/objc/Makefile @@ -1,6 +1,6 @@ OBJC_SOURCES := main.m -CFLAGS_EXTRAS := -w +CFLAGS_EXTRAS := -w -fobjc-exceptions USE_SYSTEM_STDLIB := 1 diff --git a/lldb/test/API/tools/lldb-dap/exception/objc/TestDAP_exception_objc.py b/lldb/test/API/tools/lldb-dap/exception/objc/TestDAP_exception_objc.py index 777d55f48e850..ddedf7a6de8c6 100644 --- a/lldb/test/API/tools/lldb-dap/exception/objc/TestDAP_exception_objc.py +++ b/lldb/test/API/tools/lldb-dap/exception/objc/TestDAP_exception_objc.py @@ -2,7 +2,6 @@ Test exception behavior in DAP with obj-c throw. """ - from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * import lldbdap_testcase @@ -25,3 +24,41 @@ def test_stopped_description(self): exception_details = exception_info["details"] self.assertRegex(exception_details["message"], "SomeReason") self.assertRegex(exception_details["stackTrace"], "main.m") + + @skipUnlessDarwin + def test_break_on_throw_and_catch(self): + """ + Test that breakpoints on exceptions work as expected. + """ + program = self.getBuildArtifact("a.out") + self.build_and_launch(program) + + response = self.dap_server.request_setExceptionBreakpoints( + filter_options=[ + { + "filterId": "objc_throw", + "condition": '[[((NSException *)$arg1) name] isEqual:@"ThrownException"]', + }, + ] + ) + if response: + self.assertTrue(response["success"]) + + self.continue_to_exception_breakpoint("Objective-C Throw") + + # FIXME: Catching objc exceptions do not appear to be working. + # Xcode appears to set a breakpoint on '__cxa_begin_catch' for objc + # catch, which is different than + # SBTarget::BreakpointCreateForException(eLanguageObjectiveC, /*catch_bp=*/true, /*throw_bp=*/false); + # self.continue_to_exception_breakpoint("Objective-C Catch") + + self.do_continue() + + self.assertTrue(self.verify_stop_exception_info("signal SIGABRT")) + exception_info = self.get_exceptionInfo() + self.assertEqual(exception_info["breakMode"], "always") + self.assertEqual(exception_info["description"], "signal SIGABRT") + self.assertEqual(exception_info["exceptionId"], "signal") + exception_details = exception_info["details"] + self.assertRegex(exception_details["message"], "SomeReason") + self.assertRegex(exception_details["stackTrace"], "main.m") diff --git a/lldb/test/API/tools/lldb-dap/exception/objc/main.m b/lldb/test/API/tools/lldb-dap/exception/objc/main.m index e8db04fb40de1..bbfa621992799 100644 --- a/lldb/test/API/tools/lldb-dap/exception/objc/main.m +++ b/lldb/test/API/tools/lldb-dap/exception/objc/main.m @@ -1,8 +1,14 @@ #import <Foundation/Foundation.h> int main(int argc, char const *argv[]) { - @throw [[NSException alloc] initWithName:@"ThrownException" - reason:@"SomeReason" - userInfo:nil]; + @try { + NSException *e = [[NSException alloc] initWithName:@"ThrownException" + reason:@"SomeReason" + userInfo:nil]; + @throw e; + } @catch (NSException *e) { + NSLog(@"Caught %@", e); + @throw; // let the process crash... + } return 0; } diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index b034c967594ba..58e0be64ba5b8 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -128,93 +128,81 @@ DAP::DAP(Log *log, const ReplMode default_repl_mode, DAP::~DAP() = default; 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(*this, "cpp_catch", "C++ Catch", - lldb::eLanguageTypeC_plus_plus); - exception_breakpoints->emplace_back(*this, "cpp_throw", "C++ Throw", - lldb::eLanguageTypeC_plus_plus); - } - if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeObjC)) { - exception_breakpoints->emplace_back( - *this, "objc_catch", "Objective-C Catch", lldb::eLanguageTypeObjC); - exception_breakpoints->emplace_back( - *this, "objc_throw", "Objective-C Throw", lldb::eLanguageTypeObjC); - } - if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeSwift)) { - exception_breakpoints->emplace_back(*this, "swift_catch", "Swift Catch", - lldb::eLanguageTypeSwift); - exception_breakpoints->emplace_back(*this, "swift_throw", "Swift Throw", - lldb::eLanguageTypeSwift); + if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeC_plus_plus)) { + exception_breakpoints.emplace_back(*this, "cpp_catch", "C++ Catch", + lldb::eLanguageTypeC_plus_plus, + /*is_throw=*/false, /*is_catch=*/true); + exception_breakpoints.emplace_back(*this, "cpp_throw", "C++ Throw", + lldb::eLanguageTypeC_plus_plus, + /*is_throw=*/true, /*is_catch=*/false); + } + + if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeObjC)) { + exception_breakpoints.emplace_back(*this, "objc_catch", "Objective-C Catch", + lldb::eLanguageTypeObjC, + /*is_throw=*/false, /*is_catch=*/true); + exception_breakpoints.emplace_back(*this, "objc_throw", "Objective-C Throw", + lldb::eLanguageTypeObjC, + /*is_throw=*/true, /*is_catch=*/false); + } + + if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeSwift)) { + exception_breakpoints.emplace_back(*this, "swift_catch", "Swift Catch", + lldb::eLanguageTypeSwift, + /*is_throw=*/false, /*is_catch=*/true); + exception_breakpoints.emplace_back(*this, "swift_throw", "Swift Throw", + lldb::eLanguageTypeSwift, + /*is_throw=*/true, /*is_catch=*/false); + } + + // 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( + *this, raw_lang_name + "_" + throw_keyword, + capitalized_lang_name + " " + capitalize(throw_keyword), lang, + /*is_throw=*/true, /*is_catch=*/false); } - // 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( - *this, 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"; + 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( - *this, raw_lang_name + "_" + catch_keyword, - capitalized_lang_name + " " + capitalize(catch_keyword), lang); - } + exception_breakpoints.emplace_back( + *this, raw_lang_name + "_" + catch_keyword, + capitalized_lang_name + " " + capitalize(catch_keyword), lang, + /*is_throw=*/true, /*is_catch=*/false); } - assert(!exception_breakpoints->empty() && "should not be empty"); - }); + } } ExceptionBreakpoint *DAP::GetExceptionBreakpoint(llvm::StringRef filter) { - // 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(); - - for (auto &bp : *exception_breakpoints) { + for (auto &bp : exception_breakpoints) { if (bp.GetFilter() == filter) return &bp; } @@ -222,10 +210,7 @@ ExceptionBreakpoint *DAP::GetExceptionBreakpoint(llvm::StringRef filter) { } ExceptionBreakpoint *DAP::GetExceptionBreakpoint(const lldb::break_id_t bp_id) { - // See comment in the other GetExceptionBreakpoint(). - PopulateExceptionBreakpoints(); - - for (auto &bp : *exception_breakpoints) { + for (auto &bp : exception_breakpoints) { if (bp.GetID() == bp_id) return &bp; } @@ -1117,8 +1102,9 @@ protocol::Capabilities DAP::GetCapabilities() { } // Available filters or options for the setExceptionBreakpoints request. + PopulateExceptionBreakpoints(); std::vector<protocol::ExceptionBreakpointsFilter> filters; - for (const auto &exc_bp : *exception_breakpoints) + for (const auto &exc_bp : exception_breakpoints) filters.emplace_back(CreateExceptionBreakpointFilter(exc_bp)); capabilities.exceptionBreakpointFilters = std::move(filters); diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index 89bc827c1141f..5ca5822f9bced 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -99,7 +99,7 @@ struct DAP { lldb::SBBroadcaster broadcaster; FunctionBreakpointMap function_breakpoints; InstructionBreakpointMap instruction_breakpoints; - std::optional<std::vector<ExceptionBreakpoint>> exception_breakpoints; + std::vector<ExceptionBreakpoint> exception_breakpoints; llvm::once_flag init_exception_breakpoints_flag; /// Map step in target id to list of function targets that user can choose. @@ -320,7 +320,7 @@ struct DAP { }); } - /// The set of capablities supported by this adapter. + /// The set of capabilities supported by this adapter. protocol::Capabilities GetCapabilities(); /// Debuggee will continue from stopped state. diff --git a/lldb/tools/lldb-dap/ExceptionBreakpoint.cpp b/lldb/tools/lldb-dap/ExceptionBreakpoint.cpp index 9772e7344ced6..2531291fd62cc 100644 --- a/lldb/tools/lldb-dap/ExceptionBreakpoint.cpp +++ b/lldb/tools/lldb-dap/ExceptionBreakpoint.cpp @@ -9,23 +9,32 @@ #include "ExceptionBreakpoint.h" #include "BreakpointBase.h" #include "DAP.h" +#include "Protocol/ProtocolTypes.h" #include "lldb/API/SBMutex.h" #include "lldb/API/SBTarget.h" #include <mutex> +using namespace llvm; +using namespace lldb_dap::protocol; + namespace lldb_dap { -void ExceptionBreakpoint::SetBreakpoint() { +protocol::Breakpoint ExceptionBreakpoint::SetBreakpoint(StringRef condition) { lldb::SBMutex lock = m_dap.GetAPIMutex(); std::lock_guard<lldb::SBMutex> guard(lock); - if (m_bp.IsValid()) - return; - bool catch_value = m_filter.find("_catch") != std::string::npos; - bool throw_value = m_filter.find("_throw") != std::string::npos; - m_bp = m_dap.target.BreakpointCreateForException(m_language, catch_value, - throw_value); - m_bp.AddName(BreakpointBase::kDAPBreakpointLabel); + if (!m_bp.IsValid()) { + m_bp = m_dap.target.BreakpointCreateForException(m_language, m_is_catch, + m_is_throw); + m_bp.AddName(BreakpointBase::kDAPBreakpointLabel); + } + + m_bp.SetCondition(condition.data()); + + protocol::Breakpoint breakpoint; + breakpoint.id = m_bp.GetID(); + breakpoint.verified = m_bp.IsValid(); + return breakpoint; } void ExceptionBreakpoint::ClearBreakpoint() { diff --git a/lldb/tools/lldb-dap/ExceptionBreakpoint.h b/lldb/tools/lldb-dap/ExceptionBreakpoint.h index 319b472a89a34..d453d5fcc4fd3 100644 --- a/lldb/tools/lldb-dap/ExceptionBreakpoint.h +++ b/lldb/tools/lldb-dap/ExceptionBreakpoint.h @@ -10,6 +10,7 @@ #define LLDB_TOOLS_LLDB_DAP_EXCEPTIONBREAKPOINT_H #include "DAPForward.h" +#include "Protocol/ProtocolTypes.h" #include "lldb/API/SBBreakpoint.h" #include "lldb/lldb-enumerations.h" #include "llvm/ADT/StringRef.h" @@ -21,11 +22,12 @@ namespace lldb_dap { class ExceptionBreakpoint { public: ExceptionBreakpoint(DAP &d, std::string f, std::string l, - lldb::LanguageType lang) + lldb::LanguageType lang, bool is_throw, bool is_catch) : m_dap(d), m_filter(std::move(f)), m_label(std::move(l)), - m_language(lang), m_bp() {} + m_language(lang), m_is_throw(is_throw), m_is_catch(is_catch), m_bp() {} - void SetBreakpoint(); + protocol::Breakpoint SetBreakpoint() { return SetBreakpoint(""); }; + protocol::Breakpoint SetBreakpoint(llvm::StringRef condition); void ClearBreakpoint(); lldb::break_id_t GetID() const { return m_bp.GetID(); } @@ -39,6 +41,8 @@ class ExceptionBreakpoint { std::string m_filter; std::string m_label; lldb::LanguageType m_language; + bool m_is_throw; + bool m_is_catch; lldb::SBBreakpoint m_bp; }; diff --git a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp index dcd02d61ca4f4..b499a69876e2c 100644 --- a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp @@ -54,7 +54,6 @@ llvm::Expected<InitializeResponse> InitializeRequestHandler::Run( if (llvm::Error err = dap.RunPreInitCommands()) return err; - dap.PopulateExceptionBreakpoints(); auto cmd = dap.debugger.GetCommandInterpreter().AddMultiwordCommand( "lldb-dap", "Commands for managing lldb-dap."); if (arguments.supportedFeatures.contains( diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h index d3f231589b54c..071a44bd002ad 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.h +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h @@ -386,14 +386,21 @@ class SetBreakpointsRequestHandler Run(const protocol::SetBreakpointsArguments &args) const override; }; -class SetExceptionBreakpointsRequestHandler : public LegacyRequestHandler { +class SetExceptionBreakpointsRequestHandler + : public RequestHandler< + protocol::SetExceptionBreakpointsArguments, + llvm::Expected<protocol::SetExceptionBreakpointsResponseBody>> { public: - using LegacyRequestHandler::LegacyRequestHandler; + using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "setExceptionBreakpoints"; } FeatureSet GetSupportedFeatures() const override { - return {protocol::eAdapterFeatureExceptionOptions}; + /// Prefer the `filterOptions` feature over the `exceptionOptions`. + /// exceptionOptions is not supported in VSCode, while `filterOptions` is + /// supported. + return {protocol::eAdapterFeatureExceptionFilterOptions}; } - void operator()(const llvm::json::Object &request) const override; + llvm::Expected<protocol::SetExceptionBreakpointsResponseBody> + Run(const protocol::SetExceptionBreakpointsArguments &args) const override; }; class SetFunctionBreakpointsRequestHandler diff --git a/lldb/tools/lldb-dap/Handler/SetExceptionBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetExceptionBreakpointsRequestHandler.cpp index 2214833f8a770..6a271fb825137 100644 --- a/lldb/tools/lldb-dap/Handler/SetExceptionBreakpointsRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/SetExceptionBreakpointsRequestHandler.cpp @@ -8,86 +8,61 @@ #include "DAP.h" #include "EventHelper.h" -#include "JSONUtils.h" +#include "Protocol/ProtocolRequests.h" #include "RequestHandler.h" #include <set> +using namespace llvm; +using namespace lldb_dap::protocol; + namespace lldb_dap { -// "SetExceptionBreakpointsRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "SetExceptionBreakpoints request; value of command field -// is 'setExceptionBreakpoints'. The request configures the debuggers -// response to thrown exceptions. If an exception is configured to break, a -// StoppedEvent is fired (event type 'exception').", "properties": { -// "command": { -// "type": "string", -// "enum": [ "setExceptionBreakpoints" ] -// }, -// "arguments": { -// "$ref": "#/definitions/SetExceptionBreakpointsArguments" -// } -// }, -// "required": [ "command", "arguments" ] -// }] -// }, -// "SetExceptionBreakpointsArguments": { -// "type": "object", -// "description": "Arguments for 'setExceptionBreakpoints' request.", -// "properties": { -// "filters": { -// "type": "array", -// "items": { -// "type": "string" -// }, -// "description": "IDs of checked exception options. The set of IDs is -// returned via the 'exceptionBreakpointFilters' capability." -// }, -// "exceptionOptions": { -// "type": "array", -// "items": { -// "$ref": "#/definitions/ExceptionOptions" -// }, -// "description": "Configuration options for selected exceptions." -// } -// }, -// "required": [ "filters" ] -// }, -// "SetExceptionBreakpointsResponse": { -// "allOf": [ { "$ref": "#/definitions/Response" }, { -// "type": "object", -// "description": "Response to 'setExceptionBreakpoints' request. This is -// just an acknowledgement, so no body field is required." -// }] -// } -void SetExceptionBreakpointsRequestHandler::operator()( - const llvm::json::Object &request) const { - llvm::json::Object response; - lldb::SBError error; - FillResponse(request, response); - const auto *arguments = request.getObject("arguments"); - const auto *filters = arguments->getArray("filters"); +/// The request configures the debugger’s response to thrown exceptions. Each of +/// the `filters`, `filterOptions`, and `exceptionOptions` in the request are +/// independent configurations to a debug adapter indicating a kind of exception +/// to catch. An exception thrown in a program should result in a `stopped` +/// event from the debug adapter (with reason `exception`) if any of the +/// configured filters match. +/// +/// Clients should only call this request if the corresponding capability +/// `exceptionBreakpointFilters` returns one or more filters. +Expected<SetExceptionBreakpointsResponseBody> +SetExceptionBreakpointsRequestHandler::Run( + const SetExceptionBreakpointsArguments &arguments) const { // Keep a list of any exception breakpoint filter names that weren't set // so we can clear any exception breakpoints if needed. - std::set<llvm::StringRef> unset_filters; - for (const auto &bp : *dap.exception_breakpoints) + std::set<StringRef> unset_filters; + for (const auto &bp : dap.exception_breakpoints) unset_filters.insert(bp.GetFilter()); - for (const auto &value : *filters) { - const auto filter = GetAsString(value); + SetExceptionBreakpointsResponseBody body; + for (const auto &filter : arguments.filters) { auto *exc_bp = dap.GetExceptionBreakpoint(filter); - if (exc_bp) { - exc_bp->SetBreakpoint(); - unset_filters.erase(std::string(filter)); - } + if (!exc_bp) + continue; + + body.breakpoints.push_back(exc_bp->SetBreakpoint()); + unset_filters.erase(filter); + } + for (const auto &filterOptions : arguments.filterOptions) { + auto *exc_bp = dap.GetExceptionBreakpoint(filterOptions.filterId); + if (!exc_bp) + continue; + + body.breakpoints.push_back(exc_bp->SetBreakpoint(filterOptions.condition)); + unset_filters.erase(filterOptions.filterId); } + + // Clear any unset filters. for (const auto &filter : unset_filters) { auto *exc_bp = dap.GetExceptionBreakpoint(filter); - if (exc_bp) - exc_bp->ClearBreakpoint(); + if (!exc_bp) + continue; + + exc_bp->ClearBreakpoint(); } - dap.SendJSON(llvm::json::Value(std::move(response))); + + return body; } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp index 6cdde63e9796e..9e6acde9ea4b5 100644 --- a/lldb/tools/lldb-dap/JSONUtils.cpp +++ b/lldb/tools/lldb-dap/JSONUtils.cpp @@ -487,7 +487,10 @@ CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp) { protocol::ExceptionBreakpointsFilter filter; filter.filter = bp.GetFilter(); filter.label = bp.GetLabel(); + filter.description = bp.GetLabel(); filter.defaultState = ExceptionBreakpoint::kDefaultValue; + filter.supportsCondition = true; + filter.conditionDescription = ""; return filter; } diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp index 2cb7c47d60203..16a10e5efc5be 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp @@ -438,6 +438,20 @@ json::Value toJSON(const SetDataBreakpointsResponseBody &SDBR) { return json::Object{{"breakpoints", SDBR.breakpoints}}; } +bool fromJSON(const json::Value &Params, SetExceptionBreakpointsArguments &Args, + json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("filters", Args.filters) && + O.mapOptional("filterOptions", Args.filterOptions); +} + +json::Value toJSON(const SetExceptionBreakpointsResponseBody &B) { + json::Object result; + if (!B.breakpoints.empty()) + result.insert({"breakpoints", B.breakpoints}); + return result; +} + json::Value toJSON(const ThreadsResponseBody &TR) { return json::Object{{"threads", TR.threads}}; } diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h index d199cc886b11c..f1b2a29e9c2e2 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h @@ -736,6 +736,56 @@ struct SetDataBreakpointsResponseBody { }; llvm::json::Value toJSON(const SetDataBreakpointsResponseBody &); +/// Arguments for `setExceptionBreakpoints` request. +struct SetExceptionBreakpointsArguments { + /// Set of exception filters specified by their ID. The set of all possible + /// exception filters is defined by the `exceptionBreakpointFilters` + /// capability. The `filter` and `filterOptions` sets are additive. + std::vector<std::string> filters; + + /// Set of exception filters and their options. The set of all possible + /// exception filters is defined by the `exceptionBreakpointFilters` + /// capability. This attribute is only honored by a debug adapter if the + /// corresponding capability `supportsExceptionFilterOptions` is true. The + /// `filter` and `filterOptions` sets are additive. + std::vector<ExceptionFilterOptions> filterOptions; + + // unsupported keys: exceptionOptions +}; +bool fromJSON(const llvm::json::Value &, SetExceptionBreakpointsArguments &, + llvm::json::Path); + +/// Response to `setExceptionBreakpoints` request. +/// +/// The response contains an array of `Breakpoint` objects with information +/// about each exception breakpoint or filter. The `Breakpoint` objects are in +/// the same order as the elements of the `filters`, `filterOptions`, +/// `exceptionOptions` arrays given as arguments. If both `filters` and +/// `filterOptions` are given, the returned array must start with `filters` +/// information first, followed by `filterOptions` information. +/// +/// The `verified` property of a `Breakpoint` object signals whether the +/// exception breakpoint or filter could be successfully created and whether the +/// condition is valid. In case of an error the `message` property explains the +/// problem. The `id` property can be used to introduce a unique ID for the +/// exception breakpoint or filter so that it can be updated subsequently by +/// sending breakpoint events. +/// +/// For backward compatibility both the `breakpoints` array and the enclosing +/// `body` are optional. If these elements are missing a client is not able to +/// show problems for individual exception breakpoints or filters. +struct SetExceptionBreakpointsResponseBody { + /// Information about the exception breakpoints or filters. + /// + /// The breakpoints returned are in the same order as the elements of the + /// `filters`, `filterOptions`, `exceptionOptions` arrays in the arguments. If + /// both `filters` and `filterOptions` are given, the returned array must + /// start with `filters` information first, followed by `filterOptions` + /// information. + std::vector<Breakpoint> breakpoints; +}; +llvm::json::Value toJSON(const SetExceptionBreakpointsResponseBody &); + /// Arguments to `disassemble` request. struct DisassembleArguments { /// Memory reference to the base location containing the instructions to diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp index 085d53bb006ef..dea655cf34443 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp @@ -418,23 +418,41 @@ json::Value toJSON(const Capabilities &C) { for (const auto &feature : C.supportedFeatures) result.insert({ToString(feature), true}); - if (C.exceptionBreakpointFilters && !C.exceptionBreakpointFilters->empty()) + if (!C.exceptionBreakpointFilters.empty()) + result.insert({"exceptionBreakpointFilters", C.exceptionBreakpointFilters}); + if (!C.completionTriggerCharacters.empty()) result.insert( - {"exceptionBreakpointFilters", *C.exceptionBreakpointFilters}); - if (C.completionTriggerCharacters && !C.completionTriggerCharacters->empty()) + {"completionTriggerCharacters", C.completionTriggerCharacters}); + if (!C.additionalModuleColumns.empty()) + result.insert({"additionalModuleColumns", C.additionalModuleColumns}); + if (!C.supportedChecksumAlgorithms.empty()) result.insert( - {"completionTriggerCharacters", *C.completionTriggerCharacters}); - if (C.additionalModuleColumns && !C.additionalModuleColumns->empty()) - result.insert({"additionalModuleColumns", *C.additionalModuleColumns}); - if (C.supportedChecksumAlgorithms && !C.supportedChecksumAlgorithms->empty()) - result.insert( - {"supportedChecksumAlgorithms", *C.supportedChecksumAlgorithms}); - if (C.breakpointModes && !C.breakpointModes->empty()) - result.insert({"breakpointModes", *C.breakpointModes}); + {"supportedChecksumAlgorithms", C.supportedChecksumAlgorithms}); + if (!C.breakpointModes.empty()) + result.insert({"breakpointModes", C.breakpointModes}); // lldb-dap extensions - if (C.lldbExtVersion && !C.lldbExtVersion->empty()) - result.insert({"$__lldb_version", *C.lldbExtVersion}); + if (!C.lldbExtVersion.empty()) + result.insert({"$__lldb_version", C.lldbExtVersion}); + + return result; +} + +bool fromJSON(const json::Value &Params, ExceptionFilterOptions &EFO, + json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("filterId", EFO.filterId) && + O.mapOptional("condition", EFO.condition) && + O.mapOptional("mode", EFO.mode); +} + +json::Value toJSON(const ExceptionFilterOptions &EFO) { + json::Object result{{"filterId", EFO.filterId}}; + + if (!EFO.condition.empty()) + result.insert({"condition", EFO.condition}); + if (!EFO.mode.empty()) + result.insert({"mode", EFO.mode}); return result; } diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h index c7acfc482987b..fc7d4ebcb84b3 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h @@ -253,18 +253,17 @@ struct Capabilities { /// Available exception filter options for the `setExceptionBreakpoints` /// request. - std::optional<std::vector<ExceptionBreakpointsFilter>> - exceptionBreakpointFilters; + std::vector<ExceptionBreakpointsFilter> exceptionBreakpointFilters; /// The set of characters that should trigger completion in a REPL. If not /// specified, the UI should assume the `.` character. - std::optional<std::vector<std::string>> completionTriggerCharacters; + std::vector<std::string> completionTriggerCharacters; /// The set of additional module information exposed by the debug adapter. - std::optional<std::vector<ColumnDescriptor>> additionalModuleColumns; + std::vector<ColumnDescriptor> additionalModuleColumns; /// Checksum algorithms supported by the debug adapter. - std::optional<std::vector<ChecksumAlgorithm>> supportedChecksumAlgorithms; + std::vector<ChecksumAlgorithm> supportedChecksumAlgorithms; /// Modes of breakpoints supported by the debug adapter, such as 'hardware' or /// 'software'. If present, the client may allow the user to select a mode and @@ -272,19 +271,39 @@ struct Capabilities { /// /// Clients may present the first applicable mode in this array as the /// 'default' mode in gestures that set breakpoints. - std::optional<std::vector<BreakpointMode>> breakpointModes; + std::vector<BreakpointMode> breakpointModes; /// lldb-dap Extensions /// @{ /// The version of the adapter. - std::optional<std::string> lldbExtVersion; + std::string lldbExtVersion; /// @} }; bool fromJSON(const llvm::json::Value &, Capabilities &, llvm::json::Path); llvm::json::Value toJSON(const Capabilities &); +/// An `ExceptionFilterOptions` is used to specify an exception filter together +/// with a condition for the `setExceptionBreakpoints` request. +struct ExceptionFilterOptions { + /// ID of an exception filter returned by the `exceptionBreakpointFilters` + /// capability. + std::string filterId; + + /// An expression for conditional exceptions. + /// The exception breaks into the debugger if the result of the condition is + /// true. + std::string condition; + + /// The mode of this exception breakpoint. If defined, this must be one of the + /// `breakpointModes` the debug adapter advertised in its `Capabilities`. + std::string mode; +}; +bool fromJSON(const llvm::json::Value &, ExceptionFilterOptions &, + llvm::json::Path); +llvm::json::Value toJSON(const ExceptionFilterOptions &); + /// A `Source` is a descriptor for source code. It is returned from the debug /// adapter as part of a `StackFrame` and it is used by clients when specifying /// breakpoints. diff --git a/lldb/unittests/DAP/ProtocolTypesTest.cpp b/lldb/unittests/DAP/ProtocolTypesTest.cpp index adf43c9ac2046..52b44e3ac60b5 100644 --- a/lldb/unittests/DAP/ProtocolTypesTest.cpp +++ b/lldb/unittests/DAP/ProtocolTypesTest.cpp @@ -243,14 +243,12 @@ TEST(ProtocolTypesTest, Capabilities) { deserialized_capabilities->supportedFeatures); // Verify exception breakpoint filters. - ASSERT_TRUE( - deserialized_capabilities->exceptionBreakpointFilters.has_value()); - EXPECT_EQ(capabilities.exceptionBreakpointFilters->size(), - deserialized_capabilities->exceptionBreakpointFilters->size()); - for (size_t i = 0; i < capabilities.exceptionBreakpointFilters->size(); ++i) { - const auto &original = capabilities.exceptionBreakpointFilters->at(i); + EXPECT_EQ(capabilities.exceptionBreakpointFilters.size(), + deserialized_capabilities->exceptionBreakpointFilters.size()); + for (size_t i = 0; i < capabilities.exceptionBreakpointFilters.size(); ++i) { + const auto &original = capabilities.exceptionBreakpointFilters.at(i); const auto &deserialized = - deserialized_capabilities->exceptionBreakpointFilters->at(i); + deserialized_capabilities->exceptionBreakpointFilters.at(i); EXPECT_EQ(original.filter, deserialized.filter); EXPECT_EQ(original.label, deserialized.label); EXPECT_EQ(original.description, deserialized.description); @@ -260,19 +258,16 @@ TEST(ProtocolTypesTest, Capabilities) { } // Verify completion trigger characters. - ASSERT_TRUE( - deserialized_capabilities->completionTriggerCharacters.has_value()); EXPECT_EQ(capabilities.completionTriggerCharacters, deserialized_capabilities->completionTriggerCharacters); // Verify additional module columns. - ASSERT_TRUE(deserialized_capabilities->additionalModuleColumns.has_value()); - EXPECT_EQ(capabilities.additionalModuleColumns->size(), - deserialized_capabilities->additionalModuleColumns->size()); - for (size_t i = 0; i < capabilities.additionalModuleColumns->size(); ++i) { - const auto &original = capabilities.additionalModuleColumns->at(i); + EXPECT_EQ(capabilities.additionalModuleColumns.size(), + deserialized_capabilities->additionalModuleColumns.size()); + for (size_t i = 0; i < capabilities.additionalModuleColumns.size(); ++i) { + const auto &original = capabilities.additionalModuleColumns.at(i); const auto &deserialized = - deserialized_capabilities->additionalModuleColumns->at(i); + deserialized_capabilities->additionalModuleColumns.at(i); EXPECT_EQ(original.attributeName, deserialized.attributeName); EXPECT_EQ(original.label, deserialized.label); EXPECT_EQ(original.format, deserialized.format); @@ -281,19 +276,15 @@ TEST(ProtocolTypesTest, Capabilities) { } // Verify supported checksum algorithms. - ASSERT_TRUE( - deserialized_capabilities->supportedChecksumAlgorithms.has_value()); EXPECT_EQ(capabilities.supportedChecksumAlgorithms, deserialized_capabilities->supportedChecksumAlgorithms); // Verify breakpoint modes. - ASSERT_TRUE(deserialized_capabilities->breakpointModes.has_value()); - EXPECT_EQ(capabilities.breakpointModes->size(), - deserialized_capabilities->breakpointModes->size()); - for (size_t i = 0; i < capabilities.breakpointModes->size(); ++i) { - const auto &original = capabilities.breakpointModes->at(i); - const auto &deserialized = - deserialized_capabilities->breakpointModes->at(i); + EXPECT_EQ(capabilities.breakpointModes.size(), + deserialized_capabilities->breakpointModes.size()); + for (size_t i = 0; i < capabilities.breakpointModes.size(); ++i) { + const auto &original = capabilities.breakpointModes.at(i); + const auto &deserialized = deserialized_capabilities->breakpointModes.at(i); EXPECT_EQ(original.mode, deserialized.mode); EXPECT_EQ(original.label, deserialized.label); EXPECT_EQ(original.description, deserialized.description); @@ -301,7 +292,6 @@ TEST(ProtocolTypesTest, Capabilities) { } // Verify lldb extension version. - ASSERT_TRUE(deserialized_capabilities->lldbExtVersion.has_value()); EXPECT_EQ(capabilities.lldbExtVersion, deserialized_capabilities->lldbExtVersion); } @@ -686,3 +676,72 @@ TEST(ProtocolTypesTest, CapabilitiesEventBody) { // Validate toJSON EXPECT_EQ(json, pp(body)); } + +TEST(ProtocolTypesTest, ExceptionFilterOptions) { + EXPECT_THAT_EXPECTED(parse<ExceptionFilterOptions>(R"({"filterId":"id"})"), + HasValue(Value(ExceptionFilterOptions{ + /*filterId=*/"id", /*condition=*/"", /*mode*/ ""}))); + EXPECT_THAT_EXPECTED( + parse<ExceptionFilterOptions>(R"({"filterId":"id","condition":"1+2"})"), + HasValue(Value(ExceptionFilterOptions{ + /*filterId=*/"id", /*condition=*/"1+2", /*mode*/ ""}))); + EXPECT_THAT_EXPECTED( + parse<ExceptionFilterOptions>( + R"({"filterId":"id","condition":"1+2","mode":"m"})"), + HasValue(Value(ExceptionFilterOptions{ + /*filterId=*/"id", /*condition=*/"1+2", /*mode*/ "m"}))); + + // Validate parsing errors + EXPECT_THAT_EXPECTED( + parse<ExceptionFilterOptions>(R"({})", "exceptionFilterOptions"), + FailedWithMessage("missing value at exceptionFilterOptions.filterId")); + EXPECT_THAT_EXPECTED( + parse<ExceptionFilterOptions>(R"({"filterId":"id","condition":42})", + "exceptionFilterOptions"), + FailedWithMessage("expected string at exceptionFilterOptions.condition")); + EXPECT_THAT_EXPECTED( + parse<ExceptionFilterOptions>(R"({"filterId":"id","mode":42})", + "exceptionFilterOptions"), + FailedWithMessage("expected string at exceptionFilterOptions.mode")); +} + +TEST(ProtocolTypesTest, SetExceptionBreakpointsArguments) { + EXPECT_THAT_EXPECTED( + parse<SetExceptionBreakpointsArguments>(R"({"filters":[]})"), + HasValue(testing::FieldsAre(/*filters=*/testing::IsEmpty(), + /*filterOptions=*/testing::IsEmpty()))); + EXPECT_THAT_EXPECTED( + parse<SetExceptionBreakpointsArguments>(R"({"filters":["abc"]})"), + HasValue(testing::FieldsAre(/*filters=*/std::vector<std::string>{"abc"}, + /*filterOptions=*/testing::IsEmpty()))); + EXPECT_THAT_EXPECTED( + parse<SetExceptionBreakpointsArguments>( + R"({"filters":[],"filterOptions":[{"filterId":"abc"}]})"), + HasValue(testing::FieldsAre( + /*filters=*/testing::IsEmpty(), + /*filterOptions=*/testing::Contains(testing::FieldsAre( + /*filterId=*/"abc", /*condition=*/"", /*mode=*/""))))); + + // Validate parse errors + EXPECT_THAT_EXPECTED(parse<SetExceptionBreakpointsArguments>(R"({})"), + FailedWithMessage("missing value at (root).filters")); + EXPECT_THAT_EXPECTED( + parse<SetExceptionBreakpointsArguments>(R"({"filters":false})"), + FailedWithMessage("expected array at (root).filters")); +} + +TEST(ProtocolTypesTest, SetExceptionBreakpointsResponseBody) { + SetExceptionBreakpointsResponseBody body; + Breakpoint bp; + bp.id = 12, bp.verified = true; + body.breakpoints = {bp}; + EXPECT_EQ(R"({ + "breakpoints": [ + { + "id": 12, + "verified": true + } + ] +})", + pp(body)); +} >From 0a788bf721f0cb5926d3a0bfa65d8868f53093d4 Mon Sep 17 00:00:00 2001 From: John Harrison <harj...@google.com> Date: Fri, 13 Jun 2025 13:56:36 -0700 Subject: [PATCH 2/2] Creating an enum for exception breakpoint kinds (catch, throw) instead of a bool. --- lldb/tools/lldb-dap/DAP.cpp | 17 +++++++++-------- lldb/tools/lldb-dap/ExceptionBreakpoint.cpp | 5 +++-- lldb/tools/lldb-dap/ExceptionBreakpoint.h | 12 ++++++++---- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 58e0be64ba5b8..ebbdd7aba779d 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -9,6 +9,7 @@ #include "DAP.h" #include "DAPLog.h" #include "EventHelper.h" +#include "ExceptionBreakpoint.h" #include "Handler/RequestHandler.h" #include "Handler/ResponseHandler.h" #include "JSONUtils.h" @@ -131,28 +132,28 @@ void DAP::PopulateExceptionBreakpoints() { if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeC_plus_plus)) { exception_breakpoints.emplace_back(*this, "cpp_catch", "C++ Catch", lldb::eLanguageTypeC_plus_plus, - /*is_throw=*/false, /*is_catch=*/true); + eExceptionKindCatch); exception_breakpoints.emplace_back(*this, "cpp_throw", "C++ Throw", lldb::eLanguageTypeC_plus_plus, - /*is_throw=*/true, /*is_catch=*/false); + eExceptionKindThrow); } if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeObjC)) { exception_breakpoints.emplace_back(*this, "objc_catch", "Objective-C Catch", lldb::eLanguageTypeObjC, - /*is_throw=*/false, /*is_catch=*/true); + eExceptionKindCatch); exception_breakpoints.emplace_back(*this, "objc_throw", "Objective-C Throw", lldb::eLanguageTypeObjC, - /*is_throw=*/true, /*is_catch=*/false); + eExceptionKindThrow); } if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeSwift)) { exception_breakpoints.emplace_back(*this, "swift_catch", "Swift Catch", lldb::eLanguageTypeSwift, - /*is_throw=*/false, /*is_catch=*/true); + eExceptionKindCatch); exception_breakpoints.emplace_back(*this, "swift_throw", "Swift Throw", lldb::eLanguageTypeSwift, - /*is_throw=*/true, /*is_catch=*/false); + eExceptionKindThrow); } // Besides handling the hardcoded list of languages from above, we try to find @@ -184,7 +185,7 @@ void DAP::PopulateExceptionBreakpoints() { exception_breakpoints.emplace_back( *this, raw_lang_name + "_" + throw_keyword, capitalized_lang_name + " " + capitalize(throw_keyword), lang, - /*is_throw=*/true, /*is_catch=*/false); + eExceptionKindThrow); } if (lldb::SBLanguageRuntime::SupportsExceptionBreakpointsOnCatch(lang)) { @@ -196,7 +197,7 @@ void DAP::PopulateExceptionBreakpoints() { exception_breakpoints.emplace_back( *this, raw_lang_name + "_" + catch_keyword, capitalized_lang_name + " " + capitalize(catch_keyword), lang, - /*is_throw=*/true, /*is_catch=*/false); + eExceptionKindCatch); } } } diff --git a/lldb/tools/lldb-dap/ExceptionBreakpoint.cpp b/lldb/tools/lldb-dap/ExceptionBreakpoint.cpp index 2531291fd62cc..5bf06268a5af2 100644 --- a/lldb/tools/lldb-dap/ExceptionBreakpoint.cpp +++ b/lldb/tools/lldb-dap/ExceptionBreakpoint.cpp @@ -24,8 +24,9 @@ protocol::Breakpoint ExceptionBreakpoint::SetBreakpoint(StringRef condition) { std::lock_guard<lldb::SBMutex> guard(lock); if (!m_bp.IsValid()) { - m_bp = m_dap.target.BreakpointCreateForException(m_language, m_is_catch, - m_is_throw); + m_bp = m_dap.target.BreakpointCreateForException( + m_language, m_kind == eExceptionKindCatch, + m_kind == eExceptionKindThrow); m_bp.AddName(BreakpointBase::kDAPBreakpointLabel); } diff --git a/lldb/tools/lldb-dap/ExceptionBreakpoint.h b/lldb/tools/lldb-dap/ExceptionBreakpoint.h index d453d5fcc4fd3..802ec71ce6ad3 100644 --- a/lldb/tools/lldb-dap/ExceptionBreakpoint.h +++ b/lldb/tools/lldb-dap/ExceptionBreakpoint.h @@ -19,12 +19,17 @@ namespace lldb_dap { +enum ExceptionKind : unsigned { + eExceptionKindCatch, + eExceptionKindThrow, +}; + class ExceptionBreakpoint { public: ExceptionBreakpoint(DAP &d, std::string f, std::string l, - lldb::LanguageType lang, bool is_throw, bool is_catch) + lldb::LanguageType lang, ExceptionKind kind) : m_dap(d), m_filter(std::move(f)), m_label(std::move(l)), - m_language(lang), m_is_throw(is_throw), m_is_catch(is_catch), m_bp() {} + m_language(lang), m_kind(kind), m_bp() {} protocol::Breakpoint SetBreakpoint() { return SetBreakpoint(""); }; protocol::Breakpoint SetBreakpoint(llvm::StringRef condition); @@ -41,8 +46,7 @@ class ExceptionBreakpoint { std::string m_filter; std::string m_label; lldb::LanguageType m_language; - bool m_is_throw; - bool m_is_catch; + ExceptionKind m_kind; lldb::SBBreakpoint m_bp; }; _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits