PR gdb/27570: missing support for debuginfod in core_target::build_file_mappings
[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)
90 { }
91
92 const char * const desc;
93 const char * const fname;
94 gdb::optional<ui_out::progress_meter> meter;
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 printf_filtered ("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 (total == 0)
125 return 0;
126
127 if (!data->meter.has_value ())
128 {
129 float size_in_mb = 1.0f * total / (1024 * 1024);
130 string_file styled_filename (current_uiout->can_emit_style_escape ());
131 fprintf_styled (&styled_filename,
132 file_name_style.style (),
133 "%s",
134 data->fname);
135 std::string message
136 = string_printf ("Downloading %.2f MB %s %s", size_in_mb, data->desc,
137 styled_filename.c_str());
138 data->meter.emplace (current_uiout, message, 1);
139 }
140
141 data->meter->progress ((double)cur / (double)total);
142
143 return 0;
144 }
145
146 static debuginfod_client *
147 get_debuginfod_client ()
148 {
149 static debuginfod_client_up global_client;
150
151 if (global_client == nullptr)
152 {
153 global_client.reset (debuginfod_begin ());
154
155 if (global_client != nullptr)
156 debuginfod_set_progressfn (global_client.get (), progressfn);
157 }
158
159 return global_client.get ();
160 }
161
162 /* Check if debuginfod is enabled. If configured to do so, ask the user
163 whether to enable debuginfod. */
164
165 static bool
166 debuginfod_is_enabled ()
167 {
168 const char *urls = getenv (DEBUGINFOD_URLS_ENV_VAR);
169
170 if (urls == nullptr || urls[0] == '\0'
171 || debuginfod_enabled == debuginfod_off)
172 return false;
173
174 if (debuginfod_enabled == debuginfod_ask)
175 {
176 int resp = nquery (_("\nThis GDB supports auto-downloading debuginfo " \
177 "from the following URLs:\n%s\nEnable debuginfod " \
178 "for this session? "),
179 urls);
180 if (!resp)
181 {
182 printf_filtered (_("Debuginfod has been disabled.\nTo make this " \
183 "setting permanent, add \'set debuginfod " \
184 "enabled off\' to .gdbinit.\n"));
185 debuginfod_enabled = debuginfod_off;
186 return false;
187 }
188
189 printf_filtered (_("Debuginfod has been enabled.\nTo make this " \
190 "setting permanent, add \'set debuginfod enabled " \
191 "on\' to .gdbinit.\n"));
192 debuginfod_enabled = debuginfod_on;
193 }
194
195 return true;
196 }
197
198 /* See debuginfod-support.h */
199
200 scoped_fd
201 debuginfod_source_query (const unsigned char *build_id,
202 int build_id_len,
203 const char *srcpath,
204 gdb::unique_xmalloc_ptr<char> *destname)
205 {
206 if (!debuginfod_is_enabled ())
207 return scoped_fd (-ENOSYS);
208
209 debuginfod_client *c = get_debuginfod_client ();
210
211 if (c == nullptr)
212 return scoped_fd (-ENOMEM);
213
214 char *dname = nullptr;
215 user_data data ("source file", srcpath);
216
217 debuginfod_set_user_data (c, &data);
218 gdb::optional<target_terminal::scoped_restore_terminal_state> term_state;
219 if (target_supports_terminal_ours ())
220 {
221 term_state.emplace ();
222 target_terminal::ours ();
223 }
224
225 scoped_fd fd (debuginfod_find_source (c,
226 build_id,
227 build_id_len,
228 srcpath,
229 &dname));
230 debuginfod_set_user_data (c, nullptr);
231
232 if (debuginfod_verbose > 0 && fd.get () < 0 && fd.get () != -ENOENT)
233 printf_filtered (_("Download failed: %s. Continuing without source file %ps.\n"),
234 safe_strerror (-fd.get ()),
235 styled_string (file_name_style.style (), srcpath));
236
237 if (fd.get () >= 0)
238 destname->reset (dname);
239
240 return fd;
241 }
242
243 /* See debuginfod-support.h */
244
245 scoped_fd
246 debuginfod_debuginfo_query (const unsigned char *build_id,
247 int build_id_len,
248 const char *filename,
249 gdb::unique_xmalloc_ptr<char> *destname)
250 {
251 if (!debuginfod_is_enabled ())
252 return scoped_fd (-ENOSYS);
253
254 debuginfod_client *c = get_debuginfod_client ();
255
256 if (c == nullptr)
257 return scoped_fd (-ENOMEM);
258
259 char *dname = nullptr;
260 user_data data ("separate debug info for", filename);
261
262 debuginfod_set_user_data (c, &data);
263 gdb::optional<target_terminal::scoped_restore_terminal_state> term_state;
264 if (target_supports_terminal_ours ())
265 {
266 term_state.emplace ();
267 target_terminal::ours ();
268 }
269
270 scoped_fd fd (debuginfod_find_debuginfo (c, build_id, build_id_len,
271 &dname));
272 debuginfod_set_user_data (c, nullptr);
273
274 if (debuginfod_verbose > 0 && fd.get () < 0 && fd.get () != -ENOENT)
275 printf_filtered (_("Download failed: %s. Continuing without debug info for %ps.\n"),
276 safe_strerror (-fd.get ()),
277 styled_string (file_name_style.style (), filename));
278
279 if (fd.get () >= 0)
280 destname->reset (dname);
281
282 return fd;
283 }
284
285 /* See debuginfod-support.h */
286
287 scoped_fd
288 debuginfod_exec_query (const unsigned char *build_id,
289 int build_id_len,
290 const char *filename,
291 gdb::unique_xmalloc_ptr<char> *destname)
292 {
293 if (!debuginfod_is_enabled ())
294 return scoped_fd (-ENOSYS);
295
296 debuginfod_client *c = get_debuginfod_client ();
297
298 if (c == nullptr)
299 return scoped_fd (-ENOMEM);
300
301 char *dname = nullptr;
302 user_data data ("executable for", filename);
303
304 debuginfod_set_user_data (c, &data);
305 gdb::optional<target_terminal::scoped_restore_terminal_state> term_state;
306 if (target_supports_terminal_ours ())
307 {
308 term_state.emplace ();
309 target_terminal::ours ();
310 }
311
312 scoped_fd fd (debuginfod_find_executable (c, build_id, build_id_len, &dname));
313 debuginfod_set_user_data (c, nullptr);
314
315 if (debuginfod_verbose > 0 && fd.get () < 0 && fd.get () != -ENOENT)
316 printf_filtered (_("Download failed: %s. " \
317 "Continuing without executable for %ps.\n"),
318 safe_strerror (-fd.get ()),
319 styled_string (file_name_style.style (), filename));
320
321 if (fd.get () >= 0)
322 destname->reset (dname);
323
324 return fd;
325 }
326 #endif
327
328 /* Set callback for "set debuginfod enabled". */
329
330 static void
331 set_debuginfod_enabled (const char *value)
332 {
333 #if defined(HAVE_LIBDEBUGINFOD)
334 debuginfod_enabled = value;
335 #else
336 error (NO_IMPL);
337 #endif
338 }
339
340 /* Get callback for "set debuginfod enabled". */
341
342 static const char *
343 get_debuginfod_enabled ()
344 {
345 return debuginfod_enabled;
346 }
347
348 /* Show callback for "set debuginfod enabled". */
349
350 static void
351 show_debuginfod_enabled (ui_file *file, int from_tty, cmd_list_element *cmd,
352 const char *value)
353 {
354 fprintf_filtered (file,
355 _("Debuginfod functionality is currently set to "
356 "\"%s\".\n"), debuginfod_enabled);
357 }
358
359 /* Set callback for "set debuginfod urls". */
360
361 static void
362 set_debuginfod_urls (const std::string &urls)
363 {
364 #if defined(HAVE_LIBDEBUGINFOD)
365 if (setenv (DEBUGINFOD_URLS_ENV_VAR, urls.c_str (), 1) != 0)
366 warning (_("Unable to set debuginfod URLs: %s"), safe_strerror (errno));
367 #else
368 error (NO_IMPL);
369 #endif
370 }
371
372 /* Get callback for "set debuginfod urls". */
373
374 static const std::string&
375 get_debuginfod_urls ()
376 {
377 static std::string urls;
378 #if defined(HAVE_LIBDEBUGINFOD)
379 const char *envvar = getenv (DEBUGINFOD_URLS_ENV_VAR);
380
381 if (envvar != nullptr)
382 urls = envvar;
383 else
384 urls.clear ();
385 #endif
386
387 return urls;
388 }
389
390 /* Show callback for "set debuginfod urls". */
391
392 static void
393 show_debuginfod_urls (ui_file *file, int from_tty, cmd_list_element *cmd,
394 const char *value)
395 {
396 if (value[0] == '\0')
397 fprintf_filtered (file, _("Debuginfod URLs have not been set.\n"));
398 else
399 fprintf_filtered (file, _("Debuginfod URLs are currently set to:\n%s\n"),
400 value);
401 }
402
403 /* Show callback for "set debuginfod verbose". */
404
405 static void
406 show_debuginfod_verbose_command (ui_file *file, int from_tty,
407 cmd_list_element *cmd, const char *value)
408 {
409 fprintf_filtered (file, _("Debuginfod verbose output is set to %s.\n"),
410 value);
411 }
412
413 /* Register debuginfod commands. */
414
415 void _initialize_debuginfod ();
416 void
417 _initialize_debuginfod ()
418 {
419 /* set/show debuginfod */
420 add_setshow_prefix_cmd ("debuginfod", class_run,
421 _("Set debuginfod options."),
422 _("Show debuginfod options."),
423 &set_debuginfod_prefix_list,
424 &show_debuginfod_prefix_list,
425 &setlist, &showlist);
426
427 add_setshow_enum_cmd ("enabled", class_run, debuginfod_enabled_enum,
428 _("Set whether to use debuginfod."),
429 _("Show whether to use debuginfod."),
430 _("\
431 When on, enable the use of debuginfod to download missing debug info and\n\
432 source files."),
433 set_debuginfod_enabled,
434 get_debuginfod_enabled,
435 show_debuginfod_enabled,
436 &set_debuginfod_prefix_list,
437 &show_debuginfod_prefix_list);
438
439 /* set/show debuginfod urls */
440 add_setshow_string_noescape_cmd ("urls", class_run, _("\
441 Set the list of debuginfod server URLs."), _("\
442 Show the list of debuginfod server URLs."), _("\
443 Manage the space-separated list of debuginfod server URLs that GDB will query \
444 when missing debuginfo, executables or source files.\nThe default value is \
445 copied from the DEBUGINFOD_URLS environment variable."),
446 set_debuginfod_urls,
447 get_debuginfod_urls,
448 show_debuginfod_urls,
449 &set_debuginfod_prefix_list,
450 &show_debuginfod_prefix_list);
451
452 /* set/show debuginfod verbose */
453 add_setshow_zuinteger_cmd ("verbose", class_support,
454 &debuginfod_verbose, _("\
455 Set verbosity of debuginfod output."), _("\
456 Show debuginfod debugging."), _("\
457 When set to a non-zero value, display verbose output for each debuginfod \
458 query.\nTo disable, set to zero. Verbose output is displayed by default."),
459 nullptr,
460 show_debuginfod_verbose_command,
461 &set_debuginfod_prefix_list,
462 &show_debuginfod_prefix_list);
463 }