Index: lit/Minidump/Windows/Sigsegv/Inputs/sigsegv.cpp =================================================================== --- lit/Minidump/Windows/Sigsegv/Inputs/sigsegv.cpp +++ lit/Minidump/Windows/Sigsegv/Inputs/sigsegv.cpp @@ -0,0 +1,40 @@ + +// nodefaultlib build: cl -Zi sigsegv.cpp /link /nodefaultlib + +#ifdef USE_CRT +#include +#else +int main(); +extern "C" +{ + int _fltused; + void mainCRTStartup() { main(); } + void printf(const char*, ...) {} +} +#endif + +void crash(bool crash_self) +{ + printf("Before...\n"); + if(crash_self) + { + printf("Crashing in 3, 2, 1 ...\n"); + *(volatile int*)nullptr = 0; + } + printf("After...\n"); +} + +int foo(int x, float y, const char* msg) +{ + bool flag = x > y; + if(flag) + printf("x = %d, y = %f, msg = %s\n", x, y, msg); + crash(flag); + return x << 1; +} + +int main() +{ + foo(10, 3.14, "testing"); +} + Index: lit/Minidump/Windows/Sigsegv/Inputs/sigsegv.lldbinit =================================================================== --- lit/Minidump/Windows/Sigsegv/Inputs/sigsegv.lldbinit +++ lit/Minidump/Windows/Sigsegv/Inputs/sigsegv.lldbinit @@ -0,0 +1,2 @@ +bt all +dis Index: lit/Minidump/Windows/Sigsegv/sigsegv.test =================================================================== --- lit/Minidump/Windows/Sigsegv/sigsegv.test +++ lit/Minidump/Windows/Sigsegv/sigsegv.test @@ -0,0 +1,22 @@ +// RUN: cd %p/Inputs +// RUN: env LLDB_USE_NATIVE_PDB_READER=1 \ +// RUN: %lldb -c sigsegv.dmp -s sigsegv.lldbinit | FileCheck %s + +CHECK: * thread #1, stop reason = Exception 0xc0000005 encountered at address 0x7ff7a13110d9 +CHECK: * frame #0: 0x00007ff7a13110d9 sigsegv.exe`crash at sigsegv.cpp:22 + +CHECK: sigsegv.exe`crash: +CHECK: 0x7ff7a13110b0 <+0>: movb %cl, 0x8(%rsp) +CHECK: 0x7ff7a13110b4 <+4>: subq $0x28, %rsp +CHECK: 0x7ff7a13110b8 <+8>: leaq 0x1f41(%rip), %rcx +CHECK: 0x7ff7a13110bf <+15>: callq 0x7ff7a1311019 +CHECK: 0x7ff7a13110c4 <+20>: movzbl 0x30(%rsp), %eax +CHECK: 0x7ff7a13110c9 <+25>: testl %eax, %eax +CHECK: 0x7ff7a13110cb <+27>: je 0x7ff7a13110e4 ; <+52> at sigsegv.cpp:24 +CHECK: 0x7ff7a13110cd <+29>: leaq 0x1f3c(%rip), %rcx +CHECK: 0x7ff7a13110d4 <+36>: callq 0x7ff7a1311019 +CHECK: -> 0x7ff7a13110d9 <+41>: movl $0x0, 0x0 +CHECK: 0x7ff7a13110e4 <+52>: leaq 0x1f45(%rip), %rcx +CHECK: 0x7ff7a13110eb <+59>: callq 0x7ff7a1311019 +CHECK: 0x7ff7a13110f0 <+64>: addq $0x28, %rsp +CHECK: 0x7ff7a13110f4 <+68>: retq 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 GetThreads(); + llvm::ArrayRef + GetThreadContext(const MinidumpLocationDescriptor &location); + llvm::ArrayRef GetThreadContext(const MinidumpThread &td); llvm::ArrayRef 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 -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 +MinidumpParser::GetThreadContext(const MinidumpThread &td) { + return GetThreadContext(td.thread_context); } llvm::ArrayRef Index: source/Plugins/Process/minidump/ProcessMinidump.cpp =================================================================== --- source/Plugins/Process/minidump/ProcessMinidump.cpp +++ source/Plugins/Process/minidump/ProcessMinidump.cpp @@ -22,19 +22,86 @@ #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 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, const UUID &uuid, + 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; + m_uuid = uuid; + } + +private: + void Dump(Stream *s) override { *s << "PlaceholderObjectFile\n"; } + 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; } + void CreateSections(SectionList &unified_section_list) override {} + + uint32_t GetAddressByteSize() const override { + return GetModule()->GetArchitecture().GetAddressByteSize(); + } + + lldb::ByteOrder GetByteOrder() const override { + return GetModule()->GetArchitecture().GetByteOrder(); + } + + bool GetUUID(lldb_private::UUID *uuid) override { + *uuid = m_uuid; + 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(this); + } + return m_symtab_ap.get(); + } + + ConstString GetPluginName() override { + return ConstString("placeholder-obj"); + } + + uint32_t GetPluginVersion() override { return 1; } + +private: + UUID m_uuid; +}; + +//------------------------------------------------------------------ /// 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 +120,20 @@ // 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 guard(m_mutex); + lldbassert(!m_did_load_objfile.load()); + m_objfile_sp = std::make_shared( + shared_from_this(), m_uuid, 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 +150,11 @@ section_sp, module->base_of_image); } - ObjectFile *GetObjectFile() override { return nullptr; } + ObjectFile *GetObjectFile() override { + std::lock_guard guard(m_mutex); + lldbassert(m_did_load_objfile); + return m_objfile_sp.get(); + } SectionList *GetSectionList() override { return Module::GetUnifiedSectionList(); @@ -302,15 +383,22 @@ if (m_thread_list.size() > 0) num_threads = m_thread_list.size(); - for (lldb::tid_t tid = 0; tid < num_threads; ++tid) { + for (size_t t_index = 0; t_index < num_threads; ++t_index) { llvm::ArrayRef 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 +435,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 +457,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/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 HasCompilandInfo(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 @@ -64,6 +64,9 @@ BuildInfoSym bis(SymbolRecordKind::BuildInfoSym); llvm::cantFail(SymbolDeserializer::deserializeAs(sym, bis)); + if (!index.hasIpiStream()) + return; + // S_BUILDINFO just points to an LF_BUILDINFO in the IPI stream. Let's do // a little extra work to pull out the LF_BUILDINFO. LazyRandomTypeCollection &types = index.ipi().typeCollection(); @@ -113,6 +116,13 @@ : m_id(id), m_debug_stream(std::move(debug_stream)), m_module_descriptor(std::move(descriptor)) {} +bool CompileUnitIndex::HasCompilandInfo(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 +135,7 @@ uint16_t stream = descriptor.getModuleStreamIndex(); std::unique_ptr 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/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 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(); @@ -126,6 +126,8 @@ llvm::pdb::TpiStream &tpi() { return *m_tpi; } const llvm::pdb::TpiStream &tpi() const { return *m_tpi; } + bool hasIpiStream() const { return m_ipi != nullptr; } + llvm::pdb::TpiStream &ipi() { return *m_ipi; } const llvm::pdb::TpiStream &ipi() const { return *m_ipi; } Index: source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp =================================================================== --- source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp +++ source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp @@ -46,12 +46,15 @@ std::unique_ptr 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()); ASSIGN_PTR_OR_RETURN(result->m_symrecords, file->getPDBSymbolStream()); + // The IPI stream is not always present + if (file->hasPDBIpiStream()) + ASSIGN_PTR_OR_RETURN(result->m_ipi, file->getPDBIpiStream()); + result->m_tpi->buildHashMap(); result->m_file = std::move(file); @@ -113,6 +116,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 +133,11 @@ continue; SegmentOffset so = GetSegmentAndOffset(*iter); + if (so.segment == 0) { + // TODO: https://bugs.llvm.org/show_bug.cgi?id=39882 + 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 +156,9 @@ // 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: https://bugs.llvm.org/show_bug.cgi?id=39897 + //lldbassert(insert_result.second); } } 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,59 @@ } static std::unique_ptr -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 = 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(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 = 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(binary.getBinary()); + if (!obj) + return nullptr; + + const llvm::codeview::DebugInfo *pdb_info = nullptr; + llvm::StringRef pdb_file_name_ref; + if (obj->getDebugPDBInfo(pdb_info, pdb_file_name_ref)) { + // If it doesn't have a debug directory, fail. + 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 { + // If the file isn't a PE/COFF executable, look for the PDB in the + // current directory. This provides a basic solution for debugging minidumps + // although it's only a stop-gap (until we implement a SymbolVendor) + // + // TODO: Implement a SymbolVendor for PDBs. + // + llvm::consumeError(expected_binary.takeError()); + pdb_file = obj_file.GetFileSpec() + .GetFileNameStrippingExtension() + .GetStringRef() + .str(); + pdb_file += ".pdb"; + + // Extract the debug GUID from the ObjectFile + lldb_private::UUID uuid; + obj_file.GetUUID(&uuid); + auto uuid_bytes = uuid.GetBytes(); + if (uuid_bytes.size() != sizeof(guid) + 4) // CvRecordPdb70 + return nullptr; + 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 pdb = loadPDBFile(pdb_file, allocator); @@ -140,11 +167,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 +548,7 @@ if (!m_index) { // Lazily load and match the PDB file, but only do this once. std::unique_ptr 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 +1486,10 @@ llvm::Optional modi = m_index->GetModuleIndexForVa(file_addr); if (!modi) return 0; - CompilandIndexItem *cci = m_index->compilands().GetCompiland(*modi); - if (!cci) + if (!m_index->compilands().HasCompilandInfo(*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; }