1 /* debuginfod utilities for GDB.
2    Copyright (C) 2020-2021 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 "cli/cli-style.h"
22 #include "gdbsupport/scoped_fd.h"
23 #include "debuginfod-support.h"
24 #include "gdbsupport/gdb_optional.h"
25 
26 #ifndef HAVE_LIBDEBUGINFOD
27 scoped_fd
debuginfod_source_query(const unsigned char * build_id,int build_id_len,const char * srcpath,gdb::unique_xmalloc_ptr<char> * destname)28 debuginfod_source_query (const unsigned char *build_id,
29 			 int build_id_len,
30 			 const char *srcpath,
31 			 gdb::unique_xmalloc_ptr<char> *destname)
32 {
33   return scoped_fd (-ENOSYS);
34 }
35 
36 scoped_fd
debuginfod_debuginfo_query(const unsigned char * build_id,int build_id_len,const char * filename,gdb::unique_xmalloc_ptr<char> * destname)37 debuginfod_debuginfo_query (const unsigned char *build_id,
38 			    int build_id_len,
39 			    const char *filename,
40 			    gdb::unique_xmalloc_ptr<char> *destname)
41 {
42   return scoped_fd (-ENOSYS);
43 }
44 #else
45 #include <elfutils/debuginfod.h>
46 
47 struct user_data
48 {
user_datauser_data49   user_data (const char *desc, const char *fname)
50     : desc (desc), fname (fname)
51   { }
52 
53   const char * const desc;
54   const char * const fname;
55   gdb::optional<ui_out::progress_meter> meter;
56 };
57 
58 /* Deleter for a debuginfod_client.  */
59 
60 struct debuginfod_client_deleter
61 {
operatordebuginfod_client_deleter62   void operator() (debuginfod_client *c)
63   {
64     debuginfod_end (c);
65   }
66 };
67 
68 using debuginfod_client_up
69   = std::unique_ptr<debuginfod_client, debuginfod_client_deleter>;
70 
71 static int
progressfn(debuginfod_client * c,long cur,long total)72 progressfn (debuginfod_client *c, long cur, long total)
73 {
74   user_data *data = static_cast<user_data *> (debuginfod_get_user_data (c));
75   gdb_assert (data != nullptr);
76 
77   if (check_quit_flag ())
78     {
79       printf_filtered ("Cancelling download of %s %ps...\n",
80 		       data->desc,
81 		       styled_string (file_name_style.style (), data->fname));
82       return 1;
83     }
84 
85   if (total == 0)
86     return 0;
87 
88   if (!data->meter.has_value ())
89     {
90       float size_in_mb = 1.0f * total / (1024 * 1024);
91       string_file styled_filename (current_uiout->can_emit_style_escape ());
92       fprintf_styled (&styled_filename,
93 		      file_name_style.style (),
94 		      "%s",
95 		      data->fname);
96       std::string message
97 	= string_printf ("Downloading %.2f MB %s %s", size_in_mb, data->desc,
98 			 styled_filename.c_str());
99       data->meter.emplace (current_uiout, message, 1);
100     }
101 
102   current_uiout->progress ((double)cur / (double)total);
103 
104   return 0;
105 }
106 
107 static debuginfod_client *
get_debuginfod_client()108 get_debuginfod_client ()
109 {
110   static debuginfod_client_up global_client;
111 
112   if (global_client == nullptr)
113     {
114       global_client.reset (debuginfod_begin ());
115 
116       if (global_client != nullptr)
117 	debuginfod_set_progressfn (global_client.get (), progressfn);
118     }
119 
120   return global_client.get ();
121 }
122 
123 /* See debuginfod-support.h  */
124 
125 scoped_fd
debuginfod_source_query(const unsigned char * build_id,int build_id_len,const char * srcpath,gdb::unique_xmalloc_ptr<char> * destname)126 debuginfod_source_query (const unsigned char *build_id,
127 			 int build_id_len,
128 			 const char *srcpath,
129 			 gdb::unique_xmalloc_ptr<char> *destname)
130 {
131   const char *urls_env_var = getenv (DEBUGINFOD_URLS_ENV_VAR);
132   if (urls_env_var == NULL || urls_env_var[0] == '\0')
133     return scoped_fd (-ENOSYS);
134 
135   debuginfod_client *c = get_debuginfod_client ();
136 
137   if (c == nullptr)
138     return scoped_fd (-ENOMEM);
139 
140   user_data data ("source file", srcpath);
141 
142   debuginfod_set_user_data (c, &data);
143   scoped_fd fd (debuginfod_find_source (c,
144 					build_id,
145 					build_id_len,
146 					srcpath,
147 					nullptr));
148   debuginfod_set_user_data (c, nullptr);
149 
150   /* TODO: Add 'set debug debuginfod' command to control when error messages are shown.  */
151   if (fd.get () < 0 && fd.get () != -ENOENT)
152     printf_filtered (_("Download failed: %s.  Continuing without source file %ps.\n"),
153 		     safe_strerror (-fd.get ()),
154 		     styled_string (file_name_style.style (),  srcpath));
155 
156   if (fd.get () >= 0)
157     *destname = make_unique_xstrdup (srcpath);
158 
159   return fd;
160 }
161 
162 /* See debuginfod-support.h  */
163 
164 scoped_fd
debuginfod_debuginfo_query(const unsigned char * build_id,int build_id_len,const char * filename,gdb::unique_xmalloc_ptr<char> * destname)165 debuginfod_debuginfo_query (const unsigned char *build_id,
166 			    int build_id_len,
167 			    const char *filename,
168 			    gdb::unique_xmalloc_ptr<char> *destname)
169 {
170   const char *urls_env_var = getenv (DEBUGINFOD_URLS_ENV_VAR);
171   if (urls_env_var == NULL || urls_env_var[0] == '\0')
172     return scoped_fd (-ENOSYS);
173 
174   debuginfod_client *c = get_debuginfod_client ();
175 
176   if (c == nullptr)
177     return scoped_fd (-ENOMEM);
178 
179   char *dname = nullptr;
180   user_data data ("separate debug info for", filename);
181 
182   debuginfod_set_user_data (c, &data);
183   scoped_fd fd (debuginfod_find_debuginfo (c, build_id, build_id_len,
184 					   &dname));
185   debuginfod_set_user_data (c, nullptr);
186 
187   if (fd.get () < 0 && fd.get () != -ENOENT)
188     printf_filtered (_("Download failed: %s.  Continuing without debug info for %ps.\n"),
189 		     safe_strerror (-fd.get ()),
190 		     styled_string (file_name_style.style (),  filename));
191 
192   if (fd.get () >= 0)
193     destname->reset (dname);
194 
195   return fd;
196 }
197 #endif
198