packaging: Add python3-base dependency
[platform/upstream/gdb.git] / gdb / debuginfod-support.c
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.  */
31 static cmd_list_element *set_debuginfod_prefix_list;
32 static cmd_list_element *show_debuginfod_prefix_list;
33
34 static const char debuginfod_on[] = "on";
35 static const char debuginfod_off[] = "off";
36 static const char debuginfod_ask[] = "ask";
37
38 static const char *debuginfod_enabled_enum[] =
39 {
40   debuginfod_on,
41   debuginfod_off,
42   debuginfod_ask,
43   nullptr
44 };
45
46 static const char *debuginfod_enabled =
47 #if defined(HAVE_LIBDEBUGINFOD)
48   debuginfod_ask;
49 #else
50   debuginfod_off;
51 #endif
52
53 static unsigned int debuginfod_verbose = 1;
54
55 #ifndef HAVE_LIBDEBUGINFOD
56 scoped_fd
57 debuginfod_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
65 scoped_fd
66 debuginfod_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
74 scoped_fd
75 debuginfod_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
88 struct 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;
96   ui_out::progress_update progress;
97 };
98
99 /* Deleter for a debuginfod_client.  */
100
101 struct debuginfod_client_deleter
102 {
103   void operator() (debuginfod_client *c)
104   {
105     debuginfod_end (c);
106   }
107 };
108
109 using 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
118 static const char *
119 get_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
139 static int
140 progressfn (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
183 static debuginfod_client *
184 get_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
202 static bool
203 debuginfod_is_enabled ()
204 {
205   const char *urls = skip_spaces (getenv (DEBUGINFOD_URLS_ENV_VAR));
206
207   if (debuginfod_enabled == debuginfod_off
208       || urls == nullptr
209       || *urls == '\0')
210     return false;
211
212   if (debuginfod_enabled == debuginfod_ask)
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
232           gdb_printf
233             (_("  <%ps>\n"),
234              styled_string (file_name_style.style (),
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"));
248           debuginfod_enabled = debuginfod_off;
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"));
255       debuginfod_enabled = debuginfod_on;
256     }
257
258   return true;
259 }
260
261 /* Print the result of the most recent attempted download.  */
262
263 static void
264 print_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,
273                 styled_string (file_name_style.style (), data.fname));
274 }
275
276 /* See debuginfod-support.h  */
277
278 scoped_fd
279 debuginfod_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;
297   if (target_supports_terminal_ours ())
298     {
299       term_state.emplace ();
300       target_terminal::ours ();
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
319 scoped_fd
320 debuginfod_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;
338   if (target_supports_terminal_ours ())
339     {
340       term_state.emplace ();
341       target_terminal::ours ();
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
357 scoped_fd
358 debuginfod_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;
376   if (target_supports_terminal_ours ())
377     {
378       term_state.emplace ();
379       target_terminal::ours ();
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
395 static void
396 set_debuginfod_enabled (const char *value)
397 {
398 #if defined(HAVE_LIBDEBUGINFOD)
399   debuginfod_enabled = value;
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
409 static const char *
410 get_debuginfod_enabled ()
411 {
412   return debuginfod_enabled;
413 }
414
415 /* Show callback for "set debuginfod enabled".  */
416
417 static void
418 show_debuginfod_enabled (ui_file *file, int from_tty, cmd_list_element *cmd,
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
428 static void
429 set_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
441 static const std::string&
442 get_debuginfod_urls ()
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
459 static void
460 show_debuginfod_urls (ui_file *file, int from_tty, cmd_list_element *cmd,
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
472 static void
473 show_debuginfod_verbose_command (ui_file *file, int from_tty,
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
482 void _initialize_debuginfod ();
483 void
484 _initialize_debuginfod ()
485 {
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);
493
494   add_setshow_enum_cmd ("enabled", class_run, debuginfod_enabled_enum,
495                         _("Set whether to use debuginfod."),
496                         _("Show whether to use debuginfod."),
497                         _("\
498 When on, enable the use of debuginfod to download missing debug info and\n\
499 source files."),
500                         set_debuginfod_enabled,
501                         get_debuginfod_enabled,
502                         show_debuginfod_enabled,
503                         &set_debuginfod_prefix_list,
504                         &show_debuginfod_prefix_list);
505
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."),
513                                    set_debuginfod_urls,
514                                    get_debuginfod_urls,
515                                    show_debuginfod_urls,
516                                    &set_debuginfod_prefix_list,
517                                    &show_debuginfod_prefix_list);
518
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."),
526                              nullptr,
527                              show_debuginfod_verbose_command,
528                              &set_debuginfod_prefix_list,
529                              &show_debuginfod_prefix_list);
530 }