1 /* debuginfod utilities for GDB.
2 Copyright (C) 2020-2023 Free Software Foundation, Inc.
4 This file is part of GDB.
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.
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.
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/>. */
20 #include "diagnostics.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"
30 /* Set/show debuginfod commands. */
31 static cmd_list_element
*set_debuginfod_prefix_list
;
32 static cmd_list_element
*show_debuginfod_prefix_list
;
34 static const char debuginfod_on
[] = "on";
35 static const char debuginfod_off
[] = "off";
36 static const char debuginfod_ask
[] = "ask";
38 static const char *debuginfod_enabled_enum
[] =
46 static const char *debuginfod_enabled
=
47 #if defined(HAVE_LIBDEBUGINFOD)
53 static unsigned int debuginfod_verbose
= 1;
55 #ifndef HAVE_LIBDEBUGINFOD
57 debuginfod_source_query (const unsigned char *build_id
,
60 gdb::unique_xmalloc_ptr
<char> *destname
)
62 return scoped_fd (-ENOSYS
);
66 debuginfod_debuginfo_query (const unsigned char *build_id
,
69 gdb::unique_xmalloc_ptr
<char> *destname
)
71 return scoped_fd (-ENOSYS
);
75 debuginfod_exec_query (const unsigned char *build_id
,
78 gdb::unique_xmalloc_ptr
<char> *destname
)
80 return scoped_fd (-ENOSYS
);
83 #define NO_IMPL _("Support for debuginfod is not compiled into GDB.")
86 #include <elfutils/debuginfod.h>
90 user_data (const char *desc
, const char *fname
)
91 : desc (desc
), fname (fname
)
94 const char * const desc
;
95 const char * const fname
;
96 ui_out::progress_update progress
;
99 /* Deleter for a debuginfod_client. */
101 struct debuginfod_client_deleter
103 void operator() (debuginfod_client
*c
)
109 using debuginfod_client_up
110 = std::unique_ptr
<debuginfod_client
, debuginfod_client_deleter
>;
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"
119 get_size_and_unit (double &size
)
122 /* If size is less than 1 KB then set unit to B. */
127 /* If size is less than 1 MB then set unit to KB. */
132 /* If size is less than 1 GB then set unit to MB. */
140 progressfn (debuginfod_client
*c
, long cur
, long total
)
142 user_data
*data
= static_cast<user_data
*> (debuginfod_get_user_data (c
));
143 gdb_assert (data
!= nullptr);
145 string_file
styled_fname (current_uiout
->can_emit_style_escape ());
146 fprintf_styled (&styled_fname
, file_name_style
.style (), "%s",
149 if (check_quit_flag ())
151 gdb_printf ("Cancelling download of %s %s...\n",
152 data
->desc
, styled_fname
.c_str ());
156 if (debuginfod_verbose
== 0)
159 /* Print progress update. Include the transfer size if available. */
162 /* Transfer size is known. */
163 double howmuch
= (double) cur
/ (double) total
;
165 if (howmuch
>= 0.0 && howmuch
<= 1.0)
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
);
177 std::string msg
= string_printf ("Downloading %s %s",
178 data
->desc
, styled_fname
.c_str ());
179 data
->progress
.update_progress (msg
);
183 static debuginfod_client
*
184 get_debuginfod_client ()
186 static debuginfod_client_up global_client
;
188 if (global_client
== nullptr)
190 global_client
.reset (debuginfod_begin ());
192 if (global_client
!= nullptr)
193 debuginfod_set_progressfn (global_client
.get (), progressfn
);
196 return global_client
.get ();
199 /* Check if debuginfod is enabled. If configured to do so, ask the user
200 whether to enable debuginfod. */
203 debuginfod_is_enabled ()
205 const char *urls
= skip_spaces (getenv (DEBUGINFOD_URLS_ENV_VAR
));
207 if (debuginfod_enabled
== debuginfod_off
212 if (debuginfod_enabled
== debuginfod_ask
)
214 gdb_printf (_("\nThis GDB supports auto-downloading debuginfo " \
215 "from the following URLs:\n"));
217 gdb::string_view
url_view (urls
);
220 size_t off
= url_view
.find_first_not_of (' ');
221 if (off
== gdb::string_view::npos
)
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
229 DIAGNOSTIC_IGNORE_STRINGOP_OVERREAD
230 off
= url_view
.find_first_of (' ');
234 styled_string (file_name_style
.style (),
235 gdb::to_string (url_view
.substr (0,
237 if (off
== gdb::string_view::npos
)
239 url_view
= url_view
.substr (off
);
242 int resp
= nquery (_("Enable debuginfod for this session? "));
245 gdb_printf (_("Debuginfod has been disabled.\nTo make this " \
246 "setting permanent, add \'set debuginfod " \
247 "enabled off\' to .gdbinit.\n"));
248 debuginfod_enabled
= debuginfod_off
;
252 gdb_printf (_("Debuginfod has been enabled.\nTo make this " \
253 "setting permanent, add \'set debuginfod enabled " \
254 "on\' to .gdbinit.\n"));
255 debuginfod_enabled
= debuginfod_on
;
261 /* Print the result of the most recent attempted download. */
264 print_outcome (user_data
&data
, int fd
)
266 /* Clears the current line of progress output. */
267 current_uiout
->do_progress_end ();
269 if (fd
< 0 && fd
!= -ENOENT
)
270 gdb_printf (_("Download failed: %s. Continuing without %s %ps.\n"),
273 styled_string (file_name_style
.style (), data
.fname
));
276 /* See debuginfod-support.h */
279 debuginfod_source_query (const unsigned char *build_id
,
282 gdb::unique_xmalloc_ptr
<char> *destname
)
284 if (!debuginfod_is_enabled ())
285 return scoped_fd (-ENOSYS
);
287 debuginfod_client
*c
= get_debuginfod_client ();
290 return scoped_fd (-ENOMEM
);
292 char *dname
= nullptr;
293 user_data
data ("source file", srcpath
);
295 debuginfod_set_user_data (c
, &data
);
296 gdb::optional
<target_terminal::scoped_restore_terminal_state
> term_state
;
297 if (target_supports_terminal_ours ())
299 term_state
.emplace ();
300 target_terminal::ours ();
303 scoped_fd
fd (debuginfod_find_source (c
,
308 debuginfod_set_user_data (c
, nullptr);
309 print_outcome (data
, fd
.get ());
312 destname
->reset (dname
);
317 /* See debuginfod-support.h */
320 debuginfod_debuginfo_query (const unsigned char *build_id
,
322 const char *filename
,
323 gdb::unique_xmalloc_ptr
<char> *destname
)
325 if (!debuginfod_is_enabled ())
326 return scoped_fd (-ENOSYS
);
328 debuginfod_client
*c
= get_debuginfod_client ();
331 return scoped_fd (-ENOMEM
);
333 char *dname
= nullptr;
334 user_data
data ("separate debug info for", filename
);
336 debuginfod_set_user_data (c
, &data
);
337 gdb::optional
<target_terminal::scoped_restore_terminal_state
> term_state
;
338 if (target_supports_terminal_ours ())
340 term_state
.emplace ();
341 target_terminal::ours ();
344 scoped_fd
fd (debuginfod_find_debuginfo (c
, build_id
, build_id_len
,
346 debuginfod_set_user_data (c
, nullptr);
347 print_outcome (data
, fd
.get ());
350 destname
->reset (dname
);
355 /* See debuginfod-support.h */
358 debuginfod_exec_query (const unsigned char *build_id
,
360 const char *filename
,
361 gdb::unique_xmalloc_ptr
<char> *destname
)
363 if (!debuginfod_is_enabled ())
364 return scoped_fd (-ENOSYS
);
366 debuginfod_client
*c
= get_debuginfod_client ();
369 return scoped_fd (-ENOMEM
);
371 char *dname
= nullptr;
372 user_data
data ("executable for", filename
);
374 debuginfod_set_user_data (c
, &data
);
375 gdb::optional
<target_terminal::scoped_restore_terminal_state
> term_state
;
376 if (target_supports_terminal_ours ())
378 term_state
.emplace ();
379 target_terminal::ours ();
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 ());
387 destname
->reset (dname
);
393 /* Set callback for "set debuginfod enabled". */
396 set_debuginfod_enabled (const char *value
)
398 #if defined(HAVE_LIBDEBUGINFOD)
399 debuginfod_enabled
= value
;
401 /* Disabling debuginfod when gdb is not built with it is a no-op. */
402 if (value
!= debuginfod_off
)
407 /* Get callback for "set debuginfod enabled". */
410 get_debuginfod_enabled ()
412 return debuginfod_enabled
;
415 /* Show callback for "set debuginfod enabled". */
418 show_debuginfod_enabled (ui_file
*file
, int from_tty
, cmd_list_element
*cmd
,
422 _("Debuginfod functionality is currently set to "
423 "\"%s\".\n"), debuginfod_enabled
);
426 /* Set callback for "set debuginfod urls". */
429 set_debuginfod_urls (const std::string
&urls
)
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
));
439 /* Get callback for "set debuginfod urls". */
441 static const std::string
&
442 get_debuginfod_urls ()
444 static std::string urls
;
445 #if defined(HAVE_LIBDEBUGINFOD)
446 const char *envvar
= getenv (DEBUGINFOD_URLS_ENV_VAR
);
448 if (envvar
!= nullptr)
457 /* Show callback for "set debuginfod urls". */
460 show_debuginfod_urls (ui_file
*file
, int from_tty
, cmd_list_element
*cmd
,
463 if (value
[0] == '\0')
464 gdb_printf (file
, _("Debuginfod URLs have not been set.\n"));
466 gdb_printf (file
, _("Debuginfod URLs are currently set to:\n%s\n"),
470 /* Show callback for "set debuginfod verbose". */
473 show_debuginfod_verbose_command (ui_file
*file
, int from_tty
,
474 cmd_list_element
*cmd
, const char *value
)
476 gdb_printf (file
, _("Debuginfod verbose output is set to %s.\n"),
480 /* Register debuginfod commands. */
482 void _initialize_debuginfod ();
484 _initialize_debuginfod ()
486 /* set/show debuginfod */
487 add_setshow_prefix_cmd ("debuginfod", class_run
,
488 _("Set debuginfod options."),
489 _("Show debuginfod options."),
490 &set_debuginfod_prefix_list
,
491 &show_debuginfod_prefix_list
,
492 &setlist
, &showlist
);
494 add_setshow_enum_cmd ("enabled", class_run
, debuginfod_enabled_enum
,
495 _("Set whether to use debuginfod."),
496 _("Show whether to use debuginfod."),
498 When on, enable the use of debuginfod to download missing debug info and\n\
500 set_debuginfod_enabled
,
501 get_debuginfod_enabled
,
502 show_debuginfod_enabled
,
503 &set_debuginfod_prefix_list
,
504 &show_debuginfod_prefix_list
);
506 /* set/show debuginfod urls */
507 add_setshow_string_noescape_cmd ("urls", class_run
, _("\
508 Set the list of debuginfod server URLs."), _("\
509 Show the list of debuginfod server URLs."), _("\
510 Manage the space-separated list of debuginfod server URLs that GDB will query \
511 when missing debuginfo, executables or source files.\nThe default value is \
512 copied from the DEBUGINFOD_URLS environment variable."),
515 show_debuginfod_urls
,
516 &set_debuginfod_prefix_list
,
517 &show_debuginfod_prefix_list
);
519 /* set/show debuginfod verbose */
520 add_setshow_zuinteger_cmd ("verbose", class_support
,
521 &debuginfod_verbose
, _("\
522 Set verbosity of debuginfod output."), _("\
523 Show debuginfod debugging."), _("\
524 When set to a non-zero value, display verbose output for each debuginfod \
525 query.\nTo disable, set to zero. Verbose output is displayed by default."),
527 show_debuginfod_verbose_command
,
528 &set_debuginfod_prefix_list
,
529 &show_debuginfod_prefix_list
);