#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>
#include <inttypes.h>
using namespace llvm;
using namespace gsym;
raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const InlineInfo &II) {
if (!II.isValid())
return OS;
bool First = true;
for (auto Range : II.Ranges) {
if (First)
First = false;
else
OS << ' ';
OS << Range;
}
OS << " Name = " << HEX32(II.Name) << ", CallFile = " << II.CallFile
<< ", CallLine = " << II.CallFile << '\n';
for (const auto &Child : II.Children)
OS << Child;
return OS;
}
static bool getInlineStackHelper(const InlineInfo &II, uint64_t Addr,
std::vector<const InlineInfo *> &InlineStack) {
if (II.Ranges.contains(Addr)) {
if (II.Name != 0)
InlineStack.insert(InlineStack.begin(), &II);
for (const auto &Child : II.Children) {
if (::getInlineStackHelper(Child, Addr, InlineStack))
break;
}
return !InlineStack.empty();
}
return false;
}
llvm::Optional<InlineInfo::InlineArray> InlineInfo::getInlineStack(uint64_t Addr) const {
InlineArray Result;
if (getInlineStackHelper(*this, Addr, Result))
return Result;
return llvm::None;
}
static bool skip(DataExtractor &Data, uint64_t &Offset, bool SkippedRanges) {
if (!SkippedRanges) {
if (skipRanges(Data, Offset) == 0)
return false;
}
bool HasChildren = Data.getU8(&Offset) != 0;
Data.getU32(&Offset); Data.getULEB128(&Offset); Data.getULEB128(&Offset); if (HasChildren) {
while (skip(Data, Offset, false ))
;
}
return true;
}
static bool lookup(const GsymReader &GR, DataExtractor &Data, uint64_t &Offset,
uint64_t BaseAddr, uint64_t Addr, SourceLocations &SrcLocs,
llvm::Error &Err) {
InlineInfo Inline;
decodeRanges(Inline.Ranges, Data, BaseAddr, Offset);
if (Inline.Ranges.empty())
return true;
if (!Inline.Ranges.contains(Addr)) {
skip(Data, Offset, true );
return false;
}
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) {
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;
}
if (CallFile->Dir || CallFile->Base) {
SourceLocation SrcLoc;
SrcLoc.Name = SrcLocs.back().Name;
SrcLoc.Offset = SrcLocs.back().Offset;
SrcLoc.Dir = GR.getString(CallFile->Dir);
SrcLoc.Base = GR.getString(CallFile->Base);
SrcLoc.Line = Inline.CallLine;
SrcLocs.back().Name = GR.getString(Inline.Name);
SrcLocs.back().Offset = Addr - Inline.Ranges[0].start();
SrcLocs.push_back(SrcLoc);
}
return true;
}
llvm::Error InlineInfo::lookup(const GsymReader &GR, DataExtractor &Data,
uint64_t BaseAddr, uint64_t Addr,
SourceLocations &SrcLocs) {
uint64_t Offset = 0;
llvm::Error Err = Error::success();
::lookup(GR, Data, Offset, BaseAddr, Addr, SrcLocs, Err);
return Err;
}
static llvm::Expected<InlineInfo> decode(DataExtractor &Data, uint64_t &Offset,
uint64_t BaseAddr) {
InlineInfo Inline;
if (!Data.isValidOffset(Offset))
return createStringError(std::errc::io_error,
"0x%8.8" PRIx64 ": missing InlineInfo address ranges data", Offset);
decodeRanges(Inline.Ranges, Data, BaseAddr, Offset);
if (Inline.Ranges.empty())
return Inline;
if (!Data.isValidOffsetForDataOfSize(Offset, 1))
return createStringError(std::errc::io_error,
"0x%8.8" PRIx64 ": missing InlineInfo uint8_t indicating children",
Offset);
bool HasChildren = Data.getU8(&Offset) != 0;
if (!Data.isValidOffsetForDataOfSize(Offset, 4))
return createStringError(std::errc::io_error,
"0x%8.8" PRIx64 ": missing InlineInfo uint32_t for name", Offset);
Inline.Name = Data.getU32(&Offset);
if (!Data.isValidOffset(Offset))
return createStringError(std::errc::io_error,
"0x%8.8" PRIx64 ": missing ULEB128 for InlineInfo call file", Offset);
Inline.CallFile = (uint32_t)Data.getULEB128(&Offset);
if (!Data.isValidOffset(Offset))
return createStringError(std::errc::io_error,
"0x%8.8" PRIx64 ": missing ULEB128 for InlineInfo call line", Offset);
Inline.CallLine = (uint32_t)Data.getULEB128(&Offset);
if (HasChildren) {
const auto ChildBaseAddr = Inline.Ranges[0].start();
while (true) {
llvm::Expected<InlineInfo> Child = decode(Data, Offset, ChildBaseAddr);
if (!Child)
return Child.takeError();
if (Child.get().Ranges.empty())
break;
Inline.Children.emplace_back(std::move(*Child));
}
}
return Inline;
}
llvm::Expected<InlineInfo> InlineInfo::decode(DataExtractor &Data,
uint64_t BaseAddr) {
uint64_t Offset = 0;
return ::decode(Data, Offset, BaseAddr);
}
llvm::Error InlineInfo::encode(FileWriter &O, uint64_t BaseAddr) const {
if (!isValid())
return createStringError(std::errc::invalid_argument,
"attempted to encode invalid InlineInfo object");
encodeRanges(Ranges, O, BaseAddr);
bool HasChildren = !Children.empty();
O.writeU8(HasChildren);
O.writeU32(Name);
O.writeULEB(CallFile);
O.writeULEB(CallLine);
if (HasChildren) {
const uint64_t ChildBaseAddr = Ranges[0].start();
for (const auto &Child : Children) {
for (const auto &ChildRange: Child.Ranges) {
if (!Ranges.contains(ChildRange))
return createStringError(std::errc::invalid_argument,
"child range not contained in parent");
}
llvm::Error Err = Child.encode(O, ChildBaseAddr);
if (Err)
return Err;
}
O.writeULEB(0);
}
return Error::success();
}