22944ce50bbe112295ffe98deb21a0a3850b0a4f
[binutils-gdb.git] / gdb / debuginfod-support.c
1 /* debuginfod utilities for GDB.
2 Copyright (C) 2020-2022 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 <errno.h>
21 #include "gdbsupport/scoped_fd.h"
22 #include "debuginfod-support.h"
23 #include "gdbsupport/gdb_optional.h"
24 #include "cli/cli-cmds.h"
25 #include "cli/cli-style.h"
26 #include "target.h"
27
28 /* Set/show debuginfod commands. */
29 static cmd_list_element *set_debuginfod_prefix_list;
30 static cmd_list_element *show_debuginfod_prefix_list;
31
32 static const char debuginfod_on[] = "on";
33 static const char debuginfod_off[] = "off";
34 static const char debuginfod_ask[] = "ask";
35
36 static const char *debuginfod_enabled_enum[] =
37 {
38 debuginfod_on,
39 debuginfod_off,
40 debuginfod_ask,
41 nullptr
42 };
43
44 static const char *debuginfod_enabled =
45 #if defined(HAVE_LIBDEBUGINFOD)
46 debuginfod_ask;
47 #else
48 debuginfod_off;
49 #endif
50
51 static unsigned int debuginfod_verbose = 1;
52
53 #ifndef HAVE_LIBDEBUGINFOD
54 scoped_fd
55 debuginfod_source_query (const unsigned char *build_id,
56 int build_id_len,
57 const char *srcpath,
58 gdb::unique_xmalloc_ptr<char> *destname)
59 {
60 return scoped_fd (-ENOSYS);
61 }
62
63 scoped_fd
64 debuginfod_debuginfo_query (const unsigned char *build_id,
65 int build_id_len,
66 const char *filename,
67 gdb::unique_xmalloc_ptr<char> *destname)
68 {
69 return scoped_fd (-ENOSYS);
70 }
71
72 scoped_fd
73 debuginfod_exec_query (const unsigned char *build_id,
74 int build_id_len,
75 const char *filename,
76 gdb::unique_xmalloc_ptr<char> *destname)
77 {
78 return scoped_fd (-ENOSYS);
79 }
80
81 #define NO_IMPL _("Support for debuginfod is not compiled into GDB.")
82
83 #else
84 #include <elfutils/debuginfod.h>
85
86 struct user_data
87 {
88 user_data (const char *desc, const char *fname)
89 : desc (desc), fname (fname), has_printed (false)
90 { }
91
92 const char * const desc;
93 const char * const fname;
94 bool has_printed;
95 };
96
97 /* Deleter for a debuginfod_client. */
98
99 struct debuginfod_client_deleter
100 {
101 void operator() (debuginfod_client *c)
102 {
103 debuginfod_end (c);
104 }
105 };
106
107 using debuginfod_client_up
108 = std::unique_ptr<debuginfod_client, debuginfod_client_deleter>;
109
110 static int
111 progressfn (debuginfod_client *c, long cur, long total)
112 {
113 user_data *data = static_cast<user_data *> (debuginfod_get_user_data (c));
114 gdb_assert (data != nullptr);
115
116 if (check_quit_flag ())
117 {
118 gdb_printf ("Cancelling download of %s %ps...\n",
119 data->desc,
120 styled_string (file_name_style.style (), data->fname));
121 return 1;
122 }
123
124 if (!data->has_printed)
125 {
126 /* Include the transfer size, if available. */
127 if (total > 0)
128 {
129 float size = 1.0f * total / 1024;
130 const char *unit = "KB";
131
132 /* If size is greater than 0.01 MB, set unit to MB. */
133 if (size > 10.24)
134 {
135 size /= 1024;
136 unit = "MB";
137 }
138
139 gdb_printf ("Downloading %.2f %s %s %ps...\n",
140 size, unit, data->desc,
141 styled_string (file_name_style.style (),
142 data->fname));
143 }
144 else
145 gdb_printf ("Downloading %s %ps...\n", data->desc,
146 styled_string (file_name_style.style (), data->fname));
147
148 data->has_printed = true;
149 }
150
151 return 0;
152 }
153
154 static debuginfod_client *
155 get_debuginfod_client ()
156 {
157 static debuginfod_client_up global_client;
158
159 if (global_client == nullptr)
160 {
161 global_client.reset (debuginfod_begin ());
162
163 if (global_client != nullptr)
164 debuginfod_set_progressfn (global_client.get (), progressfn);
165 }
166
167 return global_client.get ();
168 }
169
170 /* Check if debuginfod is enabled. If configured to do so, ask the user
171 whether to enable debuginfod. */
172
173 static bool
174 debuginfod_is_enabled ()
175 {
176 const char *urls = getenv (DEBUGINFOD_URLS_ENV_VAR);
177
178 if (urls == nullptr || urls[0] == '\0'
179 || debuginfod_enabled == debuginfod_off)
180 return false;
181
182 if (debuginfod_enabled == debuginfod_ask)
183 {
184 gdb_printf (_("\nThis GDB supports auto-downloading debuginfo " \
185 "from the following URLs:\n"));
186
187 gdb::string_view url_view (urls);
188 while (true)
189 {
190 url_view = url_view.substr (url_view.find_first_not_of (' '));
191 if (url_view.empty ())
192 break;
193 size_t off = url_view.find_first_of (' ');
194 gdb_printf
195 (_(" <%ps>\n"),
196 styled_string (file_name_style.style (),
197 gdb::to_string (url_view.substr (0,
198 off)).c_str ()));
199 if (off == gdb::string_view::npos)
200 break;
201 url_view = url_view.substr (off);
202 }
203
204 int resp = nquery (_("Enable debuginfod for this session? "));
205 if (!resp)
206 {
207 gdb_printf (_("Debuginfod has been disabled.\nTo make this " \
208 "setting permanent, add \'set debuginfod " \
209 "enabled off\' to .gdbinit.\n"));
210 debuginfod_enabled = debuginfod_off;
211 return false;
212 }
213
214 gdb_printf (_("Debuginfod has been enabled.\nTo make this " \
215 "setting permanent, add \'set debuginfod enabled " \
216 "on\' to .gdbinit.\n"));
217 debuginfod_enabled = debuginfod_on;
218 }
219
220 return true;
221 }
222
223 /* See debuginfod-support.h */
224
225 scoped_fd
226 debuginfod_source_query (const unsigned char *build_id,
227 int build_id_len,
228 const char *srcpath,
229 gdb::unique_xmalloc_ptr<char> *destname)
230 {
231 if (!debuginfod_is_enabled ())
232 return scoped_fd (-ENOSYS);
233
234 debuginfod_client *c = get_debuginfod_client ();
235
236 if (c == nullptr)
237 return scoped_fd (-ENOMEM);
238
239 char *dname = nullptr;
240 user_data data ("source file", srcpath);
241
242 debuginfod_set_user_data (c, &data);
243 gdb::optional<target_terminal::scoped_restore_terminal_state> term_state;
244 if (target_supports_terminal_ours ())
245 {
246 term_state.emplace ();
247 target_terminal::ours ();
248 }
249
250 scoped_fd fd (debuginfod_find_source (c,
251 build_id,
252 build_id_len,
253 srcpath,
254 &dname));
255 debuginfod_set_user_data (c, nullptr);
256
257 if (debuginfod_verbose > 0 && fd.get () < 0 && fd.get () != -ENOENT)
258 gdb_printf (_("Download failed: %s. Continuing without source file %ps.\n"),
259 safe_strerror (-fd.get ()),
260 styled_string (file_name_style.style (), srcpath));
261
262 if (fd.get () >= 0)
263 destname->reset (dname);
264
265 return fd;
266 }
267
268 /* See debuginfod-support.h */
269
270 scoped_fd
271 debuginfod_debuginfo_query (const unsigned char *build_id,
272 int build_id_len,
273 const char *filename,
274 gdb::unique_xmalloc_ptr<char> *destname)
275 {
276 if (!debuginfod_is_enabled ())
277 return scoped_fd (-ENOSYS);
278
279 debuginfod_client *c = get_debuginfod_client ();
280
281 if (c == nullptr)
282 return scoped_fd (-ENOMEM);
283
284 char *dname = nullptr;
285 user_data data ("separate debug info for", filename);
286
287 debuginfod_set_user_data (c, &data);
288 gdb::optional<target_terminal::scoped_restore_terminal_state> term_state;
289 if (target_supports_terminal_ours ())
290 {
291 term_state.emplace ();
292 target_terminal::ours ();
293 }
294
295 scoped_fd fd (debuginfod_find_debuginfo (c, build_id, build_id_len,
296 &dname));
297 debuginfod_set_user_data (c, nullptr);
298
299 if (debuginfod_verbose > 0 && fd.get () < 0 && fd.get () != -ENOENT)
300 gdb_printf (_("Download failed: %s. Continuing without debug info for %ps.\n"),
301 safe_strerror (-fd.get ()),
302 styled_string (file_name_style.style (), filename));
303
304 if (fd.get () >= 0)
305 destname->reset (dname);
306
307 return fd;
308 }
309
310 /* See debuginfod-support.h */
311
312 scoped_fd
313 debuginfod_exec_query (const unsigned char *build_id,
314 int build_id_len,
315 const char *filename,
316 gdb::unique_xmalloc_ptr<char> *destname)
317 {
318 if (!debuginfod_is_enabled ())
319 return scoped_fd (-ENOSYS);
320
321 debuginfod_client *c = get_debuginfod_client ();
322
323 if (c == nullptr)
324 return scoped_fd (-ENOMEM);
325
326 char *dname = nullptr;
327 user_data data ("executable for", filename);
328
329 debuginfod_set_user_data (c, &data);
330 gdb::optional<target_terminal::scoped_restore_terminal_state> term_state;
331 if (target_supports_terminal_ours ())
332 {
333 term_state.emplace ();
334 target_terminal::ours ();
335 }
336
337 scoped_fd fd (debuginfod_find_executable (c, build_id, build_id_len, &dname));
338 debuginfod_set_user_data (c, nullptr);
339
340 if (debuginfod_verbose > 0 && fd.get () < 0 && fd.get () != -ENOENT)
341 gdb_printf (_("Download failed: %s. " \
342 "Continuing without executable for %ps.\n"),
343 safe_strerror (-fd.get ()),
344 styled_string (file_name_style.style (), filename));
345
346 if (fd.get () >= 0)
347 destname->reset (dname);
348
349 return fd;
350 }
351 #endif
352
353 /* Set callback for "set debuginfod enabled". */
354
355 static void
356 set_debuginfod_enabled (const char *value)
357 {
358 #if defined(HAVE_LIBDEBUGINFOD)
359 debuginfod_enabled = value;
360 #else
361 error (NO_IMPL);
362 #endif
363 }
364
365 /* Get callback for "set debuginfod enabled". */
366
367 static const char *
368 get_debuginfod_enabled ()
369 {
370 return debuginfod_enabled;
371 }
372
373 /* Show callback for "set debuginfod enabled". */
374
375 static void
376 show_debuginfod_enabled (ui_file *file, int from_tty, cmd_list_element *cmd,
377 const char *value)
378 {
379 gdb_printf (file,
380 _("Debuginfod functionality is currently set to "
381 "\"%s\".\n"), debuginfod_enabled);
382 }
383
384 /* Set callback for "set debuginfod urls". */
385
386 static void
387 set_debuginfod_urls (const std::string &urls)
388 {
389 #if defined(HAVE_LIBDEBUGINFOD)
390 if (setenv (DEBUGINFOD_URLS_ENV_VAR, urls.c_str (), 1) != 0)
391 warning (_("Unable to set debuginfod URLs: %s"), safe_strerror (errno));
392 #else
393 error (NO_IMPL);
394 #endif
395 }
396
397 /* Get callback for "set debuginfod urls". */
398
399 static const std::string&
400 get_debuginfod_urls ()
401 {
402 static std::string urls;
403 #if defined(HAVE_LIBDEBUGINFOD)
404 const char *envvar = getenv (DEBUGINFOD_URLS_ENV_VAR);
405
406 if (envvar != nullptr)
407 urls = envvar;
408 else
409 urls.clear ();
410 #endif
411
412 return urls;
413 }
414
415 /* Show callback for "set debuginfod urls". */
416
417 static void
418 show_debuginfod_urls (ui_file *file, int from_tty, cmd_list_element *cmd,
419 const char *value)
420 {
421 if (value[0] == '\0')
422 gdb_printf (file, _("Debuginfod URLs have not been set.\n"));
423 else
424 gdb_printf (file, _("Debuginfod URLs are currently set to:\n%s\n"),
425 value);
426 }
427
428 /* Show callback for "set debuginfod verbose". */
429
430 static void
431 show_debuginfod_verbose_command (ui_file *file, int from_tty,
432 cmd_list_element *cmd, const char *value)
433 {
434 gdb_printf (file, _("Debuginfod verbose output is set to %s.\n"),
435 value);
436 }
437
438 /* Register debuginfod commands. */
439
440 void _initialize_debuginfod ();
441 void
442 _initialize_debuginfod ()
443 {
444 /* set/show debuginfod */
445 add_setshow_prefix_cmd ("debuginfod", class_run,
446 _("Set debuginfod options."),
447 _("Show debuginfod options."),
448 &set_debuginfod_prefix_list,
449 &show_debuginfod_prefix_list,
450 &setlist, &showlist);
451
452 add_setshow_enum_cmd ("enabled", class_run, debuginfod_enabled_enum,
453 _("Set whether to use debuginfod."),
454 _("Show whether to use debuginfod."),
455 _("\
456 When on, enable the use of debuginfod to download missing debug info and\n\
457 source files."),
458 set_debuginfod_enabled,
459 get_debuginfod_enabled,
460 show_debuginfod_enabled,
461 &set_debuginfod_prefix_list,
462 &show_debuginfod_prefix_list);
463
464 /* set/show debuginfod urls */
465 add_setshow_string_noescape_cmd ("urls", class_run, _("\
466 Set the list of debuginfod server URLs."), _("\
467 Show the list of debuginfod server URLs."), _("\
468 Manage the space-separated list of debuginfod server URLs that GDB will query \
469 when missing debuginfo, executables or source files.\nThe default value is \
470 copied from the DEBUGINFOD_URLS environment variable."),
471 set_debuginfod_urls,
472 get_debuginfod_urls,
473 show_debuginfod_urls,
474 &set_debuginfod_prefix_list,
475 &show_debuginfod_prefix_list);
476
477 /* set/show debuginfod verbose */
478 add_setshow_zuinteger_cmd ("verbose", class_support,
479 &debuginfod_verbose, _("\
480 Set verbosity of debuginfod output."), _("\
481 Show debuginfod debugging."), _("\
482 When set to a non-zero value, display verbose output for each debuginfod \
483 query.\nTo disable, set to zero. Verbose output is displayed by default."),
484 nullptr,
485 show_debuginfod_verbose_command,
486 &set_debuginfod_prefix_list,
487 &show_debuginfod_prefix_list);
488 }