549 changes: 549 additions & 0 deletions lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp

Large diffs are not rendered by default.

190 changes: 190 additions & 0 deletions lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
//===-- SymbolFilePDB.h -------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#ifndef lldb_Plugins_SymbolFile_PDB_SymbolFilePDB_h_
#define lldb_Plugins_SymbolFile_PDB_SymbolFilePDB_h_

#include <unordered_map>

#include "lldb/Core/UserID.h"
#include "lldb/Symbol/SymbolFile.h"

#include "llvm/DebugInfo/PDB/IPDBSession.h"
#include "llvm/DebugInfo/PDB/PDB.h"

class SymbolFilePDB : public lldb_private::SymbolFile
{
public:
//------------------------------------------------------------------
// Static Functions
//------------------------------------------------------------------
static void
Initialize();

static void
Terminate();

static void
DebuggerInitialize(lldb_private::Debugger &debugger);

static lldb_private::ConstString
GetPluginNameStatic();

static const char *
GetPluginDescriptionStatic();

static lldb_private::SymbolFile *
CreateInstance(lldb_private::ObjectFile *obj_file);

//------------------------------------------------------------------
// Constructors and Destructors
//------------------------------------------------------------------
SymbolFilePDB(lldb_private::ObjectFile *ofile);

~SymbolFilePDB() override;

uint32_t
CalculateAbilities() override;

void
InitializeObject() override;

//------------------------------------------------------------------
// Compile Unit function calls
//------------------------------------------------------------------

uint32_t
GetNumCompileUnits() override;

lldb::CompUnitSP
ParseCompileUnitAtIndex(uint32_t index) override;

lldb::LanguageType
ParseCompileUnitLanguage(const lldb_private::SymbolContext &sc) override;

size_t
ParseCompileUnitFunctions(const lldb_private::SymbolContext &sc) override;

bool
ParseCompileUnitLineTable(const lldb_private::SymbolContext &sc) override;

bool
ParseCompileUnitDebugMacros(const lldb_private::SymbolContext &sc) override;

bool
ParseCompileUnitSupportFiles(const lldb_private::SymbolContext &sc,
lldb_private::FileSpecList &support_files) override;

bool
ParseImportedModules(const lldb_private::SymbolContext &sc,
std::vector<lldb_private::ConstString> &imported_modules) override;

size_t
ParseFunctionBlocks(const lldb_private::SymbolContext &sc) override;

size_t
ParseTypes(const lldb_private::SymbolContext &sc) override;

size_t
ParseVariablesForContext(const lldb_private::SymbolContext &sc) override;

lldb_private::Type *
ResolveTypeUID(lldb::user_id_t type_uid) override;

bool
CompleteType(lldb_private::CompilerType &compiler_type) override;

lldb_private::CompilerDecl
GetDeclForUID(lldb::user_id_t uid) override;

lldb_private::CompilerDeclContext
GetDeclContextForUID(lldb::user_id_t uid) override;

lldb_private::CompilerDeclContext
GetDeclContextContainingUID(lldb::user_id_t uid) override;

void
ParseDeclsForContext(lldb_private::CompilerDeclContext decl_ctx) override;

uint32_t
ResolveSymbolContext(const lldb_private::Address &so_addr, uint32_t resolve_scope,
lldb_private::SymbolContext &sc) override;

uint32_t
ResolveSymbolContext(const lldb_private::FileSpec &file_spec, uint32_t line, bool check_inlines,
uint32_t resolve_scope, lldb_private::SymbolContextList &sc_list) override;

uint32_t
FindGlobalVariables(const lldb_private::ConstString &name, const lldb_private::CompilerDeclContext *parent_decl_ctx,
bool append, uint32_t max_matches, lldb_private::VariableList &variables) override;

uint32_t
FindGlobalVariables(const lldb_private::RegularExpression &regex, bool append, uint32_t max_matches,
lldb_private::VariableList &variables) override;

uint32_t
FindFunctions(const lldb_private::ConstString &name, const lldb_private::CompilerDeclContext *parent_decl_ctx,
uint32_t name_type_mask, bool include_inlines, bool append,
lldb_private::SymbolContextList &sc_list) override;

uint32_t
FindFunctions(const lldb_private::RegularExpression &regex, bool include_inlines, bool append,
lldb_private::SymbolContextList &sc_list) override;

void
GetMangledNamesForFunction(const std::string &scope_qualified_name,
std::vector<lldb_private::ConstString> &mangled_names) override;

uint32_t
FindTypes(const lldb_private::SymbolContext &sc, const lldb_private::ConstString &name,
const lldb_private::CompilerDeclContext *parent_decl_ctx, bool append, uint32_t max_matches,
llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files, lldb_private::TypeMap &types) override;

size_t
FindTypes(const std::vector<lldb_private::CompilerContext> &context, bool append,
lldb_private::TypeMap &types) override;

lldb_private::TypeList *
GetTypeList() override;

size_t
GetTypes(lldb_private::SymbolContextScope *sc_scope, uint32_t type_mask,
lldb_private::TypeList &type_list) override;

lldb_private::TypeSystem *
GetTypeSystemForLanguage(lldb::LanguageType language) override;

lldb_private::CompilerDeclContext
FindNamespace(const lldb_private::SymbolContext &sc, const lldb_private::ConstString &name,
const lldb_private::CompilerDeclContext *parent_decl_ctx) override;

lldb_private::ConstString
GetPluginName() override;

uint32_t
GetPluginVersion() override;

private:
lldb::CompUnitSP
ParseCompileUnitForSymIndex(uint32_t id);

bool
ParseCompileUnitLineTable(const lldb_private::SymbolContext &sc, uint32_t match_line);

void
BuildSupportFileIdToSupportFileIndexMap(const llvm::PDBSymbolCompiland &cu,
std::unordered_map<uint32_t, uint32_t> &index_map) const;

std::unordered_map<uint32_t, lldb::CompUnitSP> m_comp_units;

std::unique_ptr<llvm::IPDBSession> m_session_up;
uint32_t m_cached_compile_unit_count;
};

#endif // lldb_Plugins_SymbolFile_PDB_SymbolFilePDB_h_
16 changes: 16 additions & 0 deletions lldb/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,31 @@ function(add_lldb_unittest test_name)
${ARGN}
)

add_custom_command(
TARGET ${test_name}
POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/Inputs)

lldb_link_common_libs(${test_name} EXE)
target_link_libraries(${test_name} ${CLANG_USED_LIBS} ${LLDB_SYSTEM_LIBS})
llvm_config(${test_name} ${LLVM_LINK_COMPONENTS})
endfunction()

function(add_unittest_inputs test_name inputs)
foreach (INPUT ${inputs})
add_custom_command(
TARGET ${test_name}
POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy ${CMAKE_CURRENT_SOURCE_DIR}/Inputs/${INPUT} ${CMAKE_CURRENT_BINARY_DIR}/Inputs
COMMENT "Copying ${INPUT} to binary directory.")
endforeach()
endfunction()

add_subdirectory(Core)
add_subdirectory(Editline)
add_subdirectory(Expression)
add_subdirectory(Host)
add_subdirectory(Interpreter)
add_subdirectory(ScriptInterpreter)
add_subdirectory(SymbolFile)
add_subdirectory(Utility)
1 change: 1 addition & 0 deletions lldb/unittests/SymbolFile/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
add_subdirectory(PDB)
10 changes: 10 additions & 0 deletions lldb/unittests/SymbolFile/PDB/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
add_lldb_unittest(SymbolFilePDBTests
SymbolFilePDBTests.cpp
)

set(test_inputs
test-pdb.exe
test-pdb.pdb
test-dwarf.exe)

add_unittest_inputs(SymbolFilePDBTests "${test_inputs}")
14 changes: 14 additions & 0 deletions lldb/unittests/SymbolFile/PDB/Inputs/test-dwarf.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Compile with "cl /c /Zi /GR- test.cpp"
// Link with "link test.obj /debug /nodefaultlib /entry:main /out:test.exe"

int __cdecl _purecall(void)
{
return 0;
}

int
main(int argc, char **argv)
{

return 0;
}
Binary file not shown.
9 changes: 9 additions & 0 deletions lldb/unittests/SymbolFile/PDB/Inputs/test-pdb-alt.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Compile with "cl /c /Zi /GR- test-pdb-alt.cpp"
// Link with "link test-pdb.obj test-pdb-alt.obj /debug /nodefaultlib /entry:main /out:test-pdb.exe"

#include "test-pdb.h"

int bar(int n)
{
return n-1;
}
9 changes: 9 additions & 0 deletions lldb/unittests/SymbolFile/PDB/Inputs/test-pdb-nested.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#ifndef TEST_PDB_NESTED_H
#define TEST_PDB_NESTED_H

inline int baz(int n)
{
return n+1;
}

#endif
15 changes: 15 additions & 0 deletions lldb/unittests/SymbolFile/PDB/Inputs/test-pdb.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Compile with "cl /c /Zi /GR- test-pdb.cpp"
// Link with "link test-pdb.obj /debug /nodefaultlib /entry:main /out:test-pdb.exe"

#include "test-pdb.h"

int __cdecl _purecall(void)
{
return 0;
}

int
main(int argc, char **argv)
{
return foo(argc) + bar(argc);
}
Binary file not shown.
13 changes: 13 additions & 0 deletions lldb/unittests/SymbolFile/PDB/Inputs/test-pdb.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#ifndef TEST_PDB_H
#define TEST_PDB_H

#include "test-pdb-nested.h"

int bar(int n);

inline int foo(int n)
{
return baz(n)+1;
}

#endif
Binary file not shown.
344 changes: 344 additions & 0 deletions lldb/unittests/SymbolFile/PDB/SymbolFilePDBTests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,344 @@
//===-- PythonDataObjectsTests.cpp ------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "gtest/gtest.h"

#include "llvm/ADT/STLExtras.h"
#include "llvm/Config/config.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"

#include "lldb/Core/Address.h"
#include "lldb/Core/ArchSpec.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Host/FileSpec.h"
#include "lldb/Host/HostInfo.h"
#include "lldb/Symbol/CompileUnit.h"
#include "lldb/Symbol/LineTable.h"
#include "lldb/Symbol/SymbolVendor.h"

#include "Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h"
#include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h"
#include "Plugins/SymbolFile/PDB/SymbolFilePDB.h"

#if defined(_MSC_VER)
#include <objbase.h>
#endif

extern const char *TestMainArgv0;

using namespace lldb_private;

class SymbolFilePDBTests : public testing::Test
{
public:
void
SetUp() override
{
#if defined(_MSC_VER)
::CoInitializeEx(nullptr, COINIT_MULTITHREADED);
#endif

HostInfoBase::Initialize();
ObjectFilePECOFF::Initialize();
SymbolFileDWARF::Initialize();
SymbolFilePDB::Initialize();

llvm::StringRef exe_folder = llvm::sys::path::parent_path(TestMainArgv0);
llvm::SmallString<128> inputs_folder = exe_folder;
llvm::sys::path::append(inputs_folder, "Inputs");

m_pdb_test_exe = inputs_folder;
m_dwarf_test_exe = inputs_folder;
llvm::sys::path::append(m_pdb_test_exe, "test-pdb.exe");
llvm::sys::path::append(m_dwarf_test_exe, "test-dwarf.exe");
}

void
TearDown() override
{
#if defined(_MSC_VER)
::CoUninitialize();
#endif
SymbolFilePDB::Terminate();
SymbolFileDWARF::Terminate();
ObjectFilePECOFF::Terminate();
}

protected:
llvm::SmallString<128> m_pdb_test_exe;
llvm::SmallString<128> m_dwarf_test_exe;

bool
FileSpecMatchesAsBaseOrFull(const FileSpec &left, const FileSpec &right) const
{
// If the filenames don't match, the paths can't be equal
if (!left.FileEquals(right))
return false;
// If BOTH have a directory, also compare the directories.
if (left.GetDirectory() && right.GetDirectory())
return left.DirectoryEquals(right);

// If one has a directory but not the other, they match.
return true;
}

void
VerifyLineEntry(lldb::ModuleSP module, const SymbolContext &sc, const FileSpec &spec, LineTable &lt, uint32_t line,
lldb::addr_t addr)
{
LineEntry entry;
Address address;
EXPECT_TRUE(module->ResolveFileAddress(addr, address));

EXPECT_TRUE(lt.FindLineEntryByAddress(address, entry));
EXPECT_EQ(line, entry.line);
EXPECT_EQ(address, entry.range.GetBaseAddress());

EXPECT_TRUE(FileSpecMatchesAsBaseOrFull(spec, entry.file));
}

bool
ContainsCompileUnit(const SymbolContextList &sc_list, const FileSpec &spec) const
{
for (int i = 0; i < sc_list.GetSize(); ++i)
{
const SymbolContext &sc = sc_list[i];
if (FileSpecMatchesAsBaseOrFull(*sc.comp_unit, spec))
return true;
}
return false;
}
};

#if defined(HAVE_DIA_SDK)
#define REQUIRES_DIA_SDK(TestName) TestName
#else
#define REQUIRES_DIA_SDK(TestName) DISABLED_##TestName
#endif

TEST_F(SymbolFilePDBTests, TestAbilitiesForDWARF)
{
// Test that when we have Dwarf debug info, SymbolFileDWARF is used.
FileSpec fspec(m_dwarf_test_exe.c_str(), false);
ArchSpec aspec("i686-pc-windows");
lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec);

SymbolVendor *plugin = module->GetSymbolVendor();
EXPECT_NE(nullptr, plugin);
SymbolFile *symfile = plugin->GetSymbolFile();
EXPECT_NE(nullptr, symfile);
EXPECT_EQ(symfile->GetPluginName(), SymbolFileDWARF::GetPluginNameStatic());

uint32_t expected_abilities = SymbolFile::kAllAbilities;
EXPECT_EQ(expected_abilities, symfile->CalculateAbilities());
}

TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestAbilitiesForPDB))
{
// Test that when we have PDB debug info, SymbolFilePDB is used.
FileSpec fspec(m_pdb_test_exe.c_str(), false);
ArchSpec aspec("i686-pc-windows");
lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec);

SymbolVendor *plugin = module->GetSymbolVendor();
EXPECT_NE(nullptr, plugin);
SymbolFile *symfile = plugin->GetSymbolFile();
EXPECT_NE(nullptr, symfile);
EXPECT_EQ(symfile->GetPluginName(), SymbolFilePDB::GetPluginNameStatic());

uint32_t expected_abilities = SymbolFile::CompileUnits | SymbolFile::LineTables;
EXPECT_EQ(expected_abilities, symfile->CalculateAbilities());
}

TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestResolveSymbolContextBasename))
{
// Test that attempting to call ResolveSymbolContext with only a basename finds all full paths
// with the same basename
FileSpec fspec(m_pdb_test_exe.c_str(), false);
ArchSpec aspec("i686-pc-windows");
lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec);

SymbolVendor *plugin = module->GetSymbolVendor();
EXPECT_NE(nullptr, plugin);
SymbolFile *symfile = plugin->GetSymbolFile();

FileSpec header_spec("test-pdb.cpp", false);
SymbolContextList sc_list;
uint32_t result_count = symfile->ResolveSymbolContext(header_spec, 0, false, lldb::eSymbolContextCompUnit, sc_list);
EXPECT_EQ(1, result_count);
EXPECT_TRUE(ContainsCompileUnit(sc_list, header_spec));
}

TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestResolveSymbolContextFullPath))
{
// Test that attempting to call ResolveSymbolContext with a full path only finds the one source
// file that matches the full path.
FileSpec fspec(m_pdb_test_exe.c_str(), false);
ArchSpec aspec("i686-pc-windows");
lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec);

SymbolVendor *plugin = module->GetSymbolVendor();
EXPECT_NE(nullptr, plugin);
SymbolFile *symfile = plugin->GetSymbolFile();

FileSpec header_spec(R"spec(D:\src\llvm\tools\lldb\unittests\SymbolFile\PDB\Inputs\test-pdb.cpp)spec", false);
SymbolContextList sc_list;
uint32_t result_count = symfile->ResolveSymbolContext(header_spec, 0, false, lldb::eSymbolContextCompUnit, sc_list);
EXPECT_GE(1, result_count);
EXPECT_TRUE(ContainsCompileUnit(sc_list, header_spec));
}

TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestLookupOfHeaderFileWithInlines))
{
// Test that when looking up a header file via ResolveSymbolContext (i.e. a file that was not by itself
// compiled, but only contributes to the combined code of other source files), a SymbolContext is returned
// for each compiland which has line contributions from the requested header.
FileSpec fspec(m_pdb_test_exe.c_str(), false);
ArchSpec aspec("i686-pc-windows");
lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec);

SymbolVendor *plugin = module->GetSymbolVendor();
EXPECT_NE(nullptr, plugin);
SymbolFile *symfile = plugin->GetSymbolFile();

FileSpec header_specs[] = {FileSpec("test-pdb.h", false), FileSpec("test-pdb-nested.h", false)};
FileSpec main_cpp_spec("test-pdb.cpp", false);
FileSpec alt_cpp_spec("test-pdb-alt.cpp", false);
for (const auto &hspec : header_specs)
{
SymbolContextList sc_list;
uint32_t result_count = symfile->ResolveSymbolContext(hspec, 0, true, lldb::eSymbolContextCompUnit, sc_list);
EXPECT_EQ(2, result_count);
EXPECT_TRUE(ContainsCompileUnit(sc_list, main_cpp_spec));
EXPECT_TRUE(ContainsCompileUnit(sc_list, alt_cpp_spec));
}
}

TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestLookupOfHeaderFileWithNoInlines))
{
// Test that when looking up a header file via ResolveSymbolContext (i.e. a file that was not by itself
// compiled, but only contributes to the combined code of other source files), that if check_inlines
// is false, no SymbolContexts are returned.
FileSpec fspec(m_pdb_test_exe.c_str(), false);
ArchSpec aspec("i686-pc-windows");
lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec);

SymbolVendor *plugin = module->GetSymbolVendor();
EXPECT_NE(nullptr, plugin);
SymbolFile *symfile = plugin->GetSymbolFile();

FileSpec header_specs[] = {FileSpec("test-pdb.h", false), FileSpec("test-pdb-nested.h", false)};
for (const auto &hspec : header_specs)
{
SymbolContextList sc_list;
uint32_t result_count = symfile->ResolveSymbolContext(hspec, 0, false, lldb::eSymbolContextCompUnit, sc_list);
EXPECT_EQ(0, result_count);
}
}

TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestLineTablesMatchAll))
{
// Test that when calling ResolveSymbolContext with a line number of 0, all line entries from
// the specified files are returned.
FileSpec fspec(m_pdb_test_exe.c_str(), false);
ArchSpec aspec("i686-pc-windows");
lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec);

SymbolVendor *plugin = module->GetSymbolVendor();
SymbolFile *symfile = plugin->GetSymbolFile();

FileSpec source_file("test-pdb.cpp", false);
FileSpec header1("test-pdb.h", false);
FileSpec header2("test-pdb-nested.h", false);
uint32_t cus = symfile->GetNumCompileUnits();
EXPECT_EQ(2, cus);

SymbolContextList sc_list;
uint32_t scope = lldb::eSymbolContextCompUnit | lldb::eSymbolContextLineEntry;

uint32_t count = symfile->ResolveSymbolContext(source_file, 0, true, scope, sc_list);
EXPECT_EQ(1, count);
SymbolContext sc;
EXPECT_TRUE(sc_list.GetContextAtIndex(0, sc));

LineTable *lt = sc.comp_unit->GetLineTable();
EXPECT_NE(nullptr, lt);
count = lt->GetSize();
// We expect one extra entry for termination (per function)
EXPECT_EQ(16, count);

VerifyLineEntry(module, sc, source_file, *lt, 7, 0x401040);
VerifyLineEntry(module, sc, source_file, *lt, 8, 0x401043);
VerifyLineEntry(module, sc, source_file, *lt, 9, 0x401045);

VerifyLineEntry(module, sc, source_file, *lt, 13, 0x401050);
VerifyLineEntry(module, sc, source_file, *lt, 14, 0x401054);
VerifyLineEntry(module, sc, source_file, *lt, 15, 0x401070);

VerifyLineEntry(module, sc, header1, *lt, 9, 0x401090);
VerifyLineEntry(module, sc, header1, *lt, 10, 0x401093);
VerifyLineEntry(module, sc, header1, *lt, 11, 0x4010a2);

VerifyLineEntry(module, sc, header2, *lt, 5, 0x401080);
VerifyLineEntry(module, sc, header2, *lt, 6, 0x401083);
VerifyLineEntry(module, sc, header2, *lt, 7, 0x401089);
}

TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestLineTablesMatchSpecific))
{
// Test that when calling ResolveSymbolContext with a specific line number, only line entries
// which match the requested line are returned.
FileSpec fspec(m_pdb_test_exe.c_str(), false);
ArchSpec aspec("i686-pc-windows");
lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec);

SymbolVendor *plugin = module->GetSymbolVendor();
SymbolFile *symfile = plugin->GetSymbolFile();

FileSpec source_file("test-pdb.cpp", false);
FileSpec header1("test-pdb.h", false);
FileSpec header2("test-pdb-nested.h", false);
uint32_t cus = symfile->GetNumCompileUnits();
EXPECT_EQ(2, cus);

SymbolContextList sc_list;
uint32_t scope = lldb::eSymbolContextCompUnit | lldb::eSymbolContextLineEntry;

// First test with line 7, and verify that only line 7 entries are added.
uint32_t count = symfile->ResolveSymbolContext(source_file, 7, true, scope, sc_list);
EXPECT_EQ(1, count);
SymbolContext sc;
EXPECT_TRUE(sc_list.GetContextAtIndex(0, sc));

LineTable *lt = sc.comp_unit->GetLineTable();
EXPECT_NE(nullptr, lt);
count = lt->GetSize();
// We expect one extra entry for termination
EXPECT_EQ(3, count);

VerifyLineEntry(module, sc, source_file, *lt, 7, 0x401040);
VerifyLineEntry(module, sc, header2, *lt, 7, 0x401089);

sc_list.Clear();
// Then test with line 9, and verify that only line 9 entries are added.
count = symfile->ResolveSymbolContext(source_file, 9, true, scope, sc_list);
EXPECT_EQ(1, count);
EXPECT_TRUE(sc_list.GetContextAtIndex(0, sc));

lt = sc.comp_unit->GetLineTable();
EXPECT_NE(nullptr, lt);
count = lt->GetSize();
// We expect one extra entry for termination
EXPECT_EQ(3, count);

VerifyLineEntry(module, sc, source_file, *lt, 9, 0x401045);
VerifyLineEntry(module, sc, header1, *lt, 9, 0x401090);
}