diff --git a/Makefile b/Makefile index 1d8b948..ffb60b2 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ ROOT = . -DIRS = runtime +DIRS = runtime tools include $(ROOT)/common.mk diff --git a/common.mk b/common.mk index fd9e23f..78a52c5 100644 --- a/common.mk +++ b/common.mk @@ -4,7 +4,7 @@ CXX = clang++ CFLAGS ?= CXXFLAGS ?= $(CFLAGS) CXXLIB = $(CXX) -shared -fPIC -LINKFLAGS ?= +LINKFLAGS ?= -L$(ROOT)/deps/libelfin/dwarf -L$(ROOT)/deps/libelfin/elf # Don't build into subdirectories by default DIRS ?= @@ -107,8 +107,15 @@ $(RECURSIVE_TARGETS):: $(MAKE) -C $$dir $@ DEBUG=$(DEBUG); \ done +$(ROOT)/deps/libelfin/dwarf $(ROOT)/deps/libelfin/elf: + @echo $(INDENT)[git] Checking out libelfin + @rm -rf $(ROOT)/deps/libelfin + @mkdir -p $(ROOT)/deps + @git clone git@github.com:ccurtsinger/libelfin.git $(ROOT)/deps/libelfin + @cd $(ROOT)/deps/libelfin; make + $(ROOT)/deps/cppgoodies/include: - @ echo $(INDENT)[git] Checking out cppgoodies + @echo $(INDENT)[git] Checking out cppgoodies @rm -rf $(ROOT)/deps/cppgoodies @mkdir -p $(ROOT)/deps @git clone git://github.com/ccurtsinger/cppgoodies.git $(ROOT)/deps/cppgoodies diff --git a/tools/Makefile b/tools/Makefile new file mode 100644 index 0000000..6248eab --- /dev/null +++ b/tools/Makefile @@ -0,0 +1,4 @@ +ROOT = .. +DIRS = inspect + +include $(ROOT)/common.mk diff --git a/tools/inspect/Makefile b/tools/inspect/Makefile new file mode 100644 index 0000000..bd8643b --- /dev/null +++ b/tools/inspect/Makefile @@ -0,0 +1,14 @@ +ROOT = ../.. +TARGETS = inspect +LIBS = dwarf++ elf++ boost_filesystem boost_system +INCLUDE_DIRS = $(ROOT)/runtime \ + $(ROOT)/deps/cppgoodies/include \ + $(ROOT)/deps/libelfin/dwarf \ + $(ROOT)/deps/libelfin/elf + +include $(ROOT)/common.mk + +CXXFLAGS += --std=c++11 -fPIC + +test: debug + ./inspect $(ROOT)/tests/kmeans/kmeans diff --git a/tools/inspect/inspect.cpp b/tools/inspect/inspect.cpp new file mode 100644 index 0000000..e9abd33 --- /dev/null +++ b/tools/inspect/inspect.cpp @@ -0,0 +1,225 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "dwarf++.hh" +#include "elf++.hh" +#include "interval.h" +#include "log.h" + +using namespace std; +using namespace boost; +using namespace boost::filesystem; + +string find_build_id(elf::elf& f) { + for(auto& section : f.sections()) { + if(section.get_hdr().type == elf::sht::note) { + uintptr_t base = reinterpret_cast(section.data()); + size_t offset = 0; + while(offset < section.size()) { + Elf64_Nhdr* hdr = reinterpret_cast(base + offset); + + if(hdr->n_type == NT_GNU_BUILD_ID) { + // Found the build-id note + stringstream ss; + uintptr_t desc_base = base + offset + sizeof(Elf64_Nhdr) + hdr->n_namesz; + uint8_t* build_id = reinterpret_cast(desc_base); + for(size_t i = 0; i < hdr->n_descsz; i++) { + ss.flags(ios::hex); + ss.width(2); + ss.fill('0'); + ss << static_cast(build_id[i]); + } + return ss.str(); + + } else { + // Advance to the next note header + offset += sizeof(Elf64_Nhdr) + hdr->n_namesz + hdr->n_descsz; + } + } + } + } + return ""; +} + +const string get_full_path(const string filename) { + if(filename[0] == '/') { + return filename; + + } else if(filename[0] == '.') { + return string("/lib/x86_64-linux-gnu/libc-2.18.so"); + return canonical(filename).string(); + + } else { + // Search the environment's path for the first match + const string path_env = getenv("PATH"); + vector search_dirs; + split(search_dirs, path_env, boost::is_any_of(":")); + + for(const string& dir : search_dirs) { + auto p = path(dir) / filename; + if(exists(p)) { + return p.string(); + } else { + INFO << p.string() << " does not exist..."; + } + } + } + + return ""; +} + +elf::elf locate_debug_executable(const string filename) { + elf::elf f; + + const string full_path = get_full_path(filename); + + // If a full path wasn't found, return the invalid ELF file + if(full_path.length() == 0) { + return f; + } + + INFO << "Opening " << full_path; + + int fd = open(full_path.c_str(), O_RDONLY); + + // If the file couldn't be opened, return the invalid ELF file + if(fd < 0) { + return f; + } + + INFO << "Opened " << full_path; + + // Load the opened ELF file + f = elf::elf(elf::create_mmap_loader(fd)); + + INFO << "Loaded"; + + // If this file has a .debug_info section, return it + if(f.get_section(".debug_info").valid()) { + return f; + } + + // If there isn't a .debug_info section, check for the .gnu_debuglink section + auto& link_section = f.get_section(".gnu_debuglink"); + + // Store the full path to the executable and its directory name + string directory = full_path.substr(0, full_path.find_last_of('/')); + + // Build a vector of paths to search for a debug version of the file + vector search_paths; + + // Check for a build-id section + string build_id = find_build_id(f); + if(build_id.length() > 0) { + string prefix = build_id.substr(0, 2); + string suffix = build_id.substr(2); + + auto p = path("/usr/lib/debug/.build-id") / prefix / (suffix + ".debug"); + search_paths.push_back(p.string()); + } + + // Check for a debug_link section + if(link_section.valid()) { + string link_name = reinterpret_cast(link_section.data()); + + search_paths.push_back(directory + "/" + link_name); + search_paths.push_back(directory + "/.debug/" + link_name); + search_paths.push_back("/usr/lib/debug" + directory + "/" + link_name); + } + + // Try all the usable search paths + for(const string& path : search_paths) { + INFO << "Trying " << path; + fd = open(path.c_str(), O_RDONLY); + if(fd >= 0) { + f = elf::elf(elf::create_mmap_loader(fd)); + if(f.get_section(".debug_info").valid()) { + INFO << "Found debug symbols in " << path; + break; + } + f = elf::elf(); + } + } + + return f; +} + +int main(int argc, char** argv) { + if(argc != 2) { + cerr << "Usage: " << argv[0] << " \n"; + return 2; + } + + elf::elf f = locate_debug_executable(argv[1]); + + REQUIRE(f.valid()) << "Couldn't find a debug version of " << argv[1]; + + dwarf::dwarf d(dwarf::elf::create_loader(f)); + + map>> line_map; + + for(auto unit : d.compilation_units()) { + string prev_file; + size_t prev_line; + uintptr_t prev_address = 0; + for(auto& line : unit.get_line_table()) { + if(prev_address != 0) { + vector& intervals = line_map[prev_file][prev_line]; + + if(intervals.size() > 0 && intervals.back().getLimit() == prev_address) { + prev_address = intervals.back().getBase(); + intervals.pop_back(); + } + + line_map[prev_file][prev_line].push_back(interval(prev_address, line.address)); + } + + if(line.end_sequence) { + prev_address = 0; + } else { + prev_file = line.file->path; + prev_line = line.line; + prev_address = line.address; + } + } + } + + map> inverted; + + for(const auto& file : line_map) { + cout << file.first << "\n"; + for(const auto& line : file.second) { + cout << " " << line.first << ": "; + for(const auto& range : line.second) { + auto iter = inverted.find(range); + if(iter != inverted.end()) { + PREFER(iter->second.first == file.first && iter->second.second == line.first) + << "New line information for:\n" + << " " << file.first << ":" << line.first << " (" << range << ")\n" + << " overlaps with entry for " + << iter->second.first << ":" << iter->second.second << " (" << iter->first << ")"; + } + + inverted[range] = pair(file.first, line.first); + + cout << hex << range << " " << dec; + } + cout << "\n"; + } + cout << "\n"; + } + + return 0; +}