//===- llvm/unittest/DebugInfo/GSYMTest.cpp -------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "llvm/ADT/DenseMap.h" #include "llvm/DebugInfo/GSYM/FileEntry.h" #include "llvm/DebugInfo/GSYM/FunctionInfo.h" #include "llvm/DebugInfo/GSYM/InlineInfo.h" #include "llvm/DebugInfo/GSYM/Range.h" #include "llvm/DebugInfo/GSYM/StringTable.h" #include "llvm/Testing/Support/Error.h" #include "gtest/gtest.h" #include using namespace llvm; using namespace gsym; TEST(GSYMTest, TestFileEntry) { // Make sure default constructed GSYM FileEntry has zeroes in the // directory and basename string table indexes. FileEntry empty1; FileEntry empty2; EXPECT_EQ(empty1.Dir, 0u); EXPECT_EQ(empty1.Base, 0u); // Verify equality operator works FileEntry a1(10, 30); FileEntry a2(10, 30); FileEntry b(10, 40); EXPECT_EQ(empty1, empty2); EXPECT_EQ(a1, a2); EXPECT_NE(a1, b); EXPECT_NE(a1, empty1); // Test we can use llvm::gsym::FileEntry in llvm::DenseMap. DenseMap EntryToIndex; constexpr uint32_t Index1 = 1; constexpr uint32_t Index2 = 1; auto R = EntryToIndex.insert(std::make_pair(a1, Index1)); EXPECT_TRUE(R.second); EXPECT_EQ(R.first->second, Index1); R = EntryToIndex.insert(std::make_pair(a1, Index1)); EXPECT_FALSE(R.second); EXPECT_EQ(R.first->second, Index1); R = EntryToIndex.insert(std::make_pair(b, Index2)); EXPECT_TRUE(R.second); EXPECT_EQ(R.first->second, Index2); R = EntryToIndex.insert(std::make_pair(a1, Index2)); EXPECT_FALSE(R.second); EXPECT_EQ(R.first->second, Index2); } TEST(GSYMTest, TestFunctionInfo) { // Test GSYM FunctionInfo structs and functionality. FunctionInfo invalid; EXPECT_FALSE(invalid.isValid()); EXPECT_FALSE(invalid.hasRichInfo()); const uint64_t StartAddr = 0x1000; const uint64_t EndAddr = 0x1100; const uint64_t Size = EndAddr - StartAddr; const uint32_t NameOffset = 30; FunctionInfo FI(StartAddr, Size, NameOffset); EXPECT_TRUE(FI.isValid()); EXPECT_FALSE(FI.hasRichInfo()); EXPECT_EQ(FI.startAddress(), StartAddr); EXPECT_EQ(FI.endAddress(), EndAddr); EXPECT_EQ(FI.size(), Size); const uint32_t FileIdx = 1; const uint32_t Line = 12; FI.Lines.push_back(LineEntry(StartAddr, FileIdx, Line)); EXPECT_TRUE(FI.hasRichInfo()); FI.clear(); EXPECT_FALSE(FI.isValid()); EXPECT_FALSE(FI.hasRichInfo()); FunctionInfo A1(0x1000, 0x100, NameOffset); FunctionInfo A2(0x1000, 0x100, NameOffset); FunctionInfo B; // Check == operator EXPECT_EQ(A1, A2); // Make sure things are not equal if they only differ by start address. B = A2; B.setStartAddress(0x2000); EXPECT_NE(B, A2); // Make sure things are not equal if they only differ by size. B = A2; B.setSize(0x101); EXPECT_NE(B, A2); // Make sure things are not equal if they only differ by name. B = A2; B.Name = 60; EXPECT_NE(B, A2); // Check < operator. // Check less than where address differs. B = A2; B.setStartAddress(A2.startAddress() + 0x1000); EXPECT_LT(A1, B); // We use the < operator to take a variety of different FunctionInfo // structs from a variety of sources: symtab, debug info, runtime info // and we sort them and want the sorting to allow us to quickly get the // best version of a function info. FunctionInfo FISymtab(StartAddr, Size, NameOffset); FunctionInfo FIWithLines(StartAddr, Size, NameOffset); FIWithLines.Lines.push_back(LineEntry(StartAddr, FileIdx, Line)); // Test that a FunctionInfo with just a name and size is less than one // that has name, size and any number of line table entries EXPECT_LT(FISymtab, FIWithLines); FunctionInfo FIWithLinesAndInline = FIWithLines; FIWithLinesAndInline.Inline.Ranges.insert( AddressRange(StartAddr, StartAddr + 0x10)); // Test that a FunctionInfo with name, size, and line entries is less than // the same one with valid inline info EXPECT_LT(FIWithLines, FIWithLinesAndInline); // Test if we have an entry with lines and one with more lines for the same // range, the ones with more lines is greater than the one with less. FunctionInfo FIWithMoreLines = FIWithLines; FIWithMoreLines.Lines.push_back(LineEntry(StartAddr, FileIdx, Line + 5)); EXPECT_LT(FIWithLines, FIWithMoreLines); // Test that if we have the same number of lines we compare the line entries // in the FunctionInfo.Lines vector. FunctionInfo FIWithLinesWithHigherAddress = FIWithLines; FIWithLinesWithHigherAddress.Lines[0].Addr += 0x10; EXPECT_LT(FIWithLines, FIWithLinesWithHigherAddress); } TEST(GSYMTest, TestInlineInfo) { // Test InlineInfo structs. InlineInfo II; EXPECT_FALSE(II.isValid()); II.Ranges.insert(AddressRange(0x1000, 0x2000)); // Make sure InlineInfo in valid with just an address range since // top level InlineInfo objects have ranges with no name, call file // or call line EXPECT_TRUE(II.isValid()); // Make sure InlineInfo isn't after being cleared. II.clear(); EXPECT_FALSE(II.isValid()); // Create an InlineInfo that contains the following data. The // indentation of the address range indicates the parent child // relationships of the InlineInfo objects: // // Variable Range and values // =========== ==================================================== // Root [0x100-0x200) (no name, file, or line) // Inline1 [0x150-0x160) Name = 1, File = 1, Line = 11 // Inline1Sub1 [0x152-0x155) Name = 2, File = 2, Line = 22 // Inline1Sub2 [0x157-0x158) Name = 3, File = 3, Line = 33 InlineInfo Root; Root.Ranges.insert(AddressRange(0x100, 0x200)); InlineInfo Inline1; Inline1.Ranges.insert(AddressRange(0x150, 0x160)); Inline1.Name = 1; Inline1.CallFile = 1; Inline1.CallLine = 11; InlineInfo Inline1Sub1; Inline1Sub1.Ranges.insert(AddressRange(0x152, 0x155)); Inline1Sub1.Name = 2; Inline1Sub1.CallFile = 2; Inline1Sub1.CallLine = 22; InlineInfo Inline1Sub2; Inline1Sub2.Ranges.insert(AddressRange(0x157, 0x158)); Inline1Sub2.Name = 3; Inline1Sub2.CallFile = 3; Inline1Sub2.CallLine = 33; Inline1.Children.push_back(Inline1Sub1); Inline1.Children.push_back(Inline1Sub2); Root.Children.push_back(Inline1); // Make sure an address that is out of range won't match EXPECT_FALSE(Root.getInlineStack(0x50)); // Verify that we get no inline stacks for addresses out of [0x100-0x200) EXPECT_FALSE(Root.getInlineStack(Root.Ranges[0].Start - 1)); EXPECT_FALSE(Root.getInlineStack(Root.Ranges[0].End)); // Verify we get no inline stack entries for addresses that are in // [0x100-0x200) but not in [0x150-0x160) EXPECT_FALSE(Root.getInlineStack(Inline1.Ranges[0].Start - 1)); EXPECT_FALSE(Root.getInlineStack(Inline1.Ranges[0].End)); // Verify we get one inline stack entry for addresses that are in // [[0x150-0x160)) but not in [0x152-0x155) or [0x157-0x158) auto InlineInfos = Root.getInlineStack(Inline1.Ranges[0].Start); ASSERT_TRUE(InlineInfos); ASSERT_EQ(InlineInfos->size(), 1u); ASSERT_EQ(*InlineInfos->at(0), Inline1); InlineInfos = Root.getInlineStack(Inline1.Ranges[0].End - 1); EXPECT_TRUE(InlineInfos); ASSERT_EQ(InlineInfos->size(), 1u); ASSERT_EQ(*InlineInfos->at(0), Inline1); // Verify we get two inline stack entries for addresses that are in // [0x152-0x155) InlineInfos = Root.getInlineStack(Inline1Sub1.Ranges[0].Start); EXPECT_TRUE(InlineInfos); ASSERT_EQ(InlineInfos->size(), 2u); ASSERT_EQ(*InlineInfos->at(0), Inline1Sub1); ASSERT_EQ(*InlineInfos->at(1), Inline1); InlineInfos = Root.getInlineStack(Inline1Sub1.Ranges[0].End - 1); EXPECT_TRUE(InlineInfos); ASSERT_EQ(InlineInfos->size(), 2u); ASSERT_EQ(*InlineInfos->at(0), Inline1Sub1); ASSERT_EQ(*InlineInfos->at(1), Inline1); // Verify we get two inline stack entries for addresses that are in // [0x157-0x158) InlineInfos = Root.getInlineStack(Inline1Sub2.Ranges[0].Start); EXPECT_TRUE(InlineInfos); ASSERT_EQ(InlineInfos->size(), 2u); ASSERT_EQ(*InlineInfos->at(0), Inline1Sub2); ASSERT_EQ(*InlineInfos->at(1), Inline1); InlineInfos = Root.getInlineStack(Inline1Sub2.Ranges[0].End - 1); EXPECT_TRUE(InlineInfos); ASSERT_EQ(InlineInfos->size(), 2u); ASSERT_EQ(*InlineInfos->at(0), Inline1Sub2); ASSERT_EQ(*InlineInfos->at(1), Inline1); } TEST(GSYMTest, TestLineEntry) { // test llvm::gsym::LineEntry structs. const uint64_t ValidAddr = 0x1000; const uint64_t InvalidFileIdx = 0; const uint32_t ValidFileIdx = 1; const uint32_t ValidLine = 5; LineEntry Invalid; EXPECT_FALSE(Invalid.isValid()); // Make sure that an entry is invalid if it has a bad file index. LineEntry BadFile(ValidAddr, InvalidFileIdx, ValidLine); EXPECT_FALSE(BadFile.isValid()); // Test operators LineEntry E1(ValidAddr, ValidFileIdx, ValidLine); LineEntry E2(ValidAddr, ValidFileIdx, ValidLine); LineEntry DifferentAddr(ValidAddr + 1, ValidFileIdx, ValidLine); LineEntry DifferentFile(ValidAddr, ValidFileIdx + 1, ValidLine); LineEntry DifferentLine(ValidAddr, ValidFileIdx, ValidLine + 1); EXPECT_TRUE(E1.isValid()); EXPECT_EQ(E1, E2); EXPECT_NE(E1, DifferentAddr); EXPECT_NE(E1, DifferentFile); EXPECT_NE(E1, DifferentLine); EXPECT_LT(E1, DifferentAddr); } TEST(GSYMTest, TestRanges) { // test llvm::gsym::AddressRange. const uint64_t StartAddr = 0x1000; const uint64_t EndAddr = 0x2000; // Verify constructor and API to ensure it takes start and end address. const AddressRange Range(StartAddr, EndAddr); EXPECT_EQ(Range.size(), EndAddr - StartAddr); // Verify llvm::gsym::AddressRange::contains(). EXPECT_FALSE(Range.contains(0)); EXPECT_FALSE(Range.contains(StartAddr - 1)); EXPECT_TRUE(Range.contains(StartAddr)); EXPECT_TRUE(Range.contains(EndAddr - 1)); EXPECT_FALSE(Range.contains(EndAddr)); EXPECT_FALSE(Range.contains(UINT64_MAX)); const AddressRange RangeSame(StartAddr, EndAddr); const AddressRange RangeDifferentStart(StartAddr + 1, EndAddr); const AddressRange RangeDifferentEnd(StartAddr, EndAddr + 1); const AddressRange RangeDifferentStartEnd(StartAddr + 1, EndAddr + 1); // Test == and != with values that are the same EXPECT_EQ(Range, RangeSame); EXPECT_FALSE(Range != RangeSame); // Test == and != with values that are the different EXPECT_NE(Range, RangeDifferentStart); EXPECT_NE(Range, RangeDifferentEnd); EXPECT_NE(Range, RangeDifferentStartEnd); EXPECT_FALSE(Range == RangeDifferentStart); EXPECT_FALSE(Range == RangeDifferentEnd); EXPECT_FALSE(Range == RangeDifferentStartEnd); // Test "bool operator<(const AddressRange &, const AddressRange &)". EXPECT_FALSE(Range < RangeSame); EXPECT_FALSE(RangeSame < Range); EXPECT_LT(Range, RangeDifferentStart); EXPECT_LT(Range, RangeDifferentEnd); EXPECT_LT(Range, RangeDifferentStartEnd); // Test "bool operator<(const AddressRange &, uint64_t)" EXPECT_LT(Range.Start, StartAddr + 1); // Test "bool operator<(uint64_t, const AddressRange &)" EXPECT_LT(StartAddr - 1, Range.Start); // Verify llvm::gsym::AddressRange::isContiguousWith() and // llvm::gsym::AddressRange::intersects(). const AddressRange EndsBeforeRangeStart(0, StartAddr - 1); const AddressRange EndsAtRangeStart(0, StartAddr); const AddressRange OverlapsRangeStart(StartAddr - 1, StartAddr + 1); const AddressRange InsideRange(StartAddr + 1, EndAddr - 1); const AddressRange OverlapsRangeEnd(EndAddr - 1, EndAddr + 1); const AddressRange StartsAtRangeEnd(EndAddr, EndAddr + 0x100); const AddressRange StartsAfterRangeEnd(EndAddr + 1, EndAddr + 0x100); EXPECT_FALSE(Range.intersects(EndsBeforeRangeStart)); EXPECT_FALSE(Range.intersects(EndsAtRangeStart)); EXPECT_TRUE(Range.intersects(OverlapsRangeStart)); EXPECT_TRUE(Range.intersects(InsideRange)); EXPECT_TRUE(Range.intersects(OverlapsRangeEnd)); EXPECT_FALSE(Range.intersects(StartsAtRangeEnd)); EXPECT_FALSE(Range.intersects(StartsAfterRangeEnd)); // Test the functions that maintain GSYM address ranges: // "bool AddressRange::contains(uint64_t Addr) const;" // "void AddressRanges::insert(const AddressRange &R);" AddressRanges Ranges; Ranges.insert(AddressRange(0x1000, 0x2000)); Ranges.insert(AddressRange(0x2000, 0x3000)); Ranges.insert(AddressRange(0x4000, 0x5000)); EXPECT_FALSE(Ranges.contains(0)); EXPECT_FALSE(Ranges.contains(0x1000 - 1)); EXPECT_TRUE(Ranges.contains(0x1000)); EXPECT_TRUE(Ranges.contains(0x2000)); EXPECT_TRUE(Ranges.contains(0x4000)); EXPECT_TRUE(Ranges.contains(0x2000 - 1)); EXPECT_TRUE(Ranges.contains(0x3000 - 1)); EXPECT_FALSE(Ranges.contains(0x3000 + 1)); EXPECT_TRUE(Ranges.contains(0x5000 - 1)); EXPECT_FALSE(Ranges.contains(0x5000 + 1)); EXPECT_FALSE(Ranges.contains(UINT64_MAX)); // Verify that intersecting ranges get combined Ranges.clear(); Ranges.insert(AddressRange(0x1100, 0x1F00)); // Verify a wholy contained range that is added doesn't do anything. Ranges.insert(AddressRange(0x1500, 0x1F00)); EXPECT_EQ(Ranges.size(), 1u); EXPECT_EQ(Ranges[0], AddressRange(0x1100, 0x1F00)); // Verify a range that starts before and intersects gets combined. Ranges.insert(AddressRange(0x1000, Ranges[0].Start + 1)); EXPECT_EQ(Ranges.size(), 1u); EXPECT_EQ(Ranges[0], AddressRange(0x1000, 0x1F00)); // Verify a range that starts inside and extends ranges gets combined. Ranges.insert(AddressRange(Ranges[0].End - 1, 0x2000)); EXPECT_EQ(Ranges.size(), 1u); EXPECT_EQ(Ranges[0], AddressRange(0x1000, 0x2000)); // Verify that adjacent ranges don't get combined Ranges.insert(AddressRange(0x2000, 0x3000)); EXPECT_EQ(Ranges.size(), 2u); EXPECT_EQ(Ranges[0], AddressRange(0x1000, 0x2000)); EXPECT_EQ(Ranges[1], AddressRange(0x2000, 0x3000)); // Verify if we add an address range that intersects two ranges // that they get combined Ranges.insert(AddressRange(Ranges[0].End - 1, Ranges[1].Start + 1)); EXPECT_EQ(Ranges.size(), 1u); EXPECT_EQ(Ranges[0], AddressRange(0x1000, 0x3000)); Ranges.insert(AddressRange(0x3000, 0x4000)); Ranges.insert(AddressRange(0x4000, 0x5000)); Ranges.insert(AddressRange(0x2000, 0x4500)); EXPECT_EQ(Ranges.size(), 1u); EXPECT_EQ(Ranges[0], AddressRange(0x1000, 0x5000)); } TEST(GSYMTest, TestStringTable) { StringTable StrTab(StringRef("\0Hello\0World\0", 13)); // Test extracting strings from a string table. EXPECT_EQ(StrTab.getString(0), ""); EXPECT_EQ(StrTab.getString(1), "Hello"); EXPECT_EQ(StrTab.getString(7), "World"); EXPECT_EQ(StrTab.getString(8), "orld"); // Test pointing to last NULL terminator gets empty string. EXPECT_EQ(StrTab.getString(12), ""); // Test pointing to past end gets empty string. EXPECT_EQ(StrTab.getString(13), ""); }