cache debug directory scans

This commit is contained in:
Robin Nicholson 2018-03-16 23:39:49 +01:00
parent aeea06b089
commit 4efb2b3b56

View File

@ -5,6 +5,7 @@
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <unordered_map>
#include "elfspy/Report.h"
#include "elfspy/Fail.h"
@ -57,6 +58,38 @@ inline void Address::round_up()
value_ *= page_size;
}
std::string get_lib_core_name(const char* file_name)
{
// strip trailing [-major[.minor]].so[.n[.n]]
const char* root_end = nullptr;
const char* pos;
for (pos = file_name; *pos; ++pos) {
if (*pos == '-') {
root_end = pos;
} else if (strncmp(pos, ".so", 3) == 0) {
break;
}
}
if (!*pos) {
return { }; // no .so extension - not a shared lib
}
if (root_end) {
for (const char* c = root_end + 1; c != pos; ++c) {
if (!isdigit(*c) && *c != '.') {
// this is not major.minor format so don't exclude it
root_end = pos;
break;
}
}
} else {
root_end = pos;
}
return { file_name, root_end };
}
using Listing = std::unordered_map<std::string, std::string>;
std::unordered_map<std::string, Listing> debug_files;
// see if a library has a corresponding debug file where the symbols are kept
std::string get_debug_file_name(const char* file_name)
{
@ -65,35 +98,33 @@ std::string get_debug_file_name(const char* file_name)
if (*seek == '/') slash = seek;
}
if (!slash) return { };
std::string name;
name = "/usr/lib/debug";
name.append(file_name, slash - file_name);
DIR* dir = opendir(name.c_str());
if (!dir) return { };
const char* period = nullptr;
for (const char* seek = slash; *seek; ++seek) {
if (*seek == '.') {
period = seek;
break;
}
}
if (!period) return { };
const char* base_name = slash + 1;
size_t name_root_len = period - base_name;
struct dirent* entry;
while ((entry = readdir(dir))) {
if (strncmp(entry->d_name, base_name, name_root_len) == 0) {
const char* after_root = entry->d_name + name_root_len;
// files in debug dir seem to be named libxxx-major.minor.so
if (*after_root == '-' || *after_root == '.') {
closedir(dir);
name += '/';
name += entry->d_name;
return name;
// see if directory has already been scanned
std::string dir_name(file_name, slash);
auto insert = debug_files.emplace(std::move(dir_name), std::move(Listing{}));
auto& listing = *insert.first;
auto& entries = listing.second;
if (insert.second) {
const std::string& dir_name = listing.first;
std::string name;
name.reserve(1024); // reasonable unscientific value
name = "/usr/lib/debug";
name.append(dir_name);
DIR* dir = opendir(name.c_str());
if (!dir) return { };
name.push_back('/');
struct dirent* entry;
while ((entry = readdir(dir))) {
std::string root_name = get_lib_core_name(entry->d_name);
if (!root_name.empty()) {
entries.emplace(std::move(root_name), std::move(name + entry->d_name));
}
}
closedir(dir);
}
auto seek = entries.find(get_lib_core_name(slash + 1));
if (seek != entries.end()) {
return seek->second;
}
closedir(dir);
return { };
}