]> sourceware.org Git - systemtap.git/blob - setupdwfl.cxx
PR12773 cont'd: Added Kernel Debuginfo Support
[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 #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 }
37
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);
42
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);
49
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.
52 // This is a kludge.
53 static systemtap_session* current_session_for_find_debuginfo;
54
55 // The path to the abrt-action-install-debuginfo-to-abrt-cache
56 // program.
57 #define ABRT_PATH "/usr/bin/abrt-action-install-debuginfo-to-abrt-cache"
58
59 static const Dwfl_Callbacks kernel_callbacks =
60 {
61 dwfl_linux_kernel_find_elf,
62 internal_find_debuginfo,
63 dwfl_offline_section_address,
64 (char **) & debuginfo_path
65 };
66
67 static const Dwfl_Callbacks user_callbacks =
68 {
69 NULL,
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
74 };
75
76 using namespace std;
77
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;
82
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;
90
91 // Whether or not we are done reporting kernel modules in
92 // set_dwfl_report_kernel_p().
93 static bool setup_dwfl_done;
94
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;
98
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;
104
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
107 // setup_mod_deps().
108 static string elfutils_kernel_path;
109
110 static bool is_comma_dash(const char c) { return (c == ',' || c == '-'); }
111
112 // The module name is the basename (without the extension) of the
113 // module path, with ',' and '-' replaced by '_'.
114 static string
115 modname_from_path(const string &path)
116 {
117 size_t dot = path.rfind('.');
118 size_t slash = path.rfind('/');
119 if (dot == string::npos || slash == string::npos || dot < slash)
120 return "";
121 string name = path.substr(slash + 1, dot - slash - 1);
122 replace_if(name.begin(), name.end(), is_comma_dash, '_');
123 return name;
124 }
125
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)
129 // dependencies.
130 static void
131 setup_mod_deps()
132 {
133 string modulesdep;
134 ifstream in;
135 string l;
136
137 if (elfutils_kernel_path[0] == '/')
138 {
139 modulesdep = elfutils_kernel_path;
140 modulesdep += "/modules.dep";
141 }
142 else
143 {
144 modulesdep = "/lib/modules/";
145 modulesdep += elfutils_kernel_path;
146 modulesdep += "/modules.dep";
147 }
148 in.open(modulesdep.c_str());
149 if (in.fail ())
150 return;
151
152 while (getline (in, l))
153 {
154 size_t off = l.find (':');
155 if (off != string::npos)
156 {
157 string modpath, modname;
158 modpath = l.substr (0, off);
159 modname = modname_from_path (modpath);
160 if (modname == "")
161 continue;
162
163 bool dep_needed;
164 if (offline_search_modname != NULL)
165 {
166 if (dwflpp::name_has_wildcard (offline_search_modname))
167 {
168 dep_needed = !fnmatch (offline_search_modname,
169 modname.c_str (), 0);
170 if (dep_needed)
171 offline_search_names.insert (modname);
172 }
173 else
174 {
175 dep_needed = ! strcmp(modname.c_str (),
176 offline_search_modname);
177 if (dep_needed)
178 offline_search_names.insert (modname);
179 }
180 }
181 else
182 dep_needed = (offline_search_names.find (modname)
183 != offline_search_names.end ());
184
185 if (! dep_needed)
186 continue;
187
188 string depstring = l.substr (off + 1);
189 if (depstring.size () > 0)
190 {
191 stringstream ss (depstring);
192 string deppath;
193 while (ss >> deppath)
194 offline_search_names.insert (modname_from_path(deppath));
195
196 }
197 }
198 }
199
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;
204 }
205
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.
210 static int
211 setup_dwfl_report_kernel_p(const char* modname, const char* filename)
212 {
213 if (pending_interrupts || setup_dwfl_done)
214 return -1;
215
216 // elfutils sends us NULL filenames sometimes if it can't find dwarf
217 if (filename == NULL)
218 return 0;
219
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"))
224 {
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;
230 else
231 setup_mod_deps();
232
233 offline_modules_found++;
234 return 1;
235 }
236
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)
241 {
242 if (dwflpp::name_has_wildcard (offline_search_modname))
243 {
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().
247 if (match_p)
248 offline_modules_found++;
249 return match_p;
250 }
251 else
252 { /* non-wildcard mode, reject mismatching module names */
253 if (strcmp(modname, offline_search_modname))
254 return 0;
255 else
256 {
257 // Done, only one name needed and found it.
258 offline_modules_found++;
259 setup_dwfl_done = true;
260 return 1;
261 }
262 }
263 }
264 else
265 { /* find all in set mode, reject mismatching module names */
266 if (offline_search_names.find(modname) == offline_search_names.end())
267 return 0;
268 else
269 {
270 offline_modules_found++;
271 if (offline_search_names.size() == offline_modules_found)
272 setup_dwfl_done = true;
273 return 1;
274 }
275 }
276 }
277
278 static DwflPtr
279 setup_dwfl_kernel (unsigned *modules_found, systemtap_session &s)
280 {
281 Dwfl *dwfl = dwfl_begin (&kernel_callbacks);
282 dwfl_assert ("dwfl_begin", dwfl);
283 dwfl_report_begin (dwfl);
284
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/"
294 + s.kernel_release
295 + "/build"))
296 elfutils_kernel_path = s.kernel_release;
297 else
298 elfutils_kernel_path = s.kernel_build_tree;
299
300 offline_modules_found = 0;
301
302 // First try to report full path modules.
303 set<string>::iterator it = offline_search_names.begin();
304 int kernel = 0;
305 while (it != offline_search_names.end())
306 {
307 if ((*it)[0] == '/')
308 {
309 const char *cname = (*it).c_str();
310 Dwfl_Module *mod = dwfl_report_offline (dwfl, cname, cname, -1);
311 if (mod)
312 offline_modules_found++;
313 }
314 else if ((*it) == "kernel")
315 kernel = 1;
316 it++;
317 }
318
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);
326
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". */
330
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.
335
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)
339 {
340 rc = download_kernel_debuginfo(s);
341 if(rc >= 0)
342 return setup_dwfl_kernel (modules_found, s);
343 }
344
345 dwfl_assert ("dwfl_report_end", dwfl_report_end(dwfl, NULL, NULL));
346 *modules_found = offline_modules_found;
347
348 StapDwfl *stap_dwfl = new StapDwfl(dwfl);
349 kernel_dwfl = DwflPtr(stap_dwfl);
350
351 return kernel_dwfl;
352 }
353
354 DwflPtr
355 setup_dwfl_kernel(const std::string &name,
356 unsigned *found,
357 systemtap_session &s)
358 {
359 current_session_for_find_debuginfo = &s;
360 const char *modname = name.c_str();
361 set<string> names; // Default to empty
362
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))
366 {
367 names.insert(name);
368 modname = NULL;
369 }
370
371 if (kernel_dwfl != NULL
372 && offline_search_modname == modname
373 && offline_search_names == names)
374 {
375 *found = offline_modules_found;
376 return kernel_dwfl;
377 }
378
379 offline_search_modname = modname;
380 offline_search_names = names;
381
382 return setup_dwfl_kernel(found, s);
383 }
384
385 DwflPtr
386 setup_dwfl_kernel(const std::set<std::string> &names,
387 unsigned *found,
388 systemtap_session &s)
389 {
390 if (kernel_dwfl != NULL
391 && offline_search_modname == NULL
392 && offline_search_names == names)
393 {
394 *found = offline_modules_found;
395 return kernel_dwfl;
396 }
397
398 offline_search_modname = NULL;
399 offline_search_names = names;
400 return setup_dwfl_kernel(found, s);
401 }
402
403 DwflPtr
404 setup_dwfl_user(const std::string &name)
405 {
406 if (user_dwfl != NULL
407 && user_modset.size() == 1
408 && (*user_modset.begin()) == name)
409 return user_dwfl;
410
411 user_modset.clear();
412 user_modset.insert(name);
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 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));
422 if (! mod)
423 {
424 dwfl_end(dwfl);
425 dwfl = NULL;
426 }
427
428 StapDwfl *stap_dwfl = new StapDwfl(dwfl);
429 user_dwfl = DwflPtr(stap_dwfl);
430
431 return user_dwfl;
432 }
433
434 DwflPtr
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)
438 {
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)
443 return user_dwfl;
444
445 user_modset = modset;
446
447 Dwfl *dwfl = dwfl_begin (&user_callbacks);
448 dwfl_assert("dwfl_begin", dwfl);
449 dwfl_report_begin (dwfl);
450
451 // XXX: should support buildid-based naming
452 while (begin != end && dwfl != NULL)
453 {
454 const char *cname = (*begin).c_str();
455 Dwfl_Module *mod = dwfl_report_offline (dwfl, cname, cname, -1);
456 if (! mod && all_needed)
457 {
458 dwfl_end(dwfl);
459 dwfl = NULL;
460 }
461 begin++;
462 }
463 if (dwfl)
464 dwfl_assert ("dwfl_report_end", dwfl_report_end(dwfl, NULL, NULL));
465
466 StapDwfl *stap_dwfl = new StapDwfl(dwfl);
467 user_dwfl = DwflPtr(stap_dwfl);
468
469 return user_dwfl;
470 }
471
472 bool
473 is_user_module(const std::string &m)
474 {
475 return m[0] == '/' && m.rfind(".ko", m.length() - 1) != m.length() - 3;
476 }
477
478 int
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)
487 {
488 int bits_length;
489 string hex;
490
491 /* To Keep track of whether the abrt successfully installed the debuginfo */
492 static int install_dbinfo_failed = 0;
493
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;
497
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;
501
502 /* Check that we haven't already run this */
503 if (install_dbinfo_failed < 0)
504 {
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;
508 }
509
510 /* Extract the build ID */
511 const unsigned char *bits;
512 GElf_Addr vaddr;
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);
516
517 /* Convert the binary bits to a hex string */
518 hex = hex_dump(bits, bits_length);
519
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;
523 if (bits_length > 0)
524 {
525 int fd = dwfl_build_id_find_debuginfo(mod,
526 NULL, NULL, 0,
527 NULL, NULL, 0,
528 debuginfo_file_name);
529 if (fd >= 0)
530 return fd;
531 }
532
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;
537
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);
545
546 if(execute_abrt_action_install_debuginfo_to_abrt_cache (hex) < 0)
547 {
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;
552 }
553
554 _sc_clk_tck = sysconf (_SC_CLK_TCK);
555 times (& tms_after);
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;
565
566 call_dwfl_standard_find_debuginfo:
567
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);
572
573 }
574
575 int
576 execute_abrt_action_install_debuginfo_to_abrt_cache (string hex)
577 {
578 /* Check that abrt exists */
579 if(access (ABRT_PATH, X_OK) < 0)
580 return -1;
581
582 int timeout = current_session_for_find_debuginfo->download_dbinfo;;
583 vector<string> cmd;
584 cmd.push_back ("/bin/sh");
585 cmd.push_back ("-c");
586
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)
590 {
591 cmd.push_back ("echo " + hex + " | " + ABRT_PATH + " --ids=-");
592 timeout = INT_MAX;
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."));
595 }
596 else
597 cmd.push_back ("echo " + hex + " | " + ABRT_PATH + " -y --ids=-");
598
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."));
603
604 int pid;
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);
609 else
610 {
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)
615 return -1;
616 if(posix_spawn_file_actions_addopen(&fa, 1, "/dev/null", O_WRONLY, 0) != 0)
617 {
618 posix_spawn_file_actions_destroy(&fa);
619 return -1;
620 }
621 pid = stap_spawn(current_session_for_find_debuginfo->verbose, cmd, &fa);
622 posix_spawn_file_actions_destroy(&fa);
623 }
624
625 /* Check to see if either the program successfully completed, or if it timed out. */
626 int rstatus = 0;
627 int timer = 0;
628 int rc = 0;
629 while(timer < timeout)
630 {
631 sleep(1);
632 rc = waitpid(pid, &rstatus, WNOHANG);
633 if(rc < 0)
634 return -1;
635 if (rc > 0 && WIFEXITED(rstatus))
636 break;
637 if(pending_interrupts)
638 return -1;
639 timer++;
640 }
641 if(timer == timeout)
642 {
643 /* Timed out! */
644 kill(-pid, SIGINT);
645 if (!current_session_for_find_debuginfo->suppress_warnings)
646 current_session_for_find_debuginfo->print_warning(_("Aborted downloading debuginfo: timed out."));
647 return -1;
648 }
649
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;
655 #endif
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;
658
659 return 0;
660 }
661
662 /* Look for a build ID note in /sys/kernel/notes */
663 string
664 get_kernel_build_id_from_notes ()
665 {
666 const char *notesfile = "/sys/kernel/notes";
667 int fd = open64 (notesfile, O_RDONLY);
668 if (fd < 0)
669 return "";
670
671 assert (sizeof (Elf32_Nhdr) == sizeof (GElf_Nhdr));
672 assert (sizeof (Elf64_Nhdr) == sizeof (GElf_Nhdr));
673
674 union
675 {
676 GElf_Nhdr nhdr;
677 unsigned char data[8192];
678 } buf;
679
680 ssize_t n = read (fd, buf.data, sizeof buf);
681 close (fd);
682
683 if (n <= 0)
684 return "";
685
686 unsigned char *p = buf.data;
687 while (p < &buf.data[n])
688 {
689 /* No translation required since we are reading the native kernel. */
690 GElf_Nhdr *nhdr = (GElf_Nhdr *) p;
691 p += sizeof *nhdr;
692 unsigned char *name = p;
693 p += (nhdr->n_namesz + 3) & -4U;
694 unsigned char *bits = p;
695 p += (nhdr->n_descsz + 3) & -4U;
696
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"))
701 {
702 // Found it.
703 return hex_dump(bits, nhdr->n_descsz);
704 }
705 }
706 return "";
707 }
708
709 /* Find the kernel build ID and attempt to download the matching debuginfo */
710 int download_kernel_debuginfo (systemtap_session &s)
711 {
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.
715 bool found = false;
716 string hex;
717
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)
721 return -1;
722
723 // Get the kernel information
724 struct utsname kernelinfo;
725 if( uname(&kernelinfo) < 0)
726 return -1;
727
728 // Try to find BuildID from vmlinux.id
729 string kernel_buildID_path = "/lib/modules/"
730 + (string)kernelinfo.release
731 + "/build/vmlinux.id";
732 if(s.verbose > 1)
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())
737 {
738 getline(buildIDfile, hex);
739 if(buildIDfile.good())
740 {
741 found = true;
742 }
743 buildIDfile.close();
744 }
745
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)
749 {
750 if(s.verbose > 1)
751 clog << _("Attempting to extract kernel debuginfo build ID from /sys/kernel/notes") << endl;
752
753 hex = get_kernel_build_id_from_notes();
754 if (!hex.empty())
755 found = true;
756 }
757
758 // If we still didn't find it, return -1
759 if(found == false)
760 return -1;
761
762 // We found the BuildID hex, so attempt to download the debuginfo
763 if(s.verbose > 1)
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;
767 if (rc < 0)
768 return -1;
769
770 // Success!
771 return 0;
772 }
This page took 0.071695 seconds and 6 git commands to generate.