Skip to content
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
209 changes: 151 additions & 58 deletions lldb/tools/lldb-dap/Handler/ExceptionInfoRequestHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,75 +7,168 @@
//===----------------------------------------------------------------------===//

#include "DAP.h"
#include "DAPError.h"
#include "Protocol/ProtocolRequests.h"
#include "Protocol/ProtocolTypes.h"
#include "EventHelper.h"
#include "JSONUtils.h"
#include "RequestHandler.h"
#include "lldb/API/SBStream.h"

using namespace lldb_dap::protocol;

namespace lldb_dap {

/// Retrieves the details of the exception that caused this event to be raised.
///
/// Clients should only call this request if the corresponding capability
/// `supportsExceptionInfoRequest` is true.
llvm::Expected<ExceptionInfoResponseBody>
ExceptionInfoRequestHandler::Run(const ExceptionInfoArguments &args) const {

lldb::SBThread thread = dap.GetLLDBThread(args.threadId);
if (!thread.IsValid())
return llvm::make_error<DAPError>(
llvm::formatv("Invalid thread id: {}", args.threadId).str());

ExceptionInfoResponseBody response;
response.breakMode = eExceptionBreakModeAlways;
const lldb::StopReason stop_reason = thread.GetStopReason();
switch (stop_reason) {
case lldb::eStopReasonSignal:
response.exceptionId = "signal";
break;
case lldb::eStopReasonBreakpoint: {
const ExceptionBreakpoint *exc_bp =
dap.GetExceptionBPFromStopReason(thread);
if (exc_bp) {
response.exceptionId = exc_bp->GetFilter();
response.description = exc_bp->GetLabel();
// "ExceptionInfoRequest": {
// "allOf": [ { "$ref": "#/definitions/Request" }, {
// "type": "object",
// "description": "Retrieves the details of the exception that
// caused this event to be raised. Clients should only call this request if
// the corresponding capability `supportsExceptionInfoRequest` is true.",
// "properties": {
// "command": {
// "type": "string",
// "enum": [ "exceptionInfo" ]
// },
// "arguments": {
// "$ref": "#/definitions/ExceptionInfoArguments"
// }
// },
// "required": [ "command", "arguments" ]
// }]
// },
// "ExceptionInfoArguments": {
// "type": "object",
// "description": "Arguments for `exceptionInfo` request.",
// "properties": {
// "threadId": {
// "type": "integer",
// "description": "Thread for which exception information should be
// retrieved."
// }
// },
// "required": [ "threadId" ]
// },
// "ExceptionInfoResponse": {
// "allOf": [ { "$ref": "#/definitions/Response" }, {
// "type": "object",
// "description": "Response to `exceptionInfo` request.",
// "properties": {
// "body": {
// "type": "object",
// "properties": {
// "exceptionId": {
// "type": "string",
// "description": "ID of the exception that was thrown."
// },
// "description": {
// "type": "string",
// "description": "Descriptive text for the exception."
// },
// "breakMode": {
// "$ref": "#/definitions/ExceptionBreakMode",
// "description": "Mode that caused the exception notification to
// be raised."
// },
// "details": {
// "$ref": "#/definitions/ExceptionDetails",
// "description": "Detailed information about the exception."
// }
// },
// "required": [ "exceptionId", "breakMode" ]
// }
// },
// "required": [ "body" ]
// }]
// }
// "ExceptionDetails": {
// "type": "object",
// "description": "Detailed information about an exception that has
// occurred.", "properties": {
// "message": {
// "type": "string",
// "description": "Message contained in the exception."
// },
// "typeName": {
// "type": "string",
// "description": "Short type name of the exception object."
// },
// "fullTypeName": {
// "type": "string",
// "description": "Fully-qualified type name of the exception object."
// },
// "evaluateName": {
// "type": "string",
// "description": "An expression that can be evaluated in the current
// scope to obtain the exception object."
// },
// "stackTrace": {
// "type": "string",
// "description": "Stack trace at the time the exception was thrown."
// },
// "innerException": {
// "type": "array",
// "items": {
// "$ref": "#/definitions/ExceptionDetails"
// },
// "description": "Details of the exception contained by this exception,
// if any."
// }
// }
// },
void ExceptionInfoRequestHandler::operator()(
const llvm::json::Object &request) const {
llvm::json::Object response;
FillResponse(request, response);
const auto *arguments = request.getObject("arguments");
llvm::json::Object body;
lldb::SBThread thread = dap.GetLLDBThread(*arguments);
if (thread.IsValid()) {
auto stopReason = thread.GetStopReason();
if (stopReason == lldb::eStopReasonSignal)
body.try_emplace("exceptionId", "signal");
else if (stopReason == lldb::eStopReasonBreakpoint) {
ExceptionBreakpoint *exc_bp = dap.GetExceptionBPFromStopReason(thread);
if (exc_bp) {
EmplaceSafeString(body, "exceptionId", exc_bp->GetFilter());
EmplaceSafeString(body, "description", exc_bp->GetLabel());
} else {
body.try_emplace("exceptionId", "exception");
}
} else {
response.exceptionId = "exception";
}
} break;
default:
response.exceptionId = "exception";
}

lldb::SBStream stream;
if (response.description.empty()) {
if (thread.GetStopDescription(stream)) {
response.description = {stream.GetData(), stream.GetSize()};
body.try_emplace("exceptionId", "exception");
}
}

if (lldb::SBValue exception = thread.GetCurrentException()) {
stream.Clear();
response.details = ExceptionDetails{};
if (exception.GetDescription(stream)) {
response.details->message = {stream.GetData(), stream.GetSize()};
if (!ObjectContainsKey(body, "description")) {
char description[1024];
if (thread.GetStopDescription(description, sizeof(description))) {
EmplaceSafeString(body, "description", description);
}
}
body.try_emplace("breakMode", "always");
auto exception = thread.GetCurrentException();
if (exception.IsValid()) {
llvm::json::Object details;
lldb::SBStream stream;
if (exception.GetDescription(stream)) {
EmplaceSafeString(details, "message", stream.GetData());
}

if (lldb::SBThread exception_backtrace =
thread.GetCurrentExceptionBacktrace()) {
stream.Clear();
exception_backtrace.GetDescription(stream);

for (uint32_t idx = 0; idx < exception_backtrace.GetNumFrames(); idx++) {
lldb::SBFrame frame = exception_backtrace.GetFrameAtIndex(idx);
frame.GetDescription(stream);
auto exceptionBacktrace = thread.GetCurrentExceptionBacktrace();
if (exceptionBacktrace.IsValid()) {
lldb::SBStream stream;
exceptionBacktrace.GetDescription(stream);
for (uint32_t i = 0; i < exceptionBacktrace.GetNumFrames(); i++) {
lldb::SBFrame frame = exceptionBacktrace.GetFrameAtIndex(i);
frame.GetDescription(stream);
}
EmplaceSafeString(details, "stackTrace", stream.GetData());
}
response.details->stackTrace = {stream.GetData(), stream.GetSize()};

body.try_emplace("details", std::move(details));
}
// auto excInfoCount = thread.GetStopReasonDataCount();
// for (auto i=0; i<excInfoCount; ++i) {
// uint64_t exc_data = thread.GetStopReasonDataAtIndex(i);
// }
} else {
response["success"] = llvm::json::Value(false);
}
return response;
response.try_emplace("body", std::move(body));
dap.SendJSON(llvm::json::Value(std::move(response)));
}
} // namespace lldb_dap
10 changes: 3 additions & 7 deletions lldb/tools/lldb-dap/Handler/RequestHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -302,18 +302,14 @@ class EvaluateRequestHandler : public LegacyRequestHandler {
}
};

class ExceptionInfoRequestHandler final
: public RequestHandler<
protocol::ExceptionInfoArguments,
llvm::Expected<protocol::ExceptionInfoResponseBody>> {
class ExceptionInfoRequestHandler : public LegacyRequestHandler {
public:
using RequestHandler::RequestHandler;
using LegacyRequestHandler::LegacyRequestHandler;
static llvm::StringLiteral GetCommand() { return "exceptionInfo"; }
FeatureSet GetSupportedFeatures() const override {
return {protocol::eAdapterFeatureExceptionInfoRequest};
}
llvm::Expected<protocol::ExceptionInfoResponseBody>
Run(const protocol::ExceptionInfoArguments &args) const override;
void operator()(const llvm::json::Object &request) const override;
};

class InitializeRequestHandler
Expand Down
18 changes: 0 additions & 18 deletions lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -625,22 +625,4 @@ llvm::json::Value toJSON(const ModuleSymbolsResponseBody &DGMSR) {
return result;
}

bool fromJSON(const json::Value &Params, ExceptionInfoArguments &Args,
json::Path Path) {
json::ObjectMapper O(Params, Path);
return O && O.map("threadId", Args.threadId);
}

json::Value toJSON(const ExceptionInfoResponseBody &ERB) {
json::Object result{{"exceptionId", ERB.exceptionId},
{"breakMode", ERB.breakMode}};

if (!ERB.description.empty())
result.insert({"description", ERB.description.c_str()});
if (ERB.details.has_value())
result.insert({"details", *ERB.details});

return result;
}

} // namespace lldb_dap::protocol
22 changes: 0 additions & 22 deletions lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -1039,28 +1039,6 @@ struct ModuleSymbolsResponseBody {
};
llvm::json::Value toJSON(const ModuleSymbolsResponseBody &);

struct ExceptionInfoArguments {
/// Thread for which exception information should be retrieved.
lldb::tid_t threadId = LLDB_INVALID_THREAD_ID;
};
bool fromJSON(const llvm::json::Value &, ExceptionInfoArguments &,
llvm::json::Path);

struct ExceptionInfoResponseBody {
/// ID of the exception that was thrown.
std::string exceptionId;

/// Descriptive text for the exception.
std::string description;

/// Mode that caused the exception notification to be raised.
ExceptionBreakMode breakMode = eExceptionBreakModeNever;

/// Detailed information about the exception.
std::optional<ExceptionDetails> details;
};
llvm::json::Value toJSON(const ExceptionInfoResponseBody &);

} // namespace lldb_dap::protocol

#endif
33 changes: 0 additions & 33 deletions lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1136,37 +1136,4 @@ bool fromJSON(const json::Value &Param, Variable &V, json::Path Path) {
Path, /*required=*/false);
}

json::Value toJSON(const ExceptionBreakMode Mode) {
switch (Mode) {
case eExceptionBreakModeNever:
return "never";
case eExceptionBreakModeAlways:
return "always";
case eExceptionBreakModeUnhandled:
return "unhandled";
case eExceptionBreakModeUserUnhandled:
return "userUnhandled";
}
llvm_unreachable("unhandled exception breakMode.");
}

json::Value toJSON(const ExceptionDetails &ED) {
json::Object result;

if (!ED.message.empty())
result.insert({"message", ED.message});
if (!ED.typeName.empty())
result.insert({"typeName", ED.typeName});
if (!ED.fullTypeName.empty())
result.insert({"fullTypeName", ED.fullTypeName});
if (!ED.evaluateName.empty())
result.insert({"evaluateName", ED.evaluateName});
if (!ED.stackTrace.empty())
result.insert({"stackTrace", ED.stackTrace});
if (!ED.innerException.empty())
result.insert({"innerException", ED.innerException});

return result;
}

} // namespace lldb_dap::protocol
30 changes: 0 additions & 30 deletions lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1007,36 +1007,6 @@ struct Variable {
llvm::json::Value toJSON(const Variable &);
bool fromJSON(const llvm::json::Value &, Variable &, llvm::json::Path);

enum ExceptionBreakMode : unsigned {
eExceptionBreakModeNever,
eExceptionBreakModeAlways,
eExceptionBreakModeUnhandled,
eExceptionBreakModeUserUnhandled,
};
llvm::json::Value toJSON(ExceptionBreakMode);

struct ExceptionDetails {
/// Message contained in the exception.
std::string message;

/// Short type name of the exception object.
std::string typeName;

/// Fully-qualified type name of the exception object.
std::string fullTypeName;

/// An expression that can be evaluated in the current scope to obtain the
/// exception object.
std::string evaluateName;

/// Stack trace at the time the exception was thrown.
std::string stackTrace;

/// Details of the exception contained by this exception, if any.
std::vector<ExceptionDetails> innerException;
};
llvm::json::Value toJSON(const ExceptionDetails &);

} // namespace lldb_dap::protocol

#endif
1 change: 0 additions & 1 deletion lldb/unittests/DAP/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ add_lldb_unittest(DAPTests
Handler/ContinueTest.cpp
JSONUtilsTest.cpp
LLDBUtilsTest.cpp
ProtocolRequestsTest.cpp
ProtocolTypesTest.cpp
ProtocolUtilsTest.cpp
TestBase.cpp
Expand Down
Loading
Loading