https://github.com/DrSergei updated 
https://github.com/llvm/llvm-project/pull/173226

>From 45ddcb394c5648e4d0b1f3a2f203dec90531dba0 Mon Sep 17 00:00:00 2001
From: Druzhkov Sergei <[email protected]>
Date: Sat, 20 Dec 2025 22:28:19 +0300
Subject: [PATCH 1/2] [lldb-dap] Migrate stackTrace request to structured types

---
 lldb/tools/lldb-dap/Handler/RequestHandler.h  |  19 +-
 .../Handler/StackTraceRequestHandler.cpp      | 255 +++++++++---------
 lldb/tools/lldb-dap/JSONUtils.cpp             | 158 -----------
 lldb/tools/lldb-dap/JSONUtils.h               |  50 ----
 .../lldb-dap/Protocol/ProtocolRequests.cpp    |  18 ++
 .../lldb-dap/Protocol/ProtocolRequests.h      |  38 +++
 .../tools/lldb-dap/Protocol/ProtocolTypes.cpp |  54 ++++
 lldb/tools/lldb-dap/Protocol/ProtocolTypes.h  |  86 ++++++
 lldb/unittests/DAP/ProtocolRequestsTest.cpp   |  68 +++++
 9 files changed, 394 insertions(+), 352 deletions(-)

diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h 
b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index a18a8049c804d..a01fb23005b41 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -79,16 +79,6 @@ class BaseRequestHandler {
   DAP &dap;
 };
 
-/// FIXME: Migrate callers to typed RequestHandler for improved type handling.
-class LegacyRequestHandler : public BaseRequestHandler {
-  using BaseRequestHandler::BaseRequestHandler;
-  virtual void operator()(const llvm::json::Object &request) const = 0;
-  void operator()(const protocol::Request &request) const override {
-    auto req = toJSON(request);
-    (*this)(*req.getAsObject());
-  }
-};
-
 template <typename Args>
 llvm::Expected<Args> parseArgs(const protocol::Request &request) {
   if (!is_optional_v<Args> && !request.arguments)
@@ -542,11 +532,14 @@ class SourceRequestHandler final
   Run(const protocol::SourceArguments &args) const override;
 };
 
-class StackTraceRequestHandler : public LegacyRequestHandler {
+class StackTraceRequestHandler
+    : public RequestHandler<protocol::StackTraceArguments,
+                            llvm::Expected<protocol::StackTraceResponseBody>> {
 public:
-  using LegacyRequestHandler::LegacyRequestHandler;
+  using RequestHandler::RequestHandler;
   static llvm::StringLiteral GetCommand() { return "stackTrace"; }
-  void operator()(const llvm::json::Object &request) const override;
+  llvm::Expected<protocol::StackTraceResponseBody>
+  Run(const protocol::StackTraceArguments &args) const override;
   FeatureSet GetSupportedFeatures() const override {
     return {protocol::eAdapterFeatureDelayedStackTraceLoading};
   }
diff --git a/lldb/tools/lldb-dap/Handler/StackTraceRequestHandler.cpp 
b/lldb/tools/lldb-dap/Handler/StackTraceRequestHandler.cpp
index 77ef952a1e343..362d9bbfb7a4d 100644
--- a/lldb/tools/lldb-dap/Handler/StackTraceRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/StackTraceRequestHandler.cpp
@@ -8,14 +8,105 @@
 
 #include "DAP.h"
 #include "EventHelper.h"
-#include "JSONUtils.h"
+#include "LLDBUtils.h"
+#include "Protocol/ProtocolRequests.h"
+#include "ProtocolUtils.h"
 #include "RequestHandler.h"
+#include "lldb/API/SBStream.h"
 
-namespace lldb_dap {
+using namespace lldb_dap;
+using namespace lldb_dap::protocol;
 
-/// Page size used for reporting addtional frames in the 'stackTrace' request.
+/// Page size used for reporting additional frames in the 'stackTrace' request.
 static constexpr int StackPageSize = 20;
 
+// Create a "StackFrame" object for a LLDB frame object.
+static StackFrame CreateStackFrame(DAP &dap, lldb::SBFrame &frame,
+                                   lldb::SBFormat &format) {
+  StackFrame stack_frame;
+  stack_frame.id = MakeDAPFrameID(frame);
+
+  lldb::SBStream stream;
+  if (format && frame.GetDescriptionWithFormat(format, stream).Success()) {
+    stack_frame.name = stream.GetData();
+
+    // `function_name` can be a nullptr, which throws an error when assigned to
+    // an `std::string`.
+  } else if (const char *name = frame.GetDisplayFunctionName()) {
+    stack_frame.name = name;
+  }
+
+  if (stack_frame.name.empty()) {
+    // If the function name is unavailable, display the pc address as a 
16-digit
+    // hex string, e.g. "0x0000000000012345"
+    stack_frame.name = GetLoadAddressString(frame.GetPC());
+  }
+
+  // We only include `[opt]` if a custom frame format is not specified.
+  if (!format && frame.GetFunction().GetIsOptimized())
+    stack_frame.name += " [opt]";
+
+  std::optional<protocol::Source> source = dap.ResolveSource(frame);
+  if (source && !IsAssemblySource(*source)) {
+    // This is a normal source with a valid line entry.
+    auto line_entry = frame.GetLineEntry();
+    stack_frame.line = line_entry.GetLine();
+    stack_frame.column = line_entry.GetColumn();
+  } else if (frame.GetSymbol().IsValid()) {
+    // This is a source where the disassembly is used, but there is a valid
+    // symbol. Calculate the line of the current PC from the start of the
+    // current symbol.
+    lldb::SBInstructionList inst_list = dap.target.ReadInstructions(
+        frame.GetSymbol().GetStartAddress(), frame.GetPCAddress(), nullptr);
+    size_t inst_line = inst_list.GetSize();
+
+    // Line numbers are 1-based.
+    stack_frame.line = inst_line + 1;
+    stack_frame.column = 1;
+  } else {
+    // No valid line entry or symbol.
+    stack_frame.line = 1;
+    stack_frame.column = 1;
+  }
+
+  stack_frame.source = std::move(source);
+  stack_frame.instructionPointerReference = frame.GetPC();
+
+  if (frame.IsArtificial() || frame.IsHidden())
+    stack_frame.presentationHint = StackFrame::ePresentationHintSubtle;
+  lldb::SBModule module = frame.GetModule();
+  if (module.IsValid()) {
+    if (const llvm::StringRef uuid = module.GetUUIDString(); !uuid.empty())
+      stack_frame.moduleId = uuid.str();
+  }
+
+  return stack_frame;
+}
+
+// Create a "StackFrame" label object for a LLDB thread.
+static StackFrame CreateExtendedStackFrameLabel(lldb::SBThread &thread,
+                                                lldb::SBFormat &format) {
+  StackFrame stack_frame;
+  lldb::SBStream stream;
+  if (format && thread.GetDescriptionWithFormat(format, stream).Success()) {
+    stack_frame.name = stream.GetData();
+  } else {
+    const uint32_t thread_idx = 
thread.GetExtendedBacktraceOriginatingIndexID();
+    const char *queue_name = thread.GetQueueName();
+    if (queue_name != nullptr) {
+      stack_frame.name = llvm::formatv("Enqueued from {0} (Thread {1})",
+                                       queue_name, thread_idx);
+    } else {
+      stack_frame.name = llvm::formatv("Thread {0}", thread_idx);
+    }
+  }
+
+  stack_frame.id = thread.GetThreadID() + 1;
+  stack_frame.presentationHint = StackFrame::ePresentationHintLabel;
+
+  return stack_frame;
+}
+
 // Fill in the stack frames of the thread.
 //
 // Threads stacks may contain runtime specific extended backtraces, when
@@ -50,13 +141,12 @@ static constexpr int StackPageSize = 20;
 // s=3,l=3 = [th0->s3, label1, th1->s0]
 static bool FillStackFrames(DAP &dap, lldb::SBThread &thread,
                             lldb::SBFormat &frame_format,
-                            llvm::json::Array &stack_frames, int64_t &offset,
-                            const int64_t start_frame, const int64_t levels,
-                            const bool include_all) {
+                            std::vector<StackFrame> &stack_frames,
+                            uint32_t &offset, const uint32_t start_frame,
+                            const uint32_t levels, const bool include_all) {
   bool reached_end_of_stack = false;
-  for (int64_t i = start_frame;
-       static_cast<int64_t>(stack_frames.size()) < levels; i++) {
-    if (i == -1) {
+  for (uint32_t i = start_frame; stack_frames.size() < levels; i++) {
+    if (i == UINT32_MAX) {
       stack_frames.emplace_back(
           CreateExtendedStackFrameLabel(thread, frame_format));
       continue;
@@ -83,9 +173,9 @@ static bool FillStackFrames(DAP &dap, lldb::SBThread &thread,
 
       reached_end_of_stack = FillStackFrames(
           dap, backtrace, frame_format, stack_frames, offset,
-          (start_frame - offset) > 0 ? start_frame - offset : -1, levels,
+          start_frame > offset ? start_frame - offset : UINT32_MAX, levels,
           include_all);
-      if (static_cast<int64_t>(stack_frames.size()) >= levels)
+      if (stack_frames.size() >= levels)
         break;
     }
   }
@@ -93,150 +183,53 @@ static bool FillStackFrames(DAP &dap, lldb::SBThread 
&thread,
   return reached_end_of_stack;
 }
 
-// "StackTraceRequest": {
-//   "allOf": [ { "$ref": "#/definitions/Request" }, {
-//     "type": "object",
-//     "description": "StackTrace request; value of command field is
-//     'stackTrace'. The request returns a stacktrace from the current 
execution
-//     state.", "properties": {
-//       "command": {
-//         "type": "string",
-//         "enum": [ "stackTrace" ]
-//       },
-//       "arguments": {
-//         "$ref": "#/definitions/StackTraceArguments"
-//       }
-//     },
-//     "required": [ "command", "arguments"  ]
-//   }]
-// },
-// "StackTraceArguments": {
-//   "type": "object",
-//   "description": "Arguments for 'stackTrace' request.",
-//   "properties": {
-//     "threadId": {
-//       "type": "integer",
-//       "description": "Retrieve the stacktrace for this thread."
-//     },
-//     "startFrame": {
-//       "type": "integer",
-//       "description": "The index of the first frame to return; if omitted
-//       frames start at 0."
-//     },
-//     "levels": {
-//       "type": "integer",
-//       "description": "The maximum number of frames to return. If levels is
-//       not specified or 0, all frames are returned."
-//     },
-//     "format": {
-//       "$ref": "#/definitions/StackFrameFormat",
-//       "description": "Specifies details on how to format the stack frames.
-//       The attribute is only honored by a debug adapter if the corresponding
-//       capability `supportsValueFormattingOptions` is true."
-//     }
-//  },
-//   "required": [ "threadId" ]
-// },
-// "StackTraceResponse": {
-//   "allOf": [ { "$ref": "#/definitions/Response" }, {
-//     "type": "object",
-//     "description": "Response to `stackTrace` request.",
-//     "properties": {
-//       "body": {
-//         "type": "object",
-//         "properties": {
-//           "stackFrames": {
-//             "type": "array",
-//             "items": {
-//               "$ref": "#/definitions/StackFrame"
-//             },
-//             "description": "The frames of the stackframe. If the array has
-//             length zero, there are no stackframes available. This means that
-//             there is no location information available."
-//           },
-//           "totalFrames": {
-//             "type": "integer",
-//             "description": "The total number of frames available in the
-//             stack. If omitted or if `totalFrames` is larger than the
-//             available frames, a client is expected to request frames until
-//             a request returns less frames than requested (which indicates
-//             the end of the stack). Returning monotonically increasing
-//             `totalFrames` values for subsequent requests can be used to
-//             enforce paging in the client."
-//           }
-//         },
-//         "required": [ "stackFrames" ]
-//       }
-//     },
-//     "required": [ "body" ]
-//   }]
-// }
-void StackTraceRequestHandler::operator()(
-    const llvm::json::Object &request) const {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  lldb::SBError error;
-  const auto *arguments = request.getObject("arguments");
-  lldb::SBThread thread = dap.GetLLDBThread(*arguments);
-  llvm::json::Array stack_frames;
-  llvm::json::Object body;
+llvm::Expected<protocol::StackTraceResponseBody>
+StackTraceRequestHandler::Run(const protocol::StackTraceArguments &args) const 
{
+  lldb::SBThread thread = dap.GetLLDBThread(args.threadId);
 
   lldb::SBFormat frame_format = dap.frame_format;
   bool include_all = dap.configuration.displayExtendedBacktrace;
 
-  if (const auto *format = arguments->getObject("format")) {
-    // Indicates that all stack frames should be included, even those the debug
-    // adapter might otherwise hide.
-    include_all = GetBoolean(format, "includeAll").value_or(false);
+  if (args.format) {
+    const StackFrameFormat &format = *args.format;
 
-    // Parse the properties that have a corresponding format string.
-    // FIXME: Support "parameterTypes" and "hex".
-    const bool module = GetBoolean(format, "module").value_or(false);
-    const bool line = GetBoolean(format, "line").value_or(false);
-    const bool parameters = GetBoolean(format, "parameters").value_or(false);
-    const bool parameter_names =
-        GetBoolean(format, "parameterNames").value_or(false);
-    const bool parameter_values =
-        GetBoolean(format, "parameterValues").value_or(false);
+    include_all = format.includeAll;
 
+    // FIXME: Support "parameterTypes" and "hex".
     // Only change the format string if we have to.
-    if (module || line || parameters || parameter_names || parameter_values) {
+    if (format.module || format.line || format.parameters ||
+        format.parameterNames || format.parameterValues) {
       std::string format_str;
       llvm::raw_string_ostream os(format_str);
 
-      if (module)
+      if (format.module)
         os << "{${module.file.basename} }";
 
-      if (line)
+      if (format.line)
         os << "{${line.file.basename}:${line.number}:${line.column} }";
 
-      if (parameters || parameter_names || parameter_values)
+      if (format.parameters || format.parameterNames || format.parameterValues)
         os << "{${function.name-with-args}}";
       else
         os << "{${function.name-without-args}}";
 
       lldb::SBError error;
       frame_format = lldb::SBFormat(format_str.c_str(), error);
-      assert(error.Success());
+      if (error.Fail())
+        return ToError(error);
     }
   }
 
+  StackTraceResponseBody body;
   if (thread.IsValid()) {
-    const auto start_frame =
-        GetInteger<uint64_t>(arguments, "startFrame").value_or(0);
-    const auto levels = GetInteger<uint64_t>(arguments, "levels").value_or(0);
-    int64_t offset = 0;
-    bool reached_end_of_stack = FillStackFrames(
-        dap, thread, frame_format, stack_frames, offset, start_frame,
-        levels == 0 ? INT64_MAX : levels, include_all);
-    body.try_emplace("totalFrames",
-                     start_frame + stack_frames.size() +
-                         (reached_end_of_stack ? 0 : StackPageSize));
+    const auto levels = args.levels == 0 ? UINT32_MAX : args.levels;
+    uint32_t offset = 0;
+    bool reached_end_of_stack =
+        FillStackFrames(dap, thread, frame_format, body.stackFrames, offset,
+                        args.startFrame, levels, include_all);
+    body.totalFrames = args.startFrame + body.stackFrames.size() +
+                       (reached_end_of_stack ? 0 : StackPageSize);
   }
 
-  body.try_emplace("stackFrames", std::move(stack_frames));
-  response.try_emplace("body", std::move(body));
-  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 1beee416bf333..5815f1ea394a0 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -430,164 +430,6 @@ llvm::json::Object CreateEventObject(const 
llvm::StringRef event_name) {
   return event;
 }
 
-// "StackFrame": {
-//   "type": "object",
-//   "description": "A Stackframe contains the source location.",
-//   "properties": {
-//     "id": {
-//       "type": "integer",
-//       "description": "An identifier for the stack frame. It must be unique
-//                       across all threads. This id can be used to retrieve
-//                       the scopes of the frame with the 'scopesRequest' or
-//                       to restart the execution of a stackframe."
-//     },
-//     "name": {
-//       "type": "string",
-//       "description": "The name of the stack frame, typically a method name."
-//     },
-//     "source": {
-//       "$ref": "#/definitions/Source",
-//       "description": "The optional source of the frame."
-//     },
-//     "line": {
-//       "type": "integer",
-//       "description": "The line within the file of the frame. If source is
-//                       null or doesn't exist, line is 0 and must be ignored."
-//     },
-//     "column": {
-//       "type": "integer",
-//       "description": "The column within the line. If source is null or
-//                       doesn't exist, column is 0 and must be ignored."
-//     },
-//     "endLine": {
-//       "type": "integer",
-//       "description": "An optional end line of the range covered by the
-//                       stack frame."
-//     },
-//     "endColumn": {
-//       "type": "integer",
-//       "description": "An optional end column of the range covered by the
-//                       stack frame."
-//     },
-//     "instructionPointerReference": {
-//          "type": "string",
-//          "description": "A memory reference for the current instruction
-//                         pointer in this frame."
-//     },
-//     "moduleId": {
-//       "type": ["integer", "string"],
-//       "description": "The module associated with this frame, if any."
-//     },
-//     "presentationHint": {
-//       "type": "string",
-//       "enum": [ "normal", "label", "subtle" ],
-//       "description": "An optional hint for how to present this frame in
-//                       the UI. A value of 'label' can be used to indicate
-//                       that the frame is an artificial frame that is used
-//                       as a visual label or separator. A value of 'subtle'
-//                       can be used to change the appearance of a frame in
-//                       a 'subtle' way."
-//     }
-//   },
-//   "required": [ "id", "name", "line", "column" ]
-// }
-llvm::json::Value CreateStackFrame(DAP &dap, lldb::SBFrame &frame,
-                                   lldb::SBFormat &format) {
-  llvm::json::Object object;
-  int64_t frame_id = MakeDAPFrameID(frame);
-  object.try_emplace("id", frame_id);
-
-  std::string frame_name;
-  lldb::SBStream stream;
-  if (format && frame.GetDescriptionWithFormat(format, stream).Success()) {
-    frame_name = stream.GetData();
-
-    // `function_name` can be a nullptr, which throws an error when assigned to
-    // an `std::string`.
-  } else if (const char *name = frame.GetDisplayFunctionName()) {
-    frame_name = name;
-  }
-
-  if (frame_name.empty()) {
-    // If the function name is unavailable, display the pc address as a 
16-digit
-    // hex string, e.g. "0x0000000000012345"
-    frame_name = GetLoadAddressString(frame.GetPC());
-  }
-
-  // We only include `[opt]` if a custom frame format is not specified.
-  if (!format && frame.GetFunction().GetIsOptimized())
-    frame_name += " [opt]";
-
-  EmplaceSafeString(object, "name", frame_name);
-
-  std::optional<protocol::Source> source = dap.ResolveSource(frame);
-
-  if (source && !IsAssemblySource(*source)) {
-    // This is a normal source with a valid line entry.
-    auto line_entry = frame.GetLineEntry();
-    object.try_emplace("line", line_entry.GetLine());
-    auto column = line_entry.GetColumn();
-    object.try_emplace("column", column);
-  } else if (frame.GetSymbol().IsValid()) {
-    // This is a source where the disassembly is used, but there is a valid
-    // symbol. Calculate the line of the current PC from the start of the
-    // current symbol.
-    lldb::SBInstructionList inst_list = dap.target.ReadInstructions(
-        frame.GetSymbol().GetStartAddress(), frame.GetPCAddress(), nullptr);
-    size_t inst_line = inst_list.GetSize();
-
-    // Line numbers are 1-based.
-    object.try_emplace("line", inst_line + 1);
-    object.try_emplace("column", 1);
-  } else {
-    // No valid line entry or symbol.
-    object.try_emplace("line", 1);
-    object.try_emplace("column", 1);
-  }
-
-  if (source)
-    object.try_emplace("source", std::move(source).value());
-
-  const auto pc = frame.GetPC();
-  if (pc != LLDB_INVALID_ADDRESS) {
-    std::string formatted_addr = "0x" + llvm::utohexstr(pc);
-    object.try_emplace("instructionPointerReference", formatted_addr);
-  }
-
-  if (frame.IsArtificial() || frame.IsHidden())
-    object.try_emplace("presentationHint", "subtle");
-
-  lldb::SBModule module = frame.GetModule();
-  if (module.IsValid()) {
-    if (const llvm::StringRef uuid = module.GetUUIDString(); !uuid.empty())
-      object.try_emplace("moduleId", uuid.str());
-  }
-
-  return llvm::json::Value(std::move(object));
-}
-
-llvm::json::Value CreateExtendedStackFrameLabel(lldb::SBThread &thread,
-                                                lldb::SBFormat &format) {
-  std::string name;
-  lldb::SBStream stream;
-  if (format && thread.GetDescriptionWithFormat(format, stream).Success()) {
-    name = stream.GetData();
-  } else {
-    const uint32_t thread_idx = 
thread.GetExtendedBacktraceOriginatingIndexID();
-    const char *queue_name = thread.GetQueueName();
-    if (queue_name != nullptr) {
-      name = llvm::formatv("Enqueued from {0} (Thread {1})", queue_name,
-                           thread_idx);
-    } else {
-      name = llvm::formatv("Thread {0}", thread_idx);
-    }
-  }
-
-  return llvm::json::Value(llvm::json::Object{{"id", thread.GetThreadID() + 1},
-                                              {"name", name},
-                                              {"presentationHint", "label"}});
-}
-
 // "StoppedEvent": {
 //   "allOf": [ { "$ref": "#/definitions/Event" }, {
 //     "type": "object",
diff --git a/lldb/tools/lldb-dap/JSONUtils.h b/lldb/tools/lldb-dap/JSONUtils.h
index 0a907599fc9ee..c5c538dd03def 100644
--- a/lldb/tools/lldb-dap/JSONUtils.h
+++ b/lldb/tools/lldb-dap/JSONUtils.h
@@ -234,56 +234,6 @@ void FillResponse(const llvm::json::Object &request,
 ///     definition outlined by Microsoft.
 llvm::json::Object CreateEventObject(const llvm::StringRef event_name);
 
-/// Create a "StackFrame" object for a LLDB frame object.
-///
-/// This function will fill in the following keys in the returned
-/// object:
-///   "id" - the stack frame ID as an integer
-///   "name" - the function name as a string
-///   "source" - source file information as a "Source" DAP object
-///   "line" - the source file line number as an integer
-///   "column" - the source file column number as an integer
-///
-/// \param[in] dap
-///     The DAP session associated with the stopped thread.
-///
-/// \param[in] frame
-///     The LLDB stack frame to use when populating out the "StackFrame"
-///     object.
-///
-/// \param[in] format
-///     The LLDB format to use when populating out the "StackFrame"
-///     object.
-///
-/// \return
-///     A "StackFrame" JSON object with that follows the formal JSON
-///     definition outlined by Microsoft.
-llvm::json::Value CreateStackFrame(DAP &dap, lldb::SBFrame &frame,
-                                   lldb::SBFormat &format);
-
-/// Create a "StackFrame" label object for a LLDB thread.
-///
-/// This function will fill in the following keys in the returned
-/// object:
-///   "id" - the thread ID as an integer
-///   "name" - the thread name as a string which combines the LLDB
-///            thread index ID along with the string name of the thread
-///            from the OS if it has a name.
-///   "presentationHint" - "label"
-///
-/// \param[in] thread
-///     The LLDB thread to use when populating out the "Thread"
-///     object.
-///
-/// \param[in] format
-///     The configured formatter for the DAP session.
-///
-/// \return
-///     A "StackFrame" JSON object with that follows the formal JSON
-///     definition outlined by Microsoft.
-llvm::json::Value CreateExtendedStackFrameLabel(lldb::SBThread &thread,
-                                                lldb::SBFormat &format);
-
 /// Create a "StoppedEvent" object for a LLDB thread object.
 ///
 /// This function will fill in the following keys in the returned
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp 
b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
index c87111d8f1b78..c3225f6ba0e35 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
@@ -802,4 +802,22 @@ bool fromJSON(const llvm::json::Value &Params, 
RestartArguments &Args,
   return false;
 }
 
+bool fromJSON(const llvm::json::Value &Params, StackTraceArguments &Args,
+              llvm::json::Path Path) {
+  json::ObjectMapper O(Params, Path);
+  return O && O.map("threadId", Args.threadId) &&
+         O.mapOptional("startFrame", Args.startFrame) &&
+         O.mapOptional("levels", Args.levels) &&
+         O.mapOptional("format", Args.format);
+}
+
+llvm::json::Value toJSON(const StackTraceResponseBody &Body) {
+  json::Object result{{"stackFrames", Body.stackFrames}};
+
+  if (Body.totalFrames)
+    result.insert({"totalFrames", Body.totalFrames});
+
+  return result;
+}
+
 } // namespace lldb_dap::protocol
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h 
b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
index 33fcaae1710b5..9a99af9068ef1 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
@@ -1268,6 +1268,44 @@ bool fromJSON(const llvm::json::Value &, 
RestartArguments &, llvm::json::Path);
 /// field is required.
 using RestartResponse = VoidResponse;
 
+/// Arguments for `stackTrace` request.
+struct StackTraceArguments {
+  /// Retrieve the stacktrace for this thread.
+  lldb::tid_t threadId = LLDB_INVALID_THREAD_ID;
+
+  /// The index of the first frame to return; if omitted frames start at 0.
+  uint32_t startFrame = 0;
+
+  /// The maximum number of frames to return. If levels is not specified or 0,
+  /// all frames are returned.
+  uint32_t levels = 0;
+
+  /// Specifies details on how to format the returned `StackFrame.name`. The
+  /// debug adapter may format requested details in any way that would make
+  /// sense to a developer. The attribute is only honored by a debug adapter if
+  /// the corresponding capability `supportsValueFormattingOptions` is true.
+  std::optional<StackFrameFormat> format;
+};
+bool fromJSON(const llvm::json::Value &, StackTraceArguments &,
+              llvm::json::Path);
+
+/// Response to `stackTrace` request.
+struct StackTraceResponseBody {
+  /// The frames of the stack frame. If the array has length zero, there are no
+  /// stack frames available.
+  /// This means that there is no location information available.
+  std::vector<StackFrame> stackFrames;
+
+  /// The total number of frames available in the stack. If omitted or if
+  /// `totalFrames` is larger than the available frames, a client is expected 
to
+  /// request frames until a request returns less frames than requested (which
+  /// indicates the end of the stack). Returning monotonically increasing
+  /// `totalFrames` values for subsequent requests can be used to enforce 
paging
+  /// in the client.
+  uint32_t totalFrames = 0;
+};
+llvm::json::Value toJSON(const StackTraceResponseBody &);
+
 } // namespace lldb_dap::protocol
 
 #endif
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp 
b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
index c7f7c447b5b6f..a5c7fc283b796 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
@@ -1174,4 +1174,58 @@ llvm::json::Value toJSON(const CompileUnit &CU) {
   return result;
 }
 
+bool fromJSON(const llvm::json::Value &Params, StackFrameFormat &SFF,
+              llvm::json::Path Path) {
+  json::ObjectMapper O(Params, Path);
+  return O && O.mapOptional("parameters", SFF.parameters) &&
+         O.mapOptional("parameterTypes", SFF.parameterTypes) &&
+         O.mapOptional("parameterNames", SFF.parameterNames) &&
+         O.mapOptional("parameterValues", SFF.parameterValues) &&
+         O.mapOptional("line", SFF.line) &&
+         O.mapOptional("module", SFF.module) &&
+         O.mapOptional("includeAll", SFF.includeAll);
+}
+
+llvm::json::Value toJSON(const StackFrame::PresentationHint &PH) {
+  switch (PH) {
+  case StackFrame::ePresentationHintNormal:
+    return "normal";
+  case StackFrame::ePresentationHintLabel:
+    return "label";
+  case StackFrame::ePresentationHintSubtle:
+    return "subtle";
+  }
+  llvm_unreachable("unhandled stackFrame presentationHint.");
+}
+
+llvm::json::Value toJSON(const StackFrame &SF) {
+  json::Object result{{"id", SF.id}, {"name", SF.name}};
+
+  if (SF.source) {
+    result.insert({"source", *SF.source});
+    assert(SF.line != LLDB_INVALID_LINE_NUMBER);
+    result.insert({"line", SF.line});
+    assert(SF.column != LLDB_INVALID_COLUMN_NUMBER);
+    result.insert({"column", SF.column});
+    if (SF.endLine != 0 && SF.endLine != LLDB_INVALID_LINE_NUMBER)
+      result.insert({"endLine", SF.endLine});
+    if (SF.endColumn != 0 && SF.endColumn != LLDB_INVALID_COLUMN_NUMBER)
+      result.insert({"endColumn", SF.endColumn});
+  } else {
+    result.insert({"line", 0});
+    result.insert({"column", 0});
+  }
+  if (SF.canRestart)
+    result.insert({"canRestart", SF.canRestart});
+  if (SF.instructionPointerReference != LLDB_INVALID_ADDRESS)
+    result.insert({"instructionPointerReference",
+                   EncodeMemoryReference(SF.instructionPointerReference)});
+  if (SF.moduleId)
+    result.insert({"moduleId", *SF.moduleId});
+  if (SF.presentationHint)
+    result.insert({"presentationHint", *SF.presentationHint});
+
+  return result;
+}
+
 } // namespace lldb_dap::protocol
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h 
b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
index 4ead4786bc661..cad09df145367 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
@@ -1044,6 +1044,92 @@ struct CompileUnit {
 };
 llvm::json::Value toJSON(const CompileUnit &);
 
+/// Provides formatting information for a stack frame.
+struct StackFrameFormat {
+  /// Displays parameters for the stack frame.
+  bool parameters = false;
+
+  /// Displays the types of parameters for the stack frame.
+  bool parameterTypes = false;
+
+  /// Displays the names of parameters for the stack frame.
+  bool parameterNames = false;
+
+  /// Displays the values of parameters for the stack frame.
+  bool parameterValues = false;
+
+  /// Displays the line number of the stack frame.
+  bool line = false;
+
+  /// Displays the module of the stack frame.
+  bool module = false;
+
+  /// Includes all stack frames, including those the debug adapter might
+  /// otherwise hide.
+  bool includeAll = false;
+};
+bool fromJSON(const llvm::json::Value &, StackFrameFormat &, llvm::json::Path);
+
+/// A Stackframe contains the source location.
+struct StackFrame {
+  enum PresentationHint : unsigned {
+    ePresentationHintNormal,
+    ePresentationHintLabel,
+    ePresentationHintSubtle,
+  };
+
+  /// An identifier for the stack frame. It must be unique across all threads.
+  /// This id can be used to retrieve the scopes of the frame with the `scopes`
+  /// request or to restart the execution of a stack frame.
+  int64_t id;
+
+  /// The name of the stack frame, typically a method name.
+  std::string name;
+
+  /// The source of the frame.
+  std::optional<Source> source;
+
+  /// The line within the source of the frame. If the source attribute is
+  /// missing or doesn't exist, `line` is 0 and should be ignored by the 
client.
+  uint32_t line = LLDB_INVALID_LINE_NUMBER;
+
+  /// Start position of the range covered by the stack frame. It is measured in
+  /// UTF-16 code units and the client capability `columnsStartAt1` determines
+  /// whether it is 0- or 1-based. If attribute `source` is missing or doesn't
+  /// exist, `column` is 0 and should be ignored by the client.
+  uint32_t column = LLDB_INVALID_COLUMN_NUMBER;
+
+  /// The end line of the range covered by the stack frame.
+  uint32_t endLine = LLDB_INVALID_LINE_NUMBER;
+
+  /// End position of the range covered by the stack frame. It is measured in
+  /// UTF-16 code units and the client capability `columnsStartAt1` determines
+  /// whether it is 0- or 1-based.
+  uint32_t endColumn = LLDB_INVALID_COLUMN_NUMBER;
+
+  /// Indicates whether this frame can be restarted with the `restartFrame`
+  /// request. Clients should only use this if the debug adapter supports the
+  /// `restart` request and the corresponding capability `supportsRestartFrame`
+  /// is true. If a debug adapter has this capability, then `canRestart`
+  /// defaults to `true` if the property is absent.
+  bool canRestart = false;
+
+  /// A memory reference for the current instruction pointer in this frame.
+  lldb::addr_t instructionPointerReference = LLDB_INVALID_ADDRESS;
+
+  /// The module associated with this frame, if any.
+  std::optional<std::string> moduleId;
+
+  /// A hint for how to present this frame in the UI. A value of `label` can be
+  /// used to indicate that the frame is an artificial frame that is used as a
+  /// visual label or separator. A value of `subtle` can be used to change the
+  /// appearance of a frame in a 'subtle' way. Values: 'normal', 'label',
+  /// 'subtle'
+  std::optional<PresentationHint> presentationHint;
+};
+llvm::json::Value toJSON(const StackFrame::PresentationHint &);
+llvm::json::Value toJSON(const StackFrame &);
+
 } // namespace lldb_dap::protocol
 
 #endif
diff --git a/lldb/unittests/DAP/ProtocolRequestsTest.cpp 
b/lldb/unittests/DAP/ProtocolRequestsTest.cpp
index c639e40453fb0..cdc012b448c8f 100644
--- a/lldb/unittests/DAP/ProtocolRequestsTest.cpp
+++ b/lldb/unittests/DAP/ProtocolRequestsTest.cpp
@@ -344,3 +344,71 @@ TEST(ProtocolRequestsTest, RestartArguments) {
   EXPECT_NE(attach_args, nullptr);
   EXPECT_EQ(attach_args->pid, 123U);
 }
+
+TEST(ProtocolRequestsTest, StackTraceArguments) {
+  llvm::Expected<StackTraceArguments> expected = 
parse<StackTraceArguments>(R"({
+    "threadId": 42,
+    "startFrame": 1,
+    "levels": 10,
+    "format": {
+      "parameters": true,
+      "line": true
+    }
+  })");
+  ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
+  EXPECT_EQ(expected->threadId, 42U);
+  EXPECT_EQ(expected->startFrame, 1U);
+  EXPECT_EQ(expected->levels, 10U);
+  EXPECT_EQ(expected->format->parameters, true);
+  EXPECT_EQ(expected->format->line, true);
+
+  // Check required keys.
+  EXPECT_THAT_EXPECTED(parse<StackTraceArguments>(R"({})"),
+                       FailedWithMessage("missing value at (root).threadId"));
+}
+
+TEST(ProtocolRequestsTest, StackTraceResponseBody) {
+  StackFrame frame1;
+  frame1.id = 1;
+  frame1.name = "main";
+  frame1.source = Source{};
+  frame1.source->name = "main.cpp";
+  frame1.source->sourceReference = 123;
+  frame1.line = 23;
+  frame1.column = 1;
+  StackFrame frame2;
+  frame2.id = 2;
+  frame2.name = "test";
+  frame2.presentationHint = StackFrame::ePresentationHintLabel;
+
+  StackTraceResponseBody body;
+  body.stackFrames = {frame1, frame2};
+  body.totalFrames = 2;
+
+  // Check required keys.
+  Expected<json::Value> expected = parse(R"({
+    "stackFrames": [
+      {
+        "id": 1,
+        "name": "main",
+        "source": {
+          "name": "main.cpp",
+          "sourceReference": 123
+        },
+        "line": 23,
+        "column": 1
+      },
+      {
+        "id": 2,
+        "name": "test",
+        "line": 0,
+        "column": 0,
+        "presentationHint": "label"
+      }
+    ],
+    "totalFrames": 2
+  })");
+
+  ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
+  EXPECT_EQ(PrettyPrint(*expected), PrettyPrint(body));
+}

>From c0aa811061de19242d133ada44f485037386fa2e Mon Sep 17 00:00:00 2001
From: Druzhkov Sergei <[email protected]>
Date: Mon, 22 Dec 2025 12:29:08 +0300
Subject: [PATCH 2/2] Try fix CI

---
 lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp 
b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
index a5c7fc283b796..73e0b2a2acd46 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
@@ -1205,7 +1205,6 @@ llvm::json::Value toJSON(const StackFrame &SF) {
     result.insert({"source", *SF.source});
     assert(SF.line != LLDB_INVALID_LINE_NUMBER);
     result.insert({"line", SF.line});
-    assert(SF.column != LLDB_INVALID_COLUMN_NUMBER);
     result.insert({"column", SF.column});
     if (SF.endLine != 0 && SF.endLine != LLDB_INVALID_LINE_NUMBER)
       result.insert({"endLine", SF.endLine});

_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to