xref: /netbsd-src/external/gpl3/gdb.old/dist/gdb/debuginfod-support.c (revision 6881a4007f077b54e5f51159c52b9b25f57deb0d)
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 }
531