diff --git a/lldb/include/lldb/Interpreter/OptionValue.h b/lldb/include/lldb/Interpreter/OptionValue.h index f293a3a33bfa0..7e48a675e2b7f 100644 --- a/lldb/include/lldb/Interpreter/OptionValue.h +++ b/lldb/include/lldb/Interpreter/OptionValue.h @@ -17,6 +17,7 @@ #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/FileSpecList.h" #include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" #include "lldb/Utility/StringList.h" #include "lldb/Utility/UUID.h" #include "lldb/lldb-defines.h" @@ -61,6 +62,8 @@ class OptionValue { eDumpOptionDescription = (1u << 3), eDumpOptionRaw = (1u << 4), eDumpOptionCommand = (1u << 5), + eDumpOptionDefaultValue = (1u << 6), + eDumpOptionOnlyChanged = (1u << 7), eDumpGroupValue = (eDumpOptionName | eDumpOptionType | eDumpOptionValue), eDumpGroupHelp = (eDumpOptionName | eDumpOptionType | eDumpOptionDescription), @@ -247,6 +250,13 @@ class OptionValue { void SetOptionWasSet() { m_value_was_set = true; } + /// Return true if the current value equals the default value. + /// + /// Subclasses that store a default value should override this to compare + /// against it. The base implementation falls back to `OptionWasSet()`, which + /// is a reasonable approximation for types without an explicit default. + virtual bool IsDefault() const { return !OptionWasSet(); } + void SetParent(const lldb::OptionValueSP &parent_sp) { m_parent_wp = parent_sp; } @@ -338,6 +348,20 @@ class OptionValue { // DeepCopy to it. Inherit from Cloneable to avoid doing this manually. virtual lldb::OptionValueSP Clone() const = 0; + class DefaultValueFormat { + public: + DefaultValueFormat(Stream &stream) : stream(stream) { + stream.PutCString(" (default: "); + } + ~DefaultValueFormat() { stream.PutChar(')'); } + + DefaultValueFormat(const DefaultValueFormat &) = delete; + DefaultValueFormat &operator=(const DefaultValueFormat &) = delete; + + private: + Stream &stream; + }; + lldb::OptionValueWP m_parent_wp; std::function m_callback; bool m_value_was_set = false; // This can be used to see if a value has been diff --git a/lldb/include/lldb/Interpreter/OptionValueArch.h b/lldb/include/lldb/Interpreter/OptionValueArch.h index 3ba07b65dd618..8b6954f03dd29 100644 --- a/lldb/include/lldb/Interpreter/OptionValueArch.h +++ b/lldb/include/lldb/Interpreter/OptionValueArch.h @@ -49,6 +49,8 @@ class OptionValueArch : public Cloneable { m_value_was_set = false; } + bool IsDefault() const override { return m_current_value == m_default_value; } + void AutoComplete(CommandInterpreter &interpreter, lldb_private::CompletionRequest &request) override; diff --git a/lldb/include/lldb/Interpreter/OptionValueBoolean.h b/lldb/include/lldb/Interpreter/OptionValueBoolean.h index 6d15dcd2fca5d..72c1ce446b8a0 100644 --- a/lldb/include/lldb/Interpreter/OptionValueBoolean.h +++ b/lldb/include/lldb/Interpreter/OptionValueBoolean.h @@ -45,6 +45,8 @@ class OptionValueBoolean : public Cloneable { void AutoComplete(CommandInterpreter &interpreter, CompletionRequest &request) override; + bool IsDefault() const override { return m_current_value == m_default_value; } + // Subclass specific functions /// Convert to bool operator. diff --git a/lldb/include/lldb/Interpreter/OptionValueChar.h b/lldb/include/lldb/Interpreter/OptionValueChar.h index 2e2cf1ac1e08d..c1f83a3daf846 100644 --- a/lldb/include/lldb/Interpreter/OptionValueChar.h +++ b/lldb/include/lldb/Interpreter/OptionValueChar.h @@ -43,6 +43,8 @@ class OptionValueChar : public Cloneable { m_value_was_set = false; } + bool IsDefault() const override { return m_current_value == m_default_value; } + // Subclass specific functions const char &operator=(char c) { diff --git a/lldb/include/lldb/Interpreter/OptionValueEnumeration.h b/lldb/include/lldb/Interpreter/OptionValueEnumeration.h index a3a13ca7b15eb..e8566934d9fc5 100644 --- a/lldb/include/lldb/Interpreter/OptionValueEnumeration.h +++ b/lldb/include/lldb/Interpreter/OptionValueEnumeration.h @@ -52,6 +52,8 @@ class OptionValueEnumeration m_value_was_set = false; } + bool IsDefault() const override { return m_current_value == m_default_value; } + void AutoComplete(CommandInterpreter &interpreter, CompletionRequest &request) override; @@ -72,6 +74,7 @@ class OptionValueEnumeration protected: void SetEnumerations(const OptionEnumValues &enumerators); + void DumpEnum(Stream &strm, enum_type value); enum_type m_current_value; enum_type m_default_value; diff --git a/lldb/include/lldb/Interpreter/OptionValueFileSpec.h b/lldb/include/lldb/Interpreter/OptionValueFileSpec.h index 66c5e328180f5..66f2b2a04ff53 100644 --- a/lldb/include/lldb/Interpreter/OptionValueFileSpec.h +++ b/lldb/include/lldb/Interpreter/OptionValueFileSpec.h @@ -53,6 +53,8 @@ class OptionValueFileSpec : public Cloneable { void AutoComplete(CommandInterpreter &interpreter, CompletionRequest &request) override; + bool IsDefault() const override { return m_current_value == m_default_value; } + // Subclass specific functions FileSpec &GetCurrentValue() { return m_current_value; } diff --git a/lldb/include/lldb/Interpreter/OptionValueFormat.h b/lldb/include/lldb/Interpreter/OptionValueFormat.h index 5fd3192304573..661e8b507d64f 100644 --- a/lldb/include/lldb/Interpreter/OptionValueFormat.h +++ b/lldb/include/lldb/Interpreter/OptionValueFormat.h @@ -42,6 +42,8 @@ class OptionValueFormat m_value_was_set = false; } + bool IsDefault() const override { return m_current_value == m_default_value; } + // Subclass specific functions lldb::Format GetCurrentValue() const { return m_current_value; } diff --git a/lldb/include/lldb/Interpreter/OptionValueFormatEntity.h b/lldb/include/lldb/Interpreter/OptionValueFormatEntity.h index c10d56cbeb70b..bbc1f8c1eec43 100644 --- a/lldb/include/lldb/Interpreter/OptionValueFormatEntity.h +++ b/lldb/include/lldb/Interpreter/OptionValueFormatEntity.h @@ -34,6 +34,10 @@ class OptionValueFormatEntity void Clear() override; + bool IsDefault() const override { + return m_current_format == m_default_format; + } + void AutoComplete(CommandInterpreter &interpreter, CompletionRequest &request) override; diff --git a/lldb/include/lldb/Interpreter/OptionValueLanguage.h b/lldb/include/lldb/Interpreter/OptionValueLanguage.h index e1c1f85493ad6..41ddb2a13f15e 100644 --- a/lldb/include/lldb/Interpreter/OptionValueLanguage.h +++ b/lldb/include/lldb/Interpreter/OptionValueLanguage.h @@ -44,6 +44,8 @@ class OptionValueLanguage : public Cloneable { m_value_was_set = false; } + bool IsDefault() const override { return m_current_value == m_default_value; } + // Subclass specific functions lldb::LanguageType GetCurrentValue() const { return m_current_value; } diff --git a/lldb/include/lldb/Interpreter/OptionValueProperties.h b/lldb/include/lldb/Interpreter/OptionValueProperties.h index 91a3955962372..fbd0cbd557337 100644 --- a/lldb/include/lldb/Interpreter/OptionValueProperties.h +++ b/lldb/include/lldb/Interpreter/OptionValueProperties.h @@ -46,6 +46,8 @@ class OptionValueProperties void DumpValue(const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) override; + bool IsDefault() const override; + llvm::json::Value ToJSON(const ExecutionContext *exe_ctx) const override; llvm::StringRef GetName() const override { return m_name; } diff --git a/lldb/include/lldb/Interpreter/OptionValueRegex.h b/lldb/include/lldb/Interpreter/OptionValueRegex.h index b952cb2476012..2799fea1538dc 100644 --- a/lldb/include/lldb/Interpreter/OptionValueRegex.h +++ b/lldb/include/lldb/Interpreter/OptionValueRegex.h @@ -41,6 +41,10 @@ class OptionValueRegex : public Cloneable { m_value_was_set = false; } + bool IsDefault() const override { + return m_regex.GetText() == m_default_regex_str; + } + // Subclass specific functions const RegularExpression *GetCurrentValue() const { return (m_regex.IsValid() ? &m_regex : nullptr); diff --git a/lldb/include/lldb/Interpreter/OptionValueSInt64.h b/lldb/include/lldb/Interpreter/OptionValueSInt64.h index c220ac29e461f..f19f3f8ab875e 100644 --- a/lldb/include/lldb/Interpreter/OptionValueSInt64.h +++ b/lldb/include/lldb/Interpreter/OptionValueSInt64.h @@ -48,6 +48,8 @@ class OptionValueSInt64 : public Cloneable { m_value_was_set = false; } + bool IsDefault() const override { return m_current_value == m_default_value; } + // Subclass specific functions const int64_t &operator=(int64_t value) { diff --git a/lldb/include/lldb/Interpreter/OptionValueString.h b/lldb/include/lldb/Interpreter/OptionValueString.h index 4ec98176b6f8b..e199443fa8b49 100644 --- a/lldb/include/lldb/Interpreter/OptionValueString.h +++ b/lldb/include/lldb/Interpreter/OptionValueString.h @@ -82,6 +82,8 @@ class OptionValueString : public Cloneable { m_value_was_set = false; } + bool IsDefault() const override { return m_current_value == m_default_value; } + // Subclass specific functions Flags &GetOptions() { return m_options; } diff --git a/lldb/include/lldb/Interpreter/OptionValueUInt64.h b/lldb/include/lldb/Interpreter/OptionValueUInt64.h index 087c1d3ee321a..2a87c19c54bbf 100644 --- a/lldb/include/lldb/Interpreter/OptionValueUInt64.h +++ b/lldb/include/lldb/Interpreter/OptionValueUInt64.h @@ -51,6 +51,8 @@ class OptionValueUInt64 : public Cloneable { m_value_was_set = false; } + bool IsDefault() const override { return m_current_value == m_default_value; } + // Subclass specific functions const uint64_t &operator=(uint64_t value) { diff --git a/lldb/include/lldb/Utility/ArchSpec.h b/lldb/include/lldb/Utility/ArchSpec.h index 4ce8b22c4521d..d0cfacba6d85e 100644 --- a/lldb/include/lldb/Utility/ArchSpec.h +++ b/lldb/include/lldb/Utility/ArchSpec.h @@ -567,6 +567,7 @@ class ArchSpec { /// \return true if \a lhs is less than \a rhs bool operator<(const ArchSpec &lhs, const ArchSpec &rhs); bool operator==(const ArchSpec &lhs, const ArchSpec &rhs); +bool operator!=(const ArchSpec &lhs, const ArchSpec &rhs); bool ParseMachCPUDashSubtypeTriple(llvm::StringRef triple_str, ArchSpec &arch); diff --git a/lldb/source/Commands/CommandObjectSettings.cpp b/lldb/source/Commands/CommandObjectSettings.cpp index 7bbb0dd567ab1..34a59d506da7f 100644 --- a/lldb/source/Commands/CommandObjectSettings.cpp +++ b/lldb/source/Commands/CommandObjectSettings.cpp @@ -237,28 +237,78 @@ insert-before or insert-after."); }; // CommandObjectSettingsShow -- Show current values +#define LLDB_OPTIONS_settings_show +#include "CommandOptions.inc" class CommandObjectSettingsShow : public CommandObjectParsed { public: CommandObjectSettingsShow(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "settings show", "Show matching debugger settings and their current " - "values. Defaults to showing all settings.", - nullptr) { + "values. Defaults to showing all settings.") { AddSimpleArgumentList(eArgTypeSettingVariableName, eArgRepeatOptional); } ~CommandObjectSettingsShow() override = default; + Options *GetOptions() override { return &m_options; } + + class CommandOptions : public Options { + public: + ~CommandOptions() override = default; + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) override { + const int short_option = m_getopt_table[option_idx].val; + switch (short_option) { + case 'd': + m_include_defaults = true; + break; + case 'c': + m_only_changed = true; + break; + default: + llvm_unreachable("Unimplemented option"); + } + return {}; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_include_defaults = false; + m_only_changed = false; + } + + llvm::ArrayRef GetDefinitions() override { + return g_settings_show_options; + } + + bool m_include_defaults = false; + bool m_only_changed = false; + }; + protected: void DoExecute(Args &args, CommandReturnObject &result) override { result.SetStatus(eReturnStatusSuccessFinishResult); + uint32_t dump_mask = OptionValue::eDumpGroupValue; + if (m_options.m_include_defaults) + dump_mask |= OptionValue::eDumpOptionDefaultValue; + if (m_options.m_only_changed) { + dump_mask |= OptionValue::eDumpOptionOnlyChanged; + dump_mask |= OptionValue::eDumpOptionDefaultValue; + } + if (!args.empty()) { for (const auto &arg : args) { + if (m_options.m_only_changed) { + Status lookup_error; + lldb::OptionValueSP value_sp = GetDebugger().GetPropertyValue( + &m_exe_ctx, arg.ref(), lookup_error); + if (value_sp && value_sp->IsDefault()) + continue; + } Status error(GetDebugger().DumpPropertyValue( - &m_exe_ctx, result.GetOutputStream(), arg.ref(), - OptionValue::eDumpGroupValue)); + &m_exe_ctx, result.GetOutputStream(), arg.ref(), dump_mask)); if (error.Success()) { result.GetOutputStream().EOL(); } else { @@ -267,9 +317,12 @@ class CommandObjectSettingsShow : public CommandObjectParsed { } } else { GetDebugger().DumpAllPropertyValues(&m_exe_ctx, result.GetOutputStream(), - OptionValue::eDumpGroupValue); + dump_mask); } } + +private: + CommandOptions m_options; }; // CommandObjectSettingsWrite -- Write settings to file diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td index bbfdebaaeb40c..81ba27db8a090 100644 --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -56,6 +56,14 @@ let Command = "settings clear" in { Desc<"Clear all settings.">; } +let Command = "settings show" in { + def setshow_defaults : Option<"defaults", "d">, + Desc<"Include default values if defined.">; + def setshow_changed : Option<"changed", "c">, + Desc<"Only show settings whose value differs from the " + "default.">; +} + let Command = "breakpoint list" in { // FIXME: We need to add an "internal" command, and then add this sort of // thing to it. But I need to see it for now, and don't want to wait. diff --git a/lldb/source/Interpreter/OptionValueArch.cpp b/lldb/source/Interpreter/OptionValueArch.cpp index ea15ccaaefe2b..a960e39d35a62 100644 --- a/lldb/source/Interpreter/OptionValueArch.cpp +++ b/lldb/source/Interpreter/OptionValueArch.cpp @@ -11,6 +11,7 @@ #include "lldb/DataFormatters/FormatManager.h" #include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Args.h" #include "lldb/Utility/State.h" @@ -30,6 +31,12 @@ void OptionValueArch::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, if (arch_name) strm.PutCString(arch_name); } + + if (dump_mask & eDumpOptionDefaultValue && + m_current_value != m_default_value && m_default_value.IsValid()) { + DefaultValueFormat label(strm); + strm.PutCString(m_default_value.GetArchitectureName()); + } } } diff --git a/lldb/source/Interpreter/OptionValueArray.cpp b/lldb/source/Interpreter/OptionValueArray.cpp index f6c14dee525e9..5600333c111a7 100644 --- a/lldb/source/Interpreter/OptionValueArray.cpp +++ b/lldb/source/Interpreter/OptionValueArray.cpp @@ -8,6 +8,7 @@ #include "lldb/Interpreter/OptionValueArray.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Args.h" #include "lldb/Utility/Stream.h" @@ -27,8 +28,15 @@ void OptionValueArray::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, if (dump_mask & eDumpOptionValue) { const bool one_line = dump_mask & eDumpOptionCommand; const uint32_t size = m_values.size(); - if (dump_mask & eDumpOptionType) - strm.Printf(" =%s", (m_values.size() > 0 && !one_line) ? "\n" : ""); + if (dump_mask & (eDumpOptionType | eDumpOptionDefaultValue)) { + strm.PutCString(" ="); + if (dump_mask & eDumpOptionDefaultValue && !m_values.empty()) { + DefaultValueFormat label(strm); + strm.PutCString("empty"); + } + if (!m_values.empty() && !one_line) + strm.PutCString("\n"); + } if (!one_line) strm.IndentMore(); for (uint32_t i = 0; i < size; ++i) { diff --git a/lldb/source/Interpreter/OptionValueBoolean.cpp b/lldb/source/Interpreter/OptionValueBoolean.cpp index d4fda762fea6b..023c243b3efc1 100644 --- a/lldb/source/Interpreter/OptionValueBoolean.cpp +++ b/lldb/source/Interpreter/OptionValueBoolean.cpp @@ -10,6 +10,7 @@ #include "lldb/Host/PosixApi.h" #include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/StringList.h" #include "llvm/ADT/STLExtras.h" @@ -27,6 +28,11 @@ void OptionValueBoolean::DumpValue(const ExecutionContext *exe_ctx, if (dump_mask & eDumpOptionType) strm.PutCString(" = "); strm.PutCString(m_current_value ? "true" : "false"); + if (dump_mask & eDumpOptionDefaultValue && + m_current_value != m_default_value) { + DefaultValueFormat label(strm); + strm.PutCString(m_default_value ? "true" : "false"); + } } } diff --git a/lldb/source/Interpreter/OptionValueChar.cpp b/lldb/source/Interpreter/OptionValueChar.cpp index 2aadcff158388..595dcba49b61a 100644 --- a/lldb/source/Interpreter/OptionValueChar.cpp +++ b/lldb/source/Interpreter/OptionValueChar.cpp @@ -9,6 +9,7 @@ #include "lldb/Interpreter/OptionValueChar.h" #include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/StringList.h" #include "llvm/ADT/STLExtras.h" @@ -16,6 +17,13 @@ using namespace lldb; using namespace lldb_private; +static void DumpChar(Stream &strm, char value) { + if (value != '\0') + strm.PutChar(value); + else + strm.PutCString("(null)"); +} + void OptionValueChar::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) { if (dump_mask & eDumpOptionType) @@ -24,10 +32,12 @@ void OptionValueChar::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, if (dump_mask & eDumpOptionValue) { if (dump_mask & eDumpOptionType) strm.PutCString(" = "); - if (m_current_value != '\0') - strm.PutChar(m_current_value); - else - strm.PutCString("(null)"); + DumpChar(strm, m_current_value); + if (dump_mask & eDumpOptionDefaultValue && + m_current_value != m_default_value) { + DefaultValueFormat label(strm); + DumpChar(strm, m_default_value); + } } } diff --git a/lldb/source/Interpreter/OptionValueDictionary.cpp b/lldb/source/Interpreter/OptionValueDictionary.cpp index 19e21dd6f4c9a..0efc76bb7cd94 100644 --- a/lldb/source/Interpreter/OptionValueDictionary.cpp +++ b/lldb/source/Interpreter/OptionValueDictionary.cpp @@ -9,6 +9,7 @@ #include "lldb/Interpreter/OptionValueDictionary.h" #include "lldb/DataFormatters/FormatManager.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Interpreter/OptionValueEnumeration.h" #include "lldb/Interpreter/OptionValueString.h" #include "lldb/Utility/Args.h" @@ -30,8 +31,13 @@ void OptionValueDictionary::DumpValue(const ExecutionContext *exe_ctx, } if (dump_mask & eDumpOptionValue) { const bool one_line = dump_mask & eDumpOptionCommand; - if (dump_mask & eDumpOptionType) + if (dump_mask & (eDumpOptionType | eDumpOptionDefaultValue)) { strm.PutCString(" ="); + if (dump_mask & eDumpOptionDefaultValue && !m_values.empty()) { + DefaultValueFormat label(strm); + strm.PutCString("empty"); + } + } if (!one_line) strm.IndentMore(); diff --git a/lldb/source/Interpreter/OptionValueEnumeration.cpp b/lldb/source/Interpreter/OptionValueEnumeration.cpp index cf646233c80da..eb31bde498b7c 100644 --- a/lldb/source/Interpreter/OptionValueEnumeration.cpp +++ b/lldb/source/Interpreter/OptionValueEnumeration.cpp @@ -8,6 +8,7 @@ #include "lldb/Interpreter/OptionValueEnumeration.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/StringList.h" using namespace lldb; @@ -19,6 +20,17 @@ OptionValueEnumeration::OptionValueEnumeration( SetEnumerations(enumerators); } +void OptionValueEnumeration::DumpEnum(Stream &strm, enum_type value) { + const size_t count = m_enumerations.GetSize(); + for (size_t i = 0; i < count; ++i) + if (m_enumerations.GetValueAtIndexUnchecked(i).value == value) { + strm.PutCString(m_enumerations.GetCStringAtIndex(i)); + return; + } + + strm.Printf("%" PRIu64, (uint64_t)value); +} + void OptionValueEnumeration::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) { if (dump_mask & eDumpOptionType) @@ -26,14 +38,12 @@ void OptionValueEnumeration::DumpValue(const ExecutionContext *exe_ctx, if (dump_mask & eDumpOptionValue) { if (dump_mask & eDumpOptionType) strm.PutCString(" = "); - const size_t count = m_enumerations.GetSize(); - for (size_t i = 0; i < count; ++i) { - if (m_enumerations.GetValueAtIndexUnchecked(i).value == m_current_value) { - strm.PutCString(m_enumerations.GetCStringAtIndex(i).GetStringRef()); - return; - } + DumpEnum(strm, m_current_value); + if (dump_mask & eDumpOptionDefaultValue && + m_current_value != m_default_value) { + DefaultValueFormat label(strm); + DumpEnum(strm, m_default_value); } - strm.Printf("%" PRIu64, (uint64_t)m_current_value); } } diff --git a/lldb/source/Interpreter/OptionValueFileSpec.cpp b/lldb/source/Interpreter/OptionValueFileSpec.cpp index 6fa6af4ace02a..8d4966dcda0bc 100644 --- a/lldb/source/Interpreter/OptionValueFileSpec.cpp +++ b/lldb/source/Interpreter/OptionValueFileSpec.cpp @@ -12,6 +12,7 @@ #include "lldb/Host/FileSystem.h" #include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Args.h" #include "lldb/Utility/State.h" @@ -41,7 +42,12 @@ void OptionValueFileSpec::DumpValue(const ExecutionContext *exe_ctx, strm.PutCString(" = "); if (m_current_value) { - strm << '"' << m_current_value.GetPath().c_str() << '"'; + strm << '"' << m_current_value.GetPath() << '"'; + } + if (dump_mask & eDumpOptionDefaultValue && + m_current_value != m_default_value && m_default_value) { + DefaultValueFormat label(strm); + strm << '"' << m_default_value.GetPath() << '"'; } } } diff --git a/lldb/source/Interpreter/OptionValueFileSpecList.cpp b/lldb/source/Interpreter/OptionValueFileSpecList.cpp index f252dc4603cc1..f331c5d13f461 100644 --- a/lldb/source/Interpreter/OptionValueFileSpecList.cpp +++ b/lldb/source/Interpreter/OptionValueFileSpecList.cpp @@ -8,6 +8,7 @@ #include "lldb/Interpreter/OptionValueFileSpecList.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Args.h" #include "lldb/Utility/Stream.h" @@ -22,9 +23,15 @@ void OptionValueFileSpecList::DumpValue(const ExecutionContext *exe_ctx, if (dump_mask & eDumpOptionValue) { const bool one_line = dump_mask & eDumpOptionCommand; const uint32_t size = m_current_value.GetSize(); - if (dump_mask & eDumpOptionType) - strm.Printf(" =%s", - (m_current_value.GetSize() > 0 && !one_line) ? "\n" : ""); + if (dump_mask & (eDumpOptionType | eDumpOptionDefaultValue)) { + strm.Printf(" ="); + if (dump_mask & eDumpOptionDefaultValue && !m_current_value.IsEmpty()) { + DefaultValueFormat label(strm); + strm.PutCString("empty"); + } + if (!m_current_value.IsEmpty() && !one_line) + strm.PutCString("\n"); + } if (!one_line) strm.IndentMore(); for (uint32_t i = 0; i < size; ++i) { diff --git a/lldb/source/Interpreter/OptionValueFormat.cpp b/lldb/source/Interpreter/OptionValueFormat.cpp index bc4e77923d10e..05990fb50c356 100644 --- a/lldb/source/Interpreter/OptionValueFormat.cpp +++ b/lldb/source/Interpreter/OptionValueFormat.cpp @@ -10,6 +10,7 @@ #include "lldb/DataFormatters/FormatManager.h" #include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Stream.h" using namespace lldb; @@ -23,6 +24,11 @@ void OptionValueFormat::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, if (dump_mask & eDumpOptionType) strm.PutCString(" = "); strm.PutCString(FormatManager::GetFormatAsCString(m_current_value)); + if (dump_mask & eDumpOptionDefaultValue && + m_current_value != m_default_value) { + DefaultValueFormat label(strm); + strm.PutCString(FormatManager::GetFormatAsCString(m_default_value)); + } } } diff --git a/lldb/source/Interpreter/OptionValueFormatEntity.cpp b/lldb/source/Interpreter/OptionValueFormatEntity.cpp index d8b830115005c..b31dd4e475878 100644 --- a/lldb/source/Interpreter/OptionValueFormatEntity.cpp +++ b/lldb/source/Interpreter/OptionValueFormatEntity.cpp @@ -10,6 +10,7 @@ #include "lldb/Core/Module.h" #include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/StringList.h" using namespace lldb; @@ -33,10 +34,9 @@ void OptionValueFormatEntity::Clear() { m_value_was_set = false; } -static void EscapeBackticks(llvm::StringRef str, std::string &dst) { - dst.clear(); +static std::string EscapeBackticks(llvm::StringRef str) { + std::string dst; dst.reserve(str.size()); - for (size_t i = 0, e = str.size(); i != e; ++i) { char c = str[i]; if (c == '`') { @@ -45,6 +45,7 @@ static void EscapeBackticks(llvm::StringRef str, std::string &dst) { } dst += c; } + return dst; } void OptionValueFormatEntity::DumpValue(const ExecutionContext *exe_ctx, @@ -54,17 +55,18 @@ void OptionValueFormatEntity::DumpValue(const ExecutionContext *exe_ctx, if (dump_mask & eDumpOptionValue) { if (dump_mask & eDumpOptionType) strm.PutCString(" = "); - std::string escaped; - EscapeBackticks(m_current_format, escaped); - strm << '"' << escaped << '"'; + strm << '"' << EscapeBackticks(m_current_format) << '"'; + if (dump_mask & eDumpOptionDefaultValue && + m_current_format != m_default_format) { + DefaultValueFormat label(strm); + strm << '"' << EscapeBackticks(m_default_format) << '"'; + } } } llvm::json::Value OptionValueFormatEntity::ToJSON(const ExecutionContext *exe_ctx) const { - std::string escaped; - EscapeBackticks(m_current_format, escaped); - return escaped; + return EscapeBackticks(m_current_format); } Status OptionValueFormatEntity::SetValueFromString(llvm::StringRef value_str, diff --git a/lldb/source/Interpreter/OptionValueLanguage.cpp b/lldb/source/Interpreter/OptionValueLanguage.cpp index 0fdaacb02594e..bb28dc8debe6d 100644 --- a/lldb/source/Interpreter/OptionValueLanguage.cpp +++ b/lldb/source/Interpreter/OptionValueLanguage.cpp @@ -9,8 +9,9 @@ #include "lldb/Interpreter/OptionValueLanguage.h" #include "lldb/DataFormatters/FormatManager.h" -#include "lldb/Target/Language.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Symbol/TypeSystem.h" +#include "lldb/Target/Language.h" #include "lldb/Utility/Args.h" #include "lldb/Utility/Stream.h" @@ -26,6 +27,12 @@ void OptionValueLanguage::DumpValue(const ExecutionContext *exe_ctx, strm.PutCString(" = "); if (m_current_value != eLanguageTypeUnknown) strm.PutCString(Language::GetNameForLanguageType(m_current_value)); + if (dump_mask & eDumpOptionDefaultValue && + m_current_value != m_default_value && + m_default_value != eLanguageTypeUnknown) { + DefaultValueFormat label(strm); + strm.PutCString(Language::GetNameForLanguageType(m_default_value)); + } } } diff --git a/lldb/source/Interpreter/OptionValuePathMappings.cpp b/lldb/source/Interpreter/OptionValuePathMappings.cpp index 95b8e64171455..abf4d429602e4 100644 --- a/lldb/source/Interpreter/OptionValuePathMappings.cpp +++ b/lldb/source/Interpreter/OptionValuePathMappings.cpp @@ -9,6 +9,7 @@ #include "lldb/Interpreter/OptionValuePathMappings.h" #include "lldb/Host/FileSystem.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Args.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/Stream.h" @@ -28,8 +29,15 @@ void OptionValuePathMappings::DumpValue(const ExecutionContext *exe_ctx, if (dump_mask & eDumpOptionType) strm.Printf("(%s)", GetTypeAsCString()); if (dump_mask & eDumpOptionValue) { - if (dump_mask & eDumpOptionType) - strm.Printf(" =%s", (m_path_mappings.GetSize() > 0) ? "\n" : ""); + if (dump_mask & (eDumpOptionType | eDumpOptionDefaultValue)) { + strm.Printf(" ="); + if (dump_mask & eDumpOptionDefaultValue && !m_path_mappings.IsEmpty()) { + DefaultValueFormat label(strm); + strm.PutCString("empty"); + } + if (!m_path_mappings.IsEmpty()) + strm.PutCString("\n"); + } m_path_mappings.Dump(&strm); } } diff --git a/lldb/source/Interpreter/OptionValueProperties.cpp b/lldb/source/Interpreter/OptionValueProperties.cpp index cb71bffa8fe25..74e9a3e2d8923 100644 --- a/lldb/source/Interpreter/OptionValueProperties.cpp +++ b/lldb/source/Interpreter/OptionValueProperties.cpp @@ -326,6 +326,8 @@ void OptionValueProperties::DumpValue(const ExecutionContext *exe_ctx, if (property) { OptionValue *option_value = property->GetValue().get(); assert(option_value); + if ((dump_mask & eDumpOptionOnlyChanged) && option_value->IsDefault()) + continue; const bool transparent_value = option_value->ValueIsTransparent(); property->Dump(exe_ctx, strm, dump_mask); if (!transparent_value) @@ -334,6 +336,15 @@ void OptionValueProperties::DumpValue(const ExecutionContext *exe_ctx, } } +bool OptionValueProperties::IsDefault() const { + for (const Property &property : m_properties) { + if (OptionValue *value = property.GetValue().get()) + if (!value->IsDefault()) + return false; + } + return true; +} + llvm::json::Value OptionValueProperties::ToJSON(const ExecutionContext *exe_ctx) const { llvm::json::Object json_properties; diff --git a/lldb/source/Interpreter/OptionValueRegex.cpp b/lldb/source/Interpreter/OptionValueRegex.cpp index 91ec41df6ee50..30e307a7240ef 100644 --- a/lldb/source/Interpreter/OptionValueRegex.cpp +++ b/lldb/source/Interpreter/OptionValueRegex.cpp @@ -8,6 +8,7 @@ #include "lldb/Interpreter/OptionValueRegex.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Stream.h" using namespace lldb; @@ -24,6 +25,12 @@ void OptionValueRegex::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, llvm::StringRef regex_text = m_regex.GetText(); strm.Printf("%s", regex_text.str().c_str()); } + if (dump_mask & eDumpOptionDefaultValue && + m_regex.GetText() != m_default_regex_str && + !m_default_regex_str.empty()) { + DefaultValueFormat label(strm); + strm.PutCString(m_default_regex_str); + } } } diff --git a/lldb/source/Interpreter/OptionValueSInt64.cpp b/lldb/source/Interpreter/OptionValueSInt64.cpp index df7aee99e212c..00f1cc37dddac 100644 --- a/lldb/source/Interpreter/OptionValueSInt64.cpp +++ b/lldb/source/Interpreter/OptionValueSInt64.cpp @@ -8,6 +8,7 @@ #include "lldb/Interpreter/OptionValueSInt64.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Stream.h" using namespace lldb; @@ -26,6 +27,11 @@ void OptionValueSInt64::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, if (dump_mask & eDumpOptionType) strm.PutCString(" = "); strm.Printf("%" PRIi64, m_current_value); + if (dump_mask & eDumpOptionDefaultValue && + m_current_value != m_default_value) { + DefaultValueFormat label(strm); + strm.Printf("%" PRIi64, m_default_value); + } } } diff --git a/lldb/source/Interpreter/OptionValueString.cpp b/lldb/source/Interpreter/OptionValueString.cpp index ae30661a56d05..022a27d069f48 100644 --- a/lldb/source/Interpreter/OptionValueString.cpp +++ b/lldb/source/Interpreter/OptionValueString.cpp @@ -9,12 +9,28 @@ #include "lldb/Interpreter/OptionValueString.h" #include "lldb/Host/OptionParser.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Args.h" #include "lldb/Utility/Stream.h" using namespace lldb; using namespace lldb_private; +static void DumpString(Stream &strm, const std::string &str, bool escape, + bool raw) { + if (escape) { + std::string escaped_str; + Args::ExpandEscapedCharacters(str.c_str(), escaped_str); + DumpString(strm, escaped_str, false, raw); + return; + } + + if (raw) + strm.PutCString(str); + else + strm.QuotedCString(str.c_str()); +} + void OptionValueString::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) { if (dump_mask & eDumpOptionType) @@ -22,21 +38,15 @@ void OptionValueString::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, if (dump_mask & eDumpOptionValue) { if (dump_mask & eDumpOptionType) strm.PutCString(" = "); - if (!m_current_value.empty() || m_value_was_set) { - if (m_options.Test(eOptionEncodeCharacterEscapeSequences)) { - std::string expanded_escape_value; - Args::ExpandEscapedCharacters(m_current_value.c_str(), - expanded_escape_value); - if (dump_mask & eDumpOptionRaw) - strm.Printf("%s", expanded_escape_value.c_str()); - else - strm.Printf("\"%s\"", expanded_escape_value.c_str()); - } else { - if (dump_mask & eDumpOptionRaw) - strm.Printf("%s", m_current_value.c_str()); - else - strm.Printf("\"%s\"", m_current_value.c_str()); - } + const bool escape = m_options.Test(eOptionEncodeCharacterEscapeSequences); + const bool raw = dump_mask & eDumpOptionRaw; + if (!m_current_value.empty() || m_value_was_set) + DumpString(strm, m_current_value, escape, raw); + + if (dump_mask & eDumpOptionDefaultValue && + m_current_value != m_default_value && !m_default_value.empty()) { + DefaultValueFormat label(strm); + DumpString(strm, m_default_value, escape, raw); } } } diff --git a/lldb/source/Interpreter/OptionValueUInt64.cpp b/lldb/source/Interpreter/OptionValueUInt64.cpp index aa5e9a21c8c28..63f83cbcd60a3 100644 --- a/lldb/source/Interpreter/OptionValueUInt64.cpp +++ b/lldb/source/Interpreter/OptionValueUInt64.cpp @@ -8,6 +8,7 @@ #include "lldb/Interpreter/OptionValueUInt64.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Stream.h" using namespace lldb; @@ -30,6 +31,11 @@ void OptionValueUInt64::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, if (dump_mask & eDumpOptionType) strm.PutCString(" = "); strm.Printf("%" PRIu64, m_current_value); + if (dump_mask & eDumpOptionDefaultValue && + m_current_value != m_default_value) { + DefaultValueFormat label(strm); + strm.Printf("%" PRIu64, m_default_value); + } } } diff --git a/lldb/source/Utility/ArchSpec.cpp b/lldb/source/Utility/ArchSpec.cpp index f02a3c0298126..9af0b7e180165 100644 --- a/lldb/source/Utility/ArchSpec.cpp +++ b/lldb/source/Utility/ArchSpec.cpp @@ -1448,6 +1448,10 @@ bool lldb_private::operator==(const ArchSpec &lhs, const ArchSpec &rhs) { return lhs.GetCore() == rhs.GetCore(); } +bool lldb_private::operator!=(const ArchSpec &lhs, const ArchSpec &rhs) { + return !(lhs == rhs); +} + bool ArchSpec::IsFullySpecifiedTriple() const { if (!TripleOSWasSpecified()) return false; diff --git a/lldb/test/API/commands/settings/TestSettings.py b/lldb/test/API/commands/settings/TestSettings.py index bc864942055c0..19f0b56145857 100644 --- a/lldb/test/API/commands/settings/TestSettings.py +++ b/lldb/test/API/commands/settings/TestSettings.py @@ -238,6 +238,111 @@ def test_set_auto_confirm(self): startstr="auto-confirm (boolean) = false", ) + def test_settings_show_changed(self): + """Test `settings show --changed` filters the listing to non-default values.""" + setting = "target.max-children-count" + + def cleanup(): + self.runCmd("settings clear %s" % setting, check=False) + + self.addTearDownHook(cleanup) + + # Ensure a clean slate for this setting. + self.runCmd("settings clear %s" % setting) + + # With the setting at its default, it should not show up under --changed. + self.expect( + "settings show --changed", + matching=False, + substrs=[setting], + ) + + # After explicitly changing the setting, it should show up along with + # the default value. + self.runCmd("settings set %s 42" % setting) + self.expect( + "settings show --changed", + substrs=["%s (unsigned) = 42 (default: 24)" % setting], + ) + + # After clearing, it should no longer show up. + self.runCmd("settings clear %s" % setting) + self.expect( + "settings show --changed", + matching=False, + substrs=[setting], + ) + + # An explicit property path at its default prints nothing. + self.expect( + "settings show --changed %s" % setting, + matching=False, + substrs=[setting], + ) + + # When the value has been changed, the explicit path prints the + # current value and default. + self.runCmd("settings set %s 42" % setting) + self.expect( + "settings show --changed %s" % setting, + substrs=["%s (unsigned) = 42 (default: 24)" % setting], + ) + + def test_settings_show_changed_per_target(self): + """Test that `settings show --changed` reflects per-target values: a + per-target setting changed in target A should show as changed when A is + selected, and as unchanged when target B is selected.""" + setting = "target.max-children-count" + + def cleanup(): + self.runCmd("settings clear %s" % setting, check=False) + + self.addTearDownHook(cleanup) + self.runCmd("settings clear %s" % setting) + + target_a = self.dbg.CreateTarget("") + self.assertTrue(target_a.IsValid(), "Created target A") + target_b = self.dbg.CreateTarget("") + self.assertTrue(target_b.IsValid(), "Created target B") + + index_a = self.dbg.GetIndexOfTarget(target_a) + index_b = self.dbg.GetIndexOfTarget(target_b) + + # Select target A and override the per-target setting there. + self.runCmd("target select %d" % index_a) + self.runCmd("settings set %s 42" % setting) + + # With A selected, the changed listing should include the override. + self.expect( + "settings show --changed", + substrs=["%s (unsigned) = 42 (default: 24)" % setting], + ) + self.expect( + "settings show --changed %s" % setting, + substrs=["%s (unsigned) = 42 (default: 24)" % setting], + ) + + # Switch to target B: the same setting is still at its default for B, + # so it must not appear in either form of the changed listing. + self.runCmd("target select %d" % index_b) + self.expect( + "settings show --changed", + matching=False, + substrs=[setting], + ) + self.expect( + "settings show --changed %s" % setting, + matching=False, + substrs=[setting], + ) + + # Sanity check: switching back to A still shows the override. + self.runCmd("target select %d" % index_a) + self.expect( + "settings show --changed %s" % setting, + substrs=["%s (unsigned) = 42 (default: 24)" % setting], + ) + @skipIf(archs=no_match(["x86_64", "i386", "i686"])) def test_disassembler_settings(self): """Test that user options for the disassembler take effect.""" @@ -972,6 +1077,123 @@ def test_settings_set_exists(self): # A known option should fail if its argument is invalid. self.expect("settings set auto-confirm bogus", error=True) + def test_settings_show_defaults(self): + # boolean + self.expect( + "settings show --defaults auto-one-line-summaries", + matching=False, + substrs=["(default: true)"], + ) + self.runCmd("settings set auto-one-line-summaries false") + self.expect( + "settings show --defaults auto-one-line-summaries", + substrs=["= false (default: true)"], + ) + # unsigned + self.expect( + "settings show --defaults stop-line-count-before", + matching=False, + patterns=[r"\(default: \d+\)"], + ) + self.runCmd("settings set stop-line-count-before 99") + self.expect( + "settings show --defaults stop-line-count-before", + patterns=[r"= 99 \(default: \d+\)"], + ) + # string + self.expect( + "settings show --defaults prompt", + matching=False, + patterns=[r'\(default: ".+"\)'], + ) + self.runCmd("settings set prompt ' '") + self.expect( + "settings show --defaults prompt", + patterns=[r'= " " \(default: ".+"\)'], + ) + # enum + self.expect( + "settings show --defaults stop-disassembly-display", + matching=False, + patterns=[r"\(default: .+\)"], + ) + self.runCmd("settings set stop-disassembly-display no-source") + self.expect( + "settings show --defaults stop-disassembly-display", + patterns=[r"= no-source \(default: .+\)"], + ) + # regex + self.expect( + "settings show --defaults target.process.thread.step-avoid-regexp", + matching=False, + patterns=[r"\(default: .+\)"], + ) + self.runCmd("settings set target.process.thread.step-avoid-regexp dotstar") + self.expect( + "settings show --defaults target.process.thread.step-avoid-regexp", + patterns=[r"= dotstar \(default: .+\)"], + ) + # format-string + self.expect( + "settings show --defaults disassembly-format", + matching=False, + patterns=[r'\(default: ".+"\)'], + ) + self.runCmd("settings set disassembly-format dollar") + self.expect( + "settings show --defaults disassembly-format", + patterns=[r'= "dollar" \(default: ".+"\)'], + ) + # arrays + self.expect( + "settings show --defaults target.unset-env-vars", + matching=False, + substrs=["(default: empty)"], + ) + self.runCmd("settings set target.unset-env-vars PATH") + self.expect( + "settings show --defaults target.unset-env-vars", + substrs=["(default: empty)", '[0]: "PATH"'], + ) + # dictionaries + self.runCmd("settings clear target.env-vars") + self.expect( + "settings show --defaults target.env-vars", + matching=False, + substrs=["(default: empty)"], + ) + self.runCmd("settings set target.env-vars THING=value") + self.expect( + "settings show --defaults target.env-vars", + substrs=["(default: empty)", "THING=value"], + ) + pwd = os.getcwd() + # file list + self.expect( + "settings show --defaults target.exec-search-paths", + matching=False, + substrs=["(default: empty)"], + ) + self.runCmd(f"settings set target.exec-search-paths {pwd}") + self.expect( + "settings show --defaults target.exec-search-paths", + substrs=["(default: empty)", f"[0]: {pwd}"], + ) + # path map + self.expect( + "settings show --defaults target.source-map", + matching=False, + substrs=["(default: empty)"], + ) + self.runCmd(f"settings set target.source-map /abc {pwd}") + self.expect( + "settings show --defaults target.source-map", + patterns=[ + r"\(default: empty\)", + rf'\[0\] "[/\\]abc" -> "{re.escape(pwd)}"', + ], + ) + def get_setting_json(self, setting_path=None): settings_data = self.dbg.GetSetting(setting_path) stream = lldb.SBStream()