xref: /llvm-project/bolt/include/bolt/Profile/DataReader.h (revision 1a2f83366b86433bb86f3b60fa19b3f096313a21)
1 //===- bolt/Profile/DataReader.h - Perf data reader -------------*- C++ -*-===//
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 //
9 // This family of functions reads profile data written by the perf2bolt
10 // utility and stores it in memory for llvm-bolt consumption.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef BOLT_PROFILE_DATA_READER_H
15 #define BOLT_PROFILE_DATA_READER_H
16 
17 #include "bolt/Profile/ProfileReaderBase.h"
18 #include "llvm/ADT/DenseMap.h"
19 #include "llvm/ADT/StringMap.h"
20 #include "llvm/ADT/StringSet.h"
21 #include "llvm/Support/ErrorOr.h"
22 #include "llvm/Support/MemoryBuffer.h"
23 #include "llvm/Support/raw_ostream.h"
24 #include <map>
25 #include <unordered_map>
26 #include <vector>
27 
28 namespace llvm {
29 class MCSymbol;
30 
31 namespace bolt {
32 
33 class BinaryFunction;
34 
35 struct LBREntry {
36   uint64_t From;
37   uint64_t To;
38   bool Mispred;
39 };
40 
41 inline raw_ostream &operator<<(raw_ostream &OS, const LBREntry &LBR) {
42   OS << "0x" << Twine::utohexstr(LBR.From) << " -> 0x"
43      << Twine::utohexstr(LBR.To);
44   return OS;
45 }
46 
47 struct Location {
48   bool IsSymbol;
49   StringRef Name;
50   uint64_t Offset;
51 
LocationLocation52   explicit Location(uint64_t Offset)
53       : IsSymbol(false), Name("[unknown]"), Offset(Offset) {}
54 
LocationLocation55   Location(bool IsSymbol, StringRef Name, uint64_t Offset)
56       : IsSymbol(IsSymbol), Name(Name), Offset(Offset) {}
57 
58   bool operator==(const Location &RHS) const {
59     return IsSymbol == RHS.IsSymbol && Name == RHS.Name &&
60            (Name == "[heap]" || Offset == RHS.Offset);
61   }
62 
63   bool operator<(const Location &RHS) const {
64     if (IsSymbol != RHS.IsSymbol)
65       return IsSymbol < RHS.IsSymbol;
66 
67     if (Name != RHS.Name)
68       return Name < RHS.Name;
69 
70     return Name != "[heap]" && Offset < RHS.Offset;
71   }
72 
73   friend raw_ostream &operator<<(raw_ostream &OS, const Location &Loc);
74 };
75 
76 typedef std::vector<std::pair<Location, Location>> BranchContext;
77 
78 struct BranchInfo {
79   Location From;
80   Location To;
81   int64_t Mispreds;
82   int64_t Branches;
83 
BranchInfoBranchInfo84   BranchInfo(Location From, Location To, int64_t Mispreds, int64_t Branches)
85       : From(std::move(From)), To(std::move(To)), Mispreds(Mispreds),
86         Branches(Branches) {}
87 
88   bool operator==(const BranchInfo &RHS) const {
89     return From == RHS.From && To == RHS.To;
90   }
91 
92   bool operator<(const BranchInfo &RHS) const {
93     if (From < RHS.From)
94       return true;
95 
96     if (From == RHS.From)
97       return (To < RHS.To);
98 
99     return false;
100   }
101 
102   /// Merges branch and misprediction counts of \p BI with those of this object.
103   void mergeWith(const BranchInfo &BI);
104 
105   void print(raw_ostream &OS) const;
106 };
107 
108 struct FuncBranchData {
109   typedef std::vector<BranchInfo> ContainerTy;
110 
111   StringRef Name;
112   ContainerTy Data;
113   ContainerTy EntryData;
114 
115   /// Total execution count for the function.
116   int64_t ExecutionCount{0};
117 
118   /// Indicate if the data was used.
119   bool Used{false};
120 
FuncBranchDataFuncBranchData121   FuncBranchData() {}
122 
FuncBranchDataFuncBranchData123   FuncBranchData(StringRef Name, ContainerTy Data)
124       : Name(Name), Data(std::move(Data)) {}
125 
FuncBranchDataFuncBranchData126   FuncBranchData(StringRef Name, ContainerTy Data, ContainerTy EntryData)
127       : Name(Name), Data(std::move(Data)), EntryData(std::move(EntryData)) {}
128 
129   ErrorOr<const BranchInfo &> getBranch(uint64_t From, uint64_t To) const;
130 
131   /// Returns the branch info object associated with a direct call originating
132   /// from the given offset. If no branch info object is found, an error is
133   /// returned. If the offset corresponds to an indirect call the behavior is
134   /// undefined.
135   ErrorOr<const BranchInfo &> getDirectCallBranch(uint64_t From) const;
136 
137   /// Append the branch data of another function located \p Offset bytes away
138   /// from the entry of this function.
139   void appendFrom(const FuncBranchData &FBD, uint64_t Offset);
140 
141   /// Returns the total number of executed branches in this function
142   /// by counting the number of executed branches for each BranchInfo
143   uint64_t getNumExecutedBranches() const;
144 
145   /// Aggregation helpers
146   DenseMap<uint64_t, DenseMap<uint64_t, size_t>> IntraIndex;
147   DenseMap<uint64_t, DenseMap<Location, size_t>> InterIndex;
148   DenseMap<uint64_t, DenseMap<Location, size_t>> EntryIndex;
149 
150   void bumpBranchCount(uint64_t OffsetFrom, uint64_t OffsetTo, uint64_t Count,
151                        uint64_t Mispreds);
152   void bumpCallCount(uint64_t OffsetFrom, const Location &To, uint64_t Count,
153                      uint64_t Mispreds);
154   void bumpEntryCount(const Location &From, uint64_t OffsetTo, uint64_t Count,
155                       uint64_t Mispreds);
156 };
157 
158 /// MemInfo represents a single memory load from an address \p Addr at an \p
159 /// Offset within a function.  \p Count represents how many times a particular
160 /// address was seen.
161 struct MemInfo {
162   Location Offset;
163   Location Addr;
164   uint64_t Count;
165 
166   bool operator==(const MemInfo &RHS) const {
167     return Offset == RHS.Offset && Addr == RHS.Addr;
168   }
169 
170   bool operator<(const MemInfo &RHS) const {
171     if (Offset < RHS.Offset)
172       return true;
173 
174     if (Offset == RHS.Offset)
175       return (Addr < RHS.Addr);
176 
177     return false;
178   }
179 
mergeWithMemInfo180   void mergeWith(const MemInfo &MI) { Count += MI.Count; }
181 
182   void print(raw_ostream &OS) const;
183   void prettyPrint(raw_ostream &OS) const;
184 
185   MemInfo(const Location &Offset, const Location &Addr, uint64_t Count = 0)
OffsetMemInfo186       : Offset(Offset), Addr(Addr), Count(Count) {}
187 
188   friend raw_ostream &operator<<(raw_ostream &OS, const MemInfo &MI) {
189     MI.prettyPrint(OS);
190     return OS;
191   }
192 };
193 
194 /// Helper class to store memory load events recorded in the address space of
195 /// a given function, analogous to FuncBranchData but for memory load events
196 /// instead of branches.
197 struct FuncMemData {
198   typedef std::vector<MemInfo> ContainerTy;
199 
200   StringRef Name;
201   ContainerTy Data;
202 
203   /// Indicate if the data was used.
204   bool Used{false};
205 
206   DenseMap<uint64_t, DenseMap<Location, size_t>> EventIndex;
207 
208   /// Update \p Data with a memory event.  Events with the same
209   /// \p Offset and \p Addr will be coalesced.
210   void update(const Location &Offset, const Location &Addr);
211 
FuncMemDataFuncMemData212   FuncMemData() {}
213 
FuncMemDataFuncMemData214   FuncMemData(StringRef Name, ContainerTy Data)
215       : Name(Name), Data(std::move(Data)) {}
216 };
217 
218 /// Similar to BranchInfo, but instead of recording from-to address (an edge),
219 /// it records the address of a perf event and the number of times samples hit
220 /// this address.
221 struct SampleInfo {
222   Location Loc;
223   int64_t Hits;
224 
SampleInfoSampleInfo225   SampleInfo(Location Loc, int64_t Hits) : Loc(std::move(Loc)), Hits(Hits) {}
226 
227   bool operator==(const SampleInfo &RHS) const { return Loc == RHS.Loc; }
228 
229   bool operator<(const SampleInfo &RHS) const {
230     if (Loc < RHS.Loc)
231       return true;
232 
233     return false;
234   }
235 
236   void print(raw_ostream &OS) const;
237 
238   void mergeWith(const SampleInfo &SI);
239 };
240 
241 /// Helper class to store samples recorded in the address space of a given
242 /// function, analogous to FuncBranchData but for samples instead of branches.
243 struct FuncSampleData {
244   typedef std::vector<SampleInfo> ContainerTy;
245 
246   StringRef Name;
247   ContainerTy Data;
248 
FuncSampleDataFuncSampleData249   FuncSampleData(StringRef Name, ContainerTy Data)
250       : Name(Name), Data(std::move(Data)) {}
251 
252   /// Get the number of samples recorded in [Start, End)
253   uint64_t getSamples(uint64_t Start, uint64_t End) const;
254 
255   /// Aggregation helper
256   DenseMap<uint64_t, size_t> Index;
257 
258   void bumpCount(uint64_t Offset, uint64_t Count);
259 };
260 
261 /// DataReader Class
262 ///
263 class DataReader : public ProfileReaderBase {
264 public:
DataReader(StringRef Filename)265   explicit DataReader(StringRef Filename)
266       : ProfileReaderBase(Filename), Diag(errs()) {}
267 
getReaderName()268   StringRef getReaderName() const override { return "branch profile reader"; }
269 
isTrustedSource()270   bool isTrustedSource() const override { return false; }
271 
272   Error preprocessProfile(BinaryContext &BC) override;
273 
274   Error readProfilePreCFG(BinaryContext &BC) override;
275 
276   Error readProfile(BinaryContext &BC) override;
277 
278   bool hasLocalsWithFileName() const override;
279 
280   bool mayHaveProfileData(const BinaryFunction &BF) override;
281 
282   /// Return all event names used to collect this profile
getEventNames()283   StringSet<> getEventNames() const override { return EventNames; }
284 
285 protected:
286   /// Read profile information available for the function.
287   void readProfile(BinaryFunction &BF);
288 
289   /// In functions with multiple entry points, the profile collection records
290   /// data for other entry points in a different function entry. This function
291   /// attempts to fetch extra profile data for each secondary entry point.
292   bool fetchProfileForOtherEntryPoints(BinaryFunction &BF);
293 
294   /// Find the best matching profile for a function after the creation of basic
295   /// blocks.
296   void matchProfileData(BinaryFunction &BF);
297 
298   /// Find the best matching memory data profile for a function before the
299   /// creation of basic blocks.
300   void matchProfileMemData(BinaryFunction &BF);
301 
302   /// Check how closely \p BranchData matches the function \p BF.
303   /// Return accuracy (ranging from 0.0 to 1.0) of the matching.
304   float evaluateProfileData(BinaryFunction &BF,
305                             const FuncBranchData &BranchData) const;
306 
307   /// If our profile data comes from sample addresses instead of LBR entries,
308   /// collect sample count for all addresses in this function address space,
309   /// aggregating them per basic block and assigning an execution count to each
310   /// basic block based on the number of samples recorded at those addresses.
311   /// The last step is to infer edge counts based on BB execution count. Note
312   /// this is the opposite of the LBR way, where we infer BB execution count
313   /// based on edge counts.
314   void readSampleData(BinaryFunction &BF);
315 
316   /// Convert function-level branch data into instruction annotations.
317   void convertBranchData(BinaryFunction &BF) const;
318 
319   /// Update function \p BF profile with a taken branch.
320   /// \p Count could be 0 if verification of the branch is required.
321   ///
322   /// Return true if the branch is valid, false otherwise.
323   bool recordBranch(BinaryFunction &BF, uint64_t From, uint64_t To,
324                     uint64_t Count = 1, uint64_t Mispreds = 0) const;
325 
326   /// Parses the input bolt data file into internal data structures. We expect
327   /// the file format to follow the syntax below.
328   ///
329   /// <is symbol?> <closest elf symbol or DSO name> <relative FROM address>
330   /// <is symbol?> <closest elf symbol or DSO name> <relative TO address>
331   /// <number of mispredictions> <number of branches>
332   ///
333   /// In <is symbol?> field we record 0 if our closest address is a DSO load
334   /// address or 1 if our closest address is an ELF symbol.
335   ///
336   /// Examples:
337   ///
338   ///  1 main 3fb 0 /lib/ld-2.21.so 12 4 221
339   ///
340   /// The example records branches from symbol main, offset 3fb, to DSO ld-2.21,
341   /// offset 12, with 4 mispredictions and 221 branches.
342   ///
343   ///  2 t2.c/func 11 1 globalfunc 1d 0 1775 2
344   ///  0 1002 2
345   ///  2 t2.c/func 31 2 t2.c/func d
346   ///  2 t2.c/func 18 2 t2.c/func 20
347   ///  0 773 2
348   ///  2 t2.c/func 71 2 t2.c/func d
349   ///  2 t2.c/func 18 2 t2.c/func 60
350   ///
351   /// The examples records branches from local symbol func (from t2.c), offset
352   /// 11, to global symbol globalfunc, offset 1d, with 1775 branches, no
353   /// mispreds. Of these branches, 1002 were preceded by a sequence of
354   /// branches from func, offset 18 to offset 20 and then from offset 31 to
355   /// offset d. The rest 773 branches were preceded by a different sequence
356   /// of branches, from func, offset 18 to offset 60 and then from offset 71 to
357   /// offset d.
358   std::error_code parse();
359 
360   /// When no_lbr is the first line of the file, activate No LBR mode. In this
361   /// mode we read the addresses where samples were recorded directly instead of
362   /// LBR entries. The line format is almost the same, except for a missing <to>
363   /// triple and a missing mispredictions field:
364   ///
365   /// no_lbr
366   /// <is symbol?> <closest elf symbol or DSO name> <relative address> <count>
367   /// ...
368   ///
369   /// Example:
370   ///
371   /// no_lbr                           # First line of fdata file
372   ///  1 BZ2_compressBlock 466c 3
373   ///  1 BZ2_hbMakeCodeLengths 29c 1
374   ///
375   std::error_code parseInNoLBRMode();
376 
377   /// Return branch data matching one of the names in \p FuncNames.
378   FuncBranchData *
379   getBranchDataForNames(const std::vector<StringRef> &FuncNames);
380 
381   /// Return branch data matching one of the \p Symbols.
382   FuncBranchData *
383   getBranchDataForSymbols(const std::vector<MCSymbol *> &Symbols);
384 
385   /// Return mem data matching one of the names in \p FuncNames.
386   FuncMemData *getMemDataForNames(const std::vector<StringRef> &FuncNames);
387 
388   FuncSampleData *getFuncSampleData(const std::vector<StringRef> &FuncNames);
389 
390   /// Return a vector of all FuncBranchData matching the list of names.
391   /// Internally use fuzzy matching to match special names like LTO-generated
392   /// function names.
393   std::vector<FuncBranchData *>
394   getBranchDataForNamesRegex(const std::vector<StringRef> &FuncNames);
395 
396   /// Return a vector of all FuncMemData matching the list of names.
397   /// Internally use fuzzy matching to match special names like LTO-generated
398   /// function names.
399   std::vector<FuncMemData *>
400   getMemDataForNamesRegex(const std::vector<StringRef> &FuncNames);
401 
402   /// Return branch data profile associated with function \p BF  or nullptr
403   /// if the function has no associated profile.
getBranchData(const BinaryFunction & BF)404   FuncBranchData *getBranchData(const BinaryFunction &BF) const {
405     auto FBDI = FuncsToBranches.find(&BF);
406     if (FBDI == FuncsToBranches.end())
407       return nullptr;
408     return FBDI->second;
409   }
410 
411   /// Updates branch profile data associated with function \p BF.
setBranchData(const BinaryFunction & BF,FuncBranchData * FBD)412   void setBranchData(const BinaryFunction &BF, FuncBranchData *FBD) {
413     FuncsToBranches[&BF] = FBD;
414   }
415 
416   /// Return memory profile data associated with function \p BF, or nullptr
417   /// if the function has no associated profile.
getMemData(const BinaryFunction & BF)418   FuncMemData *getMemData(const BinaryFunction &BF) const {
419     auto FMDI = FuncsToMemData.find(&BF);
420     if (FMDI == FuncsToMemData.end())
421       return nullptr;
422     return FMDI->second;
423   }
424 
425   /// Updates the memory profile data associated with function \p BF.
setMemData(const BinaryFunction & BF,FuncMemData * FMD)426   void setMemData(const BinaryFunction &BF, FuncMemData *FMD) {
427     FuncsToMemData[&BF] = FMD;
428   }
429 
430   using NamesToBranchesMapTy = std::map<StringRef, FuncBranchData>;
431   using NamesToSamplesMapTy = std::map<StringRef, FuncSampleData>;
432   using NamesToMemEventsMapTy = std::map<StringRef, FuncMemData>;
433   using FuncsToBranchesMapTy =
434       std::unordered_map<const BinaryFunction *, FuncBranchData *>;
435   using FuncsToMemDataMapTy =
436       std::unordered_map<const BinaryFunction *, FuncMemData *>;
437 
438   /// Dumps the entire data structures parsed. Used for debugging.
439   void dump() const;
440 
441   /// Return false only if we are running with profiling data that lacks LBR.
hasLBR()442   bool hasLBR() const { return !NoLBRMode; }
443 
444   /// Return true if the profiling data was collected in a bolted binary. This
445   /// means we lose the ability to identify stale data at some branch locations,
446   /// since we have to be more permissive in some cases.
collectedInBoltedBinary()447   bool collectedInBoltedBinary() const { return BATMode; }
448 
449   /// Return true if event named \p Name was used to collect this profile data.
usesEvent(StringRef Name)450   bool usesEvent(StringRef Name) const {
451     for (auto I = EventNames.begin(), E = EventNames.end(); I != E; ++I) {
452       StringRef Event = I->getKey();
453       if (Event.contains(Name))
454         return true;
455     }
456     return false;
457   }
458 
459   /// Open the file and parse the contents.
460   std::error_code parseInput();
461 
462   /// Build suffix map once the profile data is parsed.
463   void buildLTONameMaps();
464 
465   void reportError(StringRef ErrorMsg);
466   bool expectAndConsumeFS();
467   void consumeAllRemainingFS();
468   bool checkAndConsumeNewLine();
469   ErrorOr<StringRef> parseString(char EndChar, bool EndNl = false);
470   ErrorOr<int64_t> parseNumberField(char EndChar, bool EndNl = false);
471   ErrorOr<uint64_t> parseHexField(char EndChar, bool EndNl = false);
472   ErrorOr<Location> parseLocation(char EndChar, bool EndNl, bool ExpectMemLoc);
473   ErrorOr<Location> parseLocation(char EndChar, bool EndNl = false) {
474     return parseLocation(EndChar, EndNl, false);
475   }
476   ErrorOr<Location> parseMemLocation(char EndChar, bool EndNl = false) {
477     return parseLocation(EndChar, EndNl, true);
478   }
479   ErrorOr<BranchInfo> parseBranchInfo();
480   ErrorOr<SampleInfo> parseSampleInfo();
481   ErrorOr<MemInfo> parseMemInfo();
482   ErrorOr<bool> maybeParseNoLBRFlag();
483   ErrorOr<bool> maybeParseBATFlag();
484   bool hasBranchData();
485   bool hasMemData();
486 
487   /// An in-memory copy of the input data file - owns strings used in reader.
488   std::unique_ptr<MemoryBuffer> FileBuf;
489   raw_ostream &Diag;
490   StringRef ParsingBuf;
491   unsigned Line{0};
492   unsigned Col{0};
493   NamesToBranchesMapTy NamesToBranches;
494   NamesToSamplesMapTy NamesToSamples;
495   NamesToMemEventsMapTy NamesToMemEvents;
496   FuncsToBranchesMapTy FuncsToBranches;
497   FuncsToMemDataMapTy FuncsToMemData;
498   bool NoLBRMode{false};
499   bool BATMode{false};
500   StringSet<> EventNames;
501   static const char FieldSeparator = ' ';
502 
503   /// Maps of common LTO names to possible matching profiles.
504   StringMap<std::vector<FuncBranchData *>> LTOCommonNameMap;
505   StringMap<std::vector<FuncMemData *>> LTOCommonNameMemMap;
506 
507 public:
setParsingBuffer(StringRef Buffer)508   void setParsingBuffer(StringRef Buffer) { ParsingBuf = Buffer; }
509 };
510 
511 } // namespace bolt
512 
513 /// DenseMapInfo allows us to use the DenseMap LLVM data structure to store
514 /// Locations
515 template <> struct DenseMapInfo<bolt::Location> {
516   static inline bolt::Location getEmptyKey() {
517     return bolt::Location(true, StringRef(), static_cast<uint64_t>(-1LL));
518   }
519   static inline bolt::Location getTombstoneKey() {
520     return bolt::Location(true, StringRef(), static_cast<uint64_t>(-2LL));
521     ;
522   }
523   static unsigned getHashValue(const bolt::Location &L) {
524     return (unsigned(DenseMapInfo<StringRef>::getHashValue(L.Name)) >> 4) ^
525            (unsigned(L.Offset));
526   }
527   static bool isEqual(const bolt::Location &LHS, const bolt::Location &RHS) {
528     return LHS.IsSymbol == RHS.IsSymbol && LHS.Name == RHS.Name &&
529            LHS.Offset == RHS.Offset;
530   }
531 };
532 
533 } // namespace llvm
534 
535 #endif
536