LLVM 18.0.0git
InstrProfCorrelator.cpp
Go to the documentation of this file.
1//===-- InstrProfCorrelator.cpp -------------------------------------------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8
17#include "llvm/Object/MachO.h"
18#include "llvm/Support/Debug.h"
19#include "llvm/Support/Format.h"
21#include <optional>
22
23#define DEBUG_TYPE "correlator"
24
25using namespace llvm;
26
27/// Get profile section.
29 InstrProfSectKind IPSK) {
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)
37 return Section;
38 return make_error<InstrProfError>(
39 instrprof_error::unable_to_correlate_profile,
40 "could not find section (" + Twine(ExpectedSectionName) + ")");
41}
42
43const char *InstrProfCorrelator::FunctionNameAttributeName = "Function Name";
44const char *InstrProfCorrelator::CFGHashAttributeName = "CFG Hash";
45const char *InstrProfCorrelator::NumCountersAttributeName = "Num Counters";
46
48InstrProfCorrelator::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));
59}
60
63 if (FileKind == DEBUG_INFO) {
64 auto DsymObjectsOrErr =
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
70 // dSYM bundle.
71 if (DsymObjectsOrErr->size() > 1)
72 return make_error<InstrProfError>(
74 "using multiple objects is not yet supported");
75 Filename = *DsymObjectsOrErr->begin();
76 }
77 auto BufferOrErr = errorOrToExpected(MemoryBuffer::getFile(Filename));
78 if (auto Err = BufferOrErr.takeError())
79 return std::move(Err);
80
81 return get(std::move(*BufferOrErr), FileKind);
82 }
83 return make_error<InstrProfError>(
85 "unsupported correlation kind (only DWARF debug info is supported)");
86}
87
89InstrProfCorrelator::get(std::unique_ptr<MemoryBuffer> Buffer,
90 ProfCorrelatorKind FileKind) {
91 auto BinOrErr = object::createBinary(*Buffer);
92 if (auto Err = BinOrErr.takeError())
93 return std::move(Err);
94
95 if (auto *Obj = dyn_cast<object::ObjectFile>(BinOrErr->get())) {
96 auto CtxOrErr = Context::get(std::move(Buffer), *Obj);
97 if (auto Err = CtxOrErr.takeError())
98 return std::move(Err);
99 auto T = Obj->makeTriple();
100 if (T.isArch64Bit())
101 return InstrProfCorrelatorImpl<uint64_t>::get(std::move(*CtxOrErr), *Obj,
102 FileKind);
103 if (T.isArch32Bit())
104 return InstrProfCorrelatorImpl<uint32_t>::get(std::move(*CtxOrErr), *Obj,
105 FileKind);
106 }
107 return make_error<InstrProfError>(
109}
110
111std::optional<size_t> InstrProfCorrelator::getDataSize() const {
112 if (auto *C = dyn_cast<InstrProfCorrelatorImpl<uint32_t>>(this)) {
113 return C->getDataSize();
114 } else if (auto *C = dyn_cast<InstrProfCorrelatorImpl<uint64_t>>(this)) {
115 return C->getDataSize();
116 }
117 return {};
118}
119
120namespace llvm {
121
122template <>
124 std::unique_ptr<InstrProfCorrelator::Context> Ctx)
126 std::move(Ctx)) {}
127template <>
129 std::unique_ptr<InstrProfCorrelator::Context> Ctx)
131 std::move(Ctx)) {}
132template <>
134 return C->getKind() == InstrProfCorrelatorKind::CK_32Bit;
135}
136template <>
138 return C->getKind() == InstrProfCorrelatorKind::CK_64Bit;
139}
140
141} // end namespace llvm
142
143template <class IntPtrT>
146 std::unique_ptr<InstrProfCorrelator::Context> Ctx,
147 const object::ObjectFile &Obj, ProfCorrelatorKind FileKind) {
148 if (FileKind == DEBUG_INFO) {
149 if (Obj.isELF() || Obj.isMachO()) {
150 auto DICtx = DWARFContext::create(Obj);
151 return std::make_unique<DwarfInstrProfCorrelator<IntPtrT>>(
152 std::move(DICtx), std::move(Ctx));
153 }
154 return make_error<InstrProfError>(
155 instrprof_error::unable_to_correlate_profile,
156 "unsupported debug info format (only DWARF is supported)");
157 }
158 return make_error<InstrProfError>(
159 instrprof_error::unable_to_correlate_profile,
160 "unsupported correlation file type (only DWARF is supported)");
161}
162
163template <class IntPtrT>
165 assert(Data.empty() && Names.empty() && NamesVec.empty());
166 correlateProfileDataImpl(MaxWarnings);
167 if (this->Data.empty())
168 return make_error<InstrProfError>(
169 instrprof_error::unable_to_correlate_profile,
170 "could not find any profile data metadata in correlated file");
171 Error Result = correlateProfileNameImpl();
172 this->CounterOffsets.clear();
173 this->NamesVec.clear();
174 return Result;
175}
176
177template <> struct yaml::MappingTraits<InstrProfCorrelator::CorrelationData> {
178 static void mapping(yaml::IO &io,
180 io.mapRequired("Probes", Data.Probes);
181 }
182};
183
184template <> struct yaml::MappingTraits<InstrProfCorrelator::Probe> {
185 static void mapping(yaml::IO &io, InstrProfCorrelator::Probe &P) {
186 io.mapRequired("Function Name", P.FunctionName);
187 io.mapOptional("Linkage Name", P.LinkageName);
188 io.mapRequired("CFG Hash", P.CFGHash);
189 io.mapRequired("Counter Offset", P.CounterOffset);
190 io.mapRequired("Num Counters", P.NumCounters);
191 io.mapOptional("File", P.FilePath);
192 io.mapOptional("Line", P.LineNumber);
193 }
194};
195
196template <> struct yaml::SequenceElementTraits<InstrProfCorrelator::Probe> {
197 static const bool flow = false;
198};
199
200template <class IntPtrT>
202 raw_ostream &OS) {
204 correlateProfileDataImpl(MaxWarnings, &Data);
205 if (Data.Probes.empty())
206 return make_error<InstrProfError>(
207 instrprof_error::unable_to_correlate_profile,
208 "could not find any profile data metadata in debug info");
209 yaml::Output YamlOS(OS);
210 YamlOS << Data;
211 return Error::success();
212}
213
214template <class IntPtrT>
216 uint64_t CFGHash,
217 IntPtrT CounterOffset,
218 IntPtrT FunctionPtr,
219 uint32_t NumCounters) {
220 // Check if a probe was already added for this counter offset.
221 if (!CounterOffsets.insert(CounterOffset).second)
222 return;
223 Data.push_back({
224 maybeSwap<uint64_t>(IndexedInstrProf::ComputeHash(FunctionName)),
225 maybeSwap<uint64_t>(CFGHash),
226 // In this mode, CounterPtr actually stores the section relative address
227 // of the counter.
228 maybeSwap<IntPtrT>(CounterOffset),
229 // TODO: MC/DC is not yet supported.
230 /*BitmapOffset=*/maybeSwap<IntPtrT>(0),
231 maybeSwap<IntPtrT>(FunctionPtr),
232 // TODO: Value profiling is not yet supported.
233 /*ValuesPtr=*/maybeSwap<IntPtrT>(0),
234 maybeSwap<uint32_t>(NumCounters),
235 /*NumValueSites=*/{maybeSwap<uint16_t>(0), maybeSwap<uint16_t>(0)},
236 // TODO: MC/DC is not yet supported.
237 /*NumBitmapBytes=*/maybeSwap<uint32_t>(0),
238 });
239 NamesVec.push_back(FunctionName.str());
240}
241
242template <class IntPtrT>
243std::optional<uint64_t>
245 auto Locations = Die.getLocations(dwarf::DW_AT_location);
246 if (!Locations) {
247 consumeError(Locations.takeError());
248 return {};
249 }
250 auto &DU = *Die.getDwarfUnit();
251 auto AddressSize = DU.getAddressByteSize();
252 for (auto &Location : *Locations) {
253 DataExtractor Data(Location.Expr, DICtx->isLittleEndian(), AddressSize);
254 DWARFExpression Expr(Data, AddressSize);
255 for (auto &Op : Expr) {
256 if (Op.getCode() == dwarf::DW_OP_addr) {
257 return Op.getRawOperand(0);
258 } else if (Op.getCode() == dwarf::DW_OP_addrx) {
260 if (auto SA = DU.getAddrOffsetSectionItem(Index))
261 return SA->Address;
262 }
263 }
264 }
265 return {};
266}
267
268template <class IntPtrT>
270 const auto &ParentDie = Die.getParent();
271 if (!Die.isValid() || !ParentDie.isValid() || Die.isNULL())
272 return false;
273 if (Die.getTag() != dwarf::DW_TAG_variable)
274 return false;
275 if (!ParentDie.isSubprogramDIE())
276 return false;
277 if (!Die.hasChildren())
278 return false;
279 if (const char *Name = Die.getName(DINameKind::ShortName))
281 return false;
282}
283
284template <class IntPtrT>
286 int MaxWarnings, InstrProfCorrelator::CorrelationData *Data) {
287 bool UnlimitedWarnings = (MaxWarnings == 0);
288 // -N suppressed warnings means we can emit up to N (unsuppressed) warnings
289 int NumSuppressedWarnings = -MaxWarnings;
290 auto maybeAddProbe = [&](DWARFDie Die) {
291 if (!isDIEOfProbe(Die))
292 return;
293 std::optional<const char *> FunctionName;
294 std::optional<uint64_t> CFGHash;
295 std::optional<uint64_t> CounterPtr = getLocation(Die);
296 auto FnDie = Die.getParent();
297 auto FunctionPtr = dwarf::toAddress(FnDie.find(dwarf::DW_AT_low_pc));
298 std::optional<uint64_t> NumCounters;
299 for (const DWARFDie &Child : Die.children()) {
300 if (Child.getTag() != dwarf::DW_TAG_LLVM_annotation)
301 continue;
302 auto AnnotationFormName = Child.find(dwarf::DW_AT_name);
303 auto AnnotationFormValue = Child.find(dwarf::DW_AT_const_value);
304 if (!AnnotationFormName || !AnnotationFormValue)
305 continue;
306 auto AnnotationNameOrErr = AnnotationFormName->getAsCString();
307 if (auto Err = AnnotationNameOrErr.takeError()) {
308 consumeError(std::move(Err));
309 continue;
310 }
311 StringRef AnnotationName = *AnnotationNameOrErr;
312 if (AnnotationName.compare(
314 if (auto EC =
315 AnnotationFormValue->getAsCString().moveInto(FunctionName))
316 consumeError(std::move(EC));
317 } else if (AnnotationName.compare(
319 CFGHash = AnnotationFormValue->getAsUnsignedConstant();
320 } else if (AnnotationName.compare(
322 NumCounters = AnnotationFormValue->getAsUnsignedConstant();
323 }
324 }
325 if (!FunctionName || !CFGHash || !CounterPtr || !NumCounters) {
326 if (UnlimitedWarnings || ++NumSuppressedWarnings < 1) {
328 << "Incomplete DIE for function " << FunctionName
329 << ": CFGHash=" << CFGHash << " CounterPtr=" << CounterPtr
330 << " NumCounters=" << NumCounters << "\n";
331 LLVM_DEBUG(Die.dump(dbgs()));
332 }
333 return;
334 }
335 uint64_t CountersStart = this->Ctx->CountersSectionStart;
336 uint64_t CountersEnd = this->Ctx->CountersSectionEnd;
337 if (*CounterPtr < CountersStart || *CounterPtr >= CountersEnd) {
338 if (UnlimitedWarnings || ++NumSuppressedWarnings < 1) {
340 << format("CounterPtr out of range for function %s: Actual=0x%x "
341 "Expected=[0x%x, 0x%x)\n",
342 *FunctionName, *CounterPtr, CountersStart, CountersEnd);
343 LLVM_DEBUG(Die.dump(dbgs()));
344 }
345 return;
346 }
347 if (!FunctionPtr && (UnlimitedWarnings || ++NumSuppressedWarnings < 1)) {
348 WithColor::warning() << format("Could not find address of function %s\n",
349 *FunctionName);
350 LLVM_DEBUG(Die.dump(dbgs()));
351 }
352 IntPtrT CounterOffset = *CounterPtr - CountersStart;
353 if (Data) {
355 P.FunctionName = *FunctionName;
356 if (auto Name = FnDie.getName(DINameKind::LinkageName))
357 P.LinkageName = Name;
358 P.CFGHash = *CFGHash;
359 P.CounterOffset = CounterOffset;
360 P.NumCounters = *NumCounters;
361 auto FilePath = FnDie.getDeclFile(
362 DILineInfoSpecifier::FileLineInfoKind::RelativeFilePath);
363 if (!FilePath.empty())
364 P.FilePath = FilePath;
365 if (auto LineNumber = FnDie.getDeclLine())
366 P.LineNumber = LineNumber;
367 Data->Probes.push_back(P);
368 } else {
369 this->addProbe(*FunctionName, *CFGHash, CounterOffset,
370 FunctionPtr.value_or(0), *NumCounters);
371 }
372 };
373 for (auto &CU : DICtx->normal_units())
374 for (const auto &Entry : CU->dies())
375 maybeAddProbe(DWARFDie(CU.get(), &Entry));
376 for (auto &CU : DICtx->dwo_units())
377 for (const auto &Entry : CU->dies())
378 maybeAddProbe(DWARFDie(CU.get(), &Entry));
379
380 if (!UnlimitedWarnings && NumSuppressedWarnings > 0)
381 WithColor::warning() << format("Suppressed %d additional warnings\n",
382 NumSuppressedWarnings);
383}
384
385template <class IntPtrT>
387 if (this->NamesVec.empty()) {
388 return make_error<InstrProfError>(
389 instrprof_error::unable_to_correlate_profile,
390 "could not find any profile name metadata in debug info");
391 }
392 auto Result =
393 collectGlobalObjectNameStrings(this->NamesVec,
394 /*doCompression=*/false, this->Names);
395 return Result;
396}
#define LLVM_DEBUG(X)
Definition: Debug.h:101
std::string Name
Expected< object::SectionRef > getInstrProfSection(const object::ObjectFile &Obj, InstrProfSectKind IPSK)
Get profile section.
#define P(N)
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
static MemoryLocation getLocation(Instruction *I)
raw_pwrite_stream & OS
static std::unique_ptr< DWARFContext > create(const object::ObjectFile &Obj, ProcessDebugRelocations RelocAction=ProcessDebugRelocations::Process, const LoadedObjectInfo *L=nullptr, std::string DWPName="", std::function< void(Error)> RecoverableErrorHandler=WithColor::defaultErrorHandler, std::function< void(Error)> WarningHandler=WithColor::defaultWarningHandler, bool ThreadSafe=false)
Utility class that carries the DWARF compile/type unit and the debug info entry in an object.
Definition: DWARFDie.h:42
iterator_range< iterator > children() const
Definition: DWARFDie.h:395
DWARFDie getParent() const
Get the parent of this DIE object.
Definition: DWARFDie.cpp:624
DWARFUnit * getDwarfUnit() const
Definition: DWARFDie.h:53
bool hasChildren() const
Definition: DWARFDie.h:78
const char * getName(DINameKind Kind) const
Return the DIE name resolving DW_AT_specification or DW_AT_abstract_origin references if necessary.
Definition: DWARFDie.cpp:444
dwarf::Tag getTag() const
Definition: DWARFDie.h:71
Expected< DWARFLocationExpressionsVector > getLocations(dwarf::Attribute Attr) const
Definition: DWARFDie.cpp:408
bool isNULL() const
Returns true for a valid DIE that terminates a sibling chain.
Definition: DWARFDie.h:84
bool isValid() const
Definition: DWARFDie.h:50
void dump(raw_ostream &OS, unsigned indent=0, DIDumpOptions DumpOpts=DIDumpOptions()) const
Dump the DIE and all of its attributes to the supplied stream.
Definition: DWARFDie.cpp:564
This class represents an Operation in the Expression.
uint64_t getRawOperand(unsigned Idx) const
uint8_t getAddressByteSize() const
Definition: DWARFUnit.h:324
DwarfInstrProfCorrelator - A child of InstrProfCorrelatorImpl that takes DWARF debug info as input to...
Lightweight error class with error context and mandatory checking.
Definition: Error.h:160
static ErrorSuccess success()
Create a success value.
Definition: Error.h:334
Tagged union holding either a T or a Error.
Definition: Error.h:474
InstrProfCorrelatorImpl - A child of InstrProfCorrelator with a template pointer type so that the Pro...
static llvm::Expected< std::unique_ptr< InstrProfCorrelatorImpl< IntPtrT > > > get(std::unique_ptr< InstrProfCorrelator::Context > Ctx, const object::ObjectFile &Obj, ProfCorrelatorKind FileKind)
Error correlateProfileData(int MaxWarnings) override
Construct a ProfileData vector used to correlate raw instrumentation data to their functions.
static bool classof(const InstrProfCorrelator *C)
InstrProfCorrelatorImpl(std::unique_ptr< InstrProfCorrelator::Context > Ctx)
void addProbe(StringRef FunctionName, uint64_t CFGHash, IntPtrT CounterOffset, IntPtrT FunctionPtr, uint32_t NumCounters)
Error dumpYaml(int MaxWarnings, raw_ostream &OS) override
Process debug info and dump the correlation data.
InstrProfCorrelator - A base class used to create raw instrumentation data to their functions.
static const char * FunctionNameAttributeName
static const char * CFGHashAttributeName
static llvm::Expected< std::unique_ptr< InstrProfCorrelator > > get(StringRef Filename, ProfCorrelatorKind FileKind)
static const char * NumCountersAttributeName
ProfCorrelatorKind
Indicate which kind correlator to use.
std::optional< size_t > getDataSize() const
Return the number of ProfileData elements.
static ErrorOr< std::unique_ptr< MemoryBuffer > > getFile(const Twine &Filename, bool IsText=false, bool RequiresNullTerminator=true, bool IsVolatile=false, std::optional< Align > Alignment=std::nullopt)
Open the specified file as a MemoryBuffer, returning a new MemoryBuffer if successful,...
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:50
std::string str() const
str - Get the contents as an std::string.
Definition: StringRef.h:222
bool startswith(StringRef Prefix) const
Definition: StringRef.h:261
int compare(StringRef RHS) const
compare - Compare two strings; the result is negative, zero, or positive if this string is lexicograp...
Definition: StringRef.h:177
ObjectFormatType
Definition: Triple.h:281
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition: Twine.h:81
static raw_ostream & warning()
Convenience method for printing "warning: " to stderr.
Definition: WithColor.cpp:85
Triple::ObjectFormatType getTripleObjectFormat() const
Definition: Binary.h:163
bool isLittleEndian() const
Definition: Binary.h:155
bool isMachO() const
Definition: Binary.h:127
bool isELF() const
Definition: Binary.h:123
static Expected< std::vector< std::string > > findDsymObjectMembers(StringRef Path)
If the input path is a .dSYM bundle (as created by the dsymutil tool), return the paths to the object...
This class is the base class for all object file types.
Definition: ObjectFile.h:229
section_iterator_range sections() const
Definition: ObjectFile.h:328
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition: raw_ostream.h:52
@ C
The default llvm calling convention, compatible with C.
Definition: CallingConv.h:34
uint64_t ComputeHash(StringRef K)
Definition: InstrProf.h:1031
std::optional< uint64_t > toAddress(const std::optional< DWARFFormValue > &V)
Take an optional DWARFFormValue and try to extract an address.
Expected< std::unique_ptr< Binary > > createBinary(MemoryBufferRef Source, LLVMContext *Context=nullptr, bool InitContent=true)
Create a Binary from Source, autodetecting the file type.
Definition: Binary.cpp:45
static const bool IsLittleEndianHost
Definition: SwapByteOrder.h:29
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition: Casting.h:649
std::string getInstrProfSectionName(InstrProfSectKind IPSK, Triple::ObjectFormatType OF, bool AddSegmentInfo=true)
Return the name of the profile section corresponding to IPSK.
Definition: InstrProf.cpp:224
InstrProfSectKind
Definition: InstrProf.h:58
StringRef getInstrProfCountersVarPrefix()
Return the name prefix of profile counter variables.
Definition: InstrProf.h:96
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:163
format_object< Ts... > format(const char *Fmt, const Ts &... Vals)
These are helper functions used to produce formatted output.
Definition: Format.h:125
Expected< T > errorOrToExpected(ErrorOr< T > &&EO)
Convert an ErrorOr<T> to an Expected<T>.
Definition: Error.h:1184
Error collectGlobalObjectNameStrings(ArrayRef< std::string > NameStrs, bool doCompression, std::string &Result)
Given a vector of strings (names of global objects like functions or, virtual tables) NameStrs,...
Definition: InstrProf.cpp:567
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Definition: STLExtras.h:1853
void consumeError(Error Err)
Consume a Error without doing anything.
Definition: Error.h:1041
Implement std::hash so that hash_code can be used in STL containers.
Definition: BitVector.h:858
static llvm::Expected< std::unique_ptr< Context > > get(std::unique_ptr< MemoryBuffer > Buffer, const object::ObjectFile &Obj)
std::unique_ptr< MemoryBuffer > Buffer
static void mapping(yaml::IO &io, InstrProfCorrelator::CorrelationData &Data)
static void mapping(yaml::IO &io, InstrProfCorrelator::Probe &P)