diff options
Diffstat (limited to 'llvm/lib/DebugInfo')
-rw-r--r-- | llvm/lib/DebugInfo/GSYM/CMakeLists.txt | 1 | ||||
-rw-r--r-- | llvm/lib/DebugInfo/GSYM/FunctionInfo.cpp | 102 | ||||
-rw-r--r-- | llvm/lib/DebugInfo/GSYM/GsymReader.cpp | 22 | ||||
-rw-r--r-- | llvm/lib/DebugInfo/GSYM/InlineInfo.cpp | 110 | ||||
-rw-r--r-- | llvm/lib/DebugInfo/GSYM/LineTable.cpp | 12 | ||||
-rw-r--r-- | llvm/lib/DebugInfo/GSYM/LookupResult.cpp | 68 | ||||
-rw-r--r-- | llvm/lib/DebugInfo/GSYM/Range.cpp | 12 |
7 files changed, 316 insertions, 11 deletions
diff --git a/llvm/lib/DebugInfo/GSYM/CMakeLists.txt b/llvm/lib/DebugInfo/GSYM/CMakeLists.txt index 632ccff5d79..d0fb2caa881 100644 --- a/llvm/lib/DebugInfo/GSYM/CMakeLists.txt +++ b/llvm/lib/DebugInfo/GSYM/CMakeLists.txt @@ -6,6 +6,7 @@ add_llvm_component_library(LLVMDebugInfoGSYM GsymReader.cpp InlineInfo.cpp LineTable.cpp + LookupResult.cpp Range.cpp ADDITIONAL_HEADER_DIRS diff --git a/llvm/lib/DebugInfo/GSYM/FunctionInfo.cpp b/llvm/lib/DebugInfo/GSYM/FunctionInfo.cpp index e234a13fe06..26aab06108b 100644 --- a/llvm/lib/DebugInfo/GSYM/FunctionInfo.cpp +++ b/llvm/lib/DebugInfo/GSYM/FunctionInfo.cpp @@ -8,6 +8,7 @@ #include "llvm/DebugInfo/GSYM/FunctionInfo.h" #include "llvm/DebugInfo/GSYM/FileWriter.h" +#include "llvm/DebugInfo/GSYM/GsymReader.h" #include "llvm/DebugInfo/GSYM/LineTable.h" #include "llvm/DebugInfo/GSYM/InlineInfo.h" #include "llvm/Support/DataExtractor.h" @@ -145,3 +146,104 @@ llvm::Expected<uint64_t> FunctionInfo::encode(FileWriter &O) const { O.writeU32(0); return FuncInfoOffset; } + + +llvm::Expected<LookupResult> FunctionInfo::lookup(DataExtractor &Data, + const GsymReader &GR, + uint64_t FuncAddr, + uint64_t Addr) { + LookupResult LR; + LR.LookupAddr = Addr; + LR.FuncRange.Start = FuncAddr; + uint64_t Offset = 0; + LR.FuncRange.End = FuncAddr + Data.getU32(&Offset); + uint32_t NameOffset = Data.getU32(&Offset); + // The "lookup" functions doesn't report errors as accurately as the "decode" + // function as it is meant to be fast. For more accurage errors we could call + // "decode". + if (!Data.isValidOffset(Offset)) + return createStringError(std::errc::io_error, + "FunctionInfo data is truncated"); + // This function will be called with the result of a binary search of the + // address table, we must still make sure the address does not fall into a + // gap between functions are after the last function. + if (Addr >= LR.FuncRange.End) + return createStringError(std::errc::io_error, + "address 0x%" PRIx64 " is not in GSYM", Addr); + + if (NameOffset == 0) + return createStringError(std::errc::io_error, + "0x%8.8" PRIx64 ": invalid FunctionInfo Name value 0x00000000", + Offset - 4); + LR.FuncName = GR.getString(NameOffset); + bool Done = false; + Optional<LineEntry> LineEntry; + Optional<DataExtractor> InlineInfoData; + while (!Done) { + if (!Data.isValidOffsetForDataOfSize(Offset, 8)) + return createStringError(std::errc::io_error, + "FunctionInfo data is truncated"); + const uint32_t InfoType = Data.getU32(&Offset); + const uint32_t InfoLength = Data.getU32(&Offset); + const StringRef InfoBytes = Data.getData().substr(Offset, InfoLength); + if (InfoLength != InfoBytes.size()) + return createStringError(std::errc::io_error, + "FunctionInfo data is truncated"); + DataExtractor InfoData(InfoBytes, Data.isLittleEndian(), + Data.getAddressSize()); + switch (InfoType) { + case InfoType::EndOfList: + Done = true; + break; + + case InfoType::LineTableInfo: + if (auto ExpectedLE = LineTable::lookup(InfoData, FuncAddr, Addr)) + LineEntry = ExpectedLE.get(); + else + return ExpectedLE.takeError(); + break; + + case InfoType::InlineInfo: + // We will parse the inline info after our line table, but only if + // we have a line entry. + InlineInfoData = InfoData; + break; + + default: + break; + } + Offset += InfoLength; + } + + if (!LineEntry) { + // We don't have a valid line entry for our address, fill in our source + // location as best we can and return. + SourceLocation SrcLoc; + SrcLoc.Name = LR.FuncName; + LR.Locations.push_back(SrcLoc); + return LR; + } + + Optional<FileEntry> LineEntryFile = GR.getFile(LineEntry->File); + if (!LineEntryFile) + return createStringError(std::errc::invalid_argument, + "failed to extract file[%" PRIu32 "]", + LineEntry->File); + + SourceLocation SrcLoc; + SrcLoc.Name = LR.FuncName; + SrcLoc.Dir = GR.getString(LineEntryFile->Dir); + SrcLoc.Base = GR.getString(LineEntryFile->Base); + SrcLoc.Line = LineEntry->Line; + LR.Locations.push_back(SrcLoc); + // If we don't have inline information, we are done. + if (!InlineInfoData) + return LR; + // We have inline information. Try to augment the lookup result with this + // data. + llvm::Error Err = InlineInfo::lookup(GR, *InlineInfoData, FuncAddr, Addr, + LR.Locations); + if (Err) + return std::move(Err); + return LR; +} diff --git a/llvm/lib/DebugInfo/GSYM/GsymReader.cpp b/llvm/lib/DebugInfo/GSYM/GsymReader.cpp index 1b448cf80b7..b4f3f2052ae 100644 --- a/llvm/lib/DebugInfo/GSYM/GsymReader.cpp +++ b/llvm/lib/DebugInfo/GSYM/GsymReader.cpp @@ -1,9 +1,8 @@ //===- GsymReader.cpp -----------------------------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -263,3 +262,18 @@ llvm::Expected<FunctionInfo> GsymReader::getFunctionInfo(uint64_t Addr) const { "failed to extract address[%" PRIu64 "]", *AddressIndex); } + +llvm::Expected<LookupResult> GsymReader::lookup(uint64_t Addr) const { + Expected<uint64_t> AddressIndex = getAddressIndex(Addr); + if (!AddressIndex) + return AddressIndex.takeError(); + // Address info offsets size should have been checked in parse(). + assert(*AddressIndex < AddrInfoOffsets.size()); + auto AddrInfoOffset = AddrInfoOffsets[*AddressIndex]; + DataExtractor Data(MemBuffer->getBuffer().substr(AddrInfoOffset), Endian, 4); + if (Optional<uint64_t> OptAddr = getAddress(*AddressIndex)) + return FunctionInfo::lookup(Data, *this, *OptAddr, Addr); + return createStringError(std::errc::invalid_argument, + "failed to extract address[%" PRIu64 "]", + *AddressIndex); +} diff --git a/llvm/lib/DebugInfo/GSYM/InlineInfo.cpp b/llvm/lib/DebugInfo/GSYM/InlineInfo.cpp index 32ed2c70957..1b8c974fdcd 100644 --- a/llvm/lib/DebugInfo/GSYM/InlineInfo.cpp +++ b/llvm/lib/DebugInfo/GSYM/InlineInfo.cpp @@ -1,14 +1,14 @@ //===- InlineInfo.cpp -------------------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/GSYM/FileEntry.h" #include "llvm/DebugInfo/GSYM/FileWriter.h" +#include "llvm/DebugInfo/GSYM/GsymReader.h" #include "llvm/DebugInfo/GSYM/InlineInfo.h" #include "llvm/Support/DataExtractor.h" #include <algorithm> @@ -60,6 +60,108 @@ llvm::Optional<InlineInfo::InlineArray> InlineInfo::getInlineStack(uint64_t Addr return llvm::None; } +/// Skip an InlineInfo object in the specified data at the specified offset. +/// +/// Used during the InlineInfo::lookup() call to quickly skip child InlineInfo +/// objects where the addres ranges isn't contained in the InlineInfo object +/// or its children. This avoids allocations by not appending child InlineInfo +/// objects to the InlineInfo::Children array. +/// +/// \param Data The binary stream to read the data from. +/// +/// \param Offset The byte offset within \a Data. +/// +/// \param SkippedRanges If true, address ranges have already been skipped. + +static bool skip(DataExtractor &Data, uint64_t &Offset, bool SkippedRanges) { + if (!SkippedRanges) { + if (AddressRanges::skip(Data, Offset) == 0) + return false; + } + bool HasChildren = Data.getU8(&Offset) != 0; + Data.getU32(&Offset); // Skip Inline.Name. + Data.getULEB128(&Offset); // Skip Inline.CallFile. + Data.getULEB128(&Offset); // Skip Inline.CallLine. + if (HasChildren) { + while (skip(Data, Offset, false /* SkippedRanges */)) + /* Do nothing */; + } + // We skipped a valid InlineInfo. + return true; +} + +/// A Lookup helper functions. +/// +/// Used during the InlineInfo::lookup() call to quickly only parse an +/// InlineInfo object if the address falls within this object. This avoids +/// allocations by not appending child InlineInfo objects to the +/// InlineInfo::Children array and also skips any InlineInfo objects that do +/// not contain the address we are looking up. +/// +/// \param Data The binary stream to read the data from. +/// +/// \param Offset The byte offset within \a Data. +/// +/// \param BaseAddr The address that the relative address range offsets are +/// relative to. + +static bool lookup(const GsymReader &GR, DataExtractor &Data, uint64_t &Offset, + uint64_t BaseAddr, uint64_t Addr, SourceLocations &SrcLocs, + llvm::Error &Err) { + InlineInfo Inline; + Inline.Ranges.decode(Data, BaseAddr, Offset); + if (Inline.Ranges.empty()) + return true; + // Check if the address is contained within the inline information, and if + // not, quickly skip this InlineInfo object and all its children. + if (!Inline.Ranges.contains(Addr)) { + skip(Data, Offset, true /* SkippedRanges */); + return false; + } + + // The address range is contained within this InlineInfo, add the source + // location for this InlineInfo and any children that contain the address. + bool HasChildren = Data.getU8(&Offset) != 0; + Inline.Name = Data.getU32(&Offset); + Inline.CallFile = (uint32_t)Data.getULEB128(&Offset); + Inline.CallLine = (uint32_t)Data.getULEB128(&Offset); + if (HasChildren) { + // Child address ranges are encoded relative to the first address in the + // parent InlineInfo object. + const auto ChildBaseAddr = Inline.Ranges[0].Start; + bool Done = false; + while (!Done) + Done = lookup(GR, Data, Offset, ChildBaseAddr, Addr, SrcLocs, Err); + } + + Optional<FileEntry> CallFile = GR.getFile(Inline.CallFile); + if (!CallFile) { + Err = createStringError(std::errc::invalid_argument, + "failed to extract file[%" PRIu32 "]", + Inline.CallFile); + return false; + } + + SourceLocation SrcLoc; + SrcLoc.Name = SrcLocs.back().Name; + SrcLoc.Dir = GR.getString(CallFile->Dir); + SrcLoc.Base = GR.getString(CallFile->Base); + SrcLoc.Line = Inline.CallLine; + SrcLocs.back().Name = GR.getString(Inline.Name); + SrcLocs.push_back(SrcLoc); + return true; +} + +llvm::Error InlineInfo::lookup(const GsymReader &GR, DataExtractor &Data, + uint64_t BaseAddr, uint64_t Addr, + SourceLocations &SrcLocs) { + // Call our recursive helper function starting at offset zero. + uint64_t Offset = 0; + llvm::Error Err = Error::success(); + ::lookup(GR, Data, Offset, BaseAddr, Addr, SrcLocs, Err); + return Err; +} + /// Decode an InlineInfo in Data at the specified offset. /// /// A local helper function to decode InlineInfo objects. This function is diff --git a/llvm/lib/DebugInfo/GSYM/LineTable.cpp b/llvm/lib/DebugInfo/GSYM/LineTable.cpp index 824c0041be9..a49a3ba9bf2 100644 --- a/llvm/lib/DebugInfo/GSYM/LineTable.cpp +++ b/llvm/lib/DebugInfo/GSYM/LineTable.cpp @@ -262,8 +262,8 @@ llvm::Expected<LineTable> LineTable::decode(DataExtractor &Data, // Parse the line table on the fly and find the row we are looking for. // We will need to determine if we need to cache the line table by calling // LineTable::parseAllEntries(...) or just call this function each time. -// There is a CPU vs memory tradeoff we will need to determine. -LineEntry LineTable::lookup(DataExtractor &Data, uint64_t BaseAddr, uint64_t Addr) { +// There is a CPU vs memory tradeoff we will need to determined. +Expected<LineEntry> LineTable::lookup(DataExtractor &Data, uint64_t BaseAddr, uint64_t Addr) { LineEntry Result; llvm::Error Err = parse(Data, BaseAddr, [Addr, &Result](const LineEntry &Row) -> bool { @@ -277,7 +277,13 @@ LineEntry LineTable::lookup(DataExtractor &Data, uint64_t BaseAddr, uint64_t Add } return true; // Keep parsing till we find the right row. }); - return Result; + if (Err) + return std::move(Err); + if (Result.isValid()) + return Result; + return createStringError(std::errc::invalid_argument, + "address 0x%" PRIx64 " is not in the line table", + Addr); } raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const LineTable <) { diff --git a/llvm/lib/DebugInfo/GSYM/LookupResult.cpp b/llvm/lib/DebugInfo/GSYM/LookupResult.cpp new file mode 100644 index 00000000000..7b7ee8c3d79 --- /dev/null +++ b/llvm/lib/DebugInfo/GSYM/LookupResult.cpp @@ -0,0 +1,68 @@ +//===- LookupResult.cpp -------------------------------------------------*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/GSYM/LookupResult.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace gsym; + +std::string LookupResult::getSourceFile(uint32_t Index) const { + std::string Fullpath; + if (Index < Locations.size()) { + if (!Locations[Index].Dir.empty()) { + if (Locations[Index].Base.empty()) { + Fullpath = Locations[Index].Dir; + } else { + llvm::SmallString<64> Storage; + llvm::sys::path::append(Storage, Locations[Index].Dir, + Locations[Index].Base); + Fullpath.assign(Storage.begin(), Storage.end()); + } + } else if (!Locations[Index].Base.empty()) + Fullpath = Locations[Index].Base; + } + return Fullpath; +} + +raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const SourceLocation &SL) { + OS << SL.Name << " @ "; + if (!SL.Dir.empty()) { + OS << SL.Dir; + if (SL.Dir.contains('\\') and not SL.Dir.contains('/')) + OS << '\\'; + else + OS << '/'; + } + if (SL.Base.empty()) + OS << "<invalid-file>"; + else + OS << SL.Base; + OS << ':' << SL.Line; + return OS; +} + +raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const LookupResult &LR) { + OS << HEX64(LR.LookupAddr) << ": "; + auto NumLocations = LR.Locations.size(); + for (size_t I = 0; I < NumLocations; ++I) { + if (I > 0) { + OS << '\n'; + OS.indent(20); + } + const bool IsInlined = I + 1 != NumLocations; + OS << LR.Locations[I]; + if (IsInlined) + OS << " [inlined]"; + } + OS << '\n'; + return OS; +} diff --git a/llvm/lib/DebugInfo/GSYM/Range.cpp b/llvm/lib/DebugInfo/GSYM/Range.cpp index 19ab700fdd5..f78101e49bf 100644 --- a/llvm/lib/DebugInfo/GSYM/Range.cpp +++ b/llvm/lib/DebugInfo/GSYM/Range.cpp @@ -100,3 +100,15 @@ void AddressRanges::decode(DataExtractor &Data, uint64_t BaseAddr, for (auto &Range : Ranges) Range.decode(Data, BaseAddr, Offset); } + +void AddressRange::skip(DataExtractor &Data, uint64_t &Offset) { + Data.getULEB128(&Offset); + Data.getULEB128(&Offset); +} + +uint64_t AddressRanges::skip(DataExtractor &Data, uint64_t &Offset) { + uint64_t NumRanges = Data.getULEB128(&Offset); + for (uint64_t I=0; I<NumRanges; ++I) + AddressRange::skip(Data, Offset); + return NumRanges; +} |