CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
620 lines
22 KiB
C++
620 lines
22 KiB
C++
//===- GsymReader.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/GsymReader.h"
|
|
|
|
#include <assert.h>
|
|
#include <inttypes.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "llvm/DebugInfo/GSYM/GsymReaderV1.h"
|
|
#include "llvm/DebugInfo/GSYM/GsymReaderV2.h"
|
|
#include "llvm/DebugInfo/GSYM/Header.h"
|
|
#include "llvm/DebugInfo/GSYM/HeaderV2.h"
|
|
#include "llvm/DebugInfo/GSYM/InlineInfo.h"
|
|
#include "llvm/DebugInfo/GSYM/LineTable.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
|
|
using namespace llvm;
|
|
using namespace gsym;
|
|
|
|
GsymReader::GsymReader(std::unique_ptr<MemoryBuffer> Buffer,
|
|
llvm::endianness Endian)
|
|
: MemBuffer(std::move(Buffer)), Endian(Endian),
|
|
AddrInfoOffsetsData(StringRef(), true), FileEntryData(StringRef(), true) {
|
|
}
|
|
|
|
/// Check magic bytes, determine endianness, and return the GSYM version and
|
|
/// endianness. If magic bytes are invalid, return error.
|
|
static Expected<std::pair<uint16_t, llvm::endianness>>
|
|
checkMagicAndDetectVersionEndian(StringRef Bytes) {
|
|
if (Bytes.size() < 6)
|
|
return createStringError(std::errc::invalid_argument,
|
|
"data too small to be a GSYM file");
|
|
// Detect host endian
|
|
const auto HostEndian = llvm::endianness::native;
|
|
const bool IsHostLittleEndian = (HostEndian == llvm::endianness::little);
|
|
// Read magic bytes using host endian
|
|
GsymDataExtractor Data(Bytes, IsHostLittleEndian);
|
|
uint64_t Offset = 0;
|
|
uint32_t Magic = Data.getU32(&Offset);
|
|
llvm::endianness FileEndian;
|
|
// If magic bytes looks alright, the host and the file have the same
|
|
// endianness, vice versa.
|
|
if (Magic == GSYM_MAGIC) {
|
|
FileEndian = HostEndian;
|
|
} else if (Magic == GSYM_CIGAM) {
|
|
FileEndian =
|
|
IsHostLittleEndian ? llvm::endianness::big : llvm::endianness::little;
|
|
// Re-create GsymDataExtractor with correct endianness to read version.
|
|
Data = GsymDataExtractor(Bytes, !IsHostLittleEndian);
|
|
} else {
|
|
return createStringError(std::errc::invalid_argument,
|
|
"not a GSYM file (bad magic)");
|
|
}
|
|
// Read version using the correct endian
|
|
uint16_t Version = Data.getU16(&Offset);
|
|
return std::make_pair(Version, FileEndian);
|
|
}
|
|
|
|
llvm::Expected<std::unique_ptr<GsymReader>>
|
|
GsymReader::openFile(StringRef Filename) {
|
|
// Open the input file and return an appropriate error if needed.
|
|
ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
|
|
MemoryBuffer::getFileOrSTDIN(Filename);
|
|
auto Err = BuffOrErr.getError();
|
|
if (Err)
|
|
return llvm::errorCodeToError(Err);
|
|
auto &Buf = BuffOrErr.get();
|
|
Buf->randomAccessIfMmap();
|
|
return create(Buf);
|
|
}
|
|
|
|
llvm::Expected<std::unique_ptr<GsymReader>>
|
|
GsymReader::copyBuffer(StringRef Bytes) {
|
|
auto MemBuffer = MemoryBuffer::getMemBufferCopy(Bytes, "GSYM bytes");
|
|
return create(MemBuffer);
|
|
}
|
|
|
|
llvm::Expected<std::unique_ptr<GsymReader>>
|
|
GsymReader::create(std::unique_ptr<MemoryBuffer> &MemBuffer) {
|
|
if (!MemBuffer)
|
|
return createStringError(std::errc::invalid_argument,
|
|
"invalid memory buffer");
|
|
Expected<std::pair<uint16_t, llvm::endianness>> VersionEndianOrErr =
|
|
checkMagicAndDetectVersionEndian(MemBuffer->getBuffer());
|
|
if (!VersionEndianOrErr)
|
|
return VersionEndianOrErr.takeError();
|
|
uint16_t Version;
|
|
llvm::endianness Endian;
|
|
std::tie(Version, Endian) = *VersionEndianOrErr;
|
|
std::unique_ptr<GsymReader> GR;
|
|
switch (Version) {
|
|
case Header::getVersion():
|
|
GR.reset(new GsymReaderV1(std::move(MemBuffer), Endian));
|
|
break;
|
|
case HeaderV2::getVersion():
|
|
GR.reset(new GsymReaderV2(std::move(MemBuffer), Endian));
|
|
break;
|
|
default:
|
|
return createStringError(std::errc::invalid_argument,
|
|
"unsupported GSYM version %u", Version);
|
|
}
|
|
if (auto Err = GR->parse())
|
|
return std::move(Err);
|
|
return std::move(GR);
|
|
}
|
|
|
|
llvm::Error GsymReader::parse() {
|
|
// Step 1: Parse the version-specific header and populate GlobalDataSections.
|
|
if (auto Err = parseHeaderAndGlobalDataEntries())
|
|
return Err;
|
|
|
|
// Step 2: Validate that all required sections are present and consistent.
|
|
for (auto Type :
|
|
{GlobalInfoType::AddrOffsets, GlobalInfoType::AddrInfoOffsets,
|
|
GlobalInfoType::StringTable, GlobalInfoType::FileTable,
|
|
GlobalInfoType::FunctionInfo})
|
|
if (!GlobalDataSections.count(Type))
|
|
return createStringError(
|
|
std::errc::invalid_argument, "missing required section type %s (%u)",
|
|
getNameForGlobalInfoType(Type).data(), static_cast<uint32_t>(Type));
|
|
|
|
if (GlobalDataSections[GlobalInfoType::AddrOffsets].FileSize !=
|
|
static_cast<uint64_t>(getNumAddresses()) * getAddressOffsetSize())
|
|
return createStringError(std::errc::invalid_argument,
|
|
"AddrOffsets section size mismatch");
|
|
|
|
if (GlobalDataSections[GlobalInfoType::AddrInfoOffsets].FileSize !=
|
|
static_cast<uint64_t>(getNumAddresses()) * getAddressInfoOffsetSize())
|
|
return createStringError(std::errc::invalid_argument,
|
|
"AddrInfoOffsets section size mismatch");
|
|
|
|
// Step 3: Parse each global data section.
|
|
llvm::Expected<StringRef> Bytes =
|
|
getRequiredGlobalDataBytes(GlobalInfoType::AddrOffsets);
|
|
if (!Bytes)
|
|
return Bytes.takeError();
|
|
if (auto Err = parseAddrOffsets(*Bytes))
|
|
return Err;
|
|
|
|
Bytes = getRequiredGlobalDataBytes(GlobalInfoType::AddrInfoOffsets);
|
|
if (!Bytes)
|
|
return Bytes.takeError();
|
|
if (auto Err = setAddrInfoOffsetsData(*Bytes))
|
|
return Err;
|
|
|
|
Bytes = getRequiredGlobalDataBytes(GlobalInfoType::StringTable);
|
|
if (!Bytes)
|
|
return Bytes.takeError();
|
|
if (auto Err = setStringTableData(*Bytes))
|
|
return Err;
|
|
|
|
Bytes = getRequiredGlobalDataBytes(GlobalInfoType::FileTable);
|
|
if (!Bytes)
|
|
return Bytes.takeError();
|
|
if (auto Err = setFileTableData(*Bytes))
|
|
return Err;
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
llvm::Error GsymReader::parseGlobalDataEntries(uint64_t Offset) {
|
|
if (getVersion() < HeaderV2::getVersion())
|
|
return createStringError(std::errc::invalid_argument,
|
|
"GlobalData section not supported in GSYM V1");
|
|
|
|
const StringRef Buf = MemBuffer->getBuffer();
|
|
const uint64_t BufSize = Buf.size();
|
|
GsymDataExtractor Data(Buf, isLittleEndian());
|
|
while (Offset + sizeof(GlobalData) <= BufSize) {
|
|
auto GDOrErr = GlobalData::decode(Data, Offset);
|
|
if (!GDOrErr)
|
|
return GDOrErr.takeError();
|
|
const GlobalData &GD = *GDOrErr;
|
|
|
|
if (GD.Type == GlobalInfoType::EndOfList)
|
|
return Error::success();
|
|
|
|
if (GD.FileSize == 0)
|
|
return createStringError(std::errc::invalid_argument,
|
|
"GlobalData section type %u has zero size",
|
|
static_cast<uint32_t>(GD.Type));
|
|
|
|
if (GD.FileOffset + GD.FileSize > BufSize)
|
|
return createStringError(
|
|
std::errc::invalid_argument,
|
|
"GlobalData section type %u extends beyond "
|
|
"buffer (offset=%" PRIu64 ", size=%" PRIu64 ", bufsize=%" PRIu64 ")",
|
|
static_cast<uint32_t>(GD.Type), GD.FileOffset, GD.FileSize, BufSize);
|
|
|
|
GlobalDataSections[GD.Type] = GD;
|
|
}
|
|
return createStringError(std::errc::invalid_argument,
|
|
"GlobalData array not terminated by EndOfList");
|
|
}
|
|
|
|
llvm::Error GsymReader::parseAddrOffsets(StringRef Bytes) {
|
|
const uint8_t AddrOffSize = getAddressOffsetSize();
|
|
const uint32_t NumAddrs = getNumAddresses();
|
|
const size_t TotalBytes = NumAddrs * AddrOffSize;
|
|
if (Bytes.size() < TotalBytes)
|
|
return createStringError(std::errc::invalid_argument,
|
|
"failed to read address table");
|
|
|
|
// Parse the non-swap case
|
|
if (Endian == llvm::endianness::native) {
|
|
AddrOffsets = ArrayRef<uint8_t>(
|
|
reinterpret_cast<const uint8_t *>(Bytes.data()), TotalBytes);
|
|
return Error::success();
|
|
}
|
|
|
|
// Parse the swap case
|
|
GsymDataExtractor Data(Bytes, isLittleEndian());
|
|
uint64_t Offset = 0;
|
|
SwappedAddrOffsets.resize(TotalBytes);
|
|
switch (AddrOffSize) {
|
|
case 1:
|
|
if (!Data.getU8(&Offset, SwappedAddrOffsets.data(), NumAddrs))
|
|
return createStringError(std::errc::invalid_argument,
|
|
"failed to read address table");
|
|
break;
|
|
case 2:
|
|
if (!Data.getU16(&Offset,
|
|
reinterpret_cast<uint16_t *>(SwappedAddrOffsets.data()),
|
|
NumAddrs))
|
|
return createStringError(std::errc::invalid_argument,
|
|
"failed to read address table");
|
|
break;
|
|
case 4:
|
|
if (!Data.getU32(&Offset,
|
|
reinterpret_cast<uint32_t *>(SwappedAddrOffsets.data()),
|
|
NumAddrs))
|
|
return createStringError(std::errc::invalid_argument,
|
|
"failed to read address table");
|
|
break;
|
|
case 8:
|
|
if (!Data.getU64(&Offset,
|
|
reinterpret_cast<uint64_t *>(SwappedAddrOffsets.data()),
|
|
NumAddrs))
|
|
return createStringError(std::errc::invalid_argument,
|
|
"failed to read address table");
|
|
break;
|
|
}
|
|
AddrOffsets = ArrayRef<uint8_t>(SwappedAddrOffsets);
|
|
return Error::success();
|
|
}
|
|
|
|
llvm::Error GsymReader::setAddrInfoOffsetsData(StringRef Bytes) {
|
|
AddrInfoOffsetsData = GsymDataExtractor(Bytes, isLittleEndian());
|
|
return Error::success();
|
|
}
|
|
|
|
llvm::Error GsymReader::setStringTableData(StringRef Bytes) {
|
|
StrTab.Data = Bytes;
|
|
return Error::success();
|
|
}
|
|
|
|
llvm::Error GsymReader::setFileTableData(StringRef Bytes) {
|
|
const uint8_t StrpSize = getStringOffsetSize();
|
|
GsymDataExtractor Data(Bytes, isLittleEndian(), StrpSize);
|
|
uint64_t Offset = 0;
|
|
uint32_t NumFiles = Data.getU32(&Offset);
|
|
uint64_t EntriesSize =
|
|
static_cast<uint64_t>(NumFiles) * FileEntry::getEncodedSize(StrpSize);
|
|
if (Bytes.size() < Offset + EntriesSize)
|
|
return createStringError(std::errc::invalid_argument,
|
|
"FileTable section too small for %u files",
|
|
NumFiles);
|
|
FileEntryData = GsymDataExtractor(Data, Offset, EntriesSize);
|
|
return Error::success();
|
|
}
|
|
|
|
std::optional<GlobalData> GsymReader::getGlobalData(GlobalInfoType Type) const {
|
|
auto It = GlobalDataSections.find(Type);
|
|
if (It == GlobalDataSections.end())
|
|
return std::nullopt;
|
|
return It->second;
|
|
}
|
|
|
|
llvm::Expected<StringRef>
|
|
GsymReader::getRequiredGlobalDataBytes(GlobalInfoType Type) const {
|
|
if (auto Data = getOptionalGlobalDataBytes(Type))
|
|
return *Data;
|
|
const char *TypeName = getNameForGlobalInfoType(Type).data();
|
|
std::optional<GlobalData> GD = getGlobalData(Type);
|
|
// We have a GlobalData entry but didn't get any bytes — the file may be
|
|
// truncated.
|
|
if (GD)
|
|
return createStringError(
|
|
std::errc::invalid_argument,
|
|
"missing bytes for %s, GSYM file might be truncated", TypeName);
|
|
return createStringError(std::errc::invalid_argument,
|
|
"missing required section type %s", TypeName);
|
|
}
|
|
|
|
std::optional<StringRef>
|
|
GsymReader::getOptionalGlobalDataBytes(GlobalInfoType Type) const {
|
|
std::optional<GlobalData> GD = getGlobalData(Type);
|
|
if (!GD)
|
|
return std::nullopt;
|
|
StringRef Buf = MemBuffer->getBuffer();
|
|
if (GD->FileSize == 0 || GD->FileOffset + GD->FileSize > Buf.size())
|
|
return std::nullopt;
|
|
return Buf.substr(GD->FileOffset, GD->FileSize);
|
|
}
|
|
|
|
std::optional<uint64_t> GsymReader::getAddress(size_t Index) const {
|
|
switch (getAddressOffsetSize()) {
|
|
case 1: return addressForIndex<uint8_t>(Index);
|
|
case 2: return addressForIndex<uint16_t>(Index);
|
|
case 4: return addressForIndex<uint32_t>(Index);
|
|
case 8: return addressForIndex<uint64_t>(Index);
|
|
default:
|
|
llvm_unreachable("unsupported address offset size");
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<uint64_t> GsymReader::getAddressInfoOffset(size_t Index) const {
|
|
if (Index >= getNumAddresses())
|
|
return std::nullopt;
|
|
const uint8_t AddrInfoOffsetSize = getAddressInfoOffsetSize();
|
|
uint64_t Offset = Index * AddrInfoOffsetSize;
|
|
uint64_t AddrInfoOffset =
|
|
AddrInfoOffsetsData.getUnsigned(&Offset, AddrInfoOffsetSize);
|
|
// V1 stores absolute file offsets in AddrInfoOffsets, so no base offset is
|
|
// needed. V2+ stores offsets relative to the FunctionInfo section start.
|
|
if (getVersion() != Header::getVersion())
|
|
AddrInfoOffset +=
|
|
GlobalDataSections.at(GlobalInfoType::FunctionInfo).FileOffset;
|
|
return AddrInfoOffset;
|
|
}
|
|
|
|
Expected<uint64_t> GsymReader::getAddressIndex(const uint64_t Addr) const {
|
|
const uint64_t BaseAddr = getBaseAddress();
|
|
if (Addr >= BaseAddr) {
|
|
const uint64_t AddrOffset = Addr - BaseAddr;
|
|
std::optional<uint64_t> AddrOffsetIndex;
|
|
switch (getAddressOffsetSize()) {
|
|
case 1:
|
|
AddrOffsetIndex = getAddressOffsetIndex<uint8_t>(AddrOffset);
|
|
break;
|
|
case 2:
|
|
AddrOffsetIndex = getAddressOffsetIndex<uint16_t>(AddrOffset);
|
|
break;
|
|
case 4:
|
|
AddrOffsetIndex = getAddressOffsetIndex<uint32_t>(AddrOffset);
|
|
break;
|
|
case 8:
|
|
AddrOffsetIndex = getAddressOffsetIndex<uint64_t>(AddrOffset);
|
|
break;
|
|
default:
|
|
return createStringError(std::errc::invalid_argument,
|
|
"unsupported address offset size %u",
|
|
getAddressOffsetSize());
|
|
}
|
|
if (AddrOffsetIndex)
|
|
return *AddrOffsetIndex;
|
|
}
|
|
return createStringError(std::errc::invalid_argument,
|
|
"address 0x%" PRIx64 " is not in GSYM", Addr);
|
|
}
|
|
|
|
llvm::Expected<GsymDataExtractor>
|
|
GsymReader::getFunctionInfoDataForAddress(uint64_t Addr,
|
|
uint64_t &FuncStartAddr) const {
|
|
Expected<uint64_t> ExpectedAddrIdx = getAddressIndex(Addr);
|
|
if (!ExpectedAddrIdx)
|
|
return ExpectedAddrIdx.takeError();
|
|
const uint64_t FirstAddrIdx = *ExpectedAddrIdx;
|
|
// The AddrIdx is the first index of the function info entries that match
|
|
// \a Addr. We need to iterate over all function info objects that start with
|
|
// the same address until we find a range that contains \a Addr.
|
|
std::optional<uint64_t> FirstFuncStartAddr;
|
|
const size_t NumAddresses = getNumAddresses();
|
|
for (uint64_t AddrIdx = FirstAddrIdx; AddrIdx < NumAddresses; ++AddrIdx) {
|
|
auto ExpextedData = getFunctionInfoDataAtIndex(AddrIdx, FuncStartAddr);
|
|
// If there was an error, return the error.
|
|
if (!ExpextedData)
|
|
return ExpextedData;
|
|
|
|
// Remember the first function start address if it hasn't already been set.
|
|
// If it is already valid, check to see if it matches the first function
|
|
// start address and only continue if it matches.
|
|
if (FirstFuncStartAddr.has_value()) {
|
|
if (*FirstFuncStartAddr != FuncStartAddr)
|
|
break; // Done with consecutive function entries with same address.
|
|
} else {
|
|
FirstFuncStartAddr = FuncStartAddr;
|
|
}
|
|
// Make sure the current function address ranges contains \a Addr.
|
|
// Some symbols on Darwin don't have valid sizes, so if we run into a
|
|
// symbol with zero size, then we have found a match for our address.
|
|
|
|
// The first thing the encoding of a FunctionInfo object is the function
|
|
// size.
|
|
uint64_t Offset = 0;
|
|
uint32_t FuncSize = ExpextedData->getU32(&Offset);
|
|
if (FuncSize == 0 ||
|
|
AddressRange(FuncStartAddr, FuncStartAddr + FuncSize).contains(Addr))
|
|
return ExpextedData;
|
|
}
|
|
return createStringError(std::errc::invalid_argument,
|
|
"address 0x%" PRIx64 " is not in GSYM", Addr);
|
|
}
|
|
|
|
llvm::Expected<GsymDataExtractor>
|
|
GsymReader::getFunctionInfoDataAtIndex(uint64_t AddrIdx,
|
|
uint64_t &FuncStartAddr) const {
|
|
const std::optional<uint64_t> AddrInfoOffset = getAddressInfoOffset(AddrIdx);
|
|
if (AddrInfoOffset == std::nullopt)
|
|
return createStringError(std::errc::invalid_argument,
|
|
"invalid address index %" PRIu64, AddrIdx);
|
|
assert((Endian == endianness::big || Endian == endianness::little) &&
|
|
"Endian must be either big or little");
|
|
StringRef Bytes = MemBuffer->getBuffer().substr(*AddrInfoOffset);
|
|
if (Bytes.empty())
|
|
return createStringError(std::errc::invalid_argument,
|
|
"invalid address info offset 0x%" PRIx64,
|
|
*AddrInfoOffset);
|
|
std::optional<uint64_t> OptFuncStartAddr = getAddress(AddrIdx);
|
|
if (!OptFuncStartAddr)
|
|
return createStringError(std::errc::invalid_argument,
|
|
"failed to extract address[%" PRIu64 "]", AddrIdx);
|
|
FuncStartAddr = *OptFuncStartAddr;
|
|
GsymDataExtractor Data(Bytes, isLittleEndian(), getStringOffsetSize());
|
|
return Data;
|
|
}
|
|
|
|
llvm::Expected<FunctionInfo> GsymReader::getFunctionInfo(uint64_t Addr) const {
|
|
uint64_t FuncStartAddr = 0;
|
|
if (auto ExpectedData = getFunctionInfoDataForAddress(Addr, FuncStartAddr))
|
|
return FunctionInfo::decode(*ExpectedData, FuncStartAddr);
|
|
else
|
|
return ExpectedData.takeError();
|
|
}
|
|
|
|
llvm::Expected<FunctionInfo>
|
|
GsymReader::getFunctionInfoAtIndex(uint64_t Idx) const {
|
|
uint64_t FuncStartAddr = 0;
|
|
if (auto ExpectedData = getFunctionInfoDataAtIndex(Idx, FuncStartAddr))
|
|
return FunctionInfo::decode(*ExpectedData, FuncStartAddr);
|
|
else
|
|
return ExpectedData.takeError();
|
|
}
|
|
|
|
llvm::Expected<LookupResult> GsymReader::lookup(
|
|
uint64_t Addr,
|
|
std::optional<GsymDataExtractor> *MergedFunctionsData) const {
|
|
uint64_t FuncStartAddr = 0;
|
|
if (auto ExpectedData = getFunctionInfoDataForAddress(Addr, FuncStartAddr))
|
|
return FunctionInfo::lookup(*ExpectedData, *this, FuncStartAddr, Addr,
|
|
MergedFunctionsData);
|
|
else
|
|
return ExpectedData.takeError();
|
|
}
|
|
|
|
llvm::Expected<std::vector<LookupResult>>
|
|
GsymReader::lookupAll(uint64_t Addr) const {
|
|
std::vector<LookupResult> Results;
|
|
std::optional<GsymDataExtractor> MergedFunctionsData;
|
|
|
|
// First perform a lookup to get the primary function info result.
|
|
auto MainResult = lookup(Addr, &MergedFunctionsData);
|
|
if (!MainResult)
|
|
return MainResult.takeError();
|
|
|
|
// Add the main result as the first entry.
|
|
Results.push_back(std::move(*MainResult));
|
|
|
|
// Now process any merged functions data that was found during the lookup.
|
|
if (MergedFunctionsData) {
|
|
// Get data extractors for each merged function.
|
|
auto ExpectedMergedFuncExtractors =
|
|
MergedFunctionsInfo::getFuncsDataExtractors(*MergedFunctionsData);
|
|
if (!ExpectedMergedFuncExtractors)
|
|
return ExpectedMergedFuncExtractors.takeError();
|
|
|
|
// Process each merged function data.
|
|
for (GsymDataExtractor &MergedData : *ExpectedMergedFuncExtractors) {
|
|
if (auto FI = FunctionInfo::lookup(MergedData, *this,
|
|
MainResult->FuncRange.start(), Addr)) {
|
|
Results.push_back(std::move(*FI));
|
|
} else {
|
|
return FI.takeError();
|
|
}
|
|
}
|
|
}
|
|
|
|
return Results;
|
|
}
|
|
|
|
void GsymReader::dump(raw_ostream &OS, const FunctionInfo &FI,
|
|
uint32_t Indent) {
|
|
OS.indent(Indent);
|
|
OS << FI.Range << " \"" << getString(FI.Name) << "\"\n";
|
|
if (FI.OptLineTable)
|
|
dump(OS, *FI.OptLineTable, Indent);
|
|
if (FI.Inline)
|
|
dump(OS, *FI.Inline, Indent);
|
|
|
|
if (FI.CallSites)
|
|
dump(OS, *FI.CallSites, Indent);
|
|
|
|
if (FI.MergedFunctions) {
|
|
assert(Indent == 0 && "MergedFunctionsInfo should only exist at top level");
|
|
dump(OS, *FI.MergedFunctions);
|
|
}
|
|
}
|
|
|
|
void GsymReader::dump(raw_ostream &OS, const MergedFunctionsInfo &MFI) {
|
|
for (uint32_t inx = 0; inx < MFI.MergedFunctions.size(); inx++) {
|
|
OS << "++ Merged FunctionInfos[" << inx << "]:\n";
|
|
dump(OS, MFI.MergedFunctions[inx], 4);
|
|
}
|
|
}
|
|
|
|
void GsymReader::dump(raw_ostream &OS, const CallSiteInfo &CSI) {
|
|
OS << HEX16(CSI.ReturnOffset);
|
|
|
|
std::string Flags;
|
|
auto addFlag = [&](const char *Flag) {
|
|
if (!Flags.empty())
|
|
Flags += " | ";
|
|
Flags += Flag;
|
|
};
|
|
|
|
if (CSI.Flags == CallSiteInfo::Flags::None)
|
|
Flags = "None";
|
|
else {
|
|
if (CSI.Flags & CallSiteInfo::Flags::InternalCall)
|
|
addFlag("InternalCall");
|
|
|
|
if (CSI.Flags & CallSiteInfo::Flags::ExternalCall)
|
|
addFlag("ExternalCall");
|
|
}
|
|
OS << " Flags[" << Flags << "]";
|
|
|
|
if (!CSI.MatchRegex.empty()) {
|
|
OS << " MatchRegex[";
|
|
for (uint32_t i = 0; i < CSI.MatchRegex.size(); ++i) {
|
|
if (i > 0)
|
|
OS << ";";
|
|
OS << getString(CSI.MatchRegex[i]);
|
|
}
|
|
OS << "]";
|
|
}
|
|
}
|
|
|
|
void GsymReader::dump(raw_ostream &OS, const CallSiteInfoCollection &CSIC,
|
|
uint32_t Indent) {
|
|
OS.indent(Indent);
|
|
OS << "CallSites (by relative return offset):\n";
|
|
for (const auto &CS : CSIC.CallSites) {
|
|
OS.indent(Indent);
|
|
OS << " ";
|
|
dump(OS, CS);
|
|
OS << "\n";
|
|
}
|
|
}
|
|
|
|
void GsymReader::dump(raw_ostream &OS, const LineTable <, uint32_t Indent) {
|
|
OS.indent(Indent);
|
|
OS << "LineTable:\n";
|
|
for (auto &LE : LT) {
|
|
OS.indent(Indent);
|
|
OS << " " << HEX64(LE.Addr) << ' ';
|
|
if (LE.File)
|
|
dump(OS, getFile(LE.File));
|
|
OS << ':' << LE.Line << '\n';
|
|
}
|
|
}
|
|
|
|
void GsymReader::dump(raw_ostream &OS, const InlineInfo &II, uint32_t Indent) {
|
|
if (Indent == 0)
|
|
OS << "InlineInfo:\n";
|
|
else
|
|
OS.indent(Indent);
|
|
OS << II.Ranges << ' ' << getString(II.Name);
|
|
if (II.CallFile != 0) {
|
|
if (auto File = getFile(II.CallFile)) {
|
|
OS << " called from ";
|
|
dump(OS, File);
|
|
OS << ':' << II.CallLine;
|
|
}
|
|
}
|
|
OS << '\n';
|
|
for (const auto &ChildII : II.Children)
|
|
dump(OS, ChildII, Indent + 2);
|
|
}
|
|
|
|
void GsymReader::dump(raw_ostream &OS, std::optional<FileEntry> FE) {
|
|
if (FE) {
|
|
// IF we have the file from index 0, then don't print anything
|
|
if (FE->Dir == 0 && FE->Base == 0)
|
|
return;
|
|
StringRef Dir = getString(FE->Dir);
|
|
StringRef Base = getString(FE->Base);
|
|
if (!Dir.empty()) {
|
|
OS << Dir;
|
|
if (Dir.contains('\\') && !Dir.contains('/'))
|
|
OS << '\\';
|
|
else
|
|
OS << '/';
|
|
}
|
|
if (!Base.empty()) {
|
|
OS << Base;
|
|
}
|
|
if (!Dir.empty() || !Base.empty())
|
|
return;
|
|
}
|
|
OS << "<invalid-file>";
|
|
}
|