1 //===-- InstrProfCorrelator.cpp -------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "llvm/ProfileData/InstrProfCorrelator.h"
10 #include "llvm/DebugInfo/DIContext.h"
11 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
12 #include "llvm/DebugInfo/DWARF/DWARFDie.h"
13 #include "llvm/DebugInfo/DWARF/DWARFExpression.h"
14 #include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
15 #include "llvm/DebugInfo/DWARF/DWARFLocationExpression.h"
16 #include "llvm/DebugInfo/DWARF/DWARFUnit.h"
17 #include "llvm/Object/MachO.h"
18 #include "llvm/Support/Debug.h"
21 #define DEBUG_TYPE "correlator"
25 /// Get the __llvm_prf_cnts section.
26 Expected
<object::SectionRef
> getCountersSection(const object::ObjectFile
&Obj
) {
27 for (auto &Section
: Obj
.sections())
28 if (auto SectionName
= Section
.getName())
29 if (SectionName
.get() == INSTR_PROF_CNTS_SECT_NAME
)
31 return make_error
<InstrProfError
>(
32 instrprof_error::unable_to_correlate_profile
,
33 "could not find counter section (" INSTR_PROF_CNTS_SECT_NAME
")");
36 const char *InstrProfCorrelator::FunctionNameAttributeName
= "Function Name";
37 const char *InstrProfCorrelator::CFGHashAttributeName
= "CFG Hash";
38 const char *InstrProfCorrelator::NumCountersAttributeName
= "Num Counters";
40 llvm::Expected
<std::unique_ptr
<InstrProfCorrelator::Context
>>
41 InstrProfCorrelator::Context::get(std::unique_ptr
<MemoryBuffer
> Buffer
,
42 const object::ObjectFile
&Obj
) {
43 auto CountersSection
= getCountersSection(Obj
);
44 if (auto Err
= CountersSection
.takeError())
45 return std::move(Err
);
46 auto C
= std::make_unique
<Context
>();
47 C
->Buffer
= std::move(Buffer
);
48 C
->CountersSectionStart
= CountersSection
->getAddress();
49 C
->CountersSectionEnd
= C
->CountersSectionStart
+ CountersSection
->getSize();
50 C
->ShouldSwapBytes
= Obj
.isLittleEndian() != sys::IsLittleEndianHost
;
51 return Expected
<std::unique_ptr
<Context
>>(std::move(C
));
54 llvm::Expected
<std::unique_ptr
<InstrProfCorrelator
>>
55 InstrProfCorrelator::get(StringRef DebugInfoFilename
) {
56 auto DsymObjectsOrErr
=
57 object::MachOObjectFile::findDsymObjectMembers(DebugInfoFilename
);
58 if (auto Err
= DsymObjectsOrErr
.takeError())
59 return std::move(Err
);
60 if (!DsymObjectsOrErr
->empty()) {
61 // TODO: Enable profile correlation when there are multiple objects in a
63 if (DsymObjectsOrErr
->size() > 1)
64 return make_error
<InstrProfError
>(
65 instrprof_error::unable_to_correlate_profile
,
66 "using multiple objects is not yet supported");
67 DebugInfoFilename
= *DsymObjectsOrErr
->begin();
70 errorOrToExpected(MemoryBuffer::getFile(DebugInfoFilename
));
71 if (auto Err
= BufferOrErr
.takeError())
72 return std::move(Err
);
74 return get(std::move(*BufferOrErr
));
77 llvm::Expected
<std::unique_ptr
<InstrProfCorrelator
>>
78 InstrProfCorrelator::get(std::unique_ptr
<MemoryBuffer
> Buffer
) {
79 auto BinOrErr
= object::createBinary(*Buffer
);
80 if (auto Err
= BinOrErr
.takeError())
81 return std::move(Err
);
83 if (auto *Obj
= dyn_cast
<object::ObjectFile
>(BinOrErr
->get())) {
84 auto CtxOrErr
= Context::get(std::move(Buffer
), *Obj
);
85 if (auto Err
= CtxOrErr
.takeError())
86 return std::move(Err
);
87 auto T
= Obj
->makeTriple();
89 return InstrProfCorrelatorImpl
<uint64_t>::get(std::move(*CtxOrErr
), *Obj
);
91 return InstrProfCorrelatorImpl
<uint32_t>::get(std::move(*CtxOrErr
), *Obj
);
93 return make_error
<InstrProfError
>(
94 instrprof_error::unable_to_correlate_profile
, "not an object file");
97 std::optional
<size_t> InstrProfCorrelator::getDataSize() const {
98 if (auto *C
= dyn_cast
<InstrProfCorrelatorImpl
<uint32_t>>(this)) {
99 return C
->getDataSize();
100 } else if (auto *C
= dyn_cast
<InstrProfCorrelatorImpl
<uint64_t>>(this)) {
101 return C
->getDataSize();
109 InstrProfCorrelatorImpl
<uint32_t>::InstrProfCorrelatorImpl(
110 std::unique_ptr
<InstrProfCorrelator::Context
> Ctx
)
111 : InstrProfCorrelatorImpl(InstrProfCorrelatorKind::CK_32Bit
,
114 InstrProfCorrelatorImpl
<uint64_t>::InstrProfCorrelatorImpl(
115 std::unique_ptr
<InstrProfCorrelator::Context
> Ctx
)
116 : InstrProfCorrelatorImpl(InstrProfCorrelatorKind::CK_64Bit
,
119 bool InstrProfCorrelatorImpl
<uint32_t>::classof(const InstrProfCorrelator
*C
) {
120 return C
->getKind() == InstrProfCorrelatorKind::CK_32Bit
;
123 bool InstrProfCorrelatorImpl
<uint64_t>::classof(const InstrProfCorrelator
*C
) {
124 return C
->getKind() == InstrProfCorrelatorKind::CK_64Bit
;
127 } // end namespace llvm
129 template <class IntPtrT
>
130 llvm::Expected
<std::unique_ptr
<InstrProfCorrelatorImpl
<IntPtrT
>>>
131 InstrProfCorrelatorImpl
<IntPtrT
>::get(
132 std::unique_ptr
<InstrProfCorrelator::Context
> Ctx
,
133 const object::ObjectFile
&Obj
) {
134 if (Obj
.isELF() || Obj
.isMachO()) {
135 auto DICtx
= DWARFContext::create(Obj
);
136 return std::make_unique
<DwarfInstrProfCorrelator
<IntPtrT
>>(std::move(DICtx
),
139 return make_error
<InstrProfError
>(
140 instrprof_error::unable_to_correlate_profile
,
141 "unsupported debug info format (only DWARF is supported)");
144 template <class IntPtrT
>
145 Error InstrProfCorrelatorImpl
<IntPtrT
>::correlateProfileData() {
146 assert(Data
.empty() && Names
.empty() && NamesVec
.empty());
147 correlateProfileDataImpl();
148 if (Data
.empty() || NamesVec
.empty())
149 return make_error
<InstrProfError
>(
150 instrprof_error::unable_to_correlate_profile
,
151 "could not find any profile metadata in debug info");
153 collectPGOFuncNameStrings(NamesVec
, /*doCompression=*/false, Names
);
154 CounterOffsets
.clear();
159 template <> struct yaml::MappingTraits
<InstrProfCorrelator::CorrelationData
> {
160 static void mapping(yaml::IO
&io
,
161 InstrProfCorrelator::CorrelationData
&Data
) {
162 io
.mapRequired("Probes", Data
.Probes
);
166 template <> struct yaml::MappingTraits
<InstrProfCorrelator::Probe
> {
167 static void mapping(yaml::IO
&io
, InstrProfCorrelator::Probe
&P
) {
168 io
.mapRequired("Function Name", P
.FunctionName
);
169 io
.mapOptional("Linkage Name", P
.LinkageName
);
170 io
.mapRequired("CFG Hash", P
.CFGHash
);
171 io
.mapRequired("Counter Offset", P
.CounterOffset
);
172 io
.mapRequired("Num Counters", P
.NumCounters
);
173 io
.mapOptional("File", P
.FilePath
);
174 io
.mapOptional("Line", P
.LineNumber
);
178 template <> struct yaml::SequenceElementTraits
<InstrProfCorrelator::Probe
> {
179 static const bool flow
= false;
182 template <class IntPtrT
>
183 Error InstrProfCorrelatorImpl
<IntPtrT
>::dumpYaml(raw_ostream
&OS
) {
184 InstrProfCorrelator::CorrelationData Data
;
185 correlateProfileDataImpl(&Data
);
186 if (Data
.Probes
.empty())
187 return make_error
<InstrProfError
>(
188 instrprof_error::unable_to_correlate_profile
,
189 "could not find any profile metadata in debug info");
190 yaml::Output
YamlOS(OS
);
192 return Error::success();
195 template <class IntPtrT
>
196 void InstrProfCorrelatorImpl
<IntPtrT
>::addProbe(StringRef FunctionName
,
198 IntPtrT CounterOffset
,
200 uint32_t NumCounters
) {
201 // Check if a probe was already added for this counter offset.
202 if (!CounterOffsets
.insert(CounterOffset
).second
)
205 maybeSwap
<uint64_t>(IndexedInstrProf::ComputeHash(FunctionName
)),
206 maybeSwap
<uint64_t>(CFGHash
),
207 // In this mode, CounterPtr actually stores the section relative address
209 maybeSwap
<IntPtrT
>(CounterOffset
),
210 maybeSwap
<IntPtrT
>(FunctionPtr
),
211 // TODO: Value profiling is not yet supported.
212 /*ValuesPtr=*/maybeSwap
<IntPtrT
>(0),
213 maybeSwap
<uint32_t>(NumCounters
),
214 /*NumValueSites=*/{maybeSwap
<uint16_t>(0), maybeSwap
<uint16_t>(0)},
216 NamesVec
.push_back(FunctionName
.str());
219 template <class IntPtrT
>
220 std::optional
<uint64_t>
221 DwarfInstrProfCorrelator
<IntPtrT
>::getLocation(const DWARFDie
&Die
) const {
222 auto Locations
= Die
.getLocations(dwarf::DW_AT_location
);
224 consumeError(Locations
.takeError());
227 auto &DU
= *Die
.getDwarfUnit();
228 auto AddressSize
= DU
.getAddressByteSize();
229 for (auto &Location
: *Locations
) {
230 DataExtractor
Data(Location
.Expr
, DICtx
->isLittleEndian(), AddressSize
);
231 DWARFExpression
Expr(Data
, AddressSize
);
232 for (auto &Op
: Expr
) {
233 if (Op
.getCode() == dwarf::DW_OP_addr
) {
234 return Op
.getRawOperand(0);
235 } else if (Op
.getCode() == dwarf::DW_OP_addrx
) {
236 uint64_t Index
= Op
.getRawOperand(0);
237 if (auto SA
= DU
.getAddrOffsetSectionItem(Index
))
245 template <class IntPtrT
>
246 bool DwarfInstrProfCorrelator
<IntPtrT
>::isDIEOfProbe(const DWARFDie
&Die
) {
247 const auto &ParentDie
= Die
.getParent();
248 if (!Die
.isValid() || !ParentDie
.isValid() || Die
.isNULL())
250 if (Die
.getTag() != dwarf::DW_TAG_variable
)
252 if (!ParentDie
.isSubprogramDIE())
254 if (!Die
.hasChildren())
256 if (const char *Name
= Die
.getName(DINameKind::ShortName
))
257 return StringRef(Name
).startswith(getInstrProfCountersVarPrefix());
261 template <class IntPtrT
>
262 void DwarfInstrProfCorrelator
<IntPtrT
>::correlateProfileDataImpl(
263 InstrProfCorrelator::CorrelationData
*Data
) {
264 auto maybeAddProbe
= [&](DWARFDie Die
) {
265 if (!isDIEOfProbe(Die
))
267 std::optional
<const char *> FunctionName
;
268 std::optional
<uint64_t> CFGHash
;
269 std::optional
<uint64_t> CounterPtr
= getLocation(Die
);
270 auto FnDie
= Die
.getParent();
271 auto FunctionPtr
= dwarf::toAddress(FnDie
.find(dwarf::DW_AT_low_pc
));
272 std::optional
<uint64_t> NumCounters
;
273 for (const DWARFDie
&Child
: Die
.children()) {
274 if (Child
.getTag() != dwarf::DW_TAG_LLVM_annotation
)
276 auto AnnotationFormName
= Child
.find(dwarf::DW_AT_name
);
277 auto AnnotationFormValue
= Child
.find(dwarf::DW_AT_const_value
);
278 if (!AnnotationFormName
|| !AnnotationFormValue
)
280 auto AnnotationNameOrErr
= AnnotationFormName
->getAsCString();
281 if (auto Err
= AnnotationNameOrErr
.takeError()) {
282 consumeError(std::move(Err
));
285 StringRef AnnotationName
= *AnnotationNameOrErr
;
286 if (AnnotationName
.compare(
287 InstrProfCorrelator::FunctionNameAttributeName
) == 0) {
289 AnnotationFormValue
->getAsCString().moveInto(FunctionName
))
290 consumeError(std::move(EC
));
291 } else if (AnnotationName
.compare(
292 InstrProfCorrelator::CFGHashAttributeName
) == 0) {
293 CFGHash
= AnnotationFormValue
->getAsUnsignedConstant();
294 } else if (AnnotationName
.compare(
295 InstrProfCorrelator::NumCountersAttributeName
) == 0) {
296 NumCounters
= AnnotationFormValue
->getAsUnsignedConstant();
299 if (!FunctionName
|| !CFGHash
|| !CounterPtr
|| !NumCounters
) {
300 LLVM_DEBUG(dbgs() << "Incomplete DIE for probe\n\tFunctionName: "
301 << FunctionName
<< "\n\tCFGHash: " << CFGHash
302 << "\n\tCounterPtr: " << CounterPtr
303 << "\n\tNumCounters: " << NumCounters
);
304 LLVM_DEBUG(Die
.dump(dbgs()));
307 uint64_t CountersStart
= this->Ctx
->CountersSectionStart
;
308 uint64_t CountersEnd
= this->Ctx
->CountersSectionEnd
;
309 if (*CounterPtr
< CountersStart
|| *CounterPtr
>= CountersEnd
) {
311 dbgs() << "CounterPtr out of range for probe\n\tFunction Name: "
312 << FunctionName
<< "\n\tExpected: [0x"
313 << Twine::utohexstr(CountersStart
) << ", 0x"
314 << Twine::utohexstr(CountersEnd
) << ")\n\tActual: 0x"
315 << Twine::utohexstr(*CounterPtr
));
316 LLVM_DEBUG(Die
.dump(dbgs()));
320 LLVM_DEBUG(dbgs() << "Could not find address of " << *FunctionName
322 LLVM_DEBUG(Die
.dump(dbgs()));
324 IntPtrT CounterOffset
= *CounterPtr
- CountersStart
;
326 InstrProfCorrelator::Probe P
;
327 P
.FunctionName
= *FunctionName
;
328 if (auto Name
= FnDie
.getName(DINameKind::LinkageName
))
329 P
.LinkageName
= Name
;
330 P
.CFGHash
= *CFGHash
;
331 P
.CounterOffset
= CounterOffset
;
332 P
.NumCounters
= *NumCounters
;
333 auto FilePath
= FnDie
.getDeclFile(
334 DILineInfoSpecifier::FileLineInfoKind::RelativeFilePath
);
335 if (!FilePath
.empty())
336 P
.FilePath
= FilePath
;
337 if (auto LineNumber
= FnDie
.getDeclLine())
338 P
.LineNumber
= LineNumber
;
339 Data
->Probes
.push_back(P
);
341 this->addProbe(*FunctionName
, *CFGHash
, CounterOffset
,
342 FunctionPtr
.value_or(0), *NumCounters
);
345 for (auto &CU
: DICtx
->normal_units())
346 for (const auto &Entry
: CU
->dies())
347 maybeAddProbe(DWARFDie(CU
.get(), &Entry
));
348 for (auto &CU
: DICtx
->dwo_units())
349 for (const auto &Entry
: CU
->dies())
350 maybeAddProbe(DWARFDie(CU
.get(), &Entry
));