123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488 |
- /* debuginfod utilities for GDB.
- Copyright (C) 2020-2022 Free Software Foundation, Inc.
- This file is part of GDB.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
- #include "defs.h"
- #include <errno.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
- debuginfod_source_query (const unsigned char *build_id,
- int build_id_len,
- const char *srcpath,
- gdb::unique_xmalloc_ptr<char> *destname)
- {
- return scoped_fd (-ENOSYS);
- }
- scoped_fd
- debuginfod_debuginfo_query (const unsigned char *build_id,
- int build_id_len,
- const char *filename,
- gdb::unique_xmalloc_ptr<char> *destname)
- {
- 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), has_printed (false)
- { }
- const char * const desc;
- const char * const fname;
- bool has_printed;
- };
- /* Deleter for a debuginfod_client. */
- struct debuginfod_client_deleter
- {
- void operator() (debuginfod_client *c)
- {
- debuginfod_end (c);
- }
- };
- using debuginfod_client_up
- = std::unique_ptr<debuginfod_client, debuginfod_client_deleter>;
- static int
- 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 ())
- {
- gdb_printf ("Cancelling download of %s %ps...\n",
- data->desc,
- styled_string (file_name_style.style (), data->fname));
- return 1;
- }
- if (!data->has_printed)
- {
- /* 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;
- }
- return 0;
- }
- static debuginfod_client *
- get_debuginfod_client ()
- {
- static debuginfod_client_up global_client;
- if (global_client == nullptr)
- {
- global_client.reset (debuginfod_begin ());
- if (global_client != nullptr)
- debuginfod_set_progressfn (global_client.get (), progressfn);
- }
- 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 = getenv (DEBUGINFOD_URLS_ENV_VAR);
- if (urls == nullptr || urls[0] == '\0'
- || debuginfod_enabled == debuginfod_off)
- 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)
- {
- url_view = url_view.substr (url_view.find_first_not_of (' '));
- if (url_view.empty ())
- break;
- size_t off = url_view.find_first_of (' ');
- 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 */
- scoped_fd
- debuginfod_source_query (const unsigned char *build_id,
- int build_id_len,
- const char *srcpath,
- 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 ("source file", srcpath);
- 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,
- &dname));
- debuginfod_set_user_data (c, nullptr);
- if (fd.get () < 0 && fd.get () != -ENOENT)
- 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->reset (dname);
- return fd;
- }
- /* See debuginfod-support.h */
- scoped_fd
- debuginfod_debuginfo_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 ("separate debug info 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_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)
- 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
- 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);
- }
|