1 | //===-- VariablesRequestHandler.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 | |
14 | namespace lldb_dap { |
15 | |
16 | // "VariablesRequest": { |
17 | // "allOf": [ { "$ref": "#/definitions/Request" }, { |
18 | // "type": "object", |
19 | // "description": "Variables request; value of command field is 'variables'. |
20 | // Retrieves all child variables for the given variable reference. An |
21 | // optional filter can be used to limit the fetched children to either named |
22 | // or indexed children.", "properties": { |
23 | // "command": { |
24 | // "type": "string", |
25 | // "enum": [ "variables" ] |
26 | // }, |
27 | // "arguments": { |
28 | // "$ref": "#/definitions/VariablesArguments" |
29 | // } |
30 | // }, |
31 | // "required": [ "command", "arguments" ] |
32 | // }] |
33 | // }, |
34 | // "VariablesArguments": { |
35 | // "type": "object", |
36 | // "description": "Arguments for 'variables' request.", |
37 | // "properties": { |
38 | // "variablesReference": { |
39 | // "type": "integer", |
40 | // "description": "The Variable reference." |
41 | // }, |
42 | // "filter": { |
43 | // "type": "string", |
44 | // "enum": [ "indexed", "named" ], |
45 | // "description": "Optional filter to limit the child variables to either |
46 | // named or indexed. If ommited, both types are fetched." |
47 | // }, |
48 | // "start": { |
49 | // "type": "integer", |
50 | // "description": "The index of the first variable to return; if omitted |
51 | // children start at 0." |
52 | // }, |
53 | // "count": { |
54 | // "type": "integer", |
55 | // "description": "The number of variables to return. If count is missing |
56 | // or 0, all variables are returned." |
57 | // }, |
58 | // "format": { |
59 | // "$ref": "#/definitions/ValueFormat", |
60 | // "description": "Specifies details on how to format the Variable |
61 | // values." |
62 | // } |
63 | // }, |
64 | // "required": [ "variablesReference" ] |
65 | // }, |
66 | // "VariablesResponse": { |
67 | // "allOf": [ { "$ref": "#/definitions/Response" }, { |
68 | // "type": "object", |
69 | // "description": "Response to 'variables' request.", |
70 | // "properties": { |
71 | // "body": { |
72 | // "type": "object", |
73 | // "properties": { |
74 | // "variables": { |
75 | // "type": "array", |
76 | // "items": { |
77 | // "$ref": "#/definitions/Variable" |
78 | // }, |
79 | // "description": "All (or a range) of variables for the given |
80 | // variable reference." |
81 | // } |
82 | // }, |
83 | // "required": [ "variables" ] |
84 | // } |
85 | // }, |
86 | // "required": [ "body" ] |
87 | // }] |
88 | // } |
89 | void VariablesRequestHandler::operator()( |
90 | const llvm::json::Object &request) const { |
91 | llvm::json::Object response; |
92 | FillResponse(request, response); |
93 | llvm::json::Array variables; |
94 | const auto *arguments = request.getObject(K: "arguments" ); |
95 | const auto variablesReference = |
96 | GetInteger<uint64_t>(obj: arguments, key: "variablesReference" ).value_or(u: 0); |
97 | const auto start = GetInteger<int64_t>(obj: arguments, key: "start" ).value_or(u: 0); |
98 | const auto count = GetInteger<int64_t>(obj: arguments, key: "count" ).value_or(u: 0); |
99 | bool hex = false; |
100 | const auto *format = arguments->getObject(K: "format" ); |
101 | if (format) |
102 | hex = GetBoolean(obj: format, key: "hex" ).value_or(u: false); |
103 | |
104 | if (lldb::SBValueList *top_scope = |
105 | dap.variables.GetTopLevelScope(variablesReference)) { |
106 | // variablesReference is one of our scopes, not an actual variable it is |
107 | // asking for the list of args, locals or globals. |
108 | int64_t start_idx = 0; |
109 | int64_t num_children = 0; |
110 | |
111 | if (variablesReference == VARREF_REGS) { |
112 | // Change the default format of any pointer sized registers in the first |
113 | // register set to be the lldb::eFormatAddressInfo so we show the pointer |
114 | // and resolve what the pointer resolves to. Only change the format if the |
115 | // format was set to the default format or if it was hex as some registers |
116 | // have formats set for them. |
117 | const uint32_t addr_size = dap.target.GetProcess().GetAddressByteSize(); |
118 | lldb::SBValue reg_set = dap.variables.registers.GetValueAtIndex(idx: 0); |
119 | const uint32_t num_regs = reg_set.GetNumChildren(); |
120 | for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) { |
121 | lldb::SBValue reg = reg_set.GetChildAtIndex(idx: reg_idx); |
122 | const lldb::Format format = reg.GetFormat(); |
123 | if (format == lldb::eFormatDefault || format == lldb::eFormatHex) { |
124 | if (reg.GetByteSize() == addr_size) |
125 | reg.SetFormat(lldb::eFormatAddressInfo); |
126 | } |
127 | } |
128 | } |
129 | |
130 | num_children = top_scope->GetSize(); |
131 | if (num_children == 0 && variablesReference == VARREF_LOCALS) { |
132 | // Check for an error in the SBValueList that might explain why we don't |
133 | // have locals. If we have an error display it as the sole value in the |
134 | // the locals. |
135 | |
136 | // "error" owns the error string so we must keep it alive as long as we |
137 | // want to use the returns "const char *" |
138 | lldb::SBError error = top_scope->GetError(); |
139 | const char *var_err = error.GetCString(); |
140 | if (var_err) { |
141 | // Create a fake variable named "error" to explain why variables were |
142 | // not available. This new error will help let users know when there was |
143 | // a problem that kept variables from being available for display and |
144 | // allow users to fix this issue instead of seeing no variables. The |
145 | // errors are only set when there is a problem that the user could |
146 | // fix, so no error will show up when you have no debug info, only when |
147 | // we do have debug info and something that is fixable can be done. |
148 | llvm::json::Object object; |
149 | EmplaceSafeString(obj&: object, key: "name" , str: "<error>" ); |
150 | EmplaceSafeString(obj&: object, key: "type" , str: "const char *" ); |
151 | EmplaceSafeString(obj&: object, key: "value" , str: var_err); |
152 | object.try_emplace(K: "variablesReference" , Args: (int64_t)0); |
153 | variables.emplace_back(A: std::move(object)); |
154 | } |
155 | } |
156 | const int64_t end_idx = start_idx + ((count == 0) ? num_children : count); |
157 | |
158 | // We first find out which variable names are duplicated |
159 | std::map<std::string, int> variable_name_counts; |
160 | for (auto i = start_idx; i < end_idx; ++i) { |
161 | lldb::SBValue variable = top_scope->GetValueAtIndex(idx: i); |
162 | if (!variable.IsValid()) |
163 | break; |
164 | variable_name_counts[GetNonNullVariableName(value&: variable)]++; |
165 | } |
166 | |
167 | // Show return value if there is any ( in the local top frame ) |
168 | if (variablesReference == VARREF_LOCALS) { |
169 | auto process = dap.target.GetProcess(); |
170 | auto selected_thread = process.GetSelectedThread(); |
171 | lldb::SBValue stop_return_value = selected_thread.GetStopReturnValue(); |
172 | |
173 | if (stop_return_value.IsValid() && |
174 | (selected_thread.GetSelectedFrame().GetFrameID() == 0)) { |
175 | auto renamed_return_value = stop_return_value.Clone(new_name: "(Return Value)" ); |
176 | int64_t return_var_ref = 0; |
177 | |
178 | if (stop_return_value.MightHaveChildren() || |
179 | stop_return_value.IsSynthetic()) { |
180 | return_var_ref = dap.variables.InsertVariable(variable: stop_return_value, |
181 | /*is_permanent=*/false); |
182 | } |
183 | variables.emplace_back(A: CreateVariable( |
184 | v: renamed_return_value, var_ref: return_var_ref, format_hex: hex, |
185 | auto_variable_summaries: dap.configuration.enableAutoVariableSummaries, |
186 | synthetic_child_debugging: dap.configuration.enableSyntheticChildDebugging, is_name_duplicated: false)); |
187 | } |
188 | } |
189 | |
190 | // Now we construct the result with unique display variable names |
191 | for (auto i = start_idx; i < end_idx; ++i) { |
192 | lldb::SBValue variable = top_scope->GetValueAtIndex(idx: i); |
193 | |
194 | if (!variable.IsValid()) |
195 | break; |
196 | |
197 | int64_t var_ref = |
198 | dap.variables.InsertVariable(variable, /*is_permanent=*/false); |
199 | variables.emplace_back(A: CreateVariable( |
200 | v: variable, var_ref, format_hex: hex, auto_variable_summaries: dap.configuration.enableAutoVariableSummaries, |
201 | synthetic_child_debugging: dap.configuration.enableSyntheticChildDebugging, |
202 | is_name_duplicated: variable_name_counts[GetNonNullVariableName(value&: variable)] > 1)); |
203 | } |
204 | } else { |
205 | // We are expanding a variable that has children, so we will return its |
206 | // children. |
207 | lldb::SBValue variable = dap.variables.GetVariable(var_ref: variablesReference); |
208 | if (variable.IsValid()) { |
209 | auto addChild = [&](lldb::SBValue child, |
210 | std::optional<std::string> custom_name = {}) { |
211 | if (!child.IsValid()) |
212 | return; |
213 | bool is_permanent = |
214 | dap.variables.IsPermanentVariableReference(var_ref: variablesReference); |
215 | int64_t var_ref = dap.variables.InsertVariable(variable: child, is_permanent); |
216 | variables.emplace_back(A: CreateVariable( |
217 | v: child, var_ref, format_hex: hex, auto_variable_summaries: dap.configuration.enableAutoVariableSummaries, |
218 | synthetic_child_debugging: dap.configuration.enableSyntheticChildDebugging, |
219 | /*is_name_duplicated=*/false, custom_name)); |
220 | }; |
221 | const int64_t num_children = variable.GetNumChildren(); |
222 | int64_t end_idx = start + ((count == 0) ? num_children : count); |
223 | int64_t i = start; |
224 | for (; i < end_idx && i < num_children; ++i) |
225 | addChild(variable.GetChildAtIndex(idx: i)); |
226 | |
227 | // If we haven't filled the count quota from the request, we insert a new |
228 | // "[raw]" child that can be used to inspect the raw version of a |
229 | // synthetic member. That eliminates the need for the user to go to the |
230 | // debug console and type `frame var <variable> to get these values. |
231 | if (dap.configuration.enableSyntheticChildDebugging && |
232 | variable.IsSynthetic() && i == num_children) |
233 | addChild(variable.GetNonSyntheticValue(), "[raw]" ); |
234 | } |
235 | } |
236 | llvm::json::Object body; |
237 | body.try_emplace(K: "variables" , Args: std::move(variables)); |
238 | response.try_emplace(K: "body" , Args: std::move(body)); |
239 | dap.SendJSON(json: llvm::json::Value(std::move(response))); |
240 | } |
241 | |
242 | } // namespace lldb_dap |
243 | |