Commit fe532686 authored by Omid Azizi's avatar Omid Azizi
Browse files

DwarfReader: Basic foundation

Summary:
This diff adds a basic DwarfReader library, to extract dwarf information from an object file. The basic entity that is returned is a DIE object, which has information we're looking for. The information from the DIE is not yet extracted.

A tool is also included for testing purposes. The tool primitively replicates llvm-dwarfdump.

There is a basic test, but this is just a foundation.

Test Plan: Basic test case added.

Reviewers: yzhao, #engineering

Reviewed By: yzhao, #engineering

JIRA Issues: PP-1367

Differential Revision: https://phab.corp.pixielabs.ai/D4765

GitOrigin-RevId: 77638d8b8a2e72a5483cafa4cb6c2fd6a5866682
parent 0b617440
Showing with 252 additions and 2 deletions
+252 -2
......@@ -41,6 +41,18 @@ pl_cc_test(
],
)
pl_cc_test(
name = "dwarf_tools_test",
srcs = ["dwarf_tools_test.cc"],
data = [
"//src/stirling/obj_tools/testdata:prebuilt_exe",
],
deps = [
":cc_library",
"//src/common/exec:cc_library",
],
)
pl_cc_test(
name = "obj_tools_test",
srcs = ["obj_tools_test.cc"],
......@@ -75,3 +87,12 @@ pl_cc_binary(
":cc_library",
],
)
pl_cc_binary(
name = "dwarfdump_tool",
srcs = ["dwarfdump_tool.cc"],
deps = [
":cc_library",
"//:llvm",
],
)
#include "src/stirling/obj_tools/dwarf_tools.h"
#include <llvm/DebugInfo/DIContext.h>
#include <llvm/Object/ObjectFile.h>
namespace pl {
namespace stirling {
namespace dwarf_tools {
using llvm::DWARFContext;
using llvm::DWARFDie;
StatusOr<std::unique_ptr<DwarfReader>> DwarfReader::Create(std::string_view obj_filename) {
using llvm::MemoryBuffer;
std::error_code ec;
llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> buff_or_err =
MemoryBuffer::getFileOrSTDIN(std::string(obj_filename));
ec = buff_or_err.getError();
if (ec) {
return error::Internal(ec.message());
}
std::unique_ptr<llvm::MemoryBuffer> buffer = std::move(buff_or_err.get());
llvm::Expected<std::unique_ptr<llvm::object::Binary>> bin_or_err =
llvm::object::createBinary(*buffer);
ec = errorToErrorCode(bin_or_err.takeError());
if (ec) {
return error::Internal(ec.message());
}
auto* obj_file = llvm::dyn_cast<llvm::object::ObjectFile>(bin_or_err->get());
if (!obj_file) {
return error::Internal("Could not create DWARFContext.");
}
return std::unique_ptr<DwarfReader>(
new DwarfReader(std::move(buffer), DWARFContext::create(*obj_file)));
}
namespace {
bool IsMatchingDIE(std::string_view name, std::string_view die_name) { return (name == die_name); }
} // namespace
Status DwarfReader::GetMatchingDIEs(DWARFContext::unit_iterator_range CUs, std::string_view name,
std::vector<DWARFDie>* dies_out) {
for (const auto& CU : CUs) {
for (const auto& Entry : CU->dies()) {
DWARFDie die = {CU.get(), &Entry};
if (const char* die_name = die.getName(llvm::DINameKind::ShortName)) {
if (IsMatchingDIE(name, die_name)) {
dies_out->push_back(std::move(die));
continue;
}
}
if (const char* die_name = die.getName(llvm::DINameKind::LinkageName)) {
if (IsMatchingDIE(name, die_name)) {
dies_out->push_back(std::move(die));
}
}
}
}
return Status::OK();
}
StatusOr<std::vector<DWARFDie>> DwarfReader::GetMatchingDIEs(std::string_view name) {
DCHECK(dwarf_context_ != nullptr);
std::vector<DWARFDie> dies;
PL_RETURN_IF_ERROR(GetMatchingDIEs(dwarf_context_->normal_units(), name, &dies));
// Some day this might be useful. It searches .dwo files.
// PL_RETURN_IF_ERROR(
// GetMatchingDIEs(dwarf_context_->dwo_units(), name, &dies));
return dies;
}
} // namespace dwarf_tools
} // namespace stirling
} // namespace pl
#pragma once
#include <llvm/DebugInfo/DWARF/DWARFContext.h>
#include <llvm/Support/TargetSelect.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "src/common/base/base.h"
namespace pl {
namespace stirling {
namespace dwarf_tools {
class DwarfReader {
public:
/**
* Creates a DwarfReader that provides access to DWARF Debugging information entries (DIEs).
* @param obj_filename The object file from which to read DWARF information.
* @return error if file does not exist or is not a valid object file. Otherwise returns
* a unique pointer to a DwarfReader.
*/
static StatusOr<std::unique_ptr<DwarfReader>> Create(std::string_view obj_filename);
/**
* Searches the debug information for Debugging information entries (DIEs)
* that match the name.
* @param name Search string, which must be an exact match.
* @return Error if DIEs could not be searched, otherwise a vector of DIEs that match the search
* string.
*/
StatusOr<std::vector<llvm::DWARFDie>> GetMatchingDIEs(std::string_view name);
private:
DwarfReader(std::unique_ptr<llvm::MemoryBuffer> buffer,
std::unique_ptr<llvm::DWARFContext> dwarf_context)
: memory_buffer_(std::move(buffer)), dwarf_context_(std::move(dwarf_context)) {
// TODO(oazizi): This might cause it to get called too many times. Check perf cost.
// There is also a similar call in elf_tools.cc.
llvm::InitializeNativeTarget();
}
static Status GetMatchingDIEs(llvm::DWARFContext::unit_iterator_range CUs, std::string_view name,
std::vector<llvm::DWARFDie>* dies_out);
std::unique_ptr<llvm::MemoryBuffer> memory_buffer_;
std::unique_ptr<llvm::DWARFContext> dwarf_context_;
};
} // namespace dwarf_tools
} // namespace stirling
} // namespace pl
#include "src/stirling/obj_tools/dwarf_tools.h"
#include "src/common/testing/test_environment.h"
#include "src/common/testing/testing.h"
namespace pl {
namespace stirling {
namespace dwarf_tools {
const std::string_view kBinary = "src/stirling/obj_tools/testdata/prebuilt_dummy_exe";
using ::pl::stirling::dwarf_tools::DwarfReader;
using ::testing::IsEmpty;
using ::testing::SizeIs;
TEST(DwarfReaderTest, NonExistentPath) {
auto s = pl::stirling::dwarf_tools::DwarfReader::Create("/bogus");
ASSERT_NOT_OK(s);
}
TEST(DwarfReaderTest, Basic) {
const std::string path = pl::testing::BazelBinTestFilePath(kBinary);
ASSERT_OK_AND_ASSIGN(std::unique_ptr<DwarfReader> dwarf_reader, DwarfReader::Create(path));
ASSERT_OK_AND_THAT(dwarf_reader->GetMatchingDIEs("foo"), IsEmpty());
ASSERT_OK_AND_THAT(dwarf_reader->GetMatchingDIEs("PairStruct"), SizeIs(1));
}
} // namespace dwarf_tools
} // namespace stirling
} // namespace pl
#include <llvm/DebugInfo/DWARF/DWARFContext.h>
#include <llvm/Support/ToolOutputFile.h>
#include <llvm/Support/raw_ostream.h>
#include <string>
#include "src/common/base/base.h"
#include "src/stirling/obj_tools/dwarf_tools.h"
constexpr char kProgramDescription[] =
"A simple tool that finds debug information in object files with DWARF info.\n"
"Like dwarfdump, but simplified.";
DEFINE_string(filename, "", "Object file to search.");
DEFINE_string(die_name, "", "The Debugging Information Entry (DIE) to search for.");
void InitDumpOpts(llvm::DIDumpOptions* opts) {
opts->DumpType = llvm::DIDT_DebugInfo; // Other options: DIDT_UUID, DIDT_All, DIDT_Null
opts->ChildRecurseDepth = -1;
opts->ParentRecurseDepth = -1;
opts->ShowAddresses = true;
opts->ShowChildren = true;
opts->ShowParents = false;
opts->ShowForm = false;
opts->SummarizeTypes = false;
opts->Verbose = false;
}
int main(int argc, char** argv) {
gflags::SetUsageMessage(kProgramDescription);
pl::EnvironmentGuard env_guard(&argc, argv);
std::error_code ec;
llvm::ToolOutputFile OutputFile("-", ec, llvm::sys::fs::OF_Text);
if (ec) {
LOG(ERROR) << absl::Substitute("Unable to open file for writing. msg=$0", ec.message());
exit(1);
}
PL_ASSIGN_OR_EXIT(auto dwarf_reader,
pl::stirling::dwarf_tools::DwarfReader::Create(FLAGS_filename));
PL_ASSIGN_OR_EXIT(std::vector<llvm::DWARFDie> dies,
dwarf_reader->GetMatchingDIEs(FLAGS_die_name));
llvm::DIDumpOptions dump_opts;
InitDumpOpts(&dump_opts);
for (const auto& d : dies) {
d.dump(OutputFile.os(), 0, dump_opts);
}
return 0;
}
......@@ -26,7 +26,7 @@ TEST(ElfReaderTest, NonExistentPath) {
auto SymbolNameIs(const std::string& n) { return Field(&ElfReader::SymbolInfo::name, n); }
TEST(ElfReaderTest, ListSymbolsAnyMatch) {
const std::string path = pl::testing::TestFilePath(kBinary);
const std::string path = pl::testing::BazelBinTestFilePath(kBinary);
ASSERT_OK_AND_ASSIGN(std::unique_ptr<ElfReader> elf_reader, ElfReader::Create(path));
......
// This executable is only for testing purposes, to see if we can find the function symbols.
// This executable is only for testing purposes.
// We use it to see if we can find the function symbols and debug information.
struct PairStruct {
int a;
int b;
};
// Using extern C to avoid name mangling (which just keeps the test a bit more readable).
extern "C" {
int CanYouFindThis(int a, int b) { return a + b; }
int SomeFunction(PairStruct x) { return x.a + x.b; }
}
int main() {
......
No preview for this file type
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment