/* debuginfod utilities for GDB.
- Copyright (C) 2020 Free Software Foundation, Inc.
+ Copyright (C) 2020-2022 Free Software Foundation, Inc.
This file is part of GDB.
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "defs.h"
+#include "diagnostics.h"
#include <errno.h>
-#include "cli/cli-style.h"
#include "gdbsupport/scoped_fd.h"
#include "debuginfod-support.h"
#include "gdbsupport/gdb_optional.h"
+#include "cli/cli-cmds.h"
+#include "cli/cli-style.h"
+#include "target.h"
+
+/* Set/show debuginfod commands. */
+static cmd_list_element *set_debuginfod_prefix_list;
+static cmd_list_element *show_debuginfod_prefix_list;
+
+static const char debuginfod_on[] = "on";
+static const char debuginfod_off[] = "off";
+static const char debuginfod_ask[] = "ask";
+
+static const char *debuginfod_enabled_enum[] =
+{
+ debuginfod_on,
+ debuginfod_off,
+ debuginfod_ask,
+ nullptr
+};
+
+static const char *debuginfod_enabled =
+#if defined(HAVE_LIBDEBUGINFOD)
+ debuginfod_ask;
+#else
+ debuginfod_off;
+#endif
+
+static unsigned int debuginfod_verbose = 1;
#ifndef HAVE_LIBDEBUGINFOD
scoped_fd
{
return scoped_fd (-ENOSYS);
}
+
+scoped_fd
+debuginfod_exec_query (const unsigned char *build_id,
+ int build_id_len,
+ const char *filename,
+ gdb::unique_xmalloc_ptr<char> *destname)
+{
+ return scoped_fd (-ENOSYS);
+}
+
+#define NO_IMPL _("Support for debuginfod is not compiled into GDB.")
+
#else
#include <elfutils/debuginfod.h>
struct user_data
{
user_data (const char *desc, const char *fname)
- : desc (desc), fname (fname)
+ : desc (desc), fname (fname), has_printed (false)
{ }
const char * const desc;
const char * const fname;
- gdb::optional<ui_out::progress_meter> meter;
+ bool has_printed;
};
/* Deleter for a debuginfod_client. */
progressfn (debuginfod_client *c, long cur, long total)
{
user_data *data = static_cast<user_data *> (debuginfod_get_user_data (c));
+ gdb_assert (data != nullptr);
if (check_quit_flag ())
{
- printf_filtered ("Cancelling download of %s %ps...\n",
- data->desc,
- styled_string (file_name_style.style (), data->fname));
+ gdb_printf ("Cancelling download of %s %ps...\n",
+ data->desc,
+ styled_string (file_name_style.style (), data->fname));
return 1;
}
- if (total == 0)
- return 0;
-
- if (!data->meter.has_value ())
+ if (!data->has_printed)
{
- float size_in_mb = 1.0f * total / (1024 * 1024);
- string_file styled_filename (current_uiout->can_emit_style_escape ());
- fprintf_styled (&styled_filename,
- file_name_style.style (),
- "%s",
- data->fname);
- std::string message
- = string_printf ("Downloading %.2f MB %s %s", size_in_mb, data->desc,
- styled_filename.c_str());
- data->meter.emplace (current_uiout, message, 1);
+ /* Include the transfer size, if available. */
+ if (total > 0)
+ {
+ float size = 1.0f * total / 1024;
+ const char *unit = "KB";
+
+ /* If size is greater than 0.01 MB, set unit to MB. */
+ if (size > 10.24)
+ {
+ size /= 1024;
+ unit = "MB";
+ }
+
+ gdb_printf ("Downloading %.2f %s %s %ps...\n",
+ size, unit, data->desc,
+ styled_string (file_name_style.style (),
+ data->fname));
+ }
+ else
+ gdb_printf ("Downloading %s %ps...\n", data->desc,
+ styled_string (file_name_style.style (), data->fname));
+
+ data->has_printed = true;
}
- current_uiout->progress ((double)cur / (double)total);
-
return 0;
}
-static debuginfod_client_up
-debuginfod_init ()
+static debuginfod_client *
+get_debuginfod_client ()
{
- debuginfod_client_up c (debuginfod_begin ());
+ static debuginfod_client_up global_client;
- if (c != nullptr)
- debuginfod_set_progressfn (c.get (), progressfn);
+ if (global_client == nullptr)
+ {
+ global_client.reset (debuginfod_begin ());
+
+ if (global_client != nullptr)
+ debuginfod_set_progressfn (global_client.get (), progressfn);
+ }
- return c;
+ return global_client.get ();
+}
+
+/* Check if debuginfod is enabled. If configured to do so, ask the user
+ whether to enable debuginfod. */
+
+static bool
+debuginfod_is_enabled ()
+{
+ const char *urls = skip_spaces (getenv (DEBUGINFOD_URLS_ENV_VAR));
+
+ if (debuginfod_enabled == debuginfod_off
+ || urls == nullptr
+ || *urls == '\0')
+ return false;
+
+ if (debuginfod_enabled == debuginfod_ask)
+ {
+ gdb_printf (_("\nThis GDB supports auto-downloading debuginfo " \
+ "from the following URLs:\n"));
+
+ gdb::string_view url_view (urls);
+ while (true)
+ {
+ size_t off = url_view.find_first_not_of (' ');
+ if (off == gdb::string_view::npos)
+ break;
+ url_view = url_view.substr (off);
+ /* g++ 11.2.1 on s390x, g++ 11.3.1 on ppc64le and g++ 11 on
+ hppa seem convinced url_view might be of SIZE_MAX length.
+ And so complains because the length of an array can only
+ be PTRDIFF_MAX. */
+ DIAGNOSTIC_PUSH
+ DIAGNOSTIC_IGNORE_STRINGOP_OVERREAD
+ off = url_view.find_first_of (' ');
+ DIAGNOSTIC_POP
+ gdb_printf
+ (_(" <%ps>\n"),
+ styled_string (file_name_style.style (),
+ gdb::to_string (url_view.substr (0,
+ off)).c_str ()));
+ if (off == gdb::string_view::npos)
+ break;
+ url_view = url_view.substr (off);
+ }
+
+ int resp = nquery (_("Enable debuginfod for this session? "));
+ if (!resp)
+ {
+ gdb_printf (_("Debuginfod has been disabled.\nTo make this " \
+ "setting permanent, add \'set debuginfod " \
+ "enabled off\' to .gdbinit.\n"));
+ debuginfod_enabled = debuginfod_off;
+ return false;
+ }
+
+ gdb_printf (_("Debuginfod has been enabled.\nTo make this " \
+ "setting permanent, add \'set debuginfod enabled " \
+ "on\' to .gdbinit.\n"));
+ debuginfod_enabled = debuginfod_on;
+ }
+
+ return true;
}
/* See debuginfod-support.h */
const char *srcpath,
gdb::unique_xmalloc_ptr<char> *destname)
{
- const char *urls_env_var = getenv (DEBUGINFOD_URLS_ENV_VAR);
- if (urls_env_var == NULL || urls_env_var[0] == '\0')
+ if (!debuginfod_is_enabled ())
return scoped_fd (-ENOSYS);
- debuginfod_client_up c = debuginfod_init ();
+ debuginfod_client *c = get_debuginfod_client ();
if (c == nullptr)
return scoped_fd (-ENOMEM);
+ char *dname = nullptr;
user_data data ("source file", srcpath);
- debuginfod_set_user_data (c.get (), &data);
- scoped_fd fd (debuginfod_find_source (c.get (),
+ debuginfod_set_user_data (c, &data);
+ gdb::optional<target_terminal::scoped_restore_terminal_state> term_state;
+ if (target_supports_terminal_ours ())
+ {
+ term_state.emplace ();
+ target_terminal::ours ();
+ }
+
+ scoped_fd fd (debuginfod_find_source (c,
build_id,
build_id_len,
srcpath,
- nullptr));
+ &dname));
+ debuginfod_set_user_data (c, nullptr);
- /* TODO: Add 'set debug debuginfod' command to control when error messages are shown. */
if (fd.get () < 0 && fd.get () != -ENOENT)
- printf_filtered (_("Download failed: %s. Continuing without source file %ps.\n"),
- safe_strerror (-fd.get ()),
- styled_string (file_name_style.style (), srcpath));
+ gdb_printf (_("Download failed: %s. Continuing without source file %ps.\n"),
+ safe_strerror (-fd.get ()),
+ styled_string (file_name_style.style (), srcpath));
if (fd.get () >= 0)
- *destname = make_unique_xstrdup (srcpath);
+ destname->reset (dname);
return fd;
}
const char *filename,
gdb::unique_xmalloc_ptr<char> *destname)
{
- const char *urls_env_var = getenv (DEBUGINFOD_URLS_ENV_VAR);
- if (urls_env_var == NULL || urls_env_var[0] == '\0')
+ if (!debuginfod_is_enabled ())
return scoped_fd (-ENOSYS);
- debuginfod_client_up c = debuginfod_init ();
+ debuginfod_client *c = get_debuginfod_client ();
if (c == nullptr)
return scoped_fd (-ENOMEM);
char *dname = nullptr;
user_data data ("separate debug info for", filename);
- debuginfod_set_user_data (c.get (), &data);
- scoped_fd fd (debuginfod_find_debuginfo (c.get (), build_id, build_id_len,
+ debuginfod_set_user_data (c, &data);
+ gdb::optional<target_terminal::scoped_restore_terminal_state> term_state;
+ if (target_supports_terminal_ours ())
+ {
+ term_state.emplace ();
+ target_terminal::ours ();
+ }
+
+ scoped_fd fd (debuginfod_find_debuginfo (c, build_id, build_id_len,
&dname));
+ debuginfod_set_user_data (c, nullptr);
+
+ if (fd.get () < 0 && fd.get () != -ENOENT)
+ gdb_printf (_("Download failed: %s. Continuing without debug info for %ps.\n"),
+ safe_strerror (-fd.get ()),
+ styled_string (file_name_style.style (), filename));
+
+ if (fd.get () >= 0)
+ destname->reset (dname);
+
+ return fd;
+}
+
+/* See debuginfod-support.h */
+
+scoped_fd
+debuginfod_exec_query (const unsigned char *build_id,
+ int build_id_len,
+ const char *filename,
+ gdb::unique_xmalloc_ptr<char> *destname)
+{
+ if (!debuginfod_is_enabled ())
+ return scoped_fd (-ENOSYS);
+
+ debuginfod_client *c = get_debuginfod_client ();
+
+ if (c == nullptr)
+ return scoped_fd (-ENOMEM);
+
+ char *dname = nullptr;
+ user_data data ("executable for", filename);
+
+ debuginfod_set_user_data (c, &data);
+ gdb::optional<target_terminal::scoped_restore_terminal_state> term_state;
+ if (target_supports_terminal_ours ())
+ {
+ term_state.emplace ();
+ target_terminal::ours ();
+ }
+
+ scoped_fd fd (debuginfod_find_executable (c, build_id, build_id_len, &dname));
+ debuginfod_set_user_data (c, nullptr);
if (fd.get () < 0 && fd.get () != -ENOENT)
- printf_filtered (_("Download failed: %s. Continuing without debug info for %ps.\n"),
- safe_strerror (-fd.get ()),
- styled_string (file_name_style.style (), filename));
+ gdb_printf (_("Download failed: %s. " \
+ "Continuing without executable for %ps.\n"),
+ safe_strerror (-fd.get ()),
+ styled_string (file_name_style.style (), filename));
if (fd.get () >= 0)
destname->reset (dname);
return fd;
}
#endif
+
+/* Set callback for "set debuginfod enabled". */
+
+static void
+set_debuginfod_enabled (const char *value)
+{
+#if defined(HAVE_LIBDEBUGINFOD)
+ debuginfod_enabled = value;
+#else
+ /* Disabling debuginfod when gdb is not built with it is a no-op. */
+ if (value != debuginfod_off)
+ error (NO_IMPL);
+#endif
+}
+
+/* Get callback for "set debuginfod enabled". */
+
+static const char *
+get_debuginfod_enabled ()
+{
+ return debuginfod_enabled;
+}
+
+/* Show callback for "set debuginfod enabled". */
+
+static void
+show_debuginfod_enabled (ui_file *file, int from_tty, cmd_list_element *cmd,
+ const char *value)
+{
+ gdb_printf (file,
+ _("Debuginfod functionality is currently set to "
+ "\"%s\".\n"), debuginfod_enabled);
+}
+
+/* Set callback for "set debuginfod urls". */
+
+static void
+set_debuginfod_urls (const std::string &urls)
+{
+#if defined(HAVE_LIBDEBUGINFOD)
+ if (setenv (DEBUGINFOD_URLS_ENV_VAR, urls.c_str (), 1) != 0)
+ warning (_("Unable to set debuginfod URLs: %s"), safe_strerror (errno));
+#else
+ error (NO_IMPL);
+#endif
+}
+
+/* Get callback for "set debuginfod urls". */
+
+static const std::string&
+get_debuginfod_urls ()
+{
+ static std::string urls;
+#if defined(HAVE_LIBDEBUGINFOD)
+ const char *envvar = getenv (DEBUGINFOD_URLS_ENV_VAR);
+
+ if (envvar != nullptr)
+ urls = envvar;
+ else
+ urls.clear ();
+#endif
+
+ return urls;
+}
+
+/* Show callback for "set debuginfod urls". */
+
+static void
+show_debuginfod_urls (ui_file *file, int from_tty, cmd_list_element *cmd,
+ const char *value)
+{
+ if (value[0] == '\0')
+ gdb_printf (file, _("Debuginfod URLs have not been set.\n"));
+ else
+ gdb_printf (file, _("Debuginfod URLs are currently set to:\n%s\n"),
+ value);
+}
+
+/* Show callback for "set debuginfod verbose". */
+
+static void
+show_debuginfod_verbose_command (ui_file *file, int from_tty,
+ cmd_list_element *cmd, const char *value)
+{
+ gdb_printf (file, _("Debuginfod verbose output is set to %s.\n"),
+ value);
+}
+
+/* Register debuginfod commands. */
+
+void _initialize_debuginfod ();
+void
+_initialize_debuginfod ()
+{
+ /* set/show debuginfod */
+ add_setshow_prefix_cmd ("debuginfod", class_run,
+ _("Set debuginfod options."),
+ _("Show debuginfod options."),
+ &set_debuginfod_prefix_list,
+ &show_debuginfod_prefix_list,
+ &setlist, &showlist);
+
+ add_setshow_enum_cmd ("enabled", class_run, debuginfod_enabled_enum,
+ _("Set whether to use debuginfod."),
+ _("Show whether to use debuginfod."),
+ _("\
+When on, enable the use of debuginfod to download missing debug info and\n\
+source files."),
+ set_debuginfod_enabled,
+ get_debuginfod_enabled,
+ show_debuginfod_enabled,
+ &set_debuginfod_prefix_list,
+ &show_debuginfod_prefix_list);
+
+ /* set/show debuginfod urls */
+ add_setshow_string_noescape_cmd ("urls", class_run, _("\
+Set the list of debuginfod server URLs."), _("\
+Show the list of debuginfod server URLs."), _("\
+Manage the space-separated list of debuginfod server URLs that GDB will query \
+when missing debuginfo, executables or source files.\nThe default value is \
+copied from the DEBUGINFOD_URLS environment variable."),
+ set_debuginfod_urls,
+ get_debuginfod_urls,
+ show_debuginfod_urls,
+ &set_debuginfod_prefix_list,
+ &show_debuginfod_prefix_list);
+
+ /* set/show debuginfod verbose */
+ add_setshow_zuinteger_cmd ("verbose", class_support,
+ &debuginfod_verbose, _("\
+Set verbosity of debuginfod output."), _("\
+Show debuginfod debugging."), _("\
+When set to a non-zero value, display verbose output for each debuginfod \
+query.\nTo disable, set to zero. Verbose output is displayed by default."),
+ nullptr,
+ show_debuginfod_verbose_command,
+ &set_debuginfod_prefix_list,
+ &show_debuginfod_prefix_list);
+}