2019-06-26 22:09:09 +08:00
|
|
|
//===- 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"
|
2019-08-22 05:48:11 +08:00
|
|
|
#include "llvm/ADT/SmallString.h"
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
|
|
|
|
#include "llvm/DebugInfo/GSYM/DwarfTransformer.h"
|
2019-09-18 01:46:13 +08:00
|
|
|
#include "llvm/DebugInfo/GSYM/Header.h"
|
2019-06-26 22:09:09 +08:00
|
|
|
#include "llvm/DebugInfo/GSYM/FileEntry.h"
|
2019-08-22 05:48:11 +08:00
|
|
|
#include "llvm/DebugInfo/GSYM/FileWriter.h"
|
2019-06-26 22:09:09 +08:00
|
|
|
#include "llvm/DebugInfo/GSYM/FunctionInfo.h"
|
2019-10-11 01:10:11 +08:00
|
|
|
#include "llvm/DebugInfo/GSYM/GsymCreator.h"
|
|
|
|
#include "llvm/DebugInfo/GSYM/GsymReader.h"
|
2019-06-26 22:09:09 +08:00
|
|
|
#include "llvm/DebugInfo/GSYM/InlineInfo.h"
|
|
|
|
#include "llvm/DebugInfo/GSYM/Range.h"
|
|
|
|
#include "llvm/DebugInfo/GSYM/StringTable.h"
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
#include "llvm/ObjectYAML/DWARFEmitter.h"
|
2019-08-22 05:48:11 +08:00
|
|
|
#include "llvm/Support/DataExtractor.h"
|
|
|
|
#include "llvm/Support/Endian.h"
|
Add lookup functions for efficient lookups of addresses when using GsymReader classes.
Summary:
Lookup functions are designed to not fully decode a FunctionInfo, LineTable or InlineInfo, they decode only what is needed into a LookupResult object. This allows lookups to avoid costly memory allocations and avoid parsing large amounts of information one a suitable match is found.
LookupResult objects contain the address that was looked up, the concrete function address range, the name of the concrete function, and a list of source locations. One for each inline function, and one for the concrete function. This allows one address to turn into multiple frames and improves the signal you get when symbolicating addresses in GSYM files.
Reviewers: labath, aprantl
Subscribers: mgorny, hiraditya, llvm-commits, lldb-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D70993
2019-12-04 08:44:02 +08:00
|
|
|
#include "llvm/Testing/Support/Error.h"
|
2019-06-26 22:09:09 +08:00
|
|
|
|
|
|
|
#include "gtest/gtest.h"
|
Add lookup functions for efficient lookups of addresses when using GsymReader classes.
Summary:
Lookup functions are designed to not fully decode a FunctionInfo, LineTable or InlineInfo, they decode only what is needed into a LookupResult object. This allows lookups to avoid costly memory allocations and avoid parsing large amounts of information one a suitable match is found.
LookupResult objects contain the address that was looked up, the concrete function address range, the name of the concrete function, and a list of source locations. One for each inline function, and one for the concrete function. This allows one address to turn into multiple frames and improves the signal you get when symbolicating addresses in GSYM files.
Reviewers: labath, aprantl
Subscribers: mgorny, hiraditya, llvm-commits, lldb-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D70993
2019-12-04 08:44:02 +08:00
|
|
|
#include "gmock/gmock.h"
|
2019-06-26 22:09:09 +08:00
|
|
|
#include <string>
|
|
|
|
|
|
|
|
using namespace llvm;
|
|
|
|
using namespace gsym;
|
|
|
|
|
2019-09-18 00:15:49 +08:00
|
|
|
void checkError(ArrayRef<std::string> ExpectedMsgs, Error Err) {
|
|
|
|
ASSERT_TRUE(bool(Err));
|
|
|
|
size_t WhichMsg = 0;
|
|
|
|
Error Remaining =
|
|
|
|
handleErrors(std::move(Err), [&](const ErrorInfoBase &Actual) {
|
|
|
|
ASSERT_LT(WhichMsg, ExpectedMsgs.size());
|
|
|
|
// Use .str(), because googletest doesn't visualise a StringRef
|
|
|
|
// properly.
|
|
|
|
EXPECT_EQ(Actual.message(), ExpectedMsgs[WhichMsg++]);
|
|
|
|
});
|
|
|
|
EXPECT_EQ(WhichMsg, ExpectedMsgs.size());
|
|
|
|
EXPECT_FALSE(Remaining);
|
|
|
|
}
|
|
|
|
|
|
|
|
void checkError(std::string ExpectedMsg, Error Err) {
|
|
|
|
checkError(ArrayRef<std::string>{ExpectedMsg}, std::move(Err));
|
|
|
|
}
|
2019-06-26 22:09:09 +08:00
|
|
|
TEST(GSYMTest, TestFileEntry) {
|
2019-06-28 16:58:05 +08:00
|
|
|
// Make sure default constructed GSYM FileEntry has zeroes in the
|
2019-06-26 22:09:09 +08:00
|
|
|
// directory and basename string table indexes.
|
|
|
|
FileEntry empty1;
|
|
|
|
FileEntry empty2;
|
|
|
|
EXPECT_EQ(empty1.Dir, 0u);
|
|
|
|
EXPECT_EQ(empty1.Base, 0u);
|
|
|
|
// Verify equality operator works
|
2019-06-28 16:58:05 +08:00
|
|
|
FileEntry a1(10, 30);
|
|
|
|
FileEntry a2(10, 30);
|
|
|
|
FileEntry b(10, 40);
|
2019-06-26 22:09:09 +08:00
|
|
|
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<FileEntry, uint32_t> 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;
|
2019-09-12 06:24:45 +08:00
|
|
|
FI.OptLineTable = LineTable();
|
|
|
|
FI.OptLineTable->push(LineEntry(StartAddr,FileIdx,Line));
|
2019-06-26 22:09:09 +08:00
|
|
|
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);
|
2019-06-28 16:58:05 +08:00
|
|
|
|
2019-06-26 22:09:09 +08:00
|
|
|
// 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
|
2019-06-28 16:58:05 +08:00
|
|
|
// best version of a function info.
|
2019-06-26 22:09:09 +08:00
|
|
|
FunctionInfo FISymtab(StartAddr, Size, NameOffset);
|
|
|
|
FunctionInfo FIWithLines(StartAddr, Size, NameOffset);
|
2019-09-12 06:24:45 +08:00
|
|
|
FIWithLines.OptLineTable = LineTable();
|
|
|
|
FIWithLines.OptLineTable->push(LineEntry(StartAddr,FileIdx,Line));
|
2019-06-26 22:09:09 +08:00
|
|
|
// 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;
|
2019-09-12 04:51:03 +08:00
|
|
|
FIWithLinesAndInline.Inline = InlineInfo();
|
|
|
|
FIWithLinesAndInline.Inline->Ranges.insert(
|
2019-06-28 16:58:05 +08:00
|
|
|
AddressRange(StartAddr, StartAddr + 0x10));
|
2019-06-26 22:09:09 +08:00
|
|
|
// 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;
|
2019-09-12 06:24:45 +08:00
|
|
|
FIWithMoreLines.OptLineTable->push(LineEntry(StartAddr,FileIdx,Line+5));
|
2019-06-26 22:09:09 +08:00
|
|
|
EXPECT_LT(FIWithLines, FIWithMoreLines);
|
|
|
|
|
|
|
|
// Test that if we have the same number of lines we compare the line entries
|
2019-09-12 06:24:45 +08:00
|
|
|
// in the FunctionInfo.OptLineTable.Lines vector.
|
2019-06-26 22:09:09 +08:00
|
|
|
FunctionInfo FIWithLinesWithHigherAddress = FIWithLines;
|
2019-09-12 06:24:45 +08:00
|
|
|
FIWithLinesWithHigherAddress.OptLineTable->get(0).Addr += 0x10;
|
2019-06-26 22:09:09 +08:00
|
|
|
EXPECT_LT(FIWithLines, FIWithLinesWithHigherAddress);
|
|
|
|
}
|
|
|
|
|
2019-09-18 00:15:49 +08:00
|
|
|
static void TestFunctionInfoDecodeError(llvm::support::endianness ByteOrder,
|
2020-01-29 03:23:46 +08:00
|
|
|
StringRef Bytes,
|
2019-09-18 00:15:49 +08:00
|
|
|
const uint64_t BaseAddr,
|
|
|
|
std::string ExpectedErrorMsg) {
|
|
|
|
uint8_t AddressSize = 4;
|
|
|
|
DataExtractor Data(Bytes, ByteOrder == llvm::support::little, AddressSize);
|
|
|
|
llvm::Expected<FunctionInfo> Decoded = FunctionInfo::decode(Data, BaseAddr);
|
|
|
|
// Make sure decoding fails.
|
|
|
|
ASSERT_FALSE((bool)Decoded);
|
|
|
|
// Make sure decoded object is the same as the one we encoded.
|
|
|
|
checkError(ExpectedErrorMsg, Decoded.takeError());
|
2019-09-05 01:32:51 +08:00
|
|
|
}
|
|
|
|
|
2019-09-18 00:15:49 +08:00
|
|
|
TEST(GSYMTest, TestFunctionInfoDecodeErrors) {
|
|
|
|
// Test decoding FunctionInfo objects that ensure we report an appropriate
|
|
|
|
// error message.
|
|
|
|
const llvm::support::endianness ByteOrder = llvm::support::little;
|
|
|
|
SmallString<512> Str;
|
|
|
|
raw_svector_ostream OutStrm(Str);
|
|
|
|
FileWriter FW(OutStrm, ByteOrder);
|
|
|
|
const uint64_t BaseAddr = 0x100;
|
|
|
|
TestFunctionInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr,
|
|
|
|
"0x00000000: missing FunctionInfo Size");
|
|
|
|
FW.writeU32(0x100); // Function size.
|
|
|
|
TestFunctionInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr,
|
|
|
|
"0x00000004: missing FunctionInfo Name");
|
|
|
|
// Write out an invalid Name string table offset of zero.
|
|
|
|
FW.writeU32(0);
|
|
|
|
TestFunctionInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr,
|
|
|
|
"0x00000004: invalid FunctionInfo Name value 0x00000000");
|
|
|
|
// Modify the Name to be 0x00000001, which is a valid value.
|
|
|
|
FW.fixup32(0x00000001, 4);
|
|
|
|
TestFunctionInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr,
|
|
|
|
"0x00000008: missing FunctionInfo InfoType value");
|
|
|
|
auto FixupOffset = FW.tell();
|
|
|
|
FW.writeU32(1); // InfoType::LineTableInfo.
|
|
|
|
TestFunctionInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr,
|
|
|
|
"0x0000000c: missing FunctionInfo InfoType length");
|
|
|
|
FW.fixup32(4, FixupOffset); // Write an invalid InfoType enumeration value
|
|
|
|
FW.writeU32(0); // LineTableInfo InfoType data length.
|
|
|
|
TestFunctionInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr,
|
|
|
|
"0x00000008: unsupported InfoType 4");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void TestFunctionInfoEncodeError(llvm::support::endianness ByteOrder,
|
|
|
|
const FunctionInfo &FI,
|
|
|
|
std::string ExpectedErrorMsg) {
|
|
|
|
SmallString<512> Str;
|
|
|
|
raw_svector_ostream OutStrm(Str);
|
|
|
|
FileWriter FW(OutStrm, ByteOrder);
|
|
|
|
Expected<uint64_t> ExpectedOffset = FI.encode(FW);
|
|
|
|
ASSERT_FALSE(ExpectedOffset);
|
|
|
|
checkError(ExpectedErrorMsg, ExpectedOffset.takeError());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(GSYMTest, TestFunctionInfoEncodeErrors) {
|
|
|
|
const uint64_t FuncAddr = 0x1000;
|
|
|
|
const uint64_t FuncSize = 0x100;
|
|
|
|
const uint32_t InvalidName = 0;
|
|
|
|
const uint32_t ValidName = 1;
|
|
|
|
FunctionInfo InvalidNameFI(FuncAddr, FuncSize, InvalidName);
|
|
|
|
TestFunctionInfoEncodeError(llvm::support::little, InvalidNameFI,
|
|
|
|
"attempted to encode invalid FunctionInfo object");
|
|
|
|
|
|
|
|
FunctionInfo InvalidLineTableFI(FuncAddr, FuncSize, ValidName);
|
|
|
|
// Empty line tables are not valid. Verify if the encoding of anything
|
|
|
|
// in our line table fails, that we see get the error propagated.
|
|
|
|
InvalidLineTableFI.OptLineTable = LineTable();
|
|
|
|
TestFunctionInfoEncodeError(llvm::support::little, InvalidLineTableFI,
|
|
|
|
"attempted to encode invalid LineTable object");
|
|
|
|
|
|
|
|
FunctionInfo InvalidInlineInfoFI(FuncAddr, FuncSize, ValidName);
|
|
|
|
// Empty line tables are not valid. Verify if the encoding of anything
|
|
|
|
// in our line table fails, that we see get the error propagated.
|
|
|
|
InvalidInlineInfoFI.Inline = InlineInfo();
|
|
|
|
TestFunctionInfoEncodeError(llvm::support::little, InvalidInlineInfoFI,
|
|
|
|
"attempted to encode invalid InlineInfo object");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void TestFunctionInfoEncodeDecode(llvm::support::endianness ByteOrder,
|
|
|
|
const FunctionInfo &FI) {
|
|
|
|
// Test encoding and decoding FunctionInfo objects.
|
|
|
|
SmallString<512> Str;
|
|
|
|
raw_svector_ostream OutStrm(Str);
|
|
|
|
FileWriter FW(OutStrm, ByteOrder);
|
|
|
|
llvm::Expected<uint64_t> ExpectedOffset = FI.encode(FW);
|
|
|
|
ASSERT_TRUE(bool(ExpectedOffset));
|
|
|
|
// Verify we got the encoded offset back from the encode function.
|
|
|
|
ASSERT_EQ(ExpectedOffset.get(), 0ULL);
|
|
|
|
std::string Bytes(OutStrm.str());
|
|
|
|
uint8_t AddressSize = 4;
|
|
|
|
DataExtractor Data(Bytes, ByteOrder == llvm::support::little, AddressSize);
|
|
|
|
llvm::Expected<FunctionInfo> Decoded = FunctionInfo::decode(Data,
|
|
|
|
FI.Range.Start);
|
|
|
|
// Make sure decoding succeeded.
|
|
|
|
ASSERT_TRUE((bool)Decoded);
|
|
|
|
// Make sure decoded object is the same as the one we encoded.
|
|
|
|
EXPECT_EQ(FI, Decoded.get());
|
|
|
|
}
|
|
|
|
|
2019-09-18 04:31:01 +08:00
|
|
|
static void AddLines(uint64_t FuncAddr, uint32_t FileIdx, FunctionInfo &FI) {
|
2019-09-18 00:15:49 +08:00
|
|
|
FI.OptLineTable = LineTable();
|
2019-09-18 01:24:55 +08:00
|
|
|
LineEntry Line0(FuncAddr + 0x000, FileIdx, 10);
|
|
|
|
LineEntry Line1(FuncAddr + 0x010, FileIdx, 11);
|
|
|
|
LineEntry Line2(FuncAddr + 0x100, FileIdx, 1000);
|
2019-09-18 00:15:49 +08:00
|
|
|
FI.OptLineTable->push(Line0);
|
|
|
|
FI.OptLineTable->push(Line1);
|
|
|
|
FI.OptLineTable->push(Line2);
|
2019-09-18 04:31:01 +08:00
|
|
|
}
|
2019-09-18 00:15:49 +08:00
|
|
|
|
2019-09-18 04:31:01 +08:00
|
|
|
|
|
|
|
static void AddInline(uint64_t FuncAddr, uint64_t FuncSize, FunctionInfo &FI) {
|
2019-09-18 00:15:49 +08:00
|
|
|
FI.Inline = InlineInfo();
|
2019-09-18 01:24:55 +08:00
|
|
|
FI.Inline->Ranges.insert(AddressRange(FuncAddr, FuncAddr + FuncSize));
|
2019-09-18 00:15:49 +08:00
|
|
|
InlineInfo Inline1;
|
2019-09-18 01:24:55 +08:00
|
|
|
Inline1.Ranges.insert(AddressRange(FuncAddr + 0x10, FuncAddr + 0x30));
|
2019-09-18 00:15:49 +08:00
|
|
|
Inline1.Name = 1;
|
|
|
|
Inline1.CallFile = 1;
|
|
|
|
Inline1.CallLine = 11;
|
|
|
|
FI.Inline->Children.push_back(Inline1);
|
2019-09-18 04:31:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(GSYMTest, TestFunctionInfoEncoding) {
|
|
|
|
constexpr uint64_t FuncAddr = 0x1000;
|
|
|
|
constexpr uint64_t FuncSize = 0x100;
|
|
|
|
constexpr uint32_t FuncName = 1;
|
|
|
|
constexpr uint32_t FileIdx = 1;
|
|
|
|
// Make sure that we can encode and decode a FunctionInfo with no line table
|
|
|
|
// or inline info.
|
|
|
|
FunctionInfo FI(FuncAddr, FuncSize, FuncName);
|
|
|
|
TestFunctionInfoEncodeDecode(llvm::support::little, FI);
|
|
|
|
TestFunctionInfoEncodeDecode(llvm::support::big, FI);
|
2019-09-18 00:15:49 +08:00
|
|
|
|
|
|
|
// Make sure that we can encode and decode a FunctionInfo with a line table
|
|
|
|
// and no inline info.
|
|
|
|
FunctionInfo FILines(FuncAddr, FuncSize, FuncName);
|
2019-09-18 04:31:01 +08:00
|
|
|
AddLines(FuncAddr, FileIdx, FILines);
|
2019-09-18 00:15:49 +08:00
|
|
|
TestFunctionInfoEncodeDecode(llvm::support::little, FILines);
|
|
|
|
TestFunctionInfoEncodeDecode(llvm::support::big, FILines);
|
|
|
|
|
|
|
|
// Make sure that we can encode and decode a FunctionInfo with no line table
|
|
|
|
// and with inline info.
|
|
|
|
FunctionInfo FIInline(FuncAddr, FuncSize, FuncName);
|
2019-09-18 04:31:01 +08:00
|
|
|
AddInline(FuncAddr, FuncSize, FIInline);
|
2019-09-18 00:15:49 +08:00
|
|
|
TestFunctionInfoEncodeDecode(llvm::support::little, FIInline);
|
|
|
|
TestFunctionInfoEncodeDecode(llvm::support::big, FIInline);
|
|
|
|
|
|
|
|
// Make sure that we can encode and decode a FunctionInfo with no line table
|
|
|
|
// and with inline info.
|
|
|
|
FunctionInfo FIBoth(FuncAddr, FuncSize, FuncName);
|
2019-09-18 04:31:01 +08:00
|
|
|
AddLines(FuncAddr, FileIdx, FIBoth);
|
|
|
|
AddInline(FuncAddr, FuncSize, FIBoth);
|
2019-09-18 00:15:49 +08:00
|
|
|
TestFunctionInfoEncodeDecode(llvm::support::little, FIBoth);
|
|
|
|
TestFunctionInfoEncodeDecode(llvm::support::big, FIBoth);
|
2019-09-05 01:32:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void TestInlineInfoEncodeDecode(llvm::support::endianness ByteOrder,
|
|
|
|
const InlineInfo &Inline) {
|
|
|
|
// Test encoding and decoding InlineInfo objects
|
|
|
|
SmallString<512> Str;
|
|
|
|
raw_svector_ostream OutStrm(Str);
|
|
|
|
FileWriter FW(OutStrm, ByteOrder);
|
|
|
|
const uint64_t BaseAddr = Inline.Ranges[0].Start;
|
|
|
|
llvm::Error Err = Inline.encode(FW, BaseAddr);
|
|
|
|
ASSERT_FALSE(Err);
|
|
|
|
std::string Bytes(OutStrm.str());
|
|
|
|
uint8_t AddressSize = 4;
|
|
|
|
DataExtractor Data(Bytes, ByteOrder == llvm::support::little, AddressSize);
|
|
|
|
llvm::Expected<InlineInfo> Decoded = InlineInfo::decode(Data, BaseAddr);
|
|
|
|
// Make sure decoding succeeded.
|
|
|
|
ASSERT_TRUE((bool)Decoded);
|
|
|
|
// Make sure decoded object is the same as the one we encoded.
|
|
|
|
EXPECT_EQ(Inline, Decoded.get());
|
|
|
|
}
|
|
|
|
|
|
|
|
static void TestInlineInfoDecodeError(llvm::support::endianness ByteOrder,
|
2020-01-29 03:23:46 +08:00
|
|
|
StringRef Bytes, const uint64_t BaseAddr,
|
2019-09-05 01:32:51 +08:00
|
|
|
std::string ExpectedErrorMsg) {
|
|
|
|
uint8_t AddressSize = 4;
|
|
|
|
DataExtractor Data(Bytes, ByteOrder == llvm::support::little, AddressSize);
|
|
|
|
llvm::Expected<InlineInfo> Decoded = InlineInfo::decode(Data, BaseAddr);
|
|
|
|
// Make sure decoding fails.
|
|
|
|
ASSERT_FALSE((bool)Decoded);
|
|
|
|
// Make sure decoded object is the same as the one we encoded.
|
|
|
|
checkError(ExpectedErrorMsg, Decoded.takeError());
|
|
|
|
}
|
|
|
|
|
|
|
|
static void TestInlineInfoEncodeError(llvm::support::endianness ByteOrder,
|
|
|
|
const InlineInfo &Inline,
|
|
|
|
std::string ExpectedErrorMsg) {
|
|
|
|
SmallString<512> Str;
|
|
|
|
raw_svector_ostream OutStrm(Str);
|
|
|
|
FileWriter FW(OutStrm, ByteOrder);
|
|
|
|
const uint64_t BaseAddr = Inline.Ranges.empty() ? 0 : Inline.Ranges[0].Start;
|
|
|
|
llvm::Error Err = Inline.encode(FW, BaseAddr);
|
|
|
|
checkError(ExpectedErrorMsg, std::move(Err));
|
|
|
|
}
|
|
|
|
|
2019-06-26 22:09:09 +08:00
|
|
|
TEST(GSYMTest, TestInlineInfo) {
|
|
|
|
// Test InlineInfo structs.
|
|
|
|
InlineInfo II;
|
|
|
|
EXPECT_FALSE(II.isValid());
|
2019-06-28 16:58:05 +08:00
|
|
|
II.Ranges.insert(AddressRange(0x1000, 0x2000));
|
2019-06-26 22:09:09 +08:00
|
|
|
// 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;
|
2019-06-28 16:58:05 +08:00
|
|
|
Root.Ranges.insert(AddressRange(0x100, 0x200));
|
2019-06-26 22:09:09 +08:00
|
|
|
InlineInfo Inline1;
|
2019-06-28 16:58:05 +08:00
|
|
|
Inline1.Ranges.insert(AddressRange(0x150, 0x160));
|
2019-06-26 22:09:09 +08:00
|
|
|
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;
|
2019-06-28 16:58:05 +08:00
|
|
|
Inline1Sub2.Ranges.insert(AddressRange(0x157, 0x158));
|
2019-06-26 22:09:09 +08:00
|
|
|
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));
|
|
|
|
|
2019-06-28 16:58:05 +08:00
|
|
|
// Verify that we get no inline stacks for addresses out of [0x100-0x200)
|
2019-06-28 18:06:11 +08:00
|
|
|
EXPECT_FALSE(Root.getInlineStack(Root.Ranges[0].Start - 1));
|
|
|
|
EXPECT_FALSE(Root.getInlineStack(Root.Ranges[0].End));
|
2019-06-26 22:09:09 +08:00
|
|
|
|
|
|
|
// Verify we get no inline stack entries for addresses that are in
|
|
|
|
// [0x100-0x200) but not in [0x150-0x160)
|
2019-06-28 18:06:11 +08:00
|
|
|
EXPECT_FALSE(Root.getInlineStack(Inline1.Ranges[0].Start - 1));
|
|
|
|
EXPECT_FALSE(Root.getInlineStack(Inline1.Ranges[0].End));
|
2019-06-26 22:09:09 +08:00
|
|
|
|
|
|
|
// Verify we get one inline stack entry for addresses that are in
|
|
|
|
// [[0x150-0x160)) but not in [0x152-0x155) or [0x157-0x158)
|
2019-06-28 18:06:11 +08:00
|
|
|
auto InlineInfos = Root.getInlineStack(Inline1.Ranges[0].Start);
|
2019-06-26 22:09:09 +08:00
|
|
|
ASSERT_TRUE(InlineInfos);
|
|
|
|
ASSERT_EQ(InlineInfos->size(), 1u);
|
|
|
|
ASSERT_EQ(*InlineInfos->at(0), Inline1);
|
2019-06-28 18:06:11 +08:00
|
|
|
InlineInfos = Root.getInlineStack(Inline1.Ranges[0].End - 1);
|
2019-06-26 22:09:09 +08:00
|
|
|
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)
|
2019-06-28 18:06:11 +08:00
|
|
|
InlineInfos = Root.getInlineStack(Inline1Sub1.Ranges[0].Start);
|
2019-06-26 22:09:09 +08:00
|
|
|
EXPECT_TRUE(InlineInfos);
|
|
|
|
ASSERT_EQ(InlineInfos->size(), 2u);
|
|
|
|
ASSERT_EQ(*InlineInfos->at(0), Inline1Sub1);
|
|
|
|
ASSERT_EQ(*InlineInfos->at(1), Inline1);
|
2019-06-28 18:06:11 +08:00
|
|
|
InlineInfos = Root.getInlineStack(Inline1Sub1.Ranges[0].End - 1);
|
2019-06-26 22:09:09 +08:00
|
|
|
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)
|
2019-06-28 18:06:11 +08:00
|
|
|
InlineInfos = Root.getInlineStack(Inline1Sub2.Ranges[0].Start);
|
2019-06-26 22:09:09 +08:00
|
|
|
EXPECT_TRUE(InlineInfos);
|
|
|
|
ASSERT_EQ(InlineInfos->size(), 2u);
|
|
|
|
ASSERT_EQ(*InlineInfos->at(0), Inline1Sub2);
|
|
|
|
ASSERT_EQ(*InlineInfos->at(1), Inline1);
|
2019-06-28 18:06:11 +08:00
|
|
|
InlineInfos = Root.getInlineStack(Inline1Sub2.Ranges[0].End - 1);
|
2019-06-26 22:09:09 +08:00
|
|
|
EXPECT_TRUE(InlineInfos);
|
|
|
|
ASSERT_EQ(InlineInfos->size(), 2u);
|
|
|
|
ASSERT_EQ(*InlineInfos->at(0), Inline1Sub2);
|
|
|
|
ASSERT_EQ(*InlineInfos->at(1), Inline1);
|
2019-09-05 01:32:51 +08:00
|
|
|
|
|
|
|
// Test encoding and decoding InlineInfo objects
|
|
|
|
TestInlineInfoEncodeDecode(llvm::support::little, Root);
|
|
|
|
TestInlineInfoEncodeDecode(llvm::support::big, Root);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(GSYMTest, TestInlineInfoEncodeErrors) {
|
|
|
|
// Test InlineInfo encoding errors.
|
|
|
|
|
|
|
|
// Test that we get an error when trying to encode an InlineInfo object
|
|
|
|
// that has no ranges.
|
|
|
|
InlineInfo Empty;
|
|
|
|
std::string EmptyErr("attempted to encode invalid InlineInfo object");
|
|
|
|
TestInlineInfoEncodeError(llvm::support::little, Empty, EmptyErr);
|
|
|
|
TestInlineInfoEncodeError(llvm::support::big, Empty, EmptyErr);
|
|
|
|
|
|
|
|
// Verify that we get an error trying to encode an InlineInfo object that has
|
|
|
|
// a child InlineInfo that has no ranges.
|
|
|
|
InlineInfo ContainsEmpty;
|
|
|
|
ContainsEmpty.Ranges.insert({0x100,200});
|
|
|
|
ContainsEmpty.Children.push_back(Empty);
|
|
|
|
TestInlineInfoEncodeError(llvm::support::little, ContainsEmpty, EmptyErr);
|
|
|
|
TestInlineInfoEncodeError(llvm::support::big, ContainsEmpty, EmptyErr);
|
|
|
|
|
|
|
|
// Verify that we get an error trying to encode an InlineInfo object that has
|
|
|
|
// a child whose address range is not contained in the parent address range.
|
|
|
|
InlineInfo ChildNotContained;
|
|
|
|
std::string ChildNotContainedErr("child range not contained in parent");
|
|
|
|
ChildNotContained.Ranges.insert({0x100,200});
|
|
|
|
InlineInfo ChildNotContainedChild;
|
|
|
|
ChildNotContainedChild.Ranges.insert({0x200,300});
|
|
|
|
ChildNotContained.Children.push_back(ChildNotContainedChild);
|
|
|
|
TestInlineInfoEncodeError(llvm::support::little, ChildNotContained,
|
|
|
|
ChildNotContainedErr);
|
|
|
|
TestInlineInfoEncodeError(llvm::support::big, ChildNotContained,
|
|
|
|
ChildNotContainedErr);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(GSYMTest, TestInlineInfoDecodeErrors) {
|
|
|
|
// Test decoding InlineInfo objects that ensure we report an appropriate
|
|
|
|
// error message.
|
|
|
|
const llvm::support::endianness ByteOrder = llvm::support::little;
|
|
|
|
SmallString<512> Str;
|
|
|
|
raw_svector_ostream OutStrm(Str);
|
|
|
|
FileWriter FW(OutStrm, ByteOrder);
|
|
|
|
const uint64_t BaseAddr = 0x100;
|
|
|
|
TestInlineInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr,
|
|
|
|
"0x00000000: missing InlineInfo address ranges data");
|
|
|
|
AddressRanges Ranges;
|
|
|
|
Ranges.insert({BaseAddr, BaseAddr+0x100});
|
|
|
|
Ranges.encode(FW, BaseAddr);
|
|
|
|
TestInlineInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr,
|
|
|
|
"0x00000004: missing InlineInfo uint8_t indicating children");
|
|
|
|
FW.writeU8(0);
|
|
|
|
TestInlineInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr,
|
|
|
|
"0x00000005: missing InlineInfo uint32_t for name");
|
|
|
|
FW.writeU32(0);
|
|
|
|
TestInlineInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr,
|
|
|
|
"0x00000009: missing ULEB128 for InlineInfo call file");
|
|
|
|
FW.writeU8(0);
|
|
|
|
TestInlineInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr,
|
|
|
|
"0x0000000a: missing ULEB128 for InlineInfo call line");
|
2019-06-28 16:58:05 +08:00
|
|
|
}
|
2019-06-26 22:09:09 +08:00
|
|
|
|
|
|
|
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);
|
2019-06-28 16:58:05 +08:00
|
|
|
LineEntry DifferentAddr(ValidAddr + 1, ValidFileIdx, ValidLine);
|
|
|
|
LineEntry DifferentFile(ValidAddr, ValidFileIdx + 1, ValidLine);
|
|
|
|
LineEntry DifferentLine(ValidAddr, ValidFileIdx, ValidLine + 1);
|
2019-06-26 22:09:09 +08:00
|
|
|
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);
|
2019-06-28 16:58:05 +08:00
|
|
|
EXPECT_EQ(Range.size(), EndAddr - StartAddr);
|
2019-06-26 22:09:09 +08:00
|
|
|
|
|
|
|
// Verify llvm::gsym::AddressRange::contains().
|
|
|
|
EXPECT_FALSE(Range.contains(0));
|
2019-06-28 16:58:05 +08:00
|
|
|
EXPECT_FALSE(Range.contains(StartAddr - 1));
|
2019-06-26 22:09:09 +08:00
|
|
|
EXPECT_TRUE(Range.contains(StartAddr));
|
2019-06-28 16:58:05 +08:00
|
|
|
EXPECT_TRUE(Range.contains(EndAddr - 1));
|
2019-06-26 22:09:09 +08:00
|
|
|
EXPECT_FALSE(Range.contains(EndAddr));
|
|
|
|
EXPECT_FALSE(Range.contains(UINT64_MAX));
|
|
|
|
|
|
|
|
const AddressRange RangeSame(StartAddr, EndAddr);
|
2019-06-28 16:58:05 +08:00
|
|
|
const AddressRange RangeDifferentStart(StartAddr + 1, EndAddr);
|
|
|
|
const AddressRange RangeDifferentEnd(StartAddr, EndAddr + 1);
|
|
|
|
const AddressRange RangeDifferentStartEnd(StartAddr + 1, EndAddr + 1);
|
2019-06-26 22:09:09 +08:00
|
|
|
// 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)"
|
2019-06-28 18:06:11 +08:00
|
|
|
EXPECT_LT(Range.Start, StartAddr + 1);
|
2019-06-26 22:09:09 +08:00
|
|
|
// Test "bool operator<(uint64_t, const AddressRange &)"
|
2019-06-28 18:06:11 +08:00
|
|
|
EXPECT_LT(StartAddr - 1, Range.Start);
|
2019-06-26 22:09:09 +08:00
|
|
|
|
|
|
|
// Verify llvm::gsym::AddressRange::isContiguousWith() and
|
|
|
|
// llvm::gsym::AddressRange::intersects().
|
2019-06-28 16:58:05 +08:00
|
|
|
const AddressRange EndsBeforeRangeStart(0, StartAddr - 1);
|
2019-06-26 22:09:09 +08:00
|
|
|
const AddressRange EndsAtRangeStart(0, StartAddr);
|
2019-06-28 16:58:05 +08:00
|
|
|
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);
|
2019-06-26 22:09:09 +08:00
|
|
|
|
|
|
|
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));
|
2019-06-28 16:58:05 +08:00
|
|
|
EXPECT_FALSE(Ranges.contains(0x1000 - 1));
|
2019-06-26 22:09:09 +08:00
|
|
|
EXPECT_TRUE(Ranges.contains(0x1000));
|
|
|
|
EXPECT_TRUE(Ranges.contains(0x2000));
|
|
|
|
EXPECT_TRUE(Ranges.contains(0x4000));
|
2019-06-28 16:58:05 +08:00
|
|
|
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));
|
2019-06-26 22:09:09 +08:00
|
|
|
EXPECT_FALSE(Ranges.contains(UINT64_MAX));
|
|
|
|
|
2019-09-05 01:32:51 +08:00
|
|
|
EXPECT_FALSE(Ranges.contains(AddressRange()));
|
|
|
|
EXPECT_FALSE(Ranges.contains(AddressRange(0x1000-1, 0x1000)));
|
|
|
|
EXPECT_FALSE(Ranges.contains(AddressRange(0x1000, 0x1000)));
|
|
|
|
EXPECT_TRUE(Ranges.contains(AddressRange(0x1000, 0x1000+1)));
|
|
|
|
EXPECT_TRUE(Ranges.contains(AddressRange(0x1000, 0x2000)));
|
|
|
|
EXPECT_FALSE(Ranges.contains(AddressRange(0x1000, 0x2001)));
|
|
|
|
EXPECT_TRUE(Ranges.contains(AddressRange(0x2000, 0x3000)));
|
|
|
|
EXPECT_FALSE(Ranges.contains(AddressRange(0x2000, 0x3001)));
|
|
|
|
EXPECT_FALSE(Ranges.contains(AddressRange(0x3000, 0x3001)));
|
|
|
|
EXPECT_FALSE(Ranges.contains(AddressRange(0x1500, 0x4500)));
|
|
|
|
EXPECT_FALSE(Ranges.contains(AddressRange(0x5000, 0x5001)));
|
|
|
|
|
2019-06-26 22:09:09 +08:00
|
|
|
// 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.
|
2019-06-28 18:06:11 +08:00
|
|
|
Ranges.insert(AddressRange(0x1000, Ranges[0].Start + 1));
|
2019-06-26 22:09:09 +08:00
|
|
|
EXPECT_EQ(Ranges.size(), 1u);
|
|
|
|
EXPECT_EQ(Ranges[0], AddressRange(0x1000, 0x1F00));
|
|
|
|
|
|
|
|
// Verify a range that starts inside and extends ranges gets combined.
|
2019-06-28 18:06:11 +08:00
|
|
|
Ranges.insert(AddressRange(Ranges[0].End - 1, 0x2000));
|
2019-06-26 22:09:09 +08:00
|
|
|
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
|
2019-06-28 18:06:11 +08:00
|
|
|
Ranges.insert(AddressRange(Ranges[0].End - 1, Ranges[1].Start + 1));
|
2019-06-26 22:09:09 +08:00
|
|
|
EXPECT_EQ(Ranges.size(), 1u);
|
|
|
|
EXPECT_EQ(Ranges[0], AddressRange(0x1000, 0x3000));
|
2019-06-28 18:06:11 +08:00
|
|
|
|
|
|
|
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));
|
2019-06-26 22:09:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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), "");
|
|
|
|
}
|
2019-08-22 05:48:11 +08:00
|
|
|
|
|
|
|
static void TestFileWriterHelper(llvm::support::endianness ByteOrder) {
|
|
|
|
SmallString<512> Str;
|
|
|
|
raw_svector_ostream OutStrm(Str);
|
|
|
|
FileWriter FW(OutStrm, ByteOrder);
|
|
|
|
const int64_t MinSLEB = INT64_MIN;
|
|
|
|
const int64_t MaxSLEB = INT64_MAX;
|
|
|
|
const uint64_t MinULEB = 0;
|
|
|
|
const uint64_t MaxULEB = UINT64_MAX;
|
|
|
|
const uint8_t U8 = 0x10;
|
|
|
|
const uint16_t U16 = 0x1122;
|
|
|
|
const uint32_t U32 = 0x12345678;
|
|
|
|
const uint64_t U64 = 0x33445566778899aa;
|
|
|
|
const char *Hello = "hello";
|
|
|
|
FW.writeU8(U8);
|
|
|
|
FW.writeU16(U16);
|
|
|
|
FW.writeU32(U32);
|
|
|
|
FW.writeU64(U64);
|
|
|
|
FW.alignTo(16);
|
|
|
|
const off_t FixupOffset = FW.tell();
|
|
|
|
FW.writeU32(0);
|
|
|
|
FW.writeSLEB(MinSLEB);
|
|
|
|
FW.writeSLEB(MaxSLEB);
|
|
|
|
FW.writeULEB(MinULEB);
|
|
|
|
FW.writeULEB(MaxULEB);
|
|
|
|
FW.writeNullTerminated(Hello);
|
|
|
|
// Test Seek, Tell using Fixup32.
|
|
|
|
FW.fixup32(U32, FixupOffset);
|
|
|
|
|
|
|
|
std::string Bytes(OutStrm.str());
|
|
|
|
uint8_t AddressSize = 4;
|
|
|
|
DataExtractor Data(Bytes, ByteOrder == llvm::support::little, AddressSize);
|
|
|
|
uint64_t Offset = 0;
|
|
|
|
EXPECT_EQ(Data.getU8(&Offset), U8);
|
|
|
|
EXPECT_EQ(Data.getU16(&Offset), U16);
|
|
|
|
EXPECT_EQ(Data.getU32(&Offset), U32);
|
|
|
|
EXPECT_EQ(Data.getU64(&Offset), U64);
|
|
|
|
Offset = alignTo(Offset, 16);
|
|
|
|
EXPECT_EQ(Data.getU32(&Offset), U32);
|
|
|
|
EXPECT_EQ(Data.getSLEB128(&Offset), MinSLEB);
|
|
|
|
EXPECT_EQ(Data.getSLEB128(&Offset), MaxSLEB);
|
|
|
|
EXPECT_EQ(Data.getULEB128(&Offset), MinULEB);
|
|
|
|
EXPECT_EQ(Data.getULEB128(&Offset), MaxULEB);
|
|
|
|
EXPECT_EQ(Data.getCStrRef(&Offset), StringRef(Hello));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(GSYMTest, TestFileWriter) {
|
|
|
|
TestFileWriterHelper(llvm::support::little);
|
|
|
|
TestFileWriterHelper(llvm::support::big);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(GSYMTest, TestAddressRangeEncodeDecode) {
|
|
|
|
// Test encoding and decoding AddressRange objects. AddressRange objects
|
|
|
|
// are always stored as offsets from the a base address. The base address
|
|
|
|
// is the FunctionInfo's base address for function level ranges, and is
|
|
|
|
// the base address of the parent range for subranges.
|
|
|
|
SmallString<512> Str;
|
|
|
|
raw_svector_ostream OutStrm(Str);
|
|
|
|
const auto ByteOrder = llvm::support::endian::system_endianness();
|
|
|
|
FileWriter FW(OutStrm, ByteOrder);
|
|
|
|
const uint64_t BaseAddr = 0x1000;
|
|
|
|
const AddressRange Range1(0x1000, 0x1010);
|
|
|
|
const AddressRange Range2(0x1020, 0x1030);
|
|
|
|
Range1.encode(FW, BaseAddr);
|
|
|
|
Range2.encode(FW, BaseAddr);
|
|
|
|
std::string Bytes(OutStrm.str());
|
|
|
|
uint8_t AddressSize = 4;
|
|
|
|
DataExtractor Data(Bytes, ByteOrder == llvm::support::little, AddressSize);
|
|
|
|
|
|
|
|
AddressRange DecodedRange1, DecodedRange2;
|
|
|
|
uint64_t Offset = 0;
|
|
|
|
DecodedRange1.decode(Data, BaseAddr, Offset);
|
|
|
|
DecodedRange2.decode(Data, BaseAddr, Offset);
|
|
|
|
EXPECT_EQ(Range1, DecodedRange1);
|
|
|
|
EXPECT_EQ(Range2, DecodedRange2);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void TestAddressRangeEncodeDecodeHelper(const AddressRanges &Ranges,
|
|
|
|
const uint64_t BaseAddr) {
|
|
|
|
SmallString<512> Str;
|
|
|
|
raw_svector_ostream OutStrm(Str);
|
|
|
|
const auto ByteOrder = llvm::support::endian::system_endianness();
|
|
|
|
FileWriter FW(OutStrm, ByteOrder);
|
|
|
|
Ranges.encode(FW, BaseAddr);
|
|
|
|
|
|
|
|
std::string Bytes(OutStrm.str());
|
|
|
|
uint8_t AddressSize = 4;
|
|
|
|
DataExtractor Data(Bytes, ByteOrder == llvm::support::little, AddressSize);
|
|
|
|
|
|
|
|
AddressRanges DecodedRanges;
|
|
|
|
uint64_t Offset = 0;
|
|
|
|
DecodedRanges.decode(Data, BaseAddr, Offset);
|
|
|
|
EXPECT_EQ(Ranges, DecodedRanges);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(GSYMTest, TestAddressRangesEncodeDecode) {
|
|
|
|
// Test encoding and decoding AddressRanges. AddressRanges objects contain
|
|
|
|
// ranges that are stored as offsets from the a base address. The base address
|
|
|
|
// is the FunctionInfo's base address for function level ranges, and is the
|
|
|
|
// base address of the parent range for subranges.
|
|
|
|
const uint64_t BaseAddr = 0x1000;
|
|
|
|
|
|
|
|
// Test encoding and decoding with no ranges.
|
|
|
|
AddressRanges Ranges;
|
|
|
|
TestAddressRangeEncodeDecodeHelper(Ranges, BaseAddr);
|
|
|
|
|
|
|
|
// Test encoding and decoding with 1 range.
|
|
|
|
Ranges.insert(AddressRange(0x1000, 0x1010));
|
|
|
|
TestAddressRangeEncodeDecodeHelper(Ranges, BaseAddr);
|
|
|
|
|
|
|
|
// Test encoding and decoding with multiple ranges.
|
|
|
|
Ranges.insert(AddressRange(0x1020, 0x1030));
|
|
|
|
Ranges.insert(AddressRange(0x1050, 0x1070));
|
|
|
|
TestAddressRangeEncodeDecodeHelper(Ranges, BaseAddr);
|
|
|
|
}
|
2019-09-12 04:51:03 +08:00
|
|
|
|
|
|
|
static void TestLineTableHelper(llvm::support::endianness ByteOrder,
|
|
|
|
const LineTable <) {
|
|
|
|
SmallString<512> Str;
|
|
|
|
raw_svector_ostream OutStrm(Str);
|
|
|
|
FileWriter FW(OutStrm, ByteOrder);
|
|
|
|
const uint64_t BaseAddr = LT[0].Addr;
|
|
|
|
llvm::Error Err = LT.encode(FW, BaseAddr);
|
|
|
|
ASSERT_FALSE(Err);
|
|
|
|
std::string Bytes(OutStrm.str());
|
|
|
|
uint8_t AddressSize = 4;
|
|
|
|
DataExtractor Data(Bytes, ByteOrder == llvm::support::little, AddressSize);
|
|
|
|
llvm::Expected<LineTable> Decoded = LineTable::decode(Data, BaseAddr);
|
|
|
|
// Make sure decoding succeeded.
|
|
|
|
ASSERT_TRUE((bool)Decoded);
|
|
|
|
// Make sure decoded object is the same as the one we encoded.
|
|
|
|
EXPECT_EQ(LT, Decoded.get());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(GSYMTest, TestLineTable) {
|
|
|
|
const uint64_t StartAddr = 0x1000;
|
|
|
|
const uint32_t FileIdx = 1;
|
|
|
|
LineTable LT;
|
|
|
|
LineEntry Line0(StartAddr+0x000, FileIdx, 10);
|
|
|
|
LineEntry Line1(StartAddr+0x010, FileIdx, 11);
|
|
|
|
LineEntry Line2(StartAddr+0x100, FileIdx, 1000);
|
|
|
|
ASSERT_TRUE(LT.empty());
|
|
|
|
ASSERT_EQ(LT.size(), (size_t)0);
|
|
|
|
LT.push(Line0);
|
|
|
|
ASSERT_EQ(LT.size(), (size_t)1);
|
|
|
|
LT.push(Line1);
|
|
|
|
LT.push(Line2);
|
|
|
|
LT.push(LineEntry(StartAddr+0x120, FileIdx, 900));
|
|
|
|
LT.push(LineEntry(StartAddr+0x120, FileIdx, 2000));
|
|
|
|
LT.push(LineEntry(StartAddr+0x121, FileIdx, 2001));
|
|
|
|
LT.push(LineEntry(StartAddr+0x122, FileIdx, 2002));
|
|
|
|
LT.push(LineEntry(StartAddr+0x123, FileIdx, 2003));
|
|
|
|
ASSERT_FALSE(LT.empty());
|
|
|
|
ASSERT_EQ(LT.size(), (size_t)8);
|
|
|
|
// Test operator[].
|
|
|
|
ASSERT_EQ(LT[0], Line0);
|
|
|
|
ASSERT_EQ(LT[1], Line1);
|
|
|
|
ASSERT_EQ(LT[2], Line2);
|
|
|
|
|
|
|
|
// Test encoding and decoding line tables.
|
|
|
|
TestLineTableHelper(llvm::support::little, LT);
|
|
|
|
TestLineTableHelper(llvm::support::big, LT);
|
|
|
|
|
|
|
|
// Verify the clear method works as expected.
|
|
|
|
LT.clear();
|
|
|
|
ASSERT_TRUE(LT.empty());
|
|
|
|
ASSERT_EQ(LT.size(), (size_t)0);
|
|
|
|
|
|
|
|
LineTable LT1;
|
|
|
|
LineTable LT2;
|
|
|
|
|
|
|
|
// Test that two empty line tables are equal and neither are less than
|
|
|
|
// each other.
|
|
|
|
ASSERT_EQ(LT1, LT2);
|
2019-11-03 03:24:03 +08:00
|
|
|
ASSERT_FALSE(LT1 < LT1);
|
2019-09-12 04:51:03 +08:00
|
|
|
ASSERT_FALSE(LT1 < LT2);
|
2019-11-03 03:24:03 +08:00
|
|
|
ASSERT_FALSE(LT2 < LT1);
|
2019-09-12 04:51:03 +08:00
|
|
|
ASSERT_FALSE(LT2 < LT2);
|
|
|
|
|
|
|
|
// Test that a line table with less number of line entries is less than a
|
|
|
|
// line table with more line entries and that they are not equal.
|
|
|
|
LT2.push(Line0);
|
|
|
|
ASSERT_LT(LT1, LT2);
|
|
|
|
ASSERT_NE(LT1, LT2);
|
|
|
|
|
|
|
|
// Test that two line tables with the same entries are equal.
|
|
|
|
LT1.push(Line0);
|
|
|
|
ASSERT_EQ(LT1, LT2);
|
|
|
|
ASSERT_FALSE(LT1 < LT2);
|
|
|
|
ASSERT_FALSE(LT2 < LT2);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void TestLineTableDecodeError(llvm::support::endianness ByteOrder,
|
2020-01-29 03:23:46 +08:00
|
|
|
StringRef Bytes, const uint64_t BaseAddr,
|
2019-09-12 04:51:03 +08:00
|
|
|
std::string ExpectedErrorMsg) {
|
|
|
|
uint8_t AddressSize = 4;
|
|
|
|
DataExtractor Data(Bytes, ByteOrder == llvm::support::little, AddressSize);
|
|
|
|
llvm::Expected<LineTable> Decoded = LineTable::decode(Data, BaseAddr);
|
|
|
|
// Make sure decoding fails.
|
|
|
|
ASSERT_FALSE((bool)Decoded);
|
|
|
|
// Make sure decoded object is the same as the one we encoded.
|
|
|
|
checkError(ExpectedErrorMsg, Decoded.takeError());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(GSYMTest, TestLineTableDecodeErrors) {
|
|
|
|
// Test decoding InlineInfo objects that ensure we report an appropriate
|
|
|
|
// error message.
|
|
|
|
const llvm::support::endianness ByteOrder = llvm::support::little;
|
|
|
|
SmallString<512> Str;
|
|
|
|
raw_svector_ostream OutStrm(Str);
|
|
|
|
FileWriter FW(OutStrm, ByteOrder);
|
|
|
|
const uint64_t BaseAddr = 0x100;
|
|
|
|
TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr,
|
|
|
|
"0x00000000: missing LineTable MinDelta");
|
|
|
|
FW.writeU8(1); // MinDelta (ULEB)
|
|
|
|
TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr,
|
|
|
|
"0x00000001: missing LineTable MaxDelta");
|
|
|
|
FW.writeU8(10); // MaxDelta (ULEB)
|
|
|
|
TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr,
|
|
|
|
"0x00000002: missing LineTable FirstLine");
|
|
|
|
FW.writeU8(20); // FirstLine (ULEB)
|
|
|
|
TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr,
|
|
|
|
"0x00000003: EOF found before EndSequence");
|
|
|
|
// Test a SetFile with the argument missing from the stream
|
|
|
|
FW.writeU8(1); // SetFile opcode (uint8_t)
|
|
|
|
TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr,
|
|
|
|
"0x00000004: EOF found before SetFile value");
|
|
|
|
FW.writeU8(5); // SetFile value as index (ULEB)
|
|
|
|
// Test a AdvancePC with the argument missing from the stream
|
|
|
|
FW.writeU8(2); // AdvancePC opcode (uint8_t)
|
|
|
|
TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr,
|
|
|
|
"0x00000006: EOF found before AdvancePC value");
|
|
|
|
FW.writeU8(20); // AdvancePC value as offset (ULEB)
|
|
|
|
// Test a AdvancePC with the argument missing from the stream
|
|
|
|
FW.writeU8(3); // AdvanceLine opcode (uint8_t)
|
|
|
|
TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr,
|
|
|
|
"0x00000008: EOF found before AdvanceLine value");
|
|
|
|
FW.writeU8(20); // AdvanceLine value as offset (LLEB)
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(GSYMTest, TestLineTableEncodeErrors) {
|
|
|
|
const uint64_t BaseAddr = 0x1000;
|
|
|
|
const uint32_t FileIdx = 1;
|
|
|
|
const llvm::support::endianness ByteOrder = llvm::support::little;
|
|
|
|
SmallString<512> Str;
|
|
|
|
raw_svector_ostream OutStrm(Str);
|
|
|
|
FileWriter FW(OutStrm, ByteOrder);
|
|
|
|
LineTable LT;
|
|
|
|
checkError("attempted to encode invalid LineTable object",
|
|
|
|
LT.encode(FW, BaseAddr));
|
|
|
|
|
|
|
|
// Try to encode a line table where a line entry has an address that is less
|
|
|
|
// than BaseAddr and verify we get an appropriate error.
|
|
|
|
LineEntry Line0(BaseAddr+0x000, FileIdx, 10);
|
|
|
|
LineEntry Line1(BaseAddr+0x010, FileIdx, 11);
|
|
|
|
LT.push(Line0);
|
|
|
|
LT.push(Line1);
|
|
|
|
checkError("LineEntry has address 0x1000 which is less than the function "
|
|
|
|
"start address 0x1010", LT.encode(FW, BaseAddr+0x10));
|
|
|
|
LT.clear();
|
|
|
|
|
|
|
|
// Try to encode a line table where a line entries has an address that is less
|
|
|
|
// than BaseAddr and verify we get an appropriate error.
|
|
|
|
LT.push(Line1);
|
|
|
|
LT.push(Line0);
|
|
|
|
checkError("LineEntry in LineTable not in ascending order",
|
|
|
|
LT.encode(FW, BaseAddr));
|
|
|
|
LT.clear();
|
|
|
|
}
|
2019-09-18 01:46:13 +08:00
|
|
|
|
|
|
|
static void TestHeaderEncodeError(const Header &H,
|
|
|
|
std::string ExpectedErrorMsg) {
|
|
|
|
const support::endianness ByteOrder = llvm::support::little;
|
|
|
|
SmallString<512> Str;
|
|
|
|
raw_svector_ostream OutStrm(Str);
|
|
|
|
FileWriter FW(OutStrm, ByteOrder);
|
|
|
|
llvm::Error Err = H.encode(FW);
|
|
|
|
checkError(ExpectedErrorMsg, std::move(Err));
|
|
|
|
}
|
|
|
|
|
2020-01-29 03:23:46 +08:00
|
|
|
static void TestHeaderDecodeError(StringRef Bytes,
|
2019-09-18 01:46:13 +08:00
|
|
|
std::string ExpectedErrorMsg) {
|
|
|
|
const support::endianness ByteOrder = llvm::support::little;
|
|
|
|
uint8_t AddressSize = 4;
|
|
|
|
DataExtractor Data(Bytes, ByteOrder == llvm::support::little, AddressSize);
|
|
|
|
llvm::Expected<Header> Decoded = Header::decode(Data);
|
|
|
|
// Make sure decoding fails.
|
|
|
|
ASSERT_FALSE((bool)Decoded);
|
|
|
|
// Make sure decoded object is the same as the one we encoded.
|
|
|
|
checkError(ExpectedErrorMsg, Decoded.takeError());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Populate a GSYM header with valid values.
|
|
|
|
static void InitHeader(Header &H) {
|
|
|
|
H.Magic = GSYM_MAGIC;
|
|
|
|
H.Version = GSYM_VERSION;
|
|
|
|
H.AddrOffSize = 4;
|
|
|
|
H.UUIDSize = 16;
|
|
|
|
H.BaseAddress = 0x1000;
|
|
|
|
H.NumAddresses = 1;
|
|
|
|
H.StrtabOffset= 0x2000;
|
|
|
|
H.StrtabSize = 0x1000;
|
|
|
|
for (size_t i=0; i<GSYM_MAX_UUID_SIZE; ++i) {
|
|
|
|
if (i < H.UUIDSize)
|
|
|
|
H.UUID[i] = i;
|
|
|
|
else
|
|
|
|
H.UUID[i] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(GSYMTest, TestHeaderEncodeErrors) {
|
|
|
|
Header H;
|
|
|
|
InitHeader(H);
|
|
|
|
H.Magic = 12;
|
|
|
|
TestHeaderEncodeError(H, "invalid GSYM magic 0x0000000c");
|
|
|
|
InitHeader(H);
|
|
|
|
H.Version = 12;
|
|
|
|
TestHeaderEncodeError(H, "unsupported GSYM version 12");
|
|
|
|
InitHeader(H);
|
|
|
|
H.AddrOffSize = 12;
|
|
|
|
TestHeaderEncodeError(H, "invalid address offset size 12");
|
|
|
|
InitHeader(H);
|
|
|
|
H.UUIDSize = 128;
|
|
|
|
TestHeaderEncodeError(H, "invalid UUID size 128");
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(GSYMTest, TestHeaderDecodeErrors) {
|
|
|
|
const llvm::support::endianness ByteOrder = llvm::support::little;
|
|
|
|
SmallString<512> Str;
|
|
|
|
raw_svector_ostream OutStrm(Str);
|
|
|
|
FileWriter FW(OutStrm, ByteOrder);
|
|
|
|
Header H;
|
|
|
|
InitHeader(H);
|
|
|
|
llvm::Error Err = H.encode(FW);
|
|
|
|
ASSERT_FALSE(Err);
|
|
|
|
FW.fixup32(12, offsetof(Header, Magic));
|
|
|
|
TestHeaderDecodeError(OutStrm.str(), "invalid GSYM magic 0x0000000c");
|
|
|
|
FW.fixup32(GSYM_MAGIC, offsetof(Header, Magic));
|
|
|
|
FW.fixup32(12, offsetof(Header, Version));
|
|
|
|
TestHeaderDecodeError(OutStrm.str(), "unsupported GSYM version 12");
|
|
|
|
FW.fixup32(GSYM_VERSION, offsetof(Header, Version));
|
|
|
|
FW.fixup32(12, offsetof(Header, AddrOffSize));
|
|
|
|
TestHeaderDecodeError(OutStrm.str(), "invalid address offset size 12");
|
|
|
|
FW.fixup32(4, offsetof(Header, AddrOffSize));
|
|
|
|
FW.fixup32(128, offsetof(Header, UUIDSize));
|
|
|
|
TestHeaderDecodeError(OutStrm.str(), "invalid UUID size 128");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void TestHeaderEncodeDecode(const Header &H,
|
|
|
|
support::endianness ByteOrder) {
|
|
|
|
uint8_t AddressSize = 4;
|
|
|
|
SmallString<512> Str;
|
|
|
|
raw_svector_ostream OutStrm(Str);
|
|
|
|
FileWriter FW(OutStrm, ByteOrder);
|
|
|
|
llvm::Error Err = H.encode(FW);
|
|
|
|
ASSERT_FALSE(Err);
|
|
|
|
std::string Bytes(OutStrm.str());
|
|
|
|
DataExtractor Data(Bytes, ByteOrder == llvm::support::little, AddressSize);
|
|
|
|
llvm::Expected<Header> Decoded = Header::decode(Data);
|
|
|
|
// Make sure decoding succeeded.
|
|
|
|
ASSERT_TRUE((bool)Decoded);
|
|
|
|
EXPECT_EQ(H, Decoded.get());
|
|
|
|
|
|
|
|
}
|
|
|
|
TEST(GSYMTest, TestHeaderEncodeDecode) {
|
|
|
|
Header H;
|
|
|
|
InitHeader(H);
|
|
|
|
TestHeaderEncodeDecode(H, llvm::support::little);
|
|
|
|
TestHeaderEncodeDecode(H, llvm::support::big);
|
|
|
|
}
|
2019-10-11 01:10:11 +08:00
|
|
|
|
|
|
|
static void TestGsymCreatorEncodeError(llvm::support::endianness ByteOrder,
|
|
|
|
const GsymCreator &GC,
|
|
|
|
std::string ExpectedErrorMsg) {
|
|
|
|
SmallString<512> Str;
|
|
|
|
raw_svector_ostream OutStrm(Str);
|
|
|
|
FileWriter FW(OutStrm, ByteOrder);
|
|
|
|
llvm::Error Err = GC.encode(FW);
|
|
|
|
ASSERT_TRUE(bool(Err));
|
|
|
|
checkError(ExpectedErrorMsg, std::move(Err));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(GSYMTest, TestGsymCreatorEncodeErrors) {
|
|
|
|
const uint8_t ValidUUID[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
|
|
|
|
14, 15, 16};
|
|
|
|
const uint8_t InvalidUUID[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
|
|
|
|
14, 15, 16, 17, 18, 19, 20, 21};
|
|
|
|
// Verify we get an error when trying to encode an GsymCreator with no
|
|
|
|
// function infos. We shouldn't be saving a GSYM file in this case since
|
|
|
|
// there is nothing inside of it.
|
|
|
|
GsymCreator GC;
|
|
|
|
TestGsymCreatorEncodeError(llvm::support::little, GC,
|
|
|
|
"no functions to encode");
|
|
|
|
const uint64_t FuncAddr = 0x1000;
|
|
|
|
const uint64_t FuncSize = 0x100;
|
|
|
|
const uint32_t FuncName = GC.insertString("foo");
|
|
|
|
// Verify we get an error trying to encode a GsymCreator that isn't
|
|
|
|
// finalized.
|
|
|
|
GC.addFunctionInfo(FunctionInfo(FuncAddr, FuncSize, FuncName));
|
|
|
|
TestGsymCreatorEncodeError(llvm::support::little, GC,
|
|
|
|
"GsymCreator wasn't finalized prior to encoding");
|
|
|
|
std::string finalizeIssues;
|
|
|
|
raw_string_ostream OS(finalizeIssues);
|
|
|
|
llvm::Error finalizeErr = GC.finalize(OS);
|
|
|
|
ASSERT_FALSE(bool(finalizeErr));
|
|
|
|
finalizeErr = GC.finalize(OS);
|
|
|
|
ASSERT_TRUE(bool(finalizeErr));
|
|
|
|
checkError("already finalized", std::move(finalizeErr));
|
|
|
|
// Verify we get an error trying to encode a GsymCreator with a UUID that is
|
|
|
|
// too long.
|
|
|
|
GC.setUUID(InvalidUUID);
|
|
|
|
TestGsymCreatorEncodeError(llvm::support::little, GC,
|
|
|
|
"invalid UUID size 21");
|
|
|
|
GC.setUUID(ValidUUID);
|
|
|
|
// Verify errors are propagated when we try to encoding an invalid line
|
|
|
|
// table.
|
|
|
|
GC.forEachFunctionInfo([](FunctionInfo &FI) -> bool {
|
|
|
|
FI.OptLineTable = LineTable(); // Invalid line table.
|
|
|
|
return false; // Stop iterating
|
|
|
|
});
|
|
|
|
TestGsymCreatorEncodeError(llvm::support::little, GC,
|
|
|
|
"attempted to encode invalid LineTable object");
|
|
|
|
// Verify errors are propagated when we try to encoding an invalid inline
|
|
|
|
// info.
|
|
|
|
GC.forEachFunctionInfo([](FunctionInfo &FI) -> bool {
|
|
|
|
FI.OptLineTable = llvm::None;
|
|
|
|
FI.Inline = InlineInfo(); // Invalid InlineInfo.
|
|
|
|
return false; // Stop iterating
|
|
|
|
});
|
|
|
|
TestGsymCreatorEncodeError(llvm::support::little, GC,
|
|
|
|
"attempted to encode invalid InlineInfo object");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void Compare(const GsymCreator &GC, const GsymReader &GR) {
|
|
|
|
// Verify that all of the data in a GsymCreator is correctly decoded from
|
|
|
|
// a GsymReader. To do this, we iterator over
|
|
|
|
GC.forEachFunctionInfo([&](const FunctionInfo &FI) -> bool {
|
|
|
|
auto DecodedFI = GR.getFunctionInfo(FI.Range.Start);
|
|
|
|
EXPECT_TRUE(bool(DecodedFI));
|
|
|
|
EXPECT_EQ(FI, *DecodedFI);
|
|
|
|
return true; // Keep iterating over all FunctionInfo objects.
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
static void TestEncodeDecode(const GsymCreator &GC,
|
|
|
|
support::endianness ByteOrder, uint16_t Version,
|
|
|
|
uint8_t AddrOffSize, uint64_t BaseAddress,
|
|
|
|
uint32_t NumAddresses, ArrayRef<uint8_t> UUID) {
|
|
|
|
SmallString<512> Str;
|
|
|
|
raw_svector_ostream OutStrm(Str);
|
|
|
|
FileWriter FW(OutStrm, ByteOrder);
|
|
|
|
llvm::Error Err = GC.encode(FW);
|
|
|
|
ASSERT_FALSE((bool)Err);
|
|
|
|
Expected<GsymReader> GR = GsymReader::copyBuffer(OutStrm.str());
|
|
|
|
ASSERT_TRUE(bool(GR));
|
|
|
|
const Header &Hdr = GR->getHeader();
|
|
|
|
EXPECT_EQ(Hdr.Version, Version);
|
|
|
|
EXPECT_EQ(Hdr.AddrOffSize, AddrOffSize);
|
|
|
|
EXPECT_EQ(Hdr.UUIDSize, UUID.size());
|
|
|
|
EXPECT_EQ(Hdr.BaseAddress, BaseAddress);
|
|
|
|
EXPECT_EQ(Hdr.NumAddresses, NumAddresses);
|
|
|
|
EXPECT_EQ(ArrayRef<uint8_t>(Hdr.UUID, Hdr.UUIDSize), UUID);
|
|
|
|
Compare(GC, GR.get());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(GSYMTest, TestGsymCreator1ByteAddrOffsets) {
|
|
|
|
uint8_t UUID[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
|
|
|
|
GsymCreator GC;
|
|
|
|
GC.setUUID(UUID);
|
|
|
|
constexpr uint64_t BaseAddr = 0x1000;
|
|
|
|
constexpr uint8_t AddrOffSize = 1;
|
|
|
|
const uint32_t Func1Name = GC.insertString("foo");
|
|
|
|
const uint32_t Func2Name = GC.insertString("bar");
|
|
|
|
GC.addFunctionInfo(FunctionInfo(BaseAddr+0x00, 0x10, Func1Name));
|
|
|
|
GC.addFunctionInfo(FunctionInfo(BaseAddr+0x20, 0x10, Func2Name));
|
|
|
|
Error Err = GC.finalize(llvm::nulls());
|
|
|
|
ASSERT_FALSE(Err);
|
|
|
|
TestEncodeDecode(GC, llvm::support::little,
|
|
|
|
GSYM_VERSION,
|
|
|
|
AddrOffSize,
|
|
|
|
BaseAddr,
|
|
|
|
2, // NumAddresses
|
|
|
|
ArrayRef<uint8_t>(UUID));
|
|
|
|
TestEncodeDecode(GC, llvm::support::big,
|
|
|
|
GSYM_VERSION,
|
|
|
|
AddrOffSize,
|
|
|
|
BaseAddr,
|
|
|
|
2, // NumAddresses
|
|
|
|
ArrayRef<uint8_t>(UUID));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(GSYMTest, TestGsymCreator2ByteAddrOffsets) {
|
|
|
|
uint8_t UUID[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
|
|
|
|
GsymCreator GC;
|
|
|
|
GC.setUUID(UUID);
|
|
|
|
constexpr uint64_t BaseAddr = 0x1000;
|
|
|
|
constexpr uint8_t AddrOffSize = 2;
|
|
|
|
const uint32_t Func1Name = GC.insertString("foo");
|
|
|
|
const uint32_t Func2Name = GC.insertString("bar");
|
|
|
|
GC.addFunctionInfo(FunctionInfo(BaseAddr+0x000, 0x100, Func1Name));
|
|
|
|
GC.addFunctionInfo(FunctionInfo(BaseAddr+0x200, 0x100, Func2Name));
|
|
|
|
Error Err = GC.finalize(llvm::nulls());
|
|
|
|
ASSERT_FALSE(Err);
|
|
|
|
TestEncodeDecode(GC, llvm::support::little,
|
|
|
|
GSYM_VERSION,
|
|
|
|
AddrOffSize,
|
|
|
|
BaseAddr,
|
|
|
|
2, // NumAddresses
|
|
|
|
ArrayRef<uint8_t>(UUID));
|
|
|
|
TestEncodeDecode(GC, llvm::support::big,
|
|
|
|
GSYM_VERSION,
|
|
|
|
AddrOffSize,
|
|
|
|
BaseAddr,
|
|
|
|
2, // NumAddresses
|
|
|
|
ArrayRef<uint8_t>(UUID));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(GSYMTest, TestGsymCreator4ByteAddrOffsets) {
|
|
|
|
uint8_t UUID[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
|
|
|
|
GsymCreator GC;
|
|
|
|
GC.setUUID(UUID);
|
|
|
|
constexpr uint64_t BaseAddr = 0x1000;
|
|
|
|
constexpr uint8_t AddrOffSize = 4;
|
|
|
|
const uint32_t Func1Name = GC.insertString("foo");
|
|
|
|
const uint32_t Func2Name = GC.insertString("bar");
|
|
|
|
GC.addFunctionInfo(FunctionInfo(BaseAddr+0x000, 0x100, Func1Name));
|
|
|
|
GC.addFunctionInfo(FunctionInfo(BaseAddr+0x20000, 0x100, Func2Name));
|
|
|
|
Error Err = GC.finalize(llvm::nulls());
|
|
|
|
ASSERT_FALSE(Err);
|
|
|
|
TestEncodeDecode(GC, llvm::support::little,
|
|
|
|
GSYM_VERSION,
|
|
|
|
AddrOffSize,
|
|
|
|
BaseAddr,
|
|
|
|
2, // NumAddresses
|
|
|
|
ArrayRef<uint8_t>(UUID));
|
|
|
|
TestEncodeDecode(GC, llvm::support::big,
|
|
|
|
GSYM_VERSION,
|
|
|
|
AddrOffSize,
|
|
|
|
BaseAddr,
|
|
|
|
2, // NumAddresses
|
|
|
|
ArrayRef<uint8_t>(UUID));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(GSYMTest, TestGsymCreator8ByteAddrOffsets) {
|
|
|
|
uint8_t UUID[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
|
|
|
|
GsymCreator GC;
|
|
|
|
GC.setUUID(UUID);
|
|
|
|
constexpr uint64_t BaseAddr = 0x1000;
|
|
|
|
constexpr uint8_t AddrOffSize = 8;
|
|
|
|
const uint32_t Func1Name = GC.insertString("foo");
|
|
|
|
const uint32_t Func2Name = GC.insertString("bar");
|
|
|
|
GC.addFunctionInfo(FunctionInfo(BaseAddr+0x000, 0x100, Func1Name));
|
|
|
|
GC.addFunctionInfo(FunctionInfo(BaseAddr+0x100000000, 0x100, Func2Name));
|
|
|
|
Error Err = GC.finalize(llvm::nulls());
|
|
|
|
ASSERT_FALSE(Err);
|
|
|
|
TestEncodeDecode(GC, llvm::support::little,
|
|
|
|
GSYM_VERSION,
|
|
|
|
AddrOffSize,
|
|
|
|
BaseAddr,
|
|
|
|
2, // NumAddresses
|
|
|
|
ArrayRef<uint8_t>(UUID));
|
|
|
|
TestEncodeDecode(GC, llvm::support::big,
|
|
|
|
GSYM_VERSION,
|
|
|
|
AddrOffSize,
|
|
|
|
BaseAddr,
|
|
|
|
2, // NumAddresses
|
|
|
|
ArrayRef<uint8_t>(UUID));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VerifyFunctionInfo(const GsymReader &GR, uint64_t Addr,
|
|
|
|
const FunctionInfo &FI) {
|
|
|
|
auto ExpFI = GR.getFunctionInfo(Addr);
|
|
|
|
ASSERT_TRUE(bool(ExpFI));
|
|
|
|
ASSERT_EQ(FI, ExpFI.get());
|
|
|
|
}
|
|
|
|
|
|
|
|
static void VerifyFunctionInfoError(const GsymReader &GR, uint64_t Addr,
|
|
|
|
std::string ErrMessage) {
|
|
|
|
auto ExpFI = GR.getFunctionInfo(Addr);
|
|
|
|
ASSERT_FALSE(bool(ExpFI));
|
|
|
|
checkError(ErrMessage, ExpFI.takeError());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(GSYMTest, TestGsymReader) {
|
|
|
|
uint8_t UUID[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
|
|
|
|
GsymCreator GC;
|
|
|
|
GC.setUUID(UUID);
|
|
|
|
constexpr uint64_t BaseAddr = 0x1000;
|
|
|
|
constexpr uint64_t Func1Addr = BaseAddr;
|
|
|
|
constexpr uint64_t Func2Addr = BaseAddr+0x20;
|
|
|
|
constexpr uint64_t FuncSize = 0x10;
|
|
|
|
const uint32_t Func1Name = GC.insertString("foo");
|
|
|
|
const uint32_t Func2Name = GC.insertString("bar");
|
|
|
|
const auto ByteOrder = support::endian::system_endianness();
|
|
|
|
GC.addFunctionInfo(FunctionInfo(Func1Addr, FuncSize, Func1Name));
|
|
|
|
GC.addFunctionInfo(FunctionInfo(Func2Addr, FuncSize, Func2Name));
|
|
|
|
Error FinalizeErr = GC.finalize(llvm::nulls());
|
|
|
|
ASSERT_FALSE(FinalizeErr);
|
|
|
|
SmallString<512> Str;
|
|
|
|
raw_svector_ostream OutStrm(Str);
|
|
|
|
FileWriter FW(OutStrm, ByteOrder);
|
|
|
|
llvm::Error Err = GC.encode(FW);
|
|
|
|
ASSERT_FALSE((bool)Err);
|
|
|
|
if (auto ExpectedGR = GsymReader::copyBuffer(OutStrm.str())) {
|
|
|
|
const GsymReader &GR = ExpectedGR.get();
|
2020-02-16 08:46:50 +08:00
|
|
|
VerifyFunctionInfoError(GR, Func1Addr-1, "address 0xfff is not in GSYM");
|
2019-10-11 01:10:11 +08:00
|
|
|
|
|
|
|
FunctionInfo Func1(Func1Addr, FuncSize, Func1Name);
|
|
|
|
VerifyFunctionInfo(GR, Func1Addr, Func1);
|
|
|
|
VerifyFunctionInfo(GR, Func1Addr+1, Func1);
|
|
|
|
VerifyFunctionInfo(GR, Func1Addr+FuncSize-1, Func1);
|
|
|
|
VerifyFunctionInfoError(GR, Func1Addr+FuncSize,
|
2020-02-16 08:46:50 +08:00
|
|
|
"address 0x1010 is not in GSYM");
|
|
|
|
VerifyFunctionInfoError(GR, Func2Addr-1, "address 0x101f is not in GSYM");
|
2019-10-11 01:10:11 +08:00
|
|
|
FunctionInfo Func2(Func2Addr, FuncSize, Func2Name);
|
|
|
|
VerifyFunctionInfo(GR, Func2Addr, Func2);
|
|
|
|
VerifyFunctionInfo(GR, Func2Addr+1, Func2);
|
|
|
|
VerifyFunctionInfo(GR, Func2Addr+FuncSize-1, Func2);
|
|
|
|
VerifyFunctionInfoError(GR, Func2Addr+FuncSize,
|
2020-02-16 08:46:50 +08:00
|
|
|
"address 0x1030 is not in GSYM");
|
2019-10-11 01:10:11 +08:00
|
|
|
}
|
|
|
|
}
|
Add lookup functions for efficient lookups of addresses when using GsymReader classes.
Summary:
Lookup functions are designed to not fully decode a FunctionInfo, LineTable or InlineInfo, they decode only what is needed into a LookupResult object. This allows lookups to avoid costly memory allocations and avoid parsing large amounts of information one a suitable match is found.
LookupResult objects contain the address that was looked up, the concrete function address range, the name of the concrete function, and a list of source locations. One for each inline function, and one for the concrete function. This allows one address to turn into multiple frames and improves the signal you get when symbolicating addresses in GSYM files.
Reviewers: labath, aprantl
Subscribers: mgorny, hiraditya, llvm-commits, lldb-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D70993
2019-12-04 08:44:02 +08:00
|
|
|
|
|
|
|
TEST(GSYMTest, TestGsymLookups) {
|
|
|
|
// Test creating a GSYM file with a function that has a inline information.
|
|
|
|
// Verify that lookups work correctly. Lookups do not decode the entire
|
|
|
|
// FunctionInfo or InlineInfo, they only extract information needed for the
|
|
|
|
// lookup to happen which avoids allocations which can slow down
|
|
|
|
// symbolication.
|
|
|
|
GsymCreator GC;
|
|
|
|
FunctionInfo FI(0x1000, 0x100, GC.insertString("main"));
|
|
|
|
const auto ByteOrder = support::endian::system_endianness();
|
|
|
|
FI.OptLineTable = LineTable();
|
|
|
|
const uint32_t MainFileIndex = GC.insertFile("/tmp/main.c");
|
|
|
|
const uint32_t FooFileIndex = GC.insertFile("/tmp/foo.h");
|
|
|
|
FI.OptLineTable->push(LineEntry(0x1000, MainFileIndex, 5));
|
|
|
|
FI.OptLineTable->push(LineEntry(0x1010, FooFileIndex, 10));
|
|
|
|
FI.OptLineTable->push(LineEntry(0x1012, FooFileIndex, 20));
|
|
|
|
FI.OptLineTable->push(LineEntry(0x1014, FooFileIndex, 11));
|
|
|
|
FI.OptLineTable->push(LineEntry(0x1016, FooFileIndex, 30));
|
|
|
|
FI.OptLineTable->push(LineEntry(0x1018, FooFileIndex, 12));
|
|
|
|
FI.OptLineTable->push(LineEntry(0x1020, MainFileIndex, 8));
|
|
|
|
FI.Inline = InlineInfo();
|
|
|
|
|
|
|
|
FI.Inline->Name = GC.insertString("inline1");
|
|
|
|
FI.Inline->CallFile = MainFileIndex;
|
|
|
|
FI.Inline->CallLine = 6;
|
|
|
|
FI.Inline->Ranges.insert(AddressRange(0x1010, 0x1020));
|
|
|
|
InlineInfo Inline2;
|
|
|
|
Inline2.Name = GC.insertString("inline2");
|
|
|
|
Inline2.CallFile = FooFileIndex;
|
|
|
|
Inline2.CallLine = 33;
|
|
|
|
Inline2.Ranges.insert(AddressRange(0x1012, 0x1014));
|
|
|
|
FI.Inline->Children.emplace_back(Inline2);
|
|
|
|
InlineInfo Inline3;
|
|
|
|
Inline3.Name = GC.insertString("inline3");
|
|
|
|
Inline3.CallFile = FooFileIndex;
|
|
|
|
Inline3.CallLine = 35;
|
|
|
|
Inline3.Ranges.insert(AddressRange(0x1016, 0x1018));
|
|
|
|
FI.Inline->Children.emplace_back(Inline3);
|
|
|
|
GC.addFunctionInfo(std::move(FI));
|
|
|
|
Error FinalizeErr = GC.finalize(llvm::nulls());
|
|
|
|
ASSERT_FALSE(FinalizeErr);
|
|
|
|
SmallString<512> Str;
|
|
|
|
raw_svector_ostream OutStrm(Str);
|
|
|
|
FileWriter FW(OutStrm, ByteOrder);
|
|
|
|
llvm::Error Err = GC.encode(FW);
|
|
|
|
ASSERT_FALSE((bool)Err);
|
|
|
|
Expected<GsymReader> GR = GsymReader::copyBuffer(OutStrm.str());
|
|
|
|
ASSERT_TRUE(bool(GR));
|
|
|
|
|
|
|
|
// Verify inline info is correct when doing lookups.
|
|
|
|
auto LR = GR->lookup(0x1000);
|
|
|
|
ASSERT_THAT_EXPECTED(LR, Succeeded());
|
|
|
|
EXPECT_THAT(LR->Locations,
|
|
|
|
testing::ElementsAre(SourceLocation{"main", "/tmp", "main.c", 5}));
|
|
|
|
LR = GR->lookup(0x100F);
|
|
|
|
ASSERT_THAT_EXPECTED(LR, Succeeded());
|
|
|
|
EXPECT_THAT(LR->Locations,
|
2020-02-16 13:28:36 +08:00
|
|
|
testing::ElementsAre(SourceLocation{"main", "/tmp", "main.c", 5, 15}));
|
Add lookup functions for efficient lookups of addresses when using GsymReader classes.
Summary:
Lookup functions are designed to not fully decode a FunctionInfo, LineTable or InlineInfo, they decode only what is needed into a LookupResult object. This allows lookups to avoid costly memory allocations and avoid parsing large amounts of information one a suitable match is found.
LookupResult objects contain the address that was looked up, the concrete function address range, the name of the concrete function, and a list of source locations. One for each inline function, and one for the concrete function. This allows one address to turn into multiple frames and improves the signal you get when symbolicating addresses in GSYM files.
Reviewers: labath, aprantl
Subscribers: mgorny, hiraditya, llvm-commits, lldb-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D70993
2019-12-04 08:44:02 +08:00
|
|
|
|
|
|
|
LR = GR->lookup(0x1010);
|
|
|
|
ASSERT_THAT_EXPECTED(LR, Succeeded());
|
|
|
|
|
|
|
|
EXPECT_THAT(LR->Locations,
|
|
|
|
testing::ElementsAre(SourceLocation{"inline1", "/tmp", "foo.h", 10},
|
2020-02-16 13:28:36 +08:00
|
|
|
SourceLocation{"main", "/tmp", "main.c", 6, 16}));
|
Add lookup functions for efficient lookups of addresses when using GsymReader classes.
Summary:
Lookup functions are designed to not fully decode a FunctionInfo, LineTable or InlineInfo, they decode only what is needed into a LookupResult object. This allows lookups to avoid costly memory allocations and avoid parsing large amounts of information one a suitable match is found.
LookupResult objects contain the address that was looked up, the concrete function address range, the name of the concrete function, and a list of source locations. One for each inline function, and one for the concrete function. This allows one address to turn into multiple frames and improves the signal you get when symbolicating addresses in GSYM files.
Reviewers: labath, aprantl
Subscribers: mgorny, hiraditya, llvm-commits, lldb-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D70993
2019-12-04 08:44:02 +08:00
|
|
|
|
|
|
|
LR = GR->lookup(0x1012);
|
|
|
|
ASSERT_THAT_EXPECTED(LR, Succeeded());
|
|
|
|
EXPECT_THAT(LR->Locations,
|
|
|
|
testing::ElementsAre(SourceLocation{"inline2", "/tmp", "foo.h", 20},
|
2020-02-16 13:28:36 +08:00
|
|
|
SourceLocation{"inline1", "/tmp", "foo.h", 33, 2},
|
|
|
|
SourceLocation{"main", "/tmp", "main.c", 6, 18}));
|
Add lookup functions for efficient lookups of addresses when using GsymReader classes.
Summary:
Lookup functions are designed to not fully decode a FunctionInfo, LineTable or InlineInfo, they decode only what is needed into a LookupResult object. This allows lookups to avoid costly memory allocations and avoid parsing large amounts of information one a suitable match is found.
LookupResult objects contain the address that was looked up, the concrete function address range, the name of the concrete function, and a list of source locations. One for each inline function, and one for the concrete function. This allows one address to turn into multiple frames and improves the signal you get when symbolicating addresses in GSYM files.
Reviewers: labath, aprantl
Subscribers: mgorny, hiraditya, llvm-commits, lldb-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D70993
2019-12-04 08:44:02 +08:00
|
|
|
|
|
|
|
LR = GR->lookup(0x1014);
|
|
|
|
ASSERT_THAT_EXPECTED(LR, Succeeded());
|
|
|
|
EXPECT_THAT(LR->Locations,
|
2020-02-16 13:28:36 +08:00
|
|
|
testing::ElementsAre(SourceLocation{"inline1", "/tmp", "foo.h", 11, 4},
|
|
|
|
SourceLocation{"main", "/tmp", "main.c", 6, 20}));
|
Add lookup functions for efficient lookups of addresses when using GsymReader classes.
Summary:
Lookup functions are designed to not fully decode a FunctionInfo, LineTable or InlineInfo, they decode only what is needed into a LookupResult object. This allows lookups to avoid costly memory allocations and avoid parsing large amounts of information one a suitable match is found.
LookupResult objects contain the address that was looked up, the concrete function address range, the name of the concrete function, and a list of source locations. One for each inline function, and one for the concrete function. This allows one address to turn into multiple frames and improves the signal you get when symbolicating addresses in GSYM files.
Reviewers: labath, aprantl
Subscribers: mgorny, hiraditya, llvm-commits, lldb-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D70993
2019-12-04 08:44:02 +08:00
|
|
|
|
|
|
|
LR = GR->lookup(0x1016);
|
|
|
|
ASSERT_THAT_EXPECTED(LR, Succeeded());
|
|
|
|
EXPECT_THAT(LR->Locations,
|
|
|
|
testing::ElementsAre(SourceLocation{"inline3", "/tmp", "foo.h", 30},
|
2020-02-16 13:28:36 +08:00
|
|
|
SourceLocation{"inline1", "/tmp", "foo.h", 35, 6},
|
|
|
|
SourceLocation{"main", "/tmp", "main.c", 6, 22}));
|
Add lookup functions for efficient lookups of addresses when using GsymReader classes.
Summary:
Lookup functions are designed to not fully decode a FunctionInfo, LineTable or InlineInfo, they decode only what is needed into a LookupResult object. This allows lookups to avoid costly memory allocations and avoid parsing large amounts of information one a suitable match is found.
LookupResult objects contain the address that was looked up, the concrete function address range, the name of the concrete function, and a list of source locations. One for each inline function, and one for the concrete function. This allows one address to turn into multiple frames and improves the signal you get when symbolicating addresses in GSYM files.
Reviewers: labath, aprantl
Subscribers: mgorny, hiraditya, llvm-commits, lldb-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D70993
2019-12-04 08:44:02 +08:00
|
|
|
|
|
|
|
LR = GR->lookup(0x1018);
|
|
|
|
ASSERT_THAT_EXPECTED(LR, Succeeded());
|
|
|
|
EXPECT_THAT(LR->Locations,
|
2020-02-16 13:28:36 +08:00
|
|
|
testing::ElementsAre(SourceLocation{"inline1", "/tmp", "foo.h", 12, 8},
|
|
|
|
SourceLocation{"main", "/tmp", "main.c", 6, 24}));
|
Add lookup functions for efficient lookups of addresses when using GsymReader classes.
Summary:
Lookup functions are designed to not fully decode a FunctionInfo, LineTable or InlineInfo, they decode only what is needed into a LookupResult object. This allows lookups to avoid costly memory allocations and avoid parsing large amounts of information one a suitable match is found.
LookupResult objects contain the address that was looked up, the concrete function address range, the name of the concrete function, and a list of source locations. One for each inline function, and one for the concrete function. This allows one address to turn into multiple frames and improves the signal you get when symbolicating addresses in GSYM files.
Reviewers: labath, aprantl
Subscribers: mgorny, hiraditya, llvm-commits, lldb-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D70993
2019-12-04 08:44:02 +08:00
|
|
|
|
|
|
|
LR = GR->lookup(0x1020);
|
|
|
|
ASSERT_THAT_EXPECTED(LR, Succeeded());
|
|
|
|
EXPECT_THAT(LR->Locations,
|
2020-02-16 13:28:36 +08:00
|
|
|
testing::ElementsAre(SourceLocation{"main", "/tmp", "main.c", 8, 32}));
|
Add lookup functions for efficient lookups of addresses when using GsymReader classes.
Summary:
Lookup functions are designed to not fully decode a FunctionInfo, LineTable or InlineInfo, they decode only what is needed into a LookupResult object. This allows lookups to avoid costly memory allocations and avoid parsing large amounts of information one a suitable match is found.
LookupResult objects contain the address that was looked up, the concrete function address range, the name of the concrete function, and a list of source locations. One for each inline function, and one for the concrete function. This allows one address to turn into multiple frames and improves the signal you get when symbolicating addresses in GSYM files.
Reviewers: labath, aprantl
Subscribers: mgorny, hiraditya, llvm-commits, lldb-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D70993
2019-12-04 08:44:02 +08:00
|
|
|
}
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
|
|
|
|
|
|
|
|
TEST(GSYMTest, TestDWARFFunctionWithAddresses) {
|
|
|
|
// Create a single compile unit with a single function and make sure it gets
|
|
|
|
// converted to DWARF correctly. The function's address range is in where
|
|
|
|
// DW_AT_low_pc and DW_AT_high_pc are both addresses.
|
|
|
|
StringRef yamldata = R"(
|
|
|
|
debug_str:
|
|
|
|
- ''
|
|
|
|
- /tmp/main.c
|
|
|
|
- main
|
|
|
|
debug_abbrev:
|
2020-08-21 10:11:33 +08:00
|
|
|
- Table:
|
|
|
|
- Code: 0x00000001
|
|
|
|
Tag: DW_TAG_compile_unit
|
|
|
|
Children: DW_CHILDREN_yes
|
|
|
|
Attributes:
|
|
|
|
- Attribute: DW_AT_name
|
|
|
|
Form: DW_FORM_strp
|
|
|
|
- Attribute: DW_AT_low_pc
|
|
|
|
Form: DW_FORM_addr
|
|
|
|
- Attribute: DW_AT_high_pc
|
|
|
|
Form: DW_FORM_addr
|
|
|
|
- Attribute: DW_AT_language
|
|
|
|
Form: DW_FORM_data2
|
|
|
|
- Code: 0x00000002
|
|
|
|
Tag: DW_TAG_subprogram
|
|
|
|
Children: DW_CHILDREN_no
|
|
|
|
Attributes:
|
|
|
|
- Attribute: DW_AT_name
|
|
|
|
Form: DW_FORM_strp
|
|
|
|
- Attribute: DW_AT_low_pc
|
|
|
|
Form: DW_FORM_addr
|
|
|
|
- Attribute: DW_AT_high_pc
|
|
|
|
Form: DW_FORM_addr
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
debug_info:
|
2020-08-31 14:03:02 +08:00
|
|
|
- Version: 4
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
AddrSize: 8
|
|
|
|
Entries:
|
|
|
|
- AbbrCode: 0x00000001
|
|
|
|
Values:
|
|
|
|
- Value: 0x0000000000000001
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- Value: 0x0000000000002000
|
|
|
|
- Value: 0x0000000000000004
|
|
|
|
- AbbrCode: 0x00000002
|
|
|
|
Values:
|
|
|
|
- Value: 0x000000000000000D
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- Value: 0x0000000000002000
|
|
|
|
- AbbrCode: 0x00000000
|
|
|
|
)";
|
2020-06-08 17:30:23 +08:00
|
|
|
auto ErrOrSections = DWARFYAML::emitDebugSections(yamldata);
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
ASSERT_THAT_EXPECTED(ErrOrSections, Succeeded());
|
|
|
|
std::unique_ptr<DWARFContext> DwarfContext =
|
|
|
|
DWARFContext::create(*ErrOrSections, 8);
|
|
|
|
ASSERT_TRUE(DwarfContext.get() != nullptr);
|
|
|
|
auto &OS = llvm::nulls();
|
|
|
|
GsymCreator GC;
|
|
|
|
DwarfTransformer DT(*DwarfContext, OS, GC);
|
|
|
|
const uint32_t ThreadCount = 1;
|
|
|
|
ASSERT_THAT_ERROR(DT.convert(ThreadCount), Succeeded());
|
|
|
|
ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded());
|
|
|
|
SmallString<512> Str;
|
|
|
|
raw_svector_ostream OutStrm(Str);
|
|
|
|
const auto ByteOrder = support::endian::system_endianness();
|
|
|
|
FileWriter FW(OutStrm, ByteOrder);
|
|
|
|
ASSERT_THAT_ERROR(GC.encode(FW), Succeeded());
|
|
|
|
Expected<GsymReader> GR = GsymReader::copyBuffer(OutStrm.str());
|
|
|
|
ASSERT_THAT_EXPECTED(GR, Succeeded());
|
|
|
|
// There should only be one function in our GSYM.
|
|
|
|
EXPECT_EQ(GR->getNumAddresses(), 1u);
|
|
|
|
auto ExpFI = GR->getFunctionInfo(0x1000);
|
|
|
|
ASSERT_THAT_EXPECTED(ExpFI, Succeeded());
|
|
|
|
ASSERT_EQ(ExpFI->Range, AddressRange(0x1000, 0x2000));
|
|
|
|
EXPECT_FALSE(ExpFI->OptLineTable.hasValue());
|
|
|
|
EXPECT_FALSE(ExpFI->Inline.hasValue());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(GSYMTest, TestDWARFFunctionWithAddressAndOffset) {
|
|
|
|
// Create a single compile unit with a single function and make sure it gets
|
|
|
|
// converted to DWARF correctly. The function's address range is in where
|
|
|
|
// DW_AT_low_pc is an address and the DW_AT_high_pc is an offset.
|
|
|
|
StringRef yamldata = R"(
|
|
|
|
debug_str:
|
|
|
|
- ''
|
|
|
|
- /tmp/main.c
|
|
|
|
- main
|
|
|
|
debug_abbrev:
|
2020-08-21 10:11:33 +08:00
|
|
|
- Table:
|
|
|
|
- Code: 0x00000001
|
|
|
|
Tag: DW_TAG_compile_unit
|
|
|
|
Children: DW_CHILDREN_yes
|
|
|
|
Attributes:
|
|
|
|
- Attribute: DW_AT_name
|
|
|
|
Form: DW_FORM_strp
|
|
|
|
- Attribute: DW_AT_low_pc
|
|
|
|
Form: DW_FORM_addr
|
|
|
|
- Attribute: DW_AT_high_pc
|
|
|
|
Form: DW_FORM_data4
|
|
|
|
- Attribute: DW_AT_language
|
|
|
|
Form: DW_FORM_data2
|
|
|
|
- Code: 0x00000002
|
|
|
|
Tag: DW_TAG_subprogram
|
|
|
|
Children: DW_CHILDREN_no
|
|
|
|
Attributes:
|
|
|
|
- Attribute: DW_AT_name
|
|
|
|
Form: DW_FORM_strp
|
|
|
|
- Attribute: DW_AT_low_pc
|
|
|
|
Form: DW_FORM_addr
|
|
|
|
- Attribute: DW_AT_high_pc
|
|
|
|
Form: DW_FORM_data4
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
debug_info:
|
2020-08-31 14:03:02 +08:00
|
|
|
- Version: 4
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
AddrSize: 8
|
|
|
|
Entries:
|
|
|
|
- AbbrCode: 0x00000001
|
|
|
|
Values:
|
|
|
|
- Value: 0x0000000000000001
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- Value: 0x0000000000000004
|
|
|
|
- AbbrCode: 0x00000002
|
|
|
|
Values:
|
|
|
|
- Value: 0x000000000000000D
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- AbbrCode: 0x00000000
|
|
|
|
)";
|
2020-06-08 17:30:23 +08:00
|
|
|
auto ErrOrSections = DWARFYAML::emitDebugSections(yamldata);
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
ASSERT_THAT_EXPECTED(ErrOrSections, Succeeded());
|
|
|
|
std::unique_ptr<DWARFContext> DwarfContext =
|
|
|
|
DWARFContext::create(*ErrOrSections, 8);
|
|
|
|
ASSERT_TRUE(DwarfContext.get() != nullptr);
|
|
|
|
auto &OS = llvm::nulls();
|
|
|
|
GsymCreator GC;
|
|
|
|
DwarfTransformer DT(*DwarfContext, OS, GC);
|
|
|
|
const uint32_t ThreadCount = 1;
|
|
|
|
ASSERT_THAT_ERROR(DT.convert(ThreadCount), Succeeded());
|
|
|
|
ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded());
|
|
|
|
SmallString<512> Str;
|
|
|
|
raw_svector_ostream OutStrm(Str);
|
|
|
|
const auto ByteOrder = support::endian::system_endianness();
|
|
|
|
FileWriter FW(OutStrm, ByteOrder);
|
|
|
|
ASSERT_THAT_ERROR(GC.encode(FW), Succeeded());
|
|
|
|
Expected<GsymReader> GR = GsymReader::copyBuffer(OutStrm.str());
|
|
|
|
ASSERT_THAT_EXPECTED(GR, Succeeded());
|
|
|
|
// There should only be one function in our GSYM.
|
|
|
|
EXPECT_EQ(GR->getNumAddresses(), 1u);
|
|
|
|
auto ExpFI = GR->getFunctionInfo(0x1000);
|
|
|
|
ASSERT_THAT_EXPECTED(ExpFI, Succeeded());
|
|
|
|
ASSERT_EQ(ExpFI->Range, AddressRange(0x1000, 0x2000));
|
|
|
|
EXPECT_FALSE(ExpFI->OptLineTable.hasValue());
|
|
|
|
EXPECT_FALSE(ExpFI->Inline.hasValue());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(GSYMTest, TestDWARFStructMethodNoMangled) {
|
|
|
|
// Sometimes the compiler will omit the mangled name in the DWARF for static
|
|
|
|
// and member functions of classes and structs. This test verifies that the
|
|
|
|
// fully qualified name of the method is computed and used as the string for
|
|
|
|
// the function in the GSYM in these cases. Otherwise we might just get a
|
|
|
|
// function name like "erase" instead of "std::vector<int>::erase".
|
|
|
|
StringRef yamldata = R"(
|
|
|
|
debug_str:
|
|
|
|
- ''
|
|
|
|
- /tmp/main.c
|
|
|
|
- Foo
|
|
|
|
- dump
|
|
|
|
- this
|
|
|
|
debug_abbrev:
|
2020-08-21 10:11:33 +08:00
|
|
|
- Table:
|
|
|
|
- Code: 0x00000001
|
|
|
|
Tag: DW_TAG_compile_unit
|
|
|
|
Children: DW_CHILDREN_yes
|
|
|
|
Attributes:
|
|
|
|
- Attribute: DW_AT_name
|
|
|
|
Form: DW_FORM_strp
|
|
|
|
- Attribute: DW_AT_low_pc
|
|
|
|
Form: DW_FORM_addr
|
|
|
|
- Attribute: DW_AT_high_pc
|
|
|
|
Form: DW_FORM_addr
|
|
|
|
- Attribute: DW_AT_language
|
|
|
|
Form: DW_FORM_data2
|
|
|
|
- Code: 0x00000002
|
|
|
|
Tag: DW_TAG_structure_type
|
|
|
|
Children: DW_CHILDREN_yes
|
|
|
|
Attributes:
|
|
|
|
- Attribute: DW_AT_name
|
|
|
|
Form: DW_FORM_strp
|
|
|
|
- Code: 0x00000003
|
|
|
|
Tag: DW_TAG_subprogram
|
|
|
|
Children: DW_CHILDREN_yes
|
|
|
|
Attributes:
|
|
|
|
- Attribute: DW_AT_name
|
|
|
|
Form: DW_FORM_strp
|
|
|
|
- Attribute: DW_AT_low_pc
|
|
|
|
Form: DW_FORM_addr
|
|
|
|
- Attribute: DW_AT_high_pc
|
|
|
|
Form: DW_FORM_addr
|
|
|
|
- Code: 0x00000004
|
|
|
|
Tag: DW_TAG_formal_parameter
|
|
|
|
Children: DW_CHILDREN_no
|
|
|
|
Attributes:
|
|
|
|
- Attribute: DW_AT_name
|
|
|
|
Form: DW_FORM_strp
|
|
|
|
- Attribute: DW_AT_type
|
|
|
|
Form: DW_FORM_ref4
|
|
|
|
- Attribute: DW_AT_artificial
|
|
|
|
Form: DW_FORM_flag_present
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
debug_info:
|
2020-08-31 14:03:02 +08:00
|
|
|
- Version: 4
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
AddrSize: 8
|
|
|
|
Entries:
|
|
|
|
- AbbrCode: 0x00000001
|
|
|
|
Values:
|
|
|
|
- Value: 0x0000000000000001
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- Value: 0x0000000000002000
|
|
|
|
- Value: 0x0000000000000004
|
|
|
|
- AbbrCode: 0x00000002
|
|
|
|
Values:
|
|
|
|
- Value: 0x000000000000000D
|
|
|
|
- AbbrCode: 0x00000003
|
|
|
|
Values:
|
|
|
|
- Value: 0x0000000000000011
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- Value: 0x0000000000002000
|
|
|
|
- AbbrCode: 0x00000004
|
|
|
|
Values:
|
|
|
|
- Value: 0x0000000000000016
|
|
|
|
- Value: 0x0000000000000022
|
|
|
|
- Value: 0x0000000000000001
|
|
|
|
- AbbrCode: 0x00000000
|
|
|
|
- AbbrCode: 0x00000000
|
|
|
|
- AbbrCode: 0x00000000
|
|
|
|
)";
|
2020-06-08 17:30:23 +08:00
|
|
|
auto ErrOrSections = DWARFYAML::emitDebugSections(yamldata);
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
ASSERT_THAT_EXPECTED(ErrOrSections, Succeeded());
|
|
|
|
std::unique_ptr<DWARFContext> DwarfContext =
|
|
|
|
DWARFContext::create(*ErrOrSections, 8);
|
|
|
|
ASSERT_TRUE(DwarfContext.get() != nullptr);
|
|
|
|
auto &OS = llvm::nulls();
|
|
|
|
GsymCreator GC;
|
|
|
|
DwarfTransformer DT(*DwarfContext, OS, GC);
|
|
|
|
const uint32_t ThreadCount = 1;
|
|
|
|
ASSERT_THAT_ERROR(DT.convert(ThreadCount), Succeeded());
|
|
|
|
ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded());
|
|
|
|
SmallString<512> Str;
|
|
|
|
raw_svector_ostream OutStrm(Str);
|
|
|
|
const auto ByteOrder = support::endian::system_endianness();
|
|
|
|
FileWriter FW(OutStrm, ByteOrder);
|
|
|
|
ASSERT_THAT_ERROR(GC.encode(FW), Succeeded());
|
|
|
|
Expected<GsymReader> GR = GsymReader::copyBuffer(OutStrm.str());
|
|
|
|
ASSERT_THAT_EXPECTED(GR, Succeeded());
|
|
|
|
// There should only be one function in our GSYM.
|
|
|
|
EXPECT_EQ(GR->getNumAddresses(), 1u);
|
|
|
|
auto ExpFI = GR->getFunctionInfo(0x1000);
|
|
|
|
ASSERT_THAT_EXPECTED(ExpFI, Succeeded());
|
|
|
|
ASSERT_EQ(ExpFI->Range, AddressRange(0x1000, 0x2000));
|
|
|
|
EXPECT_FALSE(ExpFI->OptLineTable.hasValue());
|
|
|
|
EXPECT_FALSE(ExpFI->Inline.hasValue());
|
|
|
|
StringRef MethodName = GR->getString(ExpFI->Name);
|
|
|
|
EXPECT_EQ(MethodName, "Foo::dump");
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(GSYMTest, TestDWARFTextRanges) {
|
|
|
|
// Linkers don't understand DWARF, they just like to concatenate and
|
|
|
|
// relocate data within the DWARF sections. This means that if a function
|
|
|
|
// gets dead stripped, and if those functions use an offset as the
|
|
|
|
// DW_AT_high_pc, we can end up with many functions at address zero. The
|
|
|
|
// DwarfTransformer allows clients to specify valid .text address ranges
|
|
|
|
// and any addresses of any functions must fall within those ranges if any
|
|
|
|
// have been specified. This means that an object file can calcuate the
|
|
|
|
// address ranges within the binary where code lives and set these ranges
|
|
|
|
// as constraints in the DwarfTransformer. ObjectFile instances can
|
|
|
|
// add a address ranges of sections that have executable permissions. This
|
|
|
|
// keeps bad information from being added to a GSYM file and causing issues
|
|
|
|
// when symbolicating.
|
|
|
|
StringRef yamldata = R"(
|
|
|
|
debug_str:
|
|
|
|
- ''
|
|
|
|
- /tmp/main.c
|
|
|
|
- main
|
|
|
|
- dead_stripped
|
|
|
|
- dead_stripped2
|
|
|
|
debug_abbrev:
|
2020-08-21 10:11:33 +08:00
|
|
|
- Table:
|
|
|
|
- Code: 0x00000001
|
|
|
|
Tag: DW_TAG_compile_unit
|
|
|
|
Children: DW_CHILDREN_yes
|
|
|
|
Attributes:
|
|
|
|
- Attribute: DW_AT_name
|
|
|
|
Form: DW_FORM_strp
|
|
|
|
- Attribute: DW_AT_low_pc
|
|
|
|
Form: DW_FORM_addr
|
|
|
|
- Attribute: DW_AT_high_pc
|
|
|
|
Form: DW_FORM_data4
|
|
|
|
- Attribute: DW_AT_language
|
|
|
|
Form: DW_FORM_data2
|
|
|
|
- Code: 0x00000002
|
|
|
|
Tag: DW_TAG_subprogram
|
|
|
|
Children: DW_CHILDREN_no
|
|
|
|
Attributes:
|
|
|
|
- Attribute: DW_AT_name
|
|
|
|
Form: DW_FORM_strp
|
|
|
|
- Attribute: DW_AT_low_pc
|
|
|
|
Form: DW_FORM_addr
|
|
|
|
- Attribute: DW_AT_high_pc
|
|
|
|
Form: DW_FORM_data4
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
debug_info:
|
2020-08-31 14:03:02 +08:00
|
|
|
- Version: 4
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
AddrSize: 8
|
|
|
|
Entries:
|
|
|
|
- AbbrCode: 0x00000001
|
|
|
|
Values:
|
|
|
|
- Value: 0x0000000000000001
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- Value: 0x0000000000000004
|
|
|
|
- AbbrCode: 0x00000002
|
|
|
|
Values:
|
|
|
|
- Value: 0x000000000000000D
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- AbbrCode: 0x00000002
|
|
|
|
Values:
|
|
|
|
- Value: 0x0000000000000012
|
|
|
|
- Value: 0x0000000000000000
|
|
|
|
- Value: 0x0000000000000100
|
|
|
|
- AbbrCode: 0x00000002
|
|
|
|
Values:
|
|
|
|
- Value: 0x0000000000000020
|
|
|
|
- Value: 0x0000000000000000
|
|
|
|
- Value: 0x0000000000000040
|
|
|
|
- AbbrCode: 0x00000000
|
|
|
|
)";
|
2020-06-08 17:30:23 +08:00
|
|
|
auto ErrOrSections = DWARFYAML::emitDebugSections(yamldata);
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
ASSERT_THAT_EXPECTED(ErrOrSections, Succeeded());
|
|
|
|
std::unique_ptr<DWARFContext> DwarfContext =
|
|
|
|
DWARFContext::create(*ErrOrSections, 8);
|
|
|
|
ASSERT_TRUE(DwarfContext.get() != nullptr);
|
|
|
|
auto &OS = llvm::nulls();
|
|
|
|
GsymCreator GC;
|
|
|
|
DwarfTransformer DT(*DwarfContext, OS, GC);
|
|
|
|
// Only allow addresses between [0x1000 - 0x2000) to be linked into the
|
|
|
|
// GSYM.
|
|
|
|
AddressRanges TextRanges;
|
|
|
|
TextRanges.insert(AddressRange(0x1000, 0x2000));
|
2020-02-16 08:46:50 +08:00
|
|
|
GC.SetValidTextRanges(TextRanges);
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
const uint32_t ThreadCount = 1;
|
|
|
|
ASSERT_THAT_ERROR(DT.convert(ThreadCount), Succeeded());
|
|
|
|
ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded());
|
|
|
|
SmallString<512> Str;
|
|
|
|
raw_svector_ostream OutStrm(Str);
|
|
|
|
const auto ByteOrder = support::endian::system_endianness();
|
|
|
|
FileWriter FW(OutStrm, ByteOrder);
|
|
|
|
ASSERT_THAT_ERROR(GC.encode(FW), Succeeded());
|
|
|
|
Expected<GsymReader> GR = GsymReader::copyBuffer(OutStrm.str());
|
|
|
|
ASSERT_THAT_EXPECTED(GR, Succeeded());
|
|
|
|
// There should only be one function in our GSYM.
|
|
|
|
EXPECT_EQ(GR->getNumAddresses(), 1u);
|
|
|
|
auto ExpFI = GR->getFunctionInfo(0x1000);
|
|
|
|
ASSERT_THAT_EXPECTED(ExpFI, Succeeded());
|
|
|
|
ASSERT_EQ(ExpFI->Range, AddressRange(0x1000, 0x2000));
|
|
|
|
EXPECT_FALSE(ExpFI->OptLineTable.hasValue());
|
|
|
|
EXPECT_FALSE(ExpFI->Inline.hasValue());
|
|
|
|
StringRef MethodName = GR->getString(ExpFI->Name);
|
|
|
|
EXPECT_EQ(MethodName, "main");
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(GSYMTest, TestDWARFInlineInfo) {
|
|
|
|
// Make sure we parse the line table and inline information correctly from
|
|
|
|
// DWARF.
|
|
|
|
StringRef yamldata = R"(
|
|
|
|
debug_str:
|
|
|
|
- ''
|
|
|
|
- /tmp/main.c
|
|
|
|
- main
|
|
|
|
- inline1
|
|
|
|
debug_abbrev:
|
2020-08-21 10:11:33 +08:00
|
|
|
- Table:
|
|
|
|
- Code: 0x00000001
|
|
|
|
Tag: DW_TAG_compile_unit
|
|
|
|
Children: DW_CHILDREN_yes
|
|
|
|
Attributes:
|
|
|
|
- Attribute: DW_AT_name
|
|
|
|
Form: DW_FORM_strp
|
|
|
|
- Attribute: DW_AT_low_pc
|
|
|
|
Form: DW_FORM_addr
|
|
|
|
- Attribute: DW_AT_high_pc
|
|
|
|
Form: DW_FORM_data4
|
|
|
|
- Attribute: DW_AT_language
|
|
|
|
Form: DW_FORM_data2
|
|
|
|
- Attribute: DW_AT_stmt_list
|
|
|
|
Form: DW_FORM_sec_offset
|
|
|
|
- Code: 0x00000002
|
|
|
|
Tag: DW_TAG_subprogram
|
|
|
|
Children: DW_CHILDREN_yes
|
|
|
|
Attributes:
|
|
|
|
- Attribute: DW_AT_name
|
|
|
|
Form: DW_FORM_strp
|
|
|
|
- Attribute: DW_AT_low_pc
|
|
|
|
Form: DW_FORM_addr
|
|
|
|
- Attribute: DW_AT_high_pc
|
|
|
|
Form: DW_FORM_data4
|
|
|
|
- Code: 0x00000003
|
|
|
|
Tag: DW_TAG_inlined_subroutine
|
|
|
|
Children: DW_CHILDREN_no
|
|
|
|
Attributes:
|
|
|
|
- Attribute: DW_AT_name
|
|
|
|
Form: DW_FORM_strp
|
|
|
|
- Attribute: DW_AT_low_pc
|
|
|
|
Form: DW_FORM_addr
|
|
|
|
- Attribute: DW_AT_high_pc
|
|
|
|
Form: DW_FORM_data4
|
|
|
|
- Attribute: DW_AT_call_file
|
|
|
|
Form: DW_FORM_data4
|
|
|
|
- Attribute: DW_AT_call_line
|
|
|
|
Form: DW_FORM_data4
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
debug_info:
|
2020-08-31 14:03:02 +08:00
|
|
|
- Version: 4
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
AddrSize: 8
|
|
|
|
Entries:
|
|
|
|
- AbbrCode: 0x00000001
|
|
|
|
Values:
|
|
|
|
- Value: 0x0000000000000001
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- Value: 0x0000000000000004
|
|
|
|
- Value: 0x0000000000000000
|
|
|
|
- AbbrCode: 0x00000002
|
|
|
|
Values:
|
|
|
|
- Value: 0x000000000000000D
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- AbbrCode: 0x00000003
|
|
|
|
Values:
|
|
|
|
- Value: 0x0000000000000012
|
|
|
|
- Value: 0x0000000000001100
|
|
|
|
- Value: 0x0000000000000100
|
|
|
|
- Value: 0x0000000000000001
|
|
|
|
- Value: 0x000000000000000A
|
|
|
|
- AbbrCode: 0x00000000
|
|
|
|
- AbbrCode: 0x00000000
|
|
|
|
debug_line:
|
2020-06-13 22:04:51 +08:00
|
|
|
- Length: 96
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
Version: 2
|
|
|
|
PrologueLength: 46
|
|
|
|
MinInstLength: 1
|
|
|
|
DefaultIsStmt: 1
|
|
|
|
LineBase: 251
|
|
|
|
LineRange: 14
|
|
|
|
OpcodeBase: 13
|
|
|
|
StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ]
|
|
|
|
IncludeDirs:
|
|
|
|
- /tmp
|
|
|
|
Files:
|
|
|
|
- Name: main.c
|
|
|
|
DirIdx: 1
|
|
|
|
ModTime: 0
|
|
|
|
Length: 0
|
|
|
|
- Name: inline.h
|
|
|
|
DirIdx: 1
|
|
|
|
ModTime: 0
|
|
|
|
Length: 0
|
|
|
|
Opcodes:
|
|
|
|
- Opcode: DW_LNS_extended_op
|
|
|
|
ExtLen: 9
|
|
|
|
SubOpcode: DW_LNE_set_address
|
|
|
|
Data: 4096
|
|
|
|
- Opcode: DW_LNS_advance_line
|
|
|
|
SData: 9
|
|
|
|
Data: 4096
|
|
|
|
- Opcode: DW_LNS_copy
|
|
|
|
Data: 4096
|
|
|
|
- Opcode: DW_LNS_advance_pc
|
|
|
|
Data: 256
|
|
|
|
- Opcode: DW_LNS_set_file
|
|
|
|
Data: 2
|
|
|
|
- Opcode: DW_LNS_advance_line
|
|
|
|
SData: 10
|
|
|
|
Data: 2
|
|
|
|
- Opcode: DW_LNS_copy
|
|
|
|
Data: 2
|
|
|
|
- Opcode: DW_LNS_advance_pc
|
|
|
|
Data: 128
|
|
|
|
- Opcode: DW_LNS_advance_line
|
|
|
|
SData: 1
|
|
|
|
Data: 128
|
|
|
|
- Opcode: DW_LNS_copy
|
|
|
|
Data: 128
|
|
|
|
- Opcode: DW_LNS_advance_pc
|
|
|
|
Data: 128
|
|
|
|
- Opcode: DW_LNS_set_file
|
|
|
|
Data: 1
|
|
|
|
- Opcode: DW_LNS_advance_line
|
|
|
|
SData: -10
|
|
|
|
Data: 1
|
|
|
|
- Opcode: DW_LNS_copy
|
|
|
|
Data: 1
|
|
|
|
- Opcode: DW_LNS_advance_pc
|
|
|
|
Data: 3584
|
|
|
|
- Opcode: DW_LNS_advance_line
|
|
|
|
SData: 1
|
|
|
|
Data: 3584
|
|
|
|
- Opcode: DW_LNS_extended_op
|
|
|
|
ExtLen: 1
|
|
|
|
SubOpcode: DW_LNE_end_sequence
|
|
|
|
Data: 3584
|
|
|
|
)";
|
2020-06-08 17:30:23 +08:00
|
|
|
auto ErrOrSections = DWARFYAML::emitDebugSections(yamldata);
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
ASSERT_THAT_EXPECTED(ErrOrSections, Succeeded());
|
|
|
|
std::unique_ptr<DWARFContext> DwarfContext =
|
|
|
|
DWARFContext::create(*ErrOrSections, 8);
|
|
|
|
ASSERT_TRUE(DwarfContext.get() != nullptr);
|
|
|
|
auto &OS = llvm::nulls();
|
|
|
|
GsymCreator GC;
|
|
|
|
DwarfTransformer DT(*DwarfContext, OS, GC);
|
|
|
|
const uint32_t ThreadCount = 1;
|
|
|
|
ASSERT_THAT_ERROR(DT.convert(ThreadCount), Succeeded());
|
|
|
|
ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded());
|
|
|
|
SmallString<512> Str;
|
|
|
|
raw_svector_ostream OutStrm(Str);
|
|
|
|
const auto ByteOrder = support::endian::system_endianness();
|
|
|
|
FileWriter FW(OutStrm, ByteOrder);
|
|
|
|
ASSERT_THAT_ERROR(GC.encode(FW), Succeeded());
|
|
|
|
Expected<GsymReader> GR = GsymReader::copyBuffer(OutStrm.str());
|
|
|
|
ASSERT_THAT_EXPECTED(GR, Succeeded());
|
|
|
|
// There should only be one function in our GSYM.
|
|
|
|
EXPECT_EQ(GR->getNumAddresses(), 1u);
|
|
|
|
auto ExpFI = GR->getFunctionInfo(0x1000);
|
|
|
|
ASSERT_THAT_EXPECTED(ExpFI, Succeeded());
|
|
|
|
ASSERT_EQ(ExpFI->Range, AddressRange(0x1000, 0x2000));
|
|
|
|
EXPECT_TRUE(ExpFI->OptLineTable.hasValue());
|
|
|
|
EXPECT_TRUE(ExpFI->Inline.hasValue());
|
|
|
|
StringRef MethodName = GR->getString(ExpFI->Name);
|
|
|
|
EXPECT_EQ(MethodName, "main");
|
|
|
|
|
|
|
|
// Verify inline info is correct when doing lookups.
|
|
|
|
auto LR = GR->lookup(0x1000);
|
|
|
|
ASSERT_THAT_EXPECTED(LR, Succeeded());
|
|
|
|
EXPECT_THAT(LR->Locations,
|
|
|
|
testing::ElementsAre(SourceLocation{"main", "/tmp", "main.c", 10}));
|
|
|
|
LR = GR->lookup(0x1100-1);
|
|
|
|
ASSERT_THAT_EXPECTED(LR, Succeeded());
|
|
|
|
EXPECT_THAT(LR->Locations,
|
2020-02-16 13:28:36 +08:00
|
|
|
testing::ElementsAre(SourceLocation{"main", "/tmp", "main.c", 10, 255}));
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
|
|
|
|
LR = GR->lookup(0x1100);
|
|
|
|
ASSERT_THAT_EXPECTED(LR, Succeeded());
|
|
|
|
EXPECT_THAT(LR->Locations,
|
|
|
|
testing::ElementsAre(SourceLocation{"inline1", "/tmp", "inline.h", 20},
|
2020-02-16 13:28:36 +08:00
|
|
|
SourceLocation{"main", "/tmp", "main.c", 10, 256}));
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
LR = GR->lookup(0x1180-1);
|
|
|
|
ASSERT_THAT_EXPECTED(LR, Succeeded());
|
|
|
|
EXPECT_THAT(LR->Locations,
|
2020-02-16 13:28:36 +08:00
|
|
|
testing::ElementsAre(SourceLocation{"inline1", "/tmp", "inline.h", 20, 127},
|
|
|
|
SourceLocation{"main", "/tmp", "main.c", 10, 383}));
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
LR = GR->lookup(0x1180);
|
|
|
|
ASSERT_THAT_EXPECTED(LR, Succeeded());
|
|
|
|
EXPECT_THAT(LR->Locations,
|
2020-02-16 13:28:36 +08:00
|
|
|
testing::ElementsAre(SourceLocation{"inline1", "/tmp", "inline.h", 21, 128},
|
|
|
|
SourceLocation{"main", "/tmp", "main.c", 10, 384}));
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
LR = GR->lookup(0x1200-1);
|
|
|
|
ASSERT_THAT_EXPECTED(LR, Succeeded());
|
|
|
|
EXPECT_THAT(LR->Locations,
|
2020-02-16 13:28:36 +08:00
|
|
|
testing::ElementsAre(SourceLocation{"inline1", "/tmp", "inline.h", 21, 255},
|
|
|
|
SourceLocation{"main", "/tmp", "main.c", 10, 511}));
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
LR = GR->lookup(0x1200);
|
|
|
|
ASSERT_THAT_EXPECTED(LR, Succeeded());
|
|
|
|
EXPECT_THAT(LR->Locations,
|
2020-02-16 13:28:36 +08:00
|
|
|
testing::ElementsAre(SourceLocation{"main", "/tmp", "main.c", 11, 512}));
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST(GSYMTest, TestDWARFNoLines) {
|
|
|
|
// Check that if a DW_TAG_subprogram doesn't have line table entries that
|
|
|
|
// we fall back and use the DW_AT_decl_file and DW_AT_decl_line to at least
|
|
|
|
// point to the function definition. This DWARF file has 4 functions:
|
|
|
|
// "lines_no_decl": has line table entries, no DW_AT_decl_file/line attrs.
|
|
|
|
// "lines_with_decl": has line table entries and has DW_AT_decl_file/line,
|
|
|
|
// make sure we don't use DW_AT_decl_file/line and make
|
|
|
|
// sure there is a line table.
|
|
|
|
// "no_lines_no_decl": no line table entries and no DW_AT_decl_file/line,
|
|
|
|
// make sure there is no line table for this function.
|
|
|
|
// "no_lines_with_decl": no line table and has DW_AT_decl_file/line, make
|
|
|
|
// sure we have one line table entry that starts at
|
|
|
|
// the function start address and the decl file and
|
|
|
|
// line.
|
|
|
|
//
|
|
|
|
// 0x0000000b: DW_TAG_compile_unit
|
|
|
|
// DW_AT_name ("/tmp/main.c")
|
|
|
|
// DW_AT_low_pc (0x0000000000001000)
|
|
|
|
// DW_AT_high_pc (0x0000000000002000)
|
|
|
|
// DW_AT_language (DW_LANG_C_plus_plus)
|
|
|
|
// DW_AT_stmt_list (0x00000000)
|
|
|
|
//
|
|
|
|
// 0x00000022: DW_TAG_subprogram
|
|
|
|
// DW_AT_name ("lines_no_decl")
|
|
|
|
// DW_AT_low_pc (0x0000000000001000)
|
|
|
|
// DW_AT_high_pc (0x0000000000002000)
|
|
|
|
//
|
|
|
|
// 0x00000033: DW_TAG_subprogram
|
|
|
|
// DW_AT_name ("lines_with_decl")
|
|
|
|
// DW_AT_low_pc (0x0000000000002000)
|
|
|
|
// DW_AT_high_pc (0x0000000000003000)
|
|
|
|
// DW_AT_decl_file ("/tmp/main.c")
|
|
|
|
// DW_AT_decl_line (20)
|
|
|
|
//
|
|
|
|
// 0x00000046: DW_TAG_subprogram
|
|
|
|
// DW_AT_name ("no_lines_no_decl")
|
|
|
|
// DW_AT_low_pc (0x0000000000003000)
|
|
|
|
// DW_AT_high_pc (0x0000000000004000)
|
|
|
|
//
|
|
|
|
// 0x00000057: DW_TAG_subprogram
|
|
|
|
// DW_AT_name ("no_lines_with_decl")
|
|
|
|
// DW_AT_low_pc (0x0000000000004000)
|
|
|
|
// DW_AT_high_pc (0x0000000000005000)
|
|
|
|
// DW_AT_decl_file ("/tmp/main.c")
|
|
|
|
// DW_AT_decl_line (40)
|
|
|
|
//
|
|
|
|
// 0x0000006a: NULL
|
|
|
|
|
|
|
|
StringRef yamldata = R"(
|
|
|
|
debug_str:
|
|
|
|
- ''
|
|
|
|
- '/tmp/main.c'
|
|
|
|
- lines_no_decl
|
|
|
|
- lines_with_decl
|
|
|
|
- no_lines_no_decl
|
|
|
|
- no_lines_with_decl
|
|
|
|
debug_abbrev:
|
2020-08-21 10:11:33 +08:00
|
|
|
- Table:
|
|
|
|
- Code: 0x00000001
|
|
|
|
Tag: DW_TAG_compile_unit
|
|
|
|
Children: DW_CHILDREN_yes
|
|
|
|
Attributes:
|
|
|
|
- Attribute: DW_AT_name
|
|
|
|
Form: DW_FORM_strp
|
|
|
|
- Attribute: DW_AT_low_pc
|
|
|
|
Form: DW_FORM_addr
|
|
|
|
- Attribute: DW_AT_high_pc
|
|
|
|
Form: DW_FORM_data4
|
|
|
|
- Attribute: DW_AT_language
|
|
|
|
Form: DW_FORM_data2
|
|
|
|
- Attribute: DW_AT_stmt_list
|
|
|
|
Form: DW_FORM_sec_offset
|
|
|
|
- Code: 0x00000002
|
|
|
|
Tag: DW_TAG_subprogram
|
|
|
|
Children: DW_CHILDREN_no
|
|
|
|
Attributes:
|
|
|
|
- Attribute: DW_AT_name
|
|
|
|
Form: DW_FORM_strp
|
|
|
|
- Attribute: DW_AT_low_pc
|
|
|
|
Form: DW_FORM_addr
|
|
|
|
- Attribute: DW_AT_high_pc
|
|
|
|
Form: DW_FORM_data4
|
|
|
|
- Code: 0x00000003
|
|
|
|
Tag: DW_TAG_subprogram
|
|
|
|
Children: DW_CHILDREN_no
|
|
|
|
Attributes:
|
|
|
|
- Attribute: DW_AT_name
|
|
|
|
Form: DW_FORM_strp
|
|
|
|
- Attribute: DW_AT_low_pc
|
|
|
|
Form: DW_FORM_addr
|
|
|
|
- Attribute: DW_AT_high_pc
|
|
|
|
Form: DW_FORM_data4
|
|
|
|
- Attribute: DW_AT_decl_file
|
|
|
|
Form: DW_FORM_data1
|
|
|
|
- Attribute: DW_AT_decl_line
|
|
|
|
Form: DW_FORM_data1
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
debug_info:
|
2020-08-31 14:03:02 +08:00
|
|
|
- Version: 4
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
AddrSize: 8
|
|
|
|
Entries:
|
|
|
|
- AbbrCode: 0x00000001
|
|
|
|
Values:
|
|
|
|
- Value: 0x0000000000000001
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- Value: 0x0000000000000004
|
|
|
|
- Value: 0x0000000000000000
|
|
|
|
- AbbrCode: 0x00000002
|
|
|
|
Values:
|
|
|
|
- Value: 0x000000000000000D
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- AbbrCode: 0x00000003
|
|
|
|
Values:
|
|
|
|
- Value: 0x000000000000001B
|
|
|
|
- Value: 0x0000000000002000
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- Value: 0x0000000000000001
|
|
|
|
- Value: 0x0000000000000014
|
|
|
|
- AbbrCode: 0x00000002
|
|
|
|
Values:
|
|
|
|
- Value: 0x000000000000002B
|
|
|
|
- Value: 0x0000000000003000
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- AbbrCode: 0x00000003
|
|
|
|
Values:
|
|
|
|
- Value: 0x000000000000003C
|
|
|
|
- Value: 0x0000000000004000
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- Value: 0x0000000000000001
|
|
|
|
- Value: 0x0000000000000028
|
|
|
|
- AbbrCode: 0x00000000
|
|
|
|
debug_line:
|
2020-06-13 22:04:51 +08:00
|
|
|
- Length: 92
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
Version: 2
|
|
|
|
PrologueLength: 34
|
|
|
|
MinInstLength: 1
|
|
|
|
DefaultIsStmt: 1
|
|
|
|
LineBase: 251
|
|
|
|
LineRange: 14
|
|
|
|
OpcodeBase: 13
|
|
|
|
StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ]
|
|
|
|
IncludeDirs:
|
|
|
|
- '/tmp'
|
|
|
|
Files:
|
|
|
|
- Name: main.c
|
|
|
|
DirIdx: 1
|
|
|
|
ModTime: 0
|
|
|
|
Length: 0
|
|
|
|
Opcodes:
|
|
|
|
- Opcode: DW_LNS_extended_op
|
|
|
|
ExtLen: 9
|
|
|
|
SubOpcode: DW_LNE_set_address
|
|
|
|
Data: 4096
|
|
|
|
- Opcode: DW_LNS_advance_line
|
|
|
|
SData: 10
|
|
|
|
Data: 0
|
|
|
|
- Opcode: DW_LNS_copy
|
|
|
|
Data: 0
|
|
|
|
- Opcode: DW_LNS_advance_pc
|
|
|
|
Data: 512
|
|
|
|
- Opcode: DW_LNS_advance_line
|
|
|
|
SData: 1
|
|
|
|
Data: 0
|
|
|
|
- Opcode: DW_LNS_copy
|
|
|
|
Data: 0
|
|
|
|
- Opcode: DW_LNS_advance_pc
|
|
|
|
Data: 3584
|
|
|
|
- Opcode: DW_LNS_extended_op
|
|
|
|
ExtLen: 1
|
|
|
|
SubOpcode: DW_LNE_end_sequence
|
|
|
|
Data: 0
|
|
|
|
- Opcode: DW_LNS_extended_op
|
|
|
|
ExtLen: 9
|
|
|
|
SubOpcode: DW_LNE_set_address
|
|
|
|
Data: 8192
|
|
|
|
- Opcode: DW_LNS_advance_line
|
|
|
|
SData: 20
|
|
|
|
Data: 0
|
|
|
|
- Opcode: DW_LNS_copy
|
|
|
|
Data: 0
|
|
|
|
- Opcode: DW_LNS_advance_pc
|
|
|
|
Data: 512
|
|
|
|
- Opcode: DW_LNS_advance_line
|
|
|
|
SData: 1
|
|
|
|
Data: 0
|
|
|
|
- Opcode: DW_LNS_copy
|
|
|
|
Data: 0
|
|
|
|
- Opcode: DW_LNS_advance_pc
|
|
|
|
Data: 3584
|
|
|
|
- Opcode: DW_LNS_extended_op
|
|
|
|
ExtLen: 1
|
|
|
|
SubOpcode: DW_LNE_end_sequence
|
|
|
|
Data: 0
|
|
|
|
)";
|
2020-06-08 17:30:23 +08:00
|
|
|
auto ErrOrSections = DWARFYAML::emitDebugSections(yamldata);
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
ASSERT_THAT_EXPECTED(ErrOrSections, Succeeded());
|
|
|
|
std::unique_ptr<DWARFContext> DwarfContext =
|
|
|
|
DWARFContext::create(*ErrOrSections, 8);
|
|
|
|
ASSERT_TRUE(DwarfContext.get() != nullptr);
|
|
|
|
auto &OS = llvm::nulls();
|
|
|
|
GsymCreator GC;
|
|
|
|
DwarfTransformer DT(*DwarfContext, OS, GC);
|
|
|
|
const uint32_t ThreadCount = 1;
|
|
|
|
ASSERT_THAT_ERROR(DT.convert(ThreadCount), Succeeded());
|
|
|
|
ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded());
|
|
|
|
SmallString<512> Str;
|
|
|
|
raw_svector_ostream OutStrm(Str);
|
|
|
|
const auto ByteOrder = support::endian::system_endianness();
|
|
|
|
FileWriter FW(OutStrm, ByteOrder);
|
|
|
|
ASSERT_THAT_ERROR(GC.encode(FW), Succeeded());
|
|
|
|
Expected<GsymReader> GR = GsymReader::copyBuffer(OutStrm.str());
|
|
|
|
ASSERT_THAT_EXPECTED(GR, Succeeded());
|
|
|
|
|
|
|
|
EXPECT_EQ(GR->getNumAddresses(), 4u);
|
|
|
|
|
|
|
|
auto ExpFI = GR->getFunctionInfo(0x1000);
|
|
|
|
ASSERT_THAT_EXPECTED(ExpFI, Succeeded());
|
|
|
|
ASSERT_EQ(ExpFI->Range, AddressRange(0x1000, 0x2000));
|
|
|
|
EXPECT_TRUE(ExpFI->OptLineTable.hasValue());
|
|
|
|
StringRef MethodName = GR->getString(ExpFI->Name);
|
|
|
|
EXPECT_EQ(MethodName, "lines_no_decl");
|
|
|
|
// Make sure have two line table entries and that get the first line entry
|
|
|
|
// correct.
|
|
|
|
EXPECT_EQ(ExpFI->OptLineTable->size(), 2u);
|
|
|
|
EXPECT_EQ(ExpFI->OptLineTable->first()->Addr, 0x1000u);
|
|
|
|
EXPECT_EQ(ExpFI->OptLineTable->first()->Line, 11u);
|
|
|
|
|
|
|
|
ExpFI = GR->getFunctionInfo(0x2000);
|
|
|
|
ASSERT_THAT_EXPECTED(ExpFI, Succeeded());
|
|
|
|
ASSERT_EQ(ExpFI->Range, AddressRange(0x2000, 0x3000));
|
|
|
|
EXPECT_TRUE(ExpFI->OptLineTable.hasValue());
|
|
|
|
MethodName = GR->getString(ExpFI->Name);
|
|
|
|
EXPECT_EQ(MethodName, "lines_with_decl");
|
|
|
|
// Make sure have two line table entries and that we don't use line 20
|
|
|
|
// from the DW_AT_decl_file/line as a line table entry.
|
|
|
|
EXPECT_EQ(ExpFI->OptLineTable->size(), 2u);
|
|
|
|
EXPECT_EQ(ExpFI->OptLineTable->first()->Addr, 0x2000u);
|
|
|
|
EXPECT_EQ(ExpFI->OptLineTable->first()->Line, 21u);
|
|
|
|
|
|
|
|
ExpFI = GR->getFunctionInfo(0x3000);
|
|
|
|
ASSERT_THAT_EXPECTED(ExpFI, Succeeded());
|
|
|
|
ASSERT_EQ(ExpFI->Range, AddressRange(0x3000, 0x4000));
|
|
|
|
// Make sure we have no line table.
|
|
|
|
EXPECT_FALSE(ExpFI->OptLineTable.hasValue());
|
|
|
|
MethodName = GR->getString(ExpFI->Name);
|
|
|
|
EXPECT_EQ(MethodName, "no_lines_no_decl");
|
|
|
|
|
|
|
|
ExpFI = GR->getFunctionInfo(0x4000);
|
|
|
|
ASSERT_THAT_EXPECTED(ExpFI, Succeeded());
|
|
|
|
ASSERT_EQ(ExpFI->Range, AddressRange(0x4000, 0x5000));
|
|
|
|
EXPECT_TRUE(ExpFI->OptLineTable.hasValue());
|
|
|
|
MethodName = GR->getString(ExpFI->Name);
|
|
|
|
EXPECT_EQ(MethodName, "no_lines_with_decl");
|
|
|
|
// Make sure we have one line table entry that uses the DW_AT_decl_file/line
|
|
|
|
// as the one and only line entry.
|
|
|
|
EXPECT_EQ(ExpFI->OptLineTable->size(), 1u);
|
|
|
|
EXPECT_EQ(ExpFI->OptLineTable->first()->Addr, 0x4000u);
|
|
|
|
EXPECT_EQ(ExpFI->OptLineTable->first()->Line, 40u);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST(GSYMTest, TestDWARFDeadStripAddr4) {
|
|
|
|
// Check that various techniques that compilers use for dead code stripping
|
|
|
|
// work for 4 byte addresses. Make sure we keep the good functions and
|
|
|
|
// strip any functions whose name starts with "stripped".
|
|
|
|
//
|
|
|
|
// 1 - Compilers might set the low PC to -1 (UINT32_MAX) for compile unit
|
|
|
|
// with 4 byte addresses ("stripped1")
|
|
|
|
// 2 - Set the low and high PC to the same value ("stripped2")
|
|
|
|
// 3 - Have the high PC lower than the low PC ("stripped3")
|
|
|
|
//
|
|
|
|
// 0x0000000b: DW_TAG_compile_unit
|
|
|
|
// DW_AT_name ("/tmp/main.c")
|
|
|
|
// DW_AT_low_pc (0x0000000000001000)
|
|
|
|
// DW_AT_high_pc (0x0000000000002000)
|
|
|
|
// DW_AT_language (DW_LANG_C_plus_plus)
|
|
|
|
//
|
|
|
|
// 0x0000001a: DW_TAG_subprogram
|
|
|
|
// DW_AT_name ("main")
|
|
|
|
// DW_AT_low_pc (0x0000000000001000)
|
|
|
|
// DW_AT_high_pc (0x0000000000002000)
|
|
|
|
//
|
|
|
|
// 0x00000027: DW_TAG_subprogram
|
|
|
|
// DW_AT_name ("stripped1")
|
|
|
|
// DW_AT_low_pc (0x00000000ffffffff)
|
|
|
|
// DW_AT_high_pc (0x0000000100000000)
|
|
|
|
//
|
|
|
|
// 0x00000034: DW_TAG_subprogram
|
|
|
|
// DW_AT_name ("stripped2")
|
|
|
|
// DW_AT_low_pc (0x0000000000003000)
|
|
|
|
// DW_AT_high_pc (0x0000000000003000)
|
|
|
|
//
|
|
|
|
// 0x00000041: DW_TAG_subprogram
|
|
|
|
// DW_AT_name ("stripped3")
|
|
|
|
// DW_AT_low_pc (0x0000000000004000)
|
|
|
|
// DW_AT_high_pc (0x0000000000003fff)
|
|
|
|
//
|
|
|
|
// 0x0000004e: NULL
|
|
|
|
|
|
|
|
StringRef yamldata = R"(
|
|
|
|
debug_str:
|
|
|
|
- ''
|
|
|
|
- '/tmp/main.c'
|
|
|
|
- main
|
|
|
|
- stripped1
|
|
|
|
- stripped2
|
|
|
|
- stripped3
|
|
|
|
debug_abbrev:
|
2020-08-21 10:11:33 +08:00
|
|
|
- Table:
|
|
|
|
- Code: 0x00000001
|
|
|
|
Tag: DW_TAG_compile_unit
|
|
|
|
Children: DW_CHILDREN_yes
|
|
|
|
Attributes:
|
|
|
|
- Attribute: DW_AT_name
|
|
|
|
Form: DW_FORM_strp
|
|
|
|
- Attribute: DW_AT_low_pc
|
|
|
|
Form: DW_FORM_addr
|
|
|
|
- Attribute: DW_AT_high_pc
|
|
|
|
Form: DW_FORM_data4
|
|
|
|
- Attribute: DW_AT_language
|
|
|
|
Form: DW_FORM_data2
|
|
|
|
- Code: 0x00000002
|
|
|
|
Tag: DW_TAG_subprogram
|
|
|
|
Children: DW_CHILDREN_no
|
|
|
|
Attributes:
|
|
|
|
- Attribute: DW_AT_name
|
|
|
|
Form: DW_FORM_strp
|
|
|
|
- Attribute: DW_AT_low_pc
|
|
|
|
Form: DW_FORM_addr
|
|
|
|
- Attribute: DW_AT_high_pc
|
|
|
|
Form: DW_FORM_data4
|
|
|
|
- Code: 0x00000003
|
|
|
|
Tag: DW_TAG_subprogram
|
|
|
|
Children: DW_CHILDREN_no
|
|
|
|
Attributes:
|
|
|
|
- Attribute: DW_AT_name
|
|
|
|
Form: DW_FORM_strp
|
|
|
|
- Attribute: DW_AT_low_pc
|
|
|
|
Form: DW_FORM_addr
|
|
|
|
- Attribute: DW_AT_high_pc
|
|
|
|
Form: DW_FORM_addr
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
debug_info:
|
2020-08-31 14:03:02 +08:00
|
|
|
- Version: 4
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
AddrSize: 4
|
|
|
|
Entries:
|
|
|
|
- AbbrCode: 0x00000001
|
|
|
|
Values:
|
|
|
|
- Value: 0x0000000000000001
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- Value: 0x0000000000000004
|
|
|
|
- AbbrCode: 0x00000002
|
|
|
|
Values:
|
|
|
|
- Value: 0x000000000000000D
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- AbbrCode: 0x00000002
|
|
|
|
Values:
|
|
|
|
- Value: 0x0000000000000012
|
|
|
|
- Value: 0x00000000FFFFFFFF
|
|
|
|
- Value: 0x0000000000000001
|
|
|
|
- AbbrCode: 0x00000003
|
|
|
|
Values:
|
|
|
|
- Value: 0x000000000000001C
|
|
|
|
- Value: 0x0000000000003000
|
|
|
|
- Value: 0x0000000000003000
|
|
|
|
- AbbrCode: 0x00000003
|
|
|
|
Values:
|
|
|
|
- Value: 0x0000000000000026
|
|
|
|
- Value: 0x0000000000004000
|
|
|
|
- Value: 0x0000000000003FFF
|
|
|
|
- AbbrCode: 0x00000000
|
|
|
|
)";
|
2020-06-08 17:30:23 +08:00
|
|
|
auto ErrOrSections = DWARFYAML::emitDebugSections(yamldata);
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
ASSERT_THAT_EXPECTED(ErrOrSections, Succeeded());
|
|
|
|
std::unique_ptr<DWARFContext> DwarfContext =
|
|
|
|
DWARFContext::create(*ErrOrSections, 4);
|
|
|
|
ASSERT_TRUE(DwarfContext.get() != nullptr);
|
|
|
|
auto &OS = llvm::nulls();
|
|
|
|
GsymCreator GC;
|
|
|
|
DwarfTransformer DT(*DwarfContext, OS, GC);
|
|
|
|
const uint32_t ThreadCount = 1;
|
|
|
|
ASSERT_THAT_ERROR(DT.convert(ThreadCount), Succeeded());
|
|
|
|
ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded());
|
|
|
|
SmallString<512> Str;
|
|
|
|
raw_svector_ostream OutStrm(Str);
|
|
|
|
const auto ByteOrder = support::endian::system_endianness();
|
|
|
|
FileWriter FW(OutStrm, ByteOrder);
|
|
|
|
ASSERT_THAT_ERROR(GC.encode(FW), Succeeded());
|
|
|
|
Expected<GsymReader> GR = GsymReader::copyBuffer(OutStrm.str());
|
|
|
|
ASSERT_THAT_EXPECTED(GR, Succeeded());
|
|
|
|
|
|
|
|
// Test that the only function that made it was the "main" function.
|
|
|
|
EXPECT_EQ(GR->getNumAddresses(), 1u);
|
|
|
|
auto ExpFI = GR->getFunctionInfo(0x1000);
|
|
|
|
ASSERT_THAT_EXPECTED(ExpFI, Succeeded());
|
|
|
|
ASSERT_EQ(ExpFI->Range, AddressRange(0x1000, 0x2000));
|
|
|
|
StringRef MethodName = GR->getString(ExpFI->Name);
|
|
|
|
EXPECT_EQ(MethodName, "main");
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(GSYMTest, TestDWARFDeadStripAddr8) {
|
|
|
|
// Check that various techniques that compilers use for dead code stripping
|
|
|
|
// work for 4 byte addresses. Make sure we keep the good functions and
|
|
|
|
// strip any functions whose name starts with "stripped".
|
|
|
|
//
|
|
|
|
// 1 - Compilers might set the low PC to -1 (UINT64_MAX) for compile unit
|
|
|
|
// with 8 byte addresses ("stripped1")
|
|
|
|
// 2 - Set the low and high PC to the same value ("stripped2")
|
|
|
|
// 3 - Have the high PC lower than the low PC ("stripped3")
|
|
|
|
//
|
|
|
|
// 0x0000000b: DW_TAG_compile_unit
|
|
|
|
// DW_AT_name ("/tmp/main.c")
|
|
|
|
// DW_AT_low_pc (0x0000000000001000)
|
|
|
|
// DW_AT_high_pc (0x0000000000002000)
|
|
|
|
// DW_AT_language (DW_LANG_C_plus_plus)
|
|
|
|
//
|
|
|
|
// 0x0000001e: DW_TAG_subprogram
|
|
|
|
// DW_AT_name ("main")
|
|
|
|
// DW_AT_low_pc (0x0000000000001000)
|
|
|
|
// DW_AT_high_pc (0x0000000000002000)
|
|
|
|
//
|
|
|
|
// 0x0000002f: DW_TAG_subprogram
|
|
|
|
// DW_AT_name ("stripped1")
|
|
|
|
// DW_AT_low_pc (0xffffffffffffffff)
|
|
|
|
// DW_AT_high_pc (0x0000000000000000)
|
|
|
|
//
|
|
|
|
// 0x00000040: DW_TAG_subprogram
|
|
|
|
// DW_AT_name ("stripped2")
|
|
|
|
// DW_AT_low_pc (0x0000000000003000)
|
|
|
|
// DW_AT_high_pc (0x0000000000003000)
|
|
|
|
//
|
|
|
|
// 0x00000055: DW_TAG_subprogram
|
|
|
|
// DW_AT_name ("stripped3")
|
|
|
|
// DW_AT_low_pc (0x0000000000004000)
|
|
|
|
// DW_AT_high_pc (0x0000000000003fff)
|
|
|
|
//
|
|
|
|
// 0x0000006a: NULL
|
|
|
|
|
|
|
|
StringRef yamldata = R"(
|
|
|
|
debug_str:
|
|
|
|
- ''
|
|
|
|
- '/tmp/main.c'
|
|
|
|
- main
|
|
|
|
- stripped1
|
|
|
|
- stripped2
|
|
|
|
- stripped3
|
|
|
|
debug_abbrev:
|
2020-08-21 10:11:33 +08:00
|
|
|
- Table:
|
|
|
|
- Code: 0x00000001
|
|
|
|
Tag: DW_TAG_compile_unit
|
|
|
|
Children: DW_CHILDREN_yes
|
|
|
|
Attributes:
|
|
|
|
- Attribute: DW_AT_name
|
|
|
|
Form: DW_FORM_strp
|
|
|
|
- Attribute: DW_AT_low_pc
|
|
|
|
Form: DW_FORM_addr
|
|
|
|
- Attribute: DW_AT_high_pc
|
|
|
|
Form: DW_FORM_data4
|
|
|
|
- Attribute: DW_AT_language
|
|
|
|
Form: DW_FORM_data2
|
|
|
|
- Code: 0x00000002
|
|
|
|
Tag: DW_TAG_subprogram
|
|
|
|
Children: DW_CHILDREN_no
|
|
|
|
Attributes:
|
|
|
|
- Attribute: DW_AT_name
|
|
|
|
Form: DW_FORM_strp
|
|
|
|
- Attribute: DW_AT_low_pc
|
|
|
|
Form: DW_FORM_addr
|
|
|
|
- Attribute: DW_AT_high_pc
|
|
|
|
Form: DW_FORM_data4
|
|
|
|
- Code: 0x00000003
|
|
|
|
Tag: DW_TAG_subprogram
|
|
|
|
Children: DW_CHILDREN_no
|
|
|
|
Attributes:
|
|
|
|
- Attribute: DW_AT_name
|
|
|
|
Form: DW_FORM_strp
|
|
|
|
- Attribute: DW_AT_low_pc
|
|
|
|
Form: DW_FORM_addr
|
|
|
|
- Attribute: DW_AT_high_pc
|
|
|
|
Form: DW_FORM_addr
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
debug_info:
|
2020-08-31 14:03:02 +08:00
|
|
|
- Version: 4
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
AddrSize: 8
|
|
|
|
Entries:
|
|
|
|
- AbbrCode: 0x00000001
|
|
|
|
Values:
|
|
|
|
- Value: 0x0000000000000001
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- Value: 0x0000000000000004
|
|
|
|
- AbbrCode: 0x00000002
|
|
|
|
Values:
|
|
|
|
- Value: 0x000000000000000D
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- Value: 0x0000000000001000
|
|
|
|
- AbbrCode: 0x00000002
|
|
|
|
Values:
|
|
|
|
- Value: 0x0000000000000012
|
|
|
|
- Value: 0xFFFFFFFFFFFFFFFF
|
|
|
|
- Value: 0x0000000000000001
|
|
|
|
- AbbrCode: 0x00000003
|
|
|
|
Values:
|
|
|
|
- Value: 0x000000000000001C
|
|
|
|
- Value: 0x0000000000003000
|
|
|
|
- Value: 0x0000000000003000
|
|
|
|
- AbbrCode: 0x00000003
|
|
|
|
Values:
|
|
|
|
- Value: 0x0000000000000026
|
|
|
|
- Value: 0x0000000000004000
|
|
|
|
- Value: 0x0000000000003FFF
|
|
|
|
- AbbrCode: 0x00000000
|
|
|
|
)";
|
2020-06-08 17:30:23 +08:00
|
|
|
auto ErrOrSections = DWARFYAML::emitDebugSections(yamldata);
|
Add a DWARF transformer class that converts DWARF to GSYM.
Summary:
The DWARF transformer is added as a class so it can be unit tested fully.
The DWARF is converted to GSYM format and handles many special cases for functions:
- omit functions in compile units with 4 byte addresses whose address is UINT32_MAX (dead stripped)
- omit functions in compile units with 8 byte addresses whose address is UINT64_MAX (dead stripped)
- omit any functions whose high PC is <= low PC (dead stripped)
- StringTable builder doesn't copy strings, so we need to make backing copies of strings but only when needed. Many strings come from sections in object files and won't need to have backing copies, but some do.
- When a function doesn't have a mangled name, store the fully qualified name by creating a string by traversing the parent decl context DIEs and then. If we don't do this, we end up having cases where some function might appear in the GSYM as "erase" instead of "std::vector<int>::erase".
- omit any functions whose address isn't in the optional TextRanges member variable of DwarfTransformer. This allows object file to register address ranges that are known valid code ranges and can help omit functions that should have been dead stripped, but just had their low PC values set to zero. In this case we have many functions that all appear at address zero and can omit these functions by making sure they fall into good address ranges on the object file. Many compilers do this when the DWARF has a DW_AT_low_pc with a DW_FORM_addr, and a DW_AT_high_pc with a DW_FORM_data4 as the offset from the low PC. In this case the linker can't write the same address to both the high and low PC since there is only a relocation for the DW_AT_low_pc, so many linkers tend to just zero it out.
Reviewers: aprantl, dblaikie, probinson
Subscribers: mgorny, hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D74450
2020-02-12 08:05:59 +08:00
|
|
|
ASSERT_THAT_EXPECTED(ErrOrSections, Succeeded());
|
|
|
|
std::unique_ptr<DWARFContext> DwarfContext =
|
|
|
|
DWARFContext::create(*ErrOrSections, 8);
|
|
|
|
ASSERT_TRUE(DwarfContext.get() != nullptr);
|
|
|
|
auto &OS = llvm::nulls();
|
|
|
|
GsymCreator GC;
|
|
|
|
DwarfTransformer DT(*DwarfContext, OS, GC);
|
|
|
|
const uint32_t ThreadCount = 1;
|
|
|
|
ASSERT_THAT_ERROR(DT.convert(ThreadCount), Succeeded());
|
|
|
|
ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded());
|
|
|
|
SmallString<512> Str;
|
|
|
|
raw_svector_ostream OutStrm(Str);
|
|
|
|
const auto ByteOrder = support::endian::system_endianness();
|
|
|
|
FileWriter FW(OutStrm, ByteOrder);
|
|
|
|
ASSERT_THAT_ERROR(GC.encode(FW), Succeeded());
|
|
|
|
Expected<GsymReader> GR = GsymReader::copyBuffer(OutStrm.str());
|
|
|
|
ASSERT_THAT_EXPECTED(GR, Succeeded());
|
|
|
|
|
|
|
|
// Test that the only function that made it was the "main" function.
|
|
|
|
EXPECT_EQ(GR->getNumAddresses(), 1u);
|
|
|
|
auto ExpFI = GR->getFunctionInfo(0x1000);
|
|
|
|
ASSERT_THAT_EXPECTED(ExpFI, Succeeded());
|
|
|
|
ASSERT_EQ(ExpFI->Range, AddressRange(0x1000, 0x2000));
|
|
|
|
StringRef MethodName = GR->getString(ExpFI->Name);
|
|
|
|
EXPECT_EQ(MethodName, "main");
|
|
|
|
}
|