Index: source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp =================================================================== --- source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp +++ source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp @@ -1571,6 +1571,9 @@ line_table->AppendLineEntryToSequence( sequence.get(), prev_addr + prev_length, prev_line, 0, prev_source_idx, false, false, false, false, true); + + line_table->InsertSequence(sequence.release()); + sequence.reset(line_table->CreateLineSequenceContainer()); } if (ShouldAddLine(match_line, lno, length)) { Index: tools/lldb-test/lldb-test.cpp =================================================================== --- tools/lldb-test/lldb-test.cpp +++ tools/lldb-test/lldb-test.cpp @@ -21,6 +21,8 @@ #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/ClangASTImporter.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/LineTable.h" #include "lldb/Symbol/SymbolVendor.h" #include "lldb/Symbol/TypeList.h" #include "lldb/Symbol/VariableList.h" @@ -143,6 +145,9 @@ return Result; } +static cl::opt Verify("verify", cl::desc("Verify symbol information."), + cl::sub(SymbolsSubcommand)); + static Expected getDeclContext(SymbolVendor &Vendor); static Error findFunctions(lldb_private::Module &Module); @@ -150,6 +155,7 @@ static Error findTypes(lldb_private::Module &Module); static Error findVariables(lldb_private::Module &Module); static Error dumpModule(lldb_private::Module &Module); +static Error verify(lldb_private::Module &Module); static int dumpSymbols(Debugger &Dbg); } @@ -412,7 +418,75 @@ return Error::success(); } +Error opts::symbols::verify(lldb_private::Module &Module) { + SymbolVendor *plugin = Module.GetSymbolVendor(); + if (!plugin) + return make_error("Can't get a symbol vendor.", + inconvertibleErrorCode()); + + SymbolFile *symfile = plugin->GetSymbolFile(); + if (!symfile) + return make_error("Can't get a symbol file.", + inconvertibleErrorCode()); + + uint32_t comp_units_count = symfile->GetNumCompileUnits(); + + outs() << "Found " << comp_units_count << " compile units.\n"; + + for (uint32_t i = 0; i < comp_units_count; i++) { + lldb::CompUnitSP comp_unit = symfile->ParseCompileUnitAtIndex(i); + if (!comp_unit) + return make_error("Can't get a compile unit.", + inconvertibleErrorCode()); + + outs() << "Processing '" << comp_unit->GetFilename().AsCString() << + "' compile unit.\n"; + + LineTable *lt = comp_unit->GetLineTable(); + if (!lt) + return make_error( + "Can't get a line table of a compile unit.", + inconvertibleErrorCode()); + + uint32_t count = lt->GetSize(); + + outs() << "The line table contains " << count << " entries.\n"; + + if (count == 0) + continue; + + LineEntry le; + if (!lt->GetLineEntryAtIndex(0, le)) + return make_error( + "Can't get a line entry of a compile unit", + inconvertibleErrorCode()); + + for (uint32_t i = 1; i < count; i++) { + lldb::addr_t curr_end = + le.range.GetBaseAddress().GetFileAddress() + le.range.GetByteSize(); + + if (!lt->GetLineEntryAtIndex(i, le)) + return make_error( + "Can't get a line entry of a compile unit", + inconvertibleErrorCode()); + + if (curr_end > le.range.GetBaseAddress().GetFileAddress()) + return make_error( + "Line table of a compile unit is inconsistent", + inconvertibleErrorCode()); + } + } + + outs() << "The symbol information is verified.\n"; + + return Error::success(); +} + int opts::symbols::dumpSymbols(Debugger &Dbg) { + if (Verify && Find != FindType::None) { + WithColor::error() << "Cannot both search and verify symbol information.\n"; + return 1; + } if (Find != FindType::None && Regex && !Context.empty()) { WithColor::error() << "Cannot search using both regular expressions and context.\n"; @@ -435,23 +509,26 @@ } Error (*Action)(lldb_private::Module &); - switch (Find) { - case FindType::Function: - Action = findFunctions; - break; - case FindType::Namespace: - Action = findNamespaces; - break; - case FindType::Type: - Action = findTypes; - break; - case FindType::Variable: - Action = findVariables; - break; - case FindType::None: - Action = dumpModule; - break; - } + if (!Verify) { + switch (Find) { + case FindType::Function: + Action = findFunctions; + break; + case FindType::Namespace: + Action = findNamespaces; + break; + case FindType::Type: + Action = findTypes; + break; + case FindType::Variable: + Action = findVariables; + break; + case FindType::None: + Action = dumpModule; + break; + } + } else + Action = verify; int HadErrors = 0; for (const auto &File : InputFilenames) { Index: unittests/SymbolFile/PDB/CMakeLists.txt =================================================================== --- unittests/SymbolFile/PDB/CMakeLists.txt +++ unittests/SymbolFile/PDB/CMakeLists.txt @@ -17,6 +17,10 @@ set(test_inputs test-pdb.exe test-pdb.pdb + test-pdb-function-level-linking.exe + test-pdb-function-level-linking.pdb + test-pdb-splitted-function.exe + test-pdb-splitted-function.pdb test-pdb-types.exe test-pdb-types.pdb) Index: unittests/SymbolFile/PDB/Inputs/test-pdb-function-level-linking.h =================================================================== --- /dev/null +++ unittests/SymbolFile/PDB/Inputs/test-pdb-function-level-linking.h @@ -0,0 +1,12 @@ +#ifndef TEST_PDB_FUNCTION_LEVEL_LINKING_H +#define TEST_PDB_FUNCTION_LEVEL_LINKING_H + +int bar() { + return 0; +} + +int baz() { + return 0; +} + +#endif Index: unittests/SymbolFile/PDB/Inputs/test-pdb-function-level-linking.cpp =================================================================== --- /dev/null +++ unittests/SymbolFile/PDB/Inputs/test-pdb-function-level-linking.cpp @@ -0,0 +1,14 @@ +// Compile with "cl /c /Zi /Gy test-pdb-function-level-linking.cpp" +// Link with "link /debug:full /nodefaultlib /entry:main +// /order:@test-pdb-function-level-linking.ord +// test-pdb-function-level-linking.obj" + +#include "test-pdb-function-level-linking.h" + +int foo() { + return 0; +} + +int main() { + return foo() + bar() + baz(); +} Index: unittests/SymbolFile/PDB/Inputs/test-pdb-function-level-linking.ord =================================================================== --- /dev/null +++ unittests/SymbolFile/PDB/Inputs/test-pdb-function-level-linking.ord @@ -0,0 +1,4 @@ +?foo@@YAHXZ +?bar@@YAHXZ +main +?baz@@YAHXZ Index: unittests/SymbolFile/PDB/Inputs/test-pdb-splitted-function.cpp =================================================================== --- /dev/null +++ unittests/SymbolFile/PDB/Inputs/test-pdb-splitted-function.cpp @@ -0,0 +1,24 @@ +// Compile with "cl /c /Zi /GL /O2 /EHsc /MTd test-pdb-splitted-function.cpp" +// Link with "link /debug:full /LTCG /GENPROFILE +// test-pdb-splitted-function.obj" +// Run several times +// Link with "link /debug:full /LTCG /USEPROFILE +// test-pdb-splitted-function.obj" + +#include +#include + +int main() { + auto b = false; + for (auto i = 1; i <= 1024; i++) { + if (b) { + std::cout << "Unreachable code" << std::endl; + auto x = std::sin(i); + return x; + } + + b = (i % 2 + (i - 1) % 2) != 1; + } + + return 0; +} Index: unittests/SymbolFile/PDB/SymbolFilePDBTests.cpp =================================================================== --- unittests/SymbolFile/PDB/SymbolFilePDBTests.cpp +++ unittests/SymbolFile/PDB/SymbolFilePDBTests.cpp @@ -56,6 +56,10 @@ SymbolFilePDB::Initialize(); m_pdb_test_exe = GetInputFilePath("test-pdb.exe"); + m_function_level_linking_test_exe = + GetInputFilePath("test-pdb-function-level-linking.exe"); + m_splitted_function_test_exe = + GetInputFilePath("test-pdb-splitted-function.exe"); m_types_test_exe = GetInputFilePath("test-pdb-types.exe"); } @@ -73,6 +77,8 @@ protected: std::string m_pdb_test_exe; + std::string m_function_level_linking_test_exe; + std::string m_splitted_function_test_exe; std::string m_types_test_exe; bool FileSpecMatchesAsBaseOrFull(const FileSpec &left, @@ -354,6 +360,56 @@ VerifyLineEntry(module, sc, header1, *lt, 9, 0x401090); } +void TestLineTableConsistency(llvm::StringRef exe_path, llvm::StringRef source_name) +{ + // All line entries of compile unit's line table must be consistent + // even if compiled sources are not continuous in the binary file. + FileSpec fspec(exe_path, false); + ArchSpec aspec("i686-pc-windows"); + lldb::ModuleSP module = std::make_shared(fspec, aspec); + SymbolVendor *plugin = module->GetSymbolVendor(); + SymbolFile *symfile = plugin->GetSymbolFile(); + FileSpec source_file(source_name, false); + uint32_t scope = lldb::eSymbolContextCompUnit | lldb::eSymbolContextLineEntry; + SymbolContextList sc_list; + uint32_t count = + symfile->ResolveSymbolContext(source_file, 0, true, scope, sc_list); + EXPECT_EQ(1u, count); + + SymbolContext sc; + EXPECT_TRUE(sc_list.GetContextAtIndex(0, sc)); + + LineTable *lt = sc.comp_unit->GetLineTable(); + EXPECT_NE(nullptr, lt); + + count = lt->GetSize(); + EXPECT_LT(0u, count); + + LineEntry le; + EXPECT_TRUE(lt->GetLineEntryAtIndex(0, le)); + for (int i = 1; i < count; i++) + { + lldb::addr_t curr_end = + le.range.GetBaseAddress().GetFileAddress() + le.range.GetByteSize(); + + EXPECT_TRUE(lt->GetLineEntryAtIndex(i, le)); + + EXPECT_LE(curr_end, le.range.GetBaseAddress().GetFileAddress()); + } +} + +TEST_F(SymbolFilePDBTests, TestFunctionLevelLinking) { + TestLineTableConsistency( + m_function_level_linking_test_exe, + "test-pdb-function-level-linking.cpp"); +} + +TEST_F(SymbolFilePDBTests, TestSplittedFunction) { + TestLineTableConsistency( + m_splitted_function_test_exe, + "test-pdb-splitted-function.cpp"); +} + TEST_F(SymbolFilePDBTests, TestSimpleClassTypes) { FileSpec fspec(m_types_test_exe.c_str(), false); ArchSpec aspec("i686-pc-windows");