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"
19 #include "llvm/Support/Format.h"
20 #include "llvm/Support/WithColor.h"
23 #define DEBUG_TYPE "correlator"
27 /// Get profile section.
28 Expected
<object::SectionRef
> getInstrProfSection(const object::ObjectFile
&Obj
,
29 InstrProfSectKind IPSK
) {
30 Triple::ObjectFormatType ObjFormat
= Obj
.getTripleObjectFormat();
31 std::string ExpectedSectionName
=
32 getInstrProfSectionName(IPSK
, ObjFormat
,
33 /*AddSegmentInfo=*/false);
34 for (auto &Section
: Obj
.sections())
35 if (auto SectionName
= Section
.getName())
36 if (SectionName
.get() == ExpectedSectionName
)
38 return make_error
<InstrProfError
>(
39 instrprof_error::unable_to_correlate_profile
,
40 "could not find section (" + Twine(ExpectedSectionName
) + ")");
43 const char *InstrProfCorrelator::FunctionNameAttributeName
= "Function Name";
44 const char *InstrProfCorrelator::CFGHashAttributeName
= "CFG Hash";
45 const char *InstrProfCorrelator::NumCountersAttributeName
= "Num Counters";
47 llvm::Expected
<std::unique_ptr
<InstrProfCorrelator::Context
>>
48 InstrProfCorrelator::Context::get(std::unique_ptr
<MemoryBuffer
> Buffer
,
49 const object::ObjectFile
&Obj
) {
50 auto CountersSection
= getInstrProfSection(Obj
, IPSK_cnts
);
51 if (auto Err
= CountersSection
.takeError())
52 return std::move(Err
);
53 auto C
= std::make_unique
<Context
>();
54 C
->Buffer
= std::move(Buffer
);
55 C
->CountersSectionStart
= CountersSection
->getAddress();
56 C
->CountersSectionEnd
= C
->CountersSectionStart
+ CountersSection
->getSize();
57 C
->ShouldSwapBytes
= Obj
.isLittleEndian() != sys::IsLittleEndianHost
;
58 return Expected
<std::unique_ptr
<Context
>>(std::move(C
));
61 llvm::Expected
<std::unique_ptr
<InstrProfCorrelator
>>
62 InstrProfCorrelator::get(StringRef Filename
, ProfCorrelatorKind FileKind
) {
63 if (FileKind
== DEBUG_INFO
) {
64 auto DsymObjectsOrErr
=
65 object::MachOObjectFile::findDsymObjectMembers(Filename
);
66 if (auto Err
= DsymObjectsOrErr
.takeError())
67 return std::move(Err
);
68 if (!DsymObjectsOrErr
->empty()) {
69 // TODO: Enable profile correlation when there are multiple objects in a
71 if (DsymObjectsOrErr
->size() > 1)
72 return make_error
<InstrProfError
>(
73 instrprof_error::unable_to_correlate_profile
,
74 "using multiple objects is not yet supported");
75 Filename
= *DsymObjectsOrErr
->begin();
78 auto BufferOrErr
= errorOrToExpected(MemoryBuffer::getFile(Filename
));
79 if (auto Err
= BufferOrErr
.takeError())
80 return std::move(Err
);
82 return get(std::move(*BufferOrErr
), FileKind
);
85 llvm::Expected
<std::unique_ptr
<InstrProfCorrelator
>>
86 InstrProfCorrelator::get(std::unique_ptr
<MemoryBuffer
> Buffer
,
87 ProfCorrelatorKind FileKind
) {
88 auto BinOrErr
= object::createBinary(*Buffer
);
89 if (auto Err
= BinOrErr
.takeError())
90 return std::move(Err
);
92 if (auto *Obj
= dyn_cast
<object::ObjectFile
>(BinOrErr
->get())) {
93 auto CtxOrErr
= Context::get(std::move(Buffer
), *Obj
);
94 if (auto Err
= CtxOrErr
.takeError())
95 return std::move(Err
);
96 auto T
= Obj
->makeTriple();
98 return InstrProfCorrelatorImpl
<uint64_t>::get(std::move(*CtxOrErr
), *Obj
,
101 return InstrProfCorrelatorImpl
<uint32_t>::get(std::move(*CtxOrErr
), *Obj
,
104 return make_error
<InstrProfError
>(
105 instrprof_error::unable_to_correlate_profile
, "not an object file");
108 std::optional
<size_t> InstrProfCorrelator::getDataSize() const {
109 if (auto *C
= dyn_cast
<InstrProfCorrelatorImpl
<uint32_t>>(this)) {
110 return C
->getDataSize();
111 } else if (auto *C
= dyn_cast
<InstrProfCorrelatorImpl
<uint64_t>>(this)) {
112 return C
->getDataSize();
120 InstrProfCorrelatorImpl
<uint32_t>::InstrProfCorrelatorImpl(
121 std::unique_ptr
<InstrProfCorrelator::Context
> Ctx
)
122 : InstrProfCorrelatorImpl(InstrProfCorrelatorKind::CK_32Bit
,
125 InstrProfCorrelatorImpl
<uint64_t>::InstrProfCorrelatorImpl(
126 std::unique_ptr
<InstrProfCorrelator::Context
> Ctx
)
127 : InstrProfCorrelatorImpl(InstrProfCorrelatorKind::CK_64Bit
,
130 bool InstrProfCorrelatorImpl
<uint32_t>::classof(const InstrProfCorrelator
*C
) {
131 return C
->getKind() == InstrProfCorrelatorKind::CK_32Bit
;
134 bool InstrProfCorrelatorImpl
<uint64_t>::classof(const InstrProfCorrelator
*C
) {
135 return C
->getKind() == InstrProfCorrelatorKind::CK_64Bit
;
138 } // end namespace llvm
140 template <class IntPtrT
>
141 llvm::Expected
<std::unique_ptr
<InstrProfCorrelatorImpl
<IntPtrT
>>>
142 InstrProfCorrelatorImpl
<IntPtrT
>::get(
143 std::unique_ptr
<InstrProfCorrelator::Context
> Ctx
,
144 const object::ObjectFile
&Obj
, ProfCorrelatorKind FileKind
) {
145 if (FileKind
== DEBUG_INFO
) {
146 if (Obj
.isELF() || Obj
.isMachO()) {
147 auto DICtx
= DWARFContext::create(Obj
);
148 return std::make_unique
<DwarfInstrProfCorrelator
<IntPtrT
>>(
149 std::move(DICtx
), std::move(Ctx
));
151 return make_error
<InstrProfError
>(
152 instrprof_error::unable_to_correlate_profile
,
153 "unsupported debug info format (only DWARF is supported)");
155 return make_error
<InstrProfError
>(
156 instrprof_error::unable_to_correlate_profile
,
157 "unsupported correlation file type (only DWARF is supported)");
160 template <class IntPtrT
>
161 Error InstrProfCorrelatorImpl
<IntPtrT
>::correlateProfileData(int MaxWarnings
) {
162 assert(Data
.empty() && Names
.empty() && NamesVec
.empty());
163 correlateProfileDataImpl(MaxWarnings
);
164 if (this->Data
.empty())
165 return make_error
<InstrProfError
>(
166 instrprof_error::unable_to_correlate_profile
,
167 "could not find any profile data metadata in correlated file");
168 Error Result
= correlateProfileNameImpl();
169 this->CounterOffsets
.clear();
170 this->NamesVec
.clear();
174 template <> struct yaml::MappingTraits
<InstrProfCorrelator::CorrelationData
> {
175 static void mapping(yaml::IO
&io
,
176 InstrProfCorrelator::CorrelationData
&Data
) {
177 io
.mapRequired("Probes", Data
.Probes
);
181 template <> struct yaml::MappingTraits
<InstrProfCorrelator::Probe
> {
182 static void mapping(yaml::IO
&io
, InstrProfCorrelator::Probe
&P
) {
183 io
.mapRequired("Function Name", P
.FunctionName
);
184 io
.mapOptional("Linkage Name", P
.LinkageName
);
185 io
.mapRequired("CFG Hash", P
.CFGHash
);
186 io
.mapRequired("Counter Offset", P
.CounterOffset
);
187 io
.mapRequired("Num Counters", P
.NumCounters
);
188 io
.mapOptional("File", P
.FilePath
);
189 io
.mapOptional("Line", P
.LineNumber
);
193 template <> struct yaml::SequenceElementTraits
<InstrProfCorrelator::Probe
> {
194 static const bool flow
= false;
197 template <class IntPtrT
>
198 Error InstrProfCorrelatorImpl
<IntPtrT
>::dumpYaml(int MaxWarnings
,
200 InstrProfCorrelator::CorrelationData Data
;
201 correlateProfileDataImpl(MaxWarnings
, &Data
);
202 if (Data
.Probes
.empty())
203 return make_error
<InstrProfError
>(
204 instrprof_error::unable_to_correlate_profile
,
205 "could not find any profile data metadata in debug info");
206 yaml::Output
YamlOS(OS
);
208 return Error::success();
211 template <class IntPtrT
>
212 void InstrProfCorrelatorImpl
<IntPtrT
>::addProbe(StringRef FunctionName
,
214 IntPtrT CounterOffset
,
216 uint32_t NumCounters
) {
217 // Check if a probe was already added for this counter offset.
218 if (!CounterOffsets
.insert(CounterOffset
).second
)
221 maybeSwap
<uint64_t>(IndexedInstrProf::ComputeHash(FunctionName
)),
222 maybeSwap
<uint64_t>(CFGHash
),
223 // In this mode, CounterPtr actually stores the section relative address
225 maybeSwap
<IntPtrT
>(CounterOffset
),
226 // TODO: MC/DC is not yet supported.
227 /*BitmapOffset=*/maybeSwap
<IntPtrT
>(0),
228 maybeSwap
<IntPtrT
>(FunctionPtr
),
229 // TODO: Value profiling is not yet supported.
230 /*ValuesPtr=*/maybeSwap
<IntPtrT
>(0),
231 maybeSwap
<uint32_t>(NumCounters
),
232 /*NumValueSites=*/{maybeSwap
<uint16_t>(0), maybeSwap
<uint16_t>(0)},
233 // TODO: MC/DC is not yet supported.
234 /*NumBitmapBytes=*/maybeSwap
<uint32_t>(0),
236 NamesVec
.push_back(FunctionName
.str());
239 template <class IntPtrT
>
240 std::optional
<uint64_t>
241 DwarfInstrProfCorrelator
<IntPtrT
>::getLocation(const DWARFDie
&Die
) const {
242 auto Locations
= Die
.getLocations(dwarf::DW_AT_location
);
244 consumeError(Locations
.takeError());
247 auto &DU
= *Die
.getDwarfUnit();
248 auto AddressSize
= DU
.getAddressByteSize();
249 for (auto &Location
: *Locations
) {
250 DataExtractor
Data(Location
.Expr
, DICtx
->isLittleEndian(), AddressSize
);
251 DWARFExpression
Expr(Data
, AddressSize
);
252 for (auto &Op
: Expr
) {
253 if (Op
.getCode() == dwarf::DW_OP_addr
) {
254 return Op
.getRawOperand(0);
255 } else if (Op
.getCode() == dwarf::DW_OP_addrx
) {
256 uint64_t Index
= Op
.getRawOperand(0);
257 if (auto SA
= DU
.getAddrOffsetSectionItem(Index
))
265 template <class IntPtrT
>
266 bool DwarfInstrProfCorrelator
<IntPtrT
>::isDIEOfProbe(const DWARFDie
&Die
) {
267 const auto &ParentDie
= Die
.getParent();
268 if (!Die
.isValid() || !ParentDie
.isValid() || Die
.isNULL())
270 if (Die
.getTag() != dwarf::DW_TAG_variable
)
272 if (!ParentDie
.isSubprogramDIE())
274 if (!Die
.hasChildren())
276 if (const char *Name
= Die
.getName(DINameKind::ShortName
))
277 return StringRef(Name
).startswith(getInstrProfCountersVarPrefix());
281 template <class IntPtrT
>
282 void DwarfInstrProfCorrelator
<IntPtrT
>::correlateProfileDataImpl(
283 int MaxWarnings
, InstrProfCorrelator::CorrelationData
*Data
) {
284 bool UnlimitedWarnings
= (MaxWarnings
== 0);
285 // -N suppressed warnings means we can emit up to N (unsuppressed) warnings
286 int NumSuppressedWarnings
= -MaxWarnings
;
287 auto maybeAddProbe
= [&](DWARFDie Die
) {
288 if (!isDIEOfProbe(Die
))
290 std::optional
<const char *> FunctionName
;
291 std::optional
<uint64_t> CFGHash
;
292 std::optional
<uint64_t> CounterPtr
= getLocation(Die
);
293 auto FnDie
= Die
.getParent();
294 auto FunctionPtr
= dwarf::toAddress(FnDie
.find(dwarf::DW_AT_low_pc
));
295 std::optional
<uint64_t> NumCounters
;
296 for (const DWARFDie
&Child
: Die
.children()) {
297 if (Child
.getTag() != dwarf::DW_TAG_LLVM_annotation
)
299 auto AnnotationFormName
= Child
.find(dwarf::DW_AT_name
);
300 auto AnnotationFormValue
= Child
.find(dwarf::DW_AT_const_value
);
301 if (!AnnotationFormName
|| !AnnotationFormValue
)
303 auto AnnotationNameOrErr
= AnnotationFormName
->getAsCString();
304 if (auto Err
= AnnotationNameOrErr
.takeError()) {
305 consumeError(std::move(Err
));
308 StringRef AnnotationName
= *AnnotationNameOrErr
;
309 if (AnnotationName
.compare(
310 InstrProfCorrelator::FunctionNameAttributeName
) == 0) {
312 AnnotationFormValue
->getAsCString().moveInto(FunctionName
))
313 consumeError(std::move(EC
));
314 } else if (AnnotationName
.compare(
315 InstrProfCorrelator::CFGHashAttributeName
) == 0) {
316 CFGHash
= AnnotationFormValue
->getAsUnsignedConstant();
317 } else if (AnnotationName
.compare(
318 InstrProfCorrelator::NumCountersAttributeName
) == 0) {
319 NumCounters
= AnnotationFormValue
->getAsUnsignedConstant();
322 if (!FunctionName
|| !CFGHash
|| !CounterPtr
|| !NumCounters
) {
323 if (UnlimitedWarnings
|| ++NumSuppressedWarnings
< 1) {
325 << "Incomplete DIE for function " << FunctionName
326 << ": CFGHash=" << CFGHash
<< " CounterPtr=" << CounterPtr
327 << " NumCounters=" << NumCounters
<< "\n";
328 LLVM_DEBUG(Die
.dump(dbgs()));
332 uint64_t CountersStart
= this->Ctx
->CountersSectionStart
;
333 uint64_t CountersEnd
= this->Ctx
->CountersSectionEnd
;
334 if (*CounterPtr
< CountersStart
|| *CounterPtr
>= CountersEnd
) {
335 if (UnlimitedWarnings
|| ++NumSuppressedWarnings
< 1) {
337 << format("CounterPtr out of range for function %s: Actual=0x%x "
338 "Expected=[0x%x, 0x%x)\n",
339 *FunctionName
, *CounterPtr
, CountersStart
, CountersEnd
);
340 LLVM_DEBUG(Die
.dump(dbgs()));
344 if (!FunctionPtr
&& (UnlimitedWarnings
|| ++NumSuppressedWarnings
< 1)) {
345 WithColor::warning() << format("Could not find address of function %s\n",
347 LLVM_DEBUG(Die
.dump(dbgs()));
349 IntPtrT CounterOffset
= *CounterPtr
- CountersStart
;
351 InstrProfCorrelator::Probe P
;
352 P
.FunctionName
= *FunctionName
;
353 if (auto Name
= FnDie
.getName(DINameKind::LinkageName
))
354 P
.LinkageName
= Name
;
355 P
.CFGHash
= *CFGHash
;
356 P
.CounterOffset
= CounterOffset
;
357 P
.NumCounters
= *NumCounters
;
358 auto FilePath
= FnDie
.getDeclFile(
359 DILineInfoSpecifier::FileLineInfoKind::RelativeFilePath
);
360 if (!FilePath
.empty())
361 P
.FilePath
= FilePath
;
362 if (auto LineNumber
= FnDie
.getDeclLine())
363 P
.LineNumber
= LineNumber
;
364 Data
->Probes
.push_back(P
);
366 this->addProbe(*FunctionName
, *CFGHash
, CounterOffset
,
367 FunctionPtr
.value_or(0), *NumCounters
);
370 for (auto &CU
: DICtx
->normal_units())
371 for (const auto &Entry
: CU
->dies())
372 maybeAddProbe(DWARFDie(CU
.get(), &Entry
));
373 for (auto &CU
: DICtx
->dwo_units())
374 for (const auto &Entry
: CU
->dies())
375 maybeAddProbe(DWARFDie(CU
.get(), &Entry
));
377 if (!UnlimitedWarnings
&& NumSuppressedWarnings
> 0)
378 WithColor::warning() << format("Suppressed %d additional warnings\n",
379 NumSuppressedWarnings
);
382 template <class IntPtrT
>
383 Error DwarfInstrProfCorrelator
<IntPtrT
>::correlateProfileNameImpl() {
384 if (this->NamesVec
.empty()) {
385 return make_error
<InstrProfError
>(
386 instrprof_error::unable_to_correlate_profile
,
387 "could not find any profile name metadata in debug info");
390 collectGlobalObjectNameStrings(this->NamesVec
,
391 /*doCompression=*/false, this->Names
);