| 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 "Handler/RequestHandler.h" |
| 12 | #include "JSONUtils.h" |
| 13 | #include "ProtocolUtils.h" |
| 14 | |
| 15 | using namespace llvm; |
| 16 | using namespace lldb_dap::protocol; |
| 17 | |
| 18 | namespace lldb_dap { |
| 19 | |
| 20 | /// Retrieves all child variables for the given variable reference. |
| 21 | /// |
| 22 | /// A filter can be used to limit the fetched children to either named or |
| 23 | /// indexed children. |
| 24 | Expected<VariablesResponseBody> |
| 25 | VariablesRequestHandler::Run(const VariablesArguments &arguments) const { |
| 26 | const uint64_t var_ref = arguments.variablesReference; |
| 27 | const uint64_t count = arguments.count; |
| 28 | const uint64_t start = arguments.start; |
| 29 | bool hex = false; |
| 30 | if (arguments.format) |
| 31 | hex = arguments.format->hex; |
| 32 | |
| 33 | std::vector<Variable> variables; |
| 34 | |
| 35 | if (lldb::SBValueList *top_scope = dap.variables.GetTopLevelScope(variablesReference: var_ref)) { |
| 36 | // variablesReference is one of our scopes, not an actual variable it is |
| 37 | // asking for the list of args, locals or globals. |
| 38 | int64_t start_idx = 0; |
| 39 | int64_t num_children = 0; |
| 40 | |
| 41 | if (var_ref == VARREF_REGS) { |
| 42 | // Change the default format of any pointer sized registers in the first |
| 43 | // register set to be the lldb::eFormatAddressInfo so we show the pointer |
| 44 | // and resolve what the pointer resolves to. Only change the format if the |
| 45 | // format was set to the default format or if it was hex as some registers |
| 46 | // have formats set for them. |
| 47 | const uint32_t addr_size = dap.target.GetProcess().GetAddressByteSize(); |
| 48 | lldb::SBValue reg_set = dap.variables.registers.GetValueAtIndex(idx: 0); |
| 49 | const uint32_t num_regs = reg_set.GetNumChildren(); |
| 50 | for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) { |
| 51 | lldb::SBValue reg = reg_set.GetChildAtIndex(idx: reg_idx); |
| 52 | const lldb::Format format = reg.GetFormat(); |
| 53 | if (format == lldb::eFormatDefault || format == lldb::eFormatHex) { |
| 54 | if (reg.GetByteSize() == addr_size) |
| 55 | reg.SetFormat(lldb::eFormatAddressInfo); |
| 56 | } |
| 57 | } |
| 58 | } |
| 59 | |
| 60 | num_children = top_scope->GetSize(); |
| 61 | if (num_children == 0 && var_ref == VARREF_LOCALS) { |
| 62 | // Check for an error in the SBValueList that might explain why we don't |
| 63 | // have locals. If we have an error display it as the sole value in the |
| 64 | // the locals. |
| 65 | |
| 66 | // "error" owns the error string so we must keep it alive as long as we |
| 67 | // want to use the returns "const char *" |
| 68 | lldb::SBError error = top_scope->GetError(); |
| 69 | const char *var_err = error.GetCString(); |
| 70 | if (var_err) { |
| 71 | // Create a fake variable named "error" to explain why variables were |
| 72 | // not available. This new error will help let users know when there was |
| 73 | // a problem that kept variables from being available for display and |
| 74 | // allow users to fix this issue instead of seeing no variables. The |
| 75 | // errors are only set when there is a problem that the user could |
| 76 | // fix, so no error will show up when you have no debug info, only when |
| 77 | // we do have debug info and something that is fixable can be done. |
| 78 | Variable var; |
| 79 | var.name = "<error>" ; |
| 80 | var.type = "const char *" ; |
| 81 | var.value = var_err; |
| 82 | variables.emplace_back(args&: var); |
| 83 | } |
| 84 | } |
| 85 | const int64_t end_idx = start_idx + ((count == 0) ? num_children : count); |
| 86 | |
| 87 | // We first find out which variable names are duplicated |
| 88 | std::map<std::string, int> variable_name_counts; |
| 89 | for (auto i = start_idx; i < end_idx; ++i) { |
| 90 | lldb::SBValue variable = top_scope->GetValueAtIndex(idx: i); |
| 91 | if (!variable.IsValid()) |
| 92 | break; |
| 93 | variable_name_counts[GetNonNullVariableName(value&: variable)]++; |
| 94 | } |
| 95 | |
| 96 | // Show return value if there is any ( in the local top frame ) |
| 97 | if (var_ref == VARREF_LOCALS) { |
| 98 | auto process = dap.target.GetProcess(); |
| 99 | auto selected_thread = process.GetSelectedThread(); |
| 100 | lldb::SBValue stop_return_value = selected_thread.GetStopReturnValue(); |
| 101 | |
| 102 | if (stop_return_value.IsValid() && |
| 103 | (selected_thread.GetSelectedFrame().GetFrameID() == 0)) { |
| 104 | auto renamed_return_value = stop_return_value.Clone(new_name: "(Return Value)" ); |
| 105 | int64_t return_var_ref = 0; |
| 106 | |
| 107 | if (stop_return_value.MightHaveChildren() || |
| 108 | stop_return_value.IsSynthetic()) { |
| 109 | return_var_ref = dap.variables.InsertVariable(variable: stop_return_value, |
| 110 | /*is_permanent=*/false); |
| 111 | } |
| 112 | variables.emplace_back(args: CreateVariable( |
| 113 | v: renamed_return_value, var_ref: return_var_ref, format_hex: hex, |
| 114 | auto_variable_summaries: dap.configuration.enableAutoVariableSummaries, |
| 115 | synthetic_child_debugging: dap.configuration.enableSyntheticChildDebugging, is_name_duplicated: false)); |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | // Now we construct the result with unique display variable names |
| 120 | for (auto i = start_idx; i < end_idx; ++i) { |
| 121 | lldb::SBValue variable = top_scope->GetValueAtIndex(idx: i); |
| 122 | |
| 123 | if (!variable.IsValid()) |
| 124 | break; |
| 125 | |
| 126 | const int64_t frame_var_ref = |
| 127 | dap.variables.InsertVariable(variable, /*is_permanent=*/false); |
| 128 | variables.emplace_back(args: CreateVariable( |
| 129 | v: variable, var_ref: frame_var_ref, format_hex: hex, |
| 130 | auto_variable_summaries: dap.configuration.enableAutoVariableSummaries, |
| 131 | synthetic_child_debugging: dap.configuration.enableSyntheticChildDebugging, |
| 132 | is_name_duplicated: variable_name_counts[GetNonNullVariableName(value&: variable)] > 1)); |
| 133 | } |
| 134 | } else { |
| 135 | // We are expanding a variable that has children, so we will return its |
| 136 | // children. |
| 137 | lldb::SBValue variable = dap.variables.GetVariable(var_ref); |
| 138 | if (variable.IsValid()) { |
| 139 | const bool is_permanent = |
| 140 | dap.variables.IsPermanentVariableReference(var_ref); |
| 141 | auto addChild = [&](lldb::SBValue child, |
| 142 | std::optional<std::string> custom_name = {}) { |
| 143 | if (!child.IsValid()) |
| 144 | return; |
| 145 | const int64_t child_var_ref = |
| 146 | dap.variables.InsertVariable(variable: child, is_permanent); |
| 147 | variables.emplace_back( |
| 148 | args: CreateVariable(v: child, var_ref: child_var_ref, format_hex: hex, |
| 149 | auto_variable_summaries: dap.configuration.enableAutoVariableSummaries, |
| 150 | synthetic_child_debugging: dap.configuration.enableSyntheticChildDebugging, |
| 151 | /*is_name_duplicated=*/false, custom_name)); |
| 152 | }; |
| 153 | const int64_t num_children = variable.GetNumChildren(); |
| 154 | const int64_t end_idx = start + ((count == 0) ? num_children : count); |
| 155 | int64_t i = start; |
| 156 | for (; i < end_idx && i < num_children; ++i) |
| 157 | addChild(variable.GetChildAtIndex(idx: i)); |
| 158 | |
| 159 | // If we haven't filled the count quota from the request, we insert a new |
| 160 | // "[raw]" child that can be used to inspect the raw version of a |
| 161 | // synthetic member. That eliminates the need for the user to go to the |
| 162 | // debug console and type `frame var <variable> to get these values. |
| 163 | if (dap.configuration.enableSyntheticChildDebugging && |
| 164 | variable.IsSynthetic() && i == num_children) |
| 165 | addChild(variable.GetNonSyntheticValue(), "[raw]" ); |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | return VariablesResponseBody{.variables: variables}; |
| 170 | } |
| 171 | |
| 172 | } // namespace lldb_dap |
| 173 | |