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