================
@@ -51,155 +59,310 @@ protocol::Scope CreateScope(const ScopeKind kind, int64_t
variablesReference,
return scope;
}
-std::optional<ScopeData>
-Variables::GetTopLevelScope(int64_t variablesReference) {
- auto scope_kind_iter = m_scope_kinds.find(variablesReference);
- if (scope_kind_iter == m_scope_kinds.end())
- return std::nullopt;
+std::vector<Variable>
+ScopeStore::GetVariables(VariableReferenceStorage &storage,
+ const Configuration &config,
+ const VariablesArguments &args) {
+ LoadVariables();
+ if (m_kind == lldb_dap::eScopeKindRegisters)
+ SetRegistersFormat();
- ScopeKind scope_kind = scope_kind_iter->second.first;
- uint64_t dap_frame_id = scope_kind_iter->second.second;
+ const bool format_hex = args.format ? args.format->hex : false;
+ std::vector<Variable> variables;
+ if (m_kind == eScopeKindLocals)
+ AddReturnValue(storage, config, variables, format_hex);
- auto frame_iter = m_frames.find(dap_frame_id);
- if (frame_iter == m_frames.end())
- return std::nullopt;
+ const uint64_t count = args.count;
+ const uint32_t start_idx = 0;
+ const uint32_t num_children = m_children.GetSize();
+ const uint32_t end_idx = start_idx + ((count == 0) ? num_children : count);
- lldb::SBValueList *scope = frame_iter->second.GetScope(scope_kind);
- if (scope == nullptr)
- return std::nullopt;
+ // We first find out which variable names are duplicated.
+ std::map<llvm::StringRef, uint32_t> variable_name_counts;
+ for (auto i = start_idx; i < end_idx; ++i) {
+ lldb::SBValue variable = m_children.GetValueAtIndex(i);
+ if (!variable.IsValid())
+ break;
+ variable_name_counts[GetNonNullVariableName(variable)]++;
+ }
- ScopeData scope_data;
- scope_data.kind = scope_kind;
- scope_data.scope = *scope;
- return scope_data;
-}
+ // Now we construct the result with unique display variable names.
+ for (auto i = start_idx; i < end_idx; ++i) {
+ lldb::SBValue variable = m_children.GetValueAtIndex(i);
+
+ if (!variable.IsValid())
+ break;
+
+ const var_ref_t frame_var_ref =
+ storage.InsertVariable(variable, /*is_permanent=*/false);
+ if (LLVM_UNLIKELY(frame_var_ref.AsUInt32() >=
+ var_ref_t::k_variables_reference_threshold)) {
+ DAP_LOG(storage.log,
+ "warning: scopes variablesReference threshold is reached. "
+ "current: {} threshold: {}, maximum {}\n",
+ frame_var_ref.AsUInt32(),
+ var_ref_t::k_variables_reference_threshold,
+ var_ref_t::k_max_variables_references);
+ }
-void Variables::Clear() {
- m_referencedvariables.clear();
- m_scope_kinds.clear();
- m_frames.clear();
- m_next_temporary_var_ref = TemporaryVariableStartIndex;
+ if (LLVM_UNLIKELY(frame_var_ref.Kind() == eReferenceKindInvalid))
+ break;
+
+ variables.emplace_back(CreateVariable(
+ variable, frame_var_ref, format_hex,
config.enableAutoVariableSummaries,
+ config.enableSyntheticChildDebugging,
+ variable_name_counts[GetNonNullVariableName(variable)] > 1));
+ }
+ return variables;
}
-int64_t Variables::GetNewVariableReference(bool is_permanent) {
- if (is_permanent)
- return m_next_permanent_var_ref++;
- return m_next_temporary_var_ref++;
+lldb::SBValue ScopeStore::FindVariable(llvm::StringRef name) {
+ LoadVariables();
+
+ lldb::SBValue variable;
+ const bool is_name_duplicated = name.contains(" @");
+ // variablesReference is one of our scopes, not an actual variable it is
+ // asking for a variable in locals or globals or registers.
+ const uint32_t end_idx = m_children.GetSize();
+ // Searching backward so that we choose the variable in closest scope
+ // among variables of the same name.
+ for (const uint32_t i : llvm::reverse(llvm::seq<uint32_t>(0, end_idx))) {
+ lldb::SBValue curr_variable = m_children.GetValueAtIndex(i);
+ std::string variable_name =
+ CreateUniqueVariableNameForDisplay(curr_variable, is_name_duplicated);
+ if (variable_name == name) {
+ variable = curr_variable;
+ break;
+ }
+ }
+ return variable;
}
-bool Variables::IsPermanentVariableReference(int64_t var_ref) {
- return var_ref >= PermanentVariableStartIndex;
+void ScopeStore::LoadVariables() {
+ if (m_variables_loaded)
+ return;
+
+ m_variables_loaded = true;
+ switch (m_kind) {
+ case eScopeKindLocals:
+ m_children = m_frame.GetVariables(/*arguments=*/true,
+ /*locals=*/true,
+ /*statics=*/false,
+ /*in_scope_only=*/true);
+ break;
+ case eScopeKindGlobals:
+ m_children = m_frame.GetVariables(/*arguments=*/false,
+ /*locals=*/false,
+ /*statics=*/true,
+ /*in_scope_only=*/true);
+ break;
+ case eScopeKindRegisters:
+ m_children = m_frame.GetRegisters();
+ }
}
-lldb::SBValue Variables::GetVariable(int64_t var_ref) const {
- if (IsPermanentVariableReference(var_ref)) {
- auto pos = m_referencedpermanent_variables.find(var_ref);
- if (pos != m_referencedpermanent_variables.end())
- return pos->second;
- } else {
- auto pos = m_referencedvariables.find(var_ref);
- if (pos != m_referencedvariables.end())
- return pos->second;
+void ScopeStore::SetRegistersFormat() {
+ // Change the default format of any pointer sized registers in the first
+ // register set to be the lldb::eFormatAddressInfo so we show the pointer
+ // and resolve what the pointer resolves to. Only change the format if the
+ // format was set to the default format or if it was hex as some registers
+ // have formats set for them.
+ const uint32_t addr_size =
+ m_frame.GetThread().GetProcess().GetAddressByteSize();
+ for (lldb::SBValue reg : m_children.GetValueAtIndex(0)) {
+ const lldb::Format format = reg.GetFormat();
+ if (format == lldb::eFormatDefault || format == lldb::eFormatHex) {
+ if (reg.GetByteSize() == addr_size)
+ reg.SetFormat(lldb::eFormatAddressInfo);
+ }
}
- return lldb::SBValue();
}
-int64_t Variables::InsertVariable(lldb::SBValue variable, bool is_permanent) {
- int64_t var_ref = GetNewVariableReference(is_permanent);
- if (is_permanent)
- m_referencedpermanent_variables.insert(std::make_pair(var_ref, variable));
- else
- m_referencedvariables.insert(std::make_pair(var_ref, variable));
- return var_ref;
+void ScopeStore::AddReturnValue(VariableReferenceStorage &storage,
+ const Configuration &config,
+ std::vector<Variable> &variables,
+ bool format_hex) {
+ assert(m_kind == eScopeKindLocals &&
+ "Return Value Should only be in local scope");
+ if (m_children.GetSize() == 0) {
+ // Check for an error in the SBValueList that might explain why we don't
+ // have locals. If we have an error display it as the sole value in the
+ // the locals.
+
+ // "error" owns the error string so we must keep it alive as long as we
+ // want to use the returns "const char *".
+ lldb::SBError error = m_children.GetError();
+ if (const char *var_err = error.GetCString()) {
+ // Create a fake variable named "error" to explain why variables were
+ // not available. This new error will help let users know when there was
+ // a problem that kept variables from being available for display and
+ // allow users to fix this issue instead of seeing no variables. The
+ // errors are only set when there is a problem that the user could
+ // fix, so no error will show up when you have no debug info, only when
+ // we do have debug info and something that is fixable can be done.
+ Variable err_var;
+ err_var.name = "<error>";
+ err_var.type = "const char *";
+ err_var.value = var_err;
+ variables.push_back(std::move(err_var));
+ }
+ return;
+ }
+
+ // Show return value if there is any ( in the local top frame )
+ lldb::SBThread selected_thread = m_frame.GetThread();
+ lldb::SBValue stop_return_value = selected_thread.GetStopReturnValue();
+
+ if (stop_return_value.IsValid() &&
+ (selected_thread.GetSelectedFrame().GetFrameID() == 0)) {
+ auto renamed_return_value = stop_return_value.Clone("(Return Value)");
+ var_ref_t return_var_ref{0};
+
+ if (stop_return_value.MightHaveChildren() ||
+ stop_return_value.IsSynthetic()) {
+ return_var_ref = storage.InsertVariable(stop_return_value,
+ /*is_permanent=*/false);
+ }
+ variables.emplace_back(
+ CreateVariable(renamed_return_value, return_var_ref, format_hex,
+ config.enableAutoVariableSummaries,
+ config.enableSyntheticChildDebugging, false));
+ }
}
-lldb::SBValue Variables::FindVariable(uint64_t variablesReference,
- llvm::StringRef name) {
- lldb::SBValue variable;
- if (std::optional<ScopeData> scope_data =
- GetTopLevelScope(variablesReference)) {
- bool is_duplicated_variable_name = name.contains(" @");
- // variablesReference is one of our scopes, not an actual variable it is
- // asking for a variable in locals or globals or registers
- int64_t end_idx = scope_data->scope.GetSize();
- // Searching backward so that we choose the variable in closest scope
- // among variables of the same name.
- for (int64_t i = end_idx - 1; i >= 0; --i) {
- lldb::SBValue curr_variable = scope_data->scope.GetValueAtIndex(i);
- std::string variable_name = CreateUniqueVariableNameForDisplay(
- curr_variable, is_duplicated_variable_name);
- if (variable_name == name) {
- variable = curr_variable;
- break;
- }
+std::vector<Variable>
+ExpandableValueStore::GetVariables(VariableReferenceStorage &storage,
+ const Configuration &config,
+ const VariablesArguments &args) {
+ const var_ref_t var_ref = args.variablesReference;
+ const uint64_t count = args.count;
+ const uint64_t start = args.start;
+ const bool hex = args.format ? args.format->hex : false;
+
+ lldb::SBValue variable = storage.GetVariable(var_ref);
+ if (!variable)
+ return {};
+
+ // We are expanding a variable that has children, so we will return its
+ // children.
+ std::vector<Variable> variables;
+ const bool is_permanent = var_ref.Kind() == eReferenceKindPermanent;
+ auto addChild = [&](lldb::SBValue child,
+ std::optional<llvm::StringRef> custom_name = {}) {
+ if (!child.IsValid())
+ return;
+
+ const var_ref_t child_var_ref = storage.InsertVariable(child,
is_permanent);
+ if (LLVM_UNLIKELY(child_var_ref.AsUInt32() ==
+ var_ref_t::k_variables_reference_threshold)) {
+ DAP_LOG(storage.log,
+ "warning: {} variablesReference threshold is reached. "
+ "current: {} threshold: {}, maximum {}\n",
+ (is_permanent ? "permanent" : "temporary"),
+ child_var_ref.AsUInt32(),
+ var_ref_t::k_variables_reference_threshold,
+ var_ref_t::k_max_variables_references);
}
- } else {
- // This is not under the globals or locals scope, so there are no
- // duplicated names.
-
- // We have a named item within an actual variable so we need to find it
- // withing the container variable by name.
- lldb::SBValue container = GetVariable(variablesReference);
- variable = container.GetChildMemberWithName(name.data());
- if (!variable.IsValid()) {
- if (name.starts_with("[")) {
- llvm::StringRef index_str(name.drop_front(1));
- uint64_t index = 0;
- if (!index_str.consumeInteger(0, index)) {
- if (index_str == "]")
- variable = container.GetChildAtIndex(index);
- }
+
+ if (LLVM_UNLIKELY(child_var_ref.Kind() == eReferenceKindInvalid))
+ return;
+
+ variables.emplace_back(CreateVariable(
+ child, child_var_ref, hex, config.enableAutoVariableSummaries,
+ config.enableSyntheticChildDebugging,
+ /*is_name_duplicated=*/false, custom_name));
+ };
+
+ const uint32_t num_children = variable.GetNumChildren();
+ const uint32_t end_idx = start + ((count == 0) ? num_children : count);
+ uint32_t i = start;
+ for (; i < end_idx && i < num_children; ++i)
+ addChild(variable.GetChildAtIndex(i));
+
+ // If we haven't filled the count quota from the request, we insert a new
+ // "[raw]" child that can be used to inspect the raw version of a
+ // synthetic member. That eliminates the need for the user to go to the
+ // debug console and type `frame var <variable> to get these values.
+ if (config.enableSyntheticChildDebugging && variable.IsSynthetic() &&
+ i == num_children)
+ addChild(variable.GetNonSyntheticValue(), "[raw]");
+
+ return variables;
+}
+
+lldb::SBValue ExpandableValueStore::FindVariable(llvm::StringRef name) {
+ lldb::SBValue variable = m_value.GetChildMemberWithName(name.data());
+ if (!variable.IsValid()) {
+ if (name.starts_with('[') && name.ends_with(']')) {
+ llvm::StringRef index_str(name.drop_front().drop_back());
+ uint64_t index = 0;
+ if (!index_str.consumeInteger(0, index)) {
+ variable = m_value.GetChildAtIndex(index);
}
}
}
+
return variable;
----------------
DrSergei wrote:
Can we simplify this code
```
lldb::SBValue variable = m_value.GetChildMemberWithName(name.data());
if (variable.IsValid())
return variable;
if (!name.starts_with('[') || !name.ends_with(']'))
return {};
llvm::StringRef index_str(name.drop_front().drop_back());
uint64_t index = 0;
if (!index_str.consumeInteger(0, index))
return m_value.GetChildAtIndex(index);
return {};
```
https://github.com/llvm/llvm-project/pull/179262
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits