diff --git a/lldb/include/lldb/Symbol/LineTable.h b/lldb/include/lldb/Symbol/LineTable.h --- a/lldb/include/lldb/Symbol/LineTable.h +++ b/lldb/include/lldb/Symbol/LineTable.h @@ -206,7 +206,6 @@ LineTable *LinkLineTable(const FileRangeMap &file_range_map); -protected: struct Entry { Entry() : line(0), is_start_of_statement(false), is_start_of_basic_block(false), diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.h b/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.h --- a/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.h +++ b/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.h @@ -9,10 +9,12 @@ #ifndef LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_COMPILEUNITINDEX_H #define LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_COMPILEUNITINDEX_H +#include "lldb/Utility/RangeMap.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/IntervalMap.h" #include "llvm/ADT/Optional.h" +#include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h" #include "llvm/DebugInfo/CodeView/StringsAndChecksums.h" #include "llvm/DebugInfo/CodeView/SymbolRecord.h" #include "llvm/DebugInfo/CodeView/TypeIndex.h" @@ -69,6 +71,17 @@ // command line, etc. This usually contains exactly 5 items which // are references to other strings. llvm::SmallVector m_build_info; + + // Inlinee lines table in this compile unit. + std::map + m_inline_map; + + // It's the line table parsed from DEBUG_S_LINES sections, mapping the file + // address range to file index and source line number. + using GlobalLineTable = + lldb_private::RangeDataVector>; + GlobalLineTable m_global_line_table; }; /// Indexes information about all compile units. This is really just a map of diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp --- a/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp @@ -106,6 +106,24 @@ } } +static void ParseInlineeLineTableForCompileUnit(CompilandIndexItem &item) { + for (const auto &ss : item.m_debug_stream.getSubsectionsArray()) { + if (ss.kind() != DebugSubsectionKind::InlineeLines) + continue; + + DebugInlineeLinesSubsectionRef inlinee_lines; + llvm::BinaryStreamReader reader(ss.getRecordData()); + if (llvm::Error error = inlinee_lines.initialize(reader)) { + consumeError(std::move(error)); + continue; + } + + for (const InlineeSourceLine &Line : inlinee_lines) { + item.m_inline_map[Line.Header->Inlinee] = Line; + } + } +} + CompilandIndexItem::CompilandIndexItem( PdbCompilandId id, llvm::pdb::ModuleDebugStreamRef debug_stream, llvm::pdb::DbiModuleDescriptor descriptor) @@ -142,6 +160,7 @@ cci = std::make_unique( PdbCompilandId{modi}, std::move(debug_stream), std::move(descriptor)); ParseExtendedInfo(m_index, *cci); + ParseInlineeLineTableForCompileUnit(*cci); cci->m_strings.initialize(debug_stream.getSubsectionsArray()); PDBStringTable &strings = cantFail(m_index.pdb().getStringTable()); diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h --- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h +++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h @@ -9,6 +9,7 @@ #ifndef LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_SYMBOLFILENATIVEPDB_H #define LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_SYMBOLFILENATIVEPDB_H +#include "lldb/Symbol/LineTable.h" #include "lldb/Symbol/SymbolFile.h" #include "llvm/ADT/DenseMap.h" @@ -161,6 +162,25 @@ void DumpClangAST(Stream &s) override; private: + struct LineTableEntryComparator { + bool operator()(const lldb_private::LineTable::Entry &lhs, + const lldb_private::LineTable::Entry &rhs) const { + return lhs.file_addr < rhs.file_addr; + } + }; + + // From address range relative to function base to source line number. + using RangeSourceLineVector = + lldb_private::RangeDataVector; + // InlineSite contains information in a S_INLINESITE record. + struct InlineSite { + PdbCompilandSymId parent_id; + std::shared_ptr inline_function_info; + RangeSourceLineVector ranges; + std::vector line_entries; + InlineSite(PdbCompilandSymId parent_id) : parent_id(parent_id){}; + }; + uint32_t CalculateNumCompileUnits() override; lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override; @@ -225,6 +245,16 @@ VariableList &variables); size_t ParseVariablesForBlock(PdbCompilandSymId block_id); + llvm::Expected GetFileIndex(const CompilandIndexItem &cii, + uint32_t file_id); + + size_t ParseSymbolArrayInScope( + PdbCompilandSymId parent, + llvm::function_ref + fn); + + void ParseInlineSite(PdbCompilandSymId inline_site_id, Address func_addr); + llvm::BumpPtrAllocator m_allocator; lldb::addr_t m_obj_load_address = 0; @@ -241,6 +271,9 @@ llvm::DenseMap m_functions; llvm::DenseMap m_compilands; llvm::DenseMap m_types; + llvm::DenseMap> m_inline_sites; + // A map from file id in records to file index in support files. + llvm::DenseMap m_file_indexes; }; } // namespace npdb diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp --- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp @@ -330,31 +330,65 @@ Block &SymbolFileNativePDB::CreateBlock(PdbCompilandSymId block_id) { CompilandIndexItem *cii = m_index->compilands().GetCompiland(block_id.modi); CVSymbol sym = cii->m_debug_stream.readSymbolAtOffset(block_id.offset); - - if (sym.kind() == S_GPROC32 || sym.kind() == S_LPROC32) { - // This is a function. It must be global. Creating the Function entry for - // it automatically creates a block for it. - CompUnitSP comp_unit = GetOrCreateCompileUnit(*cii); - return GetOrCreateFunction(block_id, *comp_unit)->GetBlock(false); - } - - lldbassert(sym.kind() == S_BLOCK32); - - // This is a block. Its parent is either a function or another block. In - // either case, its parent can be viewed as a block (e.g. a function contains - // 1 big block. So just get the parent block and add this block to it. - BlockSym block(static_cast(sym.kind())); - cantFail(SymbolDeserializer::deserializeAs(sym, block)); - lldbassert(block.Parent != 0); - PdbCompilandSymId parent_id(block_id.modi, block.Parent); - Block &parent_block = GetOrCreateBlock(parent_id); + CompUnitSP comp_unit = GetOrCreateCompileUnit(*cii); lldb::user_id_t opaque_block_uid = toOpaqueUid(block_id); BlockSP child_block = std::make_shared(opaque_block_uid); - parent_block.AddChild(child_block); - m_ast->GetOrCreateBlockDecl(block_id); + switch (sym.kind()) { + case S_GPROC32: + case S_LPROC32: { + // This is a function. It must be global. Creating the Function entry + // for it automatically creates a block for it. + FunctionSP func = GetOrCreateFunction(block_id, *comp_unit); + Block &block = func->GetBlock(false); + if (block.GetNumRanges() == 0) + block.AddRange(Block::Range(0, func->GetAddressRange().GetByteSize())); + return block; + } + case S_BLOCK32: { + // This is a block. Its parent is either a function or another block. In + // either case, its parent can be viewed as a block (e.g. a function + // contains 1 big block. So just get the parent block and add this block + // to it. + BlockSym block(static_cast(sym.kind())); + cantFail(SymbolDeserializer::deserializeAs(sym, block)); + lldbassert(block.Parent != 0); + PdbCompilandSymId parent_id(block_id.modi, block.Parent); + Block &parent_block = GetOrCreateBlock(parent_id); + parent_block.AddChild(child_block); + m_ast->GetOrCreateBlockDecl(block_id); + m_blocks.insert({opaque_block_uid, child_block}); + break; + } + case S_INLINESITE: { + // This ensures line table is parsed first so we have inline sites info. + comp_unit->GetLineTable(); + + std::shared_ptr inline_site = m_inline_sites[opaque_block_uid]; + Block &parent_block = GetOrCreateBlock(inline_site->parent_id); + parent_block.AddChild(child_block); + + // Copy ranges from InlineSite to Block. + for (size_t i = 0; i < inline_site->ranges.GetSize(); ++i) { + auto *entry = inline_site->ranges.GetEntryAtIndex(i); + child_block->AddRange( + Block::Range(entry->GetRangeBase(), entry->GetByteSize())); + } + child_block->FinalizeRanges(); + + // Get the inlined function callsite info. + Declaration &decl = inline_site->inline_function_info->GetDeclaration(); + Declaration &callsite = inline_site->inline_function_info->GetCallSite(); + child_block->SetInlinedFunctionInfo( + inline_site->inline_function_info->GetName().GetCString(), nullptr, + &decl, &callsite); + m_blocks.insert({opaque_block_uid, child_block}); + break; + } + default: + lldbassert(false && "Symbol is not a block!"); + } - m_blocks.insert({opaque_block_uid, child_block}); return *child_block; } @@ -971,16 +1005,22 @@ continue; if (type == PDB_SymType::Function) { sc.function = GetOrCreateFunction(csid, *sc.comp_unit).get(); - sc.block = sc.GetFunctionBlock(); + Block &block = sc.function->GetBlock(true); + addr_t func_base = + sc.function->GetAddressRange().GetBaseAddress().GetFileAddress(); + addr_t offset = file_addr - func_base; + sc.block = block.FindInnermostBlockByOffset(offset); } if (type == PDB_SymType::Block) { sc.block = &GetOrCreateBlock(csid); sc.function = sc.block->CalculateSymbolContextFunction(); } - resolved_flags |= eSymbolContextFunction; - resolved_flags |= eSymbolContextBlock; - break; + if (sc.function) + resolved_flags |= eSymbolContextFunction; + if (sc.block) + resolved_flags |= eSymbolContextBlock; + break; } } @@ -998,43 +1038,24 @@ uint32_t SymbolFileNativePDB::ResolveSymbolContext( const SourceLocationSpec &src_location_spec, lldb::SymbolContextItem resolve_scope, SymbolContextList &sc_list) { - return 0; -} - -static void AppendLineEntryToSequence(LineTable &table, LineSequence &sequence, - const CompilandIndexItem &cci, - lldb::addr_t base_addr, - uint32_t file_number, - const LineFragmentHeader &block, - const LineNumberEntry &cur) { - LineInfo cur_info(cur.Flags); - - if (cur_info.isAlwaysStepInto() || cur_info.isNeverStepInto()) - return; - - uint64_t addr = base_addr + cur.Offset; - - bool is_statement = cur_info.isStatement(); - bool is_prologue = IsFunctionPrologue(cci, addr); - bool is_epilogue = IsFunctionEpilogue(cci, addr); - - uint32_t lno = cur_info.getStartLine(); - - table.AppendLineEntryToSequence(&sequence, addr, lno, 0, file_number, - is_statement, false, is_prologue, is_epilogue, - false); -} + std::lock_guard guard(GetModuleMutex()); + const uint32_t prev_size = sc_list.GetSize(); + if (resolve_scope & eSymbolContextCompUnit) { + for (uint32_t cu_idx = 0, num_cus = GetNumCompileUnits(); cu_idx < num_cus; + ++cu_idx) { + CompileUnit *cu = ParseCompileUnitAtIndex(cu_idx).get(); + if (!cu) + continue; -static void TerminateLineSequence(LineTable &table, - const LineFragmentHeader &block, - lldb::addr_t base_addr, uint32_t file_number, - uint32_t last_line, - std::unique_ptr seq) { - // The end is always a terminal entry, so insert it regardless. - table.AppendLineEntryToSequence(seq.get(), base_addr + block.CodeSize, - last_line, 0, file_number, false, false, - false, false, true); - table.InsertSequence(seq.get()); + bool file_spec_matches_cu_file_spec = FileSpec::Match( + src_location_spec.GetFileSpec(), cu->GetPrimaryFile()); + if (file_spec_matches_cu_file_spec) { + cu->ResolveSymbolContext(src_location_spec, resolve_scope, sc_list); + break; + } + } + } + return sc_list.GetSize() - prev_size; } bool SymbolFileNativePDB::ParseLineTable(CompileUnit &comp_unit) { @@ -1045,16 +1066,21 @@ std::lock_guard guard(GetModuleMutex()); PdbSymUid cu_id(comp_unit.GetID()); lldbassert(cu_id.kind() == PdbSymUidKind::Compiland); - CompilandIndexItem *cci = - m_index->compilands().GetCompiland(cu_id.asCompiland().modi); - lldbassert(cci); - auto line_table = std::make_unique(&comp_unit); + uint16_t modi = cu_id.asCompiland().modi; + CompilandIndexItem *cii = m_index->compilands().GetCompiland(modi); + lldbassert(cii); + + // Parse DEBUG_S_LINES subsections first, then parse all S_INLINESITE records + // in this CU. Add line entries into the set first so that if there are line + // entries with same addres, the later is always more accurate than the + // former. + std::set line_set; // This is basically a copy of the .debug$S subsections from all original COFF // object files merged together with address relocations applied. We are // looking for all DEBUG_S_LINES subsections. for (const DebugSubsectionRecord &dssr : - cci->m_debug_stream.getSubsectionsArray()) { + cii->m_debug_stream.getSubsectionsArray()) { if (dssr.kind() != DebugSubsectionKind::Lines) continue; @@ -1069,42 +1095,111 @@ uint64_t virtual_addr = m_index->MakeVirtualAddress(lfh->RelocSegment, lfh->RelocOffset); - const auto &checksums = cci->m_strings.checksums().getArray(); - const auto &strings = cci->m_strings.strings(); for (const LineColumnEntry &group : lines) { - // Indices in this structure are actually offsets of records in the - // DEBUG_S_FILECHECKSUMS subsection. Those entries then have an index - // into the global PDB string table. - auto iter = checksums.at(group.NameIndex); - if (iter == checksums.end()) + llvm::Expected file_index_or_err = + GetFileIndex(*cii, group.NameIndex); + if (!file_index_or_err) continue; + uint32_t file_index = file_index_or_err.get(); + lldbassert(!group.LineNumbers.empty()); + CompilandIndexItem::GlobalLineTable::Entry line_entry( + LLDB_INVALID_ADDRESS, 0); + for (const LineNumberEntry &entry : group.LineNumbers) { + LineInfo cur_info(entry.Flags); - llvm::Expected efn = - strings.getString(iter->FileNameOffset); - if (!efn) { - llvm::consumeError(efn.takeError()); - continue; - } + if (cur_info.isAlwaysStepInto() || cur_info.isNeverStepInto()) + continue; - // LLDB wants the index of the file in the list of support files. - auto fn_iter = llvm::find(cci->m_file_list, *efn); - lldbassert(fn_iter != cci->m_file_list.end()); - uint32_t file_index = std::distance(cci->m_file_list.begin(), fn_iter); + uint64_t addr = virtual_addr + entry.Offset; - std::unique_ptr sequence( - line_table->CreateLineSequenceContainer()); - lldbassert(!group.LineNumbers.empty()); + bool is_statement = cur_info.isStatement(); + bool is_prologue = IsFunctionPrologue(*cii, addr); + bool is_epilogue = IsFunctionEpilogue(*cii, addr); - for (const LineNumberEntry &entry : group.LineNumbers) { - AppendLineEntryToSequence(*line_table, *sequence, *cci, virtual_addr, - file_index, *lfh, entry); + uint32_t lno = cur_info.getStartLine(); + + line_set.emplace(addr, lno, 0, file_index, is_statement, false, + is_prologue, is_epilogue, false); + + if (line_entry.GetRangeBase() != LLDB_INVALID_ADDRESS) { + line_entry.SetRangeEnd(addr); + cii->m_global_line_table.Append(line_entry); + } + line_entry.SetRangeBase(addr); + line_entry.data = {file_index, lno}; } LineInfo last_line(group.LineNumbers.back().Flags); - TerminateLineSequence(*line_table, *lfh, virtual_addr, file_index, - last_line.getEndLine(), std::move(sequence)); + line_set.emplace(virtual_addr + lfh->CodeSize, last_line.getEndLine(), 0, + file_index, false, false, false, false, true); + + if (line_entry.GetRangeBase() != LLDB_INVALID_ADDRESS) { + line_entry.SetRangeEnd(virtual_addr + lfh->CodeSize); + cii->m_global_line_table.Append(line_entry); + } } } + cii->m_global_line_table.Sort(); + + // Parse all S_INLINESITE in this CU. + const CVSymbolArray &syms = cii->m_debug_stream.getSymbolArray(); + for (auto iter = syms.begin(); iter != syms.end();) { + if (iter->kind() != S_LPROC32 && iter->kind() != S_GPROC32) { + ++iter; + continue; + } + + uint32_t record_offset = iter.offset(); + CVSymbol func_record = + cii->m_debug_stream.readSymbolAtOffset(record_offset); + SegmentOffsetLength sol = GetSegmentOffsetAndLength(func_record); + addr_t file_vm_addr = m_index->MakeVirtualAddress(sol.so); + AddressRange func_range(file_vm_addr, sol.length, + comp_unit.GetModule()->GetSectionList()); + Address func_base = func_range.GetBaseAddress(); + PdbCompilandSymId func_id{modi, record_offset}; + + // Iterate all S_INLINESITEs in the function. + auto parse_inline_sites = [&](SymbolKind kind, PdbCompilandSymId id) { + if (kind != S_INLINESITE) + return false; + + ParseInlineSite(id, func_base); + + for (const auto &line_entry : + m_inline_sites[toOpaqueUid(id)]->line_entries) { + // If line_entry is not terminal entry, remove previous line entry at + // the same address and insert new one. Terminal entry inside an inline + // site might not be terminal entry for its parent. + if (!line_entry.is_terminal_entry) + line_set.erase(line_entry); + line_set.insert(line_entry); + } + // No longer useful after adding to line_set. + m_inline_sites[toOpaqueUid(id)]->line_entries.clear(); + return true; + }; + ParseSymbolArrayInScope(func_id, parse_inline_sites); + // Jump to the end of the function record. + iter = syms.at(getScopeEndOffset(func_record)); + } + + cii->m_global_line_table.Clear(); + + // Add line entries in line_set to line_table. + auto line_table = std::make_unique(&comp_unit); + std::unique_ptr sequence( + line_table->CreateLineSequenceContainer()); + for (const auto &line_entry : line_set) { + line_table->AppendLineEntryToSequence( + sequence.get(), line_entry.file_addr, line_entry.line, + line_entry.column, line_entry.file_idx, + line_entry.is_start_of_statement, line_entry.is_start_of_basic_block, + line_entry.is_prologue_end, line_entry.is_epilogue_begin, + line_entry.is_terminal_entry); + } + line_table->InsertSequence(sequence.get()); + if (line_table->GetSize() == 0) return false; @@ -1117,6 +1212,33 @@ return false; } +llvm::Expected +SymbolFileNativePDB::GetFileIndex(const CompilandIndexItem &cii, + uint32_t file_id) { + auto index_iter = m_file_indexes.find(file_id); + if (index_iter != m_file_indexes.end()) + return index_iter->getSecond(); + const auto &checksums = cii.m_strings.checksums().getArray(); + const auto &strings = cii.m_strings.strings(); + // Indices in this structure are actually offsets of records in the + // DEBUG_S_FILECHECKSUMS subsection. Those entries then have an index + // into the global PDB string table. + auto iter = checksums.at(file_id); + if (iter == checksums.end()) + return llvm::make_error(raw_error_code::no_entry); + + llvm::Expected efn = strings.getString(iter->FileNameOffset); + if (!efn) { + return efn.takeError(); + } + + // LLDB wants the index of the file in the list of support files. + auto fn_iter = llvm::find(cii.m_file_list, *efn); + lldbassert(fn_iter != cii.m_file_list.end()); + m_file_indexes[file_id] = std::distance(cii.m_file_list.begin(), fn_iter); + return m_file_indexes[file_id]; +} + bool SymbolFileNativePDB::ParseSupportFiles(CompileUnit &comp_unit, FileSpecList &support_files) { std::lock_guard guard(GetModuleMutex()); @@ -1141,11 +1263,220 @@ return false; } +void SymbolFileNativePDB::ParseInlineSite(PdbCompilandSymId id, + Address func_addr) { + lldb::user_id_t opaque_uid = toOpaqueUid(id); + if (m_inline_sites.find(opaque_uid) != m_inline_sites.end()) + return; + + addr_t func_base = func_addr.GetFileAddress(); + CompilandIndexItem *cii = m_index->compilands().GetCompiland(id.modi); + CVSymbol sym = cii->m_debug_stream.readSymbolAtOffset(id.offset); + CompUnitSP comp_unit = GetOrCreateCompileUnit(*cii); + + InlineSiteSym inline_site(static_cast(sym.kind())); + cantFail(SymbolDeserializer::deserializeAs(sym, inline_site)); + PdbCompilandSymId parent_id(id.modi, inline_site.Parent); + + std::shared_ptr inline_site_sp = + std::make_shared(parent_id); + + // Get the inlined function declaration info. + auto iter = cii->m_inline_map.find(inline_site.Inlinee); + if (iter == cii->m_inline_map.end()) + return; + InlineeSourceLine inlinee_line = iter->second; + + const FileSpecList &files = comp_unit->GetSupportFiles(); + FileSpec decl_file; + llvm::Expected file_index_or_err = + GetFileIndex(*cii, inlinee_line.Header->FileID); + if (!file_index_or_err) + return; + uint32_t decl_file_idx = file_index_or_err.get(); + decl_file = files.GetFileSpecAtIndex(decl_file_idx); + uint32_t decl_line = inlinee_line.Header->SourceLineNum; + std::unique_ptr decl_up = + std::make_unique(decl_file, decl_line); + + // Parse range and line info. + uint32_t code_offset = 0; + int32_t line_offset = 0; + bool has_base = false; + bool is_new_line_offset = false; + + bool is_start_of_statement = false; + // The first instruction is the prologue end. + bool is_prologue_end = true; + + auto change_code_offset = [&](uint32_t code_delta) { + if (has_base) { + inline_site_sp->ranges.Append(RangeSourceLineVector::Entry( + code_offset, code_delta, decl_line + line_offset)); + is_prologue_end = false; + is_start_of_statement = false; + } else { + is_start_of_statement = true; + } + has_base = true; + code_offset += code_delta; + + if (is_new_line_offset) { + LineTable::Entry line_entry(func_base + code_offset, + decl_line + line_offset, 0, decl_file_idx, + true, false, is_prologue_end, false, false); + inline_site_sp->line_entries.push_back(line_entry); + is_new_line_offset = false; + } + }; + auto change_code_length = [&](uint32_t length) { + inline_site_sp->ranges.Append(RangeSourceLineVector::Entry( + code_offset, length, decl_line + line_offset)); + has_base = false; + + LineTable::Entry end_line_entry(func_base + code_offset + length, + decl_line + line_offset, 0, decl_file_idx, + false, false, false, false, true); + inline_site_sp->line_entries.push_back(end_line_entry); + }; + auto change_line_offset = [&](int32_t line_delta) { + line_offset += line_delta; + if (has_base) { + LineTable::Entry line_entry( + func_base + code_offset, decl_line + line_offset, 0, decl_file_idx, + is_start_of_statement, false, is_prologue_end, false, false); + inline_site_sp->line_entries.push_back(line_entry); + } else { + // Add line entry in next call to change_code_offset. + is_new_line_offset = true; + } + }; + + for (auto &annot : inline_site.annotations()) { + switch (annot.OpCode) { + case BinaryAnnotationsOpCode::CodeOffset: + case BinaryAnnotationsOpCode::ChangeCodeOffset: + case BinaryAnnotationsOpCode::ChangeCodeOffsetBase: + change_code_offset(annot.U1); + break; + case BinaryAnnotationsOpCode::ChangeLineOffset: + change_line_offset(annot.S1); + break; + case BinaryAnnotationsOpCode::ChangeCodeLength: + change_code_length(annot.U1); + code_offset += annot.U1; + break; + case BinaryAnnotationsOpCode::ChangeCodeOffsetAndLineOffset: + change_code_offset(annot.U1); + change_line_offset(annot.S1); + break; + case BinaryAnnotationsOpCode::ChangeCodeLengthAndCodeOffset: + change_code_offset(annot.U2); + change_code_length(annot.U1); + break; + default: + break; + } + } + + inline_site_sp->ranges.Sort(); + inline_site_sp->ranges.CombineConsecutiveEntriesWithEqualData(); + + // Get the inlined function callsite info. + std::unique_ptr callsite_up; + if (!inline_site_sp->ranges.IsEmpty()) { + auto *entry = inline_site_sp->ranges.GetEntryAtIndex(0); + addr_t base_offset = entry->GetRangeBase(); + if (cii->m_debug_stream.readSymbolAtOffset(parent_id.offset).kind() == + S_INLINESITE) { + // Its parent is another inline site, lookup parent site's range vector + // for callsite line. + ParseInlineSite(parent_id, func_base); + std::shared_ptr parent_site = + m_inline_sites[toOpaqueUid(parent_id)]; + FileSpec &parent_decl_file = + parent_site->inline_function_info->GetDeclaration().GetFile(); + if (auto *parent_entry = + parent_site->ranges.FindEntryThatContains(base_offset)) { + callsite_up = + std::make_unique(parent_decl_file, parent_entry->data); + } + } else { + // Its parent is a function, lookup global line table for callsite. + if (auto *entry = cii->m_global_line_table.FindEntryThatContains( + func_base + base_offset)) { + const FileSpec &callsite_file = + files.GetFileSpecAtIndex(entry->data.first); + callsite_up = + std::make_unique(callsite_file, entry->data.second); + } + } + } + + // Get the inlined function name. + CVType inlinee_cvt = m_index->ipi().getType(inline_site.Inlinee); + std::string inlinee_name; + if (inlinee_cvt.kind() == LF_MFUNC_ID) { + MemberFuncIdRecord mfr; + cantFail( + TypeDeserializer::deserializeAs(inlinee_cvt, mfr)); + LazyRandomTypeCollection &types = m_index->tpi().typeCollection(); + inlinee_name.append(std::string(types.getTypeName(mfr.ClassType))); + inlinee_name.append("::"); + inlinee_name.append(mfr.getName().str()); + } else if (inlinee_cvt.kind() == LF_FUNC_ID) { + FuncIdRecord fir; + cantFail(TypeDeserializer::deserializeAs(inlinee_cvt, fir)); + TypeIndex parent_idx = fir.getParentScope(); + if (!parent_idx.isNoneType()) { + LazyRandomTypeCollection &ids = m_index->ipi().typeCollection(); + inlinee_name.append(std::string(ids.getTypeName(parent_idx))); + inlinee_name.append("::"); + } + inlinee_name.append(fir.getName().str()); + } + inline_site_sp->inline_function_info = std::make_shared( + inlinee_name.c_str(), llvm::StringRef(), decl_up.get(), + callsite_up.get()); + + m_inline_sites[opaque_uid] = inline_site_sp; +} + size_t SymbolFileNativePDB::ParseBlocksRecursive(Function &func) { std::lock_guard guard(GetModuleMutex()); - GetOrCreateBlock(PdbSymUid(func.GetID()).asCompilandSym()); - // FIXME: Parse child blocks - return 1; + PdbCompilandSymId func_id = PdbSymUid(func.GetID()).asCompilandSym(); + // After we iterate through inline sites inside the function, we already get + // all the info needed, removing from the map to save memory. + std::set remove_uids; + auto parse_inline_sites = [&](SymbolKind kind, PdbCompilandSymId id) { + if (kind != S_INLINESITE) + return false; + GetOrCreateBlock(id); + remove_uids.insert(toOpaqueUid(id)); + return true; + }; + size_t count = ParseSymbolArrayInScope(func_id, parse_inline_sites); + for (uint64_t uid : remove_uids) { + m_inline_sites.erase(uid); + } + return count; +} + +size_t SymbolFileNativePDB::ParseSymbolArrayInScope( + PdbCompilandSymId parent_id, + llvm::function_ref fn) { + CompilandIndexItem *cii = m_index->compilands().GetCompiland(parent_id.modi); + CVSymbolArray syms = + cii->m_debug_stream.getSymbolArrayForScope(parent_id.offset); + + size_t count = 1; + for (auto iter = syms.begin(); iter != syms.end(); ++iter) { + PdbCompilandSymId child_id(parent_id.modi, iter.offset()); + if (fn(iter->kind(), child_id)) + ++count; + } + + return count; } void SymbolFileNativePDB::DumpClangAST(Stream &s) { m_ast->Dump(s); } @@ -1396,6 +1727,9 @@ } case S_BLOCK32: break; + case S_INLINESITE: + // TODO: Handle inline site case. + return 0; default: lldbassert(false && "Symbol is not a block!"); return 0; diff --git a/lldb/test/Shell/SymbolFile/NativePDB/Inputs/inline_sites.lldbinit b/lldb/test/Shell/SymbolFile/NativePDB/Inputs/inline_sites.lldbinit new file mode 100644 --- /dev/null +++ b/lldb/test/Shell/SymbolFile/NativePDB/Inputs/inline_sites.lldbinit @@ -0,0 +1,17 @@ +image dump line-table a.cpp -v + +b a.cpp:5 +b a.h:5 +b a.h:6 +b a.h:7 +b b.h:6 +b c.h:6 + +image lookup -a 0x140001003 -v +image lookup -a 0x140001004 -v +image lookup -a 0x140001014 -v +image lookup -a 0x14000101a -v +image lookup -a 0x140001021 -v +image lookup -a 0x140001028 -v + +quit \ No newline at end of file diff --git a/lldb/test/Shell/SymbolFile/NativePDB/inline_sites.s b/lldb/test/Shell/SymbolFile/NativePDB/inline_sites.s new file mode 100644 --- /dev/null +++ b/lldb/test/Shell/SymbolFile/NativePDB/inline_sites.s @@ -0,0 +1,667 @@ +# clang-format off +# REQUIRES: lld, x86 + +# RUN: llvm-mc -triple=x86_64-windows-msvc --filetype=obj %s > %t.obj +# RUN: lld-link -debug:full -nodefaultlib -entry:main %t.obj -out:%t.exe +# RUN: env LLDB_USE_NATIVE_PDB_READER=1 %lldb -f %t.exe -s \ +# RUN: %p/Inputs/inline_sites.lldbinit 2>&1 | FileCheck %s + +# Compiled from the following files, but replaced the call to abort with nop. +# a.cpp: +# #include "stdlib.h" +# #include "a.h" +# int main(int argc, char** argv) { +# Namespace1::foo(2); +# return 0; +# } +# a.h: +# #include "b.h" +# namespace Namespace1 { +# inline void foo(int x) { +# static volatile int gv_foo; +# ++gv_foo; +# if (!gv_foo) +# abort(); +# Class1::bar(x + 1); +# } +# } +# b.h: +# #include "c.h" +# class Class1 { +# public: +# inline static void bar(int x) { +# static volatile int gv_bar; +# ++gv_bar; +# Namespace2::Class2::func(x + 1); +# } +# }; +# c.h: +# namespace Namespace2{ +# class Class2{ +# public: +# inline static void func(int x) { +# static volatile int gv_func; +# gv_func += x; +# } +# }; +# } + +# CHECK: (lldb) image dump line-table a.cpp -v +# CHECK-NEXT: Line table for /tmp/a.cpp in +# CHECK-NEXT: 0x0000000140001000: /tmp/a.cpp:3 +# CHECK-NEXT: 0x0000000140001004: /tmp/a.h:5, is_start_of_statement = TRUE, is_prologue_end = TRUE +# CHECK-NEXT: 0x000000014000100a: /tmp/a.h:6 +# CHECK-NEXT: 0x0000000140001014: /tmp/b.h:6, is_start_of_statement = TRUE, is_prologue_end = TRUE +# CHECK-NEXT: 0x000000014000101a: /tmp/c.h:6, is_start_of_statement = TRUE, is_prologue_end = TRUE +# CHECK-NEXT: 0x0000000140001021: /tmp/a.cpp:5 +# CHECK-NEXT: 0x0000000140001028: /tmp/a.h:7, is_start_of_statement = TRUE +# CHECK-NEXT: 0x000000014000102a: /tmp/a.cpp:5, is_terminal_entry = TRUE + +# CEHCK: (lldb) b a.cpp:5 +# CHECK: Breakpoint 1: where = {{.*}}`main + 33 at a.cpp:5, address = 0x0000000140001021 +# CEHCK: (lldb) b a.h:5 +# CHECK: Breakpoint 2: where = {{.*}}`main + 4 [inlined] Namespace1::foo at a.h:5, address = 0x0000000140001004 +# CEHCK: (lldb) b a.h:6 +# CHECK: Breakpoint 3: where = {{.*}}`main + 10 [inlined] Namespace1::foo + 6 at a.h:6, address = 0x000000014000100a +# CEHCK: (lldb) b a.h:7 +# CHECK: Breakpoint 4: where = {{.*}}`main + 40 [inlined] Namespace1::foo at a.h:7, address = 0x0000000140001028 +# CEHCK: (lldb) b b.h:6 +# CHECK: Breakpoint 5: where = {{.*}}`main + 20 [inlined] Class1::bar at b.h:6, address = 0x0000000140001014 +# CEHCK: (lldb) b c.h:6 +# CHECK: Breakpoint 6: where = {{.*}}`main + 26 [inlined] Namespace2::Class2::func at c.h:6, address = 0x000000014000101a + +# CEHCK-LABEL: (lldb) image lookup -a 0x140001003 -v +# CHECK: Summary: {{.*}}`main + 3 at a.cpp:3 +# CHECK: Function: id = {{.*}}, name = "main", range = [0x0000000140001000-0x000000014000102a) +# CHECK: Blocks: id = {{.*}}, range = [0x140001000-0x14000102a) +# CHECK: LineEntry: [0x0000000140001000-0x0000000140001004): /tmp/a.cpp:3 + +# CEHCK-LABEL: (lldb) image lookup -a 0x140001004 -v +# CHECK: Summary: {{.*}}`main + 4 [inlined] Namespace1::foo at a.h:5 +# CHECK-NEXT: {{.*}}`main + 4 at a.cpp:4 +# CHECK: Function: id = {{.*}}, name = "main", range = [0x0000000140001000-0x000000014000102a) +# CHECK: Blocks: id = {{.*}}, range = [0x140001000-0x14000102a) +# CHECK-NEXT: id = {{.*}}, ranges = [0x140001004-0x140001021)[0x140001028-0x14000102a), name = "Namespace1::foo", decl = a.h:3 +# CHECK: LineEntry: [0x0000000140001004-0x000000014000100a): /tmp/a.h:5 + +# CEHCK-LABEL: (lldb) image lookup -a 0x140001014 -v +# CHECK: Summary: {{.*}}`main + 20 [inlined] Class1::bar at b.h:6 +# CHECK-NEXT: {{.*}}`main + 20 [inlined] Namespace1::foo + 16 at a.h:8 +# CHECK-NEXT: {{.*}}`main + 4 at a.cpp:4 +# CHECK: Function: id = {{.*}}, name = "main", range = [0x0000000140001000-0x000000014000102a) +# CHECK: Blocks: id = {{.*}}, range = [0x140001000-0x14000102a) +# CHECK-NEXT: id = {{.*}}, ranges = [0x140001004-0x140001021)[0x140001028-0x14000102a), name = "Namespace1::foo", decl = a.h:3 +# CHECK-NEXT: id = {{.*}}, range = [0x140001014-0x140001021), name = "Class1::bar", decl = b.h:4 +# CHECK: LineEntry: [0x0000000140001014-0x000000014000101a): /tmp/b.h:6 + +# CEHCK-LABEL: (lldb) image lookup -a 0x14000101a -v +# CHECK: Summary: {{.*}}`main + 26 [inlined] Namespace2::Class2::func at c.h:6 +# CHECK-NEXT: {{.*}}`main + 26 [inlined] Class1::bar + 6 at b.h:7 +# CHECK-NEXT: {{.*}}`main + 20 [inlined] Namespace1::foo + 16 at a.h:8 +# CHECK-NEXT: {{.*}}`main + 4 at a.cpp:4 +# CHECK: Function: id = {{.*}}, name = "main", range = [0x0000000140001000-0x000000014000102a) +# CHECK: Blocks: id = {{.*}}, range = [0x140001000-0x14000102a) +# CHECK-NEXT: id = {{.*}}, ranges = [0x140001004-0x140001021)[0x140001028-0x14000102a), name = "Namespace1::foo", decl = a.h:3 +# CHECK-NEXT: id = {{.*}}, range = [0x140001014-0x140001021), name = "Class1::bar", decl = b.h:4 +# CHECK-NEXT: id = {{.*}}, range = [0x14000101a-0x140001021), name = "Namespace2::Class2::func", decl = c.h:4 +# CHECK: LineEntry: [0x000000014000101a-0x0000000140001021): /tmp/c.h:6 + +# CEHCK-LABEL: (lldb) image lookup -a 0x140001021 -v +# CHECK: Summary: {{.*}}`main + 33 at a.cpp:5 +# CHECK: Function: id = {{.*}}, name = "main", range = [0x0000000140001000-0x000000014000102a) +# CHECK: Blocks: id = {{.*}}, range = [0x140001000-0x14000102a) +# CHECK: LineEntry: [0x0000000140001021-0x0000000140001028): /tmp/a.cpp:5 + +# CEHCK-LABEL: (lldb) image lookup -a 0x140001028 -v +# CHECK: Summary: {{.*}}`main + 40 [inlined] Namespace1::foo at a.h:7 +# CHECK-NEXT: {{.*}}`main + 40 at a.cpp:4 +# CHECK: Function: id = {{.*}}, name = "main", range = [0x0000000140001000-0x000000014000102a) +# CHECK: Blocks: id = {{.*}}, range = [0x140001000-0x14000102a) +# CHECK-NEXT: id = {{.*}}, ranges = [0x140001004-0x140001021)[0x140001028-0x14000102a), name = "Namespace1::foo", decl = a.h:3 +# CHECK: LineEntry: [0x0000000140001028-0x000000014000102a): /tmp/a.h:7 + + .text + .def @feat.00; + .scl 3; + .type 0; + .endef + .globl @feat.00 +.set @feat.00, 0 + .intel_syntax noprefix + .file "a.cpp" + .def main; + .scl 2; + .type 32; + .endef + .section .text,"xr",one_only,main + .globl main # -- Begin function main +main: # @main +.Lfunc_begin0: + .cv_func_id 0 + .cv_file 1 "/tmp/a.cpp" "4ECCDD2814054DCF80EA72F4349036C4" 1 + .cv_loc 0 1 3 0 # a.cpp:3:0 +.seh_proc main +# %bb.0: # %entry + #DEBUG_VALUE: main:argv <- $rdx + #DEBUG_VALUE: main:argc <- $ecx + #DEBUG_VALUE: foo:x <- 2 + sub rsp, 40 + .seh_stackalloc 40 + .seh_endprologue +.Ltmp0: + .cv_file 2 "/tmp/./a.h" "9E656AFA1B1B681265C87EEA8BBE073E" 1 + .cv_inline_site_id 1 within 0 inlined_at 1 4 0 + .cv_loc 1 2 5 0 # ./a.h:5:0 + inc dword ptr [rip + "?gv_foo@?1??foo@Namespace1@@YAXH@Z@4HC"] + .cv_loc 1 2 6 0 # ./a.h:6:0 + mov eax, dword ptr [rip + "?gv_foo@?1??foo@Namespace1@@YAXH@Z@4HC"] + test eax, eax + je .LBB0_2 +.Ltmp1: +# %bb.1: # %"?foo@Namespace1@@YAXH@Z.exit" + #DEBUG_VALUE: foo:x <- 2 + #DEBUG_VALUE: main:argc <- $ecx + #DEBUG_VALUE: main:argv <- $rdx + #DEBUG_VALUE: bar:x <- [DW_OP_plus_uconst 1, DW_OP_stack_value] 2 + .cv_file 3 "/tmp/./b.h" "BE52983EB17A3B0DA14E68A5CCBC4399" 1 + .cv_inline_site_id 2 within 1 inlined_at 2 8 0 + .cv_loc 2 3 6 0 # ./b.h:6:0 + inc dword ptr [rip + "?gv_bar@?1??bar@Class1@@SAXH@Z@4HC"] +.Ltmp2: + #DEBUG_VALUE: func:x <- 4 + .cv_file 4 "/tmp/./c.h" "D1B76A1C2A54DBEA648F3A11496166B8" 1 + .cv_inline_site_id 3 within 2 inlined_at 3 7 0 + .cv_loc 3 4 6 0 # ./c.h:6:0 + add dword ptr [rip + "?gv_func@?1??func@Class2@Namespace2@@SAXH@Z@4HC"], 4 +.Ltmp3: + .cv_loc 0 1 5 0 # a.cpp:5:0 + xor eax, eax + add rsp, 40 + ret +.Ltmp4: +.LBB0_2: # %if.then.i + #DEBUG_VALUE: foo:x <- 2 + #DEBUG_VALUE: main:argc <- $ecx + #DEBUG_VALUE: main:argv <- $rdx + .cv_loc 1 2 7 0 # ./a.h:7:0 + nop +.Ltmp5: + int3 +.Ltmp6: + #DEBUG_VALUE: main:argv <- [DW_OP_LLVM_entry_value 1] $rdx + #DEBUG_VALUE: main:argc <- [DW_OP_LLVM_entry_value 1] $ecx +.Lfunc_end0: + .seh_endproc + # -- End function + .section .bss,"bw",discard,"?gv_foo@?1??foo@Namespace1@@YAXH@Z@4HC" + .globl "?gv_foo@?1??foo@Namespace1@@YAXH@Z@4HC" # @"?gv_foo@?1??foo@Namespace1@@YAXH@Z@4HC" + .p2align 2 +"?gv_foo@?1??foo@Namespace1@@YAXH@Z@4HC": + .long 0 # 0x0 + + .section .bss,"bw",discard,"?gv_bar@?1??bar@Class1@@SAXH@Z@4HC" + .globl "?gv_bar@?1??bar@Class1@@SAXH@Z@4HC" # @"?gv_bar@?1??bar@Class1@@SAXH@Z@4HC" + .p2align 2 +"?gv_bar@?1??bar@Class1@@SAXH@Z@4HC": + .long 0 # 0x0 + + .section .bss,"bw",discard,"?gv_func@?1??func@Class2@Namespace2@@SAXH@Z@4HC" + .globl "?gv_func@?1??func@Class2@Namespace2@@SAXH@Z@4HC" # @"?gv_func@?1??func@Class2@Namespace2@@SAXH@Z@4HC" + .p2align 2 +"?gv_func@?1??func@Class2@Namespace2@@SAXH@Z@4HC": + .long 0 # 0x0 + + .section .drectve,"yn" + .ascii " /DEFAULTLIB:libcmt.lib" + .ascii " /DEFAULTLIB:oldnames.lib" + .section .debug$S,"dr" + .p2align 2 + .long 4 # Debug section magic + .long 241 + .long .Ltmp8-.Ltmp7 # Subsection size +.Ltmp7: + .short .Ltmp10-.Ltmp9 # Record length +.Ltmp9: + .short 4353 # Record kind: S_OBJNAME + .long 0 # Signature + .asciz "/tmp/a-e5dd01.obj" # Object name + .p2align 2 +.Ltmp10: + .short .Ltmp12-.Ltmp11 # Record length +.Ltmp11: + .short 4412 # Record kind: S_COMPILE3 + .long 1 # Flags and language + .short 208 # CPUType + .short 14 # Frontend version + .short 0 + .short 0 + .short 0 + .short 14000 # Backend version + .short 0 + .short 0 + .short 0 + .asciz "clang version 14.0.0" # Null-terminated compiler version string + .p2align 2 +.Ltmp12: +.Ltmp8: + .p2align 2 + .long 246 # Inlinee lines subsection + .long .Ltmp14-.Ltmp13 # Subsection size +.Ltmp13: + .long 0 # Inlinee lines signature + + # Inlined function foo starts at ./a.h:3 + .long 4099 # Type index of inlined function + .cv_filechecksumoffset 2 # Offset into filechecksum table + .long 3 # Starting line number + + # Inlined function bar starts at ./b.h:4 + .long 4106 # Type index of inlined function + .cv_filechecksumoffset 3 # Offset into filechecksum table + .long 4 # Starting line number + + # Inlined function func starts at ./c.h:4 + .long 4113 # Type index of inlined function + .cv_filechecksumoffset 4 # Offset into filechecksum table + .long 4 # Starting line number +.Ltmp14: + .p2align 2 + .section .debug$S,"dr",associative,main + .p2align 2 + .long 4 # Debug section magic + .long 241 # Symbol subsection for main + .long .Ltmp16-.Ltmp15 # Subsection size +.Ltmp15: + .short .Ltmp18-.Ltmp17 # Record length +.Ltmp17: + .short 4423 # Record kind: S_GPROC32_ID + .long 0 # PtrParent + .long 0 # PtrEnd + .long 0 # PtrNext + .long .Lfunc_end0-main # Code size + .long 0 # Offset after prologue + .long 0 # Offset before epilogue + .long 4117 # Function type index + .secrel32 main # Function section relative address + .secidx main # Function section index + .byte 0 # Flags + .asciz "main" # Function name + .p2align 2 +.Ltmp18: + .short .Ltmp20-.Ltmp19 # Record length +.Ltmp19: + .short 4114 # Record kind: S_FRAMEPROC + .long 40 # FrameSize + .long 0 # Padding + .long 0 # Offset of padding + .long 0 # Bytes of callee saved registers + .long 0 # Exception handler offset + .short 0 # Exception handler section + .long 81920 # Flags (defines frame register) + .p2align 2 +.Ltmp20: + .short .Ltmp22-.Ltmp21 # Record length +.Ltmp21: + .short 4414 # Record kind: S_LOCAL + .long 116 # TypeIndex + .short 1 # Flags + .asciz "argc" + .p2align 2 +.Ltmp22: + .cv_def_range .Lfunc_begin0 .Ltmp5, reg, 18 + .short .Ltmp24-.Ltmp23 # Record length +.Ltmp23: + .short 4414 # Record kind: S_LOCAL + .long 4114 # TypeIndex + .short 1 # Flags + .asciz "argv" + .p2align 2 +.Ltmp24: + .cv_def_range .Lfunc_begin0 .Ltmp5, reg, 331 + .short .Ltmp26-.Ltmp25 # Record length +.Ltmp25: + .short 4365 # Record kind: S_GDATA32 + .long 4118 # Type + .secrel32 "?gv_foo@?1??foo@Namespace1@@YAXH@Z@4HC" # DataOffset + .secidx "?gv_foo@?1??foo@Namespace1@@YAXH@Z@4HC" # Segment + .asciz "Namespace1::foo::gv_foo" # Name + .p2align 2 +.Ltmp26: + .short .Ltmp28-.Ltmp27 # Record length +.Ltmp27: + .short 4365 # Record kind: S_GDATA32 + .long 4118 # Type + .secrel32 "?gv_bar@?1??bar@Class1@@SAXH@Z@4HC" # DataOffset + .secidx "?gv_bar@?1??bar@Class1@@SAXH@Z@4HC" # Segment + .asciz "Class1::bar::gv_bar" # Name + .p2align 2 +.Ltmp28: + .short .Ltmp30-.Ltmp29 # Record length +.Ltmp29: + .short 4365 # Record kind: S_GDATA32 + .long 4118 # Type + .secrel32 "?gv_func@?1??func@Class2@Namespace2@@SAXH@Z@4HC" # DataOffset + .secidx "?gv_func@?1??func@Class2@Namespace2@@SAXH@Z@4HC" # Segment + .asciz "Namespace2::Class2::func::gv_func" # Name + .p2align 2 +.Ltmp30: + .short .Ltmp32-.Ltmp31 # Record length +.Ltmp31: + .short 4429 # Record kind: S_INLINESITE + .long 0 # PtrParent + .long 0 # PtrEnd + .long 4099 # Inlinee type index + .cv_inline_linetable 1 2 3 .Lfunc_begin0 .Lfunc_end0 + .p2align 2 +.Ltmp32: + .short .Ltmp34-.Ltmp33 # Record length +.Ltmp33: + .short 4414 # Record kind: S_LOCAL + .long 116 # TypeIndex + .short 257 # Flags + .asciz "x" + .p2align 2 +.Ltmp34: + .short .Ltmp36-.Ltmp35 # Record length +.Ltmp35: + .short 4429 # Record kind: S_INLINESITE + .long 0 # PtrParent + .long 0 # PtrEnd + .long 4106 # Inlinee type index + .cv_inline_linetable 2 3 4 .Lfunc_begin0 .Lfunc_end0 + .p2align 2 +.Ltmp36: + .short .Ltmp38-.Ltmp37 # Record length +.Ltmp37: + .short 4414 # Record kind: S_LOCAL + .long 116 # TypeIndex + .short 257 # Flags + .asciz "x" + .p2align 2 +.Ltmp38: + .short .Ltmp40-.Ltmp39 # Record length +.Ltmp39: + .short 4429 # Record kind: S_INLINESITE + .long 0 # PtrParent + .long 0 # PtrEnd + .long 4113 # Inlinee type index + .cv_inline_linetable 3 4 4 .Lfunc_begin0 .Lfunc_end0 + .p2align 2 +.Ltmp40: + .short .Ltmp42-.Ltmp41 # Record length +.Ltmp41: + .short 4414 # Record kind: S_LOCAL + .long 116 # TypeIndex + .short 257 # Flags + .asciz "x" + .p2align 2 +.Ltmp42: + .short 2 # Record length + .short 4430 # Record kind: S_INLINESITE_END + .short 2 # Record length + .short 4430 # Record kind: S_INLINESITE_END + .short 2 # Record length + .short 4430 # Record kind: S_INLINESITE_END + .short 2 # Record length + .short 4431 # Record kind: S_PROC_ID_END +.Ltmp16: + .p2align 2 + .cv_linetable 0, main, .Lfunc_end0 + .section .debug$S,"dr" + .long 241 + .long .Ltmp44-.Ltmp43 # Subsection size +.Ltmp43: + .short .Ltmp46-.Ltmp45 # Record length +.Ltmp45: + .short 4360 # Record kind: S_UDT + .long 4103 # Type + .asciz "Class1" + .p2align 2 +.Ltmp46: + .short .Ltmp48-.Ltmp47 # Record length +.Ltmp47: + .short 4360 # Record kind: S_UDT + .long 4110 # Type + .asciz "Namespace2::Class2" + .p2align 2 +.Ltmp48: +.Ltmp44: + .p2align 2 + .cv_filechecksums # File index to string table offset subsection + .cv_stringtable # String table + .long 241 + .long .Ltmp50-.Ltmp49 # Subsection size +.Ltmp49: + .short .Ltmp52-.Ltmp51 # Record length +.Ltmp51: + .short 4428 # Record kind: S_BUILDINFO + .long 4121 # LF_BUILDINFO index + .p2align 2 +.Ltmp52: +.Ltmp50: + .p2align 2 + .section .debug$T,"dr" + .p2align 2 + .long 4 # Debug section magic + # StringId (0x1000) + .short 0x12 # Record length + .short 0x1605 # Record kind: LF_STRING_ID + .long 0x0 # Id + .asciz "Namespace1" # StringData + .byte 241 + # ArgList (0x1001) + .short 0xa # Record length + .short 0x1201 # Record kind: LF_ARGLIST + .long 0x1 # NumArgs + .long 0x74 # Argument: int + # Procedure (0x1002) + .short 0xe # Record length + .short 0x1008 # Record kind: LF_PROCEDURE + .long 0x3 # ReturnType: void + .byte 0x0 # CallingConvention: NearC + .byte 0x0 # FunctionOptions + .short 0x1 # NumParameters + .long 0x1001 # ArgListType: (int) + # FuncId (0x1003) + .short 0xe # Record length + .short 0x1601 # Record kind: LF_FUNC_ID + .long 0x1000 # ParentScope: Namespace1 + .long 0x1002 # FunctionType: void (int) + .asciz "foo" # Name + # Class (0x1004) + .short 0x2a # Record length + .short 0x1504 # Record kind: LF_CLASS + .short 0x0 # MemberCount + .short 0x280 # Properties ( ForwardReference (0x80) | HasUniqueName (0x200) ) + .long 0x0 # FieldList + .long 0x0 # DerivedFrom + .long 0x0 # VShape + .short 0x0 # SizeOf + .asciz "Class1" # Name + .asciz ".?AVClass1@@" # LinkageName + .byte 242 + .byte 241 + # MemberFunction (0x1005) + .short 0x1a # Record length + .short 0x1009 # Record kind: LF_MFUNCTION + .long 0x3 # ReturnType: void + .long 0x1004 # ClassType: Class1 + .long 0x0 # ThisType + .byte 0x0 # CallingConvention: NearC + .byte 0x0 # FunctionOptions + .short 0x1 # NumParameters + .long 0x1001 # ArgListType: (int) + .long 0x0 # ThisAdjustment + # FieldList (0x1006) + .short 0xe # Record length + .short 0x1203 # Record kind: LF_FIELDLIST + .short 0x1511 # Member kind: OneMethod ( LF_ONEMETHOD ) + .short 0xb # Attrs: Public, Static + .long 0x1005 # Type: void Class1::(int) + .asciz "bar" # Name + # Class (0x1007) + .short 0x2a # Record length + .short 0x1504 # Record kind: LF_CLASS + .short 0x1 # MemberCount + .short 0x200 # Properties ( HasUniqueName (0x200) ) + .long 0x1006 # FieldList: + .long 0x0 # DerivedFrom + .long 0x0 # VShape + .short 0x1 # SizeOf + .asciz "Class1" # Name + .asciz ".?AVClass1@@" # LinkageName + .byte 242 + .byte 241 + # StringId (0x1008) + .short 0x12 # Record length + .short 0x1605 # Record kind: LF_STRING_ID + .long 0x0 # Id + .asciz "/tmp/./b.h" # StringData + .byte 241 + # UdtSourceLine (0x1009) + .short 0xe # Record length + .short 0x1606 # Record kind: LF_UDT_SRC_LINE + .long 0x1007 # UDT: Class1 + .long 0x1008 # SourceFile: /tmp/./b.h + .long 0x2 # LineNumber + # MemberFuncId (0x100A) + .short 0xe # Record length + .short 0x1602 # Record kind: LF_MFUNC_ID + .long 0x1004 # ClassType: Class1 + .long 0x1005 # FunctionType: void Class1::(int) + .asciz "bar" # Name + # Class (0x100B) + .short 0x42 # Record length + .short 0x1504 # Record kind: LF_CLASS + .short 0x0 # MemberCount + .short 0x280 # Properties ( ForwardReference (0x80) | HasUniqueName (0x200) ) + .long 0x0 # FieldList + .long 0x0 # DerivedFrom + .long 0x0 # VShape + .short 0x0 # SizeOf + .asciz "Namespace2::Class2" # Name + .asciz ".?AVClass2@Namespace2@@" # LinkageName + .byte 243 + .byte 242 + .byte 241 + # MemberFunction (0x100C) + .short 0x1a # Record length + .short 0x1009 # Record kind: LF_MFUNCTION + .long 0x3 # ReturnType: void + .long 0x100b # ClassType: Namespace2::Class2 + .long 0x0 # ThisType + .byte 0x0 # CallingConvention: NearC + .byte 0x0 # FunctionOptions + .short 0x1 # NumParameters + .long 0x1001 # ArgListType: (int) + .long 0x0 # ThisAdjustment + # FieldList (0x100D) + .short 0x12 # Record length + .short 0x1203 # Record kind: LF_FIELDLIST + .short 0x1511 # Member kind: OneMethod ( LF_ONEMETHOD ) + .short 0xb # Attrs: Public, Static + .long 0x100c # Type: void Namespace2::Class2::(int) + .asciz "func" # Name + .byte 243 + .byte 242 + .byte 241 + # Class (0x100E) + .short 0x42 # Record length + .short 0x1504 # Record kind: LF_CLASS + .short 0x1 # MemberCount + .short 0x200 # Properties ( HasUniqueName (0x200) ) + .long 0x100d # FieldList: + .long 0x0 # DerivedFrom + .long 0x0 # VShape + .short 0x1 # SizeOf + .asciz "Namespace2::Class2" # Name + .asciz ".?AVClass2@Namespace2@@" # LinkageName + .byte 243 + .byte 242 + .byte 241 + # StringId (0x100F) + .short 0x12 # Record length + .short 0x1605 # Record kind: LF_STRING_ID + .long 0x0 # Id + .asciz "/tmp/./c.h" # StringData + .byte 241 + # UdtSourceLine (0x1010) + .short 0xe # Record length + .short 0x1606 # Record kind: LF_UDT_SRC_LINE + .long 0x100e # UDT: Namespace2::Class2 + .long 0x100f # SourceFile: /tmp/./c.h + .long 0x2 # LineNumber + # MemberFuncId (0x1011) + .short 0x12 # Record length + .short 0x1602 # Record kind: LF_MFUNC_ID + .long 0x100b # ClassType: Namespace2::Class2 + .long 0x100c # FunctionType: void Namespace2::Class2::(int) + .asciz "func" # Name + .byte 243 + .byte 242 + .byte 241 + # Pointer (0x1012) + .short 0xa # Record length + .short 0x1002 # Record kind: LF_POINTER + .long 0x670 # PointeeType: char* + .long 0x1000c # Attrs: [ Type: Near64, Mode: Pointer, SizeOf: 8 ] + # ArgList (0x1013) + .short 0xe # Record length + .short 0x1201 # Record kind: LF_ARGLIST + .long 0x2 # NumArgs + .long 0x74 # Argument: int + .long 0x1012 # Argument: char** + # Procedure (0x1014) + .short 0xe # Record length + .short 0x1008 # Record kind: LF_PROCEDURE + .long 0x74 # ReturnType: int + .byte 0x0 # CallingConvention: NearC + .byte 0x0 # FunctionOptions + .short 0x2 # NumParameters + .long 0x1013 # ArgListType: (int, char**) + # FuncId (0x1015) + .short 0x12 # Record length + .short 0x1601 # Record kind: LF_FUNC_ID + .long 0x0 # ParentScope + .long 0x1014 # FunctionType: int (int, char**) + .asciz "main" # Name + .byte 243 + .byte 242 + .byte 241 + # Modifier (0x1016) + .short 0xa # Record length + .short 0x1001 # Record kind: LF_MODIFIER + .long 0x74 # ModifiedType: int + .short 0x2 # Modifiers ( Volatile (0x2) ) + .byte 242 + .byte 241 + # StringId (0x1017) + .short 0xe # Record length + .short 0x1605 # Record kind: LF_STRING_ID + .long 0x0 # Id + .asciz "/tmp" # StringData + .byte 243 + .byte 242 + .byte 241 + # StringId (0x1018) + .short 0xe # Record length + .short 0x1605 # Record kind: LF_STRING_ID + .long 0x0 # Id + .asciz "a.cpp" # StringData + .byte 242 + .byte 241 + # BuildInfo (0x1019) + .short 0x1a # Record length + .short 0x1603 # Record kind: LF_BUILDINFO + .short 0x5 # NumArgs + .long 0x1017 # Argument: /tmp + .long 0x0 # Argument + .long 0x1018 # Argument: a.cpp + .long 0x0 # Argument + .long 0x0 # Argument + .byte 242 + .byte 241 + .addrsig + .addrsig_sym "?gv_foo@?1??foo@Namespace1@@YAXH@Z@4HC" + .addrsig_sym "?gv_bar@?1??bar@Class1@@SAXH@Z@4HC" + .addrsig_sym "?gv_func@?1??func@Class2@Namespace2@@SAXH@Z@4HC"