Skip to content

Commit e54b230

Browse files
committed
[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.
1 parent 1ecd108 commit e54b230

File tree

18 files changed

+414
-227
lines changed

18 files changed

+414
-227
lines changed

lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1050,8 +1050,12 @@ def request_setBreakpoints(self, source: Source, line_array, data=None):
10501050
self._update_verified_breakpoints(response["body"]["breakpoints"])
10511051
return response
10521052

1053-
def request_setExceptionBreakpoints(self, filters):
1053+
def request_setExceptionBreakpoints(
1054+
self, *, filters: list[str] = [], filter_options: list[dict] = []
1055+
):
10541056
args_dict = {"filters": filters}
1057+
if filter_options:
1058+
args_dict["filterOptions"] = filter_options
10551059
command_dict = {
10561060
"command": "setExceptionBreakpoints",
10571061
"type": "request",

lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setExceptionBreakpoints.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
"""
2-
Test lldb-dap setBreakpoints request
2+
Test lldb-dap setExceptionBreakpoints request
33
"""
44

5-
6-
import dap_server
75
from lldbsuite.test.decorators import *
86
from lldbsuite.test.lldbtest import *
9-
from lldbsuite.test import lldbutil
107
import lldbdap_testcase
118

129

13-
@skip("Temporarily disable the breakpoint tests")
1410
class TestDAP_setExceptionBreakpoints(lldbdap_testcase.DAPTestCaseBase):
1511
@skipIfWindows
1612
def test_functionality(self):
@@ -33,8 +29,9 @@ def test_functionality(self):
3329
program = self.getBuildArtifact("a.out")
3430
self.build_and_launch(program)
3531

36-
filters = ["cpp_throw", "cpp_catch"]
37-
response = self.dap_server.request_setExceptionBreakpoints(filters)
32+
response = self.dap_server.request_setExceptionBreakpoints(
33+
filters=["cpp_throw", "cpp_catch"],
34+
)
3835
if response:
3936
self.assertTrue(response["success"])
4037

lldb/test/API/tools/lldb-dap/exception/objc/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
OBJC_SOURCES := main.m
22

3-
CFLAGS_EXTRAS := -w
3+
CFLAGS_EXTRAS := -w -fobjc-exceptions
44

55
USE_SYSTEM_STDLIB := 1
66

lldb/test/API/tools/lldb-dap/exception/objc/TestDAP_exception_objc.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
Test exception behavior in DAP with obj-c throw.
33
"""
44

5-
65
from lldbsuite.test.decorators import *
76
from lldbsuite.test.lldbtest import *
87
import lldbdap_testcase
@@ -25,3 +24,41 @@ def test_stopped_description(self):
2524
exception_details = exception_info["details"]
2625
self.assertRegex(exception_details["message"], "SomeReason")
2726
self.assertRegex(exception_details["stackTrace"], "main.m")
27+
28+
@skipUnlessDarwin
29+
def test_break_on_throw_and_catch(self):
30+
"""
31+
Test that breakpoints on exceptions work as expected.
32+
"""
33+
program = self.getBuildArtifact("a.out")
34+
self.build_and_launch(program)
35+
36+
response = self.dap_server.request_setExceptionBreakpoints(
37+
filter_options=[
38+
{
39+
"filterId": "objc_throw",
40+
"condition": '[[((NSException *)$arg1) name] isEqual:@"ThrownException"]',
41+
},
42+
]
43+
)
44+
if response:
45+
self.assertTrue(response["success"])
46+
47+
self.continue_to_exception_breakpoint("Objective-C Throw")
48+
49+
# FIXME: Catching objc exceptions do not appear to be working.
50+
# Xcode appears to set a breakpoint on '__cxa_begin_catch' for objc
51+
# catch, which is different than
52+
# SBTarget::BreakpointCreateForException(eLanguageObjectiveC, /*catch_bp=*/true, /*throw_bp=*/false);
53+
# self.continue_to_exception_breakpoint("Objective-C Catch")
54+
55+
self.do_continue()
56+
57+
self.assertTrue(self.verify_stop_exception_info("signal SIGABRT"))
58+
exception_info = self.get_exceptionInfo()
59+
self.assertEqual(exception_info["breakMode"], "always")
60+
self.assertEqual(exception_info["description"], "signal SIGABRT")
61+
self.assertEqual(exception_info["exceptionId"], "signal")
62+
exception_details = exception_info["details"]
63+
self.assertRegex(exception_details["message"], "SomeReason")
64+
self.assertRegex(exception_details["stackTrace"], "main.m")
Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
#import <Foundation/Foundation.h>
22

33
int main(int argc, char const *argv[]) {
4-
@throw [[NSException alloc] initWithName:@"ThrownException"
5-
reason:@"SomeReason"
6-
userInfo:nil];
4+
@try {
5+
NSException *e = [[NSException alloc] initWithName:@"ThrownException"
6+
reason:@"SomeReason"
7+
userInfo:nil];
8+
@throw e;
9+
} @catch (NSException *e) {
10+
NSLog(@"Caught %@", e);
11+
@throw; // let the process crash...
12+
}
713
return 0;
814
}

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 71 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -128,104 +128,89 @@ DAP::DAP(Log *log, const ReplMode default_repl_mode,
128128
DAP::~DAP() = default;
129129

130130
void DAP::PopulateExceptionBreakpoints() {
131-
llvm::call_once(init_exception_breakpoints_flag, [this]() {
132-
exception_breakpoints = std::vector<ExceptionBreakpoint>{};
133-
134-
if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeC_plus_plus)) {
135-
exception_breakpoints->emplace_back(*this, "cpp_catch", "C++ Catch",
136-
lldb::eLanguageTypeC_plus_plus);
137-
exception_breakpoints->emplace_back(*this, "cpp_throw", "C++ Throw",
138-
lldb::eLanguageTypeC_plus_plus);
139-
}
140-
if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeObjC)) {
141-
exception_breakpoints->emplace_back(
142-
*this, "objc_catch", "Objective-C Catch", lldb::eLanguageTypeObjC);
143-
exception_breakpoints->emplace_back(
144-
*this, "objc_throw", "Objective-C Throw", lldb::eLanguageTypeObjC);
145-
}
146-
if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeSwift)) {
147-
exception_breakpoints->emplace_back(*this, "swift_catch", "Swift Catch",
148-
lldb::eLanguageTypeSwift);
149-
exception_breakpoints->emplace_back(*this, "swift_throw", "Swift Throw",
150-
lldb::eLanguageTypeSwift);
131+
if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeC_plus_plus)) {
132+
exception_breakpoints.emplace_back(*this, "cpp_catch", "C++ Catch",
133+
lldb::eLanguageTypeC_plus_plus,
134+
/*is_throw=*/false, /*is_catch=*/true);
135+
exception_breakpoints.emplace_back(*this, "cpp_throw", "C++ Throw",
136+
lldb::eLanguageTypeC_plus_plus,
137+
/*is_throw=*/true, /*is_catch=*/false);
138+
}
139+
140+
if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeObjC)) {
141+
exception_breakpoints.emplace_back(*this, "objc_catch", "Objective-C Catch",
142+
lldb::eLanguageTypeObjC,
143+
/*is_throw=*/false, /*is_catch=*/true);
144+
exception_breakpoints.emplace_back(*this, "objc_throw", "Objective-C Throw",
145+
lldb::eLanguageTypeObjC,
146+
/*is_throw=*/true, /*is_catch=*/false);
147+
}
148+
149+
if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeSwift)) {
150+
exception_breakpoints.emplace_back(*this, "swift_catch", "Swift Catch",
151+
lldb::eLanguageTypeSwift,
152+
/*is_throw=*/false, /*is_catch=*/true);
153+
exception_breakpoints.emplace_back(*this, "swift_throw", "Swift Throw",
154+
lldb::eLanguageTypeSwift,
155+
/*is_throw=*/true, /*is_catch=*/false);
156+
}
157+
158+
// Besides handling the hardcoded list of languages from above, we try to find
159+
// any other languages that support exception breakpoints using the SB API.
160+
for (int raw_lang = lldb::eLanguageTypeUnknown;
161+
raw_lang < lldb::eNumLanguageTypes; ++raw_lang) {
162+
lldb::LanguageType lang = static_cast<lldb::LanguageType>(raw_lang);
163+
164+
// We first discard any languages already handled above.
165+
if (lldb::SBLanguageRuntime::LanguageIsCFamily(lang) ||
166+
lang == lldb::eLanguageTypeSwift)
167+
continue;
168+
169+
if (!lldb::SBDebugger::SupportsLanguage(lang))
170+
continue;
171+
172+
const char *name = lldb::SBLanguageRuntime::GetNameForLanguageType(lang);
173+
if (!name)
174+
continue;
175+
std::string raw_lang_name = name;
176+
std::string capitalized_lang_name = capitalize(name);
177+
178+
if (lldb::SBLanguageRuntime::SupportsExceptionBreakpointsOnThrow(lang)) {
179+
const char *raw_throw_keyword =
180+
lldb::SBLanguageRuntime::GetThrowKeywordForLanguage(lang);
181+
std::string throw_keyword =
182+
raw_throw_keyword ? raw_throw_keyword : "throw";
183+
184+
exception_breakpoints.emplace_back(
185+
*this, raw_lang_name + "_" + throw_keyword,
186+
capitalized_lang_name + " " + capitalize(throw_keyword), lang,
187+
/*is_throw=*/true, /*is_catch=*/false);
151188
}
152-
// Besides handling the hardcoded list of languages from above, we try to
153-
// find any other languages that support exception breakpoints using the
154-
// SB API.
155-
for (int raw_lang = lldb::eLanguageTypeUnknown;
156-
raw_lang < lldb::eNumLanguageTypes; ++raw_lang) {
157-
lldb::LanguageType lang = static_cast<lldb::LanguageType>(raw_lang);
158-
159-
// We first discard any languages already handled above.
160-
if (lldb::SBLanguageRuntime::LanguageIsCFamily(lang) ||
161-
lang == lldb::eLanguageTypeSwift)
162-
continue;
163-
164-
if (!lldb::SBDebugger::SupportsLanguage(lang))
165-
continue;
166-
167-
const char *name = lldb::SBLanguageRuntime::GetNameForLanguageType(lang);
168-
if (!name)
169-
continue;
170-
std::string raw_lang_name = name;
171-
std::string capitalized_lang_name = capitalize(name);
172-
173-
if (lldb::SBLanguageRuntime::SupportsExceptionBreakpointsOnThrow(lang)) {
174-
const char *raw_throw_keyword =
175-
lldb::SBLanguageRuntime::GetThrowKeywordForLanguage(lang);
176-
std::string throw_keyword =
177-
raw_throw_keyword ? raw_throw_keyword : "throw";
178-
179-
exception_breakpoints->emplace_back(
180-
*this, raw_lang_name + "_" + throw_keyword,
181-
capitalized_lang_name + " " + capitalize(throw_keyword), lang);
182-
}
183189

184-
if (lldb::SBLanguageRuntime::SupportsExceptionBreakpointsOnCatch(lang)) {
185-
const char *raw_catch_keyword =
186-
lldb::SBLanguageRuntime::GetCatchKeywordForLanguage(lang);
187-
std::string catch_keyword =
188-
raw_catch_keyword ? raw_catch_keyword : "catch";
190+
if (lldb::SBLanguageRuntime::SupportsExceptionBreakpointsOnCatch(lang)) {
191+
const char *raw_catch_keyword =
192+
lldb::SBLanguageRuntime::GetCatchKeywordForLanguage(lang);
193+
std::string catch_keyword =
194+
raw_catch_keyword ? raw_catch_keyword : "catch";
189195

190-
exception_breakpoints->emplace_back(
191-
*this, raw_lang_name + "_" + catch_keyword,
192-
capitalized_lang_name + " " + capitalize(catch_keyword), lang);
193-
}
196+
exception_breakpoints.emplace_back(
197+
*this, raw_lang_name + "_" + catch_keyword,
198+
capitalized_lang_name + " " + capitalize(catch_keyword), lang,
199+
/*is_throw=*/true, /*is_catch=*/false);
194200
}
195-
assert(!exception_breakpoints->empty() && "should not be empty");
196-
});
201+
}
197202
}
198203

199204
ExceptionBreakpoint *DAP::GetExceptionBreakpoint(llvm::StringRef filter) {
200-
// PopulateExceptionBreakpoints() is called after g_dap.debugger is created
201-
// in a request-initialize.
202-
//
203-
// But this GetExceptionBreakpoint() method may be called before attaching, in
204-
// which case, we may not have populated the filter yet.
205-
//
206-
// We also cannot call PopulateExceptionBreakpoints() in DAP::DAP() because
207-
// we need SBDebugger::Initialize() to have been called before this.
208-
//
209-
// So just calling PopulateExceptionBreakoints(),which does lazy-populating
210-
// seems easiest. Two other options include:
211-
// + call g_dap.PopulateExceptionBreakpoints() in lldb-dap.cpp::main()
212-
// right after the call to SBDebugger::Initialize()
213-
// + Just call PopulateExceptionBreakpoints() to get a fresh list everytime
214-
// we query (a bit overkill since it's not likely to change?)
215-
PopulateExceptionBreakpoints();
216-
217-
for (auto &bp : *exception_breakpoints) {
205+
for (auto &bp : exception_breakpoints) {
218206
if (bp.GetFilter() == filter)
219207
return &bp;
220208
}
221209
return nullptr;
222210
}
223211

224212
ExceptionBreakpoint *DAP::GetExceptionBreakpoint(const lldb::break_id_t bp_id) {
225-
// See comment in the other GetExceptionBreakpoint().
226-
PopulateExceptionBreakpoints();
227-
228-
for (auto &bp : *exception_breakpoints) {
213+
for (auto &bp : exception_breakpoints) {
229214
if (bp.GetID() == bp_id)
230215
return &bp;
231216
}
@@ -1117,8 +1102,9 @@ protocol::Capabilities DAP::GetCapabilities() {
11171102
}
11181103

11191104
// Available filters or options for the setExceptionBreakpoints request.
1105+
PopulateExceptionBreakpoints();
11201106
std::vector<protocol::ExceptionBreakpointsFilter> filters;
1121-
for (const auto &exc_bp : *exception_breakpoints)
1107+
for (const auto &exc_bp : exception_breakpoints)
11221108
filters.emplace_back(CreateExceptionBreakpointFilter(exc_bp));
11231109
capabilities.exceptionBreakpointFilters = std::move(filters);
11241110

lldb/tools/lldb-dap/DAP.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ struct DAP {
9999
lldb::SBBroadcaster broadcaster;
100100
FunctionBreakpointMap function_breakpoints;
101101
InstructionBreakpointMap instruction_breakpoints;
102-
std::optional<std::vector<ExceptionBreakpoint>> exception_breakpoints;
102+
std::vector<ExceptionBreakpoint> exception_breakpoints;
103103
llvm::once_flag init_exception_breakpoints_flag;
104104

105105
/// Map step in target id to list of function targets that user can choose.
@@ -320,7 +320,7 @@ struct DAP {
320320
});
321321
}
322322

323-
/// The set of capablities supported by this adapter.
323+
/// The set of capabilities supported by this adapter.
324324
protocol::Capabilities GetCapabilities();
325325

326326
/// Debuggee will continue from stopped state.

lldb/tools/lldb-dap/ExceptionBreakpoint.cpp

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,32 @@
99
#include "ExceptionBreakpoint.h"
1010
#include "BreakpointBase.h"
1111
#include "DAP.h"
12+
#include "Protocol/ProtocolTypes.h"
1213
#include "lldb/API/SBMutex.h"
1314
#include "lldb/API/SBTarget.h"
1415
#include <mutex>
1516

17+
using namespace llvm;
18+
using namespace lldb_dap::protocol;
19+
1620
namespace lldb_dap {
1721

18-
void ExceptionBreakpoint::SetBreakpoint() {
22+
protocol::Breakpoint ExceptionBreakpoint::SetBreakpoint(StringRef condition) {
1923
lldb::SBMutex lock = m_dap.GetAPIMutex();
2024
std::lock_guard<lldb::SBMutex> guard(lock);
2125

22-
if (m_bp.IsValid())
23-
return;
24-
bool catch_value = m_filter.find("_catch") != std::string::npos;
25-
bool throw_value = m_filter.find("_throw") != std::string::npos;
26-
m_bp = m_dap.target.BreakpointCreateForException(m_language, catch_value,
27-
throw_value);
28-
m_bp.AddName(BreakpointBase::kDAPBreakpointLabel);
26+
if (!m_bp.IsValid()) {
27+
m_bp = m_dap.target.BreakpointCreateForException(m_language, m_is_catch,
28+
m_is_throw);
29+
m_bp.AddName(BreakpointBase::kDAPBreakpointLabel);
30+
}
31+
32+
m_bp.SetCondition(condition.data());
33+
34+
protocol::Breakpoint breakpoint;
35+
breakpoint.id = m_bp.GetID();
36+
breakpoint.verified = m_bp.IsValid();
37+
return breakpoint;
2938
}
3039

3140
void ExceptionBreakpoint::ClearBreakpoint() {

lldb/tools/lldb-dap/ExceptionBreakpoint.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#define LLDB_TOOLS_LLDB_DAP_EXCEPTIONBREAKPOINT_H
1111

1212
#include "DAPForward.h"
13+
#include "Protocol/ProtocolTypes.h"
1314
#include "lldb/API/SBBreakpoint.h"
1415
#include "lldb/lldb-enumerations.h"
1516
#include "llvm/ADT/StringRef.h"
@@ -21,11 +22,12 @@ namespace lldb_dap {
2122
class ExceptionBreakpoint {
2223
public:
2324
ExceptionBreakpoint(DAP &d, std::string f, std::string l,
24-
lldb::LanguageType lang)
25+
lldb::LanguageType lang, bool is_throw, bool is_catch)
2526
: m_dap(d), m_filter(std::move(f)), m_label(std::move(l)),
26-
m_language(lang), m_bp() {}
27+
m_language(lang), m_is_throw(is_throw), m_is_catch(is_catch), m_bp() {}
2728

28-
void SetBreakpoint();
29+
protocol::Breakpoint SetBreakpoint() { return SetBreakpoint(""); };
30+
protocol::Breakpoint SetBreakpoint(llvm::StringRef condition);
2931
void ClearBreakpoint();
3032

3133
lldb::break_id_t GetID() const { return m_bp.GetID(); }
@@ -39,6 +41,8 @@ class ExceptionBreakpoint {
3941
std::string m_filter;
4042
std::string m_label;
4143
lldb::LanguageType m_language;
44+
bool m_is_throw;
45+
bool m_is_catch;
4246
lldb::SBBreakpoint m_bp;
4347
};
4448

0 commit comments

Comments
 (0)