JDevlieghere created this revision.
JDevlieghere added reviewers: clayborg, jingham, teemperor.
JDevlieghere added a project: LLDB.

For the reproducer feature I need to be able to export and import the current 
LLDB configuration. To realize this I've extended the existing functionality to 
print settings. With the help of a new formatting option, we can now write the 
settings and their values to a file structured as regular commands.

Concretely the functionality works as follows:

  (lldb) settings export /path/to/file

This file contains a bunch of `settings set` commands, followed by the 
setting's name and value.

  settings set use-external-editor false
  settings set use-color true
  settings set auto-one-line-summaries true
  settings set auto-indent true

Loading the settings again is as simple as sourcing the newly created file:

  (lldb) command source /path/to/file

I had to make a few small changes here to make this work:

- When a value is not set `settings set <setting>` is equal to `settings clear 
<setting>`. This is because not all settings have a meaningful default For 
example for `settings set` we used to print `unknown` which is not a value the 
user can set.
- I introduced a new printing option and group for printing options as 
commands. This relates to printing everything on a single line for things like 
arrays and dicts.

There's one setting that is proving to be a pain: the disassembly format value. 
Currently we don't accept our own default value. I don't know what the problem 
is but I managed to work around this by escaping the back ticks.


Repository:
  rLLDB LLDB

https://reviews.llvm.org/D52651

Files:
  include/lldb/Interpreter/OptionValue.h
  lit/Settings/TestExport.test
  source/Commands/CommandObjectSettings.cpp
  source/Core/Debugger.cpp
  source/Interpreter/OptionValueArray.cpp
  source/Interpreter/OptionValueDictionary.cpp
  source/Interpreter/OptionValueFileSpecLIst.cpp
  source/Interpreter/OptionValueFormatEntity.cpp
  source/Interpreter/OptionValueLanguage.cpp
  source/Interpreter/Property.cpp

Index: source/Interpreter/Property.cpp
===================================================================
--- source/Interpreter/Property.cpp
+++ source/Interpreter/Property.cpp
@@ -233,7 +233,10 @@
                     uint32_t dump_mask) const {
   if (m_value_sp) {
     const bool dump_desc = dump_mask & OptionValue::eDumpOptionDescription;
+    const bool dump_cmd = dump_mask & OptionValue::eDumpOptionCommand;
     const bool transparent = m_value_sp->ValueIsTransparent();
+    if (dump_cmd && !transparent)
+      strm << "settings set ";
     if (dump_desc || !transparent) {
       if ((dump_mask & OptionValue::eDumpOptionName) && m_name) {
         DumpQualifiedName(strm);
Index: source/Interpreter/OptionValueLanguage.cpp
===================================================================
--- source/Interpreter/OptionValueLanguage.cpp
+++ source/Interpreter/OptionValueLanguage.cpp
@@ -28,7 +28,8 @@
   if (dump_mask & eDumpOptionValue) {
     if (dump_mask & eDumpOptionType)
       strm.PutCString(" = ");
-    strm.PutCString(Language::GetNameForLanguageType(m_current_value));
+    if (m_current_value != eLanguageTypeUnknown)
+      strm.PutCString(Language::GetNameForLanguageType(m_current_value));
   }
 }
 
Index: source/Interpreter/OptionValueFormatEntity.cpp
===================================================================
--- source/Interpreter/OptionValueFormatEntity.cpp
+++ source/Interpreter/OptionValueFormatEntity.cpp
@@ -47,8 +47,8 @@
     strm.Printf("(%s)", GetTypeAsCString());
   if (dump_mask & eDumpOptionValue) {
     if (dump_mask & eDumpOptionType)
-      strm.PutCString(" = \"");
-    strm << m_current_format.c_str() << '"';
+      strm.PutCString(" = ");
+    strm << '"' << m_current_format.c_str() << '"';
   }
 }
 
Index: source/Interpreter/OptionValueFileSpecLIst.cpp
===================================================================
--- source/Interpreter/OptionValueFileSpecLIst.cpp
+++ source/Interpreter/OptionValueFileSpecLIst.cpp
@@ -25,16 +25,24 @@
   if (dump_mask & eDumpOptionType)
     strm.Printf("(%s)", GetTypeAsCString());
   if (dump_mask & eDumpOptionValue) {
-    if (dump_mask & eDumpOptionType)
-      strm.Printf(" =%s", m_current_value.GetSize() > 0 ? "\n" : "");
-    strm.IndentMore();
+    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 (!one_line)
+      strm.IndentMore();
     for (uint32_t i = 0; i < size; ++i) {
-      strm.Indent();
-      strm.Printf("[%u]: ", i);
+      if (!one_line) {
+        strm.Indent();
+        strm.Printf("[%u]: ", i);
+      }
       m_current_value.GetFileSpecAtIndex(i).Dump(&strm);
+      if (one_line)
+        strm << ' ';
     }
-    strm.IndentLess();
+    if (!one_line)
+      strm.IndentLess();
   }
 }
 
Index: source/Interpreter/OptionValueDictionary.cpp
===================================================================
--- source/Interpreter/OptionValueDictionary.cpp
+++ source/Interpreter/OptionValueDictionary.cpp
@@ -33,16 +33,23 @@
       strm.Printf("(%s)", GetTypeAsCString());
   }
   if (dump_mask & eDumpOptionValue) {
+    const bool one_line = dump_mask & eDumpOptionCommand;
     if (dump_mask & eDumpOptionType)
       strm.PutCString(" =");
 
     collection::iterator pos, end = m_values.end();
 
-    strm.IndentMore();
+    if (!one_line)
+      strm.IndentMore();
 
     for (pos = m_values.begin(); pos != end; ++pos) {
       OptionValue *option_value = pos->second.get();
-      strm.EOL();
+
+      if (one_line)
+        strm << ' ';
+      else
+        strm.EOL();
+
       strm.Indent(pos->first.GetCString());
 
       const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0;
@@ -74,7 +81,8 @@
         break;
       }
     }
-    strm.IndentLess();
+    if (!one_line)
+      strm.IndentLess();
   }
 }
 
Index: source/Interpreter/OptionValueArray.cpp
===================================================================
--- source/Interpreter/OptionValueArray.cpp
+++ source/Interpreter/OptionValueArray.cpp
@@ -31,13 +31,17 @@
       strm.Printf("(%s)", GetTypeAsCString());
   }
   if (dump_mask & eDumpOptionValue) {
-    if (dump_mask & eDumpOptionType)
-      strm.Printf(" =%s", (m_values.size() > 0) ? "\n" : "");
-    strm.IndentMore();
+    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 (!one_line)
+      strm.IndentMore();
     for (uint32_t i = 0; i < size; ++i) {
-      strm.Indent();
-      strm.Printf("[%u]: ", i);
+      if (!one_line) {
+        strm.Indent();
+        strm.Printf("[%u]: ", i);
+      }
       const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0;
       switch (array_element_type) {
       default:
@@ -63,10 +67,16 @@
                                                   extra_dump_options);
         break;
       }
-      if (i < (size - 1))
-        strm.EOL();
+
+      if (!one_line) {
+        if (i < (size - 1))
+          strm.EOL();
+      } else {
+        strm << ' ';
+      }
     }
-    strm.IndentLess();
+    if (!one_line)
+      strm.IndentLess();
   }
 }
 
Index: source/Core/Debugger.cpp
===================================================================
--- source/Core/Debugger.cpp
+++ source/Core/Debugger.cpp
@@ -162,9 +162,9 @@
 //   3. print
 //      address <+offset>:
 #define DEFAULT_DISASSEMBLY_FORMAT                                             \
-  "{${function.initial-function}{${module.file.basename}`}{${function.name-"   \
-  "without-args}}:\n}{${function.changed}\n{${module.file.basename}`}{${"      \
-  "function.name-without-args}}:\n}{${current-pc-arrow} "                      \
+  "{${function.initial-function}{${module.file.basename}\\`}{${function.name-" \
+  "without-args}}:\\n}{${function.changed}\\n{${module.file.basename}\\`}{${"  \
+  "function.name-without-args}}:\\n}{${current-pc-arrow} "                     \
   "}${addr-file-or-load}{ "                                                    \
   "<${function.concrete-only-addr-offset-no-padding}>}: "
 
Index: source/Commands/CommandObjectSettings.cpp
===================================================================
--- source/Commands/CommandObjectSettings.cpp
+++ source/Commands/CommandObjectSettings.cpp
@@ -185,7 +185,7 @@
       return false;
 
     const size_t argc = cmd_args.GetArgumentCount();
-    if ((argc < 2) && (!m_options.m_global)) {
+    if ((argc < 1) && (!m_options.m_global)) {
       result.AppendError("'settings set' takes more arguments");
       result.SetStatus(eReturnStatusFailed);
       return false;
@@ -199,6 +199,18 @@
       return false;
     }
 
+    // A missing value corresponds to clearing the setting.
+    if (argc == 1) {
+      Status error(m_interpreter.GetDebugger().SetPropertyValue(
+          &m_exe_ctx, eVarSetOperationClear, var_name, llvm::StringRef()));
+      if (error.Fail()) {
+        result.AppendError(error.AsCString());
+        result.SetStatus(eReturnStatusFailed);
+        return false;
+      }
+      return result.Succeeded();
+    }
+
     // Split the raw command into var_name and value pair.
     llvm::StringRef raw_str(command);
     std::string var_value_string = raw_str.split(var_name).second.str();
@@ -300,6 +312,63 @@
   }
 };
 
+//-------------------------------------------------------------------------
+// CommandObjectSettingsExport -- Export current values
+//-------------------------------------------------------------------------
+
+class CommandObjectSettingsExport : public CommandObjectParsed {
+public:
+  CommandObjectSettingsExport(CommandInterpreter &interpreter)
+      : CommandObjectParsed(
+            interpreter, "settings export",
+            "Export debugger settings and their current values.", nullptr) {
+    CommandArgumentEntry arg1;
+    CommandArgumentData var_name_arg;
+
+    // Define the first (and only) variant of this arg.
+    var_name_arg.arg_type = eArgTypeFilename;
+    var_name_arg.arg_repetition = eArgRepeatOptional;
+
+    // There is only one variant this argument could be; put it into the
+    // argument entry.
+    arg1.push_back(var_name_arg);
+
+    // Push the data for the first argument into the m_arguments vector.
+    m_arguments.push_back(arg1);
+  }
+
+  ~CommandObjectSettingsExport() override = default;
+
+  int HandleArgumentCompletion(
+      CompletionRequest &request,
+      OptionElementVector &opt_element_vector) override {
+    CommandCompletions::InvokeCommonCompletionCallbacks(
+        GetCommandInterpreter(), CommandCompletions::eVariablePathCompletion,
+        request, nullptr);
+    return request.GetNumberOfMatches();
+  }
+
+protected:
+  bool DoExecute(Args &args, CommandReturnObject &result) override {
+    result.SetStatus(eReturnStatusSuccessFinishResult);
+
+    // Open file for dumping the exported settings.
+    const std::string export_path = args.GetArgumentAtIndex(0);
+    const uint32_t options = File::eOpenOptionWrite |
+                             File::eOpenOptionTruncate |
+                             File::eOpenOptionCanCreate;
+    StreamFile export_file(export_path.c_str(), options);
+
+    // Exporting should not be context sensitive.
+    ExecutionContext clean_ctx;
+
+    m_interpreter.GetDebugger().DumpAllPropertyValues(
+        &clean_ctx, export_file, OptionValue::eDumpGroupExport);
+
+    return result.Succeeded();
+  }
+};
+
 //-------------------------------------------------------------------------
 // CommandObjectSettingsList -- List settable variables
 //-------------------------------------------------------------------------
@@ -987,6 +1056,8 @@
                  CommandObjectSP(new CommandObjectSettingsAppend(interpreter)));
   LoadSubCommand("clear",
                  CommandObjectSP(new CommandObjectSettingsClear(interpreter)));
+  LoadSubCommand("export",
+                 CommandObjectSP(new CommandObjectSettingsExport(interpreter)));
 }
 
 CommandObjectMultiwordSettings::~CommandObjectMultiwordSettings() = default;
Index: lit/Settings/TestExport.test
===================================================================
--- /dev/null
+++ lit/Settings/TestExport.test
@@ -0,0 +1,10 @@
+# This tests exporting settings from LLDB. It checks that the settings can be
+# written to file and read again without altering any of the values.
+
+# FIXME: The disassembly-format is problematic because its default value is
+#        rejected without escaping the back ticks.
+
+# RUN: %lldb -b -o 'settings set disassembly-format ""' -o 'settings export %t.foo' -o 'command source %t.foo' -o 'settings export %t.bar' -o 'command source %t.bar' 2>&1 | FileCheck %s
+# RUN: diff -w %t.foo %t.bar
+
+# CHECK-NOT: error:
Index: include/lldb/Interpreter/OptionValue.h
===================================================================
--- include/lldb/Interpreter/OptionValue.h
+++ include/lldb/Interpreter/OptionValue.h
@@ -58,9 +58,11 @@
     eDumpOptionValue = (1u << 2),
     eDumpOptionDescription = (1u << 3),
     eDumpOptionRaw = (1u << 4),
+    eDumpOptionCommand = (1u << 5),
     eDumpGroupValue = (eDumpOptionName | eDumpOptionType | eDumpOptionValue),
     eDumpGroupHelp =
-        (eDumpOptionName | eDumpOptionType | eDumpOptionDescription)
+        (eDumpOptionName | eDumpOptionType | eDumpOptionDescription),
+    eDumpGroupExport = (eDumpOptionCommand | eDumpOptionName | eDumpOptionValue)
   };
 
   OptionValue()
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to