setupdwfl.cxx - systemtap
Global variables defined
Functions defined
Source code
#include "config.h"
#include "setupdwfl.h"
#include "dwarf_wrappers.h"
#include "dwflpp.h"
#include "session.h"
#include "util.h"
#include <algorithm>
#include <iostream>
#include <fstream>
#include <sstream>
#include <set>
#include <string>
extern "C" {
#include <fnmatch.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>
#include <sys/times.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/utsname.h>
#include <unistd.h>
}
XXXstatic const char *debuginfo_path_arr = "+:.debug:/usr/lib/debug:/var/cache/abrt-di/usr/lib/debug:build";
static const char *debuginfo_env_arr = getenv("SYSTEMTAP_DEBUGINFO_PATH");
static char *debuginfo_path = (char *)(debuginfo_env_arr ?: debuginfo_path_arr);
XXXstatic const char *debuginfo_usr_path_arr = "+:.debug:/usr/lib/debug:/var/cache/abrt-di/usr/lib/debug";
static char *debuginfo_usr_path = (char *)(debuginfo_env_arr
?: debuginfo_usr_path_arr);
static systemtap_session* current_session_for_find_debuginfo;
static const Dwfl_Callbacks kernel_callbacks =
{
dwfl_linux_kernel_find_elf,
internal_find_debuginfo,
dwfl_offline_section_address,
(char **) & debuginfo_path
};
static const Dwfl_Callbacks user_callbacks =
{
NULL,
internal_find_debuginfo,
NULL, (char **) & debuginfo_usr_path
};
using namespace std;
static const char *offline_search_modname;
static set<string> offline_search_names;
static unsigned offline_modules_found;
static bool setup_dwfl_done;
static const bool setup_all_deps = true;
static string elfutils_kernel_path;
static bool is_comma_dash(const char c) { return (c == ',' || c == '-'); }
static const string abrt_path =
(access ("/usr/bin/abrt-action-install-debuginfo-to-abrt-cache", X_OK) == 0
? "/usr/bin/abrt-action-install-debuginfo-to-abrt-cache"
: (access ("/usr/libexec/abrt-action-install-debuginfo-to-abrt-cache", X_OK) == 0
? "/usr/libexec/abrt-action-install-debuginfo-to-abrt-cache"
: ""));
string
modname_from_path(const string &path)
{
size_t slash = path.rfind('/');
if (slash == string::npos)
return "";
string name = path.substr(slash + 1);
size_t extension = name.rfind(".ko");
if (extension == string::npos)
extension = name.rfind('.');
if (extension == string::npos)
return "";
name.erase(extension);
replace_if(name.begin(), name.end(), is_comma_dash, '_');
return name;
}
static bool offline_search_names_find(const string &modpath) {
if (offline_search_names.find(modpath) != offline_search_names.end()) return 1;
string modname = modname_from_path (modpath);
return offline_search_names.find(modname) != offline_search_names.end();
}
static void
setup_mod_deps()
{
string modulesdep;
string kernel_path;
ifstream in;
string l;
if (elfutils_kernel_path[0] == '/')
{
kernel_path = elfutils_kernel_path;
}
else
{
string sysroot = "";
if (current_session_for_find_debuginfo)
sysroot = current_session_for_find_debuginfo->sysroot;
kernel_path = sysroot + "/lib/modules/" + elfutils_kernel_path;
}
modulesdep = kernel_path + "/modules.dep";
in.open(modulesdep.c_str());
if (in.fail ())
return;
while (getline (in, l))
{
size_t off = l.find (':');
if (off != string::npos)
{
string modpath, modname;
modpath = l.substr (0, off);
modname = modname_from_path (modpath);
if (modname == "")
continue;
if (modpath[0] != '/') modpath = kernel_path + "/" + modpath;
bool dep_needed = 0;
if (offline_search_modname != NULL)
{
if (dwflpp::name_has_wildcard (offline_search_modname))
{
dep_needed = !fnmatch (offline_search_modname,
modname.c_str (), 0);
if (dep_needed)
offline_search_names.insert (modpath);
}
else
{
dep_needed = ! strcmp(modname.c_str (),
offline_search_modname);
if (dep_needed)
offline_search_names.insert (modpath);
}
}
else if (offline_search_names.find(modpath) != offline_search_names.end())
dep_needed = 1;
else
{
set<string>::iterator it = offline_search_names.begin();
while (it != offline_search_names.end())
{
string modname;
modname = modname_from_path(modpath);
if (*it == modname)
{
dep_needed = 1;
offline_search_names.erase(it);
offline_search_names.insert(modpath);
break;
}
it++;
}
}
if (! dep_needed)
continue;
string depstring = l.substr (off + 1);
if (depstring.size () > 0)
{
stringstream ss (depstring);
string deppath;
while (ss >> deppath)
offline_search_names.insert (deppath);
}
}
}
offline_search_names.insert ("kernel");
offline_search_modname = NULL;
}
static int
setup_dwfl_report_kernel_p(const char* modname, const char* filename)
{
assert_no_interrupts();
if (setup_dwfl_done)
return -1;
assert (current_session_for_find_debuginfo);
if (current_session_for_find_debuginfo->verbose > 4)
clog << _F("checking pattern '%s' vs. module '%s' file '%s'\n",
offline_search_modname ?: "",
modname ?: "",
filename ?: "");
if (filename == NULL)
return 0;
if (setup_all_deps && ! strcmp (modname, "kernel"))
{
if ((offline_search_modname != NULL
&& ! strcmp (offline_search_modname, "kernel"))
|| (offline_search_names.size() == 1
&& *offline_search_names.begin() == "kernel"))
setup_dwfl_done = true;
else
setup_mod_deps();
offline_modules_found++;
return 1;
}
if (offline_search_modname != NULL)
{
if (dwflpp::name_has_wildcard (offline_search_modname))
{
XXX int match_p = !fnmatch(offline_search_modname, modname, 0);
if (match_p)
offline_modules_found++;
return match_p;
}
else
{ if (strcmp(modname, offline_search_modname))
return 0;
else
{
offline_modules_found++;
setup_dwfl_done = true;
return 1;
}
}
}
else
{ if (!offline_search_names_find(filename))
return 0;
else
{
offline_modules_found++;
if (offline_search_names.size() == offline_modules_found)
setup_dwfl_done = true;
return 1;
}
}
}
static char * path_insert_sysroot(string sysroot, string path)
{
char * path_new;
size_t pos = 1;
if (path[0] == '/')
path.replace(0, 1, sysroot);
while (true) {
pos = path.find(":/", pos);
if (pos == string::npos)
break;
path.replace(pos, 2, ":" + sysroot);
++pos;
}
path_new = new char[path.size()+1];
strcpy (path_new, path.c_str());
return path_new;
}
void debuginfo_path_insert_sysroot(string sysroot)
{
debuginfo_path = path_insert_sysroot(sysroot, debuginfo_path);
debuginfo_usr_path = path_insert_sysroot(sysroot, debuginfo_usr_path);
}
static Dwfl *
setup_dwfl_kernel (unsigned *modules_found, systemtap_session &s)
{
Dwfl *dwfl = dwfl_begin (&kernel_callbacks);
DWFL_ASSERT ("dwfl_begin", dwfl);
dwfl_report_begin (dwfl);
if (s.kernel_build_tree == string(s.sysroot + "/lib/modules/"
+ s.kernel_release
+ "/build"))
elfutils_kernel_path = s.kernel_release;
else
elfutils_kernel_path = s.kernel_build_tree;
offline_modules_found = 0;
set<string>::iterator it = offline_search_names.begin();
int kernel = 0;
while (it != offline_search_names.end())
{
if ((*it)[0] == '/')
{
const char *cname = (*it).c_str();
Dwfl_Module *mod = dwfl_report_offline (dwfl, cname, cname, -1);
if (mod)
offline_modules_found++;
}
else if ((*it) == "kernel")
kernel = 1;
it++;
}
setup_dwfl_done = false;
int rc = dwfl_linux_kernel_report_offline (dwfl,
elfutils_kernel_path.c_str(),
&setup_dwfl_report_kernel_p);
(void) rc;
if(kernel)
{
string hex = get_kernel_build_id(s);
if (offline_modules_found == 0 && s.download_dbinfo != 0 && !hex.empty())
{
rc = download_kernel_debuginfo(s, hex);
if(rc >= 0)
{
dwfl_end (dwfl);
return setup_dwfl_kernel (modules_found, s);
}
}
}
DWFL_ASSERT ("dwfl_report_end", dwfl_report_end(dwfl, NULL, NULL));
*modules_found = offline_modules_found;
return dwfl;
}
Dwfl*
setup_dwfl_kernel(const std::string &name,
unsigned *found,
systemtap_session &s)
{
current_session_for_find_debuginfo = &s;
const char *modname = name.c_str();
set<string> names;
if (name[0] == '/' || ! dwflpp::name_has_wildcard (modname))
{
names.insert(name);
modname = NULL;
}
offline_search_modname = modname;
offline_search_names = names;
return setup_dwfl_kernel(found, s);
}
Dwfl*
setup_dwfl_kernel(const std::set<std::string> &names,
unsigned *found,
systemtap_session &s)
{
current_session_for_find_debuginfo = &s;
offline_search_modname = NULL;
offline_search_names = names;
return setup_dwfl_kernel(found, s);
}
Dwfl*
setup_dwfl_user(const std::string &name)
{
Dwfl *dwfl = dwfl_begin (&user_callbacks);
DWFL_ASSERT("dwfl_begin", dwfl);
dwfl_report_begin (dwfl);
XXX const char *cname = name.c_str();
Dwfl_Module *mod = dwfl_report_offline (dwfl, cname, cname, -1);
DWFL_ASSERT ("dwfl_report_end", dwfl_report_end(dwfl, NULL, NULL));
if (! mod)
{
dwfl_end(dwfl);
dwfl = NULL;
}
return dwfl;
}
Dwfl*
setup_dwfl_user(std::vector<std::string>::const_iterator &begin,
const std::vector<std::string>::const_iterator &end,
bool all_needed, systemtap_session &s)
{
current_session_for_find_debuginfo = &s;
set<string> modset(begin, end);
Dwfl *dwfl = dwfl_begin (&user_callbacks);
DWFL_ASSERT("dwfl_begin", dwfl);
dwfl_report_begin (dwfl);
Dwfl_Module *mod = NULL;
XXX while (begin != end && dwfl != NULL)
{
const char *cname = (*begin).c_str();
mod = dwfl_report_offline (dwfl, cname, cname, -1);
if (! mod && all_needed)
{
dwfl_end(dwfl);
dwfl = NULL;
}
begin++;
}
if (mod)
{
const unsigned char *bits;
GElf_Addr vaddr;
if(s.verbose > 2)
clog << _("Extracting build ID.") << endl;
int bits_length = dwfl_module_build_id(mod, &bits, &vaddr);
string hex = hex_dump(bits, bits_length);
s.build_ids.push_back(hex);
}
if (dwfl)
DWFL_ASSERT ("dwfl_report_end", dwfl_report_end(dwfl, NULL, NULL));
return dwfl;
}
bool
is_user_module(const std::string &m)
{
return m[0] == '/' && m.rfind(".ko", m.length() - 1) != m.length() - 3;
}
int
internal_find_debuginfo (Dwfl_Module *mod,
void **userdata __attribute__ ((unused)),
const char *modname __attribute__ ((unused)),
GElf_Addr base __attribute__ ((unused)),
const char *file_name,
const char *debuglink_file,
GElf_Word debuglink_crc,
char **debuginfo_file_name)
{
int bits_length;
string hex;
static int install_dbinfo_failed = 0;
if(current_session_for_find_debuginfo == NULL)
goto call_dwfl_standard_find_debuginfo;
if(!current_session_for_find_debuginfo->download_dbinfo || abrt_path.empty())
goto call_dwfl_standard_find_debuginfo;
if (install_dbinfo_failed < 0)
{
if(current_session_for_find_debuginfo->verbose > 1)
current_session_for_find_debuginfo->print_warning(_F("We already tried running '%s'", abrt_path.c_str()));
goto call_dwfl_standard_find_debuginfo;
}
const unsigned char *bits;
GElf_Addr vaddr;
if(current_session_for_find_debuginfo->verbose > 2)
clog << _("Extracting build ID.") << endl;
bits_length = dwfl_module_build_id(mod, &bits, &vaddr);
hex = hex_dump(bits, bits_length);
if(current_session_for_find_debuginfo->verbose > 2)
clog << _F("Searching for debuginfo with build ID: '%s'.", hex.c_str()) << endl;
if (bits_length > 0)
{
int fd = dwfl_build_id_find_debuginfo(mod,
NULL, NULL, 0,
NULL, NULL, 0,
debuginfo_file_name);
if (fd >= 0)
return fd;
}
if(current_session_for_find_debuginfo->verbose > 1)
clog << _F("Downloading and installing debuginfo with build ID: '%s' using %s.",
hex.c_str(), abrt_path.c_str()) << endl;
struct tms tms_before;
times (& tms_before);
struct timeval tv_before;
struct tms tms_after;
unsigned _sc_clk_tck;
struct timeval tv_after;
gettimeofday (&tv_before, NULL);
if(execute_abrt_action_install_debuginfo_to_abrt_cache (hex) < 0)
{
install_dbinfo_failed = -1;
current_session_for_find_debuginfo->print_warning(_F("%s failed.", abrt_path.c_str()));
goto call_dwfl_standard_find_debuginfo;
}
_sc_clk_tck = sysconf (_SC_CLK_TCK);
times (& tms_after);
gettimeofday (&tv_after, NULL);
if(current_session_for_find_debuginfo->verbose > 1)
clog << _("Download completed in ")
<< ((tms_after.tms_cutime + tms_after.tms_utime
- tms_before.tms_cutime - tms_before.tms_utime) * 1000 / (_sc_clk_tck)) << "usr/"
<< ((tms_after.tms_cstime + tms_after.tms_stime
- tms_before.tms_cstime - tms_before.tms_stime) * 1000 / (_sc_clk_tck)) << "sys/"
<< ((tv_after.tv_sec - tv_before.tv_sec) * 1000 +
((long)tv_after.tv_usec - (long)tv_before.tv_usec) / 1000) << "real ms"<< endl;
call_dwfl_standard_find_debuginfo:
return dwfl_standard_find_debuginfo(mod, userdata, modname, base,
file_name, debuglink_file,
debuglink_crc, debuginfo_file_name);
}
int
execute_abrt_action_install_debuginfo_to_abrt_cache (string hex)
{
if (abrt_path.empty())
return -1;
int timeout = current_session_for_find_debuginfo->download_dbinfo;;
vector<string> cmd;
cmd.push_back ("/bin/sh");
cmd.push_back ("-c");
if(current_session_for_find_debuginfo->download_dbinfo == -1)
{
cmd.push_back ("echo " + hex + " | " + abrt_path + " --ids=-");
timeout = INT_MAX;
current_session_for_find_debuginfo->print_warning(_("Due to bug in abrt, it may continue downloading anyway without asking for confirmation."));
}
else
cmd.push_back ("echo " + hex + " | " + abrt_path + " -y --ids=-");
if(timeout != INT_MAX)
current_session_for_find_debuginfo->print_warning(_("Due to a bug in abrt, it may continue downloading after stopping stap if download times out."));
int pid;
if(current_session_for_find_debuginfo->verbose > 1 || current_session_for_find_debuginfo->download_dbinfo == -1)
pid = stap_spawn(current_session_for_find_debuginfo->verbose, cmd, NULL);
else
{
posix_spawn_file_actions_t fa;
if (posix_spawn_file_actions_init(&fa) != 0)
return -1;
if(posix_spawn_file_actions_addopen(&fa, 1, "/dev/null", O_WRONLY, 0) != 0)
{
posix_spawn_file_actions_destroy(&fa);
return -1;
}
pid = stap_spawn(current_session_for_find_debuginfo->verbose, cmd, &fa);
posix_spawn_file_actions_destroy(&fa);
}
int rstatus = 0;
int timer = 0;
int rc = 0;
while(timer < timeout)
{
sleep(1);
rc = waitpid(pid, &rstatus, WNOHANG);
if(rc < 0)
return -1;
if (rc > 0 && WIFEXITED(rstatus))
break;
assert_no_interrupts();
timer++;
}
if(timer == timeout)
{
kill(-pid, SIGINT);
current_session_for_find_debuginfo->print_warning(_("Aborted downloading debuginfo: timed out."));
return -1;
}
#if 0 #endif
if(current_session_for_find_debuginfo->verbose > 1 || current_session_for_find_debuginfo->download_dbinfo == -1)
clog << _("ABRT finished attempting to download debuginfo.") << endl;
return 0;
}
string
get_kernel_build_id(systemtap_session &s)
{
bool found = false;
string hex;
string kernel_buildID_path = s.kernel_build_tree + "/vmlinux.id";
if(s.verbose > 1)
clog << _F("Attempting to extract kernel debuginfo build ID from %s", kernel_buildID_path.c_str()) << endl;
ifstream buildIDfile;
buildIDfile.open(kernel_buildID_path.c_str());
if(buildIDfile.is_open())
{
getline(buildIDfile, hex);
if(buildIDfile.good())
{
found = true;
}
buildIDfile.close();
}
if(found == false && s.native_build)
{
if(s.verbose > 1)
clog << _("Attempting to extract kernel debuginfo build ID from /sys/kernel/notes") << endl;
const char *notesfile = "/sys/kernel/notes";
int fd = open64 (notesfile, O_RDONLY);
if (fd < 0)
return "";
assert (sizeof (Elf32_Nhdr) == sizeof (GElf_Nhdr));
assert (sizeof (Elf64_Nhdr) == sizeof (GElf_Nhdr));
union
{
GElf_Nhdr nhdr;
unsigned char data[8192];
} buf;
ssize_t n = read (fd, buf.data, sizeof buf);
close (fd);
if (n <= 0)
return "";
unsigned char *p = buf.data;
while (p < &buf.data[n])
{
GElf_Nhdr *nhdr = (GElf_Nhdr *) p;
p += sizeof *nhdr;
unsigned char *name = p;
p += (nhdr->n_namesz + 3) & -4U;
unsigned char *bits = p;
p += (nhdr->n_descsz + 3) & -4U;
if (p <= &buf.data[n]
&& nhdr->n_type == NT_GNU_BUILD_ID
&& nhdr->n_namesz == sizeof "GNU"
&& !memcmp (name, "GNU", sizeof "GNU"))
{
hex = hex_dump(bits, nhdr->n_descsz);
found = true;
}
}
}
if(found)
{
return hex;
}
else
return "";
}
int download_kernel_debuginfo (systemtap_session &s, string hex)
{
static int already_tried_downloading_kernel_debuginfo = 0;
if(already_tried_downloading_kernel_debuginfo)
return -1;
if(s.verbose > 1)
clog << _F("Success! Extracted kernel debuginfo build ID: %s", hex.c_str()) << endl;
int rc = execute_abrt_action_install_debuginfo_to_abrt_cache(hex);
already_tried_downloading_kernel_debuginfo = 1;
if (rc < 0)
return -1;
return 0;
}