diff options
Diffstat (limited to 'llvm/lib/DebugInfo')
54 files changed, 2193 insertions, 864 deletions
diff --git a/llvm/lib/DebugInfo/BTF/BTFContext.cpp b/llvm/lib/DebugInfo/BTF/BTFContext.cpp index 24898739b824..2e651cb378db 100644 --- a/llvm/lib/DebugInfo/BTF/BTFContext.cpp +++ b/llvm/lib/DebugInfo/BTF/BTFContext.cpp @@ -63,7 +63,9 @@ std::unique_ptr<BTFContext> BTFContext::create(const ObjectFile &Obj, std::function<void(Error)> ErrorHandler) { auto Ctx = std::make_unique<BTFContext>(); - if (Error E = Ctx->BTF.parse(Obj)) + BTFParser::ParseOptions Opts; + Opts.LoadLines = true; + if (Error E = Ctx->BTF.parse(Obj, Opts)) ErrorHandler(std::move(E)); return Ctx; } diff --git a/llvm/lib/DebugInfo/BTF/BTFParser.cpp b/llvm/lib/DebugInfo/BTF/BTFParser.cpp index 6151e1b15cbb..4fc31a445603 100644 --- a/llvm/lib/DebugInfo/BTF/BTFParser.cpp +++ b/llvm/lib/DebugInfo/BTF/BTFParser.cpp @@ -12,6 +12,8 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/BTF/BTFParser.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Endian.h" #include "llvm/Support/Errc.h" #define DEBUG_TYPE "debug-info-btf-parser" @@ -74,11 +76,13 @@ public: // Used by BTFParser::parse* auxiliary functions. struct BTFParser::ParseContext { const ObjectFile &Obj; + const ParseOptions &Opts; // Map from ELF section name to SectionRef DenseMap<StringRef, SectionRef> Sections; public: - ParseContext(const ObjectFile &Obj) : Obj(Obj) {} + ParseContext(const ObjectFile &Obj, const ParseOptions &Opts) + : Obj(Obj), Opts(Opts) {} Expected<DataExtractor> makeExtractor(SectionRef Sec) { Expected<StringRef> Contents = Sec.getContents(); @@ -119,19 +123,126 @@ Error BTFParser::parseBTF(ParseContext &Ctx, SectionRef BTF) { return Err(".BTF", C); if (HdrLen < 8) return Err("unexpected .BTF header length: ") << HdrLen; - (void)Extractor.getU32(C); // type_off - (void)Extractor.getU32(C); // type_len + uint32_t TypeOff = Extractor.getU32(C); + uint32_t TypeLen = Extractor.getU32(C); uint32_t StrOff = Extractor.getU32(C); uint32_t StrLen = Extractor.getU32(C); uint32_t StrStart = HdrLen + StrOff; uint32_t StrEnd = StrStart + StrLen; + uint32_t TypesInfoStart = HdrLen + TypeOff; + uint32_t TypesInfoEnd = TypesInfoStart + TypeLen; + uint32_t BytesExpected = std::max(StrEnd, TypesInfoEnd); if (!C) return Err(".BTF", C); - if (Extractor.getData().size() < StrEnd) + if (Extractor.getData().size() < BytesExpected) return Err("invalid .BTF section size, expecting at-least ") - << StrEnd << " bytes"; + << BytesExpected << " bytes"; + + StringsTable = Extractor.getData().slice(StrStart, StrEnd); + + if (TypeLen > 0 && Ctx.Opts.LoadTypes) { + StringRef RawData = Extractor.getData().slice(TypesInfoStart, TypesInfoEnd); + if (Error E = parseTypesInfo(Ctx, TypesInfoStart, RawData)) + return E; + } + + return Error::success(); +} + +// Compute record size for each BTF::CommonType sub-type +// (including entries in the tail position). +static size_t byteSize(BTF::CommonType *Type) { + size_t Size = sizeof(BTF::CommonType); + switch (Type->getKind()) { + case BTF::BTF_KIND_INT: + Size += sizeof(uint32_t); + break; + case BTF::BTF_KIND_ARRAY: + Size += sizeof(BTF::BTFArray); + break; + case BTF::BTF_KIND_VAR: + Size += sizeof(uint32_t); + break; + case BTF::BTF_KIND_DECL_TAG: + Size += sizeof(uint32_t); + break; + case BTF::BTF_KIND_STRUCT: + case BTF::BTF_KIND_UNION: + Size += sizeof(BTF::BTFMember) * Type->getVlen(); + break; + case BTF::BTF_KIND_ENUM: + Size += sizeof(BTF::BTFEnum) * Type->getVlen(); + break; + case BTF::BTF_KIND_ENUM64: + Size += sizeof(BTF::BTFEnum64) * Type->getVlen(); + break; + case BTF::BTF_KIND_FUNC_PROTO: + Size += sizeof(BTF::BTFParam) * Type->getVlen(); + break; + case BTF::BTF_KIND_DATASEC: + Size += sizeof(BTF::BTFDataSec) * Type->getVlen(); + break; + } + return Size; +} + +// Guard value for voids, simplifies code a bit, but NameOff is not +// actually valid. +const BTF::CommonType VoidTypeInst = {0, BTF::BTF_KIND_UNKN << 24, {0}}; + +// Type information "parsing" is very primitive: +// - The `RawData` is copied to a buffer owned by `BTFParser` instance. +// - The buffer is treated as an array of `uint32_t` values, each value +// is swapped to use native endianness. This is possible, because +// according to BTF spec all buffer elements are structures comprised +// of `uint32_t` fields. +// - `BTFParser::Types` vector is filled with pointers to buffer +// elements, using `byteSize()` function to slice the buffer at type +// record boundaries. +// - If at some point a type definition with incorrect size (logical size +// exceeding buffer boundaries) is reached it is not added to the +// `BTFParser::Types` vector and the process stops. +Error BTFParser::parseTypesInfo(ParseContext &Ctx, uint64_t TypesInfoStart, + StringRef RawData) { + using support::endian::byte_swap; + + TypesBuffer = OwningArrayRef<uint8_t>(arrayRefFromStringRef(RawData)); + // Switch endianness if necessary. + endianness Endianness = Ctx.Obj.isLittleEndian() ? llvm::endianness::little + : llvm::endianness::big; + uint32_t *TypesBuffer32 = (uint32_t *)TypesBuffer.data(); + for (uint64_t I = 0; I < TypesBuffer.size() / 4; ++I) + TypesBuffer32[I] = byte_swap(TypesBuffer32[I], Endianness); + + // The type id 0 is reserved for void type. + Types.push_back(&VoidTypeInst); + + uint64_t Pos = 0; + while (Pos < RawData.size()) { + uint64_t BytesLeft = RawData.size() - Pos; + uint64_t Offset = TypesInfoStart + Pos; + BTF::CommonType *Type = (BTF::CommonType *)&TypesBuffer[Pos]; + if (BytesLeft < sizeof(*Type)) + return Err("incomplete type definition in .BTF section:") + << " offset " << Offset << ", index " << Types.size(); + + uint64_t Size = byteSize(Type); + if (BytesLeft < Size) + return Err("incomplete type definition in .BTF section:") + << " offset=" << Offset << ", index=" << Types.size() + << ", vlen=" << Type->getVlen(); + + LLVM_DEBUG({ + llvm::dbgs() << "Adding BTF type:\n" + << " Id = " << Types.size() << "\n" + << " Kind = " << Type->getKind() << "\n" + << " Name = " << findString(Type->NameOff) << "\n" + << " Record Size = " << Size << "\n"; + }); + Types.push_back(Type); + Pos += Size; + } - StringsTable = Extractor.getData().substr(StrStart, StrLen); return Error::success(); } @@ -162,12 +273,24 @@ Error BTFParser::parseBTFExt(ParseContext &Ctx, SectionRef BTFExt) { (void)Extractor.getU32(C); // func_info_len uint32_t LineInfoOff = Extractor.getU32(C); uint32_t LineInfoLen = Extractor.getU32(C); + uint32_t RelocInfoOff = Extractor.getU32(C); + uint32_t RelocInfoLen = Extractor.getU32(C); if (!C) return Err(".BTF.ext", C); - uint32_t LineInfoStart = HdrLen + LineInfoOff; - uint32_t LineInfoEnd = LineInfoStart + LineInfoLen; - if (Error E = parseLineInfo(Ctx, Extractor, LineInfoStart, LineInfoEnd)) - return E; + + if (LineInfoLen > 0 && Ctx.Opts.LoadLines) { + uint32_t LineInfoStart = HdrLen + LineInfoOff; + uint32_t LineInfoEnd = LineInfoStart + LineInfoLen; + if (Error E = parseLineInfo(Ctx, Extractor, LineInfoStart, LineInfoEnd)) + return E; + } + + if (RelocInfoLen > 0 && Ctx.Opts.LoadRelocs) { + uint32_t RelocInfoStart = HdrLen + RelocInfoOff; + uint32_t RelocInfoEnd = RelocInfoStart + RelocInfoLen; + if (Error E = parseRelocInfo(Ctx, Extractor, RelocInfoStart, RelocInfoEnd)) + return E; + } return Error::success(); } @@ -214,11 +337,52 @@ Error BTFParser::parseLineInfo(ParseContext &Ctx, DataExtractor &Extractor, return Error::success(); } -Error BTFParser::parse(const ObjectFile &Obj) { +Error BTFParser::parseRelocInfo(ParseContext &Ctx, DataExtractor &Extractor, + uint64_t RelocInfoStart, + uint64_t RelocInfoEnd) { + DataExtractor::Cursor C = DataExtractor::Cursor(RelocInfoStart); + uint32_t RecSize = Extractor.getU32(C); + if (!C) + return Err(".BTF.ext", C); + if (RecSize < 16) + return Err("unexpected .BTF.ext field reloc info record length: ") + << RecSize; + while (C && C.tell() < RelocInfoEnd) { + uint32_t SecNameOff = Extractor.getU32(C); + uint32_t NumInfo = Extractor.getU32(C); + StringRef SecName = findString(SecNameOff); + std::optional<SectionRef> Sec = Ctx.findSection(SecName); + BTFRelocVector &Relocs = SectionRelocs[Sec->getIndex()]; + for (uint32_t I = 0; C && I < NumInfo; ++I) { + uint64_t RecStart = C.tell(); + uint32_t InsnOff = Extractor.getU32(C); + uint32_t TypeID = Extractor.getU32(C); + uint32_t OffsetNameOff = Extractor.getU32(C); + uint32_t RelocKind = Extractor.getU32(C); + if (!C) + return Err(".BTF.ext", C); + Relocs.push_back({InsnOff, TypeID, OffsetNameOff, RelocKind}); + C.seek(RecStart + RecSize); + } + llvm::stable_sort( + Relocs, [](const BTF::BPFFieldReloc &L, const BTF::BPFFieldReloc &R) { + return L.InsnOffset < R.InsnOffset; + }); + } + if (!C) + return Err(".BTF.ext", C); + + return Error::success(); +} + +Error BTFParser::parse(const ObjectFile &Obj, const ParseOptions &Opts) { StringsTable = StringRef(); SectionLines.clear(); + SectionRelocs.clear(); + Types.clear(); + TypesBuffer = OwningArrayRef<uint8_t>(); - ParseContext Ctx(Obj); + ParseContext Ctx(Obj, Opts); std::optional<SectionRef> BTF; std::optional<SectionRef> BTFExt; for (SectionRef Sec : Obj.sections()) { @@ -264,20 +428,430 @@ StringRef BTFParser::findString(uint32_t Offset) const { return StringsTable.slice(Offset, StringsTable.find(0, Offset)); } -const BTF::BPFLineInfo * -BTFParser::findLineInfo(SectionedAddress Address) const { - auto MaybeSecInfo = SectionLines.find(Address.SectionIndex); - if (MaybeSecInfo == SectionLines.end()) +template <typename T> +static const T *findInfo(const DenseMap<uint64_t, SmallVector<T, 0>> &SecMap, + SectionedAddress Address) { + auto MaybeSecInfo = SecMap.find(Address.SectionIndex); + if (MaybeSecInfo == SecMap.end()) return nullptr; - const BTFLinesVector &SecInfo = MaybeSecInfo->second; + const SmallVector<T, 0> &SecInfo = MaybeSecInfo->second; const uint64_t TargetOffset = Address.Address; - BTFLinesVector::const_iterator LineInfo = - llvm::partition_point(SecInfo, [=](const BTF::BPFLineInfo &Line) { - return Line.InsnOffset < TargetOffset; - }); - if (LineInfo == SecInfo.end() || LineInfo->InsnOffset != Address.Address) + typename SmallVector<T, 0>::const_iterator MaybeInfo = llvm::partition_point( + SecInfo, [=](const T &Entry) { return Entry.InsnOffset < TargetOffset; }); + if (MaybeInfo == SecInfo.end() || MaybeInfo->InsnOffset != Address.Address) return nullptr; - return LineInfo; + return &*MaybeInfo; +} + +const BTF::BPFLineInfo * +BTFParser::findLineInfo(SectionedAddress Address) const { + return findInfo(SectionLines, Address); +} + +const BTF::BPFFieldReloc * +BTFParser::findFieldReloc(SectionedAddress Address) const { + return findInfo(SectionRelocs, Address); +} + +const BTF::CommonType *BTFParser::findType(uint32_t Id) const { + if (Id < Types.size()) + return Types[Id]; + return nullptr; +} + +enum RelocKindGroup { + RKG_FIELD, + RKG_TYPE, + RKG_ENUMVAL, + RKG_UNKNOWN, +}; + +static RelocKindGroup relocKindGroup(const BTF::BPFFieldReloc *Reloc) { + switch (Reloc->RelocKind) { + case BTF::FIELD_BYTE_OFFSET: + case BTF::FIELD_BYTE_SIZE: + case BTF::FIELD_EXISTENCE: + case BTF::FIELD_SIGNEDNESS: + case BTF::FIELD_LSHIFT_U64: + case BTF::FIELD_RSHIFT_U64: + return RKG_FIELD; + case BTF::BTF_TYPE_ID_LOCAL: + case BTF::BTF_TYPE_ID_REMOTE: + case BTF::TYPE_EXISTENCE: + case BTF::TYPE_MATCH: + case BTF::TYPE_SIZE: + return RKG_TYPE; + case BTF::ENUM_VALUE_EXISTENCE: + case BTF::ENUM_VALUE: + return RKG_ENUMVAL; + default: + return RKG_UNKNOWN; + } +} + +static bool isMod(const BTF::CommonType *Type) { + switch (Type->getKind()) { + case BTF::BTF_KIND_VOLATILE: + case BTF::BTF_KIND_CONST: + case BTF::BTF_KIND_RESTRICT: + case BTF::BTF_KIND_TYPE_TAG: + return true; + default: + return false; + } +} + +static bool printMod(const BTFParser &BTF, const BTF::CommonType *Type, + raw_ostream &Stream) { + switch (Type->getKind()) { + case BTF::BTF_KIND_CONST: + Stream << " const"; + break; + case BTF::BTF_KIND_VOLATILE: + Stream << " volatile"; + break; + case BTF::BTF_KIND_RESTRICT: + Stream << " restrict"; + break; + case BTF::BTF_KIND_TYPE_TAG: + Stream << " type_tag(\"" << BTF.findString(Type->NameOff) << "\")"; + break; + default: + return false; + } + return true; +} + +static const BTF::CommonType *skipModsAndTypedefs(const BTFParser &BTF, + const BTF::CommonType *Type) { + while (isMod(Type) || Type->getKind() == BTF::BTF_KIND_TYPEDEF) { + auto *Base = BTF.findType(Type->Type); + if (!Base) + break; + Type = Base; + } + return Type; +} + +namespace { +struct StrOrAnon { + const BTFParser &BTF; + uint32_t Offset; + uint32_t Idx; +}; + +static raw_ostream &operator<<(raw_ostream &Stream, const StrOrAnon &S) { + StringRef Str = S.BTF.findString(S.Offset); + if (Str.empty()) + Stream << "<anon " << S.Idx << ">"; + else + Stream << Str; + return Stream; +} +} // anonymous namespace + +static void relocKindName(uint32_t X, raw_ostream &Out) { + Out << "<"; + switch (X) { + default: + Out << "reloc kind #" << X; + break; + case BTF::FIELD_BYTE_OFFSET: + Out << "byte_off"; + break; + case BTF::FIELD_BYTE_SIZE: + Out << "byte_sz"; + break; + case BTF::FIELD_EXISTENCE: + Out << "field_exists"; + break; + case BTF::FIELD_SIGNEDNESS: + Out << "signed"; + break; + case BTF::FIELD_LSHIFT_U64: + Out << "lshift_u64"; + break; + case BTF::FIELD_RSHIFT_U64: + Out << "rshift_u64"; + break; + case BTF::BTF_TYPE_ID_LOCAL: + Out << "local_type_id"; + break; + case BTF::BTF_TYPE_ID_REMOTE: + Out << "target_type_id"; + break; + case BTF::TYPE_EXISTENCE: + Out << "type_exists"; + break; + case BTF::TYPE_MATCH: + Out << "type_matches"; + break; + case BTF::TYPE_SIZE: + Out << "type_size"; + break; + case BTF::ENUM_VALUE_EXISTENCE: + Out << "enumval_exists"; + break; + case BTF::ENUM_VALUE: + Out << "enumval_value"; + break; + } + Out << ">"; +} + +// Produces a human readable description of a CO-RE relocation. +// Such relocations are generated by BPF backend, and processed +// by libbpf's BPF program loader [1]. +// +// Each relocation record has the following information: +// - Relocation kind; +// - BTF type ID; +// - Access string offset in string table. +// +// There are different kinds of relocations, these kinds could be split +// in three groups: +// - load-time information about types (size, existence), +// `BTFParser::symbolize()` output for such relocations uses the template: +// +// <relocation-kind> [<id>] <type-name> +// +// For example: +// - "<type_exists> [7] struct foo" +// - "<type_size> [7] struct foo" +// +// - load-time information about enums (literal existence, literal value), +// `BTFParser::symbolize()` output for such relocations uses the template: +// +// <relocation-kind> [<id>] <type-name>::<literal-name> = <original-value> +// +// For example: +// - "<enumval_exists> [5] enum foo::U = 1" +// - "<enumval_value> [5] enum foo::V = 2" +// +// - load-time information about fields (e.g. field offset), +// `BTFParser::symbolize()` output for such relocations uses the template: +// +// <relocation-kind> [<id>] \ +// <type-name>::[N].<field-1-name>...<field-M-name> \ +// (<access string>) +// +// For example: +// - "<byte_off> [8] struct bar::[7].v (7:1)" +// - "<field_exists> [8] struct bar::v (0:1)" +// +// If relocation description is not valid output follows the following pattern: +// +// <relocation-kind> <type-id>::<unprocessedaccess-string> <<error-msg>> +// +// For example: +// +// - "<type_sz> [42] '' <unknown type id: 42>" +// - "<byte_off> [4] '0:' <field spec too short>" +// +// Additional examples could be found in unit tests, see +// llvm/unittests/DebugInfo/BTF/BTFParserTest.cpp. +// +// [1] https://www.kernel.org/doc/html/latest/bpf/libbpf/index.html +void BTFParser::symbolize(const BTF::BPFFieldReloc *Reloc, + SmallVectorImpl<char> &Result) const { + raw_svector_ostream Stream(Result); + StringRef FullSpecStr = findString(Reloc->OffsetNameOff); + SmallVector<uint32_t, 8> RawSpec; + + auto Fail = [&](auto Msg) { + Result.resize(0); + relocKindName(Reloc->RelocKind, Stream); + Stream << " [" << Reloc->TypeID << "] '" << FullSpecStr << "'" + << " <" << Msg << ">"; + }; + + // Relocation access string follows pattern [0-9]+(:[0-9]+)*, + // e.g.: 12:22:3. Code below splits `SpecStr` by ':', parses + // numbers, and pushes them to `RawSpec`. + StringRef SpecStr = FullSpecStr; + while (SpecStr.size()) { + unsigned long long Val; + if (consumeUnsignedInteger(SpecStr, 10, Val)) + return Fail("spec string is not a number"); + RawSpec.push_back(Val); + if (SpecStr.empty()) + break; + if (SpecStr[0] != ':') + return Fail(format("unexpected spec string delimiter: '%c'", SpecStr[0])); + SpecStr = SpecStr.substr(1); + } + + // Print relocation kind to `Stream`. + relocKindName(Reloc->RelocKind, Stream); + + uint32_t CurId = Reloc->TypeID; + const BTF::CommonType *Type = findType(CurId); + if (!Type) + return Fail(format("unknown type id: %d", CurId)); + + Stream << " [" << CurId << "]"; + + // `Type` might have modifiers, e.g. for type 'const int' the `Type` + // would refer to BTF type of kind BTF_KIND_CONST. + // Print all these modifiers to `Stream`. + for (uint32_t ChainLen = 0; printMod(*this, Type, Stream); ++ChainLen) { + if (ChainLen >= 32) + return Fail("modifiers chain is too long"); + + CurId = Type->Type; + const BTF::CommonType *NextType = findType(CurId); + if (!NextType) + return Fail(format("unknown type id: %d in modifiers chain", CurId)); + Type = NextType; + } + // Print the type name to `Stream`. + if (CurId == 0) { + Stream << " void"; + } else { + switch (Type->getKind()) { + case BTF::BTF_KIND_TYPEDEF: + Stream << " typedef"; + break; + case BTF::BTF_KIND_STRUCT: + Stream << " struct"; + break; + case BTF::BTF_KIND_UNION: + Stream << " union"; + break; + case BTF::BTF_KIND_ENUM: + Stream << " enum"; + break; + case BTF::BTF_KIND_ENUM64: + Stream << " enum"; + break; + case BTF::BTF_KIND_FWD: + if (Type->Info & BTF::FWD_UNION_FLAG) + Stream << " fwd union"; + else + Stream << " fwd struct"; + break; + default: + break; + } + Stream << " " << StrOrAnon({*this, Type->NameOff, CurId}); + } + + RelocKindGroup Group = relocKindGroup(Reloc); + // Type-based relocations don't use access string but clang backend + // generates '0' and libbpf checks it's value, do the same here. + if (Group == RKG_TYPE) { + if (RawSpec.size() != 1 || RawSpec[0] != 0) + return Fail("unexpected type-based relocation spec: should be '0'"); + return; + } + + Stream << "::"; + + // For enum-based relocations access string is a single number, + // corresponding to the enum literal sequential number. + // E.g. for `enum E { U, V }`, relocation requesting value of `V` + // would look as follows: + // - kind: BTF::ENUM_VALUE + // - BTF id: id for `E` + // - access string: "1" + if (Group == RKG_ENUMVAL) { + Type = skipModsAndTypedefs(*this, Type); + + if (RawSpec.size() != 1) + return Fail("unexpected enumval relocation spec size"); + + uint32_t NameOff; + uint64_t Val; + uint32_t Idx = RawSpec[0]; + if (auto *T = dyn_cast<BTF::EnumType>(Type)) { + if (T->values().size() <= Idx) + return Fail(format("bad value index: %d", Idx)); + const BTF::BTFEnum &E = T->values()[Idx]; + NameOff = E.NameOff; + Val = E.Val; + } else if (auto *T = dyn_cast<BTF::Enum64Type>(Type)) { + if (T->values().size() <= Idx) + return Fail(format("bad value index: %d", Idx)); + const BTF::BTFEnum64 &E = T->values()[Idx]; + NameOff = E.NameOff; + Val = (uint64_t)E.Val_Hi32 << 32u | E.Val_Lo32; + } else { + return Fail(format("unexpected type kind for enum relocation: %d", + Type->getKind())); + } + + Stream << StrOrAnon({*this, NameOff, Idx}); + if (Type->Info & BTF::ENUM_SIGNED_FLAG) + Stream << " = " << (int64_t)Val; + else + Stream << " = " << (uint64_t)Val; + return; + } + + // For type-based relocations access string is an array of numbers, + // which resemble index parameters for `getelementptr` LLVM IR instruction. + // E.g. for the following types: + // + // struct foo { + // int a; + // int b; + // }; + // struct bar { + // int u; + // struct foo v[7]; + // }; + // + // Relocation requesting `offsetof(struct bar, v[2].b)` will have + // the following access string: 0:1:2:1 + // ^ ^ ^ ^ + // | | | | + // initial index | | field 'b' is a field #1 + // | | (counting from 0) + // | array index #2 + // field 'v' is a field #1 + // (counting from 0) + if (Group == RKG_FIELD) { + if (RawSpec.size() < 1) + return Fail("field spec too short"); + + if (RawSpec[0] != 0) + Stream << "[" << RawSpec[0] << "]"; + for (uint32_t I = 1; I < RawSpec.size(); ++I) { + Type = skipModsAndTypedefs(*this, Type); + uint32_t Idx = RawSpec[I]; + + if (auto *T = dyn_cast<BTF::StructType>(Type)) { + if (T->getVlen() <= Idx) + return Fail( + format("member index %d for spec sub-string %d is out of range", + Idx, I)); + + const BTF::BTFMember &Member = T->members()[Idx]; + if (I != 1 || RawSpec[0] != 0) + Stream << "."; + Stream << StrOrAnon({*this, Member.NameOff, Idx}); + Type = findType(Member.Type); + if (!Type) + return Fail(format("unknown member type id %d for spec sub-string %d", + Member.Type, I)); + } else if (auto *T = dyn_cast<BTF::ArrayType>(Type)) { + Stream << "[" << Idx << "]"; + Type = findType(T->getArray().ElemType); + if (!Type) + return Fail( + format("unknown element type id %d for spec sub-string %d", + T->getArray().ElemType, I)); + } else { + return Fail(format("unexpected type kind %d for spec sub-string %d", + Type->getKind(), I)); + } + } + + Stream << " (" << FullSpecStr << ")"; + return; + } + + return Fail(format("unknown relocation kind: %d", Reloc->RelocKind)); } diff --git a/llvm/lib/DebugInfo/CodeView/CVTypeVisitor.cpp b/llvm/lib/DebugInfo/CodeView/CVTypeVisitor.cpp index 689c643a7006..3cafa3a93a0d 100644 --- a/llvm/lib/DebugInfo/CodeView/CVTypeVisitor.cpp +++ b/llvm/lib/DebugInfo/CodeView/CVTypeVisitor.cpp @@ -185,7 +185,7 @@ Error CVTypeVisitor::visitFieldListMemberStream(BinaryStreamReader &Reader) { struct FieldListVisitHelper { FieldListVisitHelper(TypeVisitorCallbacks &Callbacks, ArrayRef<uint8_t> Data, VisitorDataSource Source) - : Stream(Data, llvm::support::little), Reader(Stream), + : Stream(Data, llvm::endianness::little), Reader(Stream), Deserializer(Reader), Visitor((Source == VDS_BytesPresent) ? Pipeline : Callbacks) { if (Source == VDS_BytesPresent) { diff --git a/llvm/lib/DebugInfo/CodeView/EnumTables.cpp b/llvm/lib/DebugInfo/CodeView/EnumTables.cpp index b2f0099bd01c..7e3087373bfa 100644 --- a/llvm/lib/DebugInfo/CodeView/EnumTables.cpp +++ b/llvm/lib/DebugInfo/CodeView/EnumTables.cpp @@ -434,6 +434,20 @@ static const EnumEntry<uint16_t> LabelTypeEnum[] = { CV_ENUM_CLASS_ENT(LabelType, Far), }; +static const EnumEntry<uint16_t> JumpTableEntrySizeNames[] = { + CV_ENUM_CLASS_ENT(JumpTableEntrySize, Int8), + CV_ENUM_CLASS_ENT(JumpTableEntrySize, UInt8), + CV_ENUM_CLASS_ENT(JumpTableEntrySize, Int16), + CV_ENUM_CLASS_ENT(JumpTableEntrySize, UInt16), + CV_ENUM_CLASS_ENT(JumpTableEntrySize, Int32), + CV_ENUM_CLASS_ENT(JumpTableEntrySize, UInt32), + CV_ENUM_CLASS_ENT(JumpTableEntrySize, Pointer), + CV_ENUM_CLASS_ENT(JumpTableEntrySize, UInt8ShiftLeft), + CV_ENUM_CLASS_ENT(JumpTableEntrySize, UInt16ShiftLeft), + CV_ENUM_CLASS_ENT(JumpTableEntrySize, Int8ShiftLeft), + CV_ENUM_CLASS_ENT(JumpTableEntrySize, Int16ShiftLeft), +}; + namespace llvm { namespace codeview { @@ -559,5 +573,9 @@ ArrayRef<EnumEntry<uint16_t>> getLabelTypeEnum() { return ArrayRef(LabelTypeEnum); } +ArrayRef<EnumEntry<uint16_t>> getJumpTableEntrySizeNames() { + return ArrayRef(JumpTableEntrySizeNames); +} + } // end namespace codeview } // end namespace llvm diff --git a/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp b/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp index 460f95d96a29..e59a0197d650 100644 --- a/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp +++ b/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp @@ -15,7 +15,6 @@ #include "llvm/DebugInfo/CodeView/RecordName.h" #include "llvm/DebugInfo/CodeView/RecordSerialization.h" #include "llvm/Support/BinaryStreamReader.h" -#include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" #include <algorithm> #include <cassert> @@ -69,13 +68,13 @@ void LazyRandomTypeCollection::reset(BinaryStreamReader &Reader, } void LazyRandomTypeCollection::reset(StringRef Data, uint32_t RecordCountHint) { - BinaryStreamReader Reader(Data, support::little); + BinaryStreamReader Reader(Data, llvm::endianness::little); reset(Reader, RecordCountHint); } void LazyRandomTypeCollection::reset(ArrayRef<uint8_t> Data, uint32_t RecordCountHint) { - BinaryStreamReader Reader(Data, support::little); + BinaryStreamReader Reader(Data, llvm::endianness::little); reset(Reader, RecordCountHint); } diff --git a/llvm/lib/DebugInfo/CodeView/RecordName.cpp b/llvm/lib/DebugInfo/CodeView/RecordName.cpp index 5fbbc4a5d497..e06b036ede63 100644 --- a/llvm/lib/DebugInfo/CodeView/RecordName.cpp +++ b/llvm/lib/DebugInfo/CodeView/RecordName.cpp @@ -324,7 +324,7 @@ StringRef llvm::codeview::getSymbolName(CVSymbol Sym) { if (Sym.kind() == SymbolKind::S_CONSTANT) { // S_CONSTANT is preceded by an APSInt, which has a variable length. So we // have to do a full deserialization. - BinaryStreamReader Reader(Sym.content(), llvm::support::little); + BinaryStreamReader Reader(Sym.content(), llvm::endianness::little); // The container doesn't matter for single records. SymbolRecordMapping Mapping(Reader, CodeViewContainer::ObjectFile); ConstantSym Const(SymbolKind::S_CONSTANT); diff --git a/llvm/lib/DebugInfo/CodeView/RecordSerialization.cpp b/llvm/lib/DebugInfo/CodeView/RecordSerialization.cpp index d76905df8681..032704478ffe 100644 --- a/llvm/lib/DebugInfo/CodeView/RecordSerialization.cpp +++ b/llvm/lib/DebugInfo/CodeView/RecordSerialization.cpp @@ -103,7 +103,7 @@ Error llvm::codeview::consume(BinaryStreamReader &Reader, APSInt &Num) { Error llvm::codeview::consume(StringRef &Data, APSInt &Num) { ArrayRef<uint8_t> Bytes(Data.bytes_begin(), Data.bytes_end()); - BinaryByteStream S(Bytes, llvm::support::little); + BinaryByteStream S(Bytes, llvm::endianness::little); BinaryStreamReader SR(S); auto EC = consume(SR, Num); Data = Data.take_back(SR.bytesRemaining()); @@ -129,7 +129,7 @@ Error llvm::codeview::consume(BinaryStreamReader &Reader, uint32_t &Item) { Error llvm::codeview::consume(StringRef &Data, uint32_t &Item) { ArrayRef<uint8_t> Bytes(Data.bytes_begin(), Data.bytes_end()); - BinaryByteStream S(Bytes, llvm::support::little); + BinaryByteStream S(Bytes, llvm::endianness::little); BinaryStreamReader SR(S); auto EC = consume(SR, Item); Data = Data.take_back(SR.bytesRemaining()); diff --git a/llvm/lib/DebugInfo/CodeView/SimpleTypeSerializer.cpp b/llvm/lib/DebugInfo/CodeView/SimpleTypeSerializer.cpp index cf0c877fdbf8..25725853fb39 100644 --- a/llvm/lib/DebugInfo/CodeView/SimpleTypeSerializer.cpp +++ b/llvm/lib/DebugInfo/CodeView/SimpleTypeSerializer.cpp @@ -34,7 +34,7 @@ SimpleTypeSerializer::~SimpleTypeSerializer() = default; template <typename T> ArrayRef<uint8_t> SimpleTypeSerializer::serialize(T &Record) { - BinaryStreamWriter Writer(ScratchBuffer, support::little); + BinaryStreamWriter Writer(ScratchBuffer, llvm::endianness::little); TypeRecordMapping Mapping(Writer); // Write the record prefix first with a dummy length but real kind. diff --git a/llvm/lib/DebugInfo/CodeView/SymbolDumper.cpp b/llvm/lib/DebugInfo/CodeView/SymbolDumper.cpp index cfb12dbae845..f56739db7c75 100644 --- a/llvm/lib/DebugInfo/CodeView/SymbolDumper.cpp +++ b/llvm/lib/DebugInfo/CodeView/SymbolDumper.cpp @@ -589,7 +589,22 @@ Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, } Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, CallerSym &Caller) { - ListScope S(W, CVR.kind() == S_CALLEES ? "Callees" : "Callers"); + llvm::StringRef ScopeName; + switch (CVR.kind()) { + case S_CALLEES: + ScopeName = "Callees"; + break; + case S_CALLERS: + ScopeName = "Callers"; + break; + case S_INLINEES: + ScopeName = "Inlinees"; + break; + default: + return llvm::make_error<CodeViewError>( + "Unknown CV Record type for a CallerSym object!"); + } + ListScope S(W, ScopeName); for (auto FuncID : Caller.Indices) printTypeIndex("FuncID", FuncID); return Error::success(); @@ -643,6 +658,20 @@ Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, return Error::success(); } +Error CVSymbolDumperImpl::visitKnownRecord(CVSymbol &CVR, + JumpTableSym &JumpTable) { + W.printHex("BaseOffset", JumpTable.BaseOffset); + W.printNumber("BaseSegment", JumpTable.BaseSegment); + W.printEnum("SwitchType", static_cast<uint16_t>(JumpTable.SwitchType), + getJumpTableEntrySizeNames()); + W.printHex("BranchOffset", JumpTable.BranchOffset); + W.printHex("TableOffset", JumpTable.TableOffset); + W.printNumber("BranchSegment", JumpTable.BranchSegment); + W.printNumber("TableSegment", JumpTable.TableSegment); + W.printNumber("EntriesCount", JumpTable.EntriesCount); + return Error::success(); +} + Error CVSymbolDumperImpl::visitUnknownSymbol(CVSymbol &CVR) { W.printNumber("Length", CVR.length()); return Error::success(); diff --git a/llvm/lib/DebugInfo/CodeView/SymbolRecordMapping.cpp b/llvm/lib/DebugInfo/CodeView/SymbolRecordMapping.cpp index 3b627930e271..b5e366b965a9 100644 --- a/llvm/lib/DebugInfo/CodeView/SymbolRecordMapping.cpp +++ b/llvm/lib/DebugInfo/CodeView/SymbolRecordMapping.cpp @@ -483,6 +483,19 @@ Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, return Error::success(); } +Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, + JumpTableSym &JumpTable) { + error(IO.mapInteger(JumpTable.BaseOffset)); + error(IO.mapInteger(JumpTable.BaseSegment)); + error(IO.mapEnum(JumpTable.SwitchType)); + error(IO.mapInteger(JumpTable.BranchOffset)); + error(IO.mapInteger(JumpTable.TableOffset)); + error(IO.mapInteger(JumpTable.BranchSegment)); + error(IO.mapInteger(JumpTable.TableSegment)); + error(IO.mapInteger(JumpTable.EntriesCount)); + return Error::success(); +} + RegisterId codeview::decodeFramePtrReg(EncodedFramePtrReg EncodedReg, CPUType CPU) { assert(unsigned(EncodedReg) < 4); diff --git a/llvm/lib/DebugInfo/CodeView/SymbolSerializer.cpp b/llvm/lib/DebugInfo/CodeView/SymbolSerializer.cpp index 5fb8d497b957..eadc50f2da80 100644 --- a/llvm/lib/DebugInfo/CodeView/SymbolSerializer.cpp +++ b/llvm/lib/DebugInfo/CodeView/SymbolSerializer.cpp @@ -8,7 +8,6 @@ #include "llvm/DebugInfo/CodeView/SymbolSerializer.h" #include "llvm/ADT/ArrayRef.h" -#include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include <cassert> @@ -20,8 +19,8 @@ using namespace llvm::codeview; SymbolSerializer::SymbolSerializer(BumpPtrAllocator &Allocator, CodeViewContainer Container) - : Storage(Allocator), Stream(RecordBuffer, support::little), Writer(Stream), - Mapping(Writer, Container) {} + : Storage(Allocator), Stream(RecordBuffer, llvm::endianness::little), + Writer(Stream), Mapping(Writer, Container) {} Error SymbolSerializer::visitSymbolBegin(CVSymbol &Record) { assert(!CurrentSymbol && "Already in a symbol mapping!"); diff --git a/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp b/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp index 682747a2b81f..59e2a85c4d4c 100644 --- a/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp +++ b/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp @@ -442,6 +442,7 @@ static bool discoverTypeIndices(ArrayRef<uint8_t> Content, SymbolKind Kind, case SymbolKind::S_THUNK32: case SymbolKind::S_FRAMECOOKIE: case SymbolKind::S_UNAMESPACE: + case SymbolKind::S_ARMSWITCHTABLE: break; // Scope ending symbols. case SymbolKind::S_END: @@ -469,7 +470,7 @@ static void resolveTypeIndexReferences(ArrayRef<uint8_t> RecordData, RecordData = RecordData.drop_front(sizeof(RecordPrefix)); - BinaryStreamReader Reader(RecordData, support::little); + BinaryStreamReader Reader(RecordData, llvm::endianness::little); for (const auto &Ref : Refs) { Reader.setOffset(Ref.Offset); FixedStreamArray<TypeIndex> Run; diff --git a/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp b/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp index 14962cd36c23..0f9c8ef485d4 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp @@ -621,7 +621,10 @@ std::optional<uint64_t> DWARFDebugNames::Entry::getCUIndex() const { if (std::optional<DWARFFormValue> Off = lookup(dwarf::DW_IDX_compile_unit)) return Off->getAsUnsignedConstant(); // In a per-CU index, the entries without a DW_IDX_compile_unit attribute - // implicitly refer to the single CU. + // implicitly refer to the single CU, but only if we don't have a + // DW_IDX_type_unit. + if (lookup(dwarf::DW_IDX_type_unit).has_value()) + return std::nullopt; if (NameIdx->getCUCount() == 1) return 0; return std::nullopt; @@ -634,8 +637,21 @@ std::optional<uint64_t> DWARFDebugNames::Entry::getCUOffset() const { return NameIdx->getCUOffset(*Index); } +std::optional<uint64_t> DWARFDebugNames::Entry::getLocalTUOffset() const { + std::optional<uint64_t> Index = getLocalTUIndex(); + if (!Index || *Index >= NameIdx->getLocalTUCount()) + return std::nullopt; + return NameIdx->getLocalTUOffset(*Index); +} + +std::optional<uint64_t> DWARFDebugNames::Entry::getLocalTUIndex() const { + if (std::optional<DWARFFormValue> Off = lookup(dwarf::DW_IDX_type_unit)) + return Off->getAsUnsignedConstant(); + return std::nullopt; +} + void DWARFDebugNames::Entry::dump(ScopedPrinter &W) const { - W.printHex("Abbrev", Abbr->Code); + W.startLine() << formatv("Abbrev: {0:x}\n", Abbr->Code); W.startLine() << formatv("Tag: {0}\n", Abbr->Tag); assert(Abbr->Attributes.size() == Values.size()); for (auto Tuple : zip_first(Abbr->Attributes, Values)) { @@ -969,3 +985,71 @@ DWARFDebugNames::getCUNameIndex(uint64_t CUOffset) { } return CUToNameIndex.lookup(CUOffset); } + +static bool isObjCSelector(StringRef Name) { + return Name.size() > 2 && (Name[0] == '-' || Name[0] == '+') && + (Name[1] == '['); +} + +std::optional<ObjCSelectorNames> llvm::getObjCNamesIfSelector(StringRef Name) { + if (!isObjCSelector(Name)) + return std::nullopt; + // "-[Atom setMass:]" + StringRef ClassNameStart(Name.drop_front(2)); + size_t FirstSpace = ClassNameStart.find(' '); + if (FirstSpace == StringRef::npos) + return std::nullopt; + + StringRef SelectorStart = ClassNameStart.drop_front(FirstSpace + 1); + if (!SelectorStart.size()) + return std::nullopt; + + ObjCSelectorNames Ans; + Ans.ClassName = ClassNameStart.take_front(FirstSpace); + Ans.Selector = SelectorStart.drop_back(); // drop ']'; + + // "-[Class(Category) selector :withArg ...]" + if (Ans.ClassName.back() == ')') { + size_t OpenParens = Ans.ClassName.find('('); + if (OpenParens != StringRef::npos) { + Ans.ClassNameNoCategory = Ans.ClassName.take_front(OpenParens); + + Ans.MethodNameNoCategory = Name.take_front(OpenParens + 2); + // FIXME: The missing space here may be a bug, but dsymutil-classic also + // does it this way. + append_range(*Ans.MethodNameNoCategory, SelectorStart); + } + } + return Ans; +} + +std::optional<StringRef> llvm::StripTemplateParameters(StringRef Name) { + // We are looking for template parameters to strip from Name. e.g. + // + // operator<<B> + // + // We look for > at the end but if it does not contain any < then we + // have something like operator>>. We check for the operator<=> case. + if (!Name.ends_with(">") || Name.count("<") == 0 || Name.ends_with("<=>")) + return {}; + + // How many < until we have the start of the template parameters. + size_t NumLeftAnglesToSkip = 1; + + // If we have operator<=> then we need to skip its < as well. + NumLeftAnglesToSkip += Name.count("<=>"); + + size_t RightAngleCount = Name.count('>'); + size_t LeftAngleCount = Name.count('<'); + + // If we have more < than > we have operator< or operator<< + // we to account for their < as well. + if (LeftAngleCount > RightAngleCount) + NumLeftAnglesToSkip += LeftAngleCount - RightAngleCount; + + size_t StartOfTemplate = 0; + while (NumLeftAnglesToSkip--) + StartOfTemplate = Name.find('<', StartOfTemplate) + 1; + + return Name.substr(0, StartOfTemplate - 1); +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp index 33168abbdc38..c671aedbc9e5 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp @@ -70,13 +70,692 @@ using DWARFLineTable = DWARFDebugLine::LineTable; using FileLineInfoKind = DILineInfoSpecifier::FileLineInfoKind; using FunctionNameKind = DILineInfoSpecifier::FunctionNameKind; + +void fixupIndexV4(DWARFContext &C, DWARFUnitIndex &Index) { + using EntryType = DWARFUnitIndex::Entry::SectionContribution; + using EntryMap = DenseMap<uint32_t, EntryType>; + EntryMap Map; + const auto &DObj = C.getDWARFObj(); + if (DObj.getCUIndexSection().empty()) + return; + + uint64_t Offset = 0; + uint32_t TruncOffset = 0; + DObj.forEachInfoDWOSections([&](const DWARFSection &S) { + if (!(C.getParseCUTUIndexManually() || + S.Data.size() >= std::numeric_limits<uint32_t>::max())) + return; + + DWARFDataExtractor Data(DObj, S, C.isLittleEndian(), 0); + while (Data.isValidOffset(Offset)) { + DWARFUnitHeader Header; + if (Error ExtractionErr = Header.extract( + C, Data, &Offset, DWARFSectionKind::DW_SECT_INFO)) { + C.getWarningHandler()( + createError("Failed to parse CU header in DWP file: " + + toString(std::move(ExtractionErr)))); + Map.clear(); + break; + } + + auto Iter = Map.insert({TruncOffset, + {Header.getOffset(), Header.getNextUnitOffset() - + Header.getOffset()}}); + if (!Iter.second) { + logAllUnhandledErrors( + createError("Collision occured between for truncated offset 0x" + + Twine::utohexstr(TruncOffset)), + errs()); + Map.clear(); + return; + } + + Offset = Header.getNextUnitOffset(); + TruncOffset = Offset; + } + }); + + if (Map.empty()) + return; + + for (DWARFUnitIndex::Entry &E : Index.getMutableRows()) { + if (!E.isValid()) + continue; + DWARFUnitIndex::Entry::SectionContribution &CUOff = E.getContribution(); + auto Iter = Map.find(CUOff.getOffset()); + if (Iter == Map.end()) { + logAllUnhandledErrors(createError("Could not find CU offset 0x" + + Twine::utohexstr(CUOff.getOffset()) + + " in the Map"), + errs()); + break; + } + CUOff.setOffset(Iter->second.getOffset()); + if (CUOff.getOffset() != Iter->second.getOffset()) + logAllUnhandledErrors(createError("Length of CU in CU index doesn't " + "match calculated length at offset 0x" + + Twine::utohexstr(CUOff.getOffset())), + errs()); + } +} + +void fixupIndexV5(DWARFContext &C, DWARFUnitIndex &Index) { + DenseMap<uint64_t, uint64_t> Map; + + const auto &DObj = C.getDWARFObj(); + DObj.forEachInfoDWOSections([&](const DWARFSection &S) { + if (!(C.getParseCUTUIndexManually() || + S.Data.size() >= std::numeric_limits<uint32_t>::max())) + return; + DWARFDataExtractor Data(DObj, S, C.isLittleEndian(), 0); + uint64_t Offset = 0; + while (Data.isValidOffset(Offset)) { + DWARFUnitHeader Header; + if (Error ExtractionErr = Header.extract( + C, Data, &Offset, DWARFSectionKind::DW_SECT_INFO)) { + C.getWarningHandler()( + createError("Failed to parse CU header in DWP file: " + + toString(std::move(ExtractionErr)))); + break; + } + bool CU = Header.getUnitType() == DW_UT_split_compile; + uint64_t Sig = CU ? *Header.getDWOId() : Header.getTypeHash(); + Map[Sig] = Header.getOffset(); + Offset = Header.getNextUnitOffset(); + } + }); + if (Map.empty()) + return; + for (DWARFUnitIndex::Entry &E : Index.getMutableRows()) { + if (!E.isValid()) + continue; + DWARFUnitIndex::Entry::SectionContribution &CUOff = E.getContribution(); + auto Iter = Map.find(E.getSignature()); + if (Iter == Map.end()) { + logAllUnhandledErrors( + createError("Could not find unit with signature 0x" + + Twine::utohexstr(E.getSignature()) + " in the Map"), + errs()); + break; + } + CUOff.setOffset(Iter->second); + } +} + +void fixupIndex(DWARFContext &C, DWARFUnitIndex &Index) { + if (Index.getVersion() < 5) + fixupIndexV4(C, Index); + else + fixupIndexV5(C, Index); +} + +template <typename T> +static T &getAccelTable(std::unique_ptr<T> &Cache, const DWARFObject &Obj, + const DWARFSection &Section, StringRef StringSection, + bool IsLittleEndian) { + if (Cache) + return *Cache; + DWARFDataExtractor AccelSection(Obj, Section, IsLittleEndian, 0); + DataExtractor StrData(StringSection, IsLittleEndian, 0); + Cache = std::make_unique<T>(AccelSection, StrData); + if (Error E = Cache->extract()) + llvm::consumeError(std::move(E)); + return *Cache; +} + + +std::unique_ptr<DWARFDebugMacro> +DWARFContext::DWARFContextState::parseMacroOrMacinfo(MacroSecType SectionType) { + auto Macro = std::make_unique<DWARFDebugMacro>(); + auto ParseAndDump = [&](DWARFDataExtractor &Data, bool IsMacro) { + if (Error Err = IsMacro ? Macro->parseMacro(SectionType == MacroSection + ? D.compile_units() + : D.dwo_compile_units(), + SectionType == MacroSection + ? D.getStringExtractor() + : D.getStringDWOExtractor(), + Data) + : Macro->parseMacinfo(Data)) { + D.getRecoverableErrorHandler()(std::move(Err)); + Macro = nullptr; + } + }; + const DWARFObject &DObj = D.getDWARFObj(); + switch (SectionType) { + case MacinfoSection: { + DWARFDataExtractor Data(DObj.getMacinfoSection(), D.isLittleEndian(), 0); + ParseAndDump(Data, /*IsMacro=*/false); + break; + } + case MacinfoDwoSection: { + DWARFDataExtractor Data(DObj.getMacinfoDWOSection(), D.isLittleEndian(), 0); + ParseAndDump(Data, /*IsMacro=*/false); + break; + } + case MacroSection: { + DWARFDataExtractor Data(DObj, DObj.getMacroSection(), D.isLittleEndian(), + 0); + ParseAndDump(Data, /*IsMacro=*/true); + break; + } + case MacroDwoSection: { + DWARFDataExtractor Data(DObj.getMacroDWOSection(), D.isLittleEndian(), 0); + ParseAndDump(Data, /*IsMacro=*/true); + break; + } + } + return Macro; +} + +class ThreadUnsafeDWARFContextState : public DWARFContext::DWARFContextState { + + DWARFUnitVector NormalUnits; + std::optional<DenseMap<uint64_t, DWARFTypeUnit *>> NormalTypeUnits; + std::unique_ptr<DWARFUnitIndex> CUIndex; + std::unique_ptr<DWARFGdbIndex> GdbIndex; + std::unique_ptr<DWARFUnitIndex> TUIndex; + std::unique_ptr<DWARFDebugAbbrev> Abbrev; + std::unique_ptr<DWARFDebugLoc> Loc; + std::unique_ptr<DWARFDebugAranges> Aranges; + std::unique_ptr<DWARFDebugLine> Line; + std::unique_ptr<DWARFDebugFrame> DebugFrame; + std::unique_ptr<DWARFDebugFrame> EHFrame; + std::unique_ptr<DWARFDebugMacro> Macro; + std::unique_ptr<DWARFDebugMacro> Macinfo; + std::unique_ptr<DWARFDebugNames> Names; + std::unique_ptr<AppleAcceleratorTable> AppleNames; + std::unique_ptr<AppleAcceleratorTable> AppleTypes; + std::unique_ptr<AppleAcceleratorTable> AppleNamespaces; + std::unique_ptr<AppleAcceleratorTable> AppleObjC; + DWARFUnitVector DWOUnits; + std::optional<DenseMap<uint64_t, DWARFTypeUnit *>> DWOTypeUnits; + std::unique_ptr<DWARFDebugAbbrev> AbbrevDWO; + std::unique_ptr<DWARFDebugMacro> MacinfoDWO; + std::unique_ptr<DWARFDebugMacro> MacroDWO; + struct DWOFile { + object::OwningBinary<object::ObjectFile> File; + std::unique_ptr<DWARFContext> Context; + }; + StringMap<std::weak_ptr<DWOFile>> DWOFiles; + std::weak_ptr<DWOFile> DWP; + bool CheckedForDWP = false; + std::string DWPName; + +public: + ThreadUnsafeDWARFContextState(DWARFContext &DC, std::string &DWP) : + DWARFContext::DWARFContextState(DC), + DWPName(std::move(DWP)) {} + + DWARFUnitVector &getNormalUnits() override { + if (NormalUnits.empty()) { + const DWARFObject &DObj = D.getDWARFObj(); + DObj.forEachInfoSections([&](const DWARFSection &S) { + NormalUnits.addUnitsForSection(D, S, DW_SECT_INFO); + }); + NormalUnits.finishedInfoUnits(); + DObj.forEachTypesSections([&](const DWARFSection &S) { + NormalUnits.addUnitsForSection(D, S, DW_SECT_EXT_TYPES); + }); + } + return NormalUnits; + } + + DWARFUnitVector &getDWOUnits(bool Lazy) override { + if (DWOUnits.empty()) { + const DWARFObject &DObj = D.getDWARFObj(); + + DObj.forEachInfoDWOSections([&](const DWARFSection &S) { + DWOUnits.addUnitsForDWOSection(D, S, DW_SECT_INFO, Lazy); + }); + DWOUnits.finishedInfoUnits(); + DObj.forEachTypesDWOSections([&](const DWARFSection &S) { + DWOUnits.addUnitsForDWOSection(D, S, DW_SECT_EXT_TYPES, Lazy); + }); + } + return DWOUnits; + } + + const DWARFDebugAbbrev *getDebugAbbrevDWO() override { + if (AbbrevDWO) + return AbbrevDWO.get(); + const DWARFObject &DObj = D.getDWARFObj(); + DataExtractor abbrData(DObj.getAbbrevDWOSection(), D.isLittleEndian(), 0); + AbbrevDWO = std::make_unique<DWARFDebugAbbrev>(abbrData); + return AbbrevDWO.get(); + } + + const DWARFUnitIndex &getCUIndex() override { + if (CUIndex) + return *CUIndex; + + DataExtractor Data(D.getDWARFObj().getCUIndexSection(), + D.isLittleEndian(), 0); + CUIndex = std::make_unique<DWARFUnitIndex>(DW_SECT_INFO); + if (CUIndex->parse(Data)) + fixupIndex(D, *CUIndex); + return *CUIndex; + } + const DWARFUnitIndex &getTUIndex() override { + if (TUIndex) + return *TUIndex; + + DataExtractor Data(D.getDWARFObj().getTUIndexSection(), + D.isLittleEndian(), 0); + TUIndex = std::make_unique<DWARFUnitIndex>(DW_SECT_EXT_TYPES); + bool isParseSuccessful = TUIndex->parse(Data); + // If we are parsing TU-index and for .debug_types section we don't need + // to do anything. + if (isParseSuccessful && TUIndex->getVersion() != 2) + fixupIndex(D, *TUIndex); + return *TUIndex; + } + + DWARFGdbIndex &getGdbIndex() override { + if (GdbIndex) + return *GdbIndex; + + DataExtractor Data(D.getDWARFObj().getGdbIndexSection(), true /*LE*/, 0); + GdbIndex = std::make_unique<DWARFGdbIndex>(); + GdbIndex->parse(Data); + return *GdbIndex; + } + + const DWARFDebugAbbrev *getDebugAbbrev() override { + if (Abbrev) + return Abbrev.get(); + + DataExtractor Data(D.getDWARFObj().getAbbrevSection(), + D.isLittleEndian(), 0); + Abbrev = std::make_unique<DWARFDebugAbbrev>(Data); + return Abbrev.get(); + } + + const DWARFDebugLoc *getDebugLoc() override { + if (Loc) + return Loc.get(); + + const DWARFObject &DObj = D.getDWARFObj(); + // Assume all units have the same address byte size. + auto Data = + D.getNumCompileUnits() + ? DWARFDataExtractor(DObj, DObj.getLocSection(), D.isLittleEndian(), + D.getUnitAtIndex(0)->getAddressByteSize()) + : DWARFDataExtractor("", D.isLittleEndian(), 0); + Loc = std::make_unique<DWARFDebugLoc>(std::move(Data)); + return Loc.get(); + } + + const DWARFDebugAranges *getDebugAranges() override { + if (Aranges) + return Aranges.get(); + + Aranges = std::make_unique<DWARFDebugAranges>(); + Aranges->generate(&D); + return Aranges.get(); + } + + Expected<const DWARFDebugLine::LineTable *> + getLineTableForUnit(DWARFUnit *U, function_ref<void(Error)> RecoverableErrorHandler) override { + if (!Line) + Line = std::make_unique<DWARFDebugLine>(); + + auto UnitDIE = U->getUnitDIE(); + if (!UnitDIE) + return nullptr; + + auto Offset = toSectionOffset(UnitDIE.find(DW_AT_stmt_list)); + if (!Offset) + return nullptr; // No line table for this compile unit. + + uint64_t stmtOffset = *Offset + U->getLineTableOffset(); + // See if the line table is cached. + if (const DWARFLineTable *lt = Line->getLineTable(stmtOffset)) + return lt; + + // Make sure the offset is good before we try to parse. + if (stmtOffset >= U->getLineSection().Data.size()) + return nullptr; + + // We have to parse it first. + DWARFDataExtractor Data(U->getContext().getDWARFObj(), U->getLineSection(), + U->isLittleEndian(), U->getAddressByteSize()); + return Line->getOrParseLineTable(Data, stmtOffset, U->getContext(), U, + RecoverableErrorHandler); + + } + + void clearLineTableForUnit(DWARFUnit *U) override { + if (!Line) + return; + + auto UnitDIE = U->getUnitDIE(); + if (!UnitDIE) + return; + + auto Offset = toSectionOffset(UnitDIE.find(DW_AT_stmt_list)); + if (!Offset) + return; + + uint64_t stmtOffset = *Offset + U->getLineTableOffset(); + Line->clearLineTable(stmtOffset); + } + + Expected<const DWARFDebugFrame *> getDebugFrame() override { + if (DebugFrame) + return DebugFrame.get(); + const DWARFObject &DObj = D.getDWARFObj(); + const DWARFSection &DS = DObj.getFrameSection(); + + // There's a "bug" in the DWARFv3 standard with respect to the target address + // size within debug frame sections. While DWARF is supposed to be independent + // of its container, FDEs have fields with size being "target address size", + // which isn't specified in DWARF in general. It's only specified for CUs, but + // .eh_frame can appear without a .debug_info section. Follow the example of + // other tools (libdwarf) and extract this from the container (ObjectFile + // provides this information). This problem is fixed in DWARFv4 + // See this dwarf-discuss discussion for more details: + // http://lists.dwarfstd.org/htdig.cgi/dwarf-discuss-dwarfstd.org/2011-December/001173.html + DWARFDataExtractor Data(DObj, DS, D.isLittleEndian(), + DObj.getAddressSize()); + auto DF = + std::make_unique<DWARFDebugFrame>(D.getArch(), /*IsEH=*/false, + DS.Address); + if (Error E = DF->parse(Data)) + return std::move(E); + + DebugFrame.swap(DF); + return DebugFrame.get(); + } + + Expected<const DWARFDebugFrame *> getEHFrame() override { + if (EHFrame) + return EHFrame.get(); + const DWARFObject &DObj = D.getDWARFObj(); + + const DWARFSection &DS = DObj.getEHFrameSection(); + DWARFDataExtractor Data(DObj, DS, D.isLittleEndian(), + DObj.getAddressSize()); + auto DF = + std::make_unique<DWARFDebugFrame>(D.getArch(), /*IsEH=*/true, + DS.Address); + if (Error E = DF->parse(Data)) + return std::move(E); + EHFrame.swap(DF); + return EHFrame.get(); + } + + const DWARFDebugMacro *getDebugMacinfo() override { + if (!Macinfo) + Macinfo = parseMacroOrMacinfo(MacinfoSection); + return Macinfo.get(); + } + const DWARFDebugMacro *getDebugMacinfoDWO() override { + if (!MacinfoDWO) + MacinfoDWO = parseMacroOrMacinfo(MacinfoDwoSection); + return MacinfoDWO.get(); + } + const DWARFDebugMacro *getDebugMacro() override { + if (!Macro) + Macro = parseMacroOrMacinfo(MacroSection); + return Macro.get(); + } + const DWARFDebugMacro *getDebugMacroDWO() override { + if (!MacroDWO) + MacroDWO = parseMacroOrMacinfo(MacroDwoSection); + return MacroDWO.get(); + } + const DWARFDebugNames &getDebugNames() override { + const DWARFObject &DObj = D.getDWARFObj(); + return getAccelTable(Names, DObj, DObj.getNamesSection(), + DObj.getStrSection(), D.isLittleEndian()); + } + const AppleAcceleratorTable &getAppleNames() override { + const DWARFObject &DObj = D.getDWARFObj(); + return getAccelTable(AppleNames, DObj, DObj.getAppleNamesSection(), + DObj.getStrSection(), D.isLittleEndian()); + + } + const AppleAcceleratorTable &getAppleTypes() override { + const DWARFObject &DObj = D.getDWARFObj(); + return getAccelTable(AppleTypes, DObj, DObj.getAppleTypesSection(), + DObj.getStrSection(), D.isLittleEndian()); + + } + const AppleAcceleratorTable &getAppleNamespaces() override { + const DWARFObject &DObj = D.getDWARFObj(); + return getAccelTable(AppleNamespaces, DObj, + DObj.getAppleNamespacesSection(), + DObj.getStrSection(), D.isLittleEndian()); + + } + const AppleAcceleratorTable &getAppleObjC() override { + const DWARFObject &DObj = D.getDWARFObj(); + return getAccelTable(AppleObjC, DObj, DObj.getAppleObjCSection(), + DObj.getStrSection(), D.isLittleEndian()); + } + + std::shared_ptr<DWARFContext> + getDWOContext(StringRef AbsolutePath) override { + if (auto S = DWP.lock()) { + DWARFContext *Ctxt = S->Context.get(); + return std::shared_ptr<DWARFContext>(std::move(S), Ctxt); + } + + std::weak_ptr<DWOFile> *Entry = &DWOFiles[AbsolutePath]; + + if (auto S = Entry->lock()) { + DWARFContext *Ctxt = S->Context.get(); + return std::shared_ptr<DWARFContext>(std::move(S), Ctxt); + } + + const DWARFObject &DObj = D.getDWARFObj(); + + Expected<OwningBinary<ObjectFile>> Obj = [&] { + if (!CheckedForDWP) { + SmallString<128> DWPName; + auto Obj = object::ObjectFile::createObjectFile( + this->DWPName.empty() + ? (DObj.getFileName() + ".dwp").toStringRef(DWPName) + : StringRef(this->DWPName)); + if (Obj) { + Entry = &DWP; + return Obj; + } else { + CheckedForDWP = true; + // TODO: Should this error be handled (maybe in a high verbosity mode) + // before falling back to .dwo files? + consumeError(Obj.takeError()); + } + } + + return object::ObjectFile::createObjectFile(AbsolutePath); + }(); + + if (!Obj) { + // TODO: Actually report errors helpfully. + consumeError(Obj.takeError()); + return nullptr; + } + + auto S = std::make_shared<DWOFile>(); + S->File = std::move(Obj.get()); + // Allow multi-threaded access if there is a .dwp file as the CU index and + // TU index might be accessed from multiple threads. + bool ThreadSafe = isThreadSafe(); + S->Context = DWARFContext::create( + *S->File.getBinary(), DWARFContext::ProcessDebugRelocations::Ignore, + nullptr, "", WithColor::defaultErrorHandler, + WithColor::defaultWarningHandler, ThreadSafe); + *Entry = S; + auto *Ctxt = S->Context.get(); + return std::shared_ptr<DWARFContext>(std::move(S), Ctxt); + } + + bool isThreadSafe() const override { return false; } + + const DenseMap<uint64_t, DWARFTypeUnit *> &getNormalTypeUnitMap() { + if (!NormalTypeUnits) { + NormalTypeUnits.emplace(); + for (const auto &U :D.normal_units()) { + if (DWARFTypeUnit *TU = dyn_cast<DWARFTypeUnit>(U.get())) + (*NormalTypeUnits)[TU->getTypeHash()] = TU; + } + } + return *NormalTypeUnits; + } + + const DenseMap<uint64_t, DWARFTypeUnit *> &getDWOTypeUnitMap() { + if (!DWOTypeUnits) { + DWOTypeUnits.emplace(); + for (const auto &U :D.dwo_units()) { + if (DWARFTypeUnit *TU = dyn_cast<DWARFTypeUnit>(U.get())) + (*DWOTypeUnits)[TU->getTypeHash()] = TU; + } + } + return *DWOTypeUnits; + } + + const DenseMap<uint64_t, DWARFTypeUnit *> & + getTypeUnitMap(bool IsDWO) override { + if (IsDWO) + return getDWOTypeUnitMap(); + else + return getNormalTypeUnitMap(); + } + + +}; + +class ThreadSafeState : public ThreadUnsafeDWARFContextState { + std::recursive_mutex Mutex; + +public: + ThreadSafeState(DWARFContext &DC, std::string &DWP) : + ThreadUnsafeDWARFContextState(DC, DWP) {} + + DWARFUnitVector &getNormalUnits() override { + std::unique_lock<std::recursive_mutex> LockGuard(Mutex); + return ThreadUnsafeDWARFContextState::getNormalUnits(); + } + DWARFUnitVector &getDWOUnits(bool Lazy) override { + std::unique_lock<std::recursive_mutex> LockGuard(Mutex); + // We need to not do lazy parsing when we need thread safety as + // DWARFUnitVector, in lazy mode, will slowly add things to itself and + // will cause problems in a multi-threaded environment. + return ThreadUnsafeDWARFContextState::getDWOUnits(false); + } + const DWARFUnitIndex &getCUIndex() override { + std::unique_lock<std::recursive_mutex> LockGuard(Mutex); + return ThreadUnsafeDWARFContextState::getCUIndex(); + } + const DWARFDebugAbbrev *getDebugAbbrevDWO() override { + std::unique_lock<std::recursive_mutex> LockGuard(Mutex); + return ThreadUnsafeDWARFContextState::getDebugAbbrevDWO(); + } + + const DWARFUnitIndex &getTUIndex() override { + std::unique_lock<std::recursive_mutex> LockGuard(Mutex); + return ThreadUnsafeDWARFContextState::getTUIndex(); + } + DWARFGdbIndex &getGdbIndex() override { + std::unique_lock<std::recursive_mutex> LockGuard(Mutex); + return ThreadUnsafeDWARFContextState::getGdbIndex(); + } + const DWARFDebugAbbrev *getDebugAbbrev() override { + std::unique_lock<std::recursive_mutex> LockGuard(Mutex); + return ThreadUnsafeDWARFContextState::getDebugAbbrev(); + } + const DWARFDebugLoc *getDebugLoc() override { + std::unique_lock<std::recursive_mutex> LockGuard(Mutex); + return ThreadUnsafeDWARFContextState::getDebugLoc(); + } + const DWARFDebugAranges *getDebugAranges() override { + std::unique_lock<std::recursive_mutex> LockGuard(Mutex); + return ThreadUnsafeDWARFContextState::getDebugAranges(); + } + Expected<const DWARFDebugLine::LineTable *> + getLineTableForUnit(DWARFUnit *U, function_ref<void(Error)> RecoverableErrorHandler) override { + std::unique_lock<std::recursive_mutex> LockGuard(Mutex); + return ThreadUnsafeDWARFContextState::getLineTableForUnit(U, RecoverableErrorHandler); + } + void clearLineTableForUnit(DWARFUnit *U) override { + std::unique_lock<std::recursive_mutex> LockGuard(Mutex); + return ThreadUnsafeDWARFContextState::clearLineTableForUnit(U); + } + Expected<const DWARFDebugFrame *> getDebugFrame() override { + std::unique_lock<std::recursive_mutex> LockGuard(Mutex); + return ThreadUnsafeDWARFContextState::getDebugFrame(); + } + Expected<const DWARFDebugFrame *> getEHFrame() override { + std::unique_lock<std::recursive_mutex> LockGuard(Mutex); + return ThreadUnsafeDWARFContextState::getEHFrame(); + } + const DWARFDebugMacro *getDebugMacinfo() override { + std::unique_lock<std::recursive_mutex> LockGuard(Mutex); + return ThreadUnsafeDWARFContextState::getDebugMacinfo(); + } + const DWARFDebugMacro *getDebugMacinfoDWO() override { + std::unique_lock<std::recursive_mutex> LockGuard(Mutex); + return ThreadUnsafeDWARFContextState::getDebugMacinfoDWO(); + } + const DWARFDebugMacro *getDebugMacro() override { + std::unique_lock<std::recursive_mutex> LockGuard(Mutex); + return ThreadUnsafeDWARFContextState::getDebugMacro(); + } + const DWARFDebugMacro *getDebugMacroDWO() override { + std::unique_lock<std::recursive_mutex> LockGuard(Mutex); + return ThreadUnsafeDWARFContextState::getDebugMacroDWO(); + } + const DWARFDebugNames &getDebugNames() override { + std::unique_lock<std::recursive_mutex> LockGuard(Mutex); + return ThreadUnsafeDWARFContextState::getDebugNames(); + } + const AppleAcceleratorTable &getAppleNames() override { + std::unique_lock<std::recursive_mutex> LockGuard(Mutex); + return ThreadUnsafeDWARFContextState::getAppleNames(); + } + const AppleAcceleratorTable &getAppleTypes() override { + std::unique_lock<std::recursive_mutex> LockGuard(Mutex); + return ThreadUnsafeDWARFContextState::getAppleTypes(); + } + const AppleAcceleratorTable &getAppleNamespaces() override { + std::unique_lock<std::recursive_mutex> LockGuard(Mutex); + return ThreadUnsafeDWARFContextState::getAppleNamespaces(); + } + const AppleAcceleratorTable &getAppleObjC() override { + std::unique_lock<std::recursive_mutex> LockGuard(Mutex); + return ThreadUnsafeDWARFContextState::getAppleObjC(); + } + std::shared_ptr<DWARFContext> + getDWOContext(StringRef AbsolutePath) override { + std::unique_lock<std::recursive_mutex> LockGuard(Mutex); + return ThreadUnsafeDWARFContextState::getDWOContext(AbsolutePath); + } + + bool isThreadSafe() const override { return true; } + + const DenseMap<uint64_t, DWARFTypeUnit *> & + getTypeUnitMap(bool IsDWO) override { + std::unique_lock<std::recursive_mutex> LockGuard(Mutex); + return ThreadUnsafeDWARFContextState::getTypeUnitMap(IsDWO); + } +}; + + + DWARFContext::DWARFContext(std::unique_ptr<const DWARFObject> DObj, std::string DWPName, std::function<void(Error)> RecoverableErrorHandler, - std::function<void(Error)> WarningHandler) - : DIContext(CK_DWARF), DWPName(std::move(DWPName)), + std::function<void(Error)> WarningHandler, + bool ThreadSafe) + : DIContext(CK_DWARF), RecoverableErrorHandler(RecoverableErrorHandler), - WarningHandler(WarningHandler), DObj(std::move(DObj)) {} + WarningHandler(WarningHandler), DObj(std::move(DObj)) { + if (ThreadSafe) + State = std::make_unique<ThreadSafeState>(*this, DWPName); + else + State = std::make_unique<ThreadUnsafeDWARFContextState>(*this, DWPName); + } DWARFContext::~DWARFContext() = default; @@ -266,47 +945,6 @@ static void dumpRnglistsSection( } } -std::unique_ptr<DWARFDebugMacro> -DWARFContext::parseMacroOrMacinfo(MacroSecType SectionType) { - auto Macro = std::make_unique<DWARFDebugMacro>(); - auto ParseAndDump = [&](DWARFDataExtractor &Data, bool IsMacro) { - if (Error Err = IsMacro ? Macro->parseMacro(SectionType == MacroSection - ? compile_units() - : dwo_compile_units(), - SectionType == MacroSection - ? getStringExtractor() - : getStringDWOExtractor(), - Data) - : Macro->parseMacinfo(Data)) { - RecoverableErrorHandler(std::move(Err)); - Macro = nullptr; - } - }; - switch (SectionType) { - case MacinfoSection: { - DWARFDataExtractor Data(DObj->getMacinfoSection(), isLittleEndian(), 0); - ParseAndDump(Data, /*IsMacro=*/false); - break; - } - case MacinfoDwoSection: { - DWARFDataExtractor Data(DObj->getMacinfoDWOSection(), isLittleEndian(), 0); - ParseAndDump(Data, /*IsMacro=*/false); - break; - } - case MacroSection: { - DWARFDataExtractor Data(*DObj, DObj->getMacroSection(), isLittleEndian(), - 0); - ParseAndDump(Data, /*IsMacro=*/true); - break; - } - case MacroDwoSection: { - DWARFDataExtractor Data(DObj->getMacroDWOSection(), isLittleEndian(), 0); - ParseAndDump(Data, /*IsMacro=*/true); - break; - } - } - return Macro; -} static void dumpLoclistsSection(raw_ostream &OS, DIDumpOptions DumpOpts, DWARFDataExtractor Data, const DWARFObject &Obj, @@ -700,34 +1338,22 @@ void DWARFContext::dump( DWARFTypeUnit *DWARFContext::getTypeUnitForHash(uint16_t Version, uint64_t Hash, bool IsDWO) { - parseDWOUnits(LazyParse); - + DWARFUnitVector &DWOUnits = State->getDWOUnits(); if (const auto &TUI = getTUIndex()) { if (const auto *R = TUI.getFromHash(Hash)) return dyn_cast_or_null<DWARFTypeUnit>( DWOUnits.getUnitForIndexEntry(*R)); return nullptr; } - - struct UnitContainers { - const DWARFUnitVector &Units; - std::optional<DenseMap<uint64_t, DWARFTypeUnit *>> ⤅ - }; - UnitContainers Units = IsDWO ? UnitContainers{DWOUnits, DWOTypeUnits} - : UnitContainers{NormalUnits, NormalTypeUnits}; - if (!Units.Map) { - Units.Map.emplace(); - for (const auto &U : IsDWO ? dwo_units() : normal_units()) { - if (DWARFTypeUnit *TU = dyn_cast<DWARFTypeUnit>(U.get())) - (*Units.Map)[TU->getTypeHash()] = TU; - } - } - - return (*Units.Map)[Hash]; + const DenseMap<uint64_t, DWARFTypeUnit *> &Map = State->getTypeUnitMap(IsDWO); + auto Iter = Map.find(Hash); + if (Iter != Map.end()) + return Iter->second; + return nullptr; } DWARFCompileUnit *DWARFContext::getDWOCompileUnitForHash(uint64_t Hash) { - parseDWOUnits(LazyParse); + DWARFUnitVector &DWOUnits = State->getDWOUnits(LazyParse); if (const auto &CUI = getCUIndex()) { if (const auto *R = CUI.getFromHash(Hash)) @@ -757,8 +1383,7 @@ DWARFCompileUnit *DWARFContext::getDWOCompileUnitForHash(uint64_t Hash) { } DWARFDie DWARFContext::getDIEForOffset(uint64_t Offset) { - parseNormalUnits(); - if (auto *CU = NormalUnits.getUnitForOffset(Offset)) + if (auto *CU = State->getNormalUnits().getUnitForOffset(Offset)) return CU->getDIEForOffset(Offset); return DWARFDie(); } @@ -782,302 +1407,77 @@ bool DWARFContext::verify(raw_ostream &OS, DIDumpOptions DumpOpts) { return Success; } -void fixupIndexV4(const DWARFObject &DObj, DWARFContext &C, - DWARFUnitIndex &Index) { - using EntryType = DWARFUnitIndex::Entry::SectionContribution; - using EntryMap = DenseMap<uint32_t, EntryType>; - EntryMap Map; - if (DObj.getCUIndexSection().empty()) - return; - - uint64_t Offset = 0; - uint32_t TruncOffset = 0; - DObj.forEachInfoDWOSections([&](const DWARFSection &S) { - if (!(C.getParseCUTUIndexManually() || - S.Data.size() >= std::numeric_limits<uint32_t>::max())) - return; - - DWARFDataExtractor Data(DObj, S, C.isLittleEndian(), 0); - while (Data.isValidOffset(Offset)) { - DWARFUnitHeader Header; - if (!Header.extract(C, Data, &Offset, DWARFSectionKind::DW_SECT_INFO)) { - logAllUnhandledErrors( - createError("Failed to parse CU header in DWP file"), errs()); - Map.clear(); - break; - } - - auto Iter = Map.insert({TruncOffset, - {Header.getOffset(), Header.getNextUnitOffset() - - Header.getOffset()}}); - if (!Iter.second) { - logAllUnhandledErrors( - createError("Collision occured between for truncated offset 0x" + - Twine::utohexstr(TruncOffset)), - errs()); - Map.clear(); - return; - } - - Offset = Header.getNextUnitOffset(); - TruncOffset = Offset; - } - }); - - if (Map.empty()) - return; - - for (DWARFUnitIndex::Entry &E : Index.getMutableRows()) { - if (!E.isValid()) - continue; - DWARFUnitIndex::Entry::SectionContribution &CUOff = E.getContribution(); - auto Iter = Map.find(CUOff.getOffset()); - if (Iter == Map.end()) { - logAllUnhandledErrors(createError("Could not find CU offset 0x" + - Twine::utohexstr(CUOff.getOffset()) + - " in the Map"), - errs()); - break; - } - CUOff.setOffset(Iter->second.getOffset()); - if (CUOff.getOffset() != Iter->second.getOffset()) - logAllUnhandledErrors(createError("Length of CU in CU index doesn't " - "match calculated length at offset 0x" + - Twine::utohexstr(CUOff.getOffset())), - errs()); - } -} - -void fixupIndexV5(const DWARFObject &DObj, DWARFContext &C, - DWARFUnitIndex &Index) { - DenseMap<uint64_t, uint64_t> Map; - - DObj.forEachInfoDWOSections([&](const DWARFSection &S) { - if (!(C.getParseCUTUIndexManually() || - S.Data.size() >= std::numeric_limits<uint32_t>::max())) - return; - DWARFDataExtractor Data(DObj, S, C.isLittleEndian(), 0); - uint64_t Offset = 0; - while (Data.isValidOffset(Offset)) { - DWARFUnitHeader Header; - if (!Header.extract(C, Data, &Offset, DWARFSectionKind::DW_SECT_INFO)) { - logAllUnhandledErrors( - createError("Failed to parse unit header in DWP file"), errs()); - break; - } - bool CU = Header.getUnitType() == DW_UT_split_compile; - uint64_t Sig = CU ? *Header.getDWOId() : Header.getTypeHash(); - Map[Sig] = Header.getOffset(); - Offset = Header.getNextUnitOffset(); - } - }); - if (Map.empty()) - return; - for (DWARFUnitIndex::Entry &E : Index.getMutableRows()) { - if (!E.isValid()) - continue; - DWARFUnitIndex::Entry::SectionContribution &CUOff = E.getContribution(); - auto Iter = Map.find(E.getSignature()); - if (Iter == Map.end()) { - logAllUnhandledErrors( - createError("Could not find unit with signature 0x" + - Twine::utohexstr(E.getSignature()) + " in the Map"), - errs()); - break; - } - CUOff.setOffset(Iter->second); - } -} - -void fixupIndex(const DWARFObject &DObj, DWARFContext &C, - DWARFUnitIndex &Index) { - if (Index.getVersion() < 5) - fixupIndexV4(DObj, C, Index); - else - fixupIndexV5(DObj, C, Index); -} - const DWARFUnitIndex &DWARFContext::getCUIndex() { - if (CUIndex) - return *CUIndex; - - DataExtractor CUIndexData(DObj->getCUIndexSection(), isLittleEndian(), 0); - CUIndex = std::make_unique<DWARFUnitIndex>(DW_SECT_INFO); - bool IsParseSuccessful = CUIndex->parse(CUIndexData); - if (IsParseSuccessful) - fixupIndex(*DObj, *this, *CUIndex); - return *CUIndex; + return State->getCUIndex(); } const DWARFUnitIndex &DWARFContext::getTUIndex() { - if (TUIndex) - return *TUIndex; - - DataExtractor TUIndexData(DObj->getTUIndexSection(), isLittleEndian(), 0); - TUIndex = std::make_unique<DWARFUnitIndex>(DW_SECT_EXT_TYPES); - bool isParseSuccessful = TUIndex->parse(TUIndexData); - // If we are parsing TU-index and for .debug_types section we don't need - // to do anything. - if (isParseSuccessful && TUIndex->getVersion() != 2) - fixupIndex(*DObj, *this, *TUIndex); - return *TUIndex; + return State->getTUIndex(); } DWARFGdbIndex &DWARFContext::getGdbIndex() { - if (GdbIndex) - return *GdbIndex; - - DataExtractor GdbIndexData(DObj->getGdbIndexSection(), true /*LE*/, 0); - GdbIndex = std::make_unique<DWARFGdbIndex>(); - GdbIndex->parse(GdbIndexData); - return *GdbIndex; + return State->getGdbIndex(); } const DWARFDebugAbbrev *DWARFContext::getDebugAbbrev() { - if (Abbrev) - return Abbrev.get(); - - DataExtractor abbrData(DObj->getAbbrevSection(), isLittleEndian(), 0); - Abbrev = std::make_unique<DWARFDebugAbbrev>(abbrData); - return Abbrev.get(); + return State->getDebugAbbrev(); } const DWARFDebugAbbrev *DWARFContext::getDebugAbbrevDWO() { - if (AbbrevDWO) - return AbbrevDWO.get(); - - DataExtractor abbrData(DObj->getAbbrevDWOSection(), isLittleEndian(), 0); - AbbrevDWO = std::make_unique<DWARFDebugAbbrev>(abbrData); - return AbbrevDWO.get(); + return State->getDebugAbbrevDWO(); } const DWARFDebugLoc *DWARFContext::getDebugLoc() { - if (Loc) - return Loc.get(); - - // Assume all units have the same address byte size. - auto LocData = - getNumCompileUnits() - ? DWARFDataExtractor(*DObj, DObj->getLocSection(), isLittleEndian(), - getUnitAtIndex(0)->getAddressByteSize()) - : DWARFDataExtractor("", isLittleEndian(), 0); - Loc.reset(new DWARFDebugLoc(std::move(LocData))); - return Loc.get(); + return State->getDebugLoc(); } const DWARFDebugAranges *DWARFContext::getDebugAranges() { - if (Aranges) - return Aranges.get(); - - Aranges.reset(new DWARFDebugAranges()); - Aranges->generate(this); - return Aranges.get(); + return State->getDebugAranges(); } Expected<const DWARFDebugFrame *> DWARFContext::getDebugFrame() { - if (DebugFrame) - return DebugFrame.get(); - - const DWARFSection &DS = DObj->getFrameSection(); - - // There's a "bug" in the DWARFv3 standard with respect to the target address - // size within debug frame sections. While DWARF is supposed to be independent - // of its container, FDEs have fields with size being "target address size", - // which isn't specified in DWARF in general. It's only specified for CUs, but - // .eh_frame can appear without a .debug_info section. Follow the example of - // other tools (libdwarf) and extract this from the container (ObjectFile - // provides this information). This problem is fixed in DWARFv4 - // See this dwarf-discuss discussion for more details: - // http://lists.dwarfstd.org/htdig.cgi/dwarf-discuss-dwarfstd.org/2011-December/001173.html - DWARFDataExtractor DebugFrameData(*DObj, DS, isLittleEndian(), - DObj->getAddressSize()); - auto DF = - std::make_unique<DWARFDebugFrame>(getArch(), /*IsEH=*/false, DS.Address); - if (Error E = DF->parse(DebugFrameData)) - return std::move(E); - - DebugFrame.swap(DF); - return DebugFrame.get(); + return State->getDebugFrame(); } Expected<const DWARFDebugFrame *> DWARFContext::getEHFrame() { - if (EHFrame) - return EHFrame.get(); - - const DWARFSection &DS = DObj->getEHFrameSection(); - DWARFDataExtractor DebugFrameData(*DObj, DS, isLittleEndian(), - DObj->getAddressSize()); - - auto DF = - std::make_unique<DWARFDebugFrame>(getArch(), /*IsEH=*/true, DS.Address); - if (Error E = DF->parse(DebugFrameData)) - return std::move(E); - DebugFrame.swap(DF); - return DebugFrame.get(); + return State->getEHFrame(); } const DWARFDebugMacro *DWARFContext::getDebugMacro() { - if (!Macro) - Macro = parseMacroOrMacinfo(MacroSection); - return Macro.get(); + return State->getDebugMacro(); } const DWARFDebugMacro *DWARFContext::getDebugMacroDWO() { - if (!MacroDWO) - MacroDWO = parseMacroOrMacinfo(MacroDwoSection); - return MacroDWO.get(); + return State->getDebugMacroDWO(); } const DWARFDebugMacro *DWARFContext::getDebugMacinfo() { - if (!Macinfo) - Macinfo = parseMacroOrMacinfo(MacinfoSection); - return Macinfo.get(); + return State->getDebugMacinfo(); } const DWARFDebugMacro *DWARFContext::getDebugMacinfoDWO() { - if (!MacinfoDWO) - MacinfoDWO = parseMacroOrMacinfo(MacinfoDwoSection); - return MacinfoDWO.get(); + return State->getDebugMacinfoDWO(); } -template <typename T> -static T &getAccelTable(std::unique_ptr<T> &Cache, const DWARFObject &Obj, - const DWARFSection &Section, StringRef StringSection, - bool IsLittleEndian) { - if (Cache) - return *Cache; - DWARFDataExtractor AccelSection(Obj, Section, IsLittleEndian, 0); - DataExtractor StrData(StringSection, IsLittleEndian, 0); - Cache.reset(new T(AccelSection, StrData)); - if (Error E = Cache->extract()) - llvm::consumeError(std::move(E)); - return *Cache; -} const DWARFDebugNames &DWARFContext::getDebugNames() { - return getAccelTable(Names, *DObj, DObj->getNamesSection(), - DObj->getStrSection(), isLittleEndian()); + return State->getDebugNames(); } const AppleAcceleratorTable &DWARFContext::getAppleNames() { - return getAccelTable(AppleNames, *DObj, DObj->getAppleNamesSection(), - DObj->getStrSection(), isLittleEndian()); + return State->getAppleNames(); } const AppleAcceleratorTable &DWARFContext::getAppleTypes() { - return getAccelTable(AppleTypes, *DObj, DObj->getAppleTypesSection(), - DObj->getStrSection(), isLittleEndian()); + return State->getAppleTypes(); } const AppleAcceleratorTable &DWARFContext::getAppleNamespaces() { - return getAccelTable(AppleNamespaces, *DObj, - DObj->getAppleNamespacesSection(), - DObj->getStrSection(), isLittleEndian()); + return State->getAppleNamespaces(); } const AppleAcceleratorTable &DWARFContext::getAppleObjC() { - return getAccelTable(AppleObjC, *DObj, DObj->getAppleObjCSection(), - DObj->getStrSection(), isLittleEndian()); + return State->getAppleObjC(); } const DWARFDebugLine::LineTable * @@ -1093,77 +1493,20 @@ DWARFContext::getLineTableForUnit(DWARFUnit *U) { Expected<const DWARFDebugLine::LineTable *> DWARFContext::getLineTableForUnit( DWARFUnit *U, function_ref<void(Error)> RecoverableErrorHandler) { - if (!Line) - Line.reset(new DWARFDebugLine); - - auto UnitDIE = U->getUnitDIE(); - if (!UnitDIE) - return nullptr; - - auto Offset = toSectionOffset(UnitDIE.find(DW_AT_stmt_list)); - if (!Offset) - return nullptr; // No line table for this compile unit. - - uint64_t stmtOffset = *Offset + U->getLineTableOffset(); - // See if the line table is cached. - if (const DWARFLineTable *lt = Line->getLineTable(stmtOffset)) - return lt; - - // Make sure the offset is good before we try to parse. - if (stmtOffset >= U->getLineSection().Data.size()) - return nullptr; - - // We have to parse it first. - DWARFDataExtractor lineData(*DObj, U->getLineSection(), isLittleEndian(), - U->getAddressByteSize()); - return Line->getOrParseLineTable(lineData, stmtOffset, *this, U, - RecoverableErrorHandler); + return State->getLineTableForUnit(U, RecoverableErrorHandler); } void DWARFContext::clearLineTableForUnit(DWARFUnit *U) { - if (!Line) - return; - - auto UnitDIE = U->getUnitDIE(); - if (!UnitDIE) - return; - - auto Offset = toSectionOffset(UnitDIE.find(DW_AT_stmt_list)); - if (!Offset) - return; - - uint64_t stmtOffset = *Offset + U->getLineTableOffset(); - Line->clearLineTable(stmtOffset); + return State->clearLineTableForUnit(U); } -void DWARFContext::parseNormalUnits() { - if (!NormalUnits.empty()) - return; - DObj->forEachInfoSections([&](const DWARFSection &S) { - NormalUnits.addUnitsForSection(*this, S, DW_SECT_INFO); - }); - NormalUnits.finishedInfoUnits(); - DObj->forEachTypesSections([&](const DWARFSection &S) { - NormalUnits.addUnitsForSection(*this, S, DW_SECT_EXT_TYPES); - }); -} - -void DWARFContext::parseDWOUnits(bool Lazy) { - if (!DWOUnits.empty()) - return; - DObj->forEachInfoDWOSections([&](const DWARFSection &S) { - DWOUnits.addUnitsForDWOSection(*this, S, DW_SECT_INFO, Lazy); - }); - DWOUnits.finishedInfoUnits(); - DObj->forEachTypesDWOSections([&](const DWARFSection &S) { - DWOUnits.addUnitsForDWOSection(*this, S, DW_SECT_EXT_TYPES, Lazy); - }); +DWARFUnitVector &DWARFContext::getDWOUnits(bool Lazy) { + return State->getDWOUnits(Lazy); } DWARFCompileUnit *DWARFContext::getCompileUnitForOffset(uint64_t Offset) { - parseNormalUnits(); return dyn_cast_or_null<DWARFCompileUnit>( - NormalUnits.getUnitForOffset(Offset)); + State->getNormalUnits().getUnitForOffset(Offset)); } DWARFCompileUnit *DWARFContext::getCompileUnitForCodeAddress(uint64_t Address) { @@ -1187,7 +1530,7 @@ DWARFCompileUnit *DWARFContext::getCompileUnitForDataAddress(uint64_t Address) { // So, we walk the CU's and their child DI's manually, looking for the // specific global variable. for (std::unique_ptr<DWARFUnit> &CU : compile_units()) { - if (DWARFDie Die = CU->getVariableForAddress(Address)) { + if (CU->getVariableForAddress(Address)) { return static_cast<DWARFCompileUnit *>(CU.get()); } } @@ -1519,52 +1862,7 @@ DWARFContext::getInliningInfoForAddress(object::SectionedAddress Address, std::shared_ptr<DWARFContext> DWARFContext::getDWOContext(StringRef AbsolutePath) { - if (auto S = DWP.lock()) { - DWARFContext *Ctxt = S->Context.get(); - return std::shared_ptr<DWARFContext>(std::move(S), Ctxt); - } - - std::weak_ptr<DWOFile> *Entry = &DWOFiles[AbsolutePath]; - - if (auto S = Entry->lock()) { - DWARFContext *Ctxt = S->Context.get(); - return std::shared_ptr<DWARFContext>(std::move(S), Ctxt); - } - - Expected<OwningBinary<ObjectFile>> Obj = [&] { - if (!CheckedForDWP) { - SmallString<128> DWPName; - auto Obj = object::ObjectFile::createObjectFile( - this->DWPName.empty() - ? (DObj->getFileName() + ".dwp").toStringRef(DWPName) - : StringRef(this->DWPName)); - if (Obj) { - Entry = &DWP; - return Obj; - } else { - CheckedForDWP = true; - // TODO: Should this error be handled (maybe in a high verbosity mode) - // before falling back to .dwo files? - consumeError(Obj.takeError()); - } - } - - return object::ObjectFile::createObjectFile(AbsolutePath); - }(); - - if (!Obj) { - // TODO: Actually report errors helpfully. - consumeError(Obj.takeError()); - return nullptr; - } - - auto S = std::make_shared<DWOFile>(); - S->File = std::move(Obj.get()); - S->Context = DWARFContext::create(*S->File.getBinary(), - ProcessDebugRelocations::Ignore); - *Entry = S; - auto *Ctxt = S->Context.get(); - return std::shared_ptr<DWARFContext>(std::move(S), Ctxt); + return State->getDWOContext(AbsolutePath); } static Error createError(const Twine &Reason, llvm::Error E) { @@ -1909,7 +2207,7 @@ public: continue; if (!Section.relocations().empty() && Name.ends_with(".dwo") && - RelSecName.startswith(".debug")) { + RelSecName.starts_with(".debug")) { HandleWarning(createError("unexpected relocations for dwo section '" + RelSecName + "'")); } @@ -2115,23 +2413,27 @@ DWARFContext::create(const object::ObjectFile &Obj, ProcessDebugRelocations RelocAction, const LoadedObjectInfo *L, std::string DWPName, std::function<void(Error)> RecoverableErrorHandler, - std::function<void(Error)> WarningHandler) { + std::function<void(Error)> WarningHandler, + bool ThreadSafe) { auto DObj = std::make_unique<DWARFObjInMemory>( Obj, L, RecoverableErrorHandler, WarningHandler, RelocAction); - return std::make_unique<DWARFContext>(std::move(DObj), std::move(DWPName), + return std::make_unique<DWARFContext>(std::move(DObj), + std::move(DWPName), RecoverableErrorHandler, - WarningHandler); + WarningHandler, + ThreadSafe); } std::unique_ptr<DWARFContext> DWARFContext::create(const StringMap<std::unique_ptr<MemoryBuffer>> &Sections, uint8_t AddrSize, bool isLittleEndian, std::function<void(Error)> RecoverableErrorHandler, - std::function<void(Error)> WarningHandler) { + std::function<void(Error)> WarningHandler, + bool ThreadSafe) { auto DObj = std::make_unique<DWARFObjInMemory>(Sections, AddrSize, isLittleEndian); return std::make_unique<DWARFContext>( - std::move(DObj), "", RecoverableErrorHandler, WarningHandler); + std::move(DObj), "", RecoverableErrorHandler, WarningHandler, ThreadSafe); } uint8_t DWARFContext::getCUAddrSize() { diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugAbbrev.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugAbbrev.cpp index 3014e61f566a..85959ecc5e17 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugAbbrev.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugAbbrev.cpp @@ -105,9 +105,9 @@ std::string DWARFAbbreviationDeclarationSet::getCodeRange() const { DWARFDebugAbbrev::DWARFDebugAbbrev(DataExtractor Data) : AbbrDeclSets(), PrevAbbrOffsetPos(AbbrDeclSets.end()), Data(Data) {} -void DWARFDebugAbbrev::parse() const { +Error DWARFDebugAbbrev::parse() const { if (!Data) - return; + return Error::success(); uint64_t Offset = 0; auto I = AbbrDeclSets.begin(); while (Data->isValidOffset(Offset)) { @@ -116,17 +116,19 @@ void DWARFDebugAbbrev::parse() const { uint64_t CUAbbrOffset = Offset; DWARFAbbreviationDeclarationSet AbbrDecls; if (Error Err = AbbrDecls.extract(*Data, &Offset)) { - // FIXME: We should propagate the error upwards. - consumeError(std::move(Err)); - break; + Data = std::nullopt; + return Err; } AbbrDeclSets.insert(I, std::make_pair(CUAbbrOffset, std::move(AbbrDecls))); } Data = std::nullopt; + return Error::success(); } void DWARFDebugAbbrev::dump(raw_ostream &OS) const { - parse(); + if (Error Err = parse()) + // FIXME: We should propagate this error or otherwise display it. + llvm::consumeError(std::move(Err)); if (AbbrDeclSets.empty()) { OS << "< EMPTY >\n"; diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp index 6f2afe5d50e9..78792cf83891 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp @@ -170,9 +170,14 @@ void DWARFDebugLine::Prologue::dump(raw_ostream &OS, if (ContentTypes.HasLength) OS << format(" length: 0x%8.8" PRIx64 "\n", FileEntry.Length); if (ContentTypes.HasSource) { - OS << " source: "; - FileEntry.Source.dump(OS, DumpOptions); - OS << '\n'; + auto Source = FileEntry.Source.getAsCString(); + if (!Source) + consumeError(Source.takeError()); + else if ((*Source)[0]) { + OS << " source: "; + FileEntry.Source.dump(OS, DumpOptions); + OS << '\n'; + } } } } diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp index 7af7ed8be7b4..66492f7bf804 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDie.h" +#include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Dwarf.h" @@ -147,7 +148,8 @@ static void dumpAttribute(raw_ostream &OS, const DWARFDie &Die, if (!Name.empty()) WithColor(OS, Color) << Name; - else if (Attr == DW_AT_decl_line || Attr == DW_AT_call_line) { + else if (Attr == DW_AT_decl_line || Attr == DW_AT_decl_column || + Attr == DW_AT_call_line || Attr == DW_AT_call_column) { if (std::optional<uint64_t> Val = FormValue.getAsUnsignedConstant()) OS << *Val; else @@ -189,7 +191,8 @@ static void dumpAttribute(raw_ostream &OS, const DWARFDie &Die, // We have dumped the attribute raw value. For some attributes // having both the raw value and the pretty-printed value is // interesting. These attributes are handled below. - if (Attr == DW_AT_specification || Attr == DW_AT_abstract_origin) { + if (Attr == DW_AT_specification || Attr == DW_AT_abstract_origin || + Attr == DW_AT_call_origin) { if (const char *Name = Die.getAttributeValueAsReferencedDie(FormValue).getName( DINameKind::LinkageName)) @@ -487,18 +490,23 @@ void DWARFDie::getCallerFrame(uint32_t &CallFile, uint32_t &CallLine, CallDiscriminator = toUnsigned(find(DW_AT_GNU_discriminator), 0); } -std::optional<uint64_t> DWARFDie::getTypeSize(uint64_t PointerSize) { - if (auto SizeAttr = find(DW_AT_byte_size)) +static std::optional<uint64_t> +getTypeSizeImpl(DWARFDie Die, uint64_t PointerSize, + SmallPtrSetImpl<const DWARFDebugInfoEntry *> &Visited) { + // Cycle detected? + if (!Visited.insert(Die.getDebugInfoEntry()).second) + return {}; + if (auto SizeAttr = Die.find(DW_AT_byte_size)) if (std::optional<uint64_t> Size = SizeAttr->getAsUnsignedConstant()) return Size; - switch (getTag()) { + switch (Die.getTag()) { case DW_TAG_pointer_type: case DW_TAG_reference_type: case DW_TAG_rvalue_reference_type: return PointerSize; case DW_TAG_ptr_to_member_type: { - if (DWARFDie BaseType = getAttributeValueAsReferencedDie(DW_AT_type)) + if (DWARFDie BaseType = Die.getAttributeValueAsReferencedDie(DW_AT_type)) if (BaseType.getTag() == DW_TAG_subroutine_type) return 2 * PointerSize; return PointerSize; @@ -508,19 +516,20 @@ std::optional<uint64_t> DWARFDie::getTypeSize(uint64_t PointerSize) { case DW_TAG_volatile_type: case DW_TAG_restrict_type: case DW_TAG_typedef: { - if (DWARFDie BaseType = getAttributeValueAsReferencedDie(DW_AT_type)) - return BaseType.getTypeSize(PointerSize); + if (DWARFDie BaseType = Die.getAttributeValueAsReferencedDie(DW_AT_type)) + return getTypeSizeImpl(BaseType, PointerSize, Visited); break; } case DW_TAG_array_type: { - DWARFDie BaseType = getAttributeValueAsReferencedDie(DW_AT_type); + DWARFDie BaseType = Die.getAttributeValueAsReferencedDie(DW_AT_type); if (!BaseType) return std::nullopt; - std::optional<uint64_t> BaseSize = BaseType.getTypeSize(PointerSize); + std::optional<uint64_t> BaseSize = + getTypeSizeImpl(BaseType, PointerSize, Visited); if (!BaseSize) return std::nullopt; uint64_t Size = *BaseSize; - for (DWARFDie Child : *this) { + for (DWARFDie Child : Die) { if (Child.getTag() != DW_TAG_subrange_type) continue; @@ -540,13 +549,18 @@ std::optional<uint64_t> DWARFDie::getTypeSize(uint64_t PointerSize) { return Size; } default: - if (DWARFDie BaseType = getAttributeValueAsReferencedDie(DW_AT_type)) - return BaseType.getTypeSize(PointerSize); + if (DWARFDie BaseType = Die.getAttributeValueAsReferencedDie(DW_AT_type)) + return getTypeSizeImpl(BaseType, PointerSize, Visited); break; } return std::nullopt; } +std::optional<uint64_t> DWARFDie::getTypeSize(uint64_t PointerSize) { + SmallPtrSet<const DWARFDebugInfoEntry *, 4> Visited; + return getTypeSizeImpl(*this, PointerSize, Visited); +} + /// Helper to dump a DIE with all of its parents, but no siblings. static unsigned dumpParentChain(DWARFDie Die, raw_ostream &OS, unsigned Indent, DIDumpOptions DumpOpts, unsigned Depth = 0) { diff --git a/llvm/lib/DebugInfo/DWARF/DWARFTypePrinter.cpp b/llvm/lib/DebugInfo/DWARF/DWARFTypePrinter.cpp index c474de607626..20242d958b6b 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFTypePrinter.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFTypePrinter.cpp @@ -8,7 +8,7 @@ void DWARFTypePrinter::appendTypeTagName(dwarf::Tag T) { StringRef TagStr = TagString(T); static constexpr StringRef Prefix = "DW_TAG_"; static constexpr StringRef Suffix = "_type"; - if (!TagStr.startswith(Prefix) || !TagStr.endswith(Suffix)) + if (!TagStr.starts_with(Prefix) || !TagStr.ends_with(Suffix)) return; OS << TagStr.substr(Prefix.size(), TagStr.size() - (Prefix.size() + Suffix.size())) @@ -181,7 +181,7 @@ DWARFTypePrinter::appendUnqualifiedNameBefore(DWARFDie D, Word = true; StringRef Name = NamePtr; static constexpr StringRef MangledPrefix = "_STN|"; - if (Name.startswith(MangledPrefix)) { + if (Name.starts_with(MangledPrefix)) { Name = Name.drop_front(MangledPrefix.size()); auto Separator = Name.find('|'); assert(Separator != StringRef::npos); @@ -191,12 +191,12 @@ DWARFTypePrinter::appendUnqualifiedNameBefore(DWARFDie D, *OriginalFullName = (BaseName + TemplateArgs).str(); Name = BaseName; } else - EndedWithTemplate = Name.endswith(">"); + EndedWithTemplate = Name.ends_with(">"); OS << Name; // This check would be insufficient for operator overloads like // "operator>>" - but for now Clang doesn't try to simplify them, so this // is OK. Add more nuanced operator overload handling here if/when needed. - if (Name.endswith(">")) + if (Name.ends_with(">")) break; if (!appendTemplateParameters(D)) break; @@ -620,6 +620,9 @@ void DWARFTypePrinter::appendSubroutineNameAfter( case CallingConvention::DW_CC_LLVM_X86RegCall: OS << " __attribute__((regcall))"; break; + case CallingConvention::DW_CC_LLVM_M68kRTD: + OS << " __attribute__((m68k_rtd))"; + break; } } diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp index 19678f121982..9f455fa7e96a 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp @@ -81,8 +81,11 @@ void DWARFUnitVector::addUnitsImpl( if (!Data.isValidOffset(Offset)) return nullptr; DWARFUnitHeader Header; - if (!Header.extract(Context, Data, &Offset, SectionKind)) + if (Error ExtractErr = + Header.extract(Context, Data, &Offset, SectionKind)) { + Context.getWarningHandler()(std::move(ExtractErr)); return nullptr; + } if (!IndexEntry && IsDWO) { const DWARFUnitIndex &Index = getDWARFUnitIndex( Context, Header.isTypeUnit() ? DW_SECT_EXT_TYPES : DW_SECT_INFO); @@ -244,10 +247,10 @@ Expected<uint64_t> DWARFUnit::getStringOffsetSectionItem(uint32_t Index) const { return DA.getRelocatedValue(ItemSize, &Offset); } -bool DWARFUnitHeader::extract(DWARFContext &Context, - const DWARFDataExtractor &debug_info, - uint64_t *offset_ptr, - DWARFSectionKind SectionKind) { +Error DWARFUnitHeader::extract(DWARFContext &Context, + const DWARFDataExtractor &debug_info, + uint64_t *offset_ptr, + DWARFSectionKind SectionKind) { Offset = *offset_ptr; Error Err = Error::success(); IndexEntry = nullptr; @@ -277,72 +280,58 @@ bool DWARFUnitHeader::extract(DWARFContext &Context, } else if (UnitType == DW_UT_split_compile || UnitType == DW_UT_skeleton) DWOId = debug_info.getU64(offset_ptr, &Err); - if (Err) { - Context.getWarningHandler()(joinErrors( + if (Err) + return joinErrors( createStringError( errc::invalid_argument, "DWARF unit at 0x%8.8" PRIx64 " cannot be parsed:", Offset), - std::move(Err))); - return false; - } + std::move(Err)); // Header fields all parsed, capture the size of this unit header. assert(*offset_ptr - Offset <= 255 && "unexpected header size"); Size = uint8_t(*offset_ptr - Offset); uint64_t NextCUOffset = Offset + getUnitLengthFieldByteSize() + getLength(); - if (!debug_info.isValidOffset(getNextUnitOffset() - 1)) { - Context.getWarningHandler()( - createStringError(errc::invalid_argument, - "DWARF unit from offset 0x%8.8" PRIx64 " incl. " - "to offset 0x%8.8" PRIx64 " excl. " - "extends past section size 0x%8.8zx", - Offset, NextCUOffset, debug_info.size())); - return false; - } + if (!debug_info.isValidOffset(getNextUnitOffset() - 1)) + return createStringError(errc::invalid_argument, + "DWARF unit from offset 0x%8.8" PRIx64 " incl. " + "to offset 0x%8.8" PRIx64 " excl. " + "extends past section size 0x%8.8zx", + Offset, NextCUOffset, debug_info.size()); - if (!DWARFContext::isSupportedVersion(getVersion())) { - Context.getWarningHandler()(createStringError( + if (!DWARFContext::isSupportedVersion(getVersion())) + return createStringError( errc::invalid_argument, "DWARF unit at offset 0x%8.8" PRIx64 " " "has unsupported version %" PRIu16 ", supported are 2-%u", - Offset, getVersion(), DWARFContext::getMaxSupportedVersion())); - return false; - } + Offset, getVersion(), DWARFContext::getMaxSupportedVersion()); // Type offset is unit-relative; should be after the header and before // the end of the current unit. - if (isTypeUnit() && TypeOffset < Size) { - Context.getWarningHandler()( - createStringError(errc::invalid_argument, - "DWARF type unit at offset " - "0x%8.8" PRIx64 " " - "has its relocated type_offset 0x%8.8" PRIx64 " " - "pointing inside the header", - Offset, Offset + TypeOffset)); - return false; - } - if (isTypeUnit() && - TypeOffset >= getUnitLengthFieldByteSize() + getLength()) { - Context.getWarningHandler()(createStringError( + if (isTypeUnit() && TypeOffset < Size) + return createStringError(errc::invalid_argument, + "DWARF type unit at offset " + "0x%8.8" PRIx64 " " + "has its relocated type_offset 0x%8.8" PRIx64 " " + "pointing inside the header", + Offset, Offset + TypeOffset); + + if (isTypeUnit() && TypeOffset >= getUnitLengthFieldByteSize() + getLength()) + return createStringError( errc::invalid_argument, "DWARF type unit from offset 0x%8.8" PRIx64 " incl. " "to offset 0x%8.8" PRIx64 " excl. has its " "relocated type_offset 0x%8.8" PRIx64 " pointing past the unit end", - Offset, NextCUOffset, Offset + TypeOffset)); - return false; - } + Offset, NextCUOffset, Offset + TypeOffset); if (Error SizeErr = DWARFContext::checkAddressSizeSupported( getAddressByteSize(), errc::invalid_argument, - "DWARF unit at offset 0x%8.8" PRIx64, Offset)) { - Context.getWarningHandler()(std::move(SizeErr)); - return false; - } + "DWARF unit at offset 0x%8.8" PRIx64, Offset)) + return SizeErr; // Keep track of the highest DWARF version we encounter across all units. Context.setMaxVersionIfGreater(getVersion()); - return true; + return Error::success(); } bool DWARFUnitHeader::applyIndexEntry(const DWARFUnitIndex::Entry *Entry) { @@ -784,7 +773,7 @@ void DWARFUnit::updateVariableDieMap(DWARFDie Die) { for (const DWARFLocationExpression &Location : *Locations) { uint8_t AddressSize = getAddressByteSize(); - DataExtractor Data(Location.Expr, /*IsLittleEndian=*/true, AddressSize); + DataExtractor Data(Location.Expr, isLittleEndian(), AddressSize); DWARFExpression Expr(Data, AddressSize); auto It = Expr.begin(); if (It == Expr.end()) @@ -828,7 +817,7 @@ void DWARFUnit::updateVariableDieMap(DWARFDie Die) { // no type), then we use a size of one to still allow symbolization of the // exact address. uint64_t GVSize = 1; - if (DWARFDie BaseType = Die.getAttributeValueAsReferencedDie(DW_AT_type)) + if (Die.getAttributeValueAsReferencedDie(DW_AT_type)) if (std::optional<uint64_t> Size = Die.getTypeSize(getAddressByteSize())) GVSize = *Size; diff --git a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp index 58900e1e80cb..43ed60d7f977 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp @@ -1351,12 +1351,34 @@ DWARFVerifier::verifyNameIndexAbbrevs(const DWARFDebugNames::NameIndex &NI) { return NumErrors; } -static SmallVector<StringRef, 2> getNames(const DWARFDie &DIE, - bool IncludeLinkageName = true) { - SmallVector<StringRef, 2> Result; - if (const char *Str = DIE.getShortName()) - Result.emplace_back(Str); - else if (DIE.getTag() == dwarf::DW_TAG_namespace) +static SmallVector<std::string, 3> getNames(const DWARFDie &DIE, + bool IncludeStrippedTemplateNames, + bool IncludeObjCNames = true, + bool IncludeLinkageName = true) { + SmallVector<std::string, 3> Result; + if (const char *Str = DIE.getShortName()) { + StringRef Name(Str); + Result.emplace_back(Name); + if (IncludeStrippedTemplateNames) { + if (std::optional<StringRef> StrippedName = + StripTemplateParameters(Result.back())) + // Convert to std::string and push; emplacing the StringRef may trigger + // a vector resize which may destroy the StringRef memory. + Result.push_back(StrippedName->str()); + } + + if (IncludeObjCNames) { + if (std::optional<ObjCSelectorNames> ObjCNames = + getObjCNamesIfSelector(Name)) { + Result.emplace_back(ObjCNames->ClassName); + Result.emplace_back(ObjCNames->Selector); + if (ObjCNames->ClassNameNoCategory) + Result.emplace_back(*ObjCNames->ClassNameNoCategory); + if (ObjCNames->MethodNameNoCategory) + Result.push_back(std::move(*ObjCNames->MethodNameNoCategory)); + } + } + } else if (DIE.getTag() == dwarf::DW_TAG_namespace) Result.emplace_back("(anonymous namespace)"); if (IncludeLinkageName) { @@ -1423,7 +1445,12 @@ unsigned DWARFVerifier::verifyNameIndexEntries( ++NumErrors; } - auto EntryNames = getNames(DIE); + // We allow an extra name for functions: their name without any template + // parameters. + auto IncludeStrippedTemplateNames = + DIE.getTag() == DW_TAG_subprogram || + DIE.getTag() == DW_TAG_inlined_subroutine; + auto EntryNames = getNames(DIE, IncludeStrippedTemplateNames); if (!is_contained(EntryNames, Str)) { error() << formatv("Name Index @ {0:x}: Entry @ {1:x}: mismatched Name " "of DIE @ {2:x}: index - {3}; debug_info - {4}.\n", @@ -1496,7 +1523,12 @@ unsigned DWARFVerifier::verifyNameIndexCompleteness( // the linkage name." auto IncludeLinkageName = Die.getTag() == DW_TAG_subprogram || Die.getTag() == DW_TAG_inlined_subroutine; - auto EntryNames = getNames(Die, IncludeLinkageName); + // We *allow* stripped template names / ObjectiveC names as extra entries into + // the table, but we don't *require* them to pass the completeness test. + auto IncludeStrippedTemplateNames = false; + auto IncludeObjCNames = false; + auto EntryNames = getNames(Die, IncludeStrippedTemplateNames, + IncludeObjCNames, IncludeLinkageName); if (EntryNames.empty()) return 0; diff --git a/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp b/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp index d266960ae302..0b225376349e 100644 --- a/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp +++ b/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp @@ -65,10 +65,10 @@ struct llvm::gsym::CUInfo { /// the first client that asks for a compile unit file index will end up /// doing the conversion, and subsequent clients will get the cached GSYM /// index. - uint32_t DWARFToGSYMFileIndex(GsymCreator &Gsym, uint32_t DwarfFileIdx) { - if (!LineTable) - return 0; - assert(DwarfFileIdx < FileCache.size()); + std::optional<uint32_t> DWARFToGSYMFileIndex(GsymCreator &Gsym, + uint32_t DwarfFileIdx) { + if (!LineTable || DwarfFileIdx >= FileCache.size()) + return std::nullopt; uint32_t &GsymFileIdx = FileCache[DwarfFileIdx]; if (GsymFileIdx != UINT32_MAX) return GsymFileIdx; @@ -132,11 +132,11 @@ static DWARFDie GetParentDeclContextDIE(DWARFDie &Die) { static std::optional<uint32_t> getQualifiedNameIndex(DWARFDie &Die, uint64_t Language, GsymCreator &Gsym) { // If the dwarf has mangled name, use mangled name - if (auto LinkageName = - dwarf::toString(Die.findRecursively({dwarf::DW_AT_MIPS_linkage_name, - dwarf::DW_AT_linkage_name}), - nullptr)) - return Gsym.insertString(LinkageName, /* Copy */ false); + if (auto LinkageName = Die.getLinkageName()) { + // We have seen cases were linkage name is actually empty. + if (strlen(LinkageName) > 0) + return Gsym.insertString(LinkageName, /* Copy */ false); + } StringRef ShortName(Die.getName(DINameKind::ShortName)); if (ShortName.empty()) @@ -156,7 +156,7 @@ getQualifiedNameIndex(DWARFDie &Die, uint64_t Language, GsymCreator &Gsym) { // Some GCC optimizations create functions with names ending with .isra.<num> // or .part.<num> and those names are just DW_AT_name, not DW_AT_linkage_name // If it looks like it could be the case, don't add any prefix - if (ShortName.startswith("_Z") && + if (ShortName.starts_with("_Z") && (ShortName.contains(".isra.") || ShortName.contains(".part."))) return Gsym.insertString(ShortName, /* Copy */ false); @@ -205,9 +205,21 @@ static bool hasInlineInfo(DWARFDie Die, uint32_t Depth) { return false; } -static void parseInlineInfo(GsymCreator &Gsym, CUInfo &CUI, DWARFDie Die, - uint32_t Depth, FunctionInfo &FI, - InlineInfo &parent) { +static AddressRanges +ConvertDWARFRanges(const DWARFAddressRangesVector &DwarfRanges) { + AddressRanges Ranges; + for (const DWARFAddressRange &DwarfRange : DwarfRanges) { + if (DwarfRange.LowPC < DwarfRange.HighPC) + Ranges.insert({DwarfRange.LowPC, DwarfRange.HighPC}); + } + return Ranges; +} + +static void parseInlineInfo(GsymCreator &Gsym, raw_ostream *Log, CUInfo &CUI, + DWARFDie Die, uint32_t Depth, FunctionInfo &FI, + InlineInfo &Parent, + const AddressRanges &AllParentRanges, + bool &WarnIfEmpty) { if (!hasInlineInfo(Die, Depth)) return; @@ -215,39 +227,80 @@ static void parseInlineInfo(GsymCreator &Gsym, CUInfo &CUI, DWARFDie Die, if (Tag == dwarf::DW_TAG_inlined_subroutine) { // create new InlineInfo and append to parent.children InlineInfo II; - DWARFAddressRange FuncRange = - DWARFAddressRange(FI.startAddress(), FI.endAddress()); + AddressRanges AllInlineRanges; Expected<DWARFAddressRangesVector> RangesOrError = Die.getAddressRanges(); if (RangesOrError) { - for (const DWARFAddressRange &Range : RangesOrError.get()) { - // Check that the inlined function is within the range of the function - // info, it might not be in case of split functions - if (FuncRange.LowPC <= Range.LowPC && Range.HighPC <= FuncRange.HighPC) - II.Ranges.insert(AddressRange(Range.LowPC, Range.HighPC)); + AllInlineRanges = ConvertDWARFRanges(RangesOrError.get()); + uint32_t EmptyCount = 0; + for (const AddressRange &InlineRange : AllInlineRanges) { + // Check for empty inline range in case inline function was outlined + // or has not code + if (InlineRange.empty()) { + ++EmptyCount; + } else { + if (Parent.Ranges.contains(InlineRange)) { + II.Ranges.insert(InlineRange); + } else { + // Only warn if the current inline range is not within any of all + // of the parent ranges. If we have a DW_TAG_subpgram with multiple + // ranges we will emit a FunctionInfo for each range of that + // function that only emits information within the current range, + // so we only want to emit an error if the DWARF has issues, not + // when a range currently just isn't in the range we are currently + // parsing for. + if (AllParentRanges.contains(InlineRange)) { + WarnIfEmpty = false; + } else if (Log) { + *Log << "error: inlined function DIE at " + << HEX32(Die.getOffset()) << " has a range [" + << HEX64(InlineRange.start()) << " - " + << HEX64(InlineRange.end()) << ") that isn't contained in " + << "any parent address ranges, this inline range will be " + "removed.\n"; + } + } + } } + // If we have all empty ranges for the inlines, then don't warn if we + // have an empty InlineInfo at the top level as all inline functions + // were elided. + if (EmptyCount == AllInlineRanges.size()) + WarnIfEmpty = false; } if (II.Ranges.empty()) return; if (auto NameIndex = getQualifiedNameIndex(Die, CUI.Language, Gsym)) II.Name = *NameIndex; - II.CallFile = CUI.DWARFToGSYMFileIndex( - Gsym, dwarf::toUnsigned(Die.find(dwarf::DW_AT_call_file), 0)); - II.CallLine = dwarf::toUnsigned(Die.find(dwarf::DW_AT_call_line), 0); - // parse all children and append to parent - for (DWARFDie ChildDie : Die.children()) - parseInlineInfo(Gsym, CUI, ChildDie, Depth + 1, FI, II); - parent.Children.emplace_back(std::move(II)); + const uint64_t DwarfFileIdx = dwarf::toUnsigned( + Die.findRecursively(dwarf::DW_AT_call_file), UINT32_MAX); + std::optional<uint32_t> OptGSymFileIdx = + CUI.DWARFToGSYMFileIndex(Gsym, DwarfFileIdx); + if (OptGSymFileIdx) { + II.CallFile = OptGSymFileIdx.value(); + II.CallLine = dwarf::toUnsigned(Die.find(dwarf::DW_AT_call_line), 0); + // parse all children and append to parent + for (DWARFDie ChildDie : Die.children()) + parseInlineInfo(Gsym, Log, CUI, ChildDie, Depth + 1, FI, II, + AllInlineRanges, WarnIfEmpty); + Parent.Children.emplace_back(std::move(II)); + } else if (Log) { + *Log << "error: inlined function DIE at " << HEX32(Die.getOffset()) + << " has an invalid file index " << DwarfFileIdx + << " in its DW_AT_call_file attribute, this inline entry and all " + << "children will be removed.\n"; + } return; } if (Tag == dwarf::DW_TAG_subprogram || Tag == dwarf::DW_TAG_lexical_block) { // skip this Die and just recurse down for (DWARFDie ChildDie : Die.children()) - parseInlineInfo(Gsym, CUI, ChildDie, Depth + 1, FI, parent); + parseInlineInfo(Gsym, Log, CUI, ChildDie, Depth + 1, FI, Parent, + AllParentRanges, WarnIfEmpty); } } -static void convertFunctionLineTable(raw_ostream &Log, CUInfo &CUI, +static void convertFunctionLineTable(raw_ostream *Log, CUInfo &CUI, DWARFDie Die, GsymCreator &Gsym, FunctionInfo &FI) { std::vector<uint32_t> RowVector; @@ -263,8 +316,20 @@ static void convertFunctionLineTable(raw_ostream &Log, CUInfo &CUI, // the DW_AT_decl_file an d DW_AT_decl_line if we have both attributes. std::string FilePath = Die.getDeclFile( DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath); - if (FilePath.empty()) + if (FilePath.empty()) { + // If we had a DW_AT_decl_file, but got no file then we need to emit a + // warning. + if (Log) { + const uint64_t DwarfFileIdx = dwarf::toUnsigned( + Die.findRecursively(dwarf::DW_AT_decl_file), UINT32_MAX); + *Log << "error: function DIE at " << HEX32(Die.getOffset()) + << " has an invalid file index " << DwarfFileIdx + << " in its DW_AT_decl_file attribute, unable to create a single " + << "line entry from the DW_AT_decl_file/DW_AT_decl_line " + << "attributes.\n"; + } return; + } if (auto Line = dwarf::toUnsigned(Die.findRecursively({dwarf::DW_AT_decl_line}))) { LineEntry LE(StartAddress, Gsym.insertFile(FilePath), *Line); @@ -279,7 +344,20 @@ static void convertFunctionLineTable(raw_ostream &Log, CUInfo &CUI, for (uint32_t RowIndex : RowVector) { // Take file number and line/column from the row. const DWARFDebugLine::Row &Row = CUI.LineTable->Rows[RowIndex]; - const uint32_t FileIdx = CUI.DWARFToGSYMFileIndex(Gsym, Row.File); + std::optional<uint32_t> OptFileIdx = + CUI.DWARFToGSYMFileIndex(Gsym, Row.File); + if (!OptFileIdx) { + if (Log) { + *Log << "error: function DIE at " << HEX32(Die.getOffset()) << " has " + << "a line entry with invalid DWARF file index, this entry will " + << "be removed:\n"; + Row.dumpTableHeader(*Log, /*Indent=*/0); + Row.dump(*Log); + *Log << "\n"; + } + continue; + } + const uint32_t FileIdx = OptFileIdx.value(); uint64_t RowAddress = Row.Address.Address; // Watch out for a RowAddress that is in the middle of a line table entry // in the DWARF. If we pass an address in between two line table entries @@ -289,10 +367,12 @@ static void convertFunctionLineTable(raw_ostream &Log, CUInfo &CUI, // an error, but not worth stopping the creation of the GSYM. if (!FI.Range.contains(RowAddress)) { if (RowAddress < FI.Range.start()) { - Log << "error: DIE has a start address whose LowPC is between the " - "line table Row[" << RowIndex << "] with address " - << HEX64(RowAddress) << " and the next one.\n"; - Die.dump(Log, 0, DIDumpOptions::getForSingleDIE()); + if (Log) { + *Log << "error: DIE has a start address whose LowPC is between the " + "line table Row[" << RowIndex << "] with address " + << HEX64(RowAddress) << " and the next one.\n"; + Die.dump(*Log, 0, DIDumpOptions::getForSingleDIE()); + } RowAddress = FI.Range.start(); } else { continue; @@ -302,25 +382,25 @@ static void convertFunctionLineTable(raw_ostream &Log, CUInfo &CUI, LineEntry LE(RowAddress, FileIdx, Row.Line); if (RowIndex != RowVector[0] && Row.Address < PrevRow.Address) { // We have seen full duplicate line tables for functions in some - // DWARF files. Watch for those here by checking the the last + // DWARF files. Watch for those here by checking the last // row was the function's end address (HighPC) and that the // current line table entry's address is the same as the first // line entry we already have in our "function_info.Lines". If // so break out after printing a warning. auto FirstLE = FI.OptLineTable->first(); if (FirstLE && *FirstLE == LE) { - if (!Gsym.isQuiet()) { - Log << "warning: duplicate line table detected for DIE:\n"; - Die.dump(Log, 0, DIDumpOptions::getForSingleDIE()); + if (Log && !Gsym.isQuiet()) { + *Log << "warning: duplicate line table detected for DIE:\n"; + Die.dump(*Log, 0, DIDumpOptions::getForSingleDIE()); } } else { - // Print out (ignore if os == nulls as this is expensive) - Log << "error: line table has addresses that do not " - << "monotonically increase:\n"; - for (uint32_t RowIndex2 : RowVector) { - CUI.LineTable->Rows[RowIndex2].dump(Log); + if (Log) { + *Log << "error: line table has addresses that do not " + << "monotonically increase:\n"; + for (uint32_t RowIndex2 : RowVector) + CUI.LineTable->Rows[RowIndex2].dump(*Log); + Die.dump(*Log, 0, DIDumpOptions::getForSingleDIE()); } - Die.dump(Log, 0, DIDumpOptions::getForSingleDIE()); } break; } @@ -349,7 +429,7 @@ static void convertFunctionLineTable(raw_ostream &Log, CUInfo &CUI, FI.OptLineTable = std::nullopt; } -void DwarfTransformer::handleDie(raw_ostream &OS, CUInfo &CUI, DWARFDie Die) { +void DwarfTransformer::handleDie(raw_ostream *OS, CUInfo &CUI, DWARFDie Die) { switch (Die.getTag()) { case dwarf::DW_TAG_subprogram: { Expected<DWARFAddressRangesVector> RangesOrError = Die.getAddressRanges(); @@ -362,11 +442,20 @@ void DwarfTransformer::handleDie(raw_ostream &OS, CUInfo &CUI, DWARFDie Die) { break; auto NameIndex = getQualifiedNameIndex(Die, CUI.Language, Gsym); if (!NameIndex) { - OS << "error: function at " << HEX64(Die.getOffset()) - << " has no name\n "; - Die.dump(OS, 0, DIDumpOptions::getForSingleDIE()); + if (OS) { + *OS << "error: function at " << HEX64(Die.getOffset()) + << " has no name\n "; + Die.dump(*OS, 0, DIDumpOptions::getForSingleDIE()); + } break; } + // All ranges for the subprogram DIE in case it has multiple. We need to + // pass this down into parseInlineInfo so we don't warn about inline + // ranges that are not in the current subrange of a function when they + // actually are in another subgrange. We do this because when a function + // has discontiguos ranges, we create multiple function entries with only + // the info for that range contained inside of it. + AddressRanges AllSubprogramRanges = ConvertDWARFRanges(Ranges); // Create a function_info for each range for (const DWARFAddressRange &Range : Ranges) { @@ -393,11 +482,13 @@ void DwarfTransformer::handleDie(raw_ostream &OS, CUInfo &CUI, DWARFDie Die) { if (Range.LowPC != 0) { if (!Gsym.isQuiet()) { // Unexpected invalid address, emit a warning - OS << "warning: DIE has an address range whose start address is " - "not in any executable sections (" - << *Gsym.GetValidTextRanges() - << ") and will not be processed:\n"; - Die.dump(OS, 0, DIDumpOptions::getForSingleDIE()); + if (OS) { + *OS << "warning: DIE has an address range whose start address " + "is not in any executable sections (" + << *Gsym.GetValidTextRanges() + << ") and will not be processed:\n"; + Die.dump(*OS, 0, DIDumpOptions::getForSingleDIE()); + } } } break; @@ -406,14 +497,33 @@ void DwarfTransformer::handleDie(raw_ostream &OS, CUInfo &CUI, DWARFDie Die) { FunctionInfo FI; FI.Range = {Range.LowPC, Range.HighPC}; FI.Name = *NameIndex; - if (CUI.LineTable) { + if (CUI.LineTable) convertFunctionLineTable(OS, CUI, Die, Gsym, FI); - } + if (hasInlineInfo(Die, 0)) { FI.Inline = InlineInfo(); FI.Inline->Name = *NameIndex; FI.Inline->Ranges.insert(FI.Range); - parseInlineInfo(Gsym, CUI, Die, 0, FI, *FI.Inline); + bool WarnIfEmpty = true; + parseInlineInfo(Gsym, OS, CUI, Die, 0, FI, *FI.Inline, + AllSubprogramRanges, WarnIfEmpty); + // Make sure we at least got some valid inline info other than just + // the top level function. If we didn't then remove the inline info + // from the function info. We have seen cases where LTO tries to modify + // the DWARF for functions and it messes up the address ranges for + // the inline functions so it is no longer valid. + // + // By checking if there are any valid children on the top level inline + // information object, we will know if we got anything valid from the + // debug info. + if (FI.Inline->Children.empty()) { + if (WarnIfEmpty && OS && !Gsym.isQuiet()) { + *OS << "warning: DIE contains inline function information that has " + "no valid ranges, removing inline information:\n"; + Die.dump(*OS, 0, DIDumpOptions::getForSingleDIE()); + } + FI.Inline = std::nullopt; + } } Gsym.addFunctionInfo(std::move(FI)); } @@ -425,18 +535,18 @@ void DwarfTransformer::handleDie(raw_ostream &OS, CUInfo &CUI, DWARFDie Die) { handleDie(OS, CUI, ChildDie); } -Error DwarfTransformer::convert(uint32_t NumThreads) { +Error DwarfTransformer::convert(uint32_t NumThreads, raw_ostream *OS) { size_t NumBefore = Gsym.getNumFunctionInfos(); auto getDie = [&](DWARFUnit &DwarfUnit) -> DWARFDie { DWARFDie ReturnDie = DwarfUnit.getUnitDIE(false); - if (std::optional<uint64_t> DWOId = DwarfUnit.getDWOId()) { + if (DwarfUnit.getDWOId()) { DWARFUnit *DWOCU = DwarfUnit.getNonSkeletonUnitDIE(false).getDwarfUnit(); - if (!DWOCU->isDWOUnit()) { + if (OS && !DWOCU->isDWOUnit()) { std::string DWOName = dwarf::toString( DwarfUnit.getUnitDIE().find( {dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}), ""); - Log << "warning: Unable to retrieve DWO .debug_info section for " + *OS << "warning: Unable to retrieve DWO .debug_info section for " << DWOName << "\n"; } else { ReturnDie = DWOCU->getUnitDIE(false); @@ -450,7 +560,7 @@ Error DwarfTransformer::convert(uint32_t NumThreads) { for (const auto &CU : DICtx.compile_units()) { DWARFDie Die = getDie(*CU); CUInfo CUI(DICtx, dyn_cast<DWARFCompileUnit>(CU.get())); - handleDie(Log, CUI, Die); + handleDie(OS, CUI, Die); } } else { // LLVM Dwarf parser is not thread-safe and we need to parse all DWARF up @@ -476,15 +586,15 @@ Error DwarfTransformer::convert(uint32_t NumThreads) { DWARFDie Die = getDie(*CU); if (Die) { CUInfo CUI(DICtx, dyn_cast<DWARFCompileUnit>(CU.get())); - pool.async([this, CUI, &LogMutex, Die]() mutable { + pool.async([this, CUI, &LogMutex, OS, Die]() mutable { std::string ThreadLogStorage; raw_string_ostream ThreadOS(ThreadLogStorage); - handleDie(ThreadOS, CUI, Die); + handleDie(OS ? &ThreadOS: nullptr, CUI, Die); ThreadOS.flush(); - if (!ThreadLogStorage.empty()) { + if (OS && !ThreadLogStorage.empty()) { // Print ThreadLogStorage lines into an actual stream under a lock std::lock_guard<std::mutex> guard(LogMutex); - Log << ThreadLogStorage; + *OS << ThreadLogStorage; } }); } @@ -492,11 +602,12 @@ Error DwarfTransformer::convert(uint32_t NumThreads) { pool.wait(); } size_t FunctionsAddedCount = Gsym.getNumFunctionInfos() - NumBefore; - Log << "Loaded " << FunctionsAddedCount << " functions from DWARF.\n"; + if (OS) + *OS << "Loaded " << FunctionsAddedCount << " functions from DWARF.\n"; return Error::success(); } -llvm::Error DwarfTransformer::verify(StringRef GsymPath) { +llvm::Error DwarfTransformer::verify(StringRef GsymPath, raw_ostream &Log) { Log << "Verifying GSYM file \"" << GsymPath << "\":\n"; auto Gsym = GsymReader::openFile(GsymPath); diff --git a/llvm/lib/DebugInfo/GSYM/FunctionInfo.cpp b/llvm/lib/DebugInfo/GSYM/FunctionInfo.cpp index 145a43d3b381..07303d551af5 100644 --- a/llvm/lib/DebugInfo/GSYM/FunctionInfo.cpp +++ b/llvm/lib/DebugInfo/GSYM/FunctionInfo.cpp @@ -101,7 +101,7 @@ uint64_t FunctionInfo::cacheEncoding() { if (!isValid()) return 0; raw_svector_ostream OutStrm(EncodingCache); - FileWriter FW(OutStrm, support::endian::system_endianness()); + FileWriter FW(OutStrm, llvm::endianness::native); llvm::Expected<uint64_t> Result = encode(FW); if (!Result) { EncodingCache.clear(); @@ -123,7 +123,7 @@ llvm::Expected<uint64_t> FunctionInfo::encode(FileWriter &Out) const { // precompute exactly how big FunctionInfo objects encode into so we can // accurately make segments of a specific size. if (!EncodingCache.empty() && - support::endian::system_endianness() == Out.getByteOrder()) { + llvm::endianness::native == Out.getByteOrder()) { // We already encoded this object, just write out the bytes. Out.writeData(llvm::ArrayRef<uint8_t>((const uint8_t *)EncodingCache.data(), EncodingCache.size())); diff --git a/llvm/lib/DebugInfo/GSYM/GsymCreator.cpp b/llvm/lib/DebugInfo/GSYM/GsymCreator.cpp index 60b6dbc6a12d..ee7b0efba5ea 100644 --- a/llvm/lib/DebugInfo/GSYM/GsymCreator.cpp +++ b/llvm/lib/DebugInfo/GSYM/GsymCreator.cpp @@ -61,9 +61,7 @@ uint32_t GsymCreator::copyFile(const GsymCreator &SrcGC, uint32_t FileIdx) { return insertFileEntry(DstFE); } - -llvm::Error GsymCreator::save(StringRef Path, - llvm::support::endianness ByteOrder, +llvm::Error GsymCreator::save(StringRef Path, llvm::endianness ByteOrder, std::optional<uint64_t> SegmentSize) const { if (SegmentSize) return saveSegments(Path, ByteOrder, *SegmentSize); @@ -187,35 +185,12 @@ llvm::Error GsymCreator::encode(FileWriter &O) const { return ErrorSuccess(); } -// Similar to std::remove_if, but the predicate is binary and it is passed both -// the previous and the current element. -template <class ForwardIt, class BinaryPredicate> -static ForwardIt removeIfBinary(ForwardIt FirstIt, ForwardIt LastIt, - BinaryPredicate Pred) { - if (FirstIt != LastIt) { - auto PrevIt = FirstIt++; - FirstIt = std::find_if(FirstIt, LastIt, [&](const auto &Curr) { - return Pred(*PrevIt++, Curr); - }); - if (FirstIt != LastIt) - for (ForwardIt CurrIt = FirstIt; ++CurrIt != LastIt;) - if (!Pred(*PrevIt, *CurrIt)) { - PrevIt = FirstIt; - *FirstIt++ = std::move(*CurrIt); - } - } - return FirstIt; -} - llvm::Error GsymCreator::finalize(llvm::raw_ostream &OS) { std::lock_guard<std::mutex> Guard(Mutex); if (Finalized) return createStringError(std::errc::invalid_argument, "already finalized"); Finalized = true; - // Sort function infos so we can emit sorted functions. - llvm::sort(Funcs); - // Don't let the string table indexes change by finalizing in order. StrTab.finalizeInOrder(); @@ -239,83 +214,85 @@ llvm::Error GsymCreator::finalize(llvm::raw_ostream &OS) { // Note that in case of (b), we cannot include Y in the result because then // we wouldn't find any function for range (end of Y, end of X) // with binary search - auto NumBefore = Funcs.size(); - Funcs.erase( - removeIfBinary(Funcs.begin(), Funcs.end(), - [&](const auto &Prev, const auto &Curr) { - // Empty ranges won't intersect, but we still need to - // catch the case where we have multiple symbols at the - // same address and coalesce them. - const bool ranges_equal = Prev.Range == Curr.Range; - if (ranges_equal || Prev.Range.intersects(Curr.Range)) { - // Overlapping ranges or empty identical ranges. - if (ranges_equal) { - // Same address range. Check if one is from debug - // info and the other is from a symbol table. If - // so, then keep the one with debug info. Our - // sorting guarantees that entries with matching - // address ranges that have debug info are last in - // the sort. - if (Prev == Curr) { - // FunctionInfo entries match exactly (range, - // lines, inlines) - - // We used to output a warning here, but this was - // so frequent on some binaries, in particular - // when those were built with GCC, that it slowed - // down processing extremely. - return true; - } else { - if (!Prev.hasRichInfo() && Curr.hasRichInfo()) { - // Same address range, one with no debug info - // (symbol) and the next with debug info. Keep - // the latter. - return true; - } else { - if (!Quiet) { - OS << "warning: same address range contains " - "different debug " - << "info. Removing:\n" - << Prev << "\nIn favor of this one:\n" - << Curr << "\n"; - } - return true; - } - } - } else { - if (!Quiet) { // print warnings about overlaps - OS << "warning: function ranges overlap:\n" - << Prev << "\n" - << Curr << "\n"; - } - } - } else if (Prev.Range.size() == 0 && - Curr.Range.contains(Prev.Range.start())) { - if (!Quiet) { - OS << "warning: removing symbol:\n" - << Prev << "\nKeeping:\n" - << Curr << "\n"; - } - return true; - } - return false; - }), - Funcs.end()); - - // If our last function info entry doesn't have a size and if we have valid - // text ranges, we should set the size of the last entry since any search for - // a high address might match our last entry. By fixing up this size, we can - // help ensure we don't cause lookups to always return the last symbol that - // has no size when doing lookups. - if (!Funcs.empty() && Funcs.back().Range.size() == 0 && ValidTextRanges) { - if (auto Range = - ValidTextRanges->getRangeThatContains(Funcs.back().Range.start())) { - Funcs.back().Range = {Funcs.back().Range.start(), Range->end()}; + const auto NumBefore = Funcs.size(); + // Only sort and unique if this isn't a segment. If this is a segment we + // already finalized the main GsymCreator with all of the function infos + // and then the already sorted and uniqued function infos were added to this + // object. + if (!IsSegment) { + if (NumBefore > 1) { + // Sort function infos so we can emit sorted functions. + llvm::sort(Funcs); + std::vector<FunctionInfo> FinalizedFuncs; + FinalizedFuncs.reserve(Funcs.size()); + FinalizedFuncs.emplace_back(std::move(Funcs.front())); + for (size_t Idx=1; Idx < NumBefore; ++Idx) { + FunctionInfo &Prev = FinalizedFuncs.back(); + FunctionInfo &Curr = Funcs[Idx]; + // Empty ranges won't intersect, but we still need to + // catch the case where we have multiple symbols at the + // same address and coalesce them. + const bool ranges_equal = Prev.Range == Curr.Range; + if (ranges_equal || Prev.Range.intersects(Curr.Range)) { + // Overlapping ranges or empty identical ranges. + if (ranges_equal) { + // Same address range. Check if one is from debug + // info and the other is from a symbol table. If + // so, then keep the one with debug info. Our + // sorting guarantees that entries with matching + // address ranges that have debug info are last in + // the sort. + if (!(Prev == Curr)) { + if (Prev.hasRichInfo() && Curr.hasRichInfo()) { + if (!Quiet) { + OS << "warning: same address range contains " + "different debug " + << "info. Removing:\n" + << Prev << "\nIn favor of this one:\n" + << Curr << "\n"; + } + } + // We want to swap the current entry with the previous since + // later entries with the same range always have more debug info + // or different debug info. + std::swap(Prev, Curr); + } + } else { + if (!Quiet) { // print warnings about overlaps + OS << "warning: function ranges overlap:\n" + << Prev << "\n" + << Curr << "\n"; + } + FinalizedFuncs.emplace_back(std::move(Curr)); + } + } else { + if (Prev.Range.size() == 0 && Curr.Range.contains(Prev.Range.start())) { + // Symbols on macOS don't have address ranges, so if the range + // doesn't match and the size is zero, then we replace the empty + // symbol function info with the current one. + std::swap(Prev, Curr); + } else { + FinalizedFuncs.emplace_back(std::move(Curr)); + } + } + } + std::swap(Funcs, FinalizedFuncs); + } + // If our last function info entry doesn't have a size and if we have valid + // text ranges, we should set the size of the last entry since any search for + // a high address might match our last entry. By fixing up this size, we can + // help ensure we don't cause lookups to always return the last symbol that + // has no size when doing lookups. + if (!Funcs.empty() && Funcs.back().Range.size() == 0 && ValidTextRanges) { + if (auto Range = + ValidTextRanges->getRangeThatContains(Funcs.back().Range.start())) { + Funcs.back().Range = {Funcs.back().Range.start(), Range->end()}; + } } + OS << "Pruned " << NumBefore - Funcs.size() << " functions, ended with " + << Funcs.size() << " total\n"; } - OS << "Pruned " << NumBefore - Funcs.size() << " functions, ended with " - << Funcs.size() << " total\n"; return Error::success(); } @@ -355,7 +332,6 @@ uint32_t GsymCreator::insertString(StringRef S, bool Copy) { void GsymCreator::addFunctionInfo(FunctionInfo &&FI) { std::lock_guard<std::mutex> Guard(Mutex); - Ranges.insert(FI.Range); Funcs.emplace_back(std::move(FI)); } @@ -388,31 +364,24 @@ bool GsymCreator::IsValidTextAddress(uint64_t Addr) const { return true; // No valid text ranges has been set, so accept all ranges. } -bool GsymCreator::hasFunctionInfoForAddress(uint64_t Addr) const { - std::lock_guard<std::mutex> Guard(Mutex); - return Ranges.contains(Addr); -} - std::optional<uint64_t> GsymCreator::getFirstFunctionAddress() const { - if (Finalized && !Funcs.empty()) + // If we have finalized then Funcs are sorted. If we are a segment then + // Funcs will be sorted as well since function infos get added from an + // already finalized GsymCreator object where its functions were sorted and + // uniqued. + if ((Finalized || IsSegment) && !Funcs.empty()) return std::optional<uint64_t>(Funcs.front().startAddress()); - // This code gets used by the segmentation of GSYM files to help determine the - // size of the GSYM header while continually adding new FunctionInfo objects - // to this object, so we haven't finalized this object yet. - if (Ranges.empty()) - return std::nullopt; - return std::optional<uint64_t>(Ranges.begin()->start()); + return std::nullopt; } std::optional<uint64_t> GsymCreator::getLastFunctionAddress() const { - if (Finalized && !Funcs.empty()) + // If we have finalized then Funcs are sorted. If we are a segment then + // Funcs will be sorted as well since function infos get added from an + // already finalized GsymCreator object where its functions were sorted and + // uniqued. + if ((Finalized || IsSegment) && !Funcs.empty()) return std::optional<uint64_t>(Funcs.back().startAddress()); - // This code gets used by the segmentation of GSYM files to help determine the - // size of the GSYM header while continually adding new FunctionInfo objects - // to this object, so we haven't finalized this object yet. - if (Ranges.empty()) - return std::nullopt; - return std::optional<uint64_t>((Ranges.end() - 1)->end()); + return std::nullopt; } std::optional<uint64_t> GsymCreator::getBaseAddress() const { @@ -477,7 +446,6 @@ uint64_t GsymCreator::copyFunctionInfo(const GsymCreator &SrcGC, size_t FuncIdx) // this GsymCreator and then copy the function info and update the string // table offsets to match the new offsets. const FunctionInfo &SrcFI = SrcGC.Funcs[FuncIdx]; - Ranges.insert(SrcFI.Range); FunctionInfo DstFI; DstFI.Range = SrcFI.Range; @@ -503,12 +471,12 @@ uint64_t GsymCreator::copyFunctionInfo(const GsymCreator &SrcGC, size_t FuncIdx) fixupInlineInfo(SrcGC, *DstFI.Inline); } std::lock_guard<std::mutex> Guard(Mutex); - Funcs.push_back(DstFI); + Funcs.emplace_back(DstFI); return Funcs.back().cacheEncoding(); } llvm::Error GsymCreator::saveSegments(StringRef Path, - llvm::support::endianness ByteOrder, + llvm::endianness ByteOrder, uint64_t SegmentSize) const { if (SegmentSize == 0) return createStringError(std::errc::invalid_argument, @@ -551,6 +519,10 @@ GsymCreator::createSegment(uint64_t SegmentSize, size_t &FuncIdx) const { return std::unique_ptr<GsymCreator>(); std::unique_ptr<GsymCreator> GC(new GsymCreator(/*Quiet=*/true)); + + // Tell the creator that this is a segment. + GC->setIsSegment(); + // Set the base address if there is one. if (BaseAddress) GC->setBaseAddress(*BaseAddress); diff --git a/llvm/lib/DebugInfo/GSYM/GsymReader.cpp b/llvm/lib/DebugInfo/GSYM/GsymReader.cpp index 6afaeea8f598..4b1b35246617 100644 --- a/llvm/lib/DebugInfo/GSYM/GsymReader.cpp +++ b/llvm/lib/DebugInfo/GSYM/GsymReader.cpp @@ -23,11 +23,10 @@ using namespace llvm; using namespace gsym; -GsymReader::GsymReader(std::unique_ptr<MemoryBuffer> Buffer) : - MemBuffer(std::move(Buffer)), - Endian(support::endian::system_endianness()) {} +GsymReader::GsymReader(std::unique_ptr<MemoryBuffer> Buffer) + : MemBuffer(std::move(Buffer)), Endian(llvm::endianness::native) {} - GsymReader::GsymReader(GsymReader &&RHS) = default; +GsymReader::GsymReader(GsymReader &&RHS) = default; GsymReader::~GsymReader() = default; @@ -60,8 +59,7 @@ GsymReader::create(std::unique_ptr<MemoryBuffer> &MemBuffer) { llvm::Error GsymReader::parse() { - BinaryStreamReader FileData(MemBuffer->getBuffer(), - support::endian::system_endianness()); + BinaryStreamReader FileData(MemBuffer->getBuffer(), llvm::endianness::native); // Check for the magic bytes. This file format is designed to be mmap'ed // into a process and accessed as read only. This is done for performance // and efficiency for symbolicating and parsing GSYM data. @@ -69,14 +67,15 @@ GsymReader::parse() { return createStringError(std::errc::invalid_argument, "not enough data for a GSYM header"); - const auto HostByteOrder = support::endian::system_endianness(); + const auto HostByteOrder = llvm::endianness::native; switch (Hdr->Magic) { case GSYM_MAGIC: Endian = HostByteOrder; break; case GSYM_CIGAM: // This is a GSYM file, but not native endianness. - Endian = sys::IsBigEndianHost ? support::little : support::big; + Endian = sys::IsBigEndianHost ? llvm::endianness::little + : llvm::endianness::big; Swap.reset(new SwappedData); break; default: @@ -84,7 +83,7 @@ GsymReader::parse() { "not a GSYM file"); } - bool DataIsLittleEndian = HostByteOrder != support::little; + bool DataIsLittleEndian = HostByteOrder != llvm::endianness::little; // Read a correctly byte swapped header if we need to. if (Swap) { DataExtractor Data(MemBuffer->getBuffer(), DataIsLittleEndian, 4); @@ -254,41 +253,94 @@ GsymReader::getAddressIndex(const uint64_t Addr) const { } -llvm::Expected<FunctionInfo> GsymReader::getFunctionInfo(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 (std::optional<uint64_t> OptAddr = getAddress(*AddressIndex)) { - auto ExpectedFI = FunctionInfo::decode(Data, *OptAddr); - if (ExpectedFI) { - if (ExpectedFI->Range.contains(Addr) || ExpectedFI->Range.size() == 0) - return ExpectedFI; - return createStringError(std::errc::invalid_argument, - "address 0x%" PRIx64 " is not in GSYM", Addr); +llvm::Expected<DataExtractor> +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, - "failed to extract address[%" PRIu64 "]", - *AddressIndex); + "address 0x%" PRIx64 " is not in GSYM", Addr); +} + +llvm::Expected<DataExtractor> +GsymReader::getFunctionInfoDataAtIndex(uint64_t AddrIdx, + uint64_t &FuncStartAddr) const { + if (AddrIdx >= getNumAddresses()) + return createStringError(std::errc::invalid_argument, + "invalid address index %" PRIu64, AddrIdx); + const uint32_t AddrInfoOffset = AddrInfoOffsets[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%" PRIx32, + AddrInfoOffset); + std::optional<uint64_t> OptFuncStartAddr = getAddress(AddrIdx); + if (!OptFuncStartAddr) + return createStringError(std::errc::invalid_argument, + "failed to extract address[%" PRIu64 "]", AddrIdx); + FuncStartAddr = *OptFuncStartAddr; + return DataExtractor(Bytes, Endian == llvm::endianness::little, 4); +} + +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) 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 (std::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); + uint64_t FuncStartAddr = 0; + if (auto ExpectedData = getFunctionInfoDataForAddress(Addr, FuncStartAddr)) + return FunctionInfo::lookup(*ExpectedData, *this, FuncStartAddr, Addr); + else + return ExpectedData.takeError(); } void GsymReader::dump(raw_ostream &OS) { @@ -339,7 +391,7 @@ void GsymReader::dump(raw_ostream &OS) { for (uint32_t I = 0; I < Header.NumAddresses; ++I) { OS << "FunctionInfo @ " << HEX32(AddrInfoOffsets[I]) << ": "; - if (auto FI = getFunctionInfo(*getAddress(I))) + if (auto FI = getFunctionInfoAtIndex(I)) dump(OS, *FI); else logAllUnhandledErrors(FI.takeError(), OS, "FunctionInfo:"); diff --git a/llvm/lib/DebugInfo/GSYM/InlineInfo.cpp b/llvm/lib/DebugInfo/GSYM/InlineInfo.cpp index f775ab8fb65c..ecfb21501eda 100644 --- a/llvm/lib/DebugInfo/GSYM/InlineInfo.cpp +++ b/llvm/lib/DebugInfo/GSYM/InlineInfo.cpp @@ -264,3 +264,14 @@ llvm::Error InlineInfo::encode(FileWriter &O, uint64_t BaseAddr) const { } return Error::success(); } + +static uint64_t GetTotalNumChildren(const InlineInfo &II) { + uint64_t NumChildren = II.Children.size(); + for (const auto &Child : II.Children) + NumChildren += GetTotalNumChildren(Child); + return NumChildren; +} + +bool InlineInfo::operator<(const InlineInfo &RHS) const { + return GetTotalNumChildren(*this) < GetTotalNumChildren(RHS); +} diff --git a/llvm/lib/DebugInfo/GSYM/LineTable.cpp b/llvm/lib/DebugInfo/GSYM/LineTable.cpp index a49a3ba9bf2a..666d9f15f1b4 100644 --- a/llvm/lib/DebugInfo/GSYM/LineTable.cpp +++ b/llvm/lib/DebugInfo/GSYM/LineTable.cpp @@ -270,11 +270,6 @@ Expected<LineEntry> LineTable::lookup(DataExtractor &Data, uint64_t BaseAddr, ui if (Addr < Row.Addr) return false; // Stop parsing, result contains the line table row! Result = Row; - if (Addr == Row.Addr) { - // Stop parsing, this is the row we are looking for since the address - // matches. - return false; - } return true; // Keep parsing till we find the right row. }); if (Err) diff --git a/llvm/lib/DebugInfo/GSYM/ObjectFileTransformer.cpp b/llvm/lib/DebugInfo/GSYM/ObjectFileTransformer.cpp index ad35aefe7774..a60b2d386076 100644 --- a/llvm/lib/DebugInfo/GSYM/ObjectFileTransformer.cpp +++ b/llvm/lib/DebugInfo/GSYM/ObjectFileTransformer.cpp @@ -68,7 +68,7 @@ static std::vector<uint8_t> getUUID(const object::ObjectFile &Obj) { } llvm::Error ObjectFileTransformer::convert(const object::ObjectFile &Obj, - raw_ostream &Log, + raw_ostream *Log, GsymCreator &Gsym) { using namespace llvm::object; @@ -92,15 +92,18 @@ llvm::Error ObjectFileTransformer::convert(const object::ObjectFile &Obj, return AddrOrErr.takeError(); if (SymType.get() != SymbolRef::Type::ST_Function || - !Gsym.IsValidTextAddress(*AddrOrErr) || - Gsym.hasFunctionInfoForAddress(*AddrOrErr)) + !Gsym.IsValidTextAddress(*AddrOrErr)) continue; // Function size for MachO files will be 0 constexpr bool NoCopy = false; const uint64_t size = IsELF ? ELFSymbolRef(Sym).getSize() : 0; Expected<StringRef> Name = Sym.getName(); if (!Name) { - logAllUnhandledErrors(Name.takeError(), Log, "ObjectFileTransformer: "); + if (Log) + logAllUnhandledErrors(Name.takeError(), *Log, + "ObjectFileTransformer: "); + else + consumeError(Name.takeError()); continue; } // Remove the leading '_' character in any symbol names if there is one @@ -111,6 +114,8 @@ llvm::Error ObjectFileTransformer::convert(const object::ObjectFile &Obj, FunctionInfo(*AddrOrErr, size, Gsym.insertString(*Name, NoCopy))); } size_t FunctionsAddedCount = Gsym.getNumFunctionInfos() - NumBefore; - Log << "Loaded " << FunctionsAddedCount << " functions from symbol table.\n"; + if (Log) + *Log << "Loaded " << FunctionsAddedCount + << " functions from symbol table.\n"; return Error::success(); } diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVCompare.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVCompare.cpp index 65baf52ffb44..3ed0de14f93f 100644 --- a/llvm/lib/DebugInfo/LogicalView/Core/LVCompare.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVCompare.cpp @@ -206,11 +206,10 @@ Error LVCompare::execute(LVReader *ReferenceReader, LVReader *TargetReader) { updateExpected(Reference); Reference->setIsInCompare(); LVElement *CurrentTarget = nullptr; - if (std::any_of(Targets.begin(), Targets.end(), - [&](auto Target) -> bool { - CurrentTarget = Target; - return Reference->equals(Target); - })) { + if (llvm::any_of(Targets, [&](auto Target) -> bool { + CurrentTarget = Target; + return Reference->equals(Target); + })) { if (Pass == LVComparePass::Missing && Reference->getIsScope()) { // If the elements being compared are scopes and are a match, // they are recorded, to be used when creating the augmented diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp index cfe304eead51..30ce937cda44 100644 --- a/llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp @@ -252,8 +252,7 @@ void LVElement::generateName(std::string &Prefix) const { Prefix.append(isLined() ? lineNumberAsString(/*ShowZero=*/true) : "?"); // Remove any whitespaces. - Prefix.erase(std::remove_if(Prefix.begin(), Prefix.end(), ::isspace), - Prefix.end()); + llvm::erase_if(Prefix, ::isspace); } // Generate a name for unnamed elements. diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp index 2f26025d01ec..8bbaf93db0ca 100644 --- a/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp @@ -299,20 +299,12 @@ void LVScope::addMissingElements(LVScope *Reference) { LVSymbols References; References.append(ReferenceSymbols->begin(), ReferenceSymbols->end()); - auto RemoveSymbol = [&](LVSymbols &Symbols, LVSymbol *Symbol) { - LVSymbols::iterator Iter = std::remove_if( - Symbols.begin(), Symbols.end(), - [Symbol](LVSymbol *Item) -> bool { return Item == Symbol; }); - if (Iter != Symbols.end()) - Symbols.erase(Iter, Symbols.end()); - }; - // Erase abstract symbols already in this scope from the collection of // symbols in the referenced scope. if (getSymbols()) for (const LVSymbol *Symbol : *getSymbols()) if (Symbol->getHasReferenceAbstract()) - RemoveSymbol(References, Symbol->getReference()); + llvm::erase(References, Symbol->getReference()); // If we have elements left in 'References', those are the elements that // need to be inserted in the current scope. diff --git a/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewReader.cpp b/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewReader.cpp index d72fe2683f92..1f6724988ae9 100644 --- a/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewReader.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewReader.cpp @@ -221,7 +221,7 @@ bool LVCodeViewReader::isSystemEntry(LVElement *Element, StringRef Name) const { return StringRef::npos != Name.find(String); }; auto Starts = [=](const char *Pattern) -> bool { - return Name.startswith(Pattern); + return Name.starts_with(Pattern); }; auto CheckExclude = [&]() -> bool { if (Starts("__") || Starts("_PMD") || Starts("_PMFN")) @@ -276,7 +276,7 @@ Error LVCodeViewReader::collectInlineeInfo( } Error LVCodeViewReader::traverseInlineeLines(StringRef Subsection) { - BinaryStreamReader SR(Subsection, llvm::support::little); + BinaryStreamReader SR(Subsection, llvm::endianness::little); DebugInlineeLinesSubsectionRef Lines; if (Error E = Lines.initialize(SR)) return createStringError(errorToErrorCode(std::move(E)), getFileName()); @@ -349,7 +349,7 @@ Error LVCodeViewReader::initializeFileAndStringTables( if (Error E = Reader.readFixedString(Contents, SubSectionSize)) return createStringError(errorToErrorCode(std::move(E)), getFileName()); - BinaryStreamRef ST(Contents, support::little); + BinaryStreamRef ST(Contents, llvm::endianness::little); switch (DebugSubsectionKind(SubType)) { case DebugSubsectionKind::FileChecksums: if (Error E = CVFileChecksumTable.initialize(ST)) @@ -478,8 +478,8 @@ Error LVCodeViewReader::loadPrecompiledObject(PrecompRecord &Precomp, if (Magic != COFF::DEBUG_SECTION_MAGIC) return errorCodeToError(object_error::parse_failed); - ReaderPrecomp = - std::make_unique<BinaryStreamReader>(*DataOrErr, support::little); + ReaderPrecomp = std::make_unique<BinaryStreamReader>( + *DataOrErr, llvm::endianness::little); cantFail( ReaderPrecomp->readArray(CVTypesPrecomp, ReaderPrecomp->getLength())); @@ -514,7 +514,7 @@ Error LVCodeViewReader::loadPrecompiledObject(PrecompRecord &Precomp, [&](TypeIndex TI, const CVType &Type) { TypeArray.push_back(Type); }); ItemStream = - std::make_unique<BinaryItemStream<CVType>>(llvm::support::little); + std::make_unique<BinaryItemStream<CVType>>(llvm::endianness::little); ItemStream->setItems(TypeArray); TypeStream.setUnderlyingStream(*ItemStream); @@ -550,7 +550,7 @@ Error LVCodeViewReader::traverseTypeSection(StringRef SectionName, // Get the first type record. It will indicate if this object uses a type // server (/Zi) or a PCH file (/Yu). CVTypeArray CVTypes; - BinaryStreamReader Reader(*DataOrErr, support::little); + BinaryStreamReader Reader(*DataOrErr, llvm::endianness::little); cantFail(Reader.readArray(CVTypes, Reader.getLength())); CVTypeArray::Iterator FirstType = CVTypes.begin(); @@ -621,7 +621,7 @@ Error LVCodeViewReader::traverseSymbolsSubsection(StringRef Subsection, LVSymbolVisitorDelegate VisitorDelegate(this, Section, &getObj(), SectionContents); CVSymbolArray Symbols; - BinaryStreamReader Reader(BinaryData, llvm::support::little); + BinaryStreamReader Reader(BinaryData, llvm::endianness::little); if (Error E = Reader.readArray(Symbols, Reader.getLength())) return createStringError(errorToErrorCode(std::move(E)), getFileName()); @@ -664,7 +664,7 @@ Error LVCodeViewReader::traverseSymbolSection(StringRef SectionName, if (Magic != COFF::DEBUG_SECTION_MAGIC) return createStringError(object_error::parse_failed, getFileName()); - BinaryStreamReader FSReader(Data, support::little); + BinaryStreamReader FSReader(Data, llvm::endianness::little); if (Error Err = initializeFileAndStringTables(FSReader)) return Err; @@ -752,7 +752,8 @@ Error LVCodeViewReader::traverseSymbolSection(StringRef SectionName, W.printString("Symbol Name", SymbolName); }); - BinaryStreamReader Reader(FunctionLineTables[SymbolName], support::little); + BinaryStreamReader Reader(FunctionLineTables[SymbolName], + llvm::endianness::little); DebugLinesSubsectionRef Lines; if (Error E = Lines.initialize(Reader)) diff --git a/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp b/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp index e4f5f533262b..1d0178532882 100644 --- a/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp @@ -465,13 +465,10 @@ LVScope *LVNamespaceDeduction::get(LVStringRefs Components) { LVScope *LVNamespaceDeduction::get(StringRef ScopedName, bool CheckScope) { LVStringRefs Components = getAllLexicalComponents(ScopedName); if (CheckScope) - Components.erase(std::remove_if(Components.begin(), Components.end(), - [&](StringRef Component) { - LookupSet::iterator Iter = - IdentifiedNamespaces.find(Component); - return Iter == IdentifiedNamespaces.end(); - }), - Components.end()); + llvm::erase_if(Components, [&](StringRef Component) { + LookupSet::iterator Iter = IdentifiedNamespaces.find(Component); + return Iter == IdentifiedNamespaces.end(); + }); LLVM_DEBUG( { dbgs() << formatv("ScopedName: '{0}'\n", ScopedName.str().c_str()); }); @@ -1688,6 +1685,48 @@ Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, return Error::success(); } +// S_ARMSWITCHTABLE +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &CVR, + JumpTableSym &JumpTable) { + LLVM_DEBUG({ + W.printHex("BaseOffset", JumpTable.BaseOffset); + W.printNumber("BaseSegment", JumpTable.BaseSegment); + W.printFlags("SwitchType", static_cast<uint16_t>(JumpTable.SwitchType), + getJumpTableEntrySizeNames()); + W.printHex("BranchOffset", JumpTable.BranchOffset); + W.printHex("TableOffset", JumpTable.TableOffset); + W.printNumber("BranchSegment", JumpTable.BranchSegment); + W.printNumber("TableSegment", JumpTable.TableSegment); + W.printNumber("EntriesCount", JumpTable.EntriesCount); + }); + return Error::success(); +} + +// S_CALLERS, S_CALLEES, S_INLINEES +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, CallerSym &Caller) { + LLVM_DEBUG({ + llvm::StringRef FieldName; + switch (Caller.getKind()) { + case SymbolRecordKind::CallerSym: + FieldName = "Callee"; + break; + case SymbolRecordKind::CalleeSym: + FieldName = "Caller"; + break; + case SymbolRecordKind::InlineesSym: + FieldName = "Inlinee"; + break; + default: + return llvm::make_error<CodeViewError>( + "Unknown CV Record type for a CallerSym object!"); + } + for (auto FuncID : Caller.Indices) { + printTypeIndex(FieldName, FuncID); + } + }); + return Error::success(); +} + #undef DEBUG_TYPE #define DEBUG_TYPE "CodeViewLogicalVisitor" @@ -2897,7 +2936,7 @@ Error LVLogicalVisitor::finishVisitation(CVType &Record, TypeIndex TI, // Customized version of 'FieldListVisitHelper'. Error LVLogicalVisitor::visitFieldListMemberStream( TypeIndex TI, LVElement *Element, ArrayRef<uint8_t> FieldList) { - BinaryByteStream Stream(FieldList, llvm::support::little); + BinaryByteStream Stream(FieldList, llvm::endianness::little); BinaryStreamReader Reader(Stream); FieldListDeserializer Deserializer(Reader); TypeVisitorCallbackPipeline Pipeline; diff --git a/llvm/lib/DebugInfo/LogicalView/Readers/LVELFReader.cpp b/llvm/lib/DebugInfo/LogicalView/Readers/LVELFReader.cpp index ab458341a0bd..4469092099da 100644 --- a/llvm/lib/DebugInfo/LogicalView/Readers/LVELFReader.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Readers/LVELFReader.cpp @@ -1058,7 +1058,7 @@ void LVELFReader::processLocationMember(dwarf::Attribute Attr, CurrentSymbol->addLocationConstant(Attr, *FormValue.getAsUnsignedConstant(), OffsetOnEntry); else - // This is a a location description, or a reference to one. + // This is a location description, or a reference to one. processLocationList(Attr, FormValue, Die, OffsetOnEntry); } diff --git a/llvm/lib/DebugInfo/MSF/MSFBuilder.cpp b/llvm/lib/DebugInfo/MSF/MSFBuilder.cpp index c26caa647ed9..ed2d14dd79e4 100644 --- a/llvm/lib/DebugInfo/MSF/MSFBuilder.cpp +++ b/llvm/lib/DebugInfo/MSF/MSFBuilder.cpp @@ -16,6 +16,7 @@ #include "llvm/Support/Error.h" #include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/TimeProfiler.h" #include <algorithm> #include <cassert> #include <cstdint> @@ -248,6 +249,8 @@ uint32_t MSFBuilder::computeDirectoryByteSize() const { } Expected<MSFLayout> MSFBuilder::generateLayout() { + llvm::TimeTraceScope timeScope("MSF: Generate layout"); + SuperBlock *SB = Allocator.Allocate<SuperBlock>(); MSFLayout L; L.SB = SB; @@ -336,6 +339,8 @@ static void commitFpm(WritableBinaryStream &MsfBuffer, const MSFLayout &Layout, Expected<FileBufferByteStream> MSFBuilder::commit(StringRef Path, MSFLayout &Layout) { + llvm::TimeTraceScope timeScope("Commit MSF"); + Expected<MSFLayout> L = generateLayout(); if (!L) return L.takeError(); @@ -381,7 +386,7 @@ Expected<FileBufferByteStream> MSFBuilder::commit(StringRef Path, return std::move(EC); FileBufferByteStream Buffer(std::move(*OutFileOrError), - llvm::support::little); + llvm::endianness::little); BinaryStreamWriter Writer(Buffer); if (auto EC = Writer.writeObject(*Layout.SB)) diff --git a/llvm/lib/DebugInfo/MSF/MappedBlockStream.cpp b/llvm/lib/DebugInfo/MSF/MappedBlockStream.cpp index 94935d63452e..5ebb76994b31 100644 --- a/llvm/lib/DebugInfo/MSF/MappedBlockStream.cpp +++ b/llvm/lib/DebugInfo/MSF/MappedBlockStream.cpp @@ -10,7 +10,6 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/DebugInfo/MSF/MSFCommon.h" #include "llvm/Support/BinaryStreamWriter.h" -#include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" #include "llvm/Support/MathExtras.h" #include <algorithm> diff --git a/llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptor.cpp b/llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptor.cpp index 9755f2ca3bdc..f27a645c7704 100644 --- a/llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptor.cpp +++ b/llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptor.cpp @@ -9,7 +9,6 @@ #include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h" #include "llvm/DebugInfo/PDB/Native/RawTypes.h" #include "llvm/Support/BinaryStreamReader.h" -#include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" #include "llvm/Support/MathExtras.h" #include <cstdint> diff --git a/llvm/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp b/llvm/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp index 37c1b0407268..ad3d09ae50e9 100644 --- a/llvm/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp +++ b/llvm/lib/DebugInfo/PDB/Native/DbiStreamBuilder.cpp @@ -18,6 +18,7 @@ #include "llvm/Object/COFF.h" #include "llvm/Support/BinaryStreamWriter.h" #include "llvm/Support/Parallel.h" +#include "llvm/Support/TimeProfiler.h" using namespace llvm; using namespace llvm::codeview; @@ -187,7 +188,7 @@ Error DbiStreamBuilder::generateFileInfoSubstream() { uint32_t NamesOffset = calculateNamesOffset(); FileInfoBuffer = MutableBinaryByteStream(MutableArrayRef<uint8_t>(Data, Size), - llvm::support::little); + llvm::endianness::little); WritableBinaryStreamRef MetadataBuffer = WritableBinaryStreamRef(FileInfoBuffer).keep_front(NamesOffset); @@ -381,6 +382,7 @@ void DbiStreamBuilder::createSectionMap( Error DbiStreamBuilder::commit(const msf::MSFLayout &Layout, WritableBinaryStreamRef MsfBuffer) { + llvm::TimeTraceScope timeScope("Commit DBI stream"); if (auto EC = finalize()) return EC; diff --git a/llvm/lib/DebugInfo/PDB/Native/FormatUtil.cpp b/llvm/lib/DebugInfo/PDB/Native/FormatUtil.cpp index 9c05d585831a..c5999bffc021 100644 --- a/llvm/lib/DebugInfo/PDB/Native/FormatUtil.cpp +++ b/llvm/lib/DebugInfo/PDB/Native/FormatUtil.cpp @@ -9,6 +9,7 @@ #include "llvm/DebugInfo/PDB/Native/FormatUtil.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/STLForwardCompat.h" #include "llvm/ADT/StringExtras.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/DebugInfo/CodeView/CodeView.h" @@ -119,9 +120,7 @@ std::string llvm::pdb::formatTypeLeafKind(TypeLeafKind K) { return #EnumName; #include "llvm/DebugInfo/CodeView/CodeViewTypes.def" default: - return formatv("UNKNOWN RECORD ({0:X})", - static_cast<std::underlying_type_t<TypeLeafKind>>(K)) - .str(); + return formatv("UNKNOWN RECORD ({0:X})", llvm::to_underlying(K)).str(); } } diff --git a/llvm/lib/DebugInfo/PDB/Native/GSIStreamBuilder.cpp b/llvm/lib/DebugInfo/PDB/Native/GSIStreamBuilder.cpp index b17fbd63e9fd..c195754c0c67 100644 --- a/llvm/lib/DebugInfo/PDB/Native/GSIStreamBuilder.cpp +++ b/llvm/lib/DebugInfo/PDB/Native/GSIStreamBuilder.cpp @@ -26,6 +26,7 @@ #include "llvm/Support/BinaryItemStream.h" #include "llvm/Support/BinaryStreamWriter.h" #include "llvm/Support/Parallel.h" +#include "llvm/Support/TimeProfiler.h" #include "llvm/Support/xxhash.h" #include <algorithm> #include <vector> @@ -393,7 +394,7 @@ static Error writePublics(BinaryStreamWriter &Writer, static Error writeRecords(BinaryStreamWriter &Writer, ArrayRef<CVSymbol> Records) { - BinaryItemStream<CVSymbol> ItemStream(support::endianness::little); + BinaryItemStream<CVSymbol> ItemStream(llvm::endianness::little); ItemStream.setItems(Records); BinaryStreamRef RecordsRef(ItemStream); return Writer.writeStreamRef(RecordsRef); @@ -478,6 +479,7 @@ Error GSIStreamBuilder::commitGlobalsHashStream( Error GSIStreamBuilder::commit(const msf::MSFLayout &Layout, WritableBinaryStreamRef Buffer) { + llvm::TimeTraceScope timeScope("Commit GSI stream"); auto GS = WritableMappedBlockStream::createIndexedStream( Layout, Buffer, getGlobalsStreamIndex(), Msf.getAllocator()); auto PS = WritableMappedBlockStream::createIndexedStream( diff --git a/llvm/lib/DebugInfo/PDB/Native/InfoStreamBuilder.cpp b/llvm/lib/DebugInfo/PDB/Native/InfoStreamBuilder.cpp index e8f5a451b08e..95107125701d 100644 --- a/llvm/lib/DebugInfo/PDB/Native/InfoStreamBuilder.cpp +++ b/llvm/lib/DebugInfo/PDB/Native/InfoStreamBuilder.cpp @@ -14,6 +14,7 @@ #include "llvm/DebugInfo/PDB/Native/RawTypes.h" #include "llvm/Support/BinaryStreamReader.h" #include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/TimeProfiler.h" using namespace llvm; using namespace llvm::codeview; @@ -55,6 +56,7 @@ Error InfoStreamBuilder::finalizeMsfLayout() { Error InfoStreamBuilder::commit(const msf::MSFLayout &Layout, WritableBinaryStreamRef Buffer) const { + llvm::TimeTraceScope timeScope("Commit info stream"); auto InfoS = WritableMappedBlockStream::createIndexedStream( Layout, Buffer, StreamPDB, Msf.getAllocator()); BinaryStreamWriter Writer(*InfoS); diff --git a/llvm/lib/DebugInfo/PDB/Native/InjectedSourceStream.cpp b/llvm/lib/DebugInfo/PDB/Native/InjectedSourceStream.cpp index f1e8adeb1b21..841068c77d48 100644 --- a/llvm/lib/DebugInfo/PDB/Native/InjectedSourceStream.cpp +++ b/llvm/lib/DebugInfo/PDB/Native/InjectedSourceStream.cpp @@ -14,7 +14,6 @@ #include "llvm/DebugInfo/PDB/Native/RawConstants.h" #include "llvm/DebugInfo/PDB/Native/RawTypes.h" #include "llvm/Support/BinaryStreamReader.h" -#include "llvm/Support/Endian.h" using namespace llvm; using namespace llvm::msf; diff --git a/llvm/lib/DebugInfo/PDB/Native/InputFile.cpp b/llvm/lib/DebugInfo/PDB/Native/InputFile.cpp index 85c22483fa90..328d0f5ab060 100644 --- a/llvm/lib/DebugInfo/PDB/Native/InputFile.cpp +++ b/llvm/lib/DebugInfo/PDB/Native/InputFile.cpp @@ -107,7 +107,7 @@ static inline bool isCodeViewDebugSubsection(object::SectionRef Section, return false; } - Reader = BinaryStreamReader(*ContentsOrErr, support::little); + Reader = BinaryStreamReader(*ContentsOrErr, llvm::endianness::little); uint32_t Magic; if (Reader.bytesRemaining() < sizeof(uint32_t)) return false; @@ -561,7 +561,7 @@ static bool isMyCode(const SymbolGroup &Group) { return true; StringRef Name = Group.name(); - if (Name.startswith("Import:")) + if (Name.starts_with("Import:")) return false; if (Name.ends_with_insensitive(".dll")) return false; diff --git a/llvm/lib/DebugInfo/PDB/Native/NamedStreamMap.cpp b/llvm/lib/DebugInfo/PDB/Native/NamedStreamMap.cpp index 500923e57fbb..bdf8e6ec1acd 100644 --- a/llvm/lib/DebugInfo/PDB/Native/NamedStreamMap.cpp +++ b/llvm/lib/DebugInfo/PDB/Native/NamedStreamMap.cpp @@ -7,7 +7,6 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h" -#include "llvm/ADT/SparseBitVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/DebugInfo/PDB/Native/Hash.h" diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp index 91b428afaddb..d5cac33d1519 100644 --- a/llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp +++ b/llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp @@ -74,7 +74,7 @@ Error NativeSession::createFromPdb(std::unique_ptr<MemoryBuffer> Buffer, std::unique_ptr<IPDBSession> &Session) { StringRef Path = Buffer->getBufferIdentifier(); auto Stream = std::make_unique<MemoryBufferByteStream>( - std::move(Buffer), llvm::support::little); + std::move(Buffer), llvm::endianness::little); auto Allocator = std::make_unique<BumpPtrAllocator>(); auto File = std::make_unique<PDBFile>(Path, std::move(Stream), *Allocator); @@ -104,8 +104,8 @@ loadPdbFile(StringRef PdbPath, std::unique_ptr<BumpPtrAllocator> &Allocator) { if (EC || Magic != file_magic::pdb) return make_error<RawError>(EC); - auto Stream = std::make_unique<MemoryBufferByteStream>(std::move(Buffer), - llvm::support::little); + auto Stream = std::make_unique<MemoryBufferByteStream>( + std::move(Buffer), llvm::endianness::little); auto File = std::make_unique<PDBFile>(PdbPath, std::move(Stream), *Allocator); if (auto EC = File->parseFileHeaders()) @@ -176,7 +176,7 @@ NativeSession::searchForPdb(const PdbSearchOptions &Opts) { if (!PathOrErr) return PathOrErr.takeError(); StringRef PathFromExe = PathOrErr.get(); - sys::path::Style Style = PathFromExe.startswith("/") + sys::path::Style Style = PathFromExe.starts_with("/") ? sys::path::Style::posix : sys::path::Style::windows; StringRef PdbName = sys::path::filename(PathFromExe, Style); diff --git a/llvm/lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp b/llvm/lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp index cd30b56be7cd..06e379c3f6d2 100644 --- a/llvm/lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp +++ b/llvm/lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp @@ -25,6 +25,7 @@ #include "llvm/Support/BinaryStreamWriter.h" #include "llvm/Support/CRC.h" #include "llvm/Support/Path.h" +#include "llvm/Support/TimeProfiler.h" #include "llvm/Support/xxhash.h" #include <ctime> @@ -129,6 +130,7 @@ void PDBFileBuilder::addInjectedSource(StringRef Name, } Error PDBFileBuilder::finalizeMsfLayout() { + llvm::TimeTraceScope timeScope("MSF layout"); if (Ipi && Ipi->getRecordCount() > 0) { // In theory newer PDBs always have an ID stream, but by saying that we're @@ -254,6 +256,7 @@ void PDBFileBuilder::commitInjectedSources(WritableBinaryStream &MsfBuffer, if (InjectedSourceTable.empty()) return; + llvm::TimeTraceScope timeScope("Commit injected sources"); commitSrcHeaderBlock(MsfBuffer, Layout); for (const auto &IS : InjectedSources) { @@ -290,15 +293,18 @@ Error PDBFileBuilder::commit(StringRef Filename, codeview::GUID *Guid) { if (auto EC = Strings.commit(NSWriter)) return EC; - for (const auto &NSE : NamedStreamData) { - if (NSE.second.empty()) - continue; + { + llvm::TimeTraceScope timeScope("Named stream data"); + for (const auto &NSE : NamedStreamData) { + if (NSE.second.empty()) + continue; - auto NS = WritableMappedBlockStream::createIndexedStream( - Layout, Buffer, NSE.first, Allocator); - BinaryStreamWriter NSW(*NS); - if (auto EC = NSW.writeBytes(arrayRefFromStringRef(NSE.second))) - return EC; + auto NS = WritableMappedBlockStream::createIndexedStream( + Layout, Buffer, NSE.first, Allocator); + BinaryStreamWriter NSW(*NS); + if (auto EC = NSW.writeBytes(arrayRefFromStringRef(NSE.second))) + return EC; + } } if (Info) { @@ -338,6 +344,8 @@ Error PDBFileBuilder::commit(StringRef Filename, codeview::GUID *Guid) { // Set the build id at the very end, after every other byte of the PDB // has been written. if (Info->hashPDBContentsToGUID()) { + llvm::TimeTraceScope timeScope("Compute build ID"); + // Compute a hash of all sections of the output file. uint64_t Digest = xxh3_64bits({Buffer.getBufferStart(), Buffer.getBufferEnd()}); diff --git a/llvm/lib/DebugInfo/PDB/Native/PDBStringTableBuilder.cpp b/llvm/lib/DebugInfo/PDB/Native/PDBStringTableBuilder.cpp index c0245dc17cf1..91b3dd5c32b9 100644 --- a/llvm/lib/DebugInfo/PDB/Native/PDBStringTableBuilder.cpp +++ b/llvm/lib/DebugInfo/PDB/Native/PDBStringTableBuilder.cpp @@ -13,6 +13,7 @@ #include "llvm/DebugInfo/PDB/Native/RawTypes.h" #include "llvm/Support/BinaryStreamWriter.h" #include "llvm/Support/Endian.h" +#include "llvm/Support/TimeProfiler.h" #include <map> @@ -207,6 +208,7 @@ Error PDBStringTableBuilder::writeEpilogue(BinaryStreamWriter &Writer) const { } Error PDBStringTableBuilder::commit(BinaryStreamWriter &Writer) const { + llvm::TimeTraceScope timeScope("Commit strings table"); BinaryStreamWriter SectionWriter; std::tie(SectionWriter, Writer) = Writer.split(sizeof(PDBStringTableHeader)); diff --git a/llvm/lib/DebugInfo/PDB/Native/PublicsStream.cpp b/llvm/lib/DebugInfo/PDB/Native/PublicsStream.cpp index c7b9f443da5e..c350e0e0b3e1 100644 --- a/llvm/lib/DebugInfo/PDB/Native/PublicsStream.cpp +++ b/llvm/lib/DebugInfo/PDB/Native/PublicsStream.cpp @@ -26,7 +26,6 @@ #include "llvm/DebugInfo/PDB/Native/RawError.h" #include "llvm/DebugInfo/PDB/Native/RawTypes.h" #include "llvm/Support/BinaryStreamReader.h" -#include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" #include <cstdint> diff --git a/llvm/lib/DebugInfo/PDB/Native/SymbolStream.cpp b/llvm/lib/DebugInfo/PDB/Native/SymbolStream.cpp index 5802d1c77527..5dd636f326b7 100644 --- a/llvm/lib/DebugInfo/PDB/Native/SymbolStream.cpp +++ b/llvm/lib/DebugInfo/PDB/Native/SymbolStream.cpp @@ -9,7 +9,6 @@ #include "llvm/DebugInfo/PDB/Native/SymbolStream.h" #include "llvm/DebugInfo/MSF/MappedBlockStream.h" -#include "llvm/Support/Endian.h" using namespace llvm; using namespace llvm::msf; diff --git a/llvm/lib/DebugInfo/PDB/Native/TpiHashing.cpp b/llvm/lib/DebugInfo/PDB/Native/TpiHashing.cpp index b71b2b158144..941ce78027a2 100644 --- a/llvm/lib/DebugInfo/PDB/Native/TpiHashing.cpp +++ b/llvm/lib/DebugInfo/PDB/Native/TpiHashing.cpp @@ -19,7 +19,7 @@ using namespace llvm::pdb; // Corresponds to `fUDTAnon`. static bool isAnonymous(StringRef Name) { return Name == "<unnamed-tag>" || Name == "__unnamed" || - Name.endswith("::<unnamed-tag>") || Name.endswith("::__unnamed"); + Name.ends_with("::<unnamed-tag>") || Name.ends_with("::__unnamed"); } // Computes the hash for a user-defined type record. This could be a struct, diff --git a/llvm/lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp b/llvm/lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp index aad5847651a0..22663f009637 100644 --- a/llvm/lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp +++ b/llvm/lib/DebugInfo/PDB/Native/TpiStreamBuilder.cpp @@ -19,6 +19,7 @@ #include "llvm/Support/BinaryStreamWriter.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" +#include "llvm/Support/TimeProfiler.h" #include <algorithm> #include <cstdint> #include <numeric> @@ -164,13 +165,14 @@ Error TpiStreamBuilder::finalizeMsfLayout() { reinterpret_cast<const uint8_t *>(HashBuffer.data()), calculateHashBufferSize()); HashValueStream = - std::make_unique<BinaryByteStream>(Bytes, llvm::support::little); + std::make_unique<BinaryByteStream>(Bytes, llvm::endianness::little); } return Error::success(); } Error TpiStreamBuilder::commit(const msf::MSFLayout &Layout, WritableBinaryStreamRef Buffer) { + llvm::TimeTraceScope timeScope("Commit TPI stream"); if (auto EC = finalize()) return EC; diff --git a/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp b/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp index f9669b554b47..716312f26e0b 100644 --- a/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp +++ b/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp @@ -90,7 +90,7 @@ public: size_t PosEnd = PrunedSource->find('\n', Pos); StringRef String = PrunedSource->substr( Pos, (PosEnd == StringRef::npos) ? StringRef::npos : (PosEnd - Pos)); - if (String.endswith("\r")) + if (String.ends_with("\r")) String = String.drop_back(1); OS << format_decimal(L, MaxLineNumberWidth); if (L == Line) @@ -105,10 +105,10 @@ public: } }; -void PlainPrinterBase::printHeader(uint64_t Address) { - if (Config.PrintAddress) { +void PlainPrinterBase::printHeader(std::optional<uint64_t> Address) { + if (Address.has_value() && Config.PrintAddress) { OS << "0x"; - OS.write_hex(Address); + OS.write_hex(*Address); StringRef Delimiter = Config.Pretty ? ": " : "\n"; OS << Delimiter; } @@ -182,7 +182,7 @@ void PlainPrinterBase::print(const DILineInfo &Info, bool Inlined) { } void PlainPrinterBase::print(const Request &Request, const DILineInfo &Info) { - printHeader(*Request.Address); + printHeader(Request.Address); print(Info, false); printFooter(); } @@ -260,9 +260,15 @@ void PlainPrinterBase::print(const Request &Request, printFooter(); } -void PlainPrinterBase::printInvalidCommand(const Request &Request, - StringRef Command) { - OS << Command << '\n'; +void PlainPrinterBase::print(const Request &Request, + const std::vector<DILineInfo> &Locations) { + if (Locations.empty()) { + print(Request, DILineInfo()); + } else { + for (const DILineInfo &L : Locations) + print(L, false); + printFooter(); + } } bool PlainPrinterBase::printError(const Request &Request, @@ -278,6 +284,8 @@ static std::string toHex(uint64_t V) { static json::Object toJSON(const Request &Request, StringRef ErrorMsg = "") { json::Object Json({{"ModuleName", Request.ModuleName.str()}}); + if (!Request.Symbol.empty()) + Json["SymName"] = Request.Symbol.str(); if (Request.Address) Json["Address"] = toHex(*Request.Address); if (!ErrorMsg.empty()) @@ -367,11 +375,17 @@ void JSONPrinter::print(const Request &Request, printJSON(std::move(Json)); } -void JSONPrinter::printInvalidCommand(const Request &Request, - StringRef Command) { - printError(Request, - StringError("unable to parse arguments: " + Command, - std::make_error_code(std::errc::invalid_argument))); +void JSONPrinter::print(const Request &Request, + const std::vector<DILineInfo> &Locations) { + json::Array Definitions; + for (const DILineInfo &L : Locations) + Definitions.push_back(toJSON(L)); + json::Object Json = toJSON(Request); + Json["Loc"] = std::move(Definitions); + if (ObjectList) + ObjectList->push_back(std::move(Json)); + else + printJSON(std::move(Json)); } bool JSONPrinter::printError(const Request &Request, diff --git a/llvm/lib/DebugInfo/Symbolize/MarkupFilter.cpp b/llvm/lib/DebugInfo/Symbolize/MarkupFilter.cpp index a2bc2577b70a..f7503ef49693 100644 --- a/llvm/lib/DebugInfo/Symbolize/MarkupFilter.cpp +++ b/llvm/lib/DebugInfo/Symbolize/MarkupFilter.cpp @@ -552,7 +552,7 @@ std::optional<uint64_t> MarkupFilter::parseAddr(StringRef Str) const { } if (all_of(Str, [](char C) { return C == '0'; })) return 0; - if (!Str.startswith("0x")) { + if (!Str.starts_with("0x")) { reportTypeError(Str, "address"); return std::nullopt; } @@ -741,7 +741,7 @@ uint64_t MarkupFilter::adjustAddr(uint64_t Addr, PCType Type) const { } StringRef MarkupFilter::lineEnding() const { - return Line.endswith("\r\n") ? "\r\n" : "\n"; + return Line.ends_with("\r\n") ? "\r\n" : "\n"; } bool MarkupFilter::MMap::contains(uint64_t Addr) const { diff --git a/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.cpp b/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.cpp index 6b8068a531c0..697303038507 100644 --- a/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.cpp +++ b/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.cpp @@ -351,6 +351,19 @@ std::vector<DILocal> SymbolizableObjectFile::symbolizeFrame( return DebugInfoContext->getLocalsForAddress(ModuleOffset); } +std::vector<object::SectionedAddress> +SymbolizableObjectFile::findSymbol(StringRef Symbol) const { + std::vector<object::SectionedAddress> Result; + for (const SymbolDesc &Sym : Symbols) { + if (Sym.Name.equals(Symbol)) { + object::SectionedAddress A{Sym.Addr, + getModuleSectionIndexForAddress(Sym.Addr)}; + Result.push_back(A); + } + } + return Result; +} + /// Search for the first occurence of specified Address in ObjectFile. uint64_t SymbolizableObjectFile::getModuleSectionIndexForAddress( uint64_t Address) const { diff --git a/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp b/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp index 517f1e7dc284..15f2a6ece8b8 100644 --- a/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp +++ b/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp @@ -231,6 +231,50 @@ LLVMSymbolizer::symbolizeFrame(ArrayRef<uint8_t> BuildID, return symbolizeFrameCommon(BuildID, ModuleOffset); } +template <typename T> +Expected<std::vector<DILineInfo>> +LLVMSymbolizer::findSymbolCommon(const T &ModuleSpecifier, StringRef Symbol) { + auto InfoOrErr = getOrCreateModuleInfo(ModuleSpecifier); + if (!InfoOrErr) + return InfoOrErr.takeError(); + + SymbolizableModule *Info = *InfoOrErr; + std::vector<DILineInfo> Result; + + // A null module means an error has already been reported. Return an empty + // result. + if (!Info) + return Result; + + for (object::SectionedAddress A : Info->findSymbol(Symbol)) { + DILineInfo LineInfo = Info->symbolizeCode( + A, DILineInfoSpecifier(Opts.PathStyle, Opts.PrintFunctions), + Opts.UseSymbolTable); + if (LineInfo.FileName != DILineInfo::BadString) { + if (Opts.Demangle) + LineInfo.FunctionName = DemangleName(LineInfo.FunctionName, Info); + Result.push_back(LineInfo); + } + } + + return Result; +} + +Expected<std::vector<DILineInfo>> +LLVMSymbolizer::findSymbol(const ObjectFile &Obj, StringRef Symbol) { + return findSymbolCommon(Obj, Symbol); +} + +Expected<std::vector<DILineInfo>> +LLVMSymbolizer::findSymbol(StringRef ModuleName, StringRef Symbol) { + return findSymbolCommon(ModuleName.str(), Symbol); +} + +Expected<std::vector<DILineInfo>> +LLVMSymbolizer::findSymbol(ArrayRef<uint8_t> BuildID, StringRef Symbol) { + return findSymbolCommon(BuildID, Symbol); +} + void LLVMSymbolizer::flush() { ObjectForUBPathAndArch.clear(); LRUBinaries.clear(); @@ -673,7 +717,7 @@ StringRef demanglePE32ExternCFunc(StringRef SymbolName) { // Remove any ending '@' for vectorcall. bool IsVectorCall = false; - if (HasAtNumSuffix && SymbolName.endswith("@")) { + if (HasAtNumSuffix && SymbolName.ends_with("@")) { SymbolName = SymbolName.drop_back(); IsVectorCall = true; } |
