yonghong-song created this revision. yonghong-song added a project: debug-info. Herald added subscribers: dexonsmith, pengfei, JDevlieghere, hiraditya, kristof.beyls, mgorny. yonghong-song requested review of this revision. Herald added projects: clang, LLVM. Herald added subscribers: llvm-commits, cfe-commits.
This is a Proof-Of-Concept patch and intends to seek suggestions from llvm community on how to put an attribute with arbitrary string into the final debuginfo in the object file. The Use Cases ============= In BPF ecosystem, BTF is the debuginfo used for validation and additional information. https://www.kernel.org/doc/html/latest/bpf/btf.html Currently, BTF in vmlinux (x86_64, aarch64, etc.) are generated by using pahole to convert dwarf to BTF and vmlinux BTF is used to validate bpf program compliance, e.g., bpf program signature must match kernel function signature for certain tracing programs. Beyond signature checking, the following are use cases which will further help verifier. 1. annotation of "user"/"rcu" etc for function arguments, structure fields and global/static variables. Kernel currently uses `address_space` attributes for `sparse` tool. But we could like to carry this information to debuginfo. Previous attempt https://reviews.llvm.org/D69393 tries to use `address_space` which is halted as it needs to touch a lot of other llvm places. 2. annotation of functions. Currently, kernel tries to group them with separate logic, e.g., foo() __attribute__("property1", "property2") since the above attribute is not supported, kernel has to do some magic like global btf_property1: btf type id for foo, ... global btf_property2: btf type id for foo, ... this is really error prone as the function definition may be under some configs and the `global btf_property1 ...` may not even in the same source file as the function. Such a disconnect between function definition and function attributes already caused numerous issues. We also want to annotate functions with certain pre-conditions (e.g., a socket lock has been hold), as bpf programs has started to call kernel functions. Such annotations should be really directly applied to the function definition to avoid any potential later mismatch issues. 3. annotation of structures, e.g., if somehow this structure fields may have been randomized, verifier should know it as it cannot trust debuginfo structure layout any more. Sorry for tense explanation of use cases. The main takeaway is we want to annotate structure/field/func/argument/variable with *arbitrary* strings and want such strings to be preserved in final dwarf (or BTF) output. An Example ========== In this patch, I hacked clang Frontend to put annotations in debuginfo and hacked llvm/CodeGen to "output" these annotations into BTF. The target architecture is x86. Note that I didn't really output these attributes to BTF yet. I would like to seek llvm community advise first. Below is an example to show what the source code looks like. I am using "annotate" attribute as it accepts arbitrary strings. $ cat t1.c /* a pointer pointing to user memory */ #define __user __attribute__((annotate("user"))) /* a pointer protected by rcu */ #define __rcu __attribute__((annotate("rcu"))) /* the struct has some special property */ #define __special_struct __attribute__((annotate("special_struct"))) /* sock_lock is held for the function */ #define __sock_lock_held __attribute((annotate("sock_lock_held"))) /* the hash table element type is socket */ #define __special_info __attribute__((annotate("elem_type:socket"))) struct hlist_node; struct hlist_head { struct hlist_node *prev; struct hlist_node *next; } __special_struct; struct hlist { struct hlist_head head __special_info; }; extern void bar(struct hlist *); int foo(struct hlist *h, int *a __user, int *b __rcu) __sock_lock_held { bar(h); return *a + *b; } $ clang --target x86_64 -O2 -c -g t1.c TODO (BTF2Debug.cpp): Add func arg 'a' annotation 'user' to .BTF section TODO (BTF2Debug.cpp): Add func arg 'b' annotation 'rcu' to .BTF section TODO (BTF2Debug.cpp): Add subroutine 'foo' annotation 'sock_lock_held' to .BTF section TODO (BTF2Debug.cpp): Add field 'head' annotation 'elem_type:socket' to .BTF section TODO (BTF2Debug.cpp): Add struct 'hlist_head' annotation 'special_struct' to .BTF section $ What Is Next ============ First, using "annotate" attribute is not the best choice as I generated extra globals and IRs. Maybe a different clang specific attribute? Second, in the above example, I tried to put these attributes in BTF as I researched and didn't find a way to put these attributes in dwarf. Do we have a way to put it into dwarf? That works for us too. Otherwise, we can let x86/arm64 etc. generates BTF (with a flag of course) which will have these attribute information. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D103549 Files: clang/lib/CodeGen/CGDebugInfo.cpp llvm/include/llvm/IR/DebugInfoMetadata.h llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp llvm/lib/CodeGen/AsmPrinter/BTF2Debug.cpp llvm/lib/CodeGen/AsmPrinter/BTF2Debug.h llvm/lib/CodeGen/AsmPrinter/CMakeLists.txt
Index: llvm/lib/CodeGen/AsmPrinter/CMakeLists.txt =================================================================== --- llvm/lib/CodeGen/AsmPrinter/CMakeLists.txt +++ llvm/lib/CodeGen/AsmPrinter/CMakeLists.txt @@ -6,6 +6,7 @@ AsmPrinter.cpp AsmPrinterDwarf.cpp AsmPrinterInlineAsm.cpp + BTF2Debug.cpp DbgEntityHistoryCalculator.cpp DebugHandlerBase.cpp DebugLocStream.cpp Index: llvm/lib/CodeGen/AsmPrinter/BTF2Debug.h =================================================================== --- /dev/null +++ llvm/lib/CodeGen/AsmPrinter/BTF2Debug.h @@ -0,0 +1,295 @@ +//===- BTF2Debug.h ----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains support for writing BTF debug info. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_CODEGEN_ASMPRINTER_BTFDEBUG_H +#define LLVM_LIB_cODEGEN_ASMPRINTER_BTFDEBUG_H + +#include "llvm/CodeGen/DebugHandlerBase.h" +#include <map> +#include <vector> + +namespace llvm { + +class AsmPrinter; +class BTF2Debug; +class MachineInstr; +class MCStreamer; + +namespace BTF2 { +enum : uint32_t { MAGIC = 0xeB9F, VERSION = 1 }; +enum { + HeaderSize = 24, + CommonTypeSize = 12, + BTFArraySize = 12, + BTFEnumSize = 8, + BTFMemberSize = 12, + BTFParamSize = 8, + BTFDataSecVarSize = 12, +}; +enum TypeKinds : uint8_t { + BTF_KIND_UNKN = 0, + BTF_KIND_INT = 1, + BTF_KIND_PTR = 2, + BTF_KIND_ARRAY = 3, + BTF_KIND_STRUCT= 4, + BTF_KIND_UNION = 5, + BTF_KIND_ENUM = 6, + BTF_KIND_FWD = 7, + BTF_KIND_TYPEDEF = 8, + BTF_KIND_VOLATILE = 9, + BTF_KIND_CONST = 10, + BTF_KIND_RESTRICT = 11, + BTF_KIND_FUNC = 12, + BTF_KIND_FUNC_PROTO = 13, + BTF_KIND_VAR = 14, + BTF_KIND_DATASEC = 15, + BTF_KIND_FLOAT = 16, +}; + +struct CommonType { + uint32_t NameOff; + uint32_t Info; + union { + uint32_t Size; + uint32_t Type; + }; +}; + +enum : uint8_t { + INT_SIGNED = (1 << 0), + INT_CHAR = (1 << 1), + INT_BOOL = (1 << 2) +}; + +struct BTFEnum { + uint32_t NameOff; + int32_t Val; +}; + +struct BTFArray { + uint32_t ElemType; + uint32_t IndexType; + uint32_t Nelems; +}; + +struct BTFMember { + uint32_t NameOff; ///< Member name offset in the string table + uint32_t Type; ///< Member type + uint32_t Offset; ///< BitOffset or BitFieldSize+BitOffset +}; + +struct BTFParam { + uint32_t NameOff; + uint32_t Type; +}; + +enum : uint8_t { + FUNC_STATIC = 0, + FUNC_GLOBAL = 1, + FUNC_EXTERN = 2, +}; + +enum : uint8_t { + VAR_STATIC = 0, ///< Linkage: InternalLinkage + VAR_GLOBAL_ALLOCATED = 1, ///< Linkage: ExternalLinkage + VAR_GLOBAL_EXTERNAL = 2, ///< Linkage: ExternalLinkage +}; + +struct BTFDataSec { + uint32_t Type; ///< A BTF_KIND_VAR type + uint32_t Offset; ///< In-section offset + uint32_t Size; ///< Occupied memory size +}; + +} + +class BTF2TypeBase { +protected: + uint8_t Kind; + uint32_t Id; + struct BTF2::CommonType BTFType; + +public: + BTF2TypeBase() {} + virtual ~BTF2TypeBase() = default; + void setId(uint32_t Id) { this->Id = Id; } + void setType(uint32_t Type) { BTFType.Type = Type; } + uint32_t roundupToBytes(uint32_t NumBits) { return (NumBits + 7) >> 3; } + virtual uint32_t getSize() { return BTF2::CommonTypeSize; } + virtual void emitType(MCStreamer &OS); +}; + +class BTF2TypeInt : public BTF2TypeBase { + uint32_t IntVal; ///< Encoding, offset, bits + +public: + BTF2TypeInt(uint32_t Encoding, uint32_t SizeInBits, uint32_t OffsetInBits, + uint32_t NameOff); + uint32_t getSize() override { return BTF2TypeBase::getSize() + sizeof(uint32_t); } + void emitType(MCStreamer &OS) override; +}; + +class BTF2TypeDerived : public BTF2TypeBase { +public: + BTF2TypeDerived(const DIDerivedType *Ty, uint32_t NameOff); +}; + +class BTF2TypeFwd : public BTF2TypeBase { +public: + BTF2TypeFwd(uint32_t NameOff, bool IsUnion); +}; + +class BTF2TypeEnum : public BTF2TypeBase { +public: + std::vector<struct BTF2::BTFEnum> EnumValues; + + BTF2TypeEnum(const DICompositeType *ETy, uint32_t NameOff, uint32_t NumValues); + uint32_t getSize() override { + return BTF2TypeBase::getSize() + EnumValues.size() * BTF2::BTFEnumSize; + } + void emitType(MCStreamer &OS) override; +}; + +class BTF2TypeArray : public BTF2TypeBase { +public: + struct BTF2::BTFArray ArrayInfo; + + BTF2TypeArray(uint32_t ElemTypeId, uint32_t IndexTypeId, uint32_t NumElems); + uint32_t getSize() override { return BTF2TypeBase::getSize() + BTF2::BTFArraySize; } + void emitType(MCStreamer &OS) override; +}; + +class BTF2TypeStruct : public BTF2TypeBase { +public: + std::vector<struct BTF2::BTFMember> Members; + + BTF2TypeStruct(const DICompositeType *STy, bool IsStruct, bool HasBitField, + uint32_t NameOff, uint32_t NumMembers); + uint32_t getSize() override { + return BTF2TypeBase::getSize() + Members.size() * BTF2::BTFMemberSize; + } + void emitType(MCStreamer &OS) override; +}; + +class BTF2TypeFuncProto : public BTF2TypeBase { +public: + std::vector<struct BTF2::BTFParam> Parameters; + + BTF2TypeFuncProto(uint32_t NumParams); + uint32_t getSize() override { + return BTF2TypeBase::getSize() + Parameters.size() * BTF2::BTFParamSize; + } + void emitType(MCStreamer &OS) override; +}; + +class BTF2TypeFunc : public BTF2TypeBase { +public: + BTF2TypeFunc(uint32_t NameOff, uint32_t ProtoTypeId, uint32_t Scope); +}; + +class BTF2KindVar : public BTF2TypeBase { + uint32_t Info; + +public: + BTF2KindVar(uint32_t NameOff, uint32_t TypeId, uint32_t VarInfo); + uint32_t getSize() override { return BTF2TypeBase::getSize() + 4; } + void emitType(MCStreamer &OS) override; +}; + +class BTF2KindDataSec : public BTF2TypeBase { + AsmPrinter *Asm; + std::vector<std::tuple<uint32_t, const MCSymbol *, uint32_t>> Vars; + +public: + BTF2KindDataSec(AsmPrinter *AsmPrt, uint32_t SecNameOff); + uint32_t getSize() override { + return BTF2TypeBase::getSize() + BTF2::BTFDataSecVarSize * Vars.size(); + } + void addDataSecEntry(uint32_t Id, const MCSymbol *Sym, uint32_t Size) { + Vars.push_back(std::make_tuple(Id, Sym, Size)); + BTFType.Info += 1; + } + void emitType(MCStreamer &OS) override; +}; + +class BTF2TypeFloat : public BTF2TypeBase { +public: + BTF2TypeFloat(uint32_t SizeInBits, uint32_t NameOff); +}; + +/// String table. +class BTF2StringTable { + uint32_t Size; + std::map<uint32_t, uint32_t> OffsetToIdMap; + std::vector<std::string> Table; + +public: + BTF2StringTable() : Size(0) {} + uint32_t getSize() { return Size; } + std::vector<std::string> &getTable() { return Table; } + uint32_t addString(StringRef S); + void padStringTable(uint32_t PadLen); +}; + +class BTF2Debug : public DebugHandlerBase { +private: + MCStreamer &OS; + std::map<const DIType *, uint32_t> DIToIdMap; + std::vector<std::unique_ptr<BTF2TypeBase>> TypeEntries; + std::map<std::string, std::unique_ptr<BTF2KindDataSec>> DataSecEntries; + uint32_t ArrayIndexTypeId; + BTF2StringTable StringTable; + + uint32_t calcBasicType(const DIBasicType *BTy); + uint32_t calcSubroutineType(const DISubroutineType *STy, bool ForSubprog, + std::map<uint32_t, StringRef> &FuncArgNames); + uint32_t calcFwdDeclType(const DICompositeType *CTy, bool IsUnion); + uint32_t calcStructType(const DICompositeType *STy, bool IsStruct); + uint32_t calcArrayType(const DICompositeType *CTy); + uint32_t calcEnumType(const DICompositeType *ETy); + uint32_t calcCompositeType(const DICompositeType *CTy); + uint32_t calcDerivedType(const DIDerivedType *DTy); + uint32_t getOrCalcTypeEntry(const DIType *Ty); + + uint32_t addType(std::unique_ptr<BTF2TypeBase> TypeEntry, const DIType *Ty); + void emitBTFSection(); + +protected: + void beginFunctionImpl(const MachineFunction *MF) override {} + void endFunctionImpl(const MachineFunction *MF) override {} + +public: + BTF2Debug(AsmPrinter *AP); + + uint32_t getArrayIndexTypeId() { + assert(ArrayIndexTypeId); + return ArrayIndexTypeId; + } + + size_t addString(StringRef S) { return StringTable.addString(S); } + void padStringTable(uint32_t PadLen) { StringTable.padStringTable(PadLen); } + + uint32_t getTypeId(const DIType *Ty) { + assert(Ty && "Invalid null Type"); + assert(DIToIdMap.find(Ty) != DIToIdMap.end() && + "DIType not added in the BDIToIdMap"); + return DIToIdMap[Ty]; + } + + void setSymbolSize(const MCSymbol *Symbol, uint64_t Size) override {} + void endModule() override; +}; + +} // end namespace llvm + +#endif Index: llvm/lib/CodeGen/AsmPrinter/BTF2Debug.cpp =================================================================== --- /dev/null +++ llvm/lib/CodeGen/AsmPrinter/BTF2Debug.cpp @@ -0,0 +1,645 @@ +//===----- llvm/lib/CodeGen/AsmPrinter/BPFDebug.cpp -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains support for writing BTF debug info. +// +//===----------------------------------------------------------------------===// + +#include "BTF2Debug.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCSectionELF.h" +#include "llvm/MC/MCStreamer.h" + +using namespace llvm; + +static const char *BTFKindStr[] = { + "BTF_KIND_UNKN", + "BTF_KIND_INT", + "BTF_KIND_PTR", + "BTF_KIND_ARRAY", + "BTF_KIND_STRUCT", + "BTF_KIND_UNION", + "BTF_KIND_ENUM", + "BTF_KIND_FWD", + "BTF_KIND_TYPEDEF", + "BTF_KIND_VOLATILE", + "BTF_KIND_CONST", + "BTF_KIND_RESTRICT", + "BTF_KIND_FUNC", + "BTF_KIND_FUNC_PROTO", + "BTF_KIND_VAR", + "BTF_KIND_DATASEC", + "BTF_KIND_FLOAT", +}; + +/// Emit a BTF common type. +void BTF2TypeBase::emitType(MCStreamer &OS) { + OS.AddComment(std::string(BTFKindStr[Kind]) + "(id = " + std::to_string(Id) + ")"); + OS.emitInt32(BTFType.NameOff); + OS.AddComment("0x" + Twine::utohexstr(BTFType.Info)); + OS.emitInt32(BTFType.Info); + OS.emitInt32(BTFType.Size); +} + +BTF2TypeInt::BTF2TypeInt(uint32_t Encoding, uint32_t SizeInBits, + uint32_t OffsetInBits, uint32_t NameOff) { + // Translate IR int encoding to BTF int encoding. + uint8_t BTFEncoding; + switch (Encoding) { + case dwarf::DW_ATE_boolean: + BTFEncoding = BTF2::INT_BOOL; + break; + case dwarf::DW_ATE_signed: + case dwarf::DW_ATE_signed_char: + BTFEncoding = BTF2::INT_SIGNED; + break; + case dwarf::DW_ATE_unsigned: + case dwarf::DW_ATE_unsigned_char: + BTFEncoding = 0; + break; + default: + llvm_unreachable("Unknown BTF2TypeInt Encoding"); + } + + Kind = BTF2::BTF_KIND_INT; + BTFType.NameOff = NameOff; + BTFType.Info = Kind << 24; + BTFType.Size = roundupToBytes(SizeInBits); + IntVal = (BTFEncoding << 24) | OffsetInBits << 16 | SizeInBits; +} + +void BTF2TypeInt::emitType(MCStreamer &OS) { + BTF2TypeBase::emitType(OS); + OS.AddComment("0x" + Twine::utohexstr(IntVal)); + OS.emitInt32(IntVal); +} + +BTF2TypeDerived::BTF2TypeDerived(const DIDerivedType *DTy, uint32_t NameOff) { + unsigned Tag = DTy->getTag(); + switch (Tag) { + case dwarf::DW_TAG_pointer_type: + Kind = BTF2::BTF_KIND_PTR; + break; + case dwarf::DW_TAG_const_type: + Kind = BTF2::BTF_KIND_CONST; + break; + case dwarf::DW_TAG_volatile_type: + Kind = BTF2::BTF_KIND_VOLATILE; + break; + case dwarf::DW_TAG_typedef: + Kind = BTF2::BTF_KIND_TYPEDEF; + break; + case dwarf::DW_TAG_restrict_type: + Kind = BTF2::BTF_KIND_RESTRICT; + break; + default: + llvm_unreachable("Unknown DIDerivedType Tag"); + } + BTFType.NameOff = NameOff; + BTFType.Info = Kind << 24; +} + +BTF2TypeFwd::BTF2TypeFwd(uint32_t NameOff, bool IsUnion) { + Kind = BTF2::BTF_KIND_FWD; + BTFType.NameOff = NameOff; + BTFType.Info = IsUnion << 31 | Kind << 24; + BTFType.Type = 0; +} + +BTF2TypeEnum::BTF2TypeEnum(const DICompositeType *ETy, uint32_t NameOff, uint32_t VLen) { + Kind = BTF2::BTF_KIND_ENUM; + BTFType.NameOff = NameOff; + BTFType.Info = Kind << 24 | VLen; + BTFType.Size = roundupToBytes(ETy->getSizeInBits()); +} + +void BTF2TypeEnum::emitType(MCStreamer &OS) { + BTF2TypeBase::emitType(OS); + for (const auto &Enum : EnumValues) { + OS.emitInt32(Enum.NameOff); + OS.emitInt32(Enum.Val); + } +} + +BTF2TypeArray::BTF2TypeArray(uint32_t ElemTypeId, uint32_t IndexTypeId, uint32_t NumElems) { + Kind = BTF2::BTF_KIND_ARRAY; + BTFType.NameOff = 0; + BTFType.Info = Kind << 24; + BTFType.Size = 0; + + ArrayInfo.ElemType = ElemTypeId; + ArrayInfo.IndexType = IndexTypeId; + ArrayInfo.Nelems = NumElems; +} + +void BTF2TypeArray::emitType(MCStreamer &OS) { + BTF2TypeBase::emitType(OS); + OS.emitInt32(ArrayInfo.ElemType); + OS.emitInt32(ArrayInfo.IndexType); + OS.emitInt32(ArrayInfo.Nelems); +} + +BTF2TypeStruct::BTF2TypeStruct(const DICompositeType *STy, bool IsStruct, + bool HasBitField, uint32_t NameOff, + uint32_t Vlen) { + Kind = IsStruct ? BTF2::BTF_KIND_STRUCT : BTF2::BTF_KIND_UNION; + BTFType.NameOff = NameOff; + BTFType.Info = (HasBitField << 31) | (Kind << 24) | Vlen; + BTFType.Size = roundupToBytes(STy->getSizeInBits()); +} + +void BTF2TypeStruct::emitType(MCStreamer &OS) { + BTF2TypeBase::emitType(OS); + for (const auto &Member : Members) { + OS.emitInt32(Member.NameOff); + OS.emitInt32(Member.Type); + OS.AddComment("0x" + Twine::utohexstr(Member.Offset)); + OS.emitInt32(Member.Offset); + } +} + +BTF2TypeFuncProto::BTF2TypeFuncProto(uint32_t VLen) { + Kind = BTF2::BTF_KIND_FUNC_PROTO; + BTFType.NameOff = 0; + BTFType.Info = (Kind << 24) | VLen; +} + +void BTF2TypeFuncProto::emitType(MCStreamer &OS) { + BTF2TypeBase::emitType(OS); + for (const auto &Param : Parameters) { + OS.emitInt32(Param.NameOff); + OS.emitInt32(Param.Type); + } +} + +BTF2TypeFunc::BTF2TypeFunc(uint32_t NameOff, uint32_t ProtoTypeId, + uint32_t Scope) { + BTFType.NameOff = NameOff; + Kind = BTF2::BTF_KIND_FUNC; + BTFType.Info = (Kind << 24) | Scope; + BTFType.Type = ProtoTypeId; +} + +BTF2KindVar::BTF2KindVar(uint32_t NameOff, uint32_t TypeId, uint32_t VarInfo) { + Kind = BTF2::BTF_KIND_VAR; + BTFType.NameOff = NameOff; + BTFType.Info = Kind << 24; + BTFType.Type = TypeId; + Info = VarInfo; +} + +void BTF2KindVar::emitType(MCStreamer &OS) { + BTF2TypeBase::emitType(OS); + OS.emitInt32(Info); +} + +BTF2KindDataSec::BTF2KindDataSec(AsmPrinter *AsmPrt, uint32_t SecNameOff) + : Asm(AsmPrt) { + Kind = BTF2::BTF_KIND_DATASEC; + BTFType.NameOff = SecNameOff; + BTFType.Info = Kind << 24; + BTFType.Size = 0; +} + +void BTF2KindDataSec::emitType(MCStreamer &OS) { + BTF2TypeBase::emitType(OS); + + for (const auto &V : Vars) { + OS.emitInt32(std::get<0>(V)); + Asm->emitLabelReference(std::get<1>(V), 4); + OS.emitInt32(std::get<2>(V)); + } +} + +BTF2TypeFloat::BTF2TypeFloat(uint32_t SizeInBits, uint32_t NameOff) { + Kind = BTF2::BTF_KIND_FLOAT; + BTFType.NameOff = NameOff; + BTFType.Info = Kind << 24; + BTFType.Size = roundupToBytes(SizeInBits); +} + +uint32_t BTF2StringTable::addString(StringRef S) { + for (auto &OffsetM : OffsetToIdMap) { + if (Table[OffsetM.second] == S) + return OffsetM.first; + } + uint32_t Offset = Size; + OffsetToIdMap[Offset] = Table.size(); + Table.push_back(std::string(S)); + Size += S.size() + 1; + return Offset; +} + +void BTF2StringTable::padStringTable(uint32_t PadLen) { + for (uint32_t i = 0; i < PadLen; i++) + Table.push_back(std::string("\0")); + Size += PadLen; +} + +BTF2Debug::BTF2Debug(AsmPrinter *AP) + : DebugHandlerBase(AP), OS(*Asm->OutStreamer), + ArrayIndexTypeId(0) { + addString("\0"); +} + +uint32_t BTF2Debug::addType(std::unique_ptr<BTF2TypeBase> TypeEntry, + const DIType *Ty) { + uint32_t Id = TypeEntries.size() + 1; + TypeEntry->setId(Id); + if (Ty != nullptr) + DIToIdMap[Ty] = Id; + TypeEntries.push_back(std::move(TypeEntry)); + return Id; +} + +uint32_t BTF2Debug::calcBasicType(const DIBasicType *BTy) { + uint32_t Encoding = BTy->getEncoding(); + std::unique_ptr<BTF2TypeBase> TypeEntry; + switch (Encoding) { + case dwarf::DW_ATE_boolean: + case dwarf::DW_ATE_signed: + case dwarf::DW_ATE_signed_char: + case dwarf::DW_ATE_unsigned: + case dwarf::DW_ATE_unsigned_char: + TypeEntry = std::make_unique<BTF2TypeInt>( + Encoding, BTy->getSizeInBits(), BTy->getOffsetInBits(), + addString(BTy->getName())); + break; + case dwarf::DW_ATE_float: + TypeEntry = + std::make_unique<BTF2TypeFloat>(BTy->getSizeInBits(), + addString(BTy->getName())); + break; + default: + llvm_unreachable("Unknown BTF2TypeInt Encoding"); + } + + return addType(std::move(TypeEntry), BTy); +} + +uint32_t BTF2Debug::calcFwdDeclType(const DICompositeType *CTy, bool IsUnion) { + auto TypeEntry = std::make_unique<BTF2TypeFwd>(addString(CTy->getName()), IsUnion); + return addType(std::move(TypeEntry), CTy); +} + +uint32_t BTF2Debug::calcStructType(const DICompositeType *CTy, bool IsStruct) { + const DINodeArray Elements = CTy->getElements(); + uint32_t VLen = Elements.size(); + + // Check whether we have any bitfield members or not + bool HasBitField = false; + for (const auto *Element : Elements) { + auto E = cast<DIDerivedType>(Element); + if (E->isBitField()) { + HasBitField = true; + break; + } + } + + auto TypeEntry = + std::make_unique<BTF2TypeStruct>(CTy, IsStruct, HasBitField, + addString(CTy->getName()), VLen); + BTF2TypeStruct *StructEntry = TypeEntry.get(); + uint32_t TypeId = addType(std::move(TypeEntry), CTy); + + for (const auto &Annotation : CTy->getAnnotations()) { + fprintf(stderr, "TODO (BTF2Debug.cpp): Add %s '%s' annotation '%s' to .BTF section\n", + IsStruct? "struct" : "union", + CTy->getName().str().c_str(), Annotation.str().c_str()); + } + + // Visit all struct members. + for (const auto *Element : Elements) { + struct BTF2::BTFMember BTFMember; + const auto *DDTy = cast<DIDerivedType>(Element); + + for (const auto &Annotation : DDTy->getAnnotations()) { + fprintf(stderr, "TODO (BTF2Debug.cpp): Add field '%s' annotation '%s' to .BTF section\n", + DDTy->getName().str().c_str(), Annotation.str().c_str()); + } + + BTFMember.NameOff = addString(DDTy->getName()); + if (HasBitField) { + uint8_t BitFieldSize = DDTy->isBitField() ? DDTy->getSizeInBits() : 0; + BTFMember.Offset = BitFieldSize << 24 | DDTy->getOffsetInBits(); + } else { + BTFMember.Offset = DDTy->getOffsetInBits(); + } + const auto *BaseTy = DDTy->getBaseType(); + BTFMember.Type = getOrCalcTypeEntry(BaseTy); + StructEntry->Members.push_back(BTFMember); + } + + return TypeId; +} + +uint32_t BTF2Debug::calcArrayType(const DICompositeType *CTy) { + if (!ArrayIndexTypeId) { + auto TypeEntry = std::make_unique<BTF2TypeInt>(dwarf::DW_ATE_unsigned, 32, + 0, addString("__ARRAY_SIZE_TYPE__")); + ArrayIndexTypeId = addType(std::move(TypeEntry), nullptr); + } + + const DIType *ElemType = CTy->getBaseType(); + uint32_t ElemTypeId = getOrCalcTypeEntry(ElemType); + + std::unique_ptr<BTF2TypeArray> TypeEntry; + + // Visit array dimensions. + DINodeArray Elements = CTy->getElements(); + for (int I = Elements.size() - 1; I >= 0; --I) { + if (auto *Element = dyn_cast_or_null<DINode>(Elements[I])) + if (Element->getTag() == dwarf::DW_TAG_subrange_type) { + const DISubrange *SR = cast<DISubrange>(Element); + auto *CI = SR->getCount().dyn_cast<ConstantInt *>(); + int64_t Count = CI->getSExtValue(); + + // For struct s { int b; char c[]; }, the c[] will be represented + // as an array with Count = -1. + TypeEntry = + std::make_unique<BTF2TypeArray>(ElemTypeId, + ArrayIndexTypeId, + Count >= 0 ? Count : 0); + if (I == 0) + ElemTypeId = addType(std::move(TypeEntry), CTy); + else + ElemTypeId = addType(std::move(TypeEntry), nullptr); + } + } + + return ElemTypeId; +} + +uint32_t BTF2Debug::calcEnumType(const DICompositeType *CTy) { + DINodeArray Elements = CTy->getElements(); + uint32_t VLen = Elements.size(); + + auto TypeEntry = std::make_unique<BTF2TypeEnum>(CTy, addString(CTy->getName()), VLen); + BTF2TypeEnum *EnumType = TypeEntry.get(); + uint32_t TypeId = addType(std::move(TypeEntry), CTy); + + for (const auto Element : Elements) { + const auto *Enum = cast<DIEnumerator>(Element); + + struct BTF2::BTFEnum BTFEnum; + BTFEnum.NameOff = addString(Enum->getName()); + uint32_t Value; + if (Enum->isUnsigned()) + Value = static_cast<uint32_t>(Enum->getValue().getZExtValue()); + else + Value = static_cast<uint32_t>(Enum->getValue().getSExtValue()); + BTFEnum.Val = Value; + EnumType->EnumValues.push_back(BTFEnum); + } + + return TypeId; +} + +uint32_t BTF2Debug::calcSubroutineType(const DISubroutineType *STy, + bool ForSubprog, + std::map<uint32_t, StringRef> &FuncArgNames) { + DITypeRefArray Elements = STy->getTypeArray(); + uint32_t VLen = Elements.size() - 1; + + // Subprogram has a valid non-zero-length name, and the pointee of + // a function pointer has an empty name. The subprogram type will + // not be added to DIToIdMap as it should not be referenced by + // any other types. + uint32_t TypeId; + auto TypeEntry = std::make_unique<BTF2TypeFuncProto>(VLen); + BTF2TypeFuncProto *FuncProtoType = TypeEntry.get(); + if (ForSubprog) + TypeId = addType(std::move(TypeEntry), nullptr); // For subprogram + else + TypeId = addType(std::move(TypeEntry), STy); // For func ptr + + auto RetType = Elements[0]; + FuncProtoType->setType(RetType ? getOrCalcTypeEntry(RetType) : 0); + + // For null parameter which is typically the last one + // to represent the vararg, encode the NameOff/Type to be 0. + for (unsigned I = 1, N = Elements.size(); I < N; ++I) { + struct BTF2::BTFParam Param; + auto Element = Elements[I]; + if (Element) { + Param.NameOff = addString(FuncArgNames[I]); + Param.Type = getOrCalcTypeEntry(Element); + } else { + Param.NameOff = 0; + Param.Type = 0; + } + FuncProtoType->Parameters.push_back(Param); + } + + return TypeId; +} + +uint32_t BTF2Debug::calcCompositeType(const DICompositeType *CTy) { + auto Tag = CTy->getTag(); + if (Tag == dwarf::DW_TAG_structure_type || Tag == dwarf::DW_TAG_union_type) { + // Handle forward declaration differently as it does not have members. + if (CTy->isForwardDecl()) + return calcFwdDeclType(CTy, Tag == dwarf::DW_TAG_union_type); + else + return calcStructType(CTy, Tag == dwarf::DW_TAG_structure_type); + } else if (Tag == dwarf::DW_TAG_array_type) + return calcArrayType(CTy); + else if (Tag == dwarf::DW_TAG_enumeration_type) + return calcEnumType(CTy); + + llvm_unreachable("Unknown Composite Type"); +} + +uint32_t BTF2Debug::calcDerivedType(const DIDerivedType *DTy) { + unsigned Tag = DTy->getTag(); + if (Tag != dwarf::DW_TAG_pointer_type && Tag != dwarf::DW_TAG_typedef && + Tag != dwarf::DW_TAG_const_type && Tag != dwarf::DW_TAG_volatile_type && + Tag != dwarf::DW_TAG_restrict_type) + return 0; + + auto TypeEntry = std::make_unique<BTF2TypeDerived>(DTy, addString(DTy->getName())); + BTF2TypeDerived *DerivedType = TypeEntry.get(); + uint32_t TypeId = addType(std::move(TypeEntry), DTy); + + uint32_t BaseTypeId; + const DIType *ResolvedType = DTy->getBaseType(); + if (!ResolvedType) { + assert((Tag == dwarf::DW_TAG_pointer_type || Tag == dwarf::DW_TAG_const_type || + Tag == dwarf::DW_TAG_volatile_type) && + "Invalid null basetype"); + BaseTypeId = 0; + } else { + BaseTypeId = getOrCalcTypeEntry(ResolvedType); + } + DerivedType->setType(BaseTypeId); + + return TypeId; +} + +uint32_t BTF2Debug::getOrCalcTypeEntry(const DIType *Ty) { + if (!Ty || DIToIdMap.find(Ty) != DIToIdMap.end()) { + return DIToIdMap[Ty]; + } + + if (const auto *BTy = dyn_cast<DIBasicType>(Ty)) + return calcBasicType(BTy); + else if (const auto *STy = dyn_cast<DISubroutineType>(Ty)) { + std::map<uint32_t, StringRef> FuncArgNames; + return calcSubroutineType(STy, false, FuncArgNames); + } else if (const auto *CTy = dyn_cast<DICompositeType>(Ty)) + return calcCompositeType(CTy); + else if (const auto *DTy = dyn_cast<DIDerivedType>(Ty)) + return calcDerivedType(DTy); + + llvm_unreachable("Unknown DIType"); +} + +void BTF2Debug::emitBTFSection() { + if (!TypeEntries.size() && StringTable.getSize() == 1) + return; + + MCContext &Ctx = OS.getContext(); + OS.SwitchSection(Ctx.getELFSection(".BTF", ELF::SHT_PROGBITS, ELF::SHF_ALLOC)); + + // Emit header + OS.AddComment("0x" + Twine::utohexstr(BTF2::MAGIC)); + OS.emitIntValue(BTF2::MAGIC, 2); + OS.emitInt8(BTF2::VERSION); + OS.emitInt8(0); + OS.emitInt32(BTF2::HeaderSize); + + uint32_t TypeLen = 0, StrLen; + for (const auto &TypeEntry : TypeEntries) + TypeLen += TypeEntry->getSize(); + StrLen = StringTable.getSize(); + + OS.emitInt32(0); + OS.emitInt32(TypeLen); + OS.emitInt32(TypeLen); + OS.emitInt32(StrLen); + + for (const auto &TypeEntry : TypeEntries) + TypeEntry->emitType(OS); + + uint32_t StringOffset = 0; + for (const auto &S : StringTable.getTable()) { + OS.AddComment("string offset=" + std::to_string(StringOffset)); + OS.emitBytes(S); + OS.emitBytes(StringRef("\0", 1)); + StringOffset += S.size() + 1; + } +} + +void BTF2Debug::endModule() { + if (!Asm || !MMI->hasDebugInfo()) + return; + + const Module *M = MMI->getModule(); + + // Types may not be referenced by globals and functions + for (DICompileUnit *CUNode : M->debug_compile_units()) { + for (auto *Ty : CUNode->getEnumTypes()) { + getOrCalcTypeEntry(cast<DIType>(Ty)); + } + for (auto *Ty : CUNode->getRetainedTypes()) { + getOrCalcTypeEntry(dyn_cast<DIType>(Ty)); + } + } + + for (const GlobalVariable &Global: M->globals()) { + auto Linkage = Global.getLinkage(); + uint32_t GVarInfo; + if (Linkage == GlobalValue::InternalLinkage) + GVarInfo = BTF2::VAR_STATIC; + else if (Global.hasInitializer()) + GVarInfo = BTF2::VAR_GLOBAL_ALLOCATED; + else + continue; + + SmallVector<DIGlobalVariableExpression *, 1> GVs; + Global.getDebugInfo(GVs); + if (GVs.size() == 0) + continue; + + uint32_t GVTypeId; + for (auto *GVE : GVs) { + GVTypeId = getOrCalcTypeEntry(GVE->getVariable()->getType()); + break; + } + + auto VarEntry = + std::make_unique<BTF2KindVar>(addString(Global.getName()), GVTypeId, GVarInfo); + uint32_t VarId = addType(std::move(VarEntry), nullptr); + + StringRef SecName = Global.getSection(); + if (SecName != ".data..percpu") + continue; + + if (DataSecEntries.find(std::string(SecName)) == DataSecEntries.end()) { + DataSecEntries[std::string(SecName)] = + std::make_unique<BTF2KindDataSec>(Asm, addString(SecName)); + } + + const DataLayout &DL = Global.getParent()->getDataLayout(); + uint32_t Size = DL.getTypeAllocSize(Global.getType()->getElementType()); + + DataSecEntries[std::string(SecName)]->addDataSecEntry(VarId, + Asm->getSymbol(&Global), Size); + } + + for (const Function &Func: M->functions()) { + const DISubprogram *SP = Func.getSubprogram(); + if (!SP || !SP->isDefinition()) + continue; + + std::map<uint32_t, StringRef> FuncArgNames; + for (const DINode *DN : SP->getRetainedNodes()) { + if (const auto *DV = dyn_cast<DILocalVariable>(DN)) { + // Collect function arguments for subprogram func type. + uint32_t Arg = DV->getArg(); + if (!Arg) + continue; + + FuncArgNames[Arg] = DV->getName(); + + for (const auto &Annotation : DV->getAnnotations()) { + fprintf(stderr, "TODO (BTF2Debug.cpp): Add func arg '%s' annotation '%s' to .BTF section\n", + DV->getName().str().c_str(), Annotation.str().c_str()); + } + } + } + + uint32_t ProtoTypeId; + const auto *STy = cast<DISubroutineType>(SP->getType()); + for (const auto &Annotation : STy->getAnnotations()) { + fprintf(stderr, "TODO (BTF2Debug.cpp): Add subroutine '%s' annotation '%s' to .BTF section\n", + SP->getName().str().c_str(), Annotation.str().c_str()); + } + + ProtoTypeId = calcSubroutineType(STy, true, FuncArgNames); + + uint8_t Scope = SP->isLocalToUnit() ? BTF2::FUNC_STATIC : BTF2::FUNC_GLOBAL; + auto FuncTypeEntry = + std::make_unique<BTF2TypeFunc>(addString(SP->getName()), ProtoTypeId, Scope); + addType(std::move(FuncTypeEntry), nullptr); + } + + for (auto &DataSec : DataSecEntries) + addType(std::move(DataSec.second), nullptr); + + // Make String section 4-byte aligned. + uint32_t StrLen = StringTable.getSize(); + uint32_t PadLen = ((StrLen + 3) & ~0x3) - StrLen; + if (PadLen) + padStringTable(PadLen); + + emitBTFSection(); +} Index: llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "llvm/CodeGen/AsmPrinter.h" +#include "BTF2Debug.h" #include "CodeViewDebug.h" #include "DwarfDebug.h" #include "DwarfException.h" @@ -154,6 +155,8 @@ const char PPTimerDescription[] = "Pseudo Probe Emission"; const char PPGroupName[] = "pseudo probe"; const char PPGroupDescription[] = "Pseudo Probe Emission"; +const char BTFGroupName[] = "btf2"; +const char BTFGroupDescription[] = "BTF2 Emission"; STATISTIC(EmittedInsts, "Number of machine instrs printed"); @@ -342,6 +345,14 @@ DWARFGroupDescription); } } + + std::string TripleName = Target.getTriple(); + if (TripleName != "bpf" && TripleName != "bpfel" && TripleName != "bpfeb" && + !M.debug_compile_units().empty()) { + Handlers.emplace_back(std::make_unique<BTF2Debug>(this), + DbgTimerName, DbgTimerDescription, + BTFGroupName, BTFGroupDescription); + } } if (M.getNamedMetadata(PseudoProbeDescMetadataName)) { Index: llvm/include/llvm/IR/DebugInfoMetadata.h =================================================================== --- llvm/include/llvm/IR/DebugInfoMetadata.h +++ llvm/include/llvm/IR/DebugInfoMetadata.h @@ -128,6 +128,8 @@ friend class LLVMContextImpl; friend class MDNode; + SmallVector<StringRef, 4> Annotations; + protected: DINode(LLVMContext &C, unsigned ID, StorageType Storage, unsigned Tag, ArrayRef<Metadata *> Ops1, ArrayRef<Metadata *> Ops2 = None) @@ -183,6 +185,10 @@ static DIFlags splitFlags(DIFlags Flags, SmallVectorImpl<DIFlags> &SplitFlags); + void addAnnotation(StringRef Annotation) { Annotations.emplace_back(Annotation); } + SmallVectorImpl<StringRef> &getAnnotations() { return Annotations; } + const SmallVectorImpl<StringRef> &getAnnotations() const { return Annotations; } + static bool classof(const Metadata *MD) { switch (MD->getMetadataID()) { default: @@ -707,7 +713,6 @@ DIScope *getScope() const { return cast_or_null<DIScope>(getRawScope()); } StringRef getName() const { return getStringOperand(2); } - Metadata *getRawScope() const { return getOperand(1); } MDString *getRawName() const { return getOperandAs<MDString>(2); } Index: clang/lib/CodeGen/CGDebugInfo.cpp =================================================================== --- clang/lib/CodeGen/CGDebugInfo.cpp +++ clang/lib/CodeGen/CGDebugInfo.cpp @@ -1516,6 +1516,10 @@ FieldType = createFieldType(name, type, field->getLocation(), field->getAccess(), OffsetInBits, Align, tunit, RecordTy, RD); + if (field->hasAttr<AnnotateAttr>()) { + for (const auto *I : field->specific_attrs<AnnotateAttr>()) + FieldType->addAnnotation(I->getAnnotation()); + } } elements.push_back(FieldType); @@ -3487,6 +3491,11 @@ break; } + if (D->hasAttr<AnnotateAttr>()) { + for (const auto *I : D->specific_attrs<AnnotateAttr>()) + RealDecl->addAnnotation(I->getAnnotation()); + } + RegionMap[Ty->getDecl()].reset(RealDecl); TypeCache[QualType(Ty, 0).getAsOpaquePtr()].reset(RealDecl); @@ -3880,7 +3889,12 @@ getDwarfCC(CC)); } - return cast<llvm::DISubroutineType>(getOrCreateType(FnType, F)); + auto DISubroutineTy = cast<llvm::DISubroutineType>(getOrCreateType(FnType, F)); + if (D->hasAttr<AnnotateAttr>()) { + for (const auto *I : D->specific_attrs<AnnotateAttr>()) + DISubroutineTy->addAnnotation(I->getAnnotation()); + } + return DISubroutineTy; } void CGDebugInfo::emitFunctionStart(GlobalDecl GD, SourceLocation Loc, @@ -4375,6 +4389,10 @@ if (ArgNo) { D = DBuilder.createParameterVariable(Scope, Name, *ArgNo, Unit, Line, Ty, CGM.getLangOpts().Optimize, Flags); + if (VD->hasAttr<AnnotateAttr>()) { + for (const auto *I : VD->specific_attrs<AnnotateAttr>()) + D->addAnnotation(I->getAnnotation()); + } } else { // For normal local variable, we will try to find out whether 'VD' is the // copy parameter of coroutine.
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits