mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-21 04:42:53 +08:00
2a8f1f4744
The 'set debuginfod' change caused a regression in unittest.exp: Running selftest help_doc_invariants. help doc broken invariant: command 'info set debuginfod' help doc first line is not terminated with a '.' character help doc broken invariant: command 'set debuginfod' help doc first line is not terminated with a '.' character help doc broken invariant: command 'show debuginfod' help doc first line is not terminated with a '.' character Self test failed: self-test failed at ../../binutils-gdb/gdb/unittests/command-def-selftests.c:100 This patch fixes the problem. I'm checking it in.
458 lines
12 KiB
C
458 lines
12 KiB
C
/* debuginfod utilities for GDB.
|
|
Copyright (C) 2020-2021 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"
|
|
|
|
/* 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_enable = debuginfod_ask;
|
|
static unsigned 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);
|
|
}
|
|
|
|
#define NO_IMPL _("Support for debuginfod is not compiled into GDB.")
|
|
|
|
/* Stub set/show commands that indicate debuginfod is not supported. */
|
|
|
|
static void
|
|
set_debuginfod_on_command (const char *args, int from_tty)
|
|
{
|
|
error (NO_IMPL);
|
|
debuginfod_enable = debuginfod_off;
|
|
}
|
|
|
|
static void
|
|
set_debuginfod_off_command (const char *args, int from_tty)
|
|
{
|
|
error (NO_IMPL);
|
|
debuginfod_enable = debuginfod_off;
|
|
}
|
|
|
|
static void
|
|
set_debuginfod_ask_command (const char *args, int from_tty)
|
|
{
|
|
error (NO_IMPL);
|
|
debuginfod_enable = debuginfod_off;
|
|
}
|
|
|
|
static void
|
|
show_debuginfod_status_command (const char *args, int from_tty)
|
|
{
|
|
error (NO_IMPL);
|
|
}
|
|
|
|
static void
|
|
set_debuginfod_urls_command (const std::string& urls)
|
|
{
|
|
error (NO_IMPL);
|
|
}
|
|
|
|
static const std::string&
|
|
get_debuginfod_urls_command ()
|
|
{
|
|
static std::string empty;
|
|
return empty;
|
|
}
|
|
|
|
static void
|
|
show_debuginfod_urls_command (struct ui_file *file, int from_tty,
|
|
struct cmd_list_element *cmd, const char *value)
|
|
{
|
|
error (NO_IMPL);
|
|
}
|
|
|
|
static void
|
|
set_debuginfod_verbose_command (const char *args, int from_tty,
|
|
struct cmd_list_element *c)
|
|
{
|
|
error (NO_IMPL);
|
|
debuginfod_verbose = 0;
|
|
}
|
|
|
|
static void
|
|
show_debuginfod_verbose_command (struct ui_file *file, int from_tty,
|
|
struct cmd_list_element *cmd,
|
|
const char *value)
|
|
{
|
|
error (NO_IMPL);
|
|
}
|
|
#else
|
|
#include <elfutils/debuginfod.h>
|
|
|
|
struct user_data
|
|
{
|
|
user_data (const char *desc, const char *fname)
|
|
: desc (desc), fname (fname)
|
|
{ }
|
|
|
|
const char * const desc;
|
|
const char * const fname;
|
|
gdb::optional<ui_out::progress_meter> meter;
|
|
};
|
|
|
|
/* 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>;
|
|
|
|
/* Enable debuginfod. */
|
|
|
|
static void
|
|
set_debuginfod_on_command (const char *args, int from_tty)
|
|
{
|
|
debuginfod_enable = debuginfod_on;
|
|
}
|
|
|
|
/* Disable debuginfod. */
|
|
|
|
static void
|
|
set_debuginfod_off_command (const char *args, int from_tty)
|
|
{
|
|
debuginfod_enable = debuginfod_off;
|
|
}
|
|
|
|
/* Before next query, ask user whether to enable debuginfod. */
|
|
|
|
static void
|
|
set_debuginfod_ask_command (const char *args, int from_tty)
|
|
{
|
|
debuginfod_enable = debuginfod_ask;
|
|
}
|
|
|
|
/* Show whether debuginfod is enabled. */
|
|
|
|
static void
|
|
show_debuginfod_status_command (const char *args, int from_tty)
|
|
{
|
|
printf_unfiltered (_("Debuginfod functionality is currently set to " \
|
|
"\"%s\".\n"), debuginfod_enable);
|
|
}
|
|
|
|
/* Set the URLs that debuginfod will query. */
|
|
|
|
static void
|
|
set_debuginfod_urls_command (const std::string& urls)
|
|
{
|
|
if (setenv ("DEBUGINFOD_URLS", urls.c_str (), 1) != 0)
|
|
warning (_("Unable to set debuginfod URLs: %s"), safe_strerror (errno));
|
|
}
|
|
|
|
/* Get current debuginfod URLs. */
|
|
|
|
static const std::string&
|
|
get_debuginfod_urls_command ()
|
|
{
|
|
static std::string urls;
|
|
const char *envvar = getenv (DEBUGINFOD_URLS_ENV_VAR);
|
|
|
|
if (envvar != nullptr)
|
|
urls = envvar;
|
|
else
|
|
urls.clear ();
|
|
|
|
return urls;
|
|
}
|
|
|
|
/* Show the URLs that debuginfod will query. */
|
|
|
|
static void
|
|
show_debuginfod_urls_command (struct ui_file *file, int from_tty,
|
|
struct cmd_list_element *cmd, const char *value)
|
|
{
|
|
if (value == nullptr || value[0] == '\0')
|
|
fprintf_unfiltered (file, _("Debuginfod URLs have not been set.\n"));
|
|
else
|
|
fprintf_filtered (file, _("Debuginfod URLs are currently set to:\n%s\n"),
|
|
value);
|
|
}
|
|
|
|
/* No-op setter used for compatibility when gdb is built without debuginfod. */
|
|
|
|
static void
|
|
set_debuginfod_verbose_command (const char *args, int from_tty,
|
|
struct cmd_list_element *c)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* Show verbosity. */
|
|
|
|
static void
|
|
show_debuginfod_verbose_command (struct ui_file *file, int from_tty,
|
|
struct cmd_list_element *cmd, const char *value)
|
|
{
|
|
fprintf_filtered (file, _("Debuginfod verbose output is set to %s.\n"),
|
|
value);
|
|
}
|
|
|
|
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 ())
|
|
{
|
|
printf_filtered ("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 ())
|
|
{
|
|
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);
|
|
}
|
|
|
|
current_uiout->progress ((double)cur / (double)total);
|
|
|
|
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_enabled ()
|
|
{
|
|
const char *urls = getenv (DEBUGINFOD_URLS_ENV_VAR);
|
|
|
|
if (urls == nullptr || urls[0] == '\0'
|
|
|| debuginfod_enable == debuginfod_off)
|
|
return false;
|
|
|
|
if (debuginfod_enable == debuginfod_ask)
|
|
{
|
|
int resp = nquery (_("\nThis GDB supports auto-downloading debuginfo " \
|
|
"from the following URLs:\n%s\nEnable debuginfod " \
|
|
"for this session? "),
|
|
urls);
|
|
if (!resp)
|
|
{
|
|
printf_filtered (_("Debuginfod has been disabled.\nTo make this " \
|
|
"setting permanent, add \'set debuginfod off\' " \
|
|
"to .gdbinit.\n"));
|
|
debuginfod_enable = debuginfod_off;
|
|
return false;
|
|
}
|
|
|
|
printf_filtered (_("Debuginfod has been enabled.\nTo make this " \
|
|
"setting permanent, add \'set debuginfod on\' " \
|
|
"to .gdbinit.\n"));
|
|
debuginfod_enable = 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_enabled ())
|
|
return scoped_fd (-ENOSYS);
|
|
|
|
debuginfod_client *c = get_debuginfod_client ();
|
|
|
|
if (c == nullptr)
|
|
return scoped_fd (-ENOMEM);
|
|
|
|
user_data data ("source file", srcpath);
|
|
|
|
debuginfod_set_user_data (c, &data);
|
|
scoped_fd fd (debuginfod_find_source (c,
|
|
build_id,
|
|
build_id_len,
|
|
srcpath,
|
|
nullptr));
|
|
debuginfod_set_user_data (c, nullptr);
|
|
|
|
if (debuginfod_verbose > 0 && 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));
|
|
|
|
if (fd.get () >= 0)
|
|
*destname = make_unique_xstrdup (srcpath);
|
|
|
|
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_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);
|
|
scoped_fd fd (debuginfod_find_debuginfo (c, build_id, build_id_len,
|
|
&dname));
|
|
debuginfod_set_user_data (c, nullptr);
|
|
|
|
if (debuginfod_verbose > 0 && 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));
|
|
|
|
if (fd.get () >= 0)
|
|
destname->reset (dname);
|
|
|
|
return fd;
|
|
}
|
|
#endif
|
|
|
|
/* 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);
|
|
|
|
/* set debuginfod on */
|
|
add_cmd ("on", class_run, set_debuginfod_on_command,
|
|
_("Enable debuginfod."), &set_debuginfod_prefix_list);
|
|
|
|
/* set debuginfod off */
|
|
add_cmd ("off", class_run, set_debuginfod_off_command,
|
|
_("Disable debuginfod."), &set_debuginfod_prefix_list);
|
|
|
|
/* set debuginfod ask */
|
|
add_cmd ("ask", class_run, set_debuginfod_ask_command, _("\
|
|
Ask the user whether to enable debuginfod before performing the next query."),
|
|
&set_debuginfod_prefix_list);
|
|
|
|
/* show debuginfod status */
|
|
add_cmd ("status", class_run, show_debuginfod_status_command,
|
|
_("Show whether debuginfod is set to \"on\", \"off\" or \"ask\"."),
|
|
&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_command,
|
|
get_debuginfod_urls_command,
|
|
show_debuginfod_urls_command,
|
|
&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."),
|
|
set_debuginfod_verbose_command,
|
|
show_debuginfod_verbose_command,
|
|
&set_debuginfod_prefix_list,
|
|
&show_debuginfod_prefix_list);
|
|
}
|