[Lldb-commits] [lldb] [lldb-dap] Creating protocol types for setExceptionBreakpoints. (PR #144153)
John Harrison via lldb-commits
lldb-commits at lists.llvm.org
Fri Jun 13 13:57:09 PDT 2025
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 <harjohn at 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
- at 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 <harjohn at 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;
};
More information about the lldb-commits
mailing list