================
@@ -27,285 +26,394 @@
using namespace lldb_dap;
using namespace lldb_dap::protocol;
-namespace lldb_dap {
-
-protocol::Scope CreateScope(ScopeKind kind, var_ref_t variablesReference,
- bool expensive) {
- protocol::Scope scope;
- scope.variablesReference = variablesReference;
- scope.expensive = expensive;
+namespace {
+
+/// A Variable store for fetching variables within a specific scope (locals,
+/// globals, or registers) for a given stack frame.
+class ScopeStore final : public VariableStore {
+public:
+ explicit ScopeStore(ScopeKind kind, const lldb::SBFrame &frame)
+ : m_frame(frame), m_kind(kind) {}
+
+ std::vector<protocol::Variable>
+ GetVariables(VariableReferenceStorage &storage,
+ const protocol::Configuration &config,
+ const protocol::VariablesArguments &args) override {
+ LoadVariables();
+ if (m_kind == lldb_dap::eScopeKindRegisters)
+ SetRegistersFormat();
+
+ const bool format_hex = args.format ? args.format->hex : false;
+ std::vector<Variable> variables;
+ if (m_kind == eScopeKindLocals)
+ AddReturnValue(storage, config, variables, format_hex);
+
+ 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);
+
+ // 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)]++;
+ }
- // TODO: Support "arguments" and "return value" scope.
- // At the moment lldb-dap includes the arguments and return_value into the
- // "locals" scope.
- // VS Code only expands the first non-expensive scope. This causes friction
- // if we add the arguments above the local scope, as the locals scope will
not
- // be expanded if we enter a function with arguments. It becomes more
- // annoying when the scope has arguments, return_value and locals.
- switch (kind) {
- case eScopeKindLocals:
- scope.presentationHint = protocol::Scope::eScopePresentationHintLocals;
- scope.name = "Locals";
- break;
- case eScopeKindGlobals:
- scope.name = "Globals";
- break;
- case eScopeKindRegisters:
- scope.presentationHint = protocol::Scope::eScopePresentationHintRegisters;
- scope.name = "Registers";
- break;
+ // 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.Insert(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 reached. "
+ "current: {} threshold: {}, maximum {}.",
+ frame_var_ref.AsUInt32(),
+ var_ref_t::k_variables_reference_threshold,
+ var_ref_t::k_max_variables_references);
+ }
+
+ 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;
}
- return scope;
-}
-
-std::vector<Variable>
-ScopeStore::GetVariables(VariableReferenceStorage &storage,
- const Configuration &config,
- const VariablesArguments &args) {
- LoadVariables();
- if (m_kind == lldb_dap::eScopeKindRegisters)
- SetRegistersFormat();
-
- const bool format_hex = args.format ? args.format->hex : false;
- std::vector<Variable> variables;
- if (m_kind == eScopeKindLocals)
- AddReturnValue(storage, config, variables, format_hex);
-
- 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);
-
- // 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)]++;
+ lldb::SBValue FindVariable(llvm::StringRef name) override {
+ 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;
}
- // 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);
+ lldb::SBValue GetVariable() const override { return lldb::SBValue(); }
- 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 reached. "
- "current: {} threshold: {}, maximum {}.",
- frame_var_ref.AsUInt32(),
- var_ref_t::k_variables_reference_threshold,
- var_ref_t::k_max_variables_references);
- }
+private:
+ void LoadVariables() {
+ if (m_variables_loaded)
+ return;
- if (LLVM_UNLIKELY(frame_var_ref.Kind() == eReferenceKindInvalid))
+ 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;
-
- variables.emplace_back(CreateVariable(
- variable, frame_var_ref, format_hex,
config.enableAutoVariableSummaries,
- config.enableSyntheticChildDebugging,
- variable_name_counts[GetNonNullVariableName(variable)] > 1));
- }
- return variables;
-}
-
-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;
+ 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();
}
}
- return variable;
-}
-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();
+ void 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);
+ }
+ }
}
-}
-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);
+ void AddReturnValue(VariableReferenceStorage &storage,
+ const protocol::Configuration &config,
+ std::vector<protocol::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;
}
- }
-}
-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));
+ // 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{var_ref_t::k_no_child};
+
+ if (stop_return_value.MightHaveChildren() ||
+ stop_return_value.IsSynthetic()) {
+ return_var_ref = storage.Insert(stop_return_value,
+ /*is_permanent=*/false);
+ }
+ variables.emplace_back(
+ CreateVariable(renamed_return_value, return_var_ref, format_hex,
+ config.enableAutoVariableSummaries,
+ config.enableSyntheticChildDebugging, false));
}
- 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();
+ lldb::SBFrame m_frame;
+ lldb::SBValueList m_children;
+ ScopeKind m_kind;
+ bool m_variables_loaded = false;
+};
+
+/// Variable store for expandable values.
+///
+/// Manages children variables of complex types (structs, arrays, pointers,
+/// etc.) that can be expanded in the debugger UI.
+class ExpandableValueStore final : public VariableStore {
+
+public:
+ explicit ExpandableValueStore(const lldb::SBValue &value) : m_value(value) {}
+
+ std::vector<protocol::Variable>
+ GetVariables(VariableReferenceStorage &storage,
+ const protocol::Configuration &config,
+ const protocol::VariablesArguments &args) override {
+ 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.Insert(child, is_permanent);
+ if (LLVM_UNLIKELY(child_var_ref.AsUInt32() ==
+ var_ref_t::k_variables_reference_threshold)) {
+ DAP_LOG(storage.log,
+ "warning: {} variablesReference threshold reached. "
+ "current: {} threshold: {}, maximum {}.",
+ (is_permanent ? "permanent" : "temporary"),
+ child_var_ref.AsUInt32(),
+ var_ref_t::k_variables_reference_threshold,
+ var_ref_t::k_max_variables_references);
+ }
+
+ if (LLVM_UNLIKELY(child_var_ref.Kind() == eReferenceKindInvalid)) {
+ DAP_LOG(storage.log,
+ "error: invalid variablesReference created for {} variable
{}.",
+ (is_permanent ? "permanent" : "temporary"),
variable.GetName());
+ 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;
+ }
- 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{var_ref_t::k_no_child};
+ lldb::SBValue FindVariable(llvm::StringRef name) override {
+ lldb::SBValue variable = m_value.GetChildMemberWithName(name.data());
+ if (variable.IsValid())
+ return variable;
- if (stop_return_value.MightHaveChildren() ||
- stop_return_value.IsSynthetic()) {
- return_var_ref = storage.InsertVariable(stop_return_value,
- /*is_permanent=*/false);
+ if (name.consume_front('[') && name.consume_back("]")) {
+ uint64_t index = 0;
+ if (!name.consumeInteger(0, index)) {
+ variable = m_value.GetChildAtIndex(index);
+ }
}
- variables.emplace_back(
- CreateVariable(renamed_return_value, return_var_ref, format_hex,
- config.enableAutoVariableSummaries,
- config.enableSyntheticChildDebugging, false));
+ return variable;
}
-}
-
-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 reached. "
- "current: {} threshold: {}, maximum {}.",
- (is_permanent ? "permanent" : "temporary"),
- child_var_ref.AsUInt32(),
- var_ref_t::k_variables_reference_threshold,
- var_ref_t::k_max_variables_references);
+ [[nodiscard]] lldb::SBValue GetVariable() const override { return m_value; };
+
+private:
+ lldb::SBValue m_value;
+};
+
+class ExpandableValueListStore final : public VariableStore {
+
+public:
+ explicit ExpandableValueListStore(const lldb::SBValueList &list)
+ : m_value_list(list) {}
+
+ std::vector<protocol::Variable>
+ GetVariables(VariableReferenceStorage &storage,
+ const protocol::Configuration &config,
+ const protocol::VariablesArguments &args) override {
+ const bool format_hex = args.format ? args.format->hex : false;
+ const uint64_t count = args.count;
+ const uint32_t start_idx = 0;
+ const uint32_t num_children = m_value_list.GetSize();
+ const uint32_t end_idx = start_idx + ((count == 0) ? num_children : count);
+ std::vector<Variable> variables;
+
+ // 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_value_list.GetValueAtIndex(i);
+ if (!variable.IsValid())
+ break;
+ variable_name_counts[GetNonNullVariableName(variable)]++;
}
- if (LLVM_UNLIKELY(child_var_ref.Kind() == eReferenceKindInvalid)) {
- DAP_LOG(storage.log,
- "error: invalid variablesReference created for {} variable {}.",
- (is_permanent ? "permanent" : "temporary"), variable.GetName());
- return;
+ // Now we construct the result with unique display variable names.
+ for (auto i = start_idx; i < end_idx; ++i) {
+ lldb::SBValue variable = m_value_list.GetValueAtIndex(i);
+
+ if (!variable.IsValid() || !variable.IsInScope())
+ continue;
+
+ const var_ref_t frame_var_ref =
+ storage.Insert(variable, /*is_permanent=*/true);
+ if (LLVM_UNLIKELY(frame_var_ref.AsUInt32() >=
+ var_ref_t::k_variables_reference_threshold)) {
+ DAP_LOG(storage.log,
+ "warning: scopes variablesReference threshold reached. "
+ "current: {} threshold: {}, maximum {}.",
+ frame_var_ref.AsUInt32(),
+ var_ref_t::k_variables_reference_threshold,
+ var_ref_t::k_max_variables_references);
+ }
+
+ if (LLVM_UNLIKELY(frame_var_ref.Kind() == eReferenceKindInvalid))
+ break;
----------------
DrSergei wrote:
The same questions here
https://github.com/llvm/llvm-project/pull/183176
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits