[BOLT] Report per-section hotness in bolt-heatmap.

This patch adds a new feature to bolt heatmap to print the hotness of each section in terms of the percentage of samples within that section.

Sample output generated for the clang binary:

Section Name, Begin Address, End Address, Percentage Hotness
.text, 0x1a7b9b0, 0x20a2cc0, 1.4709
.init, 0x20a2cc0, 0x20a2ce1, 0.0001
.fini, 0x20a2ce4, 0x20a2cf2, 0.0000
.text.unlikely, 0x20a2d00, 0x431990c, 0.3061
.text.hot, 0x4319910, 0x4bc6927, 97.2197
.text.startup, 0x4bc6930, 0x4c10c89, 0.0058
.plt, 0x4c10c90, 0x4c12010, 0.9974

Reviewed By: rafauler

Differential Revision: https://reviews.llvm.org/D124412
This commit is contained in:
Rahman Lavaee 2022-05-05 11:37:15 -07:00
parent f6dff93641
commit 733dc3e50b
3 changed files with 100 additions and 4 deletions

View file

@ -12,12 +12,20 @@
#include "llvm/ADT/StringRef.h"
#include <cstdint>
#include <map>
#include <vector>
namespace llvm {
class raw_ostream;
namespace bolt {
/// Struct representing a section name and its address range in the binary.
struct SectionNameAndRange {
StringRef Name;
uint64_t BeginAddress;
uint64_t EndAddress;
};
class Heatmap {
/// Number of bytes per entry in the heat map.
size_t BucketSize;
@ -34,11 +42,15 @@ class Heatmap {
/// Map buckets to the number of samples.
std::map<uint64_t, uint64_t> Map;
/// Map section names to their address range.
const std::vector<SectionNameAndRange> TextSections;
public:
explicit Heatmap(uint64_t BucketSize = 4096, uint64_t MinAddress = 0,
uint64_t MaxAddress = std::numeric_limits<uint64_t>::max())
: BucketSize(BucketSize), MinAddress(MinAddress),
MaxAddress(MaxAddress){};
uint64_t MaxAddress = std::numeric_limits<uint64_t>::max(),
std::vector<SectionNameAndRange> TextSections = {})
: BucketSize(BucketSize), MinAddress(MinAddress), MaxAddress(MaxAddress),
TextSections(TextSections) {}
inline bool ignoreAddress(uint64_t Address) const {
return (Address > MaxAddress) || (Address < MinAddress);
@ -65,6 +77,10 @@ public:
void printCDF(raw_ostream &OS) const;
void printSectionHotness(StringRef Filename) const;
void printSectionHotness(raw_ostream &OS) const;
size_t size() const { return Map.size(); }
};

View file

@ -116,6 +116,22 @@ namespace {
const char TimerGroupName[] = "aggregator";
const char TimerGroupDesc[] = "Aggregator";
std::vector<SectionNameAndRange> getTextSections(const BinaryContext *BC) {
std::vector<SectionNameAndRange> sections;
for (BinarySection &Section : BC->sections()) {
if (!Section.isText())
continue;
if (Section.getSize() == 0)
continue;
sections.push_back(
{Section.getName(), Section.getAddress(), Section.getEndAddress()});
}
std::sort(sections.begin(), sections.end(),
[](const SectionNameAndRange &A, const SectionNameAndRange &B) {
return A.BeginAddress < B.BeginAddress;
});
return sections;
}
}
constexpr uint64_t DataAggregator::KernelBaseAddr;
@ -1292,7 +1308,7 @@ std::error_code DataAggregator::printLBRHeatMap() {
opts::HeatmapMinAddress = KernelBaseAddr;
}
Heatmap HM(opts::HeatmapBlock, opts::HeatmapMinAddress,
opts::HeatmapMaxAddress);
opts::HeatmapMaxAddress, getTextSections(BC));
uint64_t NumTotalSamples = 0;
if (opts::BasicAggregation) {
@ -1374,6 +1390,10 @@ std::error_code DataAggregator::printLBRHeatMap() {
HM.printCDF(opts::OutputFilename);
else
HM.printCDF(opts::OutputFilename + ".csv");
if (opts::OutputFilename == "-")
HM.printSectionHotness(opts::OutputFilename);
else
HM.printSectionHotness(opts::OutputFilename + "-section-hotness.csv");
return std::error_code();
}

View file

@ -8,6 +8,7 @@
#include "bolt/Profile/Heatmap.h"
#include "bolt/Utils/CommandLineOpts.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
@ -251,5 +252,64 @@ void Heatmap::printCDF(raw_ostream &OS) const {
Counts.clear();
}
void Heatmap::printSectionHotness(StringRef FileName) const {
std::error_code EC;
raw_fd_ostream OS(FileName, EC, sys::fs::OpenFlags::OF_None);
if (EC) {
errs() << "error opening output file: " << EC.message() << '\n';
exit(1);
}
printSectionHotness(OS);
}
void Heatmap::printSectionHotness(raw_ostream &OS) const {
uint64_t NumTotalCounts = 0;
StringMap<uint64_t> SectionHotness;
unsigned TextSectionIndex = 0;
if (TextSections.empty())
return;
uint64_t UnmappedHotness = 0;
auto RecordUnmappedBucket = [&](uint64_t Address, uint64_t Frequency) {
errs() << "Couldn't map the address bucket [0x" << Twine::utohexstr(Address)
<< ", 0x" << Twine::utohexstr(Address + BucketSize)
<< "] containing " << Frequency
<< " samples to a text section in the binary.";
UnmappedHotness += Frequency;
};
for (const std::pair<const uint64_t, uint64_t> &KV : Map) {
NumTotalCounts += KV.second;
// We map an address bucket to the first section (lowest address)
// overlapping with that bucket.
auto Address = KV.first * BucketSize;
while (TextSectionIndex < TextSections.size() &&
Address >= TextSections[TextSectionIndex].EndAddress)
TextSectionIndex++;
if (TextSectionIndex >= TextSections.size() ||
Address + BucketSize < TextSections[TextSectionIndex].BeginAddress) {
RecordUnmappedBucket(Address, KV.second);
continue;
}
SectionHotness[TextSections[TextSectionIndex].Name] += KV.second;
}
assert(NumTotalCounts > 0 &&
"total number of heatmap buckets should be greater than 0");
OS << "Section Name, Begin Address, End Address, Percentage Hotness\n";
for (auto &TextSection : TextSections) {
OS << TextSection.Name << ", 0x"
<< Twine::utohexstr(TextSection.BeginAddress) << ", 0x"
<< Twine::utohexstr(TextSection.EndAddress) << ", "
<< format("%.4f",
100.0 * SectionHotness[TextSection.Name] / NumTotalCounts)
<< "\n";
}
if (UnmappedHotness > 0)
OS << "[unmapped], 0x0, 0x0, "
<< format("%.4f", 100.0 * UnmappedHotness / NumTotalCounts) << "\n";
}
} // namespace bolt
} // namespace llvm