12f09f445SMaksim Panchenko //===- bolt/Profile/Heatmap.cpp -------------------------------------------===// 2a34c753fSRafael Auler // 3a34c753fSRafael Auler // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4a34c753fSRafael Auler // See https://llvm.org/LICENSE.txt for license information. 5a34c753fSRafael Auler // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6a34c753fSRafael Auler // 7a34c753fSRafael Auler //===----------------------------------------------------------------------===// 8a34c753fSRafael Auler 9a34c753fSRafael Auler #include "bolt/Profile/Heatmap.h" 10a34c753fSRafael Auler #include "bolt/Utils/CommandLineOpts.h" 11733dc3e5SRahman Lavaee #include "llvm/ADT/StringMap.h" 12a34c753fSRafael Auler #include "llvm/ADT/Twine.h" 13a34c753fSRafael Auler #include "llvm/Support/Debug.h" 14a34c753fSRafael Auler #include "llvm/Support/FileSystem.h" 15a34c753fSRafael Auler #include "llvm/Support/Format.h" 16*34433fdcSPaschalis Mpeis #include "llvm/Support/FormatVariadic.h" 17a34c753fSRafael Auler #include "llvm/Support/MathExtras.h" 18a34c753fSRafael Auler #include "llvm/Support/raw_ostream.h" 19a34c753fSRafael Auler #include <algorithm> 2005aa2a16SFabian Parzefall #include <cctype> 21a34c753fSRafael Auler #include <cmath> 22a34c753fSRafael Auler #include <vector> 23a34c753fSRafael Auler 24a34c753fSRafael Auler #define DEBUG_TYPE "bolt-heatmap" 25a34c753fSRafael Auler 26a34c753fSRafael Auler using namespace llvm; 27a34c753fSRafael Auler 28a34c753fSRafael Auler namespace llvm { 29a34c753fSRafael Auler namespace bolt { 30a34c753fSRafael Auler 31a34c753fSRafael Auler void Heatmap::registerAddressRange(uint64_t StartAddress, uint64_t EndAddress, 32a34c753fSRafael Auler uint64_t Count) { 33a34c753fSRafael Auler if (ignoreAddress(StartAddress)) { 34a34c753fSRafael Auler ++NumSkippedRanges; 35a34c753fSRafael Auler return; 36a34c753fSRafael Auler } 37a34c753fSRafael Auler 3840c2e0faSMaksim Panchenko if (StartAddress > EndAddress || EndAddress - StartAddress > 64 * 1024) { 39a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "invalid range : 0x" << Twine::utohexstr(StartAddress) 40a34c753fSRafael Auler << " -> 0x" << Twine::utohexstr(EndAddress) << '\n'); 41a34c753fSRafael Auler ++NumSkippedRanges; 42a34c753fSRafael Auler return; 43a34c753fSRafael Auler } 44a34c753fSRafael Auler 45a34c753fSRafael Auler for (uint64_t Bucket = StartAddress / BucketSize; 46def464aaSAmir Ayupov Bucket <= EndAddress / BucketSize; ++Bucket) 47a34c753fSRafael Auler Map[Bucket] += Count; 48a34c753fSRafael Auler } 49a34c753fSRafael Auler 50a34c753fSRafael Auler void Heatmap::print(StringRef FileName) const { 51a34c753fSRafael Auler std::error_code EC; 52a34c753fSRafael Auler raw_fd_ostream OS(FileName, EC, sys::fs::OpenFlags::OF_None); 53a34c753fSRafael Auler if (EC) { 54a34c753fSRafael Auler errs() << "error opening output file: " << EC.message() << '\n'; 55a34c753fSRafael Auler exit(1); 56a34c753fSRafael Auler } 57a34c753fSRafael Auler print(OS); 58a34c753fSRafael Auler } 59a34c753fSRafael Auler 60a34c753fSRafael Auler void Heatmap::print(raw_ostream &OS) const { 61a34c753fSRafael Auler const char FillChar = '.'; 62a34c753fSRafael Auler 63a34c753fSRafael Auler const auto DefaultColor = raw_ostream::WHITE; 64a34c753fSRafael Auler auto changeColor = [&](raw_ostream::Colors Color) -> void { 65a34c753fSRafael Auler static auto CurrentColor = raw_ostream::BLACK; 66a34c753fSRafael Auler if (CurrentColor == Color) 67a34c753fSRafael Auler return; 68a34c753fSRafael Auler OS.changeColor(Color); 69a34c753fSRafael Auler CurrentColor = Color; 70a34c753fSRafael Auler }; 71a34c753fSRafael Auler 72a34c753fSRafael Auler const uint64_t BytesPerLine = opts::BucketsPerLine * BucketSize; 73a34c753fSRafael Auler 74a34c753fSRafael Auler // Calculate the max value for scaling. 75a34c753fSRafael Auler uint64_t MaxValue = 0; 76def464aaSAmir Ayupov for (const std::pair<const uint64_t, uint64_t> &Entry : Map) 77a34c753fSRafael Auler MaxValue = std::max<uint64_t>(MaxValue, Entry.second); 78a34c753fSRafael Auler 79a34c753fSRafael Auler // Print start of the line and fill it with an empty space right before 80a34c753fSRafael Auler // the Address. 81a34c753fSRafael Auler auto startLine = [&](uint64_t Address, bool Empty = false) { 82a34c753fSRafael Auler changeColor(DefaultColor); 83a34c753fSRafael Auler const uint64_t LineAddress = Address / BytesPerLine * BytesPerLine; 84a34c753fSRafael Auler 85a34c753fSRafael Auler if (MaxAddress > 0xffffffff) 86a34c753fSRafael Auler OS << format("0x%016" PRIx64 ": ", LineAddress); 87a34c753fSRafael Auler else 88a34c753fSRafael Auler OS << format("0x%08" PRIx64 ": ", LineAddress); 89a34c753fSRafael Auler 90a34c753fSRafael Auler if (Empty) 91a34c753fSRafael Auler Address = LineAddress + BytesPerLine; 92def464aaSAmir Ayupov for (uint64_t Fill = LineAddress; Fill < Address; Fill += BucketSize) 93a34c753fSRafael Auler OS << FillChar; 94a34c753fSRafael Auler }; 95a34c753fSRafael Auler 96a34c753fSRafael Auler // Finish line after \p Address was printed. 97a34c753fSRafael Auler auto finishLine = [&](uint64_t Address) { 98a34c753fSRafael Auler const uint64_t End = alignTo(Address + 1, BytesPerLine); 99a34c753fSRafael Auler for (uint64_t Fill = Address + BucketSize; Fill < End; Fill += BucketSize) 100a34c753fSRafael Auler OS << FillChar; 101a34c753fSRafael Auler OS << '\n'; 102a34c753fSRafael Auler }; 103a34c753fSRafael Auler 104a34c753fSRafael Auler // Fill empty space in (Start, End) range. 105a34c753fSRafael Auler auto fillRange = [&](uint64_t Start, uint64_t End) { 106a34c753fSRafael Auler if ((Start / BytesPerLine) == (End / BytesPerLine)) { 107a34c753fSRafael Auler for (uint64_t Fill = Start + BucketSize; Fill < End; Fill += BucketSize) { 108a34c753fSRafael Auler changeColor(DefaultColor); 109a34c753fSRafael Auler OS << FillChar; 110a34c753fSRafael Auler } 111a34c753fSRafael Auler return; 112a34c753fSRafael Auler } 113a34c753fSRafael Auler 114a34c753fSRafael Auler changeColor(DefaultColor); 115a34c753fSRafael Auler finishLine(Start); 116a34c753fSRafael Auler Start = alignTo(Start, BytesPerLine); 117a34c753fSRafael Auler 118a34c753fSRafael Auler uint64_t NumEmptyLines = (End - Start) / BytesPerLine; 119a34c753fSRafael Auler 120a34c753fSRafael Auler if (NumEmptyLines > 32) { 121a34c753fSRafael Auler OS << '\n'; 122a34c753fSRafael Auler } else { 123a34c753fSRafael Auler while (NumEmptyLines--) { 124a34c753fSRafael Auler startLine(Start, /*Empty=*/true); 125a34c753fSRafael Auler OS << '\n'; 126a34c753fSRafael Auler Start += BytesPerLine; 127a34c753fSRafael Auler } 128a34c753fSRafael Auler } 129a34c753fSRafael Auler 130a34c753fSRafael Auler startLine(End); 131a34c753fSRafael Auler }; 132a34c753fSRafael Auler 133a34c753fSRafael Auler static raw_ostream::Colors Colors[] = { 13440c2e0faSMaksim Panchenko raw_ostream::WHITE, raw_ostream::WHITE, raw_ostream::CYAN, 13540c2e0faSMaksim Panchenko raw_ostream::GREEN, raw_ostream::YELLOW, raw_ostream::RED}; 136a34c753fSRafael Auler constexpr size_t NumRanges = sizeof(Colors) / sizeof(Colors[0]); 137a34c753fSRafael Auler 138a34c753fSRafael Auler uint64_t Range[NumRanges]; 139a34c753fSRafael Auler for (uint64_t I = 0; I < NumRanges; ++I) 14040c2e0faSMaksim Panchenko Range[I] = std::max(I + 1, (uint64_t)std::pow((double)MaxValue, 141a34c753fSRafael Auler (double)(I + 1) / NumRanges)); 142a34c753fSRafael Auler Range[NumRanges - 1] = std::max((uint64_t)NumRanges, MaxValue); 143a34c753fSRafael Auler 144a34c753fSRafael Auler // Print scaled value 14505aa2a16SFabian Parzefall auto printValue = [&](uint64_t Value, char Character, bool ResetColor) { 146a34c753fSRafael Auler assert(Value && "should only print positive values"); 147a34c753fSRafael Auler for (unsigned I = 0; I < sizeof(Range) / sizeof(Range[0]); ++I) { 148a34c753fSRafael Auler if (Value <= Range[I]) { 149a34c753fSRafael Auler changeColor(Colors[I]); 150a34c753fSRafael Auler break; 151a34c753fSRafael Auler } 152a34c753fSRafael Auler } 153def464aaSAmir Ayupov if (Value <= Range[0]) 15405aa2a16SFabian Parzefall OS << static_cast<char>(std::tolower(Character)); 155def464aaSAmir Ayupov else 15605aa2a16SFabian Parzefall OS << static_cast<char>(std::toupper(Character)); 157def464aaSAmir Ayupov 158a34c753fSRafael Auler if (ResetColor) 159a34c753fSRafael Auler changeColor(DefaultColor); 160a34c753fSRafael Auler }; 161a34c753fSRafael Auler 162a34c753fSRafael Auler // Print against black background 163a34c753fSRafael Auler OS.changeColor(raw_ostream::BLACK, /*Bold=*/false, /*Background=*/true); 164a34c753fSRafael Auler changeColor(DefaultColor); 165a34c753fSRafael Auler 166a34c753fSRafael Auler // Print map legend 167a34c753fSRafael Auler OS << "Legend:\n"; 168*34433fdcSPaschalis Mpeis OS << "\nRanges:\n"; 169a34c753fSRafael Auler uint64_t PrevValue = 0; 170a34c753fSRafael Auler for (unsigned I = 0; I < sizeof(Range) / sizeof(Range[0]); ++I) { 171a34c753fSRafael Auler const uint64_t Value = Range[I]; 172a34c753fSRafael Auler OS << " "; 17305aa2a16SFabian Parzefall printValue(Value, 'o', /*ResetColor=*/true); 174a34c753fSRafael Auler OS << " : (" << PrevValue << ", " << Value << "]\n"; 175a34c753fSRafael Auler PrevValue = Value; 176a34c753fSRafael Auler } 177*34433fdcSPaschalis Mpeis if (opts::HeatmapPrintMappings) { 178*34433fdcSPaschalis Mpeis OS << "\nSections:\n"; 179*34433fdcSPaschalis Mpeis unsigned SectionIdx = 0; 180*34433fdcSPaschalis Mpeis for (auto TxtSeg : TextSections) { 181*34433fdcSPaschalis Mpeis const char Upper = static_cast<char>('A' + ((SectionIdx++) % 26)); 182*34433fdcSPaschalis Mpeis const char Lower = static_cast<char>(std::tolower(Upper)); 183*34433fdcSPaschalis Mpeis OS << formatv(" {0}/{1} : {2,-10} ", Lower, Upper, TxtSeg.Name); 184*34433fdcSPaschalis Mpeis if (MaxAddress > 0xffffffff) 185*34433fdcSPaschalis Mpeis OS << format("0x%016" PRIx64, TxtSeg.BeginAddress) << "-" 186*34433fdcSPaschalis Mpeis << format("0x%016" PRIx64, TxtSeg.EndAddress) << "\n"; 187*34433fdcSPaschalis Mpeis else 188*34433fdcSPaschalis Mpeis OS << format("0x%08" PRIx64, TxtSeg.BeginAddress) << "-" 189*34433fdcSPaschalis Mpeis << format("0x%08" PRIx64, TxtSeg.EndAddress) << "\n"; 190*34433fdcSPaschalis Mpeis } 191*34433fdcSPaschalis Mpeis OS << "\n"; 192*34433fdcSPaschalis Mpeis } 193a34c753fSRafael Auler 194a34c753fSRafael Auler // Pos - character position from right in hex form. 195a34c753fSRafael Auler auto printHeader = [&](unsigned Pos) { 196a34c753fSRafael Auler OS << " "; 197a34c753fSRafael Auler if (MaxAddress > 0xffffffff) 198a34c753fSRafael Auler OS << " "; 199a34c753fSRafael Auler unsigned PrevValue = unsigned(-1); 200a34c753fSRafael Auler for (unsigned I = 0; I < BytesPerLine; I += BucketSize) { 201a34c753fSRafael Auler const unsigned Value = (I & ((1 << Pos * 4) - 1)) >> (Pos - 1) * 4; 202a34c753fSRafael Auler if (Value != PrevValue) { 203a34c753fSRafael Auler OS << Twine::utohexstr(Value); 204a34c753fSRafael Auler PrevValue = Value; 205a34c753fSRafael Auler } else { 206a34c753fSRafael Auler OS << ' '; 207a34c753fSRafael Auler } 208a34c753fSRafael Auler } 209a34c753fSRafael Auler OS << '\n'; 210a34c753fSRafael Auler }; 211a34c753fSRafael Auler for (unsigned I = 5; I > 0; --I) 212a34c753fSRafael Auler printHeader(I); 213a34c753fSRafael Auler 21405aa2a16SFabian Parzefall auto SectionStart = TextSections.begin(); 215a34c753fSRafael Auler uint64_t PrevAddress = 0; 216a34c753fSRafael Auler for (auto MI = Map.begin(), ME = Map.end(); MI != ME; ++MI) { 217a34c753fSRafael Auler const std::pair<const uint64_t, uint64_t> &Entry = *MI; 218a34c753fSRafael Auler uint64_t Address = Entry.first * BucketSize; 21905aa2a16SFabian Parzefall char Character = 'o'; 22005aa2a16SFabian Parzefall 22105aa2a16SFabian Parzefall // Check if address is in the current or any later section. 22205aa2a16SFabian Parzefall auto Section = std::find_if( 22305aa2a16SFabian Parzefall SectionStart, TextSections.end(), [&](const SectionNameAndRange &S) { 22405aa2a16SFabian Parzefall return Address >= S.BeginAddress && Address < S.EndAddress; 22505aa2a16SFabian Parzefall }); 22605aa2a16SFabian Parzefall if (Section != TextSections.end()) { 22705aa2a16SFabian Parzefall // Shift the section forward (if SectionStart is different from Section). 22805aa2a16SFabian Parzefall // This works, because TextSections is sorted by start address. 22905aa2a16SFabian Parzefall SectionStart = Section; 23005aa2a16SFabian Parzefall Character = 'a' + ((Section - TextSections.begin()) % 26); 23105aa2a16SFabian Parzefall } 232a34c753fSRafael Auler 233def464aaSAmir Ayupov if (PrevAddress) 234a34c753fSRafael Auler fillRange(PrevAddress, Address); 235def464aaSAmir Ayupov else 236a34c753fSRafael Auler startLine(Address); 237a34c753fSRafael Auler 23805aa2a16SFabian Parzefall printValue(Entry.second, Character, /*ResetColor=*/false); 239a34c753fSRafael Auler 240a34c753fSRafael Auler PrevAddress = Address; 241a34c753fSRafael Auler } 242a34c753fSRafael Auler 243a34c753fSRafael Auler if (PrevAddress) { 244a34c753fSRafael Auler changeColor(DefaultColor); 245a34c753fSRafael Auler finishLine(PrevAddress); 246a34c753fSRafael Auler } 247a34c753fSRafael Auler } 248a34c753fSRafael Auler 249a34c753fSRafael Auler void Heatmap::printCDF(StringRef FileName) const { 250a34c753fSRafael Auler std::error_code EC; 251a34c753fSRafael Auler raw_fd_ostream OS(FileName, EC, sys::fs::OpenFlags::OF_None); 252a34c753fSRafael Auler if (EC) { 253a34c753fSRafael Auler errs() << "error opening output file: " << EC.message() << '\n'; 254a34c753fSRafael Auler exit(1); 255a34c753fSRafael Auler } 256a34c753fSRafael Auler printCDF(OS); 257a34c753fSRafael Auler } 258a34c753fSRafael Auler 259a34c753fSRafael Auler void Heatmap::printCDF(raw_ostream &OS) const { 260a34c753fSRafael Auler uint64_t NumTotalCounts = 0; 261a34c753fSRafael Auler std::vector<uint64_t> Counts; 262a34c753fSRafael Auler 263a34c753fSRafael Auler for (const std::pair<const uint64_t, uint64_t> &KV : Map) { 264a34c753fSRafael Auler Counts.push_back(KV.second); 265a34c753fSRafael Auler NumTotalCounts += KV.second; 266a34c753fSRafael Auler } 267a34c753fSRafael Auler 268d2c87699SAmir Ayupov llvm::sort(Counts, std::greater<uint64_t>()); 269a34c753fSRafael Auler 270a34c753fSRafael Auler double RatioLeftInKB = (1.0 * BucketSize) / 1024; 271a34c753fSRafael Auler assert(NumTotalCounts > 0 && 272a34c753fSRafael Auler "total number of heatmap buckets should be greater than 0"); 273a34c753fSRafael Auler double RatioRightInPercent = 100.0 / NumTotalCounts; 274a34c753fSRafael Auler uint64_t RunningCount = 0; 275a34c753fSRafael Auler 276a34c753fSRafael Auler OS << "Bucket counts, Size (KB), CDF (%)\n"; 277a34c753fSRafael Auler for (uint64_t I = 0; I < Counts.size(); I++) { 278a34c753fSRafael Auler RunningCount += Counts[I]; 279a34c753fSRafael Auler OS << format("%llu", (I + 1)) << ", " 280a34c753fSRafael Auler << format("%.4f", RatioLeftInKB * (I + 1)) << ", " 281a34c753fSRafael Auler << format("%.4f", RatioRightInPercent * (RunningCount)) << "\n"; 282a34c753fSRafael Auler } 283a34c753fSRafael Auler 284a34c753fSRafael Auler Counts.clear(); 285a34c753fSRafael Auler } 286a34c753fSRafael Auler 287733dc3e5SRahman Lavaee void Heatmap::printSectionHotness(StringRef FileName) const { 288733dc3e5SRahman Lavaee std::error_code EC; 289733dc3e5SRahman Lavaee raw_fd_ostream OS(FileName, EC, sys::fs::OpenFlags::OF_None); 290733dc3e5SRahman Lavaee if (EC) { 291733dc3e5SRahman Lavaee errs() << "error opening output file: " << EC.message() << '\n'; 292733dc3e5SRahman Lavaee exit(1); 293733dc3e5SRahman Lavaee } 294733dc3e5SRahman Lavaee printSectionHotness(OS); 295733dc3e5SRahman Lavaee } 296733dc3e5SRahman Lavaee 297733dc3e5SRahman Lavaee void Heatmap::printSectionHotness(raw_ostream &OS) const { 298733dc3e5SRahman Lavaee uint64_t NumTotalCounts = 0; 299733dc3e5SRahman Lavaee StringMap<uint64_t> SectionHotness; 300733dc3e5SRahman Lavaee unsigned TextSectionIndex = 0; 301733dc3e5SRahman Lavaee 302733dc3e5SRahman Lavaee if (TextSections.empty()) 303733dc3e5SRahman Lavaee return; 304733dc3e5SRahman Lavaee 305733dc3e5SRahman Lavaee uint64_t UnmappedHotness = 0; 306733dc3e5SRahman Lavaee auto RecordUnmappedBucket = [&](uint64_t Address, uint64_t Frequency) { 307733dc3e5SRahman Lavaee errs() << "Couldn't map the address bucket [0x" << Twine::utohexstr(Address) 308733dc3e5SRahman Lavaee << ", 0x" << Twine::utohexstr(Address + BucketSize) 309733dc3e5SRahman Lavaee << "] containing " << Frequency 310733dc3e5SRahman Lavaee << " samples to a text section in the binary."; 311733dc3e5SRahman Lavaee UnmappedHotness += Frequency; 312733dc3e5SRahman Lavaee }; 313733dc3e5SRahman Lavaee 314733dc3e5SRahman Lavaee for (const std::pair<const uint64_t, uint64_t> &KV : Map) { 315733dc3e5SRahman Lavaee NumTotalCounts += KV.second; 316733dc3e5SRahman Lavaee // We map an address bucket to the first section (lowest address) 317733dc3e5SRahman Lavaee // overlapping with that bucket. 318733dc3e5SRahman Lavaee auto Address = KV.first * BucketSize; 319733dc3e5SRahman Lavaee while (TextSectionIndex < TextSections.size() && 320733dc3e5SRahman Lavaee Address >= TextSections[TextSectionIndex].EndAddress) 321733dc3e5SRahman Lavaee TextSectionIndex++; 322733dc3e5SRahman Lavaee if (TextSectionIndex >= TextSections.size() || 323733dc3e5SRahman Lavaee Address + BucketSize < TextSections[TextSectionIndex].BeginAddress) { 324733dc3e5SRahman Lavaee RecordUnmappedBucket(Address, KV.second); 325733dc3e5SRahman Lavaee continue; 326733dc3e5SRahman Lavaee } 327733dc3e5SRahman Lavaee SectionHotness[TextSections[TextSectionIndex].Name] += KV.second; 328733dc3e5SRahman Lavaee } 329733dc3e5SRahman Lavaee 330733dc3e5SRahman Lavaee assert(NumTotalCounts > 0 && 331733dc3e5SRahman Lavaee "total number of heatmap buckets should be greater than 0"); 332733dc3e5SRahman Lavaee 333733dc3e5SRahman Lavaee OS << "Section Name, Begin Address, End Address, Percentage Hotness\n"; 334733dc3e5SRahman Lavaee for (auto &TextSection : TextSections) { 335733dc3e5SRahman Lavaee OS << TextSection.Name << ", 0x" 336733dc3e5SRahman Lavaee << Twine::utohexstr(TextSection.BeginAddress) << ", 0x" 337733dc3e5SRahman Lavaee << Twine::utohexstr(TextSection.EndAddress) << ", " 338733dc3e5SRahman Lavaee << format("%.4f", 339733dc3e5SRahman Lavaee 100.0 * SectionHotness[TextSection.Name] / NumTotalCounts) 340733dc3e5SRahman Lavaee << "\n"; 341733dc3e5SRahman Lavaee } 342733dc3e5SRahman Lavaee if (UnmappedHotness > 0) 343733dc3e5SRahman Lavaee OS << "[unmapped], 0x0, 0x0, " 344733dc3e5SRahman Lavaee << format("%.4f", 100.0 * UnmappedHotness / NumTotalCounts) << "\n"; 345733dc3e5SRahman Lavaee } 346a34c753fSRafael Auler } // namespace bolt 347a34c753fSRafael Auler } // namespace llvm 348