GDB (xrefs)
Loading...
Searching...
No Matches
debuginfod-support.c
Go to the documentation of this file.
1/* debuginfod utilities for GDB.
2 Copyright (C) 2020-2023 Free Software Foundation, Inc.
3
4 This file is part of GDB.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18
19#include "defs.h"
20#include "diagnostics.h"
21#include <errno.h>
22#include "gdbsupport/scoped_fd.h"
23#include "debuginfod-support.h"
24#include "gdbsupport/gdb_optional.h"
25#include "cli/cli-cmds.h"
26#include "cli/cli-style.h"
27#include "cli-out.h"
28#include "target.h"
29
30/* Set/show debuginfod commands. */
33
34static const char debuginfod_on[] = "on";
35static const char debuginfod_off[] = "off";
36static const char debuginfod_ask[] = "ask";
37
38static const char *debuginfod_enabled_enum[] =
39{
43 nullptr
44};
45
46static const char *debuginfod_enabled =
47#if defined(HAVE_LIBDEBUGINFOD)
49#else
51#endif
52
53static unsigned int debuginfod_verbose = 1;
54
55#ifndef HAVE_LIBDEBUGINFOD
56scoped_fd
57debuginfod_source_query (const unsigned char *build_id,
58 int build_id_len,
59 const char *srcpath,
60 gdb::unique_xmalloc_ptr<char> *destname)
61{
62 return scoped_fd (-ENOSYS);
63}
64
65scoped_fd
66debuginfod_debuginfo_query (const unsigned char *build_id,
67 int build_id_len,
68 const char *filename,
69 gdb::unique_xmalloc_ptr<char> *destname)
70{
71 return scoped_fd (-ENOSYS);
72}
73
74scoped_fd
75debuginfod_exec_query (const unsigned char *build_id,
76 int build_id_len,
77 const char *filename,
78 gdb::unique_xmalloc_ptr<char> *destname)
79{
80 return scoped_fd (-ENOSYS);
81}
82
83#define NO_IMPL _("Support for debuginfod is not compiled into GDB.")
84
85#else
86#include <elfutils/debuginfod.h>
87
88struct user_data
89{
90 user_data (const char *desc, const char *fname)
91 : desc (desc), fname (fname)
92 { }
93
94 const char * const desc;
95 const char * const fname;
97};
98
99/* Deleter for a debuginfod_client. */
100
101struct debuginfod_client_deleter
102{
103 void operator() (debuginfod_client *c)
104 {
105 debuginfod_end (c);
106 }
107};
108
109using debuginfod_client_up
110 = std::unique_ptr<debuginfod_client, debuginfod_client_deleter>;
111
112
113/* Convert SIZE into a unit suitable for use with progress updates.
114 SIZE should in given in bytes and will be converted into KB, MB, GB
115 or remain unchanged. UNIT will be set to "B", "KB", "MB" or "GB"
116 accordingly. */
117
118static const char *
119get_size_and_unit (double &size)
120{
121 if (size < 1024)
122 /* If size is less than 1 KB then set unit to B. */
123 return "B";
124
125 size /= 1024;
126 if (size < 1024)
127 /* If size is less than 1 MB then set unit to KB. */
128 return "K";
129
130 size /= 1024;
131 if (size < 1024)
132 /* If size is less than 1 GB then set unit to MB. */
133 return "M";
134
135 size /= 1024;
136 return "G";
137}
138
139static int
140progressfn (debuginfod_client *c, long cur, long total)
141{
142 user_data *data = static_cast<user_data *> (debuginfod_get_user_data (c));
143 gdb_assert (data != nullptr);
144
145 string_file styled_fname (current_uiout->can_emit_style_escape ());
146 fprintf_styled (&styled_fname, file_name_style.style (), "%s",
147 data->fname);
148
149 if (check_quit_flag ())
150 {
151 gdb_printf ("Cancelling download of %s %s...\n",
152 data->desc, styled_fname.c_str ());
153 return 1;
154 }
155
156 if (debuginfod_verbose == 0)
157 return 0;
158
159 /* Print progress update. Include the transfer size if available. */
160 if (total > 0)
161 {
162 /* Transfer size is known. */
163 double howmuch = (double) cur / (double) total;
164
165 if (howmuch >= 0.0 && howmuch <= 1.0)
166 {
167 double d_total = (double) total;
168 const char *unit = get_size_and_unit (d_total);
169 std::string msg = string_printf ("Downloading %0.2f %s %s %s",
170 d_total, unit, data->desc,
171 styled_fname.c_str ());
172 data->progress.update_progress (msg, unit, howmuch, d_total);
173 return 0;
174 }
175 }
176
177 std::string msg = string_printf ("Downloading %s %s",
178 data->desc, styled_fname.c_str ());
179 data->progress.update_progress (msg);
180 return 0;
181}
182
183static debuginfod_client *
184get_debuginfod_client ()
185{
186 static debuginfod_client_up global_client;
187
188 if (global_client == nullptr)
189 {
190 global_client.reset (debuginfod_begin ());
191
192 if (global_client != nullptr)
193 debuginfod_set_progressfn (global_client.get (), progressfn);
194 }
195
196 return global_client.get ();
197}
198
199/* Check if debuginfod is enabled. If configured to do so, ask the user
200 whether to enable debuginfod. */
201
202static bool
203debuginfod_is_enabled ()
204{
205 const char *urls = skip_spaces (getenv (DEBUGINFOD_URLS_ENV_VAR));
206
208 || urls == nullptr
209 || *urls == '\0')
210 return false;
211
213 {
214 gdb_printf (_("\nThis GDB supports auto-downloading debuginfo " \
215 "from the following URLs:\n"));
216
217 gdb::string_view url_view (urls);
218 while (true)
219 {
220 size_t off = url_view.find_first_not_of (' ');
221 if (off == gdb::string_view::npos)
222 break;
223 url_view = url_view.substr (off);
224 /* g++ 11.2.1 on s390x, g++ 11.3.1 on ppc64le and g++ 11 on
225 hppa seem convinced url_view might be of SIZE_MAX length.
226 And so complains because the length of an array can only
227 be PTRDIFF_MAX. */
228 DIAGNOSTIC_PUSH
229 DIAGNOSTIC_IGNORE_STRINGOP_OVERREAD
230 off = url_view.find_first_of (' ');
231 DIAGNOSTIC_POP
233 (_(" <%ps>\n"),
235 gdb::to_string (url_view.substr (0,
236 off)).c_str ()));
237 if (off == gdb::string_view::npos)
238 break;
239 url_view = url_view.substr (off);
240 }
241
242 int resp = nquery (_("Enable debuginfod for this session? "));
243 if (!resp)
244 {
245 gdb_printf (_("Debuginfod has been disabled.\nTo make this " \
246 "setting permanent, add \'set debuginfod " \
247 "enabled off\' to .gdbinit.\n"));
249 return false;
250 }
251
252 gdb_printf (_("Debuginfod has been enabled.\nTo make this " \
253 "setting permanent, add \'set debuginfod enabled " \
254 "on\' to .gdbinit.\n"));
256 }
257
258 return true;
259}
260
261/* Print the result of the most recent attempted download. */
262
263static void
264print_outcome (user_data &data, int fd)
265{
266 /* Clears the current line of progress output. */
267 current_uiout->do_progress_end ();
268
269 if (fd < 0 && fd != -ENOENT)
270 gdb_printf (_("Download failed: %s. Continuing without %s %ps.\n"),
271 safe_strerror (-fd),
272 data.desc,
274}
275
276/* See debuginfod-support.h */
277
278scoped_fd
279debuginfod_source_query (const unsigned char *build_id,
280 int build_id_len,
281 const char *srcpath,
282 gdb::unique_xmalloc_ptr<char> *destname)
283{
284 if (!debuginfod_is_enabled ())
285 return scoped_fd (-ENOSYS);
286
287 debuginfod_client *c = get_debuginfod_client ();
288
289 if (c == nullptr)
290 return scoped_fd (-ENOMEM);
291
292 char *dname = nullptr;
293 user_data data ("source file", srcpath);
294
295 debuginfod_set_user_data (c, &data);
296 gdb::optional<target_terminal::scoped_restore_terminal_state> term_state;
298 {
299 term_state.emplace ();
301 }
302
303 scoped_fd fd (debuginfod_find_source (c,
304 build_id,
305 build_id_len,
306 srcpath,
307 &dname));
308 debuginfod_set_user_data (c, nullptr);
309 print_outcome (data, fd.get ());
310
311 if (fd.get () >= 0)
312 destname->reset (dname);
313
314 return fd;
315}
316
317/* See debuginfod-support.h */
318
319scoped_fd
320debuginfod_debuginfo_query (const unsigned char *build_id,
321 int build_id_len,
322 const char *filename,
323 gdb::unique_xmalloc_ptr<char> *destname)
324{
325 if (!debuginfod_is_enabled ())
326 return scoped_fd (-ENOSYS);
327
328 debuginfod_client *c = get_debuginfod_client ();
329
330 if (c == nullptr)
331 return scoped_fd (-ENOMEM);
332
333 char *dname = nullptr;
334 user_data data ("separate debug info for", filename);
335
336 debuginfod_set_user_data (c, &data);
337 gdb::optional<target_terminal::scoped_restore_terminal_state> term_state;
339 {
340 term_state.emplace ();
342 }
343
344 scoped_fd fd (debuginfod_find_debuginfo (c, build_id, build_id_len,
345 &dname));
346 debuginfod_set_user_data (c, nullptr);
347 print_outcome (data, fd.get ());
348
349 if (fd.get () >= 0)
350 destname->reset (dname);
351
352 return fd;
353}
354
355/* See debuginfod-support.h */
356
357scoped_fd
358debuginfod_exec_query (const unsigned char *build_id,
359 int build_id_len,
360 const char *filename,
361 gdb::unique_xmalloc_ptr<char> *destname)
362{
363 if (!debuginfod_is_enabled ())
364 return scoped_fd (-ENOSYS);
365
366 debuginfod_client *c = get_debuginfod_client ();
367
368 if (c == nullptr)
369 return scoped_fd (-ENOMEM);
370
371 char *dname = nullptr;
372 user_data data ("executable for", filename);
373
374 debuginfod_set_user_data (c, &data);
375 gdb::optional<target_terminal::scoped_restore_terminal_state> term_state;
377 {
378 term_state.emplace ();
380 }
381
382 scoped_fd fd (debuginfod_find_executable (c, build_id, build_id_len, &dname));
383 debuginfod_set_user_data (c, nullptr);
384 print_outcome (data, fd.get ());
385
386 if (fd.get () >= 0)
387 destname->reset (dname);
388
389 return fd;
390}
391#endif
392
393/* Set callback for "set debuginfod enabled". */
394
395static void
397{
398#if defined(HAVE_LIBDEBUGINFOD)
400#else
401 /* Disabling debuginfod when gdb is not built with it is a no-op. */
402 if (value != debuginfod_off)
403 error (NO_IMPL);
404#endif
405}
406
407/* Get callback for "set debuginfod enabled". */
408
409static const char *
411{
412 return debuginfod_enabled;
413}
414
415/* Show callback for "set debuginfod enabled". */
416
417static void
419 const char *value)
420{
421 gdb_printf (file,
422 _("Debuginfod functionality is currently set to "
423 "\"%s\".\n"), debuginfod_enabled);
424}
425
426/* Set callback for "set debuginfod urls". */
427
428static void
429set_debuginfod_urls (const std::string &urls)
430{
431#if defined(HAVE_LIBDEBUGINFOD)
432 if (setenv (DEBUGINFOD_URLS_ENV_VAR, urls.c_str (), 1) != 0)
433 warning (_("Unable to set debuginfod URLs: %s"), safe_strerror (errno));
434#else
435 error (NO_IMPL);
436#endif
437}
438
439/* Get callback for "set debuginfod urls". */
440
441static const std::string&
443{
444 static std::string urls;
445#if defined(HAVE_LIBDEBUGINFOD)
446 const char *envvar = getenv (DEBUGINFOD_URLS_ENV_VAR);
447
448 if (envvar != nullptr)
449 urls = envvar;
450 else
451 urls.clear ();
452#endif
453
454 return urls;
455}
456
457/* Show callback for "set debuginfod urls". */
458
459static void
461 const char *value)
462{
463 if (value[0] == '\0')
464 gdb_printf (file, _("Debuginfod URLs have not been set.\n"));
465 else
466 gdb_printf (file, _("Debuginfod URLs are currently set to:\n%s\n"),
467 value);
468}
469
470/* Show callback for "set debuginfod verbose". */
471
472static void
474 cmd_list_element *cmd, const char *value)
475{
476 gdb_printf (file, _("Debuginfod verbose output is set to %s.\n"),
477 value);
478}
479
480/* Register debuginfod commands. */
481
483void
485{
486 /* set/show debuginfod */
487 add_setshow_prefix_cmd ("debuginfod", class_run,
488 _("Set debuginfod options."),
489 _("Show debuginfod options."),
492 &setlist, &showlist);
493
495 _("Set whether to use debuginfod."),
496 _("Show whether to use debuginfod."),
497 _("\
498When on, enable the use of debuginfod to download missing debug info and\n\
499source files."),
505
506 /* set/show debuginfod urls */
508Set the list of debuginfod server URLs."), _("\
509Show the list of debuginfod server URLs."), _("\
510Manage the space-separated list of debuginfod server URLs that GDB will query \
511when missing debuginfo, executables or source files.\nThe default value is \
512copied from the DEBUGINFOD_URLS environment variable."),
518
519 /* set/show debuginfod verbose */
521 &debuginfod_verbose, _("\
522Set verbosity of debuginfod output."), _("\
523Show debuginfod debugging."), _("\
524When set to a non-zero value, display verbose output for each debuginfod \
525query.\nTo disable, set to zero. Verbose output is displayed by default."),
526 nullptr,
530}
ui_file_style style() const
Definition cli-style.c:169
static void ours()
Definition target.c:1065
struct cmd_list_element * showlist
Definition cli-cmds.c:125
struct cmd_list_element * setlist
Definition cli-cmds.c:117
set_show_commands add_setshow_enum_cmd(const char *name, enum command_class theclass, const char *const *enumlist, const char **var, const char *set_doc, const char *show_doc, const char *help_doc, cmd_func_ftype *set_func, show_value_ftype *show_func, struct cmd_list_element **set_list, struct cmd_list_element **show_list)
Definition cli-decode.c:618
set_show_commands add_setshow_prefix_cmd(const char *name, command_class theclass, const char *set_doc, const char *show_doc, cmd_list_element **set_subcommands_list, cmd_list_element **show_subcommands_list, cmd_list_element **set_list, cmd_list_element **show_list)
Definition cli-decode.c:428
set_show_commands add_setshow_string_noescape_cmd(const char *name, enum command_class theclass, std::string *var, const char *set_doc, const char *show_doc, const char *help_doc, cmd_func_ftype *set_func, show_value_ftype *show_func, struct cmd_list_element **set_list, struct cmd_list_element **show_list)
Definition cli-decode.c:883
set_show_commands add_setshow_zuinteger_cmd(const char *name, enum command_class theclass, unsigned int *var, const char *set_doc, const char *show_doc, const char *help_doc, cmd_func_ftype *set_func, show_value_ftype *show_func, struct cmd_list_element **set_list, struct cmd_list_element **show_list)
cli_style_option file_name_style
@ class_support
Definition command.h:58
@ class_run
Definition command.h:54
static const char debuginfod_on[]
static cmd_list_element * set_debuginfod_prefix_list
static void show_debuginfod_urls(ui_file *file, int from_tty, cmd_list_element *cmd, const char *value)
static void set_debuginfod_enabled(const char *value)
static unsigned int debuginfod_verbose
static const char * debuginfod_enabled
scoped_fd debuginfod_debuginfo_query(const unsigned char *build_id, int build_id_len, const char *filename, gdb::unique_xmalloc_ptr< char > *destname)
#define NO_IMPL
void _initialize_debuginfod()
scoped_fd debuginfod_exec_query(const unsigned char *build_id, int build_id_len, const char *filename, gdb::unique_xmalloc_ptr< char > *destname)
static const char debuginfod_ask[]
scoped_fd debuginfod_source_query(const unsigned char *build_id, int build_id_len, const char *srcpath, gdb::unique_xmalloc_ptr< char > *destname)
static void show_debuginfod_verbose_command(ui_file *file, int from_tty, cmd_list_element *cmd, const char *value)
static const char * get_debuginfod_enabled()
static void set_debuginfod_urls(const std::string &urls)
static const char debuginfod_off[]
static cmd_list_element * show_debuginfod_prefix_list
static void show_debuginfod_enabled(ui_file *file, int from_tty, cmd_list_element *cmd, const char *value)
static const char * debuginfod_enabled_enum[]
static const std::string & get_debuginfod_urls()
int check_quit_flag(void)
Definition extension.c:805
size_t size
Definition go32-nat.c:241
Definition value.c:181
bool target_supports_terminal_ours(void)
Definition target.c:1109
static styled_string_s * styled_string(const ui_file_style &style, const char *str, styled_string_s &&tmp={})
Definition ui-out.h:151
#define current_uiout
Definition ui-out.h:40
int nquery(const char *ctlstr,...)
Definition utils.c:975
void fprintf_styled(struct ui_file *stream, const ui_file_style &style, const char *format,...)
Definition utils.c:1877
void gdb_printf(struct ui_file *stream, const char *format,...)
Definition utils.c:1865