]> sourceware.org Git - systemtap.git/blob - setupdwfl.cxx
Consolidate task_finder/vma tracker initialization.
[systemtap.git] / setupdwfl.cxx
1 // Setup routines for creating fully populated DWFLs. Used in pass 2 and 3.
2 // Copyright (C) 2009 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
16 #include <algorithm>
17 #include <iostream>
18 #include <fstream>
19 #include <sstream>
20 #include <set>
21 #include <string>
22
23 extern "C" {
24 #include <fnmatch.h>
25 #include <stdlib.h>
26 }
27
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);
32
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);
39
40 static const Dwfl_Callbacks kernel_callbacks =
41 {
42 dwfl_linux_kernel_find_elf,
43 dwfl_standard_find_debuginfo,
44 dwfl_offline_section_address,
45 (char **) & debuginfo_path
46 };
47
48 static const Dwfl_Callbacks user_callbacks =
49 {
50 NULL,
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
55 };
56
57 using namespace std;
58
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;
63
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;
71
72 // Whether or not we are done reporting kernel modules in
73 // set_dwfl_report_kernel_p().
74 static bool setup_dwfl_done;
75
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;
79
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;
85
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
88 // setup_mod_deps().
89 static string elfutils_kernel_path;
90
91 static bool is_comma_dash(const char c) { return (c == ',' || c == '-'); }
92
93 // The module name is the basename (without the extension) of the
94 // module path, with ',' and '-' replaced by '_'.
95 static string
96 modname_from_path(const string &path)
97 {
98 size_t dot = path.rfind('.');
99 size_t slash = path.rfind('/');
100 if (dot == string::npos || slash == string::npos || dot < slash)
101 return "";
102 string name = path.substr(slash + 1, dot - slash - 1);
103 replace_if(name.begin(), name.end(), is_comma_dash, '_');
104 return name;
105 }
106
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)
110 // dependencies.
111 static void
112 setup_mod_deps()
113 {
114 string modulesdep;
115 ifstream in;
116 string l;
117
118 if (elfutils_kernel_path[0] == '/')
119 {
120 modulesdep = elfutils_kernel_path;
121 modulesdep += "/modules.dep";
122 }
123 else
124 {
125 modulesdep = "/lib/modules/";
126 modulesdep += elfutils_kernel_path;
127 modulesdep += "/modules.dep";
128 }
129 in.open(modulesdep.c_str());
130 if (in.fail ())
131 return;
132
133 while (getline (in, l))
134 {
135 size_t off = l.find (':');
136 if (off != string::npos)
137 {
138 string modpath, modname;
139 modpath = l.substr (0, off);
140 modname = modname_from_path (modpath);
141 if (modname == "")
142 continue;
143
144 bool dep_needed;
145 if (offline_search_modname != NULL)
146 {
147 if (dwflpp::name_has_wildcard (offline_search_modname))
148 {
149 dep_needed = !fnmatch (offline_search_modname,
150 modname.c_str (), 0);
151 if (dep_needed)
152 offline_search_names.insert (modname);
153 }
154 else
155 {
156 dep_needed = ! strcmp(modname.c_str (),
157 offline_search_modname);
158 if (dep_needed)
159 offline_search_names.insert (modname);
160 }
161 }
162 else
163 dep_needed = (offline_search_names.find (modname)
164 != offline_search_names.end ());
165
166 if (! dep_needed)
167 continue;
168
169 string depstring = l.substr (off + 1);
170 if (depstring.size () > 0)
171 {
172 stringstream ss (depstring);
173 string deppath;
174 while (ss >> deppath)
175 offline_search_names.insert (modname_from_path(deppath));
176
177 }
178 }
179 }
180
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;
185 }
186
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.
191 static int
192 setup_dwfl_report_kernel_p(const char* modname, const char* filename)
193 {
194 if (pending_interrupts || setup_dwfl_done)
195 return -1;
196
197 // elfutils sends us NULL filenames sometimes if it can't find dwarf
198 if (filename == NULL)
199 return 0;
200
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"))
205 {
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;
211 else
212 setup_mod_deps();
213
214 offline_modules_found++;
215 return 1;
216 }
217
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)
222 {
223 if (dwflpp::name_has_wildcard (offline_search_modname))
224 {
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().
228 if (match_p)
229 offline_modules_found++;
230 return match_p;
231 }
232 else
233 { /* non-wildcard mode, reject mismatching module names */
234 if (strcmp(modname, offline_search_modname))
235 return 0;
236 else
237 {
238 // Done, only one name needed and found it.
239 offline_modules_found++;
240 setup_dwfl_done = true;
241 return 1;
242 }
243 }
244 }
245 else
246 { /* find all in set mode, reject mismatching module names */
247 if (offline_search_names.find(modname) == offline_search_names.end())
248 return 0;
249 else
250 {
251 offline_modules_found++;
252 if (offline_search_names.size() == offline_modules_found)
253 setup_dwfl_done = true;
254 return 1;
255 }
256 }
257 }
258
259 static DwflPtr
260 setup_dwfl_kernel (unsigned *modules_found, systemtap_session &s)
261 {
262 Dwfl *dwfl = dwfl_begin (&kernel_callbacks);
263 dwfl_assert ("dwfl_begin", dwfl);
264 dwfl_report_begin (dwfl);
265
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/"
275 + s.kernel_release
276 + "/build"))
277 elfutils_kernel_path = s.kernel_release;
278 else
279 elfutils_kernel_path = s.kernel_build_tree;
280
281 offline_modules_found = 0;
282
283 // First try to report full path modules.
284 set<string>::iterator it = offline_search_names.begin();
285 while (it != offline_search_names.end())
286 {
287 if ((*it)[0] == '/')
288 {
289 const char *cname = (*it).c_str();
290 Dwfl_Module *mod = dwfl_report_offline (dwfl, cname, cname, -1);
291 if (mod)
292 offline_modules_found++;
293 }
294 it++;
295 }
296
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);
304
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". */
308
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.
313
314 dwfl_assert ("dwfl_report_end", dwfl_report_end(dwfl, NULL, NULL));
315 *modules_found = offline_modules_found;
316
317 StapDwfl *stap_dwfl = new StapDwfl(dwfl);
318 kernel_dwfl = DwflPtr(stap_dwfl);
319
320 return kernel_dwfl;
321 }
322
323 DwflPtr
324 setup_dwfl_kernel(const std::string &name,
325 unsigned *found,
326 systemtap_session &s)
327 {
328 const char *modname = name.c_str();
329 set<string> names; // Default to empty
330
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))
334 {
335 names.insert(name);
336 modname = NULL;
337 }
338
339 if (kernel_dwfl != NULL
340 && offline_search_modname == modname
341 && offline_search_names == names)
342 {
343 *found = offline_modules_found;
344 return kernel_dwfl;
345 }
346
347 offline_search_modname = modname;
348 offline_search_names = names;
349
350 return setup_dwfl_kernel(found, s);
351 }
352
353 DwflPtr
354 setup_dwfl_kernel(const std::set<std::string> &names,
355 unsigned *found,
356 systemtap_session &s)
357 {
358 if (kernel_dwfl != NULL
359 && offline_search_modname == NULL
360 && offline_search_names == names)
361 {
362 *found = offline_modules_found;
363 return kernel_dwfl;
364 }
365
366 offline_search_modname = NULL;
367 offline_search_names = names;
368 return setup_dwfl_kernel(found, s);
369 }
370
371 DwflPtr
372 setup_dwfl_user(const std::string &name)
373 {
374 if (user_dwfl != NULL
375 && user_modset.size() == 1
376 && (*user_modset.begin()) == name)
377 return user_dwfl;
378
379 user_modset.clear();
380 user_modset.insert(name);
381
382 Dwfl *dwfl = dwfl_begin (&user_callbacks);
383 dwfl_assert("dwfl_begin", dwfl);
384 dwfl_report_begin (dwfl);
385
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));
390 if (! mod)
391 {
392 dwfl_end(dwfl);
393 dwfl = NULL;
394 }
395
396 StapDwfl *stap_dwfl = new StapDwfl(dwfl);
397 user_dwfl = DwflPtr(stap_dwfl);
398
399 return user_dwfl;
400 }
401
402 DwflPtr
403 setup_dwfl_user(std::vector<std::string>::const_iterator &begin,
404 const std::vector<std::string>::const_iterator &end,
405 bool all_needed)
406 {
407 // See if we have this dwfl already cached
408 set<string> modset(begin, end);
409 if (user_dwfl != NULL && modset == user_modset)
410 return user_dwfl;
411
412 user_modset = modset;
413
414 Dwfl *dwfl = dwfl_begin (&user_callbacks);
415 dwfl_assert("dwfl_begin", dwfl);
416 dwfl_report_begin (dwfl);
417
418 // XXX: should support buildid-based naming
419 while (begin != end && dwfl != NULL)
420 {
421 const char *cname = (*begin).c_str();
422 Dwfl_Module *mod = dwfl_report_offline (dwfl, cname, cname, -1);
423 if (! mod && all_needed)
424 {
425 dwfl_end(dwfl);
426 dwfl = NULL;
427 }
428 begin++;
429 }
430 if (dwfl)
431 dwfl_assert ("dwfl_report_end", dwfl_report_end(dwfl, NULL, NULL));
432
433 StapDwfl *stap_dwfl = new StapDwfl(dwfl);
434 user_dwfl = DwflPtr(stap_dwfl);
435
436 return user_dwfl;
437 }
438
439 bool
440 is_user_module(const std::string &m)
441 {
442 return m[0] == '/' && m.rfind(".ko", m.length() - 1) != m.length() - 3;
443 }
This page took 0.053482 seconds and 5 git commands to generate.