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
14namespace 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// }
89void 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

source code of lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp