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