Branch data Line data Source code
1 : : // Setup routines for creating fully populated DWFLs. Used in pass 2 and 3.
2 : : // Copyright (C) 2009-2011 Red Hat, Inc.
3 : : //
4 : : // This file is part of systemtap, and is free software. You can
5 : : // redistribute it and/or modify it under the terms of the GNU General
6 : : // Public License (GPL); either version 2, or (at your option) any
7 : : // later version.
8 : :
9 : : #include "config.h"
10 : : #include "setupdwfl.h"
11 : :
12 : : #include "dwarf_wrappers.h"
13 : : #include "dwflpp.h"
14 : : #include "session.h"
15 : : #include "util.h"
16 : :
17 : : #include <algorithm>
18 : : #include <iostream>
19 : : #include <fstream>
20 : : #include <sstream>
21 : : #include <set>
22 : : #include <string>
23 : :
24 : : extern "C" {
25 : : #include <fnmatch.h>
26 : : #include <stdlib.h>
27 : : #include <assert.h>
28 : : #include <time.h>
29 : : #include <sys/times.h>
30 : : #include <sys/time.h>
31 : : #include <sys/types.h>
32 : : #include <sys/wait.h>
33 : : #include <fcntl.h>
34 : : #include <limits.h>
35 : : #include <sys/utsname.h>
36 : : #include <unistd.h>
37 : : }
38 : :
39 : : // XXX: also consider adding $HOME/.debug/ for perf build-id-cache
40 : : static const char *debuginfo_path_arr = "+:.debug:/usr/lib/debug:/var/cache/abrt-di/usr/lib/debug:build";
41 : 2414 : static const char *debuginfo_env_arr = getenv("SYSTEMTAP_DEBUGINFO_PATH");
42 [ + + ]: 2414 : static char *debuginfo_path = (char *)(debuginfo_env_arr ?: debuginfo_path_arr);
43 : :
44 : : // NB: kernel_build_tree doesn't enter into this, as it's for
45 : : // kernel-side modules only.
46 : : // XXX: also consider adding $HOME/.debug/ for perf build-id-cache
47 : : static const char *debuginfo_usr_path_arr = "+:.debug:/usr/lib/debug:/var/cache/abrt-di/usr/lib/debug";
48 [ + + ]: 2414 : static char *debuginfo_usr_path = (char *)(debuginfo_env_arr
49 : : ?: debuginfo_usr_path_arr);
50 : :
51 : : // A pointer to the current systemtap session for use only by a few
52 : : // dwfl calls. DO NOT rely on this, as it is cleared after use.
53 : : // This is a kludge.
54 : : static systemtap_session* current_session_for_find_debuginfo;
55 : :
56 : : static const Dwfl_Callbacks kernel_callbacks =
57 : : {
58 : : dwfl_linux_kernel_find_elf,
59 : : internal_find_debuginfo,
60 : : dwfl_offline_section_address,
61 : : (char **) & debuginfo_path
62 : : };
63 : :
64 : : static const Dwfl_Callbacks user_callbacks =
65 : : {
66 : : NULL,
67 : : internal_find_debuginfo,
68 : : NULL, /* ET_REL not supported for user space, only ET_EXEC and ET_DYN.
69 : : dwfl_offline_section_address, */
70 : : (char **) & debuginfo_usr_path
71 : : };
72 : :
73 : : using namespace std;
74 : :
75 : : // Store last kernel and user Dwfl for reuse since they are often
76 : : // re-requested (in phase 2 and then in phase 3).
77 : 2414 : static DwflPtr kernel_dwfl;
78 : 2414 : static DwflPtr user_dwfl;
79 : :
80 : : // Setup in setup_dwfl_kernel(), for use in setup_dwfl_report_kernel_p().
81 : : // Either offline_search_modname or offline_search_names is
82 : : // used. When offline_search_modname is not NULL then
83 : : // offline_search_names is ignored.
84 : : static const char *offline_search_modname;
85 : 2414 : static set<string> offline_search_names;
86 : : static unsigned offline_modules_found;
87 : :
88 : : // Whether or not we are done reporting kernel modules in
89 : : // set_dwfl_report_kernel_p().
90 : : static bool setup_dwfl_done;
91 : :
92 : : // Kept for user_dwfl cache, user modules don't allow wildcards, so
93 : : // just keep the set of module strings.
94 : 2414 : static set<string> user_modset;
95 : :
96 : : // Determines whether or not we will make setup_dwfl_report_kernel_p
97 : : // report true for all module dependencies. This is necessary for
98 : : // correctly resolving some dwarf constructs that relocate against
99 : : // symbols in vmlinux and/or other modules they depend on. See PR10678.
100 : : static const bool setup_all_deps = true;
101 : :
102 : : // Where to find the kernel (and the Modules.dep file). Setup in
103 : : // setup_dwfl_kernel(), used by dwfl_linux_kernel_report_offline() and
104 : : // setup_mod_deps().
105 : 2414 : static string elfutils_kernel_path;
106 : :
107 [ + - ][ + + ]: 830023 : static bool is_comma_dash(const char c) { return (c == ',' || c == '-'); }
108 : :
109 : : // The path to the abrt-action-install-debuginfo-to-abrt-cache program.
110 [ + - + - ]: 4828 : static const string abrt_path =
[ + - ]
111 : 2414 : (access ("/usr/bin/abrt-action-install-debuginfo-to-abrt-cache", X_OK) == 0
112 : : ? "/usr/bin/abrt-action-install-debuginfo-to-abrt-cache"
113 : 2414 : : (access ("/usr/libexec/abrt-action-install-debuginfo-to-abrt-cache", X_OK) == 0
114 : : ? "/usr/libexec/abrt-action-install-debuginfo-to-abrt-cache"
115 : : : ""));
116 : :
117 : : // The module name is the basename (without the extension) of the
118 : : // module path, with ',' and '-' replaced by '_'.
119 : : static string
120 : 91729 : modname_from_path(const string &path)
121 : : {
122 [ + - ]: 91729 : size_t dot = path.rfind('.');
123 [ + - ]: 91729 : size_t slash = path.rfind('/');
124 [ + - ][ + - ]: 91729 : if (dot == string::npos || slash == string::npos || dot < slash)
[ - + ]
125 [ # # ]: 0 : return "";
126 [ + - ]: 91729 : string name = path.substr(slash + 1, dot - slash - 1);
127 [ + - ][ + - ]: 91729 : replace_if(name.begin(), name.end(), is_comma_dash, '_');
[ + - ]
128 [ + - ][ + - ]: 91729 : return name;
129 : : }
130 : :
131 : : // Try to parse modules.dep file,
132 : : // Simple format: module path (either full or relative), colon,
133 : : // (possibly empty) space delimited list of module (path)
134 : : // dependencies.
135 : : static void
136 : 617 : setup_mod_deps()
137 : : {
138 [ + - ]: 617 : string modulesdep;
139 [ + - ]: 617 : ifstream in;
140 [ + - ]: 617 : string l;
141 : :
142 [ + - ][ - + ]: 617 : if (elfutils_kernel_path[0] == '/')
143 : : {
144 [ # # ]: 0 : modulesdep = elfutils_kernel_path;
145 [ # # ]: 0 : modulesdep += "/modules.dep";
146 : : }
147 : : else
148 : : {
149 [ + - ]: 617 : string sysroot = "";
150 [ + + ]: 617 : if (current_session_for_find_debuginfo)
151 [ + - ]: 616 : sysroot = current_session_for_find_debuginfo->sysroot;
152 [ + - ][ + - ]: 617 : modulesdep = sysroot + "/lib/modules/";
[ + - ]
153 [ + - ]: 617 : modulesdep += elfutils_kernel_path;
154 [ + - ][ + - ]: 617 : modulesdep += "/modules.dep";
155 : : }
156 [ + - ][ + - ]: 617 : in.open(modulesdep.c_str());
157 [ + - ][ - + ]: 617 : if (in.fail ())
158 : 617 : return;
159 : :
160 [ + - ][ + - ]: 91933 : while (getline (in, l))
[ + + ]
161 : : {
162 [ + - ]: 91316 : size_t off = l.find (':');
163 [ + - ]: 91316 : if (off != string::npos)
164 : : {
165 [ + - ][ + - ]: 91316 : string modpath, modname;
166 [ + - ][ + - ]: 91316 : modpath = l.substr (0, off);
[ + - ]
167 [ + - ][ + - ]: 91316 : modname = modname_from_path (modpath);
[ + - ]
168 [ + - ][ - + ]: 91316 : if (modname == "")
169 : 0 : continue;
170 : :
171 : : bool dep_needed;
172 [ + + ]: 91316 : if (offline_search_modname != NULL)
173 : : {
174 [ + - ][ + - ]: 592 : if (dwflpp::name_has_wildcard (offline_search_modname))
[ + - ][ + - ]
175 : : {
176 : : dep_needed = !fnmatch (offline_search_modname,
177 [ + - ][ + - ]: 592 : modname.c_str (), 0);
178 [ + + ]: 592 : if (dep_needed)
179 [ + - ]: 298 : offline_search_names.insert (modname);
180 : : }
181 : : else
182 : : {
183 : : dep_needed = ! strcmp(modname.c_str (),
184 [ # # ]: 0 : offline_search_modname);
185 [ # # ]: 0 : if (dep_needed)
186 [ # # ]: 0 : offline_search_names.insert (modname);
187 : : }
188 : : }
189 : : else
190 : : dep_needed = (offline_search_names.find (modname)
191 [ + - ][ + - ]: 90724 : != offline_search_names.end ());
192 : :
193 [ + + ]: 91316 : if (! dep_needed)
194 : 90778 : continue;
195 : :
196 [ + - ]: 538 : string depstring = l.substr (off + 1);
197 [ + - ][ + + ]: 538 : if (depstring.size () > 0)
198 : : {
199 [ + - ]: 246 : stringstream ss (depstring);
200 [ + - ]: 246 : string deppath;
201 [ + - ][ + - ]: 659 : while (ss >> deppath)
[ + + ]
202 [ + - ][ + - ]: 659 : offline_search_names.insert (modname_from_path(deppath));
[ + - ][ + - ]
[ + - ]
203 : :
204 [ + - ][ + - ]: 91316 : }
[ + + ][ + - ]
[ + + ]
205 : : }
206 : : }
207 : :
208 : : // We always want kernel (needed in list so size checks match).
209 : : // Everything needed now stored in offline_search_names.
210 [ + - ][ + - ]: 617 : offline_search_names.insert ("kernel");
[ + - ]
211 [ + - ][ - + ]: 617 : offline_search_modname = NULL;
[ + - ][ - + ]
[ + - ][ + - ]
212 : : }
213 : :
214 : : // Set up our offline search for kernel modules. We don't want the
215 : : // offline search iteration to do a complete search of the kernel
216 : : // build tree, since that's wasteful, so create a predicate that
217 : : // filters and stops reporting as soon as we got everything.
218 : : static int
219 : 169197 : setup_dwfl_report_kernel_p(const char* modname, const char* filename)
220 : : {
221 : 169197 : assert_no_interrupts();
222 [ + + ]: 169197 : if (setup_dwfl_done)
223 : 735 : return -1;
224 : :
225 : : // elfutils sends us NULL filenames sometimes if it can't find dwarf
226 [ + + ]: 168462 : if (filename == NULL)
227 : 2 : return 0;
228 : :
229 : : // Check kernel first since it is often the only thing needed,
230 : : // then we never have to parse and setup the module deps map.
231 : : // It will be reported as the very first thing.
232 [ + + ]: 168460 : if (setup_all_deps && ! strcmp (modname, "kernel"))
233 : : {
234 [ + + ][ + - ]: 4947 : if ((offline_search_modname != NULL
[ + + ][ + + ]
[ + + ]
235 : 4 : && ! strcmp (offline_search_modname, "kernel"))
236 [ + - ]: 1263 : || (offline_search_names.size() == 1
237 [ + - ][ + - ]: 2421 : && *offline_search_names.begin() == "kernel"))
[ + + ][ # # ]
238 : 646 : setup_dwfl_done = true;
239 : : else
240 : 617 : setup_mod_deps();
241 : :
242 : 1263 : offline_modules_found++;
243 : 1263 : return 1;
244 : : }
245 : :
246 : : // If offline_search_modname is setup use it (either as regexp or
247 : : // explicit module/kernel name) and ignore offline_search_names.
248 : : // Otherwise use offline_search_names exclusively.
249 [ - + ]: 167197 : if (offline_search_modname != NULL)
250 : : {
251 [ # # ][ # # ]: 0 : if (dwflpp::name_has_wildcard (offline_search_modname))
[ # # ][ # # ]
252 : : {
253 : 0 : int match_p = !fnmatch(offline_search_modname, modname, 0);
254 : : // In the wildcard case, we don't short-circuit (return -1)
255 : : // analogously to dwflpp::module_name_final_match().
256 [ # # ]: 0 : if (match_p)
257 : 0 : offline_modules_found++;
258 : 0 : return match_p;
259 : : }
260 : : else
261 : : { /* non-wildcard mode, reject mismatching module names */
262 [ # # ]: 0 : if (strcmp(modname, offline_search_modname))
263 : 0 : return 0;
264 : : else
265 : : {
266 : : // Done, only one name needed and found it.
267 : 0 : offline_modules_found++;
268 : 0 : setup_dwfl_done = true;
269 : 0 : return 1;
270 : : }
271 : : }
272 : : }
273 : : else
274 : : { /* find all in set mode, reject mismatching module names */
275 [ + - ][ + - ]: 167197 : if (offline_search_names.find(modname) == offline_search_names.end())
[ + - ][ + - ]
[ + + ]
276 : 166586 : return 0;
277 : : else
278 : : {
279 : 611 : offline_modules_found++;
280 [ + + ]: 611 : if (offline_search_names.size() == offline_modules_found)
281 : 89 : setup_dwfl_done = true;
282 : 169197 : return 1;
283 : : }
284 : : }
285 : : }
286 : :
287 : 0 : static char * path_insert_sysroot(string sysroot, string path)
288 : : {
289 : : char * path_new;
290 : 0 : size_t pos = 1;
291 [ # # ]: 0 : if (path[0] == '/')
292 : 0 : path.replace(0, 1, sysroot);
293 : 0 : while (true) {
294 : 0 : pos = path.find(":/", pos);
295 [ # # ]: 0 : if (pos == string::npos)
296 : 0 : break;
297 [ # # ]: 0 : path.replace(pos, 2, ":" + sysroot);
298 : 0 : ++pos;
299 : : }
300 : 0 : path_new = new char[path.size()+1];
301 : 0 : strcpy (path_new, path.c_str());
302 : 0 : return path_new;
303 : : }
304 : :
305 : 0 : void debuginfo_path_insert_sysroot(string sysroot)
306 : : {
307 [ # # ][ # # ]: 0 : debuginfo_path = path_insert_sysroot(sysroot, debuginfo_path);
[ # # ]
308 [ # # ][ # # ]: 0 : debuginfo_usr_path = path_insert_sysroot(sysroot, debuginfo_usr_path);
[ # # ]
309 : 0 : }
310 : :
311 : : static DwflPtr
312 : 1265 : setup_dwfl_kernel (unsigned *modules_found, systemtap_session &s)
313 : : {
314 [ + - ]: 1265 : Dwfl *dwfl = dwfl_begin (&kernel_callbacks);
315 [ + - ][ + - ]: 1265 : dwfl_assert ("dwfl_begin", dwfl);
[ + - ]
316 [ + - ]: 1265 : dwfl_report_begin (dwfl);
317 : :
318 : : // We have a problem with -r REVISION vs -r BUILDDIR here. If
319 : : // we're running against a fedora/rhel style kernel-debuginfo
320 : : // tree, s.kernel_build_tree is not the place where the unstripped
321 : : // vmlinux will be installed. Rather, it's over yonder at
322 : : // /usr/lib/debug/lib/modules/$REVISION/. It seems that there is
323 : : // no way to set the dwfl_callback.debuginfo_path and always
324 : : // passs the plain kernel_release here. So instead we have to
325 : : // hard-code this magic here.
326 [ + - ][ + - ]: 2530 : if (s.kernel_build_tree == string(s.sysroot + "/lib/modules/"
327 : : + s.kernel_release
328 [ + - ][ + - ]: 2530 : + "/build"))
[ + - ][ + - ]
[ + - ][ + - ]
329 [ + - ]: 1265 : elfutils_kernel_path = s.kernel_release;
330 : : else
331 [ # # ]: 0 : elfutils_kernel_path = s.kernel_build_tree;
332 : :
333 : 1265 : offline_modules_found = 0;
334 : :
335 : : // First try to report full path modules.
336 [ + - ]: 1265 : set<string>::iterator it = offline_search_names.begin();
337 : 1265 : int kernel = 0;
338 [ + - ][ + + ]: 5827 : while (it != offline_search_names.end())
339 : : {
340 [ + - ][ + + ]: 4562 : if ((*it)[0] == '/')
341 : : {
342 [ + - ]: 3676 : const char *cname = (*it).c_str();
343 [ + - ]: 3676 : Dwfl_Module *mod = dwfl_report_offline (dwfl, cname, cname, -1);
344 [ + - ]: 3676 : if (mod)
345 : 3676 : offline_modules_found++;
346 : : }
347 [ + - ][ + + ]: 886 : else if ((*it) == "kernel")
348 : 657 : kernel = 1;
349 : 4562 : it++;
350 : : }
351 : :
352 : : // We always need this, even when offline_search_modname is NULL
353 : : // and offline_search_names is empty because we still might want
354 : : // the kernel vmlinux reported.
355 : 1265 : setup_dwfl_done = false;
356 : : int rc = dwfl_linux_kernel_report_offline (dwfl,
357 : : elfutils_kernel_path.c_str(),
358 [ + - ][ + - ]: 1265 : &setup_dwfl_report_kernel_p);
359 : :
360 : : (void) rc; /* Ignore since the predicate probably returned -1 at some point,
361 : : And libdwfl interprets that as "whole query failed" rather than
362 : : "found it already, stop looking". */
363 : :
364 : : // NB: the result of an _offline call is the assignment of
365 : : // virtualized addresses to relocatable objects such as
366 : : // modules. These have to be converted to real addresses at
367 : : // run time. See the dwarf_derived_probe ctor and its caller.
368 : :
369 : : // If no modules were found, and we are probing the kernel,
370 : : // attempt to download the kernel debuginfo.
371 [ + + ]: 1265 : if(kernel)
372 : : {
373 : : // Get the kernel build ID. We still need to call this even if we
374 : : // already have the kernel debuginfo installed as it adds the
375 : : // build ID to the script hash.
376 [ + - ]: 657 : string hex = get_kernel_build_id(s);
377 [ + + ][ - + ]: 657 : if (offline_modules_found == 0 && s.download_dbinfo != 0 && !hex.empty())
[ # # ][ # # ]
[ - + ]
378 : : {
379 [ # # ][ # # ]: 0 : rc = download_kernel_debuginfo(s, hex);
[ # # ]
380 [ # # ]: 0 : if(rc >= 0)
381 [ # # ]: 657 : return setup_dwfl_kernel (modules_found, s);
382 [ + - ][ + - ]: 657 : }
383 : : }
384 : :
385 [ + - ][ + - ]: 1265 : dwfl_assert ("dwfl_report_end", dwfl_report_end(dwfl, NULL, NULL));
[ + - ][ + - ]
386 : 1265 : *modules_found = offline_modules_found;
387 : :
388 [ + - ]: 1265 : StapDwfl *stap_dwfl = new StapDwfl(dwfl);
389 [ + - ][ + - ]: 1265 : kernel_dwfl = DwflPtr(stap_dwfl);
[ + - ]
390 : :
391 [ + - ]: 1265 : return kernel_dwfl;
392 : : }
393 : :
394 : : DwflPtr
395 : 1056 : setup_dwfl_kernel(const std::string &name,
396 : : unsigned *found,
397 : : systemtap_session &s)
398 : : {
399 : 1056 : current_session_for_find_debuginfo = &s;
400 [ + - ]: 1056 : const char *modname = name.c_str();
401 [ + - ]: 1056 : set<string> names; // Default to empty
402 : :
403 : : /* Support full path kernel modules, these cannot be regular
404 : : expressions, so just put them in the search set. */
405 [ + - ][ + + ]: 1056 : if (name[0] == '/' || ! dwflpp::name_has_wildcard (modname))
[ + - ][ + - ]
[ + + ][ + + ]
[ + - ][ + + ]
[ + + # #
# # ]
406 : : {
407 [ + - ]: 1052 : names.insert(name);
408 : 1052 : modname = NULL;
409 : : }
410 : :
411 [ + + ][ + + ]: 1511 : if (kernel_dwfl != NULL
[ + + ][ + + ]
412 : : && offline_search_modname == modname
413 [ + - ]: 455 : && offline_search_names == names)
414 : : {
415 : 18 : *found = offline_modules_found;
416 [ + - ]: 18 : return kernel_dwfl;
417 : : }
418 : :
419 : 1038 : offline_search_modname = modname;
420 [ + - ]: 1038 : offline_search_names = names;
421 : :
422 [ + - ][ + - ]: 1056 : return setup_dwfl_kernel(found, s);
423 : : }
424 : :
425 : : DwflPtr
426 : 314 : setup_dwfl_kernel(const std::set<std::string> &names,
427 : : unsigned *found,
428 : : systemtap_session &s)
429 : : {
430 [ + + ]: 609 : if (kernel_dwfl != NULL
[ + - + + ]
[ + + ]
431 : : && offline_search_modname == NULL
432 : 295 : && offline_search_names == names)
433 : : {
434 : 87 : *found = offline_modules_found;
435 : 87 : return kernel_dwfl;
436 : : }
437 : :
438 : 227 : offline_search_modname = NULL;
439 : 227 : offline_search_names = names;
440 : 314 : return setup_dwfl_kernel(found, s);
441 : : }
442 : :
443 : : DwflPtr
444 : 19 : setup_dwfl_user(const std::string &name)
445 : : {
446 [ + + ][ + - ]: 74 : if (user_dwfl != NULL
[ + + ][ + + ]
447 [ + - ]: 18 : && user_modset.size() == 1
448 [ + - ][ + - ]: 37 : && (*user_modset.begin()) == name)
[ + + ][ # # ]
449 : 6 : return user_dwfl;
450 : :
451 : 13 : user_modset.clear();
452 : 13 : user_modset.insert(name);
453 : :
454 : 13 : Dwfl *dwfl = dwfl_begin (&user_callbacks);
455 [ + - ][ + - ]: 13 : dwfl_assert("dwfl_begin", dwfl);
[ + - ]
456 : 13 : dwfl_report_begin (dwfl);
457 : :
458 : : // XXX: should support buildid-based naming
459 : 13 : const char *cname = name.c_str();
460 : 13 : Dwfl_Module *mod = dwfl_report_offline (dwfl, cname, cname, -1);
461 [ + - ][ + - ]: 13 : dwfl_assert ("dwfl_report_end", dwfl_report_end(dwfl, NULL, NULL));
[ + - ][ + - ]
462 [ - + ]: 13 : if (! mod)
463 : : {
464 : 0 : dwfl_end(dwfl);
465 : 0 : dwfl = NULL;
466 : : }
467 : :
468 : 13 : StapDwfl *stap_dwfl = new StapDwfl(dwfl);
469 [ + - ]: 13 : user_dwfl = DwflPtr(stap_dwfl);
470 : :
471 : 19 : return user_dwfl;
472 : : }
473 : :
474 : : DwflPtr
475 : 69 : setup_dwfl_user(std::vector<std::string>::const_iterator &begin,
476 : : const std::vector<std::string>::const_iterator &end,
477 : : bool all_needed, systemtap_session &s)
478 : : {
479 : 69 : current_session_for_find_debuginfo = &s;
480 : : // See if we have this dwfl already cached
481 [ + - ]: 69 : set<string> modset(begin, end);
482 [ + + ][ + - ]: 69 : if (user_dwfl != NULL && modset == user_modset)
[ - + ][ - + ]
483 [ # # ]: 0 : return user_dwfl;
484 : :
485 [ + - ]: 69 : user_modset = modset;
486 : :
487 [ + - ]: 69 : Dwfl *dwfl = dwfl_begin (&user_callbacks);
488 [ + - ][ + - ]: 69 : dwfl_assert("dwfl_begin", dwfl);
[ + - ]
489 [ + - ]: 69 : dwfl_report_begin (dwfl);
490 : 69 : Dwfl_Module *mod = NULL;
491 : : // XXX: should support buildid-based naming
492 [ + - ][ + + ]: 138 : while (begin != end && dwfl != NULL)
[ + - ][ + + ]
493 : : {
494 [ + - ]: 69 : const char *cname = (*begin).c_str();
495 [ + - ]: 69 : mod = dwfl_report_offline (dwfl, cname, cname, -1);
496 [ + + ][ + - ]: 69 : if (! mod && all_needed)
497 : : {
498 [ + - ]: 8 : dwfl_end(dwfl);
499 : 8 : dwfl = NULL;
500 : : }
501 [ + - ]: 69 : begin++;
502 : : }
503 : :
504 : : /* Extract the build id and add it to the session variable
505 : : * so it will be added to the script hash */
506 [ + + ]: 69 : if (mod)
507 : : {
508 : : const unsigned char *bits;
509 : : GElf_Addr vaddr;
510 [ + + ]: 61 : if(s.verbose > 2)
511 [ + - ][ + - ]: 1 : clog << _("Extracting build ID.") << endl;
512 [ + - ]: 61 : int bits_length = dwfl_module_build_id(mod, &bits, &vaddr);
513 : :
514 : : /* Convert the binary bits to a hex string */
515 [ + - ]: 61 : string hex = hex_dump(bits, bits_length);
516 : :
517 : : //Store the build ID in the session
518 [ + - ][ + - ]: 61 : s.build_ids.push_back(hex);
519 : : }
520 : :
521 [ + + ]: 69 : if (dwfl)
522 [ + - ][ + - ]: 61 : dwfl_assert ("dwfl_report_end", dwfl_report_end(dwfl, NULL, NULL));
[ + - ][ + - ]
523 : :
524 [ + - ]: 69 : StapDwfl *stap_dwfl = new StapDwfl(dwfl);
525 [ + - ][ + - ]: 69 : user_dwfl = DwflPtr(stap_dwfl);
[ + - ]
526 : :
527 [ + - ][ + - ]: 69 : return user_dwfl;
528 : : }
529 : :
530 : : bool
531 : 10129 : is_user_module(const std::string &m)
532 : : {
533 [ + + ][ + + ]: 10129 : return m[0] == '/' && m.rfind(".ko", m.length() - 1) != m.length() - 3;
534 : : }
535 : :
536 : : int
537 : 48 : internal_find_debuginfo (Dwfl_Module *mod,
538 : : void **userdata __attribute__ ((unused)),
539 : : const char *modname __attribute__ ((unused)),
540 : : GElf_Addr base __attribute__ ((unused)),
541 : : const char *file_name,
542 : : const char *debuglink_file,
543 : : GElf_Word debuglink_crc,
544 : : char **debuginfo_file_name)
545 : : {
546 : :
547 : : int bits_length;
548 [ + - ]: 48 : string hex;
549 : :
550 : : /* To Keep track of whether the abrt successfully installed the debuginfo */
551 : : static int install_dbinfo_failed = 0;
552 : :
553 : : /* Make sure the current session variable is not null */
554 [ - + ]: 48 : if(current_session_for_find_debuginfo == NULL)
555 : 0 : goto call_dwfl_standard_find_debuginfo;
556 : :
557 : : /* Check to see if download-debuginfo=0 was set */
558 [ - + ][ # # ]: 48 : if(!current_session_for_find_debuginfo->download_dbinfo || abrt_path.empty())
[ # # ][ + - ]
559 : 48 : goto call_dwfl_standard_find_debuginfo;
560 : :
561 : : /* Check that we haven't already run this */
562 [ # # ]: 0 : if (install_dbinfo_failed < 0)
563 : : {
564 [ # # ]: 0 : if(current_session_for_find_debuginfo->verbose > 1)
565 [ # # ][ # # ]: 0 : current_session_for_find_debuginfo->print_warning(_F("We already tried running '%s'", abrt_path.c_str()));
[ # # ][ # # ]
566 : 0 : goto call_dwfl_standard_find_debuginfo;
567 : : }
568 : :
569 : : /* Extract the build ID */
570 : : const unsigned char *bits;
571 : : GElf_Addr vaddr;
572 [ # # ]: 0 : if(current_session_for_find_debuginfo->verbose > 2)
573 [ # # ][ # # ]: 0 : clog << _("Extracting build ID.") << endl;
574 [ # # ]: 0 : bits_length = dwfl_module_build_id(mod, &bits, &vaddr);
575 : :
576 : : /* Convert the binary bits to a hex string */
577 [ # # ][ # # ]: 0 : hex = hex_dump(bits, bits_length);
[ # # ]
578 : :
579 : : /* Search for the debuginfo with the build ID */
580 [ # # ]: 0 : if(current_session_for_find_debuginfo->verbose > 2)
581 [ # # ][ # # ]: 0 : clog << _F("Searching for debuginfo with build ID: '%s'.", hex.c_str()) << endl;
[ # # ][ # # ]
[ # # ]
582 [ # # ]: 0 : if (bits_length > 0)
583 : : {
584 : : int fd = dwfl_build_id_find_debuginfo(mod,
585 : : NULL, NULL, 0,
586 : : NULL, NULL, 0,
587 [ # # ]: 0 : debuginfo_file_name);
588 [ # # ]: 0 : if (fd >= 0)
589 : 0 : return fd;
590 : : }
591 : :
592 : : /* The above failed, so call abrt-action-install-debuginfo-to-abrt-cache
593 : : to download and install the debuginfo */
594 [ # # ]: 0 : if(current_session_for_find_debuginfo->verbose > 1)
595 [ # # ][ # # ]: 0 : clog << _F("Downloading and installing debuginfo with build ID: '%s' using %s.",
[ # # ][ # # ]
[ # # ]
596 [ # # ]: 0 : hex.c_str(), abrt_path.c_str()) << endl;
597 : :
598 : : struct tms tms_before;
599 : 0 : times (& tms_before);
600 : : struct timeval tv_before;
601 : : struct tms tms_after;
602 : : unsigned _sc_clk_tck;
603 : : struct timeval tv_after;
604 : 0 : gettimeofday (&tv_before, NULL);
605 : :
606 [ # # ][ # # ]: 0 : if(execute_abrt_action_install_debuginfo_to_abrt_cache (hex) < 0)
[ # # ][ # # ]
607 : : {
608 : 0 : install_dbinfo_failed = -1;
609 [ # # ][ # # ]: 0 : current_session_for_find_debuginfo->print_warning(_F("%s failed.", abrt_path.c_str()));
[ # # ][ # # ]
610 : 0 : goto call_dwfl_standard_find_debuginfo;
611 : : }
612 : :
613 : 0 : _sc_clk_tck = sysconf (_SC_CLK_TCK);
614 : 0 : times (& tms_after);
615 : 0 : gettimeofday (&tv_after, NULL);
616 [ # # ]: 0 : if(current_session_for_find_debuginfo->verbose > 1)
617 [ # # ]: 0 : clog << _("Download completed in ")
618 : : << ((tms_after.tms_cutime + tms_after.tms_utime
619 [ # # ][ # # ]: 0 : - tms_before.tms_cutime - tms_before.tms_utime) * 1000 / (_sc_clk_tck)) << "usr/"
620 : : << ((tms_after.tms_cstime + tms_after.tms_stime
621 [ # # ][ # # ]: 0 : - tms_before.tms_cstime - tms_before.tms_stime) * 1000 / (_sc_clk_tck)) << "sys/"
622 : : << ((tv_after.tv_sec - tv_before.tv_sec) * 1000 +
623 [ # # ][ # # ]: 0 : ((long)tv_after.tv_usec - (long)tv_before.tv_usec) / 1000) << "real ms"<< endl;
[ # # ]
624 : :
625 : : call_dwfl_standard_find_debuginfo:
626 : :
627 : : /* Call the original dwfl_standard_find_debuginfo */
628 : : return dwfl_standard_find_debuginfo(mod, userdata, modname, base,
629 : : file_name, debuglink_file,
630 [ + - ][ + - ]: 48 : debuglink_crc, debuginfo_file_name);
631 : :
632 : : }
633 : :
634 : : int
635 : 0 : execute_abrt_action_install_debuginfo_to_abrt_cache (string hex)
636 : : {
637 : : /* Be sure that abrt exists */
638 [ # # ][ # # ]: 0 : if (abrt_path.empty())
639 : 0 : return -1;
640 : :
641 : 0 : int timeout = current_session_for_find_debuginfo->download_dbinfo;;
642 [ # # ]: 0 : vector<string> cmd;
643 [ # # ][ # # ]: 0 : cmd.push_back ("/bin/sh");
[ # # ]
644 [ # # ][ # # ]: 0 : cmd.push_back ("-c");
[ # # ]
645 : :
646 : : /* NOTE: abrt does not currently work with asking for confirmation
647 : : * in version abrt-2.0.3-1.fc15.x86_64, Bugzilla: BZ726192 */
648 [ # # ]: 0 : if(current_session_for_find_debuginfo->download_dbinfo == -1)
649 : : {
650 [ # # ][ # # ]: 0 : cmd.push_back ("echo " + hex + " | " + abrt_path + " --ids=-");
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
651 : 0 : timeout = INT_MAX;
652 [ # # ][ # # ]: 0 : current_session_for_find_debuginfo->print_warning(_("Due to bug in abrt, it may continue downloading anyway without asking for confirmation."));
[ # # ]
653 : : }
654 : : else
655 [ # # ][ # # ]: 0 : cmd.push_back ("echo " + hex + " | " + abrt_path + " -y --ids=-");
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
656 : :
657 : : /* NOTE: abrt does not allow canceling the download process at the moment
658 : : * in version abrt-2.0.3-1.fc15.x86_64, Bugzilla: BZ730107 */
659 [ # # ]: 0 : if(timeout != INT_MAX)
660 [ # # ][ # # ]: 0 : current_session_for_find_debuginfo->print_warning(_("Due to a bug in abrt, it may continue downloading after stopping stap if download times out."));
[ # # ]
661 : :
662 : : int pid;
663 [ # # ][ # # ]: 0 : if(current_session_for_find_debuginfo->verbose > 1 || current_session_for_find_debuginfo->download_dbinfo == -1)
664 : : /* Execute abrt-action-install-debuginfo-to-abrt-cache,
665 : : * showing output from abrt */
666 [ # # ][ # # ]: 0 : pid = stap_spawn(current_session_for_find_debuginfo->verbose, cmd, NULL);
[ # # ]
667 : : else
668 : : {
669 : : /* Execute abrt-action-install-debuginfo-to-abrt-cache,
670 : : * without showing output from abrt */
671 : : posix_spawn_file_actions_t fa;
672 [ # # ]: 0 : if (posix_spawn_file_actions_init(&fa) != 0)
673 : 0 : return -1;
674 [ # # ]: 0 : if(posix_spawn_file_actions_addopen(&fa, 1, "/dev/null", O_WRONLY, 0) != 0)
675 : : {
676 : 0 : posix_spawn_file_actions_destroy(&fa);
677 : 0 : return -1;
678 : : }
679 [ # # ][ # # ]: 0 : pid = stap_spawn(current_session_for_find_debuginfo->verbose, cmd, &fa);
[ # # ]
680 : 0 : posix_spawn_file_actions_destroy(&fa);
681 : : }
682 : :
683 : : /* Check to see if either the program successfully completed, or if it timed out. */
684 : 0 : int rstatus = 0;
685 : 0 : int timer = 0;
686 : 0 : int rc = 0;
687 [ # # ]: 0 : while(timer < timeout)
688 : : {
689 [ # # ]: 0 : sleep(1);
690 [ # # ]: 0 : rc = waitpid(pid, &rstatus, WNOHANG);
691 [ # # ]: 0 : if(rc < 0)
692 : 0 : return -1;
693 [ # # ][ # # ]: 0 : if (rc > 0 && WIFEXITED(rstatus))
694 : 0 : break;
695 [ # # ]: 0 : assert_no_interrupts();
696 : 0 : timer++;
697 : : }
698 [ # # ]: 0 : if(timer == timeout)
699 : : {
700 : : /* Timed out! */
701 : 0 : kill(-pid, SIGINT);
702 [ # # ][ # # ]: 0 : current_session_for_find_debuginfo->print_warning(_("Aborted downloading debuginfo: timed out."));
[ # # ]
703 : 0 : return -1;
704 : : }
705 : :
706 : : /* Successfully finished downloading! */
707 : : #if 0 // Should not print this until BZ733690 is fixed as abrt could fail to download
708 : : // and it would still print success.
709 : : if(current_session_for_find_debuginfo->verbose > 1 || current_session_for_find_debuginfo->download_dbinfo == -1)
710 : : clog << _("Download Completed Successfully!") << endl;
711 : : #endif
712 [ # # ][ # # ]: 0 : if(current_session_for_find_debuginfo->verbose > 1 || current_session_for_find_debuginfo->download_dbinfo == -1)
713 [ # # ][ # # ]: 0 : clog << _("ABRT finished attempting to download debuginfo.") << endl;
714 : :
715 [ # # ]: 0 : return 0;
716 : : }
717 : :
718 : : /* Get the kernel build ID */
719 : : string
720 : 657 : get_kernel_build_id(systemtap_session &s)
721 : : {
722 : 657 : bool found = false;
723 [ + - ]: 657 : string hex;
724 : :
725 : : // Try to find BuildID from vmlinux.id
726 [ + - ]: 657 : string kernel_buildID_path = s.kernel_build_tree + "/vmlinux.id";
727 [ + + ]: 657 : if(s.verbose > 1)
728 [ + - ][ + - ]: 9 : clog << _F("Attempting to extract kernel debuginfo build ID from %s", kernel_buildID_path.c_str()) << endl;
[ + - ][ + - ]
[ + - ]
729 [ + - ]: 657 : ifstream buildIDfile;
730 [ + - ][ + - ]: 657 : buildIDfile.open(kernel_buildID_path.c_str());
731 [ + - ][ - + ]: 657 : if(buildIDfile.is_open())
732 : : {
733 [ # # ]: 0 : getline(buildIDfile, hex);
734 [ # # ][ # # ]: 0 : if(buildIDfile.good())
735 : : {
736 : 0 : found = true;
737 : : }
738 [ # # ]: 0 : buildIDfile.close();
739 : : }
740 : :
741 : : // Try to find BuildID from the notes file if the above didn't work and we are
742 : : // building a native module
743 [ + - ][ + - ]: 657 : if(found == false && s.native_build)
744 : : {
745 [ + + ]: 657 : if(s.verbose > 1)
746 [ + - ][ + - ]: 9 : clog << _("Attempting to extract kernel debuginfo build ID from /sys/kernel/notes") << endl;
747 : :
748 : 657 : const char *notesfile = "/sys/kernel/notes";
749 [ + - ]: 657 : int fd = open64 (notesfile, O_RDONLY);
750 [ - + ]: 657 : if (fd < 0)
751 [ # # ]: 0 : return "";
752 : :
753 : : assert (sizeof (Elf32_Nhdr) == sizeof (GElf_Nhdr));
754 : : assert (sizeof (Elf64_Nhdr) == sizeof (GElf_Nhdr));
755 : :
756 : : union
757 : : {
758 : : GElf_Nhdr nhdr;
759 : : unsigned char data[8192];
760 : : } buf;
761 : :
762 [ + - ]: 657 : ssize_t n = read (fd, buf.data, sizeof buf);
763 [ + - ]: 657 : close (fd);
764 : :
765 [ - + ]: 657 : if (n <= 0)
766 [ # # ]: 0 : return "";
767 : :
768 : 657 : unsigned char *p = buf.data;
769 [ + + ]: 1314 : while (p < &buf.data[n])
770 : : {
771 : : /* No translation required since we are reading the native kernel. */
772 : 657 : GElf_Nhdr *nhdr = (GElf_Nhdr *) p;
773 : 657 : p += sizeof *nhdr;
774 : 657 : unsigned char *name = p;
775 : 657 : p += (nhdr->n_namesz + 3) & -4U;
776 : 657 : unsigned char *bits = p;
777 : 657 : p += (nhdr->n_descsz + 3) & -4U;
778 : :
779 [ + - ][ + - ]: 657 : if (p <= &buf.data[n]
[ + - ][ + - ]
780 : : && nhdr->n_type == NT_GNU_BUILD_ID
781 : : && nhdr->n_namesz == sizeof "GNU"
782 : 657 : && !memcmp (name, "GNU", sizeof "GNU"))
783 : : {
784 : : // Found it.
785 [ + - ][ + - ]: 657 : hex = hex_dump(bits, nhdr->n_descsz);
[ + - ]
786 : 657 : found = true;
787 : : }
788 : : }
789 : : }
790 [ + - ]: 657 : if(found)
791 : : {
792 [ + - ]: 657 : return hex;
793 : : }
794 : : else
795 [ # # ][ + - ]: 657 : return "";
[ + - ][ + - ]
796 : : }
797 : :
798 : : /* Find the kernel build ID and attempt to download the matching debuginfo */
799 : 0 : int download_kernel_debuginfo (systemtap_session &s, string hex)
800 : : {
801 : : // NOTE: At some point we want to base the
802 : : // already_tried_downloading_kernel_debuginfo flag on the build ID rather
803 : : // than just the stap process.
804 : :
805 : : // Don't try this again if we already did.
806 : : static int already_tried_downloading_kernel_debuginfo = 0;
807 [ # # ]: 0 : if(already_tried_downloading_kernel_debuginfo)
808 : 0 : return -1;
809 : :
810 : : // Attempt to download the debuginfo
811 [ # # ]: 0 : if(s.verbose > 1)
812 [ # # ][ # # ]: 0 : clog << _F("Success! Extracted kernel debuginfo build ID: %s", hex.c_str()) << endl;
813 [ # # ]: 0 : int rc = execute_abrt_action_install_debuginfo_to_abrt_cache(hex);
814 : 0 : already_tried_downloading_kernel_debuginfo = 1;
815 [ # # ]: 0 : if (rc < 0)
816 : 0 : return -1;
817 : :
818 : : // Success!
819 : 0 : return 0;
820 [ + - ][ + - ]: 7242 : }
|