forked from OSchip/llvm-project
[yaml2obj/obj2yaml] - Add support for the SHT_LLVM_CALL_GRAPH_PROFILE section.
This is a LLVM specific section that is well described here: https://llvm.org/docs/Extensions.html#sht-llvm-call-graph-profile-section-call-graph-profile This patch teaches yaml2obj and obj2yaml about how to work with it. Differential revision: https://reviews.llvm.org/D73788
This commit is contained in:
parent
92570718a8
commit
bec54e464e
|
@ -153,6 +153,7 @@ struct Chunk {
|
|||
Fill,
|
||||
LinkerOptions,
|
||||
DependentLibraries,
|
||||
CallGraphProfile
|
||||
};
|
||||
|
||||
ChunkKind Kind;
|
||||
|
@ -385,6 +386,27 @@ struct DependentLibrariesSection : Section {
|
|||
}
|
||||
};
|
||||
|
||||
// Represents the call graph profile section entry.
|
||||
struct CallGraphEntry {
|
||||
// The symbol of the source of the edge.
|
||||
StringRef From;
|
||||
// The symbol index of the destination of the edge.
|
||||
StringRef To;
|
||||
// The weight of the edge.
|
||||
uint64_t Weight;
|
||||
};
|
||||
|
||||
struct CallGraphProfileSection : Section {
|
||||
Optional<std::vector<CallGraphEntry>> Entries;
|
||||
Optional<yaml::BinaryRef> Content;
|
||||
|
||||
CallGraphProfileSection() : Section(ChunkKind::CallGraphProfile) {}
|
||||
|
||||
static bool classof(const Chunk *S) {
|
||||
return S->Kind == ChunkKind::CallGraphProfile;
|
||||
}
|
||||
};
|
||||
|
||||
struct SymverSection : Section {
|
||||
std::vector<uint16_t> Entries;
|
||||
|
||||
|
@ -514,6 +536,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::AddrsigSymbol)
|
|||
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::StackSizeEntry)
|
||||
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::DynamicEntry)
|
||||
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::LinkerOption)
|
||||
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::CallGraphEntry)
|
||||
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::NoteEntry)
|
||||
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::ProgramHeader)
|
||||
LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr<llvm::ELFYAML::Chunk>)
|
||||
|
@ -685,6 +708,10 @@ template <> struct MappingTraits<ELFYAML::LinkerOption> {
|
|||
static void mapping(IO &IO, ELFYAML::LinkerOption &Sym);
|
||||
};
|
||||
|
||||
template <> struct MappingTraits<ELFYAML::CallGraphEntry> {
|
||||
static void mapping(IO &IO, ELFYAML::CallGraphEntry &E);
|
||||
};
|
||||
|
||||
template <> struct MappingTraits<ELFYAML::Relocation> {
|
||||
static void mapping(IO &IO, ELFYAML::Relocation &Rel);
|
||||
};
|
||||
|
|
|
@ -210,6 +210,9 @@ template <class ELFT> class ELFState {
|
|||
void writeSectionContent(Elf_Shdr &SHeader,
|
||||
const ELFYAML::DependentLibrariesSection &Section,
|
||||
ContiguousBlobAccumulator &CBA);
|
||||
void writeSectionContent(Elf_Shdr &SHeader,
|
||||
const ELFYAML::CallGraphProfileSection &Section,
|
||||
ContiguousBlobAccumulator &CBA);
|
||||
|
||||
void writeFill(ELFYAML::Fill &Fill, ContiguousBlobAccumulator &CBA);
|
||||
|
||||
|
@ -492,6 +495,8 @@ void ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders,
|
|||
writeSectionContent(SHeader, *S, CBA);
|
||||
} else if (auto S = dyn_cast<ELFYAML::DependentLibrariesSection>(Sec)) {
|
||||
writeSectionContent(SHeader, *S, CBA);
|
||||
} else if (auto S = dyn_cast<ELFYAML::CallGraphProfileSection>(Sec)) {
|
||||
writeSectionContent(SHeader, *S, CBA);
|
||||
} else {
|
||||
llvm_unreachable("Unknown section type");
|
||||
}
|
||||
|
@ -982,6 +987,41 @@ void ELFState<ELFT>::writeSectionContent(
|
|||
}
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void ELFState<ELFT>::writeSectionContent(
|
||||
Elf_Shdr &SHeader, const ELFYAML::CallGraphProfileSection &Section,
|
||||
ContiguousBlobAccumulator &CBA) {
|
||||
raw_ostream &OS =
|
||||
CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign);
|
||||
|
||||
if (Section.EntSize)
|
||||
SHeader.sh_entsize = *Section.EntSize;
|
||||
else
|
||||
SHeader.sh_entsize = 16;
|
||||
|
||||
unsigned Link = 0;
|
||||
if (Section.Link.empty() && SN2I.lookup(".symtab", Link))
|
||||
SHeader.sh_link = Link;
|
||||
|
||||
if (Section.Content) {
|
||||
SHeader.sh_size = writeContent(OS, Section.Content, None);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Section.Entries)
|
||||
return;
|
||||
|
||||
for (const ELFYAML::CallGraphEntry &E : *Section.Entries) {
|
||||
unsigned From = toSymbolIndex(E.From, Section.Name, /*IsDynamic=*/false);
|
||||
unsigned To = toSymbolIndex(E.To, Section.Name, /*IsDynamic=*/false);
|
||||
|
||||
support::endian::write<uint32_t>(OS, From, ELFT::TargetEndianness);
|
||||
support::endian::write<uint32_t>(OS, To, ELFT::TargetEndianness);
|
||||
support::endian::write<uint64_t>(OS, E.Weight, ELFT::TargetEndianness);
|
||||
SHeader.sh_size += 16;
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader,
|
||||
const ELFYAML::HashSection &Section,
|
||||
|
|
|
@ -1149,6 +1149,12 @@ static void sectionMapping(IO &IO,
|
|||
IO.mapOptional("Content", Section.Content);
|
||||
}
|
||||
|
||||
static void sectionMapping(IO &IO, ELFYAML::CallGraphProfileSection &Section) {
|
||||
commonSectionMapping(IO, Section);
|
||||
IO.mapOptional("Entries", Section.Entries);
|
||||
IO.mapOptional("Content", Section.Content);
|
||||
}
|
||||
|
||||
void MappingTraits<ELFYAML::SectionOrType>::mapping(
|
||||
IO &IO, ELFYAML::SectionOrType §ionOrType) {
|
||||
IO.mapRequired("SectionOrType", sectionOrType.sectionNameOrType);
|
||||
|
@ -1282,6 +1288,11 @@ void MappingTraits<std::unique_ptr<ELFYAML::Chunk>>::mapping(
|
|||
sectionMapping(IO,
|
||||
*cast<ELFYAML::DependentLibrariesSection>(Section.get()));
|
||||
break;
|
||||
case ELF::SHT_LLVM_CALL_GRAPH_PROFILE:
|
||||
if (!IO.outputting())
|
||||
Section.reset(new ELFYAML::CallGraphProfileSection());
|
||||
sectionMapping(IO, *cast<ELFYAML::CallGraphProfileSection>(Section.get()));
|
||||
break;
|
||||
default:
|
||||
if (!IO.outputting()) {
|
||||
StringRef Name;
|
||||
|
@ -1463,6 +1474,12 @@ StringRef MappingTraits<std::unique_ptr<ELFYAML::Chunk>>::validate(
|
|||
return {};
|
||||
}
|
||||
|
||||
if (const auto *CGP = dyn_cast<ELFYAML::CallGraphProfileSection>(C.get())) {
|
||||
if (CGP->Entries && CGP->Content)
|
||||
return "\"Entries\" and \"Content\" can't be used together";
|
||||
return {};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -1600,6 +1617,14 @@ void MappingTraits<ELFYAML::LinkerOption>::mapping(IO &IO,
|
|||
IO.mapRequired("Value", Opt.Value);
|
||||
}
|
||||
|
||||
void MappingTraits<ELFYAML::CallGraphEntry>::mapping(
|
||||
IO &IO, ELFYAML::CallGraphEntry &E) {
|
||||
assert(IO.getContext() && "The IO context is not initialized");
|
||||
IO.mapRequired("From", E.From);
|
||||
IO.mapRequired("To", E.To);
|
||||
IO.mapRequired("Weight", E.Weight);
|
||||
}
|
||||
|
||||
LLVM_YAML_STRONG_TYPEDEF(uint8_t, MIPS_AFL_REG)
|
||||
LLVM_YAML_STRONG_TYPEDEF(uint8_t, MIPS_ABI_FP)
|
||||
LLVM_YAML_STRONG_TYPEDEF(uint32_t, MIPS_AFL_EXT)
|
||||
|
|
|
@ -0,0 +1,269 @@
|
|||
## Test how we dump SHT_LLVM_CALL_GRAPH_PROFILE sections for 32 and 64-bit targets.
|
||||
|
||||
## Test we use the "Entries" property when it is possible to dump values correctly.
|
||||
|
||||
# RUN: yaml2obj --docnum=1 %s -o %t.le64
|
||||
# RUN: obj2yaml %t.le64 | FileCheck %s --check-prefix=BASIC
|
||||
# RUN: yaml2obj --docnum=2 %s -o %t.be64
|
||||
# RUN: obj2yaml %t.be64 | FileCheck %s --check-prefix=BASIC
|
||||
# RUN: yaml2obj --docnum=3 %s -o %t.le32
|
||||
# RUN: obj2yaml %t.le32 | FileCheck %s --check-prefix=BASIC
|
||||
# RUN: yaml2obj --docnum=4 %s -o %t.be32
|
||||
# RUN: obj2yaml %t.be32 | FileCheck %s --check-prefix=BASIC
|
||||
|
||||
# BASIC: Sections:
|
||||
# BASIC-NEXT: - Name: .llvm.call-graph-profile
|
||||
# BASIC-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
# BASIC-NEXT: Link: .symtab
|
||||
# BASIC-NEXT: EntSize: 0x0000000000000010
|
||||
# BASIC-NEXT: Entries:
|
||||
# BASIC-NEXT: - From: foo
|
||||
# BASIC-NEXT: To: bar
|
||||
# BASIC-NEXT: Weight: 89
|
||||
# BASIC-NEXT: - From: bar
|
||||
# BASIC-NEXT: To: foo
|
||||
# BASIC-NEXT: Weight: 98
|
||||
# BASIC-NEXT: Symbols:
|
||||
|
||||
## TODO: we should really improve yaml2obj somehow to be able to collapse
|
||||
## the following four YAML descriptions into a single one.
|
||||
|
||||
--- !ELF
|
||||
FileHeader:
|
||||
Class: ELFCLASS64
|
||||
Data: ELFDATA2LSB
|
||||
Type: ET_DYN
|
||||
Machine: EM_X86_64
|
||||
Sections:
|
||||
- Name: .llvm.call-graph-profile
|
||||
Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
Entries:
|
||||
- From: 1
|
||||
To: 2
|
||||
Weight: 89
|
||||
- From: 2
|
||||
To: 1
|
||||
Weight: 98
|
||||
Symbols:
|
||||
- Name: foo
|
||||
- Name: bar
|
||||
|
||||
--- !ELF
|
||||
FileHeader:
|
||||
Class: ELFCLASS64
|
||||
Data: ELFDATA2MSB
|
||||
Type: ET_DYN
|
||||
Machine: EM_X86_64
|
||||
Sections:
|
||||
- Name: .llvm.call-graph-profile
|
||||
Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
Entries:
|
||||
- From: 1
|
||||
To: 2
|
||||
Weight: 89
|
||||
- From: 2
|
||||
To: 1
|
||||
Weight: 98
|
||||
Symbols:
|
||||
- Name: foo
|
||||
- Name: bar
|
||||
|
||||
--- !ELF
|
||||
FileHeader:
|
||||
Class: ELFCLASS32
|
||||
Data: ELFDATA2LSB
|
||||
Type: ET_DYN
|
||||
Machine: EM_386
|
||||
Sections:
|
||||
- Name: .llvm.call-graph-profile
|
||||
Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
Entries:
|
||||
- From: 1
|
||||
To: 2
|
||||
Weight: 89
|
||||
- From: 2
|
||||
To: 1
|
||||
Weight: 98
|
||||
Symbols:
|
||||
- Name: foo
|
||||
- Name: bar
|
||||
|
||||
--- !ELF
|
||||
FileHeader:
|
||||
Class: ELFCLASS32
|
||||
Data: ELFDATA2MSB
|
||||
Type: ET_DYN
|
||||
Machine: EM_386
|
||||
Sections:
|
||||
- Name: .llvm.call-graph-profile
|
||||
Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
Entries:
|
||||
- From: 1
|
||||
To: 2
|
||||
Weight: 89
|
||||
- From: 2
|
||||
To: 1
|
||||
Weight: 98
|
||||
Symbols:
|
||||
- Name: foo
|
||||
- Name: bar
|
||||
|
||||
## Check how we handle broken cases.
|
||||
|
||||
# RUN: yaml2obj --docnum=5 %s -o %t.invalid
|
||||
# RUN: obj2yaml %t.invalid | FileCheck %s --check-prefix=INVALID
|
||||
|
||||
# INVALID: --- !ELF
|
||||
# INVALID-NEXT: FileHeader:
|
||||
# INVALID-NEXT: Class: ELFCLASS32
|
||||
# INVALID-NEXT: Data: ELFDATA2MSB
|
||||
# INVALID-NEXT: Type: ET_DYN
|
||||
# INVALID-NEXT: Machine: EM_386
|
||||
# INVALID-NEXT: Sections:
|
||||
# INVALID-NEXT: - Name: .empty
|
||||
# INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
# INVALID-NEXT: Link: .symtab
|
||||
# INVALID-NEXT: EntSize: 0x0000000000000010
|
||||
# INVALID-NEXT: - Name: .multiple.16.valid
|
||||
# INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
# INVALID-NEXT: Link: .symtab
|
||||
# INVALID-NEXT: EntSize: 0x0000000000000010
|
||||
# INVALID-NEXT: Entries:
|
||||
# INVALID-NEXT: - From: foo
|
||||
# INVALID-NEXT: To: bar
|
||||
# INVALID-NEXT: Weight: 3
|
||||
# INVALID-NEXT: - Name: .non.multiple.16
|
||||
# INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
# INVALID-NEXT: Link: .symtab
|
||||
# INVALID-NEXT: EntSize: 0x0000000000000010
|
||||
# INVALID-NEXT: Content: '0000000100000002000000000000000300'
|
||||
# INVALID-NEXT: - Name: .multiple.16.invalid
|
||||
# INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
# INVALID-NEXT: Link: .symtab
|
||||
# INVALID-NEXT: EntSize: 0x0000000000000010
|
||||
# INVALID-NEXT: Content: 00112233445566778899AABBCCDDEEFF
|
||||
# INVALID-NEXT: - Name: .unknown.symbol.1
|
||||
# INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
# INVALID-NEXT: Link: .symtab
|
||||
# INVALID-NEXT: EntSize: 0x0000000000000010
|
||||
# INVALID-NEXT: Content: 000000FF000000020000000000000003
|
||||
# INVALID-NEXT: - Name: .unknown.symbol.2
|
||||
# INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
# INVALID-NEXT: Link: .symtab
|
||||
# INVALID-NEXT: EntSize: 0x0000000000000010
|
||||
# INVALID-NEXT: Content: 00000001000000FF0000000000000003
|
||||
# INVALID-NEXT: - Name: .link.to.symtable
|
||||
# INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
# INVALID-NEXT: Link: .symtab
|
||||
# INVALID-NEXT: EntSize: 0x0000000000000010
|
||||
# INVALID-NEXT: Entries:
|
||||
# INVALID-NEXT: - From: foo
|
||||
# INVALID-NEXT: To: bar
|
||||
# INVALID-NEXT: Weight: 0
|
||||
# INVALID-NEXT: - Name: .link.to.non.symtable.1
|
||||
# INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
# INVALID-NEXT: EntSize: 0x0000000000000010
|
||||
# INVALID-NEXT: Content: '00000001000000020000000000000000'
|
||||
# INVALID-NEXT: - Name: .link.to.non.symtable.2
|
||||
# INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
# INVALID-NEXT: Link: .empty
|
||||
# INVALID-NEXT: EntSize: 0x0000000000000010
|
||||
# INVALID-NEXT: Content: '00000001000000020000000000000000'
|
||||
# INVALID-NEXT: - Name: .zero.entry.size
|
||||
# INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
# INVALID-NEXT: Link: .symtab
|
||||
# INVALID-NEXT: Entries:
|
||||
# INVALID-NEXT: - From: foo
|
||||
# INVALID-NEXT: To: bar
|
||||
# INVALID-NEXT: Weight: 0
|
||||
# INVALID-NEXT: - Name: .invalid.entry.size
|
||||
# INVALID-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
# INVALID-NEXT: Link: .symtab
|
||||
# INVALID-NEXT: EntSize: 0x0000000000000001
|
||||
# INVALID-NEXT: Entries:
|
||||
# INVALID-NEXT: - From: foo
|
||||
# INVALID-NEXT: To: bar
|
||||
# INVALID-NEXT: Weight: 0
|
||||
# INVALID-NEXT: Symbols:
|
||||
# INVALID-NEXT: - Name: foo
|
||||
# INVALID-NEXT: - Name: bar
|
||||
|
||||
--- !ELF
|
||||
FileHeader:
|
||||
Class: ELFCLASS32
|
||||
Data: ELFDATA2MSB
|
||||
Type: ET_DYN
|
||||
Machine: EM_386
|
||||
Sections:
|
||||
## Case 1: Content is empty.
|
||||
- Name: .empty
|
||||
Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
## Case 2: Check that we use the "Entries" property to dump the data when it
|
||||
## has a size that is a multiple of 16 and is valid (it is possible to match
|
||||
## symbol indexes to symbols), but fallback to dumping the whole section
|
||||
## using the "Content" property otherwise.
|
||||
## TODO: Teach yaml2obj to accept 'Size' key for SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
## sections and use Entries for cases below.
|
||||
- Name: .multiple.16.valid
|
||||
Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
Content: "00000001000000020000000000000003"
|
||||
- Name: .non.multiple.16
|
||||
Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
Content: "0000000100000002000000000000000300"
|
||||
- Name: .multiple.16.invalid
|
||||
Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
Content: "00112233445566778899AABBCCDDEEFF"
|
||||
## Case 3: Check we use the "Content" property when unable to match a
|
||||
## symbol index to a symbol.
|
||||
- Name: .unknown.symbol.1
|
||||
Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
Entries:
|
||||
- From: 0xff
|
||||
To: 2
|
||||
Weight: 3
|
||||
- Name: .unknown.symbol.2
|
||||
Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
Entries:
|
||||
- From: 1
|
||||
To: 0xff
|
||||
Weight: 3
|
||||
## Case 4: Check we use the "Content" property when a linked section
|
||||
## is not a symbol table.
|
||||
- Name: .link.to.symtable
|
||||
Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
Entries:
|
||||
- From: 1
|
||||
To: 2
|
||||
Weight: 0
|
||||
- Name: .link.to.non.symtable.1
|
||||
Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
Link: 0
|
||||
Entries:
|
||||
- From: 1
|
||||
To: 2
|
||||
Weight: 0
|
||||
- Name: .link.to.non.symtable.2
|
||||
Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
Link: 1
|
||||
Entries:
|
||||
- From: 1
|
||||
To: 2
|
||||
Weight: 0
|
||||
## Case 5: Check we can dump a section that has a sh_entsize that is not a multiple of 16.
|
||||
- Name: .zero.entry.size
|
||||
Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
EntSize: 0
|
||||
Entries:
|
||||
- From: 1
|
||||
To: 2
|
||||
Weight: 0
|
||||
- Name: .invalid.entry.size
|
||||
Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
EntSize: 1
|
||||
Entries:
|
||||
- From: 1
|
||||
To: 2
|
||||
Weight: 0
|
||||
Symbols:
|
||||
- Name: foo
|
||||
- Name: bar
|
|
@ -0,0 +1,295 @@
|
|||
## Test how we create SHT_LLVM_CALL_GRAPH_PROFILE sections.
|
||||
|
||||
## Test that the content of SHT_LLVM_CALL_GRAPH_PROFILE sections
|
||||
## for 32/64-bit little/big endian targets is correct.
|
||||
# RUN: yaml2obj --docnum=1 %s -o %t.le64
|
||||
# RUN: llvm-readobj --elf-cg-profile --sections --section-data %t.le64 | FileCheck %s --check-prefixes=BASIC,BASIC-LE
|
||||
# RUN: yaml2obj --docnum=2 %s -o %t.be64
|
||||
# RUN: llvm-readobj --elf-cg-profile --sections --section-data %t.be64 | FileCheck %s --check-prefixes=BASIC,BASIC-BE
|
||||
# RUN: yaml2obj --docnum=3 %s -o %t.le32
|
||||
# RUN: llvm-readobj --elf-cg-profile --sections --section-data %t.le32 | FileCheck %s --check-prefixes=BASIC,BASIC-LE
|
||||
# RUN: yaml2obj --docnum=4 %s -o %t.be32
|
||||
# RUN: llvm-readobj --elf-cg-profile --sections --section-data %t.be32 | FileCheck %s --check-prefixes=BASIC,BASIC-BE
|
||||
|
||||
# BASIC: Name: .llvm.call-graph-profile
|
||||
# BASIC-NEXT: Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
# BASIC-NEXT: Flags [
|
||||
# BASIC-NEXT: ]
|
||||
# BASIC-NEXT: Address: 0x0
|
||||
# BASIC-NEXT: Offset:
|
||||
# BASIC-NEXT: Size: 32
|
||||
## Check that we link SHT_LLVM_CALL_GRAPH_PROFILE section with .symtab by default.
|
||||
# BASIC-NEXT: Link: [[SYMTABNDX:.*]]
|
||||
# BASIC-NEXT: Info: 0
|
||||
# BASIC-NEXT: AddressAlignment: 0
|
||||
## Check that the entry size is set to 16 by default.
|
||||
# BASIC-NEXT: EntrySize: 16
|
||||
# BASIC-NEXT: SectionData (
|
||||
# BASIC-LE-NEXT: 0000: 01000000 02000000 59000000 00000000
|
||||
# BASIC-LE-NEXT: 0010: 02000000 01000000 62000000 00000000
|
||||
# BASIC-BE-NEXT: 0000: 00000001 00000002 00000000 00000059
|
||||
# BASIC-BE-NEXT: 0010: 00000002 00000001 00000000 00000062
|
||||
# BASIC-NEXT: )
|
||||
# BASIC-NEXT: }
|
||||
# BASIC-NEXT: Section {
|
||||
# BASIC-NEXT: Index: [[SYMTABNDX]]
|
||||
# BASIC-NEXT: Name: .symtab
|
||||
|
||||
# BASIC: CGProfile [
|
||||
# BASIC-NEXT: CGProfileEntry {
|
||||
# BASIC-NEXT: From: foo (1)
|
||||
# BASIC-NEXT: To: bar (2)
|
||||
# BASIC-NEXT: Weight: 89
|
||||
# BASIC-NEXT: }
|
||||
# BASIC-NEXT: CGProfileEntry {
|
||||
# BASIC-NEXT: From: bar (2)
|
||||
# BASIC-NEXT: To: foo (1)
|
||||
# BASIC-NEXT: Weight: 98
|
||||
# BASIC-NEXT: }
|
||||
# BASIC-NEXT: ]
|
||||
|
||||
## TODO: we should really improve yaml2obj somehow to be able to collapse
|
||||
## the following four YAML descriptions into a single one.
|
||||
|
||||
--- !ELF
|
||||
FileHeader:
|
||||
Class: ELFCLASS64
|
||||
Data: ELFDATA2LSB
|
||||
Type: ET_DYN
|
||||
Machine: EM_X86_64
|
||||
Sections:
|
||||
- Name: .llvm.call-graph-profile
|
||||
Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
Entries:
|
||||
- From: 1
|
||||
To: 2
|
||||
Weight: 89
|
||||
- From: 2
|
||||
To: 1
|
||||
Weight: 98
|
||||
Symbols:
|
||||
- Name: foo
|
||||
- Name: bar
|
||||
|
||||
--- !ELF
|
||||
FileHeader:
|
||||
Class: ELFCLASS64
|
||||
Data: ELFDATA2MSB
|
||||
Type: ET_DYN
|
||||
Machine: EM_X86_64
|
||||
Sections:
|
||||
- Name: .llvm.call-graph-profile
|
||||
Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
Entries:
|
||||
- From: 1
|
||||
To: 2
|
||||
Weight: 89
|
||||
- From: 2
|
||||
To: 1
|
||||
Weight: 98
|
||||
Symbols:
|
||||
- Name: foo
|
||||
- Name: bar
|
||||
|
||||
--- !ELF
|
||||
FileHeader:
|
||||
Class: ELFCLASS32
|
||||
Data: ELFDATA2LSB
|
||||
Type: ET_DYN
|
||||
Machine: EM_386
|
||||
Sections:
|
||||
- Name: .llvm.call-graph-profile
|
||||
Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
Entries:
|
||||
- From: 1
|
||||
To: 2
|
||||
Weight: 89
|
||||
- From: 2
|
||||
To: 1
|
||||
Weight: 98
|
||||
Symbols:
|
||||
- Name: foo
|
||||
- Name: bar
|
||||
|
||||
--- !ELF
|
||||
FileHeader:
|
||||
Class: ELFCLASS32
|
||||
Data: ELFDATA2MSB
|
||||
Type: ET_DYN
|
||||
Machine: EM_386
|
||||
Sections:
|
||||
- Name: .llvm.call-graph-profile
|
||||
Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
Entries:
|
||||
- From: 1
|
||||
To: 2
|
||||
Weight: 89
|
||||
- From: 2
|
||||
To: 1
|
||||
Weight: 98
|
||||
Symbols:
|
||||
- Name: foo
|
||||
- Name: bar
|
||||
|
||||
## Check we can set arbitrary sh_link and sh_entsize values.
|
||||
## Check we can specify neither "Content" nor "Entries" tags.
|
||||
# RUN: yaml2obj --docnum=5 %s -o %t.link
|
||||
# RUN: llvm-readelf --sections %t.link | FileCheck %s --check-prefix=LINK
|
||||
|
||||
# LINK: [Nr] Name Type Address Off Size ES Flg Lk
|
||||
# LINK: [ 1] .llvm.foo LLVM_CALL_GRAPH_PROFILE 0000000000000000 000040 000000 00 0
|
||||
# LINK: [ 2] .llvm.bar LLVM_CALL_GRAPH_PROFILE 0000000000000000 000040 000000 ff 255
|
||||
|
||||
--- !ELF
|
||||
FileHeader:
|
||||
Class: ELFCLASS64
|
||||
Data: ELFDATA2LSB
|
||||
Type: ET_DYN
|
||||
Machine: EM_X86_64
|
||||
Sections:
|
||||
- Name: .llvm.foo
|
||||
Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
Link: 0x0
|
||||
EntSize: 0
|
||||
- Name: .llvm.bar
|
||||
Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
Link: 0xFF
|
||||
EntSize: 0xFF
|
||||
|
||||
## Check we can't specify both "Content" and "Entries" tags.
|
||||
# RUN: not yaml2obj --docnum=6 %s 2>&1 | FileCheck %s --check-prefix=BOTH
|
||||
# BOTH: error: "Entries" and "Content" can't be used together
|
||||
|
||||
--- !ELF
|
||||
FileHeader:
|
||||
Class: ELFCLASS64
|
||||
Data: ELFDATA2LSB
|
||||
Type: ET_DYN
|
||||
Machine: EM_X86_64
|
||||
Sections:
|
||||
- Name: .llvm.foo
|
||||
Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
Content: ""
|
||||
Entries: []
|
||||
|
||||
## Check we can refer to symbols by name.
|
||||
# RUN: yaml2obj --docnum=7 %s -o %t.sym
|
||||
# RUN: llvm-readobj --elf-cg-profile %t.sym | FileCheck %s --check-prefix=SYMBOL-NAMES
|
||||
|
||||
# SYMBOL-NAMES: CGProfile [
|
||||
# SYMBOL-NAMES-NEXT: CGProfileEntry {
|
||||
# SYMBOL-NAMES-NEXT: From: foo (1)
|
||||
# SYMBOL-NAMES-NEXT: To: bar (2)
|
||||
# SYMBOL-NAMES-NEXT: Weight: 10
|
||||
# SYMBOL-NAMES-NEXT: }
|
||||
# SYMBOL-NAMES-NEXT: CGProfileEntry {
|
||||
# SYMBOL-NAMES-NEXT: From: foo (1)
|
||||
# SYMBOL-NAMES-NEXT: To: foo (3)
|
||||
# SYMBOL-NAMES-NEXT: Weight: 30
|
||||
# SYMBOL-NAMES-NEXT: }
|
||||
# SYMBOL-NAMES-NEXT: ]
|
||||
|
||||
--- !ELF
|
||||
FileHeader:
|
||||
Class: ELFCLASS64
|
||||
Data: ELFDATA2LSB
|
||||
Type: ET_DYN
|
||||
Machine: EM_X86_64
|
||||
Sections:
|
||||
- Name: .llvm.call-graph-profile
|
||||
Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
Entries:
|
||||
## Case 1: Test we can use symbol names to describe an entry.
|
||||
- From: foo
|
||||
To: bar
|
||||
Weight: 10
|
||||
## Case 2: Test we can refer to symbols with suffixes.
|
||||
- From: foo
|
||||
To: 'foo [1]'
|
||||
Weight: 30
|
||||
Symbols:
|
||||
- Name: foo
|
||||
- Name: bar
|
||||
- Name: 'foo [1]'
|
||||
|
||||
## Check we can describe SHT_LLVM_CALL_GRAPH_PROFILE sections using the "Content" tag.
|
||||
# RUN: yaml2obj --docnum=8 %s -o %t.content
|
||||
# RUN: llvm-readobj --sections --section-data %t.content | FileCheck %s --check-prefix=CONTENT
|
||||
|
||||
# CONTENT: Name: .llvm.call-graph-profile
|
||||
# CONTENT: SectionData (
|
||||
# CONTENT-NEXT: 0000: 11223344 |
|
||||
# CONTENT-NEXT: )
|
||||
|
||||
--- !ELF
|
||||
FileHeader:
|
||||
Class: ELFCLASS64
|
||||
Data: ELFDATA2LSB
|
||||
Type: ET_DYN
|
||||
Machine: EM_X86_64
|
||||
Sections:
|
||||
- Name: .llvm.call-graph-profile
|
||||
Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
Content: "11223344"
|
||||
|
||||
## Check we can't reference unknown symbols by name.
|
||||
# RUN: not yaml2obj --docnum=9 %s 2>&1 | FileCheck %s --check-prefix=UNKNOWN-NAME
|
||||
# RUN: not yaml2obj --docnum=10 %s 2>&1 | FileCheck %s --check-prefix=UNKNOWN-NAME
|
||||
# UNKNOWN-NAME: error: unknown symbol referenced: 'bar' by YAML section '.llvm.call-graph-profile'
|
||||
|
||||
--- !ELF
|
||||
FileHeader:
|
||||
Class: ELFCLASS64
|
||||
Data: ELFDATA2LSB
|
||||
Type: ET_DYN
|
||||
Machine: EM_X86_64
|
||||
Sections:
|
||||
- Name: .llvm.call-graph-profile
|
||||
Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
## The first symbol is valid, but the second is unknown.
|
||||
Entries:
|
||||
- From: foo
|
||||
To: bar
|
||||
Weight: 10
|
||||
Symbols:
|
||||
- Name: foo
|
||||
|
||||
--- !ELF
|
||||
FileHeader:
|
||||
Class: ELFCLASS64
|
||||
Data: ELFDATA2LSB
|
||||
Type: ET_DYN
|
||||
Machine: EM_X86_64
|
||||
Sections:
|
||||
- Name: .llvm.call-graph-profile
|
||||
Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
## The first symbol is unknown, but the second is valid.
|
||||
Entries:
|
||||
- From: bar
|
||||
To: foo
|
||||
Weight: 10
|
||||
Symbols:
|
||||
- Name: foo
|
||||
|
||||
## Check we can specify arbitrary symbol indexes for an SHT_LLVM_CALL_GRAPH_PROFILE section entry.
|
||||
# RUN: yaml2obj --docnum=11 %s -o %t.unk
|
||||
# RUN: llvm-readobj --sections --section-data %t.unk | FileCheck %s --check-prefix=UNKNOWN-INDEX
|
||||
|
||||
# UNKNOWN-INDEX: Name: .llvm.call-graph-profile
|
||||
# UNKNOWN-INDEX: SectionData (
|
||||
# UNKNOWN-INDEX-NEXT: 0000: 01000000 02000000 03000000 00000000 |
|
||||
# UNKNOWN-INDEX-NEXT: )
|
||||
|
||||
--- !ELF
|
||||
FileHeader:
|
||||
Class: ELFCLASS64
|
||||
Data: ELFDATA2LSB
|
||||
Type: ET_DYN
|
||||
Machine: EM_X86_64
|
||||
Sections:
|
||||
- Name: .llvm.call-graph-profile
|
||||
Type: SHT_LLVM_CALL_GRAPH_PROFILE
|
||||
Entries:
|
||||
- From: 1
|
||||
To: 2
|
||||
Weight: 3
|
|
@ -65,6 +65,8 @@ class ELFDumper {
|
|||
dumpLinkerOptionsSection(const Elf_Shdr *Shdr);
|
||||
Expected<ELFYAML::DependentLibrariesSection *>
|
||||
dumpDependentLibrariesSection(const Elf_Shdr *Shdr);
|
||||
Expected<ELFYAML::CallGraphProfileSection *>
|
||||
dumpCallGraphProfileSection(const Elf_Shdr *Shdr);
|
||||
Expected<ELFYAML::DynamicSection *> dumpDynamicSection(const Elf_Shdr *Shdr);
|
||||
Expected<ELFYAML::RelocationSection *> dumpRelocSection(const Elf_Shdr *Shdr);
|
||||
Expected<ELFYAML::RelrSection *> dumpRelrSection(const Elf_Shdr *Shdr);
|
||||
|
@ -346,6 +348,14 @@ template <class ELFT> Expected<ELFYAML::Object *> ELFDumper<ELFT>::dump() {
|
|||
Y->Chunks.emplace_back(*SecOrErr);
|
||||
break;
|
||||
}
|
||||
case ELF::SHT_LLVM_CALL_GRAPH_PROFILE: {
|
||||
Expected<ELFYAML::CallGraphProfileSection *> SecOrErr =
|
||||
dumpCallGraphProfileSection(&Sec);
|
||||
if (!SecOrErr)
|
||||
return SecOrErr.takeError();
|
||||
Y->Chunks.emplace_back(*SecOrErr);
|
||||
break;
|
||||
}
|
||||
case ELF::SHT_NULL: {
|
||||
// We only dump the SHT_NULL section at index 0 when it
|
||||
// has at least one non-null field, because yaml2obj
|
||||
|
@ -677,6 +687,62 @@ ELFDumper<ELFT>::dumpDependentLibrariesSection(const Elf_Shdr *Shdr) {
|
|||
return DL.release();
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
Expected<ELFYAML::CallGraphProfileSection *>
|
||||
ELFDumper<ELFT>::dumpCallGraphProfileSection(const Elf_Shdr *Shdr) {
|
||||
auto S = std::make_unique<ELFYAML::CallGraphProfileSection>();
|
||||
if (Error E = dumpCommonSection(Shdr, *S))
|
||||
return std::move(E);
|
||||
|
||||
Expected<ArrayRef<uint8_t>> ContentOrErr = Obj.getSectionContents(Shdr);
|
||||
if (!ContentOrErr)
|
||||
return ContentOrErr.takeError();
|
||||
ArrayRef<uint8_t> Content = *ContentOrErr;
|
||||
|
||||
// Dump the section by using the Content key when it is truncated.
|
||||
// There is no need to create either "Content" or "Entries" fields when the
|
||||
// section is empty.
|
||||
if (Content.empty() || Content.size() % 16 != 0) {
|
||||
if (!Content.empty())
|
||||
S->Content = yaml::BinaryRef(Content);
|
||||
return S.release();
|
||||
}
|
||||
|
||||
std::vector<ELFYAML::CallGraphEntry> Entries(Content.size() / 16);
|
||||
DataExtractor Data(Content, Obj.isLE(), /*AddressSize=*/0);
|
||||
DataExtractor::Cursor Cur(0);
|
||||
auto ReadEntry = [&](ELFYAML::CallGraphEntry &E) {
|
||||
uint32_t FromSymIndex = Data.getU32(Cur);
|
||||
uint32_t ToSymIndex = Data.getU32(Cur);
|
||||
E.Weight = Data.getU64(Cur);
|
||||
if (!Cur) {
|
||||
consumeError(Cur.takeError());
|
||||
return false;
|
||||
}
|
||||
|
||||
Expected<StringRef> From = getSymbolName(Shdr->sh_link, FromSymIndex);
|
||||
Expected<StringRef> To = getSymbolName(Shdr->sh_link, ToSymIndex);
|
||||
if (From && To) {
|
||||
E.From = *From;
|
||||
E.To = *To;
|
||||
return true;
|
||||
}
|
||||
consumeError(From.takeError());
|
||||
consumeError(To.takeError());
|
||||
return false;
|
||||
};
|
||||
|
||||
for (ELFYAML::CallGraphEntry &E : Entries) {
|
||||
if (ReadEntry(E))
|
||||
continue;
|
||||
S->Content = yaml::BinaryRef(Content);
|
||||
return S.release();
|
||||
}
|
||||
|
||||
S->Entries = std::move(Entries);
|
||||
return S.release();
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
Expected<ELFYAML::DynamicSection *>
|
||||
ELFDumper<ELFT>::dumpDynamicSection(const Elf_Shdr *Shdr) {
|
||||
|
|
Loading…
Reference in New Issue