[PATCH 5/7] gdb: add mechanism to auto-save startup options

Andrew Burgess
2020-10-07T19:05:41Z

As we add settings to GDB (in the following patches) that effect GDB's
startup behaviour a user might desire a mechanism to automatically
save these options into a file.

The reason that this could be important is that, unlike other,
non-startup options, the startup options will only effect GDB's
startup process. As a result setting the option within a GDB session
will not change how the user experiences that session, but could only
effect the following sessions.

We could take the position that it is entirely the users
responsibility to add these options to a startup file and maintain
them, however, this commit provides a mechanism to automate that
management.

If a user adds the following lines to ~/.config/gdb/gdbstartup:

set startup-save-filename ~/.config/gdb/auto-save
source ~/.config/gdb/auto-save

Then GDB will write all startup options to the file auto-save file any
time one of the options is modified. The source ensures that the
options are reloaded correctly.

This auto saving feature is currently off by default.

There are currently no options that can be auto-saved in this way, but
later commits will add some.

gdb/ChangeLog:

* cli/cli-setshow.c: Add 'main.h' and 'cli/cli-style.h' include.
(startup_save_filename): New static global.
(show_startup_save_filename): New function.
(write_startup_functions): New static global.
(write_startup_file): New function.
(default_startup_writer_callback): New function.
(add_startup_writer): New function.
(add_default_startup_writer): New function.
(_initialize_cli_setshow): New function.
* cli/cli-setshow.h (write_startup_file): Declare.
(write_startup_setting_ftype): New typedef.
(add_startup_writer): Declare.
(add_default_startup_writer): Declare.
* main.c (startup_file_read): New static global.
(gdb_startup_file_read_p): New function.
(captured_main_1): Set startup_file_read.
* main.h (gdb_startup_file_read_p): Declare.

gdb/doc/ChangeLog:

* gdb.texinfo (Commands): Add link to new section.
(Automatically saving startup options): New section.
---
gdb/ChangeLog | 20 +++++++
gdb/cli/cli-setshow.c | 122 +++++++++++++++++++++++++++++++++++++++++-
gdb/cli/cli-setshow.h | 23 ++++++++
gdb/doc/ChangeLog | 5 ++
gdb/doc/gdb.texinfo | 29 ++++++++++
gdb/main.c | 12 +++++
gdb/main.h | 5 ++
7 files changed, 215 insertions(+), 1 deletion(-)

diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c
index 19a5565bfe0..cdbb99f8a2d 100644
--- a/gdb/cli/cli-setshow.c
+++ b/gdb/cli/cli-setshow.c
@@ -21,6 +21,7 @@
#include <ctype.h>
#include "arch-utils.h"
#include "observable.h"
+#include "main.h"

#include "ui-out.h"

@@ -28,6 +29,110 @@
#include "cli/cli-cmds.h"
#include "cli/cli-setshow.h"
#include "cli/cli-utils.h"
+#include "cli/cli-style.h"
+
+/* Filename where startup options are written to when modified. If this
+ is NULL or the empty string then startup options are not automatically
+ saved when set. */
+
+static char *startup_save_filename;
+
+/* Implement 'show startup-save-filename'. */
+
+static void
+show_startup_save_filename (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ if (startup_save_filename == NULL
+ || *startup_save_filename == '\0')
+ fprintf_filtered (file,
+ _("Startup options will not be written to and file.\n"));
+ else
+ fprintf_filtered (file, _("Startup options will be written to \"%ps\".\n"),
+ styled_string (file_name_style.style (), value));
+}
+
+/* Callbacks that will be called to write out startup settings. */
+
+static std::vector<std::pair<write_startup_setting_ftype *,
+ const cmd_list_element *>> write_startup_functions;
+
+/* See cli-setshow.h. */
+
+void
+write_startup_file ()
+{
+ if (!gdb_startup_file_read_p ())
+ return;
+
+ /* Saving of these options is disabled. */
+ if (startup_save_filename == NULL
+ || *startup_save_filename == '\0')
+ {
+ static bool warned = false;
+ if (!warned)
+ {
+ warning (_("auto-save of startup options is disabled. Use `set "
+ "startup-save-filename FILENAME` to save startup "
+ "options."));
+ warned = true;
+ }
+ return;
+ }
+
+ stdio_file outfile;
+
+ if (!outfile.open (startup_save_filename, FOPEN_WT))
+ perror_with_name (startup_save_filename);
+
+ fprintf_unfiltered (&outfile, "\
+# This file is written by gdb whenever the relevant settings are changed.\n\
+# Any edits you make here will be overwritten by gdb.\n");
+
+ for (auto &callback_and_cmd : write_startup_functions)
+ callback_and_cmd.first (&outfile, callback_and_cmd.second);
+}
+
+/* A default callback that can be used to write the value of CMD into the
+ startup configuration file. All the required information is extracted
+ from CMD and the result written to OUTFILE. */
+
+static void
+default_startup_writer_callback (ui_file *outfile,
+ const cmd_list_element *cmd)
+{
+ std::string str;
+ std::function<void(const cmd_list_element *)> build_cmd;
+ build_cmd = [&] (const cmd_list_element *c) -> void
+ {
+ if (c->prefix != nullptr)
+ build_cmd (c->prefix);
+ (str += c->name) += " ";
+ };
+
+ build_cmd (cmd);
+ str += get_setshow_command_value_string (cmd);
+ str += "\n";
+ fputs_unfiltered (str.c_str (), outfile);
+}
+
+/* See cli-setshow.h. */
+
+void
+add_startup_writer (write_startup_setting_ftype *callback,
+ const cmd_list_element *cmd)
+{
+ write_startup_functions.emplace_back (callback, cmd);
+}
+
+/* See cli-setshow.h. */
+
+void
+add_default_startup_writer (const cmd_list_element *cmd)
+{
+ gdb_assert (cmd != nullptr);
+ add_startup_writer (default_startup_writer_callback, cmd);
+}

/* Return true if the change of command parameter should be notified. */

@@ -774,4 +879,19 @@ cmd_show_list (struct cmd_list_element *list, int from_tty)
}
}

-
+extern void _initialize_cli_setshow ();
+void
+_initialize_cli_setshow ()
+{
+ add_setshow_filename_cmd ("startup-save-filename", class_support,
+ &startup_save_filename, _("\
+Set the filename to which startup options should be written."), _("\
+Show the filename to which startup options are written."), _("\
+Some options only effect the startup of GDB. When these options are\n\
+modified GDB can arrange to write the values of all of these options\n\
+to a file which can then be sourced during startup to reapply these\n\
+options."),
+ NULL,
+ show_startup_save_filename,
+ &setlist, &showlist);
+}
diff --git a/gdb/cli/cli-setshow.h b/gdb/cli/cli-setshow.h
index 83e4984ed6c..3bc856a3b84 100644
--- a/gdb/cli/cli-setshow.h
+++ b/gdb/cli/cli-setshow.h
@@ -62,4 +62,27 @@ extern std::string get_setshow_command_value_string (const cmd_list_element *c);

extern void cmd_show_list (struct cmd_list_element *list, int from_tty);

+/* Write the file of gdb "set" commands that is read early in the
+ startup sequence. */
+
+extern void write_startup_file ();
+
+/* The type of a callback function that is used when writing the
+ startup file. */
+
+class ui_file;
+typedef void write_startup_setting_ftype (ui_file *, const cmd_list_element *);
+
+/* Add a callback function that will be called when writing the
+ startup sequence. */
+
+extern void add_startup_writer (write_startup_setting_ftype *callback,
+ const cmd_list_element *cmd = nullptr);
+
+/* Add the default callback function that will be called when writing the
+ startup sequence. The default callback builds a string to set the
+ option from CMD. */
+
+extern void add_default_startup_writer (const cmd_list_element *cmd);
+
#endif /* CLI_CLI_SETSHOW_H */
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index f141616bea1..66b87608b89 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -1717,6 +1717,7 @@
* Completion:: Command completion
* Command Options:: Command options
* Command aliases default args:: Automatically prepend default arguments to user-defined aliases
+* Automatically saving startup settings:: Automatically saving startup settings
* Help:: How to ask @value{GDBN} for help
@end menu

@@ -2220,6 +2221,34 @@
For more information about the @code{with} command usage,
see @ref{Command Settings}.

+@node Automatically saving startup settings
+@section Automatically saving startup settings
+@table @code
+@kindex show startup-save-filename
+@kindex set startup-save-filename filename
+@cindex @value{GDBN} startup settings
+@item show startup-save-filename
+@itemx set startup-save-filename @var{filename}
+Some settings only effect the early startup behaviour of @value{GDBN}.
+In order to make use of these settings they will need to be added to a
+startup file, for example @file{~/.config/gdb/gdbstartup}
+(@pxref{Startup}).
+
+When @samp{startup-save-filename} is set to a valid path @value{GDBN}
+will automatically save all startup settings to this file whenever they
+are modified in a @value{GDBN} session. The save file can then be
+loaded using @samp{source} (@pxref{Command Files}).
+
+For example, these commands could be placed into
+@file{~/.config/gdb/gdbstartup} in order to have GDB automatically
+save and restore startup settings:
+
+@smallexample
+set startup-save-filename ~/.config/gdb/autosave
+source ~/.config/gdb/autosave
+@end smallexample
+@end table
+
@node Help
@section Getting Help
@cindex online documentation
diff --git a/gdb/main.c b/gdb/main.c
index 8c2240085f7..850b6b6708d 100644
--- a/gdb/main.c
+++ b/gdb/main.c
@@ -95,6 +95,17 @@ int batch_silent = 0;
int return_child_result = 0;
int return_child_result_value = -1;

+/* True after gdb has read the startup files, and processed any startup
+ command line options. */
+static bool startup_file_read = false;
+
+/* See main.h. */
+
+bool
+gdb_startup_file_read_p ()
+{
+ return startup_file_read;
+}

/* GDB as it has been invoked from the command line (i.e. argv[0]). */
static char *gdb_program_name;
@@ -1067,6 +1078,7 @@ captured_main_1 (struct captured_main_args *context)
}
execute_cmdargs (&cmdarg_vec, CMDARG_STARTUP_FILE,
CMDARG_STARTUP_COMMAND, &ret);
+ startup_file_read = true;

/* Now that gdb_init has created the initial inferior, we're in
position to set args for that inferior. */
diff --git a/gdb/main.h b/gdb/main.h
index 78be7c5c4e0..eb4e6958272 100644
--- a/gdb/main.h
+++ b/gdb/main.h
@@ -52,4 +52,9 @@ extern const char *get_gdb_program_name (void);

extern void set_gdb_data_directory (const char *new_data_dir);

+/* Return true once GDB has finished reading in the startup files, and
+ finished processing any startup command line options. */
+
+extern bool gdb_startup_file_read_p ();
+
#endif
--
2.25.4