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/CommandLine.h"
14a34c753fSRafael Auler #include "llvm/Support/Debug.h"
15a34c753fSRafael Auler #include "llvm/Support/FileSystem.h"
16a34c753fSRafael Auler #include "llvm/Support/Format.h"
17a34c753fSRafael Auler #include "llvm/Support/MathExtras.h"
18a34c753fSRafael Auler #include "llvm/Support/raw_ostream.h"
19a34c753fSRafael Auler #include <algorithm>
20a34c753fSRafael Auler #include <cmath>
21a34c753fSRafael Auler #include <vector>
22a34c753fSRafael Auler 
23a34c753fSRafael Auler #define DEBUG_TYPE "bolt-heatmap"
24a34c753fSRafael Auler 
25a34c753fSRafael Auler using namespace llvm;
26a34c753fSRafael Auler 
27a34c753fSRafael Auler namespace llvm {
28a34c753fSRafael Auler namespace bolt {
29a34c753fSRafael Auler 
registerAddressRange(uint64_t StartAddress,uint64_t EndAddress,uint64_t Count)30a34c753fSRafael Auler void Heatmap::registerAddressRange(uint64_t StartAddress, uint64_t EndAddress,
31a34c753fSRafael Auler                                    uint64_t Count) {
32a34c753fSRafael Auler   if (ignoreAddress(StartAddress)) {
33a34c753fSRafael Auler     ++NumSkippedRanges;
34a34c753fSRafael Auler     return;
35a34c753fSRafael Auler   }
36a34c753fSRafael Auler 
3740c2e0faSMaksim Panchenko   if (StartAddress > EndAddress || EndAddress - StartAddress > 64 * 1024) {
38a34c753fSRafael Auler     LLVM_DEBUG(dbgs() << "invalid range : 0x" << Twine::utohexstr(StartAddress)
39a34c753fSRafael Auler                       << " -> 0x" << Twine::utohexstr(EndAddress) << '\n');
40a34c753fSRafael Auler     ++NumSkippedRanges;
41a34c753fSRafael Auler     return;
42a34c753fSRafael Auler   }
43a34c753fSRafael Auler 
44a34c753fSRafael Auler   for (uint64_t Bucket = StartAddress / BucketSize;
45def464aaSAmir Ayupov        Bucket <= EndAddress / BucketSize; ++Bucket)
46a34c753fSRafael Auler     Map[Bucket] += Count;
47a34c753fSRafael Auler }
48a34c753fSRafael Auler 
print(StringRef FileName) const49a34c753fSRafael Auler void Heatmap::print(StringRef FileName) const {
50a34c753fSRafael Auler   std::error_code EC;
51a34c753fSRafael Auler   raw_fd_ostream OS(FileName, EC, sys::fs::OpenFlags::OF_None);
52a34c753fSRafael Auler   if (EC) {
53a34c753fSRafael Auler     errs() << "error opening output file: " << EC.message() << '\n';
54a34c753fSRafael Auler     exit(1);
55a34c753fSRafael Auler   }
56a34c753fSRafael Auler   print(OS);
57a34c753fSRafael Auler }
58a34c753fSRafael Auler 
print(raw_ostream & OS) const59a34c753fSRafael Auler void Heatmap::print(raw_ostream &OS) const {
60a34c753fSRafael Auler   const char FillChar = '.';
61a34c753fSRafael Auler 
62a34c753fSRafael Auler   const auto DefaultColor = raw_ostream::WHITE;
63a34c753fSRafael Auler   auto changeColor = [&](raw_ostream::Colors Color) -> void {
64a34c753fSRafael Auler     static auto CurrentColor = raw_ostream::BLACK;
65a34c753fSRafael Auler     if (CurrentColor == Color)
66a34c753fSRafael Auler       return;
67a34c753fSRafael Auler     OS.changeColor(Color);
68a34c753fSRafael Auler     CurrentColor = Color;
69a34c753fSRafael Auler   };
70a34c753fSRafael Auler 
71a34c753fSRafael Auler   const uint64_t BytesPerLine = opts::BucketsPerLine * BucketSize;
72a34c753fSRafael Auler 
73a34c753fSRafael Auler   // Calculate the max value for scaling.
74a34c753fSRafael Auler   uint64_t MaxValue = 0;
75def464aaSAmir Ayupov   for (const std::pair<const uint64_t, uint64_t> &Entry : Map)
76a34c753fSRafael Auler     MaxValue = std::max<uint64_t>(MaxValue, Entry.second);
77a34c753fSRafael Auler 
78a34c753fSRafael Auler   // Print start of the line and fill it with an empty space right before
79a34c753fSRafael Auler   // the Address.
80a34c753fSRafael Auler   auto startLine = [&](uint64_t Address, bool Empty = false) {
81a34c753fSRafael Auler     changeColor(DefaultColor);
82a34c753fSRafael Auler     const uint64_t LineAddress = Address / BytesPerLine * BytesPerLine;
83a34c753fSRafael Auler 
84a34c753fSRafael Auler     if (MaxAddress > 0xffffffff)
85a34c753fSRafael Auler       OS << format("0x%016" PRIx64 ": ", LineAddress);
86a34c753fSRafael Auler     else
87a34c753fSRafael Auler       OS << format("0x%08" PRIx64 ": ", LineAddress);
88a34c753fSRafael Auler 
89a34c753fSRafael Auler     if (Empty)
90a34c753fSRafael Auler       Address = LineAddress + BytesPerLine;
91def464aaSAmir Ayupov     for (uint64_t Fill = LineAddress; Fill < Address; Fill += BucketSize)
92a34c753fSRafael Auler       OS << FillChar;
93a34c753fSRafael Auler   };
94a34c753fSRafael Auler 
95a34c753fSRafael Auler   // Finish line after \p Address was printed.
96a34c753fSRafael Auler   auto finishLine = [&](uint64_t Address) {
97a34c753fSRafael Auler     const uint64_t End = alignTo(Address + 1, BytesPerLine);
98a34c753fSRafael Auler     for (uint64_t Fill = Address + BucketSize; Fill < End; Fill += BucketSize)
99a34c753fSRafael Auler       OS << FillChar;
100a34c753fSRafael Auler     OS << '\n';
101a34c753fSRafael Auler   };
102a34c753fSRafael Auler 
103a34c753fSRafael Auler   // Fill empty space in (Start, End) range.
104a34c753fSRafael Auler   auto fillRange = [&](uint64_t Start, uint64_t End) {
105a34c753fSRafael Auler     if ((Start / BytesPerLine) == (End / BytesPerLine)) {
106a34c753fSRafael Auler       for (uint64_t Fill = Start + BucketSize; Fill < End; Fill += BucketSize) {
107a34c753fSRafael Auler         changeColor(DefaultColor);
108a34c753fSRafael Auler         OS << FillChar;
109a34c753fSRafael Auler       }
110a34c753fSRafael Auler       return;
111a34c753fSRafael Auler     }
112a34c753fSRafael Auler 
113a34c753fSRafael Auler     changeColor(DefaultColor);
114a34c753fSRafael Auler     finishLine(Start);
115a34c753fSRafael Auler     Start = alignTo(Start, BytesPerLine);
116a34c753fSRafael Auler 
117a34c753fSRafael Auler     uint64_t NumEmptyLines = (End - Start) / BytesPerLine;
118a34c753fSRafael Auler 
119a34c753fSRafael Auler     if (NumEmptyLines > 32) {
120a34c753fSRafael Auler       OS << '\n';
121a34c753fSRafael Auler     } else {
122a34c753fSRafael Auler       while (NumEmptyLines--) {
123a34c753fSRafael Auler         startLine(Start, /*Empty=*/true);
124a34c753fSRafael Auler         OS << '\n';
125a34c753fSRafael Auler         Start += BytesPerLine;
126a34c753fSRafael Auler       }
127a34c753fSRafael Auler     }
128a34c753fSRafael Auler 
129a34c753fSRafael Auler     startLine(End);
130a34c753fSRafael Auler   };
131a34c753fSRafael Auler 
132a34c753fSRafael Auler   static raw_ostream::Colors Colors[] = {
13340c2e0faSMaksim Panchenko       raw_ostream::WHITE, raw_ostream::WHITE,  raw_ostream::CYAN,
13440c2e0faSMaksim Panchenko       raw_ostream::GREEN, raw_ostream::YELLOW, raw_ostream::RED};
135a34c753fSRafael Auler   constexpr size_t NumRanges = sizeof(Colors) / sizeof(Colors[0]);
136a34c753fSRafael Auler 
137a34c753fSRafael Auler   uint64_t Range[NumRanges];
138a34c753fSRafael Auler   for (uint64_t I = 0; I < NumRanges; ++I)
13940c2e0faSMaksim Panchenko     Range[I] = std::max(I + 1, (uint64_t)std::pow((double)MaxValue,
140a34c753fSRafael Auler                                                   (double)(I + 1) / NumRanges));
141a34c753fSRafael Auler   Range[NumRanges - 1] = std::max((uint64_t)NumRanges, MaxValue);
142a34c753fSRafael Auler 
143a34c753fSRafael Auler   // Print scaled value
144a34c753fSRafael Auler   auto printValue = [&](uint64_t Value, bool ResetColor = false) {
145a34c753fSRafael Auler     assert(Value && "should only print positive values");
146a34c753fSRafael Auler     for (unsigned I = 0; I < sizeof(Range) / sizeof(Range[0]); ++I) {
147a34c753fSRafael Auler       if (Value <= Range[I]) {
148a34c753fSRafael Auler         changeColor(Colors[I]);
149a34c753fSRafael Auler         break;
150a34c753fSRafael Auler       }
151a34c753fSRafael Auler     }
152def464aaSAmir Ayupov     if (Value <= Range[0])
153a34c753fSRafael Auler       OS << 'o';
154def464aaSAmir Ayupov     else
155a34c753fSRafael Auler       OS << 'O';
156def464aaSAmir Ayupov 
157a34c753fSRafael Auler     if (ResetColor)
158a34c753fSRafael Auler       changeColor(DefaultColor);
159a34c753fSRafael Auler   };
160a34c753fSRafael Auler 
161a34c753fSRafael Auler   // Print against black background
162a34c753fSRafael Auler   OS.changeColor(raw_ostream::BLACK, /*Bold=*/false, /*Background=*/true);
163a34c753fSRafael Auler   changeColor(DefaultColor);
164a34c753fSRafael Auler 
165a34c753fSRafael Auler   // Print map legend
166a34c753fSRafael Auler   OS << "Legend:\n";
167a34c753fSRafael Auler   uint64_t PrevValue = 0;
168a34c753fSRafael Auler   for (unsigned I = 0; I < sizeof(Range) / sizeof(Range[0]); ++I) {
169a34c753fSRafael Auler     const uint64_t Value = Range[I];
170a34c753fSRafael Auler     OS << "  ";
171a34c753fSRafael Auler     printValue(Value, true);
172a34c753fSRafael Auler     OS << " : (" << PrevValue << ", " << Value << "]\n";
173a34c753fSRafael Auler     PrevValue = Value;
174a34c753fSRafael Auler   }
175a34c753fSRafael Auler 
176a34c753fSRafael Auler   // Pos - character position from right in hex form.
177a34c753fSRafael Auler   auto printHeader = [&](unsigned Pos) {
178a34c753fSRafael Auler     OS << "            ";
179a34c753fSRafael Auler     if (MaxAddress > 0xffffffff)
180a34c753fSRafael Auler       OS << "        ";
181a34c753fSRafael Auler     unsigned PrevValue = unsigned(-1);
182a34c753fSRafael Auler     for (unsigned I = 0; I < BytesPerLine; I += BucketSize) {
183a34c753fSRafael Auler       const unsigned Value = (I & ((1 << Pos * 4) - 1)) >> (Pos - 1) * 4;
184a34c753fSRafael Auler       if (Value != PrevValue) {
185a34c753fSRafael Auler         OS << Twine::utohexstr(Value);
186a34c753fSRafael Auler         PrevValue = Value;
187a34c753fSRafael Auler       } else {
188a34c753fSRafael Auler         OS << ' ';
189a34c753fSRafael Auler       }
190a34c753fSRafael Auler     }
191a34c753fSRafael Auler     OS << '\n';
192a34c753fSRafael Auler   };
193a34c753fSRafael Auler   for (unsigned I = 5; I > 0; --I)
194a34c753fSRafael Auler     printHeader(I);
195a34c753fSRafael Auler 
196a34c753fSRafael Auler   uint64_t PrevAddress = 0;
197a34c753fSRafael Auler   for (auto MI = Map.begin(), ME = Map.end(); MI != ME; ++MI) {
198a34c753fSRafael Auler     const std::pair<const uint64_t, uint64_t> &Entry = *MI;
199a34c753fSRafael Auler     uint64_t Address = Entry.first * BucketSize;
200a34c753fSRafael Auler 
201def464aaSAmir Ayupov     if (PrevAddress)
202a34c753fSRafael Auler       fillRange(PrevAddress, Address);
203def464aaSAmir Ayupov     else
204a34c753fSRafael Auler       startLine(Address);
205a34c753fSRafael Auler 
206a34c753fSRafael Auler     printValue(Entry.second);
207a34c753fSRafael Auler 
208a34c753fSRafael Auler     PrevAddress = Address;
209a34c753fSRafael Auler   }
210a34c753fSRafael Auler 
211a34c753fSRafael Auler   if (PrevAddress) {
212a34c753fSRafael Auler     changeColor(DefaultColor);
213a34c753fSRafael Auler     finishLine(PrevAddress);
214a34c753fSRafael Auler   }
215a34c753fSRafael Auler }
216a34c753fSRafael Auler 
printCDF(StringRef FileName) const217a34c753fSRafael Auler void Heatmap::printCDF(StringRef FileName) const {
218a34c753fSRafael Auler   std::error_code EC;
219a34c753fSRafael Auler   raw_fd_ostream OS(FileName, EC, sys::fs::OpenFlags::OF_None);
220a34c753fSRafael Auler   if (EC) {
221a34c753fSRafael Auler     errs() << "error opening output file: " << EC.message() << '\n';
222a34c753fSRafael Auler     exit(1);
223a34c753fSRafael Auler   }
224a34c753fSRafael Auler   printCDF(OS);
225a34c753fSRafael Auler }
226a34c753fSRafael Auler 
printCDF(raw_ostream & OS) const227a34c753fSRafael Auler void Heatmap::printCDF(raw_ostream &OS) const {
228a34c753fSRafael Auler   uint64_t NumTotalCounts = 0;
229a34c753fSRafael Auler   std::vector<uint64_t> Counts;
230a34c753fSRafael Auler 
231a34c753fSRafael Auler   for (const std::pair<const uint64_t, uint64_t> &KV : Map) {
232a34c753fSRafael Auler     Counts.push_back(KV.second);
233a34c753fSRafael Auler     NumTotalCounts += KV.second;
234a34c753fSRafael Auler   }
235a34c753fSRafael Auler 
236*d2c87699SAmir Ayupov   llvm::sort(Counts, std::greater<uint64_t>());
237a34c753fSRafael Auler 
238a34c753fSRafael Auler   double RatioLeftInKB = (1.0 * BucketSize) / 1024;
239a34c753fSRafael Auler   assert(NumTotalCounts > 0 &&
240a34c753fSRafael Auler          "total number of heatmap buckets should be greater than 0");
241a34c753fSRafael Auler   double RatioRightInPercent = 100.0 / NumTotalCounts;
242a34c753fSRafael Auler   uint64_t RunningCount = 0;
243a34c753fSRafael Auler 
244a34c753fSRafael Auler   OS << "Bucket counts, Size (KB), CDF (%)\n";
245a34c753fSRafael Auler   for (uint64_t I = 0; I < Counts.size(); I++) {
246a34c753fSRafael Auler     RunningCount += Counts[I];
247a34c753fSRafael Auler     OS << format("%llu", (I + 1)) << ", "
248a34c753fSRafael Auler        << format("%.4f", RatioLeftInKB * (I + 1)) << ", "
249a34c753fSRafael Auler        << format("%.4f", RatioRightInPercent * (RunningCount)) << "\n";
250a34c753fSRafael Auler   }
251a34c753fSRafael Auler 
252a34c753fSRafael Auler   Counts.clear();
253a34c753fSRafael Auler }
254a34c753fSRafael Auler 
printSectionHotness(StringRef FileName) const255733dc3e5SRahman Lavaee void Heatmap::printSectionHotness(StringRef FileName) const {
256733dc3e5SRahman Lavaee   std::error_code EC;
257733dc3e5SRahman Lavaee   raw_fd_ostream OS(FileName, EC, sys::fs::OpenFlags::OF_None);
258733dc3e5SRahman Lavaee   if (EC) {
259733dc3e5SRahman Lavaee     errs() << "error opening output file: " << EC.message() << '\n';
260733dc3e5SRahman Lavaee     exit(1);
261733dc3e5SRahman Lavaee   }
262733dc3e5SRahman Lavaee   printSectionHotness(OS);
263733dc3e5SRahman Lavaee }
264733dc3e5SRahman Lavaee 
printSectionHotness(raw_ostream & OS) const265733dc3e5SRahman Lavaee void Heatmap::printSectionHotness(raw_ostream &OS) const {
266733dc3e5SRahman Lavaee   uint64_t NumTotalCounts = 0;
267733dc3e5SRahman Lavaee   StringMap<uint64_t> SectionHotness;
268733dc3e5SRahman Lavaee   unsigned TextSectionIndex = 0;
269733dc3e5SRahman Lavaee 
270733dc3e5SRahman Lavaee   if (TextSections.empty())
271733dc3e5SRahman Lavaee     return;
272733dc3e5SRahman Lavaee 
273733dc3e5SRahman Lavaee   uint64_t UnmappedHotness = 0;
274733dc3e5SRahman Lavaee   auto RecordUnmappedBucket = [&](uint64_t Address, uint64_t Frequency) {
275733dc3e5SRahman Lavaee     errs() << "Couldn't map the address bucket [0x" << Twine::utohexstr(Address)
276733dc3e5SRahman Lavaee            << ", 0x" << Twine::utohexstr(Address + BucketSize)
277733dc3e5SRahman Lavaee            << "] containing " << Frequency
278733dc3e5SRahman Lavaee            << " samples to a text section in the binary.";
279733dc3e5SRahman Lavaee     UnmappedHotness += Frequency;
280733dc3e5SRahman Lavaee   };
281733dc3e5SRahman Lavaee 
282733dc3e5SRahman Lavaee   for (const std::pair<const uint64_t, uint64_t> &KV : Map) {
283733dc3e5SRahman Lavaee     NumTotalCounts += KV.second;
284733dc3e5SRahman Lavaee     // We map an address bucket to the first section (lowest address)
285733dc3e5SRahman Lavaee     // overlapping with that bucket.
286733dc3e5SRahman Lavaee     auto Address = KV.first * BucketSize;
287733dc3e5SRahman Lavaee     while (TextSectionIndex < TextSections.size() &&
288733dc3e5SRahman Lavaee            Address >= TextSections[TextSectionIndex].EndAddress)
289733dc3e5SRahman Lavaee       TextSectionIndex++;
290733dc3e5SRahman Lavaee     if (TextSectionIndex >= TextSections.size() ||
291733dc3e5SRahman Lavaee         Address + BucketSize < TextSections[TextSectionIndex].BeginAddress) {
292733dc3e5SRahman Lavaee       RecordUnmappedBucket(Address, KV.second);
293733dc3e5SRahman Lavaee       continue;
294733dc3e5SRahman Lavaee     }
295733dc3e5SRahman Lavaee     SectionHotness[TextSections[TextSectionIndex].Name] += KV.second;
296733dc3e5SRahman Lavaee   }
297733dc3e5SRahman Lavaee 
298733dc3e5SRahman Lavaee   assert(NumTotalCounts > 0 &&
299733dc3e5SRahman Lavaee          "total number of heatmap buckets should be greater than 0");
300733dc3e5SRahman Lavaee 
301733dc3e5SRahman Lavaee   OS << "Section Name, Begin Address, End Address, Percentage Hotness\n";
302733dc3e5SRahman Lavaee   for (auto &TextSection : TextSections) {
303733dc3e5SRahman Lavaee     OS << TextSection.Name << ", 0x"
304733dc3e5SRahman Lavaee        << Twine::utohexstr(TextSection.BeginAddress) << ", 0x"
305733dc3e5SRahman Lavaee        << Twine::utohexstr(TextSection.EndAddress) << ", "
306733dc3e5SRahman Lavaee        << format("%.4f",
307733dc3e5SRahman Lavaee                  100.0 * SectionHotness[TextSection.Name] / NumTotalCounts)
308733dc3e5SRahman Lavaee        << "\n";
309733dc3e5SRahman Lavaee   }
310733dc3e5SRahman Lavaee   if (UnmappedHotness > 0)
311733dc3e5SRahman Lavaee     OS << "[unmapped], 0x0, 0x0, "
312733dc3e5SRahman Lavaee        << format("%.4f", 100.0 * UnmappedHotness / NumTotalCounts) << "\n";
313733dc3e5SRahman Lavaee }
314a34c753fSRafael Auler } // namespace bolt
315a34c753fSRafael Auler } // namespace llvm
316