1 // Setup routines for creating fully populated DWFLs. Used in pass 2 and 3.
2 // Copyright (C) 2009 Red Hat, Inc.
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
10 #include "setupdwfl.h"
12 #include "dwarf_wrappers.h"
29 #include <sys/times.h>
31 #include <sys/types.h>
35 #include <sys/utsname.h>
38 // XXX: also consider adding $HOME/.debug/ for perf build-id-cache
39 static const char *debuginfo_path_arr
= "+:.debug:/usr/lib/debug:/var/cache/abrt-di/usr/lib/debug:build";
40 static const char *debuginfo_env_arr
= getenv("SYSTEMTAP_DEBUGINFO_PATH");
41 static const char *debuginfo_path
= (debuginfo_env_arr
?: debuginfo_path_arr
);
43 // NB: kernel_build_tree doesn't enter into this, as it's for
44 // kernel-side modules only.
45 // XXX: also consider adding $HOME/.debug/ for perf build-id-cache
46 static const char *debuginfo_usr_path_arr
= "+:.debug:/usr/lib/debug:/var/cache/abrt-di/usr/lib/debug";
47 static const char *debuginfo_usr_path
= (debuginfo_env_arr
48 ?: debuginfo_usr_path_arr
);
50 // A pointer to the current systemtap session for use only by a few
51 // dwfl calls. DO NOT rely on this, as it is cleared after use.
53 static systemtap_session
* current_session_for_find_debuginfo
;
55 // The path to the abrt-action-install-debuginfo-to-abrt-cache
57 #define ABRT_PATH "/usr/bin/abrt-action-install-debuginfo-to-abrt-cache"
59 static const Dwfl_Callbacks kernel_callbacks
=
61 dwfl_linux_kernel_find_elf
,
62 internal_find_debuginfo
,
63 dwfl_offline_section_address
,
64 (char **) & debuginfo_path
67 static const Dwfl_Callbacks user_callbacks
=
70 internal_find_debuginfo
,
71 NULL
, /* ET_REL not supported for user space, only ET_EXEC and ET_DYN.
72 dwfl_offline_section_address, */
73 (char **) & debuginfo_usr_path
78 // Store last kernel and user Dwfl for reuse since they are often
79 // re-requested (in phase 2 and then in phase 3).
80 static DwflPtr kernel_dwfl
;
81 static DwflPtr user_dwfl
;
83 // Setup in setup_dwfl_kernel(), for use in setup_dwfl_report_kernel_p().
84 // Either offline_search_modname or offline_search_names is
85 // used. When offline_search_modname is not NULL then
86 // offline_search_names is ignored.
87 static const char *offline_search_modname
;
88 static set
<string
> offline_search_names
;
89 static unsigned offline_modules_found
;
91 // Whether or not we are done reporting kernel modules in
92 // set_dwfl_report_kernel_p().
93 static bool setup_dwfl_done
;
95 // Kept for user_dwfl cache, user modules don't allow wildcards, so
96 // just keep the set of module strings.
97 static set
<string
> user_modset
;
99 // Determines whether or not we will make setup_dwfl_report_kernel_p
100 // report true for all module dependencies. This is necessary for
101 // correctly resolving some dwarf constructs that relocate against
102 // symbols in vmlinux and/or other modules they depend on. See PR10678.
103 static const bool setup_all_deps
= true;
105 // Where to find the kernel (and the Modules.dep file). Setup in
106 // setup_dwfl_kernel(), used by dwfl_linux_kernel_report_offline() and
108 static string elfutils_kernel_path
;
110 static bool is_comma_dash(const char c
) { return (c
== ',' || c
== '-'); }
112 // The module name is the basename (without the extension) of the
113 // module path, with ',' and '-' replaced by '_'.
115 modname_from_path(const string
&path
)
117 size_t dot
= path
.rfind('.');
118 size_t slash
= path
.rfind('/');
119 if (dot
== string::npos
|| slash
== string::npos
|| dot
< slash
)
121 string name
= path
.substr(slash
+ 1, dot
- slash
- 1);
122 replace_if(name
.begin(), name
.end(), is_comma_dash
, '_');
126 // Try to parse modules.dep file,
127 // Simple format: module path (either full or relative), colon,
128 // (possibly empty) space delimited list of module (path)
137 if (elfutils_kernel_path
[0] == '/')
139 modulesdep
= elfutils_kernel_path
;
140 modulesdep
+= "/modules.dep";
144 modulesdep
= "/lib/modules/";
145 modulesdep
+= elfutils_kernel_path
;
146 modulesdep
+= "/modules.dep";
148 in
.open(modulesdep
.c_str());
152 while (getline (in
, l
))
154 size_t off
= l
.find (':');
155 if (off
!= string::npos
)
157 string modpath
, modname
;
158 modpath
= l
.substr (0, off
);
159 modname
= modname_from_path (modpath
);
164 if (offline_search_modname
!= NULL
)
166 if (dwflpp::name_has_wildcard (offline_search_modname
))
168 dep_needed
= !fnmatch (offline_search_modname
,
169 modname
.c_str (), 0);
171 offline_search_names
.insert (modname
);
175 dep_needed
= ! strcmp(modname
.c_str (),
176 offline_search_modname
);
178 offline_search_names
.insert (modname
);
182 dep_needed
= (offline_search_names
.find (modname
)
183 != offline_search_names
.end ());
188 string depstring
= l
.substr (off
+ 1);
189 if (depstring
.size () > 0)
191 stringstream
ss (depstring
);
193 while (ss
>> deppath
)
194 offline_search_names
.insert (modname_from_path(deppath
));
200 // We always want kernel (needed in list so size checks match).
201 // Everything needed now stored in offline_search_names.
202 offline_search_names
.insert ("kernel");
203 offline_search_modname
= NULL
;
206 // Set up our offline search for kernel modules. We don't want the
207 // offline search iteration to do a complete search of the kernel
208 // build tree, since that's wasteful, so create a predicate that
209 // filters and stops reporting as soon as we got everything.
211 setup_dwfl_report_kernel_p(const char* modname
, const char* filename
)
213 if (pending_interrupts
|| setup_dwfl_done
)
216 // elfutils sends us NULL filenames sometimes if it can't find dwarf
217 if (filename
== NULL
)
220 // Check kernel first since it is often the only thing needed,
221 // then we never have to parse and setup the module deps map.
222 // It will be reported as the very first thing.
223 if (setup_all_deps
&& ! strcmp (modname
, "kernel"))
225 if ((offline_search_modname
!= NULL
226 && ! strcmp (offline_search_modname
, "kernel"))
227 || (offline_search_names
.size() == 1
228 && *offline_search_names
.begin() == "kernel"))
229 setup_dwfl_done
= true;
233 offline_modules_found
++;
237 // If offline_search_modname is setup use it (either as regexp or
238 // explicit module/kernel name) and ignore offline_search_names.
239 // Otherwise use offline_search_names exclusively.
240 if (offline_search_modname
!= NULL
)
242 if (dwflpp::name_has_wildcard (offline_search_modname
))
244 int match_p
= !fnmatch(offline_search_modname
, modname
, 0);
245 // In the wildcard case, we don't short-circuit (return -1)
246 // analogously to dwflpp::module_name_final_match().
248 offline_modules_found
++;
252 { /* non-wildcard mode, reject mismatching module names */
253 if (strcmp(modname
, offline_search_modname
))
257 // Done, only one name needed and found it.
258 offline_modules_found
++;
259 setup_dwfl_done
= true;
265 { /* find all in set mode, reject mismatching module names */
266 if (offline_search_names
.find(modname
) == offline_search_names
.end())
270 offline_modules_found
++;
271 if (offline_search_names
.size() == offline_modules_found
)
272 setup_dwfl_done
= true;
279 setup_dwfl_kernel (unsigned *modules_found
, systemtap_session
&s
)
281 Dwfl
*dwfl
= dwfl_begin (&kernel_callbacks
);
282 dwfl_assert ("dwfl_begin", dwfl
);
283 dwfl_report_begin (dwfl
);
285 // We have a problem with -r REVISION vs -r BUILDDIR here. If
286 // we're running against a fedora/rhel style kernel-debuginfo
287 // tree, s.kernel_build_tree is not the place where the unstripped
288 // vmlinux will be installed. Rather, it's over yonder at
289 // /usr/lib/debug/lib/modules/$REVISION/. It seems that there is
290 // no way to set the dwfl_callback.debuginfo_path and always
291 // passs the plain kernel_release here. So instead we have to
292 // hard-code this magic here.
293 if (s
.kernel_build_tree
== string("/lib/modules/"
296 elfutils_kernel_path
= s
.kernel_release
;
298 elfutils_kernel_path
= s
.kernel_build_tree
;
300 offline_modules_found
= 0;
302 // First try to report full path modules.
303 set
<string
>::iterator it
= offline_search_names
.begin();
305 while (it
!= offline_search_names
.end())
309 const char *cname
= (*it
).c_str();
310 Dwfl_Module
*mod
= dwfl_report_offline (dwfl
, cname
, cname
, -1);
312 offline_modules_found
++;
314 else if ((*it
) == "kernel")
319 // We always need this, even when offline_search_modname is NULL
320 // and offline_search_names is empty because we still might want
321 // the kernel vmlinux reported.
322 setup_dwfl_done
= false;
323 int rc
= dwfl_linux_kernel_report_offline (dwfl
,
324 elfutils_kernel_path
.c_str(),
325 &setup_dwfl_report_kernel_p
);
327 (void) rc
; /* Ignore since the predicate probably returned -1 at some point,
328 And libdwfl interprets that as "whole query failed" rather than
329 "found it already, stop looking". */
331 // NB: the result of an _offline call is the assignment of
332 // virtualized addresses to relocatable objects such as
333 // modules. These have to be converted to real addresses at
334 // run time. See the dwarf_derived_probe ctor and its caller.
336 // If no modules were found, and we are probing the kernel,
337 // attempt to download the kernel debuginfo.
338 if(offline_modules_found
== 0 && kernel
)
340 rc
= download_kernel_debuginfo(s
);
342 return setup_dwfl_kernel (modules_found
, s
);
345 dwfl_assert ("dwfl_report_end", dwfl_report_end(dwfl
, NULL
, NULL
));
346 *modules_found
= offline_modules_found
;
348 StapDwfl
*stap_dwfl
= new StapDwfl(dwfl
);
349 kernel_dwfl
= DwflPtr(stap_dwfl
);
355 setup_dwfl_kernel(const std::string
&name
,
357 systemtap_session
&s
)
359 current_session_for_find_debuginfo
= &s
;
360 const char *modname
= name
.c_str();
361 set
<string
> names
; // Default to empty
363 /* Support full path kernel modules, these cannot be regular
364 expressions, so just put them in the search set. */
365 if (name
[0] == '/' || ! dwflpp::name_has_wildcard (modname
))
371 if (kernel_dwfl
!= NULL
372 && offline_search_modname
== modname
373 && offline_search_names
== names
)
375 *found
= offline_modules_found
;
379 offline_search_modname
= modname
;
380 offline_search_names
= names
;
382 return setup_dwfl_kernel(found
, s
);
386 setup_dwfl_kernel(const std::set
<std::string
> &names
,
388 systemtap_session
&s
)
390 if (kernel_dwfl
!= NULL
391 && offline_search_modname
== NULL
392 && offline_search_names
== names
)
394 *found
= offline_modules_found
;
398 offline_search_modname
= NULL
;
399 offline_search_names
= names
;
400 return setup_dwfl_kernel(found
, s
);
404 setup_dwfl_user(const std::string
&name
)
406 if (user_dwfl
!= NULL
407 && user_modset
.size() == 1
408 && (*user_modset
.begin()) == name
)
412 user_modset
.insert(name
);
414 Dwfl
*dwfl
= dwfl_begin (&user_callbacks
);
415 dwfl_assert("dwfl_begin", dwfl
);
416 dwfl_report_begin (dwfl
);
418 // XXX: should support buildid-based naming
419 const char *cname
= name
.c_str();
420 Dwfl_Module
*mod
= dwfl_report_offline (dwfl
, cname
, cname
, -1);
421 dwfl_assert ("dwfl_report_end", dwfl_report_end(dwfl
, NULL
, NULL
));
428 StapDwfl
*stap_dwfl
= new StapDwfl(dwfl
);
429 user_dwfl
= DwflPtr(stap_dwfl
);
435 setup_dwfl_user(std::vector
<std::string
>::const_iterator
&begin
,
436 const std::vector
<std::string
>::const_iterator
&end
,
437 bool all_needed
, systemtap_session
&s
)
439 current_session_for_find_debuginfo
= &s
;
440 // See if we have this dwfl already cached
441 set
<string
> modset(begin
, end
);
442 if (user_dwfl
!= NULL
&& modset
== user_modset
)
445 user_modset
= modset
;
447 Dwfl
*dwfl
= dwfl_begin (&user_callbacks
);
448 dwfl_assert("dwfl_begin", dwfl
);
449 dwfl_report_begin (dwfl
);
451 // XXX: should support buildid-based naming
452 while (begin
!= end
&& dwfl
!= NULL
)
454 const char *cname
= (*begin
).c_str();
455 Dwfl_Module
*mod
= dwfl_report_offline (dwfl
, cname
, cname
, -1);
456 if (! mod
&& all_needed
)
464 dwfl_assert ("dwfl_report_end", dwfl_report_end(dwfl
, NULL
, NULL
));
466 StapDwfl
*stap_dwfl
= new StapDwfl(dwfl
);
467 user_dwfl
= DwflPtr(stap_dwfl
);
473 is_user_module(const std::string
&m
)
475 return m
[0] == '/' && m
.rfind(".ko", m
.length() - 1) != m
.length() - 3;
479 internal_find_debuginfo (Dwfl_Module
*mod
,
480 void **userdata
__attribute__ ((unused
)),
481 const char *modname
__attribute__ ((unused
)),
482 GElf_Addr base
__attribute__ ((unused
)),
483 const char *file_name
,
484 const char *debuglink_file
,
485 GElf_Word debuglink_crc
,
486 char **debuginfo_file_name
)
491 /* To Keep track of whether the abrt successfully installed the debuginfo */
492 static int install_dbinfo_failed
= 0;
494 /* Make sure the current session variable is not null */
495 if(current_session_for_find_debuginfo
== NULL
)
496 goto call_dwfl_standard_find_debuginfo
;
498 /* Check to see if download-debuginfo=0 was set */
499 if(!current_session_for_find_debuginfo
->download_dbinfo
)
500 goto call_dwfl_standard_find_debuginfo
;
502 /* Check that we haven't already run this */
503 if (install_dbinfo_failed
< 0)
505 if(current_session_for_find_debuginfo
->verbose
> 1 && !current_session_for_find_debuginfo
->suppress_warnings
)
506 current_session_for_find_debuginfo
->print_warning( _F("We already tried running '%s'", ABRT_PATH
));
507 goto call_dwfl_standard_find_debuginfo
;
510 /* Extract the build ID */
511 const unsigned char *bits
;
513 if(current_session_for_find_debuginfo
->verbose
> 2)
514 clog
<< _("Extracting build ID.") << endl
;
515 bits_length
= dwfl_module_build_id(mod
, &bits
, &vaddr
);
517 /* Convert the binary bits to a hex string */
518 hex
= hex_dump(bits
, bits_length
);
520 /* Search for the debuginfo with the build ID */
521 if(current_session_for_find_debuginfo
->verbose
> 2)
522 clog
<< _F("Searching for debuginfo with build ID: '%s'.", hex
.c_str()) << endl
;
525 int fd
= dwfl_build_id_find_debuginfo(mod
,
528 debuginfo_file_name
);
533 /* The above failed, so call abrt-action-install-debuginfo-to-abrt-cache
534 to download and install the debuginfo */
535 if(current_session_for_find_debuginfo
->verbose
> 1)
536 clog
<< _F("Downloading and installing debuginfo with build ID: '%s' using %s.", hex
.c_str(), ABRT_PATH
) << endl
;
538 struct tms tms_before
;
539 times (& tms_before
);
540 struct timeval tv_before
;
541 struct tms tms_after
;
542 unsigned _sc_clk_tck
;
543 struct timeval tv_after
;
544 gettimeofday (&tv_before
, NULL
);
546 if(execute_abrt_action_install_debuginfo_to_abrt_cache (hex
) < 0)
548 install_dbinfo_failed
= -1;
549 if (!current_session_for_find_debuginfo
->suppress_warnings
)
550 current_session_for_find_debuginfo
->print_warning(_F("%s failed.", ABRT_PATH
));
551 goto call_dwfl_standard_find_debuginfo
;
554 _sc_clk_tck
= sysconf (_SC_CLK_TCK
);
556 gettimeofday (&tv_after
, NULL
);
557 if(current_session_for_find_debuginfo
->verbose
> 1)
558 clog
<< _("Download completed in ")
559 << ((tms_after
.tms_cutime
+ tms_after
.tms_utime
560 - tms_before
.tms_cutime
- tms_before
.tms_utime
) * 1000 / (_sc_clk_tck
)) << "usr/"
561 << ((tms_after
.tms_cstime
+ tms_after
.tms_stime
562 - tms_before
.tms_cstime
- tms_before
.tms_stime
) * 1000 / (_sc_clk_tck
)) << "sys/"
563 << ((tv_after
.tv_sec
- tv_before
.tv_sec
) * 1000 +
564 ((long)tv_after
.tv_usec
- (long)tv_before
.tv_usec
) / 1000) << "real ms"<< endl
;
566 call_dwfl_standard_find_debuginfo
:
568 /* Call the original dwfl_standard_find_debuginfo */
569 return dwfl_standard_find_debuginfo(mod
, userdata
, modname
, base
,
570 file_name
, debuglink_file
,
571 debuglink_crc
, debuginfo_file_name
);
576 execute_abrt_action_install_debuginfo_to_abrt_cache (string hex
)
578 /* Check that abrt exists */
579 if(access (ABRT_PATH
, X_OK
) < 0)
582 int timeout
= current_session_for_find_debuginfo
->download_dbinfo
;;
584 cmd
.push_back ("/bin/sh");
585 cmd
.push_back ("-c");
587 /* NOTE: abrt does not currently work with asking for confirmation
588 * in version abrt-2.0.3-1.fc15.x86_64, Bugzilla: BZ726192 */
589 if(current_session_for_find_debuginfo
->download_dbinfo
== -1)
591 cmd
.push_back ("echo " + hex
+ " | " + ABRT_PATH
+ " --ids=-");
593 if(!current_session_for_find_debuginfo
->suppress_warnings
)
594 current_session_for_find_debuginfo
->print_warning(_("Due to bug in abrt, it may continue downloading anyway without asking for confirmation."));
597 cmd
.push_back ("echo " + hex
+ " | " + ABRT_PATH
+ " -y --ids=-");
599 /* NOTE: abrt does not allow canceling the download process at the moment
600 * in version abrt-2.0.3-1.fc15.x86_64, Bugzilla: BZ730107 */
601 if(timeout
!= INT_MAX
&& !current_session_for_find_debuginfo
->suppress_warnings
)
602 current_session_for_find_debuginfo
->print_warning(_("Due to a bug in abrt, it may continue downloading after stopping stap if download times out."));
605 if(current_session_for_find_debuginfo
->verbose
> 1 || current_session_for_find_debuginfo
->download_dbinfo
== -1)
606 /* Execute abrt-action-install-debuginfo-to-abrt-cache,
607 * showing output from abrt */
608 pid
= stap_spawn(current_session_for_find_debuginfo
->verbose
, cmd
, NULL
);
611 /* Execute abrt-action-install-debuginfo-to-abrt-cache,
612 * without showing output from abrt */
613 posix_spawn_file_actions_t fa
;
614 if (posix_spawn_file_actions_init(&fa
) != 0)
616 if(posix_spawn_file_actions_addopen(&fa
, 1, "/dev/null", O_WRONLY
, 0) != 0)
618 posix_spawn_file_actions_destroy(&fa
);
621 pid
= stap_spawn(current_session_for_find_debuginfo
->verbose
, cmd
, &fa
);
622 posix_spawn_file_actions_destroy(&fa
);
625 /* Check to see if either the program successfully completed, or if it timed out. */
629 while(timer
< timeout
)
632 rc
= waitpid(pid
, &rstatus
, WNOHANG
);
635 if (rc
> 0 && WIFEXITED(rstatus
))
637 if(pending_interrupts
)
645 if (!current_session_for_find_debuginfo
->suppress_warnings
)
646 current_session_for_find_debuginfo
->print_warning(_("Aborted downloading debuginfo: timed out."));
650 /* Successfully finished downloading! */
651 #if 0 // Should not print this until BZ733690 is fixed as abrt could fail to download
652 // and it would still print success.
653 if(current_session_for_find_debuginfo
->verbose
> 1 || current_session_for_find_debuginfo
->download_dbinfo
== -1)
654 clog
<< _("Download Completed Successfully!") << endl
;
656 if(current_session_for_find_debuginfo
->verbose
> 1 || current_session_for_find_debuginfo
->download_dbinfo
== -1)
657 clog
<< _("ABRT finished attempting to download debuginfo.") << endl
;
662 /* Look for a build ID note in /sys/kernel/notes */
664 get_kernel_build_id_from_notes ()
666 const char *notesfile
= "/sys/kernel/notes";
667 int fd
= open64 (notesfile
, O_RDONLY
);
671 assert (sizeof (Elf32_Nhdr
) == sizeof (GElf_Nhdr
));
672 assert (sizeof (Elf64_Nhdr
) == sizeof (GElf_Nhdr
));
677 unsigned char data
[8192];
680 ssize_t n
= read (fd
, buf
.data
, sizeof buf
);
686 unsigned char *p
= buf
.data
;
687 while (p
< &buf
.data
[n
])
689 /* No translation required since we are reading the native kernel. */
690 GElf_Nhdr
*nhdr
= (GElf_Nhdr
*) p
;
692 unsigned char *name
= p
;
693 p
+= (nhdr
->n_namesz
+ 3) & -4U;
694 unsigned char *bits
= p
;
695 p
+= (nhdr
->n_descsz
+ 3) & -4U;
697 if (p
<= &buf
.data
[n
]
698 && nhdr
->n_type
== NT_GNU_BUILD_ID
699 && nhdr
->n_namesz
== sizeof "GNU"
700 && !memcmp (name
, "GNU", sizeof "GNU"))
703 return hex_dump(bits
, nhdr
->n_descsz
);
709 /* Find the kernel build ID and attempt to download the matching debuginfo */
710 int download_kernel_debuginfo (systemtap_session
&s
)
712 // NOTE: At some point we want to base the
713 // already_tried_downloading_kernel_debuginfo flag on the build ID rather
714 // than just the stap process.
718 // Don't try this again if we already did.
719 static int already_tried_downloading_kernel_debuginfo
= 0;
720 if(already_tried_downloading_kernel_debuginfo
)
723 // Get the kernel information
724 struct utsname kernelinfo
;
725 if( uname(&kernelinfo
) < 0)
728 // Try to find BuildID from vmlinux.id
729 string kernel_buildID_path
= "/lib/modules/"
730 + (string
)kernelinfo
.release
731 + "/build/vmlinux.id";
733 clog
<< _F("Attempting to extract kernel debuginfo build ID from %s", kernel_buildID_path
.c_str()) << endl
;
734 ifstream buildIDfile
;
735 buildIDfile
.open(kernel_buildID_path
.c_str());
736 if(buildIDfile
.is_open())
738 getline(buildIDfile
, hex
);
739 if(buildIDfile
.good())
746 // Try to find BuildID from the notes file if the above didn't work and we are
747 // building a native module
748 if(found
== false && s
.release
== kernelinfo
.release
)
751 clog
<< _("Attempting to extract kernel debuginfo build ID from /sys/kernel/notes") << endl
;
753 hex
= get_kernel_build_id_from_notes();
758 // If we still didn't find it, return -1
762 // We found the BuildID hex, so attempt to download the debuginfo
764 clog
<< _F("Success! Extracted kernel debuginfo build ID: %s", hex
.c_str()) << endl
;
765 int rc
= execute_abrt_action_install_debuginfo_to_abrt_cache(hex
);
766 already_tried_downloading_kernel_debuginfo
= 1;