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