1 | //===-- ExceptionInfoRequestHandler.cpp -----------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "DAP.h" |
10 | #include "EventHelper.h" |
11 | #include "JSONUtils.h" |
12 | #include "RequestHandler.h" |
13 | #include "lldb/API/SBStream.h" |
14 | |
15 | namespace lldb_dap { |
16 | |
17 | // "ExceptionInfoRequest": { |
18 | // "allOf": [ { "$ref": "#/definitions/Request" }, { |
19 | // "type": "object", |
20 | // "description": "Retrieves the details of the exception that |
21 | // caused this event to be raised. Clients should only call this request if |
22 | // the corresponding capability `supportsExceptionInfoRequest` is true.", |
23 | // "properties": { |
24 | // "command": { |
25 | // "type": "string", |
26 | // "enum": [ "exceptionInfo" ] |
27 | // }, |
28 | // "arguments": { |
29 | // "$ref": "#/definitions/ExceptionInfoArguments" |
30 | // } |
31 | // }, |
32 | // "required": [ "command", "arguments" ] |
33 | // }] |
34 | // }, |
35 | // "ExceptionInfoArguments": { |
36 | // "type": "object", |
37 | // "description": "Arguments for `exceptionInfo` request.", |
38 | // "properties": { |
39 | // "threadId": { |
40 | // "type": "integer", |
41 | // "description": "Thread for which exception information should be |
42 | // retrieved." |
43 | // } |
44 | // }, |
45 | // "required": [ "threadId" ] |
46 | // }, |
47 | // "ExceptionInfoResponse": { |
48 | // "allOf": [ { "$ref": "#/definitions/Response" }, { |
49 | // "type": "object", |
50 | // "description": "Response to `exceptionInfo` request.", |
51 | // "properties": { |
52 | // "body": { |
53 | // "type": "object", |
54 | // "properties": { |
55 | // "exceptionId": { |
56 | // "type": "string", |
57 | // "description": "ID of the exception that was thrown." |
58 | // }, |
59 | // "description": { |
60 | // "type": "string", |
61 | // "description": "Descriptive text for the exception." |
62 | // }, |
63 | // "breakMode": { |
64 | // "$ref": "#/definitions/ExceptionBreakMode", |
65 | // "description": "Mode that caused the exception notification to |
66 | // be raised." |
67 | // }, |
68 | // "details": { |
69 | // "$ref": "#/definitions/ExceptionDetails", |
70 | // "description": "Detailed information about the exception." |
71 | // } |
72 | // }, |
73 | // "required": [ "exceptionId", "breakMode" ] |
74 | // } |
75 | // }, |
76 | // "required": [ "body" ] |
77 | // }] |
78 | // } |
79 | // "ExceptionDetails": { |
80 | // "type": "object", |
81 | // "description": "Detailed information about an exception that has |
82 | // occurred.", "properties": { |
83 | // "message": { |
84 | // "type": "string", |
85 | // "description": "Message contained in the exception." |
86 | // }, |
87 | // "typeName": { |
88 | // "type": "string", |
89 | // "description": "Short type name of the exception object." |
90 | // }, |
91 | // "fullTypeName": { |
92 | // "type": "string", |
93 | // "description": "Fully-qualified type name of the exception object." |
94 | // }, |
95 | // "evaluateName": { |
96 | // "type": "string", |
97 | // "description": "An expression that can be evaluated in the current |
98 | // scope to obtain the exception object." |
99 | // }, |
100 | // "stackTrace": { |
101 | // "type": "string", |
102 | // "description": "Stack trace at the time the exception was thrown." |
103 | // }, |
104 | // "innerException": { |
105 | // "type": "array", |
106 | // "items": { |
107 | // "$ref": "#/definitions/ExceptionDetails" |
108 | // }, |
109 | // "description": "Details of the exception contained by this exception, |
110 | // if any." |
111 | // } |
112 | // } |
113 | // }, |
114 | void ExceptionInfoRequestHandler::operator()( |
115 | const llvm::json::Object &request) const { |
116 | llvm::json::Object response; |
117 | FillResponse(request, response); |
118 | const auto *arguments = request.getObject(K: "arguments" ); |
119 | llvm::json::Object body; |
120 | lldb::SBThread thread = dap.GetLLDBThread(arguments: *arguments); |
121 | if (thread.IsValid()) { |
122 | auto stopReason = thread.GetStopReason(); |
123 | if (stopReason == lldb::eStopReasonSignal) |
124 | body.try_emplace(K: "exceptionId" , Args: "signal" ); |
125 | else if (stopReason == lldb::eStopReasonBreakpoint) { |
126 | ExceptionBreakpoint *exc_bp = dap.GetExceptionBPFromStopReason(thread); |
127 | if (exc_bp) { |
128 | EmplaceSafeString(obj&: body, key: "exceptionId" , str: exc_bp->GetFilter()); |
129 | EmplaceSafeString(obj&: body, key: "description" , str: exc_bp->GetLabel()); |
130 | } else { |
131 | body.try_emplace(K: "exceptionId" , Args: "exception" ); |
132 | } |
133 | } else { |
134 | body.try_emplace(K: "exceptionId" , Args: "exception" ); |
135 | } |
136 | if (!ObjectContainsKey(obj: body, key: "description" )) { |
137 | char description[1024]; |
138 | if (thread.GetStopDescription(dst_or_null: description, dst_len: sizeof(description))) { |
139 | EmplaceSafeString(obj&: body, key: "description" , str: description); |
140 | } |
141 | } |
142 | body.try_emplace(K: "breakMode" , Args: "always" ); |
143 | auto exception = thread.GetCurrentException(); |
144 | if (exception.IsValid()) { |
145 | llvm::json::Object details; |
146 | lldb::SBStream stream; |
147 | if (exception.GetDescription(description&: stream)) { |
148 | EmplaceSafeString(obj&: details, key: "message" , str: stream.GetData()); |
149 | } |
150 | |
151 | auto exceptionBacktrace = thread.GetCurrentExceptionBacktrace(); |
152 | if (exceptionBacktrace.IsValid()) { |
153 | lldb::SBStream stream; |
154 | exceptionBacktrace.GetDescription(description&: stream); |
155 | for (uint32_t i = 0; i < exceptionBacktrace.GetNumFrames(); i++) { |
156 | lldb::SBFrame frame = exceptionBacktrace.GetFrameAtIndex(idx: i); |
157 | frame.GetDescription(description&: stream); |
158 | } |
159 | EmplaceSafeString(obj&: details, key: "stackTrace" , str: stream.GetData()); |
160 | } |
161 | |
162 | body.try_emplace(K: "details" , Args: std::move(details)); |
163 | } |
164 | // auto excInfoCount = thread.GetStopReasonDataCount(); |
165 | // for (auto i=0; i<excInfoCount; ++i) { |
166 | // uint64_t exc_data = thread.GetStopReasonDataAtIndex(i); |
167 | // } |
168 | } else { |
169 | response["success" ] = llvm::json::Value(false); |
170 | } |
171 | response.try_emplace(K: "body" , Args: std::move(body)); |
172 | dap.SendJSON(json: llvm::json::Value(std::move(response))); |
173 | } |
174 | } // namespace lldb_dap |
175 | |