[Lldb-commits] [lldb] [lldb-dap] Return raw stop info from exceptionInfo (PR #231760)
Jonas Devlieghere via lldb-commits
lldb-commits at lists.llvm.org
Tue Oct 13 22:55:40 PDT 2026
https://github.com/JDevlieghere created https://github.com/llvm/llvm-project/pull/231760
lldb-dap folds the thread's extended stop info and the process's extended crash information into the exceptionInfo description as formatted text. Clients that want interpret these themselves currently have to parse them back out.
Return both as raw JSON in two new lldb-dap extension fields, `$__lldb_stopReasonExtendedInfo` and `$__lldb_extendedCrashInformation`.
rdar://188761988
>From 0bce25dbfcd1e1f13d4d3f3b775df5de0c41ffc4 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Tue, 13 Oct 2026 22:54:08 -0700
Subject: [PATCH] [lldb-dap] Return raw stop info from exceptionInfo
lldb-dap folds the thread's extended stop info and the process's
extended crash information into the exceptionInfo description as
formatted text. Clients that want interpret these themselves currently
have to parse them back out.
Return both as raw JSON in two new lldb-dap extension fields,
$__lldb_stopReasonExtendedInfo and $__lldb_extendedCrashInformation.
rdar://188761988
---
.../lldbsuite/test/tools/lldb_dap/types.py | 6 ++++
.../lldb-dap/exception/asan/TestDAP_asan.py | 6 ++++
.../lldb-dap/exception/ubsan/TestDAP_ubsan.py | 7 ++++
.../Handler/ExceptionInfoRequestHandler.cpp | 34 +++++++++++++++++++
.../lldb-dap/Protocol/ProtocolRequests.cpp | 6 ++++
.../lldb-dap/Protocol/ProtocolRequests.h | 13 +++++++
lldb/unittests/DAP/ProtocolRequestsTest.cpp | 9 +++++
7 files changed, 81 insertions(+)
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb_dap/types.py b/lldb/packages/Python/lldbsuite/test/tools/lldb_dap/types.py
index 24b33ae938161..310a9f01131d5 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb_dap/types.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb_dap/types.py
@@ -1731,6 +1731,12 @@ class Body:
breakMode: ExceptionBreakMode
description: Optional[str] = None
details: Optional[ExceptionDetails] = None
+ lldb_stop_reason_extended_info: Optional[Dict[str, Any]] = field(
+ metadata={"alias": "$__lldb_stopReasonExtendedInfo"}, default=None
+ )
+ lldb_extended_crash_information: Optional[Dict[str, Any]] = field(
+ metadata={"alias": "$__lldb_extendedCrashInformation"}, default=None
+ )
body: Body
diff --git a/lldb/test/API/tools/lldb-dap/exception/asan/TestDAP_asan.py b/lldb/test/API/tools/lldb-dap/exception/asan/TestDAP_asan.py
index 395cd68db25fe..24f0907068516 100644
--- a/lldb/test/API/tools/lldb-dap/exception/asan/TestDAP_asan.py
+++ b/lldb/test/API/tools/lldb-dap/exception/asan/TestDAP_asan.py
@@ -28,3 +28,9 @@ def test_asan(self):
description = self.expect_not_none(exception_info.description)
self.assertRegex(description, r"fatal_error: heap-use-after-free")
self.assertEqual(exception_info.exceptionId, "runtime-instrumentation")
+
+ stop_info = self.expect_not_none(
+ exception_info.lldb_stop_reason_extended_info
+ )
+ self.assertEqual(stop_info["instrumentation_class"], "AddressSanitizer")
+ self.assertEqual(stop_info["description"], "heap-use-after-free")
diff --git a/lldb/test/API/tools/lldb-dap/exception/ubsan/TestDAP_ubsan.py b/lldb/test/API/tools/lldb-dap/exception/ubsan/TestDAP_ubsan.py
index 23ba0747311e9..4e5cfe836e52a 100644
--- a/lldb/test/API/tools/lldb-dap/exception/ubsan/TestDAP_ubsan.py
+++ b/lldb/test/API/tools/lldb-dap/exception/ubsan/TestDAP_ubsan.py
@@ -30,6 +30,13 @@ def test_ubsan(self):
self.assertRegex(description, r"Out of bounds index")
self.assertEqual(exception_info.exceptionId, "runtime-instrumentation")
+ stop_info = self.expect_not_none(
+ exception_info.lldb_stop_reason_extended_info
+ )
+ self.assertEqual(
+ stop_info["instrumentation_class"], "UndefinedBehaviorSanitizer"
+ )
+
# FIXME: Check on non macOS platform the stop information location heuristic
# may be wrong. enable when we have updated Ubsan stopInfo heuristic.
if self.platformIsDarwin():
diff --git a/lldb/tools/lldb-dap/Handler/ExceptionInfoRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ExceptionInfoRequestHandler.cpp
index f15d35f3954da..bea427519126a 100644
--- a/lldb/tools/lldb-dap/Handler/ExceptionInfoRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/ExceptionInfoRequestHandler.cpp
@@ -271,6 +271,34 @@ static std::string FormatCrashReport(lldb::SBThread &thread) {
return report;
}
+static std::optional<json::Value> ParseJSON(DAP &dap, lldb::SBStream &stream) {
+ Expected<json::Value> value =
+ json::parse({stream.GetData(), stream.GetSize()});
+ if (!value) {
+ DAP_LOG_ERROR(dap.log, value.takeError(), "invalid JSON: {0}");
+ return std::nullopt;
+ }
+ return std::move(*value);
+}
+
+static std::optional<json::Value> GetExtendedStopInfo(DAP &dap,
+ lldb::SBThread &thread) {
+ lldb::SBStream stream;
+ if (!thread.GetStopReasonExtendedInfoAsJSON(stream))
+ return std::nullopt;
+ return ParseJSON(dap, stream);
+}
+
+static std::optional<json::Value>
+GetExtendedCrashInformation(DAP &dap, lldb::SBThread &thread) {
+ lldb::SBStructuredData crash_info =
+ thread.GetProcess().GetExtendedCrashInformation();
+ lldb::SBStream stream;
+ if (!crash_info || crash_info.GetAsJSON(stream).Fail())
+ return std::nullopt;
+ return ParseJSON(dap, stream);
+}
+
static std::string FormatStackTrace(lldb::SBThread &thread) {
std::string stack_trace;
raw_string_ostream OS(stack_trace);
@@ -348,6 +376,12 @@ ExceptionInfoRequestHandler::Run(const ExceptionInfoArguments &args) const {
!crash_report.empty())
body.description += formatv("\n\n{0}", crash_report);
+ // Clients that interpret these themselves shouldn't have to parse them back
+ // out of the description.
+ body.lldbExtStopReasonExtendedInfo = GetExtendedStopInfo(dap, thread);
+ body.lldbExtExtendedCrashInformation =
+ GetExtendedCrashInformation(dap, thread);
+
lldb::SBProcess process = thread.GetProcess();
for (uint32_t idx = 0; idx < lldb::eNumInstrumentationRuntimeTypes; idx++) {
auto type = static_cast<lldb::InstrumentationRuntimeType>(idx);
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
index c952c17512952..6a7f4e57b243a 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
@@ -695,6 +695,12 @@ json::Value toJSON(const ExceptionInfoResponseBody &ERB) {
result.insert({"description", ERB.description});
if (ERB.details.has_value())
result.insert({"details", *ERB.details});
+ if (ERB.lldbExtStopReasonExtendedInfo)
+ result.insert(
+ {"$__lldb_stopReasonExtendedInfo", *ERB.lldbExtStopReasonExtendedInfo});
+ if (ERB.lldbExtExtendedCrashInformation)
+ result.insert({"$__lldb_extendedCrashInformation",
+ *ERB.lldbExtExtendedCrashInformation});
return result;
}
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
index d8cc9d4cad223..169052183b110 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
@@ -1071,6 +1071,19 @@ struct ExceptionInfoResponseBody {
/// Detailed information about the exception.
std::optional<ExceptionDetails> details;
+
+ /// lldb-dap Extensions
+ /// @{
+
+ /// The thread's stop reason extended info, as reported by
+ /// `SBThread::GetStopReasonExtendedInfoAsJSON`.
+ std::optional<llvm::json::Value> lldbExtStopReasonExtendedInfo;
+
+ /// The process's extended crash information, as reported by
+ /// `SBProcess::GetExtendedCrashInformation`.
+ std::optional<llvm::json::Value> lldbExtExtendedCrashInformation;
+
+ /// @}
};
llvm::json::Value toJSON(const ExceptionInfoResponseBody &);
diff --git a/lldb/unittests/DAP/ProtocolRequestsTest.cpp b/lldb/unittests/DAP/ProtocolRequestsTest.cpp
index af0d69f3a9ac4..f0a71d25f21dd 100644
--- a/lldb/unittests/DAP/ProtocolRequestsTest.cpp
+++ b/lldb/unittests/DAP/ProtocolRequestsTest.cpp
@@ -55,6 +55,9 @@ TEST(ProtocolRequestsTest, ExceptionInfoResponseBody) {
body.breakMode = eExceptionBreakModeNever;
body.details = ExceptionDetails{};
body.details->message = "some message";
+ body.lldbExtStopReasonExtendedInfo =
+ json::Object{{"instrumentation_class", "AddressSanitizer"}};
+ body.lldbExtExtendedCrashInformation = json::Object{{"crash-info", "abort"}};
Expected<json::Value> expected_opt = parse(
R"({
@@ -63,6 +66,12 @@ TEST(ProtocolRequestsTest, ExceptionInfoResponseBody) {
"breakMode": "never",
"details": {
"message": "some message"
+ },
+ "$__lldb_stopReasonExtendedInfo": {
+ "instrumentation_class": "AddressSanitizer"
+ },
+ "$__lldb_extendedCrashInformation": {
+ "crash-info": "abort"
}
})");
More information about the lldb-commits
mailing list