1 //===-- PythonDataObjectsTests.cpp ------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "gtest/gtest.h"
11 
12 #include "llvm/ADT/STLExtras.h"
13 #include "llvm/Config/config.h"
14 #include "llvm/Support/FileSystem.h"
15 #include "llvm/Support/Path.h"
16 
17 #include "lldb/Core/Address.h"
18 #include "lldb/Core/ArchSpec.h"
19 #include "lldb/Core/Module.h"
20 #include "lldb/Core/ModuleSpec.h"
21 #include "lldb/Host/FileSpec.h"
22 #include "lldb/Host/HostInfo.h"
23 #include "lldb/Symbol/CompileUnit.h"
24 #include "lldb/Symbol/LineTable.h"
25 #include "lldb/Symbol/SymbolVendor.h"
26 
27 #include "Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h"
28 #include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h"
29 #include "Plugins/SymbolFile/PDB/SymbolFilePDB.h"
30 
31 #if defined(_MSC_VER)
32 #include <objbase.h>
33 #endif
34 
35 extern const char *TestMainArgv0;
36 
37 using namespace lldb_private;
38 
39 class SymbolFilePDBTests : public testing::Test
40 {
41 public:
42     void
43     SetUp() override
44     {
45 #if defined(_MSC_VER)
46         ::CoInitializeEx(nullptr, COINIT_MULTITHREADED);
47 #endif
48 
49         HostInfoBase::Initialize();
50         ObjectFilePECOFF::Initialize();
51         SymbolFileDWARF::Initialize();
52         SymbolFilePDB::Initialize();
53 
54         llvm::StringRef exe_folder = llvm::sys::path::parent_path(TestMainArgv0);
55         llvm::SmallString<128> inputs_folder = exe_folder;
56         llvm::sys::path::append(inputs_folder, "Inputs");
57 
58         m_pdb_test_exe = inputs_folder;
59         m_dwarf_test_exe = inputs_folder;
60         llvm::sys::path::append(m_pdb_test_exe, "test-pdb.exe");
61         llvm::sys::path::append(m_dwarf_test_exe, "test-dwarf.exe");
62     }
63 
64     void
65     TearDown() override
66     {
67 #if defined(_MSC_VER)
68         ::CoUninitialize();
69 #endif
70         SymbolFilePDB::Terminate();
71         SymbolFileDWARF::Terminate();
72         ObjectFilePECOFF::Terminate();
73     }
74 
75 protected:
76     llvm::SmallString<128> m_pdb_test_exe;
77     llvm::SmallString<128> m_dwarf_test_exe;
78 
79     bool
80     FileSpecMatchesAsBaseOrFull(const FileSpec &left, const FileSpec &right) const
81     {
82         // If the filenames don't match, the paths can't be equal
83         if (!left.FileEquals(right))
84             return false;
85         // If BOTH have a directory, also compare the directories.
86         if (left.GetDirectory() && right.GetDirectory())
87             return left.DirectoryEquals(right);
88 
89         // If one has a directory but not the other, they match.
90         return true;
91     }
92 
93     void
94     VerifyLineEntry(lldb::ModuleSP module, const SymbolContext &sc, const FileSpec &spec, LineTable &lt, uint32_t line,
95                     lldb::addr_t addr)
96     {
97         LineEntry entry;
98         Address address;
99         EXPECT_TRUE(module->ResolveFileAddress(addr, address));
100 
101         EXPECT_TRUE(lt.FindLineEntryByAddress(address, entry));
102         EXPECT_EQ(line, entry.line);
103         EXPECT_EQ(address, entry.range.GetBaseAddress());
104 
105         EXPECT_TRUE(FileSpecMatchesAsBaseOrFull(spec, entry.file));
106     }
107 
108     bool
109     ContainsCompileUnit(const SymbolContextList &sc_list, const FileSpec &spec) const
110     {
111         for (size_t i = 0; i < sc_list.GetSize(); ++i)
112         {
113             const SymbolContext &sc = sc_list[i];
114             if (FileSpecMatchesAsBaseOrFull(*sc.comp_unit, spec))
115                 return true;
116         }
117         return false;
118     }
119 };
120 
121 #if defined(HAVE_DIA_SDK)
122 #define REQUIRES_DIA_SDK(TestName) TestName
123 #else
124 #define REQUIRES_DIA_SDK(TestName) DISABLED_##TestName
125 #endif
126 
127 TEST_F(SymbolFilePDBTests, TestAbilitiesForDWARF)
128 {
129     // Test that when we have Dwarf debug info, SymbolFileDWARF is used.
130     FileSpec fspec(m_dwarf_test_exe.c_str(), false);
131     ArchSpec aspec("i686-pc-windows");
132     lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec);
133 
134     SymbolVendor *plugin = module->GetSymbolVendor();
135     EXPECT_NE(nullptr, plugin);
136     SymbolFile *symfile = plugin->GetSymbolFile();
137     EXPECT_NE(nullptr, symfile);
138     EXPECT_EQ(symfile->GetPluginName(), SymbolFileDWARF::GetPluginNameStatic());
139 
140     uint32_t expected_abilities = SymbolFile::kAllAbilities;
141     EXPECT_EQ(expected_abilities, symfile->CalculateAbilities());
142 }
143 
144 TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestAbilitiesForPDB))
145 {
146     // Test that when we have PDB debug info, SymbolFilePDB is used.
147     FileSpec fspec(m_pdb_test_exe.c_str(), false);
148     ArchSpec aspec("i686-pc-windows");
149     lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec);
150 
151     SymbolVendor *plugin = module->GetSymbolVendor();
152     EXPECT_NE(nullptr, plugin);
153     SymbolFile *symfile = plugin->GetSymbolFile();
154     EXPECT_NE(nullptr, symfile);
155     EXPECT_EQ(symfile->GetPluginName(), SymbolFilePDB::GetPluginNameStatic());
156 
157     uint32_t expected_abilities = SymbolFile::CompileUnits | SymbolFile::LineTables;
158     EXPECT_EQ(expected_abilities, symfile->CalculateAbilities());
159 }
160 
161 TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestResolveSymbolContextBasename))
162 {
163     // Test that attempting to call ResolveSymbolContext with only a basename finds all full paths
164     // with the same basename
165     FileSpec fspec(m_pdb_test_exe.c_str(), false);
166     ArchSpec aspec("i686-pc-windows");
167     lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec);
168 
169     SymbolVendor *plugin = module->GetSymbolVendor();
170     EXPECT_NE(nullptr, plugin);
171     SymbolFile *symfile = plugin->GetSymbolFile();
172 
173     FileSpec header_spec("test-pdb.cpp", false);
174     SymbolContextList sc_list;
175     uint32_t result_count = symfile->ResolveSymbolContext(header_spec, 0, false, lldb::eSymbolContextCompUnit, sc_list);
176     EXPECT_EQ(1u, result_count);
177     EXPECT_TRUE(ContainsCompileUnit(sc_list, header_spec));
178 }
179 
180 TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestResolveSymbolContextFullPath))
181 {
182     // Test that attempting to call ResolveSymbolContext with a full path only finds the one source
183     // file that matches the full path.
184     FileSpec fspec(m_pdb_test_exe.c_str(), false);
185     ArchSpec aspec("i686-pc-windows");
186     lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec);
187 
188     SymbolVendor *plugin = module->GetSymbolVendor();
189     EXPECT_NE(nullptr, plugin);
190     SymbolFile *symfile = plugin->GetSymbolFile();
191 
192     FileSpec header_spec(R"spec(D:\src\llvm\tools\lldb\unittests\SymbolFile\PDB\Inputs\test-pdb.cpp)spec", false);
193     SymbolContextList sc_list;
194     uint32_t result_count = symfile->ResolveSymbolContext(header_spec, 0, false, lldb::eSymbolContextCompUnit, sc_list);
195     EXPECT_GE(1u, result_count);
196     EXPECT_TRUE(ContainsCompileUnit(sc_list, header_spec));
197 }
198 
199 TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestLookupOfHeaderFileWithInlines))
200 {
201     // Test that when looking up a header file via ResolveSymbolContext (i.e. a file that was not by itself
202     // compiled, but only contributes to the combined code of other source files), a SymbolContext is returned
203     // for each compiland which has line contributions from the requested header.
204     FileSpec fspec(m_pdb_test_exe.c_str(), false);
205     ArchSpec aspec("i686-pc-windows");
206     lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec);
207 
208     SymbolVendor *plugin = module->GetSymbolVendor();
209     EXPECT_NE(nullptr, plugin);
210     SymbolFile *symfile = plugin->GetSymbolFile();
211 
212     FileSpec header_specs[] = {FileSpec("test-pdb.h", false), FileSpec("test-pdb-nested.h", false)};
213     FileSpec main_cpp_spec("test-pdb.cpp", false);
214     FileSpec alt_cpp_spec("test-pdb-alt.cpp", false);
215     for (const auto &hspec : header_specs)
216     {
217         SymbolContextList sc_list;
218         uint32_t result_count = symfile->ResolveSymbolContext(hspec, 0, true, lldb::eSymbolContextCompUnit, sc_list);
219         EXPECT_EQ(2u, result_count);
220         EXPECT_TRUE(ContainsCompileUnit(sc_list, main_cpp_spec));
221         EXPECT_TRUE(ContainsCompileUnit(sc_list, alt_cpp_spec));
222     }
223 }
224 
225 TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestLookupOfHeaderFileWithNoInlines))
226 {
227     // Test that when looking up a header file via ResolveSymbolContext (i.e. a file that was not by itself
228     // compiled, but only contributes to the combined code of other source files), that if check_inlines
229     // is false, no SymbolContexts are returned.
230     FileSpec fspec(m_pdb_test_exe.c_str(), false);
231     ArchSpec aspec("i686-pc-windows");
232     lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec);
233 
234     SymbolVendor *plugin = module->GetSymbolVendor();
235     EXPECT_NE(nullptr, plugin);
236     SymbolFile *symfile = plugin->GetSymbolFile();
237 
238     FileSpec header_specs[] = {FileSpec("test-pdb.h", false), FileSpec("test-pdb-nested.h", false)};
239     for (const auto &hspec : header_specs)
240     {
241         SymbolContextList sc_list;
242         uint32_t result_count = symfile->ResolveSymbolContext(hspec, 0, false, lldb::eSymbolContextCompUnit, sc_list);
243         EXPECT_EQ(0u, result_count);
244     }
245 }
246 
247 TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestLineTablesMatchAll))
248 {
249     // Test that when calling ResolveSymbolContext with a line number of 0, all line entries from
250     // the specified files are returned.
251     FileSpec fspec(m_pdb_test_exe.c_str(), false);
252     ArchSpec aspec("i686-pc-windows");
253     lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec);
254 
255     SymbolVendor *plugin = module->GetSymbolVendor();
256     SymbolFile *symfile = plugin->GetSymbolFile();
257 
258     FileSpec source_file("test-pdb.cpp", false);
259     FileSpec header1("test-pdb.h", false);
260     FileSpec header2("test-pdb-nested.h", false);
261     uint32_t cus = symfile->GetNumCompileUnits();
262     EXPECT_EQ(2u, cus);
263 
264     SymbolContextList sc_list;
265     uint32_t scope = lldb::eSymbolContextCompUnit | lldb::eSymbolContextLineEntry;
266 
267     uint32_t count = symfile->ResolveSymbolContext(source_file, 0, true, scope, sc_list);
268     EXPECT_EQ(1u, count);
269     SymbolContext sc;
270     EXPECT_TRUE(sc_list.GetContextAtIndex(0, sc));
271 
272     LineTable *lt = sc.comp_unit->GetLineTable();
273     EXPECT_NE(nullptr, lt);
274     count = lt->GetSize();
275     // We expect one extra entry for termination (per function)
276     EXPECT_EQ(16u, count);
277 
278     VerifyLineEntry(module, sc, source_file, *lt, 7, 0x401040);
279     VerifyLineEntry(module, sc, source_file, *lt, 8, 0x401043);
280     VerifyLineEntry(module, sc, source_file, *lt, 9, 0x401045);
281 
282     VerifyLineEntry(module, sc, source_file, *lt, 13, 0x401050);
283     VerifyLineEntry(module, sc, source_file, *lt, 14, 0x401054);
284     VerifyLineEntry(module, sc, source_file, *lt, 15, 0x401070);
285 
286     VerifyLineEntry(module, sc, header1, *lt, 9, 0x401090);
287     VerifyLineEntry(module, sc, header1, *lt, 10, 0x401093);
288     VerifyLineEntry(module, sc, header1, *lt, 11, 0x4010a2);
289 
290     VerifyLineEntry(module, sc, header2, *lt, 5, 0x401080);
291     VerifyLineEntry(module, sc, header2, *lt, 6, 0x401083);
292     VerifyLineEntry(module, sc, header2, *lt, 7, 0x401089);
293 }
294 
295 TEST_F(SymbolFilePDBTests, REQUIRES_DIA_SDK(TestLineTablesMatchSpecific))
296 {
297     // Test that when calling ResolveSymbolContext with a specific line number, only line entries
298     // which match the requested line are returned.
299     FileSpec fspec(m_pdb_test_exe.c_str(), false);
300     ArchSpec aspec("i686-pc-windows");
301     lldb::ModuleSP module = std::make_shared<Module>(fspec, aspec);
302 
303     SymbolVendor *plugin = module->GetSymbolVendor();
304     SymbolFile *symfile = plugin->GetSymbolFile();
305 
306     FileSpec source_file("test-pdb.cpp", false);
307     FileSpec header1("test-pdb.h", false);
308     FileSpec header2("test-pdb-nested.h", false);
309     uint32_t cus = symfile->GetNumCompileUnits();
310     EXPECT_EQ(2u, cus);
311 
312     SymbolContextList sc_list;
313     uint32_t scope = lldb::eSymbolContextCompUnit | lldb::eSymbolContextLineEntry;
314 
315     // First test with line 7, and verify that only line 7 entries are added.
316     uint32_t count = symfile->ResolveSymbolContext(source_file, 7, true, scope, sc_list);
317     EXPECT_EQ(1u, count);
318     SymbolContext sc;
319     EXPECT_TRUE(sc_list.GetContextAtIndex(0, sc));
320 
321     LineTable *lt = sc.comp_unit->GetLineTable();
322     EXPECT_NE(nullptr, lt);
323     count = lt->GetSize();
324     // We expect one extra entry for termination
325     EXPECT_EQ(3u, count);
326 
327     VerifyLineEntry(module, sc, source_file, *lt, 7, 0x401040);
328     VerifyLineEntry(module, sc, header2, *lt, 7, 0x401089);
329 
330     sc_list.Clear();
331     // Then test with line 9, and verify that only line 9 entries are added.
332     count = symfile->ResolveSymbolContext(source_file, 9, true, scope, sc_list);
333     EXPECT_EQ(1u, count);
334     EXPECT_TRUE(sc_list.GetContextAtIndex(0, sc));
335 
336     lt = sc.comp_unit->GetLineTable();
337     EXPECT_NE(nullptr, lt);
338     count = lt->GetSize();
339     // We expect one extra entry for termination
340     EXPECT_EQ(3u, count);
341 
342     VerifyLineEntry(module, sc, source_file, *lt, 9, 0x401045);
343     VerifyLineEntry(module, sc, header1, *lt, 9, 0x401090);
344 }
345