Skip to content

[lldb-dap] Adding a DAPError for showing users error messages. #132255

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lldb/tools/lldb-dap/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ add_lldb_tool(lldb-dap
Breakpoint.cpp
BreakpointBase.cpp
DAP.cpp
DAPError.cpp
DAPLog.cpp
EventHelper.cpp
ExceptionBreakpoint.cpp
Expand Down
29 changes: 29 additions & 0 deletions lldb/tools/lldb-dap/DAPError.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//===-- DAPError.cpp ------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "DAPError.h"
#include "llvm/Support/Error.h"
#include <system_error>

namespace lldb_dap {

char DAPError::ID;

DAPError::DAPError(std::string message, std::error_code EC, bool show_user,
std::optional<std::string> url,
std::optional<std::string> url_label)
: m_message(message), m_ec(EC), m_show_user(show_user), m_url(url),
m_url_label(url_label) {}

void DAPError::log(llvm::raw_ostream &OS) const { OS << m_message; }

std::error_code DAPError::convertToErrorCode() const {
return llvm::inconvertibleErrorCode();
}

} // namespace lldb_dap
43 changes: 43 additions & 0 deletions lldb/tools/lldb-dap/DAPError.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//===-- DAPError.h --------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "llvm/Support/Error.h"
#include <optional>
#include <string>
#include <system_error>

namespace lldb_dap {

/// An Error that is reported as a DAP Error Message, which may be presented to
/// the user.
class DAPError : public llvm::ErrorInfo<DAPError> {
public:
static char ID;

DAPError(std::string message,
std::error_code EC = llvm::inconvertibleErrorCode(),
bool show_user = true, std::optional<std::string> url = std::nullopt,
std::optional<std::string> url_label = std::nullopt);

void log(llvm::raw_ostream &OS) const override;
std::error_code convertToErrorCode() const override;

const std::string &getMessage() const { return m_message; }
bool getShowUser() const { return m_show_user; }
const std::optional<std::string> &getURL() const { return m_url; }
const std::optional<std::string> &getURLLabel() const { return m_url; }

private:
std::string m_message;
std::error_code m_ec;
bool m_show_user;
std::optional<std::string> m_url;
std::optional<std::string> m_url_label;
};

} // namespace lldb_dap
34 changes: 27 additions & 7 deletions lldb/tools/lldb-dap/Handler/RequestHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@
#define LLDB_TOOLS_LLDB_DAP_HANDLER_HANDLER_H

#include "DAP.h"
#include "DAPError.h"
#include "DAPLog.h"
#include "Protocol/ProtocolBase.h"
#include "Protocol/ProtocolRequests.h"
#include "lldb/API/SBError.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/JSON.h"
#include <optional>
#include <type_traits>
Expand Down Expand Up @@ -118,20 +120,38 @@ class RequestHandler : public BaseRequestHandler {
std::string parse_failure;
llvm::raw_string_ostream OS(parse_failure);
root.printErrorContext(request.arguments, OS);

protocol::ErrorMessage error_message;
error_message.format = parse_failure;

protocol::ErrorResponseBody body;
body.error = error_message;

response.success = false;
response.message = parse_failure;
response.body = std::move(body);

dap.Send(response);
return;
}

auto body = Run(arguments);
// FIXME: Add a dedicated DAPError for enhanced errors that are
// user-visibile.
llvm::Expected<Body> body = Run(arguments);
if (auto Err = body.takeError()) {
protocol::ErrorMessage error_message;
error_message.sendTelemetry = false;
if (llvm::Error unhandled = llvm::handleErrors(
std::move(Err), [&](const DAPError &E) -> llvm::Error {
error_message.format = E.getMessage();
error_message.showUser = E.getShowUser();
error_message.id = E.convertToErrorCode().value();
error_message.url = E.getURL();
error_message.urlLabel = E.getURLLabel();
return llvm::Error::success();
}))
error_message.format = llvm::toString(std::move(unhandled));
protocol::ErrorResponseBody body;
body.error = error_message;
response.success = false;
// FIXME: Build ErrorMessage based on error details instead of using the
// 'message' field.
response.message = llvm::toString(std::move(Err));
response.body = std::move(body);
} else {
response.success = true;
if constexpr (!std::is_same_v<Body, std::monostate>)
Expand Down
4 changes: 2 additions & 2 deletions lldb/tools/lldb-dap/Handler/SourceRequestHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ SourceRequestHandler::Run(const protocol::SourceArguments &args) const {
args.source->sourceReference.value_or(args.sourceReference);

if (!source)
return llvm::createStringError(
return llvm::make_error<DAPError>(
"invalid arguments, expected source.sourceReference to be set");

lldb::SBProcess process = dap.target.GetProcess();
Expand All @@ -39,7 +39,7 @@ SourceRequestHandler::Run(const protocol::SourceArguments &args) const {
// Lower 32 bits is the frame index
lldb::SBFrame frame = thread.GetFrameAtIndex(GetLLDBFrameID(source));
if (!frame.IsValid())
return llvm::createStringError("source not found");
return llvm::make_error<DAPError>("source not found");

lldb::SBInstructionList insts = frame.GetSymbol().GetInstructions(dap.target);
lldb::SBStream stream;
Expand Down
9 changes: 9 additions & 0 deletions lldb/tools/lldb-dap/Protocol/ProtocolBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,4 +286,13 @@ json::Value toJSON(const Message &M) {
return std::visit([](auto &M) { return toJSON(M); }, M);
}

json::Value toJSON(const ErrorResponseBody &E) {
json::Object result{};

if (E.error)
result.insert({"error", *E.error});

return result;
}

} // namespace lldb_dap::protocol
13 changes: 10 additions & 3 deletions lldb/tools/lldb-dap/Protocol/ProtocolBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ struct ErrorMessage {
/// requirement that every user visible error message needs a corresponding
/// error number, so that users or customer support can find information about
/// the specific error more easily.
uint64_t id;
uint64_t id = 0;

/// A format string for the message. Embedded variables have the form
/// `{name}`. If variable name starts with an underscore character, the
Expand All @@ -122,10 +122,10 @@ struct ErrorMessage {
std::optional<std::map<std::string, std::string>> variables;

/// If true send to telemetry.
bool sendTelemetry;
bool sendTelemetry = false;

/// If true show user.
bool showUser;
bool showUser = false;

/// A url where additional information about this message can be found.
std::optional<std::string> url;
Expand All @@ -141,6 +141,13 @@ using Message = std::variant<Request, Response, Event>;
bool fromJSON(const llvm::json::Value &, Message &, llvm::json::Path);
llvm::json::Value toJSON(const Message &);

/// On error (whenever `success` is false), the body can provide more details.
struct ErrorResponseBody {
/// A structured error message.
std::optional<ErrorMessage> error;
};
llvm::json::Value toJSON(const ErrorResponseBody &);

/// This is just an acknowledgement, so no body field is required.
using VoidResponse = std::monostate;

Expand Down