Skip to content

Commit b76e25a

Browse files
committed
Add functionality to export settings
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 -f /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 ... You can import the settings again by either sourcing the file or using the settings read command. (lldb) settings read -f /path/to/file Differential revision: https://reviews.llvm.org/D52651 llvm-svn: 345346
1 parent 24faf85 commit b76e25a

9 files changed

+290
-21
lines changed

lldb/include/lldb/Interpreter/OptionValue.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,11 @@ class OptionValue {
5858
eDumpOptionValue = (1u << 2),
5959
eDumpOptionDescription = (1u << 3),
6060
eDumpOptionRaw = (1u << 4),
61+
eDumpOptionCommand = (1u << 5),
6162
eDumpGroupValue = (eDumpOptionName | eDumpOptionType | eDumpOptionValue),
6263
eDumpGroupHelp =
63-
(eDumpOptionName | eDumpOptionType | eDumpOptionDescription)
64+
(eDumpOptionName | eDumpOptionType | eDumpOptionDescription),
65+
eDumpGroupExport = (eDumpOptionCommand | eDumpOptionName | eDumpOptionValue)
6466
};
6567

6668
OptionValue()

lldb/lit/Settings/TestExport.test

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# This tests writing and reading settings from LLDB.
2+
3+
# Check that the settings can be written to file and read again without
4+
# altering the values.
5+
# RUN: %lldb -b -o 'settings write -f %t.foo' -o 'settings read -f %t.foo' -o 'settings export %t.bar' -o 'settings read -f %t.bar' 2>&1 | FileCheck %s --check-prefix SUCCESS
6+
# RUN: diff -w %t.foo %t.bar
7+
# SUCCESS-NOT: error:
8+
9+
# Check that exporting target settings only export target settings and nothing else.
10+
# RUN: %lldb -b -o 'settings write -f %t.target target' 2>&1 | FileCheck %s --check-prefix SUCCESS
11+
# RUN: cat %t.target | FileCheck %s --check-prefix TARGET
12+
# TARGET: settings set target
13+
# TARGET-NOT: settings set platform
14+
# TARGET-NOT: settings set symbols
15+
# TARGET-NOT: settings set interpreter
16+
# TARGET-NOT: settings set plugin
17+
18+
# Check that settings appear twice when appending.
19+
# RUN: %lldb -b -o 'settings write -a -f %t.append target' -o 'settings write -a -f %t.append target' 2>&1 | FileCheck %s --check-prefix SUCCESS
20+
# RUN: cat %t.append | FileCheck %s --check-prefix APPEND
21+
# APPEND: settings set target.language
22+
# APPEND: settings set target.language
23+
24+
# Check that an error is printed for non-existing setting.
25+
# RUN: echo "settings set bogus" > %t.bogus_setting
26+
# RUN: %lldb -b -o 'settings read -f %t.bogus_setting' 2>&1 | FileCheck %s --check-prefix BOGUS-SETTING
27+
# BOGUS-SETTING: error: invalid value path
28+
29+
# Check that an error is printed for invalid value.
30+
# RUN: echo "settings set target.language bogus" > %t.bogus_value
31+
# RUN: %lldb -b -o 'settings read -f %t.bogus_value' 2>&1 | FileCheck %s --check-prefix BOGUS-VALUE
32+
# BOGUS-VALUE: error: invalid language type

lldb/source/Commands/CommandObjectSettings.cpp

+205
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,207 @@ class CommandObjectSettingsShow : public CommandObjectParsed {
320320
}
321321
};
322322

323+
//-------------------------------------------------------------------------
324+
// CommandObjectSettingsWrite -- Write settings to file
325+
//-------------------------------------------------------------------------
326+
327+
static constexpr OptionDefinition g_settings_write_options[] = {
328+
// clang-format off
329+
{ LLDB_OPT_SET_ALL, true, "file", 'f', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eDiskFileCompletion, eArgTypeFilename, "The file into which to write the settings." },
330+
{ LLDB_OPT_SET_ALL, false, "append",'a', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Append to saved settings file if it exists."},
331+
// clang-format on
332+
};
333+
334+
class CommandObjectSettingsWrite : public CommandObjectParsed {
335+
public:
336+
CommandObjectSettingsWrite(CommandInterpreter &interpreter)
337+
: CommandObjectParsed(
338+
interpreter, "settings export",
339+
"Write matching debugger settings and their "
340+
"current values to a file that can be read in with "
341+
"\"settings read\". Defaults to writing all settings.",
342+
nullptr),
343+
m_options() {
344+
CommandArgumentEntry arg1;
345+
CommandArgumentData var_name_arg;
346+
347+
// Define the first (and only) variant of this arg.
348+
var_name_arg.arg_type = eArgTypeSettingVariableName;
349+
var_name_arg.arg_repetition = eArgRepeatOptional;
350+
351+
// There is only one variant this argument could be; put it into the
352+
// argument entry.
353+
arg1.push_back(var_name_arg);
354+
355+
// Push the data for the first argument into the m_arguments vector.
356+
m_arguments.push_back(arg1);
357+
}
358+
359+
~CommandObjectSettingsWrite() override = default;
360+
361+
Options *GetOptions() override { return &m_options; }
362+
363+
class CommandOptions : public Options {
364+
public:
365+
CommandOptions() : Options() {}
366+
367+
~CommandOptions() override = default;
368+
369+
Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
370+
ExecutionContext *execution_context) override {
371+
Status error;
372+
const int short_option = m_getopt_table[option_idx].val;
373+
374+
switch (short_option) {
375+
case 'f':
376+
m_filename.assign(option_arg);
377+
break;
378+
case 'a':
379+
m_append = true;
380+
break;
381+
default:
382+
error.SetErrorStringWithFormat("unrecognized option '%c'",
383+
short_option);
384+
break;
385+
}
386+
387+
return error;
388+
}
389+
390+
void OptionParsingStarting(ExecutionContext *execution_context) override {
391+
m_filename.clear();
392+
m_append = false;
393+
}
394+
395+
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
396+
return llvm::makeArrayRef(g_settings_write_options);
397+
}
398+
399+
// Instance variables to hold the values for command options.
400+
std::string m_filename;
401+
bool m_append = false;
402+
};
403+
404+
protected:
405+
bool DoExecute(Args &args, CommandReturnObject &result) override {
406+
std::string path(FileSpec(m_options.m_filename, true).GetPath());
407+
uint32_t options = File::OpenOptions::eOpenOptionWrite |
408+
File::OpenOptions::eOpenOptionCanCreate;
409+
if (m_options.m_append)
410+
options |= File::OpenOptions::eOpenOptionAppend;
411+
else
412+
options |= File::OpenOptions::eOpenOptionTruncate;
413+
414+
StreamFile out_file(path.c_str(), options,
415+
lldb::eFilePermissionsFileDefault);
416+
417+
if (!out_file.GetFile().IsValid()) {
418+
result.AppendErrorWithFormat("%s: unable to write to file", path.c_str());
419+
result.SetStatus(eReturnStatusFailed);
420+
return false;
421+
}
422+
423+
// Exporting should not be context sensitive.
424+
ExecutionContext clean_ctx;
425+
426+
if (args.empty()) {
427+
m_interpreter.GetDebugger().DumpAllPropertyValues(
428+
&clean_ctx, out_file, OptionValue::eDumpGroupExport);
429+
return result.Succeeded();
430+
}
431+
432+
for (const auto &arg : args) {
433+
Status error(m_interpreter.GetDebugger().DumpPropertyValue(
434+
&clean_ctx, out_file, arg.ref, OptionValue::eDumpGroupExport));
435+
if (!error.Success()) {
436+
result.AppendError(error.AsCString());
437+
result.SetStatus(eReturnStatusFailed);
438+
}
439+
}
440+
441+
return result.Succeeded();
442+
}
443+
444+
private:
445+
CommandOptions m_options;
446+
};
447+
448+
//-------------------------------------------------------------------------
449+
// CommandObjectSettingsRead -- Read settings from file
450+
//-------------------------------------------------------------------------
451+
452+
static constexpr OptionDefinition g_settings_read_options[] = {
453+
// clang-format off
454+
{LLDB_OPT_SET_ALL, true, "file",'f', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eDiskFileCompletion, eArgTypeFilename, "The file from which to read the breakpoints." },
455+
// clang-format on
456+
};
457+
458+
class CommandObjectSettingsRead : public CommandObjectParsed {
459+
public:
460+
CommandObjectSettingsRead(CommandInterpreter &interpreter)
461+
: CommandObjectParsed(
462+
interpreter, "settings read",
463+
"Read settings previously saved to a file with \"settings write\".",
464+
nullptr),
465+
m_options() {}
466+
467+
~CommandObjectSettingsRead() override = default;
468+
469+
Options *GetOptions() override { return &m_options; }
470+
471+
class CommandOptions : public Options {
472+
public:
473+
CommandOptions() : Options() {}
474+
475+
~CommandOptions() override = default;
476+
477+
Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
478+
ExecutionContext *execution_context) override {
479+
Status error;
480+
const int short_option = m_getopt_table[option_idx].val;
481+
482+
switch (short_option) {
483+
case 'f':
484+
m_filename.assign(option_arg);
485+
break;
486+
default:
487+
error.SetErrorStringWithFormat("unrecognized option '%c'",
488+
short_option);
489+
break;
490+
}
491+
492+
return error;
493+
}
494+
495+
void OptionParsingStarting(ExecutionContext *execution_context) override {
496+
m_filename.clear();
497+
}
498+
499+
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
500+
return llvm::makeArrayRef(g_settings_read_options);
501+
}
502+
503+
// Instance variables to hold the values for command options.
504+
std::string m_filename;
505+
};
506+
507+
protected:
508+
bool DoExecute(Args &command, CommandReturnObject &result) override {
509+
FileSpec file(m_options.m_filename, true);
510+
ExecutionContext clean_ctx;
511+
CommandInterpreterRunOptions options;
512+
options.SetAddToHistory(false);
513+
options.SetEchoCommands(false);
514+
options.SetPrintResults(true);
515+
options.SetStopOnError(false);
516+
m_interpreter.HandleCommandsFromFile(file, &clean_ctx, options, result);
517+
return result.Succeeded();
518+
}
519+
520+
private:
521+
CommandOptions m_options;
522+
};
523+
323524
//-------------------------------------------------------------------------
324525
// CommandObjectSettingsList -- List settable variables
325526
//-------------------------------------------------------------------------
@@ -1007,6 +1208,10 @@ CommandObjectMultiwordSettings::CommandObjectMultiwordSettings(
10071208
CommandObjectSP(new CommandObjectSettingsAppend(interpreter)));
10081209
LoadSubCommand("clear",
10091210
CommandObjectSP(new CommandObjectSettingsClear(interpreter)));
1211+
LoadSubCommand("write",
1212+
CommandObjectSP(new CommandObjectSettingsWrite(interpreter)));
1213+
LoadSubCommand("read",
1214+
CommandObjectSP(new CommandObjectSettingsRead(interpreter)));
10101215
}
10111216

10121217
CommandObjectMultiwordSettings::~CommandObjectMultiwordSettings() = default;

lldb/source/Interpreter/OptionValueArray.cpp

+18-8
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,17 @@ void OptionValueArray::DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
3131
strm.Printf("(%s)", GetTypeAsCString());
3232
}
3333
if (dump_mask & eDumpOptionValue) {
34-
if (dump_mask & eDumpOptionType)
35-
strm.Printf(" =%s", (m_values.size() > 0) ? "\n" : "");
36-
strm.IndentMore();
34+
const bool one_line = dump_mask & eDumpOptionCommand;
3735
const uint32_t size = m_values.size();
36+
if (dump_mask & eDumpOptionType)
37+
strm.Printf(" =%s", (m_values.size() > 0 && !one_line) ? "\n" : "");
38+
if (!one_line)
39+
strm.IndentMore();
3840
for (uint32_t i = 0; i < size; ++i) {
39-
strm.Indent();
40-
strm.Printf("[%u]: ", i);
41+
if (!one_line) {
42+
strm.Indent();
43+
strm.Printf("[%u]: ", i);
44+
}
4145
const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0;
4246
switch (array_element_type) {
4347
default:
@@ -63,10 +67,16 @@ void OptionValueArray::DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
6367
extra_dump_options);
6468
break;
6569
}
66-
if (i < (size - 1))
67-
strm.EOL();
70+
71+
if (!one_line) {
72+
if (i < (size - 1))
73+
strm.EOL();
74+
} else {
75+
strm << ' ';
76+
}
6877
}
69-
strm.IndentLess();
78+
if (!one_line)
79+
strm.IndentLess();
7080
}
7181
}
7282

lldb/source/Interpreter/OptionValueDictionary.cpp

+11-3
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,23 @@ void OptionValueDictionary::DumpValue(const ExecutionContext *exe_ctx,
3333
strm.Printf("(%s)", GetTypeAsCString());
3434
}
3535
if (dump_mask & eDumpOptionValue) {
36+
const bool one_line = dump_mask & eDumpOptionCommand;
3637
if (dump_mask & eDumpOptionType)
3738
strm.PutCString(" =");
3839

3940
collection::iterator pos, end = m_values.end();
4041

41-
strm.IndentMore();
42+
if (!one_line)
43+
strm.IndentMore();
4244

4345
for (pos = m_values.begin(); pos != end; ++pos) {
4446
OptionValue *option_value = pos->second.get();
45-
strm.EOL();
47+
48+
if (one_line)
49+
strm << ' ';
50+
else
51+
strm.EOL();
52+
4653
strm.Indent(pos->first.GetCString());
4754

4855
const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0;
@@ -74,7 +81,8 @@ void OptionValueDictionary::DumpValue(const ExecutionContext *exe_ctx,
7481
break;
7582
}
7683
}
77-
strm.IndentLess();
84+
if (!one_line)
85+
strm.IndentLess();
7886
}
7987
}
8088

lldb/source/Interpreter/OptionValueFileSpecLIst.cpp

+14-6
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,24 @@ void OptionValueFileSpecList::DumpValue(const ExecutionContext *exe_ctx,
2525
if (dump_mask & eDumpOptionType)
2626
strm.Printf("(%s)", GetTypeAsCString());
2727
if (dump_mask & eDumpOptionValue) {
28-
if (dump_mask & eDumpOptionType)
29-
strm.Printf(" =%s", m_current_value.GetSize() > 0 ? "\n" : "");
30-
strm.IndentMore();
28+
const bool one_line = dump_mask & eDumpOptionCommand;
3129
const uint32_t size = m_current_value.GetSize();
30+
if (dump_mask & eDumpOptionType)
31+
strm.Printf(" =%s",
32+
(m_current_value.GetSize() > 0 && !one_line) ? "\n" : "");
33+
if (!one_line)
34+
strm.IndentMore();
3235
for (uint32_t i = 0; i < size; ++i) {
33-
strm.Indent();
34-
strm.Printf("[%u]: ", i);
36+
if (!one_line) {
37+
strm.Indent();
38+
strm.Printf("[%u]: ", i);
39+
}
3540
m_current_value.GetFileSpecAtIndex(i).Dump(&strm);
41+
if (one_line)
42+
strm << ' ';
3643
}
37-
strm.IndentLess();
44+
if (!one_line)
45+
strm.IndentLess();
3846
}
3947
}
4048

lldb/source/Interpreter/OptionValueFormatEntity.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,10 @@ void OptionValueFormatEntity::DumpValue(const ExecutionContext *exe_ctx,
6161
strm.Printf("(%s)", GetTypeAsCString());
6262
if (dump_mask & eDumpOptionValue) {
6363
if (dump_mask & eDumpOptionType)
64-
strm.PutCString(" = \"");
64+
strm.PutCString(" = ");
6565
std::string escaped;
6666
EscapeBackticks(m_current_format, escaped);
67-
strm << escaped << '"';
67+
strm << '"' << escaped << '"';
6868
}
6969
}
7070

lldb/source/Interpreter/OptionValueLanguage.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ void OptionValueLanguage::DumpValue(const ExecutionContext *exe_ctx,
2828
if (dump_mask & eDumpOptionValue) {
2929
if (dump_mask & eDumpOptionType)
3030
strm.PutCString(" = ");
31-
strm.PutCString(Language::GetNameForLanguageType(m_current_value));
31+
if (m_current_value != eLanguageTypeUnknown)
32+
strm.PutCString(Language::GetNameForLanguageType(m_current_value));
3233
}
3334
}
3435

0 commit comments

Comments
 (0)