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"
28 // XXX: also consider adding $HOME/.debug/ for perf build-id-cache
29 static const char *debuginfo_path_arr
= "+:.debug:/usr/lib/debug:/var/cache/abrt-di/usr/lib/debug:build";
30 static const char *debuginfo_env_arr
= getenv("SYSTEMTAP_DEBUGINFO_PATH");
31 static const char *debuginfo_path
= (debuginfo_env_arr
?: debuginfo_path_arr
);
33 // NB: kernel_build_tree doesn't enter into this, as it's for
34 // kernel-side modules only.
35 // XXX: also consider adding $HOME/.debug/ for perf build-id-cache
36 static const char *debuginfo_usr_path_arr
= "+:.debug:/usr/lib/debug:/var/cache/abrt-di/usr/lib/debug";
37 static const char *debuginfo_usr_path
= (debuginfo_env_arr
38 ?: debuginfo_usr_path_arr
);
40 static const Dwfl_Callbacks kernel_callbacks
=
42 dwfl_linux_kernel_find_elf
,
43 dwfl_standard_find_debuginfo
,
44 dwfl_offline_section_address
,
45 (char **) & debuginfo_path
48 static const Dwfl_Callbacks user_callbacks
=
51 dwfl_standard_find_debuginfo
,
52 NULL
, /* ET_REL not supported for user space, only ET_EXEC and ET_DYN.
53 dwfl_offline_section_address, */
54 (char **) & debuginfo_usr_path
59 // Store last kernel and user Dwfl for reuse since they are often
60 // re-requested (in phase 2 and then in phase 3).
61 static DwflPtr kernel_dwfl
;
62 static DwflPtr user_dwfl
;
64 // Setup in setup_dwfl_kernel(), for use in setup_dwfl_report_kernel_p().
65 // Either offline_search_modname or offline_search_names is
66 // used. When offline_search_modname is not NULL then
67 // offline_search_names is ignored.
68 static const char *offline_search_modname
;
69 static set
<string
> offline_search_names
;
70 static unsigned offline_modules_found
;
72 // Whether or not we are done reporting kernel modules in
73 // set_dwfl_report_kernel_p().
74 static bool setup_dwfl_done
;
76 // Kept for user_dwfl cache, user modules don't allow wildcards, so
77 // just keep the set of module strings.
78 static set
<string
> user_modset
;
80 // Determines whether or not we will make setup_dwfl_report_kernel_p
81 // report true for all module dependencies. This is necessary for
82 // correctly resolving some dwarf constructs that relocate against
83 // symbols in vmlinux and/or other modules they depend on. See PR10678.
84 static const bool setup_all_deps
= true;
86 // Where to find the kernel (and the Modules.dep file). Setup in
87 // setup_dwfl_kernel(), used by dwfl_linux_kernel_report_offline() and
89 static string elfutils_kernel_path
;
91 static bool is_comma_dash(const char c
) { return (c
== ',' || c
== '-'); }
93 // The module name is the basename (without the extension) of the
94 // module path, with ',' and '-' replaced by '_'.
96 modname_from_path(const string
&path
)
98 size_t dot
= path
.rfind('.');
99 size_t slash
= path
.rfind('/');
100 if (dot
== string::npos
|| slash
== string::npos
|| dot
< slash
)
102 string name
= path
.substr(slash
+ 1, dot
- slash
- 1);
103 replace_if(name
.begin(), name
.end(), is_comma_dash
, '_');
107 // Try to parse modules.dep file,
108 // Simple format: module path (either full or relative), colon,
109 // (possibly empty) space delimited list of module (path)
118 if (elfutils_kernel_path
[0] == '/')
120 modulesdep
= elfutils_kernel_path
;
121 modulesdep
+= "/modules.dep";
125 modulesdep
= "/lib/modules/";
126 modulesdep
+= elfutils_kernel_path
;
127 modulesdep
+= "/modules.dep";
129 in
.open(modulesdep
.c_str());
133 while (getline (in
, l
))
135 size_t off
= l
.find (':');
136 if (off
!= string::npos
)
138 string modpath
, modname
;
139 modpath
= l
.substr (0, off
);
140 modname
= modname_from_path (modpath
);
145 if (offline_search_modname
!= NULL
)
147 if (dwflpp::name_has_wildcard (offline_search_modname
))
149 dep_needed
= !fnmatch (offline_search_modname
,
150 modname
.c_str (), 0);
152 offline_search_names
.insert (modname
);
156 dep_needed
= ! strcmp(modname
.c_str (),
157 offline_search_modname
);
159 offline_search_names
.insert (modname
);
163 dep_needed
= (offline_search_names
.find (modname
)
164 != offline_search_names
.end ());
169 string depstring
= l
.substr (off
+ 1);
170 if (depstring
.size () > 0)
172 stringstream
ss (depstring
);
174 while (ss
>> deppath
)
175 offline_search_names
.insert (modname_from_path(deppath
));
181 // We always want kernel (needed in list so size checks match).
182 // Everything needed now stored in offline_search_names.
183 offline_search_names
.insert ("kernel");
184 offline_search_modname
= NULL
;
187 // Set up our offline search for kernel modules. We don't want the
188 // offline search iteration to do a complete search of the kernel
189 // build tree, since that's wasteful, so create a predicate that
190 // filters and stops reporting as soon as we got everything.
192 setup_dwfl_report_kernel_p(const char* modname
, const char* filename
)
194 if (pending_interrupts
|| setup_dwfl_done
)
197 // elfutils sends us NULL filenames sometimes if it can't find dwarf
198 if (filename
== NULL
)
201 // Check kernel first since it is often the only thing needed,
202 // then we never have to parse and setup the module deps map.
203 // It will be reported as the very first thing.
204 if (setup_all_deps
&& ! strcmp (modname
, "kernel"))
206 if ((offline_search_modname
!= NULL
207 && ! strcmp (offline_search_modname
, "kernel"))
208 || (offline_search_names
.size() == 1
209 && *offline_search_names
.begin() == "kernel"))
210 setup_dwfl_done
= true;
214 offline_modules_found
++;
218 // If offline_search_modname is setup use it (either as regexp or
219 // explicit module/kernel name) and ignore offline_search_names.
220 // Otherwise use offline_search_names exclusively.
221 if (offline_search_modname
!= NULL
)
223 if (dwflpp::name_has_wildcard (offline_search_modname
))
225 int match_p
= !fnmatch(offline_search_modname
, modname
, 0);
226 // In the wildcard case, we don't short-circuit (return -1)
227 // analogously to dwflpp::module_name_final_match().
229 offline_modules_found
++;
233 { /* non-wildcard mode, reject mismatching module names */
234 if (strcmp(modname
, offline_search_modname
))
238 // Done, only one name needed and found it.
239 offline_modules_found
++;
240 setup_dwfl_done
= true;
246 { /* find all in set mode, reject mismatching module names */
247 if (offline_search_names
.find(modname
) == offline_search_names
.end())
251 offline_modules_found
++;
252 if (offline_search_names
.size() == offline_modules_found
)
253 setup_dwfl_done
= true;
260 setup_dwfl_kernel (unsigned *modules_found
, systemtap_session
&s
)
262 Dwfl
*dwfl
= dwfl_begin (&kernel_callbacks
);
263 dwfl_assert ("dwfl_begin", dwfl
);
264 dwfl_report_begin (dwfl
);
266 // We have a problem with -r REVISION vs -r BUILDDIR here. If
267 // we're running against a fedora/rhel style kernel-debuginfo
268 // tree, s.kernel_build_tree is not the place where the unstripped
269 // vmlinux will be installed. Rather, it's over yonder at
270 // /usr/lib/debug/lib/modules/$REVISION/. It seems that there is
271 // no way to set the dwfl_callback.debuginfo_path and always
272 // passs the plain kernel_release here. So instead we have to
273 // hard-code this magic here.
274 if (s
.kernel_build_tree
== string("/lib/modules/"
277 elfutils_kernel_path
= s
.kernel_release
;
279 elfutils_kernel_path
= s
.kernel_build_tree
;
281 offline_modules_found
= 0;
283 // First try to report full path modules.
284 set
<string
>::iterator it
= offline_search_names
.begin();
285 while (it
!= offline_search_names
.end())
289 const char *cname
= (*it
).c_str();
290 Dwfl_Module
*mod
= dwfl_report_offline (dwfl
, cname
, cname
, -1);
292 offline_modules_found
++;
297 // We always need this, even when offline_search_modname is NULL
298 // and offline_search_names is empty because we still might want
299 // the kernel vmlinux reported.
300 setup_dwfl_done
= false;
301 int rc
= dwfl_linux_kernel_report_offline (dwfl
,
302 elfutils_kernel_path
.c_str(),
303 &setup_dwfl_report_kernel_p
);
305 (void) rc
; /* Ignore since the predicate probably returned -1 at some point,
306 And libdwfl interprets that as "whole query failed" rather than
307 "found it already, stop looking". */
309 // NB: the result of an _offline call is the assignment of
310 // virtualized addresses to relocatable objects such as
311 // modules. These have to be converted to real addresses at
312 // run time. See the dwarf_derived_probe ctor and its caller.
314 dwfl_assert ("dwfl_report_end", dwfl_report_end(dwfl
, NULL
, NULL
));
315 *modules_found
= offline_modules_found
;
317 StapDwfl
*stap_dwfl
= new StapDwfl(dwfl
);
318 kernel_dwfl
= DwflPtr(stap_dwfl
);
324 setup_dwfl_kernel(const std::string
&name
,
326 systemtap_session
&s
)
328 const char *modname
= name
.c_str();
329 set
<string
> names
; // Default to empty
331 /* Support full path kernel modules, these cannot be regular
332 expressions, so just put them in the search set. */
333 if (name
[0] == '/' || ! dwflpp::name_has_wildcard (modname
))
339 if (kernel_dwfl
!= NULL
340 && offline_search_modname
== modname
341 && offline_search_names
== names
)
343 *found
= offline_modules_found
;
347 offline_search_modname
= modname
;
348 offline_search_names
= names
;
350 return setup_dwfl_kernel(found
, s
);
354 setup_dwfl_kernel(const std::set
<std::string
> &names
,
356 systemtap_session
&s
)
358 if (kernel_dwfl
!= NULL
359 && offline_search_modname
== NULL
360 && offline_search_names
== names
)
362 *found
= offline_modules_found
;
366 offline_search_modname
= NULL
;
367 offline_search_names
= names
;
368 return setup_dwfl_kernel(found
, s
);
372 setup_dwfl_user(const std::string
&name
)
374 if (user_dwfl
!= NULL
375 && user_modset
.size() == 1
376 && (*user_modset
.begin()) == name
)
380 user_modset
.insert(name
);
382 Dwfl
*dwfl
= dwfl_begin (&user_callbacks
);
383 dwfl_assert("dwfl_begin", dwfl
);
384 dwfl_report_begin (dwfl
);
386 // XXX: should support buildid-based naming
387 const char *cname
= name
.c_str();
388 Dwfl_Module
*mod
= dwfl_report_offline (dwfl
, cname
, cname
, -1);
389 dwfl_assert ("dwfl_report_end", dwfl_report_end(dwfl
, NULL
, NULL
));
396 StapDwfl
*stap_dwfl
= new StapDwfl(dwfl
);
397 user_dwfl
= DwflPtr(stap_dwfl
);
403 setup_dwfl_user(std::vector
<std::string
>::const_iterator
&begin
,
404 const std::vector
<std::string
>::const_iterator
&end
,
407 // See if we have this dwfl already cached
408 set
<string
> modset(begin
, end
);
409 if (user_dwfl
!= NULL
&& modset
== user_modset
)
412 user_modset
= modset
;
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 while (begin
!= end
&& dwfl
!= NULL
)
421 const char *cname
= (*begin
).c_str();
422 Dwfl_Module
*mod
= dwfl_report_offline (dwfl
, cname
, cname
, -1);
423 if (! mod
&& all_needed
)
431 dwfl_assert ("dwfl_report_end", dwfl_report_end(dwfl
, NULL
, NULL
));
433 StapDwfl
*stap_dwfl
= new StapDwfl(dwfl
);
434 user_dwfl
= DwflPtr(stap_dwfl
);
440 is_user_module(const std::string
&m
)
442 return m
[0] == '/' && m
.rfind(".ko", m
.length() - 1) != m
.length() - 3;