lemo created this revision.
lemo added reviewers: labath, zturner, amccarth.
lemo added a project: LLDB.
Herald added subscribers: jfb, abidh, arphaman.
A few changes required to enable the use of the native PDB reader when
debugging minidumps:
1. Consume PDBs even if the module binary is not available Implementing a
PlaceholderObjectFile to complement the existing PlaceholderModule
2. Use the minidump exception record if present
3. Relax the PDB file search As noted in the comments this is a stop-gap until
we add a proper SymbolVendor
Overall this is a modest step towards improving the minidump debugging
experience. But more importantly,
it enables the basic consumption of PDBs using the NativePDB(TM) reader as a
foundation to add more functionality incrementally.
Repository:
rLLDB LLDB
https://reviews.llvm.org/D55142
Files:
source/Plugins/Process/minidump/MinidumpParser.cpp
source/Plugins/Process/minidump/MinidumpParser.h
source/Plugins/Process/minidump/ProcessMinidump.cpp
source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp
source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.h
source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp
source/Plugins/SymbolFile/NativePDB/PdbIndex.h
source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
Index: source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
===================================================================
--- source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
+++ source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
@@ -30,6 +30,7 @@
#include "lldb/Symbol/SymbolVendor.h"
#include "lldb/Symbol/Variable.h"
#include "lldb/Symbol/VariableList.h"
+#include "lldb/Utility/UUID.h"
#include "llvm/DebugInfo/CodeView/CVRecord.h"
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
@@ -102,33 +103,53 @@
}
static std::unique_ptr<PDBFile>
-loadMatchingPDBFile(std::string exe_path, llvm::BumpPtrAllocator &allocator) {
- // Try to find a matching PDB for an EXE.
+loadMatchingPDBFile(lldb_private::ObjectFile *obj_file,
+ llvm::BumpPtrAllocator &allocator) {
using namespace llvm::object;
- auto expected_binary = createBinary(exe_path);
- // If the file isn't a PE/COFF executable, fail.
- if (!expected_binary) {
- llvm::consumeError(expected_binary.takeError());
- return nullptr;
- }
- OwningBinary<Binary> binary = std::move(*expected_binary);
+ // Try to find a matching PDB for an EXE.
+ std::string pdb_file;
+ llvm::codeview::GUID guid;
- auto *obj = llvm::dyn_cast<llvm::object::COFFObjectFile>(binary.getBinary());
- if (!obj)
- return nullptr;
- const llvm::codeview::DebugInfo *pdb_info = nullptr;
+ auto expected_binary = createBinary(obj_file->GetFileSpec().GetPath());
+ if (expected_binary) {
+ OwningBinary<Binary> binary = std::move(*expected_binary);
- // If it doesn't have a debug directory, fail.
- llvm::StringRef pdb_file;
- auto ec = obj->getDebugPDBInfo(pdb_info, pdb_file);
- if (ec)
- return nullptr;
+ auto *obj = llvm::dyn_cast<COFFObjectFile>(binary.getBinary());
+ if (!obj)
+ return nullptr;
+ const llvm::codeview::DebugInfo *pdb_info = nullptr;
+
+ // If it doesn't have a debug directory, fail.
+ llvm::StringRef pdb_file_name_ref;
+ if (obj->getDebugPDBInfo(pdb_info, pdb_file_name_ref))
+ return nullptr;
+ pdb_file = pdb_file_name_ref;
+
+ // Extract the debug GUID from the debug directory
+ memcpy(&guid, pdb_info->PDB70.Signature, sizeof(guid));
+ } else {
+ // TODO: If the file isn't a PE/COFF executable, look for the PDB in the
+ // current directly. This provides a basic solution for debugging minidumps
+ // although it's only a stop-gap until we implement a proper SymbolVendor
+ // for PDBs.
+ llvm::consumeError(expected_binary.takeError());
+ pdb_file =
+ obj_file->GetFileSpec().GetFileNameStrippingExtension().GetCString();
+ pdb_file += ".pdb";
+
+ // Extract the debug GUID from the ObjectFile
+ lldb_private::UUID uuid;
+ obj_file->GetUUID(&uuid);
+ auto uuid_bytes = uuid.GetBytes();
+ lldbassert(uuid_bytes.size() == sizeof(llvm::codeview::GUID) + 4);
+ memcpy(&guid, uuid_bytes.data(), sizeof(guid));
+ }
// if the file doesn't exist, is not a pdb, or doesn't have a matching guid,
// fail.
llvm::file_magic magic;
- ec = llvm::identify_magic(pdb_file, magic);
+ auto ec = llvm::identify_magic(pdb_file, magic);
if (ec || magic != llvm::file_magic::pdb)
return nullptr;
std::unique_ptr<PDBFile> pdb = loadPDBFile(pdb_file, allocator);
@@ -140,11 +161,11 @@
llvm::consumeError(expected_info.takeError());
return nullptr;
}
- llvm::codeview::GUID guid;
- memcpy(&guid, pdb_info->PDB70.Signature, 16);
+ // TODO: we need to compare the age, in addition to the GUID
if (expected_info->getGuid() != guid)
return nullptr;
+
return pdb;
}
@@ -521,7 +542,7 @@
if (!m_index) {
// Lazily load and match the PDB file, but only do this once.
std::unique_ptr<PDBFile> file_up =
- loadMatchingPDBFile(m_obj_file->GetFileSpec().GetPath(), m_allocator);
+ loadMatchingPDBFile(m_obj_file, m_allocator);
if (!file_up) {
auto module_sp = m_obj_file->GetModule();
@@ -1459,11 +1480,10 @@
llvm::Optional<uint16_t> modi = m_index->GetModuleIndexForVa(file_addr);
if (!modi)
return 0;
- CompilandIndexItem *cci = m_index->compilands().GetCompiland(*modi);
- if (!cci)
+ if (!m_index->compilands().HasIndexForModule(*modi))
return 0;
-
- sc.comp_unit = GetOrCreateCompileUnit(*cci).get();
+ CompilandIndexItem cci = m_index->compilands().GetOrCreateCompiland(*modi);
+ sc.comp_unit = GetOrCreateCompileUnit(cci).get();
resolved_flags |= eSymbolContextCompUnit;
}
Index: source/Plugins/SymbolFile/NativePDB/PdbIndex.h
===================================================================
--- source/Plugins/SymbolFile/NativePDB/PdbIndex.h
+++ source/Plugins/SymbolFile/NativePDB/PdbIndex.h
@@ -103,7 +103,7 @@
/// Maps virtual address to module index
llvm::IntervalMap<lldb::addr_t, uint16_t> m_va_to_modi;
- /// The address at which the program has been loaded into memory.
+ /// The address at which the module has been loaded into memory.
lldb::addr_t m_load_address = 0;
PdbIndex();
Index: source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp
===================================================================
--- source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp
+++ source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp
@@ -46,7 +46,6 @@
std::unique_ptr<PdbIndex> result(new PdbIndex());
ASSIGN_PTR_OR_RETURN(result->m_dbi, file->getPDBDbiStream());
ASSIGN_PTR_OR_RETURN(result->m_tpi, file->getPDBTpiStream());
- ASSIGN_PTR_OR_RETURN(result->m_ipi, file->getPDBIpiStream());
ASSIGN_PTR_OR_RETURN(result->m_info, file->getPDBInfoStream());
ASSIGN_PTR_OR_RETURN(result->m_publics, file->getPDBPublicsStream());
ASSIGN_PTR_OR_RETURN(result->m_globals, file->getPDBGlobalsStream());
@@ -113,6 +112,7 @@
// range, so we have to subtract 1.
m_imap.insert(va, end - 1, C.Imod);
}
+
void visit(const SectionContrib2 &C) override { visit(C.Base); }
};
Visitor v(*this, m_va_to_modi);
@@ -129,6 +129,10 @@
continue;
SegmentOffset so = GetSegmentAndOffset(*iter);
+ if (so.segment == 0) {
+ lldbassert(so.offset == 0);
+ continue;
+ }
lldb::addr_t va = MakeVirtualAddress(so);
// We need to add 4 here to adjust for the codeview debug magic
@@ -147,7 +151,10 @@
// MakeVirtualAddress are much higher than the odds of encountering bad
// debug info, so assert that this item was inserted in the map as opposed
// to having already been there.
- lldbassert(insert_result.second);
+ //
+ // TODO: revisit this check since it fires for apparently valid PDBs
+ //
+ //lldbassert(insert_result.second);
}
}
Index: source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.h
===================================================================
--- source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.h
+++ source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.h
@@ -88,6 +88,8 @@
CompilandIndexItem *GetCompiland(uint16_t modi);
llvm::SmallString<64> GetMainSourceFile(const CompilandIndexItem &item) const;
+
+ bool HasIndexForModule(uint16_t modi) const;
};
} // namespace npdb
} // namespace lldb_private
Index: source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp
===================================================================
--- source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp
+++ source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp
@@ -113,6 +113,13 @@
: m_id(id), m_debug_stream(std::move(debug_stream)),
m_module_descriptor(std::move(descriptor)) {}
+bool CompileUnitIndex::HasIndexForModule(uint16_t modi) const {
+ const DbiModuleList &modules = m_index.dbi().modules();
+ llvm::pdb::DbiModuleDescriptor descriptor = modules.getModuleDescriptor(modi);
+ uint16_t stream = descriptor.getModuleStreamIndex();
+ return stream != kInvalidStreamIndex;
+}
+
CompilandIndexItem &CompileUnitIndex::GetOrCreateCompiland(uint16_t modi) {
auto result = m_comp_units.try_emplace(modi, nullptr);
if (!result.second)
@@ -125,6 +132,7 @@
uint16_t stream = descriptor.getModuleStreamIndex();
std::unique_ptr<llvm::msf::MappedBlockStream> stream_data =
m_index.pdb().createIndexedStream(stream);
+ lldbassert(stream_data);
llvm::pdb::ModuleDebugStreamRef debug_stream(descriptor,
std::move(stream_data));
cantFail(debug_stream.reload());
Index: source/Plugins/Process/minidump/ProcessMinidump.cpp
===================================================================
--- source/Plugins/Process/minidump/ProcessMinidump.cpp
+++ source/Plugins/Process/minidump/ProcessMinidump.cpp
@@ -22,19 +22,76 @@
#include "lldb/Utility/LLDBAssert.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/State.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Utility/Timer.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Threading.h"
#include "Plugins/Process/Utility/StopInfoMachException.h"
+
// C includes
// C++ includes
+#include <memory>
using namespace lldb;
using namespace lldb_private;
using namespace minidump;
//------------------------------------------------------------------
+/// A minimal ObjectFile implementation intended to enable the ability to
+/// consume debug information even when the module binary is not available
+///
+/// See also: PlaceholderModule
+//------------------------------------------------------------------
+class PlaceholderObjectFile : public ObjectFile {
+public:
+ PlaceholderObjectFile(const lldb::ModuleSP &module_sp, lldb::offset_t base,
+ lldb::offset_t size)
+ : ObjectFile(module_sp, &module_sp->GetFileSpec(), 0, 0, nullptr, 0) {
+ m_type = ObjectFile::eTypeObjectFile;
+ m_file_offset = base;
+ m_length = size;
+ }
+
+private:
+ void Dump(Stream *s) override { *s << "PlaceholderObjectFile\n"; }
+ uint32_t GetAddressByteSize() const override { return 0; }
+ uint32_t GetDependentModules(FileSpecList &file_list) override { return 0; }
+ bool IsExecutable() const override { return false; }
+ bool IsStripped() override { return true; }
+ bool ParseHeader() override { return true; }
+ Type CalculateType() override { return m_type; }
+ Strata CalculateStrata() override { return eStrataUnknown; }
+ lldb::ByteOrder GetByteOrder() const override { return eByteOrderLittle; }
+ void CreateSections(SectionList &unified_section_list) override {}
+
+ bool GetUUID(lldb_private::UUID *uuid) override {
+ *uuid = GetModule()->GetUUID();
+ return true;
+ }
+
+ bool GetArchitecture(ArchSpec &arch) override {
+ arch = GetModule()->GetArchitecture();
+ return true;
+ }
+
+ Symtab *GetSymtab() override {
+ if (!m_symtab_ap) {
+ m_symtab_ap = llvm::make_unique<Symtab>(this);
+ }
+ return m_symtab_ap.get();
+ }
+
+ ConstString GetPluginName() override {
+ return ConstString("placeholder-obj");
+ }
+
+ uint32_t GetPluginVersion() override { return 1; }
+};
+
+//------------------------------------------------------------------
/// A placeholder module used for minidumps, where the original
/// object files may not be available (so we can't parse the object
/// files to extract the set of sections/segments)
@@ -53,10 +110,19 @@
// Creates a synthetic module section covering the whole module image (and
// sets the section load address as well)
void CreateImageSection(const MinidumpModule *module, Target& target) {
+ // Create the placeholder object file
+ {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ lldbassert(!m_did_load_objfile.load());
+ m_objfile_sp = std::make_shared<PlaceholderObjectFile>(
+ shared_from_this(), module->base_of_image, module->size_of_image);
+ m_did_load_objfile = true;
+ }
+
const ConstString section_name(".module_image");
lldb::SectionSP section_sp(new Section(
shared_from_this(), // Module to which this section belongs.
- nullptr, // ObjectFile
+ GetObjectFile(), // ObjectFile
0, // Section ID.
section_name, // Section name.
eSectionTypeContainer, // Section type.
@@ -73,7 +139,11 @@
section_sp, module->base_of_image);
}
- ObjectFile *GetObjectFile() override { return nullptr; }
+ ObjectFile *GetObjectFile() override {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ lldbassert(m_did_load_objfile);
+ return m_objfile_sp.get();
+ }
SectionList *GetSectionList() override {
return Module::GetUnifiedSectionList();
@@ -302,15 +372,22 @@
if (m_thread_list.size() > 0)
num_threads = m_thread_list.size();
- for (lldb::tid_t tid = 0; tid < num_threads; ++tid) {
+ for (lldb::tid_t t_index = 0; t_index < num_threads; ++t_index) {
llvm::ArrayRef<uint8_t> context;
+ const auto& thread = m_thread_list[t_index];
+ auto context_location = thread.thread_context;
+ if (m_active_exception != nullptr &&
+ m_active_exception->thread_id == thread.thread_id) {
+ // If the minidump contains an exception context, use it
+ context_location = m_active_exception->thread_context;
+ }
if (!m_is_wow64)
- context = m_minidump_parser.GetThreadContext(m_thread_list[tid]);
+ context = m_minidump_parser.GetThreadContext(context_location);
else
- context = m_minidump_parser.GetThreadContextWow64(m_thread_list[tid]);
+ context = m_minidump_parser.GetThreadContextWow64(thread);
lldb::ThreadSP thread_sp(
- new ThreadMinidump(*this, m_thread_list[tid], context));
+ new ThreadMinidump(*this, m_thread_list[t_index], context));
new_thread_list.AddThread(thread_sp);
}
return new_thread_list.GetSize(false) > 0;
@@ -347,6 +424,7 @@
auto file_spec = FileSpec(name.getValue(), GetArchitecture().GetTriple());
FileSystem::Instance().Resolve(file_spec);
ModuleSpec module_spec(file_spec, uuid);
+ module_spec.GetArchitecture() = GetArchitecture();
Status error;
lldb::ModuleSP module_sp = GetTarget().GetSharedModule(module_spec, &error);
if (!module_sp || error.Fail()) {
@@ -368,6 +446,9 @@
placeholder_module->CreateImageSection(module, GetTarget());
module_sp = placeholder_module;
GetTarget().GetImages().Append(module_sp);
+
+ if (GetTarget().GetPreloadSymbols())
+ module_sp->PreloadSymbols();
}
if (log) {
Index: source/Plugins/Process/minidump/MinidumpParser.h
===================================================================
--- source/Plugins/Process/minidump/MinidumpParser.h
+++ source/Plugins/Process/minidump/MinidumpParser.h
@@ -58,6 +58,9 @@
llvm::ArrayRef<MinidumpThread> GetThreads();
+ llvm::ArrayRef<uint8_t>
+ GetThreadContext(const MinidumpLocationDescriptor &location);
+
llvm::ArrayRef<uint8_t> GetThreadContext(const MinidumpThread &td);
llvm::ArrayRef<uint8_t> GetThreadContextWow64(const MinidumpThread &td);
Index: source/Plugins/Process/minidump/MinidumpParser.cpp
===================================================================
--- source/Plugins/Process/minidump/MinidumpParser.cpp
+++ source/Plugins/Process/minidump/MinidumpParser.cpp
@@ -106,11 +106,15 @@
}
llvm::ArrayRef<uint8_t>
-MinidumpParser::GetThreadContext(const MinidumpThread &td) {
- if (td.thread_context.rva + td.thread_context.data_size > GetData().size())
+MinidumpParser::GetThreadContext(const MinidumpLocationDescriptor &location) {
+ if (location.rva + location.data_size > GetData().size())
return {};
+ return GetData().slice(location.rva, location.data_size);
+}
- return GetData().slice(td.thread_context.rva, td.thread_context.data_size);
+llvm::ArrayRef<uint8_t>
+MinidumpParser::GetThreadContext(const MinidumpThread &td) {
+ return GetThreadContext(td.thread_context);
}
llvm::ArrayRef<uint8_t>
_______________________________________________
lldb-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits